diff --git a/Cargo.lock b/Cargo.lock index b5da9de5d62..96eda77abb2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5406,9 +5406,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" [[package]] name = "ui_test" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a2e70adc9d18b9b4dd80ea57aeec447103c6fbb354a07c080adad451c645e1" +checksum = "c21899b59f53717dfad29e4f46e5b21a200a1b6888ab86532a07cfc8b48dd78c" dependencies = [ "bstr", "cargo-platform", diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml index 8068db1cf4f..c87b3e42323 100644 --- a/src/tools/miri/.github/workflows/ci.yml +++ b/src/tools/miri/.github/workflows/ci.yml @@ -15,6 +15,10 @@ on: env: CARGO_UNSTABLE_SPARSE_REGISTRY: 'true' +defaults: + run: + shell: bash + jobs: build: runs-on: ${{ matrix.os }} @@ -59,12 +63,9 @@ jobs: - name: Install rustup-toolchain-install-master if: ${{ steps.cache.outputs.cache-hit != 'true' }} - shell: bash - run: | - cargo install -f rustup-toolchain-install-master + run: cargo install -f rustup-toolchain-install-master - name: Install "master" toolchain - shell: bash run: | if [[ ${{ github.event_name }} == 'schedule' ]]; then echo "Building against latest rustc git version" @@ -79,7 +80,7 @@ jobs: cargo -V - name: Test - run: bash ./ci.sh + run: ./ci.sh style: name: style checks @@ -111,14 +112,10 @@ jobs: - name: Install rustup-toolchain-install-master if: ${{ steps.cache.outputs.cache-hit != 'true' }} - shell: bash - run: | - cargo install -f rustup-toolchain-install-master + run: cargo install -f rustup-toolchain-install-master - name: Install "master" toolchain - shell: bash - run: | - ./miri toolchain + run: ./miri toolchain - name: Show Rust version run: | @@ -138,7 +135,6 @@ jobs: # workflow is successful listening to webhooks only. # # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! - # (`fmt` is deliberately not listed, we want bors to ignore it.) end-success: name: bors build finished runs-on: ubuntu-latest @@ -166,12 +162,12 @@ jobs: - name: Install zulip-send run: pip3 install zulip - name: Send Zulip notification - shell: bash env: ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }} ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }} run: | - ~/.local/bin/zulip-send --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \ + ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \ + --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \ --message 'Dear @*T-miri*, It would appear that the [Miri cron job build]('"https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"') failed. @@ -183,9 +179,12 @@ jobs: Thanks in advance! Sincerely, - The Miri Cronjobs Bot' \ - --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com + The Miri Cronjobs Bot' + # Attempt to auto-sync with rustc + - uses: actions/checkout@v3 + with: + fetch-depth: 256 # get a bit more of the history - name: install josh-proxy run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r22.12.06 - name: start josh-proxy @@ -196,16 +195,24 @@ jobs: git config --global user.email 'miri@cron.bot' - name: get changes from rustc run: ./miri rustc-pull + - name: Install rustup-toolchain-install-master + run: cargo install -f rustup-toolchain-install-master - name: format changes (if any) run: | ./miri toolchain ./miri fmt --check || (./miri fmt && git commit -am "fmt") - name: Push changes to a branch run: | - git switch -c "rustup$(date -u +%Y-%m)" - git push + BRANCH="rustup$(date -u +%Y-%m-%d)" + git switch -c $BRANCH + git push -u origin $BRANCH - name: Create Pull Request - run: gh pr create -B master --title 'Automatic sync from rustc' --body '' + run: | + PR=$(gh pr create -B master --title 'Automatic sync from rustc' --body '') + ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \ + --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \ + --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience." env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }} + ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }} diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock index edb3ee48a4e..4232d7fda78 100644 --- a/src/tools/miri/Cargo.lock +++ b/src/tools/miri/Cargo.lock @@ -842,9 +842,9 @@ dependencies = [ [[package]] name = "ui_test" -version = "0.11.6" +version = "0.11.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a2e70adc9d18b9b4dd80ea57aeec447103c6fbb354a07c080adad451c645e1" +checksum = "c21899b59f53717dfad29e4f46e5b21a200a1b6888ab86532a07cfc8b48dd78c" dependencies = [ "bstr", "cargo-platform", diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml index 50864334fc5..a625e1696e1 100644 --- a/src/tools/miri/Cargo.toml +++ b/src/tools/miri/Cargo.toml @@ -36,7 +36,7 @@ libloading = "0.7" [dev-dependencies] colored = "2" -ui_test = "0.11.6" +ui_test = "0.11.7" rustc_version = "0.4" # Features chosen to match those required by env_logger, to avoid rebuilds regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] } diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs index 85c9cdad7df..6178670b4f0 100644 --- a/src/tools/miri/cargo-miri/src/main.rs +++ b/src/tools/miri/cargo-miri/src/main.rs @@ -80,7 +80,11 @@ fn main() { match first.as_str() { "miri" => phase_cargo_miri(args), "runner" => phase_runner(args, RunnerPhase::Cargo), - arg if arg == env::var("RUSTC").unwrap() => { + arg if arg == env::var("RUSTC").unwrap_or_else(|_| { + show_error!( + "`cargo-miri` called without RUSTC set; please only invoke this binary through `cargo miri`" + ) + }) => { // If the first arg is equal to the RUSTC env variable (which should be set at this // point), then we need to behave as rustc. This is the somewhat counter-intuitive // behavior of having both RUSTC and RUSTC_WRAPPER set diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs index 60f39cb36ab..4c19ed97fb8 100644 --- a/src/tools/miri/cargo-miri/src/util.rs +++ b/src/tools/miri/cargo-miri/src/util.rs @@ -82,7 +82,7 @@ pub enum MiriCommand { pub fn escape_for_toml(s: &str) -> String { // We want to surround this string in quotes `"`. So we first escape all quotes, // and also all backslashes (that are used to escape quotes). - let s = s.replace('\\', r#"\\"#).replace('"', r#"\""#); + let s = s.replace('\\', r"\\").replace('"', r#"\""#); format!("\"{s}\"") } @@ -130,7 +130,7 @@ pub fn exec(mut cmd: Command) -> ! { { use std::os::unix::process::CommandExt; let error = cmd.exec(); - Err(error).expect("failed to run command") + panic!("failed to run command: {error}") } } diff --git a/src/tools/miri/miri b/src/tools/miri/miri index 7cda995879c..ace3d17ae2a 100755 --- a/src/tools/miri/miri +++ b/src/tools/miri/miri @@ -124,7 +124,7 @@ rustc-pull) git commit rust-version -m "Preparing for merge from rustc" || (echo "FAILED to commit rust-version file, something went wrong"; exit 1) # 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 || (echo "FAILED to fetch new commits, something went wrong"; exit 1) - git merge FETCH_HEAD --no-ff -m "Merge from rustc" || (echo "FAILED to merge new commits, something went wrong"; exit 1) + git merge FETCH_HEAD --no-ff -m "Merge from rustc" || (echo "FAILED to merge new commits ($(git rev-parse FETCH_HEAD)), something went wrong"; exit 1) exit 0 ;; rustc-push) @@ -325,6 +325,7 @@ run|run-dep) MIRIFLAGS="$MIRIFLAGS --target $MIRI_TEST_TARGET" fi + CARGO="$CARGO --quiet" # First build and get a sysroot. $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml find_sysroot diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 6790454bdb7..02b0dd16f91 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -75726cae37317c7262b69d3e9fd11a3496a88d04 +d4096e0412ac5de785d739a0aa2b1c1c7b9d3b7d diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 2dc33334417..1ec4cbc4de7 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -67,7 +67,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { if tcx.sess.compile_status().is_err() { tcx.sess.fatal("miri cannot be run on programs that fail compilation"); } -; + init_late_loggers(handler, tcx); if !tcx.sess.crate_types().contains(&CrateType::Executable) { tcx.sess.fatal("miri only makes sense on bin crates"); diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs index c9674e0a2fe..de307a3c5f5 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs @@ -221,7 +221,10 @@ impl AllocHistory { impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { pub fn start_grant(&mut self, perm: Permission) { let Operation::Retag(op) = &mut self.operation else { - unreachable!("start_grant must only be called during a retag, this is: {:?}", self.operation) + unreachable!( + "start_grant must only be called during a retag, this is: {:?}", + self.operation + ) }; op.permission = Some(perm); @@ -286,7 +289,8 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { tag: BorTag, protector_tag: Option, ) -> Option { - let Some(created) = self.history + let Some(created) = self + .history .creations .iter() .rev() @@ -315,22 +319,27 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> { None } }) - }).or_else(|| { + }) + .or_else(|| { // If we didn't find a retag that created this tag, it might be the base tag of // this allocation. if self.history.base.0.tag() == tag { Some(( - format!("{tag:?} was created here, as the base tag for {:?}", self.history.id), - self.history.base.1.data() + format!( + "{tag:?} was created here, as the base tag for {:?}", + self.history.id + ), + self.history.base.1.data(), )) } else { None } - }) else { - // But if we don't have a creation event, this is related to a wildcard, and there - // is really nothing we can do to help. - return None; - }; + }) + else { + // But if we don't have a creation event, this is related to a wildcard, and there + // is really nothing we can do to help. + return None; + }; let invalidated = self.history.invalidations.iter().rev().find_map(|event| { if event.tag == tag { Some(event.generate_diagnostic()) } else { None } diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 581327e54f1..ca0f69450c9 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -430,12 +430,15 @@ impl<'tcx> Stack { .find_granting(AccessKind::Write, derived_from, exposed_tags) .map_err(|()| dcx.grant_error(self))?; - let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from) else { + let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from) + else { // The parent is a wildcard pointer or matched the unknown bottom. // This is approximate. Nobody knows what happened, so forget everything. // The new thing is SRW anyway, so we cannot push it "on top of the unknown part" // (for all we know, it might join an SRW group inside the unknown). - trace!("reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown"); + trace!( + "reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown" + ); self.set_unknown_bottom(global.next_ptr_tag); return Ok(()); }; @@ -1008,7 +1011,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We have to turn the place into a pointer to use the existing code. // (The pointer type does not matter, so we use a raw pointer.) - let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx,return_place.layout.ty))?; + let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, return_place.layout.ty))?; let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout); // Reborrow it. With protection! That is part of the point. let new_perm = NewPermission::Uniform { diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs index a1e949183ad..291807c25ee 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs @@ -196,19 +196,19 @@ impl<'tcx> Stack { let ProvenanceExtra::Concrete(tag) = tag else { // Handle the wildcard case. // Go search the stack for an exposed tag. - if let Some(idx) = - self.borrows - .iter() - .enumerate() // we also need to know *where* in the stack - .rev() // search top-to-bottom - .find_map(|(idx, item)| { - // If the item fits and *might* be this wildcard, use it. - if item.perm().grants(access) && exposed_tags.contains(&item.tag()) { - Some(idx) - } else { - None - } - }) + if let Some(idx) = self + .borrows + .iter() + .enumerate() // we also need to know *where* in the stack + .rev() // search top-to-bottom + .find_map(|(idx, item)| { + // If the item fits and *might* be this wildcard, use it. + if item.perm().grants(access) && exposed_tags.contains(&item.tag()) { + Some(idx) + } else { + None + } + }) { return Ok(Some(idx)); } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index a87a4bbddad..3b2d6f9608e 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -570,9 +570,13 @@ impl DisplayRepr { extraction_aux(tree, tree.root, show_unnamed, &mut v); let Some(root) = v.pop() else { if show_unnamed { - unreachable!("This allocation contains no tags, not even a root. This should not happen."); + unreachable!( + "This allocation contains no tags, not even a root. This should not happen." + ); } - eprintln!("This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags."); + eprintln!( + "This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags." + ); return None; }; assert!(v.is_empty()); diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index 4c0690c585c..e134b739888 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -256,7 +256,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<' ptr_size.bytes() ); - let Some(new_perm) = new_perm else { return Ok(Some((alloc_id, orig_tag))); }; + let Some(new_perm) = new_perm else { + return Ok(Some((alloc_id, orig_tag))); + }; if let Some(protect) = new_perm.protector { // We register the protection in two different places. @@ -509,7 +511,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We have to turn the place into a pointer to use the existing code. // (The pointer type does not matter, so we use a raw pointer.) - let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx,return_place.layout.ty))?; + let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, return_place.layout.ty))?; let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout); // Reborrow it. With protection! That is part of the point. // FIXME: do we truly want a 2phase borrow here? diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 56de9c007d0..aba7dd5a9fe 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -305,7 +305,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( for arg in config.args.iter() { // Make space for `0` terminator. let size = u64::try_from(arg.len()).unwrap().checked_add(1).unwrap(); - let arg_type = Ty::new_array(tcx,tcx.types.u8, size); + let arg_type = Ty::new_array(tcx, tcx.types.u8, size); let arg_place = ecx.allocate(ecx.layout_of(arg_type)?, MiriMemoryKind::Machine.into())?; ecx.write_os_str_to_c_str(OsStr::new(arg), arg_place.ptr, size)?; @@ -313,9 +313,11 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( argvs.push(arg_place.to_ref(&ecx)); } // Make an array with all these pointers, in the Miri memory. - let argvs_layout = ecx.layout_of( - Ty::new_array(tcx,Ty::new_imm_ptr(tcx,tcx.types.u8), u64::try_from(argvs.len()).unwrap()), - )?; + let argvs_layout = ecx.layout_of(Ty::new_array( + tcx, + Ty::new_imm_ptr(tcx, tcx.types.u8), + u64::try_from(argvs.len()).unwrap(), + ))?; let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?; for (idx, arg) in argvs.into_iter().enumerate() { let place = ecx.mplace_field(&argvs_place, idx)?; @@ -333,7 +335,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( ecx.machine.argc = Some(*argc_place); let argv_place = ecx.allocate( - ecx.layout_of(Ty::new_imm_ptr(tcx,tcx.types.unit))?, + ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?, MiriMemoryKind::Machine.into(), )?; ecx.write_immediate(argv, &argv_place.into())?; @@ -345,7 +347,8 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( // Construct a command string with all the arguments. let cmd_utf16: Vec = args_to_utf16_command_string(config.args.iter()); - let cmd_type = Ty::new_array(tcx,tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap()); + let cmd_type = + Ty::new_array(tcx, tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap()); let cmd_place = ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Machine.into())?; ecx.machine.cmd_line = Some(*cmd_place); @@ -366,7 +369,11 @@ pub fn create_ecx<'mir, 'tcx: 'mir>( match entry_type { EntryFnType::Main { .. } => { - let start_id = tcx.lang_items().start_fn().unwrap(); + let start_id = tcx.lang_items().start_fn().unwrap_or_else(|| { + tcx.sess.fatal( + "could not find start function. Make sure the entry point is marked with `#[start]`." + ); + }); let main_ret_ty = tcx.fn_sig(entry_id).no_bound_vars().unwrap().output(); let main_ret_ty = main_ret_ty.no_bound_vars().unwrap(); let start_instance = ty::Instance::resolve( diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index e79bb47c78b..7e92dc7a0c7 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -9,6 +9,7 @@ #![feature(local_key_cell_methods)] #![feature(round_ties_even)] #![feature(os_str_bytes)] +#![feature(lint_reasons)] // Configure clippy and other lints #![allow( clippy::collapsible_else_if, diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 5fcf3905199..ac2bad22119 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -313,10 +313,12 @@ pub struct PrimitiveLayouts<'tcx> { impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> { fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result> { let tcx = layout_cx.tcx; - let mut_raw_ptr = Ty::new_ptr(tcx,TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut }); - let const_raw_ptr = Ty::new_ptr(tcx,TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not }); + let mut_raw_ptr = + Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut }); + let const_raw_ptr = + Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not }); Ok(Self { - unit: layout_cx.layout_of(Ty::new_unit(tcx,))?, + unit: layout_cx.layout_of(Ty::new_unit(tcx))?, i8: layout_cx.layout_of(tcx.types.i8)?, i16: layout_cx.layout_of(tcx.types.i16)?, i32: layout_cx.layout_of(tcx.types.i32)?, diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs index eaa66f1f874..adf9a35d5c3 100644 --- a/src/tools/miri/src/shims/backtrace.rs +++ b/src/tools/miri/src/shims/backtrace.rs @@ -71,7 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let len: u64 = ptrs.len().try_into().unwrap(); let ptr_ty = this.machine.layouts.mut_raw_ptr.ty; - let array_layout = this.layout_of(Ty::new_array(tcx.tcx,ptr_ty, len)).unwrap(); + let array_layout = this.layout_of(Ty::new_array(tcx.tcx, ptr_ty, len)).unwrap(); match flags { // storage for pointers is allocated by miri diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index 3d4967f1f83..1dcb877a83f 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -5,8 +5,8 @@ use std::mem; use rustc_const_eval::interpret::Pointer; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::Ty; use rustc_target::abi::Size; use crate::helpers::target_os_is_unix; @@ -449,9 +449,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { vars.push(Pointer::null()); // Make an array with all these pointers inside Miri. let tcx = this.tcx; - let vars_layout = this.layout_of( - Ty::new_array(tcx.tcx,this.machine.layouts.mut_raw_ptr.ty, u64::try_from(vars.len()).unwrap()), - )?; + let vars_layout = this.layout_of(Ty::new_array( + tcx.tcx, + this.machine.layouts.mut_raw_ptr.ty, + u64::try_from(vars.len()).unwrap(), + ))?; let vars_place = this.allocate(vars_layout, MiriMemoryKind::Runtime.into())?; for (idx, var) in vars.into_iter().enumerate() { let place = this.mplace_field(&vars_place, idx)?; diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index f4e91c30d9f..6915c396d61 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -763,6 +763,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let ptr_dest = this.read_pointer(ptr_dest)?; let ptr_src = this.read_pointer(ptr_src)?; let n = this.read_target_usize(n)?; + + // C requires that this must always be a valid pointer, even if `n` is zero, so we better check that. + // (This is more than Rust requires, so `mem_copy` is not sufficient.) + this.ptr_get_alloc_id(ptr_dest)?; + this.ptr_get_alloc_id(ptr_src)?; + this.mem_copy( ptr_src, Align::ONE, diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index 1995db715e8..94f8cfbfb1c 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -483,7 +483,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // `index` is an array, not a SIMD type let ty::Array(_, index_len) = index.layout.ty.kind() else { - span_bug!(this.cur_span(), "simd_shuffle index argument has non-array type {}", index.layout.ty) + span_bug!( + this.cur_span(), + "simd_shuffle index argument has non-array type {}", + index.layout.ty + ) }; let index_len = index_len.eval_target_usize(*this.tcx, this.param_env()); @@ -622,9 +626,7 @@ fn fmax_op<'tcx>( right: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { assert_eq!(left.layout.ty, right.layout.ty); - let ty::Float(float_ty) = left.layout.ty.kind() else { - bug!("fmax operand is not a float") - }; + let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmax operand is not a float") }; let left = left.to_scalar(); let right = right.to_scalar(); Ok(match float_ty { @@ -638,9 +640,7 @@ fn fmin_op<'tcx>( right: &ImmTy<'tcx, Provenance>, ) -> InterpResult<'tcx, Scalar> { assert_eq!(left.layout.ty, right.layout.ty); - let ty::Float(float_ty) = left.layout.ty.kind() else { - bug!("fmin operand is not a float") - }; + let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmin operand is not a float") }; let left = left.to_scalar(); let right = right.to_scalar(); Ok(match float_ty { diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs index 4cdd54fa21b..f08f0aad5e7 100644 --- a/src/tools/miri/src/shims/os_str.rs +++ b/src/tools/miri/src/shims/os_str.rs @@ -7,8 +7,8 @@ use std::os::unix::ffi::{OsStrExt, OsStringExt}; #[cfg(windows)] use std::os::windows::ffi::{OsStrExt, OsStringExt}; -use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; +use rustc_middle::ty::Ty; use crate::*; @@ -141,7 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator. let this = self.eval_context_mut(); - let arg_type = Ty::new_array(this.tcx.tcx,this.tcx.types.u8, size); + let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u8, size); let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; let (written, _) = self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap(); assert!(written); @@ -157,7 +157,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator. let this = self.eval_context_mut(); - let arg_type = Ty::new_array(this.tcx.tcx,this.tcx.types.u16, size); + let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size); let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?; let (written, _) = self.write_os_str_to_wide_str(os_str, arg_place.ptr, size, /*truncate*/ false).unwrap(); diff --git a/src/tools/miri/src/shims/unix/linux/fd.rs b/src/tools/miri/src/shims/unix/linux/fd.rs index dc395d39ce1..87e887000c5 100644 --- a/src/tools/miri/src/shims/unix/linux/fd.rs +++ b/src/tools/miri/src/shims/unix/linux/fd.rs @@ -181,6 +181,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics. /// /// + #[expect(clippy::needless_if)] fn eventfd( &mut self, val: &OpTy<'tcx, Provenance>, diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs index 73671e716ef..59143550253 100644 --- a/src/tools/miri/tests/compiletest.rs +++ b/src/tools/miri/tests/compiletest.rs @@ -145,7 +145,8 @@ fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> R // The files we're actually interested in (all `.rs` files). |path| { path.extension().is_some_and(|ext| ext == "rs") - && (filters.is_empty() || filters.iter().any(|f| path.starts_with(f))) + && (filters.is_empty() + || filters.iter().any(|f| path.display().to_string().contains(f))) }, // This could be used to overwrite the `Config` on a per-test basis. |_, _| None, @@ -274,13 +275,13 @@ fn main() -> Result<()> { fn run_dep_mode(target: String, mut args: impl Iterator) -> Result<()> { let path = args.next().expect("./miri run-dep must be followed by a file name"); let mut config = test_config(&target, "", Mode::Yolo, /* with dependencies */ true); - config.program.args.remove(0); // remove the `--error-format=json` argument - config.program.args.push("--color".into()); - config.program.args.push("always".into()); - let mut cmd = ui_test::test_command(config, Path::new(&path))?; - // Separate the arguments to the `cargo miri` invocation from - // the arguments to the interpreted prog - cmd.arg("--"); + config.program.args.clear(); // We want to give the user full control over flags + config.build_dependencies_and_link_them()?; + + let mut cmd = config.program.build(&config.out_dir); + + cmd.arg(path); + cmd.args(args); if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) } } diff --git a/src/tools/miri/tests/fail/shims/memchr_null.rs b/src/tools/miri/tests/fail/shims/memchr_null.rs new file mode 100644 index 00000000000..6bc7af7e6bf --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memchr_null.rs @@ -0,0 +1,10 @@ +//@ignore-target-windows: No libc on Windows + +use std::ptr; + +// null is explicitly called out as UB in the C docs. +fn main() { + unsafe { + libc::memchr(ptr::null(), 0, 0); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/shims/memchr_null.stderr b/src/tools/miri/tests/fail/shims/memchr_null.stderr new file mode 100644 index 00000000000..d48606f34ad --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memchr_null.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) + --> $DIR/memchr_null.rs:LL:CC + | +LL | libc::memchr(ptr::null(), 0, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/memchr_null.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/shims/memcmp_null.rs b/src/tools/miri/tests/fail/shims/memcmp_null.rs new file mode 100644 index 00000000000..a4e0034c40b --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcmp_null.rs @@ -0,0 +1,10 @@ +//@ignore-target-windows: No libc on Windows + +use std::ptr; + +// null is explicitly called out as UB in the C docs. +fn main() { + unsafe { + libc::memcmp(ptr::null(), ptr::null(), 0); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/shims/memcmp_null.stderr b/src/tools/miri/tests/fail/shims/memcmp_null.stderr new file mode 100644 index 00000000000..7a09c779894 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcmp_null.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) + --> $DIR/memcmp_null.rs:LL:CC + | +LL | libc::memcmp(ptr::null(), ptr::null(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/memcmp_null.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/shims/memcpy_zero.rs b/src/tools/miri/tests/fail/shims/memcpy_zero.rs new file mode 100644 index 00000000000..5283fea4cb9 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcpy_zero.rs @@ -0,0 +1,12 @@ +//@ignore-target-windows: No libc on Windows +//@compile-flags: -Zmiri-permissive-provenance +// C's memcpy is 0 bytes is UB for some pointers that are allowed in Rust's `copy_nonoverlapping`. + +fn main() { + let from = 42 as *const u8; + let to = 23 as *mut u8; + unsafe { + to.copy_from(from, 0); // this is fine + libc::memcpy(to.cast(), from.cast(), 0); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/shims/memcpy_zero.stderr b/src/tools/miri/tests/fail/shims/memcpy_zero.stderr new file mode 100644 index 00000000000..7c1c3fe20c4 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memcpy_zero.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: out-of-bounds pointer use: 0x17[noalloc] is a dangling pointer (it has no provenance) + --> $DIR/memcpy_zero.rs:LL:CC + | +LL | libc::memcpy(to.cast(), from.cast(), 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: 0x17[noalloc] is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/memcpy_zero.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/fail/shims/memrchr_null.rs b/src/tools/miri/tests/fail/shims/memrchr_null.rs new file mode 100644 index 00000000000..b6707d558d8 --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memrchr_null.rs @@ -0,0 +1,11 @@ +//@ignore-target-windows: No libc on Windows +//@ignore-target-apple: No `memrchr` on some apple targets + +use std::ptr; + +// null is explicitly called out as UB in the C docs. +fn main() { + unsafe { + libc::memrchr(ptr::null(), 0, 0); //~ERROR: dangling + } +} diff --git a/src/tools/miri/tests/fail/shims/memrchr_null.stderr b/src/tools/miri/tests/fail/shims/memrchr_null.stderr new file mode 100644 index 00000000000..b5b7630e7fd --- /dev/null +++ b/src/tools/miri/tests/fail/shims/memrchr_null.stderr @@ -0,0 +1,15 @@ +error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance) + --> $DIR/memrchr_null.rs:LL:CC + | +LL | libc::memrchr(ptr::null(), 0, 0); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance) + | + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information + = note: BACKTRACE: + = note: inside `main` at $DIR/memrchr_null.rs:LL:CC + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to previous error + diff --git a/src/tools/miri/tests/pass/vec.rs b/src/tools/miri/tests/pass/vec.rs index 048f7d1c351..4ab2bcb7f22 100644 --- a/src/tools/miri/tests/pass/vec.rs +++ b/src/tools/miri/tests/pass/vec.rs @@ -100,7 +100,7 @@ fn vec_push_ptr_stable() { v.push(0); let v0 = unsafe { &mut *(&mut v[0] as *mut _) }; // laundering the lifetime -- we take care that `v` does not reallocate, so that's okay. v.push(1); - let _val = *v0; + *v0 = *v0; } fn vec_extend_ptr_stable() { @@ -109,23 +109,23 @@ fn vec_extend_ptr_stable() { let v0 = unsafe { &mut *(&mut v[0] as *mut _) }; // laundering the lifetime -- we take care that `v` does not reallocate, so that's okay. // `slice::Iter` (with `T: Copy`) specialization v.extend(&[1]); - let _val = *v0; + *v0 = *v0; // `vec::IntoIter` specialization v.extend(vec![2]); - let _val = *v0; + *v0 = *v0; // `TrustedLen` specialization v.extend(std::iter::once(3)); - let _val = *v0; + *v0 = *v0; // base case v.extend(std::iter::once(3).filter(|_| true)); - let _val = *v0; + *v0 = *v0; } fn vec_truncate_ptr_stable() { let mut v = vec![0; 10]; let v0 = unsafe { &mut *(&mut v[0] as *mut _) }; // laundering the lifetime -- we take care that `v` does not reallocate, so that's okay. v.truncate(5); - let _val = *v0; + *v0 = *v0; } fn push_str_ptr_stable() {