Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

netpoll: fix IPv6 local-address corruption

netpoll_setup() decides whether to auto-populate the local source
address by testing np->local_ip.ip, which only inspects the first 4
bytes of the union inet_addr storage.

For an IPv6 netpoll whose caller-supplied local address has a zero
high-32 bits (::1, ::<suffix>, IPv4-mapped ::ffff:a.b.c.d, etc.), this
misdetects the address as unset (which they are not, but the first
4 bytes are empty), calls netpoll_take_ipv6() and overwrites it with
whatever matching link-local/global address the device happens to expose
first.

Introduce a helper netpoll_local_ip_unset() that picks the correct
family-aware test (ipv6_addr_any() for IPv6, !.ip for IPv4) and use it
from netpoll_setup().

Reproducer is something like:

echo "::2" > local_ip
echo 1 > enabled
cat local_ip
# before this fix: 2001:db8::1 (caller-supplied ::2 was clobbered)
# after this fix: ::2

Fixes: b7394d2429c1 ("netpoll: prepare for ipv6")
Signed-off-by: Breno Leitao <leitao@debian.org>
Link: https://patch.msgid.link/20260424-netpoll_fix-v1-1-3a55348c625f@debian.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Breno Leitao and committed by
Jakub Kicinski
3bc179bc 2b9f6f70

+18 -1
+18 -1
net/core/netpoll.c
··· 704 704 return 0; 705 705 } 706 706 707 + /* 708 + * Test whether the caller left np->local_ip unset, so that 709 + * netpoll_setup() should auto-populate it from the egress device. 710 + * 711 + * np->local_ip is a union of __be32 (IPv4) and struct in6_addr (IPv6), 712 + * so an IPv6 address whose first 4 bytes are zero (e.g. ::1, ::2, 713 + * IPv4-mapped ::ffff:a.b.c.d) must not be tested via the IPv4 arm — 714 + * doing so would misclassify a caller-supplied address as unset and 715 + * silently overwrite it with whatever address the device exposes. 716 + */ 717 + static bool netpoll_local_ip_unset(const struct netpoll *np) 718 + { 719 + if (np->ipv6) 720 + return ipv6_addr_any(&np->local_ip.in6); 721 + return !np->local_ip.ip; 722 + } 723 + 707 724 int netpoll_setup(struct netpoll *np) 708 725 { 709 726 struct net *net = current->nsproxy->net_ns; ··· 764 747 rtnl_lock(); 765 748 } 766 749 767 - if (!np->local_ip.ip) { 750 + if (netpoll_local_ip_unset(np)) { 768 751 if (!np->ipv6) { 769 752 err = netpoll_take_ipv4(np, ndev); 770 753 if (err)