* [RFC 1/2] nvme : Add dynamic whitelisting for passthru
2022-10-07 13:22 ` [RFC 0/2] nvme : Add whitelist for admin commands in passthru Joel Granados
@ 2022-10-07 13:22 ` Joel Granados
2022-10-07 13:22 ` [RFC 2/2] nvme : Add ioctls for passthru admin whitelisting Joel Granados
2022-10-07 18:26 ` [RFC 0/2] nvme : Add whitelist for admin commands in passthru Keith Busch
2 siblings, 0 replies; 5+ messages in thread
From: Joel Granados @ 2022-10-07 13:22 UTC (permalink / raw)
To: kbusch, hch, sagi
Cc: linux-nvme, javier.gonz, gost.dev, joshi.k, Joel Granados
Drive information such as block size and total usable LBAs are needed to
send IO down the nvme passthru path. Currently only privileged users can
get these parameters. This patch implements a dynamic whitelist that
allows the privileged user to define what nvme opcodes are available for
the unprivileged.
A bitmap is added at the nvme driver level that controls what opcodes are
allowed. The unprivileged user will be able to execute an nvme opcode
(using passthru) when it is whitelisted and if mode matches FMODE_WRITE.
This contains only the whitelist implementation and is a preparation commit
for the ioctl calls.
Signed-off-by: Joel Granados <j.granados@samsung.com>
---
drivers/nvme/host/core.c | 10 ++++++++++
drivers/nvme/host/ioctl.c | 11 +++++++++--
drivers/nvme/host/nvme.h | 1 +
include/linux/nvme.h | 1 +
4 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 13080a017ecf..05d1e6fd633d 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -107,6 +107,8 @@ EXPORT_SYMBOL_GPL(nvme_reset_wq);
struct workqueue_struct *nvme_delete_wq;
EXPORT_SYMBOL_GPL(nvme_delete_wq);
+DECLARE_BITMAP(nvme_admin_whitelist, nvme_admin_last);
+
static LIST_HEAD(nvme_subsystems);
static DEFINE_MUTEX(nvme_subsystems_lock);
@@ -125,6 +127,12 @@ static void nvme_remove_invalid_namespaces(struct nvme_ctrl *ctrl,
static void nvme_update_keep_alive(struct nvme_ctrl *ctrl,
struct nvme_command *cmd);
+static void nvme_init_admin_whitelist(void)
+{
+ bitmap_zero(nvme_admin_whitelist, nvme_admin_last);
+ __set_bit(nvme_admin_identify, nvme_admin_whitelist);
+}
+
void nvme_queue_scan(struct nvme_ctrl *ctrl)
{
/*
@@ -5231,6 +5239,8 @@ static int __init nvme_core_init(void)
goto unregister_generic_ns;
}
+ nvme_init_admin_whitelist();
+
return 0;
unregister_generic_ns:
diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index ebe04e977baa..73e4287d2c44 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -20,6 +20,14 @@ static void __user *nvme_to_user_ptr(uintptr_t ptrval)
return (void __user *)ptrval;
}
+
+bool nvme_admin_cmd_allowed(u8 opcode, fmode_t mode)
+{
+ if (test_bit(opcode, nvme_admin_whitelist))
+ return (mode & FMODE_WRITE);
+ return false;
+}
+
bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c, fmode_t mode)
{
u8 opcode = c->common.opcode;
@@ -27,9 +35,8 @@ bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c, fmode_t mode)
if (capable(CAP_SYS_ADMIN))
return true;
- /* admin commands are not allowed */
if (ns == NULL)
- return false;
+ return nvme_admin_cmd_allowed(opcode, mode);
/* exclude vendor-specific io and fabrics commands */
if (opcode >= nvme_cmd_vendor_start || opcode == nvme_fabrics_command)
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h
index 216acbe953b3..18a55e3483bd 100644
--- a/drivers/nvme/host/nvme.h
+++ b/drivers/nvme/host/nvme.h
@@ -46,6 +46,7 @@ extern unsigned int admin_timeout;
extern struct workqueue_struct *nvme_wq;
extern struct workqueue_struct *nvme_reset_wq;
extern struct workqueue_struct *nvme_delete_wq;
+extern unsigned long nvme_admin_whitelist[];
/*
* List of workarounds for devices that required behavior not specified in
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 8396eb7ecb68..18a75496299c 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -1123,6 +1123,7 @@ enum nvme_admin_opcode {
nvme_admin_sanitize_nvm = 0x84,
nvme_admin_get_lba_status = 0x86,
nvme_admin_vendor_start = 0xC0,
+ nvme_admin_last,
};
#define nvme_admin_opcode_name(opcode) { opcode, #opcode }
--
2.30.2
^ permalink raw reply related [flat|nested] 5+ messages in thread* [RFC 2/2] nvme : Add ioctls for passthru admin whitelisting
2022-10-07 13:22 ` [RFC 0/2] nvme : Add whitelist for admin commands in passthru Joel Granados
2022-10-07 13:22 ` [RFC 1/2] nvme : Add dynamic whitelisting for passthru Joel Granados
@ 2022-10-07 13:22 ` Joel Granados
2022-10-07 18:26 ` [RFC 0/2] nvme : Add whitelist for admin commands in passthru Keith Busch
2 siblings, 0 replies; 5+ messages in thread
From: Joel Granados @ 2022-10-07 13:22 UTC (permalink / raw)
To: kbusch, hch, sagi
Cc: linux-nvme, javier.gonz, gost.dev, joshi.k, Joel Granados
An ioctl (NVME_IOCTL_PTHRU_WLIST) is added in order to control the passthu
admin opcode whiltelist. A new struct (nvme_pthru_wlist) is used to pass
an action (Add, Remove, Test), opcode number and result data between user
and kernel space.
Signed-off-by: Joel Granados <j.granados@samsung.com>
---
drivers/nvme/host/ioctl.c | 32 ++++++++++++++++++++++++++++++++
include/uapi/linux/nvme_ioctl.h | 14 ++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/drivers/nvme/host/ioctl.c b/drivers/nvme/host/ioctl.c
index 73e4287d2c44..76393d50a798 100644
--- a/drivers/nvme/host/ioctl.c
+++ b/drivers/nvme/host/ioctl.c
@@ -50,6 +50,36 @@ bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c, fmode_t mode)
return true;
}
+static int nvme_admin_cmd_whitelist(struct nvme_pthru_wlist __user *u_op)
+{
+ __u8 opcode;
+ struct nvme_pthru_wlist nvme_pthru_wlist = {0};
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (copy_from_user(&nvme_pthru_wlist, u_op, sizeof(struct nvme_pthru_wlist)))
+ return -EFAULT;
+ opcode = nvme_pthru_wlist.opcode;
+ if (opcode >= nvme_admin_last || opcode < 0)
+ return -EINVAL;
+
+ switch (nvme_pthru_wlist.action) {
+ case NVME_PTHRU_WLIST_ADD:
+ __set_bit(opcode, nvme_admin_whitelist);
+ return 0;
+ case NVME_PTHRU_WLIST_REM:
+ __clear_bit(opcode, nvme_admin_whitelist);
+ return 0;
+ case NVME_PTHRU_WLIST_TEST:
+ if (put_user(test_bit(opcode, nvme_admin_whitelist),
+ &u_op->result))
+ return -EFAULT;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
static void *nvme_add_user_metadata(struct bio *bio, void __user *ubuf,
unsigned len, u32 seed, bool write)
{
@@ -586,6 +616,8 @@ static int nvme_ns_ioctl(struct nvme_ns *ns, unsigned int cmd,
return nvme_user_cmd64(ns->ctrl, ns, argp, false, mode);
case NVME_IOCTL_IO64_CMD_VEC:
return nvme_user_cmd64(ns->ctrl, ns, argp, true, mode);
+ case NVME_IOCTL_PTHRU_WLIST:
+ return nvme_admin_cmd_whitelist(argp);
default:
return -ENOTTY;
}
diff --git a/include/uapi/linux/nvme_ioctl.h b/include/uapi/linux/nvme_ioctl.h
index 2f76cba67166..748ce70517ba 100644
--- a/include/uapi/linux/nvme_ioctl.h
+++ b/include/uapi/linux/nvme_ioctl.h
@@ -45,6 +45,19 @@ struct nvme_passthru_cmd {
__u32 result;
};
+enum nvme_pthru_wlist_action {
+ NVME_PTHRU_WLIST_ADD,
+ NVME_PTHRU_WLIST_REM,
+ NVME_PTHRU_WLIST_TEST
+};
+
+struct nvme_pthru_wlist {
+ __u8 opcode;
+ __u8 flags;
+ __u32 result;
+ enum nvme_pthru_wlist_action action;
+};
+
struct nvme_passthru_cmd64 {
__u8 opcode;
__u8 flags;
@@ -104,6 +117,7 @@ struct nvme_uring_cmd {
#define NVME_IOCTL_ADMIN64_CMD _IOWR('N', 0x47, struct nvme_passthru_cmd64)
#define NVME_IOCTL_IO64_CMD _IOWR('N', 0x48, struct nvme_passthru_cmd64)
#define NVME_IOCTL_IO64_CMD_VEC _IOWR('N', 0x49, struct nvme_passthru_cmd64)
+#define NVME_IOCTL_PTHRU_WLIST _IOWR('N', 0x50, struct nvme_pthru_wlist)
/* io_uring async commands: */
#define NVME_URING_CMD_IO _IOWR('N', 0x80, struct nvme_uring_cmd)
--
2.30.2
^ permalink raw reply related [flat|nested] 5+ messages in thread