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