]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_middle/src/mir/interpret/pointer.rs
New upstream version 1.66.0+dfsg1
[rustc.git] / compiler / rustc_middle / src / mir / interpret / pointer.rs
CommitLineData
5869c6ff 1use super::{AllocId, InterpResult};
dc9dc135 2
532ac7d7 3use rustc_macros::HashStable;
ba9703b0 4use rustc_target::abi::{HasDataLayout, Size};
a1dfa0c6 5
c295e0f8 6use std::convert::{TryFrom, TryInto};
f9f354fc 7use std::fmt;
dc9dc135 8
a1dfa0c6
XL
9////////////////////////////////////////////////////////////////////////////////
10// Pointer arithmetic
11////////////////////////////////////////////////////////////////////////////////
12
ba9703b0 13pub trait PointerArithmetic: HasDataLayout {
a1dfa0c6
XL
14 // These are not supposed to be overridden.
15
16 #[inline(always)]
17 fn pointer_size(&self) -> Size {
18 self.data_layout().pointer_size
19 }
20
5e7ed085
FG
21 #[inline(always)]
22 fn max_size_of_val(&self) -> Size {
23 Size::from_bytes(self.machine_isize_max())
24 }
25
60c5eb7d 26 #[inline]
ba9703b0 27 fn machine_usize_max(&self) -> u64 {
c295e0f8 28 self.pointer_size().unsigned_int_max().try_into().unwrap()
60c5eb7d
XL
29 }
30
f9f354fc
XL
31 #[inline]
32 fn machine_isize_min(&self) -> i64 {
c295e0f8 33 self.pointer_size().signed_int_min().try_into().unwrap()
f9f354fc
XL
34 }
35
60c5eb7d 36 #[inline]
ba9703b0 37 fn machine_isize_max(&self) -> i64 {
c295e0f8 38 self.pointer_size().signed_int_max().try_into().unwrap()
60c5eb7d
XL
39 }
40
136023e0
XL
41 #[inline]
42 fn machine_usize_to_isize(&self, val: u64) -> i64 {
43 let val = val as i64;
c295e0f8 44 // Now wrap-around into the machine_isize range.
136023e0 45 if val > self.machine_isize_max() {
2b03887a 46 // This can only happen if the ptr size is < 64, so we know max_usize_plus_1 fits into
136023e0 47 // i64.
c295e0f8 48 debug_assert!(self.pointer_size().bits() < 64);
136023e0
XL
49 let max_usize_plus_1 = 1u128 << self.pointer_size().bits();
50 val - i64::try_from(max_usize_plus_1).unwrap()
51 } else {
52 val
53 }
54 }
55
dc9dc135
XL
56 /// Helper function: truncate given value-"overflowed flag" pair to pointer size and
57 /// update "overflowed flag" if there was an overflow.
58 /// This should be called by all the other methods before returning!
a1dfa0c6 59 #[inline]
dc9dc135 60 fn truncate_to_ptr(&self, (val, over): (u64, bool)) -> (u64, bool) {
ba9703b0 61 let val = u128::from(val);
a1dfa0c6 62 let max_ptr_plus_1 = 1u128 << self.pointer_size().bits();
ba9703b0 63 (u64::try_from(val % max_ptr_plus_1).unwrap(), over || val >= max_ptr_plus_1)
a1dfa0c6
XL
64 }
65
66 #[inline]
67 fn overflowing_offset(&self, val: u64, i: u64) -> (u64, bool) {
f9f354fc
XL
68 // We do not need to check if i fits in a machine usize. If it doesn't,
69 // either the wrapping_add will wrap or res will not fit in a pointer.
dc9dc135
XL
70 let res = val.overflowing_add(i);
71 self.truncate_to_ptr(res)
a1dfa0c6
XL
72 }
73
a1dfa0c6 74 #[inline]
ba9703b0 75 fn overflowing_signed_offset(&self, val: u64, i: i64) -> (u64, bool) {
f9f354fc 76 // We need to make sure that i fits in a machine isize.
5869c6ff 77 let n = i.unsigned_abs();
f9f354fc
XL
78 if i >= 0 {
79 let (val, over) = self.overflowing_offset(val, n);
80 (val, over || i > self.machine_isize_max())
a1dfa0c6 81 } else {
f9f354fc
XL
82 let res = val.overflowing_sub(n);
83 let (val, over) = self.truncate_to_ptr(res);
84 (val, over || i < self.machine_isize_min())
a1dfa0c6
XL
85 }
86 }
dc9dc135
XL
87
88 #[inline]
89 fn offset<'tcx>(&self, val: u64, i: u64) -> InterpResult<'tcx, u64> {
90 let (res, over) = self.overflowing_offset(val, i);
60c5eb7d 91 if over { throw_ub!(PointerArithOverflow) } else { Ok(res) }
dc9dc135
XL
92 }
93
94 #[inline]
95 fn signed_offset<'tcx>(&self, val: u64, i: i64) -> InterpResult<'tcx, u64> {
ba9703b0 96 let (res, over) = self.overflowing_signed_offset(val, i);
60c5eb7d 97 if over { throw_ub!(PointerArithOverflow) } else { Ok(res) }
dc9dc135 98 }
a1dfa0c6
XL
99}
100
ba9703b0 101impl<T: HasDataLayout> PointerArithmetic for T {}
a1dfa0c6 102
136023e0
XL
103/// This trait abstracts over the kind of provenance that is associated with a `Pointer`. It is
104/// mostly opaque; the `Machine` trait extends it with some more operations that also have access to
105/// some global state.
106/// We don't actually care about this `Debug` bound (we use `Provenance::fmt` to format the entire
5e7ed085 107/// pointer), but `derive` adds some unnecessary bounds.
136023e0
XL
108pub trait Provenance: Copy + fmt::Debug {
109 /// Says whether the `offset` field of `Pointer`s with this provenance is the actual physical address.
f2b60f7d
FG
110 /// - If `false`, the offset *must* be relative. This means the bytes representing a pointer are
111 /// different from what the Abstract Machine prescribes, so the interpreter must prevent any
112 /// operation that would inspect the underlying bytes of a pointer, such as ptr-to-int
113 /// transmutation. A `ReadPointerAsBytes` error will be raised in such situations.
114 /// - If `true`, the interpreter will permit operations to inspect the underlying bytes of a
115 /// pointer, and implement ptr-to-int transmutation by stripping provenance.
136023e0
XL
116 const OFFSET_IS_ADDR: bool;
117
94222f64
XL
118 /// We also use this trait to control whether to abort execution when a pointer is being partially overwritten
119 /// (this avoids a separate trait in `allocation.rs` just for this purpose).
120 const ERR_ON_PARTIAL_PTR_OVERWRITE: bool;
121
136023e0
XL
122 /// Determines how a pointer should be printed.
123 fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result
124 where
125 Self: Sized;
126
04454e1e
FG
127 /// If `OFFSET_IS_ADDR == false`, provenance must always be able to
128 /// identify the allocation this ptr points to (i.e., this must return `Some`).
129 /// Otherwise this function is best-effort (but must agree with `Machine::ptr_get_alloc`).
136023e0 130 /// (Identifying the offset in that allocation, however, is harder -- use `Memory::ptr_get_alloc` for that.)
04454e1e 131 fn get_alloc_id(self) -> Option<AllocId>;
f2b60f7d
FG
132
133 /// Defines the 'join' of provenance: what happens when doing a pointer load and different bytes have different provenance.
134 fn join(left: Option<Self>, right: Option<Self>) -> Option<Self>;
136023e0
XL
135}
136
137impl Provenance for AllocId {
138 // With the `AllocId` as provenance, the `offset` is interpreted *relative to the allocation*,
139 // so ptr-to-int casts are not possible (since we do not know the global physical offset).
140 const OFFSET_IS_ADDR: bool = false;
141
94222f64
XL
142 // For now, do not allow this, so that we keep our options open.
143 const ERR_ON_PARTIAL_PTR_OVERWRITE: bool = true;
144
136023e0
XL
145 fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 // Forward `alternate` flag to `alloc_id` printing.
147 if f.alternate() {
148 write!(f, "{:#?}", ptr.provenance)?;
149 } else {
150 write!(f, "{:?}", ptr.provenance)?;
151 }
152 // Print offset only if it is non-zero.
153 if ptr.offset.bytes() > 0 {
064997fb 154 write!(f, "+{:#x}", ptr.offset.bytes())?;
136023e0
XL
155 }
156 Ok(())
157 }
158
04454e1e
FG
159 fn get_alloc_id(self) -> Option<AllocId> {
160 Some(self)
136023e0 161 }
f2b60f7d
FG
162
163 fn join(_left: Option<Self>, _right: Option<Self>) -> Option<Self> {
164 panic!("merging provenance is not supported when `OFFSET_IS_ADDR` is false")
165 }
136023e0
XL
166}
167
f9f354fc 168/// Represents a pointer in the Miri engine.
a1dfa0c6 169///
136023e0 170/// Pointers are "tagged" with provenance information; typically the `AllocId` they belong to.
3dfed10e 171#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, TyEncodable, TyDecodable, Hash)]
ba9703b0 172#[derive(HashStable)]
064997fb
FG
173pub struct Pointer<Prov = AllocId> {
174 pub(super) offset: Size, // kept private to avoid accidental misinterpretation (meaning depends on `Prov` type)
175 pub provenance: Prov,
a1dfa0c6
XL
176}
177
48663c56 178static_assert_size!(Pointer, 16);
064997fb 179// `Option<Prov>` pointers are also passed around quite a bit
04454e1e
FG
180// (but not stored in permanent machine state).
181static_assert_size!(Pointer<Option<AllocId>>, 16);
9fa01778 182
f9f354fc
XL
183// We want the `Debug` output to be readable as it is used by `derive(Debug)` for
184// all the Miri types.
064997fb 185impl<Prov: Provenance> fmt::Debug for Pointer<Prov> {
f9f354fc 186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136023e0 187 Provenance::fmt(self, f)
f9f354fc
XL
188 }
189}
190
064997fb 191impl<Prov: Provenance> fmt::Debug for Pointer<Option<Prov>> {
dc9dc135 192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136023e0 193 match self.provenance {
064997fb
FG
194 Some(prov) => Provenance::fmt(&Pointer::new(prov, self.offset), f),
195 None => write!(f, "{:#x}[noalloc]", self.offset.bytes()),
196 }
197 }
198}
199
200impl<Prov: Provenance> fmt::Display for Pointer<Option<Prov>> {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 if self.provenance.is_none() && self.offset.bytes() == 0 {
203 write!(f, "null pointer")
204 } else {
205 fmt::Debug::fmt(self, f)
136023e0 206 }
dc9dc135
XL
207 }
208}
209
60c5eb7d 210/// Produces a `Pointer` that points to the beginning of the `Allocation`.
a1dfa0c6
XL
211impl From<AllocId> for Pointer {
212 #[inline(always)]
213 fn from(alloc_id: AllocId) -> Self {
214 Pointer::new(alloc_id, Size::ZERO)
215 }
216}
217
064997fb 218impl<Prov> From<Pointer<Prov>> for Pointer<Option<Prov>> {
a1dfa0c6 219 #[inline(always)]
064997fb
FG
220 fn from(ptr: Pointer<Prov>) -> Self {
221 let (prov, offset) = ptr.into_parts();
222 Pointer::new(Some(prov), offset)
a1dfa0c6 223 }
136023e0 224}
a1dfa0c6 225
064997fb
FG
226impl<Prov> Pointer<Option<Prov>> {
227 /// Convert this pointer that *might* have a provenance into a pointer that *definitely* has a
228 /// provenance, or an absolute address.
04454e1e
FG
229 ///
230 /// This is rarely what you want; call `ptr_try_get_alloc_id` instead.
064997fb 231 pub fn into_pointer_or_addr(self) -> Result<Pointer<Prov>, Size> {
136023e0 232 match self.provenance {
064997fb 233 Some(prov) => Ok(Pointer::new(prov, self.offset)),
136023e0
XL
234 None => Err(self.offset),
235 }
236 }
04454e1e
FG
237
238 /// Returns the absolute address the pointer points to.
064997fb 239 /// Only works if Prov::OFFSET_IS_ADDR is true!
04454e1e
FG
240 pub fn addr(self) -> Size
241 where
064997fb 242 Prov: Provenance,
04454e1e 243 {
064997fb 244 assert!(Prov::OFFSET_IS_ADDR);
04454e1e
FG
245 self.offset
246 }
136023e0
XL
247}
248
064997fb
FG
249impl<Prov> Pointer<Option<Prov>> {
250 #[inline(always)]
251 pub fn from_addr(addr: u64) -> Self {
252 Pointer { provenance: None, offset: Size::from_bytes(addr) }
253 }
254
48663c56 255 #[inline(always)]
136023e0 256 pub fn null() -> Self {
064997fb 257 Pointer::from_addr(0)
48663c56 258 }
a1dfa0c6
XL
259}
260
064997fb 261impl<'tcx, Prov> Pointer<Prov> {
a1dfa0c6 262 #[inline(always)]
064997fb 263 pub fn new(provenance: Prov, offset: Size) -> Self {
136023e0
XL
264 Pointer { provenance, offset }
265 }
266
064997fb 267 /// Obtain the constituents of this pointer. Not that the meaning of the offset depends on the type `Prov`!
136023e0
XL
268 /// This function must only be used in the implementation of `Machine::ptr_get_alloc`,
269 /// and when a `Pointer` is taken apart to be stored efficiently in an `Allocation`.
270 #[inline(always)]
064997fb 271 pub fn into_parts(self) -> (Prov, Size) {
136023e0
XL
272 (self.provenance, self.offset)
273 }
274
064997fb 275 pub fn map_provenance(self, f: impl FnOnce(Prov) -> Prov) -> Self {
136023e0 276 Pointer { provenance: f(self.provenance), ..self }
a1dfa0c6
XL
277 }
278
279 #[inline]
dc9dc135 280 pub fn offset(self, i: Size, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
136023e0
XL
281 Ok(Pointer {
282 offset: Size::from_bytes(cx.data_layout().offset(self.offset.bytes(), i.bytes())?),
283 ..self
284 })
a1dfa0c6
XL
285 }
286
287 #[inline]
288 pub fn overflowing_offset(self, i: Size, cx: &impl HasDataLayout) -> (Self, bool) {
289 let (res, over) = cx.data_layout().overflowing_offset(self.offset.bytes(), i.bytes());
136023e0
XL
290 let ptr = Pointer { offset: Size::from_bytes(res), ..self };
291 (ptr, over)
a1dfa0c6
XL
292 }
293
294 #[inline(always)]
295 pub fn wrapping_offset(self, i: Size, cx: &impl HasDataLayout) -> Self {
296 self.overflowing_offset(i, cx).0
297 }
298
299 #[inline]
dc9dc135 300 pub fn signed_offset(self, i: i64, cx: &impl HasDataLayout) -> InterpResult<'tcx, Self> {
136023e0
XL
301 Ok(Pointer {
302 offset: Size::from_bytes(cx.data_layout().signed_offset(self.offset.bytes(), i)?),
303 ..self
304 })
a1dfa0c6
XL
305 }
306
307 #[inline]
ba9703b0 308 pub fn overflowing_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> (Self, bool) {
a1dfa0c6 309 let (res, over) = cx.data_layout().overflowing_signed_offset(self.offset.bytes(), i);
136023e0
XL
310 let ptr = Pointer { offset: Size::from_bytes(res), ..self };
311 (ptr, over)
a1dfa0c6
XL
312 }
313
314 #[inline(always)]
315 pub fn wrapping_signed_offset(self, i: i64, cx: &impl HasDataLayout) -> Self {
ba9703b0 316 self.overflowing_signed_offset(i, cx).0
a1dfa0c6 317 }
a1dfa0c6 318}