git: 349360b17eab - main - pf: return errors from pf_route() and pf_route6()
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 04 Aug 2025 08:12:02 UTC
The branch main has been updated by kp:
URL: https://cgit.FreeBSD.org/src/commit/?id=349360b17eaba47be3d4e7a452e28e70a133b807
commit 349360b17eaba47be3d4e7a452e28e70a133b807
Author: Kristof Provost <kp@FreeBSD.org>
AuthorDate: 2025-07-28 16:00:50 +0000
Commit: Kristof Provost <kp@FreeBSD.org>
CommitDate: 2025-08-04 06:08:31 +0000
pf: return errors from pf_route() and pf_route6()
If we fail to route the packet in pf_route()/pf_route6() (e.g. because it
hit the TTL limit) we free the mbuf. If that packet is an SCTP packet that
establishes extra (i.e. multihome) states we have a queued job to handle that.
These jobs reference the now freed mbuf.
Pass the error from pf_route()/pf_route6() on, so that
pf_sctp_multihome_delayed() doesn't attempt to use the invalid mbuf pointer (or
establishes states for a packet we're not passing).
PR: 288274
Reported by: Robert Morris <rtm@lcs.mit.edu>
Sponsored by: Rubicon Communications, LLC ("Netgate")
Differential Revision: https://reviews.freebsd.org/D51627
---
sys/netpfil/pf/pf.c | 66 ++++++++++++++++++++++++++++++-------------
tests/sys/netpfil/pf/nat64.py | 28 ++++++++++++++++++
2 files changed, 75 insertions(+), 19 deletions(-)
diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c
index f6ee02626624..4801b3e2c766 100644
--- a/sys/netpfil/pf/pf.c
+++ b/sys/netpfil/pf/pf.c
@@ -409,14 +409,14 @@ static void pf_mtag_free(struct m_tag *);
static void pf_packet_rework_nat(struct pf_pdesc *, int,
struct pf_state_key *);
#ifdef INET
-static void pf_route(struct pf_krule *,
+static int pf_route(struct pf_krule *,
struct ifnet *, struct pf_kstate *,
struct pf_pdesc *, struct inpcb *);
#endif /* INET */
#ifdef INET6
static void pf_change_a6(struct pf_addr *, u_int16_t *,
struct pf_addr *, u_int8_t);
-static void pf_route6(struct pf_krule *,
+static int pf_route6(struct pf_krule *,
struct ifnet *, struct pf_kstate *,
struct pf_pdesc *, struct inpcb *);
#endif /* INET6 */
@@ -8914,7 +8914,7 @@ pf_routable(struct pf_addr *addr, sa_family_t af, struct pfi_kkif *kif,
}
#ifdef INET
-static void
+static int
pf_route(struct pf_krule *r, struct ifnet *oifp,
struct pf_kstate *s, struct pf_pdesc *pd, struct inpcb *inp)
{
@@ -8929,6 +8929,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
uint16_t tmp;
int r_dir;
bool skip_test = false;
+ int action = PF_PASS;
KASSERT(pd->m && r && oifp, ("%s: invalid parameters", __func__));
@@ -8950,6 +8951,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
m0 = pd->m;
pd->m = NULL;
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
+ action = PF_DROP;
goto bad_locked;
}
@@ -8963,11 +8965,12 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
}
if (ifp == oifp) {
/* When the 2nd interface is not skipped */
- return;
+ return (action);
} else {
m0 = pd->m;
pd->m = NULL;
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
+ action = PF_DROP;
goto bad;
}
} else {
@@ -8975,7 +8978,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
if (((m0 = m_dup(pd->m, M_NOWAIT)) == NULL)) {
if (s)
PF_STATE_UNLOCK(s);
- return;
+ return (action);
}
}
} else {
@@ -8984,7 +8987,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
pf_dummynet(pd, s, r, &pd->m);
if (s)
PF_STATE_UNLOCK(s);
- return;
+ return (action);
} else {
if (r_dir == PF_IN) {
skip_test = true;
@@ -9024,6 +9027,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
pf_send_icmp(m0, ICMP_TIMXCEED,
ICMP_TIMXCEED_INTRANS, 0, pd->af, r,
pd->act.rtableid);
+ action = PF_DROP;
goto bad_locked;
}
ip->ip_ttl -= IPTTLDEC;
@@ -9070,6 +9074,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
if (ifp == NULL) {
m0 = pd->m;
pd->m = NULL;
+ action = PF_DROP;
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
goto bad;
}
@@ -9080,9 +9085,11 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
if (pd->dir == PF_IN && !skip_test) {
if (pf_test(AF_INET, PF_OUT, PFIL_FWD, ifp, &m0, inp,
&pd->act) != PF_PASS) {
+ action = PF_DROP;
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
goto bad;
} else if (m0 == NULL) {
+ action = PF_DROP;
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
goto done;
}
@@ -9090,6 +9097,7 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
DPFPRINTF(PF_DEBUG_URGENT,
"%s: m0->m_len < sizeof(struct ip)", __func__);
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
+ action = PF_DROP;
goto bad;
}
ip = mtod(m0, struct ip *);
@@ -9171,12 +9179,14 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
ifp->if_mtu, pd->af, r, pd->act.rtableid);
}
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
+ action = PF_DROP;
goto bad;
}
error = ip_fragment(ip, &m0, ifp->if_mtu, ifp->if_hwassist);
if (error) {
SDT_PROBE1(pf, ip, route_to, drop, __LINE__);
+ action = PF_DROP;
goto bad;
}
@@ -9203,7 +9213,9 @@ pf_route(struct pf_krule *r, struct ifnet *oifp,
done:
if (pd->act.rt != PF_DUPTO)
pd->m = NULL;
- return;
+ else
+ action = PF_PASS;
+ return (action);
bad_locked:
if (s)
@@ -9215,7 +9227,7 @@ bad:
#endif /* INET */
#ifdef INET6
-static void
+static int
pf_route6(struct pf_krule *r, struct ifnet *oifp,
struct pf_kstate *s, struct pf_pdesc *pd, struct inpcb *inp)
{
@@ -9226,6 +9238,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
struct ifnet *ifp = NULL;
int r_dir;
bool skip_test = false;
+ int action = PF_PASS;
KASSERT(pd->m && r && oifp, ("%s: invalid parameters", __func__));
@@ -9246,6 +9259,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
pd->pf_mtag->routed++ > 3) {
m0 = pd->m;
pd->m = NULL;
+ action = PF_DROP;
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
goto bad_locked;
}
@@ -9260,10 +9274,11 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
}
if (ifp == oifp) {
/* When the 2nd interface is not skipped */
- return;
+ return (action);
} else {
m0 = pd->m;
pd->m = NULL;
+ action = PF_DROP;
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
goto bad;
}
@@ -9272,7 +9287,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
if (((m0 = m_dup(pd->m, M_NOWAIT)) == NULL)) {
if (s)
PF_STATE_UNLOCK(s);
- return;
+ return (action);
}
}
} else {
@@ -9281,7 +9296,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
pf_dummynet(pd, s, r, &pd->m);
if (s)
PF_STATE_UNLOCK(s);
- return;
+ return (action);
} else {
if (r_dir == PF_IN) {
skip_test = true;
@@ -9321,6 +9336,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
pf_send_icmp(m0, ICMP6_TIME_EXCEEDED,
ICMP6_TIME_EXCEED_TRANSIT, 0, pd->af, r,
pd->act.rtableid);
+ action = PF_DROP;
goto bad_locked;
}
ip6->ip6_hlim -= IPV6_HLIMDEC;
@@ -9375,6 +9391,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
if (ifp == NULL) {
m0 = pd->m;
pd->m = NULL;
+ action = PF_DROP;
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
goto bad;
}
@@ -9385,9 +9402,11 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
if (pd->dir == PF_IN && !skip_test) {
if (pf_test(AF_INET6, PF_OUT, PFIL_FWD | PF_PFIL_NOREFRAGMENT,
ifp, &m0, inp, &pd->act) != PF_PASS) {
+ action = PF_DROP;
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
goto bad;
} else if (m0 == NULL) {
+ action = PF_DROP;
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
goto done;
}
@@ -9395,6 +9414,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
DPFPRINTF(PF_DEBUG_URGENT,
"%s: m0->m_len < sizeof(struct ip6_hdr)",
__func__);
+ action = PF_DROP;
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
goto bad;
}
@@ -9470,6 +9490,7 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
pf_send_icmp(m0, ICMP6_PACKET_TOO_BIG, 0,
ifp->if_mtu, pd->af, r, pd->act.rtableid);
}
+ action = PF_DROP;
SDT_PROBE1(pf, ip6, route_to, drop, __LINE__);
goto bad;
}
@@ -9477,7 +9498,9 @@ pf_route6(struct pf_krule *r, struct ifnet *oifp,
done:
if (pd->act.rt != PF_DUPTO)
pd->m = NULL;
- return;
+ else
+ action = PF_PASS;
+ return (action);
bad_locked:
if (s)
@@ -11033,15 +11056,18 @@ done:
break;
}
#ifdef INET
- if (pd.naf == AF_INET)
- pf_route(r, kif->pfik_ifp, s, &pd, inp);
+ if (pd.naf == AF_INET) {
+ action = pf_route(r, kif->pfik_ifp, s, &pd,
+ inp);
+ }
#endif /* INET */
#ifdef INET6
- if (pd.naf == AF_INET6)
- pf_route6(r, kif->pfik_ifp, s, &pd, inp);
+ if (pd.naf == AF_INET6) {
+ action = pf_route6(r, kif->pfik_ifp, s, &pd,
+ inp);
+}
#endif /* INET6 */
*m0 = pd.m;
- action = PF_PASS;
goto out;
break;
default:
@@ -11050,13 +11076,15 @@ done:
#ifdef INET
case AF_INET:
/* pf_route() returns unlocked. */
- pf_route(r, kif->pfik_ifp, s, &pd, inp);
+ action = pf_route(r, kif->pfik_ifp, s, &pd,
+ inp);
break;
#endif /* INET */
#ifdef INET6
case AF_INET6:
/* pf_route6() returns unlocked. */
- pf_route6(r, kif->pfik_ifp, s, &pd, inp);
+ action = pf_route6(r, kif->pfik_ifp, s, &pd,
+ inp);
break;
#endif /* INET6 */
}
diff --git a/tests/sys/netpfil/pf/nat64.py b/tests/sys/netpfil/pf/nat64.py
index a5890fc4a161..0908c90c34a0 100644
--- a/tests/sys/netpfil/pf/nat64.py
+++ b/tests/sys/netpfil/pf/nat64.py
@@ -326,3 +326,31 @@ class TestNAT64(VnetTestTemplate):
packets = sp.sniff(iface=ifname, timeout=5)
for r in packets:
r.show()
+
+ @pytest.mark.require_user("root")
+ @pytest.mark.require_progs(["scapy"])
+ def test_ttl_zero(self):
+ """
+ PR 288274: we can use an mbuf after free on TTL = 0
+ """
+ ifname = self.vnet.iface_alias_map["if1"].name
+ gw_mac = self.vnet.iface_alias_map["if1"].epairb.ether
+ 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::192.0.2.2", hlim=0) \
+ / sp.SCTP(sport=1111, dport=2222) \
+ / sp.SCTPChunkInit(init_tag=1, n_in_streams=1, n_out_streams=1, \
+ a_rwnd=1500, params=[ \
+ sp.SCTPChunkParamIPv4Addr() \
+ ])
+ pkt.show()
+ sp.hexdump(pkt)
+ s = DelayedSend(pkt, sendif=ifname)
+
+ packets = sp.sniff(iface=ifname, timeout=5)
+ for r in packets:
+ r.show()
+