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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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 <[email protected]>
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
[email protected]
https://www.redhat.com/mailman/listinfo/crash-utility