Remote unauthenticated kernel heap+stack memory disclosure via ARP reply using attacker-controlled ar_hln/ar_pln
| Field | Value |
|---|---|
| ID | DF-0494 |
| Status | new |
| Severity | High |
| CVSS 3.1 | CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N |
| CWE | CWE-125 Out-of-bounds Read |
| File | sys/netinet/if_ether.c |
| Lines | 1182-1242 |
| Area | net (ARP) |
| Confidence | certain |
| Discovered | 2026-07-01 |
| Reported | pending |
Summary
The ARP ingress parser (arpintr) validates ar_hrd and ar_pro but never
validates ar_hln (hardware address length) or ar_pln (protocol address
length). The ARP reply builder (in_arpreply) then uses these attacker-controlled
byte values as memcpy lengths when copying from kernel-internal buffers that
hold only 6 bytes (IF_LLADDR) or 4 bytes (&taddr on stack). The over-read
bytes fill the ARP reply mbuf, which is transmitted back to the attacker. A
single crafted ARP request from any host on the same L2 segment leaks up to
~249 bytes of kernel heap and ~251 bytes of kernel stack.
Root cause
Ingress validation gap (arpintr, lines 627-667):
ar_hrd = ntohs(ar->ar_hrd);
if (ar_hrd != ARPHRD_ETHER && ar_hrd != ARPHRD_IEEE802) { // validated
...m_freem(m); return;
}
// ar_pro checked via switch(ETHERTYPE_IP) at line 658 // validated
// ar_hln: NEVER validated
// ar_pln: NEVER validated
if (m->m_pkthdr.len < arphdr_len(ar)) { // uses ar's hln/pln
m = m_pullup(m, arphdr_len(ar)); // just ensures pkt is padded
}
OOB in reply builder (in_arpreply, lines 1162-1242):
enaddr = (const uint8_t *)IF_LLADDR(ifp); // points at 6 valid bytes (if_addrlen=6)
if (taddr == myaddr) {
memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); // within-packet: OK
memcpy(ar_sha(ah), enaddr, ah->ar_hln); // HEAP OOB: reads ar_hln bytes from 6-byte enaddr
}
...
memcpy(ar_tpa(ah), ar_spa(ah), ah->ar_pln); // within-packet: OK
memcpy(ar_spa(ah), &taddr, ah->ar_pln); // STACK OOB: reads ar_pln bytes from 4-byte &taddr
When ar_hln=200, the memcpy at line 1183 copies 200 bytes starting at
IF_LLADDR(ifp) β which resolves to LLADDR(sdl) = sdl_data + sdl_nlen,
pointing at exactly sdl_alen (=6 for Ethernet) valid bytes inside a
heap-allocated sockaddr_dl/ifaddr. The remaining 194 bytes are read from
adjacent kernel heap.
When ar_pln=200, the memcpy at line 1242 copies 200 bytes starting at
&taddr β a 4-byte in_addr_t on in_arpreply's stack frame. The remaining
196 bytes are read from adjacent stack.
The over-read bytes land in the ARP reply's SHA/SPA fields. The entire mbuf is
then transmitted to the attacker via ifp->if_output.
Proxy paths are equally affected: lines 1226 (memcpy(ar_sha(ah), enaddr, ah->ar_hln))
and 1237 (memcpy(ar_sha(ah), LLADDR(sdl), ah->ar_hln)).
Threat model & preconditions
- Attacker position: Remote unauthenticated, same L2 segment (adjacent network).
- Privileges gained or impact: Kernel memory disclosure (heap + stack). KASLR bypass. Potential exposure of key material or other secrets in adjacent heap objects.
- Required config or capabilities: Default kernel configuration. No special privileges, no authentication. Host must have an IPv4 address on the target interface (default).
- Reachability: Attacker sends a single crafted ARP REQUEST:
- Set
ar_hlnto a large value (e.g. 200) for heap leak, orar_plnlarge for stack leak. - Set
ar_op = ARPOP_REQUEST. - Set
ar_tpato the victim's IPv4 address. - Pad the frame to
arphdr_len(ar_hln, ar_pln)bytes. - Victim receives,
arpintraccepts (validar_hrd, validar_pro, packet long enough), dispatches toin_arpinput, which hitstaddr == myaddrβgoto replyβin_arpreplyβ OOBmemcpyβ ARP reply with leaked bytes transmitted to attacker.
Proof of concept
PoC source: findings/poc/DF-0494/arp_heap_leak.py
Build & run
# On attacker machine (same L2 segment as victim): sudo python3 findings/poc/DF-0494/arp_heap_leak.py <victim-ip> <attacker-iface>
Expected output
[*] Sending crafted ARP request (ar_hln=200) to 192.168.1.10 on eth0
[*] Waiting for ARP reply...
[+] Got ARP reply (416 bytes)
[+] Reply SHA (bytes 8..207): 00:11:22:33:44:55 (victim MAC)
bytes 14..207 contain leaked kernel heap data:
00 00 00 00 00 00 00 00 a8 03 72 41 ff ff ff ff ........Gr.A....
e8 1f 40 82 ff ff ff ff 01 00 00 00 00 00 00 00 ..@.............
... (194 bytes of adjacent kernel heap)
[*] Leaked kernel heap contains potential pointer: 0xffffffff82401fe8
Impact
- Deterministic, repeatable, single-packet kernel memory disclosure.
- Leaks up to ~249 bytes of kernel heap adjacent to the interface
sockaddr_dlper request (heap variant), and up to ~251 bytes of stack per request (stack variant viaar_pln). - KASLR bypass: leaked heap frequently contains kernel text/data pointers from
adjacent allocations (function pointers,
ifaddrback-pointers, etc.). - Key material exposure: if a crypto buffer or key cache allocation happens to
be adjacent to the
sockaddr_dlin the slab, its contents leak verbatim. - No crash, no authentication, no log entry β fully silent.
- Every DragonFlyBSD host with an IPv4 interface is affected by default.
- CVSS 3.1 base score: 8.1 (High) β adjacent network, no privileges, high confidentiality impact.
Recommended fix
Validate ar_hln and ar_pln before any field-derived memcpy. The simplest
single-point fix is in in_arpreply:
--- a/sys/netinet/if_ether.c
+++ b/sys/netinet/if_ether.c
@@ -1174,6 +1174,12 @@ in_arpreply(struct mbuf *m, in_addr_t taddr, in_addr_t myaddr)
m_freem(m);
return;
}
+
+ if (ah->ar_hln != ifp->if_addrlen ||
+ ah->ar_pln != sizeof(struct in_addr)) {
+ m_freem(m);
+ return;
+ }
+
enaddr = (const uint8_t *)IF_LLADDR(ifp);
if (taddr == myaddr) {
/* I am the target */
For defense-in-depth, also validate at ingress in arpintr (after the
arphdr_len pullup) or in in_arpinput (after the req_len pullup), so no
downstream consumer ever sees a bogus ar_hln/ar_pln.
References
- RFC 826 β An Ethernet Address Resolution Protocol (ARP)
- RFC 5227 β IPv4 Address Conflict Detection
- FreeBSD
in_arpinput()validatesar_hln == ifp->if_addrlenat ingress - NetBSD
in_arpinput()validatesar_hlnagainstifp->if_addrlen - DragonFlyBSD
sys/netinet/if_ether.c:1182-1242β the vulnerable memcpy calls
Timeline
- 2026-07-01 Discovered during automated audit.
- 2026-07-01 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-0494 Β· 14 files| File | Type | Description | Size | |
|---|---|---|---|---|
| arp_leak.c | trigger-source | tap(4)-based oversized-ARP crafter + REPLY capture + heap/stack leak analyser | 10.8 KB | view raw |
| arp_heap_leak.py | trigger-source | seeded scapy PoC (reference for real same-L2 interface; not used in this guest run) | 6.5 KB | view raw |
| build.sh | build-script | cc -O2 -o arp_leak arp_leak.c | 224 B | view raw |
| run.sh | run-script | creates tap0 victim IP, injects crafted ARP, captures reply. args [ar_hln] [ar_pln] | 768 B | view raw |
| run.log | run-log | full untrimmed baseline reproduction (heap 94B + stack 96B + scaling) | 4.5 KB | view raw |
| leak_sample.txt | leak-sample | annotated raw leaked heap+stack bytes, 10 kernel pointers, scaling table, objdump pinning | 4.3 KB | view raw |
| env.txt | environment | uname, kern.version, cc, cpu, tap0 setup | 411 B | view raw |
| VERDICT.md | verdict | full narrative: mechanism, evidence, harness caveat, fix before/after | 8.8 KB | β raw |
| README.md | readme | self-contained repro instructions + network setup explanation | 4.2 KB | β raw |
| fix.diff | suggested-fix | git-apply-able: validate ar_hln/ar_pln in arpintr() ETHERTYPE_IP case | 783 B | view raw |
| fix_build.log | build-log | full patched-kernel nativekernel build output (rc=0) | 5.6 MB | β download |
| fix_run.log | run-log | patched-kernel PoC run: oversized ARP dropped, legal ARP works | 1.6 KB | view 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-0494 PoC: ARP ar_hln/ar_pln kernel heap+stack memory disclosure
Status: REPRODUCED + FIX VALIDATED. Remote-unauthenticated (same-L2)
kernel OOB read via a crafted ARP REQUEST. Read primitive only (info leak /
KASLR defeat); see VERDICT.md for the full analysis.
Vulnerability
arpintr() (sys/netinet/if_ether.c:627) validates ar_hrd and ar_pro and
the packet length, but never validates ar_hln/ar_pln. in_arpreply()
(:1162) then uses those attacker-controlled byte values as memcpy lengths
against a 6-byte source (IF_LLADDR, the interface MAC) and a 4-byte source
(&taddr, a stack in_addr_t), over-reading adjacent kernel heap and
stack into the ARP REPLY mbuf, which is transmitted to the attacker.
sys/netinet/if_ether.c:1183β heap OOB viamemcpy(ar_sha, IF_LLADDR, ar_hln)sys/netinet/if_ether.c:1242β stack OOB viamemcpy(ar_spa, &taddr, ar_pln)- (proxy paths :1226, :1237 carry the same heap OOB)
Files
| file | purpose |
|---|---|
arp_leak.c |
trigger: tap(4)-based oversized-ARP crafter + REPLY capture + leak analyser |
arp_heap_leak.py |
seeded scapy PoC (reference for real-network use; needs same-L2 iface) |
build.sh |
cc -O2 -o arp_leak arp_leak.c |
run.sh |
sets up tap0 (victim IP), injects, captures. args: [ar_hln] [ar_pln] |
run.log |
full untrimmed output of the decisive reproduction (heap + stack leaks) |
leak_sample.txt |
annotated raw leaked bytes + kernel-pointer scan |
env.txt |
guest uname, cc, cpu, tap0 setup |
fix.diff |
git-apply-able fix (validate ar_hln/ar_pln in arpintr) |
fix_build.log |
full patched-kernel build log |
fix_run.log |
patched-kernel PoC run (leak gone) |
VERDICT.md |
full narrative + before/after |
manifest.json |
artifact catalog |
Reproduce
The PoC simulates a same-L2 remote attacker inside the guest using tap0 as a
private L2 island (QEMU user-mode/slirp doesn't let the host inject into the
guest's L2). Root is needed only to create tap0 / open /dev/tap0; the bug
itself needs no privilege β a real remote attacker just sends one ARP frame.
# on the DragonFly guest (as root):
cd poc/DF-0494
./build.sh # cc -O2 -o arp_leak arp_leak.c
./run.sh # default: ar_hln=200 ar_pln=200 (auto-clamped)
./run.sh 100 4 # heap leak: 94 OOB bytes past 6-byte MAC
./run.sh 6 100 # stack leak: 96 OOB bytes past 4-byte &taddr
./run.sh 6 4 # sanity: a LEGAL arp -> clean reply, no leak
Expected (bug present, unpatched #0 kernel)
./run.sh 100 4β ARP REPLY whosear_sha= victim MAC + 94 bytes of kernel heap (kernel residue; the request's0xCCfiller is NOT echoed)../run.sh 6 100β ARP REPLY whosear_spa= victim IP + 96 bytes of kernel stack containing ~10 canonical kernel pointers (kernel-.textreturn addresses0xffffffff80??????, kernel-virtual0xfffff800????????)../run.sh 6 4β clean 6-byte MAC / 4-byte IP REPLY, 0 OOB bytes (sanity).
Expected (FIXED kernel, fix.diff applied)
./run.sh 100 4and./run.sh 6 100β NO ARP REPLY (the oversized REQUEST is dropped by the newarpintr()guard)../run.sh 6 4still works.
Network setup (why tap0)
QEMU user-mode (slirp) networking gives the guest vtnet0 behind NAT; the host
cannot inject raw L2 frames into it. The PoC therefore creates a tap0
interface with a private IP (10.99.99.1), writes a crafted Ethernet/ARP frame
to /dev/tap0 (== frame arriving on the wire β ether_input β arpintr), and
reads the REPLY back from /dev/tap0 (== frame the kernel emitted). This
exercises the exact kernel code path a remote same-L2 attacker would.
Harness caveat: the tapwrite() allocator builds an mbuf chain, so the
demonstrable leak is capped where all four ARP variable fields fit in the first
mbuf (ar_hln β€ ~106, ar_pln β€ ~103). On real hardware (single contiguous
RX cluster mbuf) the full attacker range β ar_hln/ar_pln up to 255, i.e. up
to ~249 heap / ~251 stack leaked bytes β applies, exactly as the finding
states. The mechanism and the leak-size proportionality are identical.
DF-0494 β VERDICT
Verdict: REPRODUCED β remote-unauthenticated kernel heap+stack OOB read via
crafted ARP REQUEST with attacker-controlled ar_hln/ar_pln. Fix validated
(leak gone on patched kernel).
The bug (confirmed line-by-line in sys/netinet/if_ether.c)
arpintr() (:627) validates ar_hrd (:642) and ar_pro (:658 switch) and that
the packet is at least arphdr_len(ar) bytes long (:650), but it never
validates ar_hln or ar_pln. It then dispatches IPv4 ARP straight to
in_arpinput() (:661).
in_arpreply() (:1162) reuses the attacker-supplied mbuf and treats
ah->ar_hln / ah->ar_pln as trusted memcpy lengths against kernel
buffers that hold only 6 / 4 bytes:
-
sys/netinet/if_ether.c:1183βmemcpy(ar_sha(ah), enaddr, ah->ar_hln)whereenaddr = IF_LLADDR(ifp)points at the interface's 6-byte MAC inside asockaddr_dl. Withar_hln > 6this over-reads up toar_hln-6bytes of adjacent kernel heap into the reply'sar_shafield. (Same OOB on the proxy paths at :1226 and :1237.) -
sys/netinet/if_ether.c:1242βmemcpy(ar_spa(ah), &taddr, ah->ar_pln)wheretaddris a 4-bytein_addr_tonin_arpreply()'s stack. Withar_pln > 4this over-reads up toar_pln-4bytes of the kernel stack (return addresses, saved frame pointers, locals) into the reply'sar_spa.
The reply mbuf β now carrying the leaked bytes β is transmitted to the attacker
via ifp->if_output(ifp, m, &sa, NULL) (:1259). Single packet, deterministic,
unauthenticated, same-L2. Trigger: ar_op=REQUEST, ar_tpa = a victim IP so
in_arpinput() reaches the reply path (taddr==myaddr).
(Note: arp_update_oncpu:826 does guard the write at :839 with
if (ifp->if_addrlen != ah->ar_hln) return; for the cache-update path, so the
cache-entry memcpy is not itself overflowed; but the read OOB in
in_arpreply() is completely unguarded and is the leak.)
Reproduction (this run, guest 6.5-DEVELOPMENT #0)
A real same-L2 attacker just sends one raw ARP frame and reads the reply.
DragonFly's QEMU user-mode (slirp) network does not let the host inject into
the guest's L2, so the PoC simulates "the wire" with a tap(4) interface that
the victim kernel owns (private IP 10.99.99.1). Writing a frame to
/dev/tap0 == a frame arriving on the wire (tap if_input β ether_input β
arpintr); reading /dev/tap0 == a frame the kernel emitted. The local root
requirement is purely a property of the harness (creating tap0 / opening the
char device); the vulnerability itself needs no privilege at all β a remote
same-L2 host sends one ARP and reads the leak.
Harness note (honest caveat): the tapwrite() allocator
(sys/net/tap/if_tap.c:948) builds an mbuf chain (MGETHDR of MHLEN
bytes + MGET continuations) rather than a single cluster. Because
in_arpinput() reads ar_tpa/ar_spa and in_arpreply() writes the reply
fields via plain pointer arithmetic (no m_pullup to consolidate once
pkthdr.len >= arphdr_len), fields that fall beyond the first mbuf's MHLEN
bytes are inaccessible through this harness. On real hardware the RX path
delivers a single contiguous cluster mbuf, so the full attacker range
(ar_hln/ar_pln up to 255 β up to ~249 heap / ~251 stack leaked bytes, as
the finding states) applies. The tap harness therefore caps the demonstrable
leak at the boundary where all four ARP variable fields still fit in the first
mbuf (ar_hln β€ ~106 for the heap leak, ar_pln β€ ~103 for the stack leak).
This is purely a harness allocator artifact; the vulnerability mechanism and
the proportionality to ar_hln/ar_pln are identical.
Evidence captured (see run.log, leak_sample.txt)
Heap leak (ar_hln=100, ar_pln=4): reply ar_sha = tap0 MAC (6 bytes) +
94 bytes of adjacent kernel heap (kernel residue: 0x0c, 0xffffffff,
0xffff000000000000; the request's 0xCC filler is absent β genuine kernel
memory). Leak size scales exactly as ar_hln - 6:
| ar_hln | leaked heap bytes |
|---|---|
| 16 | 10 |
| 32 | 26 |
| 48 | 42 |
| 64 | 58 |
| 100 | 94 |
Stack leak (ar_hln=6, ar_pln=100): reply ar_spa = victim IP (4 bytes) +
96 bytes of in_arpreply() stack containing 10 canonical kernel
addresses, including kernel-.text return addresses:
0xfffff8008d1f8a80 kernel virtual (struct/heap pointer) 0xffffffff8067b1d2 kernel .text (return addr β objdump: instruction after `callq ssleep`) 0xfffff8008babf030 kernel virtual 0xfffff8008babf000 kernel virtual 0xfffff8008babf000 kernel virtual 0xffffffff81176040 kernel .text (return address) 0xfffff8008d1f8ab0 kernel virtual 0xffffffff807464a9 kernel .text (return address) 0xffffffff81176040 kernel .text (return address) 0xffffffff818c2000 kernel .text/.data
All four 0xffffffff80?????? values fall inside the kernel's .text/.data
symbol range (0xffffffff80200000β0xffffffff81b25e20 from
nm /boot/kernel/kernel). objdump pins 0xffffffff8067b1d2 exactly to the
instruction immediately following a callq ssleep β a real call-stack return
address leaked to userspace.
Impact
Unauthenticated remote (same-L2) kernel info leak: direct KASLR defeat
(kernel-text base disclosed), kernel heap-pointer disclosure, and kernel
stack-layout disclosure, all from a single ARP REQUEST. CVSS
AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N (High). Read primitive only β no write,
no corruption β so there is no escalation chain; the realistic ceiling is
KASLR defeat + disclosure of whatever kernel heap/stack bytes lie adjacent to
IF_LLADDR / &taddr (potential exposure of keys, credentials, or pointers
usable to stack a follow-on write-primitive exploit).
PoC changes (vs the seeded arp_heap_leak.py)
The seeded PoC used scapy + sniff on a real same-L2 interface, which
cannot loop back through QEMU's slirp. I authored arp_leak.c, a self-contained
tap(4)-based harness that faithfully reproduces the remote attacker inside the
guest: it builds the oversized ARP REQUEST, injects via /dev/tap0 ingress,
captures the REPLY egress, hexdumps the ar_sha (heap) and ar_spa (stack)
regions, and scans for canonical kernel pointers. build.sh / run.sh are
the exact repro commands. The seeded arp_heap_leak.py is retained as the
reference for real-network use.
Recommended fix (validated)
Validate ar_hln and ar_pln in arpintr()'s ETHERTYPE_IP case before
dispatching to in_arpinput() β for Ethernet/IPv4 ARP they must equal the
receiving interface's link-layer address length and sizeof(struct in_addr).
This is the single chokepoint that closes all downstream OOB paths
(in_arpinput, in_arpreply, proxy paths). Full git-apply-able diff in
fix.diff. (Supersedes the finding's proposal, which was the same idea; this
is the precise, line-accurate, validated implementation.)
Fix validation (Phase 8)
- Baseline (
#0, unpatched): PoC leaks 94 heap bytes (ar_hln=100) and 96 stack bytes incl. 10 kernel pointers (ar_pln=100). Reproduced. - Patched (single-fix kernel,
fix.diff): the oversized ARP REQUEST is dropped by the new guard inarpintr(); no ARP REPLY is returned and a normalar_hln=6/ar_pln=4ARP still works correctly. Leak gone.
Before/after evidence in fix_run.log; build log in fix_build.log.
Re-validation (2026-07-16, this verification run)
Re-confirmed end-to-end on a fresh with-src snapshot (#0 unpatched baseline),
then rebuilt and rebooted into a single-fix kernel (#1,
DragonFly 6.5-DEVELOPMENT #1: Thu Jul 16 18:34:26 UTC 2026,
sha256 49df2d6baffe34d5bef6cf755b08c68bed507f7633546e3d3ba3e41297381866):
- Baseline
#0(run.log): ar_hln=100, ar_pln=4β REPLY with 94 bytes heap OOB past the 6-byte tap0 MAC (kernel residue0x0c,0xffffffff; request's0xCCfiller absent β genuine kernel memory).ar_hln=6, ar_pln=100β REPLY with 96 bytes stack OOB past the 4-byte&taddr, containing 10 canonical kernel addresses including 4 kernel-.textreturn addresses (0xffffffff8067b1d2= instr aftercallq ssleep,0xffffffff807464a9,0xffffffff81176040Γ2,0xffffffff818c2000).ar_hln=6, ar_pln=4β clean REPLY, 0 leak (sanity).- Patched
#1(fix_run.log): ar_hln=100, ar_pln=4β NO REPLY (oversized REQUEST dropped by the newarpintr()guard;arp_leakexitsrc=2).ar_hln=6, ar_pln=100β NO REPLY (same).ar_hln=6, ar_pln=4β clean REPLY, 0 leak (legal ARP still works,rc=0).
Leak bytes were byte-for-byte identical to the prior run (no-KASLR environment
β deterministic kernel heap/stack layout). Fix status: fixed β clean
before/after, leak closed, legal ARP unaffected.
Fix verification
fixedVALIDATED: baseline 94B heap + 96B stack leak; patched oversized ARP dropped, legal 6/4 works.
BEFORE: 94B heap + 96B stack. AFTER: no reply for oversized, 0B for legal.
Confirmed kernel references
- sys/netinet/if_ether.c:627
- sys/netinet/if_ether.c:642
- sys/netinet/if_ether.c:650
- sys/netinet/if_ether.c:658
- sys/netinet/if_ether.c:661
- sys/netinet/if_ether.c:1162
- sys/netinet/if_ether.c:1183
- sys/netinet/if_ether.c:1226
- sys/netinet/if_ether.c:1237
- sys/netinet/if_ether.c:1242
- sys/netinet/if_ether.c:1259
- sys/net/tap/if_tap.c:948
Detail
Exploit chain
none -- read-only OOB. KASLR defeat + heap/stack disclosure. No write primitive.
Evidence (decisive lines)
BEFORE: ar_hln=100 -> 94B heap leak; ar_pln=100 -> 96B stack leak with 10 KVA ptrs. AFTER: oversized ARP dropped (no reply), legal 6/4 still works.
PoC changes
arp_leak.c (tap L2 ARP injector), fix.diff (arpintr guard ar_hln==if_addrlen && ar_pln==sizeof(in_addr)), VERDICT.md, manifest.json.
Verified recommended fix
In arpintr ETHERTYPE_IP case before in_arpinput: if(ar->ar_hln!=rcvif->if_addrlen||ar->ar_pln!=sizeof(struct in_addr)){m_freem(m);return;}. Closes all 4 downstream OOB memcpy sites. Full diff in findings/poc/DF-0494/fix.diff.
Verdict
REPRODUCED. arpintr if_ether.c:627 validates ar_hrd/ar_pro/len but NEVER ar_hln/ar_pln. in_arpreply uses attacker-controlled ar_hln/ar_pln as memcpy lengths -> 94B heap OOB (past 6B MAC) + 96B stack OOB (past 4B &taddr incl 10 KVA ptrs + 4 .text ret addrs). Remote unauthenticated same-L2 single ARP REQUEST.
No comments yet.