On netns teardown, fqdir_pre_exit() walks the fqdir rhashtable and flushes every fragment queue that is not yet complete using inet_frag_queue_flush(). That helper frees all the skbs queued on the fragment queue but does not set INET_FRAG_COMPLETE, and leaves q->fragments_tail and q->last_run_head pointing at the freed skbs. The queue itself stays in the rhashtable. fqdir_pre_exit() first lowers high_thresh to 0 to stop new queue lookups, but it cannot stop a fragment that already obtained the queue through inet_frag_find() earlier and stalled just before taking the queue lock. Once that fragment resumes after the flush and takes the queue lock, it passes the INET_FRAG_COMPLETE check and then dereferences the freed fragments_tail. inet_frag_queue_insert() reads FRAG_CB() and ->len of that pointer and, on the append path, writes ->next_frag, causing a slab use-after-free. IPv6, nf_conntrack_reasm6 and 6lowpan reassembly share the same flush path and are affected as well. Mark the queue complete and reset its remaining pointers under the same lock right after the flush. With INET_FRAG_COMPLETE set, the insert in each reassembly path bails out at its check as soon as it takes the queue lock and no longer accesses the freed fragments_tail. Fixes: 006a5035b495 ("inet: frags: flush pending skbs in fqdir_pre_exit()") Signed-off-by: Hyunwoo Kim --- net/ipv4/inet_fragment.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 393770920abd..d532f6182c8a 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -243,8 +243,13 @@ void fqdir_pre_exit(struct fqdir *fqdir) continue; } spin_lock_bh(&fq->lock); - if (!(fq->flags & INET_FRAG_COMPLETE)) + if (!(fq->flags & INET_FRAG_COMPLETE)) { inet_frag_queue_flush(fq, 0); + fq->flags |= INET_FRAG_COMPLETE; + fq->rb_fragments = RB_ROOT; + fq->fragments_tail = NULL; + fq->last_run_head = NULL; + } spin_unlock_bh(&fq->lock); } -- 2.43.0