⬢ DragonFlyBSD Kernel Audit
DF-4541 / hptinit.c
← back to finding ↓ download raw
/*
 * hptinit.c - trigger for DF-4541
 *
 * hptmv: HPT_IOCTL_SET_ARRAY_STATE / AS_INITIALIZE_START queues the DPC
 * with the wrong flag (VERIFY instead of INITIALIZE) at
 * sys/dev/raid/hptmv/ioctl.c:580, so rf_initializing is never set; the
 * ioctl thread then spins in 1-tick tsleep loops for 80 seconds
 * (hz*80 iterations, ioctl.c:583-589) and returns 0 (success) although
 * nothing was initialized.
 *
 * The hptmv ioctl channel is the writable sysctl dev.hptmv.status
 * (hptproc.c:614-617, CTLFLAG_RW): the packet is an HPT_IOCTL_PARAM
 * (hptintf.h:744-753) followed by the raw in/out buffers.
 *
 * Usage: ./hptinit <array-id>
 * (Requires a HighPoint RocketRAID 18xx/22xx SATA controller with a
 * valid RAID1/5 array id reported by HPT_IOCTL_GET_LOGICAL_DEVICES.
 * Root only - sysctl writes require privilege.)
 *
 * Expected on vulnerable driver, id=<valid array>, state=AS_INITIALIZE_START:
 *   - ioctl takes ~80 seconds (80 * hz 1-tick tsleep iterations)
 *   - returns success (sysctl write returns the written length)
 *   - driver log shows "Verifying started." (VERIFY event), NOT
 *     "Initializing started." -- initialization never happened.
 */
#include <sys/types.h>
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define HPT_IOCTL_MAGIC		0xA1B2C3D4
#define HPT_IOCTL_SET_ARRAY_STATE	(15 + 0xFF00)
#define AS_INITIALIZE_START	7

typedef unsigned int  DWORD;
typedef unsigned long ULONG_PTR;
typedef void         *LPVOID;
typedef DWORD        *LPDWORD;

typedef struct _HPT_IOCTL_PARAM {
	DWORD   Magic;
	DWORD   dwIoControlCode;
	LPVOID  lpInBuffer;
	DWORD   nInBufferSize;
	LPVOID  lpOutBuffer;
	DWORD   nOutBufferSize;
	LPDWORD lpBytesReturned;
} HPT_IOCTL_PARAM;

#pragma pack(1)
typedef struct {
	DWORD idArray;		/* DEVICEID */
	DWORD state;		/* AS_INITIALIZE_START */
} HPT_SET_STATE_PARAM_T;
#pragma pack()

int main(int argc, char **argv)
{
	HPT_IOCTL_PARAM p;
	HPT_SET_STATE_PARAM_T in;
	unsigned char packet[sizeof(p) + sizeof(in)];
	char out[16];
	size_t len;
	time_t t0, t1;

	if (argc != 2) {
		fprintf(stderr, "usage: %s <array-device-id>\n", argv[0]);
		return 2;
	}

	memset(&p, 0, sizeof(p));
	p.Magic = HPT_IOCTL_MAGIC;
	p.dwIoControlCode = HPT_IOCTL_SET_ARRAY_STATE;
	p.lpInBuffer = &in;
	p.nInBufferSize = sizeof(in);
	p.lpOutBuffer = out;
	p.nOutBufferSize = 0;
	p.lpBytesReturned = NULL;

	memset(&in, 0, sizeof(in));
	in.idArray = (DWORD)strtoul(argv[1], NULL, 0);
	in.state = AS_INITIALIZE_START;

	/* the sysctl handler wants the flat packet: param struct then in buf */
	memcpy(packet, &p, sizeof(p));
	memcpy(packet + sizeof(p), &in, sizeof(in));
	len = sizeof(packet);

	t0 = time(NULL);
	if (sysctlbyname("dev.hptmv.status", NULL, NULL, packet, len) < 0) {
		perror("sysctl dev.hptmv.status (write)");
		return 1;
	}
	t1 = time(NULL);
	printf("SET_ARRAY_STATE(AS_INITIALIZE_START) returned SUCCESS in %lld s\n",
	       (long long)(t1 - t0));
	printf("BUG if: elapsed ~= 80s and dmesg says 'Verifying started.'\n");
	return 0;
}