1 /*
2  * Copyright (C) 2013-2021 Canonical, Ltd.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * This code is a complete clean re-write of the stress tool by
19  * Colin Ian King <colin.king@canonical.com> and attempts to be
20  * backwardly compatible with the stress tool by Amos Waterland
21  * <apw@rossby.metr.ou.edu> but has more stress tests and more
22  * functionality.
23  *
24  */
25 #include "stress-ng.h"
26 
27 static volatile bool page_fault = false;
28 
29 static const stress_help_t help[] = {
30 	{ NULL,	"mmapaddr N",	  "start N workers stressing mmap with random addresses" },
31 	{ NULL,	"mmapaddr-ops N", "stop after N mmapaddr bogo operations" },
32 	{ NULL,	NULL,		  NULL }
33 };
34 
stress_fault_handler(int signum)35 static void stress_fault_handler(int signum)
36 {
37 	(void)signum;
38 
39 	page_fault = true;
40 }
41 
42 /*
43  *  stress_mmapaddr_check()
44  *	perform some quick sanity checks to see if page is mapped OK
45  */
stress_mmapaddr_check(const stress_args_t * args,uint8_t * map_addr)46 static int stress_mmapaddr_check(const stress_args_t *args, uint8_t *map_addr)
47 {
48 	unsigned char vec[1];
49 	volatile uint8_t val;
50 	int ret;
51 
52 	page_fault = false;
53 	/* Should not fault! */
54 	val = *map_addr;
55 	(void)val;
56 
57 	if (page_fault) {
58 		pr_err("%s: read of mmap'd address %p SEGFAULTed\n",
59 			args->name, (void *)map_addr);
60 		return -1;
61 	}
62 
63 	vec[0] = 0;
64 	ret = shim_mincore(map_addr, args->page_size, vec);
65 	if (ret != 0) {
66 		pr_err("%s: mincore on address %p failed, errno=%d (%s)\n",
67 			args->name, (void *)map_addr, errno, strerror(errno));
68 		return -1;
69 	}
70 	if ((vec[0] & 1) == 0) {
71 		pr_inf("%s: mincore on address %p suggests page is not resident\n",
72 			args->name, (void *)map_addr);
73 		return -1;
74 	}
75 	return 0;
76 }
77 
78 /*
79  *  stress_mmapaddr_get_addr()
80  *	try to find an unmapp'd address
81  */
stress_mmapaddr_get_addr(const stress_args_t * args,const uintptr_t mask,const size_t page_size)82 static void *stress_mmapaddr_get_addr(
83 	const stress_args_t *args,
84 	const uintptr_t mask,
85 	const size_t page_size)
86 {
87 	unsigned char vec[1];
88 	void *addr = NULL;
89 
90 	while (keep_stressing(args)) {
91 		int ret;
92 
93 		vec[0] = 0;
94 		addr = (void *)(intptr_t)(stress_mwc64() & mask);
95 		ret = shim_mincore(addr, page_size, vec);
96 		if (ret == 0) {
97 			addr = NULL; /* it's mapped already */
98 		} else if (ret <= 0) {
99 			if (errno == ENOSYS) {
100 				addr = NULL;
101 				break;
102 			}
103 			if (errno == ENOMEM) {
104 				break;
105 			}
106 		} else {
107 			addr = NULL;
108 		}
109 	}
110 	return addr;
111 }
112 
stress_mmapaddr_child(const stress_args_t * args,void * context)113 static int stress_mmapaddr_child(const stress_args_t *args, void *context)
114 {
115 	const size_t page_size = args->page_size;
116 	const uintptr_t page_mask = ~(page_size - 1);
117 	const uintptr_t page_mask32 = page_mask & 0xffffffff;
118 
119 	(void)context;
120 
121 	stress_set_proc_state(args->name, STRESS_STATE_RUN);
122 
123 	do {
124 		uint8_t *addr, *map_addr, *remap_addr;
125 		int flags;
126 		uint8_t rnd = stress_mwc8();
127 #if defined(MAP_POPULATE)
128 		const int mmap_flags = MAP_POPULATE | MAP_PRIVATE | MAP_ANONYMOUS;
129 #else
130 		const int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
131 #endif
132 		/* Randomly chosen low or high address mask */
133 		const uintptr_t mask = (rnd & 0x80) ? page_mask : page_mask32;
134 
135 		addr = stress_mmapaddr_get_addr(args, mask, page_size);
136 		if (!addr) {
137 			if (errno == ENOSYS)
138 				break;
139 			continue;
140 		}
141 
142 		/* We get here if page is not already mapped */
143 #if defined(MAP_FIXED)
144 		flags = mmap_flags | ((rnd & 0x40) ? MAP_FIXED : 0);
145 #endif
146 #if defined(MAP_LOCKED)
147 		flags |= ((rnd & 0x20) ? MAP_LOCKED : 0);
148 #endif
149 		map_addr = (uint8_t *)mmap((void *)addr, page_size, PROT_READ, flags, -1, 0);
150 		if (!map_addr || (map_addr == MAP_FAILED))
151 			continue;
152 
153 		if (stress_mmapaddr_check(args, map_addr) < 0)
154 			goto unmap;
155 
156 		/* Now attempt to mmap the newly map'd page */
157 #if defined(MAP_32BIT)
158 		flags = mmap_flags;
159 		addr = map_addr;
160 		if (rnd & 0x10) {
161 			addr = NULL;
162 			flags |= MAP_32BIT;
163 		}
164 #endif
165 		remap_addr = (uint8_t *)mmap((void *)addr, page_size, PROT_READ, flags, -1, 0);
166 		if (!remap_addr || (remap_addr == MAP_FAILED))
167 			goto unmap;
168 
169 		(void)stress_mmapaddr_check(args, remap_addr);
170 		(void)munmap((void *)remap_addr, page_size);
171 
172 #if defined(HAVE_MREMAP) &&	\
173     NEED_GLIBC(2,4,0) &&	\
174     defined(MREMAP_FIXED) &&	\
175     defined(MREMAP_MAYMOVE)
176 		addr = stress_mmapaddr_get_addr(args, mask, page_size);
177 		if (!addr)
178 			goto unmap;
179 
180 		/* Now try to remap with a new fixed address */
181 		remap_addr = mremap(map_addr, page_size, page_size, MREMAP_FIXED | MREMAP_MAYMOVE, addr);
182 		if (remap_addr && (remap_addr != MAP_FAILED))
183 			map_addr = remap_addr;
184 #endif
185 
186 #if defined(MAP_FIXED_NOREPLACE)
187 		{
188 			uint8_t *noreplace_addr;
189 
190 			/*
191 			 *  mmap on to an existing address, force -EEXIST
192 			 */
193 			noreplace_addr = (uint8_t *)mmap((void *)addr, page_size, PROT_NONE,
194 							MAP_FIXED_NOREPLACE | flags,
195 							-1, 0);
196 			/*
197 			 * Should never succeed, but unmap just in case
198 			 * it got mapped
199 			 */
200 			if (noreplace_addr != MAP_FAILED)
201 				(void)munmap(noreplace_addr, page_size);
202 		}
203 #endif
204 unmap:
205 		(void)munmap((void *)map_addr, page_size);
206 		inc_counter(args);
207 	} while (keep_stressing(args));
208 
209 	stress_set_proc_state(args->name, STRESS_STATE_DEINIT);
210 
211 	return EXIT_SUCCESS;
212 }
213 
214 /*
215  *  stress_mmapaddr()
216  *	stress mmap with randomly chosen addresses
217  */
stress_mmapaddr(const stress_args_t * args)218 static int stress_mmapaddr(const stress_args_t *args)
219 {
220 	if (stress_sighandler(args->name, SIGSEGV, stress_fault_handler, NULL) < 0)
221 		return EXIT_FAILURE;
222 
223 	return stress_oomable_child(args, NULL, stress_mmapaddr_child, STRESS_OOMABLE_NORMAL);
224 }
225 
226 stressor_info_t stress_mmapaddr_info = {
227 	.stressor = stress_mmapaddr,
228 	.class = CLASS_VM | CLASS_OS,
229 	.help = help
230 };
231