diff options
author | Hannes Frederic Sowa <hannes@stressinduktion.org> | 2013-07-01 14:21:30 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-07-02 15:44:18 -0400 |
commit | 8822b64a0fa64a5dd1dfcf837c5b0be83f8c05d1 (patch) | |
tree | 4dd6ee49abf26c938b4dc7d08bc7ada963c8f414 | |
parent | b9eef55c2ab33053ae236b5d383965f9ee6a0094 (diff) |
ipv6: call udp_push_pending_frames when uncorking a socket with AF_INET pending data
We accidentally call down to ip6_push_pending_frames when uncorking
pending AF_INET data on a ipv6 socket. This results in the following
splat (from Dave Jones):
skbuff: skb_under_panic: text:ffffffff816765f6 len:48 put:40 head:ffff88013deb6df0 data:ffff88013deb6dec tail:0x2c end:0xc0 dev:<NULL>
------------[ cut here ]------------
kernel BUG at net/core/skbuff.c:126!
invalid opcode: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC
Modules linked in: dccp_ipv4 dccp 8021q garp bridge stp dlci mpoa snd_seq_dummy sctp fuse hidp tun bnep nfnetlink scsi_transport_iscsi rfcomm can_raw can_bcm af_802154 appletalk caif_socket can caif ipt_ULOG x25 rose af_key pppoe pppox ipx phonet irda llc2 ppp_generic slhc p8023 psnap p8022 llc crc_ccitt atm bluetooth
+netrom ax25 nfc rfkill rds af_rxrpc coretemp hwmon kvm_intel kvm crc32c_intel snd_hda_codec_realtek ghash_clmulni_intel microcode pcspkr snd_hda_codec_hdmi snd_hda_intel snd_hda_codec snd_hwdep usb_debug snd_seq snd_seq_device snd_pcm e1000e snd_page_alloc snd_timer ptp snd pps_core soundcore xfs libcrc32c
CPU: 2 PID: 8095 Comm: trinity-child2 Not tainted 3.10.0-rc7+ #37
task: ffff8801f52c2520 ti: ffff8801e6430000 task.ti: ffff8801e6430000
RIP: 0010:[<ffffffff816e759c>] [<ffffffff816e759c>] skb_panic+0x63/0x65
RSP: 0018:ffff8801e6431de8 EFLAGS: 00010282
RAX: 0000000000000086 RBX: ffff8802353d3cc0 RCX: 0000000000000006
RDX: 0000000000003b90 RSI: ffff8801f52c2ca0 RDI: ffff8801f52c2520
RBP: ffff8801e6431e08 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000001 R11: 0000000000000001 R12: ffff88022ea0c800
R13: ffff88022ea0cdf8 R14: ffff8802353ecb40 R15: ffffffff81cc7800
FS: 00007f5720a10740(0000) GS:ffff880244c00000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000005862000 CR3: 000000022843c000 CR4: 00000000001407e0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000600
Stack:
ffff88013deb6dec 000000000000002c 00000000000000c0 ffffffff81a3f6e4
ffff8801e6431e18 ffffffff8159a9aa ffff8801e6431e90 ffffffff816765f6
ffffffff810b756b 0000000700000002 ffff8801e6431e40 0000fea9292aa8c0
Call Trace:
[<ffffffff8159a9aa>] skb_push+0x3a/0x40
[<ffffffff816765f6>] ip6_push_pending_frames+0x1f6/0x4d0
[<ffffffff810b756b>] ? mark_held_locks+0xbb/0x140
[<ffffffff81694919>] udp_v6_push_pending_frames+0x2b9/0x3d0
[<ffffffff81694660>] ? udplite_getfrag+0x20/0x20
[<ffffffff8162092a>] udp_lib_setsockopt+0x1aa/0x1f0
[<ffffffff811cc5e7>] ? fget_light+0x387/0x4f0
[<ffffffff816958a4>] udpv6_setsockopt+0x34/0x40
[<ffffffff815949f4>] sock_common_setsockopt+0x14/0x20
[<ffffffff81593c31>] SyS_setsockopt+0x71/0xd0
[<ffffffff816f5d54>] tracesys+0xdd/0xe2
Code: 00 00 48 89 44 24 10 8b 87 d8 00 00 00 48 89 44 24 08 48 8b 87 e8 00 00 00 48 c7 c7 c0 04 aa 81 48 89 04 24 31 c0 e8 e1 7e ff ff <0f> 0b 55 48 89 e5 0f 0b 55 48 89 e5 0f 0b 55 48 89 e5 0f 0b 55
RIP [<ffffffff816e759c>] skb_panic+0x63/0x65
RSP <ffff8801e6431de8>
This patch adds a check if the pending data is of address family AF_INET
and directly calls udp_push_ending_frames from udp_v6_push_pending_frames
if that is the case.
This bug was found by Dave Jones with trinity.
(Also move the initialization of fl6 below the AF_INET check, even if
not strictly necessary.)
Cc: Dave Jones <davej@redhat.com>
Cc: YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Signed-off-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/net/udp.h | 1 | ||||
-rw-r--r-- | net/ipv4/udp.c | 3 | ||||
-rw-r--r-- | net/ipv6/udp.c | 7 |
3 files changed, 9 insertions, 2 deletions
diff --git a/include/net/udp.h b/include/net/udp.h index b30a71a51839..74c10ec5e74f 100644 --- a/include/net/udp.h +++ b/include/net/udp.h | |||
@@ -181,6 +181,7 @@ extern int udp_get_port(struct sock *sk, unsigned short snum, | |||
181 | extern void udp_err(struct sk_buff *, u32); | 181 | extern void udp_err(struct sk_buff *, u32); |
182 | extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, | 182 | extern int udp_sendmsg(struct kiocb *iocb, struct sock *sk, |
183 | struct msghdr *msg, size_t len); | 183 | struct msghdr *msg, size_t len); |
184 | extern int udp_push_pending_frames(struct sock *sk); | ||
184 | extern void udp_flush_pending_frames(struct sock *sk); | 185 | extern void udp_flush_pending_frames(struct sock *sk); |
185 | extern int udp_rcv(struct sk_buff *skb); | 186 | extern int udp_rcv(struct sk_buff *skb); |
186 | extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); | 187 | extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg); |
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 959502afd8d9..6b270e53c207 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c | |||
@@ -800,7 +800,7 @@ send: | |||
800 | /* | 800 | /* |
801 | * Push out all pending data as one UDP datagram. Socket is locked. | 801 | * Push out all pending data as one UDP datagram. Socket is locked. |
802 | */ | 802 | */ |
803 | static int udp_push_pending_frames(struct sock *sk) | 803 | int udp_push_pending_frames(struct sock *sk) |
804 | { | 804 | { |
805 | struct udp_sock *up = udp_sk(sk); | 805 | struct udp_sock *up = udp_sk(sk); |
806 | struct inet_sock *inet = inet_sk(sk); | 806 | struct inet_sock *inet = inet_sk(sk); |
@@ -819,6 +819,7 @@ out: | |||
819 | up->pending = 0; | 819 | up->pending = 0; |
820 | return err; | 820 | return err; |
821 | } | 821 | } |
822 | EXPORT_SYMBOL(udp_push_pending_frames); | ||
822 | 823 | ||
823 | int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, | 824 | int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, |
824 | size_t len) | 825 | size_t len) |
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index f77e34c5a0e2..b6f31437a1f8 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c | |||
@@ -959,11 +959,16 @@ static int udp_v6_push_pending_frames(struct sock *sk) | |||
959 | struct udphdr *uh; | 959 | struct udphdr *uh; |
960 | struct udp_sock *up = udp_sk(sk); | 960 | struct udp_sock *up = udp_sk(sk); |
961 | struct inet_sock *inet = inet_sk(sk); | 961 | struct inet_sock *inet = inet_sk(sk); |
962 | struct flowi6 *fl6 = &inet->cork.fl.u.ip6; | 962 | struct flowi6 *fl6; |
963 | int err = 0; | 963 | int err = 0; |
964 | int is_udplite = IS_UDPLITE(sk); | 964 | int is_udplite = IS_UDPLITE(sk); |
965 | __wsum csum = 0; | 965 | __wsum csum = 0; |
966 | 966 | ||
967 | if (up->pending == AF_INET) | ||
968 | return udp_push_pending_frames(sk); | ||
969 | |||
970 | fl6 = &inet->cork.fl.u.ip6; | ||
971 | |||
967 | /* Grab the skbuff where UDP header space exists. */ | 972 | /* Grab the skbuff where UDP header space exists. */ |
968 | if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) | 973 | if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) |
969 | goto out; | 974 | goto out; |