Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp3716665imm; Mon, 18 Jun 2018 02:53:21 -0700 (PDT) X-Google-Smtp-Source: ADUXVKJK377xBzrpvCd01J298eo2ODTMN+Cy/UexuCUIgag1b9rjh/A8ti6paryUEkmC94fueWV9 X-Received: by 2002:a65:578c:: with SMTP id b12-v6mr10545084pgr.315.1529315601929; Mon, 18 Jun 2018 02:53:21 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1529315601; cv=none; d=google.com; s=arc-20160816; b=dS3iBkVW/FJd0FHq1JFnEm2VvhXOc5FrCIWmBv0svbUVvoNBM7/8DmVXl/N+Z7OrEz DZ2URYL4GvUfROQXEheWOs7F0jfkbKVI5PNDNFH+PhDaBFglg/GSbuBzaY3E6HkntK0P KSpegK+bHWnUIJSqbFjjGqI5eaQqE1230M4Tiez7qOHqsYV9gnbVVk/9WE84hsi5eu9b zcBTTGklcN2t+eFzBbCAcRTor3Q3ibKk88weqXzms6k+QF044+sTiMcPQXJE8WDcKNOD FJNGBmHrJWTNtUnTOdAlwADqRoJ5JD0E2tiXrAY3oS9KPxq8ByLKpaKIo2cOwfY31gmP HYcw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:user-agent:references :in-reply-to:message-id:date:subject:cc:to:from :arc-authentication-results; bh=yEBt/pZ2e2Ob1mNWp8NX9Lwcxd8CDYuNDvJm/TpOjTI=; b=yoi9/t0uioZC/dEpIvPViLZ83xCp0RpBQo5C5g/FYd+24G5mB/RDfkDEPwTSuXQTos zl37Hnt+MtBUm+cdMhUcu9JXSCkjGxsWiCHOxg3584wxeRkfNuisbTiYzMnhX8foPNmP ZondMcvcaCN7bbTJ5Nho2t749IbMEI+BsPQ8zjUmEWxXxCW4hpoG9g8wHBYmajCqHAsM l9iPi4PW322iK2uoJ5+P1FoGD60oxipQ8+WuYkkOxKhBGxzQt7q/4a+0eEIVnJd+JwsM C0f0e5D/l4ae0WXVkzWA7nY1ABd1wvJVx5qFNZp7bag6o6xpRLkTG3oMpUoQswzZ23Mw 6lPQ== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id s136-v6si14735822pfc.222.2018.06.18.02.53.08; Mon, 18 Jun 2018 02:53:21 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S965393AbeFRJv4 (ORCPT + 99 others); Mon, 18 Jun 2018 05:51:56 -0400 Received: from mail.linuxfoundation.org ([140.211.169.12]:55244 "EHLO mail.linuxfoundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S935757AbeFRIVJ (ORCPT ); Mon, 18 Jun 2018 04:21:09 -0400 Received: from localhost (LFbn-1-12247-202.w90-92.abo.wanadoo.fr [90.92.61.202]) by mail.linuxfoundation.org (Postfix) with ESMTPSA id EA03EC7A; Mon, 18 Jun 2018 08:21:08 +0000 (UTC) From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Parav Pandit , Leon Romanovsky , Doug Ledford , Sasha Levin Subject: [PATCH 4.16 129/279] RDMA/cma: Fix use after destroy access to net namespace for IPoIB Date: Mon, 18 Jun 2018 10:11:54 +0200 Message-Id: <20180618080614.175021940@linuxfoundation.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20180618080608.851973560@linuxfoundation.org> References: <20180618080608.851973560@linuxfoundation.org> User-Agent: quilt/0.65 X-stable: review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 4.16-stable review patch. If anyone has any objections, please let me know. ------------------ From: Parav Pandit [ Upstream commit 2918c1a900252b4a0c730715ec205437c7daf79d ] There are few issues with validation of netdevice and listen id lookup for IB (IPoIB) while processing incoming CM request as below. 1. While performing lookup of bind_list in cma_ps_find(), net namespace of the netdevice can get deleted in cma_exit_net(), resulting in use after free access of idr and/or net namespace structures. This lookup occurs from the workqueue context (and not userspace context where net namespace is always valid). CPU0 CPU1 ==== ==== bind_list = cma_ps_find(); move netdevice to new namespace delete net namespace cma_exit_net() idr_destroy(idr); [..] cma_find_listener(bind_list, ..); 2. While netdevice is validated for IP address in given net namespace, netdevice's net namespace and/or ifindex can change in cma_get_net_dev() and cma_match_net_dev(). Above issues are overcome by using rcu lock along with netdevice UP/DOWN state as described below. When a net namespace is getting deleted, netdevice is closed and shutdown before moving it back to init_net namespace. change_net_namespace() synchronizes with any existing use of netdevice before changing the netdev properties such as net or ifindex. Once netdevice IFF_UP flags is cleared, such fields are not guaranteed to be valid. Therefore, rcu lock along with netdevice state check ensures that, while route lookup and cm_id lookup is in progress, netdevice of interest won't migrate to any other net namespace. This ensures that associated net namespace of netdevice won't get deleted while rcu lock is held for netdevice which is in IFF_UP state. Fixes: fa20105e09e9 ("IB/cma: Add support for network namespaces") Fixes: 4be74b42a6d0 ("IB/cma: Separate port allocation to network namespaces") Fixes: f887f2ac87c2 ("IB/cma: Validate routing of incoming requests") Signed-off-by: Parav Pandit Signed-off-by: Leon Romanovsky Signed-off-by: Doug Ledford Signed-off-by: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/core/cma.c | 53 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -420,6 +420,8 @@ struct cma_hdr { #define CMA_VERSION 0x00 struct cma_req_info { + struct sockaddr_storage listen_addr_storage; + struct sockaddr_storage src_addr_storage; struct ib_device *device; int port; union ib_gid local_gid; @@ -1373,11 +1375,11 @@ static bool validate_net_dev(struct net_ } static struct net_device *cma_get_net_dev(struct ib_cm_event *ib_event, - const struct cma_req_info *req) + struct cma_req_info *req) { - struct sockaddr_storage listen_addr_storage, src_addr_storage; - struct sockaddr *listen_addr = (struct sockaddr *)&listen_addr_storage, - *src_addr = (struct sockaddr *)&src_addr_storage; + struct sockaddr *listen_addr = + (struct sockaddr *)&req->listen_addr_storage; + struct sockaddr *src_addr = (struct sockaddr *)&req->src_addr_storage; struct net_device *net_dev; const union ib_gid *gid = req->has_gid ? &req->local_gid : NULL; int err; @@ -1392,11 +1394,6 @@ static struct net_device *cma_get_net_de if (!net_dev) return ERR_PTR(-ENODEV); - if (!validate_net_dev(net_dev, listen_addr, src_addr)) { - dev_put(net_dev); - return ERR_PTR(-EHOSTUNREACH); - } - return net_dev; } @@ -1532,15 +1529,51 @@ static struct rdma_id_private *cma_id_fr } } + /* + * Net namespace might be getting deleted while route lookup, + * cm_id lookup is in progress. Therefore, perform netdevice + * validation, cm_id lookup under rcu lock. + * RCU lock along with netdevice state check, synchronizes with + * netdevice migrating to different net namespace and also avoids + * case where net namespace doesn't get deleted while lookup is in + * progress. + * If the device state is not IFF_UP, its properties such as ifindex + * and nd_net cannot be trusted to remain valid without rcu lock. + * net/core/dev.c change_net_namespace() ensures to synchronize with + * ongoing operations on net device after device is closed using + * synchronize_net(). + */ + rcu_read_lock(); + if (*net_dev) { + /* + * If netdevice is down, it is likely that it is administratively + * down or it might be migrating to different namespace. + * In that case avoid further processing, as the net namespace + * or ifindex may change. + */ + if (((*net_dev)->flags & IFF_UP) == 0) { + id_priv = ERR_PTR(-EHOSTUNREACH); + goto err; + } + + if (!validate_net_dev(*net_dev, + (struct sockaddr *)&req.listen_addr_storage, + (struct sockaddr *)&req.src_addr_storage)) { + id_priv = ERR_PTR(-EHOSTUNREACH); + goto err; + } + } + bind_list = cma_ps_find(*net_dev ? dev_net(*net_dev) : &init_net, rdma_ps_from_service_id(req.service_id), cma_port_from_service_id(req.service_id)); id_priv = cma_find_listener(bind_list, cm_id, ib_event, &req, *net_dev); +err: + rcu_read_unlock(); if (IS_ERR(id_priv) && *net_dev) { dev_put(*net_dev); *net_dev = NULL; } - return id_priv; }