Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

iommupt: Fix unlikely flows in increase_top()

Since increase_top() does it's own READ_ONCE() on top_of_table, the
caller's prior READ_ONCE() could be inconsistent and the first time
through the loop we may actually already have the right level if two
threads are racing map.

In this case new_level will be left uninitialized.

Further all the exits from the loop have to either commit to the new top
or free any memory allocated so the early return must be a goto err_free.

Make it so the only break from the loop always sets new_level to the right
value and all other exits go to err_free. Use pts.level (the pts
represents the top we are stacking) within the loop instead of new_level.

Fixes: dcd6a011a8d5 ("iommupt: Add map_pages op")
Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
Closes: https://lore.kernel.org/r/aRwgNW9PiW2j-Qwo@stanley.mountain
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
Reviewed-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com>
Reviewed-by: Vasant Hegde <vasant.hegde@amd.com>
Reviewed-by: Kevin Tian <kevin.tian@intel.com>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>

authored by

Jason Gunthorpe and committed by
Joerg Roedel
152c862c 1e8b6eb1

+11 -6
+11 -6
drivers/iommu/generic_pt/iommu_pt.h
··· 683 683 top_range.va = range->va; 684 684 top_range.last_va = range->last_va; 685 685 686 - if (!pt_check_range(&top_range) && map->leaf_level <= pts.level) 686 + if (!pt_check_range(&top_range) && 687 + map->leaf_level <= pts.level) { 688 + new_level = pts.level; 687 689 break; 690 + } 688 691 689 692 pts.level++; 690 693 if (pts.level > PT_MAX_TOP_LEVEL || ··· 696 693 goto err_free; 697 694 } 698 695 699 - new_level = pts.level; 700 696 table_mem = 701 697 table_alloc_top(common, _pt_top_set(NULL, pts.level), 702 698 map->attrs.gfp, ALLOC_DEFER_COHERENT_FLUSH); 703 - if (IS_ERR(table_mem)) 704 - return PTR_ERR(table_mem); 699 + if (IS_ERR(table_mem)) { 700 + ret = PTR_ERR(table_mem); 701 + goto err_free; 702 + } 705 703 iommu_pages_list_add(&free_list, table_mem); 706 704 707 705 /* The new table links to the lower table always at index 0 */ 708 706 top_range.va = 0; 709 - top_range.top_level = new_level; 707 + top_range.top_level = pts.level; 710 708 pts.table_lower = pts.table; 711 709 pts.table = table_mem; 712 710 pt_load_single_entry(&pts); ··· 739 735 */ 740 736 domain_lock = iommu_table->driver_ops->get_top_lock(iommu_table); 741 737 spin_lock_irqsave(domain_lock, flags); 742 - if (common->top_of_table != top_of_table) { 738 + if (common->top_of_table != top_of_table || 739 + top_of_table == new_top_of_table) { 743 740 spin_unlock_irqrestore(domain_lock, flags); 744 741 ret = -EAGAIN; 745 742 goto err_free;