Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src Introduce new ptrace(2) API to allow/prevent exection of LWP



details:   https://anonhg.NetBSD.org/src/rev/29933f50a3e1
branches:  trunk
changeset: 821947:29933f50a3e1
user:      kamil <kamil%NetBSD.org@localhost>
date:      Wed Feb 22 23:43:43 2017 +0000

description:
Introduce new ptrace(2) API to allow/prevent exection of LWP

Introduce new API for debuggers to allow/prevent execution of the specified
thread.

New ptrace(2) operations:

     PT_RESUME     Allow execution of a specified thread, change its state
                   from suspended to continued.  The addr argument is unused.
                   The data argument specifies the LWP ID.

                   This call is equivalent to _lwp_continue(2) called by a
                   traced process.  This call does not change the general
                   process state from stopped to continued.

     PT_SUSPEND    Prevent execution of a specified thread, change its state
                   from continued to suspended.  The addr argument is unused.
                   The data argument specifies the requested LWP ID.

                   This call is equivalent to _lwp_suspend(2) called by a
                   traced process.  This call does not change the general
                   process state from continued to stopped.

This interface is modeled after FreeBSD, however with NetBSD specific arguments
passed to ptrace(2) -- FreeBSD passes only thread id, NetBSD passes process and
thread id.

Extend PT_LWPINFO operation in ptrace(2) to report suspended threads. In the
ptrace_lwpinfo structure in pl_event next to PL_EVENT_NONE and PL_EVENT_SIGNAL
add new value PL_EVENT_SUSPENDED.

Add new errno(2) value EDEADLK that might be returned by ptrace(2). It prevents
dead-locking in a scenario of resuming a process or thread that is prevented
from execution. This fixes bug that old API was vulnerable to this scenario.

Kernel bump delayed till introduction of PT_GETDBREGS/PT_SETDBREGS soon.

Add new ATF tests:
 - resume1
   Verify that a thread can be suspended by a debugger and later
   resumed by the debugger

 - suspend1
   Verify that a thread can be suspended by a debugger and later
   resumed by a tracee

 - suspend2
   Verify that the while the only thread within a process is
   suspended, the whole process cannot be unstopped

Sponsored by <The NetBSD Foundation>

diffstat:

 lib/libc/sys/ptrace.2        |   40 ++++-
 sys/kern/sys_ptrace_common.c |   76 ++++++++-
 sys/sys/ptrace.h             |   15 +-
 tests/kernel/t_ptrace_wait.c |  341 ++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 459 insertions(+), 13 deletions(-)

diffs (truncated from 615 to 300 lines):

diff -r 4bc9235d7679 -r 29933f50a3e1 lib/libc/sys/ptrace.2
--- a/lib/libc/sys/ptrace.2     Wed Feb 22 15:08:55 2017 +0000
+++ b/lib/libc/sys/ptrace.2     Wed Feb 22 23:43:43 2017 +0000
@@ -1,4 +1,4 @@
-.\"    $NetBSD: ptrace.2,v 1.59 2017/02/12 06:09:53 kamil Exp $
+.\"    $NetBSD: ptrace.2,v 1.60 2017/02/22 23:43:43 kamil Exp $
 .\"
 .\" This file is in the public domain.
 .Dd February 12, 2016
@@ -355,6 +355,7 @@
 .Bl -tag -width 30n -offset indent -compact
 .It Dv PL_EVENT_NONE
 .It Dv PL_EVENT_SIGNAL
+.It Dv PL_EVENT_SUSPENDED
 .El
 .Pp
 The
@@ -539,6 +540,34 @@
 .Fa data
 argument contains the LWP ID of the thread whose mask is to be read.
 If zero is supplied, the first thread of the process is read.
+.It Dv PT_RESUME
+Allow execution of a specified thread,
+change its state from suspended to continued.
+The
+.Fa addr
+argument is unused.
+The
+.Fa data
+argument specifies the LWP ID.
+.Pp
+This call is equivalent to
+.Xr _lwp_continue 2
+called by a traced process.
+This call does not change the general process state from stopped to continued.
+.It Dv PT_SUSPEND
+Prevent execution of a specified thread,
+change its state from continued to suspended.
+The
+.Fa addr
+argument is unused.
+The
+.Fa data
+argument specifies the requested LWP ID.
+.Pp
+This call is equivalent to
+.Xr _lwp_suspend 2
+called by a traced process.
+This call does not change the general process state from continued to stopped.
 .El
 .Pp
 Additionally, the following requests exist but are
@@ -692,6 +721,8 @@
 .Dv PT_ATTACH )
 specified a process that wasn't stopped.
 .El
