Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #3846 properly, so that subtrees can be skipped again #4006

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
7 changes: 7 additions & 0 deletions bench-cargo-miri/string-replace/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions bench-cargo-miri/string-replace/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "string-replace"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
1 change: 1 addition & 0 deletions bench-cargo-miri/string-replace/data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"_id":"6724e6fc58417687afba2b85","index":0,"guid":"5c1bd108-2ee2-40bd-bce8-895c206409df","isActive":true,"balance":"$2,927.88","picture":"http://placehold.it/32x32","age":40,"eyeColor":"green","name":"Wynn Bradshaw","gender":"male","company":"ROTODYNE","email":"[email protected]","phone":"+1 (904) 559-3130","address":"287 Bergen Avenue, Sperryville, Alaska, 5392","about":"Adipisicing fugiat aute adipisicing qui esse cillum. Lorem consequat consectetur voluptate id pariatur nostrud incididunt aliquip incididunt laboris aliqua. Magna nulla adipisicing cupidatat ea velit aliquip magna duis duis sunt ipsum. Cillum labore mollit fugiat tempor dolor sit.\r\n","registered":"2017-01-26T01:28:10 -01:00","latitude":46.089504,"longitude":51.763723,"greeting":"Hello, Wynn Bradshaw! You have 6 unread messages.","favoriteFruit":"banana"},{"_id":"6724e6fce8619d86c0389ccf","index":1,"guid":"ced7fbb7-3b1a-419b-9fc7-d47582bbb3ea","isActive":true,"balance":"$1,856.38","picture":"http://placehold.it/32x32","age":21,"eyeColor":"brown","name":"Olsen Larsen","gender":"male","company":"VERBUS","email":"[email protected]","phone":"+1 (936) 480-3749","address":"370 Losee Terrace, Churchill, Maine, 4040","about":"Consequat Lorem in laboris fugiat veniam tempor eiusmod eu incididunt enim do et qui. Sit commodo eu excepteur cillum ex tempor commodo ex ex laboris esse. Aute aute nulla dolore dolor do. Irure esse proident nostrud non. Incididunt velit reprehenderit incididunt laboris do. Consequat nulla est id ex veniam tempor. Sit Lorem magna cillum aliquip irure quis sit minim anim.\r\n","registered":"2016-07-12T02:08:39 -02:00","latitude":-12.843628,"longitude":-124.143829,"greeting":"Hello, Olsen Larsen! You have 1 unread messages.","favoriteFruit":"apple"},{"_id":"6724e6fc01b471965ea560cf","index":2,"guid":"21fde9a3-13ba-46be-baed-fb503f668b9e","isActive":false,"balance":"$2,025.88","picture":"http://placehold.it/32x32","age":29,"eyeColor":"green","name":"Ramirez Kinney","gender":"male","company":"QUAREX","email":"[email protected]","phone":"+1 (852) 447-2930","address":"986 Cornelia Street, Oberlin, Texas, 362","about":"Minim ea proident quis eiusmod aliquip duis excepteur velit minim aute cupidatat. Esse qui ex aliquip laborum id reprehenderit. Anim dolore commodo deserunt laborum nulla duis. Sint quis anim mollit fugiat sit incididunt reprehenderit occaecat aliqua dolor. Ullamco ipsum eiusmod incididunt proident qui exercitation adipisicing voluptate elit aliquip. Tempor duis aute incididunt adipisicing.\r\n","registered":"2016-02-23T05:34:14 -01:00","latitude":-56.21645,"longitude":44.048129,"greeting":"Hello, Ramirez Kinney! You have 9 unread messages.","favoriteFruit":"banana"},{"_id":"6724e6fc3ea8e4182b9e170f","index":3,"guid":"46b20637-eecc-40db-87d7-03da9bcd1cea","isActive":true,"balance":"$3,399.31","picture":"http://placehold.it/32x32","age":39,"eyeColor":"brown","name":"Hansen Kaufman","gender":"male","company":"EVENTAGE","email":"[email protected]","phone":"+1 (827) 483-2303","address":"916 Brighton Court, Sunbury, New Mexico, 3804","about":"Nisi in voluptate aute ullamco ipsum proident fugiat veniam anim reprehenderit. In ad irure dolor labore culpa incididunt veniam mollit Lorem deserunt cupidatat incididunt. Aliquip aliquip proident ut culpa.\r\n","registered":"2023-10-18T07:03:48 -02:00","latitude":-40.239135,"longitude":49.802049,"greeting":"Hello, Hansen Kaufman! You have 10 unread messages.","favoriteFruit":"apple"},{"_id":"6724e6fc721f83a10cf2aa37","index":4,"guid":"3d23743b-1e82-474e-8f7a-855fa46170d1","isActive":false,"balance":"$1,967.87","picture":"http://placehold.it/32x32","age":35,"eyeColor":"green","name":"Imelda Stephens","gender":"female","company":"OHMNET","email":"[email protected]","phone":"+1 (893) 523-2400","address":"391 Wilson Street, Glidden, Kansas, 7226","about":"Officia sunt magna adipisicing id exercitation deserunt deserunt aliquip excepteur Lorem enim fugiat. Nulla culpa ut cupidatat excepteur do deserunt labore id eu laboris ullamco adipisicing ad. Et non nisi adipisicing minim aliquip ea ut qui adipisicing do laboris ex dolore duis.\r\n","registered":"2020-10-20T07:03:38 -02:00","latitude":0.348698,"longitude":-157.961956,"greeting":"Hello, Imelda Stephens! You have 2 unread messages.","favoriteFruit":"strawberry"},{"_id":"6724e6fc7ad7274b9f4c406c","index":5,"guid":"626292b1-ae84-4887-9e29-78e548cd24e6","isActive":true,"balance":"$1,577.44","picture":"http://placehold.it/32x32","age":40,"eyeColor":"brown","name":"Lynne Jarvis","gender":"female","company":"CORECOM","email":"[email protected]","phone":"+1 (899) 556-3876","address":"465 National Drive, Davenport, Palau, 9786","about":"Aliquip elit dolore sint quis do laboris exercitation elit aliqua eiusmod. Excepteur ad aliqua eiusmod incididunt tempor laboris officia consectetur sit. Cupidatat voluptate deserunt ut consectetur qui laborum duis elit incididunt occaecat laborum. Mollit aute velit officia amet aute minim fugiat sit laborum Lorem deserunt in. Exercitation eu sunt nulla adipisicing quis ea aute est. Lorem ea cillum ad labore quis minim et est laboris deserunt proident. Amet ut tempor laborum occaecat exercitation ullamco laborum adipisicing fugiat ea voluptate quis fugiat.\r\n","registered":"2018-11-03T03:53:15 -01:00","latitude":89.827087,"longitude":-136.882799,"greeting":"Hello, Lynne Jarvis! You have 3 unread messages.","favoriteFruit":"strawberry"},{"_id":"6724e6fcef1a1db2cf170762","index":6,"guid":"b8777c06-b90f-49a4-8737-96712fc504a3","isActive":false,"balance":"$2,285.03","picture":"http://placehold.it/32x32","age":37,"eyeColor":"green","name":"Price Bolton","gender":"male","company":"IMANT","email":"[email protected]","phone":"+1 (825) 424-2873","address":"237 Aberdeen Street, Sattley, Montana, 2918","about":"Non cillum irure fugiat consequat ad ex. Magna magna tempor excepteur irure quis. Duis in laboris ipsum adipisicing culpa magna reprehenderit nisi incididunt est veniam quis. Labore culpa ut culpa veniam est est consectetur ipsum ex esse.\r\n","registered":"2014-04-26T01:20:19 -02:00","latitude":70.349258,"longitude":126.810102,"greeting":"Hello, Price Bolton! You have 10 unread messages.","favoriteFruit":"banana"},{"_id":"6724e6fc8bcb952208c159f9","index":7,"guid":"a4e6c6c8-3fe3-42de-ae28-79c16956d309","isActive":false,"balance":"$1,298.07","picture":"http://placehold.it/32x32","age":28,"eyeColor":"blue","name":"Gretchen Wynn","gender":"female","company":"TERASCAPE","email":"[email protected]","phone":"+1 (882) 447-2895","address":"973 Suydam Place, Shindler, Nebraska, 8094","about":"Anim mollit labore magna proident ipsum culpa enim deserunt dolore sunt veniam fugiat. Ad fugiat cupidatat nisi commodo dolore duis commodo nostrud est. Enim proident ullamco non adipisicing magna consequat mollit ad reprehenderit laboris. Ex quis duis anim id non commodo amet sunt est magna officia.\r\n","registered":"2021-08-13T08:51:32 -02:00","latitude":14.551848,"longitude":-27.142242,"greeting":"Hello, Gretchen Wynn! You have 7 unread messages.","favoriteFruit":"apple"},{"_id":"6724e6fcc8243c2dfa47f5d4","index":8,"guid":"27df20d5-c1d8-419b-ad38-bdd1e6094775","isActive":true,"balance":"$3,005.40","picture":"http://placehold.it/32x32","age":33,"eyeColor":"blue","name":"Chen Travis","gender":"male","company":"MEMORA","email":"[email protected]","phone":"+1 (980) 500-2406","address":"182 Dahlgreen Place, Baker, South Carolina, 9817","about":"Ad nisi consequat aliquip eiusmod aute pariatur est sint magna. Ad magna anim esse qui Lorem nulla veniam dolore eiusmod. Cillum consequat sit aliqua est proident exercitation eiusmod irure. Minim eu laboris ad incididunt enim sunt. Sunt in excepteur aute non tempor irure mollit laboris. Eu et duis ullamco dolor sint occaecat officia culpa ipsum anim anim eu veniam aliquip. Exercitation ipsum dolor sint cillum duis incididunt minim quis irure enim reprehenderit do do incididunt.\r\n","registered":"2019-09-01T02:57:37 -02:00","latitude":25.442301,"longitude":48.381036,"greeting":"Hello, Chen Travis! You have 1 unread messages.","favoriteFruit":"banana"}]
7 changes: 7 additions & 0 deletions bench-cargo-miri/string-replace/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const TCB_INFO_JSON: &str = include_str!("../data.json");

