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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user