--- /dev/null
+From b954f94023dcc61388c8384f0f14eb8e42c863c5 Mon Sep 17 00:00:00 2001
+From: Paolo Abeni <pabeni@redhat.com>
+Date: Mon, 12 Mar 2018 14:54:24 +0100
+Subject: l2tp: fix races with ipv4-mapped ipv6 addresses
+
+From: Paolo Abeni <pabeni@redhat.com>
+
+commit b954f94023dcc61388c8384f0f14eb8e42c863c5 upstream.
+
+The l2tp_tunnel_create() function checks for v4mapped ipv6
+sockets and cache that flag, so that l2tp core code can
+reusing it at xmit time.
+
+If the socket is provided by the userspace, the connection
+status of the tunnel sockets can change between the tunnel
+creation and the xmit call, so that syzbot is able to
+trigger the following splat:
+
+BUG: KASAN: use-after-free in ip6_dst_idev include/net/ip6_fib.h:192
+[inline]
+BUG: KASAN: use-after-free in ip6_xmit+0x1f76/0x2260
+net/ipv6/ip6_output.c:264
+Read of size 8 at addr ffff8801bd949318 by task syz-executor4/23448
+
+CPU: 0 PID: 23448 Comm: syz-executor4 Not tainted 4.16.0-rc4+ #65
+Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS
+Google 01/01/2011
+Call Trace:
+ __dump_stack lib/dump_stack.c:17 [inline]
+ dump_stack+0x194/0x24d lib/dump_stack.c:53
+ print_address_description+0x73/0x250 mm/kasan/report.c:256
+ kasan_report_error mm/kasan/report.c:354 [inline]
+ kasan_report+0x23c/0x360 mm/kasan/report.c:412
+ __asan_report_load8_noabort+0x14/0x20 mm/kasan/report.c:433
+ ip6_dst_idev include/net/ip6_fib.h:192 [inline]
+ ip6_xmit+0x1f76/0x2260 net/ipv6/ip6_output.c:264
+ inet6_csk_xmit+0x2fc/0x580 net/ipv6/inet6_connection_sock.c:139
+ l2tp_xmit_core net/l2tp/l2tp_core.c:1053 [inline]
+ l2tp_xmit_skb+0x105f/0x1410 net/l2tp/l2tp_core.c:1148
+ pppol2tp_sendmsg+0x470/0x670 net/l2tp/l2tp_ppp.c:341
+ sock_sendmsg_nosec net/socket.c:630 [inline]
+ sock_sendmsg+0xca/0x110 net/socket.c:640
+ ___sys_sendmsg+0x767/0x8b0 net/socket.c:2046
+ __sys_sendmsg+0xe5/0x210 net/socket.c:2080
+ SYSC_sendmsg net/socket.c:2091 [inline]
+ SyS_sendmsg+0x2d/0x50 net/socket.c:2087
+ do_syscall_64+0x281/0x940 arch/x86/entry/common.c:287
+ entry_SYSCALL_64_after_hwframe+0x42/0xb7
+RIP: 0033:0x453e69
+RSP: 002b:00007f819593cc68 EFLAGS: 00000246 ORIG_RAX: 000000000000002e
+RAX: ffffffffffffffda RBX: 00007f819593d6d4 RCX: 0000000000453e69
+RDX: 0000000000000081 RSI: 000000002037ffc8 RDI: 0000000000000004
+RBP: 000000000072bea0 R08: 0000000000000000 R09: 0000000000000000
+R10: 0000000000000000 R11: 0000000000000246 R12: 00000000ffffffff
+R13: 00000000000004c3 R14: 00000000006f72e8 R15: 0000000000000000
+
+This change addresses the issues:
+* explicitly checking for TCP_ESTABLISHED for user space provided sockets
+* dropping the v4mapped flag usage - it can become outdated - and
+ explicitly invoking ipv6_addr_v4mapped() instead
+
+The issue is apparently there since ancient times.
+
+v1 -> v2: (many thanks to Guillaume)
+ - with csum issue introduced in v1
+ - replace pr_err with pr_debug
+ - fix build issue with IPV6 disabled
+ - move l2tp_sk_is_v4mapped in l2tp_core.c
+
+v2 -> v3:
+ - don't update inet_daddr for v4mapped address, unneeded
+ - drop rendundant check at creation time
+
+Reported-and-tested-by: syzbot+92fa328176eb07e4ac1a@syzkaller.appspotmail.com
+Fixes: 3557baabf280 ("[L2TP]: PPP over L2TP driver core")
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+
+---
+ net/l2tp/l2tp_core.c | 38 ++++++++++++++++++--------------------
+ net/l2tp/l2tp_core.h | 3 ---
+ 2 files changed, 18 insertions(+), 23 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -112,6 +112,13 @@ struct l2tp_net {
+ spinlock_t l2tp_session_hlist_lock;
+ };
+
++#if IS_ENABLED(CONFIG_IPV6)
++static bool l2tp_sk_is_v6(struct sock *sk)
++{
++ return sk->sk_family == PF_INET6 &&
++ !ipv6_addr_v4mapped(&sk->sk_v6_daddr);
++}
++#endif
+
+ static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk)
+ {
+@@ -1136,7 +1143,7 @@ static int l2tp_xmit_core(struct l2tp_se
+ skb->ignore_df = 1;
+ skb_dst_drop(skb);
+ #if IS_ENABLED(CONFIG_IPV6)
+- if (tunnel->sock->sk_family == PF_INET6 && !tunnel->v4mapped)
++ if (l2tp_sk_is_v6(tunnel->sock))
+ error = inet6_csk_xmit(tunnel->sock, skb, NULL);
+ else
+ #endif
+@@ -1199,6 +1206,15 @@ int l2tp_xmit_skb(struct l2tp_session *s
+ goto out_unlock;
+ }
+
++ /* The user-space may change the connection status for the user-space
++ * provided socket at run time: we must check it under the socket lock
++ */
++ if (tunnel->fd >= 0 && sk->sk_state != TCP_ESTABLISHED) {
++ kfree_skb(skb);
++ ret = NET_XMIT_DROP;
++ goto out_unlock;
++ }
++
+ inet = inet_sk(sk);
+ fl = &inet->cork.fl;
+ switch (tunnel->encap) {
+@@ -1214,7 +1230,7 @@ int l2tp_xmit_skb(struct l2tp_session *s
+
+ /* Calculate UDP checksum if configured to do so */
+ #if IS_ENABLED(CONFIG_IPV6)
+- if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)
++ if (l2tp_sk_is_v6(sk))
+ udp6_set_csum(udp_get_no_check6_tx(sk),
+ skb, &inet6_sk(sk)->saddr,
+ &sk->sk_v6_daddr, udp_len);
+@@ -1620,24 +1636,6 @@ int l2tp_tunnel_create(struct net *net,
+ if (cfg != NULL)
+ tunnel->debug = cfg->debug;
+
+-#if IS_ENABLED(CONFIG_IPV6)
+- if (sk->sk_family == PF_INET6) {
+- struct ipv6_pinfo *np = inet6_sk(sk);
+-
+- if (ipv6_addr_v4mapped(&np->saddr) &&
+- ipv6_addr_v4mapped(&sk->sk_v6_daddr)) {
+- struct inet_sock *inet = inet_sk(sk);
+-
+- tunnel->v4mapped = true;
+- inet->inet_saddr = np->saddr.s6_addr32[3];
+- inet->inet_rcv_saddr = sk->sk_v6_rcv_saddr.s6_addr32[3];
+- inet->inet_daddr = sk->sk_v6_daddr.s6_addr32[3];
+- } else {
+- tunnel->v4mapped = false;
+- }
+- }
+-#endif
+-
+ /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
+ tunnel->encap = encap;
+ if (encap == L2TP_ENCAPTYPE_UDP) {
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -191,9 +191,6 @@ struct l2tp_tunnel {
+ struct sock *sock; /* Parent socket */
+ int fd; /* Parent fd, if tunnel socket
+ * was created by userspace */
+-#if IS_ENABLED(CONFIG_IPV6)
+- bool v4mapped;
+-#endif
+
+ struct work_struct del_work;
+
--- /dev/null
+From 2f987a76a97773beafbc615b9c4d8fe79129a7f4 Mon Sep 17 00:00:00 2001
+From: Paolo Abeni <pabeni@redhat.com>
+Date: Mon, 12 Mar 2018 14:54:23 +0100
+Subject: net: ipv6: keep sk status consistent after datagram connect failure
+
+From: Paolo Abeni <pabeni@redhat.com>
+
+commit 2f987a76a97773beafbc615b9c4d8fe79129a7f4 upstream.
+
+On unsuccesful ip6_datagram_connect(), if the failure is caused by
+ip6_datagram_dst_update(), the sk peer information are cleared, but
+the sk->sk_state is preserved.
+
+If the socket was already in an established status, the overall sk
+status is inconsistent and fouls later checks in datagram code.
+
+Fix this saving the old peer information and restoring them in
+case of failure. This also aligns ipv6 datagram connect() behavior
+with ipv4.
+
+v1 -> v2:
+ - added missing Fixes tag
+
+Fixes: 85cb73ff9b74 ("net: ipv6: reset daddr and dport in sk if connect() fails")
+Signed-off-by: Paolo Abeni <pabeni@redhat.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Cc: Suren Baghdasaryan <surenb@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/ipv6/datagram.c | 21 +++++++++++++++++----
+ 1 file changed, 17 insertions(+), 4 deletions(-)
+
+--- a/net/ipv6/datagram.c
++++ b/net/ipv6/datagram.c
+@@ -145,10 +145,12 @@ int __ip6_datagram_connect(struct sock *
+ struct sockaddr_in6 *usin = (struct sockaddr_in6 *) uaddr;
+ struct inet_sock *inet = inet_sk(sk);
+ struct ipv6_pinfo *np = inet6_sk(sk);
+- struct in6_addr *daddr;
++ struct in6_addr *daddr, old_daddr;
++ __be32 fl6_flowlabel = 0;
++ __be32 old_fl6_flowlabel;
++ __be32 old_dport;
+ int addr_type;
+ int err;
+- __be32 fl6_flowlabel = 0;
+
+ if (usin->sin6_family == AF_INET) {
+ if (__ipv6_only_sock(sk))
+@@ -238,9 +240,13 @@ ipv4_connected:
+ }
+ }
+
++ /* save the current peer information before updating it */
++ old_daddr = sk->sk_v6_daddr;
++ old_fl6_flowlabel = np->flow_label;
++ old_dport = inet->inet_dport;
++
+ sk->sk_v6_daddr = *daddr;
+ np->flow_label = fl6_flowlabel;
+-
+ inet->inet_dport = usin->sin6_port;
+
+ /*
+@@ -249,8 +255,15 @@ ipv4_connected:
+ */
+
+ err = ip6_datagram_dst_update(sk, true);
+- if (err)
++ if (err) {
++ /* Restore the socket peer info, to keep it consistent with
++ * the old socket state
++ */
++ sk->sk_v6_daddr = old_daddr;
++ np->flow_label = old_fl6_flowlabel;
++ inet->inet_dport = old_dport;
+ goto out;
++ }
+
+ sk->sk_state = TCP_ESTABLISHED;
+ sk_set_txhash(sk);