fn main() {
let tcb_json = TCB_INFO_JSON;
let bad_tcb_json = tcb_json.replace("female", "male");
std::hint::black_box(bad_tcb_json);
}
94 changes: 94 additions & 0 deletions src/borrow_tracker/tree_borrows/foreign_access_skipping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use super::AccessKind;
use super::tree::AccessRelatedness;

/// To speed up tree traversals, we want to skip traversing subtrees when we know the traversal will have no effect.
/// This is often the case for foreign accesses, since usually foreign accesses happen several times in a row, but also
/// foreign accesses are idempotent. In particular, see tests `foreign_read_is_noop_after_foreign_write` and `all_transitions_idempotent`.
/// Thus, for each node we keep track of the "strongest idempotent foreign access," (SIFA) i.e. which foreign access can be skipped.
/// This enum represents the kinds of values we store:
/// `LocalAccess` means that the last access was local, and thus the next foreign access needs to happen, it can not be skipped.
/// `ForeignRead` means that we are idempotent under foreign reads, but not (yet) under foreign writes.
/// `ForeignWrite` means that we are idempotent under foreign writes. In other words, this node can be skipped for all foreign accesses.
/// Due to the subtree property of traversals, this also means that all our children can be skipped, which makes this optimization
/// worthwhile. In order for this to work, the invariant for multiple nodes in a tree is that at each location, the SIFA at each
/// child must be stronger than that at the parent. However, note that this is quite invariant, due to retags inserting nodes across
/// the entire range, which can violate this invariant without explicitly causing a local access which would reset things.
/// So this needs to be done manually, thus `ensure_no_stronger_than`, and also the test `permission_sifa_is_correct` in `perms.rs`
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
pub enum LastForeignAccess {
#[default]
LocalAccess,
ForeignRead,
ForeignWrite,
}

