kernel: Fix when calling iterate_dir() under an encrypted directory (F2FS + file-based encryption), the kernel encountered a NEON/FPSIMD register state error while decrypting filenames.

Error Log :
[ T4681] Call trace:
[ T4681]  fpsimd_save_state+0x4/0x58
[ T4681]  cts_cbc_decrypt+0x268/0x384
[ T4681]  fscrypt_fname_disk_to_usr+0x1dc/0x338
[ T4681]  f2fs_fill_dentries+0x1cc/0x330
[ T4681]  f2fs_readdir+0x1a0/0x3ec
[ T4681]  iterate_dir+0x80/0x170
[ T4681]  scan_user_data_for_uids+0x170/0x560
[ T4681]  throne_tracker_thread+0x68/0x290
This commit is contained in:
ShirkNeko
2025-09-16 22:36:26 +08:00
parent 4e8d699654
commit 7b6074cfc3

View File

@@ -15,6 +15,8 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/atomic.h> #include <linux/atomic.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/preempt.h>
#include <linux/hardirq.h>
#include "klog.h" #include "klog.h"
#include "ksu.h" #include "ksu.h"
@@ -24,6 +26,14 @@
#define KERN_PATH_TIMEOUT_MS 100 #define KERN_PATH_TIMEOUT_MS 100
#define MAX_FUSE_CHECK_RETRIES 3 #define MAX_FUSE_CHECK_RETRIES 3
// Magic Number: File System Superblock Identifier
#define FUSE_SUPER_MAGIC 0x65735546 // FUSE (Userspace filesystem)
#define OVERLAYFS_SUPER_MAGIC 0x794c7630 // OverlayFS
#define TMPFS_MAGIC 0x01021994 // tmpfs
#define F2FS_SUPER_MAGIC 0xF2F52010 // F2FS (Flash-Friendly File System)
#define EXT4_SUPER_MAGIC 0xEF53 // ext4
extern bool is_lock_held(const char *path); extern bool is_lock_held(const char *path);
static struct workqueue_struct *scan_workqueue; static struct workqueue_struct *scan_workqueue;
@@ -38,15 +48,34 @@ struct work_buffers *get_work_buffer(void)
static bool is_dangerous_fs_magic(unsigned long magic) static bool is_dangerous_fs_magic(unsigned long magic)
{ {
switch (magic) { switch (magic) {
case 0x65735546: case FUSE_SUPER_MAGIC:
case 0x794c7630: case OVERLAYFS_SUPER_MAGIC:
case 0x01021994: case TMPFS_MAGIC:
case F2FS_SUPER_MAGIC:
case EXT4_SUPER_MAGIC:
return true; return true;
default: default:
return false; return false;
} }
} }
// Check whether the file system is an encrypted user data file system
static bool is_encrypted_userdata_fs(struct super_block *sb, const char *path)
{
if (!sb || !path)
return true;
if (strstr(path, "/data/user_de") || strstr(path, "/data/user")) {
return true;
}
if (is_dangerous_fs_magic(sb->s_magic)) {
return true;
}
return false;
}
static bool is_path_for_kern_path(const char *path, struct super_block *expected_sb) static bool is_path_for_kern_path(const char *path, struct super_block *expected_sb)
{ {
if (fatal_signal_pending(current)) { if (fatal_signal_pending(current)) {
@@ -60,9 +89,8 @@ static bool is_path_for_kern_path(const char *path, struct super_block *expected
return false; return false;
} }
if (expected_sb && is_dangerous_fs_magic(expected_sb->s_magic)) { if (in_interrupt() || in_atomic()) {
pr_info("Skipping dangerous filesystem (magic=0x%lx): %s\n", pr_warn("Cannot scan path in atomic context: %s\n", path);
expected_sb->s_magic, path);
return false; return false;
} }
@@ -70,21 +98,40 @@ static bool is_path_for_kern_path(const char *path, struct super_block *expected
return false; return false;
} }
if (strstr(path, ".tmp") || strstr(path, ".removing") || strstr(path, ".unmounting")) { if (strstr(path, ".tmp") || strstr(path, ".removing") ||
strstr(path, ".unmounting") || strstr(path, ".pending")) {
pr_debug("Path appears to be in transition state: %s\n", path); pr_debug("Path appears to be in transition state: %s\n", path);
return false; return false;
} }
if (expected_sb) {
if (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 (is_encrypted_userdata_fs(expected_sb, path)) {
pr_warn("Skipping potentially encrypted userdata filesystem: %s\n", path);
return false;
}
}
return true; return true;
} }
static int kern_path_with_timeout(const char *path, unsigned int flags, struct path *result) static int kern_path_with_timeout(const char *path, unsigned int flags,
struct path *result)
{ {
unsigned long start_time = jiffies; unsigned long start_time = jiffies;
unsigned long timeout = start_time + msecs_to_jiffies(KERN_PATH_TIMEOUT_MS); unsigned long timeout = start_time + msecs_to_jiffies(KERN_PATH_TIMEOUT_MS);
int retries = 0; int retries = 0;
int err; int err;
if (!is_path_for_kern_path(path, NULL)) {
return -EPERM;
}
do { do {
if (time_after(jiffies, timeout)) { if (time_after(jiffies, timeout)) {
pr_warn("kern_path timeout for: %s\n", path); pr_warn("kern_path timeout for: %s\n", path);
@@ -96,13 +143,22 @@ static int kern_path_with_timeout(const char *path, unsigned int flags, struct p
return -EINTR; return -EINTR;
} }
if (in_atomic() || irqs_disabled()) {
pr_warn("Cannot call kern_path in atomic context: %s\n", path);
return -EINVAL;
}
err = kern_path(path, flags, result); err = kern_path(path, flags, result);
if (err == 0) { if (err == 0) {
if (!is_path_for_kern_path(path, result->mnt->mnt_sb)) {
path_put(result);
return -EPERM;
}
return 0; return 0;
} }
if (err == -ENOENT || err == -ENOTDIR || err == -EACCES) { if (err == -ENOENT || err == -ENOTDIR || err == -EACCES || err == -EPERM) {
return err; return err;
} }
@@ -199,7 +255,7 @@ static int process_deferred_paths(struct list_head *deferred_paths, struct list_
struct path path; struct path path;
int err = kern_path_with_timeout(path_info->path, LOOKUP_FOLLOW, &path); int err = kern_path_with_timeout(path_info->path, LOOKUP_FOLLOW, &path);
if (err) { if (err) {
if (err != -ENOENT) { if (err != -ENOENT && err != -EPERM) {
pr_debug("Path lookup failed: %s (%d)\n", path_info->path, err); pr_debug("Path lookup failed: %s (%d)\n", path_info->path, err);
} }
list_del(&path_info->list); list_del(&path_info->list);
@@ -207,17 +263,7 @@ static int process_deferred_paths(struct list_head *deferred_paths, struct list_
continue; continue;
} }
// Check the file system type // Check lock status
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;
}
int tries = 0; int tries = 0;
do { do {
if (!is_lock_held(path_info->path)) if (!is_lock_held(path_info->path))
@@ -307,10 +353,9 @@ static int scan_primary_user_apps(struct list_head *uid_list,
return PTR_ERR(dir_file); return PTR_ERR(dir_file);
} }
// Check the file system type // Check file system security
if (is_dangerous_fs_magic(dir_file->f_inode->i_sb->s_magic)) { if (!is_path_for_kern_path(PRIMARY_USER_PATH, dir_file->f_inode->i_sb)) {
pr_err("Primary user path is on dangerous filesystem (magic=0x%lx), aborting\n", pr_err("Primary user path is not safe for scanning, aborting\n");
dir_file->f_inode->i_sb->s_magic);
filp_close(dir_file, NULL); filp_close(dir_file, NULL);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
@@ -388,11 +433,12 @@ static int get_all_active_users(struct work_buffers *work_buf, size_t *found_cou
} }
// Check the file system type of the base path // Check the file system type of the base path
if (is_dangerous_fs_magic(dir_file->f_inode->i_sb->s_magic)) { if (!is_path_for_kern_path(USER_DATA_BASE_PATH, dir_file->f_inode->i_sb)) {
pr_err("User data base path is on dangerous filesystem (magic=0x%lx), aborting\n", pr_warn("User data base path is not safe for scanning, using primary user only\n");
dir_file->f_inode->i_sb->s_magic);
filp_close(dir_file, NULL); filp_close(dir_file, NULL);
return -EOPNOTSUPP; work_buf->user_ids_buffer[0] = 0;
*found_count = 1;
return 0;
} }
struct user_id_ctx uctx = { struct user_id_ctx uctx = {
@@ -437,10 +483,9 @@ static void scan_user_worker(struct work_struct *work)
goto done; goto done;
} }
// Check the file system type of the user directory // Check User Directory Security
if (is_dangerous_fs_magic(dir_file->f_inode->i_sb->s_magic)) { if (!is_path_for_kern_path(path_buffer, dir_file->f_inode->i_sb)) {
pr_info("User path %s is on dangerous filesystem (magic=0x%lx), skipping\n", pr_warn("User path %s is not safe for scanning, skipping\n", path_buffer);
path_buffer, dir_file->f_inode->i_sb->s_magic);
filp_close(dir_file, NULL); filp_close(dir_file, NULL);
goto done; goto done;
} }
@@ -500,7 +545,7 @@ static int scan_secondary_users_apps(struct list_head *uid_list,
} }
for (size_t i = 0; i < user_count; i++) { for (size_t i = 0; i < user_count; i++) {
// Skip the main user since it was already scanned in the first step // Skip the main user since it has already been scanned.
if (work_buf->user_ids_buffer[i] == 0) if (work_buf->user_ids_buffer[i] == 0)
continue; continue;
@@ -544,6 +589,11 @@ int scan_user_data_for_uids(struct list_head *uid_list, bool scan_all_users)
if (!uid_list) if (!uid_list)
return -EINVAL; return -EINVAL;
if (in_interrupt() || in_atomic()) {
pr_err("Cannot scan user data in atomic context\n");
return -EINVAL;
}
struct work_buffers *work_buf = get_work_buffer(); struct work_buffers *work_buf = get_work_buffer();
if (!work_buf) { if (!work_buf) {
pr_err("Failed to get work buffer\n"); pr_err("Failed to get work buffer\n");
@@ -569,7 +619,7 @@ int scan_user_data_for_uids(struct list_head *uid_list, bool scan_all_users)
size_t active_users; size_t active_users;
ret = get_all_active_users(work_buf, &active_users); ret = get_all_active_users(work_buf, &active_users);
if (ret < 0 || active_users == 0) { if (ret < 0 || active_users == 0) {
pr_warn("Failed to get active users, using primary user only: %d\n", ret); pr_warn("Failed to get active users or no additional users found, using primary user only: %d\n", ret);
return primary_pkg_count > 0 ? 0 : -ENOENT; return primary_pkg_count > 0 ? 0 : -ENOENT;
} }