Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.vscode/*
.history/

.claude
**/.DS_Store

build
Expand Down
3 changes: 3 additions & 0 deletions kernel/linux/include/uapi/linux/limits.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
#define XATTR_NAME_MAX 255 /* # chars in an extended attribute name */
#define XATTR_SIZE_MAX 65536 /* size of an extended attribute value (64k) */
#define XATTR_LIST_MAX 65536 /* size of extended attribute namelist (64k) */
#define UINT_MAX 4294967295U
#define INT_MAX 2147483647
#define INT_MIN (-2147483647 - 1)

#define RTSIG_MAX 32

Expand Down
244 changes: 243 additions & 1 deletion kernel/patch/android/userd.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
#include <linux/umh.h>
#include <uapi/scdefs.h>
#include <uapi/linux/stat.h>

#include <sucompat.h>
#include <userd.h>
#include <uapi/linux/limits.h>

#define REPLACE_RC_FILE "/dev/user_init.rc"

Expand All @@ -45,6 +47,7 @@
#define MAGISK_SCTX "u:r:magisk:s0"
#define APD_PATH "/data/adb/apd"
#define MAGISK_POLICY_PATH "/data/adb/ap/bin/magiskpolicy"
#define AP_PACKAGE_CONFIG_PATH "/data/adb/ap/package_config"



Expand Down Expand Up @@ -124,6 +127,244 @@ static loff_t kernel_write_file(const char *path, const void *data, loff_t len,
return off;
}

// Simple CSV field parser helper function
static char *parse_csv_field(char **line_ptr)
{
char *start = *line_ptr;
char *end = start;

if (!start || *start == '\0') return NULL;

// Skip leading whitespace
while (*start == ' ' || *start == '\t') start++;

// Find comma or end of line
end = start;
while (*end && *end != ',' && *end != '\n' && *end != '\r') {
end++;
}

// Preserve delimiter before modifying buffer
{
char delim = *end;

// Remove trailing whitespace only if field is non-empty
if (end > start) {
char *trim_end = end - 1;
while (trim_end > start && (*trim_end == ' ' || *trim_end == '\t')) {
trim_end--;
}
*(trim_end + 1) = '\0';
} else {
// Empty field: terminate at start so caller sees an empty string
*start = '\0';
}

// Update pointer position based on original delimiter
if (delim == ',') {
*line_ptr = end + 1;
} else {
*line_ptr = end;
}
}

return start;
}

