Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit e57f309

Browse files
committedAug 5, 2024
Auto merge of #128699 - RalfJung:miri-sync, r=RalfJung
Miri subtree update r? `@ghost`
2 parents f7eefec + f6edc8a commit e57f309

File tree

34 files changed

+981
-626
lines changed

34 files changed

+981
-626
lines changed
 

‎Cargo.lock

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2282,6 +2282,7 @@ dependencies = [
22822282
"smallvec",
22832283
"tempfile",
22842284
"ui_test 0.21.2",
2285+
"windows-sys 0.52.0",
22852286
]
22862287

22872288
[[package]]
@@ -3156,9 +3157,9 @@ dependencies = [
31563157

31573158
[[package]]
31583159
name = "rustc-build-sysroot"
3159-
version = "0.5.2"
3160+
version = "0.5.3"
31603161
source = "registry+https://github.com/rust-lang/crates.io-index"
3161-
checksum = "fa3ca63cc537c1cb69e4c2c0afc5fda2ccd36ac84c97d5a4ae05e69b1c834afb"
3162+
checksum = "2471f8f296262437d7e848e527b4210b44a96e53a3b4435b890227ce3e6da106"
31623163
dependencies = [
31633164
"anyhow",
31643165
"rustc_version",

‎src/tools/miri/Cargo.lock

Lines changed: 135 additions & 102 deletions
Large diffs are not rendered by default.

‎src/tools/miri/Cargo.toml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,12 @@ default-run = "miri"
99
edition = "2021"
1010

1111
[lib]
12-
test = true # we have unit tests
12+
test = true # we have unit tests
1313
doctest = false # but no doc tests
1414

1515
[[bin]]
1616
name = "miri"
17-
test = false # we have no unit tests
17+
test = false # we have no unit tests
1818
doctest = false # and no doc tests
1919

2020
[dependencies]
@@ -42,6 +42,13 @@ libc = "0.2"
4242
libffi = "3.2.0"
4343
libloading = "0.8"
4444

45+
[target.'cfg(target_family = "windows")'.dependencies]
46+
windows-sys = { version = "0.52", features = [
47+
"Win32_Foundation",
48+
"Win32_System_IO",
49+
"Win32_Storage_FileSystem",
50+
] }
51+
4552
[dev-dependencies]
4653
colored = "2"
4754
ui_test = "0.21.1"

‎src/tools/miri/README.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,19 @@ Miri provides some `extern` functions that programs can import to access
476476
Miri-specific functionality. They are declared in
477477
[/tests/utils/miri\_extern.rs](/tests/utils/miri_extern.rs).
478478

479+
## Entry point for no-std binaries
480+
481+
Binaries that do not use the standard library are expected to declare a function like this so that
482+
Miri knows where it is supposed to start execution:
483+
484+
```rust
485+
#[cfg(miri)]
486+
#[no_mangle]
487+
fn miri_start(argc: isize, argv: *const *const u8) -> isize {
488+
// Call the actual start function that your project implements, based on your target's conventions.
489+
}
490+
```
491+
479492
## Contributing and getting help
480493

481494
If you want to contribute to Miri, great! Please check out our

‎src/tools/miri/cargo-miri/Cargo.lock

Lines changed: 88 additions & 65 deletions
Original file line numberDiff line numberDiff line change

‎src/tools/miri/cargo-miri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ directories = "5"
1818
rustc_version = "0.4"
1919
serde_json = "1.0.40"
2020
cargo_metadata = "0.18.0"
21-
rustc-build-sysroot = "0.5.2"
21+
rustc-build-sysroot = "0.5.3"
2222

2323
# Enable some feature flags that dev-dependencies need but dependencies
2424
# do not. This makes `./miri install` after `./miri build` faster.

‎src/tools/miri/miri-script/src/commands.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,10 @@ impl Command {
355355
let head = cmd!(sh, "git rev-parse HEAD").read()?;
356356
let fetch_head = cmd!(sh, "git rev-parse FETCH_HEAD").read()?;
357357
if head != fetch_head {
358-
bail!("Josh created a non-roundtrip push! Do NOT merge this into rustc!");
358+
bail!(
359+
"Josh created a non-roundtrip push! Do NOT merge this into rustc!\n\
360+
Expected {head}, got {fetch_head}."
361+
);
359362
}
360363
println!(
361364
"Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:"

‎src/tools/miri/rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
a526d7ce45fd2284e0e7c7556ccba2425b9d25e5
1+
29e924841f06bb181d87494eba2783761bc1ddec

‎src/tools/miri/src/bin/miri.rs

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ extern crate tracing;
1414
extern crate rustc_data_structures;
1515
extern crate rustc_driver;
1616
extern crate rustc_hir;
17+
extern crate rustc_hir_analysis;
1718
extern crate rustc_interface;
1819
extern crate rustc_log;
1920
extern crate rustc_metadata;
2021
extern crate rustc_middle;
2122
extern crate rustc_session;
23+
extern crate rustc_span;
24+
extern crate rustc_target;
2225

2326
use std::env::{self, VarError};
2427
use std::num::NonZero;
@@ -29,20 +32,25 @@ use tracing::debug;
2932

3033
use rustc_data_structures::sync::Lrc;
3134
use rustc_driver::Compilation;
35+
use rustc_hir::def_id::LOCAL_CRATE;
3236
use rustc_hir::{self as hir, Node};
37+
use rustc_hir_analysis::check::check_function_signature;
3338
use rustc_interface::interface::Config;
3439
use rustc_middle::{
3540
middle::{
3641
codegen_fn_attrs::CodegenFnAttrFlags,
3742
exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel},
3843
},
3944
query::LocalCrate,
40-
ty::TyCtxt,
45+
traits::{ObligationCause, ObligationCauseCode},
46+
ty::{self, Ty, TyCtxt},
4147
util::Providers,
4248
};
43-
use rustc_session::config::{CrateType, ErrorOutputType, OptLevel};
49+
use rustc_session::config::{CrateType, EntryFnType, ErrorOutputType, OptLevel};
4450
use rustc_session::search_paths::PathKind;
4551
use rustc_session::{CtfeBacktrace, EarlyDiagCtxt};
52+
use rustc_span::def_id::DefId;
53+
use rustc_target::spec::abi::Abi;
4654

4755
use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields, ValidationMode};
4856

@@ -82,11 +90,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
8290
tcx.dcx().fatal("miri only makes sense on bin crates");
8391
}
8492

85-
let (entry_def_id, entry_type) = if let Some(entry_def) = tcx.entry_fn(()) {
86-
entry_def
87-
} else {
88-
tcx.dcx().fatal("miri can only run programs that have a main function");
89-
};
93+
let (entry_def_id, entry_type) = entry_fn(tcx);
9094
let mut config = self.miri_config.clone();
9195

9296
// Add filename to `miri` arguments.
@@ -351,6 +355,56 @@ fn jemalloc_magic() {
351355
}
352356
}
353357

