diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 207ea4734384d0771cc04897a43a30779a78f29c..1adec072b04849380de6592be5ed3bbfc1a21307 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -1285,6 +1285,8 @@ EXPORT_SYMBOL(ptrace_pre_hook); SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, unsigned long, data) { + int (*hook)(long request, long pid, struct task_struct *task, long addr, + long data); struct task_struct *child; long ret; @@ -1299,11 +1301,21 @@ SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, goto out; } - if (ptrace_pre_hook) { - ret = ptrace_pre_hook(request, pid, child, addr, data); - if (ret) + /* + * The hook is provided by a loadable module and disappears when that + * module is unloaded, so the call has to be inside an RCU read-side + * critical section; the unload path waits for a grace period. + */ + rcu_read_lock(); + hook = READ_ONCE(ptrace_pre_hook); + if (hook) { + ret = hook(request, pid, child, addr, data); + if (ret) { + rcu_read_unlock(); goto out_put_task_struct; + } } + rcu_read_unlock(); if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) { ret = ptrace_attach(child, request, addr, data); @@ -1430,6 +1442,8 @@ int compat_ptrace_request(struct task_struct *child, compat_long_t request, COMPAT_SYSCALL_DEFINE4(ptrace, compat_long_t, request, compat_long_t, pid, compat_long_t, addr, compat_long_t, data) { + int (*hook)(long request, long pid, struct task_struct *task, long addr, + long data); struct task_struct *child; long ret; @@ -1444,11 +1458,21 @@ COMPAT_SYSCALL_DEFINE4(ptrace, compat_long_t, request, compat_long_t, pid, goto out; } - if (ptrace_pre_hook) { - ret = ptrace_pre_hook(request, pid, child, addr, data); - if (ret) + /* + * The hook is provided by a loadable module and disappears when that + * module is unloaded, so the call has to be inside an RCU read-side + * critical section; the unload path waits for a grace period. + */ + rcu_read_lock(); + hook = READ_ONCE(ptrace_pre_hook); + if (hook) { + ret = hook(request, pid, child, addr, data); + if (ret) { + rcu_read_unlock(); goto out_put_task_struct; + } } + rcu_read_unlock(); if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) { ret = ptrace_attach(child, request, addr, data); diff --git a/kernel/tkernel/ttools/ttools_module.c b/kernel/tkernel/ttools/ttools_module.c index e1302187a15b18f9971f54ea7344d8c6666abb8b..385eeaf70e2dc49524c42d39ba68edc1b8fd2853 100644 --- a/kernel/tkernel/ttools/ttools_module.c +++ b/kernel/tkernel/ttools/ttools_module.c @@ -258,7 +258,7 @@ static int ttools_init(void) if (ret) return ret; - ptrace_pre_hook = ttools_ptrace_hook; + WRITE_ONCE(ptrace_pre_hook, ttools_ptrace_hook); smp_wmb(); smp_call_function(flush_icache_1, NULL, 1); pr_info("ttools " TTOOLS_VER " loaded\n"); @@ -267,9 +267,11 @@ static int ttools_init(void) static void ttools_exit(void) { - ptrace_pre_hook = NULL; + WRITE_ONCE(ptrace_pre_hook, NULL); smp_wmb(); smp_call_function(flush_icache_1, NULL, 1); + /* wait for callers which are still inside the hook */ + synchronize_rcu(); misc_deregister(&ttools_dev); ttools_clean_task_list(); pr_info("ttools " TTOOLS_VER " unloaded\n");