`iter::Rev`'s `TrustedLen` impl is unsound with trait objects
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
This unsoundness exploit uses the same construction as #154619, #156917, and #156920.
Tracking issue for TrustedLen: https://github.com/rust-lang/rust/issues/37572
The following code compiles and causes a segfault at run time. (The explanation of the issue is below the code.)
#![feature(trusted_len)]
use std::iter::{self, TrustedLen};
trait Trait: Iterator<Item = i32> + TrustedLen {}
impl DoubleEndedIterator for dyn Trait + '_ {
fn next_back(&mut self) -> Option<i32> {
Some(1)
}
}
impl Trait for iter::Empty<i32> {}
fn main() {
let mut iter = iter::empty::<i32>();
let dyn_trait: &mut dyn Trait = &mut iter;
let _v: Vec<i32> = dyn_trait.rev().collect();
println!("done");
}
The way I see this issue, the root of the unsoundness starts from an ambiguity in the documentation of TrustedLen, which says:
The iterator must produce exactly the number of elements it reported or diverge before reaching the end.
It is not clear what "produce" means, when DoubleEndedIterator is involved. There are two possible interpretations of this:
- Interpretation 1: implementing
TrustedLendoes not impose any requirements on the behavior ofDoubleEndedIterator. That is, calling.next_back()repeatedly is not guaranteed to produce the number of elements specified by.size_hint(). - Interpretation 2: implementing
TrustedLenmeans that the number of elements produced, by.next()or.next_back()combined, must total to the number specified by.size_hint().
The iter::Rev iterator has the following implementation for TrustedLen:
unsafe impl<I> TrustedLen for Rev<I> where I: TrustedLen + DoubleEndedIterator {}
This implementation implicitly assumes that TrustedLen's safety requirement is to be interpreted as per interpretation 2, since Rev's .next() method delegates to the .next_back() method of the iterator inside. So, Rev<I> being a well-behaved TrustedLen requires I to have its DoubleEndedIterator impl to be well-behaved as per TrustedLen's interpretation 2.
Therefore, if we can create a type which implements TrustedLen correctly according to interpretation 1, but incorrectly according to interpretation 2, we can cause unsoundness.
To concretely produce unsoundness, we have a Trait which is a subtrait of TrustedLen. This essentially results in an implicit impl TrustedLen for dyn Trait + '_, generated by the compiler.
As TrustedLen is a subtrait of Iterator, this means that dyn Trait's Iterator impl has to inherit the behavior of the concrete type behind the trait object. That is, this compiler-generated TrustedLen impl is correct according to interpretation 1.
We then implement DoubleEndedIterator on dyn Trait, such that its length disagrees with .size_hint(). This causes the compiler-generated TrustedLen impl to be incorrect according to interpretation 2.
We then create a Rev<&mut dyn Trait>, which "transfers" the incorrectness according to interpretation 2, into incorrectness according to interpretation 1. That is, Rev<&mut dyn Trait> implements TrustedLen incorrectly according to both interpretation 1 and 2. (Note that this also involves the implementation of TrustedLen on &mut)
We then collect the iterator into a Vec. Vec's specialization sees that Rev<&mut dyn Trait> is TrustedLen, and therefore trusts the .size_hint() method of the iterator. This size_hint() returns 0, since it's from iter::Empty's Iterator impl, so the Vec reserves space for zero elements. However, calling .next() on the Rev<&mut dyn Trait> delegates to .next_back() on dyn Trait, which produces infinitely many elements. This writes out of bounds and eventually causes a segfault.
Meta
Reproducible on the playground with version 1.99.0-nightly (2026-07-27 09ee43b2d6055539771b)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reproducing the example with the nightly compiler, then read the TrustedLen and Rev documentation and the linked implementations for &mut and Vec collection. Trace how dyn Trait, Rev, and collect combine their length assumptions; done means the unsound behavior is resolved without allowing the reported out-of-bounds write.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers, security
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100