// Load APatch package_config configuration file
// Returns: number of entries loaded, or negative error code
int load_ap_package_config()
{
loff_t len = 0;
const char *data = kernel_read_file(AP_PACKAGE_CONFIG_PATH, &len);

if (!data || len <= 0) {
log_boot("package_config not found or empty\n");
return -ENOENT;
}
if (len > 10 * 1024 * 1024){
log_boot("package_config too large: %lld\n", len);
return -EFBIG;
}

log_boot("loading package_config, size: %lld\n", len);

Comment on lines +178 to +191
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

load_ap_package_config() reads the entire /data/adb/ap/package_config into a single vmalloc(len) buffer with no upper bound. Since this can be triggered automatically at boot and also via supercall, a large file can cause excessive memory usage/OOM (and is a DoS vector). Consider enforcing a reasonable max size (and failing with a clear error) before allocating/reading.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

char *content = (char *)data;
char *line_start = content;
int line_num = 0;
int loaded_count = 0;
int skipped_count = 0;

// Parse CSV line by line
while (line_start < content + len) {
char *line_end = line_start;
int has_newline = 0;

// Find end of line
while (line_end < content + len && *line_end != '\n' && *line_end != '\r') {
line_end++;
}

// Check if we found a newline
if (line_end < content + len) {
has_newline = 1;
*line_end = '\0'; // Safe because line_end < content + len
}
Comment on lines +203 to +212
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line splitting logic treats both \r and \n as newline, but for CRLF it will process the \n as an extra empty line (because only \r is consumed). This will inflate line_num and produce spurious “missing required fields” logs. Consider skipping a following \n when the delimiter is \r.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback


line_num++;

// Skip CSV header
if (line_num == 1) {
if (has_newline) {
line_start = line_end + 1;
} else {
break;
}
continue;
}

// Process current line
char *line_ptr = line_start;
int valid_line = 1;

// Parse CSV fields: pkg,exclude,allow,uid,to_uid,sctx
parse_csv_field(&line_ptr); // skip pkg field
char *exclude_str = parse_csv_field(&line_ptr);
char *allow_str = parse_csv_field(&line_ptr);
char *uid_str = parse_csv_field(&line_ptr);
char *to_uid_str = parse_csv_field(&line_ptr);
char *sctx = parse_csv_field(&line_ptr);

// Check required fields
if (!uid_str || !to_uid_str || !sctx) {
log_boot("package_config: line %d missing required fields (uid/to_uid/sctx)\n", line_num);
valid_line = 0;
goto next_line;
}

unsigned long long uid_tmp = 0, to_uid_tmp = 0;
unsigned long long exclude_tmp = 0, allow_tmp = 0;
int ret;

// Convert UID fields - must succeed
ret = kstrtoull(uid_str, 10, &uid_tmp);
if (ret) {
log_boot("package_config: line %d invalid uid '%s': %d\n", line_num, uid_str, ret);
valid_line = 0;
goto next_line;
}

ret = kstrtoull(to_uid_str, 10, &to_uid_tmp);
if (ret) {
log_boot("package_config: line %d invalid to_uid '%s': %d\n", line_num, to_uid_str, ret);
valid_line = 0;
goto next_line;
}

// Range check for uid_t (typically unsigned int)
if (uid_tmp > UINT_MAX) {
log_boot("package_config: line %d uid %llu out of range\n", line_num, uid_tmp);
valid_line = 0;
goto next_line;
}
if (to_uid_tmp > UINT_MAX) {
log_boot("package_config: line %d to_uid %llu out of range\n", line_num, to_uid_tmp);
valid_line = 0;
goto next_line;
}

// Convert optional fields (exclude and allow)
if (exclude_str && *exclude_str) {
ret = kstrtoull(exclude_str, 10, &exclude_tmp);
if (ret) {
log_boot("package_config: line %d invalid exclude '%s': %d, using default 0\n",
line_num, exclude_str, ret);
exclude_tmp = 0;
}
if (exclude_tmp > INT_MAX) {
log_boot("package_config: line %d exclude %llu out of range, clamping\n",
line_num, exclude_tmp);
exclude_tmp = INT_MAX;
}
}

if (allow_str && *allow_str) {
ret = kstrtoull(allow_str, 10, &allow_tmp);
if (ret) {
log_boot("package_config: line %d invalid allow '%s': %d, using default 0\n",
line_num, allow_str, ret);
allow_tmp = 0;
}
if (allow_tmp > INT_MAX) {
log_boot("package_config: line %d allow %llu out of range, clamping\n",
line_num, allow_tmp);
allow_tmp = INT_MAX;
}
}

uid_t uid = (uid_t)uid_tmp;
uid_t to_uid = (uid_t)to_uid_tmp;
int exclude = (int)exclude_tmp;
int allow = (int)allow_tmp;

// Validate sctx is not empty
if (!sctx || !*sctx) {
log_boot("package_config: line %d empty sctx\n", line_num);
valid_line = 0;
goto next_line;
}

// CRITICAL FIX: Safely copy sctx into a fixed-size buffer with NUL termination
// This prevents buffer overflow and ensures proper string handling
char sctx_buf[SUPERCALL_SCONTEXT_LEN];
size_t sctx_len = strlen(sctx);

if (sctx_len >= SUPERCALL_SCONTEXT_LEN) {
// Truncate and log warning
log_boot("package_config: line %d sctx too long (%zu bytes), truncating to %d bytes\n",
line_num, sctx_len, SUPERCALL_SCONTEXT_LEN - 1);
memcpy(sctx_buf, sctx, SUPERCALL_SCONTEXT_LEN - 1);
sctx_buf[SUPERCALL_SCONTEXT_LEN - 1] = '\0';
} else {
// Safe copy with NUL termination
memcpy(sctx_buf, sctx, sctx_len + 1); // +1 includes the NUL terminator
}

// Apply configuration with safe sctx buffer
if (allow) {
int rc = su_add_allow_uid(uid, to_uid, sctx_buf);
if (rc == 0) {
loaded_count++;
} else {
log_boot("package_config: line %d failed to add allow rule: %d\n", line_num, rc);
valid_line = 0;
}
}

// Set exclude flag
if (exclude) {
set_ap_mod_exclude(uid, exclude);
}

next_line:
if (!valid_line) {
skipped_count++;
}

// Move to next line
if (has_newline) {
line_start = line_end + 1;
} else {
break;
}
}

kvfree(data);
log_boot("package_config loaded: %d entries, skipped: %d\n", loaded_count, skipped_count);
return loaded_count;
}
KP_EXPORT_SYMBOL(load_ap_package_config);

