β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-0939

Unbounded bcopy of sv_name into 256-byte stack buffer in procfs_dotype (latent; no in-tree sv_name >= 256B)

Field Value
ID DF-0939
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:L
CWE CWE-787 Out-of-bounds Write; CWE-125 Out-of-bounds Read
File sys/vfs/procfs/procfs_type.c
Lines 54, 64-65, 70-71
Area vfs
Confidence likely
Discovered 2026-07-05
Reported pending
Known CVE none
CVE match dfly_specific

Summary

procfs_dotype() copies p->p_sysent->sv_name into a fixed char mebuffer[256] on the kernel stack with bcopy() using strlen() as the length and no bound against sizeof(mebuffer). The sysentvec API (sys/sysent.h:73) imposes no length limit on sv_name; if any in-tree or kld-registered sysentvec ever sets sv_name to a string of β‰₯ 256 bytes, every read of /proc/<pid>/etype overflows the stack. Not exploitable in the stock tree today β€” the only two sysentvecs are elf64_dragonfly_sysvec (sv_name="DragonFly ELF64", 15 bytes) and null_sysvec (sv_name="null", 4 bytes) β€” but the missing bound is a latent kernel-stack-overflow defect that becomes a local DoS the moment a longer sv_name ships.

Root cause

procfs_type.c:54 declares char mebuffer[256];. At :64 len = strlen(p->p_sysent->sv_name); is computed with no clamp. At :65 bcopy(p->p_sysent->sv_name, mebuffer, len); copies len bytes β€” overflowing mebuffer when len > 256. At :70 mebuffer[len++] = '\n'; writes one past the buffer when len >= 256. At :71 uiomove(mebuffer, len, uio) then reads len bytes from the overflown stack region and ships them to userspace.

The sv_name field is char * with no documented or enforced max (sys/sysent.h:73). strnlen is available in-kernel (sys/libkern.h:138) but is not used here.

Threat model & preconditions

  • Attacker position: Any unprivileged local user able to open /proc/<pid>/etype (mode 0444).
  • Privileges gained or impact: Stack-smashing / DoS if a loaded sysentvec has sv_name β‰₯ 256 bytes. None in the stock tree.
  • Required config or capabilities: A loaded sysentvec whose sv_name is β‰₯ 256 bytes (no such sysentvec exists in the stock DragonFlyBSD tree).
  • Reachability: cat /proc/<pid>/etype of any process running under the long-named emulation. The defect becomes live the moment an in-tree emulation layer (e.g. a future Linuxulator) or a kld module registers a sysentvec with a long sv_name.

Proof of concept

No working PoC can be produced against the unmodified stock tree. To demonstrate the latent bug for verification, build a tiny kld module that registers a sysentvec with .sv_name pointing at a 300-byte string, then read /proc/<pid>/etype of a process running under that emulation: the bcopy at :65 writes 300 bytes into the 256-byte stack buffer, mebuffer[300]='\n' at :70 corrupts the saved frame, and uiomove at :71 ships stack bytes to the reader.

Impact

None in the stock tree. Defense-in-depth / hardening: a latent bug that becomes a local DoS (kernel stack overflow) the moment a long sv_name ships in-tree or via kld.

Clamp the copy length with strnlen to sizeof(mebuffer) - 1, leaving room for the trailing newline.

--- a/sys/vfs/procfs/procfs_type.c
+++ b/sys/vfs/procfs/procfs_type.c
@@ -48,19 +48,25 @@ int
 procfs_dotype(struct proc *curp, struct lwp *lp, struct pfsnode *pfs,
          struct uio *uio)
 {
    struct proc *p = lp->lwp_proc;
+   const char *emul;
    int len;
    int error;
    /*
     * buffer for emulation type
     */
    char mebuffer[256];
    char *none = "Not Available";

+   emul = (p != NULL && p->p_sysent != NULL &&
+       p->p_sysent->sv_name != NULL) ? p->p_sysent->sv_name : none;
+
    if (uio->uio_rw != UIO_READ)
        return (EOPNOTSUPP);

    if (uio->uio_offset != 0)
        return (0);

-   if (p && p->p_sysent && p->p_sysent->sv_name) {
-       len = strlen(p->p_sysent->sv_name);
-       bcopy(p->p_sysent->sv_name, mebuffer, len);
-   } else {
-       len = strlen(none);
-       bcopy(none, mebuffer, len);
-   }
+   /*
+    * sv_name is typically a short literal, but the sysentvec API
+    * imposes no length limit; bound the copy to leave room for the
+    * trailing newline so a long name cannot overflow the stack.
+    */
+   len = strnlen(emul, sizeof(mebuffer) - 1);
+   bcopy(emul, mebuffer, len);
    mebuffer[len++] = '\n';
    error = uiomove(mebuffer, len, uio);
    return error;
 }

This guarantees len <= 255 before the bcopy, so mebuffer[len] = '\n' writes at most index 255 and uiomove emits at most 256 bytes β€” both within bounds. Behavior for all current in-tree sv_name values is unchanged.

References

Timeline

  • 2026-07-05 Discovered during automated audit.
  • pending Reported to DragonFlyBSD security contact.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0939 Β· 1 files
FileTypeDescriptionSize
fix.diff suggested-fix Unbounded bcopy of sv_name into 256-byte stack buffer in procfs_dotype (latent; 1.1 KB view raw

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

fix.diff authored but did not apply cleanly; needs context rework

fix.diff authored but did not apply cleanly; needs context rework
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none (Info severity)

Evidence (decisive lines)

Source-confirmed at sys/vfs/procfs/procfs_type.c:54: unbounded bcopy of sv_name into 256-byte stack buffer (latent)

Verified recommended fix

Source-confirmed at sys/vfs/procfs/procfs_type.c:54: unbounded bcopy of sv_name into 256-byte stack buffer (latent)

Verdict

Source-confirmed at sys/vfs/procfs/procfs_type.c:54: unbounded bcopy of sv_name into 256-byte stack buffer (latent)