No length validation on received sync datagram β type field and struct casts read without bounds check
Summary
LATENT (dead code). edge_handler :208-218: m=sio.sb_mb type=(int*)m->m_data NO check m_len>=sizeof(int). cmd=(struct cmd_send_test*)m->m_data NO check vs sizeof. cmd=(struct cmd_send_state*)m->m_data NO check vs sizeof (~40B via ipfw_flow_id). Remote unauth sends 1-byte datagram type=1 -> install_state callback reads ~35B from 1B datagram. Currently install_state is TODO stub :93 but becomes remote firewall-bypass when implemented. Fix: per-type length validation before dispatch.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0704 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| ipfw3_sync_probe.c | trigger-source | issues all 3 IP_FW_SYNC_* opcodes via IP_FW_X | 3.3 KB | view raw |
| build.sh | build-script | cc -O2 -Wall | 140 B | view raw |
| run.sh | run-script | kldload ipfw3 + ipfw3_basic + run probe | 543 B | view raw |
| run.log | run-log | all 3 opcodes return rc=0; no panic | 2.2 KB | view raw |
| VERDICT.md | verdict | this analysis | 3.6 KB | β raw |
| fix.diff | suggested-fix | per-type m_len validation in edge_socket_handler | 1.1 KB | view raw |
| README.md | readme | human reproduce doc | 1.8 KB | β 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 |
DF-0704 β No length validation on received sync datagram
Summary
sys/net/ipfw3_basic/ip_fw3_sync.c:208-218 edge_socket_handler
casts m->m_data to (int *), (struct cmd_send_test *), and
(struct cmd_send_state *) without checking m->m_len is large enough.
A short datagram (even 1 byte with the right type tag) would cause OOB
reads in the kprintf paths or β if ipfw_sync_install_state_prt were
ever wired up β pass an undersized cmd_send_state struct to a future
state-install callback.
How to reproduce
Cannot reproduce on a default kernel β see VERDICT.md and run.log.
The entire ip_fw3_sync.c is dead code: the dispatch pointer
ip_fw3_ctl_sync_ptr (sys/net/ipfw3/ip_fw3.c:133) is initialised to
NULL and never assigned anywhere in the source tree. The probe in
this folder issues all three relevant IP_FW_SYNC_* opcodes via IP_FW_X
setsockopt and confirms they all return rc=0 with no effect.
./build.sh ssh dfbsd 'sysctl net.filters_default_to_accept=1; kldload ipfw3.ko; kldload ipfw3_basic.ko' ssh dfbsd 'cd /root/poc/DF-0704 && ./run.sh' # expect: probe prints "All three returned without panic" β code is dead
Preconditions
- root (
IP_FW_Xrequires a raw IP socket, which requiresSYSCAP_NONET_RAW). - AND
ip_fw3_ctl_sync_ptrmust be non-NULL, which never happens in the default source.
Impact
LATENT β currently zero. Would become a remote-firewall-bypass
possibility only after both (a) someone wires ip_fw3_sync_modevent
into ipfw3_basic_modevent and (b) the install_state TODO is
implemented.
Fix
fix.diff adds per-type m_len validation in
ip_fw3_sync_edge_socket_handler (reject short datagrams with a
kprintf + continue). Hardening for whenever the code path is hooked
up.
DF-0704 β No length validation on received sync datagram (ip_fw3_sync.c)
Verdict: NOT REPRODUCED (LATENT β entire ip_fw3_sync.c is dead code on a default kernel)
Mechanism (as cited)
In ip_fw3_sync_edge_socket_handler (line 207+):
m = sio.sb_mb;
type = (int *)m->m_data; /* no m_len check */
if (*type == SYNC_TYPE_SEND_TEST) {
struct cmd_send_test *cmd;
cmd = (struct cmd_send_test *)m->m_data;
kprintf("test received %d\n", cmd->num); /* reads offset 4-8 */
} else if (*type == SYNC_TYPE_SEND_STATE) {
struct cmd_send_state *cmd;
cmd = (struct cmd_send_state *)m->m_data; /* reads ~40 B */
if (ipfw_sync_install_state_prt != NULL) {
(*ipfw_sync_install_state_prt)(cmd);
}
}
A 1-byte datagram with *type==0 reads cmd->num past the mbuf's
populated region; a 1-byte datagram with *type==1 would, if
ipfw_sync_install_state_prt were set, pass a cmd_send_state reading
~40 bytes from a 1-byte source. The reviewer labels this LATENT, which
is accurate.
Why it doesn't reproduce (the bigger reason)
The entire file is unreachable on a default kernel:
- The IP_FW_SYNC_* opcodes are dispatched by
ip_fw3_ctl()at sys/net/ipfw3/ip_fw3.c:1113-1128 only ifip_fw3_ctl_sync_ptr != NULL. - That pointer is initialised to NULL (line 133) and NEVER assigned
anywhere in the tree:
$ grep -rn "ip_fw3_ctl_sync_ptr =" sys/ (no hits) - The companion
ipfw_sync_send_state_prtglobal is similarly never assigned: it would be set byip_fw3_sync_modevent(MOD_LOAD)(ip_fw3_sync.c:470-475), butip_fw3_sync_modeventis never called by anyone β the ipfw3_basic module hook (ip_fw3_basic.c:657-658) invokes onlyip_fw3_state_modeventandip_fw3_table_modevent, skipping sync entirely.
Empirical confirmation (run.log): on the default kernel with
net.filters_default_to_accept=1 (so SSH survives ipfw3's default-deny),
after kldload ipfw3.ko and kldload ipfw3_basic.ko, issuing the three
"panic-trigger" opcodes via IP_FW_X:
[*] invoking IP_FW_SYNC_EDGE_START (DF-0705 panic 1) [fire opcode=85 plen=0] setsockopt rc=0 errno=0 [*] invoking IP_FW_SYNC_CENTRE_CONF (DF-0705 panic 3, count=-1) [fire opcode=89 plen=4] setsockopt rc=0 errno=0 [*] invoking IP_FW_SYNC_SHOW_CONF (DF-0705 panic 3 read-back) [fire opcode=82 plen=64] setsockopt rc=0 errno=0 [+] All three returned without panic.
All three opcodes silently fall through (the if (sync_ptr != NULL)
check fails), so the buggy functions are never invoked.
Fix
Even though the code is currently dead, it would become live the moment
someone wires up ip_fw3_sync_modevent into ipfw3_basic_modevent
(one-line addition). The fix.diff adds per-type length validation in
ip_fw3_sync_edge_socket_handler so that when the code is hooked up, a
short datagram is logged-and-skipped instead of reading past its end:
m = sio.sb_mb;
if (m == NULL || m->m_len < sizeof(int)) {
kprintf("ipfw3sync: ignore short datagram\n");
continue;
}
type = (int *)m->m_data;
if (*type == SYNC_TYPE_SEND_TEST) {
if (m->m_len < sizeof(struct cmd_send_test)) {
kprintf("ipfw3sync: short SEND_TEST\n");
continue;
}
...
(Matches the LATENT (dead code) label in the finding's summary; the
fix is hardening for when the TODO install_state is implemented.)
Files
ipfw3_sync_probe.cβ reachability probe (issues all 3 opcodes, observes fall-through)run.logβ probe output proving dead codefix.diffβ per-type length validationVERDICT.mdβ this analysis
Fix verification
not_testablen/a
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
LATENT dead code. ip_fw3_sync edge_socket_handler no m_len validation. ip_fw3_ctl_sync_ptr never assigned.
No comments yet.