8#include <sys/resource.h>
16static void* change_shared_limit(
void* argument) {
20 CHECK(ml_receive(context->command,
'G') == 0);
21 CHECK(getrlimit(RLIMIT_MEMLOCK, &limit) == 0 && limit.rlim_cur == 2 * ml_page);
22 CHECK(mlock(context->page, ml_page) == 0);
23 CHECK(ml_limit(ml_page) == 0 && ml_send(context->report,
'C') == 0);
24 CHECK(ml_receive(context->command,
'R') == 0);
25 CHECK(munlock(context->page, ml_page) == 0);
28 ml_send(context->report,
'E');
29 return (
void*)(uintptr_t)failed;
32static int shared_threads(
void) {
34 int command[2] = {-1, -1};
35 int report[2] = {-1, -1};
38 unsigned char* pages = MAP_FAILED;
42 CHECK(ml_limit(2 * ml_page) == 0 && ml_unprivileged() == 0);
43 CHECK(pipe(command) == 0 && pipe(report) == 0);
44 pages = mmap(NULL, 3 * ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
45 CHECK(pages != MAP_FAILED);
46 context = (
struct shared_wait){command[0], report[1], pages + ml_page};
47 CHECK(pthread_create(&thread, NULL, change_shared_limit, &context) == 0);
49 CHECK(mlock(pages, ml_page) == 0 && ml_send(command[1],
'G') == 0);
50 CHECK(ml_receive(report[0],
'C') == 0);
51 CHECK(getrlimit(RLIMIT_MEMLOCK, &limit) == 0 && limit.rlim_cur == ml_page);
53 CHECK(madvise(pages, 2 * ml_page, MADV_DONTNEED) == -1 && errno == EINVAL);
54 CHECK(munlock(pages, ml_page) == 0);
56 CHECK(mlock(pages + 2 * ml_page, ml_page) == -1 && errno == ENOMEM);
57 CHECK(ml_send(command[1],
'R') == 0);
58 CHECK(pthread_join(thread, &result) == 0);
60 CHECK(result == NULL && mlock(pages + 2 * ml_page, ml_page) == 0);
63 ml_send(command[1],
'R');
64 pthread_join(thread, NULL);
67 for (
int n = 0; n < 2; ++n) {
73 if (pages != MAP_FAILED)
74 munmap(pages, 3 * ml_page);
78static void* leave_locked_stack(
void* argument) {
80 volatile unsigned char storage[2 * ml_page];
81 void* aligned = (
void*)(((uintptr_t)storage + ml_page - 1) & ~(uintptr_t)(ml_page - 1));
83 return (
void*)(uintptr_t)(mlock(aligned, ml_page) != 0);
86static int thread_retirement(
void) {
91 void* page = MAP_FAILED;
92 CHECK(ml_limit(ml_page) == 0);
93 page = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
94 CHECK(page != MAP_FAILED && pthread_create(&thread, NULL, leave_locked_stack, NULL) == 0);
96 CHECK(pthread_join(thread, &result) == 0);
98 CHECK(result == NULL && mlock(page, ml_page) == 0);
101 pthread_join(thread, NULL);
103 if (page != MAP_FAILED)
104 munmap(page, ml_page);
108static int fork_child(
unsigned char* inherited) {
111 void* fresh = MAP_FAILED;
112 CHECK(getrlimit(RLIMIT_MEMLOCK, &limit) == 0 && limit.rlim_cur == 2 * ml_page);
113 CHECK(madvise(inherited, 2 * ml_page, MADV_DONTNEED) == 0 && inherited[0] == 0);
114 fresh = mmap(NULL, 3 * ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
115 CHECK(fresh != MAP_FAILED && mlock(fresh, 2 * ml_page) == 0);
117 CHECK(mlock((
unsigned char*)fresh + 2 * ml_page, ml_page) == -1 && errno == ENOMEM);
121 if (fresh != MAP_FAILED)
122 munmap(fresh, 3 * ml_page);
126static int fork_policy(
void) {
129 unsigned char* pages = MAP_FAILED;
130 void* extra = MAP_FAILED;
131 CHECK(ml_limit(2 * ml_page) == 0);
132 pages = mmap(NULL, 2 * ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
133 extra = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
134 CHECK(pages != MAP_FAILED && extra != MAP_FAILED);
136 CHECK(mlock(pages, 2 * ml_page) == 0 && mlockall(MCL_FUTURE | MCL_ONFAULT) == 0);
141 _exit(fork_child(pages));
143 int status = ml_reap(child, 10000);
145 CHECK(status == 0 && pages[0] == 0x31);
147 CHECK(madvise(pages, 2 * ml_page, MADV_DONTNEED) == -1 && errno == EINVAL);
149 CHECK(mlock(extra, ml_page) == -1 && errno == ENOMEM);
152 kill(child, SIGKILL);
153 ml_reap(child, 2000);
156 if (extra != MAP_FAILED)
157 munmap(extra, ml_page);
158 if (pages != MAP_FAILED)
159 munmap(pages, 2 * ml_page);
163int ml_exec(
int argc,
char** argv) {
167 void* pages = MAP_FAILED;
169 CHECK(argc == 2 && geteuid() != 0);
170 CHECK(getrlimit(RLIMIT_MEMLOCK, &limit) == 0 && limit.rlim_cur == 2 * ml_page &&
171 limit.rlim_max == 16 * 1024 * 1024);
172 pages = mmap(NULL, 3 * ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
173 CHECK(pages != MAP_FAILED && mlock(pages, 2 * ml_page) == 0);
175 CHECK(mlock((
unsigned char*)pages + 2 * ml_page, ml_page) == -1 && errno == ENOMEM);
178 if (pages != MAP_FAILED)
179 munmap(pages, 3 * ml_page);
180 printf(
"MEMORY-LOCK-CONTRACT: EXEC %s\n", failed ?
"FAIL" :
"PASS");
184static int exec_policy(
void) {
185 pid_t child = fork();
191 mmap(NULL, 2 * ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
192 if (pages == MAP_FAILED || ml_limit(2 * ml_page) || mlock(pages, 2 * ml_page) ||
193 mlockall(MCL_FUTURE | MCL_ONFAULT))
195 execl(
"/applications/memory-lock-contract-test",
"memory-lock-contract-test",
196 "memory-lock-exec", NULL);
199 int result = ml_reap(child, 10000);
201 fprintf(stderr,
"MEMORY-LOCK-CONTRACT: exec child status=%d\n", result);
205int ml_lifetime(
void) {
206 return shared_threads() || thread_retirement() || fork_policy() || exec_policy();