Implement injection of negative return values

Extend the range of injected return value to the maximum, print warnings
if negative injection value can be clipped in compat personality or can
inadvertently turn into a fault injection.

* defs.h (INJECT_F_ERROR): New macro.
* filter_qualify.c (parse_inject_token): Revert type of intval local
variable back to int, check INJECT_F_ERROR along with INJECT_F_RETVAL,
use strtoull to parse retval argument, print warnings in case of retval
clipping and inadvertent fault injection.
(qualify_inject_common): Set INJECT_F_ERROR instead of INJECT_F_RETVAL.
* syscall.c (tamper_with_syscall_exiting): Check inject_data.flags
to determine whether a fault injection or retval injection has to be
performed.
(syscall_exiting_trace) <case RVAL_DECIMAL>: Explicitly print
tcp->u_rval as int if current_klongsize < sizeof(tcp->u_rval).
* tests/inject-nf.c (main): Update.
* tests/inject-nf.test: Test injection of negative return values.
* tests/qual_inject-syntax.test: Remove retval=-1 check as it is now
allowed, add checks for invalid retval parameters.

Co-Authored-by: Dmitry V. Levin <ldv@altlinux.org>
This commit is contained in:
Eugene Syromyatnikov 2018-02-12 00:41:36 +01:00 committed by Dmitry V. Levin
parent 37c517b03c
commit 4c9ed3f146
6 changed files with 85 additions and 40 deletions

11
defs.h
View File

