Implement injection of syscalls with no side effects as an alternative to -1
* defs.h (INJECT_F_SYSCALL, INJECT_ACTION_FLAGS, TCB_TAMPERED_NO_FAIL, syscall_tampered_nofail): New macros. (inject_data): Add scno field. * filter_qualify.c (struct inject_personality_data): New type. (parse_inject_token): Add "pdata" argument, parse "syscall=" option. (parse_inject_expression): Add "pdata" argument, forward it to parse_inject_token. (qualify_inject_common) <pdata>: New variable array, pass it to parse_inject_expression, copy it into inject_vec. * syscall.c (tamper_with_syscall_entering): Inject the specified syscall if INJECT_F_SYSCALL is set. (tamper_with_syscall_exiting): Update the check for a failed syscall injection. (get_syscall_result): Update get_error invocation. * strace.1.in: Document new syscall injection expression. * NEWS: Mention it. Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org> Closes: https://github.com/strace/strace/issues/26
This commit is contained in:
parent
7e0c7e0e88
commit
883617d8b7
2
NEWS
2
NEWS
@ -19,6 +19,8 @@ Noteworthy changes in release ?.?? (????-??-??)
|
||||
using --enable-stacktrace option.
|
||||
* Added -X option for configuring xlat output formatting (addresses
|
||||
Debian bug #692915).
|
||||
* Implemented injection of syscalls with no side effects as an alternative
|
||||
to injection of an invalid syscall (-e inject=SET:syscall= expression).
|
||||
* Improved support for reproducible builds (addresses Debian bug #896016).
|
||||
* Implemented decoding of BPF_PROG_QUERY and BPF_RAW_TRACEPOINT_OPEN bpf
|
||||
syscall commands.
|
||||
|
15
defs.h
15
defs.h
@ -182,12 +182,22 @@ typedef struct ioctlent {
|
||||
#define INJECT_F_RETVAL 0x04
|
||||
#define INJECT_F_DELAY_ENTER 0x08
|
||||
#define INJECT_F_DELAY_EXIT 0x10
|
||||
#define INJECT_F_SYSCALL 0x20
|
||||
|
||||
#define INJECT_ACTION_FLAGS \
|
||||
(INJECT_F_SIGNAL \
|
||||
|INJECT_F_ERROR \
|
||||
|INJECT_F_RETVAL \
|
||||
|INJECT_F_DELAY_ENTER \
|
||||
|INJECT_F_DELAY_EXIT \
|
||||
)
|
||||
|
||||
struct inject_data {
|
||||
uint8_t flags; /* 5 of 8 flags are used so far */
|
||||
uint8_t flags; /* 6 of 8 flags are used so far */
|
||||
uint8_t signo; /* NSIG <= 128 */
|
||||
uint16_t rval_idx; /* index in retval_vec */
|
||||
uint16_t delay_idx; /* index in delay_data_vec */
|
||||
uint16_t scno; /* syscall to be injected instead of -1 */
|
||||
};
|
||||
|
||||
struct inject_opts {
|
||||
@ -261,6 +271,8 @@ struct tcb {
|
||||
#define TCB_INJECT_DELAY_EXIT 0x800 /* Current syscall needs to be delayed
|
||||
on exit */
|
||||
#define TCB_DELAYED 0x1000 /* Current syscall has been delayed */
|
||||
#define TCB_TAMPERED_NO_FAIL 0x2000 /* We tamper tcb with syscall
|
||||
that should not fail. */
|
||||
|
||||
/* qualifier flags */
|
||||
#define QUAL_TRACE 0x001 /* this system call should be traced */
|
||||
@ -285,6 +297,7 @@ struct tcb {
|
||||
#define recovering(tcp) ((tcp)->flags & TCB_RECOVERING)
|
||||
#define inject_delay_exit(tcp) ((tcp)->flags & TCB_INJECT_DELAY_EXIT)
|
||||
#define syscall_delayed(tcp) ((tcp)->flags & TCB_DELAYED)
|
||||
#define syscall_tampered_nofail(tcp) ((tcp)->flags & TCB_TAMPERED_NO_FAIL)
|
||||
|
||||
#include "xlat.h"
|
||||
|
||||
|
@ -43,6 +43,11 @@ static struct number_set *raw_set;
|
||||
static struct number_set *trace_set;
|
||||
static struct number_set *verbose_set;
|
||||
|
||||
/* Only syscall numbers are personality-specific so far. */
|
||||
struct inject_personality_data {
|
||||
uint16_t scno;
|
||||
};
|
||||
|
||||
static int
|
||||
sigstr_to_uint(const char *s)
|
||||
{
|
||||
@ -102,6 +107,7 @@ parse_delay_token(const char *input, struct inject_opts *fopts, bool isenter)
|
||||
|
||||
static bool
|
||||
parse_inject_token(const char *const token, struct inject_opts *const fopts,
|
||||
struct inject_personality_data *const pdata,
|
||||
const bool fault_tokens_only)
|
||||
{
|
||||
const char *val;
|
||||
@ -137,6 +143,27 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts,
|
||||
/* F == F+0 */
|
||||
fopts->step = 0;
|
||||
}
|
||||
} else if ((val = STR_STRIP_PREFIX(token, "syscall=")) != token) {
|
||||
if (fopts->data.flags & INJECT_F_SYSCALL)
|
||||
return false;
|
||||
|
||||
for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
|
||||
kernel_long_t scno = scno_by_name(val, p, 0);
|
||||
|
||||
if (scno < 0)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* We want to inject only pure system calls with no side
|
||||
* effects.
|
||||
*/
|
||||
if (!(sysent_vec[p][scno].sys_flags & TRACE_PURE))
|
||||
return false;
|
||||
|
||||
pdata[p].scno = scno;
|
||||
}
|
||||
|
||||
fopts->data.flags |= INJECT_F_SYSCALL;
|
||||
} else if ((val = STR_STRIP_PREFIX(token, "error=")) != token) {
|
||||
if (fopts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL))
|
||||
return false;
|
||||
@ -223,6 +250,7 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts,
|
||||
static const char *
|
||||
parse_inject_expression(char *const str,
|
||||
struct inject_opts *const fopts,
|
||||
struct inject_personality_data *const pdata,
|
||||
const bool fault_tokens_only)
|
||||
{
|
||||
if (str[0] == '\0' || str[0] == ':')
|
||||
@ -233,7 +261,7 @@ parse_inject_expression(char *const str,
|
||||
|
||||
char *token;
|
||||
while ((token = strtok_r(NULL, ":", &saveptr))) {
|
||||
if (!parse_inject_token(token, fopts, fault_tokens_only))
|
||||
if (!parse_inject_token(token, fopts, pdata, fault_tokens_only))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -308,9 +336,10 @@ qualify_inject_common(const char *const str,
|
||||
.delay_idx = -1
|
||||
}
|
||||
};
|
||||
struct inject_personality_data pdata[SUPPORTED_PERSONALITIES] = { { 0 } };
|
||||
char *copy = xstrdup(str);
|
||||
const char *name =
|
||||
parse_inject_expression(copy, &opts, fault_tokens_only);
|
||||
parse_inject_expression(copy, &opts, pdata, fault_tokens_only);
|
||||
if (!name)
|
||||
error_msg_and_die("invalid %s '%s'", description, str);
|
||||
|
||||
@ -321,7 +350,7 @@ qualify_inject_common(const char *const str,
|
||||
free(copy);
|
||||
|
||||
/* If neither of retval, error, signal or delay is specified, then ... */
|
||||
if (!opts.data.flags) {
|
||||
if (!(opts.data.flags & INJECT_ACTION_FLAGS)) {
|
||||
if (fault_tokens_only) {
|
||||
/* in fault= syntax the default error code is ENOSYS. */
|
||||
opts.data.rval_idx = retval_new(ENOSYS);
|
||||
@ -353,6 +382,10 @@ qualify_inject_common(const char *const str,
|
||||
if (is_number_in_set_array(i, tmp_set, p)) {
|
||||
add_number_to_set_array(i, inject_set, p);
|
||||
inject_vec[p][i] = opts;
|
||||
|
||||
/* Copy per-personality data. */
|
||||
inject_vec[p][i].data.scno =
|
||||
pdata[p].scno;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
11
strace.1.in
11
strace.1.in
@ -626,7 +626,7 @@ Note that this is independent from the normal tracing of the
|
||||
system call which is controlled by the option
|
||||
.BR -e "\ " trace = write .
|
||||
.TP
|
||||
\fB\-e\ inject\fR=\,\fIset\/\fR[:\fBerror\fR=\,\fIerrno\/\fR|:\fBretval\fR=\,\fIvalue\/\fR][:\fBsignal\fR=\,\fIsig\/\fR][:\fBdelay_enter\fR=\,\fIusecs\/\fR][:\fBdelay_exit\fR=\,\fIusecs\/\fR][:\fBwhen\fR=\,\fIexpr\/\fR]
|
||||
\fB\-e\ inject\fR=\,\fIset\/\fR[:\fBerror\fR=\,\fIerrno\/\fR|:\fBretval\fR=\,\fIvalue\/\fR][:\fBsignal\fR=\,\fIsig\/\fR][:\fBsyscall\fR=\fIsyscall\fR][:\fBdelay_enter\fR=\,\fIusecs\/\fR][:\fBdelay_exit\fR=\,\fIusecs\/\fR][:\fBwhen\fR=\,\fIexpr\/\fR]
|
||||
Perform syscall tampering for the specified set of syscalls.
|
||||
|
||||
At least one of
|
||||
@ -644,7 +644,8 @@ are mutually exclusive.
|
||||
|
||||
If :\fBerror\fR=\,\fIerrno\/\fR option is specified,
|
||||
a fault is injected into a syscall invocation:
|
||||
the syscall number is replaced by -1 which corresponds to an invalid syscall,
|
||||
the syscall number is replaced by -1 which corresponds to an invalid syscall
|
||||
(unless a syscall is specified with :\fBsyscall=\fR option),
|
||||
and the error code is specified using a symbolic
|
||||
.I errno
|
||||
value like
|
||||
@ -685,6 +686,12 @@ If both :\fBerror\fR=\,\fIerrno\/\fR or :\fBretval\fR=\,\fIvalue\/\fR
|
||||
and :\fBsignal\fR=\,\fIsig\/\fR options are specified, then both
|
||||
a fault or success is injected and a signal is delivered.
|
||||
|
||||
if :\fBsyscall\fR=\fIsyscall\fR option is specified, the corresponding syscall
|
||||
with no side effects is injected instead of -1.
|
||||
Currently, only "pure" (see
|
||||
.BR "-e trace" = "%pure"
|
||||
description) syscalls can be specified there.
|
||||
|
||||
Unless a :\fBwhen\fR=\,\fIexpr\fR subexpression is specified,
|
||||
an injection is being made into every invocation of each syscall from the
|
||||
.IR set .
|
||||
|
29
syscall.c
29
syscall.c
@ -507,9 +507,18 @@ tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo)
|
||||
if (!recovering(tcp)) {
|
||||
if (opts->data.flags & INJECT_F_SIGNAL)
|
||||
*signo = opts->data.signo;
|
||||
if (opts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL) &&
|
||||
!arch_set_scno(tcp, -1))
|
||||
tcp->flags |= TCB_TAMPERED;
|
||||
if (opts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL)) {
|
||||
kernel_long_t scno =
|
||||
(opts->data.flags & INJECT_F_SYSCALL)
|
||||
? (kernel_long_t) shuffle_scno(opts->data.scno)
|
||||
: -1;
|
||||
|
||||
if (!arch_set_scno(tcp, scno)) {
|
||||
tcp->flags |= TCB_TAMPERED;
|
||||
if (scno != -1)
|
||||
tcp->flags |= TCB_TAMPERED_NO_FAIL;
|
||||
}
|
||||
}
|
||||
if (opts->data.flags & INJECT_F_DELAY_ENTER)
|
||||
delay_tcb(tcp, opts->data.delay_idx, true);
|
||||
if (opts->data.flags & INJECT_F_DELAY_EXIT)
|
||||
@ -532,10 +541,11 @@ tamper_with_syscall_exiting(struct tcb *tcp)
|
||||
if (!syscall_tampered(tcp))
|
||||
return 0;
|
||||
|
||||
if (!syserror(tcp)) {
|
||||
error_msg("Failed to tamper with process %d: got no error "
|
||||
"(return value %#" PRI_klx ")",
|
||||
tcp->pid, tcp->u_rval);
|
||||
if (!syserror(tcp) ^ !!syscall_tampered_nofail(tcp)) {
|
||||
error_msg("Failed to tamper with process %d: unexpectedly got"
|
||||
" %serror (return value %#" PRI_klx ", error %lu)",
|
||||
tcp->pid, syscall_tampered_nofail(tcp) ? "" : "no ",
|
||||
tcp->u_rval, tcp->u_error);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@ -1237,8 +1247,9 @@ get_syscall_result(struct tcb *tcp)
|
||||
return -1;
|
||||
tcp->u_error = 0;
|
||||
get_error(tcp,
|
||||
!(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS)
|
||||
|| syscall_tampered(tcp));
|
||||
(!(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS)
|
||||
|| syscall_tampered(tcp))
|
||||
&& !syscall_tampered_nofail(tcp));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user