Skip to content

Commit 71312d6

Browse files
committed
Auto merge of #2661 - DrMeepster:deref_operand_as, r=oli-obk
Dereference pointers in shims as correct types Currently, shims will dereference pointers as the type written by the user. This can cause false positives, incorrect behavior such as #2136, and even ICEs if a field is not present. This PR fixes this by having shims dereference pointers with types from `std` or `libc` that we can rely on the layout and field names of instead of with whatever the user passed in. Fixes #1123
2 parents 940a070 + d537ab9 commit 71312d6

File tree

14 files changed

+233
-125
lines changed

14 files changed

+233
-125
lines changed

Diff for: src/concurrency/init_once.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::collections::VecDeque;
22
use std::num::NonZeroU32;
33

44
use rustc_index::Idx;
5+
use rustc_middle::ty::layout::TyAndLayout;
56

67
use super::sync::EvalContextExtPriv as _;
78
use super::thread::MachineCallback;
@@ -94,10 +95,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
9495
fn init_once_get_or_create_id(
9596
&mut self,
9697
lock_op: &OpTy<'tcx, Provenance>,
98+
lock_layout: TyAndLayout<'tcx>,
9799
offset: u64,
98100
) -> InterpResult<'tcx, InitOnceId> {
99101
let this = self.eval_context_mut();
100-
this.init_once_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset))
102+
this.init_once_get_or_create(|ecx, next_id| {
103+
ecx.get_or_create_id(next_id, lock_op, lock_layout, offset)
104+
})
101105
}
102106

103107
/// Provides the closure with the next InitOnceId. Creates that InitOnce if the closure returns None,

Diff for: src/concurrency/sync.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use log::trace;
66

77
use rustc_data_structures::fx::FxHashMap;
88
use rustc_index::{Idx, IndexVec};
9+
use rustc_middle::ty::layout::TyAndLayout;
910

1011
use super::init_once::InitOnce;
1112
use super::vector_clock::VClock;
@@ -200,11 +201,12 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>:
200201
&mut self,
201202
next_id: Id,
202203
lock_op: &OpTy<'tcx, Provenance>,
204+
lock_layout: TyAndLayout<'tcx>,
203205
offset: u64,
204206
) -> InterpResult<'tcx, Option<Id>> {
205207
let this = self.eval_context_mut();
206208
let value_place =
207-
this.deref_operand_and_offset(lock_op, offset, this.machine.layouts.u32)?;
209+
this.deref_operand_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
208210

209211
// Since we are lazy, this update has to be atomic.
210212
let (old, success) = this
@@ -278,28 +280,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
278280
fn mutex_get_or_create_id(
279281
&mut self,
280282
lock_op: &OpTy<'tcx, Provenance>,
283+
lock_layout: TyAndLayout<'tcx>,
281284
offset: u64,
282285
) -> InterpResult<'tcx, MutexId> {
283286
let this = self.eval_context_mut();
284-
this.mutex_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset))
287+
this.mutex_get_or_create(|ecx, next_id| {
288+
ecx.get_or_create_id(next_id, lock_op, lock_layout, offset)
289+
})
285290
}
286291

287292
fn rwlock_get_or_create_id(
288293
&mut self,
289294
lock_op: &OpTy<'tcx, Provenance>,
295+
lock_layout: TyAndLayout<'tcx>,
290296
offset: u64,
291297
) -> InterpResult<'tcx, RwLockId> {
292298
let this = self.eval_context_mut();
293-
this.rwlock_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset))
299+
this.rwlock_get_or_create(|ecx, next_id| {
300+
ecx.get_or_create_id(next_id, lock_op, lock_layout, offset)
301+
})
294302
}
295303

296304
fn condvar_get_or_create_id(
297305
&mut self,
298306
lock_op: &OpTy<'tcx, Provenance>,
307+
lock_layout: TyAndLayout<'tcx>,
299308
offset: u64,
300309
) -> InterpResult<'tcx, CondvarId> {
301310
let this = self.eval_context_mut();
302-
this.condvar_get_or_create(|ecx, next_id| ecx.get_or_create_id(next_id, lock_op, offset))
311+
this.condvar_get_or_create(|ecx, next_id| {
312+
ecx.get_or_create_id(next_id, lock_op, lock_layout, offset)
313+
})
303314
}
304315

305316
#[inline]

Diff for: src/helpers.rs

+41-8
Original file line numberDiff line numberDiff line change
@@ -730,31 +730,63 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
730730
}
731731
}
732732

