From: Niklas Cassel <[email protected]>
When a passthru command targets a specific namespace, the ns parameter to
nvme_user_cmd()/nvme_user_cmd64() is set. However, there is currently no
validation that the nsid specified in the passthru command targets the
namespace/nsid represented by the block device that the ioctl was
performed on.
Add a check that validates that the nsid in the passthru command matches
that of the supplied namespace.
Signed-off-by: Niklas Cassel <[email protected]>
---
Currently, if doing NVME_IOCTL_IO_CMD on the controller char device,
if and only if there is a single namespace in the ctrl->namespaces list,
nvme_dev_user_cmd() will call nvme_user_cmd() with ns parameter set.
While it might be good that we validate the nsid even in this case,
perhaps we want to allow a nsid value in the passthru command to be
0x0 and/or the NSID broadcast value? (Only when NVME_IOCTL_IO_CMD was
done on the controller char device though.)
drivers/nvme/host/core.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 40215a0246e4..e4591a4c68a8 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -1632,6 +1632,8 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
return -EFAULT;
if (cmd.flags)
return -EINVAL;
+ if (ns && cmd.nsid != ns->head->ns_id)
+ return -EINVAL;
memset(&c, 0, sizeof(c));
c.common.opcode = cmd.opcode;
@@ -1676,6 +1678,8 @@ static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
return -EFAULT;
if (cmd.flags)
return -EINVAL;
+ if (ns && cmd.nsid != ns->head->ns_id)
+ return -EINVAL;
memset(&c, 0, sizeof(c));
c.common.opcode = cmd.opcode;
--
2.30.2
On Thu, Mar 25, 2021 at 09:48:37AM +0000, Niklas Cassel wrote:
> From: Niklas Cassel <[email protected]>
>
> When a passthru command targets a specific namespace, the ns parameter to
> nvme_user_cmd()/nvme_user_cmd64() is set. However, there is currently no
> validation that the nsid specified in the passthru command targets the
> namespace/nsid represented by the block device that the ioctl was
> performed on.
>
> Add a check that validates that the nsid in the passthru command matches
> that of the supplied namespace.
>
> Signed-off-by: Niklas Cassel <[email protected]>
> ---
> Currently, if doing NVME_IOCTL_IO_CMD on the controller char device,
> if and only if there is a single namespace in the ctrl->namespaces list,
> nvme_dev_user_cmd() will call nvme_user_cmd() with ns parameter set.
> While it might be good that we validate the nsid even in this case,
> perhaps we want to allow a nsid value in the passthru command to be
> 0x0 and/or the NSID broadcast value? (Only when NVME_IOCTL_IO_CMD was
> done on the controller char device though.)
TL;DR:
Since nvme_dev_user_cmd() only calls nvme_user_cmd() if and only if
there is a single namespace in the ctrl->namespaces list, I think that
this patch is good as is.
Long story:
If we merge Javier's patch series, then we will have all namespaces
(rejected or not) in ctrl->namespaces.
We could then decide to remove the weird restriction that you can
only use the contoller char device to do NVME_IOCTL_IO_CMD
when there is one and only one entry in the ctrl->namespaces list.
i.e. the user could specify any NSID, we just iterate the list and
get the struct nvme_ns that matches the cmd.nsid.
We could then also allow the broadcast value being specified as
an NSID, and in that case, use a disk less request_queue (like
Keith suggested in an earlier thread).
Being allowed to specify any NSID when using the controller char
device could be ok, as you probably wouldn't allow everyone access
to it.
So Javier's patch series still provides value, even if we allow any
NSID to be specified in the controller char device, since the per
namespace char devices can have more fine grained access control.
Kind regards,
Niklas
On Thu, Mar 25, 2021 at 09:48:37AM +0000, Niklas Cassel wrote:
> From: Niklas Cassel <[email protected]>
>
> When a passthru command targets a specific namespace, the ns parameter to
> nvme_user_cmd()/nvme_user_cmd64() is set. However, there is currently no
> validation that the nsid specified in the passthru command targets the
> namespace/nsid represented by the block device that the ioctl was
> performed on.
>
> Add a check that validates that the nsid in the passthru command matches
> that of the supplied namespace.
>
> Signed-off-by: Niklas Cassel <[email protected]>
> ---
> Currently, if doing NVME_IOCTL_IO_CMD on the controller char device,
> if and only if there is a single namespace in the ctrl->namespaces list,
> nvme_dev_user_cmd() will call nvme_user_cmd() with ns parameter set.
> While it might be good that we validate the nsid even in this case,
> perhaps we want to allow a nsid value in the passthru command to be
> 0x0 and/or the NSID broadcast value? (Only when NVME_IOCTL_IO_CMD was
> done on the controller char device though.)
There are no IO commands accepting a 0 NSID, so rejecting those from the
driver should be okay.
FLUSH is the only IO command that takes a broadcast NSID. I suspect at
least some entities were unfortunately sending broadcast flush through
this interface, so it's possible we'll hear of breakage, but I'd agree
your patch is still the right thing to do.
> drivers/nvme/host/core.c | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
> index 40215a0246e4..e4591a4c68a8 100644
> --- a/drivers/nvme/host/core.c
> +++ b/drivers/nvme/host/core.c
> @@ -1632,6 +1632,8 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
> return -EFAULT;
> if (cmd.flags)
> return -EINVAL;
> + if (ns && cmd.nsid != ns->head->ns_id)
> + return -EINVAL;
>
> memset(&c, 0, sizeof(c));
> c.common.opcode = cmd.opcode;
> @@ -1676,6 +1678,8 @@ static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
> return -EFAULT;
> if (cmd.flags)
> return -EINVAL;
> + if (ns && cmd.nsid != ns->head->ns_id)
> + return -EINVAL;
>
> memset(&c, 0, sizeof(c));
> c.common.opcode = cmd.opcode;
> --
> 2.30.2
On Thu, Mar 25, 2021 at 09:48:37AM +0000, Niklas Cassel wrote:
> @@ -1632,6 +1632,8 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns,
> return -EFAULT;
> if (cmd.flags)
> return -EINVAL;
> + if (ns && cmd.nsid != ns->head->ns_id)
> + return -EINVAL;
Shouldn't we log something to the kernel log including the device
and current->comm?
On Fri, Mar 26, 2021 at 12:19:42AM +0900, Keith Busch wrote:
> On Thu, Mar 25, 2021 at 09:48:37AM +0000, Niklas Cassel wrote:
> > From: Niklas Cassel <[email protected]>
> >
> > When a passthru command targets a specific namespace, the ns parameter to
> > nvme_user_cmd()/nvme_user_cmd64() is set. However, there is currently no
> > validation that the nsid specified in the passthru command targets the
> > namespace/nsid represented by the block device that the ioctl was
> > performed on.
> >
> > Add a check that validates that the nsid in the passthru command matches
> > that of the supplied namespace.
> >
> > Signed-off-by: Niklas Cassel <[email protected]>
> > ---
> > Currently, if doing NVME_IOCTL_IO_CMD on the controller char device,
> > if and only if there is a single namespace in the ctrl->namespaces list,
> > nvme_dev_user_cmd() will call nvme_user_cmd() with ns parameter set.
> > While it might be good that we validate the nsid even in this case,
> > perhaps we want to allow a nsid value in the passthru command to be
> > 0x0 and/or the NSID broadcast value? (Only when NVME_IOCTL_IO_CMD was
> > done on the controller char device though.)
>
> There are no IO commands accepting a 0 NSID, so rejecting those from the
> driver should be okay.
>
> FLUSH is the only IO command that takes a broadcast NSID. I suspect at
> least some entities were unfortunately sending broadcast flush through
> this interface, so it's possible we'll hear of breakage, but I'd agree
> your patch is still the right thing to do.
I don't think this should be a problem.
You shouldn't be sending a broadcast NSID via the per namespace block
device. It just seems silly to specify a specific namespace block device,
and then use the broadcast NSID. (This obviously should never have been
allowed.)
If you wanted to send a broadcast NSID, you should have used the controller
character device. However, nvme_dev_user_cmd() currently rejects any
NVME_IOCTL_IO_CMD when there is more than one namespace in ctrl->namespaces
list, so they could never have used the controller character device to send
a flush to more than one namespace. So here we don't change anything.
Kind regards,
Niklas