| From 1c2a8adcbda611f64bbb58462d08d6b43253ab33 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 10 Jun 2021 10:49:20 +0800 |
| Subject: iommu/arm-smmu: Fix arm_smmu_device refcount leak in address |
| translation |
| |
| From: Xiyu Yang <xiyuyang19@fudan.edu.cn> |
| |
| [ Upstream commit 7c8f176d6a3fa18aa0f8875da6f7c672ed2a8554 ] |
| |
| The reference counting issue happens in several exception handling paths |
| of arm_smmu_iova_to_phys_hard(). When those error scenarios occur, the |
| function forgets to decrease the refcount of "smmu" increased by |
| arm_smmu_rpm_get(), causing a refcount leak. |
| |
| Fix this issue by jumping to "out" label when those error scenarios |
| occur. |
| |
| Signed-off-by: Xiyu Yang <xiyuyang19@fudan.edu.cn> |
| Signed-off-by: Xin Tan <tanxin.ctf@gmail.com> |
| Reviewed-by: Rob Clark <robdclark@chromium.org> |
| Link: https://lore.kernel.org/r/1623293391-17261-1-git-send-email-xiyuyang19@fudan.edu.cn |
| Signed-off-by: Will Deacon <will@kernel.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/iommu/arm/arm-smmu/arm-smmu.c | 8 ++++++-- |
| 1 file changed, 6 insertions(+), 2 deletions(-) |
| |
| diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c |
| index 128c2c87b4e5..c6ff32797a23 100644 |
| --- a/drivers/iommu/arm/arm-smmu/arm-smmu.c |
| +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c |
| @@ -1268,6 +1268,7 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, |
| u64 phys; |
| unsigned long va, flags; |
| int ret, idx = cfg->cbndx; |
| + phys_addr_t addr = 0; |
| |
| ret = arm_smmu_rpm_get(smmu); |
| if (ret < 0) |
| @@ -1287,6 +1288,7 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, |
| dev_err(dev, |
| "iova to phys timed out on %pad. Falling back to software table walk.\n", |
| &iova); |
| + arm_smmu_rpm_put(smmu); |
| return ops->iova_to_phys(ops, iova); |
| } |
| |
| @@ -1295,12 +1297,14 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain, |
| if (phys & ARM_SMMU_CB_PAR_F) { |
| dev_err(dev, "translation fault!\n"); |
| dev_err(dev, "PAR = 0x%llx\n", phys); |
| - return 0; |
| + goto out; |
| } |
| |
| + addr = (phys & GENMASK_ULL(39, 12)) | (iova & 0xfff); |
| +out: |
| arm_smmu_rpm_put(smmu); |
| |
| - return (phys & GENMASK_ULL(39, 12)) | (iova & 0xfff); |
| + return addr; |
| } |
| |
| static phys_addr_t arm_smmu_iova_to_phys(struct iommu_domain *domain, |
| -- |
| 2.30.2 |
| |