From 45ed340016ede5239a12a03eddf82de57de2c740 Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Sun, 4 Dec 2016 14:39:48 +0000 Subject: [PATCH] Rewrite remaining qual_* parsers using bit sets * defs.h (struct fault_opts): Replace forward declaration with a definition. (qualbits_t, qualify_read, qualify_write, qualify_signals): Remove. (qual_flags): New function prototype. (nsyscall_vec, sysent_vec, fault_vec): New variable prototypes. * qualify.c (abbrev_set, fault_set, raw_set, trace_set, verbose_set): New variables. (qualify_read, qualify_write, qualify_signals): Add static qualifier. (find_errno_by_name, lookup_class, parse_fault_expression, parse_fault_token, qual_flags, qualify, qualify_abbrev, qualify_fault, qualify_raw, qualify_syscall, qualify_syscall_class, qualify_syscall_name, qualify_syscall_number, qualify_syscall_tokens, qualify_trace, qualify_verbose, strip_prefix): New functions. * syscall.c (nsyscall_vec, nsysent_vec): Remove static qualifier. (MAX_NSYSCALLS1, MAX_NSYSCALLS2, MAX_NSYSCALLS, qual_vec, qual_flags, qual_fault, qual_syscall, qual_options, fault_opts, qualify_one, qualify_scno, lookup_class, qualify_syscall_class, qualify_syscall_name, qual_syscall_ex, qual_syscall, strip_prefix, find_errno_by_name, parse_fault_token, parse_fault_expression, qual_fault, qualify): Remove. (decode_socket_subcall, decode_ipc_subcall, decode_mips_subcall, get_scno): Update use of qual_flags. (inject_syscall_fault_entering): Update per-personality allocation of tcp->fault_vec. * tests/fault_injection-exit_group.test: Check parsing of inversed fault sets. * tests/fault_injection.test: Check parsing of -efault=none. * tests/options-syntax.test: Check parsing of invalid syscall numbers. --- defs.h | 19 +- qualify.c | 434 +++++++++++++++++++++++++- syscall.c | 410 +----------------------- tests/fault_injection-exit_group.test | 27 +- tests/fault_injection.test | 2 + tests/options-syntax.test | 10 + 6 files changed, 476 insertions(+), 426 deletions(-) diff --git a/defs.h b/defs.h index f98c8f95..cdeb22fd 100644 --- a/defs.h +++ b/defs.h @@ -210,14 +210,18 @@ typedef struct ioctlent { unsigned int code; } struct_ioctlent; +struct fault_opts { + uint16_t first; + uint16_t step; + uint16_t err; +}; + #if defined LINUX_MIPSN32 || defined X32 # define HAVE_STRUCT_TCB_EXT_ARG 1 #else # define HAVE_STRUCT_TCB_EXT_ARG 0 #endif -struct fault_opts; - /* Trace Control Block */ struct tcb { int flags; /* See below for TCB_ values */ @@ -292,7 +296,6 @@ struct tcb { #define QUAL_SIGNAL 0x100 /* report events with this signal */ #define QUAL_READ 0x200 /* dump data read from this file descriptor */ #define QUAL_WRITE 0x400 /* dump data written to this file descriptor */ -typedef uint8_t qualbits_t; #define DEFAULT_QUAL_FLAGS (QUAL_TRACE | QUAL_ABBREV | QUAL_VERBOSE) @@ -448,7 +451,6 @@ extern int read_int_from_file(const char *, int *); extern void set_sortby(const char *); extern void set_overhead(int); -extern void qualify(const char *); extern void print_pc(struct tcb *); extern int trace_syscall(struct tcb *); extern void count_syscall(struct tcb *, const struct timeval *); @@ -670,9 +672,8 @@ extern struct number_set write_set; extern struct number_set signal_set; extern bool is_number_in_set(unsigned int number, const struct number_set *); -extern void qualify_read(const char *); -extern void qualify_write(const char *); -extern void qualify_signals(const char *); +extern void qualify(const char *); +extern unsigned int qual_flags(const unsigned int); extern int dm_ioctl(struct tcb *, const unsigned int, long); extern int file_ioctl(struct tcb *, const unsigned int, long); @@ -859,6 +860,10 @@ extern unsigned nerrnos; extern unsigned nsignals; extern unsigned nioctlents; +extern const unsigned int nsyscall_vec[SUPPORTED_PERSONALITIES]; +extern const struct_sysent *const sysent_vec[SUPPORTED_PERSONALITIES]; +extern struct fault_opts *fault_vec[SUPPORTED_PERSONALITIES]; + #ifdef IN_MPERS_BOOTSTRAP /* Transform multi-line MPERS_PRINTER_DECL statements to one-liners. */ # define MPERS_PRINTER_DECL(type, name, ...) MPERS_PRINTER_DECL(type, name, __VA_ARGS__) diff --git a/qualify.c b/qualify.c index 30e7ac99..a7d276cd 100644 --- a/qualify.c +++ b/qualify.c @@ -40,6 +40,12 @@ struct number_set read_set; struct number_set write_set; struct number_set signal_set; +static struct number_set abbrev_set[SUPPORTED_PERSONALITIES]; +static struct number_set fault_set[SUPPORTED_PERSONALITIES]; +static struct number_set raw_set[SUPPORTED_PERSONALITIES]; +static struct number_set trace_set[SUPPORTED_PERSONALITIES]; +static struct number_set verbose_set[SUPPORTED_PERSONALITIES]; + static void number_setbit(const unsigned int i, number_slot_t *const vec) { @@ -142,18 +148,6 @@ handle_inversion: } } -void -qualify_read(const char *const str) -{ - qualify_tokens(str, &read_set, string_to_uint, "descriptor"); -} - -void -qualify_write(const char *const str) -{ - qualify_tokens(str, &write_set, string_to_uint, "descriptor"); -} - static int sigstr_to_uint(const char *s) { @@ -182,8 +176,422 @@ sigstr_to_uint(const char *s) return -1; } -void +static bool +qualify_syscall_number(const char *s, struct number_set *set) +{ + int n = string_to_uint(s); + if (n < 0) + return false; + + unsigned int p; + bool done = false; + + for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) { + if ((unsigned) n >= nsyscall_vec[p]) { + continue; + } + add_number_to_set(n, &set[p]); + done = true; + } + + return done; +} + +static unsigned int +lookup_class(const char *s) +{ + static const struct { + const char *name; + unsigned int value; + } syscall_class[] = { + { "desc", TRACE_DESC }, + { "file", TRACE_FILE }, + { "memory", TRACE_MEMORY }, + { "process", TRACE_PROCESS }, + { "signal", TRACE_SIGNAL }, + { "ipc", TRACE_IPC }, + { "network", TRACE_NETWORK }, + }; + + unsigned int i; + for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) { + if (strcmp(s, syscall_class[i].name) == 0) { + return syscall_class[i].value; + } + } + + return 0; +} + +static bool +qualify_syscall_class(const char *s, struct number_set *set) +{ + const unsigned int n = lookup_class(s); + if (!n) + return false; + + unsigned int p; + for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) { + unsigned int i; + + for (i = 0; i < nsyscall_vec[p]; ++i) { + if (!sysent_vec[p][i].sys_name + || (sysent_vec[p][i].sys_flags & n) != n) { + continue; + } + add_number_to_set(i, &set[p]); + } + } + + return true; +} + +static bool +qualify_syscall_name(const char *s, struct number_set *set) +{ + unsigned int p; + bool found = false; + + for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) { + unsigned int i; + + for (i = 0; i < nsyscall_vec[p]; ++i) { + if (!sysent_vec[p][i].sys_name + || strcmp(s, sysent_vec[p][i].sys_name)) { + continue; + } + add_number_to_set(i, &set[p]); + found = true; + } + } + + return found; +} + +static bool +qualify_syscall(const char *token, struct number_set *set) +{ + if (*token >= '0' && *token <= '9') + return qualify_syscall_number(token, set); + return qualify_syscall_class(token, set) + || qualify_syscall_name(token, set); +} + +/* + * Add syscall numbers to SETs for each supported personality + * according to STR specification. + */ +static void +qualify_syscall_tokens(const char *const str, struct number_set *const set, + const char *const name) +{ + /* Clear all sets. */ + unsigned int p; + for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) { + if (set[p].nslots) + memset(set[p].vec, 0, + sizeof(*set[p].vec) * set[p].nslots); + set[p].not = false; + } + + /* + * Each leading ! character means inversion + * of the remaining specification. + */ + const char *s = str; +handle_inversion: + while (*s == '!') { + for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) { + set[p].not = !set[p].not; + } + ++s; + } + + if (strcmp(s, "none") == 0) { + /* + * No syscall numbers are added to sets. + * Subsequent is_number_in_set invocations + * will return set[p]->not. + */ + return; + } else if (strcmp(s, "all") == 0) { + s = "!none"; + goto handle_inversion; + } + + /* + * Split the string into comma separated tokens. + * For each token, call qualify_syscall that will take care + * if adding appropriate syscall numbers to sets. + * The absence of tokens or a negative return code + * from qualify_syscall is a fatal error. + */ + char *copy = xstrdup(s); + char *saveptr = NULL; + const char *token; + bool done = false; + + for (token = strtok_r(copy, ",", &saveptr); token; + token = strtok_r(NULL, ",", &saveptr)) { + done = qualify_syscall(token, set); + if (!done) { + error_msg_and_die("invalid %s '%s'", name, token); + } + } + + free(copy); + + if (!done) { + error_msg_and_die("invalid %s '%s'", name, str); + } +} + +/* + * Returns NULL if STR does not start with PREFIX, + * or a pointer to the first char in STR after PREFIX. + */ +static const char * +strip_prefix(const char *prefix, const char *str) +{ + size_t len = strlen(prefix); + + return strncmp(prefix, str, len) ? NULL : str + len; +} + +static int +find_errno_by_name(const char *name) +{ + unsigned int i; + + for (i = 1; i < nerrnos; ++i) { + if (errnoent[i] && (strcmp(name, errnoent[i]) == 0)) + return i; + } + + return -1; +} + +static bool +parse_fault_token(const char *const token, struct fault_opts *const fopts) +{ + const char *val; + int intval; + + if ((val = strip_prefix("when=", token))) { + /* + * == 1+1 + * F == F+0 + * F+ == F+1 + * F+S + */ + char *end; + intval = string_to_uint_ex(val, &end, 0xffff, "+"); + if (intval < 1) + return false; + + fopts->first = intval; + + if (*end) { + val = end + 1; + if (*val) { + /* F+S */ + intval = string_to_uint_upto(val, 0xffff); + if (intval < 1) + return false; + fopts->step = intval; + } else { + /* F+ == F+1 */ + fopts->step = 1; + } + } else { + /* F == F+0 */ + fopts->step = 0; + } + } else if ((val = strip_prefix("error=", token))) { + intval = string_to_uint_upto(val, 4095); + if (intval < 0) + intval = find_errno_by_name(val); + if (intval < 1) + return false; + fopts->err = intval; + } else { + return false; + } + + return true; +} + +static char * +parse_fault_expression(const char *const s, char **buf, + struct fault_opts *const fopts) +{ + char *saveptr = NULL; + char *name = NULL; + char *token; + + *buf = xstrdup(s); + for (token = strtok_r(*buf, ":", &saveptr); token; + token = strtok_r(NULL, ":", &saveptr)) { + if (!name) + name = token; + else if (!parse_fault_token(token, fopts)) + goto parse_error; + } + + if (name) + return name; + +parse_error: + free(*buf); + return *buf = NULL; +} + +static void +qualify_read(const char *const str) +{ + qualify_tokens(str, &read_set, string_to_uint, "descriptor"); +} + +static void +qualify_write(const char *const str) +{ + qualify_tokens(str, &write_set, string_to_uint, "descriptor"); +} + +static void qualify_signals(const char *const str) { qualify_tokens(str, &signal_set, sigstr_to_uint, "signal"); } + +static void +qualify_trace(const char *const str) +{ + qualify_syscall_tokens(str, trace_set, "system call"); +} + +static void +qualify_abbrev(const char *const str) +{ + qualify_syscall_tokens(str, abbrev_set, "system call"); +} + +static void +qualify_verbose(const char *const str) +{ + qualify_syscall_tokens(str, verbose_set, "system call"); +} + +static void +qualify_raw(const char *const str) +{ + qualify_syscall_tokens(str, raw_set, "system call"); +} + +static void +qualify_fault(const char *const str) +{ + struct fault_opts opts = { + .first = 1, + .step = 1, + .err = 0 + }; + char *buf = NULL; + char *name = parse_fault_expression(str, &buf, &opts); + if (!name) { + error_msg_and_die("invalid %s '%s'", "fault argument", str); + } + + + struct number_set tmp_set[SUPPORTED_PERSONALITIES]; + memset(tmp_set, 0, sizeof(tmp_set)); + qualify_syscall_tokens(name, tmp_set, "fault argument"); + + free(buf); + + /* + * Initialize fault_vec accourding to tmp_set. + * Merge tmp_set into fault_set. + */ + unsigned int p; + for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) { + if (!tmp_set[p].nslots && !tmp_set[p].not) { + continue; + } + + if (!fault_vec[p]) { + fault_vec[p] = xcalloc(nsyscall_vec[p], + sizeof(*fault_vec[p])); + } + + unsigned int i; + for (i = 0; i < nsyscall_vec[p]; ++i) { + if (is_number_in_set(i, &tmp_set[p])) { + add_number_to_set(i, &fault_set[p]); + fault_vec[p][i] = opts; + } + } + + free(tmp_set[p].vec); + } +} + +static const struct qual_options { + const char *name; + void (*qualify)(const char *); +} qual_options[] = { + { "trace", qualify_trace }, + { "t", qualify_trace }, + { "abbrev", qualify_abbrev }, + { "a", qualify_abbrev }, + { "verbose", qualify_verbose }, + { "v", qualify_verbose }, + { "raw", qualify_raw }, + { "x", qualify_raw }, + { "signal", qualify_signals }, + { "signals", qualify_signals }, + { "s", qualify_signals }, + { "read", qualify_read }, + { "reads", qualify_read }, + { "r", qualify_read }, + { "write", qualify_write }, + { "writes", qualify_write }, + { "w", qualify_write }, + { "fault", qualify_fault }, +}; + +void +qualify(const char *str) +{ + const struct qual_options *opt = qual_options; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(qual_options); ++i) { + const char *p = qual_options[i].name; + unsigned int len = strlen(p); + + if (strncmp(str, p, len) || str[len] != '=') + continue; + + opt = &qual_options[i]; + str += len + 1; + break; + } + + opt->qualify(str); +} + +unsigned int +qual_flags(const unsigned int scno) +{ + return (is_number_in_set(scno, &trace_set[current_personality]) + ? QUAL_TRACE : 0) + | (is_number_in_set(scno, &abbrev_set[current_personality]) + ? QUAL_ABBREV : 0) + | (is_number_in_set(scno, &verbose_set[current_personality]) + ? QUAL_VERBOSE : 0) + | (is_number_in_set(scno, &raw_set[current_personality]) + ? QUAL_RAW : 0) + | (is_number_in_set(scno, &fault_set[current_personality]) + ? QUAL_FAULT : 0); +} diff --git a/syscall.c b/syscall.c index 1fd5dde5..685ea929 100644 --- a/syscall.c +++ b/syscall.c @@ -224,7 +224,7 @@ unsigned nerrnos = nerrnos0; unsigned nsignals = nsignals0; unsigned nioctlents = nioctlents0; -static const unsigned nsyscall_vec[SUPPORTED_PERSONALITIES] = { +const unsigned int nsyscall_vec[SUPPORTED_PERSONALITIES] = { nsyscalls0, #if SUPPORTED_PERSONALITIES > 1 nsyscalls1, @@ -233,7 +233,7 @@ static const unsigned nsyscall_vec[SUPPORTED_PERSONALITIES] = { nsyscalls2, #endif }; -static const struct_sysent *const sysent_vec[SUPPORTED_PERSONALITIES] = { +const struct_sysent *const sysent_vec[SUPPORTED_PERSONALITIES] = { sysent0, #if SUPPORTED_PERSONALITIES > 1 sysent1, @@ -243,23 +243,6 @@ static const struct_sysent *const sysent_vec[SUPPORTED_PERSONALITIES] = { #endif }; -enum { - MAX_NSYSCALLS1 = (nsyscalls0 -#if SUPPORTED_PERSONALITIES > 1 - > nsyscalls1 ? nsyscalls0 : nsyscalls1 -#endif - ), - MAX_NSYSCALLS2 = (MAX_NSYSCALLS1 -#if SUPPORTED_PERSONALITIES > 2 - > nsyscalls2 ? MAX_NSYSCALLS1 : nsyscalls2 -#endif - ), - MAX_NSYSCALLS = MAX_NSYSCALLS2 -}; - -static qualbits_t qual_vec[SUPPORTED_PERSONALITIES][MAX_NSYSCALLS]; -#define qual_flags (qual_vec[current_personality]) - #if SUPPORTED_PERSONALITIES > 1 unsigned current_personality; @@ -353,379 +336,6 @@ update_personality(struct tcb *tcp, unsigned int personality) } #endif -static int qual_fault(const char *, unsigned int, int); -static int qual_syscall(const char *, unsigned int, int); - -static const struct qual_options { - unsigned int bitflag; - const char *option_name; - int (*qualify)(const char *, unsigned int, int); - const char *argument_name; -} qual_options[] = { - { QUAL_TRACE, "trace", qual_syscall, "system call" }, - { QUAL_TRACE, "t", qual_syscall, "system call" }, - { QUAL_ABBREV, "abbrev", qual_syscall, "system call" }, - { QUAL_ABBREV, "a", qual_syscall, "system call" }, - { QUAL_VERBOSE, "verbose", qual_syscall, "system call" }, - { QUAL_VERBOSE, "v", qual_syscall, "system call" }, - { QUAL_RAW, "raw", qual_syscall, "system call" }, - { QUAL_RAW, "x", qual_syscall, "system call" }, - { QUAL_SIGNAL, "signal", NULL, "signal" }, - { QUAL_SIGNAL, "signals", NULL, "signal" }, - { QUAL_SIGNAL, "s", NULL, "signal" }, - { QUAL_READ, "read", NULL, "descriptor" }, - { QUAL_READ, "reads", NULL, "descriptor" }, - { QUAL_READ, "r", NULL, "descriptor" }, - { QUAL_WRITE, "write", NULL, "descriptor" }, - { QUAL_WRITE, "writes", NULL, "descriptor" }, - { QUAL_WRITE, "w", NULL, "descriptor" }, - { QUAL_FAULT, "fault", qual_fault, "fault argument"}, - { 0, NULL, NULL, NULL }, -}; - -struct fault_opts { - uint16_t first; - uint16_t step; - uint16_t err; -}; - -static struct fault_opts fault_vec[SUPPORTED_PERSONALITIES][MAX_NSYSCALLS]; - -static void -qualify_one(const unsigned int n, unsigned int bitflag, const int not, - const int pers, const struct fault_opts *fopts) -{ - int p; - - for (p = 0; p < SUPPORTED_PERSONALITIES; p++) { - if (pers == p || pers < 0) { - if (not) - qual_vec[p][n] &= ~bitflag; - else { - qual_vec[p][n] |= bitflag; - if (fopts) - memcpy(&fault_vec[p][n], fopts, - sizeof(*fopts)); - } - } - } -} - -static bool -qualify_scno(const char *const s, const unsigned int bitflag, - const int not, const struct fault_opts *const fopts) -{ - int n = string_to_uint_upto(s, MAX_NSYSCALLS - 1); - if (n < 0) - return false; - - if (not && fopts) { - /* set bitflag for all syscall numbers except n */ - unsigned int p; - for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) { - unsigned int i; - - for (i = 0; i < nsyscall_vec[p]; ++i) { - if (i != (unsigned int) n - && sysent_vec[p][i].sys_name) { - qualify_one(i, bitflag, 0, p, fopts); - } - } - } - } else { - qualify_one(n, bitflag, not, -1, fopts); - } - - return true; -} - -static int -lookup_class(const char *s) -{ - if (strcmp(s, "all") == 0) - return 0; - if (strcmp(s, "file") == 0) - return TRACE_FILE; - if (strcmp(s, "ipc") == 0) - return TRACE_IPC; - if (strcmp(s, "network") == 0) - return TRACE_NETWORK; - if (strcmp(s, "process") == 0) - return TRACE_PROCESS; - if (strcmp(s, "signal") == 0) - return TRACE_SIGNAL; - if (strcmp(s, "desc") == 0) - return TRACE_DESC; - if (strcmp(s, "memory") == 0) - return TRACE_MEMORY; - return -1; -} - -static bool -qualify_syscall_class(const char *const s, const unsigned int bitflag, - const int not, const struct fault_opts *const fopts) -{ - unsigned int p; - const int n = lookup_class(s); - - if (n < 0) - return false; - - for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) { - unsigned int i; - - for (i = 0; i < nsyscall_vec[p]; ++i) { - if (!sysent_vec[p][i].sys_name) - continue; - const bool match = (sysent_vec[p][i].sys_flags & n) == n; - if (match ^ (not && fopts)) { - qualify_one(i, bitflag, not && !fopts, p, fopts); - } - } - } - - return true; -} - -static bool -qualify_syscall_name(const char *const s, const unsigned int bitflag, - const int not, const struct fault_opts *const fopts) -{ - bool found = false; - unsigned int p; - - for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) { - unsigned int i; - - for (i = 0; i < nsyscall_vec[p]; ++i) { - if (!sysent_vec[p][i].sys_name) - continue; - const bool match = !strcmp(s, sysent_vec[p][i].sys_name); - found = found || match; - if (match ^ (not && fopts)) { - qualify_one(i, bitflag, not && !fopts, p, fopts); - } - } - } - - return found; -} - -static int -qual_syscall_ex(const char *const s, const unsigned int bitflag, - const int not, const struct fault_opts *const fopts) -{ - if (qualify_scno(s, bitflag, not, fopts) - || qualify_syscall_class(s, bitflag, not, fopts) - || qualify_syscall_name(s, bitflag, not, fopts)) { - return 0; - } - - return -1; -} - -static int -qual_syscall(const char *const s, const unsigned int bitflag, const int not) -{ - return qual_syscall_ex(s, bitflag, not, NULL); -} - -/* - * Returns NULL if STR does not start with PREFIX, - * or a pointer to the first char in STR after PREFIX. - */ -static const char * -strip_prefix(const char *prefix, const char *str) -{ - size_t len = strlen(prefix); - - return strncmp(prefix, str, len) ? NULL : str + len; -} - -static int -find_errno_by_name(const char *name) -{ - unsigned int i; - - for (i = 1; i < nerrnos; ++i) { - if (errnoent[i] && (strcmp(name, errnoent[i]) == 0)) - return i; - } - - return -1; -} - -static bool -parse_fault_token(const char *const token, struct fault_opts *const fopts) -{ - const char *val; - int intval; - - if ((val = strip_prefix("when=", token))) { - /* - * == 1+1 - * F == F+0 - * F+ == F+1 - * F+S - */ - char *end; - intval = string_to_uint_ex(val, &end, 0xffff, "+"); - if (intval < 1) - return false; - - fopts->first = intval; - - if (*end) { - val = end + 1; - if (*val) { - /* F+S */ - intval = string_to_uint_upto(val, 0xffff); - if (intval < 1) - return false; - fopts->step = intval; - } else { - /* F+ == F+1 */ - fopts->step = 1; - } - } else { - /* F == F+0 */ - fopts->step = 0; - } - } else if ((val = strip_prefix("error=", token))) { - intval = string_to_uint_upto(val, 4095); - if (intval < 0) - intval = find_errno_by_name(val); - if (intval < 1) - return false; - fopts->err = intval; - } else { - return false; - } - - return true; -} - -static char * -parse_fault_expression(const char *const s, char **buf, - struct fault_opts *const fopts) -{ - char *saveptr = NULL; - char *name = NULL; - char *token; - - *buf = xstrdup(s); - for (token = strtok_r(*buf, ":", &saveptr); token; - token = strtok_r(NULL, ":", &saveptr)) { - if (!name) - name = token; - else if (!parse_fault_token(token, fopts)) - goto parse_error; - } - - if (name) - return name; - -parse_error: - free(*buf); - return *buf = NULL; -} - -static int -qual_fault(const char *const s, const unsigned int bitflag, const int not) -{ - struct fault_opts opts = { - .first = 1, - .step = 1, - .err = 0 - }; - - char *buf = NULL; - char *name = parse_fault_expression(s, &buf, &opts); - char *saveptr = NULL; - const char *token; - int rc = -1; - - if (!name) - return -1; - - for (token = strtok_r(name, ",", &saveptr); token; - token = strtok_r(NULL, ",", &saveptr)) { - rc = qual_syscall_ex(token, bitflag, not, &opts); - if (rc) - break; - } - - free(buf); - return rc; -} - -void -qualify(const char *s) -{ - const struct qual_options *opt; - char *copy; - const char *p; - int not; - int i; - - opt = &qual_options[0]; - for (i = 0; (p = qual_options[i].option_name); i++) { - unsigned int len = strlen(p); - if (strncmp(s, p, len) == 0 && s[len] == '=') { - opt = &qual_options[i]; - s += len + 1; - break; - } - } - - switch (opt->bitflag) { - case QUAL_SIGNAL: - qualify_signals(s); - return; - case QUAL_READ: - qualify_read(s); - return; - case QUAL_WRITE: - qualify_write(s); - return; - } - - not = 0; - if (*s == '!') { - not = 1; - s++; - } - if (strcmp(s, "none") == 0) { - not = 1 - not; - s = "all"; - } - if (opt->bitflag == QUAL_FAULT) { - if (opt->qualify(s, opt->bitflag, not)) { - error_msg_and_die("invalid %s '%s'", - opt->argument_name, s); - } - return; - } - if (strcmp(s, "all") == 0) { - for (i = 0; i < MAX_NSYSCALLS; ++i) { - qualify_one(i, opt->bitflag, not, -1, NULL); - } - return; - } - for (i = 0; i < MAX_NSYSCALLS; ++i) { - qualify_one(i, opt->bitflag, !not, -1, NULL); - } - copy = xstrdup(s); - for (p = strtok(copy, ","); p; p = strtok(NULL, ",")) { - if (opt->qualify(p, opt->bitflag, not)) { - error_msg_and_die("invalid %s '%s'", - opt->argument_name, p); - } - } - free(copy); - return; -} - #ifdef SYS_socket_subcall static void decode_socket_subcall(struct tcb *tcp) @@ -743,7 +353,7 @@ decode_socket_subcall(struct tcb *tcp) return; tcp->scno = scno; - tcp->qual_flg = qual_flags[scno]; + tcp->qual_flg = qual_flags(scno); tcp->s_ent = &sysent[scno]; unsigned int i; @@ -783,7 +393,7 @@ decode_ipc_subcall(struct tcb *tcp) } tcp->scno = SYS_ipc_subcall + call; - tcp->qual_flg = qual_flags[tcp->scno]; + tcp->qual_flg = qual_flags(tcp->scno); tcp->s_ent = &sysent[tcp->scno]; const unsigned int n = tcp->s_ent->nargs; @@ -800,7 +410,7 @@ decode_mips_subcall(struct tcb *tcp) if (!SCNO_IS_VALID(tcp->u_arg[0])) return; tcp->scno = tcp->u_arg[0]; - tcp->qual_flg = qual_flags[tcp->scno]; + tcp->qual_flg = qual_flags(tcp->scno); tcp->s_ent = &sysent[tcp->scno]; memmove(&tcp->u_arg[0], &tcp->u_arg[1], sizeof(tcp->u_arg) - sizeof(tcp->u_arg[0])); @@ -939,6 +549,8 @@ static int arch_set_scno(struct tcb *, long); static void get_error(struct tcb *, const bool); static int arch_set_error(struct tcb *); +struct fault_opts *fault_vec[SUPPORTED_PERSONALITIES]; + static struct fault_opts * tcb_fault_opts(struct tcb *tcp) { @@ -952,15 +564,15 @@ inject_syscall_fault_entering(struct tcb *tcp) { if (!tcp->fault_vec[current_personality]) { tcp->fault_vec[current_personality] = - xcalloc(MAX_NSYSCALLS, sizeof(struct fault_opts)); + xcalloc(nsyscalls, sizeof(**fault_vec)); memcpy(tcp->fault_vec[current_personality], fault_vec[current_personality], - MAX_NSYSCALLS * sizeof(struct fault_opts)); + nsyscalls * sizeof(**fault_vec)); } struct fault_opts *opts = tcb_fault_opts(tcp); - if (opts->first == 0) + if (!opts || opts->first == 0) return 0; --opts->first; @@ -1606,7 +1218,7 @@ get_scno(struct tcb *tcp) if (SCNO_IS_VALID(tcp->scno)) { tcp->s_ent = &sysent[tcp->scno]; - tcp->qual_flg = qual_flags[tcp->scno]; + tcp->qual_flg = qual_flags(tcp->scno); } else { struct sysent_buf *s = xcalloc(1, sizeof(*s)); diff --git a/tests/fault_injection-exit_group.test b/tests/fault_injection-exit_group.test index b2a22b42..fa224d51 100755 --- a/tests/fault_injection-exit_group.test +++ b/tests/fault_injection-exit_group.test @@ -29,12 +29,25 @@ . "${srcdir=.}/init.sh" -> "$LOG" || fail_ "failed to write $LOG" -set -- -eexit,exit_group -efault=exit_group:error=ENOSYS ./answer +test_with() +{ + > "$LOG" || fail_ "failed to write $LOG" -$STRACE -o "$LOG" "$@" -rc=$? -[ $rc -eq 42 ] || - dump_log_and_fail_with "$STRACE $* failed with code $rc" + $STRACE -o "$LOG" "$@" + rc=$? + [ $rc -eq 42 ] || + dump_log_and_fail_with "$STRACE $* failed with code $rc" -match_diff + match_diff +} + +test_with -eexit,exit_group -efault=exit_group:error=ENOSYS ./answer + +test_with -eexit,exit_group -efault=exit_group:error=ENOSYS \ + -efault=\!process:error=1 ./answer + +test_with -eexit,exit_group -efault=all:error=ENOSYS \ + -efault=exit:error=1:when=2+ ./answer + +test_with -eexit,exit_group -efault=exit_group:error=ENOSYS \ + -efault=\!desc,file,memory,process,signal,network,ipc:error=1 ./answer diff --git a/tests/fault_injection.test b/tests/fault_injection.test index 40d44aac..69bdd3c6 100755 --- a/tests/fault_injection.test +++ b/tests/fault_injection.test @@ -88,6 +88,8 @@ for err in '' ENOSYS 22 EINVAL; do for fault in writev desc,51; do check_fault_injection \ writev $fault "$err" '' '' -efault=chdir + check_fault_injection \ + writev $fault "$err" '' '' -efault=chdir -efault=none for F in 1 2 3 5 7 11; do check_fault_injection \ writev $fault "$err" $F '' diff --git a/tests/options-syntax.test b/tests/options-syntax.test index ee8ed3df..592b0e03 100755 --- a/tests/options-syntax.test +++ b/tests/options-syntax.test @@ -67,6 +67,16 @@ check_e "Invalid process id: 'a'" -p 1,a check_e "Syscall 'chdir' for -b isn't supported" -b chdir check_e "Syscall 'chdir' for -b isn't supported" -b execve -b chdir +check_e "invalid system call '-1'" -e-1 +check_e "invalid system call '-2'" -e -2 +check_e "invalid system call '-3'" -etrace=-3 +check_e "invalid system call '-4'" -e trace=-4 +check_e "invalid system call '-5'" -e trace=1,-5 +check_e "invalid system call '2147483647'" -e 2147483647 +check_e "invalid system call '2147483648'" -e 2147483648 +check_e "invalid system call '4294967295'" -e 4294967295 +check_e "invalid system call '4294967296'" -e 4294967296 + check_e "invalid descriptor '-1'" -eread=-1 check_e "invalid descriptor '-42'" -ewrite=-42 check_e "invalid descriptor '2147483648'" -eread=2147483648 -- 2.40.0