733+
/// Dereference a pointer operand to a place using `layout` instead of the pointer's declared type
734+
fn deref_operand_as(
735+
&self,
736+
op: &OpTy<'tcx, Provenance>,
737+
layout: TyAndLayout<'tcx>,
738+
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
739+
let this = self.eval_context_ref();
740+
let ptr = this.read_pointer(op)?;
741+
742+
let mplace = MPlaceTy::from_aligned_ptr(ptr, layout);
743+
744+
this.check_mplace(mplace)?;
745+
746+
Ok(mplace)
747+
}
748+
749+
fn deref_pointer_as(
750+
&self,
751+
val: &ImmTy<'tcx, Provenance>,
752+
layout: TyAndLayout<'tcx>,
753+
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
754+
let this = self.eval_context_ref();
755+
let mut mplace = this.ref_to_mplace(val)?;
756+
757+
mplace.layout = layout;
758+
mplace.align = layout.align.abi;
759+
760+
Ok(mplace)
761+
}
762+
733763
/// Calculates the MPlaceTy given the offset and layout of an access on an operand
734764
fn deref_operand_and_offset(
735765
&self,
736766
op: &OpTy<'tcx, Provenance>,
737767
offset: u64,
738-
layout: TyAndLayout<'tcx>,
768+
base_layout: TyAndLayout<'tcx>,
769+
value_layout: TyAndLayout<'tcx>,
739770
) -> InterpResult<'tcx, MPlaceTy<'tcx, Provenance>> {
740771
let this = self.eval_context_ref();
741-
let op_place = this.deref_operand(op)?; // FIXME: we still deref with the original type!
772+
let op_place = this.deref_operand_as(op, base_layout)?;
742773
let offset = Size::from_bytes(offset);
743774

744775
// Ensure that the access is within bounds.
745-
assert!(op_place.layout.size >= offset + layout.size);
746-
let value_place = op_place.offset(offset, layout, this)?;
776+
assert!(base_layout.size >= offset + value_layout.size);
777+
let value_place = op_place.offset(offset, value_layout, this)?;
747778
Ok(value_place)
748779
}
749780

750781
fn read_scalar_at_offset(
751782
&self,
752783
op: &OpTy<'tcx, Provenance>,
753784
offset: u64,
754-
layout: TyAndLayout<'tcx>,
785+
base_layout: TyAndLayout<'tcx>,
786+
value_layout: TyAndLayout<'tcx>,
755787
) -> InterpResult<'tcx, Scalar<Provenance>> {
756788
let this = self.eval_context_ref();
757-
let value_place = this.deref_operand_and_offset(op, offset, layout)?;
789+
let value_place = this.deref_operand_and_offset(op, offset, base_layout, value_layout)?;
758790
this.read_scalar(&value_place.into())
759791
}
760792

@@ -763,10 +795,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
763795
op: &OpTy<'tcx, Provenance>,
764796
offset: u64,
765797
value: impl Into<Scalar<Provenance>>,
766-
layout: TyAndLayout<'tcx>,
798+
base_layout: TyAndLayout<'tcx>,
799+
value_layout: TyAndLayout<'tcx>,
767800
) -> InterpResult<'tcx, ()> {
768801
let this = self.eval_context_mut();
769-
let value_place = this.deref_operand_and_offset(op, offset, layout)?;
802+
let value_place = this.deref_operand_and_offset(op, offset, base_layout, value_layout)?;
770803
this.write_scalar(value, &value_place.into())
771804
}
772805

Diff for: src/shims/foreign_items.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -409,14 +409,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
409409
// &mut self,
410410
// arg1: &OpTy<'tcx, Provenance>,
411411
// arg2: &OpTy<'tcx, Provenance>,
412-
// arg3: &OpTy<'tcx, Provenance>)
412+
// arg3: &OpTy<'tcx, Provenance>,
413+
// arg4: &OpTy<'tcx, Provenance>)
413414
// -> InterpResult<'tcx, Scalar<Provenance>> {
414415
// let this = self.eval_context_mut();
415416
//
416417
// // First thing: load all the arguments. Details depend on the shim.
417418
// let arg1 = this.read_scalar(arg1)?.to_u32()?;
418419
// let arg2 = this.read_pointer(arg2)?; // when you need to work with the pointer directly
419-
// let arg3 = this.deref_operand(arg3)?; // when you want to load/store through the pointer at its declared type
420+
// let arg3 = this.deref_operand(arg3)?; // when you want to load/store through the pointer
421+
// let arg4 = this.deref_operand_as(arg4, this.libc_ty_layout("some_libc_struct")?)
420422
//
421423
// // ...
422424
//

