vfs: fix data corruption when blocksize < pagesize for mmaped data
[platform/adaptation/renesas_rcar/renesas_kernel.git] / mm / huge_memory.c
index 1546655..718bfa1 100644 (file)
@@ -1611,16 +1611,23 @@ pmd_t *page_check_address_pmd(struct page *page,
                              enum page_check_address_pmd_flag flag,
                              spinlock_t **ptl)
 {
+       pgd_t *pgd;
+       pud_t *pud;
        pmd_t *pmd;
 
        if (address & ~HPAGE_PMD_MASK)
                return NULL;
 
-       pmd = mm_find_pmd(mm, address);
-       if (!pmd)
+       pgd = pgd_offset(mm, address);
+       if (!pgd_present(*pgd))
                return NULL;
+       pud = pud_offset(pgd, address);
+       if (!pud_present(*pud))
+               return NULL;
+       pmd = pmd_offset(pud, address);
+
        *ptl = pmd_lock(mm, pmd);
-       if (pmd_none(*pmd))
+       if (!pmd_present(*pmd))
                goto unlock;
        if (pmd_page(*pmd) != page)
                goto unlock;
@@ -1812,21 +1819,24 @@ static int __split_huge_page_map(struct page *page,
        if (pmd) {
                pgtable = pgtable_trans_huge_withdraw(mm, pmd);
                pmd_populate(mm, &_pmd, pgtable);
+               if (pmd_write(*pmd))
+                       BUG_ON(page_mapcount(page) != 1);
 
                haddr = address;
                for (i = 0; i < HPAGE_PMD_NR; i++, haddr += PAGE_SIZE) {
                        pte_t *pte, entry;
                        BUG_ON(PageCompound(page+i));
+                       /*
+                        * Note that pmd_numa is not transferred deliberately
+                        * to avoid any possibility that pte_numa leaks to
+                        * a PROT_NONE VMA by accident.
+                        */
                        entry = mk_pte(page + i, vma->vm_page_prot);
                        entry = maybe_mkwrite(pte_mkdirty(entry), vma);
                        if (!pmd_write(*pmd))
                                entry = pte_wrprotect(entry);
-                       else
-                               BUG_ON(page_mapcount(page) != 1);
                        if (!pmd_young(*pmd))
                                entry = pte_mkold(entry);
-                       if (pmd_numa(*pmd))
-                               entry = pte_mknuma(entry);
                        pte = pte_offset_map(&_pmd, haddr);
                        BUG_ON(!pte_none(*pte));
                        set_pte_at(mm, haddr, pte, entry);