โฌข DragonFlyBSD Kernel Audit
โ† dashboard
DF-0024

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

sys/kern/kern_linker.c:

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) via sys_kldload (:794).
  • Privileges gained or impact: unintentional kernel heap corruption (~16-byte overflow into adjacent M_LINKER objects) โ€” a local DoS (panic) and, with heap grooming (corrupt an adjacent linker_file refs/flags/ userrefs), potentially a UAF/double-free primitive. Root can already load arbitrary .ko modules, 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).

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

Timeline

  • 2026-06-29 Discovered during automated file-by-file audit of sys/kern/kern_linker.c.
  • pending Reported to DragonFlyBSD security contact.