Skip to content

Commit 19e0f6f

Browse files
committed
Calculate layout of Invariant as needed, use enum for size
1 parent 16f7536 commit 19e0f6f

File tree

5 files changed

+128
-70
lines changed

5 files changed

+128
-70
lines changed
Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,48 @@
1-
use rustc_middle::mir::interpret::{Allocation, ConstAllocation};
2-
use rustc_middle::mir::Mutability;
1+
use rustc_hir::lang_items::LangItem;
2+
use rustc_middle::mir::interpret::{AllocRange, Allocation, ConstAllocation, Scalar as MirScalar};
33
use rustc_middle::ty::layout::LayoutCx;
44
use rustc_middle::ty::{ParamEnv, ParamEnvAnd};
55
use rustc_middle::ty::{Ty, TyCtxt};
66
use rustc_target::abi::{
7-
Abi, Align, Endian, FieldsShape, HasDataLayout, Scalar, Size, WrappingRange,
7+
Abi, FieldsShape, HasDataLayout, Integer, Primitive, Scalar, Size, TyAndLayout, WrappingRange,
88
};
99

10+
#[derive(Debug, Clone, Copy)]
11+
enum InvariantSize {
12+
U8,
13+
U16,
14+
U32,
15+
U64,
16+
U128,
17+
Pointer,
18+
}
19+
1020
#[derive(Debug, Clone, Copy)]
1121
struct Invariant {
1222
offset: Size,
13-
size: Size,
14-
start: u128,
15-
end: u128,
23+
size: InvariantSize,
24+
valid_range_start: u128,
25+
valid_range_end: u128,
1626
}
1727

