Iterator::map_windows stores its active window in an internal Buffer<T, N>. Buffer<T, N>::clone first constructs a destination Buffer with start = self.start and uninitialized backing storage, then calls buffer.as_uninit_array_mut().write(self.as_array_ref().clone()). If cloning the [T; N] panics before the destination array is written, unwinding drops the destination Buffer.
This matters because Drop for Buffer<T, N> unconditionally drops N elements starting at start. In the panic path above, those slots are uninitialized, so safe Rust can cause ptr::drop_in_place to run on uninitialized memory. The PoC below demonstrates this as an invalid reference dereference during Drop.
Security impact
Expected to be low.
Triggering the bug requires all of the following conditions:
- Use of nightly-only unstable
#![feature(iter_map_windows)].
- A
MapWindows iterator has been advanced far enough for inner.buffer to be Some(Buffer<_, N>).
- The
MapWindows iterator is cloned.
I::Item: Clone, and an item’s Clone::clone panics while cloning the active window.
- Panic unwinding is enabled, so the partially constructed internal
Buffer is dropped.
Data flow trace
MapWindows::clone → MapWindowsInner::clone → Option<Buffer>::clone → Buffer::clone → T::clone panic → Buffer::drop
-
Buffer: documents that self.buffer[self.start..self.start + N] is initialized.
|
// `Buffer` uses two times of space to reduce moves among the iterations. |
|
// `Buffer<T, N>` is semantically `[MaybeUninit<T>; 2 * N]`. However, due |
|
// to limitations of const generics, we use this different type. Note that |
|
// it has the same underlying memory layout. |
|
struct Buffer<T, const N: usize> { |
|
// Invariant: `self.buffer[self.start..self.start + N]` is initialized, |
|
// with all other elements being uninitialized. This also |
|
// implies that `self.start <= N`. |
|
buffer: [[MaybeUninit<T>; N]; 2], |
|
start: usize, |
|
} |
-
Buffer::clone: constructs a destination Buffer with uninitialized storage and an active Drop, then performs panic-capable cloning before the destination slots are initialized.
|
impl<T: Clone, const N: usize> Clone for Buffer<T, N> { |
|
fn clone(&self) -> Self { |
|
let mut buffer = Buffer { |
|
buffer: [[const { MaybeUninit::uninit() }; N], [const { MaybeUninit::uninit() }; N]], |
|
start: self.start, |
|
}; |
|
buffer.as_uninit_array_mut().write(self.as_array_ref().clone()); |
|
buffer |
|
} |
-
Buffer::drop: drops N slots starting at start.
|
impl<T, const N: usize> Drop for Buffer<T, N> { |
|
fn drop(&mut self) { |
|
// SAFETY: our invariant guarantees that N elements starting from |
|
// `self.start` are initialized. We drop them here. |
|
unsafe { |
|
let initialized_part: *mut [T] = crate::ptr::slice_from_raw_parts_mut( |
|
self.buffer_mut_ptr().add(self.start).cast(), |
|
N, |
|
); |
|
ptr::drop_in_place(initialized_part); |
|
} |
|
} |
Demonstration
This PoC uses only safe Rust. It initializes a map_windows buffer, clones the iterator, panics immediately from CloneBomb::clone, then crashes when Buffer::drop drops uninitialized CloneBomb slots.
Run with:
cargo +nightly run --features unstable-map-windows --bin map_windows_clone_panic_poc
// cargo +nightly run --features unstable-map-windows --bin map_windows_clone_panic_poc
#![feature(iter_map_windows)]
#![deny(unsafe_code)]
const WINDOW: usize = 4;
const STATIC_U8: &u8 = &42;
fn main() {
let input: [CloneBomb; WINDOW] = std::array::from_fn(|_| CloneBomb::new());
let mut iter = IntoIterator::into_iter(input).map_windows::<_, _, WINDOW>(|_| ());
iter.next();
eprintln!("initialized a {WINDOW}-element map_windows buffer from an array");
eprintln!("cloning the iterator will panic");
let _clone = iter.clone();
}
struct CloneBomb {
payload: &'static u8,
}
impl Clone for CloneBomb {
fn clone(&self) -> Self {
panic!("intentional panic from CloneBomb::clone");
}
}
impl Drop for CloneBomb {
fn drop(&mut self) {
// Buffer::drop calls this for an uninitialized slot after clone panics.
// Printing the raw address first shows the bad reference before the
// following dereference turns it into a visible abort in a plain run.
let payload_addr = self.payload as *const u8;
eprintln!("CloneBomb::drop, payload ref {payload_addr:p}");
eprintln!("payload byte: {}", *self.payload);
}
}
impl CloneBomb {
fn new() -> Self {
Self { payload: STATIC_U8 }
}
}
Output
Pointer values and the final crash message may vary by platform.
initialized a 4-element map_windows buffer from an array
cloning the iterator will panic
thread 'main' (1099108) panicked at src/bin/map_windows_clone_panic_poc.rs:26:9:
intentional panic from CloneBomb::clone
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
CloneBomb::drop, payload ref 0x599659d14f28
payload byte: 42
CloneBomb::drop, payload ref 0x599659d14f28
payload byte: 42
CloneBomb::drop, payload ref 0x2828282828282828
payload byte: Segmentation fault (core dumped) cargo +nightly run --features unstable-map-windows --bin map_windows_clone_panic_poc 2>&1
Environment
$ rustc +nightly --version --verbose
rustc 1.97.0-nightly (4b0c9d76a 2026-05-10)
binary: rustc
commit-hash: 4b0c9d76ae7d387229caea55cfa73c280b08b8a7
commit-date: 2026-05-10
host: x86_64-unknown-linux-gnu
release: 1.97.0-nightly
LLVM version: 22.1.4
$ lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 26.04 LTS
Release: 26.04
Codename: resolute
The initial discovery was made by AI. The analysis, exploitation, and this report have been reviewed and verified by human experts.
Reporting on behalf of Autonomous Code Security (ACS) team at Microsoft.
Iterator::map_windowsstores its active window in an internalBuffer<T, N>.Buffer<T, N>::clonefirst constructs a destinationBufferwithstart = self.startand uninitialized backing storage, then callsbuffer.as_uninit_array_mut().write(self.as_array_ref().clone()). If cloning the[T; N]panics before the destination array is written, unwinding drops the destinationBuffer.This matters because
Drop for Buffer<T, N>unconditionally dropsNelements starting atstart. In the panic path above, those slots are uninitialized, so safe Rust can causeptr::drop_in_placeto run on uninitialized memory. The PoC below demonstrates this as an invalid reference dereference duringDrop.Security impact
Expected to be low.
Triggering the bug requires all of the following conditions:
#![feature(iter_map_windows)].MapWindowsiterator has been advanced far enough forinner.bufferto beSome(Buffer<_, N>).MapWindowsiterator is cloned.I::Item: Clone, and an item’sClone::clonepanics while cloning the active window.Bufferis dropped.Data flow trace
MapWindows::clone→MapWindowsInner::clone→Option<Buffer>::clone→Buffer::clone→T::clonepanic →Buffer::dropBuffer: documents thatself.buffer[self.start..self.start + N]is initialized.rust/library/core/src/iter/adapters/map_windows.rs
Lines 33 to 43 in 4b0c9d7
Buffer::clone: constructs a destinationBufferwith uninitialized storage and an activeDrop, then performs panic-capable cloning before the destination slots are initialized.rust/library/core/src/iter/adapters/map_windows.rs
Lines 195 to 203 in 4b0c9d7
Buffer::drop: dropsNslots starting atstart.rust/library/core/src/iter/adapters/map_windows.rs
Lines 216 to 227 in 4b0c9d7
Demonstration
This PoC uses only safe Rust. It initializes a
map_windowsbuffer, clones the iterator, panics immediately fromCloneBomb::clone, then crashes whenBuffer::dropdrops uninitializedCloneBombslots.Run with:
Output
Pointer values and the final crash message may vary by platform.
Environment
The initial discovery was made by AI. The analysis, exploitation, and this report have been reviewed and verified by human experts.
Reporting on behalf of Autonomous Code Security (ACS) team at Microsoft.