Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions crates/ssz/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,9 @@ fn decode_byte_array_vec<const N: usize>(bytes: &[u8]) -> Result<Vec<[u8; N]>, D
}
let count = bytes.len() / N;
let mut result = Vec::<[u8; N]>::with_capacity(count);
// SAFETY: The length check guarantees `count * N == bytes.len()`, and
// `with_capacity(count)` allocates at least `count * N` bytes, so the
// copy and `set_len` stay in bounds.
unsafe {
core::ptr::copy_nonoverlapping(bytes.as_ptr(), result.as_mut_ptr() as *mut u8, bytes.len());
result.set_len(count);
Expand Down
38 changes: 28 additions & 10 deletions crates/ssz/src/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,23 @@ pub trait SszEncode {
}
}

/// Reinterpret `&[T]` as `&[u8]` and append to `buf`.
///
/// # Safety
///
/// `T` must not have padding bytes.
///
/// # Correctness
///
/// `T` must have a little-endian in-memory layout matching SSZ wire
/// format, unless `T` is endianness-independent (e.g. `[u8; N]`).
#[cfg(feature = "alloc")]
fn append_fixed_slice_as_bytes<T>(items: &[T], buf: &mut Vec<u8>) {
let byte_slice = unsafe {
core::slice::from_raw_parts(items.as_ptr().cast::<u8>(), core::mem::size_of_val(items))
};
unsafe fn append_fixed_slice_as_bytes<T>(items: &[T], buf: &mut Vec<u8>) {
// Padding bytes in a valid `T` may be uninitialized, and a `&[u8]`
// covering uninitialized bytes is UB (a reference must point to
// valid values, and uninitialized `u8` is invalid).
let byte_slice =
core::slice::from_raw_parts(items.as_ptr().cast::<u8>(), core::mem::size_of_val(items));
buf.extend_from_slice(byte_slice);
}

Expand Down Expand Up @@ -95,7 +107,8 @@ impl SszEncode for u8 {
fn ssz_append_fixed_slice(items: &[Self], buf: &mut Vec<u8>) {
#[cfg(target_endian = "little")]
{
append_fixed_slice_as_bytes(items, buf);
// SAFETY: primitive integers have no padding.
unsafe { append_fixed_slice_as_bytes(items, buf) };
}
#[cfg(not(target_endian = "little"))]
{
Expand Down Expand Up @@ -128,7 +141,8 @@ impl SszEncode for u16 {
fn ssz_append_fixed_slice(items: &[Self], buf: &mut Vec<u8>) {
#[cfg(target_endian = "little")]
{
append_fixed_slice_as_bytes(items, buf);
// SAFETY: primitive integers have no padding.
unsafe { append_fixed_slice_as_bytes(items, buf) };
}
#[cfg(not(target_endian = "little"))]
{
Expand Down Expand Up @@ -161,7 +175,8 @@ impl SszEncode for u32 {
fn ssz_append_fixed_slice(items: &[Self], buf: &mut Vec<u8>) {
#[cfg(target_endian = "little")]
{
append_fixed_slice_as_bytes(items, buf);
// SAFETY: primitive integers have no padding.
unsafe { append_fixed_slice_as_bytes(items, buf) };
}
#[cfg(not(target_endian = "little"))]
{
Expand Down Expand Up @@ -194,7 +209,8 @@ impl SszEncode for u64 {
fn ssz_append_fixed_slice(items: &[Self], buf: &mut Vec<u8>) {
#[cfg(target_endian = "little")]
{
append_fixed_slice_as_bytes(items, buf);
// SAFETY: primitive integers have no padding.
unsafe { append_fixed_slice_as_bytes(items, buf) };
}
#[cfg(not(target_endian = "little"))]
{
Expand Down Expand Up @@ -227,7 +243,8 @@ impl SszEncode for u128 {
fn ssz_append_fixed_slice(items: &[Self], buf: &mut Vec<u8>) {
#[cfg(target_endian = "little")]
{
append_fixed_slice_as_bytes(items, buf);
// SAFETY: primitive integers have no padding.
unsafe { append_fixed_slice_as_bytes(items, buf) };
}
#[cfg(not(target_endian = "little"))]
{
Expand Down Expand Up @@ -260,7 +277,8 @@ impl<const N: usize> SszEncode for [u8; N] {
}
#[cfg(feature = "alloc")]
fn ssz_append_fixed_slice(items: &[Self], buf: &mut Vec<u8>) {
append_fixed_slice_as_bytes(items, buf);
// SAFETY: [u8; N] has no padding.
unsafe { append_fixed_slice_as_bytes(items, buf) };
}
}

Expand Down
Loading