/ src / lib / asan.c
asan.c
  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_SEPARATE_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
 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_SEPARATE_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  
 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  
 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  
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  
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;
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  
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  
236  static __always_inline void check_memory_region_inline(unsigned long addr,
237  						size_t size, bool write,
238  						unsigned long ret_ip)
239  {
240  #if ENV_SEPARATE_ROMSTAGE
241  	if (((uintptr_t)addr < (uintptr_t)&_car_region_start) ||
242  		((uintptr_t)addr > (uintptr_t)&_ebss))
243  		return;
244  #elif ENV_RAMSTAGE
245  	if (((uintptr_t)addr < (uintptr_t)&_data) ||
246  		((uintptr_t)addr > (uintptr_t)&_eheap))
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  
270  uintptr_t __asan_shadow_offset(uintptr_t addr)
271  {
272  #if ENV_SEPARATE_ROMSTAGE
273  	return (uintptr_t)&_asan_shadow - (((uintptr_t)&_car_region_start) >>
274  		ASAN_SHADOW_SCALE_SHIFT);
275  #elif ENV_RAMSTAGE
276  	return (uintptr_t)&_asan_shadow - (((uintptr_t)&_data) >>
277  		ASAN_SHADOW_SCALE_SHIFT);
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,
289  		ASAN_GLOBAL_REDZONE);
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_SEPARATE_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  {
338  	asan_poison_shadow(addr, ALIGN_UP(size, ASAN_SHADOW_SCALE_SIZE),
339  			    ASAN_USE_AFTER_SCOPE);
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  
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);
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  
391  void __asan_handle_no_return(void)
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  
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);
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  
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);
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  }