The Pedigree Project 0.1
managed.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <unistd.h>
4
5#include "contract.h"
6#include <sys/mman.h>
7
8static int future_mappings(void) {
9 int failed = 0;
10 unsigned char* existing = MAP_FAILED;
11 unsigned char* victim = MAP_FAILED;
12 void* future = MAP_FAILED;
13 void* populated = MAP_FAILED;
14 void* locked = MAP_FAILED;
15 existing = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
16 victim = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
17 CHECK(existing != MAP_FAILED && victim != MAP_FAILED);
18 victim[0] = 0x6d;
19 CHECK(ml_limit(2 * ml_page) == 0);
20 CHECK(mlock(existing, ml_page) == 0);
21 CHECK(mlockall(MCL_FUTURE | MCL_ONFAULT) == 0);
22 CHECK(ml_unprivileged() == 0);
23 future = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
24 CHECK(future != MAP_FAILED && ml_resident(future, 1, 0, "future onfault mapping") == 0);
25 errno = 0;
26 CHECK(madvise(existing, ml_page, MADV_DONTNEED) == -1 && errno == EINVAL);
27 errno = 0;
28 CHECK(mmap(NULL, ml_page, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) == MAP_FAILED &&
29 errno == EAGAIN);
30 errno = 0;
31 CHECK(mmap(victim, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1,
32 0) == MAP_FAILED &&
33 errno == EAGAIN);
34 CHECK(victim[0] == 0x6d);
35 CHECK(munmap(future, ml_page) == 0);
36 future = MAP_FAILED;
37 future = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
38 CHECK(future != MAP_FAILED);
39 CHECK(munlockall() == 0 && madvise(existing, ml_page, MADV_DONTNEED) == 0);
40 CHECK(madvise(future, ml_page, MADV_DONTNEED) == 0);
41
42 locked =
43 mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0);
44 CHECK(locked != MAP_FAILED && ml_resident(locked, 1, 1, "MAP_LOCKED eager mapping") == 0);
45 errno = 0;
46 CHECK(mmap(NULL, 2 * ml_page, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0) ==
47 MAP_FAILED &&
48 errno == EAGAIN);
49 populated = mmap(NULL, 2 * ml_page, PROT_READ | PROT_WRITE,
50 MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
51 CHECK(populated != MAP_FAILED && ml_resident(populated, 2, 3, "MAP_POPULATE") == 0);
52 CHECK(mlock(populated, ml_page) == 0);
53 errno = 0;
54 CHECK(mlock((unsigned char*)populated + ml_page, ml_page) == -1 && errno == ENOMEM);
55out:
56 munlockall();
57 if (locked != MAP_FAILED)
58 munmap(locked, ml_page);
59 if (populated != MAP_FAILED)
60 munmap(populated, 2 * ml_page);
61 if (future != MAP_FAILED)
62 munmap(future, ml_page);
63 if (victim != MAP_FAILED)
64 munmap(victim, ml_page);
65 if (existing != MAP_FAILED)
66 munmap(existing, ml_page);
67 return failed;
68}
69
70static int current_mappings(void) {
71 int failed = 0;
72 void* lazy = MAP_FAILED;
73 void* later = MAP_FAILED;
74 CHECK(ml_limit(16 * 1024 * 1024) == 0);
75 lazy = mmap(NULL, 3 * ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
76 CHECK(lazy != MAP_FAILED && ml_resident(lazy, 3, 0, "before CURRENT") == 0);
77 CHECK(mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) == 0);
78 CHECK(ml_resident(lazy, 3, 0, "CURRENT ONFAULT") == 0);
79 errno = 0;
80 CHECK(madvise(lazy, 3 * ml_page, MADV_DONTNEED) == -1 && errno == EINVAL);
81 CHECK(mlockall(MCL_CURRENT) == 0);
82 CHECK(ml_resident(lazy, 3, 7, "CURRENT eager population") == 0);
83 CHECK(ml_limit(0) == 0);
84 later = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
85 CHECK(later != MAP_FAILED && madvise(later, ml_page, MADV_DONTNEED) == 0);
86 errno = 0;
87 CHECK(madvise(lazy, ml_page, MADV_DONTNEED) == -1 && errno == EINVAL);
88 CHECK(munlockall() == 0 && madvise(lazy, 3 * ml_page, MADV_DONTNEED) == 0);
89out:
90 munlockall();
91 if (later != MAP_FAILED)
92 munmap(later, ml_page);
93 if (lazy != MAP_FAILED)
94 munmap(lazy, 3 * ml_page);
95 return failed;
96}
97
98static int remap_charge(void) {
99 int failed = 0;
100 unsigned char* source = MAP_FAILED;
101 unsigned char* target = MAP_FAILED;
102 void* extra = MAP_FAILED;
103 size_t source_length = 2 * ml_page;
104 int moved = 0;
105 CHECK(ml_limit(2 * ml_page) == 0);
106 source = mmap(NULL, source_length, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
107 target = mmap(NULL, 3 * ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
108 extra = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
109 CHECK(source != MAP_FAILED && target != MAP_FAILED && extra != MAP_FAILED);
110 source[0] = 0x39;
111 source[ml_page] = 0x62;
112 target[0] = target[2 * ml_page] = 0x74;
113 CHECK(mlock(source, source_length) == 0);
114 errno = 0;
115 CHECK(mremap(source, source_length, 3 * ml_page, MREMAP_MAYMOVE | MREMAP_FIXED, target) ==
116 MAP_FAILED &&
117 errno == EAGAIN);
118 CHECK(source[0] == 0x39 && source[ml_page] == 0x62 && target[0] == 0x74);
119 CHECK(mremap(source, source_length, source_length, MREMAP_MAYMOVE | MREMAP_FIXED, target) ==
120 target);
121 source = target;
122 moved = 1;
123 CHECK(source[0] == 0x39 && source[ml_page] == 0x62 && target[2 * ml_page] == 0x74);
124 CHECK(ml_resident(source, 2, 3, "locked remap retains residency") == 0);
125 errno = 0;
126 CHECK(mlock(extra, ml_page) == -1 && errno == ENOMEM);
127 CHECK(mremap(source, source_length, ml_page, 0) == source);
128 source_length = ml_page;
129 CHECK(mlock(extra, ml_page) == 0);
130 errno = 0;
131 CHECK(madvise(source, ml_page, MADV_DONTNEED) == -1 && errno == EINVAL);
132out:
133 munlockall();
134 if (extra != MAP_FAILED)
135 munmap(extra, ml_page);
136 if (source != MAP_FAILED && !moved)
137 munmap(source, source_length);
138 if (target != MAP_FAILED)
139 munmap(target, 3 * ml_page);
140 return failed;
141}
142
143static int file_charge(void) {
144 int failed = 0;
145 int fd = -1;
146 unsigned char* shared = MAP_FAILED;
147 unsigned char* private = MAP_FAILED;
148 void* alias = MAP_FAILED;
149 CHECK(ml_limit(4 * ml_page) == 0);
150 fd = memfd_create("locked-file", MFD_CLOEXEC);
151 CHECK(fd >= 0 && ftruncate(fd, 3 * ml_page) == 0);
152 for (size_t n = 0; n < 3; ++n) {
153 unsigned char value = 0x41 + n;
154 CHECK(pwrite(fd, &value, 1, n * ml_page) == 1);
155 }
156 shared = mmap(NULL, 3 * ml_page, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
157 private = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
158 alias = mmap(NULL, ml_page, PROT_READ, MAP_SHARED, fd, ml_page);
159 CHECK(shared != MAP_FAILED && private != MAP_FAILED && alias != MAP_FAILED);
160 CHECK(mlock(shared, 3 * ml_page) == 0 && mlock(private, ml_page) == 0);
161 private[0] = 0x77;
162 CHECK(shared[0] == 0x41);
163 errno = 0;
164 CHECK(mlock(alias, ml_page) == -1 && errno == ENOMEM);
165 errno = 0;
166 CHECK(msync(shared, ml_page, MS_SYNC | MS_INVALIDATE) == -1 && errno == EBUSY);
167 CHECK(ftruncate(fd, ml_page + 137) == 0);
168 CHECK(ml_resident(shared, 2, 3, "locked file retained prefix") == 0);
169 CHECK(shared[0] == 0x41 && shared[ml_page] == 0x42 && private[0] == 0x77);
170 errno = 0;
171 CHECK(mlock(alias, ml_page) == -1 && errno == ENOMEM);
172 CHECK(ftruncate(fd, 3 * ml_page) == 0);
173 CHECK(shared[ml_page + 137] == 0 && shared[2 * ml_page] == 0);
174 CHECK(munlock(shared, ml_page) == 0 && mlock(alias, ml_page) == 0);
175 errno = 0;
176 CHECK(madvise(shared, 3 * ml_page, MADV_DONTNEED) == -1 && errno == EINVAL);
177 CHECK(shared[0] == 0x41 && private[0] == 0x77);
178out:
179 munlockall();
180 if (alias != MAP_FAILED)
181 munmap(alias, ml_page);
182 if (private != MAP_FAILED)
183 munmap(private, ml_page);
184 if (shared != MAP_FAILED)
185 munmap(shared, 3 * ml_page);
186 if (fd >= 0)
187 close(fd);
188 return failed;
189}
190
191int ml_managed(void) {
192 return future_mappings() || current_mappings() || remap_charge() || file_charge();
193}