Skip to content

Commit aa01891

Browse files
committed
Auto merge of #99420 - RalfJung:vtable, r=oli-obk
make vtable pointers entirely opaque This implements the scheme discussed in rust-lang/unsafe-code-guidelines#338: vtable pointers should be considered entirely opaque and not even readable by Rust code, similar to function pointers. - We have a new kind of `GlobalAlloc` that symbolically refers to a vtable. - Miri uses that kind of allocation when generating a vtable. - The codegen backends, upon encountering such an allocation, call `vtable_allocation` to obtain an actually dataful allocation for this vtable. - We need new intrinsics to obtain the size and align from a vtable (for some `ptr::metadata` APIs), since direct accesses are UB now. I had to touch quite a bit of code that I am not very familiar with, so some of this might not make much sense... r? `@oli-obk`
2 parents 31b9b01 + d46dfa2 commit aa01891

Some content is hidden

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

69 files changed

+673
-527
lines changed

compiler/rustc_codegen_cranelift/src/constant.rs

+27-10
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,8 @@ pub(crate) fn codegen_const_value<'tcx>(
195195
}
196196
Scalar::Ptr(ptr, _size) => {
197197
let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative
198-
let alloc_kind = fx.tcx.get_global_alloc(alloc_id);
199-
let base_addr = match alloc_kind {
200-
Some(GlobalAlloc::Memory(alloc)) => {
198+
let base_addr = match fx.tcx.global_alloc(alloc_id) {
199+
GlobalAlloc::Memory(alloc) => {
201200
let data_id = data_id_for_alloc_id(
202201
&mut fx.constants_cx,
203202
fx.module,
@@ -211,13 +210,27 @@ pub(crate) fn codegen_const_value<'tcx>(
211210
}
212211
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
213212
}
214-
Some(GlobalAlloc::Function(instance)) => {
213+
GlobalAlloc::Function(instance) => {
215214
let func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
216215
let local_func_id =
217216
fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
218217
fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
219218
}
220-
Some(GlobalAlloc::Static(def_id)) => {
219+
GlobalAlloc::VTable(ty, trait_ref) => {
220+
let alloc_id = fx.tcx.vtable_allocation((ty, trait_ref));
221+
let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
222+
// FIXME: factor this common code with the `Memory` arm into a function?
223+
let data_id = data_id_for_alloc_id(
224+
&mut fx.constants_cx,
225+
fx.module,
226+
alloc_id,
227+
alloc.inner().mutability,
228+
);
229+
let local_data_id =
230+
fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
231+
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
232+
}
233+
GlobalAlloc::Static(def_id) => {
221234
assert!(fx.tcx.is_static(def_id));
222235
let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
223236
let local_data_id =
@@ -227,7 +240,6 @@ pub(crate) fn codegen_const_value<'tcx>(
227240
}
228241
fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
229242
}
230-
None => bug!("missing allocation {:?}", alloc_id),
231243
};
232244
let val = if offset.bytes() != 0 {
233245
fx.bcx.ins().iadd_imm(base_addr, i64::try_from(offset.bytes()).unwrap())
@@ -357,10 +369,11 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
357369
while let Some(todo_item) = cx.todo.pop() {
358370
let (data_id, alloc, section_name) = match todo_item {
359371
TodoItem::Alloc(alloc_id) => {
360-
//println!("alloc_id {}", alloc_id);
361-
let alloc = match tcx.get_global_alloc(alloc_id).unwrap() {
372+
let alloc = match tcx.global_alloc(alloc_id) {
362373
GlobalAlloc::Memory(alloc) => alloc,
363-
GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(),
374+
GlobalAlloc::Function(_) | GlobalAlloc::Static(_) | GlobalAlloc::VTable(..) => {
375+
unreachable!()
376+
}
364377
};
365378
let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
366379
module
@@ -424,7 +437,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
424437
read_target_uint(endianness, bytes).unwrap()
425438
};
426439

427-
let reloc_target_alloc = tcx.get_global_alloc(alloc_id).unwrap();
440+
let reloc_target_alloc = tcx.global_alloc(alloc_id);
428441
let data_id = match reloc_target_alloc {
429442
GlobalAlloc::Function(instance) => {
430443
assert_eq!(addend, 0);
@@ -436,6 +449,10 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
436449
GlobalAlloc::Memory(target_alloc) => {
437450
data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability)
438451
}
452+
GlobalAlloc::VTable(ty, trait_ref) => {
453+
let alloc_id = tcx.vtable_allocation((ty, trait_ref));
454+
data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not)
455+
}
439456
GlobalAlloc::Static(def_id) => {
440457
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
441458
{

compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,16 @@ fn codegen_regular_intrinsic_call<'tcx>(
431431
ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
432432
};
433433

434+
vtable_size, (v vtable) {
435+
let size = crate::vtable::size_of_obj(fx, vtable);
436+
ret.write_cvalue(fx, CValue::by_val(size, usize_layout));
437+
};
438+
439+
vtable_align, (v vtable) {
440+
let align = crate::vtable::min_align_of_obj(fx, vtable);
441+
ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
442+
};
443+
434444
unchecked_add | unchecked_sub | unchecked_mul | unchecked_div | exact_div | unchecked_rem
435445
| unchecked_shl | unchecked_shr, (c x, c y) {
436446
// FIXME trap on overflow

compiler/rustc_codegen_gcc/src/common.rs

+5
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,11 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
201201
GlobalAlloc::Function(fn_instance) => {
202202
self.get_fn_addr(fn_instance)
203203
},
204+
GlobalAlloc::VTable(ty, trait_ref) => {
205+
let alloc = self.tcx.global_alloc(self.tcx.vtable_allocation((ty, trait_ref))).unwrap_memory();
206+
let init = const_alloc_to_gcc(self, alloc);
207+
self.static_addr_of(init, alloc.inner().align, None)
208+
}
204209
GlobalAlloc::Static(def_id) => {
205210
assert!(self.tcx.is_static(def_id));
206211
self.get_static(def_id).get_address(None)

compiler/rustc_codegen_llvm/src/common.rs

+9
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,15 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
257257
self.get_fn_addr(fn_instance.polymorphize(self.tcx)),
258258
self.data_layout().instruction_address_space,
259259
),
260+
GlobalAlloc::VTable(ty, trait_ref) => {
261+
let alloc = self
262+
.tcx
263+
.global_alloc(self.tcx.vtable_allocation((ty, trait_ref)))
264+
.unwrap_memory();
265+
let init = const_alloc_to_llvm(self, alloc);
266+
let value = self.static_addr_of(init, alloc.inner().align, None);
267+
(value, AddressSpace::DATA)
268+
}
260269
GlobalAlloc::Static(def_id) => {
261270
assert!(self.tcx.is_static(def_id));
262271
assert!(!self.tcx.is_thread_local_static(def_id));

compiler/rustc_codegen_llvm/src/consts.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ pub fn const_alloc_to_llvm<'ll>(cx: &CodegenCx<'ll, '_>, alloc: ConstAllocation<
101101

102102
let address_space = match cx.tcx.global_alloc(alloc_id) {
103103
GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
104-
GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) => AddressSpace::DATA,
104+
GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => {
105+
AddressSpace::DATA
106+
}
105107
};
106108

107109
llvals.push(cx.scalar_to_backend(

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1420,7 +1420,7 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
14201420
cx,
14211421
type_map::stub(
14221422
cx,
1423-
Stub::VtableTy { vtable_holder },
1423+
Stub::VTableTy { vtable_holder },
14241424
unique_type_id,
14251425
&vtable_type_name,
14261426
(size, pointer_align),

compiler/rustc_codegen_llvm/src/debuginfo/metadata/type_map.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ impl<'ll> DINodeCreationResult<'ll> {
146146
pub enum Stub<'ll> {
147147
Struct,
148148
Union,
149-
VtableTy { vtable_holder: &'ll DIType },
149+
VTableTy { vtable_holder: &'ll DIType },
150150
}
151151

152152
pub struct StubInfo<'ll, 'tcx> {
@@ -180,9 +180,9 @@ pub(super) fn stub<'ll, 'tcx>(
180180
let unique_type_id_str = unique_type_id.generate_unique_id_string(cx.tcx);
181181

182182
let metadata = match kind {
183-
Stub::Struct | Stub::VtableTy { .. } => {
183+
Stub::Struct | Stub::VTableTy { .. } => {
184184
let vtable_holder = match kind {
185-
Stub::VtableTy { vtable_holder } => Some(vtable_holder),
185+
Stub::VTableTy { vtable_holder } => Some(vtable_holder),
186186
_ => None,
187187
};
188188
unsafe {

compiler/rustc_codegen_ssa/src/base.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ pub fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
171171
);
172172
let new_vptr = bx.load(ptr_ty, gep, ptr_align);
173173
bx.nonnull_metadata(new_vptr);
174-
// Vtable loads are invariant.
174+
// VTable loads are invariant.
175175
bx.set_invariant_load(new_vptr);
176176
new_vptr
177177
} else {

compiler/rustc_codegen_ssa/src/meth.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ impl<'a, 'tcx> VirtualIndex {
3939
let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
4040
let ptr = bx.load(llty, gep, ptr_align);
4141
bx.nonnull_metadata(ptr);
42-
// Vtable loads are invariant.
42+
// VTable loads are invariant.
4343
bx.set_invariant_load(ptr);
4444
ptr
4545
}
@@ -58,7 +58,7 @@ impl<'a, 'tcx> VirtualIndex {
5858
let usize_align = bx.tcx().data_layout.pointer_align.abi;
5959
let gep = bx.inbounds_gep(llty, llvtable, &[bx.const_usize(self.0)]);
6060
let ptr = bx.load(llty, gep, usize_align);
61-
// Vtable loads are invariant.
61+
// VTable loads are invariant.
6262
bx.set_invariant_load(ptr);
6363
ptr
6464
}

compiler/rustc_codegen_ssa/src/mir/intrinsic.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@ use super::place::PlaceRef;
33
use super::FunctionCx;
44
use crate::common::{span_invalid_monomorphization_error, IntPredicate};
55
use crate::glue;
6+
use crate::meth;
67
use crate::traits::*;
78
use crate::MemFlags;
89

910
use rustc_middle::ty::{self, Ty, TyCtxt};
1011
use rustc_span::{sym, Span};
11-
use rustc_target::abi::call::{FnAbi, PassMode};
12+
use rustc_target::abi::{
13+
call::{FnAbi, PassMode},
14+
WrappingRange,
15+
};
1216

1317
fn copy_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
1418
bx: &mut Bx,
@@ -102,6 +106,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
102106
bx.const_usize(bx.layout_of(tp_ty).align.abi.bytes())
103107
}
104108
}
109+
sym::vtable_size | sym::vtable_align => {
110+
let vtable = args[0].immediate();
111+
let idx = match name {
112+
sym::vtable_size => ty::COMMON_VTABLE_ENTRIES_SIZE,
113+
sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN,
114+
_ => bug!(),
115+
};
116+
let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable);
117+
if name == sym::vtable_align {
118+
// Alignment is always nonzero.
119+
bx.range_metadata(value, WrappingRange { start: 1, end: !0 });
120+
};
121+
value
122+
}
105123
sym::pref_align_of
106124
| sym::needs_drop
107125
| sym::type_id

compiler/rustc_const_eval/src/const_eval/machine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
369369
// we don't deallocate it.
370370
let (alloc_id, _, _) = ecx.ptr_get_alloc_id(ptr)?;
371371
let is_allocated_in_another_const = matches!(
372-
ecx.tcx.get_global_alloc(alloc_id),
372+
ecx.tcx.try_get_global_alloc(alloc_id),
373373
Some(interpret::GlobalAlloc::Memory(_))
374374
);
375375

compiler/rustc_const_eval/src/const_eval/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ pub(crate) fn deref_mir_constant<'tcx>(
138138
let mplace = ecx.deref_operand(&op).unwrap();
139139
if let Some(alloc_id) = mplace.ptr.provenance {
140140
assert_eq!(
141-
tcx.get_global_alloc(alloc_id).unwrap().unwrap_memory().0.0.mutability,
141+
tcx.global_alloc(alloc_id).unwrap_memory().0.0.mutability,
142142
Mutability::Not,
143143
"deref_mir_constant cannot be used with mutable allocations as \
144144
that could allow pattern matching to observe mutable statics",

compiler/rustc_const_eval/src/interpret/cast.rs

+8-20
Original file line numberDiff line numberDiff line change
@@ -298,30 +298,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
298298
self.write_immediate(val, dest)
299299
}
300300
(&ty::Dynamic(ref data_a, ..), &ty::Dynamic(ref data_b, ..)) => {
301-
let val = self.read_immediate(src)?;
302-
if data_a.principal_def_id() == data_b.principal_def_id() {
303-
return self.write_immediate(*val, dest);
304-
}
305-
// trait upcasting coercion
306-
let vptr_entry_idx = self.tcx.vtable_trait_upcasting_coercion_new_vptr_slot((
307-
src_pointee_ty,
308-
dest_pointee_ty,
309-
));
310-
311-
if let Some(entry_idx) = vptr_entry_idx {
312-
let entry_idx = u64::try_from(entry_idx).unwrap();
313-
let (old_data, old_vptr) = val.to_scalar_pair()?;
314-
let old_vptr = self.scalar_to_ptr(old_vptr)?;
315-
let new_vptr = self
316-
.read_new_vtable_after_trait_upcasting_from_vtable(old_vptr, entry_idx)?;
317-
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
318-
} else {
319-
self.write_immediate(*val, dest)
301+
let (old_data, old_vptr) = self.read_immediate(src)?.to_scalar_pair()?;
302+
let old_vptr = self.scalar_to_ptr(old_vptr)?;
303+
let (ty, old_trait) = self.get_ptr_vtable(old_vptr)?;
304+
if old_trait != data_a.principal() {
305+
throw_ub_format!("upcast on a pointer whose vtable does not match its type");
320306
}
307+
let new_vptr = self.get_vtable_ptr(ty, data_b.principal())?;
308+
self.write_immediate(Immediate::new_dyn_trait(old_data, new_vptr, self), dest)
321309
}
322310
(_, &ty::Dynamic(ref data, _)) => {
323311
// Initial cast from sized to dyn trait
324-
let vtable = self.get_vtable(src_pointee_ty, data.principal())?;
312+
let vtable = self.get_vtable_ptr(src_pointee_ty, data.principal())?;
325313
let ptr = self.read_immediate(src)?.to_scalar()?;
326314
let val = Immediate::new_dyn_trait(ptr, vtable, &*self.tcx);
327315
self.write_immediate(val, dest)

compiler/rustc_const_eval/src/interpret/eval_context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
631631
ty::Dynamic(..) => {
632632
let vtable = self.scalar_to_ptr(metadata.unwrap_meta())?;
633633
// Read size and align from vtable (already checks size).
634-
Ok(Some(self.read_size_and_align_from_vtable(vtable)?))
634+
Ok(Some(self.get_vtable_size_and_align(vtable)?))
635635
}
636636

637637
ty::Slice(_) | ty::Str => {

compiler/rustc_const_eval/src/interpret/intern.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ fn intern_shallow<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx, const_eval:
9494
// to validation to error -- it has the much better error messages, pointing out where
9595
// in the value the dangling reference lies.
9696
// The `delay_span_bug` ensures that we don't forget such a check in validation.
97-
if tcx.get_global_alloc(alloc_id).is_none() {
97+
if tcx.try_get_global_alloc(alloc_id).is_none() {
9898
tcx.sess.delay_span_bug(ecx.tcx.span, "tried to intern dangling pointer");
9999
}
100100
// treat dangling pointers like other statics
@@ -454,7 +454,7 @@ pub fn intern_const_alloc_recursive<
454454
.sess
455455
.span_err(ecx.tcx.span, "encountered dangling pointer in final constant");
456456
return Err(reported);
457-
} else if ecx.tcx.get_global_alloc(alloc_id).is_none() {
457+
} else if ecx.tcx.try_get_global_alloc(alloc_id).is_none() {
458458
// We have hit an `AllocId` that is neither in local or global memory and isn't
459459
// marked as dangling by local memory. That should be impossible.
460460
span_bug!(ecx.tcx.span, "encountered unknown alloc id {:?}", alloc_id);

compiler/rustc_const_eval/src/interpret/intrinsics.rs

+12
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
492492
let result = self.raw_eq_intrinsic(&args[0], &args[1])?;
493493
self.write_scalar(result, dest)?;
494494
}
495+
496+
sym::vtable_size => {
497+
let ptr = self.read_pointer(&args[0])?;
498+
let (size, _align) = self.get_vtable_size_and_align(ptr)?;
499+
self.write_scalar(Scalar::from_machine_usize(size.bytes(), self), dest)?;
500+
}
501+
sym::vtable_align => {
502+
let ptr = self.read_pointer(&args[0])?;
503+
let (_size, align) = self.get_vtable_size_and_align(ptr)?;
504+
self.write_scalar(Scalar::from_machine_usize(align.bytes(), self), dest)?;
505+
}
506+
495507
_ => return Ok(false),
496508
}
497509

0 commit comments

Comments
 (0)