|  | 
| 23 | 23 | #else | 
| 24 | 24 | #include <linux/sched.h> /* fatal_signal_pending */ | 
| 25 | 25 | #endif | 
|  | 26 | +#include <linux/slab.h> | 
| 26 | 27 | 
 | 
| 27 | 28 | #include "allowlist.h" | 
| 28 | 29 | #include "klog.h" // IWYU pragma: keep | 
| @@ -154,6 +155,154 @@ static int __maybe_unused count(struct user_arg_ptr argv, int max) | 
| 154 | 155 | 	return i; | 
| 155 | 156 | } | 
| 156 | 157 | 
 | 
|  | 158 | +// since _ksud handler only uses argv and envp for comparisons | 
|  | 159 | +// this can probably work | 
|  | 160 | +// adapted from ksu_handle_execveat_ksud | 
|  | 161 | +static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len) | 
|  | 162 | +{ | 
|  | 163 | +	static const char app_process[] = "/system/bin/app_process"; | 
|  | 164 | +	static bool first_app_process = true; | 
|  | 165 | + | 
|  | 166 | +	/* This applies to versions Android 10+ */ | 
|  | 167 | +	static const char system_bin_init[] = "/system/bin/init"; | 
|  | 168 | +	/* This applies to versions between Android 6 ~ 9  */ | 
|  | 169 | +	static const char old_system_init[] = "/init"; | 
|  | 170 | +	static bool init_second_stage_executed = false; | 
|  | 171 | + | 
|  | 172 | +	// return early when disabled | 
|  | 173 | +	if (!ksu_execveat_hook) | 
|  | 174 | +		return 0; | 
|  | 175 | + | 
|  | 176 | +	if (!filename) | 
|  | 177 | +		return 0; | 
|  | 178 | + | 
|  | 179 | + | 
|  | 180 | +	// debug! remove me! | 
|  | 181 | +	pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len); | 
|  | 182 | + | 
|  | 183 | +	if (init_second_stage_executed) | 
|  | 184 | +		goto first_app_process; | 
|  | 185 | + | 
|  | 186 | +	// /system/bin/init with argv1 | 
|  | 187 | +	if (!init_second_stage_executed  | 
|  | 188 | +		&& (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) { | 
|  | 189 | +		if (argv1 && !strcmp(argv1, "second_stage")) { | 
|  | 190 | +			pr_info("%s: /system/bin/init second_stage executed\n", __func__); | 
|  | 191 | +			apply_kernelsu_rules(); | 
|  | 192 | +			init_second_stage_executed = true; | 
|  | 193 | +			ksu_android_ns_fs_check(); | 
|  | 194 | +		} | 
|  | 195 | +	} | 
|  | 196 | + | 
|  | 197 | +	// /init with argv1 | 
|  | 198 | +	if (!init_second_stage_executed  | 
|  | 199 | +		&& (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) { | 
|  | 200 | +		if (argv1 && !strcmp(argv1, "--second-stage")) { | 
|  | 201 | +			pr_info("%s: /init --second-stage executed\n", __func__); | 
|  | 202 | +			apply_kernelsu_rules(); | 
|  | 203 | +			init_second_stage_executed = true; | 
|  | 204 | +			ksu_android_ns_fs_check(); | 
|  | 205 | +		} | 
|  | 206 | +	} | 
|  | 207 | + | 
|  | 208 | +	// /init without argv1/useless-argv1 but usable envp | 
|  | 209 | +	// untested! TODO: test and debug me! | 
|  | 210 | +	if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) { | 
|  | 211 | +		 | 
|  | 212 | +		// we hunt for "INIT_SECOND_STAGE" | 
|  | 213 | +		const char *envp_n = envp; | 
|  | 214 | +		unsigned int envc = 1; | 
|  | 215 | +		do { | 
|  | 216 | +			if (strstarts(envp_n, "INIT_SECOND_STAGE")) | 
|  | 217 | +				break; | 
|  | 218 | +			envp_n += strlen(envp_n) + 1; | 
|  | 219 | +			envc++; | 
|  | 220 | +		} while (envp_n < envp + envp_len); | 
|  | 221 | +		pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n); | 
|  | 222 | +		 | 
|  | 223 | +		if (!strcmp(envp_n, "INIT_SECOND_STAGE=1") | 
|  | 224 | +			|| !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) { | 
|  | 225 | +			pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__); | 
|  | 226 | +			apply_kernelsu_rules(); | 
|  | 227 | +			init_second_stage_executed = true; | 
|  | 228 | +			ksu_android_ns_fs_check(); | 
|  | 229 | +		} | 
|  | 230 | +	} | 
|  | 231 | + | 
|  | 232 | +first_app_process: | 
|  | 233 | +	if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) { | 
|  | 234 | +		first_app_process = false; | 
|  | 235 | +		pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed); | 
|  | 236 | +		on_post_fs_data(); | 
|  | 237 | +		stop_execve_hook(); | 
|  | 238 | +	} | 
|  | 239 | + | 
|  | 240 | +	return 0; | 
|  | 241 | +} | 
|  | 242 | + | 
|  | 243 | +int ksu_handle_pre_ksud(const char *filename) | 
|  | 244 | +{ | 
|  | 245 | +	if (likely(!ksu_execveat_hook)) | 
|  | 246 | +		return 0; | 
|  | 247 | + | 
|  | 248 | +	// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy) | 
|  | 249 | +	// return 0; | 
|  | 250 | +	if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init") | 
|  | 251 | +		&& !strstarts(filename, "/system/bin/app_process") )) | 
|  | 252 | +		return 0; | 
|  | 253 | + | 
|  | 254 | +	if (!current || !current->mm) | 
|  | 255 | +		return 0; | 
|  | 256 | + | 
|  | 257 | +	// https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429 | 
|  | 258 | +	// unsigned long arg_start, arg_end, env_start, env_end; | 
|  | 259 | +	unsigned long arg_start = current->mm->arg_start; | 
|  | 260 | +	unsigned long arg_end = current->mm->arg_end; | 
|  | 261 | +	unsigned long env_start = current->mm->env_start; | 
|  | 262 | +	unsigned long env_end = current->mm->env_end; | 
|  | 263 | + | 
|  | 264 | +	size_t arg_len = arg_end - arg_start; | 
|  | 265 | +	size_t envp_len = env_end - env_start; | 
|  | 266 | + | 
|  | 267 | +	if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it | 
|  | 268 | +		return 0; | 
|  | 269 | + | 
|  | 270 | +	#define ARGV_MAX 32  // this is enough for argv1 | 
|  | 271 | +	#define ENVP_MAX 256  // this is enough for INIT_SECOND_STAGE | 
|  | 272 | +	char args[ARGV_MAX]; | 
|  | 273 | +	size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len; | 
|  | 274 | +	char envp[ENVP_MAX]; | 
|  | 275 | +	size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len; | 
|  | 276 | + | 
|  | 277 | +	// we cant use strncpy on here, else it will truncate once it sees \0 | 
|  | 278 | +	if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len)) | 
|  | 279 | +		return 0; | 
|  | 280 | + | 
|  | 281 | +	if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len)) | 
|  | 282 | +		return 0; | 
|  | 283 | + | 
|  | 284 | +	args[ARGV_MAX - 1] = '\0'; | 
|  | 285 | +	envp[ENVP_MAX - 1] = '\0'; | 
|  | 286 | + | 
|  | 287 | +#ifdef CONFIG_KSU_DEBUG | 
|  | 288 | +	char *envp_n = envp; | 
|  | 289 | +	unsigned int envc = 1; | 
|  | 290 | +	do { | 
|  | 291 | +		pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n); | 
|  | 292 | +		envp_n += strlen(envp_n) + 1; | 
|  | 293 | +		envc++; | 
|  | 294 | +	} while (envp_n < envp + envp_copy_len); | 
|  | 295 | +#endif | 
|  | 296 | + | 
|  | 297 | +	// we only need argv1 ! | 
|  | 298 | +	// abuse strlen here since it only gets length up to \0 | 
|  | 299 | +	char *argv1 = args + strlen(args) + 1; | 
|  | 300 | +	if (argv1 >= args + argv_copy_len) // out of bounds! | 
|  | 301 | +		argv1 = ""; | 
|  | 302 | + | 
|  | 303 | +	return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len); | 
|  | 304 | +} | 
|  | 305 | + | 
| 157 | 306 | // IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version | 
| 158 | 307 | static int __ksu_handle_execveat_ksud(int *fd, char *filename, | 
| 159 | 308 | 			     struct user_arg_ptr *argv, | 
|  | 
0 commit comments