From patchwork Mon Jun 26 15:47:17 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Amit Pundir X-Patchwork-Id: 106352 Delivered-To: patch@linaro.org Received: by 10.140.101.48 with SMTP id t45csp161306qge; Mon, 26 Jun 2017 08:47:42 -0700 (PDT) X-Received: by 10.99.168.67 with SMTP id i3mr743946pgp.23.1498492062462; Mon, 26 Jun 2017 08:47:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1498492062; cv=none; d=google.com; s=arc-20160816; b=frOJ+nPAmC7rxv8XaLEZ2WhRynb9RXKGvToZt8tt4lpWsSOv+I6JFStHp04chtpJs/ mFjvDmA5xUliChfbjRAsNLfgQ53BDWv48xivBEwxJu0UmZmAMki/Y30CYwkDT61C23hS tSjv4M+Q6E2sSu+cHGMbYecJ8D8fQXUqlUgMLJEkM4gv3RXIHTWqvTTCM2MCczGOe0dg 9rLeqKHmx4/xgJnYaotVGTFj2oqfxkG35QQw6d+Wawjm8XRarzXjq/WtGpUUPoA390f1 98z0Gz1YqZiAoqMXTrIVKLlN3dEGwhwxHKTTiXRxKMpiVx0P53Ih8TfC2f2Xj1J6audR 5BCA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dkim-signature:arc-authentication-results; bh=bKOziGCW5wbW58HOoYQrV0JbgGc4QB8YkDuYYfcTcRc=; b=ycotIKQ9ZdywXxxTFETgD6JCYrYeheAmAf9crPqfiATF6ZNqAL4dxtzAXibA41nwi0 FGZWzxYqwm/jt4i5eZGY8yx9+WztwbSdA3S5M/uZHxjQd7uTdRBSHghuGeJlpaBbNaZw DPjM37dsdif4gNF82mTeNOAtbn/Py6/XcMUMuO6kOpaLKI9utKribnM+BCQKYbaDdACb W2IK4DsJtlSEvj8sNNhAoCTuDa+UV659fbvTNShSW4Z+ZzqBy4fxLwjjlxyfjYf0eOqn QW2hrzBQzCelk1huomp741sGXPBYq65emaRNVLjlGgziaV0egMKULzxB5d7CJqSxmN6h mfYg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@linaro.org header.b=CF1wvNH9; spf=pass (google.com: best guess record for domain of stable-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=stable-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id 63si282868plf.50.2017.06.26.08.47.42; Mon, 26 Jun 2017 08:47:42 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of stable-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.b=CF1wvNH9; spf=pass (google.com: best guess record for domain of stable-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=stable-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751976AbdFZPrk (ORCPT + 6 others); Mon, 26 Jun 2017 11:47:40 -0400 Received: from mail-pf0-f181.google.com ([209.85.192.181]:32818 "EHLO mail-pf0-f181.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751473AbdFZPrj (ORCPT ); Mon, 26 Jun 2017 11:47:39 -0400 Received: by mail-pf0-f181.google.com with SMTP id e7so2269758pfk.0 for ; Mon, 26 Jun 2017 08:47:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=bKOziGCW5wbW58HOoYQrV0JbgGc4QB8YkDuYYfcTcRc=; b=CF1wvNH9IKXOQg459BEcszfF+J8GDVR61hKZHIfLbScIu+m0mm+4Z9vlEoXcKdFPY/ i7HzzT4U4iSr2lJT1RadUJYu1C8Fsf+R2WwTsZwh6LJGtCv432NrW73gxAmWG1MChFs9 Bj0k6XfmvG5HmonK92bkwgnKld6jh+fUGHNfg= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=bKOziGCW5wbW58HOoYQrV0JbgGc4QB8YkDuYYfcTcRc=; b=ZUq05p0cqintRFGaW5vvjkPpUDZ/vgbjCPLaU625B2gVbXiW4sJWWb63EFAaF7kicj wzb+C7Xo2PzTurRgTQvYhl0OuP1/mUMPwRl6ReA7FpwWIIY+aJUpxmLJns94zqmi1bBM reA7ksb6TxXg6QI8US8buD+SDU2Fdb3KrcMp2cClfLBWW3RjuhXKNr6wGmzaEpj1WlON yuloWsGqLHwxhmXrmDpMBm/QXUEjY3G9mUKaN6EyHJX0dAv2Sn5tQCR88xv4g4s0nAo2 RR5BMlJXcfgZvLPFRdPuMWa9mG9DzRwgQ2W3gkMiV0WOxvnnKLIXQNdYzzLyhO67tZAj 1ULQ== X-Gm-Message-State: AKS2vOyPr+mpIfEtgACfsnS+Q0XGwRoOZKnDekivvZHJZRQYcobI9Hc7 GgcXcEZVkghQkCo3c1cjzA== X-Received: by 10.98.156.131 with SMTP id u3mr807436pfk.16.1498492053921; Mon, 26 Jun 2017 08:47:33 -0700 (PDT) Received: from localhost.localdomain ([106.51.139.251]) by smtp.gmail.com with ESMTPSA id s9sm829854pfe.21.2017.06.26.08.47.31 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Mon, 26 Jun 2017 08:47:32 -0700 (PDT) From: Amit Pundir To: Greg KH , Guillaume Nault , "David S . Miller" Cc: Stable Subject: [PATCH for-4.9 3/5] l2tp: fix duplicate session creation Date: Mon, 26 Jun 2017 21:17:17 +0530 Message-Id: <1498492039-26905-4-git-send-email-amit.pundir@linaro.org> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1498492039-26905-1-git-send-email-amit.pundir@linaro.org> References: <1498492039-26905-1-git-send-email-amit.pundir@linaro.org> Sender: stable-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: stable@vger.kernel.org From: Guillaume Nault commit dbdbc73b44782e22b3b4b6e8b51e7a3d245f3086 upstream. l2tp_session_create() relies on its caller for checking for duplicate sessions. This is racy since a session can be concurrently inserted after the caller's verification. Fix this by letting l2tp_session_create() verify sessions uniqueness upon insertion. Callers need to be adapted to check for l2tp_session_create()'s return code instead of calling l2tp_session_find(). pppol2tp_connect() is a bit special because it has to work on existing sessions (if they're not connected) or to create a new session if none is found. When acting on a preexisting session, a reference must be held or it could go away on us. So we have to use l2tp_session_get() instead of l2tp_session_find() and drop the reference before exiting. Fixes: d9e31d17ceba ("l2tp: Add L2TP ethernet pseudowire support") Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts") Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller Signed-off-by: Amit Pundir --- 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(-) -- 2.7.4 diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 046a5ba0ebd2..f29911ab3b80 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -378,6 +378,48 @@ struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname) } 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_header_len); 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(int priv_size, struct l2tp_tunnel *tunn 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(int priv_size, struct l2tp_tunnel *tunn /* 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); diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 965f7e344cef..eecc64e138de 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -223,12 +223,6 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p 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 *net, u32 tunnel_id, u32 session_id, u32 p 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; } diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index c1c9a9e08d08..1696f1fd5877 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -583,6 +583,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, 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 socket *sock, struct sockaddr *uservaddr, 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 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, 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(struct net *net, u32 tunnel_id, u32 session_i 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(struct net *net, u32 tunnel_id, u32 session_i 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;