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