3232#include <linux/umh.h>
3333#include <uapi/scdefs.h>
3434#include <uapi/linux/stat.h>
35-
35+ #include <sucompat.h>
36+ #include <userd.h>
37+ #include <uapi/linux/limits.h>
3638
3739#define REPLACE_RC_FILE "/dev/user_init.rc"
3840
4547#define MAGISK_SCTX "u:r:magisk:s0"
4648#define APD_PATH "/data/adb/apd"
4749#define MAGISK_POLICY_PATH "/data/adb/ap/bin/magiskpolicy"
50+ #define AP_PACKAGE_CONFIG_PATH "/data/adb/ap/package_config"
4851
4952
5053
@@ -124,6 +127,244 @@ static loff_t kernel_write_file(const char *path, const void *data, loff_t len,
124127 return off ;
125128}
126129
130+ // Simple CSV field parser helper function
131+ static char * parse_csv_field (char * * line_ptr )
132+ {
133+ char * start = * line_ptr ;
134+ char * end = start ;
135+
136+ if (!start || * start == '\0' ) return NULL ;
137+
138+ // Skip leading whitespace
139+ while (* start == ' ' || * start == '\t' ) start ++ ;
140+
141+ // Find comma or end of line
142+ end = start ;
143+ while (* end && * end != ',' && * end != '\n' && * end != '\r' ) {
144+ end ++ ;
145+ }
146+
147+ // Preserve delimiter before modifying buffer
148+ {
149+ char delim = * end ;
150+
151+ // Remove trailing whitespace only if field is non-empty
152+ if (end > start ) {
153+ char * trim_end = end - 1 ;
154+ while (trim_end > start && (* trim_end == ' ' || * trim_end == '\t' )) {
155+ trim_end -- ;
156+ }
157+ * (trim_end + 1 ) = '\0' ;
158+ } else {
159+ // Empty field: terminate at start so caller sees an empty string
160+ * start = '\0' ;
161+ }
162+
163+ // Update pointer position based on original delimiter
164+ if (delim == ',' ) {
165+ * line_ptr = end + 1 ;
166+ } else {
167+ * line_ptr = end ;
168+ }
169+ }
170+
171+ return start ;
172+ }
173+
174+ // Load APatch package_config configuration file
175+ // Returns: number of entries loaded, or negative error code
176+ int load_ap_package_config ()
177+ {
178+ loff_t len = 0 ;
179+ const char * data = kernel_read_file (AP_PACKAGE_CONFIG_PATH , & len );
180+
181+ if (!data || len <= 0 ) {
182+ log_boot ("package_config not found or empty\n" );
183+ return - ENOENT ;
184+ }
185+ if (len > 10 * 1024 * 1024 ){
186+ log_boot ("package_config too large: %lld\n" , len );
187+ return - EFBIG ;
188+ }
189+
190+ log_boot ("loading package_config, size: %lld\n" , len );
191+
192+ char * content = (char * )data ;
193+ char * line_start = content ;
194+ int line_num = 0 ;
195+ int loaded_count = 0 ;
196+ int skipped_count = 0 ;
197+
198+ // Parse CSV line by line
199+ while (line_start < content + len ) {
200+ char * line_end = line_start ;
201+ int has_newline = 0 ;
202+
203+ // Find end of line
204+ while (line_end < content + len && * line_end != '\n' && * line_end != '\r' ) {
205+ line_end ++ ;
206+ }
207+
208+ // Check if we found a newline
209+ if (line_end < content + len ) {
210+ has_newline = 1 ;
211+ * line_end = '\0' ; // Safe because line_end < content + len
212+ }
213+
214+ line_num ++ ;
215+
216+ // Skip CSV header
217+ if (line_num == 1 ) {
218+ if (has_newline ) {
219+ line_start = line_end + 1 ;
220+ } else {
221+ break ;
222+ }
223+ continue ;
224+ }
225+
226+ // Process current line
227+ char * line_ptr = line_start ;
228+ int valid_line = 1 ;
229+
230+ // Parse CSV fields: pkg,exclude,allow,uid,to_uid,sctx
231+ parse_csv_field (& line_ptr ); // skip pkg field
232+ char * exclude_str = parse_csv_field (& line_ptr );
233+ char * allow_str = parse_csv_field (& line_ptr );
234+ char * uid_str = parse_csv_field (& line_ptr );
235+ char * to_uid_str = parse_csv_field (& line_ptr );
236+ char * sctx = parse_csv_field (& line_ptr );
237+
238+ // Check required fields
239+ if (!uid_str || !to_uid_str || !sctx ) {
240+ log_boot ("package_config: line %d missing required fields (uid/to_uid/sctx)\n" , line_num );
241+ valid_line = 0 ;
242+ goto next_line ;
243+ }
244+
245+ unsigned long long uid_tmp = 0 , to_uid_tmp = 0 ;
246+ unsigned long long exclude_tmp = 0 , allow_tmp = 0 ;
247+ int ret ;
248+
249+ // Convert UID fields - must succeed
250+ ret = kstrtoull (uid_str , 10 , & uid_tmp );
251+ if (ret ) {
252+ log_boot ("package_config: line %d invalid uid '%s': %d\n" , line_num , uid_str , ret );
253+ valid_line = 0 ;
254+ goto next_line ;
255+ }
256+
257+ ret = kstrtoull (to_uid_str , 10 , & to_uid_tmp );
258+ if (ret ) {
259+ log_boot ("package_config: line %d invalid to_uid '%s': %d\n" , line_num , to_uid_str , ret );
260+ valid_line = 0 ;
261+ goto next_line ;
262+ }
263+
264+ // Range check for uid_t (typically unsigned int)
265+ if (uid_tmp > UINT_MAX ) {
266+ log_boot ("package_config: line %d uid %llu out of range\n" , line_num , uid_tmp );
267+ valid_line = 0 ;
268+ goto next_line ;
269+ }
270+ if (to_uid_tmp > UINT_MAX ) {
271+ log_boot ("package_config: line %d to_uid %llu out of range\n" , line_num , to_uid_tmp );
272+ valid_line = 0 ;
273+ goto next_line ;
274+ }
275+
276+ // Convert optional fields (exclude and allow)
277+ if (exclude_str && * exclude_str ) {
278+ ret = kstrtoull (exclude_str , 10 , & exclude_tmp );
279+ if (ret ) {
280+ log_boot ("package_config: line %d invalid exclude '%s': %d, using default 0\n" ,
281+ line_num , exclude_str , ret );
282+ exclude_tmp = 0 ;
283+ }
284+ if (exclude_tmp > INT_MAX ) {
285+ log_boot ("package_config: line %d exclude %llu out of range, clamping\n" ,
286+ line_num , exclude_tmp );
287+ exclude_tmp = INT_MAX ;
288+ }
289+ }
290+
291+ if (allow_str && * allow_str ) {
292+ ret = kstrtoull (allow_str , 10 , & allow_tmp );
293+ if (ret ) {
294+ log_boot ("package_config: line %d invalid allow '%s': %d, using default 0\n" ,
295+ line_num , allow_str , ret );
296+ allow_tmp = 0 ;
297+ }
298+ if (allow_tmp > INT_MAX ) {
299+ log_boot ("package_config: line %d allow %llu out of range, clamping\n" ,
300+ line_num , allow_tmp );
301+ allow_tmp = INT_MAX ;
302+ }
303+ }
304+
305+ uid_t uid = (uid_t )uid_tmp ;
306+ uid_t to_uid = (uid_t )to_uid_tmp ;
307+ int exclude = (int )exclude_tmp ;
308+ int allow = (int )allow_tmp ;
309+
310+ // Validate sctx is not empty
311+ if (!sctx || !* sctx ) {
312+ log_boot ("package_config: line %d empty sctx\n" , line_num );
313+ valid_line = 0 ;
314+ goto next_line ;
315+ }
316+
317+ // CRITICAL FIX: Safely copy sctx into a fixed-size buffer with NUL termination
318+ // This prevents buffer overflow and ensures proper string handling
319+ char sctx_buf [SUPERCALL_SCONTEXT_LEN ];
320+ size_t sctx_len = strlen (sctx );
321+
322+ if (sctx_len >= SUPERCALL_SCONTEXT_LEN ) {
323+ // Truncate and log warning
324+ log_boot ("package_config: line %d sctx too long (%zu bytes), truncating to %d bytes\n" ,
325+ line_num , sctx_len , SUPERCALL_SCONTEXT_LEN - 1 );
326+ memcpy (sctx_buf , sctx , SUPERCALL_SCONTEXT_LEN - 1 );
327+ sctx_buf [SUPERCALL_SCONTEXT_LEN - 1 ] = '\0' ;
328+ } else {
329+ // Safe copy with NUL termination
330+ memcpy (sctx_buf , sctx , sctx_len + 1 ); // +1 includes the NUL terminator
331+ }
332+
333+ // Apply configuration with safe sctx buffer
334+ if (allow ) {
335+ int rc = su_add_allow_uid (uid , to_uid , sctx_buf );
336+ if (rc == 0 ) {
337+ loaded_count ++ ;
338+ } else {
339+ log_boot ("package_config: line %d failed to add allow rule: %d\n" , line_num , rc );
340+ valid_line = 0 ;
341+ }
342+ }
343+
344+ // Set exclude flag
345+ if (exclude ) {
346+ set_ap_mod_exclude (uid , exclude );
347+ }
348+
349+ next_line :
350+ if (!valid_line ) {
351+ skipped_count ++ ;
352+ }
353+
354+ // Move to next line
355+ if (has_newline ) {
356+ line_start = line_end + 1 ;
357+ } else {
358+ break ;
359+ }
360+ }
361+
362+ kvfree (data );
363+ log_boot ("package_config loaded: %d entries, skipped: %d\n" , loaded_count , skipped_count );
364+ return loaded_count ;
365+ }
366+ KP_EXPORT_SYMBOL (load_ap_package_config );
367+
127368static void pre_user_exec_init ()
128369{
129370 log_boot ("event: %s\n" , EXTRA_EVENT_PRE_EXEC_INIT );
@@ -133,6 +374,7 @@ static void pre_user_exec_init()
133374static void pre_init_second_stage ()
134375{
135376 log_boot ("event: %s\n" , EXTRA_EVENT_PRE_SECOND_STAGE );
377+
136378}
137379
138380static void on_first_app_process ()
0 commit comments