Received: by 10.223.185.111 with SMTP id b44csp49018wrg; Fri, 9 Mar 2018 00:36:31 -0800 (PST) X-Google-Smtp-Source: AG47ELvM8HwriJaZIOcHmTAaTnRknFIaqPyMeJsTkpoW3CLtELWetgkJ4l4uHhdxACQDSph6ZrGu X-Received: by 10.99.122.70 with SMTP id j6mr22166247pgn.17.1520584591822; Fri, 09 Mar 2018 00:36:31 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1520584591; cv=none; d=google.com; s=arc-20160816; b=LxYsvK2U+siUm7O570Pudi5YiY3foeKIbNMmc2uWobjeHd8JzpWRHIi1YA8yLEunqD NEaWHOxaBANsIngRHbJKnGEyMId0MHKV0zAMavo3RRi5AqySdmtZCEbFjfej7eP8eqFS Cbr0YMIpWOtR6hljkYiiLTRZJfaGgHalFXh0L2MwPBbyTt63AsOTFWrZnCoXq7Aqnuoi Nk8vOfeLmXL0bQBjNW/1GlPskT5YDDc+1IGKP7XUD0EReYJeNx66Tq1hflm3J1JT70hB QNWatVceMdPjJQJBBo5ezcM2Rv/pNtcDkN7eirhJDeJmpN5lQZ+zIA6E26+QkNbjFrdf Zn0Q== 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=T4wrNvw6JlSwhp1a686x0FC6ofix5CcdqhjJcdt5Tuo=; b=d+cUbzxHiMP4T2FAjxhE3aYtRSzhwhUhgkati+Ns+eg/YN+eMaFLbqWwo72SJY6H86 UQLnxrAPw9nXnMd2/fN86YzqZ60TcYVheqNjJF64LW590zhWbXslvQzjBcPPDNFe7i8q oEt7QzUtalSaBr2N5n7q/BkXOK9uDElz64nNq88JvGZxlVeivGGyhtV99gW8gwD9x+zY qXBFz57KWphqs9rxH5gZMQgz2qePG/rvFpD1MqPB85iqmJ8eCNbEKjD4+OO6vWMRd6/k zBykZa6lOtj/bBFunCeWxo7ubr9Pknkp3KUGpZn7FpgLBVipknmm3lz5WTa2tURzrTkl q2vw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@cisco.com header.s=iport header.b=AI/osGFm; 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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=cisco.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id p5-v6si506353pls.238.2018.03.09.00.36.17; Fri, 09 Mar 2018 00:36:31 -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; dkim=pass header.i=@cisco.com header.s=iport header.b=AI/osGFm; 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; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=cisco.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751991AbeCIIew (ORCPT + 99 others); Fri, 9 Mar 2018 03:34:52 -0500 Received: from alln-iport-3.cisco.com ([173.37.142.90]:48752 "EHLO alln-iport-3.cisco.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751242AbeCIIeu (ORCPT ); Fri, 9 Mar 2018 03:34:50 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=cisco.com; i=@cisco.com; l=5219; q=dns/txt; s=iport; t=1520584490; x=1521794090; h=from:to:cc:subject:date:message-id:in-reply-to: references; bh=4UQDlBxzPymJ+EKIWFxLLTQBoD6NPEZ0Ci/K07VefCo=; b=AI/osGFmpg/w4D5V3pdOgWgEXQjJzfLEeVF/dpqokcgQuqttoNzst6dd cwFRyDu5BngFHfbfjvrvHp0GIfDZjwKP+93DvIQNXt8S/b8JHzUFSMXlv hkpp02wT+uduZzwApamtSWZslXCLikj+SqXT25F+zXjjhldboq1PZ89zJ I=; X-IronPort-Anti-Spam-Filtered: true X-IronPort-Anti-Spam-Result: =?us-ascii?q?A0AtAgD+RaJa/5xdJa1eGQEBAQEBAQEBA?= =?us-ascii?q?QEBAQcBAQEBAYNQZm8om2WDGIcejyEKH4UGAoMPITcVAQIBAQEBAQECayeFJAY?= =?us-ascii?q?nUhAZOCE2BgEShQEDCA0Prgw6hHGCOQ2BMIIVBRSFIoIugVaBZoYXRAGHXQSaJ?= =?us-ascii?q?DEJhkmGaYM3Ao5eATCJSTmGbgIREwGBLDQigVJwFTqCQx+CEhyBe0A3iwwBAQE?= X-IronPort-AV: E=Sophos;i="5.47,444,1515456000"; d="scan'208";a="81642043" Received: from rcdn-core-5.cisco.com ([173.37.93.156]) by alln-iport-3.cisco.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 09 Mar 2018 08:34:50 +0000 Received: from sjc-ads-7132.cisco.com (sjc-ads-7132.cisco.com [10.30.217.207]) (authenticated bits=0) by rcdn-core-5.cisco.com (8.14.5/8.14.5) with ESMTP id w298YfZx003070 (version=TLSv1/SSLv3 cipher=AES128-SHA256 bits=128 verify=NO); Fri, 9 Mar 2018 08:34:49 GMT From: Taras Kondratiuk To: Tejun Heo , Lin Ming Cc: xe-linux-external@cisco.com, linux-ide@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH] libata: add refcounting to ata_host Date: Fri, 9 Mar 2018 08:34:41 +0000 Message-Id: <1520584481-9643-1-git-send-email-takondra@cisco.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <20171113201037.GL983427@devbig577.frc2.facebook.com> References: <20171113201037.GL983427@devbig577.frc2.facebook.com> X-Auto-Response-Suppress: DR, OOF, AutoReply X-Authenticated-User: takondra@cisco.com Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org After commit 9a6d6a2ddabb ("ata: make ata port as parent device of scsi host") manual driver unbind/remove causes use-after-free. Unbind unconditionally invokes devres_release_all() which calls ata_host_release() and frees ata_host/ata_port memory while it is still being referenced as a parent of SCSI host. When SCSI host is finally released scsi_host_dev_release() calls put_device(parent) and accesses freed ata_port memory. Add reference counting to make sure that ata_host lives long enough. Bug report: https://lkml.org/lkml/2017/11/1/945 Fixes: 9a6d6a2ddabb ("ata: make ata port as parent device of scsi host") Cc: Tejun Heo Cc: Lin Ming Cc: linux-ide@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Taras Kondratiuk --- Based on v4.16-rc4. drivers/ata/libata-core.c | 44 ++++++++++++++++++++++++++++++++++-------- drivers/ata/libata-transport.c | 4 ++++ drivers/ata/libata.h | 2 ++ include/linux/libata.h | 1 + 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 3c09122bf038..ee67f4b113c5 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6006,7 +6006,7 @@ struct ata_port *ata_port_alloc(struct ata_host *host) return ap; } -static void ata_host_release(struct device *gendev, void *res) +static void ata_devres_release(struct device *gendev, void *res) { struct ata_host *host = dev_get_drvdata(gendev); int i; @@ -6020,13 +6020,36 @@ static void ata_host_release(struct device *gendev, void *res) if (ap->scsi_host) scsi_host_put(ap->scsi_host); + } + + dev_set_drvdata(gendev, NULL); + ata_host_put(host); +} + +static void ata_host_release(struct kref *kref) +{ + struct ata_host *host = container_of(kref, struct ata_host, kref); + int i; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + kfree(ap->pmp_link); kfree(ap->slave_link); kfree(ap); host->ports[i] = NULL; } + kfree(host); +} - dev_set_drvdata(gendev, NULL); +void ata_host_get(struct ata_host *host) +{ + kref_get(&host->kref); +} + +void ata_host_put(struct ata_host *host) +{ + kref_put(&host->kref, ata_host_release); } /** @@ -6054,26 +6077,31 @@ struct ata_host *ata_host_alloc(struct device *dev, int max_ports) struct ata_host *host; size_t sz; int i; + void *dr; DPRINTK("ENTER\n"); - if (!devres_open_group(dev, NULL, GFP_KERNEL)) - return NULL; - /* alloc a container for our list of ATA ports (buses) */ sz = sizeof(struct ata_host) + (max_ports + 1) * sizeof(void *); - /* alloc a container for our list of ATA ports (buses) */ - host = devres_alloc(ata_host_release, sz, GFP_KERNEL); + host = kzalloc(sz, GFP_KERNEL); if (!host) + return NULL; + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return NULL; + + dr = devres_alloc(ata_devres_release, 0, GFP_KERNEL); + if (!dr) goto err_out; - devres_add(dev, host); + devres_add(dev, dr); dev_set_drvdata(dev, host); spin_lock_init(&host->lock); mutex_init(&host->eh_mutex); host->dev = dev; host->n_ports = max_ports; + kref_init(&host->kref); /* allocate ports bound to this host */ for (i = 0; i < max_ports; i++) { diff --git a/drivers/ata/libata-transport.c b/drivers/ata/libata-transport.c index 19e6e539a061..a0b0b4d986f2 100644 --- a/drivers/ata/libata-transport.c +++ b/drivers/ata/libata-transport.c @@ -224,6 +224,8 @@ static DECLARE_TRANSPORT_CLASS(ata_port_class, static void ata_tport_release(struct device *dev) { + struct ata_port *ap = tdev_to_port(dev); + ata_host_put(ap->host); } /** @@ -284,6 +286,7 @@ int ata_tport_add(struct device *parent, dev->type = &ata_port_type; dev->parent = parent; + ata_host_get(ap->host); dev->release = ata_tport_release; dev_set_name(dev, "ata%d", ap->print_id); transport_setup_device(dev); @@ -314,6 +317,7 @@ int ata_tport_add(struct device *parent, tport_err: transport_destroy_device(dev); put_device(dev); + ata_host_put(ap->host); return error; } diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index f953cb4bb1ba..9e21c49cf6be 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -100,6 +100,8 @@ extern int ata_port_probe(struct ata_port *ap); extern void __ata_port_probe(struct ata_port *ap); extern unsigned int ata_read_log_page(struct ata_device *dev, u8 log, u8 page, void *buf, unsigned int sectors); +extern void ata_host_get(struct ata_host *host); +extern void ata_host_put(struct ata_host *host); #define to_ata_port(d) container_of(d, struct ata_port, tdev) diff --git a/include/linux/libata.h b/include/linux/libata.h index ed9826b21c5e..1795fecdea17 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -617,6 +617,7 @@ struct ata_host { void *private_data; struct ata_port_operations *ops; unsigned long flags; + struct kref kref; struct mutex eh_mutex; struct task_struct *eh_owner; -- 2.10.3.dirty