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