Merge tag 'riscv-for-linus-5.14-rc3' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / kernel / ptrace.c
index 61db50f..f8589bf 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/cn_proc.h>
 #include <linux/compat.h>
 #include <linux/sched/signal.h>
+#include <linux/minmax.h>
 
 #include <asm/syscall.h>       /* for syscall_get_* */
 
@@ -169,6 +170,21 @@ void __ptrace_unlink(struct task_struct *child)
        spin_unlock(&child->sighand->siglock);
 }
 
+static bool looks_like_a_spurious_pid(struct task_struct *task)
+{
+       if (task->exit_code != ((PTRACE_EVENT_EXEC << 8) | SIGTRAP))
+               return false;
+
+       if (task_pid_vnr(task) == task->ptrace_message)
+               return false;
+       /*
+        * The tracee changed its pid but the PTRACE_EVENT_EXEC event
+        * was not wait()'ed, most probably debugger targets the old
+        * leader which was destroyed in de_thread().
+        */
+       return true;
+}
+
 /* Ensure that nothing can wake it up, even SIGKILL */
 static bool ptrace_freeze_traced(struct task_struct *task)
 {
@@ -179,8 +195,9 @@ static bool ptrace_freeze_traced(struct task_struct *task)
                return ret;
 
        spin_lock_irq(&task->sighand->siglock);
-       if (task_is_traced(task) && !__fatal_signal_pending(task)) {
-               task->state = __TASK_TRACED;
+       if (task_is_traced(task) && !looks_like_a_spurious_pid(task) &&
+           !__fatal_signal_pending(task)) {
+               WRITE_ONCE(task->__state, __TASK_TRACED);
                ret = true;
        }
        spin_unlock_irq(&task->sighand->siglock);
@@ -190,7 +207,7 @@ static bool ptrace_freeze_traced(struct task_struct *task)
 
 static void ptrace_unfreeze_traced(struct task_struct *task)
 {
-       if (task->state != __TASK_TRACED)
+       if (READ_ONCE(task->__state) != __TASK_TRACED)
                return;
 
        WARN_ON(!task->ptrace || task->parent != current);
@@ -200,11 +217,11 @@ static void ptrace_unfreeze_traced(struct task_struct *task)
         * Recheck state under the lock to close this race.
         */
        spin_lock_irq(&task->sighand->siglock);
-       if (task->state == __TASK_TRACED) {
+       if (READ_ONCE(task->__state) == __TASK_TRACED) {
                if (__fatal_signal_pending(task))
                        wake_up_state(task, __TASK_TRACED);
                else
-                       task->state = TASK_TRACED;
+                       WRITE_ONCE(task->__state, TASK_TRACED);
        }
        spin_unlock_irq(&task->sighand->siglock);
 }
@@ -239,7 +256,7 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state)
         */
        read_lock(&tasklist_lock);
        if (child->ptrace && child->parent == current) {
-               WARN_ON(child->state == __TASK_TRACED);
+               WARN_ON(READ_ONCE(child->__state) == __TASK_TRACED);
                /*
                 * child->sighand can't be NULL, release_task()
                 * does ptrace_unlink() before __exit_signal().
@@ -256,7 +273,7 @@ static int ptrace_check_attach(struct task_struct *child, bool ignore_state)
                         * ptrace_stop() changes ->state back to TASK_RUNNING,
                         * so we should not worry about leaking __TASK_TRACED.
                         */
-                       WARN_ON(child->state == __TASK_TRACED);
+                       WARN_ON(READ_ONCE(child->__state) == __TASK_TRACED);
                        ret = -ESRCH;
                }
        }
@@ -779,6 +796,24 @@ static int ptrace_peek_siginfo(struct task_struct *child,
        return ret;
 }
 
+#ifdef CONFIG_RSEQ
+static long ptrace_get_rseq_configuration(struct task_struct *task,
+                                         unsigned long size, void __user *data)
+{
+       struct ptrace_rseq_configuration conf = {
+               .rseq_abi_pointer = (u64)(uintptr_t)task->rseq,
+               .rseq_abi_size = sizeof(*task->rseq),
+               .signature = task->rseq_sig,
+               .flags = 0,
+       };
+
+       size = min_t(unsigned long, size, sizeof(conf));
+       if (copy_to_user(data, &conf, size))
+               return -EFAULT;
+       return sizeof(conf);
+}
+#endif
+
 #ifdef PTRACE_SINGLESTEP
 #define is_singlestep(request)         ((request) == PTRACE_SINGLESTEP)
 #else
@@ -1222,6 +1257,12 @@ int ptrace_request(struct task_struct *child, long request,
                ret = seccomp_get_metadata(child, addr, datavp);
                break;
 
+#ifdef CONFIG_RSEQ
+       case PTRACE_GET_RSEQ_CONFIGURATION:
+               ret = ptrace_get_rseq_configuration(child, addr, datavp);
+               break;
+#endif
+
        default:
                break;
        }