ipfilter: Fix use after free on packet with broken lengths

Under the scenario with a packet with length of 67 bytes, a header length
using the default of 20 bytes and a TCP data offset (th_off) of 48 will
cause m_pullup() to fail to make sure bytes are arragned contiguously.
m_pullup() will free the mbuf chain and return a null. ipfilter stores
the resultant mbuf address (or the resulting NULL) in its fr_info_t
structure. Unfortuntely the eroneous packet is not flagged for drop.
This results in a kernel page fault at line 410 of sys/netinet/ip_fastfwd.c
as it tries to use a now previously freed, by m_pullup(), mbuf.

PR:		266442
Reported by:	Robert Morris <rtm@lcs.mit.edu>
MFC after:	1 week
This commit is contained in:
Cy Schubert 2023-02-01 16:49:08 -08:00
parent c941e8c65d
commit 79f7745c09

View File

@ -1113,8 +1113,10 @@ ipf_pr_pullup(fr_info_t *fin, int plen)
if (M_LEN(fin->fin_m) < plen + fin->fin_ipoff) {
#if defined(_KERNEL)
if (ipf_pullup(fin->fin_m, fin, plen) == NULL) {
DT(ipf_pullup_fail);
DT1(ipf_pullup_fail, fr_info_t *, fin);
LBUMP(ipf_stats[fin->fin_out].fr_pull[1]);
fin->fin_reason = FRB_PULLUP;
fin->fin_flx |= FI_BAD;
return (-1);
}
LBUMP(ipf_stats[fin->fin_out].fr_pull[0]);
@ -1127,6 +1129,7 @@ ipf_pr_pullup(fr_info_t *fin, int plen)
*fin->fin_mp = NULL;
fin->fin_m = NULL;
fin->fin_ip = NULL;
fin->fin_flx |= FI_BAD;
return (-1);
#endif
}
@ -3180,6 +3183,14 @@ ipf_check(void *ctx, ip_t *ip, int hlen, struct ifnet *ifp, int out
SPL_X(s);
if (fin->fin_m == NULL && fin->fin_flx & FI_BAD &&
fin->fin_reason == FRB_PULLUP) {
/* m_pullup() has freed the mbuf */
LBUMP(ipf_stats[out].fr_blocked[fin->fin_reason]);
return (-1);
}
#ifdef _KERNEL
if (FR_ISPASS(pass))
return (0);