Skip to content

Commit 8d2b142

Browse files
authored
Rollup merge of #147636 - RalfJung:miri, r=RalfJung
miri subtree update Subtree update of `miri` to rust-lang/miri@47d6568. Created using https://github.com/rust-lang/josh-sync. r? `@ghost`
2 parents 81dd408 + 15716a3 commit 8d2b142

File tree

231 files changed

+3731
-704
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

231 files changed

+3731
-704
lines changed

src/tools/miri/.github/workflows/ci.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,9 @@ jobs:
231231
exit ${exitcode}
232232
fi
233233
234+
# Store merge commit message
235+
git log -1 --pretty=%B > message.txt
236+
234237
# Format changes
235238
./miri toolchain
236239
./miri fmt --check || (./miri fmt && git commit -am "fmt")
@@ -239,7 +242,7 @@ jobs:
239242
BRANCH="rustup-$(date -u +%Y-%m-%d)"
240243
git switch -c $BRANCH
241244
git push -u origin $BRANCH
242-
gh pr create -B master --title 'Automatic Rustup' --body "Update \`rustc\` to https://github.com/rust-lang/rust/commit/$(cat rust-version)."
245+
gh pr create -B master --title 'Automatic Rustup' --body-file message.txt
243246
env:
244247
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
245248

src/tools/miri/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,21 @@ such races.
246246

247247
Note: `cargo-nextest` does not support doctests, see https://github.com/nextest-rs/nextest/issues/16
248248

249+
### Directly invoking the `miri` driver
250+
251+
The recommended way to invoke Miri is via `cargo miri`. Directly invoking the underlying `miri`
252+
driver is not supported, which is why that binary is not even installed into the PATH. However, if
253+
you need to run Miri on many small tests and want to invoke it directly like you would invoke
254+
`rustc`, that is still possible with a bit of extra effort:
255+
256+
```sh
257+
# one-time setup
258+
cargo +nightly miri setup
259+
SYSROOT=$(cargo +nightly miri setup --print-sysroot)
260+
# per file
261+
~/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/miri --sysroot "$SYSROOT" file.rs
262+
```
263+
249264
### Common Problems
250265

251266
When using the above instructions, you may encounter a number of confusing compiler

src/tools/miri/doc/genmc.md

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
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+
5+
36
[GenMC](https://github.com/MPI-SWS/genmc) is a stateless model checker for exploring concurrent executions of a program.
47
Miri-GenMC integrates that model checker into Miri.
58

6-
**NOTE: Currently, no actual GenMC functionality is part of Miri, this is still WIP.**
9+
Miri in GenMC mode takes a program as input like regular Miri, but instead of running it once, the program is executed repeatedly, until all possible executions allowed by the Rust memory model are explored.
10+
This includes all possible thread interleavings and all allowed return values for atomic operations, including cases that are very rare to encounter on actual hardware.
11+
(However, this does not include other sources of non-determinism, such as the absolute addresses of allocations.
12+
It is hence still possible to have latent bugs in a test case even if they passed GenMC.)
713

8-
<!-- FIXME(genmc): add explanation. -->
14+
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).
16+
17+
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).
18+
Large programs may not be verifiable in a reasonable amount of time.
919

1020
## Usage
1121

