lkml.org 
[lkml]   [2019]   [Mar]   [18]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 4.19 14/52] route: set the deleted fnhe fnhe_daddr to 0 in ip_del_fnhe to fix a race
    Date
    4.19-stable review patch.  If anyone has any objections, please let me know.

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

    From: Xin Long <lucien.xin@gmail.com>

    [ Upstream commit ee60ad219f5c7c4fb2f047f88037770063ef785f ]

    The race occurs in __mkroute_output() when 2 threads lookup a dst:

    CPU A CPU B
    find_exception()
    find_exception() [fnhe expires]
    ip_del_fnhe() [fnhe is deleted]
    rt_bind_exception()

    In rt_bind_exception() it will bind a deleted fnhe with the new dst, and
    this dst will get no chance to be freed. It causes a dev defcnt leak and
    consecutive dmesg warnings:

    unregister_netdevice: waiting for ethX to become free. Usage count = 1

    Especially thanks Jon to identify the issue.

    This patch fixes it by setting fnhe_daddr to 0 in ip_del_fnhe() to stop
    binding the deleted fnhe with a new dst when checking fnhe's fnhe_daddr
    and daddr in rt_bind_exception().

    It works as both ip_del_fnhe() and rt_bind_exception() are protected by
    fnhe_lock and the fhne is freed by kfree_rcu().

    Fixes: deed49df7390 ("route: check and remove route cache when we get route")
    Signed-off-by: Jon Maxwell <jmaxwell37@gmail.com>
    Signed-off-by: Xin Long <lucien.xin@gmail.com>
    Reviewed-by: David Ahern <dsahern@gmail.com>
    Signed-off-by: David S. Miller <davem@davemloft.net>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    net/ipv4/route.c | 4 ++++
    1 file changed, 4 insertions(+)

    --- a/net/ipv4/route.c
    +++ b/net/ipv4/route.c
    @@ -1308,6 +1308,10 @@ static void ip_del_fnhe(struct fib_nh *n
    if (fnhe->fnhe_daddr == daddr) {
    rcu_assign_pointer(*fnhe_p, rcu_dereference_protected(
    fnhe->fnhe_next, lockdep_is_held(&fnhe_lock)));
    + /* set fnhe_daddr to 0 to ensure it won't bind with
    + * new dsts in rt_bind_exception().
    + */
    + fnhe->fnhe_daddr = 0;
    fnhe_flush_routes(fnhe);
    kfree_rcu(fnhe, rcu);
    break;

    \
     
     \ /
      Last update: 2019-03-18 10:43    [W:5.732 / U:0.372 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site