--- /dev/null
+From 57377d63547861919ee634b845c7caa38de4a452 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:26 +0200
+Subject: l2tp: ensure session can't get removed during pppol2tp_session_ioctl()
+
+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: Amit Pundir <amit.pundir@linaro.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
+@@ -1141,11 +1141,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
--- /dev/null
+From dbdbc73b44782e22b3b4b6e8b51e7a3d245f3086 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:27 +0200
+Subject: l2tp: fix duplicate session creation
+
+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: Amit Pundir <amit.pundir@linaro.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
+@@ -378,6 +378,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)
+@@ -1787,6 +1829,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) {
+@@ -1842,6 +1885,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.
+ */
+@@ -1851,28 +1901,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
+@@ -583,6 +583,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;
+
+@@ -684,36 +685,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 */
+@@ -778,6 +779,8 @@ out_no_ppp:
+ session->name);
+
+ end:
++ if (drop_refcnt)
++ l2tp_session_dec_refcount(session);
+ release_sock(sk);
+
+ return error;
+@@ -805,12 +808,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;
+@@ -818,12 +815,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;
--- /dev/null
+From 61b9a047729bb230978178bca6729689d0c50ca2 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:25 +0200
+Subject: l2tp: fix race in l2tp_recv_common()
+
+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: Amit Pundir <amit.pundir@linaro.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
+@@ -278,6 +278,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)
+ {
+@@ -637,6 +686,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,
+@@ -646,14 +698,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)) {
+@@ -806,8 +850,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:
+@@ -816,8 +858,6 @@ discard:
+
+ if (session->deref)
+ (*session->deref)(session);
+-
+- l2tp_session_dec_refcount(session);
+ }
+ EXPORT_SYMBOL(l2tp_recv_common);
+
+@@ -924,8 +964,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",
+@@ -934,6 +980,7 @@ static int l2tp_udp_recv_core(struct l2t
+ }
+
+ 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
+@@ -240,6 +240,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
+@@ -143,19 +143,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;
+@@ -165,6 +165,7 @@ static int l2tp_ip_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;
+
+@@ -203,6 +204,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
+@@ -156,19 +156,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;
+@@ -179,6 +179,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:
+@@ -216,6 +218,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);
+
--- /dev/null
+From 5e6a9e5a3554a5b3db09cdc22253af1849c65dff Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:29 +0200
+Subject: l2tp: hold session while sending creation notifications
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 5e6a9e5a3554a5b3db09cdc22253af1849c65dff upstream.
+
+l2tp_session_find() doesn't take any reference on the returned session.
+Therefore, the session may disappear while sending the notification.
+
+Use l2tp_session_get() instead and decrement session's refcount once
+the notification is sent.
+
+Fixes: 33f72e6f0c67 ("l2tp : multicast notification to the registered listeners")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ net/l2tp/l2tp_netlink.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -634,10 +634,12 @@ static int l2tp_nl_cmd_session_create(st
+ session_id, peer_session_id, &cfg);
+
+ if (ret >= 0) {
+- session = l2tp_session_find(net, tunnel, session_id);
+- if (session)
++ session = l2tp_session_get(net, tunnel, session_id, false);
++ if (session) {
+ ret = l2tp_session_notify(&l2tp_nl_family, info, session,
+ L2TP_CMD_SESSION_CREATE);
++ l2tp_session_dec_refcount(session);
++ }
+ }
+
+ out:
--- /dev/null
+From 2777e2ab5a9cf2b4524486c6db1517a6ded25261 Mon Sep 17 00:00:00 2001
+From: Guillaume Nault <g.nault@alphalink.fr>
+Date: Fri, 31 Mar 2017 13:02:30 +0200
+Subject: l2tp: take a reference on sessions used in genetlink handlers
+
+From: Guillaume Nault <g.nault@alphalink.fr>
+
+commit 2777e2ab5a9cf2b4524486c6db1517a6ded25261 upstream.
+
+Callers of l2tp_nl_session_find() need to hold a reference on the
+returned session since there's no guarantee that it isn't going to
+disappear from under them.
+
+Relying on the fact that no l2tp netlink message may be processed
+concurrently isn't enough: sessions can be deleted by other means
+(e.g. by closing the PPPOL2TP socket of a ppp pseudowire).
+
+l2tp_nl_cmd_session_delete() is a bit special: it runs a callback
+function that may require a previous call to session->ref(). In
+particular, for ppp pseudowires, the callback is l2tp_session_delete(),
+which then calls pppol2tp_session_close() and dereferences the PPPOL2TP
+socket. The socket might already be gone at the moment
+l2tp_session_delete() calls session->ref(), so we need to take a
+reference during the session lookup. So we need to pass the do_ref
+variable down to l2tp_session_get() and l2tp_session_get_by_ifname().
+
+Since all callers have to be updated, l2tp_session_find_by_ifname() and
+l2tp_nl_session_find() are renamed to reflect their new behaviour.
+
+Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
+Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/l2tp/l2tp_core.c | 9 +++++++--
+ net/l2tp/l2tp_core.h | 3 ++-
+ net/l2tp/l2tp_netlink.c | 39 ++++++++++++++++++++++++++-------------
+ 3 files changed, 35 insertions(+), 16 deletions(-)
+
+--- a/net/l2tp/l2tp_core.c
++++ b/net/l2tp/l2tp_core.c
+@@ -356,7 +356,8 @@ EXPORT_SYMBOL_GPL(l2tp_session_get_nth);
+ /* Lookup a session by interface name.
+ * This is very inefficient but is only used by management interfaces.
+ */
+-struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname)
++struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
++ bool do_ref)
+ {
+ struct l2tp_net *pn = l2tp_pernet(net);
+ int hash;
+@@ -366,7 +367,11 @@ struct l2tp_session *l2tp_session_find_b
+ for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) {
+ hlist_for_each_entry_rcu(session, &pn->l2tp_session_hlist[hash], global_hlist) {
+ if (!strcmp(session->ifname, ifname)) {
++ l2tp_session_inc_refcount(session);
++ if (do_ref && session->ref)
++ session->ref(session);
+ rcu_read_unlock_bh();
++
+ return session;
+ }
+ }
+@@ -376,7 +381,7 @@ struct l2tp_session *l2tp_session_find_b
+
+ return NULL;
+ }
+-EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname);
++EXPORT_SYMBOL_GPL(l2tp_session_get_by_ifname);
+
+ static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel,
+ struct l2tp_session *session)
+--- a/net/l2tp/l2tp_core.h
++++ b/net/l2tp/l2tp_core.h
+@@ -248,7 +248,8 @@ struct l2tp_session *l2tp_session_find(s
+ u32 session_id);
+ struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
+ bool do_ref);
+-struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname);
++struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
++ bool do_ref);
+ struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
+ struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
+
+--- a/net/l2tp/l2tp_netlink.c
++++ b/net/l2tp/l2tp_netlink.c
+@@ -55,7 +55,8 @@ static int l2tp_nl_session_send(struct s
+ /* Accessed under genl lock */
+ static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX];
+
+-static struct l2tp_session *l2tp_nl_session_find(struct genl_info *info)
++static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info,
++ bool do_ref)
+ {
+ u32 tunnel_id;
+ u32 session_id;
+@@ -66,14 +67,15 @@ static struct l2tp_session *l2tp_nl_sess
+
+ if (info->attrs[L2TP_ATTR_IFNAME]) {
+ ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
+- session = l2tp_session_find_by_ifname(net, ifname);
++ session = l2tp_session_get_by_ifname(net, ifname, do_ref);
+ } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) &&
+ (info->attrs[L2TP_ATTR_CONN_ID])) {
+ tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
+ session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
+ tunnel = l2tp_tunnel_find(net, tunnel_id);
+ if (tunnel)
+- session = l2tp_session_find(net, tunnel, session_id);
++ session = l2tp_session_get(net, tunnel, session_id,
++ do_ref);
+ }
+
+ return session;
+@@ -652,7 +654,7 @@ static int l2tp_nl_cmd_session_delete(st
+ struct l2tp_session *session;
+ u16 pw_type;
+
+- session = l2tp_nl_session_find(info);
++ session = l2tp_nl_session_get(info, true);
+ if (session == NULL) {
+ ret = -ENODEV;
+ goto out;
+@@ -666,6 +668,10 @@ static int l2tp_nl_cmd_session_delete(st
+ if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
+ ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session);
+
++ if (session->deref)
++ session->deref(session);
++ l2tp_session_dec_refcount(session);
++
+ out:
+ return ret;
+ }
+@@ -675,7 +681,7 @@ static int l2tp_nl_cmd_session_modify(st
+ int ret = 0;
+ struct l2tp_session *session;
+
+- session = l2tp_nl_session_find(info);
++ session = l2tp_nl_session_get(info, false);
+ if (session == NULL) {
+ ret = -ENODEV;
+ goto out;
+@@ -710,6 +716,8 @@ static int l2tp_nl_cmd_session_modify(st
+ ret = l2tp_session_notify(&l2tp_nl_family, info,
+ session, L2TP_CMD_SESSION_MODIFY);
+
++ l2tp_session_dec_refcount(session);
++
+ out:
+ return ret;
+ }
+@@ -805,29 +813,34 @@ static int l2tp_nl_cmd_session_get(struc
+ struct sk_buff *msg;
+ int ret;
+
+- session = l2tp_nl_session_find(info);
++ session = l2tp_nl_session_get(info, false);
+ if (session == NULL) {
+ ret = -ENODEV;
+- goto out;
++ goto err;
+ }
+
+ msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (!msg) {
+ ret = -ENOMEM;
+- goto out;
++ goto err_ref;
+ }
+
+ ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
+ 0, session, L2TP_CMD_SESSION_GET);
+ if (ret < 0)
+- goto err_out;
++ goto err_ref_msg;
+
+- return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
++ ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
+
+-err_out:
+- nlmsg_free(msg);
++ l2tp_session_dec_refcount(session);
+
+-out:
++ return ret;
++
++err_ref_msg:
++ nlmsg_free(msg);
++err_ref:
++ l2tp_session_dec_refcount(session);
++err:
+ return ret;
+ }
+
--- /dev/null
+From 3c226c637b69104f6b9f1c6ec5b08d7b741b3229 Mon Sep 17 00:00:00 2001
+From: Mark Rutland <mark.rutland@arm.com>
+Date: Fri, 16 Jun 2017 14:02:34 -0700
+Subject: mm: numa: avoid waiting on freed migrated pages
+
+From: Mark Rutland <mark.rutland@arm.com>
+
+commit 3c226c637b69104f6b9f1c6ec5b08d7b741b3229 upstream.
+
+In do_huge_pmd_numa_page(), we attempt to handle a migrating thp pmd by
+waiting until the pmd is unlocked before we return and retry. However,
+we can race with migrate_misplaced_transhuge_page():
+
+ // do_huge_pmd_numa_page // migrate_misplaced_transhuge_page()
+ // Holds 0 refs on page // Holds 2 refs on page
+
+ vmf->ptl = pmd_lock(vma->vm_mm, vmf->pmd);
+ /* ... */
+ if (pmd_trans_migrating(*vmf->pmd)) {
+ page = pmd_page(*vmf->pmd);
+ spin_unlock(vmf->ptl);
+ ptl = pmd_lock(mm, pmd);
+ if (page_count(page) != 2)) {
+ /* roll back */
+ }
+ /* ... */
+ mlock_migrate_page(new_page, page);
+ /* ... */
+ spin_unlock(ptl);
+ put_page(page);
+ put_page(page); // page freed here
+ wait_on_page_locked(page);
+ goto out;
+ }
+
+This can result in the freed page having its waiters flag set
+unexpectedly, which trips the PAGE_FLAGS_CHECK_AT_PREP checks in the
+page alloc/free functions. This has been observed on arm64 KVM guests.
+
+We can avoid this by having do_huge_pmd_numa_page() take a reference on
+the page before dropping the pmd lock, mirroring what we do in
+__migration_entry_wait().
+
+When we hit the race, migrate_misplaced_transhuge_page() will see the
+reference and abort the migration, as it may do today in other cases.
+
+Fixes: b8916634b77bffb2 ("mm: Prevent parallel splits during THP migration")
+Link: http://lkml.kernel.org/r/1497349722-6731-2-git-send-email-will.deacon@arm.com
+Signed-off-by: Mark Rutland <mark.rutland@arm.com>
+Signed-off-by: Will Deacon <will.deacon@arm.com>
+Acked-by: Steve Capper <steve.capper@arm.com>
+Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
+Acked-by: Vlastimil Babka <vbabka@suse.cz>
+Cc: Mel Gorman <mgorman@suse.de>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ mm/huge_memory.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/mm/huge_memory.c
++++ b/mm/huge_memory.c
+@@ -1227,8 +1227,11 @@ int do_huge_pmd_numa_page(struct fault_e
+ */
+ if (unlikely(pmd_trans_migrating(*fe->pmd))) {
+ page = pmd_page(*fe->pmd);
++ if (!get_page_unless_zero(page))
++ goto out_unlock;
+ spin_unlock(fe->ptl);
+ wait_on_page_locked(page);
++ put_page(page);
+ goto out;
+ }
+
+@@ -1260,8 +1263,11 @@ int do_huge_pmd_numa_page(struct fault_e
+
+ /* Migration could have started since the pmd_trans_migrating check */
+ if (!page_locked) {
++ if (!get_page_unless_zero(page))
++ goto out_unlock;
+ spin_unlock(fe->ptl);
+ wait_on_page_locked(page);
++ put_page(page);
+ page_nid = -1;
+ goto out;
+ }
drm-vmwgfx-free-hash-table-allocated-by-cmdbuf-managed-res-mgr.patch
dm-thin-do-not-queue-freed-thin-mapping-for-next-stage-processing.patch
x86-mm-fix-boot-crash-caused-by-incorrect-loop-count-calculation-in-sync_global_pgds.patch
+usb-gadget-f_fs-fix-possibe-deadlock.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-hold-session-while-sending-creation-notifications.patch
+l2tp-take-a-reference-on-sessions-used-in-genetlink-handlers.patch
+mm-numa-avoid-waiting-on-freed-migrated-pages.patch
--- /dev/null
+From b3ce3ce02d146841af012d08506b4071db8ffde3 Mon Sep 17 00:00:00 2001
+From: Baolin Wang <baolin.wang@linaro.org>
+Date: Thu, 8 Dec 2016 19:55:22 +0800
+Subject: usb: gadget: f_fs: Fix possibe deadlock
+
+From: Baolin Wang <baolin.wang@linaro.org>
+
+commit b3ce3ce02d146841af012d08506b4071db8ffde3 upstream.
+
+When system try to close /dev/usb-ffs/adb/ep0 on one core, at the same
+time another core try to attach new UDC, which will cause deadlock as
+below scenario. Thus we should release ffs lock before issuing
+unregister_gadget_item().
+
+[ 52.642225] c1 ======================================================
+[ 52.642228] c1 [ INFO: possible circular locking dependency detected ]
+[ 52.642236] c1 4.4.6+ #1 Tainted: G W O
+[ 52.642241] c1 -------------------------------------------------------
+[ 52.642245] c1 usb ffs open/2808 is trying to acquire lock:
+[ 52.642270] c0 (udc_lock){+.+.+.}, at: [<ffffffc00065aeec>]
+ usb_gadget_unregister_driver+0x3c/0xc8
+[ 52.642272] c1 but task is already holding lock:
+[ 52.642283] c0 (ffs_lock){+.+.+.}, at: [<ffffffc00066b244>]
+ ffs_data_clear+0x30/0x140
+[ 52.642285] c1 which lock already depends on the new lock.
+[ 52.642287] c1
+ the existing dependency chain (in reverse order) is:
+[ 52.642295] c0
+ -> #1 (ffs_lock){+.+.+.}:
+[ 52.642307] c0 [<ffffffc00012340c>] __lock_acquire+0x20f0/0x2238
+[ 52.642314] c0 [<ffffffc000123b54>] lock_acquire+0xe4/0x298
+[ 52.642322] c0 [<ffffffc000aaf6e8>] mutex_lock_nested+0x7c/0x3cc
+[ 52.642328] c0 [<ffffffc00066f7bc>] ffs_func_bind+0x504/0x6e8
+[ 52.642334] c0 [<ffffffc000654004>] usb_add_function+0x84/0x184
+[ 52.642340] c0 [<ffffffc000658ca4>] configfs_composite_bind+0x264/0x39c
+[ 52.642346] c0 [<ffffffc00065b348>] udc_bind_to_driver+0x58/0x11c
+[ 52.642352] c0 [<ffffffc00065b49c>] usb_udc_attach_driver+0x90/0xc8
+[ 52.642358] c0 [<ffffffc0006598e0>] gadget_dev_desc_UDC_store+0xd4/0x128
+[ 52.642369] c0 [<ffffffc0002c14e8>] configfs_write_file+0xd0/0x13c
+[ 52.642376] c0 [<ffffffc00023c054>] vfs_write+0xb8/0x214
+[ 52.642381] c0 [<ffffffc00023cad4>] SyS_write+0x54/0xb0
+[ 52.642388] c0 [<ffffffc000085ff0>] el0_svc_naked+0x24/0x28
+[ 52.642395] c0
+ -> #0 (udc_lock){+.+.+.}:
+[ 52.642401] c0 [<ffffffc00011e3d0>] print_circular_bug+0x84/0x2e4
+[ 52.642407] c0 [<ffffffc000123454>] __lock_acquire+0x2138/0x2238
+[ 52.642412] c0 [<ffffffc000123b54>] lock_acquire+0xe4/0x298
+[ 52.642420] c0 [<ffffffc000aaf6e8>] mutex_lock_nested+0x7c/0x3cc
+[ 52.642427] c0 [<ffffffc00065aeec>] usb_gadget_unregister_driver+0x3c/0xc8
+[ 52.642432] c0 [<ffffffc00065995c>] unregister_gadget_item+0x28/0x44
+[ 52.642439] c0 [<ffffffc00066b34c>] ffs_data_clear+0x138/0x140
+[ 52.642444] c0 [<ffffffc00066b374>] ffs_data_reset+0x20/0x6c
+[ 52.642450] c0 [<ffffffc00066efd0>] ffs_data_closed+0xac/0x12c
+[ 52.642454] c0 [<ffffffc00066f070>] ffs_ep0_release+0x20/0x2c
+[ 52.642460] c0 [<ffffffc00023dbe4>] __fput+0xb0/0x1f4
+[ 52.642466] c0 [<ffffffc00023dd9c>] ____fput+0x20/0x2c
+[ 52.642473] c0 [<ffffffc0000ee944>] task_work_run+0xb4/0xe8
+[ 52.642482] c0 [<ffffffc0000cd45c>] do_exit+0x360/0xb9c
+[ 52.642487] c0 [<ffffffc0000cf228>] do_group_exit+0x4c/0xb0
+[ 52.642494] c0 [<ffffffc0000dd3c8>] get_signal+0x380/0x89c
+[ 52.642501] c0 [<ffffffc00008a8f0>] do_signal+0x154/0x518
+[ 52.642507] c0 [<ffffffc00008af00>] do_notify_resume+0x70/0x78
+[ 52.642512] c0 [<ffffffc000085ee8>] work_pending+0x1c/0x20
+[ 52.642514] c1
+ other info that might help us debug this:
+[ 52.642517] c1 Possible unsafe locking scenario:
+[ 52.642518] c1 CPU0 CPU1
+[ 52.642520] c1 ---- ----
+[ 52.642525] c0 lock(ffs_lock);
+[ 52.642529] c0 lock(udc_lock);
+[ 52.642533] c0 lock(ffs_lock);
+[ 52.642537] c0 lock(udc_lock);
+[ 52.642539] c1
+ *** DEADLOCK ***
+[ 52.642543] c1 1 lock held by usb ffs open/2808:
+[ 52.642555] c0 #0: (ffs_lock){+.+.+.}, at: [<ffffffc00066b244>]
+ ffs_data_clear+0x30/0x140
+[ 52.642557] c1 stack backtrace:
+[ 52.642563] c1 CPU: 1 PID: 2808 Comm: usb ffs open Tainted: G
+[ 52.642565] c1 Hardware name: Spreadtrum SP9860g Board (DT)
+[ 52.642568] c1 Call trace:
+[ 52.642573] c1 [<ffffffc00008b430>] dump_backtrace+0x0/0x170
+[ 52.642577] c1 [<ffffffc00008b5c0>] show_stack+0x20/0x28
+[ 52.642583] c1 [<ffffffc000422694>] dump_stack+0xa8/0xe0
+[ 52.642587] c1 [<ffffffc00011e548>] print_circular_bug+0x1fc/0x2e4
+[ 52.642591] c1 [<ffffffc000123454>] __lock_acquire+0x2138/0x2238
+[ 52.642595] c1 [<ffffffc000123b54>] lock_acquire+0xe4/0x298
+[ 52.642599] c1 [<ffffffc000aaf6e8>] mutex_lock_nested+0x7c/0x3cc
+[ 52.642604] c1 [<ffffffc00065aeec>] usb_gadget_unregister_driver+0x3c/0xc8
+[ 52.642608] c1 [<ffffffc00065995c>] unregister_gadget_item+0x28/0x44
+[ 52.642613] c1 [<ffffffc00066b34c>] ffs_data_clear+0x138/0x140
+[ 52.642618] c1 [<ffffffc00066b374>] ffs_data_reset+0x20/0x6c
+[ 52.642621] c1 [<ffffffc00066efd0>] ffs_data_closed+0xac/0x12c
+[ 52.642625] c1 [<ffffffc00066f070>] ffs_ep0_release+0x20/0x2c
+[ 52.642629] c1 [<ffffffc00023dbe4>] __fput+0xb0/0x1f4
+[ 52.642633] c1 [<ffffffc00023dd9c>] ____fput+0x20/0x2c
+[ 52.642636] c1 [<ffffffc0000ee944>] task_work_run+0xb4/0xe8
+[ 52.642640] c1 [<ffffffc0000cd45c>] do_exit+0x360/0xb9c
+[ 52.642644] c1 [<ffffffc0000cf228>] do_group_exit+0x4c/0xb0
+[ 52.642647] c1 [<ffffffc0000dd3c8>] get_signal+0x380/0x89c
+[ 52.642651] c1 [<ffffffc00008a8f0>] do_signal+0x154/0x518
+[ 52.642656] c1 [<ffffffc00008af00>] do_notify_resume+0x70/0x78
+[ 52.642659] c1 [<ffffffc000085ee8>] work_pending+0x1c/0x20
+
+Acked-by: Michal Nazarewicz <mina86@mina86.com>
+Signed-off-by: Baolin Wang <baolin.wang@linaro.org>
+Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
+Cc: Jerry Zhang <zhangjerry@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ drivers/usb/gadget/function/f_fs.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -3688,6 +3688,7 @@ static void ffs_closed(struct ffs_data *
+ {
+ struct ffs_dev *ffs_obj;
+ struct f_fs_opts *opts;
++ struct config_item *ci;
+
+ ENTER();
+ ffs_dev_lock();
+@@ -3711,8 +3712,11 @@ static void ffs_closed(struct ffs_data *
+ || !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount))
+ goto done;
+
+- unregister_gadget_item(ffs_obj->opts->
+- func_inst.group.cg_item.ci_parent->ci_parent);
++ ci = opts->func_inst.group.cg_item.ci_parent->ci_parent;
++ ffs_dev_unlock();
++
++ unregister_gadget_item(ci);
++ return;
+ done:
+ ffs_dev_unlock();
+ }