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>
47 #define TEST_PATH_LEN 256
48 static char test_path
[TEST_PATH_LEN
];
54 snprintf(test_path
, sizeof(test_path
), "%s/tmp.XXXXXX",
55 getenv("TMPDIR") == NULL
? "/tmp" : getenv("TMPDIR"));
56 test_path
[sizeof(test_path
) - 1] = '\0';
57 ATF_REQUIRE_MSG(mkstemp(test_path
) != -1,
58 "mkstemp failed; errno=%d", errno
);
59 ATF_REQUIRE_MSG(unlink(test_path
) == 0,
60 "unlink failed; errno=%d", errno
);
64 * Attempt a shm_open() that should fail with an expected error of 'error'.
67 shm_open_should_fail(const char *path
, int flags
, mode_t mode
, int error
)
71 fd
= shm_open(path
, flags
, mode
);
72 ATF_CHECK_MSG(fd
== -1, "shm_open didn't fail");
73 ATF_CHECK_MSG(error
== errno
,
74 "shm_open didn't fail with expected errno; errno=%d; expected "
75 "errno=%d", errno
, error
);
79 * Attempt a shm_unlink() that should fail with an expected error of 'error'.
82 shm_unlink_should_fail(const char *path
, int error
)
85 ATF_CHECK_MSG(shm_unlink(path
) == -1, "shm_unlink didn't fail");
86 ATF_CHECK_MSG(error
== errno
,
87 "shm_unlink didn't fail with expected errno; errno=%d; expected "
88 "errno=%d", errno
, error
);
92 * Open the test object and write '1' to the first byte. Returns valid fd
93 * on success and -1 on failure.
103 ATF_REQUIRE(0 < (pagesize
= getpagesize()));
105 fd
= shm_open(test_path
, O_CREAT
|O_EXCL
|O_RDWR
, 0777);
106 if (fd
< 0 && errno
== EEXIST
) {
107 if (shm_unlink(test_path
) < 0)
108 atf_tc_fail("shm_unlink");
109 fd
= shm_open(test_path
, O_CREAT
| O_EXCL
| O_RDWR
, 0777);
112 atf_tc_fail("shm_open failed; errno=%d", errno
);
113 if (ftruncate(fd
, pagesize
) < 0)
114 atf_tc_fail("ftruncate failed; errno=%d", errno
);
116 page
= mmap(0, pagesize
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
117 if (page
== MAP_FAILED
)
118 atf_tc_fail("mmap failed; errno=%d", errno
);
121 ATF_REQUIRE_MSG(munmap(page
, pagesize
) == 0, "munmap failed; errno=%d",
127 ATF_TC_WITHOUT_HEAD(remap_object
);
128 ATF_TC_BODY(remap_object
, tc
)
133 ATF_REQUIRE(0 < (pagesize
= getpagesize()));
135 fd
= scribble_object();
137 page
= mmap(0, pagesize
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
138 if (page
== MAP_FAILED
)
139 atf_tc_fail("mmap(2) failed; errno=%d", errno
);
142 atf_tc_fail("missing data ('%c' != '1')", page
[0]);
145 ATF_REQUIRE_MSG(munmap(page
, pagesize
) == 0, "munmap failed; errno=%d",
148 ATF_REQUIRE_MSG(shm_unlink(test_path
) != -1,
149 "shm_unlink failed; errno=%d", errno
);
152 ATF_TC_WITHOUT_HEAD(reopen_object
);
153 ATF_TC_BODY(reopen_object
, tc
)
158 ATF_REQUIRE(0 < (pagesize
= getpagesize()));
160 fd
= scribble_object();
163 fd
= shm_open(test_path
, O_RDONLY
, 0777);
165 atf_tc_fail("shm_open(2) failed; errno=%d", errno
);
167 page
= mmap(0, pagesize
, PROT_READ
, MAP_SHARED
, fd
, 0);
168 if (page
== MAP_FAILED
)
169 atf_tc_fail("mmap(2) failed; errno=%d", errno
);
172 atf_tc_fail("missing data ('%c' != '1')", page
[0]);
174 ATF_REQUIRE_MSG(munmap(page
, pagesize
) == 0, "munmap failed; errno=%d",
177 ATF_REQUIRE_MSG(shm_unlink(test_path
) != -1,
178 "shm_unlink failed; errno=%d", errno
);
181 ATF_TC_WITHOUT_HEAD(readonly_mmap_write
);
182 ATF_TC_BODY(readonly_mmap_write
, tc
)
187 ATF_REQUIRE(0 < (pagesize
= getpagesize()));
191 fd
= shm_open(test_path
, O_RDONLY
| O_CREAT
, 0777);
192 ATF_REQUIRE_MSG(fd
>= 0, "shm_open failed; errno=%d", errno
);
194 /* PROT_WRITE should fail with EACCES. */
195 page
= mmap(0, pagesize
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
196 if (page
!= MAP_FAILED
)
197 atf_tc_fail("mmap(PROT_WRITE) succeeded unexpectedly");
200 atf_tc_fail("mmap(PROT_WRITE) didn't fail with EACCES; "
204 ATF_REQUIRE_MSG(shm_unlink(test_path
) != -1,
205 "shm_unlink failed; errno=%d", errno
);
208 ATF_TC_WITHOUT_HEAD(open_after_link
);
209 ATF_TC_BODY(open_after_link
, tc
)
215 fd
= shm_open(test_path
, O_RDONLY
| O_CREAT
, 0777);
216 ATF_REQUIRE_MSG(fd
>= 0, "shm_open(1) failed; errno=%d", errno
);
219 ATF_REQUIRE_MSG(shm_unlink(test_path
) != -1, "shm_unlink failed: %d",
222 shm_open_should_fail(test_path
, O_RDONLY
, 0777, ENOENT
);
225 ATF_TC_WITHOUT_HEAD(open_invalid_path
);
226 ATF_TC_BODY(open_invalid_path
, tc
)
229 shm_open_should_fail("blah", O_RDONLY
, 0777, EINVAL
);
232 ATF_TC_WITHOUT_HEAD(open_write_only
);
233 ATF_TC_BODY(open_write_only
, tc
)
238 shm_open_should_fail(test_path
, O_WRONLY
, 0777, EINVAL
);
241 ATF_TC_WITHOUT_HEAD(open_extra_flags
);
242 ATF_TC_BODY(open_extra_flags
, tc
)
247 shm_open_should_fail(test_path
, O_RDONLY
| O_DIRECT
, 0777, EINVAL
);
250 ATF_TC_WITHOUT_HEAD(open_anon
);
251 ATF_TC_BODY(open_anon
, tc
)
255 fd
= shm_open(SHM_ANON
, O_RDWR
, 0777);
256 ATF_REQUIRE_MSG(fd
>= 0, "shm_open failed; errno=%d", errno
);
260 ATF_TC_WITHOUT_HEAD(open_anon_readonly
);
261 ATF_TC_BODY(open_anon_readonly
, tc
)
264 shm_open_should_fail(SHM_ANON
, O_RDONLY
, 0777, EINVAL
);
267 ATF_TC_WITHOUT_HEAD(open_bad_path_pointer
);
268 ATF_TC_BODY(open_bad_path_pointer
, tc
)
271 shm_open_should_fail((char *)1024, O_RDONLY
, 0777, EFAULT
);
274 ATF_TC_WITHOUT_HEAD(open_path_too_long
);
275 ATF_TC_BODY(open_path_too_long
, tc
)
279 page
= malloc(MAXPATHLEN
+ 1);
280 memset(page
, 'a', MAXPATHLEN
);
281 page
[MAXPATHLEN
] = '\0';
282 shm_open_should_fail(page
, O_RDONLY
, 0777, ENAMETOOLONG
);
286 ATF_TC_WITHOUT_HEAD(open_nonexisting_object
);
287 ATF_TC_BODY(open_nonexisting_object
, tc
)
290 shm_open_should_fail("/notreallythere", O_RDONLY
, 0777, ENOENT
);
293 ATF_TC_WITHOUT_HEAD(open_create_existing_object
);
294 ATF_TC_BODY(open_create_existing_object
, tc
)
300 fd
= shm_open(test_path
, O_RDONLY
|O_CREAT
, 0777);
301 ATF_REQUIRE_MSG(fd
>= 0, "shm_open failed; errno=%d", errno
);
304 shm_open_should_fail(test_path
, O_RDONLY
|O_CREAT
|O_EXCL
,
307 ATF_REQUIRE_MSG(shm_unlink(test_path
) != -1,
308 "shm_unlink failed; errno=%d", errno
);
311 ATF_TC_WITHOUT_HEAD(trunc_resets_object
);
312 ATF_TC_BODY(trunc_resets_object
, tc
)
319 /* Create object and set size to 1024. */
320 fd
= shm_open(test_path
, O_RDWR
| O_CREAT
, 0777);
321 ATF_REQUIRE_MSG(fd
>= 0, "shm_open(1) failed; errno=%d", errno
);
322 ATF_REQUIRE_MSG(ftruncate(fd
, 1024) != -1,
323 "ftruncate failed; errno=%d", errno
);
324 ATF_REQUIRE_MSG(fstat(fd
, &sb
) != -1,
325 "fstat(1) failed; errno=%d", errno
);
326 ATF_REQUIRE_MSG(sb
.st_size
== 1024, "size %d != 1024", (int)sb
.st_size
);
329 /* Open with O_TRUNC which should reset size to 0. */
330 fd
= shm_open(test_path
, O_RDWR
| O_TRUNC
, 0777);
331 ATF_REQUIRE_MSG(fd
>= 0, "shm_open(2) failed; errno=%d", errno
);
332 ATF_REQUIRE_MSG(fstat(fd
, &sb
) != -1,
333 "fstat(2) failed; errno=%d", errno
);
334 ATF_REQUIRE_MSG(sb
.st_size
== 0,
335 "size was not 0 after truncation: %d", (int)sb
.st_size
);
337 ATF_REQUIRE_MSG(shm_unlink(test_path
) != -1,
338 "shm_unlink failed; errno=%d", errno
);
341 ATF_TC_WITHOUT_HEAD(unlink_bad_path_pointer
);
342 ATF_TC_BODY(unlink_bad_path_pointer
, tc
)
345 shm_unlink_should_fail((char *)1024, EFAULT
);
348 ATF_TC_WITHOUT_HEAD(unlink_path_too_long
);
349 ATF_TC_BODY(unlink_path_too_long
, tc
)
353 page
= malloc(MAXPATHLEN
+ 1);
354 memset(page
, 'a', MAXPATHLEN
);
355 page
[MAXPATHLEN
] = '\0';
356 shm_unlink_should_fail(page
, ENAMETOOLONG
);
360 ATF_TC_WITHOUT_HEAD(object_resize
);
361 ATF_TC_BODY(object_resize
, tc
)
366 int fd
, pagesize
, status
;
368 ATF_REQUIRE(0 < (pagesize
= getpagesize()));
370 /* Start off with a size of a single page. */
371 fd
= shm_open(SHM_ANON
, O_CREAT
|O_RDWR
, 0777);
373 atf_tc_fail("shm_open failed; errno=%d", errno
);
375 if (ftruncate(fd
, pagesize
) < 0)
376 atf_tc_fail("ftruncate(1) failed; errno=%d", errno
);
378 if (fstat(fd
, &sb
) < 0)
379 atf_tc_fail("fstat(1) failed; errno=%d", errno
);
381 if (sb
.st_size
!= pagesize
)
382 atf_tc_fail("first resize failed (%d != %d)",
383 (int)sb
.st_size
, pagesize
);
385 /* Write a '1' to the first byte. */
386 page
= mmap(0, pagesize
, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
387 if (page
== MAP_FAILED
)
388 atf_tc_fail("mmap(1)");
392 ATF_REQUIRE_MSG(munmap(page
, pagesize
) == 0, "munmap failed; errno=%d",
395 /* Grow the object to 2 pages. */
396 if (ftruncate(fd
, pagesize
* 2) < 0)
397 atf_tc_fail("ftruncate(2) failed; errno=%d", errno
);
399 if (fstat(fd
, &sb
) < 0)
400 atf_tc_fail("fstat(2) failed; errno=%d", errno
);
402 if (sb
.st_size
!= pagesize
* 2)
403 atf_tc_fail("second resize failed (%d != %d)",
404 (int)sb
.st_size
, pagesize
* 2);
406 /* Check for '1' at the first byte. */
407 page
= mmap(0, pagesize
* 2, PROT_READ
|PROT_WRITE
, MAP_SHARED
, fd
, 0);
408 if (page
== MAP_FAILED
)
409 atf_tc_fail("mmap(2) failed; errno=%d", errno
);
412 atf_tc_fail("'%c' != '1'", page
[0]);
414 /* Write a '2' at the start of the second page. */
415 page
[pagesize
] = '2';
417 /* Shrink the object back to 1 page. */
418 if (ftruncate(fd
, pagesize
) < 0)
419 atf_tc_fail("ftruncate(3) failed; errno=%d", errno
);
421 if (fstat(fd
, &sb
) < 0)
422 atf_tc_fail("fstat(3) failed; errno=%d", errno
);
424 if (sb
.st_size
!= pagesize
)
425 atf_tc_fail("third resize failed (%d != %d)",
426 (int)sb
.st_size
, pagesize
);
429 * Fork a child process to make sure the second page is no
434 atf_tc_fail("fork failed; errno=%d", errno
);
440 /* Don't generate a core dump. */
441 ATF_REQUIRE(getrlimit(RLIMIT_CORE
, &lim
) == 0);
443 ATF_REQUIRE(setrlimit(RLIMIT_CORE
, &lim
) == 0);
446 * The previous ftruncate(2) shrunk the backing object
447 * so that this address is no longer valid, so reading
448 * from it should trigger a SIGSEGV.
451 fprintf(stderr
, "child: page 1: '%c'\n", c
);
455 if (wait(&status
) < 0)
456 atf_tc_fail("wait failed; errno=%d", errno
);
458 if (!WIFSIGNALED(status
) || WTERMSIG(status
) != SIGSEGV
)
459 atf_tc_fail("child terminated with status %x", status
);
461 /* Grow the object back to 2 pages. */
462 if (ftruncate(fd
, pagesize
* 2) < 0)
463 atf_tc_fail("ftruncate(2) failed; errno=%d", errno
);
465 if (fstat(fd
, &sb
) < 0)
466 atf_tc_fail("fstat(2) failed; errno=%d", errno
);
468 if (sb
.st_size
!= pagesize
* 2)
469 atf_tc_fail("fourth resize failed (%d != %d)",
470 (int)sb
.st_size
, pagesize
);
473 * Note that the mapping at 'page' for the second page is
474 * still valid, and now that the shm object has been grown
475 * back up to 2 pages, there is now memory backing this page
476 * so the read will work. However, the data should be zero
477 * rather than '2' as the old data was thrown away when the
478 * object was shrunk and the new pages when an object are
479 * grown are zero-filled.
481 if (page
[pagesize
] != 0)
482 atf_tc_fail("invalid data at %d: %x != 0",
483 pagesize
, (int)page
[pagesize
]);
488 /* Signal handler which does nothing. */
490 ignoreit(int sig __unused
)
495 ATF_TC_WITHOUT_HEAD(shm_functionality_across_fork
);
496 ATF_TC_BODY(shm_functionality_across_fork
, tc
)
506 #ifndef _POSIX_SHARED_MEMORY_OBJECTS
507 printf("_POSIX_SHARED_MEMORY_OBJECTS is undefined\n");
509 printf("_POSIX_SHARED_MEMORY_OBJECTS is defined as %ld\n",
510 (long)_POSIX_SHARED_MEMORY_OBJECTS
- 0);
511 if (_POSIX_SHARED_MEMORY_OBJECTS
- 0 == -1)
512 printf("***Indicates this feature may be unsupported!\n");
515 scval
= sysconf(_SC_SHARED_MEMORY_OBJECTS
);
516 if (scval
== -1 && errno
!= 0) {
517 atf_tc_fail("sysconf(_SC_SHARED_MEMORY_OBJECTS) failed; "
520 printf("sysconf(_SC_SHARED_MEMORY_OBJECTS) returns %ld\n",
523 printf("***Indicates this feature is unsupported!\n");
527 scval
= sysconf(_SC_PAGESIZE
);
528 if (scval
== -1 && errno
!= 0) {
529 atf_tc_fail("sysconf(_SC_PAGESIZE) failed; errno=%d", errno
);
530 } else if (scval
<= 0) {
531 fprintf(stderr
, "bogus return from sysconf(_SC_PAGESIZE): %ld",
535 printf("sysconf(_SC_PAGESIZE) returns %ld\n", scval
);
540 desc
= shm_open(test_path
, O_EXCL
| O_CREAT
| O_RDWR
, 0600);
542 ATF_REQUIRE_MSG(desc
>= 0, "shm_open failed; errno=%d", errno
);
543 ATF_REQUIRE_MSG(shm_unlink(test_path
) == 0,
544 "shm_unlink failed; errno=%d", errno
);
545 ATF_REQUIRE_MSG(ftruncate(desc
, (off_t
)psize
) != -1,
546 "ftruncate failed; errno=%d", errno
);
548 region
= mmap(NULL
, psize
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, desc
, 0);
549 ATF_REQUIRE_MSG(region
!= MAP_FAILED
, "mmap failed; errno=%d", errno
);
550 memset(region
, '\377', psize
);
553 sa
.sa_handler
= ignoreit
;
554 sigemptyset(&sa
.sa_mask
);
555 ATF_REQUIRE_MSG(sigaction(SIGUSR1
, &sa
, (struct sigaction
*)0) == 0,
556 "sigaction failed; errno=%d", errno
);
559 sigaddset(&ss
, SIGUSR1
);
560 ATF_REQUIRE_MSG(sigprocmask(SIG_BLOCK
, &ss
, (sigset_t
*)0) == 0,
561 "sigprocmask failed; errno=%d", errno
);
564 ATF_REQUIRE_MSG(rv
!= -1, "fork failed; errno=%d", errno
);
569 for (cp
= region
; cp
< (char *)region
+ psize
; cp
++) {
573 if (lseek(desc
, 0, SEEK_SET
) == -1)
575 for (i
= 0; i
< psize
; i
++) {
576 error
= read(desc
, &c
, 1);
584 memset(region
, '\151', psize
- 2);
585 error
= pwrite(desc
, region
, 2, psize
- 2);
588 atf_tc_fail("short write; %d bytes written",
591 atf_tc_fail("shmfd write");
594 waitpid(rv
, &status
, 0);
596 if (WIFEXITED(status
) && WEXITSTATUS(status
) == 0) {
597 printf("Functionality test successful\n");
598 } else if (WIFEXITED(status
)) {
599 atf_tc_fail("Child process exited with status %d",
600 WEXITSTATUS(status
));
602 atf_tc_fail("Child process terminated with %s",
603 strsignal(WTERMSIG(status
)));
607 ATF_REQUIRE_MSG(munmap(region
, psize
) == 0, "munmap failed; errno=%d",
609 shm_unlink(test_path
);
615 ATF_TP_ADD_TC(tp
, remap_object
);
616 ATF_TP_ADD_TC(tp
, reopen_object
);
617 ATF_TP_ADD_TC(tp
, readonly_mmap_write
);
618 ATF_TP_ADD_TC(tp
, open_after_link
);
619 ATF_TP_ADD_TC(tp
, open_invalid_path
);
620 ATF_TP_ADD_TC(tp
, open_write_only
);
621 ATF_TP_ADD_TC(tp
, open_extra_flags
);
622 ATF_TP_ADD_TC(tp
, open_anon
);
623 ATF_TP_ADD_TC(tp
, open_anon_readonly
);
624 ATF_TP_ADD_TC(tp
, open_bad_path_pointer
);
625 ATF_TP_ADD_TC(tp
, open_path_too_long
);
626 ATF_TP_ADD_TC(tp
, open_nonexisting_object
);
627 ATF_TP_ADD_TC(tp
, open_create_existing_object
);
628 ATF_TP_ADD_TC(tp
, shm_functionality_across_fork
);
629 ATF_TP_ADD_TC(tp
, trunc_resets_object
);
630 ATF_TP_ADD_TC(tp
, unlink_bad_path_pointer
);
631 ATF_TP_ADD_TC(tp
, unlink_path_too_long
);
632 ATF_TP_ADD_TC(tp
, object_resize
);
634 return (atf_no_error());