From a7926435c12e2304f46c9efadd4216f469f68acc Mon Sep 17 00:00:00 2001 From: Kristof Provost Date: Thu, 25 Feb 2021 08:07:36 +0100 Subject: pf: Fix incorrect fragment handling A sequence of overlapping IPv4 fragments could crash the kernel in pf due to an assertion. Approved by: re (gjb) Reported by: Alexander Bluhm Obtained from: OpenBSD MFC after: 3 days Sponsored by: Rubicon Communications, LLC ("Netgate") (cherry picked from commit 5f1b1f184b7f12330cf4a027e3db7c6700c67640) (cherry picked from commit 86ebf4d3e12c3eae94d3e9a8dcf5bd5741889b58) --- sys/netpfil/pf/pf_norm.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c index b7a84437630b..d7310c7bccb4 100644 --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -549,6 +549,7 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent, struct pf_frent *after, *next, *prev; struct pf_fragment *frag; uint16_t total; + int old_index, new_index; PF_FRAG_ASSERT(); @@ -660,8 +661,30 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent, DPFPRINTF(("adjust overlap %d\n", aftercut)); if (aftercut < after->fe_len) { m_adj(after->fe_m, aftercut); + old_index = pf_frent_index(after); after->fe_off += aftercut; after->fe_len -= aftercut; + new_index = pf_frent_index(after); + if (old_index != new_index) { + DPFPRINTF(("frag index %d, new %d", + old_index, new_index)); + /* Fragment switched queue as fe_off changed */ + after->fe_off -= aftercut; + after->fe_len += aftercut; + /* Remove restored fragment from old queue */ + pf_frent_remove(frag, after); + after->fe_off += aftercut; + after->fe_len -= aftercut; + /* Insert into correct queue */ + if (pf_frent_insert(frag, after, prev)) { + DPFPRINTF( + ("fragment requeue limit exceeded")); + m_freem(after->fe_m); + uma_zfree(V_pf_frent_z, after); + /* There is not way to recover */ + goto bad_fragment; + } + } break; } -- cgit v1.2.3