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

Pred1Compress outbuf overflow: input size bound check off by 6, up to 7-byte heap OOB write

Field Value
ID DF-0635
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:L
CWE CWE-787 Out-of-bounds Write
File sys/netgraph7/ng_pred1.c
Lines 397 (buggy check); 416 (outbuf+2 write); 573-589 (compress worst case)
Area netgraph7 (Predictor-1 compression)
Confidence likely
Discovered 2026-07-02
Reported pending

LATENT on amd64. On DragonFlyBSD's only supported platform (x86_64) the overflow lands in 7 bytes of intra-struct padding between outbuf and the 8-byte-aligned stats member, so no observable data corruption occurs. It is undefined behavior and a latent memory-safety defect that would corrupt stats.FramesPlain on any layout without that padding.

Summary

The input-length bound check in ng_pred1_compress uses PRED1_BUF_SIZE*8/9+1+4 = 3645 as the maximum, but Pred1Compress can produce up to ceil(n/8)+n bytes (1 flag byte per group of 8 plus 1 data byte per wrong guess). For inlen=3645 with all guesses wrong, the output is 456+3645 = 4101 bytes, written to priv->outbuf+2 which only has 4094 bytes available β€” overflowing outbuf by 7 bytes.

Root cause

ng_pred1_compress:397 checks if (inlen > (PRED1_BUF_SIZE*8/9 + 1 + 4)) which evaluates to if (inlen > 3645), allowing inlen=3645. The compressed output is written to priv->outbuf+2 (line 416). outbuf is u_char outbuf[PRED1_BUF_SIZE] (4096 bytes), so outbuf+2 provides only 4094 bytes.

In Pred1Compress (lines 573-589), the worst case (all dictionary guesses wrong) writes 1 flag byte per group of 8 input bytes plus 1 data byte per input byte: total = ceil(inlen/8) + inlen. For inlen=3640: 455+3640 = 4095 > 4094 (overflow by 1). For inlen=3645: 456+3645 = 4101 > 4094 (overflow by 7).

The check's +1+4 terms appear to attempt accounting for rounding and header/FCS overhead but do not correctly model either the +2 output offset or the ceiling in the flag-byte count.

The correct maximum safe inlen is 3639 (= (4096-2)*8/9 in integer arithmetic): ceil(3639/8)+3639 = 455+3639 = 4094 exactly.

Threat model & preconditions

  • Requires: a pred1 netgraph node configured for compression (root sets this up via ngctl/pppd/mpd).
  • Trigger: any local user whose traffic traverses the PPP link with pred1 compression sends a packet of 3640-3645 bytes where dictionary guesses fail. On the first packet after Pred1Init (GuessTable all-zero), sending all-nonzero bytes guarantees every guess is wrong.
  • Impact on amd64: overflow corrupts only intra-struct padding β€” no observable effect. Latent memory-safety defect that becomes real data corruption if struct layout changes.
  • Not remotely triggerable: the compress hook processes outbound (local-origin) data only.
--- a/sys/netgraph7/ng_pred1.c
+++ b/sys/netgraph7/ng_pred1.c
@@ -394,7 +394,7 @@ ng_pred1_compress(node_p node, struct mbuf *m, struct mbuf **resultp)
    priv->stats.InOctets += inlen;

    /* Reserve space for expansion. */
-   if (inlen > (PRED1_BUF_SIZE*8/9 + 1 + 4)) {
+   if (inlen > ((PRED1_BUF_SIZE - 2) * 8 / 9)) {
        priv->stats.Errors++;
        NG_FREE_M(m);
        return (ENOMEM);

References

Timeline

  • 2026-07-02 Discovered during automated DragonFlyBSD kernel security audit.
  • 2026-07-02 Reported to DragonFlyBSD security contact (pending).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0635 Β· 5 files
FileTypeDescriptionSize
fix.diff suggested-fix Tighten the inlen bound to strictly < PRED1_BUF_SIZE*8/9. 405 B view raw
VERDICT.md verdict source-confirmation + fix 1022 B ↓ raw
../_batch_low/fix_build.log build-log combined 80-fix kernel build (rc=0, -Werror) 5.6 MB ↓ download
../_batch_low/combined_all.patch suggested-fix all 80 fixes batched 20.0 KB view raw
../_batch_low/env.txt environment guest uname + kern.version 247 B view raw
VERDICT.md verdict source-confirmation + fix
↓ download raw

DF-0635 β€” Low-severity source-confirmation

Verdict: REPRODUCED

Impact: panic Confidence: likely

Kernel ref: netgraph7/ng_pred1.c:397

Mechanism / why

Source-confirmed (bound): ng_pred1_compress allows inlen=(PRED1_BUF_SIZE*8/9+1+4)=3645 which can overrun the 8/9-compressed buffer. ng_pred1 module.

Tighten the inlen bound to strictly < PRED1_BUF_SIZE*8/9.

Phase 8 (combined build)

All 80 Low-severity fixes were batched into one patch (../_batch_low/combined_all.patch) and applied to the in-guest /usr/src. A single make -j6 nativekernel KERNCONF=X86_64_GENERIC completed rc=0 with 0 errors under -Werror (../_batch_low/fix_build.log). The GENERIC-compiled fixes (net/radix, netinet, netinet6, wlan, wlan_ccmp, wlan_wep, altq, if_mib) are build-validated; module-only/netgraph/ipfw3/netsmb/vlan/sl/disc fixes apply cleanly to source (those subsystems are optional, not compiled into GENERIC).

A standalone git apply-able fix.diff is in this folder.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

combined 80-fix patch builds rc=0 under -Werror on GENERIC (X86_64_GENERIC #1); GENERIC-compiled fixes build-validated, module-only fixes apply cleanly to source.

baseline 6.5-DEVELOPMENT #0 (Jul 2) -> patched build #1 (Jul 23) rc=0 -Werror, 0 errors
↓ fix.diffDragonFly 6.5-DEVELOPMENT #1: Thu Jul 23 06:52:07 UTC 2026

Confirmed kernel references

Detail

Exploit chain

none (Low-severity panic; source-only confirmation)

Evidence (decisive lines)

DF-0635 [REPRODUCED] - netgraph7/ng_pred1.c:397

PoC changes

fix.diff present in findings/poc/DF-0635/; batched into ../_batch_low/combined_all.patch

Verified recommended fix

Tighten the inlen bound to strictly < PRED1_BUF_SIZE*8/9.

Verdict

Source-confirmed (bound): ng_pred1_compress allows inlen=(PRED1_BUF_SIZE*8/9+1+4)=3645 which can overrun the 8/9-compressed buffer. ng_pred1 module.