1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <errno.h>
18 #include <error.h>
19 #include <fcntl.h>
20 #include <getopt.h>
21 #include <limits.h>
22 #include <malloc.h>
23 #include <paths.h>
24 #include <pthread.h>
25 #include <pwd.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/prctl.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34
35 #include <bionic/mte.h>
36
37 // crashes if built with -fsanitize={address,hwaddress}
test_crash_malloc_overflow()38 void test_crash_malloc_overflow() {
39 volatile char* heap = reinterpret_cast<volatile char *>(malloc(32));
40 heap[32] = heap[32];
41 printf("Heap Overflow Test Failed\n");
42 }
43
44 // crashes if built with -fsanitize={address,hwaddresss}
test_crash_malloc_uaf()45 void test_crash_malloc_uaf() {
46 volatile char* heap = reinterpret_cast<volatile char *>(malloc(32));
47 free((void *)heap);
48 heap[0] = heap[0];
49 printf("Heap UAF Test Failed\n");
50 }
51
52 // crashes if built with -fsanitize=address
test_crash_stack()53 void test_crash_stack() {
54 volatile char stack[32];
55 volatile char* p_stack = stack;
56 p_stack[32] = p_stack[32];
57 printf("(HW)ASAN: Stack Test Failed\n");
58 }
59
test_crash_pthread_mutex_unlock()60 void test_crash_pthread_mutex_unlock() {
61 volatile char* heap = reinterpret_cast<volatile char *>(malloc(32));
62 pthread_mutex_unlock((pthread_mutex_t*)&heap[32]);
63 printf("HWASAN: Libc Test Failed\n");
64 }
65
data_asan_exists()66 int data_asan_exists() {
67 int fd = open("/data/asan", O_DIRECTORY | O_PATH | O_CLOEXEC, 0);
68 if(fd < 0) {
69 printf("ASAN: Missing /data/asan\n");
70 return 1;
71 }
72 close(fd);
73 return 0;
74 }
75
76 // crashes if built with -fsanitize=memory
test_msan_crash_stack()77 void test_msan_crash_stack() {
78 volatile int stack[10];
79 stack[5] = 0;
80 if (stack[0]) { // NOLINT
81 stack[0] = 1;
82 }
83 printf("MSAN: Stack Test Failed\n");
84 }
85
86 // crashes if built with -fsanitize=integer
test_integer_overflow()87 void test_integer_overflow() {
88 size_t max = (size_t)-1;
89 max++;
90 printf("UBSAN: Integer Overflow Test Failed\n");
91 }
92
93 // returns 0 if kcov is enabled
test_kcov()94 int test_kcov() {
95 const char* kcov_file = "/sys/kernel/debug/kcov";
96 int fd = open(kcov_file, O_RDWR);
97 if (fd == -1) {
98 printf("KCOV: Could not open %s\n", kcov_file);
99 return 1;
100 }
101 close(fd);
102 return 0;
103 }
104
105 // returns 0 if kasan was compiled in
test_kasan()106 int test_kasan() {
107 // rely on the exit status of grep to propagate
108 if (system("gzip -d < /proc/config.gz | grep CONFIG_KASAN=y >/dev/null")) {
109 printf("KASAN: CONFIG_KASAN not in /proc/config.gz\n");
110 return 1;
111 }
112 return 0;
113 }
114
115 // Number of iterations required to reliably guarantee a GWP-ASan crash.
116 // GWP-ASan's sample rate is not truly nondeterministic, it initialises a
117 // thread-local counter at 2*SampleRate, and decrements on each malloc(). Once
118 // the counter reaches zero, we provide a sampled allocation. GWP-ASan's current
119 // default sample rate is 1/5000.
120 #define GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH (0x10000)
121
122 // crashes with GWP-ASan
test_crash_gwp_asan()123 void test_crash_gwp_asan() {
124 for (unsigned i = 0; i < GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH; ++i ) {
125 volatile char* x = reinterpret_cast<volatile char *>(malloc(1));
126 free((void*) x);
127 *x = 0;
128 }
129 printf("GWP-ASan: Use after Free Failed\n");
130 }
131
132 // executes a test that is expected to crash
133 // returns 0 if the test crashes
test(void (* function)())134 int test(void (*function)()) {
135 fflush(stdout);
136
137 pid_t child = fork();
138 int status = 0;
139
140 if (child == -1) {
141 perror("fork");
142 exit(1);
143 }
144
145 if (child == 0) {
146 // Silence the ASAN report that is generated
147 close(2);
148
149 // Invoke the target function. If it does not crash, terminate the process.
150 function();
151 exit(EXIT_SUCCESS);
152 }
153
154 // Wait for the child to either crash, or exit cleanly
155 while (child == waitpid(child, &status, 0)) {
156 if (!WIFEXITED(status))
157 continue;
158 if (WEXITSTATUS(status) == EXIT_SUCCESS)
159 return 1;
160 break;
161 }
162 return 0;
163 }
164
have_option(const char * option,const char ** argv,const int argc)165 int have_option(const char* option, const char** argv, const int argc) {
166 for (int i = 1; i < argc; i++)
167 if (!strcmp(option, argv[i]))
168 return 1;
169 return 0;
170 }
171
main(int argc,const char ** argv)172 int main(int argc, const char** argv) {
173 int test_everything = 0;
174 int failures = 0;
175
176 if (argc <= 1)
177 test_everything = 1;
178
179 if (test_everything || have_option("asan", argv, argc)) {
180 int asan_failures = 0;
181
182 #if !__has_feature(address_sanitizer)
183 asan_failures += 1;
184 printf("ASAN: Compiler flags failed!\n");
185 #endif
186
187 asan_failures += test(test_crash_malloc_overflow);
188 asan_failures += test(test_crash_malloc_uaf);
189 asan_failures += test(test_crash_stack);
190 asan_failures += data_asan_exists();
191
192 if (!asan_failures)
193 printf("ASAN: OK\n");
194
195 failures += asan_failures;
196 }
197
198 if (test_everything || have_option("hwasan", argv, argc)) {
199 int hwasan_failures = 0;
200
201 #if !__has_feature(hwaddress_sanitizer)
202 hwasan_failures += 1;
203 printf("HWASAN: Compiler flags failed!\n");
204 #endif
205
206 hwasan_failures += test(test_crash_malloc_overflow);
207 hwasan_failures += test(test_crash_malloc_uaf);
208 hwasan_failures += test(test_crash_stack);
209 hwasan_failures += test(test_crash_pthread_mutex_unlock);
210
211 if (!hwasan_failures)
212 printf("HWASAN: OK\n");
213
214 failures += hwasan_failures;
215 }
216
217 if (test_everything || have_option("msan", argv, argc)) {
218 int msan_failures = 0;
219
220 msan_failures += test(test_msan_crash_stack);
221
222 if (!msan_failures)
223 printf("MSAN: OK\n");
224
225 failures += msan_failures;
226 }
227
228 if (test_everything || have_option("kasan", argv, argc)) {
229 int kasan_failures = 0;
230
231 kasan_failures += test_kasan();
232
233 if(!kasan_failures)
234 printf("KASAN: OK\n");
235
236 failures += kasan_failures;
237 }
238
239 if (test_everything || have_option("kcov", argv, argc)) {
240 int kcov_failures = 0;
241
242 kcov_failures += test_kcov();
243
244 if (!kcov_failures)
245 printf("KCOV: OK\n");
246
247 failures += kcov_failures;
248 }
249
250 if (test_everything || have_option("ubsan", argv, argc)) {
251 int ubsan_failures = 0;
252
253 ubsan_failures += test(test_integer_overflow);
254
255 if (!ubsan_failures)
256 printf("UBSAN: OK\n");
257
258 failures += ubsan_failures;
259 }
260
261 if (test_everything || have_option("gwp_asan", argv, argc)) {
262 int gwp_asan_failures = 0;
263
264 gwp_asan_failures += test(test_crash_gwp_asan);
265
266 if (!gwp_asan_failures)
267 printf("GWP-ASan: OK\n");
268
269 failures += gwp_asan_failures;
270 }
271
272 if (test_everything || have_option("mte", argv, argc)) {
273 int mte_failures = 0;
274
275 if (!(mte_supported() && !__has_feature(address_sanitizer) &&
276 !__has_feature(hwaddress_sanitizer))) {
277 mte_failures += 1;
278 printf("MTE: Not supported\n");
279 }
280
281 mte_failures += test(test_crash_malloc_overflow);
282 mte_failures += test(test_crash_malloc_uaf);
283
284 int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
285 if (tagged_addr_ctrl < 0) {
286 mte_failures += 1;
287 printf("MTE: PR_GET_TAGGED_ADDR_CTRL failed\n");
288 }
289
290 HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
291 if (mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level) == 0) {
292 mte_failures += 1;
293 printf("MTE: mallopt failed\n");
294 }
295
296 mte_failures += test(test_crash_malloc_overflow);
297 mte_failures += test(test_crash_malloc_uaf);
298
299 if (!mte_failures)
300 printf("MTE: OK\n");
301
302 failures += mte_failures;
303 }
304
305 return failures > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
306 }
307