]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 22 May 2020 12:08:39 +0000 (14:08 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 22 May 2020 12:08:39 +0000 (14:08 +0200)
added patches:
l2tp-don-t-register-sessions-in-l2tp_session_create.patch
l2tp-initialise-l2tp_eth-sessions-before-registering-them.patch
l2tp-initialise-ppp-sessions-before-registering-them.patch
l2tp-protect-sock-pointer-of-struct-pppol2tp_session-with-rcu.patch

queue-4.14/arm64-fix-the-flush_icache_range-arguments-in-machin.patch
queue-4.14/l2tp-don-t-register-sessions-in-l2tp_session_create.patch [new file with mode: 0644]
queue-4.14/l2tp-initialise-l2tp_eth-sessions-before-registering-them.patch [new file with mode: 0644]
queue-4.14/l2tp-initialise-ppp-sessions-before-registering-them.patch [new file with mode: 0644]
queue-4.14/l2tp-protect-sock-pointer-of-struct-pppol2tp_session-with-rcu.patch [new file with mode: 0644]
queue-4.14/series

index 27ba2acc095be6a3580a92cfcd10c8888e89c610..37861fab3e7cdc71b3b69c41fbe349bdedc23f7c 100644 (file)
@@ -15,14 +15,12 @@ Signed-off-by: Christoph Hellwig <hch@lst.de>
 Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
 Signed-off-by: Sasha Levin <sashal@kernel.org>
 ---
- arch/arm64/kernel/machine_kexec.c | 3 ++-
+ arch/arm64/kernel/machine_kexec.c |    3 ++-
  1 file changed, 2 insertions(+), 1 deletion(-)
 
-diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c
-index 11121f608eb5..f7e593965c1d 100644
 --- a/arch/arm64/kernel/machine_kexec.c
 +++ b/arch/arm64/kernel/machine_kexec.c
-@@ -184,7 +184,8 @@ void machine_kexec(struct kimage *kimage)
+@@ -184,7 +184,8 @@ void machine_kexec(struct kimage *kimage
        /* Flush the reboot_code_buffer in preparation for its execution. */
        __flush_dcache_area(reboot_code_buffer, arm64_relocate_new_kernel_size);
        flush_icache_range((uintptr_t)reboot_code_buffer,
@@ -32,6 +30,3 @@ index 11121f608eb5..f7e593965c1d 100644
  
        /* Flush the kimage list and its buffers. */
        kexec_list_flush(kimage);
--- 
-2.25.1
-
diff --git a/queue-4.14/l2tp-don-t-register-sessions-in-l2tp_session_create.patch b/queue-4.14/l2tp-don-t-register-sessions-in-l2tp_session_create.patch
new file mode 100644 (file)
index 0000000..b82e6b9
--- /dev/null
@@ -0,0 +1,202 @@
+From 3953ae7b218df4d1e544b98a393666f9ae58a78c Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 27 Oct 2017 16:51:50 +0200
+Subject: l2tp: don't register sessions in l2tp_session_create()
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 3953ae7b218df4d1e544b98a393666f9ae58a78c upstream.
+
+Sessions created by l2tp_session_create() aren't fully initialised:
+some pseudo-wire specific operations need to be done before making the
+session usable. Therefore the PPP and Ethernet pseudo-wires continue
+working on the returned l2tp session while it's already been exposed to
+the rest of the system.
+This can lead to various issues. In particular, the session may enter
+the deletion process before having been fully initialised, which will
+confuse the session removal code.
+
+This patch moves session registration out of l2tp_session_create(), so
+that callers can control when the session is exposed to the rest of the
+system. This is done by the new l2tp_session_register() function.
+
+Only pppol2tp_session_create() can be easily converted to avoid
+modifying its session after registration (the debug message is dropped
+in order to avoid the need for holding a reference on the session).
+
+For pppol2tp_connect() and l2tp_eth_create()), more work is needed.
+That'll be done in followup patches. For now, let's just register the
+session right after its creation, like it was done before. The only
+difference is that we can easily take a reference on the session before
+registering it, so, at least, we're sure it's not going to be freed
+while we're working on it.
+
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/l2tp/l2tp_core.c |   21 +++++++--------------
+ net/l2tp/l2tp_core.h |    3 +++
+ net/l2tp/l2tp_eth.c  |    9 +++++++++
+ net/l2tp/l2tp_ppp.c  |   27 +++++++++++++++++++--------
+ 4 files changed, 38 insertions(+), 22 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -328,8 +328,8 @@ struct l2tp_session *l2tp_session_get_by
+ }
+ EXPORT_SYMBOL_GPL(l2tp_session_get_by_ifname);
+-static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel,
+-                                    struct l2tp_session *session)
++int l2tp_session_register(struct l2tp_session *session,
++                        struct l2tp_tunnel *tunnel)
+ {
+       struct l2tp_session *session_walk;
+       struct hlist_head *g_head;
+@@ -382,6 +382,10 @@ static int l2tp_session_add_to_tunnel(st
+       hlist_add_head(&session->hlist, head);
+       write_unlock_bh(&tunnel->hlist_lock);
++      /* Ignore management session in session count value */
++      if (session->session_id != 0)
++              atomic_inc(&l2tp_session_count);
++
+       return 0;
+ err_tlock_pnlock:
+@@ -391,6 +395,7 @@ err_tlock:
+       return err;
+ }
++EXPORT_SYMBOL_GPL(l2tp_session_register);
+ /* Lookup a tunnel by id
+  */
+@@ -1791,7 +1796,6 @@ EXPORT_SYMBOL_GPL(l2tp_session_set_heade
+ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
+ {
+       struct l2tp_session *session;
+-      int err;
+       session = kzalloc(sizeof(struct l2tp_session) + priv_size, GFP_KERNEL);
+       if (session != NULL) {
+@@ -1848,17 +1852,6 @@ struct l2tp_session *l2tp_session_create
+               refcount_set(&session->ref_count, 1);
+-              err = l2tp_session_add_to_tunnel(tunnel, session);
+-              if (err) {
+-                      kfree(session);
+-
+-                      return ERR_PTR(err);
+-              }
+-
+-              /* Ignore management session in session count value */
+-              if (session->session_id != 0)
+-                      atomic_inc(&l2tp_session_count);
+-
+               return session;
+       }
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -257,6 +257,9 @@ struct l2tp_session *l2tp_session_create
+                                        struct l2tp_tunnel *tunnel,
+                                        u32 session_id, u32 peer_session_id,
+                                        struct l2tp_session_cfg *cfg);
++int l2tp_session_register(struct l2tp_session *session,
++                        struct l2tp_tunnel *tunnel);
++
+ void __l2tp_session_unhash(struct l2tp_session *session);
+ int l2tp_session_delete(struct l2tp_session *session);
+ void l2tp_session_free(struct l2tp_session *session);
+--- a/net/l2tp/l2tp_eth.c
++++ b/net/l2tp/l2tp_eth.c
+@@ -271,6 +271,13 @@ static int l2tp_eth_create(struct net *n
+               goto out;
+       }
++      l2tp_session_inc_refcount(session);
++      rc = l2tp_session_register(session, tunnel);
++      if (rc < 0) {
++              kfree(session);
++              goto out;
++      }
++
+       dev = alloc_netdev(sizeof(*priv), name, name_assign_type,
+                          l2tp_eth_dev_setup);
+       if (!dev) {
+@@ -304,6 +311,7 @@ static int l2tp_eth_create(struct net *n
+       __module_get(THIS_MODULE);
+       /* Must be done after register_netdev() */
+       strlcpy(session->ifname, dev->name, IFNAMSIZ);
++      l2tp_session_dec_refcount(session);
+       dev_hold(dev);
+@@ -314,6 +322,7 @@ out_del_dev:
+       spriv->dev = NULL;
+ out_del_session:
+       l2tp_session_delete(session);
++      l2tp_session_dec_refcount(session);
+ out:
+       return rc;
+ }
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -725,6 +725,14 @@ static int pppol2tp_connect(struct socke
+                       error = PTR_ERR(session);
+                       goto end;
+               }
++
++              l2tp_session_inc_refcount(session);
++              error = l2tp_session_register(session, tunnel);
++              if (error < 0) {
++                      kfree(session);
++                      goto end;
++              }
++              drop_refcnt = true;
+       }
+       /* Associate session with its PPPoL2TP socket */
+@@ -812,7 +820,7 @@ static int pppol2tp_session_create(struc
+       /* Error if tunnel socket is not prepped */
+       if (!tunnel->sock) {
+               error = -ENOENT;
+-              goto out;
++              goto err;
+       }
+       /* Default MTU values. */
+@@ -827,18 +835,21 @@ static int pppol2tp_session_create(struc
+                                     peer_session_id, cfg);
+       if (IS_ERR(session)) {
+               error = PTR_ERR(session);
+-              goto out;
++              goto err;
+       }
+       ps = l2tp_session_priv(session);
+       ps->tunnel_sock = tunnel->sock;
+-      l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n",
+-                session->name);
+-
+-      error = 0;
+-
+-out:
++      error = l2tp_session_register(session, tunnel);
++      if (error < 0)
++              goto err_sess;
++
++      return 0;
++
++err_sess:
++      kfree(session);
++err:
+       return error;
+ }
diff --git a/queue-4.14/l2tp-initialise-l2tp_eth-sessions-before-registering-them.patch b/queue-4.14/l2tp-initialise-l2tp_eth-sessions-before-registering-them.patch
new file mode 100644 (file)
index 0000000..514f5a1
--- /dev/null
@@ -0,0 +1,229 @@
+From ee28de6bbd78c2e18111a0aef43ea746f28d2073 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 27 Oct 2017 16:51:51 +0200
+Subject: l2tp: initialise l2tp_eth sessions before registering them
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit ee28de6bbd78c2e18111a0aef43ea746f28d2073 upstream.
+
+Sessions must be initialised before being made externally visible by
+l2tp_session_register(). Otherwise the session may be concurrently
+deleted before being initialised, which can confuse the deletion path
+and eventually lead to kernel oops.
+
+Therefore, we need to move l2tp_session_register() down in
+l2tp_eth_create(), but also handle the intermediate step where only the
+session or the netdevice has been registered.
+
+We can't just call l2tp_session_register() in ->ndo_init() because
+we'd have no way to properly undo this operation in ->ndo_uninit().
+Instead, let's register the session and the netdevice in two different
+steps and protect the session's device pointer with RCU.
+
+And now that we allow the session's .dev field to be NULL, we don't
+need to prevent the netdevice from being removed anymore. So we can
+drop the dev_hold() and dev_put() calls in l2tp_eth_create() and
+l2tp_eth_dev_uninit().
+
+Fixes: d9e31d17ceba ("l2tp: Add L2TP ethernet pseudowire support")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_eth.c |  106 ++++++++++++++++++++++++++++++++++++----------------
+ 1 file changed, 75 insertions(+), 31 deletions(-)
+
+--- a/net/l2tp/l2tp_eth.c
++++ b/net/l2tp/l2tp_eth.c
+@@ -54,7 +54,7 @@ struct l2tp_eth {
+ /* via l2tp_session_priv() */
+ struct l2tp_eth_sess {
+-      struct net_device       *dev;
++      struct net_device __rcu *dev;
+ };
+@@ -72,7 +72,14 @@ static int l2tp_eth_dev_init(struct net_
+ static void l2tp_eth_dev_uninit(struct net_device *dev)
+ {
+-      dev_put(dev);
++      struct l2tp_eth *priv = netdev_priv(dev);
++      struct l2tp_eth_sess *spriv;
++
++      spriv = l2tp_session_priv(priv->session);
++      RCU_INIT_POINTER(spriv->dev, NULL);
++      /* No need for synchronize_net() here. We're called by
++       * unregister_netdev*(), which does the synchronisation for us.
++       */
+ }
+ static int l2tp_eth_dev_xmit(struct sk_buff *skb, struct net_device *dev)
+@@ -130,8 +137,8 @@ static void l2tp_eth_dev_setup(struct ne
+ static void l2tp_eth_dev_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len)
+ {
+       struct l2tp_eth_sess *spriv = l2tp_session_priv(session);
+-      struct net_device *dev = spriv->dev;
+-      struct l2tp_eth *priv = netdev_priv(dev);
++      struct net_device *dev;
++      struct l2tp_eth *priv;
+       if (session->debug & L2TP_MSG_DATA) {
+               unsigned int length;
+@@ -155,16 +162,25 @@ static void l2tp_eth_dev_recv(struct l2t
+       skb_dst_drop(skb);
+       nf_reset(skb);
++      rcu_read_lock();
++      dev = rcu_dereference(spriv->dev);
++      if (!dev)
++              goto error_rcu;
++
++      priv = netdev_priv(dev);
+       if (dev_forward_skb(dev, skb) == NET_RX_SUCCESS) {
+               atomic_long_inc(&priv->rx_packets);
+               atomic_long_add(data_len, &priv->rx_bytes);
+       } else {
+               atomic_long_inc(&priv->rx_errors);
+       }
++      rcu_read_unlock();
++
+       return;
++error_rcu:
++      rcu_read_unlock();
+ error:
+-      atomic_long_inc(&priv->rx_errors);
+       kfree_skb(skb);
+ }
+@@ -175,11 +191,15 @@ static void l2tp_eth_delete(struct l2tp_
+       if (session) {
+               spriv = l2tp_session_priv(session);
+-              dev = spriv->dev;
++
++              rtnl_lock();
++              dev = rtnl_dereference(spriv->dev);
+               if (dev) {
+-                      unregister_netdev(dev);
+-                      spriv->dev = NULL;
++                      unregister_netdevice(dev);
++                      rtnl_unlock();
+                       module_put(THIS_MODULE);
++              } else {
++                      rtnl_unlock();
+               }
+       }
+ }
+@@ -189,9 +209,20 @@ static void l2tp_eth_show(struct seq_fil
+ {
+       struct l2tp_session *session = arg;
+       struct l2tp_eth_sess *spriv = l2tp_session_priv(session);
+-      struct net_device *dev = spriv->dev;
++      struct net_device *dev;
++
++      rcu_read_lock();
++      dev = rcu_dereference(spriv->dev);
++      if (!dev) {
++              rcu_read_unlock();
++              return;
++      }
++      dev_hold(dev);
++      rcu_read_unlock();
+       seq_printf(m, "   interface %s\n", dev->name);
++
++      dev_put(dev);
+ }
+ #endif
+@@ -268,21 +299,14 @@ static int l2tp_eth_create(struct net *n
+                                     peer_session_id, cfg);
+       if (IS_ERR(session)) {
+               rc = PTR_ERR(session);
+-              goto out;
+-      }
+-
+-      l2tp_session_inc_refcount(session);
+-      rc = l2tp_session_register(session, tunnel);
+-      if (rc < 0) {
+-              kfree(session);
+-              goto out;
++              goto err;
+       }
+       dev = alloc_netdev(sizeof(*priv), name, name_assign_type,
+                          l2tp_eth_dev_setup);
+       if (!dev) {
+               rc = -ENOMEM;
+-              goto out_del_session;
++              goto err_sess;
+       }
+       dev_net_set(dev, net);
+@@ -302,28 +326,48 @@ static int l2tp_eth_create(struct net *n
+ #endif
+       spriv = l2tp_session_priv(session);
+-      spriv->dev = dev;
+-      rc = register_netdev(dev);
+-      if (rc < 0)
+-              goto out_del_dev;
++      l2tp_session_inc_refcount(session);
++
++      rtnl_lock();
++
++      /* Register both device and session while holding the rtnl lock. This
++       * ensures that l2tp_eth_delete() will see that there's a device to
++       * unregister, even if it happened to run before we assign spriv->dev.
++       */
++      rc = l2tp_session_register(session, tunnel);
++      if (rc < 0) {
++              rtnl_unlock();
++              goto err_sess_dev;
++      }
++
++      rc = register_netdevice(dev);
++      if (rc < 0) {
++              rtnl_unlock();
++              l2tp_session_delete(session);
++              l2tp_session_dec_refcount(session);
++              free_netdev(dev);
++
++              return rc;
++      }
+-      __module_get(THIS_MODULE);
+-      /* Must be done after register_netdev() */
+       strlcpy(session->ifname, dev->name, IFNAMSIZ);
++      rcu_assign_pointer(spriv->dev, dev);
++
++      rtnl_unlock();
++
+       l2tp_session_dec_refcount(session);
+-      dev_hold(dev);
++      __module_get(THIS_MODULE);
+       return 0;
+-out_del_dev:
+-      free_netdev(dev);
+-      spriv->dev = NULL;
+-out_del_session:
+-      l2tp_session_delete(session);
++err_sess_dev:
+       l2tp_session_dec_refcount(session);
+-out:
++      free_netdev(dev);
++err_sess:
++      kfree(session);
++err:
+       return rc;
+ }
diff --git a/queue-4.14/l2tp-initialise-ppp-sessions-before-registering-them.patch b/queue-4.14/l2tp-initialise-ppp-sessions-before-registering-them.patch
new file mode 100644 (file)
index 0000000..c573e2d
--- /dev/null
@@ -0,0 +1,190 @@
+From f98be6c6359e7e4a61aaefb9964c1db31cb9ec0c Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 27 Oct 2017 16:51:52 +0200
+Subject: l2tp: initialise PPP sessions before registering them
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit f98be6c6359e7e4a61aaefb9964c1db31cb9ec0c upstream.
+
+pppol2tp_connect() initialises L2TP sessions after they've been exposed
+to the rest of the system by l2tp_session_register(). This puts
+sessions into transient states that are the source of several races, in
+particular with session's deletion path.
+
+This patch centralises the initialisation code into
+pppol2tp_session_init(), which is called before the registration phase.
+The only field that can't be set before session registration is the
+pppol2tp socket pointer, which has already been converted to RCU. So
+pppol2tp_connect() should now be race-free.
+
+The session's .session_close() callback is now set before registration.
+Therefore, it's always called when l2tp_core deletes the session, even
+if it was created by pppol2tp_session_create() and hasn't been plugged
+to a pppol2tp socket yet. That'd prevent session free because the extra
+reference taken by pppol2tp_session_close() wouldn't be dropped by the
+socket's ->sk_destruct() callback (pppol2tp_session_destruct()).
+We could set .session_close() only while connecting a session to its
+pppol2tp socket, or teach pppol2tp_session_close() to avoid grabbing a
+reference when the session isn't connected, but that'd require adding
+some form of synchronisation to be race free.
+
+Instead of that, we can just let the pppol2tp socket hold a reference
+on the session as soon as it starts depending on it (that is, in
+pppol2tp_connect()). Then we don't need to utilise
+pppol2tp_session_close() to hold a reference at the last moment to
+prevent l2tp_core from dropping it.
+
+When releasing the socket, pppol2tp_release() now deletes the session
+using the standard l2tp_session_delete() function, instead of merely
+removing it from hash tables. l2tp_session_delete() drops the reference
+the sessions holds on itself, but also makes sure it doesn't remove a
+session twice. So it can safely be called, even if l2tp_core already
+tried, or is concurrently trying, to remove the session.
+Finally, pppol2tp_session_destruct() drops the reference held by the
+socket.
+
+Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_ppp.c |   69 ++++++++++++++++++++++++++++------------------------
+ 1 file changed, 38 insertions(+), 31 deletions(-)
+
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -449,9 +449,6 @@ static void pppol2tp_session_close(struc
+                       inet_shutdown(sk->sk_socket, SEND_SHUTDOWN);
+               sock_put(sk);
+       }
+-
+-      /* Don't let the session go away before our socket does */
+-      l2tp_session_inc_refcount(session);
+ }
+ /* Really kill the session socket. (Called from sock_put() if
+@@ -507,8 +504,7 @@ static int pppol2tp_release(struct socke
+       if (session != NULL) {
+               struct pppol2tp_session *ps;
+-              __l2tp_session_unhash(session);
+-              l2tp_session_queue_purge(session);
++              l2tp_session_delete(session);
+               ps = l2tp_session_priv(session);
+               mutex_lock(&ps->sk_lock);
+@@ -600,6 +596,35 @@ static void pppol2tp_show(struct seq_fil
+ }
+ #endif
++static void pppol2tp_session_init(struct l2tp_session *session)
++{
++      struct pppol2tp_session *ps;
++      struct dst_entry *dst;
++
++      session->recv_skb = pppol2tp_recv;
++      session->session_close = pppol2tp_session_close;
++#if IS_ENABLED(CONFIG_L2TP_DEBUGFS)
++      session->show = pppol2tp_show;
++#endif
++
++      ps = l2tp_session_priv(session);
++      mutex_init(&ps->sk_lock);
++      ps->tunnel_sock = session->tunnel->sock;
++      ps->owner = current->pid;
++
++      /* If PMTU discovery was enabled, use the MTU that was discovered */
++      dst = sk_dst_get(session->tunnel->sock);
++      if (dst) {
++              u32 pmtu = dst_mtu(dst);
++
++              if (pmtu) {
++                      session->mtu = pmtu - PPPOL2TP_HEADER_OVERHEAD;
++                      session->mru = pmtu - PPPOL2TP_HEADER_OVERHEAD;
++              }
++              dst_release(dst);
++      }
++}
++
+ /* connect() handler. Attach a PPPoX socket to a tunnel UDP socket
+  */
+ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
+@@ -611,7 +636,6 @@ static int pppol2tp_connect(struct socke
+       struct l2tp_session *session = NULL;
+       struct l2tp_tunnel *tunnel;
+       struct pppol2tp_session *ps;
+-      struct dst_entry *dst;
+       struct l2tp_session_cfg cfg = { 0, };
+       int error = 0;
+       u32 tunnel_id, peer_tunnel_id;
+@@ -763,8 +787,8 @@ static int pppol2tp_connect(struct socke
+                       goto end;
+               }
++              pppol2tp_session_init(session);
+               ps = l2tp_session_priv(session);
+-              mutex_init(&ps->sk_lock);
+               l2tp_session_inc_refcount(session);
+               mutex_lock(&ps->sk_lock);
+@@ -777,26 +801,6 @@ static int pppol2tp_connect(struct socke
+               drop_refcnt = true;
+       }
+-      ps->owner            = current->pid;
+-      ps->tunnel_sock = tunnel->sock;
+-
+-      session->recv_skb       = pppol2tp_recv;
+-      session->session_close  = pppol2tp_session_close;
+-#if IS_ENABLED(CONFIG_L2TP_DEBUGFS)
+-      session->show           = pppol2tp_show;
+-#endif
+-
+-      /* If PMTU discovery was enabled, use the MTU that was discovered */
+-      dst = sk_dst_get(tunnel->sock);
+-      if (dst != NULL) {
+-              u32 pmtu = dst_mtu(dst);
+-
+-              if (pmtu != 0)
+-                      session->mtu = session->mru = pmtu -
+-                              PPPOL2TP_HEADER_OVERHEAD;
+-              dst_release(dst);
+-      }
+-
+       /* Special case: if source & dest session_id == 0x0000, this
+        * socket is being created to manage the tunnel. Just set up
+        * the internal context for use by ioctl() and sockopt()
+@@ -830,6 +834,12 @@ out_no_ppp:
+       rcu_assign_pointer(ps->sk, sk);
+       mutex_unlock(&ps->sk_lock);
++      /* Keep the reference we've grabbed on the session: sk doesn't expect
++       * the session to disappear. pppol2tp_session_destruct() is responsible
++       * for dropping it.
++       */
++      drop_refcnt = false;
++
+       sk->sk_state = PPPOX_CONNECTED;
+       l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n",
+                 session->name);
+@@ -853,7 +863,6 @@ static int pppol2tp_session_create(struc
+ {
+       int error;
+       struct l2tp_session *session;
+-      struct pppol2tp_session *ps;
+       /* Error if tunnel socket is not prepped */
+       if (!tunnel->sock) {
+@@ -876,9 +885,7 @@ static int pppol2tp_session_create(struc
+               goto err;
+       }
+-      ps = l2tp_session_priv(session);
+-      mutex_init(&ps->sk_lock);
+-      ps->tunnel_sock = tunnel->sock;
++      pppol2tp_session_init(session);
+       error = l2tp_session_register(session, tunnel);
+       if (error < 0)
diff --git a/queue-4.14/l2tp-protect-sock-pointer-of-struct-pppol2tp_session-with-rcu.patch b/queue-4.14/l2tp-protect-sock-pointer-of-struct-pppol2tp_session-with-rcu.patch
new file mode 100644 (file)
index 0000000..a346497
--- /dev/null
@@ -0,0 +1,387 @@
+From ee40fb2e1eb5bc0ddd3f2f83c6e39a454ef5a741 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 27 Oct 2017 16:51:52 +0200
+Subject: l2tp: protect sock pointer of struct pppol2tp_session with RCU
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit ee40fb2e1eb5bc0ddd3f2f83c6e39a454ef5a741 upstream.
+
+pppol2tp_session_create() registers sessions that can't have their
+corresponding socket initialised. This socket has to be created by
+userspace, then connected to the session by pppol2tp_connect().
+Therefore, we need to protect the pppol2tp socket pointer of L2TP
+sessions, so that it can safely be updated when userspace is connecting
+or closing the socket. This will eventually allow pppol2tp_connect()
+to avoid generating transient states while initialising its parts of the
+session.
+
+To this end, this patch protects the pppol2tp socket pointer using RCU.
+
+The pppol2tp socket pointer is still set in pppol2tp_connect(), but
+only once we know the function isn't going to fail. It's eventually
+reset by pppol2tp_release(), which now has to wait for a grace period
+to elapse before it can drop the last reference on the socket. This
+ensures that pppol2tp_session_get_sock() can safely grab a reference
+on the socket, even after ps->sk is reset to NULL but before this
+operation actually gets visible from pppol2tp_session_get_sock().
+
+The rest is standard RCU conversion: pppol2tp_recv(), which already
+runs in atomic context, is simply enclosed by rcu_read_lock() and
+rcu_read_unlock(), while other functions are converted to use
+pppol2tp_session_get_sock() followed by sock_put().
+pppol2tp_session_setsockopt() is a special case. It used to retrieve
+the pppol2tp socket from the L2TP session, which itself was retrieved
+from the pppol2tp socket. Therefore we can just avoid dereferencing
+ps->sk and directly use the original socket pointer instead.
+
+With all users of ps->sk now handling NULL and concurrent updates, the
+L2TP ->ref() and ->deref() callbacks aren't needed anymore. Therefore,
+rather than converting pppol2tp_session_sock_hold() and
+pppol2tp_session_sock_put(), we can just drop them.
+
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Giuliano Procida <gprocida@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_ppp.c |  154 ++++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 101 insertions(+), 53 deletions(-)
+
+--- a/net/l2tp/l2tp_ppp.c
++++ b/net/l2tp/l2tp_ppp.c
+@@ -122,8 +122,11 @@
+ struct pppol2tp_session {
+       int                     owner;          /* pid that opened the socket */
+-      struct sock             *sock;          /* Pointer to the session
++      struct mutex            sk_lock;        /* Protects .sk */
++      struct sock __rcu       *sk;            /* Pointer to the session
+                                                * PPPoX socket */
++      struct sock             *__sk;          /* Copy of .sk, for cleanup */
++      struct rcu_head         rcu;            /* For asynchronous release */
+       struct sock             *tunnel_sock;   /* Pointer to the tunnel UDP
+                                                * socket */
+       int                     flags;          /* accessed by PPPIOCGFLAGS.
+@@ -138,6 +141,24 @@ static const struct ppp_channel_ops pppo
+ static const struct proto_ops pppol2tp_ops;
++/* Retrieves the pppol2tp socket associated to a session.
++ * A reference is held on the returned socket, so this function must be paired
++ * with sock_put().
++ */
++static struct sock *pppol2tp_session_get_sock(struct l2tp_session *session)
++{
++      struct pppol2tp_session *ps = l2tp_session_priv(session);
++      struct sock *sk;
++
++      rcu_read_lock();
++      sk = rcu_dereference(ps->sk);
++      if (sk)
++              sock_hold(sk);
++      rcu_read_unlock();
++
++      return sk;
++}
++
+ /* Helpers to obtain tunnel/session contexts from sockets.
+  */
+ static inline struct l2tp_session *pppol2tp_sock_to_session(struct sock *sk)
+@@ -224,7 +245,8 @@ static void pppol2tp_recv(struct l2tp_se
+       /* If the socket is bound, send it in to PPP's input queue. Otherwise
+        * queue it on the session socket.
+        */
+-      sk = ps->sock;
++      rcu_read_lock();
++      sk = rcu_dereference(ps->sk);
+       if (sk == NULL)
+               goto no_sock;
+@@ -247,30 +269,16 @@ static void pppol2tp_recv(struct l2tp_se
+                       kfree_skb(skb);
+               }
+       }
++      rcu_read_unlock();
+       return;
+ no_sock:
++      rcu_read_unlock();
+       l2tp_info(session, L2TP_MSG_DATA, "%s: no socket\n", session->name);
+       kfree_skb(skb);
+ }
+-static void pppol2tp_session_sock_hold(struct l2tp_session *session)
+-{
+-      struct pppol2tp_session *ps = l2tp_session_priv(session);
+-
+-      if (ps->sock)
+-              sock_hold(ps->sock);
+-}
+-
+-static void pppol2tp_session_sock_put(struct l2tp_session *session)
+-{
+-      struct pppol2tp_session *ps = l2tp_session_priv(session);
+-
+-      if (ps->sock)
+-              sock_put(ps->sock);
+-}
+-
+ /************************************************************************
+  * Transmit handling
+  ***********************************************************************/
+@@ -431,14 +439,16 @@ abort:
+  */
+ static void pppol2tp_session_close(struct l2tp_session *session)
+ {
+-      struct pppol2tp_session *ps = l2tp_session_priv(session);
+-      struct sock *sk = ps->sock;
+-      struct socket *sock = sk->sk_socket;
++      struct sock *sk;
+       BUG_ON(session->magic != L2TP_SESSION_MAGIC);
+-      if (sock)
+-              inet_shutdown(sock, SEND_SHUTDOWN);
++      sk = pppol2tp_session_get_sock(session);
++      if (sk) {
++              if (sk->sk_socket)
++                      inet_shutdown(sk->sk_socket, SEND_SHUTDOWN);
++              sock_put(sk);
++      }
+       /* Don't let the session go away before our socket does */
+       l2tp_session_inc_refcount(session);
+@@ -461,6 +471,14 @@ static void pppol2tp_session_destruct(st
+       }
+ }
++static void pppol2tp_put_sk(struct rcu_head *head)
++{
++      struct pppol2tp_session *ps;
++
++      ps = container_of(head, typeof(*ps), rcu);
++      sock_put(ps->__sk);
++}
++
+ /* Called when the PPPoX socket (session) is closed.
+  */
+ static int pppol2tp_release(struct socket *sock)
+@@ -486,11 +504,24 @@ static int pppol2tp_release(struct socke
+       session = pppol2tp_sock_to_session(sk);
+-      /* Purge any queued data */
+       if (session != NULL) {
++              struct pppol2tp_session *ps;
++
+               __l2tp_session_unhash(session);
+               l2tp_session_queue_purge(session);
+-              sock_put(sk);
++
++              ps = l2tp_session_priv(session);
++              mutex_lock(&ps->sk_lock);
++              ps->__sk = rcu_dereference_protected(ps->sk,
++                                                   lockdep_is_held(&ps->sk_lock));
++              RCU_INIT_POINTER(ps->sk, NULL);
++              mutex_unlock(&ps->sk_lock);
++              call_rcu(&ps->rcu, pppol2tp_put_sk);
++
++              /* Rely on the sock_put() call at the end of the function for
++               * dropping the reference held by pppol2tp_sock_to_session().
++               * The last reference will be dropped by pppol2tp_put_sk().
++               */
+       }
+       release_sock(sk);
+@@ -557,12 +588,14 @@ out:
+ static void pppol2tp_show(struct seq_file *m, void *arg)
+ {
+       struct l2tp_session *session = arg;
+-      struct pppol2tp_session *ps = l2tp_session_priv(session);
++      struct sock *sk;
++
++      sk = pppol2tp_session_get_sock(session);
++      if (sk) {
++              struct pppox_sock *po = pppox_sk(sk);
+-      if (ps) {
+-              struct pppox_sock *po = pppox_sk(ps->sock);
+-              if (po)
+-                      seq_printf(m, "   interface %s\n", ppp_dev_name(&po->chan));
++              seq_printf(m, "   interface %s\n", ppp_dev_name(&po->chan));
++              sock_put(sk);
+       }
+ }
+ #endif
+@@ -703,13 +736,17 @@ static int pppol2tp_connect(struct socke
+               /* Using a pre-existing session is fine as long as it hasn't
+                * been connected yet.
+                */
+-              if (ps->sock) {
++              mutex_lock(&ps->sk_lock);
++              if (rcu_dereference_protected(ps->sk,
++                                            lockdep_is_held(&ps->sk_lock))) {
++                      mutex_unlock(&ps->sk_lock);
+                       error = -EEXIST;
+                       goto end;
+               }
+               /* consistency checks */
+               if (ps->tunnel_sock != tunnel->sock) {
++                      mutex_unlock(&ps->sk_lock);
+                       error = -EEXIST;
+                       goto end;
+               }
+@@ -726,19 +763,21 @@ static int pppol2tp_connect(struct socke
+                       goto end;
+               }
++              ps = l2tp_session_priv(session);
++              mutex_init(&ps->sk_lock);
+               l2tp_session_inc_refcount(session);
++
++              mutex_lock(&ps->sk_lock);
+               error = l2tp_session_register(session, tunnel);
+               if (error < 0) {
++                      mutex_unlock(&ps->sk_lock);
+                       kfree(session);
+                       goto end;
+               }
+               drop_refcnt = true;
+       }
+-      /* Associate session with its PPPoL2TP socket */
+-      ps = l2tp_session_priv(session);
+       ps->owner            = current->pid;
+-      ps->sock             = sk;
+       ps->tunnel_sock = tunnel->sock;
+       session->recv_skb       = pppol2tp_recv;
+@@ -747,12 +786,6 @@ static int pppol2tp_connect(struct socke
+       session->show           = pppol2tp_show;
+ #endif
+-      /* We need to know each time a skb is dropped from the reorder
+-       * queue.
+-       */
+-      session->ref = pppol2tp_session_sock_hold;
+-      session->deref = pppol2tp_session_sock_put;
+-
+       /* If PMTU discovery was enabled, use the MTU that was discovered */
+       dst = sk_dst_get(tunnel->sock);
+       if (dst != NULL) {
+@@ -786,12 +819,17 @@ static int pppol2tp_connect(struct socke
+       po->chan.mtu     = session->mtu;
+       error = ppp_register_net_channel(sock_net(sk), &po->chan);
+-      if (error)
++      if (error) {
++              mutex_unlock(&ps->sk_lock);
+               goto end;
++      }
+ out_no_ppp:
+       /* This is how we get the session context from the socket. */
+       sk->sk_user_data = session;
++      rcu_assign_pointer(ps->sk, sk);
++      mutex_unlock(&ps->sk_lock);
++
+       sk->sk_state = PPPOX_CONNECTED;
+       l2tp_info(session, L2TP_MSG_CONTROL, "%s: created\n",
+                 session->name);
+@@ -839,6 +877,7 @@ static int pppol2tp_session_create(struc
+       }
+       ps = l2tp_session_priv(session);
++      mutex_init(&ps->sk_lock);
+       ps->tunnel_sock = tunnel->sock;
+       error = l2tp_session_register(session, tunnel);
+@@ -1010,12 +1049,10 @@ static int pppol2tp_session_ioctl(struct
+                "%s: pppol2tp_session_ioctl(cmd=%#x, arg=%#lx)\n",
+                session->name, cmd, arg);
+-      sk = ps->sock;
++      sk = pppol2tp_session_get_sock(session);
+       if (!sk)
+               return -EBADR;
+-      sock_hold(sk);
+-
+       switch (cmd) {
+       case SIOCGIFMTU:
+               err = -ENXIO;
+@@ -1291,7 +1328,6 @@ static int pppol2tp_session_setsockopt(s
+                                      int optname, int val)
+ {
+       int err = 0;
+-      struct pppol2tp_session *ps = l2tp_session_priv(session);
+       switch (optname) {
+       case PPPOL2TP_SO_RECVSEQ:
+@@ -1312,8 +1348,8 @@ static int pppol2tp_session_setsockopt(s
+               }
+               session->send_seq = !!val;
+               {
+-                      struct sock *ssk      = ps->sock;
+-                      struct pppox_sock *po = pppox_sk(ssk);
++                      struct pppox_sock *po = pppox_sk(sk);
++
+                       po->chan.hdrlen = val ? PPPOL2TP_L2TP_HDR_SIZE_SEQ :
+                               PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
+               }
+@@ -1652,8 +1688,9 @@ static void pppol2tp_seq_session_show(st
+ {
+       struct l2tp_session *session = v;
+       struct l2tp_tunnel *tunnel = session->tunnel;
+-      struct pppol2tp_session *ps = l2tp_session_priv(session);
+-      struct pppox_sock *po = pppox_sk(ps->sock);
++      unsigned char state;
++      char user_data_ok;
++      struct sock *sk;
+       u32 ip = 0;
+       u16 port = 0;
+@@ -1663,6 +1700,15 @@ static void pppol2tp_seq_session_show(st
+               port = ntohs(inet->inet_sport);
+       }
++      sk = pppol2tp_session_get_sock(session);
++      if (sk) {
++              state = sk->sk_state;
++              user_data_ok = (session == sk->sk_user_data) ? 'Y' : 'N';
++      } else {
++              state = 0;
++              user_data_ok = 'N';
++      }
++
+       seq_printf(m, "  SESSION '%s' %08X/%d %04X/%04X -> "
+                  "%04X/%04X %d %c\n",
+                  session->name, ip, port,
+@@ -1670,9 +1716,7 @@ static void pppol2tp_seq_session_show(st
+                  session->session_id,
+                  tunnel->peer_tunnel_id,
+                  session->peer_session_id,
+-                 ps->sock->sk_state,
+-                 (session == ps->sock->sk_user_data) ?
+-                 'Y' : 'N');
++                 state, user_data_ok);
+       seq_printf(m, "   %d/%d/%c/%c/%s %08x %u\n",
+                  session->mtu, session->mru,
+                  session->recv_seq ? 'R' : '-',
+@@ -1689,8 +1733,12 @@ static void pppol2tp_seq_session_show(st
+                  atomic_long_read(&session->stats.rx_bytes),
+                  atomic_long_read(&session->stats.rx_errors));
+-      if (po)
++      if (sk) {
++              struct pppox_sock *po = pppox_sk(sk);
++
+               seq_printf(m, "   interface %s\n", ppp_dev_name(&po->chan));
++              sock_put(sk);
++      }
+ }
+ static int pppol2tp_seq_show(struct seq_file *m, void *v)
index c28ade9f459405ace5cf0428489dc8739c29af93..b61f2332408c8bdaa692ab5b1d8abef58cff7920 100644 (file)
@@ -28,3 +28,7 @@ padata-replace-delayed-timer-with-immediate-workqueu.patch
 padata-initialize-pd-cpu-with-effective-cpumask.patch
 padata-purge-get_cpu-and-reorder_via_wq-from-padata_.patch
 arm64-fix-the-flush_icache_range-arguments-in-machin.patch
+l2tp-don-t-register-sessions-in-l2tp_session_create.patch
+l2tp-initialise-l2tp_eth-sessions-before-registering-them.patch
+l2tp-protect-sock-pointer-of-struct-pppol2tp_session-with-rcu.patch
+l2tp-initialise-ppp-sessions-before-registering-them.patch