| 1 | |
| 2 |
|
| 3 | |
| 4 | |
| 5 | |
| 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 | |
| 19 | |
| 20 | |
| 21 | |
| 22 | |
| 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 | |
| 53 | |
| 54 | |
| 55 | |
| 56 | |
| 57 | pub trait Pointer {
|
| 58 | |
| 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 | |
| 76 | |
| 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 | |
| 84 | |
| 85 | |
| 86 | |
| 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 | |
| 98 | let s = unsafe { core::str::from_utf8_unchecked(&buf[..len as usize]) };
|
| 99 |
|
| 100 | if f.sign_aware_zero_pad() {
|
| 101 | |
| 102 | f.write_str(s)
|
| 103 | } else {
|
| 104 | f.pad(s)
|
| 105 | }
|
| 106 | }
|
| 107 | }
|
| 108 |
|
| 109 | |
| 110 | |
| 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 | |
| 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 | |
| 155 | |
| 156 | |
| 157 | |
| 158 | |
| 159 | |
| 160 | |
| 161 | pub trait Display {
|
| 162 | |
| 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 | |
| 254 | |
| 255 | |
| 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 | |
| 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 | }
|