The Pedigree Project 0.1
process-memory-contract-test/mappings.c
1#define _GNU_SOURCE
2#include <fcntl.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#include <sys/syscall.h>
10
12 int fd;
13 unsigned char* cow;
14};
16 uintptr_t anonymous, shared, private, cow, stack, heap;
17};
18static unsigned char pattern(size_t n) {
19 return (unsigned char)(n % 251 + 1);
20}
21
22static int mapping_target(int command, int report, void* argument) {
23 struct map_arguments* args = argument;
24 int failed = 0;
25 unsigned char *anonymous = MAP_FAILED, *shared = MAP_FAILED, *private = MAP_FAILED;
26 volatile unsigned char stack[32] = {0x27};
27 anonymous = mmap(NULL, 3 * pm_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
28 shared = mmap(NULL, 3 * pm_page, PROT_READ | PROT_WRITE, MAP_SHARED, args->fd, 0);
29 private = mmap(NULL, 3 * pm_page, PROT_READ | PROT_WRITE, MAP_PRIVATE, args->fd, 0);
30 CHECK(anonymous != MAP_FAILED && shared != MAP_FAILED && private != MAP_FAILED);
31 uintptr_t original = (uintptr_t)syscall(SYS_brk, 0);
32 CHECK(original && original != UINTPTR_MAX && original < UINTPTR_MAX - 2 * pm_page);
33 uintptr_t heap = (original + pm_page - 1) & ~(uintptr_t)(pm_page - 1);
34 CHECK((uintptr_t)syscall(SYS_brk, heap + pm_page) == heap + pm_page);
35 *(volatile unsigned char*)heap = 0x38;
36 struct map_addresses addresses = {(uintptr_t)anonymous, (uintptr_t)shared, (uintptr_t)private,
37 (uintptr_t)args->cow, (uintptr_t)stack, heap};
38 CHECK(!pm_dumpable(1) && !pm_write(report, &addresses, sizeof(addresses)));
39 CHECK(!pm_receive(command, 'w'));
40 CHECK(anonymous[0] == 0x31 && stack[0] == 0x42 && *(volatile unsigned char*)heap == 0x53);
41 CHECK(args->cow[0] == 0x64 && private[7] == 0x75 && shared[11] == 0x86);
42 CHECK(private[11] == pattern(11) && shared[7] == pattern(7));
43 CHECK(!msync(shared, 3 * pm_page, MS_SYNC) && !fsync(args->fd));
44 CHECK(!pm_send(report, 's'));
45 CHECK(!pm_receive(command, 'p'));
46 CHECK(!mprotect(anonymous + pm_page, pm_page, PROT_NONE));
47 CHECK(!mprotect(private, 3 * pm_page, PROT_READ));
48 CHECK(!pm_send(report, 'p'));
49 CHECK(!pm_receive(command, 'e'));
50 CHECK(!ftruncate(args->fd, pm_page + 137));
51 CHECK(!pm_send(report, 'e') && !pm_receive(command, 'q'));
52out:
53 if (private != MAP_FAILED)
54 munmap(private, 3 * pm_page);
55 if (shared != MAP_FAILED)
56 munmap(shared, 3 * pm_page);
57 if (anonymous != MAP_FAILED)
58 munmap(anonymous, 3 * pm_page);
59 /* The bounded raw heap belongs to this child and retires with its image. */
60 return failed;
61}
62static int backend(int disk) {
63 int failed = 0, fd = -1;
64 char path[128] = {0};
65 struct pm_peer peer = PM_PEER_INITIALIZER;
66 struct map_addresses addresses;
67 unsigned char *buffer = MAP_FAILED, *cow = MAP_FAILED;
68 buffer = mmap(NULL, 3 * pm_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
69 cow = mmap(NULL, pm_page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
70 CHECK(buffer != MAP_FAILED && cow != MAP_FAILED);
71 memset(cow, 0x19, pm_page);
72 if (disk) {
73 snprintf(path, sizeof(path), "/process-memory-%d", getpid());
74 fd = open(path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600);
75 } else
76 fd = memfd_create("process-memory", MFD_CLOEXEC);
77 CHECK(fd >= 0);
78 for (size_t n = 0; n < 3 * pm_page; ++n)
79 buffer[n] = pattern(n);
80 CHECK(!pm_write(fd, buffer, 3 * pm_page));
81 struct map_arguments arguments = {fd, cow};
82 CHECK(!pm_spawn(&peer, mapping_target, &arguments));
83 CHECK(!pm_read(peer.report, &addresses, sizeof(addresses)));
84 memset(buffer, 0xff, 3 * pm_page);
85 CHECK(pm_copy(peer.pid, buffer, (void*)addresses.anonymous, 3 * pm_page, 0) ==
86 (ssize_t)(3 * pm_page));
87 for (size_t n = 0; n < 3 * pm_page; ++n)
88 CHECK(buffer[n] == 0);
89 CHECK(pm_copy(peer.pid, buffer, (void*)addresses.private, pm_page, 0) == (ssize_t)pm_page);
90 for (size_t n = 0; n < pm_page; ++n)
91 CHECK(buffer[n] == pattern(n));
92 unsigned char byte = 0;
93 CHECK(pm_copy(peer.pid, &byte, (void*)addresses.stack, 1, 0) == 1 && byte == 0x27);
94 CHECK(pm_copy(peer.pid, &byte, (void*)addresses.heap, 1, 0) == 1 && byte == 0x38);
95 CHECK(pm_copy(peer.pid, &byte, (void*)addresses.cow, 1, 0) == 1 && byte == 0x19);
96 byte = 0x31;
97 CHECK(pm_copy(peer.pid, &byte, (void*)addresses.anonymous, 1, 1) == 1);
98 byte = 0x42;
99 CHECK(pm_copy(peer.pid, &byte, (void*)addresses.stack, 1, 1) == 1);
100 byte = 0x53;
101 CHECK(pm_copy(peer.pid, &byte, (void*)addresses.heap, 1, 1) == 1);
102 byte = 0x64;
103 CHECK(pm_copy(peer.pid, &byte, (void*)addresses.cow, 1, 1) == 1);
104 byte = 0x75;
105 CHECK(pm_copy(peer.pid, &byte, (void*)(addresses.private + 7), 1, 1) == 1);
106 byte = 0x86;
107 CHECK(pm_copy(peer.pid, &byte, (void*)(addresses.shared + 11), 1, 1) == 1);
108 CHECK(cow[0] == 0x19 && cow[pm_page - 1] == 0x19);
109 CHECK(!pm_send(peer.command, 'w') && !pm_receive(peer.report, 's'));
110 CHECK(pread(fd, &byte, 1, 7) == 1 && byte == pattern(7));
111 CHECK(pread(fd, &byte, 1, 11) == 1 && byte == 0x86);
112 CHECK(!pm_send(peer.command, 'p') && !pm_receive(peer.report, 'p'));
113 errno = 0;
114 CHECK(pm_copy(peer.pid, &byte, (void*)addresses.private, 1, 1) == -1 && errno == EFAULT);
115 CHECK(pm_copy(peer.pid, &byte, (void*)(addresses.private + 7), 1, 0) == 1 && byte == 0x75);
116 memset(buffer, 0x91, 3 * pm_page);
117 CHECK(pm_copy(peer.pid, buffer, (void*)addresses.anonymous, 3 * pm_page, 0) == (ssize_t)pm_page);
118 CHECK(buffer[0] == 0x31 && buffer[pm_page] == 0x91 && buffer[2 * pm_page] == 0x91);
119 memset(buffer, 0xa2, 3 * pm_page);
120 CHECK(pm_copy(peer.pid, buffer, (void*)addresses.anonymous, 3 * pm_page, 1) == (ssize_t)pm_page);
121 CHECK(pm_copy(peer.pid, &byte, (void*)(addresses.anonymous + 2 * pm_page), 1, 0) == 1 &&
122 byte == 0);
123 CHECK(!pm_send(peer.command, 'e') && !pm_receive(peer.report, 'e'));
124 memset(buffer, 0xb3, 3 * pm_page);
125 CHECK(pm_copy(peer.pid, buffer, (void*)addresses.shared, 3 * pm_page, 0) ==
126 (ssize_t)(2 * pm_page));
127 CHECK(buffer[11] == 0x86 && buffer[pm_page + 136] == pattern(pm_page + 136));
128 for (size_t n = pm_page + 137; n < 2 * pm_page; ++n)
129 CHECK(buffer[n] == 0);
130 CHECK(buffer[2 * pm_page] == 0xb3);
131 byte = 0xc4;
132 CHECK(pm_copy(peer.pid, &byte, (void*)(addresses.shared + pm_page + 200), 1, 1) == 1);
133 CHECK(pm_copy(peer.pid, &byte, (void*)(addresses.shared + pm_page + 200), 1, 0) == 1 &&
134 byte == 0xc4);
135 errno = 0;
136 CHECK(pm_copy(peer.pid, &byte, (void*)(addresses.shared + 2 * pm_page), 1, 1) == -1 &&
137 errno == EFAULT);
138 struct stat st;
139 CHECK(!fstat(fd, &st) && st.st_size == (off_t)(pm_page + 137));
140 CHECK(!pm_send(peer.command, 'q') && !pm_join(&peer));
141out:
142 pm_cleanup(&peer);
143 if (fd >= 0)
144 close(fd);
145 if (path[0])
146 unlink(path);
147 if (cow != MAP_FAILED)
148 munmap(cow, pm_page);
149 if (buffer != MAP_FAILED)
150 munmap(buffer, 3 * pm_page);
151 return failed;
152}
153int pm_mappings(void) {
154 return backend(0) || backend(1);
155}