diff options
author | Kristof Provost <kp@FreeBSD.org> | 2025-01-06 10:48:40 +0000 |
---|---|---|
committer | Kristof Provost <kp@FreeBSD.org> | 2025-01-17 16:00:41 +0000 |
commit | 7f846fc0e7ce30c80e3265c957a138d7192af397 (patch) | |
tree | c4c13de07d2eaa0c800ca1d66e220469d0e8b351 | |
parent | 76e00c722bee123f902febce7b637ea7afa5e364 (diff) |
pf tests: reproduce use-after-free in fragment reassembly
Produce an IPv6 packet that's longer than 65535 bytes so it'll get dropped in
pf_reassemble6(). This can then causes pf_normalize_ip6() to return an error,
which led pf_setup_pdesc() to fail to update *m0, eventually ending up with
pf_scrub() attempting to modify *m0 (now different from pd->m), a freed mbuf.
This does depend on pf_join_fragment()'s call to m_cat() freeing the relevant
mbuf rather than adding it to the chain. Accomplish this by ensuring there's
sufficient free space, by having dummymbuf re-allocate larger mbufs for our
fragments.
PR: 283705
Reported by: Yichen Chai <yichen.chai@gmail.com>, Zhuo Ying Jiang Li <zyj20@cl.cam.ac.uk>
Sponsored by: Rubicon Communications, LLC ("Netgate")
-rw-r--r-- | tests/sys/netpfil/pf/frag6.py | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/tests/sys/netpfil/pf/frag6.py b/tests/sys/netpfil/pf/frag6.py index f54381fba8cb..f274fc28a3bf 100644 --- a/tests/sys/netpfil/pf/frag6.py +++ b/tests/sys/netpfil/pf/frag6.py @@ -2,6 +2,7 @@ import pytest import logging import threading import time +import random logging.getLogger("scapy").setLevel(logging.CRITICAL) from atf_python.sys.net.tools import ToolsHelper from atf_python.sys.net.vnet import VnetTestTemplate @@ -19,7 +20,7 @@ class DelayedSend(threading.Thread): sp.send(self._packet) class TestFrag6(VnetTestTemplate): - REQUIRED_MODULES = ["pf"] + REQUIRED_MODULES = ["pf", "dummymbuf"] TOPOLOGY = { "vnet1": {"ifaces": ["if1"]}, "vnet2": {"ifaces": ["if1"]}, @@ -27,12 +28,15 @@ class TestFrag6(VnetTestTemplate): } def vnet2_handler(self, vnet): + ifname = vnet.iface_alias_map["if1"].name ToolsHelper.print_output("/sbin/pfctl -e") ToolsHelper.pf_rules([ - "scrub fragment reassemble", + "scrub fragment reassemble min-ttl 10", "pass", "block in inet6 proto icmp6 icmp6-type echoreq", ]) + ToolsHelper.print_output("/sbin/pfilctl link -i dummymbuf:inet6 inet6") + ToolsHelper.print_output("/sbin/sysctl net.dummymbuf.rules=\"inet6 in %s enlarge 3000;\"" % ifname) def check_ping_reply(self, packet): print(packet) @@ -59,6 +63,38 @@ class TestFrag6(VnetTestTemplate): for p in packets: assert not p.getlayer(sp.ICMPv6EchoReply) + @pytest.mark.require_user("root") + def test_overlong(self): + "Test overly long fragmented packet" + + # Import in the correct vnet, so at to not confuse Scapy + import scapy.all as sp + + curr = 0 + pkts = [] + + frag_id = random.randint(0,0xffffffff) + gran = 1200 + + i = 0 + while curr <= 65535: + ipv61 = sp.IPv6(src="2001:db8::1", dst="2001:db8::2") + more = True + g = gran + if curr + gran > 65535: + more = False + g = 65530 - curr + if i == 0: + pkt = ipv61 / sp.IPv6ExtHdrHopByHop(options=[sp.PadN(optlen=2), sp.Pad1()]) / \ + sp.IPv6ExtHdrFragment(id = frag_id, offset = curr // 8, m = more) / bytes([i] * g) + else: + pkt = ipv61 / sp.IPv6ExtHdrFragment(id = frag_id, offset = curr // 8, m = more) / bytes([i] * g) + pkts.append(pkt) + curr += gran + i += 1 + + sp.send(pkts, inter = 0.1) + class TestFrag6_Overlap(VnetTestTemplate): REQUIRED_MODULES = ["pf"] TOPOLOGY = { |