Commit 59c36286 authored by David Woodhouse's avatar David Woodhouse

intel-iommu: Fix integer overflow in dma_pte_{clear_range,free_pagetable}()

If end_pfn is equal to (unsigned long)-1, then the loop will never end.

Seen on 32-bit kernel, but could have happened on 64-bit too once we get
hardware that supports 64-bit guest addresses.

Change both functions to a 'do {} while' loop with the test at the end,
and check for the PFN having wrapper round to zero.
Reported-by: default avatarBenjamin LaHaise <ben.lahaise@neterion.com>
Tested-by: default avatarBenjamin LaHaise <ben.lahaise@neterion.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 2ebe3151
...@@ -785,9 +785,10 @@ static void dma_pte_clear_range(struct dmar_domain *domain, ...@@ -785,9 +785,10 @@ static void dma_pte_clear_range(struct dmar_domain *domain,
BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width);
BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width);
BUG_ON(start_pfn > last_pfn);
/* we don't need lock here; nobody else touches the iova range */ /* we don't need lock here; nobody else touches the iova range */
while (start_pfn <= last_pfn) { do {
first_pte = pte = dma_pfn_level_pte(domain, start_pfn, 1); first_pte = pte = dma_pfn_level_pte(domain, start_pfn, 1);
if (!pte) { if (!pte) {
start_pfn = align_to_level(start_pfn + 1, 2); start_pfn = align_to_level(start_pfn + 1, 2);
...@@ -801,7 +802,8 @@ static void dma_pte_clear_range(struct dmar_domain *domain, ...@@ -801,7 +802,8 @@ static void dma_pte_clear_range(struct dmar_domain *domain,
domain_flush_cache(domain, first_pte, domain_flush_cache(domain, first_pte,
(void *)pte - (void *)first_pte); (void *)pte - (void *)first_pte);
}
} while (start_pfn && start_pfn <= last_pfn);
} }
/* free page table pages. last level pte should already be cleared */ /* free page table pages. last level pte should already be cleared */
...@@ -817,6 +819,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, ...@@ -817,6 +819,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && start_pfn >> addr_width);
BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && last_pfn >> addr_width);
BUG_ON(start_pfn > last_pfn);
/* We don't need lock here; nobody else touches the iova range */ /* We don't need lock here; nobody else touches the iova range */
level = 2; level = 2;
...@@ -827,7 +830,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, ...@@ -827,7 +830,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
if (tmp + level_size(level) - 1 > last_pfn) if (tmp + level_size(level) - 1 > last_pfn)
return; return;
while (tmp + level_size(level) - 1 <= last_pfn) { do {
first_pte = pte = dma_pfn_level_pte(domain, tmp, level); first_pte = pte = dma_pfn_level_pte(domain, tmp, level);
if (!pte) { if (!pte) {
tmp = align_to_level(tmp + 1, level + 1); tmp = align_to_level(tmp + 1, level + 1);
...@@ -846,7 +849,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain, ...@@ -846,7 +849,7 @@ static void dma_pte_free_pagetable(struct dmar_domain *domain,
domain_flush_cache(domain, first_pte, domain_flush_cache(domain, first_pte,
(void *)pte - (void *)first_pte); (void *)pte - (void *)first_pte);
} } while (tmp && tmp + level_size(level) - 1 <= last_pfn);
level++; level++;
} }
/* free pgd */ /* free pgd */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment