authorbors <bors@rust-lang.org> 2025-12-06 21:42:15 UTC
committerbors <bors@rust-lang.org> 2025-12-06 21:42:15 UTC
logd427ddfe90367eaa6d2ed7bb8a16559f0230f47a
tree747fd920bd2bd4cbebe012a620441f35871fba47
parentba86c0460b0233319e01fd789a42a7276eade805
parent874b7c2e0bbe4396afb6fe1f46489094f9f063e7

Auto merge of #149717 - matthiaskrgr:rollup-spntobh, r=matthiaskrgr

Rollup of 5 pull requests Successful merges: - rust-lang/rust#149659 (Look for typos when reporting an unknown nightly feature) - rust-lang/rust#149699 (Implement `Vec::from_fn`) - rust-lang/rust#149700 (rustdoc: fix bugs with search aliases and merging) - rust-lang/rust#149713 (Update windows-gnullvm platform support doc) - rust-lang/rust#149716 (miri subtree update) r? `@ghost` `@rustbot` modify labels: rollup

105 files changed, 2751 insertions(+), 977 deletions(-)

compiler/rustc_parse/src/parser/diagnostics.rs+10-14
...@@ -16,7 +16,6 @@ use rustc_errors::{...@@ -16,7 +16,6 @@ use rustc_errors::{
16 pluralize,16 pluralize,
17};17};
18use rustc_session::errors::ExprParenthesesNeeded;18use rustc_session::errors::ExprParenthesesNeeded;
19use rustc_span::edit_distance::find_best_match_for_name;
20use rustc_span::source_map::Spanned;19use rustc_span::source_map::Spanned;
21use rustc_span::symbol::used_keywords;20use rustc_span::symbol::used_keywords;
22use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym};21use rustc_span::{BytePos, DUMMY_SP, Ident, Span, SpanSnippetError, Symbol, kw, sym};
...@@ -222,6 +221,8 @@ impl std::fmt::Display for UnaryFixity {...@@ -222,6 +221,8 @@ impl std::fmt::Display for UnaryFixity {
222 style = "verbose"221 style = "verbose"
223)]222)]
224struct MisspelledKw {223struct MisspelledKw {
224 // We use a String here because `Symbol::into_diag_arg` calls `Symbol::to_ident_string`, which
225 // prefix the keyword with a `r#` because it aims to print the symbol as an identifier.
225 similar_kw: String,226 similar_kw: String,
226 #[primary_span]227 #[primary_span]
227 span: Span,228 span: Span,
...@@ -229,20 +230,15 @@ struct MisspelledKw {...@@ -229,20 +230,15 @@ struct MisspelledKw {
229}230}
230231
231/// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`.232/// Checks if the given `lookup` identifier is similar to any keyword symbol in `candidates`.
233///
234/// This is a specialized version of [`Symbol::find_similar`] that constructs an error when a
235/// candidate is found.
232fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option<MisspelledKw> {236fn find_similar_kw(lookup: Ident, candidates: &[Symbol]) -> Option<MisspelledKw> {
233 let lowercase = lookup.name.as_str().to_lowercase();237 lookup.name.find_similar(candidates).map(|(similar_kw, is_incorrect_case)| MisspelledKw {
234 let lowercase_sym = Symbol::intern(&lowercase);238 similar_kw: similar_kw.to_string(),
235 if candidates.contains(&lowercase_sym) {239 is_incorrect_case,
236 Some(MisspelledKw { similar_kw: lowercase, span: lookup.span, is_incorrect_case: true })240 span: lookup.span,
237 } else if let Some(similar_sym) = find_best_match_for_name(candidates, lookup.name, None) {241 })
238 Some(MisspelledKw {
239 similar_kw: similar_sym.to_string(),
240 span: lookup.span,
241 is_incorrect_case: false,
242 })
243 } else {
244 None
245 }
246}242}
247243
248struct MultiSugg {244struct MultiSugg {
compiler/rustc_passes/messages.ftl+2
...@@ -421,6 +421,8 @@ passes_missing_panic_handler =...@@ -421,6 +421,8 @@ passes_missing_panic_handler =
421passes_missing_stability_attr =421passes_missing_stability_attr =
422 {$descr} has missing stability attribute422 {$descr} has missing stability attribute
423423
424passes_misspelled_feature = there is a feature with a similar name: `{$actual_name}`
425
424passes_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}`426passes_mixed_export_name_and_no_mangle = `{$no_mangle_attr}` attribute may not be used in combination with `{$export_name_attr}`
425 .label = `{$no_mangle_attr}` is ignored427 .label = `{$no_mangle_attr}` is ignored
426 .note = `{$export_name_attr}` takes precedence428 .note = `{$export_name_attr}` takes precedence
compiler/rustc_passes/src/errors.rs+15
...@@ -1183,6 +1183,21 @@ pub(crate) struct UnknownFeature {...@@ -1183,6 +1183,21 @@ pub(crate) struct UnknownFeature {
1183 #[primary_span]1183 #[primary_span]
1184 pub span: Span,1184 pub span: Span,
1185 pub feature: Symbol,1185 pub feature: Symbol,
1186 #[subdiagnostic]
1187 pub suggestion: Option<MisspelledFeature>,
1188}
1189
1190#[derive(Subdiagnostic)]
1191#[suggestion(
1192 passes_misspelled_feature,
1193 style = "verbose",
1194 code = "{actual_name}",
1195 applicability = "maybe-incorrect"
1196)]
1197pub(crate) struct MisspelledFeature {
1198 #[primary_span]
1199 pub span: Span,
1200 pub actual_name: Symbol,
1186}1201}
11871202
1188#[derive(Diagnostic)]1203#[derive(Diagnostic)]
compiler/rustc_passes/src/stability.rs+24-6
...@@ -6,7 +6,7 @@ use std::num::NonZero;...@@ -6,7 +6,7 @@ use std::num::NonZero;
6use rustc_ast_lowering::stability::extern_abi_stability;6use rustc_ast_lowering::stability::extern_abi_stability;
7use rustc_data_structures::fx::FxIndexMap;7use rustc_data_structures::fx::FxIndexMap;
8use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};8use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
9use rustc_feature::{EnabledLangFeature, EnabledLibFeature};9use rustc_feature::{EnabledLangFeature, EnabledLibFeature, UNSTABLE_LANG_FEATURES};
10use rustc_hir::attrs::{AttributeKind, DeprecatedSince};10use rustc_hir::attrs::{AttributeKind, DeprecatedSince};
11use rustc_hir::def::{DefKind, Res};11use rustc_hir::def::{DefKind, Res};
12use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId};12use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId};
...@@ -1062,11 +1062,13 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {...@@ -1062,11 +1062,13 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
1062 // no unknown features, because the collection also does feature attribute validation.1062 // no unknown features, because the collection also does feature attribute validation.
1063 let local_defined_features = tcx.lib_features(LOCAL_CRATE);1063 let local_defined_features = tcx.lib_features(LOCAL_CRATE);
1064 if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {1064 if !remaining_lib_features.is_empty() || !remaining_implications.is_empty() {
1065 let crates = tcx.crates(());
1066
1065 // Loading the implications of all crates is unavoidable to be able to emit the partial1067 // Loading the implications of all crates is unavoidable to be able to emit the partial
1066 // stabilization diagnostic, but it can be avoided when there are no1068 // stabilization diagnostic, but it can be avoided when there are no
1067 // `remaining_lib_features`.1069 // `remaining_lib_features`.
1068 let mut all_implications = remaining_implications.clone();1070 let mut all_implications = remaining_implications.clone();
1069 for &cnum in tcx.crates(()) {1071 for &cnum in crates {
1070 all_implications1072 all_implications
1071 .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));1073 .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));
1072 }1074 }
...@@ -1079,7 +1081,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {...@@ -1079,7 +1081,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
1079 &all_implications,1081 &all_implications,
1080 );1082 );
10811083
1082 for &cnum in tcx.crates(()) {1084 for &cnum in crates {
1083 if remaining_lib_features.is_empty() && remaining_implications.is_empty() {1085 if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
1084 break;1086 break;
1085 }1087 }
...@@ -1091,10 +1093,26 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {...@@ -1091,10 +1093,26 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
1091 &all_implications,1093 &all_implications,
1092 );1094 );
1093 }1095 }
1094 }
10951096
1096 for (feature, span) in remaining_lib_features {1097 if !remaining_lib_features.is_empty() {
1097 tcx.dcx().emit_err(errors::UnknownFeature { span, feature });1098 let lang_features =
1099 UNSTABLE_LANG_FEATURES.iter().map(|feature| feature.name).collect::<Vec<_>>();
1100 let lib_features = crates
1101 .into_iter()
1102 .flat_map(|&cnum| {
1103 tcx.lib_features(cnum).stability.keys().copied().into_sorted_stable_ord()
1104 })
1105 .collect::<Vec<_>>();
1106
1107 let valid_feature_names = [lang_features, lib_features].concat();
1108
1109 for (feature, span) in remaining_lib_features {
1110 let suggestion = feature
1111 .find_similar(&valid_feature_names)
1112 .map(|(actual_name, _)| errors::MisspelledFeature { span, actual_name });
1113 tcx.dcx().emit_err(errors::UnknownFeature { span, feature, suggestion });
1114 }
1115 }
1098 }1116 }
10991117
1100 for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {1118 for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {
compiler/rustc_span/src/symbol.rs+22
...@@ -14,6 +14,7 @@ use rustc_data_structures::stable_hasher::{...@@ -14,6 +14,7 @@ use rustc_data_structures::stable_hasher::{
14use rustc_data_structures::sync::Lock;14use rustc_data_structures::sync::Lock;
15use rustc_macros::{Decodable, Encodable, HashStable_Generic, symbols};15use rustc_macros::{Decodable, Encodable, HashStable_Generic, symbols};
1616
17use crate::edit_distance::find_best_match_for_name;
17use crate::{DUMMY_SP, Edition, Span, with_session_globals};18use crate::{DUMMY_SP, Edition, Span, with_session_globals};
1819
19#[cfg(test)]20#[cfg(test)]
...@@ -2843,6 +2844,27 @@ impl Symbol {...@@ -2843,6 +2844,27 @@ impl Symbol {
2843 // Avoid creating an empty identifier, because that asserts in debug builds.2844 // Avoid creating an empty identifier, because that asserts in debug builds.
2844 if self == sym::empty { String::new() } else { Ident::with_dummy_span(self).to_string() }2845 if self == sym::empty { String::new() } else { Ident::with_dummy_span(self).to_string() }
2845 }2846 }
2847
2848 /// Checks if `self` is similar to any symbol in `candidates`.
2849 ///
2850 /// The returned boolean represents whether the candidate is the same symbol with a different
2851 /// casing.
2852 ///
2853 /// All the candidates are assumed to be lowercase.
2854 pub fn find_similar(
2855 self,
2856 candidates: &[Symbol],
2857 ) -> Option<(Symbol, /* is incorrect case */ bool)> {
2858 let lowercase = self.as_str().to_lowercase();
2859 let lowercase_sym = Symbol::intern(&lowercase);
2860 if candidates.contains(&lowercase_sym) {
2861 Some((lowercase_sym, true))
2862 } else if let Some(similar_sym) = find_best_match_for_name(candidates, self, None) {
2863 Some((similar_sym, false))
2864 } else {
2865 None
2866 }
2867 }
2846}2868}
28472869
2848impl fmt::Debug for Symbol {2870impl fmt::Debug for Symbol {
library/alloc/src/vec/mod.rs+53
...@@ -745,6 +745,59 @@ impl<T> Vec<T> {...@@ -745,6 +745,59 @@ impl<T> Vec<T> {
745 unsafe { Self::from_parts_in(ptr, length, capacity, Global) }745 unsafe { Self::from_parts_in(ptr, length, capacity, Global) }
746 }746 }
747747
748 /// Creates a `Vec<T>` where each element is produced by calling `f` with
749 /// that element's index while walking forward through the `Vec<T>`.
750 ///
751 /// This is essentially the same as writing
752 ///
753 /// ```text
754 /// vec![f(0), f(1), f(2), …, f(length - 2), f(length - 1)]
755 /// ```
756 /// and is similar to `(0..i).map(f)`, just for `Vec<T>`s not iterators.
757 ///
758 /// If `length == 0`, this produces an empty `Vec<T>` without ever calling `f`.
759 ///
760 /// # Example
761 ///
762 /// ```rust
763 /// #![feature(vec_from_fn)]
764 ///
765 /// let vec = Vec::from_fn(5, |i| i);
766 ///
767 /// // indexes are: 0 1 2 3 4
768 /// assert_eq!(vec, [0, 1, 2, 3, 4]);
769 ///
770 /// let vec2 = Vec::from_fn(8, |i| i * 2);
771 ///
772 /// // indexes are: 0 1 2 3 4 5 6 7
773 /// assert_eq!(vec2, [0, 2, 4, 6, 8, 10, 12, 14]);
774 ///
775 /// let bool_vec = Vec::from_fn(5, |i| i % 2 == 0);
776 ///
777 /// // indexes are: 0 1 2 3 4
778 /// assert_eq!(bool_vec, [true, false, true, false, true]);
779 /// ```
780 ///
781 /// The `Vec<T>` is generated in ascending index order, starting from the front
782 /// and going towards the back, so you can use closures with mutable state:
783 /// ```
784 /// #![feature(vec_from_fn)]
785 ///
786 /// let mut state = 1;
787 /// let a = Vec::from_fn(6, |_| { let x = state; state *= 2; x });
788 ///
789 /// assert_eq!(a, [1, 2, 4, 8, 16, 32]);
790 /// ```
791 #[cfg(not(no_global_oom_handling))]
792 #[inline]
793 #[unstable(feature = "vec_from_fn", reason = "new API", issue = "149698")]
794 pub fn from_fn<F>(length: usize, f: F) -> Self
795 where
796 F: FnMut(usize) -> T,
797 {
798 (0..length).map(f).collect()
799 }
800
748 /// Decomposes a `Vec<T>` into its raw components: `(pointer, length, capacity)`.801 /// Decomposes a `Vec<T>` into its raw components: `(pointer, length, capacity)`.
749 ///802 ///
750 /// Returns the raw pointer to the underlying data, the length of803 /// Returns the raw pointer to the underlying data, the length of
src/doc/rustc/src/platform-support/windows-gnullvm.md+26-28
...@@ -2,9 +2,11 @@...@@ -2,9 +2,11 @@
22
3**Tier: 2 (with host tools)**3**Tier: 2 (with host tools)**
44
5Windows targets similar to `*-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of GCC/Binutils.5Windows targets similar to `*-windows-gnu` but using UCRT as the runtime and various LLVM tools/libraries instead of
6GCC/Binutils.
67
7Target triples available so far:8Target triples available so far:
9
8- `aarch64-pc-windows-gnullvm`10- `aarch64-pc-windows-gnullvm`
9- `i686-pc-windows-gnullvm`11- `i686-pc-windows-gnullvm`
10- `x86_64-pc-windows-gnullvm`12- `x86_64-pc-windows-gnullvm`
...@@ -16,10 +18,11 @@ Target triples available so far:...@@ -16,10 +18,11 @@ Target triples available so far:
1618
17## Requirements19## Requirements
1820
19The easiest way to obtain these targets is cross-compilation, but native build from `x86_64-pc-windows-gnu` is possible with few hacks which I don't recommend.21Building those targets requires an LLVM-based C toolchain, for example, [llvm-mingw][1] or [MSYS2][2] with CLANG*
20Std support is expected to be on par with `*-windows-gnu`.22environment.
2123
22Binaries for this target should be at least on par with `*-windows-gnu` in terms of requirements and functionality.24Binaries for this target should be at least on par with `*-windows-gnu` in terms of requirements and functionality,
25except for implicit self-contained mode (explained in [the section below](#building-rust-programs)).
2326
24Those targets follow Windows calling convention for `extern "C"`.27Those targets follow Windows calling convention for `extern "C"`.
2528
...@@ -27,37 +30,32 @@ Like with any other Windows target, created binaries are in PE format....@@ -27,37 +30,32 @@ Like with any other Windows target, created binaries are in PE format.
2730
28## Building the target31## Building the target
2932
30These targets can be easily cross-compiled33Both native and cross-compilation builds are supported and function similarly to other Rust targets.
31using [llvm-mingw](https://github.com/mstorsjo/llvm-mingw) toolchain or [MSYS2 CLANG*](https://www.msys2.org/docs/environments/) environments.
32Just fill `[target.*]` sections for both build and resulting compiler and set installation prefix in `bootstrap.toml`.
33Then run `./x.py install`.
34In my case I had ran `./x.py install --host x86_64-pc-windows-gnullvm --target x86_64-pc-windows-gnullvm` inside MSYS2 MINGW64 shell
35so `x86_64-pc-windows-gnu` was my build toolchain.
36
37Native bootstrapping is doable in two ways:
38- cross-compile gnullvm host toolchain and use it as build toolchain for the next build,
39- copy libunwind libraries and rename them to mimic libgcc like here: https://github.com/msys2/MINGW-packages/blob/68e640756df2df6df6afa60f025e3f936e7b977c/mingw-w64-rust/PKGBUILD#L108-L109, stage0 compiler will be mostly broken but good enough to build the next stage.
40
41The second option might stop working anytime, so it's not recommended.
4234
43## Building Rust programs35## Building Rust programs
4436
45Rust does ship a pre-compiled std library for those targets.37Rust ships both std and host tools for those targets. That allows using them as both the host and the target.
46That means one can easily cross-compile for those targets from other hosts if C proper toolchain is installed.
4738
48Alternatively full toolchain can be built as described in the previous section.39When used as the host and building pure Rust programs, no additional C toolchain is required.
40The only requirements are to install `rust-mingw` component and to set `rust-lld` as the linker.
41Otherwise, you will need to install the C toolchain mentioned previously.
42There is no automatic fallback to `rust-lld` when the C toolchain is missing yet, but it may be added in the future.
4943
50## Testing44## Testing
5145
52Created binaries work fine on Windows or Wine using native hardware. Testing AArch64 on x86_64 is problematic though and requires spending some time with QEMU.46Created binaries work fine on Windows and Linux with Wine using native hardware.
53Most of x86_64 testsuite does pass when cross-compiling,47Testing AArch64 on x86_64 is problematic, though, and requires launching a whole AArch64 system with QEMU.
54with exception for `rustdoc` and `ui-fulldeps` that fail with and error regarding a missing library,48
55they do pass in native builds though.49Most of the x86_64 testsuite does pass, but because it isn't run on CI, different failures are expected over time.
56The only failing test is std's `process::tests::test_proc_thread_attributes` for unknown reason.
5750
58## Cross-compilation toolchains and C code51## Cross-compilation toolchains and C code
5952
60Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and `x86_64-pc-windows-gnu` targets as long as LLVM-based C toolchains are used.53Compatible C code can be built with Clang's `aarch64-pc-windows-gnu`, `i686-pc-windows-gnullvm` and
61Those include:54`x86_64-pc-windows-gnu` targets as long as LLVM-based C toolchains are used. Those include:
62- [llvm-mingw](https://github.com/mstorsjo/llvm-mingw)55
63- [MSYS2 with CLANG* environment](https://www.msys2.org/docs/environments)56- [llvm-mingw][1]
57- [MSYS2][2] with CLANG* environment
58
59[1]: https://github.com/mstorsjo/llvm-mingw
60
61[2]: https://www.msys2.org/docs/environments
src/librustdoc/html/render/search_index.rs+10-2
...@@ -24,6 +24,7 @@ use tracing::instrument;...@@ -24,6 +24,7 @@ use tracing::instrument;
2424
25use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};25use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
26use crate::clean::{self, utils};26use crate::clean::{self, utils};
27use crate::config::ShouldMerge;
27use crate::error::Error;28use crate::error::Error;
28use crate::formats::cache::{Cache, OrphanImplItem};29use crate::formats::cache::{Cache, OrphanImplItem};
29use crate::formats::item_type::ItemType;30use crate::formats::item_type::ItemType;
...@@ -721,7 +722,9 @@ impl SerializedSearchIndex {...@@ -721,7 +722,9 @@ impl SerializedSearchIndex {
721 }722 }
722 },723 },
723 ),724 ),
724 self.alias_pointers[id].and_then(|alias| map.get(&alias).copied()),725 self.alias_pointers[id].and_then(|alias| {
726 if self.names[alias].is_empty() { None } else { map.get(&alias).copied() }
727 }),
725 );728 );
726 }729 }
727 new.generic_inverted_index = self730 new.generic_inverted_index = self
...@@ -1248,6 +1251,7 @@ pub(crate) fn build_index(...@@ -1248,6 +1251,7 @@ pub(crate) fn build_index(
1248 tcx: TyCtxt<'_>,1251 tcx: TyCtxt<'_>,
1249 doc_root: &Path,1252 doc_root: &Path,
1250 resource_suffix: &str,1253 resource_suffix: &str,
1254 should_merge: &ShouldMerge,
1251) -> Result<SerializedSearchIndex, Error> {1255) -> Result<SerializedSearchIndex, Error> {
1252 let mut search_index = std::mem::take(&mut cache.search_index);1256 let mut search_index = std::mem::take(&mut cache.search_index);
12531257
...@@ -1298,7 +1302,11 @@ pub(crate) fn build_index(...@@ -1298,7 +1302,11 @@ pub(crate) fn build_index(
1298 //1302 //
1299 // if there's already a search index, load it into memory and add the new entries to it1303 // if there's already a search index, load it into memory and add the new entries to it
1300 // otherwise, do nothing1304 // otherwise, do nothing
1301 let mut serialized_index = SerializedSearchIndex::load(doc_root, resource_suffix)?;1305 let mut serialized_index = if should_merge.read_rendered_cci {
1306 SerializedSearchIndex::load(doc_root, resource_suffix)?
1307 } else {
1308 SerializedSearchIndex::default()
1309 };
13021310
1303 // The crate always goes first in this list1311 // The crate always goes first in this list
1304 let crate_name = krate.name(tcx);1312 let crate_name = krate.name(tcx);
src/librustdoc/html/render/write_shared.rs+8-2
...@@ -66,8 +66,14 @@ pub(crate) fn write_shared(...@@ -66,8 +66,14 @@ pub(crate) fn write_shared(
66 // Write shared runs within a flock; disable thread dispatching of IO temporarily.66 // Write shared runs within a flock; disable thread dispatching of IO temporarily.
67 let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);67 let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file);
6868
69 let search_index =69 let search_index = build_index(
70 build_index(krate, &mut cx.shared.cache, tcx, &cx.dst, &cx.shared.resource_suffix)?;70 krate,
71 &mut cx.shared.cache,
72 tcx,
73 &cx.dst,
74 &cx.shared.resource_suffix,
75 &opt.should_merge,
76 )?;
7177
72 let crate_name = krate.name(cx.tcx());78 let crate_name = krate.name(cx.tcx());
73 let crate_name = crate_name.as_str(); // rand79 let crate_name = crate_name.as_str(); // rand
src/tools/miri/.github/workflows/ci.yml+5-5
...@@ -31,13 +31,13 @@ jobs:...@@ -31,13 +31,13 @@ jobs:
31 os: ubuntu-24.04-arm31 os: ubuntu-24.04-arm
32 multiarch: armhf32 multiarch: armhf
33 gcc_cross: arm-linux-gnueabihf33 gcc_cross: arm-linux-gnueabihf
34 - host_target: riscv64gc-unknown-linux-gnu
35 os: ubuntu-latest
36 multiarch: riscv64
37 gcc_cross: riscv64-linux-gnu
38 qemu: true
39 # Ubuntu mirrors are not reliable enough for these architectures34 # Ubuntu mirrors are not reliable enough for these architectures
40 # (see <https://bugs.launchpad.net/ubuntu/+bug/2130309>).35 # (see <https://bugs.launchpad.net/ubuntu/+bug/2130309>).
36 # - host_target: riscv64gc-unknown-linux-gnu
37 # os: ubuntu-latest
38 # multiarch: riscv64
39 # gcc_cross: riscv64-linux-gnu
40 # qemu: true
41 # - host_target: s390x-unknown-linux-gnu41 # - host_target: s390x-unknown-linux-gnu
42 # os: ubuntu-latest42 # os: ubuntu-latest
43 # multiarch: s390x43 # multiarch: s390x
src/tools/miri/README.md+1
...@@ -624,6 +624,7 @@ Definite bugs found:...@@ -624,6 +624,7 @@ Definite bugs found:
624* [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite)624* [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite)
625* [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248)625* [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248)
626* [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816)626* [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816)
627* [`winit` registering a global constructor with the wrong ABI on Windows](https://github.com/rust-windowing/winit/issues/4435)
627628
628Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):629Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):
629630
src/tools/miri/etc/rust_analyzer_helix.toml+2
...@@ -28,5 +28,7 @@ overrideCommand = [...@@ -28,5 +28,7 @@ overrideCommand = [
28 "./miri",28 "./miri",
29 "check",29 "check",
30 "--no-default-features",30 "--no-default-features",
31 "-Zunstable-options",
32 "--compile-time-deps",
31 "--message-format=json",33 "--message-format=json",
32]34]
src/tools/miri/etc/rust_analyzer_vscode.json+2
...@@ -22,6 +22,8 @@...@@ -22,6 +22,8 @@
22 "./miri",22 "./miri",
23 "check",23 "check",
24 "--no-default-features",24 "--no-default-features",
25 "-Zunstable-options",
26 "--compile-time-deps",
25 "--message-format=json",27 "--message-format=json",
26 ],28 ],
27}29}
src/tools/miri/etc/rust_analyzer_zed.json+2
...@@ -31,6 +31,8 @@...@@ -31,6 +31,8 @@
31 "./miri",31 "./miri",
32 "check",32 "check",
33 "--no-default-features",33 "--no-default-features",
34 "-Zunstable-options",
35 "--compile-time-deps",
34 "--message-format=json"36 "--message-format=json"
35 ]37 ]
36 }38 }
src/tools/miri/rust-version+1-1
...@@ -1 +1 @@...@@ -1 +1 @@
11eb0657f78777f0b4d6bcc49c126d5d35212cae5136b2369c91d32c2659887ed6fe3d570640f44fd2
src/tools/miri/src/bin/miri.rs+25-38
...@@ -272,18 +272,13 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {...@@ -272,18 +272,13 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
272 }272 }
273}273}
274274
275struct MiriBeRustCompilerCalls {275/// This compiler produces rlibs that are meant for later consumption by Miri.
276 target_crate: bool,276struct MiriDepCompilerCalls;
277}
278277
279impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {278impl rustc_driver::Callbacks for MiriDepCompilerCalls {
280 #[allow(rustc::potential_query_instability)] // rustc_codegen_ssa (where this code is copied from) also allows this lint279 #[allow(rustc::potential_query_instability)] // rustc_codegen_ssa (where this code is copied from) also allows this lint
281 fn config(&mut self, config: &mut Config) {280 fn config(&mut self, config: &mut Config) {
282 if !self.target_crate {281 // We don't need actual codegen, we just emit an rlib that Miri can later consume.
283 // For a host crate, we fully behave like rustc.
284 return;
285 }
286 // For a target crate, we emit an rlib that Miri can later consume.
287 config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend));282 config.make_codegen_backend = Some(Box::new(make_miri_codegen_backend));
288283
289 // Avoid warnings about unsupported crate types. However, only do that we we are *not* being284 // Avoid warnings about unsupported crate types. However, only do that we we are *not* being
...@@ -367,16 +362,12 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {...@@ -367,16 +362,12 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
367 _: &rustc_interface::interface::Compiler,362 _: &rustc_interface::interface::Compiler,
368 tcx: TyCtxt<'tcx>,363 tcx: TyCtxt<'tcx>,
369 ) -> Compilation {364 ) -> Compilation {
370 if self.target_crate {365 // While the dummy codegen backend doesn't do any codegen, we are still emulating
371 // cargo-miri has patched the compiler flags to make these into check-only builds,366 // regular rustc builds, which would perform post-mono const-eval during collection.
372 // but we are still emulating regular rustc builds, which would perform post-mono367 // So let's also do that here. In particular this is needed to make `compile_fail`
373 // const-eval during collection. So let's also do that here, even if we might be368 // doc tests trigger post-mono errors.
374 // running with `--emit=metadata`. In particular this is needed to make369 let _ = tcx.collect_and_partition_mono_items(());
375 // `compile_fail` doc tests trigger post-mono errors.370
376 // In general `collect_and_partition_mono_items` is not safe to call in check-only
377 // builds, but we are setting `-Zalways-encode-mir` which avoids those issues.
378 let _ = tcx.collect_and_partition_mono_items(());
379 }
380 Compilation::Continue371 Compilation::Continue
381 }372 }
382}373}
...@@ -457,32 +448,28 @@ fn main() {...@@ -457,32 +448,28 @@ fn main() {
457448
458 // If the environment asks us to actually be rustc, then do that.449 // If the environment asks us to actually be rustc, then do that.
459 if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {450 if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {
451 if crate_kind == "host" {
452 // For host crates like proc macros and build scripts, we are an entirely normal rustc.
453 // These eventually produce actual binaries and never run in Miri.
454 match rustc_driver::main() {
455 // Empty match proves this function will never return.
456 }
457 } else if crate_kind != "target" {
458 panic!("invalid `MIRI_BE_RUSTC` value: {crate_kind:?}")
459 };
460
460 // Earliest rustc setup.461 // Earliest rustc setup.
461 rustc_driver::install_ice_hook(rustc_driver::DEFAULT_BUG_REPORT_URL, |_| ());462 rustc_driver::install_ice_hook(rustc_driver::DEFAULT_BUG_REPORT_URL, |_| ());
462 rustc_driver::init_rustc_env_logger(&early_dcx);463 rustc_driver::init_rustc_env_logger(&early_dcx);
463464
464 let target_crate = if crate_kind == "target" {
465 true
466 } else if crate_kind == "host" {
467 false
468 } else {
469 panic!("invalid `MIRI_BE_RUSTC` value: {crate_kind:?}")
470 };
471
472 let mut args = args;465 let mut args = args;
473 // Don't insert `MIRI_DEFAULT_ARGS`, in particular, `--cfg=miri`, if we are building466 // Splice in the default arguments after the program name.
474 // a "host" crate. That may cause procedural macros (and probably build scripts) to467 // Some options have different defaults in Miri than in plain rustc; apply those by making
475 // depend on Miri-only symbols, such as `miri_resolve_frame`:468 // them the first arguments after the binary name (but later arguments can overwrite them).
476 // https://github.com/rust-lang/miri/issues/1760469 args.splice(1..1, miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string));
477 if target_crate {
478 // Splice in the default arguments after the program name.
479 // Some options have different defaults in Miri than in plain rustc; apply those by making
480 // them the first arguments after the binary name (but later arguments can overwrite them).
481 args.splice(1..1, miri::MIRI_DEFAULT_ARGS.iter().map(ToString::to_string));
482 }
483470
484 // We cannot use `rustc_driver::main` as we want it to use `args` as the CLI arguments.471 // We cannot use `rustc_driver::main` as we want it to use `args` as the CLI arguments.
485 run_compiler_and_exit(&args, &mut MiriBeRustCompilerCalls { target_crate })472 run_compiler_and_exit(&args, &mut MiriDepCompilerCalls)
486 }473 }
487474
488 // Add an ICE bug report hook.475 // Add an ICE bug report hook.
src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs+84-25
...@@ -488,6 +488,8 @@ struct DisplayFmtPadding {...@@ -488,6 +488,8 @@ struct DisplayFmtPadding {
488 indent_middle: S,488 indent_middle: S,
489 /// Indentation for the last child.489 /// Indentation for the last child.
490 indent_last: S,490 indent_last: S,
491 /// Replaces `join_last` for a wildcard root.
492 wildcard_root: S,
491}493}
492/// How to show whether a location has been accessed494/// How to show whether a location has been accessed
493///495///
...@@ -561,6 +563,11 @@ impl DisplayFmt {...@@ -561,6 +563,11 @@ impl DisplayFmt {
561 })563 })
562 .unwrap_or("")564 .unwrap_or("")
563 }565 }
566
567 /// Print extra text if the tag is exposed.
568 fn print_exposed(&self, exposed: bool) -> S {
569 if exposed { " (exposed)" } else { "" }
570 }
564}571}
565572
566/// Track the indentation of the tree.573/// Track the indentation of the tree.
...@@ -607,23 +614,21 @@ fn char_repeat(c: char, n: usize) -> String {...@@ -607,23 +614,21 @@ fn char_repeat(c: char, n: usize) -> String {
607struct DisplayRepr {614struct DisplayRepr {
608 tag: BorTag,615 tag: BorTag,
609 name: Option<String>,616 name: Option<String>,
617 exposed: bool,
610 rperm: Vec<Option<LocationState>>,618 rperm: Vec<Option<LocationState>>,
611 children: Vec<DisplayRepr>,619 children: Vec<DisplayRepr>,
612}620}
613621
614impl DisplayRepr {622impl DisplayRepr {
615 fn from(tree: &Tree, show_unnamed: bool) -> Option<Self> {623 fn from(tree: &Tree, root: UniIndex, show_unnamed: bool) -> Option<Self> {
616 let mut v = Vec::new();624 let mut v = Vec::new();
617 extraction_aux(tree, tree.root, show_unnamed, &mut v);625 extraction_aux(tree, root, show_unnamed, &mut v);
618 let Some(root) = v.pop() else {626 let Some(root) = v.pop() else {
619 if show_unnamed {627 if show_unnamed {
620 unreachable!(628 unreachable!(
621 "This allocation contains no tags, not even a root. This should not happen."629 "This allocation contains no tags, not even a root. This should not happen."
622 );630 );
623 }631 }
624 eprintln!(
625 "This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags."
626 );
627 return None;632 return None;
628 };633 };
629 assert!(v.is_empty());634 assert!(v.is_empty());
...@@ -637,6 +642,7 @@ impl DisplayRepr {...@@ -637,6 +642,7 @@ impl DisplayRepr {
637 ) {642 ) {
638 let node = tree.nodes.get(idx).unwrap();643 let node = tree.nodes.get(idx).unwrap();
639 let name = node.debug_info.name.clone();644 let name = node.debug_info.name.clone();
645 let exposed = node.is_exposed;
640 let children_sorted = {646 let children_sorted = {
641 let mut children = node.children.iter().cloned().collect::<Vec<_>>();647 let mut children = node.children.iter().cloned().collect::<Vec<_>>();
642 children.sort_by_key(|idx| tree.nodes.get(*idx).unwrap().tag);648 children.sort_by_key(|idx| tree.nodes.get(*idx).unwrap().tag);
...@@ -661,12 +667,13 @@ impl DisplayRepr {...@@ -661,12 +667,13 @@ impl DisplayRepr {
661 for child_idx in children_sorted {667 for child_idx in children_sorted {
662 extraction_aux(tree, child_idx, show_unnamed, &mut children);668 extraction_aux(tree, child_idx, show_unnamed, &mut children);
663 }669 }
664 acc.push(DisplayRepr { tag: node.tag, name, rperm, children });670 acc.push(DisplayRepr { tag: node.tag, name, rperm, children, exposed });
665 }671 }
666 }672 }
667 }673 }
668 fn print(674 fn print(
669 &self,675 main_root: &Option<DisplayRepr>,
676 wildcard_subtrees: &[DisplayRepr],
670 fmt: &DisplayFmt,677 fmt: &DisplayFmt,
671 indenter: &mut DisplayIndent,678 indenter: &mut DisplayIndent,
672 protected_tags: &FxHashMap<BorTag, ProtectorKind>,679 protected_tags: &FxHashMap<BorTag, ProtectorKind>,
...@@ -703,15 +710,41 @@ impl DisplayRepr {...@@ -703,15 +710,41 @@ impl DisplayRepr {
703 block.push(s);710 block.push(s);
704 }711 }
705 // This is the actual work712 // This is the actual work
706 print_aux(713 if let Some(root) = main_root {
707 self,714 print_aux(
708 &range_padding,715 root,
709 fmt,716 &range_padding,
710 indenter,717 fmt,
711 protected_tags,718 indenter,
712 true, /* root _is_ the last child */719 protected_tags,
713 &mut block,720 true, /* root _is_ the last child */
714 );721 false, /* not a wildcard_root*/
722 &mut block,
723 );
724 }
725 for tree in wildcard_subtrees.iter() {
726 let mut gap_line = String::new();
727 gap_line.push_str(fmt.perm.open);
728 for (i, &pad) in range_padding.iter().enumerate() {
729 if i > 0 {
730 gap_line.push_str(fmt.perm.sep);
731 }
732 gap_line.push_str(&format!("{}{}", char_repeat(' ', pad), " "));
733 }
734 gap_line.push_str(fmt.perm.close);
735 block.push(gap_line);
736
737 print_aux(
738 tree,
739 &range_padding,
740 fmt,
741 indenter,
742 protected_tags,
743 true, /* root _is_ the last child */
744 true, /* wildcard_root*/
745 &mut block,
746 );
747 }
715 // Then it's just prettifying it with a border of dashes.748 // Then it's just prettifying it with a border of dashes.
716 {749 {
717 let wr = &fmt.wrapper;750 let wr = &fmt.wrapper;
...@@ -741,6 +774,7 @@ impl DisplayRepr {...@@ -741,6 +774,7 @@ impl DisplayRepr {
741 indent: &mut DisplayIndent,774 indent: &mut DisplayIndent,
742 protected_tags: &FxHashMap<BorTag, ProtectorKind>,775 protected_tags: &FxHashMap<BorTag, ProtectorKind>,
743 is_last_child: bool,776 is_last_child: bool,
777 is_wildcard_root: bool,
744 acc: &mut Vec<String>,778 acc: &mut Vec<String>,
745 ) {779 ) {
746 let mut line = String::new();780 let mut line = String::new();
...@@ -760,7 +794,9 @@ impl DisplayRepr {...@@ -760,7 +794,9 @@ impl DisplayRepr {
760 indent.write(&mut line);794 indent.write(&mut line);
761 {795 {
762 // padding796 // padding
763 line.push_str(if is_last_child {797 line.push_str(if is_wildcard_root {
798 fmt.padding.wildcard_root
799 } else if is_last_child {
764 fmt.padding.join_last800 fmt.padding.join_last
765 } else {801 } else {
766 fmt.padding.join_middle802 fmt.padding.join_middle
...@@ -777,12 +813,22 @@ impl DisplayRepr {...@@ -777,12 +813,22 @@ impl DisplayRepr {
777 line.push_str(&fmt.print_tag(tree.tag, &tree.name));813 line.push_str(&fmt.print_tag(tree.tag, &tree.name));
778 let protector = protected_tags.get(&tree.tag);814 let protector = protected_tags.get(&tree.tag);
779 line.push_str(fmt.print_protector(protector));815 line.push_str(fmt.print_protector(protector));
816 line.push_str(fmt.print_exposed(tree.exposed));
780 // Push the line to the accumulator then recurse.817 // Push the line to the accumulator then recurse.
781 acc.push(line);818 acc.push(line);
782 let nb_children = tree.children.len();819 let nb_children = tree.children.len();
783 for (i, child) in tree.children.iter().enumerate() {820 for (i, child) in tree.children.iter().enumerate() {
784 indent.increment(fmt, is_last_child);821 indent.increment(fmt, is_last_child);
785 print_aux(child, padding, fmt, indent, protected_tags, i + 1 == nb_children, acc);822 print_aux(
823 child,
824 padding,
825 fmt,
826 indent,
827 protected_tags,
828 /* is_last_child */ i + 1 == nb_children,
829 /* is_wildcard_root */ false,
830 acc,
831 );
786 indent.decrement(fmt);832 indent.decrement(fmt);
787 }833 }
788 }834 }
...@@ -803,6 +849,7 @@ const DEFAULT_FORMATTER: DisplayFmt = DisplayFmt {...@@ -803,6 +849,7 @@ const DEFAULT_FORMATTER: DisplayFmt = DisplayFmt {
803 indent_last: " ",849 indent_last: " ",
804 join_haschild: "┬",850 join_haschild: "┬",
805 join_default: "─",851 join_default: "─",
852 wildcard_root: "*",
806 },853 },
807 accessed: DisplayFmtAccess { yes: " ", no: "?", meh: "-" },854 accessed: DisplayFmtAccess { yes: " ", no: "?", meh: "-" },
808};855};
...@@ -816,15 +863,27 @@ impl<'tcx> Tree {...@@ -816,15 +863,27 @@ impl<'tcx> Tree {
816 ) -> InterpResult<'tcx> {863 ) -> InterpResult<'tcx> {
817 let mut indenter = DisplayIndent::new();864 let mut indenter = DisplayIndent::new();
818 let ranges = self.locations.iter_all().map(|(range, _loc)| range).collect::<Vec<_>>();865 let ranges = self.locations.iter_all().map(|(range, _loc)| range).collect::<Vec<_>>();
819 if let Some(repr) = DisplayRepr::from(self, show_unnamed) {866 let main_tree = DisplayRepr::from(self, self.roots[0], show_unnamed);
820 repr.print(867 let wildcard_subtrees = self.roots[1..]
821 &DEFAULT_FORMATTER,868 .iter()
822 &mut indenter,869 .filter_map(|root| DisplayRepr::from(self, *root, show_unnamed))
823 protected_tags,870 .collect::<Vec<_>>();
824 ranges,871
825 /* print warning message about tags not shown */ !show_unnamed,872 if main_tree.is_none() && wildcard_subtrees.is_empty() {
873 eprintln!(
874 "This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags."
826 );875 );
827 }876 }
877
878 DisplayRepr::print(
879 &main_tree,
880 wildcard_subtrees.as_slice(),
881 &DEFAULT_FORMATTER,
882 &mut indenter,
883 protected_tags,
884 ranges,
885 /* print warning message about tags not shown */ !show_unnamed,
886 );
828 interp_ok(())887 interp_ok(())
829 }888 }
830}889}
src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs+6-10
...@@ -13,6 +13,7 @@ pub mod diagnostics;...@@ -13,6 +13,7 @@ pub mod diagnostics;
13mod foreign_access_skipping;13mod foreign_access_skipping;
14mod perms;14mod perms;
15mod tree;15mod tree;
16mod tree_visitor;
16mod unimap;17mod unimap;
17mod wildcard;18mod wildcard;
1819
...@@ -239,18 +240,14 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -239,18 +240,14 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
239 return interp_ok(new_prov);240 return interp_ok(new_prov);
240 }241 }
241 };242 };
243 let new_prov = Provenance::Concrete { alloc_id, tag: new_tag };
242244
243 log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;245 log_creation(this, Some((alloc_id, base_offset, parent_prov)))?;
244246
245 let orig_tag = match parent_prov {
246 ProvenanceExtra::Wildcard => return interp_ok(place.ptr().provenance), // TODO: handle retagging wildcard pointers
247 ProvenanceExtra::Concrete(tag) => tag,
248 };
249
250 trace!(247 trace!(
251 "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",248 "reborrow: reference {:?} derived from {:?} (pointee {}): {:?}, size {}",
252 new_tag,249 new_tag,
253 orig_tag,250 parent_prov,
254 place.layout.ty,251 place.layout.ty,
255 interpret::Pointer::new(alloc_id, base_offset),252 interpret::Pointer::new(alloc_id, base_offset),
256 ptr_size.bytes()253 ptr_size.bytes()
...@@ -281,7 +278,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -281,7 +278,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
281 assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here278 assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
282 // There's not actually any bytes here where accesses could even be tracked.279 // There's not actually any bytes here where accesses could even be tracked.
283 // Just produce the new provenance, nothing else to do.280 // Just produce the new provenance, nothing else to do.
284 return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));281 return interp_ok(Some(new_prov));
285 }282 }
286283
287 let protected = new_perm.protector.is_some();284 let protected = new_perm.protector.is_some();
...@@ -367,11 +364,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -367,11 +364,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
367 }364 }
368 }365 }
369 }366 }
370
371 // Record the parent-child pair in the tree.367 // Record the parent-child pair in the tree.
372 tree_borrows.new_child(368 tree_borrows.new_child(
373 base_offset,369 base_offset,
374 orig_tag,370 parent_prov,
375 new_tag,371 new_tag,
376 inside_perms,372 inside_perms,
377 new_perm.outside_perm,373 new_perm.outside_perm,
...@@ -380,7 +376,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -380,7 +376,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
380 )?;376 )?;
381 drop(tree_borrows);377 drop(tree_borrows);
382378
383 interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }))379 interp_ok(Some(new_prov))
384 }380 }
385381
386 fn tb_retag_place(382 fn tb_retag_place(
src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs+278-436
...@@ -18,15 +18,15 @@ use rustc_data_structures::fx::FxHashSet;...@@ -18,15 +18,15 @@ use rustc_data_structures::fx::FxHashSet;
18use rustc_span::Span;18use rustc_span::Span;
19use smallvec::SmallVec;19use smallvec::SmallVec;
2020
21use super::diagnostics::AccessCause;21use super::Permission;
22use super::wildcard::WildcardState;22use super::diagnostics::{
23use crate::borrow_tracker::tree_borrows::Permission;23 self, AccessCause, NodeDebugInfo, TbError, TransitionError, no_valid_exposed_references_error,
24use crate::borrow_tracker::tree_borrows::diagnostics::{
25 self, NodeDebugInfo, TbError, TransitionError, no_valid_exposed_references_error,
26};24};
27use crate::borrow_tracker::tree_borrows::foreign_access_skipping::IdempotentForeignAccess;25use super::foreign_access_skipping::IdempotentForeignAccess;
28use crate::borrow_tracker::tree_borrows::perms::PermTransition;26use super::perms::PermTransition;
29use crate::borrow_tracker::tree_borrows::unimap::{UniIndex, UniKeyMap, UniValMap};27use super::tree_visitor::{ChildrenVisitMode, ContinueTraversal, NodeAppArgs, TreeVisitor};
28use super::unimap::{UniIndex, UniKeyMap, UniValMap};
29use super::wildcard::WildcardState;
30use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind};30use crate::borrow_tracker::{AccessKind, GlobalState, ProtectorKind};
31use crate::*;31use crate::*;
3232
...@@ -91,11 +91,11 @@ impl LocationState {...@@ -91,11 +91,11 @@ impl LocationState {
91 nodes: &mut UniValMap<Node>,91 nodes: &mut UniValMap<Node>,
92 wildcard_accesses: &mut UniValMap<WildcardState>,92 wildcard_accesses: &mut UniValMap<WildcardState>,
93 access_kind: AccessKind,93 access_kind: AccessKind,
94 access_cause: AccessCause,94 access_cause: AccessCause, //diagnostics
95 access_range: Option<AllocRange>,95 access_range: Option<AllocRange>, //diagnostics
96 relatedness: AccessRelatedness,96 relatedness: AccessRelatedness,
97 span: Span,97 span: Span, //diagnostics
98 location_range: Range<u64>,98 location_range: Range<u64>, //diagnostics
99 protected: bool,99 protected: bool,
100 ) -> Result<(), TransitionError> {100 ) -> Result<(), TransitionError> {
101 // Call this function now (i.e. only if we know `relatedness`), which101 // Call this function now (i.e. only if we know `relatedness`), which
...@@ -294,8 +294,22 @@ pub struct Tree {...@@ -294,8 +294,22 @@ pub struct Tree {
294 pub(super) nodes: UniValMap<Node>,294 pub(super) nodes: UniValMap<Node>,
295 /// Associates with each location its state and wildcard access tracking.295 /// Associates with each location its state and wildcard access tracking.
296 pub(super) locations: DedupRangeMap<LocationTree>,296 pub(super) locations: DedupRangeMap<LocationTree>,
297 /// The index of the root node.297 /// Contains both the root of the main tree as well as the roots of the wildcard subtrees.
298 pub(super) root: UniIndex,298 ///
299 /// If we reborrow a reference which has wildcard provenance, then we do not know where in
300 /// the tree to attach them. Instead we create a new additional tree for this allocation
301 /// with this new reference as a root. We call this additional tree a wildcard subtree.
302 ///
303 /// The actual structure should be a single tree but with wildcard provenance we approximate
304 /// this with this ordered set of trees. Each wildcard subtree is the direct child of *some* exposed
305 /// tag (that is smaller than the root), but we do not know which. This also means that it can only be the
306 /// child of a tree that comes before it in the vec ensuring we don't have any cycles in our
307 /// approximated tree.
308 ///
309 /// Sorted according to `BorTag` from low to high. This also means the main root is `root[0]`.
310 ///
311 /// Has array size 2 because that still ensures the minimum size for SmallVec.
312 pub(super) roots: SmallVec<[UniIndex; 2]>,
299}313}
300314
301/// A node in the borrow tree. Each node is uniquely identified by a tag via315/// A node in the borrow tree. Each node is uniquely identified by a tag via
...@@ -325,262 +339,6 @@ pub(super) struct Node {...@@ -325,262 +339,6 @@ pub(super) struct Node {
325 pub debug_info: NodeDebugInfo,339 pub debug_info: NodeDebugInfo,
326}340}
327341
328/// Data given to the transition function
329struct NodeAppArgs<'visit> {
330 /// The index of the current node.
331 idx: UniIndex,
332 /// Relative position of the access.
333 rel_pos: AccessRelatedness,
334 /// The node map of this tree.
335 nodes: &'visit mut UniValMap<Node>,
336 /// The permissions map of this tree.
337 loc: &'visit mut LocationTree,
338}
339/// Internal contents of `Tree` with the minimum of mutable access for
340/// For soundness do not modify the children or parent indexes of nodes
341/// during traversal.
342struct TreeVisitor<'tree> {
343 nodes: &'tree mut UniValMap<Node>,
344 loc: &'tree mut LocationTree,
345}
346
347/// Whether to continue exploring the children recursively or not.
348enum ContinueTraversal {
349 Recurse,
350 SkipSelfAndChildren,
351}
352
353#[derive(Clone, Copy)]
354pub enum ChildrenVisitMode {
355 VisitChildrenOfAccessed,
356 SkipChildrenOfAccessed,
357}
358
359enum RecursionState {
360 BeforeChildren,
361 AfterChildren,
362}
363
364/// Stack of nodes left to explore in a tree traversal.
365/// See the docs of `traverse_this_parents_children_other` for details on the
366/// traversal order.
367struct TreeVisitorStack<NodeContinue, NodeApp> {
368 /// Function describing whether to continue at a tag.
369 /// This is only invoked for foreign accesses.
370 f_continue: NodeContinue,
371 /// Function to apply to each tag.
372 f_propagate: NodeApp,
373 /// Mutable state of the visit: the tags left to handle.
374 /// Every tag pushed should eventually be handled,
375 /// and the precise order is relevant for diagnostics.
376 /// Since the traversal is piecewise bottom-up, we need to
377 /// remember whether we're here initially, or after visiting all children.
378 /// The last element indicates this.
379 /// This is just an artifact of how you hand-roll recursion,
380 /// it does not have a deeper meaning otherwise.
381 stack: Vec<(UniIndex, AccessRelatedness, RecursionState)>,
382}
383
384impl<NodeContinue, NodeApp, Err> TreeVisitorStack<NodeContinue, NodeApp>
385where
386 NodeContinue: Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
387 NodeApp: Fn(NodeAppArgs<'_>) -> Result<(), Err>,
388{
389 fn should_continue_at(
390 &self,
391 this: &mut TreeVisitor<'_>,
392 idx: UniIndex,
393 rel_pos: AccessRelatedness,
394 ) -> ContinueTraversal {
395 let args = NodeAppArgs { idx, rel_pos, nodes: this.nodes, loc: this.loc };
396 (self.f_continue)(&args)
397 }
398
399 fn propagate_at(
400 &mut self,
401 this: &mut TreeVisitor<'_>,
402 idx: UniIndex,
403 rel_pos: AccessRelatedness,
404 ) -> Result<(), Err> {
405 (self.f_propagate)(NodeAppArgs { idx, rel_pos, nodes: this.nodes, loc: this.loc })
406 }
407
408 fn go_upwards_from_accessed(
409 &mut self,
410 this: &mut TreeVisitor<'_>,
411 accessed_node: UniIndex,
412 visit_children: ChildrenVisitMode,
413 ) -> Result<(), Err> {
414 // We want to visit the accessed node's children first.
415 // However, we will below walk up our parents and push their children (our cousins)
416 // onto the stack. To ensure correct iteration order, this method thus finishes
417 // by reversing the stack. This only works if the stack is empty initially.
418 assert!(self.stack.is_empty());
419 // First, handle accessed node. A bunch of things need to
420 // be handled differently here compared to the further parents
421 // of `accesssed_node`.
422 {
423 self.propagate_at(this, accessed_node, AccessRelatedness::LocalAccess)?;
424 if matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed) {
425 let accessed_node = this.nodes.get(accessed_node).unwrap();
426 // We `rev()` here because we reverse the entire stack later.
427 for &child in accessed_node.children.iter().rev() {
428 self.stack.push((
429 child,
430 AccessRelatedness::ForeignAccess,
431 RecursionState::BeforeChildren,
432 ));
433 }
434 }
435 }
436 // Then, handle the accessed node's parents. Here, we need to
437 // make sure we only mark the "cousin" subtrees for later visitation,
438 // not the subtree that contains the accessed node.
439 let mut last_node = accessed_node;
440 while let Some(current) = this.nodes.get(last_node).unwrap().parent {
441 self.propagate_at(this, current, AccessRelatedness::LocalAccess)?;
442 let node = this.nodes.get(current).unwrap();
443 // We `rev()` here because we reverse the entire stack later.
444 for &child in node.children.iter().rev() {
445 if last_node == child {
446 continue;
447 }
448 self.stack.push((
449 child,
450 AccessRelatedness::ForeignAccess,
451 RecursionState::BeforeChildren,
452 ));
453 }
454 last_node = current;
455 }
456 // Reverse the stack, as discussed above.
457 self.stack.reverse();
458 Ok(())
459 }
460
461 fn finish_foreign_accesses(&mut self, this: &mut TreeVisitor<'_>) -> Result<(), Err> {
462 while let Some((idx, rel_pos, step)) = self.stack.last_mut() {
463 let idx = *idx;
464 let rel_pos = *rel_pos;
465 match *step {
466 // How to do bottom-up traversal, 101: Before you handle a node, you handle all children.
467 // For this, you must first find the children, which is what this code here does.
468 RecursionState::BeforeChildren => {
469 // Next time we come back will be when all the children are handled.
470 *step = RecursionState::AfterChildren;
471 // Now push the children, except if we are told to skip this subtree.
472 let handle_children = self.should_continue_at(this, idx, rel_pos);
473 match handle_children {
474 ContinueTraversal::Recurse => {
475 let node = this.nodes.get(idx).unwrap();
476 for &child in node.children.iter() {
477 self.stack.push((child, rel_pos, RecursionState::BeforeChildren));
478 }
479 }
480 ContinueTraversal::SkipSelfAndChildren => {
481 // skip self
482 self.stack.pop();
483 continue;
484 }
485 }
486 }
487 // All the children are handled, let's actually visit this node
488 RecursionState::AfterChildren => {
489 self.stack.pop();
490 self.propagate_at(this, idx, rel_pos)?;
491 }
492 }
493 }
494 Ok(())
495 }
496
497 fn new(f_continue: NodeContinue, f_propagate: NodeApp) -> Self {
498 Self { f_continue, f_propagate, stack: Vec::new() }
499 }
500}
501
502impl<'tree> TreeVisitor<'tree> {
503 /// Applies `f_propagate` to every vertex of the tree in a piecewise bottom-up way: First, visit
504 /// all ancestors of `start_idx` (starting with `start_idx` itself), then children of `start_idx`, then the rest,
505 /// going bottom-up in each of these two "pieces" / sections.
506 /// This ensures that errors are triggered in the following order
507 /// - first invalid accesses with insufficient permissions, closest to the accessed node first,
508 /// - then protector violations, bottom-up, starting with the children of the accessed node, and then
509 /// going upwards and outwards.
510 ///
511 /// The following graphic visualizes it, with numbers indicating visitation order and `start_idx` being
512 /// the node that is visited first ("1"):
513 ///
514 /// ```text
515 /// 3
516 /// /|
517 /// / |
518 /// 9 2
519 /// | |\
520 /// | | \
521 /// 8 1 7
522 /// / \
523 /// 4 6
524 /// |
525 /// 5
526 /// ```
527 ///
528 /// `f_propagate` should follow the following format: for a given `Node` it updates its
529 /// `Permission` depending on the position relative to `start_idx` (given by an
530 /// `AccessRelatedness`).
531 /// `f_continue` is called earlier on foreign nodes, and describes whether to even start
532 /// visiting the subtree at that node. If it e.g. returns `SkipSelfAndChildren` on node 6
533 /// above, then nodes 5 _and_ 6 would not be visited by `f_propagate`. It is not used for
534 /// notes having a child access (nodes 1, 2, 3).
535 ///
536 /// Finally, remember that the iteration order is not relevant for UB, it only affects
537 /// diagnostics. It also affects tree traversal optimizations built on top of this, so
538 /// those need to be reviewed carefully as well whenever this changes.
539 fn traverse_this_parents_children_other<Err>(
540 mut self,
541 start_idx: UniIndex,
542 f_continue: impl Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
543 f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<(), Err>,
544 ) -> Result<(), Err> {
545 let mut stack = TreeVisitorStack::new(f_continue, f_propagate);
546 // Visits the accessed node itself, and all its parents, i.e. all nodes
547 // undergoing a child access. Also pushes the children and the other
548 // cousin nodes (i.e. all nodes undergoing a foreign access) to the stack
549 // to be processed later.
550 stack.go_upwards_from_accessed(
551 &mut self,
552 start_idx,
553 ChildrenVisitMode::VisitChildrenOfAccessed,
554 )?;
555 // Now visit all the foreign nodes we remembered earlier.
556 // For this we go bottom-up, but also allow f_continue to skip entire
557 // subtrees from being visited if it would be a NOP.
558 stack.finish_foreign_accesses(&mut self)
559 }
560
561 /// Like `traverse_this_parents_children_other`, but skips the children of `start_idx`.
562 fn traverse_nonchildren<Err>(
563 mut self,
564 start_idx: UniIndex,
565 f_continue: impl Fn(&NodeAppArgs<'_>) -> ContinueTraversal,
566 f_propagate: impl Fn(NodeAppArgs<'_>) -> Result<(), Err>,
567 ) -> Result<(), Err> {
568 let mut stack = TreeVisitorStack::new(f_continue, f_propagate);
569 // Visits the accessed node itself, and all its parents, i.e. all nodes
570 // undergoing a child access. Also pushes the other cousin nodes to the
571 // stack, but not the children of the accessed node.
572 stack.go_upwards_from_accessed(
573 &mut self,
574 start_idx,
575 ChildrenVisitMode::SkipChildrenOfAccessed,
576 )?;
577 // Now visit all the foreign nodes we remembered earlier.
578 // For this we go bottom-up, but also allow f_continue to skip entire
579 // subtrees from being visited if it would be a NOP.
580 stack.finish_foreign_accesses(&mut self)
581 }
582}
583
584impl Tree {342impl Tree {
585 /// Create a new tree, with only a root pointer.343 /// Create a new tree, with only a root pointer.
586 pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self {344 pub fn new(root_tag: BorTag, size: Size, span: Span) -> Self {
...@@ -625,7 +383,7 @@ impl Tree {...@@ -625,7 +383,7 @@ impl Tree {
625 let wildcard_accesses = UniValMap::default();383 let wildcard_accesses = UniValMap::default();
626 DedupRangeMap::new(size, LocationTree { perms, wildcard_accesses })384 DedupRangeMap::new(size, LocationTree { perms, wildcard_accesses })
627 };385 };
628 Self { root: root_idx, nodes, locations, tag_mapping }386 Self { roots: SmallVec::from_slice(&[root_idx]), nodes, locations, tag_mapping }
629 }387 }
630}388}
631389
...@@ -639,7 +397,7 @@ impl<'tcx> Tree {...@@ -639,7 +397,7 @@ impl<'tcx> Tree {
639 pub(super) fn new_child(397 pub(super) fn new_child(
640 &mut self,398 &mut self,
641 base_offset: Size,399 base_offset: Size,
642 parent_tag: BorTag,400 parent_prov: ProvenanceExtra,
643 new_tag: BorTag,401 new_tag: BorTag,
644 inside_perms: DedupRangeMap<LocationState>,402 inside_perms: DedupRangeMap<LocationState>,
645 outside_perm: Permission,403 outside_perm: Permission,
...@@ -647,7 +405,11 @@ impl<'tcx> Tree {...@@ -647,7 +405,11 @@ impl<'tcx> Tree {
647 span: Span,405 span: Span,
648 ) -> InterpResult<'tcx> {406 ) -> InterpResult<'tcx> {
649 let idx = self.tag_mapping.insert(new_tag);407 let idx = self.tag_mapping.insert(new_tag);
650 let parent_idx = self.tag_mapping.get(&parent_tag).unwrap();408 let parent_idx = match parent_prov {
409 ProvenanceExtra::Concrete(parent_tag) =>
410 Some(self.tag_mapping.get(&parent_tag).unwrap()),
411 ProvenanceExtra::Wildcard => None,
412 };
651 assert!(outside_perm.is_initial());413 assert!(outside_perm.is_initial());
652414
653 let default_strongest_idempotent =415 let default_strongest_idempotent =
...@@ -657,7 +419,7 @@ impl<'tcx> Tree {...@@ -657,7 +419,7 @@ impl<'tcx> Tree {
657 idx,419 idx,
658 Node {420 Node {
659 tag: new_tag,421 tag: new_tag,
660 parent: Some(parent_idx),422 parent: parent_idx,
661 children: SmallVec::default(),423 children: SmallVec::default(),
662 default_initial_perm: outside_perm,424 default_initial_perm: outside_perm,
663 default_initial_idempotent_foreign_access: default_strongest_idempotent,425 default_initial_idempotent_foreign_access: default_strongest_idempotent,
...@@ -665,9 +427,17 @@ impl<'tcx> Tree {...@@ -665,9 +427,17 @@ impl<'tcx> Tree {
665 debug_info: NodeDebugInfo::new(new_tag, outside_perm, span),427 debug_info: NodeDebugInfo::new(new_tag, outside_perm, span),
666 },428 },
667 );429 );
668 let parent_node = self.nodes.get_mut(parent_idx).unwrap();430 if let Some(parent_idx) = parent_idx {
669 // Register new_tag as a child of parent_tag431 let parent_node = self.nodes.get_mut(parent_idx).unwrap();
670 parent_node.children.push(idx);432 // Register new_tag as a child of parent_tag
433 parent_node.children.push(idx);
434 } else {
435 // If the parent had wildcard provenance, then register the idx
436 // as a new wildcard root.
437 // This preserves the orderedness of `roots` because a newly created
438 // tag is greater than all previous tags.
439 self.roots.push(idx);
440 }
671441
672 // We need to know the weakest SIFA for `update_idempotent_foreign_access_after_retag`.442 // We need to know the weakest SIFA for `update_idempotent_foreign_access_after_retag`.
673 let mut min_sifa = default_strongest_idempotent;443 let mut min_sifa = default_strongest_idempotent;
...@@ -691,19 +461,27 @@ impl<'tcx> Tree {...@@ -691,19 +461,27 @@ impl<'tcx> Tree {
691461
692 // We need to ensure the consistency of the wildcard access tracking data structure.462 // We need to ensure the consistency of the wildcard access tracking data structure.
693 // For this, we insert the correct entry for this tag based on its parent, if it exists.463 // For this, we insert the correct entry for this tag based on its parent, if it exists.
464 // If we are inserting a new wildcard root (with Wildcard as parent_prov) then we insert
465 // the special wildcard root initial state instead.
694 for (_range, loc) in self.locations.iter_mut_all() {466 for (_range, loc) in self.locations.iter_mut_all() {
695 if let Some(parent_access) = loc.wildcard_accesses.get(parent_idx) {467 if let Some(parent_idx) = parent_idx {
696 loc.wildcard_accesses.insert(idx, parent_access.for_new_child());468 if let Some(parent_access) = loc.wildcard_accesses.get(parent_idx) {
469 loc.wildcard_accesses.insert(idx, parent_access.for_new_child());
470 }
471 } else {
472 loc.wildcard_accesses.insert(idx, WildcardState::for_wildcard_root());
697 }473 }
698 }474 }
699475 // If the parent is a wildcard pointer, then it doesn't track SIFA and doesn't need to be updated.
700 // Inserting the new perms might have broken the SIFA invariant (see476 if let Some(parent_idx) = parent_idx {
701 // `foreign_access_skipping.rs`) if the SIFA we inserted is weaker than that of some parent.477 // Inserting the new perms might have broken the SIFA invariant (see
702 // We now weaken the recorded SIFA for our parents, until the invariant is restored. We478 // `foreign_access_skipping.rs`) if the SIFA we inserted is weaker than that of some parent.
703 // could weaken them all to `None`, but it is more efficient to compute the SIFA for the new479 // We now weaken the recorded SIFA for our parents, until the invariant is restored. We
704 // permission statically, and use that. For this we need the *minimum* SIFA (`None` needs480 // could weaken them all to `None`, but it is more efficient to compute the SIFA for the new
705 // more fixup than `Write`).481 // permission statically, and use that. For this we need the *minimum* SIFA (`None` needs
706 self.update_idempotent_foreign_access_after_retag(parent_idx, min_sifa);482 // more fixup than `Write`).
483 self.update_idempotent_foreign_access_after_retag(parent_idx, min_sifa);
484 }
707485
708 interp_ok(())486 interp_ok(())
709 }487 }
...@@ -772,52 +550,68 @@ impl<'tcx> Tree {...@@ -772,52 +550,68 @@ impl<'tcx> Tree {
772 span,550 span,
773 )?;551 )?;
774552
775 // The order in which we check if any nodes are invalidated only
776 // matters to diagnostics, so we use the root as a default tag.
777 let start_idx = match prov {553 let start_idx = match prov {
778 ProvenanceExtra::Concrete(tag) => self.tag_mapping.get(&tag).unwrap(),554 ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()),
779 ProvenanceExtra::Wildcard => self.root,555 ProvenanceExtra::Wildcard => None,
780 };556 };
781557
782 // Check if this breaks any strong protector.558 // Check if this breaks any strong protector.
783 // (Weak protectors are already handled by `perform_access`.)559 // (Weak protectors are already handled by `perform_access`.)
784 for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {560 for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
785 TreeVisitor { nodes: &mut self.nodes, loc }.traverse_this_parents_children_other(561 // Checks the tree containing `idx` for strong protector violations.
786 start_idx,562 // It does this in traversal order.
787 // Visit all children, skipping none.563 let mut check_tree = |idx| {
788 |_| ContinueTraversal::Recurse,564 TreeVisitor { nodes: &mut self.nodes, data: loc }
789 |args: NodeAppArgs<'_>| {565 .traverse_this_parents_children_other(
790 let node = args.nodes.get(args.idx).unwrap();566 idx,
791 let perm = args.loc.perms.entry(args.idx);567 // Visit all children, skipping none.
792568 |_| ContinueTraversal::Recurse,
793 let perm = perm.get().copied().unwrap_or_else(|| node.default_location_state());569 |args: NodeAppArgs<'_, _>| {
794 if global.borrow().protected_tags.get(&node.tag)570 let node = args.nodes.get(args.idx).unwrap();
795 == Some(&ProtectorKind::StrongProtector)571
796 // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`).572 let perm = args
797 // Related to https://github.com/rust-lang/rust/issues/55005.573 .data
798 && !perm.permission.is_cell()574 .perms
799 // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579.575 .get(args.idx)
800 && perm.accessed576 .copied()
801 {577 .unwrap_or_else(|| node.default_location_state());
802 Err(TbError {578 if global.borrow().protected_tags.get(&node.tag)
803 conflicting_info: &node.debug_info,579 == Some(&ProtectorKind::StrongProtector)
804 access_cause: diagnostics::AccessCause::Dealloc,580 // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`).
805 alloc_id,581 // Related to https://github.com/rust-lang/rust/issues/55005.
806 error_offset: loc_range.start,582 && !perm.permission.is_cell()
807 error_kind: TransitionError::ProtectedDealloc,583 // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579.
808 accessed_info: match prov {584 && perm.accessed
809 ProvenanceExtra::Concrete(_) =>585 {
810 Some(&args.nodes.get(start_idx).unwrap().debug_info),586 Err(TbError {
811 // We don't know from where the access came during a wildcard access.587 conflicting_info: &node.debug_info,
812 ProvenanceExtra::Wildcard => None,588 access_cause: diagnostics::AccessCause::Dealloc,
813 },589 alloc_id,
814 }590 error_offset: loc_range.start,
815 .build())591 error_kind: TransitionError::ProtectedDealloc,
816 } else {592 accessed_info: start_idx
817 Ok(())593 .map(|idx| &args.nodes.get(idx).unwrap().debug_info),
818 }594 }
819 },595 .build())
820 )?;596 } else {
597 Ok(())
598 }
599 },
600 )
601 };
602 // If we have a start index we first check its subtree in traversal order.
603 // This results in us showing the error of the closest node instead of an
604 // arbitrary one.
605 let accessed_root = start_idx.map(&mut check_tree).transpose()?;
606 // Afterwards we check all other trees.
607 // We iterate over the list in reverse order to ensure that we do not visit
608 // a parent before its child.
609 for &root in self.roots.iter().rev() {
610 if Some(root) == accessed_root {
611 continue;
612 }
613 check_tree(root)?;
614 }
821 }615 }
822 interp_ok(())616 interp_ok(())
823 }617 }
...@@ -849,20 +643,20 @@ impl<'tcx> Tree {...@@ -849,20 +643,20 @@ impl<'tcx> Tree {
849 span: Span, // diagnostics643 span: Span, // diagnostics
850 ) -> InterpResult<'tcx> {644 ) -> InterpResult<'tcx> {
851 #[cfg(feature = "expensive-consistency-checks")]645 #[cfg(feature = "expensive-consistency-checks")]
852 if matches!(prov, ProvenanceExtra::Wildcard) {646 if self.roots.len() > 1 || matches!(prov, ProvenanceExtra::Wildcard) {
853 self.verify_wildcard_consistency(global);647 self.verify_wildcard_consistency(global);
854 }648 }
649
855 let source_idx = match prov {650 let source_idx = match prov {
856 ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()),651 ProvenanceExtra::Concrete(tag) => Some(self.tag_mapping.get(&tag).unwrap()),
857 ProvenanceExtra::Wildcard => None,652 ProvenanceExtra::Wildcard => None,
858 };653 };
859
860 if let Some((access_range, access_kind, access_cause)) = access_range_and_kind {654 if let Some((access_range, access_kind, access_cause)) = access_range_and_kind {
861 // Default branch: this is a "normal" access through a known range.655 // Default branch: this is a "normal" access through a known range.
862 // We iterate over affected locations and traverse the tree for each of them.656 // We iterate over affected locations and traverse the tree for each of them.
863 for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {657 for (loc_range, loc) in self.locations.iter_mut(access_range.start, access_range.size) {
864 loc.perform_access(658 loc.perform_access(
865 self.root,659 self.roots.iter().copied(),
866 &mut self.nodes,660 &mut self.nodes,
867 source_idx,661 source_idx,
868 loc_range,662 loc_range,
...@@ -898,7 +692,7 @@ impl<'tcx> Tree {...@@ -898,7 +692,7 @@ impl<'tcx> Tree {
898 {692 {
899 let access_cause = diagnostics::AccessCause::FnExit(access_kind);693 let access_cause = diagnostics::AccessCause::FnExit(access_kind);
900 loc.perform_access(694 loc.perform_access(
901 self.root,695 self.roots.iter().copied(),
902 &mut self.nodes,696 &mut self.nodes,
903 Some(source_idx),697 Some(source_idx),
904 loc_range,698 loc_range,
...@@ -920,7 +714,9 @@ impl<'tcx> Tree {...@@ -920,7 +714,9 @@ impl<'tcx> Tree {
920/// Integration with the BorTag garbage collector714/// Integration with the BorTag garbage collector
921impl Tree {715impl Tree {
922 pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) {716 pub fn remove_unreachable_tags(&mut self, live_tags: &FxHashSet<BorTag>) {
923 self.remove_useless_children(self.root, live_tags);717 for i in 0..(self.roots.len()) {
718 self.remove_useless_children(self.roots[i], live_tags);
719 }
924 // Right after the GC runs is a good moment to check if we can720 // Right after the GC runs is a good moment to check if we can
925 // merge some adjacent ranges that were made equal by the removal of some721 // merge some adjacent ranges that were made equal by the removal of some
926 // tags (this does not necessarily mean that they have identical internal representations,722 // tags (this does not necessarily mean that they have identical internal representations,
...@@ -1073,20 +869,20 @@ impl<'tcx> LocationTree {...@@ -1073,20 +869,20 @@ impl<'tcx> LocationTree {
1073 /// * `visit_children`: Whether to skip updating the children of `access_source`.869 /// * `visit_children`: Whether to skip updating the children of `access_source`.
1074 fn perform_access(870 fn perform_access(
1075 &mut self,871 &mut self,
1076 root: UniIndex,872 roots: impl Iterator<Item = UniIndex>,
1077 nodes: &mut UniValMap<Node>,873 nodes: &mut UniValMap<Node>,
1078 access_source: Option<UniIndex>,874 access_source: Option<UniIndex>,
1079 loc_range: Range<u64>,875 loc_range: Range<u64>, // diagnostics
1080 access_range: Option<AllocRange>,876 access_range: Option<AllocRange>, // diagnostics
1081 access_kind: AccessKind,877 access_kind: AccessKind,
1082 access_cause: diagnostics::AccessCause,878 access_cause: diagnostics::AccessCause, // diagnostics
1083 global: &GlobalState,879 global: &GlobalState,
1084 alloc_id: AllocId, // diagnostics880 alloc_id: AllocId, // diagnostics
1085 span: Span, // diagnostics881 span: Span, // diagnostics
1086 visit_children: ChildrenVisitMode,882 visit_children: ChildrenVisitMode,
1087 ) -> InterpResult<'tcx> {883 ) -> InterpResult<'tcx> {
1088 if let Some(idx) = access_source {884 let accessed_root = if let Some(idx) = access_source {
1089 self.perform_normal_access(885 Some(self.perform_normal_access(
1090 idx,886 idx,
1091 nodes,887 nodes,
1092 loc_range.clone(),888 loc_range.clone(),
...@@ -1097,13 +893,38 @@ impl<'tcx> LocationTree {...@@ -1097,13 +893,38 @@ impl<'tcx> LocationTree {
1097 alloc_id,893 alloc_id,
1098 span,894 span,
1099 visit_children,895 visit_children,
1100 )896 )?)
1101 } else {897 } else {
1102 // `SkipChildrenOfAccessed` only gets set on protector release.898 // `SkipChildrenOfAccessed` only gets set on protector release, which only
1103 // Since a wildcard reference are never protected this assert shouldn't fail.899 // occurs on a known node.
1104 assert!(matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed));900 assert!(matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed));
901 None
902 };
903
904 let accessed_root_tag = accessed_root.map(|idx| nodes.get(idx).unwrap().tag);
905 if matches!(visit_children, ChildrenVisitMode::SkipChildrenOfAccessed) {
906 // FIXME: approximate which roots could be children of the accessed node and only skip them instead of all other trees.
907 return interp_ok(());
908 }
909 for root in roots {
910 // We don't perform a wildcard access on the tree we already performed a
911 // normal access on.
912 if Some(root) == accessed_root {
913 continue;
914 }
915 // The choice of `max_local_tag` requires some thought.
916 // This can only be a local access for nodes that are a parent of the accessed node
917 // and are therefore smaller, so the accessed node itself is a valid choice for `max_local_tag`.
918 // However, using `accessed_root` is better since that will be smaller. It is still a valid choice
919 // because for nodes *in other trees*, if they are a parent of the accessed node then they
920 // are a parent of `accessed_root`.
921 //
922 // As a consequence of this, since the root of the main tree is the smallest tag in the entire
923 // allocation, if the access occurred in the main tree then other subtrees will only see foreign accesses.
1105 self.perform_wildcard_access(924 self.perform_wildcard_access(
1106 root,925 root,
926 access_source,
927 /*max_local_tag*/ accessed_root_tag,
1107 nodes,928 nodes,
1108 loc_range.clone(),929 loc_range.clone(),
1109 access_range,930 access_range,
...@@ -1112,11 +933,14 @@ impl<'tcx> LocationTree {...@@ -1112,11 +933,14 @@ impl<'tcx> LocationTree {
1112 global,933 global,
1113 alloc_id,934 alloc_id,
1114 span,935 span,
1115 )936 )?;
1116 }937 }
938 interp_ok(())
1117 }939 }
1118940
1119 /// Performs a normal access on the tree containing `access_source`.941 /// Performs a normal access on the tree containing `access_source`.
942 ///
943 /// Returns the root index of this tree.
1120 /// * `access_source`: The index of the tag being accessed.944 /// * `access_source`: The index of the tag being accessed.
1121 /// * `visit_children`: Whether to skip the children of `access_source`945 /// * `visit_children`: Whether to skip the children of `access_source`
1122 /// during the access. Used for protector end access.946 /// during the access. Used for protector end access.
...@@ -1124,15 +948,15 @@ impl<'tcx> LocationTree {...@@ -1124,15 +948,15 @@ impl<'tcx> LocationTree {
1124 &mut self,948 &mut self,
1125 access_source: UniIndex,949 access_source: UniIndex,
1126 nodes: &mut UniValMap<Node>,950 nodes: &mut UniValMap<Node>,
1127 loc_range: Range<u64>,951 loc_range: Range<u64>, // diagnostics
1128 access_range: Option<AllocRange>,952 access_range: Option<AllocRange>, // diagnostics
1129 access_kind: AccessKind,953 access_kind: AccessKind,
1130 access_cause: diagnostics::AccessCause,954 access_cause: diagnostics::AccessCause, // diagnostics
1131 global: &GlobalState,955 global: &GlobalState,
1132 alloc_id: AllocId, // diagnostics956 alloc_id: AllocId, // diagnostics
1133 span: Span, // diagnostics957 span: Span, // diagnostics
1134 visit_children: ChildrenVisitMode,958 visit_children: ChildrenVisitMode,
1135 ) -> InterpResult<'tcx> {959 ) -> InterpResult<'tcx, UniIndex> {
1136 // Performs the per-node work:960 // Performs the per-node work:
1137 // - insert the permission if it does not exist961 // - insert the permission if it does not exist
1138 // - perform the access962 // - perform the access
...@@ -1141,18 +965,18 @@ impl<'tcx> LocationTree {...@@ -1141,18 +965,18 @@ impl<'tcx> LocationTree {
1141 // - skip the traversal of the children in some cases965 // - skip the traversal of the children in some cases
1142 // - do not record noop transitions966 // - do not record noop transitions
1143 //967 //
1144 // `perms_range` is only for diagnostics (it is the range of968 // `loc_range` is only for diagnostics (it is the range of
1145 // the `RangeMap` on which we are currently working).969 // the `RangeMap` on which we are currently working).
1146 let node_skipper = |args: &NodeAppArgs<'_>| -> ContinueTraversal {970 let node_skipper = |args: &NodeAppArgs<'_, LocationTree>| -> ContinueTraversal {
1147 let node = args.nodes.get(args.idx).unwrap();971 let node = args.nodes.get(args.idx).unwrap();
1148 let perm = args.loc.perms.get(args.idx);972 let perm = args.data.perms.get(args.idx);
1149973
1150 let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());974 let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());
1151 old_state.skip_if_known_noop(access_kind, args.rel_pos)975 old_state.skip_if_known_noop(access_kind, args.rel_pos)
1152 };976 };
1153 let node_app = |args: NodeAppArgs<'_>| -> Result<(), _> {977 let node_app = |args: NodeAppArgs<'_, LocationTree>| {
1154 let node = args.nodes.get_mut(args.idx).unwrap();978 let node = args.nodes.get_mut(args.idx).unwrap();
1155 let mut perm = args.loc.perms.entry(args.idx);979 let mut perm = args.data.perms.entry(args.idx);
1156980
1157 let state = perm.or_insert(node.default_location_state());981 let state = perm.or_insert(node.default_location_state());
1158982
...@@ -1161,10 +985,10 @@ impl<'tcx> LocationTree {...@@ -1161,10 +985,10 @@ impl<'tcx> LocationTree {
1161 .perform_transition(985 .perform_transition(
1162 args.idx,986 args.idx,
1163 args.nodes,987 args.nodes,
1164 &mut args.loc.wildcard_accesses,988 &mut args.data.wildcard_accesses,
1165 access_kind,989 access_kind,
1166 access_cause,990 access_cause,
1167 /* access_range */ access_range,991 access_range,
1168 args.rel_pos,992 args.rel_pos,
1169 span,993 span,
1170 loc_range.clone(),994 loc_range.clone(),
...@@ -1182,7 +1006,8 @@ impl<'tcx> LocationTree {...@@ -1182,7 +1006,8 @@ impl<'tcx> LocationTree {
1182 .build()1006 .build()
1183 })1007 })
1184 };1008 };
1185 let visitor = TreeVisitor { nodes, loc: self };1009
1010 let visitor = TreeVisitor { nodes, data: self };
1186 match visit_children {1011 match visit_children {
1187 ChildrenVisitMode::VisitChildrenOfAccessed =>1012 ChildrenVisitMode::VisitChildrenOfAccessed =>
1188 visitor.traverse_this_parents_children_other(access_source, node_skipper, node_app),1013 visitor.traverse_this_parents_children_other(access_source, node_skipper, node_app),
...@@ -1191,31 +1016,61 @@ impl<'tcx> LocationTree {...@@ -1191,31 +1016,61 @@ impl<'tcx> LocationTree {
1191 }1016 }
1192 .into()1017 .into()
1193 }1018 }
1019
1194 /// Performs a wildcard access on the tree with root `root`. Takes the `access_relatedness`1020 /// Performs a wildcard access on the tree with root `root`. Takes the `access_relatedness`
1195 /// for each node from the `WildcardState` datastructure.1021 /// for each node from the `WildcardState` datastructure.
1196 /// * `root`: Root of the tree being accessed.1022 /// * `root`: Root of the tree being accessed.
1023 /// * `access_source`: the index of the accessed tag, if any.
1024 /// This is only used for printing the correct tag on errors.
1025 /// * `max_local_tag`: The access can only be local for nodes whose tag is
1026 /// at most `max_local_tag`.
1197 fn perform_wildcard_access(1027 fn perform_wildcard_access(
1198 &mut self,1028 &mut self,
1199 root: UniIndex,1029 root: UniIndex,
1030 access_source: Option<UniIndex>,
1031 max_local_tag: Option<BorTag>,
1200 nodes: &mut UniValMap<Node>,1032 nodes: &mut UniValMap<Node>,
1201 loc_range: Range<u64>,1033 loc_range: Range<u64>, // diagnostics
1202 access_range: Option<AllocRange>,1034 access_range: Option<AllocRange>, // diagnostics
1203 access_kind: AccessKind,1035 access_kind: AccessKind,
1204 access_cause: diagnostics::AccessCause,1036 access_cause: diagnostics::AccessCause, // diagnostics
1205 global: &GlobalState,1037 global: &GlobalState,
1206 alloc_id: AllocId, // diagnostics1038 alloc_id: AllocId, // diagnostics
1207 span: Span, // diagnostics1039 span: Span, // diagnostics
1208 ) -> InterpResult<'tcx> {1040 ) -> InterpResult<'tcx> {
1209 let f_continue =1041 let get_relatedness = |idx: UniIndex, node: &Node, loc: &LocationTree| {
1210 |idx: UniIndex, nodes: &UniValMap<Node>, loc: &LocationTree| -> ContinueTraversal {1042 let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default();
1211 let node = nodes.get(idx).unwrap();1043 // If the tag is larger than `max_local_tag` then the access can only be foreign.
1212 let perm = loc.perms.get(idx);1044 let only_foreign = max_local_tag.is_some_and(|max_local_tag| max_local_tag < node.tag);
1213 let wildcard_state = loc.wildcard_accesses.get(idx).cloned().unwrap_or_default();1045 wildcard_state.access_relatedness(access_kind, only_foreign)
1046 };
1047
1048 // This does a traversal across the tree updating children before their parents. The
1049 // difference to `perform_normal_access` is that we take the access relatedness from
1050 // the wildcard tracking state of the node instead of from the visitor itself.
1051 //
1052 // Unlike for a normal access, the iteration order is important for improving the
1053 // accuracy of wildcard accesses if `max_local_tag` is `Some`: processing the effects of this
1054 // access further down the tree can cause exposed nodes to lose permissions, thus updating
1055 // the wildcard data structure, which will be taken into account when processing the parent
1056 // nodes. Also see the test `cross_tree_update_older_invalid_exposed2.rs`
1057 // (Doing accesses in the opposite order cannot help with precision but the reasons are complicated;
1058 // see <https://github.com/rust-lang/miri/pull/4707#discussion_r2581661123>.)
1059 //
1060 // Note, however, that this is an approximation: there can be situations where a node is
1061 // marked as having an exposed foreign node, but actually that foreign node cannot be
1062 // the source of the access due to `max_local_tag`. The wildcard tracking cannot know
1063 // about `max_local_tag` so we will incorrectly assume that this might be a foreign access.
1064 TreeVisitor { data: self, nodes }.traverse_children_this(
1065 root,
1066 |args| -> ContinueTraversal {
1067 let node = args.nodes.get(args.idx).unwrap();
1068 let perm = args.data.perms.get(args.idx);
12141069
1215 let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());1070 let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());
1216 // If we know where, relative to this node, the wildcard access occurs,1071 // If we know where, relative to this node, the wildcard access occurs,
1217 // then check if we can skip the entire subtree.1072 // then check if we can skip the entire subtree.
1218 if let Some(relatedness) = wildcard_state.access_relatedness(access_kind)1073 if let Some(relatedness) = get_relatedness(args.idx, node, args.data)
1219 && let Some(relatedness) = relatedness.to_relatedness()1074 && let Some(relatedness) = relatedness.to_relatedness()
1220 {1075 {
1221 // We can use the usual SIFA machinery to skip nodes.1076 // We can use the usual SIFA machinery to skip nodes.
...@@ -1223,78 +1078,64 @@ impl<'tcx> LocationTree {...@@ -1223,78 +1078,64 @@ impl<'tcx> LocationTree {
1223 } else {1078 } else {
1224 ContinueTraversal::Recurse1079 ContinueTraversal::Recurse
1225 }1080 }
1226 };1081 },
1227 // This does a traversal starting from the root through the tree updating1082 |args| {
1228 // the permissions of each node.1083 let node = args.nodes.get_mut(args.idx).unwrap();
1229 // The difference to `perform_access` is that we take the access1084
1230 // relatedness from the wildcard tracking state of the node instead of1085 let protected = global.borrow().protected_tags.contains_key(&node.tag);
1231 // from the visitor itself.1086
1232 TreeVisitor { loc: self, nodes }1087 let Some(wildcard_relatedness) = get_relatedness(args.idx, node, args.data) else {
1233 .traverse_this_parents_children_other(1088 // There doesn't exist a valid exposed reference for this access to
1234 root,1089 // happen through.
1235 |args| f_continue(args.idx, args.nodes, args.loc),1090 // This can only happen if `root` is the main root: We set
1236 |args| {1091 // `max_foreign_access==Write` on all wildcard roots, so at least a foreign access
1237 let node = args.nodes.get_mut(args.idx).unwrap();1092 // is always possible on all nodes in a wildcard subtree.
1238 let mut entry = args.loc.perms.entry(args.idx);1093 return Err(no_valid_exposed_references_error(
1239 let perm = entry.or_insert(node.default_location_state());1094 alloc_id,
12401095 loc_range.start,
1241 let protected = global.borrow().protected_tags.contains_key(&node.tag);1096 access_cause,
1097 ));
1098 };
12421099
1243 let Some(wildcard_relatedness) = args1100 let Some(relatedness) = wildcard_relatedness.to_relatedness() else {
1244 .loc1101 // If the access type is Either, then we do not apply any transition
1245 .wildcard_accesses1102 // to this node, but we still update each of its children.
1246 .get(args.idx)1103 // This is an imprecision! In the future, maybe we can still do some sort
1247 .and_then(|s| s.access_relatedness(access_kind))1104 // of best-effort update here.
1248 else {1105 return Ok(());
1249 // There doesn't exist a valid exposed reference for this access to1106 };
1250 // happen through.
1251 // If this fails for one id, then it fails for all ids so this.
1252 // Since we always check the root first, this means it should always
1253 // fail on the root.
1254 assert_eq!(root, args.idx);
1255 return Err(no_valid_exposed_references_error(
1256 alloc_id,
1257 loc_range.start,
1258 access_cause,
1259 ));
1260 };
12611107
1262 let Some(relatedness) = wildcard_relatedness.to_relatedness() else {1108 let mut entry = args.data.perms.entry(args.idx);
1263 // If the access type is Either, then we do not apply any transition1109 let perm = entry.or_insert(node.default_location_state());
1264 // to this node, but we still update each of its children.1110 // We know the exact relatedness, so we can actually do precise checks.
1265 // This is an imprecision! In the future, maybe we can still do some sort1111 perm.perform_transition(
1266 // of best-effort update here.1112 args.idx,
1267 return Ok(());1113 args.nodes,
1268 };1114 &mut args.data.wildcard_accesses,
1269 // We know the exact relatedness, so we can actually do precise checks.1115 access_kind,
1270 perm.perform_transition(1116 access_cause,
1271 args.idx,1117 access_range,
1272 args.nodes,1118 relatedness,
1273 &mut args.loc.wildcard_accesses,1119 span,
1274 access_kind,1120 loc_range.clone(),
1121 protected,
1122 )
1123 .map_err(|trans| {
1124 let node = args.nodes.get(args.idx).unwrap();
1125 TbError {
1126 conflicting_info: &node.debug_info,
1275 access_cause,1127 access_cause,
1276 access_range,1128 alloc_id,
1277 relatedness,1129 error_offset: loc_range.start,
1278 span,1130 error_kind: trans,
1279 loc_range.clone(),1131 accessed_info: access_source
1280 protected,1132 .map(|idx| &args.nodes.get(idx).unwrap().debug_info),
1281 )1133 }
1282 .map_err(|trans| {1134 .build()
1283 let node = args.nodes.get(args.idx).unwrap();1135 })
1284 TbError {1136 },
1285 conflicting_info: &node.debug_info,1137 )?;
1286 access_cause,1138 interp_ok(())
1287 alloc_id,
1288 error_offset: loc_range.start,
1289 error_kind: trans,
1290 // We don't know from where the access came during a wildcard access.
1291 accessed_info: None,
1292 }
1293 .build()
1294 })
1295 },
1296 )
1297 .into()
1298 }1139 }
1299}1140}
13001141
...@@ -1309,10 +1150,11 @@ impl Node {...@@ -1309,10 +1150,11 @@ impl Node {
13091150
1310impl VisitProvenance for Tree {1151impl VisitProvenance for Tree {
1311 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {1152 fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
1312 // To ensure that the root never gets removed, we visit it1153 // To ensure that the roots never get removed, we visit them.
1313 // (the `root` node of `Tree` is not an `Option<_>`)1154 // FIXME: it should be possible to GC wildcard tree roots.
1314 visit(None, Some(self.nodes.get(self.root).unwrap().tag));1155 for id in self.roots.iter().copied() {
13151156 visit(None, Some(self.nodes.get(id).unwrap().tag));
1157 }
1316 // We also need to keep around any exposed tags through which1158 // We also need to keep around any exposed tags through which
1317 // an access could still happen.1159 // an access could still happen.
1318 for (_id, node) in self.nodes.iter() {1160 for (_id, node) in self.nodes.iter() {
src/tools/miri/src/borrow_tracker/tree_borrows/tree_visitor.rs created+289
...@@ -0,0 +1,289 @@
1use std::marker::PhantomData;
2
3use super::tree::{AccessRelatedness, Node};
4use super::unimap::{UniIndex, UniValMap};
5
6/// Data given to the transition function
7pub struct NodeAppArgs<'visit, T> {
8 /// The index of the current node.
9 pub idx: UniIndex,
10 /// Relative position of the access.
11 pub rel_pos: AccessRelatedness,
12 /// The node map of this tree.
13 pub nodes: &'visit mut UniValMap<Node>,
14 /// Additional data we want to be able to modify in f_propagate and read in f_continue.
15 pub data: &'visit mut T,
16}
17/// Internal contents of `Tree` with the minimum of mutable access for
18/// For soundness do not modify the children or parent indexes of nodes
19/// during traversal.
20pub struct TreeVisitor<'tree, T> {
21 pub nodes: &'tree mut UniValMap<Node>,
22 pub data: &'tree mut T,
23}
24
25/// Whether to continue exploring the children recursively or not.
26#[derive(Debug)]
27pub enum ContinueTraversal {
28 Recurse,
29 SkipSelfAndChildren,
30}
31
32#[derive(Clone, Copy, Debug)]
33pub enum ChildrenVisitMode {
34 VisitChildrenOfAccessed,
35 SkipChildrenOfAccessed,
36}
37
38enum RecursionState {
39 BeforeChildren,
40 AfterChildren,
41}
42
43/// Stack of nodes left to explore in a tree traversal.
44/// See the docs of `traverse_this_parents_children_other` for details on the
45/// traversal order.
46struct TreeVisitorStack<NodeContinue, NodeApp, T> {
47 /// Function describing whether to continue at a tag.
48 /// This is only invoked for foreign accesses.
49 f_continue: NodeContinue,
50 /// Function to apply to each tag.
51 f_propagate: NodeApp,
52 /// Mutable state of the visit: the tags left to handle.
53 /// Every tag pushed should eventually be handled,
54 /// and the precise order is relevant for diagnostics.
55 /// Since the traversal is piecewise bottom-up, we need to
56 /// remember whether we're here initially, or after visiting all children.
57 /// The last element indicates this.
58 /// This is just an artifact of how you hand-roll recursion,
59 /// it does not have a deeper meaning otherwise.
60 stack: Vec<(UniIndex, AccessRelatedness, RecursionState)>,
61 phantom: PhantomData<T>,
62}
63
64impl<NodeContinue, NodeApp, T, Err> TreeVisitorStack<NodeContinue, NodeApp, T>
65where
66 NodeContinue: Fn(&NodeAppArgs<'_, T>) -> ContinueTraversal,
67 NodeApp: FnMut(NodeAppArgs<'_, T>) -> Result<(), Err>,
68{
69 fn should_continue_at(
70 &self,
71 this: &mut TreeVisitor<'_, T>,
72 idx: UniIndex,
73 rel_pos: AccessRelatedness,
74 ) -> ContinueTraversal {
75 let args = NodeAppArgs { idx, rel_pos, nodes: this.nodes, data: this.data };
76 (self.f_continue)(&args)
77 }
78
79 fn propagate_at(
80 &mut self,
81 this: &mut TreeVisitor<'_, T>,
82 idx: UniIndex,
83 rel_pos: AccessRelatedness,
84 ) -> Result<(), Err> {
85 (self.f_propagate)(NodeAppArgs { idx, rel_pos, nodes: this.nodes, data: this.data })
86 }
87
88 /// Returns the root of this tree.
89 fn go_upwards_from_accessed(
90 &mut self,
91 this: &mut TreeVisitor<'_, T>,
92 accessed_node: UniIndex,
93 visit_children: ChildrenVisitMode,
94 ) -> Result<UniIndex, Err> {
95 // We want to visit the accessed node's children first.
96 // However, we will below walk up our parents and push their children (our cousins)
97 // onto the stack. To ensure correct iteration order, this method thus finishes
98 // by reversing the stack. This only works if the stack is empty initially.
99 assert!(self.stack.is_empty());
100 // First, handle accessed node. A bunch of things need to
101 // be handled differently here compared to the further parents
102 // of `accesssed_node`.
103 {
104 self.propagate_at(this, accessed_node, AccessRelatedness::LocalAccess)?;
105 if matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed) {
106 let accessed_node = this.nodes.get(accessed_node).unwrap();
107 // We `rev()` here because we reverse the entire stack later.
108 for &child in accessed_node.children.iter().rev() {
109 self.stack.push((
110 child,
111 AccessRelatedness::ForeignAccess,
112 RecursionState::BeforeChildren,
113 ));
114 }
115 }
116 }
117 // Then, handle the accessed node's parents. Here, we need to
118 // make sure we only mark the "cousin" subtrees for later visitation,
119 // not the subtree that contains the accessed node.
120 let mut last_node = accessed_node;
121 while let Some(current) = this.nodes.get(last_node).unwrap().parent {
122 self.propagate_at(this, current, AccessRelatedness::LocalAccess)?;
123 let node = this.nodes.get(current).unwrap();
124 // We `rev()` here because we reverse the entire stack later.
125 for &child in node.children.iter().rev() {
126 if last_node == child {
127 continue;
128 }
129 self.stack.push((
130 child,
131 AccessRelatedness::ForeignAccess,
132 RecursionState::BeforeChildren,
133 ));
134 }
135 last_node = current;
136 }
137 // Reverse the stack, as discussed above.
138 self.stack.reverse();
139 Ok(last_node)
140 }
141
142 fn finish_foreign_accesses(&mut self, this: &mut TreeVisitor<'_, T>) -> Result<(), Err> {
143 while let Some((idx, rel_pos, step)) = self.stack.last_mut() {
144 let idx = *idx;
145 let rel_pos = *rel_pos;
146 match *step {
147 // How to do bottom-up traversal, 101: Before you handle a node, you handle all children.
148 // For this, you must first find the children, which is what this code here does.
149 RecursionState::BeforeChildren => {
150 // Next time we come back will be when all the children are handled.
151 *step = RecursionState::AfterChildren;
152 // Now push the children, except if we are told to skip this subtree.
153 let handle_children = self.should_continue_at(this, idx, rel_pos);
154 match handle_children {
155 ContinueTraversal::Recurse => {
156 let node = this.nodes.get(idx).unwrap();
157 for &child in node.children.iter() {
158 self.stack.push((child, rel_pos, RecursionState::BeforeChildren));
159 }
160 }
161 ContinueTraversal::SkipSelfAndChildren => {
162 // skip self
163 self.stack.pop();
164 continue;
165 }
166 }
167 }
168 // All the children are handled, let's actually visit this node
169 RecursionState::AfterChildren => {
170 self.stack.pop();
171 self.propagate_at(this, idx, rel_pos)?;
172 }
173 }
174 }
175 Ok(())
176 }
177
178 fn new(f_continue: NodeContinue, f_propagate: NodeApp) -> Self {
179 Self { f_continue, f_propagate, stack: Vec::new(), phantom: PhantomData }
180 }
181}
182
183impl<'tree, T> TreeVisitor<'tree, T> {
184 /// Applies `f_propagate` to every vertex of the tree in a piecewise bottom-up way: First, visit
185 /// all ancestors of `start_idx` (starting with `start_idx` itself), then children of `start_idx`, then the rest,
186 /// going bottom-up in each of these two "pieces" / sections.
187 /// This ensures that errors are triggered in the following order
188 /// - first invalid accesses with insufficient permissions, closest to the accessed node first,
189 /// - then protector violations, bottom-up, starting with the children of the accessed node, and then
190 /// going upwards and outwards.
191 ///
192 /// The following graphic visualizes it, with numbers indicating visitation order and `start_idx` being
193 /// the node that is visited first ("1"):
194 ///
195 /// ```text
196 /// 3
197 /// /|
198 /// / |
199 /// 9 2
200 /// | |\
201 /// | | \
202 /// 8 1 7
203 /// / \
204 /// 4 6
205 /// |
206 /// 5
207 /// ```
208 ///
209 /// `f_propagate` should follow the following format: for a given `Node` it updates its
210 /// `Permission` depending on the position relative to `start_idx` (given by an
211 /// `AccessRelatedness`).
212 /// `f_continue` is called earlier on foreign nodes, and describes whether to even start
213 /// visiting the subtree at that node. If it e.g. returns `SkipSelfAndChildren` on node 6
214 /// above, then nodes 5 _and_ 6 would not be visited by `f_propagate`. It is not used for
215 /// notes having a child access (nodes 1, 2, 3).
216 ///
217 /// Finally, remember that the iteration order is not relevant for UB, it only affects
218 /// diagnostics. It also affects tree traversal optimizations built on top of this, so
219 /// those need to be reviewed carefully as well whenever this changes.
220 ///
221 /// Returns the index of the root of the accessed tree.
222 pub fn traverse_this_parents_children_other<Err>(
223 mut self,
224 start_idx: UniIndex,
225 f_continue: impl Fn(&NodeAppArgs<'_, T>) -> ContinueTraversal,
226 f_propagate: impl FnMut(NodeAppArgs<'_, T>) -> Result<(), Err>,
227 ) -> Result<UniIndex, Err> {
228 let mut stack = TreeVisitorStack::new(f_continue, f_propagate);
229 // Visits the accessed node itself, and all its parents, i.e. all nodes
230 // undergoing a child access. Also pushes the children and the other
231 // cousin nodes (i.e. all nodes undergoing a foreign access) to the stack
232 // to be processed later.
233 let root = stack.go_upwards_from_accessed(
234 &mut self,
235 start_idx,
236 ChildrenVisitMode::VisitChildrenOfAccessed,
237 )?;
238 // Now visit all the foreign nodes we remembered earlier.
239 // For this we go bottom-up, but also allow f_continue to skip entire
240 // subtrees from being visited if it would be a NOP.
241 stack.finish_foreign_accesses(&mut self)?;
242 Ok(root)
243 }
244
245 /// Like `traverse_this_parents_children_other`, but skips the children of `start_idx`.
246 ///
247 /// Returns the index of the root of the accessed tree.
248 pub fn traverse_nonchildren<Err>(
249 mut self,
250 start_idx: UniIndex,
251 f_continue: impl Fn(&NodeAppArgs<'_, T>) -> ContinueTraversal,
252 f_propagate: impl FnMut(NodeAppArgs<'_, T>) -> Result<(), Err>,
253 ) -> Result<UniIndex, Err> {
254 let mut stack = TreeVisitorStack::new(f_continue, f_propagate);
255 // Visits the accessed node itself, and all its parents, i.e. all nodes
256 // undergoing a child access. Also pushes the other cousin nodes to the
257 // stack, but not the children of the accessed node.
258 let root = stack.go_upwards_from_accessed(
259 &mut self,
260 start_idx,
261 ChildrenVisitMode::SkipChildrenOfAccessed,
262 )?;
263 // Now visit all the foreign nodes we remembered earlier.
264 // For this we go bottom-up, but also allow f_continue to skip entire
265 // subtrees from being visited if it would be a NOP.
266 stack.finish_foreign_accesses(&mut self)?;
267 Ok(root)
268 }
269
270 /// Traverses all children of `start_idx` including `start_idx` itself.
271 /// Uses `f_continue` to filter out subtrees and then processes each node
272 /// with `f_propagate` so that the children get processed before their
273 /// parents.
274 pub fn traverse_children_this<Err>(
275 mut self,
276 start_idx: UniIndex,
277 f_continue: impl Fn(&NodeAppArgs<'_, T>) -> ContinueTraversal,
278 f_propagate: impl FnMut(NodeAppArgs<'_, T>) -> Result<(), Err>,
279 ) -> Result<(), Err> {
280 let mut stack = TreeVisitorStack::new(f_continue, f_propagate);
281
282 stack.stack.push((
283 start_idx,
284 AccessRelatedness::ForeignAccess,
285 RecursionState::BeforeChildren,
286 ));
287 stack.finish_foreign_accesses(&mut self)
288 }
289}
src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs+43-4
...@@ -88,10 +88,26 @@ impl WildcardState {...@@ -88,10 +88,26 @@ impl WildcardState {
88 }88 }
8989
90 /// From where relative to the node with this wildcard info a read or write access could happen.90 /// From where relative to the node with this wildcard info a read or write access could happen.
91 pub fn access_relatedness(&self, kind: AccessKind) -> Option<WildcardAccessRelatedness> {91 /// If `only_foreign` is true then we treat `LocalAccess` as impossible. This means we return
92 match kind {92 /// `None` if only a `LocalAccess` is possible, and we treat `EitherAccess` as a
93 /// `ForeignAccess`.
94 pub fn access_relatedness(
95 &self,
96 kind: AccessKind,
97 only_foreign: bool,
98 ) -> Option<WildcardAccessRelatedness> {
99 let rel = match kind {
93 AccessKind::Read => self.read_access_relatedness(),100 AccessKind::Read => self.read_access_relatedness(),
94 AccessKind::Write => self.write_access_relatedness(),101 AccessKind::Write => self.write_access_relatedness(),
102 };
103 if only_foreign {
104 use WildcardAccessRelatedness as E;
105 match rel {
106 Some(E::EitherAccess | E::ForeignAccess) => Some(E::ForeignAccess),
107 Some(E::LocalAccess) | None => None,
108 }
109 } else {
110 rel
95 }111 }
96 }112 }
97113
...@@ -131,6 +147,15 @@ impl WildcardState {...@@ -131,6 +147,15 @@ impl WildcardState {
131 ..Default::default()147 ..Default::default()
132 }148 }
133 }149 }
150 /// Crates the initial `WildcardState` for a wildcard root.
151 /// This has `max_foreign_access==Write` as it actually is the child of *some* exposed node
152 /// through which we can receive foreign accesses.
153 ///
154 /// This is different from the main root which has `max_foreign_access==None`, since there
155 /// cannot be a foreign access to the root of the allocation.
156 pub fn for_wildcard_root() -> Self {
157 Self { max_foreign_access: WildcardAccessLevel::Write, ..Default::default() }
158 }
134159
135 /// Pushes the nodes of `children` onto the stack who's `max_foreign_access`160 /// Pushes the nodes of `children` onto the stack who's `max_foreign_access`
136 /// needs to be updated.161 /// needs to be updated.
...@@ -435,6 +460,10 @@ impl Tree {...@@ -435,6 +460,10 @@ impl Tree {
435 /// Checks that the wildcard tracking data structure is internally consistent and460 /// Checks that the wildcard tracking data structure is internally consistent and
436 /// has the correct `exposed_as` values.461 /// has the correct `exposed_as` values.
437 pub fn verify_wildcard_consistency(&self, global: &GlobalState) {462 pub fn verify_wildcard_consistency(&self, global: &GlobalState) {
463 // We rely on the fact that `roots` is ordered according to tag from low to high.
464 assert!(self.roots.is_sorted_by_key(|idx| self.nodes.get(*idx).unwrap().tag));
465 let main_root_idx = self.roots[0];
466
438 let protected_tags = &global.borrow().protected_tags;467 let protected_tags = &global.borrow().protected_tags;
439 for (_, loc) in self.locations.iter_all() {468 for (_, loc) in self.locations.iter_all() {
440 let wildcard_accesses = &loc.wildcard_accesses;469 let wildcard_accesses = &loc.wildcard_accesses;
...@@ -447,7 +476,8 @@ impl Tree {...@@ -447,7 +476,8 @@ impl Tree {
447 let state = wildcard_accesses.get(id).unwrap();476 let state = wildcard_accesses.get(id).unwrap();
448477
449 let expected_exposed_as = if node.is_exposed {478 let expected_exposed_as = if node.is_exposed {
450 let perm = perms.get(id).unwrap();479 let perm =
480 perms.get(id).copied().unwrap_or_else(|| node.default_location_state());
451481
452 perm.permission()482 perm.permission()
453 .strongest_allowed_child_access(protected_tags.contains_key(&node.tag))483 .strongest_allowed_child_access(protected_tags.contains_key(&node.tag))
...@@ -477,7 +507,16 @@ impl Tree {...@@ -477,7 +507,16 @@ impl Tree {
477 .max(parent_state.max_foreign_access)507 .max(parent_state.max_foreign_access)
478 .max(parent_state.exposed_as)508 .max(parent_state.exposed_as)
479 } else {509 } else {
480 WildcardAccessLevel::None510 if main_root_idx == id {
511 // There can never be a foreign access to the root of the allocation.
512 // So its foreign access level is always `None`.
513 WildcardAccessLevel::None
514 } else {
515 // For wildcard roots any access on a different subtree can be foreign
516 // to it. So a wildcard root has the maximum possible foreign access
517 // level.
518 WildcardAccessLevel::Write
519 }
481 };520 };
482521
483 // Count how many children can be the source of wildcard reads or writes522 // Count how many children can be the source of wildcard reads or writes
src/tools/miri/src/concurrency/thread.rs+17-6
...@@ -14,7 +14,7 @@ use rustc_hir::def_id::DefId;...@@ -14,7 +14,7 @@ use rustc_hir::def_id::DefId;
14use rustc_index::{Idx, IndexVec};14use rustc_index::{Idx, IndexVec};
15use rustc_middle::mir::Mutability;15use rustc_middle::mir::Mutability;
16use rustc_middle::ty::layout::TyAndLayout;16use rustc_middle::ty::layout::TyAndLayout;
17use rustc_span::Span;17use rustc_span::{DUMMY_SP, Span};
18use rustc_target::spec::Os;18use rustc_target::spec::Os;
1919
20use crate::concurrency::GlobalDataRaceHandler;20use crate::concurrency::GlobalDataRaceHandler;
...@@ -174,6 +174,10 @@ pub struct Thread<'tcx> {...@@ -174,6 +174,10 @@ pub struct Thread<'tcx> {
174 /// The virtual call stack.174 /// The virtual call stack.
175 stack: Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>>,175 stack: Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>>,
176176
177 /// A span that explains where the thread (or more specifically, its current root
178 /// frame) "comes from".
179 pub(crate) origin_span: Span,
180
177 /// The function to call when the stack ran empty, to figure out what to do next.181 /// The function to call when the stack ran empty, to figure out what to do next.
178 /// Conceptually, this is the interpreter implementation of the things that happen 'after' the182 /// Conceptually, this is the interpreter implementation of the things that happen 'after' the
179 /// Rust language entry point for this thread returns (usually implemented by the C or OS runtime).183 /// Rust language entry point for this thread returns (usually implemented by the C or OS runtime).
...@@ -303,6 +307,7 @@ impl<'tcx> Thread<'tcx> {...@@ -303,6 +307,7 @@ impl<'tcx> Thread<'tcx> {
303 state: ThreadState::Enabled,307 state: ThreadState::Enabled,
304 thread_name: name.map(|name| Vec::from(name.as_bytes())),308 thread_name: name.map(|name| Vec::from(name.as_bytes())),
305 stack: Vec::new(),309 stack: Vec::new(),
310 origin_span: DUMMY_SP,
306 top_user_relevant_frame: None,311 top_user_relevant_frame: None,
307 join_status: ThreadJoinStatus::Joinable,312 join_status: ThreadJoinStatus::Joinable,
308 unwind_payloads: Vec::new(),313 unwind_payloads: Vec::new(),
...@@ -318,6 +323,7 @@ impl VisitProvenance for Thread<'_> {...@@ -318,6 +323,7 @@ impl VisitProvenance for Thread<'_> {
318 unwind_payloads: panic_payload,323 unwind_payloads: panic_payload,
319 last_error,324 last_error,
320 stack,325 stack,
326 origin_span: _,
321 top_user_relevant_frame: _,327 top_user_relevant_frame: _,
322 state: _,328 state: _,
323 thread_name: _,329 thread_name: _,
...@@ -584,6 +590,10 @@ impl<'tcx> ThreadManager<'tcx> {...@@ -584,6 +590,10 @@ impl<'tcx> ThreadManager<'tcx> {
584 &self.threads[self.active_thread]590 &self.threads[self.active_thread]
585 }591 }
586592
593 pub fn thread_ref(&self, thread_id: ThreadId) -> &Thread<'tcx> {
594 &self.threads[thread_id]
595 }
596
587 /// Mark the thread as detached, which means that no other thread will try597 /// Mark the thread as detached, which means that no other thread will try
588 /// to join it and the thread is responsible for cleaning up.598 /// to join it and the thread is responsible for cleaning up.
589 ///599 ///
...@@ -704,8 +714,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {...@@ -704,8 +714,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
704 #[inline]714 #[inline]
705 fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {715 fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
706 let this = self.eval_context_mut();716 let this = self.eval_context_mut();
707 let mut callback = this717 let active_thread = this.active_thread_mut();
708 .active_thread_mut()718 active_thread.origin_span = DUMMY_SP; // reset, the old value no longer applied
719 let mut callback = active_thread
709 .on_stack_empty720 .on_stack_empty
710 .take()721 .take()
711 .expect("`on_stack_empty` not set up, or already running");722 .expect("`on_stack_empty` not set up, or already running");
...@@ -891,11 +902,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -891,11 +902,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
891 let this = self.eval_context_mut();902 let this = self.eval_context_mut();
892903
893 // Create the new thread904 // Create the new thread
905 let current_span = this.machine.current_user_relevant_span();
894 let new_thread_id = this.machine.threads.create_thread({906 let new_thread_id = this.machine.threads.create_thread({
895 let mut state = tls::TlsDtorsState::default();907 let mut state = tls::TlsDtorsState::default();
896 Box::new(move |m| state.on_stack_empty(m))908 Box::new(move |m| state.on_stack_empty(m))
897 });909 });
898 let current_span = this.machine.current_user_relevant_span();
899 match &mut this.machine.data_race {910 match &mut this.machine.data_race {
900 GlobalDataRaceHandler::None => {}911 GlobalDataRaceHandler::None => {}
901 GlobalDataRaceHandler::Vclocks(data_race) =>912 GlobalDataRaceHandler::Vclocks(data_race) =>
...@@ -934,12 +945,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -934,12 +945,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
934 // it.945 // it.
935 let ret_place = this.allocate(ret_layout, MiriMemoryKind::Machine.into())?;946 let ret_place = this.allocate(ret_layout, MiriMemoryKind::Machine.into())?;
936947
937 this.call_function(948 this.call_thread_root_function(
938 instance,949 instance,
939 start_abi,950 start_abi,
940 &[func_arg],951 &[func_arg],
941 Some(&ret_place),952 Some(&ret_place),
942 ReturnContinuation::Stop { cleanup: true },953 current_span,
943 )?;954 )?;
944955
945 // Restore the old active thread frame.956 // Restore the old active thread frame.
src/tools/miri/src/diagnostics.rs+19-2
...@@ -444,7 +444,11 @@ pub fn report_result<'tcx>(...@@ -444,7 +444,11 @@ pub fn report_result<'tcx>(
444 write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), res)).unwrap();444 write!(primary_msg, "{}", format_interp_error(ecx.tcx.dcx(), res)).unwrap();
445445
446 if labels.is_empty() {446 if labels.is_empty() {
447 labels.push(format!("{} occurred here", title.unwrap_or("error")));447 labels.push(format!(
448 "{} occurred {}",
449 title.unwrap_or("error"),
450 if stacktrace.is_empty() { "due to this code" } else { "here" }
451 ));
448 }452 }
449453
450 report_msg(454 report_msg(
...@@ -552,7 +556,14 @@ pub fn report_msg<'tcx>(...@@ -552,7 +556,14 @@ pub fn report_msg<'tcx>(
552 thread: Option<ThreadId>,556 thread: Option<ThreadId>,
553 machine: &MiriMachine<'tcx>,557 machine: &MiriMachine<'tcx>,
554) {558) {
555 let span = stacktrace.first().map_or(DUMMY_SP, |fi| fi.span);559 let span = match stacktrace.first() {
560 Some(fi) => fi.span,
561 None =>
562 match thread {
563 Some(thread_id) => machine.threads.thread_ref(thread_id).origin_span,
564 None => DUMMY_SP,
565 },
566 };
556 let sess = machine.tcx.sess;567 let sess = machine.tcx.sess;
557 let level = match diag_level {568 let level = match diag_level {
558 DiagLevel::Error => Level::Error,569 DiagLevel::Error => Level::Error,
...@@ -620,6 +631,12 @@ pub fn report_msg<'tcx>(...@@ -620,6 +631,12 @@ pub fn report_msg<'tcx>(
620 err.note(format!("{frame_info} at {span}"));631 err.note(format!("{frame_info} at {span}"));
621 }632 }
622 }633 }
634 } else if stacktrace.len() == 0 && !span.is_dummy() {
635 err.note(format!(
636 "this {} occurred while pushing a call frame onto an empty stack",
637 level.to_str()
638 ));
639 err.note("the span indicates which code caused the function to be called, but may not be the literal call site");
623 }640 }
624641
625 err.emit();642 err.emit();
src/tools/miri/src/helpers.rs+22-4
...@@ -472,6 +472,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -472,6 +472,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
472 )472 )
473 }473 }
474474
475 /// Call a function in an "empty" thread.
476 fn call_thread_root_function(
477 &mut self,
478 f: ty::Instance<'tcx>,
479 caller_abi: ExternAbi,
480 args: &[ImmTy<'tcx>],
481 dest: Option<&MPlaceTy<'tcx>>,
482 span: Span,
483 ) -> InterpResult<'tcx> {
484 let this = self.eval_context_mut();
485 assert!(this.active_thread_stack().is_empty());
486 assert!(this.active_thread_ref().origin_span.is_dummy());
487 this.active_thread_mut().origin_span = span;
488 this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true })
489 }
490
475 /// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter491 /// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter
476 /// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`.492 /// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`.
477 /// The range is relative to `place`.493 /// The range is relative to `place`.
...@@ -995,11 +1011,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -995,11 +1011,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
995 interp_ok(())1011 interp_ok(())
996 }1012 }
9971013
998 /// Lookup an array of immediates from any linker sections matching the provided predicate.1014 /// Lookup an array of immediates from any linker sections matching the provided predicate,
1015 /// with the spans of where they were found.
999 fn lookup_link_section(1016 fn lookup_link_section(
1000 &mut self,1017 &mut self,
1001 include_name: impl Fn(&str) -> bool,1018 include_name: impl Fn(&str) -> bool,
1002 ) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {1019 ) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
1003 let this = self.eval_context_mut();1020 let this = self.eval_context_mut();
1004 let tcx = this.tcx.tcx;1021 let tcx = this.tcx.tcx;
10051022
...@@ -1012,6 +1029,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -1012,6 +1029,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1012 };1029 };
1013 if include_name(link_section.as_str()) {1030 if include_name(link_section.as_str()) {
1014 let instance = ty::Instance::mono(tcx, def_id);1031 let instance = ty::Instance::mono(tcx, def_id);
1032 let span = tcx.def_span(def_id);
1015 let const_val = this.eval_global(instance).unwrap_or_else(|err| {1033 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
1016 panic!(1034 panic!(
1017 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"1035 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
...@@ -1019,12 +1037,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -1019,12 +1037,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1019 });1037 });
1020 match const_val.layout.ty.kind() {1038 match const_val.layout.ty.kind() {
1021 ty::FnPtr(..) => {1039 ty::FnPtr(..) => {
1022 array.push(this.read_immediate(&const_val)?);1040 array.push((this.read_immediate(&const_val)?, span));
1023 }1041 }
1024 ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {1042 ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
1025 let mut elems = this.project_array_fields(&const_val)?;1043 let mut elems = this.project_array_fields(&const_val)?;
1026 while let Some((_idx, elem)) = elems.next(this)? {1044 while let Some((_idx, elem)) = elems.next(this)? {
1027 array.push(this.read_immediate(&elem)?);1045 array.push((this.read_immediate(&elem)?, span));
1028 }1046 }
1029 }1047 }
1030 _ =>1048 _ =>
src/tools/miri/src/lib.rs+1
...@@ -39,6 +39,7 @@...@@ -39,6 +39,7 @@
39 clippy::needless_question_mark,39 clippy::needless_question_mark,
40 clippy::needless_lifetimes,40 clippy::needless_lifetimes,
41 clippy::too_long_first_doc_paragraph,41 clippy::too_long_first_doc_paragraph,
42 clippy::len_zero,
42 // We don't use translatable diagnostics43 // We don't use translatable diagnostics
43 rustc::diagnostic_outside_of_impl,44 rustc::diagnostic_outside_of_impl,
44 // We are not implementing queries here so it's fine45 // We are not implementing queries here so it's fine
src/tools/miri/src/shims/global_ctor.rs+5-4
...@@ -3,6 +3,7 @@...@@ -3,6 +3,7 @@
3use std::task::Poll;3use std::task::Poll;
44
5use rustc_abi::ExternAbi;5use rustc_abi::ExternAbi;
6use rustc_span::Span;
6use rustc_target::spec::BinaryFormat;7use rustc_target::spec::BinaryFormat;
78
8use crate::*;9use crate::*;
...@@ -15,7 +16,7 @@ enum GlobalCtorStatePriv<'tcx> {...@@ -15,7 +16,7 @@ enum GlobalCtorStatePriv<'tcx> {
15 #[default]16 #[default]
16 Init,17 Init,
17 /// The list of constructor functions that we still have to call.18 /// The list of constructor functions that we still have to call.
18 Ctors(Vec<ImmTy<'tcx>>),19 Ctors(Vec<(ImmTy<'tcx>, Span)>),
19 Done,20 Done,
20}21}
2122
...@@ -67,19 +68,19 @@ impl<'tcx> GlobalCtorState<'tcx> {...@@ -67,19 +68,19 @@ impl<'tcx> GlobalCtorState<'tcx> {
67 break 'new_state Ctors(ctors);68 break 'new_state Ctors(ctors);
68 }69 }
69 Ctors(ctors) => {70 Ctors(ctors) => {
70 if let Some(ctor) = ctors.pop() {71 if let Some((ctor, span)) = ctors.pop() {
71 let this = this.eval_context_mut();72 let this = this.eval_context_mut();
7273
73 let ctor = ctor.to_scalar().to_pointer(this)?;74 let ctor = ctor.to_scalar().to_pointer(this)?;
74 let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?;75 let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?;
7576
76 // The signature of this function is `unsafe extern "C" fn()`.77 // The signature of this function is `unsafe extern "C" fn()`.
77 this.call_function(78 this.call_thread_root_function(
78 thread_callback,79 thread_callback,
79 ExternAbi::C { unwind: false },80 ExternAbi::C { unwind: false },
80 &[],81 &[],
81 None,82 None,
82 ReturnContinuation::Stop { cleanup: true },83 span,
83 )?;84 )?;
8485
85 return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter)86 return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter)
src/tools/miri/src/shims/tls.rs+23-22
...@@ -6,6 +6,7 @@ use std::task::Poll;...@@ -6,6 +6,7 @@ use std::task::Poll;
66
7use rustc_abi::{ExternAbi, HasDataLayout, Size};7use rustc_abi::{ExternAbi, HasDataLayout, Size};
8use rustc_middle::ty;8use rustc_middle::ty;
9use rustc_span::Span;
9use rustc_target::spec::Os;10use rustc_target::spec::Os;
1011
11use crate::*;12use crate::*;
...@@ -17,7 +18,7 @@ pub struct TlsEntry<'tcx> {...@@ -17,7 +18,7 @@ pub struct TlsEntry<'tcx> {
17 /// The data for this key. None is used to represent NULL.18 /// The data for this key. None is used to represent NULL.
18 /// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.)19 /// (We normalize this early to avoid having to do a NULL-ptr-test each time we access the data.)
19 data: BTreeMap<ThreadId, Scalar>,20 data: BTreeMap<ThreadId, Scalar>,
20 dtor: Option<ty::Instance<'tcx>>,21 dtor: Option<(ty::Instance<'tcx>, Span)>,
21}22}
2223
23#[derive(Default, Debug)]24#[derive(Default, Debug)]
...@@ -38,7 +39,7 @@ pub struct TlsData<'tcx> {...@@ -38,7 +39,7 @@ pub struct TlsData<'tcx> {
3839
39 /// On macOS, each thread holds a list of destructor functions with their40 /// On macOS, each thread holds a list of destructor functions with their
40 /// respective data arguments.41 /// respective data arguments.
41 macos_thread_dtors: BTreeMap<ThreadId, Vec<(ty::Instance<'tcx>, Scalar)>>,42 macos_thread_dtors: BTreeMap<ThreadId, Vec<(ty::Instance<'tcx>, Scalar, Span)>>,
42}43}
4344
44impl<'tcx> Default for TlsData<'tcx> {45impl<'tcx> Default for TlsData<'tcx> {
...@@ -57,7 +58,7 @@ impl<'tcx> TlsData<'tcx> {...@@ -57,7 +58,7 @@ impl<'tcx> TlsData<'tcx> {
57 #[expect(clippy::arithmetic_side_effects)]58 #[expect(clippy::arithmetic_side_effects)]
58 pub fn create_tls_key(59 pub fn create_tls_key(
59 &mut self,60 &mut self,
60 dtor: Option<ty::Instance<'tcx>>,61 dtor: Option<(ty::Instance<'tcx>, Span)>,
61 max_size: Size,62 max_size: Size,
62 ) -> InterpResult<'tcx, TlsKey> {63 ) -> InterpResult<'tcx, TlsKey> {
63 let new_key = self.next_key;64 let new_key = self.next_key;
...@@ -126,8 +127,9 @@ impl<'tcx> TlsData<'tcx> {...@@ -126,8 +127,9 @@ impl<'tcx> TlsData<'tcx> {
126 thread: ThreadId,127 thread: ThreadId,
127 dtor: ty::Instance<'tcx>,128 dtor: ty::Instance<'tcx>,
128 data: Scalar,129 data: Scalar,
130 span: Span,
129 ) -> InterpResult<'tcx> {131 ) -> InterpResult<'tcx> {
130 self.macos_thread_dtors.entry(thread).or_default().push((dtor, data));132 self.macos_thread_dtors.entry(thread).or_default().push((dtor, data, span));
131 interp_ok(())133 interp_ok(())
132 }134 }
133135
...@@ -154,7 +156,7 @@ impl<'tcx> TlsData<'tcx> {...@@ -154,7 +156,7 @@ impl<'tcx> TlsData<'tcx> {
154 &mut self,156 &mut self,
155 key: Option<TlsKey>,157 key: Option<TlsKey>,
156 thread_id: ThreadId,158 thread_id: ThreadId,
157 ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey)> {159 ) -> Option<(ty::Instance<'tcx>, Scalar, TlsKey, Span)> {
158 use std::ops::Bound::*;160 use std::ops::Bound::*;
159161
160 let thread_local = &mut self.keys;162 let thread_local = &mut self.keys;
...@@ -172,11 +174,10 @@ impl<'tcx> TlsData<'tcx> {...@@ -172,11 +174,10 @@ impl<'tcx> TlsData<'tcx> {
172 for (&key, TlsEntry { data, dtor }) in thread_local.range_mut((start, Unbounded)) {174 for (&key, TlsEntry { data, dtor }) in thread_local.range_mut((start, Unbounded)) {
173 match data.entry(thread_id) {175 match data.entry(thread_id) {
174 BTreeEntry::Occupied(entry) => {176 BTreeEntry::Occupied(entry) => {
175 if let Some(dtor) = dtor {177 if let Some((dtor, span)) = dtor {
176 // Set TLS data to NULL, and call dtor with old value.178 // Set TLS data to NULL, and call dtor with old value.
177 let data_scalar = entry.remove();179 let data_scalar = entry.remove();
178 let ret = Some((*dtor, data_scalar, key));180 return Some((*dtor, data_scalar, key, *span));
179 return ret;
180 }181 }
181 }182 }
182 BTreeEntry::Vacant(_) => {}183 BTreeEntry::Vacant(_) => {}
...@@ -205,7 +206,7 @@ impl VisitProvenance for TlsData<'_> {...@@ -205,7 +206,7 @@ impl VisitProvenance for TlsData<'_> {
205 for scalar in keys.values().flat_map(|v| v.data.values()) {206 for scalar in keys.values().flat_map(|v| v.data.values()) {
206 scalar.visit_provenance(visit);207 scalar.visit_provenance(visit);
207 }208 }
208 for (_, scalar) in macos_thread_dtors.values().flatten() {209 for (_, scalar, _) in macos_thread_dtors.values().flatten() {
209 scalar.visit_provenance(visit);210 scalar.visit_provenance(visit);
210 }211 }
211 }212 }
...@@ -222,7 +223,7 @@ enum TlsDtorsStatePriv<'tcx> {...@@ -222,7 +223,7 @@ enum TlsDtorsStatePriv<'tcx> {
222 PthreadDtors(RunningDtorState),223 PthreadDtors(RunningDtorState),
223 /// For Windows Dtors, we store the list of functions that we still have to call.224 /// For Windows Dtors, we store the list of functions that we still have to call.
224 /// These are functions from the magic `.CRT$XLB` linker section.225 /// These are functions from the magic `.CRT$XLB` linker section.
225 WindowsDtors(Vec<ImmTy<'tcx>>),226 WindowsDtors(Vec<(ImmTy<'tcx>, Span)>),
226 Done,227 Done,
227}228}
228229
...@@ -273,8 +274,8 @@ impl<'tcx> TlsDtorsState<'tcx> {...@@ -273,8 +274,8 @@ impl<'tcx> TlsDtorsState<'tcx> {
273 }274 }
274 }275 }
275 WindowsDtors(dtors) => {276 WindowsDtors(dtors) => {
276 if let Some(dtor) = dtors.pop() {277 if let Some((dtor, span)) = dtors.pop() {
277 this.schedule_windows_tls_dtor(dtor)?;278 this.schedule_windows_tls_dtor(dtor, span)?;
278 return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter)279 return interp_ok(Poll::Pending); // we stay in this state (but `dtors` got shorter)
279 } else {280 } else {
280 // No more destructors to run.281 // No more destructors to run.
...@@ -297,7 +298,7 @@ impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}...@@ -297,7 +298,7 @@ impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {}
297trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {298trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
298 /// Schedule TLS destructors for Windows.299 /// Schedule TLS destructors for Windows.
299 /// On windows, TLS destructors are managed by std.300 /// On windows, TLS destructors are managed by std.
300 fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {301 fn lookup_windows_tls_dtors(&mut self) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
301 let this = self.eval_context_mut();302 let this = self.eval_context_mut();
302303
303 // Windows has a special magic linker section that is run on certain events.304 // Windows has a special magic linker section that is run on certain events.
...@@ -305,7 +306,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -305,7 +306,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
305 interp_ok(this.lookup_link_section(|section| section == ".CRT$XLB")?)306 interp_ok(this.lookup_link_section(|section| section == ".CRT$XLB")?)
306 }307 }
307308
308 fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> {309 fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>, span: Span) -> InterpResult<'tcx> {
309 let this = self.eval_context_mut();310 let this = self.eval_context_mut();
310311
311 let dtor = dtor.to_scalar().to_pointer(this)?;312 let dtor = dtor.to_scalar().to_pointer(this)?;
...@@ -320,12 +321,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -320,12 +321,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
320 // The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`.321 // The signature of this function is `unsafe extern "system" fn(h: c::LPVOID, dwReason: c::DWORD, pv: c::LPVOID)`.
321 // FIXME: `h` should be a handle to the current module and what `pv` should be is unknown322 // FIXME: `h` should be a handle to the current module and what `pv` should be is unknown
322 // but both are ignored by std.323 // but both are ignored by std.
323 this.call_function(324 this.call_thread_root_function(
324 thread_callback,325 thread_callback,
325 ExternAbi::System { unwind: false },326 ExternAbi::System { unwind: false },
326 &[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr],327 &[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr],
327 None,328 None,
328 ReturnContinuation::Stop { cleanup: true },329 span,
329 )?;330 )?;
330 interp_ok(())331 interp_ok(())
331 }332 }
...@@ -338,15 +339,15 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -338,15 +339,15 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
338 // registers another destructor, it will be run next.339 // registers another destructor, it will be run next.
339 // See https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/DyldRuntimeState.cpp#L2277340 // See https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/DyldRuntimeState.cpp#L2277
340 let dtor = this.machine.tls.macos_thread_dtors.get_mut(&thread_id).and_then(Vec::pop);341 let dtor = this.machine.tls.macos_thread_dtors.get_mut(&thread_id).and_then(Vec::pop);
341 if let Some((instance, data)) = dtor {342 if let Some((instance, data, span)) = dtor {
342 trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id);343 trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id);
343344
344 this.call_function(345 this.call_thread_root_function(
345 instance,346 instance,
346 ExternAbi::C { unwind: false },347 ExternAbi::C { unwind: false },
347 &[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)],348 &[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)],
348 None,349 None,
349 ReturnContinuation::Stop { cleanup: true },350 span,
350 )?;351 )?;
351352
352 return interp_ok(Poll::Pending);353 return interp_ok(Poll::Pending);
...@@ -370,7 +371,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -370,7 +371,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
370 // We ran each dtor once, start over from the beginning.371 // We ran each dtor once, start over from the beginning.
371 None => this.machine.tls.fetch_tls_dtor(None, active_thread),372 None => this.machine.tls.fetch_tls_dtor(None, active_thread),
372 };373 };
373 if let Some((instance, ptr, key)) = dtor {374 if let Some((instance, ptr, key, span)) = dtor {
374 state.last_key = Some(key);375 state.last_key = Some(key);
375 trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread);376 trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread);
376 assert!(377 assert!(
...@@ -378,12 +379,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -378,12 +379,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
378 "data can't be NULL when dtor is called!"379 "data can't be NULL when dtor is called!"
379 );380 );
380381
381 this.call_function(382 this.call_thread_root_function(
382 instance,383 instance,
383 ExternAbi::C { unwind: false },384 ExternAbi::C { unwind: false },
384 &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],385 &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],
385 None,386 None,
386 ReturnContinuation::Stop { cleanup: true },387 span,
387 )?;388 )?;
388389
389 return interp_ok(Poll::Pending);390 return interp_ok(Poll::Pending);
src/tools/miri/src/shims/unix/foreign_items.rs+10-71
...@@ -235,33 +235,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -235,33 +235,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
235 trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);235 trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
236 this.write(fd, buf, count, Some(offset), dest)?;236 this.write(fd, buf, count, Some(offset), dest)?;
237 }237 }
238 "pread64" => {
239 let [fd, buf, count, offset] = this.check_shim_sig(
240 shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize),
241 link_name,
242 abi,
243 args,
244 )?;
245 let fd = this.read_scalar(fd)?.to_i32()?;
246 let buf = this.read_pointer(buf)?;
247 let count = this.read_target_usize(count)?;
248 let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
249 this.read(fd, buf, count, Some(offset), dest)?;
250 }
251 "pwrite64" => {
252 let [fd, buf, n, offset] = this.check_shim_sig(
253 shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize),
254 link_name,
255 abi,
256 args,
257 )?;
258 let fd = this.read_scalar(fd)?.to_i32()?;
259 let buf = this.read_pointer(buf)?;
260 let count = this.read_target_usize(n)?;
261 let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
262 trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
263 this.write(fd, buf, count, Some(offset), dest)?;
264 }
265 "close" => {238 "close" => {
266 let [fd] = this.check_shim_sig(239 let [fd] = this.check_shim_sig(
267 shim_sig!(extern "C" fn(i32) -> i32),240 shim_sig!(extern "C" fn(i32) -> i32),
...@@ -317,7 +290,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -317,7 +290,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
317 }290 }
318291
319 // File and file system access292 // File and file system access
320 "open" | "open64" => {293 "open" => {
321 // `open` is variadic, the third argument is only present when the second argument294 // `open` is variadic, the third argument is only present when the second argument
322 // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set295 // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
323 let ([path_raw, flag], varargs) =296 let ([path_raw, flag], varargs) =
...@@ -345,6 +318,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -345,6 +318,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
345 let result = this.symlink(target, linkpath)?;318 let result = this.symlink(target, linkpath)?;
346 this.write_scalar(result, dest)?;319 this.write_scalar(result, dest)?;
347 }320 }
321 "fstat" => {
322 let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
323 let result = this.fstat(fd, buf)?;
324 this.write_scalar(result, dest)?;
325 }
348 "rename" => {326 "rename" => {
349 let [oldpath, newpath] = this.check_shim_sig(327 let [oldpath, newpath] = this.check_shim_sig(
350 shim_sig!(extern "C" fn(*const _, *const _) -> i32),328 shim_sig!(extern "C" fn(*const _, *const _) -> i32),
...@@ -395,18 +373,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -395,18 +373,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
395 let result = this.closedir(dirp)?;373 let result = this.closedir(dirp)?;
396 this.write_scalar(result, dest)?;374 this.write_scalar(result, dest)?;
397 }375 }
398 "lseek64" => {
399 let [fd, offset, whence] = this.check_shim_sig(
400 shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t),
401 link_name,
402 abi,
403 args,
404 )?;
405 let fd = this.read_scalar(fd)?.to_i32()?;
406 let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
407 let whence = this.read_scalar(whence)?.to_i32()?;
408 this.lseek64(fd, offset, whence, dest)?;
409 }
410 "lseek" => {376 "lseek" => {
411 let [fd, offset, whence] = this.check_shim_sig(377 let [fd, offset, whence] = this.check_shim_sig(
412 shim_sig!(extern "C" fn(i32, libc::off_t, i32) -> libc::off_t),378 shim_sig!(extern "C" fn(i32, libc::off_t, i32) -> libc::off_t),
...@@ -419,18 +385,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -419,18 +385,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
419 let whence = this.read_scalar(whence)?.to_i32()?;385 let whence = this.read_scalar(whence)?.to_i32()?;
420 this.lseek64(fd, offset, whence, dest)?;386 this.lseek64(fd, offset, whence, dest)?;
421 }387 }
422 "ftruncate64" => {
423 let [fd, length] = this.check_shim_sig(
424 shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32),
425 link_name,
426 abi,
427 args,
428 )?;
429 let fd = this.read_scalar(fd)?.to_i32()?;
430 let length = this.read_scalar(length)?.to_int(length.layout.size)?;
431 let result = this.ftruncate64(fd, length)?;
432 this.write_scalar(result, dest)?;
433 }
434 "ftruncate" => {388 "ftruncate" => {
435 let [fd, length] = this.check_shim_sig(389 let [fd, length] = this.check_shim_sig(
436 shim_sig!(extern "C" fn(i32, libc::off_t) -> i32),390 shim_sig!(extern "C" fn(i32, libc::off_t) -> i32),
...@@ -511,24 +465,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -511,24 +465,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
511 this.write_scalar(result, dest)?;465 this.write_scalar(result, dest)?;
512 }466 }
513467
514 "posix_fallocate64" => {
515 // posix_fallocate64 is only supported on Linux and Android
516 this.check_target_os(&[Os::Linux, Os::Android], link_name)?;
517 let [fd, offset, len] = this.check_shim_sig(
518 shim_sig!(extern "C" fn(i32, libc::off64_t, libc::off64_t) -> i32),
519 link_name,
520 abi,
521 args,
522 )?;
523
524 let fd = this.read_scalar(fd)?.to_i32()?;
525 let offset = this.read_scalar(offset)?.to_i64()?;
526 let len = this.read_scalar(len)?.to_i64()?;
527
528 let result = this.posix_fallocate(fd, offset, len)?;
529 this.write_scalar(result, dest)?;
530 }
531
532 "realpath" => {468 "realpath" => {
533 let [path, resolved_path] = this.check_shim_sig(469 let [path, resolved_path] = this.check_shim_sig(
534 shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _),470 shim_sig!(extern "C" fn(*const _, *mut _) -> *mut _),
...@@ -698,7 +634,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -698,7 +634,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
698634
699 // Extract the function type out of the signature (that seems easier than constructing it ourselves).635 // Extract the function type out of the signature (that seems easier than constructing it ourselves).
700 let dtor = if !this.ptr_is_null(dtor)? {636 let dtor = if !this.ptr_is_null(dtor)? {
701 Some(this.get_ptr_fn(dtor)?.as_instance()?)637 Some((
638 this.get_ptr_fn(dtor)?.as_instance()?,
639 this.machine.current_user_relevant_span(),
640 ))
702 } else {641 } else {
703 None642 None
704 };643 };
src/tools/miri/src/shims/unix/freebsd/foreign_items.rs+2-8
...@@ -148,15 +148,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -148,15 +148,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
148 let result = this.macos_fbsd_solarish_lstat(path, buf)?;148 let result = this.macos_fbsd_solarish_lstat(path, buf)?;
149 this.write_scalar(result, dest)?;149 this.write_scalar(result, dest)?;
150 }150 }
151 "fstat" | "fstat@FBSD_1.0" => {151 "fstat@FBSD_1.0" => {
152 let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;152 let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
153 let result = this.macos_fbsd_solarish_fstat(fd, buf)?;153 let result = this.fstat(fd, buf)?;
154 this.write_scalar(result, dest)?;
155 }
156 "readdir_r" | "readdir_r@FBSD_1.0" => {
157 let [dirp, entry, result] =
158 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
159 let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
160 this.write_scalar(result, dest)?;154 this.write_scalar(result, dest)?;
161 }155 }
162 "readdir" | "readdir@FBSD_1.0" => {156 "readdir" | "readdir@FBSD_1.0" => {
src/tools/miri/src/shims/unix/fs.rs+22-48
...@@ -118,7 +118,7 @@ impl UnixFileDescription for FileHandle {...@@ -118,7 +118,7 @@ impl UnixFileDescription for FileHandle {
118118
119impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {}119impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {}
120trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {120trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
121 fn macos_fbsd_solarish_write_stat_buf(121 fn write_stat_buf(
122 &mut self,122 &mut self,
123 metadata: FileMetadata,123 metadata: FileMetadata,
124 buf_op: &OpTy<'tcx>,124 buf_op: &OpTy<'tcx>,
...@@ -130,7 +130,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -130,7 +130,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
130 let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0));130 let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0));
131 let mode = metadata.mode.to_uint(this.libc_ty_layout("mode_t").size)?;131 let mode = metadata.mode.to_uint(this.libc_ty_layout("mode_t").size)?;
132132
133 let buf = this.deref_pointer_as(buf_op, this.libc_ty_layout("stat"))?;133 // We do *not* use `deref_pointer_as` here since determining the right pointee type
134 // is highly non-trivial: it depends on which exact alias of the function was invoked
135 // (e.g. `fstat` vs `fstat64`), and then on FreeBSD it also depends on the ABI level
136 // which can be different between the libc used by std and the libc used by everyone else.
137 let buf = this.deref_pointer(buf_op)?;
134 this.write_int_fields_named(138 this.write_int_fields_named(
135 &[139 &[
136 ("st_dev", metadata.dev.into()),140 ("st_dev", metadata.dev.into()),
...@@ -141,8 +145,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -141,8 +145,11 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
141 ("st_gid", metadata.gid.into()),145 ("st_gid", metadata.gid.into()),
142 ("st_rdev", 0),146 ("st_rdev", 0),
143 ("st_atime", access_sec.into()),147 ("st_atime", access_sec.into()),
148 ("st_atime_nsec", access_nsec.into()),
144 ("st_mtime", modified_sec.into()),149 ("st_mtime", modified_sec.into()),
150 ("st_mtime_nsec", modified_nsec.into()),
145 ("st_ctime", 0),151 ("st_ctime", 0),
152 ("st_ctime_nsec", 0),
146 ("st_size", metadata.size.into()),153 ("st_size", metadata.size.into()),
147 ("st_blocks", 0),154 ("st_blocks", 0),
148 ("st_blksize", 0),155 ("st_blksize", 0),
...@@ -153,9 +160,6 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -153,9 +160,6 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
153 if matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd) {160 if matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd) {
154 this.write_int_fields_named(161 this.write_int_fields_named(
155 &[162 &[
156 ("st_atime_nsec", access_nsec.into()),
157 ("st_mtime_nsec", modified_nsec.into()),
158 ("st_ctime_nsec", 0),
159 ("st_birthtime", created_sec.into()),163 ("st_birthtime", created_sec.into()),
160 ("st_birthtime_nsec", created_nsec.into()),164 ("st_birthtime_nsec", created_nsec.into()),
161 ("st_flags", 0),165 ("st_flags", 0),
...@@ -550,7 +554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -550,7 +554,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
550 Err(err) => return this.set_last_error_and_return_i32(err),554 Err(err) => return this.set_last_error_and_return_i32(err),
551 };555 };
552556
553 interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?))557 interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?))
554 }558 }
555559
556 // `lstat` is used to get symlink metadata.560 // `lstat` is used to get symlink metadata.
...@@ -583,22 +587,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -583,22 +587,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
583 Err(err) => return this.set_last_error_and_return_i32(err),587 Err(err) => return this.set_last_error_and_return_i32(err),
584 };588 };
585589
586 interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?))590 interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?))
587 }591 }
588592
589 fn macos_fbsd_solarish_fstat(593 fn fstat(&mut self, fd_op: &OpTy<'tcx>, buf_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
590 &mut self,
591 fd_op: &OpTy<'tcx>,
592 buf_op: &OpTy<'tcx>,
593 ) -> InterpResult<'tcx, Scalar> {
594 let this = self.eval_context_mut();594 let this = self.eval_context_mut();
595595
596 if !matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos)596 if !matches!(
597 {597 &this.tcx.sess.target.os,
598 panic!(598 Os::MacOs | Os::FreeBsd | Os::Solaris | Os::Illumos | Os::Linux
599 "`macos_fbsd_solaris_fstat` should not be called on {}",599 ) {
600 this.tcx.sess.target.os600 panic!("`fstat` should not be called on {}", this.tcx.sess.target.os);
601 );
602 }601 }
603602
604 let fd = this.read_scalar(fd_op)?.to_i32()?;603 let fd = this.read_scalar(fd_op)?.to_i32()?;
...@@ -614,7 +613,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -614,7 +613,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
614 Ok(metadata) => metadata,613 Ok(metadata) => metadata,
615 Err(err) => return this.set_last_error_and_return_i32(err),614 Err(err) => return this.set_last_error_and_return_i32(err),
616 };615 };
617 interp_ok(Scalar::from_i32(this.macos_fbsd_solarish_write_stat_buf(metadata, buf_op)?))616 interp_ok(Scalar::from_i32(this.write_stat_buf(metadata, buf_op)?))
618 }617 }
619618
620 fn linux_statx(619 fn linux_statx(
...@@ -1031,7 +1030,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -1031,7 +1030,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1031 interp_ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this))1030 interp_ok(Scalar::from_maybe_pointer(entry.unwrap_or_else(Pointer::null), this))
1032 }1031 }
10331032
1034 fn macos_fbsd_readdir_r(1033 fn macos_readdir_r(
1035 &mut self,1034 &mut self,
1036 dirp_op: &OpTy<'tcx>,1035 dirp_op: &OpTy<'tcx>,
1037 entry_op: &OpTy<'tcx>,1036 entry_op: &OpTy<'tcx>,
...@@ -1039,9 +1038,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -1039,9 +1038,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
1039 ) -> InterpResult<'tcx, Scalar> {1038 ) -> InterpResult<'tcx, Scalar> {
1040 let this = self.eval_context_mut();1039 let this = self.eval_context_mut();
10411040
1042 if !matches!(&this.tcx.sess.target.os, Os::MacOs | Os::FreeBsd) {1041 this.assert_target_os(Os::MacOs, "readdir_r");
1043 panic!("`macos_fbsd_readdir_r` should not be called on {}", this.tcx.sess.target.os);
1044 }
10451042
1046 let dirp = this.read_target_usize(dirp_op)?;1043 let dirp = this.read_target_usize(dirp_op)?;
1047 let result_place = this.deref_pointer_as(result_op, this.machine.layouts.mut_raw_ptr)?;1044 let result_place = this.deref_pointer_as(result_op, this.machine.layouts.mut_raw_ptr)?;
...@@ -1097,39 +1094,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -1097,39 +1094,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
10971094
1098 let file_type = this.file_type_to_d_type(dir_entry.file_type())?;1095 let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
10991096
1100 // Common fields.
1101 this.write_int_fields_named(1097 this.write_int_fields_named(
1102 &[1098 &[
1103 ("d_reclen", 0),1099 ("d_reclen", 0),
1104 ("d_namlen", file_name_len.into()),1100 ("d_namlen", file_name_len.into()),
1105 ("d_type", file_type.into()),1101 ("d_type", file_type.into()),
1102 ("d_ino", ino.into()),
1103 ("d_seekoff", 0),
1106 ],1104 ],
1107 &entry_place,1105 &entry_place,
1108 )?;1106 )?;
1109 // Special fields.
1110 match this.tcx.sess.target.os {
1111 Os::MacOs => {
1112 #[rustfmt::skip]
1113 this.write_int_fields_named(
1114 &[
1115 ("d_ino", ino.into()),
1116 ("d_seekoff", 0),
1117 ],
1118 &entry_place,
1119 )?;
1120 }
1121 Os::FreeBsd => {
1122 #[rustfmt::skip]
1123 this.write_int_fields_named(
1124 &[
1125 ("d_fileno", ino.into()),
1126 ("d_off", 0),
1127 ],
1128 &entry_place,
1129 )?;
1130 }
1131 _ => unreachable!(),
1132 }
1133 this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;1107 this.write_scalar(this.read_scalar(entry_op)?, &result_place)?;
11341108
1135 Scalar::from_i32(0)1109 Scalar::from_i32(0)
src/tools/miri/src/shims/unix/linux/foreign_items.rs+74-1
...@@ -36,6 +36,80 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -36,6 +36,80 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
3636
37 match link_name.as_str() {37 match link_name.as_str() {
38 // File related shims38 // File related shims
39 "open64" => {
40 // `open64` is variadic, the third argument is only present when the second argument
41 // has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
42 let ([path_raw, flag], varargs) =
43 this.check_shim_sig_variadic_lenient(abi, CanonAbi::C, link_name, args)?;
44 let result = this.open(path_raw, flag, varargs)?;
45 this.write_scalar(result, dest)?;
46 }
47 "pread64" => {
48 let [fd, buf, count, offset] = this.check_shim_sig(
49 shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off64_t) -> isize),
50 link_name,
51 abi,
52 args,
53 )?;
54 let fd = this.read_scalar(fd)?.to_i32()?;
55 let buf = this.read_pointer(buf)?;
56 let count = this.read_target_usize(count)?;
57 let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
58 this.read(fd, buf, count, Some(offset), dest)?;
59 }
60 "pwrite64" => {
61 let [fd, buf, n, offset] = this.check_shim_sig(
62 shim_sig!(extern "C" fn(i32, *const _, usize, libc::off64_t) -> isize),
63 link_name,
64 abi,
65 args,
66 )?;
67 let fd = this.read_scalar(fd)?.to_i32()?;
68 let buf = this.read_pointer(buf)?;
69 let count = this.read_target_usize(n)?;
70 let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
71 trace!("Called pwrite64({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset);
72 this.write(fd, buf, count, Some(offset), dest)?;
73 }
74 "lseek64" => {
75 let [fd, offset, whence] = this.check_shim_sig(
76 shim_sig!(extern "C" fn(i32, libc::off64_t, i32) -> libc::off64_t),
77 link_name,
78 abi,
79 args,
80 )?;
81 let fd = this.read_scalar(fd)?.to_i32()?;
82 let offset = this.read_scalar(offset)?.to_int(offset.layout.size)?;
83 let whence = this.read_scalar(whence)?.to_i32()?;
84 this.lseek64(fd, offset, whence, dest)?;
85 }
86 "ftruncate64" => {
87 let [fd, length] = this.check_shim_sig(
88 shim_sig!(extern "C" fn(i32, libc::off64_t) -> i32),
89 link_name,
90 abi,
91 args,
92 )?;
93 let fd = this.read_scalar(fd)?.to_i32()?;
94 let length = this.read_scalar(length)?.to_int(length.layout.size)?;
95 let result = this.ftruncate64(fd, length)?;
96 this.write_scalar(result, dest)?;
97 }
98 "posix_fallocate64" => {
99 let [fd, offset, len] = this.check_shim_sig(
100 shim_sig!(extern "C" fn(i32, libc::off64_t, libc::off64_t) -> i32),
101 link_name,
102 abi,
103 args,
104 )?;
105
106 let fd = this.read_scalar(fd)?.to_i32()?;
107 let offset = this.read_scalar(offset)?.to_i64()?;
108 let len = this.read_scalar(len)?.to_i64()?;
109
110 let result = this.posix_fallocate(fd, offset, len)?;
111 this.write_scalar(result, dest)?;
112 }
39 "readdir64" => {113 "readdir64" => {
40 let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;114 let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
41 let result = this.readdir64("dirent64", dirp)?;115 let result = this.readdir64("dirent64", dirp)?;
...@@ -53,7 +127,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -53,7 +127,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
53 let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;127 let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
54 this.write_scalar(result, dest)?;128 this.write_scalar(result, dest)?;
55 }129 }
56
57 // epoll, eventfd130 // epoll, eventfd
58 "epoll_create1" => {131 "epoll_create1" => {
59 let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;132 let [flag] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
src/tools/miri/src/shims/unix/macos/foreign_items.rs+11-6
...@@ -46,19 +46,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -46,19 +46,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
46 let result = this.close(result)?;46 let result = this.close(result)?;
47 this.write_scalar(result, dest)?;47 this.write_scalar(result, dest)?;
48 }48 }
49 "stat" | "stat64" | "stat$INODE64" => {49 "stat" | "stat$INODE64" => {
50 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;50 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
51 let result = this.macos_fbsd_solarish_stat(path, buf)?;51 let result = this.macos_fbsd_solarish_stat(path, buf)?;
52 this.write_scalar(result, dest)?;52 this.write_scalar(result, dest)?;
53 }53 }
54 "lstat" | "lstat64" | "lstat$INODE64" => {54 "lstat" | "lstat$INODE64" => {
55 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;55 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
56 let result = this.macos_fbsd_solarish_lstat(path, buf)?;56 let result = this.macos_fbsd_solarish_lstat(path, buf)?;
57 this.write_scalar(result, dest)?;57 this.write_scalar(result, dest)?;
58 }58 }
59 "fstat" | "fstat64" | "fstat$INODE64" => {59 "fstat$INODE64" => {
60 let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;60 let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
61 let result = this.macos_fbsd_solarish_fstat(fd, buf)?;61 let result = this.fstat(fd, buf)?;
62 this.write_scalar(result, dest)?;62 this.write_scalar(result, dest)?;
63 }63 }
64 "opendir$INODE64" => {64 "opendir$INODE64" => {
...@@ -69,7 +69,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -69,7 +69,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
69 "readdir_r" | "readdir_r$INODE64" => {69 "readdir_r" | "readdir_r$INODE64" => {
70 let [dirp, entry, result] =70 let [dirp, entry, result] =
71 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;71 this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
72 let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;72 let result = this.macos_readdir_r(dirp, entry, result)?;
73 this.write_scalar(result, dest)?;73 this.write_scalar(result, dest)?;
74 }74 }
75 "realpath$DARWIN_EXTSN" => {75 "realpath$DARWIN_EXTSN" => {
...@@ -158,7 +158,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -158,7 +158,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
158 let dtor = this.get_ptr_fn(dtor)?.as_instance()?;158 let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
159 let data = this.read_scalar(data)?;159 let data = this.read_scalar(data)?;
160 let active_thread = this.active_thread();160 let active_thread = this.active_thread();
161 this.machine.tls.add_macos_thread_dtor(active_thread, dtor, data)?;161 this.machine.tls.add_macos_thread_dtor(
162 active_thread,
163 dtor,
164 data,
165 this.machine.current_user_relevant_span(),
166 )?;
162 }167 }
163168
164 // Querying system information169 // Querying system information
src/tools/miri/src/shims/unix/solarish/foreign_items.rs+2-7
...@@ -90,21 +90,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {...@@ -90,21 +90,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
90 }90 }
9191
92 // File related shims92 // File related shims
93 "stat" | "stat64" => {93 "stat" => {
94 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;94 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
95 let result = this.macos_fbsd_solarish_stat(path, buf)?;95 let result = this.macos_fbsd_solarish_stat(path, buf)?;
96 this.write_scalar(result, dest)?;96 this.write_scalar(result, dest)?;
97 }97 }
98 "lstat" | "lstat64" => {98 "lstat" => {
99 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;99 let [path, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
100 let result = this.macos_fbsd_solarish_lstat(path, buf)?;100 let result = this.macos_fbsd_solarish_lstat(path, buf)?;
101 this.write_scalar(result, dest)?;101 this.write_scalar(result, dest)?;
102 }102 }
103 "fstat" | "fstat64" => {
104 let [fd, buf] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
105 let result = this.macos_fbsd_solarish_fstat(fd, buf)?;
106 this.write_scalar(result, dest)?;
107 }
108 "readdir" => {103 "readdir" => {
109 let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;104 let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
110 let result = this.readdir64("dirent", dirp)?;105 let result = this.readdir64("dirent", dirp)?;
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs+1-1
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1//@ignore-target: windows # No pthreads on Windows1//@ignore-target: windows # No pthreads on Windows
2//~^ERROR: calling a function with more arguments than it expected
32
4//! The thread function must have exactly one argument.3//! The thread function must have exactly one argument.
54
...@@ -17,6 +16,7 @@ fn main() {...@@ -17,6 +16,7 @@ fn main() {
17 mem::transmute(thread_start);16 mem::transmute(thread_start);
18 assert_eq!(17 assert_eq!(
19 libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),18 libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
19 //~^ERROR: calling a function with more arguments than it expected
20 020 0
21 );21 );
22 assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);22 assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_few_args.stderr+6-2
...@@ -1,9 +1,13 @@...@@ -1,9 +1,13 @@
1error: Undefined Behavior: calling a function with more arguments than it expected1error: Undefined Behavior: calling a function with more arguments than it expected
2 --> tests/fail-dep/concurrency/libc_pthread_create_too_few_args.rs:LL:CC
3 |
4LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
2 |6 |
3 = note: Undefined Behavior occurred here
4 = note: (no span available)
5 = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior7 = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
6 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information8 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9 = note: this error occurred while pushing a call frame onto an empty stack
10 = note: the span indicates which code caused the function to be called, but may not be the literal call site
711
8error: aborting due to 1 previous error12error: aborting due to 1 previous error
913
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs+1-1
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1//@ignore-target: windows # No pthreads on Windows1//@ignore-target: windows # No pthreads on Windows
2//~^ERROR: calling a function with fewer arguments than it requires
32
4//! The thread function must have exactly one argument.3//! The thread function must have exactly one argument.
54
...@@ -17,6 +16,7 @@ fn main() {...@@ -17,6 +16,7 @@ fn main() {
17 mem::transmute(thread_start);16 mem::transmute(thread_start);
18 assert_eq!(17 assert_eq!(
19 libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),18 libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
19 //~^ERROR: calling a function with fewer arguments than it requires
20 020 0
21 );21 );
22 assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);22 assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_create_too_many_args.stderr+6-2
...@@ -1,9 +1,13 @@...@@ -1,9 +1,13 @@
1error: Undefined Behavior: calling a function with fewer arguments than it requires1error: Undefined Behavior: calling a function with fewer arguments than it requires
2 --> tests/fail-dep/concurrency/libc_pthread_create_too_many_args.rs:LL:CC
3 |
4LL | libc::pthread_create(&mut native, ptr::null(), thread_start, ptr::null_mut()),
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
2 |6 |
3 = note: Undefined Behavior occurred here
4 = note: (no span available)
5 = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior7 = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
6 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information8 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9 = note: this error occurred while pushing a call frame onto an empty stack
10 = note: the span indicates which code caused the function to be called, but may not be the literal call site
711
8error: aborting due to 1 previous error12error: aborting due to 1 previous error
913
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_read_while_queued.rs+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1//@ignore-target: windows # No pthreads on Windows1//@ignore-target: windows # No pthreads on Windows
2//@compile-flags: -Zmiri-fixed-schedule2//@compile-flags: -Zmiri-deterministic-concurrency
33
4use std::cell::UnsafeCell;4use std::cell::UnsafeCell;
5use std::sync::atomic::*;5use std::sync::atomic::*;
src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_write_while_queued.rs+1-1
...@@ -1,5 +1,5 @@...@@ -1,5 +1,5 @@
1//@ignore-target: windows # No pthreads on Windows1//@ignore-target: windows # No pthreads on Windows
2//@compile-flags: -Zmiri-fixed-schedule2//@compile-flags: -Zmiri-deterministic-concurrency
33
4use std::cell::UnsafeCell;4use std::cell::UnsafeCell;
5use std::sync::atomic::*;5use std::sync::atomic::*;
src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs created+21
...@@ -0,0 +1,21 @@
1//@ignore-target: windows # No pthreads on Windows
2
3use std::{mem, ptr};
4
5pub type Key = libc::pthread_key_t;
6
7pub unsafe fn create(dtor: unsafe fn(*mut u8)) -> Key {
8 let mut key = 0;
9 assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0);
10 //~^ERROR: calling a function with calling convention "Rust"
11 key
12}
13
14unsafe fn dtor(_ptr: *mut u8) {}
15
16fn main() {
17 unsafe {
18 let key = create(dtor);
19 libc::pthread_setspecific(key, ptr::without_provenance(1));
20 }
21}
src/tools/miri/tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.stderr created+13
...@@ -0,0 +1,13 @@
1error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C"
2 --> tests/fail-dep/concurrency/tls_pthread_dtor_wrong_abi.rs:LL:CC
3 |
4LL | assert_eq!(libc::pthread_key_create(&mut key, mem::transmute(dtor)), 0);
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
6 |
7 = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9 = note: this error occurred while pushing a call frame onto an empty stack
10 = note: the span indicates which code caused the function to be called, but may not be the literal call site
11
12error: aborting due to 1 previous error
13
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.rs created+21
...@@ -0,0 +1,21 @@
1//@revisions: stack tree
2//@compile-flags: -Zmiri-permissive-provenance
3//@[tree]compile-flags: -Zmiri-tree-borrows
4
5fn main() {
6 unsafe {
7 let root = &mut 42;
8 let addr = root as *mut i32 as usize;
9 let exposed_ptr = addr as *mut i32;
10 // From the exposed ptr, we get a new unique ptr.
11 let root2 = &mut *exposed_ptr;
12 let _fool = root2 as *mut _; // this would have fooled the old untagged pointer logic
13 // Stack: Unknown(<N), Unique(N), SRW(N+1)
14 // And we test that it has uniqueness by doing a conflicting write.
15 *exposed_ptr = 0;
16 // Stack: Unknown(<N)
17 let _val = *root2;
18 //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/
19 //~[tree]| ERROR: /read access through .* is forbidden/
20 }
21}
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.stack.stderr created+23
...@@ -0,0 +1,23 @@
1error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
2 --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
3 |
4LL | let _val = *root2;
5 | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
9help: <TAG> was created by a Unique retag at offsets [0x0..0x4]
10 --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
11 |
12LL | let root2 = &mut *exposed_ptr;
13 | ^^^^^^^^^^^^^^^^^
14help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access
15 --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
16 |
17LL | *exposed_ptr = 0;
18 | ^^^^^^^^^^^^^^^^
19
20note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
21
22error: aborting due to 1 previous error
23
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed1.tree.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
3 |
4LL | let _val = *root2;
5 | ^^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
12 |
13LL | let root2 = &mut *exposed_ptr;
14 | ^^^^^^^^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/both_borrows/illegal_read_despite_exposed1.rs:LL:CC
17 |
18LL | *exposed_ptr = 0;
19 | ^^^^^^^^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.rs created+28
...@@ -0,0 +1,28 @@
1//@revisions: stack tree
2//@compile-flags: -Zmiri-permissive-provenance
3//@[tree]compile-flags: -Zmiri-tree-borrows
4
5fn main() {
6 unsafe {
7 let root = &mut 42;
8 let addr = root as *mut i32 as usize;
9 let exposed_ptr = addr as *mut i32;
10 // From the exposed ptr, we get a new unique ptr.
11 let root2 = &mut *exposed_ptr;
12 // Activate the reference (unnecessary on Stacked Borrows).
13 *root2 = 42;
14 // let _fool = root2 as *mut _; // this would fool us, since SRW(N+1) remains on the stack
15 // Stack: Unknown(<N), Unique(N)
16 // Stack if _fool existed: Unknown(<N), Unique(N), SRW(N+1)
17 // And we test that it has uniqueness by doing a conflicting read.
18 let _val = *exposed_ptr;
19 // Stack: Unknown(<N), Disabled(N)
20 // collapsed to Unknown(<N)
21 // Stack if _fool existed: Unknown(<N), Disabled(N), SRW(N+1); collapsed to Unknown(<N+2) which would not cause an ERROR
22
23 // Stack borrows would also fail if we replaced this with a read, but tree borrows would let it pass.
24 *root2 = 3;
25 //~[stack]^ ERROR: /write access .* tag does not exist in the borrow stack/
26 //~[tree]| ERROR: /write access through .* is forbidden/
27 }
28}
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.stack.stderr created+23
...@@ -0,0 +1,23 @@
1error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
2 --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
3 |
4LL | *root2 = 3;
5 | ^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
9help: <TAG> was created by a Unique retag at offsets [0x0..0x4]
10 --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
11 |
12LL | let root2 = &mut *exposed_ptr;
13 | ^^^^^^^^^^^^^^^^^
14help: <TAG> was later invalidated at offsets [0x0..0x4] by a read access
15 --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
16 |
17LL | let _val = *exposed_ptr;
18 | ^^^^^^^^^^^^
19
20note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
21
22error: aborting due to 1 previous error
23
src/tools/miri/tests/fail/both_borrows/illegal_read_despite_exposed2.tree.stderr created+31
...@@ -0,0 +1,31 @@
1error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
3 |
4LL | *root2 = 3;
5 | ^^^^^^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Frozen which forbids this child write access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
12 |
13LL | let root2 = &mut *exposed_ptr;
14 | ^^^^^^^^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
16 --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
17 |
18LL | *root2 = 42;
19 | ^^^^^^^^^^^
20 = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
21help: the accessed tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x4]
22 --> tests/fail/both_borrows/illegal_read_despite_exposed2.rs:LL:CC
23 |
24LL | let _val = *exposed_ptr;
25 | ^^^^^^^^^^^^
26 = help: this transition corresponds to a loss of write permissions
27
28note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
29
30error: aborting due to 1 previous error
31
src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.rs created+22
...@@ -0,0 +1,22 @@
1//@revisions: stack tree
2//@compile-flags: -Zmiri-permissive-provenance
3//@[tree]compile-flags: -Zmiri-tree-borrows
4
5fn main() {
6 unsafe {
7 let root = &mut 42;
8 let addr = root as *mut i32 as usize;
9 let exposed_ptr = addr as *mut i32;
10 // From the exposed ptr, we get a new SRO ptr.
11 let root2 = &*exposed_ptr;
12 // Stack: Unknown(<N), SRO(N), SRO(N+1)
13 // And we test that it is read-only by doing a conflicting write.
14 // (The write is still fine, using the `root as *mut i32` provenance which got exposed.)
15 *exposed_ptr = 0;
16 // Stack: Unknown(<N)
17
18 let _val = *root2;
19 //~[stack]^ ERROR: /read access .* tag does not exist in the borrow stack/
20 //~[tree]| ERROR: /read access through .* is forbidden/
21 }
22}
src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.stack.stderr created+23
...@@ -0,0 +1,23 @@
1error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
2 --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
3 |
4LL | let _val = *root2;
5 | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
9help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x4]
10 --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
11 |
12LL | let root2 = &*exposed_ptr;
13 | ^^^^^^^^^^^^^
14help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access
15 --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
16 |
17LL | *exposed_ptr = 0;
18 | ^^^^^^^^^^^^^^^^
19
20note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
21
22error: aborting due to 1 previous error
23
src/tools/miri/tests/fail/both_borrows/illegal_write_despite_exposed1.tree.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
3 |
4LL | let _val = *root2;
5 | ^^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Frozen
11 --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
12 |
13LL | let root2 = &*exposed_ptr;
14 | ^^^^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/both_borrows/illegal_write_despite_exposed1.rs:LL:CC
17 |
18LL | *exposed_ptr = 0;
19 | ^^^^^^^^^^^^^^^^
20 = help: this transition corresponds to a loss of read permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.rs+1-1
...@@ -1,5 +1,4 @@...@@ -1,5 +1,4 @@
1unsafe extern "C" fn ctor() -> i32 {1unsafe extern "C" fn ctor() -> i32 {
2 //~^ERROR: calling a function with return type i32 passing return place of type ()
3 02 0
4}3}
54
...@@ -31,6 +30,7 @@ macro_rules! ctor {...@@ -31,6 +30,7 @@ macro_rules! ctor {
31 )]30 )]
32 #[used]31 #[used]
33 static $ident: unsafe extern "C" fn() -> i32 = $ctor;32 static $ident: unsafe extern "C" fn() -> i32 = $ctor;
33 //~^ERROR: calling a function with return type i32 passing return place of type ()
34 };34 };
35}35}
3636
src/tools/miri/tests/fail/shims/ctor_wrong_ret_type.stderr+10-2
...@@ -1,11 +1,19 @@...@@ -1,11 +1,19 @@
1error: Undefined Behavior: calling a function with return type i32 passing return place of type ()1error: Undefined Behavior: calling a function with return type i32 passing return place of type ()
2 --> tests/fail/shims/ctor_wrong_ret_type.rs:LL:CC
3 |
4LL | static $ident: unsafe extern "C" fn() -> i32 = $ctor;
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
6...
7LL | ctor! { CTOR = ctor }
8 | --------------------- in this macro invocation
2 |9 |
3 = note: Undefined Behavior occurred here
4 = note: (no span available)
5 = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior10 = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
6 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information11 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
7 = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets12 = help: this means these two types are not *guaranteed* to be ABI-compatible across all targets
8 = help: if you think this code should be accepted anyway, please report an issue with Miri13 = help: if you think this code should be accepted anyway, please report an issue with Miri
14 = note: this error occurred while pushing a call frame onto an empty stack
15 = note: the span indicates which code caused the function to be called, but may not be the literal call site
16 = note: this error originates in the macro `ctor` (in Nightly builds, run with -Z macro-backtrace for more info)
917
10error: aborting due to 1 previous error18error: aborting due to 1 previous error
1119
src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.rs created+18
...@@ -0,0 +1,18 @@
1//@only-target: darwin
2
3use std::{mem, ptr};
4
5extern "C" {
6 fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
7}
8
9fn register(dtor: unsafe fn(*mut u8)) {
10 unsafe {
11 _tlv_atexit(mem::transmute(dtor), ptr::null_mut());
12 //~^ERROR: calling a function with calling convention "Rust"
13 }
14}
15
16fn main() {
17 register(|_| ());
18}
src/tools/miri/tests/fail/shims/macos_tlv_atexit_wrong_abi.stderr created+13
...@@ -0,0 +1,13 @@
1error: Undefined Behavior: calling a function with calling convention "Rust" using calling convention "C"
2 --> tests/fail/shims/macos_tlv_atexit_wrong_abi.rs:LL:CC
3 |
4LL | _tlv_atexit(mem::transmute(dtor), ptr::null_mut());
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred due to this code
6 |
7 = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
8 = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
9 = note: this error occurred while pushing a call frame onto an empty stack
10 = note: the span indicates which code caused the function to be called, but may not be the literal call site
11
12error: aborting due to 1 previous error
13
src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs deleted-17
...@@ -1,17 +0,0 @@
1//@compile-flags: -Zmiri-permissive-provenance
2
3fn main() {
4 unsafe {
5 let root = &mut 42;
6 let addr = root as *mut i32 as usize;
7 let exposed_ptr = addr as *mut i32;
8 // From the exposed ptr, we get a new unique ptr.
9 let root2 = &mut *exposed_ptr;
10 let _fool = root2 as *mut _; // this would have fooled the old untagged pointer logic
11 // Stack: Unknown(<N), Unique(N), SRW(N+1)
12 // And we test that it has uniqueness by doing a conflicting write.
13 *exposed_ptr = 0;
14 // Stack: Unknown(<N)
15 let _val = *root2; //~ ERROR: /read access .* tag does not exist in the borrow stack/
16 }
17}
src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed1.stderr deleted-23
...@@ -1,23 +0,0 @@
1error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
2 --> tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs:LL:CC
3 |
4LL | let _val = *root2;
5 | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
9help: <TAG> was created by a Unique retag at offsets [0x0..0x4]
10 --> tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs:LL:CC
11 |
12LL | let root2 = &mut *exposed_ptr;
13 | ^^^^^^^^^^^^^^^^^
14help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access
15 --> tests/fail/stacked_borrows/illegal_read_despite_exposed1.rs:LL:CC
16 |
17LL | *exposed_ptr = 0;
18 | ^^^^^^^^^^^^^^^^
19
20note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
21
22error: aborting due to 1 previous error
23
src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs deleted-20
...@@ -1,20 +0,0 @@
1//@compile-flags: -Zmiri-permissive-provenance
2
3fn main() {
4 unsafe {
5 let root = &mut 42;
6 let addr = root as *mut i32 as usize;
7 let exposed_ptr = addr as *mut i32;
8 // From the exposed ptr, we get a new unique ptr.
9 let root2 = &mut *exposed_ptr;
10 // let _fool = root2 as *mut _; // this would fool us, since SRW(N+1) remains on the stack
11 // Stack: Unknown(<N), Unique(N)
12 // Stack if _fool existed: Unknown(<N), Unique(N), SRW(N+1)
13 // And we test that it has uniqueness by doing a conflicting read.
14 let _val = *exposed_ptr;
15 // Stack: Unknown(<N), Disabled(N)
16 // collapsed to Unknown(<N)
17 // Stack if _fool existed: Unknown(<N), Disabled(N), SRW(N+1); collapsed to Unknown(<N+2) which would not cause an ERROR
18 let _val = *root2; //~ ERROR: /read access .* tag does not exist in the borrow stack/
19 }
20}
src/tools/miri/tests/fail/stacked_borrows/illegal_read_despite_exposed2.stderr deleted-23
...@@ -1,23 +0,0 @@
1error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
2 --> tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs:LL:CC
3 |
4LL | let _val = *root2;
5 | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
9help: <TAG> was created by a Unique retag at offsets [0x0..0x4]
10 --> tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs:LL:CC
11 |
12LL | let root2 = &mut *exposed_ptr;
13 | ^^^^^^^^^^^^^^^^^
14help: <TAG> was later invalidated at offsets [0x0..0x4] by a read access
15 --> tests/fail/stacked_borrows/illegal_read_despite_exposed2.rs:LL:CC
16 |
17LL | let _val = *exposed_ptr;
18 | ^^^^^^^^^^^^
19
20note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
21
22error: aborting due to 1 previous error
23
src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs deleted-17
...@@ -1,17 +0,0 @@
1//@compile-flags: -Zmiri-permissive-provenance
2
3fn main() {
4 unsafe {
5 let root = &mut 42;
6 let addr = root as *mut i32 as usize;
7 let exposed_ptr = addr as *mut i32;
8 // From the exposed ptr, we get a new SRO ptr.
9 let root2 = &*exposed_ptr;
10 // Stack: Unknown(<N), SRO(N), SRO(N+1)
11 // And we test that it is read-only by doing a conflicting write.
12 // (The write is still fine, using the `root as *mut i32` provenance which got exposed.)
13 *exposed_ptr = 0;
14 // Stack: Unknown(<N)
15 let _val = *root2; //~ ERROR: /read access .* tag does not exist in the borrow stack/
16 }
17}
src/tools/miri/tests/fail/stacked_borrows/illegal_write_despite_exposed1.stderr deleted-23
...@@ -1,23 +0,0 @@
1error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
2 --> tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs:LL:CC
3 |
4LL | let _val = *root2;
5 | ^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
9help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x4]
10 --> tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs:LL:CC
11 |
12LL | let root2 = &*exposed_ptr;
13 | ^^^^^^^^^^^^^
14help: <TAG> was later invalidated at offsets [0x0..0x4] by a write access
15 --> tests/fail/stacked_borrows/illegal_write_despite_exposed1.rs:LL:CC
16 |
17LL | *exposed_ptr = 0;
18 | ^^^^^^^^^^^^^^^^
19
20note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
21
22error: aborting due to 1 previous error
23
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs created+36
...@@ -0,0 +1,36 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3/// Checks how accesses from one subtree affect other subtrees.
4/// This test checks the case where the access is to the main tree.
5pub fn main() {
6 let mut x: u32 = 42;
7
8 let ptr_base = &mut x as *mut u32;
9 let ref1 = unsafe { &mut *ptr_base };
10 let ref2 = unsafe { &mut *ptr_base };
11
12 let int1 = ref1 as *mut u32 as usize;
13 let wild = int1 as *mut u32;
14
15 let reb3 = unsafe { &mut *wild };
16
17 // ┌────────────┐
18 // │ │
19 // │ ptr_base ├───────────┐ *
20 // │ │ │ │
21 // └──────┬─────┘ │ │
22 // │ │ │
23 // │ │ │
24 // ▼ ▼ ▼
25 // ┌────────────┐ ┌───────────┐ ┌───────────┐
26 // │ │ │ │ │ │
27 // │ ref1(Res)* │ │ ref2(Res) │ │ reb3(Res) │
28 // │ │ │ │ │ │
29 // └────────────┘ └───────────┘ └───────────┘
30
31 // ref2 is part of the main tree and therefore foreign to all subtrees.
32 // Therefore, this disables reb3.
33 *ref2 = 13;
34
35 let _fail = *reb3; //~ ERROR: /read access through .* is forbidden/
36}
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_from_main.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs:LL:CC
3 |
4LL | let _fail = *reb3;
5 | ^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs:LL:CC
12 |
13LL | let reb3 = unsafe { &mut *wild };
14 | ^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/tree_borrows/wildcard/cross_tree_from_main.rs:LL:CC
17 |
18LL | *ref2 = 13;
19 | ^^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs created+37
...@@ -0,0 +1,37 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3/// Checks how accesses from one subtree affect other subtrees.
4/// This tests how main is effected by an access through a subtree.
5pub fn main() {
6 let mut x: u32 = 42;
7
8 let ptr_base = &mut x as *mut u32;
9 let ref1 = unsafe { &mut *ptr_base };
10 let ref2 = unsafe { &mut *ptr_base };
11
12 let int1 = ref1 as *mut u32 as usize;
13 let wild = int1 as *mut u32;
14
15 let reb = unsafe { &mut *wild };
16
17 // ┌────────────┐
18 // │ │
19 // │ ptr_base ├───────────┐ *
20 // │ │ │ │
21 // └──────┬─────┘ │ │
22 // │ │ │
23 // │ │ │
24 // ▼ ▼ ▼
25 // ┌────────────┐ ┌───────────┐ ┌───────────┐
26 // │ │ │ │ │ │
27 // │ ref1(Res)* │ │ ref2(Res) │ │ reb(Res) │
28 // │ │ │ │ │ │
29 // └────────────┘ └───────────┘ └───────────┘
30
31 // Writes through the reborrowed reference causing a wildcard
32 // write on the main tree. This disables ref2 as it doesn't
33 // have any exposed children.
34 *reb = 13;
35
36 let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
37}
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs:LL:CC
3 |
4LL | let _fail = *ref2;
5 | ^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs:LL:CC
12 |
13LL | let ref2 = unsafe { &mut *ptr_base };
14 | ^^^^^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/tree_borrows/wildcard/cross_tree_update_main.rs:LL:CC
17 |
18LL | *reb = 13;
19 | ^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.rs created+44
...@@ -0,0 +1,44 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3/// Checks how accesses from one subtree affect other subtrees.
4/// This test checks that an access from a subtree performs a
5/// wildcard access on all earlier trees, and that local
6/// accesses are treated as access errors for tags that are
7/// larger than the root of the accessed subtree.
8pub fn main() {
9 let mut x: u32 = 42;
10
11 let ptr_base = &mut x as *mut u32;
12 let ref1 = unsafe { &mut *ptr_base };
13
14 // Activates ref1.
15 *ref1 = 4;
16
17 let int1 = ref1 as *mut u32 as usize;
18 let wild = int1 as *mut u32;
19
20 let ref2 = unsafe { &mut *wild };
21
22 // Freezes ref1.
23 let ref3 = unsafe { &mut *ptr_base };
24 let _int3 = ref3 as *mut u32 as usize;
25
26 // ┌──────────────┐
27 // │ │
28 // │ptr_base(Act) ├───────────┐ *
29 // │ │ │ │
30 // └──────┬───────┘ │ │
31 // │ │ │
32 // │ │ │
33 // ▼ ▼ ▼
34 // ┌─────────────┐ ┌────────────┐ ┌───────────┐
35 // │ │ │ │ │ │
36 // │ ref1(Frz)* │ │ ref3(Res)* │ │ ref2(Res) │
37 // │ │ │ │ │ │
38 // └─────────────┘ └────────────┘ └───────────┘
39
40 // Performs a wildcard access on the main root. However, as there are
41 // no exposed tags with write permissions and a tag smaller than ref2
42 // this access fails.
43 *ref2 = 13; //~ ERROR: /write access through .* is forbidden/
44}
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.stderr created+14
...@@ -0,0 +1,14 @@
1error: Undefined Behavior: write access through <wildcard> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed.rs:LL:CC
3 |
4LL | *ref2 = 13;
5 | ^^^^^^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: there are no exposed tags which may perform this access here
10
11note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
12
13error: aborting due to 1 previous error
14
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs created+47
...@@ -0,0 +1,47 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3/// Checks how accesses from one subtree affect other subtrees.
4/// This test checks that an access from an earlier created subtree
5/// is foreign to a later created one.
6pub fn main() {
7 let mut x: u32 = 42;
8
9 let ref_base = &mut x;
10
11 let int0 = ref_base as *mut u32 as usize;
12 let wild = int0 as *mut u32;
13
14 let reb1 = unsafe { &mut *wild };
15
16 let reb2 = unsafe { &mut *wild };
17
18 let ref3 = &mut *reb1;
19 let _int3 = ref3 as *mut u32 as usize;
20 // ┌──────────────┐
21 // │ │
22 // │ptr_base(Res)*│ * *
23 // │ │ │ │
24 // └──────────────┘ │ │
25 // │ │
26 // │ │
27 // ▼ ▼
28 // ┌────────────┐ ┌────────────┐
29 // │ │ │ │
30 // │ reb1(Res) ├ │ reb2(Res) ├
31 // │ │ │ │
32 // └──────┬─────┘ └────────────┘
33 // │
34 // │
35 // ▼
36 // ┌────────────┐
37 // │ │
38 // │ ref3(Res)* │
39 // │ │
40 // └────────────┘
41
42 // This access disables reb2 because ref3 cannot be a child of it
43 // as reb2 both has a higher tag and doesn't have any exposed children.
44 *ref3 = 13;
45
46 let _fail = *reb2; //~ ERROR: /read access through .* is forbidden/
47}
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs:LL:CC
3 |
4LL | let _fail = *reb2;
5 | ^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs:LL:CC
12 |
13LL | let reb2 = unsafe { &mut *wild };
14 | ^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer.rs:LL:CC
17 |
18LL | *ref3 = 13;
19 | ^^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs created+48
...@@ -0,0 +1,48 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3/// Checks how accesses from one subtree affect other subtrees.
4/// This test checks that an access from an earlier created subtree
5/// is foreign to a later created one.
6pub fn main() {
7 let mut x: u32 = 42;
8
9 let ref_base = &mut x;
10
11 let int0 = ref_base as *mut u32 as usize;
12 let wild = int0 as *mut u32;
13
14 let reb1 = unsafe { &mut *wild };
15
16 let reb2 = unsafe { &mut *wild };
17 let _int2 = reb2 as *mut u32 as usize;
18
19 let ref3 = &mut *reb1;
20 let _int3 = ref3 as *mut u32 as usize;
21 // ┌──────────────┐
22 // │ │
23 // │ptr_base(Res)*│ * *
24 // │ │ │ │
25 // └──────────────┘ │ │
26 // │ │
27 // │ │
28 // ▼ ▼
29 // ┌────────────┐ ┌────────────┐
30 // │ │ │ │
31 // │ reb1(Res) │ │ reb2(Res)* │
32 // │ │ │ │
33 // └──────┬─────┘ └────────────┘
34 // │
35 // │
36 // ▼
37 // ┌────────────┐
38 // │ │
39 // │ ref3(Res)* │
40 // │ │
41 // └────────────┘
42
43 // This access disables reb2 because ref3 cannot be a child of it
44 // as ref3's root has a lower tag than reb2.
45 *ref3 = 13;
46
47 let _fail = *reb2; //~ ERROR: /read access through .* is forbidden/
48}
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs:LL:CC
3 |
4LL | let _fail = *reb2;
5 | ^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs:LL:CC
12 |
13LL | let reb2 = unsafe { &mut *wild };
14 | ^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/tree_borrows/wildcard/cross_tree_update_newer_exposed.rs:LL:CC
17 |
18LL | *ref3 = 13;
19 | ^^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs created+47
...@@ -0,0 +1,47 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3/// Checks how accesses from one subtree affect other subtrees.
4/// This test checks that an access from a newer created subtree
5/// performs a wildcard access on all earlier trees.
6pub fn main() {
7 let mut x: u32 = 42;
8
9 let ref_base = &mut x;
10
11 let int0 = ref_base as *mut u32 as usize;
12 let wild = int0 as *mut u32;
13
14 let reb1 = unsafe { &mut *wild };
15
16 let reb2 = unsafe { &mut *wild };
17
18 let ref3 = &mut *reb2;
19 let _int3 = ref3 as *mut u32 as usize;
20 // ┌──────────────┐
21 // │ │
22 // │ptr_base(Res)*│ * *
23 // │ │ │ │
24 // └──────────────┘ │ │
25 // │ │
26 // │ │
27 // ▼ ▼
28 // ┌────────────┐ ┌────────────┐
29 // │ │ │ │
30 // │ reb1(Res) ├ │ reb2(Res) ├
31 // │ │ │ │
32 // └────────────┘ └──────┬─────┘
33 // │
34 // │
35 // ▼
36 // ┌────────────┐
37 // │ │
38 // │ ref3(Res)* │
39 // │ │
40 // └────────────┘
41
42 // this access disables reb2 because ref3 cannot be a child of it
43 // as reb1 does not have any exposed children.
44 *ref3 = 13;
45
46 let _fail = *reb1; //~ ERROR: /read access through .* is forbidden/
47}
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs:LL:CC
3 |
4LL | let _fail = *reb1;
5 | ^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs:LL:CC
12 |
13LL | let reb1 = unsafe { &mut *wild };
14 | ^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/tree_borrows/wildcard/cross_tree_update_older.rs:LL:CC
17 |
18LL | *ref3 = 13;
19 | ^^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs created+53
...@@ -0,0 +1,53 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3/// Checks how accesses from one subtree affect other subtrees.
4/// This test checks that an access from a newer created subtree
5/// performs a wildcard access on all earlier trees, and that
6/// either accesses are treated as foreign for tags that are
7/// larger than the root of the accessed subtree.
8pub fn main() {
9 let mut x: u32 = 42;
10
11 let ref_base = &mut x;
12
13 let int0 = ref_base as *mut u32 as usize;
14 let wild = int0 as *mut u32;
15
16 let reb1 = unsafe { &mut *wild };
17
18 let reb2 = unsafe { &mut *wild };
19
20 let ref3 = &mut *reb1;
21 let _int3 = ref3 as *mut u32 as usize;
22
23 let ref4 = &mut *reb2;
24 let _int4 = ref4 as *mut u32 as usize;
25 // ┌──────────────┐
26 // │ │
27 // │ptr_base(Res)*│ * *
28 // │ │ │ │
29 // └──────────────┘ │ │
30 // │ │
31 // │ │
32 // ▼ ▼
33 // ┌────────────┐ ┌────────────┐
34 // │ │ │ │
35 // │ reb1(Res) ├ │ reb2(Res) ├
36 // │ │ │ │
37 // └──────┬─────┘ └──────┬─────┘
38 // │ │
39 // │ │
40 // ▼ ▼
41 // ┌────────────┐ ┌────────────┐
42 // │ │ │ │
43 // │ ref3(Res)* │ │ ref4(Res)* │
44 // │ │ │ │
45 // └────────────┘ └────────────┘
46
47 // This access disables ref3 and reb1 because ref4 cannot be a child of it
48 // as reb2 has a smaller tag than ref3.
49 *ref4 = 13;
50
51 // Fails because ref3 is disabled.
52 let _fail = *ref3; //~ ERROR: /read access through .* is forbidden/
53}
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs:LL:CC
3 |
4LL | let _fail = *ref3;
5 | ^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs:LL:CC
12 |
13LL | let ref3 = &mut *reb1;
14 | ^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed.rs:LL:CC
17 |
18LL | *ref4 = 13;
19 | ^^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs created+59
...@@ -0,0 +1,59 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3/// Checks how accesses from one subtree affect other subtrees.
4/// This test checks that an access from a newer created subtree
5/// performs a wildcard access on all earlier trees, and that
6/// either accesses are treated as foreign for tags that are
7/// larger than the root of the accessed subtree.
8/// This tests the special case where these updates get propagated
9/// up the tree.
10pub fn main() {
11 let mut x: u32 = 42;
12
13 let ref_base = &mut x;
14
15 let int0 = ref_base as *mut u32 as usize;
16 let wild = int0 as *mut u32;
17
18 let reb1 = unsafe { &mut *wild };
19
20 let reb2 = unsafe { &mut *wild };
21
22 let ref3 = &mut *reb1;
23 let _int3 = ref3 as *mut u32 as usize;
24
25 let ref4 = &mut *reb2;
26 let _int4 = ref4 as *mut u32 as usize;
27 // ┌──────────────┐
28 // │ │
29 // │ptr_base(Res)*│ * *
30 // │ │ │ │
31 // └──────────────┘ │ │
32 // │ │
33 // │ │
34 // ▼ ▼
35 // ┌────────────┐ ┌────────────┐
36 // │ │ │ │
37 // │ reb1(Res) ├ │ reb2(Res) ├
38 // │ │ │ │
39 // └──────┬─────┘ └──────┬─────┘
40 // │ │
41 // │ │
42 // ▼ ▼
43 // ┌────────────┐ ┌────────────┐
44 // │ │ │ │
45 // │ ref3(Res)* │ │ ref4(Res)* │
46 // │ │ │ │
47 // └────────────┘ └────────────┘
48
49 // This access disables ref3 and reb1 because ref4 cannot be a child of it
50 // as reb2 has a smaller tag than ref3.
51 //
52 // Because of the update order during a wildcard access (child before parent)
53 // ref3 gets disabled before we update reb1. So reb1 has no exposed children
54 // with write access at the time it gets updated so it also gets disabled.
55 *ref4 = 13;
56
57 // Fails because reb1 is disabled.
58 let _fail = *reb1; //~ ERROR: /read access through .* is forbidden/
59}
src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs:LL:CC
3 |
4LL | let _fail = *reb1;
5 | ^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs:LL:CC
12 |
13LL | let reb1 = unsafe { &mut *wild };
14 | ^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/tree_borrows/wildcard/cross_tree_update_older_invalid_exposed2.rs:LL:CC
17 |
18LL | *ref4 = 13;
19 | ^^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.rs created+39
...@@ -0,0 +1,39 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3/// Checks if we pass a reference derived from a wildcard pointer
4/// that it gets correctly protected.
5pub fn main() {
6 let mut x: u32 = 32;
7 let ref1 = &mut x;
8
9 let ref2 = &mut *ref1;
10 let int2 = ref2 as *mut u32 as usize;
11
12 let wild = int2 as *mut u32;
13 let wild_ref = unsafe { &mut *wild };
14
15 let mut protect = |_arg: &mut u32| {
16 // _arg is a protected pointer with wildcard parent.
17
18 // ┌────────────┐
19 // │ │
20 // │ ref1(Res) │ *
21 // │ │ │
22 // └──────┬─────┘ │
23 // │ │
24 // │ │
25 // ▼ ▼
26 // ┌────────────┐ ┌────────────┐
27 // │ │ │ │
28 // │ ref2(Res)* │ │ _arg(Res) │
29 // │ │ │ │
30 // └────────────┘ └────────────┘
31
32 // Writes to ref1, causing a foreign write to ref2 and _arg.
33 // Since _arg is protected this is UB.
34 *ref1 = 13; //~ ERROR: /write access through .* is forbidden/
35 };
36
37 // We pass a pointer with wildcard provenance to the function.
38 protect(wild_ref);
39}
src/tools/miri/tests/fail/tree_borrows/wildcard/protected_wildcard.stderr created+37
...@@ -0,0 +1,37 @@
1error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
3 |
4LL | *ref1 = 13;
5 | ^^^^^^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
10 = help: this foreign write access would cause the protected tag <TAG> (currently Reserved) to become Disabled
11 = help: protected tags must never be Disabled
12help: the accessed tag <TAG> was created here
13 --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
14 |
15LL | let mut protect = |_arg: &mut u32| {
16 | _______________________^
17... |
18LL | | *ref1 = 13;
19LL | | };
20 | |_____^
21help: the protected tag <TAG> was created here, in the initial state Reserved
22 --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
23 |
24LL | let mut protect = |_arg: &mut u32| {
25 | ^^^^
26 = note: BACKTRACE (of the first span):
27 = note: inside closure at tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
28note: inside `main`
29 --> tests/fail/tree_borrows/wildcard/protected_wildcard.rs:LL:CC
30 |
31LL | protect(wild_ref);
32 | ^^^^^^^^^^^^^^^^^
33
34note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
35
36error: aborting due to 1 previous error
37
src/tools/miri/tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.stderr+7-2
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1error: Undefined Behavior: deallocation through <wildcard> at ALLOC[0x0] is forbidden1error: Undefined Behavior: deallocation through <TAG> at ALLOC[0x0] is forbidden
2 --> RUSTLIB/alloc/src/boxed.rs:LL:CC2 --> RUSTLIB/alloc/src/boxed.rs:LL:CC
3 |3 |
4LL | self.1.deallocate(From::from(ptr.cast()), layout);4LL | self.1.deallocate(From::from(ptr.cast()), layout);
...@@ -6,8 +6,13 @@ LL | self.1.deallocate(From::from(ptr.cast()), layout);...@@ -6,8 +6,13 @@ LL | self.1.deallocate(From::from(ptr.cast()), layout);
6 |6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the allocation of the accessed tag <wildcard> also contains the strongly protected tag <TAG>9 = help: the allocation of the accessed tag <TAG> also contains the strongly protected tag <TAG>
10 = help: the strongly protected tag <TAG> disallows deallocations10 = help: the strongly protected tag <TAG> disallows deallocations
11help: the accessed tag <TAG> was created here
12 --> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC
13 |
14LL | drop(unsafe { Box::from_raw(raw as *mut i32) });
15 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11help: the strongly protected tag <TAG> was created here, in the initial state Reserved16help: the strongly protected tag <TAG> was created here, in the initial state Reserved
12 --> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC17 --> tests/fail/tree_borrows/wildcard/strongly_protected_wildcard.rs:LL:CC
13 |18 |
src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs created+44
...@@ -0,0 +1,44 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3// Checks if we correctly infer the relatedness of nodes that are
4// part of the same wildcard root.
5pub fn main() {
6 let mut x: u32 = 42;
7
8 let ref_base = &mut x;
9
10 let int1 = ref_base as *mut u32 as usize;
11 let wild = int1 as *mut u32;
12
13 let reb = unsafe { &mut *wild };
14 let ptr_reb = reb as *mut u32;
15 let ref1 = unsafe { &mut *ptr_reb };
16 let ref2 = unsafe { &mut *ptr_reb };
17
18 // ┌──────────────┐
19 // │ │
20 // │ptr_base(Res)*│ *
21 // │ │ │
22 // └──────────────┘ │
23 // │
24 // │
25 // ▼
26 // ┌────────────┐
27 // │ │
28 // │ reb(Res) ├───────────┐
29 // │ │ │
30 // └──────┬─────┘ │
31 // │ │
32 // │ │
33 // ▼ ▼
34 // ┌────────────┐ ┌───────────┐
35 // │ │ │ │
36 // │ ref1(Res) │ │ ref2(Res) │
37 // │ │ │ │
38 // └────────────┘ └───────────┘
39
40 // ref1 is foreign to ref2, so this should disable ref2.
41 *ref1 = 13;
42
43 let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
44}
src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs:LL:CC
3 |
4LL | let _fail = *ref2;
5 | ^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs:LL:CC
12 |
13LL | let ref2 = unsafe { &mut *ptr_reb };
14 | ^^^^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness.rs:LL:CC
17 |
18LL | *ref1 = 13;
19 | ^^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs created+49
...@@ -0,0 +1,49 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3// Checks if we correctly infer the relatedness of nodes that are
4// part of the same wildcard root during a wildcard access.
5pub fn main() {
6 let mut x: u32 = 42;
7
8 let ref_base = &mut x;
9
10 let int = ref_base as *mut u32 as usize;
11 let wild = int as *mut u32;
12
13 let reb = unsafe { &mut *wild };
14 let ptr_reb = reb as *mut u32;
15 let ref1 = unsafe { &mut *ptr_reb };
16 let _int1 = ref1 as *mut u32 as usize;
17 let ref2 = unsafe { &mut *ptr_reb };
18
19 // ┌──────────────┐
20 // │ │
21 // │ptr_base(Res)*│ *
22 // │ │ │
23 // └──────────────┘ │
24 // │
25 // │
26 // ▼
27 // ┌────────────┐
28 // │ │
29 // │ reb(Res) ├───────────┐
30 // │ │ │
31 // └──────┬─────┘ │
32 // │ │
33 // │ │
34 // ▼ ▼
35 // ┌────────────┐ ┌───────────┐
36 // │ │ │ │
37 // │ ref1(Res)* │ │ ref2(Res) │
38 // │ │ │ │
39 // └────────────┘ └───────────┘
40
41 // Writes either through ref1 or ptr_base.
42 // This disables ref2 as the access is foreign to it in either case.
43 unsafe { *wild = 13 };
44
45 // This is fine because the earlier write could have come from ref1.
46 let _succ = *ref1;
47 // ref2 is disabled so this fails.
48 let _fail = *ref2; //~ ERROR: /read access through .* is forbidden/
49}
src/tools/miri/tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.stderr created+25
...@@ -0,0 +1,25 @@
1error: Undefined Behavior: read access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs:LL:CC
3 |
4LL | let _fail = *ref2;
5 | ^^^^^ Undefined Behavior occurred here
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
9 = help: the accessed tag <TAG> has state Disabled which forbids this child read access
10help: the accessed tag <TAG> was created here, in the initial state Reserved
11 --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs:LL:CC
12 |
13LL | let ref2 = unsafe { &mut *ptr_reb };
14 | ^^^^^^^^^^^^^
15help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x4]
16 --> tests/fail/tree_borrows/wildcard/subtree_internal_relatedness_wildcard.rs:LL:CC
17 |
18LL | unsafe { *wild = 13 };
19 | ^^^^^^^^^^
20 = help: this transition corresponds to a loss of read and write permissions
21
22note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
23
24error: aborting due to 1 previous error
25
src/tools/miri/tests/fail/tree_borrows/write_to_shr.stderr deleted-26
...@@ -1,26 +0,0 @@
1error: Undefined Behavior: write access through <TAG> is forbidden
2 --> $DIR/write_to_shr.rs:LL:CC
3 |
4LL | *xmut = 31;
5 | ^^^^^^^^^^ write access through <TAG> is forbidden
6 |
7 = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
8 = help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
9 = help: the conflicting tag <TAG> has state Frozen which forbids child write accesses
10help: the accessed tag <TAG> was created here
11 --> $DIR/write_to_shr.rs:LL:CC
12 |
13LL | let xmut = unsafe { &mut *(xref as *const u64 as *mut u64) };
14 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
15help: the conflicting tag <TAG> was created here, in the initial state Frozen
16 --> $DIR/write_to_shr.rs:LL:CC
17 |
18LL | let xref = unsafe { &*(x as *mut u64) };
19 | ^^^^^^^^^^^^^^^^^
20 = note: BACKTRACE (of the first span):
21 = note: inside `main` at $DIR/write_to_shr.rs:LL:CC
22
23note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
24
25error: aborting due to 1 previous error
26
src/tools/miri/tests/genmc/fail/shims/mutex_diff_thread_unlock.stderr+2-2
...@@ -18,7 +18,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -18,7 +18,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
18 = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC18 = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
19 = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC19 = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC
20 = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC20 = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC
21 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC21 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
22note: inside `miri_start`22note: inside `miri_start`
23 --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC23 --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC
24 |24 |
...@@ -48,7 +48,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -48,7 +48,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
48 = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC48 = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
49 = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC49 = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC
50 = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC50 = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC
51 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC51 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
52note: inside `miri_start`52note: inside `miri_start`
53 --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC53 --> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC
54 |54 |
src/tools/miri/tests/genmc/pass/std/arc.check_count.stderr+3-3
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1Running GenMC Verification...1Running GenMC Verification...
2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
3 --> RUSTLIB/std/src/thread/mod.rs:LL:CC3 --> RUSTLIB/std/src/thread/id.rs:LL:CC
4 |4 |
5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {
6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
...@@ -46,7 +46,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -46,7 +46,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
46 |46 |
47 = note: BACKTRACE:47 = note: BACKTRACE:
48 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC48 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
49 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC49 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
50note: inside `main`50note: inside `main`
51 --> tests/genmc/pass/std/arc.rs:LL:CC51 --> tests/genmc/pass/std/arc.rs:LL:CC
52 |52 |
...@@ -67,7 +67,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -67,7 +67,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
67 |67 |
68 = note: BACKTRACE:68 = note: BACKTRACE:
69 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC69 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
70 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC70 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
71note: inside `main`71note: inside `main`
72 --> tests/genmc/pass/std/arc.rs:LL:CC72 --> tests/genmc/pass/std/arc.rs:LL:CC
73 |73 |
src/tools/miri/tests/genmc/pass/std/arc.try_upgrade.stderr+3-3
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1Running GenMC Verification...1Running GenMC Verification...
2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
3 --> RUSTLIB/std/src/thread/mod.rs:LL:CC3 --> RUSTLIB/std/src/thread/id.rs:LL:CC
4 |4 |
5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {
6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
...@@ -46,7 +46,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -46,7 +46,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
46 |46 |
47 = note: BACKTRACE:47 = note: BACKTRACE:
48 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC48 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
49 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC49 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
50note: inside `main`50note: inside `main`
51 --> tests/genmc/pass/std/arc.rs:LL:CC51 --> tests/genmc/pass/std/arc.rs:LL:CC
52 |52 |
...@@ -67,7 +67,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -67,7 +67,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
67 |67 |
68 = note: BACKTRACE:68 = note: BACKTRACE:
69 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC69 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
70 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC70 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
71note: inside `main`71note: inside `main`
72 --> tests/genmc/pass/std/arc.rs:LL:CC72 --> tests/genmc/pass/std/arc.rs:LL:CC
73 |73 |
src/tools/miri/tests/genmc/pass/std/empty_main.stderr+1-1
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1Running GenMC Verification...1Running GenMC Verification...
2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
3 --> RUSTLIB/std/src/thread/mod.rs:LL:CC3 --> RUSTLIB/std/src/thread/id.rs:LL:CC
4 |4 |
5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {
6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
src/tools/miri/tests/genmc/pass/std/spawn_std_threads.stderr+3-3
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1Running GenMC Verification...1Running GenMC Verification...
2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
3 --> RUSTLIB/std/src/thread/mod.rs:LL:CC3 --> RUSTLIB/std/src/thread/id.rs:LL:CC
4 |4 |
5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {
6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
...@@ -20,7 +20,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -20,7 +20,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
20 |20 |
21 = note: BACKTRACE:21 = note: BACKTRACE:
22 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC22 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
23 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC23 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
24note: inside closure24note: inside closure
25 --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC25 --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC
26 |26 |
...@@ -52,7 +52,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -52,7 +52,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
52 |52 |
53 = note: BACKTRACE:53 = note: BACKTRACE:
54 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC54 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
55 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC55 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
56note: inside closure56note: inside closure
57 --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC57 --> tests/genmc/pass/std/spawn_std_threads.rs:LL:CC
58 |58 |
src/tools/miri/tests/genmc/pass/std/thread_locals.stderr+3-3
...@@ -1,6 +1,6 @@...@@ -1,6 +1,6 @@
1Running GenMC Verification...1Running GenMC Verification...
2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.2warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
3 --> RUSTLIB/std/src/thread/mod.rs:LL:CC3 --> RUSTLIB/std/src/thread/id.rs:LL:CC
4 |4 |
5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {5LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) {
6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code6 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
...@@ -20,7 +20,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -20,7 +20,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
20 |20 |
21 = note: BACKTRACE:21 = note: BACKTRACE:
22 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC22 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
23 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC23 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
24note: inside `main`24note: inside `main`
25 --> tests/genmc/pass/std/thread_locals.rs:LL:CC25 --> tests/genmc/pass/std/thread_locals.rs:LL:CC
26 |26 |
...@@ -42,7 +42,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir...@@ -42,7 +42,7 @@ LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquir
42 |42 |
43 = note: BACKTRACE:43 = note: BACKTRACE:
44 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC44 = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
45 = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC45 = note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
46note: inside `main`46note: inside `main`
47 --> tests/genmc/pass/std/thread_locals.rs:LL:CC47 --> tests/genmc/pass/std/thread_locals.rs:LL:CC
48 |48 |
src/tools/miri/tests/pass-dep/libc/libc-fs.rs+37-1
...@@ -38,10 +38,11 @@ fn main() {...@@ -38,10 +38,11 @@ fn main() {
38 test_posix_fadvise();38 test_posix_fadvise();
39 #[cfg(not(target_os = "macos"))]39 #[cfg(not(target_os = "macos"))]
40 test_posix_fallocate::<libc::off_t>(libc::posix_fallocate);40 test_posix_fallocate::<libc::off_t>(libc::posix_fallocate);
41 #[cfg(any(target_os = "linux", target_os = "android"))]41 #[cfg(target_os = "linux")]
42 test_posix_fallocate::<libc::off64_t>(libc::posix_fallocate64);42 test_posix_fallocate::<libc::off64_t>(libc::posix_fallocate64);
43 #[cfg(target_os = "linux")]43 #[cfg(target_os = "linux")]
44 test_sync_file_range();44 test_sync_file_range();
45 test_fstat();
45 test_isatty();46 test_isatty();
46 test_read_and_uninit();47 test_read_and_uninit();
47 test_nofollow_not_symlink();48 test_nofollow_not_symlink();
...@@ -452,6 +453,41 @@ fn test_sync_file_range() {...@@ -452,6 +453,41 @@ fn test_sync_file_range() {
452 assert_eq!(result_2, 0);453 assert_eq!(result_2, 0);
453}454}
454455
456fn test_fstat() {
457 use std::mem::MaybeUninit;
458 use std::os::unix::io::AsRawFd;
459
460 let path = utils::prepare_with_content("miri_test_libc_fstat.txt", b"hello");
461 let file = File::open(&path).unwrap();
462 let fd = file.as_raw_fd();
463
464 let mut stat = MaybeUninit::<libc::stat>::uninit();
465 let res = unsafe { libc::fstat(fd, stat.as_mut_ptr()) };
466 assert_eq!(res, 0);
467 let stat = unsafe { stat.assume_init_ref() };
468
469 assert_eq!(stat.st_size, 5);
470 assert_eq!(stat.st_mode & libc::S_IFMT, libc::S_IFREG);
471
472 // Check that all fields are initialized.
473 let _st_nlink = stat.st_nlink;
474 let _st_blksize = stat.st_blksize;
475 let _st_blocks = stat.st_blocks;
476 let _st_ino = stat.st_ino;
477 let _st_dev = stat.st_dev;
478 let _st_uid = stat.st_uid;
479 let _st_gid = stat.st_gid;
480 let _st_rdev = stat.st_rdev;
481 let _st_atime = stat.st_atime;
482 let _st_mtime = stat.st_mtime;
483 let _st_ctime = stat.st_ctime;
484 let _st_atime_nsec = stat.st_atime_nsec;
485 let _st_mtime_nsec = stat.st_mtime_nsec;
486 let _st_ctime_nsec = stat.st_ctime_nsec;
487
488 remove_file(&path).unwrap();
489}
490
455fn test_isatty() {491fn test_isatty() {
456 // Testing whether our isatty shim returns the right value would require controlling whether492 // Testing whether our isatty shim returns the right value would require controlling whether
457 // these streams are actually TTYs, which is hard.493 // these streams are actually TTYs, which is hard.
src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.rs created+36
...@@ -0,0 +1,36 @@
1// We disable the GC for this test because it would change what is printed.
2//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance -Zmiri-provenance-gc=0
3
4#[path = "../../../utils/mod.rs"]
5#[macro_use]
6mod utils;
7
8fn main() {
9 unsafe {
10 let x = &0u8;
11 name!(x);
12 let xa = &*x;
13 name!(xa);
14 let xb = &*x;
15 name!(xb);
16 let wild = xb as *const u8 as usize as *const u8;
17
18 let y = &*wild;
19 name!(y);
20 let ya = &*y;
21 name!(ya);
22 let yb = &*y;
23 name!(yb);
24 let _int = ya as *const u8 as usize;
25
26 let z = &*wild;
27 name!(z);
28
29 let u = &*wild;
30 name!(u);
31 let ua = &*u;
32 name!(ua);
33 let alloc_id = alloc_id!(x);
34 print_state!(alloc_id);
35 }
36}
src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr created+17
...@@ -0,0 +1,17 @@
1──────────────────────────────────────────────────
2Warning: this tree is indicative only. Some tags may have been hidden.
30.. 1
4| Act | └─┬──<TAG=root of the allocation>
5| Frz | └─┬──<TAG=x>
6| Frz | ├────<TAG=xa>
7| Frz | └────<TAG=xb> (exposed)
8| |
9| Frz | *─┬──<TAG=y>
10| Frz | ├────<TAG=ya> (exposed)
11| Frz | └────<TAG=yb>
12| |
13| Frz | *────<TAG=z>
14| |
15| Frz | *─┬──<TAG=u>
16| Frz | └────<TAG=ua>
17──────────────────────────────────────────────────
src/tools/miri/tests/pass/tree_borrows/wildcard/reborrow.rs created+224
...@@ -0,0 +1,224 @@
1//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance
2
3pub fn main() {
4 multiple_exposed_siblings1();
5 multiple_exposed_siblings2();
6 reborrow3();
7 returned_mut_is_usable();
8 only_foreign_is_temporary();
9}
10
11/// Checks that accessing through a reborrowed wildcard doesn't
12/// disable any exposed reference.
13fn multiple_exposed_siblings1() {
14 let mut x: u32 = 42;
15
16 let ptr_base = &mut x as *mut u32;
17
18 let ref1 = unsafe { &mut *ptr_base };
19 let int1 = ref1 as *mut u32 as usize;
20
21 let ref2 = unsafe { &mut *ptr_base };
22 let _int2 = ref2 as *mut u32 as usize;
23
24 let wild = int1 as *mut u32;
25
26 let reb = unsafe { &mut *wild };
27
28 // ┌────────────┐
29 // │ │
30 // │ ptr_base ├────────────┐ *
31 // │ │ │ │
32 // └──────┬─────┘ │ │
33 // │ │ │
34 // │ │ │
35 // ▼ ▼ ▼
36 // ┌────────────┐ ┌────────────┐ ┌────────────┐
37 // │ │ │ │ │ │
38 // │ ref1(Res)* │ │ ref2(Res)* │ │ reb(Res) │
39 // │ │ │ │ │ │
40 // └────────────┘ └────────────┘ └────────────┘
41
42 // Could either have as a parent ref1 or ref2.
43 // So we can't disable either of them.
44 *reb = 13;
45
46 // We can still access either ref1 or ref2.
47 // Although it is actually UB to access both of them.
48 assert_eq!(*ref2, 13);
49 assert_eq!(*ref1, 13);
50}
51
52/// Checks that wildcard accesses do not invalidate any exposed
53/// nodes through which the access could have happened.
54/// It checks this for the case where some reborrowed wildcard
55/// pointers are exposed as well.
56fn multiple_exposed_siblings2() {
57 let mut x: u32 = 42;
58
59 let ptr_base = &mut x as *mut u32;
60 let int = ptr_base as usize;
61
62 let wild = int as *mut u32;
63
64 let reb_ptr = unsafe { &mut *wild } as *mut u32;
65
66 let ref1 = unsafe { &mut *reb_ptr };
67 let _int1 = ref1 as *mut u32 as usize;
68
69 let ref2 = unsafe { &mut *reb_ptr };
70 let _int2 = ref2 as *mut u32 as usize;
71
72 // ┌────────────┐
73 // │ │
74 // │ ptr_base* │ *
75 // │ │ │
76 // └────────────┘ │
77 // │
78 // │
79 // ▼
80 // ┌────────────┐
81 // │ │
82 // │ reb ├────────────┐
83 // │ │ │
84 // └──────┬─────┘ │
85 // │ │
86 // │ │
87 // ▼ ▼
88 // ┌────────────┐ ┌────────────┐
89 // │ │ │ │
90 // │ ref1(Res)* │ │ ref2(Res)* │
91 // │ │ │ │
92 // └────────────┘ └────────────┘
93
94 // Writes either through ref1, ref2 or ptr_base, which are all exposed.
95 // Since we don't know which we do not apply any transitions to any of
96 // the references.
97 unsafe { wild.write(13) };
98
99 // We should be able to access either ref1 or ref2.
100 // Although it is actually UB to access ref1 and ref2 together.
101 assert_eq!(*ref2, 13);
102 assert_eq!(*ref1, 13);
103}
104
105/// Checks that accessing a reborrowed wildcard reference doesn't
106/// invalidate other reborrowed wildcard references, if they
107/// are also exposed.
108fn reborrow3() {
109 let mut x: u32 = 42;
110
111 let ptr_base = &mut x as *mut u32;
112 let int = ptr_base as usize;
113
114 let wild = int as *mut u32;
115
116 let reb1 = unsafe { &mut *wild };
117 let ref2 = &mut *reb1;
118 let _int = ref2 as *mut u32 as usize;
119
120 let reb3 = unsafe { &mut *wild };
121
122 // ┌────────────┐
123 // │ │
124 // │ ptr_base* │ * *
125 // │ │ │ │
126 // └────────────┘ │ │
127 // │ │
128 // │ │
129 // ▼ ▼
130 // ┌────────────┐ ┌────────────┐
131 // │ │ │ │
132 // │ reb1(Res) | │ reb3(Res) |
133 // │ │ │ │
134 // └──────┬─────┘ └────────────┘
135 // │
136 // │
137 // ▼
138 // ┌────────────┐
139 // │ │
140 // │ ref2(Res)* │
141 // │ │
142 // └────────────┘
143
144 // This is the only valid ordering these accesses can happen in.
145
146 // reb3 could be a child of ref2 so we don't disable ref2, reb1.
147 *reb3 = 1;
148 // Disables reb3 as it cannot be an ancestor of ref2.
149 *ref2 = 2;
150 // Disables ref2 (and reb3 if it wasn't already).
151 *reb1 = 3;
152}
153
154/// Analogous to same test in `../tree-borrows.rs` but with returning a
155/// reborrowed wildcard reference.
156fn returned_mut_is_usable() {
157 let mut x: u32 = 32;
158 let ref1 = &mut x;
159
160 let y = protect(ref1);
161
162 fn protect(arg: &mut u32) -> &mut u32 {
163 // Reborrow `arg` through a wildcard.
164 let int = arg as *mut u32 as usize;
165 let wild = int as *mut u32;
166 let ref2 = unsafe { &mut *wild };
167
168 // Activate the reference so that it is vulnerable to foreign reads.
169 *ref2 = 42;
170
171 ref2
172 // An implicit read through `arg` is inserted here.
173 }
174
175 *y = 4;
176}
177
178/// When accessing an allocation through a tag that was created from wildcard reference
179/// we treat nodes with a larger tag as if the access could only have been foreign to them.
180/// This change in access relatedness should not be visible in later accesses.
181fn only_foreign_is_temporary() {
182 let mut x = 0u32;
183 let wild = &mut x as *mut u32 as usize as *mut u32;
184
185 let reb1 = unsafe { &mut *wild };
186 let reb2 = unsafe { &mut *wild };
187 let ref3 = &mut *reb1;
188 let _int = ref3 as *mut u32 as usize;
189
190 let reb4 = unsafe { &mut *wild };
191
192 //
193 //
194 // * * *
195 // │ │ │
196 // │ │ │
197 // │ │ │
198 // │ │ │
199 // ▼ ▼ ▼
200 // ┌────────────┐ ┌────────────┐ ┌────────────┐
201 // │ │ │ │ │ │
202 // │ reb1(Res) │ │ reb2(Res) │ │ reb4(Res) │
203 // │ │ │ │ │ │
204 // └──────┬─────┘ └────────────┘ └────────────┘
205 // │
206 // │
207 // ▼
208 // ┌────────────┐
209 // │ │
210 // │ ref3(Res)* │
211 // │ │
212 // └────────────┘
213
214 // Performs a foreign read on ref3 and doesn't update reb1.
215 // This temporarily treats ref3 as if only foreign accesses are possible to
216 // it. This is because the accessed tag reb2 has a larger tag than ref3.
217 let _x = *reb2;
218 // Should not update ref3, reb1 as we don't know if the access is local or foreign.
219 // This should stop treating ref3 as only foreign because the accessed tag reb4
220 // has a larger tag than ref3.
221 *reb4 = 32;
222 // The previous write could have been local to ref3, so this access should still work.
223 *ref3 = 4;
224}
src/tools/miri/tests/pass/tree_borrows/wildcard/undetected_ub.rs+55-39
...@@ -4,7 +4,7 @@...@@ -4,7 +4,7 @@
4pub fn main() {4pub fn main() {
5 uncertain_provenance();5 uncertain_provenance();
6 protected_exposed();6 protected_exposed();
7 protected_wildcard();7 cross_tree_update_older_invalid_exposed();
8}8}
99
10/// Currently, if we do not know for a tag if an access is local or foreign,10/// Currently, if we do not know for a tag if an access is local or foreign,
...@@ -101,45 +101,61 @@ pub fn protected_exposed() {...@@ -101,45 +101,61 @@ pub fn protected_exposed() {
101 }101 }
102 protect(ref1);102 protect(ref1);
103103
104 // ref2 is disabled, so this read causes UB, but we currently don't protect this.104 // ref2 should be disabled, so this read causes UB, but we currently don't detect this.
105 let _fail = *ref2;105 let _fail = *ref2;
106}106}
107107
108/// Currently, we do not assign protectors to wildcard references.108/// Checks how accesses from one subtree affect other subtrees.
109/// This test has UB because it does a foreign write to a protected reference.109/// This test shows an example where we don't update a node whose exposed
110/// However, that reference is a wildcard, so this doesn't get detected.110/// children are greater than `max_local_tag`.
111#[allow(unused_variables)]111pub fn cross_tree_update_older_invalid_exposed() {
112pub fn protected_wildcard() {112 let mut x: [u32; 2] = [42, 43];
113 let mut x: u32 = 32;113
114 let ref1 = &mut x;114 let ref_base = &mut x;
115 let ref2 = &mut *ref1;115
116116 let int0 = ref_base as *mut u32 as usize;
117 let int = ref2 as *mut u32 as usize;117 let wild = int0 as *mut u32;
118 let wild = int as *mut u32;118
119 let wild_ref = unsafe { &mut *wild };119 let reb1 = unsafe { &mut *wild };
120120 *reb1 = 44;
121 let mut protect = |arg: &mut u32| {121
122 // arg is a protected pointer with wildcard provenance.122 // We need this reference to be at a different location,
123123 // so that creating it doesn't freeze reb1.
124 // ┌────────────┐124 let reb2 = unsafe { &mut *wild.wrapping_add(1) };
125 // │ │125 let reb2_ptr = (reb2 as *mut u32).wrapping_sub(1);
126 // │ ref1(Res) │126
127 // │ │127 let ref3 = &mut *reb1;
128 // └──────┬─────┘128 let _int3 = ref3 as *mut u32 as usize;
129 // │129
130 // │130 // ┌──────────────┐
131 // ▼131 // │ │
132 // ┌────────────┐132 // │ptr_base(Res)*│ * *
133 // │ │133 // │ │ │ │
134 // │ ref2(Res)* │134 // └──────────────┘ │ │
135 // │ │135 // │ │
136 // └────────────┘136 // │ │
137137 // ▼ ▼
138 // Writes to ref1, disabling ref2, i.e. disabling all exposed references.138 // ┌────────────┐ ┌────────────┐
139 // Since a wildcard reference is protected, this is UB. But we currently don't detect this.139 // │ │ │ │
140 *ref1 = 13;140 // │ reb1(Act) │ │ reb2(Res) │
141 };141 // │ │ │ │
142142 // └──────┬─────┘ └────────────┘
143 // We pass a pointer with wildcard provenance to the function.143 // │
144 protect(wild_ref);144 // │
145 // ▼
146 // ┌────────────┐
147 // │ │
148 // │ ref3(Res)* │
149 // │ │
150 // └────────────┘
151
152 // This access doesn't freeze reb1 even though no access could have come from its
153 // child ref3 (since ref3>reb2). This is because ref3 doesnt get disabled during this
154 // access.
155 //
156 // ref3 doesn't get frozen because it's still reserved.
157 let _y = unsafe { *reb2_ptr };
158
159 // reb1 should be frozen so a write should be UB. But we currently don't detect this.
160 *reb1 = 4;
145}161}
src/tools/miri/tests/pass/tree_borrows/wildcard/wildcard.rs-1
...@@ -151,7 +151,6 @@ fn protector_conflicted_release() {...@@ -151,7 +151,6 @@ fn protector_conflicted_release() {
151151
152/// Analogous to same test in `../tree-borrows.rs` but with a protected wildcard reference.152/// Analogous to same test in `../tree-borrows.rs` but with a protected wildcard reference.
153fn returned_mut_is_usable() {153fn returned_mut_is_usable() {
154 // NOTE: Currently we ignore protectors on wildcard references.
155 fn reborrow(x: &mut u8) -> &mut u8 {154 fn reborrow(x: &mut u8) -> &mut u8 {
156 let y = &mut *x;155 let y = &mut *x;
157 // Activate the reference so that it is vulnerable to foreign reads.156 // Activate the reference so that it is vulnerable to foreign reads.
src/tools/miri/tests/utils/macros.rs+2-2
...@@ -9,7 +9,7 @@...@@ -9,7 +9,7 @@
9/// The id obtained can be passed directly to `print_state!`.9/// The id obtained can be passed directly to `print_state!`.
10macro_rules! alloc_id {10macro_rules! alloc_id {
11 ($ptr:expr) => {11 ($ptr:expr) => {
12 $crate::utils::miri_get_alloc_id($ptr as *const u8 as *const ())12 $crate::utils::miri_get_alloc_id($ptr as *const _ as *const ())
13 };13 };
14}14}
1515
...@@ -52,6 +52,6 @@ macro_rules! name {...@@ -52,6 +52,6 @@ macro_rules! name {
52 };52 };
53 ($ptr:expr => $nth_parent:expr, $name:expr) => {53 ($ptr:expr => $nth_parent:expr, $name:expr) => {
54 let name = $name.as_bytes();54 let name = $name.as_bytes();
55 $crate::utils::miri_pointer_name($ptr as *const u8 as *const (), $nth_parent, name);55 $crate::utils::miri_pointer_name($ptr as *const _ as *const (), $nth_parent, name);
56 };56 };
57}57}
tests/run-make/rustdoc-merge-directory-alias/dep1.rs created+10
...@@ -0,0 +1,10 @@
1pub struct Dep1;
2pub struct Dep2;
3pub struct Dep3;
4pub struct Dep4;
5
6//@ hasraw crates.js 'dep1'
7//@ hasraw search.index/name/*.js 'Dep1'
8//@ has dep1/index.html
9#[doc(alias = "dep1_missing")]
10pub struct Dep5;
tests/run-make/rustdoc-merge-directory-alias/dep2.rs created+4
...@@ -0,0 +1,4 @@
1//@ hasraw crates.js 'dep2'
2//@ hasraw search.index/name/*.js 'Second'
3//@ has dep2/index.html
4pub struct Second;
tests/run-make/rustdoc-merge-directory-alias/dep_missing.rs created+4
...@@ -0,0 +1,4 @@
1//@ !hasraw crates.js 'dep_missing'
2//@ !hasraw search.index/name/*.js 'DepMissing'
3//@ has dep_missing/index.html
4pub struct DepMissing;
tests/run-make/rustdoc-merge-directory-alias/rmake.rs created+88
...@@ -0,0 +1,88 @@
1// Running --merge=finalize without an input crate root should not trigger ICE.
2// Issue: https://github.com/rust-lang/rust/issues/146646
3
4//@ needs-target-std
5
6use run_make_support::{htmldocck, path, rustdoc};
7
8fn main() {
9 let out_dir = path("out");
10 let merged_dir = path("merged");
11 let parts_out_dir = path("parts");
12
13 rustdoc()
14 .input("dep1.rs")
15 .out_dir(&out_dir)
16 .arg("-Zunstable-options")
17 .arg(format!("--parts-out-dir={}", parts_out_dir.display()))
18 .arg("--merge=none")
19 .run();
20 assert!(parts_out_dir.join("dep1.json").exists());
21
22 let output = rustdoc()
23 .arg("-Zunstable-options")
24 .out_dir(&out_dir)
25 .arg(format!("--include-parts-dir={}", parts_out_dir.display()))
26 .arg("--merge=finalize")
27 .run();
28 output.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug.");
29
30 rustdoc()
31 .input("dep2.rs")
32 .out_dir(&out_dir)
33 .arg("-Zunstable-options")
34 .arg(format!("--parts-out-dir={}", parts_out_dir.display()))
35 .arg("--merge=none")
36 .run();
37 assert!(parts_out_dir.join("dep2.json").exists());
38
39 let output2 = rustdoc()
40 .arg("-Zunstable-options")
41 .out_dir(&out_dir)
42 .arg(format!("--include-parts-dir={}", parts_out_dir.display()))
43 .arg("--merge=finalize")
44 .run();
45 output2.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug.");
46
47 rustdoc()
48 .input("dep1.rs")
49 .out_dir(&out_dir)
50 .arg("-Zunstable-options")
51 .arg(format!("--parts-out-dir={}", parts_out_dir.display()))
52 .arg("--merge=none")
53 .run();
54 assert!(parts_out_dir.join("dep1.json").exists());
55
56 let output3 = rustdoc()
57 .arg("-Zunstable-options")
58 .out_dir(&out_dir)
59 .arg(format!("--include-parts-dir={}", parts_out_dir.display()))
60 .arg("--merge=finalize")
61 .run();
62 output3.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug.");
63
64 // dep_missing is different, because --parts-out-dir is not supplied
65 rustdoc().input("dep_missing.rs").out_dir(&out_dir).run();
66 assert!(parts_out_dir.join("dep2.json").exists());
67
68 rustdoc()
69 .input("dep1.rs")
70 .out_dir(&out_dir)
71 .arg("-Zunstable-options")
72 .arg(format!("--parts-out-dir={}", parts_out_dir.display()))
73 .arg("--merge=none")
74 .run();
75 assert!(parts_out_dir.join("dep1.json").exists());
76
77 let output4 = rustdoc()
78 .arg("-Zunstable-options")
79 .out_dir(&out_dir)
80 .arg(format!("--include-parts-dir={}", parts_out_dir.display()))
81 .arg("--merge=finalize")
82 .run();
83 output4.assert_stderr_not_contains("error: the compiler unexpectedly panicked. this is a bug.");
84
85 htmldocck().arg(&out_dir).arg("dep1.rs").run();
86 htmldocck().arg(&out_dir).arg("dep2.rs").run();
87 htmldocck().arg(&out_dir).arg("dep_missing.rs").run();
88}
tests/ui/feature-gates/unknown-feature.rs+14-1
...@@ -1,3 +1,16 @@...@@ -1,3 +1,16 @@
1#![feature(unknown_rust_feature)] //~ ERROR unknown feature1#![feature(
2 unknown_rust_feature,
3 //~^ ERROR unknown feature
4
5 // Typo for lang feature
6 associated_types_default,
7 //~^ ERROR unknown feature
8 //~| HELP there is a feature with a similar name
9
10 // Typo for lib feature
11 core_intrnisics,
12 //~^ ERROR unknown feature
13 //~| HELP there is a feature with a similar name
14)]
215
3fn main() {}16fn main() {}
tests/ui/feature-gates/unknown-feature.stderr+28-4
...@@ -1,9 +1,33 @@...@@ -1,9 +1,33 @@
1error[E0635]: unknown feature `unknown_rust_feature`1error[E0635]: unknown feature `unknown_rust_feature`
2 --> $DIR/unknown-feature.rs:1:122 --> $DIR/unknown-feature.rs:2:5
3 |3 |
4LL | #![feature(unknown_rust_feature)]4LL | unknown_rust_feature,
5 | ^^^^^^^^^^^^^^^^^^^^5 | ^^^^^^^^^^^^^^^^^^^^
66
7error: aborting due to 1 previous error7error[E0635]: unknown feature `associated_types_default`
8 --> $DIR/unknown-feature.rs:6:5
9 |
10LL | associated_types_default,
11 | ^^^^^^^^^^^^^^^^^^^^^^^^
12 |
13help: there is a feature with a similar name: `associated_type_defaults`
14 |
15LL - associated_types_default,
16LL + associated_type_defaults,
17 |
18
19error[E0635]: unknown feature `core_intrnisics`
20 --> $DIR/unknown-feature.rs:11:5
21 |
22LL | core_intrnisics,
23 | ^^^^^^^^^^^^^^^
24 |
25help: there is a feature with a similar name: `core_intrinsics`
26 |
27LL - core_intrnisics,
28LL + core_intrinsics,
29 |
30
31error: aborting due to 3 previous errors
832
9For more information about this error, try `rustc --explain E0635`.33For more information about this error, try `rustc --explain E0635`.
tests/ui/suggestions/deref-path-method.stderr+1-1
...@@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll...@@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<_, _>` consider using one of the foll
9 Vec::<T>::with_capacity9 Vec::<T>::with_capacity
10 Vec::<T>::try_with_capacity10 Vec::<T>::try_with_capacity
11 Vec::<T>::from_raw_parts11 Vec::<T>::from_raw_parts
12 and 6 others12 and 7 others
13 --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL13 --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
14help: the function `contains` is implemented on `[_]`14help: the function `contains` is implemented on `[_]`
15 |15 |
tests/ui/ufcs/bad-builder.stderr+1-1
...@@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<Q>` consider using one of the followi...@@ -9,7 +9,7 @@ note: if you're trying to build a new `Vec<Q>` consider using one of the followi
9 Vec::<T>::with_capacity9 Vec::<T>::with_capacity
10 Vec::<T>::try_with_capacity10 Vec::<T>::try_with_capacity
11 Vec::<T>::from_raw_parts11 Vec::<T>::from_raw_parts
12 and 6 others12 and 7 others
13 --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL13 --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
14help: there is an associated function `new` with a similar name14help: there is an associated function `new` with a similar name
15 |15 |