DF-3824 / sg_layout_proof.c
/* * DF-3824 -- layout proof for the ASR_init_message() SGL overflow. * * Reproduces, with byte-for-byte struct replicas taken from * sys/dev/raid/asr/i2omsg.h (I2O_MESSAGE_FRAME, bitfields, * I2O_PRIVATE_MESSAGE_FRAME) * sys/dev/raid/asr/i2odpt.h (PRIVATE_SCSI_SCB_EXECUTE_MESSAGE) * sys/dev/raid/asr/asr.c (SG_SIZE=58, MAX_INBOUND_SIZE=512, * ASR_init_message() loop structure) * the write-offset arithmetic that lets the mandatory sense SGL element * land past the end of asr_action()'s 512-byte stack buffer. * * Build & run anywhere (guest cc 8.3 verified): it only computes and * prints offsets; no kernel objects are touched. */ #include <stdio.h> #include <stddef.h> #include <stdint.h> typedef uint8_t U8; typedef uint16_t U16; typedef uint32_t U32; /* i2omsg.h:297 (I2O_64BIT_CONTEXT == 0 on this driver) */ typedef struct { U8 VersionOffset; U8 MsgFlags; U16 MessageSize; uint32_t bits; U32 InitiatorContext; } I2O_MESSAGE_FRAME; typedef U32 I2O_TRANSACTION_CONTEXT; /* i2omsg.h:362 */ typedef struct { I2O_MESSAGE_FRAME StdMessageFrame; I2O_TRANSACTION_CONTEXT TransactionContext; U16 XFunctionCode; U16 OrganizationID; } I2O_PRIVATE_MESSAGE_FRAME; /* i2omsg.h:766,866 */ typedef struct { uint32_t bits; } I2O_FLAGS_COUNT; typedef struct { I2O_FLAGS_COUNT FlagsCount; U32 PhysicalAddress; } I2O_SGE_SIMPLE_ELEMENT; /* i2omsg.h:~915 I2O_SG_ELEMENT -- largest member is Simple[2] */ typedef struct { I2O_SGE_SIMPLE_ELEMENT Simple[2]; } I2O_SG_ELEMENT; /* i2odpt.h:24 -- with LP64 bitfield packing in one 32-bit unit */ typedef struct { I2O_PRIVATE_MESSAGE_FRAME PrivateMessageFrame; uint32_t tid_bits; /* TID:16 | Interpret:1 | Physical:1 | rsvd:14 */ U8 CDBLength; U8 Reserved; U16 SCBFlags; U8 CDB[16]; U32 ByteCount; I2O_SG_ELEMENT SGL; } PRIVATE_SCSI_SCB_EXECUTE_MESSAGE; #define SG_SIZE 58 /* asr.c:258 */ #define MAX_INBOUND_SIZE 512 /* asr.c:264 */ int main(void) { size_t hdr = offsetof(PRIVATE_SCSI_SCB_EXECUTE_MESSAGE, SGL); size_t el = sizeof(I2O_SGE_SIMPLE_ELEMENT); size_t buf = MAX_INBOUND_SIZE; size_t loop_end = hdr + SG_SIZE * el; /* first address the loop refuses to write */ size_t sense_at_full = hdr + SG_SIZE * el; /* where SG(sg,0,..) then writes the sense elem */ size_t sense_end = sense_at_full + el; printf("sizeof(I2O_MESSAGE_FRAME) = %zu (expect 12)\n", sizeof(I2O_MESSAGE_FRAME)); printf("offsetof(...SCB_EXECUTE, SGL) = %zu (expect 48)\n", hdr); printf("sizeof(I2O_SGE_SIMPLE_ELEMENT) = %zu (expect 8)\n", el); printf("asr_action stack buffer `struct Message' = %zu bytes\n", buf); printf("loop writes SGL elements [0..%d] -> max offset %zu\n", SG_SIZE - 1, loop_end - 1); printf("sense element after a FULL loop lands at [%zu, %zu)\n", sense_at_full, sense_end); printf("overflow past the %zu-byte buffer: %zu bytes\n", buf, sense_end > buf ? sense_end - buf : 0); printf("=> a single XPT_SCSI_IO whose data spans >= %zu physical runs\n" " (>= %zu KiB with 4K pages) drives the mandatory sense\n" " element write out of bounds. Buffer-cache I/O is capped\n" " by MAXPHYS=128KiB (~%d runs); CAM pass-through is not.\n", (size_t)SG_SIZE, (size_t)(SG_SIZE * 4), 128 / 4 + 1); return (0); } |