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
sysentvechassv_name β₯ 256bytes. None in the stock tree. - Required config or capabilities: A loaded
sysentvecwhosesv_nameis β₯ 256 bytes (no suchsysentvecexists in the stock DragonFlyBSD tree). - Reachability:
cat /proc/<pid>/etypeof 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 asysentvecwith a longsv_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.
Recommended fix
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
sys/sysent.h:73βsysentvec.sv_nameischar *with no enforced max.sys/libkern.h:138βstrnlenis available in-kernel.sys/cpu/x86_64/misc/elf_machdep.c:52β"DragonFly ELF64"(15 B).sys/kern/init_main.c:352β"null"(4 B).
Timeline
- 2026-07-05 Discovered during automated audit.
- pending Reported to DragonFlyBSD security contact.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0939 Β· 1 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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_testablefix.diff authored but did not apply cleanly; needs context rework
fix.diff authored but did not apply cleanly; needs context rework
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)
No comments yet.