The Pedigree Project 0.1
user/applications/memory-lock-contract-test/raw.c
1#define _GNU_SOURCE
2#include <pthread.h>
3#include <stdlib.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/mman.h>
8#include <sys/syscall.h>
9
10static _Thread_local volatile unsigned tls_value;
11
12static int allocator_current(void) {
13 int failed = 0;
14 unsigned char* allocation = malloc(192 * 1024);
15 CHECK(allocation != NULL);
16 for (size_t n = 0; n < 192 * 1024; ++n)
17 allocation[n] = (unsigned char)(n * 17 + 0x39);
18 unsigned char* grown = realloc(allocation, 384 * 1024);
19 CHECK(grown != NULL);
20 allocation = grown;
21 for (size_t n = 0; n < 192 * 1024; ++n)
22 CHECK(allocation[n] == (unsigned char)(n * 17 + 0x39));
23 CHECK(ml_limit(16 * 1024 * 1024) == 0 && ml_unprivileged() == 0);
24 CHECK(mlockall(MCL_CURRENT | MCL_ONFAULT) == 0);
25 CHECK(mlockall(MCL_CURRENT) == 0);
26 CHECK(allocation[0] == 0x39 && allocation[ml_page] == (unsigned char)(ml_page * 17 + 0x39));
27out:
28 munlockall();
29 free(allocation);
30 return failed;
31}
32
33static int initial_stack(void) {
34 int failed = 0;
35 volatile unsigned char storage[3 * ml_page];
36 volatile unsigned char* aligned =
37 (volatile unsigned char*)(((uintptr_t)storage + ml_page - 1) & ~(uintptr_t)(ml_page - 1));
38 void* managed = MAP_FAILED;
39 aligned[0] = 0x47;
40 aligned[ml_page] = 0x68;
41 managed = mmap(NULL, ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
42 CHECK(managed != MAP_FAILED && ml_limit(2 * ml_page) == 0);
43 CHECK(mlock((void*)aligned, 2 * ml_page) == 0);
44 errno = 0;
45 CHECK(mlock(managed, ml_page) == -1 && errno == ENOMEM);
46 CHECK(munlock((void*)aligned, ml_page) == 0 && mlock(managed, ml_page) == 0);
47 CHECK(aligned[0] == 0x47 && aligned[ml_page] == 0x68);
48out:
49 munlockall();
50 if (managed != MAP_FAILED)
51 munmap(managed, ml_page);
52 return failed;
53}
54
55static void* touch_tls(void* argument) {
56 tls_value = 0x69;
57 return (void*)(uintptr_t)(tls_value != 0x69 || argument != (void*)1);
58}
59
60static int future_thread(void) {
61 int failed = 0;
62 pthread_t thread;
63 pthread_attr_t attributes;
64 int attributes_live = 0;
65 int thread_live = 0;
66 void* result = NULL;
67 CHECK(pthread_attr_init(&attributes) == 0);
68 attributes_live = 1;
69 CHECK(pthread_attr_setstacksize(&attributes, 32 * ml_page) == 0);
70 CHECK(pthread_create(&thread, &attributes, touch_tls, (void*)1) == 0);
71 thread_live = 1;
72 CHECK(pthread_join(thread, &result) == 0);
73 thread_live = 0;
74 CHECK(result == NULL && ml_limit(ml_page) == 0);
75 CHECK(mlockall(MCL_FUTURE | MCL_ONFAULT) == 0);
76 int created = pthread_create(&thread, &attributes, touch_tls, (void*)1);
77 if (!created)
78 thread_live = 1;
79 CHECK(created == EAGAIN);
80 CHECK(munlockall() == 0);
81 CHECK(pthread_create(&thread, &attributes, touch_tls, (void*)1) == 0);
82 thread_live = 1;
83 CHECK(pthread_join(thread, &result) == 0);
84 thread_live = 0;
85 CHECK(result == NULL);
86out:
87 munlockall();
88 if (thread_live)
89 pthread_join(thread, NULL);
90 if (attributes_live)
91 pthread_attr_destroy(&attributes);
92 return failed;
93}
94
95static int future_heap(void) {
96 int failed = 0;
97 unsigned char* managed = MAP_FAILED;
98 size_t managed_length = 0;
99 CHECK(ml_limit(2 * ml_page) == 0);
100 uintptr_t original = (uintptr_t)syscall(SYS_brk, 0);
101 CHECK(original && original != UINTPTR_MAX && original < UINTPTR_MAX - 5 * ml_page);
102 uintptr_t start = (original + ml_page - 1) & ~(uintptr_t)(ml_page - 1);
103 CHECK((uintptr_t)syscall(SYS_brk, start) == start);
104 CHECK(mlockall(MCL_FUTURE | MCL_ONFAULT) == 0);
105 CHECK((uintptr_t)syscall(SYS_brk, start + 1) == start + 1);
106 CHECK((uintptr_t)syscall(SYS_brk, start + ml_page - 1) == start + ml_page - 1);
107 uintptr_t accepted = start + ml_page + 1;
108 CHECK((uintptr_t)syscall(SYS_brk, accepted) == accepted);
109 errno = 0;
110 CHECK((uintptr_t)syscall(SYS_brk, start + 2 * ml_page + 1) == accepted && errno == 0);
111 CHECK((uintptr_t)syscall(SYS_brk, 0) == accepted);
112 volatile unsigned char* bytes = (volatile unsigned char*)start;
113 CHECK(bytes[0] == 0 && bytes[ml_page] == 0);
114 bytes[0] = 0x53;
115 bytes[ml_page] = 0x76;
116 CHECK(munlock((void*)start, ml_page) == 0);
117 CHECK((uintptr_t)syscall(SYS_brk, start + 2 * ml_page + 1) == start + 2 * ml_page + 1);
118 CHECK(bytes[0] == 0x53 && bytes[ml_page] == 0x76 && bytes[2 * ml_page] == 0);
119 CHECK(munlockall() == 0);
120 CHECK((uintptr_t)syscall(SYS_brk, start + 4 * ml_page) == start + 4 * ml_page);
121 bytes[2 * ml_page] = 0x29;
122 bytes[3 * ml_page] = 0x64;
123 CHECK(mlock((void*)start, 2 * ml_page) == 0);
124 managed = mmap(NULL, 3 * ml_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
125 CHECK(managed != MAP_FAILED);
126 managed_length = 3 * ml_page;
127 errno = 0;
128 CHECK(mlock(managed, ml_page) == -1 && errno == ENOMEM);
129 CHECK(munmap(managed + 2 * ml_page, ml_page) == 0);
130 managed_length = 2 * ml_page;
131 CHECK(bytes[0] == 0x53 && bytes[ml_page] == 0x76 && bytes[2 * ml_page] == 0x29 &&
132 bytes[3 * ml_page] == 0x64);
133 errno = 0;
134 CHECK(mlock(managed, ml_page) == -1 && errno == ENOMEM);
135
136 // Retiring a raw page must release only its own lock charge.
137 CHECK(munmap((void*)(start + ml_page), ml_page) == 0);
138 unsigned char resident;
139 errno = 0;
140 CHECK(mincore((void*)(start + ml_page), ml_page, &resident) == -1 && errno == ENOMEM);
141 CHECK(bytes[0] == 0x53 && bytes[2 * ml_page] == 0x29 && bytes[3 * ml_page] == 0x64);
142 CHECK(mlock(managed, ml_page) == 0);
143 errno = 0;
144 CHECK(mlock((void*)(start + 2 * ml_page), ml_page) == -1 && errno == ENOMEM);
145 CHECK(munmap((void*)start, ml_page) == 0);
146 CHECK(mlock((void*)(start + 2 * ml_page), ml_page) == 0);
147 errno = 0;
148 CHECK(mlock(managed + ml_page, ml_page) == -1 && errno == ENOMEM);
149 CHECK(bytes[2 * ml_page] == 0x29 && bytes[3 * ml_page] == 0x64);
150 CHECK(munmap((void*)(start + 3 * ml_page), ml_page) == 0);
151 errno = 0;
152 CHECK(mincore((void*)(start + 3 * ml_page), ml_page, &resident) == -1 && errno == ENOMEM);
153 CHECK(bytes[2 * ml_page] == 0x29);
154 errno = 0;
155 CHECK(mlock(managed + ml_page, ml_page) == -1 && errno == ENOMEM);
156out:
157 // The public musl brk/sbrk wrappers do not grow this heap; the isolated child owns it.
158 munlockall();
159 if (managed != MAP_FAILED)
160 munmap(managed, managed_length);
161 return failed;
162}
163
164int ml_raw(void) {
165 return allocator_current() || initial_stack() || future_thread() || future_heap();
166}