]> granicus.if.org Git - strace/commitdiff
Implement signal injection
authorSeraphime Kirkovski <kirkseraph@gmail.com>
Tue, 27 Dec 2016 11:14:06 +0000 (12:14 +0100)
committerDmitry V. Levin <ldv@altlinux.org>
Wed, 28 Dec 2016 18:33:28 +0000 (18:33 +0000)
This extends the fault injection capability with :signal=SIG option
which injects a signal on entering each syscall from the specified set.

:signal and :error options are complementary, if they are both specified
the syscall will be fault injected as usual and the specified signal
will be delivered to the tracee.

* defs.h (struct fault_opts): Change the type of err field to int16_t,
add signo field.
(trace_syscall): Add a pointer argument.
* qualify.c: Include "nsig.h".
(parse_fault_token): Handle signal= option.
(qualify_fault): Update default fault_opts.
* strace.c (trace): Forward signal number from trace_syscall
to ptrace_restart(PTRACE_SYSCALL).
* syscall.c (inject_syscall_fault_entering): Add pointer argument,
save there the signal number specified by fault options.  Do not inject
a syscall fault unless instructed by fault options.
(update_syscall_fault_exiting): Update the error code injection check.
(trace_syscall_entering): Add pointer argument, forward it to
inject_syscall_fault_entering.
(trace_syscall): Add pointer argument, forward it to
trace_syscall_entering.

Signed-off-by: Seraphime Kirkovski <kirkseraph@gmail.com>
Signed-off-by: Dmitry V. Levin <ldv@altlinux.org>
defs.h
qualify.c
strace.c
syscall.c

diff --git a/defs.h b/defs.h
index 81d26d5ca61bfe58f305d9011479ab569beb981e..9591e171cfe5df18b3810fbd8c43887c5c99c23f 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -217,7 +217,8 @@ typedef struct ioctlent {
 struct fault_opts {
        uint16_t first;
        uint16_t step;
-       uint16_t err;
+       int16_t err;
+       uint16_t signo;
 };
 
 /* Trace Control Block */
@@ -439,7 +440,7 @@ extern int read_int_from_file(const char *, int *);
 extern void set_sortby(const char *);
 extern void set_overhead(int);
 extern void print_pc(struct tcb *);
-extern int trace_syscall(struct tcb *);
+extern int trace_syscall(struct tcb *, unsigned int *);
 extern void count_syscall(struct tcb *, const struct timeval *);
 extern void call_summary(FILE *);
 
index a7d276cdb42cb9b6b5f491e152af31c156fb79a0..fd203491060d97a51596b87fd3bedcb557931c49 100644 (file)
--- a/qualify.c
+++ b/qualify.c
@@ -26,6 +26,7 @@
  */
 
 #include "defs.h"
+#include "nsig.h"
 
 typedef unsigned int number_slot_t;
 #define BITS_PER_SLOT (sizeof(number_slot_t) * 8)
@@ -414,6 +415,11 @@ parse_fault_token(const char *const token, struct fault_opts *const fopts)
                if (intval < 1)
                        return false;
                fopts->err = intval;
+       } else if ((val = strip_prefix("signal=", token))) {
+               intval = sigstr_to_uint(val);
+               if (intval < 1 || intval > NSIG_BYTES * 8)
+                       return false;
+               fopts->signo = intval;
        } else {
                return false;
        }
@@ -494,7 +500,8 @@ qualify_fault(const char *const str)
        struct fault_opts opts = {
                .first = 1,
                .step = 1,
-               .err = 0
+               .err = -1,
+               .signo = 0
        };
        char *buf = NULL;
        char *name = parse_fault_expression(str, &buf, &opts);
@@ -502,6 +509,12 @@ qualify_fault(const char *const str)
                error_msg_and_die("invalid %s '%s'", "fault argument", str);
        }
 
+       /*
+        * If neither error nor signal is specified,
+        * fallback to the default platform error code.
+        */
+       if (opts.signo == 0 && opts.err == -1)
+               opts.err = 0;
 
        struct number_set tmp_set[SUPPORTED_PERSONALITIES];
        memset(tmp_set, 0, sizeof(tmp_set));
index 4659ddbd2ad516482a7c7c4639688b2bafbeaa3e..23ca108d6e1ba7109c099b2b2a127f37e7fed84d 100644 (file)
--- a/strace.c
+++ b/strace.c
@@ -2447,7 +2447,8 @@ show_stopsig:
         * This should be syscall entry or exit.
         * Handle it.
         */
-       if (trace_syscall(tcp) < 0) {
+       sig = 0;
+       if (trace_syscall(tcp, &sig) < 0) {
                /*
                 * ptrace() failed in trace_syscall().
                 * Likely a result of process disappearing mid-flight.
@@ -2461,6 +2462,7 @@ show_stopsig:
                 */
                return true;
        }
+       goto restart_tracee;
 
 restart_tracee_with_sig_0:
        sig = 0;
index 6613a1e886fba28a53d797552a46f3e4fbd6068e..c821d7efdb01ebb7909c2c73a9111451c3c716e8 100644 (file)
--- a/syscall.c
+++ b/syscall.c
@@ -568,7 +568,7 @@ tcb_fault_opts(struct tcb *tcp)
 
 
 static long
-inject_syscall_fault_entering(struct tcb *tcp)
+inject_syscall_fault_entering(struct tcb *tcp, unsigned int *signo)
 {
        if (!tcp->fault_vec[current_personality]) {
                tcp->fault_vec[current_personality] =
@@ -590,7 +590,9 @@ inject_syscall_fault_entering(struct tcb *tcp)
 
        opts->first = opts->step;
 
-       if (!arch_set_scno(tcp, -1))
+       if (opts->signo > 0)
+               *signo = opts->signo;
+       if (opts->err != -1 && !arch_set_scno(tcp, -1))
                tcp->flags |= TCB_FAULT_INJ;
 
        return 0;
@@ -601,7 +603,7 @@ update_syscall_fault_exiting(struct tcb *tcp)
 {
        struct fault_opts *opts = tcb_fault_opts(tcp);
 
-       if (opts && opts->err && tcp->u_error != opts->err) {
+       if (opts && opts->err > 0 && tcp->u_error != (uint16_t) opts->err) {
                unsigned long u_error = tcp->u_error;
                tcp->u_error = opts->err;
                if (arch_set_error(tcp))
@@ -612,7 +614,7 @@ update_syscall_fault_exiting(struct tcb *tcp)
 }
 
 static int
-trace_syscall_entering(struct tcb *tcp)
+trace_syscall_entering(struct tcb *tcp, unsigned int *sig)
 {
        int res, scno_good;
 
@@ -683,7 +685,7 @@ trace_syscall_entering(struct tcb *tcp)
        }
 
        if (tcp->qual_flg & QUAL_FAULT)
-               inject_syscall_fault_entering(tcp);
+               inject_syscall_fault_entering(tcp, sig);
 
        if (cflag == CFLAG_ONLY_STATS) {
                res = 0;
@@ -962,10 +964,10 @@ trace_syscall_exiting(struct tcb *tcp)
 }
 
 int
-trace_syscall(struct tcb *tcp)
+trace_syscall(struct tcb *tcp, unsigned int *signo)
 {
        return exiting(tcp) ?
-               trace_syscall_exiting(tcp) : trace_syscall_entering(tcp);
+               trace_syscall_exiting(tcp) : trace_syscall_entering(tcp, signo);
 }
 
 bool