358+
fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, EntryFnType) {
359+
if let Some(entry_def) = tcx.entry_fn(()) {
360+
return entry_def;
361+
}
362+
// Look for a symbol in the local crate named `miri_start`, and treat that as the entry point.
363+
let sym = tcx.exported_symbols(LOCAL_CRATE).iter().find_map(|(sym, _)| {
364+
if sym.symbol_name_for_local_instance(tcx).name == "miri_start" { Some(sym) } else { None }
365+
});
366+
if let Some(ExportedSymbol::NonGeneric(id)) = sym {
367+
let start_def_id = id.expect_local();
368+
let start_span = tcx.def_span(start_def_id);
369+
370+
let expected_sig = ty::Binder::dummy(tcx.mk_fn_sig(
371+
[tcx.types.isize, Ty::new_imm_ptr(tcx, Ty::new_imm_ptr(tcx, tcx.types.u8))],
372+
tcx.types.isize,
373+
false,
374+
hir::Safety::Safe,
375+
Abi::Rust,
376+
));
377+
378+
let correct_func_sig = check_function_signature(
379+
tcx,
380+
ObligationCause::new(start_span, start_def_id, ObligationCauseCode::Misc),
381+
*id,
382+
expected_sig,
383+
)
384+
.is_ok();
385+
386+
if correct_func_sig {
387+
(*id, EntryFnType::Start)
388+
} else {
389+
tcx.dcx().fatal(
390+
"`miri_start` must have the following signature:\n\
391+
fn miri_start(argc: isize, argv: *const *const u8) -> isize",
392+
);
393+
}
394+
} else {
395+
tcx.dcx().fatal(
396+
"Miri can only run programs that have a main function.\n\
397+
Alternatively, you can export a `miri_start` function:\n\
398+
\n\
399+
#[cfg(miri)]\n\
400+
#[no_mangle]\n\
401+
fn miri_start(argc: isize, argv: *const *const u8) -> isize {\
402+
\n // Call the actual start function that your project implements, based on your target's conventions.\n\
403+
}"
404+
);
405+
}
406+
}
407+
354408
fn main() {
355409
#[cfg(any(target_os = "linux", target_os = "macos"))]
356410
jemalloc_magic();

‎src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,8 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
673673
// attempt to use it for a non-zero-sized access.
674674
// Dangling slices are a common case here; it's valid to get their length but with raw
675675
// pointer tagging for example all calls to get_unchecked on them are invalid.
676-
if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr(), 0) {
676+
if let Ok((alloc_id, base_offset, orig_tag)) = this.ptr_try_get_alloc_id(place.ptr(), 0)
677+
{
677678
log_creation(this, Some((alloc_id, base_offset, orig_tag)))?;
678679
// Still give it the new provenance, it got retagged after all.
679680
return Ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));

‎src/tools/miri/src/machine.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -932,7 +932,10 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
932932
ecx.machine.validation != ValidationMode::No
933933
}
934934
#[inline(always)]
935-
fn enforce_validity_recursively(ecx: &InterpCx<'tcx, Self>, _layout: TyAndLayout<'tcx>) -> bool {
935+
fn enforce_validity_recursively(
936+
ecx: &InterpCx<'tcx, Self>,
937+
_layout: TyAndLayout<'tcx>,
938+
) -> bool {
936939
ecx.machine.validation == ValidationMode::Deep
937940
}
938941

‎src/tools/miri/src/shims/time.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
9393
Ok(Scalar::from_i32(0))
9494
}
9595

96-
fn gettimeofday(&mut self, tv_op: &OpTy<'tcx>, tz_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
96+
fn gettimeofday(
97+
&mut self,
98+
tv_op: &OpTy<'tcx>,
99+
tz_op: &OpTy<'tcx>,
100+
) -> InterpResult<'tcx, Scalar> {
97101
let this = self.eval_context_mut();
98102

99103
this.assert_target_os_is_unix("gettimeofday");
@@ -106,7 +110,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
106110
if !this.ptr_is_null(tz)? {
107111
let einval = this.eval_libc("EINVAL");
108112
this.set_last_error(einval)?;
109-
return Ok(-1);
113+
return Ok(Scalar::from_i32(-1));
110114
}
111115

112116
let duration = system_time_to_duration(&SystemTime::now())?;
@@ -115,7 +119,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
115119

116120
this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &tv)?;
117121

118-
Ok(0)
122+
Ok(Scalar::from_i32(0))
119123
}
120124

121125
// The localtime() function shall convert the time in seconds since the Epoch pointed to by
@@ -308,7 +312,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
308312
&mut self,
309313
req_op: &OpTy<'tcx>,
310314
_rem: &OpTy<'tcx>, // Signal handlers are not supported, so rem will never be written to.
311-
) -> InterpResult<'tcx, i32> {
315+
) -> InterpResult<'tcx, Scalar> {
312316
let this = self.eval_context_mut();
313317

314318
this.assert_target_os_is_unix("nanosleep");
@@ -320,7 +324,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
320324
None => {
321325
let einval = this.eval_libc("EINVAL");
322326
this.set_last_error(einval)?;
323-
return Ok(-1);
327+
return Ok(Scalar::from_i32(-1));
324328
}
325329
};
326330

@@ -333,7 +337,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
333337
@timeout = |_this| { Ok(()) }
334338
),
335339
);
336-
Ok(0)
340+
Ok(Scalar::from_i32(0))
337341
}
338342

339343
#[allow(non_snake_case)]

‎src/tools/miri/src/shims/unix/env.rs

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,10 @@ impl<'tcx> UnixEnvVars<'tcx> {
8181
return Ok(None);
8282
};
8383
// The offset is used to strip the "{name}=" part of the string.
84-
let var_ptr = var_ptr
85-
.wrapping_offset(Size::from_bytes(u64::try_from(name.len()).unwrap().strict_add(1)), ecx);
84+
let var_ptr = var_ptr.wrapping_offset(
85+
Size::from_bytes(u64::try_from(name.len()).unwrap().strict_add(1)),
86+
ecx,
87+
);
8688
Ok(Some(var_ptr))
8789
}
8890

@@ -148,7 +150,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
148150
Ok(var_ptr.unwrap_or_else(Pointer::null))
149151
}
150152

