| From foo@baz Sat Feb 2 10:53:21 CET 2019 |
| From: Jacob Wen <jian.w.wen@oracle.com> |
| Date: Wed, 30 Jan 2019 14:55:14 +0800 |
| Subject: l2tp: fix reading optional fields of L2TPv3 |
| |
| From: Jacob Wen <jian.w.wen@oracle.com> |
| |
| [ Upstream commit 4522a70db7aa5e77526a4079628578599821b193 ] |
| |
| Use pskb_may_pull() to make sure the optional fields are in skb linear |
| parts, so we can safely read them later. |
| |
| It's easy to reproduce the issue with a net driver that supports paged |
| skb data. Just create a L2TPv3 over IP tunnel and then generates some |
| network traffic. |
| Once reproduced, rx err in /sys/kernel/debug/l2tp/tunnels will increase. |
| |
| Changes in v4: |
| 1. s/l2tp_v3_pull_opt/l2tp_v3_ensure_opt_in_linear/ |
| 2. s/tunnel->version != L2TP_HDR_VER_2/tunnel->version == L2TP_HDR_VER_3/ |
| 3. Add 'Fixes' in commit messages. |
| |
| Changes in v3: |
| 1. To keep consistency, move the code out of l2tp_recv_common. |
| 2. Use "net" instead of "net-next", since this is a bug fix. |
| |
| Changes in v2: |
| 1. Only fix L2TPv3 to make code simple. |
| To fix both L2TPv3 and L2TPv2, we'd better refactor l2tp_recv_common. |
| It's complicated to do so. |
| 2. Reloading pointers after pskb_may_pull |
| |
| Fixes: f7faffa3ff8e ("l2tp: Add L2TPv3 protocol support") |
| Fixes: 0d76751fad77 ("l2tp: Add L2TPv3 IP encapsulation (no UDP) support") |
| Fixes: a32e0eec7042 ("l2tp: introduce L2TPv3 IP encapsulation support for IPv6") |
| Signed-off-by: Jacob Wen <jian.w.wen@oracle.com> |
| Acked-by: Guillaume Nault <gnault@redhat.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/l2tp/l2tp_core.c | 4 ++++ |
| net/l2tp/l2tp_core.h | 20 ++++++++++++++++++++ |
| net/l2tp/l2tp_ip.c | 3 +++ |
| net/l2tp/l2tp_ip6.c | 3 +++ |
| 4 files changed, 30 insertions(+) |
| |
| --- a/net/l2tp/l2tp_core.c |
| +++ b/net/l2tp/l2tp_core.c |
| @@ -883,6 +883,10 @@ static int l2tp_udp_recv_core(struct l2t |
| goto error; |
| } |
| |
| + if (tunnel->version == L2TP_HDR_VER_3 && |
| + l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr)) |
| + goto error; |
| + |
| l2tp_recv_common(session, skb, ptr, optr, hdrflags, length); |
| l2tp_session_dec_refcount(session); |
| |
| --- a/net/l2tp/l2tp_core.h |
| +++ b/net/l2tp/l2tp_core.h |
| @@ -301,6 +301,26 @@ static inline bool l2tp_tunnel_uses_xfrm |
| } |
| #endif |
| |
| +static inline int l2tp_v3_ensure_opt_in_linear(struct l2tp_session *session, struct sk_buff *skb, |
| + unsigned char **ptr, unsigned char **optr) |
| +{ |
| + int opt_len = session->peer_cookie_len + l2tp_get_l2specific_len(session); |
| + |
| + if (opt_len > 0) { |
| + int off = *ptr - *optr; |
| + |
| + if (!pskb_may_pull(skb, off + opt_len)) |
| + return -1; |
| + |
| + if (skb->data != *optr) { |
| + *optr = skb->data; |
| + *ptr = skb->data + off; |
| + } |
| + } |
| + |
| + return 0; |
| +} |
| + |
| #define l2tp_printk(ptr, type, func, fmt, ...) \ |
| do { \ |
| if (((ptr)->debug) & (type)) \ |
| --- a/net/l2tp/l2tp_ip.c |
| +++ b/net/l2tp/l2tp_ip.c |
| @@ -165,6 +165,9 @@ static int l2tp_ip_recv(struct sk_buff * |
| print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length); |
| } |
| |
| + if (l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr)) |
| + goto discard_sess; |
| + |
| l2tp_recv_common(session, skb, ptr, optr, 0, skb->len); |
| l2tp_session_dec_refcount(session); |
| |
| --- a/net/l2tp/l2tp_ip6.c |
| +++ b/net/l2tp/l2tp_ip6.c |
| @@ -178,6 +178,9 @@ static int l2tp_ip6_recv(struct sk_buff |
| print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, ptr, length); |
| } |
| |
| + if (l2tp_v3_ensure_opt_in_linear(session, skb, &ptr, &optr)) |
| + goto discard_sess; |
| + |
| l2tp_recv_common(session, skb, ptr, optr, 0, skb->len); |
| l2tp_session_dec_refcount(session); |
| |