From 91cbe7e1f386718dad06f9d8b291f2795e49f34a Mon Sep 17 00:00:00 2001 From: Jan Stancek Date: Thu, 12 Dec 2024 10:04:32 +0100 Subject: [PATCH] hugemmap34: change how test finds suitable huge page and stack for test The test is trying to find a place for huge page with mmap(MAP_FIXED_NOREPLACE) starting at top of stack and going downwards until it succeeds. The problem is that before it finds one, it goes over other existing mappings. So, child process does crash when trying to extend stack, but not because it hits mapped huge page, it hits one of the existing mappings. This search is slow, specially on -debug kernels. This patch changes the test, by giving child process a separate stack that is always immediately above a huge page. Setup temporarily allocates huge pages and uses those addresses to map potential child stacks. When it allocates huge page and finds that mapping after it is one of child stacks, it uses that pair for the test. Signed-off-by: Jan Stancek Reviewed-by: Cyril Hrubis Reviewed-by: Wei Gao --- .../kernel/mem/hugetlb/hugemmap/hugemmap34.c | 148 ++++++++++++++---- 1 file changed, 115 insertions(+), 33 deletions(-) diff --git a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap34.c b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap34.c index a7a88fbb2d9..2283c7d2385 100644 --- a/testcases/kernel/mem/hugetlb/hugemmap/hugemmap34.c +++ b/testcases/kernel/mem/hugetlb/hugemmap/hugemmap34.c @@ -20,25 +20,32 @@ * huge page-only segment -- resulting in bugs. */ +#define _GNU_SOURCE #include "lapi/mmap.h" #include "hugetlb.h" #include +#include +#include #ifdef __LP64__ #define STACK_ALLOCATION_SIZE (256*1024*1024) #else #define STACK_ALLOCATION_SIZE (16*1024*1024) #endif -#define PALIGN(p, a) ((void *)LTP_ALIGN((unsigned long)(p), (a))) #define MNTPOINT "hugetlbfs/" -static int fd = -1; -static unsigned long long hpage_size; -static int page_size; +#define PATH_HUGEPAGE "/sys/kernel/mm/hugepages" + +#define STACKS_MAX 64 +static int fd = -1; +static uintptr_t hpage_size, stacks[STACKS_MAX]; +static int stacks_num; +static void *hpage_addr, *stack_addr; +static void **shared_area; -void do_child(void *stop_address) +int do_child(void *stop_address) { - volatile int *x; + volatile char *x; /* corefile from this process is not interesting and limiting * its size can save a lot of time. '1' is a special value, @@ -46,57 +53,77 @@ void do_child(void *stop_address) * sets limit to RLIM_INFINITY. */ tst_no_corefile(1); + tst_res(TINFO, "Child process starting with top of stack at %p", &x); do { x = alloca(STACK_ALLOCATION_SIZE); + *shared_area = (void *)x; *x = 1; } while ((void *)x >= stop_address); + exit(0); } static void run_test(void) { int pid, status; - void *stack_address, *mmap_address, *heap_address, *map; - stack_address = alloca(0); - heap_address = sbrk(0); - - /* - * 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 { - map = mmap(mmap_address, hpage_size, PROT_READ|PROT_WRITE, - MAP_SHARED | MAP_FIXED_NOREPLACE, fd, 0); - if (map == MAP_FAILED) { - if (errno == ENOMEM) { - tst_res(TCONF, "There is no enough memory in the system to do mmap"); - return; - } - } - mmap_address -= hpage_size; - /* - * if we get all the way down to the heap, stop trying - */ - } while (mmap_address <= heap_address); - pid = SAFE_FORK(); + pid = ltp_clone(CLONE_VM | CLONE_VFORK | SIGCHLD, do_child, + hpage_addr, hpage_size, stack_addr); if (pid == 0) - do_child(mmap_address); + do_child(hpage_addr); SAFE_WAITPID(pid, &status, 0); + tst_res(TINFO, "Child process extended stack up to %p, hasn't reached %p", + *shared_area, *shared_area - STACK_ALLOCATION_SIZE); if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) tst_res(TPASS, "Child killed by %s as expected", tst_strsig(SIGSEGV)); else tst_res(TFAIL, "Child: %s", tst_strstatus(status)); } +/* Return start address of next mapping or 0 */ +static uintptr_t get_next_mapping_start(uintptr_t addr) +{ + FILE *fp = fopen("/proc/self/maps", "r"); + + if (fp == NULL) + tst_brk(TBROK | TERRNO, "Failed to open /proc/self/maps."); + + while (!feof(fp)) { + uintptr_t start, end; + int ret; + + ret = fscanf(fp, "%"PRIxPTR"-%"PRIxPTR" %*[^\n]\n", &start, &end); + if (ret != 2) { + fclose(fp); + tst_brk(TBROK | TERRNO, "Couldn't parse /proc/self/maps line."); + } + + if (start > addr) { + fclose(fp); + return start; + } + } + fclose(fp); + return 0; +} + +static int is_addr_in_stacks(uintptr_t addr) +{ + int i; + + for (i = 0; i < stacks_num; i++) { + if (addr == stacks[i]) + return 1; + } + return 0; +} + void setup(void) { struct rlimit r; + int i; - page_size = getpagesize(); hpage_size = tst_get_hugepage_size(); /* * Setting the stack size to unlimited. @@ -107,7 +134,62 @@ void setup(void) SAFE_GETRLIMIT(RLIMIT_STACK, &r); if (r.rlim_cur != RLIM_INFINITY) tst_brk(TCONF, "Stack rlimit must be 'unlimited'"); + fd = tst_creat_unlinked(MNTPOINT, 0); + + /* shared memory to pass a (void *) from child process */ + shared_area = SAFE_MMAP(0, getpagesize(), PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_ANONYMOUS, -1, 0); + + /* + * We search for potential stack addresses by looking at + * places where kernel would map next huge page and occupying that + * address as potential stack. When huge page lands in such place + * that next mapping is one of our stack mappings, we use those + * two for the test. We try to map huge pages in child process so that + * slices in parent are not affected. + */ + tst_res(TINFO, "searching for huge page and child stack placement"); + for (i = 0; i < STACKS_MAX; i++) { + uintptr_t next_start; + int pid, status; + + pid = SAFE_FORK(); + if (pid == 0) { + *shared_area = SAFE_MMAP(0, hpage_size, PROT_READ|PROT_WRITE, + MAP_PRIVATE, fd, 0); + SAFE_MUNMAP(*shared_area, hpage_size); + exit(0); + } + SAFE_WAITPID(pid, &status, 0); + if (status != 0) + tst_brk(TFAIL, "Child: %s", tst_strstatus(status)); + + next_start = get_next_mapping_start((uintptr_t)*shared_area); + if (is_addr_in_stacks(next_start)) { + stack_addr = (void *)next_start; + hpage_addr = *shared_area; + break; + } + + tst_res(TINFO, "potential stack at address %p", *shared_area); + stacks[stacks_num++] = (uintptr_t) SAFE_MMAP(*shared_area, hpage_size, + PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE|MAP_GROWSDOWN, -1, 0); + } + + if (!stack_addr) + tst_brk(TCONF, "failed to find good place for huge page and stack"); + + hpage_addr = mmap(hpage_addr, hpage_size, PROT_READ|PROT_WRITE, + MAP_SHARED|MAP_FIXED, fd, 0); + if (hpage_addr == MAP_FAILED) { + if (errno == ENOMEM) + tst_brk(TCONF, "Not enough memory."); + tst_brk(TBROK|TERRNO, "mmap failed"); + } + tst_res(TINFO, "stack = %p-%p, hugepage = %p-%p", stack_addr, stack_addr+hpage_size, + hpage_addr, hpage_addr+hpage_size); } void cleanup(void)