From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-eopbgr700098.outbound.protection.outlook.com ([40.107.70.98]:16799 "EHLO NAM04-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727495AbeIBRUo (ORCPT ); Sun, 2 Sep 2018 13:20:44 -0400 From: Sasha Levin To: "stable@vger.kernel.org" , "linux-kernel@vger.kernel.org" CC: Mike Christie , "Martin K . Petersen" , Sasha Levin Subject: [PATCH AUTOSEL 4.18 059/131] scsi: tcmu: do not set max_blocks if data_bitmap has been setup Date: Sun, 2 Sep 2018 13:04:16 +0000 Message-ID: <20180902064601.183036-59-alexander.levin@microsoft.com> References: <20180902064601.183036-1-alexander.levin@microsoft.com> In-Reply-To: <20180902064601.183036-1-alexander.levin@microsoft.com> Content-Language: en-US Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Sender: stable-owner@vger.kernel.org List-ID: From: Mike Christie [ Upstream commit c97840c84f5a4362a596a2751e9245a979377a16 ] This patch prevents a bug where data_bitmap is allocated in tcmu_configure_device, userspace changes the max_blocks setting, the device is mapped to a LUN, then we try to access the data_bitmap based on the new max_blocks limit which may now be out of range. To prevent this, we just check if data_bitmap has been setup. If it has then we fail the max_blocks update operation. Signed-off-by: Mike Christie Reviewed-by: Xiubo Li Signed-off-by: Martin K. Petersen Signed-off-by: Sasha Levin --- drivers/target/target_core_user.c | 73 +++++++++++++++++-------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core= _user.c index d8dc3d22051f..b8dc5efc606b 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -1745,9 +1745,11 @@ static int tcmu_configure_device(struct se_device *d= ev) =20 info =3D &udev->uio_info; =20 + mutex_lock(&udev->cmdr_lock); udev->data_bitmap =3D kcalloc(BITS_TO_LONGS(udev->max_blocks), sizeof(unsigned long), GFP_KERNEL); + mutex_unlock(&udev->cmdr_lock); if (!udev->data_bitmap) { ret =3D -ENOMEM; goto err_bitmap_alloc; @@ -1957,7 +1959,7 @@ static match_table_t tokens =3D { {Opt_hw_block_size, "hw_block_size=3D%u"}, {Opt_hw_max_sectors, "hw_max_sectors=3D%u"}, {Opt_nl_reply_supported, "nl_reply_supported=3D%d"}, - {Opt_max_data_area_mb, "max_data_area_mb=3D%u"}, + {Opt_max_data_area_mb, "max_data_area_mb=3D%d"}, {Opt_err, NULL} }; =20 @@ -1985,13 +1987,48 @@ static int tcmu_set_dev_attrib(substring_t *arg, u3= 2 *dev_attrib) return 0; } =20 +static int tcmu_set_max_blocks_param(struct tcmu_dev *udev, substring_t *a= rg) +{ + int val, ret; + + ret =3D match_int(arg, &val); + if (ret < 0) { + pr_err("match_int() failed for max_data_area_mb=3D. Error %d.\n", + ret); + return ret; + } + + if (val <=3D 0) { + pr_err("Invalid max_data_area %d.\n", val); + return -EINVAL; + } + + mutex_lock(&udev->cmdr_lock); + if (udev->data_bitmap) { + pr_err("Cannot set max_data_area_mb after it has been enabled.\n"); + ret =3D -EINVAL; + goto unlock; + } + + udev->max_blocks =3D TCMU_MBS_TO_BLOCKS(val); + if (udev->max_blocks > tcmu_global_max_blocks) { + pr_err("%d is too large. Adjusting max_data_area_mb to global limit of %= u\n", + val, TCMU_BLOCKS_TO_MBS(tcmu_global_max_blocks)); + udev->max_blocks =3D tcmu_global_max_blocks; + } + +unlock: + mutex_unlock(&udev->cmdr_lock); + return ret; +} + static ssize_t tcmu_set_configfs_dev_params(struct se_device *dev, const char *page, ssize_t count) { struct tcmu_dev *udev =3D TCMU_DEV(dev); char *orig, *ptr, *opts, *arg_p; substring_t args[MAX_OPT_ARGS]; - int ret =3D 0, token, tmpval; + int ret =3D 0, token; =20 opts =3D kstrdup(page, GFP_KERNEL); if (!opts) @@ -2044,37 +2081,7 @@ static ssize_t tcmu_set_configfs_dev_params(struct s= e_device *dev, pr_err("kstrtoint() failed for nl_reply_supported=3D\n"); break; case Opt_max_data_area_mb: - if (dev->export_count) { - pr_err("Unable to set max_data_area_mb while exports exist\n"); - ret =3D -EINVAL; - break; - } - - arg_p =3D match_strdup(&args[0]); - if (!arg_p) { - ret =3D -ENOMEM; - break; - } - ret =3D kstrtoint(arg_p, 0, &tmpval); - kfree(arg_p); - if (ret < 0) { - pr_err("kstrtoint() failed for max_data_area_mb=3D\n"); - break; - } - - if (tmpval <=3D 0) { - pr_err("Invalid max_data_area %d\n", tmpval); - ret =3D -EINVAL; - break; - } - - udev->max_blocks =3D TCMU_MBS_TO_BLOCKS(tmpval); - if (udev->max_blocks > tcmu_global_max_blocks) { - pr_err("%d is too large. Adjusting max_data_area_mb to global limit of= %u\n", - tmpval, - TCMU_BLOCKS_TO_MBS(tcmu_global_max_blocks)); - udev->max_blocks =3D tcmu_global_max_blocks; - } + ret =3D tcmu_set_max_blocks_param(udev, &args[0]); break; default: break; --=20 2.17.1