The Pedigree Project 0.1
memfd-contract-test/races.c
1#define _GNU_SOURCE
2#include <pthread.h>
3#include <unistd.h>
4
5#include "contract.h"
6#include <sys/mman.h>
7
8struct race {
9 int fd;
10 size_t page;
11 volatile int gate;
12 volatile int ready[2];
13 volatile int done[2];
14 int mapping_race;
15 int seal_result, seal_error;
16 ssize_t write_result;
17 int operation_error;
18 void* mapping;
19};
20static void* operation(void* argument) {
21 struct race* race = argument;
22 __atomic_store_n(&race->ready[0], 1, __ATOMIC_RELEASE);
23 if (!mf_wait(&race->gate, 5000)) {
24 errno = 0;
25 if (race->mapping_race)
26 race->mapping = mmap(NULL, race->page, PROT_READ | PROT_WRITE, MAP_SHARED, race->fd, 0);
27 else
28 race->write_result = pwrite(race->fd, "b", 1, 0);
29 race->operation_error = errno;
30 } else
31 race->operation_error = ETIMEDOUT;
32 __atomic_store_n(&race->done[0], 1, __ATOMIC_RELEASE);
33 return NULL;
34}
35static void* sealer(void* argument) {
36 struct race* race = argument;
37 __atomic_store_n(&race->ready[1], 1, __ATOMIC_RELEASE);
38 if (!mf_wait(&race->gate, 5000)) {
39 errno = 0;
40 race->seal_result = fcntl(race->fd, F_ADD_SEALS, F_SEAL_WRITE);
41 race->seal_error = errno;
42 } else
43 race->seal_error = ETIMEDOUT;
44 __atomic_store_n(&race->done[1], 1, __ATOMIC_RELEASE);
45 return NULL;
46}
47static int run_race(int mapping_race, unsigned iteration) {
48 int failed = 0, started = 0;
49 pthread_t threads[2];
50 struct race race = {.fd = -1,
51 .page = sysconf(_SC_PAGESIZE),
52 .mapping_race = mapping_race,
53 .seal_result = -2,
54 .write_result = -2,
55 .mapping = MAP_FAILED};
56 CHECK((race.fd = mf_make(race.page)) >= 0 && pwrite(race.fd, "a", 1, 0) == 1);
57 CHECK(!pthread_create(&threads[0], NULL, operation, &race));
58 started = 1;
59 CHECK(!pthread_create(&threads[1], NULL, sealer, &race));
60 started = 2;
61 CHECK(!mf_wait(&race.ready[0], 5000) && !mf_wait(&race.ready[1], 5000));
62 __atomic_store_n(&race.gate, 1, __ATOMIC_RELEASE);
63 CHECK(!mf_wait(&race.done[0], 5000) && !mf_wait(&race.done[1], 5000));
64 if (mapping_race) {
65 if (race.mapping == MAP_FAILED) {
66 CHECK(race.operation_error == EPERM && race.seal_result == 0);
67 } else {
68 /* The mapping stays published until both results have been observed. */
69 CHECK(race.seal_result == -1 && race.seal_error == EBUSY);
70 CHECK(fcntl(race.fd, F_GET_SEALS) == 0);
71 CHECK(!munmap(race.mapping, race.page));
72 race.mapping = MAP_FAILED;
73 CHECK(!fcntl(race.fd, F_ADD_SEALS, F_SEAL_WRITE));
74 }
75 } else {
76 CHECK(race.seal_result == 0);
77 CHECK(race.write_result == 1 || (race.write_result == -1 && race.operation_error == EPERM));
78 CHECK(!mf_contents(race.fd, 0, race.write_result == 1 ? "b" : "a", 1));
79 }
80 CHECK(fcntl(race.fd, F_GET_SEALS) == F_SEAL_WRITE);
81 CHECK(pwrite(race.fd, "c", 1, 0) == -1 && errno == EPERM);
82 CHECK(mmap(NULL, race.page, PROT_READ | PROT_WRITE, MAP_SHARED, race.fd, 0) == MAP_FAILED &&
83 errno == EPERM);
84 CHECK(!mf_contents(race.fd, 0, !mapping_race && race.write_result == 1 ? "b" : "a", 1));
85out:
86 __atomic_store_n(&race.gate, 1, __ATOMIC_RELEASE);
87 for (int n = 0; n < started; ++n) {
88 if (mf_wait(&race.done[n], 5000)) {
89 fprintf(stderr, "MEMFD-CONTRACT: race worker=%d failed to finish\n", n);
90 _exit(60);
91 }
92 pthread_join(threads[n], NULL);
93 }
94 if (failed)
95 fprintf(stderr, "MEMFD-CONTRACT: race=%s iteration=%u seal=%d/%d mapping=%p write=%ld/%d\n",
96 mapping_race ? "mmap" : "write", iteration, race.seal_result, race.seal_error,
97 race.mapping, (long)race.write_result, race.operation_error);
98 if (race.mapping != MAP_FAILED)
99 munmap(race.mapping, race.page);
100 if (race.fd >= 0)
101 close(race.fd);
102 return failed;
103}
104int memfd_races(void) {
105 for (unsigned n = 0; n < 8; ++n)
106 if (run_race(1, n) || run_race(0, n))
107 return 1;
108 return 0;
109}