uint icon indicating copy to clipboard operation
uint copied to clipboard

`support/borsh`: Replace the unsafety with `generic_const_exprs` when

Open github-actions[bot] opened this issue 1 year ago • 3 comments

On 2024-12-30 @prestwich wrote in 6a513a0 “Merge pull request #424 from Evalir/evalir/copy-to-buf”:

Replace the unsafety with generic_const_exprs when available

    #[inline]
    fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
        #[cfg(target_endian = "little")]
        return writer.write_all(self.as_le_slice());

        // TODO: Replace the unsafety with `generic_const_exprs` when
        // available
        #[cfg(target_endian = "big")]
        {
            let mut limbs = [0u64; LIMBS];
            // SAFETY: `limbs` is known to have identical memory layout and
            // alignment to `[u8; LIMBS * 8]`, which is guaranteed to safely

From src/support/borsh.rs:47

github-actions[bot] avatar Dec 30 '24 16:12 github-actions[bot]

This unsafety can be fixed with bytemuck, no?

kpp avatar Feb 28 '25 15:02 kpp

that's pushing safety management into a dep, which is not exactly the same thing as fixing it

prestwich avatar Feb 28 '25 16:02 prestwich

Yes that's true. Same discussion happened here https://github.com/rust-random/rand/issues/1574 with the zerocopy crate in rand. Unfortunately, this unsafety can be also managed with the https://docs.rs/zerocopy/latest/zerocopy/macro.transmute_mut.html macro for the same generic_const_exprs reason.

It can be achieved like this though:

diff --git a/src/support/borsh.rs b/src/support/borsh.rs
index 00dec73..b52bf2b 100644
--- a/src/support/borsh.rs
+++ b/src/support/borsh.rs
@@ -5,26 +5,17 @@
 
 use crate::{Bits, Uint};
 use borsh::{io, BorshDeserialize, BorshSerialize};
+use zerocopy::IntoBytes;
 
 impl<const BITS: usize, const LIMBS: usize> BorshDeserialize for Uint<BITS, LIMBS> {
     #[inline]
     fn deserialize_reader<R: io::Read>(reader: &mut R) -> io::Result<Self> {
-        // This is a bit of an end-run around missing `generic_const_exprs`
-        // We cannot declare a `[u8; Self::BYTES]` or `[u8; LIMBS * 8]`,
-        // so we declare a `[u8; LIMBS]` and use unsafe to write to it.
+        assert!(LIMBS * 8 >= Self::BYTES);
 
-        // TODO: Replace the unsafety with `generic_const_exprs` when
-        // available
         let mut limbs = [0u64; LIMBS];
+        let bytes = limbs.as_mut_bytes();
+        let (target, _rest) = bytes.split_at_mut(Self::BYTES);
 
-        // SAFETY: `limbs` is known to have identical memory layout and
-        // alignment to `[u8; LIMBS * 8]`, which is guaranteed to safely
-        // contain  [u8; Self::BYTES]`, as `LIMBS * 8 >= Self::BYTES`.
-        // Reference:
-        // https://doc.rust-lang.org/reference/type-layout.html#array-layout
-        let target = unsafe {
-            core::slice::from_raw_parts_mut(limbs.as_mut_ptr().cast::<u8>(), Self::BYTES)
-        };
         reader.read_exact(target)?;
 
         // Using `Self::from_limbs(limbs)` would be incorrect here, as the
@@ -44,21 +35,16 @@ impl<const BITS: usize, const LIMBS: usize> BorshSerialize for Uint<BITS, LIMBS>
         #[cfg(target_endian = "little")]
         return writer.write_all(self.as_le_slice());
 
-        // TODO: Replace the unsafety with `generic_const_exprs` when
-        // available
         #[cfg(target_endian = "big")]
         {
+            assert!(LIMBS * 8 >= Self::BYTES);
+
             let mut limbs = [0u64; LIMBS];
-            // SAFETY: `limbs` is known to have identical memory layout and
-            // alignment to `[u8; LIMBS * 8]`, which is guaranteed to safely
-            // contain  [u8; Self::BYTES]`, as `LIMBS * 8 >= Self::BYTES`.
-            // Reference:
-            // https://doc.rust-lang.org/reference/type-layout.html#array-layout
-            let mut buf = unsafe {
-                core::slice::from_raw_parts_mut(limbs.as_mut_ptr().cast::<u8>(), Self::BYTES)
-            };
-            self.copy_le_bytes_to(&mut buf);
-            writer.write_all(&buf)
+            let bytes = limbs.as_mut_bytes();
+            let (buf, _rest) = bytes.split_at_mut(Self::BYTES);
+
+            self.copy_le_bytes_to(buf);
+            writer.write_all(buf)
         }
     }
 }

kpp avatar Mar 01 '25 12:03 kpp