The Pedigree Project 0.1
residency.c
1#define _GNU_SOURCE
2#include <signal.h>
3#include <string.h>
4#include <unistd.h>
5
6#include "contract.h"
7#include <sys/mman.h>
8#include <sys/stat.h>
9
10static int anonymous_residency(void) {
11 int failed = 0;
12 const size_t p = vm_page;
13 unsigned char* area =
14 mmap(NULL, 4 * p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
15 unsigned char* output =
16 mmap(NULL, 2 * p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
17 unsigned char vector[6];
18 CHECK(area != MAP_FAILED && output != MAP_FAILED);
19 memset(vector, 0xa5, sizeof(vector));
20 CHECK(mincore(area, 4 * p - 1, vector + 1) == 0 && vector[0] == 0xa5 && vector[5] == 0xa5);
21 CHECK(vm_uniform(vector + 1, 4, 0));
22 CHECK(mincore(area, 4 * p, vector + 1) == 0 && vm_uniform(vector + 1, 4, 0));
23 area[p] = 0xb1;
24 CHECK(mprotect(area + p, 2 * p, PROT_NONE) == 0);
25 CHECK(mincore(area, 4 * p, vector) == 0 && vector[0] == 0 && vector[1] == 1 && vector[2] == 0 &&
26 vector[3] == 0);
27 CHECK(area[0] == 0);
28 CHECK(mincore(area, 4 * p, vector) == 0 && vector[0] == 1 && vector[1] == 1 && vector[2] == 0 &&
29 vector[3] == 0);
30 CHECK(mprotect(area + p, p, PROT_READ | PROT_WRITE) == 0 && area[p] == 0xb1);
31 CHECK(mincore(area + 1, p, vector) == -1 && errno == EINVAL);
32 CHECK(mprotect(output + p, p, PROT_NONE) == 0);
33 CHECK(mincore(area, p, output + p) == -1 && errno == EFAULT);
34 output[p - 1] = 0xa5;
35 CHECK(mincore(area, 2 * p, output + p - 1) == -1 && errno == EFAULT && output[p - 1] == 0xa5);
36 CHECK(mincore(area, 0, output + p) == 0);
37 CHECK(mincore(area, 4 * p, vector) == 0 && vector[2] == 0 && vector[3] == 0);
38 CHECK(munmap(area + 3 * p, p) == 0);
39 CHECK(mincore(area, 4 * p, vector) == -1 && errno == ENOMEM);
40 CHECK(mincore(area + 3 * p, p, vector) == -1 && errno == ENOMEM);
41out:
42 if (area != MAP_FAILED)
43 munmap(area, 4 * p);
44 if (output != MAP_FAILED)
45 munmap(output, 2 * p);
46 return failed;
47}
48
49static int file_residency(void) {
50 int failed = 0, fd = -1;
51 const size_t p = vm_page;
52 pid_t child = -1;
53 unsigned char *area = MAP_FAILED, *alias = MAP_FAILED;
54 unsigned char vector[3], byte;
55 struct stat metadata;
56 CHECK((fd = vm_file(1, NULL)) >= 0);
57 CHECK(fchmod(fd, 0600) == 0);
58 CHECK(fstat(fd, &metadata) == 0 && (metadata.st_mode & 0777) == 0600);
59 CHECK((area = mmap(NULL, 3 * p, PROT_NONE, MAP_PRIVATE, fd, 0)) != MAP_FAILED);
60 CHECK((alias = mmap(NULL, p, PROT_READ, MAP_SHARED, fd, 0)) != MAP_FAILED);
61 CHECK(pread(fd, &byte, 1, 0) == 1 && byte == 0x20);
62 CHECK(mincore(area, 3 * p, vector) == 0 && vector[0] == 1 && vector[1] == 0 && vector[2] == 0);
63 CHECK(alias[0] == 0x20);
64 CHECK(madvise(alias, p, MADV_DONTNEED) == 0);
65 CHECK(mincore(alias, p, vector) == 0 && vector[0] == 1);
66 CHECK(mincore(area, 3 * p, vector) == 0 && vector[0] == 1 && vector[1] == 0 && vector[2] == 0);
67 if (geteuid() == 0) {
68 CHECK((child = fork()) >= 0);
69 if (!child) {
70 alarm(3);
71 if (setgid(65534) || setuid(65534))
72 _exit(10);
73 errno = EDOM;
74 const int before = errno;
75 const int result = mincore(area, 3 * p, vector);
76 const int after = errno;
77 if (result || after != EDOM || !vm_uniform(vector, 3, 1)) {
78 fprintf(stderr,
79 "VM-EXPANSION-CONTRACT: residency masked mincore=%d errno=%d->%d "
80 "uid=%lu euid=%lu gid=%lu egid=%lu vector=%u,%u,%u\n",
81 result, before, after, (unsigned long)getuid(), (unsigned long)geteuid(),
82 (unsigned long)getgid(), (unsigned long)getegid(), (unsigned)vector[0],
83 (unsigned)vector[1], (unsigned)vector[2]);
84 _exit(11);
85 }
86 _exit(0);
87 }
88 int status = vm_reap(child, 4000);
89 child = -1;
90 CHECK(status == 0);
91 CHECK(mincore(area, 3 * p, vector) == 0 && vector[1] == 0 && vector[2] == 0);
92 } else {
93 puts("VM-EXPANSION-CONTRACT: SKIP residency credential masking requires root");
94 }
95out:
96 if (child > 0) {
97 kill(child, SIGKILL);
98 vm_reap(child, 1000);
99 }
100 if (fd >= 0)
101 close(fd);
102 if (area != MAP_FAILED)
103 munmap(area, 3 * p);
104 if (alias != MAP_FAILED)
105 munmap(alias, p);
106 return failed;
107}
108
109int vm_test_residency(void) {
110 return anonymous_residency() || file_residency();
111}