icmp: icmp_sk() should not use smp_processor_id() in preemptible code

Pass namespace into icmp_xmit_lock, obtain socket inside and return
it as a result for caller.

Thanks Alexey Dobryan for this report:

Steps to reproduce:

CONFIG_PREEMPT=y
CONFIG_DEBUG_PREEMPT=y
tracepath <something>

BUG: using smp_processor_id() in preemptible [00000000] code: tracepath/3205
caller is icmp_sk+0x15/0x30
Pid: 3205, comm: tracepath Not tainted 2.6.27-rc4 #1

Call Trace:
[<ffffffff8031af14>] debug_smp_processor_id+0xe4/0xf0
[<ffffffff80409405>] icmp_sk+0x15/0x30
[<ffffffff8040a17b>] icmp_send+0x4b/0x3f0
[<ffffffff8025a415>] ? trace_hardirqs_on_caller+0xd5/0x160
[<ffffffff8025a4ad>] ? trace_hardirqs_on+0xd/0x10
[<ffffffff8023a475>] ? local_bh_enable_ip+0x95/0x110
[<ffffffff804285b9>] ? _spin_unlock_bh+0x39/0x40
[<ffffffff8025a26c>] ? mark_held_locks+0x4c/0x90
[<ffffffff8025a4ad>] ? trace_hardirqs_on+0xd/0x10
[<ffffffff8025a415>] ? trace_hardirqs_on_caller+0xd5/0x160
[<ffffffff803e91b4>] ip_fragment+0x8d4/0x900
[<ffffffff803e7030>] ? ip_finish_output2+0x0/0x290
[<ffffffff803e91e0>] ? ip_finish_output+0x0/0x60
[<ffffffff803e6650>] ? dst_output+0x0/0x10
[<ffffffff803e922c>] ip_finish_output+0x4c/0x60
[<ffffffff803e92e3>] ip_output+0xa3/0xf0
[<ffffffff803e68d0>] ip_local_out+0x20/0x30
[<ffffffff803e753f>] ip_push_pending_frames+0x27f/0x400
[<ffffffff80406313>] udp_push_pending_frames+0x233/0x3d0
[<ffffffff804067d1>] udp_sendmsg+0x321/0x6f0
[<ffffffff8040d155>] inet_sendmsg+0x45/0x80
[<ffffffff803b967f>] sock_sendmsg+0xdf/0x110
[<ffffffff8024a100>] ? autoremove_wake_function+0x0/0x40
[<ffffffff80257ce5>] ? validate_chain+0x415/0x1010
[<ffffffff8027dc10>] ? __do_fault+0x140/0x450
[<ffffffff802597d0>] ? __lock_acquire+0x260/0x590
[<ffffffff803b9e55>] ? sockfd_lookup_light+0x45/0x80
[<ffffffff803ba50a>] sys_sendto+0xea/0x120
[<ffffffff80428e42>] ? _spin_unlock_irqrestore+0x42/0x80
[<ffffffff803134bc>] ? __up_read+0x4c/0xb0
[<ffffffff8024e0c6>] ? up_read+0x26/0x30
[<ffffffff8020b8bb>] system_call_fastpath+0x16/0x1b

icmp6_sk() is similar.

Signed-off-by: Denis V. Lunev <den@openvz.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by Denis V. Lunev and committed by David S. Miller fdc0bde9 f6e0b239

+26 -19
+14 -8
net/ipv4/icmp.c
··· 204 204 return net->ipv4.icmp_sk[smp_processor_id()]; 205 205 } 206 206 207 - static inline int icmp_xmit_lock(struct sock *sk) 207 + static inline struct sock *icmp_xmit_lock(struct net *net) 208 208 { 209 + struct sock *sk; 210 + 209 211 local_bh_disable(); 212 + 213 + sk = icmp_sk(net); 210 214 211 215 if (unlikely(!spin_trylock(&sk->sk_lock.slock))) { 212 216 /* This can happen if the output path signals a 213 217 * dst_link_failure() for an outgoing ICMP packet. 214 218 */ 215 219 local_bh_enable(); 216 - return 1; 220 + return NULL; 217 221 } 218 - return 0; 222 + return sk; 219 223 } 220 224 221 225 static inline void icmp_xmit_unlock(struct sock *sk) ··· 358 354 struct ipcm_cookie ipc; 359 355 struct rtable *rt = skb->rtable; 360 356 struct net *net = dev_net(rt->u.dst.dev); 361 - struct sock *sk = icmp_sk(net); 362 - struct inet_sock *inet = inet_sk(sk); 357 + struct sock *sk; 358 + struct inet_sock *inet; 363 359 __be32 daddr; 364 360 365 361 if (ip_options_echo(&icmp_param->replyopts, skb)) 366 362 return; 367 363 368 - if (icmp_xmit_lock(sk)) 364 + sk = icmp_xmit_lock(net); 365 + if (sk == NULL) 369 366 return; 367 + inet = inet_sk(sk); 370 368 371 369 icmp_param->data.icmph.checksum = 0; 372 370 ··· 425 419 if (!rt) 426 420 goto out; 427 421 net = dev_net(rt->u.dst.dev); 428 - sk = icmp_sk(net); 429 422 430 423 /* 431 424 * Find the original header. It is expected to be valid, of course. ··· 488 483 } 489 484 } 490 485 491 - if (icmp_xmit_lock(sk)) 486 + sk = icmp_xmit_lock(net); 487 + if (sk == NULL) 492 488 return; 493 489 494 490 /*
+12 -11
net/ipv6/icmp.c
··· 91 91 .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, 92 92 }; 93 93 94 - static __inline__ int icmpv6_xmit_lock(struct sock *sk) 94 + static __inline__ struct sock *icmpv6_xmit_lock(struct net *net) 95 95 { 96 + struct sock *sk; 97 + 96 98 local_bh_disable(); 97 99 100 + sk = icmpv6_sk(net); 98 101 if (unlikely(!spin_trylock(&sk->sk_lock.slock))) { 99 102 /* This can happen if the output path (f.e. SIT or 100 103 * ip6ip6 tunnel) signals dst_link_failure() for an 101 104 * outgoing ICMP6 packet. 102 105 */ 103 106 local_bh_enable(); 104 - return 1; 107 + return NULL; 105 108 } 106 - return 0; 109 + return sk; 107 110 } 108 111 109 112 static __inline__ void icmpv6_xmit_unlock(struct sock *sk) ··· 395 392 fl.fl_icmp_code = code; 396 393 security_skb_classify_flow(skb, &fl); 397 394 398 - sk = icmpv6_sk(net); 399 - np = inet6_sk(sk); 400 - 401 - if (icmpv6_xmit_lock(sk)) 395 + sk = icmpv6_xmit_lock(net); 396 + if (sk == NULL) 402 397 return; 398 + np = inet6_sk(sk); 403 399 404 400 if (!icmpv6_xrlim_allow(sk, type, &fl)) 405 401 goto out; ··· 541 539 fl.fl_icmp_type = ICMPV6_ECHO_REPLY; 542 540 security_skb_classify_flow(skb, &fl); 543 541 544 - sk = icmpv6_sk(net); 545 - np = inet6_sk(sk); 546 - 547 - if (icmpv6_xmit_lock(sk)) 542 + sk = icmpv6_xmit_lock(net); 543 + if (sk == NULL) 548 544 return; 545 + np = inet6_sk(sk); 549 546 550 547 if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) 551 548 fl.oif = np->mcast_oif;