Soundness issue when the two uses of the `$count` expression evaluate to different things.
The implementation contains $count
in two places
let mut vec = $crate::__ArrayVec::<_, {$count}>(unsafe { … });
while vec.0.len < $count {
…
}
These could evaluate to different things. Even though it's forced to be a const expression, so there won't be any run-time side-effects, a procedural macro could evaluate to two different literals when called twice in an expression like array![0; proc_macro_call!()]
. (Recall that the outer macro call evaluates first.)
Furthermore, this is even exploitable without proc-macros by abusing the differences in type-inference information available at the two places (one is known to be T == usize
, the other just needs usize: PartialOrd<T>
) which can influence coercions in different ways:
use array_macro::array;
fn main() {
impl PartialEq<Foo> for usize {
fn eq(&self, _: &Foo) -> bool {
*self == 0
}
}
impl PartialOrd<Foo> for usize {
fn partial_cmp(&self, _: &Foo) -> Option<std::cmp::Ordering> {
self.partial_cmp(&0)
}
}
trait Tr<P> {
const CON: P;
}
const fn bar<P, T>(_: &T) -> P
where
(T,): Tr<P>,
{
<(T,)>::CON
}
impl Tr<usize> for ((),) {
const CON: usize = 1;
}
struct Foo;
impl Tr<Foo> for (&(),) {
const CON: Foo = Foo;
}
let x = array![Box::new(String::new()); bar(&&() as _)];
println!("{x:?}");
}
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
Running `target/debug/…CRATE_NAME…`
[1] 494953 segmentation fault (core dumped) cargo run
One possible proposed fix: Add a function
pub fn __array_vec_capacity<T, const N: usize>(_: &__ArrayVec<T, N>) -> usize {
N
}
and use while vec.0.len < $crate::__array_vec_capacity(&vec)
for the loop.
Edited by Frank Steffahn