Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1fe1944 authored by Florian Westphal's avatar Florian Westphal Committed by David S. Miller
Browse files

inet: frag: don't re-use chainlist for evictor



commit 65ba1f1e ("inet: frags: fix a race between inet_evict_bucket
and inet_frag_kill") describes the bug, but the fix doesn't work reliably.

Problem is that ->flags member can be set on other cpu without chainlock
being held by that task, i.e. the RMW-Cycle can clear INET_FRAG_EVICTED
bit after we put the element on the evictor private list.

We can crash when walking the 'private' evictor list since an element can
be deleted from list underneath the evictor.

Join work with Nikolay Alexandrov.

Fixes: b13d3cbf ("inet: frag: move eviction of queues to work queue")
Reported-by: default avatarJohan Schuijt <johan@transip.nl>
Tested-by: default avatarFrank Schreuder <fschreuder@transip.nl>
Signed-off-by: default avatarNikolay Alexandrov <nikolay@cumulusnetworks.com>
Signed-off-by: default avatarFlorian Westphal <fw@strlen.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 81296fc6
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ enum {
 * @flags: fragment queue flags
 * @max_size: maximum received fragment size
 * @net: namespace that this frag belongs to
 * @list_evictor: list of queues to forcefully evict (e.g. due to low memory)
 */
struct inet_frag_queue {
	spinlock_t		lock;
@@ -59,6 +60,7 @@ struct inet_frag_queue {
	__u8			flags;
	u16			max_size;
	struct netns_frags	*net;
	struct hlist_node	list_evictor;
};

#define INETFRAGS_HASHSZ	1024
+3 −5
Original line number Diff line number Diff line
@@ -151,14 +151,13 @@ inet_evict_bucket(struct inet_frags *f, struct inet_frag_bucket *hb)
		}

		fq->flags |= INET_FRAG_EVICTED;
		hlist_del(&fq->list);
		hlist_add_head(&fq->list, &expired);
		hlist_add_head(&fq->list_evictor, &expired);
		++evicted;
	}

	spin_unlock(&hb->chain_lock);

	hlist_for_each_entry_safe(fq, n, &expired, list)
	hlist_for_each_entry_safe(fq, n, &expired, list_evictor)
		f->frag_expire((unsigned long) fq);

	return evicted;
@@ -284,7 +283,6 @@ static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f)
	struct inet_frag_bucket *hb;

	hb = get_frag_bucket_locked(fq, f);
	if (!(fq->flags & INET_FRAG_EVICTED))
	hlist_del(&fq->list);
	spin_unlock(&hb->chain_lock);
}