mirror of
https://github.com/torvalds/linux.git
synced 2026-09-25 17:42:03 +02:00
iommu/tegra241-cmdqv: Fix CMD_SYNC use-after-free on teardown
arm_smmu_impl_remove() is registered as a devres action in
arm_smmu_impl_probe(), before arm_smmu_init_queues() allocates
smmu->cmdq.q.base. On a devres unwind, whether a failed probe or an
unbind, the queue is freed first and arm_smmu_impl_remove() then runs
tegra241_cmdqv_remove_vintf(), whose VINTF deinit issues a CMD_SYNC on
the freed memory.
Observed during testing with a QEMU hack that makes the VCMDQ fail to
enable, so the impl reset fails and probe aborts into the devres unwind:
platform NVDA200C:00: tegra241_cmdqv: VINTF0: VCMDQ0/LVCMDQ0: failed to enable, STATUS=0x00000000
platform NVDA200C:00: tegra241_cmdqv: VINTF0: VCMDQ0/LVCMDQ0: GERRORN=0x0, GERROR=0x4, CONS=0x0
platform NVDA200C:00: tegra241_cmdqv: VINTF0: VCMDQ0/LVCMDQ0: uncleared error detected, resetting
arm-smmu-v3 arm-smmu-v3.0.auto: failed to reset impl
arm-smmu-v3 arm-smmu-v3.0.auto: probe with driver arm-smmu-v3 failed with error -110
Unable to handle kernel paging request at virtual address ffff8000891e0098
...
Internal error: Oops: 0000000096000047 [#1] SMP
...
Call trace:
arm_smmu_cmdq_issue_cmdlist+0x320/0x6fc (P)
tegra241_vcmdq_hw_deinit+0x98/0x168
tegra241_vintf_hw_deinit+0x5c/0x1b0
tegra241_cmdqv_remove_vintf+0x34/0xec
tegra241_cmdqv_remove+0x40/0x9c
arm_smmu_impl_remove+0x20/0x30
devm_action_release+0x14/0x20
devres_release_all+0xa8/0x110
device_unbind_cleanup+0x18/0x84
really_probe+0x1f0/0x29c
Drop the VINTF deinit from tegra241_cmdqv_remove_vintf() so the unwind no
longer touches the freed queue. Quiesce the VINTFs earlier instead. Add a
device_disable() impl op and run it from arm_smmu_disable_action() while
the CMDQ is still up. That handles a live unbind. A failed reset is already
handled because tegra241_vintf_hw_init() deinits the VINTF on its own error
path. tegra241_cmdqv_remove_vintf() is also used by the iommufd viommu
destroy path, so quiesce there too.
Fixes: 4dc0d12474 ("iommu/tegra241-cmdqv: Add user-space use support")
Cc: stable@vger.kernel.org
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
parent
2bd22a0d40
commit
9ff145a25c
|
|
@ -4740,6 +4740,8 @@ static void arm_smmu_disable_action(void *data)
|
|||
{
|
||||
struct arm_smmu_device *smmu = data;
|
||||
|
||||
if (smmu->impl_ops && smmu->impl_ops->device_disable)
|
||||
smmu->impl_ops->device_disable(smmu);
|
||||
arm_smmu_device_disable(smmu);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -871,6 +871,7 @@ struct arm_smmu_strtab_cfg {
|
|||
|
||||
struct arm_smmu_impl_ops {
|
||||
int (*device_reset)(struct arm_smmu_device *smmu);
|
||||
void (*device_disable)(struct arm_smmu_device *smmu);
|
||||
void (*device_remove)(struct arm_smmu_device *smmu);
|
||||
int (*init_structures)(struct arm_smmu_device *smmu);
|
||||
struct arm_smmu_cmdq *(*get_secondary_cmdq)(
|
||||
|
|
|
|||
|
|
@ -761,8 +761,6 @@ static void tegra241_cmdqv_remove_vintf(struct tegra241_cmdqv *cmdqv, u16 idx)
|
|||
struct tegra241_vintf *vintf = cmdqv->vintfs[idx];
|
||||
u16 lidx;
|
||||
|
||||
tegra241_vintf_hw_deinit(vintf);
|
||||
|
||||
/* Remove LVCMDQ resources */
|
||||
for (lidx = 0; lidx < vintf->cmdqv->num_lvcmdqs_per_vintf; lidx++)
|
||||
if (vintf->lvcmdqs[lidx])
|
||||
|
|
@ -779,6 +777,17 @@ static void tegra241_cmdqv_remove_vintf(struct tegra241_cmdqv *cmdqv, u16 idx)
|
|||
}
|
||||
}
|
||||
|
||||
static void tegra241_cmdqv_hw_disable(struct arm_smmu_device *smmu)
|
||||
{
|
||||
struct tegra241_cmdqv *cmdqv =
|
||||
container_of(smmu, struct tegra241_cmdqv, smmu);
|
||||
u16 idx;
|
||||
|
||||
for (idx = 0; idx < cmdqv->num_vintfs; idx++)
|
||||
if (cmdqv->vintfs[idx])
|
||||
tegra241_vintf_hw_deinit(cmdqv->vintfs[idx]);
|
||||
}
|
||||
|
||||
static void tegra241_cmdqv_remove(struct arm_smmu_device *smmu)
|
||||
{
|
||||
struct tegra241_cmdqv *cmdqv =
|
||||
|
|
@ -844,6 +853,7 @@ static struct arm_smmu_impl_ops tegra241_cmdqv_impl_ops = {
|
|||
/* For in-kernel use */
|
||||
.get_secondary_cmdq = tegra241_cmdqv_get_cmdq,
|
||||
.device_reset = tegra241_cmdqv_hw_reset,
|
||||
.device_disable = tegra241_cmdqv_hw_disable,
|
||||
.device_remove = tegra241_cmdqv_remove,
|
||||
/* For user-space use */
|
||||
.hw_info = tegra241_cmdqv_hw_info,
|
||||
|
|
@ -1152,6 +1162,7 @@ static void tegra241_cmdqv_destroy_vintf_user(struct iommufd_viommu *viommu)
|
|||
if (vintf->mmap_offset)
|
||||
iommufd_viommu_destroy_mmap(&vintf->vsmmu.core,
|
||||
vintf->mmap_offset);
|
||||
tegra241_vintf_hw_deinit(vintf);
|
||||
tegra241_cmdqv_remove_vintf(vintf->cmdqv, vintf->idx);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user