| From 7061e17d06decf63b27432a83011934ca47b0fd0 Mon Sep 17 00:00:00 2001 |
| From: Alexei Starovoitov <ast@plumgrid.com> |
| Date: Tue, 19 Nov 2013 19:12:34 -0800 |
| Subject: ipv4: fix race in concurrent ip_route_input_slow() |
| |
| From: Alexei Starovoitov <ast@plumgrid.com> |
| |
| [ Upstream commit dcdfdf56b4a6c9437fc37dbc9cee94a788f9b0c4 ] |
| |
| CPUs can ask for local route via ip_route_input_noref() concurrently. |
| if nh_rth_input is not cached yet, CPUs will proceed to allocate |
| equivalent DSTs on 'lo' and then will try to cache them in nh_rth_input |
| via rt_cache_route() |
| Most of the time they succeed, but on occasion the following two lines: |
| orig = *p; |
| prev = cmpxchg(p, orig, rt); |
| in rt_cache_route() do race and one of the cpus fails to complete cmpxchg. |
| But ip_route_input_slow() doesn't check the return code of rt_cache_route(), |
| so dst is leaking. dst_destroy() is never called and 'lo' device |
| refcnt doesn't go to zero, which can be seen in the logs as: |
| unregister_netdevice: waiting for lo to become free. Usage count = 1 |
| Adding mdelay() between above two lines makes it easily reproducible. |
| Fix it similar to nh_pcpu_rth_output case. |
| |
| Fixes: d2d68ba9fe8b ("ipv4: Cache input routes in fib_info nexthops.") |
| Signed-off-by: Alexei Starovoitov <ast@plumgrid.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/ipv4/route.c | 8 ++++++-- |
| 1 file changed, 6 insertions(+), 2 deletions(-) |
| |
| --- a/net/ipv4/route.c |
| +++ b/net/ipv4/route.c |
| @@ -1720,8 +1720,12 @@ local_input: |
| rth->dst.error= -err; |
| rth->rt_flags &= ~RTCF_LOCAL; |
| } |
| - if (do_cache) |
| - rt_cache_route(&FIB_RES_NH(res), rth); |
| + if (do_cache) { |
| + if (unlikely(!rt_cache_route(&FIB_RES_NH(res), rth))) { |
| + rth->dst.flags |= DST_NOCACHE; |
| + rt_add_uncached_list(rth); |
| + } |
| + } |
| skb_dst_set(skb, &rth->dst); |
| err = 0; |
| goto out; |