From a329008ea54056f0ed9d85cc3d0d9129474f7cd5 Mon Sep 17 00:00:00 2001 Message-Id: In-Reply-To: <192ac21a3c057c5dedca4cdd1bf700f38992030c.1496667760.git.jstancek@redhat.com> References: <192ac21a3c057c5dedca4cdd1bf700f38992030c.1496667760.git.jstancek@redhat.com> From: Jan Stancek Date: Thu, 1 Jun 2017 10:00:47 +0200 Subject: [PATCH v2 2/2] stack_grow_into_huge: don't clobber existing mappings This test allocates hugepages above stack using MAP_FIXED and then grows stack while it can. If a MAP_FIXED request is successful, then mapping established by mmap() replaces any previous mappings for the process' pages. If there's anything important there (libc mappings), these can get clobbered as described here: http://marc.info/?l=linux-arm-kernel&m=149036535209519&w=2. This patch is creating extra stack for new child and maps one hugepage above it. The search starts at heap until it hits existing mapping or until it can successfully map huge page and stack below it. If suitable place can't be found, test PASSes as inconclusive. Signed-off-by: Jan Stancek --- tests/stack_grow_into_huge.c | 101 ++++++++++++++++++++++++++++--------------- 1 file changed, 67 insertions(+), 34 deletions(-) This is a v2 series for: https://groups.google.com/forum/#!topic/libhugetlbfs/tAsWjuJ7x8k diff --git a/tests/stack_grow_into_huge.c b/tests/stack_grow_into_huge.c index a380da063264..9b8ea8d74887 100644 --- a/tests/stack_grow_into_huge.c +++ b/tests/stack_grow_into_huge.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "hugetests.h" @@ -54,7 +55,10 @@ #define STACK_ALLOCATION_SIZE (16*1024*1024) #endif -void do_child(void *stop_address) +#define MIN_CHILD_STACK (2*1024*1024) +#define STEP (STACK_ALLOCATION_SIZE) + +int do_child(void *stop_address) { struct rlimit r; volatile int *x; @@ -71,15 +75,68 @@ void do_child(void *stop_address) x = alloca(STACK_ALLOCATION_SIZE); *x = 1; } while ((void *)x >= stop_address); + + return 0; +} + +void *try_setup_stack_and_huge(int fd, void *hint) +{ + void *mmap_address, *stack_start, *tmp; + long hpage_size = gethugepagesize(); + void *stop = alloca(1); + + /* + * Find a spot for huge page. We start at "hint" and + * keep going down in "STEP" increments until we find + * a place where we can mmap huge page. + */ + mmap_address = PALIGN(hint, hpage_size); + do { + mmap_address += STEP; + if (mmap_address >= stop) + return NULL; + if (range_is_mapped((unsigned long)mmap_address, + (unsigned long)mmap_address + hpage_size)) + continue; + tmp = mmap(mmap_address, hpage_size, + PROT_READ|PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0); + } while (tmp == MAP_FAILED); + verbose_printf("huge page is at: %p-%p\n", + mmap_address, mmap_address + hpage_size); + + /* + * Find a spot for stack below huge page. We start at end of + * huge page we found above and keep trying to mmap stack + * below. Because stack needs to grow into hugepage, we + * also have to make sure nothing is mapped in gap between + * stack and huge page. + */ + stack_start = mmap_address + hpage_size; + do { + if (range_is_mapped((unsigned long)stack_start, + (unsigned long)stack_start + STEP + MIN_CHILD_STACK)) { + verbose_printf("range is mapped: %p-%p\n", stack_start, + stack_start + STEP + MIN_CHILD_STACK); + munmap(mmap_address, hpage_size); + return NULL; + } + stack_start += STEP; + if (stack_start >= stop) + return NULL; + tmp = mmap(stack_start, MIN_CHILD_STACK, PROT_READ|PROT_WRITE, + MAP_GROWSDOWN|MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); + } while (tmp == MAP_FAILED); + + verbose_printf("Child stack is at %p-%p\n", + stack_start, stack_start + MIN_CHILD_STACK); + return stack_start + MIN_CHILD_STACK; } int main(int argc, char *argv[]) { int fd, pid, s, ret; struct rlimit r; - char *b; - long hpage_size = gethugepagesize(); - void *stack_address, *mmap_address, *heap_address; + void *stack_end; test_init(argc, argv); @@ -94,37 +151,13 @@ int main(int argc, char *argv[]) if (fd < 0) CONFIG("Couldn't get hugepage fd"); - stack_address = alloca(0); - heap_address = sbrk(0); + stack_end = try_setup_stack_and_huge(fd, sbrk(0)); + if (!stack_end) + PASS_INCONCLUSIVE(); - /* - * paranoia: start mapping two hugepages below the start of the stack, - * in case the alignment would cause us to map over something if we - * only used a gap of one hugepage. - */ - mmap_address = PALIGN(stack_address - 2 * hpage_size, hpage_size); - - do { - b = mmap(mmap_address, hpage_size, PROT_READ|PROT_WRITE, - MAP_FIXED|MAP_SHARED, fd, 0); - mmap_address -= hpage_size; - /* - * if we get all the way down to the heap, stop trying - */ - if (mmap_address <= heap_address) - break; - } while (b == MAP_FAILED); - - if (b == MAP_FAILED) - FAIL("mmap: %s", strerror(errno)); - - if ((pid = fork()) < 0) - FAIL("fork: %s", strerror(errno)); - - if (pid == 0) { - do_child(mmap_address); - exit(0); - } + pid = clone(do_child, stack_end, SIGCHLD, 0); + if (pid < 0) + FAIL("clone: %s", strerror(errno)); ret = waitpid(pid, &s, 0); if (ret == -1) -- 1.8.3.1