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)
PoC verification
Evidence pack
findings/poc/DF-0701 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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.
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.diffapplies cleanly to the original source.- Compile-validated: applied (as the
centre_confrewrite, superseding DF-0700's check) with DF-0702/0703,make -j6 nativekernelβ rc=0,ip_fw3_sync.ccompiles 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)
sys/net/ipfw3_basic/ip_fw3_sync.c:137-154βcentre_confsys/net/ipfw3_basic/ip_fw3_sync.c:143βint size = count * LEN_SYNC_EDGE(signed truncation)sys/net/ipfw3_basic/ip_fw3_sync.c:145βkmalloc(size, M_NOWAIT)(wrapped/negative size, no NULL check)sys/net/ipfw3_basic/ip_fw3_sync.c:151-152βbcopy(ioc_centre->edges, ..., count*LEN_SYNC_EDGE)(OOB read)sys/net/ipfw3_basic/ip_fw3_sync.c:115-116βshow_confexfiltratesedgesback to usersys/net/ipfw3_basic/ip_fw3_sync.h:46-50βLEN_SYNC_EDGE = sizeof(struct ipfw3_sync_edge) = 8sys/net/ipfw3/ip_fw3.c:133βip_fw3_ctl_sync_ptr = NULL(dead dispatcher)
Fix verification
not_testablecompile 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.
No comments yet.