298 lines · 9.6 KB · Rust
RawDownload
// 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<T>(pub T); macro_rules! impl_fmt_adapter_forward {    ($($trait:ident),* $(,)?) => {        $(            impl<T: $trait> $trait for Adapter<T> {                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<T: ?Sized>(pub *const T); impl<T: ?Sized> Pointer for HashedPtr<T> {    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::<usize>() + 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::<c_void>(),            )        };         // 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<T: ?Sized> Pointer for *const T {    #[inline]    fn fmt(&self, f: &mut Formatter<'_>) -> Result {        Pointer::fmt(&HashedPtr(*self), f)    }} impl<T: ?Sized> Pointer for *mut T {    #[inline]    fn fmt(&self, f: &mut Formatter<'_>) -> Result {        Pointer::fmt(&HashedPtr(*self), f)    }} impl<T: ?Sized> Pointer for &T {    #[inline]    fn fmt(&self, f: &mut Formatter<'_>) -> Result {        Pointer::fmt(&HashedPtr(*self), f)    }} impl<T: ?Sized> Pointer for &mut T {    #[inline]    fn fmt(&self, f: &mut Formatter<'_>) -> Result {        Pointer::fmt(&HashedPtr(core::ptr::from_ref(*self)), f)    }} impl<T: ?Sized> Pointer for NonNull<T> {    #[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<T: Pointer> 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<T: ?Sized + Display> Display for &T {    fn fmt(&self, f: &mut Formatter<'_>) -> Result {        Display::fmt(*self, f)    }} impl<T: ?Sized + Display> 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,    {<T: ?Sized>} crate::sync::Arc<T> {where crate::sync::Arc<T>: core::fmt::Display},    {<T: ?Sized>} crate::sync::UniqueArc<T> {where crate::sync::UniqueArc<T>: 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(())    }}