Skip to content

Commit 5c65433

Browse files
authored
Merge pull request #4751 from RalfJung/genmc-warnings
genmc: suppress compare_exchange warnings from dependencies
2 parents ac4639d + 6dfabbd commit 5c65433

File tree

15 files changed

+62
-655
lines changed

15 files changed

+62
-655
lines changed

doc/genmc.md

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# **(WIP)** Documentation for Miri-GenMC
22

3-
**NOTE: GenMC mode is not yet fully implemented, and has [several correctness issues](https://github.com/rust-lang/miri/issues/4572). Using GenMC mode currently requires manually compiling Miri, see [Usage](#usage).**
4-
3+
**NOTE: GenMC mode is not yet fully implemented, and has [several correctness issues](https://github.com/rust-lang/miri/issues/4572) and [other limitations](#limitations). Using GenMC mode currently requires manually compiling Miri, see [Usage](#usage).**
54

65
[GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program.
76
Miri-GenMC integrates that model checker into Miri.
@@ -12,11 +11,14 @@ This includes all possible thread interleavings and all allowed return values fo
1211
It is hence still possible to have latent bugs in a test case even if they passed GenMC.)
1312

1413
GenMC requires the input program to be bounded, i.e., have finitely many possible executions, otherwise it will not terminate.
15-
Any loops that may run infinitely must be replaced or bounded (see below).
14+
Any loops that may run infinitely must be replaced or bounded (see [below](#eliminating-unbounded-loops)).
1615

1716
GenMC makes use of Dynamic Partial Order Reduction (DPOR) to reduce the number of executions that must be explored, but the runtime can still be super-exponential in the size of the input program (number of threads and amount of interaction between threads).
1817
Large programs may not be verifiable in a reasonable amount of time.
1918

19+
GenMC currently only supports Linux hosts.
20+
Both the host and the target must be 64-bit little-endian.
21+
2022
## Usage
2123

2224
For testing/developing Miri-GenMC:
@@ -50,16 +52,24 @@ Note that `cargo miri test` in GenMC mode is currently not supported.
5052
- `debug2`: Print the execution graph after every memory access.
5153
- `debug3`: Print reads-from values considered by GenMC.
5254
- `-Zmiri-genmc-verbose`: Show more information, such as estimated number of executions, and time taken for verification.
53-
54-
#### Regular Miri parameters useful for GenMC mode
55-
5655
- `-Zmiri-disable-weak-memory-emulation`: Disable any weak memory effects (effectively upgrading all atomic orderings in the program to `SeqCst`). This option may reduce the number of explored program executions, but any bugs related to weak memory effects will be missed. This option can help determine if an error is caused by weak memory effects (i.e., if it disappears with this option enabled).
5756

5857
<!-- FIXME(genmc): explain Miri-GenMC specific functions. -->
5958

60-
## Tips
59+
## Limitations
60+
61+
There are several limitations which can make GenMC miss bugs:
62+
- GenMC does not support re-using freed memory for new allocations, so any bugs related to that will be missed.
63+
- GenMC does not support `compare_exchange_weak`, so the consequences of spurious failures are not explored.
64+
A warning will be emitted if this affects code you wrote (but not if it happens inside your dependencies).
65+
- GenMC does not support the separate failure ordering of `compare_exchange`. Miri will take the maximum of the success and failure ordering and use that for the access; outcomes that rely on the real ordering being weaker will not be explored.
66+
A warning will be emitted if this affects code you wrote (but not if it happens inside your dependencies).
67+
- GenMC is incompatible with borrow tracking (Stacked/Tree Borrows). You need to set `-Zmiri-disable-stacked-borrows` to use GenMC.
68+
- Like all C++ memory model verification tools, GenMC has to solve the [out-of-thin-air problem](https://www.cl.cam.ac.uk/~pes20/cpp/notes42.html).
69+
It takes the [usual approach](https://plv.mpi-sws.org/scfix/paper.pdf) of requiring the union of "program-order" and "reads-from" to be acyclic.
70+
This means it excludes certain behaviors allowed by the C++ memory model, some of which can occur on hardware that performs load buffering.
6171

62-
<!-- FIXME(genmc): add tips for using Miri-GenMC more efficiently. -->
72+
## Tips
6373

6474
### Eliminating unbounded loops
6575

@@ -121,24 +131,11 @@ fn count_until_true_genmc(flag: &AtomicBool) -> u64 {
121131
<!-- FIXME: update the code above once Miri supports a loop bounding features like GenMC's `--unroll=N`. -->
122132
<!-- FIXME: update this section once Miri-GenMC supports automatic program transformations (like spinloop-assume replacement). -->
123133

124-
## Limitations
125-
126-
Some or all of these limitations might get removed in the future:
127-
128-
- Borrow tracking is currently incompatible (stacked/tree borrows).
129-
- Only Linux is supported for now.
130-
- No support for 32-bit or big-endian targets.
131-
- No cross-target interpretation.
132-
133-
<!-- FIXME(genmc): document remaining limitations -->
134-
135134
## Development
136135

137136
GenMC is written in C++, which complicates development a bit.
138137
The prerequisites for building Miri-GenMC are:
139-
- A compiler with C++23 support.
140-
- LLVM developments headers and clang.
141-
<!-- FIXME(genmc,llvm): remove once LLVM dependency is no longer required. -->
138+
- A compiler with sufficient C++20 support (we are testing GCC 13).
142139

143140
The actual code for GenMC is not contained in the Miri repo itself, but in a [separate GenMC repo](https://github.com/MPI-SWS/genmc) (with its own maintainers).
144141
These sources need to be available to build Miri-GenMC.
@@ -149,6 +146,8 @@ The process for obtaining them is as follows:
149146
If you place this directory inside the Miri folder, it is recommended to call it `genmc-src` as that tells `./miri fmt` to avoid
150147
formatting the Rust files inside that folder.
151148

149+
<!-- FIXME(genmc): explain how submitting code to GenMC should be handled. -->
150+
152151
### Formatting the C++ code
153152

154153
For formatting the C++ code we provide a `.clang-format` file in the `genmc-sys` directory.
@@ -157,7 +156,3 @@ With `clang-format` installed, run this command to format the c++ files (replace
157156
find ./genmc-sys/cpp/ -name "*.cpp" -o -name "*.hpp" | xargs clang-format --style=file:"./genmc-sys/.clang-format" -i
158157
```
159158
NOTE: this is currently not done automatically on pull requests to Miri.
160-
161-
<!-- FIXME(genmc): explain how submitting code to GenMC should be handled. -->
162-
163-
<!-- FIXME(genmc): explain development. -->

src/concurrency/genmc/helper.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,8 @@ impl AtomicRwOrd {
171171
(AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed) => AtomicRwOrd::Acquire,
172172
(AtomicReadOrd::Relaxed, AtomicWriteOrd::Release) => AtomicRwOrd::Release,
173173
(AtomicReadOrd::Acquire, AtomicWriteOrd::Release) => AtomicRwOrd::AcqRel,
174-
(AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst) => AtomicRwOrd::SeqCst,
175-
_ =>
176-
panic!(
177-
"Unsupported memory ordering combination ({read_ordering:?}, {write_ordering:?})"
178-
),
174+
(AtomicReadOrd::SeqCst, _) => AtomicRwOrd::SeqCst,
175+
(_, AtomicWriteOrd::SeqCst) => AtomicRwOrd::SeqCst,
179176
}
180177
}
181178

src/concurrency/genmc/mod.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,8 +402,20 @@ impl GenmcCtx {
402402

403403
// FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`.
404404
let (effective_failure_ordering, _) = upgraded_success_ordering.split_memory_orderings();
405-
// Return a warning if the actual orderings don't match the upgraded ones.
406-
if success != upgraded_success_ordering || effective_failure_ordering != fail {
405+
406+
// Return a warning if we cannot explore all behaviors of this operation.
407+
// Only emit this if the operation is "in user code": walk up across `#[track_caller]`
408+
// frames, then check if the next frame is local.
409+
let show_warning = || {
410+
ecx.active_thread_stack()
411+
.iter()
412+
.rev()
413+
.find(|f| !f.instance().def.requires_caller_location(*ecx.tcx))
414+
.is_none_or(|f| ecx.machine.is_local(f.instance()))
415+
};
416+
if (success != upgraded_success_ordering || effective_failure_ordering != fail)
417+
&& show_warning()
418+
{
407419
static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new();
408420
ecx.dedup_diagnostic(&DEDUP, |_first| {
409421
NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch {
@@ -415,7 +427,7 @@ impl GenmcCtx {
415427
});
416428
}
417429
// FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`.
418-
if can_fail_spuriously {
430+
if can_fail_spuriously && show_warning() {
419431
static DEDUP: SpanDedupDiagnostic = SpanDedupDiagnostic::new();
420432
ecx.dedup_diagnostic(&DEDUP, |_first| NonHaltingDiagnostic::GenmcCompareExchangeWeak);
421433
}
Lines changed: 1 addition & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,4 @@
11
Running GenMC Verification...
2-
warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access.
3-
--> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC
4-
|
5-
LL | || self
6-
| ________________^
7-
LL | | .state
8-
LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed)
9-
| |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code
10-
|
11-
= note: BACKTRACE:
12-
= note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC
13-
= note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC
14-
= note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
15-
= note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
16-
= note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::<std::option::Option<std::ffi::OsString>>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
17-
= note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::<std::option::Option<std::ffi::OsString>>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
18-
= note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
19-
= note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC
20-
= note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC
21-
= note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
22-
note: inside `miri_start`
23-
--> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC
24-
|
25-
LL | let handle = std::thread::spawn(move || {
26-
| __________________^
27-
LL | | let guard = guard; // avoid field capturing
28-
LL | | drop(guard);
29-
LL | | });
30-
| |______^
31-
32-
warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
33-
--> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC
34-
|
35-
LL | || self
36-
| ________________^
37-
LL | | .state
38-
LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed)
39-
| |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code
40-
|
41-
= note: BACKTRACE:
42-
= note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC
43-
= note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC
44-
= note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
45-
= note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
46-
= note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::<std::option::Option<std::ffi::OsString>>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
47-
= note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::<std::option::Option<std::ffi::OsString>>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC
48-
= note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC
49-
= note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC
50-
= note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC
51-
= note: inside closure at RUSTLIB/std/src/thread/lifecycle.rs:LL:CC
52-
note: inside `miri_start`
53-
--> tests/genmc/fail/shims/mutex_diff_thread_unlock.rs:LL:CC
54-
|
55-
LL | let handle = std::thread::spawn(move || {
56-
| __________________^
57-
LL | | let guard = guard; // avoid field capturing
58-
LL | | drop(guard);
59-
LL | | });
60-
| |______^
61-
622
error: Undefined Behavior: Invalid unlock() operation
633
--> RUSTLIB/std/src/sync/poison/mutex.rs:LL:CC
644
|
@@ -82,5 +22,5 @@ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a
8222

8323
note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
8424

85-
error: aborting due to 1 previous error; 2 warnings emitted
25+
error: aborting due to 1 previous error
8626

tests/genmc/pass/atomics/cas_simple.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,10 @@ fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
3030
if 2 != VALUE.load(SeqCst) {
3131
std::process::abort()
3232
}
33+
34+
// Check that we emit warnings for cases that are not fully supported.
35+
let _ = VALUE.compare_exchange(99, 99, SeqCst, Relaxed);
36+
let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst);
37+
3338
0
3439
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,20 @@
11
Running GenMC Verification...
2+
warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'SeqCst', the failure ordering 'Relaxed' is treated like 'SeqCst'. Miri with GenMC might miss bugs related to this memory access.
3+
--> tests/genmc/pass/atomics/cas_simple.rs:LL:CC
4+
|
5+
LL | let _ = VALUE.compare_exchange(99, 99, SeqCst, Relaxed);
6+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
7+
8+
warning: GenMC currently does not model the failure ordering for `compare_exchange`. Success ordering 'Relaxed' was upgraded to 'SeqCst' to match failure ordering 'SeqCst'. Miri with GenMC might miss bugs related to this memory access.
9+
--> tests/genmc/pass/atomics/cas_simple.rs:LL:CC
10+
|
11+
LL | let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst);
12+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
13+
14+
warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures.
15+
--> tests/genmc/pass/atomics/cas_simple.rs:LL:CC
16+
|
17+
LL | let _ = VALUE.compare_exchange_weak(99, 99, Relaxed, SeqCst);
18+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
19+
220
Verification complete with 1 executions. No errors found.

0 commit comments

Comments
 (0)