Take the previous daemon down on an emulated soft reboot - #914
Conversation
`ksud soft-reboot` does not reboot the kernel. It runs the `emulated-soft-reboot` stage, `stop`, post-fs-data, `start`, and then `on_services()`, which runs every active module's service.sh a second time. `stop` never reaches the daemon the previous cycle started, because service.sh detaches it, so from the second soft reboot onwards two daemons are alive at once. They then claim the same proxy service name, each latches the other as `originService`, and `SystemServerService.onTransact` forwards to it unconditionally, so the binder ping-pong overflows a Java stack: the new daemon dies on `JNI FatalError ... StackOverflowError`, the survivor times out on the bridge, and nothing is injected. That stage is how a module is meant to shut its daemon down, and Zygisk Next uses it. `-f` is required because app_process sets the nice name in argv only, so comm stays "main" and `pkill vectord` matches nothing, and the `^` anchor keeps the pattern off any shell whose own command line contains the word. customize.sh sets SKIPUNZIP=1 and installs from a fixed list, so the script has to be named there as well or it ships in the zip without ever being installed.
|
More verification since opening this, from my own runs only. Script absent, fresh boot, three consecutive soft reboots:
The pid is frozen across all three — the previous cycle's daemon is still the one running, which is the Script present, seven cycles across two boots: one daemon every cycle, injection succeeded every cycle, and the pid changes every cycle instead of freezing. Trace from a copy with a logging line added:
Two notes for anyone verifying this. A marker written to This removes the duplicate daemon; it does not make every path to "nothing injected" impossible. A report with a single daemon alive and no |
Fixes #878.
ksud soft-rebootdoes not reboot the kernel. It runs theemulated-soft-rebootstage,stop, post-fs-data,start, and thenon_services(), which runs every active module'sservice.sha second time.stopnever reaches the daemon the previous cycle started, becauseservice.shdetaches it, so from the second soft reboot onwards two daemons are alive at once. They then claim the same proxy service name — one squatting it on startup, one re-claiming it from its death recipient — each latches the other asoriginService,SystemServerService.onTransactforwards to it unconditionally, and the resulting binder ping-pong overflows a Java stack. The new daemon dies onJNI FatalError ... StackOverflowError, the survivor times out on the bridge, nothing is injected, and Xposed stays off until a real reboot.FileSystem.tryLock()does not prevent it: it returns true while the previous daemon is alive.That stage is how a module is meant to shut its daemon down, and Zygisk Next uses it (
zygiskd exit). This adds the equivalent for Vector.Two notes on the script.
-fis required becauseapp_processsets the nice name inargvonly, socommstaysmainandpkill vectordmatches nothing; the^anchor is required or the pattern also matches any shell whose own command line contains the word. The stage is blocking, so it has to stay cheap — this costs about 60 ms, where a/procscan forking once per pid cost 9.5 s. If you would rather stop the daemon gracefully,killfollowed by a short wait and akill -9fallback would work too; I kept the plainkill -9because that is the form I measured.customize.shneeds the second hunk because it setsSKIPUNZIP=1and installs from a fixed list, so a new file inzygisk/module/ships in the zip but never lands in/data/adb/modules/zygisk_vector. The.sha256sidecarextract()verifies is generated for every staged file byprepareModuleFiles, so no build change is needed. Installed permissions come fromset_perm_recursive "$MODPATH" 0 0 0755 0644, which matches how Zygisk Next ships its own copy.Verified on a Galaxy SM-S9280, Android 16, KernelSU 3.2.5 late-loaded as an LKM, Vector
a0ab735e, by scripting the module directory directly on the device: four consecutive soft reboots on one boot, the two without this script left the framework dead as described in #878, and the two with it produced a singlevectord, noStackOverflowError,Injected Vector framework into system_server,Successfully injected Vector IPC binder for applications, a workingcli statusand modules loading into apps again. I have not rebuilt the zip end to end, so the packaging change is reviewed rather than measured. The file here differs from the one I ran in two cosmetic ways: it drops a/dev/kmsgline I used to confirm the stage had fired, and the shebang, sinceexec_scriptinvokes stage scripts asbusybox sh <path>and the other module scripts carry none.