1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2005-2006 IBM Corporation
4 * Author: David Gibson & Adam Litke
5 */
6
7 /*\
8 * [Description]
9 *
10 * On PowerPC, the address space is divided into segments. These segments can
11 * contain either huge pages or normal pages, but not both. All segments are
12 * initially set up to map normal pages. When a huge page mapping is created
13 * within a set of empty segments, they are "enabled" for huge pages at that
14 * time. Once enabled for huge pages, they can not be used again for normal
15 * pages for the remaining lifetime of the process.
16 *
17 * If the segment immediately preceeding the segment containing the stack is
18 * converted to huge pages and the stack is made to grow into the this
19 * preceeding segment, some kernels may attempt to map normal pages into the
20 * huge page-only segment -- resulting in bugs.
21 */
22
23 #define _GNU_SOURCE
24 #include "lapi/mmap.h"
25 #include "hugetlb.h"
26 #include <errno.h>
27 #include <inttypes.h>
28 #include <sched.h>
29
30 #ifdef __LP64__
31 #define STACK_ALLOCATION_SIZE (256*1024*1024)
32 #else
33 #define STACK_ALLOCATION_SIZE (16*1024*1024)
34 #endif
35 #define MNTPOINT "hugetlbfs/"
36 #define PATH_HUGEPAGE "/sys/kernel/mm/hugepages"
37
38 #define STACKS_MAX 64
39
40 static int fd = -1;
41 static uintptr_t hpage_size, stacks[STACKS_MAX];
42 static int stacks_num;
43 static void *hpage_addr, *stack_addr;
44 static void **shared_area;
45
do_child(void * stop_address)46 int do_child(void *stop_address)
47 {
48 volatile char *x;
49
50 /* corefile from this process is not interesting and limiting
51 * its size can save a lot of time. '1' is a special value,
52 * that will also abort dumping via pipe, which by default
53 * sets limit to RLIM_INFINITY.
54 */
55 tst_no_corefile(1);
56 tst_res(TINFO, "Child process starting with top of stack at %p", &x);
57
58 do {
59 x = alloca(STACK_ALLOCATION_SIZE);
60 *shared_area = (void *)x;
61 *x = 1;
62 } while ((void *)x >= stop_address);
63 exit(0);
64 }
65
run_test(void)66 static void run_test(void)
67 {
68 int pid, status;
69
70 pid = ltp_clone(CLONE_VM | CLONE_VFORK | SIGCHLD, do_child,
71 hpage_addr, hpage_size, stack_addr);
72 if (pid == 0)
73 do_child(hpage_addr);
74
75 SAFE_WAITPID(pid, &status, 0);
76 tst_res(TINFO, "Child process extended stack up to %p, hasn't reached %p",
77 *shared_area, *shared_area - STACK_ALLOCATION_SIZE);
78 if (WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV)
79 tst_res(TPASS, "Child killed by %s as expected", tst_strsig(SIGSEGV));
80 else
81 tst_res(TFAIL, "Child: %s", tst_strstatus(status));
82 }
83
84 /* Return start address of next mapping or 0 */
get_next_mapping_start(uintptr_t addr)85 static uintptr_t get_next_mapping_start(uintptr_t addr)
86 {
87 FILE *fp = fopen("/proc/self/maps", "r");
88
89 if (fp == NULL)
90 tst_brk(TBROK | TERRNO, "Failed to open /proc/self/maps.");
91
92 while (!feof(fp)) {
93 uintptr_t start, end;
94 int ret;
95
96 ret = fscanf(fp, "%"PRIxPTR"-%"PRIxPTR" %*[^\n]\n", &start, &end);
97 if (ret != 2) {
98 fclose(fp);
99 tst_brk(TBROK | TERRNO, "Couldn't parse /proc/self/maps line.");
100 }
101
102 if (start > addr) {
103 fclose(fp);
104 return start;
105 }
106 }
107 fclose(fp);
108 return 0;
109 }
110
is_addr_in_stacks(uintptr_t addr)111 static int is_addr_in_stacks(uintptr_t addr)
112 {
113 int i;
114
115 for (i = 0; i < stacks_num; i++) {
116 if (addr == stacks[i])
117 return 1;
118 }
119 return 0;
120 }
121
setup(void)122 void setup(void)
123 {
124 struct rlimit r;
125 int i;
126
127 hpage_size = tst_get_hugepage_size();
128 /*
129 * Setting the stack size to unlimited.
130 */
131 r.rlim_cur = RLIM_INFINITY;
132 r.rlim_max = RLIM_INFINITY;
133 SAFE_SETRLIMIT(RLIMIT_STACK, &r);
134 SAFE_GETRLIMIT(RLIMIT_STACK, &r);
135 if (r.rlim_cur != RLIM_INFINITY)
136 tst_brk(TCONF, "Stack rlimit must be 'unlimited'");
137
138 fd = tst_creat_unlinked(MNTPOINT, 0, 0600);
139
140 /* shared memory to pass a (void *) from child process */
141 shared_area = SAFE_MMAP(0, getpagesize(), PROT_READ|PROT_WRITE,
142 MAP_SHARED|MAP_ANONYMOUS, -1, 0);
143
144 /*
145 * We search for potential stack addresses by looking at
146 * places where kernel would map next huge page and occupying that
147 * address as potential stack. When huge page lands in such place
148 * that next mapping is one of our stack mappings, we use those
149 * two for the test. We try to map huge pages in child process so that
150 * slices in parent are not affected.
151 */
152 tst_res(TINFO, "searching for huge page and child stack placement");
153 for (i = 0; i < STACKS_MAX; i++) {
154 uintptr_t next_start;
155 int pid, status;
156
157 pid = SAFE_FORK();
158 if (pid == 0) {
159 *shared_area = SAFE_MMAP(0, hpage_size, PROT_READ|PROT_WRITE,
160 MAP_PRIVATE, fd, 0);
161 SAFE_MUNMAP(*shared_area, hpage_size);
162 exit(0);
163 }
164 SAFE_WAITPID(pid, &status, 0);
165 if (status != 0)
166 tst_brk(TFAIL, "Child: %s", tst_strstatus(status));
167
168 next_start = get_next_mapping_start((uintptr_t)*shared_area);
169 if (is_addr_in_stacks(next_start)) {
170 stack_addr = (void *)next_start;
171 hpage_addr = *shared_area;
172 break;
173 }
174
175 tst_res(TINFO, "potential stack at address %p", *shared_area);
176 stacks[stacks_num++] = (uintptr_t) SAFE_MMAP(*shared_area, hpage_size,
177 PROT_READ|PROT_WRITE,
178 MAP_ANONYMOUS|MAP_PRIVATE|MAP_GROWSDOWN, -1, 0);
179 }
180
181 if (!stack_addr)
182 tst_brk(TCONF, "failed to find good place for huge page and stack");
183
184 hpage_addr = mmap(hpage_addr, hpage_size, PROT_READ|PROT_WRITE,
185 MAP_SHARED|MAP_FIXED, fd, 0);
186 if (hpage_addr == MAP_FAILED) {
187 if (errno == ENOMEM)
188 tst_brk(TCONF, "Not enough memory.");
189 tst_brk(TBROK|TERRNO, "mmap failed");
190 }
191 tst_res(TINFO, "stack = %p-%p, hugepage = %p-%p", stack_addr, stack_addr+hpage_size,
192 hpage_addr, hpage_addr+hpage_size);
193 }
194
cleanup(void)195 void cleanup(void)
196 {
197 if (fd > 0)
198 SAFE_CLOSE(fd);
199 }
200
201 static struct tst_test test = {
202 .tags = (struct tst_tag[]) {
203 {"linux-git", "0d59a01bc461"},
204 {}
205 },
206 .needs_root = 1,
207 .mntpoint = MNTPOINT,
208 .needs_hugetlbfs = 1,
209 .needs_tmpdir = 1,
210 .setup = setup,
211 .cleanup = cleanup,
212 .test_all = run_test,
213 .hugepages = {1, TST_NEEDS},
214 .forks_child = 1,
215 .supported_archs = (const char *const []){"ppc", "ppc64", NULL},
216 };
217