The Pedigree Project 0.1
memfd-contract-test/mappings.c
1#define _GNU_SOURCE
2#include <string.h>
3#include <unistd.h>
4
5#include "contract.h"
6#include <sys/mman.h>
7
8static int write_capability(int protection) {
9 int failed = 0, fd = -1;
10 const size_t page = sysconf(_SC_PAGESIZE);
11 char* shared = MAP_FAILED;
12 char* private = MAP_FAILED;
13 unsigned char resident[2];
14 CHECK((fd = mf_make(2 * page)) >= 0);
15 CHECK(pwrite(fd, "a", 1, 0) == 1);
16 shared = mmap(NULL, 2 * page, protection, MAP_SHARED, fd, 0);
17 CHECK(shared != MAP_FAILED);
18 /* No access is made through shared: residency cannot stand in for write capability. */
19 CHECK(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == -1 && errno == EBUSY);
20 CHECK(fcntl(fd, F_GET_SEALS) == 0);
21 CHECK(!mprotect(shared, 2 * page, PROT_NONE));
22 CHECK(!madvise(shared, 2 * page, MADV_DONTNEED));
23 CHECK(!mincore(shared, 2 * page, resident));
24 CHECK(!ftruncate(fd, 3 * page));
25 CHECK(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1 && errno == EBUSY);
26 CHECK(!munmap(shared, 2 * page));
27 shared = MAP_FAILED;
28 private = mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
29 CHECK(private != MAP_FAILED && private[0] == 'a');
30 private[0] = 'p';
31 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE));
32 CHECK(private[0] == 'p' && !mf_contents(fd, 0, "a", 1));
33 shared = mmap(NULL, 2 * page, PROT_READ, MAP_SHARED, fd, 0);
34 CHECK(shared != MAP_FAILED && shared[0] == 'a');
35 CHECK(mprotect(shared, 2 * page, PROT_READ | PROT_WRITE) == -1 && errno == EACCES);
36 CHECK(mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) == MAP_FAILED &&
37 errno == EPERM);
38out:
39 if (private != MAP_FAILED)
40 munmap(private, page);
41 if (shared != MAP_FAILED)
42 munmap(shared, 2 * page);
43 if (fd >= 0)
44 close(fd);
45 return failed;
46}
47static int future_mappings(void) {
48 int failed = 0, fd = -1, split = 0, prefix_moved = 0;
49 const size_t page = sysconf(_SC_PAGESIZE);
50 char *old = MAP_FAILED, *old_read = MAP_FAILED, *new_read = MAP_FAILED;
51 char *private = MAP_FAILED, *victim = MAP_FAILED, *moved = MAP_FAILED;
52 unsigned char residency;
53 CHECK((fd = mf_make(3 * page)) >= 0);
54 CHECK(pwrite(fd, "a", 1, 0) == 1 && pwrite(fd, "c", 1, 2 * page) == 1);
55 old = mmap(NULL, 3 * page, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
56 old_read = mmap(NULL, page, PROT_READ, MAP_SHARED, fd, 0);
57 CHECK(old != MAP_FAILED && old_read != MAP_FAILED);
58 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE));
59 old[0] = 'b';
60 CHECK(!mf_contents(fd, 0, "b", 1));
61 CHECK(!mprotect(old_read, page, PROT_READ | PROT_WRITE));
62 old_read[0] = 'd';
63 CHECK(old[0] == 'd' && !mf_contents(fd, 0, "d", 1));
64 CHECK(pwrite(fd, "x", 1, 0) == -1 && errno == EPERM);
65 CHECK(mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0) == MAP_FAILED &&
66 errno == EPERM);
67 new_read = mmap(NULL, page, PROT_READ, MAP_SHARED, fd, 0);
68 CHECK(new_read != MAP_FAILED && new_read[0] == 'd');
69 CHECK(mprotect(new_read, page, PROT_READ | PROT_WRITE) == -1 && errno == EACCES);
70 private = mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
71 CHECK(private != MAP_FAILED);
72 private[0] = 'p';
73 CHECK(new_read[0] == 'd' && !mf_contents(fd, 0, "d", 1));
74 victim = mmap(NULL, page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
75 CHECK(victim != MAP_FAILED);
76 memset(victim, 0x5a, page);
77 CHECK(mmap(victim, page, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0) == MAP_FAILED &&
78 errno == EPERM);
79 CHECK(!mincore(victim, page, &residency) && (residency & 1));
80 for (size_t n = 0; n < page; ++n)
81 CHECK(victim[n] == 0x5a);
82 CHECK(!mprotect(old, 3 * page, PROT_READ));
83 CHECK(!munmap(old + page, page));
84 split = 1;
85 CHECK(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW) == -1 && errno == EBUSY);
86 CHECK(fcntl(fd, F_GET_SEALS) == F_SEAL_FUTURE_WRITE);
87 moved = mremap(old, page, page, MREMAP_MAYMOVE | MREMAP_FIXED, victim);
88 CHECK(moved == victim);
89 prefix_moved = 1;
90 victim = MAP_FAILED;
91 CHECK(!madvise(moved, page, MADV_DONTNEED) && moved[0] == 'd');
92 CHECK(!mprotect(moved, page, PROT_READ | PROT_WRITE));
93 moved[0] = 'e';
94 CHECK(new_read[0] == 'e' && !mf_contents(fd, 0, "e", 1) && private[0] == 'p');
95 CHECK(!munmap(old_read, page));
96 old_read = MAP_FAILED;
97 CHECK(!munmap(moved, page));
98 moved = MAP_FAILED;
99 CHECK(fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE) == -1 && errno == EBUSY);
100 /* Replacing the last old fragment retires its capability just as unmap does. */
101 CHECK(mmap(old + 2 * page, page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
102 -1, 0) == old + 2 * page);
103 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_WRITE));
104 CHECK(fcntl(fd, F_GET_SEALS) == (F_SEAL_WRITE | F_SEAL_FUTURE_WRITE));
105 CHECK(new_read[0] == 'e' && private[0] == 'p');
106out:
107 if (moved != MAP_FAILED)
108 munmap(moved, page);
109 if (victim != MAP_FAILED)
110 munmap(victim, page);
111 if (private != MAP_FAILED)
112 munmap(private, page);
113 if (new_read != MAP_FAILED)
114 munmap(new_read, page);
115 if (old_read != MAP_FAILED)
116 munmap(old_read, page);
117 if (old != MAP_FAILED) {
118 if (split) {
119 if (!prefix_moved)
120 munmap(old, page);
121 munmap(old + 2 * page, page);
122 } else
123 munmap(old, 3 * page);
124 }
125 if (fd >= 0)
126 close(fd);
127 return failed;
128}
129static int resize_nonmutation(void) {
130 int failed = 0, fd = -1;
131 const size_t page = sysconf(_SC_PAGESIZE);
132 char* mapping = MAP_FAILED;
133 CHECK((fd = mf_make(2 * page)) >= 0);
134 mapping = mmap(NULL, 2 * page, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
135 CHECK(mapping != MAP_FAILED);
136 mapping[0] = 'a';
137 mapping[page] = 'b';
138 CHECK(!fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK));
139 CHECK(ftruncate(fd, page) == -1 && errno == EPERM);
140 CHECK(ftruncate(fd, 3 * page) == -1 && errno == EPERM);
141 CHECK(!mf_size(fd, 2 * page));
142 CHECK(mapping[0] == 'a' && mapping[page] == 'b');
143 CHECK(!madvise(mapping, 2 * page, MADV_DONTNEED));
144 CHECK(mapping[0] == 'a' && mapping[page] == 'b');
145 mapping[page] = 'c';
146 CHECK(!mf_contents(fd, page, "c", 1));
147out:
148 if (mapping != MAP_FAILED)
149 munmap(mapping, 2 * page);
150 if (fd >= 0)
151 close(fd);
152 return failed;
153}
154int memfd_mappings(void) {
155 return write_capability(PROT_READ) || write_capability(PROT_NONE) ||
156 write_capability(PROT_READ | PROT_WRITE) || future_mappings() || resize_nonmutation();
157}