diff --git a/kernel/user_data_scanner.c b/kernel/user_data_scanner.c index 7002cd3c..cb134576 100644 --- a/kernel/user_data_scanner.c +++ b/kernel/user_data_scanner.c @@ -8,18 +8,114 @@ #include #include #include +#include +#include +#include #include "klog.h" #include "ksu.h" #include "kernel_compat.h" #include "user_data_scanner.h" +#define KERN_PATH_TIMEOUT_MS 100 +#define MAX_FUSE_CHECK_RETRIES 3 + struct work_buffers *get_work_buffer(void) { static struct work_buffers global_buffer; return &global_buffer; } +// Check the file system type +static bool is_dangerous_fs_magic(unsigned long magic) +{ + switch (magic) { + case 0x65735546: + case 0x794c7630: + case 0x01021994: + return true; + default: + return false; + } +} + +static bool is_path_for_kern_path(const char *path, struct super_block *expected_sb) +{ + if (fatal_signal_pending(current)) { + pr_warn("Fatal signal pending, skip path: %s\n", path); + return false; + } + + if (need_resched()) { + cond_resched(); + if (fatal_signal_pending(current)) + return false; + } + + if (expected_sb && is_dangerous_fs_magic(expected_sb->s_magic)) { + pr_info("Skipping dangerous filesystem (magic=0x%lx): %s\n", + expected_sb->s_magic, path); + return false; + } + + if (!path || strlen(path) == 0 || strlen(path) >= PATH_MAX) { + return false; + } + + if (strstr(path, ".tmp") || strstr(path, ".removing") || strstr(path, ".unmounting")) { + pr_debug("Path appears to be in transition state: %s\n", path); + return false; + } + + return true; +} + +static int kern_path_with_timeout(const char *path, unsigned int flags, struct path *result) +{ + unsigned long start_time = jiffies; + unsigned long timeout = start_time + msecs_to_jiffies(KERN_PATH_TIMEOUT_MS); + int retries = 0; + int err; + + do { + if (time_after(jiffies, timeout)) { + pr_warn("kern_path timeout for: %s\n", path); + return -ETIMEDOUT; + } + + if (fatal_signal_pending(current)) { + pr_warn("Fatal signal during kern_path: %s\n", path); + return -EINTR; + } + + err = kern_path(path, flags, result); + + if (err == 0) { + return 0; + } + + if (err == -ENOENT || err == -ENOTDIR || err == -EACCES) { + return err; + } + + if (err == -EBUSY || err == -EAGAIN) { + retries++; + if (retries >= MAX_FUSE_CHECK_RETRIES) { + pr_warn("Max retries reached for: %s (err=%d)\n", path, err); + return err; + } + + usleep_range(1000, 2000); + continue; + } + + return err; + + } while (retries < MAX_FUSE_CHECK_RETRIES); + + return err; +} + FILLDIR_RETURN_TYPE scan_user_packages(struct dir_context *ctx, const char *name, int namelen, loff_t off, u64 ino, unsigned int d_type) { @@ -32,6 +128,10 @@ FILLDIR_RETURN_TYPE scan_user_packages(struct dir_context *ctx, const char *name scan_ctx->processed_count++; if (scan_ctx->processed_count % SCHEDULE_INTERVAL == 0) { cond_resched(); + if (fatal_signal_pending(current)) { + pr_info("Fatal signal received, stopping scan\n"); + return FILLDIR_ACTOR_STOP; + } } if (d_type != DT_DIR || namelen <= 0) @@ -77,17 +177,40 @@ static int process_deferred_paths(struct list_head *deferred_paths, struct list_ { struct deferred_path_info *path_info, *n; int success_count = 0; + int skip_count = 0; list_for_each_entry_safe(path_info, n, deferred_paths, list) { - struct path path; - int err = kern_path(path_info->path, LOOKUP_FOLLOW, &path); - if (err) { - pr_debug("Path lookup failed: %s (%d)\n", path_info->path, err); + if (!is_path_for_kern_path(path_info->path, NULL)) { + pr_debug("Skipping unsafe path: %s\n", path_info->path); + skip_count++; list_del(&path_info->list); kfree(path_info); continue; } + // Retrieve path information + struct path path; + int err = kern_path_with_timeout(path_info->path, LOOKUP_FOLLOW, &path); + if (err) { + if (err != -ENOENT) { + pr_debug("Path lookup failed: %s (%d)\n", path_info->path, err); + } + list_del(&path_info->list); + kfree(path_info); + continue; + } + + // Check the file system type + if (is_dangerous_fs_magic(path.mnt->mnt_sb->s_magic)) { + pr_info("Skipping path on dangerous filesystem: %s (magic=0x%lx)\n", + path_info->path, path.mnt->mnt_sb->s_magic); + path_put(&path); + list_del(&path_info->list); + kfree(path_info); + skip_count++; + continue; + } + struct kstat stat; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0) || defined(KSU_HAS_NEW_VFS_GETATTR) err = vfs_getattr(&path, &stat, STATX_UID, AT_STATX_SYNC_AS_STAT); @@ -134,9 +257,17 @@ static int process_deferred_paths(struct list_head *deferred_paths, struct list_ if (success_count % 10 == 0) { cond_resched(); + if (fatal_signal_pending(current)) { + pr_info("Fatal signal received, stopping path processing\n"); + break; + } } } + if (skip_count > 0) { + pr_info("Skipped %d potentially dangerous paths for safety\n", skip_count); + } + return success_count; } @@ -159,6 +290,14 @@ static int scan_primary_user_apps(struct list_head *uid_list, return PTR_ERR(dir_file); } + // Check the file system type + if (is_dangerous_fs_magic(dir_file->f_inode->i_sb->s_magic)) { + pr_err("Primary user path is on dangerous filesystem (magic=0x%lx), aborting\n", + dir_file->f_inode->i_sb->s_magic); + filp_close(dir_file, NULL); + return -EOPNOTSUPP; + } + struct user_scan_ctx scan_ctx = { .deferred_paths = &deferred_paths, .user_id = 0, @@ -195,6 +334,8 @@ FILLDIR_RETURN_TYPE collect_user_ids(struct dir_context *ctx, const char *name, uctx->processed_count++; if (uctx->processed_count % SCHEDULE_INTERVAL == 0) { cond_resched(); + if (fatal_signal_pending(current)) + return FILLDIR_ACTOR_STOP; } if (d_type != DT_DIR || namelen <= 0) @@ -229,6 +370,14 @@ static int get_all_active_users(struct work_buffers *work_buf, size_t *found_cou return PTR_ERR(dir_file); } + // Check the file system type of the base path + if (is_dangerous_fs_magic(dir_file->f_inode->i_sb->s_magic)) { + pr_err("User data base path is on dangerous filesystem (magic=0x%lx), aborting\n", + dir_file->f_inode->i_sb->s_magic); + filp_close(dir_file, NULL); + return -EOPNOTSUPP; + } + struct user_id_ctx uctx = { .ctx.actor = collect_user_ids, .user_ids = work_buf->user_ids_buffer, @@ -260,6 +409,11 @@ static int scan_secondary_users_apps(struct list_head *uid_list, *total_pkg_count = *total_error_count = 0; for (size_t i = 0; i < user_count; i++) { + if (fatal_signal_pending(current)) { + pr_info("Fatal signal received, stopping secondary user scan\n"); + break; + } + // Skip the main user since it was already scanned in the first step if (work_buf->user_ids_buffer[i] == 0) continue; @@ -278,6 +432,14 @@ static int scan_secondary_users_apps(struct list_head *uid_list, continue; } + // Check the file system type of the user directory + if (is_dangerous_fs_magic(dir_file->f_inode->i_sb->s_magic)) { + pr_info("User path %s is on dangerous filesystem (magic=0x%lx), skipping\n", + work_buf->path_buffer, dir_file->f_inode->i_sb->s_magic); + filp_close(dir_file, NULL); + continue; + } + struct user_scan_ctx scan_ctx = { .deferred_paths = &deferred_paths, .user_id = work_buf->user_ids_buffer[i],