Skip to content

Commit 91341fa

Browse files
edumazetdavem330
authored andcommitted
inet: frags: annotate races around fqdir->dead and fqdir->high_thresh
Both fields can be read/written without synchronization, add proper accessors and documentation. Fixes: d5dd887 ("inet: fix various use-after-free in defrags units") Signed-off-by: Eric Dumazet <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 3ba8c62 commit 91341fa

File tree

4 files changed

+18
-7
lines changed

4 files changed

+18
-7
lines changed

include/net/inet_frag.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,15 @@ int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net);
117117

118118
static inline void fqdir_pre_exit(struct fqdir *fqdir)
119119
{
120-
fqdir->high_thresh = 0; /* prevent creation of new frags */
121-
fqdir->dead = true;
120+
/* Prevent creation of new frags.
121+
* Pairs with READ_ONCE() in inet_frag_find().
122+
*/
123+
WRITE_ONCE(fqdir->high_thresh, 0);
124+
125+
/* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire()
126+
* and ip6frag_expire_frag_queue().
127+
*/
128+
WRITE_ONCE(fqdir->dead, true);
122129
}
123130
void fqdir_exit(struct fqdir *fqdir);
124131

include/net/ipv6_frag.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ ip6frag_expire_frag_queue(struct net *net, struct frag_queue *fq)
6767
struct sk_buff *head;
6868

6969
rcu_read_lock();
70-
if (fq->q.fqdir->dead)
70+
/* Paired with the WRITE_ONCE() in fqdir_pre_exit(). */
71+
if (READ_ONCE(fq->q.fqdir->dead))
7172
goto out_rcu_unlock;
7273
spin_lock(&fq->q.lock);
7374

net/ipv4/inet_fragment.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,9 @@ void inet_frag_kill(struct inet_frag_queue *fq)
235235
/* The RCU read lock provides a memory barrier
236236
* guaranteeing that if fqdir->dead is false then
237237
* the hash table destruction will not start until
238-
* after we unlock. Paired with inet_frags_exit_net().
238+
* after we unlock. Paired with fqdir_pre_exit().
239239
*/
240-
if (!fqdir->dead) {
240+
if (!READ_ONCE(fqdir->dead)) {
241241
rhashtable_remove_fast(&fqdir->rhashtable, &fq->node,
242242
fqdir->f->rhash_params);
243243
refcount_dec(&fq->refcnt);
@@ -352,9 +352,11 @@ static struct inet_frag_queue *inet_frag_create(struct fqdir *fqdir,
352352
/* TODO : call from rcu_read_lock() and no longer use refcount_inc_not_zero() */
353353
struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key)
354354
{
355+
/* This pairs with WRITE_ONCE() in fqdir_pre_exit(). */
356+
long high_thresh = READ_ONCE(fqdir->high_thresh);
355357
struct inet_frag_queue *fq = NULL, *prev;
356358

357-
if (!fqdir->high_thresh || frag_mem_limit(fqdir) > fqdir->high_thresh)
359+
if (!high_thresh || frag_mem_limit(fqdir) > high_thresh)
358360
return NULL;
359361

360362
rcu_read_lock();

net/ipv4/ip_fragment.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ static void ip_expire(struct timer_list *t)
144144

145145
rcu_read_lock();
146146

147-
if (qp->q.fqdir->dead)
147+
/* Paired with WRITE_ONCE() in fqdir_pre_exit(). */
148+
if (READ_ONCE(qp->q.fqdir->dead))
148149
goto out_rcu_unlock;
149150

150151
spin_lock(&qp->q.lock);

0 commit comments

Comments
 (0)