/*
 * DF-5679 fix validation demo.
 *
 * Compiles against EITHER the pristine icrc32.c (baseline) or a copy
 * patched by fix.diff (size_t length end-to-end). It forks; the child
 * calls iscsi_crc32(p, 0x100000009ULL) where p has exactly 9 valid
 * bytes ending at a PROT_NONE guard page.
 *
 *   baseline (truncating):  child reads only 9 bytes, returns silently
 *                           -> "FIX-BAD" / exit 1
 *   patched (size_t):       child honors 4 GiB + 9, faults on the guard
 *                           -> "FIX-OK"  / exit 0
 *
 * Build: cc -O2 -o fixdemo fixdemo.c <icrc32-object-under-test>
 * (link with icrc32.c directly; crc32.c not needed).
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <signal.h>

uint32_t iscsi_crc32(const void *buf, size_t size);

int
main(void)
{
	size_t ps = (size_t)sysconf(_SC_PAGESIZE);
	unsigned char *map = mmap(NULL, 2 * ps, PROT_READ | PROT_WRITE,
	    MAP_ANON | MAP_PRIVATE, -1, 0);

	if (map == MAP_FAILED) {
		perror("mmap");
		return (2);
	}
	mprotect(map + ps, ps, PROT_NONE);
	memset(map, 0xAB, ps);

	pid_t pid = fork();
	if (pid == 0) {
		uint32_t v = iscsi_crc32(map + ps - 9, 0x100000009ULL);
		printf("child: silently returned %08x after \"4GiB+9\" "
		    "request (length was truncated)\n", v);
		_exit(0);
	}
	int st = 0;
	waitpid(pid, &st, 0);
	if (WIFSIGNALED(st) &&
	    (WTERMSIG(st) == SIGSEGV || WTERMSIG(st) == SIGBUS)) {
		printf("FIX-OK: length honored -> child faulted on guard "
		    "page (sig %d)\n", WTERMSIG(st));
		return (0);
	}
	printf("FIX-BAD: no fault (status 0x%x); truncation still "
	    "present\n", st);
	return (1);
}
