The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=e9ca883b12c0db6210717025f539ef846b986d5b
commit e9ca883b12c0db6210717025f539ef846b986d5b Author: Kristof Provost <k...@freebsd.org> AuthorDate: 2025-07-24 14:31:41 +0000 Commit: Kristof Provost <k...@freebsd.org> CommitDate: 2025-07-29 13:13:10 +0000 netinet6: Don't return non-IPv6 enabled interfaces from in6_getlinkifnet() There are scenarios where we can end up looking up an interface by its scope and turn up an interface that doesn't have IPv6 enabled on it. If that happens we could end up dereferencing a NULL pointer accessing ifp->if_afdata[AF_INET6]. Check for this. One such scenario is if a firewall rewrites a destination address to a link-local address, with an embedded scope for such an interface. Attach a test case which provokes this. PR: 288263 Reported by: Robert Morris <r...@lcs.mit.edu> Reviewed by: zlei Sponsored by: Rubicon Communications, LLC ("Netgate") Differential Revision: https://reviews.freebsd.org/D51500 --- sys/netinet6/scope6.c | 17 ++++++++++++++++- tests/sys/netpfil/pf/nat64.py | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/sys/netinet6/scope6.c b/sys/netinet6/scope6.c index 0987ea7e99ad..08702a2e81ab 100644 --- a/sys/netinet6/scope6.c +++ b/sys/netinet6/scope6.c @@ -505,8 +505,23 @@ in6_set_unicast_scopeid(struct in6_addr *in6, uint32_t scopeid) struct ifnet* in6_getlinkifnet(uint32_t zoneid) { + struct ifnet *ifp; - return (ifnet_byindex((u_short)zoneid)); + ifp = ifnet_byindex((u_short)zoneid); + + if (ifp == NULL) + return (NULL); + + /* An interface might not be IPv6 capable. */ + if (ifp->if_afdata[AF_INET6] == NULL) { + log(LOG_NOTICE, + "%s: embedded scope points to an interface without " + "IPv6: %s%%%d.\n", __func__, + if_name(ifp), zoneid); + return (NULL); + } + + return (ifp); } /* diff --git a/tests/sys/netpfil/pf/nat64.py b/tests/sys/netpfil/pf/nat64.py index 5cc4713a16cc..a5890fc4a161 100644 --- a/tests/sys/netpfil/pf/nat64.py +++ b/tests/sys/netpfil/pf/nat64.py @@ -33,7 +33,7 @@ from atf_python.sys.net.tools import ToolsHelper from atf_python.sys.net.vnet import VnetTestTemplate class TestNAT64(VnetTestTemplate): - REQUIRED_MODULES = [ "pf" ] + REQUIRED_MODULES = [ "pf", "pflog" ] TOPOLOGY = { "vnet1": {"ifaces": ["if1"]}, "vnet2": {"ifaces": ["if1", "if2"]}, @@ -92,12 +92,15 @@ class TestNAT64(VnetTestTemplate): def vnet2_handler(self, vnet): ifname = vnet.iface_alias_map["if1"].name + ToolsHelper.print_output("/sbin/sysctl net.inet6.ip6.forwarding=1") ToolsHelper.print_output("/sbin/route add default 192.0.2.2") ToolsHelper.print_output("/sbin/pfctl -e") ToolsHelper.pf_rules([ "pass inet6 proto icmp6", "pass in on %s inet6 af-to inet from 192.0.2.1" % ifname]) + vnet.pipe.send(socket.if_nametoindex("pflog0")) + @pytest.mark.require_user("root") @pytest.mark.require_progs(["scapy"]) def test_tcp_rst(self): @@ -287,3 +290,39 @@ class TestNAT64(VnetTestTemplate): reply = sp.sr1(packet, timeout=3) # We don't expect a reply to a corrupted packet assert not reply + + @pytest.mark.require_user("root") + @pytest.mark.require_progs(["scapy"]) + def test_noip6(self): + """ + PR 288263: link-local target address in icmp6 ADVERT can cause NULL deref + """ + ifname = self.vnet.iface_alias_map["if1"].name + gw_mac = self.vnet.iface_alias_map["if1"].epairb.ether + scopeid = self.wait_object(self.vnet_map["vnet2"].pipe) + ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1") + + import scapy.all as sp + + pkt = sp.Ether(dst=gw_mac) \ + / sp.IPv6(dst="64:ff9b::203.0.113.2") \ + / sp.ICMPv6ND_NA(tgt="FFA2:%x:2821:125F:1D27:B3B2:3F6F:C43C" % scopeid) + pkt.show() + sp.hexdump(pkt) + s = DelayedSend(pkt, sendif=ifname) + + packets = sp.sniff(iface=ifname, timeout=5) + for r in packets: + r.show() + + # Try scope id that likely doesn't have an interface at all + pkt = sp.Ether(dst=gw_mac) \ + / sp.IPv6(dst="64:ff9b::203.0.113.2") \ + / sp.ICMPv6ND_NA(tgt="FFA2:%x:2821:125F:1D27:B3B2:3F6F:C43C" % 255) + pkt.show() + sp.hexdump(pkt) + s = DelayedSend(pkt, sendif=ifname) + + packets = sp.sniff(iface=ifname, timeout=5) + for r in packets: + r.show()