• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "number_set.h"
31 #include "filter.h"
32 #include <regex.h>
33 
34 static bool
qualify_syscall_number(const char * s,struct number_set * set)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
regerror_msg_and_die(int errcode,const regex_t * preg,const char * str,const char * pattern)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
qualify_syscall_regex(const char * s,struct number_set * set)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
lookup_class(const char * s)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
qualify_syscall_class(const char * s,struct number_set * set)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
scno_by_name(const char * s,unsigned int p,kernel_long_t start)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
qualify_syscall_name(const char * s,struct number_set * set)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
qualify_syscall(const char * token,struct number_set * set)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
qualify_syscall_tokens(const char * const str,struct number_set * const set)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
qualify_tokens(const char * const str,struct number_set * const set,string_to_uint_func func,const char * const name)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