Skip to main content

kernel_api/memory/
asan.rs

1//! ABI for interfacing with KASAN shadow memory.
2//!
3//! TODO(doc): overview of kasan?
4//!
5//! KASAN functions by keeping an in-memory shadow map of all kernelspace memory.
6//! Each byte in the shadow map represents 8 bytes of kernel memory, which can either be
7//! accessible, partially accessible, or poisoned.
8//!
9//! There are a range of poison values used in Popcorn2, some of which are defined by compiler ABI.
10//! In practice not all of these values are used.
11//! The current list is:
12//! - `0xfa`: Heap left redzone - memory just before a heap allocation
13//! - `0xfb`: Heap right redzone - memory just after a heap allocation
14//! - `0xfc`: Heap headers - memory used by heap internals
15//! - `0xfd`: Freed heap memory - heap memory that has recently been deallocated, and is currently
16//!   in a quarantine period
17//! - `0xf1`: Stack left redzone - memory just before a stack allocation
18//! - `0xf2`: Stack mid redzone - memory between two stack allocations
19//! - `0xf3`: Stack right redzone - memory just after a stack allocation
20//! - `0xf4`: Stack guard page - the page of unmapped memory below the stack to catch stack overflows
21//! - `0xf5`: Stack after return - the stack frame of a function that has already returned, used to
22//!   catch dangling references returned by a function
23//! - `0xf8`: Stack use after scope - a stack slot in the current function but is now out of scope
24//! - `0xf9`: Global redzone - memory around global variables
25//! - `0xc0`: Freed virtual memory - memory that has just been deallocated by a [`Vmm`](crate::allocator::Vmm)
26//! - `0xc1`: Uninitialized virtual memory - memory that has never been allocated
27//! - `0xcc`: Shadow gap - the shadow map itself
28//!
29//! The entire region from [`SHADOW_MAP_START`] through to [`SHADOW_MAP_END`] is safe to read and write
30//! to as a `[u8]`, since any unmapped memory will be automatically mapped. Helper functions for this are
31//! provided as [`read_shadow_map_for`]/[`write_shadow_map_for`] and [`read_shadow_map_raw`]/[`write_shadow_map_raw`].
32
33use crate::memory::VirtualAddress;
34
35/// Offset to add to `addr / 8` to calculate shadow map address.
36pub const SHADOW_MAP_SHIFT: usize = cfg_select! {
37	target_arch = "x86_64" => 0xdfff_d000_0000_0000,
38};
39
40/// Address of the start of the shadow map region.
41pub const SHADOW_MAP_START: VirtualAddress = cfg_select! {
42	target_arch = "x86_64" => VirtualAddress::new(0xffff_c000_0000_0000),
43};
44
45/// Address of the end of the shadow map region.
46pub 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/// Converts the passed `address` into the corresponding address in the shadow map.
55#[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/// Converts the number of bytes into the lower bound number of bytes in the shadow map.
64#[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    /// Prevents the contained code from being checked with the address sanitizer.
100	///
101	/// This should be used when accessing memory known to be marked as poisoned, for example
102	/// inside a heap allocator when reading internal metadata contained in a _heap headers_ zone.
103	/// This does **not** need to be used to wrap any functions in this module.
104	///
105	/// This macro should be called as a wrapper around closure-like syntax, either as
106	/// `no_asan_shim!(|a: usize| -> usize { a + 1 })` or `no_asan_shim!(|a: usize| a)`.
107	/// Generic arguments can be specified in square brackets before the closure, for example
108	/// `no_asan_shim!([T: Add<usize>]|a: T| -> T::Output { a + 1 })`.
109	/// All types including return types must be specified.
110	/// Arguments must correspond to existing in-scope bindings.
111	///
112	/// # Examples
113	///
114	/// ```no_run
115	/// use kernel_api::memory::asan::no_asan_shim;
116	///
117	/// for chunk in heap_chunks {
118	///     let allocated = no_asan_shim!(|chunk: *mut Chunk| -> bool {
119	///         unsafe { (&raw const (*chunk).allocated).read() }
120	///     });
121	///     if allocated { info!("allocated heap memory at {chunk:p}"); }
122	/// }
123	/// ```
124	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	/// Reads the value of the shadow map at the index `idx`.
140	///
141	/// See the [module level documentation](`crate::memory::asan`) for information
142	/// on the meaning of the read values.
143	///
144	/// # Panics
145	///
146	/// If `idx` is greater than `SHADOW_MAP_END - SHADOW_MAP_START`.
147	#[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			// SAFETY: just checked the index is within the shadow map and all values within shadow map
152			//  are aligned and accessible due to lazy mapping
153			unsafe { *SHADOW_MAP_START.as_ptr().byte_add(idx).cast() }
154		})
155	}
156
157	/// Reads the value of the shadow map for `addr`.
158	///
159	/// See the [module level documentation](`crate::memory::asan`) for information
160	/// on the meaning of the read values.
161	///
162	/// # Panics
163	///
164	/// If `addr` is not covered by the shadow map (i.e. userspace addresses).
165	#[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	/// Write `val` to the shadow map at the index `idx`.
171	///
172	/// See the [module level documentation](`crate::memory::asan`) for information
173	/// on the meaning of values that can be written.
174	///
175	/// # Panics
176	///
177	/// If `idx` is greater than `SHADOW_MAP_END - SHADOW_MAP_START`.
178	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			// SAFETY: just checked the index is within the shadow map and all values within shadow map
182			//  are aligned and accessible due to lazy mapping
183			unsafe { *SHADOW_MAP_START.as_ptr().byte_add(idx).cast() = val };
184		});
185	}
186
187	/// Write `val` to the shadow map for `addr`.
188	///
189	/// See the [module level documentation](`crate::memory::asan`) for information
190	/// on the meaning of values that can be written.
191	///
192	/// # Panics
193	///
194	/// If `addr` is not covered by the shadow map (i.e. userspace addresses).
195	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			// SAFETY: At worst this should cause interleaved access to the UART chip
208			//  which would cause interleaved or corrupted serial output.
209			//  At this point the kernel is already dying so just do the best we can.
210			unsafe { force_serial(s) };
211			Ok(())
212		}
213	}
214
215	/// # Panics
216	///
217	/// Unconditionally.
218	#[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)); // make this an inclusive range so we can print up to usize::MAX
229
230		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	//#[no_sanitize(address)]
397	pub extern "C-unwind" fn __asan_handle_no_return() {
398		if !cfg!(kasan) { return; }
399
400		/* idk what to do here */
401		/*let rsp: usize;
402		unsafe {
403			core::arch::asm!("mov {}, rsp", out(reg) rsp);
404		}
405		let rsp_shadow = SHADOW_MAP_SHIFT + (rsp >> 3);
406		let _ = writeln!(&mut Serial, "__asan_handle_no_return - clearing from {rsp_shadow:#x} to {SHADOW_MAP_END:#x}");
407		for i in 0..(SHADOW_MAP_END as usize - rsp_shadow) {
408			unsafe { *(rsp_shadow as *mut u8).offset(1) = 0; }
409		}*/
410		warn!("ignoring `__asan_handle_no_return`");
411	}
412
413	/// Marks `count` entries in the shadow map starting at `address` as _stack left redzone_.
414	///
415	/// # Safety
416	///
417	/// `address` through `address + count` must be addresses in the shadow map.
418	#[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		// SAFETY: `address` through `address + count` are addresses within the shadow map
427		unsafe {
428			core::ptr::write_bytes(address.as_ptr(), 0xf1, count);
429		}
430	}
431
432	/// Marks `count` entries in the shadow map starting at `address` as _stack use after scope_.
433	///
434	/// # Safety
435	///
436	/// `address` through `address + count` must be addresses in the shadow map.
437	#[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		// SAFETY: `address` through `address + count` are addresses within the shadow map
446		unsafe {
447			core::ptr::write_bytes(address.as_ptr(), 0xf8, count);
448		}
449	}
450
451	/// Marks `count` entries in the shadow map starting at `address` as _accessible_.
452	///
453	/// # Safety
454	///
455	/// `address` through `address + count` must be addresses in the shadow map.
456	#[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		// SAFETY: `address` through `address + count` are addresses within the shadow map
464		unsafe {
465			core::ptr::write_bytes(address.as_ptr(), 0, count);
466		}
467	}
468
469	/// Marks `count` entries in the shadow map starting at `address` as _freed virtual memory_.
470	///
471	/// # Safety
472	///
473	/// `address` through `address + count` must be addresses in the shadow map.
474	#[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		// SAFETY: `address` through `address + count` are addresses within the shadow map
481		unsafe {
482			core::ptr::write_bytes(address.as_ptr(), 0xc0, count);
483		}
484	}
485
486	/// Marks `count` entries in the shadow map starting at `address` as _uninitialized virtual memory_.
487	///
488	/// This is the default value for lazily allocated shadow memory.
489	///
490	/// # Safety
491	///
492	/// `address` through `address + count` must be addresses in the shadow map.
493	#[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		// SAFETY: `address` through `address + count` are addresses within the shadow map
500		unsafe {
501			core::ptr::write_bytes(address.as_ptr(), 0xc1, count);
502		}
503	}
504
505	/// Marks `count` entries in the shadow map starting at `address` as _heap left redzone_.
506	///
507	/// # Safety
508	///
509	/// `address` through `address + count` must be addresses in the shadow map.
510	#[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		// SAFETY: `address` through `address + count` are addresses within the shadow map
518		unsafe {
519			core::ptr::write_bytes(address.as_ptr(), 0xfa, count);
520		}
521	}
522
523	/// Marks `count` entries in the shadow map starting at `address` as _heap right redzone_.
524	///
525	/// # Safety
526	///
527	/// `address` through `address + count` must be addresses in the shadow map.
528	#[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		// SAFETY: `address` through `address + count` are addresses within the shadow map
535		unsafe {
536			core::ptr::write_bytes(address.as_ptr(), 0xfb, count);
537		}
538	}
539
540	/// Marks `count` entries in the shadow map starting at `address` as _heap headers_.
541	///
542	/// # Safety
543	///
544	/// `address` through `address + count` must be addresses in the shadow map.
545	#[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		// SAFETY: `address` through `address + count` are addresses within the shadow map
552		unsafe {
553			core::ptr::write_bytes(address.as_ptr(), 0xfc, count);
554		}
555	}
556
557	/// Marks `count` entries in the shadow map starting at `address` as _freed heap memory_.
558	///
559	/// # Safety
560	///
561	/// `address` through `address + count` must be addresses in the shadow map.
562	#[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		// SAFETY: `address` through `address + count` are addresses within the shadow map
570		unsafe {
571			core::ptr::write_bytes(address.as_ptr(), 0xfd, count);
572		}
573	}
574
575	/// Marks the region from `start` to `start + count` as _accessible_.
576	///
577	/// This internally calculates the correct shadow map entries.
578	///
579	/// # Panics
580	///
581	/// If the region is not in the kernel region of the address space, or does not
582	/// start on an 8 byte boundary.
583	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		// SAFETY: checked that the address is in kernelspace, and `mem_to_shadow` returns
591		//  a valid shadow map address for all kernelspace addresses.
592		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}