Skip to content

Commit 6d4eaab

Browse files
committed
adjust for symbolic vtables
1 parent 36a7a65 commit 6d4eaab

File tree

11 files changed

+120
-80
lines changed

11 files changed

+120
-80
lines changed

src/intptrcast.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,24 +86,31 @@ impl<'mir, 'tcx> GlobalStateInner {
8686
if global_state.exposed.contains(&alloc_id) {
8787
let (_size, _align, kind) = ecx.get_alloc_info(alloc_id);
8888
match kind {
89-
AllocKind::LiveData | AllocKind::Function => return Some(alloc_id),
89+
AllocKind::LiveData | AllocKind::Function | AllocKind::Vtable => {
90+
return Some(alloc_id);
91+
}
9092
AllocKind::Dead => {}
9193
}
9294
}
9395

9496
None
9597
}
9698

97-
pub fn expose_ptr(ecx: &mut MiriEvalContext<'mir, 'tcx>, alloc_id: AllocId, sb: SbTag) {
99+
pub fn expose_ptr(
100+
ecx: &mut MiriEvalContext<'mir, 'tcx>,
101+
alloc_id: AllocId,
102+
sb: SbTag,
103+
) -> InterpResult<'tcx> {
98104
let global_state = ecx.machine.intptrcast.get_mut();
99105
// In strict mode, we don't need this, so we can save some cycles by not tracking it.
100106
if global_state.provenance_mode != ProvenanceMode::Strict {
101107
trace!("Exposing allocation id {alloc_id:?}");
102108
global_state.exposed.insert(alloc_id);
103109
if ecx.machine.stacked_borrows.is_some() {
104-
ecx.expose_tag(alloc_id, sb);
110+
ecx.expose_tag(alloc_id, sb)?;
105111
}
106112
}
113+
Ok(())
107114
}
108115

109116
pub fn ptr_from_addr_transmute(
@@ -188,8 +195,8 @@ impl<'mir, 'tcx> GlobalStateInner {
188195

189196
// Remember next base address. If this allocation is zero-sized, leave a gap
190197
// of at least 1 to avoid two allocations having the same base address.
191-
// (The logic in `alloc_id_from_addr` assumes unique addresses, and function
192-
// pointers to different functions need to be distinguishable!)
198+
// (The logic in `alloc_id_from_addr` assumes unique addresses, and different
199+
// function/vtable pointers need to be distinguishable!)
193200
global_state.next_base_addr = base_addr.checked_add(max(size.bytes(), 1)).unwrap();
194201
// Given that `next_base_addr` increases in each allocation, pushing the
195202
// corresponding tuple keeps `int_to_ptr_map` sorted

src/machine.rs

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -620,12 +620,35 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
620620
) -> InterpResult<'tcx, Pointer<Tag>> {
621621
let link_name = ecx.item_link_name(def_id);
622622
if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) {
623+
// Various parts of the engine rely on `get_alloc_info` for size and alignment
624+
// information. That uses the type information of this static.
625+
// Make sure it matches the Miri allocation for this.
626+
let Tag::Concrete { alloc_id, .. } = ptr.provenance else {
627+
panic!("extern_statics cannot contain wildcards")
628+
};
629+
let (shim_size, shim_align, _kind) = ecx.get_alloc_info(alloc_id);
630+
let extern_decl_layout =
631+
ecx.tcx.layout_of(ty::ParamEnv::empty().and(ecx.tcx.type_of(def_id))).unwrap();
632+
if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align {
633+
throw_unsup_format!(
634+
"`extern` static `{name}` from crate `{krate}` has been declared \
635+
with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
636+
but Miri emulates it via an extern static shim \
637+
with a size of {shim_size} bytes and alignment of {shim_align} byes",
638+
name = ecx.tcx.def_path_str(def_id),
639+
krate = ecx.tcx.crate_name(def_id.krate),
640+
decl_size = extern_decl_layout.size.bytes(),
641+
decl_align = extern_decl_layout.align.abi.bytes(),
642+
shim_size = shim_size.bytes(),
643+
shim_align = shim_align.bytes(),
644+
)
645+
}
623646
Ok(ptr)
624647
} else {
625648
throw_unsup_format!(
626-
"`extern` static `{}` from crate `{}` is not supported by Miri",
627-
ecx.tcx.def_path_str(def_id),
628-
ecx.tcx.crate_name(def_id.krate),
649+
"`extern` static `{name}` from crate `{krate}` is not supported by Miri",
650+
name = ecx.tcx.def_path_str(def_id),
651+
krate = ecx.tcx.crate_name(def_id.krate),
629652
)
630653
}
631654
}
@@ -692,7 +715,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
692715
if cfg!(debug_assertions) {
693716
// The machine promises to never call us on thread-local or extern statics.
694717
let alloc_id = ptr.provenance;
695-
match ecx.tcx.get_global_alloc(alloc_id) {
718+
match ecx.tcx.try_get_global_alloc(alloc_id) {
696719
Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
697720
panic!("tag_alloc_base_pointer called on thread-local static")
698721
}
@@ -737,14 +760,14 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
737760
) -> InterpResult<'tcx> {
738761
match ptr.provenance {
739762
Tag::Concrete { alloc_id, sb } => {
740-
intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb);
763+
intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb)
741764
}
742765
Tag::Wildcard => {
743766
// No need to do anything for wildcard pointers as
744767
// their provenances have already been previously exposed.
768+
Ok(())
745769
}
746770
}
747-
Ok(())
748771
}
749772

