























Good catch.That code has been there eating memory for 15 years, and was added just before the dnsmasq git repository started, so how it ended up like that is a bit of a mystery.
The code looks like it's using a common design pattern in dnsmasq, where the buffer is stored in a long-lived iovec and expand_buf() only does anything if the existing buffer is too small. This avoids lots of malloc()/free() calls in hot code paths and resulting heap fragmentation. The only problem is that the iovec in this case is not long-lived. My guess is that this code got copied from elsewhere, and the importance of that detail was missed.
The fix is to declare struct iovec buff as static. Then the buffer becomes long-lived and all the rest of the code, without any free() calls, makes sense.
I just committed https://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=4bcc9650fac9a48502679cd793d269ef60caef07which does exactly that. It would be great if you could check it works OK on your tests.
What would be _really_ nice (hint, hint) would be to extend this code to return IPv6 neighbours.
Cheers, Simon. On 26.04.2026 17:18, Sagie Duchovne-Nave wrote:
arp_enumerate() allocates a heap buffer via expand_buf() to hold the
kernel ARP table dump retrieved through sysctl(NET_RT_FLAGS). This
buffer is never freed on any return path -- neither the early error
returns nor the normal return after iteration -- causing a leak on
every call.
The leak is most acute in the DHCPv6 path. get_client_mac() calls
find_mac() up to five times per packet with lazy=0. Because the
'updated' flag is local to each find_mac() invocation, a cached
ARP_EMPTY entry for an unresolvable IPv6 address does not short-
circuit the kernel lookup: each call falls through to iface_enumerate()
-> arp_enumerate(), leaking one buffer per call. This yields up to
five leaked allocations per DHCPv6 SOLICIT packet. The leak size per
call equals the full system-wide IPv4 ARP table dump across all
interfaces.
The condition is readily triggered by a DHCPv6 client whose MAC
address cannot be resolved via NDP -- which is the common case on
FreeBSD, because arp_enumerate() queries NET_RT_FLAGS/RTF_LLINFO,
which returns IPv4 ARP entries only; IPv6 NDP neighbour entries are
not included. As a result every IPv6 MAC lookup fails unconditionally
on FreeBSD, every failed lookup produces an ARP_EMPTY record that is
never promoted, and every subsequent packet for that client leaks five
buffers.
Fix: free buff.iov_base on all return paths in arp_enumerate(),
including the early returns inside the retry loop where iov_base may
already be non-NULL from a prior expand_buf() call.
Reported against: FreeBSD 14, dnsmasq 2.91
Observed symptom: steady process memory growth correlated with DHCPv6
SOLICIT traffic from a client whose NDP entry is absent from the
kernel table (confirmed by disabling the client stopping the balloon).
Signed-off-by: Sagie D.
---
bpf.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/bpf.c b/bpf.c
index XXXXXXX..XXXXXXX 100644
--- a/bpf.c
+++ b/bpf.c
@@ -xx,12 +xx,18 @@ int arp_enumerate(void *parm, callback_t callback)
while (1)
{
if (!expand_buf(&buff, needed))
- return 0;
+ {
+ free(buff.iov_base);
+ return 0;
+ }
if ((rc = sysctl(mib, 6, buff.iov_base, &needed, NULL, 0)) == 0 ||
errno != ENOMEM)
break;
needed += needed / 8;
}
if (rc == -1)
- return 0;
+ {
+ free(buff.iov_base);
+ return 0;
+ }
for (next = buff.iov_base ; next < (char *)buff.iov_base + needed;
next += rtm->rtm_msglen)
{
rtm = (struct rt_msghdr *)next;
sin2 = (struct sockaddr_inarp *)(rtm + 1);
sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
if (!callback.af_unspec(AF_INET, &sin2->sin_addr, LLADDR(sdl),
sdl->sdl_alen, parm))
- return 0;
+ {
+ free(buff.iov_base);
+ return 0;
+ }
}
- return 1;
+ free(buff.iov_base);
+ return 1;
}
_______________________________________________
Dnsmasq-discuss mailing list
[email protected]
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss
_______________________________________________ Dnsmasq-discuss mailing list [email protected] https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。