Skip to content

Commit 3838168

Browse files
Rollup merge of #130885 - RalfJung:interp-error-discard, r=oli-obk
panic when an interpreter error gets unintentionally discarded One important invariant of Miri is that when an interpreter error is raised (*in particular* a UB error), those must not be discarded: it's not okay to just check `foo().is_err()` and then continue executing. This seems to catch new contributors by surprise fairly regularly, so this PR tries to make it so that *if* this ever happens, we get a panic rather than a silent missed UB bug. The interpreter error type now contains a "guard" that panics on drop, and that is explicitly passed to `mem::forget` when an error is deliberately discarded. Fixes rust-lang#3855
2 parents 7503c71 + 124c7ad commit 3838168

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+735
-724
lines changed

src/alloc_addresses/mod.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
200200
AllocKind::Dead => unreachable!(),
201201
};
202202
// Ensure this pointer's provenance is exposed, so that it can be used by FFI code.
203-
return Ok(base_ptr.expose_provenance().try_into().unwrap());
203+
return interp_ok(base_ptr.expose_provenance().try_into().unwrap());
204204
}
205205
// We are not in native lib mode, so we control the addresses ourselves.
206206
if let Some((reuse_addr, clock)) =
@@ -209,7 +209,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
209209
if let Some(clock) = clock {
210210
ecx.acquire_clock(&clock);
211211
}
212-
Ok(reuse_addr)
212+
interp_ok(reuse_addr)
213213
} else {
214214
// We have to pick a fresh address.
215215
// Leave some space to the previous allocation, to give it some chance to be less aligned.
@@ -234,7 +234,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
234234
throw_exhaust!(AddressSpaceFull);
235235
}
236236

237-
Ok(base_addr)
237+
interp_ok(base_addr)
238238
}
239239
}
240240

@@ -248,7 +248,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
248248
let global_state = &mut *global_state;
249249

250250
match global_state.base_addr.get(&alloc_id) {
251-
Some(&addr) => Ok(addr),
251+
Some(&addr) => interp_ok(addr),
252252
None => {
253253
// First time we're looking for the absolute address of this allocation.
254254
let base_addr =
@@ -274,7 +274,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
274274
};
275275
global_state.int_to_ptr_map.insert(pos, (base_addr, alloc_id));
276276

277-
Ok(base_addr)
277+
interp_ok(base_addr)
278278
}
279279
}
280280
}
@@ -287,20 +287,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
287287
let global_state = ecx.machine.alloc_addresses.get_mut();
288288
// In strict mode, we don't need this, so we can save some cycles by not tracking it.
289289
if global_state.provenance_mode == ProvenanceMode::Strict {
290-
return Ok(());
290+
return interp_ok(());
291291
}
292292
// Exposing a dead alloc is a no-op, because it's not possible to get a dead allocation
293293
// via int2ptr.
294294
if !ecx.is_alloc_live(alloc_id) {
295-
return Ok(());
295+
return interp_ok(());
296296
}
297297
trace!("Exposing allocation id {alloc_id:?}");
298298
let global_state = ecx.machine.alloc_addresses.get_mut();
299299
global_state.exposed.insert(alloc_id);
300300
if ecx.machine.borrow_tracker.is_some() {
301301
ecx.expose_tag(alloc_id, tag)?;
302302
}
303-
Ok(())
303+
interp_ok(())
304304
}
305305

306306
fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer> {
@@ -337,7 +337,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
337337
// cast is fairly irrelevant. Instead we generate this as a "wildcard" pointer, such that
338338
// *every time the pointer is used*, we do an `AllocId` lookup to find the (exposed)
339339
// allocation it might be referencing.
340-
Ok(Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr)))
340+
interp_ok(Pointer::new(Some(Provenance::Wildcard), Size::from_bytes(addr)))
341341
}
342342

343343
/// Convert a relative (tcx) pointer to a Miri pointer.
@@ -359,7 +359,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
359359
Size::from_bytes(base_addr),
360360
);
361361
// Add offset with the right kind of pointer-overflowing arithmetic.
362-
Ok(base_ptr.wrapping_offset(offset, ecx))
362+
interp_ok(base_ptr.wrapping_offset(offset, ecx))
363363
}
364364

365365
// This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved
@@ -390,9 +390,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
390390
assert_eq!(prepared_alloc_bytes.len(), bytes.len());
391391
// Copy allocation contents into prepared memory.
392392
prepared_alloc_bytes.copy_from_slice(bytes);
393-
Ok(prepared_alloc_bytes)
393+
interp_ok(prepared_alloc_bytes)
394394
} else {
395-
Ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
395+
interp_ok(MiriAllocBytes::from_bytes(std::borrow::Cow::Borrowed(bytes), align))
396396
}
397397
}
398398

