From: Greg Kroah-Hartman Date: Fri, 22 May 2020 12:08:39 +0000 (+0200) Subject: 4.14-stable patches X-Git-Tag: v4.4.225~58 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6907499b4eb2d3b8646cb0f1d453cba9d1313e2f;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-stable patches 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 --- diff --git a/queue-4.14/arm64-fix-the-flush_icache_range-arguments-in-machin.patch b/queue-4.14/arm64-fix-the-flush_icache_range-arguments-in-machin.patch index 27ba2acc095..37861fab3e7 100644 --- a/queue-4.14/arm64-fix-the-flush_icache_range-arguments-in-machin.patch +++ b/queue-4.14/arm64-fix-the-flush_icache_range-arguments-in-machin.patch @@ -15,14 +15,12 @@ Signed-off-by: Christoph Hellwig Signed-off-by: Catalin Marinas Signed-off-by: Sasha Levin --- - 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 index 00000000000..b82e6b965e8 --- /dev/null +++ b/queue-4.14/l2tp-don-t-register-sessions-in-l2tp_session_create.patch @@ -0,0 +1,202 @@ +From 3953ae7b218df4d1e544b98a393666f9ae58a78c Mon Sep 17 00:00:00 2001 +From: Guillaume Nault +Date: Fri, 27 Oct 2017 16:51:50 +0200 +Subject: l2tp: don't register sessions in l2tp_session_create() + +From: Guillaume Nault + +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 +Signed-off-by: David S. Miller +Signed-off-by: Giuliano Procida +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..514f5a14b39 --- /dev/null +++ b/queue-4.14/l2tp-initialise-l2tp_eth-sessions-before-registering-them.patch @@ -0,0 +1,229 @@ +From ee28de6bbd78c2e18111a0aef43ea746f28d2073 Mon Sep 17 00:00:00 2001 +From: Guillaume Nault +Date: Fri, 27 Oct 2017 16:51:51 +0200 +Subject: l2tp: initialise l2tp_eth sessions before registering them + +From: Guillaume Nault + +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 +Signed-off-by: David S. Miller +Signed-off-by: Giuliano Procida +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..c573e2d9cc8 --- /dev/null +++ b/queue-4.14/l2tp-initialise-ppp-sessions-before-registering-them.patch @@ -0,0 +1,190 @@ +From f98be6c6359e7e4a61aaefb9964c1db31cb9ec0c Mon Sep 17 00:00:00 2001 +From: Guillaume Nault +Date: Fri, 27 Oct 2017 16:51:52 +0200 +Subject: l2tp: initialise PPP sessions before registering them + +From: Guillaume Nault + +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 +Signed-off-by: David S. Miller +Signed-off-by: Giuliano Procida +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 00000000000..a3464972743 --- /dev/null +++ b/queue-4.14/l2tp-protect-sock-pointer-of-struct-pppol2tp_session-with-rcu.patch @@ -0,0 +1,387 @@ +From ee40fb2e1eb5bc0ddd3f2f83c6e39a454ef5a741 Mon Sep 17 00:00:00 2001 +From: Guillaume Nault +Date: Fri, 27 Oct 2017 16:51:52 +0200 +Subject: l2tp: protect sock pointer of struct pppol2tp_session with RCU + +From: Guillaume Nault + +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 +Signed-off-by: David S. Miller +Signed-off-by: Giuliano Procida +Signed-off-by: Greg Kroah-Hartman +--- + 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) diff --git a/queue-4.14/series b/queue-4.14/series index c28ade9f459..b61f2332408 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -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