Implement -e inject= option
Introduce -e inject= as a general injection option, limit -e fault= option to syscall fault injection. Change default return code of syscall fault injection to ENOSYS. * qualify.c (parse_inject_token): Add fault_tokens_only argument, do not accept retval= and signal= tokens when fault_tokens_only is set to true. (parse_inject_expression): Add fault_tokens_only argument, forward it to parse_inject_token. (qualify_inject_common): New function. (qualify_fault): Use it. (qualify_inject): New function. (qual_options): New entry. * strace.1: Describe -e inject= option. * NEWS: Mention -e inject= option. * tests/qual_fault-syntax.test: Test that -e fault= option does not support retval=, signal=, and multiple error= tokens. * tests/qual_fault.c (DEFAULT_ERRNO): Set to ENOSYS unconditionally. * tests/qual_inject-retval.test: Replace -e fault= option with -e inject= option. * tests/qual_inject-syntax.test: New test. * tests/Makefile.am (MISC_TESTS): Add it.
This commit is contained in:
parent
d1dfcc533e
commit
be73ca4bae
3
NEWS
3
NEWS
@ -2,7 +2,8 @@ Noteworthy changes in release ?.?? (????-??-??)
|
||||
===============================================
|
||||
|
||||
* Improvements
|
||||
* Extended fault injection syntax with signal= and retval= options.
|
||||
* Implemented syscall return value injection (-e inject=SET:retval= option).
|
||||
* Implemented signal injection (-e inject=SET:signal= option).
|
||||
* Updated lists of ioctl commands from Linux 4.10.
|
||||
|
||||
Noteworthy changes in release 4.15 (2016-12-14)
|
||||
|
53
qualify.c
53
qualify.c
@ -373,7 +373,8 @@ find_errno_by_name(const char *name)
|
||||
}
|
||||
|
||||
static bool
|
||||
parse_inject_token(const char *const token, struct inject_opts *const fopts)
|
||||
parse_inject_token(const char *const token, struct inject_opts *const fopts,
|
||||
const bool fault_tokens_only)
|
||||
{
|
||||
const char *val;
|
||||
int intval;
|
||||
@ -417,14 +418,14 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts)
|
||||
if (intval < 1)
|
||||
return false;
|
||||
fopts->rval = -intval;
|
||||
} else if ((val = strip_prefix("retval=", token))) {
|
||||
} else if (!fault_tokens_only && (val = strip_prefix("retval=", token))) {
|
||||
if (fopts->rval != INJECT_OPTS_RVAL_DEFAULT)
|
||||
return false;
|
||||
intval = string_to_uint(val);
|
||||
if (intval < 0)
|
||||
return false;
|
||||
fopts->rval = intval;
|
||||
} else if ((val = strip_prefix("signal=", token))) {
|
||||
} else if (!fault_tokens_only && (val = strip_prefix("signal=", token))) {
|
||||
intval = sigstr_to_uint(val);
|
||||
if (intval < 1 || intval > NSIG_BYTES * 8)
|
||||
return false;
|
||||
@ -438,7 +439,8 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts)
|
||||
|
||||
static char *
|
||||
parse_inject_expression(const char *const s, char **buf,
|
||||
struct inject_opts *const fopts)
|
||||
struct inject_opts *const fopts,
|
||||
const bool fault_tokens_only)
|
||||
{
|
||||
char *saveptr = NULL;
|
||||
char *name = NULL;
|
||||
@ -449,7 +451,7 @@ parse_inject_expression(const char *const s, char **buf,
|
||||
token = strtok_r(NULL, ":", &saveptr)) {
|
||||
if (!name)
|
||||
name = token;
|
||||
else if (!parse_inject_token(token, fopts))
|
||||
else if (!parse_inject_token(token, fopts, fault_tokens_only))
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
@ -504,7 +506,9 @@ qualify_raw(const char *const str)
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_fault(const char *const str)
|
||||
qualify_inject_common(const char *const str,
|
||||
const bool fault_tokens_only,
|
||||
const char *const description)
|
||||
{
|
||||
struct inject_opts opts = {
|
||||
.first = 1,
|
||||
@ -513,22 +517,28 @@ qualify_fault(const char *const str)
|
||||
.signo = 0
|
||||
};
|
||||
char *buf = NULL;
|
||||
char *name = parse_inject_expression(str, &buf, &opts);
|
||||
char *name = parse_inject_expression(str, &buf, &opts, fault_tokens_only);
|
||||
if (!name) {
|
||||
error_msg_and_die("invalid %s '%s'", "fault argument", str);
|
||||
error_msg_and_die("invalid %s '%s'", description, str);
|
||||
}
|
||||
|
||||
/*
|
||||
* If signal is specified but neither retval nor error are specified,
|
||||
* disable syscall fault injection.
|
||||
*/
|
||||
if (opts.signo && opts.rval == INJECT_OPTS_RVAL_DEFAULT) {
|
||||
opts.rval = INJECT_OPTS_RVAL_DISABLE;
|
||||
if (opts.rval == INJECT_OPTS_RVAL_DEFAULT) {
|
||||
/* If neither retval nor error is specified, then ... */
|
||||
if (opts.signo) {
|
||||
/* disable syscall fault injection if signal is specified. */
|
||||
opts.rval = INJECT_OPTS_RVAL_DISABLE;
|
||||
} else if (fault_tokens_only) {
|
||||
/* default error code for fault= syntax is ENOSYS */
|
||||
opts.rval = -ENOSYS;
|
||||
} else {
|
||||
/* an error has to be specified in inject= syntax. */
|
||||
error_msg_and_die("invalid %s '%s'", description, str);
|
||||
}
|
||||
}
|
||||
|
||||
struct number_set tmp_set[SUPPORTED_PERSONALITIES];
|
||||
memset(tmp_set, 0, sizeof(tmp_set));
|
||||
qualify_syscall_tokens(name, tmp_set, "fault argument");
|
||||
qualify_syscall_tokens(name, tmp_set, description);
|
||||
|
||||
free(buf);
|
||||
|
||||
@ -559,6 +569,18 @@ qualify_fault(const char *const str)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_fault(const char *const str)
|
||||
{
|
||||
qualify_inject_common(str, true, "fault argument");
|
||||
}
|
||||
|
||||
static void
|
||||
qualify_inject(const char *const str)
|
||||
{
|
||||
qualify_inject_common(str, false, "inject argument");
|
||||
}
|
||||
|
||||
static const struct qual_options {
|
||||
const char *name;
|
||||
void (*qualify)(const char *);
|
||||
@ -581,6 +603,7 @@ static const struct qual_options {
|
||||
{ "writes", qualify_write },
|
||||
{ "w", qualify_write },
|
||||
{ "fault", qualify_fault },
|
||||
{ "inject", qualify_inject },
|
||||
};
|
||||
|
||||
void
|
||||
|
70
strace.1
70
strace.1
@ -343,8 +343,9 @@ is one of
|
||||
.BR signal ,
|
||||
.BR read ,
|
||||
.BR write ,
|
||||
.BR fault ,
|
||||
or
|
||||
.B fault
|
||||
.B inject
|
||||
and
|
||||
.I value
|
||||
is a qualifier-dependent symbol or number. The default
|
||||
@ -469,31 +470,33 @@ 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\ fault\fR=\,\fIset\/\fR[:\fBerror\fR=\,\fIerrno\/\fR|:\fBretval\fR=\,\fIvalue\/\fR][:\fBsignal\fR=\,\fIsig\/\fR][:\fBwhen\fR=\,\fIexpr\/\fR]
|
||||
\fB\-e\ inject\fR=\,\fIset\/\fR[:\fBerror\fR=\,\fIerrno\/\fR|:\fBretval\fR=\,\fIvalue\/\fR][:\fBsignal\fR=\,\fIsig\/\fR][:\fBwhen\fR=\,\fIexpr\/\fR]
|
||||
Perform syscall tampering for the specified set of syscalls.
|
||||
|
||||
When no options are specified or :\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.
|
||||
|
||||
If an error code is specified using a symbolic
|
||||
.I errno
|
||||
value like
|
||||
.B ENOSYS
|
||||
or a numeric value within 1..4095 range, this error code overrides
|
||||
the default error code returned by the kernel, which is traditionally
|
||||
.B ENOSYS
|
||||
for invalid syscall numbers on most architectures.
|
||||
|
||||
If :\fBretval\fR=\,\fIvalue\/\fR option is specified,
|
||||
success injection is performed: the syscall number is replaced by -1,
|
||||
but a bogus success value is returned to the callee.
|
||||
|
||||
At least one of
|
||||
.BR error ,
|
||||
.BR retval ,
|
||||
or
|
||||
.B signal
|
||||
options has to be specified.
|
||||
.B error
|
||||
and
|
||||
.B retval
|
||||
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,
|
||||
and the error code is specified using a symbolic
|
||||
.I errno
|
||||
value like
|
||||
.B ENOSYS
|
||||
or a numeric value within 1..4095 range.
|
||||
|
||||
If :\fBretval\fR=\,\fIvalue\/\fR option is specified,
|
||||
success injection is performed: the syscall number is replaced by -1,
|
||||
but a bogus success value is returned to the callee.
|
||||
|
||||
If :\fBsignal\fR=\,\fIsig\/\fR option is specified with either a symbolic value
|
||||
like
|
||||
.B SIGSEGV
|
||||
@ -525,7 +528,7 @@ The format of the subexpression is one of the following:
|
||||
.RS 4
|
||||
For every syscall from the
|
||||
.IR set ,
|
||||
perform a syscall fault injection for the syscall invocation number
|
||||
perform an injection for the syscall invocation number
|
||||
.I first
|
||||
only.
|
||||
.RE
|
||||
@ -534,7 +537,7 @@ only.
|
||||
.RS 4
|
||||
For every syscall from the
|
||||
.IR set ,
|
||||
perform syscall fault injections for the syscall invocation number
|
||||
perform injections for the syscall invocation number
|
||||
.I first
|
||||
and all subsequent invocations.
|
||||
.RE
|
||||
@ -543,7 +546,7 @@ and all subsequent invocations.
|
||||
.RS 4
|
||||
For every syscall from the
|
||||
.IR set ,
|
||||
perform syscall fault injections for syscall invocations number
|
||||
perform injections for syscall invocations number
|
||||
.IR first ,
|
||||
.IR first + step ,
|
||||
.IR first + step + step ,
|
||||
@ -554,7 +557,7 @@ and so on.
|
||||
For example, to fail each third and subsequent chdir syscalls with
|
||||
.BR ENOENT ,
|
||||
use
|
||||
\fB\-e\ fault\fR=\,\fIchdir\/\fR:\fBerror\fR=\,\fIENOENT\/\fR:\fBwhen\fR=\,\fI3\/\fB+\fR.
|
||||
\fB\-e\ inject\fR=\,\fIchdir\/\fR:\fBerror\fR=\,\fIENOENT\/\fR:\fBwhen\fR=\,\fI3\/\fB+\fR.
|
||||
|
||||
The valid range for numbers
|
||||
.I first
|
||||
@ -562,21 +565,32 @@ and
|
||||
.I step
|
||||
is 1..65535.
|
||||
|
||||
A fault expression can contain only one
|
||||
An injection expression can contain only one
|
||||
.BR error =
|
||||
or
|
||||
.BR retval =
|
||||
specification.
|
||||
If a fault expression contains multiple
|
||||
If an injection expression contains multiple
|
||||
.BR when =
|
||||
specifications, the last one takes precedence.
|
||||
|
||||
Accounting of syscalls that are subject to fault injection
|
||||
Accounting of syscalls that are subject to injection
|
||||
is done per syscall and per tracee.
|
||||
|
||||
Specification of syscall fault injection can be combined
|
||||
Specification of syscall injection can be combined
|
||||
with other syscall filtering options, for example,
|
||||
\fB\-P \fI/dev/urandom \fB\-e fault\fR=\,\fIall\/\fR:\fBerror\fR=\,\fIENOENT\fR.
|
||||
\fB\-P \fI/dev/urandom \fB\-e inject\fR=\,\fIfile\/\fR:\fBerror\fR=\,\fIENOENT\fR.
|
||||
|
||||
.TP
|
||||
\fB\-e\ fault\fR=\,\fIset\/\fR[:\fBerror\fR=\,\fIerrno\/\fR][:\fBwhen\fR=\,\fIexpr\/\fR]
|
||||
Perform syscall fault injection for the specified set of syscalls.
|
||||
|
||||
This is equivalent to more generic
|
||||
\fB\-e\ inject\fR= expression with default value of
|
||||
.I errno
|
||||
option set to
|
||||
.IR ENOSYS .
|
||||
|
||||
.TP
|
||||
.BI "\-P " path
|
||||
Trace only system calls accessing
|
||||
|
@ -846,6 +846,7 @@ MISC_TESTS = \
|
||||
qual_fault-syntax.test \
|
||||
qual_fault.test \
|
||||
qual_inject-retval.test \
|
||||
qual_inject-syntax.test \
|
||||
qual_signal.test \
|
||||
qual_syscall.test \
|
||||
redirect-fds.test \
|
||||
|
@ -2,7 +2,7 @@
|
||||
#
|
||||
# Check -e fault= syntax.
|
||||
#
|
||||
# Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
|
||||
# Copyright (c) 2016=2017 Dmitry V. Levin <ldv@altlinux.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
@ -98,6 +98,9 @@ for arg in '' , ,, ,,, : :: ::: \! \!, \!: \
|
||||
\!chdir,nonsense \
|
||||
1,nonsense \
|
||||
\!1,nonsense \
|
||||
chdir:retval=0 \
|
||||
chdir:signal=1 \
|
||||
chdir:error=1:error=2 \
|
||||
; do
|
||||
$STRACE -e fault="$arg" true 2> "$LOG" &&
|
||||
fail_with "$arg"
|
||||
|
@ -41,12 +41,7 @@
|
||||
static const int expfd = 4;
|
||||
static const int gotfd = 5;
|
||||
|
||||
#ifndef __s390__
|
||||
# define DEFAULT_ERRNO ENOSYS
|
||||
#else /* __s390__ */
|
||||
/* Invalid syscall number causes EPERM instead of traditional ENOSYS. */
|
||||
# define DEFAULT_ERRNO EPERM
|
||||
#endif
|
||||
#define DEFAULT_ERRNO ENOSYS
|
||||
|
||||
static const char *errstr;
|
||||
static int is_raw, err, first, step, iter, try;
|
||||
|
@ -11,7 +11,7 @@ check_injection()
|
||||
syscall=chdir
|
||||
rval="$1"; shift
|
||||
|
||||
run_strace -a12 -e$syscall -efault="$syscall:retval=$rval" "$@" \
|
||||
run_strace -a12 -e$syscall -einject="$syscall:retval=$rval" "$@" \
|
||||
./qual_inject-retval "$rval" > "$EXP"
|
||||
match_diff "$LOG" "$EXP"
|
||||
rm -f "$EXP"
|
||||
|
118
tests/qual_inject-syntax.test
Executable file
118
tests/qual_inject-syntax.test
Executable file
@ -0,0 +1,118 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Check -e inject= syntax.
|
||||
#
|
||||
# Copyright (c) 2016-2017 Dmitry V. Levin <ldv@altlinux.org>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
. "${srcdir=.}/init.sh"
|
||||
|
||||
#
|
||||
# F
|
||||
# F+
|
||||
# F+S
|
||||
|
||||
fail_with()
|
||||
{
|
||||
dump_log_and_fail_with \
|
||||
"strace -e inject=$* failed to handle an argument error properly"
|
||||
}
|
||||
|
||||
for arg in '' , ,, ,,, : :: ::: \! \!, \!: \
|
||||
invalid_syscall_name \
|
||||
invalid_syscall_name:when=3 \
|
||||
-1 \!-1 \
|
||||
-1:when=4 \
|
||||
-2 \
|
||||
-2:when=5 \
|
||||
32767 \!32767 \
|
||||
32767:when=6 \
|
||||
42 \
|
||||
chdir \
|
||||
chdir:42 \!chdir:42 \
|
||||
chdir:42:when=7 \
|
||||
chdir:invalid \
|
||||
chdir:invalid:when=8 \
|
||||
chdir:error= \
|
||||
chdir:error=:when=10 \
|
||||
chdir:error=invalid_error_name \
|
||||
chdir:error=invalid_error_name:when=11 \
|
||||
chdir:error=-1 \
|
||||
chdir:error=-1:when=12 \
|
||||
chdir:error=-2 \
|
||||
chdir:error=-2:when=13 \
|
||||
chdir:error=3+ \
|
||||
chdir:error=3+:when=14 \
|
||||
chdir:error=4096 \
|
||||
chdir:error=4096:when=15 \
|
||||
chdir:when=7:error=invalid_error_name \
|
||||
chdir:when= \
|
||||
chdir:when=:error=19 \
|
||||
chdir:when=0 \
|
||||
chdir:when=0:error=20 \
|
||||
chdir:when=-1 \
|
||||
chdir:when=-1:error=21 \
|
||||
chdir:when=-2+ \
|
||||
chdir:when=-2+:error=22 \
|
||||
chdir:when=-3+0 \
|
||||
chdir:when=-3+0:error=23 \
|
||||
chdir:when=4- \
|
||||
chdir:when=4-:error=24 \
|
||||
chdir:when=5+- \
|
||||
chdir:when=5+-:error=25 \
|
||||
chdir:when=6++ \
|
||||
chdir:when=6++:error=26 \
|
||||
chdir:when=7+0 \
|
||||
chdir:when=7+0:error=27 \
|
||||
chdir:when=8+-1 \
|
||||
chdir:when=8+-1:error=28 \
|
||||
chdir:when=9+1+ \
|
||||
chdir:when=9+1+:error=29 \
|
||||
chdir:when=65536 \
|
||||
chdir:when=65536:error=30 \
|
||||
chdir:when=1+65536 \
|
||||
chdir:when=1+65536:error=31 \
|
||||
file,nonsense \
|
||||
\!desc,nonsense \
|
||||
chdir,nonsense \
|
||||
\!chdir,nonsense \
|
||||
1,nonsense \
|
||||
\!1,nonsense \
|
||||
chdir:retval=-1 \
|
||||
chdir:signal=0 \
|
||||
chdir:signal=129 \
|
||||
chdir:retval=0:retval=1 \
|
||||
chdir:error=1:error=2 \
|
||||
chdir:retval=0:error=1 \
|
||||
chdir:error=1:retval=0 \
|
||||
chdir:retval=0:signal=1:error=1 \
|
||||
; do
|
||||
$STRACE -e inject="$arg" true 2> "$LOG" &&
|
||||
fail_with "$arg"
|
||||
LC_ALL=C grep -F 'invalid inject argument' < "$LOG" > /dev/null ||
|
||||
fail_with "$arg"
|
||||
done
|
||||
|
||||
exit 0
|
Loading…
x
Reference in New Issue
Block a user