]> Zhao Yanbai Git Server - minix.git/commitdiff
add ptrace(2) TO_NOEXEC flag
authorDavid van Moolenbroek <david@minix3.org>
Tue, 5 Jan 2010 09:30:28 +0000 (09:30 +0000)
committerDavid van Moolenbroek <david@minix3.org>
Tue, 5 Jan 2010 09:30:28 +0000 (09:30 +0000)
include/sys/ptrace.h
man/man2/ptrace.2
servers/pm/exec.c
servers/pm/trace.c
test/test42.c

index 4c84753749b21ddea6d8f7d2ba9695e9e95d5daf..7299db9f234b9180eaae8bca4c43da263a16404f 100644 (file)
@@ -34,6 +34,7 @@
 /* Trace options. */
 #define TO_TRACEFORK   0x1     /* automatically attach to forked children */
 #define TO_ALTEXEC     0x2     /* send SIGSTOP on successful exec() */
+#define TO_NOEXEC      0x4     /* do not send signal on successful exec() */
 
 /* Trace spaces. */
 #define TS_INS         0       /* text space */
index 05f085c89c750cb9a6b63e7a9f136b5560b39908..386d225c105ea2f096f09011faf38eee411d1231 100644 (file)
@@ -117,6 +117,15 @@ The child will be stopped with a \fBSIGSTOP\fP signal right after forking.
 Send \fBSIGSTOP\fP instead of \fBSIGTRAP\fP upon a successful
 .BR execve (2).
 This allows the tracer to disambiguate between this case and other traps.
+.TP
+.B TO_NOEXEC
+Do not send any signal upon a successful
+.BR execve (2).
+.PP
+The default set of trace options when tracing is initiated with \fBT_OK\fP is
+\fB0\fP.
+The default set of trace options after attaching with \fBT_ATTACH\fP is
+\fBTO_NOEXEC\fP.
 .PP
 The \fBT_GETRANGE\fP and \fBT_SETRANGE\fP calls use the following structure:
 .PP
index 7113eefcb05c2ffc71505964ec580e82f8d819d1..e9740f9a52ca25b7435c23748ffe124150094861 100644 (file)
@@ -181,7 +181,8 @@ int result;
        /* Cause a signal if this process is traced.
         * Do this before making the process runnable again!
         */
