Auto merge of #104617 - RalfJung:miri, r=RalfJung

update Miri

r? `@thomcc` for the lib changes (removing a `cfg(miri)` that is no longer needed)
This commit is contained in:
bors 2022-11-20 12:57:48 +00:00
commit 911cbf8e46
17 changed files with 154 additions and 117 deletions

View File

@ -2330,7 +2330,6 @@ dependencies = [
"regex",
"rustc-workspace-hack",
"rustc_version",
"shell-escape",
"smallvec",
"ui_test",
]

View File

@ -150,7 +150,7 @@ impl From<libc::timespec> for Timespec {
}
#[cfg(any(
all(target_os = "macos", any(not(target_arch = "aarch64"), miri)),
all(target_os = "macos", any(not(target_arch = "aarch64"))),
target_os = "ios",
target_os = "watchos"
))]
@ -270,7 +270,7 @@ mod inner {
}
#[cfg(not(any(
all(target_os = "macos", any(not(target_arch = "aarch64"), miri)),
all(target_os = "macos", any(not(target_arch = "aarch64"))),
target_os = "ios",
target_os = "watchos"
)))]

View File

@ -383,7 +383,6 @@ dependencies = [
"regex",
"rustc-workspace-hack",
"rustc_version",
"shell-escape",
"smallvec",
"ui_test",
]
@ -632,12 +631,6 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "shell-escape"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]]
name = "smallvec"
version = "1.9.0"

View File

@ -21,7 +21,6 @@ doctest = false # and no doc tests
getrandom = { version = "0.2", features = ["std"] }
env_logger = "0.9"
log = "0.4"
shell-escape = "0.1.4"
rand = "0.8"
smallvec = "1.7"

View File

