Skip to content

Commit d71fa84

Browse files
misalehwilldeacon
authored andcommitted
iommu/io-pgtable-arm: Fix stage-2 map/unmap for concatenated tables
ARM_LPAE_LVL_IDX() takes into account concatenated PGDs and can return an index spanning multiple page-table pages given a sufficiently large input address. However, when the resulting index is used to calculate the number of remaining entries in the page, the possibility of concatenation is ignored and we end up computing a negative upper bound: max_entries = ARM_LPAE_PTES_PER_TABLE(data) - map_idx_start; On the map path, this results in a negative 'mapped' value being returned but on the unmap path we can leak child tables if they are skipped in __arm_lpae_free_pgtable(). Introduce an arm_lpae_max_entries() helper to convert a table index into the remaining number of entries within a single page-table page. Cc: <stable@vger.kernel.org> Signed-off-by: Mostafa Saleh <smostafa@google.com> Link: https://lore.kernel.org/r/20241024162516.2005652-2-smostafa@google.com [will: Tweaked comment and commit message] Signed-off-by: Will Deacon <will@kernel.org>
1 parent 89edbe8 commit d71fa84

File tree

1 file changed

+15
-3
lines changed

1 file changed

+15
-3
lines changed

drivers/iommu/io-pgtable-arm.c

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,18 @@ static phys_addr_t iopte_to_paddr(arm_lpae_iopte pte,
199199
return (paddr | (paddr << (48 - 12))) & (ARM_LPAE_PTE_ADDR_MASK << 4);
200200
}
201201

202+
/*
203+
* Convert an index returned by ARM_LPAE_PGD_IDX(), which can point into
204+
* a concatenated PGD, into the maximum number of entries that can be
205+
* mapped in the same table page.
206+
*/
207+
static inline int arm_lpae_max_entries(int i, struct arm_lpae_io_pgtable *data)
208+
{
209+
int ptes_per_table = ARM_LPAE_PTES_PER_TABLE(data);
210+
211+
return ptes_per_table - (i & (ptes_per_table - 1));
212+
}
213+
202214
static bool selftest_running = false;
203215

204216
static dma_addr_t __arm_lpae_dma_addr(void *pages)
@@ -390,7 +402,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
390402

391403
/* If we can install a leaf entry at this level, then do so */
392404
if (size == block_size) {
393-
max_entries = ARM_LPAE_PTES_PER_TABLE(data) - map_idx_start;
405+
max_entries = arm_lpae_max_entries(map_idx_start, data);
394406
num_entries = min_t(int, pgcount, max_entries);
395407
ret = arm_lpae_init_pte(data, iova, paddr, prot, lvl, num_entries, ptep);
396408
if (!ret)
@@ -592,7 +604,7 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
592604

593605
if (size == split_sz) {
594606
unmap_idx_start = ARM_LPAE_LVL_IDX(iova, lvl, data);
595-
max_entries = ptes_per_table - unmap_idx_start;
607+
max_entries = arm_lpae_max_entries(unmap_idx_start, data);
596608
num_entries = min_t(int, pgcount, max_entries);
597609
}
598610

@@ -650,7 +662,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
650662

651663
/* If the size matches this level, we're in the right place */
652664
if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) {
653-
max_entries = ARM_LPAE_PTES_PER_TABLE(data) - unmap_idx_start;
665+
max_entries = arm_lpae_max_entries(unmap_idx_start, data);
654666
num_entries = min_t(int, pgcount, max_entries);
655667

656668
/* Find and handle non-leaf entries */

0 commit comments

Comments
 (0)