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 <paths.h>
23 #include <pthread.h>
24 #include <pwd.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32
33 // crashes if built with -fsanitize=address
test_crash_malloc()34 void test_crash_malloc() {
35 volatile char* heap = malloc(32);
36 heap[32] = heap[32];
37 printf("(HW)ASAN: Heap Test Failed\n");
38 }
39
40 // crashes if built with -fsanitize=address
test_crash_stack()41 void test_crash_stack() {
42 volatile char stack[32];
43 volatile char* p_stack = stack;
44 p_stack[32] = p_stack[32];
45 printf("(HW)ASAN: Stack Test Failed\n");
46 }
47
test_crash_pthread_mutex_unlock()48 void test_crash_pthread_mutex_unlock() {
49 volatile char* heap = malloc(32);
50 pthread_mutex_unlock((void*)&heap[32]);
51 printf("HWASAN: Libc Test Failed\n");
52 }
53
data_asan_exists()54 int data_asan_exists() {
55 int fd = open("/data/asan", O_DIRECTORY | O_PATH | O_CLOEXEC, 0);
56 if(fd < 0) {
57 printf("ASAN: Missing /data/asan\n");
58 return 1;
59 }
60 close(fd);
61 return 0;
62 }
63
64 // crashes if built with -fsanitize=memory
test_msan_crash_stack()65 void test_msan_crash_stack() {
66 volatile int stack[10];
67 stack[5] = 0;
68 if (stack[0]) {
69 stack[0] = 1;
70 }
71 printf("MSAN: Stack Test Failed\n");
72 }
73
74 // crashes if built with -fsanitize=integer
test_integer_overflow()75 void test_integer_overflow() {
76 size_t max = (size_t)-1;
77 max++;
78 printf("UBSAN: Integer Overflow Test Failed\n");
79 }
80
81 // returns 0 if kcov is enabled
test_kcov()82 int test_kcov() {
83 const char* kcov_file = "/sys/kernel/debug/kcov";
84 int fd = open(kcov_file, O_RDWR);
85 if (fd == -1) {
86 printf("KCOV: Could not open %s\n", kcov_file);
87 return 1;
88 }
89 close(fd);
90 return 0;
91 }
92
93 // returns 0 if kasan was compiled in
test_kasan()94 int test_kasan() {
95 // rely on the exit status of grep to propagate
96 if (system("gzip -d < /proc/config.gz | grep CONFIG_KASAN=y >/dev/null")) {
97 printf("KASAN: CONFIG_KASAN not in /proc/config.gz\n");
98 return 1;
99 }
100 return 0;
101 }
102
103 // Number of iterations required to reliably guarantee a GWP-ASan crash.
104 // GWP-ASan's sample rate is not truly nondeterministic, it initialises a
105 // thread-local counter at 2*SampleRate, and decrements on each malloc(). Once
106 // the counter reaches zero, we provide a sampled allocation. GWP-ASan's current
107 // default sample rate is 1/5000.
108 #define GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH (0x10000)
109
110 // crashes with GWP-ASan
test_crash_gwp_asan()111 void test_crash_gwp_asan() {
112 for (unsigned i = 0; i < GWP_ASAN_ITERATIONS_TO_ENSURE_CRASH; ++i ) {
113 volatile char *x = malloc(1);
114 free((void*) x);
115 *x = 0;
116 }
117 printf("GWP-ASan: Use after Free Failed\n");
118 }
119
120 // executes a test that is expected to crash
121 // returns 0 if the test crashes
test(void (* function)())122 int test(void (*function)()) {
123 fflush(stdout);
124
125 pid_t child = fork();
126 int status = 0;
127
128 if (child == -1) {
129 perror("fork");
130 exit(1);
131 }
132
133 if (child == 0) {
134 // Silence the ASAN report that is generated
135 close(2);
136
137 // Invoke the target function. If it does not crash, terminate the process.
138 function();
139 exit(EXIT_SUCCESS);
140 }
141
142 // Wait for the child to either crash, or exit cleanly
143 while (child == waitpid(child, &status, 0)) {
144 if (!WIFEXITED(status))
145 continue;
146 if (WEXITSTATUS(status) == EXIT_SUCCESS)
147 return 1;
148 break;
149 }
150 return 0;
151 }
152
have_option(const char * option,const char ** argv,const int argc)153 int have_option(const char* option, const char** argv, const int argc) {
154 for (int i = 1; i < argc; i++)
155 if (!strcmp(option, argv[i]))
156 return 1;
157 return 0;
158 }
159
sanitizer_status(int argc,const char ** argv)160 int sanitizer_status(int argc, const char** argv) {
161 int test_everything = 0;
162 int failures = 0;
163
164 if (argc <= 1)
165 test_everything = 1;
166
167 if (test_everything || have_option("asan", argv, argc)) {
168 int asan_failures = 0;
169
170 #if !defined(ANDROID_SANITIZE_ADDRESS)
171 asan_failures += 1;
172 printf("ASAN: Compiler flags failed!\n");
173 #endif
174
175 asan_failures += test(test_crash_malloc);
176 asan_failures += test(test_crash_stack);
177 asan_failures += data_asan_exists();
178
179 if (!asan_failures)
180 printf("ASAN: OK\n");
181
182 failures += asan_failures;
183 }
184
185 if (test_everything || have_option("hwasan", argv, argc)) {
186 int hwasan_failures = 0;
187
188 #if !defined(ANDROID_SANITIZE_HWADDRESS)
189 hwasan_failures += 1;
190 printf("HWASAN: Compiler flags failed!\n");
191 #endif
192
193 hwasan_failures += test(test_crash_malloc);
194 hwasan_failures += test(test_crash_stack);
195 hwasan_failures += test(test_crash_pthread_mutex_unlock);
196
197 if (!hwasan_failures)
198 printf("HWASAN: OK\n");
199
200 failures += hwasan_failures;
201 }
202
203 if(test_everything || have_option("cov", argv, argc)) {
204 int cov_failures = 0;
205
206 #ifndef ANDROID_SANITIZE_COVERAGE
207 printf("COV: Compiler flags failed!\n");
208 cov_failures += 1;
209 #endif
210
211 if (!cov_failures)
212 printf("COV: OK\n");
213
214 failures += cov_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 return failures > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
273 }
274