|
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,173 @@ 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_hex) |
| 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 | + // debug! remove me! |
| 180 | + pr_info("%s: filaname: %s argv1: %s \n", __func__, filename, argv1); |
| 181 | + pr_info("%s: envp (hex): %s\n", __func__, envp_hex); |
| 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 | + // ksu_bprm_check passed it packed, so for pattern |
| 210 | + // 494E49545F5345434F4E445F53544147453D31 = INIT_SECOND_STAGE=1 |
| 211 | + // 494E49545F5345434F4E445F53544147453D74727565 = INIT_SECOND_STAGE=true |
| 212 | + // untested! TODO: test and debug me! |
| 213 | + if (!init_second_stage_executed && envp_hex |
| 214 | + && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) { |
| 215 | + if (strstr(envp_hex, "494E49545F5345434F4E445F53544147453D31") |
| 216 | + || strstr(envp_hex, "494E49545F5345434F4E445F53544147453D74727565") ) { |
| 217 | + pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__); |
| 218 | + apply_kernelsu_rules(); |
| 219 | + init_second_stage_executed = true; |
| 220 | + ksu_android_ns_fs_check(); |
| 221 | + } |
| 222 | + } |
| 223 | + |
| 224 | +first_app_process: |
| 225 | + if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) { |
| 226 | + first_app_process = false; |
| 227 | + pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed); |
| 228 | + on_post_fs_data(); |
| 229 | + stop_execve_hook(); |
| 230 | + } |
| 231 | + |
| 232 | + return 0; |
| 233 | +} |
| 234 | + |
| 235 | +// needs some locking, checking with copy_from_user_nofault, |
| 236 | +// theres actually failed / incomplete copies |
| 237 | +static bool is_locked_copy_ok(void *to, const void __user *from, size_t len) |
| 238 | +{ |
| 239 | + DEFINE_SPINLOCK(ksu_usercopy_spinlock); |
| 240 | + spin_lock(&ksu_usercopy_spinlock); |
| 241 | + bool ret = !ksu_copy_from_user_nofault(to, from, len); |
| 242 | + spin_unlock(&ksu_usercopy_spinlock); |
| 243 | + |
| 244 | + if (likely(ret)) |
| 245 | + return ret; |
| 246 | + |
| 247 | + // if nofault copy fails, well, atleast we can try again |
| 248 | + // this happening is very bad though |
| 249 | + // I'm adding this just for the sake of resilience |
| 250 | + pr_info("%s: _nofault copy failed !! report this incident\n", __func__); |
| 251 | + if (unlikely(!ksu_access_ok(from, len))) |
| 252 | + return false; |
| 253 | + |
| 254 | + return !copy_from_user(to, from, len); |
| 255 | +} |
| 256 | + |
| 257 | +int ksu_handle_pre_ksud(const char *filename) |
| 258 | +{ |
| 259 | + |
| 260 | + if (likely(!ksu_execveat_hook)) |
| 261 | + return 0; |
| 262 | + |
| 263 | + // not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy) |
| 264 | + // return 0; |
| 265 | + if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init") |
| 266 | + && !strstarts(filename, "/system/bin/app_process") )) |
| 267 | + return 0; |
| 268 | + |
| 269 | + if (!current || !current->mm) |
| 270 | + return 0; |
| 271 | + |
| 272 | + // https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429 |
| 273 | + // unsigned long arg_start, arg_end, env_start, env_end; |
| 274 | + unsigned long arg_start = current->mm->arg_start; |
| 275 | + unsigned long arg_end = current->mm->arg_end; |
| 276 | + unsigned long env_start = current->mm->env_start; |
| 277 | + unsigned long env_end = current->mm->env_end; |
| 278 | + |
| 279 | + size_t arg_len = arg_end - arg_start; |
| 280 | + size_t envp_len = env_end - env_start; |
| 281 | + |
| 282 | + if (arg_len == 0 || envp_len == 0) // this wont make sense, filter it |
| 283 | + goto out; |
| 284 | + |
| 285 | + char *args = kmalloc(arg_len + 1, GFP_ATOMIC); |
| 286 | + char *envp = kmalloc(envp_len + 1, GFP_ATOMIC); |
| 287 | + char *envp_hex = kmalloc(envp_len * 2 + 1, GFP_ATOMIC); // x2 since bin2hex |
| 288 | + if (!args || !envp || !envp_hex) |
| 289 | + goto out; |
| 290 | + |
| 291 | + // we cant use strncpy on here, else it will truncate once it sees \0 |
| 292 | + if (!is_locked_copy_ok(args, (void __user *)arg_start, arg_len)) |
| 293 | + goto out; |
| 294 | + |
| 295 | + if (!is_locked_copy_ok(envp, (void __user *)env_start, envp_len)) |
| 296 | + goto out; |
| 297 | + |
| 298 | + args[arg_len] = '\0'; |
| 299 | + |
| 300 | + // I fail to simplify the loop so, lets just pack it |
| 301 | + bin2hex(envp_hex, envp, envp_len); |
| 302 | + envp_hex[envp_len * 2] = '\0'; |
| 303 | + |
| 304 | + // debug! |
| 305 | + //pr_info("%s: envp (hex): %s\n", __func__, envp_hex); |
| 306 | + |
| 307 | + // we only need argv1 ! |
| 308 | + // abuse strlen here since it only gets length up to \0 |
| 309 | + char *argv1 = args + strlen(args) + 1; |
| 310 | + if (argv1 >= args + arg_len) // out of bounds! |
| 311 | + argv1 = ""; |
| 312 | + |
| 313 | + // pass whole for envp?!! |
| 314 | + // pr_info("%s: fname: %s argv1: %s \n", __func__, filename, argv1); |
| 315 | + ksu_handle_bprm_ksud(filename, argv1, envp_hex); |
| 316 | + |
| 317 | +out: |
| 318 | + kfree(args); |
| 319 | + kfree(envp); |
| 320 | + kfree(envp_hex); |
| 321 | + |
| 322 | + return 0; |
| 323 | +} |
| 324 | + |
157 | 325 | // IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version |
158 | 326 | static int __ksu_handle_execveat_ksud(int *fd, char *filename, |
159 | 327 | struct user_arg_ptr *argv, |
|
0 commit comments