Implement signal injection
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>
This commit is contained in:
parent
22894ccff6
commit
a864a32a76
5
defs.h
5
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 *);
|
||||
|
||||
|
15
qualify.c
15
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));
|
||||
|
4
strace.c
4
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;
|
||||
|
16
syscall.c
16
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
|
||||
|
Loading…
x
Reference in New Issue
Block a user