| From 72fb96e7bdbbdd4421b0726992496531060f3636 Mon Sep 17 00:00:00 2001 |
| From: Eric Dumazet <edumazet@google.com> |
| Date: Thu, 9 Feb 2017 16:15:52 -0800 |
| Subject: [PATCH] l2tp: do not use udp_ioctl() |
| |
| commit 72fb96e7bdbbdd4421b0726992496531060f3636 upstream. |
| |
| udp_ioctl(), as its name suggests, is used by UDP protocols, |
| but is also used by L2TP :( |
| |
| L2TP should use its own handler, because it really does not |
| look the same. |
| |
| SIOCINQ for instance should not assume UDP checksum or headers. |
| |
| Thanks to Andrey and syzkaller team for providing the report |
| and a nice reproducer. |
| |
| While crashes only happen on recent kernels (after commit |
| 7c13f97ffde6 ("udp: do fwd memory scheduling on dequeue")), this |
| probably needs to be backported to older kernels. |
| |
| Fixes: 7c13f97ffde6 ("udp: do fwd memory scheduling on dequeue") |
| Fixes: 85584672012e ("udp: Fix udp_poll() and ioctl()") |
| Signed-off-by: Eric Dumazet <edumazet@google.com> |
| Reported-by: Andrey Konovalov <andreyknvl@google.com> |
| Acked-by: Paolo Abeni <pabeni@redhat.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| |
| diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h |
| index 8f560f7140a0..aebf281d09ee 100644 |
| --- a/net/l2tp/l2tp_core.h |
| +++ b/net/l2tp/l2tp_core.h |
| @@ -263,6 +263,7 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, |
| int l2tp_nl_register_ops(enum l2tp_pwtype pw_type, |
| const struct l2tp_nl_cmd_ops *ops); |
| void l2tp_nl_unregister_ops(enum l2tp_pwtype pw_type); |
| +int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg); |
| |
| /* Session reference counts. Incremented when code obtains a reference |
| * to a session. |
| diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c |
| index 3d73278b86ca..28c21546d5b6 100644 |
| --- a/net/l2tp/l2tp_ip.c |
| +++ b/net/l2tp/l2tp_ip.c |
| @@ -11,6 +11,7 @@ |
| |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
| |
| +#include <asm/ioctls.h> |
| #include <linux/icmp.h> |
| #include <linux/module.h> |
| #include <linux/skbuff.h> |
| @@ -553,6 +554,30 @@ out: |
| return err ? err : copied; |
| } |
| |
| +int l2tp_ioctl(struct sock *sk, int cmd, unsigned long arg) |
| +{ |
| + struct sk_buff *skb; |
| + int amount; |
| + |
| + switch (cmd) { |
| + case SIOCOUTQ: |
| + amount = sk_wmem_alloc_get(sk); |
| + break; |
| + case SIOCINQ: |
| + spin_lock_bh(&sk->sk_receive_queue.lock); |
| + skb = skb_peek(&sk->sk_receive_queue); |
| + amount = skb ? skb->len : 0; |
| + spin_unlock_bh(&sk->sk_receive_queue.lock); |
| + break; |
| + |
| + default: |
| + return -ENOIOCTLCMD; |
| + } |
| + |
| + return put_user(amount, (int __user *)arg); |
| +} |
| +EXPORT_SYMBOL(l2tp_ioctl); |
| + |
| static struct proto l2tp_ip_prot = { |
| .name = "L2TP/IP", |
| .owner = THIS_MODULE, |
| @@ -561,7 +586,7 @@ static struct proto l2tp_ip_prot = { |
| .bind = l2tp_ip_bind, |
| .connect = l2tp_ip_connect, |
| .disconnect = l2tp_ip_disconnect, |
| - .ioctl = udp_ioctl, |
| + .ioctl = l2tp_ioctl, |
| .destroy = l2tp_ip_destroy_sock, |
| .setsockopt = ip_setsockopt, |
| .getsockopt = ip_getsockopt, |
| diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c |
| index 331ccf5a7bad..f47c45250f86 100644 |
| --- a/net/l2tp/l2tp_ip6.c |
| +++ b/net/l2tp/l2tp_ip6.c |
| @@ -722,7 +722,7 @@ static struct proto l2tp_ip6_prot = { |
| .bind = l2tp_ip6_bind, |
| .connect = l2tp_ip6_connect, |
| .disconnect = l2tp_ip6_disconnect, |
| - .ioctl = udp_ioctl, |
| + .ioctl = l2tp_ioctl, |
| .destroy = l2tp_ip6_destroy_sock, |
| .setsockopt = ipv6_setsockopt, |
| .getsockopt = ipv6_getsockopt, |
| -- |
| 2.12.0 |
| |