Skip to content

Commit bd5fce6

Browse files
committed
Rustc changes for permissive provenance
1 parent 0034bbc commit bd5fce6

File tree

12 files changed

+142
-45
lines changed

12 files changed

+142
-45
lines changed

compiler/rustc_const_eval/src/const_eval/machine.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ use rustc_target::spec::abi::Abi;
1818

1919
use crate::interpret::{
2020
self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
21-
OpTy, PlaceTy, Scalar, StackPopUnwind,
21+
OpTy, PlaceTy, Pointer, Scalar, StackPopUnwind,
2222
};
2323

2424
use super::error::*;
@@ -443,6 +443,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
443443
Ok(())
444444
}
445445

446+
#[inline(always)]
447+
fn expose_ptr(
448+
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
449+
_ptr: Pointer<AllocId>,
450+
) -> InterpResult<'tcx> {
451+
Err(ConstEvalErrKind::NeedsRfc("exposing pointers".to_string()).into())
452+
}
453+
446454
#[inline(always)]
447455
fn init_frame_extra(
448456
ecx: &mut InterpCx<'mir, 'tcx, Self>,

compiler/rustc_const_eval/src/interpret/cast.rs

+44-15
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
9898
}
9999

100100
pub fn misc_cast(
101-
&self,
101+
&mut self,
102102
src: &ImmTy<'tcx, M::PointerTag>,
103103
cast_ty: Ty<'tcx>,
104104
) -> InterpResult<'tcx, Immediate<M::PointerTag>> {
@@ -139,7 +139,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
139139
if let Some(discr) = src.layout.ty.discriminant_for_variant(*self.tcx, index) {
140140
assert!(src.layout.is_zst());
141141
let discr_layout = self.layout_of(discr.ty)?;
142-
return Ok(self.cast_from_int_like(discr.val, discr_layout, cast_ty).into());
142+
143+
let scalar = Scalar::from_uint(discr.val, discr_layout.layout.size());
144+
return Ok(self.cast_from_int_like(scalar, discr_layout, cast_ty)?.into());
143145
}
144146
}
145147
Variants::Multiple { .. } => {}
@@ -170,38 +172,65 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
170172
}
171173

172174
// # The remaining source values are scalar and "int-like".
175+
let scalar = src.to_scalar()?;
176+
177+
// If we are casting from a pointer to something
178+
// that is not a pointer, mark the pointer as exposed
179+
if src.layout.ty.is_any_ptr() && !cast_ty.is_any_ptr() {
180+
let ptr = self.scalar_to_ptr(scalar)?;
181+
182+
match ptr.into_pointer_or_addr() {
183+
Ok(ptr) => {
184+
M::expose_ptr(self, ptr)?;
185+
}
186+
Err(_) => {
187+
// do nothing, exposing an invalid pointer
188+
// has no meaning
189+
}
190+
};
191+
}
173192

174-
// For all remaining casts, we either
175-
// (a) cast a raw ptr to usize, or
176-
// (b) cast from an integer-like (including bool, char, enums).
177-
// In both cases we want the bits.
178-
let bits = src.to_scalar()?.to_bits(src.layout.size)?;
179-
Ok(self.cast_from_int_like(bits, src.layout, cast_ty).into())
193+
Ok(self.cast_from_int_like(scalar, src.layout, cast_ty)?.into())
180194
}
181195

