DF-1727 / harness.c
/* * 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; } |