diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 99e57138..27476dfb 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -42,7 +42,6 @@ #include "manager.h" #include "selinux/selinux.h" #include "throne_tracker.h" -#include "throne_tracker.h" #include "kernel_compat.h" #include "kpm/kpm.h" diff --git a/kernel/dynamic_sign.c b/kernel/dynamic_sign.c index 848b13ea..56c9b9e1 100644 --- a/kernel/dynamic_sign.c +++ b/kernel/dynamic_sign.c @@ -5,9 +5,6 @@ #include #include #include -#include -#include -#include #ifdef CONFIG_KSU_DEBUG #include #endif @@ -22,13 +19,10 @@ #include "klog.h" // IWYU pragma: keep #include "kernel_compat.h" #include "manager.h" -#include "throne_tracker.h" #define MAX_MANAGERS 2 -#define MAX_RETRY_COUNT 3 -#define RETRY_DELAY_MS 100 -// Dynamic sign configuration with atomic operations support +// Dynamic sign configuration static struct dynamic_sign_config dynamic_sign = { .size = 0x300, .hash = "0000000000000000000000000000000000000000000000000000000000000000", @@ -40,23 +34,10 @@ static struct manager_info active_managers[MAX_MANAGERS]; static DEFINE_SPINLOCK(managers_lock); static DEFINE_SPINLOCK(dynamic_sign_lock); -// Work queues for persistent storage and manager rescanning -static struct workqueue_struct *ksu_dynamic_wq; +// Work queues for persistent storage static struct work_struct ksu_save_dynamic_sign_work; static struct work_struct ksu_load_dynamic_sign_work; static struct work_struct ksu_clear_dynamic_sign_work; -static struct work_struct ksu_rescan_manager_work; - -// Completion for synchronous operations when needed -static struct completion save_completion; -static struct completion load_completion; - -// Error recovery state -static atomic_t save_retry_count = ATOMIC_INIT(0); -static atomic_t load_retry_count = ATOMIC_INIT(0); - -// Exit flag to prevent new operations -static atomic_t dynamic_sign_exiting = ATOMIC_INIT(0); bool ksu_is_dynamic_sign_enabled(void) { @@ -181,7 +162,7 @@ int ksu_get_manager_signature_index(uid_t uid) return signature_index; } -static void clear_dynamic_managers_only(void) +static void clear_dynamic_manager(void) { unsigned long flags; int i; @@ -190,8 +171,8 @@ static void clear_dynamic_managers_only(void) for (i = 0; i < MAX_MANAGERS; i++) { if (active_managers[i].is_active) { - pr_info("Clearing manager uid=%d due to dynamic_sign disable\n", - active_managers[i].uid); + pr_info("Clearing dynamic manager uid=%d (signature_index=%d) for rescan\n", + active_managers[i].uid, active_managers[i].signature_index); active_managers[i].is_active = false; } } @@ -234,267 +215,104 @@ int ksu_get_active_managers(struct manager_list_info *info) return 0; } - -// Manager rescanning work handler -static void ksu_rescan_manager_work_handler(struct work_struct *work) -{ - pr_info("Starting manager rescan for dynamic sign changes\n"); - - // Clear only dynamic managers, preserve default manager - clear_dynamic_managers_only(); - - // Note: We preserve the traditional manager (index 0) and only rescan for dynamic managers - pr_info("Preserved traditional manager, rescanning for dynamic managers\n"); - - // Trigger manager scanning - track_throne(); - - pr_info("Manager rescan completed\n"); -} - -bool ksu_trigger_manager_rescan(void) -{ - if (!ksu_dynamic_wq || atomic_read(&dynamic_sign_exiting)) { - pr_err("Dynamic sign workqueue not initialized\n"); - return false; - } - return queue_work(ksu_dynamic_wq, &ksu_rescan_manager_work); -} - -// Enhanced file operations with error recovery -static int safe_file_write(struct file *fp, const void *data, size_t size, loff_t *pos) -{ - ssize_t written; - int retry = 0; - - while (retry < MAX_RETRY_COUNT) { - written = ksu_kernel_write_compat(fp, data, size, pos); - if (written == size) { - return 0; - } - - pr_warn("File write failed, attempt %d/%d, written=%zd, expected=%zu\n", - retry + 1, MAX_RETRY_COUNT, written, size); - - if (written < 0) { - return written; - } - - retry++; - if (retry < MAX_RETRY_COUNT) { - msleep(RETRY_DELAY_MS); - } - } - - return -EIO; -} - -static int safe_file_read(struct file *fp, void *data, size_t size, loff_t *pos) -{ - ssize_t read_bytes; - int retry = 0; - - while (retry < MAX_RETRY_COUNT) { - read_bytes = ksu_kernel_read_compat(fp, data, size, pos); - if (read_bytes == size) { - return 0; - } - - pr_warn("File read failed, attempt %d/%d, read=%zd, expected=%zu\n", - retry + 1, MAX_RETRY_COUNT, read_bytes, size); - - if (read_bytes < 0) { - return read_bytes; - } - - retry++; - if (retry < MAX_RETRY_COUNT) { - msleep(RETRY_DELAY_MS); - } - } - - return -EIO; -} - -static void do_save_dynamic_sign_with_recovery(struct work_struct *work) +static void do_save_dynamic_sign(struct work_struct *work) { u32 magic = DYNAMIC_SIGN_FILE_MAGIC; u32 version = DYNAMIC_SIGN_FILE_VERSION; struct dynamic_sign_config config_to_save; - struct dynamic_sign_config backup_config; loff_t off = 0; unsigned long flags; - struct file *fp = NULL; - int ret = 0; - int current_retry; + struct file *fp; - // Get current retry count - current_retry = atomic_read(&save_retry_count); - - // Backup current state before any operations spin_lock_irqsave(&dynamic_sign_lock, flags); config_to_save = dynamic_sign; - backup_config = dynamic_sign; spin_unlock_irqrestore(&dynamic_sign_lock, flags); if (!config_to_save.is_set) { pr_info("Dynamic sign config not set, skipping save\n"); - goto complete; + return; } - pr_info("Saving dynamic sign config (attempt %d/%d)\n", - current_retry + 1, MAX_RETRY_COUNT); - fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (IS_ERR(fp)) { - ret = PTR_ERR(fp); - pr_err("save_dynamic_sign create file failed: %d\n", ret); - goto retry_or_fail; - } - - // Write with error checking - ret = safe_file_write(fp, &magic, sizeof(magic), &off); - if (ret) { - pr_err("save_dynamic_sign write magic failed: %d\n", ret); - goto cleanup_and_retry; - } - - ret = safe_file_write(fp, &version, sizeof(version), &off); - if (ret) { - pr_err("save_dynamic_sign write version failed: %d\n", ret); - goto cleanup_and_retry; - } - - ret = safe_file_write(fp, &config_to_save, sizeof(config_to_save), &off); - if (ret) { - pr_err("save_dynamic_sign write config failed: %d\n", ret); - goto cleanup_and_retry; - } - - // Force sync to ensure data is written - if (fp->f_op && fp->f_op->fsync) { - ret = fp->f_op->fsync(fp, 0, LLONG_MAX, 1); - if (ret) { - pr_warn("save_dynamic_sign fsync failed: %d\n", ret); - // Continue anyway, fsync failure is not critical - } - } - - filp_close(fp, 0); - fp = NULL; - - // Reset retry count on success - atomic_set(&save_retry_count, 0); - pr_info("Dynamic sign config saved successfully\n"); - goto complete; - -cleanup_and_retry: - if (fp && !IS_ERR(fp)) { - filp_close(fp, 0); - fp = NULL; - } - -retry_or_fail: - if (current_retry < MAX_RETRY_COUNT - 1) { - atomic_inc(&save_retry_count); - pr_info("Retrying save operation in %dms\n", RETRY_DELAY_MS); - - // Schedule retry - if (ksu_dynamic_wq) { - queue_delayed_work(ksu_dynamic_wq, - (struct delayed_work *)&ksu_save_dynamic_sign_work, - msecs_to_jiffies(RETRY_DELAY_MS)); - } + pr_err("save_dynamic_sign create file failed: %ld\n", PTR_ERR(fp)); return; - } else { - // All retries failed, restore backup state - pr_err("Save operation failed after %d attempts, restoring backup state\n", - MAX_RETRY_COUNT); - - spin_lock_irqsave(&dynamic_sign_lock, flags); - dynamic_sign = backup_config; - spin_unlock_irqrestore(&dynamic_sign_lock, flags); - - atomic_set(&save_retry_count, 0); } -complete: - complete(&save_completion); + if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) { + pr_err("save_dynamic_sign write magic failed.\n"); + goto exit; + } + + if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) { + pr_err("save_dynamic_sign write version failed.\n"); + goto exit; + } + + if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) { + pr_err("save_dynamic_sign write config failed.\n"); + goto exit; + } + + pr_info("Dynamic sign config saved successfully\n"); + +exit: + filp_close(fp, 0); } -static void do_load_dynamic_sign_with_recovery(struct work_struct *work) +static void do_load_dynamic_sign(struct work_struct *work) { loff_t off = 0; + ssize_t ret = 0; struct file *fp = NULL; u32 magic; u32 version; struct dynamic_sign_config loaded_config; - struct dynamic_sign_config backup_config; unsigned long flags; - int ret = 0; int i; - int current_retry; - - // Get current retry count - current_retry = atomic_read(&load_retry_count); - - pr_info("Loading dynamic sign config (attempt %d/%d)\n", - current_retry + 1, MAX_RETRY_COUNT); - - // Backup current state - spin_lock_irqsave(&dynamic_sign_lock, flags); - backup_config = dynamic_sign; - spin_unlock_irqrestore(&dynamic_sign_lock, flags); fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_RDONLY, 0); if (IS_ERR(fp)) { - ret = PTR_ERR(fp); - if (ret == -ENOENT) { + if (PTR_ERR(fp) == -ENOENT) { pr_info("No saved dynamic sign config found\n"); - atomic_set(&load_retry_count, 0); - goto complete; } else { - pr_err("load_dynamic_sign open file failed: %d\n", ret); - goto retry_or_fail; + pr_err("load_dynamic_sign open file failed: %ld\n", PTR_ERR(fp)); } + return; } - // Read and validate magic - ret = safe_file_read(fp, &magic, sizeof(magic), &off); - if (ret || magic != DYNAMIC_SIGN_FILE_MAGIC) { - pr_err("dynamic sign file invalid magic: %x (expected: %x)\n", - magic, DYNAMIC_SIGN_FILE_MAGIC); - ret = -EINVAL; - goto cleanup_and_retry; + if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) || + magic != DYNAMIC_SIGN_FILE_MAGIC) { + pr_err("dynamic sign file invalid magic: %x!\n", magic); + goto exit; } - // Read version - ret = safe_file_read(fp, &version, sizeof(version), &off); - if (ret) { - pr_err("dynamic sign read version failed: %d\n", ret); - goto cleanup_and_retry; + if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) { + pr_err("dynamic sign read version failed\n"); + goto exit; } pr_info("dynamic sign file version: %d\n", version); - // Read config - ret = safe_file_read(fp, &loaded_config, sizeof(loaded_config), &off); - if (ret) { - pr_err("load_dynamic_sign read config failed: %d\n", ret); - goto cleanup_and_retry; + ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off); + if (ret <= 0) { + pr_info("load_dynamic_sign read err: %zd\n", ret); + goto exit; + } + + if (ret != sizeof(loaded_config)) { + pr_err("load_dynamic_sign read incomplete config: %zd/%zu\n", ret, sizeof(loaded_config)); + goto exit; } - // Validate loaded config if (loaded_config.size < 0x100 || loaded_config.size > 0x1000) { pr_err("Invalid saved config size: 0x%x\n", loaded_config.size); - ret = -EINVAL; - goto cleanup_and_retry; + goto exit; } if (strlen(loaded_config.hash) != 64) { pr_err("Invalid saved config hash length: %zu\n", strlen(loaded_config.hash)); - ret = -EINVAL; - goto cleanup_and_retry; + goto exit; } // Validate hash format @@ -502,71 +320,31 @@ static void do_load_dynamic_sign_with_recovery(struct work_struct *work) char c = loaded_config.hash[i]; if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) { pr_err("Invalid saved config hash character at position %d: %c\n", i, c); - ret = -EINVAL; - goto cleanup_and_retry; + goto exit; } } - filp_close(fp, 0); - fp = NULL; - - // Apply loaded config spin_lock_irqsave(&dynamic_sign_lock, flags); dynamic_sign = loaded_config; spin_unlock_irqrestore(&dynamic_sign_lock, flags); - // Reset retry count on success - atomic_set(&load_retry_count, 0); pr_info("Dynamic sign config loaded: size=0x%x, hash=%.16s...\n", loaded_config.size, loaded_config.hash); - goto complete; -cleanup_and_retry: - if (fp && !IS_ERR(fp)) { - filp_close(fp, 0); - fp = NULL; - } - -retry_or_fail: - if (current_retry < MAX_RETRY_COUNT - 1) { - atomic_inc(&load_retry_count); - pr_info("Retrying load operation in %dms\n", RETRY_DELAY_MS); - - // Schedule retry - if (ksu_dynamic_wq) { - queue_delayed_work(ksu_dynamic_wq, - (struct delayed_work *)&ksu_load_dynamic_sign_work, - msecs_to_jiffies(RETRY_DELAY_MS)); - } - return; - } else { - // All retries failed, keep backup state - pr_err("Load operation failed after %d attempts, keeping current state\n", - MAX_RETRY_COUNT); - atomic_set(&load_retry_count, 0); - } - -complete: - complete(&load_completion); +exit: + filp_close(fp, 0); } static bool persistent_dynamic_sign(void) { - if (!ksu_dynamic_wq || atomic_read(&dynamic_sign_exiting)) { - pr_err("Dynamic sign workqueue not initialized\n"); - return false; - } - - reinit_completion(&save_completion); - return queue_work(ksu_dynamic_wq, &ksu_save_dynamic_sign_work); + return ksu_queue_work(&ksu_save_dynamic_sign_work); } -static void do_clear_dynamic_sign_file(struct work_struct *work) +static void do_clear_dynamic_sign(struct work_struct *work) { loff_t off = 0; struct file *fp; char zero_buffer[512]; - int ret; memset(zero_buffer, 0, sizeof(zero_buffer)); @@ -577,9 +355,8 @@ static void do_clear_dynamic_sign_file(struct work_struct *work) } // Write null bytes to overwrite the file content - ret = safe_file_write(fp, zero_buffer, sizeof(zero_buffer), &off); - if (ret) { - pr_err("clear_dynamic_sign write null bytes failed: %d\n", ret); + if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) { + pr_err("clear_dynamic_sign write null bytes failed.\n"); } else { pr_info("Dynamic sign config file cleared successfully\n"); } @@ -589,11 +366,7 @@ static void do_clear_dynamic_sign_file(struct work_struct *work) static bool clear_dynamic_sign_file(void) { - if (!ksu_dynamic_wq || atomic_read(&dynamic_sign_exiting)) { - pr_err("Dynamic sign workqueue not initialized\n"); - return false; - } - return queue_work(ksu_dynamic_wq, &ksu_clear_dynamic_sign_work); + return ksu_queue_work(&ksu_clear_dynamic_sign_work); } int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config) @@ -627,7 +400,6 @@ int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config) } } - // Update configuration atomically spin_lock_irqsave(&dynamic_sign_lock, flags); dynamic_sign.size = config->size; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) @@ -638,14 +410,9 @@ int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config) dynamic_sign.is_set = 1; spin_unlock_irqrestore(&dynamic_sign_lock, flags); - // Trigger async save persistent_dynamic_sign(); pr_info("dynamic sign updated: size=0x%x, hash=%.16s... (multi-manager enabled)\n", config->size, config->hash); - - // Always trigger manager rescan when dynamic sign is set - pr_info("Dynamic sign set, triggering manager rescan\n"); - ksu_trigger_manager_rescan(); break; case DYNAMIC_SIGN_OP_GET: @@ -670,18 +437,14 @@ int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config) strcpy(dynamic_sign.hash, "0000000000000000000000000000000000000000000000000000000000000000"); dynamic_sign.is_set = 0; spin_unlock_irqrestore(&dynamic_sign_lock, flags); - - // Clear only dynamic managers, preserve default manager - clear_dynamic_managers_only(); - // Clear file using async operation + // Clear only dynamic managers, preserve default manager + clear_dynamic_manager(); + + // Clear file using the same method as save clear_dynamic_sign_file(); pr_info("Dynamic sign config cleared (multi-manager disabled)\n"); - - // Always trigger manager rescan when dynamic sign is cleared - pr_info("Dynamic sign cleared, triggering manager rescan\n"); - ksu_trigger_manager_rescan(); break; default: @@ -694,68 +457,34 @@ int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config) bool ksu_load_dynamic_sign(void) { - if (!ksu_dynamic_wq || atomic_read(&dynamic_sign_exiting)) { - pr_err("Dynamic sign workqueue not initialized\n"); - return false; - } - - reinit_completion(&load_completion); - return queue_work(ksu_dynamic_wq, &ksu_load_dynamic_sign_work); + return ksu_queue_work(&ksu_load_dynamic_sign_work); } void ksu_dynamic_sign_init(void) { int i; - // Create dedicated workqueue for dynamic sign operations - ksu_dynamic_wq = alloc_workqueue("ksu_dynamic", WQ_UNBOUND | WQ_MEM_RECLAIM, 0); - if (!ksu_dynamic_wq) { - pr_err("Failed to create dynamic sign workqueue\n"); - return; - } - - // Initialize work structures - INIT_WORK(&ksu_save_dynamic_sign_work, do_save_dynamic_sign_with_recovery); - INIT_WORK(&ksu_load_dynamic_sign_work, do_load_dynamic_sign_with_recovery); - INIT_WORK(&ksu_clear_dynamic_sign_work, do_clear_dynamic_sign_file); - INIT_WORK(&ksu_rescan_manager_work, ksu_rescan_manager_work_handler); - - // Initialize completions - init_completion(&save_completion); - init_completion(&load_completion); + INIT_WORK(&ksu_save_dynamic_sign_work, do_save_dynamic_sign); + INIT_WORK(&ksu_load_dynamic_sign_work, do_load_dynamic_sign); + INIT_WORK(&ksu_clear_dynamic_sign_work, do_clear_dynamic_sign); // Initialize manager slots for (i = 0; i < MAX_MANAGERS; i++) { active_managers[i].is_active = false; } + + ksu_load_dynamic_sign(); - // Reset retry counters - atomic_set(&save_retry_count, 0); - atomic_set(&load_retry_count, 0); - - pr_info("Dynamic sign initialized with enhanced error recovery and dedicated workqueue\n"); - - // Auto-load existing dynamic sign configuration after initialization - if (ksu_load_dynamic_sign()) { - pr_info("Auto-loading dynamic sign configuration...\n"); - } else { - pr_warn("Failed to schedule auto-load of dynamic sign configuration\n"); - } + pr_info("Dynamic sign initialized with conditional multi-manager support\n"); } void ksu_dynamic_sign_exit(void) { - // Set exit flag to prevent new operations - atomic_set(&dynamic_sign_exiting, 1); + clear_dynamic_manager(); - // Clear only dynamic managers on exit, preserve default manager - clear_dynamic_managers_only(); - - // Wait for any pending operations to complete - if (ksu_dynamic_wq) { - ksu_dynamic_wq = NULL; - } - pr_info("Dynamic sign exit flag set, cleared dynamic managers, preserved default manager\n"); + // Save current config before exit + do_save_dynamic_sign(NULL); + pr_info("Dynamic sign exited with persistent storage\n"); } // Get dynamic sign configuration for signature verification diff --git a/kernel/dynamic_sign.h b/kernel/dynamic_sign.h index ca77e3df..eadbfea5 100644 --- a/kernel/dynamic_sign.h +++ b/kernel/dynamic_sign.h @@ -2,7 +2,6 @@ #define __KSU_H_DYNAMIC_SIGN #include -#include #include "ksu.h" #define DYNAMIC_SIGN_FILE_MAGIC 0x7f445347 // 'DSG', u32 @@ -21,7 +20,7 @@ struct manager_info { bool is_active; }; -// Enhanced dynamic sign operations with error recovery +// Dynamic sign operations int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config); void ksu_dynamic_sign_init(void); void ksu_dynamic_sign_exit(void); @@ -35,12 +34,9 @@ bool ksu_is_any_manager(uid_t uid); int ksu_get_manager_signature_index(uid_t uid); int ksu_get_active_managers(struct manager_list_info *info); -// Multi-manager APK verification with timeout protection +// Multi-manager APK verification bool ksu_is_multi_manager_apk(char *path, int *signature_index); -// Manager rescanning functionality -bool ksu_trigger_manager_rescan(void); - // Configuration access for signature verification bool ksu_get_dynamic_sign_config(unsigned int *size, const char **hash);