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

  1. 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.
  2. 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.
  3. Run the case under Miri with extra checks (`-Zmiri-track-raw-pointers -Zmiri-validate-resolution`) to surface the first inconsistent write.
  4. 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

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


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/edfbff0d618a4ab8.json. Report an issue: GitHub.