123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- /*
- * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
- * Copyright (c) 2016-2018 The strace developers.
- * All rights reserved.
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
- #include "defs.h"
- #include "nsig.h"
- #include "number_set.h"
- #include "filter.h"
- #include "delay.h"
- #include "retval.h"
-
- struct number_set *read_set;
- struct number_set *write_set;
- struct number_set *signal_set;
-
- static struct number_set *abbrev_set;
- static struct number_set *inject_set;
- static struct number_set *raw_set;
- static struct number_set *trace_set;
- static struct number_set *verbose_set;
-
- /* Only syscall numbers are personality-specific so far. */
- struct inject_personality_data {
- uint16_t scno;
- };
-
- static int
- sigstr_to_uint(const char *s)
- {
- if (*s >= '0' && *s <= '9')
- return string_to_uint_upto(s, 255);
-
- if (strncasecmp(s, "SIG", 3) == 0)
- s += 3;
-
- for (int i = 1; i <= 255; ++i) {
- const char *name = signame(i);
-
- if (!name)
- continue;
-
- if (strncasecmp(name, "SIG", 3) != 0)
- continue;
-
- name += 3;
-
- if (strcasecmp(name, s) != 0)
- continue;
-
- return i;
- }
-
- return -1;
- }
-
- static int
- find_errno_by_name(const char *name)
- {
- for (unsigned int i = 1; i < nerrnos; ++i) {
- if (errnoent[i] && (strcasecmp(name, errnoent[i]) == 0))
- return i;
- }
-
- return -1;
- }
-
- static bool
- parse_delay_token(const char *input, struct inject_opts *fopts, bool isenter)
- {
- unsigned flag = isenter ? INJECT_F_DELAY_ENTER : INJECT_F_DELAY_EXIT;
-
- if (fopts->data.flags & flag) /* duplicate */
- return false;
- long long intval = string_to_ulonglong(input);
- if (intval < 0) /* couldn't parse */
- return false;
-
- if (fopts->data.delay_idx == (uint16_t) -1)
- fopts->data.delay_idx = alloc_delay_data();
- /* populate .ts_enter or .ts_exit */
- fill_delay_data(fopts->data.delay_idx, intval, isenter);
- fopts->data.flags |= flag;
-
- return true;
- }
-
- static bool
- parse_inject_token(const char *const token, struct inject_opts *const fopts,
- struct inject_personality_data *const pdata,
- const bool fault_tokens_only)
- {
- const char *val;
- int intval;
-
- if ((val = STR_STRIP_PREFIX(token, "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 = STR_STRIP_PREFIX(token, "syscall=")) != token) {
- if (fopts->data.flags & INJECT_F_SYSCALL)
- return false;
-
- for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
- kernel_long_t scno = scno_by_name(val, p, 0);
-
- if (scno < 0)
- return false;
-
- /*
- * We want to inject only pure system calls with no side
- * effects.
- */
- if (!(sysent_vec[p][scno].sys_flags & TRACE_PURE))
- return false;
-
- pdata[p].scno = scno;
- }
-
- fopts->data.flags |= INJECT_F_SYSCALL;
- } else if ((val = STR_STRIP_PREFIX(token, "error=")) != token) {
- 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_ERROR;
- } else if (!fault_tokens_only
- && (val = STR_STRIP_PREFIX(token, "retval=")) != token) {
-
- if (fopts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL))
- return false;
-
- 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 ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
- bool inadvertent_fault_injection = false;
- #endif
-
- #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) {
- if (fopts->data.flags & INJECT_F_SIGNAL)
- return false;
- intval = sigstr_to_uint(val);
- if (intval < 1 || intval > NSIG_BYTES * 8)
- return false;
- fopts->data.signo = intval;
- fopts->data.flags |= INJECT_F_SIGNAL;
- } else if (!fault_tokens_only
- && (val = STR_STRIP_PREFIX(token, "delay_enter=")) != token) {
- if (!parse_delay_token(val, fopts, true))
- return false;
- } else if (!fault_tokens_only
- && (val = STR_STRIP_PREFIX(token, "delay_exit=")) != token) {
- if (!parse_delay_token(val, fopts, false))
- return false;
- } else {
- return false;
- }
-
- return true;
- }
-
- static const char *
- parse_inject_expression(char *const str,
- struct inject_opts *const fopts,
- struct inject_personality_data *const pdata,
- const bool fault_tokens_only)
- {
- if (str[0] == '\0' || str[0] == ':')
- return "";
-
- char *saveptr = NULL;
- const char *name = strtok_r(str, ":", &saveptr);
-
- char *token;
- while ((token = strtok_r(NULL, ":", &saveptr))) {
- if (!parse_inject_token(token, fopts, pdata, fault_tokens_only))
- return NULL;
- }
-
- return name;
- }
-
- static void
- qualify_read(const char *const str)
- {
- if (!read_set)
- read_set = alloc_number_set_array(1);
- qualify_tokens(str, read_set, string_to_uint, "descriptor");
- }
-
- static void
- qualify_write(const char *const str)
- {
- if (!write_set)
- write_set = alloc_number_set_array(1);
- qualify_tokens(str, write_set, string_to_uint, "descriptor");
- }
-
- static void
- qualify_signals(const char *const str)
- {
- if (!signal_set)
- signal_set = alloc_number_set_array(1);
- qualify_tokens(str, signal_set, sigstr_to_uint, "signal");
- }
-
- static void
- qualify_trace(const char *const str)
- {
- if (!trace_set)
- trace_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
- qualify_syscall_tokens(str, trace_set);
- }
-
- static void
- qualify_abbrev(const char *const str)
- {
- if (!abbrev_set)
- abbrev_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
- qualify_syscall_tokens(str, abbrev_set);
- }
-
- static void
- qualify_verbose(const char *const str)
- {
- if (!verbose_set)
- verbose_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
- qualify_syscall_tokens(str, verbose_set);
- }
-
- static void
- qualify_raw(const char *const str)
- {
- if (!raw_set)
- raw_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
- qualify_syscall_tokens(str, raw_set);
- }
-
- static void
- 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_personality_data pdata[SUPPORTED_PERSONALITIES] = { { 0 } };
- char *copy = xstrdup(str);
- const char *name =
- parse_inject_expression(copy, &opts, pdata, 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);
-
- /* If neither of retval, error, signal or delay is specified, then ... */
- if (!(opts.data.flags & INJECT_ACTION_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);
- }
- }
-
- /*
- * 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]));
- }
-
- 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;
-
- /* Copy per-personality data. */
- inject_vec[p][i].data.scno =
- pdata[p].scno;
- }
- }
- }
-
- free_number_set_array(tmp_set, SUPPORTED_PERSONALITIES);
- }
-
- static void
- qualify_fault(const char *const str)
- {
- qualify_inject_common(str, true, "fault argument");
- }
-
- static void
- qualify_inject(const char *const str)
- {
- qualify_inject_common(str, false, "inject argument");
- }
-
- static void
- qualify_kvm(const char *const str)
- {
- if (strcmp(str, "vcpu") == 0) {
- #ifdef HAVE_LINUX_KVM_H
- if (os_release >= KERNEL_VERSION(4, 16, 0))
- kvm_run_structure_decoder_init();
- else
- error_msg("-e kvm=vcpu option needs"
- " Linux 4.16.0 or higher");
- #else
- error_msg("-e kvm=vcpu option is not implemented"
- " for this architecture");
- #endif
- } else {
- error_msg_and_die("invalid -e kvm= argument: '%s'", str);
- }
- }
-
- 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 },
- { "inject", qualify_inject },
- { "kvm", qualify_kvm },
- };
-
- void
- qualify(const char *str)
- {
- const struct qual_options *opt = qual_options;
-
- for (unsigned int i = 0; i < ARRAY_SIZE(qual_options); ++i) {
- const char *name = qual_options[i].name;
- const size_t len = strlen(name);
- const char *val = str_strip_prefix_len(str, name, len);
-
- if (val == str || *val != '=')
- continue;
- str = val + 1;
- opt = &qual_options[i];
- break;
- }
-
- opt->qualify(str);
- }
-
- unsigned int
- qual_flags(const unsigned int scno)
- {
- return (is_number_in_set_array(scno, trace_set, current_personality)
- ? QUAL_TRACE : 0)
- | (is_number_in_set_array(scno, abbrev_set, current_personality)
- ? QUAL_ABBREV : 0)
- | (is_number_in_set_array(scno, verbose_set, current_personality)
- ? QUAL_VERBOSE : 0)
- | (is_number_in_set_array(scno, raw_set, current_personality)
- ? QUAL_RAW : 0)
- | (is_number_in_set_array(scno, inject_set, current_personality)
- ? QUAL_INJECT : 0);
- }
-
- unsigned int
- next_set_qual_scno(const unsigned int scno, unsigned qual_flg)
- {
- static const struct qual_set {
- unsigned qual;
- struct number_set ** const set;
- } sets[] = {
- { QUAL_TRACE, &trace_set },
- { QUAL_ABBREV, &abbrev_set },
- { QUAL_VERBOSE, &verbose_set },
- { QUAL_RAW, &raw_set },
- { QUAL_INJECT, &inject_set },
- { 0, NULL }
- };
-
- const struct qual_set *pos = sets;
- unsigned res = nsyscalls;
- unsigned ret;
-
- while (qual_flg && pos->qual) {
- ret = next_set_bit_in_set_array(scno, *(pos->set),
- current_personality,
- nsyscalls);
-
- res = MIN(res, ret);
- qual_flg &= ~pos->qual;
- pos++;
- }
-
- return res;
- }
|