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/.github/workflows/build-manager.yml b/.github/workflows/build-manager.yml
index 9031b92da45d..3b9c229c83e8 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:
@@ -19,7 +20,9 @@ on:
- 'manager/**'
- 'kernel/**'
- 'userspace/**'
+ - 'scripts/ksubot.py'
workflow_call:
+ workflow_dispatch:
jobs:
build-lkm:
@@ -79,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
{
@@ -95,7 +98,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
@@ -157,11 +160,10 @@ 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 }}
- COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
- COMMIT_URL: ${{ github.event.head_commit.url }}
+ MESSAGE_THREAD_ID: ${{ vars.MESSAGE_THREAD_ID }}
+ 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/.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/docs/README.md b/docs/README.md
index 03a9346ea904..353d29e8ef73 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
-A kernel-based root solution for Android devices.
+A [KernelSU](https://github.com/tiann/KernelSU/commit/139b8139c9634b324f7956bbb309b14b6e35a107)-based root solution for Android devices.
[](https://github.com/tiann/KernelSU/releases/latest)
[](https://hosted.weblate.org/engage/kernelsu)
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:
diff --git a/kernel/Kbuild b/kernel/Kbuild
index 14fd03d0c109..7727c4743b29 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
@@ -52,12 +53,35 @@ $(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
+
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
@@ -70,6 +94,16 @@ $(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 += -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/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)
diff --git a/kernel/allowlist.c b/kernel/allowlist.c
index 5822b1beb584..095029d5f910 100644
--- a/kernel/allowlist.c
+++ b/kernel/allowlist.c
@@ -281,6 +281,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)));
@@ -334,6 +338,10 @@ struct root_profile *ksu_get_root_profile(uid_t uid)
struct perm_data *p = NULL;
struct list_head *pos = NULL;
+ if (unlikely(allow_shell && uid == SHELL_UID)) {
+ return &default_root_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) {
diff --git a/kernel/allowlist.h b/kernel/allowlist.h
index e1c49757c21f..34947cb35d29 100644
--- a/kernel/allowlist.h
+++ b/kernel/allowlist.h
@@ -51,3 +51,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/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
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/kernel/ksud.c b/kernel/ksud.c
index 715f891fc3a7..9ce02bdc635b 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,10 @@ 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)
-{
- struct filename *filename;
+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";
static bool first_zygote = true;
@@ -229,22 +218,14 @@ 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 &&
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();
@@ -252,26 +233,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 +353,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 +389,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 +449,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,6 +527,36 @@ static int sys_fstat_handler_post(struct kretprobe_instance *p,
}
}
+ 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;
}
@@ -571,39 +569,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 +644,20 @@ 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_finit_module_hook()
+{
+ replace_syscall_table(__NR_finit_module, orig_finit_module, NULL);
}
static void stop_input_hook()
@@ -636,28 +675,25 @@ 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);
+ 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);
- 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;
diff --git a/kernel/supercalls.c b/kernel/supercalls.c
index f66be06bbd53..2909d74bbf52 100644
--- a/kernel/supercalls.c
+++ b/kernel/supercalls.c
@@ -11,6 +11,7 @@
#include
#include
#include
+#include
#include "supercalls.h"
#include "arch.h"
@@ -548,6 +549,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,
@@ -622,6 +643,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 af4f8752ebd3..a5653d6523d2 100644
--- a/kernel/supercalls.h
+++ b/kernel/supercalls.h
@@ -123,6 +123,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/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 33d5bfde89a8..b084a7006b97 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
@@ -240,7 +240,7 @@ fun FlashScreen(
@Parcelize
sealed class FlashIt : Parcelable {
- data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean, val partition: String? = null) :
+ data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean, val partition: String? = null, val allowShell: Boolean = false, val enableAdb: Boolean = false) :
FlashIt()
data class FlashModules(val uris: List) : FlashIt()
@@ -261,6 +261,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 ba89ae2756a3..45b9bedfac6b 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
@@ -112,6 +112,8 @@ fun InstallScreen(navigator: DestinationsNavigator) {
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 ->
@@ -121,7 +123,9 @@ fun InstallScreen(navigator: DestinationsNavigator) {
boot = if (method is InstallMethod.SelectFile) method.uri else null,
lkm = lkmSelection,
ota = isOta,
- partition = partitionSelection
+ partition = partitionSelection,
+ allowShell = allowShell,
+ enableAdb = enableAdb,
)
navigator.navigate(FlashScreenDestination(flashIt)) {
launchSingleTop = true
@@ -282,6 +286,22 @@ fun InstallScreen(navigator: DestinationsNavigator) {
)
}
)
+ 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 f0ceb12ac6d8..2c8e5d1f5c20 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
@@ -265,6 +265,8 @@ fun installBoot(
lkm: LkmSelection,
ota: Boolean,
partition: String?,
+ allowShell: Boolean,
+ enableAdb: Boolean,
onStdout: (String) -> Unit,
onStderr: (String) -> Unit,
): FlashResult {
@@ -291,6 +293,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 e95223cb6860..3c341225065c 100644
--- a/manager/app/src/main/res/values-zh-rCN/strings.xml
+++ b/manager/app/src/main/res/values-zh-rCN/strings.xml
@@ -179,4 +179,8 @@
信息
内核不支持此特性。
此功能由一个模块管理。
+ 总是给 shell 授予 root 权限
+ 用于救砖
+ 启动时强制启用 ADB 调试
+ 用于救砖
diff --git a/manager/app/src/main/res/values/strings.xml b/manager/app/src/main/res/values/strings.xml
index 5d6489d96924..4f282c6c23ef 100644
--- a/manager/app/src/main/res/values/strings.xml
+++ b/manager/app/src/main/res/values/strings.xml
@@ -181,4 +181,8 @@
README
Releases
Info
+ Always grant root to Shell
+ For rescue from bootloop.
+ Force enable adb on boot
+ For rescue from bootloop.
diff --git a/scripts/ksubot.py b/scripts/ksubot.py
index 80604094d248..1dc63e427630 100644
--- a/scripts/ksubot.py
+++ b/scripts/ksubot.py
@@ -1,110 +1,161 @@
-import asyncio
-import os
-import sys
-from telethon import TelegramClient
-from telethon.tl.functions.help import GetConfigRequest
-
-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:
- CHAT_ID = int(CHAT_ID)
- 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 None:
- print("[-] Invaild MESSAGE_THREAD_ID")
- exit(1)
- else:
- MESSAGE_THREAD_ID = int(MESSAGE_THREAD_ID)
-
-
-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
+import re
+
+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"))
+
+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] + '...'
+ 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']
+ 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 = ''
+
+if upstream_diff is not None:
+ commit_message += upstream_diff
+
+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}")
diff --git a/userspace/ksud/src/boot_patch.rs b/userspace/ksud/src/boot_patch.rs
index b35d684c7b72..eefb2694ca4a 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")]
@@ -590,7 +614,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");
@@ -601,7 +625,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")?;
}
@@ -627,34 +651,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/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 f3ee4db63d80..74186774ecfb 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(())
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(())
}