]> granicus.if.org Git - strace/blob - filter_qualify.c
clone: fix print_tls_arg on x86
[strace] / filter_qualify.c
1 /*
2  * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
3  * Copyright (c) 2016-2019 The strace developers.
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: LGPL-2.1-or-later
7  */
8
9 #include "defs.h"
10 #include "nsig.h"
11 #include "number_set.h"
12 #include "filter.h"
13 #include "delay.h"
14 #include "retval.h"
15 #include "static_assert.h"
16
17 struct number_set *read_set;
18 struct number_set *write_set;
19 struct number_set *signal_set;
20 struct number_set *status_set;
21 struct number_set *trace_set;
22
23 static struct number_set *abbrev_set;
24 static struct number_set *inject_set;
25 static struct number_set *raw_set;
26 static struct number_set *verbose_set;
27
28 /* Only syscall numbers are personality-specific so far.  */
29 struct inject_personality_data {
30         uint16_t scno;
31 };
32
33 static int
34 sigstr_to_uint(const char *s)
35 {
36         if (*s >= '0' && *s <= '9')
37                 return string_to_uint_upto(s, 255);
38
39         if (strncasecmp(s, "SIG", 3) == 0)
40                 s += 3;
41
42         for (int i = 1; i <= 255; ++i) {
43                 const char *name = signame(i);
44
45                 if (!name)
46                         continue;
47
48                 if (strncasecmp(name, "SIG", 3) != 0)
49                         continue;
50
51                 name += 3;
52
53                 if (strcasecmp(name, s) != 0)
54                         continue;
55
56                 return i;
57         }
58
59         return -1;
60 }
61
62 static const char *statuses[] = {
63         "successful",
64         "failed",
65         "unfinished",
66         "unavailable",
67         "detached",
68 };
69 static_assert(ARRAY_SIZE(statuses) == NUMBER_OF_STATUSES,
70               "statuses array and status_t enum mismatch");
71
72 static int
73 statusstr_to_uint(const char *str)
74 {
75         unsigned int i;
76
77         for (i = 0; i < NUMBER_OF_STATUSES; ++i)
78                 if (strcasecmp(str, statuses[i]) == 0)
79                         return i;
80
81         return -1;
82 }
83
84 static int
85 find_errno_by_name(const char *name)
86 {
87         for (unsigned int i = 1; i < nerrnos; ++i) {
88                 if (errnoent[i] && (strcasecmp(name, errnoent[i]) == 0))
89                         return i;
90         }
91
92         return -1;
93 }
94
95 static bool
96 parse_delay_token(const char *input, struct inject_opts *fopts, bool isenter)
97 {
98        unsigned flag = isenter ? INJECT_F_DELAY_ENTER : INJECT_F_DELAY_EXIT;
99
100        if (fopts->data.flags & flag) /* duplicate */
101                return false;
102        struct timespec tsval;
103
104        if (parse_ts(input, &tsval) < 0) /* couldn't parse */
105                return false;
106
107        if (fopts->data.delay_idx == (uint16_t) -1)
108                fopts->data.delay_idx = alloc_delay_data();
109        /* populate .ts_enter or .ts_exit */
110        fill_delay_data(fopts->data.delay_idx, &tsval, isenter);
111        fopts->data.flags |= flag;
112
113        return true;
114 }
115
116 static bool
117 parse_inject_token(const char *const token, struct inject_opts *const fopts,
118                    struct inject_personality_data *const pdata,
119                    const bool fault_tokens_only)
120 {
121         const char *val;
122         int intval;
123
124         if ((val = STR_STRIP_PREFIX(token, "when=")) != token) {
125                 /*
126                  *      == 1+1
127                  * F    == F+0
128                  * F+   == F+1
129                  * F+S
130                  */
131                 char *end;
132                 intval = string_to_uint_ex(val, &end, 0xffff, "+");
133                 if (intval < 1)
134                         return false;
135
136                 fopts->first = intval;
137
138                 if (*end) {
139                         val = end + 1;
140                         if (*val) {
141                                 /* F+S */
142                                 intval = string_to_uint_upto(val, 0xffff);
143                                 if (intval < 1)
144                                         return false;
145                                 fopts->step = intval;
146                         } else {
147                                 /* F+ == F+1 */
148                                 fopts->step = 1;
149                         }
150                 } else {
151                         /* F == F+0 */
152                         fopts->step = 0;
153                 }
154         } else if ((val = STR_STRIP_PREFIX(token, "syscall=")) != token) {
155                 if (fopts->data.flags & INJECT_F_SYSCALL)
156                         return false;
157
158                 for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
159                         kernel_long_t scno = scno_by_name(val, p, 0);
160
161                         if (scno < 0)
162                                 return false;
163
164                         /*
165                          * We want to inject only pure system calls with no side
166                          * effects.
167                          */
168                         if (!(sysent_vec[p][scno].sys_flags & TRACE_PURE))
169                                 return false;
170
171                         pdata[p].scno = scno;
172                 }
173
174                 fopts->data.flags |= INJECT_F_SYSCALL;
175         } else if ((val = STR_STRIP_PREFIX(token, "error=")) != token) {
176                 if (fopts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL))
177                         return false;
178                 intval = string_to_uint_upto(val, MAX_ERRNO_VALUE);
179                 if (intval < 0)
180                         intval = find_errno_by_name(val);
181                 if (intval < 1)
182                         return false;
183                 fopts->data.rval_idx = retval_new(intval);
184                 fopts->data.flags |= INJECT_F_ERROR;
185         } else if (!fault_tokens_only
186                    && (val = STR_STRIP_PREFIX(token, "retval=")) != token) {
187
188                 if (fopts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL))
189                         return false;
190
191                 errno = 0;
192                 char *endp;
193                 unsigned long long ullval = strtoull(val, &endp, 0);
194                 if (endp == val || *endp || (kernel_ulong_t) ullval != ullval
195                     || ((ullval == 0 || ullval == ULLONG_MAX) && errno))
196                         return false;
197
198 #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
199                 bool inadvertent_fault_injection = false;
200 #endif
201
202 #if !HAVE_ARCH_DEDICATED_ERR_REG
203                 if ((kernel_long_t) ullval < 0
204                     && (kernel_long_t) ullval >= -MAX_ERRNO_VALUE) {
205 # if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
206                         inadvertent_fault_injection = true;
207 # endif
208                         error_msg("Inadvertent injection of error %" PRI_kld
209                                   " is possible for retval=%llu",
210                                   -(kernel_long_t) ullval, ullval);
211                 }
212 # if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
213                 else if ((int) ullval < 0 && (int) ullval >= -MAX_ERRNO_VALUE) {
214                         inadvertent_fault_injection = true;
215                         error_msg("Inadvertent injection of error %d is"
216                                   " possible in compat personality for"
217                                   " retval=%llu",
218                                   -(int) ullval, ullval);
219                 }
220 # endif
221 #endif
222
223 #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
224                 if (!inadvertent_fault_injection
225                     && (unsigned int) ullval != ullval) {
226                         error_msg("Injected return value %llu will be"
227                                   " clipped to %u in compat personality",
228                                   ullval, (unsigned int) ullval);
229                 }
230 #endif
231
232                 fopts->data.rval_idx = retval_new(ullval);
233                 fopts->data.flags |= INJECT_F_RETVAL;
234         } else if (!fault_tokens_only
235                    && (val = STR_STRIP_PREFIX(token, "signal=")) != token) {
236                 if (fopts->data.flags & INJECT_F_SIGNAL)
237                         return false;
238                 intval = sigstr_to_uint(val);
239                 if (intval < 1 || intval > NSIG_BYTES * 8)
240                         return false;
241                 fopts->data.signo = intval;
242                 fopts->data.flags |= INJECT_F_SIGNAL;
243         } else if (!fault_tokens_only
244                 && (val = STR_STRIP_PREFIX(token, "delay_enter=")) != token) {
245                 if (!parse_delay_token(val, fopts, true))
246                         return false;
247         } else if (!fault_tokens_only
248                 && (val = STR_STRIP_PREFIX(token, "delay_exit=")) != token) {
249                 if (!parse_delay_token(val, fopts, false))
250                         return false;
251         } else {
252                 return false;
253         }
254
255         return true;
256 }
257
258 static const char *
259 parse_inject_expression(char *const str,
260                         struct inject_opts *const fopts,
261                         struct inject_personality_data *const pdata,
262                         const bool fault_tokens_only)
263 {
264         if (str[0] == '\0' || str[0] == ':')
265                 return "";
266
267         char *saveptr = NULL;
268         const char *name = strtok_r(str, ":", &saveptr);
269
270         char *token;
271         while ((token = strtok_r(NULL, ":", &saveptr))) {
272                 if (!parse_inject_token(token, fopts, pdata, fault_tokens_only))
273                         return NULL;
274         }
275
276         return name;
277 }
278
279 static void
280 qualify_read(const char *const str)
281 {
282         if (!read_set)
283                 read_set = alloc_number_set_array(1);
284         qualify_tokens(str, read_set, string_to_uint, "descriptor");
285 }
286
287 static void
288 qualify_write(const char *const str)
289 {
290         if (!write_set)
291                 write_set = alloc_number_set_array(1);
292         qualify_tokens(str, write_set, string_to_uint, "descriptor");
293 }
294
295 static void
296 qualify_signals(const char *const str)
297 {
298         if (!signal_set)
299                 signal_set = alloc_number_set_array(1);
300         qualify_tokens(str, signal_set, sigstr_to_uint, "signal");
301 }
302
303 static void
304 qualify_status(const char *const str)
305 {
306         if (!status_set)
307                 status_set = alloc_number_set_array(1);
308         qualify_tokens(str, status_set, statusstr_to_uint, "status");
309 }
310
311 static void
312 qualify_trace(const char *const str)
313 {
314         if (!trace_set)
315                 trace_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
316         qualify_syscall_tokens(str, trace_set);
317 }
318
319 static void
320 qualify_abbrev(const char *const str)
321 {
322         if (!abbrev_set)
323                 abbrev_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
324         qualify_syscall_tokens(str, abbrev_set);
325 }
326
327 static void
328 qualify_verbose(const char *const str)
329 {
330         if (!verbose_set)
331                 verbose_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
332         qualify_syscall_tokens(str, verbose_set);
333 }
334
335 static void
336 qualify_raw(const char *const str)
337 {
338         if (!raw_set)
339                 raw_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
340         qualify_syscall_tokens(str, raw_set);
341 }
342
343 static void
344 qualify_inject_common(const char *const str,
345                       const bool fault_tokens_only,
346                       const char *const description)
347 {
348         struct inject_opts opts = {
349                 .first = 1,
350                 .step = 1,
351                 .data = {
352                         .delay_idx = -1
353                 }
354         };
355         struct inject_personality_data pdata[SUPPORTED_PERSONALITIES] = { { 0 } };
356         char *copy = xstrdup(str);
357         const char *name =
358                 parse_inject_expression(copy, &opts, pdata, fault_tokens_only);
359         if (!name)
360                 error_msg_and_die("invalid %s '%s'", description, str);
361
362         struct number_set *tmp_set =
363                 alloc_number_set_array(SUPPORTED_PERSONALITIES);
364         qualify_syscall_tokens(name, tmp_set);
365
366         free(copy);
367
368         /* If neither of retval, error, signal or delay is specified, then ... */
369         if (!(opts.data.flags & INJECT_ACTION_FLAGS)) {
370                 if (fault_tokens_only) {
371                         /* in fault= syntax the default error code is ENOSYS. */
372                         opts.data.rval_idx = retval_new(ENOSYS);
373                         opts.data.flags |= INJECT_F_ERROR;
374                 } else {
375                         /* in inject= syntax this is not allowed. */
376                         error_msg_and_die("invalid %s '%s'", description, str);
377                 }
378         }
379
380         /*
381          * Initialize inject_vec according to tmp_set.
382          * Merge tmp_set into inject_set.
383          */
384         for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
385                 if (number_set_array_is_empty(tmp_set, p))
386                         continue;
387
388                 if (!inject_set) {
389                         inject_set =
390                                 alloc_number_set_array(SUPPORTED_PERSONALITIES);
391                 }
392                 if (!inject_vec[p]) {
393                         inject_vec[p] = xcalloc(nsyscall_vec[p],
394                                                 sizeof(*inject_vec[p]));
395                 }
396
397                 for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
398                         if (is_number_in_set_array(i, tmp_set, p)) {
399                                 add_number_to_set_array(i, inject_set, p);
400                                 inject_vec[p][i] = opts;
401
402                                 /* Copy per-personality data.  */
403                                 inject_vec[p][i].data.scno =
404                                         pdata[p].scno;
405                         }
406                 }
407         }
408
409         free_number_set_array(tmp_set, SUPPORTED_PERSONALITIES);
410 }
411
412 static void
413 qualify_fault(const char *const str)
414 {
415         qualify_inject_common(str, true, "fault argument");
416 }
417
418 static void
419 qualify_inject(const char *const str)
420 {
421         qualify_inject_common(str, false, "inject argument");
422 }
423
424 static void
425 qualify_kvm(const char *const str)
426 {
427         if (strcmp(str, "vcpu") == 0) {
428 #ifdef HAVE_LINUX_KVM_H
429                 if (os_release >= KERNEL_VERSION(4, 16, 0))
430                         kvm_run_structure_decoder_init();
431                 else
432                         error_msg("-e kvm=vcpu option needs"
433                                   " Linux 4.16.0 or higher");
434 #else
435                 error_msg("-e kvm=vcpu option is not implemented"
436                           " for this architecture");
437 #endif
438         } else {
439                 error_msg_and_die("invalid -e kvm= argument: '%s'", str);
440         }
441 }
442
443 static const struct qual_options {
444         const char *name;
445         void (*qualify)(const char *);
446 } qual_options[] = {
447         { "trace",      qualify_trace   },
448         { "t",          qualify_trace   },
449         { "abbrev",     qualify_abbrev  },
450         { "a",          qualify_abbrev  },
451         { "verbose",    qualify_verbose },
452         { "v",          qualify_verbose },
453         { "raw",        qualify_raw     },
454         { "x",          qualify_raw     },
455         { "signal",     qualify_signals },
456         { "signals",    qualify_signals },
457         { "status",     qualify_status  },
458         { "s",          qualify_signals },
459         { "read",       qualify_read    },
460         { "reads",      qualify_read    },
461         { "r",          qualify_read    },
462         { "write",      qualify_write   },
463         { "writes",     qualify_write   },
464         { "w",          qualify_write   },
465         { "fault",      qualify_fault   },
466         { "inject",     qualify_inject  },
467         { "kvm",        qualify_kvm     },
468 };
469
470 void
471 qualify(const char *str)
472 {
473         const struct qual_options *opt = qual_options;
474
475         for (unsigned int i = 0; i < ARRAY_SIZE(qual_options); ++i) {
476                 const char *name = qual_options[i].name;
477                 const size_t len = strlen(name);
478                 const char *val = str_strip_prefix_len(str, name, len);
479
480                 if (val == str || *val != '=')
481                         continue;
482                 str = val + 1;
483                 opt = &qual_options[i];
484                 break;
485         }
486
487         opt->qualify(str);
488 }
489
490 unsigned int
491 qual_flags(const unsigned int scno)
492 {
493         return  (is_number_in_set_array(scno, trace_set, current_personality)
494                    ? QUAL_TRACE : 0)
495                 | (is_number_in_set_array(scno, abbrev_set, current_personality)
496                    ? QUAL_ABBREV : 0)
497                 | (is_number_in_set_array(scno, verbose_set, current_personality)
498                    ? QUAL_VERBOSE : 0)
499                 | (is_number_in_set_array(scno, raw_set, current_personality)
500                    ? QUAL_RAW : 0)
501                 | (is_number_in_set_array(scno, inject_set, current_personality)
502                    ? QUAL_INJECT : 0);
503 }