Home | History | Annotate | Download | only in strace
      1 /*
      2  * Copyright (c) 2016 Dmitry V. Levin <ldv (at) altlinux.org>
      3  * Copyright (c) 2016-2018 The strace developers.
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "defs.h"
     30 
     31 #include <regex.h>
     32 
     33 #include "filter.h"
     34 #include "number_set.h"
     35 #include "xstring.h"
     36 
     37 
     38 /**
     39  * Checks whether a @-separated personality specification suffix is present.
     40  * Personality suffix is a one of strings stored in personality_designators
     41  * array.
     42  *
     43  * @param[in]  s Specification string to check.
     44  * @param[out] p Where to store personality number if it is found.
     45  * @return       If personality is found, the provided string is copied without
     46  *               suffix and returned as a result (callee should de-alllocate it
     47  *               with free() after use), and personality number is written to p.
     48  *               Otherwise, NULL is returned and p is untouched.
     49  */
     50 static char *
     51 qualify_syscall_separate_personality(const char *s, unsigned int *p)
     52 {
     53 	char *pos = strchr(s, '@');
     54 
     55 	if (!pos)
     56 		return NULL;
     57 
     58 	for (unsigned int i = 0; i < SUPPORTED_PERSONALITIES; i++) {
     59 		if (!strcmp(pos + 1, personality_designators[i])) {
     60 			*p = i;
     61 			return xstrndup(s, pos - s);
     62 		}
     63 	}
     64 
     65 	error_msg_and_help("incorrect personality designator '%s'"
     66 			   " in qualification '%s'", pos + 1, s);
     67 }
     68 
     69 static bool
     70 qualify_syscall_number_personality(int n, unsigned int p,
     71 				   struct number_set *set)
     72 {
     73 	if ((unsigned int) n >= nsyscall_vec[p])
     74 		return false;
     75 
     76 	add_number_to_set_array(n, set, p);
     77 
     78 	return true;
     79 }
     80 
     81 static bool
     82 qualify_syscall_number(const char *s, struct number_set *set)
     83 {
     84 	unsigned int p;
     85 	char *num_str = qualify_syscall_separate_personality(s, &p);
     86 	int n;
     87 
     88 	if (num_str) {
     89 		n = string_to_uint(num_str);
     90 		free(num_str);
     91 
     92 		if (n < 0)
     93 			return false;
     94 
     95 		return qualify_syscall_number_personality(n, p, set);
     96 	}
     97 
     98 	n = string_to_uint(s);
     99 	if (n < 0)
    100 		return false;
    101 
    102 	bool done = false;
    103 
    104 	for (p = 0; p < SUPPORTED_PERSONALITIES; ++p)
    105 		done |= qualify_syscall_number_personality(n, p, set);
    106 
    107 	return done;
    108 }
    109 
    110 static void
    111 regerror_msg_and_die(int errcode, const regex_t *preg,
    112 		     const char *str, const char *pattern)
    113 {
    114 	char buf[512];
    115 
    116 	regerror(errcode, preg, buf, sizeof(buf));
    117 	error_msg_and_die("%s: %s: %s", str, pattern, buf);
    118 }
    119 
    120 static bool
    121 qualify_syscall_regex(const char *s, struct number_set *set)
    122 {
    123 	regex_t preg;
    124 	int rc;
    125 
    126 	if ((rc = regcomp(&preg, s, REG_EXTENDED | REG_NOSUB)) != 0)
    127 		regerror_msg_and_die(rc, &preg, "regcomp", s);
    128 
    129 	bool found = false;
    130 
    131 	for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    132 		for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
    133 			if (!sysent_vec[p][i].sys_name)
    134 				continue;
    135 
    136 			rc = regexec(&preg, sysent_vec[p][i].sys_name,
    137 				     0, NULL, 0);
    138 
    139 			if (rc == REG_NOMATCH) {
    140 				char name_buf[128];
    141 				char *pos = stpcpy(name_buf,
    142 						   sysent_vec[p][i].sys_name);
    143 
    144 				(void) xappendstr(name_buf, pos, "@%s",
    145 						  personality_designators[p]);
    146 
    147 				rc = regexec(&preg, name_buf, 0, NULL, 0);
    148 			}
    149 
    150 			if (rc == REG_NOMATCH)
    151 				continue;
    152 			else if (rc)
    153 				regerror_msg_and_die(rc, &preg, "regexec", s);
    154 
    155 			add_number_to_set_array(i, set, p);
    156 			found = true;
    157 		}
    158 	}
    159 
    160 	regfree(&preg);
    161 	return found;
    162 }
    163 
    164 static unsigned int
    165 lookup_class(const char *s)
    166 {
    167 	static const struct {
    168 		const char *name;
    169 		unsigned int value;
    170 	} syscall_class[] = {
    171 		{ "%desc",	TRACE_DESC	},
    172 		{ "%file",	TRACE_FILE	},
    173 		{ "%memory",	TRACE_MEMORY	},
    174 		{ "%process",	TRACE_PROCESS	},
    175 		{ "%signal",	TRACE_SIGNAL	},
    176 		{ "%ipc",	TRACE_IPC	},
    177 		{ "%network",	TRACE_NETWORK	},
    178 		{ "%stat",	TRACE_STAT	},
    179 		{ "%lstat",	TRACE_LSTAT	},
    180 		{ "%fstat",	TRACE_FSTAT	},
    181 		{ "%%stat",	TRACE_STAT_LIKE	},
    182 		{ "%statfs",	TRACE_STATFS	},
    183 		{ "%fstatfs",	TRACE_FSTATFS	},
    184 		{ "%%statfs",	TRACE_STATFS_LIKE	},
    185 		{ "%pure",	TRACE_PURE	},
    186 		/* legacy class names */
    187 		{ "desc",	TRACE_DESC	},
    188 		{ "file",	TRACE_FILE	},
    189 		{ "memory",	TRACE_MEMORY	},
    190 		{ "process",	TRACE_PROCESS	},
    191 		{ "signal",	TRACE_SIGNAL	},
    192 		{ "ipc",	TRACE_IPC	},
    193 		{ "network",	TRACE_NETWORK	},
    194 	};
    195 
    196 	for (unsigned int i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
    197 		if (strcmp(s, syscall_class[i].name) == 0)
    198 			return syscall_class[i].value;
    199 	}
    200 
    201 	return 0;
    202 }
    203 
    204 static bool
    205 qualify_syscall_class(const char *s, struct number_set *set)
    206 {
    207 	const unsigned int n = lookup_class(s);
    208 	if (!n)
    209 		return false;
    210 
    211 	for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    212 		for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
    213 			if (sysent_vec[p][i].sys_name &&
    214 			    (sysent_vec[p][i].sys_flags & n) == n)
    215 				add_number_to_set_array(i, set, p);
    216 		}
    217 	}
    218 
    219 	return true;
    220 }
    221 
    222 kernel_long_t
    223 scno_by_name(const char *s, unsigned int p, kernel_long_t start)
    224 {
    225 	if (p >= SUPPORTED_PERSONALITIES)
    226 		return -1;
    227 
    228 	for (kernel_ulong_t i = start; i < nsyscall_vec[p]; ++i) {
    229 		if (sysent_vec[p][i].sys_name &&
    230 		    strcmp(s, sysent_vec[p][i].sys_name) == 0)
    231 			return i;
    232 	}
    233 
    234 	return -1;
    235 }
    236 
    237 static bool
    238 qualify_syscall_name_personality(const char *s, unsigned int p,
    239 				 struct number_set *set)
    240 {
    241 	bool found = false;
    242 
    243 	for (kernel_long_t scno = 0; (scno = scno_by_name(s, p, scno)) >= 0;
    244 	     ++scno) {
    245 		add_number_to_set_array(scno, set, p);
    246 		found = true;
    247 	}
    248 
    249 	return found;
    250 }
    251 
    252 static bool
    253 qualify_syscall_name(const char *s, struct number_set *set)
    254 {
    255 	unsigned int p;
    256 	char *name_str = qualify_syscall_separate_personality(s, &p);
    257 	bool found = false;
    258 
    259 	if (name_str) {
    260 		found = qualify_syscall_name_personality(name_str, p, set);
    261 		free(name_str);
    262 
    263 		return found;
    264 	}
    265 
    266 	for (p = 0; p < SUPPORTED_PERSONALITIES; ++p)
    267 		found |= qualify_syscall_name_personality(s, p, set);
    268 
    269 	return found;
    270 }
    271 
    272 static bool
    273 qualify_syscall(const char *token, struct number_set *set)
    274 {
    275 	bool ignore_fail = false;
    276 
    277 	while (*token == '?') {
    278 		token++;
    279 		ignore_fail = true;
    280 	}
    281 	if (*token >= '0' && *token <= '9')
    282 		return qualify_syscall_number(token, set) || ignore_fail;
    283 	if (*token == '/')
    284 		return qualify_syscall_regex(token + 1, set) || ignore_fail;
    285 	return qualify_syscall_class(token, set)
    286 	       || qualify_syscall_name(token, set)
    287 	       || ignore_fail;
    288 }
    289 
    290 /*
    291  * Add syscall numbers to SETs for each supported personality
    292  * according to STR specification.
    293  */
    294 void
    295 qualify_syscall_tokens(const char *const str, struct number_set *const set)
    296 {
    297 	/* Clear all sets. */
    298 	clear_number_set_array(set, SUPPORTED_PERSONALITIES);
    299 
    300 	/*
    301 	 * Each leading ! character means inversion
    302 	 * of the remaining specification.
    303 	 */
    304 	const char *s = str;
    305 	while (*s == '!') {
    306 		invert_number_set_array(set, SUPPORTED_PERSONALITIES);
    307 		++s;
    308 	}
    309 
    310 	if (strcmp(s, "none") == 0) {
    311 		/*
    312 		 * No syscall numbers are added to sets.
    313 		 * Subsequent is_number_in_set* invocations
    314 		 * will return set[p]->not.
    315 		 */
    316 		return;
    317 	} else if (strcmp(s, "all") == 0) {
    318 		/* "all" == "!none" */
    319 		invert_number_set_array(set, SUPPORTED_PERSONALITIES);
    320 		return;
    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 	bool done = false;
    333 
    334 	for (const char *token = strtok_r(copy, ",", &saveptr);
    335 	     token; token = strtok_r(NULL, ",", &saveptr)) {
    336 		done = qualify_syscall(token, set);
    337 		if (!done)
    338 			error_msg_and_die("invalid system call '%s'", token);
    339 	}
    340 
    341 	free(copy);
    342 
    343 	if (!done)
    344 		error_msg_and_die("invalid system call '%s'", str);
    345 }
    346 
    347 /*
    348  * Add numbers to SET according to STR specification.
    349  */
    350 void
    351 qualify_tokens(const char *const str, struct number_set *const set,
    352 	       string_to_uint_func func, const char *const name)
    353 {
    354 	/* Clear the set. */
    355 	clear_number_set_array(set, 1);
    356 
    357 	/*
    358 	 * Each leading ! character means inversion
    359 	 * of the remaining specification.
    360 	 */
    361 	const char *s = str;
    362 	while (*s == '!') {
    363 		invert_number_set_array(set, 1);
    364 		++s;
    365 	}
    366 
    367 	if (strcmp(s, "none") == 0) {
    368 		/*
    369 		 * No numbers are added to the set.
    370 		 * Subsequent is_number_in_set* invocations
    371 		 * will return set->not.
    372 		 */
    373 		return;
    374 	} else if (strcmp(s, "all") == 0) {
    375 		/* "all" == "!none" */
    376 		invert_number_set_array(set, 1);
    377 		return;
    378 	}
    379 
    380 	/*
    381 	 * Split the string into comma separated tokens.
    382 	 * For each token, find out the corresponding number
    383 	 * by calling FUNC, and add that number to the set.
    384 	 * The absence of tokens or a negative answer
    385 	 * from FUNC is a fatal error.
    386 	 */
    387 	char *copy = xstrdup(s);
    388 	char *saveptr = NULL;
    389 	int number = -1;
    390 
    391 	for (const char *token = strtok_r(copy, ",", &saveptr);
    392 	     token; token = strtok_r(NULL, ",", &saveptr)) {
    393 		number = func(token);
    394 		if (number < 0)
    395 			error_msg_and_die("invalid %s '%s'", name, token);
    396 
    397 		add_number_to_set(number, set);
    398 	}
    399 
    400 	free(copy);
    401 
    402 	if (number < 0)
    403 		error_msg_and_die("invalid %s '%s'", name, str);
    404 }
    405