| From f7116e115acdd74bc75a4daf6492b11d43505125 Mon Sep 17 00:00:00 2001 |
| From: David Dillow <dillow@google.com> |
| Date: Mon, 30 Jan 2017 19:11:11 -0800 |
| Subject: iommu/vt-d: Don't over-free page table directories |
| |
| From: David Dillow <dillow@google.com> |
| |
| commit f7116e115acdd74bc75a4daf6492b11d43505125 upstream. |
| |
| dma_pte_free_level() recurses down the IOMMU page tables and frees |
| directory pages that are entirely contained in the given PFN range. |
| Unfortunately, it incorrectly calculates the starting address covered |
| by the PTE under consideration, which can lead to it clearing an entry |
| that is still in use. |
| |
| This occurs if we have a scatterlist with an entry that has a length |
| greater than 1026 MB and is aligned to 2 MB for both the IOMMU and |
| physical addresses. For example, if __domain_mapping() is asked to map a |
| two-entry scatterlist with 2 MB and 1028 MB segments to PFN 0xffff80000, |
| it will ask if dma_pte_free_pagetable() is asked to PFNs from |
| 0xffff80200 to 0xffffc05ff, it will also incorrectly clear the PFNs from |
| 0xffff80000 to 0xffff801ff because of this issue. The current code will |
| set level_pfn to 0xffff80200, and 0xffff80200-0xffffc01ff fits inside |
| the range being cleared. Properly setting the level_pfn for the current |
| level under consideration catches that this PTE is outside of the range |
| being cleared. |
| |
| This patch also changes the value passed into dma_pte_free_level() when |
| it recurses. This only affects the first PTE of the range being cleared, |
| and is handled by the existing code that ensures we start our cursor no |
| lower than start_pfn. |
| |
| This was found when using dma_map_sg() to map large chunks of contiguous |
| memory, which immediatedly led to faults on the first access of the |
| erroneously-deleted mappings. |
| |
| Fixes: 3269ee0bd668 ("intel-iommu: Fix leaks in pagetable freeing") |
| Reviewed-by: Benjamin Serebrin <serebrin@google.com> |
| Signed-off-by: David Dillow <dillow@google.com> |
| Signed-off-by: Joerg Roedel <jroedel@suse.de> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/iommu/intel-iommu.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/drivers/iommu/intel-iommu.c |
| +++ b/drivers/iommu/intel-iommu.c |
| @@ -980,7 +980,7 @@ static void dma_pte_free_level(struct dm |
| if (!dma_pte_present(pte) || dma_pte_superpage(pte)) |
| goto next; |
| |
| - level_pfn = pfn & level_mask(level - 1); |
| + level_pfn = pfn & level_mask(level); |
| level_pte = phys_to_virt(dma_pte_addr(pte)); |
| |
| if (level > 2) |