1 /*
2  * Copyright (c) 2016 Dmitry V. Levin <ldv@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 *
qualify_syscall_separate_personality(const char * s,unsigned int * p)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
qualify_syscall_number_personality(int n,unsigned int p,struct number_set * set)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
qualify_syscall_number(const char * s,struct number_set * set)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
regerror_msg_and_die(int errcode,const regex_t * preg,const char * str,const char * pattern)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
qualify_syscall_regex(const char * s,struct number_set * set)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
lookup_class(const char * s)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
qualify_syscall_class(const char * s,struct number_set * set)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
scno_by_name(const char * s,unsigned int p,kernel_long_t start)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
qualify_syscall_name_personality(const char * s,unsigned int p,struct number_set * set)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
qualify_syscall_name(const char * s,struct number_set * set)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
qualify_syscall(const char * token,struct number_set * set)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
qualify_syscall_tokens(const char * const str,struct number_set * const set)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
qualify_tokens(const char * const str,struct number_set * const set,string_to_uint_func func,const char * const name)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