The Pedigree Project 0.1
remap-shm.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/ipc.h>
8#include <sys/mman.h>
9#include <sys/shm.h>
10
11static int attachment_move(void) {
12 int failed = 0, id = -1;
13 const size_t p = vm_page;
14 pid_t child = -1;
15 unsigned char *view = (void*)-1, *alias = (void*)-1, *target = MAP_FAILED;
16 struct shmid_ds status;
17 CHECK((id = shmget(IPC_PRIVATE, 3 * p, 0600)) >= 0);
18 CHECK((view = shmat(id, NULL, 0)) != (void*)-1 &&
19 (alias = shmat(id, NULL, SHM_RDONLY)) != (void*)-1);
20 for (int n = 0; n < 3; ++n)
21 memset(view + n * p, 0xa1 + n, p);
22 CHECK(shmctl(id, IPC_RMID, NULL) == 0);
23 CHECK((target = mmap(NULL, 3 * p, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED);
24 void* old = view;
25 CHECK(mremap(view, 3 * p, 3 * p, MREMAP_MAYMOVE | MREMAP_FIXED, target) == target);
26 view = target;
27 target = MAP_FAILED;
28 CHECK(shmdt(old) == -1 && errno == EINVAL);
29 CHECK(shmctl(id, IPC_STAT, &status) == 0 && status.shm_nattch == 2 && status.shm_segsz == 3 * p);
30 CHECK(vm_uniform(view, p, 0xa1) && vm_uniform(alias + 2 * p, p, 0xa3));
31 view[0] = 0xa4;
32 CHECK(alias[0] == 0xa4 && (child = fork()) >= 0);
33 if (!child) {
34 alarm(5);
35 if (shmctl(id, IPC_STAT, &status) || status.shm_nattch != 4 || view[0] != 0xa4 || shmdt(view) ||
36 shmctl(id, IPC_STAT, &status) || status.shm_nattch != 3)
37 _exit(10);
38 _exit(0);
39 }
40 int result = vm_reap(child, 3000);
41 child = -1;
42 CHECK(result == 0 && shmctl(id, IPC_STAT, &status) == 0 && status.shm_nattch == 2);
43 CHECK(mremap(view, 3 * p, 2 * p, 0) == view);
44 CHECK(view[0] == 0xa4 && view[p] == 0xa2 && alias[2 * p] == 0xa3);
45 CHECK(shmctl(id, IPC_STAT, &status) == 0 && status.shm_nattch == 2 && status.shm_segsz == 3 * p);
46 CHECK(shmdt(view) == 0);
47 view = (void*)-1;
48 CHECK(shmctl(id, IPC_STAT, &status) == 0 && status.shm_nattch == 1);
49 CHECK(shmdt(alias) == 0);
50 alias = (void*)-1;
51 CHECK(shmctl(id, IPC_STAT, &status) == -1 && errno == EINVAL);
52 id = -1;
53out:
54 if (child > 0) {
55 kill(child, SIGKILL);
56 vm_reap(child, 1000);
57 }
58 if (target != MAP_FAILED)
59 munmap(target, 3 * p);
60 if (view != (void*)-1)
61 shmdt(view);
62 if (alias != (void*)-1)
63 shmdt(alias);
64 if (id >= 0)
65 shmctl(id, IPC_RMID, NULL);
66 return failed;
67}
68
69static int rejected_attachment_forms(void) {
70 int failed = 0, id = -1;
71 const size_t p = vm_page;
72 unsigned char* view = (void*)-1;
73 struct shmid_ds status;
74 CHECK((id = shmget(IPC_PRIVATE, 3 * p, 0600)) >= 0 && (view = shmat(id, NULL, 0)) != (void*)-1);
75 memset(view, 0xb1, 3 * p);
76 CHECK(mremap(view + p, p, p, MREMAP_MAYMOVE) == MAP_FAILED && errno == EOPNOTSUPP);
77 CHECK(mremap(view, 3 * p, 4 * p, MREMAP_MAYMOVE) == MAP_FAILED && errno == EOPNOTSUPP);
78 CHECK(shmctl(id, IPC_STAT, &status) == 0 && status.shm_nattch == 1 &&
79 vm_uniform(view, 3 * p, 0xb1));
80 CHECK(munmap(view + p, p) == 0);
81 CHECK(mremap(view, p, p, MREMAP_MAYMOVE) == MAP_FAILED && errno == EOPNOTSUPP);
82 CHECK(shmctl(id, IPC_STAT, &status) == 0 && status.shm_nattch == 1 && vm_uniform(view, p, 0xb1) &&
83 vm_uniform(view + 2 * p, p, 0xb1));
84 CHECK(shmdt(view) == 0);
85 view = (void*)-1;
86 CHECK(shmctl(id, IPC_STAT, &status) == 0 && status.shm_nattch == 0);
87out:
88 if (view != (void*)-1)
89 shmdt(view);
90 if (id >= 0)
91 shmctl(id, IPC_RMID, NULL);
92 return failed;
93}
94
95static int attachment_victims(void) {
96 int failed = 0, id = -1, attached = 0;
97 const size_t p = vm_page;
98 unsigned char *view = (void*)-1, *alias = (void*)-1, *source = MAP_FAILED;
99 struct shmid_ds status;
100 CHECK((id = shmget(IPC_PRIVATE, 3 * p, 0600)) >= 0);
101 CHECK((view = shmat(id, NULL, 0)) != (void*)-1);
102 attached = 1;
103 CHECK((alias = shmat(id, NULL, 0)) != (void*)-1);
104 memset(view, 0xc1, 3 * p);
105 const int order[3] = {1, 0, 2};
106 for (int n = 0; n < 3; ++n) {
107 CHECK((source = mmap(NULL, p, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) !=
108 MAP_FAILED);
109 memset(source, 0xd1 + n, p);
110 unsigned char* victim = view + order[n] * p;
111 CHECK(mremap(source, p, p, MREMAP_MAYMOVE | MREMAP_FIXED, victim) == victim);
112 source = MAP_FAILED;
113 if (n == 2)
114 attached = 0;
115 CHECK(shmctl(id, IPC_STAT, &status) == 0 && status.shm_nattch == (n == 2 ? 1U : 2U));
116 CHECK(vm_uniform(victim, p, 0xd1 + n) && vm_uniform(alias, 3 * p, 0xc1));
117 }
118 CHECK(shmdt(view) == -1 && errno == EINVAL);
119 CHECK(vm_uniform(view, p, 0xd2) && vm_uniform(view + p, p, 0xd1) &&
120 vm_uniform(view + 2 * p, p, 0xd3));
121out:
122 if (source != MAP_FAILED)
123 munmap(source, p);
124 if (attached)
125 shmdt(view);
126 if (view != (void*)-1)
127 munmap(view, 3 * p);
128 if (alias != (void*)-1)
129 shmdt(alias);
130 if (id >= 0)
131 shmctl(id, IPC_RMID, NULL);
132 return failed;
133}
134
135int vm_test_remap_shm(void) {
136 return attachment_move() || rejected_attachment_forms() || attachment_victims();
137}