@ -1,10 +1,5 @@
# Miri
[![Actions build status][actions-badge]][actions-url]
[actions-badge]: https://github.com/rust-lang/miri/workflows/CI/badge.svg?branch=master
[actions-url]: https://github.com/rust-lang/miri/actions
An experimental interpreter for [Rust][rust]'s
[mid-level intermediate representation][mir] (MIR). It can run binaries and
test suites of cargo projects and detect certain classes of
@ -200,7 +195,7 @@ randomness that is used to determine allocation base addresses. The following
snippet calls Miri in a loop with different values for the seed:
```
for SEED in $({ echo obase=16; seq 0 255; } | bc); do
for SEED in $(seq 0 255); do
echo "Trying seed: $SEED"
MIRIFLAGS=-Zmiri-seed=$SEED cargo miri test || { echo "Failing seed: $SEED"; break; };
done
@ -308,7 +303,7 @@ environment variable. We first document the most relevant and most commonly used
tell what it is doing when a program just keeps running. You can customize how frequently the
report is printed via `-Zmiri-report-progress=<blocks>`, which prints the report every N basic
blocks.
* `-Zmiri-seed=<hex>` configures the seed of the RNG that Miri uses to resolve non-determinism. This
* `-Zmiri-seed=<num>` configures the seed of the RNG that Miri uses to resolve non-determinism. This
RNG is used to pick base addresses for allocations, to determine preemption and failure of
`compare_exchange_weak`, and to control store buffering for weak memory emulation. When isolation
is enabled (the default), this is also used to emulate system entropy. The default seed is 0. You

View File

@ -48,10 +48,10 @@ Update and activate the rustup toolchain 'miri' to the commit given in the
`rustup-toolchain-install-master` must be installed for this to work. Any extra
flags are passed to `rustup-toolchain-install-master`.
./miri rustc-pull:
Pull and merge Miri changes from the rustc repo. The fetched commit is stored in
the `rust-version` file, so the next `./miri toolchain` will install the rustc
we just pulled.
./miri rustc-pull <commit>:
Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
rustc commit. The fetched commit is stored in the `rust-version` file, so the
next `./miri toolchain` will install the rustc that just got pulled.
./miri rustc-push <github user> <branch>:
Push Miri changes back to the rustc repo. This will pull a copy of the rustc
@ -113,18 +113,17 @@ toolchain)
;;
rustc-pull)
cd "$MIRIDIR"
FETCH_COMMIT=$(git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1)
# We can't pull from a commit with josh
# (https://github.com/josh-project/josh/issues/1034), so we just hope that
# nothing gets merged into rustc *during* this pull.
git fetch http://localhost:8000/rust-lang/rust.git$JOSH_FILTER.git master
# Just verify that `master` didn't move.
if [[ $FETCH_COMMIT != $(git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1) ]]; then
echo "Looks like something got merged into Rust *while we were pulling*. Aborting. Please try again."
FETCH_COMMIT="$1"
if [ -z "$FETCH_COMMIT" ]; then
FETCH_COMMIT=$(git ls-remote https://github.com/rust-lang/rust/ HEAD | cut -f 1)
fi
echo "$FETCH_COMMIT" > rust-version # do this *before* merging as merging will fail in case of conflicts
# Update rust-version file. As a separate commit, since making it part of
# the merge has confused the heck out of josh in the past.
echo "$FETCH_COMMIT" > rust-version
git commit rust-version -m "Preparing for merge from rustc"
# Fetch given rustc commit and note down which one that was
git fetch http://localhost:8000/rust-lang/rust.git@$FETCH_COMMIT$JOSH_FILTER.git
git merge FETCH_HEAD --no-ff -m "Merge from rustc"
git commit rust-version --amend -m "Merge from rustc"
exit 0
;;
rustc-push)
@ -157,16 +156,27 @@ rustc-push)
fi
git fetch https://github.com/rust-lang/rust $BASE
git push https://github.com/$USER/rust $BASE:refs/heads/$BRANCH -f
echo
# Do the actual push.
cd "$MIRIDIR"
echo "Pushing Miri changes..."
git push http://localhost:8000/$USER/rust.git$JOSH_FILTER.git HEAD:$BRANCH
exit 0
# Do a round-trip check to make sure the push worked as expected.
echo
git fetch http://localhost:8000/$USER/rust.git@$JOSH_FILTER.git $BRANCH &>/dev/null
if [[ $(git rev-parse HEAD) != $(git rev-parse FETCH_HEAD) ]]; then
echo "ERROR: Josh created a non-roundtrip push! Do NOT merge this into rustc!"
exit 1
else
echo "Confirmed that the push round-trips back to Miri properly. Please create a rustc PR:"
echo " https://github.com/$USER/rust/pull/new/$BRANCH"
exit 0
fi
;;
many-seeds)
for SEED in $({ echo obase=16; seq 0 255; } | bc); do
for SEED in $(seq 0 255); do
echo "Trying seed: $SEED"
MIRIFLAGS="$MIRIFLAGS -Zmiri-seed=$SEED" $@ || { echo "Failing seed: $SEED"; break; }
MIRIFLAGS="$MIRIFLAGS -Zlayout-seed=$SEED -Zmiri-seed=$SEED" $@ || { echo "Failing seed: $SEED"; break; }
done
exit 0
;;

View File

@ -1 +1 @@
101e1822c3e54e63996c8aaa014d55716f3937eb
7477c1f4f7d6bef037d523099b240d22aa1b63a0

View File

@ -394,10 +394,9 @@ fn main() {
if miri_config.seed.is_some() {
show_error!("Cannot specify -Zmiri-seed multiple times!");
}
let seed = u64::from_str_radix(param, 16)
.unwrap_or_else(|_| show_error!(
"-Zmiri-seed should only contain valid hex digits [0-9a-fA-F] and must fit into a u64 (max 16 characters)"
));
let seed = param.parse::<u64>().unwrap_or_else(|_| {
show_error!("-Zmiri-seed must be an integer that fits into u64")
});
miri_config.seed = Some(seed);
} else if let Some(_param) = arg.strip_prefix("-Zmiri-env-exclude=") {
show_error!(

View File

@ -89,6 +89,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
let ptr = this.read_pointer(ptr_op)?;
// If this carries no provenance, treat it like an integer.
if ptr.provenance.is_none() {
// Use actual implementation.
return Ok(false);
}
if let Ok((alloc_id, _offset, _)) = this.ptr_try_get_alloc_id(ptr) {
// Only do anything if we can identify the allocation this goes to.
let (_size, cur_align, _kind) = this.get_alloc_info(alloc_id);

View File

@ -22,21 +22,44 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut();
this.assert_target_os("linux", "clock_gettime");
this.assert_target_os_is_unix("clock_gettime");
let clk_id = this.read_scalar(clk_id_op)?.to_i32()?;
// Linux has two main kinds of clocks. REALTIME clocks return the actual time since the
// Unix epoch, including effects which may cause time to move backwards such as NTP.
// Linux further distinguishes regular and "coarse" clocks, but the "coarse" version
// is just specified to be "faster and less precise", so we implement both the same way.
let absolute_clocks =
[this.eval_libc_i32("CLOCK_REALTIME")?, this.eval_libc_i32("CLOCK_REALTIME_COARSE")?];
// The second kind is MONOTONIC clocks for which 0 is an arbitrary time point, but they are
// never allowed to go backwards. We don't need to do any additonal monotonicity
// enforcement because std::time::Instant already guarantees that it is monotonic.
let relative_clocks =
[this.eval_libc_i32("CLOCK_MONOTONIC")?, this.eval_libc_i32("CLOCK_MONOTONIC_COARSE")?];
let absolute_clocks;
let mut relative_clocks;
match this.tcx.sess.target.os.as_ref() {
"linux" => {
// Linux has two main kinds of clocks. REALTIME clocks return the actual time since the
// Unix epoch, including effects which may cause time to move backwards such as NTP.
// Linux further distinguishes regular and "coarse" clocks, but the "coarse" version
// is just specified to be "faster and less precise", so we implement both the same way.
absolute_clocks = vec![
this.eval_libc_i32("CLOCK_REALTIME")?,
this.eval_libc_i32("CLOCK_REALTIME_COARSE")?,
];
// The second kind is MONOTONIC clocks for which 0 is an arbitrary time point, but they are
// never allowed to go backwards. We don't need to do any additonal monotonicity
// enforcement because std::time::Instant already guarantees that it is monotonic.
relative_clocks = vec![
this.eval_libc_i32("CLOCK_MONOTONIC")?,
this.eval_libc_i32("CLOCK_MONOTONIC_COARSE")?,
];
}
"macos" => {
absolute_clocks = vec![this.eval_libc_i32("CLOCK_REALTIME")?];
relative_clocks = vec![this.eval_libc_i32("CLOCK_MONOTONIC")?];
// Some clocks only seem to exist in the aarch64 version of the target.
if this.tcx.sess.target.arch == "aarch64" {
// `CLOCK_UPTIME_RAW` supposed to not increment while the system is asleep... but
// that's not really something a program running inside Miri can tell, anyway.
// We need to support it because std uses it.
relative_clocks.push(this.eval_libc_i32("CLOCK_UPTIME_RAW")?);
}
}
target => throw_unsup_format!("`clock_gettime` is not supported on target OS {target}"),
}
let duration = if absolute_clocks.contains(&clk_id) {
this.check_no_isolation("`clock_gettime` with `REALTIME` clocks")?;
@ -44,6 +67,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
} else if relative_clocks.contains(&clk_id) {
this.machine.clock.now().duration_since(this.machine.clock.anchor())
} else {
// Unsupported clock.
let einval = this.eval_libc("EINVAL")?;
this.set_last_error(einval)?;
return Ok(Scalar::from_i32(-1));

View File

@ -180,6 +180,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let result = this.gettimeofday(tv, tz)?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"clock_gettime" => {
let [clk_id, tp] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.clock_gettime(clk_id, tp)?;
this.write_scalar(result, dest)?;
}
// Allocation
"posix_memalign" => {

View File

@ -43,15 +43,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_scalar(result, dest)?;
}
// Time related shims
"clock_gettime" => {
// This is a POSIX function but it has only been tested on linux.
let [clk_id, tp] =
this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let result = this.clock_gettime(clk_id, tp)?;
this.write_scalar(result, dest)?;
}
// Threading
"pthread_condattr_setclock" => {
let [attr, clock_id] =

View File

@ -14,7 +14,6 @@ use rustc_middle::mir::RetagKind;
use rustc_middle::ty::{
self,
layout::{HasParamEnv, LayoutOf},
Ty,
};
use rustc_target::abi::Abi;
use rustc_target::abi::Size;
@ -983,28 +982,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let mut visitor = RetagVisitor { ecx: this, kind, retag_cause, retag_fields };
return visitor.visit_value(place);
// Determine mutability and whether to add a protector.
// Cannot use `builtin_deref` because that reports *immutable* for `Box`,
// making it useless.
fn qualify(ty: Ty<'_>, kind: RetagKind) -> Option<(RefKind, bool)> {
match ty.kind() {
// References are simple.
ty::Ref(_, _, Mutability::Mut) =>
Some((
RefKind::Unique { two_phase: kind == RetagKind::TwoPhase },
kind == RetagKind::FnEntry,
)),
ty::Ref(_, _, Mutability::Not) =>
Some((RefKind::Shared, kind == RetagKind::FnEntry)),
// Raw pointers need to be enabled.
ty::RawPtr(tym) if kind == RetagKind::Raw =>
Some((RefKind::Raw { mutable: tym.mutbl == Mutability::Mut }, false)),
// Boxes are handled separately due to that allocator situation,
// see the visitor below.
_ => None,
}
}
// The actual visitor.
struct RetagVisitor<'ecx, 'mir, 'tcx> {
ecx: &'ecx mut MiriInterpCx<'mir, 'tcx>,
@ -1057,34 +1034,58 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
return Ok(());
}
let recurse_for_fields = || {
match self.retag_fields {
RetagFields::No => false,
RetagFields::Yes => true,
RetagFields::OnlyScalar => {
// Matching `ArgAbi::new` at the time of writing, only fields of
// `Scalar` and `ScalarPair` ABI are considered.
matches!(place.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..))
// Check the type of this value to see what to do with it (retag, or recurse).
match place.layout.ty.kind() {
ty::Ref(_, _, mutbl) => {
let ref_kind = match mutbl {
Mutability::Mut =>
RefKind::Unique { two_phase: self.kind == RetagKind::TwoPhase },
Mutability::Not => RefKind::Shared,
};
self.retag_place(
place,
ref_kind,
self.retag_cause,
/*protector*/ self.kind == RetagKind::FnEntry,
)?;
}
ty::RawPtr(tym) => {
// We definitely do *not* want to recurse into raw pointers -- wide raw
// pointers have fields, and for dyn Trait pointees those can have reference
// type!
if self.kind == RetagKind::Raw {
// Raw pointers need to be enabled.
self.retag_place(
place,
RefKind::Raw { mutable: tym.mutbl == Mutability::Mut },
self.retag_cause,
/*protector*/ false,
)?;
}
}
_ if place.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_box()) => {
// Recurse for boxes, they require some tricky handling and will end up in `visit_box` above.
// (Yes this means we technically also recursively retag the allocator itself
// even if field retagging is not enabled. *shrug*)
self.walk_value(place)?;
}
_ => {
// Not a reference/pointer/box. Only recurse if configured appropriately.
let recurse = match self.retag_fields {
RetagFields::No => false,
RetagFields::Yes => true,
RetagFields::OnlyScalar => {
// Matching `ArgAbi::new` at the time of writing, only fields of
// `Scalar` and `ScalarPair` ABI are considered.
matches!(place.layout.abi, Abi::Scalar(..) | Abi::ScalarPair(..))
}
};
if recurse {
self.walk_value(place)?;
}
}
};
if let Some((ref_kind, protector)) = qualify(place.layout.ty, self.kind) {
self.retag_place(place, ref_kind, self.retag_cause, protector)?;
} else if matches!(place.layout.ty.kind(), ty::RawPtr(..)) {
// Wide raw pointers *do* have fields and their types are strange.
// vtables have a type like `&[*const (); 3]` or so!
// Do *not* recurse into them.
// (No need to worry about wide references, those always "qualify". And Boxes
// are handles specially by the visitor anyway.)
} else if recurse_for_fields()
|| place.layout.ty.ty_adt_def().is_some_and(|adt| adt.is_box())
{
// Recurse deeper. Need to always recurse for `Box` to even hit `visit_box`.
// (Yes this means we technically also recursively retag the allocator itself
// even if field retagging is not enabled. *shrug*)
self.walk_value(place)?;
}
Ok(())
}
}

View File

@ -133,7 +133,7 @@ def test_cargo_miri_test():
test("`cargo miri test`",
cargo_miri("test"),
default_ref, "test.stderr-empty.ref",
env={'MIRIFLAGS': "-Zmiri-permissive-provenance -Zmiri-seed=feed"},
env={'MIRIFLAGS': "-Zmiri-permissive-provenance -Zmiri-seed=4242"},
)
test("`cargo miri test` (no isolation, no doctests)",
cargo_miri("test") + ["--bins", "--tests"], # no `--lib`, we disabled that in `Cargo.toml`

View File

@ -181,17 +181,25 @@ fn test_thread_local_errno() {
}
/// Tests whether clock support exists at all
#[cfg(target_os = "linux")]
fn test_clocks() {
let mut tp = std::mem::MaybeUninit::<libc::timespec>::uninit();
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
#[cfg(target_os = "linux")]
{
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
let is_error =
unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_COARSE, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
}
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
{
let is_error = unsafe { libc::clock_gettime(libc::CLOCK_UPTIME_RAW, tp.as_mut_ptr()) };
assert_eq!(is_error, 0);
}
}
fn test_posix_gettimeofday() {
@ -293,11 +301,11 @@ fn main() {
test_thread_local_errno();
test_isatty();
test_clocks();
#[cfg(target_os = "linux")]
{
test_posix_fadvise();
test_sync_file_range();
test_clocks();
}
}

View File

@ -1,4 +1,7 @@
//@compile-flags: -Zmiri-symbolic-alignment-check
#![feature(strict_provenance)]
use std::ptr;
fn test_align_offset() {
let d = Box::new([0u32; 4]);
@ -16,6 +19,9 @@ fn test_align_offset() {
assert_eq!(raw.wrapping_offset(2).align_offset(2), 0);
assert_eq!(raw.wrapping_offset(2).align_offset(4), 2);
assert_eq!(raw.wrapping_offset(2).align_offset(8), usize::MAX); // requested alignment higher than allocation alignment
let p = ptr::invalid::<()>(1);
assert_eq!(p.align_offset(1), 0);
}
fn test_align_to() {

View File

@ -69,7 +69,7 @@ fn main() {
});
test(Some("align_offset: align is not a power-of-two"), |_old_val| {
(0usize as *const u8).align_offset(3);
let _ = (0usize as *const u8).align_offset(3);
loop {}
});