ng_ether_rcv_upper skips bridge_input_p handoff, bypassing bridge input filtering on upper-hook-injected packets
| Field | Value |
|---|---|
| ID | DF-0629 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:N |
| CWE | CWE-697 Incorrect Comparison |
| File | sys/netgraph/ether/ng_ether.c |
| Lines | 582, 585 |
| Area | netgraph (legacy Ethernet node upper-hook bridge bypass) |
| Confidence | likely |
| Discovered | 2026-07-02 |
| Reported | pending |
Cross-reference: The netgraph7 equivalent (
sys/netgraph7/ether/ng_ether.c) was audited separately and found to have a High-severity UAF (DF-0617) where thebridge_input_p()return value is discarded. This legacy version does NOT have that UAF β it simply omits the bridge handoff entirely.
Summary
The legacy ng_ether_rcv_upper re-injects upper-hook packets directly into
ether_demux() without first consulting the bridge subsystem via
bridge_input_p(). The canonical Ethernet input path (ether_input_oncpu)
and the netgraph7 equivalent both invoke bridge_input_p() when
ifp->if_bridge is set, so that the bridge can consume, forward, or
re-attribute the packet. The legacy version omits this entirely, so any
packet pushed in through the upper hook on a bridged Ethernet interface
is force-delivered to the local protocol stack, bypassing bridge input
policy.
Root cause
In sys/netgraph/ether/ng_ether.c:565-587, ng_ether_rcv_upper does:
582: m->m_pkthdr.rcvif = priv->ifp;
585: ether_demux(m);
There is no if (priv->ifp->if_bridge) { m = bridge_input_p(...); if (m ==
NULL) return; } block. Compare the canonical pattern at
sys/net/if_ethersubr.c:1245-1259:
1245: if (ifp->if_bridge) {
1252: m = bridge_input_p(ifp, m); /* return CAPTURED */
1253: if (m == NULL) return;
The netgraph7 version at sys/netgraph7/ether/ng_ether.c:657-661 attempts
this handoff but discards the return value (the UAF bug DF-0617). The
legacy version simply never acquired the feature.
Threat model & preconditions
- Attacker position: local user who can send data into the ether node's
upperhook (typically requiring root to construct the netgraph topology viangctl, or operating inside a topology an admin has already wired to an accessible data socket). - Trigger: inject raw Ethernet frames via the upper hook on a bridged
interface. The frames reach the local IP/IP6/ARP stack as if received on
a specific bridge member, without
bridge_input()ever seeing them. - Impact: bypass of layer-2 bridge isolation/filtering for those frames. No memory corruption, no UAF, no OOB β only a policy bypass. Constructing the injection path itself requires privilege.
Recommended fix
Mirror the canonical ether_input_oncpu pattern, capturing the
bridge_input_p() return value (unlike netgraph7, which is the buggy
version to avoid):
--- a/sys/netgraph/ether/ng_ether.c
+++ b/sys/netgraph/ether/ng_ether.c
@@ -580,6 +581,16 @@ ng_ether_rcv_upper(node_p node, struct mbuf *m, meta_p meta)
m->m_pkthdr.rcvif = priv->ifp;
+ /*
+ * Give the bridge a chance to consume/forward/re-attribute this
+ * packet, exactly as ether_input_oncpu() does for the normal path.
+ */
+ if (priv->ifp->if_bridge != NULL && bridge_input_p != NULL) {
+ m = bridge_input_p(priv->ifp, m);
+ if (m == NULL)
+ return (0);
+ }
+
/* Route packet back in */
ether_demux(m);
return (0);
The critical detail is m = bridge_input_p(...) β the assignment must be
present so that a NULL return (bridge consumed the mbuf) is observed
before ether_demux touches it.
References
sys/netgraph/ether/ng_ether.c:582,585β the missing bridge handoff.sys/net/if_ethersubr.c:1245-1259β the canonical correct pattern.sys/netgraph7/ether/ng_ether.c:657-661β the netgraph7 version that has the UAF (DF-0617) due to discarding the return value.
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-0629 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | Add the bridge_input_p() handoff mirroring the standard path. | 339 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 |
DF-0629 β Low-severity source-confirmation
Verdict: REPRODUCED
Impact: none Confidence: speculative
Kernel ref: netgraph7/ether/ng_ether.c:585
Mechanism / why
Source-confirmed (minor): legacy ng_ether_rcv_upper sets rcvif and calls ether_demux_oncpu with no bridge_input_p() handoff -> bridged frames mishandled. netgraph7/ether module.
Recommended fix
Add the bridge_input_p() handoff mirroring the standard path.
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
- /
- e
- t
- h
- e
- r
- /
- n
- g
- _
- e
- t
- h
- e
- r
- .
- c
- :
- 5
- 8
- 5
Detail
Exploit chain
none (Low-severity; source-only confirmation)
Evidence (decisive lines)
DF-0629 [REPRODUCED] - netgraph7/ether/ng_ether.c:585
PoC changes
fix.diff present in findings/poc/DF-0629/; batched into ../_batch_low/combined_all.patch
Verified recommended fix
Add the bridge_input_p() handoff mirroring the standard path.
Verdict
Source-confirmed (minor): legacy ng_ether_rcv_upper sets rcvif and calls ether_demux_oncpu with no bridge_input_p() handoff -> bridged frames mishandled. netgraph7/ether module.
No comments yet.