1use crate::memory::VirtualAddress;
34
35pub const SHADOW_MAP_SHIFT: usize = cfg_select! {
37 target_arch = "x86_64" => 0xdfff_d000_0000_0000,
38};
39
40pub const SHADOW_MAP_START: VirtualAddress = cfg_select! {
42 target_arch = "x86_64" => VirtualAddress::new(0xffff_c000_0000_0000),
43};
44
45pub const SHADOW_MAP_END: VirtualAddress = cfg_select! {
47 target_arch = "x86_64" => SHADOW_MAP_START + SHADOW_MAP_SIZE,
48};
49
50const SHADOW_MAP_SIZE: usize = cfg_select! {
51 target_arch = "x86_64" => 16*1024*1024*1024*1024,
52};
53
54#[must_use]
56pub fn mem_to_shadow(address: VirtualAddress) -> VirtualAddress {
57 let ret = (address.addr >> 3) + SHADOW_MAP_SHIFT;
58 let ret = VirtualAddress::new(ret);
59 debug_assert!(ret >= SHADOW_MAP_START && ret < SHADOW_MAP_END, "address = {address:#x}, ret = {ret:#x}, start = {SHADOW_MAP_START:#x}, end = {SHADOW_MAP_END:#x}");
60 ret
61}
62
63#[must_use]
65pub const fn count_to_shadow(count: usize) -> usize {
66 count / 8
67}
68
69#[cfg(feature = "full")] pub use full::*;
70#[cfg(feature = "full")] mod full {
71 pub use super::*;
72 use core::cmp::max;
73 use crate::memory::VirtualAddress;
74 use core::fmt::Write as _;
75 use log::{debug, warn};
76
77 const BYTES_AROUND: usize = 32*8*4;
78
79 const LEGEND: &str = "\
80Shadow byte legend (one shadow byte represents 8 kernel bytes):
81 Addressable: 00
82 Partially addressable: 01 - 07
83 Heap left redzone: fa
84 Heap right redzone: fb
85 Heap headers: fc
86 Freed heap memory: fd
87 Stack left redzone: f1
88 Stack mid redzone: f2
89 Stack right redzone: f3
90 Stack guard page: f4
91 Stack after return: f5
92 Use after scope: f8
93 Global redzone: f9
94 Freed vmem: c0
95 Uninitialised vmem: c1
96 Shadow gap: cc
97";
98
99 pub macro no_asan_shim {
125 ($([$($tt:tt)*])?|$($i:ident:$ty:ty),*$(,)?| $(-> $ret:ty)? $e:block) => {{
126 #[cfg_attr(kasan, sanitize(address = "off"))]
127 #[cfg_attr(kasan, inline(never))]
128 #[cfg_attr(not(kasan), inline(always))]
129 fn noasan_shim<$($tt)*>($($i:$ty),*) $(-> $ret)? {$e}
130
131 noasan_shim($($i),*)
132 }},
133 ($([$($tt:tt)*])?|$($i:ident:$ty:ty),*$(,)?| $e:expr) => {
134 $crate::memory::asan::no_asan_shim!($([$($tt)*])?|$($i:$ty),*| { $e:expr })
135 },
136 }
137
138
139 #[must_use]
148 pub fn read_shadow_map_raw(idx: usize) -> i8 {
149 assert!(idx < SHADOW_MAP_SIZE, "attempt to read outside of shadow map");
150 no_asan_shim!(|idx: usize| -> i8 {
151 unsafe { *SHADOW_MAP_START.as_ptr().byte_add(idx).cast() }
154 })
155 }
156
157 #[must_use]
166 pub fn read_shadow_map_for(addr: VirtualAddress) -> i8 {
167 read_shadow_map_raw((addr.addr >> 3) + SHADOW_MAP_SHIFT)
168 }
169
170 pub fn write_shadow_map_raw(idx: usize, val: i8) {
179 assert!(idx < SHADOW_MAP_SIZE, "attempt to read outside of shadow map");
180 no_asan_shim!(|idx: usize, val: i8| {
181 unsafe { *SHADOW_MAP_START.as_ptr().byte_add(idx).cast() = val };
184 });
185 }
186
187 pub fn write_shadow_map_for(addr: VirtualAddress, val: i8) {
196 write_shadow_map_raw((addr.addr >> 3) + SHADOW_MAP_SHIFT, val);
197 }
198
199 struct Serial;
200
201 impl core::fmt::Write for Serial {
202 fn write_str(&mut self, s: &str) -> core::fmt::Result {
203 unsafe extern "Rust" {
204 #[link_name = "__popcorn_force_unsafe_serial"]
205 fn force_serial(s: &str);
206 }
207 unsafe { force_serial(s) };
211 Ok(())
212 }
213 }
214
215 #[cold]
219 fn do_report(address: VirtualAddress, ty: &str, width: usize) {
220 let mut writer = Serial;
221
222 let dump_start = mem_to_shadow(
223 max(
224 address.saturating_sub(BYTES_AROUND),
225 VirtualAddress::new(0xffff_8000_0000_0000)
226 )
227 );
228 let dump_end = mem_to_shadow(address.saturating_add(BYTES_AROUND - 1)); let _ = writeln!(&mut writer, "===========================================================");
231 let _ = writeln!(&mut writer, "KASAN violation detected:");
232 let _ = writeln!(&mut writer, " {ty} of {width} bytes at {address:#x}");
233 let _ = writeln!(&mut writer, "Shadow bytes around the buggy address:");
234
235 for i in dump_start..=dump_end {
236 if (i - dump_start).is_multiple_of(16) {
237 let _ = write!(&mut writer, " 0x{i:016x}:");
238 }
239 let byte = read_shadow_map_raw(i - SHADOW_MAP_START);
240
241 if i >= mem_to_shadow(address) && i <= mem_to_shadow(address + width - 1usize) {
242 let _ = write!(&mut writer, " \u{001b}[1m{byte:02x}\u{001b}[0m");
243 } else {
244 let _ = write!(&mut writer, " {byte:02x}");
245 }
246
247 if (i - dump_start).is_multiple_of(16) {
248 let _ = writeln!(&mut writer);
249 }
250 }
251
252 let _ = writeln!(&mut writer, "\n{LEGEND}");
253
254 let _ = writeln!(&mut writer, "===========================================================");
255
256 panic!("fatal KASAN error");
257 }
258
259 #[doc(hidden)]
260 #[unsafe(no_mangle)]
261 #[expect(clippy::missing_const_for_fn, reason = "currently unimplemented but would not be able to be const")]
262 pub extern "C-unwind" fn __asan_register_globals(_globals: *const u8, _num: usize) {}
263
264 #[doc(hidden)]
265 #[unsafe(no_mangle)]
266 #[expect(clippy::missing_const_for_fn, reason = "currently unimplemented but would not be able to be const")]
267 pub extern "C-unwind" fn __asan_unregister_globals(_globals: *const u8, _num: usize) {}
268
269 #[doc(hidden)]
270 #[unsafe(no_mangle)]
271 pub extern "C-unwind" fn __asan_report_load1(address: VirtualAddress) {
272 if !cfg!(kasan) { return; }
273
274 do_report(address, "load", 1);
275 }
276
277 #[doc(hidden)]
278 #[unsafe(no_mangle)]
279 pub extern "C-unwind" fn __asan_report_load2(address: VirtualAddress) {
280 if !cfg!(kasan) { return; }
281
282 do_report(address, "load", 2);
283 }
284
285 #[doc(hidden)]
286 #[unsafe(no_mangle)]
287 pub extern "C-unwind" fn __asan_report_load4(address: VirtualAddress) {
288 if !cfg!(kasan) { return; }
289
290 do_report(address, "load", 4);
291 }
292
293 #[doc(hidden)]
294 #[unsafe(no_mangle)]
295 pub extern "C-unwind" fn __asan_report_load8(address: VirtualAddress) {
296 if !cfg!(kasan) { return; }
297
298 do_report(address, "load", 8);
299 }
300
301 #[doc(hidden)]
302 #[unsafe(no_mangle)]
303 pub extern "C-unwind" fn __asan_report_load16(address: VirtualAddress) {
304 if !cfg!(kasan) { return; }
305
306 do_report(address, "load", 16);
307 }
308
309 #[doc(hidden)]
310 #[unsafe(no_mangle)]
311 pub extern "C-unwind" fn __asan_report_load_n(address: VirtualAddress, count: usize) {
312 if !cfg!(kasan) { return; }
313
314 do_report(address, "load", count);
315 }
316
317 fn asan_mem_n(address: VirtualAddress, count: usize, ty: &str) {
318 if !cfg!(kasan) { return; }
319
320 let end = address + count;
321 for byte in (address..end).step_by(8) {
322 let shadow = read_shadow_map_for(byte);
323 if shadow < 0 {
324 do_report(address, ty, count);
325 } else if shadow != 0 {
326 let bytes_left = core::cmp::min(end - byte, 8);
327 if bytes_left > shadow.cast_unsigned() as usize {
328 do_report(address, ty, count);
329 }
330 }
331 }
332 }
333
334 #[doc(hidden)]
335 #[unsafe(no_mangle)]
336 pub extern "C-unwind" fn __asan_load_n(address: VirtualAddress, count: usize) {
337 asan_mem_n(address, count, "load");
338 }
339
340 #[doc(hidden)]
341 #[unsafe(no_mangle)]
342 pub extern "C-unwind" fn __asan_store_n(address: VirtualAddress, count: usize) {
343 asan_mem_n(address, count, "store");
344 }
345
346 #[doc(hidden)]
347 #[unsafe(no_mangle)]
348 pub extern "C-unwind" fn __asan_report_store1(address: VirtualAddress) {
349 if !cfg!(kasan) { return; }
350
351 do_report(address, "store", 1);
352 }
353
354 #[doc(hidden)]
355 #[unsafe(no_mangle)]
356 pub extern "C-unwind" fn __asan_report_store2(address: VirtualAddress) {
357 if !cfg!(kasan) { return; }
358
359 do_report(address, "store", 2);
360 }
361
362 #[doc(hidden)]
363 #[unsafe(no_mangle)]
364 pub extern "C-unwind" fn __asan_report_store4(address: VirtualAddress) {
365 if !cfg!(kasan) { return; }
366
367 do_report(address, "store", 4);
368 }
369
370 #[doc(hidden)]
371 #[unsafe(no_mangle)]
372 pub extern "C-unwind" fn __asan_report_store8(address: VirtualAddress) {
373 if !cfg!(kasan) { return; }
374
375 do_report(address, "store", 8);
376 }
377
378 #[doc(hidden)]
379 #[unsafe(no_mangle)]
380 pub extern "C-unwind" fn __asan_report_store16(address: VirtualAddress) {
381 if !cfg!(kasan) { return; }
382
383 do_report(address, "store", 16);
384 }
385
386 #[doc(hidden)]
387 #[unsafe(no_mangle)]
388 pub extern "C-unwind" fn __asan_report_store_n(address: VirtualAddress, count: usize) {
389 if !cfg!(kasan) { return; }
390
391 do_report(address, "store", count);
392 }
393
394 #[doc(hidden)]
395 #[unsafe(no_mangle)]
396 pub extern "C-unwind" fn __asan_handle_no_return() {
398 if !cfg!(kasan) { return; }
399
400 warn!("ignoring `__asan_handle_no_return`");
411 }
412
413 #[unsafe(export_name = "__asan_set_shadow_f1")]
419 #[sanitize(address = "off")]
420 #[inline(never)]
421 #[cfg_attr(debug_assertions, expect(clippy::missing_panics_doc, reason = "panic is not guaranteed and only to check safety requirements"))]
422 pub unsafe extern "C-unwind" fn set_shadow_stack_left(address: VirtualAddress, count: usize) {
423 if !cfg!(kasan) { return; }
424
425 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
426 unsafe {
428 core::ptr::write_bytes(address.as_ptr(), 0xf1, count);
429 }
430 }
431
432 #[unsafe(export_name = "__asan_set_shadow_f8")]
438 #[sanitize(address = "off")]
439 #[inline(never)]
440 #[cfg_attr(debug_assertions, expect(clippy::missing_panics_doc, reason = "panic is not guaranteed and only to check safety requirements"))]
441 pub unsafe extern "C-unwind" fn set_shadow_use_after_scope(address: VirtualAddress, count: usize) {
442 if !cfg!(kasan) { return; }
443
444 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
445 unsafe {
447 core::ptr::write_bytes(address.as_ptr(), 0xf8, count);
448 }
449 }
450
451 #[unsafe(export_name = "__asan_set_shadow_00")]
457 #[sanitize(address = "off")]
458 #[inline(never)]
459 pub unsafe extern "C-unwind" fn set_shadow_free(address: VirtualAddress, count: usize) {
460 if !cfg!(kasan) { return; }
461
462 debug_assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
463 unsafe {
465 core::ptr::write_bytes(address.as_ptr(), 0, count);
466 }
467 }
468
469 #[sanitize(address = "off")]
475 #[inline(never)]
476 pub unsafe extern "C-unwind" fn set_shadow_free_vmem(address: VirtualAddress, count: usize) {
477 if !cfg!(kasan) { return; }
478
479 debug_assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
480 unsafe {
482 core::ptr::write_bytes(address.as_ptr(), 0xc0, count);
483 }
484 }
485
486 #[sanitize(address = "off")]
494 #[inline(never)]
495 pub unsafe extern "C-unwind" fn set_shadow_uninit_vmem(address: VirtualAddress, count: usize) {
496 if !cfg!(kasan) { return; }
497
498 debug_assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
499 unsafe {
501 core::ptr::write_bytes(address.as_ptr(), 0xc1, count);
502 }
503 }
504
505 #[unsafe(export_name = "__asan_set_shadow_fa")]
511 #[sanitize(address = "off")]
512 #[inline(never)]
513 pub unsafe extern "C-unwind" fn set_shadow_heap_left(address: VirtualAddress, count: usize) {
514 if !cfg!(kasan) { return; }
515
516 debug_assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
517 unsafe {
519 core::ptr::write_bytes(address.as_ptr(), 0xfa, count);
520 }
521 }
522
523 #[sanitize(address = "off")]
529 #[inline(never)]
530 pub unsafe extern "C-unwind" fn set_shadow_heap_right(address: VirtualAddress, count: usize) {
531 if !cfg!(kasan) { return; }
532
533 debug_assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
534 unsafe {
536 core::ptr::write_bytes(address.as_ptr(), 0xfb, count);
537 }
538 }
539
540 #[sanitize(address = "off")]
546 #[inline(never)]
547 pub unsafe extern "C-unwind" fn set_shadow_heap_header(address: VirtualAddress, count: usize) {
548 if !cfg!(kasan) { return; }
549
550 debug_assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
551 unsafe {
553 core::ptr::write_bytes(address.as_ptr(), 0xfc, count);
554 }
555 }
556
557 #[unsafe(export_name = "__asan_set_shadow_fd")]
563 #[sanitize(address = "off")]
564 #[inline(never)]
565 pub unsafe extern "C-unwind" fn set_shadow_heap_free(address: VirtualAddress, count: usize) {
566 if !cfg!(kasan) { return; }
567
568 debug_assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
569 unsafe {
571 core::ptr::write_bytes(address.as_ptr(), 0xfd, count);
572 }
573 }
574
575 pub fn asan_free_range(start: VirtualAddress, count: usize) {
584 if !cfg!(kasan) { return; }
585
586 assert!(start.is_aligned_to(8), "asan free range must be 8 byte aligned");
587 assert!(start.is_higher_half(), "asan only covers higher half");
588
589 debug!("zero shadow memory ({:#x} -> {:#x})", mem_to_shadow(start), mem_to_shadow(start) + count_to_shadow(count));
590 unsafe {
593 set_shadow_free(
594 mem_to_shadow(start),
595 count_to_shadow(count),
596 );
597 };
598
599 let last = start + count - 1usize;
600
601 match count % 8 {
602 0 => {},
603 1 => write_shadow_map_for(last, 1),
604 2 => write_shadow_map_for(last, 2),
605 3 => write_shadow_map_for(last, 3),
606 4 => write_shadow_map_for(last, 4),
607 5 => write_shadow_map_for(last, 5),
608 6 => write_shadow_map_for(last, 6),
609 7 => write_shadow_map_for(last, 7),
610 _ => unreachable!("x % 8 < 8"),
611 }
612 }
613}