diff options
| author | Jiri Slaby <jirislaby@gmail.com> | 2009-10-26 06:11:43 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-10-26 07:38:59 -0400 |
| commit | 72ed7de74e8f0fad0d8e567ae1f987b740accb3f (patch) | |
| tree | dd8e7dbd4b6305bcca35fc2cfe29479918e034d2 | |
| parent | ae1b22f6e46c03cede7cea234d0bf2253b4261cf (diff) | |
x86: crash_dump: Fix non-pae kdump kernel memory accesses
Non-PAE 32-bit dump kernels may wrap an address around 4G and
poke unwanted space. ptes there are 32-bit long, and since
pfn << PAGE_SIZE may exceed this limit, high pfn bits are
cropped and wrong address mapped by kmap_atomic_pfn in
copy_oldmem_page.
Don't allow this behavior in non-PAE kdump kernels by checking
pfns passed into copy_oldmem_page. In the case of failure,
userspace process gets EFAULT.
[v2]
- fix comments
- move ifdefs inside the function
Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Eric W. Biederman <ebiederm@xmission.com>
Cc: Simon Horman <horms@verge.net.au>
Cc: Paul Mundt <lethal@linux-sh.org>
LKML-Reference: <1256551903-30567-1-git-send-email-jirislaby@gmail.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
| -rw-r--r-- | arch/x86/kernel/crash_dump_32.c | 19 |
1 files changed, 19 insertions, 0 deletions
diff --git a/arch/x86/kernel/crash_dump_32.c b/arch/x86/kernel/crash_dump_32.c index f7cdb3b457aa..cd97ce18c29d 100644 --- a/arch/x86/kernel/crash_dump_32.c +++ b/arch/x86/kernel/crash_dump_32.c | |||
| @@ -16,6 +16,22 @@ static void *kdump_buf_page; | |||
| 16 | /* Stores the physical address of elf header of crash image. */ | 16 | /* Stores the physical address of elf header of crash image. */ |
| 17 | unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX; | 17 | unsigned long long elfcorehdr_addr = ELFCORE_ADDR_MAX; |
| 18 | 18 | ||
| 19 | static inline bool is_crashed_pfn_valid(unsigned long pfn) | ||
| 20 | { | ||
| 21 | #ifndef CONFIG_X86_PAE | ||
| 22 | /* | ||
| 23 | * non-PAE kdump kernel executed from a PAE one will crop high pte | ||
| 24 | * bits and poke unwanted space counting again from address 0, we | ||
| 25 | * don't want that. pte must fit into unsigned long. In fact the | ||
| 26 | * test checks high 12 bits for being zero (pfn will be shifted left | ||
| 27 | * by PAGE_SHIFT). | ||
| 28 | */ | ||
| 29 | return pte_pfn(pfn_pte(pfn, __pgprot(0))) == pfn; | ||
| 30 | #else | ||
| 31 | return true; | ||
| 32 | #endif | ||
| 33 | } | ||
| 34 | |||
| 19 | /** | 35 | /** |
| 20 | * copy_oldmem_page - copy one page from "oldmem" | 36 | * copy_oldmem_page - copy one page from "oldmem" |
| 21 | * @pfn: page frame number to be copied | 37 | * @pfn: page frame number to be copied |
| @@ -41,6 +57,9 @@ ssize_t copy_oldmem_page(unsigned long pfn, char *buf, | |||
| 41 | if (!csize) | 57 | if (!csize) |
| 42 | return 0; | 58 | return 0; |
| 43 | 59 | ||
| 60 | if (!is_crashed_pfn_valid(pfn)) | ||
| 61 | return -EFAULT; | ||
| 62 | |||
| 44 | vaddr = kmap_atomic_pfn(pfn, KM_PTE0); | 63 | vaddr = kmap_atomic_pfn(pfn, KM_PTE0); |
| 45 | 64 | ||
| 46 | if (!userbuf) { | 65 | if (!userbuf) { |