+.It Bq Er EDEADLK
+An attempt to unstop a process with locked threads.
 .It Bq Er EINVAL
 .Bl -bullet -compact
 .It
@@ -761,3 +792,10 @@
 .Dv PTRACE_VFORK
 is currently unimplemented and it will return
 .Er ENOTSUP .
+.Pp
+.Dv PT_SET_SIGINFO ,
+.Dv PT_RESUME
+and
+.Dv PT_SUSPEND
+can change the image of process returned by
+.Dv PT_LWPINFO .
diff -r 4bc9235d7679 -r 29933f50a3e1 sys/kern/sys_ptrace_common.c
--- a/sys/kern/sys_ptrace_common.c      Wed Feb 22 15:08:55 2017 +0000
+++ b/sys/kern/sys_ptrace_common.c      Wed Feb 22 23:43:43 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: sys_ptrace_common.c,v 1.14 2017/02/12 06:09:52 kamil Exp $     */
+/*     $NetBSD: sys_ptrace_common.c,v 1.15 2017/02/22 23:43:43 kamil Exp $     */
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -118,7 +118,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.14 2017/02/12 06:09:52 kamil Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sys_ptrace_common.c,v 1.15 2017/02/22 23:43:43 kamil Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ptrace.h"
@@ -236,6 +236,8 @@
        case PT_SYSCALL:
        case PT_SYSCALLEMU:
        case PT_DUMPCORE:
+       case PT_RESUME:
+       case PT_SUSPEND:
                result = KAUTH_RESULT_ALLOW;
                break;
 
@@ -457,6 +459,8 @@
        case  PT_SET_EVENT_MASK:
        case  PT_GET_EVENT_MASK:
        case  PT_GET_PROCESS_STATE:
