Repository navigation
Conversation
Embed a static nolibc runner into the kernel module and stage it in a shmem file at boot. When PID 1 execs "/system/bin/init selinux_setup", swap the runner in via /proc/self/fd/N so it runs before the SELinux policy is loaded and before init.rc is parsed. The runner parses preinit.conf from each module synced under /metadata and performs insmod/exec/write actions, with a boot counter that disables preinit after repeated failed boots. The runner source (preinit/runner/) vendors nolibc from Linux v6.18 and is built by a dedicated Kbuild rule, then linked in via .incbin. preinit can be turned off with the preinit=0 module parameter. Move userspace_stack_buffer to util.h so the execve hook can reuse it. The preinit data lives under /metadata/vold/ksu when available, else /metadata/watchdog/ksu, else /metadata/ksu.
Copy each enabled module's preinit/ directory to the preinit partition so the kernel runner can execute it on the next boot. Pick /metadata/vold/ksu when /metadata/vold exists (hidden, does not change /metadata's link count), else /metadata/watchdog/ksu, else /metadata/ksu, and label the files to match the host dir. Sync is manifest-based and incremental, stays within a space budget on the small /metadata partition, cleans leftovers from the other candidate dirs, and is owned by root. Add `ksud module preinit status|reset` to inspect the per-module state (ok/no_space/error/bootloop) and clear the boot counter, regenerate the preinit content on module changes alongside modules.rc, and add `--no-preinit` to boot-patch / boot-patch-v2 to set the preinit=0 kernel parameter.
Parse the "preinit" field from `ksud module list` and show a warning tag on modules whose preinit content was not applied (no_space/error/ bootloop), so users notice when a module's preinit actions will not run.
Describe the module preinit/ directory and preinit.conf, the actions, the space budget on /metadata, the bootloop protection, and how to recover, in both English and Simplified Chinese.
Add KSU_IOCTL_INSMOD (UAPI v6): load a kernel module from a caller-provided binary image, resolving its undefined (non-exported) symbols against kallsyms before handing it to the kernel's internal load_module(). This lets a module reference non-exported symbols, acting as a self-contained replacement for an external LKM loader. ksud and the preinit runner prefer this supercall when supported and fall back to the userspace loader / finit_module otherwise.
Follow ksuinit's approach: after binding the undefined symbols against kallsyms, stage the patched image in an anonymous mapping of the caller and load it with the regular init_module syscall instead of calling the static load_module() resolved by name. CAP_SYS_MODULE, modules_disabled, LSM and signature checks apply again, LTO renaming of load_module is no longer an issue, and the build no longer depends on the private struct load_info. Validate the ELF header and bound every section and string offset taken from the image before patching it, the previous checks could overflow and walk past the buffer. Take the symbol strtab from the symtab's sh_link. Also move a const to the top of debug::insmod to satisfy clippy.
After killing a timed out worker, wait for it for at most a second: a worker stuck in a module's init cannot be killed, and blocking there would hang the boot. When reaping orphans, only collect what has already exited instead of polling for a second, first stage init may have long-lived children of its own (snapuserd). Only fall back from the KSU insmod supercall to finit_module when the supercall is unusable (ENOTTY/EPERM/ENOEXEC), so a module whose init failed is not loaded a second time. Treat EEXIST from the supercall as success.
- Remove /metadata/vold/ksu on uninstall, it was left behind. - Key the preinit manifest on ctime instead of mtime: extraction keeps the zip mtime, so a same-size content change could go unnoticed. - Docs: list /metadata/vold/ksu/preinit in the recovery steps and describe insmod's kallsyms resolution with the finit_module fallback.
Kernels before UAPI 6 only look for modules.rc under /metadata/watchdog and /metadata. A new ksud on such a kernel moved modules.rc to /metadata/vold and deleted the old copies, silently dropping the modules' init rc. Keep preferring /metadata/vold, but only when the running kernel reports UAPI 6 or later, otherwise keep the previous watchdog/default choice.
KSU_IOCTL_INSMOD parsed and patched the module ELF inside the kernel. Drop it for KSU_IOCTL_LOOKUP_SYMBOLS (same number, UAPI v6 is unreleased): a generic batch lookup that resolves up to KSU_LOOKUP_SYMBOLS_MAX kernel symbols, exported or not, per call and returns the number found. Missing, empty or overlong names report address 0 instead of failing the batch. Callers now bind a module's undefined symbols in userspace and load it with init_module themselves: - preinit runner: maps the validated module privately, binds its undefined symbols in batches of 256 and loads it with init_module; a module that needs no binding still goes through finit_module on the fd. - ksuinit: add load_module_with_resolver, sharing symbol collection, binding and the vermagic retry with the kallsyms based load_module. - ksud: add ksucalls::lookup_symbols; debug insmod uses it on UAPI v6+ and falls back to /proc/kallsyms on older kernels.
Match kernel symbols the way ksuinit's /proc/kallsyms walk does, so a module binds to the same addresses whichever loader is used: a kallsyms name is cut at its first '$', else at ".llvm.", the first match in kallsyms order wins, and module symbols are never returned. The previous exact lookup missed ".llvm." suffixed names on 6.12+, where the kernel no longer strips them, and could pick a later duplicate. 6.1+ kernels index kallsyms by name, so names are still looked up one by one there, keeping the first match. What that misses, and every name on 5.10/5.15 where a lookup by name is a linear scan, is resolved by a single kallsyms walk against the sorted batch instead of one scan per name. Names are copied in up front with strndup_user; an empty or overlong name still reports address 0.
… files Add KSU_GET_INFO_FLAG_PREINIT, set when the runner is built in, not disabled with preinit=0 and KernelSU was not late loaded. Without it ksud copies nothing to /metadata, removes what an earlier sync left there and reports the modules as "unsupported", which the manager now shows. The manifest tracks ctime, so a module script that chmods or chcons its preinit files on every boot made each post-fs-data look like new content and cleared the boot counter, defeating the bootloop protection. When the manifest differs, compare against the existing copy first and leave both the copy and the counter alone if the content is the same. Remove modules that are gone before copying anything, and before installing a changed module check the space actually free: the budget counts the old copy as free while install writes the new copy first, so drop the old copy when both would cut into the reserve. Docs: move preinit/ out of the initrc/ subtree in the module layout, describe the two stage init limitation precisely and the unsupported state.
- After an exec action times out, wait for the killed child for at most a second: a child stuck in D state ignores SIGKILL and blocked the worker until the global deadline. PID 1 kills and reaps what is left. - Exec the program through the fd that passed the ownership checks (execveat with AT_EMPTY_PATH) instead of resolving the path again. - The runner includes uapi/supercall.h, list the UAPI headers as prerequisites so an ioctl change rebuilds the embedded runner.
Pick the modules that fit the budget first and remove everything else before installing anything. The budget counts every existing copy as free, but copies of modules that no longer fit were only removed at the end, so a new module was refused for space while an older one that did not fit kept its copy, and which module ran depended on install order. The no_space message now shows the space actually free. Write the status file and clear the boot counter even when the sync fails part way: a read-only /metadata left the status of the last successful sync in place, and an error after new content was installed kept a parked counter, so the updated module never ran. ksud insmod: fall back to /proc/kallsyms when KSU_IOCTL_LOOKUP_SYMBOLS fails, not only on kernels before UAPI v6. The kallsyms walk moves into ksuinit::resolve_kallsyms, which load_module now uses too.
chisewaguri
pushed a commit
to chisewaguri/KernelSU
that referenced
this pull request
Oct 17, 2026
Adds a **preinit** mechanism that lets a module run a few actions
**before the SELinux policy is loaded and before `init.rc` is parsed** —
e.g. load a kernel module, run a static program, or write to
procfs/sysfs. A module describes them in a `preinit/` directory:
```
mymodule/
└── preinit/
├── preinit.conf # one action per line: insmod / exec / write
└── ... # .ko files, programs, ...
```
- **Kernel runner**: a static, nolibc-built runner is embedded in the
kernel module (`.incbin`) and staged in a shmem file at boot. When PID 1
execs `/system/bin/init selinux_setup`, the execve hook swaps the runner
in via `/proc/self/fd/N`. The runner parses each module's
`preinit.conf`, performs the actions, then execs the real init. A boot
counter disables preinit after 3 failed boots in a row.
- **ksud** syncs each enabled module's `preinit/` dir to the preinit
partition (manifest-based, incremental, within a space budget on the
small `/metadata`). Storage dir is `/metadata/vold/ksu` when available
(hidden, does not change `/metadata`'s link count), else
`/metadata/watchdog/ksu`, else `/metadata/ksu`. `ksud module preinit
status|reset` inspects per-module state and clears the boot counter.
- `boot-patch` / `boot-patch-v2` gain `--no-preinit` (sets the
`preinit=0` kernel parameter).
- **Manager** shows a warning tag on modules whose preinit content was
not applied (no space / error / bootloop).
- **Docs**: `preinit.conf` format, actions, space budget, bootloop
recovery (EN + zh_CN).
- Verified on a Redmi K80 (android14-6.1): runner runs before policy
load (process context `kernel`), `/metadata` write + fsync survives
reboot, bootloop counter parks and `reset` recovers, all conf actions
(insmod/exec/write, optional `?`, quoting, exit codes, output
forwarding). Confirmed with both v1 (ksuinit in init_boot) and v2 (ko
injected into boot). `--no-preinit` disables it (`preinit: disabled`).
Charger mode (`androidboot.mode=charger`) is skipped and does not bump
the counter. Data lands under `/metadata/vold/ksu` with the
`vold_metadata_file` label; `/metadata` root link count is unchanged.
- Builds pass for all 8 KMIs (android12-5.10 … android17-6.18).
(cherry picked from commit 0faec0c)
pershoot
pushed a commit
to KernelSU-Next/KernelSU-Next
that referenced
this pull request
Oct 17, 2026
…rnelSU#3907) Adds a **preinit** mechanism that lets a module run a few actions **before the SELinux policy is loaded and before `init.rc` is parsed** — e.g. load a kernel module, run a static program, or write to procfs/sysfs. A module describes them in a `preinit/` directory: ``` mymodule/ └── preinit/ ├── preinit.conf # one action per line: insmod / exec / write └── ... # .ko files, programs, ... ``` - **Kernel runner**: a static, nolibc-built runner is embedded in the kernel module (`.incbin`) and staged in a shmem file at boot. When PID 1 execs `/system/bin/init selinux_setup`, the execve hook swaps the runner in via `/proc/self/fd/N`. The runner parses each module's `preinit.conf`, performs the actions, then execs the real init. A boot counter disables preinit after 3 failed boots in a row. - **ksud** syncs each enabled module's `preinit/` dir to the preinit partition (manifest-based, incremental, within a space budget on the small `/metadata`). Storage dir is `/metadata/vold/ksu` when available (hidden, does not change `/metadata`'s link count), else `/metadata/watchdog/ksu`, else `/metadata/ksu`. `ksud module preinit status|reset` inspects per-module state and clears the boot counter. - `boot-patch` / `boot-patch-v2` gain `--no-preinit` (sets the `preinit=0` kernel parameter). - **Manager** shows a warning tag on modules whose preinit content was not applied (no space / error / bootloop). - **Docs**: `preinit.conf` format, actions, space budget, bootloop recovery (EN + zh_CN). - Verified on a Redmi K80 (android14-6.1): runner runs before policy load (process context `kernel`), `/metadata` write + fsync survives reboot, bootloop counter parks and `reset` recovers, all conf actions (insmod/exec/write, optional `?`, quoting, exit codes, output forwarding). Confirmed with both v1 (ksuinit in init_boot) and v2 (ko injected into boot). `--no-preinit` disables it (`preinit: disabled`). Charger mode (`androidboot.mode=charger`) is skipped and does not bump the counter. Data lands under `/metadata/vold/ksu` with the `vold_metadata_file` label; `/metadata` root link count is unchanged. - Builds pass for all 8 KMIs (android12-5.10 … android17-6.18). -com.rifsxd.ksunext: Port (Module.kt / ModuleViewModel.kt / ModulePreinit.kt) -Omit: docs: module.md (doesn't exist; will be followed up)
omrxm18
added a commit
to omrxm18/KernelSU-Next
that referenced
this pull request
Oct 17, 2026
…rnelSU#3907) Adds a **preinit** mechanism that lets a module run a few actions **before the SELinux policy is loaded and before is parsed** — e.g. load a kernel module, run a static program, or write to procfs/sysfs. A module describes them in a directory: - **Kernel runner**: a static, nolibc-built runner is embedded in the kernel module () and staged in a shmem file at boot. When PID 1 execs , the execve hook swaps the runner in via . The runner parses each module's , performs the actions, then execs the real init. A boot counter disables preinit after 3 failed boots in a row. - Verified on a Redmi Note 10s (android11-4.19): runner runs before policy load (process context), write + fsync survives reboot, bootloop counter parks and recovers, all conf actions (insmod/exec/write, optional , quoting, exit codes, output forwarding). Confirmed with both v1 (ksuinit in init_boot) and v2 (ko injected into boot). disables it (). Charger mode () is skipped and does not bump the counter. Data lands under with the label; root link count is unchanged.
omrxm18
added a commit
to omrxm18/KernelSU-Next
that referenced
this pull request
Oct 17, 2026
…rnelSU#3907) Adds a **preinit** mechanism that lets a module run a few actions **before the SELinux policy is loaded and before is parsed** — e.g. load a kernel module, run a static program, or write to procfs/sysfs. A module describes them in a directory: - **Kernel runner**: a static, nolibc-built runner is embedded in the kernel module () and staged in a shmem file at boot. When PID 1 execs, the execve hook swaps the runner in via `/proc/self/fd/N`. The runner parses each module's `preinit.conf, performs the actions, then execs the real init. A boot counter disables preinit after 3 failed boots in a row. - Verified on a Redmi Note 10s (android11-4.19): runner runs before policy load (process context `kernel`), `/metadata` write + fsync survives reboot, bootloop counter parks and recovers, all conf actions (insmod/exec/write, optional , quoting, exit codes, output forwarding). Confirmed with both v1 (ksuinit in init_boot) and v2 (ko injected into boot). disables it (). Charger mode () is skipped and does not bump the counter. Data lands under with the label; root link count is unchanged.
omrxm18
added a commit
to omrxm18/KernelSU-Next
that referenced
this pull request
Oct 17, 2026
…rnelSU#3907) Adds a **preinit** mechanism that lets a module run a few actions **before the SELinux policy is loaded and before is parsed** — e.g. load a kernel module, run a static program, or write to procfs/sysfs. A module describes them in a directory: - **Kernel runner**: a static, nolibc-built runner is embedded in the kernel module () and staged in a shmem file at boot. When PID 1 execs, the execve hook swaps the runner in via `/proc/self/fd/N`. The runner parses each module's `preinit.conf, performs the actions, then execs the real init. A boot counter disables preinit after 3 failed boots in a row. - Verified on a Redmi Note 10s (android11-4.19): runner runs before policy load (process context `kernel`), `/metadata` write + fsync survives reboot, bootloop counter parks and recovers, all conf actions (insmod/exec/write. optional quoting, exit codes, output forwarding). Confirmed with both v1 (ksuinit in init_boot) and v2 (ko injected into boot). `--no-preinit` disables it (). Charger mode () is skipped and does not bump the counter. Data lands under with the label; root link count is unchanged.
maxsteeel
pushed a commit
to KernelSU-Next/KernelSU-Next
that referenced
this pull request
Oct 17, 2026
…rnelSU#3907) Adds a **preinit** mechanism that lets a module run a few actions **before the SELinux policy is loaded and before is parsed** — e.g. load a kernel module, run a static program, or write to procfs/sysfs. A module describes them in a directory: - **Kernel runner**: a static, nolibc-built runner is embedded in the kernel module () and staged in a shmem file at boot. When PID 1 execs, the execve hook swaps the runner in via `/proc/self/fd/N`. The runner parses each module's `preinit.conf, performs the actions, then execs the real init. A boot counter disables preinit after 3 failed boots in a row. - Verified on a Redmi Note 10s (android11-4.19): runner runs before policy load (process context `kernel`), `/metadata` write + fsync survives reboot, bootloop counter parks and recovers, all conf actions (insmod/exec/write. optional quoting, exit codes, output forwarding). Confirmed with both v1 (ksuinit in init_boot) and v2 (ko injected into boot). `--no-preinit` disables it (). Charger mode () is skipped and does not bump the counter. Data lands under with the label; root link count is unchanged.
GuitarHeroStyles
added a commit
to GuitarHeroStyles/kernel_samsung_sm7325
that referenced
this pull request
Oct 17, 2026
Pulls in 0b17f242 (tiann/KernelSU#3907): adds a preinit mechanism for modules to run actions before SELinux policy loads. Falls back to disabled with a build warning if UAPI headers for preinit are missing.
github-actions Bot
added a commit
to L7NEG/kernel_samsung_universal9611
that referenced
this pull request
Oct 18, 2026
…rnelSU#3907) Recent legacy changes: 0b17f242 Add module preinit: run actions before SELinux policy loads (tiann/KernelSU#3907) 465490a2 kernel: selinux: never sleep while holding the policy rwlock write side e3292239 legacy: port shell_su_adb_only and force_feature from dev 3.4.2 a56b9199 pkg_observer, sulog, manager: stop the deadlock, log su, unstick the queue 0550f9eb selinux: fix four policy-mutation defects
Anatdx
pushed a commit
to Rouyashiki/YukiSU
that referenced
this pull request
Oct 19, 2026
Port preinit into the C++ module lifecycle and current Manager. Stage a bounded runner at boot, sync enabled modules within the metadata reserve, and retain bootloop recovery, status reporting and the disable option. Add batched kernel symbol lookup while preserving existing YukiSU ioctls. Port tiann/KernelSU#3907: tiann/KernelSU#3907 Upstream commit: 0faec0cc7b5f655170d89bf9c4d28bb5ca271fa2 Co-authored-by: Ylarod <me@ylarod.cn>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds a preinit mechanism that lets a module run a few actions before the SELinux policy is loaded and before
init.rcis parsed — e.g. load a kernel module, run a static program, or write to procfs/sysfs. A module describes them in apreinit/directory:How it works
.incbin) and staged in a shmem file at boot. When PID 1 execs/system/bin/init selinux_setup, the execve hook swaps the runner in via/proc/self/fd/N. The runner parses each module'spreinit.conf, performs the actions, then execs the real init. A boot counter disables preinit after 3 failed boots in a row.preinit/dir to the preinit partition (manifest-based, incremental, within a space budget on the small/metadata). Storage dir is/metadata/vold/ksuwhen available (hidden, does not change/metadata's link count), else/metadata/watchdog/ksu, else/metadata/ksu.ksud module preinit status|resetinspects per-module state and clears the boot counter.boot-patch/boot-patch-v2gain--no-preinit(sets thepreinit=0kernel parameter).preinit.confformat, actions, space budget, bootloop recovery (EN + zh_CN).Testing
kernel),/metadatawrite + fsync survives reboot, bootloop counter parks andresetrecovers, all conf actions (insmod/exec/write, optional?, quoting, exit codes, output forwarding). Confirmed with both v1 (ksuinit in init_boot) and v2 (ko injected into boot).--no-preinitdisables it (preinit: disabled). Charger mode (androidboot.mode=charger) is skipped and does not bump the counter. Data lands under/metadata/vold/ksuwith thevold_metadata_filelabel;/metadataroot link count is unchanged.