182-
fn cast_from_int_like(
196+
pub fn cast_from_int_like(
183197
&self,
184-
v: u128, // raw bits (there is no ScalarTy so we separate data+layout)
198+
scalar: Scalar<M::PointerTag>, // input value (there is no ScalarTy so we separate data+layout)
185199
src_layout: TyAndLayout<'tcx>,
186200
cast_ty: Ty<'tcx>,
187-
) -> Scalar<M::PointerTag> {
201+
) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
188202
// Let's make sure v is sign-extended *if* it has a signed type.
189203
let signed = src_layout.abi.is_signed(); // Also asserts that abi is `Scalar`.
204+
205+
let v = scalar.to_bits(src_layout.size)?;
190206
let v = if signed { self.sign_extend(v, src_layout) } else { v };
191207
trace!("cast_from_scalar: {}, {} -> {}", v, src_layout.ty, cast_ty);
192208
use rustc_middle::ty::TyKind::*;
193-
match *cast_ty.kind() {
194-
Int(_) | Uint(_) | RawPtr(_) => {
209+
210+
Ok(match *cast_ty.kind() {
211+
Int(_) | Uint(_) => {
195212
let size = match *cast_ty.kind() {
196213
Int(t) => Integer::from_int_ty(self, t).size(),
197214
Uint(t) => Integer::from_uint_ty(self, t).size(),
198-
RawPtr(_) => self.pointer_size(),
199215
_ => bug!(),
200216
};
201217
let v = size.truncate(v);
202218
Scalar::from_uint(v, size)
203219
}
204220

221+
RawPtr(_) => {
222+
assert!(src_layout.ty.is_integral());
223+
224+
let size = self.pointer_size();
225+
let addr = u64::try_from(size.truncate(v)).unwrap();
226+
227+
let ptr = M::ptr_from_addr_cast(&self, addr);
228+
if addr == 0 {
229+
assert!(ptr.provenance.is_none(), "null pointer can never have an AllocId");
230+
}
231+
Scalar::from_maybe_pointer(ptr, self)
232+
}
233+
205234
Float(FloatTy::F32) if signed => Scalar::from_f32(Single::from_i128(v as i128).value),
206235
Float(FloatTy::F64) if signed => Scalar::from_f64(Double::from_i128(v as i128).value),
207236
Float(FloatTy::F32) => Scalar::from_f32(Single::from_u128(v).value),
@@ -214,7 +243,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
214243

215244
// Casts to bool are not permitted by rustc, no need to handle them here.
216245
_ => span_bug!(self.cur_span(), "invalid int to {:?} cast", cast_ty),
217-
}
246+
})
218247
}
219248

220249
fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::PointerTag>

compiler/rustc_const_eval/src/interpret/eval_context.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -905,7 +905,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
905905
trace!(
906906
"deallocating local {:?}: {:?}",
907907
local,
908-
self.dump_alloc(ptr.provenance.unwrap().get_alloc_id())
908+
// Locals always have a `alloc_id` (they are never the result of a int2ptr).
909+
self.dump_alloc(ptr.provenance.unwrap().get_alloc_id().unwrap())
909910
);
910911
self.deallocate_ptr(ptr, None, MemoryKind::Stack)?;
911912
};
@@ -1013,9 +1014,13 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> std::fmt::Debug
10131014
}
10141015
}
10151016

