[PATCH RFC v4 1/4] iommu/arm-smmu-v3: Skip duplicate SIDs when erasing streams from RB tree

From: Peng Fan (OSS)

Date: Thu Oct 01 2026 - 09:02:34 EST


From: Peng Fan <peng.fan@xxxxxxx>

Bridged PCI devices may end up with duplicated Stream IDs in
master->streams[]. During insertion, rb_find_add() silently
drops the duplicate node when it collides with an already-inserted
entry from the same master, leaving the duplicate's rb_node in an
uninitialized state.

However, both arm_smmu_remove_master() and the error rollback path
in arm_smmu_insert_master() unconditionally call rb_erase() on
every element of the streams array, including duplicates that were
never inserted. Calling rb_erase() on an uninitialized rb_node
corrupts the RB tree.

The streams array is sorted by SID before insertion, so duplicates
are always adjacent. Skip them during erasure to match the
insertion behavior.

Fixes: b00d24997a11c ("iommu/arm-smmu-v3: Fix iommu_device_probe bug due to duplicated stream ids")
Assisted-by: LLM
Signed-off-by: Peng Fan <peng.fan@xxxxxxx>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 5732f3ba0122d..a79f2250a4886 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -4153,8 +4153,11 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu,
}

if (ret) {
- for (i--; i >= 0; i--)
+ for (i--; i >= 0; i--) {
+ if (i > 0 && master->streams[i - 1].id == master->streams[i].id)
+ continue;
rb_erase(&master->streams[i].node, &smmu->streams);
+ }
kfree(master->streams);
kfree(master->build_invs);
}
@@ -4173,8 +4176,11 @@ static void arm_smmu_remove_master(struct arm_smmu_master *master)
return;

mutex_lock(&smmu->streams_mutex);
- for (i = 0; i < fwspec->num_ids; i++)
+ for (i = 0; i < fwspec->num_ids; i++) {
+ if (i > 0 && master->streams[i - 1].id == master->streams[i].id)
+ continue;
rb_erase(&master->streams[i].node, &smmu->streams);
+ }
mutex_unlock(&smmu->streams_mutex);

kfree(master->streams);

--
2.34.1