]> granicus.if.org Git - strace/commitdiff
Rewrite remaining qual_* parsers using bit sets
authorDmitry V. Levin <ldv@altlinux.org>
Sun, 4 Dec 2016 14:39:48 +0000 (14:39 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Mon, 5 Dec 2016 20:49:59 +0000 (20:49 +0000)
* 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
qualify.c
syscall.c
tests/fault_injection-exit_group.test
tests/fault_injection.test
tests/options-syntax.test

diff --git a/defs.h b/defs.h
index f98c8f95dcdd7a7d8d30e70b3b456de8d346c8a4..cdeb22fd333867b383c7a1dccc87b09a2f946199 100644 (file)
--- 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__)
index 30e7ac997f4496c248ea140a2b01bdd7cfb00a77..a7d276cdb42cb9b6b5f491e152af31c156fb79a0 100644 (file)
--- 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);
+}
index 1fd5dde5b00586c586fdd5f3064548875ee32c06..685ea9297e7c99ff17dc8faa3d782d0e0c5dac64 100644 (file)
--- 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));
 
index b2a22b42b021a4c2992837f8a89c7a5e50fa3efe..fa224d51754f5185e3c992705a7c725948d1a741 100755 (executable)
 
 . "${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
index 40d44aac0f8f36e33ca5b8ac1aba57e4b7712a85..69bdd3c6f881a4e834248bb368a03d183320266b 100755 (executable)
@@ -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 ''
index ee8ed3df0e18d306a9e49da07da6dcbc33d4ab79..592b0e03413cec1fa5e88dd1724598828029c0c7 100755 (executable)
@@ -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