diff options
author | Nikolay Marchuk <marchuk.nikolay.a@gmail.com> | 2017-08-27 12:09:04 +0700 |
---|---|---|
committer | Dmitry V. Levin <ldv@altlinux.org> | 2018-06-13 15:05:09 +0000 |
commit | f1bba432e1b6b2b9c165212a9cfd52c2d826ca0e (patch) | |
tree | 8ac21de87c1e2efdff976cf731aa0a1ae82dad3f | |
parent | f939d6d2b1fdf53882df012497ac01f24d9299e1 (diff) | |
download | strace-f1bba432e1b6b2b9c165212a9cfd52c2d826ca0e.tar.gz |
Implement inject and fault actions
* basic_actions.c (not_injected, apply_inject, parse_inject_common,
parse_inject, apply_fault, parse_fault): New functions.
* defs.h (struct inject_opts): Add init flag.
(qual_flags): Remove declaration.
* filter_action.c (action_types): Add inject and fault action types.
(set_filter_action_priv_data): New function.
* filter_qualify.c (inject_set): Remove variable.
(parse_inject_expression): Remove function.
(parse_inject_common_args): Add function for inject/fault arguments parsing.
(qualify_inject_common): Use parse_inject_common_args instead of
parse_inject_expression, use new filtering API.
(qualify_fault, qualify_inject): Remove "argument" from description
argument of qualify_inject_common.
(qual_flags): Remove function.
* filter.h (parse_inject_common_args, not_injected,
set_filter_action_priv_data): New declarations.
(DECL_FILTER_ACTION): Declare inject and fault actions.
(DECL_FILTER_ACTION_PARSER): Declare inject and fault action parsers.
* strace.c (trace_syscall): Call filter_syscall only when tcp->qual_flg
is empty.
* syscall.c (decode_socket_subcall): Remove qual_flags from decoder.
(decode_ipc_subcall): Likewise.
(decode_mips_subcall): Likewise.
(get_scno): Likewise.
(inject_vec, tamper_with_syscall_entering): Remove inject_vec support code.
[ldv: fix segfault in parse_inject_common_args]
[ldv: simplify *_qualify_mode]
-rw-r--r-- | basic_actions.c | 53 | ||||
-rw-r--r-- | defs.h | 1 | ||||
-rw-r--r-- | filter.h | 8 | ||||
-rw-r--r-- | filter_action.c | 9 | ||||
-rw-r--r-- | filter_qualify.c | 124 | ||||
-rw-r--r-- | strace.c | 3 | ||||
-rw-r--r-- | syscall.c | 16 |
7 files changed, 122 insertions, 92 deletions
diff --git a/basic_actions.c b/basic_actions.c index bc83edb95..d07dcea04 100644 --- a/basic_actions.c +++ b/basic_actions.c @@ -34,6 +34,12 @@ is_traced(struct tcb *tcp) return traced(tcp); } +bool +not_injected(struct tcb *tcp) +{ + return !inject(tcp); +} + void apply_trace(struct tcb *tcp, void *priv_data) { @@ -57,3 +63,50 @@ apply_verbose(struct tcb *tcp, void *priv_data) { tcp->qual_flg |= QUAL_VERBOSE; } + +void +apply_inject(struct tcb *tcp, void *priv_data) +{ + struct inject_opts *opts = priv_data; + + tcp->qual_flg |= QUAL_INJECT; + if (!tcp->inject_vec[current_personality]) + tcp->inject_vec[current_personality] = + xcalloc(nsyscalls, sizeof(struct inject_opts)); + if (scno_in_range(tcp->scno) + && !tcp->inject_vec[current_personality][tcp->scno].data.flags) + tcp->inject_vec[current_personality][tcp->scno] = *opts; +} + +static void * +parse_inject_common(const char *str, bool fault_tokens_only, + const char *description) +{ + struct inject_opts *opts = xmalloc(sizeof(struct inject_opts)); + char *copy = xstrdup(str); + + parse_inject_common_args(copy, opts, fault_tokens_only, false); + if (!opts->data.flags) + error_msg_and_die("invalid %s argument '%s'", + description, str ? str : ""); + free(copy); + return opts; +} + +void * +parse_inject(const char *str) +{ + return parse_inject_common(str, false, "inject"); +} + +void +apply_fault(struct tcb *tcp, void *priv_data) +{ + apply_inject(tcp, priv_data); +} + +void * +parse_fault(const char *str) +{ + return parse_inject_common(str, true, "fault"); +} @@ -914,7 +914,6 @@ extern void print_ifindex(unsigned int); extern void print_bpf_filter_code(const uint16_t code, bool extended); extern void qualify(const char *); -extern unsigned int qual_flags(const unsigned int); extern void filtering_parsing_finish(void); extern void filter_syscall(struct tcb *); @@ -42,7 +42,10 @@ typedef int (*string_to_uint_func)(const char *); void qualify_tokens(const char *str, struct number_set *set, string_to_uint_func func, const char *name); void qualify_syscall_tokens(const char *str, struct number_set *set); +void parse_inject_common_args(char *, struct inject_opts *, + const bool fault_tokens_only, bool qualify_mode); bool is_traced(struct tcb *); +bool not_injected(struct tcb *); /* filter api */ struct filter* add_filter_to_array(struct filter **, unsigned int *nfilters, @@ -56,6 +59,7 @@ void set_filters_qualify_mode(struct filter **, unsigned int *nfilters); struct filter *create_filter(struct filter_action *, const char *name); struct filter_action *find_or_add_action(const char *); void set_qualify_mode(struct filter_action *); +void set_filter_action_priv_data(struct filter_action *, void *); /* filter expression api */ struct bool_expression *create_expression(); @@ -83,6 +87,8 @@ DECL_FILTER_ACTION(trace); DECL_FILTER_ACTION(raw); DECL_FILTER_ACTION(abbrev); DECL_FILTER_ACTION(verbose); +DECL_FILTER_ACTION(inject); +DECL_FILTER_ACTION(fault); #undef DECL_FILTER_ACTION #define DECL_FILTER_ACTION_PARSER(name) \ @@ -91,6 +97,8 @@ parse_ ## name(const char *); \ /* End of DECL_FILTER_ACTION_PARSER definition. */ #define parse_NULL NULL +DECL_FILTER_ACTION_PARSER(inject); +DECL_FILTER_ACTION_PARSER(fault); #undef DECL_FILTER_ACTION_PARSER #endif /* !STRACE_FILTER_H */ diff --git a/filter_action.c b/filter_action.c index b2e868660..c404a2094 100644 --- a/filter_action.c +++ b/filter_action.c @@ -42,6 +42,8 @@ static const struct filter_action_type { void (*apply)(struct tcb *, void *); } action_types[] = { FILTER_ACTION_TYPE(trace, 0, QUAL_TRACE, NULL, NULL), + FILTER_ACTION_TYPE(inject, 1, QUAL_INJECT, inject, not_injected), + FILTER_ACTION_TYPE(fault, 1, QUAL_INJECT, fault, not_injected), FILTER_ACTION_TYPE(raw, 2, QUAL_RAW, NULL, is_traced), FILTER_ACTION_TYPE(abbrev, 2, QUAL_ABBREV, NULL, is_traced), FILTER_ACTION_TYPE(verbose, 2, QUAL_VERBOSE, NULL, is_traced), @@ -174,3 +176,10 @@ filter_syscall(struct tcb *tcp) for (unsigned int i = 0; i < nfilter_actions; ++i) run_filter_action(tcp, &filter_actions[i]); } + +void +set_filter_action_priv_data(struct filter_action *action, void *priv_data) +{ + if (action) + action->priv_data = priv_data; +} diff --git a/filter_qualify.c b/filter_qualify.c index f36db94ce..809a1ee45 100644 --- a/filter_qualify.c +++ b/filter_qualify.c @@ -37,8 +37,6 @@ struct number_set *read_set; struct number_set *write_set; struct number_set *signal_set; -static struct number_set *inject_set; - static int sigstr_to_uint(const char *s) { @@ -216,24 +214,41 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts, return true; } -static const char * -parse_inject_expression(char *const str, - struct inject_opts *const fopts, - const bool fault_tokens_only) +void +parse_inject_common_args(char *const str, struct inject_opts *const opts, + const bool fault_tokens_only, const bool qualify_mode) { - if (str[0] == '\0' || str[0] == ':') - return ""; - char *saveptr = NULL; - const char *name = strtok_r(str, ":", &saveptr); + const char *delim = qualify_mode ? ":" : ";"; + + *opts = (struct inject_opts) { + .first = 1, + .step = 1, + .data = { + .delay_idx = -1 + } + }; - char *token; - while ((token = strtok_r(NULL, ":", &saveptr))) { - if (!parse_inject_token(token, fopts, fault_tokens_only)) - return NULL; + for (char *token = str ? strtok_r(str, delim, &saveptr) : NULL; + token; token = strtok_r(NULL, delim, &saveptr)) { + if (!parse_inject_token(token, opts, fault_tokens_only)) { + /* return an error by resetting inject flags */ + opts->data.flags = 0; + return; + } } - return name; + /* If neither of retval, error, signal or delay is specified, then ... */ + 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_ERROR; + } else { + /* in inject= syntax this is not allowed. */ + return; + } + } } static void @@ -300,75 +315,39 @@ qualify_inject_common(const char *const str, const bool fault_tokens_only, const char *const description) { - struct inject_opts opts = { - .first = 1, - .step = 1, - .data = { - .delay_idx = -1 - } - }; + struct inject_opts *opts = xmalloc(sizeof(struct inject_opts)); char *copy = xstrdup(str); - const char *name = - parse_inject_expression(copy, &opts, fault_tokens_only); - if (!name) - error_msg_and_die("invalid %s '%s'", description, str); - - struct number_set *tmp_set = - alloc_number_set_array(SUPPORTED_PERSONALITIES); - qualify_syscall_tokens(name, tmp_set); - - free(copy); + char *args = strchr(copy, ':'); + struct filter_action *action; + struct filter *filter; - /* If neither of retval, error, signal or delay is specified, then ... */ - 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_ERROR; - } else { - /* in inject= syntax this is not allowed. */ - error_msg_and_die("invalid %s '%s'", description, str); - } - } + if (args) + *(args++) = '\0'; - /* - * Initialize inject_vec according to tmp_set. - * Merge tmp_set into inject_set. - */ - for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) { - if (number_set_array_is_empty(tmp_set, p)) - continue; - - if (!inject_set) { - inject_set = - alloc_number_set_array(SUPPORTED_PERSONALITIES); - } - if (!inject_vec[p]) { - inject_vec[p] = xcalloc(nsyscall_vec[p], - sizeof(*inject_vec[p])); - } + action = find_or_add_action(fault_tokens_only ? "fault" : "inject"); + filter = create_filter(action, "syscall"); + parse_filter(filter, copy); + set_qualify_mode(action); + parse_inject_common_args(args, opts, fault_tokens_only, true); - for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) { - if (is_number_in_set_array(i, tmp_set, p)) { - add_number_to_set_array(i, inject_set, p); - inject_vec[p][i] = opts; - } - } - } + if (!opts->data.flags) + error_msg_and_die("invalid %s argument '%s'", description, + args ? args : ""); + free(copy); - free_number_set_array(tmp_set, SUPPORTED_PERSONALITIES); + set_filter_action_priv_data(action, opts); } static void qualify_fault(const char *const str) { - qualify_inject_common(str, true, "fault argument"); + qualify_inject_common(str, true, "fault"); } static void qualify_inject(const char *const str) { - qualify_inject_common(str, false, "inject argument"); + qualify_inject_common(str, false, "inject"); } static const struct qual_options { @@ -415,10 +394,3 @@ qualify(const char *str) opt->qualify(str); } - -unsigned int -qual_flags(const unsigned int scno) -{ - return is_number_in_set_array(scno, inject_set, current_personality) - ? QUAL_INJECT : 0; -} @@ -2415,7 +2415,8 @@ trace_syscall(struct tcb *tcp, unsigned int *sig) case 0: return 0; case 1: - filter_syscall(tcp); + if (!tcp->qual_flg) + filter_syscall(tcp); res = syscall_entering_trace(tcp, sig); } syscall_entering_finish(tcp, res); @@ -304,7 +304,6 @@ decode_socket_subcall(struct tcb *tcp) return; tcp->scno = scno; - tcp->qual_flg = qual_flags(scno); tcp->s_ent = &sysent[scno]; unsigned int i; @@ -344,7 +343,6 @@ decode_ipc_subcall(struct tcb *tcp) } tcp->scno = SYS_ipc_subcall + call; - tcp->qual_flg = qual_flags(tcp->scno); tcp->s_ent = &sysent[tcp->scno]; const unsigned int n = tcp->s_ent->nargs; @@ -361,7 +359,6 @@ decode_syscall_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->s_ent = &sysent[tcp->scno]; memmove(&tcp->u_arg[0], &tcp->u_arg[1], sizeof(tcp->u_arg) - sizeof(tcp->u_arg[0])); @@ -471,8 +468,6 @@ static void get_error(struct tcb *, const bool); static int arch_set_error(struct tcb *); static int arch_set_success(struct tcb *); -struct inject_opts *inject_vec[SUPPORTED_PERSONALITIES]; - static struct inject_opts * tcb_inject_opts(struct tcb *tcp) { @@ -484,14 +479,6 @@ tcb_inject_opts(struct tcb *tcp) static long tamper_with_syscall_entering(struct tcb *tcp, unsigned int *signo) { - if (!tcp->inject_vec[current_personality]) { - tcp->inject_vec[current_personality] = - xcalloc(nsyscalls, sizeof(**inject_vec)); - memcpy(tcp->inject_vec[current_personality], - inject_vec[current_personality], - nsyscalls * sizeof(**inject_vec)); - } - struct inject_opts *opts = tcb_inject_opts(tcp); if (!opts || opts->first == 0) @@ -1188,7 +1175,8 @@ get_scno(struct tcb *tcp) if (scno_is_valid(tcp->scno)) { tcp->s_ent = &sysent[tcp->scno]; - tcp->qual_flg = qual_flags(tcp->scno); + /* Clear qual_flg to distinguish valid syscall from printargs */ + tcp->qual_flg = 0; } else { struct sysent_buf *s = xcalloc(1, sizeof(*s)); |