151-
fn setenv(&mut self, name_op: &OpTy<'tcx>, value_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
153+
fn setenv(
154+
&mut self,
155+
name_op: &OpTy<'tcx>,
156+
value_op: &OpTy<'tcx>,
157+
) -> InterpResult<'tcx, Scalar> {
152158
let this = self.eval_context_mut();
153159
this.assert_target_os_is_unix("setenv");
154160

@@ -169,16 +175,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
169175
this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?;
170176
}
171177
this.update_environ()?;
172-
Ok(0) // return zero on success
178+
Ok(Scalar::from_i32(0)) // return zero on success
173179
} else {
174180
// name argument is a null pointer, points to an empty string, or points to a string containing an '=' character.
175181
let einval = this.eval_libc("EINVAL");
176182
this.set_last_error(einval)?;
177-
Ok(-1)
183+
Ok(Scalar::from_i32(-1))
178184
}
179185
}
180186

181-
fn unsetenv(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
187+
fn unsetenv(&mut self, name_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
182188
let this = self.eval_context_mut();
183189
this.assert_target_os_is_unix("unsetenv");
184190

@@ -195,12 +201,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
195201
this.deallocate_ptr(var, None, MiriMemoryKind::Runtime.into())?;
196202
}
197203
this.update_environ()?;
198-
Ok(0)
204+
Ok(Scalar::from_i32(0))
199205
} else {
200206
// name argument is a null pointer, points to an empty string, or points to a string containing an '=' character.
201207
let einval = this.eval_libc("EINVAL");
202208
this.set_last_error(einval)?;
203-
Ok(-1)
209+
Ok(Scalar::from_i32(-1))
204210
}
205211
}
206212

@@ -232,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
232238
Ok(Pointer::null())
233239
}
234240

235-
fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
241+
fn chdir(&mut self, path_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
236242
let this = self.eval_context_mut();
237243
this.assert_target_os_is_unix("chdir");
238244

@@ -242,16 +248,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
242248
this.reject_in_isolation("`chdir`", reject_with)?;
243249
this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
244250

245-
return Ok(-1);
251+
return Ok(Scalar::from_i32(-1));
246252
}
247253

248-
match env::set_current_dir(path) {
249-
Ok(()) => Ok(0),
250-
Err(e) => {
251-
this.set_last_error_from_io_error(e)?;
252-
Ok(-1)
253-
}
254-
}
254+
let result = env::set_current_dir(path).map(|()| 0);
255+
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
255256
}
256257

257258
/// Updates the `environ` static.
@@ -270,18 +271,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
270271
Ok(())
271272
}
272273

273-
fn getpid(&mut self) -> InterpResult<'tcx, i32> {
274+
fn getpid(&mut self) -> InterpResult<'tcx, Scalar> {
274275
let this = self.eval_context_mut();
275276
this.assert_target_os_is_unix("getpid");
276277

277278
// The reason we need to do this wacky of a conversion is because
278279
// `libc::getpid` returns an i32, however, `std::process::id()` return an u32.
279280
// So we un-do the conversion that stdlib does and turn it back into an i32.
280-
#[allow(clippy::cast_possible_wrap)]
281-
Ok(this.get_pid() as i32)
281+
// In `Scalar` representation, these are the same, so we don't need to anything else.
282+
Ok(Scalar::from_u32(this.get_pid()))
282283
}
283284

284-
fn linux_gettid(&mut self) -> InterpResult<'tcx, i32> {
285+
fn linux_gettid(&mut self) -> InterpResult<'tcx, Scalar> {
285286
let this = self.eval_context_ref();
286287
this.assert_target_os("linux", "gettid");
287288

@@ -290,7 +291,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
290291
// Compute a TID for this thread, ensuring that the main thread has PID == TID.
291292
let tid = this.get_pid().strict_add(index);
292293

293-
#[allow(clippy::cast_possible_wrap)]
294-
Ok(tid as i32)
294+
Ok(Scalar::from_u32(tid))
295295
}
296296
}

‎src/tools/miri/src/shims/unix/fd.rs

Lines changed: 102 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@ use rustc_target::abi::Size;
1212
use crate::shims::unix::*;
1313
use crate::*;
1414

