Skip to content

Commit

Permalink
Temp work on unknown_bottom
Browse files Browse the repository at this point in the history
  • Loading branch information
carbotaniuman committed Jun 15, 2022
1 parent ee68602 commit eedacbd
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 124 deletions.
186 changes: 118 additions & 68 deletions src/stacked_borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ pub enum ItemOrUnknown {
Unknown(PtrId),
}

impl ItemOrUnknown {
fn item(self) -> Option<Item> {
match self {
ItemOrUnknown::Item(item) => Some(item),
ItemOrUnknown::Unknown(_) => None,
}
}
}

/// Extra per-location state.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Stack {
Expand All @@ -105,15 +114,15 @@ pub struct Stacks {
stacks: RefCell<RangeMap<Stack>>,
/// Stores past operations on this allocation
history: RefCell<AllocHistory>,
/// The tags that have been exposed
exposed_tags: FxHashSet<SbTag>,
}

/// Extra global state, available to the memory access hooks.
#[derive(Debug)]
pub struct GlobalStateInner {
/// Next unused pointer ID (tag).
next_ptr_id: PtrId,
/// The tags that have been exposed
exposed_tags: FxHashSet<SbTag>,
/// Table storing the "base" tag for each allocation.
/// The base tag is the one used for the initial pointer.
/// We need this in a separate table to handle cyclic statics.
Expand Down Expand Up @@ -183,7 +192,6 @@ impl GlobalStateInner {
) -> Self {
GlobalStateInner {
next_ptr_id: NonZeroU64::new(1).unwrap(),
exposed_tags: FxHashSet::default(),
base_ptr_ids: FxHashMap::default(),
next_call_id: NonZeroU64::new(1).unwrap(),
active_calls: FxHashSet::default(),
Expand Down Expand Up @@ -285,31 +293,50 @@ impl Permission {
impl<'tcx> Stack {
/// Find the item granting the given kind of access to the given tag, and return where
/// it is on the stack.
fn find_granting(&self, access: AccessKind, tag: Option<SbTag>) -> Option<usize> {
fn find_granting(
&self,
access: AccessKind,
tag: Option<SbTag>,
exposed_tags: &FxHashSet<SbTag>,
) -> Option<usize> {
self.borrows
.iter()
.enumerate() // we also need to know *where* in the stack
.rev() // search top-to-bottom
// Return permission of first item that grants access.
// We require a permission with the right tag, ensuring U3 and F3.
.find_map(|(idx, item)| {
// A wildcard tag allows access to all `Unknown` tags.
if let Some(tag) = tag {
if tag == item.tag && item.perm.grants(access) {
Some(idx)
} else {
None
}
} else {
todo!()
match item {
ItemOrUnknown::Unknown(id) =>
match tag {
Some(tag) =>
match tag {
SbTag::Tagged(tag_id) if tag_id < *id => Some(idx),
SbTag::Untagged => todo!(),
_ => None,
},
None => Some(idx),
},
ItemOrUnknown::Item(item) =>
match tag {
Some(tag) if tag == item.tag && item.perm.grants(access) => Some(idx),
None if exposed_tags.contains(&item.tag) => Some(idx),
_ => None,
},
}
})
}

/// Find the first write-incompatible item above the given one --
/// i.e, find the height to which the stack will be truncated when writing to `granting`.
fn find_first_write_incompatible(&self, granting: usize) -> usize {
let perm = self.borrows[granting].perm;
let perm = if let ItemOrUnknown::Item(item) = self.borrows[granting] {
item.perm
} else {
// TODO: probably the least restirctive?
Permission::SharedReadWrite
};

match perm {
Permission::SharedReadOnly => bug!("Cannot use SharedReadOnly for writing"),
Permission::Disabled => bug!("Cannot use Disabled for anything"),
Expand All @@ -319,12 +346,16 @@ impl<'tcx> Stack {
// The SharedReadWrite *just* above us are compatible, to skip those.
let mut idx = granting + 1;
while let Some(item) = self.borrows.get(idx) {
if item.perm == Permission::SharedReadWrite {
// Go on.
idx += 1;
if let ItemOrUnknown::Item(item) = item {
if item.perm == Permission::SharedReadWrite {
// Go on.
idx += 1;
} else {
// Found first incompatible!
break;
}
} else {
// Found first incompatible!
break;
bug!("Unknown found in the middle of the stack")
}
}
idx
Expand Down Expand Up @@ -394,20 +425,15 @@ impl<'tcx> Stack {
global: &mut GlobalStateInner,
current_span: &mut CurrentSpan<'_, '_, 'tcx>,
alloc_history: &mut AllocHistory,
exposed_tags: &FxHashSet<SbTag>,
) -> InterpResult<'tcx> {
// Two main steps: Find granting item, remove incompatible items above.

// Step 1: Find granting item.
let granting_idx = self.find_granting(access, tag).ok_or_else(|| {
alloc_history.access_error(
access,
tag,
alloc_id,
alloc_range,
offset,
self,
)
})?;
let granting_idx =
self.find_granting(access, tag, exposed_tags).ok_or_else(|| {
alloc_history.access_error(access, tag, alloc_id, alloc_range, offset, self)
})?;

// Step 2: Remove incompatible items above them. Make sure we do not remove protected
// items. Behavior differs for reads and writes.
Expand All @@ -417,14 +443,18 @@ impl<'tcx> Stack {
// pointers become invalid on write accesses (ensures F2a, and ensures U2 for write accesses).
let first_incompatible_idx = self.find_first_write_incompatible(granting_idx);
for item in self.borrows.drain(first_incompatible_idx..).rev() {
trace!("access: popping item {:?}", item);
Stack::check_protector(
&item,
Some((tag, alloc_range, offset, access)),
global,
alloc_history,
)?;
alloc_history.log_invalidation(item.tag, alloc_range, current_span);
if let ItemOrUnknown::Item(item) = item {
trace!("access: popping item {:?}", item);
Stack::check_protector(
&item,
Some((tag, alloc_range, offset, access)),
global,
alloc_history,
)?;
alloc_history.log_invalidation(item.tag, alloc_range, current_span);
} else {
bug!("Unknown found in the middle of the stack")
}
}
} else {
// On a read, *disable* all `Unique` above the granting item. This ensures U2 for read accesses.
Expand All @@ -437,22 +467,27 @@ impl<'tcx> Stack {
// We *disable* instead of removing `Unique` to avoid "connecting" two neighbouring blocks of SRWs.
for idx in ((granting_idx + 1)..self.borrows.len()).rev() {
let item = &mut self.borrows[idx];
if item.perm == Permission::Unique {
trace!("access: disabling item {:?}", item);
Stack::check_protector(
item,
Some((tag, alloc_range, offset, access)),
global,
alloc_history,
)?;
item.perm = Permission::Disabled;
alloc_history.log_invalidation(item.tag, alloc_range, current_span);

if let ItemOrUnknown::Item(item) = item {
if item.perm == Permission::Unique {
trace!("access: disabling item {:?}", item);
Stack::check_protector(
item,
Some((tag, alloc_range, offset, access)),
global,
alloc_history,
)?;
item.perm = Permission::Disabled;
alloc_history.log_invalidation(item.tag, alloc_range, current_span);
}
} else {
bug!("Unknown found in the middle of the stack")
}
}
}
} else {
self.borrows.clear();
todo!()
self.borrows.push(ItemOrUnknown::Unknown(global.next_ptr_id));
}

// Done.
Expand All @@ -467,9 +502,10 @@ impl<'tcx> Stack {
(alloc_id, alloc_range, offset): (AllocId, AllocRange, Size), // just for debug printing and error messages
global: &GlobalStateInner,
alloc_history: &mut AllocHistory,
exposed_tags: &FxHashSet<SbTag>,
) -> InterpResult<'tcx> {
// Step 1: Find granting item.
self.find_granting(AccessKind::Write, tag).ok_or_else(|| {
self.find_granting(AccessKind::Write, tag, exposed_tags).ok_or_else(|| {
err_sb_ub(format!(
"no item granting write access for deallocation to tag {:?} at {:?} found in borrow stack",
tag, alloc_id,
Expand All @@ -481,7 +517,11 @@ impl<'tcx> Stack {

// Step 2: Remove all items. Also checks for protectors.
for item in self.borrows.drain(..).rev() {
Stack::check_protector(&item, None, global, alloc_history)?;
if let ItemOrUnknown::Item(item) = item {
Stack::check_protector(&item, None, global, alloc_history)?;
} else {
// TODO: I think we just ignore the protectors here?
}
}

Ok(())
Expand All @@ -501,6 +541,7 @@ impl<'tcx> Stack {
global: &mut GlobalStateInner,
current_span: &mut CurrentSpan<'_, '_, 'tcx>,
alloc_history: &mut AllocHistory,
exposed_tags: &FxHashSet<SbTag>,
) -> InterpResult<'tcx> {
// Figure out which access `perm` corresponds to.
let access =
Expand All @@ -517,16 +558,18 @@ impl<'tcx> Stack {

// Now we figure out which item grants our parent (`derived_from`) this kind of access.
// We use that to determine where to put the new item.
let granting_idx = self.find_granting(access, derived_from).ok_or_else(|| {
alloc_history.grant_error(
derived_from,
new,
alloc_id,
alloc_range,
offset,
self,
)
})?;
let granting_idx = self
.find_granting(access, derived_from, exposed_tags)
.ok_or_else(|| {
alloc_history.grant_error(
derived_from,
new,
alloc_id,
alloc_range,
offset,
self,
)
})?;

// SharedReadWrite can coexist with "existing loans", meaning they don't act like a write
// access. Instead of popping the stack, we insert the item at the place the stack would
Expand All @@ -544,6 +587,7 @@ impl<'tcx> Stack {
global,
current_span,
alloc_history,
exposed_tags,
)?;

// We insert "as far up as possible": We know only compatible items are remaining
Expand All @@ -554,12 +598,14 @@ impl<'tcx> Stack {
};

// Put the new item there. As an optimization, deduplicate if it is equal to one of its new neighbors.
if self.borrows[new_idx - 1] == new || self.borrows.get(new_idx) == Some(&new) {
if self.borrows[new_idx - 1] == ItemOrUnknown::Item(new)
|| self.borrows.get(new_idx) == Some(&ItemOrUnknown::Item(new))
{
// Optimization applies, done.
trace!("reborrow: avoiding adding redundant item {:?}", new);
} else {
trace!("reborrow: adding item {:?}", new);
self.borrows.insert(new_idx, new);
self.borrows.insert(new_idx, ItemOrUnknown::Item(new));
}

Ok(())
Expand All @@ -572,11 +618,12 @@ impl<'tcx> Stacks {
/// Creates new stack with initial tag.
fn new(size: Size, perm: Permission, tag: SbTag) -> Self {
let item = Item { perm, tag, protector: None };
let stack = Stack { borrows: vec![item], unknown_bottom: None };
let stack = Stack { borrows: vec![ItemOrUnknown::Item(item)], unknown_bottom: None };

Stacks {
stacks: RefCell::new(RangeMap::new(size, stack)),
history: RefCell::new(AllocHistory::new()),
exposed_tags: FxHashSet::default(),
}
}

Expand All @@ -598,7 +645,7 @@ impl<'tcx> Stacks {
fn for_each_mut(
&mut self,
range: AllocRange,
mut f: impl FnMut(Size, &mut Stack, &mut AllocHistory) -> InterpResult<'tcx>,
mut f: impl FnMut(Size, &mut Stack, &mut AllocHistory, &mut FxHashSet<SbTag>) -> InterpResult<'tcx>,
) -> InterpResult<'tcx> {
let stacks = self.stacks.get_mut();
let history = &mut *self.history.borrow_mut();
Expand Down Expand Up @@ -666,7 +713,7 @@ impl Stacks {
alloc_id: AllocId,
tag: SbTag,
range: AllocRange,
state: &GlobalState,
_state: &GlobalState,
) -> InterpResult<'tcx> {
trace!(
"allocation exposed with tag {:?}: {:?}, size {}",
Expand All @@ -675,8 +722,7 @@ impl Stacks {
range.size.bytes()
);

let mut state = state.borrow_mut();
state.exposed_tags.insert(tag);
self.exposed_tags.insert(tag);

Ok(())
}
Expand Down Expand Up @@ -705,6 +751,7 @@ impl Stacks {
&mut state,
&mut current_span,
history,
&self.exposed_tags,
)
})
}
Expand Down Expand Up @@ -733,6 +780,7 @@ impl Stacks {
&mut state,
&mut current_span,
history,
&self.exposed_tags,
)
})
}
Expand All @@ -748,7 +796,7 @@ impl Stacks {
trace!("deallocation with tag {:?}: {:?}, size {}", tag, alloc_id, range.size.bytes());
let state = state.borrow();
self.for_each_mut(range, |offset, stack, history| {
stack.dealloc(tag, (alloc_id, range, offset), &state, history)
stack.dealloc(tag, (alloc_id, range, offset), &state, history, &self.exposed_tags)
})?;
Ok(())
}
Expand Down Expand Up @@ -886,6 +934,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
&mut *global,
current_span,
history,
&stacked_borrows.exposed_tags,
)
})
})?;
Expand All @@ -910,6 +959,7 @@ trait EvalContextPrivExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
&mut global,
current_span,
history,
&stacked_borrows.exposed_tags,
)
})?;

Expand Down
Loading

0 comments on commit eedacbd

Please sign in to comment.