1/* $OpenBSD: subr_kubsan.c,v 1.12 2019/11/06 19:16:48 anton Exp $ */ 2 3/* 4 * Copyright (c) 2019 Anton Lindqvist <anton@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/types.h> 20#include <sys/param.h> 21#include <sys/atomic.h> 22#include <sys/syslimits.h> 23#include <sys/systm.h> 24#include <sys/timeout.h> 25 26#include <uvm/uvm_extern.h> 27 28#define KUBSAN_INTERVAL 100 /* report interval in msec */ 29#define KUBSAN_NSLOTS 32 30 31#define NUMBER_BUFSIZ 32 32#define LOCATION_BUFSIZ (PATH_MAX + 32) /* filename:line:column */ 33#define LOCATION_REPORTED (1U << 31) 34 35#define NBITS(typ) (1 << ((typ)->t_info >> 1)) 36#define SIGNED(typ) ((typ)->t_info & 1) 37 38struct kubsan_report { 39 enum { 40 KUBSAN_FLOAT_CAST_OVERFLOW, 41 KUBSAN_INVALID_VALUE, 42 KUBSAN_NEGATE_OVERFLOW, 43 KUBSAN_NONNULL_ARG, 44 KUBSAN_OUT_OF_BOUNDS, 45 KUBSAN_OVERFLOW, 46 KUBSAN_POINTER_OVERFLOW, 47 KUBSAN_SHIFT_OUT_OF_BOUNDS, 48 KUBSAN_TYPE_MISMATCH, 49 KUBSAN_UNREACHABLE, 50 } kr_type; 51 52 struct source_location *kr_src; 53 54 union { 55 struct { 56 const struct float_cast_overflow_data *v_data; 57 unsigned long v_val; 58 } v_float_cast_overflow; 59 60 struct { 61 const struct invalid_value_data *v_data; 62 unsigned long v_val; 63 } v_invalid_value; 64 65 struct { 66 const struct overflow_data *v_data; 67 unsigned int v_val; 68 } v_negate_overflow; 69 70 struct { 71 const struct nonnull_arg_data *v_data; 72 } v_nonnull_arg; 73 74 struct { 75 const struct out_of_bounds_data *v_data; 76 unsigned int v_idx; 77 } v_out_of_bounds; 78 79 struct { 80 const struct overflow_data *v_data; 81 unsigned long v_lhs; 82 unsigned long v_rhs; 83 char v_op; 84 } v_overflow; 85 86 struct { 87 const struct pointer_overflow_data *v_data; 88 unsigned long v_base; 89 unsigned long v_res; 90 } v_pointer_overflow; 91 92 struct { 93 const struct shift_out_of_bounds_data *v_data; 94 unsigned long v_lhs; 95 unsigned long v_rhs; 96 } v_shift_out_of_bounds; 97 98 struct { 99 const struct type_mismatch_data *v_data; 100 unsigned long v_ptr; 101 } v_type_mismatch; 102 } kr_u; 103}; 104#define kr_float_cast_overflow kr_u.v_float_cast_overflow 105#define kr_invalid_value kr_u.v_invalid_value 106#define kr_negate_overflow kr_u.v_negate_overflow 107#define kr_nonnull_arg kr_u.v_nonnull_arg 108#define kr_out_of_bounds kr_u.v_out_of_bounds 109#define kr_overflow kr_u.v_overflow 110#define kr_pointer_overflow kr_u.v_pointer_overflow 111#define kr_shift_out_of_bounds kr_u.v_shift_out_of_bounds 112#define kr_type_mismatch kr_u.v_type_mismatch 113 114struct type_descriptor { 115 uint16_t t_kind; 116 uint16_t t_info; 117 char t_name[1]; /* type name as variable length array */ 118}; 119 120struct source_location { 121 const char *sl_filename; 122 uint32_t sl_line; 123 uint32_t sl_column; 124}; 125 126struct float_cast_overflow_data { 127 struct source_location d_src; 128 struct type_descriptor *d_ftype; /* from type */ 129 struct type_descriptor *d_ttype; /* to type */ 130}; 131 132struct invalid_value_data { 133 struct source_location d_src; 134 struct type_descriptor *d_type; 135}; 136 137struct nonnull_arg_data { 138 struct source_location d_src; 139 struct source_location d_attr_src; /* __attribute__ location */ 140 int d_idx; 141}; 142 143struct out_of_bounds_data { 144 struct source_location d_src; 145 struct type_descriptor *d_atype; /* array type */ 146 struct type_descriptor *d_itype; /* index type */ 147}; 148 149struct overflow_data { 150 struct source_location d_src; 151 struct type_descriptor *d_type; 152}; 153 154struct pointer_overflow_data { 155 struct source_location d_src; 156}; 157 158struct shift_out_of_bounds_data { 159 struct source_location d_src; 160 struct type_descriptor *d_ltype; 161 struct type_descriptor *d_rtype; 162}; 163 164struct type_mismatch_data { 165 struct source_location d_src; 166 struct type_descriptor *d_type; 167 uint8_t d_align; /* log2 alignment */ 168 uint8_t d_kind; 169}; 170 171struct unreachable_data { 172 struct source_location d_src; 173}; 174 175int64_t kubsan_deserialize_int(struct type_descriptor *, 176 unsigned long); 177uint64_t kubsan_deserialize_uint(struct type_descriptor *, 178 unsigned long); 179void kubsan_defer_report(struct kubsan_report *); 180void kubsan_format_int(struct type_descriptor *, unsigned long, 181 char *, size_t); 182int kubsan_format_location(const struct source_location *, char *, 183 size_t); 184int kubsan_is_reported(struct source_location *); 185const char *kubsan_kind(uint8_t); 186void kubsan_report(void *); 187void kubsan_unreport(struct source_location *); 188 189static int is_negative(struct type_descriptor *, unsigned long); 190static int is_shift_exponent_too_large(struct type_descriptor *, 191 unsigned long); 192 193static const char *pathstrip(const char *); 194 195#ifdef KUBSAN_WATCH 196int kubsan_watch = 2; 197#else 198int kubsan_watch = 1; 199#endif 200 201struct kubsan_report *kubsan_reports = NULL; 202struct timeout kubsan_timo = TIMEOUT_INITIALIZER(kubsan_report, NULL); 203unsigned int kubsan_slot = 0; 204int kubsan_cold = 1; 205 206/* 207 * Compiling the kernel with `-fsanitize=undefined' will cause the following 208 * functions to be called when a sanitizer detects undefined behavior. 209 * Some sanitizers are omitted since they are only applicable to C++. 210 * 211 * Every __ubsan_*() sanitizer function also has a corresponding 212 * __ubsan_*_abort() function as part of the ABI provided by Clang. 213 * But, since the kernel never is compiled with `fno-sanitize-recover' for 214 * obvious reasons, they are also omitted. 215 */ 216 217void 218__ubsan_handle_add_overflow(struct overflow_data *data, 219 unsigned long lhs, unsigned long rhs) 220{ 221 struct kubsan_report kr = { 222 .kr_type = KUBSAN_OVERFLOW, 223 .kr_src = &data->d_src, 224 .kr_overflow = { data, lhs, rhs, '+' }, 225 }; 226 227 kubsan_defer_report(&kr); 228} 229 230void 231__ubsan_handle_builtin_unreachable(struct unreachable_data *data) 232{ 233 struct kubsan_report kr = { 234 .kr_type = KUBSAN_UNREACHABLE, 235 .kr_src = &data->d_src, 236 }; 237 238 kubsan_defer_report(&kr); 239} 240 241void 242__ubsan_handle_divrem_overflow(struct overflow_data *data, 243 unsigned long lhs, unsigned long rhs) 244{ 245 struct kubsan_report kr = { 246 .kr_type = KUBSAN_OVERFLOW, 247 .kr_src = &data->d_src, 248 .kr_overflow = { data, lhs, rhs, '/' }, 249 }; 250 251 kubsan_defer_report(&kr); 252} 253 254void 255__ubsan_handle_float_cast_overflow(struct float_cast_overflow_data *data, 256 unsigned long val) 257{ 258 struct kubsan_report kr = { 259 .kr_type = KUBSAN_FLOAT_CAST_OVERFLOW, 260 .kr_src = &data->d_src, 261 .kr_float_cast_overflow = { data, val }, 262 }; 263 264 kubsan_defer_report(&kr); 265} 266 267void 268__ubsan_handle_load_invalid_value(struct invalid_value_data *data, 269 unsigned long val) 270{ 271 struct kubsan_report kr = { 272 .kr_type = KUBSAN_INVALID_VALUE, 273 .kr_src = &data->d_src, 274 .kr_invalid_value = { data, val }, 275 }; 276 277 kubsan_defer_report(&kr); 278} 279 280void 281__ubsan_handle_nonnull_arg(struct nonnull_arg_data *data) 282{ 283 struct kubsan_report kr = { 284 .kr_type = KUBSAN_NONNULL_ARG, 285 .kr_src = &data->d_src, 286 .kr_nonnull_arg = { data }, 287 }; 288 289 kubsan_defer_report(&kr); 290} 291 292void 293__ubsan_handle_mul_overflow(struct overflow_data *data, 294 unsigned long lhs, unsigned long rhs) 295{ 296 struct kubsan_report kr = { 297 .kr_type = KUBSAN_OVERFLOW, 298 .kr_src = &data->d_src, 299 .kr_overflow = { data, lhs, rhs, '*' }, 300 }; 301 302 kubsan_defer_report(&kr); 303} 304 305void 306__ubsan_handle_negate_overflow(struct overflow_data *data, unsigned long val) 307{ 308 struct kubsan_report kr = { 309 .kr_type = KUBSAN_NEGATE_OVERFLOW, 310 .kr_src = &data->d_src, 311 .kr_negate_overflow = { data, val }, 312 }; 313 314 kubsan_defer_report(&kr); 315} 316 317void 318__ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, 319 unsigned long idx) 320{ 321 struct kubsan_report kr = { 322 .kr_type = KUBSAN_OUT_OF_BOUNDS, 323 .kr_src = &data->d_src, 324 .kr_out_of_bounds = { data, idx }, 325 }; 326 327 kubsan_defer_report(&kr); 328} 329 330void 331__ubsan_handle_pointer_overflow(struct pointer_overflow_data *data, 332 unsigned long base, unsigned long res) 333{ 334 struct kubsan_report kr = { 335 .kr_type = KUBSAN_POINTER_OVERFLOW, 336 .kr_src = &data->d_src, 337 .kr_pointer_overflow = { data, base, res }, 338 }; 339 340 kubsan_defer_report(&kr); 341} 342 343void 344__ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data, 345 unsigned long lhs, unsigned long rhs) 346{ 347 struct kubsan_report kr = { 348 .kr_type = KUBSAN_SHIFT_OUT_OF_BOUNDS, 349 .kr_src = &data->d_src, 350 .kr_shift_out_of_bounds = { data, lhs, rhs }, 351 }; 352 353 kubsan_defer_report(&kr); 354} 355 356void 357__ubsan_handle_sub_overflow(struct overflow_data *data, 358 unsigned long lhs, unsigned long rhs) 359{ 360 struct kubsan_report kr = { 361 .kr_type = KUBSAN_OVERFLOW, 362 .kr_src = &data->d_src, 363 .kr_overflow = { data, lhs, rhs, '-' }, 364 }; 365 366 kubsan_defer_report(&kr); 367} 368 369void 370__ubsan_handle_type_mismatch_v1(struct type_mismatch_data *data, 371 unsigned long ptr) 372{ 373 struct kubsan_report kr = { 374 .kr_type = KUBSAN_TYPE_MISMATCH, 375 .kr_src = &data->d_src, 376 .kr_type_mismatch = { data, ptr }, 377 }; 378 379 kubsan_defer_report(&kr); 380} 381 382/* 383 * Allocate storage for reports and schedule the reporter. 384 * Must be called as early on as possible in order to catch undefined behavior 385 * during boot. 386 */ 387void 388kubsan_init(void) 389{ 390 kubsan_reports = (void *)uvm_pageboot_alloc( 391 sizeof(struct kubsan_report) * KUBSAN_NSLOTS); 392 kubsan_cold = 0; 393 394 timeout_add_msec(&kubsan_timo, KUBSAN_INTERVAL); 395} 396 397int64_t 398kubsan_deserialize_int(struct type_descriptor *typ, unsigned long val) 399{ 400 switch (NBITS(typ)) { 401 case 8: 402 return ((int8_t)val); 403 case 16: 404 return ((int16_t)val); 405 case 32: 406 return ((int32_t)val); 407 case 64: 408 default: 409 return ((int64_t)val); 410 } 411} 412 413uint64_t 414kubsan_deserialize_uint(struct type_descriptor *typ, unsigned long val) 415{ 416 switch (NBITS(typ)) { 417 case 8: 418 return ((uint8_t)val); 419 case 16: 420 return ((uint16_t)val); 421 case 32: 422 return ((uint32_t)val); 423 case 64: 424 default: 425 return ((uint64_t)val); 426 } 427} 428 429void 430kubsan_defer_report(struct kubsan_report *kr) 431{ 432 unsigned int slot; 433 434 if (__predict_false(kubsan_cold == 1) || 435 kubsan_is_reported(kr->kr_src)) 436 return; 437 438 slot = atomic_inc_int_nv(&kubsan_slot) - 1; 439 if (slot >= KUBSAN_NSLOTS) { 440 /* 441 * No slots left, flag source location as not reported and 442 * hope a slot will be available next time. 443 */ 444 kubsan_unreport(kr->kr_src); 445 return; 446 } 447 448 memcpy(&kubsan_reports[slot], kr, sizeof(*kr)); 449} 450 451void 452kubsan_format_int(struct type_descriptor *typ, unsigned long val, 453 char *buf, size_t bufsiz) 454{ 455 switch (typ->t_kind) { 456 case 0: /* integer */ 457 if (SIGNED(typ)) { 458 int64_t i = kubsan_deserialize_int(typ, val); 459 snprintf(buf, bufsiz, "%lld", i); 460 } else { 461 uint64_t u = kubsan_deserialize_uint(typ, val); 462 snprintf(buf, bufsiz, "%llu", u); 463 } 464 break; 465 default: 466 snprintf(buf, bufsiz, "%#x<NaN>", typ->t_kind); 467 } 468} 469 470int 471kubsan_format_location(const struct source_location *src, char *buf, 472 size_t bufsiz) 473{ 474 const char *path; 475 476 path = pathstrip(src->sl_filename); 477 478 return snprintf(buf, bufsiz, "%s:%u:%u", 479 path, src->sl_line & ~LOCATION_REPORTED, src->sl_column); 480} 481 482int 483kubsan_is_reported(struct source_location *src) 484{ 485 uint32_t *line = &src->sl_line; 486 uint32_t prev; 487 488 /* 489 * Treat everything as reported when disabled. 490 * Otherwise, new violations would go by unnoticed. 491 */ 492 if (__predict_false(kubsan_watch == 0)) 493 return (1); 494 495 do { 496 prev = *line; 497 /* If already reported, avoid redundant atomic operation. */ 498 if (prev & LOCATION_REPORTED) 499 break; 500 } while (atomic_cas_uint(line, prev, prev | LOCATION_REPORTED) != prev); 501 502 return (prev & LOCATION_REPORTED); 503} 504 505const char * 506kubsan_kind(uint8_t kind) 507{ 508 static const char *kinds[] = { 509 "load of", 510 "store to", 511 "reference binding to", 512 "member access within", 513 "member call on", 514 "constructor call on", 515 "downcast of", 516 "downcast of", 517 "upcast of", 518 "cast to virtual base of", 519 "_Nonnull binding to", 520 "dynamic operation on" 521 }; 522 523 if (kind >= nitems(kinds)) 524 return ("?"); 525 526 return (kinds[kind]); 527} 528 529void 530kubsan_report(void *arg) 531{ 532 char bloc[LOCATION_BUFSIZ]; 533 char blhs[NUMBER_BUFSIZ]; 534 char brhs[NUMBER_BUFSIZ]; 535 struct kubsan_report *kr; 536 unsigned int nslots; 537 unsigned int i = 0; 538 539again: 540 nslots = kubsan_slot; 541 if (nslots == 0) 542 goto done; 543 if (nslots > KUBSAN_NSLOTS) 544 nslots = KUBSAN_NSLOTS; 545 546 for (; i < nslots; i++) { 547 kr = &kubsan_reports[i]; 548 549 kubsan_format_location(kr->kr_src, bloc, sizeof(bloc)); 550 switch (kr->kr_type) { 551 case KUBSAN_FLOAT_CAST_OVERFLOW: { 552 const struct float_cast_overflow_data *data = 553 kr->kr_float_cast_overflow.v_data; 554 555 kubsan_format_int(data->d_ftype, 556 kr->kr_float_cast_overflow.v_val, 557 blhs, sizeof(blhs)); 558 printf("kubsan: %s: %s of type %s is outside the range " 559 "of representable values of type %s\n", 560 bloc, blhs, data->d_ftype->t_name, 561 data->d_ttype->t_name); 562 break; 563 } 564 565 case KUBSAN_INVALID_VALUE: { 566 const struct invalid_value_data *data = 567 kr->kr_invalid_value.v_data; 568 569 kubsan_format_int(data->d_type, 570 kr->kr_invalid_value.v_val, blhs, sizeof(blhs)); 571 printf("kubsan: %s: load invalid value: load of value " 572 "%s is not a valid value for type %s\n", 573 bloc, blhs, data->d_type->t_name); 574 break; 575 } 576 577 case KUBSAN_NEGATE_OVERFLOW: { 578 const struct overflow_data *data = 579 kr->kr_negate_overflow.v_data; 580 581 kubsan_format_int(data->d_type, 582 kr->kr_negate_overflow.v_val, blhs, sizeof(blhs)); 583 printf("kubsan: %s: negate overflow: negation of %s " 584 "cannot be represented in type %s\n", 585 bloc, blhs, data->d_type->t_name); 586 break; 587 } 588 589 case KUBSAN_NONNULL_ARG: { 590 const struct nonnull_arg_data *data = 591 kr->kr_nonnull_arg.v_data; 592 593 if (data->d_attr_src.sl_filename) 594 kubsan_format_location(&data->d_attr_src, 595 blhs, sizeof(blhs)); 596 else 597 blhs[0] = '\0'; 598 599 printf("kubsan: %s: null pointer passed as argument " 600 "%d, which is declared to never be null%s%s\n", 601 bloc, data->d_idx, 602 blhs[0] ? "nonnull specified in " : "", blhs); 603 break; 604 } 605 606 case KUBSAN_OUT_OF_BOUNDS: { 607 const struct out_of_bounds_data *data = 608 kr->kr_out_of_bounds.v_data; 609 610 kubsan_format_int(data->d_itype, 611 kr->kr_out_of_bounds.v_idx, blhs, sizeof(blhs)); 612 printf("kubsan: %s: out of bounds: index %s is out of " 613 "range for type %s\n", 614 bloc, blhs, data->d_atype->t_name); 615 break; 616 } 617 618 case KUBSAN_OVERFLOW: { 619 const struct overflow_data *data = 620 kr->kr_overflow.v_data; 621 622 kubsan_format_int(data->d_type, 623 kr->kr_overflow.v_lhs, blhs, sizeof(blhs)); 624 kubsan_format_int(data->d_type, 625 kr->kr_overflow.v_rhs, brhs, sizeof(brhs)); 626 printf("kubsan: %s: %s integer overflow: %s %c %s " 627 "cannot be represented in type %s\n", 628 bloc, SIGNED(data->d_type) ? "signed" : "unsigned", 629 blhs, kr->kr_overflow.v_op, brhs, 630 data->d_type->t_name); 631 break; 632 } 633 634 case KUBSAN_POINTER_OVERFLOW: 635 printf("kubsan: %s: pointer overflow: pointer " 636 "expression with base %#lx overflowed to %#lx\n", 637 bloc, kr->kr_pointer_overflow.v_base, 638 kr->kr_pointer_overflow.v_res); 639 break; 640 641 case KUBSAN_SHIFT_OUT_OF_BOUNDS: { 642 const struct shift_out_of_bounds_data *data = 643 kr->kr_shift_out_of_bounds.v_data; 644 unsigned long lhs = kr->kr_shift_out_of_bounds.v_lhs; 645 unsigned long rhs = kr->kr_shift_out_of_bounds.v_rhs; 646 647 kubsan_format_int(data->d_ltype, lhs, blhs, 648 sizeof(blhs)); 649 kubsan_format_int(data->d_rtype, rhs, brhs, 650 sizeof(brhs)); 651 if (is_negative(data->d_rtype, rhs)) 652 printf("kubsan: %s: shift: shift exponent %s " 653 "is negative\n", 654 bloc, brhs); 655 else if (is_shift_exponent_too_large(data->d_rtype, rhs)) 656 printf("kubsan: %s: shift: shift exponent %s " 657 "is too large for %u-bit type\n", 658 bloc, brhs, NBITS(data->d_rtype)); 659 else if (is_negative(data->d_ltype, lhs)) 660 printf("kubsan: %s: shift: left shift of " 661 "negative value %s\n", 662 bloc, blhs); 663 else 664 printf("kubsan: %s: shift: left shift of %s by " 665 "%s places cannot be represented in type " 666 "%s\n", 667 bloc, blhs, brhs, data->d_ltype->t_name); 668 break; 669 } 670 671 case KUBSAN_TYPE_MISMATCH: { 672 const struct type_mismatch_data *data = 673 kr->kr_type_mismatch.v_data; 674 unsigned long ptr = kr->kr_type_mismatch.v_ptr; 675 unsigned long align = 1UL << data->d_align; 676 677 if (ptr == 0UL) 678 printf("kubsan: %s: type mismatch: %s null " 679 "pointer of type %s\n", 680 bloc, kubsan_kind(data->d_kind), 681 data->d_type->t_name); 682 else if (ptr & (align - 1)) 683 printf("kubsan: %s: type mismatch: %s " 684 "misaligned address %p for type %s which " 685 "requires %lu byte alignment\n", 686 bloc, kubsan_kind(data->d_kind), 687 (void *)ptr, data->d_type->t_name, align); 688 else 689 printf("kubsan: %s: type mismatch: %s address " 690 "%p with insufficient space for an object " 691 "of type %s\n", 692 bloc, kubsan_kind(data->d_kind), 693 (void *)ptr, data->d_type->t_name); 694 break; 695 } 696 697 case KUBSAN_UNREACHABLE: 698 printf("kubsan: %s: unreachable: calling " 699 "__builtin_unreachable()\n", 700 bloc); 701 break; 702 } 703 704#ifdef DDB 705 if (kubsan_watch == 2) 706 db_enter(); 707#endif 708 } 709 710 /* New reports can arrive at any time. */ 711 if (atomic_cas_uint(&kubsan_slot, nslots, 0) != nslots) { 712 if (nslots < KUBSAN_NSLOTS) 713 goto again; 714 atomic_swap_uint(&kubsan_slot, 0); 715 } 716 717done: 718 timeout_add_msec(&kubsan_timo, KUBSAN_INTERVAL); 719} 720 721void 722kubsan_unreport(struct source_location *src) 723{ 724 uint32_t *line = &src->sl_line; 725 726 atomic_clearbits_int(line, LOCATION_REPORTED); 727} 728 729static int 730is_negative(struct type_descriptor *typ, unsigned long val) 731{ 732 return (SIGNED(typ) && kubsan_deserialize_int(typ, val) < 0); 733} 734 735static int 736is_shift_exponent_too_large(struct type_descriptor *typ, unsigned long val) 737{ 738 return (kubsan_deserialize_int(typ, val) >= NBITS(typ)); 739} 740 741/* 742 * A source location is an absolute path making reports quite long. 743 * Instead, use everything after the first /sys/ segment as the path. 744 */ 745static const char * 746pathstrip(const char *path) 747{ 748 const char *needle = "/sys/"; 749 size_t i, j; 750 751 for (i = j = 0; path[i] != '\0'; i++) { 752 if (path[i] != needle[j]) { 753 j = 0; 754 continue; 755 } 756 757 if (needle[++j] == '\0') 758 return path + i + 1; 759 } 760 761 return path; 762} 763