Skip to content

Commit a964a92

Browse files
committed
Auto merge of #131068 - RalfJung:immediate-offset-sanity-check, r=nnethercote
Don't use Immediate::offset to transmute pointers to integers This applies the relatively new `assert_matches_abi` check in the `offset` operation on immediates, which makes sure that if offsets are used to alter the layout (which is possible because the field layout is arbitrarily picked by the caller), this is not done in a way that breaks the invariant of the `Immediate` type. This leads to ICEs in a GVN mir-opt test, so the second commit fixes GVN. Fixes #131064.
2 parents 1b3b8e7 + 7d63efd commit a964a92

File tree

6 files changed

+76
-46
lines changed

6 files changed

+76
-46
lines changed

Diff for: compiler/rustc_const_eval/src/interpret/operand.rs

+35-29
Original file line numberDiff line numberDiff line change
@@ -113,28 +113,47 @@ impl<Prov: Provenance> Immediate<Prov> {
113113
}
114114

115115
/// Assert that this immediate is a valid value for the given ABI.
116-
pub fn assert_matches_abi(self, abi: Abi, cx: &impl HasDataLayout) {
116+
pub fn assert_matches_abi(self, abi: Abi, msg: &str, cx: &impl HasDataLayout) {
117117
match (self, abi) {
118118
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
119-
assert_eq!(scalar.size(), s.size(cx));
119+
assert_eq!(scalar.size(), s.size(cx), "{msg}: scalar value has wrong size");
120120
if !matches!(s.primitive(), abi::Pointer(..)) {
121121
// This is not a pointer, it should not carry provenance.
122-
assert!(matches!(scalar, Scalar::Int(..)));
122+
assert!(
123+
matches!(scalar, Scalar::Int(..)),
124+
"{msg}: scalar value should be an integer, but has provenance"
125+
);
123126
}
124127
}
125128
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
126-
assert_eq!(a_val.size(), a.size(cx));
129+
assert_eq!(
130+
a_val.size(),
131+
a.size(cx),
132+
"{msg}: first component of scalar pair has wrong size"
133+
);
127134
if !matches!(a.primitive(), abi::Pointer(..)) {
128-
assert!(matches!(a_val, Scalar::Int(..)));
135+
assert!(
136+
matches!(a_val, Scalar::Int(..)),
137+
"{msg}: first component of scalar pair should be an integer, but has provenance"
138+
);
129139
}
130-
assert_eq!(b_val.size(), b.size(cx));
140+
assert_eq!(
141+
b_val.size(),
142+
b.size(cx),
143+
"{msg}: second component of scalar pair has wrong size"
144+
);
131145
if !matches!(b.primitive(), abi::Pointer(..)) {
132-
assert!(matches!(b_val, Scalar::Int(..)));
146+
assert!(
147+
matches!(b_val, Scalar::Int(..)),
148+
"{msg}: second component of scalar pair should be an integer, but has provenance"
149+
);
133150
}
134151
}
135-
(Immediate::Uninit, _) => {}
152+
(Immediate::Uninit, _) => {
153+
assert!(abi.is_sized(), "{msg}: unsized immediates are not a thing");
154+
}
136155
_ => {
137-
bug!("value {self:?} does not match ABI {abi:?})",)
156+
bug!("{msg}: value {self:?} does not match ABI {abi:?})",)
138157
}
139158
}
140159
}
@@ -241,6 +260,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
241260

