git: 7f846fc0e7ce - main - pf tests: reproduce use-after-free in fragment reassembly
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 17 Jan 2025 16:02:15 UTC
The branch main has been updated by kp: URL: https://cgit.FreeBSD.org/src/commit/?id=7f846fc0e7ce30c80e3265c957a138d7192af397 commit 7f846fc0e7ce30c80e3265c957a138d7192af397 Author: Kristof Provost <kp@FreeBSD.org> AuthorDate: 2025-01-06 10:48:40 +0000 Commit: Kristof Provost <kp@FreeBSD.org> CommitDate: 2025-01-17 16:00:41 +0000 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") --- tests/sys/netpfil/pf/frag6.py | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file 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 = {