static void pre_user_exec_init()
{
log_boot("event: %s\n", EXTRA_EVENT_PRE_EXEC_INIT);
Expand All @@ -133,6 +374,7 @@ static void pre_user_exec_init()
static void pre_init_second_stage()
{
log_boot("event: %s\n", EXTRA_EVENT_PRE_SECOND_STAGE);

}

static void on_first_app_process()
Expand Down
12 changes: 12 additions & 0 deletions kernel/patch/common/supercall.c
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,14 @@ static long call_su_get_safemode()
logkfd("[call_su_get_safemode] %d\n", result);
return result;
}

extern int load_ap_package_config(void);
static long call_ap_load_package_config()
{
int result = load_ap_package_config();
logkfd("[call_ap_load_package_config] loaded %d entries\n", result);
return result;
}
#endif

static long call_su_list_allow_uid(uid_t *__user uids, int num)
Expand Down Expand Up @@ -273,6 +281,10 @@ static long supercall(int is_key_auth, long cmd, long arg1, long arg2, long arg3
return kver;
case SUPERCALL_BUILD_TIME:
return call_buildtime((char *__user)arg1, (int)arg2);
#ifdef ANDROID
case SUPERCALL_AP_LOAD_PACKAGE_CONFIG:
return call_ap_load_package_config();
#endif
}

switch (cmd) {
Expand Down
7 changes: 7 additions & 0 deletions kernel/patch/common/supercmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ static const char supercmd_help[] =
" version: Print Kernel version and KernelPatch version.\n"
" buildtime: Print KernelPatch build time.\n "
" eg: 50a0a,a06 means kernel version 5.10.10, KernelPatch version 0.10.6.\n"
" reload-cfg: Reload package configuration.\n "
" -c <COMMAND> [...]: Pass a single COMMAND to the default shell.\n"
" exec <PATH> [...]: Execute command with full PATH.\n"
" sumgr <SubCommand> [...]: SU permission manager\n"
Expand Down Expand Up @@ -450,6 +451,12 @@ void handle_supercmd(char **__user u_filename_p, char **__user uargv)
} else if (!strcmp("buildtime", cmd)) {
cmd_res.msg = get_build_time();
goto echo;
#ifdef ANDROID
} else if (!strcmp("reload-cfg", cmd)) {
load_ap_package_config();
cmd_res.msg = "reload package config success";
goto echo;
#endif
} else if (!strcmp("sumgr", cmd)) {
handle_cmd_sumgr(u_filename_p, carr, buffer, sizeof(buffer), &cmd_res);
} else if (!strcmp("event", cmd)) {
Expand Down
14 changes: 12 additions & 2 deletions kernel/patch/common/user_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@
*/

#include <user_event.h>

#include <userd.h>
#include <baselib.h>
#include <log.h>

int report_user_event(const char *event, const char *args)
{
logki("user report event: %s, args: %s\n", event, args);
const char *safe_event = event ? event : "";
const char *safe_args = args ? args : "";

#ifdef ANDROID
if (lib_strcmp(safe_event, "post-fs-data") == 0 && lib_strcmp(safe_args, "before") == 0) {
logki("post-fs-data before event received, loading ap package config ...\n");
load_ap_package_config();
}
#endif
logki("user report event: %s, args: %s\n", safe_event, safe_args);
return 0;
}
4 changes: 4 additions & 0 deletions kernel/patch/include/uapi/scdefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ static inline long hash_key(const char *key)
#define SUPERCALL_KERNELPATCH_VER 0x1008
#define SUPERCALL_KERNEL_VER 0x1009


#define SUPERCALL_SKEY_GET 0x100a
#define SUPERCALL_SKEY_SET 0x100b
#define SUPERCALL_SKEY_ROOT_ENABLE 0x100c
#define SUPERCALL_AP_LOAD_PACKAGE_CONFIG 0x100d

#define SUPERCALL_SU 0x1010
#define SUPERCALL_SU_TASK 0x1011 // syscall(__NR_gettid)
Expand Down Expand Up @@ -109,6 +111,8 @@ struct su_profile
#define SUPERCALL_SU_RESET_PATH 0x1111
#define SUPERCALL_SU_GET_SAFEMODE 0x1112



#define SUPERCALL_MAX 0x1200

#define SUPERCALL_RES_SUCCEED 0
Expand Down
11 changes: 11 additions & 0 deletions kernel/patch/include/userd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2024 bmax121. All Rights Reserved.
*/

#ifndef _KP_USERD_H_
#define _KP_USERD_H_

int load_ap_package_config(void);

#endif
Loading