Re: [PATCH RESEND v13 2/2] rust: fmt: route {:p} through HashedPtr to prevent address leaks

Ke Sun <[email protected]>
Newsgroups org.kernel.vger.rust-for-linux
Message-ID <[email protected]>
On 8/6/26 22:41, Gary Guo wrote:
> On Mon Jul 6, 2026 at 6:18 AM BST, Ke Sun wrote:
>> Define a custom `kernel::fmt::Pointer` trait and `HashedPtr` wrapper
>> so that `{:p}` formatting uses the kernel's `%p` hashed format instead
>> of printing raw pointer values, preventing kernel address space leaks.
>>
>> Signed-off-by: Ke Sun <[email protected]>
>> ---
>>   rust/kernel/fmt.rs | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 164 insertions(+), 2 deletions(-)
>>
>> diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs
>> index cd7d9664ff5b9..3d154dad06f64 100644
>> --- a/rust/kernel/fmt.rs
>> +++ b/rust/kernel/fmt.rs
>> @@ -39,13 +39,106 @@ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
>>       LowerExp,
>>       LowerHex,
>>       Octal,
>> -    Pointer,
>>       UpperExp,
>>       UpperHex, //
>>   };
>> +use core::ptr::NonNull;
>>   impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp);
>>   
>> -impl<T: ?Sized + Pointer> Pointer for Adapter<&T> {
>> +/// 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];
>> +
>> +        // SAFETY: `buf` is a valid, writable buffer of 32 bytes, sufficient for all architectures
>> +        // (max 19 bytes for 64-bit). The format string `c"0x%p"` is null-terminated and `%p`
>> +        // matches the pointer argument.
>> +        let len = unsafe {
>> +            crate::bindings::scnprintf(
>> +                buf.as_mut_ptr().cast(),
>> +                buf.len(),
>> +                // Rust's `{:p}` includes a "0x" prefix, the kernel's `%p` does not.
> `%#p` should do the trick?
>
>> +                c"0x%p".as_char_ptr(),
>> +                self.0.cast::<core::ffi::c_void>(),
>> +            )
>> +        };
>> +
>> +        // SAFETY: "0x%p" produces only ASCII, which is valid UTF-8.
>> +        let hashed_str = unsafe { core::str::from_utf8_unchecked(&buf[..len as usize]) };
>> +
>> +        // Handle `{:0width$p}`: insert zeros after "0x" prefix.
>> +        if f.sign_aware_zero_pad() {
> zero pad can be implemented by `%0*p`.

`%#0*p` handles both the "0x" prefix and zero-padding in one pass,
and also fixes the "0x(____ptrval____)" issue Alice reported.

>
>> +            if let Some(width) = f.width() {
>> +                if hashed_str.len() < width && hashed_str.starts_with("0x") {
>> +                    return write!(f, "0x{:0>width$}", &hashed_str[2..], width = width - 2);
>> +                }
>> +            }
>> +        }
>> +
>> +        // Use `f.pad` to handle width/alignment formatting.
>> +        f.pad(hashed_str)
>> +    }
>> +}
>> +
>> +// 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 {
>> +        <*const T as Pointer>::fmt(&(*self).cast_const(), f)
> This could just be
>
>      Pointer::fmt(&HashedPtr(*self), f)

All the impls are `#[inline]`, so the compiler will eliminate the
intermediate calls anyway — but the direct style is cleaner. I'll
switch the other impls to the `HashedPtr(...)` form in v14.

Best regards,
Alvin

>
> by making use of `*mut T` -> `*const T` coercion. This would avoid doing
> multiple indirection. Same for all other impls below.
>
> Best,
> Gary
>
>> +    }
>> +}
>> +
>> +impl<T: ?Sized> Pointer for &T {
>> +    #[inline]
>> +    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
>> +        <*const T as Pointer>::fmt(&core::ptr::from_ref(*self), f)
>> +    }
>> +}
>> +
>> +impl<T: ?Sized> Pointer for &mut T {
>> +    #[inline]
>> +    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
>> +        <*const T as Pointer>::fmt(&core::ptr::from_ref(*self), f)
>> +    }
>> +}
>> +
>> +impl<T: ?Sized> Pointer for NonNull<T> {
>> +    #[inline]
>> +    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
>> +        <*const T as Pointer>::fmt(&self.as_ptr().cast_const(), 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)
>> @@ -112,3 +205,72 @@ fn fmt(&self, f: &mut Formatter<'_>) -> Result {
>>       {<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},
>>   );
>>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.