348 Commits

Author SHA1 Message Date
shirkneko
d5be402710 manager: fix sulog error
Some checks failed
Build Manager / build-lkm (push) Has been cancelled
Build Manager / build-user_scanner (ubuntu-latest, All-linux-android) (push) Has been cancelled
Build Manager / build-ksud (ubuntu-latest, aarch64-linux-android) (push) Has been cancelled
Build Manager / build-ksud (ubuntu-latest, armv7-linux-androideabi) (push) Has been cancelled
Build Manager / build-ksud (ubuntu-latest, x86_64-linux-android) (push) Has been cancelled
Build Manager / build-manager (false) (push) Has been cancelled
Build Manager / build-manager (true) (push) Has been cancelled
2025-11-27 21:55:22 +08:00
YuKongA
809b74a5f3 manager: fix the color of TextField in TemplateEditor 2025-11-27 20:39:39 +08:00
YuKongA
a98a718d61 manager: add new AID 2025-11-27 20:35:39 +08:00
shirkneko
45ea8455fc kernel: fix build 2025-11-27 20:33:01 +08:00
YuKongA
f81023246f manager: add markdown-ext-tables 2025-11-27 19:33:58 +08:00
YuKongA
8b06df3468 manager: fix Markdown table rendering 2025-11-27 19:31:31 +08:00
YuKongA
46cfd936a0 userspace: add feature get --config
* fix #2980
2025-11-27 19:28:30 +08:00
YuKongA
de04ea9db0 manager: redo fetchAppList onCreate 2025-11-27 19:19:52 +08:00
YuKongA
3853928305 manager: add basic module repo support 2 2025-11-27 19:19:36 +08:00
weishu
5e64eee624 kernel: Fix execve filename access on ARM64 2025-11-27 19:12:10 +08:00
YuKongA
3a97e6580f manager: add basic module repo support 2025-11-27 19:11:31 +08:00
YuKongA
228b6b1273 manager: fix dark theme color 2025-11-27 19:08:19 +08:00
YuKongA
314fbe8cf7 manager: fix SearchPager color 2025-11-27 19:04:06 +08:00
YuKongA
ccb38061ee manager: upgrade ndk29 2025-11-27 19:03:56 +08:00
weishu
b631344e7c kernel: Add preempt_{disable|enable}_notrace for MODULE 2025-11-27 19:02:10 +08:00
weishu
85d739a153 sucompat: Fix execve filename access on ARM64 2025-11-27 19:00:35 +08:00
KOWX712
9817724a10 manager: provide monet color to webui (#2981)
Provide app color scheme using css variable in suPath, follow MMRL monet
color scheme standard since some module has already support this for a
while. This will not break current module's WebUI, it is an opt-in
feature, you'll need to import before using it. Example:
```css
@import url('https://mui.kernelsu.org/internal/colors.css');
:root {
    --my-background-color: var(--surface, #FEF7FF);
    --my-text-color: var(--onSurface, #1D1B20);
}
```
This is only provided when monet color is selected in settings.

Co-Authored-By: Der_Googler
<54764558+dergoogler@users.noreply.github.com>
Co-Authored-By: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
Signed-off-by: KOWX712 <leecc0503@gmail.com>

---------

Signed-off-by: KOWX712 <leecc0503@gmail.com>
Co-authored-by: Der_Googler <54764558+dergoogler@users.noreply.github.com>
Co-authored-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-26 17:14:54 +08:00
Wang Han
7c7e72f111 kernel: Remove unreachable vfs_statx handling 2025-11-26 17:11:40 +08:00
5ec1cff
c5d473c815 ksud: refine boot patch, add --out-name arg to boot-patch and boot-restore command (#2982) 2025-11-24 11:35:53 +08:00
ShirkNeko
f2de18bc26 kernel: Avoid duplicating the built in uninstall path adder with the path manager. 2025-11-24 11:33:30 +08:00
ShirkNeko
61f5785729 manager: Remove unused susfs functionality status 2025-11-24 00:04:11 +08:00
ShirkNeko
a7713f0445 manager: Updated the susfs binary file
- made the umount manager publicly
available

- and removed the “try umount” functionality
2025-11-23 23:16:29 +08:00
Wang Han
0109723187 kernel: Unmount all isolated process which forks from zygote
Kernel has few information about which isolated process belongs to which
application, so there is actually no good choice if we don't implement a
userspace daemon. One choice is to access cmdline memory from kernel,
but cmdline is __user, and it is likely to trigger detections. Before we
have more good ideas, use this.
2025-11-23 19:17:54 +08:00
ShirkNeko
15fe454b6d Step 7-2: Removed redundant pop-ups from the susfs interface and fixed path issues (on Android 16). 2025-11-23 13:32:06 +08:00
ShirkNeko
5c80febdbd manager: Deprecated AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT, the leftover add_sus_mount cli and umount_for_zygote_system_process
Reason:
 - AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT is also causing a bit more performance overheads and still it cannot catch all the sus mounts in all situations. Actually it can easily be done in boot-completed.sh, and it should be more accurate, see module templates for more details.

- Official KernelSU also allows ksud to add custom path to try_umount list as well, users can use their own way to add only the desired sus mounts to try_umount list, but remember to disable susfs ADD_TRY_UMOUNT in kernel if users want to use the official one.

- There are less use cases for umount_for_zygote_system_process, and sometimes enabling this may cause bootloop with some modules enabled, instead user can use busybox nsenter to umount the sus mounts for specific process later by themmselves.
2025-11-22 22:54:10 +08:00
ShirkNeko
39f4a5991a Add cursor rules 2025-11-22 22:08:52 +08:00
Ylarod
b2565fda08 ksud: fmt 2025-11-22 17:41:53 +08:00
Wang Han
923ba8c213 ksud: Set KSU_MODULE only for module script (#2971) 2025-11-22 17:41:49 +08:00
Ylarod
c94608a2eb ksud: config set support read from stdin, and less restriction 2025-11-22 17:41:46 +08:00
Ylarod
ccb59cb7ca ksud: larger config value size limit, update docs 2025-11-22 17:15:57 +08:00
Tools-app
36d93501c8 Fix compile on x86_64
Co-authored-by: weishu <twsxtd@gmail.com>
Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-11-22 16:58:25 +08:00
生于生时 亡于亡刻
27f6db889a chore(ksud): enable clippy::all, clippy::pedantic && make clippy happy (#617)
* Revert "chore(ksud): bump ksud's deps (#585)"
* Because it may cause compilation errors.

This reverts commit c8020b2066.

* chore(ksud): remove unused Result

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* chore(ksud): enable clippy::all, clippy::pedantic && make clippy happy

https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or

https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args

https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items

https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls

https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
...
and use some #![allow(...)] or #[allow(...)]

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

---------

Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-11-22 16:58:19 +08:00
ShirkNeko
6898d82daf Step 7-1: Optimize the susfs interface format and refactor the code
- Remove unused resources
2025-11-22 16:56:51 +08:00
ShirkNeko
8d8d0180ae manager: fix build 2025-11-22 04:22:01 +08:00
ShirkNeko
f7b875fc16 Revert "Step 7: Add Custom Background (beta):"
This reverts commit a585989a03.
2025-11-22 04:16:24 +08:00
ShirkNeko
0d73908d1b Step 7: Add susfs interface 2025-11-22 04:16:04 +08:00
5ec1cff
3dd210cfec manager: no need to check overlayfs 2025-11-22 00:56:04 +08:00
YuKongA
18c65c8495 manager: try fix HyperOS2- edgeToEdge 2025-11-21 22:53:33 +08:00
KOWX712
4f9b745cd0 manager: make inset synchronize 2025-11-21 22:25:58 +08:00
ShirkNeko
a585989a03 Step 7: Add Custom Background (beta): 2025-11-21 22:24:22 +08:00
weishu
ba6f29557e meta-overlayfs: Moved to module repo 2025-11-21 14:03:20 +08:00
Wang Han
79b78e35ba ksud: Use regex to validate module id (#2968)
https://github.com/tiann/KernelSU/blob/main/website/docs/guide/module.md?plain=1#L106
2025-11-21 13:10:15 +08:00
ShirkNeko
932fabd35c Step 6: feat: add direct zip flash for AnyKernel3 and modules
- fix Chrome zip open failure
- one-tap flash AnyKernel3 kernel packages
- bulk install with state de-duplication
- refine share UI & color scheme

---------------------------------
Co-Authored-By: Der_Googler <54764558+dergoogler@users.noreply.github.com>
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: KOWX712 <leecc0503@gmail.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-21 13:08:54 +08:00
weishu
4ea5c8f450 metaovl: Fix incorrect permission, Add updateJson and changelog 2025-11-21 11:12:56 +08:00
ShirkNeko
c6b184793e Step 5-2: Simplify and separate the main logic for flashing anykernel3 2025-11-21 01:48:33 +08:00
Ylarod
e3ef521de5 add module config, migrate managedFeatures (#2965)
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
2025-11-20 22:06:12 +08:00
ShirkNeko
3d4e0e48b4 [skip ci]ksud: fmt & clippy 2025-11-20 20:30:06 +08:00
ShirkNeko
ff3071ca08 Step 5-1-1: No longer need to add unmounting for default mount points 2025-11-20 19:53:18 +08:00
weishu
dd969eac22 meta-overlayfs: quote rel path removal 2025-11-20 19:08:19 +08:00
weishu
9f2e5f513d metaovl: copy selinux context when install 2025-11-20 19:08:02 +08:00
ShirkNeko
385f4ab2c5 Step 5-1: Move the KPM interface to the settings
- Avoid multiple page re-rendering
- Add hook type information
- Clean up code
2025-11-20 18:38:53 +08:00
ShirkNeko
6826406494 manager: fix dark mode color issue
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
2025-11-20 17:01:54 +08:00
ShirkNeko
6465e7a874 Step 5: Add a settings tool page to migrate some settings to it
- Add SELinux status toggle

- Add backup and restore for the allowlist
2025-11-20 16:28:37 +08:00
ShirkNeko
c753dd1345 ci: Fix omitted checks 2025-11-20 14:39:45 +08:00
weishu
06c8580788 metaovl: Use xcp to copy image faster. 2025-11-20 14:33:25 +08:00
ShirkNeko
5f228f1896 ci :Skip the disguised manager build for the MIUIX branch 2025-11-20 14:28:57 +08:00
weishu
2368c5afd5 metaovl: use cp instead of mv to copy files 2025-11-20 14:02:19 +08:00
ShirkNeko
16ec695b63 kernel/makefile: Adjust the build branch 2025-11-20 14:02:05 +08:00
ShirkNeko
404352b536 Step 4-1: Fixed incorrect homepage indexing after enabling KPM
- Adjusted the position of the personalized menu
2025-11-20 13:59:40 +08:00
ShirkNeko
8e7f1f1cc7 manager: Support monet colors
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
2025-11-20 12:02:49 +08:00
ShirkNeko
d2a6fa4513 Step 4: Add KPM interface and flash anykernel3 2025-11-20 03:18:13 +08:00
YuKongA
9574409955 manager: fix dialog text of multiple modules install 2025-11-19 23:50:14 +08:00
weishu
9c2924de78 meta-overlayfs: avoid moving skip-mount modules 2025-11-19 23:50:07 +08:00
ShirkNeko
d7878ddd45 manager: If SELinuxStatus is the last information component, set the margin to 0. 2025-11-19 23:49:24 +08:00
ShirkNeko
bc3399fd1b Step 3: Added theme mode switching, introduced uninstall path manager and user-mode scanning toggle 2025-11-19 23:39:07 +08:00
ShirkNeko
ba1aaaa160 ksud: fix build 2025-11-19 21:18:53 +08:00
weishu
a4e5a571bd ksud: Fix the metamodule's non-meta stage script, which is executed twice. 2025-11-19 21:11:10 +08:00
ShirkNeko
3c501295b7 Step 2: Add the remaining dynamic manager configurations 2025-11-19 21:10:34 +08:00
ShirkNeko
a8acea9180 support metamodule, remove built-in overlayfs mount
Co-authored-by: weishu <twsxtd@gmail.com>
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
Co-authored-by: Ylarod <me@ylarod.cn>
2025-11-19 19:33:01 +08:00
ShirkNeko
4f79c94ab9 Step 1: Import susfs and sulog to modify 2025-11-19 18:45:00 +08:00
ShirkNeko
a14551b3ec Introducing miuix
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
2025-11-19 16:11:33 +08:00
ShirkNeko
2ea748dac1 manager: add inset support to webui (#2952)
ref:
https://github.com/MMRLApp/WebUI-X-Portable/blob/master/webui/src/main/kotlin/com/dergoogler/mmrl/webui/model/Insets.kt

Co-Authored-By: Der_Googler
<54764558+dergoogler@users.noreply.github.com>
Signed-off-by: KOWX712 <leecc0503@gmail.com>

---------

Co-authored-by: KOWX712 <leecc0503@gmail.com>
Co-authored-by: Der_Googler <54764558+dergoogler@users.noreply.github.com>
Co-authored-by: Light_summer <93428659+lightsummer233@users.noreply.github.com>
2025-11-19 14:23:49 +08:00
ShirkNeko
429874b4d6 manager: fix webui package manager
Co-authored-by: KOWX712 <leecc0503@gmail.com>
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
2025-11-19 13:13:11 +08:00
ShirkNeko
cd86589ad3 manager: Fixed an error caused by the line count identifier being omitted in the Umount Path Manager. 2025-11-18 13:49:21 +08:00
ShirkNeko
22cb7596a7 ksud: fmt & fix build 2025-11-18 12:44:27 +08:00
backslashxx
029ae8d389 kernel: expose umount list to ioctl interface (#2950)
This idea is borrowed from simonpunk's susfs4ksu.
What we see here is that, yeah well, lets just have userspace send us
what it
wants unmounted, this is better than hardcoding everything.

This also solves that issue where MNT_DETACH fails, as long as we send
unmountables in proper order.

A small anti-duplicate mechanism is also added.

While in-kernel umount is a bit worse than zygisk-provider-based ones,
this can still
serve as a healthy alternative.

---------

- Remove duplicate checks

Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
Co-authored-by: weishu <twsxtd@gmail.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-18 12:36:42 +08:00
Wang Han
58c8289890 kernel/ksud: Fix KSU_IOCTL_NUKE_EXT4_SYSFS definition 2025-11-18 12:17:00 +08:00
weishu
94fa1e360a ksud: Add cli interface for nuke_ext4_sysfs 2025-11-18 12:15:49 +08:00
weishu
1d1ce396d3 kernel: Add nuke_ext4_sysfs interface 2025-11-18 12:12:06 +08:00
ShirkNeko
99d58c8cfd manager: Only display the option to use LKM files when the kernel version is greater than 5.10 2025-11-18 00:34:19 +08:00
ShirkNeko
ad2a23f55e manager: Move certain settings to the Advanced Settings section. 2025-11-17 12:37:53 +08:00
ShirkNeko
f48d2e6cac manger: fmt code 2025-11-17 12:20:45 +08:00
ShirkNeko
d45676f059 manager: bump susfs assets 2025-11-17 11:46:32 +08:00
AlexLiuDev233
0439a00f1d manager: increase appGroup performance & add appGroup status notice 2025-11-16 23:02:15 +08:00
ShirkNeko
cbcaa07fd5 manager: Set up in real time using the correct method 2025-11-16 17:19:48 +08:00
ShirkNeko
c4d8c49e5c kernel/manager/ksud: Add switch functionality to sulog
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-16 16:53:55 +08:00
生于生时 亡于亡刻
c8020b2066 chore(ksud): bump ksud's deps (#585)
Name                                              Project            Compat   Latest   Kind         Platform
----                                              -------            ------   ------   ----         --------
addr2line->gimli                                  0.31.1             Removed  Removed  Normal       ---
ahash->cfg-if                                     1.0.0              Removed  Removed  Normal       ---
ahash->once_cell                                  1.21.3             Removed  Removed  Normal       cfg(not(all(target_arch = "arm", target_os = "none")))
ahash->version_check                              0.9.5              Removed  Removed  Build        ---
ahash->zerocopy                                   0.8.25             Removed  Removed  Normal       ---
android_logger                                    0.14.1             ---      0.15.1   Normal       cfg(target_os = "android")
android_logger->env_filter                        0.1.3              0.1.4    0.1.4    Normal       ---
android_logger->log                               0.4.27             0.4.28   0.4.28   Normal       ---
android_system_properties->libc                   0.2.172            0.2.177  0.2.177  Normal       ---
anstream->anstyle                                 1.0.10             1.0.13   1.0.13   Normal       ---
anstream->anstyle-parse                           0.2.6              0.2.7    0.2.7    Normal       ---
anstream->anstyle-query                           1.1.2              1.1.5    1.1.5    Normal       ---
anstream->anstyle-wincon                          3.0.7              3.0.11   3.0.11   Normal       cfg(windows)
anstream->colorchoice                             1.0.3              1.0.4    1.0.4    Normal       ---
anstream->is_terminal_polyfill                    1.70.1             1.70.2   1.70.2   Normal       ---
anstyle-query->windows-sys                        0.59.0             0.61.2   0.61.2   Normal       cfg(windows)
anstyle-wincon->anstyle                           1.0.10             1.0.13   1.0.13   Normal       ---
anstyle-wincon->once_cell                         1.21.3             Removed  Removed  Normal       cfg(windows)
anstyle-wincon->windows-sys                       0.59.0             0.61.2   0.61.2   Normal       cfg(windows)
anyhow                                            1.0.98             1.0.100  1.0.100  Normal       ---
arbitrary->derive_arbitrary                       1.4.1              1.4.2    1.4.2    Normal       ---
async-trait->proc-macro2                          1.0.95             1.0.103  1.0.103  Normal       ---
async-trait->quote                                1.0.40             1.0.42   1.0.42   Normal       ---
async-trait->syn                                  2.0.101            2.0.110  2.0.110  Normal       ---
backtrace->addr2line                              0.24.2             Removed  Removed  Normal       cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))
backtrace->cfg-if                                 1.0.0              Removed  Removed  Normal       ---
backtrace->libc                                   0.2.172            Removed  Removed  Normal       cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))
backtrace->miniz_oxide                            0.8.8              Removed  Removed  Normal       cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))
backtrace->object                                 0.36.7             Removed  Removed  Normal       cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))
backtrace->rustc-demangle                         0.1.24             Removed  Removed  Normal       ---
backtrace->windows-targets                        0.52.6             Removed  Removed  Normal       cfg(any(windows, target_os = "cygwin"))
cc->shlex                                         1.3.0              ---      Removed  Normal       ---
chrono                                            0.4.41             0.4.42   0.4.42   Normal       ---
chrono->android-tzdata                            0.1.1              Removed  Removed  Normal       cfg(target_os = "android")
chrono->iana-time-zone                            0.1.63             0.1.64   0.1.64   Normal       cfg(unix)
chrono->js-sys                                    0.3.77             0.3.82   0.3.82   Normal       cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))
chrono->wasm-bindgen                              0.2.100            0.2.105  0.2.105  Normal       cfg(all(target_arch = "wasm32", not(any(target_os = "emscripten", target_os = "wasi"))))
chrono->windows-link                              0.1.3              0.2.1    0.2.1    Normal       cfg(windows)
clap                                              4.5.38             4.5.51   4.5.51   Normal       ---
clap->clap_builder                                4.5.38             4.5.51   4.5.51   Normal       ---
clap->clap_derive                                 4.5.32             4.5.49   4.5.49   Normal       ---
clap_builder->anstream                            0.6.18             0.6.21   0.6.21   Normal       ---
clap_builder->anstyle                             1.0.10             1.0.13   1.0.13   Normal       ---
clap_builder->clap_lex                            0.7.4              0.7.6    0.7.6    Normal       ---
clap_derive->proc-macro2                          1.0.95             1.0.103  1.0.103  Normal       ---
clap_derive->quote                                1.0.40             1.0.42   1.0.42   Normal       ---
clap_derive->syn                                  2.0.101            2.0.110  2.0.110  Normal       ---
const_format                                      0.2.34             0.2.35   0.2.35   Normal       ---
const_format_proc_macros->proc-macro2             1.0.95             1.0.103  1.0.103  Normal       ---
const_format_proc_macros->quote                   1.0.40             1.0.42   1.0.42   Normal       ---
core2->memchr                                     2.7.4              2.7.6    2.7.6    Normal       ---
cpufeatures->libc                                 0.2.172            0.2.177  0.2.177  Normal       aarch64-linux-android
crc->crc-catalog                                  2.4.0              ---      Removed  Normal       ---
crc32fast->cfg-if                                 1.0.0              1.0.4    1.0.4    Normal       ---
crossbeam-channel->crossbeam-utils                0.8.21             ---      Removed  Normal       ---
crypto-common->typenum                            1.18.0             1.19.0   1.19.0   Normal       ---
deranged->powerfmt                                0.2.0              ---      Removed  Normal       ---
derive-new->proc-macro2                           1.0.95             1.0.103  1.0.103  Normal       ---
derive-new->quote                                 1.0.40             1.0.42   1.0.42   Normal       ---
derive-new->syn                                   2.0.101            2.0.110  2.0.110  Normal       ---
derive_arbitrary->proc-macro2                     1.0.95             1.0.103  1.0.103  Normal       ---
derive_arbitrary->quote                           1.0.40             1.0.42   1.0.42   Normal       ---
derive_arbitrary->syn                             2.0.101            2.0.110  2.0.110  Normal       ---
digest->crypto-common                             0.1.6              0.1.7    0.1.7    Normal       ---
encoding_rs->cfg-if                               1.0.0              1.0.4    1.0.4    Normal       ---
env_filter->log                                   0.4.27             0.4.28   0.4.28   Normal       ---
env_logger->env_filter                            0.1.3              0.1.4    0.1.4    Normal       ---
env_logger->log                                   0.4.27             0.4.28   0.4.28   Normal       ---
errno->libc                                       0.2.172            0.2.177  0.2.177  Normal       cfg(target_os = "hermit")
errno->windows-sys                                0.59.0             0.61.2   0.61.2   Normal       cfg(windows)
errno-dragonfly->cc                               1.2.22             1.2.46   1.2.46   Build        ---
errno-dragonfly->libc                             0.2.172            0.2.177  0.2.177  Normal       ---
extattr->libc                                     0.2.172            0.2.177  0.2.177  Normal       ---
filetime->cfg-if                                  1.0.0              1.0.4    Removed  Normal       ---
filetime->libc                                    0.2.172            0.2.177  Removed  Normal       cfg(unix)
filetime->libredox                                0.1.9              0.1.10   Removed  Normal       cfg(target_os = "redox")
filetime->windows-sys                             0.60.2             ---      Removed  Normal       cfg(windows)
flate2->crc32fast                                 1.4.2              1.5.0    1.5.0    Normal       ---
flate2->miniz_oxide                               0.8.8              0.8.9    0.8.9    Normal       ---
fsevent-sys->libc                                 0.2.172            0.2.177  0.2.177  Normal       ---
generic-array->typenum                            1.18.0             1.19.0   1.19.0   Normal       ---
getopts                                           0.2.21             0.2.24   0.2.24   Normal       ---
getopts->unicode-width                            0.1.14             0.2.2    0.2.2    Normal       ---
getrandom->cfg-if                                 1.0.0              1.0.4    1.0.4    Normal       ---
getrandom->libc                                   0.2.172            0.2.177  0.2.177  Normal       cfg(all(any(target_os = "linux", target_os = "android"), not(any(all(target_os = "linux", target_env = ""), getrandom_backend = "custom", getrandom_backend = "linux_raw", getrandom_backend = "rdrand", getrandom_backend = "rndr"))))
getrandom->r-efi                                  5.2.0              5.3.0    5.3.0    Normal       cfg(all(target_os = "uefi", getrandom_backend = "efi_rng"))
getrandom->wasi                                   0.14.2+wasi-0.2.4  Removed  Removed  Normal       cfg(all(target_arch = "wasm32", target_os = "wasi", target_env = "p2"))
hashbrown->ahash                                  0.8.12             Removed  Removed  Normal       ---
iana-time-zone->js-sys                            0.3.77             0.3.82   0.3.82   Normal       cfg(all(target_arch = "wasm32", target_os = "unknown"))
iana-time-zone->log                               0.4.27             0.4.28   0.4.28   Normal       cfg(all(target_arch = "wasm32", target_os = "unknown"))
iana-time-zone->wasm-bindgen                      0.2.100            0.2.105  0.2.105  Normal       cfg(all(target_arch = "wasm32", target_os = "unknown"))
iana-time-zone->windows-core                      0.61.0             0.62.2   0.62.2   Normal       cfg(target_os = "windows")
iana-time-zone-haiku->cc                          1.2.22             1.2.46   1.2.46   Build        ---
include-flate->include-flate-codegen              0.2.0              0.3.1    0.3.1    Normal       ---
include-flate->lazy_static                        1.5.0              Removed  Removed  Normal       ---
include-flate->libflate                           2.1.0              2.2.1    2.2.1    Normal       ---
include-flate-codegen->libflate                   2.1.0              2.2.1    2.2.1    Normal       ---
include-flate-codegen->proc-macro2                1.0.95             1.0.103  1.0.103  Normal       ---
include-flate-codegen->quote                      1.0.40             1.0.42   1.0.42   Normal       ---
include-flate-codegen->syn                        2.0.101            2.0.110  2.0.110  Normal       ---
indexmap->hashbrown                               0.15.2             0.16.0   0.16.0   Normal       ---
inotify->bitflags                                 1.3.2              ---      2.10.0   Normal       ---
inotify->libc                                     0.2.172            0.2.177  0.2.177  Normal       ---
inotify-sys->libc                                 0.2.172            0.2.177  0.2.177  Normal       ---
is_executable                                     1.0.4              1.0.5    1.0.5    Normal       ---
is_executable->winapi                             0.3.9              Removed  Removed  Normal       cfg(target_os = "windows")
java-properties->regex-lite                       0.1.6              0.1.8    0.1.8    Normal       ---
js-sys->wasm-bindgen                              0.2.100            0.2.105  0.2.105  Normal       ---
jwalk->rayon                                      1.10.0             1.11.0   1.11.0   Normal       ---
kqueue->libc                                      0.2.172            0.2.177  0.2.177  Normal       ---
kqueue-sys->libc                                  0.2.172            0.2.177  0.2.177  Normal       ---
libc                                              0.2.172            0.2.177  0.2.177  Normal       ---
libflate->crc32fast                               1.4.2              1.5.0    1.5.0    Normal       ---
libflate->dary_heap                               0.3.7              0.3.8    0.3.8    Normal       ---
libflate->libflate_lz77                           2.1.0              2.2.0    2.2.0    Normal       ---
libflate_lz77->hashbrown                          0.14.5             0.16.0   0.16.0   Normal       ---
libredox->bitflags                                2.8.0              2.10.0   Removed  Normal       ---
libredox->libc                                    0.2.172            0.2.177  Removed  Normal       ---
libredox->redox_syscall                           0.5.17             0.5.18   Removed  Normal       ---
log                                               0.4.27             0.4.28   0.4.28   Normal       ---
lzma-rs->byteorder                                1.5.0              ---      Removed  Normal       ---
lzma-rs->crc                                      3.3.0              ---      Removed  Normal       ---
lzma-sys->cc                                      1.2.22             1.2.46   1.2.46   Build        ---
lzma-sys->cc                                      1.2.22             1.2.46   Removed  Build        ---
lzma-sys->libc                                    0.2.172            0.2.177  0.2.177  Normal       ---
lzma-sys->libc                                    0.2.172            0.2.177  Removed  Normal       ---
lzma-sys->pkg-config                              0.3.32             ---      Removed  Build        ---
miniz_oxide->adler2                               2.0.0              2.0.1    2.0.1    Normal       ---
miniz_oxide->adler2                               2.0.0              Removed  Removed  Normal       ---
mio->libc                                         0.2.172            0.2.177  0.2.177  Normal       cfg(target_os = "wasi")
mio->log                                          0.4.27             0.4.28   0.4.28   Normal       ---
mio->windows-sys                                  0.48.0             ---      0.61.2   Normal       cfg(windows)
nom->memchr                                       2.7.4              2.7.6    2.7.6    Normal       ---
notify                                            6.1.1              ---      8.2.0    Normal       ---
notify->bitflags                                  2.8.0              2.10.0   2.10.0   Normal       cfg(target_os = "macos")
notify->crossbeam-channel                         0.5.15             ---      Removed  Normal       ---
notify->filetime                                  0.2.26             ---      Removed  Normal       ---
notify->inotify                                   0.9.6              ---      0.11.0   Normal       cfg(any(target_os = "linux", target_os = "android"))
notify->libc                                      0.2.172            0.2.177  0.2.177  Normal       ---
notify->log                                       0.4.27             0.4.28   0.4.28   Normal       ---
notify->mio                                       0.8.11             ---      1.1.0    Normal       cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "dragonflybsd"))
notify->windows-sys                               0.48.0             ---      0.60.2   Normal       cfg(windows)
num-traits->autocfg                               1.4.0              1.5.0    1.5.0    Build        ---
object->memchr                                    2.7.4              Removed  Removed  Normal       ---
proc-macro2->unicode-ident                        1.0.18             1.0.22   1.0.22   Normal       ---
proc-macro2->unicode-ident                        1.0.18             1.0.22   Removed  Normal       ---
proc-macro2->unicode-ident                        1.0.18             Removed  Removed  Normal       ---
quote->proc-macro2                                1.0.95             1.0.103  1.0.103  Normal       ---
quote->proc-macro2                                1.0.95             1.0.103  Removed  Normal       ---
quote->proc-macro2                                1.0.95             Removed  Removed  Normal       ---
rayon->rayon-core                                 1.12.1             1.13.0   1.13.0   Normal       ---
redox_syscall->bitflags                           2.8.0              2.10.0   Removed  Normal       ---
regex-lite                                        0.1.6              0.1.8    0.1.8    Normal       ---
rust-embed                                        8.7.2              8.9.0    8.9.0    Normal       ---
rust-embed->include-flate                         0.3.0              0.3.1    0.3.1    Normal       ---
rust-embed->rust-embed-impl                       8.7.2              8.9.0    8.9.0    Normal       ---
rust-embed->rust-embed-utils                      8.7.2              8.9.0    8.9.0    Normal       ---
rust-embed-impl->proc-macro2                      1.0.95             1.0.103  1.0.103  Normal       ---
rust-embed-impl->quote                            1.0.40             1.0.42   1.0.42   Normal       ---
rust-embed-impl->rust-embed-utils                 8.7.2              8.9.0    8.9.0    Normal       ---
rust-embed-impl->syn                              2.0.101            2.0.110  2.0.110  Normal       ---
rustix                                            0.38.34            1.1.2    1.1.2    Normal       cfg(any(target_os = "android", target_os = "linux"))
rustix->bitflags                                  2.8.0              2.10.0   2.10.0   Normal       ---
rustix->errno                                     0.3.10             0.3.14   0.3.14   Development  ---
rustix->itoa                                      1.0.15             Removed  Removed  Normal       ---
rustix->libc                                      0.2.172            0.2.177  0.2.177  Development  ---
rustix->linux-raw-sys                             0.4.15             0.11.0   0.11.0   Normal       cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", target_endian = "little", any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))
rustix->linux-raw-sys                             0.9.4              0.11.0   0.11.0   Normal       cfg(all(any(target_os = "android", target_os = "linux"), any(rustix_use_libc, miri, not(all(target_os = "linux", any(target_endian = "little", any(target_arch = "s390x", target_arch = "powerpc")), any(target_arch = "arm", all(target_arch = "aarch64", target_pointer_width = "64"), target_arch = "riscv64", all(rustix_use_experimental_asm, target_arch = "powerpc"), all(rustix_use_experimental_asm, target_arch = "powerpc64"), all(rustix_use_experimental_asm, target_arch = "s390x"), all(rustix_use_experimental_asm, target_arch = "mips"), all(rustix_use_experimental_asm, target_arch = "mips32r6"), all(rustix_use_experimental_asm, target_arch = "mips64"), all(rustix_use_experimental_asm, target_arch = "mips64r6"), target_arch = "x86", all(target_arch = "x86_64", target_pointer_width = "64")))))))
rustix->once_cell                                 1.21.3             Removed  Removed  Normal       cfg(any(target_os = "android", target_os = "linux"))
rustix->windows-sys                               0.52.0             0.61.2   0.61.2   Normal       cfg(windows)
rustix->windows-sys                               0.59.0             0.61.2   0.61.2   Normal       cfg(windows)
same-file->winapi-util                            0.1.9              0.1.11   0.1.11   Normal       cfg(windows)
serde                                             1.0.219            1.0.228  1.0.228  Normal       ---
serde->serde_derive                               1.0.219            1.0.228  1.0.228  Normal       ---
serde->serde_derive                               1.0.219            1.0.228  Removed  Normal       ---
serde_derive->proc-macro2                         1.0.95             1.0.103  1.0.103  Normal       ---
serde_derive->proc-macro2                         1.0.95             1.0.103  Removed  Normal       ---
serde_derive->quote                               1.0.40             1.0.42   1.0.42   Normal       ---
serde_derive->quote                               1.0.40             1.0.42   Removed  Normal       ---
serde_derive->syn                                 2.0.101            2.0.110  2.0.110  Normal       ---
serde_derive->syn                                 2.0.101            2.0.110  Removed  Normal       ---
serde_json                                        1.0.140            1.0.145  1.0.145  Normal       ---
serde_json->memchr                                2.7.4              2.7.6    2.7.6    Normal       ---
serde_json->serde                                 1.0.219            1.0.228  1.0.228  Normal       ---
sha1->cfg-if                                      1.0.0              1.0.4    1.0.4    Normal       ---
sha2->cfg-if                                      1.0.0              1.0.4    1.0.4    Normal       ---
sha256->async-trait                               0.1.88             0.1.89   0.1.89   Normal       ---
sha256->bytes                                     1.10.1             1.11.0   1.11.0   Normal       ---
sha256->tokio                                     1.45.0             1.48.0   1.48.0   Normal       ---
syn->proc-macro2                                  1.0.95             1.0.103  1.0.103  Normal       ---
syn->proc-macro2                                  1.0.95             1.0.103  Removed  Normal       ---
syn->proc-macro2                                  1.0.95             Removed  Removed  Normal       ---
syn->quote                                        1.0.40             1.0.42   1.0.42   Normal       ---
syn->quote                                        1.0.40             1.0.42   Removed  Normal       ---
syn->quote                                        1.0.40             Removed  Removed  Normal       ---
syn->unicode-ident                                1.0.18             1.0.22   1.0.22   Normal       ---
syn->unicode-ident                                1.0.18             1.0.22   Removed  Normal       ---
syn->unicode-ident                                1.0.18             Removed  Removed  Normal       ---
tempfile                                          3.20.0             3.23.0   3.23.0   Normal       ---
tempfile->getrandom                               0.3.3              0.3.4    0.3.4    Normal       cfg(any(unix, windows, target_os = "wasi"))
tempfile->rustix                                  1.0.7              1.1.2    1.1.2    Normal       cfg(any(unix, target_os = "wasi"))
tempfile->windows-sys                             0.59.0             0.61.2   0.61.2   Normal       cfg(windows)
time->deranged                                    0.4.0              0.5.5    0.5.5    Normal       ---
time->deranged                                    0.4.0              0.5.5    Removed  Normal       ---
time->num-conv                                    0.1.0              ---      Removed  Normal       ---
time->powerfmt                                    0.2.0              ---      Removed  Normal       ---
time->serde                                       1.0.219            1.0.228  1.0.228  Normal       ---
time->serde                                       1.0.219            1.0.228  Removed  Normal       ---
time->time-core                                   0.1.4              0.1.6    0.1.6    Normal       ---
time->time-core                                   0.1.4              0.1.6    Removed  Normal       ---
tokio->backtrace                                  0.3.75             Removed  Removed  Normal       cfg(tokio_taskdump)
tokio->bytes                                      1.10.1             1.11.0   1.11.0   Normal       ---
walkdir->winapi-util                              0.1.9              0.1.11   0.1.11   Normal       cfg(windows)
wasi->wit-bindgen-rt                              0.39.0             Removed  Removed  Normal       ---
wasm-bindgen->cfg-if                              1.0.0              1.0.4    1.0.4    Normal       ---
wasm-bindgen->rustversion                         1.0.20             1.0.22   1.0.22   Normal       ---
wasm-bindgen->wasm-bindgen-macro                  0.2.100            0.2.105  0.2.105  Normal       ---
wasm-bindgen-backend->bumpalo                     3.17.0             Removed  Removed  Normal       ---
wasm-bindgen-backend->log                         0.4.27             Removed  Removed  Normal       ---
wasm-bindgen-backend->proc-macro2                 1.0.95             Removed  Removed  Normal       ---
wasm-bindgen-backend->quote                       1.0.40             Removed  Removed  Normal       ---
wasm-bindgen-backend->syn                         2.0.101            Removed  Removed  Normal       ---
wasm-bindgen-backend->wasm-bindgen-shared         0.2.100            Removed  Removed  Normal       ---
wasm-bindgen-macro->quote                         1.0.40             1.0.42   1.0.42   Normal       ---
wasm-bindgen-macro->wasm-bindgen-macro-support    0.2.100            0.2.105  0.2.105  Normal       ---
wasm-bindgen-macro-support->proc-macro2           1.0.95             1.0.103  1.0.103  Normal       ---
wasm-bindgen-macro-support->quote                 1.0.40             1.0.42   1.0.42   Normal       ---
wasm-bindgen-macro-support->syn                   2.0.101            2.0.110  2.0.110  Normal       ---
wasm-bindgen-macro-support->wasm-bindgen-backend  0.2.100            Removed  Removed  Normal       ---
wasm-bindgen-macro-support->wasm-bindgen-shared   0.2.100            0.2.105  0.2.105  Normal       ---
wasm-bindgen-shared->unicode-ident                1.0.18             1.0.22   1.0.22   Normal       ---
wasm-bindgen-shared->unicode-ident                1.0.18             Removed  Removed  Normal       ---
which                                             7.0.3              ---      8.0.0    Normal       ---
which->either                                     1.15.0             ---      Removed  Normal       ---
which->rustix                                     1.0.7              1.1.2    1.1.2    Normal       cfg(any(unix, target_os = "wasi", target_os = "redox"))
winapi->winapi-i686-pc-windows-gnu                0.4.0              Removed  Removed  Normal       i686-pc-windows-gnu
winapi->winapi-x86_64-pc-windows-gnu              0.4.0              Removed  Removed  Normal       x86_64-pc-windows-gnu
winapi-util->windows-sys                          0.59.0             0.61.2   0.61.2   Normal       cfg(windows)
windows-core->windows-implement                   0.60.0             0.60.2   0.60.2   Normal       ---
windows-core->windows-interface                   0.59.1             0.59.3   0.59.3   Normal       ---
windows-core->windows-link                        0.1.3              0.2.1    0.2.1    Normal       ---
windows-core->windows-result                      0.3.2              0.4.1    0.4.1    Normal       ---
windows-core->windows-strings                     0.4.0              0.5.1    0.5.1    Normal       ---
windows-implement->proc-macro2                    1.0.95             1.0.103  1.0.103  Normal       ---
windows-implement->quote                          1.0.40             1.0.42   1.0.42   Normal       ---
windows-implement->syn                            2.0.101            2.0.110  2.0.110  Normal       ---
windows-interface->proc-macro2                    1.0.95             1.0.103  1.0.103  Normal       ---
windows-interface->quote                          1.0.40             1.0.42   1.0.42   Normal       ---
windows-interface->syn                            2.0.101            2.0.110  2.0.110  Normal       ---
windows-result->windows-link                      0.1.3              0.2.1    0.2.1    Normal       ---
windows-strings->windows-link                     0.1.3              0.2.1    0.2.1    Normal       ---
windows-sys->windows-targets                      0.48.5             ---      0.53.5   Normal       ---
windows-sys->windows-targets                      0.48.5             ---      Removed  Normal       ---
windows-sys->windows-targets                      0.52.6             Removed  Removed  Normal       ---
windows-sys->windows-targets                      0.53.3             0.53.5   Removed  Normal       ---
windows-targets->windows-link                     0.1.3              0.2.1    Removed  Normal       cfg(windows_raw_dylib)
windows-targets->windows_aarch64_gnullvm          0.48.5             ---      0.53.1   Normal       aarch64-pc-windows-gnullvm
windows-targets->windows_aarch64_gnullvm          0.48.5             ---      Removed  Normal       aarch64-pc-windows-gnullvm
windows-targets->windows_aarch64_gnullvm          0.52.6             Removed  Removed  Normal       aarch64-pc-windows-gnullvm
windows-targets->windows_aarch64_gnullvm          0.53.0             0.53.1   Removed  Normal       aarch64-pc-windows-gnullvm
windows-targets->windows_aarch64_msvc             0.48.5             ---      0.53.1   Normal       cfg(all(target_arch = "aarch64", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_aarch64_msvc             0.48.5             ---      Removed  Normal       cfg(all(target_arch = "aarch64", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_aarch64_msvc             0.52.6             Removed  Removed  Normal       cfg(all(target_arch = "aarch64", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_aarch64_msvc             0.53.0             0.53.1   Removed  Normal       cfg(all(target_arch = "aarch64", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_i686_gnu                 0.48.5             ---      0.53.1   Normal       cfg(all(target_arch = "x86", target_env = "gnu", not(windows_raw_dylib)))
windows-targets->windows_i686_gnu                 0.48.5             ---      Removed  Normal       cfg(all(target_arch = "x86", target_env = "gnu", not(windows_raw_dylib)))
windows-targets->windows_i686_gnu                 0.52.6             Removed  Removed  Normal       cfg(all(target_arch = "x86", target_env = "gnu", not(target_abi = "llvm"), not(windows_raw_dylib)))
windows-targets->windows_i686_gnu                 0.53.0             0.53.1   Removed  Normal       cfg(all(target_arch = "x86", target_env = "gnu", not(target_abi = "llvm"), not(windows_raw_dylib)))
windows-targets->windows_i686_gnullvm             0.52.6             Removed  Removed  Normal       i686-pc-windows-gnullvm
windows-targets->windows_i686_gnullvm             0.53.0             0.53.1   Removed  Normal       i686-pc-windows-gnullvm
windows-targets->windows_i686_msvc                0.48.5             ---      0.53.1   Normal       cfg(all(target_arch = "x86", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_i686_msvc                0.48.5             ---      Removed  Normal       cfg(all(target_arch = "x86", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_i686_msvc                0.52.6             Removed  Removed  Normal       cfg(all(target_arch = "x86", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_i686_msvc                0.53.0             0.53.1   Removed  Normal       cfg(all(target_arch = "x86", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_x86_64_gnu               0.48.5             ---      0.53.1   Normal       cfg(all(target_arch = "x86_64", target_env = "gnu", not(target_abi = "llvm"), not(windows_raw_dylib)))
windows-targets->windows_x86_64_gnu               0.48.5             ---      Removed  Normal       cfg(all(target_arch = "x86_64", target_env = "gnu", not(target_abi = "llvm"), not(windows_raw_dylib)))
windows-targets->windows_x86_64_gnu               0.52.6             Removed  Removed  Normal       cfg(all(target_arch = "x86_64", target_env = "gnu", not(target_abi = "llvm"), not(windows_raw_dylib)))
windows-targets->windows_x86_64_gnu               0.53.0             0.53.1   Removed  Normal       cfg(all(target_arch = "x86_64", target_env = "gnu", not(target_abi = "llvm"), not(windows_raw_dylib)))
windows-targets->windows_x86_64_gnullvm           0.48.5             ---      0.53.1   Normal       x86_64-pc-windows-gnullvm
windows-targets->windows_x86_64_gnullvm           0.48.5             ---      Removed  Normal       x86_64-pc-windows-gnullvm
windows-targets->windows_x86_64_gnullvm           0.52.6             Removed  Removed  Normal       x86_64-pc-windows-gnullvm
windows-targets->windows_x86_64_gnullvm           0.53.0             0.53.1   Removed  Normal       x86_64-pc-windows-gnullvm
windows-targets->windows_x86_64_msvc              0.48.5             ---      0.53.1   Normal       cfg(all(target_arch = "x86_64", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_x86_64_msvc              0.48.5             ---      Removed  Normal       cfg(all(target_arch = "x86_64", target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_x86_64_msvc              0.52.6             Removed  Removed  Normal       cfg(all(any(target_arch = "x86_64", target_arch = "arm64ec"), target_env = "msvc", not(windows_raw_dylib)))
windows-targets->windows_x86_64_msvc              0.53.0             0.53.1   Removed  Normal       cfg(all(any(target_arch = "x86_64", target_arch = "arm64ec"), target_env = "msvc", not(windows_raw_dylib)))
wit-bindgen-rt->bitflags                          2.8.0              Removed  Removed  Normal       ---
xz2->lzma-sys                                     0.1.20             ---      Removed  Normal       ---
zerocopy->zerocopy-derive                         0.8.25             Removed  Removed  Normal       ---
zerocopy-derive->proc-macro2                      1.0.95             Removed  Removed  Normal       ---
zerocopy-derive->quote                            1.0.40             Removed  Removed  Normal       ---
zerocopy-derive->syn                              2.0.101            Removed  Removed  Normal       ---
zip                                               3.0.0              ---      6.0.0    Normal       ---
zip->arbitrary                                    1.4.1              1.4.2    1.4.2    Normal       cfg(fuzzing)
zip->crc32fast                                    1.4.2              1.5.0    1.5.0    Normal       ---
zip->deflate64                                    0.1.9              0.1.10   0.1.10   Normal       ---
zip->deflate64                                    0.1.9              0.1.10   Removed  Normal       ---
zip->flate2                                       1.1.1              1.1.5    1.1.5    Normal       ---
zip->indexmap                                     2.9.0              2.12.0   2.12.0   Normal       ---
zip->lzma-rs                                      0.3.0              ---      Removed  Normal       ---
zip->memchr                                       2.7.4              2.7.6    2.7.6    Normal       ---
zip->time                                         0.3.41             0.3.44   0.3.44   Normal       ---
zip->time                                         0.3.41             0.3.44   Removed  Normal       ---
zip->xz2                                          0.1.7              ---      Removed  Normal       ---
zip->zopfli                                       0.8.2              0.8.3    0.8.3    Normal       ---
zopfli->bumpalo                                   3.17.0             3.19.0   3.19.0   Normal       ---
zopfli->crc32fast                                 1.4.2              1.5.0    1.5.0    Normal       ---
zopfli->log                                       0.4.27             0.4.28   0.4.28   Normal       ---

Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-11-16 15:47:11 +08:00
ShirkNeko
8442ebcb7a kernel: Optimise multi manager scanning logic 2025-11-16 15:35:26 +08:00
Wang Han
25fbc22f66 kernel: Replace kmalloc() usages with kzalloc() (#2939)
This ensures we won't use uninitialized pointers for task work.
2025-11-16 15:24:49 +08:00
ShirkNeko
a4a9df3a25 kernel: fmt 2025-11-16 14:29:10 +08:00
ShirkNeko
19a67fb76c mamager: fix the crash of Superuser in multi-user environments 2025-11-16 14:03:06 +08:00
AlexLiuDev233
3a2d55237d manager: make group shows like upstream & add some animation to SuperUser menu i18n file pick from upstream
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
2025-11-16 13:34:26 +08:00
生于生时 亡于亡刻
98e617f1bd refactor(ksud): refactor ksud's kpm
- remove useless code
- ignore create dir all return
- when kpm file delete failed, return Error
2025-11-16 07:11:16 +08:00
AlexLiuDev233
d84a88f059 manager: disable webuix exitConfirm 2025-11-16 00:58:32 +08:00
ShirkNeko
ae7b4dcbed manager: fix MarkdownContent scrollBehavior
Co-authored-by: AlexLiuDev233 <wzylin11@outlook.com>
2025-11-16 00:02:29 +08:00
ShirkNeko
684a5d1ccd manager: display the same UID as a group
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-15 23:44:38 +08:00
Tools-app
35b02e3c73 ci: remove cache for ksud
Because it no truth save

Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-11-15 19:57:40 +08:00
5ec1cff
db6a59ec4e kernel: file_wrapper: copy mode of original inode
Bionic uses fstat to determine whether an fd is a tty and set proper
buffering flags, so we also need to set the wrapper file's inode mode to
the original inode mode.

see:
https://cs.android.com/android/platform/superproject/main/+/main:bionic/libc/upstream-openbsd/lib/libc/stdio/makebuf.c;l=61-95;drc=9a4b68e20d617b2cb3355071521f16e8c3d538df
2025-11-15 19:54:16 +08:00
ShirkNeko
46846dce91 kernel: Utilise the existing try_umount to avoid repackaging 2025-11-15 19:29:12 +08:00
Tools-app
cd21af6728 ci: fix shared-key not find (ksud)
Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-11-15 18:56:19 +08:00
生于生时 亡于亡刻
92a483d222 ksud: Optimise ksud (#576)
* opt: Optimize printing for result isn't successful
- print chain && backtrace to stderr

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* ci(ksud): add CARGO_TERM_COLOR for build

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* fix(ksud): fix uninstall package name is `me.weishu.kernelsu`

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* fix(ksud): Reverted to a private function for get_kernel_version

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* chore: format code for magic_mount.rs

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* fix: fix rustfmt

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* feat: Move the statement to the correct place && remove allow dead_code

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

---------

Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-11-15 18:25:17 +08:00
5ec1cff
04ca981e4d kernel: no need to remark process on post-fs-data and boot-completed
- Remark on post-fs-data may unmark zygote unexpectedly, and there is no
necessity to remark on these stages, so simply remove them.
2025-11-15 17:27:48 +08:00
ShirkNeko
83209a5259 ci: Fixed build information 2025-11-15 12:47:11 +08:00
YC酱luyancib
3cfc6d6a31 Manager/CI: Add spoofed manager build (#570)
* manager: add script for randomizing pkg name

Signed-off-by: rsuntk <rsuntk@yukiprjkt.my.id>
Co-Authored-By: YC酱luyancib <luyancib@qq.com>

* manager: Update randomizer to avoid bug

Revert "manager: Update randomizer to avoid bug"

This reverts commit af9205e70a11db3e3d43bb34de880bbd6b4185b5.

manager: Update randomizer to avoid bug

* mamager: Update randomizer to fix error
fixed
> Task :app:compileReleaseAidl FAILED
ERROR: /home/runner/work/SukiSU-Ultra/SukiSU-Ultra/manager/app/src/main/aidl/org/knifhr/zako/IKsuInterface.aidl:7.1-10: IKsuInterface should be declared in a file called com/sukisu/zako/IKsuInterface.aidl

* manager:clean randomizer

* ci:add spoofed manager build with matrix

* 更新 build-manager.yml

* 更新 build-manager.yml

* fix

* Revert "更新 build-manager.yml"

This reverts commit da80e3f13a.

* ksubot: make MESSAGE_THREAD_ID optional

---------

Signed-off-by: rsuntk <rsuntk@yukiprjkt.my.id>
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
Co-authored-by: 5ec1cff <ewtqyqyewtqyqy@gmail.com>
2025-11-15 10:58:10 +08:00
Wang Han
01ac06c3fd kernel: Prune allowlist only after boot completed
For unknown reason, packages.list is not reliable during boot for oplus
devices, so we have to disable pruning and re-run pruning after boot.
2025-11-15 10:54:19 +08:00
ShirkNeko
906c4bdb01 manager: bump susfs version to 2.0.0
- Remove versions below susfs 2.0.0 that are no longer supported.
2025-11-14 21:53:17 +08:00
weishu
c17d7b38eb ci: fix android16-6.12 gki build (#2931) 2025-11-14 19:02:48 +08:00
KOWX712
fa57ccccf4 manager: introduce webui package manager api (#2928)
this is a squash of:

* manager: introduce app package info API for webui-next
(KernelSU-Next/KernelSU-Next@58167a4)

* manager: sort a-z order for webui-next list packages api
(KernelSU-Next/KernelSU-Next@4a9733c)

* manager: implement getPackagesIcons and cacheAllPackageIcons api to
webui-next (KernelSU-Next/KernelSU-Next@a361fa3)

* manager/webui: let getPackagesIcons generate icon and store in cache
as well when called (KernelSU-Next/KernelSU-Next@6afa86d)

* POC: load icon app via ksu://icon/[packageName] (KernelSU-Next#674)
(KernelSU-Next/KernelSU-Next@bc9927b)

* manager: refine webui package manager (KOWX712/KernelSU@0400c42)

Co-Authored-By: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
Co-Authored-By: Fahrez256Bit
<167403685+fahrez256@users.noreply.github.com>
Signed-off-by: KOWX712 <leecc0503@gmail.com>

---------

Signed-off-by: KOWX712 <leecc0503@gmail.com>
Co-authored-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
Co-authored-by: Fahrez256Bit <167403685+fahrez256@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-14 19:02:21 +08:00
ShirkNeko
7e7713ee4a kernel/sulog: Optimise the sulog log format 2025-11-14 12:55:57 +08:00
ShirkNeko
7afcdb3059 kernel: Optimise sulog to prevent deadlocks caused by global lock contexts. 2025-11-14 12:29:55 +08:00
ShirkNeko
5df821ed41 kernel: Resolved potential deadlock issues arising from operations not being performed within locks. 2025-11-14 11:55:06 +08:00
YuKongA
12fc2e6d5e userspace: remove vendor_boot selection logic
- Only patch it when user actively selects vendor_boot.
2025-11-13 20:36:44 +08:00
5ec1cff
9f869090d2 kernel: refine syscall_hook_manager
- Don't unmark process when setuid if syscall tracepoint is in use
- Remark process when app profile updated
- Ensure zygote is marked on first boot
2025-11-13 20:09:32 +08:00
ShirkNeko
bb8b991110 kernel: restart enable sulog for LKM 2025-11-13 17:46:08 +08:00
ShirkNeko
10548f9243 kernel: fix build 2025-11-13 17:06:27 +08:00
weishu
96d33e62bb kernel: fix zygote mark on first boot (#2924) 2025-11-13 16:31:26 +08:00
ShirkNeko
7be8c15b85 kernel: Remove redundant ksu_handle_inode_permission hooks, calling only for manual_su 2025-11-13 15:02:20 +08:00
ShirkNeko
14bb6afd0b New Crowdin updates (#568)
* New translations strings.xml (Russian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Chinese Traditional, Hong Kong)
2025-11-13 13:23:31 +08:00
ShirkNeko
54dad4ceb2 assets: bump KPM version to 0.12.2 2025-11-12 18:52:22 +08:00
ShirkNeko
a60395ba35 manager: install: do not always recommended to select boot partition if not rooted
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
2025-11-12 18:04:49 +08:00
5ec1cff
095385f814 kernel: fix wrong show_fdinfo impl
Signed-off-by: Wang Han <416810799@qq.com>
2025-11-12 13:25:20 +08:00
ShirkNeko
7e595e1730 kernel: Public ksu_handle_sys_reboot
- clean code
2025-11-11 16:49:09 +08:00
Wang Han
ded31c2043 kernel: Fix task flag marking for root and shell UID
Signed-off-by: Wang Han <416810799@qq.com>
Co-authored-by: 5ec1cff <56485584+5ec1cff@users.noreply.github.com>
2025-11-11 16:26:37 +08:00
ShirkNeko
a7f840d811 manager: fix template status display issue (#2910)
manager: 修复模板状态显示问题 (#2910)

修复当 app 设置过模板后再使用自定义配置,离开页面后再次打开,管理器显示应用使用的是模板而不是自定义配置的问题

- bump AGP version

Co-authored-by: u9521 <63995396+u9521@users.noreply.github.com>
2025-11-11 16:00:54 +08:00
Ylarod
05cca26075 ci: move workflow_dispatch to versioned ci workflow
Signed-off-by: Faris <rissu.ntk@gmail.com>
2025-11-11 15:40:09 +08:00
ShirkNeko
5ce6c210c4 manager: install: add choose partition support
manager: fix KsuCli cmd

userspace: reuse choose_boot_device

- manager: simplify find boot image

Co-authored-by: weishu <twsxtd@gmail.com>
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-11 15:16:11 +08:00
ShirkNeko
8f49898155 kernel: Use task work to install fd
There are many fd related functions that can sleep, so we have no choice
but move operations to task work. Also close fd when copy_to_user fails.

Co-authored-by: Wang Han <416810799@qq.com>
2025-11-11 01:10:36 +08:00
ShirkNeko
fd3a22360a kernel: Clean code build 2025-11-09 23:18:48 +08:00
Wang Han
ef36a36e9a kernel: Don't save allowlist on module exit
This is not needed and may trigger UAF as work is async.
2025-11-09 23:14:13 +08:00
ShirkNeko
7a1a08064b manager: After flashing anykernel3, release ksud. 2025-11-09 21:26:08 +08:00
AlexLiuDev233
36862d6175 manager: if manager incompatible with current kernel, don't save ksud (#2895)
I think we should'nt install kernelsu's userspace when manager
incompatible with current kernel
this maybe cause a lot of bug, for example, when user install 2.x
kernelsu manager, but not update
his kernel to 2.x, ksud will return "kernel version 0", because ksud
incompatible with old supercall impl
2025-11-09 21:14:40 +08:00
ShirkNeko
cda7e4c6c0 Clean up kernel code (#2898)
1) Fix memory leak of callback head in allowlist.c
2) Remove duplicated logic and incorrect log in kernel_umount.c
3) Prevent sleep in kprobe context in ksud.c
4) Remove useless is_unsupported_uid, use euid for security enhance,
   add FIXME in setuid_hook.c
5) Remove useless fd argument for execve hook, fix incorrent pointer
   usage in syscall_hook_manager.c and sucompat.c
6) Use correct errno in supercalls.c

---------

Co-authored-by: Ylarod <me@ylarod.cn>
2025-11-09 19:20:30 +08:00
ShirkNeko
0b63cc445c kernel: Expose the ksu_handle_sys_read hook 2025-11-09 18:11:30 +08:00
ShirkNeko
2433ced81a kernel: Rollback disable_seccomp
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: Faris <rissu.ntk@gmail.com>
2025-11-09 17:00:43 +08:00
ShirkNeko
1f04f13e44 kernel: Remove redundant checks 2025-11-09 16:41:54 +08:00
ShirkNeko
184467c691 kernel: Undo some changes 2025-11-09 16:09:59 +08:00
ShirkNeko
05ed1a3714 fix build 2025-11-09 14:17:27 +08:00
ShirkNeko
163531fcd2 kernel: Simplify state management logic 2025-11-09 14:07:40 +08:00
ShirkNeko
049956aaa9 Fixes only hook in LKM mode __NR_newfstatat ,__NR_faccessat ,__NR_execve system calls 2025-11-09 12:16:23 +08:00
ShirkNeko
6530d06710 kernel: clean build 2025-11-09 04:12:11 +08:00
ShirkNeko
88135d8363 Attempt to refactor and migrate inode_permission, bprm_check_security, and task_alloc entirely to syscall_hook_manager 2025-11-09 02:52:46 +08:00
ShirkNeko
548258f922 kernel: Rewrite the kernel source code (#554)
* clean unused header

* on_module_mounted in ksud.c

* refact: use app_profile

* unified hook manager

* add zygote to hook target

* move reboot hook to supercall.c

* refactor: kernel_umount setuid_hook

* update mark rules, add init mark tracker

* remove reboot from check_syscall_fastpath

* update setuid_hook, remove uneeded sucompat enable

* log freely

* kernel: Migrate kprobe hook configuration items

* kernel: fix build

* cli: add ksud debug mark

* Fix rustfmt warning

---------

Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: Wang Han <416810799@qq.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-09 01:14:26 +08:00
weishu
46b9f5fb4b kernel: fix put_task if alloc failed. 2025-11-08 21:44:04 +08:00
ShirkNeko
413e9ab8a9 kernel: Resolved compatibility issues with su when using manual hooks 2025-11-08 20:52:29 +08:00
ShirkNeko
c3644da85b assets: Bump KPM version to 0.12.1 2025-11-08 20:05:59 +08:00
生于生时 亡于亡刻
7b4b5b431f fix: fix ksud install error (#550)
Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-11-08 17:31:58 +05:30
ShirkNeko
7479c0b81b kernel: Fix build 2025-11-08 19:54:20 +08:00
ShirkNeko
0381d12be2 kernel: Resolve implicit declaration conflicts 2025-11-08 19:47:03 +08:00
Wang Han
4425c88d5a Fix missing unlock on error path 2025-11-08 19:41:11 +08:00
weishu
7828c5c107 kernel: fix save allowlist 2025-11-08 19:40:16 +08:00
ShirkNeko
76046c84cd kernel: remove unused kernel_compat
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-08 19:39:41 +08:00
weishu
623dd15cbf kernel: Use real_parent to avoid interference from ptrace. 2025-11-08 19:30:55 +08:00
ShirkNeko
ab13ed5c16 kernel: remove unused wrapper for
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-08 19:30:44 +08:00
weishu
e171ca15cb kernel: remove ksu_compat_{open,read,write} because we're in the right context now
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-08 19:28:25 +08:00
ShirkNeko
4fc369a059 kernel: remove workqueue for allowlist
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-08 19:18:20 +08:00
ShirkNeko
18ad2afadb Reworking fdwrapper
Co-authored-by: 5ec1cff <ewtqyqyewtqyqy@gmail.com>
Co-authored-by: 5ec1cff <56485584+5ec1cff@users.noreply.github.com>
Co-authored-by: Ylarod <me@ylarod.cn>
2025-11-08 19:03:14 +08:00
ShirkNeko
3badbcd4bc assets: Bump KPM version to 0.13.0 2025-11-08 18:38:53 +08:00
technotic
1b5b235bd9 add include for crc32.h in sulog.h (for dedup_calc_hash) (#548)
Co-authored-by: technotic <pixie@technotic.us>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-08 18:12:38 +08:00
AlexLiuDev233
fdf5e7104e refactor: kpm: memory management migrate to sukisu side (#539)
* refactor: kpm: memory management migrate to sukisu side

* fix: build warning in some gki2 device

fix stack frame size warning (maybe) in gki2 device, specialy in ShirkNeko's device

* chore: use pr_info instead of printk

* feat: check the validity of pointers sent from user space
Sometimes, ksud or other root processes might request a kpm ioctl,
but data incorrectly, such as invalid pointer,
which cause the kernel to crash.

If the request is made by ksud at boot time, the situation is even worse,
as it can cause the system to enter the boot loop.

Therefore, I believe we need to check pointer integrity in kernel space to fix this problem.

---------

Co-authored-by: Saksham <typeflu@gmail.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-08 18:11:29 +08:00
生于生时 亡于亡刻
ed6b2e0a8e opt: Optimize the kpm && uid_scanner (#549)
* opt: Optimize the structure of kpm.rs

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* opt: Optimize the uid_scanner startup logic in userspace && code style

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* opt: rename kpm's ioctl

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* opt: rename ksucalls::KsuKpmCmd's arg2..arg5

using
```rust
pub struct KsuKpmCmd {
    pub control_code: u64,
    pub arg1: u64,
    pub arg2: u64,
    pub result_code: u64,
}
```
This makes it easier to distinguish parameters.

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

---------

Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-11-08 13:43:32 +05:30
AlexLiuDev233
704f7cba32 kernel: core_hook: disable seccomp in 5.10.2- for allowed uids (#545)
* kernel: core_hook: disable seccomp in 5.10.2- for allowed uids

we dont have those new fancy things upstream has
lets just do original thing where we disable seccomp

* Update kernel/core_hook.c

* fmt

---------

Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
Co-authored-by: Saksham <saksham.mac@icloud.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-08 12:46:25 +08:00
ShirkNeko
860bdce295 ksud :fmt 2025-11-08 12:41:03 +08:00
生于生时 亡于亡刻
d8a8ef6458 fix: fix self exe path error && opt: Optimize ensure_dir_exists processing (#541)
* fix: fix self exe path error

* opt: Optimize ensure_dir_exists processing

---------

Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-11-08 12:34:22 +08:00
ShirkNeko
d37a78ea2d fix build & cleanup 2025-11-08 11:48:35 +08:00
Ylarod
6c9bf69718 fix build 2025-11-08 11:37:07 +08:00
Ylarod
776bcc4d5d rename to proxy_file 2025-11-08 11:36:15 +08:00
ShirkNeko
bf5cb885b5 kernel: remove devpts hook 2025-11-08 11:28:23 +08:00
Wang Han
a533a490bd Use force_sig(SIGKILL) to kill process 2025-11-08 01:34:44 +08:00
Shadichy
c6d9f76c7b kernel: Refactor selinux/selinux.c (#2881)
Signed-off-by: shadichy <shadichy@blisslabs.org>
Co-authored-by: Wang Han <416810799@qq.com>
2025-11-08 01:34:23 +08:00
Wang Han
66032391af Switch kretprobe to heap (#2880)
Co-authored-by: Ylarod <me@ylarod.cn>
2025-11-07 18:39:53 +08:00
Wang Han
da0e16bd26 Replace mutex with spinlock for tracepoint registration (#2882) 2025-11-07 18:37:12 +08:00
ShirkNeko
53d763cdf9 manager: Implement editable and removable mount points for LKM 2025-11-07 15:37:04 +08:00
ShirkNeko
9ebddde0d5 kernel: Avoid calling umount whilst holding a spinlock. 2025-11-07 14:23:58 +08:00
ShirkNeko
03a164ebb7 kernel: By default, MNT_DETACH is used as the value for the mount point. 2025-11-07 13:37:09 +08:00
ShirkNeko
4769065cfc ksud: Implementing editable, removable mount points 2025-11-07 13:15:07 +08:00
ShirkNeko
9b209765c4 kernel: Implementing editable, removable mount points 2025-11-07 12:16:42 +08:00
ShirkNeko
d7c101e244 kernel: Fix compilation 2025-11-07 11:33:02 +08:00
Ylarod
a32f89403b ci: update build 2025-11-07 11:12:21 +08:00
ShirkNeko
2cd673d776 manager: fix where the option displays "Temporary enable" after device reboot
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
2025-11-07 11:08:27 +08:00
Ylarod
14fea6f8a3 build: remove -Wno-implicit-function-declaration 2025-11-07 11:02:48 +08:00
ShirkNeko
02f1aec6e9 ksud: Fix clippy 2025-11-07 00:11:02 +08:00
5ec1cff
826661dffb feature: add devpts fd wrapper (#21)
This feature is intended to resolve devpts problem.
2025-11-06 23:56:53 +08:00
ShirkNeko
f86c71efc5 Compilation fix 2025-11-06 23:26:55 +08:00
Ylarod
06018a2f03 revert: still using workqueue for allowlist 2025-11-06 23:05:29 +08:00
Ylarod
a2193841d5 skip init_features in safe mode 2025-11-06 23:03:43 +08:00
Ylarod
1324a7f54e fix: enhanced security register 2025-11-06 23:02:27 +08:00
Ylarod
5df9431a22 kill pgrp in enhanced security 2025-11-06 23:01:59 +08:00
Ylarod
e54989e51a add mutex for sucompat mark 2025-11-06 23:01:45 +08:00
Ylarod
cf50be122e fix: sucompat (#2874)
Co-authored-by: Wang Han <416810799@qq.com>
2025-11-06 23:01:28 +08:00
ShirkNeko
7f9048724f fix 2025-11-06 22:57:13 +08:00
ShirkNeko
3dde6d9a25 manager: some ui changes
* Steeing: add enhanced security dropdown
* Settings: allow change module update check
* Settings: allow always enable/disable feat
* misc: update all deps

---------

Co-authored-by: weishu <twsxtd@gmail.com>
Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-06 22:54:43 +08:00
ShirkNeko
132e9ef8ed kernel: Resolve compilation issues 2025-11-06 13:48:26 +08:00
ShirkNeko
e6436b340c kernel: clean headers
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-06 13:08:01 +08:00
weishu
9cdf98782d kernel: Set the tracepoint flag in a tracepoint manner 2025-11-06 13:02:13 +08:00
Ylarod
dece57cacf feature: add enhanced security (#2873) 2025-11-06 12:59:09 +08:00
ShirkNeko
3f07ea29ae manager: remove outdated lkm condition
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-06 12:58:44 +08:00
weishu
c8e103062a kernel: remove unused workqueue 2025-11-06 12:56:00 +08:00
Ylarod
91312effba fix sepolicy patch hint (#2872) 2025-11-06 12:53:09 +08:00
Ylarod
fd60cda3b3 fix: mark tif (#2871) 2025-11-06 12:52:52 +08:00
ShirkNeko
5323a500dd kernel: use sys_enter tracepoint for sucompat (#533)
* use sys_enter tracepoint for sucompat

* update sucompat rules

* clean tif mark

* mark tif after load allow list

* clear all tif first, then mark target

* Fix shell su

* allow when escape

* fix bugs

* kernel: Resolve logical inconsistencies

---------

Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-06 12:45:37 +08:00
ShirkNeko
0ce7bc2627 kernel: Migrate manual_su to ioctl 2025-11-06 02:52:14 +08:00
libingxuan
c9c62b25d2 support mainline kernel (#2869) 2025-11-05 23:00:42 +08:00
ShirkNeko
f8904b1b02 kernel: Resolved permission verification issue 2025-11-05 19:04:51 +08:00
ShirkNeko
89ce65e8ba Use a more appropriate minor version number 2025-11-05 16:41:30 +08:00
ShirkNeko
994fdfddf2 kernel: Use CONFIG_KSU_MANUAL_SU to protect MANUAL_SU 2025-11-05 16:17:54 +08:00
ShirkNeko
557e7f8153 bump KSU_VERSION_API to 4.0.0 2025-11-05 16:07:23 +08:00
backslashxx
9e9bb685f0 kernel: supercall: allow escalation on ioctl interface (#2862)
Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
2025-11-05 15:52:00 +08:00
Wang Han
99bec0e439 Fix legacy prctl check condition (#2864) 2025-11-05 15:47:03 +08:00
ShirkNeko
0400b94674 v2.0.0 2025-11-05 15:43:10 +08:00
ShirkNeko
247f7d4aee Kernel: Enable processes with corresponding UIDs to utilise netlink, and optimise netlink functionality 2025-11-05 15:41:14 +08:00
ShirkNeko
088ce97697 kernel: Remove prctl; use netlink communication to control manual_su 2025-11-05 03:53:54 +08:00
ShirkNeko
c0a86544d8 kernel: Remove macro definitions, pass variables using ccflags -y, and reapply manual su protection.
kernel: stop printing useless message unless its ddk environment
* In-tree build show empty KDIR

-- KDIR:
-- MDIR: /home/runner/work/KernelSU-Test/KernelSU-Test/kernel_414/KernelSU/kernel
  AR      drivers/iommu/built-in.o
  CC      drivers/input/misc/uinput.o
-- KernelSU version: 12329
-- KernelSU: CONFIG_KSU_MANUAL_HOOK
-- Supported KernelSU Manager(s): tiann, rsuntk, 5ec1cff
  CC      drivers/kernelsu/ksu.o
  AR      drivers/input/joystick/built-in.o
  CC      drivers/hid/hid-roccat-pyra.o

Co-authored-by: Faris <rissu.ntk@gmail.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-05 02:18:38 +08:00
weishu
47bd84f3d1 kernel: init/exit umount feature 2025-11-04 22:29:55 +08:00
Ylarod
06e714b4e7 ksubot: add branch, happy 2k 2025-11-04 22:26:40 +08:00
weishu
1439e486a1 kernel: rework umount with task_work 2025-11-04 21:57:10 +08:00
ShirkNeko
801bcb0e1f Revert "Implement workqueue for unmounting" 2025-11-04 21:55:00 +08:00
Ylarod
54b5fb5fdb ignore user settings 2025-11-04 21:29:19 +08:00
Ylarod
1cc9fce2c6 rename to managedFeatures 2025-11-04 21:29:11 +08:00
ShirkNeko
46fefc299c kernel: Remove CONFIG_KSU_MANUAL_SU protection 2025-11-04 21:28:39 +08:00
ShirkNeko
23cc0ceff1 Revert "ci: bump ddk to 20251104, fix android16-6.12 lkm"
This reverts commit 257f0ca6de.
2025-11-04 21:08:24 +08:00
ShirkNeko
4a610af452 manager: Fixed warning card status error
- Reuse your own DDK
2025-11-04 20:30:04 +08:00
backslashxx
8177afa81e kernel: core_hook: provide a better reboot handler (#523)
* Revert "feat: try manual reboot hook (#521)"

This reverts commit 1853d9decf.

* kernel: core_hook: provide a better reboot handler

I propose that you pass cmd and arg as reference.
this is so we can have much more extendable use of that pointer

kernel: core_hook: provide sys_reboot handler
- 2e2727d56c

kernel: kp_ksud: add sys_reboot kp hook
- 03285886b0

I'm proposing passing arg as reference to arg pointer and also pass int cmd
we can use it to pass numbers atleast.
for advanced usage, we can use it as a delimiter so we can pass a pointer to array.

example pass a char *array[] which decays to a char ** and then use cmd as the number of array members.
we can pass the pointer of the first member of the array and use cmd as the delimiter (count) of members.

for simpler usecase, heres some that I added.

kernel: core_hook: expose  umount list on sys_reboot interface
- 352de41e4b

kernel: core_hook: expose nuke_ext4_sysfs to sys_reboot interface
- 83fc684ccb

ksud: add cmd for add-try-umount, wipe-umount-list and nuke-ext4-sysfs
- a4eab4b8c3

more usage demos
https://github.com/backslashxx/lkm_template/tree/write-pointer-on-pointer
https://github.com/backslashxx/lkm_template/tree/pointer-reuse

I actually proposed sys_reboot upstream because of this pointer that is very usable.

Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>

---------

Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
2025-11-04 19:51:40 +08:00
ShirkNeko
3588282b43 ci: build dev manager
Co-authored-by: Ylarod <me@ylarod.cn>
2025-11-04 19:49:08 +08:00
ShirkNeko
257f0ca6de ci: bump ddk to 20251104, fix android16-6.12 lkm
-kernel: Using macros to control manual su operations

Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-04 19:44:43 +08:00
ShirkNeko
c863ff6f49 ci: Attempting once more to build the LKM using the DDK 2025-11-04 19:28:13 +08:00
ShirkNeko
e99a14290f manager: fix logical confusion 2025-11-04 12:22:49 +08:00
backslashxx
18e60ededa Reapply: "kernel: Allow to use in Private Space" (#2857)
rebase of
0576495b4b

Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-04 11:17:46 +08:00
ShirkNeko
22e4b69231 manager: When the version is less than 13490, enable safe mode. 2025-11-04 11:16:42 +08:00
AlexLiuDev233
1853d9decf feat: try manual reboot hook (#521)
* feat: try manual reboot hook

* refactor: move ksu_handle_reboot to supercalls.c for ShirkNeko

---------
2025-11-04 01:08:49 +08:00
ShirkNeko
d286f49e11 [skip ci]kernel: Migrating KPM to ioctl 2025-11-04 00:52:13 +08:00
ShirkNeko
7103779a11 ksud: Migrating KPM to ioctl
- Fix compatibility manager issues with legacy kernels

Co-authored-by: AlexLiuDev233 <wzylin11@outlook.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-04 00:35:26 +08:00
ShirkNeko
4350d309da manager: Implement version restrictions for certain features 2025-11-03 14:59:12 +08:00
ShirkNeko
7051b22536 manager: Provide backward compatibility for legacy kernels 2025-11-03 14:32:16 +08:00
ShirkNeko
e0bce04e79 ksud: clippy happy & fmt 2025-11-03 12:24:36 +08:00
ShirkNeko
c75b041c40 kernel: Reset KERNEL_SU_OPTION to 0xDEADBEEF 2025-11-03 12:12:08 +08:00
ShirkNeko
d2a3f0fcad manager: fix suggest boot partition if we've root.
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-03 12:10:49 +08:00
ShirkNeko
696c3059b6 manager: fix legacy get version
Co-authored-by: weishu <twsxtd@gmail.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-03 12:09:00 +08:00
Ylarod
ab8e966b7f add check_managed_features to installer.sh 2025-11-03 11:48:03 +08:00
Ylarod
7ece40bb2c ksud: add managed_feature 2025-11-03 11:47:39 +08:00
ShirkNeko
d1aa6c8beb manager: improve local LKM selection
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-03 11:39:42 +08:00
Ylarod
bfed2d700a add legacy get_version & Full get_version
Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-11-03 11:16:55 +08:00
ShirkNeko
59339b806a Revert "ci: use ddk for faster ci, manual gki image build"
This reverts commit 33d1f18395.
2025-11-03 03:49:56 +08:00
ShirkNeko
2433d64b6b Revert "ci: Attempting to resolve the issue with DDK compilation being unavailable"
This reverts commit a622657092.
2025-11-03 03:39:46 +08:00
ShirkNeko
a622657092 ci: Attempting to resolve the issue with DDK compilation being unavailable 2025-11-03 00:57:29 +08:00
Ylarod
b4e682148a kernel: remove dynamic alloc in feature 2025-11-03 00:41:27 +08:00
Ylarod
02474a5953 update embed ksuinit to v2 2025-11-02 23:49:46 +08:00
Ylarod
450dbf14fc kernel: disable setuid debug log 2025-11-02 23:49:40 +08:00
Ylarod
d89eab2c34 use cap_task_fix_setuid hook to avoid inline issue 2025-11-02 22:04:28 +08:00
ShirkNeko
a6b86a4f99 ci: skip dup lkm build
- Fixed and added build 6.12

- Repair construction

Co-authored-by: Ylarod <me@ylarod.cn>
2025-11-02 21:53:53 +08:00
ShirkNeko
33d1f18395 ci: use ddk for faster ci, manual gki image build
Co-authored-by: Ylarod <me@ylarod.cn>
2025-11-02 20:54:58 +08:00
Ylarod
8ebe60ca04 update ioctl macro (#2850) 2025-11-02 20:15:55 +08:00
ShirkNeko
980613c6a9 ksud: fmt 2025-11-02 20:07:35 +08:00
ShirkNeko
47bcc956a3 fix lot (#518)
* refact: use feature subsystem

* use 64bit feature

* fix

* add fixme

* add feature max to get_info

* use 32bit feature id

* allow root to get/set feature

* more clean perm_check functions

* fix

* add feature command to ksud

kernel: do not expose perm checker

* fix security_task_fix_setuid_handler_pre

* add android16-6.12 ci

* manager: add kernel_umount switch

Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>

* manager: Reinstate the LKM selection function

* kernel: add name and print command value

- Optimise sulog log display

Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>

* fix

* ksud: clippy

---------

Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
Co-authored-by: weishu <twsxtd@gmail.com>
2025-11-02 20:01:24 +08:00
ꑄℭѧᝰ.ᐟ
00de4e1c64 Update Vietnamese Translation (#515)
* Update Vietnamese Translation

* Fix typo strings
2025-11-02 18:42:34 +08:00
Ylarod
97ec718fea switch ns umount 2025-11-02 12:23:23 +08:00
Wang Han
5c96f951b5 Implement workqueue for unmounting
umount schedules, so it cannot be used in kprobe context.
2025-11-02 12:21:00 +08:00
Ylarod
7e446efac4 back to kprobe setuid hook 2025-11-02 12:17:21 +08:00
ShirkNeko
c06d694ebc kernel: When CONFIG_KSU = m, disable sulog 2025-11-02 03:09:55 +08:00
ShirkNeko
bd0b07cba9 kernel: Add sulog records for supercalls 2025-11-02 03:04:35 +08:00
ShirkNeko
e54339cf4e Compilation fixed; defaults to fallback using packages.list 2025-11-02 01:31:41 +08:00
ShirkNeko
320e08b8fb new supercall impl (#511)
* refactor: replace throne tracker with ksud token

* use snprintf

* refactor: new supercall impl

- Import the sukisu command

* disable seccomp for supercall users

* kernel: fmt clear

* kernel: Enable macro protection for sulog

- Only enabled on kernel versions greater than 5.10.245

* kernel: Refactor kprobe hooks and implement LSM hooks for improved security handling

* debug mode

* kernel: Add functionality to generate and validate authentication tokens for cmd_su

* kernel: Simplified manual SU command processing for code

* kernel: replace renameat hook with fsnotify

* Revert "refactor: replace throne tracker with ksud token"

This reverts commit aa2cbbf9cd.

* kernel: fix compile

* kernel: fix compile below 6.0

* Fix compile err; Add become_manager

* kernel: install fd for manager automaticlly

- extend to import the corresponding command

* manager: new supercall impl

* temp changes for ksud

* ksud: fix compile

* fix wrong opcode

* kernel: fix compile

* kernel: Fixed hook type and KPM status retrieval errors

* kernel: Fixed potential null pointer issue with current->mm in kernel version 5.10

When calling get_full_comm() within system call hooks, current->mm may be null (prctl). A fallback mechanism for current->comm must be added beforehand to prevent null pointer dereferences when accessing mm->arg_start/arg_end.

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>

* ksud: fix cargo check

* manager: Fixed an issue where the KSUD release and user-mode scanning switch failed to function correctly.

- kernel: fix spin lock mutual

kernel: Fixed potential null pointer issue with current->mm in kernel version 5.10

When calling get_full_comm() within system call hooks, current->mm may be null (prctl). A fallback mechanism for current->comm must be added beforehand to prevent null pointer dereferences when accessing mm->arg_start/arg_end.

kernel: try introduce like susfs's method to fix prctl delay

* seccomp: allow reboot

* use u32

* update clang-format

* 4 spaces save the world

* ksud: Fix build on macOS

* manager: bump minimal supported kernel.

- When get_hook_type is empty, display “Unknown”.


* Fix ksud build (#2841)

* try fix ksud

* fix for macos

* remove any

* Fix ksud build, take 3

* try fix allowlist

* bring lsm hook back

* fix: a lot again

* Fix ksud build, take 4 (#2846)

Remove init_driver_fd function for non-linux/android targets

* manager: Return to the native method via KSUd installation

* Merge with susfs-mian format

---------

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: weishu <twsxtd@gmail.com>
Co-authored-by: AlexLiuDev233 <wzylin11@outlook.com>
Co-authored-by: Wang Han <416810799@qq.com>
2025-11-01 23:30:30 +08:00
NkBe
0da8ecb071 enjoy: 專門為MiUI桌面卸載添加了提示,提升使用者體驗 (#502) 2025-10-30 14:50:13 +08:00
NkBe
cc54abd273 strings: Update zh-rTW (#501) 2025-10-30 14:49:57 +08:00
ShirkNeko
ee781cf959 kernel: Fixed potential null pointer issue with current->mm in kernel version 5.10
When calling get_full_comm() within system call hooks, current->mm may be null (prctl). A fallback mechanism for current->comm must be added beforehand to prevent null pointer dereferences when accessing mm->arg_start/arg_end.

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-10-27 22:02:12 +08:00
ShirkNeko
a21b18c5de kernel: Simplified manual SU command processing for code 2025-10-27 16:02:04 +08:00
ShirkNeko
1644f22c98 manager: Restore the colour scheme on the sulog page 2025-10-27 12:31:26 +08:00
由崎黑板
7a338b1b43 Revert "Disable Samsung Activation Verify to solve -45(2000) error during activation on China Mainland Samsung devices (#446)" (#492)
This reverts commit 7ef9230d66.
2025-10-26 22:22:11 +08:00
ShirkNeko
324dc0844f kernel: Greenlight the new prctl command 2025-10-26 20:43:51 +08:00
ShirkNeko
f7fe0cf748 kernel: Add functionality to generate and validate authentication tokens for cmd_su 2025-10-26 19:58:16 +08:00
ShirkNeko
bf1a45963b kernel: try introduce like susfs's method to fix prctl delay
Co-authored-by: AlexLiuDev233 <wzylin11@outlook.com>
2025-10-25 18:35:53 +08:00
ShirkNeko
64ee09fd12 kernel: Enable macro protection for sulog
- Only enabled on kernel versions greater than 5.10.245
2025-10-25 00:45:43 +08:00
ShirkNeko
b9a84a15bc manager: Remove sulog file selection 2025-10-24 21:55:32 +08:00
ShirkNeko
6a1e1d788b kernel: sulog: Simplify code using vfs_truncate 2025-10-23 22:16:06 +08:00
ShirkNeko
4b86989bf9 kernel: Restore the sequence of sulog logs 2025-10-23 21:48:55 +08:00
ShirkNeko
d3f8c128da kernel: Add a UID blacklist feature to restrict the operational permissions of specific users 2025-10-23 02:08:45 +08:00
ShirkNeko
bbb2748494 kernel: Adjust the command processing sequence 2025-10-22 23:54:55 +08:00
ShirkNeko
b9e6246d65 manager: Avoid page crashes caused by excessive data. 2025-10-22 23:53:05 +08:00
ShirkNeko
7bf13fbfca manager: Add an option to exclude the current application and certain system calls from the log viewer. 2025-10-22 22:42:26 +08:00
ShirkNeko
e9ee2304d3 kernel: Extend sulog to also record system call logs 2025-10-22 20:56:46 +08:00
ShirkNeko
f7b4b4b82d kernel: Optimized log timestamp processing and added manual refresh functionality. 2025-10-22 18:42:50 +08:00
ShirkNeko
15b9c4dbbf manager: Add the sulog log viewer interface and functionality 2025-10-22 18:19:16 +08:00
ShirkNeko
3b966c536b Kernel: Implement sulog for enhanced logging of SU attempts and permissions 2025-10-22 17:21:25 +08:00
ShirkNeko
b5e5be2572 Revert: ksud, kernel: Pick some upstream changes and remove unused functions
* Upstream commit:
f130f7572f

Signed-off-by: Faris <rsuntk@yukiprjkt.my.id>
2025-10-22 00:09:56 +08:00
ShirkNeko
253df1ea47 New translations strings.xml (Vietnamese) (#484) 2025-10-21 16:40:43 +08:00
ShirkNeko
134684a139 Kernel(Makefile): Add KSU_SRC to set path correctly #483
try Fix version define for 6.12 Kernel

Co-authored-by: YC酱luyancib <luyancib@qq.com>
2025-10-19 19:23:18 +08:00
ShirkNeko
9c07fa6889 ksud: Fixed compilation errors 2025-10-19 19:00:47 +08:00
Sultan Alsawaf
c3f66e15e9 kernel: Fix kernel panics caused by thread info flag corruption
Signed-off-by: Sultan Alsawaf <sultan@kerneltoast.com>
2025-10-19 18:44:15 +08:00
Faris
b6e2fa383a ksud: fix compilation and remove unused imports
Signed-off-by: Faris <rsuntk@yukiprjkt.my.id>
2025-10-19 18:44:07 +08:00
Ylarod
61f85a029e ksud, kernel: Pick some upstream changes and remove unused functions
* Upstream commit:
f130f7572f

Signed-off-by: Faris <rsuntk@yukiprjkt.my.id>
2025-10-19 18:41:30 +08:00
5ec1cff
b1564b77a2 ksud: refine mount
- `magic_mount` now takes the tmp_path from its argument
- `.notmpfs` and `.nomount` now behave the same

skip:
ksud: cache tmp path (74ce44de21963fddc83897846eed28eca89d78aa)

Co-authored-by: shatyuka <shatyuka@qq.com>
Signed-off-by: Faris <rsuntk@yukiprjkt.my.id>
2025-10-19 18:39:57 +08:00
Vayruz Rafli
cc0a3590ce docs: Fix little typo on tracepoint hook guide (#482) 2025-10-19 13:00:58 +08:00
PKQYPKJ
e793219c2b manager: Remove unnecessary translations (#478)
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-10-18 22:43:54 +08:00
ShirkNeko
776a753206 manager: Rewrite UI state management 2025-10-18 22:37:17 +08:00
ShirkNeko
7b6470cc79 manager: Fixed sus tab mapping error 2025-10-18 17:40:23 +08:00
ShirkNeko
eb5fdbbf3f manager: SuSFS: Add SUS_MAP feature configuration item 2025-10-18 15:09:02 +08:00
ShirkNeko
8db55f56a9 manager: Reuse the aidi reflection to retrieve the list of applications 2025-10-17 01:59:56 +08:00
ShirkNeko
62635879e0 manager: Add multiple activity declarations for the backup icon close #469 2025-10-16 23:43:18 +08:00
ShirkNeko
86634aac3d Manager: Increase the loading priority of ksud
- This will resolve data loading errors that occur upon first launch.
2025-10-16 22:51:38 +08:00
ShirkNeko
af25f8d49e manager: Optimize Language Settings
- Do not update language configurations by refreshing activities; instead, introduce consistent language configurations from kernelsu-next.

Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
2025-10-16 22:37:52 +08:00
Wang Han
cd78c2693a Don't write newline character to cgroup node (#2804)
This prevents su hang on oplus devices, maybe related to bad kernel
hooks.
2025-10-16 12:28:36 +08:00
ShirkNeko
8ff9fab414 manager: Fixed an issue where ksud failed to release properly during the first installation. 2025-10-15 14:15:13 +08:00
ShirkNeko
b8eebcda5a kernel: Bump susfs binary version to 1.5.12
- Removed functionally coupled versions 1.5.10-1.5.11

- Removed `statusMagicMount` status retrieval

- Use `getSuSFSFeatures` to replace `CMD_GET_SUSFS_FEATURE_STATUS` for obtaining SUSFS status.
2025-10-13 17:40:33 +08:00
ShirkNeko
85291de02a manager: Restructure the file directory to keep it clean 2025-10-12 18:58:02 +08:00
Cynthia
cb7abc88dd strings: full Indonesian translation (values-ind/strings.xml) (#464) 2025-10-12 15:50:29 +08:00
James Sparrow
918e7ae0b7 [skip ci]Update Vietnamese Translation (#462) 2025-10-12 15:50:05 +08:00
生于生时 亡于亡刻
0a804ba170 feat: Optimize some codes (ksud) (#465)
* chore: make cargo clippy happy

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

* chore: Optimize import
-  Format as a standard import

Signed-off-by: Tools-app <localhost.hutao@gmail.com>

---------

Signed-off-by: Tools-app <localhost.hutao@gmail.com>
2025-10-12 15:48:24 +08:00
ShirkNeko
4c512dc7ff manager: Add detailed information about the module / anykernel3 compressed package from sharing and direct flashing
- Treat certain XP module APK files as modules for processing
2025-10-11 17:49:36 +08:00
ShirkNeko
fcb7c3e99d manager: Avoid repeated flashing caused by unconditionally clearing the Anykernel3 flashing status. 2025-10-10 23:56:03 +08:00
Prslc
b827360ac6 docs: Sync TRACEPOINT HOOK v1.1 (#456)
- Replace trace_ksu_trace_execveat_sucompat_hook with trace_ksu_trace_execveat_hook to align with v1.1 changes
  The new hook now passes argv/envp and a flags argument, adding support for 32-on-64 environments.
- Remove the devpts hook section from both English and Chinese guides
  The devpts (pty.c) hook example was deprecated in v1.1 and is no longer required.

Link: f1f7c61aee

Signed-off-by: Prslc <prslc113@gmail.com>
2025-10-10 00:39:15 +08:00
ShirkNeko
ca7b53370e manager: Enhanced External Selection Sharing Single/Batch Module Installation
- Reduce the delay exit time in seconds
2025-10-08 19:16:30 +08:00
ShirkNeko
230ca54d63 manager: Expand the option to directly open the file and flash the anykernel3 kernel package 2025-10-08 18:23:53 +08:00
ShirkNeko
2f43ad4f76 manager: add support for opening zip file and directly flash module
- refine zip intent method

- use MMRL method to handle zip, fix failed to open zip from Chrome

Co-Authored-By: Der_Googler <54764558+dergoogler@users.noreply.github.com>
Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-10-08 16:11:55 +08:00
ShirkNeko
9c1ff635e3 Kernel: Add support for 6.12
Filtered from tiann/KernelSU #2793

Co-authored-by: libingxuan <84086386+aaaaaaaa-815@users.noreply.github.com>
2025-10-08 15:00:04 +08:00
rsuntk
ef97f0e4d9 kernel: make apk_sign_key as a typedef instead
Style preference.

Signed-off-by: rsuntk <rsuntk@yukiprjkt.my.id>
2025-10-08 14:50:50 +08:00
ShirkNeko
2e394903cc kernel: Simplified Logic 2025-10-08 14:46:58 +08:00
unknow-tech
7978cbafa5 Add support for 6.13 (#454)
Fiz issue https://github.com/SukiSU-Ultra/SukiSU-Ultra/issues/364

cdd30ebb1b
2025-10-08 14:46:50 +08:00
rsuntk
c89a3dbcd9 kernel: add guard for avoiding LKM being builded on Linux 6.8+
* Due to numerous changes on LSM (Linux Security Module) in Linux 6.8+
* This is temporary guard until a working solution exist.

Signed-off-by: rsuntk <rsuntk@yukiprjkt.my.id>
2025-10-08 14:46:00 +08:00
Huy Minh
13c7912320 kernel: add initial 6.8+/6.14 kernel support
* This is a squashed of un-merged pull requests of Official KernelSU
* LKM support are not available.
* Require this additional patch to avoid kernel panic because of "Too many LSMs registered":
7042991a5c

* Un-merged pull requests of Official KernelSU:
https://github.com/tiann/KernelSU/pull/1785
https://github.com/tiann/KernelSU/pull/2662

* This commit probably not 100% completed.

Signed-off-by: rsuntk <rsuntk@yukiprjkt.my.id>
2025-10-08 14:45:36 +08:00
saitama-droid
abbe385382 kernel: core_hook: add config guard for manual SU escalation (#453)
Wrap manual SU escalation handling in CONFIG_KSU_MANUAL_SU
conditional compilation to allow builds without this functionality.

This affects:
- CMD_SU_ESCALATION_REQUEST
- CMD_ADD_PENDING_ROOT

When CONFIG_KSU_MANUAL_SU is disabled, these prctl commands
will not be compiled into the kernel.
2025-10-08 14:33:17 +08:00
Saksham
0b80137f17 Bump to latest stable ndk
Ref:https://developer.android.com/ndk/downloads#stable-downloads
2025-10-07 11:27:09 +05:30
ShirkNeko
c4ff89c13d manager: Add pull-to-refresh functionality 2025-10-07 01:29:00 +08:00
rifsxd
ce3a7ec189 kernel: handle throned UID change if manager is reinstall or changed
drop old UID and throne the new one when the manager is reinstalled or changed

- Add dynamic manager lock

Co-authored-by: rifsxd <rifat.44.azad.rifs@gmail.com>
Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-10-07 00:08:30 +08:00
ShirkNeko
2bb789212a New translations strings.xml (Vietnamese) (#449) 2025-10-06 23:15:30 +08:00
由崎黑板
7ef9230d66 Disable Samsung Activation Verify to solve -45(2000) error during activation on China Mainland Samsung devices (#446) 2025-10-06 23:02:54 +08:00
ShirkNeko
fbaa69f3fb manager: Raise the minimum version requirement to 3.1.8 close #448 2025-10-06 22:50:24 +08:00
ShirkNeko
e78ee720b5 ksud: fmt 2025-10-06 22:44:09 +08:00
ShirkNeko
04b603394a ksud: Fixed inconsistent parameter passing in kpm_prctl 2025-10-06 22:02:59 +08:00
ShirkNeko
c9c7a5f4e3 manager: Update Repository Naming 2025-10-06 21:08:23 +08:00
ShirkNeko
044b4a2f9c manager: Simplify kpm management by migrating to the ksud side. 2025-10-06 20:53:18 +08:00
ShirkNeko
59cd8d1c3b kernel: core_hook: harden prctl handler
detection is done by comparing a 0xDEADBEEF call to a non-0xDEADBEEF one.
which yeah, you will see that the non-0xDEADBEEF one returns early.

yes I know this causes delays for all prctl calls, as we straight up check uid,
but this keeps the delay consistent, which is what we want.

another is that we only should only perform this recrowning logic for multiuser

- temp fix for cimb octo's prctl abuse

Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
2025-10-06 00:19:37 +08:00
ShirkNeko
36617bf0a1 kernel: For cmd_su, bypass the susfs_is_current_proc_umounted check only for system processes. 2025-10-05 20:08:51 +08:00
unknow-tech
5b49054055 Merge pull request #445 from unknow-tech/patch-1
Fix wrong function
2025-10-05 00:37:59 +08:00
NkBe
a17a220745 manager: Update zh-rTW strings (#439)
* manager: Update zh-rTW strings

* Update strings.xml

* Update strings.xml
2025-10-04 20:52:12 +05:30
YC酱luyancib
0729066a6f manager: add susfs 1.5.11 binary (#444) 2025-10-04 18:19:15 +05:30
ShirkNeko
d4dcf610c9 kernel: Fixed disable_seccomp compatibility issues 2025-10-01 00:16:35 +08:00
ShirkNeko
78e0dc6da2 kernel: Add CONFIG_KSU_MANUAL_SU configuration
- Use random passphrase protection for manual su functionality
2025-09-30 23:29:29 +08:00
ShirkNeko
a9a10466b3 Kernel: Enhanced temporary record UID functionality and elevated privileges 2025-09-30 23:27:02 +08:00
ShirkNeko
65d5d6a494 kernel: Add the ability to manually elevate privileges for programs using prctl by specifying UID or PID. 2025-09-30 23:26:28 +08:00
ShirkNeko
e552163d9e Revert "manager: Removed SUSFS_UMOUNT_FOR_ZYGOTE_ISO_SERVICE which are no longer used in susfs 1.5.10."
This reverts commit 475b3998a1.

- bump susfs version 1.5.10
2025-09-30 23:08:34 +08:00
ShirkNeko
c950705044 Manager: fmt
- Optimized homepage refresh logic and removed the caching mechanism
2025-09-29 17:17:19 +08:00
Rifat Azad
9e7aabf3f7 userspace/su: add ndk compatible su from kernelnosu
Co-authored-by: nampud <nampud@users.noreply.github.com>
2025-09-28 00:35:26 +08:00
ShirkNeko
a20a89da03 kernel: Revert partial changes 2025-09-27 23:04:14 +08:00
ShirkNeko
9551ca4fe8 kernel: fmt ,optimization Log 2025-09-27 21:12:02 +08:00
ShirkNeko
a2431d50ce kernel: Enhance the user space scanning functionality 2025-09-27 19:47:22 +08:00
ShirkNeko
8b74f7d466 manager: Redesign zygisk to ensure display accuracy 2025-09-26 23:28:27 +08:00
ShirkNeko
b5d9607e8e manager: Switch to matching mode to match the zygisk module
- bump dependencies
2025-09-26 20:14:27 +08:00
ShirkNeko
475b3998a1 manager: Removed SUSFS_UMOUNT_FOR_ZYGOTE_ISO_SERVICE and SUSFS_RUN_UMOUNT_FOR_CURRENT_MNT_NS, which are no longer used in susfs 1.5.10.
- bump susfs version 1.5.10
2025-09-25 21:57:31 +08:00
ShirkNeko
23ed4384e6 Revert "Unmount isolated process which forks from zygote unconditionally (#2747)"
This reverts commit 695e749e3e.
2025-09-25 16:13:07 +08:00
326 changed files with 35613 additions and 43414 deletions

71
.cursor/rules/general.mdc Normal file
View File

@@ -0,0 +1,71 @@
---
description: "Project-wide coding standards and tooling rules for Kotlin, Rust, C++, C, and Java (Gradle)."
globs:
- "**/*.kt"
- "**/*.rs"
- "**/*.cpp"
- "**/*.c"
- "**/*.java"
alwaysApply: true
---
# Project Coding Standards
## Universal Rules
- Role: You are an expert senior engineer. Always generate maintainable, secure, performant, and idiomatic code.
- Prefer readability over cleverness. Keep changes focused and minimal.
- Follow SOLID, clean architecture, immutability-first, modular design.
- Remove unused imports, dead code, commented-out blocks, TODOs, debug logs.
- Use descriptive naming; avoid magic numbers.
- Document non-obvious logic and unsafe operations.
## Formatting & Tooling
- Formatters to apply automatically on save or pre-commit: `ktlint`, `detekt`, `rustfmt`, `cargo clippy`, `clang-format`, `clang-tidy`, `spotless`.
- Pre-commit hooks should run: `ktlint --format`, `cargo fmt`, `clang-format -i`, `gradle spotlessApply`.
- CI must enforce formatting and linting; any violation should fail the build.
## Kotlin (files matching `**/*.kt`)
- Target Kotlin version 1.9.
- Use `val` over `var` by default.
- Use data classes and sealed classes for value types.
- Use structured concurrency with coroutines; avoid `GlobalScope`.
- Avoid forced unwraps (`!!`); prefer safe calls (`?.`) and Elvis (`?:`) operators.
- Prefer extension functions for utilities.
- Tests must be written using JUnit 5.
## Rust (files matching `**/*.rs`)
- Use Rust edition 2024.
- Replace `unwrap()` and `expect()` in production code with `Result` + `?` propagation.
- Avoid unnecessary `.clone()`; use borrowing and lifetimes properly.
- Avoid `unsafe` blocks unless strictly necessary, and document why.
- Always run `cargo fmt` and `cargo clippy`.
## C++ (files matching `**/*.cpp`, `**/*.hpp`)
- Use C++23 standard.
- Avoid raw pointers; prefer `std::unique_ptr`, `std::shared_ptr`, `std::optional`.
- Use RAII for resource management.
- Prefer STL algorithms over manual loops.
- Avoid macros and global mutable state.
- Tests should be written using Google Test or equivalent.
## C (files matching `**/*.c`, `**/*.h`)
- Use C23 standard where possible.
- Check return values of all system/library calls.
- Avoid global mutable state.
- Use `const` correctness in declarations.
- Leverage static analysis tools: `cppcheck`, `valgrind`.
- Use header/implementation separation properly.
## Java + Gradle (files matching `build.gradle.kts`, `**/*.java`)
- Target Java version 21 (or higher if project permits).
- Use Gradle Kotlin DSL for build scripts.
- Prefer `record` for simple data carriers.
- Use `Optional` and `Stream` responsibly; prefer immutability.
- Build must be reproducible and minimal: avoid unnecessary plugins.
- Use `spotless` for formatting and `checkstyle` for static analysis.
## Testing Rules
- For each new module, write unit tests.
- Test naming convention: `should<Behavior>_when<Condition>`.
- Tests must be fast, deterministic, not rely on network/external I/O (use mocks as needed).
- Aim for coverage: critical paths ≥ 90%, overall modules ≥ 70%.

View File

@@ -1,137 +0,0 @@
name: GKI Kernel Build
on:
workflow_call:
inputs:
version_name:
required: true
type: string
description: >
With SUBLEVEL of kernel,
for example: android12-5.10.66
arch:
required: true
type: string
description: >
Build arch: aarch64/x86_64
debug:
required: false
type: boolean
default: true
manifest_name:
required: false
type: string
description: >
Local repo manifest xml path,
typically for AVD kernel build.
secrets:
BOOT_SIGN_KEY:
required: false
CHAT_ID:
required: false
BOT_TOKEN:
required: false
MESSAGE_THREAD_ID:
required: false
jobs:
build:
name: Build ${{ inputs.version_name }}
runs-on: ubuntu-22.04
steps:
- name: Maximize build space
uses: easimon/maximize-build-space@master
with:
root-reserve-mb: 8192
temp-reserve-mb: 2048
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
- uses: actions/checkout@v4
with:
path: KernelSU
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Setup kernel source
run: |
echo "Free space:"
df -h
cd $GITHUB_WORKSPACE
sudo apt-get install repo -y
mkdir android-kernel && cd android-kernel
repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -m "$GITHUB_WORKSPACE/KernelSU/.github/manifests/${{ inputs.manifest_name }}" --repo-rev=v2.16
repo --version
repo --trace sync -c -j$(nproc --all) --no-tags
df -h
- name: Setup KernelSU
env:
PATCH_PATH: ${{ inputs.patch_path }}
IS_DEBUG_KERNEL: ${{ inputs.debug }}
run: |
cd $GITHUB_WORKSPACE/android-kernel
echo "[+] KernelSU setup"
GKI_ROOT=$(pwd)
echo "[+] GKI_ROOT: $GKI_ROOT"
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
echo "[+] Add KernelSU driver to Makefile"
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
echo "[+] Apply KernelSU patches"
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
echo "[+] Enable debug features for kernel"
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
fi
repo status
echo "[+] KernelSU setup done."
cd $GITHUB_WORKSPACE/KernelSU
VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
- name: Make working directory clean to avoid dirty
working-directory: android-kernel
run: |
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
git config --global user.email "bot@kernelsu.org"
git config --global user.name "KernelSUBot"
cd common/ && git add -A && git commit -a -m "Add KernelSU"
repo status
- name: Build kernel
working-directory: android-kernel
run: |
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
fi
tools/bazel run --config=fast --config=stamp --lto=thin //common-modules/virtual-device:virtual_device_${{ inputs.arch }}_dist -- --dist_dir=dist
NAME=kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
TARGET_IMAGE=dist/bzImage
if [ ! -e $TARGET_IMAGE ]; then
TARGET_IMAGE=dist/Image
fi
mv $TARGET_IMAGE $NAME
echo "file_path=android-kernel/$NAME" >> $GITHUB_ENV
- name: Upload Kernel
uses: actions/upload-artifact@v4
with:
name: kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
path: "${{ env.file_path }}"

20
.github/workflows/build-gki-image.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Build Android GKI Image
on:
workflow_call:
jobs:
build-a12-kernel:
uses: ./.github/workflows/build-kernel-a12.yml
secrets: inherit
build-a13-kernel:
uses: ./.github/workflows/build-kernel-a13.yml
secrets: inherit
build-a14-kernel:
uses: ./.github/workflows/build-kernel-a14.yml
secrets: inherit
build-a15-kernel:
uses: ./.github/workflows/build-kernel-a15.yml
secrets: inherit
build-a16-kernel:
uses: ./.github/workflows/build-kernel-a16.yml
secrets: inherit

111
.github/workflows/build-kernel-a12.yml vendored Normal file
View File

@@ -0,0 +1,111 @@
name: Build Kernel - Android 12
on:
# push:
# branches: ["main", "ci", "checkci"]
# paths:
# - ".github/workflows/deps/gki/build-kernel-a12.yml"
# - ".github/workflows/deps/gki/gki-kernel.yml"
# - ".github/scripts/build_a12.sh"
# - "kernel/**"
workflow_call:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
workflow_dispatch:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
jobs:
build-kernel:
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
strategy:
matrix:
include:
- sub_level: 209
os_patch_level: 2024-05
- sub_level: 218
os_patch_level: 2024-08
- sub_level: 226
os_patch_level: 2024-11
- sub_level: 233
os_patch_level: 2025-02
- sub_level: 236
os_patch_level: 2025-05
uses: ./.github/workflows/gki-kernel.yml
secrets: inherit
with:
version: android12-5.10
version_name: android12-5.10.${{ matrix.sub_level }}
tag: android12-5.10-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
patch_path: "5.10"
debug: ${{ inputs.debug || false }}
upload-artifacts:
needs: build-kernel
runs-on: ubuntu-latest
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
- uses: actions/checkout@v4
with:
path: KernelSU
fetch-depth: 0
- name: List artifacts
run: |
tree
- name: Download prebuilt toolchain
run: |
AOSP_MIRROR=https://android.googlesource.com
BRANCH=main-kernel-build-2024
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
pip3 install telethon
- name: Set boot sign key
env:
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
run: |
if [ ! -z "$BOOT_SIGN_KEY" ]; then
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
fi
- name: Build boot images
run: |
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
cd $GITHUB_WORKSPACE/KernelSU
export VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
cd -
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a12.sh
- name: Display structure of boot files
run: ls -R
- name: Upload images artifact
uses: actions/upload-artifact@v4
with:
name: boot-images-android12
path: Image-android12*/*.img.gz

146
.github/workflows/build-kernel-a13.yml vendored Normal file
View File

@@ -0,0 +1,146 @@
name: Build Kernel - Android 13
on:
# push:
# branches: ["main", "ci", "checkci"]
# paths:
# - ".github/workflows/deps/gki/build-kernel-a13.yml"
# - ".github/workflows/deps/gki/gki-kernel.yml"
# - ".github/scripts/build_a13.sh"
# - "kernel/**"
workflow_call:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
workflow_dispatch:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
jobs:
build-kernel:
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
strategy:
matrix:
include:
- version: "5.10"
sub_level: 209
os_patch_level: 2024-05
- version: "5.10"
sub_level: 210
os_patch_level: 2024-06
- version: "5.10"
sub_level: 214
os_patch_level: 2024-07
- version: "5.10"
sub_level: 218
os_patch_level: 2024-08
- version: "5.10"
sub_level: 223
os_patch_level: 2024-11
- version: "5.10"
sub_level: 228
os_patch_level: 2025-01
- version: "5.10"
sub_level: 234
os_patch_level: 2025-03
- version: "5.15"
sub_level: 148
os_patch_level: 2024-05
- version: "5.15"
sub_level: 149
os_patch_level: 2024-07
- version: "5.15"
sub_level: 151
os_patch_level: 2024-08
- version: "5.15"
sub_level: 153
os_patch_level: 2024-09
- version: "5.15"
sub_level: 167
os_patch_level: 2024-11
- version: "5.15"
sub_level: 178
os_patch_level: 2024-11
- version: "5.15"
sub_level: 170
os_patch_level: 2025-01
- version: "5.15"
sub_level: 178
os_patch_level: 2025-03
uses: ./.github/workflows/gki-kernel.yml
secrets: inherit
with:
version: android13-${{ matrix.version }}
version_name: android13-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android13-${{ matrix.version }}-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}
debug: ${{ inputs.debug || false }}
upload-artifacts:
needs: build-kernel
runs-on: ubuntu-latest
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
- uses: actions/checkout@v4
with:
path: KernelSU
fetch-depth: 0
- name: List artifacts
run: |
tree
- name: Download prebuilt toolchain
run: |
AOSP_MIRROR=https://android.googlesource.com
BRANCH=main-kernel-build-2024
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
pip3 install telethon
- name: Set boot sign key
env:
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
run: |
if [ ! -z "$BOOT_SIGN_KEY" ]; then
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
fi
- name: Build boot images
run: |
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
cd $GITHUB_WORKSPACE/KernelSU
export VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
cd -
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
- name: Display structure of boot files
run: ls -R
- name: Upload images artifact
uses: actions/upload-artifact@v4
with:
name: boot-images-android13
path: Image-android13*/*.img.gz

158
.github/workflows/build-kernel-a14.yml vendored Normal file
View File

@@ -0,0 +1,158 @@
name: Build Kernel - Android 14
on:
# push:
# branches: ["main", "ci", "checkci"]
# paths:
# - ".github/workflows/deps/gki/build-kernel-a14.yml"
# - ".github/workflows/deps/gki/gki-kernel.yml"
# - ".github/scripts/build_a13.sh"
# - "kernel/**"
workflow_call:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
workflow_dispatch:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
jobs:
build-kernel:
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
strategy:
matrix:
include:
- version: "5.15"
sub_level: 148
os_patch_level: 2024-05
- version: "5.15"
sub_level: 149
os_patch_level: 2024-06
- version: "5.15"
sub_level: 153
os_patch_level: 2024-07
- version: "5.15"
sub_level: 158
os_patch_level: 2024-08
- version: "5.15"
sub_level: 164
os_patch_level: 2024-09
- version: "5.15"
sub_level: 167
os_patch_level: 2024-11
- version: "5.15"
sub_level: 170
os_patch_level: 2025-01
- version: "5.15"
sub_level: 178
os_patch_level: 2025-03
- version: "6.1"
sub_level: 75
os_patch_level: 2024-05
- version: "6.1"
sub_level: 78
os_patch_level: 2024-06
- version: "6.1"
sub_level: 84
os_patch_level: 2024-07
- version: "6.1"
sub_level: 90
os_patch_level: 2024-08
- version: "6.1"
sub_level: 93
os_patch_level: 2024-09
- version: "6.1"
sub_level: 99
os_patch_level: 2024-10
- version: "6.1"
sub_level: 112
os_patch_level: 2024-11
- version: "6.1"
sub_level: 115
os_patch_level: 2024-12
- version: "6.1"
sub_level: 118
os_patch_level: 2025-01
- version: "6.1"
sub_level: 128
os_patch_level: 2025-03
- version: "6.1"
sub_level: 134
os_patch_level: 2025-05
uses: ./.github/workflows/gki-kernel.yml
secrets: inherit
with:
version: android14-${{ matrix.version }}
version_name: android14-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android14-${{ matrix.version }}-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}
debug: ${{ inputs.debug || false }}
upload-artifacts:
needs: build-kernel
runs-on: ubuntu-latest
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
- uses: actions/checkout@v4
with:
path: KernelSU
fetch-depth: 0
- name: List artifacts
run: |
tree
- name: Download prebuilt toolchain
run: |
AOSP_MIRROR=https://android.googlesource.com
BRANCH=main-kernel-build-2024
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
pip3 install telethon
- name: Set boot sign key
env:
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
run: |
if [ ! -z "$BOOT_SIGN_KEY" ]; then
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
fi
- name: Build boot images
run: |
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
cd $GITHUB_WORKSPACE/KernelSU
export VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
cd -
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
- name: Display structure of boot files
run: ls -R
- name: Upload images artifact
uses: actions/upload-artifact@v4
with:
name: boot-images-android14
path: Image-android14*/*.img.gz

131
.github/workflows/build-kernel-a15.yml vendored Normal file
View File

@@ -0,0 +1,131 @@
name: Build Kernel - Android 15
on:
# push:
# branches: ["main", "ci", "checkci"]
# paths:
# - ".github/workflows/deps/gki/build-kernel-a15.yml"
# - ".github/workflows/deps/gki/gki-kernel.yml"
# - ".github/scripts/build_a13.sh"
# - "kernel/**"
workflow_call:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
workflow_dispatch:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
jobs:
build-kernel:
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
strategy:
matrix:
include:
- version: "6.6"
sub_level: 30
os_patch_level: 2024-08
- version: "6.6"
sub_level: 46
os_patch_level: 2024-09
- version: "6.6"
sub_level: 50
os_patch_level: 2024-10
- version: "6.6"
sub_level: 56
os_patch_level: 2024-11
- version: "6.6"
sub_level: 57
os_patch_level: 2024-12
- version: "6.6"
sub_level: 58
os_patch_level: 2025-01
- version: "6.6"
sub_level: 66
os_patch_level: 2025-02
- version: "6.6"
sub_level: 77
os_patch_level: 2025-03
- version: "6.6"
sub_level: 82
os_patch_level: 2025-04
- version: "6.6"
sub_level: 87
os_patch_level: 2025-05
uses: ./.github/workflows/gki-kernel.yml
secrets: inherit
with:
version: android15-${{ matrix.version }}
version_name: android15-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android15-${{ matrix.version }}-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}
debug: ${{ inputs.debug || false }}
upload-artifacts:
needs: build-kernel
runs-on: ubuntu-latest
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
- uses: actions/checkout@v4
with:
path: KernelSU
fetch-depth: 0
- name: List artifacts
run: |
tree
- name: Download prebuilt toolchain
run: |
AOSP_MIRROR=https://android.googlesource.com
BRANCH=main-kernel-build-2024
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
pip3 install telethon
- name: Set boot sign key
env:
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
run: |
if [ ! -z "$BOOT_SIGN_KEY" ]; then
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
fi
- name: Build boot images
run: |
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
cd $GITHUB_WORKSPACE/KernelSU
export VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
cd -
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
- name: Display structure of boot files
run: ls -R
- name: Upload images artifact
uses: actions/upload-artifact@v4
with:
name: boot-images-android15
path: Image-android15*/*.img.gz

104
.github/workflows/build-kernel-a16.yml vendored Normal file
View File

@@ -0,0 +1,104 @@
name: Build Kernel - Android 16
on:
# push:
# branches: ["main", "ci", "checkci"]
# paths:
# - ".github/workflows/deps/gki/build-kernel-a16.yml"
# - ".github/workflows/deps/gki/gki-kernel.yml"
# - ".github/scripts/build_a13.sh"
# - "kernel/**"
workflow_call:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
workflow_dispatch:
inputs:
debug:
description: 'Build debug kernel'
required: false
type: boolean
default: false
jobs:
build-kernel:
if: github.event_name != 'pull_request' && github.ref != 'refs/heads/checkci'
strategy:
matrix:
include:
- version: "6.12"
sub_level: 38
os_patch_level: 2025-08
uses: ./.github/workflows/gki-kernel.yml
secrets: inherit
with:
version: android16-${{ matrix.version }}
version_name: android16-${{ matrix.version }}.${{ matrix.sub_level }}
tag: android16-${{ matrix.version }}-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
patch_path: ${{ matrix.version }}
debug: ${{ inputs.debug || false }}
upload-artifacts:
needs: build-kernel
runs-on: ubuntu-latest
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' || github.ref == 'refs/heads/ci' }}
env:
CHAT_ID: ${{ secrets.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
steps:
- name: Download artifacts
uses: actions/download-artifact@v6
- uses: actions/checkout@v5
with:
path: KernelSU
fetch-depth: 0
- name: List artifacts
run: |
tree
- name: Download prebuilt toolchain
run: |
AOSP_MIRROR=https://android.googlesource.com
BRANCH=main-kernel-build-2024
git clone $AOSP_MIRROR/platform/prebuilts/build-tools -b $BRANCH --depth 1 build-tools
git clone $AOSP_MIRROR/kernel/prebuilts/build-tools -b $BRANCH --depth 1 kernel-build-tools
git clone $AOSP_MIRROR/platform/system/tools/mkbootimg -b $BRANCH --depth 1
pip3 install telethon
- name: Set boot sign key
env:
BOOT_SIGN_KEY: ${{ secrets.BOOT_SIGN_KEY }}
run: |
if [ ! -z "$BOOT_SIGN_KEY" ]; then
echo "$BOOT_SIGN_KEY" > ./kernel-build-tools/linux-x86/share/avb/testkey_rsa2048.pem
fi
- name: Build boot images
run: |
export AVBTOOL=$GITHUB_WORKSPACE/kernel-build-tools/linux-x86/bin/avbtool
export GZIP=$GITHUB_WORKSPACE/build-tools/path/linux-x86/gzip
export LZ4=$GITHUB_WORKSPACE/build-tools/path/linux-x86/lz4
export MKBOOTIMG=$GITHUB_WORKSPACE/mkbootimg/mkbootimg.py
export UNPACK_BOOTIMG=$GITHUB_WORKSPACE/mkbootimg/unpack_bootimg.py
cd $GITHUB_WORKSPACE/KernelSU
export VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
cd -
bash $GITHUB_WORKSPACE/KernelSU/.github/scripts/build_a13.sh
- name: Display structure of boot files
run: ls -R
- name: Upload images artifact
uses: actions/upload-artifact@v5
with:
name: boot-images-android16
path: Image-android16*/*.img.gz

View File

@@ -1,74 +0,0 @@
name: Build LKM for KernelSU Local
on:
workflow_call:
inputs:
upload:
required: true
type: boolean
default: true
description: "Whether to upload to branch"
secrets:
# username:github_pat
TOKEN:
required: true
workflow_dispatch:
inputs:
upload:
required: true
type: boolean
default: true
description: "Whether to upload to branch"
jobs:
build-lkm:
strategy:
matrix:
include:
- version: "android12-5.10"
sub_level: 236
os_patch_level: 2025-05
- version: "android13-5.10"
sub_level: 234
os_patch_level: 2025-03
- version: "android13-5.15"
sub_level: 178
os_patch_level: 2025-03
- version: "android14-5.15"
sub_level: 178
os_patch_level: 2025-03
- version: "android14-6.1"
sub_level: 134
os_patch_level: 2025-05
- version: "android15-6.6"
sub_level: 87
os_patch_level: 2025-05
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
uses: ./.github/workflows/gki-kernel-local.yml
with:
version: ${{ matrix.version }}
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
tag: ${{ matrix.version }}-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
build_lkm: true
push-to-branch:
needs: [build-lkm]
runs-on: self-hosted
if: ${{ inputs.upload }}
steps:
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4
with:
path: bin/
merge-multiple: true
- name: Push to branch LKM
run: |
cd bin
git config --global init.defaultBranch lkm
git init
git remote add origin https://${{ secrets.TOKEN }}@github.com/${{ github.repository }}
git config --local user.name "github-actions[bot]"
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
find . -type f
git add .
git commit -m "Upload LKM from ${{ github.sha }}" -m "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
git push --force --set-upstream origin lkm

View File

@@ -1,74 +1,21 @@
name: Build LKM for KernelSU
on:
workflow_call:
inputs:
upload:
required: true
type: boolean
default: true
description: "Whether to upload to branch"
secrets:
# username:github_pat
TOKEN:
required: true
workflow_dispatch:
inputs:
upload:
required: true
type: boolean
default: true
description: "Whether to upload to branch"
jobs:
build-lkm:
strategy:
fail-fast: false
matrix:
include:
- version: "android12-5.10"
sub_level: 240
os_patch_level: 2025-09
- version: "android13-5.10"
sub_level: 238
os_patch_level: 2025-07
- version: "android13-5.15"
sub_level: 189
os_patch_level: 2025-09
- version: "android14-5.15"
sub_level: 185
os_patch_level: 2025-07
- version: "android14-6.1"
sub_level: 145
os_patch_level: 2025-09
- version: "android15-6.6"
sub_level: 98
os_patch_level: 2025-09
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
uses: ./.github/workflows/gki-kernel.yml
kmi:
- android12-5.10
- android13-5.10
- android13-5.15
- android14-5.15
- android14-6.1
- android15-6.6
- android16-6.12
uses: ./.github/workflows/ddk-lkm.yml
with:
version: ${{ matrix.version }}
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
tag: ${{ matrix.version }}-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
build_lkm: true
push-to-branch:
needs: [build-lkm]
runs-on: ubuntu-latest
if: ${{ inputs.upload }}
steps:
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4
with:
path: bin/
merge-multiple: true
- name: Push to branch LKM
run: |
cd bin
git config --global init.defaultBranch lkm
git init
git remote add origin https://${{ secrets.TOKEN }}@github.com/${{ github.repository }}
git config --local user.name "github-actions[bot]"
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
find . -type f
git add .
git commit -m "Upload LKM from ${{ github.sha }}" -m "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
git push --force --set-upstream origin lkm
kmi: ${{ matrix.kmi }}
ddk_release: '20251104'

View File

@@ -1,245 +0,0 @@
name: Build Manager Manual
on:
workflow_dispatch:
inputs:
build_lkm:
required: true
type: choice
default: "auto"
options:
- "true"
- "false"
- "auto"
description: "Whether to build lkm"
upload_lkm:
required: true
type: boolean
default: true
description: "Whether to upload lkm"
jobs:
check-build-lkm:
runs-on: self-hosted
outputs:
build_lkm: ${{ steps.check-build.outputs.build_lkm }}
upload_lkm: ${{ steps.check-build.outputs.upload_lkm }}
steps:
- name: check build
id: check-build
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.build_lkm }}" != "auto" ]; then
kernel_changed="${{ inputs.build_lkm }}"
else
kernel_changed=true
mkdir tmp
cd tmp
git config --global init.defaultBranch bot
git config --global user.name 'Bot'
git config --global user.email 'bot@github.shirkneko.io'
git init .
git remote add origin https://github.com/${{ github.repository }}
CURRENT_COMMIT="${{ github.event.head_commit.id }}"
git fetch origin $CURRENT_COMMIT --depth=1
git fetch origin lkm --depth=1
LKM_COMMIT="$(git log --format=%B -n 1 origin/lkm | head -n 1)"
LKM_COMMIT="${LKM_COMMIT#Upload LKM from }"
LKM_COMMIT=$(echo "$LKM_COMMIT" | tr -d '[:space:]')
echo "LKM_COMMIT=$LKM_COMMIT"
git fetch origin "$LKM_COMMIT" --depth=1
git diff --quiet "$LKM_COMMIT" "$CURRENT_COMMIT" -- kernel :!kernel/setup.sh .github/workflows/build-lkm-local.yml .github/workflows/build-kernel-*.yml && kernel_changed=false
cd ..
rm -rf tmp
fi
if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == 'refs/heads/main' ]; then
need_upload=true
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
need_upload="${{ inputs.upload_lkm }}"
else
need_upload=false
fi
echo "kernel changed: $kernel_changed"
echo "need upload: $need_upload"
echo "build_lkm=$kernel_changed" >> "$GITHUB_OUTPUT"
echo "upload_lkm=$need_upload" >> "$GITHUB_OUTPUT"
build-lkm:
needs: check-build-lkm
uses: ./.github/workflows/build-lkm-local.yml
if: ${{ needs.check-build-lkm.outputs.build_lkm == 'true' }}
with:
upload: ${{ needs.check-build-lkm.outputs.upload_lkm == 'true' }}
secrets: inherit
build-susfs:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy:
matrix:
include:
- target: aarch64-linux-android
os: ubuntu-latest
uses: ./.github/workflows/susfs.yml
with:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
build-kpmmgr:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy:
matrix:
include:
- target: aarch64-linux-android
os: ubuntu-latest
uses: ./.github/workflows/kpmmgr.yml
with:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
build-ksud:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy:
matrix:
include:
- target: aarch64-linux-android
os: ubuntu-latest
- target: x86_64-linux-android
os: ubuntu-latest
- target: armv7-linux-androideabi
os: ubuntu-latest
uses: ./.github/workflows/ksud.yml
with:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
pack_lkm: true
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
build-manager:
if: ${{ always() }}
needs: build-ksud
runs-on: self-hosted
defaults:
run:
working-directory: ./manager
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Write key
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref == 'refs/heads/susfs' || github.ref_type == 'tag' }}
run: |
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
{
echo KEYSTORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}'
echo KEY_ALIAS='${{ secrets.KEY_ALIAS }}'
echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}'
echo KEYSTORE_FILE='key.jks'
} >> gradle.properties
echo "${{ secrets.KEYSTORE }}" | base64 -d > key.jks
fi
- name: Download arm64 susfs
uses: actions/download-artifact@v4
with:
name: susfs-aarch64-linux-android
path: .
- name: Download arm64 kpmmgr
uses: actions/download-artifact@v4
with:
name: kpmmgr-aarch64-linux-android
path: .
- name: Download arm64 ksud
uses: actions/download-artifact@v4
with:
name: ksud-aarch64-linux-android
path: .
- name: Download x86_64 ksud
uses: actions/download-artifact@v4
with:
name: ksud-x86_64-linux-android
path: .
- name: Download arm ksud
uses: actions/download-artifact@v4
with:
name: ksud-armv7-linux-androideabi
path: .
- name: Copy ksud to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
mkdir -p app/src/main/jniLibs/x86_64
mkdir -p app/src/main/jniLibs/armeabi-v7a
cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
cp -f ../armv7-linux-androideabi/release/zakozako ../manager/app/src/main/jniLibs/armeabi-v7a/libzakozako.so
- name: Copy kpmmgr to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
- name: Copy susfs to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
- name: Build with Gradle
run: |
export ANDROID_HOME=/root/.android/sdk
export PATH=$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$PATH
./gradlew clean assembleRelease
- name: Upload build artifact
uses: actions/upload-artifact@v4
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
with:
name: manager
path: manager/app/build/outputs/apk/release/*.apk
- name: Upload mappings
uses: actions/upload-artifact@v4
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
with:
name: "mappings"
path: "manager/app/build/outputs/mapping/release/"
- name: Bot session cache
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
id: bot_session_cache
uses: actions/cache@v4
with:
path: scripts/ksubot.session
key: ${{ runner.os }}-bot-session
- name: Upload to telegram
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
env:
CHAT_ID: ${{ vars.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
TITLE: Manager
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
export VERSION=$(git rev-list --count HEAD)
APK=$(find ./app/build/outputs/apk/release -name "*.apk")
python3 $GITHUB_WORKSPACE/scripts/ksubot.py $APK
fi

View File

@@ -2,117 +2,31 @@ name: Build Manager
on:
push:
branches: [ "main", "ci" ]
branches: [ "main", "dev", "ci", "miuix" ]
paths:
- '.github/workflows/build-manager.yml'
- '.github/workflows/build-lkm.yml'
- 'manager/**'
- 'kernel/**'
- 'userspace/ksud/**'
- 'userspace/susfs/**'
- 'userspace/kpmmgr/**'
- 'userspace/user_scanner/**'
pull_request:
branches: [ "main" ]
branches: [ "main", "dev", "miuix" ]
paths:
- '.github/workflows/build-manager.yml'
- '.github/workflows/build-lkm.yml'
- 'manager/**'
- 'kernel/**'
- 'userspace/ksud/**'
- 'userspace/user_scanner/**'
workflow_call:
workflow_dispatch:
inputs:
build_lkm:
required: true
type: choice
default: "auto"
options:
- "true"
- "false"
- "auto"
description: "Whether to build lkm"
upload_lkm:
required: true
type: boolean
default: true
description: "Whether to upload lkm"
jobs:
check-build-lkm:
runs-on: ubuntu-latest
outputs:
build_lkm: ${{ steps.check-build.outputs.build_lkm }}
upload_lkm: ${{ steps.check-build.outputs.upload_lkm }}
steps:
- name: check build
id: check-build
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.build_lkm }}" != "auto" ]; then
kernel_changed="${{ inputs.build_lkm }}"
else
kernel_changed=true
mkdir tmp
cd tmp
git config --global init.defaultBranch bot
git config --global user.name 'Bot'
git config --global user.email 'bot@github.shirkneko.io'
git init .
git remote add origin https://github.com/${{ github.repository }}
CURRENT_COMMIT="${{ github.event.head_commit.id }}"
git fetch origin $CURRENT_COMMIT --depth=1
git fetch origin lkm --depth=1
LKM_COMMIT="$(git log --format=%B -n 1 origin/lkm | head -n 1)"
LKM_COMMIT="${LKM_COMMIT#Upload LKM from }"
LKM_COMMIT=$(echo "$LKM_COMMIT" | tr -d '[:space:]')
echo "LKM_COMMIT=$LKM_COMMIT"
git fetch origin "$LKM_COMMIT" --depth=1
git diff --quiet "$LKM_COMMIT" "$CURRENT_COMMIT" -- kernel :!kernel/setup.sh .github/workflows/build-lkm.yml .github/workflows/build-kernel-*.yml && kernel_changed=false
cd ..
rm -rf tmp
fi
if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == 'refs/heads/main' ]; then
need_upload=true
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
need_upload="${{ inputs.upload_lkm }}"
else
need_upload=false
fi
echo "kernel changed: $kernel_changed"
echo "need upload: $need_upload"
echo "build_lkm=$kernel_changed" >> "$GITHUB_OUTPUT"
echo "upload_lkm=$need_upload" >> "$GITHUB_OUTPUT"
build-lkm:
needs: check-build-lkm
uses: ./.github/workflows/build-lkm.yml
if: ${{ needs.check-build-lkm.outputs.build_lkm == 'true' }}
with:
upload: ${{ needs.check-build-lkm.outputs.upload_lkm == 'true' }}
secrets: inherit
build-susfs:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy:
matrix:
include:
- target: aarch64-linux-android
os: ubuntu-latest
uses: ./.github/workflows/susfs.yml
with:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
build-kpmmgr:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy:
matrix:
include:
- target: aarch64-linux-android
os: ubuntu-latest
uses: ./.github/workflows/kpmmgr.yml
with:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
build-user_scanner:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
needs: build-lkm
strategy:
matrix:
include:
@@ -124,8 +38,7 @@ jobs:
os: ${{ matrix.os }}
build-ksud:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
needs: build-lkm
strategy:
matrix:
include:
@@ -139,13 +52,13 @@ jobs:
with:
target: ${{ matrix.target }}
os: ${{ matrix.os }}
pack_lkm: true
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
build-manager:
if: ${{ always() }}
needs: build-ksud
runs-on: ubuntu-latest
strategy:
matrix:
spoofed: ["true","false"]
defaults:
run:
working-directory: ./manager
@@ -165,8 +78,34 @@ jobs:
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Determine manager variant for telegram bot
id: determine
run: |
if [ "${{ github.ref_name }}" == "miuix" ] && [ "${{ matrix.spoofed }}" == "true" ]; then
echo "SKIP=true" >> $GITHUB_OUTPUT
exit 0
fi
if [ "${{ github.ref_name }}" == "miuix" ]; then
echo "title=Manager" >> $GITHUB_OUTPUT
echo "topicid=${{ vars.MESSAGE_MIUIX_THREAD_ID }}" >> $GITHUB_OUTPUT
elif [ "${{ matrix.spoofed }}" == "true" ]; then
echo "title=Spoofed-Manager" >> $GITHUB_OUTPUT
# maybe need a new var
echo "topicid=${{ vars.MESSAGE_SPOOFED_THREAD_ID }}" >> $GITHUB_OUTPUT
else
echo "title=Manager" >> $GITHUB_OUTPUT
echo "topicid=${{ vars.MESSAGE_THREAD_ID }}" >> $GITHUB_OUTPUT
fi
- name: Run randomizer
if: ${{ matrix.spoofed == 'true' && steps.determine.outputs.SKIP != 'true' }}
run: |
chmod +x randomizer
./randomizer
- name: Write key
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref == 'refs/heads/susfs' || github.ref_type == 'tag' }}
if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/miuix' )) || github.ref_type == 'tag' }}
run: |
if [ ! -z "${{ secrets.KEYSTORE }}" ]; then
{
@@ -196,18 +135,6 @@ jobs:
name: userscanner-all-linux-android
path: .
- name: Download arm64 susfs
uses: actions/download-artifact@v4
with:
name: susfs-aarch64-linux-android
path: .
- name: Download arm64 kpmmgr
uses: actions/download-artifact@v4
with:
name: kpmmgr-aarch64-linux-android
path: .
- name: Download arm64 ksud
uses: actions/download-artifact@v4
with:
@@ -231,19 +158,9 @@ jobs:
mkdir -p app/src/main/jniLibs/arm64-v8a
mkdir -p app/src/main/jniLibs/x86_64
mkdir -p app/src/main/jniLibs/armeabi-v7a
cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
cp -f ../armv7-linux-androideabi/release/zakozako ../manager/app/src/main/jniLibs/armeabi-v7a/libzakozako.so
- name: Copy kpmmgr to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
- name: Copy susfs to app jniLibs
run: |
mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
cp -f ../aarch64-linux-android/release/ksud ../manager/app/src/main/jniLibs/arm64-v8a/libksud.so
cp -f ../x86_64-linux-android/release/ksud ../manager/app/src/main/jniLibs/x86_64/libksud.so
cp -f ../armv7-linux-androideabi/release/ksud ../manager/app/src/main/jniLibs/armeabi-v7a/libksud.so
- name: Copy user_scanner to app jniLibs
run: |
@@ -255,40 +172,34 @@ jobs:
cp -f ../armeabi-v7a/uid_scanner ../manager/app/src/main/jniLibs/armeabi-v7a/libuid_scanner.so
- name: Build with Gradle
if: ${{ steps.determine.outputs.SKIP != 'true' }}
run: ./gradlew clean assembleRelease
- name: Upload build artifact
uses: actions/upload-artifact@v4
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/miuix' )) || github.ref_type == 'tag' }}
with:
name: manager
name: ${{ steps.determine.outputs.title }}
path: manager/app/build/outputs/apk/release/*.apk
- name: Upload mappings
uses: actions/upload-artifact@v4
if: ${{ ( github.event_name != 'pull_request' && github.ref == 'refs/heads/main' ) || github.ref_type == 'tag' }}
if: ${{ ( github.event_name != 'pull_request' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/miuix' )) || github.ref_type == 'tag' }}
with:
name: "mappings"
name: "${{ steps.determine.outputs.title }}-mappings"
path: "manager/app/build/outputs/mapping/release/"
- name: Bot session cache
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
id: bot_session_cache
uses: actions/cache@v4
with:
path: scripts/ksubot.session
key: ${{ runner.os }}-bot-session
- name: Upload to telegram
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true' && steps.determine.outputs.SKIP != 'true'
env:
CHAT_ID: ${{ vars.CHAT_ID }}
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
MESSAGE_THREAD_ID: ${{ steps.determine.outputs.topicid }}
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
COMMIT_URL: ${{ github.event.head_commit.url }}
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
TITLE: Manager
TITLE: ${{ steps.determine.outputs.title }}
BRANCH: ${{ github.ref_name }}
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
export VERSION=$(git rev-list --count HEAD)

View File

@@ -34,4 +34,4 @@ jobs:
- name: Run clippy
run: |
cross clippy --manifest-path userspace/ksud/Cargo.toml --target aarch64-linux-android --release
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release
cross clippy --manifest-path userspace/ksud/Cargo.toml --target x86_64-linux-android --release

53
.github/workflows/ddk-lkm.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Build KernelSU Kernel Module
on:
workflow_call:
inputs:
kmi:
description: 'KMI version'
required: true
type: string
ddk_release:
description: 'DDK release version'
required: false
default: '20251104'
type: string
jobs:
build-kernelsu-ko:
name: Build kernelsu.ko for ${{ inputs.kmi }}
runs-on: ubuntu-latest
container:
image: ghcr.io/ylarod/ddk:${{ inputs.kmi }}-${{ inputs.ddk_release }}
options: --privileged
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Build kernelsu.ko
run: |
git config --global --add safe.directory /__w/SukiSU-Ultra/SukiSU-Ultra
cd kernel
echo "=== Building kernelsu.ko for KMI: ${{ inputs.kmi }} ==="
CONFIG_KSU=m CONFIG_KSU_MANUAL_SU=y make
echo "=== Build completed ==="
# Create output directory in GitHub workspace
mkdir -p /github/workspace/out
# Copy with KMI-specific naming
OUTPUT_NAME="${{ inputs.kmi }}_kernelsu.ko"
cp kernelsu.ko "/github/workspace/out/$OUTPUT_NAME"
echo "Copied to: /github/workspace/out/$OUTPUT_NAME"
ls -la "/github/workspace/out/$OUTPUT_NAME"
echo "Size: $(du -h "/github/workspace/out/$OUTPUT_NAME" | cut -f1)"
llvm-strip -d "/github/workspace/out/$OUTPUT_NAME"
echo "Size after stripping: $(du -h "/github/workspace/out/$OUTPUT_NAME" | cut -f1)"
- name: Upload kernelsu.ko artifact
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.kmi }}-lkm
path: /github/workspace/out/${{ inputs.kmi }}_kernelsu.ko

67
.github/workflows/deploy-website.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Deploy Website
on:
push:
branches:
- main
- website
paths:
- '.github/workflows/deploy-website.yml'
- 'website/**'
workflow_dispatch:
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write
# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: pages
cancel-in-progress: false
jobs:
# Build job
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./website
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Not needed if lastUpdated is not enabled
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: latest
cache: yarn # or pnpm / yarn
cache-dependency-path: website/yarn.lock
- name: Setup Pages
uses: actions/configure-pages@v5
- name: Install dependencies
run: yarn install --frozen-lockfile
- name: Build with VitePress
run: |
yarn docs:build
touch docs/.vitepress/dist/.nojekyll
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: website/docs/.vitepress/dist
# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build
runs-on: ubuntu-latest
name: Deploy
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

View File

@@ -1,252 +0,0 @@
name: GKI Kernel Build Local
on:
workflow_call:
inputs:
version:
required: true
type: string
description: >
Output directory of gki,
for example: android12-5.10
version_name:
required: true
type: string
description: >
With SUBLEVEL of kernel,
for example: android12-5.10.66
tag:
required: true
type: string
description: >
Part of branch name of common kernel manifest,
for example: android12-5.10-2021-11
os_patch_level:
required: false
type: string
description: >
Patch level of common kernel manifest,
for example: 2021-11
default: 2022-05
patch_path:
required: false
type: string
description: >
Directory name of .github/patches/<patch_path>
for example: 5.10
use_cache:
required: false
type: boolean
default: true
embed_ksud:
required: false
type: string
default: ksud-aarch64-linux-android
description: >
Artifact name of prebuilt ksud to be embedded
for example: ksud-aarch64-linux-android
debug:
required: false
type: boolean
default: false
build_lkm:
required: false
type: boolean
default: false
secrets:
BOOT_SIGN_KEY:
required: false
CHAT_ID:
required: false
BOT_TOKEN:
required: false
MESSAGE_THREAD_ID:
required: false
jobs:
build:
name: Build ${{ inputs.version_name }}
runs-on: self-hosted
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_HARDLINK: "true"
steps:
- uses: actions/checkout@v4
with:
path: KernelSU
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Setup kernel source
run: |
echo "Free space:"
df -h
cd $GITHUB_WORKSPACE
sudo apt-get install repo -y
export REPO_URL='https://mirrors.tuna.tsinghua.edu.cn/git/git-repo'
mkdir android-kernel && cd android-kernel
repo init --depth=1 --u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.35
REMOTE_BRANCH=$(git ls-remote https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/kernel/common ${{ inputs.tag }})
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
if grep -q deprecated <<< $REMOTE_BRANCH; then
echo "Found deprecated branch: ${{ inputs.tag }}"
sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' $DEFAULT_MANIFEST_PATH
cat $DEFAULT_MANIFEST_PATH
fi
repo --version
repo --trace sync -c -j$(nproc --all) --no-tags
df -h
- name: Setup KernelSU
env:
PATCH_PATH: ${{ inputs.patch_path }}
IS_DEBUG_KERNEL: ${{ inputs.debug }}
run: |
cd $GITHUB_WORKSPACE/android-kernel
echo "[+] KernelSU setup"
GKI_ROOT=$(pwd)
echo "[+] GKI_ROOT: $GKI_ROOT"
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
echo "[+] Add KernelSU driver to Makefile"
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
echo "[+] Apply Compilation Patches"
if [ ! -e build/build.sh ]; then
GLIBC_VERSION=$(ldd --version 2>/dev/null | head -n 1 | awk '{print $NF}')
echo "GLIBC_VERSION: $GLIBC_VERSION"
if [ "$(printf '%s\n' "2.38" "$GLIBC_VERSION" | sort -V | head -n1)" = "2.38" ]; then
echo "Patching resolve_btfids/Makefile"
cd $GKI_ROOT/common/ && sed -i '/\$(Q)\$(MAKE) -C \$(SUBCMD_SRC) OUTPUT=\$(abspath \$(dir \$@))\/ \$(abspath \$@)/s//$(Q)$(MAKE) -C $(SUBCMD_SRC) EXTRA_CFLAGS="$(CFLAGS)" OUTPUT=$(abspath $(dir $@))\/ $(abspath $@)/' tools/bpf/resolve_btfids/Makefile || echo "No patch needed."
fi
fi
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
echo "[+] Enable debug features for kernel"
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
fi
repo status
echo "[+] KernelSU setup done."
- name: Symbol magic
run: |
echo "[+] Export all symbol from abi_gki_aarch64.xml"
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
echo "[+] Add KernelSU symbols"
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
- name: Setup ccache
if: inputs.use_cache == true
uses: hendrikmuhs/ccache-action@v1
with:
key: gki-kernel-aarch64-${{ inputs.version_name }}
max-size: 2G
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
- name: Setup for LKM
if: ${{ inputs.build_lkm == true }}
working-directory: android-kernel
run: |
pip install ast-grep-cli
sudo apt-get install llvm-15 -y
ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c
sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig
echo "drivers/kernelsu/kernelsu.ko" >> common/android/gki_aarch64_modules
# bazel build, android14-5.15, android14-6.1 use bazel
if [ ! -e build/build.sh ]; then
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
if [ -e common/modules.bzl ]; then
sed -i 's/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ "drivers\/kernelsu\/kernelsu.ko",/g' common/modules.bzl
fi
else
TARGET_FILE="build/kernel/build.sh"
if [ ! -e "$TARGET_FILE" ]; then
TARGET_FILE="build/build.sh"
fi
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE"
sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE
sed -i 's@${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
fi
- name: Make working directory clean to avoid dirty
working-directory: android-kernel
run: |
if [ -e common/BUILD.bazel ]; then
sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
fi
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
git config --global user.email "bot@kernelsu.org"
git config --global user.name "KernelSUBot"
cd common/ && git add -A && git commit -a -m "Add KernelSU"
repo status
- name: Build Kernel/LKM
working-directory: android-kernel
run: |
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
fi
if [ -e build/build.sh ]; then
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
else
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
fi
- name: Prepare artifacts
id: prepareArtifacts
run: |
OUTDIR=android-kernel/out/${{ inputs.version }}/dist
if [ ! -e $OUTDIR ]; then
OUTDIR=android-kernel/dist
fi
mkdir output
if [ "${{ inputs.build_lkm}}" = "true" ]; then
llvm-strip-15 -d $OUTDIR/kernelsu.ko
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
else
cp $OUTDIR/Image ./output/
cp $OUTDIR/Image.lz4 ./output/
git clone https://github.com/Kernel-SU/AnyKernel3
rm -rf ./AnyKernel3/.git
cp $OUTDIR/Image ./AnyKernel3/
fi
- name: Upload Image and Image.gz
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == false }}
with:
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./output/*
- name: Upload AnyKernel3
if: ${{ inputs.build_lkm == false }}
uses: actions/upload-artifact@v4
with:
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./AnyKernel3/*
- name: Upload LKM
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == true }}
with:
name: ${{ inputs.version }}-lkm
path: ./output/*_kernelsu.ko

View File

@@ -1,79 +0,0 @@
name: GKI Kernel Build
on:
workflow_call:
inputs:
version:
required: true
type: string
description: >
Output directory of gki,
for example: android12-5.10
version_name:
required: true
type: string
description: >
With SUBLEVEL of kernel,
for example: android12-5.10.66
tag:
required: true
type: string
description: >
Part of branch name of common kernel manifest,
for example: android12-5.10-2021-11
os_patch_level:
required: false
type: string
description: >
Patch level of common kernel manifest,
for example: 2021-11
default: 2022-05
patch_path:
required: false
type: string
description: >
Directory name of .github/patches/<patch_path>
for example: 5.10
use_cache:
required: false
type: boolean
default: true
embed_ksud:
required: false
type: string
default: ksud-aarch64-linux-android
description: >
Artifact name of prebuilt ksud to be embedded
for example: ksud-aarch64-linux-android
debug:
required: false
type: boolean
default: false
build_lkm:
required: false
type: boolean
default: false
secrets:
BOOT_SIGN_KEY:
required: false
CHAT_ID:
required: false
BOT_TOKEN:
required: false
MESSAGE_THREAD_ID:
required: false
jobs:
mock_build:
name: Mock build ${{ inputs.version_name }}
runs-on: ubuntu-latest
steps:
- name: Create mocking ko
run: |
echo "${{ inputs.version }}_kernelsu.ko" > ${{ inputs.version }}_kernelsu.ko
- name: Upload LKM
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == true }}
with:
name: ${{ inputs.version }}-lkm
path: ./*_kernelsu.ko

View File

@@ -77,10 +77,10 @@ jobs:
with:
root-reserve-mb: 8192
temp-reserve-mb: 2048
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
remove-dotnet: "true"
remove-android: "true"
remove-haskell: "true"
remove-codeql: "true"
- uses: actions/checkout@v4
with:
@@ -103,7 +103,7 @@ jobs:
cd $GITHUB_WORKSPACE
sudo apt-get install repo -y
mkdir android-kernel && cd android-kernel
repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.35
repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.16
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
if grep -q deprecated <<< $REMOTE_BRANCH; then
@@ -195,12 +195,35 @@ jobs:
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
fi
- name: Append ashmem exports if missing
if: startsWith(inputs.version, 'android16-6.12')
working-directory: android-kernel
run: |
FILE=common/drivers/staging/android/ashmem.c
if [[ -f "$FILE" ]] && ! grep -q 'is_ashmem_file' "$FILE"; then
cat >>"$FILE" <<'EOF'
bool is_ashmem_file(struct file *file) { return false; }
int ashmem_area_name(struct file *file, char *name) { return 0; }
long ashmem_area_size(struct file *file) { return 0; }
struct file *ashmem_area_vmfile(struct file *file) { return NULL; }
EXPORT_SYMBOL_GPL(is_ashmem_file);
EXPORT_SYMBOL_GPL(ashmem_area_name);
EXPORT_SYMBOL_GPL(ashmem_area_size);
EXPORT_SYMBOL_GPL(ashmem_area_vmfile);
EOF
fi
sed -i -E 's/\$\(CONFIG_ANDROID_BINDER_IPC_RUST\)/m/g' common/drivers/android/Makefile
- name: Make working directory clean to avoid dirty
working-directory: android-kernel
run: |
if [ -e common/BUILD.bazel ]; then
sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
# Fix bazel build error
if [ -f common/BUILD.bazel ]; then
[ -f android/abi_gki_protected_exports_aarch64 ] || sed -i '/^[[:space:]]*"protected_exports_list"[[:space:]]*:[[:space:]]*"android\/abi_gki_protected_exports_aarch64",$/d' common/BUILD.bazel
fi
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
git config --global user.email "bot@kernelsu.org"
git config --global user.name "KernelSUBot"
@@ -217,7 +240,11 @@ jobs:
if [ -e build/build.sh ]; then
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
else
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
if [ "${{ inputs.version }}" == "android16-6.12" ]; then
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --destdir=dist
else
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
fi
fi
- name: Prepare artifacts
@@ -228,7 +255,7 @@ jobs:
OUTDIR=android-kernel/dist
fi
mkdir output
if [ "${{ inputs.build_lkm}}" = "true" ]; then
if [ "${{ inputs.build_lkm}}" = "true" ]; then
llvm-strip-15 -d $OUTDIR/kernelsu.ko
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
else

View File

@@ -1,40 +0,0 @@
name: Build kpmmgr
on:
push:
branches: [ "mian" ]
paths:
- '.github/workflows/kpmmgr.yml'
- 'userspace/kpmmgr/**'
workflow_dispatch:
workflow_call:
inputs:
target:
required: true
type: string
os:
required: false
type: string
default: self-hosted
jobs:
build-susfs:
name: Build userspace kpmmgr
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build kpmmgr
working-directory: ./userspace/kpmmgr
run: |
$ANDROID_NDK_HOME/ndk-build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v4
with:
name: kpmmgr-aarch64-linux-android
path: ./userspace/kpmmgr/libs

View File

@@ -9,10 +9,6 @@ on:
required: false
type: string
default: ubuntu-latest
pull_lkm:
required: false
type: boolean
default: true
pack_lkm:
required: false
type: boolean
@@ -21,6 +17,8 @@ on:
required: false
type: boolean
default: true
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ${{ inputs.os }}
@@ -29,36 +27,19 @@ jobs:
with:
fetch-depth: 0
- name: Pull lkms from branch
if: ${{ inputs.pack_lkm && inputs.pull_lkm }}
uses: actions/checkout@v4
with:
ref: lkm
path: lkm
- name: Download lkms from artifacts
if: ${{ inputs.pack_lkm && !inputs.pull_lkm }}
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Prepare LKM files
if: ${{ inputs.pack_lkm && inputs.pull_lkm }}
run: |
cp lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
- name: Prepare LKM files
if: ${{ inputs.pack_lkm && !inputs.pull_lkm }}
- name: Prepare LKM fies
if: ${{ inputs.pack_lkm }}
run: |
cp android*-lkm/*_kernelsu.ko ./userspace/ksud/bin/aarch64/
- name: Setup rustup
run: |
rustup update stable
rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-darwin
- uses: Swatinem/rust-cache@v2
with:
workspaces: userspace/ksud
cache-targets: false
- name: Install cross
run: |
@@ -71,4 +52,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: ksud-${{ inputs.target }}
path: userspace/ksud/target/**/release/zakozako*
path: userspace/ksud/target/**/release/ksud*

View File

@@ -16,7 +16,7 @@ on:
jobs:
shellcheck:
runs-on: self-hosted
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

View File

@@ -1,40 +0,0 @@
name: Build susfs
on:
push:
branches: [ "mian" ]
paths:
- '.github/workflows/susfs.yml'
- 'userspace/susfs/**'
workflow_dispatch:
workflow_call:
inputs:
target:
required: true
type: string
os:
required: false
type: string
default: self-hosted
jobs:
build-susfs:
name: Build userspace susfs
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Build susfs
working-directory: ./userspace/susfs
run: |
$ANDROID_NDK_HOME/ndk-build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v4
with:
name: susfs-aarch64-linux-android
path: ./userspace/susfs/libs

2
.gitignore vendored
View File

@@ -1,3 +1,3 @@
.idea
.vscode
CLAUDE.md
.DS_Store

View File

@@ -44,7 +44,7 @@ Generally need to modify the `do_execve` and `compat_do_execve` methods in `fs/e
.ptr.compat = __envp,
};
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0); // 32-bit su and 32-on-64 support
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
@@ -237,34 +237,3 @@ Need to modify the `input_event` method in `drivers/input/input.c`, not `input_h
spin_lock_irqsave(&dev->event_lock, flags);
```
### devpts Hook (`pty.c`)
Need to modify the `pts_unix98_lookup` method in `drivers/tty/pty.c`
```patch
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -31,6 +31,10 @@
#include <linux/compat.h>
#include "tty.h"
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../../drivers/kernelsu/ksu_trace.h>
+#endif
+
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
{
struct tty_struct *tty;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
+#endif
+
mutex_lock(&devpts_mutex);
tty = devpts_get_priv(file->f_path.dentry);
mutex_unlock(&devpts_mutex);
```

View File

@@ -44,7 +44,7 @@
.ptr.compat = __envp,
};
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0)); // 32-bit su and 32-on-64 support
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
@@ -236,35 +236,4 @@
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
```
### devpts 钩子 (`pty.c`)
需要修改 `drivers/tty/pty.c``pts_unix98_lookup` 方法
```patch
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -31,6 +31,10 @@
#include <linux/compat.h>
#include "tty.h"
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../../drivers/kernelsu/ksu_trace.h>
+#endif
+
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
{
struct tty_struct *tty;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
+#endif
+
mutex_lock(&devpts_mutex);
tty = devpts_get_priv(file->f_path.dentry);
mutex_unlock(&devpts_mutex);
```
```

View File

@@ -56,8 +56,8 @@ ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false # Unknown to clang-format-4.0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 8
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
@@ -501,7 +501,7 @@ IncludeCategories:
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
#IndentPPDirectives: None # Unknown to clang-format-5.0
IndentWidth: 8
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
@@ -511,7 +511,7 @@ MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
ObjCBlockIndentWidth: 8
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
@@ -543,6 +543,6 @@ SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
TabWidth: 8
UseTab: Always
TabWidth: 4
UseTab: Never
...

22
kernel/.gitignore vendored Normal file
View File

@@ -0,0 +1,22 @@
.cache/
.thinlto-cache/
compile_commands.json
*.ko
*.o
*.mod
*.lds
*.mod.o
.*.o*
.*.mod*
*.ko*
*.mod.c
*.symvers*
*.order
.*.ko.cmd
.tmp_versions/
libs/
obj/
CLAUDE.md
.ddk-version
.vscode/settings.json

11
kernel/.vscode/c_cpp_properties.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
"configurations": [
{
"name": "Linux",
"cStandard": "c11",
"intelliSenseMode": "gcc-arm64",
"compileCommands": "${workspaceFolder}/compile_commands.json"
}
],
"version": 4
}

92
kernel/.vscode/generate_compdb.py vendored Normal file
View File

@@ -0,0 +1,92 @@
#!/usr/bin/env python3
from __future__ import print_function, division
import argparse
import fnmatch
import functools
import json
import math
import multiprocessing
import os
import re
import sys
CMD_VAR_RE = re.compile(r'^\s*(?:saved)?cmd_(\S+)\s*:=\s*(.+)\s*$', re.MULTILINE)
SOURCE_VAR_RE = re.compile(r'^\s*source_(\S+)\s*:=\s*(.+)\s*$', re.MULTILINE)
def print_progress_bar(progress):
progress_bar = '[' + '|' * int(50 * progress) + '-' * int(50 * (1.0 - progress)) + ']'
print('\r', progress_bar, "{0:.1%}".format(progress), end='\r', file=sys.stderr)
def parse_cmd_file(out_dir, cmdfile_path):
with open(cmdfile_path, 'r') as cmdfile:
cmdfile_content = cmdfile.read()
commands = { match.group(1): match.group(2) for match in CMD_VAR_RE.finditer(cmdfile_content) }
sources = { match.group(1): match.group(2) for match in SOURCE_VAR_RE.finditer(cmdfile_content) }
return [{
'directory': out_dir,
'command': commands[o_file_name],
'file': source,
'output': o_file_name
} for o_file_name, source in sources.items()]
def gen_compile_commands(cmd_file_search_path, out_dir):
print("Building *.o.cmd file list...", file=sys.stderr)
out_dir = os.path.abspath(out_dir)
if not cmd_file_search_path:
cmd_file_search_path = [out_dir]
cmd_files = []
for search_path in cmd_file_search_path:
if (os.path.isdir(search_path)):
for cur_dir, subdir, files in os.walk(search_path):
cmd_files.extend(os.path.join(cur_dir, cmdfile_name) for cmdfile_name in fnmatch.filter(files, '*.o.cmd'))
else:
cmd_files.extend(search_path)
if not cmd_files:
print("No *.o.cmd files found in", ", ".join(cmd_file_search_path), file=sys.stderr)
return
print("Parsing *.o.cmd files...", file=sys.stderr)
n_processed = 0
print_progress_bar(0)
compdb = []
pool = multiprocessing.Pool()
try:
for compdb_chunk in pool.imap_unordered(functools.partial(parse_cmd_file, out_dir), cmd_files, chunksize=int(math.sqrt(len(cmd_files)))):
compdb.extend(compdb_chunk)
n_processed += 1
print_progress_bar(n_processed / len(cmd_files))
finally:
pool.terminate()
pool.join()
print(file=sys.stderr)
print("Writing compile_commands.json...", file=sys.stderr)
with open('compile_commands.json', 'w') as compdb_file:
json.dump(compdb, compdb_file, indent=1)
def main():
cmd_parser = argparse.ArgumentParser()
cmd_parser.add_argument('-O', '--out-dir', type=str, default=os.getcwd(), help="Build output directory")
cmd_parser.add_argument('cmd_file_search_path', nargs='*', help="*.cmd file search path")
gen_compile_commands(**vars(cmd_parser.parse_args()))
if __name__ == '__main__':
main()

16
kernel/.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,16 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Generate compile_commands.json",
"type": "process",
"command": "python",
"args": [
"${workspaceRoot}/.vscode/generate_compdb.py"
],
"problemMatcher": []
}
]
}

View File

@@ -1,57 +1,42 @@
menu "KernelSU"
config KSU
tristate "KernelSU function support"
depends on OVERLAY_FS
default y
help
Enable kernel-level root privileges on Android System.
To compile as a module, choose M here: the
module will be called kernelsu.
tristate "KernelSU function support"
default y
help
Enable kernel-level root privileges on Android System.
To compile as a module, choose M here: the
module will be called kernelsu.
config KSU_DEBUG
bool "KernelSU debug mode"
depends on KSU
default n
help
Enable KernelSU debug mode.
bool "KernelSU debug mode"
depends on KSU
default n
help
Enable KernelSU debug mode.
config KSU_MANUAL_SU
bool "Use manual su"
depends on KSU
default y
help
Use manual su and authorize the corresponding command line and application via prctl
config KPM
bool "Enable SukiSU KPM"
depends on KSU && 64BIT
default n
help
Enabling this option will activate the KPM feature of SukiSU.
This option is suitable for scenarios where you need to force KPM to be enabled.
but it may affect system stability.
select KALLSYMS
select KALLSYMS_ALL
choice
prompt "KernelSU hook type"
depends on KSU
default KSU_KPROBES_HOOK
bool "Enable SukiSU KPM"
depends on KSU && 64BIT
default n
help
Hook type for KernelSU
config KSU_KPROBES_HOOK
bool "Hook KernelSU with Kprobes"
depends on KPROBES
help
If enabled, Hook required KernelSU syscalls with Kernel-probe.
config KSU_TRACEPOINT_HOOK
bool "Hook KernelSU with Tracepoint"
depends on TRACEPOINTS
help
If enabled, Hook required KernelSU syscalls with Tracepoint.
Enabling this option will activate the KPM feature of SukiSU.
This option is suitable for scenarios where you need to force KPM to be enabled.
but it may affect system stability.
select KALLSYMS
select KALLSYMS_ALL
config KSU_MANUAL_HOOK
bool "Hook KernelSU manually"
depends on KSU != m
help
If enabled, Hook required KernelSU syscalls with manually-patched function.
endchoice
depends on KSU != m
help
If enabled, Hook required KernelSU syscalls with manually-patched function.
endmenu

View File

@@ -1,17 +1,29 @@
kernelsu-objs := ksu.o
kernelsu-objs += allowlist.o
kernelsu-objs += app_profile.o
kernelsu-objs += dynamic_manager.o
kernelsu-objs += apk_sign.o
kernelsu-objs += sucompat.o
kernelsu-objs += syscall_hook_manager.o
kernelsu-objs += throne_tracker.o
kernelsu-objs += core_hook.o
kernelsu-objs += pkg_observer.o
kernelsu-objs += throne_tracker.o
kernelsu-objs += umount_manager.o
kernelsu-objs += setuid_hook.o
kernelsu-objs += kernel_umount.o
kernelsu-objs += supercalls.o
kernelsu-objs += feature.o
kernelsu-objs += ksud.o
kernelsu-objs += embed_ksud.o
kernelsu-objs += kernel_compat.o
kernelsu-objs += seccomp_cache.o
kernelsu-objs += file_wrapper.o
kernelsu-objs += util.o
kernelsu-objs += throne_comm.o
kernelsu-objs += sulog.o
ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
kernelsu-objs += ksu_trace.o
ifeq ($(CONFIG_KSU_MANUAL_SU), y)
ccflags-y += -DCONFIG_KSU_MANUAL_SU
kernelsu-objs += manual_su.o
endif
kernelsu-objs += selinux/selinux.o
@@ -21,46 +33,62 @@ ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
obj-$(CONFIG_KSU) += kernelsu.o
obj-$(CONFIG_KSU_TRACEPOINT_HOOK) += ksu_trace_export.o
obj-$(CONFIG_KPM) += kpm/
REPO_OWNER := SukiSU-Ultra
REPO_NAME := SukiSU-Ultra
REPO_BRANCH := main
KSU_VERSION_API := 3.2.0
REPO_BRANCH := miuix
KSU_VERSION_API := 4.0.0
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
KDIR := $(KDIR)
MDIR := $(realpath $(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
ifneq ($(KDIR),)
$(info -- KDIR: $(KDIR))
$(info -- MDIR: $(MDIR))
endif
KSU_GITHUB_VERSION := $(shell $(CURL_BIN) -s "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
KSU_GITHUB_VERSION_COMMIT := $(shell $(CURL_BIN) -sI "https://api.github.com/repos/$(REPO_OWNER)/$(REPO_NAME)/commits?sha=$(REPO_BRANCH)&per_page=1" | grep -i "link:" | sed -n 's/.*page=\([0-9]*\)>; rel="last".*/\1/p')
LOCAL_GIT_EXISTS := $(shell test -e $(srctree)/$(src)/../.git && echo 1 || echo 0)
ifeq ($(findstring $(srctree),$(src)),$(srctree))
KSU_SRC := $(src)
else
KSU_SRC := $(srctree)/$(src)
endif
ifneq ($(shell test -e $(KSU_SRC)/../.git && echo "in-tree"),in-tree)
KSU_SRC := $(MDIR)
endif
LOCAL_GIT_EXISTS := $(shell test -e $(KSU_SRC)/../.git && echo 1 || echo 0)
define get_ksu_version_full
v$1-$(shell cd $(srctree)/$(src); $(GIT_BIN) rev-parse --short=8 HEAD)@$(shell cd $(srctree)/$(src); $(GIT_BIN) rev-parse --abbrev-ref HEAD)
v$1-$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --short=8 HEAD)@$(shell cd $(KSU_SRC); $(GIT_BIN) rev-parse --abbrev-ref HEAD)
endef
ifeq ($(KSU_GITHUB_VERSION_COMMIT),)
ifeq ($(LOCAL_GIT_EXISTS),1)
$(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
KSU_LOCAL_VERSION := $(shell cd $(srctree)/$(src); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
KSU_VERSION := $(shell expr 10000 + $(KSU_LOCAL_VERSION) + 700)
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
KSU_LOCAL_VERSION := $(shell cd $(KSU_SRC); $(GIT_BIN) rev-list --count $(REPO_BRANCH))
KSU_VERSION := $(shell expr 40000 + $(KSU_LOCAL_VERSION) - 2815)
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION))
else
KSU_VERSION := 13000
$(warning -- Could not fetch version online or via local .git! Using fallback version: $(KSU_VERSION))
endif
else
KSU_VERSION := $(shell expr 10000 + $(KSU_GITHUB_VERSION_COMMIT) + 700)
KSU_VERSION := $(shell expr 40000 + $(KSU_GITHUB_VERSION_COMMIT) - 2815)
$(info -- $(REPO_NAME) version (GitHub): $(KSU_VERSION))
endif
ifeq ($(KSU_GITHUB_VERSION),)
ifeq ($(LOCAL_GIT_EXISTS),1)
$(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_VERSION_API))
$(info -- $(REPO_NAME) version (local .git): $(KSU_VERSION_FULL))
$(info -- $(REPO_NAME) Formatted version (local .git): $(KSU_VERSION))
@@ -69,7 +97,7 @@ ifeq ($(KSU_GITHUB_VERSION),)
$(warning -- $(REPO_NAME) version: $(KSU_VERSION_FULL))
endif
else
$(shell cd $(srctree)/$(src); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
$(shell cd $(KSU_SRC); [ -f ../.git/shallow ] && $(GIT_BIN) fetch --unshallow)
KSU_VERSION_FULL := $(call get_ksu_version_full,$(KSU_GITHUB_VERSION))
$(info -- $(REPO_NAME) version (Github): $(KSU_VERSION_FULL))
endif
@@ -93,14 +121,13 @@ ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
$(info -- SukiSU Manager package name: $(KSU_MANAGER_PACKAGE))
endif
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
ifeq ($(CONFIG_KSU_KPROBES_HOOK), y)
$(info -- SukiSU: CONFIG_KSU_KPROBES_HOOK)
else ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
$(info -- SukiSU: CONFIG_KSU_TRACEPOINT_HOOK)
else ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
$(info -- SukiSU: CONFIG_KSU_MANUAL_HOOK)
ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
ccflags-y += -DKSU_MANUAL_HOOK
$(info -- SukiSU: KSU_MANUAL_HOOK Temporarily discontinued)
else
ccflags-y += -DKSU_KPROBES_HOOK
ccflags-y += -DKSU_TP_HOOK
$(info -- SukiSU: KSU_TRACEPOINT_HOOK)
endif
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
@@ -117,14 +144,30 @@ endif
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
$(info -- KERNEL_TYPE: $(KERNEL_TYPE))
$(info -- KERNEL_VERSION: $(KERNEL_VERSION))
ifeq ($(CONFIG_KPM), y)
$(info -- KPM is enabled)
else
$(info -- KPM is disabled)
endif
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
# Check new vfs_getattr()
ifeq ($(shell grep -A1 "^int vfs_getattr" $(srctree)/fs/stat.c | grep -q "query_flags" ; echo $$?),0)
ccflags-y += -DKSU_HAS_NEW_VFS_GETATTR
endif
# Function proc_ops check
ifeq ($(shell grep -q "struct proc_ops " $(srctree)/include/linux/proc_fs.h; echo $$?),0)
ccflags-y += -DKSU_COMPAT_HAS_PROC_OPS
endif
ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat -Wno-missing-prototypes
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-unused-variable
all:
make -C $(KDIR) M=$(MDIR) modules
compdb:
python3 $(MDIR)/.vscode/generate_compdb.py -O $(KDIR) $(MDIR)
clean:
make -C $(KDIR) M=$(MDIR) clean
# Keep a new line here!! Because someone may append config

View File

@@ -1,3 +1,5 @@
#include <linux/mutex.h>
#include <linux/task_work.h>
#include <linux/capability.h>
#include <linux/compiler.h>
#include <linux/fs.h>
@@ -8,14 +10,16 @@
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
#include <linux/compiler_types.h>
#endif
#include "ksu.h"
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
#include "selinux/selinux.h"
#include "kernel_compat.h"
#include "allowlist.h"
#include "manager.h"
#include "syscall_hook_manager.h"
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
#define FILE_FORMAT_VERSION 3 // u32
@@ -29,58 +33,61 @@ static DEFINE_MUTEX(allowlist_mutex);
static struct root_profile default_root_profile;
static struct non_root_profile default_non_root_profile;
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
void persistent_allow_list(void);
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly
__aligned(PAGE_SIZE);
static int allow_list_pointer __read_mostly = 0;
static void remove_uid_from_arr(uid_t uid)
{
int *temp_arr;
int i, j;
int *temp_arr;
int i, j;
if (allow_list_pointer == 0)
return;
if (allow_list_pointer == 0)
return;
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
if (temp_arr == NULL) {
pr_err("%s: unable to allocate memory\n", __func__);
return;
}
temp_arr = kzalloc(sizeof(allow_list_arr), GFP_KERNEL);
if (temp_arr == NULL) {
pr_err("%s: unable to allocate memory\n", __func__);
return;
}
for (i = j = 0; i < allow_list_pointer; i++) {
if (allow_list_arr[i] == uid)
continue;
temp_arr[j++] = allow_list_arr[i];
}
for (i = j = 0; i < allow_list_pointer; i++) {
if (allow_list_arr[i] == uid)
continue;
temp_arr[j++] = allow_list_arr[i];
}
allow_list_pointer = j;
allow_list_pointer = j;
for (; j < ARRAY_SIZE(allow_list_arr); j++)
temp_arr[j] = -1;
for (; j < ARRAY_SIZE(allow_list_arr); j++)
temp_arr[j] = -1;
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
kfree(temp_arr);
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
kfree(temp_arr);
}
static void init_default_profiles()
static void init_default_profiles(void)
{
kernel_cap_t full_cap = CAP_FULL_SET;
kernel_cap_t full_cap = CAP_FULL_SET;
default_root_profile.uid = 0;
default_root_profile.gid = 0;
default_root_profile.groups_count = 1;
default_root_profile.groups[0] = 0;
memcpy(&default_root_profile.capabilities.effective, &full_cap,
sizeof(default_root_profile.capabilities.effective));
default_root_profile.namespaces = 0;
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
default_root_profile.uid = 0;
default_root_profile.gid = 0;
default_root_profile.groups_count = 1;
default_root_profile.groups[0] = 0;
memcpy(&default_root_profile.capabilities.effective, &full_cap,
sizeof(default_root_profile.capabilities.effective));
default_root_profile.namespaces = 0;
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
// This means that we will umount modules by default!
default_non_root_profile.umount_modules = true;
// This means that we will umount modules by default!
default_non_root_profile.umount_modules = true;
}
struct perm_data {
struct list_head list;
struct app_profile profile;
struct list_head list;
struct app_profile profile;
};
static struct list_head allow_list;
@@ -90,437 +97,535 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
static struct work_struct ksu_save_work;
static struct work_struct ksu_load_work;
bool persistent_allow_list(void);
void ksu_show_allow_list(void)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
pr_info("ksu_show_allow_list\n");
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
p->profile.allow_su);
}
struct perm_data *p = NULL;
struct list_head *pos = NULL;
pr_info("ksu_show_allow_list\n");
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
p->profile.allow_su);
}
}
#ifdef CONFIG_KSU_DEBUG
static void ksu_grant_root_to_shell()
static void ksu_grant_root_to_shell(void)
{
struct app_profile profile = {
.version = KSU_APP_PROFILE_VER,
.allow_su = true,
.current_uid = 2000,
};
strcpy(profile.key, "com.android.shell");
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
ksu_set_app_profile(&profile, false);
struct app_profile profile = {
.version = KSU_APP_PROFILE_VER,
.allow_su = true,
.current_uid = 2000,
};
strcpy(profile.key, "com.android.shell");
strcpy(profile.rp_config.profile.selinux_domain,
KSU_DEFAULT_SELINUX_DOMAIN);
ksu_set_app_profile(&profile, false);
}
#endif
bool ksu_get_app_profile(struct app_profile *profile)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool found = false;
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool found = false;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
bool uid_match = profile->current_uid == p->profile.current_uid;
if (uid_match) {
// found it, override it with ours
memcpy(profile, &p->profile, sizeof(*profile));
found = true;
goto exit;
}
}
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
bool uid_match = profile->current_uid == p->profile.current_uid;
if (uid_match) {
// found it, override it with ours
memcpy(profile, &p->profile, sizeof(*profile));
found = true;
goto exit;
}
}
exit:
return found;
return found;
}
static inline bool forbid_system_uid(uid_t uid) {
#define SHELL_UID 2000
#define SYSTEM_UID 1000
return uid < SHELL_UID && uid != SYSTEM_UID;
static inline bool forbid_system_uid(uid_t uid)
{
#define SHELL_UID 2000
#define SYSTEM_UID 1000
return uid < SHELL_UID && uid != SYSTEM_UID;
}
static bool profile_valid(struct app_profile *profile)
{
if (!profile) {
return false;
}
if (!profile) {
return false;
}
if (profile->version < KSU_APP_PROFILE_VER) {
pr_info("Unsupported profile version: %d\n", profile->version);
return false;
}
if (profile->version < KSU_APP_PROFILE_VER) {
pr_info("Unsupported profile version: %d\n", profile->version);
return false;
}
if (profile->allow_su) {
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
return false;
}
if (profile->allow_su) {
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
return false;
}
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
return false;
}
}
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
return false;
}
}
return true;
return true;
}
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool result = false;
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool result = false;
if (!profile_valid(profile)) {
pr_err("Failed to set app profile: invalid profile!\n");
return false;
}
if (!profile_valid(profile)) {
pr_err("Failed to set app profile: invalid profile!\n");
return false;
}
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
// both uid and package must match, otherwise it will break multiple package with different user id
if (profile->current_uid == p->profile.current_uid &&
!strcmp(profile->key, p->profile.key)) {
// found it, just override it all!
memcpy(&p->profile, profile, sizeof(*profile));
result = true;
goto out;
}
}
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
// both uid and package must match, otherwise it will break multiple package with different user id
if (profile->current_uid == p->profile.current_uid &&
!strcmp(profile->key, p->profile.key)) {
// found it, just override it all!
memcpy(&p->profile, profile, sizeof(*profile));
result = true;
goto out;
}
}
// not found, alloc a new node!
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
if (!p) {
pr_err("ksu_set_app_profile alloc failed\n");
return false;
}
// not found, alloc a new node!
p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL);
if (!p) {
pr_err("ksu_set_app_profile alloc failed\n");
return false;
}
memcpy(&p->profile, profile, sizeof(*profile));
if (profile->allow_su) {
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
profile->key, profile->current_uid,
profile->rp_config.profile.gid,
profile->rp_config.profile.selinux_domain);
} else {
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
profile->key, profile->current_uid,
profile->nrp_config.profile.umount_modules);
}
list_add_tail(&p->list, &allow_list);
memcpy(&p->profile, profile, sizeof(*profile));
if (profile->allow_su) {
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
profile->key, profile->current_uid,
profile->rp_config.profile.gid,
profile->rp_config.profile.selinux_domain);
} else {
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
profile->key, profile->current_uid,
profile->nrp_config.profile.umount_modules);
}
list_add_tail(&p->list, &allow_list);
out:
if (profile->current_uid <= BITMAP_UID_MAX) {
if (profile->allow_su)
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
else
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
} else {
if (profile->allow_su) {
/*
* 1024 apps with uid higher than BITMAP_UID_MAX
* registered to request superuser?
*/
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
pr_err("too many apps registered\n");
WARN_ON(1);
return false;
}
allow_list_arr[allow_list_pointer++] = profile->current_uid;
} else {
remove_uid_from_arr(profile->current_uid);
}
}
result = true;
if (profile->current_uid <= BITMAP_UID_MAX) {
if (profile->allow_su)
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |=
1 << (profile->current_uid % BITS_PER_BYTE);
else
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &=
~(1 << (profile->current_uid % BITS_PER_BYTE));
} else {
if (profile->allow_su) {
/*
* 1024 apps with uid higher than BITMAP_UID_MAX
* registered to request superuser?
*/
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
pr_err("too many apps registered\n");
WARN_ON(1);
return false;
}
allow_list_arr[allow_list_pointer++] = profile->current_uid;
} else {
remove_uid_from_arr(profile->current_uid);
}
}
result = true;
// check if the default profiles is changed, cache it to a single struct to accelerate access.
if (unlikely(!strcmp(profile->key, "$"))) {
// set default non root profile
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
sizeof(default_non_root_profile));
}
// check if the default profiles is changed, cache it to a single struct to accelerate access.
if (unlikely(!strcmp(profile->key, "$"))) {
// set default non root profile
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
sizeof(default_non_root_profile));
}
if (unlikely(!strcmp(profile->key, "#"))) {
// set default root profile
memcpy(&default_root_profile, &profile->rp_config.profile,
sizeof(default_root_profile));
}
if (unlikely(!strcmp(profile->key, "#"))) {
// set default root profile
memcpy(&default_root_profile, &profile->rp_config.profile,
sizeof(default_root_profile));
}
if (persist)
persistent_allow_list();
if (persist) {
persistent_allow_list();
// FIXME: use a new flag
ksu_mark_running_process();
}
return result;
return result;
}
bool __ksu_is_allow_uid(uid_t uid)
{
int i;
int i;
if (unlikely(uid == 0)) {
// already root, but only allow our domain.
return is_ksu_domain();
}
if (forbid_system_uid(uid)) {
// do not bother going through the list if it's system
return false;
}
if (forbid_system_uid(uid)) {
// do not bother going through the list if it's system
return false;
}
if (likely(ksu_is_manager_uid_valid()) &&
unlikely(ksu_get_manager_uid() == uid)) {
// manager is always allowed!
return true;
}
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
// manager is always allowed!
return true;
}
if (likely(uid <= BITMAP_UID_MAX)) {
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] &
(1 << (uid % BITS_PER_BYTE)));
} else {
for (i = 0; i < allow_list_pointer; i++) {
if (allow_list_arr[i] == uid)
return true;
}
}
if (likely(uid <= BITMAP_UID_MAX)) {
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
} else {
for (i = 0; i < allow_list_pointer; i++) {
if (allow_list_arr[i] == uid)
return true;
}
}
return false;
}
return false;
bool __ksu_is_allow_uid_for_current(uid_t uid)
{
if (unlikely(uid == 0)) {
// already root, but only allow our domain.
return is_ksu_domain();
}
return __ksu_is_allow_uid(uid);
}
bool ksu_uid_should_umount(uid_t uid)
{
struct app_profile profile = { .current_uid = uid };
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
// we should not umount on manager!
return false;
}
bool found = ksu_get_app_profile(&profile);
if (!found) {
// no app profile found, it must be non root app
return default_non_root_profile.umount_modules;
}
if (profile.allow_su) {
// if found and it is granted to su, we shouldn't umount for it
return false;
} else {
// found an app profile
if (profile.nrp_config.use_default) {
return default_non_root_profile.umount_modules;
} else {
return profile.nrp_config.profile.umount_modules;
}
}
struct app_profile profile = { .current_uid = uid };
if (likely(ksu_is_manager_uid_valid()) &&
unlikely(ksu_get_manager_uid() == uid)) {
// we should not umount on manager!
return false;
}
bool found = ksu_get_app_profile(&profile);
if (!found) {
// no app profile found, it must be non root app
return default_non_root_profile.umount_modules;
}
if (profile.allow_su) {
// if found and it is granted to su, we shouldn't umount for it
return false;
} else {
// found an app profile
if (profile.nrp_config.use_default) {
return default_non_root_profile.umount_modules;
} else {
return profile.nrp_config.profile.umount_modules;
}
}
}
struct root_profile *ksu_get_root_profile(uid_t uid)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
struct perm_data *p = NULL;
struct list_head *pos = NULL;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
if (uid == p->profile.current_uid && p->profile.allow_su) {
if (!p->profile.rp_config.use_default) {
return &p->profile.rp_config.profile;
}
}
}
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
if (uid == p->profile.current_uid && p->profile.allow_su) {
if (!p->profile.rp_config.use_default) {
return &p->profile.rp_config.profile;
}
}
}
// use default profile
return &default_root_profile;
// use default profile
return &default_root_profile;
}
bool ksu_get_allow_list(int *array, int *length, bool allow)
{
struct perm_data *p = NULL;
struct list_head *pos = NULL;
int i = 0;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
if (p->profile.allow_su == allow) {
array[i++] = p->profile.current_uid;
}
}
*length = i;
struct perm_data *p = NULL;
struct list_head *pos = NULL;
int i = 0;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
if (p->profile.allow_su == allow) {
array[i++] = p->profile.current_uid;
}
}
*length = i;
return true;
return true;
}
void do_save_allow_list(struct work_struct *work)
static void do_persistent_allow_list(struct callback_head *_cb)
{
u32 magic = FILE_MAGIC;
u32 version = FILE_FORMAT_VERSION;
struct perm_data *p = NULL;
struct list_head *pos = NULL;
loff_t off = 0;
u32 magic = FILE_MAGIC;
u32 version = FILE_FORMAT_VERSION;
struct perm_data *p = NULL;
struct list_head *pos = NULL;
loff_t off = 0;
struct file *fp =
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
return;
}
mutex_lock(&allowlist_mutex);
struct file *fp =
filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
goto unlock;
}
// store magic and version
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic)) {
pr_err("save_allow_list write magic failed.\n");
goto exit;
}
// store magic and version
if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
pr_err("save_allow_list write magic failed.\n");
goto close_file;
}
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
sizeof(version)) {
pr_err("save_allow_list write version failed.\n");
goto exit;
}
if (kernel_write(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("save_allow_list write version failed.\n");
goto close_file;
}
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
p->profile.key, p->profile.current_uid,
p->profile.allow_su);
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
p->profile.key, p->profile.current_uid, p->profile.allow_su);
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
&off);
}
kernel_write(fp, &p->profile, sizeof(p->profile), &off);
}
exit:
filp_close(fp, 0);
close_file:
filp_close(fp, 0);
unlock:
mutex_unlock(&allowlist_mutex);
kfree(_cb);
}
void do_load_allow_list(struct work_struct *work)
void persistent_allow_list()
{
loff_t off = 0;
ssize_t ret = 0;
struct file *fp = NULL;
u32 magic;
u32 version;
struct task_struct *tsk;
tsk = get_pid_task(find_vpid(1), PIDTYPE_PID);
if (!tsk) {
pr_err("save_allow_list find init task err\n");
return;
}
struct callback_head *cb =
kzalloc(sizeof(struct callback_head), GFP_KERNEL);
if (!cb) {
pr_err("save_allow_list alloc cb err\b");
goto put_task;
}
cb->func = do_persistent_allow_list;
task_work_add(tsk, cb, TWA_RESUME);
put_task:
put_task_struct(tsk);
}
void ksu_load_allow_list()
{
loff_t off = 0;
ssize_t ret = 0;
struct file *fp = NULL;
u32 magic;
u32 version;
#ifdef CONFIG_KSU_DEBUG
// always allow adb shell by default
ksu_grant_root_to_shell();
// always allow adb shell by default
ksu_grant_root_to_shell();
#endif
// load allowlist now!
fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
if (IS_ERR(fp)) {
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
return;
}
// load allowlist now!
fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
if (IS_ERR(fp)) {
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
return;
}
// verify magic
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic) ||
magic != FILE_MAGIC) {
pr_err("allowlist file invalid: %d!\n", magic);
goto exit;
}
// verify magic
if (kernel_read(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
magic != FILE_MAGIC) {
pr_err("allowlist file invalid: %d!\n", magic);
goto exit;
}
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
sizeof(version)) {
pr_err("allowlist read version: %d failed\n", version);
goto exit;
}
if (kernel_read(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("allowlist read version: %d failed\n", version);
goto exit;
}
pr_info("allowlist version: %d\n", version);
pr_info("allowlist version: %d\n", version);
while (true) {
struct app_profile profile;
while (true) {
struct app_profile profile;
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
&off);
ret = kernel_read(fp, &profile, sizeof(profile), &off);
if (ret <= 0) {
pr_info("load_allow_list read err: %zd\n", ret);
break;
}
if (ret <= 0) {
pr_info("load_allow_list read err: %zd\n", ret);
break;
}
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
profile.key, profile.current_uid, profile.allow_su);
ksu_set_app_profile(&profile, false);
}
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n", profile.key,
profile.current_uid, profile.allow_su);
ksu_set_app_profile(&profile, false);
}
exit:
ksu_show_allow_list();
filp_close(fp, 0);
ksu_show_allow_list();
filp_close(fp, 0);
}
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data)
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
void *data)
{
struct perm_data *np = NULL;
struct perm_data *n = NULL;
struct perm_data *np = NULL;
struct perm_data *n = NULL;
bool modified = false;
// TODO: use RCU!
mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) {
uid_t uid = np->profile.current_uid;
char *package = np->profile.key;
// we use this uid for special cases, don't prune it!
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
modified = true;
pr_info("prune uid: %d, package: %s\n", uid, package);
list_del(&np->list);
if (likely(uid <= BITMAP_UID_MAX)) {
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
}
remove_uid_from_arr(uid);
smp_mb();
kfree(np);
}
}
mutex_unlock(&allowlist_mutex);
if (!ksu_boot_completed) {
pr_info("boot not completed, skip prune\n");
return;
}
if (modified) {
persistent_allow_list();
}
}
bool modified = false;
// TODO: use RCU!
mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) {
uid_t uid = np->profile.current_uid;
char *package = np->profile.key;
// we use this uid for special cases, don't prune it!
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
modified = true;
pr_info("prune uid: %d, package: %s\n", uid, package);
list_del(&np->list);
if (likely(uid <= BITMAP_UID_MAX)) {
allow_list_bitmap[uid / BITS_PER_BYTE] &=
~(1 << (uid % BITS_PER_BYTE));
}
remove_uid_from_arr(uid);
smp_mb();
kfree(np);
}
}
mutex_unlock(&allowlist_mutex);
// make sure allow list works cross boot
bool persistent_allow_list(void)
{
return ksu_queue_work(&ksu_save_work);
}
bool ksu_load_allow_list(void)
{
return ksu_queue_work(&ksu_load_work);
if (modified) {
persistent_allow_list();
}
}
void ksu_allowlist_init(void)
{
int i;
int i;
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
allow_list_arr[i] = -1;
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
allow_list_arr[i] = -1;
INIT_LIST_HEAD(&allow_list);
INIT_LIST_HEAD(&allow_list);
INIT_WORK(&ksu_save_work, do_save_allow_list);
INIT_WORK(&ksu_load_work, do_load_allow_list);
init_default_profiles();
init_default_profiles();
}
void ksu_allowlist_exit(void)
{
struct perm_data *np = NULL;
struct perm_data *n = NULL;
struct perm_data *np = NULL;
struct perm_data *n = NULL;
do_save_allow_list(NULL);
// free allowlist
mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) {
list_del(&np->list);
kfree(np);
}
mutex_unlock(&allowlist_mutex);
// free allowlist
mutex_lock(&allowlist_mutex);
list_for_each_entry_safe (np, n, &allow_list, list) {
list_del(&np->list);
kfree(np);
}
mutex_unlock(&allowlist_mutex);
}
#ifdef CONFIG_KSU_MANUAL_SU
bool ksu_temp_grant_root_once(uid_t uid)
{
struct app_profile profile = {
.version = KSU_APP_PROFILE_VER,
.allow_su = true,
.current_uid = uid,
};
const char *default_key = "com.temp.once";
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool found = false;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
if (p->profile.current_uid == uid) {
strcpy(profile.key, p->profile.key);
found = true;
break;
}
}
if (!found) {
strcpy(profile.key, default_key);
}
profile.rp_config.profile.uid = default_root_profile.uid;
profile.rp_config.profile.gid = default_root_profile.gid;
profile.rp_config.profile.groups_count = default_root_profile.groups_count;
memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups));
memcpy(&profile.rp_config.profile.capabilities, &default_root_profile.capabilities, sizeof(default_root_profile.capabilities));
profile.rp_config.profile.namespaces = default_root_profile.namespaces;
strcpy(profile.rp_config.profile.selinux_domain, default_root_profile.selinux_domain);
bool ok = ksu_set_app_profile(&profile, false);
if (ok)
pr_info("pending_root: UID=%d granted and persisted\n", uid);
return ok;
}
void ksu_temp_revoke_root_once(uid_t uid)
{
struct app_profile profile = {
.version = KSU_APP_PROFILE_VER,
.allow_su = false,
.current_uid = uid,
};
const char *default_key = "com.temp.once";
struct perm_data *p = NULL;
struct list_head *pos = NULL;
bool found = false;
list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list);
if (p->profile.current_uid == uid) {
strcpy(profile.key, p->profile.key);
found = true;
break;
}
}
if (!found) {
strcpy(profile.key, default_key);
}
profile.nrp_config.profile.umount_modules = default_non_root_profile.umount_modules;
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
ksu_set_app_profile(&profile, false);
persistent_allow_list();
pr_info("pending_root: UID=%d removed and persist updated\n", uid);
}
#endif

View File

@@ -2,19 +2,31 @@
#define __KSU_H_ALLOWLIST
#include <linux/types.h>
#include "ksu.h"
#include <linux/uidgid.h>
#include "app_profile.h"
#define PER_USER_RANGE 100000
#define FIRST_APPLICATION_UID 10000
#define LAST_APPLICATION_UID 19999
#define FIRST_ISOLATED_UID 99000
#define LAST_ISOLATED_UID 99999
void ksu_allowlist_init(void);
void ksu_allowlist_exit(void);
bool ksu_load_allow_list(void);
void ksu_load_allow_list(void);
void ksu_show_allow_list(void);
// Check if the uid is in allow list
bool __ksu_is_allow_uid(uid_t uid);
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
// Check if the uid is in allow list, or current is ksu domain root
bool __ksu_is_allow_uid_for_current(uid_t uid);
#define ksu_is_allow_uid_for_current(uid) unlikely(__ksu_is_allow_uid_for_current(uid))
bool ksu_get_allow_list(int *array, int *length, bool allow);
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *), void *data);
@@ -24,4 +36,22 @@ bool ksu_set_app_profile(struct app_profile *, bool persist);
bool ksu_uid_should_umount(uid_t uid);
struct root_profile *ksu_get_root_profile(uid_t uid);
static inline bool is_appuid(uid_t uid)
{
uid_t appid = uid % PER_USER_RANGE;
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
}
static inline bool is_isolated_process(uid_t uid)
{
uid_t appid = uid % PER_USER_RANGE;
return appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID;
}
#ifdef CONFIG_KSU_MANUAL_SU
bool ksu_temp_grant_root_once(uid_t uid);
void ksu_temp_revoke_root_once(uid_t uid);
#endif
#endif

View File

@@ -17,69 +17,65 @@
#include "apk_sign.h"
#include "dynamic_manager.h"
#include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h"
#include "manager_sign.h"
struct sdesc {
struct shash_desc shash;
char ctx[];
struct shash_desc shash;
char ctx[];
};
static struct apk_sign_key {
unsigned size;
const char *sha256;
} apk_sign_keys[] = {
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
static apk_sign_key_t apk_sign_keys[] = {
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
#ifdef EXPECTED_SIZE
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
#endif
};
static struct sdesc *init_sdesc(struct crypto_shash *alg)
{
struct sdesc *sdesc;
int size;
struct sdesc *sdesc;
int size;
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kmalloc(size, GFP_KERNEL);
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
return sdesc;
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kzalloc(size, GFP_KERNEL);
if (!sdesc)
return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg;
return sdesc;
}
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
unsigned int datalen, unsigned char *digest)
unsigned int datalen, unsigned char *digest)
{
struct sdesc *sdesc;
int ret;
struct sdesc *sdesc;
int ret;
sdesc = init_sdesc(alg);
if (IS_ERR(sdesc)) {
pr_info("can't alloc sdesc\n");
return PTR_ERR(sdesc);
}
sdesc = init_sdesc(alg);
if (IS_ERR(sdesc)) {
pr_info("can't alloc sdesc\n");
return PTR_ERR(sdesc);
}
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
kfree(sdesc);
return ret;
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
kfree(sdesc);
return ret;
}
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
unsigned char *digest)
unsigned char *digest)
{
struct crypto_shash *alg;
char *hash_alg_name = "sha256";
int ret;
struct crypto_shash *alg;
char *hash_alg_name = "sha256";
int ret;
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
if (IS_ERR(alg)) {
pr_info("can't alloc alg %s\n", hash_alg_name);
return PTR_ERR(alg);
}
ret = calc_hash(alg, data, datalen, digest);
crypto_free_shash(alg);
return ret;
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
if (IS_ERR(alg)) {
pr_info("can't alloc alg %s\n", hash_alg_name);
return PTR_ERR(alg);
}
ret = calc_hash(alg, data, datalen, digest);
crypto_free_shash(alg);
return ret;
}
@@ -87,304 +83,307 @@ static struct dynamic_sign_key dynamic_sign = DYNAMIC_SIGN_DEFAULT_CONFIG;
static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index)
{
struct dynamic_sign_key current_dynamic_key = dynamic_sign;
if (ksu_get_dynamic_manager_config(&current_dynamic_key.size, &current_dynamic_key.hash)) {
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
current_dynamic_key.size, current_dynamic_key.hash);
}
if (size4 != current_dynamic_key.size) {
return false;
}
struct dynamic_sign_key current_dynamic_key = dynamic_sign;
if (ksu_get_dynamic_manager_config(&current_dynamic_key.size, &current_dynamic_key.hash)) {
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
current_dynamic_key.size, current_dynamic_key.hash);
}
if (size4 != current_dynamic_key.size) {
return false;
}
#define CERT_MAX_LENGTH 1024
char cert[CERT_MAX_LENGTH];
if (size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n");
return false;
}
ksu_kernel_read_compat(fp, cert, size4, pos);
unsigned char digest[SHA256_DIGEST_SIZE];
if (ksu_sha256(cert, size4, digest) < 0) {
pr_info("sha256 error\n");
return false;
}
char cert[CERT_MAX_LENGTH];
if (size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n");
return false;
}
kernel_read(fp, cert, size4, pos);
unsigned char digest[SHA256_DIGEST_SIZE];
if (ksu_sha256(cert, size4, digest) < 0) {
pr_info("sha256 error\n");
return false;
}
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash);
if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
if (matched_index) {
*matched_index = DYNAMIC_SIGN_INDEX;
}
return true;
}
return false;
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash);
if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
if (matched_index) {
*matched_index = DYNAMIC_SIGN_INDEX;
}
return true;
}
return false;
}
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
{
int i;
struct apk_sign_key sign_key;
bool signature_valid = false;
int i;
apk_sign_key_t sign_key;
bool signature_valid = false;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
kernel_read(fp, size4, 0x4, pos); // signer-sequence length
kernel_read(fp, size4, 0x4, pos); // signer length
kernel_read(fp, size4, 0x4, pos); // signed data length
*offset += 0x4 * 3;
*offset += 0x4 * 3;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
kernel_read(fp, size4, 0x4, pos); // digests-sequence length
*pos += *size4;
*offset += 0x4 + *size4;
*pos += *size4;
*offset += 0x4 + *size4;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
*offset += 0x4 * 2;
kernel_read(fp, size4, 0x4, pos); // certificates length
kernel_read(fp, size4, 0x4, pos); // certificate length
*offset += 0x4 * 2;
if (ksu_is_dynamic_manager_enabled()) {
loff_t temp_pos = *pos;
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
*pos = temp_pos;
*offset += *size4;
return true;
}
}
if (ksu_is_dynamic_manager_enabled()) {
loff_t temp_pos = *pos;
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
*pos = temp_pos;
*offset += *size4;
return true;
}
}
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
sign_key = apk_sign_keys[i];
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
sign_key = apk_sign_keys[i];
if (*size4 != sign_key.size)
continue;
*offset += *size4;
if (*size4 != sign_key.size)
continue;
*offset += *size4;
#define CERT_MAX_LENGTH 1024
char cert[CERT_MAX_LENGTH];
if (*size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n");
return false;
}
ksu_kernel_read_compat(fp, cert, *size4, pos);
unsigned char digest[SHA256_DIGEST_SIZE];
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
pr_info("sha256 error\n");
return false;
}
char cert[CERT_MAX_LENGTH];
if (*size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n");
return false;
}
kernel_read(fp, cert, *size4, pos);
unsigned char digest[SHA256_DIGEST_SIZE];
if (ksu_sha256(cert, *size4, digest) < 0 ) {
pr_info("sha256 error\n");
return false;
}
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
if (strcmp(sign_key.sha256, hash_str) == 0) {
signature_valid = true;
if (matched_index) {
*matched_index = i;
}
break;
}
}
return signature_valid;
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
if (strcmp(sign_key.sha256, hash_str) == 0) {
signature_valid = true;
if (matched_index) {
*matched_index = i;
}
break;
}
}
return signature_valid;
}
struct zip_entry_header {
uint32_t signature;
uint16_t version;
uint16_t flags;
uint16_t compression;
uint16_t mod_time;
uint16_t mod_date;
uint32_t crc32;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint16_t file_name_length;
uint16_t extra_field_length;
uint32_t signature;
uint16_t version;
uint16_t flags;
uint16_t compression;
uint16_t mod_time;
uint16_t mod_date;
uint32_t crc32;
uint32_t compressed_size;
uint32_t uncompressed_size;
uint16_t file_name_length;
uint16_t extra_field_length;
} __attribute__((packed));
// This is a necessary but not sufficient condition, but it is enough for us
static bool has_v1_signature_file(struct file *fp)
{
struct zip_entry_header header;
const char MANIFEST[] = "META-INF/MANIFEST.MF";
struct zip_entry_header header;
const char MANIFEST[] = "META-INF/MANIFEST.MF";
loff_t pos = 0;
loff_t pos = 0;
while (ksu_kernel_read_compat(fp, &header,
sizeof(struct zip_entry_header), &pos) ==
sizeof(struct zip_entry_header)) {
if (header.signature != 0x04034b50) {
// ZIP magic: 'PK'
return false;
}
// Read the entry file name
if (header.file_name_length == sizeof(MANIFEST) - 1) {
char fileName[sizeof(MANIFEST)];
ksu_kernel_read_compat(fp, fileName,
header.file_name_length, &pos);
fileName[header.file_name_length] = '\0';
while (kernel_read(fp, &header,
sizeof(struct zip_entry_header), &pos) ==
sizeof(struct zip_entry_header)) {
if (header.signature != 0x04034b50) {
// ZIP magic: 'PK'
return false;
}
// Read the entry file name
if (header.file_name_length == sizeof(MANIFEST) - 1) {
char fileName[sizeof(MANIFEST)];
kernel_read(fp, fileName,
header.file_name_length, &pos);
fileName[header.file_name_length] = '\0';
// Check if the entry matches META-INF/MANIFEST.MF
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) == 0) {
return true;
}
} else {
// Skip the entry file name
pos += header.file_name_length;
}
// Check if the entry matches META-INF/MANIFEST.MF
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
0) {
return true;
}
} else {
// Skip the entry file name
pos += header.file_name_length;
}
// Skip to the next entry
pos += header.extra_field_length + header.compressed_size;
}
// Skip to the next entry
pos += header.extra_field_length + header.compressed_size;
}
return false;
return false;
}
static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
{
unsigned char buffer[0x11] = { 0 };
u32 size4;
u64 size8, size_of_block;
loff_t pos;
bool v2_signing_valid = false;
int v2_signing_blocks = 0;
bool v3_signing_exist = false;
bool v3_1_signing_exist = false;
int matched_index = -1;
int i;
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
if (IS_ERR(fp)) {
pr_err("open %s error.\n", path);
return false;
}
unsigned char buffer[0x11] = { 0 };
u32 size4;
u64 size8, size_of_block;
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
filp_close(fp, 0);
return 0;
}
loff_t pos;
// disable inotify for this file
fp->f_mode |= FMODE_NONOTIFY;
bool v2_signing_valid = false;
int v2_signing_blocks = 0;
bool v3_signing_exist = false;
bool v3_1_signing_exist = false;
int matched_index = -1;
int i;
struct file *fp = filp_open(path, O_RDONLY, 0);
if (IS_ERR(fp)) {
pr_err("open %s error.\n", path);
return false;
}
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
for (i = 0;; ++i) {
unsigned short n;
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
ksu_kernel_read_compat(fp, &n, 2, &pos);
if (n == i) {
pos -= 22;
ksu_kernel_read_compat(fp, &size4, 4, &pos);
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
break;
}
}
if (i == 0xffff) {
pr_info("error: cannot find eocd\n");
goto clean;
}
}
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
filp_close(fp, 0);
return 0;
}
pos += 12;
// offset
ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
pos = size4 - 0x18;
// disable inotify for this file
fp->f_mode |= FMODE_NONOTIFY;
ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
ksu_kernel_read_compat(fp, buffer, 0x10, &pos);
if (strcmp((char *)buffer, "APK Sig Block 42")) {
goto clean;
}
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
for (i = 0;; ++i) {
unsigned short n;
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
kernel_read(fp, &n, 2, &pos);
if (n == i) {
pos -= 22;
kernel_read(fp, &size4, 4, &pos);
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
break;
}
}
if (i == 0xffff) {
pr_info("error: cannot find eocd\n");
goto clean;
}
}
pos = size4 - (size8 + 0x8);
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
if (size_of_block != size8) {
goto clean;
}
pos += 12;
// offset
kernel_read(fp, &size4, 0x4, &pos);
pos = size4 - 0x18;
int loop_count = 0;
while (loop_count++ < 10) {
uint32_t id;
uint32_t offset;
ksu_kernel_read_compat(fp, &size8, 0x8,
&pos); // sequence length
if (size8 == size_of_block) {
break;
}
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
offset = 4;
if (id == 0x7109871au) {
v2_signing_blocks++;
bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
if (result) {
v2_signing_valid = true;
}
} else if (id == 0xf05368c0u) {
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
v3_signing_exist = true;
} else if (id == 0x1b93ad61u) {
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
v3_1_signing_exist = true;
} else {
kernel_read(fp, &size8, 0x8, &pos);
kernel_read(fp, buffer, 0x10, &pos);
if (strcmp((char *)buffer, "APK Sig Block 42")) {
goto clean;
}
pos = size4 - (size8 + 0x8);
kernel_read(fp, &size_of_block, 0x8, &pos);
if (size_of_block != size8) {
goto clean;
}
int loop_count = 0;
while (loop_count++ < 10) {
uint32_t id;
uint32_t offset;
kernel_read(fp, &size8, 0x8,
&pos); // sequence length
if (size8 == size_of_block) {
break;
}
kernel_read(fp, &id, 0x4, &pos); // id
offset = 4;
if (id == 0x7109871au) {
v2_signing_blocks++;
bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
if (result) {
v2_signing_valid = true;
}
} else if (id == 0xf05368c0u) {
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
v3_signing_exist = true;
} else if (id == 0x1b93ad61u) {
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
v3_1_signing_exist = true;
} else {
#ifdef CONFIG_KSU_DEBUG
pr_info("Unknown id: 0x%08x\n", id);
pr_info("Unknown id: 0x%08x\n", id);
#endif
}
pos += (size8 - offset);
}
}
pos += (size8 - offset);
}
if (v2_signing_blocks != 1) {
if (v2_signing_blocks != 1) {
#ifdef CONFIG_KSU_DEBUG
pr_err("Unexpected v2 signature count: %d\n",
v2_signing_blocks);
pr_err("Unexpected v2 signature count: %d\n",
v2_signing_blocks);
#endif
v2_signing_valid = false;
}
v2_signing_valid = false;
}
if (v2_signing_valid) {
int has_v1_signing = has_v1_signature_file(fp);
if (has_v1_signing) {
pr_err("Unexpected v1 signature scheme found!\n");
filp_close(fp, 0);
return false;
}
}
if (v2_signing_valid) {
int has_v1_signing = has_v1_signature_file(fp);
if (has_v1_signing) {
pr_err("Unexpected v1 signature scheme found!\n");
filp_close(fp, 0);
return false;
}
}
clean:
filp_close(fp, 0);
filp_close(fp, 0);
if (v3_signing_exist || v3_1_signing_exist) {
if (v3_signing_exist || v3_1_signing_exist) {
#ifdef CONFIG_KSU_DEBUG
pr_err("Unexpected v3 signature scheme found!\n");
pr_err("Unexpected v3 signature scheme found!\n");
#endif
return false;
}
return false;
}
if (v2_signing_valid) {
if (signature_index) {
*signature_index = matched_index;
}
if (check_multi_manager) {
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
return true;
}
return false;
} else {
// Common manager check: any valid signature will do
return true;
}
}
return false;
if (v2_signing_valid) {
if (signature_index) {
*signature_index = matched_index;
}
if (check_multi_manager) {
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
return true;
}
return false;
} else {
// Common manager check: any valid signature will do
return true;
}
}
return false;
}
#ifdef CONFIG_KSU_DEBUG
@@ -395,19 +394,19 @@ int ksu_debug_manager_uid = -1;
static int set_expected_size(const char *val, const struct kernel_param *kp)
{
int rv = param_set_uint(val, kp);
ksu_set_manager_uid(ksu_debug_manager_uid);
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
return rv;
int rv = param_set_uint(val, kp);
ksu_set_manager_uid(ksu_debug_manager_uid);
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
return rv;
}
static struct kernel_param_ops expected_size_ops = {
.set = set_expected_size,
.get = param_get_uint,
.set = set_expected_size,
.get = param_get_uint,
};
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
#endif

303
kernel/app_profile.c Normal file
View File

@@ -0,0 +1,303 @@
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/seccomp.h>
#include <linux/thread_info.h>
#include <linux/uidgid.h>
#include <linux/version.h>
#include "objsec.h"
#include "allowlist.h"
#include "app_profile.h"
#include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h"
#include "syscall_hook_manager.h"
#include "sucompat.h"
#include "sulog.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0)
static struct group_info root_groups = { .usage = REFCOUNT_INIT(2), };
#else
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
#endif
static void setup_groups(struct root_profile *profile, struct cred *cred)
{
if (profile->groups_count > KSU_MAX_GROUPS) {
pr_warn("Failed to setgroups, too large group: %d!\n",
profile->uid);
return;
}
if (profile->groups_count == 1 && profile->groups[0] == 0) {
// setgroup to root and return early.
if (cred->group_info)
put_group_info(cred->group_info);
cred->group_info = get_group_info(&root_groups);
return;
}
u32 ngroups = profile->groups_count;
struct group_info *group_info = groups_alloc(ngroups);
if (!group_info) {
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
return;
}
int i;
for (i = 0; i < ngroups; i++) {
gid_t gid = profile->groups[i];
kgid_t kgid = make_kgid(current_user_ns(), gid);
if (!gid_valid(kgid)) {
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
put_group_info(group_info);
return;
}
group_info->gid[i] = kgid;
}
groups_sort(group_info);
set_groups(cred, group_info);
put_group_info(group_info);
}
void disable_seccomp(void)
{
assert_spin_locked(&current->sighand->siglock);
// disable seccomp
#if defined(CONFIG_GENERIC_ENTRY) && \
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
clear_syscall_work(SECCOMP);
#else
clear_thread_flag(TIF_SECCOMP);
#endif
#ifdef CONFIG_SECCOMP
current->seccomp.mode = 0;
current->seccomp.filter = NULL;
atomic_set(&current->seccomp.filter_count, 0);
#else
#endif
}
void escape_with_root_profile(void)
{
struct cred *cred;
struct task_struct *p = current;
struct task_struct *t;
cred = prepare_creds();
if (!cred) {
pr_warn("prepare_creds failed!\n");
return;
}
if (cred->euid.val == 0) {
pr_warn("Already root, don't escape!\n");
#if __SULOG_GATE
ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root_failed");
#endif
abort_creds(cred);
return;
}
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
cred->uid.val = profile->uid;
cred->suid.val = profile->uid;
cred->euid.val = profile->uid;
cred->fsuid.val = profile->uid;
cred->gid.val = profile->gid;
cred->fsgid.val = profile->gid;
cred->sgid.val = profile->gid;
cred->egid.val = profile->gid;
cred->securebits = 0;
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
sizeof(kernel_cap_t));
// setup capabilities
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
u64 cap_for_ksud =
profile->capabilities.effective | CAP_DAC_READ_SEARCH;
memcpy(&cred->cap_effective, &cap_for_ksud,
sizeof(cred->cap_effective));
memcpy(&cred->cap_permitted, &profile->capabilities.effective,
sizeof(cred->cap_permitted));
memcpy(&cred->cap_bset, &profile->capabilities.effective,
sizeof(cred->cap_bset));
setup_groups(profile, cred);
commit_creds(cred);
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
spin_lock_irq(&current->sighand->siglock);
disable_seccomp();
spin_unlock_irq(&current->sighand->siglock);
setup_selinux(profile->selinux_domain);
#if __SULOG_GATE
ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root");
#endif
for_each_thread (p, t) {
ksu_set_task_tracepoint_flag(t);
}
}
#ifdef CONFIG_KSU_MANUAL_SU
#include "ksud.h"
#ifndef DEVPTS_SUPER_MAGIC
#define DEVPTS_SUPER_MAGIC 0x1cd1
#endif
static int __manual_su_handle_devpts(struct inode *inode)
{
if (!current->mm) {
return 0;
}
uid_t uid = current_uid().val;
if (uid % 100000 < 10000) {
// not untrusted_app, ignore it
return 0;
}
if (likely(!ksu_is_allow_uid_for_current(uid)))
return 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE)
struct inode_security_struct *sec = selinux_inode(inode);
#else
struct inode_security_struct *sec =
(struct inode_security_struct *)inode->i_security;
#endif
if (ksu_file_sid && sec)
sec->sid = ksu_file_sid;
return 0;
}
static void disable_seccomp_for_task(struct task_struct *tsk)
{
assert_spin_locked(&tsk->sighand->siglock);
#ifdef CONFIG_SECCOMP
if (tsk->seccomp.mode == SECCOMP_MODE_DISABLED && !tsk->seccomp.filter)
return;
#endif
clear_tsk_thread_flag(tsk, TIF_SECCOMP);
#ifdef CONFIG_SECCOMP
tsk->seccomp.mode = SECCOMP_MODE_DISABLED;
if (tsk->seccomp.filter) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
seccomp_filter_release(tsk);
#else
put_seccomp_filter(tsk);
tsk->seccomp.filter = NULL;
#endif
}
#endif
}
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
{
struct cred *newcreds;
struct task_struct *target_task;
unsigned long flags;
struct task_struct *p = current;
struct task_struct *t;
pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid);
// Find target task by PID
rcu_read_lock();
target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID);
if (!target_task) {
rcu_read_unlock();
pr_err("cmd_su: target task not found for PID: %d\n", target_pid);
#if __SULOG_GATE
ksu_sulog_report_su_grant(target_uid, "cmd_su", "target_not_found");
#endif
return;
}
get_task_struct(target_task);
rcu_read_unlock();
if (task_uid(target_task).val == 0) {
pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid);
put_task_struct(target_task);
return;
}
newcreds = prepare_kernel_cred(target_task);
if (newcreds == NULL) {
pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid);
#if __SULOG_GATE
ksu_sulog_report_su_grant(target_uid, "cmd_su", "cred_alloc_failed");
#endif
put_task_struct(target_task);
return;
}
struct root_profile *profile = ksu_get_root_profile(target_uid);
newcreds->uid.val = profile->uid;
newcreds->suid.val = profile->uid;
newcreds->euid.val = profile->uid;
newcreds->fsuid.val = profile->uid;
newcreds->gid.val = profile->gid;
newcreds->fsgid.val = profile->gid;
newcreds->sgid.val = profile->gid;
newcreds->egid.val = profile->gid;
newcreds->securebits = 0;
u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID;
memcpy(&newcreds->cap_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective));
memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted));
memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset));
setup_groups(profile, newcreds);
task_lock(target_task);
const struct cred *old_creds = get_task_cred(target_task);
rcu_assign_pointer(target_task->real_cred, newcreds);
rcu_assign_pointer(target_task->cred, get_cred(newcreds));
task_unlock(target_task);
if (target_task->sighand) {
spin_lock_irqsave(&target_task->sighand->siglock, flags);
disable_seccomp_for_task(target_task);
spin_unlock_irqrestore(&target_task->sighand->siglock, flags);
}
setup_selinux(profile->selinux_domain);
put_cred(old_creds);
wake_up_process(target_task);
if (target_task->signal->tty) {
struct inode *inode = target_task->signal->tty->driver_data;
if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) {
__manual_su_handle_devpts(inode);
}
}
put_task_struct(target_task);
#if __SULOG_GATE
ksu_sulog_report_su_grant(target_uid, "cmd_su", "manual_escalation");
#endif
for_each_thread (p, t) {
ksu_set_task_tracepoint_flag(t);
}
pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid);
}
#endif

70
kernel/app_profile.h Normal file
View File

@@ -0,0 +1,70 @@
#ifndef __KSU_H_APP_PROFILE
#define __KSU_H_APP_PROFILE
#include <linux/types.h>
// Forward declarations
struct cred;
#define KSU_APP_PROFILE_VER 2
#define KSU_MAX_PACKAGE_NAME 256
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
#define KSU_MAX_GROUPS 32
#define KSU_SELINUX_DOMAIN 64
struct root_profile {
int32_t uid;
int32_t gid;
int32_t groups_count;
int32_t groups[KSU_MAX_GROUPS];
// kernel_cap_t is u32[2] for capabilities v3
struct {
u64 effective;
u64 permitted;
u64 inheritable;
} capabilities;
char selinux_domain[KSU_SELINUX_DOMAIN];
int32_t namespaces;
};
struct non_root_profile {
bool umount_modules;
};
struct app_profile {
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
u32 version;
// this is usually the package of the app, but can be other value for special apps
char key[KSU_MAX_PACKAGE_NAME];
int32_t current_uid;
bool allow_su;
union {
struct {
bool use_default;
char template_name[KSU_MAX_PACKAGE_NAME];
struct root_profile profile;
} rp_config;
struct {
bool use_default;
struct non_root_profile profile;
} nrp_config;
};
};
// Escalate current process to root with the appropriate profile
void escape_with_root_profile(void);
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid);
void disable_seccomp(void);
#endif

View File

@@ -18,10 +18,8 @@
#define __PT_SP_REG sp
#define __PT_IP_REG pc
#define PRCTL_SYMBOL "__arm64_sys_prctl"
#define REBOOT_SYMBOL "__arm64_sys_reboot"
#define SYS_READ_SYMBOL "__arm64_sys_read"
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
#elif defined(__x86_64__)
@@ -39,10 +37,8 @@
#define __PT_RC_REG ax
#define __PT_SP_REG sp
#define __PT_IP_REG ip
#define PRCTL_SYMBOL "__x64_sys_prctl"
#define REBOOT_SYMBOL "__x64_sys_reboot"
#define SYS_READ_SYMBOL "__x64_sys_read"
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
#else

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +0,0 @@
#ifndef __KSU_H_KSU_CORE
#define __KSU_H_KSU_CORE
#include <linux/init.h>
#include "apk_sign.h"
void __init ksu_core_init(void);
void ksu_core_exit(void);
#endif

View File

@@ -17,7 +17,6 @@
#include "dynamic_manager.h"
#include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h"
#include "manager.h"
#define MAX_MANAGERS 2
@@ -233,23 +232,23 @@ static void do_save_dynamic_manager(struct work_struct *work)
return;
}
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
return;
}
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
if (kernel_write(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
pr_err("save_dynamic_manager write magic failed.\n");
goto exit;
}
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
if (kernel_write(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("save_dynamic_manager write version failed.\n");
goto exit;
}
if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
if (kernel_write(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
pr_err("save_dynamic_manager write config failed.\n");
goto exit;
}
@@ -271,7 +270,7 @@ static void do_load_dynamic_manager(struct work_struct *work)
unsigned long flags;
int i;
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
if (IS_ERR(fp)) {
if (PTR_ERR(fp) == -ENOENT) {
pr_info("No saved dynamic manager config found\n");
@@ -281,20 +280,20 @@ static void do_load_dynamic_manager(struct work_struct *work)
return;
}
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
if (kernel_read(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
magic != DYNAMIC_MANAGER_FILE_MAGIC) {
pr_err("dynamic manager file invalid magic: %x!\n", magic);
goto exit;
}
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
if (kernel_read(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("dynamic manager read version failed\n");
goto exit;
}
pr_info("dynamic manager file version: %d\n", version);
ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off);
ret = kernel_read(fp, &loaded_config, sizeof(loaded_config), &off);
if (ret <= 0) {
pr_info("load_dynamic_manager read err: %zd\n", ret);
goto exit;
@@ -348,14 +347,14 @@ static void do_clear_dynamic_manager(struct work_struct *work)
memset(zero_buffer, 0, sizeof(zero_buffer));
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
fp = filp_open(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
return;
}
// Write null bytes to overwrite the file content
if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
if (kernel_write(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
pr_err("clear_dynamic_manager write null bytes failed.\n");
} else {
pr_info("Dynamic sign config file cleared successfully\n");

173
kernel/feature.c Normal file
View File

@@ -0,0 +1,173 @@
#include "feature.h"
#include "klog.h" // IWYU pragma: keep
#include <linux/mutex.h>
static const struct ksu_feature_handler *feature_handlers[KSU_FEATURE_MAX];
static DEFINE_MUTEX(feature_mutex);
int ksu_register_feature_handler(const struct ksu_feature_handler *handler)
{
if (!handler) {
pr_err("feature: register handler is NULL\n");
return -EINVAL;
}
if (handler->feature_id >= KSU_FEATURE_MAX) {
pr_err("feature: invalid feature_id %u\n", handler->feature_id);
return -EINVAL;
}
if (!handler->get_handler && !handler->set_handler) {
pr_err("feature: no handler provided for feature %u\n", handler->feature_id);
return -EINVAL;
}
mutex_lock(&feature_mutex);
if (feature_handlers[handler->feature_id]) {
pr_warn("feature: handler for %u already registered, overwriting\n",
handler->feature_id);
}
feature_handlers[handler->feature_id] = handler;
pr_info("feature: registered handler for %s (id=%u)\n",
handler->name ? handler->name : "unknown", handler->feature_id);
mutex_unlock(&feature_mutex);
return 0;
}
int ksu_unregister_feature_handler(u32 feature_id)
{
int ret = 0;
if (feature_id >= KSU_FEATURE_MAX) {
pr_err("feature: invalid feature_id %u\n", feature_id);
return -EINVAL;
}
mutex_lock(&feature_mutex);
if (!feature_handlers[feature_id]) {
pr_warn("feature: no handler registered for %u\n", feature_id);
ret = -ENOENT;
goto out;
}
feature_handlers[feature_id] = NULL;
pr_info("feature: unregistered handler for id=%u\n", feature_id);
out:
mutex_unlock(&feature_mutex);
return ret;
}
int ksu_get_feature(u32 feature_id, u64 *value, bool *supported)
{
int ret = 0;
const struct ksu_feature_handler *handler;
if (feature_id >= KSU_FEATURE_MAX) {
pr_err("feature: invalid feature_id %u\n", feature_id);
return -EINVAL;
}
if (!value || !supported) {
pr_err("feature: invalid parameters\n");
return -EINVAL;
}
mutex_lock(&feature_mutex);
handler = feature_handlers[feature_id];
if (!handler) {
*supported = false;
*value = 0;
pr_debug("feature: feature %u not supported\n", feature_id);
goto out;
}
*supported = true;
if (!handler->get_handler) {
pr_warn("feature: no get_handler for feature %u\n", feature_id);
ret = -EOPNOTSUPP;
goto out;
}
ret = handler->get_handler(value);
if (ret) {
pr_err("feature: get_handler for %u failed: %d\n", feature_id, ret);
}
out:
mutex_unlock(&feature_mutex);
return ret;
}
int ksu_set_feature(u32 feature_id, u64 value)
{
int ret = 0;
const struct ksu_feature_handler *handler;
if (feature_id >= KSU_FEATURE_MAX) {
pr_err("feature: invalid feature_id %u\n", feature_id);
return -EINVAL;
}
mutex_lock(&feature_mutex);
handler = feature_handlers[feature_id];
if (!handler) {
pr_err("feature: feature %u not registered\n", feature_id);
ret = -EOPNOTSUPP;
goto out;
}
if (!handler->set_handler) {
pr_warn("feature: no set_handler for feature %u\n", feature_id);
ret = -EOPNOTSUPP;
goto out;
}
ret = handler->set_handler(value);
if (ret) {
pr_err("feature: set_handler for %u failed: %d\n", feature_id, ret);
}
out:
mutex_unlock(&feature_mutex);
return ret;
}
void ksu_feature_init(void)
{
int i;
for (i = 0; i < KSU_FEATURE_MAX; i++) {
feature_handlers[i] = NULL;
}
pr_info("feature: feature management initialized\n");
}
void ksu_feature_exit(void)
{
int i;
mutex_lock(&feature_mutex);
for (i = 0; i < KSU_FEATURE_MAX; i++) {
feature_handlers[i] = NULL;
}
mutex_unlock(&feature_mutex);
pr_info("feature: feature management cleaned up\n");
}

37
kernel/feature.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef __KSU_H_FEATURE
#define __KSU_H_FEATURE
#include <linux/types.h>
enum ksu_feature_id {
KSU_FEATURE_SU_COMPAT = 0,
KSU_FEATURE_KERNEL_UMOUNT = 1,
KSU_FEATURE_ENHANCED_SECURITY = 2,
KSU_FEATURE_SULOG = 3,
KSU_FEATURE_MAX
};
typedef int (*ksu_feature_get_t)(u64 *value);
typedef int (*ksu_feature_set_t)(u64 value);
struct ksu_feature_handler {
u32 feature_id;
const char *name;
ksu_feature_get_t get_handler;
ksu_feature_set_t set_handler;
};
int ksu_register_feature_handler(const struct ksu_feature_handler *handler);
int ksu_unregister_feature_handler(u32 feature_id);
int ksu_get_feature(u32 feature_id, u64 *value, bool *supported);
int ksu_set_feature(u32 feature_id, u64 value);
void ksu_feature_init(void);
void ksu_feature_exit(void);
#endif // __KSU_H_FEATURE

341
kernel/file_wrapper.c Normal file
View File

@@ -0,0 +1,341 @@
#include <linux/export.h>
#include <linux/anon_inodes.h>
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include "klog.h" // IWYU pragma: keep
#include "selinux/selinux.h"
#include "file_wrapper.h"
static loff_t ksu_wrapper_llseek(struct file *fp, loff_t off, int flags) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->llseek(data->orig, off, flags);
}
static ssize_t ksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz, loff_t *off) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->read(orig, ptr, sz, off);
}
static ssize_t ksu_wrapper_write(struct file *fp, const char __user *ptr, size_t sz, loff_t *off) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->write(orig, ptr, sz, off);
}
static ssize_t ksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi) {
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
struct file* orig = data->orig;
iocb->ki_filp = orig;
return orig->f_op->read_iter(iocb, iovi);
}
static ssize_t ksu_wrapper_write_iter(struct kiocb *iocb, struct iov_iter *iovi) {
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
struct file* orig = data->orig;
iocb->ki_filp = orig;
return orig->f_op->write_iter(iocb, iovi);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
static int ksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch* icb, unsigned int v) {
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
struct file* orig = data->orig;
kiocb->ki_filp = orig;
return orig->f_op->iopoll(kiocb, icb, v);
}
#else
static int ksu_wrapper_iopoll(struct kiocb *kiocb, bool spin) {
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
struct file* orig = data->orig;
kiocb->ki_filp = orig;
return orig->f_op->iopoll(kiocb, spin);
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
static int ksu_wrapper_iterate (struct file *fp, struct dir_context *dc) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->iterate(orig, dc);
}
#endif
static int ksu_wrapper_iterate_shared(struct file *fp, struct dir_context *dc) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->iterate_shared(orig, dc);
}
static __poll_t ksu_wrapper_poll(struct file *fp, struct poll_table_struct *pts) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->poll(orig, pts);
}
static long ksu_wrapper_unlocked_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->unlocked_ioctl(orig, cmd, arg);
}
static long ksu_wrapper_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->compat_ioctl(orig, cmd, arg);
}
static int ksu_wrapper_mmap(struct file *fp, struct vm_area_struct * vma) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->mmap(orig, vma);
}
// static unsigned long mmap_supported_flags {}
static int ksu_wrapper_open(struct inode *ino, struct file *fp) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
struct inode *orig_ino = file_inode(orig);
return orig->f_op->open(orig_ino, orig);
}
static int ksu_wrapper_flush(struct file *fp, fl_owner_t id) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->flush(orig, id);
}
static int ksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2, int datasync) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->fsync(orig, off1, off2, datasync);
}
static int ksu_wrapper_fasync(int arg, struct file *fp, int arg2) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->fasync(arg, orig, arg2);
}
static int ksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->lock(orig, arg1, fl);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1, size_t sz, loff_t *off, int arg2) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->sendpage) {
return orig->f_op->sendpage(orig, pg, arg1, sz, off, arg2);
}
return -EINVAL;
}
#endif
static unsigned long ksu_wrapper_get_unmapped_area(struct file *fp, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->get_unmapped_area) {
return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3, arg4);
}
return -EINVAL;
}
// static int ksu_wrapper_check_flags(int arg) {}
static int ksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->flock) {
return orig->f_op->flock(orig, arg1, fl);
}
return -EINVAL;
}
static ssize_t ksu_wrapper_splice_write(struct pipe_inode_info * pii, struct file *fp, loff_t *off, size_t sz, unsigned int arg1) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->splice_write) {
return orig->f_op->splice_write(pii, orig, off, sz, arg1);
}
return -EINVAL;
}
static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off, struct pipe_inode_info *pii, size_t sz, unsigned int arg1) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->splice_read) {
return orig->f_op->splice_read(orig, off, pii, sz, arg1);
}
return -EINVAL;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
void ksu_wrapper_splice_eof(struct file *fp) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->splice_eof) {
return orig->f_op->splice_eof(orig);
}
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lease **fl, void **p) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->setlease) {
return orig->f_op->setlease(orig, arg1, fl, p);
}
return -EINVAL;
}
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lock **fl, void **p) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->setlease) {
return orig->f_op->setlease(orig, arg1, fl, p);
}
return -EINVAL;
}
#else
static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl, void **p) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->setlease) {
return orig->f_op->setlease(orig, arg1, fl, p);
}
return -EINVAL;
}
#endif
static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, loff_t len) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->fallocate) {
return orig->f_op->fallocate(orig, mode, offset, len);
}
return -EINVAL;
}
static void ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) {
struct ksu_file_wrapper* data = f->private_data;
struct file* orig = data->orig;
if (orig->f_op->show_fdinfo) {
orig->f_op->show_fdinfo(m, orig);
}
}
static ssize_t ksu_wrapper_copy_file_range(struct file *f1, loff_t off1, struct file *f2,
loff_t off2, size_t sz, unsigned int flags) {
// TODO: determine which file to use
struct ksu_file_wrapper* data = f1->private_data;
struct file* orig = data->orig;
if (orig->f_op->copy_file_range) {
return orig->f_op->copy_file_range(orig, off1, f2, off2, sz, flags);
}
return -EINVAL;
}
static loff_t ksu_wrapper_remap_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int remap_flags) {
// TODO: determine which file to use
struct ksu_file_wrapper* data = file_in->private_data;
struct file* orig = data->orig;
if (orig->f_op->remap_file_range) {
return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags);
}
return -EINVAL;
}
static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, int flags) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->fadvise) {
return orig->f_op->fadvise(orig, off1, off2, flags);
}
return -EINVAL;
}
static int ksu_wrapper_release(struct inode *inode, struct file *filp) {
ksu_delete_file_wrapper(filp->private_data);
return 0;
}
struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp) {
struct ksu_file_wrapper* p = kcalloc(sizeof(struct ksu_file_wrapper), 1, GFP_KERNEL);
if (!p) {
return NULL;
}
get_file(fp);
p->orig = fp;
p->ops.owner = THIS_MODULE;
p->ops.llseek = fp->f_op->llseek ? ksu_wrapper_llseek : NULL;
p->ops.read = fp->f_op->read ? ksu_wrapper_read : NULL;
p->ops.write = fp->f_op->write ? ksu_wrapper_write : NULL;
p->ops.read_iter = fp->f_op->read_iter ? ksu_wrapper_read_iter : NULL;
p->ops.write_iter = fp->f_op->write_iter ? ksu_wrapper_write_iter : NULL;
p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL;
#endif
p->ops.iterate_shared = fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL;
p->ops.poll = fp->f_op->poll ? ksu_wrapper_poll : NULL;
p->ops.unlocked_ioctl = fp->f_op->unlocked_ioctl ? ksu_wrapper_unlocked_ioctl : NULL;
p->ops.compat_ioctl = fp->f_op->compat_ioctl ? ksu_wrapper_compat_ioctl : NULL;
p->ops.mmap = fp->f_op->mmap ? ksu_wrapper_mmap : NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
p->ops.fop_flags = fp->f_op->fop_flags;
#else
p->ops.mmap_supported_flags = fp->f_op->mmap_supported_flags;
#endif
p->ops.open = fp->f_op->open ? ksu_wrapper_open : NULL;
p->ops.flush = fp->f_op->flush ? ksu_wrapper_flush : NULL;
p->ops.release = ksu_wrapper_release;
p->ops.fsync = fp->f_op->fsync ? ksu_wrapper_fsync : NULL;
p->ops.fasync = fp->f_op->fasync ? ksu_wrapper_fasync : NULL;
p->ops.lock = fp->f_op->lock ? ksu_wrapper_lock : NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
p->ops.sendpage = fp->f_op->sendpage ? ksu_wrapper_sendpage : NULL;
#endif
p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ? ksu_wrapper_get_unmapped_area : NULL;
p->ops.check_flags = fp->f_op->check_flags;
p->ops.flock = fp->f_op->flock ? ksu_wrapper_flock : NULL;
p->ops.splice_write = fp->f_op->splice_write ? ksu_wrapper_splice_write : NULL;
p->ops.splice_read = fp->f_op->splice_read ? ksu_wrapper_splice_read : NULL;
p->ops.setlease = fp->f_op->setlease ? ksu_wrapper_setlease : NULL;
p->ops.fallocate = fp->f_op->fallocate ? ksu_wrapper_fallocate : NULL;
p->ops.show_fdinfo = fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL;
p->ops.copy_file_range = fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL;
p->ops.remap_file_range = fp->f_op->remap_file_range ? ksu_wrapper_remap_file_range : NULL;
p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
p->ops.splice_eof = fp->f_op->splice_eof ? ksu_wrapper_splice_eof : NULL;
#endif
return p;
}
void ksu_delete_file_wrapper(struct ksu_file_wrapper* data) {
fput((struct file*) data->orig);
kfree(data);
}

14
kernel/file_wrapper.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef KSU_FILE_WRAPPER_H
#define KSU_FILE_WRAPPER_H
#include <linux/file.h>
#include <linux/fs.h>
struct ksu_file_wrapper {
struct file* orig;
struct file_operations ops;
};
struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp);
void ksu_delete_file_wrapper(struct ksu_file_wrapper* data);
#endif // KSU_FILE_WRAPPER_H

View File

@@ -1,28 +0,0 @@
#ifndef __KSU_H_KSHOOK
#define __KSU_H_KSHOOK
#include <linux/fs.h>
#include <linux/types.h>
// For sucompat
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
int *flags);
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
// For ksud
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
size_t *count_ptr, loff_t **pos);
// For ksud and sucompat
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
void *envp, int *flags);
// For volume button
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
int *value);
#endif

View File

@@ -1,94 +0,0 @@
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/nsproxy.h>
#include <linux/sched/task.h>
#include <linux/uaccess.h>
#include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h"
extern struct task_struct init_task;
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
struct ksu_ns_fs_saved {
struct nsproxy *ns;
struct fs_struct *fs;
};
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
{
ns_fs_saved->ns = current->nsproxy;
ns_fs_saved->fs = current->fs;
}
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
{
current->nsproxy = ns_fs_saved->ns;
current->fs = ns_fs_saved->fs;
}
static bool android_context_saved_checked = false;
static bool android_context_saved_enabled = false;
static struct ksu_ns_fs_saved android_context_saved;
void ksu_android_ns_fs_check()
{
if (android_context_saved_checked)
return;
android_context_saved_checked = true;
task_lock(current);
if (current->nsproxy && current->fs &&
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
android_context_saved_enabled = true;
#ifdef CONFIG_KSU_DEBUG
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
#endif
ksu_save_ns_fs(&android_context_saved);
} else {
pr_info("android context saved disabled\n");
}
task_unlock(current);
}
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
{
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
struct ksu_ns_fs_saved saved;
if (android_context_saved_enabled) {
#ifdef CONFIG_KSU_DEBUG
pr_info("start switch current nsproxy and fs to android context\n");
#endif
task_lock(current);
ksu_save_ns_fs(&saved);
ksu_load_ns_fs(&android_context_saved);
task_unlock(current);
}
struct file *fp = filp_open(filename, flags, mode);
if (android_context_saved_enabled) {
task_lock(current);
ksu_load_ns_fs(&saved);
task_unlock(current);
#ifdef CONFIG_KSU_DEBUG
pr_info("switch current nsproxy and fs back to saved successfully\n");
#endif
}
return fp;
}
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
loff_t *pos)
{
return kernel_read(p, buf, count, pos);
}
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
loff_t *pos)
{
return kernel_write(p, buf, count, pos);
}
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
long count)
{
return strncpy_from_user_nofault(dst, unsafe_addr, count);
}

View File

@@ -3,63 +3,7 @@
#include <linux/fs.h>
#include <linux/version.h>
#include "ss/policydb.h"
#include "linux/key.h"
/**
* list_count_nodes - count the number of nodes in a list
* @head: the head of the list
*
* This function iterates over the list starting from @head and counts
* the number of nodes in the list. It does not modify the list.
*
* Context: Any context. The function is safe to call in any context,
* including interrupt context, as it does not sleep or allocate
* memory.
*
* Return: the number of nodes in the list (excluding the head)
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
static inline __maybe_unused size_t list_count_nodes(const struct list_head *head)
{
const struct list_head *pos;
size_t count = 0;
if (!head)
return 0;
list_for_each(pos, head)
count++;
return count;
}
#endif
/*
* Adapt to Huawei HISI kernel without affecting other kernels ,
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
* From ss/ebitmap.h
*/
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
#ifdef HISI_SELINUX_EBITMAP_RO
#define CONFIG_IS_HW_HISI
#endif
#endif
extern long ksu_strncpy_from_user_nofault(char *dst,
const void __user *unsafe_addr,
long count);
extern void ksu_android_ns_fs_check();
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
umode_t mode);
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
loff_t *pos);
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
size_t count, loff_t *pos);
/*
* ksu_copy_from_user_retry
* try nofault copy first, if it fails, try with plain
@@ -67,14 +11,14 @@ extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
* 0 = success
*/
static long ksu_copy_from_user_retry(void *to,
const void __user *from, unsigned long count)
const void __user *from, unsigned long count)
{
long ret = copy_from_user_nofault(to, from, count);
if (likely(!ret))
return ret;
long ret = copy_from_user_nofault(to, from, count);
if (likely(!ret))
return ret;
// we faulted! fallback to slow path
return copy_from_user(to, from, count);
// we faulted! fallback to slow path
return copy_from_user(to, from, count);
}
#endif

181
kernel/kernel_umount.c Normal file
View File

@@ -0,0 +1,181 @@
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/task_work.h>
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/nsproxy.h>
#include <linux/path.h>
#include <linux/printk.h>
#include <linux/types.h>
#include "kernel_umount.h"
#include "klog.h" // IWYU pragma: keep
#include "allowlist.h"
#include "selinux/selinux.h"
#include "feature.h"
#include "ksud.h"
#include "umount_manager.h"
#include "sulog.h"
static bool ksu_kernel_umount_enabled = true;
static int kernel_umount_feature_get(u64 *value)
{
*value = ksu_kernel_umount_enabled ? 1 : 0;
return 0;
}
static int kernel_umount_feature_set(u64 value)
{
bool enable = value != 0;
ksu_kernel_umount_enabled = enable;
pr_info("kernel_umount: set to %d\n", enable);
return 0;
}
static const struct ksu_feature_handler kernel_umount_handler = {
.feature_id = KSU_FEATURE_KERNEL_UMOUNT,
.name = "kernel_umount",
.get_handler = kernel_umount_feature_get,
.set_handler = kernel_umount_feature_set,
};
extern int path_umount(struct path *path, int flags);
static void ksu_umount_mnt(struct path *path, int flags)
{
int err = path_umount(path, flags);
if (err) {
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
}
}
void try_umount(const char *mnt, int flags)
{
struct path path;
int err = kern_path(mnt, 0, &path);
if (err) {
return;
}
if (path.dentry != path.mnt->mnt_root) {
// it is not root mountpoint, maybe umounted by others already.
path_put(&path);
return;
}
ksu_umount_mnt(&path, flags);
}
struct umount_tw {
struct callback_head cb;
const struct cred *old_cred;
};
static void umount_tw_func(struct callback_head *cb)
{
struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
const struct cred *saved = NULL;
if (tw->old_cred) {
saved = override_creds(tw->old_cred);
}
struct mount_entry *entry;
down_read(&mount_list_lock);
list_for_each_entry(entry, &mount_list, list) {
pr_info("%s: unmounting: %s flags 0x%x\n", __func__, entry->umountable, entry->flags);
try_umount(entry->umountable, entry->flags);
}
up_read(&mount_list_lock);
ksu_umount_manager_execute_all(tw->old_cred);
if (saved)
revert_creds(saved);
if (tw->old_cred)
put_cred(tw->old_cred);
kfree(tw);
}
int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
{
struct umount_tw *tw;
// if there isn't any module mounted, just ignore it!
if (!ksu_module_mounted) {
return 0;
}
if (!ksu_kernel_umount_enabled) {
return 0;
}
// There are 5 scenarios:
// 1. Normal app: zygote -> appuid
// 2. Isolated process forked from zygote: zygote -> isolated_process
// 3. App zygote forked from zygote: zygote -> appuid
// 4. Isolated process froked from app zygote: appuid -> isolated_process (already handled by 3)
// 5. Isolated process froked from webview zygote (no need to handle, app cannot run custom code)
if (!is_appuid(new_uid) && !is_isolated_process(new_uid)) {
return 0;
}
if (!ksu_uid_should_umount(new_uid) && !is_isolated_process(new_uid)) {
return 0;
}
// check old process's selinux context, if it is not zygote, ignore it!
// because some su apps may setuid to untrusted_app but they are in global mount namespace
// when we umount for such process, that is a disaster!
// also handle case 4 and 5
bool is_zygote_child = is_zygote(get_current_cred());
if (!is_zygote_child) {
pr_info("handle umount ignore non zygote child: %d\n", current->pid);
return 0;
}
#if __SULOG_GATE
ksu_sulog_report_syscall(new_uid, NULL, "setuid", NULL);
#endif
// umount the target mnt
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
if (!tw)
return 0;
tw->old_cred = get_current_cred();
tw->cb.func = umount_tw_func;
int err = task_work_add(current, &tw->cb, TWA_RESUME);
if (err) {
if (tw->old_cred) {
put_cred(tw->old_cred);
}
kfree(tw);
pr_warn("unmount add task_work failed\n");
}
return 0;
}
void ksu_kernel_umount_init(void)
{
int rc = 0;
rc = ksu_umount_manager_init();
if (rc) {
pr_err("Failed to initialize umount manager: %d\n", rc);
}
if (ksu_register_feature_handler(&kernel_umount_handler)) {
pr_err("Failed to register kernel_umount feature handler\n");
}
}
void ksu_kernel_umount_exit(void)
{
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
}

25
kernel/kernel_umount.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef __KSU_H_KERNEL_UMOUNT
#define __KSU_H_KERNEL_UMOUNT
#include <linux/types.h>
#include <linux/list.h>
#include <linux/rwsem.h>
void ksu_kernel_umount_init(void);
void ksu_kernel_umount_exit(void);
void try_umount(const char *mnt, int flags);
// Handler function to be called from setresuid hook
int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
// for the umount list
struct mount_entry {
char *umountable;
unsigned int flags;
struct list_head list;
};
extern struct list_head mount_list;
extern struct rw_semaphore mount_list_lock;
#endif

View File

@@ -31,7 +31,7 @@
static int sukisu_is_su_allow_uid(uid_t uid)
{
return ksu_is_allow_uid(uid) ? 1 : 0;
return ksu_is_allow_uid_for_current(uid) ? 1 : 0;
}
static int sukisu_get_ap_mod_exclude(uid_t uid)

View File

@@ -9,13 +9,10 @@
* 并参照KernelPatch的标准KPM格式实现加载和控制
*/
#include <linux/export.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/kernfs.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/elf.h>
@@ -25,26 +22,25 @@
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <asm/elf.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/cacheflush.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/set_memory.h>
#include <linux/version.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <asm/insn.h>
#include <linux/kprobes.h>
#include <linux/stacktrace.h>
#include <linux/kallsyms.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) && defined(CONFIG_MODULES)
#include <linux/moduleloader.h>
#endif
#include "kpm.h"
#include "compact.h"
#define KPM_NAME_LEN 32
#define KPM_ARGS_LEN 1024
#ifndef NO_OPTIMIZE
#if defined(__GNUC__) && !defined(__clang__)
#define NO_OPTIMIZE __attribute__((optimize("O0")))
@@ -56,156 +52,231 @@
#endif
noinline NO_OPTIMIZE void sukisu_kpm_load_module_path(const char *path,
const char *args, void *ptr, void __user *result)
const char *args, void *ptr, int *result)
{
int res = -1;
printk("KPM: Stub function called (sukisu_kpm_load_module_path). "
pr_info("kpm: Stub function called (sukisu_kpm_load_module_path). "
"path=%s args=%s ptr=%p\n", path, args, ptr);
__asm__ volatile("nop");
if (copy_to_user(result, &res, sizeof(res)) < 1)
printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_load_module_path);
noinline NO_OPTIMIZE void sukisu_kpm_unload_module(const char *name,
void *ptr, void __user *result)
void *ptr, int *result)
{
int res = -1;
printk("KPM: Stub function called (sukisu_kpm_unload_module). "
pr_info("kpm: Stub function called (sukisu_kpm_unload_module). "
"name=%s ptr=%p\n", name, ptr);
__asm__ volatile("nop");
if (copy_to_user(result, &res, sizeof(res)) < 1)
printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_unload_module);
noinline NO_OPTIMIZE void sukisu_kpm_num(void __user *result)
noinline NO_OPTIMIZE void sukisu_kpm_num(int *result)
{
int res = 0;
printk("KPM: Stub function called (sukisu_kpm_num).\n");
pr_info("kpm: Stub function called (sukisu_kpm_num).\n");
__asm__ volatile("nop");
if (copy_to_user(result, &res, sizeof(res)) < 1)
printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_num);
noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, void __user *out,
void __user *result)
noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, char *buf, int bufferSize,
int *size)
{
int res = -1;
printk("KPM: Stub function called (sukisu_kpm_info). "
"name=%s buffer=%p\n", name, out);
pr_info("kpm: Stub function called (sukisu_kpm_info). "
"name=%s buffer=%p\n", name, buf);
__asm__ volatile("nop");
if (copy_to_user(result, &res, sizeof(res)) < 1)
printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_info);
noinline NO_OPTIMIZE void sukisu_kpm_list(void __user *out, unsigned int bufferSize,
void __user *result)
noinline NO_OPTIMIZE void sukisu_kpm_list(void *out, int bufferSize,
int *result)
{
int res = -1;
printk("KPM: Stub function called (sukisu_kpm_list). "
pr_info("kpm: Stub function called (sukisu_kpm_list). "
"buffer=%p size=%d\n", out, bufferSize);
if (copy_to_user(result, &res, sizeof(res)) < 1)
printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_list);
noinline NO_OPTIMIZE void sukisu_kpm_control(void __user *name, void __user *args,
void __user *result)
noinline NO_OPTIMIZE void sukisu_kpm_control(const char *name, const char *args, long arg_len,
int *result)
{
int res = -1;
printk("KPM: Stub function called (sukisu_kpm_control). "
"name=%p args=%p\n", name, args);
pr_info("kpm: Stub function called (sukisu_kpm_control). "
"name=%p args=%p arg_len=%ld\n", name, args, arg_len);
__asm__ volatile("nop");
if (copy_to_user(result, &res, sizeof(res)) < 1)
printk("KPM: Copy to user failed.");
}
EXPORT_SYMBOL(sukisu_kpm_control);
noinline NO_OPTIMIZE void sukisu_kpm_version(void __user *out, unsigned int bufferSize,
void __user *result)
noinline NO_OPTIMIZE void sukisu_kpm_version(char *buf, int bufferSize)
{
int res = -1;
printk("KPM: Stub function called (sukisu_kpm_version). "
"buffer=%p size=%d\n", out, bufferSize);
if (copy_to_user(result, &res, sizeof(res)) < 1)
printk("KPM: Copy to user failed.");
pr_info("kpm: Stub function called (sukisu_kpm_version). "
"buffer=%p\n", buf);
}
EXPORT_SYMBOL(sukisu_kpm_version);
noinline int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4,
unsigned long arg5)
noinline int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2,
unsigned long result_code)
{
if (arg2 == SUKISU_KPM_LOAD) {
char kernel_load_path[256] = { 0 };
char kernel_args_buffer[256] = { 0 };
int res = -1;
if (control_code == SUKISU_KPM_LOAD) {
char kernel_load_path[256];
char kernel_args_buffer[256];
if (arg3 == 0)
return -1;
if (arg1 == 0) {
res = -EINVAL;
goto exit;
}
if (!access_ok(arg1, 255)) {
goto invalid_arg;
}
strncpy_from_user((char *)&kernel_load_path, (const char __user *)arg3, 255);
strncpy_from_user((char *)&kernel_load_path, (const char *)arg1, 255);
if (arg4 != 0)
strncpy_from_user((char *)&kernel_args_buffer, (const char __user *)arg4, 255);
if (arg2 != 0) {
if (!access_ok(arg2, 255)) {
goto invalid_arg;
}
strncpy_from_user((char *)&kernel_args_buffer, (const char *)arg2, 255);
}
sukisu_kpm_load_module_path((const char *)&kernel_load_path,
(const char *)&kernel_args_buffer, NULL, (void __user *)arg5);
} else if (arg2 == SUKISU_KPM_UNLOAD) {
char kernel_name_buffer[256] = { 0 };
(const char *)&kernel_args_buffer, NULL, &res);
} else if (control_code == SUKISU_KPM_UNLOAD) {
char kernel_name_buffer[256];
if (arg3 == 0)
return -1;
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg3, 255);
sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL,
(void __user *)arg5);
} else if (arg2 == SUKISU_KPM_NUM) {
sukisu_kpm_num((void __user *)arg5);
} else if (arg2 == SUKISU_KPM_INFO) {
char kernel_name_buffer[256] = { 0 };
if (arg1 == 0) {
res = -EINVAL;
goto exit;
}
if (arg3 == 0 || arg4 == 0)
return -1;
if (!access_ok(arg1, sizeof(kernel_name_buffer))) {
goto invalid_arg;
}
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg3, 255);
strncpy_from_user((char *)&kernel_name_buffer, (const char *)arg1, sizeof(kernel_name_buffer));
sukisu_kpm_info((const char *)&kernel_name_buffer, (char __user *)arg4,
(void __user *)arg5);
} else if (arg2 == SUKISU_KPM_LIST) {
sukisu_kpm_list((char __user *)arg3, (unsigned int)arg4, (void __user *)arg5);
} else if (arg2 == SUKISU_KPM_CONTROL) {
sukisu_kpm_control((char __user *)arg3, (char __user *)arg4, (void __user *)arg5);
} else if (arg2 == SUKISU_KPM_VERSION) {
sukisu_kpm_version((char __user *)arg3, (unsigned int)arg4, (void __user *)arg5);
sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL, &res);
} else if (control_code == SUKISU_KPM_NUM) {
sukisu_kpm_num(&res);
} else if (control_code == SUKISU_KPM_INFO) {
char kernel_name_buffer[256];
char buf[256];
int size;
if (arg1 == 0 || arg2 == 0) {
res = -EINVAL;
goto exit;
}
if (!access_ok(arg1, sizeof(kernel_name_buffer))) {
goto invalid_arg;
}
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg1, sizeof(kernel_name_buffer));
sukisu_kpm_info((const char *)&kernel_name_buffer, (char *)&buf, sizeof(buf), &size);
if (!access_ok(arg2, size)) {
goto invalid_arg;
}
res = copy_to_user(arg2, &buf, size);
} else if (control_code == SUKISU_KPM_LIST) {
char buf[1024];
int len = (int) arg2;
if (len <= 0) {
res = -EINVAL;
goto exit;
}
if (!access_ok(arg2, len)) {
goto invalid_arg;
}
sukisu_kpm_list((char *)&buf, sizeof(buf), &res);
if (res > len) {
res = -ENOBUFS;
goto exit;
}
if (copy_to_user(arg1, &buf, len) != 0)
pr_info("kpm: Copy to user failed.");
} else if (control_code == SUKISU_KPM_CONTROL) {
char kpm_name[KPM_NAME_LEN] = { 0 };
char kpm_args[KPM_ARGS_LEN] = { 0 };
if (!access_ok(arg1, sizeof(kpm_name))) {
goto invalid_arg;
}
if (!access_ok(arg2, sizeof(kpm_args))) {
goto invalid_arg;
}
long name_len = strncpy_from_user((char *)&kpm_name, (const char __user *)arg1, sizeof(kpm_name));
if (name_len <= 0) {
res = -EINVAL;
goto exit;
}
long arg_len = strncpy_from_user((char *)&kpm_args, (const char __user *)arg2, sizeof(kpm_args));
sukisu_kpm_control((const char *)&kpm_name, (const char *)&kpm_args, arg_len, &res);
} else if (control_code == SUKISU_KPM_VERSION) {
char buffer[256] = {0};
sukisu_kpm_version((char*) &buffer, sizeof(buffer));
unsigned int outlen = (unsigned int) arg2;
int len = strlen(buffer);
if (len >= outlen) len = outlen - 1;
res = copy_to_user(arg1, &buffer, len + 1);
}
exit:
if (copy_to_user(result_code, &res, sizeof(res)) != 0)
pr_info("kpm: Copy to user failed.");
return 0;
invalid_arg:
pr_err("kpm: invalid pointer detected! arg1: %px arg2: %px\n", (void *)arg1, (void *)arg2);
res = -EFAULT;
goto exit;
}
EXPORT_SYMBOL(sukisu_handle_kpm);
int sukisu_is_kpm_control_code(unsigned long arg2) {
return (arg2 >= CMD_KPM_CONTROL &&
arg2 <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
int sukisu_is_kpm_control_code(unsigned long control_code) {
return (control_code >= CMD_KPM_CONTROL &&
control_code <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
}
int do_kpm(void __user *arg)
{
struct ksu_kpm_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("kpm: copy_from_user failed\n");
return -EFAULT;
}
if (!access_ok(cmd.control_code, sizeof(int))) {
pr_err("kpm: invalid control_code pointer %px\n", (void *)cmd.control_code);
return -EFAULT;
}
if (!access_ok(cmd.result_code, sizeof(int))) {
pr_err("kpm: invalid result_code pointer %px\n", (void *)cmd.result_code);
return -EFAULT;
}
return sukisu_handle_kpm(cmd.control_code, cmd.arg1, cmd.arg2, cmd.result_code);
}

View File

@@ -1,58 +1,70 @@
#ifndef __SUKISU_KPM_H
#define __SUKISU_KPM_H
extern int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4,
unsigned long arg5);
extern int sukisu_is_kpm_control_code(unsigned long arg2);
#include <linux/types.h>
#include <linux/ioctl.h>
struct ksu_kpm_cmd {
__aligned_u64 __user control_code;
__aligned_u64 __user arg1;
__aligned_u64 __user arg2;
__aligned_u64 __user result_code;
};
int sukisu_handle_kpm(unsigned long control_code, unsigned long arg3, unsigned long arg4, unsigned long result_code);
int sukisu_is_kpm_control_code(unsigned long control_code);
int do_kpm(void __user *arg);
#define KSU_IOCTL_KPM _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0)
/* KPM Control Code */
#define CMD_KPM_CONTROL 28
#define CMD_KPM_CONTROL_MAX 35
#define CMD_KPM_CONTROL 1
#define CMD_KPM_CONTROL_MAX 10
/* Control Code */
/*
* prctl(xxx, 28, "PATH", "ARGS")
* prctl(xxx, 1, "PATH", "ARGS")
* success return 0, error return -N
*/
#define SUKISU_KPM_LOAD 28
#define SUKISU_KPM_LOAD 1
/*
* prctl(xxx, 29, "NAME")
* prctl(xxx, 2, "NAME")
* success return 0, error return -N
*/
#define SUKISU_KPM_UNLOAD 29
#define SUKISU_KPM_UNLOAD 2
/*
* num = prctl(xxx, 30)
* num = prctl(xxx, 3)
* error return -N
* success return +num or 0
*/
#define SUKISU_KPM_NUM 30
#define SUKISU_KPM_NUM 3
/*
* prctl(xxx, 31, Buffer, BufferSize)
* prctl(xxx, 4, Buffer, BufferSize)
* success return +out, error return -N
*/
#define SUKISU_KPM_LIST 31
#define SUKISU_KPM_LIST 4
/*
* prctl(xxx, 32, "NAME", Buffer[256])
* prctl(xxx, 5, "NAME", Buffer[256])
* success return +out, error return -N
*/
#define SUKISU_KPM_INFO 32
#define SUKISU_KPM_INFO 5
/*
* prctl(xxx, 33, "NAME", "ARGS")
* prctl(xxx, 6, "NAME", "ARGS")
* success return KPM's result value
* error return -N
*/
#define SUKISU_KPM_CONTROL 33
#define SUKISU_KPM_CONTROL 6
/*
* prctl(xxx, 34, buffer, bufferSize)
* prctl(xxx, 7, buffer, bufferSize)
* success return KPM's result value
* error return -N
*/
#define SUKISU_KPM_VERSION 34
#define SUKISU_KPM_VERSION 7
#endif

View File

@@ -3,89 +3,103 @@
#include <linux/kobject.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/version.h>
#include "allowlist.h"
#include "arch.h"
#include "core_hook.h"
#include "feature.h"
#include "klog.h" // IWYU pragma: keep
#include "ksu.h"
#include "throne_tracker.h"
#include "syscall_hook_manager.h"
#include "ksud.h"
#include "supercalls.h"
#include "sulog.h"
#include "throne_comm.h"
#include "dynamic_manager.h"
static struct workqueue_struct *ksu_workqueue;
bool ksu_queue_work(struct work_struct *work)
{
return queue_work(ksu_workqueue, work);
return queue_work(ksu_workqueue, work);
}
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
void *argv, void *envp, int *flags);
void sukisu_custom_config_init(void)
{
}
extern void ksu_sucompat_init();
extern void ksu_sucompat_exit();
extern void ksu_ksud_init();
extern void ksu_ksud_exit();
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
extern void ksu_trace_register();
extern void ksu_trace_unregister();
void sukisu_custom_config_exit(void)
{
ksu_uid_exit();
ksu_throne_comm_exit();
ksu_dynamic_manager_exit();
#if __SULOG_GATE
ksu_sulog_exit();
#endif
}
int __init kernelsu_init(void)
{
#ifdef CONFIG_KSU_DEBUG
pr_alert("*************************************************************");
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
pr_alert("** **");
pr_alert("** You are running KernelSU in DEBUG mode **");
pr_alert("** **");
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
pr_alert("*************************************************************");
pr_alert("*************************************************************");
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
pr_alert("** **");
pr_alert("** You are running KernelSU in DEBUG mode **");
pr_alert("** **");
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
pr_alert("*************************************************************");
#endif
ksu_core_init();
ksu_feature_init();
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
ksu_supercalls_init();
ksu_allowlist_init();
sukisu_custom_config_init();
ksu_throne_tracker_init();
#ifdef CONFIG_KSU_KPROBES_HOOK
ksu_sucompat_init();
ksu_ksud_init();
ksu_syscall_hook_manager_init();
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
ksu_allowlist_init();
ksu_throne_tracker_init();
#ifdef KSU_KPROBES_HOOK
ksu_ksud_init();
#else
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
#endif
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
ksu_trace_register();
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
#endif
#ifdef MODULE
#ifndef CONFIG_KSU_DEBUG
kobject_del(&THIS_MODULE->mkobj.kobj);
kobject_del(&THIS_MODULE->mkobj.kobj);
#endif
#endif
return 0;
return 0;
}
extern void ksu_observer_exit(void);
void kernelsu_exit(void)
{
ksu_allowlist_exit();
ksu_allowlist_exit();
ksu_throne_tracker_exit();
ksu_observer_exit();
destroy_workqueue(ksu_workqueue);
ksu_throne_tracker_exit();
#ifdef CONFIG_KSU_KPROBES_HOOK
ksu_ksud_exit();
ksu_sucompat_exit();
destroy_workqueue(ksu_workqueue);
#ifdef KSU_KPROBES_HOOK
ksu_ksud_exit();
#endif
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
ksu_trace_unregister();
#endif
ksu_syscall_hook_manager_exit();
ksu_core_exit();
sukisu_custom_config_exit();
ksu_supercalls_exit();
ksu_feature_exit();
}
module_init(kernelsu_init);
@@ -94,4 +108,9 @@ module_exit(kernelsu_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("weishu");
MODULE_DESCRIPTION("Android KernelSU");
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 13, 0)
MODULE_IMPORT_NS("VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver");
#else
MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver);
#endif

View File

@@ -7,40 +7,12 @@
#define KERNEL_SU_VERSION KSU_VERSION
#define KERNEL_SU_OPTION 0xDEADBEEF
#define CMD_GRANT_ROOT 0
#define CMD_BECOME_MANAGER 1
#define CMD_GET_VERSION 2
#define CMD_ALLOW_SU 3
#define CMD_DENY_SU 4
#define CMD_GET_ALLOW_LIST 5
#define CMD_GET_DENY_LIST 6
#define CMD_REPORT_EVENT 7
#define CMD_SET_SEPOLICY 8
#define CMD_CHECK_SAFEMODE 9
#define CMD_GET_APP_PROFILE 10
#define CMD_SET_APP_PROFILE 11
#define CMD_UID_GRANTED_ROOT 12
#define CMD_UID_SHOULD_UMOUNT 13
#define CMD_IS_SU_ENABLED 14
#define CMD_ENABLE_SU 15
#define CMD_GET_FULL_VERSION 0xC0FFEE1A
#define CMD_ENABLE_KPM 100
#define CMD_HOOK_TYPE 101
#define CMD_DYNAMIC_MANAGER 103
#define CMD_GET_MANAGERS 104
extern bool ksu_uid_scanner_enabled;
#define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2
#define EVENT_MODULE_MOUNTED 3
#define KSU_APP_PROFILE_VER 2
#define KSU_MAX_PACKAGE_NAME 256
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
#define KSU_MAX_GROUPS 32
#define KSU_SELINUX_DOMAIN 64
// SukiSU Ultra kernel su version full strings
#ifndef KSU_VERSION_FULL
#define KSU_VERSION_FULL "v3.x-00000000@unknown"
@@ -51,6 +23,10 @@
#define DYNAMIC_MANAGER_OP_GET 1
#define DYNAMIC_MANAGER_OP_CLEAR 2
#define UID_SCANNER_OP_GET_STATUS 0
#define UID_SCANNER_OP_TOGGLE 1
#define UID_SCANNER_OP_CLEAR_ENV 2
struct dynamic_manager_user_config {
unsigned int operation;
unsigned int size;
@@ -65,68 +41,22 @@ struct manager_list_info {
} managers[2];
};
struct root_profile {
int32_t uid;
int32_t gid;
int32_t groups_count;
int32_t groups[KSU_MAX_GROUPS];
// kernel_cap_t is u32[2] for capabilities v3
struct {
u64 effective;
u64 permitted;
u64 inheritable;
} capabilities;
char selinux_domain[KSU_SELINUX_DOMAIN];
int32_t namespaces;
};
struct non_root_profile {
bool umount_modules;
};
struct app_profile {
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
u32 version;
// this is usually the package of the app, but can be other value for special apps
char key[KSU_MAX_PACKAGE_NAME];
int32_t current_uid;
bool allow_su;
union {
struct {
bool use_default;
char template_name[KSU_MAX_PACKAGE_NAME];
struct root_profile profile;
} rp_config;
struct {
bool use_default;
struct non_root_profile profile;
} nrp_config;
};
};
bool ksu_queue_work(struct work_struct *work);
#if 0
static inline int startswith(char *s, char *prefix)
{
return strncmp(s, prefix, strlen(prefix));
return strncmp(s, prefix, strlen(prefix));
}
static inline int endswith(const char *s, const char *t)
{
size_t slen = strlen(s);
size_t tlen = strlen(t);
if (tlen > slen)
return 1;
return strcmp(s + slen - tlen, t);
size_t slen = strlen(s);
size_t tlen = strlen(t);
if (tlen > slen)
return 1;
return strcmp(s + slen - tlen, t);
}
#endif
#endif

View File

@@ -1,69 +0,0 @@
#include "ksu_trace.h"
// extern kernelsu functions
extern bool ksu_vfs_read_hook __read_mostly;
extern bool ksu_input_hook __read_mostly;
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags);
extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *flags);
extern int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, size_t *count_ptr);
extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
// end kernelsu functions
// tracepoint callback functions
void ksu_trace_execveat_sucompat_hook_callback(void *data, int *fd, struct filename **filename_ptr,
void *argv, void *envp, int *flags)
{
ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
}
void ksu_trace_faccessat_hook_callback(void *data, int *dfd, const char __user **filename_user,
int *mode, int *flags)
{
ksu_handle_faccessat(dfd, filename_user, mode, flags);
}
void ksu_trace_sys_read_hook_callback(void *data, unsigned int fd, char __user **buf_ptr,
size_t *count_ptr)
{
if (unlikely(ksu_vfs_read_hook))
ksu_handle_sys_read(fd, buf_ptr, count_ptr);
}
void ksu_trace_stat_hook_callback(void *data, int *dfd, const char __user **filename_user,
int *flags)
{
ksu_handle_stat(dfd, filename_user, flags);
}
void ksu_trace_input_hook_callback(void *data, unsigned int *type, unsigned int *code,
int *value)
{
if (unlikely(ksu_input_hook))
ksu_handle_input_handle_event(type, code, value);
}
// end tracepoint callback functions
// register tracepoint callback functions
void ksu_trace_register(void)
{
register_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
register_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
register_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
register_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
register_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
}
// unregister tracepoint callback functions
void ksu_trace_unregister(void)
{
unregister_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
unregister_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
unregister_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
unregister_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
unregister_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
}

View File

@@ -1,37 +0,0 @@
#undef TRACE_SYSTEM
#define TRACE_SYSTEM ksu_trace
#if !defined(_KSU_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
#define _KSU_TRACE_H
#include <linux/fs.h>
#include <linux/tracepoint.h>
DECLARE_TRACE(ksu_trace_execveat_sucompat_hook,
TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags),
TP_ARGS(fd, filename_ptr, argv, envp, flags));
DECLARE_TRACE(ksu_trace_faccessat_hook,
TP_PROTO(int *dfd, const char __user **filename_user, int *mode, int *flags),
TP_ARGS(dfd, filename_user, mode, flags));
DECLARE_TRACE(ksu_trace_sys_read_hook,
TP_PROTO(unsigned int fd, char __user **buf_ptr, size_t *count_ptr),
TP_ARGS(fd, buf_ptr, count_ptr));
DECLARE_TRACE(ksu_trace_stat_hook,
TP_PROTO(int *dfd, const char __user **filename_user, int *flags),
TP_ARGS(dfd, filename_user, flags));
DECLARE_TRACE(ksu_trace_input_hook,
TP_PROTO(unsigned int *type, unsigned int *code, int *value),
TP_ARGS(type, code, value));
#endif /* _KSU_TRACE_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE ksu_trace
#include <trace/define_trace.h>

View File

@@ -1,8 +0,0 @@
#define CREATE_TRACE_POINTS
#include "ksu_trace.h"
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_execveat_sucompat_hook);
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_faccessat_hook);
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_sys_read_hook);
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_stat_hook);
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_input_hook);

File diff suppressed because it is too large Load Diff

View File

@@ -3,13 +3,19 @@
#define KSUD_PATH "/data/adb/ksud"
void ksu_ksud_init();
void ksu_ksud_exit();
void on_post_fs_data(void);
void on_module_mounted(void);
void on_boot_completed(void);
bool ksu_is_safe_mode(void);
extern u32 ksu_devpts_sid;
int nuke_ext4_sysfs(const char* mnt);
extern bool ksu_execveat_hook __read_mostly;
extern int ksu_handle_pre_ksud(const char *filename);
extern u32 ksu_file_sid;
extern bool ksu_module_mounted;
extern bool ksu_boot_completed;
#endif

View File

@@ -13,30 +13,31 @@ extern void ksu_add_manager(uid_t uid, int signature_index);
extern void ksu_remove_manager(uid_t uid);
extern int ksu_get_manager_signature_index(uid_t uid);
static inline bool ksu_is_manager_uid_valid()
static inline bool ksu_is_manager_uid_valid(void)
{
return ksu_manager_uid != KSU_INVALID_UID;
return ksu_manager_uid != KSU_INVALID_UID;
}
static inline bool is_manager()
static inline bool is_manager(void)
{
return unlikely(ksu_is_any_manager(current_uid().val) ||
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
return unlikely(ksu_is_any_manager(current_uid().val) ||
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
}
static inline uid_t ksu_get_manager_uid()
static inline uid_t ksu_get_manager_uid(void)
{
return ksu_manager_uid;
return ksu_manager_uid;
}
static inline void ksu_set_manager_uid(uid_t uid)
{
ksu_manager_uid = uid;
ksu_manager_uid = uid;
}
static inline void ksu_invalidate_manager_uid()
static inline void ksu_invalidate_manager_uid(void)
{
ksu_manager_uid = KSU_INVALID_UID;
ksu_manager_uid = KSU_INVALID_UID;
}
#endif
int ksu_observer_init(void);
#endif

View File

@@ -9,5 +9,9 @@
#define EXPECTED_SIZE_OTHER 0x300
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
typedef struct {
unsigned size;
const char *sha256;
} apk_sign_key_t;
#endif /* MANAGER_SIGN_H */
#endif /* MANAGER_SIGN_H */

357
kernel/manual_su.c Normal file
View File

@@ -0,0 +1,357 @@
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/printk.h>
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/file.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/binfmts.h>
#include "manual_su.h"
#include "ksu.h"
#include "allowlist.h"
#include "manager.h"
#include "app_profile.h"
static bool current_verified = false;
static void ksu_cleanup_expired_tokens(void);
static bool is_current_verified(void);
static void add_pending_root(uid_t uid);
static struct pending_uid pending_uids[MAX_PENDING] = {0};
static int pending_cnt = 0;
static struct ksu_token_entry auth_tokens[MAX_TOKENS] = {0};
static int token_count = 0;
static DEFINE_SPINLOCK(token_lock);
static char* get_token_from_envp(void)
{
struct mm_struct *mm;
char *envp_start, *envp_end;
char *env_ptr, *token = NULL;
unsigned long env_len;
char *env_copy = NULL;
if (!current->mm)
return NULL;
mm = current->mm;
down_read(&mm->mmap_lock);
envp_start = (char *)mm->env_start;
envp_end = (char *)mm->env_end;
env_len = envp_end - envp_start;
if (env_len <= 0 || env_len > PAGE_SIZE * 32) {
up_read(&mm->mmap_lock);
return NULL;
}
env_copy = kzalloc(env_len + 1, GFP_KERNEL);
if (!env_copy) {
up_read(&mm->mmap_lock);
return NULL;
}
if (copy_from_user(env_copy, envp_start, env_len)) {
kfree(env_copy);
up_read(&mm->mmap_lock);
return NULL;
}
up_read(&mm->mmap_lock);
env_copy[env_len] = '\0';
env_ptr = env_copy;
while (env_ptr < env_copy + env_len) {
if (strncmp(env_ptr, KSU_TOKEN_ENV_NAME "=", strlen(KSU_TOKEN_ENV_NAME) + 1) == 0) {
char *token_start = env_ptr + strlen(KSU_TOKEN_ENV_NAME) + 1;
char *token_end = strchr(token_start, '\0');
if (token_end && (token_end - token_start) == KSU_TOKEN_LENGTH) {
token = kzalloc(KSU_TOKEN_LENGTH + 1, GFP_KERNEL);
if (token) {
memcpy(token, token_start, KSU_TOKEN_LENGTH);
token[KSU_TOKEN_LENGTH] = '\0';
pr_info("manual_su: found auth token in environment\n");
}
}
break;
}
env_ptr += strlen(env_ptr) + 1;
}
kfree(env_copy);
return token;
}
static char* ksu_generate_auth_token(void)
{
static char token_buffer[KSU_TOKEN_LENGTH + 1];
unsigned long flags;
int i;
ksu_cleanup_expired_tokens();
spin_lock_irqsave(&token_lock, flags);
if (token_count >= MAX_TOKENS) {
for (i = 0; i < MAX_TOKENS - 1; i++) {
auth_tokens[i] = auth_tokens[i + 1];
}
token_count = MAX_TOKENS - 1;
}
for (i = 0; i < KSU_TOKEN_LENGTH; i++) {
u8 rand_byte;
get_random_bytes(&rand_byte, 1);
int char_type = rand_byte % 3;
if (char_type == 0) {
token_buffer[i] = 'A' + (rand_byte % 26);
} else if (char_type == 1) {
token_buffer[i] = 'a' + (rand_byte % 26);
} else {
token_buffer[i] = '0' + (rand_byte % 10);
}
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
#else
strlcpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
#endif
auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ;
auth_tokens[token_count].used = false;
token_count++;
spin_unlock_irqrestore(&token_lock, flags);
pr_info("manual_su: generated new auth token (expires in %d seconds)\n", KSU_TOKEN_EXPIRE_TIME);
return token_buffer;
}
static bool ksu_verify_auth_token(const char *token)
{
unsigned long flags;
bool valid = false;
int i;
if (!token || strlen(token) != KSU_TOKEN_LENGTH) {
return false;
}
spin_lock_irqsave(&token_lock, flags);
for (i = 0; i < token_count; i++) {
if (!auth_tokens[i].used &&
time_before(jiffies, auth_tokens[i].expire_time) &&
strcmp(auth_tokens[i].token, token) == 0) {
auth_tokens[i].used = true;
valid = true;
pr_info("manual_su: auth token verified successfully\n");
break;
}
}
spin_unlock_irqrestore(&token_lock, flags);
if (!valid) {
pr_warn("manual_su: invalid or expired auth token\n");
}
return valid;
}
static void ksu_cleanup_expired_tokens(void)
{
unsigned long flags;
int i, j;
spin_lock_irqsave(&token_lock, flags);
for (i = 0; i < token_count; ) {
if (time_after(jiffies, auth_tokens[i].expire_time) || auth_tokens[i].used) {
for (j = i; j < token_count - 1; j++) {
auth_tokens[j] = auth_tokens[j + 1];
}
token_count--;
pr_debug("manual_su: cleaned up expired/used token\n");
} else {
i++;
}
}
spin_unlock_irqrestore(&token_lock, flags);
}
static int handle_token_generation(struct manual_su_request *request)
{
if (current_uid().val > 2000) {
pr_warn("manual_su: token generation denied for app UID %d\n", current_uid().val);
return -EPERM;
}
char *new_token = ksu_generate_auth_token();
if (!new_token) {
pr_err("manual_su: failed to generate token\n");
return -ENOMEM;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
#else
strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
#endif
pr_info("manual_su: auth token generated successfully\n");
return 0;
}
static int handle_escalation_request(struct manual_su_request *request)
{
uid_t target_uid = request->target_uid;
pid_t target_pid = request->target_pid;
struct task_struct *tsk;
rcu_read_lock();
tsk = pid_task(find_vpid(target_pid), PIDTYPE_PID);
if (!tsk || ksu_task_is_dead(tsk)) {
rcu_read_unlock();
pr_err("cmd_su: PID %d is invalid or dead\n", target_pid);
return -ESRCH;
}
rcu_read_unlock();
if (current_uid().val == 0 || is_manager() || ksu_is_allow_uid_for_current(current_uid().val))
goto allowed;
char *env_token = get_token_from_envp();
if (!env_token) {
pr_warn("manual_su: no auth token found in environment\n");
return -EACCES;
}
bool token_valid = ksu_verify_auth_token(env_token);
kfree(env_token);
if (!token_valid) {
pr_warn("manual_su: token verification failed\n");
return -EACCES;
}
allowed:
current_verified = true;
escape_to_root_for_cmd_su(target_uid, target_pid);
return 0;
}
static int handle_add_pending_request(struct manual_su_request *request)
{
uid_t target_uid = request->target_uid;
if (!is_current_verified()) {
pr_warn("manual_su: add_pending denied, not verified\n");
return -EPERM;
}
add_pending_root(target_uid);
current_verified = false;
pr_info("manual_su: pending root added for UID %d\n", target_uid);
return 0;
}
int ksu_handle_manual_su_request(int option, struct manual_su_request *request)
{
if (!request) {
pr_err("manual_su: invalid request pointer\n");
return -EINVAL;
}
switch (option) {
case MANUAL_SU_OP_GENERATE_TOKEN:
pr_info("manual_su: handling token generation request\n");
return handle_token_generation(request);
case MANUAL_SU_OP_ESCALATE:
pr_info("manual_su: handling escalation request for UID %d, PID %d\n",
request->target_uid, request->target_pid);
return handle_escalation_request(request);
case MANUAL_SU_OP_ADD_PENDING:
pr_info("manual_su: handling add pending request for UID %d\n", request->target_uid);
return handle_add_pending_request(request);
default:
pr_err("manual_su: unknown option %d\n", option);
return -EINVAL;
}
}
static bool is_current_verified(void)
{
return current_verified;
}
bool is_pending_root(uid_t uid)
{
for (int i = 0; i < pending_cnt; i++) {
if (pending_uids[i].uid == uid) {
pending_uids[i].use_count++;
pending_uids[i].remove_calls++;
return true;
}
}
return false;
}
void remove_pending_root(uid_t uid)
{
for (int i = 0; i < pending_cnt; i++) {
if (pending_uids[i].uid == uid) {
pending_uids[i].remove_calls++;
if (pending_uids[i].remove_calls >= REMOVE_DELAY_CALLS) {
pending_uids[i] = pending_uids[--pending_cnt];
pr_info("pending_root: removed UID %d after %d calls\n", uid, REMOVE_DELAY_CALLS);
ksu_temp_revoke_root_once(uid);
} else {
pr_info("pending_root: UID %d remove_call=%d (<%d)\n",
uid, pending_uids[i].remove_calls, REMOVE_DELAY_CALLS);
}
return;
}
}
}
static void add_pending_root(uid_t uid)
{
if (pending_cnt >= MAX_PENDING) {
pr_warn("pending_root: cache full\n");
return;
}
for (int i = 0; i < pending_cnt; i++) {
if (pending_uids[i].uid == uid) {
pending_uids[i].use_count = 0;
pending_uids[i].remove_calls = 0;
return;
}
}
pending_uids[pending_cnt++] = (struct pending_uid){uid, 0};
ksu_temp_grant_root_once(uid);
pr_info("pending_root: cached UID %d\n", uid);
}
void ksu_try_escalate_for_uid(uid_t uid)
{
if (!is_pending_root(uid))
return;
pr_info("pending_root: UID=%d temporarily allowed\n", uid);
remove_pending_root(uid);
}

49
kernel/manual_su.h Normal file
View File

@@ -0,0 +1,49 @@
#ifndef __KSU_MANUAL_SU_H
#define __KSU_MANUAL_SU_H
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0)
#define mmap_lock mmap_sem
#endif
#define ksu_task_is_dead(t) ((t)->exit_state != 0)
#define MAX_PENDING 16
#define REMOVE_DELAY_CALLS 150
#define MAX_TOKENS 10
#define KSU_SU_VERIFIED_BIT (1UL << 0)
#define KSU_TOKEN_LENGTH 32
#define KSU_TOKEN_ENV_NAME "KSU_AUTH_TOKEN"
#define KSU_TOKEN_EXPIRE_TIME 150
#define MANUAL_SU_OP_GENERATE_TOKEN 0
#define MANUAL_SU_OP_ESCALATE 1
#define MANUAL_SU_OP_ADD_PENDING 2
struct pending_uid {
uid_t uid;
int use_count;
int remove_calls;
};
struct manual_su_request {
uid_t target_uid;
pid_t target_pid;
char token_buffer[KSU_TOKEN_LENGTH + 1];
};
struct ksu_token_entry {
char token[KSU_TOKEN_LENGTH + 1];
unsigned long expire_time;
bool used;
};
int ksu_handle_manual_su_request(int option, struct manual_su_request *request);
bool is_pending_root(uid_t uid);
void remove_pending_root(uid_t uid);
void ksu_try_escalate_for_uid(uid_t uid);
#endif

133
kernel/pkg_observer.c Normal file
View File

@@ -0,0 +1,133 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/fsnotify_backend.h>
#include <linux/slab.h>
#include <linux/rculist.h>
#include <linux/version.h>
#include "klog.h" // IWYU pragma: keep
#include "ksu.h"
#include "throne_tracker.h"
#include "throne_comm.h"
#define MASK_SYSTEM (FS_CREATE | FS_MOVE | FS_EVENT_ON_CHILD)
struct watch_dir {
const char *path;
u32 mask;
struct path kpath;
struct inode *inode;
struct fsnotify_mark *mark;
};
static struct fsnotify_group *g;
static int ksu_handle_inode_event(struct fsnotify_mark *mark, u32 mask,
struct inode *inode, struct inode *dir,
const struct qstr *file_name, u32 cookie)
{
if (!file_name)
return 0;
if (mask & FS_ISDIR)
return 0;
if (file_name->len == 13 &&
!memcmp(file_name->name, "packages.list", 13)) {
pr_info("packages.list detected: %d\n", mask);
if (ksu_uid_scanner_enabled) {
ksu_request_userspace_scan();
}
track_throne(false);
}
return 0;
}
static const struct fsnotify_ops ksu_ops = {
.handle_inode_event = ksu_handle_inode_event,
};
static int add_mark_on_inode(struct inode *inode, u32 mask,
struct fsnotify_mark **out)
{
struct fsnotify_mark *m;
m = kzalloc(sizeof(*m), GFP_KERNEL);
if (!m)
return -ENOMEM;
fsnotify_init_mark(m, g);
m->mask = mask;
if (fsnotify_add_inode_mark(m, inode, 0)) {
fsnotify_put_mark(m);
return -EINVAL;
}
*out = m;
return 0;
}
static int watch_one_dir(struct watch_dir *wd)
{
int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath);
if (ret) {
pr_info("path not ready: %s (%d)\n", wd->path, ret);
return ret;
}
wd->inode = d_inode(wd->kpath.dentry);
ihold(wd->inode);
ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark);
if (ret) {
pr_err("Add mark failed for %s (%d)\n", wd->path, ret);
path_put(&wd->kpath);
iput(wd->inode);
wd->inode = NULL;
return ret;
}
pr_info("watching %s\n", wd->path);
return 0;
}
static void unwatch_one_dir(struct watch_dir *wd)
{
if (wd->mark) {
fsnotify_destroy_mark(wd->mark, g);
fsnotify_put_mark(wd->mark);
wd->mark = NULL;
}
if (wd->inode) {
iput(wd->inode);
wd->inode = NULL;
}
if (wd->kpath.dentry) {
path_put(&wd->kpath);
memset(&wd->kpath, 0, sizeof(wd->kpath));
}
}
static struct watch_dir g_watch = { .path = "/data/system",
.mask = MASK_SYSTEM };
int ksu_observer_init(void)
{
int ret = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
g = fsnotify_alloc_group(&ksu_ops, 0);
#else
g = fsnotify_alloc_group(&ksu_ops);
#endif
if (IS_ERR(g))
return PTR_ERR(g);
ret = watch_one_dir(&g_watch);
pr_info("observer init done\n");
return 0;
}
void ksu_observer_exit(void)
{
unwatch_one_dir(&g_watch);
fsnotify_put_group(g);
pr_info("observer exit done\n");
}

69
kernel/seccomp_cache.c Normal file
View File

@@ -0,0 +1,69 @@
#include <linux/version.h>
#include <linux/fs.h>
#include <linux/nsproxy.h>
#include <linux/sched/task.h>
#include <linux/uaccess.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include "klog.h" // IWYU pragma: keep
#include "seccomp_cache.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 2) // Android backport this feature in 5.10.2
struct action_cache {
DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR);
#ifdef SECCOMP_ARCH_COMPAT
DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR);
#endif
};
struct seccomp_filter {
refcount_t refs;
refcount_t users;
bool log;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
bool wait_killable_recv;
#endif
struct action_cache cache;
struct seccomp_filter *prev;
struct bpf_prog *prog;
struct notification *notif;
struct mutex notify_lock;
wait_queue_head_t wqh;
};
void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr)
{
if (!filter) {
return;
}
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
clear_bit(nr, filter->cache.allow_native);
}
#ifdef SECCOMP_ARCH_COMPAT
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
clear_bit(nr, filter->cache.allow_compat);
}
#endif
}
void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr)
{
if (!filter) {
return;
}
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
set_bit(nr, filter->cache.allow_native);
}
#ifdef SECCOMP_ARCH_COMPAT
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
set_bit(nr, filter->cache.allow_compat);
}
#endif
}
#endif

12
kernel/seccomp_cache.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __KSU_H_SECCOMP_CACHE
#define __KSU_H_SECCOMP_CACHE
#include <linux/fs.h>
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 2) // Android backport this feature in 5.10.2
extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr);
extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr);
#endif
#endif

View File

@@ -2,15 +2,7 @@ obj-y += selinux.o
obj-y += sepolicy.o
obj-y += rules.o
ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID
endif
ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0)
ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE
endif
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion
ccflags-y += -Wno-strict-prototypes -Wno-int-conversion
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h

View File

@@ -6,7 +6,7 @@
#include "selinux.h"
#include "sepolicy.h"
#include "ss/services.h"
#include "linux/lsm_audit.h"
#include "linux/lsm_audit.h" // IWYU pragma: keep
#include "xfrm.h"
#define SELINUX_POLICY_INSTEAD_SELINUX_SS
@@ -18,119 +18,119 @@
static struct policydb *get_policydb(void)
{
struct policydb *db;
struct selinux_policy *policy = selinux_state.policy;
db = &policy->policydb;
return db;
struct policydb *db;
struct selinux_policy *policy = selinux_state.policy;
db = &policy->policydb;
return db;
}
static DEFINE_MUTEX(ksu_rules);
void apply_kernelsu_rules()
{
struct policydb *db;
struct policydb *db;
if (!getenforce()) {
pr_info("SELinux permissive or disabled, apply rules!\n");
}
if (!getenforce()) {
pr_info("SELinux permissive or disabled, apply rules!\n");
}
mutex_lock(&ksu_rules);
mutex_lock(&ksu_rules);
db = get_policydb();
db = get_policydb();
ksu_permissive(db, KERNEL_SU_DOMAIN);
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
ksu_permissive(db, KERNEL_SU_DOMAIN);
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
// Create unconstrained file type
ksu_type(db, KERNEL_SU_FILE, "file_type");
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
// Create unconstrained file type
ksu_type(db, KERNEL_SU_FILE, "file_type");
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
// allow all!
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
// allow all!
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
// allow us do any ioctl
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
}
// allow us do any ioctl
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
}
// we need to save allowlist in /data/adb/ksu
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
// we need to search /data/app
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
// we may need to do mount on shell
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
// we need to read /data/system/packages.list
ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
// Android 10+:
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
// Kernel 4.4
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
// Android 9-:
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
// our ksud triggered by init
ksu_allow(db, "init", "adb_data_file", "file", ALL);
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
// we need to umount modules in zygote
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
// we need to save allowlist in /data/adb/ksu
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
// we need to search /data/app
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
// we may need to do mount on shell
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
// we need to read /data/system/packages.list
ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
// Android 10+:
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
// Kernel 4.4
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
// Android 9-:
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
// our ksud triggered by init
ksu_allow(db, "init", "adb_data_file", "file", ALL);
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
// we need to umount modules in zygote
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
// copied from Magisk rules
// suRights
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
// copied from Magisk rules
// suRights
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
// allowLog
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
// allowLog
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
// dumpsys
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
// dumpsys
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
// bootctl
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
"getattr");
// bootctl
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
"getattr");
// For mounting loop devices, mirrors, tmpfs
ksu_allow(db, "kernel", ALL, "file", "read");
ksu_allow(db, "kernel", ALL, "file", "write");
// For mounting loop devices, mirrors, tmpfs
ksu_allow(db, "kernel", ALL, "file", "read");
ksu_allow(db, "kernel", ALL, "file", "write");
// Allow all binder transactions
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
// Allow all binder transactions
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
// Allow system server kill su process
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
mutex_unlock(&ksu_rules);
mutex_unlock(&ksu_rules);
}
#define MAX_SEPOL_LEN 128
@@ -145,401 +145,333 @@ void apply_kernelsu_rules()
#define CMD_TYPE_CHANGE 8
#define CMD_GENFSCON 9
#ifdef CONFIG_64BIT
struct sepol_data {
u32 cmd;
u32 subcmd;
u64 field_sepol1;
u64 field_sepol2;
u64 field_sepol3;
u64 field_sepol4;
u64 field_sepol5;
u64 field_sepol6;
u64 field_sepol7;
u32 cmd;
u32 subcmd;
char __user *sepol1;
char __user *sepol2;
char __user *sepol3;
char __user *sepol4;
char __user *sepol5;
char __user *sepol6;
char __user *sepol7;
};
#ifdef CONFIG_COMPAT
extern bool ksu_is_compat __read_mostly;
struct sepol_compat_data {
u32 cmd;
u32 subcmd;
u32 field_sepol1;
u32 field_sepol2;
u32 field_sepol3;
u32 field_sepol4;
u32 field_sepol5;
u32 field_sepol6;
u32 field_sepol7;
};
#endif // CONFIG_COMPAT
#else
struct sepol_data {
u32 cmd;
u32 subcmd;
u32 field_sepol1;
u32 field_sepol2;
u32 field_sepol3;
u32 field_sepol4;
u32 field_sepol5;
u32 field_sepol6;
u32 field_sepol7;
};
#endif // CONFIG_64BIT
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
char **object)
char **object)
{
if (!user_object) {
*object = ALL;
return 0;
}
if (!user_object) {
*object = ALL;
return 0;
}
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
return -1;
}
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
return -EINVAL;
}
*object = buf;
*object = buf;
return 0;
return 0;
}
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
extern int avc_ss_reset(u32 seqno);
#else
extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
#endif
// reset avc cache table, otherwise the new rules will not take effect if already denied
static void reset_avc_cache()
{
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
avc_ss_reset(0);
selnl_notify_policyload(0);
selinux_status_update_policyload(0);
avc_ss_reset(0);
selnl_notify_policyload(0);
selinux_status_update_policyload(0);
#else
struct selinux_avc *avc = selinux_state.avc;
avc_ss_reset(avc, 0);
selnl_notify_policyload(0);
selinux_status_update_policyload(&selinux_state, 0);
struct selinux_avc *avc = selinux_state.avc;
avc_ss_reset(avc, 0);
selnl_notify_policyload(0);
selinux_status_update_policyload(&selinux_state, 0);
#endif
selinux_xfrm_notify_policyload();
selinux_xfrm_notify_policyload();
}
int handle_sepolicy(unsigned long arg3, void __user *arg4)
{
struct policydb *db;
struct policydb *db;
if (!arg4) {
return -1;
}
if (!arg4) {
return -EINVAL;
}
if (!getenforce()) {
pr_info("SELinux permissive or disabled when handle policy!\n");
}
u32 cmd, subcmd;
char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7;
if (!getenforce()) {
pr_info("SELinux permissive or disabled when handle policy!\n");
}
#if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT)
if (unlikely(ksu_is_compat)) {
struct sepol_compat_data compat_data;
if (copy_from_user(&compat_data, arg4, sizeof(struct sepol_compat_data))) {
pr_err("sepol: copy sepol_data failed.\n");
return -1;
}
sepol1 = compat_ptr(compat_data.field_sepol1);
sepol2 = compat_ptr(compat_data.field_sepol2);
sepol3 = compat_ptr(compat_data.field_sepol3);
sepol4 = compat_ptr(compat_data.field_sepol4);
sepol5 = compat_ptr(compat_data.field_sepol5);
sepol6 = compat_ptr(compat_data.field_sepol6);
sepol7 = compat_ptr(compat_data.field_sepol7);
cmd = compat_data.cmd;
subcmd = compat_data.subcmd;
} else {
struct sepol_data data;
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
pr_err("sepol: copy sepol_data failed.\n");
return -1;
}
sepol1 = data.field_sepol1;
sepol2 = data.field_sepol2;
sepol3 = data.field_sepol3;
sepol4 = data.field_sepol4;
sepol5 = data.field_sepol5;
sepol6 = data.field_sepol6;
sepol7 = data.field_sepol7;
cmd = data.cmd;
subcmd = data.subcmd;
}
#else
// basically for full native, say (64BIT=y COMPAT=n) || (64BIT=n)
struct sepol_data data;
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
pr_err("sepol: copy sepol_data failed.\n");
return -1;
}
sepol1 = data.field_sepol1;
sepol2 = data.field_sepol2;
sepol3 = data.field_sepol3;
sepol4 = data.field_sepol4;
sepol5 = data.field_sepol5;
sepol6 = data.field_sepol6;
sepol7 = data.field_sepol7;
cmd = data.cmd;
subcmd = data.subcmd;
#endif
struct sepol_data data;
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
pr_err("sepol: copy sepol_data failed.\n");
return -EINVAL;
}
mutex_lock(&ksu_rules);
u32 cmd = data.cmd;
u32 subcmd = data.subcmd;
db = get_policydb();
mutex_lock(&ksu_rules);
int ret = -1;
if (cmd == CMD_NORMAL_PERM) {
char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN];
char cls_buf[MAX_SEPOL_LEN];
char perm_buf[MAX_SEPOL_LEN];
db = get_policydb();
char *s, *t, *c, *p;
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
int ret = -EINVAL;
if (cmd == CMD_NORMAL_PERM) {
char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN];
char cls_buf[MAX_SEPOL_LEN];
char perm_buf[MAX_SEPOL_LEN];
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
char *s, *t, *c, *p;
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) <
0) {
pr_err("sepol: copy perm failed.\n");
goto exit;
}
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
bool success = false;
if (subcmd == 1) {
success = ksu_allow(db, s, t, c, p);
} else if (subcmd == 2) {
success = ksu_deny(db, s, t, c, p);
} else if (subcmd == 3) {
success = ksu_auditallow(db, s, t, c, p);
} else if (subcmd == 4) {
success = ksu_dontaudit(db, s, t, c, p);
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
ret = success ? 0 : -1;
if (get_object(perm_buf, data.sepol4, sizeof(perm_buf), &p) <
0) {
pr_err("sepol: copy perm failed.\n");
goto exit;
}
} else if (cmd == CMD_XPERM) {
char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN];
char cls_buf[MAX_SEPOL_LEN];
bool success = false;
if (subcmd == 1) {
success = ksu_allow(db, s, t, c, p);
} else if (subcmd == 2) {
success = ksu_deny(db, s, t, c, p);
} else if (subcmd == 3) {
success = ksu_auditallow(db, s, t, c, p);
} else if (subcmd == 4) {
success = ksu_dontaudit(db, s, t, c, p);
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
ret = success ? 0 : -EINVAL;
char __maybe_unused
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
char perm_set[MAX_SEPOL_LEN];
} else if (cmd == CMD_XPERM) {
char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN];
char cls_buf[MAX_SEPOL_LEN];
char *s, *t, *c;
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(operation, sepol4,
sizeof(operation)) < 0) {
pr_err("sepol: copy operation failed.\n");
goto exit;
}
if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) <
0) {
pr_err("sepol: copy perm_set failed.\n");
goto exit;
}
char __maybe_unused
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
char perm_set[MAX_SEPOL_LEN];
bool success = false;
if (subcmd == 1) {
success = ksu_allowxperm(db, s, t, c, perm_set);
} else if (subcmd == 2) {
success = ksu_auditallowxperm(db, s, t, c, perm_set);
} else if (subcmd == 3) {
success = ksu_dontauditxperm(db, s, t, c, perm_set);
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
ret = success ? 0 : -1;
} else if (cmd == CMD_TYPE_STATE) {
char src[MAX_SEPOL_LEN];
char *s, *t, *c;
if (get_object(src_buf, data.sepol1, sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (get_object(tgt_buf, data.sepol2, sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (get_object(cls_buf, data.sepol3, sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(operation, data.sepol4,
sizeof(operation)) < 0) {
pr_err("sepol: copy operation failed.\n");
goto exit;
}
if (strncpy_from_user(perm_set, data.sepol5, sizeof(perm_set)) <
0) {
pr_err("sepol: copy perm_set failed.\n");
goto exit;
}
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
bool success = false;
if (subcmd == 1) {
success = ksu_allowxperm(db, s, t, c, perm_set);
} else if (subcmd == 2) {
success = ksu_auditallowxperm(db, s, t, c, perm_set);
} else if (subcmd == 3) {
success = ksu_dontauditxperm(db, s, t, c, perm_set);
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
ret = success ? 0 : -EINVAL;
} else if (cmd == CMD_TYPE_STATE) {
char src[MAX_SEPOL_LEN];
bool success = false;
if (subcmd == 1) {
success = ksu_permissive(db, src);
} else if (subcmd == 2) {
success = ksu_enforce(db, src);
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
if (success)
ret = 0;
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
char type[MAX_SEPOL_LEN];
char attr[MAX_SEPOL_LEN];
bool success = false;
if (subcmd == 1) {
success = ksu_permissive(db, src);
} else if (subcmd == 2) {
success = ksu_enforce(db, src);
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
if (success)
ret = 0;
if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) {
pr_err("sepol: copy type failed.\n");
goto exit;
}
if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n");
goto exit;
}
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
char type[MAX_SEPOL_LEN];
char attr[MAX_SEPOL_LEN];
bool success = false;
if (cmd == CMD_TYPE) {
success = ksu_type(db, type, attr);
} else {
success = ksu_typeattribute(db, type, attr);
}
if (!success) {
pr_err("sepol: %d failed.\n", cmd);
goto exit;
}
ret = 0;
if (strncpy_from_user(type, data.sepol1, sizeof(type)) < 0) {
pr_err("sepol: copy type failed.\n");
goto exit;
}
if (strncpy_from_user(attr, data.sepol2, sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n");
goto exit;
}
} else if (cmd == CMD_ATTR) {
char attr[MAX_SEPOL_LEN];
bool success = false;
if (cmd == CMD_TYPE) {
success = ksu_type(db, type, attr);
} else {
success = ksu_typeattribute(db, type, attr);
}
if (!success) {
pr_err("sepol: %d failed.\n", cmd);
goto exit;
}
ret = 0;
if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n");
goto exit;
}
if (!ksu_attribute(db, attr)) {
pr_err("sepol: %d failed.\n", cmd);
goto exit;
}
ret = 0;
} else if (cmd == CMD_ATTR) {
char attr[MAX_SEPOL_LEN];
} else if (cmd == CMD_TYPE_TRANSITION) {
char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN];
char default_type[MAX_SEPOL_LEN];
char object[MAX_SEPOL_LEN];
if (strncpy_from_user(attr, data.sepol1, sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n");
goto exit;
}
if (!ksu_attribute(db, attr)) {
pr_err("sepol: %d failed.\n", cmd);
goto exit;
}
ret = 0;
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(default_type, sepol4,
sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n");
goto exit;
}
char *real_object;
if (sepol5 == NULL) {
real_object = NULL;
} else {
if (strncpy_from_user(object, sepol5,
sizeof(object)) < 0) {
pr_err("sepol: copy object failed.\n");
goto exit;
}
real_object = object;
}
} else if (cmd == CMD_TYPE_TRANSITION) {
char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN];
char default_type[MAX_SEPOL_LEN];
char object[MAX_SEPOL_LEN];
bool success = ksu_type_transition(db, src, tgt, cls,
default_type, real_object);
if (success)
ret = 0;
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(default_type, data.sepol4,
sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n");
goto exit;
}
char *real_object;
if (data.sepol5 == NULL) {
real_object = NULL;
} else {
if (strncpy_from_user(object, data.sepol5,
sizeof(object)) < 0) {
pr_err("sepol: copy object failed.\n");
goto exit;
}
real_object = object;
}
} else if (cmd == CMD_TYPE_CHANGE) {
char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN];
char default_type[MAX_SEPOL_LEN];
bool success = ksu_type_transition(db, src, tgt, cls,
default_type, real_object);
if (success)
ret = 0;
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(default_type, sepol4,
sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n");
goto exit;
}
bool success = false;
if (subcmd == 1) {
success = ksu_type_change(db, src, tgt, cls,
default_type);
} else if (subcmd == 2) {
success = ksu_type_member(db, src, tgt, cls,
default_type);
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
if (success)
ret = 0;
} else if (cmd == CMD_GENFSCON) {
char name[MAX_SEPOL_LEN];
char path[MAX_SEPOL_LEN];
char context[MAX_SEPOL_LEN];
if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) {
pr_err("sepol: copy name failed.\n");
goto exit;
}
if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) {
pr_err("sepol: copy path failed.\n");
goto exit;
}
if (strncpy_from_user(context, sepol3, sizeof(context)) <
0) {
pr_err("sepol: copy context failed.\n");
goto exit;
}
} else if (cmd == CMD_TYPE_CHANGE) {
char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN];
char default_type[MAX_SEPOL_LEN];
if (!ksu_genfscon(db, name, path, context)) {
pr_err("sepol: %d failed.\n", cmd);
goto exit;
}
ret = 0;
} else {
pr_err("sepol: unknown cmd: %d\n", cmd);
}
if (strncpy_from_user(src, data.sepol1, sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n");
goto exit;
}
if (strncpy_from_user(tgt, data.sepol2, sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n");
goto exit;
}
if (strncpy_from_user(cls, data.sepol3, sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n");
goto exit;
}
if (strncpy_from_user(default_type, data.sepol4,
sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n");
goto exit;
}
bool success = false;
if (subcmd == 1) {
success = ksu_type_change(db, src, tgt, cls,
default_type);
} else if (subcmd == 2) {
success = ksu_type_member(db, src, tgt, cls,
default_type);
} else {
pr_err("sepol: unknown subcmd: %d\n", subcmd);
}
if (success)
ret = 0;
} else if (cmd == CMD_GENFSCON) {
char name[MAX_SEPOL_LEN];
char path[MAX_SEPOL_LEN];
char context[MAX_SEPOL_LEN];
if (strncpy_from_user(name, data.sepol1, sizeof(name)) < 0) {
pr_err("sepol: copy name failed.\n");
goto exit;
}
if (strncpy_from_user(path, data.sepol2, sizeof(path)) < 0) {
pr_err("sepol: copy path failed.\n");
goto exit;
}
if (strncpy_from_user(context, data.sepol3, sizeof(context)) <
0) {
pr_err("sepol: copy context failed.\n");
goto exit;
}
if (!ksu_genfscon(db, name, path, context)) {
pr_err("sepol: %d failed.\n", cmd);
goto exit;
}
ret = 0;
} else {
pr_err("sepol: unknown cmd: %d\n", cmd);
}
exit:
mutex_unlock(&ksu_rules);
mutex_unlock(&ksu_rules);
// only allow and xallow needs to reset avc cache, but we cannot do that because
// we are in atomic context. so we just reset it every time.
reset_avc_cache();
// only allow and xallow needs to reset avc cache, but we cannot do that because
// we are in atomic context. so we just reset it every time.
reset_avc_cache();
return ret;
}
return ret;
}

View File

@@ -1,4 +1,6 @@
#include "selinux.h"
#include "linux/cred.h"
#include "linux/sched.h"
#include "objsec.h"
#include "linux/version.h"
#include "../klog.h" // IWYU pragma: keep
@@ -7,124 +9,146 @@
static int transive_to_domain(const char *domain)
{
struct cred *cred;
struct task_security_struct *tsec;
u32 sid;
int error;
struct cred *cred;
struct task_security_struct *tsec;
u32 sid;
int error;
cred = (struct cred *)__task_cred(current);
cred = (struct cred *)__task_cred(current);
tsec = cred->security;
if (!tsec) {
pr_err("tsec == NULL!\n");
return -1;
}
tsec = cred->security;
if (!tsec) {
pr_err("tsec == NULL!\n");
return -1;
}
error = security_secctx_to_secid(domain, strlen(domain), &sid);
if (error) {
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
domain, sid, error);
}
if (!error) {
tsec->sid = sid;
tsec->create_sid = 0;
tsec->keycreate_sid = 0;
tsec->sockcreate_sid = 0;
}
return error;
error = security_secctx_to_secid(domain, strlen(domain), &sid);
if (error) {
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
domain, sid, error);
}
if (!error) {
tsec->sid = sid;
tsec->create_sid = 0;
tsec->keycreate_sid = 0;
tsec->sockcreate_sid = 0;
}
return error;
}
void setup_selinux(const char *domain)
{
if (transive_to_domain(domain)) {
pr_err("transive domain failed.\n");
return;
}
/* we didn't need this now, we have change selinux rules when boot!
if (!is_domain_permissive) {
if (set_domain_permissive() == 0) {
is_domain_permissive = true;
}
}*/
if (transive_to_domain(domain)) {
pr_err("transive domain failed.\n");
return;
}
}
void setenforce(bool enforce)
{
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
selinux_state.enforcing = enforce;
selinux_state.enforcing = enforce;
#endif
}
bool getenforce()
{
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
if (selinux_state.disabled) {
return false;
}
if (selinux_state.disabled) {
return false;
}
#endif
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
return selinux_state.enforcing;
return selinux_state.enforcing;
#else
return true;
return true;
#endif
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
!defined(KSU_COMPAT_HAS_CURRENT_SID)
/*
* get the subjective security ID of the current task
*/
static inline u32 current_sid(void)
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0)
struct lsm_context {
char *context;
u32 len;
};
static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp)
{
const struct task_security_struct *tsec = current_security();
return tsec->sid;
return security_secid_to_secctx(secid, &cp->context, &cp->len);
}
static void __security_release_secctx(struct lsm_context *cp)
{
return security_release_secctx(cp->context, cp->len);
}
#else
#define __security_secid_to_secctx security_secid_to_secctx
#define __security_release_secctx security_release_secctx
#endif
bool is_task_ksu_domain(const struct cred* cred)
{
struct lsm_context ctx;
bool result;
if (!cred) {
return false;
}
const struct task_security_struct *tsec = selinux_cred(cred);
if (!tsec) {
return false;
}
int err = __security_secid_to_secctx(tsec->sid, &ctx);
if (err) {
return false;
}
result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
__security_release_secctx(&ctx);
return result;
}
bool is_ksu_domain()
{
char *domain;
u32 seclen;
bool result;
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
if (err) {
return false;
}
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
security_release_secctx(domain, seclen);
return result;
current_sid();
return is_task_ksu_domain(current_cred());
}
bool is_zygote(void *sec)
bool is_context(const struct cred* cred, const char* context)
{
struct task_security_struct *tsec = (struct task_security_struct *)sec;
if (!tsec) {
return false;
}
char *domain;
u32 seclen;
bool result;
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
if (err) {
return false;
}
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
security_release_secctx(domain, seclen);
return result;
if (!cred) {
return false;
}
const struct task_security_struct * tsec = selinux_cred(cred);
if (!tsec) {
return false;
}
struct lsm_context ctx;
bool result;
int err = __security_secid_to_secctx(tsec->sid, &ctx);
if (err) {
return false;
}
result = strncmp(context, ctx.context, ctx.len) == 0;
__security_release_secctx(&ctx);
return result;
}
#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0"
u32 ksu_get_devpts_sid()
bool is_zygote(const struct cred* cred)
{
u32 devpts_sid = 0;
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
&devpts_sid);
if (err) {
pr_info("get devpts sid err %d\n", err);
}
return devpts_sid;
return is_context(cred, "u:r:zygote:s0");
}
bool is_init(const struct cred* cred) {
return is_context(cred, "u:r:init:s0");
}
#define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0"
u32 ksu_get_ksu_file_sid()
{
u32 ksu_file_sid = 0;
int err = security_secctx_to_secid(KSU_FILE_DOMAIN, strlen(KSU_FILE_DOMAIN),
&ksu_file_sid);
if (err) {
pr_info("get ksufile sid err %d\n", err);
}
return ksu_file_sid;
}

View File

@@ -3,6 +3,7 @@
#include "linux/types.h"
#include "linux/version.h"
#include "linux/cred.h"
void setup_selinux(const char *);
@@ -10,12 +11,18 @@ void setenforce(bool);
bool getenforce();
bool is_task_ksu_domain(const struct cred* cred);
bool is_ksu_domain();
bool is_zygote(void *cred);
bool is_zygote(const struct cred* cred);
bool is_init(const struct cred* cred);
void apply_kernelsu_rules();
u32 ksu_get_devpts_sid();
u32 ksu_get_ksu_file_sid();
int handle_sepolicy(unsigned long arg3, void __user *arg4);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -15,32 +15,32 @@ bool ksu_exists(struct policydb *db, const char *type);
// Access vector rules
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm);
const char *cls, const char *perm);
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm);
const char *cls, const char *perm);
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm);
const char *cls, const char *perm);
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm);
const char *cls, const char *perm);
// Extended permissions access vector rules
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range);
const char *cls, const char *range);
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range);
const char *cls, const char *range);
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range);
const char *cls, const char *range);
// Type rules
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def, const char *obj);
const char *cls, const char *def, const char *obj);
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def);
const char *cls, const char *def);
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def);
const char *cls, const char *def);
// File system labeling
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
const char *ctx);
const char *ctx);
#endif

171
kernel/setuid_hook.c Normal file
View File

@@ -0,0 +1,171 @@
#include <linux/compiler.h>
#include <linux/sched/signal.h>
#include <linux/slab.h>
#include <linux/task_work.h>
#include <linux/thread_info.h>
#include <linux/seccomp.h>
#include <linux/bpf.h>
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/dcache.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/init_task.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/mm.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/nsproxy.h>
#include <linux/path.h>
#include <linux/printk.h>
#include <linux/sched.h>
#include <linux/security.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/uidgid.h>
#include <linux/version.h>
#include <linux/binfmts.h>
#include <linux/tty.h>
#include "allowlist.h"
#include "setuid_hook.h"
#include "feature.h"
#include "klog.h" // IWYU pragma: keep
#include "manager.h"
#include "selinux/selinux.h"
#include "seccomp_cache.h"
#include "supercalls.h"
#include "syscall_hook_manager.h"
#include "kernel_umount.h"
#include "app_profile.h"
static bool ksu_enhanced_security_enabled = false;
static int enhanced_security_feature_get(u64 *value)
{
*value = ksu_enhanced_security_enabled ? 1 : 0;
return 0;
}
static int enhanced_security_feature_set(u64 value)
{
bool enable = value != 0;
ksu_enhanced_security_enabled = enable;
pr_info("enhanced_security: set to %d\n", enable);
return 0;
}
static const struct ksu_feature_handler enhanced_security_handler = {
.feature_id = KSU_FEATURE_ENHANCED_SECURITY,
.name = "enhanced_security",
.get_handler = enhanced_security_feature_get,
.set_handler = enhanced_security_feature_set,
};
static inline bool is_allow_su()
{
if (is_manager()) {
// we are manager, allow!
return true;
}
return ksu_is_allow_uid_for_current(current_uid().val);
}
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
{
uid_t new_uid = ruid;
uid_t old_uid = current_uid().val;
pr_info("handle_setresuid from %d to %d\n", old_uid, new_uid);
// if old process is root, ignore it.
if (old_uid != 0 && ksu_enhanced_security_enabled) {
// disallow any non-ksu domain escalation from non-root to root!
// euid is what we care about here as it controls permission
if (unlikely(euid == 0)) {
if (!is_ksu_domain()) {
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
current->pid, current->comm, old_uid, new_uid);
force_sig(SIGKILL);
return 0;
}
}
// disallow appuid decrease to any other uid if it is not allowed to su
if (is_appuid(old_uid)) {
if (euid < current_euid().val && !ksu_is_allow_uid_for_current(old_uid)) {
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
current->pid, current->comm, old_uid, new_uid);
force_sig(SIGKILL);
return 0;
}
}
return 0;
}
// if on private space, see if its possibly the manager
if (new_uid > PER_USER_RANGE && new_uid % PER_USER_RANGE == ksu_get_manager_uid()) {
ksu_set_manager_uid(new_uid);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
if (ksu_get_manager_uid() == new_uid) {
pr_info("install fd for manager: %d\n", new_uid);
ksu_install_fd();
spin_lock_irq(&current->sighand->siglock);
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
ksu_set_task_tracepoint_flag(current);
spin_unlock_irq(&current->sighand->siglock);
return 0;
}
if (ksu_is_allow_uid_for_current(new_uid)) {
if (current->seccomp.mode == SECCOMP_MODE_FILTER &&
current->seccomp.filter) {
spin_lock_irq(&current->sighand->siglock);
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
spin_unlock_irq(&current->sighand->siglock);
}
ksu_set_task_tracepoint_flag(current);
} else {
ksu_clear_task_tracepoint_flag_if_needed(current);
}
#else
if (ksu_is_allow_uid_for_current(new_uid)) {
spin_lock_irq(&current->sighand->siglock);
disable_seccomp();
spin_unlock_irq(&current->sighand->siglock);
if (ksu_get_manager_uid() == new_uid) {
pr_info("install fd for ksu manager(uid=%d)\n",
new_uid);
ksu_install_fd();
}
return 0;
}
#endif
// Handle kernel umount
ksu_handle_umount(old_uid, new_uid);
return 0;
}
void ksu_setuid_hook_init(void)
{
ksu_kernel_umount_init();
if (ksu_register_feature_handler(&enhanced_security_handler)) {
pr_err("Failed to register enhanced security feature handler\n");
}
}
void ksu_setuid_hook_exit(void)
{
pr_info("ksu_core_exit\n");
ksu_kernel_umount_exit();
ksu_unregister_feature_handler(KSU_FEATURE_ENHANCED_SECURITY);
}

14
kernel/setuid_hook.h Normal file
View File

@@ -0,0 +1,14 @@
#ifndef __KSU_H_KSU_CORE
#define __KSU_H_KSU_CORE
#include <linux/init.h>
#include <linux/types.h>
#include "apk_sign.h"
#include <linux/thread_info.h>
void ksu_setuid_hook_init(void);
void ksu_setuid_hook_exit(void);
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid);
#endif

View File

@@ -1,337 +1,198 @@
#include <linux/dcache.h>
#include <linux/security.h>
#include <linux/compiler_types.h>
#include <linux/preempt.h>
#include <linux/printk.h>
#include <linux/mm.h>
#include <linux/pgtable.h>
#include <linux/uaccess.h>
#include <asm/current.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/kprobes.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/sched/task_stack.h>
#include <linux/ptrace.h>
#include "objsec.h"
#include "allowlist.h"
#include "arch.h"
#include "feature.h"
#include "klog.h" // IWYU pragma: keep
#include "ksud.h"
#include "kernel_compat.h"
#include "sucompat.h"
#include "app_profile.h"
#include "util.h"
#include "sulog.h"
#define SU_PATH "/system/bin/su"
#define SH_PATH "/system/bin/sh"
extern void escape_to_root();
bool ksu_su_compat_enabled __read_mostly = true;
#ifndef CONFIG_KSU_KPROBES_HOOK
static bool ksu_sucompat_hook_state __read_mostly = true;
#endif
static int su_compat_feature_get(u64 *value)
{
*value = ksu_su_compat_enabled ? 1 : 0;
return 0;
}
static int su_compat_feature_set(u64 value)
{
bool enable = value != 0;
ksu_su_compat_enabled = enable;
pr_info("su_compat: set to %d\n", enable);
return 0;
}
static const struct ksu_feature_handler su_compat_handler = {
.feature_id = KSU_FEATURE_SU_COMPAT,
.name = "su_compat",
.get_handler = su_compat_feature_get,
.set_handler = su_compat_feature_set,
};
static void __user *userspace_stack_buffer(const void *d, size_t len)
{
/* To avoid having to mmap a page in userspace, just write below the stack
* pointer. */
char __user *p = (void __user *)current_user_stack_pointer() - len;
// To avoid having to mmap a page in userspace, just write below the stack
// pointer.
char __user *p = (void __user *)current_user_stack_pointer() - len;
return copy_to_user(p, d, len) ? NULL : p;
return copy_to_user(p, d, len) ? NULL : p;
}
static char __user *sh_user_path(void)
{
static const char sh_path[] = "/system/bin/sh";
static const char sh_path[] = "/system/bin/sh";
return userspace_stack_buffer(sh_path, sizeof(sh_path));
return userspace_stack_buffer(sh_path, sizeof(sh_path));
}
static char __user *ksud_user_path(void)
{
static const char ksud_path[] = KSUD_PATH;
static const char ksud_path[] = KSUD_PATH;
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
}
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
int *__unused_flags)
int *__unused_flags)
{
const char su[] = SU_PATH;
const char su[] = SU_PATH;
#ifndef CONFIG_KSU_KPROBES_HOOK
if (!ksu_sucompat_hook_state) {
return 0;
}
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
return 0;
}
char path[sizeof(su) + 1];
memset(path, 0, sizeof(path));
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (unlikely(!memcmp(path, su, sizeof(su)))) {
#if __SULOG_GATE
ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path);
#endif
pr_info("faccessat su->sh!\n");
*filename_user = sh_user_path();
}
if (!ksu_is_allow_uid(current_uid().val)) {
return 0;
}
char path[sizeof(su) + 1];
memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (unlikely(!memcmp(path, su, sizeof(su)))) {
pr_info("faccessat su->sh!\n");
*filename_user = sh_user_path();
}
return 0;
return 0;
}
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
{
// const char sh[] = SH_PATH;
const char su[] = SU_PATH;
// const char sh[] = SH_PATH;
const char su[] = SU_PATH;
#ifndef CONFIG_KSU_KPROBES_HOOK
if (!ksu_sucompat_hook_state) {
return 0;
}
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
return 0;
}
if (unlikely(!filename_user)) {
return 0;
}
char path[sizeof(su) + 1];
memset(path, 0, sizeof(path));
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (unlikely(!memcmp(path, su, sizeof(su)))) {
#if __SULOG_GATE
ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path);
#endif
if (!ksu_is_allow_uid(current_uid().val)) {
return 0;
}
pr_info("newfstatat su->sh!\n");
*filename_user = sh_user_path();
}
if (unlikely(!filename_user)) {
return 0;
}
return 0;
}
char path[sizeof(su) + 1];
memset(path, 0, sizeof(path));
// Remove this later!! we use syscall hook, so this will never happen!!!!!
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
// it becomes a `struct filename *` after 5.18
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
const char sh[] = SH_PATH;
struct filename *filename = *((struct filename **)filename_user);
if (IS_ERR(filename)) {
return 0;
}
if (likely(memcmp(filename->name, su, sizeof(su))))
return 0;
pr_info("vfs_statx su->sh!\n");
memcpy((void *)filename->name, sh, sizeof(sh));
int ksu_handle_execve_sucompat(const char __user **filename_user,
void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags)
{
const char su[] = SU_PATH;
const char __user *fn;
char path[sizeof(su) + 1];
long ret;
unsigned long addr;
if (unlikely(!filename_user))
return 0;
#if __SULOG_GATE
bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val);
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path);
if (!is_allowed)
return 0;
ksu_sulog_report_su_attempt(current_uid().val, NULL, path, is_allowed);
#else
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (unlikely(!memcmp(path, su, sizeof(su)))) {
pr_info("newfstatat su->sh!\n");
*filename_user = sh_user_path();
}
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
return 0;
}
#endif
return 0;
addr = untagged_addr((unsigned long)*filename_user);
fn = (const char __user *)addr;
memset(path, 0, sizeof(path));
ret = strncpy_from_user_nofault(path, fn, sizeof(path));
if (ret < 0 && try_set_access_flag(addr)) {
ret = strncpy_from_user_nofault(path, fn, sizeof(path));
}
if (ret < 0 && preempt_count()) {
/* This is crazy, but we know what we are doing:
* Temporarily exit atomic context to handle page faults, then restore it */
pr_info("Access filename failed, try rescue..\n");
preempt_enable_no_resched_notrace();
ret = strncpy_from_user(path, fn, sizeof(path));
preempt_disable_notrace();
}
if (ret < 0) {
pr_warn("Access filename when execve failed: %ld", ret);
return 0;
}
if (likely(memcmp(path, su, sizeof(su))))
return 0;
pr_info("sys_execve su found\n");
*filename_user = ksud_user_path();
escape_with_root_profile();
return 0;
}
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
void *envp, int *flags)
{
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
}
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags)
{
struct filename *filename;
const char sh[] = KSUD_PATH;
const char su[] = SU_PATH;
#ifndef CONFIG_KSU_KPROBES_HOOK
if (!ksu_sucompat_hook_state) {
return 0;
}
#endif
if (unlikely(!filename_ptr))
return 0;
filename = *filename_ptr;
if (IS_ERR(filename)) {
return 0;
}
if (likely(memcmp(filename->name, su, sizeof(su))))
return 0;
if (!ksu_is_allow_uid(current_uid().val))
return 0;
pr_info("do_execveat_common su found\n");
memcpy((void *)filename->name, sh, sizeof(sh));
escape_to_root();
return 0;
}
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags)
{
const char su[] = SU_PATH;
char path[sizeof(su) + 1];
#ifndef CONFIG_KSU_KPROBES_HOOK
if (!ksu_sucompat_hook_state){
return 0;
}
#endif
if (unlikely(!filename_user))
return 0;
memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (likely(memcmp(path, su, sizeof(su))))
return 0;
if (!ksu_is_allow_uid(current_uid().val))
return 0;
pr_info("sys_execve su found\n");
*filename_user = ksud_user_path();
escape_to_root();
return 0;
}
// dummified
int ksu_handle_devpts(struct inode *inode)
{
return 0;
}
int __ksu_handle_devpts(struct inode *inode)
{
#ifndef CONFIG_KSU_KPROBES_HOOK
if (!ksu_sucompat_hook_state)
return 0;
#endif
if (!current->mm) {
return 0;
}
uid_t uid = current_uid().val;
if (uid % 100000 < 10000) {
// not untrusted_app, ignore it
return 0;
}
if (likely(!ksu_is_allow_uid(uid)))
return 0;
struct inode_security_struct *sec = selinux_inode(inode);
if (ksu_devpts_sid && sec)
sec->sid = ksu_devpts_sid;
return 0;
}
#ifdef CONFIG_KSU_KPROBES_HOOK
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct pt_regs *real_regs = PT_REAL_REGS(regs);
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
const char __user **filename_user =
(const char **)&PT_REGS_PARM2(real_regs);
int *mode = (int *)&PT_REGS_PARM3(real_regs);
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
}
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct pt_regs *real_regs = PT_REAL_REGS(regs);
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
const char __user **filename_user =
(const char **)&PT_REGS_PARM2(real_regs);
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
return ksu_handle_stat(dfd, filename_user, flags);
}
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct pt_regs *real_regs = PT_REAL_REGS(regs);
const char __user **filename_user =
(const char **)&PT_REGS_PARM1(real_regs);
return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
NULL);
}
static struct kprobe *su_kps[4];
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
{
struct inode *inode;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
struct file *file = (struct file *)PT_REGS_PARM2(regs);
inode = file->f_path.dentry->d_inode;
#else
inode = (struct inode *)PT_REGS_PARM2(regs);
#endif
return ksu_handle_devpts(inode);
}
static struct kprobe *init_kprobe(const char *name,
kprobe_pre_handler_t handler)
{
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
if (!kp)
return NULL;
kp->symbol_name = name;
kp->pre_handler = handler;
int ret = register_kprobe(kp);
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
if (ret) {
kfree(kp);
return NULL;
}
return kp;
}
static void destroy_kprobe(struct kprobe **kp_ptr)
{
struct kprobe *kp = *kp_ptr;
if (!kp)
return;
unregister_kprobe(kp);
synchronize_rcu();
kfree(kp);
*kp_ptr = NULL;
}
#endif
// sucompat: permited process can execute 'su' to gain root access.
// sucompat: permitted process can execute 'su' to gain root access.
void ksu_sucompat_init()
{
#ifdef CONFIG_KSU_KPROBES_HOOK
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
#else
ksu_sucompat_hook_state = true;
pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat\n");
#endif
if (ksu_register_feature_handler(&su_compat_handler)) {
pr_err("Failed to register su_compat feature handler\n");
}
}
void ksu_sucompat_exit()
{
#ifdef CONFIG_KSU_KPROBES_HOOK
int i;
for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
destroy_kprobe(&su_kps[i]);
}
#else
ksu_sucompat_hook_state = false;
pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n");
#endif
}
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
}

18
kernel/sucompat.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef __KSU_H_SUCOMPAT
#define __KSU_H_SUCOMPAT
#include <linux/types.h>
extern bool ksu_su_compat_enabled;
void ksu_sucompat_init(void);
void ksu_sucompat_exit(void);
// Handler functions exported for hook_manager
int ksu_handle_faccessat(int *dfd, const char __user **filename_user,
int *mode, int *__unused_flags);
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
int ksu_handle_execve_sucompat(const char __user **filename_user,
void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags);
#endif

369
kernel/sulog.c Normal file
View File

@@ -0,0 +1,369 @@
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/workqueue.h>
#include <linux/cred.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include "klog.h"
#include "sulog.h"
#include "ksu.h"
#include "feature.h"
#if __SULOG_GATE
struct dedup_entry dedup_tbl[SULOG_COMM_LEN];
static DEFINE_SPINLOCK(dedup_lock);
static LIST_HEAD(sulog_queue);
static struct workqueue_struct *sulog_workqueue;
static struct work_struct sulog_work;
static bool sulog_enabled __read_mostly = true;
static int sulog_feature_get(u64 *value)
{
*value = sulog_enabled ? 1 : 0;
return 0;
}
static int sulog_feature_set(u64 value)
{
bool enable = value != 0;
sulog_enabled = enable;
pr_info("sulog: set to %d\n", enable);
return 0;
}
static const struct ksu_feature_handler sulog_handler = {
.feature_id = KSU_FEATURE_SULOG,
.name = "sulog",
.get_handler = sulog_feature_get,
.set_handler = sulog_feature_set,
};
static void get_timestamp(char *buf, size_t len)
{
struct timespec64 ts;
struct tm tm;
ktime_get_real_ts64(&ts);
time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm);
snprintf(buf, len, "%04ld-%02d-%02d %02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
static void ksu_get_cmdline(char *full_comm, const char *comm, size_t buf_len)
{
if (!full_comm || buf_len <= 0)
return;
if (comm && strlen(comm) > 0) {
KSU_STRSCPY(full_comm, comm, buf_len);
return;
}
if (in_atomic() || in_interrupt() || irqs_disabled()) {
KSU_STRSCPY(full_comm, current->comm, buf_len);
return;
}
if (!current->mm) {
KSU_STRSCPY(full_comm, current->comm, buf_len);
return;
}
int n = get_cmdline(current, full_comm, buf_len);
if (n <= 0) {
KSU_STRSCPY(full_comm, current->comm, buf_len);
return;
}
for (int i = 0; i < n && i < buf_len - 1; i++) {
if (full_comm[i] == '\0')
full_comm[i] = ' ';
}
full_comm[n < buf_len ? n : buf_len - 1] = '\0';
}
static void sanitize_string(char *str, size_t len)
{
if (!str || len == 0)
return;
size_t read_pos = 0, write_pos = 0;
while (read_pos < len && str[read_pos] != '\0') {
char c = str[read_pos];
if (c == '\n' || c == '\r') {
read_pos++;
continue;
}
if (c == ' ' && write_pos > 0 && str[write_pos - 1] == ' ') {
read_pos++;
continue;
}
str[write_pos++] = c;
read_pos++;
}
str[write_pos] = '\0';
}
static bool dedup_should_print(uid_t uid, u8 type, const char *content, size_t len)
{
struct dedup_key key = {
.crc = dedup_calc_hash(content, len),
.uid = uid,
.type = type,
};
u64 now = ktime_get_ns();
u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC;
u32 idx = key.crc & (SULOG_COMM_LEN - 1);
spin_lock(&dedup_lock);
struct dedup_entry *e = &dedup_tbl[idx];
if (e->key.crc == key.crc &&
e->key.uid == key.uid &&
e->key.type == key.type &&
(now - e->ts_ns) < delta_ns) {
spin_unlock(&dedup_lock);
return false;
}
e->key = key;
e->ts_ns = now;
spin_unlock(&dedup_lock);
return true;
}
static void sulog_work_handler(struct work_struct *work)
{
struct file *fp;
struct sulog_entry *entry, *tmp;
LIST_HEAD(local_queue);
loff_t pos = 0;
unsigned long flags;
spin_lock_irqsave(&dedup_lock, flags);
list_splice_init(&sulog_queue, &local_queue);
spin_unlock_irqrestore(&dedup_lock, flags);
if (list_empty(&local_queue))
return;
fp = filp_open(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640);
if (IS_ERR(fp)) {
pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp));
goto cleanup;
}
if (fp->f_inode->i_size > SULOG_MAX_SIZE) {
if (vfs_truncate(&fp->f_path, 0))
pr_err("sulog: failed to truncate log file\n");
pos = 0;
} else {
pos = fp->f_inode->i_size;
}
list_for_each_entry(entry, &local_queue, list)
kernel_write(fp, entry->content, strlen(entry->content), &pos);
vfs_fsync(fp, 0);
filp_close(fp, 0);
cleanup:
list_for_each_entry_safe(entry, tmp, &local_queue, list) {
list_del(&entry->list);
kfree(entry);
}
}
static void sulog_add_entry(char *log_buf, size_t len, uid_t uid, u8 dedup_type)
{
struct sulog_entry *entry;
unsigned long flags;
if (!sulog_enabled || !log_buf || len == 0)
return;
if (!dedup_should_print(uid, dedup_type, log_buf, len))
return;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry)
return;
KSU_STRSCPY(entry->content, log_buf, SULOG_ENTRY_MAX_LEN);
spin_lock_irqsave(&dedup_lock, flags);
list_add_tail(&entry->list, &sulog_queue);
spin_unlock_irqrestore(&dedup_lock, flags);
if (sulog_workqueue)
queue_work(sulog_workqueue, &sulog_work);
}
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method)
{
char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32];
char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled)
return;
get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf),
"[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n",
timestamp, uid, full_comm, method ? method : "unknown", current->pid);
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_GRANT);
}
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success)
{
char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32];
char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled)
return;
get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf),
"[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n",
timestamp, uid, full_comm, target_path ? target_path : "unknown",
success ? "SUCCESS" : "DENIED", current->pid);
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_ATTEMPT);
}
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed)
{
char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32];
char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled)
return;
get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf),
"[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n",
timestamp, uid, full_comm, allowed ? "ALLOWED" : "DENIED", current->pid);
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_PERM_CHECK);
}
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid)
{
char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32];
char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled)
return;
get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, NULL, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf),
"[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n",
timestamp, operation ? operation : "unknown", manager_uid, target_uid, full_comm, current->pid);
sulog_add_entry(log_buf, strlen(log_buf), manager_uid, DEDUP_MANAGER_OP);
}
void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall, const char *args)
{
char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32];
char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled)
return;
get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf),
"[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n",
timestamp, uid, full_comm, syscall ? syscall : "unknown",
args ? args : "none", current->pid);
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SYSCALL);
}
int ksu_sulog_init(void)
{
if (ksu_register_feature_handler(&sulog_handler)) {
pr_err("Failed to register sulog feature handler\n");
}
sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1);
if (!sulog_workqueue) {
pr_err("sulog: failed to create workqueue\n");
return -ENOMEM;
}
INIT_WORK(&sulog_work, sulog_work_handler);
pr_info("sulog: initialized successfully\n");
return 0;
}
void ksu_sulog_exit(void)
{
struct sulog_entry *entry, *tmp;
unsigned long flags;
ksu_unregister_feature_handler(KSU_FEATURE_SULOG);
sulog_enabled = false;
if (sulog_workqueue) {
flush_workqueue(sulog_workqueue);
destroy_workqueue(sulog_workqueue);
sulog_workqueue = NULL;
}
spin_lock_irqsave(&dedup_lock, flags);
list_for_each_entry_safe(entry, tmp, &sulog_queue, list) {
list_del(&entry->list);
kfree(entry);
}
spin_unlock_irqrestore(&dedup_lock, flags);
pr_info("sulog: cleaned up successfully\n");
}
#endif // __SULOG_GATE

93
kernel/sulog.h Normal file
View File

@@ -0,0 +1,93 @@
#ifndef __KSU_SULOG_H
#define __KSU_SULOG_H
#include <linux/types.h>
#include <linux/version.h>
#include <linux/crc32.h> // needed for function dedup_calc_hash
#define __SULOG_GATE 1
#if __SULOG_GATE
extern struct timezone sys_tz;
#define SULOG_PATH "/data/adb/ksu/log/sulog.log"
#define SULOG_MAX_SIZE (32 * 1024 * 1024) // 128MB
#define SULOG_ENTRY_MAX_LEN 512
#define SULOG_COMM_LEN 256
#define DEDUP_SECS 10
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0)
static inline size_t strlcpy(char *dest, const char *src, size_t size)
{
return strscpy(dest, src, size);
}
#endif
#define KSU_STRSCPY(dst, src, size) \
do { \
if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) { \
strscpy(dst, src, size); \
} else { \
strlcpy(dst, src, size); \
} \
} while (0)
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
#include <linux/rtc.h>
static inline void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
{
struct rtc_time rtc_tm;
rtc_time64_to_tm(totalsecs, &rtc_tm);
result->tm_sec = rtc_tm.tm_sec;
result->tm_min = rtc_tm.tm_min;
result->tm_hour = rtc_tm.tm_hour;
result->tm_mday = rtc_tm.tm_mday;
result->tm_mon = rtc_tm.tm_mon;
result->tm_year = rtc_tm.tm_year;
}
#endif
struct dedup_key {
u32 crc;
uid_t uid;
u8 type;
u8 _pad[1];
};
struct dedup_entry {
struct dedup_key key;
u64 ts_ns;
};
enum {
DEDUP_SU_GRANT = 0,
DEDUP_SU_ATTEMPT,
DEDUP_PERM_CHECK,
DEDUP_MANAGER_OP,
DEDUP_SYSCALL,
};
static inline u32 dedup_calc_hash(const char *content, size_t len)
{
return crc32(0, content, len);
}
struct sulog_entry {
struct list_head list;
char content[SULOG_ENTRY_MAX_LEN];
};
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method);
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success);
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed);
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid);
void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall, const char *args);
int ksu_sulog_init(void);
void ksu_sulog_exit(void);
#endif // __SULOG_GATE
#endif /* __KSU_SULOG_H */

1072
kernel/supercalls.c Normal file

File diff suppressed because it is too large Load Diff

197
kernel/supercalls.h Normal file
View File

@@ -0,0 +1,197 @@
#ifndef __KSU_H_SUPERCALLS
#define __KSU_H_SUPERCALLS
#include <linux/types.h>
#include <linux/ioctl.h>
#include "ksu.h"
#include "app_profile.h"
#ifdef CONFIG_KPM
#include "kpm/kpm.h"
#endif
// Magic numbers for reboot hook to install fd
#define KSU_INSTALL_MAGIC1 0xDEADBEEF
#define KSU_INSTALL_MAGIC2 0xCAFEBABE
// Command structures for ioctl
struct ksu_become_daemon_cmd {
__u8 token[65]; // Input: daemon token (null-terminated)
};
struct ksu_get_info_cmd {
__u32 version; // Output: KERNEL_SU_VERSION
__u32 flags; // Output: flags (bit 0: MODULE mode)
__u32 features; // Output: max feature ID supported
};
struct ksu_report_event_cmd {
__u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc.
};
struct ksu_set_sepolicy_cmd {
__u64 cmd; // Input: sepolicy command
__aligned_u64 arg; // Input: sepolicy argument pointer
};
struct ksu_check_safemode_cmd {
__u8 in_safe_mode; // Output: true if in safe mode, false otherwise
};
struct ksu_get_allow_list_cmd {
__u32 uids[128]; // Output: array of allowed/denied UIDs
__u32 count; // Output: number of UIDs in array
__u8 allow; // Input: true for allow list, false for deny list
};
struct ksu_uid_granted_root_cmd {
__u32 uid; // Input: target UID to check
__u8 granted; // Output: true if granted, false otherwise
};
struct ksu_uid_should_umount_cmd {
__u32 uid; // Input: target UID to check
__u8 should_umount; // Output: true if should umount, false otherwise
};
struct ksu_get_manager_uid_cmd {
__u32 uid; // Output: manager UID
};
struct ksu_get_app_profile_cmd {
struct app_profile profile; // Input/Output: app profile structure
};
struct ksu_set_app_profile_cmd {
struct app_profile profile; // Input: app profile structure
};
struct ksu_get_feature_cmd {
__u32 feature_id; // Input: feature ID (enum ksu_feature_id)
__u64 value; // Output: feature value/state
__u8 supported; // Output: true if feature is supported, false otherwise
};
struct ksu_set_feature_cmd {
__u32 feature_id; // Input: feature ID (enum ksu_feature_id)
__u64 value; // Input: feature value/state to set
};
struct ksu_get_wrapper_fd_cmd {
__u32 fd; // Input: userspace fd
__u32 flags; // Input: flags of userspace fd
};
struct ksu_manage_mark_cmd {
__u32 operation; // Input: KSU_MARK_*
__s32 pid; // Input: target pid (0 for all processes)
__u32 result; // Output: for get operation - mark status or reg_count
};
#define KSU_MARK_GET 1
#define KSU_MARK_MARK 2
#define KSU_MARK_UNMARK 3
#define KSU_MARK_REFRESH 4
struct ksu_nuke_ext4_sysfs_cmd {
__aligned_u64 arg; // Input: mnt pointer
};
struct ksu_add_try_umount_cmd {
__aligned_u64 arg; // char ptr, this is the mountpoint
__u32 flags; // this is the flag we use for it
__u8 mode; // denotes what to do with it 0:wipe_list 1:add_to_list 2:delete_entry
};
#define KSU_UMOUNT_WIPE 0 // ignore everything and wipe list
#define KSU_UMOUNT_ADD 1 // add entry (path + flags)
#define KSU_UMOUNT_DEL 2 // delete entry, strcmp
// Other command structures
struct ksu_get_full_version_cmd {
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
};
struct ksu_hook_type_cmd {
char hook_type[32]; // Output: hook type string
};
struct ksu_enable_kpm_cmd {
__u8 enabled; // Output: true if KPM is enabled
};
struct ksu_dynamic_manager_cmd {
struct dynamic_manager_user_config config; // Input/Output: dynamic manager config
};
struct ksu_get_managers_cmd {
struct manager_list_info manager_info; // Output: manager list information
};
struct ksu_enable_uid_scanner_cmd {
__u32 operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV)
__u32 enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE)
void __user *status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
};
#ifdef CONFIG_KSU_MANUAL_SU
struct ksu_manual_su_cmd {
__u32 option; // Input: operation type (MANUAL_SU_OP_GENERATE_TOKEN, MANUAL_SU_OP_ESCALATE, MANUAL_SU_OP_ADD_PENDING)
__u32 target_uid; // Input: target UID
__u32 target_pid; // Input: target PID
char token_buffer[33]; // Input/Output: token buffer
};
#endif
// IOCTL command definitions
#define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0)
#define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0)
#define KSU_IOCTL_REPORT_EVENT _IOC(_IOC_WRITE, 'K', 3, 0)
#define KSU_IOCTL_SET_SEPOLICY _IOC(_IOC_READ|_IOC_WRITE, 'K', 4, 0)
#define KSU_IOCTL_CHECK_SAFEMODE _IOC(_IOC_READ, 'K', 5, 0)
#define KSU_IOCTL_GET_ALLOW_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 6, 0)
#define KSU_IOCTL_GET_DENY_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 7, 0)
#define KSU_IOCTL_UID_GRANTED_ROOT _IOC(_IOC_READ|_IOC_WRITE, 'K', 8, 0)
#define KSU_IOCTL_UID_SHOULD_UMOUNT _IOC(_IOC_READ|_IOC_WRITE, 'K', 9, 0)
#define KSU_IOCTL_GET_MANAGER_UID _IOC(_IOC_READ, 'K', 10, 0)
#define KSU_IOCTL_GET_APP_PROFILE _IOC(_IOC_READ|_IOC_WRITE, 'K', 11, 0)
#define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0)
#define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0)
#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0)
#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0)
#define KSU_IOCTL_MANAGE_MARK _IOC(_IOC_READ|_IOC_WRITE, 'K', 16, 0)
#define KSU_IOCTL_NUKE_EXT4_SYSFS _IOC(_IOC_WRITE, 'K', 17, 0)
#define KSU_IOCTL_ADD_TRY_UMOUNT _IOC(_IOC_WRITE, 'K', 18, 0)
// Other IOCTL command definitions
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
#define KSU_IOCTL_ENABLE_KPM _IOC(_IOC_READ, 'K', 102, 0)
#define KSU_IOCTL_DYNAMIC_MANAGER _IOC(_IOC_READ|_IOC_WRITE, 'K', 103, 0)
#define KSU_IOCTL_GET_MANAGERS _IOC(_IOC_READ|_IOC_WRITE, 'K', 104, 0)
#define KSU_IOCTL_ENABLE_UID_SCANNER _IOC(_IOC_READ|_IOC_WRITE, 'K', 105, 0)
#ifdef CONFIG_KSU_MANUAL_SU
#define KSU_IOCTL_MANUAL_SU _IOC(_IOC_READ|_IOC_WRITE, 'K', 106, 0)
#endif
#define KSU_IOCTL_UMOUNT_MANAGER _IOC(_IOC_READ|_IOC_WRITE, 'K', 107, 0)
// IOCTL handler types
typedef int (*ksu_ioctl_handler_t)(void __user *arg);
typedef bool (*ksu_perm_check_t)(void);
// IOCTL command mapping
struct ksu_ioctl_cmd_map {
unsigned int cmd;
const char *name;
ksu_ioctl_handler_t handler;
ksu_perm_check_t perm_check; // Permission check function
};
// Install KSU fd to current process
int ksu_install_fd(void);
void ksu_supercalls_init(void);
void ksu_supercalls_exit(void);
#endif // __KSU_H_SUPERCALLS

View File

@@ -0,0 +1,385 @@
#include "linux/compiler.h"
#include "linux/cred.h"
#include "linux/printk.h"
#include "selinux/selinux.h"
#include <linux/spinlock.h>
#include <linux/kprobes.h>
#include <linux/tracepoint.h>
#include <asm/syscall.h>
#include <linux/slab.h>
#include <linux/ptrace.h>
#include <trace/events/syscalls.h>
#include <linux/namei.h>
#include "allowlist.h"
#include "arch.h"
#include "klog.h" // IWYU pragma: keep
#include "syscall_hook_manager.h"
#include "sucompat.h"
#include "setuid_hook.h"
#include "selinux/selinux.h"
#include "util.h"
// Tracepoint registration count management
// == 1: just us
// > 1: someone else is also using syscall tracepoint e.g. ftrace
static int tracepoint_reg_count = 0;
static DEFINE_SPINLOCK(tracepoint_reg_lock);
void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count <= 1) {
ksu_clear_task_tracepoint_flag(t);
}
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
}
// Process marking management
static void handle_process_mark(bool mark)
{
struct task_struct *p, *t;
read_lock(&tasklist_lock);
for_each_process_thread(p, t) {
if (mark)
ksu_set_task_tracepoint_flag(t);
else
ksu_clear_task_tracepoint_flag(t);
}
read_unlock(&tasklist_lock);
}
void ksu_mark_all_process(void)
{
handle_process_mark(true);
pr_info("hook_manager: mark all user process done!\n");
}
void ksu_unmark_all_process(void)
{
handle_process_mark(false);
pr_info("hook_manager: unmark all user process done!\n");
}
static void ksu_mark_running_process_locked()
{
struct task_struct *p, *t;
read_lock(&tasklist_lock);
for_each_process_thread (p, t) {
if (!t->mm) { // only user processes
continue;
}
int uid = task_uid(t).val;
const struct cred *cred = get_task_cred(t);
bool ksu_root_process =
uid == 0 && is_task_ksu_domain(cred);
bool is_zygote_process = is_zygote(cred);
bool is_shell = uid == 2000;
// before boot completed, we shall mark init for marking zygote
bool is_init = t->pid == 1;
if (ksu_root_process || is_zygote_process || is_shell || is_init
|| ksu_is_allow_uid(uid)) {
ksu_set_task_tracepoint_flag(t);
pr_info("hook_manager: mark process: pid:%d, uid: %d, comm:%s\n",
t->pid, uid, t->comm);
} else {
ksu_clear_task_tracepoint_flag(t);
pr_info("hook_manager: unmark process: pid:%d, uid: %d, comm:%s\n",
t->pid, uid, t->comm);
}
put_cred(cred);
}
read_unlock(&tasklist_lock);
}
void ksu_mark_running_process()
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count <= 1) {
ksu_mark_running_process_locked();
} else {
pr_info("hook_manager: not mark running process since syscall tracepoint is in use\n");
}
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
}
// Get task mark status
// Returns: 1 if marked, 0 if not marked, -ESRCH if task not found
int ksu_get_task_mark(pid_t pid)
{
struct task_struct *task;
int marked = -ESRCH;
rcu_read_lock();
task = find_task_by_vpid(pid);
if (task) {
get_task_struct(task);
rcu_read_unlock();
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
marked = test_task_syscall_work(task, SYSCALL_TRACEPOINT) ? 1 : 0;
#else
marked = test_tsk_thread_flag(task, TIF_SYSCALL_TRACEPOINT) ? 1 : 0;
#endif
put_task_struct(task);
} else {
rcu_read_unlock();
}
return marked;
}
// Set task mark status
// Returns: 0 on success, -ESRCH if task not found
int ksu_set_task_mark(pid_t pid, bool mark)
{
struct task_struct *task;
int ret = -ESRCH;
rcu_read_lock();
task = find_task_by_vpid(pid);
if (task) {
get_task_struct(task);
rcu_read_unlock();
if (mark) {
ksu_set_task_tracepoint_flag(task);
pr_info("hook_manager: marked task pid=%d comm=%s\n", pid, task->comm);
} else {
ksu_clear_task_tracepoint_flag(task);
pr_info("hook_manager: unmarked task pid=%d comm=%s\n", pid, task->comm);
}
put_task_struct(task);
ret = 0;
} else {
rcu_read_unlock();
}
return ret;
}
#ifdef CONFIG_KRETPROBES
static struct kretprobe *init_kretprobe(const char *name,
kretprobe_handler_t handler)
{
struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL);
if (!rp)
return NULL;
rp->kp.symbol_name = name;
rp->handler = handler;
rp->data_size = 0;
rp->maxactive = 0;
int ret = register_kretprobe(rp);
pr_info("hook_manager: register_%s kretprobe: %d\n", name, ret);
if (ret) {
kfree(rp);
return NULL;
}
return rp;
}
static void destroy_kretprobe(struct kretprobe **rp_ptr)
{
struct kretprobe *rp = *rp_ptr;
if (!rp)
return;
unregister_kretprobe(rp);
synchronize_rcu();
kfree(rp);
*rp_ptr = NULL;
}
static int syscall_regfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
if (tracepoint_reg_count < 1) {
// while install our tracepoint, mark our processes
ksu_mark_running_process_locked();
} else if (tracepoint_reg_count == 1) {
// while other tracepoint first added, mark all processes
ksu_mark_all_process();
}
tracepoint_reg_count++;
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
return 0;
}
static int syscall_unregfunc_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
{
unsigned long flags;
spin_lock_irqsave(&tracepoint_reg_lock, flags);
tracepoint_reg_count--;
if (tracepoint_reg_count <= 0) {
// while no tracepoint left, unmark all processes
ksu_unmark_all_process();
} else if (tracepoint_reg_count == 1) {
// while just our tracepoint left, unmark disallowed processes
ksu_mark_running_process_locked();
}
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
return 0;
}
static struct kretprobe *syscall_regfunc_rp = NULL;
static struct kretprobe *syscall_unregfunc_rp = NULL;
#endif
static inline bool check_syscall_fastpath(int nr)
{
switch (nr) {
case __NR_newfstatat:
case __NR_faccessat:
case __NR_execve:
case __NR_setresuid:
case __NR_clone:
case __NR_clone3:
return true;
default:
return false;
}
}
// Unmark init's child that are not zygote, adbd or ksud
int ksu_handle_init_mark_tracker(const char __user **filename_user)
{
char path[64];
unsigned long addr;
const char __user *fn;
long ret;
if (unlikely(!filename_user))
return 0;
addr = untagged_addr((unsigned long)*filename_user);
fn = (const char __user *)addr;
memset(path, 0, sizeof(path));
ret = strncpy_from_user_nofault(path, fn, sizeof(path));
if (ret < 0 && try_set_access_flag(addr)) {
ret = strncpy_from_user_nofault(path, fn, sizeof(path));
pr_info("ksu_handle_init_mark_tracker: %ld\n", ret);
}
if (likely(strstr(path, "/app_process") == NULL && strstr(path, "/adbd") == NULL && strstr(path, "/ksud") == NULL)) {
pr_info("hook_manager: unmark %d exec %s", current->pid, path);
ksu_clear_task_tracepoint_flag_if_needed(current);
}
return 0;
}
#ifdef CONFIG_KSU_MANUAL_SU
#include "manual_su.h"
static inline void ksu_handle_task_alloc(struct pt_regs *regs)
{
ksu_try_escalate_for_uid(current_uid().val);
}
#endif
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
// Generic sys_enter handler that dispatches to specific handlers
static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id)
{
if (unlikely(check_syscall_fastpath(id))) {
#ifdef KSU_TP_HOOK
if (ksu_su_compat_enabled) {
// Handle newfstatat
if (id == __NR_newfstatat) {
int *dfd = (int *)&PT_REGS_PARM1(regs);
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM2(regs);
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(regs);
ksu_handle_stat(dfd, filename_user, flags);
return;
}
// Handle faccessat
if (id == __NR_faccessat) {
int *dfd = (int *)&PT_REGS_PARM1(regs);
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM2(regs);
int *mode = (int *)&PT_REGS_PARM3(regs);
ksu_handle_faccessat(dfd, filename_user, mode, NULL);
return;
}
// Handle execve
if (id == __NR_execve) {
const char __user **filename_user =
(const char __user **)&PT_REGS_PARM1(regs);
if (current->pid != 1 && is_init(get_current_cred())) {
ksu_handle_init_mark_tracker(filename_user);
} else {
ksu_handle_execve_sucompat(filename_user, NULL, NULL, NULL);
}
return;
}
}
#endif
// Handle setresuid
if (id == __NR_setresuid) {
uid_t ruid = (uid_t)PT_REGS_PARM1(regs);
uid_t euid = (uid_t)PT_REGS_PARM2(regs);
uid_t suid = (uid_t)PT_REGS_PARM3(regs);
ksu_handle_setresuid(ruid, euid, suid);
return;
}
#ifdef CONFIG_KSU_MANUAL_SU
// Handle task_alloc via clone/fork
if (id == __NR_clone || id == __NR_clone3)
return ksu_handle_task_alloc(regs);
#endif
}
}
#endif
void ksu_syscall_hook_manager_init(void)
{
int ret;
pr_info("hook_manager: ksu_hook_manager_init called\n");
#ifdef CONFIG_KRETPROBES
// Register kretprobe for syscall_regfunc
syscall_regfunc_rp = init_kretprobe("syscall_regfunc", syscall_regfunc_handler);
// Register kretprobe for syscall_unregfunc
syscall_unregfunc_rp = init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler);
#endif
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
ret = register_trace_sys_enter(ksu_sys_enter_handler, NULL);
#ifndef CONFIG_KRETPROBES
ksu_mark_running_process_locked();
#endif
if (ret) {
pr_err("hook_manager: failed to register sys_enter tracepoint: %d\n", ret);
} else {
pr_info("hook_manager: sys_enter tracepoint registered\n");
}
#endif
ksu_setuid_hook_init();
ksu_sucompat_init();
}
void ksu_syscall_hook_manager_exit(void)
{
pr_info("hook_manager: ksu_hook_manager_exit called\n");
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
unregister_trace_sys_enter(ksu_sys_enter_handler, NULL);
tracepoint_synchronize_unregister();
pr_info("hook_manager: sys_enter tracepoint unregistered\n");
#endif
#ifdef CONFIG_KRETPROBES
destroy_kretprobe(&syscall_regfunc_rp);
destroy_kretprobe(&syscall_unregfunc_rp);
#endif
ksu_sucompat_exit();
ksu_setuid_hook_exit();
}

View File

@@ -0,0 +1,47 @@
#ifndef __KSU_H_HOOK_MANAGER
#define __KSU_H_HOOK_MANAGER
#include <linux/version.h>
#include <linux/sched.h>
#include <linux/thread_info.h>
#include <linux/init.h>
#include <linux/binfmts.h>
#include <linux/tty.h>
#include <linux/fs.h>
#include "selinux/selinux.h"
// Hook manager initialization and cleanup
void ksu_syscall_hook_manager_init(void);
void ksu_syscall_hook_manager_exit(void);
// Process marking for tracepoint
void ksu_mark_all_process(void);
void ksu_unmark_all_process(void);
void ksu_mark_running_process(void);
// Per-task mark operations
int ksu_get_task_mark(pid_t pid);
int ksu_set_task_mark(pid_t pid, bool mark);
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
set_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else
set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif
}
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
clear_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else
clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif
}
void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t);
#endif

View File

@@ -7,119 +7,208 @@
#include "klog.h"
#include "throne_comm.h"
#include "ksu.h"
#define PROC_UID_SCANNER "ksu_uid_scanner"
#define UID_SCANNER_STATE_FILE "/data/adb/ksu/.uid_scanner"
static struct proc_dir_entry *proc_entry = NULL;
static struct workqueue_struct *scanner_wq = NULL;
static struct work_struct scan_work;
static struct work_struct ksu_state_save_work;
static struct work_struct ksu_state_load_work;
// Signal userspace to rescan
static bool need_rescan = false;
static void rescan_work_fn(struct work_struct *work)
{
// Signal userspace through proc interface
need_rescan = true;
pr_info("requested userspace uid rescan\n");
// Signal userspace through proc interface
need_rescan = true;
pr_info("requested userspace uid rescan\n");
}
void ksu_request_userspace_scan(void)
{
if (scanner_wq) {
queue_work(scanner_wq, &scan_work);
}
if (scanner_wq) {
queue_work(scanner_wq, &scan_work);
}
}
void ksu_handle_userspace_update(void)
{
// Called when userspace notifies update complete
need_rescan = false;
pr_info("userspace uid list updated\n");
// Called when userspace notifies update complete
need_rescan = false;
pr_info("userspace uid list updated\n");
}
static void do_save_throne_state(struct work_struct *work)
{
struct file *fp;
char state_char = ksu_uid_scanner_enabled ? '1' : '0';
loff_t off = 0;
fp = filp_open(UID_SCANNER_STATE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("save_throne_state create file failed: %ld\n", PTR_ERR(fp));
return;
}
if (kernel_write(fp, &state_char, sizeof(state_char), &off) != sizeof(state_char)) {
pr_err("save_throne_state write failed\n");
goto exit;
}
pr_info("throne state saved: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
exit:
filp_close(fp, 0);
}
void do_load_throne_state(struct work_struct *work)
{
struct file *fp;
char state_char;
loff_t off = 0;
ssize_t ret;
fp = filp_open(UID_SCANNER_STATE_FILE, O_RDONLY, 0);
if (IS_ERR(fp)) {
pr_info("throne state file not found, using default: disabled\n");
ksu_uid_scanner_enabled = false;
return;
}
ret = kernel_read(fp, &state_char, sizeof(state_char), &off);
if (ret != sizeof(state_char)) {
pr_err("load_throne_state read err: %zd\n", ret);
ksu_uid_scanner_enabled = false;
goto exit;
}
ksu_uid_scanner_enabled = (state_char == '1');
pr_info("throne state loaded: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
exit:
filp_close(fp, 0);
}
bool ksu_throne_comm_load_state(void)
{
return ksu_queue_work(&ksu_state_load_work);
}
void ksu_throne_comm_save_state(void)
{
ksu_queue_work(&ksu_state_save_work);
}
static int uid_scanner_show(struct seq_file *m, void *v)
{
if (need_rescan) {
seq_puts(m, "RESCAN\n");
} else {
seq_puts(m, "OK\n");
}
return 0;
if (need_rescan) {
seq_puts(m, "RESCAN\n");
} else {
seq_puts(m, "OK\n");
}
return 0;
}
static int uid_scanner_open(struct inode *inode, struct file *file)
{
return single_open(file, uid_scanner_show, NULL);
return single_open(file, uid_scanner_show, NULL);
}
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
char cmd[16];
if (count >= sizeof(cmd))
return -EINVAL;
if (copy_from_user(cmd, buffer, count))
return -EFAULT;
cmd[count] = '\0';
// Remove newline if present
if (count > 0 && cmd[count-1] == '\n')
cmd[count-1] = '\0';
if (strcmp(cmd, "UPDATED") == 0) {
ksu_handle_userspace_update();
pr_info("received userspace update notification\n");
}
return count;
char cmd[16];
if (count >= sizeof(cmd))
return -EINVAL;
if (copy_from_user(cmd, buffer, count))
return -EFAULT;
cmd[count] = '\0';
// Remove newline if present
if (count > 0 && cmd[count-1] == '\n')
cmd[count-1] = '\0';
if (strcmp(cmd, "UPDATED") == 0) {
ksu_handle_userspace_update();
pr_info("received userspace update notification\n");
}
return count;
}
#ifdef KSU_COMPAT_HAS_PROC_OPS
static const struct proc_ops uid_scanner_proc_ops = {
.proc_open = uid_scanner_open,
.proc_read = seq_read,
.proc_write = uid_scanner_write,
.proc_write = uid_scanner_write,
.proc_lseek = seq_lseek,
.proc_release = single_release,
};
#else
static const struct file_operations uid_scanner_proc_ops = {
.owner = THIS_MODULE,
.open = uid_scanner_open,
.read = seq_read,
.write = uid_scanner_write,
.llseek = seq_lseek,
.release = single_release,
};
#endif
int ksu_throne_comm_init(void)
{
// Create workqueue
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
if (!scanner_wq) {
pr_err("failed to create scanner workqueue\n");
return -ENOMEM;
}
INIT_WORK(&scan_work, rescan_work_fn);
// Create proc entry
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
if (!proc_entry) {
pr_err("failed to create proc entry\n");
destroy_workqueue(scanner_wq);
return -ENOMEM;
}
pr_info("throne communication initialized\n");
return 0;
// Create workqueue
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
if (!scanner_wq) {
pr_err("failed to create scanner workqueue\n");
return -ENOMEM;
}
INIT_WORK(&scan_work, rescan_work_fn);
// Create proc entry
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
if (!proc_entry) {
pr_err("failed to create proc entry\n");
destroy_workqueue(scanner_wq);
return -ENOMEM;
}
pr_info("throne communication initialized\n");
return 0;
}
void ksu_throne_comm_exit(void)
{
if (proc_entry) {
proc_remove(proc_entry);
proc_entry = NULL;
}
if (scanner_wq) {
destroy_workqueue(scanner_wq);
scanner_wq = NULL;
}
pr_info("throne communication cleaned up\n");
if (proc_entry) {
proc_remove(proc_entry);
proc_entry = NULL;
}
if (scanner_wq) {
destroy_workqueue(scanner_wq);
scanner_wq = NULL;
}
pr_info("throne communication cleaned up\n");
}
int ksu_uid_init(void)
{
INIT_WORK(&ksu_state_save_work, do_save_throne_state);
INIT_WORK(&ksu_state_load_work, do_load_throne_state);
return 0;
}
void ksu_uid_exit(void)
{
do_save_throne_state(NULL);
}

View File

@@ -9,4 +9,14 @@ int ksu_throne_comm_init(void);
void ksu_throne_comm_exit(void);
int ksu_uid_init(void);
void ksu_uid_exit(void);
bool ksu_throne_comm_load_state(void);
void ksu_throne_comm_save_state(void);
void do_load_throne_state(struct work_struct *work);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,6 @@ void ksu_throne_tracker_init();
void ksu_throne_tracker_exit();
void track_throne();
void track_throne(bool prune_only);
#endif

353
kernel/umount_manager.c Normal file
View File

@@ -0,0 +1,353 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/namei.h>
#include <linux/path.h>
#include <linux/mount.h>
#include <linux/cred.h>
#include "klog.h"
#include "kernel_umount.h"
#include "umount_manager.h"
static struct umount_manager g_umount_mgr = {
.entry_count = 0,
.max_entries = 512,
};
static void try_umount_path(struct umount_entry *entry)
{
try_umount(entry->path, entry->flags);
}
static struct umount_entry *find_entry_locked(const char *path)
{
struct umount_entry *entry;
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
if (strcmp(entry->path, path) == 0) {
return entry;
}
}
return NULL;
}
static bool is_path_in_mount_list(const char *path)
{
struct mount_entry *entry;
bool found = false;
down_read(&mount_list_lock);
list_for_each_entry(entry, &mount_list, list) {
if (entry->umountable && strcmp(entry->umountable, path) == 0) {
found = true;
break;
}
}
up_read(&mount_list_lock);
return found;
}
static int copy_mount_entry_to_user(struct ksu_umount_entry_info __user *entries,
u32 idx, const char *path, int flags)
{
struct ksu_umount_entry_info info;
memset(&info, 0, sizeof(info));
strncpy(info.path, path, sizeof(info.path) - 1);
info.path[sizeof(info.path) - 1] = '\0';
info.flags = flags;
info.is_default = 1;
info.state = UMOUNT_STATE_IDLE;
info.ref_count = 0;
if (copy_to_user(&entries[idx], &info, sizeof(info))) {
return -EFAULT;
}
return 0;
}
static int copy_umount_entry_to_user(struct ksu_umount_entry_info __user *entries,
u32 idx, struct umount_entry *entry)
{
struct ksu_umount_entry_info info;
memset(&info, 0, sizeof(info));
strncpy(info.path, entry->path, sizeof(info.path) - 1);
info.path[sizeof(info.path) - 1] = '\0';
info.flags = entry->flags;
info.is_default = entry->is_default;
info.state = entry->state;
info.ref_count = entry->ref_count;
if (copy_to_user(&entries[idx], &info, sizeof(info))) {
return -EFAULT;
}
return 0;
}
static int collect_mount_list_entries(struct ksu_umount_entry_info __user *entries,
u32 max_count, u32 *out_idx)
{
struct mount_entry *mount_entry;
u32 idx = 0;
down_read(&mount_list_lock);
list_for_each_entry(mount_entry, &mount_list, list) {
if (idx >= max_count) {
break;
}
if (!mount_entry->umountable) {
continue;
}
if (copy_mount_entry_to_user(entries, idx, mount_entry->umountable,
mount_entry->flags)) {
up_read(&mount_list_lock);
return -EFAULT;
}
idx++;
}
up_read(&mount_list_lock);
*out_idx = idx;
return 0;
}
static int collect_umount_manager_entries(struct ksu_umount_entry_info __user *entries,
u32 start_idx, u32 max_count, u32 *out_idx)
{
struct umount_entry *entry;
unsigned long flags;
u32 idx = start_idx;
spin_lock_irqsave(&g_umount_mgr.lock, flags);
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
if (idx >= max_count) {
break;
}
if (is_path_in_mount_list(entry->path)) {
continue;
}
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
if (copy_umount_entry_to_user(entries, idx, entry)) {
return -EFAULT;
}
idx++;
spin_lock_irqsave(&g_umount_mgr.lock, flags);
}
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
*out_idx = idx;
return 0;
}
int ksu_umount_manager_init(void)
{
INIT_LIST_HEAD(&g_umount_mgr.entry_list);
spin_lock_init(&g_umount_mgr.lock);
return 0;
}
void ksu_umount_manager_exit(void)
{
struct umount_entry *entry, *tmp;
unsigned long flags;
spin_lock_irqsave(&g_umount_mgr.lock, flags);
list_for_each_entry_safe(entry, tmp, &g_umount_mgr.entry_list, list) {
list_del(&entry->list);
kfree(entry);
g_umount_mgr.entry_count--;
}
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
pr_info("Umount manager cleaned up\n");
}
int ksu_umount_manager_add(const char *path, int flags, bool is_default)
{
struct umount_entry *entry;
unsigned long irqflags;
int ret = 0;
if (flags == -1)
flags = MNT_DETACH;
if (!path || strlen(path) == 0 || strlen(path) >= 256) {
return -EINVAL;
}
if (is_path_in_mount_list(path)) {
pr_warn("Umount manager: path already exists in mount_list: %s\n", path);
return -EEXIST;
}
spin_lock_irqsave(&g_umount_mgr.lock, irqflags);
if (g_umount_mgr.entry_count >= g_umount_mgr.max_entries) {
pr_err("Umount manager: max entries reached\n");
ret = -ENOMEM;
goto out;
}
if (find_entry_locked(path)) {
pr_warn("Umount manager: path already exists: %s\n", path);
ret = -EEXIST;
goto out;
}
entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry) {
ret = -ENOMEM;
goto out;
}
strncpy(entry->path, path, sizeof(entry->path) - 1);
entry->flags = flags;
entry->state = UMOUNT_STATE_IDLE;
entry->is_default = is_default;
entry->ref_count = 0;
list_add_tail(&entry->list, &g_umount_mgr.entry_list);
g_umount_mgr.entry_count++;
pr_info("Umount manager: added %s entry: %s\n",
is_default ? "default" : "custom", path);
out:
spin_unlock_irqrestore(&g_umount_mgr.lock, irqflags);
return ret;
}
int ksu_umount_manager_remove(const char *path)
{
struct umount_entry *entry;
unsigned long flags;
int ret = 0;
if (!path) {
return -EINVAL;
}
spin_lock_irqsave(&g_umount_mgr.lock, flags);
entry = find_entry_locked(path);
if (!entry) {
ret = -ENOENT;
goto out;
}
if (entry->is_default) {
pr_err("Umount manager: cannot remove default entry: %s\n", path);
ret = -EPERM;
goto out;
}
if (entry->state == UMOUNT_STATE_BUSY || entry->ref_count > 0) {
pr_err("Umount manager: entry is busy: %s\n", path);
ret = -EBUSY;
goto out;
}
list_del(&entry->list);
g_umount_mgr.entry_count--;
kfree(entry);
pr_info("Umount manager: removed entry: %s\n", path);
out:
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
return ret;
}
void ksu_umount_manager_execute_all(const struct cred *cred)
{
struct umount_entry *entry;
unsigned long flags;
spin_lock_irqsave(&g_umount_mgr.lock, flags);
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
if (entry->state == UMOUNT_STATE_IDLE) {
entry->ref_count++;
}
}
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
if (entry->ref_count > 0 && entry->state == UMOUNT_STATE_IDLE) {
try_umount_path(entry);
}
}
spin_lock_irqsave(&g_umount_mgr.lock, flags);
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
if (entry->ref_count > 0) {
entry->ref_count--;
}
}
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
}
int ksu_umount_manager_get_entries(struct ksu_umount_entry_info __user *entries, u32 *count)
{
u32 max_count = *count;
u32 idx;
int ret;
ret = collect_mount_list_entries(entries, max_count, &idx);
if (ret) {
return ret;
}
if (idx < max_count) {
ret = collect_umount_manager_entries(entries, idx, max_count, &idx);
if (ret) {
return ret;
}
}
*count = idx;
return 0;
}
int ksu_umount_manager_clear_custom(void)
{
struct umount_entry *entry, *tmp;
unsigned long flags;
u32 cleared = 0;
spin_lock_irqsave(&g_umount_mgr.lock, flags);
list_for_each_entry_safe(entry, tmp, &g_umount_mgr.entry_list, list) {
if (!entry->is_default && entry->state == UMOUNT_STATE_IDLE && entry->ref_count == 0) {
list_del(&entry->list);
kfree(entry);
g_umount_mgr.entry_count--;
cleared++;
}
}
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
pr_info("Umount manager: cleared %u custom entries\n", cleared);
return 0;
}

63
kernel/umount_manager.h Normal file
View File

@@ -0,0 +1,63 @@
#ifndef __KSU_H_UMOUNT_MANAGER
#define __KSU_H_UMOUNT_MANAGER
#include <linux/types.h>
#include <linux/list.h>
#include <linux/spinlock.h>
struct cred;
enum umount_entry_state {
UMOUNT_STATE_IDLE = 0,
UMOUNT_STATE_ACTIVE = 1,
UMOUNT_STATE_BUSY = 2,
};
struct umount_entry {
struct list_head list;
char path[256];
int flags;
enum umount_entry_state state;
bool is_default;
u32 ref_count;
};
struct umount_manager {
struct list_head entry_list;
spinlock_t lock;
u32 entry_count;
u32 max_entries;
};
enum umount_manager_op {
UMOUNT_OP_ADD = 0,
UMOUNT_OP_REMOVE = 1,
UMOUNT_OP_LIST = 2,
UMOUNT_OP_CLEAR_CUSTOM = 3,
};
struct ksu_umount_manager_cmd {
__u32 operation;
char path[256];
__s32 flags;
__u32 count;
__aligned_u64 entries_ptr;
};
struct ksu_umount_entry_info {
char path[256];
__s32 flags;
__u8 is_default;
__u32 state;
__u32 ref_count;
};
int ksu_umount_manager_init(void);
void ksu_umount_manager_exit(void);
int ksu_umount_manager_add(const char *path, int flags, bool is_default);
int ksu_umount_manager_remove(const char *path);
void ksu_umount_manager_execute_all(const struct cred *cred);
int ksu_umount_manager_get_entries(struct ksu_umount_entry_info __user *entries, u32 *count);
int ksu_umount_manager_clear_custom(void);
#endif // __KSU_H_UMOUNT_MANAGER

76
kernel/util.c Normal file
View File

@@ -0,0 +1,76 @@
#include <linux/mm.h>
#include <linux/pgtable.h>
#include <linux/printk.h>
#include <asm/current.h>
#include "util.h"
bool try_set_access_flag(unsigned long addr)
{
#ifdef CONFIG_ARM64
struct mm_struct *mm = current->mm;
struct vm_area_struct *vma;
pgd_t *pgd;
p4d_t *p4d;
pud_t *pud;
pmd_t *pmd;
pte_t *ptep, pte;
spinlock_t *ptl;
bool ret = false;
if (!mm)
return false;
if (!mmap_read_trylock(mm))
return false;
vma = find_vma(mm, addr);
if (!vma || addr < vma->vm_start)
goto out_unlock;
pgd = pgd_offset(mm, addr);
if (!pgd_present(*pgd))
goto out_unlock;
p4d = p4d_offset(pgd, addr);
if (!p4d_present(*p4d))
goto out_unlock;
pud = pud_offset(p4d, addr);
if (!pud_present(*pud))
goto out_unlock;
pmd = pmd_offset(pud, addr);
if (!pmd_present(*pmd))
goto out_unlock;
if (pmd_trans_huge(*pmd))
goto out_unlock;
ptep = pte_offset_map_lock(mm, pmd, addr, &ptl);
if (!ptep)
goto out_unlock;
pte = *ptep;
if (!pte_present(pte))
goto out_pte_unlock;
if (pte_young(pte)) {
ret = true;
goto out_pte_unlock;
}
ptep_set_access_flags(vma, addr, ptep, pte_mkyoung(pte), 0);
pr_info("set AF for addr %lx\n", addr);
ret = true;
out_pte_unlock:
pte_unmap_unlock(ptep, ptl);
out_unlock:
mmap_read_unlock(mm);
return ret;
#else
return false;
#endif
}

24
kernel/util.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef __KSU_UTIL_H
#define __KSU_UTIL_H
#include <linux/types.h>
#ifndef preempt_enable_no_resched_notrace
#define preempt_enable_no_resched_notrace() \
do { \
barrier(); \
__preempt_count_dec(); \
} while (0)
#endif
#ifndef preempt_disable_notrace
#define preempt_disable_notrace() \
do { \
__preempt_count_inc(); \
barrier(); \
} while (0)
#endif
bool try_set_access_flag(unsigned long addr);
#endif

View File

@@ -2,7 +2,6 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
import com.android.build.gradle.tasks.PackageAndroidArtifact
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.agp.app)
@@ -11,12 +10,11 @@ plugins {
alias(libs.plugins.ksp)
alias(libs.plugins.lsplugin.apksign)
id("kotlin-parcelize")
}
val managerVersionCode: Int by rootProject.extra
val managerVersionName: String by rootProject.extra
val androidCmakeVersion: String by rootProject.extra
apksign {
storeFileProperty = "KEYSTORE_FILE"
@@ -25,7 +23,6 @@ apksign {
keyPasswordProperty = "KEY_PASSWORD"
}
android {
/**signingConfigs {
@@ -51,15 +48,12 @@ android {
}
buildFeatures {
aidl = true
buildConfig = true
compose = true
prefab = true
}
kotlin {
jvmToolchain(21)
}
packaging {
jniLibs {
useLegacyPackaging = true
@@ -77,7 +71,8 @@ android {
externalNativeBuild {
cmake {
path("src/main/cpp/CMakeLists.txt")
path = file("src/main/cpp/CMakeLists.txt")
version = androidCmakeVersion
}
}
@@ -119,11 +114,8 @@ dependencies {
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.material.icons.extended)
implementation(libs.androidx.compose.material)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.foundation)
implementation(libs.androidx.documentfile)
debugImplementation(libs.androidx.compose.ui.test.manifest)
@@ -146,24 +138,14 @@ dependencies {
implementation(libs.kotlinx.coroutines.core)
implementation(libs.me.zhanghai.android.appiconloader.coil)
implementation(libs.sheet.compose.dialogs.core)
implementation(libs.sheet.compose.dialogs.list)
implementation(libs.sheet.compose.dialogs.input)
implementation(libs.markdown)
implementation(libs.markdown.ext.tables)
implementation(libs.androidx.webkit)
implementation(libs.lsposed.cxx)
implementation(libs.com.github.topjohnwu.libsu.core)
implementation(libs.mmrl.platform)
compileOnly(libs.mmrl.hidden.api)
implementation(libs.mmrl.webui)
implementation(libs.mmrl.ui)
implementation(libs.accompanist.drawablepainter)
}
implementation(libs.miuix)
implementation(libs.haze)
implementation(libs.capsule)
}

View File

@@ -1,46 +0,0 @@
-verbose
-optimizationpasses 5
-dontwarn org.conscrypt.**
-dontwarn kotlinx.serialization.**
# Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin.
-dontwarn com.google.auto.service.AutoService
-dontwarn com.google.j2objc.annotations.RetainedWith
-dontwarn javax.lang.model.SourceVersion
-dontwarn javax.lang.model.element.AnnotationMirror
-dontwarn javax.lang.model.element.AnnotationValue
-dontwarn javax.lang.model.element.Element
-dontwarn javax.lang.model.element.ElementKind
-dontwarn javax.lang.model.element.ElementVisitor
-dontwarn javax.lang.model.element.ExecutableElement
-dontwarn javax.lang.model.element.Modifier
-dontwarn javax.lang.model.element.Name
-dontwarn javax.lang.model.element.PackageElement
-dontwarn javax.lang.model.element.TypeElement
-dontwarn javax.lang.model.element.TypeParameterElement
-dontwarn javax.lang.model.element.VariableElement
-dontwarn javax.lang.model.type.ArrayType
-dontwarn javax.lang.model.type.DeclaredType
-dontwarn javax.lang.model.type.ExecutableType
-dontwarn javax.lang.model.type.TypeKind
-dontwarn javax.lang.model.type.TypeMirror
-dontwarn javax.lang.model.type.TypeVariable
-dontwarn javax.lang.model.type.TypeVisitor
-dontwarn javax.lang.model.util.AbstractAnnotationValueVisitor8
-dontwarn javax.lang.model.util.AbstractTypeVisitor8
-dontwarn javax.lang.model.util.ElementFilter
-dontwarn javax.lang.model.util.Elements
-dontwarn javax.lang.model.util.SimpleElementVisitor8
-dontwarn javax.lang.model.util.SimpleTypeVisitor7
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
-dontwarn javax.lang.model.util.Types
-dontwarn javax.tools.Diagnostic$Kind
# MMRL:webui reflection
-keep class com.dergoogler.mmrl.webui.interfaces.** { *; }
-keep class com.sukisu.ultra.ui.webui.WebViewInterface { *; }
-keep,allowobfuscation class * extends com.dergoogler.mmrl.platform.content.IService { *; }

View File

@@ -3,48 +3,50 @@
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<application
android:name=".KernelSUApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:enableOnBackInvokedCallback="true"
android:enableOnBackInvokedCallback="false"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
android:theme="@style/Theme.KernelSU"
android:requestLegacyExternalStorage="true"
tools:targetApi="34">
<activity
android:name=".ui.MainActivity"
android:exported="true"
android:theme="@style/Theme.KernelSU">
android:theme="@style/Theme.KernelSU"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:mimeType="application/zip" />
<data android:mimeType="application/vnd.android.package-archive" />
<data android:scheme="content" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/zip" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/zip" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
</activity>
<activity-alias
android:name=".ui.MainActivityAlias"
android:exported="true"
android:enabled="false"
android:icon="@mipmap/ic_launcher_alt"
android:roundIcon="@mipmap/ic_launcher_alt_round"
android:targetActivity=".ui.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity
android:name=".ui.webui.WebUIActivity"
android:autoRemoveFromRecents="true"
@@ -52,13 +54,6 @@
android:exported="false"
android:theme="@style/Theme.KernelSU.WebUI" />
<activity
android:name=".ui.webui.WebUIXActivity"
android:autoRemoveFromRecents="true"
android:documentLaunchMode="intoExisting"
android:exported="false"
android:theme="@style/Theme.KernelSU.WebUI" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"

View File

@@ -0,0 +1,9 @@
// IKsuInterface.aidl
package com.sukisu.zako;
import android.content.pm.PackageInfo;
import rikka.parcelablelist.ParcelableListSlice;
interface IKsuInterface {
ParcelableListSlice<PackageInfo> getPackages(int flags);
}

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More