* [PATCH 0/6] ublk_drv: add mechanism for supporting unprivileged ublk device
@ 2022-11-16 6:08 Ming Lei
2022-11-16 6:08 ` [PATCH 1/6] ublk_drv: remove nr_aborted_queues from ublk_device Ming Lei
` (5 more replies)
0 siblings, 6 replies; 13+ messages in thread
From: Ming Lei @ 2022-11-16 6:08 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, ZiyangZhang, Stefan Hajnoczi, Ming Lei
Hello,
Stefan Hajnoczi suggested un-privileged ublk device[1] for container
use case.
So far only administrator can create/control ublk device which is too
strict and increase system administrator burden, and this patchset
implements un-privileged ublk device:
- any user can create ublk device, which can only be controlled &
accessed by the owner of the device or administrator
For using such mechanism, system administrator needs to deploy two
simple udev rules[2] after running 'make install' in ublksrv.
Userspace(ublksrv):
https://github.com/ming1/ubdsrv/tree/unprivileged-ublk
'ublk add -t $TYPE --un_privileged=1' is for creating one un-privileged
ublk device if the user is un-privileged.
[1] https://lore.kernel.org/linux-block/YoOr6jBfgVm8GvWg@stefanha-x1.localdomain/
[2] https://github.com/ming1/ubdsrv/blob/unprivileged-ublk/README.rst#un-privileged-mode
Ming Lei (6):
ublk_drv: remove nr_aborted_queues from ublk_device
ublk_drv: don't probe partitions if the ubq daemon isn't trusted
ublk_drv: move ublk_get_device_from_id into ublk_ctrl_uring_cmd
ublk_drv: add device parameter UBLK_PARAM_TYPE_DEVT
ublk_drv: add module parameter of ublks_max for limiting max allowed
ublk dev
ublk_drv: add mechanism for supporting unprivileged ublk device
Documentation/block/ublk.rst | 18 +-
drivers/block/ublk_drv.c | 340 ++++++++++++++++++++++++----------
include/uapi/linux/ublk_cmd.h | 49 ++++-
3 files changed, 299 insertions(+), 108 deletions(-)
--
2.31.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/6] ublk_drv: remove nr_aborted_queues from ublk_device
2022-11-16 6:08 [PATCH 0/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
@ 2022-11-16 6:08 ` Ming Lei
2022-11-18 9:02 ` Ziyang Zhang
2022-11-16 6:08 ` [PATCH 2/6] ublk_drv: don't probe partitions if the ubq daemon isn't trusted Ming Lei
` (4 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Ming Lei @ 2022-11-16 6:08 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, ZiyangZhang, Stefan Hajnoczi, Ming Lei
No one uses 'nr_aborted_queues' any more, so remove it.
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
drivers/block/ublk_drv.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index f96cb01e9604..fe997848c1ff 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -161,7 +161,6 @@ struct ublk_device {
struct completion completion;
unsigned int nr_queues_ready;
- atomic_t nr_aborted_queues;
/*
* Our ubq->daemon may be killed without any notification, so
--
2.31.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/6] ublk_drv: don't probe partitions if the ubq daemon isn't trusted
2022-11-16 6:08 [PATCH 0/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
2022-11-16 6:08 ` [PATCH 1/6] ublk_drv: remove nr_aborted_queues from ublk_device Ming Lei
@ 2022-11-16 6:08 ` Ming Lei
2022-11-18 10:03 ` Ziyang Zhang
2022-11-16 6:08 ` [PATCH 3/6] ublk_drv: move ublk_get_device_from_id into ublk_ctrl_uring_cmd Ming Lei
` (3 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Ming Lei @ 2022-11-16 6:08 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, ZiyangZhang, Stefan Hajnoczi, Ming Lei
If any ubq daemon is unprivileged, the ublk char device is allowed
for unprivileged user, and we can't trust the current user, so not
probe partitions.
Fixes: 71f28f3136af ("ublk_drv: add io_uring based userspace block driver")
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
drivers/block/ublk_drv.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index fe997848c1ff..a5f3d8330be5 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -149,6 +149,7 @@ struct ublk_device {
#define UB_STATE_OPEN 0
#define UB_STATE_USED 1
+#define UB_STATE_PRIVILEGED 2
unsigned long state;
int ub_number;
@@ -161,6 +162,7 @@ struct ublk_device {
struct completion completion;
unsigned int nr_queues_ready;
+ unsigned int nr_privileged_daemon;
/*
* Our ubq->daemon may be killed without any notification, so
@@ -1184,9 +1186,15 @@ static void ublk_mark_io_ready(struct ublk_device *ub, struct ublk_queue *ubq)
ubq->ubq_daemon = current;
get_task_struct(ubq->ubq_daemon);
ub->nr_queues_ready++;
+
+ if (capable(CAP_SYS_ADMIN))
+ ub->nr_privileged_daemon++;
}
- if (ub->nr_queues_ready == ub->dev_info.nr_hw_queues)
+ if (ub->nr_queues_ready == ub->dev_info.nr_hw_queues) {
+ if (ub->nr_privileged_daemon == ub->nr_queues_ready)
+ set_bit(UB_STATE_PRIVILEGED, &ub->state);
complete_all(&ub->completion);
+ }
mutex_unlock(&ub->mutex);
}
@@ -1540,6 +1548,10 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
if (ret)
goto out_put_disk;
+ /* don't probe partitions if any one ubq daemon is un-trusted */
+ if (!test_bit(UB_STATE_PRIVILEGED, &ub->state))
+ set_bit(GD_SUPPRESS_PART_SCAN, &disk->state);
+
get_device(&ub->cdev_dev);
ret = add_disk(disk);
if (ret) {
--
2.31.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 3/6] ublk_drv: move ublk_get_device_from_id into ublk_ctrl_uring_cmd
2022-11-16 6:08 [PATCH 0/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
2022-11-16 6:08 ` [PATCH 1/6] ublk_drv: remove nr_aborted_queues from ublk_device Ming Lei
2022-11-16 6:08 ` [PATCH 2/6] ublk_drv: don't probe partitions if the ubq daemon isn't trusted Ming Lei
@ 2022-11-16 6:08 ` Ming Lei
2022-11-16 6:08 ` [PATCH 4/6] ublk_drv: add device parameter UBLK_PARAM_TYPE_DEVT Ming Lei
` (2 subsequent siblings)
5 siblings, 0 replies; 13+ messages in thread
From: Ming Lei @ 2022-11-16 6:08 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, ZiyangZhang, Stefan Hajnoczi, Ming Lei
It is annoying for each control command handler to get/put ublk
device and deal with failure.
Control command handler is simplified a lot by moving
ublk_get_device_from_id into ublk_ctrl_uring_cmd().
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
drivers/block/ublk_drv.c | 138 ++++++++++++++-------------------------
1 file changed, 49 insertions(+), 89 deletions(-)
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index a5f3d8330be5..d987f76d436f 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -1506,21 +1506,16 @@ static struct ublk_device *ublk_get_device_from_id(int idx)
return ub;
}
-static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
+static int ublk_ctrl_start_dev(struct ublk_device *ub, struct io_uring_cmd *cmd)
{
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
int ublksrv_pid = (int)header->data[0];
- struct ublk_device *ub;
struct gendisk *disk;
int ret = -EINVAL;
if (ublksrv_pid <= 0)
return -EINVAL;
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
wait_for_completion_interruptible(&ub->completion);
schedule_delayed_work(&ub->monitor_work, UBLK_DAEMON_MONITOR_PERIOD);
@@ -1569,21 +1564,20 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
put_disk(disk);
out_unlock:
mutex_unlock(&ub->mutex);
- ublk_put_device(ub);
return ret;
}
-static int ublk_ctrl_get_queue_affinity(struct io_uring_cmd *cmd)
+static int ublk_ctrl_get_queue_affinity(struct ublk_device *ub,
+ struct io_uring_cmd *cmd)
{
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
void __user *argp = (void __user *)(unsigned long)header->addr;
- struct ublk_device *ub;
cpumask_var_t cpumask;
unsigned long queue;
unsigned int retlen;
unsigned int i;
- int ret = -EINVAL;
-
+ int ret;
+
if (header->len * BITS_PER_BYTE < nr_cpu_ids)
return -EINVAL;
if (header->len & (sizeof(unsigned long)-1))
@@ -1591,17 +1585,12 @@ static int ublk_ctrl_get_queue_affinity(struct io_uring_cmd *cmd)
if (!header->addr)
return -EINVAL;
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
queue = header->data[0];
if (queue >= ub->dev_info.nr_hw_queues)
- goto out_put_device;
+ return -EINVAL;
- ret = -ENOMEM;
if (!zalloc_cpumask_var(&cpumask, GFP_KERNEL))
- goto out_put_device;
+ return -ENOMEM;
for_each_possible_cpu(i) {
if (ub->tag_set.map[HCTX_TYPE_DEFAULT].mq_map[i] == queue)
@@ -1619,8 +1608,6 @@ static int ublk_ctrl_get_queue_affinity(struct io_uring_cmd *cmd)
ret = 0;
out_free_cpumask:
free_cpumask_var(cpumask);
-out_put_device:
- ublk_put_device(ub);
return ret;
}
@@ -1741,30 +1728,27 @@ static inline bool ublk_idr_freed(int id)
return ptr == NULL;
}
-static int ublk_ctrl_del_dev(int idx)
+static int ublk_ctrl_del_dev(struct ublk_device **p_ub)
{
- struct ublk_device *ub;
+ struct ublk_device *ub = *p_ub;
+ int idx = ub->ub_number;
int ret;
ret = mutex_lock_killable(&ublk_ctl_mutex);
if (ret)
return ret;
- ub = ublk_get_device_from_id(idx);
- if (ub) {
- ublk_remove(ub);
- ublk_put_device(ub);
- ret = 0;
- } else {
- ret = -ENODEV;
- }
+ ublk_remove(ub);
+
+ /* Mark the reference as consumed */
+ *p_ub = NULL;
+ ublk_put_device(ub);
/*
* Wait until the idr is removed, then it can be reused after
* DEL_DEV command is returned.
*/
- if (!ret)
- wait_event(ublk_idr_wq, ublk_idr_freed(idx));
+ wait_event(ublk_idr_wq, ublk_idr_freed(idx));
mutex_unlock(&ublk_ctl_mutex);
return ret;
@@ -1779,50 +1763,36 @@ static inline void ublk_ctrl_cmd_dump(struct io_uring_cmd *cmd)
header->data[0], header->addr, header->len);
}
-static int ublk_ctrl_stop_dev(struct io_uring_cmd *cmd)
+static int ublk_ctrl_stop_dev(struct ublk_device *ub)
{
- struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
- struct ublk_device *ub;
-
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
ublk_stop_dev(ub);
cancel_work_sync(&ub->stop_work);
cancel_work_sync(&ub->quiesce_work);
- ublk_put_device(ub);
return 0;
}
-static int ublk_ctrl_get_dev_info(struct io_uring_cmd *cmd)
+static int ublk_ctrl_get_dev_info(struct ublk_device *ub,
+ struct io_uring_cmd *cmd)
{
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
void __user *argp = (void __user *)(unsigned long)header->addr;
- struct ublk_device *ub;
- int ret = 0;
if (header->len < sizeof(struct ublksrv_ctrl_dev_info) || !header->addr)
return -EINVAL;
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
if (copy_to_user(argp, &ub->dev_info, sizeof(ub->dev_info)))
- ret = -EFAULT;
- ublk_put_device(ub);
+ return -EFAULT;
- return ret;
+ return 0;
}
-static int ublk_ctrl_get_params(struct io_uring_cmd *cmd)
+static int ublk_ctrl_get_params(struct ublk_device *ub,
+ struct io_uring_cmd *cmd)
{
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
void __user *argp = (void __user *)(unsigned long)header->addr;
struct ublk_params_header ph;
- struct ublk_device *ub;
int ret;
if (header->len <= sizeof(ph) || !header->addr)
@@ -1837,10 +1807,6 @@ static int ublk_ctrl_get_params(struct io_uring_cmd *cmd)
if (ph.len > sizeof(struct ublk_params))
ph.len = sizeof(struct ublk_params);
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
mutex_lock(&ub->mutex);
if (copy_to_user(argp, &ub->params, ph.len))
ret = -EFAULT;
@@ -1848,16 +1814,15 @@ static int ublk_ctrl_get_params(struct io_uring_cmd *cmd)
ret = 0;
mutex_unlock(&ub->mutex);
- ublk_put_device(ub);
return ret;
}
-static int ublk_ctrl_set_params(struct io_uring_cmd *cmd)
+static int ublk_ctrl_set_params(struct ublk_device *ub,
+ struct io_uring_cmd *cmd)
{
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
void __user *argp = (void __user *)(unsigned long)header->addr;
struct ublk_params_header ph;
- struct ublk_device *ub;
int ret = -EFAULT;
if (header->len <= sizeof(ph) || !header->addr)
@@ -1872,10 +1837,6 @@ static int ublk_ctrl_set_params(struct io_uring_cmd *cmd)
if (ph.len > sizeof(struct ublk_params))
ph.len = sizeof(struct ublk_params);
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return -EINVAL;
-
/* parameters can only be changed when device isn't live */
mutex_lock(&ub->mutex);
if (ub->dev_info.state == UBLK_S_DEV_LIVE) {
@@ -1888,7 +1849,6 @@ static int ublk_ctrl_set_params(struct io_uring_cmd *cmd)
ret = ublk_validate_params(ub);
}
mutex_unlock(&ub->mutex);
- ublk_put_device(ub);
return ret;
}
@@ -1915,17 +1875,13 @@ static void ublk_queue_reinit(struct ublk_device *ub, struct ublk_queue *ubq)
}
}
-static int ublk_ctrl_start_recovery(struct io_uring_cmd *cmd)
+static int ublk_ctrl_start_recovery(struct ublk_device *ub,
+ struct io_uring_cmd *cmd)
{
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
- struct ublk_device *ub;
int ret = -EINVAL;
int i;
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return ret;
-
mutex_lock(&ub->mutex);
if (!ublk_can_use_recovery(ub))
goto out_unlock;
@@ -1957,21 +1913,16 @@ static int ublk_ctrl_start_recovery(struct io_uring_cmd *cmd)
ret = 0;
out_unlock:
mutex_unlock(&ub->mutex);
- ublk_put_device(ub);
return ret;
}
-static int ublk_ctrl_end_recovery(struct io_uring_cmd *cmd)
+static int ublk_ctrl_end_recovery(struct ublk_device *ub,
+ struct io_uring_cmd *cmd)
{
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
int ublksrv_pid = (int)header->data[0];
- struct ublk_device *ub;
int ret = -EINVAL;
- ub = ublk_get_device_from_id(header->dev_id);
- if (!ub)
- return ret;
-
pr_devel("%s: Waiting for new ubq_daemons(nr: %d) are ready, dev id %d...\n",
__func__, ub->dev_info.nr_hw_queues, header->dev_id);
/* wait until new ubq_daemon sending all FETCH_REQ */
@@ -1999,7 +1950,6 @@ static int ublk_ctrl_end_recovery(struct io_uring_cmd *cmd)
ret = 0;
out_unlock:
mutex_unlock(&ub->mutex);
- ublk_put_device(ub);
return ret;
}
@@ -2007,6 +1957,7 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
unsigned int issue_flags)
{
struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+ struct ublk_device *ub = NULL;
int ret = -EINVAL;
ublk_ctrl_cmd_dump(cmd);
@@ -2018,41 +1969,50 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
if (!capable(CAP_SYS_ADMIN))
goto out;
- ret = -ENODEV;
+ if (cmd->cmd_op != UBLK_CMD_ADD_DEV) {
+ ret = -ENODEV;
+ ub = ublk_get_device_from_id(header->dev_id);
+ if (!ub)
+ goto out;
+ }
+
switch (cmd->cmd_op) {
case UBLK_CMD_START_DEV:
- ret = ublk_ctrl_start_dev(cmd);
+ ret = ublk_ctrl_start_dev(ub, cmd);
break;
case UBLK_CMD_STOP_DEV:
- ret = ublk_ctrl_stop_dev(cmd);
+ ret = ublk_ctrl_stop_dev(ub);
break;
case UBLK_CMD_GET_DEV_INFO:
- ret = ublk_ctrl_get_dev_info(cmd);
+ ret = ublk_ctrl_get_dev_info(ub, cmd);
break;
case UBLK_CMD_ADD_DEV:
ret = ublk_ctrl_add_dev(cmd);
break;
case UBLK_CMD_DEL_DEV:
- ret = ublk_ctrl_del_dev(header->dev_id);
+ ret = ublk_ctrl_del_dev(&ub);
break;
case UBLK_CMD_GET_QUEUE_AFFINITY:
- ret = ublk_ctrl_get_queue_affinity(cmd);
+ ret = ublk_ctrl_get_queue_affinity(ub, cmd);
break;
case UBLK_CMD_GET_PARAMS:
- ret = ublk_ctrl_get_params(cmd);
+ ret = ublk_ctrl_get_params(ub, cmd);
break;
case UBLK_CMD_SET_PARAMS:
- ret = ublk_ctrl_set_params(cmd);
+ ret = ublk_ctrl_set_params(ub, cmd);
break;
case UBLK_CMD_START_USER_RECOVERY:
- ret = ublk_ctrl_start_recovery(cmd);
+ ret = ublk_ctrl_start_recovery(ub, cmd);
break;
case UBLK_CMD_END_USER_RECOVERY:
- ret = ublk_ctrl_end_recovery(cmd);
+ ret = ublk_ctrl_end_recovery(ub, cmd);
break;
default:
+ ret = -ENOTSUPP;
break;
}
+ if (ub)
+ ublk_put_device(ub);
out:
io_uring_cmd_done(cmd, ret, 0);
pr_devel("%s: cmd done ret %d cmd_op %x, dev id %d qid %d\n",
--
2.31.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 4/6] ublk_drv: add device parameter UBLK_PARAM_TYPE_DEVT
2022-11-16 6:08 [PATCH 0/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
` (2 preceding siblings ...)
2022-11-16 6:08 ` [PATCH 3/6] ublk_drv: move ublk_get_device_from_id into ublk_ctrl_uring_cmd Ming Lei
@ 2022-11-16 6:08 ` Ming Lei
2022-11-16 6:08 ` [PATCH 5/6] ublk_drv: add module parameter of ublks_max for limiting max allowed ublk dev Ming Lei
2022-11-16 6:08 ` [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
5 siblings, 0 replies; 13+ messages in thread
From: Ming Lei @ 2022-11-16 6:08 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, ZiyangZhang, Stefan Hajnoczi, Ming Lei
Userspace side only knows device ID, but the associated path of ublkc* and
ublkb* could be changed by udev, and that depends on userspace's policy, so
add parameter of UBLK_PARAM_TYPE_DEVT for retrieving major/minor of the
ublkc* and ublkb*, then user may figure out major/minor of the ublk disks
he/she owns. With major/minor, it is easy to find the device node path.
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
drivers/block/ublk_drv.c | 24 +++++++++++++++++++++++-
include/uapi/linux/ublk_cmd.h | 13 +++++++++++++
2 files changed, 36 insertions(+), 1 deletion(-)
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index d987f76d436f..d047b26c1e93 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -54,7 +54,8 @@
| UBLK_F_USER_RECOVERY_REISSUE)
/* All UBLK_PARAM_TYPE_* should be included here */
-#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD)
+#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | \
+ UBLK_PARAM_TYPE_DISCARD | UBLK_PARAM_TYPE_DEVT)
struct ublk_rq_data {
union {
@@ -258,6 +259,10 @@ static int ublk_validate_params(const struct ublk_device *ub)
return -EINVAL;
}
+ /* dev_t is read-only */
+ if (ub->params.types & UBLK_PARAM_TYPE_DEVT)
+ WARN_ON_ONCE(1);
+
return 0;
}
@@ -1787,6 +1792,22 @@ static int ublk_ctrl_get_dev_info(struct ublk_device *ub,
return 0;
}
+/* TYPE_DEVT is readonly, so fill it up before returning to userspace */
+static void ublk_ctrl_fill_params_devt(struct ublk_device *ub)
+{
+ ub->params.devt.char_major = MAJOR(ub->cdev_dev.devt);
+ ub->params.devt.char_minor = MINOR(ub->cdev_dev.devt);
+
+ if (ub->ub_disk) {
+ ub->params.devt.disk_major = MAJOR(disk_devt(ub->ub_disk));
+ ub->params.devt.disk_minor = MINOR(disk_devt(ub->ub_disk));
+ } else {
+ ub->params.devt.disk_major = 0;
+ ub->params.devt.disk_minor = 0;
+ }
+ ub->params.types |= UBLK_PARAM_TYPE_DEVT;
+}
+
static int ublk_ctrl_get_params(struct ublk_device *ub,
struct io_uring_cmd *cmd)
{
@@ -1808,6 +1829,7 @@ static int ublk_ctrl_get_params(struct ublk_device *ub,
ph.len = sizeof(struct ublk_params);
mutex_lock(&ub->mutex);
+ ublk_ctrl_fill_params_devt(ub);
if (copy_to_user(argp, &ub->params, ph.len))
ret = -EFAULT;
else
diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
index 8f88e3a29998..4e38b9aa0293 100644
--- a/include/uapi/linux/ublk_cmd.h
+++ b/include/uapi/linux/ublk_cmd.h
@@ -214,6 +214,17 @@ struct ublk_param_discard {
__u16 reserved0;
};
+/*
+ * read-only, can't set via UBLK_CMD_SET_PARAMS, disk_devt is available
+ * after device is started
+ */
+struct ublk_param_devt {
+ __u32 char_major;
+ __u32 char_minor;
+ __u32 disk_major;
+ __u32 disk_minor;
+};
+
struct ublk_params {
/*
* Total length of parameters, userspace has to set 'len' for both
@@ -224,10 +235,12 @@ struct ublk_params {
__u32 len;
#define UBLK_PARAM_TYPE_BASIC (1 << 0)
#define UBLK_PARAM_TYPE_DISCARD (1 << 1)
+#define UBLK_PARAM_TYPE_DEVT (1 << 2)
__u32 types; /* types of parameter included */
struct ublk_param_basic basic;
struct ublk_param_discard discard;
+ struct ublk_param_devt devt;
};
#endif
--
2.31.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 5/6] ublk_drv: add module parameter of ublks_max for limiting max allowed ublk dev
2022-11-16 6:08 [PATCH 0/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
` (3 preceding siblings ...)
2022-11-16 6:08 ` [PATCH 4/6] ublk_drv: add device parameter UBLK_PARAM_TYPE_DEVT Ming Lei
@ 2022-11-16 6:08 ` Ming Lei
2022-11-16 6:08 ` [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
5 siblings, 0 replies; 13+ messages in thread
From: Ming Lei @ 2022-11-16 6:08 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, ZiyangZhang, Stefan Hajnoczi, Ming Lei
Prepare for supporting unprivileged ublk device by limiting max number
ublk devices added. Otherwise too many ublk devices could be added by
un-trusted user, which can be thought as one DoS.
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
drivers/block/ublk_drv.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index d047b26c1e93..28e9f1a19c9e 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -189,6 +189,15 @@ static wait_queue_head_t ublk_idr_wq; /* wait until one idr is freed */
static DEFINE_MUTEX(ublk_ctl_mutex);
+/*
+ * Max ublk devices allowed to add
+ *
+ * It can be extended to one per-user limit in future or even controlled
+ * by cgroup.
+ */
+static unsigned int ublks_max = 64;
+static unsigned int ublks_added; /* protected by ublk_ctl_mutex */
+
static struct miscdevice ublk_misc;
static void ublk_dev_param_basic_apply(struct ublk_device *ub)
@@ -1451,6 +1460,8 @@ static int ublk_add_chdev(struct ublk_device *ub)
ret = cdev_device_add(&ub->cdev, dev);
if (ret)
goto fail;
+
+ ublks_added++;
return 0;
fail:
put_device(dev);
@@ -1493,6 +1504,7 @@ static void ublk_remove(struct ublk_device *ub)
cancel_work_sync(&ub->quiesce_work);
cdev_device_del(&ub->cdev, &ub->cdev_dev);
put_device(&ub->cdev_dev);
+ ublks_added--;
}
static struct ublk_device *ublk_get_device_from_id(int idx)
@@ -1652,6 +1664,10 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
if (ret)
return ret;
+ ret = -EACCES;
+ if (ublks_added >= ublks_max)
+ goto out_unlock;
+
ret = -ENOMEM;
ub = kzalloc(sizeof(*ub), GFP_KERNEL);
if (!ub)
@@ -2102,5 +2118,8 @@ static void __exit ublk_exit(void)
module_init(ublk_init);
module_exit(ublk_exit);
+module_param(ublks_max, int, 0444);
+MODULE_PARM_DESC(ublks_max, "max number of ublk devices allowed to add(default: 64)");
+
MODULE_AUTHOR("Ming Lei <ming.lei@redhat.com>");
MODULE_LICENSE("GPL");
--
2.31.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device
2022-11-16 6:08 [PATCH 0/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
` (4 preceding siblings ...)
2022-11-16 6:08 ` [PATCH 5/6] ublk_drv: add module parameter of ublks_max for limiting max allowed ublk dev Ming Lei
@ 2022-11-16 6:08 ` Ming Lei
2022-11-17 13:17 ` Ming Lei
2022-11-22 7:17 ` Dan Carpenter
5 siblings, 2 replies; 13+ messages in thread
From: Ming Lei @ 2022-11-16 6:08 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, ZiyangZhang, Stefan Hajnoczi, Ming Lei
unprivileged ublk device is helpful for container use case, such
as: ublk device created in one unprivileged container can be controlled
and accessed by this container only.
Implement this feature by adding flag of UBLK_F_UNPRIVILEGED_DEV, and if
this flag isn't set, any control command has been run from privileged
user. Otherwise, any control command can be sent from any unprivileged
user, but the user has to be permitted to access the ublk char device
to be controlled.
In case of UBLK_F_UNPRIVILEGED_DEV:
1) for command UBLK_CMD_ADD_DEV, it is always allowed, and user needs
to provide owner's uid/gid in this command, so that udev can set correct
ownership for the created ublk device, since the device owner uid/gid
can be queried via command of UBLK_CMD_GET_DEV_INFO.
2) for other control commands, they can only be run successfully if the
current user is allowed to access the specified ublk char device, for
running the permission check, path of the ublk char device has to be
provided by these commands.
Also add one control of command UBLK_CMD_GET_DEV_INFO2 which always
include the char dev path in payload since userspace may not have
knowledge if this device is created in unprivileged mode.
For applying this mechanism, system administrator needs to take
the following policies:
1) chmod 0666 /dev/ublk-control
2) change ownership of ublkcN & ublkbN
- chown owner_uid:owner_gid /dev/ublkcN
- chown owner_uid:owner_gid /dev/ublkbN
Both can be done via one simple udev rule.
Userspace:
https://github.com/ming1/ubdsrv/tree/unprivileged-ublk
'ublk add -t $TYPE --un_privileged=1' is for creating one un-privileged
ublk device if the user is un-privileged.
Link: https://lore.kernel.org/linux-block/YoOr6jBfgVm8GvWg@stefanha-x1.localdomain/
Suggested-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
Documentation/block/ublk.rst | 18 ++---
drivers/block/ublk_drv.c | 146 ++++++++++++++++++++++++++++++++--
include/uapi/linux/ublk_cmd.h | 36 ++++++++-
3 files changed, 183 insertions(+), 17 deletions(-)
diff --git a/Documentation/block/ublk.rst b/Documentation/block/ublk.rst
index ba45c46cc0da..403ffd0f4511 100644
--- a/Documentation/block/ublk.rst
+++ b/Documentation/block/ublk.rst
@@ -180,6 +180,15 @@ managing and controlling ublk devices with help of several control commands:
double-write since the driver may issue the same I/O request twice. It
might be useful to a read-only FS or a VM backend.
+Unprivileged ublk device is supported by passing UBLK_F_UNPRIVILEGED_DEV.
+Once the flag is set, all control commands can be sent by unprivileged
+user. Except for command of ``UBLK_CMD_ADD_DEV``, permission check on
+the specified char device(``/dev/ublkc*``) is done for all other control
+commands by ublk driver, for doing that, path of the char device has to
+be provided in these commands' payload from ublk server. With this way,
+ublk device becomes container-ware, and device created in one container
+can be controlled/accessed just inside this container.
+
Data plane
----------
@@ -254,15 +263,6 @@ with specified IO tag in the command data:
Future development
==================
-Container-aware ublk deivice
-----------------------------
-
-ublk driver doesn't handle any IO logic. Its function is well defined
-for now and very limited userspace interfaces are needed, which is also
-well defined too. It is possible to make ublk devices container-aware block
-devices in future as Stefan Hajnoczi suggested [#stefan]_, by removing
-ADMIN privilege.
-
Zero copy
---------
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 28e9f1a19c9e..a21b323711d0 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -42,6 +42,7 @@
#include <linux/mm.h>
#include <asm/page.h>
#include <linux/task_work.h>
+#include <linux/namei.h>
#include <uapi/linux/ublk_cmd.h>
#define UBLK_MINORS (1U << MINORBITS)
@@ -51,7 +52,8 @@
| UBLK_F_URING_CMD_COMP_IN_TASK \
| UBLK_F_NEED_GET_DATA \
| UBLK_F_USER_RECOVERY \
- | UBLK_F_USER_RECOVERY_REISSUE)
+ | UBLK_F_USER_RECOVERY_REISSUE \
+ | UBLK_F_UNPRIVILEGED_DEV)
/* All UBLK_PARAM_TYPE_* should be included here */
#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | \
@@ -1651,15 +1653,33 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
__func__, header->queue_id);
return -EINVAL;
}
+
if (copy_from_user(&info, argp, sizeof(info)))
return -EFAULT;
- ublk_dump_dev_info(&info);
+
+ if (info.flags & UBLK_F_UNPRIVILEGED_DEV) {
+ /*
+ * user needs to provide proper owner_uid/owner_gid,
+ * which will be provided to udev rule for setting
+ * ownership on the ublk device to be created.
+ */
+ ;
+ } else {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (info.owner_uid != 0 || info.owner_gid != 0)
+ return -EINVAL;
+ }
+
if (header->dev_id != info.dev_id) {
pr_warn("%s: dev id not match %u %u\n",
__func__, header->dev_id, info.dev_id);
return -EINVAL;
}
+ ublk_dump_dev_info(&info);
+
ret = mutex_lock_killable(&ublk_ctl_mutex);
if (ret)
return ret;
@@ -1991,6 +2011,113 @@ static int ublk_ctrl_end_recovery(struct ublk_device *ub,
return ret;
}
+/*
+ * All control commands are sent via /dev/ublk-control, so we have to check
+ * the destination device's permission
+ */
+static int ublk_char_dev_permission(struct ublk_device *ub,
+ const char *dev_path, int mask)
+{
+ int err;
+ struct path path;
+ struct kstat stat;
+
+ err = kern_path(dev_path, LOOKUP_FOLLOW, &path);
+ if (err)
+ return err;
+
+ err = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
+ if (err)
+ goto exit;
+
+ if (stat.rdev != ub->cdev_dev.devt || !S_ISCHR(stat.mode))
+ goto exit;
+
+ err = inode_permission(&init_user_ns,
+ d_backing_inode(path.dentry), mask);
+exit:
+ path_put(&path);
+ return err;
+}
+
+static int ublk_ctrl_uring_cmd_permission(struct ublk_device *ub,
+ struct io_uring_cmd *cmd)
+{
+ struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+ bool unprivileged = ub->dev_info.flags & UBLK_F_UNPRIVILEGED_DEV;
+ void __user *argp = (void __user *)(unsigned long)header->addr;
+ char *dev_path = NULL;
+ int ret = 0;
+ int mask;
+
+ if (!unprivileged) {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ /*
+ * The new added command of UBLK_CMD_GET_DEV_INFO2 includes
+ * char_dev_path in payload too, since userspace may not
+ * know if the specified device is created as unprivileged
+ * mode.
+ */
+ if (cmd->cmd_op != UBLK_CMD_GET_DEV_INFO2)
+ return 0;
+ }
+
+ /*
+ * User has to provide the char device path for unprivileged ublk
+ *
+ * header->addr always points to the dev path buffer, and
+ * header->dev_path_len records length of dev path buffer.
+ */
+ if (!header->dev_path_len || header->dev_path_len > PATH_MAX)
+ return -EINVAL;
+
+ if (header->len < header->dev_path_len)
+ return -EINVAL;
+
+ dev_path = kmalloc(header->dev_path_len, GFP_KERNEL);
+ if (!dev_path)
+ return -ENOMEM;
+
+ ret = -EFAULT;
+ if (copy_from_user(dev_path, argp, header->dev_path_len))
+ goto exit;
+ dev_path[header->dev_path_len] = 0;
+
+ switch (cmd->cmd_op) {
+ case UBLK_CMD_GET_DEV_INFO:
+ case UBLK_CMD_GET_DEV_INFO2:
+ case UBLK_CMD_GET_QUEUE_AFFINITY:
+ case UBLK_CMD_GET_PARAMS:
+ mask = MAY_READ;
+ break;
+ case UBLK_CMD_START_DEV:
+ case UBLK_CMD_STOP_DEV:
+ case UBLK_CMD_ADD_DEV:
+ case UBLK_CMD_DEL_DEV:
+ case UBLK_CMD_SET_PARAMS:
+ case UBLK_CMD_START_USER_RECOVERY:
+ case UBLK_CMD_END_USER_RECOVERY:
+ mask = MAY_READ | MAY_WRITE;
+ break;
+ default:
+ break;
+ }
+
+ ret = ublk_char_dev_permission(ub, dev_path, mask);
+ if (!ret) {
+ header->len -= header->dev_path_len;
+ header->addr += header->dev_path_len;
+ }
+ pr_devel("%s: dev id %d cmd_op %x uid %d gid %d path %s ret %d\n",
+ __func__, ub->ub_number, cmd->cmd_op,
+ ub->dev_info.owner_uid, ub->dev_info.owner_gid,
+ dev_path, ret);
+exit:
+ kfree(dev_path);
+ return ret;
+}
+
static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
unsigned int issue_flags)
{
@@ -2003,17 +2130,21 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
if (!(issue_flags & IO_URING_F_SQE128))
goto out;
- ret = -EPERM;
- if (!capable(CAP_SYS_ADMIN))
- goto out;
-
if (cmd->cmd_op != UBLK_CMD_ADD_DEV) {
ret = -ENODEV;
ub = ublk_get_device_from_id(header->dev_id);
if (!ub)
goto out;
+
+ ret = ublk_ctrl_uring_cmd_permission(ub, cmd);
+ } else {
+ /* ADD_DEV permission check is done in command handler */
+ ret = 0;
}
+ if (ret)
+ goto put_dev;
+
switch (cmd->cmd_op) {
case UBLK_CMD_START_DEV:
ret = ublk_ctrl_start_dev(ub, cmd);
@@ -2022,6 +2153,7 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
ret = ublk_ctrl_stop_dev(ub);
break;
case UBLK_CMD_GET_DEV_INFO:
+ case UBLK_CMD_GET_DEV_INFO2:
ret = ublk_ctrl_get_dev_info(ub, cmd);
break;
case UBLK_CMD_ADD_DEV:
@@ -2049,6 +2181,8 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
ret = -ENOTSUPP;
break;
}
+
+ put_dev:
if (ub)
ublk_put_device(ub);
out:
diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
index 4e38b9aa0293..ae80bfef3b9f 100644
--- a/include/uapi/linux/ublk_cmd.h
+++ b/include/uapi/linux/ublk_cmd.h
@@ -19,6 +19,8 @@
#define UBLK_CMD_GET_PARAMS 0x09
#define UBLK_CMD_START_USER_RECOVERY 0x10
#define UBLK_CMD_END_USER_RECOVERY 0x11
+#define UBLK_CMD_GET_DEV_INFO2 0x12
+
/*
* IO commands, issued by ublk server, and handled by ublk driver.
*
@@ -79,6 +81,27 @@
#define UBLK_F_USER_RECOVERY_REISSUE (1UL << 4)
+/*
+ * Unprivileged user can create /dev/ublkcN and /dev/ublkbN.
+ *
+ * /dev/ublk-control needs to be available for unprivileged user, and it
+ * can be done via udev rule to make all control commands available to
+ * unprivileged user. Except for the command of UBLK_CMD_ADD_DEV, all
+ * other commands are only allowed for the owner of the specified device.
+ *
+ * When userspace sends UBLK_CMD_ADD_DEV, the device pair's owner_uid and
+ * owner_gid need to be provided via ublksrv_ctrl_dev_info, and the two
+ * ids need to match with ublk server's uid/gid, otherwise the created
+ * ublk device can't be started successfully.
+ *
+ * We still need udev rule to set correct OWNER/GROUP with the stored
+ * owner_uid and owner_gid.
+ *
+ * Then ublk server can be run as unprivileged user, and /dev/ublkbN can
+ * be accessed by user of owner_uid/owner_gid.
+ */
+#define UBLK_F_UNPRIVILEGED_DEV (1UL << 5)
+
/* device state */
#define UBLK_S_DEV_DEAD 0
#define UBLK_S_DEV_LIVE 1
@@ -98,7 +121,15 @@ struct ublksrv_ctrl_cmd {
__u64 addr;
/* inline data */
- __u64 data[2];
+ __u64 data[1];
+
+ /*
+ * Used for UBLK_F_UNPRIVILEGED_DEV and UBLK_CMD_GET_DEV_INFO2
+ * only, include null char
+ */
+ __u16 dev_path_len;
+ __u16 pad;
+ __u32 reserved;
};
struct ublksrv_ctrl_dev_info {
@@ -118,7 +149,8 @@ struct ublksrv_ctrl_dev_info {
/* For ublksrv internal use, invisible to ublk driver */
__u64 ublksrv_flags;
- __u64 reserved0;
+ __u32 owner_uid;
+ __u32 owner_gid;
__u64 reserved1;
__u64 reserved2;
};
--
2.31.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device
2022-11-16 6:08 ` [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
@ 2022-11-17 13:17 ` Ming Lei
2022-11-22 7:17 ` Dan Carpenter
1 sibling, 0 replies; 13+ messages in thread
From: Ming Lei @ 2022-11-17 13:17 UTC (permalink / raw)
To: Jens Axboe; +Cc: linux-block, ZiyangZhang, Stefan Hajnoczi, ming.lei
On Wed, Nov 16, 2022 at 02:08:35PM +0800, Ming Lei wrote:
> unprivileged ublk device is helpful for container use case, such
> as: ublk device created in one unprivileged container can be controlled
> and accessed by this container only.
BTW, this patch may cause kernel panic after allocating one un-privileged
device for a while, and one security hole, follows the fixed version:
From 0edcd7d7513cab2982b37d8e9a7224762f42e262 Mon Sep 17 00:00:00 2001
From: Ming Lei <ming.lei@redhat.com>
Date: Mon, 14 Nov 2022 07:50:41 +0000
Subject: [PATCH fixed] ublk_drv: add mechanism for supporting unprivileged ublk
device
unprivileged ublk device is helpful for container use case, such
as: ublk device created in one unprivileged container can be controlled
and accessed by this container only.
Implement this feature by adding flag of UBLK_F_UNPRIVILEGED_DEV, and if
this flag isn't set, any control command has been run from privileged
user. Otherwise, any control command can be sent from any unprivileged
user, but the user has to be permitted to access the ublk char device
to be controlled.
In case of UBLK_F_UNPRIVILEGED_DEV:
1) for command UBLK_CMD_ADD_DEV, it is always allowed, and user needs
to provide owner's uid/gid in this command, so that udev can set correct
ownership for the created ublk device, since the device owner uid/gid
can be queried via command of UBLK_CMD_GET_DEV_INFO.
2) for other control commands, they can only be run successfully if the
current user is allowed to access the specified ublk char device, for
running the permission check, path of the ublk char device has to be
provided by these commands.
Also add one control of command UBLK_CMD_GET_DEV_INFO2 which always
include the char dev path in payload since userspace may not have
knowledge if this device is created in unprivileged mode.
For applying this mechanism, system administrator needs to take
the following policies:
1) chmod 0666 /dev/ublk-control
2) change ownership of ublkcN & ublkbN
- chown owner_uid:owner_gid /dev/ublkcN
- chown owner_uid:owner_gid /dev/ublkbN
Both can be done via one simple udev rule.
Userspace:
https://github.com/ming1/ubdsrv/tree/unprivileged-ublk
'ublk add -t $TYPE --un_privileged' is for creating one un-privileged
ublk device.
Link: https://lore.kernel.org/linux-block/YoOr6jBfgVm8GvWg@stefanha-x1.localdomain/
Suggested-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
Documentation/block/ublk.rst | 18 ++---
drivers/block/ublk_drv.c | 147 ++++++++++++++++++++++++++++++++--
include/uapi/linux/ublk_cmd.h | 36 ++++++++-
3 files changed, 184 insertions(+), 17 deletions(-)
diff --git a/Documentation/block/ublk.rst b/Documentation/block/ublk.rst
index ba45c46cc0da..403ffd0f4511 100644
--- a/Documentation/block/ublk.rst
+++ b/Documentation/block/ublk.rst
@@ -180,6 +180,15 @@ managing and controlling ublk devices with help of several control commands:
double-write since the driver may issue the same I/O request twice. It
might be useful to a read-only FS or a VM backend.
+Unprivileged ublk device is supported by passing UBLK_F_UNPRIVILEGED_DEV.
+Once the flag is set, all control commands can be sent by unprivileged
+user. Except for command of ``UBLK_CMD_ADD_DEV``, permission check on
+the specified char device(``/dev/ublkc*``) is done for all other control
+commands by ublk driver, for doing that, path of the char device has to
+be provided in these commands' payload from ublk server. With this way,
+ublk device becomes container-ware, and device created in one container
+can be controlled/accessed just inside this container.
+
Data plane
----------
@@ -254,15 +263,6 @@ with specified IO tag in the command data:
Future development
==================
-Container-aware ublk deivice
-----------------------------
-
-ublk driver doesn't handle any IO logic. Its function is well defined
-for now and very limited userspace interfaces are needed, which is also
-well defined too. It is possible to make ublk devices container-aware block
-devices in future as Stefan Hajnoczi suggested [#stefan]_, by removing
-ADMIN privilege.
-
Zero copy
---------
diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
index 28e9f1a19c9e..69049172b036 100644
--- a/drivers/block/ublk_drv.c
+++ b/drivers/block/ublk_drv.c
@@ -42,6 +42,7 @@
#include <linux/mm.h>
#include <asm/page.h>
#include <linux/task_work.h>
+#include <linux/namei.h>
#include <uapi/linux/ublk_cmd.h>
#define UBLK_MINORS (1U << MINORBITS)
@@ -51,7 +52,8 @@
| UBLK_F_URING_CMD_COMP_IN_TASK \
| UBLK_F_NEED_GET_DATA \
| UBLK_F_USER_RECOVERY \
- | UBLK_F_USER_RECOVERY_REISSUE)
+ | UBLK_F_USER_RECOVERY_REISSUE \
+ | UBLK_F_UNPRIVILEGED_DEV)
/* All UBLK_PARAM_TYPE_* should be included here */
#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | \
@@ -1651,15 +1653,33 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd)
__func__, header->queue_id);
return -EINVAL;
}
+
if (copy_from_user(&info, argp, sizeof(info)))
return -EFAULT;
- ublk_dump_dev_info(&info);
+
+ if (info.flags & UBLK_F_UNPRIVILEGED_DEV) {
+ /*
+ * user needs to provide proper owner_uid/owner_gid,
+ * which will be provided to udev rule for setting
+ * ownership on the ublk device to be created.
+ */
+ ;
+ } else {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+
+ if (info.owner_uid != 0 || info.owner_gid != 0)
+ return -EINVAL;
+ }
+
if (header->dev_id != info.dev_id) {
pr_warn("%s: dev id not match %u %u\n",
__func__, header->dev_id, info.dev_id);
return -EINVAL;
}
+ ublk_dump_dev_info(&info);
+
ret = mutex_lock_killable(&ublk_ctl_mutex);
if (ret)
return ret;
@@ -1991,6 +2011,114 @@ static int ublk_ctrl_end_recovery(struct ublk_device *ub,
return ret;
}
+/*
+ * All control commands are sent via /dev/ublk-control, so we have to check
+ * the destination device's permission
+ */
+static int ublk_char_dev_permission(struct ublk_device *ub,
+ const char *dev_path, int mask)
+{
+ int err;
+ struct path path;
+ struct kstat stat;
+
+ err = kern_path(dev_path, LOOKUP_FOLLOW, &path);
+ if (err)
+ return err;
+
+ err = vfs_getattr(&path, &stat, STATX_TYPE, AT_STATX_SYNC_AS_STAT);
+ if (err)
+ goto exit;
+
+ err = -EINVAL;
+ if (stat.rdev != ub->cdev_dev.devt || !S_ISCHR(stat.mode))
+ goto exit;
+
+ err = inode_permission(&init_user_ns,
+ d_backing_inode(path.dentry), mask);
+exit:
+ path_put(&path);
+ return err;
+}
+
+static int ublk_ctrl_uring_cmd_permission(struct ublk_device *ub,
+ struct io_uring_cmd *cmd)
+{
+ struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
+ bool unprivileged = ub->dev_info.flags & UBLK_F_UNPRIVILEGED_DEV;
+ void __user *argp = (void __user *)(unsigned long)header->addr;
+ char *dev_path = NULL;
+ int ret = 0;
+ int mask;
+
+ if (!unprivileged) {
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ /*
+ * The new added command of UBLK_CMD_GET_DEV_INFO2 includes
+ * char_dev_path in payload too, since userspace may not
+ * know if the specified device is created as unprivileged
+ * mode.
+ */
+ if (cmd->cmd_op != UBLK_CMD_GET_DEV_INFO2)
+ return 0;
+ }
+
+ /*
+ * User has to provide the char device path for unprivileged ublk
+ *
+ * header->addr always points to the dev path buffer, and
+ * header->dev_path_len records length of dev path buffer.
+ */
+ if (!header->dev_path_len || header->dev_path_len > PATH_MAX)
+ return -EINVAL;
+
+ if (header->len < header->dev_path_len)
+ return -EINVAL;
+
+ dev_path = kmalloc(header->dev_path_len + 1, GFP_KERNEL);
+ if (!dev_path)
+ return -ENOMEM;
+
+ ret = -EFAULT;
+ if (copy_from_user(dev_path, argp, header->dev_path_len))
+ goto exit;
+ dev_path[header->dev_path_len] = 0;
+
+ switch (cmd->cmd_op) {
+ case UBLK_CMD_GET_DEV_INFO:
+ case UBLK_CMD_GET_DEV_INFO2:
+ case UBLK_CMD_GET_QUEUE_AFFINITY:
+ case UBLK_CMD_GET_PARAMS:
+ mask = MAY_READ;
+ break;
+ case UBLK_CMD_START_DEV:
+ case UBLK_CMD_STOP_DEV:
+ case UBLK_CMD_ADD_DEV:
+ case UBLK_CMD_DEL_DEV:
+ case UBLK_CMD_SET_PARAMS:
+ case UBLK_CMD_START_USER_RECOVERY:
+ case UBLK_CMD_END_USER_RECOVERY:
+ mask = MAY_READ | MAY_WRITE;
+ break;
+ default:
+ break;
+ }
+
+ ret = ublk_char_dev_permission(ub, dev_path, mask);
+ if (!ret) {
+ header->len -= header->dev_path_len;
+ header->addr += header->dev_path_len;
+ }
+ pr_devel("%s: dev id %d cmd_op %x uid %d gid %d path %s ret %d\n",
+ __func__, ub->ub_number, cmd->cmd_op,
+ ub->dev_info.owner_uid, ub->dev_info.owner_gid,
+ dev_path, ret);
+exit:
+ kfree(dev_path);
+ return ret;
+}
+
static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
unsigned int issue_flags)
{
@@ -2003,17 +2131,21 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
if (!(issue_flags & IO_URING_F_SQE128))
goto out;
- ret = -EPERM;
- if (!capable(CAP_SYS_ADMIN))
- goto out;
-
if (cmd->cmd_op != UBLK_CMD_ADD_DEV) {
ret = -ENODEV;
ub = ublk_get_device_from_id(header->dev_id);
if (!ub)
goto out;
+
+ ret = ublk_ctrl_uring_cmd_permission(ub, cmd);
+ } else {
+ /* ADD_DEV permission check is done in command handler */
+ ret = 0;
}
+ if (ret)
+ goto put_dev;
+
switch (cmd->cmd_op) {
case UBLK_CMD_START_DEV:
ret = ublk_ctrl_start_dev(ub, cmd);
@@ -2022,6 +2154,7 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
ret = ublk_ctrl_stop_dev(ub);
break;
case UBLK_CMD_GET_DEV_INFO:
+ case UBLK_CMD_GET_DEV_INFO2:
ret = ublk_ctrl_get_dev_info(ub, cmd);
break;
case UBLK_CMD_ADD_DEV:
@@ -2049,6 +2182,8 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd,
ret = -ENOTSUPP;
break;
}
+
+ put_dev:
if (ub)
ublk_put_device(ub);
out:
diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h
index 4e38b9aa0293..ae80bfef3b9f 100644
--- a/include/uapi/linux/ublk_cmd.h
+++ b/include/uapi/linux/ublk_cmd.h
@@ -19,6 +19,8 @@
#define UBLK_CMD_GET_PARAMS 0x09
#define UBLK_CMD_START_USER_RECOVERY 0x10
#define UBLK_CMD_END_USER_RECOVERY 0x11
+#define UBLK_CMD_GET_DEV_INFO2 0x12
+
/*
* IO commands, issued by ublk server, and handled by ublk driver.
*
@@ -79,6 +81,27 @@
#define UBLK_F_USER_RECOVERY_REISSUE (1UL << 4)
+/*
+ * Unprivileged user can create /dev/ublkcN and /dev/ublkbN.
+ *
+ * /dev/ublk-control needs to be available for unprivileged user, and it
+ * can be done via udev rule to make all control commands available to
+ * unprivileged user. Except for the command of UBLK_CMD_ADD_DEV, all
+ * other commands are only allowed for the owner of the specified device.
+ *
+ * When userspace sends UBLK_CMD_ADD_DEV, the device pair's owner_uid and
+ * owner_gid need to be provided via ublksrv_ctrl_dev_info, and the two
+ * ids need to match with ublk server's uid/gid, otherwise the created
+ * ublk device can't be started successfully.
+ *
+ * We still need udev rule to set correct OWNER/GROUP with the stored
+ * owner_uid and owner_gid.
+ *
+ * Then ublk server can be run as unprivileged user, and /dev/ublkbN can
+ * be accessed by user of owner_uid/owner_gid.
+ */
+#define UBLK_F_UNPRIVILEGED_DEV (1UL << 5)
+
/* device state */
#define UBLK_S_DEV_DEAD 0
#define UBLK_S_DEV_LIVE 1
@@ -98,7 +121,15 @@ struct ublksrv_ctrl_cmd {
__u64 addr;
/* inline data */
- __u64 data[2];
+ __u64 data[1];
+
+ /*
+ * Used for UBLK_F_UNPRIVILEGED_DEV and UBLK_CMD_GET_DEV_INFO2
+ * only, include null char
+ */
+ __u16 dev_path_len;
+ __u16 pad;
+ __u32 reserved;
};
struct ublksrv_ctrl_dev_info {
@@ -118,7 +149,8 @@ struct ublksrv_ctrl_dev_info {
/* For ublksrv internal use, invisible to ublk driver */
__u64 ublksrv_flags;
- __u64 reserved0;
+ __u32 owner_uid;
+ __u32 owner_gid;
__u64 reserved1;
__u64 reserved2;
};
--
2.37.3
Thanks,
Ming
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 1/6] ublk_drv: remove nr_aborted_queues from ublk_device
2022-11-16 6:08 ` [PATCH 1/6] ublk_drv: remove nr_aborted_queues from ublk_device Ming Lei
@ 2022-11-18 9:02 ` Ziyang Zhang
0 siblings, 0 replies; 13+ messages in thread
From: Ziyang Zhang @ 2022-11-18 9:02 UTC (permalink / raw)
To: Ming Lei, Jens Axboe; +Cc: linux-block, Stefan Hajnoczi
On 2022/11/16 14:08, Ming Lei wrote:
> No one uses 'nr_aborted_queues' any more, so remove it.
>
> Signed-off-by: Ming Lei <ming.lei@redhat.com>
> ---
> drivers/block/ublk_drv.c | 1 -
> 1 file changed, 1 deletion(-)
>
> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> index f96cb01e9604..fe997848c1ff 100644
> --- a/drivers/block/ublk_drv.c
> +++ b/drivers/block/ublk_drv.c
> @@ -161,7 +161,6 @@ struct ublk_device {
>
> struct completion completion;
> unsigned int nr_queues_ready;
> - atomic_t nr_aborted_queues;
>
> /*
> * Our ubq->daemon may be killed without any notification, so
Reviewed-by: ZiyangZhang <ZiyangZhang@linux.alibaba.com>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/6] ublk_drv: don't probe partitions if the ubq daemon isn't trusted
2022-11-16 6:08 ` [PATCH 2/6] ublk_drv: don't probe partitions if the ubq daemon isn't trusted Ming Lei
@ 2022-11-18 10:03 ` Ziyang Zhang
2022-11-18 11:51 ` Ming Lei
0 siblings, 1 reply; 13+ messages in thread
From: Ziyang Zhang @ 2022-11-18 10:03 UTC (permalink / raw)
To: Ming Lei, Jens Axboe; +Cc: linux-block, Stefan Hajnoczi
On 2022/11/16 14:08, Ming Lei wrote:
> If any ubq daemon is unprivileged, the ublk char device is allowed
> for unprivileged user, and we can't trust the current user, so not
> probe partitions.
>
> Fixes: 71f28f3136af ("ublk_drv: add io_uring based userspace block driver")
> Signed-off-by: Ming Lei <ming.lei@redhat.com>
> ---
> drivers/block/ublk_drv.c | 14 +++++++++++++-
> 1 file changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> index fe997848c1ff..a5f3d8330be5 100644
> --- a/drivers/block/ublk_drv.c
> +++ b/drivers/block/ublk_drv.c
> @@ -149,6 +149,7 @@ struct ublk_device {
>
> #define UB_STATE_OPEN 0
> #define UB_STATE_USED 1
> +#define UB_STATE_PRIVILEGED 2
> unsigned long state;
> int ub_number;
>
> @@ -161,6 +162,7 @@ struct ublk_device {
>
> struct completion completion;
> unsigned int nr_queues_ready;
> + unsigned int nr_privileged_daemon;
>
> /*
> * Our ubq->daemon may be killed without any notification, so
> @@ -1184,9 +1186,15 @@ static void ublk_mark_io_ready(struct ublk_device *ub, struct ublk_queue *ubq)
> ubq->ubq_daemon = current;
> get_task_struct(ubq->ubq_daemon);
> ub->nr_queues_ready++;
> +
> + if (capable(CAP_SYS_ADMIN))
> + ub->nr_privileged_daemon++;
> }
> - if (ub->nr_queues_ready == ub->dev_info.nr_hw_queues)
> + if (ub->nr_queues_ready == ub->dev_info.nr_hw_queues) {
> + if (ub->nr_privileged_daemon == ub->nr_queues_ready)
Hi, Ming.
Just like nr_queues_ready, ub->nr_privileged_daemon should be reset
to zero in ublk_ctrl_start_recovery(). otherwise new ubq_daemons are
always treated as unprivileged.
> + set_bit(UB_STATE_PRIVILEGED, &ub->state);
> complete_all(&ub->completion);
> + }
> mutex_unlock(&ub->mutex);
> }
>
> @@ -1540,6 +1548,10 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
> if (ret)
> goto out_put_disk;
>
> + /* don't probe partitions if any one ubq daemon is un-trusted */
> + if (!test_bit(UB_STATE_PRIVILEGED, &ub->state))
> + set_bit(GD_SUPPRESS_PART_SCAN, &disk->state);
Can we simply check if nr_queues_ready == nr_privileged_daemon here
instead of adding a new bit UB_STATE_PRIVILEGED?
BTW, I think exposing whether ub's state is privileged/unprivileged
to users(./ublk list) is a good idea.
Regards,
Zhang
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/6] ublk_drv: don't probe partitions if the ubq daemon isn't trusted
2022-11-18 10:03 ` Ziyang Zhang
@ 2022-11-18 11:51 ` Ming Lei
0 siblings, 0 replies; 13+ messages in thread
From: Ming Lei @ 2022-11-18 11:51 UTC (permalink / raw)
To: Ziyang Zhang; +Cc: Jens Axboe, linux-block, Stefan Hajnoczi, ming.lei
On Fri, Nov 18, 2022 at 06:03:48PM +0800, Ziyang Zhang wrote:
> On 2022/11/16 14:08, Ming Lei wrote:
> > If any ubq daemon is unprivileged, the ublk char device is allowed
> > for unprivileged user, and we can't trust the current user, so not
> > probe partitions.
> >
> > Fixes: 71f28f3136af ("ublk_drv: add io_uring based userspace block driver")
> > Signed-off-by: Ming Lei <ming.lei@redhat.com>
> > ---
> > drivers/block/ublk_drv.c | 14 +++++++++++++-
> > 1 file changed, 13 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c
> > index fe997848c1ff..a5f3d8330be5 100644
> > --- a/drivers/block/ublk_drv.c
> > +++ b/drivers/block/ublk_drv.c
> > @@ -149,6 +149,7 @@ struct ublk_device {
> >
> > #define UB_STATE_OPEN 0
> > #define UB_STATE_USED 1
> > +#define UB_STATE_PRIVILEGED 2
> > unsigned long state;
> > int ub_number;
> >
> > @@ -161,6 +162,7 @@ struct ublk_device {
> >
> > struct completion completion;
> > unsigned int nr_queues_ready;
> > + unsigned int nr_privileged_daemon;
> >
> > /*
> > * Our ubq->daemon may be killed without any notification, so
> > @@ -1184,9 +1186,15 @@ static void ublk_mark_io_ready(struct ublk_device *ub, struct ublk_queue *ubq)
> > ubq->ubq_daemon = current;
> > get_task_struct(ubq->ubq_daemon);
> > ub->nr_queues_ready++;
> > +
> > + if (capable(CAP_SYS_ADMIN))
> > + ub->nr_privileged_daemon++;
> > }
> > - if (ub->nr_queues_ready == ub->dev_info.nr_hw_queues)
> > + if (ub->nr_queues_ready == ub->dev_info.nr_hw_queues) {
> > + if (ub->nr_privileged_daemon == ub->nr_queues_ready)
>
> Hi, Ming.
>
> Just like nr_queues_ready, ub->nr_privileged_daemon should be reset
> to zero in ublk_ctrl_start_recovery(). otherwise new ubq_daemons are
> always treated as unprivileged.
Good catch!
>
> > + set_bit(UB_STATE_PRIVILEGED, &ub->state);
> > complete_all(&ub->completion);
> > + }
> > mutex_unlock(&ub->mutex);
> > }
> >
> > @@ -1540,6 +1548,10 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd)
> > if (ret)
> > goto out_put_disk;
> >
> > + /* don't probe partitions if any one ubq daemon is un-trusted */
> > + if (!test_bit(UB_STATE_PRIVILEGED, &ub->state))
> > + set_bit(GD_SUPPRESS_PART_SCAN, &disk->state);
>
> Can we simply check if nr_queues_ready == nr_privileged_daemon here
> instead of adding a new bit UB_STATE_PRIVILEGED?
Good idea!
>
> BTW, I think exposing whether ub's state is privileged/unprivileged
> to users(./ublk list) is a good idea.
It is actually not a state, but a flag of UBLK_F_UNPRIVILEGED_DEV, which
won't be changed for one device and is shown in 'ublk list'.
For root user, maybe we should clear the flag from UBLK_CMD_ADD_DEV.
Thanks,
Ming
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device
2022-11-16 6:08 ` [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
2022-11-17 13:17 ` Ming Lei
@ 2022-11-22 7:17 ` Dan Carpenter
2022-11-22 7:47 ` Ming Lei
1 sibling, 1 reply; 13+ messages in thread
From: Dan Carpenter @ 2022-11-22 7:17 UTC (permalink / raw)
To: oe-kbuild, Ming Lei, Jens Axboe
Cc: lkp, oe-kbuild-all, linux-block, ZiyangZhang, Stefan Hajnoczi,
Ming Lei
Hi Ming,
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Ming-Lei/ublk_drv-add-mechanism-for-supporting-unprivileged-ublk-device/20221116-141131
patch link: https://lore.kernel.org/r/20221116060835.159945-7-ming.lei%40redhat.com
patch subject: [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device
config: x86_64-randconfig-m001-20221121
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <error27@gmail.com>
smatch warnings:
drivers/block/ublk_drv.c:2107 ublk_ctrl_uring_cmd_permission() error: uninitialized symbol 'mask'.
vim +/mask +2107 drivers/block/ublk_drv.c
a922b5da71a7776 Ming Lei 2022-11-16 2043 static int ublk_ctrl_uring_cmd_permission(struct ublk_device *ub,
a922b5da71a7776 Ming Lei 2022-11-16 2044 struct io_uring_cmd *cmd)
a922b5da71a7776 Ming Lei 2022-11-16 2045 {
a922b5da71a7776 Ming Lei 2022-11-16 2046 struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
a922b5da71a7776 Ming Lei 2022-11-16 2047 bool unprivileged = ub->dev_info.flags & UBLK_F_UNPRIVILEGED_DEV;
a922b5da71a7776 Ming Lei 2022-11-16 2048 void __user *argp = (void __user *)(unsigned long)header->addr;
a922b5da71a7776 Ming Lei 2022-11-16 2049 char *dev_path = NULL;
a922b5da71a7776 Ming Lei 2022-11-16 2050 int ret = 0;
a922b5da71a7776 Ming Lei 2022-11-16 2051 int mask;
a922b5da71a7776 Ming Lei 2022-11-16 2052
a922b5da71a7776 Ming Lei 2022-11-16 2053 if (!unprivileged) {
a922b5da71a7776 Ming Lei 2022-11-16 2054 if (!capable(CAP_SYS_ADMIN))
a922b5da71a7776 Ming Lei 2022-11-16 2055 return -EPERM;
a922b5da71a7776 Ming Lei 2022-11-16 2056 /*
a922b5da71a7776 Ming Lei 2022-11-16 2057 * The new added command of UBLK_CMD_GET_DEV_INFO2 includes
a922b5da71a7776 Ming Lei 2022-11-16 2058 * char_dev_path in payload too, since userspace may not
a922b5da71a7776 Ming Lei 2022-11-16 2059 * know if the specified device is created as unprivileged
a922b5da71a7776 Ming Lei 2022-11-16 2060 * mode.
a922b5da71a7776 Ming Lei 2022-11-16 2061 */
a922b5da71a7776 Ming Lei 2022-11-16 2062 if (cmd->cmd_op != UBLK_CMD_GET_DEV_INFO2)
a922b5da71a7776 Ming Lei 2022-11-16 2063 return 0;
a922b5da71a7776 Ming Lei 2022-11-16 2064 }
a922b5da71a7776 Ming Lei 2022-11-16 2065
a922b5da71a7776 Ming Lei 2022-11-16 2066 /*
a922b5da71a7776 Ming Lei 2022-11-16 2067 * User has to provide the char device path for unprivileged ublk
a922b5da71a7776 Ming Lei 2022-11-16 2068 *
a922b5da71a7776 Ming Lei 2022-11-16 2069 * header->addr always points to the dev path buffer, and
a922b5da71a7776 Ming Lei 2022-11-16 2070 * header->dev_path_len records length of dev path buffer.
a922b5da71a7776 Ming Lei 2022-11-16 2071 */
a922b5da71a7776 Ming Lei 2022-11-16 2072 if (!header->dev_path_len || header->dev_path_len > PATH_MAX)
a922b5da71a7776 Ming Lei 2022-11-16 2073 return -EINVAL;
a922b5da71a7776 Ming Lei 2022-11-16 2074
a922b5da71a7776 Ming Lei 2022-11-16 2075 if (header->len < header->dev_path_len)
a922b5da71a7776 Ming Lei 2022-11-16 2076 return -EINVAL;
a922b5da71a7776 Ming Lei 2022-11-16 2077
a922b5da71a7776 Ming Lei 2022-11-16 2078 dev_path = kmalloc(header->dev_path_len, GFP_KERNEL);
a922b5da71a7776 Ming Lei 2022-11-16 2079 if (!dev_path)
a922b5da71a7776 Ming Lei 2022-11-16 2080 return -ENOMEM;
a922b5da71a7776 Ming Lei 2022-11-16 2081
a922b5da71a7776 Ming Lei 2022-11-16 2082 ret = -EFAULT;
a922b5da71a7776 Ming Lei 2022-11-16 2083 if (copy_from_user(dev_path, argp, header->dev_path_len))
a922b5da71a7776 Ming Lei 2022-11-16 2084 goto exit;
a922b5da71a7776 Ming Lei 2022-11-16 2085 dev_path[header->dev_path_len] = 0;
a922b5da71a7776 Ming Lei 2022-11-16 2086
a922b5da71a7776 Ming Lei 2022-11-16 2087 switch (cmd->cmd_op) {
a922b5da71a7776 Ming Lei 2022-11-16 2088 case UBLK_CMD_GET_DEV_INFO:
a922b5da71a7776 Ming Lei 2022-11-16 2089 case UBLK_CMD_GET_DEV_INFO2:
a922b5da71a7776 Ming Lei 2022-11-16 2090 case UBLK_CMD_GET_QUEUE_AFFINITY:
a922b5da71a7776 Ming Lei 2022-11-16 2091 case UBLK_CMD_GET_PARAMS:
a922b5da71a7776 Ming Lei 2022-11-16 2092 mask = MAY_READ;
a922b5da71a7776 Ming Lei 2022-11-16 2093 break;
a922b5da71a7776 Ming Lei 2022-11-16 2094 case UBLK_CMD_START_DEV:
a922b5da71a7776 Ming Lei 2022-11-16 2095 case UBLK_CMD_STOP_DEV:
a922b5da71a7776 Ming Lei 2022-11-16 2096 case UBLK_CMD_ADD_DEV:
a922b5da71a7776 Ming Lei 2022-11-16 2097 case UBLK_CMD_DEL_DEV:
a922b5da71a7776 Ming Lei 2022-11-16 2098 case UBLK_CMD_SET_PARAMS:
a922b5da71a7776 Ming Lei 2022-11-16 2099 case UBLK_CMD_START_USER_RECOVERY:
a922b5da71a7776 Ming Lei 2022-11-16 2100 case UBLK_CMD_END_USER_RECOVERY:
a922b5da71a7776 Ming Lei 2022-11-16 2101 mask = MAY_READ | MAY_WRITE;
a922b5da71a7776 Ming Lei 2022-11-16 2102 break;
a922b5da71a7776 Ming Lei 2022-11-16 2103 default:
mask not set on default path.
a922b5da71a7776 Ming Lei 2022-11-16 2104 break;
a922b5da71a7776 Ming Lei 2022-11-16 2105 }
a922b5da71a7776 Ming Lei 2022-11-16 2106
a922b5da71a7776 Ming Lei 2022-11-16 @2107 ret = ublk_char_dev_permission(ub, dev_path, mask);
a922b5da71a7776 Ming Lei 2022-11-16 2108 if (!ret) {
a922b5da71a7776 Ming Lei 2022-11-16 2109 header->len -= header->dev_path_len;
a922b5da71a7776 Ming Lei 2022-11-16 2110 header->addr += header->dev_path_len;
a922b5da71a7776 Ming Lei 2022-11-16 2111 }
a922b5da71a7776 Ming Lei 2022-11-16 2112 pr_devel("%s: dev id %d cmd_op %x uid %d gid %d path %s ret %d\n",
a922b5da71a7776 Ming Lei 2022-11-16 2113 __func__, ub->ub_number, cmd->cmd_op,
a922b5da71a7776 Ming Lei 2022-11-16 2114 ub->dev_info.owner_uid, ub->dev_info.owner_gid,
a922b5da71a7776 Ming Lei 2022-11-16 2115 dev_path, ret);
a922b5da71a7776 Ming Lei 2022-11-16 2116 exit:
a922b5da71a7776 Ming Lei 2022-11-16 2117 kfree(dev_path);
a922b5da71a7776 Ming Lei 2022-11-16 2118 return ret;
a922b5da71a7776 Ming Lei 2022-11-16 2119 }
--
0-DAY CI Kernel Test Service
https://01.org/lkp
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device
2022-11-22 7:17 ` Dan Carpenter
@ 2022-11-22 7:47 ` Ming Lei
0 siblings, 0 replies; 13+ messages in thread
From: Ming Lei @ 2022-11-22 7:47 UTC (permalink / raw)
To: Dan Carpenter
Cc: oe-kbuild, Jens Axboe, lkp, oe-kbuild-all, linux-block,
ZiyangZhang, Stefan Hajnoczi
On Tue, Nov 22, 2022 at 10:17:47AM +0300, Dan Carpenter wrote:
> Hi Ming,
>
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Ming-Lei/ublk_drv-add-mechanism-for-supporting-unprivileged-ublk-device/20221116-141131
> patch link: https://lore.kernel.org/r/20221116060835.159945-7-ming.lei%40redhat.com
> patch subject: [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device
> config: x86_64-randconfig-m001-20221121
> compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
>
> If you fix the issue, kindly add following tag where applicable
> | Reported-by: kernel test robot <lkp@intel.com>
> | Reported-by: Dan Carpenter <error27@gmail.com>
>
> smatch warnings:
> drivers/block/ublk_drv.c:2107 ublk_ctrl_uring_cmd_permission() error: uninitialized symbol 'mask'.
>
> vim +/mask +2107 drivers/block/ublk_drv.c
>
> a922b5da71a7776 Ming Lei 2022-11-16 2043 static int ublk_ctrl_uring_cmd_permission(struct ublk_device *ub,
> a922b5da71a7776 Ming Lei 2022-11-16 2044 struct io_uring_cmd *cmd)
> a922b5da71a7776 Ming Lei 2022-11-16 2045 {
> a922b5da71a7776 Ming Lei 2022-11-16 2046 struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd;
> a922b5da71a7776 Ming Lei 2022-11-16 2047 bool unprivileged = ub->dev_info.flags & UBLK_F_UNPRIVILEGED_DEV;
> a922b5da71a7776 Ming Lei 2022-11-16 2048 void __user *argp = (void __user *)(unsigned long)header->addr;
> a922b5da71a7776 Ming Lei 2022-11-16 2049 char *dev_path = NULL;
> a922b5da71a7776 Ming Lei 2022-11-16 2050 int ret = 0;
> a922b5da71a7776 Ming Lei 2022-11-16 2051 int mask;
> a922b5da71a7776 Ming Lei 2022-11-16 2052
> a922b5da71a7776 Ming Lei 2022-11-16 2053 if (!unprivileged) {
> a922b5da71a7776 Ming Lei 2022-11-16 2054 if (!capable(CAP_SYS_ADMIN))
> a922b5da71a7776 Ming Lei 2022-11-16 2055 return -EPERM;
> a922b5da71a7776 Ming Lei 2022-11-16 2056 /*
> a922b5da71a7776 Ming Lei 2022-11-16 2057 * The new added command of UBLK_CMD_GET_DEV_INFO2 includes
> a922b5da71a7776 Ming Lei 2022-11-16 2058 * char_dev_path in payload too, since userspace may not
> a922b5da71a7776 Ming Lei 2022-11-16 2059 * know if the specified device is created as unprivileged
> a922b5da71a7776 Ming Lei 2022-11-16 2060 * mode.
> a922b5da71a7776 Ming Lei 2022-11-16 2061 */
> a922b5da71a7776 Ming Lei 2022-11-16 2062 if (cmd->cmd_op != UBLK_CMD_GET_DEV_INFO2)
> a922b5da71a7776 Ming Lei 2022-11-16 2063 return 0;
> a922b5da71a7776 Ming Lei 2022-11-16 2064 }
> a922b5da71a7776 Ming Lei 2022-11-16 2065
> a922b5da71a7776 Ming Lei 2022-11-16 2066 /*
> a922b5da71a7776 Ming Lei 2022-11-16 2067 * User has to provide the char device path for unprivileged ublk
> a922b5da71a7776 Ming Lei 2022-11-16 2068 *
> a922b5da71a7776 Ming Lei 2022-11-16 2069 * header->addr always points to the dev path buffer, and
> a922b5da71a7776 Ming Lei 2022-11-16 2070 * header->dev_path_len records length of dev path buffer.
> a922b5da71a7776 Ming Lei 2022-11-16 2071 */
> a922b5da71a7776 Ming Lei 2022-11-16 2072 if (!header->dev_path_len || header->dev_path_len > PATH_MAX)
> a922b5da71a7776 Ming Lei 2022-11-16 2073 return -EINVAL;
> a922b5da71a7776 Ming Lei 2022-11-16 2074
> a922b5da71a7776 Ming Lei 2022-11-16 2075 if (header->len < header->dev_path_len)
> a922b5da71a7776 Ming Lei 2022-11-16 2076 return -EINVAL;
> a922b5da71a7776 Ming Lei 2022-11-16 2077
> a922b5da71a7776 Ming Lei 2022-11-16 2078 dev_path = kmalloc(header->dev_path_len, GFP_KERNEL);
> a922b5da71a7776 Ming Lei 2022-11-16 2079 if (!dev_path)
> a922b5da71a7776 Ming Lei 2022-11-16 2080 return -ENOMEM;
> a922b5da71a7776 Ming Lei 2022-11-16 2081
> a922b5da71a7776 Ming Lei 2022-11-16 2082 ret = -EFAULT;
> a922b5da71a7776 Ming Lei 2022-11-16 2083 if (copy_from_user(dev_path, argp, header->dev_path_len))
> a922b5da71a7776 Ming Lei 2022-11-16 2084 goto exit;
> a922b5da71a7776 Ming Lei 2022-11-16 2085 dev_path[header->dev_path_len] = 0;
> a922b5da71a7776 Ming Lei 2022-11-16 2086
> a922b5da71a7776 Ming Lei 2022-11-16 2087 switch (cmd->cmd_op) {
> a922b5da71a7776 Ming Lei 2022-11-16 2088 case UBLK_CMD_GET_DEV_INFO:
> a922b5da71a7776 Ming Lei 2022-11-16 2089 case UBLK_CMD_GET_DEV_INFO2:
> a922b5da71a7776 Ming Lei 2022-11-16 2090 case UBLK_CMD_GET_QUEUE_AFFINITY:
> a922b5da71a7776 Ming Lei 2022-11-16 2091 case UBLK_CMD_GET_PARAMS:
> a922b5da71a7776 Ming Lei 2022-11-16 2092 mask = MAY_READ;
> a922b5da71a7776 Ming Lei 2022-11-16 2093 break;
> a922b5da71a7776 Ming Lei 2022-11-16 2094 case UBLK_CMD_START_DEV:
> a922b5da71a7776 Ming Lei 2022-11-16 2095 case UBLK_CMD_STOP_DEV:
> a922b5da71a7776 Ming Lei 2022-11-16 2096 case UBLK_CMD_ADD_DEV:
> a922b5da71a7776 Ming Lei 2022-11-16 2097 case UBLK_CMD_DEL_DEV:
> a922b5da71a7776 Ming Lei 2022-11-16 2098 case UBLK_CMD_SET_PARAMS:
> a922b5da71a7776 Ming Lei 2022-11-16 2099 case UBLK_CMD_START_USER_RECOVERY:
> a922b5da71a7776 Ming Lei 2022-11-16 2100 case UBLK_CMD_END_USER_RECOVERY:
> a922b5da71a7776 Ming Lei 2022-11-16 2101 mask = MAY_READ | MAY_WRITE;
> a922b5da71a7776 Ming Lei 2022-11-16 2102 break;
> a922b5da71a7776 Ming Lei 2022-11-16 2103 default:
>
> mask not set on default path.
Good catch, and it is really one bug, will fix it in V2.
Thanks,
Ming
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2022-11-22 7:48 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-11-16 6:08 [PATCH 0/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
2022-11-16 6:08 ` [PATCH 1/6] ublk_drv: remove nr_aborted_queues from ublk_device Ming Lei
2022-11-18 9:02 ` Ziyang Zhang
2022-11-16 6:08 ` [PATCH 2/6] ublk_drv: don't probe partitions if the ubq daemon isn't trusted Ming Lei
2022-11-18 10:03 ` Ziyang Zhang
2022-11-18 11:51 ` Ming Lei
2022-11-16 6:08 ` [PATCH 3/6] ublk_drv: move ublk_get_device_from_id into ublk_ctrl_uring_cmd Ming Lei
2022-11-16 6:08 ` [PATCH 4/6] ublk_drv: add device parameter UBLK_PARAM_TYPE_DEVT Ming Lei
2022-11-16 6:08 ` [PATCH 5/6] ublk_drv: add module parameter of ublks_max for limiting max allowed ublk dev Ming Lei
2022-11-16 6:08 ` [PATCH 6/6] ublk_drv: add mechanism for supporting unprivileged ublk device Ming Lei
2022-11-17 13:17 ` Ming Lei
2022-11-22 7:17 ` Dan Carpenter
2022-11-22 7:47 ` Ming Lei
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).