Skip to content

Retagging: Recurse into compound values #526

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

Merged
merged 18 commits into from
Nov 20, 2018
Merged
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
60 changes: 30 additions & 30 deletions src/fn_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,10 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
}

"free" => {
let ptr = self.read_scalar(args[0])?.not_undef()?.erase_tag(); // raw ptr operation, no tag
let ptr = self.read_scalar(args[0])?.not_undef()?;
if !ptr.is_null_ptr(self) {
self.memory_mut().deallocate(
ptr.to_ptr()?.with_default_tag(),
ptr.to_ptr()?,
None,
MiriMemoryKind::C.into(),
)?;
Expand Down Expand Up @@ -179,7 +179,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
self.write_scalar(Scalar::Ptr(ptr), dest)?;
}
"__rust_dealloc" => {
let ptr = self.read_scalar(args[0])?.to_ptr()?.erase_tag(); // raw ptr operation, no tag
let ptr = self.read_scalar(args[0])?.to_ptr()?;
let old_size = self.read_scalar(args[1])?.to_usize(self)?;
let align = self.read_scalar(args[2])?.to_usize(self)?;
if old_size == 0 {
Expand All @@ -189,13 +189,13 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
return err!(HeapAllocNonPowerOfTwoAlignment(align));
}
self.memory_mut().deallocate(
ptr.with_default_tag(),
ptr,
Some((Size::from_bytes(old_size), Align::from_bytes(align, align).unwrap())),
MiriMemoryKind::Rust.into(),
)?;
}
"__rust_realloc" => {
let ptr = self.read_scalar(args[0])?.to_ptr()?.erase_tag(); // raw ptr operation, no tag
let ptr = self.read_scalar(args[0])?.to_ptr()?;
let old_size = self.read_scalar(args[1])?.to_usize(self)?;
let align = self.read_scalar(args[2])?.to_usize(self)?;
let new_size = self.read_scalar(args[3])?.to_usize(self)?;
Expand All @@ -206,7 +206,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
return err!(HeapAllocNonPowerOfTwoAlignment(align));
}
let new_ptr = self.memory_mut().reallocate(
ptr.with_default_tag(),
ptr,
Size::from_bytes(old_size),
Align::from_bytes(align, align).unwrap(),
Size::from_bytes(new_size),
Expand Down Expand Up @@ -238,8 +238,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo

"dlsym" => {
let _handle = self.read_scalar(args[0])?;
let symbol = self.read_scalar(args[1])?.to_ptr()?.erase_tag();
let symbol_name = self.memory().read_c_str(symbol.with_default_tag())?;
let symbol = self.read_scalar(args[1])?.to_ptr()?;
let symbol_name = self.memory().read_c_str(symbol)?;
let err = format!("bad c unicode symbol: {:?}", symbol_name);
let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
return err!(Unimplemented(format!(
Expand Down Expand Up @@ -292,13 +292,13 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
return err!(MachineError("the evaluated program panicked".to_string())),

"memcmp" => {
let left = self.read_scalar(args[0])?.not_undef()?.erase_tag(); // raw ptr operation
let right = self.read_scalar(args[1])?.not_undef()?.erase_tag(); // raw ptr operation
let left = self.read_scalar(args[0])?.not_undef()?;
let right = self.read_scalar(args[1])?.not_undef()?;
let n = Size::from_bytes(self.read_scalar(args[2])?.to_usize(self)?);

let result = {
let left_bytes = self.memory().read_bytes(left.with_default_tag(), n)?;
let right_bytes = self.memory().read_bytes(right.with_default_tag(), n)?;
let left_bytes = self.memory().read_bytes(left, n)?;
let right_bytes = self.memory().read_bytes(right, n)?;

use std::cmp::Ordering::*;
match left_bytes.cmp(right_bytes) {
Expand All @@ -315,8 +315,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
}

"memrchr" => {
let ptr = self.read_scalar(args[0])?.not_undef()?.erase_tag(); // raw ptr operation
let ptr = ptr.with_default_tag();
let ptr = self.read_scalar(args[0])?.not_undef()?;
let ptr = ptr;
let val = self.read_scalar(args[1])?.to_bytes()? as u8;
let num = self.read_scalar(args[2])?.to_usize(self)?;
if let Some(idx) = self.memory().read_bytes(ptr, Size::from_bytes(num))?
Expand All @@ -330,8 +330,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
}

"memchr" => {
let ptr = self.read_scalar(args[0])?.not_undef()?.erase_tag(); // raw ptr operation
let ptr = ptr.with_default_tag();
let ptr = self.read_scalar(args[0])?.not_undef()?;
let ptr = ptr;
let val = self.read_scalar(args[1])?.to_bytes()? as u8;
let num = self.read_scalar(args[2])?.to_usize(self)?;
if let Some(idx) = self.memory().read_bytes(ptr, Size::from_bytes(num))?.iter().position(
Expand All @@ -347,8 +347,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo

"getenv" => {
let result = {
let name_ptr = self.read_scalar(args[0])?.to_ptr()?.erase_tag(); // raw ptr operation
let name = self.memory().read_c_str(name_ptr.with_default_tag())?;
let name_ptr = self.read_scalar(args[0])?.to_ptr()?;
let name = self.memory().read_c_str(name_ptr)?;
match self.machine.env_vars.get(name) {
Some(&var) => Scalar::Ptr(var),
None => Scalar::ptr_null(&*self.tcx),
Expand All @@ -360,10 +360,10 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
"unsetenv" => {
let mut success = None;
{
let name_ptr = self.read_scalar(args[0])?.not_undef()?.erase_tag(); // raw ptr operation
let name_ptr = self.read_scalar(args[0])?.not_undef()?;
if !name_ptr.is_null_ptr(self) {
let name = self.memory().read_c_str(name_ptr.to_ptr()?
.with_default_tag())?.to_owned();
)?.to_owned();
if !name.is_empty() && !name.contains(&b'=') {
success = Some(self.machine.env_vars.remove(&name));
}
Expand All @@ -382,11 +382,11 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
"setenv" => {
let mut new = None;
{
let name_ptr = self.read_scalar(args[0])?.not_undef()?.erase_tag(); // raw ptr operation
let value_ptr = self.read_scalar(args[1])?.to_ptr()?.erase_tag(); // raw ptr operation
let value = self.memory().read_c_str(value_ptr.with_default_tag())?;
let name_ptr = self.read_scalar(args[0])?.not_undef()?;
let value_ptr = self.read_scalar(args[1])?.to_ptr()?;
let value = self.memory().read_c_str(value_ptr)?;
if !name_ptr.is_null_ptr(self) {
let name = self.memory().read_c_str(name_ptr.to_ptr()?.with_default_tag())?;
let name = self.memory().read_c_str(name_ptr.to_ptr()?)?;
if !name.is_empty() && !name.contains(&b'=') {
new = Some((name.to_owned(), value.to_owned()));
}
Expand Down Expand Up @@ -417,14 +417,14 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo

"write" => {
let fd = self.read_scalar(args[0])?.to_bytes()?;
let buf = self.read_scalar(args[1])?.not_undef()?.erase_tag();
let buf = self.read_scalar(args[1])?.not_undef()?;
let n = self.read_scalar(args[2])?.to_bytes()? as u64;
trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
let result = if fd == 1 || fd == 2 {
// stdout/stderr
use std::io::{self, Write};

let buf_cont = self.memory().read_bytes(buf.with_default_tag(), Size::from_bytes(n))?;
let buf_cont = self.memory().read_bytes(buf, Size::from_bytes(n))?;
let res = if fd == 1 {
io::stdout().write(buf_cont)
} else {
Expand All @@ -445,8 +445,8 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
}

"strlen" => {
let ptr = self.read_scalar(args[0])?.to_ptr()?.erase_tag();
let n = self.memory().read_c_str(ptr.with_default_tag())?.len();
let ptr = self.read_scalar(args[0])?.to_ptr()?;
let n = self.memory().read_c_str(ptr)?.len();
self.write_scalar(Scalar::from_uint(n as u64, dest.layout.size), dest)?;
}

Expand Down Expand Up @@ -492,7 +492,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo

// Hook pthread calls that go to the thread-local storage memory subsystem
"pthread_key_create" => {
let key_ptr = self.read_scalar(args[0])?.to_ptr()?.erase_tag(); // raw ptr operation
let key_ptr = self.read_scalar(args[0])?.to_ptr()?;

// Extract the function type out of the signature (that seems easier than constructing it ourselves...)
let dtor = match self.read_scalar(args[1])?.not_undef()? {
Expand All @@ -515,7 +515,7 @@ impl<'a, 'mir, 'tcx: 'mir + 'a> EvalContextExt<'tcx, 'mir> for super::MiriEvalCo
return err!(OutOfTls);
}
self.memory_mut().write_scalar(
key_ptr.with_default_tag(),
key_ptr,
key_layout.align,
Scalar::from_uint(key, key_layout.size).into(),
key_layout.size,
Expand Down
10 changes: 5 additions & 5 deletions src/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,12 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'a, 'mir, '
let count = self.read_scalar(args[2])?.to_usize(self)?;
let elem_align = elem_layout.align;
// erase tags: this is a raw ptr operation
let src = self.read_scalar(args[0])?.not_undef()?.erase_tag();
let dest = self.read_scalar(args[1])?.not_undef()?.erase_tag();
let src = self.read_scalar(args[0])?.not_undef()?;
let dest = self.read_scalar(args[1])?.not_undef()?;
self.memory_mut().copy(
src.with_default_tag(),
src,
elem_align,
dest.with_default_tag(),
dest,
elem_align,
Size::from_bytes(count * elem_size),
intrinsic_name.ends_with("_nonoverlapping"),
Expand Down Expand Up @@ -436,7 +436,7 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'a, 'mir, '
let ty = substs.type_at(0);
let ty_layout = self.layout_of(ty)?;
let val_byte = self.read_scalar(args[1])?.to_u8()?;
let ptr = self.read_scalar(args[0])?.not_undef()?.erase_tag().with_default_tag();
let ptr = self.read_scalar(args[0])?.not_undef()?;
let count = self.read_scalar(args[2])?.to_usize(self)?;
self.memory().check_align(ptr, ty_layout.align)?;
self.memory_mut().write_repeat(ptr, val_byte, ty_layout.size * count)?;
Expand Down
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,9 +461,9 @@ impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
// No tracking
Ok(place.ptr)
} else {
let ptr = place.ptr.to_ptr()?; // assert this is not a scalar
let tag = ecx.tag_dereference(place, size, mutability.into())?;
Ok(Scalar::Ptr(Pointer::new_with_tag(ptr.alloc_id, ptr.offset, tag)))
ecx.ptr_dereference(place, size, mutability.into())?;
// We never change the pointer
Ok(place.ptr)
}
}

Expand Down
38 changes: 17 additions & 21 deletions src/stacked_borrows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,12 +405,12 @@ impl<'tcx> Stacks {


pub trait EvalContextExt<'tcx> {
fn tag_dereference(
fn ptr_dereference(
&self,
place: MPlaceTy<'tcx, Borrow>,
size: Size,
mutability: Option<Mutability>,
) -> EvalResult<'tcx, Borrow>;
) -> EvalResult<'tcx>;

fn tag_new_allocation(
&mut self,
Expand Down Expand Up @@ -480,13 +480,13 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
///
/// Note that this does NOT mean that all this memory will actually get accessed/referenced!
/// We could be in the middle of `&(*var).1`.
fn tag_dereference(
fn ptr_dereference(
&self,
place: MPlaceTy<'tcx, Borrow>,
size: Size,
mutability: Option<Mutability>,
) -> EvalResult<'tcx, Borrow> {
trace!("tag_dereference: Accessing {} reference for {:?} (pointee {})",
) -> EvalResult<'tcx> {
trace!("ptr_dereference: Accessing {} reference for {:?} (pointee {})",
if let Some(mutability) = mutability { format!("{:?}", mutability) } else { format!("raw") },
place.ptr, place.layout.ty);
let ptr = place.ptr.to_ptr()?;
Expand All @@ -497,12 +497,8 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
// That can transmute a raw ptr to a (shared/mut) ref, and a mut ref to a shared one.
match (mutability, ptr.tag) {
(None, _) => {
// Don't use the tag, this is a raw access! They should happen tagless.
// This is needed for `*mut` to make any sense: Writes *do* enforce the
// `Uniq` tag to be up top, but we must make sure raw writes do not do that.
// This does mean, however, that `&*foo` is *not* a NOP *if* `foo` is a raw ptr.
// Also don't do any further validation, this is raw after all.
return Ok(Borrow::default());
// No further validation on raw accesses.
return Ok(());
}
(Some(MutMutable), Borrow::Uniq(_)) |
(Some(MutImmutable), Borrow::Shr(_)) => {
Expand Down Expand Up @@ -543,8 +539,8 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
alloc.extra.deref(ptr, size, kind)?;
}

// All is good, and do not change the tag
Ok(ptr.tag)
// All is good
Ok(())
}

/// The given place may henceforth be accessed through raw pointers.
Expand Down Expand Up @@ -661,14 +657,14 @@ impl<'a, 'mir, 'tcx> EvalContextExt<'tcx> for MiriEvalContext<'a, 'mir, 'tcx> {
// Primitives of reference type, that is the one thing we are interested in.
fn visit_primitive(&mut self, place: MPlaceTy<'tcx, Borrow>) -> EvalResult<'tcx>
{
match place.layout.ty.sty {
ty::Ref(_, _, mutbl) => {
let val = self.ecx.read_immediate(place.into())?;
let val = self.ecx.retag_reference(val, mutbl)?;
self.ecx.write_immediate(val, place.into())?;
}
_ => {}, // nothing to do
}
let mutbl = match place.layout.ty.sty {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be simplified to an if let on builtin_deref(false)

Copy link
Member Author

@RalfJung RalfJung Nov 17, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately that reports immutable for Box, making it useless.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment explaining this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wat. I need to review all uses of that method now -.-

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do.^^ And/or fix that method? ;)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this is related to the fact that

fn foo(x: &mut i32) { *x = 3 }

works but

fn foo(x: Box<i32>) { *x = 3 }

says it needs a mut, as in mut x: Box<i32>.

ty::Ref(_, _, mutbl) => mutbl,
ty::Adt(..) if place.layout.ty.is_box() => MutMutable,
_ => return Ok(()), // nothing to do
};
let val = self.ecx.read_immediate(place.into())?;
let val = self.ecx.retag_reference(val, mutbl)?;
self.ecx.write_immediate(val, place.into())?;
Ok(())
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
fn demo_mut_advanced_unique(mut our: Box<i32>) -> i32 {
unknown_code_1(&*our);

// This "re-asserts" uniqueness of the reference: After writing, we know
// our tag is at the top of the stack.
*our = 5;

unknown_code_2();

// We know this will return 5
*our //~ ERROR does not exist on the stack
}

// Now comes the evil context
use std::ptr;

static mut LEAK: *mut i32 = ptr::null_mut();

fn unknown_code_1(x: &i32) { unsafe {
LEAK = x as *const _ as *mut _;
} }

fn unknown_code_2() { unsafe {
*LEAK = 7;
} }

fn main() {
assert_eq!(demo_mut_advanced_unique(Box::new(0)), 5);
}
10 changes: 3 additions & 7 deletions tests/compile-fail-fullmir/stacked_borrows/buggy_as_mut_slice.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// error-pattern: mutable reference with frozen tag

mod safe {
use std::slice::from_raw_parts_mut;

Expand All @@ -12,10 +10,8 @@ mod safe {

fn main() {
let v = vec![0,1,2];
let _v1 = safe::as_mut_slice(&v);
/*
let v2 = safe::as_mut_slice(&v);
let v1 = safe::as_mut_slice(&v);
let _v2 = safe::as_mut_slice(&v);
v1[1] = 5;
v1[1] = 6;
*/
//~^ ERROR does not exist on the stack
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ use std::mem;
fn main() {
let mut x: i32 = 42;
let raw: *mut i32 = unsafe { mem::transmute(&mut x) };
let raw = raw as usize as *mut i32; // make sure we killed the tag
unsafe { *raw = 13; } //~ ERROR does not exist on the stack
}