16#define CHECK(expression) \
18 if (!(expression)) { \
19 fprintf(stderr, "LARGE-MAPPING-CONTRACT: line=%d %s errno=%d\n", __LINE__, #expression, \
26static size_t page_size;
27static const size_t gib = (size_t)1 << 30;
29static int64_t milliseconds(
void) {
31 if (clock_gettime(CLOCK_MONOTONIC, &now))
33 return (int64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000;
36static int reap(pid_t child,
int timeout) {
37 int64_t deadline = milliseconds() + timeout;
38 while (milliseconds() < deadline) {
40 pid_t result = waitpid(child, &status, WNOHANG);
42 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
43 if (result < 0 && errno != EINTR)
45 struct timespec delay = {0, 5000000};
46 nanosleep(&delay, NULL);
49 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
54static int release_and_reuse(
unsigned char* base,
size_t length) {
55 if (munmap(base, length))
57 if (mmap(base, length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0) !=
60 return munmap(base, length);
63static int whole_unmap(
void) {
65 unsigned char* base = mmap(NULL, gib, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
66 CHECK(base != MAP_FAILED);
67 CHECK(release_and_reuse(base, gib) == 0);
70 if (base != MAP_FAILED)
75static int sparse_reservation(
size_t length) {
77 unsigned char* base = mmap(NULL, length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
78 CHECK(base != MAP_FAILED);
79 unsigned char* middle = base + length / 2;
80 CHECK(mmap(middle, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,
82 CHECK(middle[0] == 0);
84 CHECK(mprotect(middle - 2 * page_size, page_size, PROT_READ | PROT_WRITE) == 0);
85 CHECK(mprotect(middle + 2 * page_size, page_size, PROT_READ | PROT_WRITE) == 0);
86 CHECK(middle[-2 * (ptrdiff_t)page_size] == 0 && middle[2 * page_size] == 0);
87 middle[-2 * (ptrdiff_t)page_size] = 0x52;
88 middle[2 * page_size] = 0x53;
89#if defined(__x86_64__)
90 unsigned char* code = middle + page_size;
91 const unsigned char return_42[] = {0xb8, 42, 0, 0, 0, 0xc3};
92 CHECK(mprotect(code, page_size, PROT_READ | PROT_WRITE) == 0);
93 memcpy(code, return_42,
sizeof(return_42));
94 __builtin___clear_cache((
char*)code, (
char*)code +
sizeof(return_42));
95 CHECK(mprotect(code, page_size, PROT_READ | PROT_EXEC) == 0);
96 CHECK(((
int (*)(
void))code)() == 42);
97 CHECK(mprotect(code, page_size, PROT_NONE) == 0);
99 unsigned char* hole = base + length / 4;
100 CHECK(munmap(hole, page_size) == 0);
101 CHECK(mmap(hole, page_size, PROT_READ | PROT_WRITE,
102 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0) == hole);
106 CHECK(mmap(middle, page_size, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1,
109 CHECK(middle[0] == 0x51 && middle[-2 * (ptrdiff_t)page_size] == 0x52 &&
110 middle[2 * page_size] == 0x53 && hole[0] == 0x54);
111 CHECK(release_and_reuse(base, length) == 0);
114 if (base != MAP_FAILED)
115 munmap(base, length);
119static int reservation(
void) {
120 return sparse_reservation(gib);
123static int sparse(
void) {
124 return sparse_reservation(64 * gib);
127static int anonymous(
void) {
130 unsigned char* base = mmap(NULL, gib, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
131 CHECK(base != MAP_FAILED);
132 unsigned char* middle = base + gib / 2;
134 base[gib - page_size] = 0x62;
135 CHECK(mmap(middle, page_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,
137 CHECK(base[0] == 0x61 && base[gib - page_size] == 0x62 && middle[0] == 0);
138 CHECK(base[2 * page_size] == 0);
140 CHECK(munmap(base + gib / 4, 2 * page_size) == 0);
141 CHECK(mmap(base + gib / 4, 2 * page_size, PROT_READ | PROT_WRITE,
142 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0) == base + gib / 4);
143 CHECK(base[gib / 4] == 0 && base[0] == 0x61 && base[gib - page_size] == 0x62);
144 CHECK((child = fork()) >= 0);
147 if (base[0] != 0x61 || base[gib - page_size] != 0x62 || base[2 * page_size] != 0)
150 base[gib - page_size] = 0x72;
151 base[2 * page_size] = 0x73;
152 _exit(release_and_reuse(base, gib) ? 3 : 0);
154 int child_status = reap(child, 20000);
156 CHECK(child_status == 0);
157 CHECK(base[0] == 0x61 && base[gib - page_size] == 0x62 && base[2 * page_size] == 0 &&
159 CHECK(release_and_reuse(base, gib) == 0);
163 kill(child, SIGKILL);
166 if (base != MAP_FAILED)
171static int backing_file(
int memory,
int* readonly) {
173 snprintf(path,
sizeof(path),
"/tmp/large-mapping-contract-%ld", (
long)getpid());
174 int fd = memory ? memfd_create(
"large-mapping-contract", MFD_CLOEXEC)
175 : open(path, O_CREAT | O_EXCL | O_RDWR, 0600);
178 unsigned char* bytes = malloc(page_size);
179 int error = bytes == NULL;
182 for (
size_t n = 0; !error && n < 4; ++n) {
183 memset(bytes, 0x40 + n, page_size);
184 error = write(fd, bytes, page_size) != (ssize_t)page_size;
187 if (!error && readonly) {
188 *readonly = open(path, O_RDONLY);
189 error = *readonly < 0;
191 if (!memory && unlink(path))
194 if (readonly && *readonly >= 0) {
204static int file_mapping(
int shared) {
205 int failed = 0, fd = -1;
206 unsigned char *base = MAP_FAILED, *alias = MAP_FAILED;
207 CHECK((fd = backing_file(shared, NULL)) >= 0);
208 CHECK((base = mmap(NULL, gib, PROT_READ | PROT_WRITE, shared ? MAP_SHARED : MAP_PRIVATE, fd,
209 page_size)) != MAP_FAILED);
210 CHECK((alias = mmap(NULL, 3 * page_size, PROT_READ, MAP_SHARED, fd, page_size)) != MAP_FAILED);
211 CHECK(base[0] == 0x41 && base[page_size] == 0x42 && base[2 * page_size] == 0x43);
213 base[page_size] = 0x82;
214 CHECK(alias[0] == (shared ? 0x81 : 0x41) && alias[page_size] == (shared ? 0x82 : 0x42));
215 CHECK(close(fd) == 0);
217 CHECK(mmap(base + page_size, page_size, PROT_READ | PROT_WRITE,
218 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0) == base + page_size);
219 CHECK(base[0] == 0x81 && base[page_size] == 0 && base[2 * page_size] == 0x43);
220 base[page_size] = 0x83;
221 CHECK(munmap(base + 4 * page_size, page_size) == 0);
222 CHECK(mmap(base + 4 * page_size, page_size, PROT_NONE,
223 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0) == base + 4 * page_size);
224 CHECK(base[0] == 0x81 && base[2 * page_size] == 0x43 &&
225 alias[page_size] == (shared ? 0x82 : 0x42));
226 CHECK(release_and_reuse(base, gib) == 0);
228 CHECK(alias[0] == (shared ? 0x81 : 0x41) && alias[page_size] == (shared ? 0x82 : 0x42) &&
229 alias[2 * page_size] == 0x43);
230 CHECK(munmap(alias, 3 * page_size) == 0);
233 if (base != MAP_FAILED)
235 if (alias != MAP_FAILED)
236 munmap(alias, 3 * page_size);
242static int file(
void) {
243 return file_mapping(0);
246static int shared(
void) {
247 return file_mapping(1);
250static int failure(
void) {
251 int failed = 0, fd = -1, readonly = -1;
252 unsigned char* base = MAP_FAILED;
253 CHECK((fd = backing_file(0, &readonly)) >= 0);
254 CHECK((base = mmap(NULL, gib, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) !=
256 unsigned char* middle = base + gib / 2;
259 base[gib - page_size] = 0x93;
261 CHECK(mmap(middle, page_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, -1, 0) == MAP_FAILED &&
263 CHECK(base[0] == 0x91 && middle[0] == 0x92 && base[gib - page_size] == 0x93);
265 CHECK(mmap(middle, page_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, readonly, 0) ==
268 CHECK(base[0] == 0x91 && middle[0] == 0x92 && base[gib - page_size] == 0x93);
269 CHECK(release_and_reuse(base, gib) == 0);
272 if (base != MAP_FAILED)
281static int run(
const char* name,
int (*test)(
void)) {
282 printf(
"LARGE-MAPPING-CONTRACT: BEGIN %s\n", name);
284 pid_t child = fork();
291 _exit(result ? 1 : 0);
294 result = reap(child, 45000);
295 printf(
"LARGE-MAPPING-CONTRACT: %s %s status=%d\n", result ?
"FAIL" :
"PASS", name, result);
300int main(
int argc,
char** argv) {
301 page_size = (size_t)sysconf(_SC_PAGESIZE);
302 if (
sizeof(
size_t) < 8 || page_size < 512 || page_size > 65536 || (page_size & (page_size - 1)))
304 static const struct {
307 } suites[] = {{
"whole-unmap", whole_unmap},
308 {
"reservation", reservation},
310 {
"anonymous", anonymous},
313 {
"failure", failure}};
315 for (
size_t n = 0; n <
sizeof(suites) /
sizeof(suites[0]); ++n) {
316 if (argc > 1 && strcmp(argv[1], suites[n].name))
319 if (run(suites[n].name, suites[n].test))
324 puts(
"LARGE-MAPPING-CONTRACT: END PASS");