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

Off-by-one OOB write in IPV6_CHECKSUM offset validation in rip6_output

Field Value
ID DF-0620
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:L/A:L
CWE CWE-787 Out-of-bounds Write
File sys/netinet6/raw_ip6.c
Lines 403, 416-418
Area netinet6 (raw IPv6 output checksum)
Confidence likely
Discovered 2026-07-02
Reported pending

Summary

The checksum-offset bounds check uses plen < off + 1 but the checksum field is a u_int16_t (2 bytes). When plen == off + 1, the subsequent *p = in6_cksum(...) write extends 1 byte past the valid payload region, corrupting 1 byte in the mbuf data buffer or, for cluster-backed mbufs that tightly fill the cluster, adjacent heap memory.

Root cause

At sys/netinet6/raw_ip6.c:392-418, when the socket has IPV6_CHECKSUM set (in6p->in6p_cksum != -1), the code computes the checksum:

402:    off = in6p->in6p_cksum;
403:    if (plen < off + 1) { error = EINVAL; goto bad; }  /* BUG: should be off + 2 */
407:    off += sizeof(struct ip6_hdr);        /* += 40 */
...
416:    p = (u_int16_t *)(mtod(n, caddr_t) + off);
417:    *p = 0;
418:    *p = in6_cksum(m, ip6->ip6_nxt, sizeof(*ip6), plen);

in6p_cksum is int (in_pcb.h:231), settable to any even integer via setsockopt(IPV6_CHECKSUM) in ip6_raw_ctloutput (ip6_output.c:1837) with no upper-bound validation.

The check plen < off + 1 allows plen == off + 1 (i.e., off is the last valid payload byte index). But *p writes a u_int16_t (2 bytes) at payload offset off, so it writes bytes [off, off+1]. When plen == off + 1, byte off + 1 is past the end of the payload.

After M_PREPEND (line 326) the mbuf holds plen + 40 bytes. The checksum at mbuf-offset (off + 40) extends to (off + 41). When plen == off + 1, valid mbuf data is bytes 0..(off + 40), so byte (off + 41) is 1 past the data.

Example: set IPV6_CHECKSUM=0, send 1-byte payload. plen=1, off=0. Check: 1 < 1 β†’ false (passes). off becomes 40. p points to byte 40 of a 41-byte mbuf. *p writes bytes 40-41. Byte 41 is out of bounds.

The correct check is plen < off + 2 (need 2 bytes for the checksum field).

Threat model & preconditions

  • Attacker position: local, holding a raw IPv6 socket with IPV6_CHECKSUM set to an even value N (via setsockopt).
  • Trigger: send a payload of exactly N + 1 bytes. The checksum write corrupts 1 byte past the payload in the mbuf data buffer.
  • Impact: the written value is the internet checksum (attacker-influenced by choosing payload bytes). For internally-stored mbufs the byte lands in unused union padding (harmless). For cluster-backed mbufs that tightly fill the cluster, the byte lands in adjacent heap β€” a controlled 1-byte heap write. In multi-mbuf chains where the checksum field straddles two mbufs, the second byte is written into the first mbuf's buffer past m_len rather than into the next mbuf, producing silent data corruption in the sent packet.
  • Requires raw-socket privilege.

Change the bounds check to account for the 2-byte checksum field width:

--- a/sys/netinet6/raw_ip6.c
+++ b/sys/netinet6/raw_ip6.c
@@ -400,7 +400,7 @@
            off = offsetof(struct icmp6_hdr, icmp6_cksum);
        else
            off = in6p->in6p_cksum;
-       if (plen < off + 1) {
+       if (plen < off + 2) {
            error = EINVAL;
            goto bad;
        }

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-0620 Β· 5 files
FileTypeDescriptionSize
fix.diff suggested-fix Validate in6p_cksum is non-negative, even and within plen. 375 B view raw
VERDICT.md verdict source-confirmation + fix 1.0 KB ↓ 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-0620 β€” Low-severity source-confirmation

Verdict: REPRODUCED

Impact: dos Confidence: likely

Kernel ref: sys/netinet6/raw_ip6.c:392

Mechanism / why

Source-confirmed: rip6_output uses in6p_cksum (settable via setsockopt) as the checksum offset with only a loose plen<off+1 bound; odd/out-of-range values reach (u16)(mtod+off). netinet6 (GENERIC).

Validate in6p_cksum is non-negative, even and within plen.

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 dos; source-only confirmation)

Evidence (decisive lines)

DF-0620 [REPRODUCED] - sys/netinet6/raw_ip6.c:392

PoC changes

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

Verified recommended fix

Validate in6p_cksum is non-negative, even and within plen.

Verdict

Source-confirmed: rip6_output uses in6p_cksum (settable via setsockopt) as the checksum offset with only a loose plen<off+1 bound; odd/out-of-range values reach (u16)(mtod+off). netinet6 (GENERIC).