rust-lang/rust · error
there must be provenance somewhere here
Error message
there must be provenance somewhere here
What it means
This `.expect` in `Allocation::get_bytes_strip_provenance` fires only when `Prov::OFFSET_IS_ADDR == false` AND `provenance.range_empty(range, cx)` returned `false` (provenance overlaps the range) yet `provenance.get_range(range, cx).next()` yields `None`. The two provenance queries are supposed to agree: if the range overlaps any provenance, `get_range` must produce at least one entry. The panic therefore signals corruption or desync inside the provenance bookkeeping data structure (the tags/blocks table), not a user error.
Source
Thrown at compiler/rustc_middle/src/mir/interpret/allocation.rs:614
#[inline]
pub fn get_bytes_strip_provenance(
&self,
cx: &impl HasDataLayout,
range: AllocRange,
) -> AllocResult<&[u8]> {
self.init_mask.is_range_initialized(range).map_err(|uninit_range| {
AllocError::InvalidUninitBytes(Some(BadBytesAccess {
access: range,
bad: uninit_range,
}))
})?;
if !Prov::OFFSET_IS_ADDR && !self.provenance.range_empty(range, cx) {
// Find the provenance.
let (prov_range, _prov) = self
.provenance
.get_range(range, cx)
.next()
.expect("there must be provenance somewhere here");
let start = prov_range.start.max(range.start); // the pointer might begin before `range`!
let end = prov_range.end().min(range.end()); // the pointer might end after `range`!
return Err(AllocError::ReadPointerAsInt(Some(BadBytesAccess {
access: range,
bad: AllocRange::from(start..end),
})));
}
Ok(self.get_bytes_unchecked(range))
}
/// This is the entirely abstraction-violating way to just get mutable access to the raw bytes.
/// Just calling this already marks everything as defined and removes provenance, so be sure to
/// actually overwrite all the data there!
///
/// It is the caller's responsibility to check bounds and alignment beforehand.
/// Most likely, you want to use the `PlaceTy` and `OperandTy`-based methods
/// on `InterpCx` instead.
pub fn get_bytes_unchecked_for_overwrite(View on GitHub (pinned to 22057b88b0)
Solutions
- Reproduce with `RUST_BACKTRACE=1` and capture the allocation id + range from the panic context, then audit the last `write_provenance`/`mark_init`/`clear` calls on that allocation.
- Bisect recent changes to `compiler/rustc_middle/src/mir/interpret/allocation.rs` and the `Provenance` impl — the bug is almost always an index update that skipped one of the two queries.
- Run the case under Miri with extra checks (`-Zmiri-track-raw-pointers -Zmiri-validate-resolution`) to surface the first inconsistent write.
- Report as a rustc ICE with a minimal repro; do not try to silence the expect.
Example fix
// This is an internal invariant; no user-side code change silences it safely.
// before: provenance.range_empty(r) == false and provenance.get_range(r).next() == None
// fix: make the Provenance impl keep `range_empty` and `get_range` in lockstep.
// e.g. in `clear`, also drop the block from the range index:
impl Provenance {
fn clear(&mut self, range: AllocRange, ...) {
self.blocks.retain(|b| !range.contains(b.range)); // keep both views consistent
}
} Defensive patterns
Strategy: validation
Validate before calling
// The expect fires when provenance.range_empty(range, cx) was false but
// get_range(range, cx).next() returned None -- an internal inconsistency.
// You cannot fully prevent an ICE here from the outside, but you can avoid
// feeding it ranges whose provenance bookkeeping you have not confirmed.
fn range_has_provenance<Prov, Cx>(
alloc: &Allocation<Prov>,
range: AllocRange,
cx: &Cx,
) -> bool
where
Prov: ProvenanceMap<Cx>,
{
!alloc.provenance.range_empty(range, cx)
&& alloc.provenance.get_range(range, cx).next().is_some()
}
if !range_has_provenance(alloc, range, cx) {
return Err("provenance bookkeeping inconsistent for range");
} Try / catch
let bytes = std::panic::catch_unwind(|| alloc.get_bytes_with_provenance(cx, range));
match bytes {
Ok(b) => /* use b */,
Err(_) => /* this is an ICE: report upstream with the allocation dump */,
} Prevention
- Treat this panic as an internal-compiler-error signal: it indicates corrupted allocation provenance, not user input, so file a rustc issue with the reproducer rather than working around it.
- When manipulating allocations directly (e.g. in Miri or a custom codegen), keep the provenance map and byte writes in lockstep so no range is ever marked as having provenance without an entry.
- Before reading bytes that may overlap a pointer, prefer the higher-level read_scalar / read_pointer APIs that return Err(AllocError) instead of the unchecked byte path.
- If you build allocations incrementally, run a self-check pass (provenance ranges are non-overlapping and within bounds) before handing the allocation to interpretation.
When it happens
Trigger: An allocation whose `Provenance` map became inconsistent — e.g. a `clear`/`init`/`write_provenance` sequence left `range_empty` and `get_range` disagreeing; a partial write that updated only one of two internal indices; or a fuzzed allocation whose bytes/provenance/init_mask triples were mutated out of sync. Reachable via Miri, const-eval, or any direct caller of `get_bytes_strip_provenance` on a corrupted `Allocation`.
Common situations: Miri/MIR-interpret test failures after touching the `Provenance` enum or switching between `Tag` and `AllocId` provenance; rmeta/const-eval regressions when a new optimization reorders provenance-clearing vs byte writes; differential-fuzzing crashes between two rustc builds.
Related errors
- statics should not have generic parameters
- got a pointer where a ScalarInt was expected
- range should be nonempty
- an interpreter error got improperly discarded; use `discard_
- invalid level/lint_id combination
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/edfbff0d618a4ab8.json.
Report an issue: GitHub.