coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
asan.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 /*
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  */
10 
11 #include <console/console.h>
12 #include <symbols.h>
13 #include <assert.h>
14 #include <arch/symbols.h>
15 #include <asan.h>
16 
17 static inline void *asan_mem_to_shadow(const void *addr)
18 {
19 #if ENV_ROMSTAGE
20  return (void *)((uintptr_t)&_asan_shadow + (((uintptr_t)addr -
22 #elif ENV_RAMSTAGE
23  return (void *)((uintptr_t)&_asan_shadow + (((uintptr_t)addr -
24  (uintptr_t)&_data) >> ASAN_SHADOW_SCALE_SHIFT));
25 #endif
26 }
27 
28 static inline const void *asan_shadow_to_mem(const void *shadow_addr)
29 {
30 #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
34  return (void *)((uintptr_t)&_data + (((uintptr_t)shadow_addr -
35  (uintptr_t)&_asan_shadow) << ASAN_SHADOW_SCALE_SHIFT));
36 #endif
37 }
38 
39 static 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 
49 void 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 
59 static __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 
71 static __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 
82 static __always_inline bool memory_is_poisoned_16(unsigned long addr)
83 {
84  u16 *shadow_addr = (u16 *)asan_mem_to_shadow((void *)addr);
85 
87  return *shadow_addr || memory_is_poisoned_1(addr + 15);
88 
89  return *shadow_addr;
90 }
91 
92 static __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 
105 static __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 
134 static __always_inline bool memory_is_poisoned_n(unsigned long addr,
135  size_t size)
136 {
137  unsigned long ret;
138 
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 
153 static __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 
173 static 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 
185 static 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;
215  bug_type = "use-after-scope";
216  break;
217  default:
218  bug_type = "unknown-crash";
219  }
220 
221  return bug_type;
222 }
223 
224 void 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 
237  size_t size, bool write,
238  unsigned long ret_ip)
239 {
240 #if ENV_ROMSTAGE
242  ((uintptr_t)addr > (uintptr_t)&_ebss))
243  return;
244 #elif ENV_RAMSTAGE
245  if (((uintptr_t)addr < (uintptr_t)&_data) ||
247  return;
248 #endif
249  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 
264 void check_memory_region(unsigned long addr, size_t size, bool write,
265  unsigned long ret_ip)
266 {
267  check_memory_region_inline(addr, size, write, ret_ip);
268 }
269 
271 {
272 #if ENV_ROMSTAGE
273  return (uintptr_t)&_asan_shadow - (((uintptr_t)&_car_region_start) >>
275 #elif ENV_RAMSTAGE
276  return (uintptr_t)&_asan_shadow - (((uintptr_t)&_data) >>
278 #endif
279 }
280 
281 static 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,
290 }
291 
292 void __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 
300 void __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  */
310 #if ENV_RAMSTAGE
311 static 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 }
322 #endif
323 
324 void asan_init(void)
325 {
326 #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
330  size_t size = (size_t)&_eheap - (size_t)&_data;
331  asan_unpoison_shadow((void *)&_data, size);
332  asan_ctors();
333 #endif
334 }
335 
336 void __asan_poison_stack_memory(const void *addr, size_t size)
337 {
340 }
341 
342 void __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  }
364 
370 
371 void __asan_loadN(unsigned long addr, size_t size)
372 {
373  check_memory_region(addr, size, false, _RET_IP_);
374 }
375 
376 void __asan_storeN(unsigned long addr, size_t size)
377 {
378  check_memory_region(addr, size, true, _RET_IP_);
379 }
380 
381 void __asan_loadN_noabort(unsigned long addr, size_t size)
382 {
383  check_memory_region(addr, size, false, _RET_IP_);
384 }
385 
386 void __asan_storeN_noabort(unsigned long addr, size_t size)
387 {
388  check_memory_region(addr, size, true, _RET_IP_);
389 }
390 
392 {
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 
407 
408 #define DEFINE_ASAN_REPORT_LOAD(size) \
409 void __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) \
415 void __asan_report_store##size##_noabort(unsigned long addr) \
416 { \
417  asan_report(addr, size, true, _RET_IP_); \
418 }
419 
430 
431 void __asan_report_load_n_noabort(unsigned long addr, size_t size)
432 {
433  asan_report(addr, size, false, _RET_IP_);
434 }
435 
436 void __asan_report_store_n_noabort(unsigned long addr, size_t size)
437 {
438  asan_report(addr, size, true, _RET_IP_);
439 }
pte_t value
Definition: mmu.c:91
char _car_region_start[]
uintptr_t __asan_shadow_offset(uintptr_t addr)
Definition: asan.c:270
#define DEFINE_ASAN_SET_SHADOW(byte)
Definition: asan.c:395
static void * asan_mem_to_shadow(const void *addr)
Definition: asan.c:17
void __asan_storeN_noabort(unsigned long addr, size_t size)
Definition: asan.c:386
void check_memory_region(unsigned long addr, size_t size, bool write, unsigned long ret_ip)
Definition: asan.c:264
static const void * asan_shadow_to_mem(const void *shadow_addr)
Definition: asan.c:28
static __always_inline unsigned long bytes_is_nonzero(const u8 *start, size_t size)
Definition: asan.c:92
static __always_inline bool memory_is_poisoned_1(unsigned long addr)
Definition: asan.c:59
static void register_global(struct asan_global *global)
Definition: asan.c:281
static __always_inline bool memory_is_poisoned_n(unsigned long addr, size_t size)
Definition: asan.c:134
#define DEFINE_ASAN_REPORT_LOAD(size)
Definition: asan.c:408
#define DEFINE_ASAN_REPORT_STORE(size)
Definition: asan.c:414
void __asan_storeN(unsigned long addr, size_t size)
Definition: asan.c:376
static __always_inline bool memory_is_poisoned(unsigned long addr, size_t size)
Definition: asan.c:153
void __asan_unpoison_stack_memory(const void *addr, size_t size)
Definition: asan.c:342
static __always_inline bool memory_is_poisoned_16(unsigned long addr)
Definition: asan.c:82
void __asan_loadN_noabort(unsigned long addr, size_t size)
Definition: asan.c:381
static const char * get_shadow_bug_type(const void *addr, size_t size)
Definition: asan.c:185
void asan_init(void)
Definition: asan.c:324
void asan_report(unsigned long addr, size_t size, bool is_write, unsigned long ip)
Definition: asan.c:224
static __always_inline void check_memory_region_inline(unsigned long addr, size_t size, bool write, unsigned long ret_ip)
Definition: asan.c:236
static __always_inline unsigned long memory_is_nonzero(const void *start, const void *end)
Definition: asan.c:105
static void asan_poison_shadow(const void *address, size_t size, u8 value)
Definition: asan.c:39
void asan_unpoison_shadow(const void *address, size_t size)
Definition: asan.c:49
void __asan_poison_stack_memory(const void *addr, size_t size)
Definition: asan.c:336
void __asan_register_globals(struct asan_global *globals, size_t size)
Definition: asan.c:292
#define DEFINE_ASAN_LOAD_STORE(size)
Definition: asan.c:347
void __asan_loadN(unsigned long addr, size_t size)
Definition: asan.c:371
void __asan_unregister_globals(struct asan_global *globals, size_t size)
Definition: asan.c:300
static const void * find_first_bad_addr(const void *addr, size_t size)
Definition: asan.c:173
void __asan_handle_no_return(void)
Definition: asan.c:391
static __always_inline bool memory_is_poisoned_2_4_8(unsigned long addr, unsigned long size)
Definition: asan.c:71
void __asan_report_load_n_noabort(unsigned long addr, size_t size)
Definition: asan.c:431
void __asan_report_store_n_noabort(unsigned long addr, size_t size)
Definition: asan.c:436
#define ASAN_GLOBAL_REDZONE
Definition: asan.h:13
#define ASAN_STACK_LEFT
Definition: asan.h:14
#define ASAN_SHADOW_MASK
Definition: asan.h:11
#define ASAN_SHADOW_SCALE_SHIFT
Definition: asan.h:8
#define ASAN_USE_AFTER_SCOPE
Definition: asan.h:18
#define ASAN_STACK_RIGHT
Definition: asan.h:16
#define ASAN_SHADOW_SCALE_SIZE
Definition: asan.h:10
#define _RET_IP_
Definition: asan.h:20
#define ASAN_STACK_MID
Definition: asan.h:15
#define ASAN_STACK_PARTIAL
Definition: asan.h:17
#define assert(statement)
Definition: assert.h:74
#define IS_ALIGNED(x, a)
Definition: helpers.h:19
#define ALIGN_UP(x, a)
Definition: helpers.h:17
static u32 addr
Definition: cirrus.c:14
#define printk(level,...)
Definition: stdlib.h:16
#define __always_inline
Definition: compiler.h:35
uint64_t address
Definition: fw_cfg_if.h:0
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define likely(expr)
Definition: lz4_wrapper.c:58
#define unlikely(expr)
Definition: lz4_wrapper.c:59
unsigned char _eheap
Definition: malloc.c:11
#define NULL
Definition: stddef.h:19
__SIZE_TYPE__ size_t
Definition: stddef.h:7
uint64_t u64
Definition: stdint.h:54
unsigned long uintptr_t
Definition: stdint.h:21
uint16_t u16
Definition: stdint.h:48
int8_t s8
Definition: stdint.h:44
uint8_t u8
Definition: stdint.h:45
size_t size
Definition: asan.h:43
const void * beg
Definition: asan.h:42
size_t size_with_redzone
Definition: asan.h:44
typedef void(X86APIP X86EMU_intrFuncs)(int num)