Re: [PATCH RESEND v13 2/2] rust: fmt: route {:p} through HashedPtr to prevent address leaks
"Gary Guo" <[email protected]>
| Newsgroups | org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <[email protected]> |
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`.
> + 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)
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},
> );
>