1/*
2 * Copyright 2023, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <fcntl.h>
10#include <sys/mman.h>
11#include <unistd.h>
12#include <OS.h>
13
14
15int gTestFd = -1;
16
17
18int
19map_negative_offset_test()
20{
21	// should fail (negative offset)
22	void* ptr = mmap(NULL, B_PAGE_SIZE, PROT_READ, MAP_PRIVATE, gTestFd, -4096);
23	if (ptr != MAP_FAILED) {
24		printf("map-negative-offset unexpectedly succeeded!\n");
25		return -1;
26	}
27	return 0;
28}
29
30
31int
32map_cut_compare_test()
33{
34	uint8* ptr1 = (uint8*)mmap(NULL, 16 * B_PAGE_SIZE, PROT_READ, MAP_PRIVATE, gTestFd, 0);
35	uint8 chunk[128];
36	memcpy(chunk, &ptr1[3 * B_PAGE_SIZE], sizeof(chunk));
37
38	// now cut the area
39	uint8* ptr2 = (uint8*)mmap(&ptr1[B_PAGE_SIZE], B_PAGE_SIZE,
40		PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0);
41
42	// validate that the area after the cut still has the expected data
43	int status = memcmp(&ptr1[3 * B_PAGE_SIZE], chunk, sizeof(chunk));
44	if (status != 0) {
45		printf("map-cut-compare test failed!\n");
46		return status;
47	}
48	return 0;
49}
50
51
52int
53map_protect_cut_test()
54{
55	uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE,
56		MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
57
58	// make the tail accessible
59	mprotect(ptr + B_PAGE_SIZE * 3, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
60
61	// store any value
62	ptr[B_PAGE_SIZE * 3] = 'a';
63
64	// cut the area in the middle, before the accessible tail
65	mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_READ | PROT_WRITE,
66		MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
67
68	// validate that this does not crash
69	if (ptr[B_PAGE_SIZE * 3] != 'a') {
70		printf("map-protect-cut test failed!\n");
71		return -1;
72	}
73	return 0;
74}
75
76
77int
78map_cut_fork_test()
79{
80	char name[24];
81	sprintf(name, "/shm-mmap-cut-fork-test-%d", getpid());
82	name[sizeof(name) - 1] = '\0';
83	shm_unlink(name);
84	int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
85	shm_unlink(name);
86
87	if (fd < 0) {
88		printf("failed to create temporary file!\n");
89		return fd;
90	}
91
92	ftruncate(fd, B_PAGE_SIZE * 4);
93
94	uint8* ptr = (uint8*)mmap(NULL, B_PAGE_SIZE * 4, PROT_NONE, MAP_PRIVATE,
95		fd, 0);
96
97	// make the head accessible and also force the kernel to allocate the
98	// page_protections array
99	mprotect(ptr, B_PAGE_SIZE, PROT_READ | PROT_WRITE);
100
101	// store any value
102	ptr[0] = 'a';
103
104	// cut the area in the middle
105	mmap(ptr + B_PAGE_SIZE, B_PAGE_SIZE, PROT_NONE,
106		MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
107
108	// validate that the fork does not crash the kernel
109	int pid = fork();
110
111	if (pid == 0)
112	{
113		exit(0);
114	}
115	else if (pid < 0)
116	{
117		printf("failed to fork the test process!\n");
118		return pid;
119	}
120
121	int status;
122	waitpid(pid, &status, 0);
123
124	// validate that this does not crash
125	if (ptr[0] != 'a') {
126		printf("map-cut-fork test failed!\n");
127		return -1;
128	}
129	return 0;
130}
131
132
133int
134main()
135{
136	gTestFd = open("/boot/system/lib/libroot.so", O_CLOEXEC | O_RDONLY);
137	if (gTestFd < 0)
138		return -1;
139
140	int status;
141
142	if ((status = map_negative_offset_test()) != 0)
143		return status;
144
145	if ((status = map_cut_compare_test()) != 0)
146		return status;
147
148	if ((status = map_protect_cut_test()) != 0)
149		return status;
150
151	if ((status = map_cut_fork_test()) != 0)
152		return status;
153
154	return 0;
155}
156