Diff for: src/shims/time.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
2525
this.assert_target_os_is_unix("clock_gettime");
2626

2727
let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
28+
let tp = this.deref_operand_as(tp_op, this.libc_ty_layout("timespec"))?;
2829

2930
let absolute_clocks;
3031
let mut relative_clocks;
@@ -76,7 +77,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
7677
let tv_sec = duration.as_secs();
7778
let tv_nsec = duration.subsec_nanos();
7879

79-
this.write_int_fields(&[tv_sec.into(), tv_nsec.into()], &this.deref_operand(tp_op)?)?;
80+
this.write_int_fields(&[tv_sec.into(), tv_nsec.into()], &tp)?;
8081

8182
Ok(Scalar::from_i32(0))
8283
}
@@ -91,6 +92,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
9192
this.assert_target_os_is_unix("gettimeofday");
9293
this.check_no_isolation("`gettimeofday`")?;
9394

95+
let tv = this.deref_operand_as(tv_op, this.libc_ty_layout("timeval"))?;
96+
9497
// Using tz is obsolete and should always be null
9598
let tz = this.read_pointer(tz_op)?;
9699
if !this.ptr_is_null(tz)? {
@@ -103,7 +106,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
103106
let tv_sec = duration.as_secs();
104107
let tv_usec = duration.subsec_micros();
105108

106-
this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &this.deref_operand(tv_op)?)?;
109+
this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &tv)?;
107110

108111
Ok(0)
109112
}
@@ -118,6 +121,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
118121
this.assert_target_os("windows", "GetSystemTimeAsFileTime");
119122
this.check_no_isolation("`GetSystemTimeAsFileTime`")?;
120123

124+
let filetime = this.deref_operand_as(LPFILETIME_op, this.windows_ty_layout("FILETIME"))?;
125+
121126
let NANOS_PER_SEC = this.eval_windows_u64("time", "NANOS_PER_SEC");
122127
let INTERVALS_PER_SEC = this.eval_windows_u64("time", "INTERVALS_PER_SEC");
123128
let INTERVALS_TO_UNIX_EPOCH = this.eval_windows_u64("time", "INTERVALS_TO_UNIX_EPOCH");
@@ -131,10 +136,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
131136

132137
let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap();
133138
let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap();
134-
this.write_int_fields(
135-
&[dwLowDateTime.into(), dwHighDateTime.into()],
136-
&this.deref_operand(LPFILETIME_op)?,
137-
)?;
139+
this.write_int_fields(&[dwLowDateTime.into(), dwHighDateTime.into()], &filetime)?;
138140

139141
Ok(())
140142
}
@@ -177,7 +179,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
177179
// and thus 10^9 counts per second.
178180
this.write_scalar(
179181
Scalar::from_i64(1_000_000_000),
180-
&this.deref_operand(lpFrequency_op)?.into(),
182+
&this.deref_operand_as(lpFrequency_op, this.machine.layouts.u64)?.into(),
181183
)?;
182184
Ok(Scalar::from_i32(-1)) // Return non-zero on success
183185
}
@@ -204,7 +206,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
204206

205207
this.assert_target_os("macos", "mach_timebase_info");
206208

207-
let info = this.deref_operand(info_op)?;
209+
let info = this.deref_operand_as(info_op, this.libc_ty_layout("mach_timebase_info"))?;
208210

209211
// Since our emulated ticks in `mach_absolute_time` *are* nanoseconds,
210212
// no scaling needs to happen.
@@ -223,7 +225,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
223225

224226
this.assert_target_os_is_unix("nanosleep");
225227

