mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
nvme: fix command effects log lifetime for multipath heads
KASAN reported a use-after-free when an I/O passthrough command was sent
through a multipath namespace head after the controller path that first
created the head had been removed:
BUG: KASAN: slab-use-after-free in nvme_command_effects+0x192/0x200 [nvme_core]
Read of size 4 at addr ffff888141b14400 by task nvme/19811
nvme_command_effects+0x192/0x200 [nvme_core]
nvme_cmd_allowed+0x7e/0x1b0 [nvme_core]
nvme_user_cmd.constprop.0+0x1b5/0x450 [nvme_core]
nvme_ns_head_chr_ioctl+0xf4/0x2a0 [nvme_core]
Move the log cache to the subsystem, with one entry per command set. Both
controllers and namespace heads hold subsystem references, keeping their
log pointers valid until subsystem release.
Fixes: be93e87e78 ("nvme: support for multiple Command Sets Supported and Effects log pages")
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Yao Sang <sangyao@kylinos.cn>
Signed-off-by: Keith Busch <kbusch@kernel.org>
This commit is contained in:
parent
3fdd281e0a
commit
72ece656ab
|
|
@ -3195,9 +3195,16 @@ static void nvme_release_subsystem(struct device *dev)
|
|||
{
|
||||
struct nvme_subsystem *subsys =
|
||||
container_of(dev, struct nvme_subsystem, dev);
|
||||
struct nvme_effects_log *cel;
|
||||
unsigned long i;
|
||||
|
||||
if (subsys->instance >= 0)
|
||||
ida_free(&nvme_instance_ida, subsys->instance);
|
||||
xa_for_each(&subsys->cels, i, cel) {
|
||||
xa_erase(&subsys->cels, i);
|
||||
kfree(cel);
|
||||
}
|
||||
xa_destroy(&subsys->cels);
|
||||
kfree(subsys);
|
||||
}
|
||||
|
||||
|
|
@ -3311,6 +3318,7 @@ static int nvme_init_subsystem(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
|
|||
kref_init(&subsys->ref);
|
||||
INIT_LIST_HEAD(&subsys->ctrls);
|
||||
INIT_LIST_HEAD(&subsys->nsheads);
|
||||
xa_init(&subsys->cels);
|
||||
nvme_init_subnqn(subsys, ctrl, id);
|
||||
memcpy(subsys->serial, id->sn, sizeof(subsys->serial));
|
||||
memcpy(subsys->model, id->mn, sizeof(subsys->model));
|
||||
|
|
@ -3413,7 +3421,7 @@ int nvme_get_log(struct nvme_ctrl *ctrl, u32 nsid, u8 log_page, u8 lsp, u8 csi,
|
|||
static int nvme_get_effects_log(struct nvme_ctrl *ctrl, u8 csi,
|
||||
struct nvme_effects_log **log)
|
||||
{
|
||||
struct nvme_effects_log *old, *cel = xa_load(&ctrl->cels, csi);
|
||||
struct nvme_effects_log *old, *cel = xa_load(&ctrl->subsys->cels, csi);
|
||||
int ret;
|
||||
|
||||
if (cel)
|
||||
|
|
@ -3430,7 +3438,7 @@ static int nvme_get_effects_log(struct nvme_ctrl *ctrl, u8 csi,
|
|||
return ret;
|
||||
}
|
||||
|
||||
old = xa_store(&ctrl->cels, csi, cel, GFP_KERNEL);
|
||||
old = xa_store(&ctrl->subsys->cels, csi, cel, GFP_KERNEL);
|
||||
if (xa_is_err(old)) {
|
||||
kfree(cel);
|
||||
return xa_err(old);
|
||||
|
|
@ -3505,7 +3513,7 @@ static int nvme_init_effects_log(struct nvme_ctrl *ctrl,
|
|||
if (!effects)
|
||||
return -ENOMEM;
|
||||
|
||||
old = xa_store(&ctrl->cels, csi, effects, GFP_KERNEL);
|
||||
old = xa_store(&ctrl->subsys->cels, csi, effects, GFP_KERNEL);
|
||||
if (xa_is_err(old)) {
|
||||
kfree(effects);
|
||||
return xa_err(old);
|
||||
|
|
@ -3551,23 +3559,27 @@ static int nvme_init_effects(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
|
|||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&ctrl->subsys->lock);
|
||||
ctrl->effects = xa_load(&ctrl->subsys->cels, NVME_CSI_NVM);
|
||||
if (ctrl->effects)
|
||||
return 0;
|
||||
goto out_unlock;
|
||||
|
||||
if (id->lpa & NVME_CTRL_LPA_CMD_EFFECTS_LOG) {
|
||||
ret = nvme_get_effects_log(ctrl, NVME_CSI_NVM, &ctrl->effects);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (!ctrl->effects) {
|
||||
ret = nvme_init_effects_log(ctrl, NVME_CSI_NVM, &ctrl->effects);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
nvme_init_known_nvm_effects(ctrl);
|
||||
return 0;
|
||||
out_unlock:
|
||||
mutex_unlock(&ctrl->subsys->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nvme_check_ctrl_fabric_info(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id)
|
||||
|
|
@ -5152,19 +5164,6 @@ void nvme_uninit_ctrl(struct nvme_ctrl *ctrl)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(nvme_uninit_ctrl);
|
||||
|
||||
static void nvme_free_cels(struct nvme_ctrl *ctrl)
|
||||
{
|
||||
struct nvme_effects_log *cel;
|
||||
unsigned long i;
|
||||
|
||||
xa_for_each(&ctrl->cels, i, cel) {
|
||||
xa_erase(&ctrl->cels, i);
|
||||
kfree(cel);
|
||||
}
|
||||
|
||||
xa_destroy(&ctrl->cels);
|
||||
}
|
||||
|
||||
static void nvme_free_ctrl(struct device *dev)
|
||||
{
|
||||
struct nvme_ctrl *ctrl =
|
||||
|
|
@ -5177,7 +5176,6 @@ static void nvme_free_ctrl(struct device *dev)
|
|||
blk_put_queue(ctrl->fabrics_q);
|
||||
if (!subsys || ctrl->instance != subsys->instance)
|
||||
ida_free(&nvme_instance_ida, ctrl->instance);
|
||||
nvme_free_cels(ctrl);
|
||||
nvme_mpath_uninit(ctrl);
|
||||
cleanup_srcu_struct(&ctrl->srcu);
|
||||
nvme_auth_stop(ctrl);
|
||||
|
|
@ -5223,7 +5221,6 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev,
|
|||
|
||||
mutex_init(&ctrl->scan_lock);
|
||||
INIT_LIST_HEAD(&ctrl->namespaces);
|
||||
xa_init(&ctrl->cels);
|
||||
ctrl->dev = dev;
|
||||
ctrl->ops = ops;
|
||||
ctrl->quirks = quirks;
|
||||
|
|
|
|||
|
|
@ -415,7 +415,6 @@ struct nvme_ctrl {
|
|||
unsigned long quirks;
|
||||
struct nvme_id_power_state psd[32];
|
||||
struct nvme_effects_log *effects;
|
||||
struct xarray cels;
|
||||
struct work_struct scan_work;
|
||||
struct work_struct async_event_work;
|
||||
struct delayed_work ka_work;
|
||||
|
|
@ -509,6 +508,8 @@ struct nvme_subsystem {
|
|||
__guarded_by(&nvme_subsystems_lock);
|
||||
struct list_head nsheads
|
||||
__guarded_by(&lock);
|
||||
/* Command effects logs, indexed by CSI and protected by lock. */
|
||||
struct xarray cels;
|
||||
char subnqn[NVMF_NQN_SIZE];
|
||||
char serial[20];
|
||||
char model[40];
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user