1 /*
2 * Copyright (C) 2020 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 <stdint.h>
18 #define TLOG_TAG "hwasan-test"
19
20 #include <memref.h>
21 #include <stdlib.h>
22 #include <trusty_unittest.h>
23
24 #include <lib/hwasan/hwasan_shadow.h>
25 #include <lk/compiler.h>
26 #include <sys/auxv.h>
27 #include <sys/mman.h>
28
29 #ifndef ARCH_ARM64
30 #error Trusty only supports HWAsan on arm64
31 #endif
32
33 #define PAGE_SIZE getauxval(AT_PAGESZ)
34
35 /* Magic number, only true for Aarch64 */
36 #define TAGGING_GRANULARITY 16
37
38 #define OK 0
39 #define ERR 1
40
41 static int hwasan_error = OK;
__hwasan_report_error(void)42 void __hwasan_report_error(void) {
43 WRITE_ONCE(hwasan_error, ERR);
44 }
45
46 /*
47 * This symbol is branched into when HWASan error is detected. Trusty HWASan
48 * runtime defines it as a weak symbol. Override it for testing.
49 */
hwasan_get_error(void)50 static int hwasan_get_error(void) {
51 int ret = READ_ONCE(hwasan_error);
52 WRITE_ONCE(hwasan_error, OK);
53 return ret;
54 }
55
56 /* To make sure variable isn't optimized away */
touch(volatile void * a)57 static void touch(volatile void* a) {
58 *((volatile uint8_t*)a) = *((volatile uint8_t*)a);
59 }
60
TEST(hwasan,hello_world)61 TEST(hwasan, hello_world) {
62 TLOGI("Hello World!\n");
63 ASSERT_EQ(hwasan_get_error(), OK);
64 test_abort:;
65 }
66
TEST(hwasan,stack_ok)67 TEST(hwasan, stack_ok) {
68 int a = 0;
69
70 WRITE_ONCE(a, 5);
71 ASSERT_EQ(hwasan_get_error(), OK);
72
73 ASSERT_EQ(READ_ONCE(a), 5);
74 ASSERT_EQ(hwasan_get_error(), OK);
75
76 test_abort:;
77 }
78
TEST(hwasan,stack_err)79 TEST(hwasan, stack_err) {
80 int a = 0;
81 int* b = (int*)hwasan_remove_ptr_tag((void*)(&a));
82
83 WRITE_ONCE(*b, 5);
84 ASSERT_EQ(hwasan_get_error(), ERR);
85
86 ASSERT_EQ(READ_ONCE(*b), 5);
87 ASSERT_EQ(hwasan_get_error(), ERR);
88
89 test_abort:;
90 }
91
TEST(hwasan,heap_ok)92 TEST(hwasan, heap_ok) {
93 int* a = malloc(sizeof(int));
94
95 WRITE_ONCE(*a, 5);
96 ASSERT_EQ(hwasan_get_error(), OK);
97
98 ASSERT_EQ(READ_ONCE(*a), 5);
99 ASSERT_EQ(hwasan_get_error(), OK);
100
101 test_abort:
102 free(a);
103 }
104
TEST(hwasan,heap_err)105 TEST(hwasan, heap_err) {
106 int* a = malloc(sizeof(int));
107 int* b = (int*)hwasan_remove_ptr_tag((void*)a);
108
109 WRITE_ONCE(*b, 5);
110 ASSERT_EQ(hwasan_get_error(), ERR);
111
112 ASSERT_EQ(READ_ONCE(*b), 5);
113 ASSERT_EQ(hwasan_get_error(), ERR);
114
115 test_abort:
116 free(a);
117 }
118
TEST(hwasan,heap_use_after_free)119 TEST(hwasan, heap_use_after_free) {
120 int* a = malloc(sizeof(int));
121
122 WRITE_ONCE(*a, 5);
123 ASSERT_EQ(*a, 5);
124
125 free(a);
126 ASSERT_EQ(READ_ONCE(*a), 5); /* heap use after free */
127 ASSERT_EQ(hwasan_get_error(), ERR);
128
129 test_abort:;
130 }
131
TEST(hwasan,memintrinsics_ok)132 TEST(hwasan, memintrinsics_ok) {
133 static const size_t size = TAGGING_GRANULARITY;
134 int8_t* a = malloc(size);
135
136 touch(a);
137 memset(a, 'a', size);
138 touch(a);
139 ASSERT_EQ(hwasan_get_error(), OK);
140
141 test_abort:
142 free(a);
143 }
144
TEST(hwasan,memintrinsics_err)145 TEST(hwasan, memintrinsics_err) {
146 static const size_t size = TAGGING_GRANULARITY;
147 int8_t* a = malloc(size);
148
149 touch(a);
150 memset(a, 'a', size + 1); /* heap buffer overflow */
151 touch(a);
152 ASSERT_EQ(hwasan_get_error(), ERR);
153
154 test_abort:
155 free(a);
156 }
157
TEST(hwasan,realloc_tag_new)158 TEST(hwasan, realloc_tag_new) {
159 static const size_t size = TAGGING_GRANULARITY;
160 int8_t* a = malloc(size);
161 int8_t* b = realloc(a, size + 1);
162 ASSERT_NE(b, NULL);
163
164 touch(b);
165 memset(b, 'b', size + 1);
166 touch(b);
167 ASSERT_EQ(hwasan_get_error(), OK);
168
169 test_abort:
170 free(b);
171 }
172
173 /* Test that realloc()'ing to new memory makes old memory inaccessible */
TEST(hwasan,realloc_untag_old)174 TEST(hwasan, realloc_untag_old) {
175 static const size_t old_size = TAGGING_GRANULARITY;
176 static const size_t new_size = 128 * TAGGING_GRANULARITY;
177 static const size_t num_tries = 100;
178
179 int8_t* a;
180 int8_t* unused;
181 int8_t* b;
182 void* a_notag;
183 void* b_notag;
184
185 /* Try getting a new memory location from realloc() */
186 for (size_t i = 0; i < num_tries; i++) {
187 a = malloc(old_size);
188 unused = malloc(old_size);
189 b = realloc(a, new_size); /* hopefully we get a new address */
190
191 a_notag = hwasan_remove_ptr_tag(a);
192 b_notag = hwasan_remove_ptr_tag(b);
193
194 if (a_notag != b_notag) {
195 break;
196 }
197
198 touch(unused);
199 touch(b);
200 free(unused);
201 free(b);
202 }
203
204 /* Might not be true, but better to not ignore this. */
205 ASSERT_NE(a_notag, b_notag);
206
207 /*
208 * "a" should have been freed and untagged by this point. Writing to &a
209 * may corrupt the heap, so avoid doing that.
210 */
211 WRITE_ONCE(*b, READ_ONCE(*a));
212 ASSERT_EQ(hwasan_get_error(), ERR);
213
214 test_abort:
215 free(unused);
216 free(b);
217 }
218
219 /* Test that realloc()'ing to same memory location retags that memory */
TEST(hwasan,realloc_retag)220 TEST(hwasan, realloc_retag) {
221 static const size_t num_tries = 100;
222
223 int8_t* a;
224 int8_t* b;
225 void* a_notag;
226 void* b_notag;
227
228 /* Try getting the same memory location from realloc() */
229 for (size_t i = 0; i < num_tries; i++) {
230 a = malloc(sizeof(*a));
231 b = realloc(a, sizeof(*a)); /* hopefully we get a new address */
232
233 a_notag = hwasan_remove_ptr_tag(a);
234 b_notag = hwasan_remove_ptr_tag(b);
235
236 if (a_notag == b_notag) {
237 break;
238 }
239
240 free(b);
241 }
242
243 /* Might not be true, but better to not ignore this. */
244 ASSERT_EQ(a_notag, b_notag);
245
246 /*
247 * Since there are no allocations between a and b, their tags can't collide
248 * (even if they point to same memory).
249 */
250 ASSERT_NE(a, b);
251
252 test_abort:
253 free(b);
254 }
255
TEST(hwasan,memalign)256 TEST(hwasan, memalign) {
257 int* a = memalign(PAGE_SIZE, PAGE_SIZE);
258 WRITE_ONCE(*a, 5);
259 ASSERT_EQ(READ_ONCE(*a), 5);
260 ASSERT_EQ(hwasan_get_error(), OK);
261
262 test_abort:
263 free(a);
264 }
265
TEST(hwasan,memref_create)266 TEST(hwasan, memref_create) {
267 void* a = memalign(PAGE_SIZE, PAGE_SIZE);
268 int handle = memref_create(a, PAGE_SIZE,
269 MMAP_FLAG_PROT_READ | MMAP_FLAG_PROT_WRITE);
270
271 ASSERT_GE(handle, 0);
272 ASSERT_EQ(hwasan_get_error(), OK);
273
274 test_abort:
275 close(handle);
276 free(a);
277 }
278
279 #define MEM_MAP_ID 1
280 #define MEM_MAP_ADDR 0x70000000U
281 #define MEM_MAP_SIZE 0x1000U
282
TEST(hwasan,mmap)283 TEST(hwasan, mmap) {
284 int ret;
285 struct dma_pmem pmem;
286 int* a;
287 uint32_t dma_flags = DMA_FLAG_FROM_DEVICE | DMA_FLAG_ALLOW_PARTIAL;
288
289 for (size_t i = 0; i < 10; i++) {
290 a = mmap(NULL, MEM_MAP_SIZE, PROT_READ | PROT_WRITE,
291 MMAP_FLAG_IO_HANDLE, MEM_MAP_ID, 0);
292 ASSERT_NE(a, MAP_FAILED);
293
294 ret = prepare_dma(a, MEM_MAP_SIZE, dma_flags, &pmem);
295 ASSERT_EQ(ret, 1);
296 ASSERT_EQ(pmem.paddr, MEM_MAP_ADDR);
297 ASSERT_EQ(pmem.size, MEM_MAP_SIZE);
298
299 touch(a);
300 finish_dma(a, MEM_MAP_SIZE, dma_flags);
301 munmap(a, MEM_MAP_SIZE);
302 }
303
304 return;
305
306 test_abort:
307 finish_dma(a, MEM_MAP_SIZE, dma_flags);
308 munmap(a, MEM_MAP_SIZE);
309 }
310
TEST(hwasan,mmap_ok)311 TEST(hwasan, mmap_ok) {
312 int* a = mmap(NULL, MEM_MAP_SIZE, PROT_READ | PROT_WRITE,
313 MMAP_FLAG_IO_HANDLE, MEM_MAP_ID, 0);
314 ASSERT_NE(a, MAP_FAILED);
315
316 WRITE_ONCE(*a, 5);
317 ASSERT_EQ(hwasan_get_error(), OK);
318
319 ASSERT_EQ(READ_ONCE(*a), 5);
320 ASSERT_EQ(hwasan_get_error(), OK);
321
322 test_abort:;
323 munmap(a, MEM_MAP_SIZE);
324 }
325
TEST(hwasan,mmap_err)326 TEST(hwasan, mmap_err) {
327 int* a = mmap(NULL, MEM_MAP_SIZE, PROT_READ | PROT_WRITE,
328 MMAP_FLAG_IO_HANDLE, MEM_MAP_ID, 0);
329 ASSERT_NE(a, MAP_FAILED);
330 int* b = (int*)hwasan_remove_ptr_tag((void*)a);
331
332 WRITE_ONCE(*b, 5);
333 ASSERT_EQ(hwasan_get_error(), ERR);
334
335 ASSERT_EQ(READ_ONCE(*b), 5);
336 ASSERT_EQ(hwasan_get_error(), ERR);
337
338 test_abort:;
339 munmap(a, MEM_MAP_SIZE);
340 }
341
342 static int bss_int;
343
TEST(hwasan,bss_ok)344 TEST(hwasan, bss_ok) {
345 WRITE_ONCE(bss_int, 5);
346 ASSERT_EQ(bss_int, 5);
347 ASSERT_EQ(hwasan_get_error(), OK);
348 test_abort:;
349 }
350
TEST(hwasan,bss_err)351 TEST(hwasan, bss_err) {
352 int* a = (int*)hwasan_remove_ptr_tag((void*)(&bss_int));
353 WRITE_ONCE(*a, 6);
354 ASSERT_EQ(READ_ONCE(*a), 6);
355
356 /* TODO(b/148877030): Sanitize globals */
357 ASSERT_EQ(hwasan_get_error(), OK);
358 test_abort:;
359 }
360
361 PORT_TEST(hwcrypto, "com.android.trusty.hwasan.user.test")
362