aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2025-01-06 10:48:40 +0000
committerKristof Provost <kp@FreeBSD.org>2025-01-17 16:00:41 +0000
commit7f846fc0e7ce30c80e3265c957a138d7192af397 (patch)
treec4c13de07d2eaa0c800ca1d66e220469d0e8b351
parent76e00c722bee123f902febce7b637ea7afa5e364 (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.py40
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 = {