@ -171,13 +171,14 @@ typedef struct ioctlent {
unsigned int code;
} struct_ioctlent;
#define INJECT_F_SIGNAL 1
#define INJECT_F_RETVAL 2
#define INJECT_F_DELAY_ENTER 4
#define INJECT_F_DELAY_EXIT 8
#define INJECT_F_SIGNAL 0x01
#define INJECT_F_ERROR 0x02
#define INJECT_F_RETVAL 0x04
#define INJECT_F_DELAY_ENTER 0x08
#define INJECT_F_DELAY_EXIT 0x10
struct inject_data {
uint8_t flags; /* only 4 of 8 flags are used so far */
uint8_t flags; /* 5 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 */

View File

@ -105,7 +105,7 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts,
const bool fault_tokens_only)
{
const char *val;
kernel_long_t intval;
int intval;
if ((val = STR_STRIP_PREFIX(token, "when=")) != token) {
/*
@ -138,37 +138,63 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts,
fopts->step = 0;
}
} else if ((val = STR_STRIP_PREFIX(token, "error=")) != token) {
if (fopts->data.flags & INJECT_F_RETVAL)
if (fopts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL))
return false;
intval = string_to_uint_upto(val, MAX_ERRNO_VALUE);
if (intval < 0)
intval = find_errno_by_name(val);
if (intval < 1)
return false;
fopts->data.rval_idx = retval_new(-intval);
fopts->data.flags |= INJECT_F_RETVAL;
fopts->data.rval_idx = retval_new(intval);
fopts->data.flags |= INJECT_F_ERROR;
} else if (!fault_tokens_only
&& (val = STR_STRIP_PREFIX(token, "retval=")) != token) {
if (fopts->data.flags & INJECT_F_RETVAL)
return false;
intval = string_to_kulong(val);
if (intval < 0)
if (fopts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL))
return false;
#if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG && !HAVE_ARCH_DEDICATED_ERR_REG
if ((int) intval != intval)
error_msg("Injected return value %" PRI_kld " will be"
" clipped to %d in compat personality",
intval, (int) intval);
errno = 0;
char *endp;
unsigned long long ullval = strtoull(val, &endp, 0);
if (endp == val || *endp || (kernel_ulong_t) ullval != ullval
|| ((ullval == 0 || ullval == ULLONG_MAX) && errno))
return false;
if ((int) intval < 0 && (int) intval >= -MAX_ERRNO_VALUE)
error_msg("Inadvertent injection of error %d is"
" possible in compat personality for"
" retval=%" PRI_kld,
-(int) intval, intval);
#if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
bool inadvertent_fault_injection = false;
#endif
fopts->data.rval_idx = retval_new(intval);
#if !HAVE_ARCH_DEDICATED_ERR_REG
if ((kernel_long_t) ullval < 0
&& (kernel_long_t) ullval >= -MAX_ERRNO_VALUE) {
# if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
inadvertent_fault_injection = true;
# endif
error_msg("Inadvertent injection of error %" PRI_kld
" is possible for retval=%llu",
-(kernel_long_t) ullval, ullval);
}
# if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
else if ((int) ullval < 0 && (int) ullval >= -MAX_ERRNO_VALUE) {
inadvertent_fault_injection = true;
error_msg("Inadvertent injection of error %d is"
" possible in compat personality for"
" retval=%llu",
-(int) ullval, ullval);
}
# endif
#endif
#if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
if (!inadvertent_fault_injection
&& (unsigned int) ullval != ullval) {
error_msg("Injected return value %llu will be"
" clipped to %u in compat personality",
ullval, (unsigned int) ullval);
}
#endif
fopts->data.rval_idx = retval_new(ullval);
fopts->data.flags |= INJECT_F_RETVAL;
} else if (!fault_tokens_only
&& (val = STR_STRIP_PREFIX(token, "signal=")) != token) {
@ -298,8 +324,8 @@ qualify_inject_common(const char *const str,
if (!opts.data.flags) {
if (fault_tokens_only) {
/* in fault= syntax the default error code is ENOSYS. */
opts.data.rval_idx = retval_new(-ENOSYS);
opts.data.flags |= INJECT_F_RETVAL;
opts.data.rval_idx = retval_new(ENOSYS);
opts.data.flags |= INJECT_F_ERROR;
} else {
/* in inject= syntax this is not allowed. */
error_msg_and_die("invalid %s '%s'", description, str);

View File

@ -543,7 +543,7 @@ 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_RETVAL &&
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_DELAY_ENTER)
@ -578,8 +578,9 @@ tamper_with_syscall_exiting(struct tcb *tcp)
bool update_tcb = false;
const kernel_long_t inject_rval = retval_get(opts->data.rval_idx);
if (inject_rval >= 0) {
if (opts->data.flags & INJECT_F_RETVAL) {
kernel_long_t inject_rval =
retval_get(opts->data.rval_idx);
kernel_long_t u_rval = tcp->u_rval;
tcp->u_rval = inject_rval;
@ -590,7 +591,7 @@ tamper_with_syscall_exiting(struct tcb *tcp)
tcp->u_error = 0;
}
} else {
unsigned long new_error = -inject_rval;
unsigned long new_error = retval_get(opts->data.rval_idx);
if (new_error != tcp->u_error && new_error <= MAX_ERRNO_VALUE) {
unsigned long u_error = tcp->u_error;
@ -935,7 +936,14 @@ syscall_exiting_trace(struct tcb *tcp, struct timespec *ts, int res)
}
break;
case RVAL_DECIMAL:
tprintf("= %" PRI_kld, tcp->u_rval);
#if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
if (current_klongsize < sizeof(tcp->u_rval)) {
tprintf("= %d", (int) tcp->u_rval);
} else
#endif
{
tprintf("= %" PRI_kld, tcp->u_rval);
}
break;
case RVAL_FD:
if (show_fd_path) {

View File

@ -77,7 +77,7 @@ main(int ac, char **av)
if (ac == 2) {
printf("%s() = %lld (INJECTED)\n",
SC_NAME, (unsigned long long) rc);
SC_NAME, sign_extend_unsigned_to_ll(rc));
puts("+++ exited with 0 +++");
}

View File

@ -28,15 +28,23 @@ test_rval()
test_rval 0
test_rval 1
test_rval 2147483647 # 0x7fffffff
test_rval 0x7fffffff
test_rval 0x80000000
test_rval 0xfffff000
test_rval 0xfffffffe
test_rval 0xffffffff
case "$SIZEOF_KERNEL_LONG_T" in
8)
test_rval 2147483648 # 0x80000000
test_rval 4294963200 # 0xfffff000
test_rval 4294967294 # 0xfffffffe
test_rval 4294967295 # 0xffffffff
test_rval 4294967296 # 0x100000000
test_rval 9223372036854775807 # 0x7fffffffffffffff
test_rval 0x80000000
test_rval 0xfffff000
test_rval 0xfffffffe
test_rval 0xffffffff
test_rval 0x100000000
test_rval 0x7fffffffffffffff
test_rval 0x8000000000000000
test_rval 0xfffffffffffff000
test_rval 0xfffffffffffffffe
test_rval 0xffffffffffffffff
;;
esac

View File

@ -84,7 +84,9 @@ for arg in 42 chdir \
chdir:when=65536:error=30 \
chdir:when=1+65536 \
chdir:when=1+65536:error=31 \
chdir:retval=-1 \
chdir:retval=a \
chdir:retval=0b \
chdir:retval=0x10000000000000000 \
chdir:signal=0 \
chdir:signal=129 \
chdir:signal=1:signal=2 \