DF-1091 / df1091_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 | /* * DF-1091 โ userspace harness for amdsmb_bread OOB MMIO read pattern. * * The kernel path (sys/bus/smbus/amdsmb/amdsmb.c:amdsmb_bread) is reachable * only via AMD-8111 SMBus hardware (PCI 1022:746a) with a malicious I2C * slave on the bus. The audit QEMU guest has NO AMD SMBus (PCI 0:1:3 is * Intel PIIX4 PM, class 0x068000 not 0x0c05), so this harness mirrors the * vulnerable algorithm byte-for-byte to demonstrate the OOB pattern. * * The harness models the SMBus EC register file as a 256-byte array * (offsets 0x00..0xff) with the 32-byte SMB_DATA window at 0x04..0x23. * A malicious slave supplies SMB_BCNT=255. The unpatched algorithm reads * SMB_DATA+i for i in [0,255), reading 224 bytes past the SMB_DATA window * into adjacent register space. The patched algorithm clamps len to 32. * * Build: cc -O0 -o df1091_harness df1091_harness.c * or: cc -O0 -DFIX -o df1091_harness_fix df1091_harness.c * Run: ./df1091_harness */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #define SMB_DATA 0x04 #define SMB_BCNT 0x24 #define EC_SPACE 256 static unsigned char ec_space[EC_SPACE]; static int oob_count; /* Mirror amdsmb_ec_read: read 1 byte from EC register offset. * In real hardware the SMBus EC register file is much smaller than * EC_SPACE; reads past the SMB_DATA window land in unrelated ACPI/EC * registers and may have side effects. We count any read with * offset > SMB_DATA + 31 (i.e. > 0x23) as the OOB bug pattern. */ static unsigned char ec_read(int off) { if (off > SMB_DATA + 31) { oob_count++; return 0xff; } if (off < 0 || off >= EC_SPACE) return 0xff; return ec_space[off]; } /* Mirror amdsmb_bread's post-wait body. */ static int amdsmb_bread_model(unsigned char slave_count, unsigned char *count, char *buf) { unsigned char len, data; int i; /* entry check uses caller-supplied *count */ if (*count < 1 || *count > 32) return -1; /* model amdsmb_wait() success */ len = slave_count; oob_count = 0; #ifndef FIX /* UNPATCHED โ loop bound is slave len, no clamp */ for (i = 0; i < len; i++) { data = ec_read(SMB_DATA + i); /* OOB when i >= 32 */ if (i < *count) buf[i] = data; } #else /* PATCHED โ clamp len to 32 (SMB_DATA register file size) */ if (len > 32) len = 32; for (i = 0; i < len; i++) { data = ec_read(SMB_DATA + i); if (i < *count) buf[i] = data; } #endif *count = len; return 0; } int main(void) { char buf[1024]; unsigned char bcount; int rc; memset(ec_space, 0xAA, sizeof(ec_space)); ec_space[SMB_BCNT] = 255; /* malicious slave */ memset(buf, 0, sizeof(buf)); bcount = 8; /* caller-supplied initial value */ printf("=== DF-1091 amdsmb_bread OOB MMIO harness (%s) ===\n", #ifdef FIX "PATCHED" #else "UNPATCHED" #endif ); printf("caller *count=%u, slave SMB_BCNT=%u\n", bcount, ec_space[SMB_BCNT]); rc = amdsmb_bread_model(ec_space[SMB_BCNT], &bcount, buf); printf("rc=%d, returned *count=%u (was 8)\n", rc, bcount); printf("MMIO reads past SMB_DATA[0..31]: %d\n", oob_count); if (bcount > 32) printf("RETURNED COUNT > 32: caller (smb.c) will copyout up to %u bytes\n" " from a buf only 32 bytes were written into -> uninitialized stack leak\n", bcount); if (oob_count > 0) printf("BUG: %d OOB MMIO reads of adjacent EC registers (SMB_BCNT/SMB_ALRM_*/beyond)\n", oob_count); if (oob_count == 0 && bcount <= 32) printf("OK: no OOB; returned count clamped to %u\n", bcount); return (0); } |