Skip to content

Commit 36716dc

Browse files
committed
add a test ensuring that we enforce noalias on accesses
1 parent ee674e7 commit 36716dc

File tree

2 files changed

+37
-7
lines changed

2 files changed

+37
-7
lines changed

Diff for: src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ mod propagation_optimization_checks {
445445
}
446446

447447
#[test]
448-
fn foreign_read_is_noop_after_write() {
448+
fn foreign_read_is_noop_after_foreign_write() {
449449
use transition::*;
450450
let old_access = AccessKind::Write;
451451
let new_access = AccessKind::Read;

Diff for: src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs

+36-6
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ impl LocationState {
110110

111111
// Helper to optimize the tree traversal.
112112
// The optimization here consists of observing thanks to the tests
113-
// `foreign_read_is_noop_after_write` and `all_transitions_idempotent`,
113+
// `foreign_read_is_noop_after_foreign_write` and `all_transitions_idempotent`,
114114
// that there are actually just three possible sequences of events that can occur
115115
// in between two child accesses that produce different results.
116116
//
@@ -139,7 +139,7 @@ impl LocationState {
139139
let new_access_noop = match (self.latest_foreign_access, access_kind) {
140140
// Previously applied transition makes the new one a guaranteed
141141
// noop in the two following cases:
142-
// (1) justified by `foreign_read_is_noop_after_write`
142+
// (1) justified by `foreign_read_is_noop_after_foreign_write`
143143
(Some(AccessKind::Write), AccessKind::Read) => true,
144144
// (2) justified by `all_transitions_idempotent`
145145
(Some(old), new) if old == new => true,
@@ -670,7 +670,8 @@ impl AccessRelatedness {
670670
mod commutation_tests {
671671
use super::*;
672672
impl LocationState {
673-
pub fn all_without_access() -> impl Iterator<Item = Self> {
673+
pub fn all() -> impl Iterator<Item = Self> {
674+
// We keep `latest_foreign_access` at `None` as that's just a cache.
674675
Permission::all().flat_map(|permission| {
675676
[false, true].into_iter().map(move |initialized| {
676677
Self { permission, initialized, latest_foreign_access: None }
@@ -695,12 +696,12 @@ mod commutation_tests {
695696
// Any protector state works, but we can't move reads across function boundaries
696697
// so the two read accesses occur under the same protector.
697698
for &protected in &[true, false] {
698-
for loc in LocationState::all_without_access() {
699+
for loc in LocationState::all() {
699700
// Apply 1 then 2. Failure here means that there is UB in the source
700701
// and we skip the check in the target.
701702
let mut loc12 = loc;
702-
let Ok(_) = loc12.perform_access(kind, rel1, protected) else { continue; };
703-
let Ok(_) = loc12.perform_access(kind, rel2, protected) else { continue; };
703+
let Ok(_) = loc12.perform_access(kind, rel1, protected) else { continue };
704+
let Ok(_) = loc12.perform_access(kind, rel2, protected) else { continue };
704705

705706
// If 1 followed by 2 succeeded, then 2 followed by 1 must also succeed...
706707
let mut loc21 = loc;
@@ -718,4 +719,33 @@ mod commutation_tests {
718719
}
719720
}
720721
}
722+
723+
#[test]
724+
#[rustfmt::skip]
725+
// Ensure that of 2 accesses happen, one foreign and one a child, and we are protected, that we
726+
// get UB unless they are both reads.
727+
fn protected_enforces_noalias() {
728+
for rel1 in AccessRelatedness::all() {
729+
for rel2 in AccessRelatedness::all() {
730+
if rel1.is_foreign() == rel2.is_foreign() {
731+
// We want to check pairs of accesses where one is foreign and one is not.
732+
continue;
733+
}
734+
for kind1 in AccessKind::all() {
735+
for kind2 in AccessKind::all() {
736+
for mut state in LocationState::all() {
737+
let protected = true;
738+
let Ok(_) = state.perform_access(kind1, rel1, protected) else { continue };
739+
let Ok(_) = state.perform_access(kind2, rel2, protected) else { continue };
740+
// If these were both allowed, it must have been two reads.
741+
assert!(
742+
kind1 == AccessKind::Read && kind2 == AccessKind::Read,
743+
"failed to enforce noalias between two accesses that are not both reads"
744+
);
745+
}
746+
}
747+
}
748+
}
749+
}
750+
}
721751
}

0 commit comments

Comments
 (0)