]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/regression/posixshm/posixshm.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / regression / posixshm / posixshm.c
1 /*-
2  * Copyright (c) 2006 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/mman.h>
32 #include <sys/resource.h>
33 #include <sys/stat.h>
34 #include <sys/syscall.h>
35 #include <sys/wait.h>
36
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43
44 #include "test.h"
45
46 #define TEST_PATH       "/tmp/posixshm_regression_test"
47
48 /*
49  * Attempt a shm_open() that should fail with an expected error of 'error'.
50  */
51 static void
52 shm_open_should_fail(const char *path, int flags, mode_t mode, int error)
53 {
54         int fd;
55
56         fd = shm_open(path, flags, mode);
57         if (fd >= 0) {
58                 fail_err("shm_open() didn't fail");
59                 close(fd);
60                 return;
61         }
62         if (errno != error) {
63                 fail_errno("shm_open");
64                 return;
65         }
66         pass();
67 }
68
69 /*
70  * Attempt a shm_unlink() that should fail with an expected error of 'error'.
71  */
72 static void
73 shm_unlink_should_fail(const char *path, int error)
74 {
75
76         if (shm_unlink(path) >= 0) {
77                 fail_err("shm_unlink() didn't fail");
78                 return;
79         }
80         if (errno != error) {
81                 fail_errno("shm_unlink");
82                 return;
83         }
84         pass();
85 }
86
87 /*
88  * Open the test object and write '1' to the first byte.  Returns valid fd
89  * on success and -1 on failure.
90  */
91 static int
92 scribble_object(void)
93 {
94         char *page;
95         int fd;
96
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");
101                         return (-1);
102                 }
103                 fd = shm_open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0777);
104         }
105         if (fd < 0) {
106                 fail_errno("shm_open");
107                 return (-1);
108         }
109         if (ftruncate(fd, getpagesize()) < 0) {
110                 fail_errno("ftruncate");
111                 close(fd);
112                 shm_unlink(TEST_PATH);
113                 return (-1);
114         }
115
116         page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
117             0);
118         if (page == MAP_FAILED) {
119                 fail_errno("mmap");
120                 close(fd);
121                 shm_unlink(TEST_PATH);
122                 return (-1);
123         }
124
125         page[0] = '1';
126
127         if (munmap(page, getpagesize()) < 0) {
128                 fail_errno("munmap");
129                 close(fd);
130                 shm_unlink(TEST_PATH);
131                 return (-1);
132         }
133
134         return (fd);
135 }
136
137 static void
138 remap_object(void)
139 {
140         char *page;
141         int fd;
142
143         fd = scribble_object();
144         if (fd < 0)
145                 return;
146
147         page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
148             0);
149         if (page == MAP_FAILED) {               
150                 fail_errno("mmap(2)");
151                 close(fd);
152                 shm_unlink(TEST_PATH);
153                 return;
154         }
155
156         if (page[0] != '1') {           
157                 fail_err("missing data");
158                 close(fd);
159                 shm_unlink(TEST_PATH);
160                 return;
161         }
162
163         close(fd);
164         if (munmap(page, getpagesize()) < 0) {
165                 fail_errno("munmap");
166                 shm_unlink(TEST_PATH);
167                 return;
168         }
169
170         if (shm_unlink(TEST_PATH) < 0) {
171                 fail_errno("shm_unlink");
172                 return;
173         }
174
175         pass();
176 }
177 TEST(remap_object, "remap object");
178
179 static void
180 reopen_object(void)
181 {
182         char *page;
183         int fd;
184
185         fd = scribble_object();
186         if (fd < 0)
187                 return;
188         close(fd);
189
190         fd = shm_open(TEST_PATH, O_RDONLY, 0777);
191         if (fd < 0) {
192                 fail_errno("shm_open(2)");
193                 shm_unlink(TEST_PATH);
194                 return;
195         }
196         page = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, fd, 0);
197         if (page == MAP_FAILED) {
198                 fail_errno("mmap(2)");
199                 close(fd);
200                 shm_unlink(TEST_PATH);
201                 return;
202         }
203
204         if (page[0] != '1') {
205                 fail_err("missing data");
206                 munmap(page, getpagesize());
207                 close(fd);
208                 shm_unlink(TEST_PATH);
209                 return;
210         }
211
212         munmap(page, getpagesize());
213         close(fd);
214         shm_unlink(TEST_PATH);
215         pass();
216 }
217 TEST(reopen_object, "reopen object");
218
219 static void
220 readonly_mmap_write(void)
221 {
222         char *page;
223         int fd;
224
225         fd = shm_open(TEST_PATH, O_RDONLY | O_CREAT, 0777);
226         if (fd < 0) {
227                 fail_errno("shm_open");
228                 return;
229         }
230
231         /* PROT_WRITE should fail with EACCES. */
232         page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
233             0);
234         if (page != MAP_FAILED) {               
235                 fail_err("mmap(PROT_WRITE) succeeded");
236                 munmap(page, getpagesize());
237                 close(fd);
238                 shm_unlink(TEST_PATH);
239                 return;
240         }
241         if (errno != EACCES) {
242                 fail_errno("mmap");
243                 close(fd);
244                 shm_unlink(TEST_PATH);
245                 return;
246         }
247
248         close(fd);
249         shm_unlink(TEST_PATH);
250         pass();
251 }
252 TEST(readonly_mmap_write, "RDONLY object");
253
254 static void
255 open_after_unlink(void)
256 {
257         int fd;
258
259         fd = shm_open(TEST_PATH, O_RDONLY | O_CREAT, 0777);
260         if (fd < 0) {
261                 fail_errno("shm_open(1)");
262                 return;
263         }
264         close(fd);
265
266         if (shm_unlink(TEST_PATH) < 0) {
267                 fail_errno("shm_unlink");
268                 return;
269         }
270
271         shm_open_should_fail(TEST_PATH, O_RDONLY, 0777, ENOENT);
272 }
273 TEST(open_after_unlink, "open after unlink");
274
275 static void
276 open_invalid_path(void)
277 {
278
279         shm_open_should_fail("blah", O_RDONLY, 0777, EINVAL);
280 }
281 TEST(open_invalid_path, "open invalid path");
282
283 static void
284 open_write_only(void)
285 {
286
287         shm_open_should_fail(TEST_PATH, O_WRONLY, 0777, EINVAL);
288 }
289 TEST(open_write_only, "open with O_WRONLY");
290
291 static void
292 open_extra_flags(void)
293 {
294
295         shm_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, EINVAL);
296 }
297 TEST(open_extra_flags, "open with extra flags");
298
299 static void
300 open_anon(void)
301 {
302         int fd;
303
304         fd = shm_open(SHM_ANON, O_RDWR, 0777);
305         if (fd < 0) {
306                 fail_errno("shm_open");
307                 return;
308         }
309         close(fd);
310         pass();
311 }
312 TEST(open_anon, "open anonymous object");
313
314 static void
315 open_anon_readonly(void)
316 {
317
318         shm_open_should_fail(SHM_ANON, O_RDONLY, 0777, EINVAL);
319 }
320 TEST(open_anon_readonly, "open SHM_ANON with O_RDONLY");
321
322 static void
323 open_bad_path_pointer(void)
324 {
325
326         shm_open_should_fail((char *)1024, O_RDONLY, 0777, EFAULT);
327 }
328 TEST(open_bad_path_pointer, "open bad path pointer");
329
330 static void
331 open_path_too_long(void)
332 {
333         char *page;
334
335         page = malloc(MAXPATHLEN + 1);
336         memset(page, 'a', MAXPATHLEN);
337         page[MAXPATHLEN] = '\0';
338         shm_open_should_fail(page, O_RDONLY, 0777, ENAMETOOLONG);
339         free(page);
340 }
341 TEST(open_path_too_long, "open pathname too long");
342
343 static void
344 open_nonexisting_object(void)
345 {
346
347         shm_open_should_fail("/notreallythere", O_RDONLY, 0777, ENOENT);
348 }
349 TEST(open_nonexisting_object, "open nonexistent object");
350
351 static void
352 exclusive_create_existing_object(void)
353 {
354         int fd;
355
356         fd = shm_open("/tmp/notreallythere", O_RDONLY | O_CREAT, 0777);
357         if (fd < 0) {
358                 fail_errno("shm_open(O_CREAT)");
359                 return;
360         }
361         close(fd);
362
363         shm_open_should_fail("/tmp/notreallythere", O_RDONLY | O_CREAT | O_EXCL,
364             0777, EEXIST);
365
366         shm_unlink("/tmp/notreallythere");
367 }
368 TEST(exclusive_create_existing_object, "O_EXCL of existing object");
369
370 static void
371 trunc_resets_object(void)
372 {
373         struct stat sb;
374         int fd;
375
376         /* Create object and set size to 1024. */
377         fd = shm_open(TEST_PATH, O_RDWR | O_CREAT, 0777);
378         if (fd < 0) {
379                 fail_errno("shm_open(1)");
380                 return;
381         }
382         if (ftruncate(fd, 1024) < 0) {
383                 fail_errno("ftruncate");
384                 close(fd);
385                 return;
386         }
387         if (fstat(fd, &sb) < 0) {               
388                 fail_errno("fstat(1)");
389                 close(fd);
390                 return;
391         }
392         if (sb.st_size != 1024) {
393                 fail_err("size %d != 1024", (int)sb.st_size);
394                 close(fd);
395                 return;
396         }
397         close(fd);
398
399         /* Open with O_TRUNC which should reset size to 0. */
400         fd = shm_open(TEST_PATH, O_RDWR | O_TRUNC, 0777);
401         if (fd < 0) {
402                 fail_errno("shm_open(2)");
403                 return;
404         }
405         if (fstat(fd, &sb) < 0) {
406                 fail_errno("fstat(2)");
407                 close(fd);
408                 return;
409         }
410         if (sb.st_size != 0) {
411                 fail_err("size after O_TRUNC %d != 0", (int)sb.st_size);
412                 close(fd);
413                 return;
414         }
415         close(fd);
416         if (shm_unlink(TEST_PATH) < 0) {
417                 fail_errno("shm_unlink");
418                 return;
419         }
420         pass();
421 }
422 TEST(trunc_resets_object, "O_TRUNC resets size");
423
424 static void
425 unlink_bad_path_pointer(void)
426 {
427
428         shm_unlink_should_fail((char *)1024, EFAULT);
429 }
430 TEST(unlink_bad_path_pointer, "unlink bad path pointer");
431
432 static void
433 unlink_path_too_long(void)
434 {
435         char *page;
436
437         page = malloc(MAXPATHLEN + 1);
438         memset(page, 'a', MAXPATHLEN);
439         page[MAXPATHLEN] = '\0';
440         shm_unlink_should_fail(page, ENAMETOOLONG);
441         free(page);
442 }
443 TEST(unlink_path_too_long, "unlink pathname too long");
444
445 static void
446 test_object_resize(void)
447 {
448         pid_t pid;
449         struct stat sb;
450         char *page;
451         int fd, status;
452
453         /* Start off with a size of a single page. */
454         fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0777);
455         if (fd < 0) {
456                 fail_errno("shm_open");
457                 return;
458         }
459         if (ftruncate(fd, getpagesize()) < 0) {
460                 fail_errno("ftruncate(1)");
461                 close(fd);
462                 return;
463         }
464         if (fstat(fd, &sb) < 0) {
465                 fail_errno("fstat(1)");
466                 close(fd);
467                 return;
468         }
469         if (sb.st_size != getpagesize()) {
470                 fail_err("first resize failed");
471                 close(fd);
472                 return;
473         }
474
475         /* Write a '1' to the first byte. */
476         page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
477             0);
478         if (page == MAP_FAILED) {
479                 fail_errno("mmap(1)");
480                 close(fd);
481                 return;
482         }
483
484         page[0] = '1';
485
486         if (munmap(page, getpagesize()) < 0) {
487                 fail_errno("munmap(1)");
488                 close(fd);
489                 return;
490         }
491
492         /* Grow the object to 2 pages. */
493         if (ftruncate(fd, getpagesize() * 2) < 0) {
494                 fail_errno("ftruncate(2)");
495                 close(fd);
496                 return;
497         }
498         if (fstat(fd, &sb) < 0) {
499                 fail_errno("fstat(2)");
500                 close(fd);
501                 return;
502         }
503         if (sb.st_size != getpagesize() * 2) {
504                 fail_err("second resize failed");
505                 close(fd);
506                 return;
507         }
508
509         /* Check for '1' at the first byte. */
510         page = mmap(0, getpagesize() * 2, PROT_READ | PROT_WRITE, MAP_SHARED,
511             fd, 0);
512         if (page == MAP_FAILED) {
513                 fail_errno("mmap(2)");
514                 close(fd);
515                 return;
516         }
517
518         if (page[0] != '1') {
519                 fail_err("missing data at 0");
520                 close(fd);
521                 return;
522         }
523
524         /* Write a '2' at the start of the second page. */
525         page[getpagesize()] = '2';
526
527         /* Shrink the object back to 1 page. */
528         if (ftruncate(fd, getpagesize()) < 0) {
529                 fail_errno("ftruncate(3)");
530                 close(fd);
531                 return;
532         }
533         if (fstat(fd, &sb) < 0) {
534                 fail_errno("fstat(3)");
535                 close(fd);
536                 return;
537         }
538         if (sb.st_size != getpagesize()) {
539                 fail_err("third resize failed");
540                 close(fd);
541                 return;
542         }
543
544         /*
545          * Fork a child process to make sure the second page is no
546          * longer valid.
547          */
548         pid = fork();
549         if (pid < 0) {
550                 fail_errno("fork");
551                 close(fd);
552                 return;
553         }
554
555         if (pid == 0) {
556                 struct rlimit lim;
557                 char c;
558
559                 /* Don't generate a core dump. */
560                 getrlimit(RLIMIT_CORE, &lim);
561                 lim.rlim_cur = 0;
562                 setrlimit(RLIMIT_CORE, &lim);
563
564                 /*
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.
568                  */
569                 c = page[getpagesize()];
570                 fprintf(stderr, "child: page 1: '%c'\n", c);
571                 exit(0);
572         }
573         if (wait(&status) < 0) {
574                 fail_errno("wait");
575                 close(fd);
576                 return;
577         }
578         if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGSEGV) {
579                 fail_err("child terminated with status %x", status);
580                 close(fd);
581                 return;
582         }
583
584         /* Grow the object back to 2 pages. */
585         if (ftruncate(fd, getpagesize() * 2) < 0) {
586                 fail_errno("ftruncate(4)");
587                 close(fd);
588                 return;
589         }
590         if (fstat(fd, &sb) < 0) {
591                 fail_errno("fstat(4)");
592                 close(fd);
593                 return;
594         }
595         if (sb.st_size != getpagesize() * 2) {
596                 fail_err("second resize failed");
597                 close(fd);
598                 return;
599         }
600
601         /*
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.
609          */
610         if (page[getpagesize()] != 0) {
611                 fail_err("invalid data at %d", getpagesize());
612                 close(fd);
613                 return;
614         }
615
616         close(fd);
617         pass();
618 }
619 TEST(test_object_resize, "object resize");
620
621 int
622 main(int argc, char *argv[])
623 {
624
625         run_tests();
626         return (0);
627 }