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

powerpc/mm: Return NULL for not present hugetlb page

We need to check whether pte is present in follow_huge_addr() and
properly return NULL if mapping is not present. Also use READ_ONCE
when dereferencing pte_t address.

Without this patch, we may wrongly return a zero pfn page in
follow_huge_addr().

Reviewed-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Aneesh Kumar K.V and committed by
Michael Ellerman
7b868e81 13bd817b

+16 -9
+16 -9
arch/powerpc/mm/hugetlbpage.c
··· 689 689 struct page * 690 690 follow_huge_addr(struct mm_struct *mm, unsigned long address, int write) 691 691 { 692 - pte_t *ptep; 693 - struct page *page; 692 + pte_t *ptep, pte; 694 693 unsigned shift; 695 694 unsigned long mask, flags; 695 + struct page *page = ERR_PTR(-EINVAL); 696 + 697 + local_irq_save(flags); 698 + ptep = find_linux_pte_or_hugepte(mm->pgd, address, &shift); 699 + if (!ptep) 700 + goto no_page; 701 + pte = READ_ONCE(*ptep); 696 702 /* 703 + * Verify it is a huge page else bail. 697 704 * Transparent hugepages are handled by generic code. We can skip them 698 705 * here. 699 706 */ 700 - local_irq_save(flags); 701 - ptep = find_linux_pte_or_hugepte(mm->pgd, address, &shift); 707 + if (!shift || pmd_trans_huge(__pmd(pte_val(pte)))) 708 + goto no_page; 702 709 703 - /* Verify it is a huge page else bail. */ 704 - if (!ptep || !shift || pmd_trans_huge(*(pmd_t *)ptep)) { 705 - local_irq_restore(flags); 706 - return ERR_PTR(-EINVAL); 710 + if (!pte_present(pte)) { 711 + page = NULL; 712 + goto no_page; 707 713 } 708 714 mask = (1UL << shift) - 1; 709 - page = pte_page(*ptep); 715 + page = pte_page(pte); 710 716 if (page) 711 717 page += (address & mask) / PAGE_SIZE; 712 718 719 + no_page: 713 720 local_irq_restore(flags); 714 721 return page; 715 722 }