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:
parent
37c517b03c
commit
4c9ed3f146
11
defs.h
11
defs.h
@ -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 */
|
||||
|
@ -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);
|
||||
|
18
syscall.c
18
syscall.c
@ -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) {
|
||||
|
@ -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 +++");
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
Loading…
x
Reference in New Issue
Block a user