1 /*
2 * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
3 * Copyright (c) 2016-2022 The strace developers.
4 * All rights reserved.
5 *
6 * SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8
9 #include "defs.h"
10
11 #include <regex.h>
12
13 #include "filter.h"
14 #include "number_set.h"
15 #include "xstring.h"
16
17 /* Personality designators to be used for specifying personality */
18 static const char *const personality_designators[] = PERSONALITY_DESIGNATORS;
19 static_assert(ARRAY_SIZE(personality_designators) == SUPPORTED_PERSONALITIES,
20 "ARRAY_SIZE(personality_designators) != SUPPORTED_PERSONALITIES");
21
22 /**
23 * Checks whether a @-separated personality specification suffix is present.
24 * Personality suffix is a one of strings stored in personality_designators
25 * array.
26 *
27 * @param[in] s Specification string to check.
28 * @param[out] p Where to store personality number if it is found.
29 * @return If personality is found, the provided string is copied without
30 * suffix and returned as a result (callee should de-allocate it
31 * with free() after use), and personality number is written to p.
32 * Otherwise, NULL is returned and p is untouched.
33 */
34 static char *
35 qualify_syscall_separate_personality(const char *s, unsigned int *p)
36 {
37 const char *pos = strrchr(s, '@');
38
39 if (!pos)
40 return NULL;
41
42 for (unsigned int i = 0; i < SUPPORTED_PERSONALITIES; i++) {
43 if (!strcmp(pos + 1, personality_designators[i])) {
44 *p = i;
45 return xstrndup(s, pos - s);
46 }
47 }
48
49 error_msg_and_help("incorrect personality designator '%s'"
50 " in qualification '%s'", pos + 1, s);
51 }
52
53 static bool
54 qualify_syscall_number(const char *str, unsigned int p, struct number_set *set)
55 {
56 unsigned int nr = string_to_uint(str);
57 unsigned int scno = shuffle_scno_pers(nr, p);
58 if (!scno_pers_is_valid(scno, p)) {
59 if (ARCH_NEEDS_NON_SHUFFLED_SCNO_CHECK &&
60 scno_pers_is_valid(nr, p))
61 scno = nr;
62 else
63 return false;
64 }
65
66 add_number_to_set_array(scno, set, p);
67 return true;
68 }
69
70 static void
71 regerror_msg_and_die(int errcode, const regex_t *preg,
72 const char *str, const char *pattern)
73 {
74 char buf[512];
75
76 regerror(errcode, preg, buf, sizeof(buf));
77 error_msg_and_die("%s: %s: %s", str, pattern, buf);
78 }
79
80 static bool
81 qualify_syscall_regex(const char *str, unsigned int p, struct number_set *set)
82 {
83 regex_t preg;
84 int rc;
85
86 if ((rc = regcomp(&preg, str, REG_EXTENDED | REG_NOSUB)) != 0)
87 regerror_msg_and_die(rc, &preg, "regcomp", str);
88
89 bool found = false;
90
91 for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
92 if (!sysent_vec[p][i].sys_name)
93 continue;
94
95 rc = regexec(&preg, sysent_vec[p][i].sys_name,
96 0, NULL, 0);
97 if (rc == REG_NOMATCH)
98 continue;
99 else if (rc)
100 regerror_msg_and_die(rc, &preg, "regexec", str);
101
102 add_number_to_set_array(i, set, p);
103 found = true;
104 }
105
106 regfree(&preg);
107 return found;
108 }
109
110 static bool
111 qualify_syscall_class(const char *str, unsigned int p, struct number_set *set)
112 {
113 static const struct xlat_data syscall_class[] = {
114 { TRACE_DESC, "%desc" },
115 { TRACE_FILE, "%file" },
116 { TRACE_MEMORY, "%memory" },
117 { TRACE_PROCESS, "%process" },
118 { TRACE_CREDS, "%creds" },
119 { TRACE_SIGNAL, "%signal" },
120 { TRACE_IPC, "%ipc" },
121 { TRACE_NETWORK, "%net" },
122 { TRACE_NETWORK, "%network" },
123 { TRACE_STAT, "%stat" },
124 { TRACE_LSTAT, "%lstat" },
125 { TRACE_FSTAT, "%fstat" },
126 { TRACE_STAT_LIKE, "%%stat" },
127 { TRACE_STATFS, "%statfs" },
128 { TRACE_FSTATFS, "%fstatfs" },
129 { TRACE_STATFS_LIKE, "%%statfs" },
130 { TRACE_PURE, "%pure" },
131 { TRACE_CLOCK, "%clock" },
132 /* legacy class names */
133 { 0, "all" },
134 { TRACE_DESC, "desc" },
135 { TRACE_FILE, "file" },
136 { TRACE_MEMORY, "memory" },
137 { TRACE_PROCESS, "process" },
138 { TRACE_SIGNAL, "signal" },
139 { TRACE_IPC, "ipc" },
140 { TRACE_NETWORK, "network" },
141 };
142 const struct xlat_data *class = find_xlat_val_case(syscall_class, str);
143 if (!class)
144 return false;
145
146 unsigned int n = class->val;
147
148 for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
149 if (sysent_vec[p][i].sys_name &&
150 (sysent_vec[p][i].sys_flags & n) == n)
151 add_number_to_set_array(i, set, p);
152 }
153
154 return true;
155 }
156
157 kernel_long_t
158 scno_by_name(const char *s, unsigned int p, kernel_long_t start)
159 {
160 if (p >= SUPPORTED_PERSONALITIES)
161 return -1;
162
163 for (kernel_ulong_t i = start; i < nsyscall_vec[p]; ++i) {
164 if (sysent_vec[p][i].sys_name &&
165 strcmp(s, sysent_vec[p][i].sys_name) == 0)
166 return i;
167 }
168
169 return -1;
170 }
171
172 static bool
173 qualify_syscall_name(const char *s, unsigned int p, struct number_set *set)
174 {
175 bool found = false;
176
177 for (kernel_long_t scno = 0; (scno = scno_by_name(s, p, scno)) >= 0;
178 ++scno) {
179 add_number_to_set_array(scno, set, p);
180 found = true;
181 }
182
183 return found;
184 }
185
186 static bool
187 qualify_syscall_pers(const char *token, unsigned int p, struct number_set *set)
188 {
189 if (*token >= '0' && *token <= '9')
190 return qualify_syscall_number(token, p, set);
191 if (*token == '/')
192 return qualify_syscall_regex(token + 1, p, set);
193 return qualify_syscall_class(token, p, set)
194 || qualify_syscall_name(token, p, set);
195 }
196
197 static bool
198 qualify_syscall(const char *token, struct number_set *set)
199 {
200 bool rc = false;
201 while (*token == '?') {
202 ++token;
203 rc = true;
204 }
205
206 unsigned int p;
207 char *str = qualify_syscall_separate_personality(token, &p);
208 if (str) {
209 rc |= qualify_syscall_pers(str, p, set);
210 free(str);
211 } else {
212 for (p = 0; p < SUPPORTED_PERSONALITIES; ++p)
213 rc |= qualify_syscall_pers(token, p, set);
214 }
215
216 return rc;
217 }
218
219 /*
220 * Add syscall numbers to SETs for each supported personality
221 * according to STR specification.
222 */
223 void
224 qualify_syscall_tokens(const char *const str, struct number_set *const set)
225 {
226 /* Clear all sets. */
227 clear_number_set_array(set, SUPPORTED_PERSONALITIES);
228
229 /*
230 * Each leading ! character means inversion
231 * of the remaining specification.
232 */
233 const char *s = str;
234 while (*s == '!') {
235 invert_number_set_array(set, SUPPORTED_PERSONALITIES);
236 ++s;
237 }
238
239 if (strcmp(s, "none") == 0) {
240 /*
241 * No syscall numbers are added to sets.
242 * Subsequent is_number_in_set* invocations
243 * will return set[p]->not.
244 */
245 return;
246 } else if (strcmp(s, "all") == 0) {
247 /* "all" == "!none" */
248 invert_number_set_array(set, SUPPORTED_PERSONALITIES);
249 return;
250 }
251
252 /*
253 * Split the string into comma separated tokens.
254 * For each token, call qualify_syscall that will take care
255 * if adding appropriate syscall numbers to sets.
256 * The absence of tokens or a negative return code
257 * from qualify_syscall is a fatal error.
258 */
259 char *copy = xstrdup(s);
260 char *saveptr = NULL;
261 bool done = false;
262
263 for (const char *token = strtok_r(copy, ",", &saveptr);
264 token; token = strtok_r(NULL, ",", &saveptr)) {
265 done = qualify_syscall(token, set);
266 if (!done)
267 error_msg_and_die("invalid system call '%s'", token);
268 }
269
270 free(copy);
271
272 if (!done)
273 error_msg_and_die("invalid system call '%s'", str);
274 }
275
276 /*
277 * Add numbers to SET according to STR specification.
278 */
279 void
280 qualify_tokens(const char *const str, struct number_set *const set,
281 string_to_uint_func func, const char *const name)
282 {
283 /* Clear the set. */
284 clear_number_set_array(set, 1);
285
286 /*
287 * Each leading ! character means inversion
288 * of the remaining specification.
289 */
290 const char *s = str;
291 while (*s == '!') {
292 invert_number_set_array(set, 1);
293 ++s;
294 }
295
296 if (strcmp(s, "none") == 0) {
297 /*
298 * No numbers are added to the set.
299 * Subsequent is_number_in_set* invocations
300 * will return set->not.
301 */
302 return;
303 } else if (strcmp(s, "all") == 0) {
304 /* "all" == "!none" */
305 invert_number_set_array(set, 1);
306 return;
307 }
308
309 /*
310 * Split the string into comma separated tokens.
311 * For each token, find out the corresponding number
312 * by calling FUNC, and add that number to the set.
313 * The absence of tokens or a negative answer
314 * from FUNC is a fatal error.
315 */
316 char *copy = xstrdup(s);
317 char *saveptr = NULL;
318 int number = -1;
319
320 for (const char *token = strtok_r(copy, ",", &saveptr);
321 token; token = strtok_r(NULL, ",", &saveptr)) {
322 number = func(token);
323 if (number < 0)
324 error_msg_and_die("invalid %s '%s'", name, token);
325
326 add_number_to_set(number, set);
327 }
328
329 free(copy);
330
331 if (number < 0)
332 error_msg_and_die("invalid %s '%s'", name, str);
333 }