DF-5585 / poc_quirkfastpath.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 | /* * poc_quirkfastpath.c -- DF-5585 rig for sys/bus/pccard/pccard_cis_quirks.c * (pass 2, GLM 5.3). Userland model, no PCMCIA hardware required. * * VERBATIM-BACKED: struct pccard_config_entry / pccard_function / * pccard_card / pccard_cis_quirk are byte-for-byte * sys/bus/pccard/pccardvar.h:87-160,165-184,205-211 (bus types typedef'd), * and pccard_cis_quirk_match() + pccard_check_cis_quirks() are byte-for-byte * sys/bus/pccard/pccard_cis_quirks.c:201-216 and :218-292 with ONLY these * mechanical substitutions (each marked * RIG: * inline): * - device_t dev / PCCARD_SOFTC(dev) -> struct pccard_softc *sc parameter * - device_printf(dev,...)/kprintf -> qlog(...) (macro) * - kmalloc(sz,M_DEVBUF,M_NOWAIT) -> qk_alloc(sz) (macro, failpoint) * - kfree(p,M_DEVBUF) -> qk_free(p) (macro, shadow-copy) * - the quirk table + n_pccard_cis_quirks become runtime-set globals * (see fidelity.diff for the normalized diff against sys/). * * The failpoint allocator snapshots every block at kfree time (kernel kfree * does not scrub; contents survive). After the run, freed blocks are * compared against their snapshots: any difference is a write into freed * memory -- the stale-pf write of the quirks.c:263-270 fast path. * * Scenarios (argv[1]): * negative shipped-table shape, no allocation failure -> clean * shippedfail shipped-table shape (func0,func1), cfe alloc of the 2nd * quirk fails (fail_nth=4) -> clean (today) * repeat 3-entry table re-using the func0 template (the natural * future extension of quirks.c:178-197), same fail_nth=4 * -> STAILQ_INSERT_TAIL writes into the pf kfree'd at * pccard_cis_quirks.c:282 * nulltemplate entry with pf==NULL after a non-empty wipe -> fast path * (pf_last==NULL==q->pf) writes into the pf freed by the * wipe loop (pccard_cis_quirks.c:248-257); empty-list * variant would write to ~address 0 (NULL page) instead * matchmatrix enumerates every card identity the SHIPPED table can * match; proves no identity ever takes the fast path * (no consecutive same-template matches, sets <= 2) * fixed_* same as repeat/nulltemplate but through * pccard_check_cis_quirks_fixed() = verbatim + the * fix.diff guard lines -> no freed-memory writes * all everything in order */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <stddef.h> #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <sys/queue.h> /* macro shims so the verbatim function bodies below compile unchanged; * kmalloc/kfree swap in the failpoint allocator, printf stands in for * device_printf/kprintf (first arg dev discarded, never evaluated) */ #define qlog printf #define kmalloc(sz, type, flags) qk_alloc(sz) #define kfree(p, type) qk_free(p) #define device_printf(dev, ...) printf(__VA_ARGS__) #define kprintf(...) printf(__VA_ARGS__) static int bootverbose = 0; /* ---------------- failpoint allocator ---------------- */ #define QK_NBLOCKS 64 #define QK_BLOCK 512 struct qk_blk { void *p; int freed; size_t serial; /* kmalloc call number that made it */ uint8_t shadow[QK_BLOCK]; /* contents at kfree time */ }; static struct qk_blk qk_pool[QK_NBLOCKS]; static int qk_used; static size_t qk_alloc_calls; static long qk_fail_nth = -1; /* fail the Nth kmalloc (1-based) */ static void *qk_alloc(size_t sz) { void *p; qk_alloc_calls++; if (qk_fail_nth > 0 && (size_t)qk_fail_nth == qk_alloc_calls) { qlog("RIG: kmalloc call #%zu (%zu bytes) -> NULL (failpoint)\n", qk_alloc_calls, sz); return NULL; } if (sz > QK_BLOCK || qk_used == QK_NBLOCKS) { qlog("RIG: pool exhausted\n"); return NULL; } p = calloc(1, QK_BLOCK); /* kernel kmalloc: not zeroed, but a * fresh cfe/pf copies over every * field it reads; calloc keeps the * model deterministic */ qk_pool[qk_used].p = p; qk_pool[qk_used].freed = 0; qk_pool[qk_used].serial = qk_alloc_calls; qk_used++; return p; } static void qk_free(void *p) { int i; if (p == NULL) return; for (i = 0; i < qk_used; i++) { if (qk_pool[i].p == p && !qk_pool[i].freed) { qk_pool[i].freed = 1; memcpy(qk_pool[i].shadow, p, QK_BLOCK); return; } } qlog("RIG: qk_free(%p): not a live pool block!\n", p); } /* writes-into-freed-memory audit */ static int qk_audit(const char *tag) { int i, dirty = 0, off, noff; uint8_t *b; for (i = 0; i < qk_used; i++) { if (!qk_pool[i].freed) continue; b = qk_pool[i].p; noff = 0; for (off = 0; off < QK_BLOCK; off++) { if (b[off] != qk_pool[i].shadow[off]) noff++; } if (noff) { dirty++; for (off = 0; off < QK_BLOCK; off++) { if (b[off] != qk_pool[i].shadow[off]) break; } qlog(" FREED-WRITE[%s]: block %p (kmalloc #%zu) " "modified: first diff at +%d, %d byte(s) changed " "(snapshot 0x%02x -> 0x%02x)\n", tag, qk_pool[i].p, qk_pool[i].serial, off, noff, qk_pool[i].shadow[off], b[off]); } } if (!dirty) qlog(" freed-memory audit[%s]: CLEAN (0 writes into " "kfree'd blocks)\n", tag); return dirty; } /* device_printf/kprintf stand-in */ #define qlog printf /* ---------------- verbatim struct definitions -------------------------- */ /* pccardvar.h:87-112 */ struct pccard_config_entry { int number; uint32_t flags; int iftype; int num_iospace; u_long iomask; struct { u_long length; u_long start; } iospace[4]; uint16_t irqmask; int num_memspace; struct { u_long length; u_long cardaddr; u_long hostaddr; } memspace[2]; int maxtwins; STAILQ_ENTRY(pccard_config_entry) cfe_list; }; /* pccardvar.h:114-126 */ struct pccard_funce_disk { int pfd_interface; }; struct pccard_funce_lan { int pfl_nidlen; uint8_t pfl_nid[8]; }; union pccard_funce { struct pccard_funce_disk pfv_disk; struct pccard_funce_lan pfv_lan; }; struct pccard_softc; /* RIG: fwd, was via bus.h */ /* pccardvar.h:128-160 (bus types typedef'd, field order verbatim) */ struct pccard_function { int number; int function; int last_config_index; uint32_t ccr_base; uint32_t ccr_mask; void *ccr_res; /* RIG: struct resource * */ int ccr_rid; STAILQ_HEAD(, pccard_config_entry) cfe_head; STAILQ_ENTRY(pccard_function) pf_list; struct pccard_softc *sc; struct pccard_config_entry *cfe; struct { /* RIG: pccard_mem_handle */ uint64_t addr, size, realsize, cardaddr; int kind; } pf_pcmh; void *dev; /* RIG: device_t */ uint32_t pf_ccr_offset; int pf_ccr_window; uint64_t pf_mfc_iobase; /* RIG: bus_addr_t */ uint64_t pf_mfc_iomax; /* RIG: bus_addr_t */ int pf_flags; void *intr_handler; /* RIG: driver_intr_t * */ void *intr_handler_arg; void *intr_handler_cookie; union pccard_funce pf_funce; }; /* pccardvar.h:165-184 */ struct pccard_card { int cis1_major; int cis1_minor; char cis1_info_buf[256]; char *cis1_info[4]; int32_t manufacturer; #define PCMCIA_VENDOR_INVALID -1 int32_t product; #define PCMCIA_PRODUCT_INVALID -1 int16_t prodext; uint16_t error; #define PCMCIA_CIS_INVALID { NULL, NULL, NULL, NULL } STAILQ_HEAD(, pccard_function) pf_head; }; struct pccard_softc { /* RIG: pccardvar.h:196-203, card only */ struct pccard_card card; }; /* pccardvar.h:205-211 */ struct pccard_cis_quirk { int32_t manufacturer; int32_t product; char *cis1_info[4]; struct pccard_function *pf; struct pccard_config_entry *cfe; }; /* RIG: the kmalloc/kfree/device_printf/kprintf/bootverbose shims live at * the top of the file; the quirk table + count are runtime-set globals * (scenario scaffolding) so the function bodies below stay verbatim. */ static struct pccard_cis_quirk *pccard_cis_quirks; static int n_pccard_cis_quirks; /* ---------------- VERBATIM pccard_cis_quirks.c:201-216 ----------------- */ static int pccard_cis_quirk_match(struct pccard_softc *sc, struct pccard_cis_quirk *q) { if ((sc->card.manufacturer == q->manufacturer) && (sc->card.product == q->product) && (((sc->card.manufacturer != PCMCIA_VENDOR_INVALID) && (sc->card.product != PCMCIA_PRODUCT_INVALID)) || ((sc->card.manufacturer == PCMCIA_VENDOR_INVALID) && (sc->card.product == PCMCIA_PRODUCT_INVALID) && sc->card.cis1_info[0] && (strcmp(sc->card.cis1_info[0], q->cis1_info[0]) == 0) && sc->card.cis1_info[1] && (strcmp(sc->card.cis1_info[1], q->cis1_info[1]) == 0)))) return (1); return (0); } /* ------------- VERBATIM pccard_cis_quirks.c:218-292 -------------------- */ void pccard_check_cis_quirks(struct pccard_softc *sc) /* RIG: was (device_t dev) */ { /* RIG: struct pccard_softc *sc = PCCARD_SOFTC(dev); -- now a param */ int wiped = 0; int i, j; struct pccard_function *pf, *pf_next, *pf_last; struct pccard_config_entry *cfe, *cfe_next; struct pccard_cis_quirk *q; pf = NULL; pf_last = NULL; for (i=0; i<n_pccard_cis_quirks; i++) { q = &pccard_cis_quirks[i]; if (!pccard_cis_quirk_match(sc, q)) continue; if (!wiped) { if (bootverbose) { /* RIG: device_printf(dev,...) -> printf; body * verbatim, not reached (bootverbose==0) */ qlog("using CIS quirks for "); for (j = 0; j < 4; j++) { if (sc->card.cis1_info[j] == NULL) break; if (j) kprintf(", "); kprintf("%s", sc->card.cis1_info[j]); } kprintf("\n"); } for (pf = STAILQ_FIRST(&sc->card.pf_head); pf != NULL; pf = pf_next) { for (cfe = STAILQ_FIRST(&pf->cfe_head); cfe != NULL; cfe = cfe_next) { cfe_next = STAILQ_NEXT(cfe, cfe_list); kfree(cfe, M_DEVBUF); } pf_next = STAILQ_NEXT(pf, pf_list); kfree(pf, M_DEVBUF); } STAILQ_INIT(&sc->card.pf_head); wiped = 1; } if (pf_last == q->pf) { cfe = kmalloc(sizeof(*cfe), M_DEVBUF, M_NOWAIT); if (cfe == NULL) { device_printf(dev, "no memory for quirk (1)\n"); continue; } *cfe = *q->cfe; STAILQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list); } else { pf = kmalloc(sizeof(*pf), M_DEVBUF, M_NOWAIT); if (pf == NULL) { device_printf(dev, "no memory for pccard function\n"); continue; } *pf = *q->pf; STAILQ_INIT(&pf->cfe_head); cfe = kmalloc(sizeof(*cfe), M_DEVBUF, M_NOWAIT); if (cfe == NULL) { kfree(pf, M_DEVBUF); device_printf(dev, "no memory for quirk (2)\n"); continue; } *cfe = *q->cfe; STAILQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list); STAILQ_INSERT_TAIL(&sc->card.pf_head, pf, pf_list); pf_last = q->pf; } } } /* ---- fix.diff variant: verbatim + the two guard lines (marked FIX) ---- */ void pccard_check_cis_quirks_fixed(struct pccard_softc *sc) { int wiped = 0; int i, j; struct pccard_function *pf, *pf_next, *pf_last; struct pccard_config_entry *cfe, *cfe_next; struct pccard_cis_quirk *q; pf = NULL; pf_last = NULL; for (i=0; i<n_pccard_cis_quirks; i++) { q = &pccard_cis_quirks[i]; if (!pccard_cis_quirk_match(sc, q)) continue; if (q->pf == NULL || q->cfe == NULL) /* FIX: malformed */ continue; /* FIX: entry */ if (!wiped) { for (pf = STAILQ_FIRST(&sc->card.pf_head); pf != NULL; pf = pf_next) { for (cfe = STAILQ_FIRST(&pf->cfe_head); cfe != NULL; cfe = cfe_next) { cfe_next = STAILQ_NEXT(cfe, cfe_list); kfree(cfe, M_DEVBUF); } pf_next = STAILQ_NEXT(pf, pf_list); kfree(pf, M_DEVBUF); } STAILQ_INIT(&sc->card.pf_head); wiped = 1; } if (pf_last == q->pf) { cfe = kmalloc(sizeof(*cfe), M_DEVBUF, M_NOWAIT); if (cfe == NULL) { device_printf(dev, "no memory for quirk (1)\n"); continue; } *cfe = *q->cfe; STAILQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list); } else { pf = kmalloc(sizeof(*pf), M_DEVBUF, M_NOWAIT); if (pf == NULL) { device_printf(dev, "no memory for pccard function\n"); continue; } *pf = *q->pf; STAILQ_INIT(&pf->cfe_head); cfe = kmalloc(sizeof(*cfe), M_DEVBUF, M_NOWAIT); if (cfe == NULL) { kfree(pf, M_DEVBUF); pf_last = NULL; /* FIX: pf no longer * tracks pf_last */ device_printf(dev, "no memory for quirk (2)\n"); continue; } *cfe = *q->cfe; STAILQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list); STAILQ_INSERT_TAIL(&sc->card.pf_head, pf, pf_list); pf_last = q->pf; } } } /* ---------------- scenario scaffolding --------------------------------- */ /* shipped templates, pccard_cis_quirks.c:51-176 (values verbatim) */ static struct pccard_function tpl_3cxem556_f0 = { 0, 6 /*PCCARD_FUNCTION_NETWORK*/, 0x07, 0x800, 0x63, }; static struct pccard_config_entry tpl_3cxem556_f0_cfe0 = { 0x07, 0x0010|0x0020|0x0100, 1 /*PCCARD_IFTYPE_IO*/, 1, 4, { { 0x0010, 0 } }, 0xffff, 0, { }, 0, }; static struct pccard_function tpl_3cxem556_f1 = { 1, 2 /*PCCARD_FUNCTION_SERIAL*/, 0x27, 0x900, 0x63, }; static struct pccard_config_entry tpl_3cxem556_f1_cfe0 = { 0x27, 0x0010|0x0100, 3, 1, 3, { { 0x0008, 0 } }, 0xffff, 0, { }, 0, }; /* a "fake CIS" function the wipe loop must free (simulates pccard_read_cis * output present at pccard.c:198 call time) */ static void seed_card_functions(struct pccard_softc *sc, int npf, int ncfe_per_pf) { struct pccard_function *pf; struct pccard_config_entry *cfe; int i, j; STAILQ_INIT(&sc->card.pf_head); for (i = 0; i < npf; i++) { pf = qk_alloc(sizeof(*pf)); memset(pf, 0, sizeof(*pf)); pf->number = i; STAILQ_INIT(&pf->cfe_head); for (j = 0; j < ncfe_per_pf; j++) { cfe = qk_alloc(sizeof(*cfe)); memset(cfe, 0, sizeof(*cfe)); cfe->number = j; STAILQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list); } STAILQ_INSERT_TAIL(&sc->card.pf_head, pf, pf_list); } } static void rig_reset(void) { int i; for (i = 0; i < qk_used; i++) free(qk_pool[i].p); memset(qk_pool, 0, sizeof(qk_pool)); qk_used = 0; qk_alloc_calls = 0; qk_fail_nth = -1; pccard_cis_quirks = NULL; n_pccard_cis_quirks = 0; } static int list_card(struct pccard_softc *sc, const char *tag) { struct pccard_function *pf; struct pccard_config_entry *cfe; int n = 0; STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { n++; qlog(" %s live pf#%d ccr_base=0x%x cfe:", tag, pf->number, pf->ccr_base); STAILQ_FOREACH(cfe, &pf->cfe_head, cfe_list) qlog(" #%d", cfe->number); qlog("\n"); } return n; } /* ---------------- scenarios -------------------------------------------- */ static int sc_negative(void) { struct pccard_softc s, *sc = &s; struct pccard_cis_quirk tbl[] = { { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f0, &tpl_3cxem556_f0_cfe0 }, { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f1, &tpl_3cxem556_f1_cfe0 }, }; rig_reset(); qlog("== negative: shipped-table shape (func0,func1), no alloc failure\n"); memset(sc, 0, sizeof(*sc)); seed_card_functions(sc, 2, 2); /* counts as kmalloc calls 1..6 */ sc->card.manufacturer = 0x0101; sc->card.product = 0x0035; pccard_cis_quirks = tbl; n_pccard_cis_quirks = 2; pccard_check_cis_quirks(sc); list_card(sc, "neg"); return qk_audit("negative"); } static int sc_shippedfail(void) { struct pccard_softc s, *sc = &s; struct pccard_cis_quirk tbl[] = { { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f0, &tpl_3cxem556_f0_cfe0 }, { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f1, &tpl_3cxem556_f1_cfe0 }, }; int bad; rig_reset(); qlog("== shippedfail: shipped-table shape, cfe alloc of quirk#2 fails " "(fail_nth=10)\n"); /* seed 6 blocks, then quirk#1 allocates pf(7),cfe(8->would be OK)... * we want quirk#2's cfe: pf(7),cfe(8),pf(9),cfe(10)FAIL -> fail_nth=10 */ memset(sc, 0, sizeof(*sc)); seed_card_functions(sc, 2, 2); sc->card.manufacturer = 0x0101; sc->card.product = 0x0035; pccard_cis_quirks = tbl; n_pccard_cis_quirks = 2; qk_fail_nth = 10; pccard_check_cis_quirks(sc); list_card(sc, "shipped"); bad = qk_audit("shippedfail"); qlog(" (expected: CLEAN -- with the shipped 2-entry shape the loop " "ends after the failure; fast path never runs)\n"); return bad; } static int sc_repeat(int fixed) { struct pccard_softc s, *sc = &s; /* future-maintainer table: a third quirk for the same card re-using * the func0 template (the exact pattern quirks.c:179-186 already * uses to share templates across two product ids) */ struct pccard_cis_quirk tbl[] = { { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f0, &tpl_3cxem556_f0_cfe0 }, { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f1, &tpl_3cxem556_f1_cfe0 }, { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f0, &tpl_3cxem556_f0_cfe0 }, }; int bad; rig_reset(); qlog("== %srepeat: 3-entry table re-using func0 template, quirk#2 cfe " "alloc fails (fail_nth=10)\n", fixed ? "fixed_" : ""); memset(sc, 0, sizeof(*sc)); seed_card_functions(sc, 2, 2); sc->card.manufacturer = 0x0101; sc->card.product = 0x0035; pccard_cis_quirks = tbl; n_pccard_cis_quirks = 3; qk_fail_nth = 10; if (fixed) pccard_check_cis_quirks_fixed(sc); else pccard_check_cis_quirks(sc); list_card(sc, fixed ? "fixed" : "repeat"); bad = qk_audit(fixed ? "fixed_repeat" : "repeat"); qlog(" offsetof(struct pccard_function, cfe_head) = %zu " "(STAILQ_INSERT_TAIL writes sqh_first at +0 and sqh_last at +8 " "of cfe_head)\n", offsetof(struct pccard_function, cfe_head)); if (!fixed && bad) qlog("RESULT: DF-5585 repeat: quirk#3 fast path " "(pf_last==q->pf) STAILQ_INSERT_TAIL'd a cfe into the pf " "kfree'd at pccard_cis_quirks.c:282 -> write-after-free\n"); if (fixed && !bad) qlog("RESULT: DF-5585 fixed_repeat: pf_last=NULL guard keeps " "the invariant; no freed-memory writes\n"); return bad; } static int sc_nulltemplate(int fixed) { struct pccard_softc s, *sc = &s; struct pccard_cis_quirk tbl[] = { { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, NULL, &tpl_3cxem556_f0_cfe0 }, /* pf == NULL */ }; int bad, st; pid_t pid; rig_reset(); qlog("== %snulltemplate: entry with pf==NULL; the wipe loop " "(:248-257) always exits with pf==NULL (it is the loop " "variable), so pf_last==NULL==q->pf takes the fast path and " "INSERT_TAIL dereferences &pf->cfe_head == 0x28\n", fixed ? "fixed_" : ""); if (!fixed) { /* the unfixed run writes to the NULL page: run it in a child * so the rig can report the fault instead of dying */ fflush(stdout); pid = fork(); if (pid == 0) { memset(sc, 0, sizeof(*sc)); seed_card_functions(sc, 2, 2); sc->card.manufacturer = 0x0101; sc->card.product = 0x0035; pccard_cis_quirks = tbl; n_pccard_cis_quirks = 1; pccard_check_cis_quirks(sc); _exit(0); /* not reached if it faults */ } waitpid(pid, &st, 0); if (WIFSIGNALED(st) && WTERMSIG(st) == 11) { qlog(" child died on SIGSEGV writing near address " "0x28/0x30 -- the kernel equivalent is a NULL-" "page write (deterministic panic)\n"); qlog("RESULT: DF-5585 nulltemplate: NULL pf template " "+ pf_last(NULL)==q->pf fast path -> " "&pf->cfe_head deref at offset 40 of NULL\n"); return 1; } qlog(" child exited status=%d (unexpected)\n", st); return 0; } memset(sc, 0, sizeof(*sc)); seed_card_functions(sc, 2, 2); sc->card.manufacturer = 0x0101; sc->card.product = 0x0035; pccard_cis_quirks = tbl; n_pccard_cis_quirks = 1; pccard_check_cis_quirks_fixed(sc); list_card(sc, "fixed"); bad = qk_audit("fixed_nulltemplate"); if (!bad) qlog("RESULT: DF-5585 fixed_nulltemplate: NULL-template guard " "skips the entry; no freed-memory writes, no fault\n"); return bad; } /* machine-checked unreachability proof for the SHIPPED table */ static int sc_matchmatrix(void) { /* full shipped table, pccard_cis_quirks.c:178-197; CIS strings per * pccarddevs:327,332 (&sp == space, see pccarddevs2h.awk) */ static struct pccard_function tpl_bi_f0 = { 0, 6, 0x07, 0x1000, 0x267 }; static struct pccard_config_entry tpl_bi_f0_cfe0 = { 0x07, 0x0010|0x0020|0x0100, 3, 1, 5, { { 0x0020, 0 } }, 0xffff, 0, { }, 0 }; static struct pccard_function tpl_bi_f1 = { 1, 2, 0x27, 0x1100, 0x277 }; static struct pccard_config_entry tpl_bi_f1_cfe0 = { 0x27, 0x0010|0x0100, 3, 1, 3, { { 0x0008, 0 } }, 0xffff, 0, { }, 0 }; static struct pccard_function tpl_svec_f0 = { 0, 6, 0x1, 0x100, 0x1 }; static struct pccard_config_entry tpl_svec_f0_cfe0 = { 0x1, 0x0001|0x0002|0x0004|0x0008|0x0020, 1, 1, 5, { { 0x20, 0x300 } }, 0xdeb8, 0, { }, 0 }; static struct pccard_function tpl_ndc_f0 = { 0, 6, 0x23, 0x3f8, 0x3 }; static struct pccard_config_entry tpl_ndc_f0_cfe0 = { 0x20, 0x0001|0x0020|0x0100, 1, 1, 5, { { 0x20, 0x300 } }, 0xdeb8, 0, { }, 0 }; static struct pccard_cis_quirk shipped[] = { { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f0, &tpl_3cxem556_f0_cfe0 }, { 0x0101, 0x0035, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f1, &tpl_3cxem556_f1_cfe0 }, { 0x0101, 0x003d, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f0, &tpl_3cxem556_f0_cfe0 }, { 0x0101, 0x003d, { NULL, NULL, NULL, NULL }, &tpl_3cxem556_f1, &tpl_3cxem556_f1_cfe0 }, { 0x0101, 0x0556, { NULL, NULL, NULL, NULL }, &tpl_bi_f0, &tpl_bi_f0_cfe0 }, { 0x0101, 0x0556, { NULL, NULL, NULL, NULL }, &tpl_bi_f1, &tpl_bi_f1_cfe0 }, { -1, -1, { "SVEC", "FD605 PCMCIA EtherNet Card", NULL, NULL }, &tpl_svec_f0, &tpl_svec_f0_cfe0 }, { -1, -1, { "NDC", "Ethernet", NULL, NULL }, &tpl_ndc_f0, &tpl_ndc_f0_cfe0 }, }; /* every card identity the shipped table can possibly match */ struct { const char *name; int32_t man, prod; char *s0, *s1; } ids[] = { { "3COM 3CXEM556 ", 0x0101, 0x0035, NULL, NULL }, { "3COM 3CXEM556INT ", 0x0101, 0x003d, NULL, NULL }, { "3COM 3CCFEM556BI ", 0x0101, 0x0556, NULL, NULL }, { "SVEC LANCARD ", -1, -1, (char *)"SVEC", (char *)"FD605 PCMCIA EtherNet Card" }, { "NDC ND5100_E ", -1, -1, (char *)"NDC", (char *)"Ethernet" }, { "no-match card ", 0xdead, 0xbeef, NULL, NULL }, }; int n_ids = sizeof(ids) / sizeof(ids[0]); int n_q = sizeof(shipped) / sizeof(shipped[0]); int c, i, nm, fastpath_ever = 0; struct pccard_softc s, *sc = &s; struct pccard_function *prev_tpl; rig_reset(); qlog("== matchmatrix: every card identity the SHIPPED 8-entry table " "can match\n"); for (c = 0; c < n_ids; c++) { memset(sc, 0, sizeof(*sc)); STAILQ_INIT(&sc->card.pf_head); sc->card.manufacturer = ids[c].man; sc->card.product = ids[c].prod; if (ids[c].s0) { snprintf(sc->card.cis1_info_buf, 128, "%s%c%s%c", ids[c].s0, 0, ids[c].s1, 0); sc->card.cis1_info[0] = sc->card.cis1_info_buf; sc->card.cis1_info[1] = sc->card.cis1_info_buf + strlen(ids[c].s0) + 1; } qlog(" card %s -> matches:", ids[c].name); nm = 0; prev_tpl = NULL; for (i = 0; i < n_q; i++) { if (pccard_cis_quirk_match(sc, &shipped[i])) { qlog(" [%d:pf=%s]", i, shipped[i].pf == &tpl_3cxem556_f0 ? "xemf0" : shipped[i].pf == &tpl_3cxem556_f1 ? "xemf1" : shipped[i].pf == &tpl_bi_f0 ? "bif0" : shipped[i].pf == &tpl_bi_f1 ? "bif1" : shipped[i].pf == &tpl_svec_f0 ? "svec" : shipped[i].pf == &tpl_ndc_f0 ? "ndc" : "??"); if (nm > 0 && prev_tpl == shipped[i].pf) { fastpath_ever = 1; qlog(" FAST-PATH-REACHED"); } prev_tpl = shipped[i].pf; nm++; } } if (nm == 0) qlog(" none"); qlog(" (set size %d)\n", nm); } qlog("RESULT: DF-5585 matchmatrix: shipped table fast-path reachable " "by ANY card identity: %s (match sets <= 2 entries, templates " "within a set always differ)\n", fastpath_ever ? "YES" : "NO"); return fastpath_ever; } int main(int argc, char **argv) { const char *w = (argc > 1) ? argv[1] : "all"; int bad = 0; if (!strcmp(w, "all")) { bad += sc_negative(); bad += sc_shippedfail(); bad += sc_repeat(0); bad += sc_repeat(1); bad += sc_nulltemplate(0); bad += sc_nulltemplate(1); bad += sc_matchmatrix(); } else if (!strcmp(w, "negative")) { bad += sc_negative(); } else if (!strcmp(w, "shippedfail")) { bad += sc_shippedfail(); } else if (!strcmp(w, "repeat")) { bad += sc_repeat(0); } else if (!strcmp(w, "fixed_repeat")) { bad += sc_repeat(1); } else if (!strcmp(w, "nulltemplate")) { bad += sc_nulltemplate(0); } else if (!strcmp(w, "fixed_nulltemplate")) { bad += sc_nulltemplate(1); } else if (!strcmp(w, "matchmatrix")) { bad += sc_matchmatrix(); } else { fprintf(stderr, "unknown scenario %s\n", w); return 2; } qlog("DF-5585 rig done (%s): freed-write findings=%d\n", w, bad); return bad ? 1 : 0; } |