2 * Copyright (c) 2006 Robert N. M. Watson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
32 #include <sys/resource.h>
34 #include <sys/syscall.h>
46 #define TEST_PATH "/tmp/posixshm_regression_test"
49 * Attempt a shm_open() that should fail with an expected error of 'error'.
52 shm_open_should_fail(const char *path
, int flags
, mode_t mode
, int error
)
56 fd
= shm_open(path
, flags
, mode
);
58 fail_err("shm_open() didn't fail");
63 fail_errno("shm_open");
70 * Attempt a shm_unlink() that should fail with an expected error of 'error'.
73 shm_unlink_should_fail(const char *path
, int error
)
76 if (shm_unlink(path
) >= 0) {
77 fail_err("shm_unlink() didn't fail");
81 fail_errno("shm_unlink");
88 * Open the test object and write '1' to the first byte. Returns valid fd
89 * on success and -1 on failure.
97 fd
= shm_open(TEST_PATH
, O_CREAT
| O_EXCL
| O_RDWR
, 0777);
98 if (fd
< 0 && errno
== EEXIST
) {
99 if (shm_unlink(TEST_PATH
) < 0) {
100 fail_errno("shm_unlink");
103 fd
= shm_open(TEST_PATH
, O_CREAT
| O_EXCL
| O_RDWR
, 0777);
106 fail_errno("shm_open");
109 if (ftruncate(fd
, getpagesize()) < 0) {
110 fail_errno("ftruncate");
112 shm_unlink(TEST_PATH
);
116 page
= mmap(0, getpagesize(), PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
,
118 if (page
== MAP_FAILED
) {
121 shm_unlink(TEST_PATH
);
127 if (munmap(page
, getpagesize()) < 0) {
128 fail_errno("munmap");
130 shm_unlink(TEST_PATH
);
143 fd
= scribble_object();
147 page
= mmap(0, getpagesize(), PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
,
149 if (page
== MAP_FAILED
) {
150 fail_errno("mmap(2)");
152 shm_unlink(TEST_PATH
);
156 if (page
[0] != '1') {
157 fail_err("missing data");
159 shm_unlink(TEST_PATH
);
164 if (munmap(page
, getpagesize()) < 0) {
165 fail_errno("munmap");
166 shm_unlink(TEST_PATH
);
170 if (shm_unlink(TEST_PATH
) < 0) {
171 fail_errno("shm_unlink");
177 TEST(remap_object
, "remap object");
185 fd
= scribble_object();
190 fd
= shm_open(TEST_PATH
, O_RDONLY
, 0777);
192 fail_errno("shm_open(2)");
193 shm_unlink(TEST_PATH
);
196 page
= mmap(0, getpagesize(), PROT_READ
, MAP_SHARED
, fd
, 0);
197 if (page
== MAP_FAILED
) {
198 fail_errno("mmap(2)");
200 shm_unlink(TEST_PATH
);
204 if (page
[0] != '1') {
205 fail_err("missing data");
206 munmap(page
, getpagesize());
208 shm_unlink(TEST_PATH
);
212 munmap(page
, getpagesize());
214 shm_unlink(TEST_PATH
);
217 TEST(reopen_object
, "reopen object");
220 readonly_mmap_write(void)
225 fd
= shm_open(TEST_PATH
, O_RDONLY
| O_CREAT
, 0777);
227 fail_errno("shm_open");
231 /* PROT_WRITE should fail with EACCES. */
232 page
= mmap(0, getpagesize(), PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
,
234 if (page
!= MAP_FAILED
) {
235 fail_err("mmap(PROT_WRITE) succeeded");
236 munmap(page
, getpagesize());
238 shm_unlink(TEST_PATH
);
241 if (errno
!= EACCES
) {
244 shm_unlink(TEST_PATH
);
249 shm_unlink(TEST_PATH
);
252 TEST(readonly_mmap_write
, "RDONLY object");
255 open_after_unlink(void)
259 fd
= shm_open(TEST_PATH
, O_RDONLY
| O_CREAT
, 0777);
261 fail_errno("shm_open(1)");
266 if (shm_unlink(TEST_PATH
) < 0) {
267 fail_errno("shm_unlink");
271 shm_open_should_fail(TEST_PATH
, O_RDONLY
, 0777, ENOENT
);
273 TEST(open_after_unlink
, "open after unlink");
276 open_invalid_path(void)
279 shm_open_should_fail("blah", O_RDONLY
, 0777, EINVAL
);
281 TEST(open_invalid_path
, "open invalid path");
284 open_write_only(void)
287 shm_open_should_fail(TEST_PATH
, O_WRONLY
, 0777, EINVAL
);
289 TEST(open_write_only
, "open with O_WRONLY");
292 open_extra_flags(void)
295 shm_open_should_fail(TEST_PATH
, O_RDONLY
| O_DIRECT
, 0777, EINVAL
);
297 TEST(open_extra_flags
, "open with extra flags");
304 fd
= shm_open(SHM_ANON
, O_RDWR
, 0777);
306 fail_errno("shm_open");
312 TEST(open_anon
, "open anonymous object");
315 open_anon_readonly(void)
318 shm_open_should_fail(SHM_ANON
, O_RDONLY
, 0777, EINVAL
);
320 TEST(open_anon_readonly
, "open SHM_ANON with O_RDONLY");
323 open_bad_path_pointer(void)
326 shm_open_should_fail((char *)1024, O_RDONLY
, 0777, EFAULT
);
328 TEST(open_bad_path_pointer
, "open bad path pointer");
331 open_path_too_long(void)
335 page
= malloc(MAXPATHLEN
+ 1);
336 memset(page
, 'a', MAXPATHLEN
);
337 page
[MAXPATHLEN
] = '\0';
338 shm_open_should_fail(page
, O_RDONLY
, 0777, ENAMETOOLONG
);
341 TEST(open_path_too_long
, "open pathname too long");
344 open_nonexisting_object(void)
347 shm_open_should_fail("/notreallythere", O_RDONLY
, 0777, ENOENT
);
349 TEST(open_nonexisting_object
, "open nonexistent object");
352 exclusive_create_existing_object(void)
356 fd
= shm_open("/tmp/notreallythere", O_RDONLY
| O_CREAT
, 0777);
358 fail_errno("shm_open(O_CREAT)");
363 shm_open_should_fail("/tmp/notreallythere", O_RDONLY
| O_CREAT
| O_EXCL
,
366 shm_unlink("/tmp/notreallythere");
368 TEST(exclusive_create_existing_object
, "O_EXCL of existing object");
371 trunc_resets_object(void)
376 /* Create object and set size to 1024. */
377 fd
= shm_open(TEST_PATH
, O_RDWR
| O_CREAT
, 0777);
379 fail_errno("shm_open(1)");
382 if (ftruncate(fd
, 1024) < 0) {
383 fail_errno("ftruncate");
387 if (fstat(fd
, &sb
) < 0) {
388 fail_errno("fstat(1)");
392 if (sb
.st_size
!= 1024) {
393 fail_err("size %d != 1024", (int)sb
.st_size
);
399 /* Open with O_TRUNC which should reset size to 0. */
400 fd
= shm_open(TEST_PATH
, O_RDWR
| O_TRUNC
, 0777);
402 fail_errno("shm_open(2)");
405 if (fstat(fd
, &sb
) < 0) {
406 fail_errno("fstat(2)");
410 if (sb
.st_size
!= 0) {
411 fail_err("size after O_TRUNC %d != 0", (int)sb
.st_size
);
416 if (shm_unlink(TEST_PATH
) < 0) {
417 fail_errno("shm_unlink");
422 TEST(trunc_resets_object
, "O_TRUNC resets size");
425 unlink_bad_path_pointer(void)
428 shm_unlink_should_fail((char *)1024, EFAULT
);
430 TEST(unlink_bad_path_pointer
, "unlink bad path pointer");
433 unlink_path_too_long(void)
437 page
= malloc(MAXPATHLEN
+ 1);
438 memset(page
, 'a', MAXPATHLEN
);
439 page
[MAXPATHLEN
] = '\0';
440 shm_unlink_should_fail(page
, ENAMETOOLONG
);
443 TEST(unlink_path_too_long
, "unlink pathname too long");
446 test_object_resize(void)
453 /* Start off with a size of a single page. */
454 fd
= shm_open(SHM_ANON
, O_CREAT
| O_RDWR
, 0777);
456 fail_errno("shm_open");
459 if (ftruncate(fd
, getpagesize()) < 0) {
460 fail_errno("ftruncate(1)");
464 if (fstat(fd
, &sb
) < 0) {
465 fail_errno("fstat(1)");
469 if (sb
.st_size
!= getpagesize()) {
470 fail_err("first resize failed");
475 /* Write a '1' to the first byte. */
476 page
= mmap(0, getpagesize(), PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
,
478 if (page
== MAP_FAILED
) {
479 fail_errno("mmap(1)");
486 if (munmap(page
, getpagesize()) < 0) {
487 fail_errno("munmap(1)");
492 /* Grow the object to 2 pages. */
493 if (ftruncate(fd
, getpagesize() * 2) < 0) {
494 fail_errno("ftruncate(2)");
498 if (fstat(fd
, &sb
) < 0) {
499 fail_errno("fstat(2)");
503 if (sb
.st_size
!= getpagesize() * 2) {
504 fail_err("second resize failed");
509 /* Check for '1' at the first byte. */
510 page
= mmap(0, getpagesize() * 2, PROT_READ
| PROT_WRITE
, MAP_SHARED
,
512 if (page
== MAP_FAILED
) {
513 fail_errno("mmap(2)");
518 if (page
[0] != '1') {
519 fail_err("missing data at 0");
524 /* Write a '2' at the start of the second page. */
525 page
[getpagesize()] = '2';
527 /* Shrink the object back to 1 page. */
528 if (ftruncate(fd
, getpagesize()) < 0) {
529 fail_errno("ftruncate(3)");
533 if (fstat(fd
, &sb
) < 0) {
534 fail_errno("fstat(3)");
538 if (sb
.st_size
!= getpagesize()) {
539 fail_err("third resize failed");
545 * Fork a child process to make sure the second page is no
559 /* Don't generate a core dump. */
560 getrlimit(RLIMIT_CORE
, &lim
);
562 setrlimit(RLIMIT_CORE
, &lim
);
565 * The previous ftruncate(2) shrunk the backing object
566 * so that this address is no longer valid, so reading
567 * from it should trigger a SIGSEGV.
569 c
= page
[getpagesize()];
570 fprintf(stderr
, "child: page 1: '%c'\n", c
);
573 if (wait(&status
) < 0) {
578 if (!WIFSIGNALED(status
) || WTERMSIG(status
) != SIGSEGV
) {
579 fail_err("child terminated with status %x", status
);
584 /* Grow the object back to 2 pages. */
585 if (ftruncate(fd
, getpagesize() * 2) < 0) {
586 fail_errno("ftruncate(4)");
590 if (fstat(fd
, &sb
) < 0) {
591 fail_errno("fstat(4)");
595 if (sb
.st_size
!= getpagesize() * 2) {
596 fail_err("second resize failed");
602 * Note that the mapping at 'page' for the second page is
603 * still valid, and now that the shm object has been grown
604 * back up to 2 pages, there is now memory backing this page
605 * so the read will work. However, the data should be zero
606 * rather than '2' as the old data was thrown away when the
607 * object was shrunk and the new pages when an object are
608 * grown are zero-filled.
610 if (page
[getpagesize()] != 0) {
611 fail_err("invalid data at %d", getpagesize());
619 TEST(test_object_resize
, "object resize");
622 main(int argc
, char *argv
[])