• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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