The Pedigree Project 0.1
vm-contract-test/main.c
1#define _GNU_SOURCE
2#include <fcntl.h>
3#include <signal.h>
4#include <stdlib.h>
5#include <string.h>
6#include <unistd.h>
7
8#include "contract.h"
9#include <sys/wait.h>
10
11size_t vm_page;
12
13int64_t vm_now(void) {
14 struct timespec now;
15 return clock_gettime(CLOCK_MONOTONIC, &now) ? -1 : (int64_t)now.tv_sec * 1000000000 + now.tv_nsec;
16}
17void vm_pause(int milliseconds) {
18 struct timespec pause = {milliseconds / 1000, (milliseconds % 1000) * 1000000};
19 while (nanosleep(&pause, &pause) && errno == EINTR) {
20 }
21}
22int vm_wait(volatile int* flag, int milliseconds) {
23 int64_t deadline = vm_now() + (int64_t)milliseconds * 1000000;
24 while (!__atomic_load_n(flag, __ATOMIC_ACQUIRE)) {
25 if (vm_now() >= deadline)
26 return -1;
27 vm_pause(2);
28 }
29 return 0;
30}
31int vm_reap(pid_t child, int milliseconds) {
32 int64_t deadline = vm_now() + (int64_t)milliseconds * 1000000;
33 while (vm_now() < deadline) {
34 int status;
35 pid_t result = waitpid(child, &status, WNOHANG);
36 if (result == child)
37 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
38 if (result < 0 && errno != EINTR)
39 return -1;
40 vm_pause(5);
41 }
42 kill(child, SIGKILL);
43 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
44 }
45 return -1;
46}
47static void expected_fault(int signal) {
48 (void)signal;
49 _exit(0);
50}
51int vm_fault(void* address, int writing, int expected_signal) {
52 pid_t child = fork();
53 if (child < 0)
54 return -1;
55 if (!child) {
56 alarm(3);
57 struct sigaction action = {.sa_handler = expected_fault};
58 sigemptyset(&action.sa_mask);
59 if (sigaction(expected_signal, &action, NULL))
60 _exit(2);
61 if (writing)
62 *(volatile unsigned char*)address = 0x7e;
63 else {
64 volatile unsigned char value = *(volatile unsigned char*)address;
65 (void)value;
66 }
67 _exit(1);
68 }
69 return vm_reap(child, 4000);
70}
71int vm_file(size_t pages, int* readonly) {
72 static unsigned sequence;
73 char name[96];
74 snprintf(name, sizeof(name), "/tmp/vm-contract-%ld-%u", (long)getpid(), sequence++);
75 int fd = open(name, O_CREAT | O_EXCL | O_RDWR, 0600);
76 if (fd < 0)
77 return -1;
78 unsigned char* bytes = malloc(vm_page);
79 int error = bytes == NULL;
80 if (readonly)
81 *readonly = -1;
82 for (size_t n = 0; !error && n < pages; ++n) {
83 memset(bytes, 0x20 + n, vm_page);
84 error = write(fd, bytes, vm_page) != (ssize_t)vm_page;
85 }
86 free(bytes);
87 if (!error && readonly) {
88 *readonly = open(name, O_RDONLY);
89 error = *readonly < 0;
90 }
91 if (unlink(name))
92 error = 1;
93 if (error) {
94 if (readonly && *readonly >= 0) {
95 close(*readonly);
96 *readonly = -1;
97 }
98 close(fd);
99 return -1;
100 }
101 return fd;
102}
103int vm_uniform(const unsigned char* bytes, size_t length, unsigned char value) {
104 for (size_t n = 0; n < length; ++n)
105 if (bytes[n] != value)
106 return 0;
107 return 1;
108}
109
110static int run(const char* name, int (*test)(void)) {
111 printf("VM-EXPANSION-CONTRACT: BEGIN %s\n", name);
112 fflush(stdout);
113 pid_t child = fork();
114 if (child < 0)
115 return -1;
116 if (!child) {
117 alarm(40);
118 int result = test();
119 fflush(stdout);
120 fflush(stderr);
121 _exit(result ? 1 : 0);
122 }
123 int result = vm_reap(child, 45000);
124 printf("VM-EXPANSION-CONTRACT: %s %s status=%d\n", result ? "FAIL" : "PASS", name, result);
125 fflush(stdout);
126 return result;
127}
128int main(int argc, char** argv) {
129 vm_page = (size_t)sysconf(_SC_PAGESIZE);
130 if (!vm_page || vm_page > 65536 || (vm_page & (vm_page - 1)))
131 return 2;
132 static const struct {
133 const char* name;
134 int (*test)(void);
135 } suites[] = {
136 {"remap", vm_test_remap}, {"residency", vm_test_residency}, {"discard", vm_test_discard}};
137 int selected = 0;
138 for (unsigned n = 0; n < sizeof(suites) / sizeof(suites[0]); ++n) {
139 if (argc > 1 && strcmp(argv[1], suites[n].name))
140 continue;
141 selected = 1;
142 if (run(suites[n].name, suites[n].test))
143 return 1;
144 }
145 if (!selected)
146 return 2;
147 puts("VM-EXPANSION-CONTRACT: END PASS");
148 return 0;
149}