The branch main has been updated by kp:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=349360b17eaba47be3d4e7a452e28e70a133b807

commit 349360b17eaba47be3d4e7a452e28e70a133b807
Author:     Kristof Provost <k...@freebsd.org>
AuthorDate: 2025-07-28 16:00:50 +0000
Commit:     Kristof Provost <k...@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 <r...@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()
+

Reply via email to