aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKristof Provost <kp@FreeBSD.org>2021-02-25 07:07:36 +0000
committerKristof Provost <kp@FreeBSD.org>2021-03-01 07:04:47 +0000
commita7926435c12e2304f46c9efadd4216f469f68acc (patch)
tree9f9a762dd09ded6a487f98eddf6ebe68b9f82161
parent113bd64cdf4ef3f54fffcd9874ae7ec07afd882e (diff)
downloadsrc-a7926435c12e2304f46c9efadd4216f469f68acc.tar.gz
src-a7926435c12e2304f46c9efadd4216f469f68acc.zip
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)
-rw-r--r--sys/netpfil/pf/pf_norm.c23
1 files changed, 23 insertions, 0 deletions
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;
}