/*
 * Truncation harness: bcmp.c:58  `int len = length;`  (size_t -> int).
 *
 * Demonstrates two distinct breaks for lengths >= 2^31 on LP64:
 *   (A) length == 2^32        -> len == 0  -> returns 0 "equal" immediately
 *                                 (bcmp.c:62-63) with ZERO bytes compared.
 *   (B) length == 2^32 + k    -> len == k  -> compares k bytes, returns
 *                                 "equal" for buffers that differ beyond k.
 *   (C) length == 2^31 (0x80000000) -> len == INT_MIN; the loop counter
 *                                 `len -= 8` (bcmp.c:88/114) overflows
 *                                 (UB) and wraps positive -> bcmp walks
 *                                 FORWARD unbounded -> hits the guard page
 *                                 -> SIGSEGV.  (Child process per case.)
 *
 * Buffers are placed in guarded pages so (C) is contained to the child.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>

int df_bcmp(const void *, const void *, size_t);

#define PS 4096

static int run_child(size_t length, unsigned char *b1, unsigned char *b2)
{
    pid_t pid = fork();
    if (pid == 0) {
        volatile int r = df_bcmp(b1, b2, length);
        printf("  child: df_bcmp(b1,b2,0x%zx) = %d  (survived)\n", length, r);
        _exit(0);
    }
    int st = 0;
    waitpid(pid, &st, 0);
    if (WIFSIGNALED(st)) return -WTERMSIG(st);
    return 0;
}

int main(void)
{
    setvbuf(stdout, NULL, _IONBF, 0);
    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) { perror("mmap"); return 2; }
    if (mprotect(a1, PS, PROT_NONE) || mprotect(a1 + 2 * PS, PS, PROT_NONE) ||
        mprotect(a2, PS, PROT_NONE) || mprotect(a2 + 2 * PS, PS, PROT_NONE)) {
        perror("mprotect"); return 2;
    }
    unsigned char *b1 = a1 + PS, *b2 = a2 + PS;
    memset(b1, 0x41, PS);
    memset(b2, 0x41, PS);

    printf("== (A) length = 2^32 : expect FALSE-EQUAL (returns 0, compared nothing)\n");
    int r = df_bcmp(b1, b2, (size_t)1 << 32);
    printf("  df_bcmp(b1,b2,2^32) = %d   -> %s\n", r,
           r == 0 ? "FALSE EQUAL (bug)" : "ok");
    int badA = (r == 0);

    printf("== (B) length = 2^32+8 : compare 8 bytes only, differ at byte 8\n");
    b2[8] = 0x42;   /* differ inside first 16 bytes, beyond len==8 window */
    r = df_bcmp(b1, b2, ((size_t)1 << 32) + 8);
    printf("  df_bcmp(b1,b2,2^32+8) = %d  -> %s (bcmp.c:88 loop ran with len=8)\n",
           r, r == 0 ? "FALSE EQUAL (bug)" : "ok");
    int badB = (r == 0);

    printf("== (C) length = 2^31 : int overflow in `len -= 8` -> unbounded forward walk\n");
    b2[8] = 0x41;   /* undo (B)'s mutation so both pages are identical again */
    int sig = run_child((size_t)1 << 31, b1, b2);
    printf("  child outcome: %s\n", sig == -11 ? "SIGSEGV in df_bcmp (unbounded walk proven)" :
           sig == 0 ? "survived (no crash)" : "other signal");
    int badC = (sig == -11);

    printf("RESULT: %s%s%s (A=%d B=%d C=%d)\n",
           badA ? "false-equal@2^32 " : "", badB ? "false-equal@2^32+8 " : "",
           badC ? "crash@2^31" : "", badA, badB, badC);
    return (badA || badB || badC) ? 0 : 1;
}