src/borrow_tracker/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
322322
match method {
323323
BorrowTrackerMethod::StackedBorrows => {
324324
this.tcx.tcx.dcx().warn("Stacked Borrows does not support named pointers; `miri_pointer_name` is a no-op");
325-
Ok(())
325+
interp_ok(())
326326
}
327327
BorrowTrackerMethod::TreeBorrows =>
328328
this.tb_give_pointer_debug_name(ptr, nth_parent, name),
@@ -333,7 +333,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
333333
let this = self.eval_context_mut();
334334
let Some(borrow_tracker) = &this.machine.borrow_tracker else {
335335
eprintln!("attempted to print borrow state, but no borrow state is being tracked");
336-
return Ok(());
336+
return interp_ok(());
337337
};
338338
let method = borrow_tracker.borrow().borrow_tracker_method;
339339
match method {
@@ -376,7 +376,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
376376
}
377377
}
378378
borrow_tracker.borrow_mut().end_call(&frame.extra);
379-
Ok(())
379+
interp_ok(())
380380
}
381381
}
382382

@@ -489,7 +489,7 @@ impl AllocState {
489489
alloc_id: AllocId, // diagnostics
490490
) -> InterpResult<'tcx> {
491491
match self {
492-
AllocState::StackedBorrows(_sb) => Ok(()),
492+
AllocState::StackedBorrows(_sb) => interp_ok(()),
493493
AllocState::TreeBorrows(tb) =>
494494
tb.borrow_mut().release_protector(machine, global, tag, alloc_id),
495495
}

src/borrow_tracker/stacked_borrows/mod.rs

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ impl<'tcx> Stack {
230230
}
231231

232232
if !item.protected() {
233-
return Ok(());
233+
return interp_ok(());
234234
}
235235

236236
// We store tags twice, once in global.protected_tags and once in each call frame.
@@ -252,10 +252,10 @@ impl<'tcx> Stack {
252252
let allowed = matches!(cause, ItemInvalidationCause::Dealloc)
253253
&& matches!(protector_kind, ProtectorKind::WeakProtector);
254254
if !allowed {
255-
return Err(dcx.protector_error(item, protector_kind).into());
255+
return Err(dcx.protector_error(item, protector_kind)).into();
256256
}
257257
}
258-
Ok(())
258+
interp_ok(())
259259
}
260260

261261
/// Test if a memory `access` using pointer tagged `tag` is granted.
@@ -295,7 +295,7 @@ impl<'tcx> Stack {
295295
self.pop_items_after(first_incompatible_idx, |item| {
296296
Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
297297
dcx.log_invalidation(item.tag());
298-
Ok(())
298+
interp_ok(())
299299
})?;
300300
} else {
301301
// On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses.
@@ -316,7 +316,7 @@ impl<'tcx> Stack {
316316
self.disable_uniques_starting_at(first_incompatible_idx, |item| {
317317
Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Conflict)?;
318318
dcx.log_invalidation(item.tag());
319-
Ok(())
319+
interp_ok(())
320320
})?;
321321
}
322322

@@ -345,7 +345,7 @@ impl<'tcx> Stack {
345345
}
346346

347347
// Done.
348-
Ok(())
348+
interp_ok(())
349349
}
350350

351351
/// Deallocate a location: Like a write access, but also there must be no
@@ -367,7 +367,7 @@ impl<'tcx> Stack {
367367
Stack::item_invalidated(&item, global, dcx, ItemInvalidationCause::Dealloc)?;
368368
}
369369

370-
Ok(())
370+
interp_ok(())
371371
}
372372

373373
/// Derive a new pointer from one with the given tag.
@@ -418,7 +418,7 @@ impl<'tcx> Stack {
418418
"reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown"
419419
);
420420
self.set_unknown_bottom(global.next_ptr_tag);
421-
return Ok(());
421+
return interp_ok(());
422422
};
423423

424424
// SharedReadWrite can coexist with "existing loans", meaning they don't act like a write
@@ -431,7 +431,7 @@ impl<'tcx> Stack {
431431
// Put the new item there.
432432
trace!("reborrow: adding item {:?}", new);
433433
self.insert(new_idx, new);
434-
Ok(())
434+
interp_ok(())
435435
}
436436
}
437437
// # Stacked Borrows Core End
@@ -491,7 +491,7 @@ impl<'tcx> Stacks {
491491
f(stack, &mut dcx, &mut self.exposed_tags)?;
492492
dcx_builder = dcx.unbuild();
493493
}
494-
Ok(())
494+
interp_ok(())
495495
}
496496
}
497497

