Check _PAGE_PROTNONE bit in uvtop()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

[Index of Archives]     [Fedora Development]     [Fedora Desktop]     [Fedora SELinux]     [Yosemite News]     [KDE Users]     [Fedora Tools]

 

Powered by Linux