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

Heap OOB read in centre_conf bcopy and integer overflow in size math

Summary

LATENT (dead code). centre_conf :142-151: bcopy(ioc_centre->edges,edges,count*LEN_SYNC_EDGE) count not validated vs sopt_valsize. count=1000 valsize=8 reads ~8KB past sopt_val heap. int size=count*8 wraps for large count kmalloc tiny buffer bcopy corrupts. count=-1 kmalloc fails bcopy to NULL panics. Leaked heap exfiltrable via show_conf :115-116. Fix: validate count vs sopt_valsize+MAX_EDGES.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0701 Β· 11 files
FileTypeDescriptionSize
ipfw3_sync_deadpath.c trigger-source empirical dead-path demo 4.0 KB view raw
build.sh build-script cc -o ipfw3_sync_deadpath ... 292 B view raw
run.sh run-script issue the SYNC_CENTRE_CONF sockopt 391 B view raw
VERDICT.md verdict source trace (OOB read + int overflow + NULL deref) + dead-code proof + fix 5.5 KB ↓ raw
fix.diff suggested-fix count vs MAX_EDGES + sopt_valsize; size_t size; NULL check 1.4 KB view raw
run.log run-log dead-path demo output (ENOPROTOOPT) 399 B view raw
env.txt environment uname, cc version 365 B view raw
README.md readme human reproduce doc 197 B ↓ raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
live_reachability_check.txt reachability-test Live ipfw3_sync reachability evidence - dead code (ptr=NULL) 1.2 KB view raw
README.md readme human reproduce doc
↓ download raw

DF-0701 β€” PoC evidence pack

See VERDICT.md for the full analysis (verdict, mechanism, fix, fix-validation). Reproduce: ./build.sh && ./run.sh. Machine-readable catalog: manifest.json.

VERDICT.md verdict source trace (OOB read + int overflow + NULL deref) + dead-code proof + fix
↓ download raw

DF-0701 β€” Heap OOB read in centre_conf bcopy + integer overflow in size math

Same ipfw3_sync dead-code caveat as DF-0700 β€” see that VERDICT for the full unreachability proof. Summary: the bug is real at source level; the function is unreachable because ip_fw3_ctl_sync_ptr (ip_fw3.c:133) is never assigned.

Verdict

NOT REPRODUCED β€” LATENT dead code. Real source bug; unreachable on this kernel (same dead dispatcher as DF-0700). Fix is defense-in-depth.

The bug (source-level trace)

ip_fw3_ctl_sync_centre_conf() (sys/net/ipfw3_basic/ip_fw3_sync.c:137-154) has three compounding defects in 12 lines:

struct ipfw3_ioc_sync_centre *ioc_centre;
int size;                                   /* <-- SIGNED int */
ioc_centre = sopt->sopt_val;
size = ioc_centre->count * LEN_SYNC_EDGE;   /* :143 β€” (1) int*size_t truncation;
                                             *         (2) no count bound */
if (fw3_sync_ctx.count == 0) {
    fw3_sync_ctx.edges = kmalloc(size, ...); /* :145 β€” tiny/negative alloc on wrap */
} else {
    fw3_sync_ctx.edges = krealloc(..., size, ...);  /* :147 */
}
                                            /* (3) no NULL check on edges */
fw3_sync_ctx.count = ioc_centre->count;
bcopy(ioc_centre->edges, fw3_sync_ctx.edges,
      ioc_centre->count * LEN_SYNC_EDGE);   /* :151 β€” uses size_t math, NOT `size` */

LEN_SYNC_EDGE = sizeof(struct ipfw3_sync_edge) = 8 (ip_fw3_sync.h:46-50).

(a) Heap OOB read off sopt->val. count is never validated against sopt->sopt_valsize. The bcopy at :151 reads count * 8 bytes starting at ioc_centre->edges, but the user only supplied sopt_valsize bytes. With count = 1000 and sopt_valsize = 8 (just the count field, no edges), the bcopy reads ~8 KB past the end of the sopt->val kernel heap buffer β€” a kernel heap OOB read whose contents are then stored into fw3_sync_ctx.edges and are exfiltrable back to userspace via ip_fw3_ctl_sync_show_conf (:115-116, which bcopys fw3_sync_ctx.count * LEN_SYNC_EDGE bytes out).

