Currently, some of uvtop() functions on x86, x86_64 and ia64 lacks checking whether in pte _PAGE_PROTNONE bit is set or not. The flag is set to pte when specifying to the corresponding memory space PROT_NONE using mprotect(), and then _PAGE_PRESENT bit in the pte is unset in order to make hardware cause segmentation fault intensionally. Important is the fact that besides _PAGE_PRESENT set, _PAGE_PROTNONE indicates the page is present (not swapped out). The architectures fixed are x86, x86_64 and ia64 except for Xen. The patchset based on crash 5.1.4 consists of 5 patches, which are divided into the ones extending machine dependent data on x86 and x86_64, and the ones actually adding _PAGE_PROTNONE checking into each uvtop(). defs.h | 6 ++++-- ia64.c | 6 +++--- x86.c | 10 ++++++++-- x86_64.c | 11 +++++++++-- 4 files changed, 24 insertions(+), 9 deletions(-) More two files attached are the programs I used in order to create dumps for tests: testpro.c allocates 5 kinds of memory spaces in combination with mprotect()ed, mlock()ed or hugepage; mempres.c can be used to cause memory pressure to force pages to be swapped out. By the way, the issue I faced but have yet fixed is that on ia64, vtop() operation on mlock()ed hugepage memory space fails. The situation can be reproduced using the attached program. Thanks. HATAYAMA, Daisuke
>From adf7f226151969db067a22d09b96b7c0558af622 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke <d.hatayama@xxxxxxxxxxxxxx> Date: Thu, 28 Apr 2011 07:41:05 +0900 Subject: [PATCH 1/5] x86_64: Handle _PAGE_PROTNONE as machine specific data The definition of _PAGE_PROTNONE changed in version 2.6.28 from _PAGE_PSE to _PAGE_GLOBAL. See commit 1796316a8b028a148be48ba5d4e7be493a39d173. --- defs.h | 3 ++- x86_64.c | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletions(-) diff --git a/defs.h b/defs.h index d01ace7..6710f61 100755 --- a/defs.h +++ b/defs.h @@ -2432,7 +2432,7 @@ struct load_module { #define _PAGE_PSE 0x080 /* 2MB page */ #define _PAGE_FILE 0x040 /* set:pagecache, unset:swap */ #define _PAGE_GLOBAL 0x100 /* Global TLB entry */ -#define _PAGE_PROTNONE 0x080 /* If not present */ +#define _PAGE_PROTNONE (machdep->machspec->page_protnone) #define _PAGE_NX (1UL<<_PAGE_BIT_NX) #define SWP_TYPE(entry) (((entry) >> 1) & 0x3f) @@ -4272,6 +4272,7 @@ struct machine_specific { ulong *crash_nmi_rsp; ulong vsyscall_page; ulong thread_return; + ulong page_protnone; }; #define KSYMS_START (0x1) diff --git a/x86_64.c b/x86_64.c index eaf6373..ba73cf9 100755 --- a/x86_64.c +++ b/x86_64.c @@ -436,6 +436,13 @@ x86_64_init(int when) x86_64_irq_eframe_link_init(); x86_64_framepointer_init(); x86_64_thread_return_init(); + + if (THIS_KERNEL_VERSION >= LINUX(2,6,28)) { + machdep->machspec->page_protnone = _PAGE_GLOBAL; + } else { + machdep->machspec->page_protnone = _PAGE_PSE; + } + break; case POST_VM: -- 1.7.4.4
>From 7aec2ffc14516de513538bead07c41e5f9f7d73e Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke <d.hatayama@xxxxxxxxxxxxxx> Date: Thu, 28 Apr 2011 07:46:44 +0900 Subject: [PATCH 2/5] x86_64: Add _PAGE_PROTNONE check _PAGE_PROTNONE collides with _PAGE_PSE on earlier than 2.6.28 but this is no problem because hugepages on those kernels are never swapped out. --- x86_64.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x86_64.c b/x86_64.c index ba73cf9..1a442a8 100755 --- a/x86_64.c +++ b/x86_64.c @@ -1288,7 +1288,7 @@ x86_64_uvtop_level4(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, in pmd_pte = ULONG(machdep->pmd + PAGEOFFSET(pmd)); if (verbose) fprintf(fp, " PMD: %lx => %lx\n", (ulong)pmd, pmd_pte); - if (!(pmd_pte & _PAGE_PRESENT)) + if (!(pmd_pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) goto no_upage; if (pmd_pte & _PAGE_PSE) { if (verbose) { @@ -2083,7 +2083,7 @@ x86_64_translate_pte(ulong pte, void *physaddr, ulonglong unused) int page_present; paddr = pte & PHYSICAL_PAGE_MASK; - page_present = pte & _PAGE_PRESENT; + page_present = pte & (_PAGE_PRESENT | _PAGE_PROTNONE); if (physaddr) { *((ulong *)physaddr) = paddr; -- 1.7.4.4
>From 2ed56eb2df107211b04aee6f6f4571ec993d38de Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke <d.hatayama@xxxxxxxxxxxxxx> Date: Thu, 28 Apr 2011 07:50:12 +0900 Subject: [PATCH 3/5] x86: Handle _PAGE_PROTNONE as machine specific data Same as on x86_64. --- defs.h | 3 ++- x86.c | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletions(-) diff --git a/defs.h b/defs.h index 6710f61..d16f13a 100755 --- a/defs.h +++ b/defs.h @@ -2254,7 +2254,7 @@ struct load_module { #define _PAGE_4M 0x080 /* 4 MB page, Pentium+, if present.. */ #define _PAGE_PSE 0x080 /* 4 MB (or 2MB) page, Pentium+, if present.. */ #define _PAGE_GLOBAL 0x100 /* Global TLB entry PPro+ */ -#define _PAGE_PROTNONE 0x080 /* If not present */ +#define _PAGE_PROTNONE (machdep->machspec->page_protnone) #define _PAGE_NX (0x8000000000000000ULL) #define NONPAE_PAGEBASE(X) (((unsigned long)(X)) & (unsigned long)machdep->pagemask) @@ -4195,6 +4195,7 @@ struct machine_specific { physaddr_t entry_tramp_start_phys; ulonglong last_pmd_read_PAE; ulonglong last_ptbl_read_PAE; + ulong page_protnone; }; struct syment *x86_is_entry_tramp_address(ulong, ulong *); diff --git a/x86.c b/x86.c index c5cf010..20f9e11 100755 --- a/x86.c +++ b/x86.c @@ -1876,6 +1876,12 @@ x86_init(int when) machdep->line_number_hooks = x86_line_number_hooks; eframe_init(); + + if (THIS_KERNEL_VERSION >= LINUX(2,6,28)) + machdep->machspec->page_protnone = _PAGE_GLOBAL; + else + machdep->machspec->page_protnone = _PAGE_PSE; + break; case POST_INIT: -- 1.7.4.4
>From 6e3abd9af5bc2c8b7104b5daa59bf0d6ea1790f4 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke <d.hatayama@xxxxxxxxxxxxxx> Date: Thu, 28 Apr 2011 07:54:22 +0900 Subject: [PATCH 4/5] x86: Add _PAGE_PROTNONE check --- x86.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x86.c b/x86.c index 20f9e11..d795bb2 100755 --- a/x86.c +++ b/x86.c @@ -2034,7 +2034,7 @@ x86_uvtop(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbose) MKSTR((ulong)page_dir)), pgd_pte); - if (!pgd_pte) + if (!(pgd_pte & (_PAGE_PRESENT | _PAGE_PROTNONE))) goto no_upage; if (pgd_pte & _PAGE_4M) { @@ -2356,7 +2356,7 @@ x86_uvtop_PAE(struct task_context *tc, ulong vaddr, physaddr_t *paddr, int verbo page_middle_entry); } - if (!(page_middle_entry & _PAGE_PRESENT)) { + if (!(page_middle_entry & (_PAGE_PRESENT | _PAGE_PROTNONE))) { goto no_upage; } -- 1.7.4.4
>From 67c99f8b59a3fa8a098967237ec1687b91632f63 Mon Sep 17 00:00:00 2001 From: HATAYAMA Daisuke <d.hatayama@xxxxxxxxxxxxxx> Date: Thu, 28 Apr 2011 07:59:51 +0900 Subject: [PATCH 5/5] ia64: Add _PAGE_PROTNONE check The variable page_present in ia64_translate_pte() has type int with 4-byte length and _PAGE_PROTNONE bit on ia64 is 63. (!!) operation is added in order to deal with overflow. --- ia64.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ia64.c b/ia64.c index 9f8c06d..5b1f99e 100755 --- a/ia64.c +++ b/ia64.c @@ -893,7 +893,7 @@ ia64_vtop_4l(ulong vaddr, physaddr_t *paddr, ulong *pgd, int verbose, int usr) if (verbose) fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); - if (!(pte & (_PAGE_P))) { + if (!(pte & (_PAGE_P | _PAGE_PROTNONE))) { if (usr) *paddr = pte; if (pte && verbose) { @@ -971,7 +971,7 @@ ia64_vtop(ulong vaddr, physaddr_t *paddr, ulong *pgd, int verbose, int usr) if (verbose) fprintf(fp, " PTE: %lx => %lx\n", (ulong)page_table, pte); - if (!(pte & (_PAGE_P))) { + if (!(pte & (_PAGE_P | _PAGE_PROTNONE))) { if (usr) *paddr = pte; if (pte && verbose) { @@ -1237,7 +1237,7 @@ ia64_translate_pte(ulong pte, void *physaddr, ulonglong unused) ulong paddr; paddr = pte & _PFN_MASK; - page_present = pte & _PAGE_P; + page_present = !!(pte & (_PAGE_P | _PAGE_PROTNONE)); if (physaddr) { *((ulong *)physaddr) = paddr; -- 1.7.4.4
/* * Take the following step before you run this program: * * 1. set up the environment to be able to use hugetlb feature. * * $ echo 10 > /proc/sys/vm/nr_hugepages * * - This operation is intended to change the number of * hugepages that system can allocate from physical memory. * * - You can check a single huge page size from /proc/meminfo * * $ grep "Huge" /proc/meminfo * HugePages_Total: 128 * HugePages_Free: 128 * HugePages_Rsvd: 0 * Hugepagesize: 2048 kB * * - You need to set up the number of hugepages enough for required memory size. * * $ mkdir -p /media/hugetlb * $ mount -t hugetlbfs none /media/hugetlb -o uid=n,gid=m,mode=0777 * $ echo m > /proc/sys/vm/hugetlb_shm_group * * - Both n and m are 0 for root user; so it's easy to specify this by * doing as root user. * * 2. Use mmap or shmget to map hugepages via /media/hugetlb. * * Reference * * [1] linux-2.6/Documentation/vm/hugetlbpage.txt * */ #include <errno.h> #include <fcntl.h> #include <libgen.h> #include <malloc.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> static void pmapx(void); static size_t get_hugepagesize(void); static void *hugepage_alloc(size_t size); int main(int argc, char **argv) { unsigned long i; char *obj_swap, *obj_locked, *obj_huge_swap, *obj_huge_locked, *obj_huge_locked_nonprotect; long pagesize; size_t hugepagesize; if ((pagesize = sysconf(_SC_PAGESIZE)) < 0) goto error; if (!(hugepagesize = get_hugepagesize())) goto error; if (!(obj_swap = valloc(pagesize))) goto error; if (!(obj_locked = valloc(pagesize))) goto error; if (!(obj_huge_swap = hugepage_alloc(hugepagesize))) goto error; if (!(obj_huge_locked = hugepage_alloc(hugepagesize))) goto error; if (!(obj_huge_locked_nonprotect = hugepage_alloc(hugepagesize))) goto error; memset(obj_swap, 's', pagesize); memset(obj_locked, 'l', pagesize); memset(obj_huge_swap, 'S', hugepagesize); memset(obj_huge_locked, 'L', hugepagesize); memset(obj_huge_locked_nonprotect, 'N', hugepagesize); if (mlock(obj_locked, pagesize)) goto error; if (mlock(obj_huge_locked, hugepagesize)) goto error; if (mlock(obj_huge_locked_nonprotect, hugepagesize)) goto error; if (mprotect(obj_locked, pagesize, PROT_NONE) < 0) goto error; if (mprotect(obj_swap, pagesize, PROT_NONE) < 0) goto error; if (mprotect(obj_huge_locked, hugepagesize, PROT_NONE) < 0) goto error; if (mprotect(obj_huge_swap, hugepagesize, PROT_NONE) < 0) goto error; for (;;) { sleep(100); } return EXIT_SUCCESS; error: fprintf(stderr, "%s: %s\n", basename(argv[0]), strerror(errno)); return EXIT_FAILURE; } static size_t get_hugepagesize(void) { char buf[128]; FILE *meminfo; size_t hugepagesize; meminfo = fopen("/proc/meminfo", "r"); if (!meminfo) return 0; for (;;) { fgets(buf, sizeof(buf), meminfo); if (ferror(meminfo)) return 0; if (strstr(buf, "Hugepagesize:")) break; if (feof(meminfo)) return 0; } fclose(meminfo); sscanf(buf, "Hugepagesize: %lu kB", &hugepagesize); hugepagesize *= 1024; /* kB => B */ return hugepagesize; } static void *hugepage_alloc(size_t size) { int c, fd; void *obj; fd = open("/media/hugetlb/hugepage_protnone", O_CREAT | O_RDWR, 0755); if (fd < 0) return NULL; c = 'K'; write(fd, &c, 1); obj = mmap(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (obj < 0) return NULL; return obj; }
#include <malloc.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <unistd.h> int main(int argc, char **argv) { size_t memsize = (1UL << 30UL); long pagesize; int i; char *obj; pagesize = sysconf(_SC_PAGESIZE); obj = memalign(pagesize, memsize); if (!obj) { perror("malloc"); exit(EXIT_FAILURE); } for (;;) { for (i = 0; i < memsize; ++i) { obj[i] = 'O'; } } return 0; }
-- Crash-utility mailing list Crash-utility@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/crash-utility