xref: /xnu-11215/tests/vm/vm_user.c (revision 8d741a5d)
1 #include <darwintest.h>
2 #include <darwintest_utils.h>
3 
4 #include <sys/types.h>
5 #include <sys/sysctl.h>
6 #include <mach/mach.h>
7 #include <mach/mach_vm.h>
8 #include <mach/vm_types.h>
9 #include <sys/mman.h>
10 #include <unistd.h>
11 #include <TargetConditionals.h>
12 
13 T_GLOBAL_META(
14 	T_META_NAMESPACE("xnu.vm"),
15 	T_META_RADAR_COMPONENT_NAME("xnu"),
16 	T_META_RADAR_COMPONENT_VERSION("VM")
17 	);
18 
19 struct child_rc {
20 	int ret;
21 	int sig;
22 };
23 
24 static struct child_rc
25 fork_child_test(void (^block)(void))
26 {
27 	struct child_rc rc = { };
28 	pid_t child_pid;
29 
30 	child_pid = fork();
31 
32 	if (child_pid == 0) {
33 		block();
34 		exit(0);
35 	}
36 
37 	T_QUIET; T_ASSERT_POSIX_SUCCESS(child_pid, "fork process");
38 
39 	/* wait for child process to exit */
40 	dt_waitpid(child_pid, &rc.ret, &rc.sig, 30);
41 	return rc;
42 }
43 
44 static mach_vm_address_t
get_permanent_mapping(mach_vm_size_t size)45 get_permanent_mapping(mach_vm_size_t size)
46 {
47 	kern_return_t kr;
48 	mach_vm_address_t addr;
49 
50 	kr = mach_vm_allocate(mach_task_self(), &addr, size,
51 	    VM_FLAGS_ANYWHERE | VM_FLAGS_PERMANENT);
52 
53 	T_ASSERT_MACH_SUCCESS(kr, "mach_vm_allocate(%lld, PERMANENT) == %p",
54 	    size, (void *)addr);
55 
56 	*(int *)addr = 42;
57 
58 	kr = mach_vm_protect(mach_task_self(), addr, size, FALSE, VM_PROT_READ);
59 
60 	T_EXPECT_MACH_SUCCESS(kr, "mach_vm_protect(PERMANENT, READ)");
61 
62 	T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
63 
64 	return addr;
65 }
66 
67 T_DECL(permanent_mapping, "check permanent mappings semantics", T_META_TAG_VM_PREFERRED)
68 {
69 	kern_return_t kr;
70 	mach_vm_size_t size = 1 << 20;
71 	struct child_rc rc;
72 
73 	T_LOG("try to bypass permanent mappings with VM_FLAGS_OVERWRITE");
74 	rc = fork_child_test(^{
75 		mach_vm_address_t addr, addr2;
76 		kern_return_t kr2;
77 
78 		addr = get_permanent_mapping(size);
79 
80 		T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
81 
82 		addr2 = addr;
83 		kr2 = mach_vm_allocate(mach_task_self(), &addr2, size,
84 		VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE);
85 
86 		/*
87 		 * because the permanent mapping wasn't removed,
88 		 * we should get an error.
89 		 */
90 		T_ASSERT_MACH_ERROR(kr2, KERN_NO_SPACE,
91 		"mach_vm_allocate(VM_FLAGS_OVERWRITE)");
92 
93 		/*
94 		 * because the permanent mapping was neutered,
95 		 * accessing it should crash.
96 		 */
97 		T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
98 	});
99 	T_EXPECT_EQ(rc.sig, SIGBUS, "accessing the mapping caused a SIGBUS");
100 
101 	T_LOG("try to bypass permanent mappings with a VM_PROT_COPY mprotect");
102 	rc = fork_child_test(^{
103 		kern_return_t kr2;
104 		mach_vm_address_t addr;
105 
106 		addr = get_permanent_mapping(size);
107 
108 		T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
109 
110 		kr2 = mach_vm_protect(mach_task_self(), addr, size, TRUE,
111 		VM_PROT_COPY | VM_PROT_DEFAULT);
112 
113 		/*
114 		 * because the permanent mapping wasn't removed,
115 		 * we should get an error.
116 		 */
117 		T_ASSERT_MACH_ERROR(kr2, KERN_NO_SPACE,
118 		"mach_vm_protect(VM_PROT_COPY)");
119 
120 		/*
121 		 * because the permanent mapping was neutered,
122 		 * accessing it should crash.
123 		 */
124 		T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
125 	});
126 	T_EXPECT_EQ(rc.sig, SIGBUS, "accessing the mapping caused a SIGBUS");
127 
128 	T_LOG("try to bypass permanent mappings with a vm_remap");
129 	rc = fork_child_test(^{
130 		kern_return_t kr2;
131 		mach_vm_address_t addr, remap_addr, addr2;
132 		vm_prot_t cur_prot, max_prot;
133 
134 		addr = get_permanent_mapping(size);
135 
136 		T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
137 
138 		addr2 = 0;
139 		kr2 = mach_vm_allocate(mach_task_self(), &addr2, size,
140 		VM_FLAGS_ANYWHERE);
141 		T_QUIET; T_EXPECT_MACH_SUCCESS(kr2, "vm_allocate()");
142 
143 		remap_addr = addr;
144 		kr2 = mach_vm_remap(mach_task_self(), &remap_addr, size, 0,
145 		VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
146 		mach_task_self(), addr2, TRUE,
147 		&cur_prot, &max_prot, VM_INHERIT_DEFAULT);
148 
149 		/*
150 		 * because the permanent mapping wasn't removed,
151 		 * we should get an error.
152 		 */
153 		T_ASSERT_MACH_ERROR(kr2, KERN_NO_SPACE,
154 		"mach_vm_remap()");
155 
156 		/*
157 		 * because the permanent mapping was neutered,
158 		 * accessing it should crash.
159 		 */
160 		T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
161 	});
162 	T_EXPECT_EQ(rc.sig, SIGBUS, "accessing the mapping caused a SIGBUS");
163 
164 	T_LOG("try to bypass permanent mappings with a vm_deallocate");
165 	rc = fork_child_test(^{
166 		kern_return_t kr2;
167 		mach_vm_address_t addr;
168 
169 		addr = get_permanent_mapping(size);
170 
171 		T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
172 
173 		kr2 = mach_vm_deallocate(mach_task_self(), addr, size);
174 
175 		/*
176 		 * the permanent mapping wasn't removed but was made
177 		 * inaccessible; we should not get an error.
178 		 */
179 		T_ASSERT_MACH_SUCCESS(kr2, "mach_vm_deallocate()");
180 
181 		/*
182 		 * because the permanent mapping was neutered,
183 		 * accessing it should crash.
184 		 */
185 		T_QUIET; T_EXPECT_EQ(*(int *)addr, 42, "we can still read what we wrote");
186 	});
187 	T_EXPECT_EQ(rc.sig, SIGBUS, "accessing the mapping caused a SIGBUS");
188 }
189