Skip to content

Prevent recursive dex2oat execution after daemon restart - #1008

Draft
XR-stb wants to merge 4 commits into
JingMatrix:masterfrom
XR-stb:fix/dex2oat-recursive-wrapper
Draft

XR-stb wants to merge 4 commits into
JingMatrix:masterfrom
XR-stb:fix/dex2oat-recursive-wrapper

Conversation

@XR-stb

@XR-stb XR-stb commented Oct 14, 2026 •

Copy link
Copy Markdown

When the daemon starts in a mount namespace containing an earlier dex2oat wrapper bind mount, Dex2OatServer can cache the wrapper itself as the stock compiler. The wrapper then execs that FD through the linker, re-enters main in the same process, and repeatedly acquires descriptors and copies the hook library.

This change:

  • Validates the opened compiler descriptor against the corresponding wrapper inode/device before caching it.
  • On compiler initialization failure, invokes the existing wrapper-disable/fallback path, closes cached descriptors, reports DEX2OAT_MOUNT_FAILED, and does not start the socket server or SELinux observer.
  • Adds a PID-scoped environment guard before any socket requests so recursive exec terminates even if the daemon-side identity check misses a replaced wrapper inode. Child processes inheriting the environment remain allowed.
  • Adds a Linux host integration test using the actual wrapper source and a mock SCM_RIGHTS server, and runs it in CI.

Validation:

  • All 3 host tests pass: normal compiler execution, wrapper-as-stock recursion rejection, and inherited parent marker.
  • The same recursion test against the unmodified source times out after 5 seconds.
  • Wrapper cross-compilation passes for Android ARM64 and ARMv7/API 27 using NDK 26.1.
  • Dex2OatServer compiles against Android API 35 with local project/hidden-API stubs (a limited syntax/type check, not the full project build).
  • Full Gradle build, regression tests, and Release/Debug artifact generation passed in fork Actions run 38583368959. The build-only branch at 7a01e11 has the same application/test sources as this PR head (6b8a273); only release-publishing workflow steps were removed. The upstream PR workflow still awaits maintainer approval.

Device validation (2026-10-14):

  • Xiaomi 14, Android 16 / HyperOS 3.0.305.0, KernelSU temporary-root mode (32680), Zygisk Next 1.5.2.
  • Installed Release build 3112 from the run above and activated it with one KernelSU soft reboot. Root remained available; the module reported enabled with no pending update. The fork labels it v1.0 because release tags were absent, not because it uses the historical v1.0 source.
  • Exactly one vectord process was observed. Cached stock compiler descriptors had device/inode pairs 65067:20 and 65067:21, distinct from wrapper files 65078:6021722 and 65078:6067890. Framework injection into WeChat and HyperCeiler module loading were logged.
  • A 60-second background sample showed 90.5–94.8% aggregate CPU idle. No repeat of the previously observed sustained compiler high-CPU loop was observed during these samples.
  • During 73.435 seconds of user-driven WeChat activity, gfxinfo counters from the same PID and unchanged stats-start time increased from 1,667 to 6,884 frames, and from 8 to 91 janky frames: 5,217 new frames, 83 janky (1.59%). The separately reported legacy jank counter increased by 821 (15.74%); these are different metrics. Aggregate CPU idle in the concurrent samples ranged from 46.1% to 90.5%.
  • The device owner reported that WeChat felt much smoother and Honor of Kings no longer felt laggy. No game frame-time trace was collected.

Interpretation and limits:

  • This is an installation/runtime smoke test and a short workload observation, not a controlled before/after performance benchmark. Stale wrapper mounts had already been removed during recovery before installation, and the tested build also moves from the older 88f8e1f Debug build to current-source Release. The observed improvement cannot be attributed solely to this patch.
  • The stale-mount fault was not deliberately recreated on the phone. The controlled recursion rejection evidence comes from the host regression test; the device checks confirm normal startup with correct stock compiler descriptors. Repeated soft-reboot lifecycle behavior and long-term stability remain untested.
  • Compatibility warnings remain in the device log (including an unavailable systemui_is_cached field); this does not establish compatibility of every module or feature.

Motivation: a Xiaomi 14 / Android 16 / KernelSU temporary-root setup running Vector 2.2 (88f8e1f) exhibited repeated wrapper entry in one PID, with stock descriptors increasing beyond 10,000 and the next DEX2OAT_CMD referring to the previous stock FD. This is consistent with recursive exec; the exact event that left the stale mount was not captured.

Related to the daemon lifecycle concerns in #878, but this does not fix duplicate daemon ownership or claim to resolve that entire issue. The existing unmount/fallback operation is best-effort; automatic stock-FD recovery and complete soft-reboot lifecycle handling remain outside this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant