DragonFlyBSD Kernel Audit
DF-1191 / harness.c
← back to finding ↓ download raw
/*
 * DF-1191 harness — ciss_cam_complete unchecked sense_length
 * (userspace replica of sys/dev/raid/ciss/ciss.c:3248-3249)
 *
 * The controller returns an ErrorInfo struct whose sense_length is a u8
 * (0..255). The driver does:
 *     bzero(&csio->sense_data, SSD_FULL_SIZE);          // 32 bytes
 *     bcopy(&ce->sense_info[0], &csio->sense_data, ce->sense_length);
 * with NO min() against SSD_FULL_SIZE (32). A malicious controller returning
 * sense_length>32 overwrites adjacent union ccb fields (cdb_io, msg_ptr) and
 * the neighbouring heap object. This harness reproduces the overflow with a
 * canary guard to make it observable without ASAN.
 *
 * Build: cc -O2 -Wall -o harness harness.c
 * Run:   ./harness
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

#define SSD_FULL_SIZE 32	/* sizeof(struct scsi_sense_data) */

/* controller ErrorInfo fragment */
struct ciss_error_info_replica {
	uint8_t  sense_length;	/* u8: controller-controlled, 0..255 */
	uint8_t  sense_info[256];
};

/* csio->sense_data sits inside union ccb; place a canary right after it. */
struct csio_replica {
	uint8_t  sense_data[SSD_FULL_SIZE];
	uint8_t  canary[64];	/* stands in for cdb_io/msg_ptr/heap neighbor */
};

static int try(uint8_t sense_length)
{
	struct ciss_error_info_replica ce;
	struct csio_replica csio;

	memset(&ce, 0xA1, sizeof(ce));
	ce.sense_length = sense_length;
	memset(&csio, 0x00, sizeof(csio));
	memset(csio.canary, 0xCD, sizeof(csio.canary));	/* poison the neighbor */

	/* --- exact replica of ciss.c:3248-3249 --- */
	memset(&csio.sense_data, 0, SSD_FULL_SIZE);
	memcpy(&csio.sense_data, &ce.sense_info[0], ce.sense_length);	/* NO clamp */

	int overflow = 0;
	for (int i = SSD_FULL_SIZE; i < (int)sizeof(csio.canary); i++) {
		if (csio.canary[i - SSD_FULL_SIZE] != 0xCD) {
			overflow++;
		}
	}
	int overflow_bytes = 0;
	if (sense_length > SSD_FULL_SIZE)
		overflow_bytes = sense_length - SSD_FULL_SIZE;
	if (overflow_bytes > (int)sizeof(csio.canary))
		overflow_bytes = sizeof(csio.canary);

	printf("sense_length=%3u : %s (overflow into neighbor canary: %d byte%c, "
	       "expected up to %d past sense_data)\n",
	       sense_length,
	       overflow ? "OVERFLOW REPRODUCED" : "in-bounds",
	       overflow, overflow == 1 ? ' ' : 's', overflow_bytes);
	return overflow ? 1 : 0;
}

int main(void)
{
	printf("== DF-1191 ciss_cam_complete sense_length harness ==\n");
	printf("SSD_FULL_SIZE (sizeof scsi_sense_data) = %d\n\n", SSD_FULL_SIZE);

	int any = 0;
	any |= try(32);		/* boundary: exactly fits, no overflow */
	any |= try(64);		/* overflows by 32 */
	any |= try(255);	/* max u8: overflows by up to 223 */

	printf("\nFixed kernel clamps to imin(sense_length, SSD_FULL_SIZE) -> 0 overflow.\n");
	return any ? 0 : 1;
}