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
outbufand the 8-byte-alignedstatsmember, so no observable data corruption occurs. It is undefined behavior and a latent memory-safety defect that would corruptstats.FramesPlainon 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(GuessTableall-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.
Recommended fix
--- 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
sys/netgraph7/ng_pred1.c:397β the buggy bound check.sys/netgraph7/ng_pred1.c:416βoutbuf+2write offset.sys/netgraph7/ng_pred1.c:573-589βPred1Compressworst-case output size.
Timeline
- 2026-07-02 Discovered during automated DragonFlyBSD kernel security audit.
- 2026-07-02 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0635 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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.
Recommended fix
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
fixedcombined 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
Confirmed kernel references
- n
- e
- t
- g
- r
- a
- p
- h
- 7
- /
- n
- g
- _
- p
- r
- e
- d
- 1
- .
- c
- :
- 3
- 9
- 7
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.
No comments yet.