15+
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
16+
pub(crate) enum FlockOp {
17+
SharedLock { nonblocking: bool },
18+
ExclusiveLock { nonblocking: bool },
19+
Unlock,
20+
}
21+
1522
/// Represents an open file descriptor.
1623
pub trait FileDescription: std::fmt::Debug + Any {
1724
fn name(&self) -> &'static str;
@@ -77,6 +84,14 @@ pub trait FileDescription: std::fmt::Debug + Any {
7784
throw_unsup_format!("cannot close {}", self.name());
7885
}
7986

87+
fn flock<'tcx>(
88+
&self,
89+
_communicate_allowed: bool,
90+
_op: FlockOp,
91+
) -> InterpResult<'tcx, io::Result<()>> {
92+
throw_unsup_format!("cannot flock {}", self.name());
93+
}
94+
8095
fn is_tty(&self, _communicate_allowed: bool) -> bool {
8196
// Most FDs are not tty's and the consequence of a wrong `false` are minor,
8297
// so we use a default impl here.
@@ -189,9 +204,13 @@ impl FileDescription for NullOutput {
189204
}
190205

191206
#[derive(Clone, Debug)]
192-
pub struct FileDescriptor(Rc<RefCell<Box<dyn FileDescription>>>);
207+
pub struct FileDescriptionRef(Rc<RefCell<Box<dyn FileDescription>>>);
208+
209+
impl FileDescriptionRef {
210+
fn new(fd: impl FileDescription) -> Self {
211+
FileDescriptionRef(Rc::new(RefCell::new(Box::new(fd))))
212+
}
193213

194-
impl FileDescriptor {
195214
pub fn borrow(&self) -> Ref<'_, dyn FileDescription> {
196215
Ref::map(self.0.borrow(), |fd| fd.as_ref())
197216
}
@@ -213,7 +232,7 @@ impl FileDescriptor {
213232
/// The file descriptor table
214233
#[derive(Debug)]
215234
pub struct FdTable {
216-
pub fds: BTreeMap<i32, FileDescriptor>,
235+
fds: BTreeMap<i32, FileDescriptionRef>,
217236
}
218237

219238
impl VisitProvenance for FdTable {
@@ -228,25 +247,25 @@ impl FdTable {
228247
}
229248
pub(crate) fn init(mute_stdout_stderr: bool) -> FdTable {
230249
let mut fds = FdTable::new();
231-
fds.insert_fd(io::stdin());
250+
fds.insert_new(io::stdin());
232251
if mute_stdout_stderr {
233-
assert_eq!(fds.insert_fd(NullOutput), 1);
234-
assert_eq!(fds.insert_fd(NullOutput), 2);
252+
assert_eq!(fds.insert_new(NullOutput), 1);
253+
assert_eq!(fds.insert_new(NullOutput), 2);
235254
} else {
236-
assert_eq!(fds.insert_fd(io::stdout()), 1);
237-
assert_eq!(fds.insert_fd(io::stderr()), 2);
255+
assert_eq!(fds.insert_new(io::stdout()), 1);
256+
assert_eq!(fds.insert_new(io::stderr()), 2);
238257
}
239258
fds
240259
}
241260

242-
/// Insert a file descriptor to the FdTable.
243-
pub fn insert_fd<T: FileDescription>(&mut self, fd: T) -> i32 {
244-
let file_handle = FileDescriptor(Rc::new(RefCell::new(Box::new(fd))));
245-
self.insert_fd_with_min_fd(file_handle, 0)
261+
/// Insert a new file description to the FdTable.
262+
pub fn insert_new(&mut self, fd: impl FileDescription) -> i32 {
263+
let file_handle = FileDescriptionRef::new(fd);
264+
self.insert_ref_with_min_fd(file_handle, 0)
246265
}
247266

248-
/// Insert a new FD that is at least `min_fd`.
249-
pub fn insert_fd_with_min_fd(&mut self, file_handle: FileDescriptor, min_fd: i32) -> i32 {
267+
/// Insert a file description, giving it a file descriptor that is at least `min_fd`.
268+
fn insert_ref_with_min_fd(&mut self, file_handle: FileDescriptionRef, min_fd: i32) -> i32 {
250269
// Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
251270
// between used FDs, the find_map combinator will return it. If the first such unused FD
252271
// is after all other used FDs, the find_map combinator will return None, and we will use
@@ -282,12 +301,12 @@ impl FdTable {
282301
Some(fd.borrow_mut())
283302
}
284303

285-
pub fn dup(&self, fd: i32) -> Option<FileDescriptor> {
304+
pub fn get_ref(&self, fd: i32) -> Option<FileDescriptionRef> {
286305
let fd = self.fds.get(&fd)?;
287306
Some(fd.clone())
288307
}
289308

290-
pub fn remove(&mut self, fd: i32) -> Option<FileDescriptor> {
309+
pub fn remove(&mut self, fd: i32) -> Option<FileDescriptionRef> {
291310
self.fds.remove(&fd)
292311
}
293312

@@ -298,33 +317,67 @@ impl FdTable {
298317

299318
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
300319
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
301-
fn dup(&mut self, old_fd: i32) -> InterpResult<'tcx, i32> {
320+
fn dup(&mut self, old_fd: i32) -> InterpResult<'tcx, Scalar> {
302321
let this = self.eval_context_mut();
303322

304-
let Some(dup_fd) = this.machine.fds.dup(old_fd) else {
305-
return this.fd_not_found();
323+
let Some(dup_fd) = this.machine.fds.get_ref(old_fd) else {
324+
return Ok(Scalar::from_i32(this.fd_not_found()?));
306325
};
307-
Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, 0))
326+
Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, 0)))
308327
}
309328

310-
fn dup2(&mut self, old_fd: i32, new_fd: i32) -> InterpResult<'tcx, i32> {
329+
fn dup2(&mut self, old_fd: i32, new_fd: i32) -> InterpResult<'tcx, Scalar> {
311330
let this = self.eval_context_mut();
312331

313-
let Some(dup_fd) = this.machine.fds.dup(old_fd) else {
314-
return this.fd_not_found();
332+
let Some(dup_fd) = this.machine.fds.get_ref(old_fd) else {
333+
return Ok(Scalar::from_i32(this.fd_not_found()?));
315334
};
316335
if new_fd != old_fd {
317336
// Close new_fd if it is previously opened.
318337
// If old_fd and new_fd point to the same description, then `dup_fd` ensures we keep the underlying file description alive.
319-
if let Some(file_descriptor) = this.machine.fds.fds.insert(new_fd, dup_fd) {
338+
if let Some(file_description) = this.machine.fds.fds.insert(new_fd, dup_fd) {
320339
// Ignore close error (not interpreter's) according to dup2() doc.
321-
file_descriptor.close(this.machine.communicate())?.ok();
340+
file_description.close(this.machine.communicate())?.ok();
322341
}
323342
}
324-
Ok(new_fd)
343+
Ok(Scalar::from_i32(new_fd))
344+
}
345+
346+
fn flock(&mut self, fd: i32, op: i32) -> InterpResult<'tcx, Scalar> {
347+
let this = self.eval_context_mut();
348+
let Some(file_descriptor) = this.machine.fds.get(fd) else {
349+
return Ok(Scalar::from_i32(this.fd_not_found()?));
350+
};
351+
352+
// We need to check that there aren't unsupported options in `op`.
353+
let lock_sh = this.eval_libc_i32("LOCK_SH");
354+
let lock_ex = this.eval_libc_i32("LOCK_EX");
355+
let lock_nb = this.eval_libc_i32("LOCK_NB");
356+
let lock_un = this.eval_libc_i32("LOCK_UN");
357+
358+
use FlockOp::*;
359+
let parsed_op = if op == lock_sh {
360+
SharedLock { nonblocking: false }
361+
} else if op == lock_sh | lock_nb {
362+
SharedLock { nonblocking: true }
363+
} else if op == lock_ex {
364+
ExclusiveLock { nonblocking: false }
365+
} else if op == lock_ex | lock_nb {
366+
ExclusiveLock { nonblocking: true }
367+
} else if op == lock_un {
368+
Unlock
369+
} else {
370+
throw_unsup_format!("unsupported flags {:#x}", op);
371+
};
372+
373+
let result = file_descriptor.flock(this.machine.communicate(), parsed_op)?;
374+
drop(file_descriptor);
375+
// return `0` if flock is successful
376+
let result = result.map(|()| 0i32);
377+
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
325378
}
326379

327-
fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, i32> {
380+
fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
328381
let this = self.eval_context_mut();
329382

330383
if args.len() < 2 {
@@ -342,11 +395,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
342395
// `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
343396
// always sets this flag when opening a file. However we still need to check that the
344397
// file itself is open.
345-
if this.machine.fds.is_fd(fd) {
346-
Ok(this.eval_libc_i32("FD_CLOEXEC"))
398+
Ok(Scalar::from_i32(if this.machine.fds.is_fd(fd) {
399+
this.eval_libc_i32("FD_CLOEXEC")
347400
} else {
348-
this.fd_not_found()
349-
}
401+
this.fd_not_found()?
402+
}))
350403
} else if cmd == this.eval_libc_i32("F_DUPFD")
351404
|| cmd == this.eval_libc_i32("F_DUPFD_CLOEXEC")
352405
{
@@ -362,16 +415,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
362415
}
363416
let start = this.read_scalar(&args[2])?.to_i32()?;
364417

365-
match this.machine.fds.dup(fd) {
366-
Some(dup_fd) => Ok(this.machine.fds.insert_fd_with_min_fd(dup_fd, start)),
367-
None => this.fd_not_found(),
418+
match this.machine.fds.get_ref(fd) {
419+
Some(dup_fd) =>
420+
Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, start))),
421+
None => Ok(Scalar::from_i32(this.fd_not_found()?)),
368422
}
369423
} else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") {
370424
// Reject if isolation is enabled.
371425
if let IsolatedOp::Reject(reject_with) = this.machine.isolated_op {
372426
this.reject_in_isolation("`fcntl`", reject_with)?;
373427
this.set_last_error_from_io_error(ErrorKind::PermissionDenied.into())?;
374-
return Ok(-1);
428+
return Ok(Scalar::from_i32(-1));
375429
}
376430

377431
this.ffullsync_fd(fd)
@@ -385,10 +439,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
385439

386440
let fd = this.read_scalar(fd_op)?.to_i32()?;
387441

388-
let Some(file_descriptor) = this.machine.fds.remove(fd) else {
442+
let Some(file_description) = this.machine.fds.remove(fd) else {
389443
return Ok(Scalar::from_i32(this.fd_not_found()?));
390444
};
391-
let result = file_descriptor.close(this.machine.communicate())?;
445+
let result = file_description.close(this.machine.communicate())?;
392446
// return `0` if close is successful
393447
let result = result.map(|()| 0i32);
394448
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
@@ -416,7 +470,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
416470
buf: Pointer,
417471
count: u64,
418472
offset: Option<i128>,
419-
) -> InterpResult<'tcx, i64> {
473+
) -> InterpResult<'tcx, Scalar> {
420474
let this = self.eval_context_mut();
421475

422476
// Isolation check is done via `FileDescriptor` trait.
@@ -434,9 +488,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
434488
let communicate = this.machine.communicate();
435489

436490
// We temporarily dup the FD to be able to retain mutable access to `this`.
437-
let Some(fd) = this.machine.fds.dup(fd) else {
491+
let Some(fd) = this.machine.fds.get_ref(fd) else {
438492
trace!("read: FD not found");
439-
return this.fd_not_found();
493+
return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
440494
};
441495

442496
trace!("read: FD mapped to {fd:?}");
@@ -450,7 +504,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
450504
let Ok(offset) = u64::try_from(offset) else {
451505
let einval = this.eval_libc("EINVAL");
452506
this.set_last_error(einval)?;
453-
return Ok(-1);
507+
return Ok(Scalar::from_target_isize(-1, this));
454508
};
455509
fd.borrow_mut().pread(communicate, &mut bytes, offset, this)
456510
}
@@ -467,11 +521,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
467521
buf,
468522
bytes[..usize::try_from(read_bytes).unwrap()].iter().copied(),
469523
)?;
470-
Ok(read_bytes)
524+
Ok(Scalar::from_target_isize(read_bytes, this))
471525
}
472526
Err(e) => {
473527
this.set_last_error_from_io_error(e)?;
474-
Ok(-1)
528+
Ok(Scalar::from_target_isize(-1, this))
475529
}
476530
}
477531
}
@@ -482,7 +536,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
482536
buf: Pointer,
483537
count: u64,
484538
offset: Option<i128>,
485-
) -> InterpResult<'tcx, i64> {
539+
) -> InterpResult<'tcx, Scalar> {
486540
let this = self.eval_context_mut();
487541

