Skip to content
  • David Ahern's avatar
    ipv6: Handle race in addrconf_dad_work · a3ce2a21
    David Ahern authored
    Rajendra reported a kernel panic when a link was taken down:
    
    [ 6870.263084] BUG: unable to handle kernel NULL pointer dereference at 00000000000000a8
    [ 6870.271856] IP: [<ffffffff8efc5764>] __ipv6_ifa_notify+0x154/0x290
    
    <snip>
    
    [ 6870.570501] Call Trace:
    [ 6870.573238] [<ffffffff8efc58c6>] ? ipv6_ifa_notify+0x26/0x40
    [ 6870.579665] [<ffffffff8efc98ec>] ? addrconf_dad_completed+0x4c/0x2c0
    [ 6870.586869] [<ffffffff8efe70c6>] ? ipv6_dev_mc_inc+0x196/0x260
    [ 6870.593491] [<ffffffff8efc9c6a>] ? addrconf_dad_work+0x10a/0x430
    [ 6870.600305] [<ffffffff8f01ade4>] ? __switch_to_asm+0x34/0x70
    [ 6870.606732] [<ffffffff8ea93a7a>] ? process_one_work+0x18a/0x430
    [ 6870.613449] [<ffffffff8ea93d6d>] ? worker_thread+0x4d/0x490
    [ 6870.619778] [<ffffffff8ea93d20>] ? process_one_work+0x430/0x430
    [ 6870.626495] [<ffffffff8ea99dd9>] ? kthread+0xd9/0xf0
    [ 6870.632145] [<ffffffff8f01ade4>] ? __switch_to_asm+0x34/0x70
    [ 6870.638573] [<ffffffff8ea99d00>] ? kthread_park+0x60/0x60
    [ 6870.644707] [<ffffffff8f01ae77>] ? ret_from_fork+0x57/0x70
    [ 6870.650936] Code: 31 c0 31 d2 41 b9 20 00 08 02 b9 09 00 00 0
    
    addrconf_dad_work is kicked to be scheduled when a device is brought
    up. There is a race between addrcond_dad_work getting scheduled and
    taking the rtnl lock and a process taking the link down (under rtnl).
    The latter removes the host route from the inet6_addr as part of
    addrconf_ifdown which is run for NETDEV_DOWN. The former attempts
    to use the host route in ipv6_ifa_notify. If the down event removes
    the host route due to the race to the rtnl, then the BUG listed above
    occurs.
    
    This scenario does not occur when the ipv6 address is not kept
    (net.ipv6.conf.all.keep_addr_on_down = 0) as addrconf_ifdown sets the
    state of the ifp to DEAD. Handle when the addresses are kept by checking
    IF_READY which is reset by addrconf_ifdown.
    
    The 'dead' flag for an inet6_addr is set only under rtnl, in
    addrconf_ifdown and it means the device is getting removed (or IPv6 is
    disabled). The interesting cases for changing the idev flag are
    addrconf_notify (NETDEV_UP and NETDEV_CHANGE) and addrconf_ifdown
    (reset the flag). The former does not have the idev lock - only rtnl;
    the latter has both. Based on that the existing dead + IF_READY check
    can be moved to right after the rtnl_lock in addrconf_dad_work.
    
    Fixes: f1705ec1
    
     ("net: ipv6: Make address flushing on ifdown optional")
    Reported-by: default avatarRajendra Dendukuri <rajendra.dendukuri@broadcom.com>
    Signed-off-by: default avatarDavid Ahern <dsahern@gmail.com>
    Reviewed-by: default avatarEric Dumazet <edumazet@google.com>
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    a3ce2a21