[PATCH v2] nvme: check if the namespace supports metadata in nvme_map_user_request()

Puranjay Mohan posted 1 patch 1 year, 3 months ago
drivers/nvme/host/ioctl.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)
[PATCH v2] nvme: check if the namespace supports metadata in nvme_map_user_request()
Posted by Puranjay Mohan 1 year, 3 months ago
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
Re: nvme: check if the namespace supports metadata in nvme_map_user_request()
Posted by Anuj Gupta 1 year, 3 months ago
Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
Re: [PATCH v2] nvme: check if the namespace supports metadata in nvme_map_user_request()
Posted by Keith Busch 1 year, 3 months ago
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?
Re: [PATCH v2] nvme: check if the namespace supports metadata in nvme_map_user_request()
Posted by Christoph Hellwig 1 year, 3 months ago
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.
Re: [PATCH v2] nvme: check if the namespace supports metadata in nvme_map_user_request()
Posted by Puranjay Mohan 1 year, 3 months ago
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
Re: [PATCH v2] nvme: check if the namespace supports metadata in nvme_map_user_request()
Posted by Keith Busch 1 year, 3 months ago
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
Re: [PATCH v2] nvme: check if the namespace supports metadata in nvme_map_user_request()
Posted by Sagi Grimberg 1 year, 3 months ago
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Re: [PATCH v2] nvme: check if the namespace supports metadata in nvme_map_user_request()
Posted by Christoph Hellwig 1 year, 3 months ago
Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>