/*
 * DF-1727 - ttm_bo_vm.c uninitialized kernel pages returned to userspace.
 *
 * Root cause is in the DragonFly linuxkpi GFP shim, not ttm itself:
 *
 *   sys/dev/drm/include/linux/gfp.h:64  alloc_page(int flags) {
 *       ...
 *       return vm_page_alloczwq(0, VM_ALLOC_NORMAL|VM_ALLOC_SYSTEM|
 *                               VM_ALLOC_INTERRUPT);
 *   }
 *
 * The flags argument is IGNORED. In particular __GFP_ZERO (mapped to
 * M_ZERO at gfp.h:45) is dropped. The call always passes the same
 * fixed flag set, which does NOT include VM_ALLOC_ZERO.
 *
 *   sys/dev/drm/include/linux/gfp.h:85  alloc_pages() calls
 *       vm_page_alloc_contig(..., VM_MEMATTR_DEFAULT) -- no zero.
 *
 * Caller chain that wanted zeroing:
 *   ttm_tt.c:67     page_flags |= TTM_PAGE_FLAG_ZERO_ALLOC (for type_device)
 *   ttm_page_alloc.c:738-739  if (ZERO_ALLOC) gfp_flags |= __GFP_ZERO;
 *   ttm_page_alloc.c:749      p = alloc_page(gfp_flags);  <-- __GFP_ZERO dropped
 *
 * So when the cached-page pool path (no pool, lines 742-757) runs on a
 * cold pool or drained pool, freshly allocated pages are NOT zeroed.
 * They are then mapped to userspace via ttm_bo_vm_fault_dfly
 * (ttm_bo_vm.c:690 m = ttm->pages[OFF_TO_IDX(offset)]; 714 *mres = m;).
 * Result: stale kernel memory disclosure to user. Repeatable, info leak,
 * KASLR defeat.
 *
 * This harness reproduces the flags-dropping logic.
 */
#include <stdio.h>
#include <stdint.h>

/* Constants lifted from sys/dev/drm/include/linux/gfp.h and sys/vm/vm_page.h */
#define M_ZERO          0x0100
#define VM_ALLOC_NORMAL    0x01
#define VM_ALLOC_SYSTEM   0x02
#define VM_ALLOC_INTERRUPT 0x04
#define VM_ALLOC_ZERO     0x08

#define __GFP_ZERO  M_ZERO
#define GFP_USER    0
#define GFP_KERNEL  0

int alloc_page_shim_dropped_zero(int flags)
{
    /* verbatim emulation of gfp.h:64-72 alloc_page() */
    int passed = VM_ALLOC_NORMAL | VM_ALLOC_SYSTEM | VM_ALLOC_INTERRUPT;
    (void)flags;   /* the bug: caller's flags (incl __GFP_ZERO) are unused */
    return passed; /* has no VM_ALLOC_ZERO */
}

int main(void)
{
    int want = GFP_KERNEL | __GFP_ZERO;   /* caller asks for zeroing */
    int got  = alloc_page_shim_dropped_zero(want);

    printf("=== DF-1727 linuxkpi alloc_page() drops __GFP_ZERO harness ===\n");
    printf("Caller requested gfp_flags=0x%x (incl __GFP_ZERO=M_ZERO=0x%x)\n",
           want, __GFP_ZERO);
    printf("alloc_page() actually passed vm_page_alloczwq flags=0x%x\n", got);
    printf("VM_ALLOC_ZERO bit (0x%x) present in passed flags? %s\n",
           VM_ALLOC_ZERO, (got & VM_ALLOC_ZERO) ? "yes" : "NO");
    printf("\n");

    if (!(got & VM_ALLOC_ZERO)) {
        printf("VERDICT: BUG CONFIRMED. The shim's flags argument is parsed\n"
               "        only for GFP_DMA32 and otherwise discarded, so\n"
               "        __GFP_ZERO never reaches the allocator. Cold-pool\n"
               "        TTM page allocations map stale/recycled kernel\n"
               "        memory into userspace via ttm_bo_vm_fault_dfly.\n");
        return 0;
    }
    printf("VERDICT: not reproduced.\n");
    return 1;
}