242261
#[inline(always)]
243262
pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self {
263+
// Without a `cx` we cannot call `assert_matches_abi`.
244264
debug_assert!(
245265
match (imm, layout.abi) {
246266
(Immediate::Scalar(..), Abi::Scalar(..)) => true,
@@ -261,7 +281,6 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
261281

262282
#[inline]
263283
pub fn from_scalar_int(s: ScalarInt, layout: TyAndLayout<'tcx>) -> Self {
264-
assert_eq!(s.size(), layout.size);
265284
Self::from_scalar(Scalar::from(s), layout)
266285
}
267286

@@ -334,7 +353,10 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
334353
/// given layout.
335354
// Not called `offset` to avoid confusion with the trait method.
336355
fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
337-
debug_assert!(layout.is_sized(), "unsized immediates are not a thing");
356+
// Verify that the input matches its type.
357+
if cfg!(debug_assertions) {
358+
self.assert_matches_abi(self.layout.abi, "invalid input to Immediate::offset", cx);
359+
}
338360
// `ImmTy` have already been checked to be in-bounds, so we can just check directly if this
339361
// remains in-bounds. This cannot actually be violated since projections are type-checked
340362
// and bounds-checked.
@@ -368,32 +390,14 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
368390
// the field covers the entire type
369391
_ if layout.size == self.layout.size => {
370392
assert_eq!(offset.bytes(), 0);
371-
assert!(
372-
match (self.layout.abi, layout.abi) {
373-
(Abi::Scalar(l), Abi::Scalar(r)) => l.size(cx) == r.size(cx),
374-
(Abi::ScalarPair(l1, l2), Abi::ScalarPair(r1, r2)) =>
375-
l1.size(cx) == r1.size(cx) && l2.size(cx) == r2.size(cx),
376-
_ => false,
377-
},
378-
"cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}",
379-
self.layout.ty,
380-
layout.ty,
381-
self.layout.abi,
382-
layout.abi,
383-
);
384393
**self
385394
}
386395
// extract fields from types with `ScalarPair` ABI
387396
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
388-
assert_matches!(layout.abi, Abi::Scalar(..));
389397
Immediate::from(if offset.bytes() == 0 {
390-
// It is "okay" to transmute from `usize` to a pointer (GVN relies on that).
391-
// So only compare the size.
392-
assert_eq!(layout.size, a.size(cx));
393398
a_val
394399
} else {
395400
assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi));
396-
assert_eq!(layout.size, b.size(cx));
397401
b_val
398402
})
399403
}
@@ -405,6 +409,8 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
405409
self.layout
406410
),
407411
};
412+
// Ensure the new layout matches the new value.
413+
inner_val.assert_matches_abi(layout.abi, "invalid field type in Immediate::offset", cx);
408414

409415
ImmTy::from_immediate(inner_val, layout)
410416
}

Diff for: compiler/rustc_const_eval/src/interpret/place.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,11 @@ where
658658
// Things can ge wrong in quite weird ways when this is violated.
659659
// Unfortunately this is too expensive to do in release builds.
660660
if cfg!(debug_assertions) {
661-
src.assert_matches_abi(local_layout.abi, self);
661+
src.assert_matches_abi(
662+
local_layout.abi,
663+
"invalid immediate for given destination place",
664+
self,
665+
);
662666
}
663667
}
664668
Left(mplace) => {
@@ -679,7 +683,7 @@ where
679683
) -> InterpResult<'tcx> {
680684
// We use the sizes from `value` below.
681685
// Ensure that matches the type of the place it is written to.
682-
value.assert_matches_abi(layout.abi, self);
686+
value.assert_matches_abi(layout.abi, "invalid immediate for given destination place", self);
683687
// Note that it is really important that the type here is the right one, and matches the
684688
// type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
685689
// to handle padding properly, which is only correct if we never look at this data with the

Diff for: compiler/rustc_const_eval/src/interpret/projection.rs

+4
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
8282
self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx)
8383
}
8484

85+
/// This does an offset-by-zero, which is effectively a transmute. Note however that
86+
/// not all transmutes are supported by all projectables -- specifically, if this is an
87+
/// `OpTy` or `ImmTy`, the new layout must have almost the same ABI as the old one
88+
/// (only changing the `valid_range` is allowed and turning integers into pointers).
8589
fn transmute<M: Machine<'tcx, Provenance = Prov>>(
8690
&self,
8791
layout: TyAndLayout<'tcx>,

Diff for: compiler/rustc_mir_transform/src/gvn.rs

+23-7
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
103103
use rustc_middle::ty::{self, Ty, TyCtxt};
104104
use rustc_span::DUMMY_SP;
105105
use rustc_span::def_id::DefId;
106-
use rustc_target::abi::{self, Abi, FIRST_VARIANT, FieldIdx, Size, VariantIdx};
106+
use rustc_target::abi::{self, Abi, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx};
107107
use smallvec::SmallVec;
108108
use tracing::{debug, instrument, trace};
109109