488542
// Isolation check is done via `FileDescriptor` trait.
@@ -499,8 +553,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
499553

500554
let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
501555
// We temporarily dup the FD to be able to retain mutable access to `this`.
502-
let Some(fd) = this.machine.fds.dup(fd) else {
503-
return this.fd_not_found();
556+
let Some(fd) = this.machine.fds.get_ref(fd) else {
557+
return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
504558
};
505559

506560
let result = match offset {
@@ -509,14 +563,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
509563
let Ok(offset) = u64::try_from(offset) else {
510564
let einval = this.eval_libc("EINVAL");
511565
this.set_last_error(einval)?;
512-
return Ok(-1);
566+
return Ok(Scalar::from_target_isize(-1, this));
513567
};
514568
fd.borrow_mut().pwrite(communicate, &bytes, offset, this)
515569
}
516570
};
517571
drop(fd);
518572

519573
let result = result?.map(|c| i64::try_from(c).unwrap());
520-
this.try_unwrap_io_result(result)
574+
Ok(Scalar::from_target_isize(this.try_unwrap_io_result(result)?, this))
521575
}
522576
}

‎src/tools/miri/src/shims/unix/foreign_items.rs

Lines changed: 80 additions & 73 deletions
Large diffs are not rendered by default.

‎src/tools/miri/src/shims/unix/fs.rs

Lines changed: 164 additions & 74 deletions
Large diffs are not rendered by default.

‎src/tools/miri/src/shims/unix/linux/epoll.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
6464
);
6565
}
6666

67-
let fd = this.machine.fds.insert_fd(Epoll::default());
67+
let fd = this.machine.fds.insert_new(Epoll::default());
6868
Ok(Scalar::from_i32(fd))
6969
}
7070

‎src/tools/miri/src/shims/unix/linux/eventfd.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
178178
throw_unsup_format!("eventfd: encountered unknown unsupported flags {:#x}", flags);
179179
}
180180

181-
let fd = this.machine.fds.insert_fd(Event {
181+
let fd = this.machine.fds.insert_new(Event {
182182
counter: val.into(),
183183
is_nonblock,
184184
clock: VClock::default(),

‎src/tools/miri/src/shims/unix/linux/foreign_items.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
4444
let [dirfd, pathname, flags, mask, statxbuf] =
4545
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
4646
let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
47-
this.write_scalar(Scalar::from_i32(result), dest)?;
47+
this.write_scalar(result, dest)?;
4848
}
4949

5050
// epoll, eventfd
@@ -97,7 +97,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
9797
"gettid" => {
9898
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
9999
let result = this.linux_gettid()?;
100-
this.write_scalar(Scalar::from_i32(result), dest)?;
100+
this.write_scalar(result, dest)?;
101101
}
102102

103103
// Dynamically invoked syscalls
@@ -176,12 +176,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
176176
"__libc_current_sigrtmin" => {
177177
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
178178

179-
this.write_scalar(Scalar::from_i32(SIGRTMIN), dest)?;
179+
this.write_int(SIGRTMIN, dest)?;
180180
}
181181
"__libc_current_sigrtmax" => {
182182
let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
183183

184-
this.write_scalar(Scalar::from_i32(SIGRTMAX), dest)?;
184+
this.write_int(SIGRTMAX, dest)?;
185185
}
186186

187187
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.

‎src/tools/miri/src/shims/unix/socket.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
219219
};
220220

221221
let fds = &mut this.machine.fds;
222-
let sv0 = fds.insert_fd(socketpair_0);
223-
let sv1 = fds.insert_fd(socketpair_1);
222+
let sv0 = fds.insert_new(socketpair_0);
223+
let sv1 = fds.insert_new(socketpair_1);
224224
let sv0 = Scalar::from_int(sv0, sv.layout.size);
225225
let sv1 = Scalar::from_int(sv1, sv.layout.size);
226226

‎src/tools/miri/src/shims/unix/sync.rs

Lines changed: 46 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -363,20 +363,20 @@ fn cond_set_clock_id<'tcx>(
363363

364364
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
365365
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
366-
fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
366+
fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
367367
let this = self.eval_context_mut();
368368

369369
let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT");
370370
mutexattr_set_kind(this, attr_op, default_kind)?;
371371

372-
Ok(0)
372+
Ok(())
373373
}
374374

375375
fn pthread_mutexattr_settype(
376376
&mut self,
377377
attr_op: &OpTy<'tcx>,
378378
kind_op: &OpTy<'tcx>,
379-
) -> InterpResult<'tcx, i32> {
379+
) -> InterpResult<'tcx, Scalar> {
380380
let this = self.eval_context_mut();
381381

