Received: by 10.192.165.148 with SMTP id m20csp2943820imm; Mon, 7 May 2018 04:04:58 -0700 (PDT) X-Google-Smtp-Source: AB8JxZp7WwYxScE6rqQFNlIZ43Aor5H/xCCzJFxFdsOzHeHBflOcdl6RuRgnM9NOwm5tMgaovYYt X-Received: by 2002:a63:774e:: with SMTP id s75-v6mr29819313pgc.162.1525691098060; Mon, 07 May 2018 04:04:58 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1525691098; cv=none; d=google.com; s=arc-20160816; b=IekI7HMTuyUCKD5VRSAAl8jPQzw8IrqwZJe7b+1q1xk3mbUW1/OJ2iQjyhIOvUdUDp BZMFnyDEoSvRqwKsEOvdPaYoTE2yw+T/t+Aylag/CehAXbFA3yIvW//M2BSuv/fRq1Zq srfHPy85srkXvubZJ7S62VDDcFAqjWDVFDlc2hbmOV7YVBTgQzHNmI5J7QzNwJQhfZRc dpR9MdMh3hn15P9kno69MaYLy9ennDWjEWOH/ulbxCN3hjm0Me4DQQrnBN4OTtjIDh9E wbcQzJnBO6GY+h0H6qwLVSn1Lzz4YWLPJeNghO4MGA4YwlAxJQWZdbvNI4bsvrINyX5C I1eA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:message-id:date:subject:cc :to:from:arc-authentication-results; bh=I3PZPQHydLt1R2MeTJUfXw7tIXmyV5hLhtRWKYHiJPQ=; b=KEkQsCi83+fT/+FQ5g+crU00eBikyCNZlyJISKlONyGxJ9yxfw79YvkLk5VamOZj6w ZWyy4AAFMbJPQiVppw996aIQlQXSMAzHbGtTJgjeZ3dfjWDXjEglpumIJtf8oYR26M04 PWea3pcAUVWsQ2cpup4xfx7ZoX8WFG8gO/L6qghfOofTpais4LWVovNxKE1PyDCLhTYn S1iDZg48NCRxhlGbAxXhc2RtpOH5EVVDuxpBRH380mpP4S1NEYlNScKHm/c/K9jV3BNC /ghGxKpHDCgaQ0nG+omZVw2ehrqfaeXxWPlQ5TPxSF4RbUHouVkyu8Fa4Zd6rjc9ugmE 9alg== 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 w16-v6si9009908plq.141.2018.05.07.04.04.44; Mon, 07 May 2018 04:04:58 -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 S1752052AbeEGLEc (ORCPT + 99 others); Mon, 7 May 2018 07:04:32 -0400 Received: from szxga07-in.huawei.com ([45.249.212.35]:52556 "EHLO huawei.com" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1751961AbeEGLEa (ORCPT ); Mon, 7 May 2018 07:04:30 -0400 Received: from DGGEMS410-HUB.china.huawei.com (unknown [172.30.72.58]) by Forcepoint Email with ESMTP id D6D56C48DDF01; Mon, 7 May 2018 19:04:25 +0800 (CST) Received: from huawei.com (10.175.124.28) by DGGEMS410-HUB.china.huawei.com (10.3.19.210) with Microsoft SMTP Server id 14.3.361.1; Mon, 7 May 2018 19:04:20 +0800 From: Jason Yan To: , CC: , , , , , Jason Yan Subject: [PATCH-next v2] scsi: libsas: dynamically allocate and free ata host Date: Mon, 7 May 2018 19:15:10 +0800 Message-ID: <20180507111510.32816-1-yanaijie@huawei.com> X-Mailer: git-send-email 2.13.6 MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [10.175.124.28] X-CFilter-Loop: Reflected Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Now the ata host for libsas is embedded in domain_device, and the ->kref member is not initialized. Afer we add ata transport class, ata_host_get() will be called when adding transport ATA port and a warning will be triggered as below: refcount_t: increment on 0; use-after-free. WARNING: CPU: 2 PID: 103 at lib/refcount.c:153 refcount_inc+0x40/0x48 ...... Call trace: refcount_inc+0x40/0x48 ata_host_get+0x10/0x18 ata_tport_add+0x40/0x120 ata_sas_tport_add+0xc/0x14 sas_ata_init+0x7c/0xc8 sas_discover_domain+0x380/0x53c process_one_work+0x12c/0x288 worker_thread+0x58/0x3f0 kthread+0xfc/0x128 ret_from_fork+0x10/0x18 And also when removing transport ATA port ata_host_put() will be called and another similar warning will be triggered. If the refcount decreased to zero, the ata host will be freed. But this ata host is only part of domain_device, it cannot be freed directly. So we have to change this embedded static ata host to a dynamically allocated ata host and initialize the ->kref member. To use ata_host_get() and ata_host_put() in libsas, we need to move the declaration of these functions to the public libata.h and export them. v2: export ata_host_get() and ata_host_put(). Fixes: b6240a4df018 ("scsi: libsas: add transport class for ATA devices") Signed-off-by: Jason Yan CC: John Garry --- drivers/ata/libata-core.c | 3 +++ drivers/ata/libata.h | 2 -- drivers/scsi/libsas/sas_ata.c | 40 +++++++++++++++++++++++++------------- drivers/scsi/libsas/sas_discover.c | 2 ++ include/linux/libata.h | 2 ++ include/scsi/libsas.h | 2 +- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 68596bd4cf06..cdb48dccfb45 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -6424,6 +6424,7 @@ void ata_host_init(struct ata_host *host, struct device *dev, host->n_tags = ATA_MAX_QUEUE - 1; host->dev = dev; host->ops = ops; + kref_init(&host->kref); } void __ata_port_probe(struct ata_port *ap) @@ -7391,3 +7392,5 @@ EXPORT_SYMBOL_GPL(ata_cable_80wire); EXPORT_SYMBOL_GPL(ata_cable_unknown); EXPORT_SYMBOL_GPL(ata_cable_ignore); EXPORT_SYMBOL_GPL(ata_cable_sata); +EXPORT_SYMBOL_GPL(ata_host_get); +EXPORT_SYMBOL_GPL(ata_host_put); \ No newline at end of file diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 9e21c49cf6be..f953cb4bb1ba 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -100,8 +100,6 @@ 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/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index ff1d612f6fb9..41cdda7a926b 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -557,34 +557,46 @@ int sas_ata_init(struct domain_device *found_dev) { struct sas_ha_struct *ha = found_dev->port->ha; struct Scsi_Host *shost = ha->core.shost; + struct ata_host *ata_host; struct ata_port *ap; int rc; - ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, &sas_sata_ops); - ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, - &sata_port_info, - shost); + ata_host = kzalloc(sizeof(*ata_host), GFP_KERNEL); + if (!ata_host) { + SAS_DPRINTK("ata host alloc failed.\n"); + return -ENOMEM; + } + + ata_host_init(ata_host, ha->dev, &sas_sata_ops); + + ap = ata_sas_port_alloc(ata_host, &sata_port_info, shost); if (!ap) { SAS_DPRINTK("ata_sas_port_alloc failed.\n"); - return -ENODEV; + rc = -ENODEV; + goto free_host; } ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; ap->scsi_host = shost; rc = ata_sas_port_init(ap); - if (rc) { - ata_sas_port_destroy(ap); - return rc; - } - rc = ata_sas_tport_add(found_dev->sata_dev.ata_host.dev, ap); - if (rc) { - ata_sas_port_destroy(ap); - return rc; - } + if (rc) + goto destroy_port; + + rc = ata_sas_tport_add(ata_host->dev, ap); + if (rc) + goto destroy_port; + + found_dev->sata_dev.ata_host = ata_host; found_dev->sata_dev.ap = ap; return 0; + +destroy_port: + ata_sas_port_destroy(ap); +free_host: + ata_host_put(ata_host); + return rc; } void sas_ata_task_abort(struct sas_task *task) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 1ffca28fe6a8..0148ae62a52a 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -316,6 +316,8 @@ void sas_free_device(struct kref *kref) if (dev_is_sata(dev) && dev->sata_dev.ap) { ata_sas_tport_delete(dev->sata_dev.ap); ata_sas_port_destroy(dev->sata_dev.ap); + ata_host_put(dev->sata_dev.ata_host); + dev->sata_dev.ata_host = NULL; dev->sata_dev.ap = NULL; } diff --git a/include/linux/libata.h b/include/linux/libata.h index 0619ebf4d475..852cdb2fc692 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1110,6 +1110,8 @@ extern struct ata_host *ata_host_alloc(struct device *dev, int max_ports); extern struct ata_host *ata_host_alloc_pinfo(struct device *dev, const struct ata_port_info * const * ppi, int n_ports); extern int ata_slave_link_init(struct ata_port *ap); +extern void ata_host_get(struct ata_host *host); +extern void ata_host_put(struct ata_host *host); extern int ata_host_start(struct ata_host *host); extern int ata_host_register(struct ata_host *host, struct scsi_host_template *sht); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 225ab7783dfd..3de3b10da19a 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -161,7 +161,7 @@ struct sata_device { u8 port_no; /* port number, if this is a PM (Port) */ struct ata_port *ap; - struct ata_host ata_host; + struct ata_host *ata_host; struct smp_resp rps_resp ____cacheline_aligned; /* report_phy_sata_resp */ u8 fis[ATA_RESP_FIS_SIZE]; }; -- 2.13.6