1 /*
2 * Copyright (c) 2016 Dmitry V. Levin <ldv@strace.io>
3 * Copyright (c) 2016-2023 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 "poke.h"
15 #include "retval.h"
16 #include "static_assert.h"
17 #include "secontext.h"
18
19 struct number_set *read_set;
20 struct number_set *write_set;
21 struct number_set *signal_set;
22 struct number_set *status_set;
23 struct number_set *quiet_set;
24 struct number_set *decode_fd_set;
25 struct number_set *decode_pid_set;
26 struct number_set *trace_set;
27 struct number_set *trace_fd_set;
28
29 bool quiet_set_updated = false;
30 bool decode_fd_set_updated = false;
31
32 static struct number_set *abbrev_set;
33 static struct number_set *inject_set;
34 static struct number_set *raw_set;
35 static struct number_set *verbose_set;
36
37 /* Only syscall numbers are personality-specific so far. */
38 struct inject_personality_data {
39 uint16_t scno;
40 };
41
42 static int
43 sigstr_to_uint(const char *s)
44 {
45 if (*s >= '0' && *s <= '9')
46 return string_to_uint_upto(s, 255);
47
48 if (strncasecmp(s, "SIG", 3) == 0)
49 s += 3;
50
51 for (int i = 1; i <= 255; ++i) {
52 const char *name = signame(i);
53
54 if (!name)
55 continue;
56
57 if (strncasecmp(name, "SIG", 3) != 0)
58 continue;
59
60 name += 3;
61
62 if (strcasecmp(name, s) != 0)
63 continue;
64
65 return i;
66 }
67
68 return -1;
69 }
70
71 static int
72 statusstr_to_uint(const char *str)
73 {
74 static const struct xlat_data statuses[] = {
75 { STATUS_SUCCESSFUL, "successful" },
76 { STATUS_FAILED, "failed" },
77 { STATUS_UNFINISHED, "unfinished" },
78 { STATUS_UNAVAILABLE, "unavailable" },
79 { STATUS_DETACHED, "detached" },
80 };
81
82 return (int) find_arg_val(str, statuses, -1ULL, -1ULL);
83 }
84
85 static int
86 quietstr_to_uint(const char *str)
87 {
88 static const struct xlat_data quiet_strs[] = {
89 { QUIET_ATTACH, "attach" },
90 { QUIET_EXIT, "exit" },
91 { QUIET_EXIT, "exits" },
92 { QUIET_PATH_RESOLVE, "path-resolution" },
93 { QUIET_PERSONALITY, "personality" },
94 { QUIET_THREAD_EXECVE, "superseded" },
95 { QUIET_THREAD_EXECVE, "thread-execve" },
96 };
97
98 return (int) find_arg_val(str, quiet_strs, -1ULL, -1ULL);
99 }
100
101 static int
102 decode_fd_str_to_uint(const char *str)
103 {
104 static const struct xlat_data decode_fd_strs[] = {
105 { DECODE_FD_PATH, "path" },
106 { DECODE_FD_SOCKET, "socket" },
107 { DECODE_FD_DEV, "dev" },
108 { DECODE_FD_PIDFD, "pidfd" },
109 { DECODE_FD_SIGNALFD, "signalfd" },
110 };
111
112 return (int) find_arg_val(str, decode_fd_strs, -1ULL, -1ULL);
113 }
114
115 static int
116 decode_pid_str_to_uint(const char *str)
117 {
118 static const struct xlat_data decode_pid_strs[] = {
119 { DECODE_PID_NS_TRANSLATION, "pidns" },
120 { DECODE_PID_COMM, "comm" },
121 };
122
123 return (int) find_arg_val(str, decode_pid_strs, -1ULL, -1ULL);
124 }
125
126 static int
127 find_errno_by_name(const char *name)
128 {
129 for (unsigned int i = 1; i < nerrnos; ++i) {
130 if (errnoent[i] && (strcasecmp(name, errnoent[i]) == 0))
131 return i;
132 }
133
134 return -1;
135 }
136
137 static bool
138 parse_delay_token(const char *input, struct inject_opts *fopts, bool isenter)
139 {
140 unsigned flag = isenter ? INJECT_F_DELAY_ENTER : INJECT_F_DELAY_EXIT;
141
142 if (fopts->data.flags & flag) /* duplicate */
143 return false;
144 struct timespec tsval;
145
146 if (parse_ts(input, &tsval) < 0) /* couldn't parse */
147 return false;
148
149 if (fopts->data.delay_idx == (uint16_t) -1)
150 fopts->data.delay_idx = alloc_delay_data();
151 /* populate .ts_enter or .ts_exit */
152 fill_delay_data(fopts->data.delay_idx, &tsval, isenter);
153 fopts->data.flags |= flag;
154
155 return true;
156 }
157
158 static bool
159 parse_poke_token(const char *input, struct inject_opts *fopts, bool isenter)
160 {
161 unsigned flag = isenter ? INJECT_F_POKE_ENTER : INJECT_F_POKE_EXIT;
162
163 /* disallow duplicates */
164 if (fopts->data.flags & flag)
165 return false;
166
167 /* empty strings */
168 if (input[0] == '\0')
169 return false;
170
171 if (fopts->data.poke_idx == (uint16_t) -1)
172 fopts->data.poke_idx = alloc_poke_data();
173
174 char *saveptr = NULL;
175 char *str_tokenized = xstrdup(input);
176 struct poke_payload *poke;
177 for (char *token = strtok_r(str_tokenized, ",", &saveptr);
178 token;
179 token = strtok_r(NULL, ",", &saveptr)) {
180 poke = xcalloc(1, sizeof(*poke));
181 poke->is_enter = isenter;
182
183 const char *val = STR_STRIP_PREFIX(token, "@arg");
184 if (val == token)
185 goto err;
186 if ((val[0] >= '1') && (val[0] <= '7')) {
187 poke->arg_no = val[0] - '0';
188 } else {
189 goto err;
190 }
191 if (val[1] != '=')
192 goto err;
193 val += 2;
194
195 size_t data_len = strlen(val);
196 if ((data_len == 0) || (data_len % 2) || (data_len > 2048))
197 goto err;
198 data_len /= 2;
199 poke->data_len = data_len;
200 poke->data = xmalloc(data_len);
201
202 for (size_t i = 0; i < data_len; i++)
203 if (sscanf(&val[2 * i], "%2hhx", &poke->data[i]) != 1)
204 goto err;
205
206 if (poke_add(fopts->data.poke_idx, poke))
207 goto err;
208 }
209 free(str_tokenized);
210
211 fopts->data.flags |= flag;
212 return true;
213
214 err:
215 free(poke->data);
216 free(poke);
217 free(str_tokenized);
218 return false;
219 }
220
221 static void
222 check_inadvertent_fault_injection(unsigned long long ullval)
223 {
224 #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
225 bool inadvertent_fault_injection = false;
226 #endif
227
228 #if !HAVE_ARCH_DEDICATED_ERR_REG
229 if ((kernel_long_t) ullval < 0
230 && (kernel_long_t) ullval >= -MAX_ERRNO_VALUE) {
231 # if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
232 inadvertent_fault_injection = true;
233 # endif
234 error_msg("Inadvertent injection of error %" PRI_kld
235 " is possible for retval=%llu",
236 -(kernel_long_t) ullval, ullval);
237 }
238 # if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
239 else if ((int) ullval < 0 && (int) ullval >= -MAX_ERRNO_VALUE) {
240 inadvertent_fault_injection = true;
241 error_msg("Inadvertent injection of error %d is possible"
242 " in compat personality for retval=%llu",
243 -(int) ullval, ullval);
244 }
245 # endif
246 #endif
247
248 #if ANY_WORDSIZE_LESS_THAN_KERNEL_LONG
249 if (!inadvertent_fault_injection
250 && (unsigned int) ullval != ullval) {
251 error_msg("Injected return value %llu will be"
252 " clipped to %u in compat personality",
253 ullval, (unsigned int) ullval);
254 }
255 #endif
256 }
257
258 static bool
259 parse_inject_token(const char *const token, struct inject_opts *const fopts,
260 struct inject_personality_data *const pdata,
261 const bool fault_tokens_only)
262 {
263 const char *val;
264 int intval;
265
266 if ((val = STR_STRIP_PREFIX(token, "when=")) != token) {
267 /*
268 * == 1..INF+1
269 * F == F..INF+0
270 * F+ == F..INF+1
271 * F+S == F..INF+S
272 * F..L == F..L+1
273 * F..L+S
274 */
275 char *end;
276 intval = string_to_uint_ex(val, &end, 0xffff, "+.");
277 if (intval < 1)
278 return false;
279
280 fopts->first = intval;
281
282 if (end[0] == '.') {
283 if (end[1] != '.')
284 return false;
285 /*
286 * F..L
287 * F..L+S
288 */
289 val = end + 2;
290 intval = string_to_uint_ex(val, &end, 0xffff, "+");
291 if (intval < fopts->first || intval == INJECT_LAST_INF)
292 return false;
293 fopts->last = intval;
294 } else {
295 /*
296 * F == F..INF+0
297 * F+ == F..INF+1
298 * F+S == F..INF+S
299 */
300 fopts->last = INJECT_LAST_INF;
301 }
302
303 if (end[0] != '\0') {
304 val = end + 1;
305 if (val[0] != '\0') {
306 /*
307 * F+S == F..INF+S
308 * F..L+S
309 */
310 intval = string_to_uint_upto(val, 0xffff);
311 if (intval < 1)
312 return false;
313 fopts->step = intval;
314 } else {
315 /*
316 * F+ == F..INF+1
317 * F..L+ == F..L+1
318 */
319 fopts->step = 1;
320 }
321 } else {
322 if (fopts->last == INJECT_LAST_INF) {
323 /* F == F..INF+0 */
324 fopts->step = 0;
325 } else {
326 /* F..L == F..L+1 */
327 fopts->step = 1;
328 }
329 }
330 } else if ((val = STR_STRIP_PREFIX(token, "syscall=")) != token) {
331 if (fopts->data.flags & INJECT_F_SYSCALL)
332 return false;
333
334 for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
335 kernel_long_t scno = scno_by_name(val, p, 0);
336
337 if (scno < 0)
338 return false;
339
340 /*
341 * We want to inject only pure system calls with no side
342 * effects.
343 */
344 if (!(sysent_vec[p][scno].sys_flags & TRACE_PURE))
345 return false;
346
347 pdata[p].scno = scno;
348 }
349
350 fopts->data.flags |= INJECT_F_SYSCALL;
351 } else if ((val = STR_STRIP_PREFIX(token, "error=")) != token) {
352 if (fopts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL))
353 return false;
354 intval = string_to_uint_upto(val, MAX_ERRNO_VALUE);
355 if (intval < 0)
356 intval = find_errno_by_name(val);
357 if (intval < 1)
358 return false;
359 fopts->data.rval_idx = retval_new(intval);
360 fopts->data.flags |= INJECT_F_ERROR;
361 } else if (!fault_tokens_only
362 && (val = STR_STRIP_PREFIX(token, "retval=")) != token) {
363
364 if (fopts->data.flags & (INJECT_F_ERROR | INJECT_F_RETVAL))
365 return false;
366
367 errno = 0;
368 char *endp;
369 unsigned long long ullval = strtoull(val, &endp, 0);
370 if (endp == val || *endp || (kernel_ulong_t) ullval != ullval
371 || ((ullval == 0 || ullval == ULLONG_MAX) && errno))
372 return false;
373
374 check_inadvertent_fault_injection(ullval);
375 fopts->data.rval_idx = retval_new(ullval);
376 fopts->data.flags |= INJECT_F_RETVAL;
377 } else if (!fault_tokens_only
378 && (val = STR_STRIP_PREFIX(token, "signal=")) != token) {
379 if (fopts->data.flags & INJECT_F_SIGNAL)
380 return false;
381 intval = sigstr_to_uint(val);
382 if (intval < 1 || intval > NSIG_BYTES * 8)
383 return false;
384 fopts->data.signo = intval;
385 fopts->data.flags |= INJECT_F_SIGNAL;
386 } else if (!fault_tokens_only
387 && (val = STR_STRIP_PREFIX(token, "poke_enter=")) != token) {
388 if (!parse_poke_token(val, fopts, true))
389 return false;
390 } else if (!fault_tokens_only
391 && (val = STR_STRIP_PREFIX(token, "poke_exit=")) != token) {
392 if (!parse_poke_token(val, fopts, false))
393 return false;
394 } else if (!fault_tokens_only
395 && (val = STR_STRIP_PREFIX(token, "delay_enter=")) != token) {
396 if (!parse_delay_token(val, fopts, true))
397 return false;
398 } else if (!fault_tokens_only
399 && (val = STR_STRIP_PREFIX(token, "delay_exit=")) != token) {
400 if (!parse_delay_token(val, fopts, false))
401 return false;
402 } else {
403 return false;
404 }
405
406 return true;
407 }
408
409 static const char *
410 parse_inject_expression(char *const str,
411 struct inject_opts *const fopts,
412 struct inject_personality_data *const pdata,
413 const bool fault_tokens_only)
414 {
415 if (str[0] == '\0' || str[0] == ':')
416 return "";
417
418 char *saveptr = NULL;
419 const char *name = strtok_r(str, ":", &saveptr);
420
421 char *token;
422 while ((token = strtok_r(NULL, ":", &saveptr))) {
423 if (!parse_inject_token(token, fopts, pdata, fault_tokens_only))
424 return NULL;
425 }
426
427 return name;
428 }
429
430 void
431 qualify_read(const char *const str)
432 {
433 if (!read_set)
434 read_set = alloc_number_set_array(1);
435 qualify_tokens(str, read_set, string_to_uint, "descriptor");
436 }
437
438 void
439 qualify_write(const char *const str)
440 {
441 if (!write_set)
442 write_set = alloc_number_set_array(1);
443 qualify_tokens(str, write_set, string_to_uint, "descriptor");
444 }
445
446 void
447 qualify_signals(const char *const str)
448 {
449 if (!signal_set)
450 signal_set = alloc_number_set_array(1);
451 qualify_tokens(str, signal_set, sigstr_to_uint, "signal");
452 }
453
454 void
455 qualify_status(const char *const str)
456 {
457 if (!status_set)
458 status_set = alloc_number_set_array(1);
459 qualify_tokens(str, status_set, statusstr_to_uint, "status");
460 }
461
462 void
463 qualify_quiet(const char *const str)
464 {
465 if (!quiet_set)
466 quiet_set = alloc_number_set_array(1);
467 else
468 quiet_set_updated = true;
469 qualify_tokens(str, quiet_set, quietstr_to_uint, "quiet");
470 }
471
472 void
473 qualify_decode_fd(const char *const str)
474 {
475 if (!decode_fd_set)
476 decode_fd_set = alloc_number_set_array(1);
477 else
478 decode_fd_set_updated = true;
479 qualify_tokens(str, decode_fd_set, decode_fd_str_to_uint,
480 "decode-fds");
481 }
482
483 void
484 qualify_decode_pid(const char *const str)
485 {
486 if (!decode_pid_set)
487 decode_pid_set = alloc_number_set_array(1);
488 qualify_tokens(str, decode_pid_set, decode_pid_str_to_uint,
489 "decode-pids");
490 }
491
492 void
493 qualify_trace(const char *const str)
494 {
495 if (!trace_set)
496 trace_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
497 qualify_syscall_tokens(str, trace_set);
498 }
499
500 void
501 qualify_trace_fd(const char *const str)
502 {
503 if (!trace_fd_set)
504 trace_fd_set = alloc_number_set_array(1);
505 qualify_tokens(str, trace_fd_set, string_to_uint, "descriptor");
506 tracing_fds = true;
507 }
508
509 void
510 qualify_abbrev(const char *const str)
511 {
512 if (!abbrev_set)
513 abbrev_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
514 qualify_syscall_tokens(str, abbrev_set);
515 }
516
517 void
518 qualify_verbose(const char *const str)
519 {
520 if (!verbose_set)
521 verbose_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
522 qualify_syscall_tokens(str, verbose_set);
523 }
524
525 void
526 qualify_raw(const char *const str)
527 {
528 if (!raw_set)
529 raw_set = alloc_number_set_array(SUPPORTED_PERSONALITIES);
530 qualify_syscall_tokens(str, raw_set);
531 }
532
533 static void
534 qualify_inject_common(const char *const str,
535 const bool fault_tokens_only,
536 const char *const description)
537 {
538 struct inject_opts opts = {
539 .first = 1,
540 .last = INJECT_LAST_INF,
541 .step = 1,
542 .data = {
543 .delay_idx = -1,
544 .poke_idx = -1
545 }
546 };
547 struct inject_personality_data pdata[SUPPORTED_PERSONALITIES] = { { 0 } };
548 char *copy = xstrdup(str);
549 const char *name =
550 parse_inject_expression(copy, &opts, pdata, fault_tokens_only);
551 if (!name)
552 error_msg_and_die("invalid %s '%s'", description, str);
553
554 struct number_set *tmp_set =
555 alloc_number_set_array(SUPPORTED_PERSONALITIES);
556 qualify_syscall_tokens(name, tmp_set);
557
558 free(copy);
559
560 /* If neither of retval, error, signal or delay is specified, then ... */
561 if (!(opts.data.flags & INJECT_ACTION_FLAGS)) {
562 if (fault_tokens_only) {
563 /* in fault= syntax the default error code is ENOSYS. */
564 opts.data.rval_idx = retval_new(ENOSYS);
565 opts.data.flags |= INJECT_F_ERROR;
566 } else {
567 /* in inject= syntax this is not allowed. */
568 error_msg_and_die("invalid %s '%s'", description, str);
569 }
570 }
571
572 /*
573 * Initialize inject_vec according to tmp_set.
574 * Merge tmp_set into inject_set.
575 */
576 for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
577 if (number_set_array_is_empty(tmp_set, p))
578 continue;
579
580 if (!inject_set) {
581 inject_set =
582 alloc_number_set_array(SUPPORTED_PERSONALITIES);
583 }
584 if (!inject_vec[p]) {
585 inject_vec[p] = xcalloc(nsyscall_vec[p],
586 sizeof(*inject_vec[p]));
587 }
588
589 for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
590 if (is_number_in_set_array(i, tmp_set, p)) {
591 add_number_to_set_array(i, inject_set, p);
592 inject_vec[p][i] = opts;
593
594 /* Copy per-personality data. */
595 inject_vec[p][i].data.scno =
596 pdata[p].scno;
597 }
598 }
599 }
600
601 free_number_set_array(tmp_set, SUPPORTED_PERSONALITIES);
602 }
603
604 void
605 qualify_fault(const char *const str)
606 {
607 qualify_inject_common(str, true, "fault argument");
608 }
609
610 void
611 qualify_inject(const char *const str)
612 {
613 qualify_inject_common(str, false, "inject argument");
614 }
615
616 void
617 qualify_kvm(const char *const str)
618 {
619 if (strcmp(str, "vcpu") == 0) {
620 #ifdef HAVE_LINUX_KVM_H
621 if (os_release >= KERNEL_VERSION(4, 16, 0))
622 kvm_run_structure_decoder_init();
623 else
624 error_msg("-e kvm=vcpu option needs"
625 " Linux 4.16.0 or higher");
626 #else
627 error_msg("-e kvm=vcpu option is not implemented"
628 " for this architecture");
629 #endif
630 } else {
631 error_msg_and_die("invalid -e kvm= argument: '%s'", str);
632 }
633 }
634
635 #ifdef ENABLE_SECONTEXT
636 struct number_set *secontext_set;
637
638 static int
639 secontextstr_to_uint(const char *s)
640 {
641 static const struct xlat_data secontext_strs[] = {
642 { SECONTEXT_FULL, "full" },
643 { SECONTEXT_MISMATCH, "mismatch" },
644 };
645
646 return (int) find_arg_val(s, secontext_strs, -1ULL, -1ULL);
647 }
648 #endif
649
650 void
651 qualify_secontext(const char *const str)
652 {
653 #ifdef ENABLE_SECONTEXT
654 if (!secontext_set)
655 secontext_set = alloc_number_set_array(1);
656 qualify_tokens(str, secontext_set, secontextstr_to_uint, "secontext");
657 #else
658 error_msg_and_die("SELinux context printing (--secontext option) "
659 "is not supported by this build of strace");
660 #endif
661 }
662
663 static const struct qual_options {
664 const char *name;
665 void (*qualify)(const char *);
666 } qual_options[] = {
667 { "trace", qualify_trace },
668 { "t", qualify_trace },
669 { "trace-fd", qualify_trace_fd },
670 { "trace-fds", qualify_trace_fd },
671 { "fd", qualify_trace_fd },
672 { "fds", qualify_trace_fd },
673 { "abbrev", qualify_abbrev },
674 { "a", qualify_abbrev },
675 { "verbose", qualify_verbose },
676 { "v", qualify_verbose },
677 { "raw", qualify_raw },
678 { "x", qualify_raw },
679 { "signal", qualify_signals },
680 { "signals", qualify_signals },
681 { "s", qualify_signals },
682 { "status", qualify_status },
683 { "quiet", qualify_quiet },
684 { "silent", qualify_quiet },
685 { "silence", qualify_quiet },
686 { "q", qualify_quiet },
687 { "read", qualify_read },
688 { "reads", qualify_read },
689 { "r", qualify_read },
690 { "write", qualify_write },
691 { "writes", qualify_write },
692 { "w", qualify_write },
693 { "fault", qualify_fault },
694 { "inject", qualify_inject },
695 { "kvm", qualify_kvm },
696 { "decode-fd", qualify_decode_fd },
697 { "decode-fds", qualify_decode_fd },
698 { "decode-pid", qualify_decode_pid },
699 { "decode-pids", qualify_decode_pid },
700 { "secontext", qualify_secontext },
701 };
702
703 void
704 qualify(const char *str)
705 {
706 const struct qual_options *opt = qual_options;
707
708 for (unsigned int i = 0; i < ARRAY_SIZE(qual_options); ++i) {
709 const char *name = qual_options[i].name;
710 const size_t len = strlen(name);
711 const char *val = str_strip_prefix_len(str, name, len);
712
713 if (val == str || *val != '=')
714 continue;
715 str = val + 1;
716 opt = &qual_options[i];
717 break;
718 }
719
720 opt->qualify(str);
721 }
722
723 unsigned int
724 qual_flags(const unsigned int scno)
725 {
726 return (is_number_in_set_array(scno, trace_set, current_personality)
727 ? QUAL_TRACE : 0)
728 | (is_number_in_set_array(scno, abbrev_set, current_personality)
729 ? QUAL_ABBREV : 0)
730 | (is_number_in_set_array(scno, verbose_set, current_personality)
731 ? QUAL_VERBOSE : 0)
732 | (is_number_in_set_array(scno, raw_set, current_personality)
733 ? QUAL_RAW : 0)
734 | (is_number_in_set_array(scno, inject_set, current_personality)
735 ? QUAL_INJECT : 0);
736 }