Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/tools/miri/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,6 @@ degree documented below):
- `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite.
- `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite.
- `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works.
- `wasi`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works.
- For targets on other operating systems, Miri might fail before even reaching the `main` function.

However, even for targets that we do support, the degree of support for accessing platform APIs
Expand Down
1 change: 0 additions & 1 deletion src/tools/miri/ci/ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,6 @@ case $HOST_TARGET in
BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator
UNIX="hello 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=aarch64-linux-android run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency epoll eventfd
TEST_TARGET=wasm32-wasip2 run_tests_minimal $BASIC hello wasm
TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std
;;
Expand Down
4 changes: 2 additions & 2 deletions src/tools/miri/genmc-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ mod downloading {
use super::GENMC_DOWNLOAD_PATH;

/// The GenMC repository the we get our commit from.
pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git";
pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/MPI-SWS/genmc.git";
/// The GenMC commit we depend on. It must be available on the specified GenMC repository.
pub(crate) const GENMC_COMMIT: &str = "ce775ccd7866db820fa12ffca66463087a11dd96";
pub(crate) const GENMC_COMMIT: &str = "d9527280bb99f1cef64326b1803ffd952e3880df";

/// Ensure that a local GenMC repo is present and set to the correct commit.
/// Return the path of the GenMC repo and whether the checked out commit was changed.
Expand Down
8 changes: 4 additions & 4 deletions src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -261,27 +261,27 @@ namespace GenmcScalarExt {
inline GenmcScalar uninit() {
return GenmcScalar {
.value = 0,
.extra = 0,
.provenance = 0,
.is_init = false,
};
}

inline GenmcScalar from_sval(SVal sval) {
return GenmcScalar {
.value = sval.get(),
.extra = sval.getExtra(),
.provenance = sval.getProvenance(),
.is_init = true,
};
}

inline SVal to_sval(GenmcScalar scalar) {
ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n");
return SVal(scalar.value, scalar.extra);
return SVal(scalar.value, scalar.provenance);
}

inline std::optional<SVal> try_to_sval(GenmcScalar scalar) {
if (scalar.is_init)
return { SVal(scalar.value, scalar.extra) };
return { SVal(scalar.value, scalar.provenance) };
return std::nullopt;
}
} // namespace GenmcScalarExt
Expand Down
11 changes: 6 additions & 5 deletions src/tools/miri/genmc-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ pub fn create_genmc_driver_handle(
}

impl GenmcScalar {
pub const UNINIT: Self = Self { value: 0, extra: 0, is_init: false };
pub const UNINIT: Self = Self { value: 0, provenance: 0, is_init: false };

pub const fn from_u64(value: u64) -> Self {
Self { value, extra: 0, is_init: true }
Self { value, provenance: 0, is_init: true }
}

pub const fn has_provenance(&self) -> bool {
self.extra != 0
self.provenance != 0
}
}

Expand Down Expand Up @@ -172,8 +172,9 @@ mod ffi {
value: u64,
/// This is zero for integer values. For pointers, this encodes the provenance by
/// storing the base address of the allocation that this pointer belongs to.
/// Operations on `SVal` in GenMC (e.g., `fetch_add`) preserve the `extra` of the left argument (`left.fetch_add(right, ...)`).
extra: u64,
/// Operations on `SVal` in GenMC (e.g., `fetch_add`) preserve the `provenance` of the left
/// argument (`left.fetch_add(right, ...)`).
provenance: u64,
/// Indicates whether this value is initialized. If this is `false`, the other fields do not matter.
/// (Ideally we'd use `std::optional` but CXX does not support that.)
is_init: bool,
Expand Down
2 changes: 1 addition & 1 deletion src/tools/miri/rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
292be5c7c05138d753bbd4b30db7a3f1a5c914f7
5f9dd05862d2e4bceb3be1031b6c936e35671501
8 changes: 3 additions & 5 deletions src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,7 @@ mod propagation_optimization_checks {
impl Exhaustive for AccessRelatedness {
fn exhaustive() -> Box<dyn Iterator<Item = Self>> {
use AccessRelatedness::*;
Box::new(vec![This, StrictChildAccess, AncestorAccess, CousinAccess].into_iter())
Box::new(vec![ForeignAccess, LocalAccess].into_iter())
}
}

Expand Down Expand Up @@ -716,13 +716,11 @@ mod propagation_optimization_checks {
// We now assert it is idempotent, and never causes UB.
// First, if the SIFA includes foreign reads, assert it is idempotent under foreign reads.
if access >= IdempotentForeignAccess::Read {
// We use `CousinAccess` here. We could also use `AncestorAccess`, since `transition::perform_access` treats these the same.
// The only place they are treated differently is in protector end accesses, but these are not handled here.
assert_eq!(perm, transition::perform_access(AccessKind::Read, AccessRelatedness::CousinAccess, perm, prot).unwrap());
assert_eq!(perm, transition::perform_access(AccessKind::Read, AccessRelatedness::ForeignAccess, perm, prot).unwrap());
}
// Then, if the SIFA includes foreign writes, assert it is idempotent under foreign writes.
if access >= IdempotentForeignAccess::Write {
assert_eq!(perm, transition::perform_access(AccessKind::Write, AccessRelatedness::CousinAccess, perm, prot).unwrap());
assert_eq!(perm, transition::perform_access(AccessKind::Write, AccessRelatedness::ForeignAccess, perm, prot).unwrap());
}
}
}
Expand Down
75 changes: 36 additions & 39 deletions src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::borrow_tracker::tree_borrows::diagnostics::{
};
use crate::borrow_tracker::tree_borrows::foreign_access_skipping::IdempotentForeignAccess;
use crate::borrow_tracker::tree_borrows::perms::PermTransition;
use crate::borrow_tracker::tree_borrows::unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap};
use crate::borrow_tracker::tree_borrows::unimap::{UniIndex, UniKeyMap, UniValMap};
use crate::borrow_tracker::{GlobalState, ProtectorKind};
use crate::*;

Expand Down Expand Up @@ -265,13 +265,15 @@ pub(super) struct Node {
}

/// Data given to the transition function
struct NodeAppArgs<'node> {
/// Node on which the transition is currently being applied
node: &'node mut Node,
/// Mutable access to its permissions
perm: UniEntry<'node, LocationState>,
/// Relative position of the access
struct NodeAppArgs<'visit> {
/// The index of the current node.
idx: UniIndex,
/// Relative position of the access.
rel_pos: AccessRelatedness,
/// The node map of this tree.
nodes: &'visit mut UniValMap<Node>,
/// The permissions map of this tree.
perms: &'visit mut UniValMap<LocationState>,
}
/// Data given to the error handler
struct ErrHandlerArgs<'node, InErr> {
Expand Down Expand Up @@ -348,8 +350,7 @@ where
idx: UniIndex,
rel_pos: AccessRelatedness,
) -> ContinueTraversal {
let node = this.nodes.get_mut(idx).unwrap();
let args = NodeAppArgs { node, perm: this.perms.entry(idx), rel_pos };
let args = NodeAppArgs { idx, rel_pos, nodes: this.nodes, perms: this.perms };
(self.f_continue)(&args)
}

Expand All @@ -359,16 +360,14 @@ where
idx: UniIndex,
rel_pos: AccessRelatedness,
) -> Result<(), OutErr> {
let node = this.nodes.get_mut(idx).unwrap();
(self.f_propagate)(NodeAppArgs { node, perm: this.perms.entry(idx), rel_pos }).map_err(
|error_kind| {
(self.f_propagate)(NodeAppArgs { idx, rel_pos, nodes: this.nodes, perms: this.perms })
.map_err(|error_kind| {
(self.err_builder)(ErrHandlerArgs {
error_kind,
conflicting_info: &this.nodes.get(idx).unwrap().debug_info,
accessed_info: &this.nodes.get(self.initial).unwrap().debug_info,
})
},
)
})
}

fn go_upwards_from_accessed(
Expand All @@ -386,14 +385,14 @@ where
// be handled differently here compared to the further parents
// of `accesssed_node`.
{
self.propagate_at(this, accessed_node, AccessRelatedness::This)?;
self.propagate_at(this, accessed_node, AccessRelatedness::LocalAccess)?;
if matches!(visit_children, ChildrenVisitMode::VisitChildrenOfAccessed) {
let accessed_node = this.nodes.get(accessed_node).unwrap();
// We `rev()` here because we reverse the entire stack later.
for &child in accessed_node.children.iter().rev() {
self.stack.push((
child,
AccessRelatedness::AncestorAccess,
AccessRelatedness::ForeignAccess,
RecursionState::BeforeChildren,
));
}
Expand All @@ -404,7 +403,7 @@ where
// not the subtree that contains the accessed node.
let mut last_node = accessed_node;
while let Some(current) = this.nodes.get(last_node).unwrap().parent {
self.propagate_at(this, current, AccessRelatedness::StrictChildAccess)?;
self.propagate_at(this, current, AccessRelatedness::LocalAccess)?;
let node = this.nodes.get(current).unwrap();
// We `rev()` here because we reverse the entire stack later.
for &child in node.children.iter().rev() {
Expand All @@ -413,7 +412,7 @@ where
}
self.stack.push((
child,
AccessRelatedness::CousinAccess,
AccessRelatedness::ForeignAccess,
RecursionState::BeforeChildren,
));
}
Expand Down Expand Up @@ -741,7 +740,9 @@ impl<'tcx> Tree {
// visit all children, skipping none
|_| ContinueTraversal::Recurse,
|args: NodeAppArgs<'_>| -> Result<(), TransitionError> {
let NodeAppArgs { node, perm, .. } = args;
let node = args.nodes.get(args.idx).unwrap();
let perm = args.perms.entry(args.idx);

let perm =
perm.get().copied().unwrap_or_else(|| node.default_location_state());
if global.borrow().protected_tags.get(&node.tag)
Expand Down Expand Up @@ -812,32 +813,34 @@ impl<'tcx> Tree {
// `perms_range` is only for diagnostics (it is the range of
// the `RangeMap` on which we are currently working).
let node_skipper = |access_kind: AccessKind, args: &NodeAppArgs<'_>| -> ContinueTraversal {
let NodeAppArgs { node, perm, rel_pos } = args;
let node = args.nodes.get(args.idx).unwrap();
let perm = args.perms.get(args.idx);

let old_state = perm.get().copied().unwrap_or_else(|| node.default_location_state());
old_state.skip_if_known_noop(access_kind, *rel_pos)
let old_state = perm.copied().unwrap_or_else(|| node.default_location_state());
old_state.skip_if_known_noop(access_kind, args.rel_pos)
};
let node_app = |perms_range: Range<u64>,
access_kind: AccessKind,
access_cause: diagnostics::AccessCause,
args: NodeAppArgs<'_>|
-> Result<(), TransitionError> {
let NodeAppArgs { node, mut perm, rel_pos } = args;
let node = args.nodes.get_mut(args.idx).unwrap();
let mut perm = args.perms.entry(args.idx);

let old_state = perm.or_insert(node.default_location_state());

// Call this function now, which ensures it is only called when
// `skip_if_known_noop` returns `Recurse`, due to the contract of
// `traverse_this_parents_children_other`.
old_state.record_new_access(access_kind, rel_pos);
old_state.record_new_access(access_kind, args.rel_pos);

let protected = global.borrow().protected_tags.contains_key(&node.tag);
let transition = old_state.perform_access(access_kind, rel_pos, protected)?;
let transition = old_state.perform_access(access_kind, args.rel_pos, protected)?;
// Record the event as part of the history
if !transition.is_noop() {
node.debug_info.history.push(diagnostics::Event {
transition,
is_foreign: rel_pos.is_foreign(),
is_foreign: args.rel_pos.is_foreign(),
access_cause,
access_range: access_range_and_kind.map(|x| x.0),
transition_range: perms_range,
Expand Down Expand Up @@ -1075,24 +1078,18 @@ impl VisitProvenance for Tree {
/// Relative position of the access
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AccessRelatedness {
/// The accessed pointer is the current one
This,
/// The accessed pointer is a (transitive) child of the current one.
// Current pointer is excluded (unlike in some other places of this module
// where "child" is inclusive).
StrictChildAccess,
/// The accessed pointer is a (transitive) parent of the current one.
// Current pointer is excluded.
AncestorAccess,
/// The accessed pointer is neither of the above.
// It's a cousin/uncle/etc., something in a side branch.
CousinAccess,
/// The access happened either through the node itself or one of
/// its transitive children.
LocalAccess,
/// The access happened through this nodes ancestor or through
/// a sibling/cousin/uncle/etc.
ForeignAccess,
}

impl AccessRelatedness {
/// Check that access is either Ancestor or Distant, i.e. not
/// a transitive child (initial pointer included).
pub fn is_foreign(self) -> bool {
matches!(self, AccessRelatedness::AncestorAccess | AccessRelatedness::CousinAccess)
matches!(self, AccessRelatedness::ForeignAccess)
}
}
12 changes: 6 additions & 6 deletions src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,15 +263,15 @@ mod spurious_read {
match xy_rel {
RelPosXY::MutuallyForeign =>
match self {
PtrSelector::X => (This, CousinAccess),
PtrSelector::Y => (CousinAccess, This),
PtrSelector::Other => (CousinAccess, CousinAccess),
PtrSelector::X => (LocalAccess, ForeignAccess),
PtrSelector::Y => (ForeignAccess, LocalAccess),
PtrSelector::Other => (ForeignAccess, ForeignAccess),
},
RelPosXY::XChildY =>
match self {
PtrSelector::X => (This, StrictChildAccess),
PtrSelector::Y => (AncestorAccess, This),
PtrSelector::Other => (CousinAccess, CousinAccess),
PtrSelector::X => (LocalAccess, LocalAccess),
PtrSelector::Y => (ForeignAccess, LocalAccess),
PtrSelector::Other => (ForeignAccess, ForeignAccess),
},
}
}
Expand Down
15 changes: 8 additions & 7 deletions src/tools/miri/src/concurrency/genmc/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub fn scalar_to_genmc_scalar<'tcx>(
rustc_const_eval::interpret::Scalar::Int(scalar_int) => {
// FIXME(genmc): Add u128 support once GenMC supports it.
let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap();
GenmcScalar { value, extra: 0, is_init: true }
GenmcScalar { value, provenance: 0, is_init: true }
}
rustc_const_eval::interpret::Scalar::Ptr(pointer, size) => {
// FIXME(genmc,borrow tracking): Borrow tracking information is lost.
Expand All @@ -69,7 +69,7 @@ pub fn scalar_to_genmc_scalar<'tcx>(
let base_addr = ecx.addr_from_alloc_id(alloc_id, None)?;
// Add the base_addr alloc_id pair to the map.
genmc_ctx.exec_state.genmc_shared_allocs_map.borrow_mut().insert(base_addr, alloc_id);
GenmcScalar { value: addr.bytes(), extra: base_addr, is_init: true }
GenmcScalar { value: addr.bytes(), provenance: base_addr, is_init: true }
}
})
}
Expand All @@ -84,16 +84,17 @@ pub fn genmc_scalar_to_scalar<'tcx>(
scalar: GenmcScalar,
size: Size,
) -> InterpResult<'tcx, Scalar> {
// If `extra` is zero, we have a regular integer.
if scalar.extra == 0 {
// If `provenance` is zero, we have a regular integer.
if scalar.provenance == 0 {
// NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated.
// FIXME(genmc): GenMC should be doing the truncation, not Miri.
let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size);
return interp_ok(Scalar::from(value_scalar_int));
}
// `extra` is non-zero, we have a pointer.
// When we get a pointer from GenMC, then we must have sent it to GenMC before in the same execution (since the reads-from relation is always respected).
let alloc_id = genmc_ctx.exec_state.genmc_shared_allocs_map.borrow()[&scalar.extra];
// `provenance` is non-zero, we have a pointer.
// When we get a pointer from GenMC, then we must have sent it to GenMC before in the same
// execution (since the reads-from relation is always respected).
let alloc_id = genmc_ctx.exec_state.genmc_shared_allocs_map.borrow()[&scalar.provenance];
// FIXME(genmc,borrow tracking): Borrow tracking not yet supported.
let provenance = machine::Provenance::Concrete { alloc_id, tag: BorTag::default() };
let ptr = interpret::Pointer::new(provenance, Size::from_bytes(scalar.value));
Expand Down
Loading
Loading