├── .cargo └── config.toml ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── settings.json ├── Cargo.toml ├── LICENSE ├── README.md ├── bytecheck ├── Cargo.toml ├── LICENSE ├── example.md └── src │ ├── lib.rs │ └── uuid.rs ├── bytecheck_derive ├── Cargo.toml ├── LICENSE └── src │ ├── attributes.rs │ ├── lib.rs │ ├── repr.rs │ └── util.rs └── rustfmt.toml /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | rustdocflags = ["--cfg", "docsrs"] 3 | 4 | [env] 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: rkyv 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | workflow_dispatch: 7 | schedule: 8 | - cron: "0 10 * * *" 9 | 10 | permissions: 11 | contents: read 12 | 13 | env: 14 | RUSTFLAGS: -Dwarnings 15 | 16 | jobs: 17 | features: 18 | name: Features / ${{ matrix.external }} 19 | runs-on: ubuntu-latest 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | external: 24 | - '' 25 | - simdutf8 uuid-1 26 | 27 | steps: 28 | - uses: actions/checkout@v4 29 | - uses: dtolnay/rust-toolchain@stable 30 | - run: cargo test --verbose --tests --no-default-features --features "${{ matrix.external }}" 31 | 32 | toolchain: 33 | name: Toolchain / ${{ matrix.toolchain }} ${{ matrix.opt }} 34 | runs-on: ubuntu-latest 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | toolchain: 39 | - stable 40 | - beta 41 | - nightly 42 | opt: 43 | - '' 44 | - --release 45 | 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: dtolnay/rust-toolchain@master 49 | with: 50 | toolchain: ${{ matrix.toolchain }} 51 | - run: cargo test --verbose ${{ matrix.opt }} 52 | 53 | miri: 54 | name: Miri / ${{ matrix.opt }} 55 | runs-on: ubuntu-latest 56 | strategy: 57 | fail-fast: false 58 | matrix: 59 | opt: 60 | - '' 61 | - --release 62 | 63 | steps: 64 | - uses: actions/checkout@v4 65 | - uses: dtolnay/rust-toolchain@miri 66 | - run: cargo miri setup 67 | - run: cargo miri test ${{ matrix.opt }} --verbose 68 | env: 69 | MIRIFLAGS: -Zmiri-disable-stacked-borrows -Zmiri-tree-borrows 70 | 71 | test: 72 | name: Test / ${{ matrix.target }} ${{ matrix.opt }} 73 | runs-on: ${{ matrix.os }} 74 | 75 | strategy: 76 | fail-fast: false 77 | matrix: 78 | opt: 79 | - '' 80 | - --release 81 | include: 82 | - os: ubuntu-latest 83 | target: x86_64-unknown-linux-gnu 84 | - os: macos-latest 85 | target: aarch64-apple-darwin 86 | - os: windows-latest 87 | target: x86_64-pc-windows-msvc 88 | 89 | steps: 90 | - uses: actions/checkout@v4 91 | - uses: dtolnay/rust-toolchain@stable 92 | - run: cargo test ${{ matrix.opt }} 93 | 94 | cross: 95 | name: Cross / ${{ matrix.target }} 96 | runs-on: ubuntu-latest 97 | 98 | strategy: 99 | fail-fast: false 100 | matrix: 101 | target: 102 | - i686-unknown-linux-gnu 103 | - i586-unknown-linux-gnu 104 | - armv7-unknown-linux-gnueabihf 105 | - aarch64-unknown-linux-gnu 106 | - thumbv6m-none-eabi 107 | 108 | steps: 109 | - uses: actions/checkout@v4 110 | - uses: dtolnay/rust-toolchain@stable 111 | - run: cargo install cross 112 | - run: cross build --no-default-features --features "simdutf8" --target ${{ matrix.target }} --verbose 113 | 114 | format: 115 | name: Format 116 | runs-on: ubuntu-latest 117 | 118 | steps: 119 | - uses: actions/checkout@v4 120 | - uses: dtolnay/rust-toolchain@nightly 121 | with: 122 | components: rustfmt 123 | - run: cargo fmt --check 124 | 125 | clippy: 126 | name: Clippy 127 | runs-on: ubuntu-latest 128 | 129 | steps: 130 | - uses: actions/checkout@v4 131 | - uses: dtolnay/rust-toolchain@nightly 132 | with: 133 | components: clippy 134 | - run: cargo clippy 135 | 136 | doc: 137 | name: Doc 138 | runs-on: ubuntu-latest 139 | 140 | steps: 141 | - uses: actions/checkout@v4 142 | - uses: dtolnay/rust-toolchain@nightly 143 | - run: cargo doc 144 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore build artifacts, backup files, and lockfiles 2 | 3 | target/ 4 | **.*.rs.bk 5 | Cargo.lock 6 | 7 | # Changes to VS Code configs are ignored by default. We intentionally check in 8 | # some settings when they're useful defaults. 9 | 10 | .vscode 11 | 12 | # Files ending in tildes are almost always temporary and not intended for 13 | # checkin. 14 | 15 | *~ 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.cargo.features": [ 3 | "uuid-1", 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "bytecheck", 4 | "bytecheck_derive", 5 | ] 6 | resolver = "2" 7 | 8 | [workspace.package] 9 | version = "0.8.1" 10 | authors = ["David Koloski "] 11 | edition = "2021" 12 | rust-version = "1.81" 13 | license = "MIT" 14 | readme = "README.md" 15 | repository = "https://github.com/rkyv/bytecheck" 16 | keywords = ["no_std", "validation", "serialization"] 17 | categories = ["encoding", "no-std", "no-std::no-alloc"] 18 | 19 | [workspace.dependencies] 20 | bytecheck = { version = "0.8", path = "bytecheck", default-features = false } 21 | bytecheck_derive = { version = "=0.8.1", path = "bytecheck_derive", default-features = false } 22 | proc-macro2 = { version = "1", default-features = false } 23 | ptr_meta = { version = "0.3", default-features = false } 24 | rancor = { version = "0.1", default-features = false } 25 | simdutf8 = { version = "0.1", default-features = false } 26 | syn = { version = "2", default-features = false } 27 | quote = { version = "1", default-features = false } 28 | 29 | [patch.crates-io] 30 | ptr_meta = { git = "https://github.com/rkyv/ptr_meta" } 31 | rancor = { git = "https://github.com/rkyv/rancor" } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 David Koloski 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `bytecheck` 2 | 3 | [![crates.io badge]][crates.io] [![docs badge]][docs] [![license badge]][license] 4 | 5 | [crates.io badge]: https://img.shields.io/crates/v/bytecheck.svg 6 | [crates.io]: https://crates.io/crates/bytecheck 7 | [docs badge]: https://img.shields.io/docsrs/bytecheck 8 | [docs]: https://docs.rs/bytecheck 9 | [license badge]: https://img.shields.io/badge/license-MIT-blue.svg 10 | [license]: https://github.com/rkyv/bytecheck/blob/master/LICENSE 11 | 12 | bytecheck is a memory validation framework for Rust. 13 | 14 | ## Documentation 15 | 16 | - [bytecheck](https://docs.rs/bytecheck), a memory validation framework for Rust 17 | - [bytecheck_derive](https://docs.rs/bytecheck_derive), the derive macro for 18 | bytecheck 19 | 20 | ## Example 21 | 22 | ```rust 23 | use bytecheck::{CheckBytes, check_bytes, rancor::Failure}; 24 | 25 | #[derive(CheckBytes, Debug)] 26 | #[repr(C)] 27 | struct Test { 28 | a: u32, 29 | b: char, 30 | c: bool, 31 | } 32 | 33 | #[repr(C, align(4))] 34 | struct Aligned([u8; N]); 35 | 36 | macro_rules! bytes { 37 | ($($byte:literal,)*) => { 38 | (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() 39 | }; 40 | ($($byte:literal),*) => { 41 | bytes!($($byte,)*) 42 | }; 43 | } 44 | 45 | // In this example, the architecture is assumed to be little-endian 46 | #[cfg(target_endian = "little")] 47 | unsafe { 48 | // These are valid bytes for a `Test` 49 | check_bytes::( 50 | bytes![ 51 | 0u8, 0u8, 0u8, 0u8, 52 | 0x78u8, 0u8, 0u8, 0u8, 53 | 1u8, 255u8, 255u8, 255u8, 54 | ].cast() 55 | ).unwrap(); 56 | 57 | // Changing the bytes for the u32 is OK, any bytes are a valid u32 58 | check_bytes::( 59 | bytes![ 60 | 42u8, 16u8, 20u8, 3u8, 61 | 0x78u8, 0u8, 0u8, 0u8, 62 | 1u8, 255u8, 255u8, 255u8, 63 | ].cast() 64 | ).unwrap(); 65 | 66 | // Characters outside the valid ranges are invalid 67 | check_bytes::( 68 | bytes![ 69 | 0u8, 0u8, 0u8, 0u8, 70 | 0x00u8, 0xd8u8, 0u8, 0u8, 71 | 1u8, 255u8, 255u8, 255u8, 72 | ].cast() 73 | ).unwrap_err(); 74 | check_bytes::( 75 | bytes![ 76 | 0u8, 0u8, 0u8, 0u8, 77 | 0x00u8, 0x00u8, 0x11u8, 0u8, 78 | 1u8, 255u8, 255u8, 255u8, 79 | ].cast() 80 | ).unwrap_err(); 81 | 82 | // 0 is a valid boolean value (false) but 2 is not 83 | check_bytes::( 84 | bytes![ 85 | 0u8, 0u8, 0u8, 0u8, 86 | 0x78u8, 0u8, 0u8, 0u8, 87 | 0u8, 255u8, 255u8, 255u8, 88 | ].cast() 89 | ).unwrap(); 90 | check_bytes::( 91 | bytes![ 92 | 0u8, 0u8, 0u8, 0u8, 93 | 0x78u8, 0u8, 0u8, 0u8, 94 | 2u8, 255u8, 255u8, 255u8, 95 | ].cast() 96 | ).unwrap_err(); 97 | } 98 | ``` 99 | -------------------------------------------------------------------------------- /bytecheck/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bytecheck" 3 | description = "Memory validation framework for Rust" 4 | version.workspace = true 5 | authors.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | keywords.workspace = true 12 | categories.workspace = true 13 | documentation = "https://docs.rs/bytecheck" 14 | 15 | [dependencies] 16 | bytecheck_derive.workspace = true 17 | ptr_meta.workspace = true 18 | rancor.workspace = true 19 | simdutf8 = { workspace = true, optional = true } 20 | 21 | # Support for various common crates. These are primarily to get users off the 22 | # ground and build some momentum. 23 | 24 | # These are NOT PLANNED to remain in bytecheck for the 1.0 release. Much like 25 | # serde, these implementations should be moved into their respective crates over 26 | # time. Before adding support for another crate, please consider getting 27 | # bytecheck support in the crate instead. 28 | 29 | uuid-1 = { package = "uuid", version = "1", optional = true, default-features = false } 30 | 31 | [features] 32 | default = ["simdutf8"] 33 | -------------------------------------------------------------------------------- /bytecheck/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2023 David Koloski 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /bytecheck/example.md: -------------------------------------------------------------------------------- 1 | ```rust 2 | use bytecheck::{CheckBytes, check_bytes, rancor::Failure}; 3 | 4 | #[derive(CheckBytes, Debug)] 5 | #[repr(C)] 6 | struct Test { 7 | a: u32, 8 | b: char, 9 | c: bool, 10 | } 11 | 12 | #[repr(C, align(4))] 13 | struct Aligned([u8; N]); 14 | 15 | macro_rules! bytes { 16 | ($($byte:literal,)*) => { 17 | (&Aligned([$($byte,)*]).0 as &[u8]).as_ptr() 18 | }; 19 | ($($byte:literal),*) => { 20 | bytes!($($byte,)*) 21 | }; 22 | } 23 | 24 | // In this example, the architecture is assumed to be little-endian 25 | #[cfg(target_endian = "little")] 26 | unsafe { 27 | // These are valid bytes for a `Test` 28 | check_bytes::( 29 | bytes![ 30 | 0u8, 0u8, 0u8, 0u8, 31 | 0x78u8, 0u8, 0u8, 0u8, 32 | 1u8, 255u8, 255u8, 255u8, 33 | ].cast() 34 | ).unwrap(); 35 | 36 | // Changing the bytes for the u32 is OK, any bytes are a valid u32 37 | check_bytes::( 38 | bytes![ 39 | 42u8, 16u8, 20u8, 3u8, 40 | 0x78u8, 0u8, 0u8, 0u8, 41 | 1u8, 255u8, 255u8, 255u8, 42 | ].cast() 43 | ).unwrap(); 44 | 45 | // Characters outside the valid ranges are invalid 46 | check_bytes::( 47 | bytes![ 48 | 0u8, 0u8, 0u8, 0u8, 49 | 0x00u8, 0xd8u8, 0u8, 0u8, 50 | 1u8, 255u8, 255u8, 255u8, 51 | ].cast() 52 | ).unwrap_err(); 53 | check_bytes::( 54 | bytes![ 55 | 0u8, 0u8, 0u8, 0u8, 56 | 0x00u8, 0x00u8, 0x11u8, 0u8, 57 | 1u8, 255u8, 255u8, 255u8, 58 | ].cast() 59 | ).unwrap_err(); 60 | 61 | // 0 is a valid boolean value (false) but 2 is not 62 | check_bytes::( 63 | bytes![ 64 | 0u8, 0u8, 0u8, 0u8, 65 | 0x78u8, 0u8, 0u8, 0u8, 66 | 0u8, 255u8, 255u8, 255u8, 67 | ].cast() 68 | ).unwrap(); 69 | check_bytes::( 70 | bytes![ 71 | 0u8, 0u8, 0u8, 0u8, 72 | 0x78u8, 0u8, 0u8, 0u8, 73 | 2u8, 255u8, 255u8, 255u8, 74 | ].cast() 75 | ).unwrap_err(); 76 | } 77 | ``` 78 | -------------------------------------------------------------------------------- /bytecheck/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # bytecheck 2 | //! 3 | //! bytecheck is a memory validation framework for Rust. 4 | //! 5 | //! For some types, creating an invalid value immediately results in undefined 6 | //! behavior. This can cause some issues when trying to validate potentially 7 | //! invalid bytes, as just casting the bytes to your type can technically cause 8 | //! errors. This makes it difficult to write validation routines, because until 9 | //! you're certain that the bytes represent valid values you cannot cast them. 10 | //! 11 | //! bytecheck provides a framework for performing these byte-level validations 12 | //! and implements checks for basic types along with a derive macro to implement 13 | //! validation for custom structs and enums. 14 | //! 15 | //! ## Design 16 | //! 17 | //! [`CheckBytes`] is at the heart of bytecheck, and does the heavy lifting of 18 | //! verifying that some bytes represent a valid type. Implementing it can be 19 | //! done manually or automatically with the [derive macro](macro@CheckBytes). 20 | //! 21 | //! ## Layout stability 22 | //! 23 | //! The layouts of types may change between compiler versions, or even different 24 | //! compilations. To guarantee stable type layout between compilations, structs, 25 | //! enums, and unions can be annotated with `#[repr(C)]`, and enums specifically 26 | //! can be annotated with `#[repr(int)]` or `#[repr(C, int)]` as well. See 27 | //! [the reference's page on type layout][reference] for more details. 28 | //! 29 | //! [reference]: https://doc.rust-lang.org/reference/type-layout.html 30 | //! 31 | //! ## Features 32 | //! 33 | //! - `derive`: Re-exports the macros from `bytecheck_derive`. Enabled by 34 | //! default. 35 | //! - `simdutf8`: Uses the `simdutf8` crate to validate UTF-8 strings. Enabled 36 | //! by default. 37 | //! 38 | //! ### Crates 39 | //! 40 | //! Bytecheck provides integrations for some common crates by default. In the 41 | //! future, crates should depend on bytecheck and provide their own integration. 42 | //! 43 | //! - [`uuid-1`](https://docs.rs/uuid/1) 44 | //! 45 | //! ## Example 46 | #![doc = include_str!("../example.md")] 47 | #![deny( 48 | future_incompatible, 49 | missing_docs, 50 | nonstandard_style, 51 | unsafe_op_in_unsafe_fn, 52 | unused, 53 | warnings, 54 | clippy::all, 55 | clippy::missing_safety_doc, 56 | clippy::undocumented_unsafe_blocks, 57 | rustdoc::broken_intra_doc_links, 58 | rustdoc::missing_crate_level_docs 59 | )] 60 | #![no_std] 61 | #![cfg_attr(all(docsrs, not(doctest)), feature(doc_cfg, doc_auto_cfg))] 62 | 63 | // Support for various common crates. These are primarily to get users off the 64 | // ground and build some momentum. 65 | 66 | // These are NOT PLANNED to remain in bytecheck for the final release. Much like 67 | // serde, these implementations should be moved into their respective crates 68 | // over time. Before adding support for another crate, please consider getting 69 | // bytecheck support in the crate instead. 70 | 71 | #[cfg(feature = "uuid-1")] 72 | mod uuid; 73 | 74 | #[cfg(not(feature = "simdutf8"))] 75 | use core::str::from_utf8; 76 | #[cfg(target_has_atomic = "8")] 77 | use core::sync::atomic::{AtomicBool, AtomicI8, AtomicU8}; 78 | #[cfg(target_has_atomic = "16")] 79 | use core::sync::atomic::{AtomicI16, AtomicU16}; 80 | #[cfg(target_has_atomic = "32")] 81 | use core::sync::atomic::{AtomicI32, AtomicU32}; 82 | #[cfg(target_has_atomic = "64")] 83 | use core::sync::atomic::{AtomicI64, AtomicU64}; 84 | use core::{ 85 | cell::{Cell, UnsafeCell}, 86 | error::Error, 87 | ffi::CStr, 88 | fmt, 89 | marker::{PhantomData, PhantomPinned}, 90 | mem::ManuallyDrop, 91 | num::{ 92 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, 93 | NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, 94 | }, 95 | ops, ptr, 96 | }; 97 | 98 | pub use bytecheck_derive::CheckBytes; 99 | pub use rancor; 100 | use rancor::{fail, Fallible, ResultExt as _, Source, Strategy, Trace}; 101 | #[cfg(feature = "simdutf8")] 102 | use simdutf8::basic::from_utf8; 103 | 104 | /// A type that can check whether a pointer points to a valid value. 105 | /// 106 | /// `CheckBytes` can be derived with [`CheckBytes`](macro@CheckBytes) or 107 | /// implemented manually for custom behavior. 108 | /// 109 | /// # Safety 110 | /// 111 | /// `check_bytes` must only return `Ok` if `value` points to a valid instance of 112 | /// `Self`. Because `value` must always be properly aligned for `Self` and point 113 | /// to enough bytes to represent the type, this implies that `value` may be 114 | /// dereferenced safely. 115 | /// 116 | /// # Example 117 | /// 118 | /// ``` 119 | /// use core::{error::Error, fmt}; 120 | /// 121 | /// use bytecheck::CheckBytes; 122 | /// use rancor::{fail, Fallible, Source}; 123 | /// 124 | /// #[repr(C, align(4))] 125 | /// pub struct NonMaxU32(u32); 126 | /// 127 | /// unsafe impl CheckBytes for NonMaxU32 128 | /// where 129 | /// C::Error: Source, 130 | /// { 131 | /// unsafe fn check_bytes( 132 | /// value: *const Self, 133 | /// context: &mut C, 134 | /// ) -> Result<(), C::Error> { 135 | /// #[derive(Debug)] 136 | /// struct NonMaxCheckError; 137 | /// 138 | /// impl fmt::Display for NonMaxCheckError { 139 | /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 140 | /// write!(f, "non-max u32 was set to u32::MAX") 141 | /// } 142 | /// } 143 | /// 144 | /// impl Error for NonMaxCheckError {} 145 | /// 146 | /// let value = unsafe { value.read() }; 147 | /// if value.0 == u32::MAX { 148 | /// fail!(NonMaxCheckError); 149 | /// } 150 | /// 151 | /// Ok(()) 152 | /// } 153 | /// } 154 | /// ``` 155 | /// 156 | /// See [`Verify`] for an example which uses less unsafe. 157 | pub unsafe trait CheckBytes { 158 | /// Checks whether the given pointer points to a valid value within the 159 | /// given context. 160 | /// 161 | /// # Safety 162 | /// 163 | /// The passed pointer must be aligned and point to enough initialized bytes 164 | /// to represent the type. 165 | unsafe fn check_bytes( 166 | value: *const Self, 167 | context: &mut C, 168 | ) -> Result<(), C::Error>; 169 | } 170 | 171 | /// A type that can check whether its invariants are upheld. 172 | /// 173 | /// When using [the derive](macro@CheckBytes), adding `#[bytecheck(verify)]` 174 | /// allows implementing `Verify` for the derived type. [`Verify::verify`] will 175 | /// be called after the type is checked and all fields are known to be valid. 176 | /// 177 | /// # Safety 178 | /// 179 | /// - `verify` must only return `Ok` if all of the invariants of this type are 180 | /// upheld by `self`. 181 | /// - `verify` may not assume that its type invariants are upheld by the given 182 | /// `self` (the invariants of each field are guaranteed to be upheld). 183 | /// 184 | /// # Example 185 | /// 186 | /// ``` 187 | /// use core::{error::Error, fmt}; 188 | /// 189 | /// use bytecheck::{CheckBytes, Verify}; 190 | /// use rancor::{fail, Fallible, Source}; 191 | /// 192 | /// #[derive(CheckBytes)] 193 | /// #[bytecheck(verify)] 194 | /// #[repr(C, align(4))] 195 | /// pub struct NonMaxU32(u32); 196 | /// 197 | /// unsafe impl Verify for NonMaxU32 198 | /// where 199 | /// C::Error: Source, 200 | /// { 201 | /// fn verify(&self, context: &mut C) -> Result<(), C::Error> { 202 | /// #[derive(Debug)] 203 | /// struct NonMaxCheckError; 204 | /// 205 | /// impl fmt::Display for NonMaxCheckError { 206 | /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 207 | /// write!(f, "non-max u32 was set to u32::MAX") 208 | /// } 209 | /// } 210 | /// 211 | /// impl Error for NonMaxCheckError {} 212 | /// 213 | /// if self.0 == u32::MAX { 214 | /// fail!(NonMaxCheckError); 215 | /// } 216 | /// 217 | /// Ok(()) 218 | /// } 219 | /// } 220 | /// ``` 221 | pub unsafe trait Verify { 222 | /// Checks whether the invariants of this type are upheld by `self`. 223 | fn verify(&self, context: &mut C) -> Result<(), C::Error>; 224 | } 225 | 226 | /// Checks whether the given pointer points to a valid value. 227 | /// 228 | /// # Safety 229 | /// 230 | /// The passed pointer must be aligned and point to enough initialized bytes to 231 | /// represent the type. 232 | /// 233 | /// # Example 234 | /// 235 | /// ``` 236 | /// use bytecheck::check_bytes; 237 | /// use rancor::Failure; 238 | /// 239 | /// unsafe { 240 | /// // 0 and 1 are valid values for bools 241 | /// check_bytes::((&0u8 as *const u8).cast()).unwrap(); 242 | /// check_bytes::((&1u8 as *const u8).cast()).unwrap(); 243 | /// 244 | /// // 2 is not a valid value 245 | /// check_bytes::((&2u8 as *const u8).cast()).unwrap_err(); 246 | /// } 247 | /// ``` 248 | #[inline] 249 | pub unsafe fn check_bytes(value: *const T) -> Result<(), E> 250 | where 251 | T: CheckBytes> + ?Sized, 252 | { 253 | // SAFETY: The safety conditions of `check_bytes_with_context` are the same 254 | // as the safety conditions of this function. 255 | unsafe { check_bytes_with_context(value, &mut ()) } 256 | } 257 | 258 | /// Checks whether the given pointer points to a valid value within the given 259 | /// context. 260 | /// 261 | /// # Safety 262 | /// 263 | /// The passed pointer must be aligned and point to enough initialized bytes to 264 | /// represent the type. 265 | /// 266 | /// # Example 267 | /// 268 | /// ``` 269 | /// use core::{error::Error, fmt}; 270 | /// 271 | /// use bytecheck::{check_bytes_with_context, CheckBytes, Verify}; 272 | /// use rancor::{fail, Failure, Fallible, Source, Strategy}; 273 | /// 274 | /// trait Context { 275 | /// fn is_allowed(&self, value: u8) -> bool; 276 | /// } 277 | /// 278 | /// impl Context for Strategy { 279 | /// fn is_allowed(&self, value: u8) -> bool { 280 | /// T::is_allowed(self, value) 281 | /// } 282 | /// } 283 | /// 284 | /// struct Allowed(u8); 285 | /// 286 | /// impl Context for Allowed { 287 | /// fn is_allowed(&self, value: u8) -> bool { 288 | /// value == self.0 289 | /// } 290 | /// } 291 | /// 292 | /// #[derive(CheckBytes)] 293 | /// #[bytecheck(verify)] 294 | /// #[repr(C)] 295 | /// pub struct ContextualByte(u8); 296 | /// 297 | /// unsafe impl Verify for ContextualByte 298 | /// where 299 | /// C::Error: Source, 300 | /// { 301 | /// fn verify(&self, context: &mut C) -> Result<(), C::Error> { 302 | /// #[derive(Debug)] 303 | /// struct InvalidByte(u8); 304 | /// 305 | /// impl fmt::Display for InvalidByte { 306 | /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 307 | /// write!(f, "invalid contextual byte: {}", self.0) 308 | /// } 309 | /// } 310 | /// 311 | /// impl Error for InvalidByte {} 312 | /// 313 | /// if !context.is_allowed(self.0) { 314 | /// fail!(InvalidByte(self.0)); 315 | /// } 316 | /// 317 | /// Ok(()) 318 | /// } 319 | /// } 320 | /// 321 | /// let value = 45u8; 322 | /// unsafe { 323 | /// // Checking passes when the context allows byte 45 324 | /// check_bytes_with_context::( 325 | /// (&value as *const u8).cast(), 326 | /// &mut Allowed(45), 327 | /// ) 328 | /// .unwrap(); 329 | /// 330 | /// // Checking fails when the conteext does not allow byte 45 331 | /// check_bytes_with_context::( 332 | /// (&value as *const u8).cast(), 333 | /// &mut Allowed(0), 334 | /// ) 335 | /// .unwrap_err(); 336 | /// } 337 | /// ``` 338 | pub unsafe fn check_bytes_with_context( 339 | value: *const T, 340 | context: &mut C, 341 | ) -> Result<(), E> 342 | where 343 | T: CheckBytes> + ?Sized, 344 | { 345 | // SAFETY: The safety conditions of `check_bytes` are the same as the safety 346 | // conditions of this function. 347 | unsafe { CheckBytes::check_bytes(value, Strategy::wrap(context)) } 348 | } 349 | 350 | macro_rules! impl_primitive { 351 | ($type:ty) => { 352 | // SAFETY: All bit patterns are valid for these primitive types. 353 | unsafe impl CheckBytes for $type { 354 | #[inline] 355 | unsafe fn check_bytes( 356 | _: *const Self, 357 | _: &mut C, 358 | ) -> Result<(), C::Error> { 359 | Ok(()) 360 | } 361 | } 362 | }; 363 | } 364 | 365 | macro_rules! impl_primitives { 366 | ($($type:ty),* $(,)?) => { 367 | $( 368 | impl_primitive!($type); 369 | )* 370 | } 371 | } 372 | 373 | impl_primitives! { 374 | (), 375 | i8, i16, i32, i64, i128, 376 | u8, u16, u32, u64, u128, 377 | f32, f64, 378 | } 379 | #[cfg(target_has_atomic = "8")] 380 | impl_primitives!(AtomicI8, AtomicU8); 381 | #[cfg(target_has_atomic = "16")] 382 | impl_primitives!(AtomicI16, AtomicU16); 383 | #[cfg(target_has_atomic = "32")] 384 | impl_primitives!(AtomicI32, AtomicU32); 385 | #[cfg(target_has_atomic = "64")] 386 | impl_primitives!(AtomicI64, AtomicU64); 387 | 388 | // SAFETY: `PhantomData` is a zero-sized type and so all bit patterns are valid. 389 | unsafe impl CheckBytes for PhantomData { 390 | #[inline] 391 | unsafe fn check_bytes(_: *const Self, _: &mut C) -> Result<(), C::Error> { 392 | Ok(()) 393 | } 394 | } 395 | 396 | // SAFETY: `PhantomPinned` is a zero-sized type and so all bit patterns are 397 | // valid. 398 | unsafe impl CheckBytes for PhantomPinned { 399 | #[inline] 400 | unsafe fn check_bytes(_: *const Self, _: &mut C) -> Result<(), C::Error> { 401 | Ok(()) 402 | } 403 | } 404 | 405 | // SAFETY: `ManuallyDrop` is a `#[repr(transparent)]` wrapper around a `T`, 406 | // and so `value` points to a valid `ManuallyDrop` if it also points to a 407 | // valid `T`. 408 | unsafe impl CheckBytes for ManuallyDrop 409 | where 410 | T: CheckBytes + ?Sized, 411 | C: Fallible + ?Sized, 412 | C::Error: Trace, 413 | { 414 | #[inline] 415 | unsafe fn check_bytes( 416 | value: *const Self, 417 | c: &mut C, 418 | ) -> Result<(), C::Error> { 419 | // SAFETY: Because `ManuallyDrop` is `#[repr(transparent)]`, a 420 | // pointer to a `ManuallyDrop` is guaranteed to be the same as a 421 | // pointer to `T`. We can't call `.cast()` here because `T` may be 422 | // an unsized type. 423 | let inner_ptr = 424 | unsafe { core::mem::transmute::<*const Self, *const T>(value) }; 425 | // SAFETY: The caller has guaranteed that `value` is aligned for 426 | // `ManuallyDrop` and points to enough bytes to represent 427 | // `ManuallyDrop`. Since `ManuallyDrop` is `#[repr(transparent)]`, 428 | // `inner_ptr` is also aligned for `T` and points to enough bytes to 429 | // represent it. 430 | unsafe { 431 | T::check_bytes(inner_ptr, c) 432 | .trace("while checking inner value of `ManuallyDrop`") 433 | } 434 | } 435 | } 436 | 437 | // SAFETY: `UnsafeCell` has the same memory layout as `T`, and so `value` 438 | // points to a valid `UnsafeCell` if it also points to a valid `T`. 439 | unsafe impl CheckBytes for UnsafeCell 440 | where 441 | T: CheckBytes + ?Sized, 442 | C: Fallible + ?Sized, 443 | C::Error: Trace, 444 | { 445 | #[inline] 446 | unsafe fn check_bytes( 447 | value: *const Self, 448 | c: &mut C, 449 | ) -> Result<(), C::Error> { 450 | // SAFETY: Because `UnsafeCell` has the same memory layout as 451 | // `T`, a pointer to an `UnsafeCell` is guaranteed to be the same 452 | // as a pointer to `T`. We can't call `.cast()` here because `T` may 453 | // be an unsized type. 454 | let inner_ptr = 455 | unsafe { core::mem::transmute::<*const Self, *const T>(value) }; 456 | // SAFETY: The caller has guaranteed that `value` is aligned for 457 | // `UnsafeCell` and points to enough bytes to represent 458 | // `UnsafeCell`. Since `UnsafeCell` has the same layout `T`, 459 | // `inner_ptr` is also aligned for `T` and points to enough bytes to 460 | // represent it. 461 | unsafe { 462 | T::check_bytes(inner_ptr, c) 463 | .trace("while checking inner value of `UnsafeCell`") 464 | } 465 | } 466 | } 467 | 468 | // SAFETY: `Cell` has the same memory layout as `UnsafeCell` (and 469 | // therefore `T` itself), and so `value` points to a valid `UnsafeCell` if it 470 | // also points to a valid `T`. 471 | unsafe impl CheckBytes for Cell 472 | where 473 | T: CheckBytes + ?Sized, 474 | C: Fallible + ?Sized, 475 | C::Error: Trace, 476 | { 477 | #[inline] 478 | unsafe fn check_bytes( 479 | value: *const Self, 480 | c: &mut C, 481 | ) -> Result<(), C::Error> { 482 | // SAFETY: Because `Cell` has the same memory layout as 483 | // `UnsafeCell` (and therefore `T` itself), a pointer to a 484 | // `Cell` is guaranteed to be the same as a pointer to `T`. We 485 | // can't call `.cast()` here because `T` may be an unsized type. 486 | let inner_ptr = 487 | unsafe { core::mem::transmute::<*const Self, *const T>(value) }; 488 | // SAFETY: The caller has guaranteed that `value` is aligned for 489 | // `Cell` and points to enough bytes to represent `Cell`. Since 490 | // `Cell` has the same layout as `UnsafeCell` ( and therefore `T` 491 | // itself), `inner_ptr` is also aligned for `T` and points to enough 492 | // bytes to represent it. 493 | unsafe { 494 | T::check_bytes(inner_ptr, c) 495 | .trace("while checking inner value of `Cell`") 496 | } 497 | } 498 | } 499 | 500 | #[derive(Debug)] 501 | struct BoolCheckError { 502 | byte: u8, 503 | } 504 | 505 | impl fmt::Display for BoolCheckError { 506 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 507 | write!( 508 | f, 509 | "bool set to invalid byte {}, expected either 0 or 1", 510 | self.byte, 511 | ) 512 | } 513 | } 514 | 515 | impl Error for BoolCheckError {} 516 | 517 | // SAFETY: A bool is a one byte value that must either be 0 or 1. `check_bytes` 518 | // only returns `Ok` if `value` is 0 or 1. 519 | unsafe impl CheckBytes for bool 520 | where 521 | C: Fallible + ?Sized, 522 | C::Error: Source, 523 | { 524 | #[inline] 525 | unsafe fn check_bytes( 526 | value: *const Self, 527 | _: &mut C, 528 | ) -> Result<(), C::Error> { 529 | // SAFETY: `value` is a pointer to a `bool`, which has a size and 530 | // alignment of one. `u8` also has a size and alignment of one, and all 531 | // bit patterns are valid for `u8`. So we can cast `value` to a `u8` 532 | // pointer and read from it. 533 | let byte = unsafe { *value.cast::() }; 534 | match byte { 535 | 0 | 1 => Ok(()), 536 | _ => fail!(BoolCheckError { byte }), 537 | } 538 | } 539 | } 540 | 541 | #[cfg(target_has_atomic = "8")] 542 | // SAFETY: `AtomicBool` has the same ABI as `bool`, so if `value` points to a 543 | // valid `bool` then it also points to a valid `AtomicBool`. 544 | unsafe impl CheckBytes for AtomicBool 545 | where 546 | C: Fallible + ?Sized, 547 | C::Error: Source, 548 | { 549 | #[inline] 550 | unsafe fn check_bytes( 551 | value: *const Self, 552 | context: &mut C, 553 | ) -> Result<(), C::Error> { 554 | // SAFETY: `AtomicBool` has the same ABI as `bool`, so a pointer that is 555 | // aligned for `AtomicBool` and points to enough bytes for `AtomicBool` 556 | // is also aligned for `bool` and points to enough bytes for `bool`. 557 | unsafe { bool::check_bytes(value.cast(), context) } 558 | } 559 | } 560 | 561 | // SAFETY: If `char::try_from` succeeds with the pointed-to-value, then it must 562 | // be a valid value for `char`. 563 | unsafe impl CheckBytes for char 564 | where 565 | C: Fallible + ?Sized, 566 | C::Error: Source, 567 | { 568 | #[inline] 569 | unsafe fn check_bytes(ptr: *const Self, _: &mut C) -> Result<(), C::Error> { 570 | // SAFETY: `char` and `u32` are both four bytes, but we're not 571 | // guaranteed that they have the same alignment. Using `read_unaligned` 572 | // ensures that we can read a `u32` regardless and try to convert it to 573 | // a `char`. 574 | let value = unsafe { ptr.cast::().read_unaligned() }; 575 | char::try_from(value).into_error()?; 576 | Ok(()) 577 | } 578 | } 579 | 580 | #[derive(Debug)] 581 | struct TupleIndexContext { 582 | index: usize, 583 | } 584 | 585 | impl fmt::Display for TupleIndexContext { 586 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 587 | write!(f, "while checking index {} of tuple", self.index) 588 | } 589 | } 590 | 591 | macro_rules! impl_tuple { 592 | ($($type:ident $index:tt),*) => { 593 | // SAFETY: A tuple is valid if all of its elements are valid, and 594 | // `check_bytes` only returns `Ok` when all of the elements validated 595 | // successfully. 596 | unsafe impl<$($type,)* C> CheckBytes for ($($type,)*) 597 | where 598 | $($type: CheckBytes,)* 599 | C: Fallible + ?Sized, 600 | C::Error: Trace, 601 | { 602 | #[inline] 603 | #[allow(clippy::unneeded_wildcard_pattern)] 604 | unsafe fn check_bytes( 605 | value: *const Self, 606 | context: &mut C, 607 | ) -> Result<(), C::Error> { 608 | $( 609 | // SAFETY: The caller has guaranteed that `value` points to 610 | // enough bytes for this tuple and is properly aligned, so 611 | // we can create pointers to each element and check them. 612 | unsafe { 613 | <$type>::check_bytes( 614 | ptr::addr_of!((*value).$index), 615 | context, 616 | ).with_trace(|| TupleIndexContext { index: $index })?; 617 | } 618 | )* 619 | Ok(()) 620 | } 621 | } 622 | } 623 | } 624 | 625 | impl_tuple!(T0 0); 626 | impl_tuple!(T0 0, T1 1); 627 | impl_tuple!(T0 0, T1 1, T2 2); 628 | impl_tuple!(T0 0, T1 1, T2 2, T3 3); 629 | impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4); 630 | impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5); 631 | impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6); 632 | impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7); 633 | impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8); 634 | impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8, T9 9); 635 | impl_tuple!(T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8, T9 9, T10 10); 636 | impl_tuple!( 637 | T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8, T9 9, T10 10, T11 11 638 | ); 639 | impl_tuple!( 640 | T0 0, T1 1, T2 2, T3 3, T4 4, T5 5, T6 6, T7 7, T8 8, T9 9, T10 10, T11 11, 641 | T12 12 642 | ); 643 | 644 | #[derive(Debug)] 645 | struct ArrayCheckContext { 646 | index: usize, 647 | } 648 | 649 | impl fmt::Display for ArrayCheckContext { 650 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 651 | write!(f, "while checking index '{}' of array", self.index) 652 | } 653 | } 654 | 655 | // SAFETY: `check_bytes` only returns `Ok` if each element of the array is 656 | // valid. If each element of the array is valid then the whole array is also 657 | // valid. 658 | unsafe impl CheckBytes for [T; N] 659 | where 660 | T: CheckBytes, 661 | C: Fallible + ?Sized, 662 | C::Error: Trace, 663 | { 664 | #[inline] 665 | unsafe fn check_bytes( 666 | value: *const Self, 667 | context: &mut C, 668 | ) -> Result<(), C::Error> { 669 | let base = value.cast::(); 670 | for index in 0..N { 671 | // SAFETY: The caller has guaranteed that `value` points to enough 672 | // bytes for this array and is properly aligned, so we can create 673 | // pointers to each element and check them. 674 | unsafe { 675 | T::check_bytes(base.add(index), context) 676 | .with_trace(|| ArrayCheckContext { index })?; 677 | } 678 | } 679 | Ok(()) 680 | } 681 | } 682 | 683 | #[derive(Debug)] 684 | struct SliceCheckContext { 685 | index: usize, 686 | } 687 | 688 | impl fmt::Display for SliceCheckContext { 689 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 690 | write!(f, "while checking index '{}' of slice", self.index) 691 | } 692 | } 693 | 694 | // SAFETY: `check_bytes` only returns `Ok` if each element of the slice is 695 | // valid. If each element of the slice is valid then the whole slice is also 696 | // valid. 697 | unsafe impl CheckBytes for [T] 698 | where 699 | T: CheckBytes, 700 | C: Fallible + ?Sized, 701 | C::Error: Trace, 702 | { 703 | #[inline] 704 | unsafe fn check_bytes( 705 | value: *const Self, 706 | context: &mut C, 707 | ) -> Result<(), C::Error> { 708 | let (data_address, len) = ptr_meta::to_raw_parts(value); 709 | let base = data_address.cast::(); 710 | for index in 0..len { 711 | // SAFETY: The caller has guaranteed that `value` points to enough 712 | // bytes for this slice and is properly aligned, so we can create 713 | // pointers to each element and check them. 714 | unsafe { 715 | T::check_bytes(base.add(index), context) 716 | .with_trace(|| SliceCheckContext { index })?; 717 | } 718 | } 719 | Ok(()) 720 | } 721 | } 722 | 723 | // SAFETY: `check_bytes` only returns `Ok` if the bytes pointed to by `str` are 724 | // valid UTF-8. If they are valid UTF-8 then the overall `str` is also valid. 725 | unsafe impl CheckBytes for str 726 | where 727 | C: Fallible + ?Sized, 728 | C::Error: Source, 729 | { 730 | #[inline] 731 | unsafe fn check_bytes( 732 | value: *const Self, 733 | _: &mut C, 734 | ) -> Result<(), C::Error> { 735 | #[derive(Debug)] 736 | struct Utf8Error; 737 | 738 | impl fmt::Display for Utf8Error { 739 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 740 | write!(f, "invalid UTF-8") 741 | } 742 | } 743 | 744 | impl Error for Utf8Error {} 745 | 746 | let slice_ptr = value as *const [u8]; 747 | // SAFETY: The caller has guaranteed that `value` is properly-aligned 748 | // and points to enough bytes for its `str`. Because a `u8` slice has 749 | // the same layout as a `str`, we can dereference it for UTF-8 750 | // validation. 751 | let slice = unsafe { &*slice_ptr }; 752 | 753 | // Checking whether a byte slice is ASCII is much faster than checking 754 | // whether it is valid UTF-8. Falling back to a full UTF-8 check 755 | // afterward nets a performance improvement for the average case. 756 | if !slice.is_ascii() { 757 | from_utf8(slice).map_err(|_| Utf8Error).into_error()?; 758 | } 759 | 760 | Ok(()) 761 | } 762 | } 763 | 764 | // SAFETY: `check_bytes` only returns `Ok` when the bytes constitute a valid 765 | // `CStr` per `CStr::from_bytes_with_nul`. 766 | unsafe impl CheckBytes for CStr 767 | where 768 | C: Fallible + ?Sized, 769 | C::Error: Source, 770 | { 771 | #[inline] 772 | unsafe fn check_bytes( 773 | value: *const Self, 774 | _: &mut C, 775 | ) -> Result<(), C::Error> { 776 | let slice_ptr = value as *const [u8]; 777 | // SAFETY: The caller has guaranteed that `value` is properly-aligned 778 | // and points to enough bytes for its `CStr`. Because a `u8` slice has 779 | // the same layout as a `CStr`, we can dereference it for validation. 780 | let slice = unsafe { &*slice_ptr }; 781 | CStr::from_bytes_with_nul(slice).into_error()?; 782 | Ok(()) 783 | } 784 | } 785 | 786 | // Generic contexts used by the derive. 787 | 788 | /// Context for errors resulting from invalid structs. 789 | /// 790 | /// This context is used by the derive macro to trace which field of a struct 791 | /// failed validation. 792 | #[derive(Debug)] 793 | pub struct StructCheckContext { 794 | /// The name of the struct with an invalid field. 795 | pub struct_name: &'static str, 796 | /// The name of the struct field that was invalid. 797 | pub field_name: &'static str, 798 | } 799 | 800 | impl fmt::Display for StructCheckContext { 801 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 802 | write!( 803 | f, 804 | "while checking field '{}' of struct '{}'", 805 | self.field_name, self.struct_name 806 | ) 807 | } 808 | } 809 | 810 | /// Context for errors resulting from invalid tuple structs. 811 | /// 812 | /// This context is used by the derive macro to trace which field of a tuple 813 | /// struct failed validation. 814 | #[derive(Debug)] 815 | pub struct TupleStructCheckContext { 816 | /// The name of the tuple struct with an invalid field. 817 | pub tuple_struct_name: &'static str, 818 | /// The index of the tuple struct field that was invalid. 819 | pub field_index: usize, 820 | } 821 | 822 | impl fmt::Display for TupleStructCheckContext { 823 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 824 | write!( 825 | f, 826 | "while checking field index {} of tuple struct '{}'", 827 | self.field_index, self.tuple_struct_name, 828 | ) 829 | } 830 | } 831 | 832 | /// An error resulting from an invalid enum tag. 833 | /// 834 | /// This context is used by the derive macro to trace what the invalid 835 | /// discriminant for an enum is. 836 | #[derive(Debug)] 837 | pub struct InvalidEnumDiscriminantError { 838 | /// The name of the enum with an invalid discriminant. 839 | pub enum_name: &'static str, 840 | /// The invalid value of the enum discriminant. 841 | pub invalid_discriminant: T, 842 | } 843 | 844 | impl fmt::Display for InvalidEnumDiscriminantError { 845 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 846 | write!( 847 | f, 848 | "invalid discriminant '{}' for enum '{}'", 849 | self.invalid_discriminant, self.enum_name 850 | ) 851 | } 852 | } 853 | 854 | impl Error for InvalidEnumDiscriminantError where 855 | T: fmt::Debug + fmt::Display 856 | { 857 | } 858 | 859 | /// Context for errors resulting from checking enum variants with named fields. 860 | /// 861 | /// This context is used by the derive macro to trace which field of an enum 862 | /// variant failed validation. 863 | #[derive(Debug)] 864 | pub struct NamedEnumVariantCheckContext { 865 | /// The name of the enum with an invalid variant. 866 | pub enum_name: &'static str, 867 | /// The name of the variant that was invalid. 868 | pub variant_name: &'static str, 869 | /// The name of the field that was invalid. 870 | pub field_name: &'static str, 871 | } 872 | 873 | impl fmt::Display for NamedEnumVariantCheckContext { 874 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 875 | write!( 876 | f, 877 | "while checking field '{}' of variant '{}' of enum '{}'", 878 | self.field_name, self.variant_name, self.enum_name, 879 | ) 880 | } 881 | } 882 | 883 | /// Context for errors resulting from checking enum variants with unnamed 884 | /// fields. 885 | /// 886 | /// This context is used by the derive macro to trace which field of an enum 887 | /// variant failed validation. 888 | #[derive(Debug)] 889 | pub struct UnnamedEnumVariantCheckContext { 890 | /// The name of the enum with an invalid variant. 891 | pub enum_name: &'static str, 892 | /// The name of the variant that was invalid. 893 | pub variant_name: &'static str, 894 | /// The name of the field that was invalid. 895 | pub field_index: usize, 896 | } 897 | 898 | impl fmt::Display for UnnamedEnumVariantCheckContext { 899 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 900 | write!( 901 | f, 902 | "while checking field index {} of variant '{}' of enum '{}'", 903 | self.field_index, self.variant_name, self.enum_name, 904 | ) 905 | } 906 | } 907 | 908 | // Range types 909 | 910 | // SAFETY: A `Range` is valid if its `start` and `end` are both valid, and 911 | // `check_bytes` only returns `Ok` when both `start` and `end` are valid. Note 912 | // that `Range` does not require `start` be less than `end`. 913 | unsafe impl CheckBytes for ops::Range 914 | where 915 | T: CheckBytes, 916 | C: Fallible + ?Sized, 917 | C::Error: Trace, 918 | { 919 | #[inline] 920 | unsafe fn check_bytes( 921 | value: *const Self, 922 | context: &mut C, 923 | ) -> Result<(), C::Error> { 924 | // SAFETY: The caller has guaranteed that `value` is aligned for a 925 | // `Range` and points to enough initialized bytes for one, so a 926 | // pointer projected to the `start` field will be properly aligned for 927 | // a `T` and point to enough initialized bytes for one too. 928 | unsafe { 929 | T::check_bytes(ptr::addr_of!((*value).start), context).with_trace( 930 | || StructCheckContext { 931 | struct_name: "Range", 932 | field_name: "start", 933 | }, 934 | )?; 935 | } 936 | // SAFETY: Same reasoning as above, but for `end`. 937 | unsafe { 938 | T::check_bytes(ptr::addr_of!((*value).end), context).with_trace( 939 | || StructCheckContext { 940 | struct_name: "Range", 941 | field_name: "end", 942 | }, 943 | )?; 944 | } 945 | Ok(()) 946 | } 947 | } 948 | 949 | // SAFETY: A `RangeFrom` is valid if its `start` is valid, and `check_bytes` 950 | // only returns `Ok` when its `start` is valid. 951 | unsafe impl CheckBytes for ops::RangeFrom 952 | where 953 | T: CheckBytes, 954 | C: Fallible + ?Sized, 955 | C::Error: Trace, 956 | { 957 | #[inline] 958 | unsafe fn check_bytes( 959 | value: *const Self, 960 | context: &mut C, 961 | ) -> Result<(), C::Error> { 962 | // SAFETY: The caller has guaranteed that `value` is aligned for a 963 | // `RangeFrom` and points to enough initialized bytes for one, so a 964 | // pointer projected to the `start` field will be properly aligned for 965 | // a `T` and point to enough initialized bytes for one too. 966 | unsafe { 967 | T::check_bytes(ptr::addr_of!((*value).start), context).with_trace( 968 | || StructCheckContext { 969 | struct_name: "RangeFrom", 970 | field_name: "start", 971 | }, 972 | )?; 973 | } 974 | Ok(()) 975 | } 976 | } 977 | 978 | // SAFETY: `RangeFull` is a ZST and so every pointer to one is valid. 979 | unsafe impl CheckBytes for ops::RangeFull { 980 | #[inline] 981 | unsafe fn check_bytes(_: *const Self, _: &mut C) -> Result<(), C::Error> { 982 | Ok(()) 983 | } 984 | } 985 | 986 | // SAFETY: A `RangeTo` is valid if its `end` is valid, and `check_bytes` only 987 | // returns `Ok` when its `end` is valid. 988 | unsafe impl CheckBytes for ops::RangeTo 989 | where 990 | T: CheckBytes, 991 | C: Fallible + ?Sized, 992 | C::Error: Trace, 993 | { 994 | #[inline] 995 | unsafe fn check_bytes( 996 | value: *const Self, 997 | context: &mut C, 998 | ) -> Result<(), C::Error> { 999 | // SAFETY: The caller has guaranteed that `value` is aligned for a 1000 | // `RangeTo` and points to enough initialized bytes for one, so a 1001 | // pointer projected to the `end` field will be properly aligned for 1002 | // a `T` and point to enough initialized bytes for one too. 1003 | unsafe { 1004 | T::check_bytes(ptr::addr_of!((*value).end), context).with_trace( 1005 | || StructCheckContext { 1006 | struct_name: "RangeTo", 1007 | field_name: "end", 1008 | }, 1009 | )?; 1010 | } 1011 | Ok(()) 1012 | } 1013 | } 1014 | 1015 | // SAFETY: A `RangeToInclusive` is valid if its `end` is valid, and 1016 | // `check_bytes` only returns `Ok` when its `end` is valid. 1017 | unsafe impl CheckBytes for ops::RangeToInclusive 1018 | where 1019 | T: CheckBytes, 1020 | C: Fallible + ?Sized, 1021 | C::Error: Trace, 1022 | { 1023 | #[inline] 1024 | unsafe fn check_bytes( 1025 | value: *const Self, 1026 | context: &mut C, 1027 | ) -> Result<(), C::Error> { 1028 | // SAFETY: The caller has guaranteed that `value` is aligned for a 1029 | // `RangeToInclusive` and points to enough initialized bytes for one, 1030 | // so a pointer projected to the `end` field will be properly aligned 1031 | // for a `T` and point to enough initialized bytes for one too. 1032 | unsafe { 1033 | T::check_bytes(ptr::addr_of!((*value).end), context).with_trace( 1034 | || StructCheckContext { 1035 | struct_name: "RangeToInclusive", 1036 | field_name: "end", 1037 | }, 1038 | )?; 1039 | } 1040 | Ok(()) 1041 | } 1042 | } 1043 | 1044 | #[derive(Debug)] 1045 | struct NonZeroCheckError; 1046 | 1047 | impl fmt::Display for NonZeroCheckError { 1048 | #[inline] 1049 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 1050 | write!(f, "nonzero integer is zero") 1051 | } 1052 | } 1053 | 1054 | impl Error for NonZeroCheckError {} 1055 | 1056 | macro_rules! impl_nonzero { 1057 | ($nonzero:ident, $underlying:ident) => { 1058 | // SAFETY: `check_bytes` only returns `Ok` when `value` is not zero, the 1059 | // only validity condition for non-zero integer types. 1060 | unsafe impl CheckBytes for $nonzero 1061 | where 1062 | C: Fallible + ?Sized, 1063 | C::Error: Source, 1064 | { 1065 | #[inline] 1066 | unsafe fn check_bytes( 1067 | value: *const Self, 1068 | _: &mut C, 1069 | ) -> Result<(), C::Error> { 1070 | // SAFETY: Non-zero integer types are guaranteed to have the 1071 | // same ABI as their corresponding integer types. Those integers 1072 | // have no validity requirements, so we can cast and dereference 1073 | // value to check if it is equal to zero. 1074 | if unsafe { *value.cast::<$underlying>() } == 0 { 1075 | fail!(NonZeroCheckError); 1076 | } else { 1077 | Ok(()) 1078 | } 1079 | } 1080 | } 1081 | }; 1082 | } 1083 | 1084 | impl_nonzero!(NonZeroI8, i8); 1085 | impl_nonzero!(NonZeroI16, i16); 1086 | impl_nonzero!(NonZeroI32, i32); 1087 | impl_nonzero!(NonZeroI64, i64); 1088 | impl_nonzero!(NonZeroI128, i128); 1089 | impl_nonzero!(NonZeroU8, u8); 1090 | impl_nonzero!(NonZeroU16, u16); 1091 | impl_nonzero!(NonZeroU32, u32); 1092 | impl_nonzero!(NonZeroU64, u64); 1093 | impl_nonzero!(NonZeroU128, u128); 1094 | 1095 | #[cfg(test)] 1096 | #[macro_use] 1097 | mod tests { 1098 | use core::ffi::CStr; 1099 | 1100 | use rancor::{Failure, Fallible, Infallible, Source, Strategy}; 1101 | 1102 | use crate::{check_bytes, check_bytes_with_context, CheckBytes, Verify}; 1103 | 1104 | #[derive(Debug)] 1105 | #[repr(transparent)] 1106 | struct CharLE(u32); 1107 | 1108 | impl From for CharLE { 1109 | fn from(c: char) -> Self { 1110 | #[cfg(target_endian = "little")] 1111 | { 1112 | Self(c as u32) 1113 | } 1114 | #[cfg(target_endian = "big")] 1115 | { 1116 | Self((c as u32).swap_bytes()) 1117 | } 1118 | } 1119 | } 1120 | 1121 | unsafe impl CheckBytes for CharLE 1122 | where 1123 | C: Fallible + ?Sized, 1124 | C::Error: Source, 1125 | { 1126 | unsafe fn check_bytes( 1127 | value: *const Self, 1128 | context: &mut C, 1129 | ) -> Result<(), C::Error> { 1130 | #[cfg(target_endian = "little")] 1131 | unsafe { 1132 | char::check_bytes(value.cast(), context) 1133 | } 1134 | #[cfg(target_endian = "big")] 1135 | unsafe { 1136 | let mut bytes = *value.cast::<[u8; 4]>(); 1137 | bytes.reverse(); 1138 | char::check_bytes(bytes.as_ref().as_ptr().cast(), context) 1139 | } 1140 | } 1141 | } 1142 | 1143 | #[repr(C, align(16))] 1144 | struct Aligned(pub [u8; N]); 1145 | 1146 | macro_rules! bytes { 1147 | ($($byte:literal),* $(,)?) => { 1148 | (&$crate::tests::Aligned([$($byte,)*]).0 as &[u8]).as_ptr() 1149 | } 1150 | } 1151 | 1152 | #[test] 1153 | fn test_tuples() { 1154 | unsafe { 1155 | check_bytes::<_, Failure>(&(42u32, true, 'x')).unwrap(); 1156 | } 1157 | unsafe { 1158 | check_bytes::<_, Failure>(&(true,)).unwrap(); 1159 | } 1160 | 1161 | unsafe { 1162 | // These tests assume the tuple is packed (u32, bool, char) 1163 | check_bytes::<(u32, bool, CharLE), Failure>( 1164 | bytes![ 1165 | 0u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 0x78u8, 0u8, 1166 | 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, 1167 | ] 1168 | .cast(), 1169 | ) 1170 | .unwrap(); 1171 | check_bytes::<(u32, bool, CharLE), Failure>( 1172 | bytes![ 1173 | 42u8, 16u8, 20u8, 3u8, 1u8, 255u8, 255u8, 255u8, 0x78u8, 1174 | 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, 1175 | ] 1176 | .cast(), 1177 | ) 1178 | .unwrap(); 1179 | check_bytes::<(u32, bool, CharLE), Failure>( 1180 | bytes![ 1181 | 0u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 0x00u8, 1182 | 0xd8u8, 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, 1183 | ] 1184 | .cast(), 1185 | ) 1186 | .unwrap_err(); 1187 | check_bytes::<(u32, bool, CharLE), Failure>( 1188 | bytes![ 1189 | 0u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8, 0x00u8, 1190 | 0x00u8, 0x11u8, 0u8, 255u8, 255u8, 255u8, 255u8, 1191 | ] 1192 | .cast(), 1193 | ) 1194 | .unwrap_err(); 1195 | check_bytes::<(u32, bool, CharLE), Failure>( 1196 | bytes![ 1197 | 0u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8, 0x78u8, 0u8, 1198 | 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, 1199 | ] 1200 | .cast(), 1201 | ) 1202 | .unwrap(); 1203 | check_bytes::<(u32, bool, CharLE), Failure>( 1204 | bytes![ 1205 | 0u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8, 0x78u8, 0u8, 1206 | 0u8, 0u8, 255u8, 255u8, 255u8, 255u8, 1207 | ] 1208 | .cast(), 1209 | ) 1210 | .unwrap_err(); 1211 | } 1212 | } 1213 | 1214 | #[test] 1215 | fn test_arrays() { 1216 | unsafe { 1217 | check_bytes::<_, Failure>(&[true, false, true, false]).unwrap(); 1218 | } 1219 | unsafe { 1220 | check_bytes::<_, Failure>(&[false, true]).unwrap(); 1221 | } 1222 | 1223 | unsafe { 1224 | check_bytes::<[bool; 4], Failure>( 1225 | bytes![1u8, 0u8, 1u8, 0u8].cast(), 1226 | ) 1227 | .unwrap(); 1228 | check_bytes::<[bool; 4], Failure>( 1229 | bytes![1u8, 2u8, 1u8, 0u8].cast(), 1230 | ) 1231 | .unwrap_err(); 1232 | check_bytes::<[bool; 4], Failure>( 1233 | bytes![2u8, 0u8, 1u8, 0u8].cast(), 1234 | ) 1235 | .unwrap_err(); 1236 | check_bytes::<[bool; 4], Failure>( 1237 | bytes![1u8, 0u8, 1u8, 2u8].cast(), 1238 | ) 1239 | .unwrap_err(); 1240 | check_bytes::<[bool; 4], Failure>( 1241 | bytes![1u8, 0u8, 1u8, 0u8, 2u8].cast(), 1242 | ) 1243 | .unwrap(); 1244 | } 1245 | } 1246 | 1247 | #[test] 1248 | fn test_unsized() { 1249 | unsafe { 1250 | check_bytes::<[i32], Infallible>( 1251 | &[1, 2, 3, 4] as &[i32] as *const [i32] 1252 | ) 1253 | .unwrap(); 1254 | check_bytes::("hello world" as *const str).unwrap(); 1255 | } 1256 | } 1257 | 1258 | #[test] 1259 | fn test_c_str() { 1260 | macro_rules! test_cases { 1261 | ($($bytes:expr, $pat:pat,)*) => { 1262 | $( 1263 | let bytes = $bytes; 1264 | let c_str = ::ptr_meta::from_raw_parts( 1265 | bytes.as_ptr().cast(), 1266 | bytes.len(), 1267 | ); 1268 | assert!(matches!( 1269 | check_bytes::(c_str), 1270 | $pat, 1271 | )); 1272 | )* 1273 | } 1274 | } 1275 | 1276 | unsafe { 1277 | test_cases! { 1278 | b"hello world\0", Ok(_), 1279 | b"hello world", Err(_), 1280 | b"", Err(_), 1281 | [0xc3u8, 0x28u8, 0x00u8], Ok(_), 1282 | [0xc3u8, 0x28u8, 0x00u8, 0xc3u8, 0x28u8, 0x00u8], Err(_), 1283 | } 1284 | } 1285 | } 1286 | 1287 | #[test] 1288 | fn test_unit_struct() { 1289 | #[derive(CheckBytes)] 1290 | #[bytecheck(crate)] 1291 | struct Test; 1292 | 1293 | unsafe { 1294 | check_bytes::<_, Infallible>(&Test).unwrap(); 1295 | } 1296 | } 1297 | 1298 | #[test] 1299 | fn test_tuple_struct() { 1300 | #[derive(CheckBytes, Debug)] 1301 | #[bytecheck(crate)] 1302 | struct Test(u32, bool, CharLE); 1303 | 1304 | let value = Test(42, true, 'x'.into()); 1305 | 1306 | unsafe { 1307 | check_bytes::<_, Failure>(&value).unwrap(); 1308 | } 1309 | 1310 | unsafe { 1311 | // These tests assume the struct is packed (u32, char, bool) 1312 | check_bytes::( 1313 | bytes![ 1314 | 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 1315 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1316 | ] 1317 | .cast(), 1318 | ) 1319 | .unwrap(); 1320 | check_bytes::( 1321 | bytes![ 1322 | 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 1323 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1324 | ] 1325 | .cast(), 1326 | ) 1327 | .unwrap(); 1328 | check_bytes::( 1329 | bytes![ 1330 | 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, 1331 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1332 | ] 1333 | .cast(), 1334 | ) 1335 | .unwrap_err(); 1336 | check_bytes::( 1337 | bytes![ 1338 | 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 1339 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1340 | ] 1341 | .cast(), 1342 | ) 1343 | .unwrap_err(); 1344 | check_bytes::( 1345 | bytes![ 1346 | 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 1347 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1348 | ] 1349 | .cast(), 1350 | ) 1351 | .unwrap(); 1352 | check_bytes::( 1353 | bytes![ 1354 | 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 1355 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1356 | ] 1357 | .cast(), 1358 | ) 1359 | .unwrap_err(); 1360 | } 1361 | } 1362 | 1363 | #[test] 1364 | fn test_struct() { 1365 | #[derive(CheckBytes, Debug)] 1366 | #[bytecheck(crate)] 1367 | struct Test { 1368 | a: u32, 1369 | b: bool, 1370 | c: CharLE, 1371 | } 1372 | 1373 | let value = Test { 1374 | a: 42, 1375 | b: true, 1376 | c: 'x'.into(), 1377 | }; 1378 | 1379 | unsafe { 1380 | check_bytes::<_, Failure>(&value).unwrap(); 1381 | } 1382 | 1383 | unsafe { 1384 | // These tests assume the struct is packed (u32, char, bool) 1385 | check_bytes::( 1386 | bytes![ 1387 | 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 1388 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1389 | ] 1390 | .cast(), 1391 | ) 1392 | .unwrap(); 1393 | check_bytes::( 1394 | bytes![ 1395 | 42u8, 16u8, 20u8, 3u8, 0x78u8, 0u8, 0u8, 0u8, 1u8, 255u8, 1396 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1397 | ] 1398 | .cast(), 1399 | ) 1400 | .unwrap(); 1401 | check_bytes::( 1402 | bytes![ 1403 | 0u8, 0u8, 0u8, 0u8, 0x00u8, 0xd8u8, 0u8, 0u8, 1u8, 255u8, 1404 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1405 | ] 1406 | .cast(), 1407 | ) 1408 | .unwrap_err(); 1409 | check_bytes::( 1410 | bytes![ 1411 | 0u8, 0u8, 0u8, 0u8, 0x00u8, 0x00u8, 0x11u8, 0u8, 1u8, 1412 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1413 | ] 1414 | .cast(), 1415 | ) 1416 | .unwrap_err(); 1417 | check_bytes::( 1418 | bytes![ 1419 | 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 0u8, 255u8, 1420 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1421 | ] 1422 | .cast(), 1423 | ) 1424 | .unwrap(); 1425 | check_bytes::( 1426 | bytes![ 1427 | 0u8, 0u8, 0u8, 0u8, 0x78u8, 0u8, 0u8, 0u8, 2u8, 255u8, 1428 | 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1429 | ] 1430 | .cast(), 1431 | ) 1432 | .unwrap_err(); 1433 | } 1434 | } 1435 | 1436 | #[test] 1437 | fn test_generic_struct() { 1438 | #[derive(CheckBytes, Debug)] 1439 | #[bytecheck(crate)] 1440 | struct Test { 1441 | a: u32, 1442 | b: T, 1443 | } 1444 | 1445 | let value = Test { a: 42, b: true }; 1446 | 1447 | unsafe { 1448 | check_bytes::<_, Failure>(&value).unwrap(); 1449 | } 1450 | 1451 | unsafe { 1452 | check_bytes::, Failure>( 1453 | bytes![0u8, 0u8, 0u8, 0u8, 1u8, 255u8, 255u8, 255u8].cast(), 1454 | ) 1455 | .unwrap(); 1456 | check_bytes::, Failure>( 1457 | bytes![12u8, 34u8, 56u8, 78u8, 1u8, 255u8, 255u8, 255u8].cast(), 1458 | ) 1459 | .unwrap(); 1460 | check_bytes::, Failure>( 1461 | bytes![0u8, 0u8, 0u8, 0u8, 0u8, 255u8, 255u8, 255u8].cast(), 1462 | ) 1463 | .unwrap(); 1464 | check_bytes::, Failure>( 1465 | bytes![0u8, 0u8, 0u8, 0u8, 2u8, 255u8, 255u8, 255u8].cast(), 1466 | ) 1467 | .unwrap_err(); 1468 | } 1469 | } 1470 | 1471 | #[test] 1472 | fn test_enum() { 1473 | #[allow(dead_code)] 1474 | #[derive(CheckBytes, Debug)] 1475 | #[bytecheck(crate)] 1476 | #[repr(u8)] 1477 | enum Test { 1478 | A(u32, bool, CharLE), 1479 | B { a: u32, b: bool, c: CharLE }, 1480 | C, 1481 | } 1482 | 1483 | let value = Test::A(42, true, 'x'.into()); 1484 | 1485 | unsafe { 1486 | check_bytes::<_, Failure>(&value).unwrap(); 1487 | } 1488 | 1489 | let value = Test::B { 1490 | a: 42, 1491 | b: true, 1492 | c: 'x'.into(), 1493 | }; 1494 | 1495 | unsafe { 1496 | check_bytes::<_, Failure>(&value).unwrap(); 1497 | } 1498 | 1499 | let value = Test::C; 1500 | 1501 | unsafe { 1502 | check_bytes::<_, Failure>(&value).unwrap(); 1503 | } 1504 | 1505 | unsafe { 1506 | check_bytes::( 1507 | bytes![ 1508 | 0u8, 0u8, 0u8, 0u8, 12u8, 34u8, 56u8, 78u8, 1u8, 255u8, 1509 | 255u8, 255u8, 120u8, 0u8, 0u8, 0u8, 1510 | ] 1511 | .cast(), 1512 | ) 1513 | .unwrap(); 1514 | check_bytes::( 1515 | bytes![ 1516 | 1u8, 0u8, 0u8, 0u8, 12u8, 34u8, 56u8, 78u8, 1u8, 255u8, 1517 | 255u8, 255u8, 120u8, 0u8, 0u8, 0u8, 1518 | ] 1519 | .cast(), 1520 | ) 1521 | .unwrap(); 1522 | check_bytes::( 1523 | bytes![ 1524 | 2u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1525 | 255u8, 255u8, 255u8, 255u8, 25u8, 255u8, 255u8, 255u8, 1526 | ] 1527 | .cast(), 1528 | ) 1529 | .unwrap(); 1530 | check_bytes::( 1531 | bytes![ 1532 | 3u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 255u8, 1533 | 255u8, 255u8, 255u8, 255u8, 25u8, 255u8, 255u8, 255u8, 1534 | ] 1535 | .cast(), 1536 | ) 1537 | .unwrap_err(); 1538 | } 1539 | } 1540 | 1541 | #[test] 1542 | fn test_explicit_enum_values() { 1543 | #[derive(CheckBytes, Debug)] 1544 | #[bytecheck(crate)] 1545 | #[repr(u8)] 1546 | enum Test { 1547 | A, 1548 | B = 100, 1549 | C, 1550 | D = 200, 1551 | E, 1552 | } 1553 | 1554 | unsafe { 1555 | check_bytes::<_, Failure>(&Test::A).unwrap(); 1556 | } 1557 | unsafe { 1558 | check_bytes::<_, Failure>(&Test::B).unwrap(); 1559 | } 1560 | unsafe { 1561 | check_bytes::<_, Failure>(&Test::C).unwrap(); 1562 | } 1563 | unsafe { 1564 | check_bytes::<_, Failure>(&Test::D).unwrap(); 1565 | } 1566 | unsafe { 1567 | check_bytes::<_, Failure>(&Test::E).unwrap(); 1568 | } 1569 | 1570 | unsafe { 1571 | check_bytes::(bytes![1u8].cast()).unwrap_err(); 1572 | check_bytes::(bytes![99u8].cast()).unwrap_err(); 1573 | check_bytes::(bytes![102u8].cast()).unwrap_err(); 1574 | check_bytes::(bytes![199u8].cast()).unwrap_err(); 1575 | check_bytes::(bytes![202u8].cast()).unwrap_err(); 1576 | check_bytes::(bytes![255u8].cast()).unwrap_err(); 1577 | } 1578 | } 1579 | 1580 | #[test] 1581 | fn test_recursive() { 1582 | struct MyBox { 1583 | inner: *const T, 1584 | } 1585 | 1586 | unsafe impl CheckBytes for MyBox 1587 | where 1588 | T: CheckBytes, 1589 | C: Fallible + ?Sized, 1590 | { 1591 | unsafe fn check_bytes( 1592 | value: *const Self, 1593 | context: &mut C, 1594 | ) -> Result<(), C::Error> { 1595 | unsafe { T::check_bytes((*value).inner, context) } 1596 | } 1597 | } 1598 | 1599 | #[allow(dead_code)] 1600 | #[derive(CheckBytes)] 1601 | #[bytecheck(crate)] 1602 | #[repr(u8)] 1603 | enum Node { 1604 | Nil, 1605 | Cons(#[bytecheck(omit_bounds)] MyBox), 1606 | } 1607 | 1608 | unsafe { 1609 | let nil = Node::Nil; 1610 | let cons = Node::Cons(MyBox { 1611 | inner: &nil as *const Node, 1612 | }); 1613 | check_bytes::(&cons).unwrap(); 1614 | } 1615 | } 1616 | 1617 | #[test] 1618 | fn test_explicit_crate_root() { 1619 | mod bytecheck {} 1620 | mod m { 1621 | pub use crate as bc; 1622 | } 1623 | 1624 | #[derive(CheckBytes)] 1625 | #[bytecheck(crate = m::bc)] 1626 | struct Test; 1627 | 1628 | unsafe { 1629 | check_bytes::<_, Infallible>(&Test).unwrap(); 1630 | } 1631 | 1632 | #[derive(CheckBytes)] 1633 | #[bytecheck(crate = crate)] 1634 | struct Test2; 1635 | 1636 | unsafe { 1637 | check_bytes::<_, Infallible>(&Test2).unwrap(); 1638 | } 1639 | } 1640 | 1641 | trait MyContext { 1642 | fn set_value(&mut self, value: i32); 1643 | } 1644 | 1645 | impl MyContext for Strategy { 1646 | fn set_value(&mut self, value: i32) { 1647 | T::set_value(self, value) 1648 | } 1649 | } 1650 | 1651 | struct FooContext { 1652 | value: i32, 1653 | } 1654 | 1655 | impl MyContext for FooContext { 1656 | fn set_value(&mut self, value: i32) { 1657 | self.value = value; 1658 | } 1659 | } 1660 | 1661 | #[test] 1662 | fn test_derive_verify_unit_struct() { 1663 | unsafe impl Verify for UnitStruct { 1664 | fn verify(&self, context: &mut C) -> Result<(), C::Error> { 1665 | context.set_value(1); 1666 | Ok(()) 1667 | } 1668 | } 1669 | 1670 | #[derive(CheckBytes)] 1671 | #[bytecheck(crate, verify)] 1672 | struct UnitStruct; 1673 | 1674 | let mut context = FooContext { value: 0 }; 1675 | unsafe { 1676 | check_bytes_with_context::<_, _, Infallible>( 1677 | &UnitStruct, 1678 | &mut context, 1679 | ) 1680 | .unwrap(); 1681 | } 1682 | 1683 | assert_eq!(context.value, 1); 1684 | } 1685 | 1686 | #[test] 1687 | fn test_derive_verify_struct() { 1688 | unsafe impl Verify for Struct { 1689 | fn verify(&self, context: &mut C) -> Result<(), C::Error> { 1690 | context.set_value(self.value); 1691 | Ok(()) 1692 | } 1693 | } 1694 | 1695 | #[derive(CheckBytes)] 1696 | #[bytecheck(crate, verify)] 1697 | struct Struct { 1698 | value: i32, 1699 | } 1700 | 1701 | let mut context = FooContext { value: 0 }; 1702 | unsafe { 1703 | check_bytes_with_context::<_, _, Infallible>( 1704 | &Struct { value: 4 }, 1705 | &mut context, 1706 | ) 1707 | .unwrap(); 1708 | } 1709 | 1710 | assert_eq!(context.value, 4); 1711 | } 1712 | 1713 | #[test] 1714 | fn test_derive_verify_tuple_struct() { 1715 | unsafe impl Verify for TupleStruct 1716 | where 1717 | C: Fallible + MyContext + ?Sized, 1718 | { 1719 | fn verify(&self, context: &mut C) -> Result<(), C::Error> { 1720 | context.set_value(self.0); 1721 | Ok(()) 1722 | } 1723 | } 1724 | 1725 | #[derive(CheckBytes)] 1726 | #[bytecheck(crate, verify)] 1727 | struct TupleStruct(i32); 1728 | 1729 | let mut context = FooContext { value: 0 }; 1730 | unsafe { 1731 | check_bytes_with_context::<_, _, Infallible>( 1732 | &TupleStruct(10), 1733 | &mut context, 1734 | ) 1735 | .unwrap(); 1736 | } 1737 | 1738 | assert_eq!(context.value, 10); 1739 | } 1740 | 1741 | #[test] 1742 | fn test_derive_verify_enum() { 1743 | unsafe impl Verify for Enum { 1744 | fn verify(&self, context: &mut C) -> Result<(), C::Error> { 1745 | match self { 1746 | Enum::A => context.set_value(2), 1747 | Enum::B(value) => context.set_value(*value), 1748 | Enum::C { value } => context.set_value(*value), 1749 | } 1750 | Ok(()) 1751 | } 1752 | } 1753 | 1754 | #[derive(CheckBytes)] 1755 | #[bytecheck(crate, verify)] 1756 | #[repr(u8)] 1757 | enum Enum { 1758 | A, 1759 | B(i32), 1760 | C { value: i32 }, 1761 | } 1762 | 1763 | // Unit variant 1764 | let mut context = FooContext { value: 0 }; 1765 | unsafe { 1766 | check_bytes_with_context::<_, _, Failure>(&Enum::A, &mut context) 1767 | .unwrap(); 1768 | } 1769 | 1770 | assert_eq!(context.value, 2); 1771 | 1772 | // Tuple variant 1773 | let mut context = FooContext { value: 0 }; 1774 | unsafe { 1775 | check_bytes_with_context::<_, _, Failure>( 1776 | &Enum::B(5), 1777 | &mut context, 1778 | ) 1779 | .unwrap(); 1780 | } 1781 | 1782 | assert_eq!(context.value, 5); 1783 | 1784 | // Struct variant 1785 | let mut context = FooContext { value: 0 }; 1786 | unsafe { 1787 | check_bytes_with_context::<_, _, Failure>( 1788 | &Enum::C { value: 7 }, 1789 | &mut context, 1790 | ) 1791 | .unwrap(); 1792 | } 1793 | 1794 | assert_eq!(context.value, 7); 1795 | } 1796 | } 1797 | -------------------------------------------------------------------------------- /bytecheck/src/uuid.rs: -------------------------------------------------------------------------------- 1 | use uuid_1::Uuid; 2 | 3 | use crate::{rancor::Fallible, CheckBytes}; 4 | 5 | // SAFETY: `Uuid` is `#[repr(transparent)]` around an inner `Bytes`, which is a 6 | // simple byte array. Byte arrays are always valid. 7 | unsafe impl CheckBytes for Uuid { 8 | #[inline] 9 | unsafe fn check_bytes(_: *const Self, _: &mut C) -> Result<(), C::Error> { 10 | Ok(()) 11 | } 12 | } 13 | 14 | #[cfg(test)] 15 | mod tests { 16 | use uuid_1::Uuid; 17 | 18 | use crate::{check_bytes, rancor::Infallible}; 19 | 20 | #[test] 21 | fn test_check_bytes() { 22 | let uuid_str = "f9168c5e-ceb2-4faa-b6bf-329bf39fa1e4"; 23 | let u = Uuid::parse_str(uuid_str).unwrap(); 24 | 25 | // SAFETY: `&u` is aligned and points to enough bytes to represent a 26 | // `Uuid`. 27 | unsafe { 28 | check_bytes::<_, Infallible>(&u).expect("failed to check uuid"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /bytecheck_derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bytecheck_derive" 3 | description = "Derive macro for bytecheck" 4 | version.workspace = true 5 | authors.workspace = true 6 | edition.workspace = true 7 | rust-version.workspace = true 8 | license.workspace = true 9 | readme.workspace = true 10 | repository.workspace = true 11 | keywords.workspace = true 12 | categories.workspace = true 13 | documentation = "https://docs.rs/bytecheck_derive" 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [lib] 18 | proc-macro = true 19 | 20 | [dependencies] 21 | proc-macro2.workspace = true 22 | syn = { workspace = true, features = ["clone-impls", "derive", "full", "parsing", "printing", "proc-macro"] } 23 | quote.workspace = true 24 | -------------------------------------------------------------------------------- /bytecheck_derive/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 David Koloski 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /bytecheck_derive/src/attributes.rs: -------------------------------------------------------------------------------- 1 | use quote::ToTokens; 2 | use syn::{ 3 | meta::ParseNestedMeta, parenthesized, parse::Parse, parse_quote, 4 | punctuated::Punctuated, AttrStyle, DeriveInput, Error, Field, Path, Token, 5 | WherePredicate, 6 | }; 7 | 8 | fn try_set_attribute( 9 | attribute: &mut Option, 10 | value: T, 11 | name: &'static str, 12 | ) -> Result<(), Error> { 13 | if attribute.is_none() { 14 | *attribute = Some(value); 15 | Ok(()) 16 | } else { 17 | Err(Error::new_spanned( 18 | value, 19 | format!("{name} already specified"), 20 | )) 21 | } 22 | } 23 | 24 | #[derive(Default)] 25 | pub struct Attributes { 26 | pub bounds: Option>, 27 | crate_path: Option, 28 | pub verify: Option, 29 | } 30 | 31 | impl Attributes { 32 | fn parse_check_bytes_attributes( 33 | &mut self, 34 | meta: ParseNestedMeta<'_>, 35 | ) -> Result<(), Error> { 36 | if meta.path.is_ident("bounds") { 37 | let bounds; 38 | parenthesized!(bounds in meta.input); 39 | let bounds = 40 | bounds.parse_terminated(WherePredicate::parse, Token![,])?; 41 | try_set_attribute(&mut self.bounds, bounds, "bounds") 42 | } else if meta.path.is_ident("crate") { 43 | if meta.input.parse::().is_ok() { 44 | let path = meta.input.parse::()?; 45 | try_set_attribute(&mut self.crate_path, path, "crate") 46 | } else if meta.input.is_empty() || meta.input.peek(Token![,]) { 47 | try_set_attribute( 48 | &mut self.crate_path, 49 | parse_quote! { crate }, 50 | "crate", 51 | ) 52 | } else { 53 | Err(meta.error("expected `crate` or `crate = ...`")) 54 | } 55 | } else if meta.path.is_ident("verify") { 56 | if !meta.input.is_empty() && !meta.input.peek(Token![,]) { 57 | return Err(meta.error("verify argument must be a path")); 58 | } 59 | 60 | try_set_attribute(&mut self.verify, meta.path, "verify") 61 | } else { 62 | Err(meta.error("unrecognized bytecheck argument")) 63 | } 64 | } 65 | 66 | pub fn parse(input: &DeriveInput) -> Result { 67 | let mut result = Self::default(); 68 | 69 | for attr in input.attrs.iter() { 70 | if !matches!(attr.style, AttrStyle::Outer) { 71 | continue; 72 | } 73 | 74 | if attr.path().is_ident("bytecheck") { 75 | attr.parse_nested_meta(|nested| { 76 | result.parse_check_bytes_attributes(nested) 77 | })?; 78 | } 79 | } 80 | 81 | Ok(result) 82 | } 83 | 84 | pub fn crate_path(&self) -> Path { 85 | self.crate_path 86 | .clone() 87 | .unwrap_or_else(|| parse_quote! { ::bytecheck }) 88 | } 89 | } 90 | 91 | #[derive(Default)] 92 | pub struct FieldAttributes { 93 | pub omit_bounds: Option, 94 | } 95 | 96 | impl FieldAttributes { 97 | fn parse_meta(&mut self, meta: ParseNestedMeta<'_>) -> Result<(), Error> { 98 | if meta.path.is_ident("omit_bounds") { 99 | self.omit_bounds = Some(meta.path); 100 | Ok(()) 101 | } else { 102 | Err(meta.error("unrecognized bytecheck arguments")) 103 | } 104 | } 105 | 106 | pub fn parse(input: &Field) -> Result { 107 | let mut result = Self::default(); 108 | 109 | for attr in input.attrs.iter() { 110 | if attr.path().is_ident("bytecheck") { 111 | attr.parse_nested_meta(|meta| result.parse_meta(meta))?; 112 | } 113 | } 114 | 115 | Ok(result) 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /bytecheck_derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Procedural macros for bytecheck. 2 | 3 | #![deny( 4 | rust_2018_compatibility, 5 | rust_2018_idioms, 6 | future_incompatible, 7 | nonstandard_style, 8 | unused, 9 | clippy::all 10 | )] 11 | 12 | mod attributes; 13 | mod repr; 14 | mod util; 15 | 16 | use proc_macro2::TokenStream; 17 | use quote::quote; 18 | use syn::{ 19 | parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Error, 20 | Field, Fields, Ident, Index, Path, 21 | }; 22 | 23 | use crate::{ 24 | attributes::{Attributes, FieldAttributes}, 25 | repr::Repr, 26 | util::{iter_fields, strip_raw}, 27 | }; 28 | 29 | /// Derives `CheckBytes` for the labeled type. 30 | /// 31 | /// This derive macro automatically adds a type bound `field: CheckBytes<__C>` 32 | /// for each field type. This can cause an overflow while evaluating trait 33 | /// bounds if the structure eventually references its own type, as the 34 | /// implementation of `CheckBytes` for a struct depends on each field type 35 | /// implementing it as well. Adding the attribute `#[check_bytes(omit_bounds)]` 36 | /// to a field will suppress this trait bound and allow recursive structures. 37 | /// This may be too coarse for some types, in which case additional type bounds 38 | /// may be required with `bounds(...)`. 39 | /// 40 | /// # Attributes 41 | /// 42 | /// Additional arguments can be specified using attributes. 43 | /// 44 | /// `#[bytecheck(...)]` accepts the following attributes: 45 | /// 46 | /// ## Types only 47 | /// 48 | /// - `bounds(...)`: Adds additional bounds to the `CheckBytes` implementation. 49 | /// This can be especially useful when dealing with recursive structures, 50 | /// where bounds may need to be omitted to prevent recursive type definitions. 51 | /// In the context of the added bounds, `__C` is the name of the context 52 | /// generic (e.g. `__C: MyContext`). 53 | /// - `crate = ...`: Chooses an alternative crate path to import bytecheck from. 54 | /// - `verify`: Adds an additional verification step after the validity of each 55 | /// field has been checked. See the `Verify` trait for more information. 56 | /// 57 | /// ## Fields only 58 | /// 59 | /// - `omit_bounds`: Omits trait bounds for the annotated field in the generated 60 | /// impl. 61 | #[proc_macro_derive(CheckBytes, attributes(bytecheck))] 62 | pub fn check_bytes_derive( 63 | input: proc_macro::TokenStream, 64 | ) -> proc_macro::TokenStream { 65 | match derive_check_bytes(parse_macro_input!(input as DeriveInput)) { 66 | Ok(result) => result.into(), 67 | Err(e) => e.to_compile_error().into(), 68 | } 69 | } 70 | 71 | fn derive_check_bytes(mut input: DeriveInput) -> Result { 72 | let attributes = Attributes::parse(&input)?; 73 | 74 | let crate_path = attributes.crate_path(); 75 | 76 | let name = &input.ident; 77 | 78 | let mut trait_generics = input.generics.clone(); 79 | 80 | // Split type generics for use later 81 | input.generics.make_where_clause(); 82 | let (type_impl_generics, type_ty_generics, type_where_clause) = 83 | input.generics.split_for_impl(); 84 | let type_where_clause = type_where_clause.unwrap(); 85 | 86 | // Trait generics are created by modifying the type generics. 87 | 88 | // We add a context parameter __C for the CheckBytes type parameter. 89 | trait_generics.params.push(parse_quote! { 90 | __C: #crate_path::rancor::Fallible + ?::core::marker::Sized 91 | }); 92 | // We add context error bounds to the where clause for the trait impl. 93 | let trait_where_clause = trait_generics.make_where_clause(); 94 | trait_where_clause.predicates.push(match &input.data { 95 | // Structs and unions just propagate any errors from checking their 96 | // fields, so the error type of the context just needs to be `Trace`. 97 | Data::Struct(_) | Data::Union(_) => parse_quote! { 98 | < 99 | __C as #crate_path::rancor::Fallible 100 | >::Error: #crate_path::rancor::Trace 101 | }, 102 | // Enums may error while checking the discriminant, so the error type of 103 | // the context needs to implement `Source` so we can create a new error 104 | // from an `InvalidEnumDiscriminantError`. 105 | Data::Enum(_) => parse_quote! { 106 | < 107 | __C as #crate_path::rancor::Fallible 108 | >::Error: #crate_path::rancor::Source 109 | }, 110 | }); 111 | // If the user specified any aditional bounds, we add them to the where 112 | // clause. 113 | if let Some(ref bounds) = attributes.bounds { 114 | for clause in bounds { 115 | trait_where_clause.predicates.push(clause.clone()); 116 | } 117 | } 118 | // If the user specified `verify`, then we need to bound `Self: Verify<__C>` 119 | // so we can call `Verify::verify`. 120 | let verify = if attributes.verify.is_some() { 121 | trait_where_clause.predicates.push(parse_quote!( 122 | #name #type_ty_generics: #crate_path::Verify<__C> 123 | )); 124 | Some(quote! { 125 | <#name #type_ty_generics as #crate_path::Verify<__C>>::verify( 126 | unsafe { &*value }, 127 | context, 128 | )?; 129 | }) 130 | } else { 131 | None 132 | }; 133 | 134 | let mut check_where = trait_where_clause.clone(); 135 | for field in iter_fields(&input.data) { 136 | let field_attrs = FieldAttributes::parse(field)?; 137 | if field_attrs.omit_bounds.is_none() { 138 | let ty = &field.ty; 139 | check_where.predicates.push(parse_quote! { 140 | #ty: #crate_path::CheckBytes<__C> 141 | }); 142 | } 143 | } 144 | 145 | // Split trait generics for use later 146 | let (trait_impl_generics, _, trait_where_clause) = 147 | trait_generics.split_for_impl(); 148 | let trait_where_clause = trait_where_clause.unwrap(); 149 | 150 | // Build CheckBytes impl 151 | let check_bytes_impl = match input.data { 152 | Data::Struct(ref data) => match data.fields { 153 | Fields::Named(ref fields) => { 154 | let field_checks = fields.named.iter().map(|f| { 155 | let field = &f.ident; 156 | let ty = &f.ty; 157 | quote! { 158 | <#ty as #crate_path::CheckBytes<__C>>::check_bytes( 159 | ::core::ptr::addr_of!((*value).#field), 160 | context 161 | ).map_err(|e| { 162 | < 163 | < 164 | __C as #crate_path::rancor::Fallible 165 | >::Error as #crate_path::rancor::Trace 166 | >::trace( 167 | e, 168 | #crate_path::StructCheckContext { 169 | struct_name: ::core::stringify!(#name), 170 | field_name: ::core::stringify!(#field), 171 | }, 172 | ) 173 | })?; 174 | } 175 | }); 176 | 177 | quote! { 178 | #[automatically_derived] 179 | // SAFETY: `check_bytes` only returns `Ok` if all of the 180 | // fields of the struct are valid. If all of the fields are 181 | // valid, then the overall struct is also valid. 182 | unsafe impl #trait_impl_generics 183 | #crate_path::CheckBytes<__C> for #name #type_ty_generics 184 | #check_where 185 | { 186 | unsafe fn check_bytes( 187 | value: *const Self, 188 | context: &mut __C, 189 | ) -> ::core::result::Result< 190 | (), 191 | <__C as #crate_path::rancor::Fallible>::Error, 192 | > { 193 | #(#field_checks)* 194 | #verify 195 | ::core::result::Result::Ok(()) 196 | } 197 | } 198 | } 199 | } 200 | Fields::Unnamed(ref fields) => { 201 | let field_checks = 202 | fields.unnamed.iter().enumerate().map(|(i, f)| { 203 | let ty = &f.ty; 204 | let index = Index::from(i); 205 | quote! { 206 | < 207 | #ty as #crate_path::CheckBytes<__C> 208 | >::check_bytes( 209 | ::core::ptr::addr_of!((*value).#index), 210 | context 211 | ).map_err(|e| { 212 | < 213 | < 214 | __C as #crate_path::rancor::Fallible 215 | >::Error as #crate_path::rancor::Trace 216 | >::trace( 217 | e, 218 | #crate_path::TupleStructCheckContext { 219 | tuple_struct_name: ::core::stringify!( 220 | #name 221 | ), 222 | field_index: #i, 223 | }, 224 | ) 225 | })?; 226 | } 227 | }); 228 | 229 | quote! { 230 | #[automatically_derived] 231 | // SAFETY: `check_bytes` only returns `Ok` if all of the 232 | // fields of the struct are valid. If all of the fields are 233 | // valid, then the overall struct is also valid. 234 | unsafe impl #trait_impl_generics 235 | #crate_path::CheckBytes<__C> for #name #type_ty_generics 236 | #check_where 237 | { 238 | unsafe fn check_bytes( 239 | value: *const Self, 240 | context: &mut __C, 241 | ) -> ::core::result::Result< 242 | (), 243 | <__C as #crate_path::rancor::Fallible>::Error, 244 | > { 245 | #(#field_checks)* 246 | #verify 247 | ::core::result::Result::Ok(()) 248 | } 249 | } 250 | } 251 | } 252 | Fields::Unit => { 253 | quote! { 254 | #[automatically_derived] 255 | // SAFETY: Unit structs are always valid since they have a 256 | // size of 0 and no invalid bit patterns. 257 | unsafe impl #trait_impl_generics 258 | #crate_path::CheckBytes<__C> for #name #type_ty_generics 259 | #trait_where_clause 260 | { 261 | unsafe fn check_bytes( 262 | value: *const Self, 263 | context: &mut __C, 264 | ) -> ::core::result::Result< 265 | (), 266 | <__C as #crate_path::rancor::Fallible>::Error, 267 | > { 268 | #verify 269 | ::core::result::Result::Ok(()) 270 | } 271 | } 272 | } 273 | } 274 | }, 275 | Data::Enum(ref data) => { 276 | let repr = Repr::from_attrs(&input.attrs)?; 277 | let primitive = match repr { 278 | Repr::Transparent => { 279 | return Err(Error::new_spanned( 280 | name, 281 | "enums cannot be repr(transparent)", 282 | )) 283 | } 284 | Repr::Primitive(i) => i, 285 | Repr::C { .. } => { 286 | return Err(Error::new_spanned( 287 | name, 288 | "repr(C) enums are not currently supported", 289 | )) 290 | } 291 | Repr::Rust { .. } => { 292 | return Err(Error::new_spanned( 293 | name, 294 | "enums implementing CheckBytes must have an explicit \ 295 | repr", 296 | )) 297 | } 298 | }; 299 | 300 | let tag_variant_defs = data.variants.iter().map(|v| { 301 | let variant = &v.ident; 302 | if let Some((_, expr)) = &v.discriminant { 303 | quote! { #variant = #expr } 304 | } else { 305 | quote! { #variant } 306 | } 307 | }); 308 | 309 | let discriminant_const_defs = data.variants.iter().map(|v| { 310 | let variant = &v.ident; 311 | quote! { 312 | #[allow(non_upper_case_globals)] 313 | const #variant: #primitive = Tag::#variant as #primitive; 314 | } 315 | }); 316 | 317 | let tag_variant_values = data.variants.iter().map(|v| { 318 | let name = &v.ident; 319 | quote! { Discriminant::#name } 320 | }); 321 | 322 | let variant_structs = data.variants.iter().map(|v| { 323 | let variant = &v.ident; 324 | let variant_name = Ident::new( 325 | &format!("Variant{}", strip_raw(variant)), 326 | v.span(), 327 | ); 328 | match v.fields { 329 | Fields::Named(ref fields) => { 330 | let fields = fields.named.iter().map(|f| { 331 | let name = &f.ident; 332 | let ty = &f.ty; 333 | quote! { #name: #ty } 334 | }); 335 | quote! { 336 | #[repr(C)] 337 | struct #variant_name #type_impl_generics 338 | #type_where_clause 339 | { 340 | __tag: Tag, 341 | #(#fields,)* 342 | __phantom: ::core::marker::PhantomData< 343 | #name #type_ty_generics 344 | >, 345 | } 346 | } 347 | } 348 | Fields::Unnamed(ref fields) => { 349 | let fields = fields.unnamed.iter().map(|f| { 350 | let ty = &f.ty; 351 | quote! { #ty } 352 | }); 353 | quote! { 354 | #[repr(C)] 355 | struct #variant_name #type_impl_generics ( 356 | Tag, 357 | #(#fields,)* 358 | ::core::marker::PhantomData< 359 | #name #type_ty_generics 360 | > 361 | ) #type_where_clause; 362 | } 363 | } 364 | Fields::Unit => quote! {}, 365 | } 366 | }); 367 | 368 | let check_arms = data.variants.iter().map(|v| { 369 | let variant = &v.ident; 370 | let variant_name = Ident::new( 371 | &format!("Variant{}", strip_raw(variant)), 372 | v.span(), 373 | ); 374 | match v.fields { 375 | Fields::Named(ref fields) => { 376 | let checks = fields.named.iter().map(|f| { 377 | check_arm_named_field(f, &crate_path, name, variant) 378 | }); 379 | quote! { { 380 | let value = 381 | value.cast::<#variant_name #type_ty_generics>(); 382 | #(#checks)* 383 | } } 384 | } 385 | Fields::Unnamed(ref fields) => { 386 | let checks = 387 | fields.unnamed.iter().enumerate().map(|(i, f)| { 388 | check_arm_unnamed_field( 389 | i, 390 | f, 391 | &crate_path, 392 | name, 393 | variant, 394 | ) 395 | }); 396 | quote! { { 397 | let value = 398 | value.cast::<#variant_name #type_ty_generics>(); 399 | #(#checks)* 400 | } } 401 | } 402 | Fields::Unit => quote! { (), }, 403 | } 404 | }); 405 | 406 | let no_matching_tag_arm = quote! { 407 | return ::core::result::Result::Err( 408 | < 409 | < 410 | __C as #crate_path::rancor::Fallible 411 | >::Error as #crate_path::rancor::Source 412 | >::new( 413 | #crate_path::InvalidEnumDiscriminantError { 414 | enum_name: ::core::stringify!(#name), 415 | invalid_discriminant: tag, 416 | } 417 | ) 418 | ) 419 | }; 420 | 421 | quote! { 422 | const _: () = { 423 | #[repr(#primitive)] 424 | enum Tag { 425 | #(#tag_variant_defs,)* 426 | } 427 | 428 | struct Discriminant; 429 | 430 | #[automatically_derived] 431 | impl Discriminant { 432 | #(#discriminant_const_defs)* 433 | } 434 | 435 | #(#variant_structs)* 436 | 437 | #[automatically_derived] 438 | // SAFETY: `check_bytes` only returns `Ok` if: 439 | // - The discriminant is valid for some variant of the enum, 440 | // and 441 | // - Each field of the variant struct is valid. 442 | // If the discriminant is valid and the fields of the 443 | // indicated variant struct are valid, then the overall enum 444 | // is valid. 445 | unsafe impl #trait_impl_generics 446 | #crate_path::CheckBytes<__C> for #name #type_ty_generics 447 | #check_where 448 | { 449 | unsafe fn check_bytes( 450 | value: *const Self, 451 | context: &mut __C, 452 | ) -> ::core::result::Result< 453 | (), 454 | <__C as #crate_path::rancor::Fallible>::Error, 455 | > { 456 | let tag = *value.cast::<#primitive>(); 457 | match tag { 458 | #(#tag_variant_values => #check_arms)* 459 | _ => #no_matching_tag_arm, 460 | } 461 | #verify 462 | ::core::result::Result::Ok(()) 463 | } 464 | } 465 | }; 466 | } 467 | } 468 | Data::Union(_) => { 469 | return Err(Error::new( 470 | input.span(), 471 | "CheckBytes cannot be derived for unions", 472 | )); 473 | } 474 | }; 475 | 476 | Ok(check_bytes_impl) 477 | } 478 | 479 | fn check_arm_named_field( 480 | f: &Field, 481 | crate_path: &Path, 482 | name: &Ident, 483 | variant: &Ident, 484 | ) -> TokenStream { 485 | let field_name = &f.ident; 486 | let ty = &f.ty; 487 | quote! { 488 | <#ty as #crate_path::CheckBytes<__C>>::check_bytes( 489 | ::core::ptr::addr_of!((*value).#field_name), 490 | context 491 | ).map_err(|e| { 492 | < 493 | < 494 | __C as #crate_path::rancor::Fallible 495 | >::Error as #crate_path::rancor::Trace 496 | >::trace( 497 | e, 498 | #crate_path::NamedEnumVariantCheckContext { 499 | enum_name: ::core::stringify!(#name), 500 | variant_name: ::core::stringify!(#variant), 501 | field_name: ::core::stringify!(#field_name), 502 | }, 503 | ) 504 | })?; 505 | } 506 | } 507 | 508 | fn check_arm_unnamed_field( 509 | i: usize, 510 | f: &Field, 511 | crate_path: &Path, 512 | name: &Ident, 513 | variant: &Ident, 514 | ) -> TokenStream { 515 | let ty = &f.ty; 516 | let index = Index::from(i + 1); 517 | quote! { 518 | <#ty as #crate_path::CheckBytes<__C>>::check_bytes( 519 | ::core::ptr::addr_of!((*value).#index), 520 | context 521 | ).map_err(|e| { 522 | < 523 | < 524 | __C as #crate_path::rancor::Fallible 525 | >::Error as #crate_path::rancor::Trace 526 | >::trace( 527 | e, 528 | #crate_path::UnnamedEnumVariantCheckContext { 529 | enum_name: ::core::stringify!(#name), 530 | variant_name: ::core::stringify!(#variant), 531 | field_index: #index, 532 | }, 533 | ) 534 | })?; 535 | } 536 | } 537 | -------------------------------------------------------------------------------- /bytecheck_derive/src/repr.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::{Span, TokenStream}; 2 | use quote::{quote, ToTokens}; 3 | use syn::{parenthesized, token, Attribute, Error, Ident, LitInt}; 4 | 5 | #[derive(Clone, Copy)] 6 | pub enum Primitive { 7 | I8, 8 | I16, 9 | I32, 10 | I64, 11 | Isize, 12 | U8, 13 | U16, 14 | U32, 15 | U64, 16 | Usize, 17 | } 18 | 19 | impl Primitive { 20 | const ALL: [Self; 10] = [ 21 | Self::I8, 22 | Self::I16, 23 | Self::I32, 24 | Self::I64, 25 | Self::Isize, 26 | Self::U8, 27 | Self::U16, 28 | Self::U32, 29 | Self::U64, 30 | Self::Usize, 31 | ]; 32 | 33 | pub const fn as_str(&self) -> &'static str { 34 | match self { 35 | Self::I8 => "i8", 36 | Self::I16 => "i16", 37 | Self::I32 => "i32", 38 | Self::I64 => "i64", 39 | Self::Isize => "isize", 40 | Self::U8 => "u8", 41 | Self::U16 => "u16", 42 | Self::U32 => "u32", 43 | Self::U64 => "u64", 44 | Self::Usize => "usize", 45 | } 46 | } 47 | } 48 | 49 | impl ToTokens for Primitive { 50 | fn to_tokens(&self, tokens: &mut TokenStream) { 51 | let ident = Ident::new(self.as_str(), Span::call_site()); 52 | tokens.extend(quote! { #ident }); 53 | } 54 | } 55 | 56 | pub enum Modifier { 57 | Packed(#[allow(dead_code)] usize), 58 | Align(#[allow(dead_code)] usize), 59 | } 60 | 61 | pub enum Repr { 62 | Transparent, 63 | Primitive(Primitive), 64 | C { 65 | #[allow(dead_code)] 66 | primitive: Option, 67 | #[allow(dead_code)] 68 | modifier: Option, 69 | }, 70 | Rust { 71 | #[allow(dead_code)] 72 | modifier: Option, 73 | }, 74 | } 75 | 76 | impl Repr { 77 | pub fn from_attrs(attrs: &[Attribute]) -> Result { 78 | let mut c = false; 79 | let mut transparent = false; 80 | let mut primitive = None; 81 | let mut modifier = None; 82 | 83 | for attr in attrs.iter().filter(|a| a.meta.path().is_ident("repr")) { 84 | attr.parse_nested_meta(|meta| { 85 | if meta.path.is_ident("C") { 86 | c = true; 87 | Ok(()) 88 | } else if meta.path.is_ident("transparent") { 89 | transparent = true; 90 | Ok(()) 91 | } else if let Some(&p) = Primitive::ALL 92 | .iter() 93 | .find(|p| meta.path.is_ident(p.as_str())) 94 | { 95 | primitive = Some(p); 96 | Ok(()) 97 | } else if meta.path.is_ident("align") { 98 | let content; 99 | parenthesized!(content in meta.input); 100 | let lit = content.parse::()?; 101 | let n = lit.base10_parse()?; 102 | modifier = Some(Modifier::Align(n)); 103 | Ok(()) 104 | } else if meta.path.is_ident("packed") { 105 | if meta.input.peek(token::Paren) { 106 | let content; 107 | parenthesized!(content in meta.input); 108 | let lit = content.parse::()?; 109 | let n = lit.base10_parse()?; 110 | modifier = Some(Modifier::Packed(n)); 111 | } else { 112 | modifier = Some(Modifier::Packed(1)); 113 | } 114 | Ok(()) 115 | } else { 116 | Err(Error::new_spanned( 117 | meta.path, 118 | "unrecognized repr argument", 119 | )) 120 | } 121 | })?; 122 | } 123 | 124 | if c { 125 | Ok(Repr::C { 126 | primitive, 127 | modifier, 128 | }) 129 | } else if transparent { 130 | Ok(Repr::Transparent) 131 | } else if let Some(primitive) = primitive { 132 | Ok(Repr::Primitive(primitive)) 133 | } else { 134 | Ok(Repr::Rust { modifier }) 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /bytecheck_derive/src/util.rs: -------------------------------------------------------------------------------- 1 | use core::iter::FlatMap; 2 | 3 | use syn::{ 4 | punctuated::Iter, Data, DataEnum, DataStruct, DataUnion, Field, Ident, 5 | Variant, 6 | }; 7 | 8 | type VariantFieldsFn = fn(&Variant) -> Iter<'_, Field>; 9 | 10 | fn variant_fields(variant: &Variant) -> Iter<'_, Field> { 11 | variant.fields.iter() 12 | } 13 | 14 | pub enum FieldsIter<'a> { 15 | Struct(Iter<'a, Field>), 16 | Enum(FlatMap, Iter<'a, Field>, VariantFieldsFn>), 17 | } 18 | 19 | impl<'a> Iterator for FieldsIter<'a> { 20 | type Item = &'a Field; 21 | 22 | fn next(&mut self) -> Option { 23 | match self { 24 | Self::Struct(iter) => iter.next(), 25 | Self::Enum(iter) => iter.next(), 26 | } 27 | } 28 | } 29 | 30 | pub fn iter_fields(data: &Data) -> FieldsIter<'_> { 31 | match data { 32 | Data::Struct(DataStruct { fields, .. }) => { 33 | FieldsIter::Struct(fields.iter()) 34 | } 35 | Data::Enum(DataEnum { variants, .. }) => { 36 | FieldsIter::Enum(variants.iter().flat_map(variant_fields)) 37 | } 38 | Data::Union(DataUnion { fields, .. }) => { 39 | FieldsIter::Struct(fields.named.iter()) 40 | } 41 | } 42 | } 43 | 44 | pub fn strip_raw(ident: &Ident) -> String { 45 | let as_string = ident.to_string(); 46 | as_string 47 | .strip_prefix("r#") 48 | .map(ToString::to_string) 49 | .unwrap_or(as_string) 50 | } 51 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 80 2 | comment_width = 80 3 | wrap_comments = true 4 | group_imports = "StdExternalCrate" 5 | imports_granularity = "Crate" 6 | condense_wildcard_suffixes = true 7 | error_on_line_overflow = true 8 | error_on_unformatted = true 9 | format_code_in_doc_comments = true 10 | format_macro_matchers = true 11 | format_macro_bodies = true 12 | format_strings = true 13 | hex_literal_case = "Lower" 14 | normalize_comments = true 15 | use_field_init_shorthand = true 16 | --------------------------------------------------------------------------------