1use crate::memory::VirtualAddress;
28
29pub const SHADOW_MAP_SHIFT: usize = cfg_select! {
31 target_arch = "x86_64" => 0xdfff_d000_0000_0000,
32};
33
34pub const SHADOW_MAP_START: VirtualAddress = cfg_select! {
36 target_arch = "x86_64" => VirtualAddress::new(0xffff_c000_0000_0000),
37};
38
39pub const SHADOW_MAP_END: VirtualAddress = cfg_select! {
41 target_arch = "x86_64" => SHADOW_MAP_START + SHADOW_MAP_SIZE,
42};
43
44const SHADOW_MAP_SIZE: usize = cfg_select! {
45 target_arch = "x86_64" => 16*1024*1024*1024*1024,
46};
47
48#[must_use]
50pub fn mem_to_shadow(address: VirtualAddress) -> VirtualAddress {
51 let ret = (address.addr >> 3) + SHADOW_MAP_SHIFT;
52 let ret = VirtualAddress::new(ret);
53 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}");
54 ret
55}
56
57#[must_use]
59pub const fn count_to_shadow(count: usize) -> usize {
60 count / 8
61}
62
63#[cfg(feature = "full")] pub use full::*;
64#[cfg(feature = "full")] mod full {
65 pub use super::*;
66 use core::cmp::max;
67 use crate::memory::VirtualAddress;
68 use core::fmt::Write as _;
69 use log::{debug, warn};
70
71 const BYTES_AROUND: usize = 32*8*4;
72
73 const LEGEND: &str = "\
74Shadow byte legend (one shadow byte represents 8 kernel bytes):
75 Addressable: 00
76 Partially addressable: 01 - 07
77 Heap left redzone: fa
78 Heap right redzone: fb
79 Heap headers: fc
80 Freed heap memory: fd
81 Stack left redzone: f1
82 Stack mid redzone: f2
83 Stack right redzone: f3
84 Stack guard page: f4
85 Stack after return: f5
86 Use after scope: f8
87 Global redzone: f9
88 Freed vmem: c0
89 Uninitialised vmem: c1
90 Shadow gap: cc
91";
92
93 pub macro no_asan_shim {
94 ($([$($tt:tt)*])?|$($i:ident:$ty:ty),*$(,)?| $(-> $ret:ty)? $e:block) => {{
95 #[cfg_attr(kasan, sanitize(address = "off"))]
96 #[cfg_attr(kasan, inline(never))]
97 #[cfg_attr(not(kasan), inline(always))]
98 fn noasan_shim<$($tt)*>($($i:$ty),*) $(-> $ret)? {$e}
99
100 noasan_shim($($i),*)
101 }},
102 ($([$($tt:tt)*])?|$($i:ident:$ty:ty),*$(,)?| $e:expr) => {
103 $crate::memory::asan::no_asan_shim!($([$($tt)*])?|$($i:$ty),*| { $e:expr })
104 },
105 }
106
107 pub fn read_shadow_map_raw(idx: usize) -> i8 {
108 assert!(idx < SHADOW_MAP_SIZE, "attempt to read outside of shadow map");
109 no_asan_shim!(|idx: usize| -> i8 {
110 unsafe { *SHADOW_MAP_START.as_ptr().byte_add(idx).cast() }
113 })
114 }
115
116 pub fn read_shadow_map_for(addr: VirtualAddress) -> i8 {
117 read_shadow_map_raw((addr.addr >> 3) + SHADOW_MAP_SHIFT)
118 }
119
120 pub fn write_shadow_map_raw(idx: usize, val: i8) {
122 assert!(idx < SHADOW_MAP_SIZE, "attempt to read outside of shadow map");
123 no_asan_shim!(|idx: usize, val: i8| {
124 unsafe { *SHADOW_MAP_START.as_ptr().byte_add(idx).cast() = val };
127 });
128 }
129
130 pub fn write_shadow_map_for(addr: VirtualAddress, val: i8) {
131 write_shadow_map_raw((addr.addr >> 3) + SHADOW_MAP_SHIFT, val);
132 }
133
134 struct Serial;
135
136 impl core::fmt::Write for Serial {
137 fn write_str(&mut self, s: &str) -> core::fmt::Result {
138 unsafe extern "Rust" {
139 #[link_name = "__popcorn_force_unsafe_serial"]
140 fn force_serial(s: &str);
141 }
142 unsafe { force_serial(s); }
143 Ok(())
144 }
145 }
146
147 #[cold]
148 fn do_report(address: VirtualAddress, ty: &str, width: usize) {
149 let mut writer = Serial;
150
151 let dump_start = mem_to_shadow(
152 max(
153 address.saturating_sub(BYTES_AROUND),
154 VirtualAddress::new(0xffff_8000_0000_0000)
155 )
156 );
157 let dump_end = mem_to_shadow(address.saturating_add(BYTES_AROUND - 1)); let _ = writeln!(&mut writer, "===========================================================");
160 let _ = writeln!(&mut writer, "KASAN violation detected:");
161 let _ = writeln!(&mut writer, " {ty} of {width} bytes at {address:#x}");
162 let _ = writeln!(&mut writer, "Shadow bytes around the buggy address:");
163
164 for i in dump_start..=dump_end {
165 if (i - dump_start) % 16 == 0 {
166 let _ = write!(&mut writer, " 0x{:016x}:", i);
167 }
168 let byte = read_shadow_map_raw(i - SHADOW_MAP_START);
169
170 if i >= mem_to_shadow(address) && i <= mem_to_shadow(address + width - 1usize) {
171 let _ = write!(&mut writer, " \u{001b}[1m{:02x}\u{001b}[0m", byte);
172 } else {
173 let _ = write!(&mut writer, " {:02x}", byte);
174 }
175
176 if (i - dump_start + 1) % 16 == 0 {
177 let _ = writeln!(&mut writer);
178 }
179 }
180
181 let _ = writeln!(&mut writer, "\n{}", LEGEND);
182
183 let _ = writeln!(&mut writer, "===========================================================");
184
185 panic!("fatal KASAN error");
186 }
187
188 #[doc(hidden)]
189 #[unsafe(no_mangle)]
190 pub extern "C-unwind" fn __asan_register_globals(_globals: *const u8, _num: usize) {}
191
192 #[doc(hidden)]
193 #[unsafe(no_mangle)]
194 pub extern "C-unwind" fn __asan_unregister_globals(_globals: *const u8, _num: usize) {}
195
196 #[doc(hidden)]
197 #[unsafe(no_mangle)]
198 pub extern "C-unwind" fn __asan_report_load1(address: VirtualAddress) {
199 if !cfg!(kasan) { return; }
200
201 do_report(address, "load", 1);
202 }
203
204 #[doc(hidden)]
205 #[unsafe(no_mangle)]
206 pub extern "C-unwind" fn __asan_report_load2(address: VirtualAddress) {
207 if !cfg!(kasan) { return; }
208
209 do_report(address, "load", 2);
210 }
211
212 #[doc(hidden)]
213 #[unsafe(no_mangle)]
214 pub extern "C-unwind" fn __asan_report_load4(address: VirtualAddress) {
215 if !cfg!(kasan) { return; }
216
217 do_report(address, "load", 4);
218 }
219
220 #[doc(hidden)]
221 #[unsafe(no_mangle)]
222 pub extern "C-unwind" fn __asan_report_load8(address: VirtualAddress) {
223 if !cfg!(kasan) { return; }
224
225 do_report(address, "load", 8);
226 }
227
228 #[doc(hidden)]
229 #[unsafe(no_mangle)]
230 pub extern "C-unwind" fn __asan_report_load16(address: VirtualAddress) {
231 if !cfg!(kasan) { return; }
232
233 do_report(address, "load", 16);
234 }
235
236 #[doc(hidden)]
237 #[unsafe(no_mangle)]
238 pub extern "C-unwind" fn __asan_report_load_n(address: VirtualAddress, count: usize) {
239 if !cfg!(kasan) { return; }
240
241 do_report(address, "load", count);
242 }
243
244 fn asan_mem_n(address: VirtualAddress, count: usize, ty: &str) {
245 if !cfg!(kasan) { return; }
246
247 let end = address + count;
248 for byte in (address..end).step_by(8) {
249 let shadow = read_shadow_map_for(byte);
250 if shadow < 0 {
251 do_report(address, ty, count);
252 } else if shadow != 0 {
253 let bytes_left = core::cmp::min(end - byte, 8);
254 if bytes_left > shadow.cast_unsigned() as usize {
255 do_report(address, ty, count);
256 }
257 }
258 }
259 }
260
261 #[doc(hidden)]
262 #[unsafe(no_mangle)]
263 pub extern "C-unwind" fn __asan_load_n(address: VirtualAddress, count: usize) {
264 asan_mem_n(address, count, "load");
265 }
266
267 #[doc(hidden)]
268 #[unsafe(no_mangle)]
269 pub extern "C-unwind" fn __asan_store_n(address: VirtualAddress, count: usize) {
270 asan_mem_n(address, count, "store");
271 }
272
273 #[doc(hidden)]
274 #[unsafe(no_mangle)]
275 pub extern "C-unwind" fn __asan_report_store1(address: VirtualAddress) {
276 if !cfg!(kasan) { return; }
277
278 do_report(address, "store", 1);
279 }
280
281 #[doc(hidden)]
282 #[unsafe(no_mangle)]
283 pub extern "C-unwind" fn __asan_report_store2(address: VirtualAddress) {
284 if !cfg!(kasan) { return; }
285
286 do_report(address, "store", 2);
287 }
288
289 #[doc(hidden)]
290 #[unsafe(no_mangle)]
291 pub extern "C-unwind" fn __asan_report_store4(address: VirtualAddress) {
292 if !cfg!(kasan) { return; }
293
294 do_report(address, "store", 4);
295 }
296
297 #[doc(hidden)]
298 #[unsafe(no_mangle)]
299 pub extern "C-unwind" fn __asan_report_store8(address: VirtualAddress) {
300 if !cfg!(kasan) { return; }
301
302 do_report(address, "store", 8);
303 }
304
305 #[doc(hidden)]
306 #[unsafe(no_mangle)]
307 pub extern "C-unwind" fn __asan_report_store16(address: VirtualAddress) {
308 if !cfg!(kasan) { return; }
309
310 do_report(address, "store", 16);
311 }
312
313 #[doc(hidden)]
314 #[unsafe(no_mangle)]
315 pub extern "C-unwind" fn __asan_report_store_n(address: VirtualAddress, count: usize) {
316 if !cfg!(kasan) { return; }
317
318 do_report(address, "store", count);
319 }
320
321 #[doc(hidden)]
322 #[unsafe(no_mangle)]
323 pub extern "C-unwind" fn __asan_handle_no_return() {
325 if !cfg!(kasan) { return; }
326
327 warn!("ignoring `__asan_handle_no_return`");
338 }
339
340 #[unsafe(export_name = "__asan_set_shadow_f1")]
346 #[sanitize(address = "off")]
347 #[inline(never)]
348 pub unsafe extern "C-unwind" fn set_shadow_stack_left(address: VirtualAddress, count: usize) {
349 if !cfg!(kasan) { return; }
350
351 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
352 unsafe {
353 core::ptr::write_bytes(address.as_ptr(), 0xf1, count);
354 }
355 }
356
357 #[unsafe(export_name = "__asan_set_shadow_f8")]
363 #[sanitize(address = "off")]
364 #[inline(never)]
365 pub unsafe extern "C-unwind" fn set_shadow_use_after_scope(address: VirtualAddress, count: usize) {
366 if !cfg!(kasan) { return; }
367
368 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
369 unsafe {
370 core::ptr::write_bytes(address.as_ptr(), 0xf8, count);
371 }
372 }
373
374 #[unsafe(export_name = "__asan_set_shadow_00")]
380 #[sanitize(address = "off")]
381 #[inline(never)]
382 pub unsafe extern "C-unwind" fn set_shadow_free(address: VirtualAddress, count: usize) {
383 if !cfg!(kasan) { return; }
384
385 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
386 unsafe {
387 core::ptr::write_bytes(address.as_ptr(), 0, count);
388 }
389 }
390
391 #[sanitize(address = "off")]
397 #[inline(never)]
398 pub unsafe extern "C-unwind" fn set_shadow_free_vmem(address: VirtualAddress, count: usize) {
399 if !cfg!(kasan) { return; }
400
401 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
402 unsafe {
403 core::ptr::write_bytes(address.as_ptr(), 0xc0, count);
404 }
405 }
406
407 #[sanitize(address = "off")]
415 #[inline(never)]
416 pub unsafe extern "C-unwind" fn set_shadow_uninit_vmem(address: VirtualAddress, count: usize) {
417 if !cfg!(kasan) { return; }
418
419 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
420 unsafe {
421 core::ptr::write_bytes(address.as_ptr(), 0xc1, count);
422 }
423 }
424
425 #[unsafe(export_name = "__asan_set_shadow_fa")]
431 #[sanitize(address = "off")]
432 #[inline(never)]
433 pub unsafe extern "C-unwind" fn set_shadow_heap_left(address: VirtualAddress, count: usize) {
434 if !cfg!(kasan) { return; }
435
436 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
437 unsafe {
438 core::ptr::write_bytes(address.as_ptr(), 0xfa, count);
439 }
440 }
441
442 #[sanitize(address = "off")]
448 #[inline(never)]
449 pub unsafe extern "C-unwind" fn set_shadow_heap_right(address: VirtualAddress, count: usize) {
450 if !cfg!(kasan) { return; }
451
452 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
453 unsafe {
455 core::ptr::write_bytes(address.as_ptr(), 0xfb, count);
456 }
457 }
458
459 #[sanitize(address = "off")]
465 #[inline(never)]
466 pub unsafe extern "C-unwind" fn set_shadow_heap_header(address: VirtualAddress, count: usize) {
467 if !cfg!(kasan) { return; }
468
469 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
470 unsafe {
472 core::ptr::write_bytes(address.as_ptr(), 0xfc, count);
473 }
474 }
475
476 #[unsafe(export_name = "__asan_set_shadow_fd")]
482 #[sanitize(address = "off")]
483 #[inline(never)]
484 pub unsafe extern "C-unwind" fn set_shadow_heap_free(address: VirtualAddress, count: usize) {
485 if !cfg!(kasan) { return; }
486
487 #[cfg(debug_assertions)] assert!(address >= SHADOW_MAP_START && address < SHADOW_MAP_END, "Safety violation: {address:#x} is not in the shadow map");
488 unsafe {
489 core::ptr::write_bytes(address.as_ptr(), 0xfd, count);
490 }
491 }
492
493 pub fn asan_free_range(start: VirtualAddress, count: usize) {
497 assert!(start.aligned_to(8), "asan free range must be 8 byte aligned");
498 if !cfg!(kasan) { return; }
499
500 assert!(start.is_higher_half(), "asan only covers higher half");
501
502 debug!("zero shadow memory ({:#x} -> {:#x})", mem_to_shadow(start), mem_to_shadow(start) + count_to_shadow(count));
503 unsafe {
504 set_shadow_free(
505 mem_to_shadow(start),
506 count_to_shadow(count),
507 );
508 }
509
510 let last = start + count - 1usize;
511
512 match count % 8 {
513 0 => {},
514 1 => write_shadow_map_for(last, 1),
515 2 => write_shadow_map_for(last, 2),
516 3 => write_shadow_map_for(last, 3),
517 4 => write_shadow_map_for(last, 4),
518 5 => write_shadow_map_for(last, 5),
519 6 => write_shadow_map_for(last, 6),
520 7 => write_shadow_map_for(last, 7),
521 _ => unreachable!("x % 8 < 8"),
522 }
523 }
524}