(b) Signed-integer overflow β†’ tiny alloc + huge bcopy = heap overflow. size is int. ioc_centre->count * LEN_SYNC_EDGE promotes count (int) with LEN_SYNC_EDGE (size_t) to size_t, but the assignment truncates back to int. For count = 0x40000000 (β‰ˆ10⁹), count * 8 = 0x200000000 (8 GiB) truncates to 0 in 32-bit int β†’ kmalloc(0, ...) returns a minimal slab chunk β†’ the bcopy then writes 8 GiB through it β†’ catastrophic kernel heap overwrite + immediate panic. (The same count also drives the BSS OOB of DF-0700 once centre_start runs.)

(c) NULL deref panic. With count such that the wrapped/negative size makes kmalloc fail (M_NOWAIT, no NULL check), fw3_sync_ctx.edges = NULL and the bcopy writes to NULL β†’ fatal trap.

Why unreachable on this kernel

Identical to DF-0700: centre_conf is a sockopt handler reachable only through ip_fw3_ctl_sync_sockopt, which is only dispatched via ip_fw3_ctl_sync_ptr (ip_fw3.c:133) β€” and that pointer is never assigned anywhere in the tree (grep-verified). Plus the base kernel's ipfw3 stub returns ENOPROTOOPT until kldload ipfw3. Empirically confirmed by ipfw3_sync_deadpath (see run.log): issuing the sockopt returns ENOPROTOOPT.

Fix (defense-in-depth)

Validate count against MAX_EDGES and sopt_valsize, switch size to size_t (no truncation), and NULL-check the alloc. See fix.diff:

struct ipfw3_ioc_sync_centre *ioc_centre;
size_t size, need;                                   /* size_t, not int */
ioc_centre = sopt->sopt_val;
if (ioc_centre->count < 0 || ioc_centre->count > MAX_EDGES)   /* DF-0700 bound */
    return EINVAL;
need = sizeof(*ioc_centre) + (size_t)ioc_centre->count * LEN_SYNC_EDGE;
if (sopt->sopt_valsize < need)                       /* DF-0701: bound vs user buf */
    return EINVAL;
size = (size_t)ioc_centre->count * LEN_SYNC_EDGE;    /* size_t: no wrap */
...
if (fw3_sync_ctx.edges == NULL) { fw3_sync_ctx.count = 0; return ENOMEM; }

This closes (a) the OOB read (valsize check), (b) the int overflow (size_t), and (c) the NULL deref (NULL check). (The MAX_EDGES check is the same line as DF-0700's fix; the two findings overlap on centre_conf.)

Fix validation

  • fix.diff applies cleanly to the original source.
  • Compile-validated: applied (as the centre_conf rewrite, superseding DF-0700's check) with DF-0702/0703, make -j6 nativekernel β†’ rc=0, ip_fw3_sync.c compiles with -Werror.
  • fix_status: not_testable β€” dead code; validated applies + compiles.

Files

file desc
ipfw3_sync_deadpath.c empirical dead-path demo
fix.diff count vs MAX_EDGES + sopt_valsize; size_t size; NULL check
run.log dead-path demo output (ENOPROTOOPT)
env.txt guest environment

Kernel references (confirmed)

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

Detail

Exploit chain

none β€” dead code.

Evidence (decisive lines)

Same as DF-0700: ip_fw3_ctl_sync_ptr NULL, sync functions absent from module.

PoC changes

Added live_reachability_check.txt.

Verified recommended fix

fix.diff validates count against sopt_valsize and MAX_EDGES. Matches finding proposal.

Verdict

NOT REPRODUCED (dead code). Same root cause as DF-0700: ip_fw3_ctl_sync_ptr = NULL, dispatch check always FALSE, sync functions not in compiled module. The heap OOB via unchecked count in centre_conf is unreachable.