Skip to content

Commit

Permalink
inet: frag: don't re-use chainlist for evictor
Browse files Browse the repository at this point in the history
commit 65ba1f1 ("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: b13d3cb ("inet: frag: move eviction of queues to work queue")
Reported-by: Johan Schuijt <johan@transip.nl>
Tested-by: Frank Schreuder <fschreuder@transip.nl>
Signed-off-by: Nikolay Alexandrov <nikolay@cumulusnetworks.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Florian Westphal authored and davem330 committed Jul 27, 2015
1 parent 81296fc commit d1fe194
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 5 deletions.
2 changes: 2 additions & 0 deletions include/net/inet_frag.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down
8 changes: 3 additions & 5 deletions net/ipv4/inet_fragment.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -284,8 +283,7 @@ 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);
hlist_del(&fq->list);
spin_unlock(&hb->chain_lock);
}

Expand Down

0 comments on commit d1fe194

Please sign in to comment.