net: fix crash in tcp_nuke_addr()
authorDmitry Torokhov <dtor@google.com>
Thu, 3 Sep 2015 20:08:37 +0000 (13:08 -0700)
committerJohn Stultz <john.stultz@linaro.org>
Tue, 16 Feb 2016 21:51:16 +0000 (13:51 -0800)
When iterating through sockets we need to skip sockets in TIME_WAIT
state as they use lightweight structure inet_timewait_sock that does not
have sk_lock member, and if we try to lock them we'll crash thusly:

[   89.376383] BUG: spinlock lockup suspected on CPU#0, netd/431
[   89.382139]  lock: 0xffffffc039d05070, .magic: 66d30606, .owner: /-1682098992, .owner_cpu: 0
[   89.390598] CPU: 0 PID: 431 Comm: netd Tainted: G     U  W 3.18.0 #5
[   89.397389] Hardware name: Google Tegra210 Smaug Rev 1+ (DT)
[   89.403049] Call trace:
[   89.405501] [<ffffffc0002072b4>] dump_backtrace+0x0/0x10c
[   89.410918] [<ffffffc0002073d0>] show_stack+0x10/0x1c
[   89.415971] [<ffffffc000a88608>] dump_stack+0x74/0x94
[   89.421018] [<ffffffc000257e8c>] spin_dump+0x78/0x88
[   89.425984] [<ffffffc0002580d8>] do_raw_spin_lock+0xfc/0x158
[   89.431666] [<ffffffc000a90090>] _raw_spin_lock+0x34/0x44
[   89.437059] [<ffffffc0009509a8>] tcp_nuke_addr+0x1fc/0x29c
[   89.442548] [<ffffffc0009735f4>] devinet_ioctl+0x288/0x680
[   89.448053] [<ffffffc000975004>] inet_ioctl+0xc4/0xf4
[   89.453103] [<ffffffc0008baedc>] sock_do_ioctl+0x2c/0x5c
[   89.458408] [<ffffffc0008bbb54>] sock_ioctl+0x210/0x230
[   89.463633] [<ffffffc000317088>] do_vfs_ioctl+0x4ac/0x590
[   89.469049] [<ffffffc0003171c8>] SyS_ioctl+0x5c/0x88

(or with NULL pointer dereference if lockdep is still working).

Change-Id: I07c70d9a60b125b1070ff05c4eec27daee1a3e90
Signed-off-by: Dmitry Torokhov <dtor@google.com>
net/ipv4/tcp.c

index c8cfe784c79a73070bf4d7a94d0b8ce0bb3bcdad..47b147ca69d116d0b88c0aef7c56777e3d7cc8bd 100644 (file)
@@ -3241,8 +3241,19 @@ restart:
                sk_nulls_for_each(sk, node, &tcp_hashinfo.ehash[bucket].chain) {
                        struct inet_sock *inet = inet_sk(sk);
 
+                       if (sk->sk_state == TCP_TIME_WAIT) {
+                               /*
+                                * Sockets that are in TIME_WAIT state are
+                                * instances of lightweight inet_timewait_sock,
+                                * we should simply skip them (or we'll try to
+                                * access non-existing fields and crash).
+                                */
+                               continue;
+                       }
+
                        if (sysctl_ip_dynaddr && sk->sk_state == TCP_SYN_SENT)
                                continue;
+
                        if (sock_flag(sk, SOCK_DEAD))
                                continue;