From 79bcbe2312bed599862267f9f2868631c832e192 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Wed, 13 Dec 2017 00:30:39 +0000 Subject: [PATCH 1/2] Add safe boolean transmutation module - add error reason for invalid values (should be refactored in next major release) - add guarded_transmute_bool_* variants which check for valid boolean values - add respective tests --- src/bool.rs | 132 ++++++++++++++++++ src/error.rs | 3 + src/lib.rs | 14 +- tests/guarded_transmute_bool_pedantic/mod.rs | 41 ++++++ .../guarded_transmute_bool_permissive/mod.rs | 35 +++++ .../mod.rs | 42 ++++++ .../mod.rs | 36 +++++ tests/lib.rs | 5 +- 8 files changed, 302 insertions(+), 6 deletions(-) create mode 100644 src/bool.rs create mode 100644 tests/guarded_transmute_bool_pedantic/mod.rs create mode 100644 tests/guarded_transmute_bool_permissive/mod.rs create mode 100644 tests/guarded_transmute_bool_vec_pedantic/mod.rs create mode 100644 tests/guarded_transmute_bool_vec_permissive/mod.rs diff --git a/src/bool.rs b/src/bool.rs new file mode 100644 index 0000000..b0295ff --- /dev/null +++ b/src/bool.rs @@ -0,0 +1,132 @@ +//! Functions for safe transmutation to `bool`. +//! Transmuting to `bool' is not undefined behavior if the transmuted value is either 0 or 1. +//! These functions will return an error if the integer value behind the `bool` value is neither one. + +use self::super::{Error, ErrorReason, guarded_transmute_vec_permissive, guarded_transmute_many_permissive, guarded_transmute_many_pedantic, + guarded_transmute_vec_pedantic}; + +use std::mem::align_of; + +/// Makes sure that the bytes represent a sequence of valid boolean values. It is done +/// this way because the language does not guarantee that `bool` is 1 byte sized. +#[cfg(target_endian = "little")] +#[inline] +fn bytes_are_bool(v: &[u8]) -> bool { + let sizeof_bool: usize = align_of::(); + for c in v.chunks(sizeof_bool).filter(|c| c.len() == sizeof_bool) { + if c[0] > 1 || c[1..].iter().any(|x| *x != 0) { + return false; + } + } + true +} + +/// Makes sure that the bytes represent a sequence of valid boolean values. It is done +/// this way because the language does not guarantee that `bool` is 1 byte sized. +#[cfg(target_endian = "big")] +#[inline] +fn bytes_are_bool(v: &[u8]) -> bool { + let sizeof_bool: usize = align_of::(); + for c in v.chunks(sizeof_bool).filter(|c| c.len() == sizeof_bool) { + let (l, r) = c.split_at(sizeof_bool - 1); + if r[0] > 1 || l.iter().any(|x| *x != 0) { + return false; + } + } + true +} + +fn check_bool(bytes: &[u8]) -> Result<(), Error> { + if bytes_are_bool(bytes) { + Ok(()) + } else { + Err(Error { + required: align_of::(), + actual: bytes.len(), + reason: ErrorReason::InvalidValue, + }) + } +} + +/// View a byte slice as a slice of boolean values. +/// +/// The resulting slice will have as many instances of `bool` as will fit, can be empty. +/// +/// # Examples +/// +/// ``` +/// # use safe_transmute::guarded_transmute_bool_permissive; +/// # fn main() { +/// assert_eq!(guarded_transmute_bool_permissive(&[0x00, 0x01, 0x00, 0x01]).unwrap(), +/// &[false, true, false, true]); +/// assert_eq!(guarded_transmute_bool_permissive(&[]).unwrap(), &[]); +/// # } + +/// ``` +pub fn guarded_transmute_bool_permissive(bytes: &[u8]) -> Result<&[bool], Error> { + check_bool(bytes)?; + unsafe { Ok(guarded_transmute_many_permissive(bytes)) } +} + +/// View a byte slice as a slice of boolean values. +/// +/// The byte slice must have at least enough bytes to fill a single `bool`. +/// +/// # Examples +/// +/// ``` +/// # use safe_transmute::guarded_transmute_bool_pedantic; +/// # fn main() { +/// assert_eq!(guarded_transmute_bool_pedantic(&[0x01, 0x01, 0x01, 0x01]).unwrap(), +/// &[true, true, true, true]); +/// assert!(guarded_transmute_bool_pedantic(&[]).is_err()); +/// # } +/// ``` +pub fn guarded_transmute_bool_pedantic(bytes: &[u8]) -> Result<&[bool], Error> { + check_bool(bytes)?; + unsafe { guarded_transmute_many_pedantic(bytes) } +} + +/// Trasform a byte vector into a vector of bool. +/// +/// The vector's allocated byte buffer will be reused when possible, and +/// have as many instances of a type as will fit, rounded down. +/// +/// # Examples +/// +/// ``` +/// # use safe_transmute::guarded_transmute_bool_vec_permissive; +/// # fn main() { +/// assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x00, 0x01, 0x00, 0x01]).unwrap(), +/// vec![false, true, false, true]); +/// assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x01, 0x00, 0x00, 0x00, 0x01]).unwrap(), +/// vec![true, false, false, false, true]); +/// assert_eq!(guarded_transmute_bool_vec_permissive(vec![]), Ok(vec![])); +/// # } +/// ``` +pub fn guarded_transmute_bool_vec_permissive(bytes: Vec) -> Result, Error> { + check_bool(&bytes)?; + unsafe { Ok(guarded_transmute_vec_permissive(bytes)) } +} + + +/// Trasform a byte vector into a vector of bool. +/// +/// The vector's allocated byte buffer will be reused when possible, and +/// should not have extraneous data. +/// +/// # Examples +/// +/// ``` +/// # use safe_transmute::guarded_transmute_bool_vec_pedantic; +/// assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x00, 0x01, 0x00, 0x01]).unwrap(), +/// vec![false, true, false, true]); +/// +/// assert!(guarded_transmute_bool_vec_pedantic(vec![]).is_err()); +/// +/// assert!(guarded_transmute_bool_vec_pedantic(vec![0x04, 0x00, 0xED]).is_err()); +/// ``` +pub fn guarded_transmute_bool_vec_pedantic(bytes: Vec) -> Result, Error> { + check_bool(&bytes)?; + unsafe { guarded_transmute_vec_pedantic(bytes) } +} diff --git a/src/error.rs b/src/error.rs index c1fda3e..fe91eaf 100644 --- a/src/error.rs +++ b/src/error.rs @@ -38,6 +38,8 @@ pub enum ErrorReason { TooManyBytes, /// The byte amount received is not the same as the type's size. InexactByteCount, + /// The byte count is fine, but the data contains an invalid value for the target type. + InvalidValue, } @@ -47,6 +49,7 @@ impl StdError for Error { ErrorReason::NotEnoughBytes => "Not enough bytes to fill type", ErrorReason::TooManyBytes => "Too many bytes for type", ErrorReason::InexactByteCount => "Not exactly the amount of bytes for type", + ErrorReason::InvalidValue => "Invalid target value detected", } } } diff --git a/src/lib.rs b/src/lib.rs index 72ccd29..a24619b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,15 @@ //! This crate contains checked implementations of `transmute()`. //! -//! The functions in this crate are not inherently safe, but just guarded against common simple mistakes -//! (like trying to create an 8-byte type from 7 bytes). -//! -//! Those functions are exactly as safe as the data passed to them - creating a null pointer, +//! The base functions in this crate are not inherently safe, but just guarded against common simple mistakes +//! (like trying to create an 8-byte type from 7 bytes. +//! These functions are exactly as safe as the data passed to them - creating a null pointer, //! for example, is not unsafe in and of itself, but dereferencing it certainly *is*, //! but they don't do that (see [here](https://github.com/nabijaczleweli/safe-transmute-rs/issues/1) //! for extended discussion). //! +//! Other functions in this crate, on the other hand, provide enough safety measures to ensure safety on +//! all circumstances. This is the case for those found in the `pod` and `bool` modules. +//! //! # Examples //! //! View bytes as a series of `u16`s: @@ -64,6 +66,7 @@ //! ``` +mod bool; mod pod; mod error; @@ -76,7 +79,8 @@ pub use self::error::{ErrorReason, Error}; pub use self::pod::{PodTransmutable, guarded_transmute_pod_many_permissive, guarded_transmute_pod_vec_permissive, guarded_transmute_pod_many_pedantic, guarded_transmute_pod_pedantic, guarded_transmute_pod_vec_pedantic, guarded_transmute_pod_many, guarded_transmute_pod, guarded_transmute_pod_vec}; - +pub use self::bool::{guarded_transmute_bool_permissive, guarded_transmute_bool_pedantic, + guarded_transmute_bool_vec_permissive, guarded_transmute_bool_vec_pedantic}; /// Transmute a byte slice into a single instance of a `Copy`able type. /// diff --git a/tests/guarded_transmute_bool_pedantic/mod.rs b/tests/guarded_transmute_bool_pedantic/mod.rs new file mode 100644 index 0000000..ab09452 --- /dev/null +++ b/tests/guarded_transmute_bool_pedantic/mod.rs @@ -0,0 +1,41 @@ +use safe_transmute::{ErrorReason, Error, guarded_transmute_bool_pedantic}; + +#[test] +fn too_short() { + assert_eq!(guarded_transmute_bool_pedantic([].as_ref()), + Err(Error { + required: 1, + actual: 0, + reason: ErrorReason::NotEnoughBytes, + })); +} + +#[test] +fn just_enough() { + assert_eq!(guarded_transmute_bool_pedantic([0x00, 0x01].as_ref()), + Ok([false, true].into_iter().as_slice())); + assert_eq!(guarded_transmute_bool_pedantic([0x01, 0x01, 0x00, 0x01].as_ref()), + Ok([true, true, false, true].into_iter().as_slice())); +} + +#[test] +fn invalid_bytes() { + assert_eq!(guarded_transmute_bool_pedantic([0x00, 0x01, 0x02].as_ref()), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_pedantic([0x05, 0x01, 0x00].as_ref()), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_pedantic([0xFF].as_ref()), + Err(Error { + required: 1, + actual: 1, + reason: ErrorReason::InvalidValue, + })); +} diff --git a/tests/guarded_transmute_bool_permissive/mod.rs b/tests/guarded_transmute_bool_permissive/mod.rs new file mode 100644 index 0000000..ea4030e --- /dev/null +++ b/tests/guarded_transmute_bool_permissive/mod.rs @@ -0,0 +1,35 @@ +use safe_transmute::{ErrorReason, Error, guarded_transmute_bool_permissive}; + +#[test] +fn too_short() { + assert_eq!(guarded_transmute_bool_permissive([].as_ref()), Ok([].as_ref())); +} + +#[test] +fn just_enough() { + assert_eq!(guarded_transmute_bool_permissive([0x00, 0x01].as_ref()), Ok([false, true].as_ref())); + assert_eq!(guarded_transmute_bool_permissive([0x00, 0x01, 0x00, 0x01].as_ref()), + Ok([false, true, false, true].as_ref())); +} + +#[test] +fn invalid_bytes() { + assert_eq!(guarded_transmute_bool_permissive([0x00, 0x01, 0x02].as_ref()), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_permissive([0x05, 0x01, 0x00].as_ref()), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_permissive([0xFF].as_ref()), + Err(Error { + required: 1, + actual: 1, + reason: ErrorReason::InvalidValue, + })); +} diff --git a/tests/guarded_transmute_bool_vec_pedantic/mod.rs b/tests/guarded_transmute_bool_vec_pedantic/mod.rs new file mode 100644 index 0000000..b0c38b5 --- /dev/null +++ b/tests/guarded_transmute_bool_vec_pedantic/mod.rs @@ -0,0 +1,42 @@ +use safe_transmute::{ErrorReason, Error, guarded_transmute_bool_vec_pedantic}; + + +#[test] +fn too_short() { + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![]), + Err(Error { + required: 1, + actual: 0, + reason: ErrorReason::NotEnoughBytes, + })); +} + +#[test] +fn just_enough() { + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x00, 0x01]), + Ok(vec![false, true])); + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x00, 0x01, 0x00, 0x01]), + Ok(vec![false, true, false, true])); +} + +#[test] +fn invalid_bytes() { + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x00, 0x01, 0x02]), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0x05, 0x01, 0x00]), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_vec_pedantic(vec![0xFF]), + Err(Error { + required: 1, + actual: 1, + reason: ErrorReason::InvalidValue, + })); +} diff --git a/tests/guarded_transmute_bool_vec_permissive/mod.rs b/tests/guarded_transmute_bool_vec_permissive/mod.rs new file mode 100644 index 0000000..3c76251 --- /dev/null +++ b/tests/guarded_transmute_bool_vec_permissive/mod.rs @@ -0,0 +1,36 @@ +use safe_transmute::{ErrorReason, Error, guarded_transmute_bool_vec_permissive}; + +#[test] +fn too_short() { + assert_eq!(guarded_transmute_bool_vec_permissive(vec![]), Ok(vec![])); +} + +#[test] +fn just_enough() { + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x00, 0x01]), + Ok(vec![false, true])); + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x00, 0x01, 0x00, 0x01]), + Ok(vec![false, true, false, true])); +} + +#[test] +fn invalid_bytes() { + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x00, 0x01, 0x02]), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0x05, 0x01, 0x00]), + Err(Error { + required: 1, + actual: 3, + reason: ErrorReason::InvalidValue, + })); + assert_eq!(guarded_transmute_bool_vec_permissive(vec![0xFF]), + Err(Error { + required: 1, + actual: 1, + reason: ErrorReason::InvalidValue, + })); +} diff --git a/tests/lib.rs b/tests/lib.rs index 18f9c19..4cda620 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -18,6 +18,9 @@ mod guarded_transmute_pod_many_permissive; mod guarded_transmute_pod_vec; mod guarded_transmute_pod_vec_pedantic; mod guarded_transmute_pod_vec_permissive; - +mod guarded_transmute_bool_pedantic; +mod guarded_transmute_bool_permissive; +mod guarded_transmute_bool_vec_pedantic; +mod guarded_transmute_bool_vec_permissive; include!("test_util/le_to_native.rs"); From 39f68d92e8a24303bfc96fcb6a4f130ec50a1075 Mon Sep 17 00:00:00 2001 From: Eduardo Pinho Date: Thu, 14 Dec 2017 23:42:33 +0000 Subject: [PATCH 2/2] Tweaks - refactor bytes_are_bool into a single fn - rustfmt - fix docs - rearrange re-exports --- src/bool.rs | 48 ++++++++++++++++++------------------------------ src/lib.rs | 6 +++--- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/src/bool.rs b/src/bool.rs index b0295ff..c0f88e0 100644 --- a/src/bool.rs +++ b/src/bool.rs @@ -1,39 +1,28 @@ //! Functions for safe transmutation to `bool`. -//! Transmuting to `bool' is not undefined behavior if the transmuted value is either 0 or 1. -//! These functions will return an error if the integer value behind the `bool` value is neither one. +//! Transmuting to `bool' is not undefined behavior if the transmuted value is +//! either 0 or 1. These functions will return an error if the integer value +//! behind the `bool` value is neither one. -use self::super::{Error, ErrorReason, guarded_transmute_vec_permissive, guarded_transmute_many_permissive, guarded_transmute_many_pedantic, - guarded_transmute_vec_pedantic}; +use self::super::{guarded_transmute_many_pedantic, guarded_transmute_many_permissive, guarded_transmute_vec_pedantic, guarded_transmute_vec_permissive, Error, + ErrorReason}; use std::mem::align_of; /// Makes sure that the bytes represent a sequence of valid boolean values. It is done /// this way because the language does not guarantee that `bool` is 1 byte sized. -#[cfg(target_endian = "little")] #[inline] -fn bytes_are_bool(v: &[u8]) -> bool { - let sizeof_bool: usize = align_of::(); - for c in v.chunks(sizeof_bool).filter(|c| c.len() == sizeof_bool) { - if c[0] > 1 || c[1..].iter().any(|x| *x != 0) { - return false; - } - } - true -} - -/// Makes sure that the bytes represent a sequence of valid boolean values. It is done -/// this way because the language does not guarantee that `bool` is 1 byte sized. -#[cfg(target_endian = "big")] -#[inline] -fn bytes_are_bool(v: &[u8]) -> bool { - let sizeof_bool: usize = align_of::(); - for c in v.chunks(sizeof_bool).filter(|c| c.len() == sizeof_bool) { - let (l, r) = c.split_at(sizeof_bool - 1); - if r[0] > 1 || l.iter().any(|x| *x != 0) { - return false; - } - } - true +pub fn bytes_are_bool(v: &[u8]) -> bool { + let sizeof_bool: usize = ::std::mem::align_of::(); + v.chunks(sizeof_bool) + .filter(|c| c.len() == sizeof_bool) + .all(|c| { + let (rest, lsb) = if cfg!(target_endian = "little") { + (&c[1..], c) + } else { + c.split_at(sizeof_bool - 1) + }; + lsb[0] <= 1 && rest.iter().all(|&x| x == 0) + }) } fn check_bool(bytes: &[u8]) -> Result<(), Error> { @@ -109,7 +98,6 @@ pub fn guarded_transmute_bool_vec_permissive(bytes: Vec) -> Result unsafe { Ok(guarded_transmute_vec_permissive(bytes)) } } - /// Trasform a byte vector into a vector of bool. /// /// The vector's allocated byte buffer will be reused when possible, and @@ -123,7 +111,7 @@ pub fn guarded_transmute_bool_vec_permissive(bytes: Vec) -> Result /// vec![false, true, false, true]); /// /// assert!(guarded_transmute_bool_vec_pedantic(vec![]).is_err()); -/// +/// /// assert!(guarded_transmute_bool_vec_pedantic(vec![0x04, 0x00, 0xED]).is_err()); /// ``` pub fn guarded_transmute_bool_vec_pedantic(bytes: Vec) -> Result, Error> { diff --git a/src/lib.rs b/src/lib.rs index a24619b..7f77e26 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! This crate contains checked implementations of `transmute()`. //! //! The base functions in this crate are not inherently safe, but just guarded against common simple mistakes -//! (like trying to create an 8-byte type from 7 bytes. +//! (like trying to create an 8-byte type from 7 bytes). //! These functions are exactly as safe as the data passed to them - creating a null pointer, //! for example, is not unsafe in and of itself, but dereferencing it certainly *is*, //! but they don't do that (see [here](https://github.com/nabijaczleweli/safe-transmute-rs/issues/1) @@ -79,8 +79,8 @@ pub use self::error::{ErrorReason, Error}; pub use self::pod::{PodTransmutable, guarded_transmute_pod_many_permissive, guarded_transmute_pod_vec_permissive, guarded_transmute_pod_many_pedantic, guarded_transmute_pod_pedantic, guarded_transmute_pod_vec_pedantic, guarded_transmute_pod_many, guarded_transmute_pod, guarded_transmute_pod_vec}; -pub use self::bool::{guarded_transmute_bool_permissive, guarded_transmute_bool_pedantic, - guarded_transmute_bool_vec_permissive, guarded_transmute_bool_vec_pedantic}; +pub use self::bool::{guarded_transmute_bool_pedantic, guarded_transmute_bool_permissive, guarded_transmute_bool_vec_pedantic, + guarded_transmute_bool_vec_permissive}; /// Transmute a byte slice into a single instance of a `Copy`able type. ///