Skip to content

Commit d1fe194

Browse files
Florian Westphaldavem330
authored andcommitted
inet: frag: don't re-use chainlist for evictor
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 <[email protected]> Tested-by: Frank Schreuder <[email protected]> Signed-off-by: Nikolay Alexandrov <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 81296fc commit d1fe194

File tree

2 files changed

+5
-5
lines changed

2 files changed

+5
-5
lines changed

include/net/inet_frag.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ enum {
4545
* @flags: fragment queue flags
4646
* @max_size: maximum received fragment size
4747
* @net: namespace that this frag belongs to
48+
* @list_evictor: list of queues to forcefully evict (e.g. due to low memory)
4849
*/
4950
struct inet_frag_queue {
5051
spinlock_t lock;
@@ -59,6 +60,7 @@ struct inet_frag_queue {
5960
__u8 flags;
6061
u16 max_size;
6162
struct netns_frags *net;
63+
struct hlist_node list_evictor;
6264
};
6365

6466
#define INETFRAGS_HASHSZ 1024

net/ipv4/inet_fragment.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,13 @@ inet_evict_bucket(struct inet_frags *f, struct inet_frag_bucket *hb)
151151
}
152152

153153
fq->flags |= INET_FRAG_EVICTED;
154-
hlist_del(&fq->list);
155-
hlist_add_head(&fq->list, &expired);
154+
hlist_add_head(&fq->list_evictor, &expired);
156155
++evicted;
157156
}
158157

159158
spin_unlock(&hb->chain_lock);
160159

161-
hlist_for_each_entry_safe(fq, n, &expired, list)
160+
hlist_for_each_entry_safe(fq, n, &expired, list_evictor)
162161
f->frag_expire((unsigned long) fq);
163162

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

286285
hb = get_frag_bucket_locked(fq, f);
287-
if (!(fq->flags & INET_FRAG_EVICTED))
288-
hlist_del(&fq->list);
286+
hlist_del(&fq->list);
289287
spin_unlock(&hb->chain_lock);
290288
}
291289

0 commit comments

Comments
 (0)