18-
// TODO: Don't add duplicate invariants (maybe use a HashMap?)
28+
// FIXME: Don't add duplicate invariants (maybe use a HashMap?)
1929
fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec<Invariant>, offset: Size) {
2030
let x = tcx.layout_of(ParamEnvAnd { param_env: ParamEnv::reveal_all(), value: ty });
2131

2232
if let Ok(layout) = x {
2333
if let Abi::Scalar(Scalar::Initialized { value, valid_range }) = layout.layout.abi() {
24-
let size = value.size(&tcx);
34+
let size = match value {
35+
Primitive::Int(Integer::I8, _) => InvariantSize::U8,
36+
Primitive::Int(Integer::I16, _) => InvariantSize::U16,
37+
Primitive::Int(Integer::I32, _) => InvariantSize::U32,
38+
Primitive::Int(Integer::I64, _) => InvariantSize::U64,
39+
Primitive::Int(Integer::I128, _) => InvariantSize::U128,
40+
Primitive::F32 => InvariantSize::U32,
41+
Primitive::F64 => InvariantSize::U64,
42+
Primitive::Pointer => InvariantSize::Pointer,
43+
};
2544
let WrappingRange { start, end } = valid_range;
26-
invs.push(Invariant { offset, size, start, end })
45+
invs.push(Invariant { offset, size, valid_range_start: start, valid_range_end: end })
2746
}
2847

2948
let param_env = ParamEnv::reveal_all();
@@ -33,8 +52,8 @@ fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec<Invarian
3352
FieldsShape::Primitive => {}
3453
FieldsShape::Union(_) => {}
3554
FieldsShape::Array { stride, count } => {
36-
// TODO: should we just bail if we're making a Too Large type?
37-
// (Like [bool; 1_000_000])
55+
// We may wish to bail out if we're generating too many invariants.
56+
// That would lead to false negatives, though.
3857
for idx in 0..*count {
3958
let off = offset + *stride * idx;
4059
let f = layout.field(&unwrap, idx as usize);
@@ -57,22 +76,13 @@ fn add_invariants<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, invs: &mut Vec<Invarian
5776
}
5877
}
5978

60-
fn extend_encoded_int(to: &mut Vec<u8>, endian: Endian, ptr_size: PointerSize, value: Size) {
61-
match (endian, ptr_size) {
62-
(Endian::Little, PointerSize::Bits16) => to.extend((value.bytes() as u16).to_le_bytes()),
63-
(Endian::Little, PointerSize::Bits32) => to.extend((value.bytes() as u32).to_le_bytes()),
64-
(Endian::Little, PointerSize::Bits64) => to.extend((value.bytes()).to_le_bytes()),
65-
(Endian::Big, PointerSize::Bits16) => to.extend((value.bytes() as u16).to_be_bytes()),
66-
(Endian::Big, PointerSize::Bits32) => to.extend((value.bytes() as u32).to_be_bytes()),
67-
(Endian::Big, PointerSize::Bits64) => to.extend((value.bytes()).to_be_bytes()),
68-
}
69-
}
70-
71-
#[derive(Clone, Copy)]
72-
enum PointerSize {
73-
Bits16,
74-
Bits32,
75-
Bits64,
79+
fn get_layout_of_invariant<'tcx>(tcx: TyCtxt<'tcx>) -> TyAndLayout<'tcx, Ty<'tcx>> {
80+
let item = tcx.require_lang_item(LangItem::ValidityInvariant, None);
81+
let ty = tcx.type_of(item);
82+
let layout = tcx
83+
.layout_of(ParamEnv::reveal_all().and(ty))
84+
.expect("invalid layout for ValidityInvariant lang item");
85+
layout
7686
}
7787

7888
/// Directly returns a `ConstAllocation` containing a list of validity invariants of the given type.
@@ -83,38 +93,54 @@ pub(crate) fn alloc_validity_invariants_of<'tcx>(
8393
let mut invs: Vec<Invariant> = Vec::new();
8494

8595
let layout = tcx.data_layout();
86-
87-
let ptr_size = match layout.pointer_size.bits() {
88-
16 => PointerSize::Bits16,
89-
32 => PointerSize::Bits32,
90-
64 => PointerSize::Bits64,
91-
_ => {
92-
// Not sure if this can happen, but just return an empty slice?
93-
let alloc =
94-
Allocation::from_bytes(Vec::new(), Align::from_bytes(8).unwrap(), Mutability::Not);
95-
return tcx.intern_const_alloc(alloc);
96-
}
97-
};
96+
let validity_invariant = get_layout_of_invariant(tcx);
9897

9998
add_invariants(tcx, ty, &mut invs, Size::ZERO);
10099

101-
let encode_range = match layout.endian {
102-
Endian::Little => |r: u128| r.to_le_bytes(),
103-
Endian::Big => |r: u128| r.to_be_bytes(),
104-
};
100+
let allocation_size = validity_invariant.layout.size() * invs.len() as u64;
101+
let mut alloc =
102+
Allocation::uninit(allocation_size, validity_invariant.layout.align().abi, true).unwrap();
103+
104+
let offset_off = validity_invariant.layout.fields().offset(0);
105+
let size_off = validity_invariant.layout.fields().offset(1);
106+
let start_off = validity_invariant.layout.fields().offset(2);
107+
let end_off = validity_invariant.layout.fields().offset(3);
108+
109+
for (idx, invariant) in invs.iter().enumerate() {
110+
let offset = idx as u64 * validity_invariant.layout.size();
111+
112+
let offset_range = AllocRange { start: offset + offset_off, size: layout.pointer_size };
113+
alloc
114+
.write_scalar(
115+
&tcx,
116+
offset_range,
117+
MirScalar::from_machine_usize(invariant.offset.bytes(), &tcx).into(),
118+
)
119+
.unwrap();
120+
121+
let size_range = AllocRange { start: offset + size_off, size: Size::from_bytes(1) };
122+
alloc
123+
.write_scalar(&tcx, size_range, MirScalar::from_u8(invariant.size as u8).into())
124+
.unwrap();
105125

106-
let mut encoded = Vec::new();
126+
let offset_range = AllocRange { start: offset + start_off, size: Size::from_bytes(16) };
127+
alloc
128+
.write_scalar(
129+
&tcx,
130+
offset_range,
131+
MirScalar::from_u128(invariant.valid_range_start).into(),
132+
)
133+
.unwrap();
107134

108-
// TODO: this needs to match the layout of `Invariant` in core/src/intrinsics.rs
109-
// how do we ensure that?
110-
for inv in invs {
111-
extend_encoded_int(&mut encoded, layout.endian, ptr_size, inv.offset);
112-
extend_encoded_int(&mut encoded, layout.endian, ptr_size, inv.size);
113-
encoded.extend(encode_range(inv.start));
114-
encoded.extend(encode_range(inv.end));
135+
let offset_range = AllocRange { start: offset + end_off, size: Size::from_bytes(16) };
136+
alloc
137+
.write_scalar(
138+
&tcx,
139+
offset_range,
140+
MirScalar::from_u128(invariant.valid_range_end).into(),
141+
)
142+
.unwrap();
115143
}
116144

117-
// TODO: The alignment here should be calculated from the struct definition, I guess?
118-
let alloc = Allocation::from_bytes(encoded, Align::from_bytes(8).unwrap(), Mutability::Not);
119145
tcx.intern_const_alloc(alloc)
120146
}

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ language_item_table! {
327327
Range, sym::Range, range_struct, Target::Struct, GenericRequirement::None;
328328
RangeToInclusive, sym::RangeToInclusive, range_to_inclusive_struct, Target::Struct, GenericRequirement::None;
329329
RangeTo, sym::RangeTo, range_to_struct, Target::Struct, GenericRequirement::None;
330+
ValidityInvariant, sym::ValidityInvariant, validity_invariant_struct, Target::Struct, GenericRequirement::None;
330331
}
331332

332333
pub enum GenericRequirement {

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ impl<Tag> Scalar<Tag> {
258258
Scalar::Int(i.into())
259259
}
260260

261+
#[inline]
262+
pub fn from_u128(i: u128) -> Self {
263+
Scalar::Int(i.into())
264+
}
265+
261266
#[inline]
262267
pub fn from_machine_usize(i: u64, cx: &impl HasDataLayout) -> Self {
263268
Self::from_uint(i, cx.data_layout().pointer_size)

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ symbols! {
277277
TyKind,
278278
Unknown,
279279
UnsafeArg,
280+
ValidityInvariant,
280281
Vec,
281282
VecDeque,
282283
Wrapper,

library/core/src/intrinsics.rs

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,18 +2138,42 @@ pub const unsafe fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: us
21382138
}
21392139
}
21402140

2141-
#[repr(C)]
21422141
#[derive(Debug, Clone, Copy)]
2143-
pub struct Invariant {
2144-
offset: u64,
2145-
size: u64,
2146-
start: u128,
2147-
end: u128,
2142+
#[repr(u8)]
2143+
enum InvariantSize {
2144+
U8 = 0,
2145+
U16 = 1,
2146+
U32 = 2,
2147+
U64 = 3,
2148+
U128 = 4,
2149+
Pointer = 5,
2150+
}
2151+
2152+
/// An Invariant is a field and its valid range. The valid range may be full, in which case it
2153+
/// should still be branched on, to allow tools like memory sanitizer to verify the memory is not
2154+
/// uninit.
2155+
// Be sure to keep the fields in order (offset, size, start, end), validity_invariants_of.rs needs
2156+
// that.
2157+
#[cfg(not(bootstrap))]
2158+
#[derive(Debug, Clone, Copy)]
2159+
#[lang = "ValidityInvariant"]
2160+
struct Invariant {
2161+
/// The offset in bytes from the start of the struct
2162+
offset: usize,
2163+
/// The size/type of the invariant.
2164+
size: InvariantSize,
2165+
/// The start point of the valid range of this field. Is allowed to be > valid_range_end, see
2166+
/// <https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/abi/struct.WrappingRange.html>
2167+
/// which this follows the semantics of.
2168+
valid_range_start: u128,
2169+
2170+
/// The end point of the range.
2171+
valid_range_end: u128,
21482172
}
21492173

21502174
#[cfg(not(bootstrap))]
21512175
/// Returns a list of all validity invariants of the type.
2152-
pub const fn validity_invariants_of<T>() -> &'static [Invariant] {
2176+
const fn validity_invariants_of<T>() -> &'static [Invariant] {
21532177
extern "rust-intrinsic" {
21542178
#[rustc_const_unstable(feature = "validity_invariants_of", issue = "none")]
21552179
pub fn validity_invariants_of<T>() -> &'static [u8];
@@ -2435,34 +2459,35 @@ pub(crate) unsafe fn assert_validity_of<T>(value: *const T) -> bool {
24352459
struct Unaligned<T>(T);
24362460

24372461
// SAFETY: The pointer dereferences here are valid if `value` is valid.
2438-
// though TODO: introduce a new size for "pointer", since reading a pointer as an int *is* UB.
24392462
unsafe {
24402463
let invariants = validity_invariants_of::<T>();
24412464
for invariant in invariants {
24422465
let off = invariant.offset as usize;
2443-
let start = invariant.start;
2444-
let end = invariant.end;
2466+
let start = invariant.valid_range_start;
2467+
let end = invariant.valid_range_end;
24452468

2446-
// TODO: Maybe replace this with an enum?
24472469
let (value, max): (u128, u128) = match invariant.size {
2448-
1 => ((*(value.cast::<u8>().add(off))).into(), u8::MAX.into()),
2449-
2 => (
2470+
InvariantSize::U8 => ((*(value.cast::<u8>().add(off))).into(), u8::MAX.into()),
2471+
InvariantSize::U16 => (
24502472
(*value.cast::<u8>().add(off).cast::<Unaligned<u16>>()).0.into(),
24512473
u16::MAX.into(),
24522474
),
2453-
4 => (
2475+
InvariantSize::U32 => (
24542476
(*value.cast::<u8>().add(off).cast::<Unaligned<u32>>()).0.into(),
24552477
u32::MAX.into(),
24562478
),
2457-
8 => (
2479+
InvariantSize::U64 => (
24582480
(*value.cast::<u8>().add(off).cast::<Unaligned<u64>>()).0.into(),
24592481
u64::MAX.into(),
24602482
),
2461-
16 => (
2483+
InvariantSize::U128 => (
24622484
(*value.cast::<u8>().add(off).cast::<Unaligned<u128>>()).0.into(),
24632485
u128::MAX.into(),
24642486
),
2465-
s => panic!("unexpected size {s}"),
2487+
InvariantSize::Pointer => (
2488+
(*value.cast::<u8>().add(off).cast::<Unaligned<*const ()>>()).0.addr() as u128,
2489+
usize::MAX as u128,
2490+
),
24662491
};
24672492

24682493
if start > end {

0 commit comments

Comments
 (0)