From: Greg Kroah-Hartman Date: Fri, 3 Apr 2020 12:36:42 +0000 (+0200) Subject: 4.4-stable patches X-Git-Tag: v5.4.31~39 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=efa8c60d769190061e671f35f5d153f58c3b160f;p=thirdparty%2Fkernel%2Fstable-queue.git 4.4-stable patches added patches: l2tp-correctly-return-ebadf-from-pppol2tp_getname.patch l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch l2tp-ensure-sessions-are-freed-after-their-pppol2tp-socket.patch l2tp-fix-duplicate-session-creation.patch l2tp-fix-race-between-l2tp_session_delete-and-l2tp_tunnel_closeall.patch l2tp-fix-race-in-l2tp_recv_common.patch l2tp-refactor-the-codes-with-existing-macros-instead-of-literal-number.patch net-l2tp-make-l2tp_ip6-namespace-aware.patch --- diff --git a/queue-4.4/l2tp-correctly-return-ebadf-from-pppol2tp_getname.patch b/queue-4.4/l2tp-correctly-return-ebadf-from-pppol2tp_getname.patch new file mode 100644 index 00000000000..046377f04ca --- /dev/null +++ b/queue-4.4/l2tp-correctly-return-ebadf-from-pppol2tp_getname.patch @@ -0,0 +1,51 @@ +From 4ac36a4adaf80013a60013d6f829f5863d5d0e05 Mon Sep 17 00:00:00 2001 +From: "phil.turnbull@oracle.com" +Date: Tue, 26 Jul 2016 15:14:35 -0400 +Subject: l2tp: Correctly return -EBADF from pppol2tp_getname. + +From: phil.turnbull@oracle.com + +commit 4ac36a4adaf80013a60013d6f829f5863d5d0e05 upstream. + +If 'tunnel' is NULL we should return -EBADF but the 'end_put_sess' path +unconditionally sets 'error' back to zero. Rework the error path so it +more closely matches pppol2tp_sendmsg. + +Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts") +Signed-off-by: Phil Turnbull +Signed-off-by: David S. Miller +Signed-off-by: Will Deacon +Signed-off-by: Greg Kroah-Hartman + +--- + net/l2tp/l2tp_ppp.c | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +--- a/net/l2tp/l2tp_ppp.c ++++ b/net/l2tp/l2tp_ppp.c +@@ -889,10 +889,8 @@ static int pppol2tp_getname(struct socke + + pls = l2tp_session_priv(session); + tunnel = l2tp_sock_to_tunnel(pls->tunnel_sock); +- if (tunnel == NULL) { +- error = -EBADF; ++ if (tunnel == NULL) + goto end_put_sess; +- } + + inet = inet_sk(tunnel->sock); + if ((tunnel->version == 2) && (tunnel->sock->sk_family == AF_INET)) { +@@ -970,12 +968,11 @@ static int pppol2tp_getname(struct socke + } + + *usockaddr_len = len; ++ error = 0; + + sock_put(pls->tunnel_sock); + end_put_sess: + sock_put(sk); +- error = 0; +- + end: + return error; + } diff --git a/queue-4.4/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch b/queue-4.4/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch new file mode 100644 index 00000000000..11adf9dbf34 --- /dev/null +++ b/queue-4.4/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch @@ -0,0 +1,51 @@ +From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST +From: Will Deacon +Date: Thu, 2 Apr 2020 18:32:46 +0100 +Subject: l2tp: ensure session can't get removed during pppol2tp_session_ioctl() +To: gregkh@linuxfoundation.org +Cc: stable@vger.kernel.org, linux-kernel@vger.kernel.org, kernel-team@android.com, g.nault@alphalink.fr, "David S . Miller" , Will Deacon +Message-ID: <20200402173250.7858-5-will@kernel.org> + +From: Guillaume Nault + +commit 57377d63547861919ee634b845c7caa38de4a452 upstream. + +Holding a reference on session is required before calling +pppol2tp_session_ioctl(). The session could get freed while processing the +ioctl otherwise. Since pppol2tp_session_ioctl() uses the session's socket, +we also need to take a reference on it in l2tp_session_get(). + +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: Will Deacon +Signed-off-by: Greg Kroah-Hartman +--- + net/l2tp/l2tp_ppp.c | 15 +++++++++++---- + 1 file changed, 11 insertions(+), 4 deletions(-) + +--- a/net/l2tp/l2tp_ppp.c ++++ b/net/l2tp/l2tp_ppp.c +@@ -1168,11 +1168,18 @@ static int pppol2tp_tunnel_ioctl(struct + if (stats.session_id != 0) { + /* resend to session ioctl handler */ + struct l2tp_session *session = +- l2tp_session_find(sock_net(sk), tunnel, stats.session_id); +- if (session != NULL) +- err = pppol2tp_session_ioctl(session, cmd, arg); +- else ++ l2tp_session_get(sock_net(sk), tunnel, ++ stats.session_id, true); ++ ++ if (session) { ++ err = pppol2tp_session_ioctl(session, cmd, ++ arg); ++ if (session->deref) ++ session->deref(session); ++ l2tp_session_dec_refcount(session); ++ } else { + err = -EBADR; ++ } + break; + } + #ifdef CONFIG_XFRM diff --git a/queue-4.4/l2tp-ensure-sessions-are-freed-after-their-pppol2tp-socket.patch b/queue-4.4/l2tp-ensure-sessions-are-freed-after-their-pppol2tp-socket.patch new file mode 100644 index 00000000000..755cb82d3a5 --- /dev/null +++ b/queue-4.4/l2tp-ensure-sessions-are-freed-after-their-pppol2tp-socket.patch @@ -0,0 +1,53 @@ +From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST +From: Will Deacon +Date: Thu, 2 Apr 2020 18:32:49 +0100 +Subject: l2tp: ensure sessions are freed after their PPPOL2TP socket +To: gregkh@linuxfoundation.org +Cc: stable@vger.kernel.org, linux-kernel@vger.kernel.org, kernel-team@android.com, g.nault@alphalink.fr, "David S . Miller" , Will Deacon +Message-ID: <20200402173250.7858-8-will@kernel.org> + +From: Guillaume Nault + +commit cdd10c9627496ad25c87ce6394e29752253c69d3 upstream. + +If l2tp_tunnel_delete() or l2tp_tunnel_closeall() deletes a session +right after pppol2tp_release() orphaned its socket, then the 'sock' +variable of the pppol2tp_session_close() callback is NULL. Yet the +session is still used by pppol2tp_release(). + +Therefore we need to take an extra reference in any case, to prevent +l2tp_tunnel_delete() or l2tp_tunnel_closeall() from freeing the session. + +Since the pppol2tp_session_close() callback is only set if the session +is associated to a PPPOL2TP socket and that both l2tp_tunnel_delete() +and l2tp_tunnel_closeall() hold the PPPOL2TP socket before calling +pppol2tp_session_close(), we're sure that pppol2tp_session_close() and +pppol2tp_session_destruct() are paired and called in the right order. +So the reference taken by the former will be released by the later. + +Signed-off-by: Guillaume Nault +Signed-off-by: David S. Miller +Signed-off-by: Will Deacon +Signed-off-by: Greg Kroah-Hartman +--- + net/l2tp/l2tp_ppp.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/net/l2tp/l2tp_ppp.c ++++ b/net/l2tp/l2tp_ppp.c +@@ -452,11 +452,11 @@ static void pppol2tp_session_close(struc + + BUG_ON(session->magic != L2TP_SESSION_MAGIC); + +- if (sock) { ++ if (sock) + inet_shutdown(sock, SEND_SHUTDOWN); +- /* Don't let the session go away before our socket does */ +- l2tp_session_inc_refcount(session); +- } ++ ++ /* 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 diff --git a/queue-4.4/l2tp-fix-duplicate-session-creation.patch b/queue-4.4/l2tp-fix-duplicate-session-creation.patch new file mode 100644 index 00000000000..bc42475118c --- /dev/null +++ b/queue-4.4/l2tp-fix-duplicate-session-creation.patch @@ -0,0 +1,278 @@ +From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST +From: Will Deacon +Date: Thu, 2 Apr 2020 18:32:47 +0100 +Subject: l2tp: fix duplicate session creation +To: gregkh@linuxfoundation.org +Cc: stable@vger.kernel.org, linux-kernel@vger.kernel.org, kernel-team@android.com, g.nault@alphalink.fr, "David S . Miller" , Will Deacon +Message-ID: <20200402173250.7858-6-will@kernel.org> + +From: Guillaume Nault + +commit dbdbc73b44782e22b3b4b6e8b51e7a3d245f3086 upstream. + +l2tp_session_create() relies on its caller for checking for duplicate +sessions. This is racy since a session can be concurrently inserted +after the caller's verification. + +Fix this by letting l2tp_session_create() verify sessions uniqueness +upon insertion. Callers need to be adapted to check for +l2tp_session_create()'s return code instead of calling +l2tp_session_find(). + +pppol2tp_connect() is a bit special because it has to work on existing +sessions (if they're not connected) or to create a new session if none +is found. When acting on a preexisting session, a reference must be +held or it could go away on us. So we have to use l2tp_session_get() +instead of l2tp_session_find() and drop the reference before exiting. + +Fixes: d9e31d17ceba ("l2tp: Add L2TP ethernet pseudowire support") +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: Will Deacon +Signed-off-by: Greg Kroah-Hartman +--- + net/l2tp/l2tp_core.c | 70 ++++++++++++++++++++++++++++++++++++++------------- + net/l2tp/l2tp_eth.c | 10 +------ + net/l2tp/l2tp_ppp.c | 60 +++++++++++++++++++++---------------------- + 3 files changed, 84 insertions(+), 56 deletions(-) + +--- a/net/l2tp/l2tp_core.c ++++ b/net/l2tp/l2tp_core.c +@@ -377,6 +377,48 @@ struct l2tp_session *l2tp_session_find_b + } + EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname); + ++static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel, ++ struct l2tp_session *session) ++{ ++ struct l2tp_session *session_walk; ++ struct hlist_head *g_head; ++ struct hlist_head *head; ++ struct l2tp_net *pn; ++ ++ head = l2tp_session_id_hash(tunnel, session->session_id); ++ ++ write_lock_bh(&tunnel->hlist_lock); ++ hlist_for_each_entry(session_walk, head, hlist) ++ if (session_walk->session_id == session->session_id) ++ goto exist; ++ ++ if (tunnel->version == L2TP_HDR_VER_3) { ++ pn = l2tp_pernet(tunnel->l2tp_net); ++ g_head = l2tp_session_id_hash_2(l2tp_pernet(tunnel->l2tp_net), ++ session->session_id); ++ ++ spin_lock_bh(&pn->l2tp_session_hlist_lock); ++ hlist_for_each_entry(session_walk, g_head, global_hlist) ++ if (session_walk->session_id == session->session_id) ++ goto exist_glob; ++ ++ hlist_add_head_rcu(&session->global_hlist, g_head); ++ spin_unlock_bh(&pn->l2tp_session_hlist_lock); ++ } ++ ++ hlist_add_head(&session->hlist, head); ++ write_unlock_bh(&tunnel->hlist_lock); ++ ++ return 0; ++ ++exist_glob: ++ spin_unlock_bh(&pn->l2tp_session_hlist_lock); ++exist: ++ write_unlock_bh(&tunnel->hlist_lock); ++ ++ return -EEXIST; ++} ++ + /* Lookup a tunnel by id + */ + struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id) +@@ -1792,6 +1834,7 @@ 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) { +@@ -1847,6 +1890,13 @@ struct l2tp_session *l2tp_session_create + + l2tp_session_set_header_len(session, tunnel->version); + ++ err = l2tp_session_add_to_tunnel(tunnel, session); ++ if (err) { ++ kfree(session); ++ ++ return ERR_PTR(err); ++ } ++ + /* Bump the reference count. The session context is deleted + * only when this drops to zero. + */ +@@ -1856,28 +1906,14 @@ struct l2tp_session *l2tp_session_create + /* Ensure tunnel socket isn't deleted */ + sock_hold(tunnel->sock); + +- /* Add session to the tunnel's hash list */ +- write_lock_bh(&tunnel->hlist_lock); +- hlist_add_head(&session->hlist, +- l2tp_session_id_hash(tunnel, session_id)); +- write_unlock_bh(&tunnel->hlist_lock); +- +- /* And to the global session list if L2TPv3 */ +- if (tunnel->version != L2TP_HDR_VER_2) { +- struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net); +- +- spin_lock_bh(&pn->l2tp_session_hlist_lock); +- hlist_add_head_rcu(&session->global_hlist, +- l2tp_session_id_hash_2(pn, session_id)); +- spin_unlock_bh(&pn->l2tp_session_hlist_lock); +- } +- + /* Ignore management session in session count value */ + if (session->session_id != 0) + atomic_inc(&l2tp_session_count); ++ ++ return session; + } + +- return session; ++ return ERR_PTR(-ENOMEM); + } + EXPORT_SYMBOL_GPL(l2tp_session_create); + +--- a/net/l2tp/l2tp_eth.c ++++ b/net/l2tp/l2tp_eth.c +@@ -223,12 +223,6 @@ static int l2tp_eth_create(struct net *n + goto out; + } + +- session = l2tp_session_find(net, tunnel, session_id); +- if (session) { +- rc = -EEXIST; +- goto out; +- } +- + if (cfg->ifname) { + dev = dev_get_by_name(net, cfg->ifname); + if (dev) { +@@ -242,8 +236,8 @@ static int l2tp_eth_create(struct net *n + + session = l2tp_session_create(sizeof(*spriv), tunnel, session_id, + peer_session_id, cfg); +- if (!session) { +- rc = -ENOMEM; ++ if (IS_ERR(session)) { ++ rc = PTR_ERR(session); + goto out; + } + +--- a/net/l2tp/l2tp_ppp.c ++++ b/net/l2tp/l2tp_ppp.c +@@ -600,6 +600,7 @@ static int pppol2tp_connect(struct socke + int error = 0; + u32 tunnel_id, peer_tunnel_id; + u32 session_id, peer_session_id; ++ bool drop_refcnt = false; + int ver = 2; + int fd; + +@@ -708,36 +709,36 @@ static int pppol2tp_connect(struct socke + if (tunnel->peer_tunnel_id == 0) + tunnel->peer_tunnel_id = peer_tunnel_id; + +- /* Create session if it doesn't already exist. We handle the +- * case where a session was previously created by the netlink +- * interface by checking that the session doesn't already have +- * a socket and its tunnel socket are what we expect. If any +- * of those checks fail, return EEXIST to the caller. +- */ +- session = l2tp_session_find(sock_net(sk), tunnel, session_id); +- if (session == NULL) { +- /* Default MTU must allow space for UDP/L2TP/PPP +- * headers. ++ session = l2tp_session_get(sock_net(sk), tunnel, session_id, false); ++ if (session) { ++ drop_refcnt = true; ++ ps = l2tp_session_priv(session); ++ ++ /* Using a pre-existing session is fine as long as it hasn't ++ * been connected yet. + */ +- cfg.mtu = cfg.mru = 1500 - PPPOL2TP_HEADER_OVERHEAD; ++ if (ps->sock) { ++ error = -EEXIST; ++ goto end; ++ } + +- /* Allocate and initialize a new session context. */ +- session = l2tp_session_create(sizeof(struct pppol2tp_session), +- tunnel, session_id, +- peer_session_id, &cfg); +- if (session == NULL) { +- error = -ENOMEM; ++ /* consistency checks */ ++ if (ps->tunnel_sock != tunnel->sock) { ++ error = -EEXIST; + goto end; + } + } else { +- ps = l2tp_session_priv(session); +- error = -EEXIST; +- if (ps->sock != NULL) +- goto end; ++ /* Default MTU must allow space for UDP/L2TP/PPP headers */ ++ cfg.mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD; ++ cfg.mru = cfg.mtu; + +- /* consistency checks */ +- if (ps->tunnel_sock != tunnel->sock) ++ session = l2tp_session_create(sizeof(struct pppol2tp_session), ++ tunnel, session_id, ++ peer_session_id, &cfg); ++ if (IS_ERR(session)) { ++ error = PTR_ERR(session); + goto end; ++ } + } + + /* Associate session with its PPPoL2TP socket */ +@@ -802,6 +803,8 @@ out_no_ppp: + session->name); + + end: ++ if (drop_refcnt) ++ l2tp_session_dec_refcount(session); + release_sock(sk); + + return error; +@@ -829,12 +832,6 @@ static int pppol2tp_session_create(struc + if (tunnel->sock == NULL) + goto out; + +- /* Check that this session doesn't already exist */ +- error = -EEXIST; +- session = l2tp_session_find(net, tunnel, session_id); +- if (session != NULL) +- goto out; +- + /* Default MTU values. */ + if (cfg->mtu == 0) + cfg->mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD; +@@ -842,12 +839,13 @@ static int pppol2tp_session_create(struc + cfg->mru = cfg->mtu; + + /* Allocate and initialize a new session context. */ +- error = -ENOMEM; + session = l2tp_session_create(sizeof(struct pppol2tp_session), + tunnel, session_id, + peer_session_id, cfg); +- if (session == NULL) ++ if (IS_ERR(session)) { ++ error = PTR_ERR(session); + goto out; ++ } + + ps = l2tp_session_priv(session); + ps->tunnel_sock = tunnel->sock; diff --git a/queue-4.4/l2tp-fix-race-between-l2tp_session_delete-and-l2tp_tunnel_closeall.patch b/queue-4.4/l2tp-fix-race-between-l2tp_session_delete-and-l2tp_tunnel_closeall.patch new file mode 100644 index 00000000000..df81e51f3ab --- /dev/null +++ b/queue-4.4/l2tp-fix-race-between-l2tp_session_delete-and-l2tp_tunnel_closeall.patch @@ -0,0 +1,81 @@ +From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST +From: Will Deacon +Date: Thu, 2 Apr 2020 18:32:50 +0100 +Subject: l2tp: fix race between l2tp_session_delete() and l2tp_tunnel_closeall() +To: gregkh@linuxfoundation.org +Cc: stable@vger.kernel.org, linux-kernel@vger.kernel.org, kernel-team@android.com, g.nault@alphalink.fr, "David S . Miller" , Will Deacon +Message-ID: <20200402173250.7858-9-will@kernel.org> + +From: Guillaume Nault + +commit b228a94066406b6c456321d69643b0d7ce11cfa6 upstream. + +There are several ways to remove L2TP sessions: + + * deleting a session explicitly using the netlink interface (with + L2TP_CMD_SESSION_DELETE), + * deleting the session's parent tunnel (either by closing the + tunnel's file descriptor or using the netlink interface), + * closing the PPPOL2TP file descriptor of a PPP pseudo-wire. + +In some cases, when these methods are used concurrently on the same +session, the session can be removed twice, leading to use-after-free +bugs. + +This patch adds a 'dead' flag, used by l2tp_session_delete() and +l2tp_tunnel_closeall() to prevent them from stepping on each other's +toes. + +The session deletion path used when closing a PPPOL2TP file descriptor +doesn't need to be adapted. It already has to ensure that a session +remains valid for the lifetime of its PPPOL2TP file descriptor. +So it takes an extra reference on the session in the ->session_close() +callback (pppol2tp_session_close()), which is eventually dropped +in the ->sk_destruct() callback of the PPPOL2TP socket +(pppol2tp_session_destruct()). +Still, __l2tp_session_unhash() and l2tp_session_queue_purge() can be +called twice and even concurrently for a given session, but thanks to +proper locking and re-initialisation of list fields, this is not an +issue. + +Signed-off-by: Guillaume Nault +Signed-off-by: David S. Miller +Signed-off-by: Will Deacon +Signed-off-by: Greg Kroah-Hartman +--- + net/l2tp/l2tp_core.c | 6 ++++++ + net/l2tp/l2tp_core.h | 1 + + 2 files changed, 7 insertions(+) + +--- a/net/l2tp/l2tp_core.c ++++ b/net/l2tp/l2tp_core.c +@@ -1351,6 +1351,9 @@ again: + + hlist_del_init(&session->hlist); + ++ if (test_and_set_bit(0, &session->dead)) ++ goto again; ++ + if (session->ref != NULL) + (*session->ref)(session); + +@@ -1799,6 +1802,9 @@ EXPORT_SYMBOL_GPL(__l2tp_session_unhash) + */ + int l2tp_session_delete(struct l2tp_session *session) + { ++ if (test_and_set_bit(0, &session->dead)) ++ return 0; ++ + if (session->ref) + (*session->ref)(session); + __l2tp_session_unhash(session); +--- a/net/l2tp/l2tp_core.h ++++ b/net/l2tp/l2tp_core.h +@@ -85,6 +85,7 @@ struct l2tp_session_cfg { + struct l2tp_session { + int magic; /* should be + * L2TP_SESSION_MAGIC */ ++ long dead; + + struct l2tp_tunnel *tunnel; /* back pointer to tunnel + * context */ diff --git a/queue-4.4/l2tp-fix-race-in-l2tp_recv_common.patch b/queue-4.4/l2tp-fix-race-in-l2tp_recv_common.patch new file mode 100644 index 00000000000..1c859665cee --- /dev/null +++ b/queue-4.4/l2tp-fix-race-in-l2tp_recv_common.patch @@ -0,0 +1,265 @@ +From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST +From: Will Deacon +Date: Thu, 2 Apr 2020 18:32:45 +0100 +Subject: l2tp: fix race in l2tp_recv_common() +To: gregkh@linuxfoundation.org +Cc: stable@vger.kernel.org, linux-kernel@vger.kernel.org, kernel-team@android.com, g.nault@alphalink.fr, "David S . Miller" , Will Deacon +Message-ID: <20200402173250.7858-4-will@kernel.org> + +From: Guillaume Nault + +commit 61b9a047729bb230978178bca6729689d0c50ca2 upstream. + +Taking a reference on sessions in l2tp_recv_common() is racy; this +has to be done by the callers. + +To this end, a new function is required (l2tp_session_get()) to +atomically lookup a session and take a reference on it. Callers then +have to manually drop this reference. + +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: Will Deacon +Signed-off-by: Greg Kroah-Hartman +--- + net/l2tp/l2tp_core.c | 73 +++++++++++++++++++++++++++++++++++++++++---------- + net/l2tp/l2tp_core.h | 3 ++ + net/l2tp/l2tp_ip.c | 17 ++++++++--- + net/l2tp/l2tp_ip6.c | 18 +++++++++--- + 4 files changed, 88 insertions(+), 23 deletions(-) + +--- a/net/l2tp/l2tp_core.c ++++ b/net/l2tp/l2tp_core.c +@@ -277,6 +277,55 @@ struct l2tp_session *l2tp_session_find(s + } + EXPORT_SYMBOL_GPL(l2tp_session_find); + ++/* Like l2tp_session_find() but takes a reference on the returned session. ++ * Optionally calls session->ref() too if do_ref is true. ++ */ ++struct l2tp_session *l2tp_session_get(struct net *net, ++ struct l2tp_tunnel *tunnel, ++ u32 session_id, bool do_ref) ++{ ++ struct hlist_head *session_list; ++ struct l2tp_session *session; ++ ++ if (!tunnel) { ++ struct l2tp_net *pn = l2tp_pernet(net); ++ ++ session_list = l2tp_session_id_hash_2(pn, session_id); ++ ++ rcu_read_lock_bh(); ++ hlist_for_each_entry_rcu(session, session_list, global_hlist) { ++ if (session->session_id == session_id) { ++ l2tp_session_inc_refcount(session); ++ if (do_ref && session->ref) ++ session->ref(session); ++ rcu_read_unlock_bh(); ++ ++ return session; ++ } ++ } ++ rcu_read_unlock_bh(); ++ ++ return NULL; ++ } ++ ++ session_list = l2tp_session_id_hash(tunnel, session_id); ++ read_lock_bh(&tunnel->hlist_lock); ++ hlist_for_each_entry(session, session_list, hlist) { ++ if (session->session_id == session_id) { ++ l2tp_session_inc_refcount(session); ++ if (do_ref && session->ref) ++ session->ref(session); ++ read_unlock_bh(&tunnel->hlist_lock); ++ ++ return session; ++ } ++ } ++ read_unlock_bh(&tunnel->hlist_lock); ++ ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(l2tp_session_get); ++ + struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth, + bool do_ref) + { +@@ -636,6 +685,9 @@ discard: + * a data (not control) frame before coming here. Fields up to the + * session-id have already been parsed and ptr points to the data + * after the session-id. ++ * ++ * session->ref() must have been called prior to l2tp_recv_common(). ++ * session->deref() will be called automatically after skb is processed. + */ + void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, + unsigned char *ptr, unsigned char *optr, u16 hdrflags, +@@ -645,14 +697,6 @@ void l2tp_recv_common(struct l2tp_sessio + int offset; + u32 ns, nr; + +- /* The ref count is increased since we now hold a pointer to +- * the session. Take care to decrement the refcnt when exiting +- * this function from now on... +- */ +- l2tp_session_inc_refcount(session); +- if (session->ref) +- (*session->ref)(session); +- + /* Parse and check optional cookie */ + if (session->peer_cookie_len > 0) { + if (memcmp(ptr, &session->peer_cookie[0], session->peer_cookie_len)) { +@@ -803,8 +847,6 @@ void l2tp_recv_common(struct l2tp_sessio + /* Try to dequeue as many skbs from reorder_q as we can. */ + l2tp_recv_dequeue(session); + +- l2tp_session_dec_refcount(session); +- + return; + + discard: +@@ -813,8 +855,6 @@ discard: + + if (session->deref) + (*session->deref)(session); +- +- l2tp_session_dec_refcount(session); + } + EXPORT_SYMBOL(l2tp_recv_common); + +@@ -921,8 +961,14 @@ static int l2tp_udp_recv_core(struct l2t + } + + /* Find the session context */ +- session = l2tp_session_find(tunnel->l2tp_net, tunnel, session_id); ++ session = l2tp_session_get(tunnel->l2tp_net, tunnel, session_id, true); + if (!session || !session->recv_skb) { ++ if (session) { ++ if (session->deref) ++ session->deref(session); ++ l2tp_session_dec_refcount(session); ++ } ++ + /* Not found? Pass to userspace to deal with */ + l2tp_info(tunnel, L2TP_MSG_DATA, + "%s: no session found (%u/%u). Passing up.\n", +@@ -935,6 +981,7 @@ static int l2tp_udp_recv_core(struct l2t + goto error; + + l2tp_recv_common(session, skb, ptr, optr, hdrflags, length, payload_hook); ++ l2tp_session_dec_refcount(session); + + return 0; + +--- a/net/l2tp/l2tp_core.h ++++ b/net/l2tp/l2tp_core.h +@@ -243,6 +243,9 @@ out: + return tunnel; + } + ++struct l2tp_session *l2tp_session_get(struct net *net, ++ struct l2tp_tunnel *tunnel, ++ u32 session_id, bool do_ref); + struct l2tp_session *l2tp_session_find(struct net *net, + struct l2tp_tunnel *tunnel, + u32 session_id); +--- a/net/l2tp/l2tp_ip.c ++++ b/net/l2tp/l2tp_ip.c +@@ -142,19 +142,19 @@ static int l2tp_ip_recv(struct sk_buff * + } + + /* Ok, this is a data packet. Lookup the session. */ +- session = l2tp_session_find(net, NULL, session_id); +- if (session == NULL) ++ session = l2tp_session_get(net, NULL, session_id, true); ++ if (!session) + goto discard; + + tunnel = session->tunnel; +- if (tunnel == NULL) +- goto discard; ++ if (!tunnel) ++ goto discard_sess; + + /* Trace packet contents, if enabled */ + if (tunnel->debug & L2TP_MSG_DATA) { + length = min(32u, skb->len); + if (!pskb_may_pull(skb, length)) +- goto discard; ++ goto discard_sess; + + /* Point to L2TP header */ + optr = ptr = skb->data; +@@ -167,6 +167,7 @@ static int l2tp_ip_recv(struct sk_buff * + goto discard; + + l2tp_recv_common(session, skb, ptr, optr, 0, skb->len, tunnel->recv_payload_hook); ++ l2tp_session_dec_refcount(session); + + return 0; + +@@ -204,6 +205,12 @@ pass_up: + + return sk_receive_skb(sk, skb, 1); + ++discard_sess: ++ if (session->deref) ++ session->deref(session); ++ l2tp_session_dec_refcount(session); ++ goto discard; ++ + discard_put: + sock_put(sk); + +--- a/net/l2tp/l2tp_ip6.c ++++ b/net/l2tp/l2tp_ip6.c +@@ -154,19 +154,19 @@ static int l2tp_ip6_recv(struct sk_buff + } + + /* Ok, this is a data packet. Lookup the session. */ +- session = l2tp_session_find(net, NULL, session_id); +- if (session == NULL) ++ session = l2tp_session_get(net, NULL, session_id, true); ++ if (!session) + goto discard; + + tunnel = session->tunnel; +- if (tunnel == NULL) +- goto discard; ++ if (!tunnel) ++ goto discard_sess; + + /* Trace packet contents, if enabled */ + if (tunnel->debug & L2TP_MSG_DATA) { + length = min(32u, skb->len); + if (!pskb_may_pull(skb, length)) +- goto discard; ++ goto discard_sess; + + /* Point to L2TP header */ + optr = ptr = skb->data; +@@ -180,6 +180,8 @@ static int l2tp_ip6_recv(struct sk_buff + + l2tp_recv_common(session, skb, ptr, optr, 0, skb->len, + tunnel->recv_payload_hook); ++ l2tp_session_dec_refcount(session); ++ + return 0; + + pass_up: +@@ -217,6 +219,12 @@ pass_up: + + return sk_receive_skb(sk, skb, 1); + ++discard_sess: ++ if (session->deref) ++ session->deref(session); ++ l2tp_session_dec_refcount(session); ++ goto discard; ++ + discard_put: + sock_put(sk); + diff --git a/queue-4.4/l2tp-refactor-the-codes-with-existing-macros-instead-of-literal-number.patch b/queue-4.4/l2tp-refactor-the-codes-with-existing-macros-instead-of-literal-number.patch new file mode 100644 index 00000000000..d2537d81aca --- /dev/null +++ b/queue-4.4/l2tp-refactor-the-codes-with-existing-macros-instead-of-literal-number.patch @@ -0,0 +1,99 @@ +From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST +From: Will Deacon +Date: Thu, 2 Apr 2020 18:32:48 +0100 +Subject: l2tp: Refactor the codes with existing macros instead of literal number +To: gregkh@linuxfoundation.org +Cc: stable@vger.kernel.org, linux-kernel@vger.kernel.org, kernel-team@android.com, g.nault@alphalink.fr, Gao Feng , "David S . Miller" , Will Deacon +Message-ID: <20200402173250.7858-7-will@kernel.org> + +From: Gao Feng + +commit 54c151d9ed1321e6e623c80ffe42cd2eb1571744 upstream. + +Use PPP_ALLSTATIONS, PPP_UI, and SEND_SHUTDOWN instead of 0xff, +0x03, and 2 separately. + +Signed-off-by: Gao Feng +Acked-by: Guillaume Nault +Signed-off-by: David S. Miller +Signed-off-by: Will Deacon +Signed-off-by: Greg Kroah-Hartman +--- + net/l2tp/l2tp_ppp.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +--- a/net/l2tp/l2tp_ppp.c ++++ b/net/l2tp/l2tp_ppp.c +@@ -177,7 +177,7 @@ static int pppol2tp_recv_payload_hook(st + if (!pskb_may_pull(skb, 2)) + return 1; + +- if ((skb->data[0] == 0xff) && (skb->data[1] == 0x03)) ++ if ((skb->data[0] == PPP_ALLSTATIONS) && (skb->data[1] == PPP_UI)) + skb_pull(skb, 2); + + return 0; +@@ -297,7 +297,6 @@ static void pppol2tp_session_sock_put(st + static int pppol2tp_sendmsg(struct socket *sock, struct msghdr *m, + size_t total_len) + { +- static const unsigned char ppph[2] = { 0xff, 0x03 }; + struct sock *sk = sock->sk; + struct sk_buff *skb; + int error; +@@ -327,7 +326,7 @@ static int pppol2tp_sendmsg(struct socke + error = -ENOMEM; + skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) + + uhlen + session->hdr_len + +- sizeof(ppph) + total_len, ++ 2 + total_len, /* 2 bytes for PPP_ALLSTATIONS & PPP_UI */ + 0, GFP_KERNEL); + if (!skb) + goto error_put_sess_tun; +@@ -340,8 +339,8 @@ static int pppol2tp_sendmsg(struct socke + skb_reserve(skb, uhlen); + + /* Add PPP header */ +- skb->data[0] = ppph[0]; +- skb->data[1] = ppph[1]; ++ skb->data[0] = PPP_ALLSTATIONS; ++ skb->data[1] = PPP_UI; + skb_put(skb, 2); + + /* Copy user data into skb */ +@@ -384,7 +383,6 @@ error: + */ + static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb) + { +- static const u8 ppph[2] = { 0xff, 0x03 }; + struct sock *sk = (struct sock *) chan->private; + struct sock *sk_tun; + struct l2tp_session *session; +@@ -413,14 +411,14 @@ static int pppol2tp_xmit(struct ppp_chan + sizeof(struct iphdr) + /* IP header */ + uhlen + /* UDP header (if L2TP_ENCAPTYPE_UDP) */ + session->hdr_len + /* L2TP header */ +- sizeof(ppph); /* PPP header */ ++ 2; /* 2 bytes for PPP_ALLSTATIONS & PPP_UI */ + if (skb_cow_head(skb, headroom)) + goto abort_put_sess_tun; + + /* Setup PPP header */ +- __skb_push(skb, sizeof(ppph)); +- skb->data[0] = ppph[0]; +- skb->data[1] = ppph[1]; ++ __skb_push(skb, 2); ++ skb->data[0] = PPP_ALLSTATIONS; ++ skb->data[1] = PPP_UI; + + local_bh_disable(); + l2tp_xmit_skb(session, skb, session->hdr_len); +@@ -455,7 +453,7 @@ static void pppol2tp_session_close(struc + BUG_ON(session->magic != L2TP_SESSION_MAGIC); + + if (sock) { +- inet_shutdown(sock, 2); ++ inet_shutdown(sock, SEND_SHUTDOWN); + /* Don't let the session go away before our socket does */ + l2tp_session_inc_refcount(session); + } diff --git a/queue-4.4/net-l2tp-make-l2tp_ip6-namespace-aware.patch b/queue-4.4/net-l2tp-make-l2tp_ip6-namespace-aware.patch new file mode 100644 index 00000000000..356f4e530fc --- /dev/null +++ b/queue-4.4/net-l2tp-make-l2tp_ip6-namespace-aware.patch @@ -0,0 +1,88 @@ +From 0e6b5259824e97a0f7e7b450421ff12865d3b0e2 Mon Sep 17 00:00:00 2001 +From: Shmulik Ladkani +Date: Thu, 26 May 2016 20:16:36 +0300 +Subject: net: l2tp: Make l2tp_ip6 namespace aware + +From: Shmulik Ladkani + +commit 0e6b5259824e97a0f7e7b450421ff12865d3b0e2 upstream. + +l2tp_ip6 tunnel and session lookups were still using init_net, although +the l2tp core infrastructure already supports lookups keyed by 'net'. + +As a result, l2tp_ip6_recv discarded packets for tunnels/sessions +created in namespaces other than the init_net. + +Fix, by using dev_net(skb->dev) or sock_net(sk) where appropriate. + +Signed-off-by: Shmulik Ladkani +Signed-off-by: David S. Miller +Signed-off-by: Will Deacon +Signed-off-by: Greg Kroah-Hartman +--- + net/l2tp/l2tp_ip6.c | 12 +++++++----- + 1 file changed, 7 insertions(+), 5 deletions(-) + +--- a/net/l2tp/l2tp_ip6.c ++++ b/net/l2tp/l2tp_ip6.c +@@ -127,6 +127,7 @@ static inline struct sock *l2tp_ip6_bind + */ + static int l2tp_ip6_recv(struct sk_buff *skb) + { ++ struct net *net = dev_net(skb->dev); + struct sock *sk; + u32 session_id; + u32 tunnel_id; +@@ -153,7 +154,7 @@ static int l2tp_ip6_recv(struct sk_buff + } + + /* Ok, this is a data packet. Lookup the session. */ +- session = l2tp_session_find(&init_net, NULL, session_id); ++ session = l2tp_session_find(net, NULL, session_id); + if (session == NULL) + goto discard; + +@@ -190,7 +191,7 @@ pass_up: + goto discard; + + tunnel_id = ntohl(*(__be32 *) &skb->data[4]); +- tunnel = l2tp_tunnel_find(&init_net, tunnel_id); ++ tunnel = l2tp_tunnel_find(net, tunnel_id); + if (tunnel) { + sk = tunnel->sock; + sock_hold(sk); +@@ -198,7 +199,7 @@ pass_up: + struct ipv6hdr *iph = ipv6_hdr(skb); + + read_lock_bh(&l2tp_ip6_lock); +- sk = __l2tp_ip6_bind_lookup(&init_net, &iph->daddr, ++ sk = __l2tp_ip6_bind_lookup(net, &iph->daddr, + 0, tunnel_id); + if (!sk) { + read_unlock_bh(&l2tp_ip6_lock); +@@ -267,6 +268,7 @@ static int l2tp_ip6_bind(struct sock *sk + struct inet_sock *inet = inet_sk(sk); + struct ipv6_pinfo *np = inet6_sk(sk); + struct sockaddr_l2tpip6 *addr = (struct sockaddr_l2tpip6 *) uaddr; ++ struct net *net = sock_net(sk); + __be32 v4addr = 0; + int addr_type; + int err; +@@ -288,7 +290,7 @@ static int l2tp_ip6_bind(struct sock *sk + + err = -EADDRINUSE; + read_lock_bh(&l2tp_ip6_lock); +- if (__l2tp_ip6_bind_lookup(&init_net, &addr->l2tp_addr, ++ if (__l2tp_ip6_bind_lookup(net, &addr->l2tp_addr, + sk->sk_bound_dev_if, addr->l2tp_conn_id)) + goto out_in_use; + read_unlock_bh(&l2tp_ip6_lock); +@@ -461,7 +463,7 @@ static int l2tp_ip6_backlog_recv(struct + return 0; + + drop: +- IP_INC_STATS(&init_net, IPSTATS_MIB_INDISCARDS); ++ IP_INC_STATS(sock_net(sk), IPSTATS_MIB_INDISCARDS); + kfree_skb(skb); + return -1; + } diff --git a/queue-4.4/series b/queue-4.4/series index 9f283d408d2..e573f4228a8 100644 --- a/queue-4.4/series +++ b/queue-4.4/series @@ -2,3 +2,11 @@ drm-bochs-downgrade-pci_request_region-failure-from-.patch ipv4-fix-a-rcu-list-lock-in-fib_triestat_seq_show.patch net-ip_tunnel-fix-interface-lookup-with-no-key.patch sctp-fix-possibly-using-a-bad-saddr-with-a-given-dst.patch +l2tp-correctly-return-ebadf-from-pppol2tp_getname.patch +net-l2tp-make-l2tp_ip6-namespace-aware.patch +l2tp-fix-race-in-l2tp_recv_common.patch +l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch +l2tp-fix-duplicate-session-creation.patch +l2tp-refactor-the-codes-with-existing-macros-instead-of-literal-number.patch +l2tp-ensure-sessions-are-freed-after-their-pppol2tp-socket.patch +l2tp-fix-race-between-l2tp_session_delete-and-l2tp_tunnel_closeall.patch