1222
For testing/developing Miri-GenMC:
23+
- install all [dependencies required by GenMC](https://github.com/MPI-SWS/genmc?tab=readme-ov-file#dependencies)
1324
- clone the Miri repo.
1425
- build Miri-GenMC with `./miri build --features=genmc`.
1526
- OR: install Miri-GenMC in the current system with `./miri install --features=genmc`
@@ -50,6 +61,66 @@ Note that `cargo miri test` in GenMC mode is currently not supported.
5061

5162
<!-- FIXME(genmc): add tips for using Miri-GenMC more efficiently. -->
5263

64+
### Eliminating unbounded loops
65+
66+
As mentioned above, GenMC requires all loops to be bounded.
67+
Otherwise, it is not possible to exhaustively explore all executions.
68+
Currently, Miri-GenMC has no support for automatically bounding loops, so this needs to be done manually.
69+
70+
#### Bounding loops without side effects
71+
72+
The easiest case is that of a loop that simply spins until it observes a certain condition, without any side effects.
73+
Such loops can be limited to one iteration, as demonstrated by the following example:
74+
75+
```rust
76+
#[cfg(miri)]
77+
unsafe extern "Rust" {
78+
// This is a special function that Miri provides.
79+
// It blocks the thread calling this function if the condition is false.
80+
pub unsafe fn miri_genmc_assume(condition: bool);
81+
}
82+
83+
// This functions loads an atomic boolean in a loop until it is true.
84+
// GenMC will explore all executions where this does 1, 2, ..., ∞ loads, which means the verification will never terminate.
85+
fn spin_until_true(flag: &AtomicBool) {
86+
while !flag.load(Relaxed) {
87+
std::hint::spin_loop();
88+
}
89+
}
90+
91+
// By replacing this loop with an assume statement, the only executions that will be explored are those with exactly 1 load that observes the expected value.
92+
// Incorrect use of assume statements can lead GenMC to miss important executions, so it is marked `unsafe`.
93+
fn spin_until_true_genmc(flag: &AtomicBool) {
94+
unsafe { miri_genmc_assume(flag.load(Relaxed)) };
95+
}
96+
```
97+
98+
#### Bounding loops with side effects
99+
100+
Some loops do contain side effects, meaning the number of explored iterations affects the rest of the program.
101+
Replacing the loop with one iteration like we did above would mean we miss all those possible executions.
102+
103+
In such a case, the loop can be limited to a fixed number of iterations instead.
104+
The choice of iteration limit trades off verification time for possibly missing bugs requiring more iterations.
105+
106+
```rust
107+
/// The loop in this function has a side effect, which is to increment the counter for the number of iterations.
108+
/// Instead of replacing the loop entirely (which would miss all executions with `count > 0`), we limit the loop to at most 3 iterations.
109+
fn count_until_true_genmc(flag: &AtomicBool) -> u64 {
110+
let mut count = 0;
111+
while !flag.load(Relaxed) {
112+
count += 1;
113+
std::hint::spin_loop();
114+
// Any execution that takes more than 3 iterations will not be explored.
115+
unsafe { miri_genmc_assume(count <= 3) };
116+
}
117+
count
118+
}
119+
```
120+
121+
<!-- FIXME: update the code above once Miri supports a loop bounding features like GenMC's `--unroll=N`. -->
122+
<!-- FIXME: update this section once Miri-GenMC supports automatic program transformations (like spinloop-assume replacement). -->
123+
53124
## Limitations
54125

55126
Some or all of these limitations might get removed in the future:

src/tools/miri/genmc-sys/build.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ mod downloading {
2828
/// The GenMC repository the we get our commit from.
2929
pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git";
3030
/// The GenMC commit we depend on. It must be available on the specified GenMC repository.
31-
pub(crate) const GENMC_COMMIT: &str = "af9cc9ccd5d412b16defc35dbf36571c63a19c76";
31+
pub(crate) const GENMC_COMMIT: &str = "ce775ccd7866db820fa12ffca66463087a11dd96";
3232

3333
/// Ensure that a local GenMC repo is present and set to the correct commit.
3434
/// Return the path of the GenMC repo and whether the checked out commit was changed.
@@ -227,12 +227,17 @@ fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) {
227227
// These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated.
228228
let definitions = llvm_definitions.split(";");
229229

230+
// These are all the C++ files we need to compile, which needs to be updated if more C++ files are added to Miri.
231+
// We use absolute paths since relative paths can confuse IDEs when attempting to go-to-source on a path in a compiler error.
232+
let cpp_files_base_path = Path::new("cpp/src/");
230233
let cpp_files = [
231-
"./cpp/src/MiriInterface/EventHandling.cpp",
232-
"./cpp/src/MiriInterface/Exploration.cpp",
233-
"./cpp/src/MiriInterface/Setup.cpp",
234-
"./cpp/src/MiriInterface/ThreadManagement.cpp",
235-
];
234+
"MiriInterface/EventHandling.cpp",
235+
"MiriInterface/Exploration.cpp",
236+
"MiriInterface/Mutex.cpp",
237+
"MiriInterface/Setup.cpp",
238+
"MiriInterface/ThreadManagement.cpp",
239+
]
240+
.map(|file| std::path::absolute(cpp_files_base_path.join(file)).unwrap());
236241

237242
let mut bridge = cxx_build::bridge("src/lib.rs");
238243
// FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file.

src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
// GenMC headers:
1414
#include "ExecutionGraph/EventLabel.hpp"
15-
#include "Static/ModuleID.hpp"
1615
#include "Support/MemOrdering.hpp"
1716
#include "Support/RMWOps.hpp"
1817
#include "Verification/Config.hpp"
@@ -36,6 +35,7 @@ struct LoadResult;
3635
struct StoreResult;
3736
struct ReadModifyWriteResult;
3837
struct CompareExchangeResult;
38+
struct MutexLockResult;
3939

4040
// GenMC uses `int` for its thread IDs.
4141
using ThreadId = int;
@@ -126,14 +126,24 @@ struct MiriGenmcShim : private GenMCDriver {
126126

127127
/**** Memory (de)allocation ****/
128128
auto handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t;
129-
void handle_free(ThreadId thread_id, uint64_t address);
129+
auto handle_free(ThreadId thread_id, uint64_t address) -> bool;
130130

131131
/**** Thread management ****/
132132
void handle_thread_create(ThreadId thread_id, ThreadId parent_id);
133133
void handle_thread_join(ThreadId thread_id, ThreadId child_id);
134134
void handle_thread_finish(ThreadId thread_id, uint64_t ret_val);
135135
void handle_thread_kill(ThreadId thread_id);
136136

137+
/**** Blocking instructions ****/
138+
/// Inform GenMC that the thread should be blocked.
139+
void handle_assume_block(ThreadId thread_id, AssumeType assume_type);
140+
141+
/**** Mutex handling ****/
142+
auto handle_mutex_lock(ThreadId thread_id, uint64_t address, uint64_t size) -> MutexLockResult;
143+
auto handle_mutex_try_lock(ThreadId thread_id, uint64_t address, uint64_t size)
144+
-> MutexLockResult;
145+
auto handle_mutex_unlock(ThreadId thread_id, uint64_t address, uint64_t size) -> StoreResult;
146+
137147
/***** Exploration related functionality *****/
138148

139149
/** Ask the GenMC scheduler for a new thread to schedule and return whether the execution is
@@ -207,9 +217,10 @@ struct MiriGenmcShim : private GenMCDriver {
207217
* Automatically calls `inc_pos` and `dec_pos` where needed for the given thread.
208218
*/
209219
template <EventLabel::EventLabelKind k, typename... Ts>
210-
auto handle_load_reset_if_none(ThreadId tid, Ts&&... params) -> HandleResult<SVal> {
220+
auto handle_load_reset_if_none(ThreadId tid, std::optional<SVal> old_val, Ts&&... params)
221+
-> HandleResult<SVal> {
211222
const auto pos = inc_pos(tid);
212-
const auto ret = GenMCDriver::handleLoad<k>(pos, std::forward<Ts>(params)...);
223+
const auto ret = GenMCDriver::handleLoad<k>(pos, old_val, std::forward<Ts>(params)...);
213224
// If we didn't get a value, we have to reset the index of the current thread.
214225
if (!std::holds_alternative<SVal>(ret)) {
215226
dec_pos(tid);
@@ -250,20 +261,28 @@ namespace GenmcScalarExt {
250261
inline GenmcScalar uninit() {
251262
return GenmcScalar {
252263
.value = 0,
264+
.extra = 0,
253265
.is_init = false,
254266
};
255267
}
256268

257269
inline GenmcScalar from_sval(SVal sval) {
258270
return GenmcScalar {
259271
.value = sval.get(),
272+
.extra = sval.getExtra(),
260273
.is_init = true,
261274
};
262275
}
263276

264277
inline SVal to_sval(GenmcScalar scalar) {
265278
ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n");
266-
return SVal(scalar.value);
279+
return SVal(scalar.value, scalar.extra);
280+
}
281+
282+
inline std::optional<SVal> try_to_sval(GenmcScalar scalar) {
283+
if (scalar.is_init)
284+
return { SVal(scalar.value, scalar.extra) };
285+
return std::nullopt;
267286
}
268287
} // namespace GenmcScalarExt
269288

@@ -342,4 +361,22 @@ inline CompareExchangeResult from_error(std::unique_ptr<std::string> error) {
342361
}
343362
} // namespace CompareExchangeResultExt
344363

364+
namespace MutexLockResultExt {
365+
inline MutexLockResult ok(bool is_lock_acquired) {
366+
return MutexLockResult { /* error: */ nullptr, /* is_reset: */ false, is_lock_acquired };
367+
}
368+
369+
inline MutexLockResult reset() {
370+
return MutexLockResult { /* error: */ nullptr,
371+
/* is_reset: */ true,
372+
/* is_lock_acquired: */ false };
373+
}
374+
375+
inline MutexLockResult from_error(std::unique_ptr<std::string> error) {
376+
return MutexLockResult { /* error: */ std::move(error),
377+
/* is_reset: */ false,
378+
/* is_lock_acquired: */ false };
379+
}
380+
} // namespace MutexLockResultExt
381+
345382
#endif /* GENMC_MIRI_INTERFACE_HPP */

0 commit comments

Comments
 (0)