Received: by 2002:ad5:474a:0:0:0:0:0 with SMTP id i10csp3335566imu; Sun, 11 Nov 2018 12:38:24 -0800 (PST) X-Google-Smtp-Source: AJdET5f7jOfYSRrtM1lVygJ2oUOjZl0qV9tXiGFyqmAw8KlwB2PcXlNdWWgRyOtiTqeR69qGqSaB X-Received: by 2002:a63:ea07:: with SMTP id c7-v6mr14422548pgi.361.1541968704279; Sun, 11 Nov 2018 12:38:24 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1541968704; cv=none; d=google.com; s=arc-20160816; b=DsP13oGzGmtfzsQE/QWnqB9/L/reuSir4Op5Aux4v4qKSNKdS8wvDD+s3yUGNDPslY m/nH05c1Ew31im0F1QPUG4P6o/dC505YzfSKKJ2/eXB9E2wMCzdVA7FbbCzuB1b6u7rm HAKLYZt6CHiNnYER82cOAhbxPG91SvrBbex1PDFFmaFV+gdQhlCwngUuVuIq3ITDBErd JBnyk+QQpcdw3iNG9rVWVCbVj8OCadKaOXvSlAz14ifUfOMKDN0JkRf7/VOfu4nOWPBm DB/7/nO2AV5UvDQAj0aTJj/6bmDkasL2FA7tEfvT/hupmfXtnW4m+U1dqekDytBUJGzZ R1Yw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:in-reply-to:subject:message-id:date:cc:to :from:mime-version:content-transfer-encoding:content-disposition; bh=YejX+HR63N7UeqqPopNGoojMCv3EHruOvzwyKxtU1YQ=; b=VkapWTGBp3dNyNHhFp6WvdfuNoC5gwAJXFCil5wQ/b+E3mqB3TzSt5upBFlcs5lCFq QMV966WYQkITvIvLzUyhsGdmkquNunVQKtPhHJ6jAmpgmDQf7MYDx3XfjQ9qUt5iQ3Lc XlbpUMTZ356V7pgheDr8qgMQfV+hYxx9DRMIoHfbkH7+9N8FoQPhCnZtSS6dq+XsYtXL PtqQ2SEMkfk0vbIJBK9M7R64i1KQljF6AScYpkZ+6L5NOnKFwjx9YYjFx7T0fIM7vl9e oNKcvTRzX112vZqCSBYaB6OV1QHIUvD30MaGEPNHz54+hMR6CQRe+av34U6pCxholNDH XV1w== 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 y35-v6si14051755pgl.14.2018.11.11.12.38.09; Sun, 11 Nov 2018 12:38:24 -0800 (PST) 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 S1730534AbeKLG1J (ORCPT + 99 others); Mon, 12 Nov 2018 01:27:09 -0500 Received: from shadbolt.e.decadent.org.uk ([88.96.1.126]:49672 "EHLO shadbolt.e.decadent.org.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730062AbeKLFsK (ORCPT ); Mon, 12 Nov 2018 00:48:10 -0500 Received: from [192.168.4.242] (helo=deadeye) by shadbolt.decadent.org.uk with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89) (envelope-from ) id 1gLvsV-0000oG-I7; Sun, 11 Nov 2018 19:58:39 +0000 Received: from ben by deadeye with local (Exim 4.91) (envelope-from ) id 1gLvsT-0001bS-BF; Sun, 11 Nov 2018 19:58:37 +0000 Content-Type: text/plain; charset="UTF-8" Content-Disposition: inline Content-Transfer-Encoding: 8bit MIME-Version: 1.0 From: Ben Hutchings To: linux-kernel@vger.kernel.org, stable@vger.kernel.org CC: akpm@linux-foundation.org, "Ross Lagerwall" , "Boris Ostrovsky" , "Juergen Gross" Date: Sun, 11 Nov 2018 19:49:05 +0000 Message-ID: X-Mailer: LinuxStableQueue (scripts by bwh) Subject: [PATCH 3.16 172/366] xen-netfront: Fix race between device setup and open In-Reply-To: X-SA-Exim-Connect-IP: 192.168.4.242 X-SA-Exim-Mail-From: ben@decadent.org.uk X-SA-Exim-Scanned: No (on shadbolt.decadent.org.uk); SAEximRunCond expanded to false Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 3.16.61-rc1 review patch. If anyone has any objections, please let me know. ------------------ From: Ross Lagerwall commit f599c64fdf7d9c108e8717fb04bc41c680120da4 upstream. When a netfront device is set up it registers a netdev fairly early on, before it has set up the queues and is actually usable. A userspace tool like NetworkManager will immediately try to open it and access its state as soon as it appears. The bug can be reproduced by hotplugging VIFs until the VM runs out of grant refs. It registers the netdev but fails to set up any queues (since there are no more grant refs). In the meantime, NetworkManager opens the device and the kernel crashes trying to access the queues (of which there are none). Fix this in two ways: * For initial setup, register the netdev much later, after the queues are setup. This avoids the race entirely. * During a suspend/resume cycle, the frontend reconnects to the backend and the queues are recreated. It is possible (though highly unlikely) to race with something opening the device and accessing the queues after they have been destroyed but before they have been recreated. Extend the region covered by the rtnl semaphore to protect against this race. There is a possibility that we fail to recreate the queues so check for this in the open function. Signed-off-by: Ross Lagerwall Reviewed-by: Boris Ostrovsky Signed-off-by: Juergen Gross Signed-off-by: Ben Hutchings --- drivers/net/xen-netfront.c | 46 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 22 deletions(-) --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -369,6 +369,9 @@ static int xennet_open(struct net_device unsigned int i = 0; struct netfront_queue *queue = NULL; + if (!np->queues) + return -ENODEV; + for (i = 0; i < num_queues; ++i) { queue = &np->queues[i]; napi_enable(&queue->napi); @@ -1394,18 +1397,8 @@ static int netfront_probe(struct xenbus_ #ifdef CONFIG_SYSFS info->netdev->sysfs_groups[0] = &xennet_dev_group; #endif - err = register_netdev(info->netdev); - if (err) { - pr_warn("%s: register_netdev err=%d\n", __func__, err); - goto fail; - } return 0; - - fail: - xennet_free_netdev(netdev); - dev_set_drvdata(&dev->dev, NULL); - return err; } static void xennet_end_access(int ref, void *page) @@ -1779,8 +1772,6 @@ static void xennet_destroy_queues(struct { unsigned int i; - rtnl_lock(); - for (i = 0; i < info->netdev->real_num_tx_queues; i++) { struct netfront_queue *queue = &info->queues[i]; @@ -1789,8 +1780,6 @@ static void xennet_destroy_queues(struct netif_napi_del(&queue->napi); } - rtnl_unlock(); - kfree(info->queues); info->queues = NULL; } @@ -1806,8 +1795,6 @@ static int xennet_create_queues(struct n if (!info->queues) return -ENOMEM; - rtnl_lock(); - for (i = 0; i < *num_queues; i++) { struct netfront_queue *queue = &info->queues[i]; @@ -1816,7 +1803,7 @@ static int xennet_create_queues(struct n ret = xennet_init_queue(queue); if (ret < 0) { - dev_warn(&info->netdev->dev, + dev_warn(&info->xbdev->dev, "only created %d queues\n", i); *num_queues = i; break; @@ -1830,10 +1817,8 @@ static int xennet_create_queues(struct n netif_set_real_num_tx_queues(info->netdev, *num_queues); - rtnl_unlock(); - if (*num_queues == 0) { - dev_err(&info->netdev->dev, "no queues\n"); + dev_err(&info->xbdev->dev, "no queues\n"); return -EINVAL; } return 0; @@ -1875,6 +1860,7 @@ static int talk_to_netback(struct xenbus goto out; } + rtnl_lock(); if (info->queues) xennet_destroy_queues(info); @@ -1885,6 +1871,7 @@ static int talk_to_netback(struct xenbus info->queues = NULL; goto out; } + rtnl_unlock(); /* Create shared ring, alloc event channel -- for each queue */ for (i = 0; i < num_queues; ++i) { @@ -1978,8 +1965,10 @@ abort_transaction_no_dev_fatal: xenbus_transaction_end(xbt, 1); destroy_ring: xennet_disconnect_backend(info); + rtnl_lock(); xennet_destroy_queues(info); out: + rtnl_unlock(); device_unregister(&dev->dev); return err; } @@ -2015,6 +2004,15 @@ static int xennet_connect(struct net_dev netdev_update_features(dev); rtnl_unlock(); + if (dev->reg_state == NETREG_UNINITIALIZED) { + err = register_netdev(dev); + if (err) { + pr_warn("%s: register_netdev err=%d\n", __func__, err); + device_unregister(&np->xbdev->dev); + return err; + } + } + /* * All public and private state should now be sane. Get * ready to start sending and receiving packets and give the driver @@ -2284,10 +2282,14 @@ static int xennet_remove(struct xenbus_d xennet_disconnect_backend(info); - unregister_netdev(info->netdev); + if (info->netdev->reg_state == NETREG_REGISTERED) + unregister_netdev(info->netdev); - if (info->queues) + if (info->queues) { + rtnl_lock(); xennet_destroy_queues(info); + rtnl_unlock(); + } xennet_free_netdev(info->netdev); return 0;