Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new supercall to load APatch package_config into kernel state and triggers that load during Android init (second stage), aiming to avoid repeated reads/writes at runtime.
Changes:
- Added
SUPERCALL_AP_LOAD_PACKAGE_CONFIGUAPI definition and a userspace wrapper (sc_ap_load_package_config). - Wired the new command into the kernel supercall dispatcher (
supercall.c). - Implemented CSV parsing + application of config entries in
kernel/patch/android/userd.c, and invoked it during init second stage. - Updated
.gitignoreto ignore.claude.
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| user/supercall.h | Adds userspace inline wrapper for the new supercall command. |
| kernel/patch/include/uapi/scdefs.h | Defines new supercall command ID SUPERCALL_AP_LOAD_PACKAGE_CONFIG. |
| kernel/patch/common/supercall.c | Routes the new command to load_ap_package_config(). |
| kernel/patch/android/userd.c | Adds file loading + CSV parsing logic and calls it during init second stage. |
| .gitignore | Ignores .claude. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Load APatch package_config during init second stage | ||
| load_ap_package_config(); |
There was a problem hiding this comment.
PR title/description mentions loading package_info, but the implementation loads /data/adb/ap/package_config via load_ap_package_config(). Please align the PR title/description (or the code) so it’s clear which file/format is being loaded at startup.
| } | ||
|
|
||
| if (line_end > line_start) { | ||
| *line_end = '\0'; |
There was a problem hiding this comment.
*line_end = '\0' can write one byte past the allocated buffer when the last line does not end with a newline (i.e., line_end == content + len). Since kernel_read_file() allocates exactly len bytes, this is an out-of-bounds write. Consider ensuring the read buffer is NUL-terminated (e.g., copy into a len+1 buffer and set the final byte to 0) or guarding against line_end == content + len before writing.
| // Remove trailing whitespace | ||
| char *trim_end = end - 1; | ||
| while (trim_end > start && (*trim_end == ' ' || *trim_end == '\t')) { | ||
| trim_end--; | ||
| } | ||
| *(trim_end + 1) = '\0'; | ||
|
|
||
| // Update pointer position | ||
| if (*end == ',') { | ||
| *line_ptr = end + 1; | ||
| } else { | ||
| *line_ptr = end; |
There was a problem hiding this comment.
parse_csv_field() modifies the delimiter character before checking *end == ','. For empty fields (start points at ,), end == start, line 151 overwrites the comma with \0, and then the pointer-advance logic won’t see the comma and won’t advance *line_ptr, which can break parsing for lines with empty fields. Preserve the delimiter (or handle end == start / empty fields explicitly) before writing a terminator.
| // Remove trailing whitespace | |
| char *trim_end = end - 1; | |
| while (trim_end > start && (*trim_end == ' ' || *trim_end == '\t')) { | |
| trim_end--; | |
| } | |
| *(trim_end + 1) = '\0'; | |
| // Update pointer position | |
| if (*end == ',') { | |
| *line_ptr = end + 1; | |
| } else { | |
| *line_ptr = 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; | |
| } |
| // Convert strings to numbers | ||
| kstrtoull(uid_str, 10, &uid_tmp); | ||
| kstrtoull(to_uid_str, 10, &to_uid_tmp); | ||
| if (exclude_str) kstrtoull(exclude_str, 10, &exclude_tmp); | ||
| if (allow_str) kstrtoull(allow_str, 10, &allow_tmp); |
There was a problem hiding this comment.
Return values from kstrtoull() are ignored. If uid_str/to_uid_str fails to parse, the corresponding *_tmp stays 0, which can incorrectly apply config to UID 0 (and potentially grant privileges) instead of rejecting the line. Check kstrtoull() results (and range-check casts to uid_t/int) and skip/log invalid lines.
| if (allow) { | ||
| int rc = su_add_allow_uid(uid, to_uid, sctx); | ||
| if (rc == 0) { | ||
| loaded_count++; | ||
| } | ||
| } |
There was a problem hiding this comment.
sctx comes from the config file and can be arbitrarily long; passing it directly to su_add_allow_uid() is risky because su_add_allow_uid() copies a fixed SUPERCALL_SCONTEXT_LEN without guaranteeing NUL-termination. Consider truncating into a local buffer of SUPERCALL_SCONTEXT_LEN and forcing a trailing \0 before calling su_add_allow_uid() to avoid unterminated strings and log overreads.
Only need to read and write once when starting up
开机只需读写一次,如果有大量排除可能会出问题