750773
/// Convert a pointer with provenance into an allocation-offset pair,

src/shims/backtrace.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,14 +122,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
122122
let this = self.eval_context_mut();
123123

124124
let ptr = this.read_pointer(ptr)?;
125-
// Take apart the pointer, we need its pieces.
125+
// Take apart the pointer, we need its pieces. The offset encodes the span.
126126
let (alloc_id, offset, _tag) = this.ptr_get_alloc_id(ptr)?;
127127

128+
// This has to be an actual global fn ptr, not a dlsym function.
128129
let fn_instance =
129-
if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(alloc_id) {
130+
if let Some(GlobalAlloc::Function(instance)) = this.tcx.try_get_global_alloc(alloc_id) {
130131
instance
131132
} else {
132-
throw_ub_format!("expected function pointer, found {:?}", ptr);
133+
throw_ub_format!("expected static function pointer, found {:?}", ptr);
133134
};
134135

135136
let lo =

src/stacked_borrows/mod.rs

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
783783
let log_creation = |this: &MiriEvalContext<'mir, 'tcx>,
784784
current_span: &mut CurrentSpan<'_, 'mir, 'tcx>,
785785
loc: Option<(AllocId, Size, SbTagExtra)>| // alloc_id, base_offset, orig_tag
786-
-> InterpResult<'tcx> {
786+
-> InterpResult<'tcx> {
787787
let global = this.machine.stacked_borrows.as_ref().unwrap().borrow();
788788
if global.tracked_pointer_tags.contains(&new_tag) {
789789
register_diagnostic(NonHaltingDiagnostic::CreatedPointerTag(
@@ -794,29 +794,40 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
794794
drop(global); // don't hold that reference any longer than we have to
795795

796796
let Some((alloc_id, base_offset, orig_tag)) = loc else {
797-
return Ok(())
797+
return Ok(());
798798
};
799799

800800
// The SB history tracking needs a parent tag, so skip if we come from a wildcard.
801801
let SbTagExtra::Concrete(orig_tag) = orig_tag else {
802802
// FIXME: should we log this?
803-
return Ok(())
803+
return Ok(());
804804
};
805805

806-
let extra = this.get_alloc_extra(alloc_id)?;
807-
let mut stacked_borrows = extra
808-
.stacked_borrows
809-
.as_ref()
810-
.expect("we should have Stacked Borrows data")
811-
.borrow_mut();
812-
stacked_borrows.history.log_creation(
813-
Some(orig_tag),
814-
new_tag,
815-
alloc_range(base_offset, size),
816-
current_span,
817-
);
818-
if protect {
819-
stacked_borrows.history.log_protector(orig_tag, new_tag, current_span);
806+
let (_size, _align, kind) = this.get_alloc_info(alloc_id);
807+
match kind {
808+
AllocKind::LiveData => {
809+
// This should have alloc_extra data, but `get_alloc_extra` can still fail
810+
// if converting this alloc_id from a global to a local one
811+
// uncovers a non-supported `extern static`.
812+
let extra = this.get_alloc_extra(alloc_id)?;
813+
let mut stacked_borrows = extra
814+
.stacked_borrows
815+
.as_ref()
816+
.expect("we should have Stacked Borrows data")
817+
.borrow_mut();
818+
stacked_borrows.history.log_creation(
819+
Some(orig_tag),
820+
new_tag,
821+
alloc_range(base_offset, size),
822+
current_span,
823+
);
824+
if protect {
825+
stacked_borrows.history.log_protector(orig_tag, new_tag, current_span);
826+
}
827+
}
828+
AllocKind::Function | AllocKind::Vtable | AllocKind::Dead => {
829+
// No stacked borrows on these allocations.
830+
}
820831
}
821832
Ok(())
822833
};
@@ -1031,16 +1042,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
10311042
fn qualify(ty: ty::Ty<'_>, kind: RetagKind) -> Option<(RefKind, bool)> {
10321043
match ty.kind() {
10331044
// References are simple.
1034-
ty::Ref(_, _, Mutability::Mut) =>
1035-
Some((
1036-
RefKind::Unique { two_phase: kind == RetagKind::TwoPhase },
1037-
kind == RetagKind::FnEntry,
1038-
)),
1039-
ty::Ref(_, _, Mutability::Not) =>
1040-
Some((RefKind::Shared, kind == RetagKind::FnEntry)),
1045+
ty::Ref(_, _, Mutability::Mut) => Some((
1046+
RefKind::Unique { two_phase: kind == RetagKind::TwoPhase },
1047+
kind == RetagKind::FnEntry,
1048+
)),
1049+
ty::Ref(_, _, Mutability::Not) => {
1050+
Some((RefKind::Shared, kind == RetagKind::FnEntry))
1051+
}
10411052
// Raw pointers need to be enabled.
1042-
ty::RawPtr(tym) if kind == RetagKind::Raw =>
1043-
Some((RefKind::Raw { mutable: tym.mutbl == Mutability::Mut }, false)),
1053+
ty::RawPtr(tym) if kind == RetagKind::Raw => {
1054+
Some((RefKind::Raw { mutable: tym.mutbl == Mutability::Mut }, false))
1055+
}
10441056
// Boxes are handled separately due to that allocator situation,
10451057
// see the visitor below.
10461058
_ => None,
@@ -1142,7 +1154,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11421154
}
11431155

11441156
/// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
1145-
fn expose_tag(&mut self, alloc_id: AllocId, tag: SbTag) {
1157+
fn expose_tag(&mut self, alloc_id: AllocId, tag: SbTag) -> InterpResult<'tcx> {
11461158
let this = self.eval_context_mut();
11471159

11481160
// Function pointers and dead objects don't have an alloc_extra so we ignore them.
@@ -1151,14 +1163,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
11511163
let (_size, _align, kind) = this.get_alloc_info(alloc_id);
11521164
match kind {
11531165
AllocKind::LiveData => {
1154-
// This should have alloc_extra data.
1155-
let alloc_extra = this.get_alloc_extra(alloc_id).unwrap();
1166+
// This should have alloc_extra data, but `get_alloc_extra` can still fail
1167+
// if converting this alloc_id from a global to a local one
1168+
// uncovers a non-supported `extern static`.
1169+
let alloc_extra = this.get_alloc_extra(alloc_id)?;
11561170
trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
11571171
alloc_extra.stacked_borrows.as_ref().unwrap().borrow_mut().exposed_tags.insert(tag);
11581172
}
1159-
AllocKind::Function | AllocKind::Dead => {
1173+
AllocKind::Function | AllocKind::Vtable | AllocKind::Dead => {
11601174
// No stacked borrows on these allocations.
11611175
}
11621176
}
1177+
Ok(())
11631178
}
11641179
}

tests/fail/issue-miri-1112.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ impl FunnyPointer {
2828
data: data as *const _ as *const (),
2929
vtable: ptr as *const _ as *const (),
3030
};
31-
let obj = std::mem::transmute::<FatPointer, *mut FunnyPointer>(obj); //~ ERROR: invalid drop function pointer in vtable
31+
let obj = std::mem::transmute::<FatPointer, *mut FunnyPointer>(obj); //~ ERROR: expected a vtable pointer
3232
&*obj
3333
}
3434
}

tests/fail/issue-miri-1112.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: constructing invalid value: encountered invalid drop function pointer in vtable (function has incompatible signature)
1+
error: Undefined Behavior: constructing invalid value: encountered $HEX[ALLOC]<TAG>, but expected a vtable pointer
22
--> $DIR/issue-miri-1112.rs:LL:CC
33
|
44
LL | let obj = std::mem::transmute::<FatPointer, *mut FunnyPointer>(obj);
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered invalid drop function pointer in vtable (function has incompatible signature)
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered $HEX[ALLOC]<TAG>, but expected a vtable pointer
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

tests/fail/stacked_borrows/vtable.stderr

Lines changed: 0 additions & 25 deletions
This file was deleted.

tests/fail/validity/invalid_wide_raw.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ fn main() {
77
#[allow(dead_code)]
88
x: *mut dyn T,
99
}
10-
dbg!(S { x: unsafe { std::mem::transmute((0usize, 0usize)) } }); //~ ERROR: encountered dangling vtable pointer in wide pointer
10+
dbg!(S { x: unsafe { std::mem::transmute((0usize, 0usize)) } }); //~ ERROR: encountered null pointer, but expected a vtable pointer
1111
}

tests/fail/validity/invalid_wide_raw.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
error: Undefined Behavior: constructing invalid value: encountered dangling vtable pointer in wide pointer
1+
error: Undefined Behavior: constructing invalid value: encountered null pointer, but expected a vtable pointer
22
--> $DIR/invalid_wide_raw.rs:LL:CC
33
|
44
LL | dbg!(S { x: unsafe { std::mem::transmute((0usize, 0usize)) } });
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered dangling vtable pointer in wide pointer
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ constructing invalid value: encountered null pointer, but expected a vtable pointer
66
|
77
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
88
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
//@error-pattern: vtable pointer does not have permission
2-
#![feature(ptr_metadata)]
1+
#![feature(ptr_metadata, layout_for_ptr)]
2+
3+
use std::{mem, ptr};
34

45
trait Foo {}
56

67
impl Foo for u32 {}
78

89
fn uwu(thin: *const (), meta: &'static ()) -> *const dyn Foo {
9-
core::ptr::from_raw_parts(thin, unsafe { core::mem::transmute(meta) })
10+
ptr::from_raw_parts(thin, unsafe { mem::transmute(meta) })
1011
}
1112

1213
fn main() {
1314
unsafe {
1415
let orig = 1_u32;
1516
let x = &orig as &dyn Foo;
1617
let (ptr, meta) = (x as *const dyn Foo).to_raw_parts();
17-
let _ = uwu(ptr, core::mem::transmute(meta));
18+
let ptr = uwu(ptr, mem::transmute(meta));
19+
mem::size_of_val_raw(ptr);
1820
}
1921
}

tests/pass/pointers.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::mem::transmute;
1+
#![feature(ptr_metadata)]
2+
3+
use std::mem::{self, transmute};
4+
use std::ptr;
25

36
fn one_line_ref() -> i16 {
47
*&1
@@ -71,6 +74,19 @@ fn wide_ptr_ops() {
7174
assert!(!(a > b));
7275
}
7376

77+
fn metadata_vtable() {
78+
let p = &0i32 as &dyn std::fmt::Debug;
79+
let meta: ptr::DynMetadata<_> = ptr::metadata(p as *const _);
80+
assert_eq!(meta.size_of(), mem::size_of::<i32>());
81+
assert_eq!(meta.align_of(), mem::align_of::<i32>());
82+
83+
type T = [i32; 16];
84+
let p = &T::default() as &dyn std::fmt::Debug;
85+
let meta: ptr::DynMetadata<_> = ptr::metadata(p as *const _);
86+
assert_eq!(meta.size_of(), mem::size_of::<T>());
87+
assert_eq!(meta.align_of(), mem::align_of::<T>());
88+
}
89+
7490
fn main() {
7591
assert_eq!(one_line_ref(), 1);
7692
assert_eq!(basic_ref(), 1);
@@ -116,4 +132,5 @@ fn main() {
116132
assert!(dangling >= 4);
117133

118134
wide_ptr_ops();
135+
metadata_vtable();
119136
}

0 commit comments

Comments
 (0)