Patchwork [3.5.y.z,extended,stable] Patch "x86/mm: Check if PUD is large when validating a kernel" has been added to staging queue

mail settings
Submitter Herton Ronaldo Krzesinski
Date Feb. 15, 2013, 5:03 p.m.
Message ID <>
Download mbox | patch
Permalink /patch/220777/
State New
Headers show


Herton Ronaldo Krzesinski - Feb. 15, 2013, 5:03 p.m.
This is a note to let you know that I have just added a patch titled

    x86/mm: Check if PUD is large when validating a kernel

to the linux-3.5.y-queue branch of the 3.5.y.z extended stable tree 
which can be found at:;a=shortlog;h=refs/heads/linux-3.5.y-queue

If you, or anyone else, feels it should not be added to this tree, please 
reply to this email.

For more information about the 3.5.y.z tree, see



From 6188e408d02d10d48f193d357e271ac9a046871a Mon Sep 17 00:00:00 2001
From: Mel Gorman <>
Date: Mon, 11 Feb 2013 14:52:36 +0000
Subject: [PATCH] x86/mm: Check if PUD is large when validating a kernel

commit 0ee364eb316348ddf3e0dfcd986f5f13f528f821 upstream.

A user reported the following oops when a backup process reads

 BUG: unable to handle kernel paging request at ffffbb00ff33b000
 IP: [<ffffffff8103157e>] kern_addr_valid+0xbe/0x110

 Call Trace:
  [<ffffffff811b8aaa>] read_kcore+0x17a/0x370
  [<ffffffff811ad847>] proc_reg_read+0x77/0xc0
  [<ffffffff81151687>] vfs_read+0xc7/0x130
  [<ffffffff811517f3>] sys_read+0x53/0xa0
  [<ffffffff81449692>] system_call_fastpath+0x16/0x1b

Investigation determined that the bug triggered when reading
system RAM at the 4G mark. On this system, that was the first
address using 1G pages for the virt->phys direct mapping so the
PUD is pointing to a physical address, not a PMD page.

The problem is that the page table walker in kern_addr_valid() is
not checking pud_large() and treats the physical address as if
it was a PMD.  If it happens to look like pmd_none then it'll
silently fail, probably returning zeros instead of real data. If
the data happens to look like a present PMD though, it will be
walked resulting in the oops above.

This patch adds the necessary pud_large() check.

Unfortunately the problem was not readily reproducible and now
they are running the backup program without accessing
/proc/kcore so the patch has not been validated but I think it
makes sense.

Signed-off-by: Mel Gorman <>
Reviewed-by: Rik van Riel <riel@redhat.coM>
Reviewed-by: Michal Hocko <>
Acked-by: Johannes Weiner <>
Signed-off-by: Ingo Molnar <>
Signed-off-by: Herton Ronaldo Krzesinski <>
 arch/x86/include/asm/pgtable.h |    5 +++++
 arch/x86/mm/init_64.c          |    3 +++
 2 files changed, 8 insertions(+)



diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index c3520d7..3f3dd52 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -142,6 +142,11 @@  static inline unsigned long pmd_pfn(pmd_t pmd)
 	return (pmd_val(pmd) & PTE_PFN_MASK) >> PAGE_SHIFT;

+static inline unsigned long pud_pfn(pud_t pud)
+	return (pud_val(pud) & PTE_PFN_MASK) >> PAGE_SHIFT;
 #define pte_page(pte)	pfn_to_page(pte_pfn(pte))

 static inline int pmd_large(pmd_t pte)
diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c
index 3baff25..ce42da7 100644
--- a/arch/x86/mm/init_64.c
+++ b/arch/x86/mm/init_64.c
@@ -829,6 +829,9 @@  int kern_addr_valid(unsigned long addr)
 	if (pud_none(*pud))
 		return 0;

+	if (pud_large(*pud))
+		return pfn_valid(pud_pfn(*pud));
 	pmd = pmd_offset(pud, addr);
 	if (pmd_none(*pmd))
 		return 0;