From: Greg Kroah-Hartman Date: Mon, 3 Jul 2017 08:51:50 +0000 (+0200) Subject: 4.9-stable patches X-Git-Tag: v3.18.60~36 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b42673de6ba26bd2effd37714f0fd6395462d04d;p=thirdparty%2Fkernel%2Fstable-queue.git 4.9-stable patches added patches: l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch l2tp-fix-duplicate-session-creation.patch l2tp-fix-race-in-l2tp_recv_common.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 usb-gadget-f_fs-fix-possibe-deadlock.patch --- diff --git a/queue-4.9/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch b/queue-4.9/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch new file mode 100644 index 00000000000..d02345b30f4 --- /dev/null +++ b/queue-4.9/l2tp-ensure-session-can-t-get-removed-during-pppol2tp_session_ioctl.patch @@ -0,0 +1,49 @@ +From 57377d63547861919ee634b845c7caa38de4a452 Mon Sep 17 00:00:00 2001 +From: Guillaume Nault +Date: Fri, 31 Mar 2017 13:02:26 +0200 +Subject: l2tp: ensure session can't get removed during pppol2tp_session_ioctl() + +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: Amit Pundir +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 +@@ -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 diff --git a/queue-4.9/l2tp-fix-duplicate-session-creation.patch b/queue-4.9/l2tp-fix-duplicate-session-creation.patch new file mode 100644 index 00000000000..0e63307f427 --- /dev/null +++ b/queue-4.9/l2tp-fix-duplicate-session-creation.patch @@ -0,0 +1,276 @@ +From dbdbc73b44782e22b3b4b6e8b51e7a3d245f3086 Mon Sep 17 00:00:00 2001 +From: Guillaume Nault +Date: Fri, 31 Mar 2017 13:02:27 +0200 +Subject: l2tp: fix duplicate session creation + +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: Amit Pundir +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 +@@ -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; diff --git a/queue-4.9/l2tp-fix-race-in-l2tp_recv_common.patch b/queue-4.9/l2tp-fix-race-in-l2tp_recv_common.patch new file mode 100644 index 00000000000..f2c96ffb5f5 --- /dev/null +++ b/queue-4.9/l2tp-fix-race-in-l2tp_recv_common.patch @@ -0,0 +1,262 @@ +From 61b9a047729bb230978178bca6729689d0c50ca2 Mon Sep 17 00:00:00 2001 +From: Guillaume Nault +Date: Fri, 31 Mar 2017 13:02:25 +0200 +Subject: l2tp: fix race in l2tp_recv_common() + +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: Amit Pundir +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 +@@ -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); + diff --git a/queue-4.9/l2tp-hold-session-while-sending-creation-notifications.patch b/queue-4.9/l2tp-hold-session-while-sending-creation-notifications.patch new file mode 100644 index 00000000000..b2d288ce5f5 --- /dev/null +++ b/queue-4.9/l2tp-hold-session-while-sending-creation-notifications.patch @@ -0,0 +1,42 @@ +From 5e6a9e5a3554a5b3db09cdc22253af1849c65dff Mon Sep 17 00:00:00 2001 +From: Guillaume Nault +Date: Fri, 31 Mar 2017 13:02:29 +0200 +Subject: l2tp: hold session while sending creation notifications + +From: Guillaume Nault + +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 +Signed-off-by: David S. Miller +Signed-off-by: Amit Pundir +Signed-off-by: Greg Kroah-Hartman + +--- + 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: diff --git a/queue-4.9/l2tp-take-a-reference-on-sessions-used-in-genetlink-handlers.patch b/queue-4.9/l2tp-take-a-reference-on-sessions-used-in-genetlink-handlers.patch new file mode 100644 index 00000000000..3b1180ce588 --- /dev/null +++ b/queue-4.9/l2tp-take-a-reference-on-sessions-used-in-genetlink-handlers.patch @@ -0,0 +1,196 @@ +From 2777e2ab5a9cf2b4524486c6db1517a6ded25261 Mon Sep 17 00:00:00 2001 +From: Guillaume Nault +Date: Fri, 31 Mar 2017 13:02:30 +0200 +Subject: l2tp: take a reference on sessions used in genetlink handlers + +From: Guillaume Nault + +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 +Signed-off-by: David S. Miller +Signed-off-by: Amit Pundir +Signed-off-by: Greg Kroah-Hartman +--- + 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; + } + diff --git a/queue-4.9/mm-numa-avoid-waiting-on-freed-migrated-pages.patch b/queue-4.9/mm-numa-avoid-waiting-on-freed-migrated-pages.patch new file mode 100644 index 00000000000..caa7c9bae4f --- /dev/null +++ b/queue-4.9/mm-numa-avoid-waiting-on-freed-migrated-pages.patch @@ -0,0 +1,89 @@ +From 3c226c637b69104f6b9f1c6ec5b08d7b741b3229 Mon Sep 17 00:00:00 2001 +From: Mark Rutland +Date: Fri, 16 Jun 2017 14:02:34 -0700 +Subject: mm: numa: avoid waiting on freed migrated pages + +From: Mark Rutland + +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 +Signed-off-by: Will Deacon +Acked-by: Steve Capper +Acked-by: Kirill A. Shutemov +Acked-by: Vlastimil Babka +Cc: Mel Gorman +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + 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; + } diff --git a/queue-4.9/series b/queue-4.9/series index de28094b82e..fcf64b99e49 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -41,3 +41,10 @@ gpiolib-fix-filtering-out-unwanted-events.patch 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 diff --git a/queue-4.9/usb-gadget-f_fs-fix-possibe-deadlock.patch b/queue-4.9/usb-gadget-f_fs-fix-possibe-deadlock.patch new file mode 100644 index 00000000000..008a34daa6f --- /dev/null +++ b/queue-4.9/usb-gadget-f_fs-fix-possibe-deadlock.patch @@ -0,0 +1,138 @@ +From b3ce3ce02d146841af012d08506b4071db8ffde3 Mon Sep 17 00:00:00 2001 +From: Baolin Wang +Date: Thu, 8 Dec 2016 19:55:22 +0800 +Subject: usb: gadget: f_fs: Fix possibe deadlock + +From: Baolin Wang + +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: [] + usb_gadget_unregister_driver+0x3c/0xc8 +[ 52.642272] c1 but task is already holding lock: +[ 52.642283] c0 (ffs_lock){+.+.+.}, at: [] + 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 [] __lock_acquire+0x20f0/0x2238 +[ 52.642314] c0 [] lock_acquire+0xe4/0x298 +[ 52.642322] c0 [] mutex_lock_nested+0x7c/0x3cc +[ 52.642328] c0 [] ffs_func_bind+0x504/0x6e8 +[ 52.642334] c0 [] usb_add_function+0x84/0x184 +[ 52.642340] c0 [] configfs_composite_bind+0x264/0x39c +[ 52.642346] c0 [] udc_bind_to_driver+0x58/0x11c +[ 52.642352] c0 [] usb_udc_attach_driver+0x90/0xc8 +[ 52.642358] c0 [] gadget_dev_desc_UDC_store+0xd4/0x128 +[ 52.642369] c0 [] configfs_write_file+0xd0/0x13c +[ 52.642376] c0 [] vfs_write+0xb8/0x214 +[ 52.642381] c0 [] SyS_write+0x54/0xb0 +[ 52.642388] c0 [] el0_svc_naked+0x24/0x28 +[ 52.642395] c0 + -> #0 (udc_lock){+.+.+.}: +[ 52.642401] c0 [] print_circular_bug+0x84/0x2e4 +[ 52.642407] c0 [] __lock_acquire+0x2138/0x2238 +[ 52.642412] c0 [] lock_acquire+0xe4/0x298 +[ 52.642420] c0 [] mutex_lock_nested+0x7c/0x3cc +[ 52.642427] c0 [] usb_gadget_unregister_driver+0x3c/0xc8 +[ 52.642432] c0 [] unregister_gadget_item+0x28/0x44 +[ 52.642439] c0 [] ffs_data_clear+0x138/0x140 +[ 52.642444] c0 [] ffs_data_reset+0x20/0x6c +[ 52.642450] c0 [] ffs_data_closed+0xac/0x12c +[ 52.642454] c0 [] ffs_ep0_release+0x20/0x2c +[ 52.642460] c0 [] __fput+0xb0/0x1f4 +[ 52.642466] c0 [] ____fput+0x20/0x2c +[ 52.642473] c0 [] task_work_run+0xb4/0xe8 +[ 52.642482] c0 [] do_exit+0x360/0xb9c +[ 52.642487] c0 [] do_group_exit+0x4c/0xb0 +[ 52.642494] c0 [] get_signal+0x380/0x89c +[ 52.642501] c0 [] do_signal+0x154/0x518 +[ 52.642507] c0 [] do_notify_resume+0x70/0x78 +[ 52.642512] c0 [] 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: [] + 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 [] dump_backtrace+0x0/0x170 +[ 52.642577] c1 [] show_stack+0x20/0x28 +[ 52.642583] c1 [] dump_stack+0xa8/0xe0 +[ 52.642587] c1 [] print_circular_bug+0x1fc/0x2e4 +[ 52.642591] c1 [] __lock_acquire+0x2138/0x2238 +[ 52.642595] c1 [] lock_acquire+0xe4/0x298 +[ 52.642599] c1 [] mutex_lock_nested+0x7c/0x3cc +[ 52.642604] c1 [] usb_gadget_unregister_driver+0x3c/0xc8 +[ 52.642608] c1 [] unregister_gadget_item+0x28/0x44 +[ 52.642613] c1 [] ffs_data_clear+0x138/0x140 +[ 52.642618] c1 [] ffs_data_reset+0x20/0x6c +[ 52.642621] c1 [] ffs_data_closed+0xac/0x12c +[ 52.642625] c1 [] ffs_ep0_release+0x20/0x2c +[ 52.642629] c1 [] __fput+0xb0/0x1f4 +[ 52.642633] c1 [] ____fput+0x20/0x2c +[ 52.642636] c1 [] task_work_run+0xb4/0xe8 +[ 52.642640] c1 [] do_exit+0x360/0xb9c +[ 52.642644] c1 [] do_group_exit+0x4c/0xb0 +[ 52.642647] c1 [] get_signal+0x380/0x89c +[ 52.642651] c1 [] do_signal+0x154/0x518 +[ 52.642656] c1 [] do_notify_resume+0x70/0x78 +[ 52.642659] c1 [] work_pending+0x1c/0x20 + +Acked-by: Michal Nazarewicz +Signed-off-by: Baolin Wang +Signed-off-by: Felipe Balbi +Cc: Jerry Zhang +Signed-off-by: Greg Kroah-Hartman + +--- + 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(); + }