impl LastForeignAccess {
/// Returns true if a node where the strongest idempotent foreign access is `self`
/// can skip the access `happening_next`. Note that if this returns
/// `true`, then the entire subtree will be skipped.
pub fn can_skip_foreign_access(self, happening_next: LastForeignAccess) -> bool {
debug_assert!(happening_next.is_foreign());
// This ordering is correct. Intuitively, if the last access here was
// a foreign write, everything can be skipped, since after a foreign write,
// all further foreign accesses are idempotent
happening_next <= self
}

/// Updates `self` to account for a foreign access.
pub fn record_new(&mut self, just_happened: LastForeignAccess) {
if just_happened.is_local() {
// If the access is local, reset it.
*self = LastForeignAccess::LocalAccess;
} else {
// Otherwise, keep it or stengthen it.
*self = just_happened.max(*self);
}
}

/// Returns true if this access is local.
pub fn is_local(self) -> bool {
matches!(self, LastForeignAccess::LocalAccess)
}

/// Returns true if this access is foreign, i.e. not local.
pub fn is_foreign(self) -> bool {
!self.is_local()
}

/// Constructs a foreign access from an `AccessKind`
pub fn foreign(acc: AccessKind) -> LastForeignAccess {
match acc {
AccessKind::Read => Self::ForeignRead,
AccessKind::Write => Self::ForeignWrite,
}
}

/// Usually, tree traversals have an `AccessKind` and an `AccessRelatedness`.
/// This methods converts these into the corresponding `LastForeignAccess`, to be used
/// to e.g. invoke `can_skip_foreign_access`.
pub fn of_acc_and_rel(acc: AccessKind, rel: AccessRelatedness) -> LastForeignAccess {
if rel.is_foreign() { Self::foreign(acc) } else { Self::LocalAccess }
}

/// During retags, the SIFA needs to be weakened to account for children with weaker SIFAs being inserted.
/// Thus, this method is called from the bottom up on each parent, until it returns false, which means the
/// "children have stronger SIFAs" invariant is restored.
pub fn ensure_no_stronger_than(&mut self, strongest_allowed: LastForeignAccess) -> bool {
if *self > strongest_allowed {
*self = strongest_allowed;
true
} else {
false
}
}
}