@@ -576,7 +576,7 @@ impl Stacks {
576576
self.for_each(alloc_range(Size::ZERO, size), dcx, |stack, dcx, exposed_tags| {
577577
stack.dealloc(tag, &state, dcx, exposed_tags)
578578
})?;
579-
Ok(())
579+
interp_ok(())
580580
}
581581
}
582582

@@ -623,7 +623,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
623623
drop(global); // don't hold that reference any longer than we have to
624624

625625
let Some((alloc_id, base_offset, orig_tag)) = loc else {
626-
return Ok(())
626+
return interp_ok(())
627627
};
628628

629629
let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id);
@@ -655,7 +655,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
655655
// No stacked borrows on these allocations.
656656
}
657657
}
658-
Ok(())
658+
interp_ok(())
659659
};
660660

661661
if size == Size::ZERO {
@@ -676,12 +676,12 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
676676
{
677677
log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
678678
// Still give it the new provenance, it got retagged after all.
679-
return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
679+
return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
680680
} else {
681681
// This pointer doesn't come with an AllocId. :shrug:
682682
log_creation(this, None)?;
683683
// Provenance unchanged.
684-
return Ok(place.ptr().provenance);
684+
return interp_ok(place.ptr().provenance);
685685
}
686686
}
687687

@@ -800,12 +800,12 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
800800
)?;
801801
}
802802
}
803-
Ok(())
803+
interp_ok(())
804804
})?;
805805
}
806806
}
807807

808-
Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
808+
interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))
809809
}
810810

811811
fn sb_retag_place(
@@ -832,7 +832,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
832832
*shown = true;
833833
this.emit_diagnostic(NonHaltingDiagnostic::ExternTypeReborrow);
834834
});
835-
return Ok(place.clone());
835+
return interp_ok(place.clone());
836836
}
837837
};
838838

@@ -845,7 +845,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
845845
// Adjust place.
846846
// (If the closure gets called, that means the old provenance was `Some`, and hence the new
847847
// one must also be `Some`.)
848-
Ok(place.clone().map_provenance(|_| new_prov.unwrap()))
848+
interp_ok(place.clone().map_provenance(|_| new_prov.unwrap()))
849849
}
850850

851851
/// Retags an individual pointer, returning the retagged version.
@@ -859,7 +859,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
859859
let this = self.eval_context_mut();
860860
let place = this.ref_to_mplace(val)?;
861861
let new_place = this.sb_retag_place(&place, new_perm, info)?;
862-
Ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
862+
interp_ok(ImmTy::from_immediate(new_place.to_ref(this), val.layout))
863863
}
864864
}
865865

@@ -917,7 +917,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
917917
in_field: self.in_field,
918918
})?;
919919
self.ecx.write_immediate(*val, place)?;
920-
Ok(())
920+
interp_ok(())
921921
}
922922
}
923923
impl<'ecx, 'tcx> ValueVisitor<'tcx, MiriMachine<'tcx>> for RetagVisitor<'ecx, 'tcx> {
@@ -935,7 +935,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
935935
let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx);
936936
self.retag_ptr_inplace(place, new_perm)?;
937937
}
938-
Ok(())
938+
interp_ok(())
939939
}
940940

941941
fn visit_value(&mut self, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
@@ -944,7 +944,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
944944
// This optimization is crucial for ZSTs, because they can contain way more fields
945945
// than we can ever visit.
946946
if place.layout.is_sized() && place.layout.size < self.ecx.pointer_size() {
947-
return Ok(());
947+
return interp_ok(());
948948
}
949949

950950
// Check the type of this value to see what to do with it (retag, or recurse).
@@ -983,7 +983,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
983983
}
984984
}
985985

986-
Ok(())
986+
interp_ok(())
987987
}
988988
}
989989
}
@@ -1028,7 +1028,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10281028
// No stacked borrows on these allocations.
10291029
}
10301030
}
1031-
Ok(())
1031+
interp_ok(())
10321032
}
10331033

10341034
fn print_stacks(&mut self, alloc_id: AllocId) -> InterpResult<'tcx> {
@@ -1046,6 +1046,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10461046
}
10471047
println!(" ]");
10481048
}
1049-
Ok(())
1049+
interp_ok(())
10501050
}
10511051
}

0 commit comments

Comments
 (0)