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 #include "number_set.h"
     31 #include "filter.h"
     32 #include <regex.h>
     33 
     34 static bool
     35 qualify_syscall_number(const char *s, struct number_set *set)
     36 {
     37 	int n = string_to_uint(s);
     38 	if (n < 0)
     39 		return false;
     40 
     41 	bool done = false;
     42 
     43 	for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
     44 		if ((unsigned) n >= nsyscall_vec[p])
     45 			continue;
     46 		add_number_to_set_array(n, set, p);
     47 		done = true;
     48 	}
     49 
     50 	return done;
     51 }
     52 
     53 static void
     54 regerror_msg_and_die(int errcode, const regex_t *preg,
     55 		     const char *str, const char *pattern)
     56 {
     57 	char buf[512];
     58 
     59 	regerror(errcode, preg, buf, sizeof(buf));
     60 	error_msg_and_die("%s: %s: %s", str, pattern, buf);
     61 }
     62 
     63 static bool
     64 qualify_syscall_regex(const char *s, struct number_set *set)
     65 {
     66 	regex_t preg;
     67 	int rc;
     68 
     69 	if ((rc = regcomp(&preg, s, REG_EXTENDED | REG_NOSUB)) != 0)
     70 		regerror_msg_and_die(rc, &preg, "regcomp", s);
     71 
     72 	bool found = false;
     73 
     74 	for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
     75 		for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
     76 			if (!sysent_vec[p][i].sys_name)
     77 				continue;
     78 			rc = regexec(&preg, sysent_vec[p][i].sys_name,
     79 				     0, NULL, 0);
     80 			if (rc == REG_NOMATCH)
     81 				continue;
     82 			else if (rc)
     83 				regerror_msg_and_die(rc, &preg, "regexec", s);
     84 			add_number_to_set_array(i, set, p);
     85 			found = true;
     86 		}
     87 	}
     88 
     89 	regfree(&preg);
     90 	return found;
     91 }
     92 
     93 static unsigned int
     94 lookup_class(const char *s)
     95 {
     96 	static const struct {
     97 		const char *name;
     98 		unsigned int value;
     99 	} syscall_class[] = {
    100 		{ "%desc",	TRACE_DESC	},
    101 		{ "%file",	TRACE_FILE	},
    102 		{ "%memory",	TRACE_MEMORY	},
    103 		{ "%process",	TRACE_PROCESS	},
    104 		{ "%signal",	TRACE_SIGNAL	},
    105 		{ "%ipc",	TRACE_IPC	},
    106 		{ "%network",	TRACE_NETWORK	},
    107 		{ "%stat",	TRACE_STAT	},
    108 		{ "%lstat",	TRACE_LSTAT	},
    109 		{ "%fstat",	TRACE_FSTAT	},
    110 		{ "%%stat",	TRACE_STAT_LIKE	},
    111 		{ "%statfs",	TRACE_STATFS	},
    112 		{ "%fstatfs",	TRACE_FSTATFS	},
    113 		{ "%%statfs",	TRACE_STATFS_LIKE	},
    114 		{ "%pure",	TRACE_PURE	},
    115 		/* legacy class names */
    116 		{ "desc",	TRACE_DESC	},
    117 		{ "file",	TRACE_FILE	},
    118 		{ "memory",	TRACE_MEMORY	},
    119 		{ "process",	TRACE_PROCESS	},
    120 		{ "signal",	TRACE_SIGNAL	},
    121 		{ "ipc",	TRACE_IPC	},
    122 		{ "network",	TRACE_NETWORK	},
    123 	};
    124 
    125 	for (unsigned int i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
    126 		if (strcmp(s, syscall_class[i].name) == 0)
    127 			return syscall_class[i].value;
    128 	}
    129 
    130 	return 0;
    131 }
    132 
    133 static bool
    134 qualify_syscall_class(const char *s, struct number_set *set)
    135 {
    136 	const unsigned int n = lookup_class(s);
    137 	if (!n)
    138 		return false;
    139 
    140 	for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    141 		for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
    142 			if (sysent_vec[p][i].sys_name &&
    143 			    (sysent_vec[p][i].sys_flags & n) == n)
    144 				add_number_to_set_array(i, set, p);
    145 		}
    146 	}
    147 
    148 	return true;
    149 }
    150 
    151 kernel_long_t
    152 scno_by_name(const char *s, unsigned int p, kernel_long_t start)
    153 {
    154 	if (p >= SUPPORTED_PERSONALITIES)
    155 		return -1;
    156 
    157 	for (kernel_ulong_t i = start; i < nsyscall_vec[p]; ++i) {
    158 		if (sysent_vec[p][i].sys_name &&
    159 		    strcmp(s, sysent_vec[p][i].sys_name) == 0)
    160 			return i;
    161 	}
    162 
    163 	return -1;
    164 }
    165 
    166 static bool
    167 qualify_syscall_name(const char *s, struct number_set *set)
    168 {
    169 	bool found = false;
    170 
    171 	for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
    172 		for (kernel_long_t scno = 0;
    173 		     (scno = scno_by_name(s, p, scno)) >= 0;
    174 		     ++scno) {
    175 			add_number_to_set_array(scno, set, p);
    176 			found = true;
    177 		}
    178 	}
    179 
    180 	return found;
    181 }
    182 
    183 static bool
    184 qualify_syscall(const char *token, struct number_set *set)
    185 {
    186 	bool ignore_fail = false;
    187 
    188 	while (*token == '?') {
    189 		token++;
    190 		ignore_fail = true;
    191 	}
    192 	if (*token >= '0' && *token <= '9')
    193 		return qualify_syscall_number(token, set) || ignore_fail;
    194 	if (*token == '/')
    195 		return qualify_syscall_regex(token + 1, set) || ignore_fail;
    196 	return qualify_syscall_class(token, set)
    197 	       || qualify_syscall_name(token, set)
    198 	       || ignore_fail;
    199 }
    200 
    201 /*
    202  * Add syscall numbers to SETs for each supported personality
    203  * according to STR specification.
    204  */
    205 void
    206 qualify_syscall_tokens(const char *const str, struct number_set *const set)
    207 {
    208 	/* Clear all sets. */
    209 	clear_number_set_array(set, SUPPORTED_PERSONALITIES);
    210 
    211 	/*
    212 	 * Each leading ! character means inversion
    213 	 * of the remaining specification.
    214 	 */
    215 	const char *s = str;
    216 	while (*s == '!') {
    217 		invert_number_set_array(set, SUPPORTED_PERSONALITIES);
    218 		++s;
    219 	}
    220 
    221 	if (strcmp(s, "none") == 0) {
    222 		/*
    223 		 * No syscall numbers are added to sets.
    224 		 * Subsequent is_number_in_set* invocations
    225 		 * will return set[p]->not.
    226 		 */
    227 		return;
    228 	} else if (strcmp(s, "all") == 0) {
    229 		/* "all" == "!none" */
    230 		invert_number_set_array(set, SUPPORTED_PERSONALITIES);
    231 		return;
    232 	}
    233 
    234 	/*
    235 	 * Split the string into comma separated tokens.
    236 	 * For each token, call qualify_syscall that will take care
    237 	 * if adding appropriate syscall numbers to sets.
    238 	 * The absence of tokens or a negative return code
    239 	 * from qualify_syscall is a fatal error.
    240 	 */
    241 	char *copy = xstrdup(s);
    242 	char *saveptr = NULL;
    243 	bool done = false;
    244 
    245 	for (const char *token = strtok_r(copy, ",", &saveptr);
    246 	     token; token = strtok_r(NULL, ",", &saveptr)) {
    247 		done = qualify_syscall(token, set);
    248 		if (!done)
    249 			error_msg_and_die("invalid system call '%s'", token);
    250 	}
    251 
    252 	free(copy);
    253 
    254 	if (!done)
    255 		error_msg_and_die("invalid system call '%s'", str);
    256 }
    257 
    258 /*
    259  * Add numbers to SET according to STR specification.
    260  */
    261 void
    262 qualify_tokens(const char *const str, struct number_set *const set,
    263 	       string_to_uint_func func, const char *const name)
    264 {
    265 	/* Clear the set. */
    266 	clear_number_set_array(set, 1);
    267 
    268 	/*
    269 	 * Each leading ! character means inversion
    270 	 * of the remaining specification.
    271 	 */
    272 	const char *s = str;
    273 	while (*s == '!') {
    274 		invert_number_set_array(set, 1);
    275 		++s;
    276 	}
    277 
    278 	if (strcmp(s, "none") == 0) {
    279 		/*
    280 		 * No numbers are added to the set.
    281 		 * Subsequent is_number_in_set* invocations
    282 		 * will return set->not.
    283 		 */
    284 		return;
    285 	} else if (strcmp(s, "all") == 0) {
    286 		/* "all" == "!none" */
    287 		invert_number_set_array(set, 1);
    288 		return;
    289 	}
    290 
    291 	/*
    292 	 * Split the string into comma separated tokens.
    293 	 * For each token, find out the corresponding number
    294 	 * by calling FUNC, and add that number to the set.
    295 	 * The absence of tokens or a negative answer
    296 	 * from FUNC is a fatal error.
    297 	 */
    298 	char *copy = xstrdup(s);
    299 	char *saveptr = NULL;
    300 	int number = -1;
    301 
    302 	for (const char *token = strtok_r(copy, ",", &saveptr);
    303 	     token; token = strtok_r(NULL, ",", &saveptr)) {
    304 		number = func(token);
    305 		if (number < 0)
    306 			error_msg_and_die("invalid %s '%s'", name, token);
    307 
    308 		add_number_to_set(number, set);
    309 	}
    310 
    311 	free(copy);
    312 
    313 	if (number < 0)
    314 		error_msg_and_die("invalid %s '%s'", name, str);
    315 }
    316