Auto merge of #130483 - matthiaskrgr:rollup-q1r0g0y, r=matthiaskrgr

Rollup of 5 pull requests

Successful merges:

 - #129477 (Fix fluent diagnostics)
 - #129674 (Add new_cyclic_in for Rc and Arc)
 - #130452 (Update Trusty target maintainers)
 - #130467 (Miri subtree update)
 - #130477 (Revert #129749 to fix segfault in LLVM)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-09-17 19:37:03 +00:00
commit 28e8f01c2a
752 changed files with 2940 additions and 2704 deletions

View File

@ -104,15 +104,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anstream"
version = "0.6.15"
@ -714,7 +705,7 @@ dependencies = [
"miow",
"miropt-test-tools",
"regex",
"rustfix 0.8.1",
"rustfix",
"serde",
"serde_json",
"tracing",
@ -2278,7 +2269,7 @@ dependencies = [
"rustc_version",
"smallvec",
"tempfile",
"ui_test 0.21.2",
"ui_test 0.26.5",
"windows-sys 0.52.0",
]
@ -2807,16 +2798,6 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "prettydiff"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ff1fec61082821f8236cf6c0c14e8172b62ce8a72a0eedc30d3b247bb68dc11"
dependencies = [
"ansi_term",
"pad",
]
[[package]]
name = "prettydiff"
version = "0.7.0"
@ -4625,18 +4606,6 @@ dependencies = [
"rustdoc",
]
[[package]]
name = "rustfix"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481"
dependencies = [
"anyhow",
"log",
"serde",
"serde_json",
]
[[package]]
name = "rustfix"
version = "0.8.1"
@ -5516,33 +5485,6 @@ version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]]
name = "ui_test"
version = "0.21.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf4bf7c184b8dfc7a4d3b90df789b1eb992ee42811cd115f32a7a1eb781058d"
dependencies = [
"annotate-snippets 0.9.2",
"anyhow",
"bstr",
"cargo-platform",
"cargo_metadata 0.15.4",
"color-eyre",
"colored",
"comma",
"crossbeam-channel",
"indicatif",
"lazy_static",
"levenshtein",
"prettydiff 0.6.4",
"regex",
"rustc_version",
"rustfix 0.6.1",
"serde",
"serde_json",
"tempfile",
]
[[package]]
name = "ui_test"
version = "0.25.0"
@ -5561,10 +5503,36 @@ dependencies = [
"indicatif",
"lazy_static",
"levenshtein",
"prettydiff 0.7.0",
"prettydiff",
"regex",
"rustc_version",
"rustfix 0.8.1",
"rustfix",
"serde",
"serde_json",
"spanned",
]
[[package]]
name = "ui_test"
version = "0.26.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32ee4c40e5a5f9fa6864ff976473e5d6a6e9884b6ce68b40690d9f87e1994c83"
dependencies = [
"annotate-snippets 0.11.4",
"anyhow",
"bstr",
"cargo-platform",
"cargo_metadata 0.18.1",
"color-eyre",
"colored",
"comma",
"crossbeam-channel",
"indicatif",
"levenshtein",
"prettydiff",
"regex",
"rustc_version",
"rustfix",
"serde",
"serde_json",
"spanned",

View File

@ -138,25 +138,8 @@ pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::Tok
// with a lowercase as rustc errors do.
err.replace_range(0..1, &err.chars().next().unwrap().to_lowercase().to_string());
let line_starts: Vec<usize> = std::iter::once(0)
.chain(
this.source()
.char_indices()
.filter_map(|(i, c)| Some(i + 1).filter(|_| c == '\n')),
)
.collect();
let line_start = line_starts
.iter()
.enumerate()
.map(|(line, idx)| (line + 1, idx))
.filter(|(_, idx)| **idx <= pos.start)
.last()
.unwrap()
.0;
let message = annotate_snippets::Level::Error.title(&err).snippet(
Snippet::source(this.source())
.line_start(line_start)
.origin(&relative_ftl_path)
.fold(true)
.annotation(annotate_snippets::Level::Error.span(pos.start..pos.end - 1)),

View File

@ -1211,11 +1211,7 @@ struct LLVMRustThinLTOData {
// Not 100% sure what these are, but they impact what's internalized and
// what's inlined across modules, I believe.
#if LLVM_VERSION_GE(18, 0)
#if LLVM_VERSION_GE(20, 0)
FunctionImporter::ImportListsTy ImportLists;
#else
DenseMap<StringRef, FunctionImporter::ImportMapTy> ImportLists;
#endif
DenseMap<StringRef, FunctionImporter::ExportSetTy> ExportLists;
DenseMap<StringRef, GVSummaryMapTy> ModuleToDefinedGVSummaries;
#else
@ -1424,13 +1420,13 @@ LLVMRustPrepareThinLTOInternalize(const LLVMRustThinLTOData *Data,
return true;
}
extern "C" bool LLVMRustPrepareThinLTOImport(LLVMRustThinLTOData *Data,
extern "C" bool LLVMRustPrepareThinLTOImport(const LLVMRustThinLTOData *Data,
LLVMModuleRef M,
LLVMTargetMachineRef TM) {
Module &Mod = *unwrap(M);
TargetMachine &Target = *unwrap(TM);
const auto &ImportList = Data->ImportLists[Mod.getModuleIdentifier()];
const auto &ImportList = Data->ImportLists.lookup(Mod.getModuleIdentifier());
auto Loader = [&](StringRef Identifier) {
const auto &Memory = Data->ModuleMap.lookup(Identifier);
auto &Context = Mod.getContext();
@ -1613,7 +1609,7 @@ extern "C" void LLVMRustComputeLTOCacheKey(RustStringRef KeyOut,
LLVMRustThinLTOData *Data) {
SmallString<40> Key;
llvm::lto::Config conf;
const auto &ImportList = Data->ImportLists[ModId];
const auto &ImportList = Data->ImportLists.lookup(ModId);
const auto &ExportList = Data->ExportLists.lookup(ModId);
const auto &ResolvedODR = Data->ResolvedODR.lookup(ModId);
const auto &DefinedGlobals = Data->ModuleToDefinedGVSummaries.lookup(ModId);

View File

@ -460,42 +460,7 @@ pub fn new_cyclic<F>(data_fn: F) -> Rc<T>
where
F: FnOnce(&Weak<T>) -> T,
{
// Construct the inner in the "uninitialized" state with a single
// weak reference.
let uninit_ptr: NonNull<_> = Box::leak(Box::new(RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: mem::MaybeUninit::<T>::uninit(),
}))
.into();
let init_ptr: NonNull<RcBox<T>> = uninit_ptr.cast();
let weak = Weak { ptr: init_ptr, alloc: Global };
// It's important we don't give up ownership of the weak pointer, or
// else the memory might be freed by the time `data_fn` returns. If
// we really wanted to pass ownership, we could create an additional
// weak pointer for ourselves, but this would result in additional
// updates to the weak reference count which might not be necessary
// otherwise.
let data = data_fn(&weak);
let strong = unsafe {
let inner = init_ptr.as_ptr();
ptr::write(ptr::addr_of_mut!((*inner).value), data);
let prev_value = (*inner).strong.get();
debug_assert_eq!(prev_value, 0, "No prior strong references should exist");
(*inner).strong.set(1);
Rc::from_inner(init_ptr)
};
// Strong references should collectively own a shared weak reference,
// so don't run the destructor for our old weak reference.
mem::forget(weak);
strong
Self::new_cyclic_in(data_fn, Global)
}
/// Constructs a new `Rc` with uninitialized contents.
@ -762,6 +727,84 @@ pub fn new_zeroed_in(alloc: A) -> Rc<mem::MaybeUninit<T>, A> {
}
}
/// Constructs a new `Rc<T, A>` in the given allocator while giving you a `Weak<T, A>` to the allocation,
/// to allow you to construct a `T` which holds a weak pointer to itself.
///
/// Generally, a structure circularly referencing itself, either directly or
/// indirectly, should not hold a strong reference to itself to prevent a memory leak.
/// Using this function, you get access to the weak pointer during the
/// initialization of `T`, before the `Rc<T, A>` is created, such that you can
/// clone and store it inside the `T`.
///
/// `new_cyclic_in` first allocates the managed allocation for the `Rc<T, A>`,
/// then calls your closure, giving it a `Weak<T, A>` to this allocation,
/// and only afterwards completes the construction of the `Rc<T, A>` by placing
/// the `T` returned from your closure into the allocation.
///
/// Since the new `Rc<T, A>` is not fully-constructed until `Rc<T, A>::new_cyclic_in`
/// returns, calling [`upgrade`] on the weak reference inside your closure will
/// fail and result in a `None` value.
///
/// # Panics
///
/// If `data_fn` panics, the panic is propagated to the caller, and the
/// temporary [`Weak<T, A>`] is dropped normally.
///
/// # Examples
///
/// See [`new_cyclic`].
///
/// [`new_cyclic`]: Rc::new_cyclic
/// [`upgrade`]: Weak::upgrade
#[cfg(not(no_global_oom_handling))]
#[unstable(feature = "allocator_api", issue = "32838")]
pub fn new_cyclic_in<F>(data_fn: F, alloc: A) -> Rc<T, A>
where
F: FnOnce(&Weak<T, A>) -> T,
{
// Construct the inner in the "uninitialized" state with a single
// weak reference.
let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in(
RcBox {
strong: Cell::new(0),
weak: Cell::new(1),
value: mem::MaybeUninit::<T>::uninit(),
},
alloc,
));
let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into();
let init_ptr: NonNull<RcBox<T>> = uninit_ptr.cast();
let weak = Weak { ptr: init_ptr, alloc: alloc };
// It's important we don't give up ownership of the weak pointer, or
// else the memory might be freed by the time `data_fn` returns. If
// we really wanted to pass ownership, we could create an additional
// weak pointer for ourselves, but this would result in additional
// updates to the weak reference count which might not be necessary
// otherwise.
let data = data_fn(&weak);
let strong = unsafe {
let inner = init_ptr.as_ptr();
ptr::write(ptr::addr_of_mut!((*inner).value), data);
let prev_value = (*inner).strong.get();
debug_assert_eq!(prev_value, 0, "No prior strong references should exist");
(*inner).strong.set(1);
// Strong references should collectively own a shared weak reference,
// so don't run the destructor for our old weak reference.
// Calling into_raw_with_allocator has the double effect of giving us back the allocator,
// and forgetting the weak reference.
let alloc = weak.into_raw_with_allocator().1;
Rc::from_inner_in(init_ptr, alloc)
};
strong
}
/// Constructs a new `Rc<T>` in the provided allocator, returning an error if the allocation
/// fails
///

View File

@ -450,54 +450,7 @@ pub fn new_cyclic<F>(data_fn: F) -> Arc<T>
where
F: FnOnce(&Weak<T>) -> T,
{
// Construct the inner in the "uninitialized" state with a single
// weak reference.
let uninit_ptr: NonNull<_> = Box::leak(Box::new(ArcInner {
strong: atomic::AtomicUsize::new(0),
weak: atomic::AtomicUsize::new(1),
data: mem::MaybeUninit::<T>::uninit(),
}))
.into();
let init_ptr: NonNull<ArcInner<T>> = uninit_ptr.cast();
let weak = Weak { ptr: init_ptr, alloc: Global };
// It's important we don't give up ownership of the weak pointer, or
// else the memory might be freed by the time `data_fn` returns. If
// we really wanted to pass ownership, we could create an additional
// weak pointer for ourselves, but this would result in additional
// updates to the weak reference count which might not be necessary
// otherwise.
let data = data_fn(&weak);
// Now we can properly initialize the inner value and turn our weak
// reference into a strong reference.
let strong = unsafe {
let inner = init_ptr.as_ptr();
ptr::write(ptr::addr_of_mut!((*inner).data), data);
// The above write to the data field must be visible to any threads which
// observe a non-zero strong count. Therefore we need at least "Release" ordering
// in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`.
//
// "Acquire" ordering is not required. When considering the possible behaviours
// of `data_fn` we only need to look at what it could do with a reference to a
// non-upgradeable `Weak`:
// - It can *clone* the `Weak`, increasing the weak reference count.
// - It can drop those clones, decreasing the weak reference count (but never to zero).
//
// These side effects do not impact us in any way, and no other side effects are
// possible with safe code alone.
let prev_value = (*inner).strong.fetch_add(1, Release);
debug_assert_eq!(prev_value, 0, "No prior strong references should exist");
Arc::from_inner(init_ptr)
};
// Strong references should collectively own a shared weak reference,
// so don't run the destructor for our old weak reference.
mem::forget(weak);
strong
Self::new_cyclic_in(data_fn, Global)
}
/// Constructs a new `Arc` with uninitialized contents.
@ -781,6 +734,98 @@ pub fn new_zeroed_in(alloc: A) -> Arc<mem::MaybeUninit<T>, A> {
}
}
/// Constructs a new `Arc<T, A>` in the given allocator while giving you a `Weak<T, A>` to the allocation,
/// to allow you to construct a `T` which holds a weak pointer to itself.
///
/// Generally, a structure circularly referencing itself, either directly or
/// indirectly, should not hold a strong reference to itself to prevent a memory leak.
/// Using this function, you get access to the weak pointer during the
/// initialization of `T`, before the `Arc<T, A>` is created, such that you can
/// clone and store it inside the `T`.
///
/// `new_cyclic_in` first allocates the managed allocation for the `Arc<T, A>`,
/// then calls your closure, giving it a `Weak<T, A>` to this allocation,
/// and only afterwards completes the construction of the `Arc<T, A>` by placing
/// the `T` returned from your closure into the allocation.
///
/// Since the new `Arc<T, A>` is not fully-constructed until `Arc<T, A>::new_cyclic_in`
/// returns, calling [`upgrade`] on the weak reference inside your closure will
/// fail and result in a `None` value.
///
/// # Panics
///
/// If `data_fn` panics, the panic is propagated to the caller, and the
/// temporary [`Weak<T>`] is dropped normally.
///
/// # Example
///
/// See [`new_cyclic`]
///
/// [`new_cyclic`]: Arc::new_cyclic
/// [`upgrade`]: Weak::upgrade
#[cfg(not(no_global_oom_handling))]
#[inline]
#[unstable(feature = "allocator_api", issue = "32838")]
pub fn new_cyclic_in<F>(data_fn: F, alloc: A) -> Arc<T, A>
where
F: FnOnce(&Weak<T, A>) -> T,
{
// Construct the inner in the "uninitialized" state with a single
// weak reference.
let (uninit_raw_ptr, alloc) = Box::into_raw_with_allocator(Box::new_in(
ArcInner {
strong: atomic::AtomicUsize::new(0),
weak: atomic::AtomicUsize::new(1),
data: mem::MaybeUninit::<T>::uninit(),
},
alloc,
));
let uninit_ptr: NonNull<_> = (unsafe { &mut *uninit_raw_ptr }).into();
let init_ptr: NonNull<ArcInner<T>> = uninit_ptr.cast();
let weak = Weak { ptr: init_ptr, alloc: alloc };
// It's important we don't give up ownership of the weak pointer, or
// else the memory might be freed by the time `data_fn` returns. If
// we really wanted to pass ownership, we could create an additional
// weak pointer for ourselves, but this would result in additional
// updates to the weak reference count which might not be necessary
// otherwise.
let data = data_fn(&weak);
// Now we can properly initialize the inner value and turn our weak
// reference into a strong reference.
let strong = unsafe {
let inner = init_ptr.as_ptr();
ptr::write(ptr::addr_of_mut!((*inner).data), data);
// The above write to the data field must be visible to any threads which
// observe a non-zero strong count. Therefore we need at least "Release" ordering
// in order to synchronize with the `compare_exchange_weak` in `Weak::upgrade`.
//
// "Acquire" ordering is not required. When considering the possible behaviours
// of `data_fn` we only need to look at what it could do with a reference to a
// non-upgradeable `Weak`:
// - It can *clone* the `Weak`, increasing the weak reference count.
// - It can drop those clones, decreasing the weak reference count (but never to zero).
//
// These side effects do not impact us in any way, and no other side effects are
// possible with safe code alone.
let prev_value = (*inner).strong.fetch_add(1, Release);
debug_assert_eq!(prev_value, 0, "No prior strong references should exist");
// Strong references should collectively own a shared weak reference,
// so don't run the destructor for our old weak reference.
// Calling into_raw_with_allocator has the double effect of giving us back the allocator,
// and forgetting the weak reference.
let alloc = weak.into_raw_with_allocator().1;
Arc::from_inner_in(init_ptr, alloc)
};
strong
}
/// Constructs a new `Pin<Arc<T, A>>` in the provided allocator. If `T` does not implement `Unpin`,
/// then `data` will be pinned in memory and unable to be moved.
#[cfg(not(no_global_oom_handling))]

View File

@ -8,7 +8,8 @@ Environment (TEE) for Android.
## Target maintainers
- Nicole LeGare (@randomPoison)
- Stephen Crane (@rinon)
- Andrei Homescu (@ahomescu)
- Chris Wailes (chriswailes@google.com)
- As a fallback trusty-dev-team@google.com can be contacted
## Requirements

View File

@ -13,7 +13,7 @@ jobs:
name: Build the sysroots
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Build the sysroots
run: |
cargo install -f rustup-toolchain-install-master

View File

@ -39,22 +39,19 @@ dependencies = [
[[package]]
name = "annotate-snippets"
version = "0.9.2"
version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccaf7e9dfbb6ab22c82e473cd1a8a7bd313c19a5b7e40970f3d89ef5a5c9e81e"
checksum = "24e35ed54e5ea7997c14ed4c70ba043478db1112e98263b3b035907aa197d991"
dependencies = [
"anstyle",
"unicode-width",
"yansi-term",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
name = "anstyle"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
[[package]]
name = "anyhow"
@ -126,9 +123,9 @@ dependencies = [
[[package]]
name = "cargo_metadata"
version = "0.15.4"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eee4243f1f26fc7a42710e7439c149e2b10b05472f88090acce52632f231a73a"
checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
dependencies = [
"camino",
"cargo-platform",
@ -737,11 +734,11 @@ dependencies = [
[[package]]
name = "prettydiff"
version = "0.6.4"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ff1fec61082821f8236cf6c0c14e8172b62ce8a72a0eedc30d3b247bb68dc11"
checksum = "abec3fb083c10660b3854367697da94c674e9e82aa7511014dc958beeb7215e9"
dependencies = [
"ansi_term",
"owo-colors",
"pad",
]
@ -865,14 +862,14 @@ dependencies = [
[[package]]
name = "rustfix"
version = "0.6.1"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481"
checksum = "70f5b7fc8060f4f8373f9381a630304b42e1183535d9beb1d3f596b236c9106a"
dependencies = [
"anyhow",
"log",
"serde",
"serde_json",
"thiserror",
"tracing",
]
[[package]]
@ -962,6 +959,16 @@ version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "spanned"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86af297923fbcfd107c20a189a6e9c872160df71a7190ae4a7a6c5dce4b2feb6"
dependencies = [
"bstr",
"color-eyre",
]
[[package]]
name = "syn"
version = "2.0.72"
@ -1065,9 +1072,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ui_test"
version = "0.21.2"
version = "0.26.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf4bf7c184b8dfc7a4d3b90df789b1eb992ee42811cd115f32a7a1eb781058d"
checksum = "32ee4c40e5a5f9fa6864ff976473e5d6a6e9884b6ce68b40690d9f87e1994c83"
dependencies = [
"annotate-snippets",
"anyhow",
@ -1079,7 +1086,6 @@ dependencies = [
"comma",
"crossbeam-channel",
"indicatif",
"lazy_static",
"levenshtein",
"prettydiff",
"regex",
@ -1087,7 +1093,7 @@ dependencies = [
"rustfix",
"serde",
"serde_json",
"tempfile",
"spanned",
]
[[package]]
@ -1120,28 +1126,6 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
@ -1281,15 +1265,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "yansi-term"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
dependencies = [
"winapi",
]
[[package]]
name = "zerocopy"
version = "0.7.35"

View File

@ -49,7 +49,7 @@ windows-sys = { version = "0.52", features = [
[dev-dependencies]
colored = "2"
ui_test = "0.21.1"
ui_test = "0.26.5"
rustc_version = "0.4"
regex = "1.5.5"
tempfile = "3"

View File

@ -187,7 +187,7 @@ Here is an example job for GitHub Actions:
name: "Miri"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Install Miri
run: |
rustup toolchain install nightly --component miri
@ -216,9 +216,9 @@ degree documented below):
- For every other target with OS `linux`, `macos`, or `windows`, Miri should generally work, but we
make no promises and we don't run tests for such targets.
- We have unofficial support (not maintained by the Miri team itself) for some further operating systems.
- `solaris` / `illumos`: maintained by @devnexen. Supports `std::{env, thread, sync}`, but not `std::fs`.
- `freebsd`: **maintainer wanted**. Supports `std::env` and parts of `std::{thread, fs}`, but not `std::sync`.
- `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works.
- `solaris` / `illumos`: maintained by @devnexen. Support very incomplete, but a basic "hello world" works.
- `wasm`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works.
- For targets on other operating systems, Miri might fail before even reaching the `main` function.

View File

@ -26,7 +26,7 @@ time ./miri install
# We enable all features to make sure the Stacked Borrows consistency check runs.
echo "Building debug version of Miri"
export CARGO_EXTRA_FLAGS="$CARGO_EXTRA_FLAGS --all-features"
time ./miri build --all-targets # the build that all the `./miri test` below will use
time ./miri build # the build that all the `./miri test` below will use
endgroup
@ -66,7 +66,7 @@ function run_tests {
time MIRIFLAGS="${MIRIFLAGS-} -O -Zmir-opt-level=4 -Cdebug-assertions=yes" MIRI_SKIP_UI_CHECKS=1 ./miri test $TARGET_FLAG tests/{pass,panic}
fi
if [ -n "${MANY_SEEDS-}" ]; then
# Also run some many-seeds tests.
# Also run some many-seeds tests. (Also tests `./miri run`.)
time for FILE in tests/many-seeds/*.rs; do
./miri run "--many-seeds=0..$MANY_SEEDS" $TARGET_FLAG "$FILE"
done
@ -75,6 +75,8 @@ function run_tests {
# Check that the benchmarks build and run, but only once.
time HYPERFINE="hyperfine -w0 -r1" ./miri bench $TARGET_FLAG
fi
# Smoke-test `./miri run --dep`.
./miri run $TARGET_FLAG --dep tests/pass-dep/getrandom.rs
## test-cargo-miri
# On Windows, there is always "python", not "python3" or "python2".
@ -148,11 +150,11 @@ case $HOST_TARGET in
# Partially supported targets (tier 2)
BASIC="empty_main integer vec string btreemap hello hashmap heap_alloc align" # ensures we have the basics: stdout/stderr, system allocator, randomness (for HashMap initialization)
UNIX="panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX threadname pthread-sync available-parallelism libc-time tls
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX threadname pthread-sync available-parallelism libc-time tls
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX
TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname pthread time fs
TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname pthread time fs
TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX thread sync available-parallelism time tls
TEST_TARGET=x86_64-pc-solaris run_tests_minimal $BASIC $UNIX thread sync available-parallelism time tls
TEST_TARGET=aarch64-linux-android run_tests_minimal $BASIC $UNIX pthread --skip threadname --skip pthread_cond_timedwait
TEST_TARGET=wasm32-wasip2 run_tests_minimal empty_main wasm heap_alloc libc-mem
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal empty_main wasm
TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std

View File

@ -61,9 +61,9 @@ checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a"
[[package]]
name = "errno"
version = "0.3.8"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys 0.52.0",
@ -98,6 +98,12 @@ dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "libc"
version = "0.2.153"
@ -117,9 +123,15 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
version = "0.4.13"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miri-script"
@ -131,6 +143,7 @@ dependencies = [
"itertools",
"path_macro",
"rustc_version",
"serde_json",
"shell-words",
"walkdir",
"which",
@ -204,9 +217,9 @@ dependencies = [
[[package]]
name = "rustix"
version = "0.38.31"
version = "0.38.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949"
checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
dependencies = [
"bitflags 2.4.2",
"errno",
@ -215,6 +228,12 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]]
name = "same-file"
version = "1.0.6"
@ -230,6 +249,38 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca"
[[package]]
name = "serde"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.128"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "shell-words"
version = "1.1.0"
@ -347,7 +398,7 @@ version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.3",
"windows-targets 0.52.6",
]
[[package]]
@ -367,17 +418,18 @@ dependencies = [
[[package]]
name = "windows-targets"
version = "0.52.3"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d380ba1dc7187569a8a9e91ed34b8ccfc33123bbacb8c0aed2d1ad7f3ef2dc5f"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.3",
"windows_aarch64_msvc 0.52.3",
"windows_i686_gnu 0.52.3",
"windows_i686_msvc 0.52.3",
"windows_x86_64_gnu 0.52.3",
"windows_x86_64_gnullvm 0.52.3",
"windows_x86_64_msvc 0.52.3",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
@ -388,9 +440,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.3"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68e5dcfb9413f53afd9c8f86e56a7b4d86d9a2fa26090ea2dc9e40fba56c6ec6"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
@ -400,9 +452,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.3"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dab469ebbc45798319e69eebf92308e541ce46760b49b18c6b3fe5e8965b30f"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
@ -412,9 +464,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.3"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a4e9b6a7cac734a8b4138a4e1044eac3404d8326b6c0f939276560687a033fb"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
@ -424,9 +482,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.3"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b0ec9c422ca95ff34a78755cfa6ad4a51371da2a5ace67500cf7ca5f232c58"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
@ -436,9 +494,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.3"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704131571ba93e89d7cd43482277d6632589b18ecf4468f591fbae0a8b101614"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
@ -448,9 +506,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.3"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42079295511643151e98d61c38c0acc444e52dd42ab456f7ccfd5152e8ecf21c"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
@ -460,9 +518,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.3"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0770833d60a970638e989b3fa9fd2bb1aaadcf88963d1659fd7d9990196ed2d6"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "xshell"

View File

@ -23,3 +23,4 @@ xshell = "0.2.6"
rustc_version = "0.4"
dunce = "1.0.4"
directories = "5"
serde_json = "1"

View File

@ -494,23 +494,25 @@ fn run(
flags: Vec<String>,
) -> Result<()> {
let mut e = MiriEnv::new()?;
// Preparation: get a sysroot, and get the miri binary.
let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
let miri_bin =
e.build_get_binary(".").context("failed to get filename of miri executable")?;
// More flags that we will pass before `flags`
// (because `flags` may contain `--`).
let mut early_flags = Vec::<OsString>::new();
// Add target, edition to flags.
if let Some(target) = &target {
early_flags.push("--target".into());
early_flags.push(target.into());
}
if verbose {
early_flags.push("--verbose".into());
// In `dep` mode, the target is already passed via `MIRI_TEST_TARGET`
if !dep {
if let Some(target) = &target {
early_flags.push("--target".into());
early_flags.push(target.into());
}
}
early_flags.push("--edition".into());
early_flags.push(edition.as_deref().unwrap_or("2021").into());
// Prepare a sysroot, add it to the flags. (Also builds cargo-miri, which we need.)
let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
early_flags.push("--sysroot".into());
early_flags.push(miri_sysroot.into());
@ -523,18 +525,19 @@ fn run(
let run_miri = |e: &MiriEnv, seed_flag: Option<String>| -> Result<()> {
// The basic command that executes the Miri driver.
let mut cmd = if dep {
// We invoke the test suite as that has all the logic for running with dependencies.
e.cargo_cmd(".", "test")
.args(&["--test", "ui"])
.args(quiet_flag)
.arg("--")
.args(&["--miri-run-dep-mode"])
} else {
e.cargo_cmd(".", "run").args(quiet_flag).arg("--")
cmd!(e.sh, "{miri_bin}")
};
cmd.set_quiet(!verbose);
// Add Miri flags
let mut cmd = cmd.args(&miri_flags).args(&seed_flag).args(&early_flags).args(&flags);
// For `--dep` we also need to set the env var.
// For `--dep` we also need to set the target in the env var.
if dep {
if let Some(target) = &target {
cmd = cmd.env("MIRI_TEST_TARGET", target);

View File

@ -1,10 +1,11 @@
use std::ffi::{OsStr, OsString};
use std::io::BufRead;
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
use std::thread;
use std::{env, iter, thread};
use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, bail, Context, Result};
use dunce::canonicalize;
use path_macro::path;
use xshell::{cmd, Cmd, Shell};
@ -73,8 +74,11 @@ pub fn new() -> Result<Self> {
let rustflags = {
let mut flags = OsString::new();
// We set the rpath so that Miri finds the private rustc libraries it needs.
flags.push("-C link-args=-Wl,-rpath,");
flags.push(libdir);
// (This only makes sense on Unix.)
if cfg!(unix) {
flags.push("-C link-args=-Wl,-rpath,");
flags.push(&libdir);
}
// Enable rustc-specific lints (ignored without `-Zunstable-options`).
flags.push(
" -Zunstable-options -Wrustc::internal -Wrust_2018_idioms -Wunused_lifetimes",
@ -88,6 +92,14 @@ pub fn new() -> Result<Self> {
};
sh.set_var("RUSTFLAGS", rustflags);
// On Windows, the `-Wl,-rpath,` above does not help. Instead we add the libdir to the PATH,
// so that Windows can find the DLLs.
if cfg!(windows) {
let old_path = sh.var("PATH")?;
let new_path = env::join_paths(iter::once(libdir).chain(env::split_paths(&old_path)))?;
sh.set_var("PATH", new_path);
}
// Get extra flags for cargo.
let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default();
let cargo_extra_flags = flagsplit(&cargo_extra_flags);
@ -126,21 +138,40 @@ pub fn install_to_sysroot(
pub fn build(&self, crate_dir: impl AsRef<OsStr>, args: &[String], quiet: bool) -> Result<()> {
let quiet_flag = if quiet { Some("--quiet") } else { None };
// We build the tests as well, (a) to avoid having rebuilds when building the tests later
// and (b) to have more parallelism during the build of Miri and its tests.
// This means `./miri run` without `--dep` will build Miri twice (for the sysroot with
// dev-dependencies, and then for running without dev-dependencies), but the way more common
// `./miri test` will avoid building Miri twice.
let mut cmd = self
.cargo_cmd(crate_dir, "build")
.args(&["--bins", "--tests"])
.args(quiet_flag)
.args(args);
// We build all targets, since building *just* the bin target doesnot include
// `dev-dependencies` and that changes feature resolution. This also gets us more
// parallelism in `./miri test` as we build Miri and its tests together.
let mut cmd =
self.cargo_cmd(crate_dir, "build").args(&["--all-targets"]).args(quiet_flag).args(args);
cmd.set_quiet(quiet);
cmd.run()?;
Ok(())
}
/// Returns the path to the main crate binary. Assumes that `build` has been called before.
pub fn build_get_binary(&self, crate_dir: impl AsRef<OsStr>) -> Result<PathBuf> {
let cmd =
self.cargo_cmd(crate_dir, "build").args(&["--all-targets", "--message-format=json"]);
let output = cmd.output()?;
let mut bin = None;
for line in output.stdout.lines() {
let line = line?;
if line.starts_with("{") {
let json: serde_json::Value = serde_json::from_str(&line)?;
if json["reason"] == "compiler-artifact"
&& !json["profile"]["test"].as_bool().unwrap()
&& !json["executable"].is_null()
{
if bin.is_some() {
bail!("found two binaries in cargo output");
}
bin = Some(PathBuf::from(json["executable"].as_str().unwrap()))
}
}
}
bin.ok_or_else(|| anyhow!("found no binary in cargo output"))
}
pub fn check(&self, crate_dir: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
self.cargo_cmd(crate_dir, "check").arg("--all-targets").args(args).run()?;
Ok(())

View File

@ -1 +1 @@
54fdef7799d9ff9470bb5cabd29fde9471a99eaa
e2dc1a1c0f97a90319181a721ab317210307617a

View File

@ -1165,7 +1165,7 @@ pub fn local_write(&self, local: mir::Local, storage_live: bool, machine: &MiriM
} else {
// This can fail to exist if `race_detecting` was false when the allocation
// occurred, in which case we can backdate this to the beginning of time.
let clocks = clocks.entry(local).or_insert_with(Default::default);
let clocks = clocks.entry(local).or_default();
clocks.write = thread_clocks.clock[index];
clocks.write_type = NaWriteType::Write;
}
@ -1186,7 +1186,7 @@ pub fn local_read(&self, local: mir::Local, machine: &MiriMachine<'_>) {
// This can fail to exist if `race_detecting` was false when the allocation
// occurred, in which case we can backdate this to the beginning of time.
let mut clocks = self.local_clocks.borrow_mut();
let clocks = clocks.entry(local).or_insert_with(Default::default);
let clocks = clocks.entry(local).or_default();
clocks.read = thread_clocks.clock[index];
}

View File

@ -1,7 +1,6 @@
use std::collections::VecDeque;
use rustc_index::Idx;
use rustc_middle::ty::layout::TyAndLayout;
use super::sync::EvalContextExtPriv as _;
use super::vector_clock::VClock;
@ -30,14 +29,12 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn init_once_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut();
this.get_or_create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.init_onces,
|_| Ok(Default::default()),

View File

@ -1,10 +1,11 @@
use std::any::Any;
use std::collections::{hash_map::Entry, VecDeque};
use std::ops::Not;
use std::time::Duration;
use rustc_data_structures::fx::FxHashMap;
use rustc_index::{Idx, IndexVec};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_target::abi::Size;
use super::init_once::InitOnce;
use super::vector_clock::VClock;
@ -66,27 +67,6 @@ pub fn to_u32_scalar(&self) -> Scalar {
declare_id!(MutexId);
/// The mutex kind.
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum MutexKind {
Invalid,
Normal,
Default,
Recursive,
ErrorCheck,
}
#[derive(Debug)]
/// Additional data that may be used by shim implementations.
pub struct AdditionalMutexData {
/// The mutex kind, used by some mutex implementations like pthreads mutexes.
pub kind: MutexKind,
/// The address of the mutex.
pub address: u64,
}
/// The mutex state.
#[derive(Default, Debug)]
struct Mutex {
@ -100,7 +80,7 @@ struct Mutex {
clock: VClock,
/// Additional data that can be set by shim implementations.
data: Option<AdditionalMutexData>,
data: Option<Box<dyn Any>>,
}
declare_id!(RwLockId);
@ -137,6 +117,9 @@ struct RwLock {
/// locks.
/// This is only relevant when there is an active reader.
clock_current_readers: VClock,
/// Additional data that can be set by shim implementations.
data: Option<Box<dyn Any>>,
}
declare_id!(CondvarId);
@ -151,6 +134,9 @@ struct Condvar {
/// Contains the clock of the last thread to
/// perform a condvar-signal.
clock: VClock,
/// Additional data that can be set by shim implementations.
data: Option<Box<dyn Any>>,
}
/// The futex state.
@ -196,21 +182,21 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline]
fn get_or_create_id<Id: SyncId + Idx, T>(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
create_obj: impl for<'a> FnOnce(&'a mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
) -> InterpResult<'tcx, Option<Id>> {
let this = self.eval_context_mut();
let value_place =
this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
let offset = Size::from_bytes(offset);
assert!(lock.layout.size >= offset + this.machine.layouts.u32.size);
let id_place = lock.offset(offset, this.machine.layouts.u32, this)?;
let next_index = get_objs(this).next_index();
// Since we are lazy, this update has to be atomic.
let (old, success) = this
.atomic_compare_exchange_scalar(
&value_place,
&id_place,
&ImmTy::from_uint(0u32, this.machine.layouts.u32),
Scalar::from_u32(next_index.to_u32()),
AtomicRwOrd::Relaxed, // deliberately *no* synchronization
@ -248,18 +234,18 @@ fn get_or_create_id<Id: SyncId + Idx, T>(
/// - `obj` must be the new sync object.
fn create_id<Id: SyncId + Idx, T>(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
obj: T,
) -> InterpResult<'tcx, Id> {
let this = self.eval_context_mut();
let value_place =
this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
let offset = Size::from_bytes(offset);
assert!(lock.layout.size >= offset + this.machine.layouts.u32.size);
let id_place = lock.offset(offset, this.machine.layouts.u32, this)?;
let new_index = get_objs(this).push(obj);
this.write_scalar(Scalar::from_u32(new_index.to_u32()), &value_place)?;
this.write_scalar(Scalar::from_u32(new_index.to_u32()), &id_place)?;
Ok(new_index)
}
@ -292,15 +278,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Eagerly create and initialize a new mutex.
fn mutex_create(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
data: Option<AdditionalMutexData>,
data: Option<Box<dyn Any>>,
) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
this.create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.mutexes,
Mutex { data, ..Default::default() },
@ -311,17 +295,15 @@ fn mutex_create(
/// `initialize_data` must return any additional data that a user wants to associate with the mutex.
fn mutex_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
initialize_data: impl for<'a> FnOnce(
&'a mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Option<AdditionalMutexData>>,
) -> InterpResult<'tcx, Option<Box<dyn Any>>>,
) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
this.get_or_create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.mutexes,
|ecx| initialize_data(ecx).map(|data| Mutex { data, ..Default::default() }),
@ -330,48 +312,84 @@ fn mutex_get_or_create_id(
}
/// Retrieve the additional data stored for a mutex.
fn mutex_get_data<'a>(&'a mut self, id: MutexId) -> Option<&'a AdditionalMutexData>
fn mutex_get_data<'a, T: 'static>(&'a mut self, id: MutexId) -> Option<&'a T>
where
'tcx: 'a,
{
let this = self.eval_context_ref();
this.machine.sync.mutexes[id].data.as_ref()
this.machine.sync.mutexes[id].data.as_deref().and_then(|p| p.downcast_ref::<T>())
}
fn rwlock_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
initialize_data: impl for<'a> FnOnce(
&'a mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Option<Box<dyn Any>>>,
) -> InterpResult<'tcx, RwLockId> {
let this = self.eval_context_mut();
this.get_or_create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.rwlocks,
|_| Ok(Default::default()),
|ecx| initialize_data(ecx).map(|data| RwLock { data, ..Default::default() }),
)?
.ok_or_else(|| err_ub_format!("rwlock has invalid ID").into())
}
/// Retrieve the additional data stored for a rwlock.
fn rwlock_get_data<'a, T: 'static>(&'a mut self, id: RwLockId) -> Option<&'a T>
where
'tcx: 'a,
{
let this = self.eval_context_ref();
this.machine.sync.rwlocks[id].data.as_deref().and_then(|p| p.downcast_ref::<T>())
}
/// Eagerly create and initialize a new condvar.
fn condvar_create(
&mut self,
condvar: &MPlaceTy<'tcx>,
offset: u64,
data: Option<Box<dyn Any>>,
) -> InterpResult<'tcx, CondvarId> {
let this = self.eval_context_mut();
this.create_id(
condvar,
offset,
|ecx| &mut ecx.machine.sync.condvars,
Condvar { data, ..Default::default() },
)
}
fn condvar_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>,
lock: &MPlaceTy<'tcx>,
offset: u64,
initialize_data: impl for<'a> FnOnce(
&'a mut MiriInterpCx<'tcx>,
) -> InterpResult<'tcx, Option<Box<dyn Any>>>,
) -> InterpResult<'tcx, CondvarId> {
let this = self.eval_context_mut();
this.get_or_create_id(
lock_op,
lock_layout,
lock,
offset,
|ecx| &mut ecx.machine.sync.condvars,
|_| Ok(Default::default()),
|ecx| initialize_data(ecx).map(|data| Condvar { data, ..Default::default() }),
)?
.ok_or_else(|| err_ub_format!("condvar has invalid ID").into())
}
/// Retrieve the additional data stored for a condvar.
fn condvar_get_data<'a, T: 'static>(&'a mut self, id: CondvarId) -> Option<&'a T>
where
'tcx: 'a,
{
let this = self.eval_context_ref();
this.machine.sync.condvars[id].data.as_deref().and_then(|p| p.downcast_ref::<T>())
}
#[inline]
/// Get the id of the thread that currently owns this lock.
fn mutex_get_owner(&mut self, id: MutexId) -> ThreadId {

View File

@ -133,10 +133,7 @@
cpu_affinity::MAX_CPUS,
data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
init_once::{EvalContextExt as _, InitOnceId},
sync::{
AdditionalMutexData, CondvarId, EvalContextExt as _, MutexId, MutexKind, RwLockId,
SynchronizationObjects,
},
sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects},
thread::{
BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager,
TimeoutAnchor, TimeoutClock, UnblockCallback,

View File

@ -602,12 +602,8 @@ pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx>) -> Self {
let layouts =
PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
let profiler = config.measureme_out.as_ref().map(|out| {
let crate_name = tcx
.sess
.opts
.crate_name
.clone()
.unwrap_or_else(|| "unknown-crate".to_string());
let crate_name =
tcx.sess.opts.crate_name.clone().unwrap_or_else(|| "unknown-crate".to_string());
let pid = process::id();
// We adopt the same naming scheme for the profiler output that rustc uses. In rustc,
// the PID is padded so that the nondeterministic value of the PID does not spread

View File

@ -21,7 +21,7 @@ pub(crate) enum FlockOp {
Unlock,
}
/// Represents an open file descriptor.
/// Represents an open file description.
pub trait FileDescription: std::fmt::Debug + Any {
fn name(&self) -> &'static str;
@ -303,7 +303,7 @@ pub struct FdTable {
impl VisitProvenance for FdTable {
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
// All our FileDescriptor do not have any tags.
// All our FileDescription instances do not have any tags.
}
}
@ -337,18 +337,18 @@ pub fn insert_new(&mut self, fd: impl FileDescription) -> i32 {
}
pub fn insert(&mut self, fd_ref: FileDescriptionRef) -> i32 {
self.insert_ref_with_min_fd(fd_ref, 0)
self.insert_with_min_num(fd_ref, 0)
}
/// Insert a file description, giving it a file descriptor that is at least `min_fd`.
fn insert_ref_with_min_fd(&mut self, file_handle: FileDescriptionRef, min_fd: i32) -> i32 {
/// Insert a file description, giving it a file descriptor that is at least `min_fd_num`.
fn insert_with_min_num(&mut self, file_handle: FileDescriptionRef, min_fd_num: i32) -> i32 {
// Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
// between used FDs, the find_map combinator will return it. If the first such unused FD
// is after all other used FDs, the find_map combinator will return None, and we will use
// the FD following the greatest FD thus far.
let candidate_new_fd =
self.fds.range(min_fd..).zip(min_fd..).find_map(|((fd, _fh), counter)| {
if *fd != counter {
self.fds.range(min_fd_num..).zip(min_fd_num..).find_map(|((fd_num, _fd), counter)| {
if *fd_num != counter {
// There was a gap in the fds stored, return the first unused one
// (note that this relies on BTreeMap iterating in key order)
Some(counter)
@ -357,61 +357,61 @@ fn insert_ref_with_min_fd(&mut self, file_handle: FileDescriptionRef, min_fd: i3
None
}
});
let new_fd = candidate_new_fd.unwrap_or_else(|| {
let new_fd_num = candidate_new_fd.unwrap_or_else(|| {
// find_map ran out of BTreeMap entries before finding a free fd, use one plus the
// maximum fd in the map
self.fds.last_key_value().map(|(fd, _)| fd.strict_add(1)).unwrap_or(min_fd)
self.fds.last_key_value().map(|(fd_num, _)| fd_num.strict_add(1)).unwrap_or(min_fd_num)
});
self.fds.try_insert(new_fd, file_handle).unwrap();
new_fd
self.fds.try_insert(new_fd_num, file_handle).unwrap();
new_fd_num
}
pub fn get(&self, fd: i32) -> Option<FileDescriptionRef> {
let fd = self.fds.get(&fd)?;
pub fn get(&self, fd_num: i32) -> Option<FileDescriptionRef> {
let fd = self.fds.get(&fd_num)?;
Some(fd.clone())
}
pub fn remove(&mut self, fd: i32) -> Option<FileDescriptionRef> {
self.fds.remove(&fd)
pub fn remove(&mut self, fd_num: i32) -> Option<FileDescriptionRef> {
self.fds.remove(&fd_num)
}
pub fn is_fd(&self, fd: i32) -> bool {
self.fds.contains_key(&fd)
pub fn is_fd_num(&self, fd_num: i32) -> bool {
self.fds.contains_key(&fd_num)
}
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn dup(&mut self, old_fd: i32) -> InterpResult<'tcx, Scalar> {
fn dup(&mut self, old_fd_num: i32) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let Some(dup_fd) = this.machine.fds.get(old_fd) else {
let Some(fd) = this.machine.fds.get(old_fd_num) else {
return Ok(Scalar::from_i32(this.fd_not_found()?));
};
Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, 0)))
Ok(Scalar::from_i32(this.machine.fds.insert(fd)))
}
fn dup2(&mut self, old_fd: i32, new_fd: i32) -> InterpResult<'tcx, Scalar> {
fn dup2(&mut self, old_fd_num: i32, new_fd_num: i32) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let Some(dup_fd) = this.machine.fds.get(old_fd) else {
let Some(fd) = this.machine.fds.get(old_fd_num) else {
return Ok(Scalar::from_i32(this.fd_not_found()?));
};
if new_fd != old_fd {
if new_fd_num != old_fd_num {
// Close new_fd if it is previously opened.
// If old_fd and new_fd point to the same description, then `dup_fd` ensures we keep the underlying file description alive.
if let Some(file_description) = this.machine.fds.fds.insert(new_fd, dup_fd) {
if let Some(old_new_fd) = this.machine.fds.fds.insert(new_fd_num, fd) {
// Ignore close error (not interpreter's) according to dup2() doc.
file_description.close(this.machine.communicate(), this)?.ok();
old_new_fd.close(this.machine.communicate(), this)?.ok();
}
}
Ok(Scalar::from_i32(new_fd))
Ok(Scalar::from_i32(new_fd_num))
}
fn flock(&mut self, fd: i32, op: i32) -> InterpResult<'tcx, Scalar> {
fn flock(&mut self, fd_num: i32, op: i32) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let Some(file_descriptor) = this.machine.fds.get(fd) else {
let Some(fd) = this.machine.fds.get(fd_num) else {
return Ok(Scalar::from_i32(this.fd_not_found()?));
};
@ -436,8 +436,8 @@ fn flock(&mut self, fd: i32, op: i32) -> InterpResult<'tcx, Scalar> {
throw_unsup_format!("unsupported flags {:#x}", op);
};
let result = file_descriptor.flock(this.machine.communicate(), parsed_op)?;
drop(file_descriptor);
let result = fd.flock(this.machine.communicate(), parsed_op)?;
drop(fd);
// return `0` if flock is successful
let result = result.map(|()| 0i32);
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
@ -452,7 +452,7 @@ fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
args.len()
);
}
let fd = this.read_scalar(&args[0])?.to_i32()?;
let fd_num = this.read_scalar(&args[0])?.to_i32()?;
let cmd = this.read_scalar(&args[1])?.to_i32()?;
// We only support getting the flags for a descriptor.
@ -461,7 +461,7 @@ fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
// `FD_CLOEXEC` value without checking if the flag is set for the file because `std`
// always sets this flag when opening a file. However we still need to check that the
// file itself is open.
Ok(Scalar::from_i32(if this.machine.fds.is_fd(fd) {
Ok(Scalar::from_i32(if this.machine.fds.is_fd_num(fd_num) {
this.eval_libc_i32("FD_CLOEXEC")
} else {
this.fd_not_found()?
@ -481,9 +481,8 @@ fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
}
let start = this.read_scalar(&args[2])?.to_i32()?;
match this.machine.fds.get(fd) {
Some(dup_fd) =>
Ok(Scalar::from_i32(this.machine.fds.insert_ref_with_min_fd(dup_fd, start))),
match this.machine.fds.get(fd_num) {
Some(fd) => Ok(Scalar::from_i32(this.machine.fds.insert_with_min_num(fd, start))),
None => Ok(Scalar::from_i32(this.fd_not_found()?)),
}
} else if this.tcx.sess.target.os == "macos" && cmd == this.eval_libc_i32("F_FULLFSYNC") {
@ -494,7 +493,7 @@ fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
return Ok(Scalar::from_i32(-1));
}
this.ffullsync_fd(fd)
this.ffullsync_fd(fd_num)
} else {
throw_unsup_format!("the {:#x} command is not supported for `fcntl`)", cmd);
}
@ -503,12 +502,12 @@ fn fcntl(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
fn close(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let fd = this.read_scalar(fd_op)?.to_i32()?;
let fd_num = this.read_scalar(fd_op)?.to_i32()?;
let Some(file_description) = this.machine.fds.remove(fd) else {
let Some(fd) = this.machine.fds.remove(fd_num) else {
return Ok(Scalar::from_i32(this.fd_not_found()?));
};
let result = file_description.close(this.machine.communicate(), this)?;
let result = fd.close(this.machine.communicate(), this)?;
// return `0` if close is successful
let result = result.map(|()| 0i32);
Ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
@ -532,16 +531,16 @@ fn fd_not_found<T: From<i32>>(&mut self) -> InterpResult<'tcx, T> {
/// and keeps the cursor unchanged.
fn read(
&mut self,
fd: i32,
fd_num: i32,
buf: Pointer,
count: u64,
offset: Option<i128>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait.
// Isolation check is done via `FileDescription` trait.
trace!("Reading from FD {}, size {}", fd, count);
trace!("Reading from FD {}, size {}", fd_num, count);
// Check that the *entire* buffer is actually valid memory.
this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccessTest)?;
@ -554,7 +553,7 @@ fn read(
let communicate = this.machine.communicate();
// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(fd) = this.machine.fds.get(fd) else {
let Some(fd) = this.machine.fds.get(fd_num) else {
trace!("read: FD not found");
return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
};
@ -597,14 +596,14 @@ fn read(
fn write(
&mut self,
fd: i32,
fd_num: i32,
buf: Pointer,
count: u64,
offset: Option<i128>,
) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait.
// Isolation check is done via `FileDescription` trait.
// Check that the *entire* buffer is actually valid memory.
this.check_ptr_access(buf, Size::from_bytes(count), CheckInAllocMsg::MemoryAccessTest)?;
@ -618,7 +617,7 @@ fn write(
let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
// We temporarily dup the FD to be able to retain mutable access to `this`.
let Some(fd) = this.machine.fds.get(fd) else {
let Some(fd) = this.machine.fds.get(fd_num) else {
return Ok(Scalar::from_target_isize(this.fd_not_found()?, this));
};

View File

@ -554,10 +554,10 @@ fn open(&mut self, args: &[OpTy<'tcx>]) -> InterpResult<'tcx, Scalar> {
Ok(Scalar::from_i32(this.try_unwrap_io_result(fd)?))
}
fn lseek64(&mut self, fd: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> {
fn lseek64(&mut self, fd_num: i32, offset: i128, whence: i32) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
// Isolation check is done via `FileDescriptor` trait.
// Isolation check is done via `FileDescription` trait.
let seek_from = if whence == this.eval_libc_i32("SEEK_SET") {
if offset < 0 {
@ -580,13 +580,11 @@ fn lseek64(&mut self, fd: i32, offset: i128, whence: i32) -> InterpResult<'tcx,
let communicate = this.machine.communicate();
let Some(file_description) = this.machine.fds.get(fd) else {
let Some(fd) = this.machine.fds.get(fd_num) else {
return Ok(Scalar::from_i64(this.fd_not_found()?));
};
let result = file_description
.seek(communicate, seek_from)?
.map(|offset| i64::try_from(offset).unwrap());
drop(file_description);
let result = fd.seek(communicate, seek_from)?.map(|offset| i64::try_from(offset).unwrap());
drop(fd);
let result = this.try_unwrap_io_result(result)?;
Ok(Scalar::from_i64(result))
@ -721,7 +719,7 @@ fn macos_fbsd_fstat(
return Ok(Scalar::from_i32(this.fd_not_found()?));
}
let metadata = match FileMetadata::from_fd(this, fd)? {
let metadata = match FileMetadata::from_fd_num(this, fd)? {
Some(metadata) => metadata,
None => return Ok(Scalar::from_i32(-1)),
};
@ -808,7 +806,7 @@ fn linux_statx(
// If the path is empty, and the AT_EMPTY_PATH flag is set, we query the open file
// represented by dirfd, whether it's a directory or otherwise.
let metadata = if path.as_os_str().is_empty() && empty_path_flag {
FileMetadata::from_fd(this, dirfd)?
FileMetadata::from_fd_num(this, dirfd)?
} else {
FileMetadata::from_path(this, &path, follow_symlink)?
};
@ -1260,7 +1258,7 @@ fn closedir(&mut self, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
}))
}
fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar> {
fn ftruncate64(&mut self, fd_num: i32, length: i128) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
// Reject if isolation is enabled.
@ -1270,30 +1268,29 @@ fn ftruncate64(&mut self, fd: i32, length: i128) -> InterpResult<'tcx, Scalar> {
return Ok(Scalar::from_i32(this.fd_not_found()?));
}
let Some(file_description) = this.machine.fds.get(fd) else {
let Some(fd) = this.machine.fds.get(fd_num) else {
return Ok(Scalar::from_i32(this.fd_not_found()?));
};
// FIXME: Support ftruncate64 for all FDs
let FileHandle { file, writable } =
file_description.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors")
})?;
let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!("`ftruncate64` is only supported on file-backed file descriptors")
})?;
if *writable {
if let Ok(length) = length.try_into() {
let result = file.set_len(length);
drop(file_description);
drop(fd);
let result = this.try_unwrap_io_result(result.map(|_| 0i32))?;
Ok(Scalar::from_i32(result))
} else {
drop(file_description);
drop(fd);
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
Ok(Scalar::from_i32(-1))
}
} else {
drop(file_description);
drop(fd);
// The file is not writable
let einval = this.eval_libc("EINVAL");
this.set_last_error(einval)?;
@ -1321,18 +1318,17 @@ fn fsync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
self.ffullsync_fd(fd)
}
fn ffullsync_fd(&mut self, fd: i32) -> InterpResult<'tcx, Scalar> {
fn ffullsync_fd(&mut self, fd_num: i32) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let Some(file_description) = this.machine.fds.get(fd) else {
let Some(fd) = this.machine.fds.get(fd_num) else {
return Ok(Scalar::from_i32(this.fd_not_found()?));
};
// Only regular files support synchronization.
let FileHandle { file, writable } =
file_description.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!("`fsync` is only supported on file-backed file descriptors")
})?;
let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!("`fsync` is only supported on file-backed file descriptors")
})?;
let io_result = maybe_sync_file(file, *writable, File::sync_all);
drop(file_description);
drop(fd);
Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?))
}
@ -1348,16 +1344,15 @@ fn fdatasync(&mut self, fd_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
return Ok(Scalar::from_i32(this.fd_not_found()?));
}
let Some(file_description) = this.machine.fds.get(fd) else {
let Some(fd) = this.machine.fds.get(fd) else {
return Ok(Scalar::from_i32(this.fd_not_found()?));
};
// Only regular files support synchronization.
let FileHandle { file, writable } =
file_description.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors")
})?;
let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!("`fdatasync` is only supported on file-backed file descriptors")
})?;
let io_result = maybe_sync_file(file, *writable, File::sync_data);
drop(file_description);
drop(fd);
Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?))
}
@ -1396,18 +1391,15 @@ fn sync_file_range(
return Ok(Scalar::from_i32(this.fd_not_found()?));
}
let Some(file_description) = this.machine.fds.get(fd) else {
let Some(fd) = this.machine.fds.get(fd) else {
return Ok(Scalar::from_i32(this.fd_not_found()?));
};
// Only regular files support synchronization.
let FileHandle { file, writable } =
file_description.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!(
"`sync_data_range` is only supported on file-backed file descriptors"
)
})?;
let FileHandle { file, writable } = fd.downcast::<FileHandle>().ok_or_else(|| {
err_unsup_format!("`sync_data_range` is only supported on file-backed file descriptors")
})?;
let io_result = maybe_sync_file(file, *writable, File::sync_data);
drop(file_description);
drop(fd);
Ok(Scalar::from_i32(this.try_unwrap_io_result(io_result)?))
}
@ -1699,15 +1691,15 @@ fn from_path<'tcx>(
FileMetadata::from_meta(ecx, metadata)
}
fn from_fd<'tcx>(
fn from_fd_num<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
fd: i32,
fd_num: i32,
) -> InterpResult<'tcx, Option<FileMetadata>> {
let Some(file_description) = ecx.machine.fds.get(fd) else {
let Some(fd) = ecx.machine.fds.get(fd_num) else {
return ecx.fd_not_found().map(|_: i32| None);
};
let file = &file_description
let file = &fd
.downcast::<FileHandle>()
.ok_or_else(|| {
err_unsup_format!(
@ -1717,7 +1709,7 @@ fn from_fd<'tcx>(
.file;
let metadata = file.metadata();
drop(file_description);
drop(fd);
FileMetadata::from_meta(ecx, metadata)
}

View File

@ -51,7 +51,10 @@ pub fn new(events: u32, data: u64) -> EpollEventInstance {
#[derive(Clone, Debug)]
pub struct EpollEventInterest {
/// The file descriptor value of the file description registered.
file_descriptor: i32,
/// This is only used for ready_list, to inform userspace which FD triggered an event.
/// For that, it is crucial to preserve the original FD number.
/// This FD number must never be "dereferenced" to a file description inside Miri.
fd_num: i32,
/// The events bitmask retrieved from `epoll_event`.
events: u32,
/// The data retrieved from `epoll_event`.
@ -61,8 +64,8 @@ pub struct EpollEventInterest {
data: u64,
/// Ready list of the epoll instance under which this EpollEventInterest is registered.
ready_list: Rc<RefCell<BTreeMap<(FdId, i32), EpollEventInstance>>>,
/// The file descriptor value that this EpollEventInterest is registered under.
epfd: i32,
/// The epoll file description that this EpollEventInterest is registered under.
weak_epfd: WeakFileDescriptionRef,
}
/// EpollReadyEvents reflects the readiness of a file description.
@ -339,11 +342,11 @@ fn epoll_ctl(
// Create an epoll_interest.
let interest = Rc::new(RefCell::new(EpollEventInterest {
file_descriptor: fd,
fd_num: fd,
events,
data,
ready_list: Rc::clone(ready_list),
epfd: epfd_value,
weak_epfd: epfd.downgrade(),
}));
if op == epoll_ctl_add {
@ -553,12 +556,12 @@ fn check_and_update_readiness(
if is_updated {
// Edge-triggered notification only notify one thread even if there are
// multiple threads block on the same epfd.
let epfd = this.machine.fds.get(epoll_interest.borrow().epfd).unwrap();
// This unwrap can never fail because if the current epoll instance were
// closed and its epfd value reused, the upgrade of weak_epoll_interest
// closed, the upgrade of weak_epoll_interest
// above would fail. This guarantee holds because only the epoll instance
// holds a strong ref to epoll_interest.
let epfd = epoll_interest.borrow().weak_epfd.upgrade().unwrap();
// FIXME: We can randomly pick a thread to unblock.
if let Some(thread_id) =
epfd.downcast::<Epoll>().unwrap().thread_id.borrow_mut().pop()
@ -615,7 +618,7 @@ fn check_and_update_one_event_interest<'tcx>(
// If there is any event that we are interested in being specified as ready,
// insert an epoll_return to the ready list.
if flags != 0 {
let epoll_key = (id, epoll_event_interest.file_descriptor);
let epoll_key = (id, epoll_event_interest.fd_num);
let ready_list = &mut epoll_event_interest.ready_list.borrow_mut();
let event_instance = EpollEventInstance::new(flags, epoll_event_interest.data);
// Triggers the notification by inserting it to the ready list.

View File

@ -14,12 +14,13 @@
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn os_unfair_lock_getid(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx, MutexId> {
fn os_unfair_lock_getid(&mut self, lock_ptr: &OpTy<'tcx>) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
let lock = this.deref_pointer(lock_ptr)?;
// os_unfair_lock holds a 32-bit value, is initialized with zero and
// must be assumed to be opaque. Therefore, we can just store our
// internal mutex ID in the structure without anyone noticing.
this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0, |_| Ok(None))
this.mutex_get_or_create_id(&lock, 0, |_| Ok(None))
}
}

View File

@ -42,10 +42,10 @@ fn mmap(
let map_shared = this.eval_libc_i32("MAP_SHARED");
let map_fixed = this.eval_libc_i32("MAP_FIXED");
// This is a horrible hack, but on MacOS and Solaris the guard page mechanism uses mmap
// This is a horrible hack, but on MacOS and Solarish the guard page mechanism uses mmap
// in a way we do not support. We just give it the return value it expects.
if this.frame_in_std()
&& matches!(&*this.tcx.sess.target.os, "macos" | "solaris")
&& matches!(&*this.tcx.sess.target.os, "macos" | "solaris" | "illumos")
&& (flags & map_fixed) != 0
{
return Ok(Scalar::from_maybe_pointer(Pointer::from_addr_invalid(addr), this));

View File

@ -11,17 +11,17 @@
#[inline]
fn mutexattr_kind_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
Ok(match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" | "macos" => 0,
"linux" | "illumos" | "solaris" | "macos" | "freebsd" | "android" => 0,
os => throw_unsup_format!("`pthread_mutexattr` is not supported on {os}"),
})
}
fn mutexattr_get_kind<'tcx>(
ecx: &MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
attr_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
attr_op,
attr_ptr,
mutexattr_kind_offset(ecx)?,
ecx.libc_ty_layout("pthread_mutexattr_t"),
ecx.machine.layouts.i32,
@ -31,11 +31,11 @@ fn mutexattr_get_kind<'tcx>(
fn mutexattr_set_kind<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
attr_ptr: &OpTy<'tcx>,
kind: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
attr_op,
attr_ptr,
mutexattr_kind_offset(ecx)?,
Scalar::from_i32(kind),
ecx.libc_ty_layout("pthread_mutexattr_t"),
@ -43,29 +43,40 @@ fn mutexattr_set_kind<'tcx>(
)
}
/// A flag that allows to distinguish `PTHREAD_MUTEX_NORMAL` from
/// `PTHREAD_MUTEX_DEFAULT`. Since in `glibc` they have the same numeric values,
/// but different behaviour, we need a way to distinguish them. We do this by
/// setting this bit flag to the `PTHREAD_MUTEX_NORMAL` mutexes. See the comment
/// in `pthread_mutexattr_settype` function.
const PTHREAD_MUTEX_NORMAL_FLAG: i32 = 0x8000000;
/// To differentiate "the mutex kind has not been changed" from
/// "the mutex kind has been set to PTHREAD_MUTEX_DEFAULT and that is
/// equal to some other mutex kind", we make the default value of this
/// field *not* PTHREAD_MUTEX_DEFAULT but this special flag.
const PTHREAD_MUTEX_KIND_UNCHANGED: i32 = 0x8000000;
fn is_mutex_kind_default<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> {
Ok(kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"))
/// The mutex kind.
#[derive(Debug, Clone, Copy)]
pub enum MutexKind {
Normal,
Default,
Recursive,
ErrorCheck,
}
fn is_mutex_kind_normal<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, bool> {
let mutex_normal_kind = ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL");
Ok(kind == (mutex_normal_kind | PTHREAD_MUTEX_NORMAL_FLAG))
#[derive(Debug)]
/// Additional data that we attach with each mutex instance.
pub struct AdditionalMutexData {
/// The mutex kind, used by some mutex implementations like pthreads mutexes.
pub kind: MutexKind,
/// The address of the mutex.
pub address: u64,
}
// pthread_mutex_t is between 24 and 48 bytes, depending on the platform.
// pthread_mutex_t is between 4 and 48 bytes, depending on the platform.
// We ignore the platform layout and store our own fields:
// - id: u32
fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
// When adding a new OS, make sure we also support all its static initializers in
// `mutex_kind_from_static_initializer`!
let offset = match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" => 0,
"linux" | "illumos" | "solaris" | "freebsd" | "android" => 0,
// macOS stores a signature in the first bytes, so we have to move to offset 4.
"macos" => 4,
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
@ -77,15 +88,28 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
// recursive or error checking mutexes. We should also add thme in this sanity check.
static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_MUTEX_INITIALIZER"]);
let id_field = static_initializer
.offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx)
.unwrap();
let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap();
assert_eq!(
id, 0,
"PTHREAD_MUTEX_INITIALIZER is incompatible with our pthread_mutex layout: id is not 0"
);
let check_static_initializer = |name| {
let static_initializer = ecx.eval_path(&["libc", name]);
let id_field = static_initializer
.offset(Size::from_bytes(offset), ecx.machine.layouts.u32, ecx)
.unwrap();
let id = ecx.read_scalar(&id_field).unwrap().to_u32().unwrap();
assert_eq!(id, 0, "{name} is incompatible with our pthread_mutex layout: id is not 0");
};
check_static_initializer("PTHREAD_MUTEX_INITIALIZER");
// Check non-standard initializers.
match &*ecx.tcx.sess.target.os {
"linux" => {
check_static_initializer("PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP");
check_static_initializer("PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP");
check_static_initializer("PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP");
}
"illumos" | "solaris" | "macos" | "freebsd" | "android" => {
// No non-standard initializers.
}
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
}
}
Ok(offset)
@ -94,15 +118,13 @@ fn mutex_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
/// Eagerly create and initialize a new mutex.
fn mutex_create<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
kind: i32,
mutex_ptr: &OpTy<'tcx>,
kind: MutexKind,
) -> InterpResult<'tcx> {
// FIXME: might be worth changing mutex_create to take the mplace
// rather than the `OpTy`.
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
let kind = translate_kind(ecx, kind)?;
let data = Some(AdditionalMutexData { address, kind });
ecx.mutex_create(mutex_op, ecx.libc_ty_layout("pthread_mutex_t"), mutex_id_offset(ecx)?, data)?;
let mutex = ecx.deref_pointer(mutex_ptr)?;
let address = mutex.ptr().addr().bytes();
let data = Box::new(AdditionalMutexData { address, kind });
ecx.mutex_create(&mutex, mutex_id_offset(ecx)?, Some(data))?;
Ok(())
}
@ -112,27 +134,23 @@ fn mutex_create<'tcx>(
/// return an error if it has.
fn mutex_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
mutex_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, MutexId> {
let address = ecx.read_pointer(mutex_op)?.addr().bytes();
let mutex = ecx.deref_pointer(mutex_ptr)?;
let address = mutex.ptr().addr().bytes();
// FIXME: might be worth changing mutex_get_or_create_id to take the mplace
// rather than the `OpTy`.
let id = ecx.mutex_get_or_create_id(
mutex_op,
ecx.libc_ty_layout("pthread_mutex_t"),
mutex_id_offset(ecx)?,
|ecx| {
// This is called if a static initializer was used and the lock has not been assigned
// an ID yet. We have to determine the mutex kind from the static initializer.
let kind = kind_from_static_initializer(ecx, mutex_op)?;
let id = ecx.mutex_get_or_create_id(&mutex, mutex_id_offset(ecx)?, |ecx| {
// This is called if a static initializer was used and the lock has not been assigned
// an ID yet. We have to determine the mutex kind from the static initializer.
let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
Ok(Some(AdditionalMutexData { kind, address }))
},
)?;
Ok(Some(Box::new(AdditionalMutexData { kind, address })))
})?;
// Check that the mutex has not been moved since last use.
let data = ecx.mutex_get_data(id).expect("data should be always exist for pthreads");
let data = ecx
.mutex_get_data::<AdditionalMutexData>(id)
.expect("data should always exist for pthreads");
if data.address != address {
throw_ub_format!("pthread_mutex_t can't be moved after first use")
}
@ -141,51 +159,64 @@ fn mutex_get_id<'tcx>(
}
/// Returns the kind of a static initializer.
fn kind_from_static_initializer<'tcx>(
fn mutex_kind_from_static_initializer<'tcx>(
ecx: &MiriInterpCx<'tcx>,
mutex_op: &OpTy<'tcx>,
mutex: &MPlaceTy<'tcx>,
) -> InterpResult<'tcx, MutexKind> {
// Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT.
let kind = match &*ecx.tcx.sess.target.os {
Ok(match &*ecx.tcx.sess.target.os {
// Only linux has static initializers other than PTHREAD_MUTEX_DEFAULT.
"linux" => {
let offset = if ecx.pointer_size().bytes() == 8 { 16 } else { 12 };
ecx.deref_pointer_and_read(
mutex_op,
offset,
ecx.libc_ty_layout("pthread_mutex_t"),
ecx.machine.layouts.i32,
)?
.to_i32()?
let kind_place =
mutex.offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx)?;
let kind = ecx.read_scalar(&kind_place)?.to_i32()?;
// Here we give PTHREAD_MUTEX_DEFAULT priority so that
// PTHREAD_MUTEX_INITIALIZER behaves like `pthread_mutex_init` with a NULL argument.
if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT") {
MutexKind::Default
} else {
mutex_translate_kind(ecx, kind)?
}
}
| "illumos" | "solaris" | "macos" => ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"),
os => throw_unsup_format!("`pthread_mutex` is not supported on {os}"),
};
translate_kind(ecx, kind)
_ => MutexKind::Default,
})
}
fn translate_kind<'tcx>(ecx: &MiriInterpCx<'tcx>, kind: i32) -> InterpResult<'tcx, MutexKind> {
Ok(if is_mutex_kind_default(ecx, kind)? {
MutexKind::Default
} else if is_mutex_kind_normal(ecx, kind)? {
fn mutex_translate_kind<'tcx>(
ecx: &MiriInterpCx<'tcx>,
kind: i32,
) -> InterpResult<'tcx, MutexKind> {
Ok(if kind == (ecx.eval_libc_i32("PTHREAD_MUTEX_NORMAL")) {
MutexKind::Normal
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK") {
MutexKind::ErrorCheck
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE") {
MutexKind::Recursive
} else if kind == ecx.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
|| kind == PTHREAD_MUTEX_KIND_UNCHANGED
{
// We check this *last* since PTHREAD_MUTEX_DEFAULT may be numerically equal to one of the
// others, and we want an explicit `mutexattr_settype` to work as expected.
MutexKind::Default
} else {
throw_unsup_format!("unsupported type of mutex: {kind}");
})
}
// pthread_rwlock_t is between 32 and 56 bytes, depending on the platform.
// pthread_rwlock_t is between 4 and 56 bytes, depending on the platform.
// We ignore the platform layout and store our own fields:
// - id: u32
#[derive(Debug)]
/// Additional data that we attach with each rwlock instance.
pub struct AdditionalRwLockData {
/// The address of the rwlock.
pub address: u64,
}
fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
let offset = match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" => 0,
"linux" | "illumos" | "solaris" | "freebsd" | "android" => 0,
// macOS stores a signature in the first bytes, so we have to move to offset 4.
"macos" => 4,
os => throw_unsup_format!("`pthread_rwlock` is not supported on {os}"),
@ -211,13 +242,24 @@ fn rwlock_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
fn rwlock_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
rwlock_op: &OpTy<'tcx>,
rwlock_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, RwLockId> {
ecx.rwlock_get_or_create_id(
rwlock_op,
ecx.libc_ty_layout("pthread_rwlock_t"),
rwlock_id_offset(ecx)?,
)
let rwlock = ecx.deref_pointer(rwlock_ptr)?;
let address = rwlock.ptr().addr().bytes();
let id = ecx.rwlock_get_or_create_id(&rwlock, rwlock_id_offset(ecx)?, |_| {
Ok(Some(Box::new(AdditionalRwLockData { address })))
})?;
// Check that the rwlock has not been moved since last use.
let data = ecx
.rwlock_get_data::<AdditionalRwLockData>(id)
.expect("data should always exist for pthreads");
if data.address != address {
throw_ub_format!("pthread_rwlock_t can't be moved after first use")
}
Ok(id)
}
// pthread_condattr_t.
@ -227,7 +269,7 @@ fn rwlock_get_id<'tcx>(
#[inline]
fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
Ok(match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" => 0,
"linux" | "illumos" | "solaris" | "freebsd" | "android" => 0,
// macOS does not have a clock attribute.
os => throw_unsup_format!("`pthread_condattr` clock field is not supported on {os}"),
})
@ -235,10 +277,10 @@ fn condattr_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u
fn condattr_get_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
attr_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
attr_op,
attr_ptr,
condattr_clock_offset(ecx)?,
ecx.libc_ty_layout("pthread_condattr_t"),
ecx.machine.layouts.i32,
@ -246,13 +288,26 @@ fn condattr_get_clock_id<'tcx>(
.to_i32()
}
fn cond_translate_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
raw_id: i32,
) -> InterpResult<'tcx, ClockId> {
Ok(if raw_id == ecx.eval_libc_i32("CLOCK_REALTIME") {
ClockId::Realtime
} else if raw_id == ecx.eval_libc_i32("CLOCK_MONOTONIC") {
ClockId::Monotonic
} else {
throw_unsup_format!("unsupported clock id: {raw_id}");
})
}
fn condattr_set_clock_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
attr_op: &OpTy<'tcx>,
attr_ptr: &OpTy<'tcx>,
clock_id: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
attr_op,
attr_ptr,
condattr_clock_offset(ecx)?,
Scalar::from_i32(clock_id),
ecx.libc_ty_layout("pthread_condattr_t"),
@ -260,14 +315,13 @@ fn condattr_set_clock_id<'tcx>(
)
}
// pthread_cond_t.
// pthread_cond_t can be only 4 bytes in size, depending on the platform.
// We ignore the platform layout and store our own fields:
// - id: u32
// - clock: i32
fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
let offset = match &*ecx.tcx.sess.target.os {
"linux" | "illumos" | "solaris" => 0,
"linux" | "illumos" | "solaris" | "freebsd" | "android" => 0,
// macOS stores a signature in the first bytes, so we have to move to offset 4.
"macos" => 4,
os => throw_unsup_format!("`pthread_cond` is not supported on {os}"),
@ -291,88 +345,42 @@ fn cond_id_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, u64> {
Ok(offset)
}
/// Determines whether this clock represents the real-time clock, CLOCK_REALTIME.
fn is_cond_clock_realtime<'tcx>(ecx: &MiriInterpCx<'tcx>, clock_id: i32) -> bool {
// To ensure compatibility with PTHREAD_COND_INITIALIZER on all platforms,
// we can't just compare with CLOCK_REALTIME: on Solarish, PTHREAD_COND_INITIALIZER
// makes the clock 0 but CLOCK_REALTIME is 3.
// However, we need to always be able to distinguish this from CLOCK_MONOTONIC.
clock_id == ecx.eval_libc_i32("CLOCK_REALTIME")
|| (clock_id == 0 && clock_id != ecx.eval_libc_i32("CLOCK_MONOTONIC"))
#[derive(Debug, Clone, Copy)]
enum ClockId {
Realtime,
Monotonic,
}
fn cond_clock_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> u64 {
// macOS doesn't have a clock attribute, but to keep the code uniform we store
// a clock ID in the pthread_cond_t anyway. There's enough space.
let offset = 8;
#[derive(Debug)]
/// Additional data that we attach with each cond instance.
struct AdditionalCondData {
/// The address of the cond.
address: u64,
// Sanity-check this against PTHREAD_COND_INITIALIZER (but only once):
// the clock must start out as CLOCK_REALTIME.
static SANITY: AtomicBool = AtomicBool::new(false);
if !SANITY.swap(true, Ordering::Relaxed) {
let static_initializer = ecx.eval_path(&["libc", "PTHREAD_COND_INITIALIZER"]);
let id_field = static_initializer
.offset(Size::from_bytes(offset), ecx.machine.layouts.i32, ecx)
.unwrap();
let id = ecx.read_scalar(&id_field).unwrap().to_i32().unwrap();
assert!(
is_cond_clock_realtime(ecx, id),
"PTHREAD_COND_INITIALIZER is incompatible with our pthread_cond layout: clock is not CLOCK_REALTIME"
);
}
offset
/// The clock id of the cond.
clock_id: ClockId,
}
fn cond_get_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
cond_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, CondvarId> {
ecx.condvar_get_or_create_id(
cond_op,
ecx.libc_ty_layout("pthread_cond_t"),
cond_id_offset(ecx)?,
)
}
let cond = ecx.deref_pointer(cond_ptr)?;
let address = cond.ptr().addr().bytes();
let id = ecx.condvar_get_or_create_id(&cond, cond_id_offset(ecx)?, |_ecx| {
// This used the static initializer. The clock there is always CLOCK_REALTIME.
Ok(Some(Box::new(AdditionalCondData { address, clock_id: ClockId::Realtime })))
})?;
fn cond_reset_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
cond_op,
cond_id_offset(ecx)?,
Scalar::from_i32(0),
ecx.libc_ty_layout("pthread_cond_t"),
ecx.machine.layouts.u32,
)
}
// Check that the mutex has not been moved since last use.
let data = ecx
.condvar_get_data::<AdditionalCondData>(id)
.expect("data should always exist for pthreads");
if data.address != address {
throw_ub_format!("pthread_cond_t can't be moved after first use")
}
fn cond_get_clock_id<'tcx>(
ecx: &MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
) -> InterpResult<'tcx, i32> {
ecx.deref_pointer_and_read(
cond_op,
cond_clock_offset(ecx),
ecx.libc_ty_layout("pthread_cond_t"),
ecx.machine.layouts.i32,
)?
.to_i32()
}
fn cond_set_clock_id<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
cond_op: &OpTy<'tcx>,
clock_id: i32,
) -> InterpResult<'tcx, ()> {
ecx.deref_pointer_and_write(
cond_op,
cond_clock_offset(ecx),
Scalar::from_i32(clock_id),
ecx.libc_ty_layout("pthread_cond_t"),
ecx.machine.layouts.i32,
)
Ok(id)
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@ -380,8 +388,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutexattr_init(&mut self, attr_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
let default_kind = this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT");
mutexattr_set_kind(this, attr_op, default_kind)?;
mutexattr_set_kind(this, attr_op, PTHREAD_MUTEX_KIND_UNCHANGED)?;
Ok(())
}
@ -394,30 +401,13 @@ fn pthread_mutexattr_settype(
let this = self.eval_context_mut();
let kind = this.read_scalar(kind_op)?.to_i32()?;
if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL") {
// In `glibc` implementation, the numeric values of
// `PTHREAD_MUTEX_NORMAL` and `PTHREAD_MUTEX_DEFAULT` are equal.
// However, a mutex created by explicitly passing
// `PTHREAD_MUTEX_NORMAL` type has in some cases different behaviour
// from the default mutex for which the type was not explicitly
// specified. For a more detailed discussion, please see
// https://github.com/rust-lang/miri/issues/1419.
//
// To distinguish these two cases in already constructed mutexes, we
// use the same trick as glibc: for the case when
// `pthread_mutexattr_settype` is called explicitly, we set the
// `PTHREAD_MUTEX_NORMAL_FLAG` flag.
let normal_kind = kind | PTHREAD_MUTEX_NORMAL_FLAG;
// Check that after setting the flag, the kind is distinguishable
// from all other kinds.
assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT"));
assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK"));
assert_ne!(normal_kind, this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE"));
mutexattr_set_kind(this, attr_op, normal_kind)?;
} else if kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
if kind == this.eval_libc_i32("PTHREAD_MUTEX_NORMAL")
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_ERRORCHECK")
|| kind == this.eval_libc_i32("PTHREAD_MUTEX_RECURSIVE")
{
// Make sure we do not mix this up with the "unchanged" kind.
assert_ne!(kind, PTHREAD_MUTEX_KIND_UNCHANGED);
mutexattr_set_kind(this, attr_op, kind)?;
} else {
let einval = this.eval_libc_i32("EINVAL");
@ -461,9 +451,9 @@ fn pthread_mutex_init(
let attr = this.read_pointer(attr_op)?;
let kind = if this.ptr_is_null(attr)? {
this.eval_libc_i32("PTHREAD_MUTEX_DEFAULT")
MutexKind::Default
} else {
mutexattr_get_kind(this, attr_op)?
mutex_translate_kind(this, mutexattr_get_kind(this, attr_op)?)?
};
mutex_create(this, mutex_op, kind)?;
@ -479,8 +469,10 @@ fn pthread_mutex_lock(
let this = self.eval_context_mut();
let id = mutex_get_id(this, mutex_op)?;
let kind =
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
let kind = this
.mutex_get_data::<AdditionalMutexData>(id)
.expect("data should always exist for pthread mutexes")
.kind;
let ret = if this.mutex_is_locked(id) {
let owner_thread = this.mutex_get_owner(id);
@ -498,10 +490,6 @@ fn pthread_mutex_lock(
this.mutex_lock(id);
0
}
_ =>
throw_unsup_format!(
"called pthread_mutex_lock on an unsupported type of mutex"
),
}
}
} else {
@ -517,8 +505,10 @@ fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx,
let this = self.eval_context_mut();
let id = mutex_get_id(this, mutex_op)?;
let kind =
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
let kind = this
.mutex_get_data::<AdditionalMutexData>(id)
.expect("data should always exist for pthread mutexes")
.kind;
Ok(Scalar::from_i32(if this.mutex_is_locked(id) {
let owner_thread = this.mutex_get_owner(id);
@ -532,10 +522,6 @@ fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx,
this.mutex_lock(id);
0
}
_ =>
throw_unsup_format!(
"called pthread_mutex_trylock on an unsupported type of mutex"
),
}
}
} else {
@ -549,8 +535,10 @@ fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx,
let this = self.eval_context_mut();
let id = mutex_get_id(this, mutex_op)?;
let kind =
this.mutex_get_data(id).expect("data should always exist for pthread mutexes").kind;
let kind = this
.mutex_get_data::<AdditionalMutexData>(id)
.expect("data should always exist for pthread mutexes")
.kind;
if let Some(_old_locked_count) = this.mutex_unlock(id)? {
// The mutex was locked by the current thread.
@ -570,10 +558,6 @@ fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx,
),
MutexKind::ErrorCheck | MutexKind::Recursive =>
Ok(Scalar::from_i32(this.eval_libc_i32("EPERM"))),
_ =>
throw_unsup_format!(
"called pthread_mutex_unlock on an unsupported type of mutex"
),
}
}
}
@ -581,15 +565,14 @@ fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx,
fn pthread_mutex_destroy(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
// Reading the field also has the side-effect that we detect double-`destroy`
// since we make the field unint below.
let id = mutex_get_id(this, mutex_op)?;
if this.mutex_is_locked(id) {
throw_ub_format!("destroyed a locked mutex");
}
// Destroying an uninit pthread_mutex is UB, so check to make sure it's not uninit.
mutex_get_id(this, mutex_op)?;
// This might lead to false positives, see comment in pthread_mutexattr_destroy
this.write_uninit(
&this.deref_pointer_as(mutex_op, this.libc_ty_layout("pthread_mutex_t"))?,
@ -691,15 +674,14 @@ fn pthread_rwlock_unlock(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx
fn pthread_rwlock_destroy(&mut self, rwlock_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
// Reading the field also has the side-effect that we detect double-`destroy`
// since we make the field unint below.
let id = rwlock_get_id(this, rwlock_op)?;
if this.rwlock_is_locked(id) {
throw_ub_format!("destroyed a locked rwlock");
}
// Destroying an uninit pthread_rwlock is UB, so check to make sure it's not uninit.
rwlock_get_id(this, rwlock_op)?;
// This might lead to false positives, see comment in pthread_mutexattr_destroy
this.write_uninit(
&this.deref_pointer_as(rwlock_op, this.libc_ty_layout("pthread_rwlock_t"))?,
@ -789,11 +771,15 @@ fn pthread_cond_init(
} else {
condattr_get_clock_id(this, attr_op)?
};
let clock_id = cond_translate_clock_id(this, clock_id)?;
// Write 0 to use the same code path as the static initializers.
cond_reset_id(this, cond_op)?;
cond_set_clock_id(this, cond_op, clock_id)?;
let cond = this.deref_pointer(cond_op)?;
let address = cond.ptr().addr().bytes();
this.condvar_create(
&cond,
cond_id_offset(this)?,
Some(Box::new(AdditionalCondData { address, clock_id })),
)?;
Ok(())
}
@ -848,7 +834,10 @@ fn pthread_cond_timedwait(
let mutex_id = mutex_get_id(this, mutex_op)?;
// Extract the timeout.
let clock_id = cond_get_clock_id(this, cond_op)?;
let clock_id = this
.condvar_get_data::<AdditionalCondData>(id)
.expect("additional data should always be present for pthreads")
.clock_id;
let duration = match this
.read_timespec(&this.deref_pointer_as(abstime_op, this.libc_ty_layout("timespec"))?)?
{
@ -859,13 +848,12 @@ fn pthread_cond_timedwait(
return Ok(());
}
};
let timeout_clock = if is_cond_clock_realtime(this, clock_id) {
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
TimeoutClock::RealTime
} else if clock_id == this.eval_libc_i32("CLOCK_MONOTONIC") {
TimeoutClock::Monotonic
} else {
throw_unsup_format!("unsupported clock id: {}", clock_id);
let timeout_clock = match clock_id {
ClockId::Realtime => {
this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?;
TimeoutClock::RealTime
}
ClockId::Monotonic => TimeoutClock::Monotonic,
};
this.condvar_wait(
@ -883,15 +871,13 @@ fn pthread_cond_timedwait(
fn pthread_cond_destroy(&mut self, cond_op: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
let this = self.eval_context_mut();
// Reading the field also has the side-effect that we detect double-`destroy`
// since we make the field unint below.
let id = cond_get_id(this, cond_op)?;
if this.condvar_is_awaited(id) {
throw_ub_format!("destroying an awaited conditional variable");
}
// Destroying an uninit pthread_cond is UB, so check to make sure it's not uninit.
cond_get_id(this, cond_op)?;
cond_get_clock_id(this, cond_op)?;
// This might lead to false positives, see comment in pthread_mutexattr_destroy
this.write_uninit(&this.deref_pointer_as(cond_op, this.libc_ty_layout("pthread_cond_t"))?)?;
// FIXME: delete interpreter state associated with this condvar.

View File

@ -10,9 +10,10 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Windows sync primitives are pointer sized.
// We only use the first 4 bytes for the id.
fn init_once_get_id(&mut self, init_once_op: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> {
fn init_once_get_id(&mut self, init_once_ptr: &OpTy<'tcx>) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut();
this.init_once_get_or_create_id(init_once_op, this.windows_ty_layout("INIT_ONCE"), 0)
let init_once = this.deref_pointer(init_once_ptr)?;
this.init_once_get_or_create_id(&init_once, 0)
}
/// Returns `true` if we were succssful, `false` if we would block.

View File

@ -172,6 +172,7 @@ dependencies = [
name = "miri-test-deps"
version = "0.1.0"
dependencies = [
"cfg-if",
"getrandom 0.1.16",
"getrandom 0.2.15",
"libc",

View File

@ -11,6 +11,7 @@ edition = "2021"
# all dependencies (and their transitive ones) listed here can be used in `tests/`.
libc = "0.2"
num_cpus = "1.10.1"
cfg-if = "1"
getrandom_01 = { package = "getrandom", version = "0.1" }
getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] }

View File

@ -1,4 +1,4 @@
//@ only-target-darwin
//@only-target: darwin
use std::cell::UnsafeCell;

View File

@ -1,11 +1,11 @@
error: abnormal termination: called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread
--> $DIR/apple_os_unfair_lock_assert_not_owner.rs:LL:CC
--> tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.rs:LL:CC
|
LL | libc::os_unfair_lock_assert_not_owner(lock.get());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread
|
= note: BACKTRACE:
= note: inside `main` at $DIR/apple_os_unfair_lock_assert_not_owner.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ only-target-darwin
//@only-target: darwin
use std::cell::UnsafeCell;

View File

@ -1,11 +1,11 @@
error: abnormal termination: called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread
--> $DIR/apple_os_unfair_lock_assert_owner.rs:LL:CC
--> tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.rs:LL:CC
|
LL | libc::os_unfair_lock_assert_owner(lock.get());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread
|
= note: BACKTRACE:
= note: inside `main` at $DIR/apple_os_unfair_lock_assert_owner.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ only-target-darwin
//@only-target: darwin
use std::cell::UnsafeCell;

View File

@ -1,11 +1,11 @@
error: abnormal termination: attempted to lock an os_unfair_lock that is already locked by the current thread
--> $DIR/apple_os_unfair_lock_reentrant.rs:LL:CC
--> tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.rs:LL:CC
|
LL | libc::os_unfair_lock_lock(lock.get());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to lock an os_unfair_lock that is already locked by the current thread
|
= note: BACKTRACE:
= note: inside `main` at $DIR/apple_os_unfair_lock_reentrant.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ only-target-darwin
//@only-target: darwin
use std::cell::UnsafeCell;

View File

@ -1,11 +1,11 @@
error: abnormal termination: attempted to unlock an os_unfair_lock not owned by the current thread
--> $DIR/apple_os_unfair_lock_unowned.rs:LL:CC
--> tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.rs:LL:CC
|
LL | libc::os_unfair_lock_unlock(lock.get());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to unlock an os_unfair_lock not owned by the current thread
|
= note: BACKTRACE:
= note: inside `main` at $DIR/apple_os_unfair_lock_unowned.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
/// Test that destroying a pthread_cond twice fails, even without a check for number validity

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
--> $DIR/libc_pthread_cond_double_destroy.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs:LL:CC
|
LL | libc::pthread_cond_destroy(cond.as_mut_ptr());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
@ -7,7 +7,7 @@ LL | libc::pthread_cond_destroy(cond.as_mut_ptr());
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_cond_double_destroy.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -0,0 +1,20 @@
error: Undefined Behavior: pthread_cond_t can't be moved after first use
--> tests/fail-dep/concurrency/libc_pthread_cond_move.rs:LL:CC
|
LL | libc::pthread_cond_destroy(cond2.as_mut_ptr());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_cond_t can't be moved after first use
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `check` at tests/fail-dep/concurrency/libc_pthread_cond_move.rs:LL:CC
note: inside `main`
--> tests/fail-dep/concurrency/libc_pthread_cond_move.rs:LL:CC
|
LL | check()
| ^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -0,0 +1,37 @@
//@revisions: static_initializer init
//@ignore-target: windows # No pthreads on Windows
/// Test that moving a pthread_cond between uses fails.
fn main() {
check()
}
#[cfg(init)]
fn check() {
unsafe {
use core::mem::MaybeUninit;
let mut cond = MaybeUninit::<libc::pthread_cond_t>::uninit();
libc::pthread_cond_init(cond.as_mut_ptr(), std::ptr::null());
// move pthread_cond_t
let mut cond2 = cond;
libc::pthread_cond_destroy(cond2.as_mut_ptr()); //~[init] ERROR: pthread_cond_t can't be moved after first use
}
}
#[cfg(static_initializer)]
fn check() {
unsafe {
let mut cond = libc::PTHREAD_COND_INITIALIZER;
libc::pthread_cond_signal(&mut cond as *mut _);
// move pthread_cond_t
let mut cond2 = cond;
libc::pthread_cond_destroy(&mut cond2 as *mut _); //~[static_initializer] ERROR: pthread_cond_t can't be moved after first use
}
}

View File

@ -0,0 +1,20 @@
error: Undefined Behavior: pthread_cond_t can't be moved after first use
--> tests/fail-dep/concurrency/libc_pthread_cond_move.rs:LL:CC
|
LL | libc::pthread_cond_destroy(&mut cond2 as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_cond_t can't be moved after first use
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `check` at tests/fail-dep/concurrency/libc_pthread_cond_move.rs:LL:CC
note: inside `main`
--> tests/fail-dep/concurrency/libc_pthread_cond_move.rs:LL:CC
|
LL | check()
| ^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View File

@ -1,5 +1,5 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target-apple: Our macOS condattr don't have any fields so we do not notice this.
//@ignore-target: windows # No pthreads on Windows
//@ignore-target: apple # Our macOS condattr don't have any fields so we do not notice this.
/// Test that destroying a pthread_condattr twice fails, even without a check for number validity

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
--> $DIR/libc_pthread_condattr_double_destroy.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs:LL:CC
|
LL | libc::pthread_condattr_destroy(attr.as_mut_ptr());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
@ -7,7 +7,7 @@ LL | libc::pthread_condattr_destroy(attr.as_mut_ptr());
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_condattr_double_destroy.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
//@error-in-other-file: the main thread terminated without waiting for all remaining threads
// Check that we terminate the program when the main thread terminates.

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
//~^ERROR: calling a function with more arguments than it expected
//! The thread function must have exactly one argument.

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
//~^ERROR: calling a function with fewer arguments than it requires
//! The thread function must have exactly one argument.

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
// Joining a detached thread is undefined behavior.

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: trying to join a detached thread
--> $DIR/libc_pthread_join_detached.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_join_detached.rs:LL:CC
|
LL | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to join a detached thread
@ -7,7 +7,7 @@ LL | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_join_detached.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_join_detached.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
// Joining an already joined thread is undefined behavior.

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: trying to join an already joined thread
--> $DIR/libc_pthread_join_joined.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_join_joined.rs:LL:CC
|
LL | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to join an already joined thread
@ -7,7 +7,7 @@ LL | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_join_joined.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_join_joined.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
// Joining the main thread is undefined behavior.

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: trying to join a detached thread
--> $DIR/libc_pthread_join_main.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_join_main.rs:LL:CC
|
LL | assert_eq!(libc::pthread_join(thread_id, ptr::null_mut()), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to join a detached thread
@ -7,7 +7,7 @@ LL | assert_eq!(libc::pthread_join(thread_id, ptr::null_mut()), 0);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at $DIR/libc_pthread_join_main.rs:LL:CC
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_join_main.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
// Joining the same thread from multiple threads is undefined behavior.

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: trying to join an already joined thread
--> $DIR/libc_pthread_join_multiple.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_join_multiple.rs:LL:CC
|
LL | ... assert_eq!(libc::pthread_join(native_copy, ptr::null_mut()), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to join an already joined thread
@ -7,7 +7,7 @@ LL | ... assert_eq!(libc::pthread_join(native_copy, ptr::null_mut()), 0);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at $DIR/libc_pthread_join_multiple.rs:LL:CC
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_join_multiple.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
// We are making scheduler assumptions here.
//@compile-flags: -Zmiri-preemption-rate=0

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: trying to join itself
--> $DIR/libc_pthread_join_self.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_join_self.rs:LL:CC
|
LL | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to join itself
@ -7,7 +7,7 @@ LL | assert_eq!(libc::pthread_join(native, ptr::null_mut()), 0);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at $DIR/libc_pthread_join_self.rs:LL:CC
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_join_self.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
//
// Check that if we pass NULL attribute, then we get the default mutex type.

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: trying to acquire already locked default mutex
--> $DIR/libc_pthread_mutex_NULL_deadlock.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs:LL:CC
|
LL | libc::pthread_mutex_lock(&mut mutex as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire already locked default mutex
@ -7,7 +7,7 @@ LL | libc::pthread_mutex_lock(&mut mutex as *mut _);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_mutex_NULL_deadlock.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_NULL_deadlock.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
//@error-in-other-file: deadlock
use std::cell::UnsafeCell;

View File

@ -1,11 +1,11 @@
error: deadlock: the evaluated program deadlocked
--> $DIR/libc_pthread_mutex_deadlock.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC
|
LL | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0);
| ^ the evaluated program deadlocked
|
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at $DIR/libc_pthread_mutex_deadlock.rs:LL:CC
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC
error: deadlock: the evaluated program deadlocked
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
@ -18,7 +18,7 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
note: inside `main`
--> $DIR/libc_pthread_mutex_deadlock.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC
|
LL | / thread::spawn(move || {
LL | | assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _), 0);

View File

@ -1,10 +1,11 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
//
// Check that if we do not set the mutex type, it is the default.
fn main() {
unsafe {
let mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
let mut mutexattr: libc::pthread_mutexattr_t = std::mem::zeroed();
assert_eq!(libc::pthread_mutexattr_init(&mut mutexattr as *mut _), 0);
let mut mutex: libc::pthread_mutex_t = std::mem::zeroed();
assert_eq!(libc::pthread_mutex_init(&mut mutex as *mut _, &mutexattr as *const _), 0);
assert_eq!(libc::pthread_mutex_lock(&mut mutex as *mut _), 0);

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: trying to acquire already locked default mutex
--> $DIR/libc_pthread_mutex_default_deadlock.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs:LL:CC
|
LL | libc::pthread_mutex_lock(&mut mutex as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trying to acquire already locked default mutex
@ -7,7 +7,7 @@ LL | libc::pthread_mutex_lock(&mut mutex as *mut _);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_mutex_default_deadlock.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_default_deadlock.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
fn main() {
unsafe {

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: destroyed a locked mutex
--> $DIR/libc_pthread_mutex_destroy_locked.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs:LL:CC
|
LL | libc::pthread_mutex_destroy(&mut mutex as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ destroyed a locked mutex
@ -7,7 +7,7 @@ LL | libc::pthread_mutex_destroy(&mut mutex as *mut _);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_mutex_destroy_locked.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_destroy_locked.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
/// Test that destroying a pthread_mutex twice fails, even without a check for number validity

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
--> $DIR/libc_pthread_mutex_double_destroy.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs:LL:CC
|
LL | libc::pthread_mutex_destroy(mutex.as_mut_ptr());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
@ -7,7 +7,7 @@ LL | libc::pthread_mutex_destroy(mutex.as_mut_ptr());
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_mutex_double_destroy.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: pthread_mutex_t can't be moved after first use
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_move.rs:LL:CC
|
LL | libc::pthread_mutex_lock(&mut m2 as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use
@ -7,9 +7,9 @@ LL | libc::pthread_mutex_lock(&mut m2 as *mut _);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `check` at $DIR/libc_pthread_mutex_move.rs:LL:CC
= note: inside `check` at tests/fail-dep/concurrency/libc_pthread_mutex_move.rs:LL:CC
note: inside `main`
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_move.rs:LL:CC
|
LL | check();
| ^^^^^^^

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
//@revisions: static_initializer init
fn main() {

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: pthread_mutex_t can't be moved after first use
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_move.rs:LL:CC
|
LL | libc::pthread_mutex_unlock(&mut m2 as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pthread_mutex_t can't be moved after first use
@ -7,9 +7,9 @@ LL | libc::pthread_mutex_unlock(&mut m2 as *mut _);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `check` at $DIR/libc_pthread_mutex_move.rs:LL:CC
= note: inside `check` at tests/fail-dep/concurrency/libc_pthread_mutex_move.rs:LL:CC
note: inside `main`
--> $DIR/libc_pthread_mutex_move.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_move.rs:LL:CC
|
LL | check();
| ^^^^^^^

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
fn main() {
unsafe {

View File

@ -1,11 +1,11 @@
error: deadlock: the evaluated program deadlocked
--> $DIR/libc_pthread_mutex_normal_deadlock.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs:LL:CC
|
LL | libc::pthread_mutex_lock(&mut mutex as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked
|
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_mutex_normal_deadlock.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_normal_deadlock.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
fn main() {
unsafe {

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread
--> $DIR/libc_pthread_mutex_normal_unlock_unlocked.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs:LL:CC
|
LL | libc::pthread_mutex_unlock(&mut mutex as *mut _);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unlocked a PTHREAD_MUTEX_NORMAL mutex that was not locked by the current thread
@ -7,7 +7,7 @@ LL | libc::pthread_mutex_unlock(&mut mutex as *mut _);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_mutex_normal_unlock_unlocked.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_normal_unlock_unlocked.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
use std::cell::UnsafeCell;
use std::sync::Arc;

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: unlocked a default mutex that was not locked by the current thread
--> $DIR/libc_pthread_mutex_wrong_owner.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs:LL:CC
|
LL | ...t_eq!(libc::pthread_mutex_unlock(lock_copy.0.get() as *mut _), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unlocked a default mutex that was not locked by the current thread
@ -7,7 +7,7 @@ LL | ...t_eq!(libc::pthread_mutex_unlock(lock_copy.0.get() as *mut _), 0);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at $DIR/libc_pthread_mutex_wrong_owner.rs:LL:CC
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_mutex_wrong_owner.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
/// Test that destroying a pthread_mutexattr twice fails, even without a check for number validity

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
--> $DIR/libc_pthread_mutexattr_double_destroy.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs:LL:CC
|
LL | libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
@ -7,7 +7,7 @@ LL | libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_mutexattr_double_destroy.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
fn main() {
let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: destroyed a locked rwlock
--> $DIR/libc_pthread_rwlock_destroy_read_locked.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs:LL:CC
|
LL | libc::pthread_rwlock_destroy(rw.get());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ destroyed a locked rwlock
@ -7,7 +7,7 @@ LL | libc::pthread_rwlock_destroy(rw.get());
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_rwlock_destroy_read_locked.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_read_locked.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
fn main() {
let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: destroyed a locked rwlock
--> $DIR/libc_pthread_rwlock_destroy_write_locked.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs:LL:CC
|
LL | libc::pthread_rwlock_destroy(rw.get());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ destroyed a locked rwlock
@ -7,7 +7,7 @@ LL | libc::pthread_rwlock_destroy(rw.get());
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_rwlock_destroy_write_locked.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_rwlock_destroy_write_locked.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
/// Test that destroying a pthread_rwlock twice fails, even without a check for number validity

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
--> $DIR/libc_pthread_rwlock_double_destroy.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs:LL:CC
|
LL | libc::pthread_rwlock_destroy(&mut lock);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
@ -7,7 +7,7 @@ LL | libc::pthread_rwlock_destroy(&mut lock);
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_rwlock_double_destroy.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
fn main() {
let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);

View File

@ -1,11 +1,11 @@
error: deadlock: the evaluated program deadlocked
--> $DIR/libc_pthread_rwlock_read_write_deadlock_single_thread.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs:LL:CC
|
LL | libc::pthread_rwlock_wrlock(rw.get());
| ^ the evaluated program deadlocked
|
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_rwlock_read_write_deadlock_single_thread.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_rwlock_read_write_deadlock_single_thread.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
use std::cell::UnsafeCell;
use std::sync::Arc;

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: unlocked an rwlock that was not locked by the active thread
--> $DIR/libc_pthread_rwlock_read_wrong_owner.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs:LL:CC
|
LL | ... assert_eq!(libc::pthread_rwlock_unlock(lock_copy.0.get() as *mut _), 0);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unlocked an rwlock that was not locked by the active thread
@ -7,7 +7,7 @@ LL | ... assert_eq!(libc::pthread_rwlock_unlock(lock_copy.0.get() as *mut _),
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at $DIR/libc_pthread_rwlock_read_wrong_owner.rs:LL:CC
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_read_wrong_owner.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
fn main() {
let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);

View File

@ -1,5 +1,5 @@
error: Undefined Behavior: unlocked an rwlock that was not locked by the active thread
--> $DIR/libc_pthread_rwlock_unlock_unlocked.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs:LL:CC
|
LL | libc::pthread_rwlock_unlock(rw.get());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unlocked an rwlock that was not locked by the active thread
@ -7,7 +7,7 @@ LL | libc::pthread_rwlock_unlock(rw.get());
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_rwlock_unlock_unlocked.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_rwlock_unlock_unlocked.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
//@error-in-other-file: deadlock
use std::cell::UnsafeCell;

View File

@ -1,11 +1,11 @@
error: deadlock: the evaluated program deadlocked
--> $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
|
LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);
| ^ the evaluated program deadlocked
|
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
error: deadlock: the evaluated program deadlocked
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
@ -18,7 +18,7 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
note: inside `main`
--> $DIR/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
|
LL | / thread::spawn(move || {
LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
fn main() {
let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);

View File

@ -1,11 +1,11 @@
error: deadlock: the evaluated program deadlocked
--> $DIR/libc_pthread_rwlock_write_read_deadlock_single_thread.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs:LL:CC
|
LL | libc::pthread_rwlock_rdlock(rw.get());
| ^ the evaluated program deadlocked
|
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_rwlock_write_read_deadlock_single_thread.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock_single_thread.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
//@error-in-other-file: deadlock
use std::cell::UnsafeCell;

View File

@ -1,11 +1,11 @@
error: deadlock: the evaluated program deadlocked
--> $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
|
LL | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);
| ^ the evaluated program deadlocked
|
= note: BACKTRACE on thread `unnamed-ID`:
= note: inside closure at $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
= note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
error: deadlock: the evaluated program deadlocked
--> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
@ -18,7 +18,7 @@ LL | let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
= note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
= note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
note: inside `main`
--> $DIR/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
|
LL | / thread::spawn(move || {
LL | | assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mut _), 0);

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
fn main() {
let rw = std::cell::UnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);

View File

@ -1,11 +1,11 @@
error: deadlock: the evaluated program deadlocked
--> $DIR/libc_pthread_rwlock_write_write_deadlock_single_thread.rs:LL:CC
--> tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs:LL:CC
|
LL | libc::pthread_rwlock_wrlock(rw.get());
| ^ the evaluated program deadlocked
|
= note: BACKTRACE:
= note: inside `main` at $DIR/libc_pthread_rwlock_write_write_deadlock_single_thread.rs:LL:CC
= note: inside `main` at tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock_single_thread.rs:LL:CC
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

View File

@ -1,4 +1,4 @@
//@ignore-target-windows: No pthreads on Windows
//@ignore-target: windows # No pthreads on Windows
use std::cell::UnsafeCell;
use std::sync::Arc;

Some files were not shown because too many files have changed in this diff Show More