ttm_agp_backend.c is dead, un-compilable, half-ported code: excluded from every build (CONFIG_AGP undefined, asm/agp.h absent), and if ever revived carries a pages-vs-bytes 8x under-allocation (heap OOB write in the bind loop) plus an agp_memory leak on every AGP rebind
Summary
Three-layer reachability refutation (Makefile commented, kconfig undefined, header absent -> un-compilable; nm shows 0 agp_tt symbols in shipped drm.ko). Latent landmines if revived: num_pages passed where the native API expects BYTES (8x under-alloc -> OOB write of page pointers) + mem leak on rebind. Bytes-convert + free-before-overwrite fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5188 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | file | 5.2 KB | β raw | |
| VERDICT.md | file | 6.1 KB | β raw | |
| manifest.json | file | 1.1 KB | view raw | |
| runtime_absence.log | file | 742 B | view raw | |
| verdict.json | file | 4.4 KB | view raw |
DF-5188 β ttm_agp_backend.c: dead, un-compilable, half-ported AGP TTM backend (latent 8x under-allocation OOB + rebind leak if revived)
File: sys/dev/drm/ttm/ttm_agp_backend.c (150 LOC) β pass 2 (GLM 5.3)
Bottom line
The file is not part of any kernel or module build in this tree and
cannot compile as-is. It is a Linux TTM source that was partially
ported toward the DragonFly native AGP API (function renamed, arguments
reordered) and then abandoned β excluded from the build behind a
never-defined CONFIG_AGP. If a future change revives it (e.g. by adding
the missing LinuxKPI AGP shim, as FreeBSD's drm2 did), the code as written
contains:
- a pages-vs-bytes unit confusion at
ttm_agp_backend.c:60that would under-allocate the backing store by exactlyPAGE_SIZE/sizeof(paddr_t)= 8x on x86-64 while the bind loop at:65-72writesnum_pagespage pointers into it β a kernel heap OOB write; and - an
agp_memoryleak on every rebind (overwrite at:73without free; unbind of a bound region returns at:91without freeing).
Today: zero reachability. No syscall/ioctl/mmap path reaches a single instruction of this file on any DragonFly configuration.
Provenance (why it is dead)
| Fact | Evidence |
|---|---|
| Excluded from the only Makefile that lists it | sys/dev/drm/drm/Makefile:110-112 β # if CONFIG_AGP is set / #SRCS+= ttm_agp_backend.c (commented out) |
CONFIG_AGP never defined |
sys/dev/drm/kconfig.h (15 defines, no CONFIG_AGP) β IS_ENABLED(CONFIG_AGP) = 0 (sys/dev/drm/include/linux/kconfig.h:36-39) |
| Only in-tree callers compiled out | sys/dev/drm/radeon/radeon_ttm.c:705,769,824 all inside #if IS_ENABLED(CONFIG_AGP) |
| Header it includes does not exist | ttm_agp_backend.c:43 #include <asm/agp.h> β no asm/agp.h anywhere under sys/ (only sys/dev/drm/include/linux/agp_backend.h, which contains nothing but a forward declaration, lines 30-31) |
| Struct fields it uses exist in no visible type | mem->pages, mem->page_count, mem->is_flushed, mem->type, mem->is_bound (:64-76,89-91) β no definition of struct agp_memory with those members is reachable from a DRM build; native struct agp_memory (sys/dev/agp/agppriv.h:55-65) has am_* members only |
| Call arity matches no available prototype | agp_free_memory(mem) 1-arg (:92), agp_bind_memory(mem, off) 2-arg (:78), agp_unbind_memory(mem) 1-arg (:91) vs native agpvar.h:100-116 which declares 2-, 3-, and 2-arg forms |
| Runtime artifact clean | nm /boot/kernel/drm.ko | grep -ci agp_tt β 0 (guest, 22 MB module built Jun 29 2026); only agp hits are kld-dependency metadata (_mod_metadata_md_drm_on_agp) |
| No local post-import edits | git log --oneline -- sys/dev/drm/ttm/ttm_agp_backend.c β single vendor-drop commit 6cc80ee9 |
The latent unit confusion (the landmine)
- Native API:
void *agp_alloc_memory(device_t dev, int type, vm_size_t bytes)(sys/dev/agp/agpvar.h:100). Bytes. Enforced page-aligned and checked againstas_maxmeminagp_generic_alloc_memory(sys/dev/agp/agp.c:456-464); backing store isvm_object_allocate(OBJT_DEFAULT, atop(round_page(size)))(sys/dev/agp/agp.c:472) β i.e. sized from the byte count. - The half-ported call:
agp_alloc_memory(agp_be->bridge, AGP_USER_MEMORY, ttm->num_pages)(ttm_agp_backend.c:60) β page count in a bytes parameter. Anum_pages-page BO would requestnum_pagesbytes =num_pages/8pages of backing on x86-64. - The consumer:
mem->pages[mem->page_count++] = page;fori < ttm->num_pages(ttm_agp_backend.c:65-72). Whoever wires the shim that providesmem->pagesfrom the under-sized object gets an 8x OOB write of attacker-influenced page pointers. - (Side note:
agp_generic_alloc_memoryalso rejectstype != 0(agp.c:468-471), soAGP_USER_MEMORY= 0x10000 would fail there too β further proof the port was never finished against the generic layer.)
The latent rebind leak
ttm_agp_unbindon a bound region returns at:91right afteragp_unbind_memory()β it neither freesagp_be->memnor NULLs it.ttm_agp_bindunconditionally overwritesagp_be->mem = memat:73(and also leaves it set whenagp_bind_memoryfails at:78-80).- Net effect if revived: every unbindβbind cycle (AGP aperture shuffle) or
failed bind followed by retry leaks one
agp_memoryplus its page array β unbounded kernel memory exhaustion from an unprivileged DRM client.
Reproduce / verify absence (what was actually run)
No PoC can exist for a function that is in no binary. Verification performed:
# source level (host)
rg -n "ttm_agp" sys/dev/drm --glob Makefile # only the commented line
rg -n "CONFIG_AGP" sys/dev/drm/kconfig.h # no match
find sys -name 'agp.h' -o -name 'agp_backend.h # no asm/agp.h anywhere
# runtime level (guest)
nm /boot/kernel/drm.ko | grep -ci agp_tt # -> 0
Recommended disposition
Either delete the file (and the #SRCS+= comment block), or β if AGP
support is ever revived β finish the port: add a real LinuxKPI AGP shim,
multiply by PAGE_SIZE at :60, free the previous agp_be->mem at :73,
and free+NULL in the bound branch of ttm_agp_unbind (:90-91).
DF-5188 β VERDICT
Status: untested (by construction β the target code exists in no kernel artifact).
Impact: none today; latent 8x heap OOB write + unbounded agp_memory leak if the backend is ever compiled in.
Confidence: certain (on every claim below; each is source- or binary-verifiable in one command).
1. What was audited
sys/dev/drm/ttm/ttm_agp_backend.c β full 150-line read (pass 2, GLM 5.3).
Pass 1 recorded no findings for this file (DB check: findings has zero rows
for it; the only ttm-family findings are on ttm_bo/ttm_bo_util/ttm_bo_vm/
ttm_page_alloc/ttm_execbuf_util/ttm_bo_manager/ttm_module/ttm_memory β see
DF-1445..DF-5138, none on this file).
2. Reachability β refuted at three independent layers
- Build system: the only Makefile referencing the file is
sys/dev/drm/drm/Makefile, where bothdrm_agpsupport.candttm_agp_backend.csit commented out under the banner# if CONFIG_AGP is set(Makefile:110-112). No other Makefile in the tree mentions it (rg -l ttm_agp sys/ --glob Makefileβ that one file). - Kconfig:
sys/dev/drm/kconfig.hdefines 15 CONFIG_ macros, none of themCONFIG_AGP; thereforeIS_ENABLED(CONFIG_AGP)expands to 0 (sys/dev/drm/include/linux/kconfig.h:36-39), and the only in-tree callers βsys/dev/drm/radeon/radeon_ttm.c:705,769,824β compile theirttm_agp_tt_create/populate/unpopulatecall sites out entirely. - The file cannot compile even if added:
#include <asm/agp.h>(ttm_agp_backend.c:43) β no such header exists anywhere undersys/; the only visiblestruct agp_memorydeclarations are the nativesys/dev/agp/agppriv.h:55-65(membersam_id/am_size/am_type/am_obj/ am_physical/am_offset/am_is_boundβ none of thepages,page_count,is_flushed,type,is_boundmembers used atttm_agp_backend.c:64-91) and the empty forward declaration insys/dev/drm/include/linux/agp_backend.h:30-31. Call arities also match nothing:agp_free_memory(mem)(1 arg,:92),agp_bind_memory(mem, off)(2 args,:78),agp_unbind_memory(mem)(1 arg,:91) versus the native prototypesagpvar.h:100-116(2, 3, and 2 args).
Runtime confirmation (guest, dfbsd-qemu/vm.sh run_root, no mutation):
nm /boot/kernel/drm.ko | grep -ci agp_tt β 0. The 22,447,656-byte
/boot/kernel/drm.ko built Jun 29 2026 contains no ttm_agp_* symbol; the
only agp-matching strings are kld dependency metadata
(_mod_metadata_md_drm_on_agp). See runtime_absence.log.
Conclusion: no syscall, ioctl, mmap, net, or mount path can reach any instruction of this file on any DragonFly configuration, current tree.
3. The latent hazards (why this is filed at Info rather than dropped)
The file shows signs of an abandoned half-port to the native API: the
alloc call was renamed from Linux's agp_allocate_memory(agp_bridge,
num_pages, AGP_USER_MEMORY) to agp_alloc_memory(agp_be->bridge,
AGP_USER_MEMORY, ttm->num_pages) β the native name and argument order
(agpvar.h:100) but with a page count fed to a vm_size_t bytes
parameter:
agp_generic_alloc_memory(sys/dev/agp/agp.c:456-473) page-align-checks the byte size, charges it againstas_maxmem, and sizes the backingvm_objectviaatop(round_page(size))(agp.c:472).- On x86-64, passing
num_pagesbytes for anum_pages-page BO under-allocates by exactlyPAGE_SIZE / 8 = 8x. - The bind loop then stores
num_pagespage pointers (ttm_agp_backend.c:65-72). Any future shim that derivesmem->pagesfrom the (under-sized) native object turns this into a kernel heap OOB write with attacker-influenced contents (page pointers of an unprivileged-created TTM BO).
Second latent hazard β lifecycle leak: ttm_agp_unbind returns at :91
after agp_unbind_memory() on a bound region without freeing or NULLing
agp_be->mem, and ttm_agp_bind overwrites agp_be->mem at :73
without freeing the previous allocation (and leaves it set on the
agp_bind_memory failure path :78-80). Revived, every AGP aperture
rebind leaks one agp_memory + page array β unbounded kernel memory
exhaustion by an unprivileged DRM client.
FreeBSD's drm2 has exactly the LinuxKPI AGP shim this file is missing, so "revive CONFIG_AGP" is a plausible future change that would walk into both landmines unless this is on record.
4. Phase V
Not applicable in the strong sense: the trigger target (compiled code) does
not exist in any kernel image; there is nothing to build or run. The
verification that was performed is the three-layer reachability refutation
above plus the runtime symbol-absence check. Per the orchestrator's status
vocabulary this is recorded as untested with impact: latent and
attempts: 1 (the nm check). No fix.diff: the fix is deletion or
completion of the port, not a one-line patch to live code; the recommended
disposition in README.md Β§"Recommended disposition" covers both.
5. Classes considered and killed (pass-2 depth)
- Populate/pages/counts OOB (
:60-72): real as latent unit bug, dead as shipped β this finding. - Unbind/bind/destroy double-free (
:85-106): destroy path (ttm_tt_destroyβ unbind-then-destroy, and the:102-103re-unbind) ends inagp_free_memoryon the not-bound member β no double-free even hypothetically; the defect is the leak, covered above. ttm_agp_tt_create(:114-134): M_WAITOK NULL-check is dead code, error path frees correctly β nothing.ttm_agp_tt_populate/unpopulate(:137-149): thin wrappers intottm_pool_(un)populateβ that surface is already filed (DF-1663, DF-4507); nothing new here.- Build gating: refuted above (Makefile:110-112, kconfig.h, radeon guards).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
(No fix.diff for this item: documented recommendation only - see Fix above.)
Fix verification
not_testablen/a β dead code, nothing to validate a fix against
Confirmed kernel references
- sys/dev/drm/ttm/ttm_agp_backend.c:43
- sys/dev/drm/ttm/ttm_agp_backend.c:60
- sys/dev/drm/ttm/ttm_agp_backend.c:65-72
- sys/dev/drm/ttm/ttm_agp_backend.c:73
- sys/dev/drm/ttm/ttm_agp_backend.c:78-80
- sys/dev/drm/ttm/ttm_agp_backend.c:89-94
- sys/dev/drm/drm/Makefile:110-112
- sys/dev/agp/agpvar.h:100
- sys/dev/agp/agp.c:456-473
- sys/dev/agp/agppriv.h:55-65
- sys/dev/drm/radeon/radeon_ttm.c:705
- sys/dev/drm/radeon/radeon_ttm.c:769
- sys/dev/drm/radeon/radeon_ttm.c:824
Detail
Evidence (decisive lines)
findings/poc/DF-5188/VERDICT.md sections 2-3 (three-layer reachability refutation with path:line + latent hazard derivation); runtime_absence.log (nm drm.ko: 0 agp_tt symbols, guest uname 6.5-DEVELOPMENT #0 Thu Jul 2 06:02:54 UTC 2026); README.md provenance table; sys/dev/drm/drm/Makefile:110-112; sys/dev/drm/kconfig.h (no CONFIG_AGP); sys/dev/drm/radeon/radeon_ttm.c:703-708,767-771,822-826; sys/dev/agp/agpvar.h:100-116; sys/dev/agp/agppriv.h:55-65; sys/dev/agp/agp.c:456-473; sys/dev/drm/include/linux/agp_backend.h:30-31
PoC changes
n/a β no PoC exists or can exist for code present in no binary; the verification artifact is the reachability refutation (source citations + nm symbol-absence on the shipped drm.ko)
Verified recommended fix
Delete ttm_agp_backend.c (and the commented #SRCS block), or if AGP support is revived: fix the unit confusion (agp_alloc_memory(bridge, type, (vm_size_t)ttm->num_pages << PAGE_SHIFT) at :60), free the prior agp_be->mem before the overwrite at :73, and free+NULL in the is_bound branch of ttm_agp_unbind (:90-91)
Verdict
ttm_agp_backend.c is dead code with zero runtime reachability: it is commented out of the only Makefile that lists it (sys/dev/drm/drm/Makefile:110-112, '# if CONFIG_AGP is set'), CONFIG_AGP is not among the 15 defines in sys/dev/drm/kconfig.h so IS_ENABLED(CONFIG_AGP)=0 compiles out the sole in-tree callers (radeon_ttm.c:705,769,824), and the file cannot compile anyway (asm/agp.h at :43 exists nowhere under sys/; the Linux-style struct agp_memory members pages/page_count/is_flushed/type/is_bound used at :64-91 exist in no header a DRM build sees; agp_free_memory/agp_bind_memory/agp_unbind_memory arities at :78,:91-92 match no available prototype). Runtime-confirmed: nm /boot/kernel/drm.ko | grep -ci agp_tt = 0. The file is an abandoned half-port toward the native API whose landmines are on record for whoever revives it: (1) ttm_agp_backend.c:60 passes ttm->num_pages (a page count) as the vm_size_t bytes parameter of agp_alloc_memory (agpvar.h:100; agp.c:456-473 sizes the backing vm_object from that byte count) β an exact 8x under-allocation on x86-64 while the bind loop at :65-72 writes num_pages page pointers, i.e. a kernel heap OOB write once a shim exists; (2) ttm_agp_unbind returns at :91 on a bound region without freeing or NULLing agp_be->mem and ttm_agp_bind overwrites agp_be->mem at :73 without freeing the prior allocation β an unbounded agp_memory leak per AGP rebind cycle. Filed Info: no demonstrated impact on any current kernel configuration.
No comments yet.