@@ -568,13 +568,29 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
568568
CastKind::Transmute => {
569569
let value = self.evaluated[value].as_ref()?;
570570
let to = self.ecx.layout_of(to).ok()?;
571-
// `offset` for immediates only supports scalar/scalar-pair ABIs,
572-
// so bail out if the target is not one.
571+
// `offset` for immediates generally only supports projections that match the
572+
// type of the immediate. However, as a HACK, we exploit that it can also do
573+
// limited transmutes: it only works between types with the same layout, and
574+
// cannot transmute pointers to integers.
573575
if value.as_mplace_or_imm().is_right() {
574-
match (value.layout.abi, to.abi) {
575-
(Abi::Scalar(..), Abi::Scalar(..)) => {}
576-
(Abi::ScalarPair(..), Abi::ScalarPair(..)) => {}
577-
_ => return None,
576+
let can_transmute = match (value.layout.abi, to.abi) {
577+
(Abi::Scalar(s1), Abi::Scalar(s2)) => {
578+
s1.size(&self.ecx) == s2.size(&self.ecx)
579+
&& !matches!(s1.primitive(), Primitive::Pointer(..))
580+
}
581+
(Abi::ScalarPair(a1, b1), Abi::ScalarPair(a2, b2)) => {
582+
a1.size(&self.ecx) == a2.size(&self.ecx) &&
583+
b1.size(&self.ecx) == b2.size(&self.ecx) &&
584+
// The alignment of the second component determines its offset, so that also needs to match.
585+
b1.align(&self.ecx) == b2.align(&self.ecx) &&
586+
// None of the inputs may be a pointer.
587+
!matches!(a1.primitive(), Primitive::Pointer(..))
588+
&& !matches!(b1.primitive(), Primitive::Pointer(..))
589+
}
590+
_ => false,
591+
};
592+
if !can_transmute {
593+
return None;
578594
}
579595
}
580596
value.offset(Size::ZERO, to, &self.ecx).discard_err()?

Diff for: tests/coverage/closure_macro.cov-map

+4-4
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,19 @@ Number of file 0 mappings: 6
2323
- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2)
2424

2525
Function name: closure_macro::main::{closure#0}
26-
Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
26+
Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 10, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
2727
Number of files: 1
2828
- file 0 => global file 1
2929
Number of expressions: 3
3030
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
3131
- expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add)
32-
- expression 2 operands: lhs = Counter(2), rhs = Zero
32+
- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
3333
Number of file 0 mappings: 5
3434
- Code(Counter(0)) at (prev + 16, 28) to (start + 3, 33)
3535
- Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39)
3636
- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22)
3737
= (c0 - c1)
38-
- Code(Zero) at (prev + 0, 23) to (start + 0, 30)
38+
- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30)
3939
- Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10)
40-
= (c1 + (c2 + Zero))
40+
= (c1 + (c2 + c3))
4141

Diff for: tests/coverage/closure_macro_async.cov-map

+4-4
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,19 @@ Number of file 0 mappings: 6
3131
- Code(Counter(0)) at (prev + 3, 1) to (start + 0, 2)
3232

3333
Function name: closure_macro_async::test::{closure#0}::{closure#0}
34-
Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 00, 05, 01, 15, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 00, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
34+
Raw bytes (35): 0x[01, 01, 03, 01, 05, 05, 0b, 09, 0d, 05, 01, 15, 1c, 03, 21, 05, 04, 11, 01, 27, 02, 03, 11, 00, 16, 0d, 00, 17, 00, 1e, 07, 02, 09, 00, 0a]
3535
Number of files: 1
3636
- file 0 => global file 1
3737
Number of expressions: 3
3838
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
3939
- expression 1 operands: lhs = Counter(1), rhs = Expression(2, Add)
40-
- expression 2 operands: lhs = Counter(2), rhs = Zero
40+
- expression 2 operands: lhs = Counter(2), rhs = Counter(3)
4141
Number of file 0 mappings: 5
4242
- Code(Counter(0)) at (prev + 21, 28) to (start + 3, 33)
4343
- Code(Counter(1)) at (prev + 4, 17) to (start + 1, 39)
4444
- Code(Expression(0, Sub)) at (prev + 3, 17) to (start + 0, 22)
4545
= (c0 - c1)
46-
- Code(Zero) at (prev + 0, 23) to (start + 0, 30)
46+
- Code(Counter(3)) at (prev + 0, 23) to (start + 0, 30)
4747
- Code(Expression(1, Add)) at (prev + 2, 9) to (start + 0, 10)
48-
= (c1 + (c2 + Zero))
48+
= (c1 + (c2 + c3))
4949

0 commit comments

Comments
 (0)