From: Sebastian Andrzej Siewior Date: Wed, 31 Aug 2016 17:21:56 +0200 Subject: [PATCH 202/351] net: add back the missing serialization in ip_send_unicast_reply() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Origin: https://git.kernel.org/cgit/linux/kernel/git/rt/linux-stable-rt.git/commit?id=507b155f20020ad477cfe379fe332536dd19a7d2 Some time ago Sami Pietikäinen reported a crash on -RT in ip_send_unicast_reply() which was later fixed by Nicholas Mc Guire (v3.12.8-rt11). Later (v3.18.8) the code was reworked and I dropped the patch. As it turns out it was mistake. I have reports that the same crash is possible with a similar backtrace. It seems that vanilla protects access to this_cpu_ptr() via local_bh_disable(). This does not work the on -RT since we can have NET_RX and NET_TX running in parallel on the same CPU. This is brings back the old locks. |Unable to handle kernel NULL pointer dereference at virtual address 00000010 |PC is at __ip_make_skb+0x198/0x3e8 |[] (__ip_make_skb) from [] (ip_push_pending_frames+0x20/0x40) |[] (ip_push_pending_frames) from [] (ip_send_unicast_reply+0x210/0x22c) |[] (ip_send_unicast_reply) from [] (tcp_v4_send_reset+0x190/0x1c0) |[] (tcp_v4_send_reset) from [] (tcp_v4_do_rcv+0x22c/0x288) |[] (tcp_v4_do_rcv) from [] (release_sock+0xb4/0x150) |[] (release_sock) from [] (tcp_close+0x240/0x454) |[] (tcp_close) from [] (inet_release+0x74/0x7c) |[] (inet_release) from [] (sock_release+0x30/0xb0) |[] (sock_release) from [] (sock_close+0x1c/0x24) |[] (sock_close) from [] (__fput+0xe8/0x20c) |[] (__fput) from [] (____fput+0x18/0x1c) |[] (____fput) from [] (task_work_run+0xa4/0xb8) |[] (task_work_run) from [] (do_work_pending+0xd0/0xe4) |[] (do_work_pending) from [] (work_pending+0xc/0x20) |Code: e3530001 8a000001 e3a00040 ea000011 (e5973010) Cc: stable-rt@vger.kernel.org Signed-off-by: Sebastian Andrzej Siewior --- net/ipv4/tcp_ipv4.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 6549e07ce19c..84adb4755c5e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -637,6 +638,7 @@ void tcp_v4_send_check(struct sock *sk, struct sk_buff *skb) } EXPORT_SYMBOL(tcp_v4_send_check); +static DEFINE_LOCAL_IRQ_LOCK(tcp_sk_lock); /* * This routine will send an RST to the other tcp. * @@ -771,6 +773,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) arg.tos = ip_hdr(skb)->tos; arg.uid = sock_net_uid(net, sk && sk_fullsock(sk) ? sk : NULL); local_bh_disable(); + local_lock(tcp_sk_lock); ctl_sk = *this_cpu_ptr(net->ipv4.tcp_sk); if (sk) ctl_sk->sk_mark = (sk->sk_state == TCP_TIME_WAIT) ? @@ -783,6 +786,7 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) ctl_sk->sk_mark = 0; __TCP_INC_STATS(net, TCP_MIB_OUTSEGS); __TCP_INC_STATS(net, TCP_MIB_OUTRSTS); + local_unlock(tcp_sk_lock); local_bh_enable(); #ifdef CONFIG_TCP_MD5SIG @@ -863,6 +867,7 @@ static void tcp_v4_send_ack(const struct sock *sk, arg.tos = tos; arg.uid = sock_net_uid(net, sk_fullsock(sk) ? sk : NULL); local_bh_disable(); + local_lock(tcp_sk_lock); ctl_sk = *this_cpu_ptr(net->ipv4.tcp_sk); if (sk) ctl_sk->sk_mark = (sk->sk_state == TCP_TIME_WAIT) ? @@ -874,6 +879,7 @@ static void tcp_v4_send_ack(const struct sock *sk, ctl_sk->sk_mark = 0; __TCP_INC_STATS(net, TCP_MIB_OUTSEGS); + local_unlock(tcp_sk_lock); local_bh_enable(); }