382382
let kind = this.read_scalar(kind_op)?.to_i32()?;
@@ -407,13 +407,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
407407
mutexattr_set_kind(this, attr_op, kind)?;
408408
} else {
409409
let einval = this.eval_libc_i32("EINVAL");
410-
return Ok(einval);
410+
return Ok(Scalar::from_i32(einval));
411411
}
412412

413-
Ok(0)
413+
Ok(Scalar::from_i32(0))
414414
}
415415

416-
fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
416+
fn pthread_mutexattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
417417
let this = self.eval_context_mut();
418418

419419
// Destroying an uninit pthread_mutexattr is UB, so check to make sure it's not uninit.
@@ -435,14 +435,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
435435
&this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_mutexattr_t"))?,
436436
)?;
437437

438-
Ok(0)
438+
Ok(())
439439
}
440440

441441
fn pthread_mutex_init(
442442
&mut self,
443443
mutex_op: &OpTy<'tcx>,
444444
attr_op: &OpTy<'tcx>,
445-
) -> InterpResult<'tcx, i32> {
445+
) -> InterpResult<'tcx, ()> {
446446
let this = self.eval_context_mut();
447447

448448
let attr = this.read_pointer(attr_op)?;
@@ -457,7 +457,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
457457

458458
mutex_set_kind(this, mutex_op, kind)?;
459459

460-
Ok(0)
460+
Ok(())
461461
}
462462

463463
fn pthread_mutex_lock(
@@ -501,25 +501,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
501501
Ok(())
502502
}
503503

504-
fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
504+
fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
505505
let this = self.eval_context_mut();
506506

507507
let kind = mutex_get_kind(this, mutex_op)?;
508508
let id = mutex_get_id(this, mutex_op)?;
509509

510-
if this.mutex_is_locked(id) {
510+
Ok(Scalar::from_i32(if this.mutex_is_locked(id) {
511511
let owner_thread = this.mutex_get_owner(id);
512512
if owner_thread != this.active_thread() {
513-
Ok(this.eval_libc_i32("EBUSY"))
513+
this.eval_libc_i32("EBUSY")
514514
} else {
515515
if is_mutex_kind_default(this, kind)?
516516
|| is_mutex_kind_normal(this, kind)?
517517
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
518518
{
519-
Ok(this.eval_libc_i32("EBUSY"))
519+
this.eval_libc_i32("EBUSY")
520520
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
521521
this.mutex_lock(id);
522-
Ok(0)
522+
0
523523
} else {
524524
throw_unsup_format!(
525525
"called pthread_mutex_trylock on an unsupported type of mutex"
@@ -529,19 +529,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
529529
} else {
530530
// The mutex is unlocked. Let's lock it.
531531
this.mutex_lock(id);
532-
Ok(0)
533-
}
532+
0
533+
}))
534534
}
535535

536-
fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
536+
fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
537537
let this = self.eval_context_mut();
538538

539539
let kind = mutex_get_kind(this, mutex_op)?;
540540
let id = mutex_get_id(this, mutex_op)?;
541541

542542
if let Some(_old_locked_count) = this.mutex_unlock(id)? {
543543
// The mutex was locked by the current thread.
544-
Ok(0)
544+
Ok(Scalar::from_i32(0))
545545
} else {
546546
// The mutex was locked by another thread or not locked at all. See
547547
// the “Unlock When Not Owner” column in
@@ -557,14 +557,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
557557
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
558558
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
559559
{
560-
Ok(this.eval_libc_i32("EPERM"))
560+
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM")))
561561
} else {
562562
throw_unsup_format!("called pthread_mutex_unlock on an unsupported type of mutex");
563563
}
564564
}
565565
}
566566

567-
fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
567+
fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
568568
let this = self.eval_context_mut();
569569

570570
let id = mutex_get_id(this, mutex_op)?;
@@ -583,7 +583,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
583583
)?;
584584
// FIXME: delete interpreter state associated with this mutex.
585585

586-
Ok(0)
586+
Ok(())
587587
}
588588

589589
fn pthread_rwlock_rdlock(
@@ -605,16 +605,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
605605
Ok(())
606606
}
607607

608-
fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
608+
fn pthread_rwlock_tryrdlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
609609
let this = self.eval_context_mut();
610610

611611
let id = rwlock_get_id(this, rwlock_op)?;
612612

613613
if this.rwlock_is_write_locked(id) {
614-
Ok(this.eval_libc_i32("EBUSY"))
614+
Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
615615
} else {
616616
this.rwlock_reader_lock(id);
617-
Ok(0)
617+
Ok(Scalar::from_i32(0))
618618
}
619619
}
620620

@@ -649,35 +649,33 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
649649
Ok(())
650650
}
651651

652-
fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
652+
fn pthread_rwlock_trywrlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
653653
let this = self.eval_context_mut();
654654

655655
let id = rwlock_get_id(this, rwlock_op)?;
656656

657657
if this.rwlock_is_locked(id) {
658-
Ok(this.eval_libc_i32("EBUSY"))
658+
Ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
659659
} else {
660660
this.rwlock_writer_lock(id);
661-
Ok(0)
661+
Ok(Scalar::from_i32(0))
662662
}
663663
}
664664

665-
fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
665+
fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
666666
let this = self.eval_context_mut();
667667

668668
let id = rwlock_get_id(this, rwlock_op)?;
669669

