1 /* SPDX-License-Identifier: GPL-2.0-only */
4 * Address sanitizer support.
6 * Parts of this file are based on mm/kasan
7 * from the Linux kernel 4.19.137.
11 #include <console/console.h>
14 #include <arch/symbols.h>
17 static inline void *asan_mem_to_shadow(const void *addr
)
19 #if ENV_SEPARATE_ROMSTAGE
20 return (void *)((uintptr_t)&_asan_shadow
+ (((uintptr_t)addr
-
21 (uintptr_t)&_car_region_start
) >> ASAN_SHADOW_SCALE_SHIFT
));
23 return (void *)((uintptr_t)&_asan_shadow
+ (((uintptr_t)addr
-
24 (uintptr_t)&_data
) >> ASAN_SHADOW_SCALE_SHIFT
));
28 static inline const void *asan_shadow_to_mem(const void *shadow_addr
)
30 #if ENV_SEPARATE_ROMSTAGE
31 return (void *)((uintptr_t)&_car_region_start
+ (((uintptr_t)shadow_addr
-
32 (uintptr_t)&_asan_shadow
) << ASAN_SHADOW_SCALE_SHIFT
));
34 return (void *)((uintptr_t)&_data
+ (((uintptr_t)shadow_addr
-
35 (uintptr_t)&_asan_shadow
) << ASAN_SHADOW_SCALE_SHIFT
));
39 static void asan_poison_shadow(const void *address
, size_t size
, u8 value
)
41 void *shadow_start
, *shadow_end
;
43 shadow_start
= asan_mem_to_shadow(address
);
44 shadow_end
= asan_mem_to_shadow(address
+ size
);
46 __builtin_memset(shadow_start
, value
, shadow_end
- shadow_start
);
49 void asan_unpoison_shadow(const void *address
, size_t size
)
51 asan_poison_shadow(address
, size
, 0);
53 if (size
& ASAN_SHADOW_MASK
) {
54 u8
*shadow
= (u8
*)asan_mem_to_shadow(address
+ size
);
55 *shadow
= size
& ASAN_SHADOW_MASK
;
59 static __always_inline
bool memory_is_poisoned_1(unsigned long addr
)
61 s8 shadow_value
= *(s8
*)asan_mem_to_shadow((void *)addr
);
63 if (unlikely(shadow_value
)) {
64 s8 last_accessible_byte
= addr
& ASAN_SHADOW_MASK
;
65 return unlikely(last_accessible_byte
>= shadow_value
);
71 static __always_inline
bool memory_is_poisoned_2_4_8(unsigned long addr
,
74 u8
*shadow_addr
= (u8
*)asan_mem_to_shadow((void *)addr
);
76 if (unlikely(((addr
+ size
- 1) & ASAN_SHADOW_MASK
) < size
- 1))
77 return *shadow_addr
|| memory_is_poisoned_1(addr
+ size
- 1);
79 return memory_is_poisoned_1(addr
+ size
- 1);
82 static __always_inline
bool memory_is_poisoned_16(unsigned long addr
)
84 u16
*shadow_addr
= (u16
*)asan_mem_to_shadow((void *)addr
);
86 if (unlikely(!IS_ALIGNED(addr
, ASAN_SHADOW_SCALE_SIZE
)))
87 return *shadow_addr
|| memory_is_poisoned_1(addr
+ 15);
92 static __always_inline
unsigned long bytes_is_nonzero(const u8
*start
,
97 return (unsigned long)start
;
105 static __always_inline
unsigned long memory_is_nonzero(const void *start
,
110 unsigned int prefix
= (unsigned long)start
% 8;
112 if (end
- start
<= 16)
113 return bytes_is_nonzero(start
, end
- start
);
117 ret
= bytes_is_nonzero(start
, prefix
);
123 words
= (end
- start
) / 8;
125 if (unlikely(*(u64
*)start
))
126 return bytes_is_nonzero(start
, 8);
131 return bytes_is_nonzero(start
, (end
- start
) % 8);
134 static __always_inline
bool memory_is_poisoned_n(unsigned long addr
,
139 ret
= memory_is_nonzero(asan_mem_to_shadow((void *)addr
),
140 asan_mem_to_shadow((void *)addr
+ size
- 1) + 1);
143 unsigned long last_byte
= addr
+ size
- 1;
144 s8
*last_shadow
= (s8
*)asan_mem_to_shadow((void *)last_byte
);
146 if (unlikely(ret
!= (unsigned long)last_shadow
||
147 ((long)(last_byte
& ASAN_SHADOW_MASK
) >= *last_shadow
)))
153 static __always_inline
bool memory_is_poisoned(unsigned long addr
, size_t size
)
155 if (__builtin_constant_p(size
)) {
158 return memory_is_poisoned_1(addr
);
162 return memory_is_poisoned_2_4_8(addr
, size
);
164 return memory_is_poisoned_16(addr
);
170 return memory_is_poisoned_n(addr
, size
);
173 static const void *find_first_bad_addr(const void *addr
, size_t size
)
175 u8 shadow_val
= *(u8
*)asan_mem_to_shadow(addr
);
176 const void *first_bad_addr
= addr
;
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
);
182 return first_bad_addr
;
185 static const char *get_shadow_bug_type(const void *addr
, size_t size
)
187 const char *bug_type
= "unknown-crash";
189 const void *first_bad_addr
;
191 if (addr
< asan_shadow_to_mem((void *) &_asan_shadow
))
194 first_bad_addr
= find_first_bad_addr(addr
, size
);
196 shadow_addr
= (u8
*)asan_mem_to_shadow(first_bad_addr
);
198 if (*shadow_addr
> 0 && *shadow_addr
<= ASAN_SHADOW_SCALE_SIZE
- 1)
201 switch (*shadow_addr
) {
202 case 0 ... ASAN_SHADOW_SCALE_SIZE
- 1:
203 bug_type
= "out-of-bounds";
205 case ASAN_GLOBAL_REDZONE
:
206 bug_type
= "global-out-of-bounds";
208 case ASAN_STACK_LEFT
:
210 case ASAN_STACK_RIGHT
:
211 case ASAN_STACK_PARTIAL
:
212 bug_type
= "stack-out-of-bounds";
214 case ASAN_USE_AFTER_SCOPE
:
215 bug_type
= "use-after-scope";
218 bug_type
= "unknown-crash";
224 void asan_report(unsigned long addr
, size_t size
, bool is_write
,
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" : ""),
233 printk(BIOS_ERR
, "\n");
236 static __always_inline
void check_memory_region_inline(unsigned long addr
,
237 size_t size
, bool write
,
238 unsigned long ret_ip
)
240 #if ENV_SEPARATE_ROMSTAGE
241 if (((uintptr_t)addr
< (uintptr_t)&_car_region_start
) ||
242 ((uintptr_t)addr
> (uintptr_t)&_ebss
))
245 if (((uintptr_t)addr
< (uintptr_t)&_data
) ||
246 ((uintptr_t)addr
> (uintptr_t)&_eheap
))
249 if (unlikely(size
== 0))
252 if (unlikely((void *)addr
<
253 asan_shadow_to_mem((void *) &_asan_shadow
))) {
254 asan_report(addr
, size
, write
, ret_ip
);
258 if (likely(!memory_is_poisoned(addr
, size
)))
261 asan_report(addr
, size
, write
, ret_ip
);
264 void check_memory_region(unsigned long addr
, size_t size
, bool write
,
265 unsigned long ret_ip
)
267 check_memory_region_inline(addr
, size
, write
, ret_ip
);
270 uintptr_t __asan_shadow_offset(uintptr_t addr
)
272 #if ENV_SEPARATE_ROMSTAGE
273 return (uintptr_t)&_asan_shadow
- (((uintptr_t)&_car_region_start
) >>
274 ASAN_SHADOW_SCALE_SHIFT
);
276 return (uintptr_t)&_asan_shadow
- (((uintptr_t)&_data
) >>
277 ASAN_SHADOW_SCALE_SHIFT
);
281 static void register_global(struct asan_global
*global
)
283 size_t aligned_size
= ALIGN_UP(global
->size
, ASAN_SHADOW_SCALE_SIZE
);
285 asan_unpoison_shadow(global
->beg
, global
->size
);
287 asan_poison_shadow(global
->beg
+ aligned_size
,
288 global
->size_with_redzone
- aligned_size
,
289 ASAN_GLOBAL_REDZONE
);
292 void __asan_register_globals(struct asan_global
*globals
, size_t size
)
296 for (i
= 0; i
< size
; i
++)
297 register_global(&globals
[i
]);
300 void __asan_unregister_globals(struct asan_global
*globals
, size_t size
)
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.
311 static void asan_ctors(void)
313 extern long __CTOR_LIST__
;
314 typedef void (*func_ptr
)(void);
315 func_ptr
*ctor
= (func_ptr
*) &__CTOR_LIST__
;
319 for (; *ctor
!= (func_ptr
) 0; ctor
++)
326 #if ENV_SEPARATE_ROMSTAGE
327 size_t size
= (size_t)&_ebss
- (size_t)&_car_region_start
;
328 asan_unpoison_shadow((void *)&_car_region_start
, size
);
330 size_t size
= (size_t)&_eheap
- (size_t)&_data
;
331 asan_unpoison_shadow((void *)&_data
, size
);
336 void __asan_poison_stack_memory(const void *addr
, size_t size
)
338 asan_poison_shadow(addr
, ALIGN_UP(size
, ASAN_SHADOW_SCALE_SIZE
),
339 ASAN_USE_AFTER_SCOPE
);
342 void __asan_unpoison_stack_memory(const void *addr
, size_t size
)
344 asan_unpoison_shadow(addr
, size
);
347 #define DEFINE_ASAN_LOAD_STORE(size) \
348 void __asan_load##size(unsigned long addr) \
350 check_memory_region_inline(addr, size, false, _RET_IP_);\
352 void __asan_load##size##_noabort(unsigned long addr) \
354 check_memory_region_inline(addr, size, false, _RET_IP_);\
356 void __asan_store##size(unsigned long addr) \
358 check_memory_region_inline(addr, size, true, _RET_IP_); \
360 void __asan_store##size##_noabort(unsigned long addr) \
362 check_memory_region_inline(addr, size, true, _RET_IP_); \
365 DEFINE_ASAN_LOAD_STORE(1);
366 DEFINE_ASAN_LOAD_STORE(2);
367 DEFINE_ASAN_LOAD_STORE(4);
368 DEFINE_ASAN_LOAD_STORE(8);
369 DEFINE_ASAN_LOAD_STORE(16);
371 void __asan_loadN(unsigned long addr
, size_t size
)
373 check_memory_region(addr
, size
, false, _RET_IP_
);
376 void __asan_storeN(unsigned long addr
, size_t size
)
378 check_memory_region(addr
, size
, true, _RET_IP_
);
381 void __asan_loadN_noabort(unsigned long addr
, size_t size
)
383 check_memory_region(addr
, size
, false, _RET_IP_
);
386 void __asan_storeN_noabort(unsigned long addr
, size_t size
)
388 check_memory_region(addr
, size
, true, _RET_IP_
);
391 void __asan_handle_no_return(void)
395 #define DEFINE_ASAN_SET_SHADOW(byte) \
396 void __asan_set_shadow_##byte(const void *addr, size_t size) \
398 __builtin_memset((void *)addr, 0x##byte, size); \
401 DEFINE_ASAN_SET_SHADOW(00);
402 DEFINE_ASAN_SET_SHADOW(f1
);
403 DEFINE_ASAN_SET_SHADOW(f2
);
404 DEFINE_ASAN_SET_SHADOW(f3
);
405 DEFINE_ASAN_SET_SHADOW(f5
);
406 DEFINE_ASAN_SET_SHADOW(f8
);
408 #define DEFINE_ASAN_REPORT_LOAD(size) \
409 void __asan_report_load##size##_noabort(unsigned long addr) \
411 asan_report(addr, size, false, _RET_IP_); \
414 #define DEFINE_ASAN_REPORT_STORE(size) \
415 void __asan_report_store##size##_noabort(unsigned long addr) \
417 asan_report(addr, size, true, _RET_IP_); \
420 DEFINE_ASAN_REPORT_LOAD(1);
421 DEFINE_ASAN_REPORT_LOAD(2);
422 DEFINE_ASAN_REPORT_LOAD(4);
423 DEFINE_ASAN_REPORT_LOAD(8);
424 DEFINE_ASAN_REPORT_LOAD(16);
425 DEFINE_ASAN_REPORT_STORE(1);
426 DEFINE_ASAN_REPORT_STORE(2);
427 DEFINE_ASAN_REPORT_STORE(4);
428 DEFINE_ASAN_REPORT_STORE(8);
429 DEFINE_ASAN_REPORT_STORE(16);
431 void __asan_report_load_n_noabort(unsigned long addr
, size_t size
)
433 asan_report(addr
, size
, false, _RET_IP_
);
436 void __asan_report_store_n_noabort(unsigned long addr
, size_t size
)
438 asan_report(addr
, size
, true, _RET_IP_
);