Oleg Nesterov wrote: > On 10/22, Tetsuo Handa wrote: > > > And again, I do not know how/if yama ensures that child is rcu-protected, > > > perhaps > > > task_is_descendant() needs to check pid_alive(child) right after > > > rcu_read_lock() ? > > > > Since the caller (ptrace() path) called get_task_struct(child), child > > itself can't be > > released. Do we still need pid_alive(child) ? > > get_task_struct(child) can only ensure that this task_struct can't be freed.
The report says that it is a use-after-free read at walker = rcu_dereference(walker->real_parent); which means that walker was already released. > > Suppose that this child exits after get_task_struct(), then its real_parent > exits > too and calls call_rcu(delayed_put_task_struct). > > Now, when task_is_descendant() is called, rcu_read_lock() can happen after > rcu gp, > iow child->parent can be already freed/reused/unmapped. > > We need to ensure that child is still protected by RCU. I wonder whether pid_alive() test helps. We can get [ 40.620318] parent or walker is dead. [ 40.624146] tracee is dead. messages using below patch and reproducer. ---------- diff --git a/kernel/ptrace.c b/kernel/ptrace.c index 99cfddd..0d9d786 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -385,6 +385,7 @@ static int ptrace_attach(struct task_struct *task, long request, if (mutex_lock_interruptible(&task->signal->cred_guard_mutex)) goto out; + schedule_timeout_killable(HZ); task_lock(task); retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH_REALCREDS); task_unlock(task); diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index ffda91a..a231ec6 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -283,6 +283,11 @@ static int task_is_descendant(struct task_struct *parent, return 0; rcu_read_lock(); + if (!pid_alive(parent) || !pid_alive(walker)) { + rcu_read_unlock(); + printk("parent or walker is dead.\n"); + return 0; + } if (!thread_group_leader(parent)) parent = rcu_dereference(parent->group_leader); while (walker->pid > 0) { @@ -315,6 +320,10 @@ static int ptracer_exception_found(struct task_struct *tracer, bool found = false; rcu_read_lock(); + if (!pid_alive(tracee)) { + printk("tracee is dead.\n"); + goto unlock; + } /* * If there's already an active tracing relationship, then make an ---------- ---------- #include <unistd.h> #include <sys/ptrace.h> #include <poll.h> int main(int argc, char *argv[]) { if (fork() == 0) { ptrace(PTRACE_ATTACH, getppid(), NULL, NULL); _exit(0); } poll(NULL, 0, 100); return 0; } ---------- But since "child" has at least one reference, reading "child->real_parent" should be safe. Therefore, I think that bailing out due to pid_is_alive(child) == false (like above patch does) cannot avoid this problem...