You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This finding was identified during an agentic unsafe Rust code review performed by Gemini AI, followed by human review and verification.
The Issue
The array_ref! and array_mut_ref! macros perform an unsafe pointer cast from a sliced result to a fixed-size array reference &[T; $len] without verifying that the runtime slice length actually matches $len.
The macros obtain a slice via &$arr[offset..offset + $len] and directly dereference slice.as_ptr() as *const [_; $len] inside as_array:
While slicing a standard slice [T] guarantees the length, the macros accept any type implementing the safe Index<Range<usize>, Output = [T]> (or IndexMut) trait.
A custom Index implementation can safely return a slice shorter than $len (such as an empty slice), causing the macro in safe caller code to construct an array reference pointing to unallocated memory or out-of-bounds elements, resulting in immediate UB.
Minimal Reproduction (Miri)
use arrayref::array_ref;structBadIndex;impl std::ops::Index<std::ops::Range<usize>>forBadIndex{typeOutput = [u8];fnindex(&self,_index: std::ops::Range<usize>) -> &[u8]{&[]}}fnmain(){let bad = BadIndex;let r:&[u8;5] = array_ref![bad,0,5];
std::hint::black_box(r);}
error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 5 bytes, but got alloc2 which is at or beyond the end of the allocation of size 0 bytes
--> src/bin/repro1.rs:14:23
|
14 | let r: &[u8; 5] = array_ref![bad, 0, 5];
| ^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: stack backtrace:
0: main::as_array::<u8>
at /usr/local/google/home/manishearth/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayref-0.3.9/src/lib.rs:62:17: 62:55
1: main
at /usr/local/google/home/manishearth/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/arrayref-0.3.9/src/lib.rs:68:17: 68:32
= note: this error originates in the macro `array_ref` (in Nightly builds, run with -Z macro-backtrace for more info)
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
Suggested Fix
Assert that the obtained slice runtime length matches $len before performing the unsafe array reference cast:
An identical assertion should also be added to array_mut_ref!.
Full Gemini Unsafe Code Audit Report
[!NOTE]
The full audit report below also contains additional minor findings (such as missing safety comments or undocumented FFI assumptions) that are probably worth fixing as well but not the primary goal of this issue. The audit report has not been human-reviewed, it may contain misleading claims.
Unsafe Rust Review: arrayref (v0_3)
Overall Safety Assessment
The arrayref crate provides macros to extract array references from slices. It contains a small amount of unsafe code inside these macros, which performs pointer casts to convert slice pointers to array references.
The unsafe code has some safety issues:
There is a critical soundness bug in the array_ref! and array_mut_ref! macros. They assume that slicing a type yields a slice of the exact expected length. However, because Index is a safe trait, a custom implementation can return a shorter slice, resulting in out-of-bounds reads/writes when dereferencing the casted pointer.
There are no # Safety documentation or // SAFETY: comments on any of the unsafe boundaries or operations.
The macros rely on left-to-right evaluation order of tuple elements to advance pointers safely, which is guaranteed by the Rust Reference but is fragile and undocumented.
Due to the soundness bug, this crate cannot be considered fully sound in the presence of custom Index implementations.
Critical Findings
Soundness Bug in array_ref! and array_mut_ref! Macros 🔴 🤸
Priority: 🔴 High
Threat Vector: 🤸 Deliberate Contortion
Bug Type: Out-of-Bounds Pointer Dereference
Description:
The macros array_ref! and array_mut_ref! cast a slice &[T] (or &mut [T]) to an array reference &[T; $len] (or &mut [T; $len]).
The slice is obtained via: let slice = &$arr[offset..offset + $len];
And the cast is: &*(slice.as_ptr() as *const [_; $len])
The macro assumes that slice.len() == $len. While this is guaranteed for standard slices ([T]), the macro can be called on any type $arr that implements Index<Range<usize>, Output = [T]>. Because Index is a safe trait, a user can provide an implementation that returns a slice of a different length (e.g., an empty slice &[]). If this happens, the macro will dereference a pointer pointing to fewer than $len elements, leading to Undefined Behavior (out-of-bounds read or write).
Proof of Concept:
structBadIndex;impl std::ops::Index<std::ops::Range<usize>>forBadIndex{typeOutput = [u8];fnindex(&self,_index: std::ops::Range<usize>) -> &[u8]{&[]// Returns an empty slice regardless of the range}}fnmain(){let bad = BadIndex;// This is safe code but triggers UB (Segmentation fault) when printed:let r:&[u8;5] = array_ref![bad,0,5];println!("r = {:?}", r);}
Remedy:
Add an assertion in the macros to verify that the slice's runtime length matches the expected length before performing the unsafe cast:
let slice = &$arr[offset..offset + $len];assert_eq!(slice.len(), $len);
Fishy Findings
Fragile Reliance on Tuple Evaluation Order 🟡 🤦
Severity: 🟡 Low
Threat Vector: 🤦 Accidental Misuse
Bug Type: Fragile Evaluation Order Reliance
Description:
The array_refs! and mut_array_refs! macros use a mutable pointer p that is modified sequentially during the construction of a tuple:
This assumes that the expressions inside the tuple are evaluated from left to right, so that p is advanced correctly before the next array reference is created.
While the Rust Reference guarantees left-to-right evaluation order for tuple expressions, relying on side effects across tuple elements to guarantee memory safety is fragile. If the compiler were to evaluate them in a different order (e.g. due to optimization bugs or future language changes), it would result in overlapping or out-of-bounds references, which is UB.
This reliance is not documented or commented in the code.
Missing Safety Comments
Missing Safety Comments on as_array and Pointer Casts 🔴
The crate lacks any safety comments. The following comments should be added:
In array_ref! (and similarly in array_mut_ref!):
/// # Safety////// The caller must ensure that `slice.len() >= $len`.constunsafefnas_array<T>(slice:&[T]) -> &[T; $len]{// SAFETY:// - `slice.as_ptr()` points to a valid allocation of at least `slice.len()` elements (AXIOM: standard library slice contract).// - By the safety contract of this function, `slice` contains at least `$len` elements (PRECONDITION).// - Therefore, casting the pointer to `*const [T; $len]` and dereferencing it is valid (AXIOM: primitive pointer dereference).// - The returned reference's lifetime is bound to the input `slice`'s lifetime (TYPE FACT).&*(slice.as_ptr()as*const[_; $len])}
In array_refs! (and similarly in mut_array_refs!):
/// # Safety////// The caller must ensure that `a.len() >= MIN_LEN`.constunsafefnas_arrays<T>(a:&[T]) -> ( $(&[T; $pre],)*&[T], $(&[T; $post],)*){constMIN_LEN:usize = 0usize $(.saturating_add($pre))* $(.saturating_add($post))*;// ...letmut p = a.as_ptr();( $({// SAFETY:// - `p` is derived from `a.as_ptr()`.// - Since we asserted `a.len() >= MIN_LEN`, the pointer `p` remains within the bounds of the valid slice `a`// for this offset (LOCAL FACT, PRECONDITION).// - The cast to `*const [T; $pre]` is valid because there are at least `$pre` elements remaining in the slice.let aref = &*(p as*const[T; $pre]);// SAFETY: `p` is advanced by `$pre` elements, which is safe because `p + $pre` is within or at most one element past the end of the slice (AXIOM: pointer arithmetic).
p = p.add($pre);
aref
},)* ...)}
Note
This finding was identified during an agentic unsafe Rust code review performed by Gemini AI, followed by human review and verification.
The Issue
The
array_ref!andarray_mut_ref!macros perform an unsafe pointer cast from a sliced result to a fixed-size array reference&[T; $len]without verifying that the runtime slice length actually matches$len.The macros obtain a slice via
&$arr[offset..offset + $len]and directly dereferenceslice.as_ptr() as *const [_; $len]insideas_array:arrayref/src/lib.rs
Lines 57 to 72 in f8d0299
arrayref/src/lib.rs
Lines 283 to 298 in f8d0299
While slicing a standard slice
[T]guarantees the length, the macros accept any type implementing the safeIndex<Range<usize>, Output = [T]>(orIndexMut) trait.A custom
Indeximplementation can safely return a slice shorter than$len(such as an empty slice), causing the macro in safe caller code to construct an array reference pointing to unallocated memory or out-of-bounds elements, resulting in immediate UB.Minimal Reproduction (Miri)
Suggested Fix
Assert that the obtained slice runtime length matches
$lenbefore performing the unsafe array reference cast:macro_rules! array_ref { ($arr:expr, $offset:expr, $len:expr) => {{ { #[inline] const unsafe fn as_array<T>(slice: &[T]) -> &[T; $len] { &*(slice.as_ptr() as *const [_; $len]) } let offset = $offset; let slice = &$arr[offset..offset + $len]; + assert!(slice.len() == $len); #[allow(unused_unsafe)] unsafe { as_array(slice) } } }}; }An identical assertion should also be added to
array_mut_ref!.Full Gemini Unsafe Code Audit Report
Unsafe Rust Review:
arrayref(v0_3)Overall Safety Assessment
The
arrayrefcrate provides macros to extract array references from slices. It contains a small amount of unsafe code inside these macros, which performs pointer casts to convert slice pointers to array references.The unsafe code has some safety issues:
array_ref!andarray_mut_ref!macros. They assume that slicing a type yields a slice of the exact expected length. However, becauseIndexis a safe trait, a custom implementation can return a shorter slice, resulting in out-of-bounds reads/writes when dereferencing the casted pointer.# Safetydocumentation or// SAFETY:comments on any of the unsafe boundaries or operations.Due to the soundness bug, this crate cannot be considered fully sound in the presence of custom
Indeximplementations.Critical Findings
Soundness Bug in
array_ref!andarray_mut_ref!Macros 🔴 🤸Description:
The macros
array_ref!andarray_mut_ref!cast a slice&[T](or&mut [T]) to an array reference&[T; $len](or&mut [T; $len]).The slice is obtained via:
let slice = &$arr[offset..offset + $len];And the cast is:
&*(slice.as_ptr() as *const [_; $len])The macro assumes that
slice.len() == $len. While this is guaranteed for standard slices ([T]), the macro can be called on any type$arrthat implementsIndex<Range<usize>, Output = [T]>. BecauseIndexis a safe trait, a user can provide an implementation that returns a slice of a different length (e.g., an empty slice&[]). If this happens, the macro will dereference a pointer pointing to fewer than$lenelements, leading to Undefined Behavior (out-of-bounds read or write).Proof of Concept:
Remedy:
Add an assertion in the macros to verify that the slice's runtime length matches the expected length before performing the unsafe cast:
Fishy Findings
Fragile Reliance on Tuple Evaluation Order 🟡 🤦
Description:
The
array_refs!andmut_array_refs!macros use a mutable pointerpthat is modified sequentially during the construction of a tuple:This assumes that the expressions inside the tuple are evaluated from left to right, so that
pis advanced correctly before the next array reference is created.While the Rust Reference guarantees left-to-right evaluation order for tuple expressions, relying on side effects across tuple elements to guarantee memory safety is fragile. If the compiler were to evaluate them in a different order (e.g. due to optimization bugs or future language changes), it would result in overlapping or out-of-bounds references, which is UB.
This reliance is not documented or commented in the code.
Missing Safety Comments
Missing Safety Comments on
as_arrayand Pointer Casts 🔴The crate lacks any safety comments. The following comments should be added:
array_ref!(and similarly inarray_mut_ref!):array_refs!(and similarly inmut_array_refs!):