DF-5554 / calcgeo.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 | /* * df5554.ko -- pass-2 evidence harness for sys/bus/cam/cam.c * cam_calc_geometry() 64->32-bit truncation (cam.c:389, cam.c:398). * * Drives the PRODUCTION kernel cam_calc_geometry() (linked from the * running kernel image, not a copy) with crafted volume_size values. * volume_size (struct ccb_calc_geometry, cam_ccb.h:779) is u_int64_t and * is filled by scsi_da.c:dasetgeom() (scsi_da.c:2303) straight from the * device-reported READ CAPACITY(16) value (scsi_8btou64, scsi_da.c:1851) * -- i.e. fully controlled by whatever SCSI target the host talks to * (USB gadget, iSCSI/NBD export, virtio-scsi backend). * * All arithmetic locals inside cam_calc_geometry are uint32_t: * * uint32_t size_mb, secs_per_cylinder; (cam.c:378) * size_mb = ccg->volume_size / size_mb; (cam.c:389) u64->u32 * ccg->cylinders = ccg->volume_size / secs_per_cylinder; (cam.c:398) u64->u32 * * For volume_size >= 2^32 * secs_per_cylinder both quotients wrap mod 2^32: * wrong extended-translation decision (H/S) and an arbitrary wrapped * cylinder count, including cylinders == 0. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/types.h> #include <bus/cam/cam.h> #include <bus/cam/cam_ccb.h> struct df_case { uint64_t vol; uint32_t bs; int ext; const char *tag; }; static const struct df_case cases[] = { { 15628000000ULL, 512, 1, "sane-8TB-disk" }, { 1ULL << 43, 512, 1, "2^43 sectors (4PiB@512B): size_mb wraps to 0" }, { 16065ULL << 32, 512, 1, "2^32*16065: cylinders wraps exactly to 0" }, { (16065ULL << 32) + 16064, 512, 1, "2^32*16065+16064: cylinders wraps to 0" }, { 0xFFFFFFFFFFFFFFFFULL, 512, 1, "max-u64 (crafted READ CAPACITY(16))" }, { 0, 512, 1, "zero-volume" }, { 1000000, 0, 1, "block_size=0 (div-guard check)" }, }; static void run_case(const struct df_case *c) { struct ccb_calc_geometry ccg; uint64_t mb_div, mb_true, spc_true, cyl_true; uint32_t heads_true, secs_true; bzero(&ccg, sizeof(ccg)); ccg.block_size = c->bs; ccg.volume_size = c->vol; /* exact 64-bit replay of what the function *should* compute */ if (c->bs == 0) { mb_true = 0; } else { mb_div = (uint64_t)1024 * 1024 / c->bs; mb_true = mb_div ? (c->vol / mb_div) : 0; } heads_true = (mb_true > 1024 && c->ext) ? 255 : 64; secs_true = (heads_true == 255) ? 63 : 32; spc_true = (uint64_t)heads_true * secs_true; cyl_true = spc_true ? (c->vol / spc_true) : 0; cam_calc_geometry(&ccg, c->ext); kprintf("DF5554[%s]\n", c->tag); kprintf("DF5554[%s] in : vol=%ju bs=%u ext=%d\n", c->tag, (uintmax_t)c->vol, c->bs, c->ext); kprintf("DF5554[%s] ker: H=%u S=%u C=%u status=0x%02x\n", c->tag, ccg.heads, ccg.secs_per_track, ccg.cylinders, ccg.ccb_h.status & CAM_STATUS_MASK); kprintf("DF5554[%s] tru: H=%u S=%u C=%ju (true size_mb=%ju)\n", c->tag, heads_true, secs_true, (uintmax_t)cyl_true, (uintmax_t)mb_true); if ((ccg.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP && (ccg.heads != heads_true || ccg.secs_per_track != secs_true || ccg.cylinders != (uint32_t)cyl_true || cyl_true > 0xFFFFFFFFu)) { kprintf("DF5554[%s] <<< MISMATCH: u64->u32 truncation " "in cam_calc_geometry (cam.c:389/cam.c:398)\n", c->tag); } else if ((ccg.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { kprintf("DF5554[%s] ok (matches 64-bit math)\n", c->tag); } else { kprintf("DF5554[%s] rejected by guard (status != CMP)\n", c->tag); } } static int df5554_handler(module_t mod, int what, void *arg) { int i; switch (what) { case MOD_LOAD: kprintf("DF5554: cam_calc_geometry truncation harness " "(production %s)\n", version); for (i = 0; i < (int)NELEM(cases); i++) run_case(&cases[i]); kprintf("DF5554: done\n"); return (0); default: return (EOPNOTSUPP); } } static moduledata_t df5554_mod = { "df5554", df5554_handler, NULL }; DECLARE_MODULE(df5554, df5554_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); |