// SPDX-License-Identifier: GPL-2.0 //! Formatting utilities. //! //! This module is intended to be used in place of `core::fmt` in kernel code. use kernel::prelude::*; pub use core::fmt::{ Arguments, Debug, Error, Formatter, Result, Write, // }; /// Internal adapter used to route and allow implementations of formatting traits for foreign types. /// /// It is inserted automatically by the [`fmt!`] macro and is not meant to be used directly. /// /// [`fmt!`]: crate::prelude::fmt! #[doc(hidden)] pub struct Adapter(pub T); macro_rules! impl_fmt_adapter_forward { ($($trait:ident),* $(,)?) => { $( impl $trait for Adapter { fn fmt(&self, f: &mut Formatter<'_>) -> Result { let Self(t) = self; $trait::fmt(t, f) } } )* }; } use core::fmt::{ Binary, LowerExp, LowerHex, Octal, UpperExp, UpperHex, // }; use core::ptr::NonNull; impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp); /// A copy of [`core::fmt::Pointer`] that allows implementing pointer formatting for foreign types. /// /// Together with the [`Adapter`] type and [`fmt!`] macro, it enables raw pointer formatting to be /// intercepted and routed to [`HashedPtr`] (kernel's `%p` hashed format), preventing kernel address /// leaks. /// /// [`fmt!`]: crate::prelude::fmt! pub trait Pointer { /// Same as [`core::fmt::Pointer::fmt`]. fn fmt(&self, f: &mut Formatter<'_>) -> Result; } /// A wrapper for pointers that formats them using kernel's `%p` format specifier. /// /// By default, `%p` prints a hashed representation of the pointer address to prevent kernel address /// leaks. When the `no_hash_pointers` kernel command-line parameter is enabled, the real address is /// printed instead (for debugging purposes). pub struct HashedPtr(pub *const T); impl Pointer for HashedPtr { fn fmt(&self, f: &mut Formatter<'_>) -> Result { use crate::str::CStrExt as _; let mut buf = [0u8; 32]; // Use `%#0*p` for the `0x` prefix and zero-padding; `+2` compensates for // the prefix counting toward the field width. let default_width = (2 * size_of::() + 2) as c_int; let width = match (f.sign_aware_zero_pad(), f.width()) { (true, Some(w)) if w > 0 => w.min(buf.len() - 1) as c_int, _ => default_width, }; // SAFETY: `buf` is a valid, writable 32-byte buffer, sufficient for // all architectures (max 19 bytes for 64-bit under the default width). // The format string is null-terminated; `width` (c_int) and pointer // match the `%*` and `%p` specifiers. let len = unsafe { crate::bindings::scnprintf( buf.as_mut_ptr().cast(), buf.len(), c"%#0*p".as_char_ptr(), width, self.0.cast::(), ) }; // SAFETY: `%#0*p` produces only ASCII, which is valid UTF-8. let s = unsafe { core::str::from_utf8_unchecked(&buf[..len as usize]) }; if f.sign_aware_zero_pad() { // `scnprintf` already applied the width and zero-padding via `%#0*p`. f.write_str(s) } else { f.pad(s) } } } // Raw pointers are formatted via `HashedPtr` (kernel `%p`: hashed by default, plain with // `no_hash_pointers`). impl Pointer for *const T { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { Pointer::fmt(&HashedPtr(*self), f) } } impl Pointer for *mut T { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { Pointer::fmt(&HashedPtr(*self), f) } } impl Pointer for &T { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { Pointer::fmt(&HashedPtr(*self), f) } } impl Pointer for &mut T { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { Pointer::fmt(&HashedPtr(core::ptr::from_ref(*self)), f) } } impl Pointer for NonNull { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { Pointer::fmt(&HashedPtr(self.as_ptr()), f) } } // `Adapter<&T>` bridges our `Pointer` trait to `core::fmt::Pointer` impl core::fmt::Pointer for Adapter<&T> { #[inline] fn fmt(&self, f: &mut Formatter<'_>) -> Result { Pointer::fmt(self.0, f) } } /// A copy of [`core::fmt::Display`] that allows us to implement it for foreign types. /// /// Types should implement this trait rather than [`core::fmt::Display`]. Together with the /// [`Adapter`] type and [`fmt!`] macro, it allows for formatting foreign types (e.g. types from /// core) which do not implement [`core::fmt::Display`] directly. /// /// [`fmt!`]: crate::prelude::fmt! pub trait Display { /// Same as [`core::fmt::Display::fmt`]. fn fmt(&self, f: &mut Formatter<'_>) -> Result; } impl Display for &T { fn fmt(&self, f: &mut Formatter<'_>) -> Result { Display::fmt(*self, f) } } impl core::fmt::Display for Adapter<&T> { fn fmt(&self, f: &mut Formatter<'_>) -> Result { let Self(t) = self; Display::fmt(t, f) } } macro_rules! impl_display_forward { ($( $( { $($generics:tt)* } )? $ty:ty $( { where $($where:tt)* } )? ),* $(,)?) => { $( impl$($($generics)*)? Display for $ty $(where $($where)*)? { fn fmt(&self, f: &mut Formatter<'_>) -> Result { core::fmt::Display::fmt(self, f) } } )* }; } impl_display_forward!( bool, char, core::panic::PanicInfo<'_>, Arguments<'_>, i128, i16, i32, i64, i8, isize, str, u128, u16, u32, u64, u8, usize, {} crate::sync::Arc {where crate::sync::Arc: core::fmt::Display}, {} crate::sync::UniqueArc {where crate::sync::UniqueArc: core::fmt::Display}, ); #[macros::kunit_tests(rust_kernel_fmt)] mod tests { use crate::{ bindings, prelude::fmt, str::CString, // }; #[cfg(CONFIG_64BIT)] mod expected { pub(super) const PTR_VALUE: usize = 0xffffffffdeadbeef; pub(super) const PTR_VAL_NO_CRNG: &str = "(____ptrval____)"; pub(super) const HASHED_PREFIX: &str = "0x00000000"; pub(super) const RAW_POINTER: &str = "0xffffffffdeadbeef"; pub(super) const PADDED_RIGHT: &str = " 0xffffffffdeadbeef"; pub(super) const ZERO_PADDED: &str = "0x000000ffffffffdeadbeef"; pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = " "; pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000"; pub(super) const CLAMPED: &str = "0x0000000000000ffffffffdeadbeef"; } #[cfg(not(CONFIG_64BIT))] mod expected { pub(super) const PTR_VALUE: usize = 0xdeadbeef; pub(super) const PTR_VAL_NO_CRNG: &str = "(ptrval)"; pub(super) const HASHED_PREFIX: &str = "0x"; pub(super) const RAW_POINTER: &str = "0xdeadbeef"; pub(super) const PADDED_RIGHT: &str = " 0xdeadbeef"; pub(super) const ZERO_PADDED: &str = "0x00000000000000deadbeef"; pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = " "; pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000"; pub(super) const CLAMPED: &str = "0x0000000000000000000000deadbeef"; } #[test] fn test_ptr_formatting() -> core::result::Result<(), crate::error::Error> { let ptr: *const u8 = core::ptr::without_provenance(expected::PTR_VALUE); // SAFETY: `no_hash_pointers` is a global variable that is never concurrently modified — // KUnit tests may run at boot (before `mark_readonly()`) or manually afterwards (when the // variable is read-only). Reading is always safe. let no_hash = unsafe { bindings::no_hash_pointers }; if no_hash { let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?; assert_eq!(cstr.to_str()?, expected::RAW_POINTER); let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?; assert_eq!(cstr.to_str()?, expected::PADDED_RIGHT); let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?; assert_eq!(cstr.to_str()?, expected::ZERO_PADDED); let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?; assert_eq!(cstr.to_str()?, expected::CLAMPED); } else { let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?; let formatted = cstr.to_str()?; // If the RNG is not yet ready, `%p` falls back to a placeholder. if formatted == expected::PTR_VAL_NO_CRNG { return Ok(()); } assert!(formatted.starts_with(expected::HASHED_PREFIX)); assert_ne!(formatted, expected::RAW_POINTER); let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?; assert!(cstr .to_str()? .starts_with(expected::HASHED_PADDED_RIGHT_PREFIX)); let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?; assert!(cstr .to_str()? .starts_with(expected::HASHED_ZERO_PADDED_PREFIX)); let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?; let output = cstr.to_str()?; assert!(output.starts_with("0x")); assert!(!output[2..].chars().all(|c| c == '0')); } Ok(()) } }