670670
#[allow(clippy::if_same_then_else)]
671-
if this.rwlock_reader_unlock(id)? {
672-
Ok(0)
673-
} else if this.rwlock_writer_unlock(id)? {
674-
Ok(0)
671+
if this.rwlock_reader_unlock(id)? || this.rwlock_writer_unlock(id)? {
672+
Ok(())
675673
} else {
676674
throw_ub_format!("unlocked an rwlock that was not locked by the active thread");
677675
}
678676
}
679677

680-
fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
678+
fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
681679
let this = self.eval_context_mut();
682680

683681
let id = rwlock_get_id(this, rwlock_op)?;
@@ -695,10 +693,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
695693
)?;
696694
// FIXME: delete interpreter state associated with this rwlock.
697695

698-
Ok(0)
696+
Ok(())
699697
}
700698

701-
fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
699+
fn pthread_condattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
702700
let this = self.eval_context_mut();
703701

704702
// no clock attribute on macOS
@@ -710,7 +708,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
710708
condattr_set_clock_id(this, attr_op, default_clock_id)?;
711709
}
712710

713-
Ok(0)
711+
Ok(())
714712
}
715713

716714
fn pthread_condattr_setclock(
@@ -737,16 +735,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
737735
&mut self,
738736
attr_op: &OpTy<'tcx>,
739737
clk_id_op: &OpTy<'tcx>,
740-
) -> InterpResult<'tcx, Scalar> {
738+
) -> InterpResult<'tcx, ()> {
741739
let this = self.eval_context_mut();
742740

743741
let clock_id = condattr_get_clock_id(this, attr_op)?;
744742
this.write_scalar(Scalar::from_i32(clock_id), &this.deref_pointer(clk_id_op)?)?;
745743

746-
Ok(Scalar::from_i32(0))
744+
Ok(())
747745
}
748746

749-
fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
747+
fn pthread_condattr_destroy(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
750748
let this = self.eval_context_mut();
751749

752750
// Destroying an uninit pthread_condattr is UB, so check to make sure it's not uninit.
@@ -761,14 +759,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
761759
&this.deref_pointer_as(attr_op, this.libc_ty_layout("pthread_condattr_t"))?,
762760
)?;
763761

764-
Ok(0)
762+
Ok(())
765763
}
766764

767765
fn pthread_cond_init(
768766
&mut self,
769767
cond_op: &OpTy<'tcx>,
770768
attr_op: &OpTy<'tcx>,
771-
) -> InterpResult<'tcx, i32> {
769+
) -> InterpResult<'tcx, ()> {
772770
let this = self.eval_context_mut();
773771

774772
let attr = this.read_pointer(attr_op)?;
@@ -784,21 +782,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
784782

785783
cond_set_clock_id(this, cond_op, clock_id)?;
786784

787-
Ok(0)
785+
Ok(())
788786
}
789787

790-
fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
788+
fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
791789
let this = self.eval_context_mut();
792790
let id = cond_get_id(this, cond_op)?;
793791
this.condvar_signal(id)?;
794-
Ok(0)
792+
Ok(())
795793
}
796794

797-
fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
795+
fn pthread_cond_broadcast(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
798796
let this = self.eval_context_mut();
799797
let id = cond_get_id(this, cond_op)?;
800798
while this.condvar_signal(id)? {}
801-
Ok(0)
799+
Ok(())
802800
}
803801

804802
fn pthread_cond_wait(
@@ -869,7 +867,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
869867
Ok(())
870868
}
871869

872-
fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
870+
fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
873871
let this = self.eval_context_mut();
874872

875873
let id = cond_get_id(this, cond_op)?;
@@ -885,6 +883,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
885883
this.write_uninit(&this.deref_pointer_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?)?;
886884
// FIXME: delete interpreter state associated with this condvar.
887885

888-
Ok(0)
886+
Ok(())
889887
}
890888
}

‎src/tools/miri/src/shims/unix/thread.rs

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1010
_attr: &OpTy<'tcx>,
1111
start_routine: &OpTy<'tcx>,
1212
arg: &OpTy<'tcx>,
13-
) -> InterpResult<'tcx, i32> {
13+
) -> InterpResult<'tcx, ()> {
1414
let this = self.eval_context_mut();
1515

1616
let thread_info_place = this.deref_pointer_as(thread, this.libc_ty_layout("pthread_t"))?;
@@ -27,14 +27,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
2727
this.layout_of(this.tcx.types.usize)?,
2828
)?;
2929

30-
Ok(0)
30+
Ok(())
3131
}
3232

33-
fn pthread_join(
34-
&mut self,
35-
thread: &OpTy<'tcx>,
36-
retval: &OpTy<'tcx>,
37-
) -> InterpResult<'tcx, i32> {
33+
fn pthread_join(&mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
3834
let this = self.eval_context_mut();
3935

4036
if !this.ptr_is_null(this.read_pointer(retval)?)? {
@@ -45,10 +41,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
4541
let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
4642
this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?;
4743

48-
Ok(0)
44+
Ok(())
4945
}
5046

51-
fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, i32> {
47+
fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
5248
let this = self.eval_context_mut();
5349

5450
let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
@@ -57,7 +53,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
5753
/*allow_terminated_joined*/ false,
5854
)?;
5955

60-
Ok(0)
56+
Ok(())
6157
}
6258

6359
fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> {
@@ -113,11 +109,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
113109
Ok(if success { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") })
114110
}
115111

116-
fn sched_yield(&mut self) -> InterpResult<'tcx, i32> {
112+
fn sched_yield(&mut self) -> InterpResult<'tcx, ()> {
117113
let this = self.eval_context_mut();
118114

119115
this.yield_active_thread();
120116

121-
Ok(0)
117+
Ok(())
122118
}
123119
}

‎src/tools/miri/src/shims/windows/env.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,11 +197,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
197197
}
198198

199199
#[allow(non_snake_case)]
200-
fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, u32> {
200+
fn GetCurrentProcessId(&mut self) -> InterpResult<'tcx, Scalar> {
201201
let this = self.eval_context_mut();
202202
this.assert_target_os("windows", "GetCurrentProcessId");
203203

204-
Ok(this.get_pid())
204+
Ok(Scalar::from_u32(this.get_pid()))
205205
}
206206

207207
#[allow(non_snake_case)]

‎src/tools/miri/src/shims/windows/foreign_items.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
141141
"GetCurrentProcessId" => {
142142
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
143143
let result = this.GetCurrentProcessId()?;
144-
this.write_int(result, dest)?;
144+
this.write_scalar(result, dest)?;
145145
}
146146

147147
// File related shims
@@ -372,7 +372,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
372372
this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
373373

374374
// Return success (`1`).
375-
this.write_scalar(Scalar::from_i32(1), dest)?;
375+
this.write_int(1, dest)?;
376376
}
377377

