1#include <global.h>
2#include <mem/asan.h>
3#include <mem/hhdm.h>
4#include <mem/page.h>
5#include <mem/pmm.h>
6#include <mem/vmm.h>
7#include <stdint.h>
8#include <string.h>
9#include <sync/spinlock.h>
10
11#ifdef DEBUG_ASAN
12LOG_SITE_DECLARE_DEFAULT(asan);
13LOG_HANDLE_DECLARE_DEFAULT(asan);
14static bool asan_ready = false;
15static uint8_t *asan_shadow_base;
16static size_t asan_shadow_size;
17
18/* Compute the shadow byte for a given real address */
19static inline uint8_t *asan_shadow_for_internal(void *addr) {
20 return asan_shadow_base + ((uintptr_t) addr >> ASAN_SHADOW_SCALE);
21}
22
23/* Poison/unpoison helpers */
24void asan_poison(void *addr, size_t size) {
25 ASAN_ABORT_IF_NOT_READY();
26 uint8_t *shadow_start = asan_shadow_for_internal(addr);
27 uint8_t *shadow_end = asan_shadow_for_internal((uint8_t *) addr + size - 1);
28 for (uint8_t *s = shadow_start; s <= shadow_end; s++)
29 *s = 0xFF;
30}
31
32void asan_unpoison(void *addr, size_t size) {
33 ASAN_ABORT_IF_NOT_READY();
34 uint8_t *shadow_start = asan_shadow_for_internal(addr);
35 uint8_t *shadow_end = asan_shadow_for_internal((uint8_t *) addr + size - 1);
36 for (uint8_t *s = shadow_start; s <= shadow_end; s++)
37 *s = 0;
38}
39
40void asan_init(void) {
41 asan_info("Bringing up ASAN... this will take time...");
42 asan_shadow_size = (global.total_pages * PAGE_SIZE) >> ASAN_SHADOW_SCALE;
43
44 paddr_t shadow_phys = pmm_alloc_pages(
45 (asan_shadow_size + PAGE_SIZE - 1) / PAGE_SIZE, ALLOC_FLAGS_DEFAULT);
46
47 if (!shadow_phys)
48 panic("ASAN: could not allocate shadow memory");
49
50 asan_shadow_base = hhdm_paddr_to_ptr(shadow_phys);
51
52 size_t remaining = asan_shadow_size;
53 uint64_t phys = shadow_phys;
54 uint64_t virt = ASAN_SHADOW_OFFSET;
55
56 while (remaining >= PAGE_2MB && (phys % PAGE_2MB) == 0 &&
57 (virt % PAGE_2MB) == 0) {
58 if (vmm_map_page(virt, phys, PAGE_PRESENT | PAGE_WRITE, VMM_FLAG_NONE,
59 VMM_MAP_PAGE_SIZE_2MB) < 0)
60 panic("ASAN: failed to map 2MB page at %lx", virt);
61 phys += PAGE_2MB;
62 virt += PAGE_2MB;
63 remaining -= PAGE_2MB;
64 }
65
66 while (remaining > 0) {
67 if (vmm_map_page(virt, phys, PAGE_PRESENT | PAGE_WRITE) < 0)
68 panic("ASAN: failed to map 4KB page at %lx", virt);
69 phys += PAGE_SIZE;
70 virt += PAGE_SIZE;
71 remaining = remaining > PAGE_SIZE ? remaining - PAGE_SIZE : 0;
72 }
73
74 asan_info("ASAN mapped up and brought up... memsetting..");
75 /* Initialize shadow memory to poisoned */
76 memset(asan_shadow_base, 0xFF, asan_shadow_size);
77
78 asan_info("shadow memory initialized at %p", asan_shadow_base);
79
80 asan_ready = true;
81}
82
83static inline uint8_t *asan_shadow_for(const void *addr) {
84 uintptr_t a = (uintptr_t) addr;
85 size_t idx = a >> ASAN_SHADOW_SCALE;
86 if (idx >= asan_shadow_size) {
87 return NULL;
88 }
89 return asan_shadow_base + idx;
90}
91
92/* Report function called on violation. Prints info and panics. */
93static void __asan_report_and_panic(const char *what, const void *addr,
94 size_t size, bool is_write) {
95 printf("[ASAN] %s at %p size=%zu %s", what, addr, size,
96 is_write ? "store" : "load");
97 panic("ASAN: aborting due to memory error");
98}
99
100/* Core access check: conservative: checks every shadow byte spanned by access.
101 * If any shadow byte != 0, treat as violation.
102 */
103#include "mem/slab/internal.h"
104static inline void asan_check_access_core(const void *addr, size_t size,
105 bool is_write) {
106 ASAN_ABORT_IF_NOT_READY();
107 vaddr_t v = (vaddr_t) addr;
108 if (v < SLAB_HEAP_START || v > SLAB_HEAP_END)
109 return;
110
111 if (size == 0)
112 return;
113
114 const uint8_t *start = (const uint8_t *) addr;
115 const uint8_t *end = start + (size - 1);
116
117 uintptr_t start_idx = (uintptr_t) start >> ASAN_SHADOW_SCALE;
118 uintptr_t end_idx = (uintptr_t) end >> ASAN_SHADOW_SCALE;
119
120 /* bounds-check shadow index to avoid accessing past shadow table */
121 if (end_idx >= asan_shadow_size) {
122 __asan_report_and_panic("ASAN: access out-of-shadow-range", addr, size,
123 is_write);
124 return;
125 }
126
127 uint8_t *s = asan_shadow_base + start_idx;
128 size_t count = (end_idx - start_idx) + 1;
129
130 /* If any shadow byte is non-zero => poisoned. */
131 for (size_t i = 0; i < count; i++) {
132 if (s[i] != 0) {
133 __asan_report_and_panic("ASAN: invalid memory access (poisoned)",
134 addr, size, is_write);
135 return;
136 }
137 }
138}
139
140/* Public runtime functions used by compiler instrumentation.
141 We'll implement 1/2/4/8 byte variants for loads and stores. */
142
143void __asan_load1(const void *addr) {
144 asan_check_access_core(addr, 1, false);
145}
146void __asan_load2(const void *addr) {
147 asan_check_access_core(addr, 2, false);
148}
149void __asan_load4(const void *addr) {
150 asan_check_access_core(addr, 4, false);
151}
152void __asan_load8(const void *addr) {
153 asan_check_access_core(addr, 8, false);
154}
155void __asan_load16(const void *addr) {
156 asan_check_access_core(addr, 16, false);
157} /* some compilers */
158
159void __asan_store1(const void *addr) {
160 asan_check_access_core(addr, 1, true);
161}
162void __asan_store2(const void *addr) {
163 asan_check_access_core(addr, 2, true);
164}
165void __asan_store4(const void *addr) {
166 asan_check_access_core(addr, 4, true);
167}
168void __asan_store8(const void *addr) {
169 asan_check_access_core(addr, 8, true);
170}
171void __asan_store16(const void *addr) {
172 asan_check_access_core(addr, 16, true);
173}
174
175/* Generic wrappers the compiler sometimes uses */
176void __asan_loadN(const void *addr, size_t size) {
177 asan_check_access_core(addr, size, false);
178}
179void __asan_storeN(const void *addr, size_t size) {
180 wait_for_interrupt();
181 asan_check_access_core(addr, size, true);
182}
183
184void __asan_poison_memory_region(void *addr, size_t size) {
185 ASAN_ABORT_IF_NOT_READY();
186 if (size == 0)
187 return;
188
189 uint8_t *start_shadow = asan_shadow_for(addr);
190 if (!start_shadow)
191 return; /* out-of-range: ignore conservatively */
192
193 uint8_t *end_shadow = asan_shadow_for((uint8_t *) addr + size - 1);
194 if (!end_shadow) {
195 /* If end is out-of-range, compute up to last valid shadow byte. */
196 size_t start_idx = (uintptr_t) addr >> ASAN_SHADOW_SCALE;
197 if (start_idx >= asan_shadow_size)
198 return;
199 end_shadow = asan_shadow_base + (asan_shadow_size - 1);
200 }
201
202 for (uint8_t *p = start_shadow; p <= end_shadow; p++)
203 *p = ASAN_POISON_VALUE;
204}
205
206void __asan_unpoison_memory_region(void *addr, size_t size) {
207 ASAN_ABORT_IF_NOT_READY();
208 if (size == 0)
209 return;
210
211 uint8_t *start_shadow = asan_shadow_for(addr);
212 if (!start_shadow)
213 return;
214
215 uint8_t *end_shadow = asan_shadow_for((uint8_t *) addr + size - 1);
216 if (!end_shadow) {
217 size_t start_idx = (uintptr_t) addr >> ASAN_SHADOW_SCALE;
218 if (start_idx >= asan_shadow_size)
219 return;
220 end_shadow = asan_shadow_base + (asan_shadow_size - 1);
221 }
222
223 for (uint8_t *p = start_shadow; p <= end_shadow; p++)
224 *p = 0;
225}
226
227/* Minimal global registration support:
228 * The compiler emits calls to __asan_register_globals() for static globals;
229 * the runtime gets an array of descriptors with address+size. We'll unpoison
230 * each global and poison small redzones around it (simple fixed redzone).
231 *
232 * This structure layout is what Clang typically expects; compilers may vary.
233 * For a minimal build, the compiler-provided descriptors should be compatible.
234 *
235 * We provide a conservative implementation: unpoison the region and poison
236 * a fixed small left/right redzone.
237 */
238struct __asan_global {
239 void *addr;
240 size_t size;
241 const char *name;
242 /* some targets include more fields; we ignore them */
243};
244
245void __asan_register_globals(struct __asan_global *globals, size_t n) {
246 ASAN_ABORT_IF_NOT_READY();
247 const size_t redzone = 16;
248 for (size_t i = 0; i < n; i++) {
249 void *addr = globals[i].addr;
250 size_t size = globals[i].size;
251 if (!addr || size == 0)
252 continue;
253
254 /* poison left redzone (if address is valid) */
255 if ((uintptr_t) addr >= redzone) /* simple check */
256 __asan_poison_memory_region((uint8_t *) addr - redzone, redzone);
257 __asan_unpoison_memory_region(addr, size);
258 __asan_poison_memory_region((uint8_t *) addr + size, redzone);
259 }
260}
261
262/* Minimal unregister (no-op) */
263void __asan_unregister_globals(void *globals, size_t n) {
264 (void) globals;
265 (void) n;
266}
267
268#define ASAN_MAX_STACK_RECORDS 1024
269struct stack_record {
270 void *addr;
271 size_t size;
272};
273
274static struct stack_record stack_records[ASAN_MAX_STACK_RECORDS];
275static size_t stack_records_count = 0;
276
277void __asan_stack_malloc(void *addr, size_t size) {
278 ASAN_ABORT_IF_NOT_READY();
279 if (!addr || size == 0)
280 return;
281 /* Poison entire region, then unpoison the real payload so redzones are left
282 poisoned. For simplicity pick small redzones here. */
283 const size_t rz = 16;
284 __asan_poison_memory_region((uint8_t *) addr - rz, size + rz * 2);
285 __asan_unpoison_memory_region(addr, size);
286
287 if (stack_records_count < ASAN_MAX_STACK_RECORDS) {
288 stack_records[stack_records_count].addr = addr;
289 stack_records[stack_records_count].size = size;
290 stack_records_count++;
291 }
292}
293
294void __asan_stack_free(void *addr) {
295 ASAN_ABORT_IF_NOT_READY();
296 /* Unpoison and remove record. */
297 for (size_t i = 0; i < stack_records_count; i++) {
298 if (stack_records[i].addr == addr) {
299 size_t size = stack_records[i].size;
300 const size_t rz = 16;
301 __asan_poison_memory_region((uint8_t *) addr - rz, size + rz * 2);
302 /* shift tail down */
303 stack_records[i] = stack_records[stack_records_count - 1];
304 stack_records_count--;
305 return;
306 }
307 }
308}
309
310/* Optional report helpers (compiler may call __asan_report_*); implement a
311 minimal mapping to the same panic/report routine so user sees a message. */
312
313void __asan_report_load1(void *addr) {
314 __asan_report_and_panic("ASAN: load1", addr, 1, false);
315}
316void __asan_report_load2(void *addr) {
317 __asan_report_and_panic("ASAN: load2", addr, 2, false);
318}
319void __asan_report_load4(void *addr) {
320 __asan_report_and_panic("ASAN: load4", addr, 4, false);
321}
322void __asan_report_load8(void *addr) {
323 __asan_report_and_panic("ASAN: load8", addr, 8, false);
324}
325void __asan_report_load16(void *addr) {
326 __asan_report_and_panic("ASAN: invalid 16-byte load", addr, 16, false);
327}
328
329void __asan_report_load32(void *addr) {
330 __asan_report_and_panic("ASAN: invalid 32-byte load", addr, 32, false);
331}
332
333void __asan_report_load64(void *addr) {
334 __asan_report_and_panic("ASAN: invalid 64-byte load", addr, 64, false);
335}
336
337void __asan_report_load_n(void *addr, size_t size) {
338 __asan_report_and_panic("ASAN: invalid variable-size load", addr, size,
339 false);
340}
341
342void __asan_report_store1(void *addr) {
343 __asan_report_and_panic("ASAN: store1", addr, 1, true);
344}
345void __asan_report_store2(void *addr) {
346 __asan_report_and_panic("ASAN: store2", addr, 2, true);
347}
348void __asan_report_store4(void *addr) {
349 __asan_report_and_panic("ASAN: store4", addr, 4, true);
350}
351void __asan_report_store8(void *addr) {
352 __asan_report_and_panic("ASAN: store8", addr, 8, true);
353}
354void __asan_report_store16(void *addr) {
355 __asan_report_and_panic("ASAN: invalid 16-byte store", addr, 16, true);
356}
357
358void __asan_report_store32(void *addr) {
359 __asan_report_and_panic("ASAN: invalid 32-byte store", addr, 32, true);
360}
361
362void __asan_report_store64(void *addr) {
363 __asan_report_and_panic("ASAN: invalid 64-byte store", addr, 64, true);
364}
365
366void __asan_report_store_n(void *addr, size_t size) {
367 __asan_report_and_panic("ASAN: invalid variable-size store", addr, size,
368 true);
369}
370
371/* --- Stack-use-after-return configuration --- */
372
373/* Compiler checks this flag to see if it should try to detect
374 * stack-use-after-return. We can just say "false" (0).
375 */
376int __asan_option_detect_stack_use_after_return = 0;
377
378/* Stack alloc/free variants, indexed by small number suffix (0–9) */
379
380void *__asan_stack_malloc_0(size_t size) {
381 (void) size;
382 return NULL;
383}
384void *__asan_stack_malloc_1(size_t size) {
385 (void) size;
386 return NULL;
387}
388void *__asan_stack_malloc_2(size_t size) {
389 (void) size;
390 return NULL;
391}
392void *__asan_stack_malloc_3(size_t size) {
393 (void) size;
394 return NULL;
395}
396void *__asan_stack_malloc_4(size_t size) {
397 (void) size;
398 return NULL;
399}
400void *__asan_stack_malloc_5(size_t size) {
401 (void) size;
402 return NULL;
403}
404void *__asan_stack_malloc_6(size_t size) {
405 (void) size;
406 return NULL;
407}
408void *__asan_stack_malloc_7(size_t size) {
409 (void) size;
410 return NULL;
411}
412void *__asan_stack_malloc_8(size_t size) {
413 (void) size;
414 return NULL;
415}
416void *__asan_stack_malloc_9(size_t size) {
417 (void) size;
418 return NULL;
419}
420
421void __asan_stack_free_0(void *p, size_t size) {
422 (void) p;
423 (void) size;
424}
425void __asan_stack_free_1(void *p, size_t size) {
426 (void) p;
427 (void) size;
428}
429void __asan_stack_free_2(void *p, size_t size) {
430 (void) p;
431 (void) size;
432}
433void __asan_stack_free_3(void *p, size_t size) {
434 (void) p;
435 (void) size;
436}
437void __asan_stack_free_4(void *p, size_t size) {
438 (void) p;
439 (void) size;
440}
441void __asan_stack_free_5(void *p, size_t size) {
442 (void) p;
443 (void) size;
444}
445void __asan_stack_free_6(void *p, size_t size) {
446 (void) p;
447 (void) size;
448}
449void __asan_stack_free_7(void *p, size_t size) {
450 (void) p;
451 (void) size;
452}
453void __asan_stack_free_8(void *p, size_t size) {
454 (void) p;
455 (void) size;
456}
457void __asan_stack_free_9(void *p, size_t size) {
458 (void) p;
459 (void) size;
460}
461
462void *__asan_malloc(size_t size) {
463 /* Not relevant for kernel: just panic if somehow called */
464 __asan_report_and_panic("asan_malloc called", NULL, size, true);
465 return NULL;
466}
467
468void __asan_free(void *p) {
469 __asan_report_and_panic("asan_free called", p, 0, true);
470}
471
472void __asan_malloc_hook(void *ptr, size_t size) {
473 (void) ptr;
474 (void) size;
475}
476void __asan_free_hook(void *ptr) {
477 (void) ptr;
478}
479
480/* --- Init/fini stubs --- */
481
482void __asan_init(void) {
483 asan_info("__asan_init runtime stub called");
484}
485
486void __asan_before_dynamic_init(const char *module_name) {
487 (void) module_name;
488}
489void __asan_after_dynamic_init(void) {}
490
491/* --- Miscellaneous runtime entrypoints --- */
492
493/* Compiler sometimes emits these for non-instrumented copies. */
494void *__asan_memcpy(void *dst, const void *src, size_t n) {
495 return memcpy(dst, src, n);
496}
497void *__asan_memmove(void *dst, const void *src, size_t n) {
498 return memmove(dst, src, n);
499}
500void *__asan_memset(void *s, int c, size_t n) {
501 return memset(s, c, n);
502}
503
504/* Some compilers also expect these “weak” entrypoints */
505void __asan_handle_no_return(void) {}
506void __asan_after_load(void) {}
507void __asan_after_store(void) {}
508void __asan_before_memory_access(void) {}
509void __asan_after_memory_access(void) {}
510
511/* Optional runtime interface for global poisoning/unpoisoning */
512void __asan_set_shadow_00_to_0x00(void) {}
513void __asan_set_shadow_f8_to_0x00(void) {}
514
515void __asan_alloca_poison(void *addr, size_t size) {
516 ASAN_ABORT_IF_NOT_READY();
517 if (!addr || size == 0)
518 return;
519 /* Conservative: poison the whole region. The compiler expects
520 later an unpoison to unmark the payload itself. */
521 __asan_poison_memory_region(addr, size);
522}
523
524void __asan_allocas_unpoison(void *addr, size_t size) {
525 ASAN_ABORT_IF_NOT_READY();
526 if (!addr || size == 0)
527 return;
528 __asan_unpoison_memory_region(addr, size);
529}
530
531__attribute__((weak)) void __asan_alloca_poison_0(void *addr, size_t size) {
532 __asan_alloca_poison(addr, size);
533}
534
535__attribute__((weak)) void __asan_allocas_unpoison_0(void *addr, size_t size) {
536 __asan_allocas_unpoison(addr, size);
537}
538
539#define ASAN_ALIAS(name, target) __attribute__((alias(#target))) void name
540
541/* Outline callbacks (forced via -asan-instrumentation-with-call-threshold=0).
542 */
543ASAN_ALIAS(__asan_load1_noabort, __asan_load1)(const void *addr);
544ASAN_ALIAS(__asan_load2_noabort, __asan_load2)(const void *addr);
545ASAN_ALIAS(__asan_load4_noabort, __asan_load4)(const void *addr);
546ASAN_ALIAS(__asan_load8_noabort, __asan_load8)(const void *addr);
547ASAN_ALIAS(__asan_load16_noabort, __asan_load16)(const void *addr);
548ASAN_ALIAS(__asan_store1_noabort, __asan_store1)(const void *addr);
549ASAN_ALIAS(__asan_store2_noabort, __asan_store2)(const void *addr);
550ASAN_ALIAS(__asan_store4_noabort, __asan_store4)(const void *addr);
551ASAN_ALIAS(__asan_store8_noabort, __asan_store8)(const void *addr);
552ASAN_ALIAS(__asan_store16_noabort, __asan_store16)(const void *addr);
553ASAN_ALIAS(__asan_loadN_noabort, __asan_loadN)(const void *addr, size_t size);
554ASAN_ALIAS(__asan_storeN_noabort, __asan_storeN)(const void *addr, size_t size);
555
556ASAN_ALIAS(__asan_report_load1_noabort, __asan_report_load1)(void *addr);
557ASAN_ALIAS(__asan_report_load2_noabort, __asan_report_load2)(void *addr);
558ASAN_ALIAS(__asan_report_load4_noabort, __asan_report_load4)(void *addr);
559ASAN_ALIAS(__asan_report_load8_noabort, __asan_report_load8)(void *addr);
560ASAN_ALIAS(__asan_report_load16_noabort, __asan_report_load16)(void *addr);
561ASAN_ALIAS(__asan_report_store1_noabort, __asan_report_store1)(void *addr);
562ASAN_ALIAS(__asan_report_store2_noabort, __asan_report_store2)(void *addr);
563ASAN_ALIAS(__asan_report_store4_noabort, __asan_report_store4)(void *addr);
564ASAN_ALIAS(__asan_report_store8_noabort, __asan_report_store8)(void *addr);
565ASAN_ALIAS(__asan_report_store16_noabort, __asan_report_store16)(void *addr);
566ASAN_ALIAS(__asan_report_load_n_noabort, __asan_report_load_n)(void *addr,
567 size_t size);
568ASAN_ALIAS(__asan_report_store_n_noabort, __asan_report_store_n)(void *addr,
569 size_t size);
570#endif
571