* [PATCH v12 0/2] rust: Add safe pointer formatting support
@ 2026-05-12 8:55 Ke Sun
2026-05-12 8:55 ` [PATCH v12 1/2] rust: fmt: fix {:p} printing stack addresses Ke Sun
2026-05-12 8:55 ` [PATCH v12 2/2] rust: fmt: route {:p} through HashedPtr to prevent address leaks Ke Sun
0 siblings, 2 replies; 4+ messages in thread
From: Ke Sun @ 2026-05-12 8:55 UTC (permalink / raw)
To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Danilo Krummrich
Cc: rust-for-linux, Ke Sun
This series fixes two issues with {:p} pointer formatting:
- The impl_fmt_adapter_forward! macro destructures self into a local
variable, causing {:p} to print a stack address instead of the actual
pointer
- Kernel address leak — {:p} prints raw pointer values, exposing kernel
address space layout
---
Changes in v12:
- Split into 2 patches: fix {:p} printing stack addresses → route {:p}
through HashedPtr
- Test cleanup: NoHashPointersGuard RAII guard replaces raw
save/restore; mod expected consolidates 32/64-bit constants
- Impl delegation: &T, &mut T, *mut T all forward to *const T (matching
core library conventions), replacing v11's blanket impl + macro
- Link to v11: https://lore.kernel.org/r/20260205-hashedptr-v11-1-bd0fec7fe6f1@kylinos.cn
Changes in v11:
- Fix inaccurate or inappropriate descriptions in comments
- Use as_char_ptr instead of as_ptr so that a *const u8 pointer is
always passed to scnprintf on all architectures
- Per Tamir's suggestion, replace doctests with mod tests and adjust
test content to make the tests more meaningful
- Remove the RawPtr wrapper type: it and HashedPtr use different
formatting mechanisms (HashedPtr uses scnprintf and pad; RawPtr would
call core's Pointer impl directly). This series focuses on fixing the
issue that without it {:p} would output the pointer's stack address,
and on using HashedPtr to safely format raw pointers and avoid leaking
kernel address space layout information
- Link to v10: https://lore.kernel.org/r/20260121050059.2315091-1-sunke@kylinos.cn
Changes in v10:
- Merge all patches into a single patch
- Improve `kernel::fmt::Pointer` trait implementation
Link to v9: https://lore.kernel.org/r/20260119033006.1453006-1-sunke@kylinos.cn
Changes in v9: https://lore.kernel.org/r/20260119033006.1453006-1-sunke@kylinos.cn
- Refactor implementation to use Pointer trait and Adapter pattern
instead of exporting ptr_to_hashval() from lib/vsprintf.c. Use
scnprintf directly in Rust for pointer hashing, eliminating the
need for C function export
- Move pointer wrapper types from rust/kernel/ptr.rs to
rust/kernel/fmt.rs
- Split implementation into more granular patches: Pointer trait
foundation, HashedPtr type, raw pointer default behavior, and
RawPtr type
- Remove documentation patch, integrate examples into code doctests
- Simplify API and improve code organization following Display trait
pattern
- Link to v8: https://lore.kernel.org/r/20260101081605.1300953-1-sunke@kylinos.cn
Changes in v8:
- Remove RestrictedPtr (%pK) support: only export ptr_to_hashval() with
EXPORT_SYMBOL_NS_GPL using "RUST_INTERNAL" namespace, provide only two
pointer wrapper types (HashedPtr, RawPtr) for %p and %px
- Change API from HashedPtr::from(ptr) to HashedPtr(ptr) for direct
construction
- Link to v7: https://lore.kernel.org/r/20251229072157.3857053-1-sunke@kylinos.cn
Changes in v7:
- Refactor kptr_restrict handling: extract kptr_restrict_value() from
restricted_pointer() in lib/vsprintf.c and export it for Rust use, and
improve RestrictedPtr::fmt() implementation to directly handle
kptr_restrict_value() return values (0, 1, 2, -1) for better code
clarity
- Remove Debug derive from pointer wrapper types (HashedPtr,
RestrictedPtr, RawPtr)
- Link to v6: https://lore.kernel.org/r/20251227033958.3713232-1-sunke@kylinos.cn
Changes in v6:
- Fix placeholder formatting to use `f.pad()` instead of `f.write_str()`
in format_hashed_ptr(), ensuring width, alignment, and padding options
are correctly applied to PTR_PLACEHOLDER
- Link to v5: https://lore.kernel.org/r/20251226140751.2215563-1-sunke@kylinos.cn
Changes in v5: https://lore.kernel.org/r/20251226140751.2215563-1-sunke@kylinos.cn
- Format use statements in rust/kernel/ptr.rs and rust/kernel/fmt.rs
using kernel vertical style with alphabetical ordering
- Remove unnecessary SAFETY comment in rust/kernel/ptr.rs (addressed
Clippy warning)
- Update type ordering to alphabetical (HashedPtr, RawPtr,
RestrictedPtr) in fmt.rs macro invocation
- Link to v4: https://lore.kernel.org/r/20251225225709.3944255-1-sunke@kylinos.cn
Changes in v4:
- Use Pointer::fmt() instead of write!(f, "{:p}", ...) to preserve
formatting options (width, alignment, padding characters)
- Improve code structure: reduce unsafe block scope, use early return
pattern
- Add doctests with formatting option tests for all pointer wrapper
types
- Enhance documentation with detailed formatting options section,
including examples for width, alignment, and padding
- Fix RestrictedPtr example to use pr_info! instead of seq_print! in
docs
- Link to v3: https://lore.kernel.org/r/20251224081315.729684-1-sunke@kylinos.cn
Changes in v3:
- Export ptr_to_hashval() from lib/vsprintf.c for Rust pointer hashing
- Add three pointer wrapper types (HashedPtr, RestrictedPtr, RawPtr) in
rust/kernel/ptr.rs corresponding to %p, %pK, and %px
- Make raw pointers automatically use HashedPtr when formatted with {:p}
- Add documentation for pointer wrapper types
- Link to v2: https://lore.kernel.org/r/20251223033018.2814732-1-sunke@kylinos.cn
Changes in v2:
- Disabled {:p} raw pointer printing by default to prevent accidental
information leaks
- Link to v1: https://lore.kernel.org/r/20251218032709.2184890-1-sunke@kylinos.cn
Signed-off-by: Ke Sun <sunke@kylinos.cn>
---
Ke Sun (2):
rust: fmt: fix {:p} printing stack addresses
rust: fmt: route {:p} through HashedPtr to prevent address leaks
rust/kernel/fmt.rs | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 176 insertions(+), 2 deletions(-)
---
base-commit: 50897c955902c93ae71c38698abb910525ebdc89
change-id: 20260512-hashedptr-22469b930113
Best regards,
--
Ke Sun <sunke@kylinos.cn>
^ permalink raw reply [flat|nested] 4+ messages in thread* [PATCH v12 1/2] rust: fmt: fix {:p} printing stack addresses 2026-05-12 8:55 [PATCH v12 0/2] rust: Add safe pointer formatting support Ke Sun @ 2026-05-12 8:55 ` Ke Sun 2026-05-12 8:55 ` [PATCH v12 2/2] rust: fmt: route {:p} through HashedPtr to prevent address leaks Ke Sun 1 sibling, 0 replies; 4+ messages in thread From: Ke Sun @ 2026-05-12 8:55 UTC (permalink / raw) To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich Cc: rust-for-linux, Ke Sun The `impl_fmt_adapter_forward!` macro forwards `Pointer` for `Adapter<T>` by destructuring `self` into a local `t`, causing `{:p}` to print the address of that temporary stack variable rather than the actual pointer. Remove `Pointer` from the macro and provide a manual impl for `Adapter<&T>` that passes `self.0` directly. Signed-off-by: Ke Sun <sunke@kylinos.cn> --- rust/kernel/fmt.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs index 1e8725eb44ed7..aba9f3dbd3175 100644 --- a/rust/kernel/fmt.rs +++ b/rust/kernel/fmt.rs @@ -28,7 +28,13 @@ fn fmt(&self, f: &mut Formatter<'_>) -> Result { } use core::fmt::{Binary, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex}; -impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, Pointer, LowerExp, UpperExp); +impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp); + +impl<T: ?Sized + Pointer> Pointer for Adapter<&T> { + 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. /// -- 2.43.0 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v12 2/2] rust: fmt: route {:p} through HashedPtr to prevent address leaks 2026-05-12 8:55 [PATCH v12 0/2] rust: Add safe pointer formatting support Ke Sun 2026-05-12 8:55 ` [PATCH v12 1/2] rust: fmt: fix {:p} printing stack addresses Ke Sun @ 2026-05-12 8:55 ` Ke Sun 2026-05-12 9:26 ` Onur Özkan 1 sibling, 1 reply; 4+ messages in thread From: Ke Sun @ 2026-05-12 8:55 UTC (permalink / raw) To: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich Cc: rust-for-linux, Ke Sun 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 <sunke@kylinos.cn> --- rust/kernel/fmt.rs | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs index aba9f3dbd3175..19f422ff7f0ed 100644 --- a/rust/kernel/fmt.rs +++ b/rust/kernel/fmt.rs @@ -27,10 +27,95 @@ fn fmt(&self, f: &mut Formatter<'_>) -> Result { }; } -use core::fmt::{Binary, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex}; +use core::fmt::{Binary, LowerExp, LowerHex, Octal, UpperExp, UpperHex}; 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. 32 bytes is sufficient for all architectures: + // 64-bit: "0x" + 16 hex digits (zero-padded by `pointer_string`) = 18 bytes; + // 32-bit: "0x" + 8 hex digits (zero-padded by `pointer_string`) = 10 bytes. + // - The format string is valid and `%p` expects a pointer argument + // - `scnprintf` is safe to call with proper arguments + // + // Note: "0x" is added because Rust's `{:p}` includes a "0x" prefix, + // but the kernel's `%p` does not. + let len = unsafe { + crate::bindings::scnprintf( + buf.as_mut_ptr().cast(), + buf.len(), + c"0x%p".as_char_ptr(), + self.0.cast::<core::ffi::c_void>(), + ) + }; + + // SAFETY: + // - `buf` is a valid buffer (see above for size justification) + // - `scnprintf` returns the number of characters written (excluding null terminator), + // which is always non-negative (see `vsnprintf` and `vscnprintf` in lib/vsprintf.c) + // - `len` is bounded by `scnprintf` to at most `buf.len() - 1` + // - The format string "0x%p" produces ASCII hex digits and "0x" prefix, + // which are valid UTF-8 (ASCII is a strict subset of UTF-8) + let hashed_str = unsafe { core::str::from_utf8_unchecked(&buf[..len as usize]) }; + + // 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 { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + Pointer::fmt(&HashedPtr(*self), f) + } +} + +impl<T: ?Sized> Pointer for *mut T { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + <*const T as Pointer>::fmt(&(*self).cast_const(), f) + } +} + +impl<T: ?Sized> Pointer for &T { + 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 { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + <*const T as Pointer>::fmt(&core::ptr::from_ref(*self), f) + } +} + +// `Adapter<&T>` bridges our `Pointer` trait to `core::fmt::Pointer` +impl<T: Pointer> core::fmt::Pointer for Adapter<&T> { fn fmt(&self, f: &mut Formatter<'_>) -> Result { Pointer::fmt(self.0, f) } @@ -96,3 +181,86 @@ 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}, ); + +#[macros::kunit_tests(rust_kernel_fmt)] +mod tests { + use crate::{ + bindings, + prelude::fmt, + str::CString, // + }; + + /// A RAII guard that temporarily sets `no_hash_pointers` and restores it on drop. + /// + /// # Safety + /// + /// `no_hash_pointers` is a `__ro_after_init` global variable. KUnit tests run during + /// `kernel_init_freeable()` (init/main.c), before `mark_readonly()` makes the + /// `.data..ro_after_init` section read-only. At this point there are no concurrent + /// readers or writers, so it is safe to modify. + struct NoHashPointersGuard { + original: bool, + } + + impl NoHashPointersGuard { + /// Sets `no_hash_pointers` to `value` and returns a guard that will restore + /// the original value on drop. + /// + /// # Safety + /// + /// See struct-level documentation. + unsafe fn new(value: bool) -> Self { + // SAFETY: See `NoHashPointersGuard` safety documentation. + let original = unsafe { bindings::no_hash_pointers }; + // SAFETY: See `NoHashPointersGuard` safety documentation. + unsafe { bindings::no_hash_pointers = value }; + Self { original } + } + } + + impl Drop for NoHashPointersGuard { + fn drop(&mut self) { + // SAFETY: See `NoHashPointersGuard` safety documentation. + unsafe { bindings::no_hash_pointers = self.original }; + } + } + + #[cfg(CONFIG_64BIT)] + mod expected { + pub(super) const PTR_VALUE: usize = 0xffffffffdeadbeef; + pub(super) const HASHED_PREFIX: &str = "0x00000000"; + pub(super) const RAW_POINTER: &str = "0xffffffffdeadbeef"; + pub(super) const PADDED_LEFT: &str = "0xffffffffdeadbeef "; + } + + #[cfg(not(CONFIG_64BIT))] + mod expected { + pub(super) const PTR_VALUE: usize = 0xdeadbeef; + pub(super) const HASHED_PREFIX: &str = "0x"; + pub(super) const RAW_POINTER: &str = "0xdeadbeef"; + pub(super) const PADDED_LEFT: &str = "0xdeadbeef "; + } + + #[test] + fn test_ptr_formatting() -> core::result::Result<(), crate::error::Error> { + let ptr = expected::PTR_VALUE as *const u8; + + // SAFETY: See `NoHashPointersGuard` safety documentation. + let _hashed = unsafe { NoHashPointersGuard::new(false) }; + let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?; + let formatted = cstr.to_str()?; + assert!(formatted.starts_with(expected::HASHED_PREFIX)); + assert_ne!(formatted, expected::RAW_POINTER); + drop(_hashed); + + // SAFETY: See `NoHashPointersGuard` safety documentation. + let _guard = unsafe { NoHashPointersGuard::new(true) }; + let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?; + assert_eq!(cstr.to_str()?, expected::RAW_POINTER); + + let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?; + assert_eq!(cstr.to_str()?, expected::PADDED_LEFT); + + Ok(()) + } +} -- 2.43.0 ^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v12 2/2] rust: fmt: route {:p} through HashedPtr to prevent address leaks 2026-05-12 8:55 ` [PATCH v12 2/2] rust: fmt: route {:p} through HashedPtr to prevent address leaks Ke Sun @ 2026-05-12 9:26 ` Onur Özkan 0 siblings, 0 replies; 4+ messages in thread From: Onur Özkan @ 2026-05-12 9:26 UTC (permalink / raw) To: Ke Sun Cc: Miguel Ojeda, Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross, Danilo Krummrich, rust-for-linux On Tue, 12 May 2026 16:55:28 +0800 Ke Sun <sunke@kylinos.cn> 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 <sunke@kylinos.cn> > --- > rust/kernel/fmt.rs | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 170 insertions(+), 2 deletions(-) > > diff --git a/rust/kernel/fmt.rs b/rust/kernel/fmt.rs > index aba9f3dbd3175..19f422ff7f0ed 100644 > --- a/rust/kernel/fmt.rs > +++ b/rust/kernel/fmt.rs > @@ -27,10 +27,95 @@ fn fmt(&self, f: &mut Formatter<'_>) -> Result { > }; > } > > -use core::fmt::{Binary, LowerExp, LowerHex, Octal, Pointer, UpperExp, UpperHex}; > +use core::fmt::{Binary, LowerExp, LowerHex, Octal, UpperExp, UpperHex}; > 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. 32 bytes is sufficient for all architectures: > + // 64-bit: "0x" + 16 hex digits (zero-padded by `pointer_string`) = 18 bytes; > + // 32-bit: "0x" + 8 hex digits (zero-padded by `pointer_string`) = 10 bytes. > + // - The format string is valid and `%p` expects a pointer argument > + // - `scnprintf` is safe to call with proper arguments > + // > + // Note: "0x" is added because Rust's `{:p}` includes a "0x" prefix, > + // but the kernel's `%p` does not. > + let len = unsafe { > + crate::bindings::scnprintf( > + buf.as_mut_ptr().cast(), > + buf.len(), > + c"0x%p".as_char_ptr(), > + self.0.cast::<core::ffi::c_void>(), > + ) > + }; > + > + // SAFETY: > + // - `buf` is a valid buffer (see above for size justification) > + // - `scnprintf` returns the number of characters written (excluding null terminator), > + // which is always non-negative (see `vsnprintf` and `vscnprintf` in lib/vsprintf.c) > + // - `len` is bounded by `scnprintf` to at most `buf.len() - 1` > + // - The format string "0x%p" produces ASCII hex digits and "0x" prefix, > + // which are valid UTF-8 (ASCII is a strict subset of UTF-8) > + let hashed_str = unsafe { core::str::from_utf8_unchecked(&buf[..len as usize]) }; > + > + // 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 { > + fn fmt(&self, f: &mut Formatter<'_>) -> Result { > + Pointer::fmt(&HashedPtr(*self), f) > + } > +} > + > +impl<T: ?Sized> Pointer for *mut T { > + fn fmt(&self, f: &mut Formatter<'_>) -> Result { > + <*const T as Pointer>::fmt(&(*self).cast_const(), f) > + } > +} > + > +impl<T: ?Sized> Pointer for &T { > + 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 { > + fn fmt(&self, f: &mut Formatter<'_>) -> Result { > + <*const T as Pointer>::fmt(&core::ptr::from_ref(*self), f) > + } > +} > + > +// `Adapter<&T>` bridges our `Pointer` trait to `core::fmt::Pointer` > +impl<T: Pointer> core::fmt::Pointer for Adapter<&T> { > fn fmt(&self, f: &mut Formatter<'_>) -> Result { > Pointer::fmt(self.0, f) > } > @@ -96,3 +181,86 @@ 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}, > ); > + > +#[macros::kunit_tests(rust_kernel_fmt)] > +mod tests { > + use crate::{ > + bindings, > + prelude::fmt, > + str::CString, // > + }; > + > + /// A RAII guard that temporarily sets `no_hash_pointers` and restores it on drop. > + /// > + /// # Safety > + /// > + /// `no_hash_pointers` is a `__ro_after_init` global variable. KUnit tests run during > + /// `kernel_init_freeable()` (init/main.c), before `mark_readonly()` makes the > + /// `.data..ro_after_init` section read-only. At this point there are no concurrent > + /// readers or writers, so it is safe to modify. > + struct NoHashPointersGuard { > + original: bool, > + } > + > + impl NoHashPointersGuard { > + /// Sets `no_hash_pointers` to `value` and returns a guard that will restore > + /// the original value on drop. > + /// > + /// # Safety > + /// > + /// See struct-level documentation. This is not how we usually write safety contracts and comments. You can write invariants on the type and put the contract on the unsafe function then explain at the call site why using `unsafe` is sound. Most of the safety related texts in this patch look wrong. > + unsafe fn new(value: bool) -> Self { > + // SAFETY: See `NoHashPointersGuard` safety documentation. > + let original = unsafe { bindings::no_hash_pointers }; > + // SAFETY: See `NoHashPointersGuard` safety documentation. > + unsafe { bindings::no_hash_pointers = value }; > + Self { original } > + } > + } > + > + impl Drop for NoHashPointersGuard { > + fn drop(&mut self) { > + // SAFETY: See `NoHashPointersGuard` safety documentation. > + unsafe { bindings::no_hash_pointers = self.original }; > + } > + } > + > + #[cfg(CONFIG_64BIT)] > + mod expected { > + pub(super) const PTR_VALUE: usize = 0xffffffffdeadbeef; > + pub(super) const HASHED_PREFIX: &str = "0x00000000"; > + pub(super) const RAW_POINTER: &str = "0xffffffffdeadbeef"; > + pub(super) const PADDED_LEFT: &str = "0xffffffffdeadbeef "; > + } > + > + #[cfg(not(CONFIG_64BIT))] > + mod expected { > + pub(super) const PTR_VALUE: usize = 0xdeadbeef; > + pub(super) const HASHED_PREFIX: &str = "0x"; > + pub(super) const RAW_POINTER: &str = "0xdeadbeef"; > + pub(super) const PADDED_LEFT: &str = "0xdeadbeef "; > + } > + > + #[test] > + fn test_ptr_formatting() -> core::result::Result<(), crate::error::Error> { > + let ptr = expected::PTR_VALUE as *const u8; > + > + // SAFETY: See `NoHashPointersGuard` safety documentation. > + let _hashed = unsafe { NoHashPointersGuard::new(false) }; > + let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?; > + let formatted = cstr.to_str()?; > + assert!(formatted.starts_with(expected::HASHED_PREFIX)); > + assert_ne!(formatted, expected::RAW_POINTER); > + drop(_hashed); > + > + // SAFETY: See `NoHashPointersGuard` safety documentation. > + let _guard = unsafe { NoHashPointersGuard::new(true) }; > + let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?; > + assert_eq!(cstr.to_str()?, expected::RAW_POINTER); > + > + let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?; > + assert_eq!(cstr.to_str()?, expected::PADDED_LEFT); > + > + Ok(()) > + } > +} > > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-05-12 9:26 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-12 8:55 [PATCH v12 0/2] rust: Add safe pointer formatting support Ke Sun
2026-05-12 8:55 ` [PATCH v12 1/2] rust: fmt: fix {:p} printing stack addresses Ke Sun
2026-05-12 8:55 ` [PATCH v12 2/2] rust: fmt: route {:p} through HashedPtr to prevent address leaks Ke Sun
2026-05-12 9:26 ` Onur Özkan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox