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,
/* Flush the kimage list and its buffers. */
kexec_list_flush(kimage);
---
-2.25.1
-
--- /dev/null
+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;
+ }
+
--- /dev/null
+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;
+ }
+
--- /dev/null
+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)
--- /dev/null
+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)
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