blob: 9493b3b51ac7e90eccb4eeb76751514218c2055a [file] [log] [blame]
Frans Hendriksf2760792021-01-29 08:55:01 +01001/* SPDX-License-Identifier: GPL-2.0-only */
Harshit Sharma2bcaba02020-06-09 20:25:16 -07002
Harshit Sharma9c88fb82020-06-17 20:19:00 -07003/*
4 * Address sanitizer support.
5 *
6 * Parts of this file are based on mm/kasan
7 * from the Linux kernel 4.19.137.
8 *
9 */
Harshit Sharma2bcaba02020-06-09 20:25:16 -070010
Elyes HAOUAS27718ac2020-09-19 09:32:36 +020011#include <console/console.h>
Harshit Sharma9c88fb82020-06-17 20:19:00 -070012#include <symbols.h>
13#include <assert.h>
Harshit Sharmaa6ebe082020-07-20 00:21:05 -070014#include <arch/symbols.h>
Harshit Sharma9c88fb82020-06-17 20:19:00 -070015#include <asan.h>
16
17static inline void *asan_mem_to_shadow(const void *addr)
18{
Harshit Sharmaa6ebe082020-07-20 00:21:05 -070019#if ENV_ROMSTAGE
20 return (void *)((uintptr_t)&_asan_shadow + (((uintptr_t)addr -
21 (uintptr_t)&_car_region_start) >> ASAN_SHADOW_SCALE_SHIFT));
22#elif ENV_RAMSTAGE
Harshit Sharma9c88fb82020-06-17 20:19:00 -070023 return (void *)((uintptr_t)&_asan_shadow + (((uintptr_t)addr -
24 (uintptr_t)&_data) >> ASAN_SHADOW_SCALE_SHIFT));
Harshit Sharmaa6ebe082020-07-20 00:21:05 -070025#endif
Harshit Sharma9c88fb82020-06-17 20:19:00 -070026}
27
28static inline const void *asan_shadow_to_mem(const void *shadow_addr)
29{
Harshit Sharmaa6ebe082020-07-20 00:21:05 -070030#if ENV_ROMSTAGE
31 return (void *)((uintptr_t)&_car_region_start + (((uintptr_t)shadow_addr -
32 (uintptr_t)&_asan_shadow) << ASAN_SHADOW_SCALE_SHIFT));
33#elif ENV_RAMSTAGE
Harshit Sharma9c88fb82020-06-17 20:19:00 -070034 return (void *)((uintptr_t)&_data + (((uintptr_t)shadow_addr -
35 (uintptr_t)&_asan_shadow) << ASAN_SHADOW_SCALE_SHIFT));
Harshit Sharmaa6ebe082020-07-20 00:21:05 -070036#endif
Harshit Sharma9c88fb82020-06-17 20:19:00 -070037}
38
39static void asan_poison_shadow(const void *address, size_t size, u8 value)
40{
41 void *shadow_start, *shadow_end;
42
43 shadow_start = asan_mem_to_shadow(address);
44 shadow_end = asan_mem_to_shadow(address + size);
45
46 __builtin_memset(shadow_start, value, shadow_end - shadow_start);
47}
48
49void asan_unpoison_shadow(const void *address, size_t size)
50{
51 asan_poison_shadow(address, size, 0);
52
53 if (size & ASAN_SHADOW_MASK) {
54 u8 *shadow = (u8 *)asan_mem_to_shadow(address + size);
55 *shadow = size & ASAN_SHADOW_MASK;
56 }
57}
58
59static __always_inline bool memory_is_poisoned_1(unsigned long addr)
60{
61 s8 shadow_value = *(s8 *)asan_mem_to_shadow((void *)addr);
62
63 if (unlikely(shadow_value)) {
64 s8 last_accessible_byte = addr & ASAN_SHADOW_MASK;
65 return unlikely(last_accessible_byte >= shadow_value);
66 }
67
68 return false;
69}
70
71static __always_inline bool memory_is_poisoned_2_4_8(unsigned long addr,
72 unsigned long size)
73{
74 u8 *shadow_addr = (u8 *)asan_mem_to_shadow((void *)addr);
75
76 if (unlikely(((addr + size - 1) & ASAN_SHADOW_MASK) < size - 1))
77 return *shadow_addr || memory_is_poisoned_1(addr + size - 1);
78
79 return memory_is_poisoned_1(addr + size - 1);
80}
81
82static __always_inline bool memory_is_poisoned_16(unsigned long addr)
83{
84 u16 *shadow_addr = (u16 *)asan_mem_to_shadow((void *)addr);
85
86 if (unlikely(!IS_ALIGNED(addr, ASAN_SHADOW_SCALE_SIZE)))
87 return *shadow_addr || memory_is_poisoned_1(addr + 15);
88
89 return *shadow_addr;
90}
91
92static __always_inline unsigned long bytes_is_nonzero(const u8 *start,
93 size_t size)
94{
95 while (size) {
96 if (unlikely(*start))
97 return (unsigned long)start;
98 start++;
99 size--;
100 }
101
102 return 0;
103}
104
105static __always_inline unsigned long memory_is_nonzero(const void *start,
106 const void *end)
107{
108 unsigned int words;
109 unsigned long ret;
110 unsigned int prefix = (unsigned long)start % 8;
111
112 if (end - start <= 16)
113 return bytes_is_nonzero(start, end - start);
114
115 if (prefix) {
116 prefix = 8 - prefix;
117 ret = bytes_is_nonzero(start, prefix);
118 if (unlikely(ret))
119 return ret;
120 start += prefix;
121 }
122
123 words = (end - start) / 8;
124 while (words) {
125 if (unlikely(*(u64 *)start))
126 return bytes_is_nonzero(start, 8);
127 start += 8;
128 words--;
129 }
130
131 return bytes_is_nonzero(start, (end - start) % 8);
132}
133
134static __always_inline bool memory_is_poisoned_n(unsigned long addr,
135 size_t size)
136{
137 unsigned long ret;
138
139 ret = memory_is_nonzero(asan_mem_to_shadow((void *)addr),
140 asan_mem_to_shadow((void *)addr + size - 1) + 1);
141
142 if (unlikely(ret)) {
143 unsigned long last_byte = addr + size - 1;
144 s8 *last_shadow = (s8 *)asan_mem_to_shadow((void *)last_byte);
145
146 if (unlikely(ret != (unsigned long)last_shadow ||
147 ((long)(last_byte & ASAN_SHADOW_MASK) >= *last_shadow)))
148 return true;
149 }
150 return false;
151}
152
153static __always_inline bool memory_is_poisoned(unsigned long addr, size_t size)
154{
155 if (__builtin_constant_p(size)) {
156 switch (size) {
157 case 1:
158 return memory_is_poisoned_1(addr);
159 case 2:
160 case 4:
161 case 8:
162 return memory_is_poisoned_2_4_8(addr, size);
163 case 16:
164 return memory_is_poisoned_16(addr);
165 default:
166 assert(0);
167 }
168 }
169
170 return memory_is_poisoned_n(addr, size);
171}
172
173static const void *find_first_bad_addr(const void *addr, size_t size)
174{
175 u8 shadow_val = *(u8 *)asan_mem_to_shadow(addr);
176 const void *first_bad_addr = addr;
177
178 while (!shadow_val && first_bad_addr < addr + size) {
179 first_bad_addr += ASAN_SHADOW_SCALE_SIZE;
180 shadow_val = *(u8 *)asan_mem_to_shadow(first_bad_addr);
181 }
182 return first_bad_addr;
183}
184
185static const char *get_shadow_bug_type(const void *addr, size_t size)
186{
187 const char *bug_type = "unknown-crash";
188 u8 *shadow_addr;
189 const void *first_bad_addr;
190
191 if (addr < asan_shadow_to_mem((void *) &_asan_shadow))
192 return bug_type;
193
194 first_bad_addr = find_first_bad_addr(addr, size);
195
196 shadow_addr = (u8 *)asan_mem_to_shadow(first_bad_addr);
197
198 if (*shadow_addr > 0 && *shadow_addr <= ASAN_SHADOW_SCALE_SIZE - 1)
199 shadow_addr++;
200
201 switch (*shadow_addr) {
202 case 0 ... ASAN_SHADOW_SCALE_SIZE - 1:
203 bug_type = "out-of-bounds";
204 break;
205 case ASAN_GLOBAL_REDZONE:
206 bug_type = "global-out-of-bounds";
207 break;
208 case ASAN_STACK_LEFT:
209 case ASAN_STACK_MID:
210 case ASAN_STACK_RIGHT:
211 case ASAN_STACK_PARTIAL:
212 bug_type = "stack-out-of-bounds";
213 break;
214 case ASAN_USE_AFTER_SCOPE:
215 bug_type = "use-after-scope";
216 break;
217 default:
218 bug_type = "unknown-crash";
219 }
220
221 return bug_type;
222}
223
224void asan_report(unsigned long addr, size_t size, bool is_write,
225 unsigned long ip)
226{
227 const char *bug_type = get_shadow_bug_type((void *) addr, size);
228 printk(BIOS_ERR, "\n");
229 printk(BIOS_ERR, "ASan: %s in %p\n", bug_type, (void *) ip);
230 printk(BIOS_ERR, "%s of %zu byte%s at addr %p\n",
231 is_write ? "Write" : "Read", size, (size > 1 ? "s" : ""),
232 (void *) addr);
233 printk(BIOS_ERR, "\n");
234}
235
236static __always_inline void check_memory_region_inline(unsigned long addr,
237 size_t size, bool write,
238 unsigned long ret_ip)
239{
Harshit Sharmaa6ebe082020-07-20 00:21:05 -0700240#if ENV_ROMSTAGE
241 if (((uintptr_t)addr < (uintptr_t)&_car_region_start) ||
242 ((uintptr_t)addr > (uintptr_t)&_ebss))
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700243 return;
Harshit Sharmaa6ebe082020-07-20 00:21:05 -0700244#elif ENV_RAMSTAGE
245 if (((uintptr_t)addr < (uintptr_t)&_data) ||
246 ((uintptr_t)addr > (uintptr_t)&_eheap))
247 return;
248#endif
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700249 if (unlikely(size == 0))
250 return;
251
252 if (unlikely((void *)addr <
253 asan_shadow_to_mem((void *) &_asan_shadow))) {
254 asan_report(addr, size, write, ret_ip);
255 return;
256 }
257
258 if (likely(!memory_is_poisoned(addr, size)))
259 return;
260
261 asan_report(addr, size, write, ret_ip);
262}
263
Harshit Sharma51593dd2020-08-08 17:51:59 -0700264void check_memory_region(unsigned long addr, size_t size, bool write,
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700265 unsigned long ret_ip)
266{
267 check_memory_region_inline(addr, size, write, ret_ip);
268}
269
270uintptr_t __asan_shadow_offset(uintptr_t addr)
271{
Harshit Sharmaa6ebe082020-07-20 00:21:05 -0700272#if ENV_ROMSTAGE
273 return (uintptr_t)&_asan_shadow - (((uintptr_t)&_car_region_start) >>
274 ASAN_SHADOW_SCALE_SHIFT);
275#elif ENV_RAMSTAGE
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700276 return (uintptr_t)&_asan_shadow - (((uintptr_t)&_data) >>
277 ASAN_SHADOW_SCALE_SHIFT);
Harshit Sharmaa6ebe082020-07-20 00:21:05 -0700278#endif
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700279}
280
281static void register_global(struct asan_global *global)
282{
283 size_t aligned_size = ALIGN_UP(global->size, ASAN_SHADOW_SCALE_SIZE);
284
285 asan_unpoison_shadow(global->beg, global->size);
286
287 asan_poison_shadow(global->beg + aligned_size,
288 global->size_with_redzone - aligned_size,
289 ASAN_GLOBAL_REDZONE);
290}
291
292void __asan_register_globals(struct asan_global *globals, size_t size)
293{
294 int i;
295
296 for (i = 0; i < size; i++)
297 register_global(&globals[i]);
298}
299
300void __asan_unregister_globals(struct asan_global *globals, size_t size)
301{
302}
303
304/*
305 * GCC adds constructors invoking __asan_register_globals() and passes
306 * information about global variable (address, size, size with redzone ...)
307 * to it so we could poison variable's redzone.
308 * This function calls those constructors.
309 */
Harshit Sharmaa6ebe082020-07-20 00:21:05 -0700310#if ENV_RAMSTAGE
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700311static void asan_ctors(void)
312{
313 extern long __CTOR_LIST__;
314 typedef void (*func_ptr)(void);
315 func_ptr *ctor = (func_ptr *) &__CTOR_LIST__;
316 if (ctor == NULL)
317 return;
318
319 for (; *ctor != (func_ptr) 0; ctor++)
320 (*ctor)();
321}
Harshit Sharmaa6ebe082020-07-20 00:21:05 -0700322#endif
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700323
324void asan_init(void)
325{
Harshit Sharmaa6ebe082020-07-20 00:21:05 -0700326#if ENV_ROMSTAGE
327 size_t size = (size_t)&_ebss - (size_t)&_car_region_start;
328 asan_unpoison_shadow((void *)&_car_region_start, size);
329#elif ENV_RAMSTAGE
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700330 size_t size = (size_t)&_eheap - (size_t)&_data;
331 asan_unpoison_shadow((void *)&_data, size);
332 asan_ctors();
Harshit Sharmaa6ebe082020-07-20 00:21:05 -0700333#endif
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700334}
335
336void __asan_poison_stack_memory(const void *addr, size_t size)
337{
338 asan_poison_shadow(addr, ALIGN_UP(size, ASAN_SHADOW_SCALE_SIZE),
339 ASAN_USE_AFTER_SCOPE);
340}
341
342void __asan_unpoison_stack_memory(const void *addr, size_t size)
343{
344 asan_unpoison_shadow(addr, size);
345}
346
347#define DEFINE_ASAN_LOAD_STORE(size) \
348 void __asan_load##size(unsigned long addr) \
349 { \
350 check_memory_region_inline(addr, size, false, _RET_IP_);\
351 } \
352 void __asan_load##size##_noabort(unsigned long addr) \
353 { \
354 check_memory_region_inline(addr, size, false, _RET_IP_);\
355 } \
356 void __asan_store##size(unsigned long addr) \
357 { \
358 check_memory_region_inline(addr, size, true, _RET_IP_); \
359 } \
360 void __asan_store##size##_noabort(unsigned long addr) \
361 { \
362 check_memory_region_inline(addr, size, true, _RET_IP_); \
363 }
Harshit Sharma2bcaba02020-06-09 20:25:16 -0700364
365DEFINE_ASAN_LOAD_STORE(1);
366DEFINE_ASAN_LOAD_STORE(2);
367DEFINE_ASAN_LOAD_STORE(4);
368DEFINE_ASAN_LOAD_STORE(8);
369DEFINE_ASAN_LOAD_STORE(16);
370
371void __asan_loadN(unsigned long addr, size_t size)
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700372{
373 check_memory_region(addr, size, false, _RET_IP_);
374}
Harshit Sharma2bcaba02020-06-09 20:25:16 -0700375
376void __asan_storeN(unsigned long addr, size_t size)
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700377{
378 check_memory_region(addr, size, true, _RET_IP_);
379}
380
381void __asan_loadN_noabort(unsigned long addr, size_t size)
382{
383 check_memory_region(addr, size, false, _RET_IP_);
384}
Harshit Sharma2bcaba02020-06-09 20:25:16 -0700385
386void __asan_storeN_noabort(unsigned long addr, size_t size)
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700387{
388 check_memory_region(addr, size, true, _RET_IP_);
389}
Harshit Sharma2bcaba02020-06-09 20:25:16 -0700390
391void __asan_handle_no_return(void)
Harshit Sharma9c88fb82020-06-17 20:19:00 -0700392{
393}
394
395#define DEFINE_ASAN_SET_SHADOW(byte) \
396 void __asan_set_shadow_##byte(const void *addr, size_t size) \
397 { \
398 __builtin_memset((void *)addr, 0x##byte, size); \
399 }
400
401DEFINE_ASAN_SET_SHADOW(00);
402DEFINE_ASAN_SET_SHADOW(f1);
403DEFINE_ASAN_SET_SHADOW(f2);
404DEFINE_ASAN_SET_SHADOW(f3);
405DEFINE_ASAN_SET_SHADOW(f5);
406DEFINE_ASAN_SET_SHADOW(f8);
407
408#define DEFINE_ASAN_REPORT_LOAD(size) \
409void __asan_report_load##size##_noabort(unsigned long addr) \
410{ \
411 asan_report(addr, size, false, _RET_IP_); \
412}
413
414#define DEFINE_ASAN_REPORT_STORE(size) \
415void __asan_report_store##size##_noabort(unsigned long addr) \
416{ \
417 asan_report(addr, size, true, _RET_IP_); \
418}
419
420DEFINE_ASAN_REPORT_LOAD(1);
421DEFINE_ASAN_REPORT_LOAD(2);
422DEFINE_ASAN_REPORT_LOAD(4);
423DEFINE_ASAN_REPORT_LOAD(8);
424DEFINE_ASAN_REPORT_LOAD(16);
425DEFINE_ASAN_REPORT_STORE(1);
426DEFINE_ASAN_REPORT_STORE(2);
427DEFINE_ASAN_REPORT_STORE(4);
428DEFINE_ASAN_REPORT_STORE(8);
429DEFINE_ASAN_REPORT_STORE(16);
430
431void __asan_report_load_n_noabort(unsigned long addr, size_t size)
432{
433 asan_report(addr, size, false, _RET_IP_);
434}
435
436void __asan_report_store_n_noabort(unsigned long addr, size_t size)
437{
438 asan_report(addr, size, true, _RET_IP_);
439}