diff options
author | Kristof Provost <kp@FreeBSD.org> | 2021-02-25 07:07:36 +0000 |
---|---|---|
committer | Kristof Provost <kp@FreeBSD.org> | 2021-02-28 15:37:07 +0000 |
commit | 5f5a45463ea068983092588ab897602f7aea328b (patch) | |
tree | 9c132c79f44972bcb61e9a051a449b26e5c3e5b3 | |
parent | 555726fda685ab5be9ccdbfcb73b9336dc2d75af (diff) | |
download | src-5f5a45463ea068983092588ab897602f7aea328b.tar.gz src-5f5a45463ea068983092588ab897602f7aea328b.zip |
pf: Fix incorrect fragment handling
A sequence of overlapping IPv4 fragments could crash the kernel in
pf due to an assertion.
Reported by: Alexander Bluhm
Obtained from: OpenBSD
MFC after: 3 days
Sponsored by: Rubicon Communications, LLC ("Netgate")
(cherry picked from commit 5f1b1f184b7f12330cf4a027e3db7c6700c67640)
-rw-r--r-- | sys/netpfil/pf/pf_norm.c | 23 |
1 files changed, 23 insertions, 0 deletions
diff --git a/sys/netpfil/pf/pf_norm.c b/sys/netpfil/pf/pf_norm.c index 0770fcfd4c58..2a3c1d442fd4 100644 --- a/sys/netpfil/pf/pf_norm.c +++ b/sys/netpfil/pf/pf_norm.c @@ -545,6 +545,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(); @@ -656,8 +657,30 @@ pf_fillup_fragment(struct pf_fragment_cmp *key, struct pf_frent *frent, DPFPRINTF(("adjust overlap %d", 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; } |