DF-1095 / df1095_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 | /* * DF-1095 โ userspace harness for iicsmb_bwrite missing iicbus_stop. * * The kernel path (sys/bus/iicbus/iicsmb.c:iicsmb_bwrite) is reachable * only when an iicbus-backed SMBus bridge is present and a slave NACKs * a byte mid-transaction. The audit QEMU guest has no iicbus/iicsmb * device in dmesg, so this harness models the iicbus state machine * (sc->started) and the call graph to demonstrate that the unpatched * bwrite leaves the bus permanently STARTED. * * Build: cc -O0 -o df1095_harness df1095_harness.c * or: cc -O0 -DFIX -o df1095_harness_fix df1095_harness.c * Run: ./df1095_harness */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> /* Mirror iicbus_softc's relevant state */ static int started; /* mirrors sc->started */ static int start_calls, stop_calls, write_calls; #define EINVAL 22 #define LSB 1 static int iicbus_start(int slave, int timeout) { (void)timeout; start_calls++; if (started) return EINVAL; started = slave; return 0; } static int iicbus_stop(void) { stop_calls++; started = 0; return 0; } /* write returns error on NACK; we let caller inject failure */ static int write_should_fail; static int iicbus_write(const char *buf, int len, int *sent, int timeout) { (void)buf; (void)len; (void)timeout; write_calls++; *sent = 0; return write_should_fail; } /* Mirror iicsmb_bwrite exactly. UNPATCHED = the bug; PATCHED = the fix. */ static int iicsmb_bwrite(unsigned char slave, char cmd, unsigned char count, char *buf) { int error, sent; #ifndef FIX /* UNPATCHED โ exact copy of iicsmb.c:462-482 */ if ((error = iicbus_start(slave & ~LSB, 0))) goto error; if ((error = iicbus_write(&cmd, 1, &sent, 0))) goto error; if ((error = iicbus_write(buf, (int)count, &sent, 0))) goto error; if ((error = iicbus_stop())) goto error; error: return (error); #else /* PATCHED โ mirrors iicsmb_bread pattern */ if ((error = iicbus_start(slave & ~LSB, 0))) return (error); if ((error = iicbus_write(&cmd, 1, &sent, 0))) goto error; if ((error = iicbus_write(buf, (int)count, &sent, 0))) goto error; error: iicbus_stop(); return (error); #endif } int main(void) { char buf[32] = {0}; int rc; printf("=== DF-1095 iicsmb_bwrite STARTED-leak harness (%s) ===\n", #ifdef FIX "PATCHED" #else "UNPATCHED" #endif ); started = 0; start_calls = stop_calls = write_calls = 0; /* Phase 1: bwrite where slave ACKs address (start succeeds) but * NACKs the data byte (write fails). */ printf("Phase 1: iicbus_start succeeds, iicbus_write NACKs data byte\n"); write_should_fail = -1; rc = iicsmb_bwrite(0x50, 0x00, 1, buf); printf(" bwrite rc=%d, start=%d stop=%d write=%d, started=%d\n", rc, start_calls, stop_calls, write_calls, started); /* Phase 2: try a second bwrite โ the bridge should still be usable */ printf("Phase 2: second bwrite attempt\n"); start_calls = stop_calls = write_calls = 0; write_should_fail = 0; rc = iicsmb_bwrite(0x50, 0x00, 1, buf); printf(" bwrite rc=%d, start=%d stop=%d write=%d, started=%d\n", rc, start_calls, stop_calls, write_calls, started); #ifndef FIX if (started != 0) printf("\nBUG: bus still STARTED after error return (rc!=0)\n" " subsequent iicbus_start returns EINVAL forever\n" " ===== SMBus bridge permanently wedged =====\n"); #else if (started == 0) printf("\nOK: bus returned to idle, subsequent transactions work\n"); #endif return (0); } |