1016-
write!(fmt, ": {:?}", self.ecx.dump_allocs(allocs))
1017+
write!(
1018+
fmt,
1019+
": {:?}",
1020+
self.ecx.dump_allocs(allocs.into_iter().filter_map(|x| x).collect())
1021+
)
10171022
}
1018-
Place::Ptr(mplace) => match mplace.ptr.provenance.map(Provenance::get_alloc_id) {
1023+
Place::Ptr(mplace) => match mplace.ptr.provenance.and_then(Provenance::get_alloc_id) {
10191024
Some(alloc_id) => write!(
10201025
fmt,
10211026
"by align({}) ref {:?}: {:?}",

compiler/rustc_const_eval/src/interpret/machine.rs

+33-5
Original file line numberDiff line numberDiff line change
@@ -286,19 +286,36 @@ pub trait Machine<'mir, 'tcx>: Sized {
286286
) -> Pointer<Self::PointerTag>;
287287

288288
/// "Int-to-pointer cast"
289-
fn ptr_from_addr(
289+
fn ptr_from_addr_cast(
290290
ecx: &InterpCx<'mir, 'tcx, Self>,
291291
addr: u64,
292292
) -> Pointer<Option<Self::PointerTag>>;
293293

294+
// FIXME: Transmuting an integer to a pointer should just always return a `None`
295+
// provenance, but that causes problems with function pointers in Miri.
296+
/// Hook for returning a pointer from a transmute-like operation on an addr.
297+
fn ptr_from_addr_transmute(
298+
ecx: &InterpCx<'mir, 'tcx, Self>,
299+
addr: u64,
300+
) -> Pointer<Option<Self::PointerTag>>;
301+
302+
/// Marks a pointer as exposed, allowing it's provenance
303+
/// to be recovered. "Pointer-to-int cast"
304+
fn expose_ptr(
305+
ecx: &mut InterpCx<'mir, 'tcx, Self>,
306+
ptr: Pointer<Self::PointerTag>,
307+
) -> InterpResult<'tcx>;
308+
294309
/// Convert a pointer with provenance into an allocation-offset pair
295310
/// and extra provenance info.
296311
///
297312
/// The returned `AllocId` must be the same as `ptr.provenance.get_alloc_id()`.
313+
///
314+
/// When this fails, that means the pointer does not point to a live allocation.
298315
fn ptr_get_alloc(
299316
ecx: &InterpCx<'mir, 'tcx, Self>,
300317
ptr: Pointer<Self::PointerTag>,
301-
) -> (AllocId, Size, Self::TagExtra);
318+
) -> Option<(AllocId, Size, Self::TagExtra)>;
302319

303320
/// Called to initialize the "extra" state of an allocation and make the pointers
304321
/// it contains (in relocations) tagged. The way we construct allocations is
@@ -480,17 +497,28 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
480497
}
481498

482499
#[inline(always)]
483-
fn ptr_from_addr(_ecx: &InterpCx<$mir, $tcx, Self>, addr: u64) -> Pointer<Option<AllocId>> {
500+
fn ptr_from_addr_transmute(
501+
_ecx: &InterpCx<$mir, $tcx, Self>,
502+
addr: u64,
503+
) -> Pointer<Option<AllocId>> {
504+
Pointer::new(None, Size::from_bytes(addr))
505+
}
506+
507+
#[inline(always)]
508+
fn ptr_from_addr_cast(
509+
_ecx: &InterpCx<$mir, $tcx, Self>,
510+
addr: u64,
511+
) -> Pointer<Option<AllocId>> {
484512
Pointer::new(None, Size::from_bytes(addr))
485513
}
486514

487515
#[inline(always)]
488516
fn ptr_get_alloc(
489517
_ecx: &InterpCx<$mir, $tcx, Self>,
490518
ptr: Pointer<AllocId>,
491-
) -> (AllocId, Size, Self::TagExtra) {
519+
) -> Option<(AllocId, Size, Self::TagExtra)> {
492520
// We know `offset` is relative to the allocation, so we can use `into_parts`.
493521
let (alloc_id, offset) = ptr.into_parts();
494-
(alloc_id, offset, ())
522+
Some((alloc_id, offset, ()))
495523
}
496524
}

compiler/rustc_const_eval/src/interpret/memory.rs

+13-7
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
770770
if reachable.insert(id) {
771771
// This is a new allocation, add its relocations to `todo`.
772772
if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
773-
todo.extend(alloc.relocations().values().map(|tag| tag.get_alloc_id()));
773+
todo.extend(
774+
alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()),
775+
);
774776
}
775777
}
776778
}
@@ -805,7 +807,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
805807
allocs_to_print: &mut VecDeque<AllocId>,
806808
alloc: &Allocation<Tag, Extra>,
807809
) -> std::fmt::Result {
808-
for alloc_id in alloc.relocations().values().map(|tag| tag.get_alloc_id()) {
810+
for alloc_id in alloc.relocations().values().filter_map(|tag| tag.get_alloc_id()) {
809811
allocs_to_print.push_back(alloc_id);
810812
}
811813
write!(fmt, "{}", display_allocation(tcx, alloc))
@@ -1125,7 +1127,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
11251127
Err(ptr) => ptr.into(),
11261128
Ok(bits) => {
11271129
let addr = u64::try_from(bits).unwrap();
1128-
let ptr = M::ptr_from_addr(&self, addr);
1130+
let ptr = M::ptr_from_addr_transmute(&self, addr);
11291131
if addr == 0 {
11301132
assert!(ptr.provenance.is_none(), "null pointer can never have an AllocId");
11311133
}
@@ -1165,10 +1167,14 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
11651167
ptr: Pointer<Option<M::PointerTag>>,
11661168
) -> Result<(AllocId, Size, M::TagExtra), u64> {
11671169
match ptr.into_pointer_or_addr() {
1168-
Ok(ptr) => {
1169-
let (alloc_id, offset, extra) = M::ptr_get_alloc(self, ptr);
1170-
Ok((alloc_id, offset, extra))
1171-
}
1170+
Ok(ptr) => match M::ptr_get_alloc(self, ptr) {
1171+
Some((alloc_id, offset, extra)) => Ok((alloc_id, offset, extra)),
1172+
None => {
1173+
assert!(M::PointerTag::OFFSET_IS_ADDR);
1174+
let (_, addr) = ptr.into_parts();
1175+
Err(addr.bytes())
1176+
}
1177+
},
11721178
Err(addr) => Err(addr.bytes()),
11731179
}
11741180
}

compiler/rustc_const_eval/src/interpret/operand.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -695,17 +695,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
695695
// Figure out which discriminant and variant this corresponds to.
696696
Ok(match *tag_encoding {
697697
TagEncoding::Direct => {
698+
let scalar = tag_val.to_scalar()?;
698699
// Generate a specific error if `tag_val` is not an integer.
699700
// (`tag_bits` itself is only used for error messages below.)
700-
let tag_bits = tag_val
701-
.to_scalar()?
701+
let tag_bits = scalar
702702
.try_to_int()
703703
.map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))?
704704
.assert_bits(tag_layout.size);
705705
// Cast bits from tag layout to discriminant layout.
706-
// After the checks we did above, this cannot fail.
706+
// After the checks we did above, this cannot fail, as
707+
// discriminants are int-like.
707708
let discr_val =
708-
self.misc_cast(&tag_val, discr_layout.ty).unwrap().to_scalar().unwrap();
709+
self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap();
709710
let discr_bits = discr_val.assert_bits(discr_layout.size);
710711
// Convert discriminant to variant index, and catch invalid discriminants.
711712
let index = match *op.layout.ty.kind() {

compiler/rustc_middle/src/mir/interpret/pointer.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,11 @@ pub trait Provenance: Copy + fmt::Debug {
120120
where
121121
Self: Sized;
122122

123-
/// Provenance must always be able to identify the allocation this ptr points to.
123+
/// If `OFFSET_IS_ADDR == false`, provenance must always be able to
124+
/// identify the allocation this ptr points to (i.e., this must return `Some`).
125+
/// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`).
124126
/// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.)
125-
fn get_alloc_id(self) -> AllocId;
127+
fn get_alloc_id(self) -> Option<AllocId>;
126128
}
127129

128130
impl Provenance for AllocId {
@@ -147,8 +149,8 @@ impl Provenance for AllocId {
147149
Ok(())
148150
}
149151

150-
fn get_alloc_id(self) -> AllocId {
151-
self
152+
fn get_alloc_id(self) -> Option<AllocId> {
153+
Some(self)
152154
}
153155
}
154156

compiler/rustc_middle/src/mir/interpret/value.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,8 @@ impl<'tcx, Tag: Provenance> Scalar<Tag> {
344344
} else {
345345
// We know `offset` is relative, since `OFFSET_IS_ADDR == false`.
346346
let (tag, offset) = ptr.into_parts();
347-
Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id(), offset), sz))
347+
// Because `OFFSET_IS_ADDR == false`, this unwrap can never fail.
348+
Err(Scalar::Ptr(Pointer::new(tag.get_alloc_id().unwrap(), offset), sz))
348349
}
349350
}
350351
}