+       case  PT_RESUME:
+       case  PT_SUSPEND:
                /*
                 * You can't do what you want to the process if:
                 *      (1) It's not being traced at all,
@@ -755,6 +759,34 @@
                        break;
                }
 
+               /* Prevent process deadlock */
+               if (resume_all) {
+#ifdef PT_STEP
+                       if (req == PT_STEP) {
+                               if (lt->l_flag & LW_WSUSPEND) {
+                                       error = EDEADLK;
+                                       break;
+                               }
+                       } else
+#endif
+                       {
+                               error = EDEADLK;
+                               LIST_FOREACH(lt2, &t->p_lwps, l_sibling) {
+                                       if ((lt2->l_flag & LW_WSUSPEND) == 0) {
+                                               error = 0;
+                                               break;
+                                       }
+                               }
+                               if (error != 0)
+                                       break;
+                       }
+               } else {
+                       if (lt->l_flag & LW_WSUSPEND) {
+                               error = EDEADLK;
+                               break;
+                       }
+               }
+
                /* If the address parameter is not (int *)1, set the pc. */
                if ((int *)addr != (int *)1) {
                        error = process_set_pc(lt, addr);
@@ -968,15 +1000,18 @@
                if (lt) {
                        lwp_addref(lt);
                        pl.pl_lwpid = lt->l_lid;
+
+                       if (lt->l_flag & LW_WSUSPEND)
+                               pl.pl_event = PL_EVENT_SUSPENDED;
                        /*
                         * If we match the lwp, or it was sent to every lwp,
                         * we set PL_EVENT_SIGNAL.
                         * XXX: ps_lwp == 0 means everyone and noone, so
                         * check ps_signo too.
                         */
-                       if (lt->l_lid == t->p_sigctx.ps_lwp
-                           || (t->p_sigctx.ps_lwp == 0 &&
-                               t->p_sigctx.ps_info._signo))
+                       else if (lt->l_lid == t->p_sigctx.ps_lwp
+                                || (t->p_sigctx.ps_lwp == 0 &&
+                                    t->p_sigctx.ps_info._signo))
                                pl.pl_event = PL_EVENT_SIGNAL;
                }
                mutex_exit(t->p_lock);
@@ -1072,6 +1107,37 @@
                        
                break;
 
+       case  PT_RESUME:
+               write = 1;
+
+       case  PT_SUSPEND:
+               /* write = 0 done above. */
+
+               tmp = data;
+               if (tmp != 0 && t->p_nlwps > 1) {
+                       lwp_delref(lt);
+                       mutex_enter(t->p_lock);
+                       lt = lwp_find(t, tmp);
+                       if (lt == NULL) {
+                               mutex_exit(t->p_lock);
+                               error = ESRCH;
+                               break;
+                       }
+                       lwp_addref(lt);
+                       mutex_exit(t->p_lock);
+               }
+               if (lt->l_flag & LW_SYSTEM) {
+                       error = EINVAL;
+               } else {
+                       lwp_lock(lt);
+                       if (write == 0)
+                               lt->l_flag |= LW_WSUSPEND;
+                       else
+                               lt->l_flag &= ~LW_WSUSPEND;
+                       lwp_unlock(lt);
+               }
+               break;
+
 #ifdef PT_SETREGS
        case  PT_SETREGS:
                write = 1;
diff -r 4bc9235d7679 -r 29933f50a3e1 sys/sys/ptrace.h
--- a/sys/sys/ptrace.h  Wed Feb 22 15:08:55 2017 +0000
+++ b/sys/sys/ptrace.h  Wed Feb 22 23:43:43 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: ptrace.h,v 1.56 2017/02/12 06:09:52 kamil Exp $        */
+/*     $NetBSD: ptrace.h,v 1.57 2017/02/22 23:43:43 kamil Exp $        */
 
 /*-
  * Copyright (c) 1984, 1993
@@ -57,6 +57,8 @@
 #define        PT_GET_SIGINFO          20      /* get signal state, defined below */
 #define        PT_SET_SIGMASK          21      /* set signal mask */
 #define        PT_GET_SIGMASK          22      /* get signal mask */
+#define        PT_RESUME               23      /* allow execution of the LWP */
+#define        PT_SUSPEND              24      /* prevent execution of the LWP */
 
 #define        PT_FIRSTMACH            32      /* for machine-specific requests */
 #include <machine/ptrace.h>            /* machine-specific requests, if any */
@@ -83,8 +85,10 @@
 /* 18 */    "PT_GET_PROCESS_STATE", \
 /* 19 */    "PT_SET_SIGINFO", \
 /* 20 */    "PT_GET_SIGINFO", \
-/* 20 */    "PT_GET_SIGMASK", \
-/* 20 */    "PT_GET_SIGMASK",
+/* 21 */    "PT_GET_SIGMASK", \
+/* 22 */    "PT_GET_SIGMASK", \
+/* 23 */    "PT_RESUME", \
+/* 24 */    "PT_SUSPEND",
 
 /* PT_{G,S}EVENT_MASK */
 typedef struct ptrace_event {
@@ -135,8 +139,9 @@
        /* Add fields at the end */
 };
 
-#define PL_EVENT_NONE  0
-#define PL_EVENT_SIGNAL        1
+#define PL_EVENT_NONE          0
+#define PL_EVENT_SIGNAL                1
+#define PL_EVENT_SUSPENDED     2
 
 /*
  * Hardware Watchpoints
diff -r 4bc9235d7679 -r 29933f50a3e1 tests/kernel/t_ptrace_wait.c
--- a/tests/kernel/t_ptrace_wait.c      Wed Feb 22 15:08:55 2017 +0000
+++ b/tests/kernel/t_ptrace_wait.c      Wed Feb 22 23:43:43 2017 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: t_ptrace_wait.c,v 1.70 2017/02/12 06:09:52 kamil Exp $ */
+/*     $NetBSD: t_ptrace_wait.c,v 1.71 2017/02/22 23:43:43 kamil Exp $ */
 
 /*-
  * Copyright (c) 2016 The NetBSD Foundation, Inc.
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__RCSID("$NetBSD: t_ptrace_wait.c,v 1.70 2017/02/12 06:09:52 kamil Exp $");
+__RCSID("$NetBSD: t_ptrace_wait.c,v 1.71 2017/02/22 23:43:43 kamil Exp $");
 
 #include <sys/param.h>
 #include <sys/types.h>
@@ -6947,6 +6947,338 @@
        TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
 }
 
+static void
+lwp_main_stop(void *arg)
+{
+       the_lwp_id = _lwp_self();
+
+       raise(SIGTRAP);
+
+       _lwp_exit();
+}
+
+ATF_TC(suspend1);
+ATF_TC_HEAD(suspend1, tc)
+{
+       atf_tc_set_md_var(tc, "descr",
+           "Verify that a thread can be suspended by a debugger and later "
+           "resumed by a tracee");
+}
+
+ATF_TC_BODY(suspend1, tc)
+{
+       const int exitval = 5;
+       const int sigval = SIGSTOP;
+       pid_t child, wpid;
+#if defined(TWAIT_HAVE_STATUS)
+       int status;
+#endif
+       ucontext_t uc;
+       lwpid_t lid;
+       static const size_t ssize = 16*1024;
+       void *stack;
+       struct ptrace_lwpinfo pl;
+       struct ptrace_siginfo psi;



Home | Main Index | Thread Index | Old Index