Skip to content

Commit 883617d

Browse files
esyrldv-alt
andcommitted
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 <[email protected]> Closes: strace/strace#26
1 parent 7e0c7e0 commit 883617d

File tree

5 files changed

+81
-15
lines changed

5 files changed

+81
-15
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ Noteworthy changes in release ?.?? (????-??-??)
1919
using --enable-stacktrace option.
2020
* Added -X option for configuring xlat output formatting (addresses
2121
Debian bug #692915).
22+
* Implemented injection of syscalls with no side effects as an alternative
23+
to injection of an invalid syscall (-e inject=SET:syscall= expression).
2224
* Improved support for reproducible builds (addresses Debian bug #896016).
2325
* Implemented decoding of BPF_PROG_QUERY and BPF_RAW_TRACEPOINT_OPEN bpf
2426
syscall commands.

defs.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,12 +182,22 @@ typedef struct ioctlent {
182182
#define INJECT_F_RETVAL 0x04
183183
#define INJECT_F_DELAY_ENTER 0x08
184184
#define INJECT_F_DELAY_EXIT 0x10
185+
#define INJECT_F_SYSCALL 0x20
186+
187+
#define INJECT_ACTION_FLAGS \
188+
(INJECT_F_SIGNAL \
189+
|INJECT_F_ERROR \
190+
|INJECT_F_RETVAL \
191+
|INJECT_F_DELAY_ENTER \
192+
|INJECT_F_DELAY_EXIT \
193+
)
185194

186195
struct inject_data {
187-
uint8_t flags; /* 5 of 8 flags are used so far */
196+
uint8_t flags; /* 6 of 8 flags are used so far */
188197
uint8_t signo; /* NSIG <= 128 */
189198
uint16_t rval_idx; /* index in retval_vec */
190199
uint16_t delay_idx; /* index in delay_data_vec */
200+
uint16_t scno; /* syscall to be injected instead of -1 */
191201
};
192202

193203
struct inject_opts {
@@ -261,6 +271,8 @@ struct tcb {
261271
#define TCB_INJECT_DELAY_EXIT 0x800 /* Current syscall needs to be delayed
262272
on exit */
263273
#define TCB_DELAYED 0x1000 /* Current syscall has been delayed */
274+
#define TCB_TAMPERED_NO_FAIL 0x2000 /* We tamper tcb with syscall
275+
that should not fail. */
264276

265277
/* qualifier flags */
266278
#define QUAL_TRACE 0x001 /* this system call should be traced */
@@ -285,6 +297,7 @@ struct tcb {
285297
#define recovering(tcp) ((tcp)->flags & TCB_RECOVERING)
286298
#define inject_delay_exit(tcp) ((tcp)->flags & TCB_INJECT_DELAY_EXIT)
287299
#define syscall_delayed(tcp) ((tcp)->flags & TCB_DELAYED)
300+
#define syscall_tampered_nofail(tcp) ((tcp)->flags & TCB_TAMPERED_NO_FAIL)
288301

289302
#include "xlat.h"
290303

filter_qualify.c

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,11 @@ static struct number_set *raw_set;
4343
static struct number_set *trace_set;
4444
static struct number_set *verbose_set;
4545

46+
/* Only syscall numbers are personality-specific so far. */
47+
struct inject_personality_data {
48+
uint16_t scno;
49+
};
50+
4651
static int
4752
sigstr_to_uint(const char *s)
4853
{
@@ -102,6 +107,7 @@ parse_delay_token(const char *input, struct inject_opts *fopts, bool isenter)
102107

103108
static bool
104109
parse_inject_token(const char *const token, struct inject_opts *const fopts,
110+
struct inject_personality_data *const pdata,
105111
const bool fault_tokens_only)
106112
{
107113
const char *val;
@@ -137,6 +143,27 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts,
137143
/* F == F+0 */
138144
fopts->step = 0;
139145
}
146+
} else if ((val = STR_STRIP_PREFIX(token, "syscall=")) != token) {
147+
if (fopts->data.flags & INJECT_F_SYSCALL)
148+
return false;
149+
150+
for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
151+
kernel_long_t scno = scno_by_name(val, p, 0);
152+
153+
if (scno < 0)
154+
return false;
155+
156+
/*
157+
* We want to inject only pure system calls with no side
158+
* effects.
159+
*/
160+
if (!(sysent_vec[p][scno].sys_flags & TRACE_PURE))
161+
return false;
162+
163+
pdata[p].scno = scno;
164+
}
165+
166+
fopts->data.flags |= INJECT_F_SYSCALL;
140167
} else if ((val = STR_STRIP_PREFIX(token, "error=")) != token) {
141168
if (fopts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL))
142169
return false;
@@ -223,6 +250,7 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts,
223250
static const char *
224251
parse_inject_expression(char *const str,
225252
struct inject_opts *const fopts,
253+
struct inject_personality_data *const pdata,
226254
const bool fault_tokens_only)
227255
{
228256
if (str[0] == '\0' || str[0] == ':')
@@ -233,7 +261,7 @@ parse_inject_expression(char *const str,
233261

234262
char *token;
235263
while ((token = strtok_r(NULL, ":", &saveptr))) {
236-
if (!parse_inject_token(token, fopts, fault_tokens_only))
264+
if (!parse_inject_token(token, fopts, pdata, fault_tokens_only))
237265
return NULL;
238266
}
239267

@@ -308,9 +336,10 @@ qualify_inject_common(const char *const str,
308336
.delay_idx = -1
309337
}
310338
};
339+
struct inject_personality_data pdata[SUPPORTED_PERSONALITIES] = { { 0 } };
311340
char *copy = xstrdup(str);
312341
const char *name =
313-
parse_inject_expression(copy, &opts, fault_tokens_only);
342+
parse_inject_expression(copy, &opts, pdata, fault_tokens_only);
314343
if (!name)
315344
error_msg_and_die("invalid %s '%s'", description, str);
316345

@@ -321,7 +350,7 @@ qualify_inject_common(const char *const str,
321350
free(copy);
322351

323352
/* If neither of retval, error, signal or delay is specified, then ... */
324-
if (!opts.data.flags) {
353+
if (!(opts.data.flags & INJECT_ACTION_FLAGS)) {
325354
if (fault_tokens_only) {
326355
/* in fault= syntax the default error code is ENOSYS. */
327356
opts.data.rval_idx = retval_new(ENOSYS);
@@ -353,6 +382,10 @@ qualify_inject_common(const char *const str,
353382
if (is_number_in_set_array(i, tmp_set, p)) {
354383
add_number_to_set_array(i, inject_set, p);
355384
inject_vec[p][i] = opts;
385+
386+
/* Copy per-personality data. */
387+
inject_vec[p][i].data.scno =
388+
pdata[p].scno;
356389
}
357390
}
358391
}

strace.1.in

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ Note that this is independent from the normal tracing of the
626626
system call which is controlled by the option
627627
.BR -e "\ " trace = write .
628628
.TP
629-
\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]
629+
\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]
630630
Perform syscall tampering for the specified set of syscalls.
631631

632632
At least one of
@@ -644,7 +644,8 @@ are mutually exclusive.
644644

645645
If :\fBerror\fR=\,\fIerrno\/\fR option is specified,
646646
a fault is injected into a syscall invocation:
647-
the syscall number is replaced by -1 which corresponds to an invalid syscall,
647+
the syscall number is replaced by -1 which corresponds to an invalid syscall
648+
(unless a syscall is specified with :\fBsyscall=\fR option),
648649
and the error code is specified using a symbolic
649650
.I errno
650651
value like
@@ -685,6 +686,12 @@ If both :\fBerror\fR=\,\fIerrno\/\fR or :\fBretval\fR=\,\fIvalue\/\fR
685686
and :\fBsignal\fR=\,\fIsig\/\fR options are specified, then both
686687
a fault or success is injected and a signal is delivered.
687688

689+
if :\fBsyscall\fR=\fIsyscall\fR option is specified, the corresponding syscall
690+
with no side effects is injected instead of -1.
691+
Currently, only "pure" (see
692+
.BR "-e trace" = "%pure"
693+
description) syscalls can be specified there.
694+
688695
Unless a :\fBwhen\fR=\,\fIexpr\fR subexpression is specified,
689696
an injection is being made into every invocation of each syscall from the
690697
.IR set .

syscall.c

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -507,9 +507,18 @@ tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo)
507507
if (!recovering(tcp)) {
508508
if (opts->data.flags & INJECT_F_SIGNAL)
509509
*signo = opts->data.signo;
510-
if (opts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL) &&
511-
!arch_set_scno(tcp, -1))
512-
tcp->flags |= TCB_TAMPERED;
510+
if (opts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL)) {
511+
kernel_long_t scno =
512+
(opts->data.flags & INJECT_F_SYSCALL)
513+
? (kernel_long_t) shuffle_scno(opts->data.scno)
514+
: -1;
515+
516+
if (!arch_set_scno(tcp, scno)) {
517+
tcp->flags |= TCB_TAMPERED;
518+
if (scno != -1)
519+
tcp->flags |= TCB_TAMPERED_NO_FAIL;
520+
}
521+
}
513522
if (opts->data.flags & INJECT_F_DELAY_ENTER)
514523
delay_tcb(tcp, opts->data.delay_idx, true);
515524
if (opts->data.flags & INJECT_F_DELAY_EXIT)
@@ -532,10 +541,11 @@ tamper_with_syscall_exiting(struct tcb *tcp)
532541
if (!syscall_tampered(tcp))
533542
return 0;
534543

535-
if (!syserror(tcp)) {
536-
error_msg("Failed to tamper with process %d: got no error "
537-
"(return value %#" PRI_klx ")",
538-
tcp->pid, tcp->u_rval);
544+
if (!syserror(tcp) ^ !!syscall_tampered_nofail(tcp)) {
545+
error_msg("Failed to tamper with process %d: unexpectedly got"
546+
" %serror (return value %#" PRI_klx ", error %lu)",
547+
tcp->pid, syscall_tampered_nofail(tcp) ? "" : "no ",
548+
tcp->u_rval, tcp->u_error);
539549

540550
return 1;
541551
}
@@ -1237,8 +1247,9 @@ get_syscall_result(struct tcb *tcp)
12371247
return -1;
12381248
tcp->u_error = 0;
12391249
get_error(tcp,
1240-
!(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS)
1241-
|| syscall_tampered(tcp));
1250+
(!(tcp->s_ent->sys_flags & SYSCALL_NEVER_FAILS)
1251+
|| syscall_tampered(tcp))
1252+
&& !syscall_tampered_nofail(tcp));
12421253

12431254
return 1;
12441255
}

0 commit comments

Comments
 (0)