compiler/rustc_mir_transform/src/const_prop.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ use crate::MirPass;
2828
use rustc_const_eval::interpret::{
2929
self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame,
3030
ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, MemoryKind, OpTy,
31-
Operand as InterpOperand, PlaceTy, Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind,
31+
Operand as InterpOperand, PlaceTy, Pointer, Scalar, ScalarMaybeUninit, StackPopCleanup,
32+
StackPopUnwind,
3233
};
3334

3435
/// The maximum number of bytes that we'll allocate space for a local or the return value.
@@ -285,6 +286,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
285286
Ok(())
286287
}
287288

289+
#[inline(always)]
290+
fn expose_ptr(
291+
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
292+
_ptr: Pointer<AllocId>,
293+
) -> InterpResult<'tcx> {
294+
throw_machine_stop_str!("exposing pointers isn't supported in ConstProp")
295+
}
296+
288297
#[inline(always)]
289298
fn init_frame_extra(
290299
_ecx: &mut InterpCx<'mir, 'tcx, Self>,

compiler/rustc_mir_transform/src/const_prop_lint.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ use crate::MirLint;
3030
use rustc_const_eval::const_eval::ConstEvalErr;
3131
use rustc_const_eval::interpret::{
3232
self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
33-
LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Scalar,
34-
ScalarMaybeUninit, StackPopCleanup, StackPopUnwind,
33+
LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer,
34+
Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind,
3535
};
3636

3737
/// The maximum number of bytes that we'll allocate space for a local or the return value.
@@ -280,6 +280,14 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
280280
Ok(())
281281
}
282282

283+
#[inline(always)]
284+
fn expose_ptr(
285+
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
286+
_ptr: Pointer<AllocId>,
287+
) -> InterpResult<'tcx> {
288+
throw_machine_stop_str!("exposing pointers isn't supported in ConstProp")
289+
}
290+
283291
#[inline(always)]
284292
fn init_frame_extra(
285293
_ecx: &mut InterpCx<'mir, 'tcx, Self>,

src/test/ui/consts/miri_unleashed/ptr_arith.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ static CMP: () = {
1414
static PTR_INT_CAST: () = {
1515
let x = &0 as *const _ as usize;
1616
//~^ ERROR could not evaluate static initializer
17-
//~| unable to turn pointer into raw bytes
17+
//~| "exposing pointers" needs an rfc before being allowed inside constants
1818
let _v = x == x;
1919
};
2020

0 commit comments

Comments
 (0)