Repository navigation
feat: add shell_su_adb_only feature to restrict shell root to adb sessions - #3882
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
The current-process ancestry check makes the target-UID UID_GRANTED_ROOT API incorrectly report shell as ungranted.
1 open finding
What changed in this PR
Adds an opt-in restriction requiring shell root requests to originate from an adbd process ancestry.
Changes:
- Adds feature ID 6 across kernel and ksud.
- Enforces adbd ancestry for shell root authorization.
- Adds persisted Manager toggles and translations.
| File | Description |
|---|---|
userspace/ksud/src/feature_id.rs |
Registers feature ID and metadata. |
userspace/ksud/src/cli.rs |
Updates CLI feature help. |
uapi/feature.h |
Defines the kernel feature ID. |
manager/app/src/main/res/values/strings.xml |
Adds English UI strings. |
manager/app/src/main/res/values-zh-rHK/strings.xml |
Adds Traditional Chinese strings. |
manager/app/src/main/res/values-zh-rCN/strings.xml |
Adds Simplified Chinese strings. |
manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SettingsViewModel.kt |
Loads, persists, and updates feature state. |
manager/app/src/main/java/me/weishu/kernelsu/ui/screen/settings/SettingsUiState.kt |
Adds state and toggle action. |
manager/app/src/main/java/me/weishu/kernelsu/ui/screen/settings/SettingsScreen.kt |
Connects the action to the view model. |
manager/app/src/main/java/me/weishu/kernelsu/ui/screen/settings/SettingsMiuix.kt |
Adds the Miuix toggle. |
manager/app/src/main/java/me/weishu/kernelsu/ui/screen/settings/SettingsMaterial.kt |
Adds the Material toggle. |
manager/app/src/main/java/me/weishu/kernelsu/data/repository/SettingsRepositoryImpl.kt |
Implements ksud feature operations. |
manager/app/src/main/java/me/weishu/kernelsu/data/repository/SettingsRepository.kt |
Extends the settings repository contract. |
kernel/selinux/selinux.h |
Declares adbd context matching. |
kernel/selinux/selinux.c |
Caches and checks the adbd SID. |
kernel/policy/feature.c |
Registers the feature name. |
kernel/policy/allowlist.c |
Applies the ancestry restriction. |
kernel/Kbuild |
Builds the new feature module. |
kernel/feature/shell_su_adb_only.h |
Declares the feature interface. |
kernel/feature/shell_su_adb_only.c |
Implements ancestry checking and feature handlers. |
kernel/core/init.c |
Initializes and unregisters the feature. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…sions Apps that manage to run code as the shell uid (e.g. through a system service bug) would inherit the root grant of com.android.shell. Add an opt-in feature (id 6, disabled by default) that, when shell (uid 2000) asks for root, additionally requires the process to descend from a u:r:adbd:s0 process, i.e. a real `adb shell` session. Ancestors are walked through real_parent up to init (at most 128 levels), so processes detached from adb (reparented to init) are denied. The check only adds a denial on top of the allowlist and never grants root by itself. It is applied in __ksu_is_allow_uid_for_current(), which covers both sucompat and the GRANT_ROOT supercall. Feature id 5 is skipped on purpose: it was webview_zygote_umount in v3.3.0, and reusing it would apply stale persisted values. - kernel: feature/shell_su_adb_only, cache adbd SID - ksud: add FeatureId::ShellSuAdbOnly - manager: add settings toggle
Ylarod
force-pushed
the
feat/shell-su-adb-only
branch
from
October 11, 2026 12:05
499c3e5 to
1ba1ae6
Compare
Modules such as altdb implement their own adb daemon: a root daemon in the ksu domain spawns services that drop to uid 2000 / u:r:shell:s0. Those sessions have no adbd ancestor, so su was denied once shell_su_adb_only was enabled. Also accept a ksu domain ancestor. This is no escalation, as only processes already granted root by KernelSU run in the ksu domain, while a shell process obtained through an app exploit only descends from zygote or init.
do_uid_granted_root() passes an arbitrary target uid to ksu_is_allow_uid_for_current(), so with shell_su_adb_only enabled a query for uid 2000 checked the caller's (e.g. the Manager's) ancestry and reported shell as not granted. Split the grant lookup into ksu_is_uid_granted_root(), which keeps the existing uid 0 handling, and apply the shell ancestry restriction only in ksu_is_allow_uid_for_current() for actual authorization paths.
The kernel trusts ancestors in the ksu domain, not any uid 0 process. Ordinary root system daemons don't satisfy the check, so don't imply they do in the Manager and ksud descriptions.
pershoot
pushed a commit
to KernelSU-Next/KernelSU-Next
that referenced
this pull request
Oct 11, 2026
…sions (tiann/KernelSU#3882) Apps that manage to run code as the shell uid (e.g. through a system service bug) would inherit the root grant of `com.android.shell`. This adds an opt-in feature `shell_su_adb_only` (disabled by default). When it is enabled and shell (uid 2000) asks for root, the process must also descend from a trusted process: - `u:r:adbd:s0`, i.e. a real `adb shell` session, or - the `ksu` domain, i.e. a process already granted root by KernelSU. This covers adb daemons provided by modules such as [altdb](https://github.com/H3XDaemon/altdb), whose root daemon spawns services that drop to uid 2000 / `u:r:shell:s0`. It is no escalation: a shell process obtained through an app exploit only descends from zygote or init. The feature only adds a denial on top of the allowlist and never grants root by itself. Shell still has to be granted, either in the allowlist or with `allow_shell`. - New `feature/shell_su_adb_only.c`, a static-key switch with feature id **6**. - The check runs in `ksu_is_allow_uid_for_current()` when `uid == 2000`. That covers both sucompat (faccessat/stat/execve) and the `GRANT_ROOT` supercall. - `UID_GRANTED_ROOT` stays a pure grant query. The lookup is split into `ksu_is_uid_granted_root()`, which keeps the existing uid 0 handling, so querying uid 2000 does not depend on the caller's (e.g. the Manager's) ancestry. - Ancestors are walked through `real_parent` under RCU, stopping at init. Processes detached from their session (reparented to init) are therefore denied. The walk is capped at 128 levels and denies past that. - The adbd SID is cached in `cache_sid()`, and `is_adbd()` is added next to `is_zygote()`/`is_init()`. Id 5 was `webview_zygote_umount`, which shipped in v3.3.0 and was removed in #3666. `feature save` persists every supported feature, so v3.3.0 users likely have id 5 in `.feature_config`. Reusing the id would silently apply that stale value to this feature, so id 5 is left unused. - `FeatureId::ShellSuAdbOnly` (`ksud feature get/set/check shell_su_adb_only`). - Settings toggle (Material and Miuix). It is persisted through `ksud feature set` + `feature save`, the same way as sulog, and supports the unsupported/managed/forced states. - Wireless debugging from a local app (LADB, Shizuku wireless mode) goes through adbd and is indistinguishable from a real adb session. - A process already in the shell domain could ptrace or inject into an existing adb shell process. That depends on SELinux/Yama and is not handled here. - Daemons started from `adb shell` lose the ability to get root after they detach. This is by design. - Kernel builds with DDK android12-5.10, android14-6.1 and android17-6.18 with no warnings. clang-format is clean. - ksud clippy (host) has no warnings. Manager `compileDebugKotlin` passes. - Tested on a Pixel 6 (Android 16, android14-6.1) booted with `fastboot boot` from the stock `boot.img` patched with this `kernelsu.ko`, with shell granted in the allowlist: | Case | off | on | off again | |---|---|---|---| | `adb shell su -c id` | root | root | root | | nested `sh -c 'sh -c "su -c id"'` in adb shell | root | root | root | | `ksud debug su` (`GRANT_ROOT`) | root | root | root | | `adb shell -tt` (pty) | – | root | – | | detached `setsid` process (PPid=1), `su` | root | **denied** (`su` not redirected) | root | | detached `setsid` process (PPid=1), `GRANT_ROOT` | root | **denied** (`EPERM`) | root | | `altdb doctor --verify-su-isolation` from a detached root process (only a ksu ancestor, no adbd) | ok | ok | ok | The altdb probe drops to the same uid/groups/capabilities and `u:r:shell:s0` as real altdb services, then runs `su`. It got root with the feature enabled, and altdb's own `KSU_NO_NEW_PRIVS` isolation still blocked su. The feature can be toggled at runtime and takes effect immediately. No warnings or RCU stalls in dmesg. - The `UID_GRANTED_ROOT` split was only build-tested. Reproducing the issue on a device needs a caller outside the ksu domain, such as the Manager. -Manager: Single-card (KernelFeaturesCard (Settings.kt))
Anatdx
pushed a commit
to Rouyashiki/YukiSU
that referenced
this pull request
Oct 13, 2026
Add an opt-in ancestry check to existing shell grants while keeping grant queries independent of the caller. Integrate feature ID 6 with C++ ksud, forced boot configuration and the shared Manager feature controls without changing legacy feature IDs or the current module teardown lifecycle. Port tiann/KernelSU#3882: tiann/KernelSU#3882 Upstream commit: 0a1f3ff71719fa2d6ec15f5568dac27fdb957fcc Co-authored-by: Ylarod <me@ylarod.cn>
genie1997
pushed a commit
to genie1997/android_kernel_xiaomi_redwood
that referenced
this pull request
Oct 13, 2026
…sions (tiann/KernelSU#3882) Apps that manage to run code as the shell uid (e.g. through a system service bug) would inherit the root grant of `com.android.shell`. This adds an opt-in feature `shell_su_adb_only` (disabled by default). When it is enabled and shell (uid 2000) asks for root, the process must also descend from a trusted process: - `u:r:adbd:s0`, i.e. a real `adb shell` session, or - the `ksu` domain, i.e. a process already granted root by KernelSU. This covers adb daemons provided by modules such as [altdb](https://github.com/H3XDaemon/altdb), whose root daemon spawns services that drop to uid 2000 / `u:r:shell:s0`. It is no escalation: a shell process obtained through an app exploit only descends from zygote or init. The feature only adds a denial on top of the allowlist and never grants root by itself. Shell still has to be granted, either in the allowlist or with `allow_shell`. - New `feature/shell_su_adb_only.c`, a static-key switch with feature id **6**. - The check runs in `ksu_is_allow_uid_for_current()` when `uid == 2000`. That covers both sucompat (faccessat/stat/execve) and the `GRANT_ROOT` supercall. - `UID_GRANTED_ROOT` stays a pure grant query. The lookup is split into `ksu_is_uid_granted_root()`, which keeps the existing uid 0 handling, so querying uid 2000 does not depend on the caller's (e.g. the Manager's) ancestry. - Ancestors are walked through `real_parent` under RCU, stopping at init. Processes detached from their session (reparented to init) are therefore denied. The walk is capped at 128 levels and denies past that. - The adbd SID is cached in `cache_sid()`, and `is_adbd()` is added next to `is_zygote()`/`is_init()`. Id 5 was `webview_zygote_umount`, which shipped in v3.3.0 and was removed in #3666. `feature save` persists every supported feature, so v3.3.0 users likely have id 5 in `.feature_config`. Reusing the id would silently apply that stale value to this feature, so id 5 is left unused. - `FeatureId::ShellSuAdbOnly` (`ksud feature get/set/check shell_su_adb_only`). - Settings toggle (Material and Miuix). It is persisted through `ksud feature set` + `feature save`, the same way as sulog, and supports the unsupported/managed/forced states. - Wireless debugging from a local app (LADB, Shizuku wireless mode) goes through adbd and is indistinguishable from a real adb session. - A process already in the shell domain could ptrace or inject into an existing adb shell process. That depends on SELinux/Yama and is not handled here. - Daemons started from `adb shell` lose the ability to get root after they detach. This is by design. - Kernel builds with DDK android12-5.10, android14-6.1 and android17-6.18 with no warnings. clang-format is clean. - ksud clippy (host) has no warnings. Manager `compileDebugKotlin` passes. - Tested on a Pixel 6 (Android 16, android14-6.1) booted with `fastboot boot` from the stock `boot.img` patched with this `kernelsu.ko`, with shell granted in the allowlist: | Case | off | on | off again | |---|---|---|---| | `adb shell su -c id` | root | root | root | | nested `sh -c 'sh -c "su -c id"'` in adb shell | root | root | root | | `ksud debug su` (`GRANT_ROOT`) | root | root | root | | `adb shell -tt` (pty) | – | root | – | | detached `setsid` process (PPid=1), `su` | root | **denied** (`su` not redirected) | root | | detached `setsid` process (PPid=1), `GRANT_ROOT` | root | **denied** (`EPERM`) | root | | `altdb doctor --verify-su-isolation` from a detached root process (only a ksu ancestor, no adbd) | ok | ok | ok | The altdb probe drops to the same uid/groups/capabilities and `u:r:shell:s0` as real altdb services, then runs `su`. It got root with the feature enabled, and altdb's own `KSU_NO_NEW_PRIVS` isolation still blocked su. The feature can be toggled at runtime and takes effect immediately. No warnings or RCU stalls in dmesg. - The `UID_GRANTED_ROOT` split was only build-tested. Reproducing the issue on a device needs a caller outside the ksu domain, such as the Manager. -Manager: Single-card (KernelFeaturesCard (Settings.kt)) [genie1997: this tree has no feature name table, so that hunk is dropped; init and exit sit next to selinux_hide's in this tree's order; the setuid hook here also feeds the susfs no_su flag from this check, so a shell refused root is treated as unrooted there too]
ThRE-Team
added a commit
to ThRE-Team/android_kernel_xiaomi_sdm845
that referenced
this pull request
Oct 14, 2026
feat: add shell_su_adb_only feature to restrict shell root to adb sessions (tiann/KernelSU#3882)
ThRE-Team
added a commit
to ThRE-Team/android_kernel_xiaomi_sdm845
that referenced
this pull request
Oct 15, 2026
feat: add shell_su_adb_only feature to restrict shell root to adb sessions (tiann/KernelSU#3882)
AlexLiuDev233
pushed a commit
to Baka-SU/BakaSU
that referenced
this pull request
Oct 21, 2026
…sions (tiann/KernelSU#3882) [cherry-picked from upstream commit tiann/KernelSU@0a1f3ff] Signed-off-by: AlexLiuDev233 <wzylin11@outlook.com>
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.


Apps that manage to run code as the shell uid (e.g. through a system service bug) would inherit the root grant of
com.android.shell. This adds an opt-in featureshell_su_adb_only(disabled by default). When it is enabled and shell (uid 2000) asks for root, the process must also descend from a trusted process:u:r:adbd:s0, i.e. a realadb shellsession, orksudomain, i.e. a process already granted root by KernelSU. This covers adb daemons provided by modules such as altdb, whose root daemon spawns services that drop to uid 2000 /u:r:shell:s0. It is no escalation: a shell process obtained through an app exploit only descends from zygote or init.The feature only adds a denial on top of the allowlist and never grants root by itself. Shell still has to be granted, either in the allowlist or with
allow_shell.Kernel
feature/shell_su_adb_only.c, a static-key switch with feature id 6.ksu_is_allow_uid_for_current()whenuid == 2000. That covers both sucompat (faccessat/stat/execve) and theGRANT_ROOTsupercall.UID_GRANTED_ROOTstays a pure grant query. The lookup is split intoksu_is_uid_granted_root(), which keeps the existing uid 0 handling, so querying uid 2000 does not depend on the caller's (e.g. the Manager's) ancestry.real_parentunder RCU, stopping at init. Processes detached from their session (reparented to init) are therefore denied. The walk is capped at 128 levels and denies past that.cache_sid(), andis_adbd()is added next tois_zygote()/is_init().Why id 6
Id 5 was
webview_zygote_umount, which shipped in v3.3.0 and was removed in #3666.feature savepersists every supported feature, so v3.3.0 users likely have id 5 in.feature_config. Reusing the id would silently apply that stale value to this feature, so id 5 is left unused.ksud / Manager
FeatureId::ShellSuAdbOnly(ksud feature get/set/check shell_su_adb_only).ksud feature set+feature save, the same way as sulog, and supports the unsupported/managed/forced states.Known limitations
adb shelllose the ability to get root after they detach. This is by design.Testing
compileDebugKotlinpasses.fastboot bootfrom the stockboot.imgpatched with thiskernelsu.ko, with shell granted in the allowlist:adb shell su -c idsh -c 'sh -c "su -c id"'in adb shellksud debug su(GRANT_ROOT)adb shell -tt(pty)setsidprocess (PPid=1),susunot redirected)setsidprocess (PPid=1),GRANT_ROOTEPERM)altdb doctor --verify-su-isolationfrom a detached root process (only a ksu ancestor, no adbd)The altdb probe drops to the same uid/groups/capabilities and
u:r:shell:s0as real altdb services, then runssu. It got root with the feature enabled, and altdb's ownKSU_NO_NEW_PRIVSisolation still blocked su. The feature can be toggled at runtime and takes effect immediately. No warnings or RCU stalls in dmesg.UID_GRANTED_ROOTsplit was only build-tested. Reproducing the issue on a device needs a caller outside the ksu domain, such as the Manager.