150276Speter/*-
2166124Srafan * Copyright (c) 2006 Robert N. M. Watson
3166124Srafan * All rights reserved.
4166124Srafan *
5166124Srafan * Redistribution and use in source and binary forms, with or without
6166124Srafan * modification, are permitted provided that the following conditions
7166124Srafan * are met:
8166124Srafan * 1. Redistributions of source code must retain the above copyright
9166124Srafan *    notice, this list of conditions and the following disclaimer.
10166124Srafan * 2. Redistributions in binary form must reproduce the above copyright
11166124Srafan *    notice, this list of conditions and the following disclaimer in the
12166124Srafan *    documentation and/or other materials provided with the distribution.
13166124Srafan *
14166124Srafan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15166124Srafan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16166124Srafan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17166124Srafan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18166124Srafan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19166124Srafan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20166124Srafan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21166124Srafan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22166124Srafan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23166124Srafan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24166124Srafan * SUCH DAMAGE.
25166124Srafan */
26166124Srafan
27166124Srafan#include <sys/cdefs.h>
28166124Srafan__FBSDID("$FreeBSD: releng/10.2/tools/regression/posixshm/posixshm.c 175383 2008-01-16 15:51:24Z jhb $");
29166124Srafan
3050276Speter#include <sys/param.h>
3150276Speter#include <sys/mman.h>
3250276Speter#include <sys/resource.h>
3350276Speter#include <sys/stat.h>
3450276Speter#include <sys/syscall.h>
3550276Speter#include <sys/wait.h>
3650276Speter
3750276Speter#include <errno.h>
3850276Speter#include <fcntl.h>
3950276Speter#include <stdio.h>
4066963Speter#include <stdlib.h>
4150276Speter#include <string.h>
4250276Speter#include <unistd.h>
4350276Speter
4450276Speter#include "test.h"
4550276Speter
4650276Speter#define	TEST_PATH	"/tmp/posixshm_regression_test"
4750276Speter
4856639Speter/*
4950276Speter * Attempt a shm_open() that should fail with an expected error of 'error'.
5050276Speter */
5150276Speterstatic void
5250276Spetershm_open_should_fail(const char *path, int flags, mode_t mode, int error)
5350276Speter{
5450276Speter	int fd;
5550276Speter
5650276Speter	fd = shm_open(path, flags, mode);
5750276Speter	if (fd >= 0) {
5850276Speter		fail_err("shm_open() didn't fail");
5950276Speter		close(fd);
6050276Speter		return;
6150276Speter	}
6250276Speter	if (errno != error) {
6350276Speter		fail_errno("shm_open");
6450276Speter		return;
6550276Speter	}
6650276Speter	pass();
6750276Speter}
6850276Speter
6950276Speter/*
7050276Speter * Attempt a shm_unlink() that should fail with an expected error of 'error'.
7150276Speter */
7250276Speterstatic void
73166124Srafanshm_unlink_should_fail(const char *path, int error)
74166124Srafan{
75166124Srafan
76166124Srafan	if (shm_unlink(path) >= 0) {
77166124Srafan		fail_err("shm_unlink() didn't fail");
78166124Srafan		return;
79166124Srafan	}
80166124Srafan	if (errno != error) {
8150276Speter		fail_errno("shm_unlink");
8250276Speter		return;
8350276Speter	}
8450276Speter	pass();
8550276Speter}
8650276Speter
8750276Speter/*
8850276Speter * Open the test object and write '1' to the first byte.  Returns valid fd
8950276Speter * on success and -1 on failure.
9050276Speter */
9150276Speterstatic int
9250276Speterscribble_object(void)
9350276Speter{
94166124Srafan	char *page;
9550276Speter	int fd;
9650276Speter
9750276Speter	fd = shm_open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0777);
9850276Speter	if (fd < 0 && errno == EEXIST) {
9950276Speter		if (shm_unlink(TEST_PATH) < 0) {
10050276Speter			fail_errno("shm_unlink");
10150276Speter			return (-1);
10250276Speter		}
10350276Speter		fd = shm_open(TEST_PATH, O_CREAT | O_EXCL | O_RDWR, 0777);
10450276Speter	}
10550276Speter	if (fd < 0) {
10650276Speter		fail_errno("shm_open");
10750276Speter		return (-1);
10850276Speter	}
10950276Speter	if (ftruncate(fd, getpagesize()) < 0) {
11050276Speter		fail_errno("ftruncate");
11150276Speter		close(fd);
11250276Speter		shm_unlink(TEST_PATH);
11350276Speter		return (-1);
11450276Speter	}
11550276Speter
11650276Speter	page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
11750276Speter	    0);
11850276Speter	if (page == MAP_FAILED) {
11950276Speter		fail_errno("mmap");
12050276Speter		close(fd);
12150276Speter		shm_unlink(TEST_PATH);
12250276Speter		return (-1);
12350276Speter	}
12450276Speter
12550276Speter	page[0] = '1';
12650276Speter
12750276Speter	if (munmap(page, getpagesize()) < 0) {
12850276Speter		fail_errno("munmap");
12950276Speter		close(fd);
13050276Speter		shm_unlink(TEST_PATH);
13150276Speter		return (-1);
13250276Speter	}
13350276Speter
13466963Speter	return (fd);
13566963Speter}
13666963Speter
13766963Speterstatic void
13850276Speterremap_object(void)
13950276Speter{
14050276Speter	char *page;
14150276Speter	int fd;
14250276Speter
14350276Speter	fd = scribble_object();
14450276Speter	if (fd < 0)
14550276Speter		return;
14650276Speter
14750276Speter	page = mmap(0, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd,
14850276Speter	    0);
14950276Speter	if (page == MAP_FAILED) {
15050276Speter		fail_errno("mmap(2)");
15150276Speter		close(fd);
15256639Speter		shm_unlink(TEST_PATH);
15350276Speter		return;
15450276Speter	}
15550276Speter
15650276Speter	if (page[0] != '1') {
15750276Speter		fail_err("missing data");
15850276Speter		close(fd);
15950276Speter		shm_unlink(TEST_PATH);
16050276Speter		return;
16150276Speter	}
16250276Speter
16350276Speter	close(fd);
16450276Speter	if (munmap(page, getpagesize()) < 0) {
16550276Speter		fail_errno("munmap");
16650276Speter		shm_unlink(TEST_PATH);
16750276Speter		return;
16850276Speter	}
16950276Speter
17050276Speter	if (shm_unlink(TEST_PATH) < 0) {
17150276Speter		fail_errno("shm_unlink");
17250276Speter		return;
17350276Speter	}
17450276Speter
17550276Speter	pass();
17650276Speter}
17750276SpeterTEST(remap_object, "remap object");
17850276Speter
17950276Speterstatic void
18050276Speterreopen_object(void)
18150276Speter{
18250276Speter	char *page;
18350276Speter	int fd;
18450276Speter
18550276Speter	fd = scribble_object();
18650276Speter	if (fd < 0)
18750276Speter		return;
18850276Speter	close(fd);
18950276Speter
19050276Speter	fd = shm_open(TEST_PATH, O_RDONLY, 0777);
19150276Speter	if (fd < 0) {
19250276Speter		fail_errno("shm_open(2)");
19350276Speter		shm_unlink(TEST_PATH);
19450276Speter		return;
19550276Speter	}
19650276Speter	page = mmap(0, getpagesize(), PROT_READ, MAP_SHARED, fd, 0);
19750276Speter	if (page == MAP_FAILED) {
19850276Speter		fail_errno("mmap(2)");
19950276Speter		close(fd);
20050276Speter		shm_unlink(TEST_PATH);
20150276Speter		return;
20250276Speter	}
20350276Speter
20450276Speter	if (page[0] != '1') {
20550276Speter		fail_err("missing data");
20650276Speter		munmap(page, getpagesize());
20750276Speter		close(fd);
20850276Speter		shm_unlink(TEST_PATH);
20966963Speter		return;
21050276Speter	}
211
212	munmap(page, getpagesize());
213	close(fd);
214	shm_unlink(TEST_PATH);
215	pass();
216}
217TEST(reopen_object, "reopen object");
218
219static void
220readonly_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}
252TEST(readonly_mmap_write, "RDONLY object");
253
254static void
255open_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}
273TEST(open_after_unlink, "open after unlink");
274
275static void
276open_invalid_path(void)
277{
278
279	shm_open_should_fail("blah", O_RDONLY, 0777, EINVAL);
280}
281TEST(open_invalid_path, "open invalid path");
282
283static void
284open_write_only(void)
285{
286
287	shm_open_should_fail(TEST_PATH, O_WRONLY, 0777, EINVAL);
288}
289TEST(open_write_only, "open with O_WRONLY");
290
291static void
292open_extra_flags(void)
293{
294
295	shm_open_should_fail(TEST_PATH, O_RDONLY | O_DIRECT, 0777, EINVAL);
296}
297TEST(open_extra_flags, "open with extra flags");
298
299static void
300open_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}
312TEST(open_anon, "open anonymous object");
313
314static void
315open_anon_readonly(void)
316{
317
318	shm_open_should_fail(SHM_ANON, O_RDONLY, 0777, EINVAL);
319}
320TEST(open_anon_readonly, "open SHM_ANON with O_RDONLY");
321
322static void
323open_bad_path_pointer(void)
324{
325
326	shm_open_should_fail((char *)1024, O_RDONLY, 0777, EFAULT);
327}
328TEST(open_bad_path_pointer, "open bad path pointer");
329
330static void
331open_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}
341TEST(open_path_too_long, "open pathname too long");
342
343static void
344open_nonexisting_object(void)
345{
346
347	shm_open_should_fail("/notreallythere", O_RDONLY, 0777, ENOENT);
348}
349TEST(open_nonexisting_object, "open nonexistent object");
350
351static void
352exclusive_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}
368TEST(exclusive_create_existing_object, "O_EXCL of existing object");
369
370static void
371trunc_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}
422TEST(trunc_resets_object, "O_TRUNC resets size");
423
424static void
425unlink_bad_path_pointer(void)
426{
427
428	shm_unlink_should_fail((char *)1024, EFAULT);
429}
430TEST(unlink_bad_path_pointer, "unlink bad path pointer");
431
432static void
433unlink_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}
443TEST(unlink_path_too_long, "unlink pathname too long");
444
445static void
446test_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}
619TEST(test_object_resize, "object resize");
620
621int
622main(int argc, char *argv[])
623{
624
625	run_tests();
626	return (0);
627}
628