DF-5082 / df5082_harness.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 | /* * DF-5082 mechanism harness for * sys/dev/raid/hptrr/hptrr_os_bsd.c (DragonFly 6.5-DEVELOPMENT guest). * * Defect: os_map_pci_bar() (hptrr_os_bsd.c:85-106) never checks the * return of bus_alloc_resource_any() (hptrr_os_bsd.c:101). When the * BAR allocation fails, rman_get_virtual(NULL) (hptrr_os_bsd.c:104) * dereferences NULL: rman_get_virtual(r) is ((r)->r_virtual) * (sys/sys/rman.h:149) and offsetof(struct resource, r_virtual)==0x40 * on amd64 -> "Fatal trap 12: page fault", fault VA 0x40, at device * attach time. Twin of DF-5039 (hpt27xx_os_bsd.c:113). * * This harness embeds a VERBATIM copy of hptrr_os_bsd.c:85-106 * (rename-only: df5082_os_map_pci_bar_copy) plus the same function * with the proposed fix.diff guard applied * (df5082_os_map_pci_bar_fixed). The HBA/pcibar types come from the * REAL driver header <dev/raid/hptrr/os_bsd.h>, so the * hba->pcibar[index] accesses have the production layout; CTASSERTs * prove it. * * The allocation failure is produced through the real newbus/pci/rman * stack, exactly as in production: the victim is a REAL PCI device * whose chosen BAR register reads 0 (unimplemented/unassigned BAR). * For such a BAR pci_add_map() (sys/bus/pci/pci.c:2525) returns * without resource_list_add(), so resource_list_alloc() takes its * "!rle -> return 0" path (sys/kern/subr_bus.c:2673) and * bus_alloc_resource_any() returns NULL. * * sysctl df5082.trigger: * 2 = fixed variant -> expected: "guarded, no panic", guest survives * 1 = verbatim copy -> expected: Fatal trap 12, fault VA 0x40 */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/sysctl.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/types.h> #include <sys/bus.h> #include <sys/rman.h> #include <bus/pci/pcivar.h> #include <bus/pci/pcireg.h> #include <dev/raid/hptrr/hptrr_config.h> #include <dev/raid/hptrr/os_bsd.h> /*-------------------------------------------------------------------- * Layout proof (real header, amd64). If any of these fire the static * analysis is wrong and the finding must be withdrawn. *------------------------------------------------------------------*/ CTASSERT(sizeof(HBA) > 0); CTASSERT(offsetof(HBA, pcidev) + sizeof(device_t) <= sizeof(HBA)); CTASSERT(offsetof(HBA, pcibar) + 6 * sizeof(((PHBA)0)->pcibar[0]) <= sizeof(HBA)); CTASSERT(sizeof(((PHBA)0)->pcibar[0]) == sizeof(struct { struct resource *res; int type; int rid; void *base; })); /*-------------------------------------------------------------------- * VERBATIM hptrr_os_bsd.c:85-106 (rename only). *------------------------------------------------------------------*/ static void * df5082_os_map_pci_bar_copy(void *osext, int index, HPT_U32 offset, HPT_U32 length) { PHBA hba = (PHBA)osext; hba->pcibar[index].rid = 0x10 + index * 4; if (pci_read_config(hba->pcidev, hba->pcibar[index].rid, 4) & 1) hba->pcibar[index].type = SYS_RES_IOPORT; else hba->pcibar[index].type = SYS_RES_MEMORY; hba->pcibar[index].res = bus_alloc_resource_any(hba->pcidev, hba->pcibar[index].type, &hba->pcibar[index].rid, RF_ACTIVE); hba->pcibar[index].base = (char *)rman_get_virtual(hba->pcibar[index].res) + offset; return hba->pcibar[index].base; } /*-------------------------------------------------------------------- * Same function with fix.diff applied (NULL guard + safe return). *------------------------------------------------------------------*/ static void * df5082_os_map_pci_bar_fixed(void *osext, int index, HPT_U32 offset, HPT_U32 length) { PHBA hba = (PHBA)osext; hba->pcibar[index].rid = 0x10 + index * 4; if (pci_read_config(hba->pcidev, hba->pcibar[index].rid, 4) & 1) hba->pcibar[index].type = SYS_RES_IOPORT; else hba->pcibar[index].type = SYS_RES_MEMORY; hba->pcibar[index].res = bus_alloc_resource_any(hba->pcidev, hba->pcibar[index].type, &hba->pcibar[index].rid, RF_ACTIVE); if (hba->pcibar[index].res == NULL) { /* fix.diff */ kprintf("DF5082-fixed: bus_alloc_resource_any(rid 0x%02x) " "returned NULL; guarded, no panic\n", hba->pcibar[index].rid); hba->pcibar[index].base = NULL; return NULL; } hba->pcibar[index].base = (char *)rman_get_virtual(hba->pcibar[index].res) + offset; return hba->pcibar[index].base; } /*-------------------------------------------------------------------- * Victim: first PCI child with a BAR register that reads 0. * Such a BAR never got a resource_list entry (pci_add_map returns * early), so bus_alloc_resource_any() fails -- the production shape * of "BAR could not be assigned". *------------------------------------------------------------------*/ static device_t df5082_find_victim(int *indexp) { devclass_t dc = devclass_find("pci"); int nunits, u; if (dc == NULL) return NULL; nunits = devclass_get_maxunit(dc); for (u = 0; u < nunits; u++) { device_t bus = devclass_get_device(dc, u); device_t *ch; int nch, c, idx; if (bus == NULL) continue; if (device_get_children(bus, &ch, &nch) != 0) continue; for (c = 0; c < nch; c++) { for (idx = 0; idx < 6; idx++) { int rid = 0x10 + idx * 4; if (pci_read_config(ch[c], rid, 4) == 0) { device_t victim = ch[c]; *indexp = idx; kfree(ch, M_TEMP); return victim; } } } kfree(ch, M_TEMP); } return NULL; } static int df5082_trigger(SYSCTL_HANDLER_ARGS) { int error, t = 0, idx = -1; device_t victim; HBA hba; void *r; error = sysctl_handle_int(oidp, &t, 0, req); if (error || req->newptr == NULL) return error; victim = df5082_find_victim(&idx); if (victim == NULL) { kprintf("DF5082: no victim with an unimplemented BAR found\n"); return 0; } memset(&hba, 0, sizeof(hba)); hba.ext_type = EXT_TYPE_HBA; hba.pcidev = victim; kprintf("DF5082: victim %s rid 0x%02x (BAR reads 0 -> " "bus_alloc_resource_any will fail)\n", device_get_nameunit(victim), 0x10 + idx * 4); if (t == 2) { r = df5082_os_map_pci_bar_fixed(&hba, idx, 0, 0x100); kprintf("DF5082: fixed variant returned %p; guest still alive\n", r); } else { kprintf("DF5082: calling VERBATIM os_map_pci_bar ...\n"); r = df5082_os_map_pci_bar_copy(&hba, idx, 0, 0x100); kprintf("DF5082: UNREACHABLE verbatim returned %p\n", r); } return 0; } SYSCTL_NODE(, OID_AUTO, df5082, CTLFLAG_RW, 0, "DF-5082 harness"); SYSCTL_PROC(_df5082, OID_AUTO, trigger, CTLTYPE_INT | CTLFLAG_RW, 0, 0, df5082_trigger, "I", "2=fixed variant (no panic), 1=verbatim copy (expect Fatal trap 12)"); static int df5082_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: kprintf("DF5082 harness loaded (sysctl df5082.trigger=N)\n"); return 0; default: return 0; } } static moduledata_t df5082_mod = { "df5082", df5082_modevent, 0 }; DECLARE_MODULE(df5082, df5082_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); |