]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.4-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 3 Apr 2020 12:36:42 +0000 (14:36 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 3 Apr 2020 12:36:42 +0000 (14:36 +0200)
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

queue-4.4/l2tp-correctly-return-ebadf-from-pppol2tp_getname.patch [new file with mode: 0644]
queue-4.4/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch [new file with mode: 0644]
queue-4.4/l2tp-ensure-sessions-are-freed-after-their-pppol2tp-socket.patch [new file with mode: 0644]
queue-4.4/l2tp-fix-duplicate-session-creation.patch [new file with mode: 0644]
queue-4.4/l2tp-fix-race-between-l2tp_session_delete-and-l2tp_tunnel_closeall.patch [new file with mode: 0644]
queue-4.4/l2tp-fix-race-in-l2tp_recv_common.patch [new file with mode: 0644]
queue-4.4/l2tp-refactor-the-codes-with-existing-macros-instead-of-literal-number.patch [new file with mode: 0644]
queue-4.4/net-l2tp-make-l2tp_ip6-namespace-aware.patch [new file with mode: 0644]
queue-4.4/series

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 (file)
index 0000000..046377f
--- /dev/null
@@ -0,0 +1,51 @@
+From 4ac36a4adaf80013a60013d6f829f5863d5d0e05 Mon Sep 17 00:00:00 2001
+From: "phil.turnbull@oracle.com" <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 <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 <phil.turnbull@oracle.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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 (file)
index 0000000..11adf9d
--- /dev/null
@@ -0,0 +1,51 @@
+From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST
+From: Will Deacon <will@kernel.org>
+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" <davem@davemloft.net>, Will Deacon <will@kernel.org>
+Message-ID: <20200402173250.7858-5-will@kernel.org>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+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 <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..755cb82
--- /dev/null
@@ -0,0 +1,53 @@
+From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST
+From: Will Deacon <will@kernel.org>
+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" <davem@davemloft.net>, Will Deacon <will@kernel.org>
+Message-ID: <20200402173250.7858-8-will@kernel.org>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+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 <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..bc42475
--- /dev/null
@@ -0,0 +1,278 @@
+From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST
+From: Will Deacon <will@kernel.org>
+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" <davem@davemloft.net>, Will Deacon <will@kernel.org>
+Message-ID: <20200402173250.7858-6-will@kernel.org>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+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 <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..df81e51
--- /dev/null
@@ -0,0 +1,81 @@
+From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST
+From: Will Deacon <will@kernel.org>
+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" <davem@davemloft.net>, Will Deacon <will@kernel.org>
+Message-ID: <20200402173250.7858-9-will@kernel.org>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+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 <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..1c85966
--- /dev/null
@@ -0,0 +1,265 @@
+From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST
+From: Will Deacon <will@kernel.org>
+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" <davem@davemloft.net>, Will Deacon <will@kernel.org>
+Message-ID: <20200402173250.7858-4-will@kernel.org>
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+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 <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..d2537d8
--- /dev/null
@@ -0,0 +1,99 @@
+From foo@baz Fri 03 Apr 2020 02:35:58 PM CEST
+From: Will Deacon <will@kernel.org>
+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 <fgao@ikuai8.com>, "David S . Miller" <davem@davemloft.net>, Will Deacon <will@kernel.org>
+Message-ID: <20200402173250.7858-7-will@kernel.org>
+
+From: Gao Feng <fgao@ikuai8.com>
+
+commit 54c151d9ed1321e6e623c80ffe42cd2eb1571744 upstream.
+
+Use PPP_ALLSTATIONS, PPP_UI, and SEND_SHUTDOWN instead of 0xff,
+0x03, and 2 separately.
+
+Signed-off-by: Gao Feng <fgao@ikuai8.com>
+Acked-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..356f4e5
--- /dev/null
@@ -0,0 +1,88 @@
+From 0e6b5259824e97a0f7e7b450421ff12865d3b0e2 Mon Sep 17 00:00:00 2001
+From: Shmulik Ladkani <shmulik.ladkani@gmail.com>
+Date: Thu, 26 May 2016 20:16:36 +0300
+Subject: net: l2tp: Make l2tp_ip6 namespace aware
+
+From: Shmulik Ladkani <shmulik.ladkani@gmail.com>
+
+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 <shmulik.ladkani@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Will Deacon <will@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
+ }
index 9f283d408d276c71011d50579da4ff6956298252..e573f4228a871ea66f93d5948a9e97ce31e1e1d4 100644 (file)
@@ -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