-       if (rmp->mp_tracer != NO_TRACER)  {
+       if (rmp->mp_tracer != NO_TRACER && !(rmp->mp_trace_flags & TO_NOEXEC))
+       {
                sn = (rmp->mp_trace_flags & TO_ALTEXEC) ? SIGSTOP : SIGTRAP;
 
                check_sig(rmp->mp_pid, sn);
index d706c9b98cab1a9ad84d65a7211ddb8e68c6362c..9b4a0783c1dff5b21261d5d0066dd0b8c44ba530 100644 (file)
@@ -83,6 +83,7 @@ PUBLIC int do_trace()
        if (child->mp_tracer != NO_TRACER) return(EBUSY);
 
        child->mp_tracer = who_p;
+       child->mp_trace_flags = TO_NOEXEC;
 
        sig_proc(child, SIGSTOP, TRUE /*trace*/);
 
index 2b00e1bae378d8bbe26650c3fdc3e1d2ba786319..39787821b795f587975c44a50d8cd9c1cecdf466 100644 (file)
@@ -62,11 +62,13 @@ _PROTOTYPE(void test_syscall_child, (void));
 _PROTOTYPE(void test_syscall, (void));
 _PROTOTYPE(void test_tracefork_child, (void));
 _PROTOTYPE(void test_tracefork, (void));
+_PROTOTYPE(void sigexec, (int setflag, int opt, int *traps, int *stop));
+_PROTOTYPE(void test_trapexec, (void));
+_PROTOTYPE(void test_altexec, (void));
+_PROTOTYPE(void test_noexec, (void));
+_PROTOTYPE(void test_defexec, (void));
 _PROTOTYPE(void test_reattach_child, (void));
 _PROTOTYPE(void test_reattach, (void));
-_PROTOTYPE(void altexec, (int setflag, int *traps, int *stop));
-_PROTOTYPE(void test_altexec, (void));
-_PROTOTYPE(void test_noaltexec, (void));
 _PROTOTYPE(void e, (int n));
 _PROTOTYPE(void quit, (void));
 
@@ -112,23 +114,25 @@ int a;
 {
   attach = a;
 
-  if (m & 0000001) timed_test(test_wait);
-  if (m & 0000002) timed_test(test_exec);
-  if (m & 0000004) timed_test(test_step);
-  if (m & 0000010) timed_test(test_sig);
-  if (m & 0000020) timed_test(test_exit);
-  if (m & 0000040) timed_test(test_term);
-  if (m & 0000100) timed_test(test_catch);
-  if (m & 0000200) timed_test(test_kill);
-  if (m & 0000400) timed_test(test_attach);
-  if (m & 0001000) timed_test(test_detach);
-  if (m & 0002000) timed_test(test_death);
-  if (m & 0004000) timed_test(test_zdeath);
-  if (m & 0010000) timed_test(test_syscall);
-  if (m & 0020000) timed_test(test_tracefork);
-  if (m & 0040000) timed_test(test_altexec);
-  if (m & 0100000) timed_test(test_noaltexec);
-  if (m & 0200000) test_reattach(); /* not timed, catches SIGALRM */
+  if (m & 00000001) timed_test(test_wait);
+  if (m & 00000002) timed_test(test_exec);
+  if (m & 00000004) timed_test(test_step);
+  if (m & 00000010) timed_test(test_sig);
+  if (m & 00000020) timed_test(test_exit);
+  if (m & 00000040) timed_test(test_term);
+  if (m & 00000100) timed_test(test_catch);
+  if (m & 00000200) timed_test(test_kill);
+  if (m & 00000400) timed_test(test_attach);
+  if (m & 00001000) timed_test(test_detach);
+  if (m & 00002000) timed_test(test_death);
+  if (m & 00004000) timed_test(test_zdeath);
+  if (m & 00010000) timed_test(test_syscall);
+  if (m & 00020000) timed_test(test_tracefork);
+  if (m & 00040000) timed_test(test_trapexec);
+  if (m & 00100000) timed_test(test_altexec);
+  if (m & 00200000) timed_test(test_noexec);
+  if (m & 00400000) timed_test(test_defexec);
+  if (m & 01000000) test_reattach(); /* not timed, catches SIGALRM */
 }
   
 static jmp_buf timed_test_context;
@@ -494,6 +498,9 @@ void test_exec()
   pid_t pid;
   int r, status;
 
+  /* This test covers the T_OK case. */
+  if (attach != 0) return;
+
   subtest = 2;
 
   pid = traced_fork(test_exec_child);
@@ -696,6 +703,7 @@ void test_exit()
 
 void test_term_child()
 {
+  signal(SIGUSR1, SIG_DFL);
   signal(SIGUSR2, dummy_handler);
 
   WRITE(0);
@@ -1274,8 +1282,9 @@ void test_tracefork()
   traced_wait();
 }
 
-void altexec(setflag, traps, stop)
+void sigexec(setflag, opt, traps, stop)
 int setflag;
+int opt;
 int *traps;
 int *stop;
 {
@@ -1290,7 +1299,7 @@ int *stop;
   if (!_WIFSTOPPED(status)) e(3);
   if (WSTOPSIG(status) != SIGSTOP) e(4);
 
-  if (setflag && ptrace(T_SETOPT, pid, 0, TO_ALTEXEC) != 0) e(5);
+  if (setflag && ptrace(T_SETOPT, pid, 0, opt) != 0) e(5);
 
   WRITE(0);
 
@@ -1326,13 +1335,31 @@ int *stop;
   traced_wait();
 }
 
-void test_altexec()
+void test_trapexec()
 {
   int traps, stop;
 
   subtest = 15;
 
-  altexec(1, &traps, &stop);
+  sigexec(1, 0, &traps, &stop);
+
+  /* The exec does not cause a SIGSTOP. This gives us an even number of traps;
+   * as above, but plus the exec()'s extra SIGTRAP. This trap is
+   * indistinguishable from a syscall trap, especially when considering failed
+   * exec() calls and immediately following signal handler invocations.
+   */
+  if (traps < 4) e(12);
+  if (traps % 2) e(13);
+  if (stop >= 0) e(14);
+}
+
+void test_altexec()
+{
+  int traps, stop;
+
+  subtest = 16;
+
+  sigexec(1, TO_ALTEXEC, &traps, &stop);
 
   /* The exec causes a SIGSTOP. This gives us an odd number of traps: a pair
    * for each system call, plus one for the final exit(). The stop must have
@@ -1344,24 +1371,45 @@ void test_altexec()
   if (!(stop % 2)) e(15);
 }
 
-void test_noaltexec()
+void test_noexec()
 {
   int traps, stop;
 
-  subtest = 16;
+  subtest = 17;
 
-  altexec(0, &traps, &stop);
+  sigexec(1, TO_NOEXEC, &traps, &stop);
 
-  /* The exec does not cause a SIGSTOP. This gives us an even number of traps;
-   * as above, but plus the exec()'s extra SIGTRAP. This trap is
-   * indistinguishable from a syscall trap, especially when considering failed
-   * exec() calls and immediately following signal handler invocations.
-   */
-  if (traps < 4) e(12);
-  if (traps % 2) e(13);
+  /* The exec causes no signal at all. As above, but without the SIGSTOPs. */
+  if (traps < 3) e(12);
+  if (!(traps % 2)) e(13);
   if (stop >= 0) e(14);
 }
 
+void test_defexec()
+{
+  int traps, stop;
+
+  /* We want to test the default of T_OK (0) and T_ATTACH (TO_NOEXEC). */
+  if (attach != 0 && attach != 1) return;
+
+  subtest = 18;
+
+  /* Do not set any options this time. */
+  sigexec(0, 0, &traps, &stop);
+
+  /* See above. */
+  if (attach == 0) {
+       if (traps < 4) e(12);
+       if (traps % 2) e(13);
+       if (stop >= 0) e(14);
+  }
+  else {
+       if (traps < 3) e(15);
+       if (!(traps % 2)) e(16);
+       if (stop >= 0) e(17);
+  }
+}
+
 void test_reattach_child()
 {
   struct timeval tv;
@@ -1380,7 +1428,7 @@ void test_reattach()
   pid_t pid;
   int r, status, count;
 
-  subtest = 17;
+  subtest = 19;
 
   pid = traced_fork(test_reattach_child);
 
@@ -1397,6 +1445,9 @@ void test_reattach()
 
   /* Start tracing system calls. We don't know how many there will be until
    * we reach the child's select(), so we have to interrupt ourselves.
+   * The hard assumption here is that the child is able to enter the select()
+   * within a second, despite being traced. If this is not the case, the test
+   * may hang or fail, and the child may die from a SIGTRAP.
    */
   if (ptrace(T_SYSCALL, pid, 0, 0) != 0) e(5);
 
@@ -1439,7 +1490,7 @@ void test_reattach()
   if ((r = WEXITSTATUS(status)) != 42) e(r);
 
   /* We must not have seen the select()'s syscall leave event, and the last
-   * event will be the syscall enter for the exec().
+   * event will be the syscall enter for the exit().
    */
   if (!(count % 2)) e(21);