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[])