DF-5012 / df5012.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 | /* * DF-5012 sink demo — mfi_disk_strategy() bio leak (sys/dev/raid/mfi/mfi_disk.c:266-274) * * The guest has no LSI MegaRAID controller, so the mfi path cannot be entered * at runtime. Following the DF-4986 precedent, this module replicates the * *exact completion pattern* of the vulnerable early-return paths on the * stock kernel: * * mfi_disk.c:266-269 (controller->hw_crit_error): * bp->b_error = EBUSY; * return (0); <-- no B_ERROR, no b_resid, no biodone() * mfi_disk.c:271-274 (issuepend_done == 0): * bp->b_error = EBUSY; * return (0); <-- same * * A raw D_DISK device built exactly like mfid (disk_create + d_strategy). * Reads at offset >= LEAK_OFF reproduce the leak verbatim. A read at POKE_OFF * ("recovery poke", standing in for what mfi never does) completes the leaked * bio so the guest can be returned to a clean state after the demo. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/conf.h> #include <sys/disk.h> #include <sys/buf2.h> #define LEAK_OFF (2UL << 20) /* offset that triggers the leak */ #define POKE_OFF (1UL << 20) /* offset that completes the leaked bio */ static d_open_t df5012_open; static d_close_t df5012_close; static d_strategy_t df5012_strategy; static struct dev_ops df5012_ops = { { "df5012", 0, D_DISK | D_MPSAFE }, .d_open = df5012_open, .d_close = df5012_close, .d_strategy = df5012_strategy, }; static struct disk df5012_disk; static struct bio *df5012_leaked; static int df5012_open(struct dev_open_args *ap) { return (0); } static int df5012_close(struct dev_close_args *ap) { return (0); } static int df5012_strategy(struct dev_strategy_args *ap) { struct bio *bio = ap->a_bio; struct buf *bp = bio->bio_buf; if (bio->bio_offset >= LEAK_OFF) { /* ---- verbatim mfi_disk.c:266-274 pattern ---- */ kprintf("df5012: LEAKING bio %p (off %jd) -- " "b_error=EBUSY, return 0, NO biodone\n", bio, (intmax_t)bio->bio_offset); if (df5012_leaked == NULL) df5012_leaked = bio; bp->b_error = EBUSY; return (0); } if (bio->bio_offset == POKE_OFF && df5012_leaked != NULL) { /* what the fixed mfi code would do for the faulted bios */ struct buf *lb = df5012_leaked->bio_buf; kprintf("df5012: poke -- completing leaked bio %p\n", df5012_leaked); lb->b_flags |= B_ERROR; lb->b_resid = lb->b_bcount; biodone(df5012_leaked); df5012_leaked = NULL; } bp->b_resid = 0; biodone(bio); return (0); } static int df5012_modevent(module_t mod, int type, void *data) { struct disk_info info; int error = 0; switch (type) { case MOD_LOAD: bzero(&df5012_disk, sizeof(df5012_disk)); bzero(&info, sizeof(info)); info.d_media_blksize = 512; info.d_media_blocks = 8192; /* 4 MiB test media */ info.d_nheads = 64; info.d_secpertrack = 32; disk_create(0, &df5012_disk, &df5012_ops); disk_setdiskinfo(&df5012_disk, &info); kprintf("df5012: raw disk \"%s\" up; read @0x%lx => leak, " "@0x%lx => recovery poke\n", df5012_disk.d_cdev->si_name, LEAK_OFF, POKE_OFF); break; case MOD_UNLOAD: if (df5012_leaked != NULL) { struct buf *bp = df5012_leaked->bio_buf; kprintf("df5012: unload -- completing leaked bio %p\n", df5012_leaked); bp->b_flags |= B_ERROR; bp->b_error = EIO; bp->b_resid = bp->b_bcount; biodone(df5012_leaked); df5012_leaked = NULL; } disk_destroy(&df5012_disk); break; default: error = EOPNOTSUPP; break; } return (error); } DEV_MODULE(df5012, df5012_modevent, NULL); |