xref: /aosp_15_r20/tools/security/sanitizer-status/sanitizer-status.cpp (revision d9ecfb0f4d734c9ce41cde8ac4d585b094fd4222)
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 char global[32] = {};
38 
39 // crashes if built with -fsanitize={address,hwaddress}
test_crash_malloc_overflow()40 void test_crash_malloc_overflow() {
41   volatile char* heap = reinterpret_cast<volatile char *>(malloc(32));
42   heap[32] = heap[32];
43   printf("Heap Overflow Test Failed\n");
44 }
45 
46 // crashes if built with -fsanitize={address,hwaddresss}
test_crash_malloc_uaf()47 void test_crash_malloc_uaf() {
48   volatile char* heap = reinterpret_cast<volatile char *>(malloc(32));
49   free((void *)heap);
50   heap[0] = heap[0];
51   printf("Heap UAF Test Failed\n");
52 }
53 
54 // crashes if built with -fsanitize={address,hwaddress,memtag-stack}
test_crash_stack()55 void test_crash_stack() {
56   volatile char stack[32];
57   volatile char* p_stack = stack;
58   p_stack[32] = p_stack[32];
59   printf("(HW)ASAN / Stack MTE: Stack Test Failed\n");
60 }
61 
62 // crashes if built with -fsanitize={address,hwaddress,memtag-globals}
test_crash_globals()63 void test_crash_globals() {
64   volatile char* p_global = global;
65   p_global[32] = p_global[32];
66   printf("(HW)ASAN / Globals MTE: Globals Test Failed\n");
67 }
68 
test_crash_pthread_mutex_unlock()69 void test_crash_pthread_mutex_unlock() {
70   volatile char* heap = reinterpret_cast<volatile char *>(malloc(32));
71   pthread_mutex_unlock((pthread_mutex_t*)&heap[32]);
72   printf("HWASAN: Libc Test Failed\n");
73 }
74 
data_asan_exists()75 int data_asan_exists() {
76   int fd = open("/data/asan", O_DIRECTORY | O_PATH | O_CLOEXEC, 0);
77   if(fd < 0) {
78     printf("ASAN: Missing /data/asan\n");
79     return 1;
80   }
81   close(fd);
82   return 0;
83 }
84 
85 // crashes if built with -fsanitize=memory
test_msan_crash_stack()86 void test_msan_crash_stack() {
87   volatile int stack[10];
88   stack[5] = 0;
89   if (stack[0]) { // NOLINT
90     stack[0] = 1;
91   }
92   printf("MSAN: Stack Test Failed\n");
93 }
94 
95 // crashes if built with -fsanitize=integer
test_integer_overflow()96 void test_integer_overflow() {
97   size_t max = (size_t)-1;
98   max++;
99   printf("UBSAN: Integer Overflow Test Failed\n");
100 }
101 
102 // returns 0 if kcov is enabled
test_kcov()103 int test_kcov() {
104   const char* kcov_file = "/sys/kernel/debug/kcov";
105   int fd = open(kcov_file, O_RDWR);
106   if (fd == -1) {
107     printf("KCOV: Could not open %s\n", kcov_file);
108     return 1;
109   }
110   close(fd);
111   return 0;
112 }
113 
114 // returns 0 if kasan was compiled in
test_kasan()115 int test_kasan() {
116   // rely on the exit status of grep to propagate
117   if (system("gzip -d < /proc/config.gz | grep CONFIG_KASAN=y >/dev/null")) {
118     printf("KASAN: CONFIG_KASAN not in /proc/config.gz\n");
119     return 1;
120   }
121   return 0;
122 }
123 
124 // Number of iterations required to reliably guarantee a GWP-ASan crash.
125 // GWP-ASan's sample rate is not truly nondeterministic, it initialises a
126 // thread-local counter at 2*SampleRate, and decrements on each malloc(). Once
127 // the counter reaches zero, we provide a sampled allocation. GWP-ASan's current
128 // default sample rate is 1/5000.
129 #define GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH (0x10000)
130 
131 // crashes with GWP-ASan
test_crash_gwp_asan()132 void test_crash_gwp_asan() {
133   for (unsigned i = 0; i < GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH; ++i ) {
134     volatile char* x = reinterpret_cast<volatile char *>(malloc(1));
135     free((void*) x);
136     *x = 0;
137   }
138   printf("GWP-ASan: Use after Free Failed\n");
139 }
140 
141 // executes a test that is expected to crash
142 // returns 0 if the test crashes
test(void (* function)())143 int test(void (*function)()) {
144   fflush(stdout);
145 
146   pid_t child = fork();
147   int status = 0;
148 
149   if (child == -1) {
150     perror("fork");
151     exit(1);
152   }
153 
154   if (child == 0) {
155     // Silence the ASAN report that is generated
156     close(2);
157 
158     // Invoke the target function.  If it does not crash, terminate the process.
159     function();
160     exit(EXIT_SUCCESS);
161   }
162 
163   // Wait for the child to either crash, or exit cleanly
164   while (child == waitpid(child, &status, 0)) {
165     if (!WIFEXITED(status))
166       continue;
167     if (WEXITSTATUS(status) == EXIT_SUCCESS)
168       return 1;
169     break;
170   }
171   return 0;
172 }
173 
have_option(const char * option,const char ** argv,const int argc)174 int have_option(const char* option, const char** argv, const int argc) {
175   for (int i = 1; i < argc; i++)
176     if (!strcmp(option, argv[i]))
177       return 1;
178   return 0;
179 }
180 
main(int argc,const char ** argv)181 int main(int argc, const char** argv) {
182   int test_everything = 0;
183   int failures = 0;
184 
185   if (argc <= 1)
186     test_everything = 1;
187 
188   if (test_everything || have_option("asan", argv, argc)) {
189     int asan_failures = 0;
190 
191 #if !__has_feature(address_sanitizer)
192     asan_failures += 1;
193     printf("ASAN: Compiler flags failed!\n");
194 #endif
195 
196     asan_failures += test(test_crash_malloc_overflow);
197     asan_failures += test(test_crash_malloc_uaf);
198     asan_failures += test(test_crash_stack);
199     asan_failures += data_asan_exists();
200 
201     if (!asan_failures)
202       printf("ASAN: OK\n");
203 
204     failures += asan_failures;
205   }
206 
207   if (test_everything || have_option("hwasan", argv, argc)) {
208     int hwasan_failures = 0;
209 
210 #if !__has_feature(hwaddress_sanitizer)
211     hwasan_failures += 1;
212     printf("HWASAN: Compiler flags failed!\n");
213 #endif
214 
215     hwasan_failures += test(test_crash_malloc_overflow);
216     hwasan_failures += test(test_crash_malloc_uaf);
217     hwasan_failures += test(test_crash_stack);
218     hwasan_failures += test(test_crash_pthread_mutex_unlock);
219     hwasan_failures += test(test_crash_globals);
220 
221     if (!hwasan_failures)
222       printf("HWASAN: OK\n");
223 
224     failures += hwasan_failures;
225   }
226 
227   if (test_everything || have_option("msan", argv, argc)) {
228     int msan_failures = 0;
229 
230     msan_failures += test(test_msan_crash_stack);
231 
232     if (!msan_failures)
233       printf("MSAN: OK\n");
234 
235     failures += msan_failures;
236   }
237 
238   if (test_everything || have_option("kasan", argv, argc)) {
239     int kasan_failures = 0;
240 
241     kasan_failures += test_kasan();
242 
243     if(!kasan_failures)
244       printf("KASAN: OK\n");
245 
246     failures += kasan_failures;
247   }
248 
249   if (test_everything || have_option("kcov", argv, argc)) {
250     int kcov_failures = 0;
251 
252     kcov_failures += test_kcov();
253 
254     if (!kcov_failures)
255       printf("KCOV: OK\n");
256 
257     failures += kcov_failures;
258   }
259 
260   if (test_everything || have_option("ubsan", argv, argc)) {
261     int ubsan_failures = 0;
262 
263     ubsan_failures += test(test_integer_overflow);
264 
265     if (!ubsan_failures)
266       printf("UBSAN: OK\n");
267 
268     failures += ubsan_failures;
269   }
270 
271   if (test_everything || have_option("gwp_asan", argv, argc)) {
272     int gwp_asan_failures = 0;
273 
274     gwp_asan_failures += test(test_crash_gwp_asan);
275 
276     if (!gwp_asan_failures)
277       printf("GWP-ASan: OK\n");
278 
279     failures += gwp_asan_failures;
280   }
281 
282   if (test_everything || have_option("mte", argv, argc)) {
283     int mte_failures = 0;
284 
285     if (!(mte_supported() && !__has_feature(address_sanitizer) &&
286           !__has_feature(hwaddress_sanitizer))) {
287       mte_failures += 1;
288       printf("MTE: Not supported\n");
289     }
290 
291     mte_failures += test(test_crash_malloc_overflow);
292     mte_failures += test(test_crash_malloc_uaf);
293 
294     int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
295     if (tagged_addr_ctrl < 0) {
296       mte_failures += 1;
297       printf("MTE: PR_GET_TAGGED_ADDR_CTRL failed\n");
298     }
299 
300     HeapTaggingLevel heap_tagging_level = M_HEAP_TAGGING_LEVEL_SYNC;
301     if (mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, heap_tagging_level) == 0) {
302       mte_failures += 1;
303       printf("MTE: mallopt failed\n");
304     }
305 
306     mte_failures += test(test_crash_malloc_overflow);
307     mte_failures += test(test_crash_malloc_uaf);
308 
309     if (!mte_failures)
310       printf("MTE: OK\n");
311 
312     failures += mte_failures;
313   }
314 
315   if (test_everything || have_option("stack_mte", argv, argc)) {
316     int stack_mte_failures = 0;
317 
318     if (!(mte_supported() && !__has_feature(address_sanitizer) &&
319           !__has_feature(hwaddress_sanitizer))) {
320       stack_mte_failures += 1;
321       printf("MTE: Not supported\n");
322     }
323 
324     stack_mte_failures += test(test_crash_stack);
325 
326     if (!stack_mte_failures)
327       printf("Stack MTE: OK\n");
328 
329     failures += stack_mte_failures;
330   }
331 
332   if (test_everything || have_option("globals_mte", argv, argc)) {
333     int globals_mte_failures = 0;
334 
335     if (!(mte_supported() && !__has_feature(address_sanitizer) &&
336           !__has_feature(hwaddress_sanitizer))) {
337       globals_mte_failures += 1;
338       printf("MTE: Not supported\n");
339     }
340 
341     globals_mte_failures += test(test_crash_globals);
342 
343     if (!globals_mte_failures)
344       printf("Globals MTE: OK\n");
345 
346     failures += globals_mte_failures;
347   }
348 
349   return failures > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
350 }
351