/*
 * DF-1257 harness — amdgpu_vm_bo_map() offset+size integer overflow -> OOB.
 *
 * sys/dev/drm/amd/amdgpu/amdgpu_vm.c:
 *   amdgpu_vm_bo_map (:2511):  if (bo && offset + size > amdgpu_bo_size(bo))
 *                                                  return -EINVAL;
 *   amdgpu_vm_bo_replace_map (:2576): same check.
 *
 * `offset` and `size` are uint64. The check `offset + size > bo_size` wraps
 * mod 2^64: offset = 0xFFFFFFFFFFFFF000, size = 0x2000 -> sum = 0x1000, which
 * is <= a 0x2000 BO, so the check PASSES and mapping->offset (line 2532 /
 * 2595) stores the HUGE offset 0xFFFFFFFFFFFFF000.
 *
 * Later, amdgpu_vm_bo_split_mapping (:1988):
 *   pfn = mapping->offset >> PAGE_SHIFT;     -> 0x000FFFFFFFFFFFFF
 *   ...
 *   addr = pages_addr[pfn];                  (:2028)  -> OOB read ~2^52 elts
 *
 * Kernel trigger requires an amdgpu GPU + an unprivileged user issuing the
 * AMDGPU_VM ioctl with the wrapped offset — absent from this QEMU guest
 * (no AMD GPU, no /dev/dri/renderD128). This harness replicates the bounds
 * check and the pfn computation, proving the overflow -> OOB math.
 */
#include <stdio.h>
#include <stdint.h>

#define PAGE_SHIFT 12

static int bo_map_check(uint64_t offset, uint64_t size, uint64_t bo_size) {
    /* amdgpu_vm.c:2511 verbatim */
    if (offset + size > bo_size)
        return -1;  /* rejected */
    return 0;       /* accepted */
}

int main(void) {
    uint64_t bo_size  = 0x2000;                 /* a 8 KiB BO */
    uint64_t offset   = 0xFFFFFFFFFFFFF000ULL;  /* near UINT64_MAX, page-aligned */
    uint64_t size     = 0x2000;                 /* page-aligned, non-zero */

    /* page-mask alignment gates (amdgpu_vm.c:2504-2506) — all pass:
     * offset & 0xfff == 0, size & 0xfff == 0, size != 0. */
    int align_ok = ((offset & 0xfff) == 0) && ((size & 0xfff) == 0) && (size != 0);
    printf("alignment gate (amdgpu_vm.c:2504): %s\n", align_ok ? "PASS" : "FAIL");

    uint64_t sum = offset + size;               /* wraps mod 2^64 */
    printf("offset = 0x%016llx\n", (unsigned long long)offset);
    printf("size   = 0x%016llx\n", (unsigned long long)size);
    printf("offset + size (uint64) = 0x%016llx   (wrapped!)\n", (unsigned long long)sum);
    printf("bo_size = 0x%016llx\n", (unsigned long long)bo_size);

    int rc = bo_map_check(offset, size, bo_size);
    printf("bounds check (amdgpu_vm.c:2511) result: %s\n", rc == 0 ? "ACCEPT (bug)" : "reject");

    if (rc == 0) {
        /* mapping->offset stored as the huge value (amdgpu_vm.c:2532) */
        uint64_t stored_offset = offset;
        uint64_t pfn = stored_offset >> PAGE_SHIFT;     /* amdgpu_vm.c:1988 */
        printf("stored mapping->offset = 0x%016llx\n", (unsigned long long)stored_offset);
        printf("pfn = mapping->offset >> %d = 0x%016llx\n", PAGE_SHIFT, (unsigned long long)pfn);
        printf("pages_addr[pfn] would read at index 0x%016llx -> MASSIVE OOB read\n",
               (unsigned long long)pfn);

        printf("\nPRIMITIVE CONFIRMED: wrapped offset+size passes the bounds check; stored offset\n");
        printf("yields pfn ~ 2^52 -> OOB read in pages_addr[]. Bug is REAL.\n");
        return 0;
    }
    printf("\nUNEXPECTED: check rejected (no overflow)\n");
    return 1;
}
