diff --git a/bitcoin/src/blockdata/script/push_bytes.rs b/bitcoin/src/blockdata/script/push_bytes.rs index 039b2214d1..89cbdc2682 100644 --- a/bitcoin/src/blockdata/script/push_bytes.rs +++ b/bitcoin/src/blockdata/script/push_bytes.rs @@ -15,7 +15,8 @@ pub use self::primitive::*; /// break invariants. Therefore auditing this module should be sufficient. mod primitive { use core::ops::{ - Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, + Bound, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, + RangeToInclusive, }; use super::PushBytesError; @@ -43,53 +44,69 @@ mod primitive { pub struct PushBytes([u8]); impl PushBytes { - /// Creates `&Self` without checking the length. + /// Creates `&PushBytes` without checking the length. /// /// # Safety /// - /// The caller is responsible for checking that the length is less than the [`LIMIT`]. + /// The caller is responsible for checking that the length is less than the 2^32. unsafe fn from_slice_unchecked(bytes: &[u8]) -> &Self { + // SAFETY: The caller must guarantee that bytes.len() < 2^32. + // If that is the case the conversion is sound because &[u8] and &PushBytes + // have the same layout (because of #[repr(transparent)] on PushBytes). &*(bytes as *const [u8] as *const PushBytes) } - /// Creates `&mut Self` without checking the length. + /// Creates `&mut PushBytes` without checking the length. /// /// # Safety /// - /// The caller is responsible for checking that the length is less than the [`LIMIT`]. + /// The caller is responsible for checking that the length is less than the 2^32. unsafe fn from_mut_slice_unchecked(bytes: &mut [u8]) -> &mut Self { + // SAFETY: The caller must guarantee that bytes.len() < 2^32. + // If that is the case the conversion is sound because &mut [u8] and &mut PushBytes + // have the same layout (because of #[repr(transparent)] on PushBytes). &mut *(bytes as *mut [u8] as *mut PushBytes) } - /// Creates an empty `PushBytes`. + /// Creates an empty `&PushBytes`. pub fn empty() -> &'static Self { - // 0 < LIMIT + // SAFETY: 0 < 2^32. unsafe { Self::from_slice_unchecked(&[]) } } /// Returns the underlying bytes. pub fn as_bytes(&self) -> &[u8] { &self.0 } - /// Returns the underlying mutbale bytes. + /// Returns the underlying mutable bytes. pub fn as_mut_bytes(&mut self) -> &mut [u8] { &mut self.0 } } macro_rules! delegate_index { ($($type:ty),* $(,)?) => { $( - /// Script subslicing operation - read [slicing safety](#slicing-safety)! impl Index<$type> for PushBytes { type Output = Self; #[inline] #[track_caller] fn index(&self, index: $type) -> &Self::Output { - // Slicing can not make slices longer + // SAFETY: Slicing can not make slices longer. unsafe { Self::from_slice_unchecked(&self.0[index]) } } } + + impl IndexMut<$type> for PushBytes { + #[inline] + #[track_caller] + fn index_mut(&mut self, index: $type) -> &mut Self::Output { + // SAFETY: Slicing can not make slices longer. + unsafe { + Self::from_mut_slice_unchecked(&mut self.0[index]) + } + } + } )* } } @@ -112,12 +129,18 @@ mod primitive { fn index(&self, index: usize) -> &Self::Output { &self.0[index] } } + impl IndexMut for PushBytes { + #[inline] + #[track_caller] + fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.0[index] } + } + impl<'a> TryFrom<&'a [u8]> for &'a PushBytes { type Error = PushBytesError; fn try_from(bytes: &'a [u8]) -> Result { check_limit(bytes.len())?; - // We've just checked the length + // SAFETY: We've just checked the length. Ok(unsafe { PushBytes::from_slice_unchecked(bytes) }) } } @@ -127,7 +150,7 @@ mod primitive { fn try_from(bytes: &'a mut [u8]) -> Result { check_limit(bytes.len())?; - // We've just checked the length + // SAFETY: We've just checked the length. Ok(unsafe { PushBytes::from_mut_slice_unchecked(bytes) }) } } @@ -139,7 +162,7 @@ mod primitive { fn from(bytes: &'a [u8; $len]) -> Self { // Check that the macro wasn't called with a wrong number. const _: () = [(); 1][($len >= 0x100000000u64) as usize]; - // We know the size of array statically and we checked macro input. + // SAFETY: We know the size of array statically and we checked macro input. unsafe { PushBytes::from_slice_unchecked(bytes) } } } @@ -147,7 +170,7 @@ mod primitive { impl<'a> From<&'a mut [u8; $len]> for &'a mut PushBytes { fn from(bytes: &'a mut [u8; $len]) -> Self { // Macro check already above, no need to duplicate. - // We know the size of array statically and we checked macro input. + // SAFETY: We know the size of array statically and we checked macro input. unsafe { PushBytes::from_mut_slice_unchecked(bytes) } } }