xref: /linux/rust/kernel/fmt.rs (revision 47f27155f17498fccb1f222f79089642337498a9)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Formatting utilities.
4 //!
5 //! This module is intended to be used in place of `core::fmt` in kernel code.
6 
7 use kernel::prelude::*;
8 
9 pub use core::fmt::{
10     Arguments,
11     Debug,
12     Error,
13     Formatter,
14     Result,
15     Write, //
16 };
17 
18 /// Internal adapter used to route and allow implementations of formatting traits for foreign types.
19 ///
20 /// It is inserted automatically by the [`fmt!`] macro and is not meant to be used directly.
21 ///
22 /// [`fmt!`]: crate::prelude::fmt!
23 #[doc(hidden)]
24 pub struct Adapter<T>(pub T);
25 
26 macro_rules! impl_fmt_adapter_forward {
27     ($($trait:ident),* $(,)?) => {
28         $(
29             impl<T: $trait> $trait for Adapter<T> {
30                 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
31                     let Self(t) = self;
32                     $trait::fmt(t, f)
33                 }
34             }
35         )*
36     };
37 }
38 
39 use core::fmt::{
40     Binary,
41     LowerExp,
42     LowerHex,
43     Octal,
44     UpperExp,
45     UpperHex, //
46 };
47 use core::ptr::NonNull;
48 impl_fmt_adapter_forward!(Debug, LowerHex, UpperHex, Octal, Binary, LowerExp, UpperExp);
49 
50 /// A copy of [`core::fmt::Pointer`] that allows implementing pointer formatting for foreign types.
51 ///
52 /// Together with the [`Adapter`] type and [`fmt!`] macro, it enables raw pointer formatting to be
53 /// intercepted and routed to [`HashedPtr`] (kernel's `%p` hashed format), preventing kernel address
54 /// leaks.
55 ///
56 /// [`fmt!`]: crate::prelude::fmt!
57 pub trait Pointer {
58     /// Same as [`core::fmt::Pointer::fmt`].
59     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
60 }
61 
62 /// A wrapper for pointers that formats them using kernel's `%p` format specifier.
63 ///
64 /// By default, `%p` prints a hashed representation of the pointer address to prevent kernel address
65 /// leaks. When the `no_hash_pointers` kernel command-line parameter is enabled, the real address is
66 /// printed instead (for debugging purposes).
67 pub struct HashedPtr<T: ?Sized>(pub *const T);
68 
69 impl<T: ?Sized> Pointer for HashedPtr<T> {
70     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
71         use crate::str::CStrExt as _;
72 
73         let mut buf = [0u8; 32];
74 
75         // Use `%#0*p` for the `0x` prefix and zero-padding; `+2` compensates for
76         // the prefix counting toward the field width.
77         let default_width = (2 * size_of::<usize>() + 2) as c_int;
78         let width = match (f.sign_aware_zero_pad(), f.width()) {
79             (true, Some(w)) if w > 0 => w.min(buf.len() - 1) as c_int,
80             _ => default_width,
81         };
82 
83         // SAFETY: `buf` is a valid, writable 32-byte buffer, sufficient for
84         // all architectures (max 19 bytes for 64-bit under the default width).
85         // The format string is null-terminated; `width` (c_int) and pointer
86         // match the `%*` and `%p` specifiers.
87         let len = unsafe {
88             crate::bindings::scnprintf(
89                 buf.as_mut_ptr().cast(),
90                 buf.len(),
91                 c"%#0*p".as_char_ptr(),
92                 width,
93                 self.0.cast::<c_void>(),
94             )
95         };
96 
97         // SAFETY: `%#0*p` produces only ASCII, which is valid UTF-8.
98         let s = unsafe { core::str::from_utf8_unchecked(&buf[..len as usize]) };
99 
100         if f.sign_aware_zero_pad() {
101             // `scnprintf` already applied the width and zero-padding via `%#0*p`.
102             f.write_str(s)
103         } else {
104             f.pad(s)
105         }
106     }
107 }
108 
109 // Raw pointers are formatted via `HashedPtr` (kernel `%p`: hashed by default, plain with
110 // `no_hash_pointers`).
111 impl<T: ?Sized> Pointer for *const T {
112     #[inline]
113     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
114         Pointer::fmt(&HashedPtr(*self), f)
115     }
116 }
117 
118 impl<T: ?Sized> Pointer for *mut T {
119     #[inline]
120     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
121         Pointer::fmt(&HashedPtr(*self), f)
122     }
123 }
124 
125 impl<T: ?Sized> Pointer for &T {
126     #[inline]
127     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
128         Pointer::fmt(&HashedPtr(*self), f)
129     }
130 }
131 
132 impl<T: ?Sized> Pointer for &mut T {
133     #[inline]
134     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
135         Pointer::fmt(&HashedPtr(core::ptr::from_ref(*self)), f)
136     }
137 }
138 
139 impl<T: ?Sized> Pointer for NonNull<T> {
140     #[inline]
141     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
142         Pointer::fmt(&HashedPtr(self.as_ptr()), f)
143     }
144 }
145 
146 // `Adapter<&T>` bridges our `Pointer` trait to `core::fmt::Pointer`
147 impl<T: Pointer> core::fmt::Pointer for Adapter<&T> {
148     #[inline]
149     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
150         Pointer::fmt(self.0, f)
151     }
152 }
153 
154 /// A copy of [`core::fmt::Display`] that allows us to implement it for foreign types.
155 ///
156 /// Types should implement this trait rather than [`core::fmt::Display`]. Together with the
157 /// [`Adapter`] type and [`fmt!`] macro, it allows for formatting foreign types (e.g. types from
158 /// core) which do not implement [`core::fmt::Display`] directly.
159 ///
160 /// [`fmt!`]: crate::prelude::fmt!
161 pub trait Display {
162     /// Same as [`core::fmt::Display::fmt`].
163     fn fmt(&self, f: &mut Formatter<'_>) -> Result;
164 }
165 
166 impl<T: ?Sized + Display> Display for &T {
167     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
168         Display::fmt(*self, f)
169     }
170 }
171 
172 impl<T: ?Sized + Display> core::fmt::Display for Adapter<&T> {
173     fn fmt(&self, f: &mut Formatter<'_>) -> Result {
174         let Self(t) = self;
175         Display::fmt(t, f)
176     }
177 }
178 
179 macro_rules! impl_display_forward {
180     ($(
181         $( { $($generics:tt)* } )? $ty:ty $( { where $($where:tt)* } )?
182     ),* $(,)?) => {
183         $(
184             impl$($($generics)*)? Display for $ty $(where $($where)*)? {
185                 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
186                     core::fmt::Display::fmt(self, f)
187                 }
188             }
189         )*
190     };
191 }
192 
193 impl_display_forward!(
194     bool,
195     char,
196     core::panic::PanicInfo<'_>,
197     Arguments<'_>,
198     i128,
199     i16,
200     i32,
201     i64,
202     i8,
203     isize,
204     str,
205     u128,
206     u16,
207     u32,
208     u64,
209     u8,
210     usize,
211     {<T: ?Sized>} crate::sync::Arc<T> {where crate::sync::Arc<T>: core::fmt::Display},
212     {<T: ?Sized>} crate::sync::UniqueArc<T> {where crate::sync::UniqueArc<T>: core::fmt::Display},
213 );
214 
215 #[macros::kunit_tests(rust_kernel_fmt)]
216 mod tests {
217     use crate::{
218         bindings,
219         prelude::fmt,
220         str::CString, //
221     };
222 
223     #[cfg(CONFIG_64BIT)]
224     mod expected {
225         pub(super) const PTR_VALUE: usize = 0xffffffffdeadbeef;
226         pub(super) const PTR_VAL_NO_CRNG: &str = "(____ptrval____)";
227         pub(super) const HASHED_PREFIX: &str = "0x00000000";
228         pub(super) const RAW_POINTER: &str = "0xffffffffdeadbeef";
229         pub(super) const PADDED_RIGHT: &str = "      0xffffffffdeadbeef";
230         pub(super) const ZERO_PADDED: &str = "0x000000ffffffffdeadbeef";
231         pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = "      ";
232         pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000";
233         pub(super) const CLAMPED: &str = "0x0000000000000ffffffffdeadbeef";
234     }
235 
236     #[cfg(not(CONFIG_64BIT))]
237     mod expected {
238         pub(super) const PTR_VALUE: usize = 0xdeadbeef;
239         pub(super) const PTR_VAL_NO_CRNG: &str = "(ptrval)";
240         pub(super) const HASHED_PREFIX: &str = "0x";
241         pub(super) const RAW_POINTER: &str = "0xdeadbeef";
242         pub(super) const PADDED_RIGHT: &str = "              0xdeadbeef";
243         pub(super) const ZERO_PADDED: &str = "0x00000000000000deadbeef";
244         pub(super) const HASHED_PADDED_RIGHT_PREFIX: &str = "              ";
245         pub(super) const HASHED_ZERO_PADDED_PREFIX: &str = "0x00000000000000";
246         pub(super) const CLAMPED: &str = "0x0000000000000000000000deadbeef";
247     }
248 
249     #[test]
250     fn test_ptr_formatting() -> core::result::Result<(), crate::error::Error> {
251         let ptr: *const u8 = core::ptr::without_provenance(expected::PTR_VALUE);
252 
253         // SAFETY: `no_hash_pointers` is a global variable that is never concurrently modified —
254         // KUnit tests may run at boot (before `mark_readonly()`) or manually afterwards (when the
255         // variable is read-only). Reading is always safe.
256         let no_hash = unsafe { bindings::no_hash_pointers };
257 
258         if no_hash {
259             let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?;
260             assert_eq!(cstr.to_str()?, expected::RAW_POINTER);
261 
262             let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?;
263             assert_eq!(cstr.to_str()?, expected::PADDED_RIGHT);
264 
265             let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?;
266             assert_eq!(cstr.to_str()?, expected::ZERO_PADDED);
267 
268             let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?;
269             assert_eq!(cstr.to_str()?, expected::CLAMPED);
270         } else {
271             let cstr = CString::try_from_fmt(fmt!("{:p}", ptr))?;
272             let formatted = cstr.to_str()?;
273             // If the RNG is not yet ready, `%p` falls back to a placeholder.
274             if formatted == expected::PTR_VAL_NO_CRNG {
275                 return Ok(());
276             }
277             assert!(formatted.starts_with(expected::HASHED_PREFIX));
278             assert_ne!(formatted, expected::RAW_POINTER);
279 
280             let cstr = CString::try_from_fmt(fmt!("{:>24p}", ptr))?;
281             assert!(cstr
282                 .to_str()?
283                 .starts_with(expected::HASHED_PADDED_RIGHT_PREFIX));
284 
285             let cstr = CString::try_from_fmt(fmt!("{:024p}", ptr))?;
286             assert!(cstr
287                 .to_str()?
288                 .starts_with(expected::HASHED_ZERO_PADDED_PREFIX));
289 
290             let cstr = CString::try_from_fmt(fmt!("{:0100p}", ptr))?;
291             let output = cstr.to_str()?;
292             assert!(output.starts_with("0x"));
293             assert!(!output[2..].chars().all(|c| c == '0'));
294         }
295 
296         Ok(())
297     }
298 }
299