add ptrace(2) TO_NOEXEC flag

This commit is contained in:
David van Moolenbroek 2010-01-05 09:30:28 +00:00
parent 709ca777bd
commit 0dcf5b7aa8
5 changed files with 100 additions and 37 deletions

View 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 */

View 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

View 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);

View 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*/);

View 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);