DF-0056
Heap overflow via unchecked p_filesz > p_memsz in PT_LOAD segment loading
| Field | Value |
|---|---|
| ID | DF-0056 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-787 Out-of-bounds Write |
| File | sys/kern/link_elf.c |
| Lines | 507-563 |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-06-29 |
| Reported | pending |
Summary
link_elf_load_file never validates p_filesz <= p_memsz for PT_LOAD segments.
The kmalloc'd buffer (mapsize) is derived from p_memsz (:541-543), but
vn_rdwr (:554) writes p_filesz bytes into it. If p_filesz > p_memsz, the
write overflows. Immediately after, bzero(segbase + p_filesz, p_memsz -
p_filesz) (:562-563) underflows (both Elf64_Xword/uint64) to a huge value,
zeroing far past the allocation. Attacker controls both the overflow size and the
data written (file bytes at p_offset). Root-only (SYSCAP_NOKLD); the cleanest
heap-overflow primitive in the ELF loaders.
Root cause
case PT_LOAD: /* :507 no p_filesz <= p_memsz check */
segs[nsegs] = phdr;
...
mapsize = round_page(segs[1]->p_vaddr + segs[1]->p_memsz) - trunc_page(segs[0]->p_vaddr); /* :543 */
ef->address = kmalloc(mapsize, M_LINKER, M_WAITOK); /* :546 */
...
vn_rdwr(UIO_READ, vp, segbase, segs[i]->p_filesz, ...); /* :554 writes p_filesz */
bzero(segbase + segs[i]->p_filesz, segs[i]->p_memsz - segs[i]->p_filesz); /* :562 underflow */
Recommended fix
--- a/sys/kern/link_elf.c
+++ b/sys/kern/link_elf.c
@@ -507,6 +507,11 @@
case PT_LOAD:
+ if (phdr->p_filesz > phdr->p_memsz) {
+ link_elf_error("p_filesz > p_memsz");
+ error = ENOEXEC;
+ goto out;
+ }
if (nsegs == 2) {
Timeline
- 2026-06-29 Discovered during automated file-by-file audit of
sys/kern/link_elf.c. - pending Reported to DragonFlyBSD security contact.