/*
 * Fixed-bcmp truncation check: huge lengths must NEVER yield a false
 * "equal".  With the fix, bcmp actually compares the requested range;
 * inside the 3-page guarded arena a >=2^31 request therefore faults
 * (as memcmp would) instead of returning 0.
 */
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>

int bcmp_fixed(const void *, const void *, size_t);
#define PS 4096

static int child_call(size_t length, unsigned char *b1, unsigned char *b2)
{
    pid_t pid = fork();
    if (pid == 0) {
        volatile int r = bcmp_fixed(b1, b2, length);
        printf("  returned %d (clean)\n", r);
        _exit(r == 0 ? 3 : 4);   /* 3 == false-equal-with-clean-exit */
    }
    int st = 0; waitpid(pid, &st, 0);
    if (WIFSIGNALED(st)) return -WTERMSIG(st);
    return WEXITSTATUS(st);
}

int main(void)
{
    unsigned char *a1 = mmap(NULL, 3*PS, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
    unsigned char *a2 = mmap(NULL, 3*PS, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
    if (a1 == MAP_FAILED || a2 == MAP_FAILED) return 2;
    mprotect(a1, PS, PROT_NONE); mprotect(a1 + 2*PS, PS, PROT_NONE);
    mprotect(a2, PS, PROT_NONE); mprotect(a2 + 2*PS, PS, PROT_NONE);
    unsigned char *b1 = a1+PS, *b2 = a2+PS;
    memset(b1, 0x41, PS); memset(b2, 0x41, PS);

    printf("fixed bcmp(2^32) on guarded pages: ");
    int r1 = child_call((size_t)1<<32, b1, b2);
    printf("outcome=%s\n", r1 == -11 ? "SIGSEGV while genuinely comparing (correct)" :
           r1 == 3 ? "FALSE EQUAL (bug remains)" : "other");
    printf("fixed bcmp(2^32+8) on guarded pages: ");
    int r2 = child_call(((size_t)1<<32)+8, b1, b2);
    printf("outcome=%s\n", r2 == -11 ? "SIGSEGV while genuinely comparing (correct)" :
           r2 == 3 ? "FALSE EQUAL (bug remains)" : "other");
    printf("RESULT: %s\n", (r1 == -11 && r2 == -11) ? "FIX-PASS (no false equal)" : "FIX-FAIL");
    return 0;
}
