1 // Copyright 2024 The Fuchsia Authors 2 // 3 // Licensed under the 2-Clause BSD License <LICENSE-BSD or 4 // https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0 5 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT 6 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. 7 // This file may not be copied, modified, or distributed except according to 8 // those terms. 9 10 //! Types related to error reporting. 11 //! 12 //! ## Single failure mode errors 13 //! 14 //! Generally speaking, zerocopy's conversions may fail for one of up to three 15 //! reasons: 16 //! - [`AlignmentError`]: the conversion source was improperly aligned 17 //! - [`SizeError`]: the conversion source was of incorrect size 18 //! - [`ValidityError`]: the conversion source contained invalid data 19 //! 20 //! Methods that only have one failure mode, like 21 //! [`FromBytes::read_from_bytes`], return that mode's corresponding error type 22 //! directly. 23 //! 24 //! ## Compound errors 25 //! 26 //! Conversion methods that have either two or three possible failure modes 27 //! return one of these error types: 28 //! - [`CastError`]: the error type of reference conversions 29 //! - [`TryCastError`]: the error type of fallible reference conversions 30 //! - [`TryReadError`]: the error type of fallible read conversions 31 //! 32 //! ## [`Unaligned`] destination types 33 //! 34 //! For [`Unaligned`] destination types, alignment errors are impossible. All 35 //! compound error types support infallibly discarding the alignment error via 36 //! [`From`] so long as `Dst: Unaligned`. For example, see [`<SizeError as 37 //! From<ConvertError>>::from`][size-error-from]. 38 //! 39 //! [size-error-from]: struct.SizeError.html#method.from-1 40 //! 41 //! ## Accessing the conversion source 42 //! 43 //! All error types provide an `into_src` method that converts the error into 44 //! the source value underlying the failed conversion. 45 //! 46 //! ## Display formatting 47 //! 48 //! All error types provide a `Display` implementation that produces a 49 //! human-readable error message. When `debug_assertions` are enabled, these 50 //! error messages are verbose and may include potentially sensitive 51 //! information, including: 52 //! 53 //! - the names of the involved types 54 //! - the sizes of the involved types 55 //! - the addresses of the involved types 56 //! - the contents of the involved types 57 //! 58 //! When `debug_assertions` are disabled (as is default for `release` builds), 59 //! such potentially sensitive information is excluded. 60 //! 61 //! In the future, we may support manually configuring this behavior. If you are 62 //! interested in this feature, [let us know on GitHub][issue-1457] so we know 63 //! to prioritize it. 64 //! 65 //! [issue-1457]: https://github.com/google/zerocopy/issues/1457 66 //! 67 //! ## Validation order 68 //! 69 //! Our conversion methods typically check alignment, then size, then bit 70 //! validity. However, we do not guarantee that this is always the case, and 71 //! this behavior may change between releases. 72 //! 73 //! ## `Send`, `Sync`, and `'static` 74 //! 75 //! Our error types are `Send`, `Sync`, and `'static` when their `Src` parameter 76 //! is `Send`, `Sync`, or `'static`, respectively. This can cause issues when an 77 //! error is sent or synchronized across threads; e.g.: 78 //! 79 //! ```compile_fail,E0515 80 //! use zerocopy::*; 81 //! 82 //! let result: SizeError<&[u8], u32> = std::thread::spawn(|| { 83 //! let source = &mut [0u8, 1, 2][..]; 84 //! // Try (and fail) to read a `u32` from `source`. 85 //! u32::read_from_bytes(source).unwrap_err() 86 //! }).join().unwrap(); 87 //! ``` 88 //! 89 //! To work around this, use [`map_src`][CastError::map_src] to convert the 90 //! source parameter to an unproblematic type; e.g.: 91 //! 92 //! ``` 93 //! use zerocopy::*; 94 //! 95 //! let result: SizeError<(), u32> = std::thread::spawn(|| { 96 //! let source = &mut [0u8, 1, 2][..]; 97 //! // Try (and fail) to read a `u32` from `source`. 98 //! u32::read_from_bytes(source).unwrap_err() 99 //! // Erase the error source. 100 //! .map_src(drop) 101 //! }).join().unwrap(); 102 //! ``` 103 //! 104 //! Alternatively, use `.to_string()` to eagerly convert the error into a 105 //! human-readable message; e.g.: 106 //! 107 //! ``` 108 //! use zerocopy::*; 109 //! 110 //! let result: Result<u32, String> = std::thread::spawn(|| { 111 //! let source = &mut [0u8, 1, 2][..]; 112 //! // Try (and fail) to read a `u32` from `source`. 113 //! u32::read_from_bytes(source) 114 //! // Eagerly render the error message. 115 //! .map_err(|err| err.to_string()) 116 //! }).join().unwrap(); 117 //! ``` 118 use core::{ 119 convert::Infallible, 120 fmt::{self, Debug, Write}, 121 ops::Deref, 122 }; 123 124 #[cfg(zerocopy_core_error_1_81_0)] 125 use core::error::Error; 126 #[cfg(all(not(zerocopy_core_error_1_81_0), any(feature = "std", test)))] 127 use std::error::Error; 128 129 use crate::{util::SendSyncPhantomData, KnownLayout, TryFromBytes, Unaligned}; 130 #[cfg(doc)] 131 use crate::{FromBytes, Ref}; 132 133 /// Zerocopy's generic error type. 134 /// 135 /// Generally speaking, zerocopy's conversions may fail for one of up to three 136 /// reasons: 137 /// - [`AlignmentError`]: the conversion source was improperly aligned 138 /// - [`SizeError`]: the conversion source was of incorrect size 139 /// - [`ValidityError`]: the conversion source contained invalid data 140 /// 141 /// However, not all conversions produce all errors. For instance, 142 /// [`FromBytes::ref_from_bytes`] may fail due to alignment or size issues, but 143 /// not validity issues. This generic error type captures these 144 /// (im)possibilities via parameterization: `A` is parameterized with 145 /// [`AlignmentError`], `S` is parameterized with [`SizeError`], and `V` is 146 /// parameterized with [`Infallible`]. 147 /// 148 /// Zerocopy never uses this type directly in its API. Rather, we provide three 149 /// pre-parameterized aliases: 150 /// - [`CastError`]: the error type of reference conversions 151 /// - [`TryCastError`]: the error type of fallible reference conversions 152 /// - [`TryReadError`]: the error type of fallible read conversions 153 #[derive(PartialEq, Eq)] 154 pub enum ConvertError<A, S, V> { 155 /// The conversion source was improperly aligned. 156 Alignment(A), 157 /// The conversion source was of incorrect size. 158 Size(S), 159 /// The conversion source contained invalid data. 160 Validity(V), 161 } 162 163 impl<Src, Dst: ?Sized + Unaligned, S, V> From<ConvertError<AlignmentError<Src, Dst>, S, V>> 164 for ConvertError<Infallible, S, V> 165 { 166 /// Infallibly discards the alignment error from this `ConvertError` since 167 /// `Dst` is unaligned. 168 /// 169 /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment 170 /// error. This method permits discarding that alignment error infallibly 171 /// and replacing it with [`Infallible`]. 172 /// 173 /// [`Dst: Unaligned`]: crate::Unaligned 174 /// 175 /// # Examples 176 /// 177 /// ``` 178 /// use core::convert::Infallible; 179 /// use zerocopy::*; 180 /// # use zerocopy_derive::*; 181 /// 182 /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)] 183 /// #[repr(C, packed)] 184 /// struct Bools { 185 /// one: bool, 186 /// two: bool, 187 /// many: [bool], 188 /// } 189 /// 190 /// impl Bools { 191 /// fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> { 192 /// // Since `Bools: Unaligned`, we can infallibly discard 193 /// // the alignment error. 194 /// Bools::try_ref_from_bytes(bytes).map_err(Into::into) 195 /// } 196 /// } 197 /// ``` 198 #[inline] from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V>199 fn from(err: ConvertError<AlignmentError<Src, Dst>, S, V>) -> ConvertError<Infallible, S, V> { 200 match err { 201 ConvertError::Alignment(e) => ConvertError::Alignment(Infallible::from(e)), 202 ConvertError::Size(e) => ConvertError::Size(e), 203 ConvertError::Validity(e) => ConvertError::Validity(e), 204 } 205 } 206 } 207 208 impl<A: fmt::Debug, S: fmt::Debug, V: fmt::Debug> fmt::Debug for ConvertError<A, S, V> { 209 #[inline] fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 211 match self { 212 Self::Alignment(e) => f.debug_tuple("Alignment").field(e).finish(), 213 Self::Size(e) => f.debug_tuple("Size").field(e).finish(), 214 Self::Validity(e) => f.debug_tuple("Validity").field(e).finish(), 215 } 216 } 217 } 218 219 /// Produces a human-readable error message. 220 /// 221 /// The message differs between debug and release builds. When 222 /// `debug_assertions` are enabled, this message is verbose and includes 223 /// potentially sensitive information. 224 impl<A: fmt::Display, S: fmt::Display, V: fmt::Display> fmt::Display for ConvertError<A, S, V> { 225 #[inline] fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 227 match self { 228 Self::Alignment(e) => e.fmt(f), 229 Self::Size(e) => e.fmt(f), 230 Self::Validity(e) => e.fmt(f), 231 } 232 } 233 } 234 235 #[cfg(any(zerocopy_core_error_1_81_0, feature = "std", test))] 236 #[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))] 237 impl<A, S, V> Error for ConvertError<A, S, V> 238 where 239 A: fmt::Display + fmt::Debug, 240 S: fmt::Display + fmt::Debug, 241 V: fmt::Display + fmt::Debug, 242 { 243 } 244 245 /// The error emitted if the conversion source is improperly aligned. 246 #[derive(PartialEq, Eq)] 247 pub struct AlignmentError<Src, Dst: ?Sized> { 248 /// The source value involved in the conversion. 249 src: Src, 250 /// The inner destination type inolved in the conversion. 251 /// 252 /// INVARIANT: An `AlignmentError` may only be constructed if `Dst`'s 253 /// alignment requirement is greater than one. 254 dst: SendSyncPhantomData<Dst>, 255 } 256 257 impl<Src, Dst: ?Sized> AlignmentError<Src, Dst> { 258 /// # Safety 259 /// 260 /// The caller must ensure that `Dst`'s alignment requirement is greater 261 /// than one. new_unchecked(src: Src) -> Self262 pub(crate) unsafe fn new_unchecked(src: Src) -> Self { 263 // INVARIANT: The caller guarantees that `Dst`'s alignment requirement 264 // is greater than one. 265 Self { src, dst: SendSyncPhantomData::default() } 266 } 267 268 /// Produces the source underlying the failed conversion. 269 #[inline] into_src(self) -> Src270 pub fn into_src(self) -> Src { 271 self.src 272 } 273 with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst>274 pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> AlignmentError<NewSrc, Dst> { 275 // INVARIANT: `with_src` doesn't change the type of `Dst`, so the 276 // invariant that `Dst`'s alignment requirement is greater than one is 277 // preserved. 278 AlignmentError { src: new_src, dst: SendSyncPhantomData::default() } 279 } 280 281 /// Maps the source value associated with the conversion error. 282 /// 283 /// This can help mitigate [issues with `Send`, `Sync` and `'static` 284 /// bounds][self#send-sync-and-static]. 285 /// 286 /// # Examples 287 /// 288 /// ``` 289 /// use zerocopy::*; 290 /// 291 /// let unaligned = Unalign::new(0u16); 292 /// 293 /// // Attempt to deref `unaligned`. This might fail with an alignment error. 294 /// let maybe_n: Result<&u16, AlignmentError<&Unalign<u16>, u16>> = unaligned.try_deref(); 295 /// 296 /// // Map the error's source to its address as a usize. 297 /// let maybe_n: Result<&u16, AlignmentError<usize, u16>> = maybe_n.map_err(|err| { 298 /// err.map_src(|src| src as *const _ as usize) 299 /// }); 300 /// ``` 301 #[inline] map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst>302 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> AlignmentError<NewSrc, Dst> { 303 AlignmentError { src: f(self.src), dst: SendSyncPhantomData::default() } 304 } 305 into<S, V>(self) -> ConvertError<Self, S, V>306 pub(crate) fn into<S, V>(self) -> ConvertError<Self, S, V> { 307 ConvertError::Alignment(self) 308 } 309 310 /// Format extra details for a verbose, human-readable error message. 311 /// 312 /// This formatting may include potentially sensitive information. display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result where Src: Deref, Dst: KnownLayout,313 fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 314 where 315 Src: Deref, 316 Dst: KnownLayout, 317 { 318 #[allow(clippy::as_conversions)] 319 let addr = self.src.deref() as *const _ as *const (); 320 let addr_align = 2usize.pow((crate::util::AsAddress::addr(addr)).trailing_zeros()); 321 322 f.write_str("\n\nSource type: ")?; 323 f.write_str(core::any::type_name::<Src>())?; 324 325 f.write_str("\nSource address: ")?; 326 addr.fmt(f)?; 327 f.write_str(" (a multiple of ")?; 328 addr_align.fmt(f)?; 329 f.write_str(")")?; 330 331 f.write_str("\nDestination type: ")?; 332 f.write_str(core::any::type_name::<Dst>())?; 333 334 f.write_str("\nDestination alignment: ")?; 335 <Dst as KnownLayout>::LAYOUT.align.get().fmt(f)?; 336 337 Ok(()) 338 } 339 } 340 341 impl<Src, Dst: ?Sized + Unaligned> From<AlignmentError<Src, Dst>> for Infallible { 342 #[inline(always)] from(_: AlignmentError<Src, Dst>) -> Infallible343 fn from(_: AlignmentError<Src, Dst>) -> Infallible { 344 // SAFETY: `AlignmentError`s can only be constructed when `Dst`'s 345 // alignment requirement is greater than one. In this block, `Dst: 346 // Unaligned`, which means that its alignment requirement is equal to 347 // one. Thus, it's not possible to reach here at runtime. 348 unsafe { core::hint::unreachable_unchecked() } 349 } 350 } 351 352 #[cfg(test)] 353 impl<Src, Dst> AlignmentError<Src, Dst> { 354 // A convenience constructor so that test code doesn't need to write 355 // `unsafe`. new_checked(src: Src) -> AlignmentError<Src, Dst>356 fn new_checked(src: Src) -> AlignmentError<Src, Dst> { 357 assert_ne!(core::mem::align_of::<Dst>(), 1); 358 // SAFETY: The preceding assertion guarantees that `Dst`'s alignment 359 // requirement is greater than one. 360 unsafe { AlignmentError::new_unchecked(src) } 361 } 362 } 363 364 impl<Src, Dst: ?Sized> fmt::Debug for AlignmentError<Src, Dst> { 365 #[inline] fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 367 f.debug_struct("AlignmentError").finish() 368 } 369 } 370 371 /// Produces a human-readable error message. 372 /// 373 /// The message differs between debug and release builds. When 374 /// `debug_assertions` are enabled, this message is verbose and includes 375 /// potentially sensitive information. 376 impl<Src, Dst: ?Sized> fmt::Display for AlignmentError<Src, Dst> 377 where 378 Src: Deref, 379 Dst: KnownLayout, 380 { 381 #[inline] fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result382 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 383 f.write_str("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.")?; 384 385 if cfg!(debug_assertions) { 386 self.display_verbose_extras(f) 387 } else { 388 Ok(()) 389 } 390 } 391 } 392 393 #[cfg(any(zerocopy_core_error_1_81_0, feature = "std", test))] 394 #[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))] 395 impl<Src, Dst: ?Sized> Error for AlignmentError<Src, Dst> 396 where 397 Src: Deref, 398 Dst: KnownLayout, 399 { 400 } 401 402 impl<Src, Dst: ?Sized, S, V> From<AlignmentError<Src, Dst>> 403 for ConvertError<AlignmentError<Src, Dst>, S, V> 404 { 405 #[inline(always)] from(err: AlignmentError<Src, Dst>) -> Self406 fn from(err: AlignmentError<Src, Dst>) -> Self { 407 Self::Alignment(err) 408 } 409 } 410 411 /// The error emitted if the conversion source is of incorrect size. 412 #[derive(PartialEq, Eq)] 413 pub struct SizeError<Src, Dst: ?Sized> { 414 /// The source value involved in the conversion. 415 src: Src, 416 /// The inner destination type inolved in the conversion. 417 dst: SendSyncPhantomData<Dst>, 418 } 419 420 impl<Src, Dst: ?Sized> SizeError<Src, Dst> { new(src: Src) -> Self421 pub(crate) fn new(src: Src) -> Self { 422 Self { src, dst: SendSyncPhantomData::default() } 423 } 424 425 /// Produces the source underlying the failed conversion. 426 #[inline] into_src(self) -> Src427 pub fn into_src(self) -> Src { 428 self.src 429 } 430 431 /// Sets the source value associated with the conversion error. with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst>432 pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> SizeError<NewSrc, Dst> { 433 SizeError { src: new_src, dst: SendSyncPhantomData::default() } 434 } 435 436 /// Maps the source value associated with the conversion error. 437 /// 438 /// This can help mitigate [issues with `Send`, `Sync` and `'static` 439 /// bounds][self#send-sync-and-static]. 440 /// 441 /// # Examples 442 /// 443 /// ``` 444 /// use zerocopy::*; 445 /// 446 /// let source: [u8; 3] = [0, 1, 2]; 447 /// 448 /// // Try to read a `u32` from `source`. This will fail because there are insufficient 449 /// // bytes in `source`. 450 /// let maybe_u32: Result<u32, SizeError<&[u8], u32>> = u32::read_from_bytes(&source[..]); 451 /// 452 /// // Map the error's source to its size. 453 /// let maybe_u32: Result<u32, SizeError<usize, u32>> = maybe_u32.map_err(|err| { 454 /// err.map_src(|src| src.len()) 455 /// }); 456 /// ``` 457 #[inline] map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst>458 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> SizeError<NewSrc, Dst> { 459 SizeError { src: f(self.src), dst: SendSyncPhantomData::default() } 460 } 461 462 /// Sets the destination type associated with the conversion error. with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst>463 pub(crate) fn with_dst<NewDst: ?Sized>(self) -> SizeError<Src, NewDst> { 464 SizeError { src: self.src, dst: SendSyncPhantomData::default() } 465 } 466 467 /// Converts the error into a general [`ConvertError`]. into<A, V>(self) -> ConvertError<A, Self, V>468 pub(crate) fn into<A, V>(self) -> ConvertError<A, Self, V> { 469 ConvertError::Size(self) 470 } 471 472 /// Format extra details for a verbose, human-readable error message. 473 /// 474 /// This formatting may include potentially sensitive information. display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result where Src: Deref, Dst: KnownLayout,475 fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 476 where 477 Src: Deref, 478 Dst: KnownLayout, 479 { 480 // include the source type 481 f.write_str("\nSource type: ")?; 482 f.write_str(core::any::type_name::<Src>())?; 483 484 // include the source.deref() size 485 let src_size = core::mem::size_of_val(&*self.src); 486 f.write_str("\nSource size: ")?; 487 src_size.fmt(f)?; 488 f.write_str(" byte")?; 489 if src_size != 1 { 490 f.write_char('s')?; 491 } 492 493 // if `Dst` is `Sized`, include the `Dst` size 494 if let crate::SizeInfo::Sized { size } = Dst::LAYOUT.size_info { 495 f.write_str("\nDestination size: ")?; 496 size.fmt(f)?; 497 f.write_str(" byte")?; 498 if size != 1 { 499 f.write_char('s')?; 500 } 501 } 502 503 // include the destination type 504 f.write_str("\nDestination type: ")?; 505 f.write_str(core::any::type_name::<Dst>())?; 506 507 Ok(()) 508 } 509 } 510 511 impl<Src, Dst: ?Sized> fmt::Debug for SizeError<Src, Dst> { 512 #[inline] fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result513 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 514 f.debug_struct("SizeError").finish() 515 } 516 } 517 518 /// Produces a human-readable error message. 519 /// 520 /// The message differs between debug and release builds. When 521 /// `debug_assertions` are enabled, this message is verbose and includes 522 /// potentially sensitive information. 523 impl<Src, Dst: ?Sized> fmt::Display for SizeError<Src, Dst> 524 where 525 Src: Deref, 526 Dst: KnownLayout, 527 { 528 #[inline] fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result529 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 530 f.write_str("The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.")?; 531 if cfg!(debug_assertions) { 532 f.write_str("\n")?; 533 self.display_verbose_extras(f)?; 534 } 535 Ok(()) 536 } 537 } 538 539 #[cfg(any(zerocopy_core_error_1_81_0, feature = "std", test))] 540 #[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))] 541 impl<Src, Dst: ?Sized> Error for SizeError<Src, Dst> 542 where 543 Src: Deref, 544 Dst: KnownLayout, 545 { 546 } 547 548 impl<Src, Dst: ?Sized, A, V> From<SizeError<Src, Dst>> for ConvertError<A, SizeError<Src, Dst>, V> { 549 #[inline(always)] from(err: SizeError<Src, Dst>) -> Self550 fn from(err: SizeError<Src, Dst>) -> Self { 551 Self::Size(err) 552 } 553 } 554 555 /// The error emitted if the conversion source contains invalid data. 556 #[derive(PartialEq, Eq)] 557 pub struct ValidityError<Src, Dst: ?Sized + TryFromBytes> { 558 /// The source value involved in the conversion. 559 pub(crate) src: Src, 560 /// The inner destination type inolved in the conversion. 561 dst: SendSyncPhantomData<Dst>, 562 } 563 564 impl<Src, Dst: ?Sized + TryFromBytes> ValidityError<Src, Dst> { new(src: Src) -> Self565 pub(crate) fn new(src: Src) -> Self { 566 Self { src, dst: SendSyncPhantomData::default() } 567 } 568 569 /// Produces the source underlying the failed conversion. 570 #[inline] into_src(self) -> Src571 pub fn into_src(self) -> Src { 572 self.src 573 } 574 575 /// Maps the source value associated with the conversion error. 576 /// 577 /// This can help mitigate [issues with `Send`, `Sync` and `'static` 578 /// bounds][self#send-sync-and-static]. 579 /// 580 /// # Examples 581 /// 582 /// ``` 583 /// use zerocopy::*; 584 /// 585 /// let source: u8 = 42; 586 /// 587 /// // Try to transmute the `source` to a `bool`. This will fail. 588 /// let maybe_bool: Result<bool, ValidityError<u8, bool>> = try_transmute!(source); 589 /// 590 /// // Drop the error's source. 591 /// let maybe_bool: Result<bool, ValidityError<(), bool>> = maybe_bool.map_err(|err| { 592 /// err.map_src(drop) 593 /// }); 594 /// ``` 595 #[inline] map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> ValidityError<NewSrc, Dst>596 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> ValidityError<NewSrc, Dst> { 597 ValidityError { src: f(self.src), dst: SendSyncPhantomData::default() } 598 } 599 600 /// Converts the error into a general [`ConvertError`]. into<A, S>(self) -> ConvertError<A, S, Self>601 pub(crate) fn into<A, S>(self) -> ConvertError<A, S, Self> { 602 ConvertError::Validity(self) 603 } 604 605 /// Format extra details for a verbose, human-readable error message. 606 /// 607 /// This formatting may include potentially sensitive information. display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result where Src: Deref, Dst: KnownLayout,608 fn display_verbose_extras(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 609 where 610 Src: Deref, 611 Dst: KnownLayout, 612 { 613 f.write_str("Destination type: ")?; 614 f.write_str(core::any::type_name::<Dst>())?; 615 Ok(()) 616 } 617 } 618 619 impl<Src, Dst: ?Sized + TryFromBytes> fmt::Debug for ValidityError<Src, Dst> { 620 #[inline] fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result621 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 622 f.debug_struct("ValidityError").finish() 623 } 624 } 625 626 /// Produces a human-readable error message. 627 /// 628 /// The message differs between debug and release builds. When 629 /// `debug_assertions` are enabled, this message is verbose and includes 630 /// potentially sensitive information. 631 impl<Src, Dst: ?Sized> fmt::Display for ValidityError<Src, Dst> 632 where 633 Src: Deref, 634 Dst: KnownLayout + TryFromBytes, 635 { 636 #[inline] fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result637 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 638 f.write_str("The conversion failed because the source bytes are not a valid value of the destination type.")?; 639 if cfg!(debug_assertions) { 640 f.write_str("\n\n")?; 641 self.display_verbose_extras(f)?; 642 } 643 Ok(()) 644 } 645 } 646 647 #[cfg(any(zerocopy_core_error_1_81_0, feature = "std", test))] 648 #[cfg_attr(doc_cfg, doc(cfg(all(rust = "1.81.0", feature = "std"))))] 649 impl<Src, Dst: ?Sized> Error for ValidityError<Src, Dst> 650 where 651 Src: Deref, 652 Dst: KnownLayout + TryFromBytes, 653 { 654 } 655 656 impl<Src, Dst: ?Sized + TryFromBytes, A, S> From<ValidityError<Src, Dst>> 657 for ConvertError<A, S, ValidityError<Src, Dst>> 658 { 659 #[inline(always)] from(err: ValidityError<Src, Dst>) -> Self660 fn from(err: ValidityError<Src, Dst>) -> Self { 661 Self::Validity(err) 662 } 663 } 664 665 /// The error type of reference conversions. 666 /// 667 /// Reference conversions, like [`FromBytes::ref_from_bytes`] may emit 668 /// [alignment](AlignmentError) and [size](SizeError) errors. 669 // Bounds on generic parameters are not enforced in type aliases, but they do 670 // appear in rustdoc. 671 #[allow(type_alias_bounds)] 672 pub type CastError<Src, Dst: ?Sized> = 673 ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, Infallible>; 674 675 impl<Src, Dst: ?Sized> CastError<Src, Dst> { 676 /// Produces the source underlying the failed conversion. 677 #[inline] into_src(self) -> Src678 pub fn into_src(self) -> Src { 679 match self { 680 Self::Alignment(e) => e.src, 681 Self::Size(e) => e.src, 682 Self::Validity(i) => match i {}, 683 } 684 } 685 686 /// Sets the source value associated with the conversion error. with_src<NewSrc>(self, new_src: NewSrc) -> CastError<NewSrc, Dst>687 pub(crate) fn with_src<NewSrc>(self, new_src: NewSrc) -> CastError<NewSrc, Dst> { 688 match self { 689 Self::Alignment(e) => CastError::Alignment(e.with_src(new_src)), 690 Self::Size(e) => CastError::Size(e.with_src(new_src)), 691 Self::Validity(i) => match i {}, 692 } 693 } 694 695 /// Maps the source value associated with the conversion error. 696 /// 697 /// This can help mitigate [issues with `Send`, `Sync` and `'static` 698 /// bounds][self#send-sync-and-static]. 699 /// 700 /// # Examples 701 /// 702 /// ``` 703 /// use zerocopy::*; 704 /// 705 /// let source: [u8; 3] = [0, 1, 2]; 706 /// 707 /// // Try to read a `u32` from `source`. This will fail because there are insufficient 708 /// // bytes in `source`. 709 /// let maybe_u32: Result<&u32, CastError<&[u8], u32>> = u32::ref_from_bytes(&source[..]); 710 /// 711 /// // Map the error's source to its size and address. 712 /// let maybe_u32: Result<&u32, CastError<(usize, usize), u32>> = maybe_u32.map_err(|err| { 713 /// err.map_src(|src| (src.len(), src.as_ptr() as usize)) 714 /// }); 715 /// ``` 716 #[inline] map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> CastError<NewSrc, Dst>717 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> CastError<NewSrc, Dst> { 718 match self { 719 Self::Alignment(e) => CastError::Alignment(e.map_src(f)), 720 Self::Size(e) => CastError::Size(e.map_src(f)), 721 Self::Validity(i) => match i {}, 722 } 723 } 724 725 /// Converts the error into a general [`ConvertError`]. into(self) -> TryCastError<Src, Dst> where Dst: TryFromBytes,726 pub(crate) fn into(self) -> TryCastError<Src, Dst> 727 where 728 Dst: TryFromBytes, 729 { 730 match self { 731 Self::Alignment(e) => TryCastError::Alignment(e), 732 Self::Size(e) => TryCastError::Size(e), 733 Self::Validity(i) => match i {}, 734 } 735 } 736 } 737 738 impl<Src, Dst: ?Sized + Unaligned> From<CastError<Src, Dst>> for SizeError<Src, Dst> { 739 /// Infallibly extracts the [`SizeError`] from this `CastError` since `Dst` 740 /// is unaligned. 741 /// 742 /// Since [`Dst: Unaligned`], it is impossible to encounter an alignment 743 /// error, and so the only error that can be encountered at runtime is a 744 /// [`SizeError`]. This method permits extracting that `SizeError` 745 /// infallibly. 746 /// 747 /// [`Dst: Unaligned`]: crate::Unaligned 748 /// 749 /// # Examples 750 /// 751 /// ```rust 752 /// use zerocopy::*; 753 /// # use zerocopy_derive::*; 754 /// 755 /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] 756 /// #[repr(C)] 757 /// struct UdpHeader { 758 /// src_port: [u8; 2], 759 /// dst_port: [u8; 2], 760 /// length: [u8; 2], 761 /// checksum: [u8; 2], 762 /// } 763 /// 764 /// #[derive(FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] 765 /// #[repr(C, packed)] 766 /// struct UdpPacket { 767 /// header: UdpHeader, 768 /// body: [u8], 769 /// } 770 /// 771 /// impl UdpPacket { 772 /// pub fn parse(bytes: &[u8]) -> Result<&UdpPacket, SizeError<&[u8], UdpPacket>> { 773 /// // Since `UdpPacket: Unaligned`, we can map the `CastError` to a `SizeError`. 774 /// UdpPacket::ref_from_bytes(bytes).map_err(Into::into) 775 /// } 776 /// } 777 /// ``` 778 #[inline(always)] from(err: CastError<Src, Dst>) -> SizeError<Src, Dst>779 fn from(err: CastError<Src, Dst>) -> SizeError<Src, Dst> { 780 match err { 781 #[allow(unreachable_code)] 782 CastError::Alignment(e) => match Infallible::from(e) {}, 783 CastError::Size(e) => e, 784 CastError::Validity(i) => match i {}, 785 } 786 } 787 } 788 789 /// The error type of fallible reference conversions. 790 /// 791 /// Fallible reference conversions, like [`TryFromBytes::try_ref_from_bytes`] 792 /// may emit [alignment](AlignmentError), [size](SizeError), and 793 /// [validity](ValidityError) errors. 794 // Bounds on generic parameters are not enforced in type aliases, but they do 795 // appear in rustdoc. 796 #[allow(type_alias_bounds)] 797 pub type TryCastError<Src, Dst: ?Sized + TryFromBytes> = 798 ConvertError<AlignmentError<Src, Dst>, SizeError<Src, Dst>, ValidityError<Src, Dst>>; 799 800 // TODO(#1139): Remove the `TryFromBytes` here and in other downstream locations 801 // (all the way to `ValidityError`) if we determine it's not necessary for rich 802 // validity errors. 803 impl<Src, Dst: ?Sized + TryFromBytes> TryCastError<Src, Dst> { 804 /// Produces the source underlying the failed conversion. 805 #[inline] into_src(self) -> Src806 pub fn into_src(self) -> Src { 807 match self { 808 Self::Alignment(e) => e.src, 809 Self::Size(e) => e.src, 810 Self::Validity(e) => e.src, 811 } 812 } 813 814 /// Maps the source value associated with the conversion error. 815 /// 816 /// This can help mitigate [issues with `Send`, `Sync` and `'static` 817 /// bounds][self#send-sync-and-static]. 818 /// 819 /// # Examples 820 /// 821 /// ``` 822 /// use core::num::NonZeroU32; 823 /// use zerocopy::*; 824 /// 825 /// let source: [u8; 3] = [0, 0, 0]; 826 /// 827 /// // Try to read a `NonZeroU32` from `source`. 828 /// let maybe_u32: Result<&NonZeroU32, TryCastError<&[u8], NonZeroU32>> 829 /// = NonZeroU32::try_ref_from_bytes(&source[..]); 830 /// 831 /// // Map the error's source to its size and address. 832 /// let maybe_u32: Result<&NonZeroU32, TryCastError<(usize, usize), NonZeroU32>> = 833 /// maybe_u32.map_err(|err| { 834 /// err.map_src(|src| (src.len(), src.as_ptr() as usize)) 835 /// }); 836 /// ``` 837 #[inline] map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryCastError<NewSrc, Dst>838 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryCastError<NewSrc, Dst> { 839 match self { 840 Self::Alignment(e) => TryCastError::Alignment(e.map_src(f)), 841 Self::Size(e) => TryCastError::Size(e.map_src(f)), 842 Self::Validity(e) => TryCastError::Validity(e.map_src(f)), 843 } 844 } 845 } 846 847 impl<Src, Dst: ?Sized + TryFromBytes> From<CastError<Src, Dst>> for TryCastError<Src, Dst> { 848 #[inline] from(value: CastError<Src, Dst>) -> Self849 fn from(value: CastError<Src, Dst>) -> Self { 850 match value { 851 CastError::Alignment(e) => Self::Alignment(e), 852 CastError::Size(e) => Self::Size(e), 853 CastError::Validity(i) => match i {}, 854 } 855 } 856 } 857 858 /// The error type of fallible read-conversions. 859 /// 860 /// Fallible read-conversions, like [`TryFromBytes::try_read_from_bytes`] may emit 861 /// [size](SizeError) and [validity](ValidityError) errors, but not alignment errors. 862 // Bounds on generic parameters are not enforced in type aliases, but they do 863 // appear in rustdoc. 864 #[allow(type_alias_bounds)] 865 pub type TryReadError<Src, Dst: ?Sized + TryFromBytes> = 866 ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>; 867 868 impl<Src, Dst: ?Sized + TryFromBytes> TryReadError<Src, Dst> { 869 /// Produces the source underlying the failed conversion. 870 #[inline] into_src(self) -> Src871 pub fn into_src(self) -> Src { 872 match self { 873 Self::Alignment(i) => match i {}, 874 Self::Size(e) => e.src, 875 Self::Validity(e) => e.src, 876 } 877 } 878 879 /// Maps the source value associated with the conversion error. 880 /// 881 /// This can help mitigate [issues with `Send`, `Sync` and `'static` 882 /// bounds][self#send-sync-and-static]. 883 /// 884 /// # Examples 885 /// 886 /// ``` 887 /// use core::num::NonZeroU32; 888 /// use zerocopy::*; 889 /// 890 /// let source: [u8; 3] = [0, 0, 0]; 891 /// 892 /// // Try to read a `NonZeroU32` from `source`. 893 /// let maybe_u32: Result<NonZeroU32, TryReadError<&[u8], NonZeroU32>> 894 /// = NonZeroU32::try_read_from_bytes(&source[..]); 895 /// 896 /// // Map the error's source to its size. 897 /// let maybe_u32: Result<NonZeroU32, TryReadError<usize, NonZeroU32>> = 898 /// maybe_u32.map_err(|err| { 899 /// err.map_src(|src| src.len()) 900 /// }); 901 /// ``` 902 #[inline] map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryReadError<NewSrc, Dst>903 pub fn map_src<NewSrc>(self, f: impl FnOnce(Src) -> NewSrc) -> TryReadError<NewSrc, Dst> { 904 match self { 905 Self::Alignment(i) => match i {}, 906 Self::Size(e) => TryReadError::Size(e.map_src(f)), 907 Self::Validity(e) => TryReadError::Validity(e.map_src(f)), 908 } 909 } 910 } 911 912 /// The error type of well-aligned, fallible casts. 913 /// 914 /// This is like [`TryCastError`], but for casts that are always well-aligned. 915 /// It is identical to `TryCastError`, except that its alignment error is 916 /// [`Infallible`]. 917 /// 918 /// As of this writing, none of zerocopy's API produces this error directly. 919 /// However, it is useful since it permits users to infallibly discard alignment 920 /// errors when they can prove statically that alignment errors are impossible. 921 /// 922 /// # Examples 923 /// 924 /// ``` 925 /// use core::convert::Infallible; 926 /// use zerocopy::*; 927 /// # use zerocopy_derive::*; 928 /// 929 /// #[derive(TryFromBytes, KnownLayout, Unaligned, Immutable)] 930 /// #[repr(C, packed)] 931 /// struct Bools { 932 /// one: bool, 933 /// two: bool, 934 /// many: [bool], 935 /// } 936 /// 937 /// impl Bools { 938 /// fn parse(bytes: &[u8]) -> Result<&Bools, AlignedTryCastError<&[u8], Bools>> { 939 /// // Since `Bools: Unaligned`, we can infallibly discard 940 /// // the alignment error. 941 /// Bools::try_ref_from_bytes(bytes).map_err(Into::into) 942 /// } 943 /// } 944 /// ``` 945 #[allow(type_alias_bounds)] 946 pub type AlignedTryCastError<Src, Dst: ?Sized + TryFromBytes> = 947 ConvertError<Infallible, SizeError<Src, Dst>, ValidityError<Src, Dst>>; 948 949 /// The error type of a failed allocation. 950 /// 951 /// This type is intended to be deprecated in favor of the standard library's 952 /// [`AllocError`] type once it is stabilized. When that happens, this type will 953 /// be replaced by a type alias to the standard library type. We do not intend 954 /// to treat this as a breaking change; users who wish to avoid breakage should 955 /// avoid writing code which assumes that this is *not* such an alias. For 956 /// example, implementing the same trait for both types will result in an impl 957 /// conflict once this type is an alias. 958 /// 959 /// [`AllocError`]: https://doc.rust-lang.org/alloc/alloc/struct.AllocError.html 960 #[derive(Copy, Clone, PartialEq, Eq, Debug)] 961 pub struct AllocError; 962 963 #[cfg(test)] 964 mod tests { 965 use super::*; 966 967 #[test] test_send_sync()968 fn test_send_sync() { 969 // Test that all error types are `Send + Sync` even if `Dst: !Send + 970 // !Sync`. 971 972 #[allow(dead_code)] 973 fn is_send_sync<T: Send + Sync>(_t: T) {} 974 975 #[allow(dead_code)] 976 fn alignment_err_is_send_sync<Src: Send + Sync, Dst>(err: AlignmentError<Src, Dst>) { 977 is_send_sync(err) 978 } 979 980 #[allow(dead_code)] 981 fn size_err_is_send_sync<Src: Send + Sync, Dst>(err: SizeError<Src, Dst>) { 982 is_send_sync(err) 983 } 984 985 #[allow(dead_code)] 986 fn validity_err_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>( 987 err: ValidityError<Src, Dst>, 988 ) { 989 is_send_sync(err) 990 } 991 992 #[allow(dead_code)] 993 fn convert_error_is_send_sync<Src: Send + Sync, Dst: TryFromBytes>( 994 err: ConvertError< 995 AlignmentError<Src, Dst>, 996 SizeError<Src, Dst>, 997 ValidityError<Src, Dst>, 998 >, 999 ) { 1000 is_send_sync(err) 1001 } 1002 } 1003 1004 #[test] alignment_display()1005 fn alignment_display() { 1006 #[repr(C, align(128))] 1007 struct Aligned { 1008 bytes: [u8; 128], 1009 } 1010 1011 impl_known_layout!(elain::Align::<8>); 1012 1013 let aligned = Aligned { bytes: [0; 128] }; 1014 1015 let bytes = &aligned.bytes[1..]; 1016 let addr = crate::util::AsAddress::addr(bytes); 1017 assert_eq!( 1018 AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), 1019 format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\ 1020 \nSource type: &[u8]\ 1021 \nSource address: 0x{:x} (a multiple of 1)\ 1022 \nDestination type: elain::Align<8>\ 1023 \nDestination alignment: 8", addr) 1024 ); 1025 1026 let bytes = &aligned.bytes[2..]; 1027 let addr = crate::util::AsAddress::addr(bytes); 1028 assert_eq!( 1029 AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), 1030 format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\ 1031 \nSource type: &[u8]\ 1032 \nSource address: 0x{:x} (a multiple of 2)\ 1033 \nDestination type: elain::Align<8>\ 1034 \nDestination alignment: 8", addr) 1035 ); 1036 1037 let bytes = &aligned.bytes[3..]; 1038 let addr = crate::util::AsAddress::addr(bytes); 1039 assert_eq!( 1040 AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), 1041 format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\ 1042 \nSource type: &[u8]\ 1043 \nSource address: 0x{:x} (a multiple of 1)\ 1044 \nDestination type: elain::Align<8>\ 1045 \nDestination alignment: 8", addr) 1046 ); 1047 1048 let bytes = &aligned.bytes[4..]; 1049 let addr = crate::util::AsAddress::addr(bytes); 1050 assert_eq!( 1051 AlignmentError::<_, elain::Align::<8>>::new_checked(bytes).to_string(), 1052 format!("The conversion failed because the address of the source is not a multiple of the alignment of the destination type.\n\ 1053 \nSource type: &[u8]\ 1054 \nSource address: 0x{:x} (a multiple of 4)\ 1055 \nDestination type: elain::Align<8>\ 1056 \nDestination alignment: 8", addr) 1057 ); 1058 } 1059 1060 #[test] size_display()1061 fn size_display() { 1062 assert_eq!( 1063 SizeError::<_, [u8]>::new(&[0u8; 2][..]).to_string(), 1064 "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\ 1065 \nSource type: &[u8]\ 1066 \nSource size: 2 bytes\ 1067 \nDestination type: [u8]" 1068 ); 1069 1070 assert_eq!( 1071 SizeError::<_, [u8; 2]>::new(&[0u8; 1][..]).to_string(), 1072 "The conversion failed because the source was incorrectly sized to complete the conversion into the destination type.\n\ 1073 \nSource type: &[u8]\ 1074 \nSource size: 1 byte\ 1075 \nDestination size: 2 bytes\ 1076 \nDestination type: [u8; 2]" 1077 ); 1078 } 1079 1080 #[test] validity_display()1081 fn validity_display() { 1082 assert_eq!( 1083 ValidityError::<_, bool>::new(&[2u8; 1][..]).to_string(), 1084 "The conversion failed because the source bytes are not a valid value of the destination type.\n\ 1085 \n\ 1086 Destination type: bool" 1087 ); 1088 } 1089 } 1090