• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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