⬢ DragonFlyBSD Kernel Audit
DF-3824 / sg_layout_proof.c
← back to finding ↓ download raw
/*
 * 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);
}