]> granicus.if.org Git - strace/blob - qualify.c
Fix the length argument passed from print_iovec to decode_netlink
[strace] / qualify.c
1 /*
2  * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "defs.h"
29 #include "nsig.h"
30 #include <regex.h>
31
32 typedef unsigned int number_slot_t;
33 #define BITS_PER_SLOT (sizeof(number_slot_t) * 8)
34
35 struct number_set {
36         number_slot_t *vec;
37         unsigned int nslots;
38         bool not;
39 };
40
41 struct number_set read_set;
42 struct number_set write_set;
43 struct number_set signal_set;
44
45 static struct number_set abbrev_set[SUPPORTED_PERSONALITIES];
46 static struct number_set inject_set[SUPPORTED_PERSONALITIES];
47 static struct number_set raw_set[SUPPORTED_PERSONALITIES];
48 static struct number_set trace_set[SUPPORTED_PERSONALITIES];
49 static struct number_set verbose_set[SUPPORTED_PERSONALITIES];
50
51 static void
52 number_setbit(const unsigned int i, number_slot_t *const vec)
53 {
54         vec[i / BITS_PER_SLOT] |= (number_slot_t) 1 << (i % BITS_PER_SLOT);
55 }
56
57 static bool
58 number_isset(const unsigned int i, const number_slot_t *const vec)
59 {
60         return vec[i / BITS_PER_SLOT] & ((number_slot_t) 1 << (i % BITS_PER_SLOT));
61 }
62
63 static void
64 reallocate_number_set(struct number_set *const set, const unsigned int new_nslots)
65 {
66         if (new_nslots <= set->nslots)
67                 return;
68         set->vec = xreallocarray(set->vec, new_nslots, sizeof(*set->vec));
69         memset(set->vec + set->nslots, 0,
70                sizeof(*set->vec) * (new_nslots - set->nslots));
71         set->nslots = new_nslots;
72 }
73
74 static void
75 add_number_to_set(const unsigned int number, struct number_set *const set)
76 {
77         reallocate_number_set(set, number / BITS_PER_SLOT + 1);
78         number_setbit(number, set->vec);
79 }
80
81 bool
82 is_number_in_set(const unsigned int number, const struct number_set *const set)
83 {
84         return ((number / BITS_PER_SLOT < set->nslots)
85                 && number_isset(number, set->vec)) ^ set->not;
86 }
87
88 typedef int (*string_to_uint_func)(const char *);
89
90 /*
91  * Add numbers to SET according to STR specification.
92  */
93 static void
94 qualify_tokens(const char *const str, struct number_set *const set,
95                string_to_uint_func func, const char *const name)
96 {
97         /* Clear the set. */
98         if (set->nslots)
99                 memset(set->vec, 0, sizeof(*set->vec) * set->nslots);
100         set->not = false;
101
102         /*
103          * Each leading ! character means inversion
104          * of the remaining specification.
105          */
106         const char *s = str;
107 handle_inversion:
108         while (*s == '!') {
109                 set->not = !set->not;
110                 ++s;
111         }
112
113         if (strcmp(s, "none") == 0) {
114                 /*
115                  * No numbers are added to the set.
116                  * Subsequent is_number_in_set invocations will return set->not.
117                  */
118                 return;
119         } else if (strcmp(s, "all") == 0) {
120                 s = "!none";
121                 goto handle_inversion;
122         }
123
124         /*
125          * Split the string into comma separated tokens.
126          * For each token, find out the corresponding number
127          * by calling FUNC, and add that number to the set.
128          * The absence of tokens or a negative answer
129          * from FUNC is a fatal error.
130          */
131         char *copy = xstrdup(s);
132         char *saveptr = NULL;
133         const char *token;
134         int number = -1;
135
136         for (token = strtok_r(copy, ",", &saveptr); token;
137              token = strtok_r(NULL, ",", &saveptr)) {
138                 number = func(token);
139                 if (number < 0) {
140                         error_msg_and_die("invalid %s '%s'", name, token);
141                 }
142
143                 add_number_to_set(number, set);
144         }
145
146         free(copy);
147
148         if (number < 0) {
149                 error_msg_and_die("invalid %s '%s'", name, str);
150         }
151 }
152
153 static int
154 sigstr_to_uint(const char *s)
155 {
156         int i;
157
158         if (*s >= '0' && *s <= '9')
159                 return string_to_uint_upto(s, 255);
160
161         if (strncasecmp(s, "SIG", 3) == 0)
162                 s += 3;
163
164         for (i = 0; i <= 255; ++i) {
165                 const char *name = signame(i);
166
167                 if (strncasecmp(name, "SIG", 3) != 0)
168                         continue;
169
170                 name += 3;
171
172                 if (strcasecmp(name, s) != 0)
173                         continue;
174
175                 return i;
176         }
177
178         return -1;
179 }
180
181 static bool
182 qualify_syscall_number(const char *s, struct number_set *set)
183 {
184         int n = string_to_uint(s);
185         if (n < 0)
186                 return false;
187
188         unsigned int p;
189         bool done = false;
190
191         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
192                 if ((unsigned) n >= nsyscall_vec[p]) {
193                         continue;
194                 }
195                 add_number_to_set(n, &set[p]);
196                 done = true;
197         }
198
199         return done;
200 }
201
202 static void
203 regerror_msg_and_die(int errcode, const regex_t *preg,
204                      const char *str, const char *pattern)
205 {
206         char buf[512];
207
208         regerror(errcode, preg, buf, sizeof(buf));
209         error_msg_and_die("%s: %s: %s", str, pattern, buf);
210 }
211
212 static bool
213 qualify_syscall_regex(const char *s, struct number_set *set)
214 {
215         regex_t preg;
216         int rc;
217
218         if ((rc = regcomp(&preg, s, REG_EXTENDED | REG_NOSUB)) != 0)
219                 regerror_msg_and_die(rc, &preg, "regcomp", s);
220
221         unsigned int p;
222         bool found = false;
223         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
224                 unsigned int i;
225
226                 for (i = 0; i < nsyscall_vec[p]; ++i) {
227                         if (!sysent_vec[p][i].sys_name)
228                                 continue;
229                         rc = regexec(&preg, sysent_vec[p][i].sys_name,
230                                      0, NULL, 0);
231                         if (rc == REG_NOMATCH)
232                                 continue;
233                         else if (rc)
234                                 regerror_msg_and_die(rc, &preg, "regexec", s);
235                         add_number_to_set(i, &set[p]);
236                         found = true;
237                 }
238         }
239
240         regfree(&preg);
241         return found;
242 }
243
244 static unsigned int
245 lookup_class(const char *s)
246 {
247         static const struct {
248                 const char *name;
249                 unsigned int value;
250         } syscall_class[] = {
251                 { "desc",       TRACE_DESC      },
252                 { "file",       TRACE_FILE      },
253                 { "memory",     TRACE_MEMORY    },
254                 { "process",    TRACE_PROCESS   },
255                 { "signal",     TRACE_SIGNAL    },
256                 { "ipc",        TRACE_IPC       },
257                 { "network",    TRACE_NETWORK   },
258                 { "%desc",      TRACE_DESC      },
259                 { "%file",      TRACE_FILE      },
260                 { "%memory",    TRACE_MEMORY    },
261                 { "%process",   TRACE_PROCESS   },
262                 { "%signal",    TRACE_SIGNAL    },
263                 { "%ipc",       TRACE_IPC       },
264                 { "%network",   TRACE_NETWORK   },
265                 { "%statfs",    TRACE_STATFS    },
266         };
267
268         unsigned int i;
269         for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
270                 if (strcmp(s, syscall_class[i].name) == 0) {
271                         return syscall_class[i].value;
272                 }
273         }
274
275         return 0;
276 }
277
278 static bool
279 qualify_syscall_class(const char *s, struct number_set *set)
280 {
281         const unsigned int n = lookup_class(s);
282         if (!n)
283                 return false;
284
285         unsigned int p;
286         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
287                 unsigned int i;
288
289                 for (i = 0; i < nsyscall_vec[p]; ++i) {
290                         if (!sysent_vec[p][i].sys_name
291                             || (sysent_vec[p][i].sys_flags & n) != n) {
292                                 continue;
293                         }
294                         add_number_to_set(i, &set[p]);
295                 }
296         }
297
298         return true;
299 }
300
301 static bool
302 qualify_syscall_name(const char *s, struct number_set *set)
303 {
304         unsigned int p;
305         bool found = false;
306
307         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
308                 unsigned int i;
309
310                 for (i = 0; i < nsyscall_vec[p]; ++i) {
311                         if (!sysent_vec[p][i].sys_name
312                             || strcmp(s, sysent_vec[p][i].sys_name)) {
313                                 continue;
314                         }
315                         add_number_to_set(i, &set[p]);
316                         found = true;
317                 }
318         }
319
320         return found;
321 }
322
323 static bool
324 qualify_syscall(const char *token, struct number_set *set)
325 {
326         if (*token >= '0' && *token <= '9')
327                 return qualify_syscall_number(token, set);
328         if (*token == '/')
329                 return qualify_syscall_regex(token + 1, set);
330         return qualify_syscall_class(token, set)
331                || qualify_syscall_name(token, set);
332 }
333
334 /*
335  * Add syscall numbers to SETs for each supported personality
336  * according to STR specification.
337  */
338 static void
339 qualify_syscall_tokens(const char *const str, struct number_set *const set,
340                        const char *const name)
341 {
342         /* Clear all sets. */
343         unsigned int p;
344         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
345                 if (set[p].nslots)
346                         memset(set[p].vec, 0,
347                                sizeof(*set[p].vec) * set[p].nslots);
348                 set[p].not = false;
349         }
350
351         /*
352          * Each leading ! character means inversion
353          * of the remaining specification.
354          */
355         const char *s = str;
356 handle_inversion:
357         while (*s == '!') {
358                 for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
359                         set[p].not = !set[p].not;
360                 }
361                 ++s;
362         }
363
364         if (strcmp(s, "none") == 0) {
365                 /*
366                  * No syscall numbers are added to sets.
367                  * Subsequent is_number_in_set invocations
368                  * will return set[p]->not.
369                  */
370                 return;
371         } else if (strcmp(s, "all") == 0) {
372                 s = "!none";
373                 goto handle_inversion;
374         }
375
376         /*
377          * Split the string into comma separated tokens.
378          * For each token, call qualify_syscall that will take care
379          * if adding appropriate syscall numbers to sets.
380          * The absence of tokens or a negative return code
381          * from qualify_syscall is a fatal error.
382          */
383         char *copy = xstrdup(s);
384         char *saveptr = NULL;
385         const char *token;
386         bool done = false;
387
388         for (token = strtok_r(copy, ",", &saveptr); token;
389              token = strtok_r(NULL, ",", &saveptr)) {
390                 done = qualify_syscall(token, set);
391                 if (!done) {
392                         error_msg_and_die("invalid %s '%s'", name, token);
393                 }
394         }
395
396         free(copy);
397
398         if (!done) {
399                 error_msg_and_die("invalid %s '%s'", name, str);
400         }
401 }
402
403 /*
404  * Returns NULL if STR does not start with PREFIX,
405  * or a pointer to the first char in STR after PREFIX.
406  */
407 static const char *
408 strip_prefix(const char *prefix, const char *str)
409 {
410         size_t len = strlen(prefix);
411
412         return strncmp(prefix, str, len) ? NULL : str + len;
413 }
414
415 static int
416 find_errno_by_name(const char *name)
417 {
418         unsigned int i;
419
420         for (i = 1; i < nerrnos; ++i) {
421                 if (errnoent[i] && (strcasecmp(name, errnoent[i]) == 0))
422                         return i;
423         }
424
425         return -1;
426 }
427
428 static bool
429 parse_inject_token(const char *const token, struct inject_opts *const fopts,
430                    const bool fault_tokens_only)
431 {
432         const char *val;
433         int intval;
434
435         if ((val = strip_prefix("when=", token))) {
436                 /*
437                  *      == 1+1
438                  * F    == F+0
439                  * F+   == F+1
440                  * F+S
441                  */
442                 char *end;
443                 intval = string_to_uint_ex(val, &end, 0xffff, "+");
444                 if (intval < 1)
445                         return false;
446
447                 fopts->first = intval;
448
449                 if (*end) {
450                         val = end + 1;
451                         if (*val) {
452                                 /* F+S */
453                                 intval = string_to_uint_upto(val, 0xffff);
454                                 if (intval < 1)
455                                         return false;
456                                 fopts->step = intval;
457                         } else {
458                                 /* F+ == F+1 */
459                                 fopts->step = 1;
460                         }
461                 } else {
462                         /* F == F+0 */
463                         fopts->step = 0;
464                 }
465         } else if ((val = strip_prefix("error=", token))) {
466                 if (fopts->rval != INJECT_OPTS_RVAL_DEFAULT)
467                         return false;
468                 intval = string_to_uint_upto(val, MAX_ERRNO_VALUE);
469                 if (intval < 0)
470                         intval = find_errno_by_name(val);
471                 if (intval < 1)
472                         return false;
473                 fopts->rval = -intval;
474         } else if (!fault_tokens_only && (val = strip_prefix("retval=", token))) {
475                 if (fopts->rval != INJECT_OPTS_RVAL_DEFAULT)
476                         return false;
477                 intval = string_to_uint(val);
478                 if (intval < 0)
479                         return false;
480                 fopts->rval = intval;
481         } else if (!fault_tokens_only && (val = strip_prefix("signal=", token))) {
482                 intval = sigstr_to_uint(val);
483                 if (intval < 1 || intval > NSIG_BYTES * 8)
484                         return false;
485                 fopts->signo = intval;
486         } else {
487                 return false;
488         }
489
490         return true;
491 }
492
493 static char *
494 parse_inject_expression(const char *const s, char **buf,
495                         struct inject_opts *const fopts,
496                         const bool fault_tokens_only)
497 {
498         char *saveptr = NULL;
499         char *name = NULL;
500         char *token;
501
502         *buf = xstrdup(s);
503         for (token = strtok_r(*buf, ":", &saveptr); token;
504              token = strtok_r(NULL, ":", &saveptr)) {
505                 if (!name)
506                         name = token;
507                 else if (!parse_inject_token(token, fopts, fault_tokens_only))
508                         goto parse_error;
509         }
510
511         if (name)
512                 return name;
513
514 parse_error:
515         free(*buf);
516         return *buf = NULL;
517 }
518
519 static void
520 qualify_read(const char *const str)
521 {
522         qualify_tokens(str, &read_set, string_to_uint, "descriptor");
523 }
524
525 static void
526 qualify_write(const char *const str)
527 {
528         qualify_tokens(str, &write_set, string_to_uint, "descriptor");
529 }
530
531 static void
532 qualify_signals(const char *const str)
533 {
534         qualify_tokens(str, &signal_set, sigstr_to_uint, "signal");
535 }
536
537 static void
538 qualify_trace(const char *const str)
539 {
540         qualify_syscall_tokens(str, trace_set, "system call");
541 }
542
543 static void
544 qualify_abbrev(const char *const str)
545 {
546         qualify_syscall_tokens(str, abbrev_set, "system call");
547 }
548
549 static void
550 qualify_verbose(const char *const str)
551 {
552         qualify_syscall_tokens(str, verbose_set, "system call");
553 }
554
555 static void
556 qualify_raw(const char *const str)
557 {
558         qualify_syscall_tokens(str, raw_set, "system call");
559 }
560
561 static void
562 qualify_inject_common(const char *const str,
563                       const bool fault_tokens_only,
564                       const char *const description)
565 {
566         struct inject_opts opts = {
567                 .first = 1,
568                 .step = 1,
569                 .rval = INJECT_OPTS_RVAL_DEFAULT,
570                 .signo = 0
571         };
572         char *buf = NULL;
573         char *name = parse_inject_expression(str, &buf, &opts, fault_tokens_only);
574         if (!name) {
575                 error_msg_and_die("invalid %s '%s'", description, str);
576         }
577
578         /* If neither of retval, error, or signal is specified, then ... */
579         if (opts.rval == INJECT_OPTS_RVAL_DEFAULT && !opts.signo) {
580                 if (fault_tokens_only) {
581                         /* in fault= syntax the default error code is ENOSYS. */
582                         opts.rval = -ENOSYS;
583                 } else {
584                         /* in inject= syntax this is not allowed. */
585                         error_msg_and_die("invalid %s '%s'", description, str);
586                 }
587         }
588
589         struct number_set tmp_set[SUPPORTED_PERSONALITIES];
590         memset(tmp_set, 0, sizeof(tmp_set));
591         qualify_syscall_tokens(name, tmp_set, description);
592
593         free(buf);
594
595         /*
596          * Initialize inject_vec accourding to tmp_set.
597          * Merge tmp_set into inject_set.
598          */
599         unsigned int p;
600         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
601                 if (!tmp_set[p].nslots && !tmp_set[p].not) {
602                         continue;
603                 }
604
605                 if (!inject_vec[p]) {
606                         inject_vec[p] = xcalloc(nsyscall_vec[p],
607                                                sizeof(*inject_vec[p]));
608                 }
609
610                 unsigned int i;
611                 for (i = 0; i < nsyscall_vec[p]; ++i) {
612                         if (is_number_in_set(i, &tmp_set[p])) {
613                                 add_number_to_set(i, &inject_set[p]);
614                                 inject_vec[p][i] = opts;
615                         }
616                 }
617
618                 free(tmp_set[p].vec);
619         }
620 }
621
622 static void
623 qualify_fault(const char *const str)
624 {
625         qualify_inject_common(str, true, "fault argument");
626 }
627
628 static void
629 qualify_inject(const char *const str)
630 {
631         qualify_inject_common(str, false, "inject argument");
632 }
633
634 static const struct qual_options {
635         const char *name;
636         void (*qualify)(const char *);
637 } qual_options[] = {
638         { "trace",      qualify_trace   },
639         { "t",          qualify_trace   },
640         { "abbrev",     qualify_abbrev  },
641         { "a",          qualify_abbrev  },
642         { "verbose",    qualify_verbose },
643         { "v",          qualify_verbose },
644         { "raw",        qualify_raw     },
645         { "x",          qualify_raw     },
646         { "signal",     qualify_signals },
647         { "signals",    qualify_signals },
648         { "s",          qualify_signals },
649         { "read",       qualify_read    },
650         { "reads",      qualify_read    },
651         { "r",          qualify_read    },
652         { "write",      qualify_write   },
653         { "writes",     qualify_write   },
654         { "w",          qualify_write   },
655         { "fault",      qualify_fault   },
656         { "inject",     qualify_inject  },
657 };
658
659 void
660 qualify(const char *str)
661 {
662         const struct qual_options *opt = qual_options;
663         unsigned int i;
664
665         for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
666                 const char *p = qual_options[i].name;
667                 unsigned int len = strlen(p);
668
669                 if (strncmp(str, p, len) || str[len] != '=')
670                         continue;
671
672                 opt = &qual_options[i];
673                 str += len + 1;
674                 break;
675         }
676
677         opt->qualify(str);
678 }
679
680 unsigned int
681 qual_flags(const unsigned int scno)
682 {
683         return  (is_number_in_set(scno, &trace_set[current_personality])
684                    ? QUAL_TRACE : 0)
685                 | (is_number_in_set(scno, &abbrev_set[current_personality])
686                    ? QUAL_ABBREV : 0)
687                 | (is_number_in_set(scno, &verbose_set[current_personality])
688                    ? QUAL_VERBOSE : 0)
689                 | (is_number_in_set(scno, &raw_set[current_personality])
690                    ? QUAL_RAW : 0)
691                 | (is_number_in_set(scno, &inject_set[current_personality])
692                    ? QUAL_INJECT : 0);
693 }