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