ipfw: pmod: avoid further rule processing after tcp-mod failures

m_pullup() here will have freed the mbuf chain, but we pass back an
IP_FW_DENY without any signal that the outer loop should finish.  Thus,
rule processing continues without an mbuf and there's a chance that we
conclude that the packet may pass (but there's no mbuf remaining)
depending on the rules that follow it.

PR:		284606
Reviewed by:	ae

(cherry picked from commit c0382512bfce872102d213b9bc2550de0bc30b67)
This commit is contained in:
Kyle Evans 2025-11-01 12:34:11 -05:00 committed by Franco Fichtner
parent b7c3332cf8
commit ffedf88a1c

View file

@ -58,7 +58,8 @@ VNET_DEFINE_STATIC(uint16_t, tcpmod_setmss_eid) = 0;
#define V_tcpmod_setmss_eid VNET(tcpmod_setmss_eid)
static int
tcpmod_setmss(struct mbuf **mp, struct tcphdr *tcp, int tlen, uint16_t mss)
tcpmod_setmss(struct mbuf **mp, struct tcphdr *tcp, int tlen, uint16_t mss,
int *done)
{
struct mbuf *m;
u_char *cp;
@ -73,8 +74,10 @@ tcpmod_setmss(struct mbuf **mp, struct tcphdr *tcp, int tlen, uint16_t mss)
* TCP header with options.
*/
*mp = m = m_pullup(m, m->m_pkthdr.len);
if (m == NULL)
if (m == NULL) {
*done = 1;
return (ret);
}
}
/* Parse TCP options. */
for (tlen -= sizeof(struct tcphdr), cp = (u_char *)(tcp + 1);
@ -115,7 +118,7 @@ tcpmod_setmss(struct mbuf **mp, struct tcphdr *tcp, int tlen, uint16_t mss)
#ifdef INET6
static int
tcpmod_ipv6_setmss(struct mbuf **mp, uint16_t mss)
tcpmod_ipv6_setmss(struct mbuf **mp, uint16_t mss, int *done)
{
struct ip6_hdr *ip6;
struct ip6_hbh *hbh;
@ -143,13 +146,13 @@ tcpmod_ipv6_setmss(struct mbuf **mp, uint16_t mss)
/* We must have TCP options and enough data in a packet. */
if (hlen <= sizeof(struct tcphdr) || hlen > plen)
return (IP_FW_DENY);
return (tcpmod_setmss(mp, tcp, hlen, mss));
return (tcpmod_setmss(mp, tcp, hlen, mss, done));
}
#endif /* INET6 */
#ifdef INET
static int
tcpmod_ipv4_setmss(struct mbuf **mp, uint16_t mss)
tcpmod_ipv4_setmss(struct mbuf **mp, uint16_t mss, int *done)
{
struct tcphdr *tcp;
struct ip *ip;
@ -163,7 +166,7 @@ tcpmod_ipv4_setmss(struct mbuf **mp, uint16_t mss)
/* We must have TCP options and enough data in a packet. */
if (hlen <= sizeof(struct tcphdr) || hlen > plen)
return (IP_FW_DENY);
return (tcpmod_setmss(mp, tcp, hlen, mss));
return (tcpmod_setmss(mp, tcp, hlen, mss, done));
}
#endif /* INET */
@ -207,19 +210,23 @@ ipfw_tcpmod(struct ip_fw_chain *chain, struct ip_fw_args *args,
switch (args->f_id.addr_type) {
#ifdef INET
case 4:
ret = tcpmod_ipv4_setmss(&args->m, htons(icmd->arg1));
ret = tcpmod_ipv4_setmss(&args->m, htons(icmd->arg1),
done);
break;
#endif
#ifdef INET6
case 6:
ret = tcpmod_ipv6_setmss(&args->m, htons(icmd->arg1));
ret = tcpmod_ipv6_setmss(&args->m, htons(icmd->arg1),
done);
break;
#endif
}
/*
* We return zero in both @ret and @done on success, and ipfw_chk()
* will update rule counters. Otherwise a packet will not be matched
* by rule.
* by rule. We passed @done around above in case we hit a fatal error
* somewhere, we'll return non-zero but signal that rule processing
* cannot succeed.
*/
return (ret);
}