Home | History | Annotate | Download | only in strace
      1 /*
      2  * Copyright (c) 2016 Dmitry V. Levin <ldv (at) altlinux.org>
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  * 3. The name of the author may not be used to endorse or promote products
     14  *    derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "defs.h"
     29 #include "nsig.h"
     30 
     31 typedef unsigned int number_slot_t;
     32 #define BITS_PER_SLOT (sizeof(number_slot_t) * 8)
     33 
     34 struct number_set {
     35 	number_slot_t *vec;
     36 	unsigned int nslots;
     37 	bool not;
     38 };
     39 
     40 struct number_set read_set;
     41 struct number_set write_set;
     42 struct number_set signal_set;
     43 
     44 static struct number_set abbrev_set[SUPPORTED_PERSONALITIES];
     45 static struct number_set inject_set[SUPPORTED_PERSONALITIES];
     46 static struct number_set raw_set[SUPPORTED_PERSONALITIES];
     47 static struct number_set trace_set[SUPPORTED_PERSONALITIES];
     48 static struct number_set verbose_set[SUPPORTED_PERSONALITIES];
     49 
     50 static void
     51 number_setbit(const unsigned int i, number_slot_t *const vec)
     52 {
     53 	vec[i / BITS_PER_SLOT] |= (number_slot_t) 1 << (i % BITS_PER_SLOT);
     54 }
     55 
     56 static bool
     57 number_isset(const unsigned int i, const number_slot_t *const vec)
     58 {
     59 	return vec[i / BITS_PER_SLOT] & ((number_slot_t) 1 << (i % BITS_PER_SLOT));
     60 }
     61 
     62 static void
     63 reallocate_number_set(struct number_set *const set, const unsigned int new_nslots)
     64 {
     65 	if (new_nslots <= set->nslots)
     66 		return;
     67 	set->vec = xreallocarray(set->vec, new_nslots, sizeof(*set->vec));
     68 	memset(set->vec + set->nslots, 0,
     69 	       sizeof(*set->vec) * (new_nslots - set->nslots));
     70 	set->nslots = new_nslots;
     71 }
     72 
     73 static void
     74 add_number_to_set(const unsigned int number, struct number_set *const set)
     75 {
     76 	reallocate_number_set(set, number / BITS_PER_SLOT + 1);
     77 	number_setbit(number, set->vec);
     78 }
     79 
     80 bool
     81 is_number_in_set(const unsigned int number, const struct number_set *const set)
     82 {
     83 	return ((number / BITS_PER_SLOT < set->nslots)
     84 		&& number_isset(number, set->vec)) ^ set->not;
     85 }
     86 
     87 typedef int (*string_to_uint_func)(const char *);
     88 
     89 /*
     90  * Add numbers to SET according to STR specification.
     91  */
     92 static void
     93 qualify_tokens(const char *const str, struct number_set *const set,
     94 	       string_to_uint_func func, const char *const name)
     95 {
     96 	/* Clear the set. */
     97 	if (set->nslots)
     98 		memset(set->vec, 0, sizeof(*set->vec) * set->nslots);
     99 	set->not = false;
    100 
    101 	/*
    102 	 * Each leading ! character means inversion
    103 	 * of the remaining specification.
    104 	 */
    105 	const char *s = str;
    106 handle_inversion:
    107 	while (*s == '!') {
    108 		set->not = !set->not;
    109 		++s;
    110 	}
    111 
    112 	if (strcmp(s, "none") == 0) {
    113 		/*
    114 		 * No numbers are added to the set.
    115 		 * Subsequent is_number_in_set invocations will return set->not.
    116 		 */
    117 		return;
    118 	} else if (strcmp(s, "all") == 0) {
    119 		s = "!none";
    120 		goto handle_inversion;
    121 	}
    122 
    123 	/*
    124 	 * Split the string into comma separated tokens.
    125 	 * For each token, find out the corresponding number
    126 	 * by calling FUNC, and add that number to the set.
    127 	 * The absence of tokens or a negative answer
    128 	 * from FUNC is a fatal error.
    129 	 */
    130 	char *copy = xstrdup(s);
    131 	char *saveptr = NULL;
    132 	const char *token;
    133 	int number = -1;
    134 
    135 	for (token = strtok_r(copy, ",", &saveptr); token;
    136 	     token = strtok_r(NULL, ",", &saveptr)) {
    137 		number = func(token);
    138 		if (number < 0) {
    139 			error_msg_and_die("invalid %s '%s'", name, token);
    140 		}
    141 
    142 		add_number_to_set(number, set);
    143 	}
    144 
    145 	free(copy);
    146 
    147 	if (number < 0) {
    148 		error_msg_and_die("invalid %s '%s'", name, str);
    149 	}
    150 }
    151 
    152 static int
    153 sigstr_to_uint(const char *s)
    154 {
    155 	int i;
    156 
    157 	if (*s >= '0' && *s <= '9')
    158 		return string_to_uint_upto(s, 255);
    159 
    160 	if (strncasecmp(s, "SIG", 3) == 0)
    161 		s += 3;
    162 
    163 	for (i = 0; i <= 255; ++i) {
    164 		const char *name = signame(i);
    165 
    166 		if (strncasecmp(name, "SIG", 3) != 0)
    167 			continue;
    168 
    169 		name += 3;
    170 
    171 		if (strcasecmp(name, s) != 0)
    172 			continue;
    173 
    174 		return i;
    175 	}
    176 
    177 	return -1;
    178 }
    179 
    180 static bool
    181 qualify_syscall_number(const char *s, struct number_set *set)
    182 {
    183 	int n = string_to_uint(s);
    184 	if (n < 0)
    185 		return false;
    186 
    187 	unsigned int p;
    188 	bool done = false;
    189 
    190 	for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    191 		if ((unsigned) n >= nsyscall_vec[p]) {
    192 			continue;
    193 		}
    194 		add_number_to_set(n, &set[p]);
    195 		done = true;
    196 	}
    197 
    198 	return done;
    199 }
    200 
    201 static unsigned int
    202 lookup_class(const char *s)
    203 {
    204 	static const struct {
    205 		const char *name;
    206 		unsigned int value;
    207 	} syscall_class[] = {
    208 		{ "desc",	TRACE_DESC	},
    209 		{ "file",	TRACE_FILE	},
    210 		{ "memory",	TRACE_MEMORY	},
    211 		{ "process",	TRACE_PROCESS	},
    212 		{ "signal",	TRACE_SIGNAL	},
    213 		{ "ipc",	TRACE_IPC	},
    214 		{ "network",	TRACE_NETWORK	},
    215 	};
    216 
    217 	unsigned int i;
    218 	for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
    219 		if (strcmp(s, syscall_class[i].name) == 0) {
    220 			return syscall_class[i].value;
    221 		}
    222 	}
    223 
    224 	return 0;
    225 }
    226 
    227 static bool
    228 qualify_syscall_class(const char *s, struct number_set *set)
    229 {
    230 	const unsigned int n = lookup_class(s);
    231 	if (!n)
    232 		return false;
    233 
    234 	unsigned int p;
    235 	for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    236 		unsigned int i;
    237 
    238 		for (i = 0; i < nsyscall_vec[p]; ++i) {
    239 			if (!sysent_vec[p][i].sys_name
    240 			    || (sysent_vec[p][i].sys_flags & n) != n) {
    241 				continue;
    242 			}
    243 			add_number_to_set(i, &set[p]);
    244 		}
    245 	}
    246 
    247 	return true;
    248 }
    249 
    250 static bool
    251 qualify_syscall_name(const char *s, struct number_set *set)
    252 {
    253 	unsigned int p;
    254 	bool found = false;
    255 
    256 	for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    257 		unsigned int i;
    258 
    259 		for (i = 0; i < nsyscall_vec[p]; ++i) {
    260 			if (!sysent_vec[p][i].sys_name
    261 			    || strcmp(s, sysent_vec[p][i].sys_name)) {
    262 				continue;
    263 			}
    264 			add_number_to_set(i, &set[p]);
    265 			found = true;
    266 		}
    267 	}
    268 
    269 	return found;
    270 }
    271 
    272 static bool
    273 qualify_syscall(const char *token, struct number_set *set)
    274 {
    275 	if (*token >= '0' && *token <= '9')
    276 		return qualify_syscall_number(token, set);
    277 	return qualify_syscall_class(token, set)
    278 	       || qualify_syscall_name(token, set);
    279 }
    280 
    281 /*
    282  * Add syscall numbers to SETs for each supported personality
    283  * according to STR specification.
    284  */
    285 static void
    286 qualify_syscall_tokens(const char *const str, struct number_set *const set,
    287 		       const char *const name)
    288 {
    289 	/* Clear all sets. */
    290 	unsigned int p;
    291 	for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    292 		if (set[p].nslots)
    293 			memset(set[p].vec, 0,
    294 			       sizeof(*set[p].vec) * set[p].nslots);
    295 		set[p].not = false;
    296 	}
    297 
    298 	/*
    299 	 * Each leading ! character means inversion
    300 	 * of the remaining specification.
    301 	 */
    302 	const char *s = str;
    303 handle_inversion:
    304 	while (*s == '!') {
    305 		for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    306 			set[p].not = !set[p].not;
    307 		}
    308 		++s;
    309 	}
    310 
    311 	if (strcmp(s, "none") == 0) {
    312 		/*
    313 		 * No syscall numbers are added to sets.
    314 		 * Subsequent is_number_in_set invocations
    315 		 * will return set[p]->not.
    316 		 */
    317 		return;
    318 	} else if (strcmp(s, "all") == 0) {
    319 		s = "!none";
    320 		goto handle_inversion;
    321 	}
    322 
    323 	/*
    324 	 * Split the string into comma separated tokens.
    325 	 * For each token, call qualify_syscall that will take care
    326 	 * if adding appropriate syscall numbers to sets.
    327 	 * The absence of tokens or a negative return code
    328 	 * from qualify_syscall is a fatal error.
    329 	 */
    330 	char *copy = xstrdup(s);
    331 	char *saveptr = NULL;
    332 	const char *token;
    333 	bool done = false;
    334 
    335 	for (token = strtok_r(copy, ",", &saveptr); token;
    336 	     token = strtok_r(NULL, ",", &saveptr)) {
    337 		done = qualify_syscall(token, set);
    338 		if (!done) {
    339 			error_msg_and_die("invalid %s '%s'", name, token);
    340 		}
    341 	}
    342 
    343 	free(copy);
    344 
    345 	if (!done) {
    346 		error_msg_and_die("invalid %s '%s'", name, str);
    347 	}
    348 }
    349 
    350 /*
    351  * Returns NULL if STR does not start with PREFIX,
    352  * or a pointer to the first char in STR after PREFIX.
    353  */
    354 static const char *
    355 strip_prefix(const char *prefix, const char *str)
    356 {
    357 	size_t len = strlen(prefix);
    358 
    359 	return strncmp(prefix, str, len) ? NULL : str + len;
    360 }
    361 
    362 static int
    363 find_errno_by_name(const char *name)
    364 {
    365 	unsigned int i;
    366 
    367 	for (i = 1; i < nerrnos; ++i) {
    368 		if (errnoent[i] && (strcasecmp(name, errnoent[i]) == 0))
    369 			return i;
    370 	}
    371 
    372 	return -1;
    373 }
    374 
    375 static bool
    376 parse_inject_token(const char *const token, struct inject_opts *const fopts,
    377 		   const bool fault_tokens_only)
    378 {
    379 	const char *val;
    380 	int intval;
    381 
    382 	if ((val = strip_prefix("when=", token))) {
    383 		/*
    384 		 * 	== 1+1
    385 		 * F	== F+0
    386 		 * F+	== F+1
    387 		 * F+S
    388 		 */
    389 		char *end;
    390 		intval = string_to_uint_ex(val, &end, 0xffff, "+");
    391 		if (intval < 1)
    392 			return false;
    393 
    394 		fopts->first = intval;
    395 
    396 		if (*end) {
    397 			val = end + 1;
    398 			if (*val) {
    399 				/* F+S */
    400 				intval = string_to_uint_upto(val, 0xffff);
    401 				if (intval < 1)
    402 					return false;
    403 				fopts->step = intval;
    404 			} else {
    405 				/* F+ == F+1 */
    406 				fopts->step = 1;
    407 			}
    408 		} else {
    409 			/* F == F+0 */
    410 			fopts->step = 0;
    411 		}
    412 	} else if ((val = strip_prefix("error=", token))) {
    413 		if (fopts->rval != INJECT_OPTS_RVAL_DEFAULT)
    414 			return false;
    415 		intval = string_to_uint_upto(val, MAX_ERRNO_VALUE);
    416 		if (intval < 0)
    417 			intval = find_errno_by_name(val);
    418 		if (intval < 1)
    419 			return false;
    420 		fopts->rval = -intval;
    421 	} else if (!fault_tokens_only && (val = strip_prefix("retval=", token))) {
    422 		if (fopts->rval != INJECT_OPTS_RVAL_DEFAULT)
    423 			return false;
    424 		intval = string_to_uint(val);
    425 		if (intval < 0)
    426 			return false;
    427 		fopts->rval = intval;
    428 	} else if (!fault_tokens_only && (val = strip_prefix("signal=", token))) {
    429 		intval = sigstr_to_uint(val);
    430 		if (intval < 1 || intval > NSIG_BYTES * 8)
    431 			return false;
    432 		fopts->signo = intval;
    433 	} else {
    434 		return false;
    435 	}
    436 
    437 	return true;
    438 }
    439 
    440 static char *
    441 parse_inject_expression(const char *const s, char **buf,
    442 			struct inject_opts *const fopts,
    443 			const bool fault_tokens_only)
    444 {
    445 	char *saveptr = NULL;
    446 	char *name = NULL;
    447 	char *token;
    448 
    449 	*buf = xstrdup(s);
    450 	for (token = strtok_r(*buf, ":", &saveptr); token;
    451 	     token = strtok_r(NULL, ":", &saveptr)) {
    452 		if (!name)
    453 			name = token;
    454 		else if (!parse_inject_token(token, fopts, fault_tokens_only))
    455 			goto parse_error;
    456 	}
    457 
    458 	if (name)
    459 		return name;
    460 
    461 parse_error:
    462 	free(*buf);
    463 	return *buf = NULL;
    464 }
    465 
    466 static void
    467 qualify_read(const char *const str)
    468 {
    469 	qualify_tokens(str, &read_set, string_to_uint, "descriptor");
    470 }
    471 
    472 static void
    473 qualify_write(const char *const str)
    474 {
    475 	qualify_tokens(str, &write_set, string_to_uint, "descriptor");
    476 }
    477 
    478 static void
    479 qualify_signals(const char *const str)
    480 {
    481 	qualify_tokens(str, &signal_set, sigstr_to_uint, "signal");
    482 }
    483 
    484 static void
    485 qualify_trace(const char *const str)
    486 {
    487 	qualify_syscall_tokens(str, trace_set, "system call");
    488 }
    489 
    490 static void
    491 qualify_abbrev(const char *const str)
    492 {
    493 	qualify_syscall_tokens(str, abbrev_set, "system call");
    494 }
    495 
    496 static void
    497 qualify_verbose(const char *const str)
    498 {
    499 	qualify_syscall_tokens(str, verbose_set, "system call");
    500 }
    501 
    502 static void
    503 qualify_raw(const char *const str)
    504 {
    505 	qualify_syscall_tokens(str, raw_set, "system call");
    506 }
    507 
    508 static void
    509 qualify_inject_common(const char *const str,
    510 		      const bool fault_tokens_only,
    511 		      const char *const description)
    512 {
    513 	struct inject_opts opts = {
    514 		.first = 1,
    515 		.step = 1,
    516 		.rval = INJECT_OPTS_RVAL_DEFAULT,
    517 		.signo = 0
    518 	};
    519 	char *buf = NULL;
    520 	char *name = parse_inject_expression(str, &buf, &opts, fault_tokens_only);
    521 	if (!name) {
    522 		error_msg_and_die("invalid %s '%s'", description, str);
    523 	}
    524 
    525 	/* If neither of retval, error, or signal is specified, then ... */
    526 	if (opts.rval == INJECT_OPTS_RVAL_DEFAULT && !opts.signo) {
    527 		if (fault_tokens_only) {
    528 			/* in fault= syntax the default error code is ENOSYS. */
    529 			opts.rval = -ENOSYS;
    530 		} else {
    531 			/* in inject= syntax this is not allowed. */
    532 			error_msg_and_die("invalid %s '%s'", description, str);
    533 		}
    534 	}
    535 
    536 	struct number_set tmp_set[SUPPORTED_PERSONALITIES];
    537 	memset(tmp_set, 0, sizeof(tmp_set));
    538 	qualify_syscall_tokens(name, tmp_set, description);
    539 
    540 	free(buf);
    541 
    542 	/*
    543 	 * Initialize inject_vec accourding to tmp_set.
    544 	 * Merge tmp_set into inject_set.
    545 	 */
    546 	unsigned int p;
    547 	for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    548 		if (!tmp_set[p].nslots && !tmp_set[p].not) {
    549 			continue;
    550 		}
    551 
    552 		if (!inject_vec[p]) {
    553 			inject_vec[p] = xcalloc(nsyscall_vec[p],
    554 					       sizeof(*inject_vec[p]));
    555 		}
    556 
    557 		unsigned int i;
    558 		for (i = 0; i < nsyscall_vec[p]; ++i) {
    559 			if (is_number_in_set(i, &tmp_set[p])) {
    560 				add_number_to_set(i, &inject_set[p]);
    561 				inject_vec[p][i] = opts;
    562 			}
    563 		}
    564 
    565 		free(tmp_set[p].vec);
    566 	}
    567 }
    568 
    569 static void
    570 qualify_fault(const char *const str)
    571 {
    572 	qualify_inject_common(str, true, "fault argument");
    573 }
    574 
    575 static void
    576 qualify_inject(const char *const str)
    577 {
    578 	qualify_inject_common(str, false, "inject argument");
    579 }
    580 
    581 static const struct qual_options {
    582 	const char *name;
    583 	void (*qualify)(const char *);
    584 } qual_options[] = {
    585 	{ "trace",	qualify_trace	},
    586 	{ "t",		qualify_trace	},
    587 	{ "abbrev",	qualify_abbrev	},
    588 	{ "a",		qualify_abbrev	},
    589 	{ "verbose",	qualify_verbose	},
    590 	{ "v",		qualify_verbose	},
    591 	{ "raw",	qualify_raw	},
    592 	{ "x",		qualify_raw	},
    593 	{ "signal",	qualify_signals	},
    594 	{ "signals",	qualify_signals	},
    595 	{ "s",		qualify_signals	},
    596 	{ "read",	qualify_read	},
    597 	{ "reads",	qualify_read	},
    598 	{ "r",		qualify_read	},
    599 	{ "write",	qualify_write	},
    600 	{ "writes",	qualify_write	},
    601 	{ "w",		qualify_write	},
    602 	{ "fault",	qualify_fault	},
    603 	{ "inject",	qualify_inject	},
    604 };
    605 
    606 void
    607 qualify(const char *str)
    608 {
    609 	const struct qual_options *opt = qual_options;
    610 	unsigned int i;
    611 
    612 	for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
    613 		const char *p = qual_options[i].name;
    614 		unsigned int len = strlen(p);
    615 
    616 		if (strncmp(str, p, len) || str[len] != '=')
    617 			continue;
    618 
    619 		opt = &qual_options[i];
    620 		str += len + 1;
    621 		break;
    622 	}
    623 
    624 	opt->qualify(str);
    625 }
    626 
    627 unsigned int
    628 qual_flags(const unsigned int scno)
    629 {
    630 	return	(is_number_in_set(scno, &trace_set[current_personality])
    631 		   ? QUAL_TRACE : 0)
    632 		| (is_number_in_set(scno, &abbrev_set[current_personality])
    633 		   ? QUAL_ABBREV : 0)
    634 		| (is_number_in_set(scno, &verbose_set[current_personality])
    635 		   ? QUAL_VERBOSE : 0)
    636 		| (is_number_in_set(scno, &raw_set[current_personality])
    637 		   ? QUAL_RAW : 0)
    638 		| (is_number_in_set(scno, &inject_set[current_personality])
    639 		   ? QUAL_INJECT : 0);
    640 }
    641