Heap overflow in linker_search_path() via over-long kldload module name
| Field | Value |
|---|---|
| ID | DF-0024 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:H |
| CWE | CWE-787 Out-of-bounds Write; CWE-120 Buffer Copy without Checking Size of Input |
| File | sys/kern/kern_linker.c |
| Lines | 1458, 1476, 1480 |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-06-29 |
| Reported | pending |
Summary
linker_search_path() kmalloc()s a MAXPATHLEN (1024) byte buffer and then
writes prefix + sep + name + ext + NUL into it without any length check. A
root caller driving a 1023-byte bare module name (the maximum copyinstr
allows at sys_kldload :798) through kldload โ linker_load_module โ
linker_search_path produces a write of ~1040 bytes into the 1024-byte buffer
โ a ~16-byte heap overflow into adjacent M_LINKER objects. The first overflow
is at strcpy(result + prefix_len, name) (:1476); a second at
strcpy(result + result_len, *ext) (:1480). Gated behind SYSCAP_NOKLD
(root), so this is a defense-in-depth / local-DoS finding: root can already
kldload an arbitrary .ko for kernel code execution.
Root cause
buf = kmalloc(MAXPATHLEN, M_LINKER, M_WAITOK); /* :1458 1024 bytes */
...
strcpy(result + prefix_len, name); /* :1476 no bounds check */
result_len = strlen(result);
for (ext = exts; *ext != NULL; ext++) {
strcpy(result + result_len, *ext); /* :1480 more */
name arrives at strlen up to MAXPATHLEN-1 (1023) from copyinstr at
:798, passed unmodified through sys_kldload (:806-812, the else branch
where modname = file) โ linker_load_module (:1522-1537) โ
linker_search_path (:1536/:1527). With the default linker_path
"/boot/kernel" (prefix_len=12, sep=1), the assembly is
12 + 1 + 1023 + 3 (".ko") + 1 (NUL) = 1040 bytes.
Threat model & preconditions
- Attacker position: requires
SYSCAP_NOKLD(root or root-equivalent) viasys_kldload(:794). - Privileges gained or impact: unintentional kernel heap corruption
(~16-byte overflow into adjacent
M_LINKERobjects) โ a local DoS (panic) and, with heap grooming (corrupt an adjacentlinker_filerefs/flags/userrefs), potentially a UAF/double-free primitive. Root can already load arbitrary.komodules, so it does not grant new privilege in the default threat model. It becomes meaningful under KLD-signature enforcement or a capsicum-restricted root with only the KLD capability. - Required config or capabilities: root (
SYSCAP_NOKLD); default kernel. - Reachability:
kldload(2)with a ~1023-byte bare module name.
Proof of concept
PoC source: findings/poc/DF-0024/kldload_overflow.c
Build & run (root, disposable VM)
cc -o kldload_overflow findings/poc/DF-0024/kldload_overflow.c ./kldload_overflow
Expected output
Kernel panic from heap corruption / slab assertion ("freed pointer ... was modified", malloc red-zone).
Impact
Real, unintentional kernel heap corruption, but root-only and dominated by root's existing ability to load arbitrary modules. Rated Low (defense-in-depth / local DoS).
Recommended fix
Refuse candidate paths whose assembly would exceed MAXPATHLEN (and compute
the max extension length once), returning ENAMETOOLONG:
--- a/sys/kern/kern_linker.c
+++ b/sys/kern/kern_linker.c
@@ -1458,6 +1458,8 @@
buf = kmalloc(MAXPATHLEN, M_LINKER, M_WAITOK);
cp = linker_path;
name_len = strlen(name);
+ size_t ext_max = 0;
+ for (ext = exts; *ext != NULL; ext++)
+ if (strlen(*ext) > ext_max)
+ ext_max = strlen(*ext);
for (;;) {
...
if (prefix_len + sep + name_len + ext_max + 1 > MAXPATHLEN) {
if (*ep == 0)
break;
cp = ep + 1;
continue;
}
result = buf;
strncpy(result, cp, prefix_len);
References
sys/kern/kern_linker.c:1458โkmalloc(MAXPATHLEN)buffer.sys/kern/kern_linker.c:1476,1480โ uncheckedstrcpyof name/ext.sys/kern/kern_linker.c:794โSYSCAP_NOKLDgate (root-only reachability).- CWE-787 Out-of-bounds Write; CWE-120 Buffer Copy without Checking Size.
Timeline
- 2026-06-29 Discovered during automated file-by-file audit of
sys/kern/kern_linker.c. - pending Reported to DragonFlyBSD security contact.