lkml.org 
[lkml]   [2018]   [Apr]   [27]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.9 64/74] net: fix deadlock while clearing neighbor proxy table
    Date
    4.9-stable review patch.  If anyone has any objections, please let me know.

    ------------------

    From: Wolfgang Bumiller <w.bumiller@proxmox.com>


    [ Upstream commit 53b76cdf7e8fecec1d09e38aad2f8579882591a8 ]

    When coming from ndisc_netdev_event() in net/ipv6/ndisc.c,
    neigh_ifdown() is called with &nd_tbl, locking this while
    clearing the proxy neighbor entries when eg. deleting an
    interface. Calling the table's pndisc_destructor() with the
    lock still held, however, can cause a deadlock: When a
    multicast listener is available an IGMP packet of type
    ICMPV6_MGM_REDUCTION may be sent out. When reaching
    ip6_finish_output2(), if no neighbor entry for the target
    address is found, __neigh_create() is called with &nd_tbl,
    which it'll want to lock.

    Move the elements into their own list, then unlock the table
    and perform the destruction.

    Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=199289
    Fixes: 6fd6ce2056de ("ipv6: Do not depend on rt->n in ip6_finish_output2().")
    Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    net/core/neighbour.c | 28 ++++++++++++++++++----------
    1 file changed, 18 insertions(+), 10 deletions(-)

    --- a/net/core/neighbour.c
    +++ b/net/core/neighbour.c
    @@ -54,7 +54,8 @@ do { \
    static void neigh_timer_handler(unsigned long arg);
    static void __neigh_notify(struct neighbour *n, int type, int flags);
    static void neigh_update_notify(struct neighbour *neigh);
    -static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev);
    +static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
    + struct net_device *dev);

    #ifdef CONFIG_PROC_FS
    static const struct file_operations neigh_stat_seq_fops;
    @@ -254,8 +255,7 @@ int neigh_ifdown(struct neigh_table *tbl
    {
    write_lock_bh(&tbl->lock);
    neigh_flush_dev(tbl, dev);
    - pneigh_ifdown(tbl, dev);
    - write_unlock_bh(&tbl->lock);
    + pneigh_ifdown_and_unlock(tbl, dev);

    del_timer_sync(&tbl->proxy_timer);
    pneigh_queue_purge(&tbl->proxy_queue);
    @@ -645,9 +645,10 @@ int pneigh_delete(struct neigh_table *tb
    return -ENOENT;
    }

    -static int pneigh_ifdown(struct neigh_table *tbl, struct net_device *dev)
    +static int pneigh_ifdown_and_unlock(struct neigh_table *tbl,
    + struct net_device *dev)
    {
    - struct pneigh_entry *n, **np;
    + struct pneigh_entry *n, **np, *freelist = NULL;
    u32 h;

    for (h = 0; h <= PNEIGH_HASHMASK; h++) {
    @@ -655,16 +656,23 @@ static int pneigh_ifdown(struct neigh_ta
    while ((n = *np) != NULL) {
    if (!dev || n->dev == dev) {
    *np = n->next;
    - if (tbl->pdestructor)
    - tbl->pdestructor(n);
    - if (n->dev)
    - dev_put(n->dev);
    - kfree(n);
    + n->next = freelist;
    + freelist = n;
    continue;
    }
    np = &n->next;
    }
    }
    + write_unlock_bh(&tbl->lock);
    + while ((n = freelist)) {
    + freelist = n->next;
    + n->next = NULL;
    + if (tbl->pdestructor)
    + tbl->pdestructor(n);
    + if (n->dev)
    + dev_put(n->dev);
    + kfree(n);
    + }
    return -ENOENT;
    }


    \
     
     \ /
      Last update: 2018-04-27 17:04    [W:4.151 / U:0.188 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site