378378
// Access to command-line arguments
@@ -563,7 +563,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
563563
let ptr = this.read_pointer(ptr)?;
564564
let len = this.read_target_usize(len)?;
565565
this.gen_random(ptr, len)?;
566-
this.write_scalar(Scalar::from_i32(1), dest)?;
566+
this.write_int(1, dest)?;
567567
}
568568
"BCryptGenRandom" => {
569569
// used by getrandom 0.2
@@ -627,7 +627,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
627627

628628
this.CloseHandle(handle)?;
629629

630-
this.write_scalar(Scalar::from_u32(1), dest)?;
630+
this.write_int(1, dest)?;
631631
}
632632
"GetModuleFileNameW" => {
633633
let [handle, filename, size] =

‎src/tools/miri/test_dependencies/Cargo.lock

Lines changed: 76 additions & 141 deletions
Large diffs are not rendered by default.

‎src/tools/miri/tests/fail-dep/tokio/sleep.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: unsupported operation: returning ready events from epoll_wait is not yet
22
--> CARGO_REGISTRY/.../epoll.rs:LL:CC
33
|
44
LL | / syscall!(epoll_wait(
5-
LL | | self.ep,
5+
LL | | self.ep.as_raw_fd(),
66
LL | | events.as_mut_ptr(),
77
LL | | events.capacity() as i32,
88
LL | | timeout,
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@compile-flags: -Cpanic=abort
2+
//@error-in-other-file: `miri_start` must have the following signature:
3+
#![no_main]
4+
#![no_std]
5+
6+
use core::fmt::Write;
7+
8+
#[path = "../utils/mod.no_std.rs"]
9+
mod utils;
10+
11+
#[no_mangle]
12+
fn miri_start() -> isize {
13+
//~^ ERROR: mismatched types
14+
writeln!(utils::MiriStdout, "Hello from miri_start!").unwrap();
15+
0
16+
}
17+
18+
#[panic_handler]
19+
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
20+
loop {}
21+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/miri_start_wrong_sig.rs:LL:CC
3+
|
4+
LL | fn miri_start() -> isize {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
6+
|
7+
= note: expected signature `fn(isize, *const *const u8) -> _`
8+
found signature `fn() -> _`
9+
10+
error: `miri_start` must have the following signature:
11+
fn miri_start(argc: isize, argv: *const *const u8) -> isize
12+
13+
error: aborting due to 2 previous errors
14+
15+
For more information about this error, try `rustc --explain E0308`.

‎src/tools/miri/tests/fail/no_main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
//@error-in-other-file: miri can only run programs that have a main function
1+
//@error-in-other-file: Miri can only run programs that have a main function.
22
#![no_main]
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
error: miri can only run programs that have a main function
1+
error: Miri can only run programs that have a main function.
2+
Alternatively, you can export a `miri_start` function:
3+
4+
#[cfg(miri)]
5+
#[no_mangle]
6+
fn miri_start(argc: isize, argv: *const *const u8) -> isize {
7+
// Call the actual start function that your project implements, based on your target's conventions.
8+
}
29

310
error: aborting due to 1 previous error
411

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//@ignore-target-windows: File handling is not implemented yet
2+
//@compile-flags: -Zmiri-disable-isolation
3+
4+
use std::{fs::File, io::Error, os::fd::AsRawFd};
5+
6+
#[path = "../../utils/mod.rs"]
7+
mod utils;
8+
9+
fn main() {
10+
let bytes = b"Hello, World!\n";
11+
let path = utils::prepare_with_content("miri_test_fs_shared_lock.txt", bytes);
12+
13+
let files: Vec<File> = (0..3).map(|_| File::open(&path).unwrap()).collect();
14+
15+
// Test that we can apply many shared locks
16+
for file in files.iter() {
17+
let fd = file.as_raw_fd();
18+
let ret = unsafe { libc::flock(fd, libc::LOCK_SH) };
19+
if ret != 0 {
20+
panic!("flock error: {}", Error::last_os_error());
21+
}
22+
}
23+
24+
// Test that shared lock prevents exclusive lock
25+
{
26+
let fd = files[0].as_raw_fd();
27+
let ret = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
28+
assert_eq!(ret, -1);
29+
let err = Error::last_os_error().raw_os_error().unwrap();
30+
assert_eq!(err, libc::EWOULDBLOCK);
31+
}
32+
33+
// Unlock shared lock
34+
for file in files.iter() {
35+
let fd = file.as_raw_fd();
36+
let ret = unsafe { libc::flock(fd, libc::LOCK_UN) };
37+
if ret != 0 {
38+
panic!("flock error: {}", Error::last_os_error());
39+
}
40+
}
41+
42+
// Take exclusive lock
43+
{
44+
let fd = files[0].as_raw_fd();
45+
let ret = unsafe { libc::flock(fd, libc::LOCK_EX) };
46+
assert_eq!(ret, 0);
47+
}
48+
49+
// Test that shared lock prevents exclusive and shared locks
50+
{
51+
let fd = files[1].as_raw_fd();
52+
let ret = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
53+
assert_eq!(ret, -1);
54+
let err = Error::last_os_error().raw_os_error().unwrap();
55+
assert_eq!(err, libc::EWOULDBLOCK);
56+
57+
let fd = files[2].as_raw_fd();
58+
let ret = unsafe { libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB) };
59+
assert_eq!(ret, -1);
60+
let err = Error::last_os_error().raw_os_error().unwrap();
61+
assert_eq!(err, libc::EWOULDBLOCK);
62+
}
63+
64+
// Unlock exclusive lock
65+
{
66+
let fd = files[0].as_raw_fd();
67+
let ret = unsafe { libc::flock(fd, libc::LOCK_UN) };
68+
assert_eq!(ret, 0);
69+
}
70+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@compile-flags: -Cpanic=abort
2+
#![no_main]
3+
#![no_std]
4+
5+
use core::fmt::Write;
6+
7+
#[path = "../utils/mod.no_std.rs"]
8+
mod utils;
9+
10+
#[no_mangle]
11+
fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
12+
writeln!(utils::MiriStdout, "Hello from miri_start!").unwrap();
13+
0
14+
}
15+
16+
#[panic_handler]
17+
fn panic_handler(_: &core::panic::PanicInfo) -> ! {
18+
loop {}
19+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello from miri_start!

‎src/tools/miri/tests/ui.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ fn miri_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) ->
7373
stdout_filters: stdout_filters().into(),
7474
mode,
7575
program,
76-
out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap()).join("ui"),
76+
out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri_ui"),
7777
edition: Some("2021".into()), // keep in sync with `./miri run`
7878
threads: std::env::var("MIRI_TEST_THREADS")
7979
.ok()

0 commit comments

Comments
 (0)
Please sign in to comment.