From 9210fc54cc0b8121c33bf40eff812e6a3469bc09 Mon Sep 17 00:00:00 2001 From: 5ec1cff <56485584+5ec1cff@users.noreply.github.com> Date: Sat, 2 Nov 2024 00:52:31 +0800 Subject: [PATCH 01/24] actions: quote shell vars --- .github/workflows/build-manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index 9031b92da45d..d70dd099f451 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -95,7 +95,7 @@ jobs: echo KEY_PASSWORD='${{ secrets.KEY_PASSWORD }}' echo KEYSTORE_FILE='key.jks' } >> gradle.properties - echo ${{ secrets.KEYSTORE }} | base64 -d > key.jks + echo "${{ secrets.KEYSTORE }}" | base64 -d > key.jks fi - name: Setup Java From 05bffc9975b3652fdb36cebe478153b0f62224e2 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Sat, 2 Nov 2024 12:07:25 +0800 Subject: [PATCH 02/24] actions: setup my sign key --- kernel/Kbuild | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/Kbuild b/kernel/Kbuild index 70511859bd85..a918cefb139d 100644 --- a/kernel/Kbuild +++ b/kernel/Kbuild @@ -61,11 +61,11 @@ ccflags-y += -DKSU_VERSION=16 endif ifndef KSU_EXPECTED_SIZE -KSU_EXPECTED_SIZE := 0x033b +KSU_EXPECTED_SIZE := 384 endif ifndef KSU_EXPECTED_HASH -KSU_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6 +KSU_EXPECTED_HASH := 7e0c6d7278a3bb8e364e0fcba95afaf3666cf5ff3c245a3b63c8833bd0445cc4 endif ifdef KSU_MANAGER_PACKAGE From 185cd5258eeb495e1684f33f593cfe38b5ba2aed Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Sat, 23 Nov 2024 19:05:53 +0800 Subject: [PATCH 03/24] actions: use var for chat id --- .github/workflows/build-manager.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index d70dd099f451..0b6b75d8c434 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -157,9 +157,9 @@ jobs: - name: Upload to telegram if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true' env: - CHAT_ID: ${{ secrets.CHAT_ID }} + CHAT_ID: ${{ vars.CHAT_ID }} BOT_TOKEN: ${{ secrets.BOT_TOKEN }} - MESSAGE_THREAD_ID: ${{ secrets.MESSAGE_THREAD_ID }} + 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 }} From 55f11def770407e53a960a52f45bd5b386fff8ed Mon Sep 17 00:00:00 2001 From: 5ec1cff <56485584+5ec1cff@users.noreply.github.com> Date: Thu, 6 Nov 2025 21:04:33 +0800 Subject: [PATCH 04/24] actions: add more path to trigger build manager when pull request --- .github/workflows/build-manager.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index 0b6b75d8c434..841dd18077de 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -19,6 +19,7 @@ on: - 'manager/**' - 'kernel/**' - 'userspace/**' + - 'scripts/ksubot.py' workflow_call: jobs: From 22f934bbc1a7fe0ba6940eeebb1406736e13c639 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Sat, 23 Nov 2024 18:41:49 +0800 Subject: [PATCH 05/24] ksubot: make MESSAGE_THREAD_ID optional --- scripts/ksubot.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/scripts/ksubot.py b/scripts/ksubot.py index 80604094d248..a4df86033b7b 100644 --- a/scripts/ksubot.py +++ b/scripts/ksubot.py @@ -2,7 +2,6 @@ import os import sys from telethon import TelegramClient -from telethon.tl.functions.help import GetConfigRequest API_ID = 611335 API_HASH = "d524b414d21f4d37f08684c1df41ac9c" @@ -55,7 +54,10 @@ def check_environ(): print("[-] Invalid CHAT_ID") exit(1) else: - CHAT_ID = int(CHAT_ID) + try: + CHAT_ID = int(CHAT_ID) + except: + pass if COMMIT_URL is None: print("[-] Invalid COMMIT_URL") exit(1) @@ -74,11 +76,14 @@ def check_environ(): if BRANCH is None: print("[-] Invalid BRANCH") exit(1) - if MESSAGE_THREAD_ID is None: - print("[-] Invaild MESSAGE_THREAD_ID") - exit(1) + if MESSAGE_THREAD_ID is not None and MESSAGE_THREAD_ID != "": + try: + MESSAGE_THREAD_ID = int(MESSAGE_THREAD_ID) + except: + print("[-] Invaild MESSAGE_THREAD_ID") + exit(1) else: - MESSAGE_THREAD_ID = int(MESSAGE_THREAD_ID) + MESSAGE_THREAD_ID = None async def main(): From 77fffe9c56112ae842af7b8bfd8c7d866564d1e8 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Sun, 7 Sep 2025 16:20:05 +0800 Subject: [PATCH 06/24] ksubot: collect all commits --- .github/workflows/build-manager.yml | 3 +- scripts/ksubot.py | 254 +++++++++++++++------------- 2 files changed, 140 insertions(+), 117 deletions(-) diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index 841dd18077de..36faf46f7699 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -161,8 +161,7 @@ jobs: 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 }} + GITHUB_EVENT: ${{ toJson(github.event) }} RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} TITLE: Manager BRANCH: ${{ github.ref_name }} diff --git a/scripts/ksubot.py b/scripts/ksubot.py index a4df86033b7b..40fb3b3331ca 100644 --- a/scripts/ksubot.py +++ b/scripts/ksubot.py @@ -1,115 +1,139 @@ -import asyncio -import os -import sys -from telethon import TelegramClient - -API_ID = 611335 -API_HASH = "d524b414d21f4d37f08684c1df41ac9c" - - -BOT_TOKEN = os.environ.get("BOT_TOKEN") -CHAT_ID = os.environ.get("CHAT_ID") -MESSAGE_THREAD_ID = os.environ.get("MESSAGE_THREAD_ID") -COMMIT_URL = os.environ.get("COMMIT_URL") -COMMIT_MESSAGE = os.environ.get("COMMIT_MESSAGE") -RUN_URL = os.environ.get("RUN_URL") -TITLE = os.environ.get("TITLE") -VERSION = os.environ.get("VERSION") -BRANCH = os.environ.get("BRANCH") -MSG_TEMPLATE = """ -**{title}** -Branch: {branch} -#ci_{version} -``` -{commit_message} -``` -[Commit]({commit_url}) -[Workflow run]({run_url}) -""".strip() - - -def get_caption(): - msg = MSG_TEMPLATE.format( - title=TITLE, - branch=BRANCH, - version=VERSION, - commit_message=COMMIT_MESSAGE, - commit_url=COMMIT_URL, - run_url=RUN_URL, - ) - if len(msg) > 1024: - msg = COMMIT_URL - if BRANCH == "dev": - msg += "\n⚠️⚠️**DEV VERSION, PLEASE BACKUP BEFORE INSTALLATION**⚠️⚠️" - msg += "\n⚠️⚠️**测试版,安装前请备份**⚠️⚠️" - return msg - - -def check_environ(): - global CHAT_ID, MESSAGE_THREAD_ID - if BOT_TOKEN is None: - print("[-] Invalid BOT_TOKEN") - exit(1) - if CHAT_ID is None: - print("[-] Invalid CHAT_ID") - exit(1) - else: - try: - CHAT_ID = int(CHAT_ID) - except: - pass - if COMMIT_URL is None: - print("[-] Invalid COMMIT_URL") - exit(1) - if COMMIT_MESSAGE is None: - print("[-] Invalid COMMIT_MESSAGE") - exit(1) - if RUN_URL is None: - print("[-] Invalid RUN_URL") - exit(1) - if TITLE is None: - print("[-] Invalid TITLE") - exit(1) - if VERSION is None: - print("[-] Invalid VERSION") - exit(1) - if BRANCH is None: - print("[-] Invalid BRANCH") - exit(1) - if MESSAGE_THREAD_ID is not None and MESSAGE_THREAD_ID != "": - try: - MESSAGE_THREAD_ID = int(MESSAGE_THREAD_ID) - except: - print("[-] Invaild MESSAGE_THREAD_ID") - exit(1) - else: - MESSAGE_THREAD_ID = None - - -async def main(): - print("[+] Uploading to telegram") - check_environ() - files = sys.argv[1:] - print("[+] Files:", files) - if len(files) <= 0: - print("[-] No files to upload") - exit(1) - print("[+] Logging in Telegram with bot") - script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) - session_dir = os.path.join(script_dir, "ksubot") - async with await TelegramClient(session=session_dir, api_id=API_ID, api_hash=API_HASH).start(bot_token=BOT_TOKEN) as bot: - caption = [""] * len(files) - caption[-1] = get_caption() - print("[+] Caption: ") - print("---") - print(caption) - print("---") - print("[+] Sending") - await bot.send_file(entity=CHAT_ID, file=files, caption=caption, reply_to=MESSAGE_THREAD_ID, parse_mode="markdown") - print("[+] Done!") - -if __name__ == "__main__": - try: - asyncio.run(main()) - except Exception as e: - print(f"[-] An error occurred: {e}") +import asyncio +import os +import sys +from telethon import TelegramClient +import json + +API_ID = 611335 +API_HASH = "d524b414d21f4d37f08684c1df41ac9c" + + +BOT_TOKEN = os.environ.get("BOT_TOKEN") +CHAT_ID = os.environ.get("CHAT_ID") +MESSAGE_THREAD_ID = os.environ.get("MESSAGE_THREAD_ID") +TITLE = os.environ.get("TITLE") +VERSION = os.environ.get("VERSION") +BRANCH = os.environ.get("BRANCH") +RUN_URL = os.environ.get("RUN_URL") + +GITHUB_EVENT = json.loads(os.environ.get("GITHUB_EVENT")) + +if 'commits' in GITHUB_EVENT: + commits = GITHUB_EVENT['commits'] + commit_message = '' + i = len(commits) + for commit in commits[::-1]: + msg = commit['message'] + if len(msg) > 256: + msg = msg[:253] + '...' + if len(msg) + 1 + len(commit_message) > 980: + commit_message = f'(other {i} commits)\n{commit_message}' + else: + commit_message = f'{msg}\n{commit_message}' + i -= 1 + commit_message = f'```\n{commit_message.strip()}\n```' +elif 'head_commit' in GITHUB_EVENT: + msg = GITHUB_EVENT["head_commt"]["msg"] + if len(msg) > 256: + msg = msg[:253] + '...' + commit_message = f'```\n{msg.strip()}\n```\n' +else: + commit_message = '' + +if 'compare' in GITHUB_EVENT: + commit_url = GITHUB_EVENT['compare'] + commit_line = '[Compare](' + commit_url + ')\n' +elif 'head_commit' in GITHUB_EVENT: + commit_url = GITHUB_EVENT['head_commit']['url'] + commit_line = '[Commit](' + commit_url + ')\n' +else: + commit_line = '' + + +MSG_TEMPLATE = """ +**{title}** +Branch: {branch} +#ci_{version} +{commit_message}{commit_url}[Workflow run]({run_url}) +""".strip() + + +def get_caption(): + msg = MSG_TEMPLATE.format( + title=TITLE, + branch=BRANCH, + version=VERSION, + commit_message=commit_message, + commit_url=commit_line, + run_url=RUN_URL, + ) + if len(msg) > 1024: + msg = COMMIT_URL + if BRANCH == "dev": + msg += "\n⚠️⚠️**DEV VERSION, PLEASE BACKUP BEFORE INSTALLATION**⚠️⚠️" + msg += "\n⚠️⚠️**测试版,安装前请备份**⚠️⚠️" + return msg + + +def check_environ(): + global CHAT_ID, MESSAGE_THREAD_ID + if BOT_TOKEN is None: + print("[-] Invalid BOT_TOKEN") + exit(1) + if CHAT_ID is None: + print("[-] Invalid CHAT_ID") + exit(1) + else: + try: + CHAT_ID = int(CHAT_ID) + except: + pass + if RUN_URL is None: + print("[-] Invalid RUN_URL") + exit(1) + if TITLE is None: + print("[-] Invalid TITLE") + exit(1) + if VERSION is None: + print("[-] Invalid VERSION") + exit(1) + if BRANCH is None: + print("[-] Invalid BRANCH") + exit(1) + if MESSAGE_THREAD_ID is not None and MESSAGE_THREAD_ID != "": + try: + MESSAGE_THREAD_ID = int(MESSAGE_THREAD_ID) + except: + print("[-] Invaild MESSAGE_THREAD_ID") + exit(1) + else: + MESSAGE_THREAD_ID = None + + +async def main(): + print("[+] Uploading to telegram") + check_environ() + files = sys.argv[1:] + print("[+] Files:", files) + if len(files) <= 0: + print("[-] No files to upload") + exit(1) + print("[+] Logging in Telegram with bot") + script_dir = os.path.dirname(os.path.abspath(sys.argv[0])) + session_dir = os.path.join(script_dir, "ksubot") + async with await TelegramClient(session=session_dir, api_id=API_ID, api_hash=API_HASH).start(bot_token=BOT_TOKEN) as bot: + caption = [""] * len(files) + caption[-1] = get_caption() + print("[+] Caption: ") + print("---") + print(caption) + print("---") + print("[+] Sending") + await bot.send_file(entity=CHAT_ID, file=files, caption=caption, reply_to=MESSAGE_THREAD_ID, parse_mode="markdown") + print("[+] Done!") + +if __name__ == "__main__": + try: + asyncio.run(main()) + except Exception as e: + print(f"[-] An error occurred: {e}") From a5db2bdc7d8032af86fd854b7e72f542b410b95c Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Thu, 6 Nov 2025 15:24:46 +0800 Subject: [PATCH 07/24] ksubot: add upstream diff --- .github/workflows/build-manager.yml | 1 + scripts/ksubot.py | 60 ++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index 36faf46f7699..5b83c9711788 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -10,6 +10,7 @@ on: - 'manager/**' - 'kernel/**' - 'userspace/**' + - 'scripts/ksubot.py' pull_request: branches: [ "main", "dev" ] paths: diff --git a/scripts/ksubot.py b/scripts/ksubot.py index 40fb3b3331ca..1dc63e427630 100644 --- a/scripts/ksubot.py +++ b/scripts/ksubot.py @@ -3,6 +3,7 @@ import sys from telethon import TelegramClient import json +import re API_ID = 611335 API_HASH = "d524b414d21f4d37f08684c1df41ac9c" @@ -18,27 +19,46 @@ GITHUB_EVENT = json.loads(os.environ.get("GITHUB_EVENT")) -if 'commits' in GITHUB_EVENT: - commits = GITHUB_EVENT['commits'] - commit_message = '' - i = len(commits) - for commit in commits[::-1]: - msg = commit['message'] +commit_message = '' +commit_line = '' +upstream_diff = None +try: + if 'commits' in GITHUB_EVENT: + commits = GITHUB_EVENT['commits'] + commit_message = '' + i = len(commits) + for commit in commits[::-1]: + msg_line = commit['message'].split('\n') + msg = msg_line[0].strip() + if len(msg_line) > 1: + msg += ' [..]' + if len(msg) > 100: + msg = msg[:97] + '...' + msg += ' by ' + commit['author']['username'] + if len(msg) + 1 + len(commit_message) > 600: + commit_message = f'(other {i} commits)\n{commit_message}' + break + else: + commit_message = f'{msg}\n{commit_message}' + i -= 1 + commit_message = f'```{commit_message.strip()}\n```' + last_commit = commits[-1] + r = re.search(r'sync with upstream\s+https://github.com/tiann/KernelSU/commit/(.*)\s*', last_commit['message']) + if r is not None: + upstream_commit = r.group(1) + before_commit = GITHUB_EVENT['before'] + repo_url = GITHUB_EVENT['repository']['html_url'] + upstream_diff = f'[Upstream Update]({repo_url}/compare/{before_commit}...{upstream_commit})\n' + elif 'head_commit' in GITHUB_EVENT: + msg = GITHUB_EVENT["head_commt"]["msg"] if len(msg) > 256: msg = msg[:253] + '...' - if len(msg) + 1 + len(commit_message) > 980: - commit_message = f'(other {i} commits)\n{commit_message}' - else: - commit_message = f'{msg}\n{commit_message}' - i -= 1 - commit_message = f'```\n{commit_message.strip()}\n```' -elif 'head_commit' in GITHUB_EVENT: - msg = GITHUB_EVENT["head_commt"]["msg"] - if len(msg) > 256: - msg = msg[:253] + '...' - commit_message = f'```\n{msg.strip()}\n```\n' -else: - commit_message = '' + commit_message = f'```\n{msg.strip()}\n```\n' + else: + commit_message = '' +except: + from traceback import print_exc + print_exc() if 'compare' in GITHUB_EVENT: commit_url = GITHUB_EVENT['compare'] @@ -49,6 +69,8 @@ else: commit_line = '' +if upstream_diff is not None: + commit_message += upstream_diff MSG_TEMPLATE = """ **{title}** From 4479605091512882518d4e7053ed270403d904a4 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Thu, 13 Nov 2025 20:45:08 +0800 Subject: [PATCH 08/24] add switch to init pgrp --- kernel/supercalls.c | 25 +++++++++++++++++++++++++ kernel/supercalls.h | 2 ++ userspace/ksud/src/defs.rs | 2 ++ userspace/ksud/src/ksucalls.rs | 7 +++++++ userspace/ksud/src/module.rs | 11 +++++++++-- 5 files changed, 45 insertions(+), 2 deletions(-) diff --git a/kernel/supercalls.c b/kernel/supercalls.c index 0cce457add13..e69d7859f923 100644 --- a/kernel/supercalls.c +++ b/kernel/supercalls.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "supercalls.h" #include "arch.h" @@ -611,6 +612,26 @@ static int add_try_umount(void __user *arg) return 0; } +static int do_set_init_pgrp(void __user *arg) +{ + int err; + write_lock_irq(&tasklist_lock); + struct task_struct *p = current->group_leader; + struct pid *init_group = task_pgrp(&init_task); + + err = -EPERM; + if (task_session(p) != task_session(&init_task)) + goto out; + + err = 0; + if (task_pgrp(p) != init_group) + change_pid(p, PIDTYPE_PGID, init_group); + +out: + write_unlock_irq(&tasklist_lock); + return err; +} + // IOCTL handlers mapping table static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = { { .cmd = KSU_IOCTL_GRANT_ROOT, @@ -693,6 +714,10 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = { .name = "ADD_TRY_UMOUNT", .handler = add_try_umount, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_SET_INIT_PGRP, + .name = "SET_INIT_PGRP", + .handler = do_set_init_pgrp, + .perm_check = only_root }, { .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL } // Sentinel }; diff --git a/kernel/supercalls.h b/kernel/supercalls.h index 2462ae4097de..f18166cc0dbc 100644 --- a/kernel/supercalls.h +++ b/kernel/supercalls.h @@ -136,6 +136,8 @@ struct ksu_add_try_umount_cmd { #define KSU_IOCTL_NUKE_EXT4_SYSFS _IOC(_IOC_WRITE, 'K', 17, 0) #define KSU_IOCTL_ADD_TRY_UMOUNT _IOC(_IOC_WRITE, 'K', 18, 0) +#define KSU_IOCTL_SET_INIT_PGRP _IOC(_IOC_NONE, 'K', 100, 0) + // IOCTL handler types typedef int (*ksu_ioctl_handler_t)(void __user *arg); typedef bool (*ksu_perm_check_t)(void); diff --git a/userspace/ksud/src/defs.rs b/userspace/ksud/src/defs.rs index 15419b40a0f0..db0aafe957a1 100644 --- a/userspace/ksud/src/defs.rs +++ b/userspace/ksud/src/defs.rs @@ -40,6 +40,8 @@ mod android { pub const KSU_BACKUP_DIR: &str = WORKING_DIR; pub const KSU_BACKUP_FILE_PREFIX: &str = "ksu_backup_"; pub const BACKUP_FILENAME: &str = "stock_image.sha1"; + + pub const USE_INIT_PGRP_PATH: &str = concatcp!(WORKING_DIR, ".initpgrp"); } pub const VERSION_CODE: &str = include_str!(concat!(env!("OUT_DIR"), "/VERSION_CODE")); diff --git a/userspace/ksud/src/ksucalls.rs b/userspace/ksud/src/ksucalls.rs index 6f0fb484bdc3..6044bb4606db 100644 --- a/userspace/ksud/src/ksucalls.rs +++ b/userspace/ksud/src/ksucalls.rs @@ -21,6 +21,7 @@ const KSU_IOCTL_GET_WRAPPER_FD: i32 = _IOW::<()>(K, 15); const KSU_IOCTL_MANAGE_MARK: i32 = _IOWR::<()>(K, 16); const KSU_IOCTL_NUKE_EXT4_SYSFS: i32 = _IOW::<()>(K, 17); const KSU_IOCTL_ADD_TRY_UMOUNT: i32 = _IOW::<()>(K, 18); +const KSU_IOCTL_SET_INIT_PGRP: i32 = _IO(K, 100); #[repr(C)] #[derive(Clone, Copy, Default)] @@ -324,3 +325,9 @@ pub fn umount_list_del(path: &str) -> anyhow::Result<()> { ksuctl(KSU_IOCTL_ADD_TRY_UMOUNT, &raw mut cmd)?; Ok(()) } + +/// Set current process's pgrp to init (0) +pub fn set_init_pgrp() -> std::io::Result<()> { + ksuctl(KSU_IOCTL_SET_INIT_PGRP, std::ptr::null_mut::())?; + Ok(()) +} diff --git a/userspace/ksud/src/module.rs b/userspace/ksud/src/module.rs index d07e8ce094da..0a6d017b7c91 100644 --- a/userspace/ksud/src/module.rs +++ b/userspace/ksud/src/module.rs @@ -205,9 +205,16 @@ pub fn exec_script>(path: T, wait: bool) -> Result<()> { let mut command = &mut Command::new(assets::BUSYBOX_PATH); #[cfg(unix)] { - command = command.process_group(0); + let use_init_pgrp = Path::new(defs::USE_INIT_PGRP_PATH).exists(); + if !use_init_pgrp { + command = command.process_group(0); + } command = unsafe { - command.pre_exec(|| { + command.pre_exec(move || { + if use_init_pgrp && let Err(e) = ksucalls::set_init_pgrp() { + log::error!("failed to set init group: {e:?}"); + libc::setpgid(0, 0); + } // ignore the error? switch_cgroups(); Ok(()) From 488781e543e09047c6d5fa7c1ac04338514c6f56 Mon Sep 17 00:00:00 2001 From: 5ec1cff <56485584+5ec1cff@users.noreply.github.com> Date: Wed, 19 Nov 2025 16:48:17 +0800 Subject: [PATCH 09/24] feature: allow shell & enable adbd --- kernel/allowlist.c | 8 ++ kernel/allowlist.h | 2 + kernel/ksu.c | 7 + .../me/weishu/kernelsu/ui/screen/Flash.kt | 6 +- .../me/weishu/kernelsu/ui/screen/Install.kt | 22 ++- .../java/me/weishu/kernelsu/ui/util/KsuCli.kt | 10 ++ .../src/main/res/values-zh-rCN/strings.xml | 4 + manager/app/src/main/res/values/strings.xml | 4 + userspace/ksud/src/boot_patch.rs | 136 ++++++++++++++---- userspace/ksuinit/src/loader.rs | 8 +- 10 files changed, 180 insertions(+), 27 deletions(-) diff --git a/kernel/allowlist.c b/kernel/allowlist.c index a4b0cd6fa5fa..f931f9ef0729 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -283,6 +283,10 @@ bool __ksu_is_allow_uid(uid_t uid) return true; } + if (unlikely(allow_shell) && uid == 2000) { + return true; + } + if (likely(uid <= BITMAP_UID_MAX)) { return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE))); @@ -339,6 +343,10 @@ void ksu_get_root_profile(uid_t uid, struct root_profile *profile) goto use_default; } + if (unlikely(allow_shell && uid == SHELL_UID)) { + goto use_default; + } + rcu_read_lock(); list_for_each_entry_rcu (p, &allow_list, list) { if (uid == p->profile.current_uid && p->profile.allow_su) { diff --git a/kernel/allowlist.h b/kernel/allowlist.h index 2afc303836c2..d8d122e9a74b 100644 --- a/kernel/allowlist.h +++ b/kernel/allowlist.h @@ -53,3 +53,5 @@ static inline bool is_isolated_process(uid_t uid) return appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID; } #endif + +extern bool allow_shell; diff --git a/kernel/ksu.c b/kernel/ksu.c index b2aabf082fae..144008369cf8 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "allowlist.h" #include "feature.h" @@ -16,6 +17,9 @@ struct cred *ksu_cred; +bool allow_shell = false; +module_param(allow_shell, bool, 0); + int __init kernelsu_init(void) { #ifdef CONFIG_KSU_DEBUG @@ -27,6 +31,9 @@ int __init kernelsu_init(void) pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); pr_alert("*************************************************************"); #endif + if (allow_shell) { + pr_alert("shell is allowed at init!"); + } ksu_cred = prepare_creds(); if (!ksu_cred) { diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt index 2eaefa69d979..912bc56e5e49 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt @@ -263,7 +263,9 @@ sealed class FlashIt : Parcelable { val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean, - val partition: String? = null + val partition: String? = null, + val allowShell: Boolean = false, + val enableAdb: Boolean = false ) : FlashIt() @Parcelize @@ -287,6 +289,8 @@ fun flashIt( flashIt.lkm, flashIt.ota, flashIt.partition, + flashIt.allowShell, + flashIt.enableAdb, onStdout, onStderr ) diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt index 8ea089b8e5ec..269e9e5486d5 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Install.kt @@ -113,6 +113,8 @@ fun InstallScreen() { var partitionSelectionIndex by remember { mutableIntStateOf(0) } var partitionsState by remember { mutableStateOf>(emptyList()) } var hasCustomSelected by remember { mutableStateOf(false) } + var allowShell by remember { mutableStateOf(false) } + var enableAdb by remember { mutableStateOf(false) } val onInstall = { installMethod?.let { method -> @@ -122,7 +124,9 @@ fun InstallScreen() { boot = if (method is InstallMethod.SelectFile) method.uri else null, lkm = lkmSelection, ota = isOta, - partition = partitionSelection + partition = partitionSelection, + allowShell = allowShell, + enableAdb = enableAdb, ) navigator.push(Route.Flash(flashIt)) } @@ -289,6 +293,22 @@ fun InstallScreen() { ) } ) + SuperCheckbox( + title = stringResource(id = R.string.allow_shell), + checked = allowShell, + summary = stringResource(id = R.string.allow_shell_summary), + onCheckedChange = { + allowShell = it + } + ) + SuperCheckbox( + title = stringResource(id = R.string.enable_adb), + checked = enableAdb, + summary = stringResource(id = R.string.enable_adb_summary), + onCheckedChange = { + enableAdb = it + } + ) } TextButton( modifier = Modifier diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt index a548aa3e4580..e05e3ccbaf14 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/util/KsuCli.kt @@ -268,6 +268,8 @@ fun installBoot( lkm: LkmSelection, ota: Boolean, partition: String?, + allowShell: Boolean, + enableAdb: Boolean, onStdout: (String) -> Unit, onStderr: (String) -> Unit, ): FlashResult { @@ -294,6 +296,14 @@ fun installBoot( " -b ${bootFile.absolutePath}" } + if (allowShell) { + cmd += " --allow-shell" + } + + if (enableAdb) { + cmd += " --enable-adbd" + } + if (ota) { cmd += " -u" } diff --git a/manager/app/src/main/res/values-zh-rCN/strings.xml b/manager/app/src/main/res/values-zh-rCN/strings.xml index 3b6f8963073a..7b370532786e 100644 --- a/manager/app/src/main/res/values-zh-rCN/strings.xml +++ b/manager/app/src/main/res/values-zh-rCN/strings.xml @@ -214,4 +214,8 @@ 使用 Apple 风格的悬浮底栏 液态玻璃 启用悬浮底栏的液态玻璃效果 + 总是给 shell 授予 root 权限 + 用于救砖 + 启动时强制启用 ADB 调试 + 用于救砖 diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml index 673dbf0d6822..924eeb609695 100644 --- a/manager/app/src/main/res/values/strings.xml +++ b/manager/app/src/main/res/values/strings.xml @@ -216,4 +216,8 @@ Use Apple style floating bottom bar Liquid glass Enable liquid glass effect for floating bottom bar + Always grant root to Shell + For rescue from bootloop. + Force enable adb on boot + For rescue from bootloop. diff --git a/userspace/ksud/src/boot_patch.rs b/userspace/ksud/src/boot_patch.rs index 6511fd04f0a0..798ab3235647 100644 --- a/userspace/ksud/src/boot_patch.rs +++ b/userspace/ksud/src/boot_patch.rs @@ -1,4 +1,7 @@ #![allow(clippy::ref_option, clippy::needless_pass_by_value)] + +use std::fs::File; +use std::io::Write; #[cfg(unix)] use std::os::unix::fs::PermissionsExt; use std::path::Path; @@ -439,6 +442,7 @@ fn find_boot_image( Ok((bootimage, bootdevice)) } +#[allow(clippy::struct_excessive_bools)] #[derive(clap::Args, Debug)] pub struct BootPatchArgs { /// boot image path, if not specified, will try to find the boot image automatically @@ -487,6 +491,22 @@ pub struct BootPatchArgs { /// File name of the output. #[arg(long, default_value = None)] pub out_name: Option, + + /// Always allow shell to get root permission + #[arg(long, default_value = "false")] + allow_shell: bool, + + /// Force enable adbd and disable adbd auth + #[arg(long, default_value = "false")] + enable_adbd: bool, + + /// Add more adb_debug prop + #[arg(long, required = false)] + adb_debug_prop: Option, + + /// Do not (re-)install kernelsu, only modify configs (allow_shell, etc.) + #[arg(long, default_value = "false")] + no_install: bool, } pub fn patch(args: BootPatchArgs) -> Result<()> { @@ -500,6 +520,10 @@ pub fn patch(args: BootPatchArgs) -> Result<()> { magiskboot: magiskboot_path, kmi, out_name, + allow_shell, + enable_adbd, + adb_debug_prop, + no_install, .. } = args; #[cfg(target_os = "android")] @@ -593,7 +617,7 @@ pub fn patch(args: BootPatchArgs) -> Result<()> { let kmod_file = workdir.join("kernelsu.ko"); if let Some(kmod) = kmod { std::fs::copy(kmod, kmod_file).context("copy kernel module failed")?; - } else { + } else if !no_install { // If kmod is not specified, extract from assets println!("- KMI: {kmi}"); let name = format!("{kmi}_kernelsu.ko"); @@ -604,7 +628,7 @@ pub fn patch(args: BootPatchArgs) -> Result<()> { let init_file = workdir.join("init"); if let Some(init) = init { std::fs::copy(init, init_file).context("copy init failed")?; - } else { + } else if !no_install { assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?; } @@ -630,34 +654,98 @@ pub fn patch(args: BootPatchArgs) -> Result<()> { ramdisk = "ramdisk.cpio".into(); } let ramdisk = ramdisk.as_path(); - let is_magisk_patched = is_magisk_patched(&magiskboot, workdir, ramdisk)?; - ensure!(!is_magisk_patched, "Cannot work with Magisk patched image"); + if !no_install { + let is_magisk_patched = is_magisk_patched(&magiskboot, workdir, ramdisk)?; + ensure!(!is_magisk_patched, "Cannot work with Magisk patched image"); + + println!("- Adding KernelSU LKM"); + let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir, ramdisk)?; + + if !is_kernelsu_patched { + // kernelsu.ko is not exist, backup init if necessary + let status = do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists init"); + if status.is_ok() { + do_cpio_cmd(&magiskboot, workdir, ramdisk, "mv init init.real")?; + } + } - println!("- Adding KernelSU LKM"); - let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir, ramdisk)?; + do_cpio_cmd(&magiskboot, workdir, ramdisk, "add 0755 init init")?; + do_cpio_cmd( + &magiskboot, + workdir, + ramdisk, + "add 0755 kernelsu.ko kernelsu.ko", + )?; - if !is_kernelsu_patched { - // kernelsu.ko is not exist, backup init if necessary - let status = do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists init"); - if status.is_ok() { - do_cpio_cmd(&magiskboot, workdir, ramdisk, "mv init init.real")?; + #[cfg(target_os = "android")] + if !is_kernelsu_patched + && flash + && let Err(e) = do_backup(&magiskboot, workdir, ramdisk, bootimage) + { + println!("- Backup stock image failed: {e}"); } } - do_cpio_cmd(&magiskboot, workdir, ramdisk, "add 0755 init init")?; - do_cpio_cmd( - &magiskboot, - workdir, - ramdisk, - "add 0755 kernelsu.ko kernelsu.ko", - )?; + if allow_shell { + println!("- Adding allow shell config"); + { + let allow_shell_file = workdir.join("ksu_allow_shell"); + File::create(allow_shell_file)?; + } + do_cpio_cmd( + &magiskboot, + workdir, + ramdisk, + "add 0755 ksu_allow_shell ksu_allow_shell", + )?; + } else if do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists ksu_allow_shell").is_ok() { + println!("- Removing allow shell config"); + do_cpio_cmd(&magiskboot, workdir, ramdisk, "rm ksu_allow_shell").ok(); + } - #[cfg(target_os = "android")] - if !is_kernelsu_patched - && flash - && let Err(e) = do_backup(&magiskboot, workdir, ramdisk, bootimage) - { - println!("- Backup stock image failed: {e}"); + if enable_adbd || adb_debug_prop.is_some() { + println!("- Adding adb_debug props"); + { + let force_debuggable_file = workdir.join("force_debuggable"); + File::create(force_debuggable_file)?; + } + do_cpio_cmd( + &magiskboot, + workdir, + ramdisk, + "add 0755 force_debuggable force_debuggable", + )?; + + { + let prop_path = workdir.join("adb_debug.prop"); + let mut prop_file = File::create(prop_path)?; + if enable_adbd { + println!("- Adding props to enable adbd"); + write!( + prop_file, + "ro.debuggable=1\nro.force.debuggable=1\nro.adb.secure=0\n" + )?; + } + if let Some(props) = adb_debug_prop { + println!("- Adding custom props"); + prop_file.write_all(props.as_bytes())?; + } + } + do_cpio_cmd( + &magiskboot, + workdir, + ramdisk, + "add 0755 adb_debug.prop adb_debug.prop", + )?; + } else { + if do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists force_debuggable").is_ok() { + println!("- Removing /force_debuggable"); + do_cpio_cmd(&magiskboot, workdir, ramdisk, "rm force_debuggable").ok(); + } + if do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists adb_debug.prop").is_ok() { + println!("- Removing /adb_debug.prop"); + do_cpio_cmd(&magiskboot, workdir, ramdisk, "rm adb_debug.prop").ok(); + } } println!("- Repacking boot image"); diff --git a/userspace/ksuinit/src/loader.rs b/userspace/ksuinit/src/loader.rs index ef97e1c95009..4713bd373973 100644 --- a/userspace/ksuinit/src/loader.rs +++ b/userspace/ksuinit/src/loader.rs @@ -89,6 +89,12 @@ pub fn load_module(path: &str) -> Result<()> { for ele in modifications { buffer.pwrite_with(ele.0, ele.1, ctx)?; } - init_module(&buffer, cstr!("")).context("init_module failed.")?; + let param = if fs::exists("/ksu_allow_shell").unwrap_or(false) { + log::warn!("ksu allow shell at init!"); + cstr!("allow_shell=1") + } else { + cstr!("") + }; + init_module(&buffer, param).context("init_module failed.")?; Ok(()) } From 310ab80bf575c514cbba79656cfe449b114fbc64 Mon Sep 17 00:00:00 2001 From: 5ec1cff <56485584+5ec1cff@users.noreply.github.com> Date: Mon, 15 Dec 2025 16:17:45 +0800 Subject: [PATCH 10/24] ci: Add workflow_dispatch trigger to build-manager --- .github/workflows/build-manager.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml index 5b83c9711788..3b9c229c83e8 100644 --- a/.github/workflows/build-manager.yml +++ b/.github/workflows/build-manager.yml @@ -22,6 +22,7 @@ on: - 'userspace/**' - 'scripts/ksubot.py' workflow_call: + workflow_dispatch: jobs: build-lkm: @@ -81,14 +82,14 @@ jobs: - name: Setup need_upload id: need_upload run: | - if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then + if [ ! -z "${{ secrets.BOT_TOKEN }}" ] && [ "${{ github.event_name }}" != "workflow_dispatch" ]; 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/dev' )) || github.ref_type == 'tag' }} + if: ${{ github.event_name != 'pull_request' || github.ref_type == 'tag' }} run: | if [ ! -z "${{ secrets.KEYSTORE }}" ]; then { From cf5ca30ac647cc350d597120208e58078ce6b223 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Mon, 22 Dec 2025 08:26:45 +0000 Subject: [PATCH 11/24] kernel: allow use with official manager --- kernel/Kbuild | 5 +++++ kernel/apk_sign.c | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/kernel/Kbuild b/kernel/Kbuild index a918cefb139d..7acd133d42bc 100644 --- a/kernel/Kbuild +++ b/kernel/Kbuild @@ -60,6 +60,9 @@ $(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git repo ccflags-y += -DKSU_VERSION=16 endif +OFFICIAL_EXPECTED_SIZE := 0x033b +OFFICIAL_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6 + ifndef KSU_EXPECTED_SIZE KSU_EXPECTED_SIZE := 384 endif @@ -78,6 +81,8 @@ $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH)) ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE) ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\" +ccflags-y += -DOFFICIAL_EXPECTED_SIZE=$(OFFICIAL_EXPECTED_SIZE) +ccflags-y += -DOFFICIAL_EXPECTED_HASH=\"$(OFFICIAL_EXPECTED_HASH)\" ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat -Wno-missing-prototypes ccflags-y += -Wno-declaration-after-statement -Wno-unused-function diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index 1c84127bd367..65a2f3fe26db 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -360,5 +360,7 @@ bool is_manager_apk(char *path) return false; } #endif - return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH); + return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH) || + check_v2_signature(path, OFFICIAL_EXPECTED_SIZE, + OFFICIAL_EXPECTED_HASH); } \ No newline at end of file From af9284cfd5304f5c53057c33bee0ab977c7ef425 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Wed, 19 Nov 2025 16:14:02 +0800 Subject: [PATCH 12/24] sync with upstream https://github.com/tiann/KernelSU/commit/dc719b58f76d67806093e7ce8cd475362c6b7f73 --- docs/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/README.md b/docs/README.md index 03a9346ea904..3776f1eb15bd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,10 +1,10 @@ **English** | [Español](README_ES.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [日本語](README_JP.md) | [한국어](README_KR.md) | [Polski](README_PL.md) | [Português (Brasil)](README_PT-BR.md) | [Türkçe](README_TR.md) | [Русский](README_RU.md) | [Tiếng Việt](README_VI.md) | [Indonesia](README_ID.md) | [עברית](README_IW.md) | [हिंदी](README_IN.md) | [Italiano](README_IT.md) -# KernelSU +# My KernelSU logo -A kernel-based root solution for Android devices. +A [KernelSU](https://github.com/tiann/KernelSU/commit/dc719b58f76d67806093e7ce8cd475362c6b7f73)-based root solution for Android devices. [![Latest release](https://img.shields.io/github/v/release/tiann/KernelSU?label=Release&logo=github)](https://github.com/tiann/KernelSU/releases/latest) [![Weblate](https://img.shields.io/badge/Localization-Weblate-teal?logo=weblate)](https://hosted.weblate.org/engage/kernelsu) From cd114c36c0aff6fd66e808176c9629f426217dc0 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Mon, 22 Dec 2025 13:57:22 +0000 Subject: [PATCH 13/24] CONFIG_KSU=m --- kernel/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/Makefile b/kernel/Makefile index 28e5a3a802d6..4cda37033424 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -7,7 +7,7 @@ $(info -- MDIR: $(MDIR)) .PHONY: all compdb clean format check-format all: check_symbol - make -C $(KDIR) M=$(MDIR) modules + CONFIG_KSU=m make -C $(KDIR) M=$(MDIR) modules ./check_symbol kernelsu.ko $(KDIR)/vmlinux compdb: python3 $(MDIR)/.vscode/generate_compdb.py -O $(KDIR) $(MDIR) From 1e2e9274a5871db721b6f044c83510e07bdd4f7a Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Mon, 22 Dec 2025 14:00:22 +0000 Subject: [PATCH 14/24] also generate compdb for kernel source code --- .gitignore | 2 +- kernel/.vscode/generate_compdb.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9e7e95876351..79d923791aec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .idea -.vscode +/.vscode CLAUDE.md AGENTS.md \ No newline at end of file diff --git a/kernel/.vscode/generate_compdb.py b/kernel/.vscode/generate_compdb.py index 8866913868f4..22c5a0007d8c 100755 --- a/kernel/.vscode/generate_compdb.py +++ b/kernel/.vscode/generate_compdb.py @@ -44,6 +44,8 @@ def gen_compile_commands(cmd_file_search_path, out_dir): if not cmd_file_search_path: cmd_file_search_path = [out_dir] + else: + cmd_file_search_path += [out_dir] cmd_files = [] for search_path in cmd_file_search_path: From 7d9de7ccda2835eb75ef158cb28df77f8658f623 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Sun, 21 Dec 2025 16:00:31 +0000 Subject: [PATCH 15/24] use syscall table hook --- .github/workflows/build-lkm.yml | 1 + kernel/Kbuild | 29 ++++ kernel/ksud.c | 262 ++++++++++++++++---------------- kernel/pte.c | 101 ++++++++++++ kernel/pte.h | 11 ++ kernel/selinux/sepolicy.c | 18 +-- 6 files changed, 280 insertions(+), 142 deletions(-) create mode 100644 kernel/pte.c create mode 100644 kernel/pte.h diff --git a/.github/workflows/build-lkm.yml b/.github/workflows/build-lkm.yml index 2da532cc0659..e5ed7eac475e 100644 --- a/.github/workflows/build-lkm.yml +++ b/.github/workflows/build-lkm.yml @@ -5,6 +5,7 @@ on: jobs: build-lkm: strategy: + fail-fast: false matrix: kmi: - android12-5.10 diff --git a/kernel/Kbuild b/kernel/Kbuild index 7acd133d42bc..b0b14d6e76cf 100644 --- a/kernel/Kbuild +++ b/kernel/Kbuild @@ -15,6 +15,7 @@ kernelsu-objs += ksud.o kernelsu-objs += seccomp_cache.o kernelsu-objs += file_wrapper.o kernelsu-objs += util.o +kernelsu-objs += pte.o kernelsu-objs += selinux/selinux.o kernelsu-objs += selinux/sepolicy.o @@ -60,6 +61,26 @@ $(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git repo ccflags-y += -DKSU_VERSION=16 endif +KSU_NEW_DCACHE_FLUSH := $(shell grep -q __flush_dcache_area $(srctree)/arch/arm64/include/asm/cacheflush.h ; echo $$?) +$(info -- KSU_NEW_DCACHE_FLUSH: $(KSU_NEW_DCACHE_FLUSH)) + +KSU_MTE_SYNC_TAGS_DEF := $(shell grep 'void mte_sync_tags' $(srctree)/arch/arm64/include/asm/mte.h | grep -v 'static inline') +# 6.6 +KSU_MTE_SYNC_TAGS_NR_PAGES := $(shell echo '$(KSU_MTE_SYNC_TAGS_DEF)' | grep -q 'nr_pages' && echo 1 || echo 0) +# 6.1 +KSU_MTE_SYNC_TAGS_NORMAL := $(shell echo '$(KSU_MTE_SYNC_TAGS_DEF)' | grep -q '(pte_t pte)' && echo 1 || echo 0) +# 13-5.10 +KSU_MTE_SYNC_TAGS_OLD_PTE := $(shell echo '$(KSU_MTE_SYNC_TAGS_DEF)' | grep -q 'old_pte' && echo 1 || echo 0) +# 12-5.10 +KSU_MTE_SYNC_TAGS_PTEP := $(shell echo '$(KSU_MTE_SYNC_TAGS_DEF)' | grep -q '*ptep' && echo 1 || echo 0) +KSU_DEF_MTE_SYNC_TAGS := 'extern $(KSU_MTE_SYNC_TAGS_DEF)' + +$(info -- KSU_DEF_MTE_SYNC_TAGS: $(KSU_DEF_MTE_SYNC_TAGS)) +$(info -- KSU_MTE_SYNC_TAGS_NR_PAGES: $(KSU_MTE_SYNC_TAGS_NR_PAGES)) +$(info -- KSU_MTE_SYNC_TAGS_PTEP: $(KSU_MTE_SYNC_TAGS_PTEP)) +$(info -- KSU_MTE_SYNC_TAGS_OLD_PTE: $(KSU_MTE_SYNC_TAGS_OLD_PTE)) +$(info -- KSU_MTE_SYNC_TAGS_NORMAL: $(KSU_MTE_SYNC_TAGS_NORMAL)) + OFFICIAL_EXPECTED_SIZE := 0x033b OFFICIAL_EXPECTED_HASH := c371061b19d8c7d7d6133c6a9bafe198fa944e50c1b31c9d8daa8d7f1fc2d2d6 @@ -84,6 +105,14 @@ ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\" ccflags-y += -DOFFICIAL_EXPECTED_SIZE=$(OFFICIAL_EXPECTED_SIZE) ccflags-y += -DOFFICIAL_EXPECTED_HASH=\"$(OFFICIAL_EXPECTED_HASH)\" +ccflags-y += -DKSU_NEW_DCACHE_FLUSH=$(KSU_NEW_DCACHE_FLUSH) + +ccflags-y += -DKSU_DEF_MTE_SYNC_TAGS=$(KSU_DEF_MTE_SYNC_TAGS) +ccflags-y += -DKSU_MTE_SYNC_TAGS_NR_PAGES=$(KSU_MTE_SYNC_TAGS_NR_PAGES) +ccflags-y += -DKSU_MTE_SYNC_TAGS_PTEP=$(KSU_MTE_SYNC_TAGS_PTEP) +ccflags-y += -DKSU_MTE_SYNC_TAGS_OLD_PTE=$(KSU_MTE_SYNC_TAGS_OLD_PTE) +ccflags-y += -DKSU_MTE_SYNC_TAGS_NORMAL=$(KSU_MTE_SYNC_TAGS_NORMAL) + ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat -Wno-missing-prototypes ccflags-y += -Wno-declaration-after-statement -Wno-unused-function diff --git a/kernel/ksud.c b/kernel/ksud.c index 715f891fc3a7..c2d2fcadafe8 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -17,13 +17,15 @@ #include #include #include +#include +#include +#include "pte.h" #include "manager.h" #include "allowlist.h" #include "arch.h" #include "klog.h" // IWYU pragma: keep #include "ksud.h" -#include "util.h" #include "selinux/selinux.h" #include "throne_tracker.h" @@ -58,8 +60,6 @@ static void stop_init_rc_hook(); static void stop_execve_hook(); static void stop_input_hook(); -static struct work_struct stop_init_rc_hook_work; -static struct work_struct stop_execve_hook_work; static struct work_struct stop_input_hook_work; void on_post_fs_data(void) @@ -182,14 +182,6 @@ static int __maybe_unused count(struct user_arg_ptr argv, int max) return i; } -static void on_post_fs_data_cbfun(struct callback_head *cb) -{ - on_post_fs_data(); -} - -static struct callback_head on_post_fs_data_cb = { .func = - on_post_fs_data_cbfun }; - static bool check_argv(struct user_arg_ptr argv, int index, const char *expected, char *buf, size_t buf_len) { @@ -215,13 +207,8 @@ static bool check_argv(struct user_arg_ptr argv, int index, return false; } -// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version -int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, - struct user_arg_ptr *argv, - struct user_arg_ptr *envp, int *flags) +void ksu_handle_execveat_ksud(const char *path, struct user_arg_ptr *argv) { - struct filename *filename; - static const char app_process[] = "/system/bin/app_process"; static bool first_zygote = true; @@ -229,17 +216,8 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, static const char system_bin_init[] = "/system/bin/init"; static bool init_second_stage_executed = false; - if (!filename_ptr) - return 0; - - filename = *filename_ptr; - if (IS_ERR(filename)) { - return 0; - } - // https://cs.android.com/android/platform/superproject/+/android-16.0.0_r2:system/core/init/main.cpp;l=77 - if (unlikely(!memcmp(filename->name, system_bin_init, - sizeof(system_bin_init) - 1) && + if (unlikely(!memcmp(path, system_bin_init, sizeof(system_bin_init) - 1) && argv)) { char buf[16]; if (!init_second_stage_executed && @@ -252,26 +230,17 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, } } - if (unlikely( - first_zygote && - !memcmp(filename->name, app_process, sizeof(app_process) - 1) && - argv)) { + if (unlikely(first_zygote && + !memcmp(path, app_process, sizeof(app_process) - 1) && argv)) { char buf[16]; if (check_argv(*argv, 1, "-Xzygote", buf, sizeof(buf))) { pr_info("exec zygote, /data prepared, second_stage: %d\n", init_second_stage_executed); - rcu_read_lock(); - struct task_struct *init_task = - rcu_dereference(current->real_parent); - if (init_task) - task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME); - rcu_read_unlock(); + on_post_fs_data(); first_zygote = false; stop_execve_hook(); } } - - return 0; } static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *); @@ -381,25 +350,21 @@ static bool is_init_rc(struct file *fp) return true; } -static void ksu_handle_sys_read(unsigned int fd) +static void ksu_install_rc_hook(struct file *file) { - struct file *file = fget(fd); - if (!file) { - return; - } - if (!is_init_rc(file)) { - goto skip; + return; } // we only process the first read static bool rc_hooked = false; if (rc_hooked) { - // we don't need these kprobe, unregister it! - stop_init_rc_hook(); - goto skip; + // we don't need these hooks, unregister it! + + return; } rc_hooked = true; + stop_init_rc_hook(); // now we can sure that the init process is reading // `/system/etc/init/init.rc` @@ -421,8 +386,16 @@ static void ksu_handle_sys_read(unsigned int fd) } // replace the file_operations file->f_op = &fops_proxy; +} -skip: +static void ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, + size_t *count_ptr) +{ + struct file *file = fget(fd); + if (!file) { + return; + } + ksu_install_rc_hook(file); fput(file); } @@ -473,76 +446,68 @@ bool ksu_is_safe_mode() return false; } -static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +static long (*orig_sys_execve)(const struct pt_regs *regs); +static long ksu_sys_execve(const 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); + const char __user **filename_user = (const char **)&PT_REGS_PARM1(regs); const char __user *const __user *__argv = - (const char __user *const __user *)PT_REGS_PARM2(real_regs); + (const char __user *const __user *)PT_REGS_PARM2(regs); struct user_arg_ptr argv = { .ptr.native = __argv }; - struct filename filename_in, *filename_p; char path[32]; long ret; unsigned long addr; const char __user *fn; if (!filename_user) - return 0; + goto do_orig; addr = untagged_addr((unsigned long)*filename_user); fn = (const char __user *)addr; memset(path, 0, sizeof(path)); - ret = strncpy_from_user_nofault(path, fn, 32); - if (ret < 0 && try_set_access_flag(addr)) { - ret = strncpy_from_user_nofault(path, fn, 32); - } + ret = strncpy_from_user(path, fn, 32); if (ret < 0) { pr_err("Access filename failed for execve_handler_pre\n"); - return 0; + goto do_orig; } - filename_in.name = path; - filename_p = &filename_in; - return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL); + ksu_handle_execveat_ksud(path, &argv); + +do_orig: + return orig_sys_execve(regs); } -static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) +static long (*orig_sys_read)(const struct pt_regs *regs); +static long ksu_sys_read(const struct pt_regs *regs) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - unsigned int fd = PT_REGS_PARM1(real_regs); + unsigned int fd = PT_REGS_PARM1(regs); + char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(regs); + size_t count_ptr = (size_t *)&PT_REGS_PARM3(regs); - ksu_handle_sys_read(fd); - return 0; + ksu_handle_sys_read(fd, buf_ptr, count_ptr); + return orig_sys_read(regs); } -static int sys_fstat_handler_pre(struct kretprobe_instance *p, - struct pt_regs *regs) +static long (*orig_sys_fstat)(const struct pt_regs *regs); +static long ksu_sys_fstat(const struct pt_regs *regs) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - unsigned int fd = PT_REGS_PARM1(real_regs); - void *statbuf = PT_REGS_PARM2(real_regs); - *(void **)&p->data = NULL; + unsigned int fd = PT_REGS_PARM1(regs); + void *statbuf = PT_REGS_PARM2(regs); + bool is_rc = false; + long ret; struct file *file = fget(fd); - if (!file) - return 1; - if (is_init_rc(file)) { - pr_info("stat init.rc"); + if (file) { + if (is_init_rc(file)) { + pr_info("stat init.rc"); + is_rc = true; + } fput(file); - *(void **)&p->data = statbuf; - return 0; } - fput(file); - return 1; -} -static int sys_fstat_handler_post(struct kretprobe_instance *p, - struct pt_regs *regs) -{ - void __user *statbuf = *(void **)&p->data; - if (statbuf) { + ret = orig_sys_fstat(regs); + + if (is_rc) { void __user *st_size_ptr = statbuf + offsetof(struct stat, st_size); long size, new_size; if (!copy_from_user_nofault(&size, st_size_ptr, sizeof(long))) { @@ -559,7 +524,7 @@ static int sys_fstat_handler_post(struct kretprobe_instance *p, } } - return 0; + return ret; } static int input_handle_event_handler_pre(struct kprobe *p, @@ -571,39 +536,74 @@ static int input_handle_event_handler_pre(struct kprobe *p, return ksu_handle_input_handle_event(type, code, value); } -static struct kprobe execve_kp = { - .symbol_name = SYS_EXECVE_SYMBOL, - .pre_handler = sys_execve_handler_pre, -}; +// This function appears in 5.14: +// https://github.com/torvalds/linux/commit/fade9c2c6ee2baea7df8e6059b3f143c681e5ce4#diff-fc9ef24572e183c6c049b5ae8029762159787f8669d909452bdf40db748f94a7L52 +// https://github.com/torvalds/linux/commit/814b186079cd54d3fe3b6b8ab539cbd44705ef9d#diff-fc9ef24572e183c6c049b5ae8029762159787f8669d909452bdf40db748f94a7R53 +// However, it's backport to android13-5.10 but not to android12-5.10. +// https://cs.android.com/android/_/android/kernel/common/+/6d9f07d8f1ffc310a6877153fe882f35ae380799 +// So we need to grep kernel source code to detect which one to use. +#if KSU_NEW_DCACHE_FLUSH +#define ksu_flush_dcache(start, sz) \ + ({ \ + unsigned long __start = (start); \ + unsigned long __end = __start + (sz); \ + dcache_clean_inval_poc(__start, __end); \ + }) +#else +#define ksu_flush_dcache(start, sz) __flush_dcache_area((void *)start, sz) +#endif -static struct kprobe sys_read_kp = { - .symbol_name = SYS_READ_SYMBOL, - .pre_handler = sys_read_handler_pre, -}; +static syscall_fn_t *syscall_table = NULL; -static struct kretprobe sys_fstat_kp = { - .kp.symbol_name = SYS_FSTAT_SYMBOL, - .entry_handler = sys_fstat_handler_pre, - .handler = sys_fstat_handler_post, - .data_size = sizeof(void *), -}; +static void replace_syscall_table(int nr, syscall_fn_t fn, syscall_fn_t *old) +{ + pte_t orig_pte, pte; + pte_t *ptep = page_from_virt((uintptr_t)&syscall_table[nr]); + if (ptep == NULL) { + pr_err("Failed to get PTE for syscall_table[%d]\n", nr); + return; + } + pr_info("syscall 0x%lx ptep=0x%lx pte=0x%lx", (uintptr_t)&syscall_table[nr], + (uintptr_t)ptep, (uintptr_t)ptep->pte); + syscall_fn_t *orig_p = &syscall_table[nr], orig = READ_ONCE(*orig_p); + orig_pte = READ_ONCE(*ptep); + if (old) { + *old = orig; + dmb(ishst); + } + + pte_t *ptep_ptep = page_from_virt((uintptr_t)ptep); + pr_info("syscall_ptep 0x%lx ptep=0x%lx pte=0x%lx", (uintptr_t)ptep, + (uintptr_t)ptep_ptep, (uintptr_t)ptep_ptep->pte); + + pr_info("Before hook syscall %d, ptr=0x%lx, *ptr=0x%lx -> 0x%lx", nr, + (unsigned long)orig_p, (unsigned long)orig, (uintptr_t)fn); + + pte = set_pte_bit(orig_pte, __pgprot(PTE_DBM)); + pte = clear_pte_bit(pte, __pgprot(PTE_RDONLY)); + ksu_set_pte(ptep, pte); + flush_tlb_all(); + pr_info("Before hook modified pte=%lx", + (uintptr_t)pte_val(READ_ONCE(*ptep))); + + syscall_table[nr] = fn; + + ksu_set_pte(ptep, orig_pte); + flush_tlb_all(); + + ksu_flush_dcache(&syscall_table[nr], sizeof(void *)); + + pr_info("After hook syscall %d, ptr=0x%lx, *ptr=0x%lx", nr, + (unsigned long)orig_p, (unsigned long)READ_ONCE(syscall_table[nr])); + pr_info("After hook modified pte=%lx", + (uintptr_t)pte_val(READ_ONCE(*ptep))); +} static struct kprobe input_event_kp = { .symbol_name = "input_event", .pre_handler = input_handle_event_handler_pre, }; -static void do_stop_init_rc_hook(struct work_struct *work) -{ - unregister_kprobe(&sys_read_kp); - unregister_kretprobe(&sys_fstat_kp); -} - -static void do_stop_execve_hook(struct work_struct *work) -{ - unregister_kprobe(&execve_kp); -} - static void do_stop_input_hook(struct work_struct *work) { unregister_kprobe(&input_event_kp); @@ -611,14 +611,15 @@ static void do_stop_input_hook(struct work_struct *work) static void stop_init_rc_hook() { - bool ret = schedule_work(&stop_init_rc_hook_work); - pr_info("unregister init_rc_hook kprobe: %d!\n", ret); + replace_syscall_table(__NR_read, orig_sys_read, NULL); + replace_syscall_table(__NR_fstat, orig_sys_fstat, NULL); + pr_info("unregister init_rc syscall hook\n"); } static void stop_execve_hook() { - bool ret = schedule_work(&stop_execve_hook_work); - pr_info("unregister execve kprobe: %d!\n", ret); + replace_syscall_table(__NR_execve, orig_sys_execve, NULL); + pr_info("unhook sys_execve\n"); } static void stop_input_hook() @@ -636,28 +637,23 @@ static void stop_input_hook() void ksu_ksud_init() { int ret; + syscall_table = kallsyms_lookup_name("sys_call_table"); - ret = register_kprobe(&execve_kp); - pr_info("ksud: execve_kp: %d\n", ret); - - ret = register_kprobe(&sys_read_kp); - pr_info("ksud: sys_read_kp: %d\n", ret); - - ret = register_kretprobe(&sys_fstat_kp); - pr_info("ksud: sys_fstat_kp: %d\n", ret); + replace_syscall_table(__NR_execve, ksu_sys_execve, &orig_sys_execve); + replace_syscall_table(__NR_read, ksu_sys_read, &orig_sys_read); + replace_syscall_table(__NR_fstat, ksu_sys_fstat, &orig_sys_fstat); ret = register_kprobe(&input_event_kp); pr_info("ksud: input_event_kp: %d\n", ret); - INIT_WORK(&stop_init_rc_hook_work, do_stop_init_rc_hook); - INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook); INIT_WORK(&stop_input_hook_work, do_stop_input_hook); } void ksu_ksud_exit() { - unregister_kprobe(&execve_kp); - // this should be done before unregister sys_read_kp - // unregister_kprobe(&sys_read_kp); + stop_execve_hook(); + // TODO: + // this should be done before unregister vfs_read_kp + // stop_init_rc_hook(); unregister_kprobe(&input_event_kp); } diff --git a/kernel/pte.c b/kernel/pte.c new file mode 100644 index 000000000000..70aa3989adad --- /dev/null +++ b/kernel/pte.c @@ -0,0 +1,101 @@ +#include "klog.h" // IWYU pragma: keep +#include "pte.h" + +// https://github.com/fuqiuluo/ovo/blob/f7da411458e87d32438dc14fce5a3313ed0c967e/ovo/mmuhack.c#L21 +pte_t *page_from_virt(unsigned long addr) +{ + struct mm_struct *mm = &init_mm; + pgd_t *pgd; + p4d_t *p4d; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + pgd = pgd_offset(mm, addr); + if (pgd_none(*pgd) || pgd_bad(*pgd)) + return NULL; + pr_info("pgd of 0x%lx p=0x%lx v=0x%lx", addr, (uintptr_t)pgd, + (uintptr_t)pgd_val(*pgd)); + + p4d = p4d_offset(pgd, addr); + if (p4d_none(*p4d) || p4d_bad(*p4d)) + return NULL; + pr_info("p4d of 0x%lx p=0x%lx v=0x%lx", addr, (uintptr_t)p4d, + (uintptr_t)p4d_val(*p4d)); + + pud = pud_offset(p4d, addr); +#if defined(pud_leaf) + if (pud_leaf(*pud)) { + pr_info( + "Address 0x%lx maps to a PUD-level huge page, returning PUD entry as PTE\n", + addr); + return (pte_t *)pud; + } +#endif + if (pud_none(*pud) || pud_bad(*pud)) + return NULL; + pr_info("pud of 0x%lx p=0x%lx v=0x%lx", addr, (uintptr_t)pud, + (uintptr_t)pud_val(*pud)); + + pmd = pmd_offset(pud, addr); +#if defined(pmd_leaf) + if (pmd_leaf(*pmd)) { + pr_info( + "Address 0x%lx maps to a PMD-level huge page, returning PMD entry as PTE\n", + addr); + return (pte_t *)pmd; + } +#endif + pr_info("pmd of 0x%lx p=0x%lx v=0x%lx", addr, (uintptr_t)pmd, + (uintptr_t)pmd_val(*pmd)); + + if (pmd_none(*pmd) || pmd_bad(*pmd)) + return NULL; + + pte = pte_offset_kernel(pmd, addr); + if (!pte) + return NULL; + if (!pte_present(*pte)) + return NULL; + + return pte; +} + +KSU_DEF_MTE_SYNC_TAGS; + +#if (KSU_MTE_SYNC_TAGS_NR_PAGES + KSU_MTE_SYNC_TAGS_PTEP + \ + KSU_MTE_SYNC_TAGS_OLD_PTE + KSU_MTE_SYNC_TAGS_NORMAL) != 1 +#error "Unsupported mte_sync_tags" +#else +#if KSU_MTE_SYNC_TAGS_NR_PAGES +#define mte_sync_tags_compat(_ptep, _old_pte, new_pte, nr_pages) \ + mte_sync_tags(new_pte, nr_pages) +#elif KSU_MTE_SYNC_TAGS_PTEP +#define mte_sync_tags_compat(ptep, _old_pte, new_pte, _nr_pages) \ + mte_sync_tags(ptep, new_pte) +#elif KSU_MTE_SYNC_TAGS_OLD_PTE +#define mte_sync_tags_compat(_ptep, old_pte, new_pte, _nr_pages) \ + mte_sync_tags(old_pte, new_pte) +#elif KSU_MTE_SYNC_TAGS_NORMAL +#define mte_sync_tags_compat(_ptep, _old_pte, new_pte, _nr_pages) \ + mte_sync_tags(new_pte); +#endif +#endif + +// https://cs.android.com/android/kernel/superproject/+/common-android16-6.12:common/arch/arm64/include/asm/pgtable.h;l=643-645;drc=4bca12f98057dba0efac9881b21fad39274715ef +void ksu_set_pte(pte_t *ptep, pte_t pte) +{ + pte_t old_pte __maybe_unused = READ_ONCE(*ptep); + + if (pte_present(pte) && pte_user_exec(pte) && !pte_special(pte)) + __sync_icache_dcache(pte); + + if (system_supports_mte() && pte_access_permitted(pte, false) && + !pte_special(pte) && pte_tagged(pte)) { + mte_sync_tags_compat(ptep, old_pte, pte, 1); + } + + WRITE_ONCE(*ptep, pte); + dsb(ishst); + isb(); +} diff --git a/kernel/pte.h b/kernel/pte.h new file mode 100644 index 000000000000..fa4dd8095458 --- /dev/null +++ b/kernel/pte.h @@ -0,0 +1,11 @@ +#ifndef __PTE_H +#define __PTE_H + +#include +#include + +pte_t *page_from_virt(unsigned long addr); + +void ksu_set_pte(pte_t *ptep, pte_t pte); + +#endif diff --git a/kernel/selinux/sepolicy.c b/kernel/selinux/sepolicy.c index 84a1ffecfe6d..0e119104bfb7 100644 --- a/kernel/selinux/sepolicy.c +++ b/kernel/selinux/sepolicy.c @@ -339,7 +339,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src, if (datum->u.xperms == NULL) { datum->u.xperms = (struct avtab_extended_perms *)(kzalloc( - sizeof(xperms), GFP_ATOMIC)); + sizeof(xperms), GFP_KERNEL)); if (!datum->u.xperms) { pr_err("alloc xperms failed\n"); return; @@ -529,11 +529,11 @@ static bool add_filename_trans(struct policydb *db, const char *s, if (trans == NULL) { trans = (struct filename_trans_datum *)kcalloc(1, sizeof(*trans), - GFP_ATOMIC); + GFP_KERNEL); struct filename_trans_key *new_key = - (struct filename_trans_key *)kzalloc(sizeof(*new_key), GFP_ATOMIC); + (struct filename_trans_key *)kzalloc(sizeof(*new_key), GFP_KERNEL); *new_key = key; - new_key->name = kstrdup(key.name, GFP_ATOMIC); + new_key->name = kstrdup(key.name, GFP_KERNEL); trans->next = last; trans->otype = def->value; hashtab_insert(&db->filename_trans, new_key, trans, @@ -552,11 +552,11 @@ static bool add_genfscon(struct policydb *db, const char *fs_name, // https://github.com/torvalds/linux/commit/590b9d576caec6b4c46bba49ed36223a399c3fc5#diff-cc9aa90e094e6e0f47bd7300db4f33cf4366b98b55d8753744f31eb69c691016R844-R845 #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -#define ksu_kvrealloc(p, new_size, _old_size) kvrealloc(p, new_size, GFP_ATOMIC) +#define ksu_kvrealloc(p, new_size, _old_size) kvrealloc(p, new_size, GFP_KERNEL) // https://github.com/torvalds/linux/commit/de2860f4636256836450c6543be744a50118fc66#diff-fa19cdd9c3369d7f59aa2e8404628109408dbf8e1b568d1157a27328f75b8410R638-R652 #elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) #define ksu_kvrealloc(p, new_size, old_size) \ - kvrealloc(p, old_size, new_size, GFP_ATOMIC) + kvrealloc(p, old_size, new_size, GFP_KERNEL) #else // https://cs.android.com/android/_/android/kernel/common/+/f5f3e54f811679761c33526e695bd296190faade // Some 5.10 kernel don't have this backport, so copy one. @@ -575,7 +575,7 @@ void *ksu_kvrealloc_compat(const void *p, size_t oldsize, size_t newsize, return newp; } #define ksu_kvrealloc(p, new_size, old_size) \ - ksu_kvrealloc_compat(p, old_size, new_size, GFP_ATOMIC) + ksu_kvrealloc_compat(p, old_size, new_size, GFP_KERNEL) #endif static bool add_type(struct policydb *db, const char *type_name, bool attr) @@ -587,7 +587,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) } u32 value = ++db->p_types.nprim; - type = (struct type_datum *)kzalloc(sizeof(struct type_datum), GFP_ATOMIC); + type = (struct type_datum *)kzalloc(sizeof(struct type_datum), GFP_KERNEL); if (!type) { pr_err("add_type: alloc type_datum failed.\n"); return false; @@ -597,7 +597,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) type->value = value; type->attribute = attr; - char *key = kstrdup(type_name, GFP_ATOMIC); + char *key = kstrdup(type_name, GFP_KERNEL); if (!key) { pr_err("add_type: alloc key failed.\n"); return false; From e356be510e032696c0a827a8e050eae16e803a04 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Fri, 2 Jan 2026 10:52:42 +0000 Subject: [PATCH 16/24] try blacklist mkp --- kernel/ksud.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/kernel/ksud.c b/kernel/ksud.c index c2d2fcadafe8..9ce02bdc635b 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -207,6 +207,8 @@ static bool check_argv(struct user_arg_ptr argv, int index, return false; } +static void stop_finit_module_hook(); + void ksu_handle_execveat_ksud(const char *path, struct user_arg_ptr *argv) { static const char app_process[] = "/system/bin/app_process"; @@ -223,6 +225,7 @@ void ksu_handle_execveat_ksud(const char *path, struct user_arg_ptr *argv) if (!init_second_stage_executed && check_argv(*argv, 1, "second_stage", buf, sizeof(buf))) { pr_info("/system/bin/init second_stage executed\n"); + stop_finit_module_hook(); apply_kernelsu_rules(); cache_sid(); setup_ksu_cred(); @@ -527,6 +530,36 @@ static long ksu_sys_fstat(const struct pt_regs *regs) return ret; } +static long (*orig_finit_module)(const struct pt_regs *regs); +static long ksu_finit_module(const struct pt_regs *regs) +{ + // https://cs.android.com/android/platform/superproject/main/+/main:system/core/libmodprobe/libmodprobe_ext.cpp;l=53;drc=61197364367c9e404c7da6900658f1b16c42d0da + unsigned int fd = PT_REGS_PARM1(regs); + const char *name; + + struct file *file = fget(fd); + if (!file) { + goto call_orig; + } + + name = file->f_path.dentry->d_name.name; + pr_info("loading kernel module %s", name); + + if (strcmp(name, "mkp.ko") == 0) { + goto skip_load; + } + + fput(file); + +call_orig: + return orig_finit_module(regs); + +skip_load: + fput(file); + pr_info("skip load %s", name); + return 0; +} + static int input_handle_event_handler_pre(struct kprobe *p, struct pt_regs *regs) { @@ -622,6 +655,11 @@ static void stop_execve_hook() pr_info("unhook sys_execve\n"); } +static void stop_finit_module_hook() +{ + replace_syscall_table(__NR_finit_module, orig_finit_module, NULL); +} + static void stop_input_hook() { static bool input_hook_stopped = false; @@ -642,6 +680,8 @@ void ksu_ksud_init() replace_syscall_table(__NR_execve, ksu_sys_execve, &orig_sys_execve); replace_syscall_table(__NR_read, ksu_sys_read, &orig_sys_read); replace_syscall_table(__NR_fstat, ksu_sys_fstat, &orig_sys_fstat); + replace_syscall_table(__NR_finit_module, ksu_finit_module, + &orig_finit_module); ret = register_kprobe(&input_event_kp); pr_info("ksud: input_event_kp: %d\n", ret); From 6a6a7ac59a33561418c2961dc8ed7d35f63cdd51 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Wed, 25 Feb 2026 17:57:36 +0800 Subject: [PATCH 17/24] inline hook tracepoint --- kernel/.gitignore | 2 +- kernel/Kbuild | 3 + kernel/hook.h | 88 +++++++ kernel/inline_hook.c | 482 ++++++++++++++++++++++++++++++++++ kernel/patch.c | 77 ++++++ kernel/syscall_hook_manager.c | 18 +- kernel/syscall_hook_manager.h | 6 + kernel/trampoline.S | 53 ++++ 8 files changed, 725 insertions(+), 4 deletions(-) create mode 100644 kernel/hook.h create mode 100644 kernel/inline_hook.c create mode 100644 kernel/patch.c create mode 100644 kernel/trampoline.S diff --git a/kernel/.gitignore b/kernel/.gitignore index 15547df1c7de..85fecbf8cdb4 100644 --- a/kernel/.gitignore +++ b/kernel/.gitignore @@ -12,7 +12,7 @@ compile_commands.json *.mod.c *.symvers* *.order -.*.ko.cmd +.*.cmd .tmp_versions/ libs/ obj/ diff --git a/kernel/Kbuild b/kernel/Kbuild index b0b14d6e76cf..5c43208f16ca 100644 --- a/kernel/Kbuild +++ b/kernel/Kbuild @@ -16,6 +16,9 @@ kernelsu-objs += seccomp_cache.o kernelsu-objs += file_wrapper.o kernelsu-objs += util.o kernelsu-objs += pte.o +kernelsu-objs += patch.o +kernelsu-objs += inline_hook.o +kernelsu-objs += trampoline.o kernelsu-objs += selinux/selinux.o kernelsu-objs += selinux/sepolicy.o diff --git a/kernel/hook.h b/kernel/hook.h new file mode 100644 index 000000000000..470baa59de69 --- /dev/null +++ b/kernel/hook.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023 bmax121. All Rights Reserved. + */ + +#ifndef __KSU_H_HOOK_ +#define __KSU_H_HOOK_ +#include "linux/types.h" // IWYU pragma: keep +#include "linux/version.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0) +#include "asm/patching.h" // IWYU pragma: keep +#else +#include "asm/insn.h" // IWYU pragma: keep +#endif + +// https://github.com/bmax121/KernelPatch/blob/94e5be9cc3f8a6fbd9574155e3e9753200ab9bfb/kernel/include/hook.h#L54 + +#define HOOK_INTO_BRANCH_FUNC + +typedef enum { + HOOK_NO_ERR = 0, + HOOK_BAD_ADDRESS = 4095, + HOOK_DUPLICATED = 4094, + HOOK_NO_MEM = 4093, + HOOK_BAD_RELO = 4092, + HOOK_TRANSIT_NO_MEM = 4091, + HOOK_CHAIN_FULL = 4090, +} hook_err_t; + +#define HOOK_MEM_REGION_NUM 4 +#define TRAMPOLINE_MAX_NUM 6 +#define RELOCATE_INST_NUM (4 * 8 + 8 - 4) + +#define HOOK_CHAIN_NUM 0x10 + +#define ARM64_NOP 0xd503201f +#define ARM64_BTI_C 0xd503245f +#define ARM64_BTI_J 0xd503249f +#define ARM64_BTI_JC 0xd50324df +#define ARM64_PACIASP 0xd503233f +#define ARM64_PACIBSP 0xd503237f + +typedef struct { + // in + uint64_t func_addr; + uint64_t origin_addr; + uint64_t replace_addr; + uint64_t relo_addr; + // out + int32_t tramp_insts_num; + int32_t relo_insts_num; + uint32_t origin_insts[TRAMPOLINE_MAX_NUM] __attribute__((aligned(8))); + uint32_t tramp_insts[TRAMPOLINE_MAX_NUM] __attribute__((aligned(8))); + uint32_t relo_insts[RELOCATE_INST_NUM] __attribute__((aligned(8))); +} hook_t __attribute__((aligned(8))); + +static inline int is_bad_address(void *addr) +{ + return ((uint64_t)addr & 0x8000000000000000) != 0x8000000000000000; +} + +int32_t branch_from_to(uint32_t *tramp_buf, uint64_t src_addr, + uint64_t dst_addr); +int32_t branch_relative(uint32_t *buf, uint64_t src_addr, uint64_t dst_addr); +int32_t branch_absolute(uint32_t *buf, uint64_t addr); +int32_t ret_absolute(uint32_t *buf, uint64_t addr); + +hook_err_t hook_prepare(hook_t *hook); +void hook_install(hook_t *hook); + +/** + * @brief Inline-hook function which address is @param func with function @param replace, + * after hook, original @param func is backuped in @param backup. + * + * @note If multiple modules hook this function simultaneously, + * it will cause abnormality when unload the modules. Please use hook_wrap instead + * + * @see hook_wrap + * + * @param func + * @param replace + * @param backup + * @return hook_err_t + */ +hook_err_t hook(void *func, void *replace, void **backup); + +#endif diff --git a/kernel/inline_hook.c b/kernel/inline_hook.c new file mode 100644 index 000000000000..12299461f98f --- /dev/null +++ b/kernel/inline_hook.c @@ -0,0 +1,482 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2023 bmax121. All Rights Reserved. + */ +#include "hook.h" +#include "klog.h" // IWYU pragma: keep +#include "linux/gfp.h" // IWYU pragma: keep +#include "linux/vmalloc.h" + +#include "asm/pgtable.h" + +// https://github.com/bmax121/KernelPatch/blob/94e5be9cc3f8a6fbd9574155e3e9753200ab9bfb/kernel/base/hook.c#L562-L603 + +#define bits32(n, high, low) \ + ((uint32_t)((n) << (31u - (high))) >> (31u - (high) + (low))) +#define bit(n, st) (((n) >> (st)) & 1) +#define sign64_extend(n, len) \ + (((uint64_t)((n) << (63u - (len - 1))) >> 63u) ? \ + ((n) | (0xFFFFFFFFFFFFFFFF << (len))) : \ + n) +#define align_ceil(x, align) \ + (((u64)(x) + (u64)(align) - 1) & ~((u64)(align) - 1)) + +typedef uint32_t inst_type_t; +typedef uint32_t inst_mask_t; + +#define INST_B 0x14000000 +#define INST_BC 0x54000000 +#define INST_BL 0x94000000 +#define INST_ADR 0x10000000 +#define INST_ADRP 0x90000000 +#define INST_LDR_32 0x18000000 +#define INST_LDR_64 0x58000000 +#define INST_LDRSW_LIT 0x98000000 +#define INST_PRFM_LIT 0xD8000000 +#define INST_LDR_SIMD_32 0x1C000000 +#define INST_LDR_SIMD_64 0x5C000000 +#define INST_LDR_SIMD_128 0x9C000000 +#define INST_CBZ 0x34000000 +#define INST_CBNZ 0x35000000 +#define INST_TBZ 0x36000000 +#define INST_TBNZ 0x37000000 +#define INST_HINT 0xD503201F +#define INST_IGNORE 0x0 + +#define MASK_B 0xFC000000 +#define MASK_BC 0xFF000010 +#define MASK_BL 0xFC000000 +#define MASK_ADR 0x9F000000 +#define MASK_ADRP 0x9F000000 +#define MASK_LDR_32 0xFF000000 +#define MASK_LDR_64 0xFF000000 +#define MASK_LDRSW_LIT 0xFF000000 +#define MASK_PRFM_LIT 0xFF000000 +#define MASK_LDR_SIMD_32 0xFF000000 +#define MASK_LDR_SIMD_64 0xFF000000 +#define MASK_LDR_SIMD_128 0xFF000000 +#define MASK_CBZ 0x7F000000u +#define MASK_CBNZ 0x7F000000u +#define MASK_TBZ 0x7F000000u +#define MASK_TBNZ 0x7F000000u +#define MASK_HINT 0xFFFFF01F +#define MASK_IGNORE 0x0 + +static inst_mask_t masks[] = { + MASK_B, MASK_BC, MASK_BL, MASK_ADR, + MASK_ADRP, MASK_LDR_32, MASK_LDR_64, MASK_LDRSW_LIT, + MASK_PRFM_LIT, MASK_LDR_SIMD_32, MASK_LDR_SIMD_64, MASK_LDR_SIMD_128, + MASK_CBZ, MASK_CBNZ, MASK_TBZ, MASK_TBNZ, + MASK_IGNORE, +}; +static inst_type_t types[] = { + INST_B, INST_BC, INST_BL, INST_ADR, + INST_ADRP, INST_LDR_32, INST_LDR_64, INST_LDRSW_LIT, + INST_PRFM_LIT, INST_LDR_SIMD_32, INST_LDR_SIMD_64, INST_LDR_SIMD_128, + INST_CBZ, INST_CBNZ, INST_TBZ, INST_TBNZ, + INST_IGNORE, +}; + +static int32_t relo_len[] = { + 6, 8, 8, 4, 4, 6, 6, 6, 8, 8, 8, 8, 6, 6, 6, 6, 2 +}; + +// static uint64_t sign_extend(uint64_t x, uint32_t len) +// { +// char sign_bit = bit(x, len - 1); +// unsigned long sign_mask = 0 - sign_bit; +// x |= ((sign_mask >> len) << len); +// return x; +// } + +static int is_in_tramp(hook_t *hook, uint64_t addr) +{ + uint64_t tramp_start = hook->origin_addr; + uint64_t tramp_end = tramp_start + hook->tramp_insts_num * 4; + if (addr >= tramp_start && addr < tramp_end) { + return 1; + } + return 0; +} + +static uint64_t relo_in_tramp(hook_t *hook, uint64_t addr) +{ + uint64_t tramp_start = hook->origin_addr; + uint64_t tramp_end = tramp_start + hook->tramp_insts_num * 4; + if (!(addr >= tramp_start && addr < tramp_end)) + return addr; + uint32_t addr_inst_index = (addr - tramp_start) / 4; + uint64_t fix_addr = hook->relo_addr; + for (int i = 0; i < addr_inst_index; i++) { + inst_type_t inst = hook->origin_insts[i]; + for (int j = 0; j < sizeof(relo_len) / sizeof(relo_len[0]); j++) { + if ((inst & masks[j]) == types[j]) { + fix_addr += relo_len[j] * 4; + break; + } + } + } + return fix_addr; +} + +#ifdef HOOK_INTO_BRANCH_FUNC + +#endif + +static hook_err_t relo_b(hook_t *hook, uint64_t inst_addr, uint32_t inst, + inst_type_t type) +{ + uint32_t *buf = hook->relo_insts + hook->relo_insts_num; + uint64_t imm64; + if (type == INST_BC) { + uint64_t imm19 = bits32(inst, 23, 5); + imm64 = sign64_extend(imm19 << 2u, 21u); + } else { + uint64_t imm26 = bits32(inst, 25, 0); + imm64 = sign64_extend(imm26 << 2u, 28u); + } + uint64_t addr = inst_addr + imm64; + addr = relo_in_tramp(hook, addr); + + uint32_t idx = 0; + if (type == INST_BC) { + buf[idx++] = (inst & 0xFF00001F) | 0x40u; // B. #8 + buf[idx++] = 0x14000006; // B #24 + } + buf[idx++] = 0x58000051; // LDR X17, #8 + buf[idx++] = 0x14000003; // B #12 + buf[idx++] = addr & 0xFFFFFFFF; + buf[idx++] = addr >> 32u; + if (type == INST_BL) { + buf[idx++] = 0x1000001E; // ADR X30, . + buf[idx++] = 0x910033DE; // ADD X30, X30, #12 + buf[idx++] = 0xD65F0220; // RET X17 + } else { + buf[idx++] = 0xD65F0220; // RET X17 + } + buf[idx++] = ARM64_NOP; + return HOOK_NO_ERR; +} + +static hook_err_t relo_adr(hook_t *hook, uint64_t inst_addr, uint32_t inst, + inst_type_t type) +{ + uint32_t *buf = hook->relo_insts + hook->relo_insts_num; + + uint32_t xd = bits32(inst, 4, 0); + uint64_t immlo = bits32(inst, 30, 29); + uint64_t immhi = bits32(inst, 23, 5); + uint64_t addr; + + if (type == INST_ADR) { + addr = inst_addr + sign64_extend((immhi << 2u) | immlo, 21u); + } else { + addr = + (inst_addr + sign64_extend((immhi << 14u) | (immlo << 12u), 33u)) & + 0xFFFFFFFFFFFFF000; + if (is_in_tramp(hook, addr)) + return -HOOK_BAD_RELO; + } + buf[0] = 0x58000040u | xd; // LDR Xd, #8 + buf[1] = 0x14000003; // B #12 + buf[2] = addr & 0xFFFFFFFF; + buf[3] = addr >> 32u; + return HOOK_NO_ERR; +} + +static hook_err_t relo_ldr(hook_t *hook, uint64_t inst_addr, uint32_t inst, + inst_type_t type) +{ + uint32_t *buf = hook->relo_insts + hook->relo_insts_num; + + uint32_t rt = bits32(inst, 4, 0); + uint64_t imm19 = bits32(inst, 23, 5); + uint64_t offset = sign64_extend((imm19 << 2u), 21u); + uint64_t addr = inst_addr + offset; + + if (is_in_tramp(hook, addr) && type != INST_PRFM_LIT) + return -HOOK_BAD_RELO; + + addr = relo_in_tramp(hook, addr); + + if (type == INST_LDR_32 || type == INST_LDR_64 || type == INST_LDRSW_LIT) { + buf[0] = 0x58000060u | rt; // LDR Xt, #12 + if (type == INST_LDR_32) { + buf[1] = 0xB9400000 | rt | (rt << 5u); // LDR Wt, [Xt] + } else if (type == INST_LDR_64) { + buf[1] = 0xF9400000 | rt | (rt << 5u); // LDR Xt, [Xt] + } else { + // LDRSW_LIT + buf[1] = 0xB9800000 | rt | (rt << 5u); // LDRSW Xt, [Xt] + } + buf[2] = 0x14000004; // B #16 + buf[3] = ARM64_NOP; + buf[4] = addr & 0xFFFFFFFF; + buf[5] = addr >> 32u; + } else { + buf[0] = 0xA93F47F0; // STP X16, X17, [SP, -0x10] + buf[1] = 0x58000091; // LDR X17, #16 + if (type == INST_PRFM_LIT) { + buf[2] = 0xF9800220 | rt; // PRFM Rt, [X17] + } else if (type == INST_LDR_SIMD_32) { + buf[2] = 0xBD400220 | rt; // LDR St, [X17] + } else if (type == INST_LDR_SIMD_64) { + buf[2] = 0xFD400220 | rt; // LDR Dt, [X17] + } else { + // LDR_SIMD_128 + buf[2] = 0x3DC00220u | rt; // LDR Qt, [X17] + } + buf[3] = 0xF85F83F1; // LDR X17, [SP, -0x8] + buf[4] = 0x14000004; // B #16 + buf[5] = ARM64_NOP; + buf[6] = addr & 0xFFFFFFFF; + buf[7] = addr >> 32u; + } + return HOOK_NO_ERR; +} + +static hook_err_t relo_cb(hook_t *hook, uint64_t inst_addr, uint32_t inst, + inst_type_t type) +{ + uint32_t *buf = hook->relo_insts + hook->relo_insts_num; + + uint64_t imm19 = bits32(inst, 23, 5); + uint64_t offset = sign64_extend((imm19 << 2u), 21u); + uint64_t addr = inst_addr + offset; + addr = relo_in_tramp(hook, addr); + + buf[0] = (inst & 0xFF00001F) | 0x40u; // CB(N)Z Rt, #8 + buf[1] = 0x14000005; // B #20 + buf[2] = 0x58000051; // LDR X17, #8 + buf[3] = 0xD65F0220; // RET X17 + buf[4] = addr & 0xFFFFFFFF; + buf[5] = addr >> 32u; + return HOOK_NO_ERR; +} + +static hook_err_t relo_tb(hook_t *hook, uint64_t inst_addr, uint32_t inst, + inst_type_t type) +{ + uint32_t *buf = hook->relo_insts + hook->relo_insts_num; + + uint64_t imm14 = bits32(inst, 18, 5); + uint64_t offset = sign64_extend((imm14 << 2u), 16u); + uint64_t addr = inst_addr + offset; + addr = relo_in_tramp(hook, addr); + + buf[0] = (inst & 0xFFF8001F) | 0x40u; // TB(N)Z Rt, #, #8 + buf[1] = 0x14000005; // B #20 + buf[2] = 0x58000051; // LDR X17, #8 + buf[3] = 0xd65f0220; // RET X17 + buf[4] = addr & 0xFFFFFFFF; + buf[5] = addr >> 32u; + return HOOK_NO_ERR; +} + +static hook_err_t relo_ignore(hook_t *hook, uint64_t inst_addr, uint32_t inst, + inst_type_t type) +{ + uint32_t *buf = hook->relo_insts + hook->relo_insts_num; + buf[0] = inst; + buf[1] = ARM64_NOP; + return HOOK_NO_ERR; +} + +static uint32_t can_b_rel(uint64_t src_addr, uint64_t dst_addr) +{ +#define B_REL_RANGE ((1 << 25) << 2) + return ((dst_addr >= src_addr) && (dst_addr - src_addr <= B_REL_RANGE)) || + ((src_addr >= dst_addr) && (src_addr - dst_addr <= B_REL_RANGE)); +} + +int32_t branch_relative(uint32_t *buf, uint64_t src_addr, uint64_t dst_addr) +{ + if (can_b_rel(src_addr, dst_addr)) { + buf[0] = 0x14000000u | + (((dst_addr - src_addr) & 0x0FFFFFFFu) >> 2u); // B