drivers/nvme/host/ioctl.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)
On an NVMe namespace that does not support metadata, it is possible to
send an IO command with metadata through io-passthru.
nvme_map_user_request() doesn't check if the namespace supports metadata
before sending it forward.
Reject an IO command with metadata when the NVMe namespace doesn't
support it.
Suggested-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Puranjay Mohan <pjy@amazon.com>
---
V1: https://lore.kernel.org/all/20240827121701.48792-1-pjy@amazon.com/
Changes in V2:
- Add a flag called 'has_metadata' and use it for the support check and
also for the check before calling bio_integrity_map_user()
---
drivers/nvme/host/ioctl.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index f1d58e70933f..74d963d425c4 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -4,6 +4,7 @@
* Copyright (c) 2017-2021 Christoph Hellwig.
*/
#include <linux/bio-integrity.h>
+#include <linux/blk-integrity.h>
#include <linux/ptrace.h> /* for force_successful_syscall_return */
#include <linux/nvme_ioctl.h>
#include <linux/io_uring/cmd.h>
@@ -119,9 +120,13 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,
struct request_queue *q = req->q;
struct nvme_ns *ns = q->queuedata;
struct block_device *bdev = ns ? ns->disk->part0 : NULL;
+ bool has_metadata = bdev && meta_buffer && meta_len;
struct bio *bio = NULL;
int ret;
+ if (has_metadata && !blk_get_integrity(bdev->bd_disk))
+ return -EINVAL;
+
if (ioucmd && (ioucmd->flags & IORING_URING_CMD_FIXED)) {
struct iov_iter iter;
@@ -143,15 +148,15 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,
goto out;
bio = req->bio;
- if (bdev) {
+ if (bdev)
bio_set_dev(bio, bdev);
- if (meta_buffer && meta_len) {
- ret = bio_integrity_map_user(bio, meta_buffer, meta_len,
- meta_seed);
- if (ret)
- goto out_unmap;
- req->cmd_flags |= REQ_INTEGRITY;
- }
+
+ if (has_metadata) {
+ ret = bio_integrity_map_user(bio, meta_buffer, meta_len,
+ meta_seed);
+ if (ret)
+ goto out_unmap;
+ req->cmd_flags |= REQ_INTEGRITY;
}
return ret;
--
2.40.1
Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
On Tue, Aug 27, 2024 at 01:23:27PM +0000, Puranjay Mohan wrote: > @@ -119,9 +120,13 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, > struct request_queue *q = req->q; > struct nvme_ns *ns = q->queuedata; > struct block_device *bdev = ns ? ns->disk->part0 : NULL; > + bool has_metadata = bdev && meta_buffer && meta_len; If this is an admin command, then bdev is NULL, so "has_metadata" is false. > struct bio *bio = NULL; > int ret; > > + if (has_metadata && !blk_get_integrity(bdev->bd_disk)) > + return -EINVAL; > + Since has_metadata is false, we continue on to process this admin command, but ignore the user's metadata settings. Do we want to return error there too?
On Wed, Aug 28, 2024 at 08:44:59AM -0600, Keith Busch wrote: > On Tue, Aug 27, 2024 at 01:23:27PM +0000, Puranjay Mohan wrote: > > @@ -119,9 +120,13 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, > > struct request_queue *q = req->q; > > struct nvme_ns *ns = q->queuedata; > > struct block_device *bdev = ns ? ns->disk->part0 : NULL; > > + bool has_metadata = bdev && meta_buffer && meta_len; > > If this is an admin command, then bdev is NULL, so "has_metadata" is > false. > > Since has_metadata is false, we continue on to process this admin > command, but ignore the user's metadata settings. Do we want to return > error there too? Ah yes. I had my brain wrapped around this the wrong way the entire time. Maybe a bool supports_metadata = bdev && blk_get_integrity(bdev->bd_disk); bool has_metadata = meta_buffer && meta_len; might help poor brains like mine.
Keith Busch <kbusch@kernel.org> writes:
> On Tue, Aug 27, 2024 at 01:23:27PM +0000, Puranjay Mohan wrote:
>> @@ -119,9 +120,13 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer,
>> struct request_queue *q = req->q;
>> struct nvme_ns *ns = q->queuedata;
>> struct block_device *bdev = ns ? ns->disk->part0 : NULL;
>> + bool has_metadata = bdev && meta_buffer && meta_len;
>
> If this is an admin command, then bdev is NULL, so "has_metadata" is
> false.
>
>> struct bio *bio = NULL;
>> int ret;
>>
>> + if (has_metadata && !blk_get_integrity(bdev->bd_disk))
>> + return -EINVAL;
>> +
>
> Since has_metadata is false, we continue on to process this admin
> command, but ignore the user's metadata settings. Do we want to return
> error there too?
As an admin command with metadata is an invalid configuration, we can
ignore the metada and go ahead with the admin command or I can add the
following after the above check:
if (!bdev && (meta_buffer || meta_len))
return -EINVAL;
I don't know what is the best approach here.
Thanks,
Puranjay
On Wed, Aug 28, 2024 at 03:31:09PM +0000, Puranjay Mohan wrote: > Keith Busch <kbusch@kernel.org> writes: > > > On Tue, Aug 27, 2024 at 01:23:27PM +0000, Puranjay Mohan wrote: > >> @@ -119,9 +120,13 @@ static int nvme_map_user_request(struct request *req, u64 ubuffer, > >> struct request_queue *q = req->q; > >> struct nvme_ns *ns = q->queuedata; > >> struct block_device *bdev = ns ? ns->disk->part0 : NULL; > >> + bool has_metadata = bdev && meta_buffer && meta_len; > > > > If this is an admin command, then bdev is NULL, so "has_metadata" is > > false. > > > >> struct bio *bio = NULL; > >> int ret; > >> > >> + if (has_metadata && !blk_get_integrity(bdev->bd_disk)) > >> + return -EINVAL; > >> + > > > > Since has_metadata is false, we continue on to process this admin > > command, but ignore the user's metadata settings. Do we want to return > > error there too? > > As an admin command with metadata is an invalid configuration, we can It's not that it's an invalid configuration. The spec defines the common command format allowing admin commands to transfer metadata. There's just no existing spec defined command that makes use of it. Nothing stops a vendor specific command from using it. If someone tried, the kernel reports success, but we didn't execute the requested command. > ignore the metada and go ahead with the admin command or I can add the > following after the above check: > > if (!bdev && (meta_buffer || meta_len)) > return -EINVAL; > > I don't know what is the best approach here. Yeah, or just do it in one line with the bdev case too: bool has_metadata = meta_buffer && meta_len; ... if (has_metadata && (!bdev || !blk_get_integrity(bdev->bd_disk))) return -EINVAL
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
© 2016 - 2025 Red Hat, Inc.