14#define CHECK(condition) \
17 fprintf(stderr, "shared-memory:%d: %s (errno=%d)\n", __LINE__, #condition, errno); \
23static int reap(pid_t child) {
25 for (
int n = 0; n < 500; ++n) {
26 pid_t result = waitpid(child, &status, WNOHANG);
28 return WIFEXITED(status) ? WEXITSTATUS(status) : -1;
29 if (result < 0 && errno != EINTR)
34 waitpid(child, &status, 0);
38int ipc_test_shared_memory_exec(
int argc,
char** argv) {
41 int id = atoi(argv[2]);
42 unsigned long expected = strtoul(argv[3], NULL, 10);
43 struct shmid_ds status;
45 if (shmctl(
id, IPC_STAT, &status) || status.shm_nattch != expected)
47 char* view = shmat(
id, NULL, SHM_RDONLY);
48 if (view == (
void*)-1 || strcmp(view,
"child-shared"))
50 if (shmdt(view) || shmctl(
id, IPC_STAT, &status) || status.shm_nattch != expected)
55int ipc_test_shared_memory(
void) {
56 int failed = 0,
id = -1, separate = -1, split_id = -1;
57 char *view = (
void*)-1, *readonly = (
void*)-1, *remapped = (
void*)-1;
58 char *split = (
void*)-1, *replacement = MAP_FAILED, *middle = MAP_FAILED;
59 void *reserved = MAP_FAILED, *bad = MAP_FAILED;
60 const size_t page = (size_t)sysconf(_SC_PAGESIZE);
61 const size_t length = page * 3;
62 key_t key = (key_t)(0x62000000U | (
unsigned)getpid());
63 struct shmid_ds status, changed;
64 struct shminfo limits;
65 struct shm_info
usage;
68 CHECK(shmget(IPC_PRIVATE, 0, 0600) == -1 && errno == EINVAL);
69 id = shmget(key, length - 7, IPC_CREAT | IPC_EXCL | 0600);
71 CHECK(shmget(key, 0, 0600) ==
id);
72 CHECK(shmget(key, length, 0600) == -1 && errno == EINVAL);
73 CHECK(shmget(key, 1, IPC_CREAT | IPC_EXCL | 0600) == -1 && errno == EEXIST);
74 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && status.shm_segsz == length - 7 &&
75 status.shm_nattch == 0 && status.shm_cpid == getpid());
76 view = shmat(
id, NULL, 0);
77 readonly = shmat(
id, NULL, SHM_RDONLY);
78 CHECK(view != (
void*)-1 && readonly != (
void*)-1 && view != readonly);
79 CHECK(view[0] == 0 && view[length - 1] == 0);
80 strcpy(view,
"parent-shared");
82 CHECK(!strcmp(readonly,
"parent-shared") && readonly[page * 2] == 42);
83 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && status.shm_nattch == 2 && status.shm_atime != 0);
84 CHECK(mprotect(readonly, length, PROT_READ | PROT_WRITE) == -1 && errno == EACCES);
85 CHECK(shmdt(view + page) == -1 && errno == EINVAL);
91 if (strcmp(readonly,
"parent-shared"))
93 strcpy(view,
"child-shared");
94 char* extra = shmat(
id, NULL, 0);
95 if (extra == (
void*)-1)
97 if (shmctl(
id, IPC_STAT, &status) || status.shm_nattch != 5)
104 CHECK(reap(child) == 0);
105 CHECK(!strcmp(view,
"child-shared") && readonly[page] == 73);
106 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && status.shm_nattch == 2 && status.shm_dtime != 0);
112 snprintf(identifier,
sizeof(identifier),
"%d",
id);
113 execl(
"/applications/ipc-contract-test",
"ipc-contract-test",
"shm-exec", identifier,
"2",
117 CHECK(reap(child) == 0);
118 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && status.shm_nattch == 2);
120 reserved = mmap(NULL, length, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
121 CHECK(reserved != MAP_FAILED);
122 CHECK(shmat(
id, reserved, 0) == (
void*)-1 && errno == EINVAL);
123 CHECK(shmat(
id, (
char*)reserved + 3, 0) == (
void*)-1 && errno == EINVAL);
124 CHECK(shmat(
id, NULL, SHM_REMAP) == (
void*)-1 && errno == EINVAL);
125 remapped = shmat(
id, (
char*)reserved + 3, SHM_RND | SHM_REMAP);
126 CHECK(remapped == reserved && !strcmp(remapped,
"child-shared"));
127 reserved = MAP_FAILED;
128 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && status.shm_nattch == 3);
129 CHECK(shmdt(remapped) == 0);
130 remapped = (
void*)-1;
132 bad = mmap(NULL, page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
133 CHECK(bad != MAP_FAILED);
134 CHECK(shmctl(
id, IPC_STAT, bad) == -1 && errno == EFAULT);
135 CHECK(shmctl(
id, IPC_SET, bad) == -1 && errno == EFAULT);
136 CHECK(shmctl(
id, IPC_STAT, &changed) == 0 && (changed.shm_perm.mode & 0777) == 0600);
137 changed.shm_perm.mode = 0640;
138 CHECK(shmctl(
id, IPC_SET, &changed) == 0);
139 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && (status.shm_perm.mode & 0777) == 0640);
140 int highest = shmctl(0, IPC_INFO, (
struct shmid_ds*)&limits);
141 CHECK(highest >= 0 && limits.shmmax >= length && limits.shmmni > 0);
142 int found_index = -1;
143 for (
int index = 0; index <= highest; ++index) {
144 if (shmctl(index, SHM_STAT, &status) ==
id)
147 CHECK(found_index >= 0);
148 CHECK(shmctl(found_index, SHM_STAT_ANY, &status) ==
id);
149 CHECK(shmctl(0, SHM_INFO, (
struct shmid_ds*)&
usage) >= 0 &&
usage.used_ids >= 1 &&
151 CHECK(shmctl(
id, SHM_LOCK, NULL) == 0);
152 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && (status.shm_perm.mode & SHM_LOCKED));
153 CHECK(shmctl(
id, SHM_UNLOCK, NULL) == 0);
154 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && !(status.shm_perm.mode & SHM_LOCKED));
156 if (geteuid() == 0) {
161 if (setgid(65534) || setuid(65534))
163 if (shmat(
id, NULL, SHM_RDONLY) != (
void*)-1 || errno != EACCES)
165 if (shmget(key, 0, 0400) != -1 || errno != EACCES)
167 if (shmctl(
id, IPC_STAT, &status) != -1 || errno != EACCES)
169 if (shmctl(
id, IPC_RMID, NULL) != -1 || errno != EPERM)
171 _exit(shmctl(found_index, SHM_STAT_ANY, &status) ==
id ? 0 : 6);
173 CHECK(reap(child) == 0);
176 CHECK(shmctl(
id, IPC_RMID, NULL) == 0);
177 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && (status.shm_perm.mode & SHM_DEST));
178 CHECK(shmget(key, 0, 0600) == -1 && errno == ENOENT);
179 separate = shmget(key, page, IPC_CREAT | IPC_EXCL | 0600);
180 CHECK(separate >= 0 && separate !=
id);
181 remapped = shmat(
id, NULL, 0);
182 CHECK(remapped != (
void*)-1 && !strcmp(remapped,
"child-shared"));
183 CHECK(shmdt(remapped) == 0);
184 remapped = (
void*)-1;
185 CHECK(shmdt(readonly) == 0);
186 readonly = (
void*)-1;
187 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && status.shm_nattch == 1);
190 CHECK(shmat(
id, view, SHM_REMAP) == view && !strcmp(view,
"child-shared"));
191 CHECK(shmctl(
id, IPC_STAT, &status) == 0 && status.shm_nattch == 1);
192 CHECK(shmdt(view) == 0);
194 CHECK(shmctl(
id, IPC_STAT, &status) == -1 && errno == EINVAL);
197 split_id = shmget(IPC_PRIVATE, length, 0600);
198 CHECK(split_id >= 0);
199 split = shmat(split_id, NULL, 0);
200 CHECK(split != (
void*)-1);
201 CHECK(shmctl(split_id, IPC_RMID, NULL) == 0);
202 CHECK(mprotect(split + page, page, PROT_READ) == 0);
203 CHECK(shmctl(split_id, IPC_STAT, &status) == 0 && status.shm_nattch == 1);
208 _exit(shmctl(split_id, IPC_STAT, &status) == 0 && status.shm_nattch == 2 ? 0 : 1);
210 CHECK(reap(child) == 0);
211 CHECK(shmctl(split_id, IPC_STAT, &status) == 0 && status.shm_nattch == 1);
212 CHECK(munmap(split, page) == 0);
214 mmap(split, page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
215 CHECK(replacement == split);
217 middle = mmap(split + page, page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
219 CHECK(middle == split + page);
221 CHECK(shmdt(split) == 0);
223 CHECK(replacement[0] == 19 && middle[0] == 29);
224 CHECK(shmctl(split_id, IPC_STAT, &status) == -1 && errno == EINVAL);
227 split_id = shmget(IPC_PRIVATE, length, 0600);
228 CHECK(split_id >= 0);
229 split = shmat(split_id, NULL, 0);
230 CHECK(split != (
void*)-1);
232 CHECK(munmap(split, page) == 0);
233 remapped = shmat(separate, split, 0);
234 CHECK(remapped == split);
236 CHECK(shmdt(remapped) == 0);
237 remapped = (
void*)-1;
238 CHECK(shmctl(separate, IPC_STAT, &status) == 0 && status.shm_nattch == 0);
239 CHECK(shmctl(split_id, IPC_STAT, &status) == 0 && status.shm_nattch == 1);
240 CHECK(split[page] == 31);
241 CHECK(shmdt(split) == 0);
243 CHECK(shmctl(split_id, IPC_RMID, NULL) == 0);
247 if (view != (
void*)-1)
249 if (readonly != (
void*)-1)
251 if (remapped != (
void*)-1)
253 if (split != (
void*)-1)
256 shmctl(
id, IPC_RMID, NULL);
258 shmctl(separate, IPC_RMID, NULL);
260 shmctl(split_id, IPC_RMID, NULL);
261 if (reserved != MAP_FAILED)
262 munmap(reserved, length);
263 if (replacement != MAP_FAILED)
264 munmap(replacement, page);
265 if (middle != MAP_FAILED)
266 munmap(middle, page);
267 if (bad != MAP_FAILED)
271 puts(
"ipc-contract-test: shared-memory passed");