226-
let duration = match this.read_timespec(&this.deref_operand(req_op)?)? {
228+
let req = this.deref_operand_as(req_op, this.libc_ty_layout("timespec"))?;
229+
230+
let duration = match this.read_timespec(&req)? {
227231
Some(duration) => duration,
228232
None => {
229233
let einval = this.eval_libc("EINVAL");

Diff for: src/shims/unix/foreign_items.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
259259
// Thread-local storage
260260
"pthread_key_create" => {
261261
let [key, dtor] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
262-
let key_place = this.deref_operand(key)?;
262+
let key_place = this.deref_operand_as(key, this.libc_ty_layout("pthread_key_t"))?;
263263
let dtor = this.read_pointer(dtor)?;
264264

265265
// Extract the function type out of the signature (that seems easier than constructing it ourselves).
@@ -520,7 +520,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
520520
// Hence we can mostly ignore the input `attr_place`.
521521
let [attr_place, addr_place, size_place] =
522522
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
523-
let _attr_place = this.deref_operand(attr_place)?;
523+
let _attr_place = this.deref_operand_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?;
524524
let addr_place = this.deref_operand(addr_place)?;
525525
let size_place = this.deref_operand(size_place)?;
526526

@@ -563,7 +563,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
563563
this.check_no_isolation("`getpwuid_r`")?;
564564

565565
let uid = this.read_scalar(uid)?.to_u32()?;
566-
let pwd = this.deref_operand(pwd)?;
566+
let pwd = this.deref_operand_as(pwd, this.libc_ty_layout("passwd"))?;
567567
let buf = this.read_pointer(buf)?;
568568
let buflen = this.read_target_usize(buflen)?;
569569
let result = this.deref_operand(result)?;

Diff for: src/shims/unix/fs.rs

+4-13
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,8 @@ trait EvalContextExtPrivate<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx
344344
let (created_sec, created_nsec) = metadata.created.unwrap_or((0, 0));
345345
let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0));
346346

347-
let buf = this.deref_operand(buf_op)?;
347+
let buf = this.deref_operand_as(buf_op, this.libc_ty_layout("stat"))?;
348+
348349
this.write_int_fields_named(
349350
&[
350351
("st_dev", 0),
@@ -1013,15 +1014,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
10131014
return Ok(-1);
10141015
}
10151016

1016-
// Under normal circumstances, we would use `deref_operand(statxbuf_op)` to produce a
1017-
// proper `MemPlace` and then write the results of this function to it. However, the
1018-
// `syscall` function is untyped. This means that all the `statx` parameters are provided
1019-
// as `isize`s instead of having the proper types. Thus, we have to recover the layout of
1020-
// `statxbuf_op` by using the `libc::statx` struct type.
1021-
let statxbuf = {
1022-
let statx_layout = this.libc_ty_layout("statx");
1023-
MPlaceTy::from_aligned_ptr(statxbuf_ptr, statx_layout)
1024-
};
1017+
let statxbuf = this.deref_operand_as(statxbuf_op, this.libc_ty_layout("statx"))?;
10251018

10261019
let path = this.read_path_from_c_str(pathname_ptr)?.into_owned();
10271020
// See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
@@ -1427,7 +1420,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
14271420
// pub d_name: [c_char; 1024],
14281421
// }
14291422

1430-
let entry_place = this.deref_operand(entry_op)?;
1423+
let entry_place = this.deref_operand_as(entry_op, this.libc_ty_layout("dirent"))?;
14311424
let name_place = this.mplace_field(&entry_place, 5)?;
14321425

14331426
let file_name = dir_entry.file_name(); // not a Path as there are no separators!
@@ -1443,8 +1436,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
14431436
);
14441437
}
14451438

1446-
let entry_place = this.deref_operand(entry_op)?;
1447-
14481439
// If the host is a Unix system, fill in the inode number with its real value.
14491440
// If not, use 0 as a fallback value.
14501441
#[cfg(unix)]

Diff for: src/shims/unix/linux/fd.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
7171
let epoll_ctl_del = this.eval_libc_i32("EPOLL_CTL_DEL");
7272

7373
if op == epoll_ctl_add || op == epoll_ctl_mod {
74-
let event = this.deref_operand(event)?;
74+
let event = this.deref_operand_as(event, this.libc_ty_layout("epoll_event"))?;
7575

7676
let events = this.mplace_field(&event, 0)?;
7777
let events = this.read_scalar(&events.into())?.to_u32()?;

Diff for: src/shims/unix/linux/foreign_items.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
191191
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
192192
this.read_scalar(pid)?.to_i32()?;
193193
this.read_target_usize(cpusetsize)?;
194-
this.deref_operand(mask)?;
194+
this.deref_operand_as(mask, this.libc_ty_layout("cpu_set_t"))?;
195195
// FIXME: we just return an error; `num_cpus` then falls back to `sysconf`.
196196
let einval = this.eval_libc("EINVAL");
197197
this.set_last_error(einval)?;

Diff for: src/shims/unix/linux/sync.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,10 @@ pub fn futex<'tcx>(
8585
return Ok(());
8686
}
8787

88-
// `deref_operand` but not actually dereferencing the ptr yet (it might be NULL!).
89-
let timeout = this.ref_to_mplace(&this.read_immediate(&args[3])?)?;
88+
let timeout = this.deref_pointer_as(
89+
&this.read_immediate(&args[3])?,
90+
this.libc_ty_layout("timespec"),
91+
)?;
9092
let timeout_time = if this.ptr_is_null(timeout.ptr)? {
9193
None
9294
} else {

0 commit comments

Comments
 (0)