From 38b3b11cf6f4f24fc82997a768082d05890ffbb8 Mon Sep 17 00:00:00 2001 From: Adam_pi3 Date: Sat, 6 Jul 2024 01:07:10 +0000 Subject: [PATCH] [Seccomp] Switch to refcount logic for kernels >= 5.9 Starting from kernel 5.9+ function 'put_seccomp_filter' has been inlined and unavailble for hooking. However, internal not-inline function was used to mitigate the problem. Unfortunately, there is no equivalent counter-part function for new hook and the old one looks incompatible which we overlooked. This patch is switching the logic for kernels 5.9+ to custom implementation of refcount logic and it should address the issue reported as #338 --- .../exploit_detection/p_exploit_detection.c | 33 +++++++---------- .../syscalls/p_seccomp/p_seccomp.c | 37 +++++++++++++++++++ .../syscalls/p_seccomp/p_seccomp.h | 9 +++++ 3 files changed, 59 insertions(+), 20 deletions(-) diff --git a/src/modules/exploit_detection/p_exploit_detection.c b/src/modules/exploit_detection/p_exploit_detection.c index 84843859..ae1d8a57 100644 --- a/src/modules/exploit_detection/p_exploit_detection.c +++ b/src/modules/exploit_detection/p_exploit_detection.c @@ -424,7 +424,7 @@ static notrace void p_dump_creds(struct p_cred *p_where, const struct cred *p_fr #if defined(CONFIG_SECCOMP) static notrace void p_dump_seccomp(struct p_seccomp *p_sec, struct task_struct *p_task, char p_force) { - P_SYM(p_get_seccomp_filter)(p_task); + p_lkrg_seccomp_filter_get(p_task); p_sec->sec.mode = p_task->seccomp.mode; // Mode p_sec->sec.filter = p_task->seccomp.filter; // Filter #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0) @@ -437,12 +437,7 @@ static notrace void p_dump_seccomp(struct p_seccomp *p_sec, struct task_struct * p_sec->flag = 0; if (p_force) p_sec->flag_sync_thread = 0; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) - P_SYM(p_put_seccomp_filter)(p_task->seccomp.filter); -#else - P_SYM(p_put_seccomp_filter)(p_task); -#endif - + p_lkrg_seccomp_filter_put(p_task); } #endif @@ -1373,8 +1368,12 @@ static int p_cmp_tasks(struct p_ed_process *p_orig, struct task_struct *p_curren #if defined(CONFIG_SECCOMP) /* Seccomp */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,9,0) if (p_orig->p_ed_task.p_sec.flag) { // SECCOMP was enabled so it make sense to compare... - P_SYM(p_get_seccomp_filter)(p_current); +#else + if (p_orig->p_ed_task.p_sec.flag && current == p_current) { // SECCOMP was enabled so it make sense to compare... +#endif + p_lkrg_seccomp_filter_get(p_current); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5,11,0) if (test_task_syscall_work(p_current,SECCOMP) != p_orig->p_ed_task.p_sec.flag) { @@ -1408,11 +1407,7 @@ static int p_cmp_tasks(struct p_ed_process *p_orig, struct task_struct *p_curren P_CMP_PTR(p_orig->p_ed_task.p_sec.sec.filter, p_current->seccomp.filter, "seccomp filter") -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) - P_SYM(p_put_seccomp_filter)(p_current->seccomp.filter); -#else - P_SYM(p_put_seccomp_filter)(p_current); -#endif + p_lkrg_seccomp_filter_put(p_current); } #endif @@ -1980,13 +1975,11 @@ int p_exploit_detection_init(void) { P_SYM_INIT(__kernel_text_address) P_SYM_INIT(mm_find_pmd) #if defined(CONFIG_SECCOMP) - P_SYM_INIT(get_seccomp_filter) -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) -#define p___put_seccomp_filter p_put_seccomp_filter - P_SYM_INIT(__put_seccomp_filter) -#else - P_SYM_INIT(put_seccomp_filter) -#endif + if (P_LKRG_SUCCESS != p_lkrg_seccomp_init()) { + p_print_log(P_LOG_FATAL, "Can't initialize seccomp() logic"); + p_ret = P_LKRG_GENERAL_ERROR; + goto p_exploit_detection_init_out; + } #endif #ifdef CONFIG_SECURITY_SELINUX diff --git a/src/modules/exploit_detection/syscalls/p_seccomp/p_seccomp.c b/src/modules/exploit_detection/syscalls/p_seccomp/p_seccomp.c index 23f22b27..f6e92156 100644 --- a/src/modules/exploit_detection/syscalls/p_seccomp/p_seccomp.c +++ b/src/modules/exploit_detection/syscalls/p_seccomp/p_seccomp.c @@ -34,6 +34,43 @@ static struct kretprobe p_seccomp_kretprobe = { .data_size = sizeof(struct p_seccomp_data), }; +int p_lkrg_seccomp_init(void) { + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,9,0) + P_SYM_INIT(get_seccomp_filter) + P_SYM_INIT(put_seccomp_filter) +#endif + + return P_LKRG_SUCCESS; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,9,0) +p_sym_error: + return P_LKRG_GENERAL_ERROR; +#endif +} + +void p_lkrg_seccomp_filter_get(struct task_struct *p_task) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,9,0) + P_SYM(p_get_seccomp_filter)(p_task); +#else + struct p_fake_seccomp_filter *p_filter = (struct p_fake_seccomp_filter *)p_task->seccomp.filter; + + if (p_filter) + refcount_inc(&p_filter->refs); +#endif +} + +void p_lkrg_seccomp_filter_put(struct task_struct *p_task) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,9,0) + P_SYM(p_put_seccomp_filter)(p_task); +#else + struct p_fake_seccomp_filter *p_filter = (struct p_fake_seccomp_filter *)p_task->seccomp.filter; + + if (p_filter) + refcount_dec(&p_filter->refs); +#endif +} + /* * x86-64 syscall ABI: * *rax - syscall_number diff --git a/src/modules/exploit_detection/syscalls/p_seccomp/p_seccomp.h b/src/modules/exploit_detection/syscalls/p_seccomp/p_seccomp.h index 5660a31a..2fe8ddc6 100644 --- a/src/modules/exploit_detection/syscalls/p_seccomp/p_seccomp.h +++ b/src/modules/exploit_detection/syscalls/p_seccomp/p_seccomp.h @@ -28,6 +28,15 @@ struct p_seccomp_data { ktime_t entry_stamp; }; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,9,0) +struct p_fake_seccomp_filter { + refcount_t refs; +}; +#endif + +int p_lkrg_seccomp_init(void); +void p_lkrg_seccomp_filter_get(struct task_struct *p_task); +void p_lkrg_seccomp_filter_put(struct task_struct *p_task); int p_seccomp_ret(struct kretprobe_instance *p_ri, struct pt_regs *p_regs); int p_seccomp_entry(struct kretprobe_instance *p_ri, struct pt_regs *p_regs);