mod tests {
use super::LastForeignAccess;
JoJoDeveloping marked this conversation as resolved.
Show resolved Hide resolved

#[test]
fn test_order() {
assert!(LastForeignAccess::LocalAccess < LastForeignAccess::ForeignRead);
assert!(LastForeignAccess::ForeignRead < LastForeignAccess::ForeignWrite);
}
}
10 changes: 9 additions & 1 deletion src/borrow_tracker/tree_borrows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::concurrency::data_race::NaReadType;
use crate::*;

pub mod diagnostics;
mod foreign_access_skipping;
mod perms;
mod tree;
mod unimap;
Expand Down Expand Up @@ -296,7 +297,14 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.machine.current_span(),
)?;
// Record the parent-child pair in the tree.
tree_borrows.new_child(orig_tag, new_tag, new_perm.initial_state, range, span)?;
tree_borrows.new_child(
orig_tag,
new_tag,
new_perm.initial_state,
range,
span,
new_perm.protector.is_some(),
)?;
drop(tree_borrows);

// Also inform the data race model (but only if any bytes are actually affected).
Expand Down
53 changes: 53 additions & 0 deletions src/borrow_tracker/tree_borrows/perms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum PermissionPriv {
Disabled,
}
use self::PermissionPriv::*;
use super::foreign_access_skipping::LastForeignAccess;

impl PartialOrd for PermissionPriv {
/// PermissionPriv is ordered by the reflexive transitive closure of
Expand Down Expand Up @@ -87,6 +88,26 @@ impl PermissionPriv {
fn compatible_with_protector(&self) -> bool {
!matches!(self, ReservedIM)
}

/// See `foreign_access_skipping.rs`. Compotes the SIFA of a permission.
fn strongest_idempotent_foreign_access(&self, prot: bool) -> LastForeignAccess {
match self {
// The read will make it conflicted, so it is not invariant under it.
ReservedFrz { conflicted } if prot && !conflicted => LastForeignAccess::LocalAccess,
// Otherwise, foreign reads do not affect it
ReservedFrz { .. } => LastForeignAccess::ForeignRead,
// Famously, ReservedIM survives foreign writes. It is never protected.
JoJoDeveloping marked this conversation as resolved.
Show resolved Hide resolved
ReservedIM => LastForeignAccess::ForeignWrite,
// Active changes on any foreign access (becomes Frozen/Disabled).
Active => LastForeignAccess::LocalAccess,
// Frozen survives foreign reads, but not writes.
Frozen => LastForeignAccess::ForeignRead,
// Disabled survives foreign reads and writes. It survives them
// even if protected, because a protected `Disabled` is not initialized
// and does therefore not trigger UB.
Disabled => LastForeignAccess::ForeignWrite,
}
}
}

/// This module controls how each permission individually reacts to an access.
Expand Down Expand Up @@ -305,6 +326,13 @@ impl Permission {
(Disabled, _) => false,
}
}

/// Returns the strongest foreign action this node survives (without change),
/// where `prot` indicates if it is protected.
/// See `foreign_access_skipping`
pub fn strongest_idempotent_foreign_access(&self, prot: bool) -> LastForeignAccess {
self.inner.strongest_idempotent_foreign_access(prot)
}
}

impl PermTransition {
Expand Down Expand Up @@ -634,6 +662,31 @@ mod propagation_optimization_checks {
}
}

#[test]
#[rustfmt::skip]
fn permission_sifa_is_correct() {
// Tests that `strongest_idempotent_foreign_access` is correct. See `foreign_access_skipping.rs`.
for perm in PermissionPriv::exhaustive() {
// Assert that adding a protector makes is less idempotent.
assert!(perm.strongest_idempotent_foreign_access(true) <= perm.strongest_idempotent_foreign_access(false));
for prot in bool::exhaustive() {
if prot {
precondition!(perm.compatible_with_protector());
}
let access = perm.strongest_idempotent_foreign_access(prot);
// 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 LastForeignAccess::ForeignRead <= access {
assert_eq!(perm, transition::perform_access(AccessKind::Read, AccessRelatedness::DistantAccess, perm, prot).unwrap());
}
// Then, if the SIFA includes foreign writes, assert it is idempotent under foreign writes.
if LastForeignAccess::ForeignWrite <= access {
assert_eq!(perm, transition::perform_access(AccessKind::Write, AccessRelatedness::DistantAccess, perm, prot).unwrap());
}
}
}
}

#[test]
// Check that all transitions are consistent with the order on PermissionPriv,
// i.e. Reserved -> Active -> Frozen -> Disabled
Expand Down
Loading
Loading