Skip to content

Commit dccf95c

Browse files
misalehSasha Levin
authored andcommitted
iommu/io-pgtable-arm: Fix stage-2 map/unmap for concatenated tables
commit d71fa84 upstream. 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: <[email protected]> Signed-off-by: Mostafa Saleh <[email protected]> Link: https://lore.kernel.org/r/[email protected] [will: Tweaked comment and commit message] Signed-off-by: Will Deacon <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent f6275d3 commit dccf95c

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
@@ -180,6 +180,18 @@ static phys_addr_t iopte_to_paddr(arm_lpae_iopte pte,
180180
return (paddr | (paddr << (48 - 12))) & (ARM_LPAE_PTE_ADDR_MASK << 4);
181181
}
182182

183+
/*
184+
* Convert an index returned by ARM_LPAE_PGD_IDX(), which can point into
185+
* a concatenated PGD, into the maximum number of entries that can be
186+
* mapped in the same table page.
187+
*/
188+
static inline int arm_lpae_max_entries(int i, struct arm_lpae_io_pgtable *data)
189+
{
190+
int ptes_per_table = ARM_LPAE_PTES_PER_TABLE(data);
191+
192+
return ptes_per_table - (i & (ptes_per_table - 1));
193+
}
194+
183195
static bool selftest_running = false;
184196

185197
static dma_addr_t __arm_lpae_dma_addr(void *pages)
@@ -357,7 +369,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
357369

358370
/* If we can install a leaf entry at this level, then do so */
359371
if (size == block_size) {
360-
max_entries = ARM_LPAE_PTES_PER_TABLE(data) - map_idx_start;
372+
max_entries = arm_lpae_max_entries(map_idx_start, data);
361373
num_entries = min_t(int, pgcount, max_entries);
362374
ret = arm_lpae_init_pte(data, iova, paddr, prot, lvl, num_entries, ptep);
363375
if (!ret && mapped)
@@ -564,7 +576,7 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
564576

565577
if (size == split_sz) {
566578
unmap_idx_start = ARM_LPAE_LVL_IDX(iova, lvl, data);
567-
max_entries = ptes_per_table - unmap_idx_start;
579+
max_entries = arm_lpae_max_entries(unmap_idx_start, data);
568580
num_entries = min_t(int, pgcount, max_entries);
569581
}
570582

@@ -622,7 +634,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
622634

623635
/* If the size matches this level, we're in the right place */
624636
if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) {
625-
max_entries = ARM_LPAE_PTES_PER_TABLE(data) - unmap_idx_start;
637+
max_entries = arm_lpae_max_entries(unmap_idx_start, data);
626638
num_entries = min_t(int, pgcount, max_entries);
627639

628640
while (i < num_entries) {

0 commit comments

Comments
 (0)