lkml.org 
[lkml]   [2022]   [Jan]   [24]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.16 0985/1039] inet: frags: annotate races around fqdir->dead and fqdir->high_thresh
    Date
    From: Eric Dumazet <edumazet@google.com>

    commit 91341fa0003befd097e190ec2a4bf63ad957c49a upstream.

    Both fields can be read/written without synchronization,
    add proper accessors and documentation.

    Fixes: d5dd88794a13 ("inet: fix various use-after-free in defrags units")
    Signed-off-by: Eric Dumazet <edumazet@google.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    include/net/inet_frag.h | 11 +++++++++--
    include/net/ipv6_frag.h | 3 ++-
    net/ipv4/inet_fragment.c | 8 +++++---
    net/ipv4/ip_fragment.c | 3 ++-
    4 files changed, 18 insertions(+), 7 deletions(-)

    --- a/include/net/inet_frag.h
    +++ b/include/net/inet_frag.h
    @@ -117,8 +117,15 @@ int fqdir_init(struct fqdir **fqdirp, st

    static inline void fqdir_pre_exit(struct fqdir *fqdir)
    {
    - fqdir->high_thresh = 0; /* prevent creation of new frags */
    - fqdir->dead = true;
    + /* Prevent creation of new frags.
    + * Pairs with READ_ONCE() in inet_frag_find().
    + */
    + WRITE_ONCE(fqdir->high_thresh, 0);
    +
    + /* Pairs with READ_ONCE() in inet_frag_kill(), ip_expire()
    + * and ip6frag_expire_frag_queue().
    + */
    + WRITE_ONCE(fqdir->dead, true);
    }
    void fqdir_exit(struct fqdir *fqdir);

    --- a/include/net/ipv6_frag.h
    +++ b/include/net/ipv6_frag.h
    @@ -67,7 +67,8 @@ ip6frag_expire_frag_queue(struct net *ne
    struct sk_buff *head;

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

    --- a/net/ipv4/inet_fragment.c
    +++ b/net/ipv4/inet_fragment.c
    @@ -235,9 +235,9 @@ void inet_frag_kill(struct inet_frag_que
    /* The RCU read lock provides a memory barrier
    * guaranteeing that if fqdir->dead is false then
    * the hash table destruction will not start until
    - * after we unlock. Paired with inet_frags_exit_net().
    + * after we unlock. Paired with fqdir_pre_exit().
    */
    - if (!fqdir->dead) {
    + if (!READ_ONCE(fqdir->dead)) {
    rhashtable_remove_fast(&fqdir->rhashtable, &fq->node,
    fqdir->f->rhash_params);
    refcount_dec(&fq->refcnt);
    @@ -352,9 +352,11 @@ static struct inet_frag_queue *inet_frag
    /* TODO : call from rcu_read_lock() and no longer use refcount_inc_not_zero() */
    struct inet_frag_queue *inet_frag_find(struct fqdir *fqdir, void *key)
    {
    + /* This pairs with WRITE_ONCE() in fqdir_pre_exit(). */
    + long high_thresh = READ_ONCE(fqdir->high_thresh);
    struct inet_frag_queue *fq = NULL, *prev;

    - if (!fqdir->high_thresh || frag_mem_limit(fqdir) > fqdir->high_thresh)
    + if (!high_thresh || frag_mem_limit(fqdir) > high_thresh)
    return NULL;

    rcu_read_lock();
    --- a/net/ipv4/ip_fragment.c
    +++ b/net/ipv4/ip_fragment.c
    @@ -144,7 +144,8 @@ static void ip_expire(struct timer_list

    rcu_read_lock();

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

    spin_lock(&qp->q.lock);

    \
     
     \ /
      Last update: 2022-01-25 04:09    [W:3.501 / U:0.064 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site