On Tue, Jan 25 2022 at 07:18, Fenghua Yu wrote: > On Mon, Jan 24, 2022 at 09:55:56PM +0100, Thomas Gleixner wrote: > /** > * ioasid_put - Release a reference to an ioasid > * @ioasid: the ID to remove
which in turn makes ioasid_put() a misnomer and the whole refcounting of the ioasid a pointless exercise. While looking at ioasid_put() usage I tripped over the following UAF issue: --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -4817,8 +4817,10 @@ static int aux_domain_add_dev(struct dma auxiliary_unlink_device(domain, dev); link_failed: spin_unlock_irqrestore(&device_domain_lock, flags); - if (list_empty(&domain->subdevices) && domain->default_pasid > 0) + if (list_empty(&domain->subdevices) && domain->default_pasid > 0) { ioasid_put(domain->default_pasid); + domain->default_pasid = INVALID_IOASID; + } return ret; } @@ -4847,8 +4849,10 @@ static void aux_domain_remove_dev(struct spin_unlock_irqrestore(&device_domain_lock, flags); - if (list_empty(&domain->subdevices) && domain->default_pasid > 0) + if (list_empty(&domain->subdevices) && domain->default_pasid > 0) { ioasid_put(domain->default_pasid); + domain->default_pasid = INVALID_IOASID; + } } static int prepare_domain_attach_device(struct iommu_domain *domain, Vs. ioasid_put() I think we should fold the following: --- a/drivers/iommu/intel/iommu.c +++ b/drivers/iommu/intel/iommu.c @@ -4818,7 +4818,7 @@ static int aux_domain_add_dev(struct dma link_failed: spin_unlock_irqrestore(&device_domain_lock, flags); if (list_empty(&domain->subdevices) && domain->default_pasid > 0) { - ioasid_put(domain->default_pasid); + ioasid_free(domain->default_pasid); domain->default_pasid = INVALID_IOASID; } @@ -4850,7 +4850,7 @@ static void aux_domain_remove_dev(struct spin_unlock_irqrestore(&device_domain_lock, flags); if (list_empty(&domain->subdevices) && domain->default_pasid > 0) { - ioasid_put(domain->default_pasid); + ioasid_free(domain->default_pasid); domain->default_pasid = INVALID_IOASID; } } --- a/drivers/iommu/ioasid.c +++ b/drivers/iommu/ioasid.c @@ -2,7 +2,7 @@ /* * I/O Address Space ID allocator. There is one global IOASID space, split into * subsets. Users create a subset with DECLARE_IOASID_SET, then allocate and - * free IOASIDs with ioasid_alloc and ioasid_put. + * free IOASIDs with ioasid_alloc() and ioasid_free(). */ #include <linux/ioasid.h> #include <linux/module.h> @@ -15,7 +15,6 @@ struct ioasid_data { struct ioasid_set *set; void *private; struct rcu_head rcu; - refcount_t refs; }; /* @@ -315,7 +314,6 @@ ioasid_t ioasid_alloc(struct ioasid_set data->set = set; data->private = private; - refcount_set(&data->refs, 1); /* * Custom allocator needs allocator data to perform platform specific @@ -348,17 +346,11 @@ ioasid_t ioasid_alloc(struct ioasid_set EXPORT_SYMBOL_GPL(ioasid_alloc); /** - * ioasid_put - Release a reference to an ioasid + * ioasid_free - Free an ioasid * @ioasid: the ID to remove - * - * Put a reference to the IOASID, free it when the number of references drops to - * zero. - * - * Return: %true if the IOASID was freed, %false otherwise. */ -bool ioasid_put(ioasid_t ioasid) +void ioasid_free(ioasid_t ioasid) { - bool free = false; struct ioasid_data *ioasid_data; spin_lock(&ioasid_allocator_lock); @@ -368,10 +360,6 @@ bool ioasid_put(ioasid_t ioasid) goto exit_unlock; } - free = refcount_dec_and_test(&ioasid_data->refs); - if (!free) - goto exit_unlock; - active_allocator->ops->free(ioasid, active_allocator->ops->pdata); /* Custom allocator needs additional steps to free the xa element */ if (active_allocator->flags & IOASID_ALLOCATOR_CUSTOM) { @@ -381,9 +369,8 @@ bool ioasid_put(ioasid_t ioasid) exit_unlock: spin_unlock(&ioasid_allocator_lock); - return free; } -EXPORT_SYMBOL_GPL(ioasid_put); +EXPORT_SYMBOL_GPL(ioasid_free); /** * ioasid_find - Find IOASID data --- a/include/linux/ioasid.h +++ b/include/linux/ioasid.h @@ -34,7 +34,7 @@ struct ioasid_allocator_ops { #if IS_ENABLED(CONFIG_IOASID) ioasid_t ioasid_alloc(struct ioasid_set *set, ioasid_t min, ioasid_t max, void *private); -bool ioasid_put(ioasid_t ioasid); +void ioasid_free(ioasid_t ioasid); void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid, bool (*getter)(void *)); int ioasid_register_allocator(struct ioasid_allocator_ops *allocator); @@ -52,10 +52,7 @@ static inline ioasid_t ioasid_alloc(stru return INVALID_IOASID; } -static inline bool ioasid_put(ioasid_t ioasid) -{ - return false; -} +static inline void ioasid_free(ioasid_t ioasid) { } static inline void *ioasid_find(struct ioasid_set *set, ioasid_t ioasid, bool (*getter)(void *)) --- a/include/linux/sched/mm.h +++ b/include/linux/sched/mm.h @@ -423,7 +423,7 @@ static inline void mm_pasid_set(struct m static inline void mm_pasid_drop(struct mm_struct *mm) { if (pasid_valid(mm->pasid)) { - ioasid_put(mm->pasid); + ioasid_free(mm->pasid); mm->pasid = INVALID_IOASID; } } _______________________________________________ iommu mailing list iommu@lists.linux-foundation.org https://lists.linuxfoundation.org/mailman/listinfo/iommu