bytecheck-0.8.0/.cargo_vcs_info.json0000644000000001470000000000100130040ustar { "git": { "sha1": "1fb8574401d92c74f1f39d0bba73299bd7b6fefc" }, "path_in_vcs": "bytecheck" }bytecheck-0.8.0/Cargo.toml0000644000000027100000000000100110000ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.81" name = "bytecheck" version = "0.8.0" authors = ["David Koloski "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "Memory validation framework for Rust" documentation = "https://docs.rs/bytecheck" readme = "README.md" keywords = [ "no_std", "validation", "serialization", ] categories = [ "encoding", "no-std", "no-std::no-alloc", ] license = "MIT" repository = "https://github.com/rkyv/bytecheck" [lib] name = "bytecheck" path = "src/lib.rs" [dependencies.bytecheck_derive] version = "=0.8.0" default-features = false [dependencies.ptr_meta] version = "0.3" default-features = false [dependencies.rancor] version = "0.1" default-features = false [dependencies.simdutf8] version = "0.1" optional = true default-features = false [dependencies.uuid-1] version = "1" optional = true default-features = false package = "uuid" [features] default = ["simdutf8"] bytecheck-0.8.0/Cargo.toml.orig000064400000000000000000000017771046102023000144750ustar 00000000000000[package] name = "bytecheck" description = "Memory validation framework for Rust" version.workspace = true authors.workspace = true edition.workspace = true rust-version.workspace = true license.workspace = true readme.workspace = true repository.workspace = true keywords.workspace = true categories.workspace = true documentation = "https://docs.rs/bytecheck" [dependencies] bytecheck_derive.workspace = true ptr_meta.workspace = true rancor.workspace = true simdutf8 = { workspace = true, optional = true } # Support for various common crates. These are primarily to get users off the # ground and build some momentum. # These are NOT PLANNED to remain in bytecheck for the 1.0 release. Much like # serde, these implementations should be moved into their respective crates over # time. Before adding support for another crate, please consider getting # bytecheck support in the crate instead. uuid-1 = { package = "uuid", version = "1", optional = true, default-features = false } [features] default = ["simdutf8"] bytecheck-0.8.0/LICENSE000064400000000000000000000020351046102023000125770ustar 00000000000000Copyright 2023 David Koloski Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. bytecheck-0.8.0/README.md000064400000000000000000000050311046102023000130500ustar 00000000000000# `bytecheck` [![crates.io badge]][crates.io] [![docs badge]][docs] [![license badge]][license] [crates.io badge]: https://img.shields.io/crates/v/bytecheck.svg [crates.io]: https://crates.io/crates/bytecheck [docs badge]: https://img.shields.io/docsrs/bytecheck [docs]: https://docs.rs/bytecheck [license badge]: https://img.shields.io/badge/license-MIT-blue.svg [license]: https://github.com/rkyv/bytecheck/blob/master/LICENSE bytecheck is a memory validation framework for Rust. ## Documentation - [bytecheck](https://docs.rs/bytecheck), a memory validation framework for Rust - [bytecheck_derive](https://docs.rs/bytecheck_derive), the derive macro for bytecheck ## Example ```rust use bytecheck::{CheckBytes, check_bytes, rancor::Failure}; #[derive(CheckBytes, Debug)] #[repr(C)] struct Test { a: u32, b: char, c: bool, } #[repr(C, align(4))] struct Aligned([u8; N]); macro_rules! bytes { ($($byte:literal,)*) => { (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() }; ($($byte:literal),*) => { bytes!($($byte,)*) }; } // In this example, the architecture is assumed to be little-endian #[cfg(target_endian = "little")] unsafe { // These are valid bytes for a `Test` check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap(); // Changing the bytes for the u32 is OK, any bytes are a valid u32 check_bytes::( bytes![ 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap(); // Characters outside the valid ranges are invalid check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap_err(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap_err(); // 0 is a valid boolean value (false) but 2 is not check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, ].cast() ).unwrap(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8, ].cast() ).unwrap_err(); } ``` bytecheck-0.8.0/example.md000064400000000000000000000036671046102023000135630ustar 00000000000000```rust use bytecheck::{CheckBytes, check_bytes, rancor::Failure}; #[derive(CheckBytes, Debug)] #[repr(C)] struct Test { a: u32, b: char, c: bool, } #[repr(C, align(4))] struct Aligned([u8; N]); macro_rules! bytes { ($($byte:literal,)*) => { (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() }; ($($byte:literal),*) => { bytes!($($byte,)*) }; } // In this example, the architecture is assumed to be little-endian #[cfg(target_endian = "little")] unsafe { // These are valid bytes for a `Test` check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap(); // Changing the bytes for the u32 is OK, any bytes are a valid u32 check_bytes::( bytes![ 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap(); // Characters outside the valid ranges are invalid check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap_err(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 255u8, 255u8, 255u8, ].cast() ).unwrap_err(); // 0 is a valid boolean value (false) but 2 is not check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, ].cast() ).unwrap(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8, ].cast() ).unwrap_err(); } ``` bytecheck-0.8.0/src/lib.rs000064400000000000000000001523771046102023000135140ustar 00000000000000//! # bytecheck //! //! bytecheck is a memory validation framework for Rust. //! //! For some types, creating an invalid value immediately results in undefined //! behavior. This can cause some issues when trying to validate potentially //! invalid bytes, as just casting the bytes to your type can technically cause //! errors. This makes it difficult to write validation routines, because until //! you're certain that the bytes represent valid values you cannot cast them. //! //! bytecheck provides a framework for performing these byte-level validations //! and implements checks for basic types along with a derive macro to implement //! validation for custom structs and enums. //! //! ## Design //! //! [`CheckBytes`] is at the heart of bytecheck, and does the heavy lifting of //! verifying that some bytes represent a valid type. Implementing it can be //! done manually or automatically with the [derive macro](macro@CheckBytes). //! //! ## Layout stability //! //! The layouts of types may change between compiler versions, or even different //! compilations. To guarantee stable type layout between compilations, structs, //! enums, and unions can be annotated with `#[repr(C)]`, and enums specifically //! can be annotated with `#[repr(int)]` or `#[repr(C, int)]` as well. See //! [the reference's page on type layout][reference] for more details. //! //! [reference]: https://doc.rust-lang.org/reference/type-layout.html //! //! ## Features //! //! - `derive`: Re-exports the macros from `bytecheck_derive`. Enabled by //! default. //! - `simdutf8`: Uses the `simdutf8` crate to validate UTF-8 strings. Enabled //! by default. //! //! ### Crates //! //! Bytecheck provides integrations for some common crates by default. In the //! future, crates should depend on bytecheck and provide their own integration. //! //! - [`uuid-1`](https://docs.rs/uuid/1) //! //! ## Example #![doc = include_str!("../example.md")] #![deny( future_incompatible, missing_docs, nonstandard_style, unsafe_op_in_unsafe_fn, unused, warnings, clippy::all, clippy::missing_safety_doc, clippy::undocumented_unsafe_blocks, rustdoc::broken_intra_doc_links, rustdoc::missing_crate_level_docs )] #![no_std] #![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg, doc_auto_cfg))] // Support for various common crates. These are primarily to get users off the // ground and build some momentum. // These are NOT PLANNED to remain in bytecheck for the final release. Much like // serde, these implementations should be moved into their respective crates // over time. Before adding support for another crate, please consider getting // bytecheck support in the crate instead. #[cfg(feature = "uuid-1")] mod uuid; #[cfg(not(feature = "simdutf8"))] use core::str::from_utf8; #[cfg(target_has_atomic = "8")] use core::sync::atomic::{AtomicBool, AtomicI8, AtomicU8}; #[cfg(target_has_atomic = "16")] use core::sync::atomic::{AtomicI16, AtomicU16}; #[cfg(target_has_atomic = "32")] use core::sync::atomic::{AtomicI32, AtomicU32}; #[cfg(target_has_atomic = "64")] use core::sync::atomic::{AtomicI64, AtomicU64}; use core::{ cell::{Cell, UnsafeCell}, error::Error, ffi::CStr, fmt, marker::{PhantomData, PhantomPinned}, mem::ManuallyDrop, num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, }, ops, ptr, }; pub use bytecheck_derive::CheckBytes; pub use rancor; use rancor::{fail, Fallible, ResultExt as _, Source, Strategy, Trace}; #[cfg(feature = "simdutf8")] use simdutf8::basic::from_utf8; /// A type that can check whether a pointer points to a valid value. /// /// `CheckBytes` can be derived with [`CheckBytes`](macro@CheckBytes) or /// implemented manually for custom behavior. /// /// # Safety /// /// `check_bytes` must only return `Ok` if `value` points to a valid instance of /// `Self`. Because `value` must always be properly aligned for `Self` and point /// to enough bytes to represent the type, this implies that `value` may be /// dereferenced safely. /// /// # Example /// /// ``` /// use core::{error::Error, fmt}; /// /// use bytecheck::CheckBytes; /// use rancor::{fail, Fallible, Source}; /// /// #[repr(C, align(4))] /// pub struct NonMaxU32(u32); /// /// unsafe impl CheckBytes for NonMaxU32 /// where /// C::Error: Source, /// { /// unsafe fn check_bytes( /// value: *const Self, /// context: &mut C, /// ) -> Result<(), C::Error> { /// #[derive(Debug)] /// struct NonMaxCheckError; /// /// impl fmt::Display for NonMaxCheckError { /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "non-max u32 was set to u32::MAX") /// } /// } /// /// impl Error for NonMaxCheckError {} /// /// let value = unsafe { value.read() }; /// if value.0 == u32::MAX { /// fail!(NonMaxCheckError); /// } /// /// Ok(()) /// } /// } /// ``` /// /// See [`Verify`] for an example which uses less unsafe. pub unsafe trait CheckBytes { /// Checks whether the given pointer points to a valid value within the /// given context. /// /// # Safety /// /// The passed pointer must be aligned and point to enough initialized bytes /// to represent the type. unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error>; } /// A type that can check whether its invariants are upheld. /// /// When using [the derive](macro@CheckBytes), adding `#[bytecheck(verify)]` /// allows implementing `Verify` for the derived type. [`Verify::verify`] will /// be called after the type is checked and all fields are known to be valid. /// /// # Safety /// /// - `verify` must only return `Ok` if all of the invariants of this type are /// upheld by `self`. /// - `verify` may not assume that its type invariants are upheld by the given /// `self` (the invariants of each field are guaranteed to be upheld). /// /// # Example /// /// ``` /// use core::{error::Error, fmt}; /// /// use bytecheck::{CheckBytes, Verify}; /// use rancor::{fail, Fallible, Source}; /// /// #[derive(CheckBytes)] /// #[bytecheck(verify)] /// #[repr(C, align(4))] /// pub struct NonMaxU32(u32); /// /// unsafe impl Verify for NonMaxU32 /// where /// C::Error: Source, /// { /// fn verify(&self, context: &mut C) -> Result<(), C::Error> { /// #[derive(Debug)] /// struct NonMaxCheckError; /// /// impl fmt::Display for NonMaxCheckError { /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "non-max u32 was set to u32::MAX") /// } /// } /// /// impl Error for NonMaxCheckError {} /// /// if self.0 == u32::MAX { /// fail!(NonMaxCheckError); /// } /// /// Ok(()) /// } /// } /// ``` pub unsafe trait Verify { /// Checks whether the invariants of this type are upheld by `self`. fn verify(&self, context: &mut C) -> Result<(), C::Error>; } /// Checks whether the given pointer points to a valid value. /// /// # Safety /// /// The passed pointer must be aligned and point to enough initialized bytes to /// represent the type. /// /// # Example /// /// ``` /// use bytecheck::check_bytes; /// use rancor::Failure; /// /// unsafe { /// // 0 and 1 are valid values for bools /// check_bytes::((&0u8 as *const u8).cast()).unwrap(); /// check_bytes::((&1u8 as *const u8).cast()).unwrap(); /// /// // 2 is not a valid value /// check_bytes::((&2u8 as *const u8).cast()).unwrap_err(); /// } /// ``` #[inline] pub unsafe fn check_bytes(value: *const T) -> Result<(), E> where T: CheckBytes> + ?Sized, { // SAFETY: The safety conditions of `check_bytes_with_context` are the same // as the safety conditions of this function. unsafe { check_bytes_with_context(value, &mut ()) } } /// Checks whether the given pointer points to a valid value within the given /// context. /// /// # Safety /// /// The passed pointer must be aligned and point to enough initialized bytes to /// represent the type. /// /// # Example /// /// ``` /// use core::{error::Error, fmt}; /// /// use bytecheck::{check_bytes_with_context, CheckBytes, Verify}; /// use rancor::{fail, Failure, Fallible, Source, Strategy}; /// /// trait Context { /// fn is_allowed(&self, value: u8) -> bool; /// } /// /// impl Context for Strategy { /// fn is_allowed(&self, value: u8) -> bool { /// T::is_allowed(self, value) /// } /// } /// /// struct Allowed(u8); /// /// impl Context for Allowed { /// fn is_allowed(&self, value: u8) -> bool { /// value == self.0 /// } /// } /// /// #[derive(CheckBytes)] /// #[bytecheck(verify)] /// #[repr(C)] /// pub struct ContextualByte(u8); /// /// unsafe impl Verify for ContextualByte /// where /// C::Error: Source, /// { /// fn verify(&self, context: &mut C) -> Result<(), C::Error> { /// #[derive(Debug)] /// struct InvalidByte(u8); /// /// impl fmt::Display for InvalidByte { /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { /// write!(f, "invalid contextual byte: {}", self.0) /// } /// } /// /// impl Error for InvalidByte {} /// /// if !context.is_allowed(self.0) { /// fail!(InvalidByte(self.0)); /// } /// /// Ok(()) /// } /// } /// /// let value = 45u8; /// unsafe { /// // Checking passes when the context allows byte 45 /// check_bytes_with_context::( /// (&value as *const u8).cast(), /// &mut Allowed(45), /// ) /// .unwrap(); /// /// // Checking fails when the conteext does not allow byte 45 /// check_bytes_with_context::( /// (&value as *const u8).cast(), /// &mut Allowed(0), /// ) /// .unwrap_err(); /// } /// ``` pub unsafe fn check_bytes_with_context( value: *const T, context: &mut C, ) -> Result<(), E> where T: CheckBytes> + ?Sized, { // SAFETY: The safety conditions of `check_bytes` are the same as the safety // conditions of this function. unsafe { CheckBytes::check_bytes(value, Strategy::wrap(context)) } } macro_rules! impl_primitive { ($type:ty) => { // SAFETY: All bit patterns are valid for these primitive types. unsafe impl CheckBytes for $type { #[inline] unsafe fn check_bytes( _: *const Self, _: &mut C, ) -> Result<(), C::Error> { Ok(()) } } }; } macro_rules! impl_primitives { ($($type:ty),* $(,)?) => { $( impl_primitive!($type); )* } } impl_primitives! { (), i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64, } #[cfg(target_has_atomic = "8")] impl_primitives!(AtomicI8, AtomicU8); #[cfg(target_has_atomic = "16")] impl_primitives!(AtomicI16, AtomicU16); #[cfg(target_has_atomic = "32")] impl_primitives!(AtomicI32, AtomicU32); #[cfg(target_has_atomic = "64")] impl_primitives!(AtomicI64, AtomicU64); // SAFETY: `PhantomData` is a zero-sized type and so all bit patterns are valid. unsafe impl CheckBytes for PhantomData { #[inline] unsafe fn check_bytes(_: *const Self, _: &mut C) -> Result<(), C::Error> { Ok(()) } } // SAFETY: `PhantomPinned` is a zero-sized type and so all bit patterns are // valid. unsafe impl CheckBytes for PhantomPinned { #[inline] unsafe fn check_bytes(_: *const Self, _: &mut C) -> Result<(), C::Error> { Ok(()) } } // SAFETY: `ManuallyDrop` is a `#[repr(transparent)]` wrapper around a `T`, // and so `value` points to a valid `ManuallyDrop` if it also points to a // valid `T`. unsafe impl CheckBytes for ManuallyDrop where T: CheckBytes + ?Sized, C: Fallible + ?Sized, C::Error: Trace, { #[inline] unsafe fn check_bytes( value: *const Self, c: &mut C, ) -> Result<(), C::Error> { // SAFETY: Because `ManuallyDrop` is `#[repr(transparent)]`, a // pointer to a `ManuallyDrop` is guaranteed to be the same as a // pointer to `T`. We can't call `.cast()` here because `T` may be // an unsized type. let inner_ptr = unsafe { core::mem::transmute::<*const Self, *const T>(value) }; // SAFETY: The caller has guaranteed that `value` is aligned for // `ManuallyDrop` and points to enough bytes to represent // `ManuallyDrop`. Since `ManuallyDrop` is `#[repr(transparent)]`, // `inner_ptr` is also aligned for `T` and points to enough bytes to // represent it. unsafe { T::check_bytes(inner_ptr, c) .trace("while checking inner value of `ManuallyDrop`") } } } // SAFETY: `UnsafeCell` has the same memory layout as `T`, and so `value` // points to a valid `UnsafeCell` if it also points to a valid `T`. unsafe impl CheckBytes for UnsafeCell where T: CheckBytes + ?Sized, C: Fallible + ?Sized, C::Error: Trace, { #[inline] unsafe fn check_bytes( value: *const Self, c: &mut C, ) -> Result<(), C::Error> { // SAFETY: Because `UnsafeCell` has the same memory layout as // `T`, a pointer to an `UnsafeCell` is guaranteed to be the same // as a pointer to `T`. We can't call `.cast()` here because `T` may // be an unsized type. let inner_ptr = unsafe { core::mem::transmute::<*const Self, *const T>(value) }; // SAFETY: The caller has guaranteed that `value` is aligned for // `UnsafeCell` and points to enough bytes to represent // `UnsafeCell`. Since `UnsafeCell` has the same layout `T`, // `inner_ptr` is also aligned for `T` and points to enough bytes to // represent it. unsafe { T::check_bytes(inner_ptr, c) .trace("while checking inner value of `UnsafeCell`") } } } // SAFETY: `Cell` has the same memory layout as `UnsafeCell` (and // therefore `T` itself), and so `value` points to a valid `UnsafeCell` if it // also points to a valid `T`. unsafe impl CheckBytes for Cell where T: CheckBytes + ?Sized, C: Fallible + ?Sized, C::Error: Trace, { #[inline] unsafe fn check_bytes( value: *const Self, c: &mut C, ) -> Result<(), C::Error> { // SAFETY: Because `Cell` has the same memory layout as // `UnsafeCell` (and therefore `T` itself), a pointer to a // `Cell` is guaranteed to be the same as a pointer to `T`. We // can't call `.cast()` here because `T` may be an unsized type. let inner_ptr = unsafe { core::mem::transmute::<*const Self, *const T>(value) }; // SAFETY: The caller has guaranteed that `value` is aligned for // `Cell` and points to enough bytes to represent `Cell`. Since // `Cell` has the same layout as `UnsafeCell` ( and therefore `T` // itself), `inner_ptr` is also aligned for `T` and points to enough // bytes to represent it. unsafe { T::check_bytes(inner_ptr, c) .trace("while checking inner value of `Cell`") } } } #[derive(Debug)] struct BoolCheckError { byte: u8, } impl fmt::Display for BoolCheckError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "bool set to invalid byte {}, expected either 0 or 1", self.byte, ) } } impl Error for BoolCheckError {} // SAFETY: A bool is a one byte value that must either be 0 or 1. `check_bytes` // only returns `Ok` if `value` is 0 or 1. unsafe impl CheckBytes for bool where C: Fallible + ?Sized, C::Error: Source, { #[inline] unsafe fn check_bytes( value: *const Self, _: &mut C, ) -> Result<(), C::Error> { // SAFETY: `value` is a pointer to a `bool`, which has a size and // alignment of one. `u8` also has a size and alignment of one, and all // bit patterns are valid for `u8`. So we can cast `value` to a `u8` // pointer and read from it. let byte = unsafe { *value.cast::() }; match byte { 0 | 1 => Ok(()), _ => fail!(BoolCheckError { byte }), } } } #[cfg(target_has_atomic = "8")] // SAFETY: `AtomicBool` has the same ABI as `bool`, so if `value` points to a // valid `bool` then it also points to a valid `AtomicBool`. unsafe impl CheckBytes for AtomicBool where C: Fallible + ?Sized, C::Error: Source, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { // SAFETY: `AtomicBool` has the same ABI as `bool`, so a pointer that is // aligned for `AtomicBool` and points to enough bytes for `AtomicBool` // is also aligned for `bool` and points to enough bytes for `bool`. unsafe { bool::check_bytes(value.cast(), context) } } } // SAFETY: If `char::try_from` succeeds with the pointed-to-value, then it must // be a valid value for `char`. unsafe impl CheckBytes for char where C: Fallible + ?Sized, C::Error: Source, { #[inline] unsafe fn check_bytes(ptr: *const Self, _: &mut C) -> Result<(), C::Error> { // SAFETY: `char` and `u32` are both four bytes, but we're not // guaranteed that they have the same alignment. Using `read_unaligned` // ensures that we can read a `u32` regardless and try to convert it to // a `char`. let value = unsafe { ptr.cast::().read_unaligned() }; char::try_from(value).into_error()?; Ok(()) } } #[derive(Debug)] struct TupleIndexContext { index: usize, } impl fmt::Display for TupleIndexContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "while checking index {} of tuple", self.index) } } macro_rules! impl_tuple { ($($type:ident $index:tt),*) => { // SAFETY: A tuple is valid if all of its elements are valid, and // `check_bytes` only returns `Ok` when all of the elements validated // successfully. unsafe impl<$($type,)* C> CheckBytes for ($($type,)*) where $($type: CheckBytes,)* C: Fallible + ?Sized, C::Error: Trace, { #[inline] #[allow(clippy::unneeded_wildcard_pattern)] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { $( // SAFETY: The caller has guaranteed that `value` points to // enough bytes for this tuple and is properly aligned, so // we can create pointers to each element and check them. unsafe { <$type>::check_bytes( ptr::addr_of!((*value).$index), context, ).with_trace(|| TupleIndexContext { index: $index })?; } )* Ok(()) } } } } impl_tuple!(T0 0); impl_tuple!(T0 0, T1 1); impl_tuple!(T0 0, T1 1, T2 2); impl_tuple!(T0 0, T1 1, T2 2, T3 3); impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4); impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5); impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6); impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7); impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8); impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8, T9 9); impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8, T9 9, T10 10); impl_tuple!( T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8, T9 9, T10 10, T11 11 ); impl_tuple!( T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8, T9 9, T10 10, T11 11, T12 12 ); #[derive(Debug)] struct ArrayCheckContext { index: usize, } impl fmt::Display for ArrayCheckContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "while checking index '{}' of array", self.index) } } // SAFETY: `check_bytes` only returns `Ok` if each element of the array is // valid. If each element of the array is valid then the whole array is also // valid. unsafe impl CheckBytes for [T; N] where T: CheckBytes, C: Fallible + ?Sized, C::Error: Trace, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { let base = value.cast::(); for index in 0..N { // SAFETY: The caller has guaranteed that `value` points to enough // bytes for this array and is properly aligned, so we can create // pointers to each element and check them. unsafe { T::check_bytes(base.add(index), context) .with_trace(|| ArrayCheckContext { index })?; } } Ok(()) } } #[derive(Debug)] struct SliceCheckContext { index: usize, } impl fmt::Display for SliceCheckContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "while checking index '{}' of slice", self.index) } } // SAFETY: `check_bytes` only returns `Ok` if each element of the slice is // valid. If each element of the slice is valid then the whole slice is also // valid. unsafe impl CheckBytes for [T] where T: CheckBytes, C: Fallible + ?Sized, C::Error: Trace, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { let (data_address, len) = ptr_meta::to_raw_parts(value); let base = data_address.cast::(); for index in 0..len { // SAFETY: The caller has guaranteed that `value` points to enough // bytes for this slice and is properly aligned, so we can create // pointers to each element and check them. unsafe { T::check_bytes(base.add(index), context) .with_trace(|| SliceCheckContext { index })?; } } Ok(()) } } // SAFETY: `check_bytes` only returns `Ok` if the bytes pointed to by `str` are // valid UTF-8. If they are valid UTF-8 then the overall `str` is also valid. unsafe impl CheckBytes for str where C: Fallible + ?Sized, C::Error: Source, { #[inline] unsafe fn check_bytes( value: *const Self, _: &mut C, ) -> Result<(), C::Error> { #[derive(Debug)] struct Utf8Error; impl fmt::Display for Utf8Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "invalid UTF-8") } } impl Error for Utf8Error {} let slice_ptr = value as *const [u8]; // SAFETY: The caller has guaranteed that `value` is properly-aligned // and points to enough bytes for its `str`. Because a `u8` slice has // the same layout as a `str`, we can dereference it for UTF-8 // validation. let slice = unsafe { &*slice_ptr }; from_utf8(slice).map_err(|_| Utf8Error).into_error()?; Ok(()) } } // SAFETY: `check_bytes` only returns `Ok` when the bytes constitute a valid // `CStr` per `CStr::from_bytes_with_nul`. unsafe impl CheckBytes for CStr where C: Fallible + ?Sized, C::Error: Source, { #[inline] unsafe fn check_bytes( value: *const Self, _: &mut C, ) -> Result<(), C::Error> { let slice_ptr = value as *const [u8]; // SAFETY: The caller has guaranteed that `value` is properly-aligned // and points to enough bytes for its `CStr`. Because a `u8` slice has // the same layout as a `CStr`, we can dereference it for validation. let slice = unsafe { &*slice_ptr }; CStr::from_bytes_with_nul(slice).into_error()?; Ok(()) } } // Generic contexts used by the derive. /// Context for errors resulting from invalid structs. /// /// This context is used by the derive macro to trace which field of a struct /// failed validation. #[derive(Debug)] pub struct StructCheckContext { /// The name of the struct with an invalid field. pub struct_name: &'static str, /// The name of the struct field that was invalid. pub field_name: &'static str, } impl fmt::Display for StructCheckContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "while checking field '{}' of struct '{}'", self.field_name, self.struct_name ) } } /// Context for errors resulting from invalid tuple structs. /// /// This context is used by the derive macro to trace which field of a tuple /// struct failed validation. #[derive(Debug)] pub struct TupleStructCheckContext { /// The name of the tuple struct with an invalid field. pub tuple_struct_name: &'static str, /// The index of the tuple struct field that was invalid. pub field_index: usize, } impl fmt::Display for TupleStructCheckContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "while checking field index {} of tuple struct '{}'", self.field_index, self.tuple_struct_name, ) } } /// An error resulting from an invalid enum tag. /// /// This context is used by the derive macro to trace what the invalid /// discriminant for an enum is. #[derive(Debug)] pub struct InvalidEnumDiscriminantError { /// The name of the enum with an invalid discriminant. pub enum_name: &'static str, /// The invalid value of the enum discriminant. pub invalid_discriminant: T, } impl fmt::Display for InvalidEnumDiscriminantError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "invalid discriminant '{}' for enum '{}'", self.invalid_discriminant, self.enum_name ) } } impl Error for InvalidEnumDiscriminantError where T: fmt::Debug + fmt::Display { } /// Context for errors resulting from checking enum variants with named fields. /// /// This context is used by the derive macro to trace which field of an enum /// variant failed validation. #[derive(Debug)] pub struct NamedEnumVariantCheckContext { /// The name of the enum with an invalid variant. pub enum_name: &'static str, /// The name of the variant that was invalid. pub variant_name: &'static str, /// The name of the field that was invalid. pub field_name: &'static str, } impl fmt::Display for NamedEnumVariantCheckContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "while checking field '{}' of variant '{}' of enum '{}'", self.field_name, self.variant_name, self.enum_name, ) } } /// Context for errors resulting from checking enum variants with unnamed /// fields. /// /// This context is used by the derive macro to trace which field of an enum /// variant failed validation. #[derive(Debug)] pub struct UnnamedEnumVariantCheckContext { /// The name of the enum with an invalid variant. pub enum_name: &'static str, /// The name of the variant that was invalid. pub variant_name: &'static str, /// The name of the field that was invalid. pub field_index: usize, } impl fmt::Display for UnnamedEnumVariantCheckContext { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "while checking field index {} of variant '{}' of enum '{}'", self.field_index, self.variant_name, self.enum_name, ) } } // Range types // SAFETY: A `Range` is valid if its `start` and `end` are both valid, and // `check_bytes` only returns `Ok` when both `start` and `end` are valid. Note // that `Range` does not require `start` be less than `end`. unsafe impl CheckBytes for ops::Range where T: CheckBytes, C: Fallible + ?Sized, C::Error: Trace, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { // SAFETY: The caller has guaranteed that `value` is aligned for a // `Range` and points to enough initialized bytes for one, so a // pointer projected to the `start` field will be properly aligned for // a `T` and point to enough initialized bytes for one too. unsafe { T::check_bytes(ptr::addr_of!((*value).start), context).with_trace( || StructCheckContext { struct_name: "Range", field_name: "start", }, )?; } // SAFETY: Same reasoning as above, but for `end`. unsafe { T::check_bytes(ptr::addr_of!((*value).end), context).with_trace( || StructCheckContext { struct_name: "Range", field_name: "end", }, )?; } Ok(()) } } // SAFETY: A `RangeFrom` is valid if its `start` is valid, and `check_bytes` // only returns `Ok` when its `start` is valid. unsafe impl CheckBytes for ops::RangeFrom where T: CheckBytes, C: Fallible + ?Sized, C::Error: Trace, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { // SAFETY: The caller has guaranteed that `value` is aligned for a // `RangeFrom` and points to enough initialized bytes for one, so a // pointer projected to the `start` field will be properly aligned for // a `T` and point to enough initialized bytes for one too. unsafe { T::check_bytes(ptr::addr_of!((*value).start), context).with_trace( || StructCheckContext { struct_name: "RangeFrom", field_name: "start", }, )?; } Ok(()) } } // SAFETY: `RangeFull` is a ZST and so every pointer to one is valid. unsafe impl CheckBytes for ops::RangeFull { #[inline] unsafe fn check_bytes(_: *const Self, _: &mut C) -> Result<(), C::Error> { Ok(()) } } // SAFETY: A `RangeTo` is valid if its `end` is valid, and `check_bytes` only // returns `Ok` when its `end` is valid. unsafe impl CheckBytes for ops::RangeTo where T: CheckBytes, C: Fallible + ?Sized, C::Error: Trace, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { // SAFETY: The caller has guaranteed that `value` is aligned for a // `RangeTo` and points to enough initialized bytes for one, so a // pointer projected to the `end` field will be properly aligned for // a `T` and point to enough initialized bytes for one too. unsafe { T::check_bytes(ptr::addr_of!((*value).end), context).with_trace( || StructCheckContext { struct_name: "RangeTo", field_name: "end", }, )?; } Ok(()) } } // SAFETY: A `RangeToInclusive` is valid if its `end` is valid, and // `check_bytes` only returns `Ok` when its `end` is valid. unsafe impl CheckBytes for ops::RangeToInclusive where T: CheckBytes, C: Fallible + ?Sized, C::Error: Trace, { #[inline] unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { // SAFETY: The caller has guaranteed that `value` is aligned for a // `RangeToInclusive` and points to enough initialized bytes for one, // so a pointer projected to the `end` field will be properly aligned // for a `T` and point to enough initialized bytes for one too. unsafe { T::check_bytes(ptr::addr_of!((*value).end), context).with_trace( || StructCheckContext { struct_name: "RangeToInclusive", field_name: "end", }, )?; } Ok(()) } } #[derive(Debug)] struct NonZeroCheckError; impl fmt::Display for NonZeroCheckError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "nonzero integer is zero") } } impl Error for NonZeroCheckError {} macro_rules! impl_nonzero { ($nonzero:ident, $underlying:ident) => { // SAFETY: `check_bytes` only returns `Ok` when `value` is not zero, the // only validity condition for non-zero integer types. unsafe impl CheckBytes for $nonzero where C: Fallible + ?Sized, C::Error: Source, { #[inline] unsafe fn check_bytes( value: *const Self, _: &mut C, ) -> Result<(), C::Error> { // SAFETY: Non-zero integer types are guaranteed to have the // same ABI as their corresponding integer types. Those integers // have no validity requirements, so we can cast and dereference // value to check if it is equal to zero. if unsafe { *value.cast::<$underlying>() } == 0 { fail!(NonZeroCheckError); } else { Ok(()) } } } }; } impl_nonzero!(NonZeroI8, i8); impl_nonzero!(NonZeroI16, i16); impl_nonzero!(NonZeroI32, i32); impl_nonzero!(NonZeroI64, i64); impl_nonzero!(NonZeroI128, i128); impl_nonzero!(NonZeroU8, u8); impl_nonzero!(NonZeroU16, u16); impl_nonzero!(NonZeroU32, u32); impl_nonzero!(NonZeroU64, u64); impl_nonzero!(NonZeroU128, u128); #[cfg(test)] #[macro_use] mod tests { use core::ffi::CStr; use rancor::{Failure, Fallible, Infallible, Source, Strategy}; use crate::{check_bytes, check_bytes_with_context, CheckBytes, Verify}; #[derive(Debug)] #[repr(transparent)] struct CharLE(u32); impl From for CharLE { fn from(c: char) -> Self { #[cfg(target_endian = "little")] { Self(c as u32) } #[cfg(target_endian = "big")] { Self((c as u32).swap_bytes()) } } } unsafe impl CheckBytes for CharLE where C: Fallible + ?Sized, C::Error: Source, { unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { #[cfg(target_endian = "little")] unsafe { char::check_bytes(value.cast(), context) } #[cfg(target_endian = "big")] unsafe { let mut bytes = *value.cast::<[u8; 4]>(); bytes.reverse(); char::check_bytes(bytes.as_ref().as_ptr().cast(), context) } } } #[repr(C, align(16))] struct Aligned(pub [u8; N]); macro_rules! bytes { ($($byte:literal),* $(,)?) => { (&$crate::tests::Aligned([$($byte,)*]).0 as &[u8]).as_ptr() } } #[test] fn test_tuples() { unsafe { check_bytes::<_, Failure>(&(42u32, true, 'x')).unwrap(); } unsafe { check_bytes::<_, Failure>(&(true,)).unwrap(); } unsafe { // These tests assume the tuple is packed (u32, bool, char) check_bytes::<(u32, bool, CharLE), Failure>( bytes![ 0u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 0x78u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::<(u32, bool, CharLE), Failure>( bytes![ 42u8, 16u8, 20u8, 3u8, 1u8, 255u8, 255u8, 255u8, 0x78u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::<(u32, bool, CharLE), Failure>( bytes![ 0u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 0x00u8, 0xd8u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); check_bytes::<(u32, bool, CharLE), Failure>( bytes![ 0u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); check_bytes::<(u32, bool, CharLE), Failure>( bytes![ 0u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 0x78u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::<(u32, bool, CharLE), Failure>( bytes![ 0u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8, 0x78u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); } } #[test] fn test_arrays() { unsafe { check_bytes::<_, Failure>(&[true, false, true, false]).unwrap(); } unsafe { check_bytes::<_, Failure>(&[false, true]).unwrap(); } unsafe { check_bytes::<[bool; 4], Failure>( bytes![1u8, 0u8, 1u8, 0u8].cast(), ) .unwrap(); check_bytes::<[bool; 4], Failure>( bytes![1u8, 2u8, 1u8, 0u8].cast(), ) .unwrap_err(); check_bytes::<[bool; 4], Failure>( bytes![2u8, 0u8, 1u8, 0u8].cast(), ) .unwrap_err(); check_bytes::<[bool; 4], Failure>( bytes![1u8, 0u8, 1u8, 2u8].cast(), ) .unwrap_err(); check_bytes::<[bool; 4], Failure>( bytes![1u8, 0u8, 1u8, 0u8, 2u8].cast(), ) .unwrap(); } } #[test] fn test_unsized() { unsafe { check_bytes::<[i32], Infallible>( &[1, 2, 3, 4] as &[i32] as *const [i32] ) .unwrap(); check_bytes::("hello world" as *const str).unwrap(); } } #[test] fn test_c_str() { macro_rules! test_cases { ($($bytes:expr, $pat:pat,)*) => { $( let bytes = $bytes; let c_str = ::ptr_meta::from_raw_parts( bytes.as_ptr().cast(), bytes.len(), ); assert!(matches!( check_bytes::(c_str), $pat, )); )* } } unsafe { test_cases! { b"hello world\0", Ok(_), b"hello world", Err(_), b"", Err(_), [0xc3u8, 0x28u8, 0x00u8], Ok(_), [0xc3u8, 0x28u8, 0x00u8, 0xc3u8, 0x28u8, 0x00u8], Err(_), } } } #[test] fn test_unit_struct() { #[derive(CheckBytes)] #[bytecheck(crate)] struct Test; unsafe { check_bytes::<_, Infallible>(&Test).unwrap(); } } #[test] fn test_tuple_struct() { #[derive(CheckBytes, Debug)] #[bytecheck(crate)] struct Test(u32, bool, CharLE); let value = Test(42, true, 'x'.into()); unsafe { check_bytes::<_, Failure>(&value).unwrap(); } unsafe { // These tests assume the struct is packed (u32, char, bool) check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::( bytes![ 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); } } #[test] fn test_struct() { #[derive(CheckBytes, Debug)] #[bytecheck(crate)] struct Test { a: u32, b: bool, c: CharLE, } let value = Test { a: 42, b: true, c: 'x'.into(), }; unsafe { check_bytes::<_, Failure>(&value).unwrap(); } unsafe { // These tests assume the struct is packed (u32, char, bool) check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::( bytes![ 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); } } #[test] fn test_generic_struct() { #[derive(CheckBytes, Debug)] #[bytecheck(crate)] struct Test { a: u32, b: T, } let value = Test { a: 42, b: true }; unsafe { check_bytes::<_, Failure>(&value).unwrap(); } unsafe { check_bytes::, Failure>( bytes![0u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8].cast(), ) .unwrap(); check_bytes::, Failure>( bytes![12u8, 34u8, 56u8, 78u8, 1u8, 255u8, 255u8, 255u8].cast(), ) .unwrap(); check_bytes::, Failure>( bytes![0u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8].cast(), ) .unwrap(); check_bytes::, Failure>( bytes![0u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8].cast(), ) .unwrap_err(); } } #[test] fn test_enum() { #[allow(dead_code)] #[derive(CheckBytes, Debug)] #[bytecheck(crate)] #[repr(u8)] enum Test { A(u32, bool, CharLE), B { a: u32, b: bool, c: CharLE }, C, } let value = Test::A(42, true, 'x'.into()); unsafe { check_bytes::<_, Failure>(&value).unwrap(); } let value = Test::B { a: 42, b: true, c: 'x'.into(), }; unsafe { check_bytes::<_, Failure>(&value).unwrap(); } let value = Test::C; unsafe { check_bytes::<_, Failure>(&value).unwrap(); } unsafe { check_bytes::( bytes![ 0u8, 0u8, 0u8, 0u8, 12u8, 34u8, 56u8, 78u8, 1u8, 255u8, 255u8, 255u8, 120u8, 0u8, 0u8, 0u8, ] .cast(), ) .unwrap(); check_bytes::( bytes![ 1u8, 0u8, 0u8, 0u8, 12u8, 34u8, 56u8, 78u8, 1u8, 255u8, 255u8, 255u8, 120u8, 0u8, 0u8, 0u8, ] .cast(), ) .unwrap(); check_bytes::( bytes![ 2u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 25u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap(); check_bytes::( bytes![ 3u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 25u8, 255u8, 255u8, 255u8, ] .cast(), ) .unwrap_err(); } } #[test] fn test_explicit_enum_values() { #[derive(CheckBytes, Debug)] #[bytecheck(crate)] #[repr(u8)] enum Test { A, B = 100, C, D = 200, E, } unsafe { check_bytes::<_, Failure>(&Test::A).unwrap(); } unsafe { check_bytes::<_, Failure>(&Test::B).unwrap(); } unsafe { check_bytes::<_, Failure>(&Test::C).unwrap(); } unsafe { check_bytes::<_, Failure>(&Test::D).unwrap(); } unsafe { check_bytes::<_, Failure>(&Test::E).unwrap(); } unsafe { check_bytes::(bytes![1u8].cast()).unwrap_err(); check_bytes::(bytes![99u8].cast()).unwrap_err(); check_bytes::(bytes![102u8].cast()).unwrap_err(); check_bytes::(bytes![199u8].cast()).unwrap_err(); check_bytes::(bytes![202u8].cast()).unwrap_err(); check_bytes::(bytes![255u8].cast()).unwrap_err(); } } #[test] fn test_recursive() { struct MyBox { inner: *const T, } unsafe impl CheckBytes for MyBox where T: CheckBytes, C: Fallible + ?Sized, { unsafe fn check_bytes( value: *const Self, context: &mut C, ) -> Result<(), C::Error> { unsafe { T::check_bytes((*value).inner, context) } } } #[allow(dead_code)] #[derive(CheckBytes)] #[bytecheck(crate)] #[repr(u8)] enum Node { Nil, Cons(#[bytecheck(omit_bounds)] MyBox), } unsafe { let nil = Node::Nil; let cons = Node::Cons(MyBox { inner: &nil as *const Node, }); check_bytes::(&cons).unwrap(); } } #[test] fn test_explicit_crate_root() { mod bytecheck {} mod m { pub use crate as bc; } #[derive(CheckBytes)] #[bytecheck(crate = m::bc)] struct Test; unsafe { check_bytes::<_, Infallible>(&Test).unwrap(); } #[derive(CheckBytes)] #[bytecheck(crate = crate)] struct Test2; unsafe { check_bytes::<_, Infallible>(&Test2).unwrap(); } } trait MyContext { fn set_value(&mut self, value: i32); } impl MyContext for Strategy { fn set_value(&mut self, value: i32) { T::set_value(self, value) } } struct FooContext { value: i32, } impl MyContext for FooContext { fn set_value(&mut self, value: i32) { self.value = value; } } #[test] fn test_derive_verify_unit_struct() { unsafe impl Verify for UnitStruct { fn verify(&self, context: &mut C) -> Result<(), C::Error> { context.set_value(1); Ok(()) } } #[derive(CheckBytes)] #[bytecheck(crate, verify)] struct UnitStruct; let mut context = FooContext { value: 0 }; unsafe { check_bytes_with_context::<_, _, Infallible>( &UnitStruct, &mut context, ) .unwrap(); } assert_eq!(context.value, 1); } #[test] fn test_derive_verify_struct() { unsafe impl Verify for Struct { fn verify(&self, context: &mut C) -> Result<(), C::Error> { context.set_value(self.value); Ok(()) } } #[derive(CheckBytes)] #[bytecheck(crate, verify)] struct Struct { value: i32, } let mut context = FooContext { value: 0 }; unsafe { check_bytes_with_context::<_, _, Infallible>( &Struct { value: 4 }, &mut context, ) .unwrap(); } assert_eq!(context.value, 4); } #[test] fn test_derive_verify_tuple_struct() { unsafe impl Verify for TupleStruct where C: Fallible + MyContext + ?Sized, { fn verify(&self, context: &mut C) -> Result<(), C::Error> { context.set_value(self.0); Ok(()) } } #[derive(CheckBytes)] #[bytecheck(crate, verify)] struct TupleStruct(i32); let mut context = FooContext { value: 0 }; unsafe { check_bytes_with_context::<_, _, Infallible>( &TupleStruct(10), &mut context, ) .unwrap(); } assert_eq!(context.value, 10); } #[test] fn test_derive_verify_enum() { unsafe impl Verify for Enum { fn verify(&self, context: &mut C) -> Result<(), C::Error> { match self { Enum::A => context.set_value(2), Enum::B(value) => context.set_value(*value), Enum::C { value } => context.set_value(*value), } Ok(()) } } #[derive(CheckBytes)] #[bytecheck(crate, verify)] #[repr(u8)] enum Enum { A, B(i32), C { value: i32 }, } // Unit variant let mut context = FooContext { value: 0 }; unsafe { check_bytes_with_context::<_, _, Failure>(&Enum::A, &mut context) .unwrap(); } assert_eq!(context.value, 2); // Tuple variant let mut context = FooContext { value: 0 }; unsafe { check_bytes_with_context::<_, _, Failure>( &Enum::B(5), &mut context, ) .unwrap(); } assert_eq!(context.value, 5); // Struct variant let mut context = FooContext { value: 0 }; unsafe { check_bytes_with_context::<_, _, Failure>( &Enum::C { value: 7 }, &mut context, ) .unwrap(); } assert_eq!(context.value, 7); } } bytecheck-0.8.0/src/uuid.rs000064400000000000000000000015021046102023000136730ustar 00000000000000use uuid_1::Uuid; use crate::{rancor::Fallible, CheckBytes}; // SAFETY: `Uuid` is `#[repr(transparent)]` around an inner `Bytes`, which is a // simple byte array. Byte arrays are always valid. unsafe impl CheckBytes for Uuid { #[inline] unsafe fn check_bytes(_: *const Self, _: &mut C) -> Result<(), C::Error> { Ok(()) } } #[cfg(test)] mod tests { use uuid_1::Uuid; use crate::{check_bytes, rancor::Infallible}; #[test] fn test_check_bytes() { let uuid_str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4"; let u = Uuid::parse_str(uuid_str).unwrap(); // SAFETY: `&u` is aligned and points to enough bytes to represent a // `Uuid`. unsafe { check_bytes::<_, Infallible>(&u).expect("failed to check uuid"); } } }