Skip to content
This repository has been archived by the owner on Aug 22, 2024. It is now read-only.

Commit

Permalink
Git rid of ExpressionType::Reference
Browse files Browse the repository at this point in the history
  • Loading branch information
Herman Venter committed May 12, 2020
1 parent 1d9417f commit 213d1a3
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 129 deletions.
Binary file modified binaries/summary_store.tar
Binary file not shown.
9 changes: 7 additions & 2 deletions checker/src/abstract_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,9 @@ impl AbstractValueTrait for Rc<AbstractValue> {
Expression::Cast {
operand,
target_type: tt,
} if *tt == ExpressionType::Reference => operand.try_to_retype_as(target_type),
} if *tt == ExpressionType::NonPrimitive || *tt == ExpressionType::ThinPointer => {
operand.try_to_retype_as(target_type)
}
Expression::ConditionalExpression {
condition,
consequent,
Expand Down Expand Up @@ -1080,7 +1082,10 @@ impl AbstractValueTrait for Rc<AbstractValue> {
operand,
target_type: cast_type,
} => {
checked_assume!(*cast_type == ExpressionType::Reference);
checked_assume!(
*cast_type == ExpressionType::NonPrimitive
|| *cast_type == ExpressionType::ThinPointer
);
operand.dereference(target_type)
}
Expression::CompileTimeConstant(..) => self.clone(),
Expand Down
123 changes: 62 additions & 61 deletions checker/src/block_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,7 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
call_visitor.check_preconditions_if_necessary(&function_summary);
call_visitor.transfer_and_refine_normal_return_state(&function_summary);
call_visitor.transfer_and_refine_cleanup_state(&function_summary);
debug!("target {:?} arguments {:?}", destination, actual_args);
debug!(
"post env {:?}",
call_visitor.block_visitor.bv.current_environment
Expand Down Expand Up @@ -584,7 +585,7 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
}
| Expression::Variable {
path,
var_type: ExpressionType::Reference,
var_type: ExpressionType::ThinPointer,
} => {
let closure_ty = self
.bv
Expand Down Expand Up @@ -1058,7 +1059,32 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
user_ty, literal, ..
} = constant.borrow();
let target_type = literal.ty;
let const_value = self.visit_constant(*user_ty, &literal);
let const_value = self.visit_constant(*user_ty, &literal); //todo: perhaps return a length value as well?
if let TyKind::Ref(_, ty, _) = &target_type.kind {
// The constant value we get back from this is a "thin" pointer to the
// underlying storage, which exists as a collection of (path, value) pairs
// where the root of each path is an alias of the constant value.
// If the value is a string or an array, the thin pointer needs to become
// a fat pointer, since pointers to strings and arrays/slices need to
// independently track the length of their view of the underlying storage.
if let TyKind::Slice(..) | TyKind::Str = &ty.kind {
// The given path is the storage for the fat pointer, so we need to
// store the thin pointer in path.0, rather than directly in path.
let thin_pointer_path = Path::new_field(path.clone(), 0);
self.bv
.current_environment
.update_value_at(thin_pointer_path, const_value.clone());
// Since this is a compile time constant, the length of the constant is
// also the length tracked in the fat pointer.
let length_path = Path::new_length(path);
let length_val = self.get_len(Path::new_alias(const_value));
self.bv
.current_environment
.update_value_at(length_path, length_val);
return;
}
}

match &const_value.expression {
Expression::HeapBlock { .. } => {
let rpath = Rc::new(
Expand Down Expand Up @@ -1594,40 +1620,13 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
},
_,
) => {
result = if let rustc_middle::ty::ConstKind::Value(ConstValue::Slice {
if let rustc_middle::ty::ConstKind::Value(ConstValue::Slice {
data,
start,
end,
}) = &literal.val
{
// The rust compiler should ensure this.
assume!(*end >= *start);
let slice_len = *end - *start;
let bytes = data
.get_bytes(
&self.bv.tcx,
// invent a pointer, only the offset is relevant anyway
mir::interpret::Pointer::new(
mir::interpret::AllocId(0),
rustc_target::abi::Size::from_bytes(*start as u64),
),
rustc_target::abi::Size::from_bytes(slice_len as u64),
)
.unwrap();

let slice = &bytes[*start..*end];
let s = std::str::from_utf8(slice).expect("non utf8 str");
let len_val: Rc<AbstractValue> =
Rc::new(ConstantDomain::U128(s.len() as u128).into());
let res = &mut self.bv.cv.constant_value_cache.get_string_for(s);

let path = Path::new_alias(Rc::new(res.clone().into()));
let len_path = Path::new_length(path);
self.bv
.current_environment
.update_value_at(len_path, len_val);

res
return self.get_value_from_slice(&ty.kind, data, *start, *end);
} else {
debug!("unsupported val of type Ref: {:?}", literal);
unimplemented!();
Expand Down Expand Up @@ -1673,7 +1672,8 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>

let slice = &bytes[*start..*end];
let e_type = ExpressionType::from(&elem_type.kind);
return self.deconstruct_constant_array(slice, e_type, None);
return self
.deconstruct_reference_to_constant_array(slice, e_type, None);
}
rustc_middle::ty::ConstKind::Value(ConstValue::ByRef { alloc, offset }) => {
let e_type = ExpressionType::from(&elem_type.kind);
Expand All @@ -1695,7 +1695,8 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
rustc_target::abi::Size::from_bytes(num_bytes),
)
.unwrap();
return self.deconstruct_constant_array(&bytes, e_type, None);
return self
.deconstruct_reference_to_constant_array(&bytes, e_type, None);
}
_ => {
debug!("unsupported val of type Ref: {:?}", literal);
Expand Down Expand Up @@ -1897,28 +1898,25 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
let slice = &bytes[start..end];
match ty {
TyKind::Ref(_, ty, _) => match ty.kind {
TyKind::Array(elem_type, ..) | TyKind::Slice(elem_type) => {
self.deconstruct_constant_array(slice, (&elem_type.kind).into(), None)
}
TyKind::Array(elem_type, ..) | TyKind::Slice(elem_type) => self
.deconstruct_reference_to_constant_array(slice, (&elem_type.kind).into(), None),
TyKind::Str => {
let s = std::str::from_utf8(slice).expect("non utf8 str");
let string_const = &mut self.bv.cv.constant_value_cache.get_string_for(s);
let string_val: Rc<AbstractValue> = Rc::new(string_const.clone().into());
let len_val: Rc<AbstractValue> =
Rc::new(ConstantDomain::U128(s.len() as u128).into());
let res: Rc<AbstractValue> = Rc::new(
self.bv
.cv
.constant_value_cache
.get_string_for(s)
.clone()
.into(),
);

let path = Path::new_alias(res.clone());
let len_path = Path::new_length(path);
let fat_pointer_path = Path::new_alias(string_val.clone());
let pointer_path = Path::new_field(fat_pointer_path.clone(), 0);
self.bv
.current_environment
.update_value_at(pointer_path, string_val.clone());
let len_path = Path::new_length(fat_pointer_path);
self.bv
.current_environment
.update_value_at(len_path, len_val);
res
string_val
}
_ => assume_unreachable!(),
},
Expand Down Expand Up @@ -1960,7 +1958,7 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
)
.unwrap();
let slice = &bytes[*start..*end];
self.deconstruct_constant_array(slice, e_type, Some(len))
self.deconstruct_reference_to_constant_array(slice, e_type, Some(len))
}
rustc_middle::ty::ConstKind::Value(ConstValue::ByRef { alloc, offset }) => {
//todo: there is no test coverage for this case
Expand All @@ -1985,7 +1983,7 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
rustc_target::abi::Size::from_bytes(num_bytes),
)
.unwrap();
self.deconstruct_constant_array(&bytes, e_type, Some(len))
self.deconstruct_reference_to_constant_array(&bytes, e_type, Some(len))
}
rustc_middle::ty::ConstKind::Value(ConstValue::Scalar(
mir::interpret::Scalar::Ptr(ptr),
Expand Down Expand Up @@ -2018,7 +2016,7 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
rustc_target::abi::Size::from_bytes(num_bytes),
)
.unwrap();
self.deconstruct_constant_array(&bytes, e_type, Some(len))
self.deconstruct_reference_to_constant_array(&bytes, e_type, Some(len))
}
_ => {
debug!("unsupported val of type Ref: {:?}", literal);
Expand All @@ -2041,7 +2039,7 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
/// The optional length is available as a separate compile time constant in the case of byte string
/// constants. It is passed in here to check against the length of the bytes array as a safety check.
#[logfn_inputs(TRACE)]
fn deconstruct_constant_array(
fn deconstruct_reference_to_constant_array(
&mut self,
bytes: &[u8],
elem_type: ExpressionType,
Expand All @@ -2051,23 +2049,26 @@ impl<'block, 'analysis, 'compilation, 'tcx, E>
let alignment = self.get_u128_const_val((elem_type.bit_length() / 8) as u128);
let byte_len_value = self.get_u128_const_val(byte_len as u128);
let array_value = self.bv.get_new_heap_block(byte_len_value, alignment, false);
if byte_len > k_limits::MAX_BYTE_ARRAY_LENGTH {
return array_value;
}
let array_path = Path::get_as_path(array_value);
let array_path = Path::get_as_path(array_value.clone());
let thin_pointer_path = Path::new_field(array_path.clone(), 0);
self.bv
.current_environment
.update_value_at(thin_pointer_path, array_value);
let mut last_index: u128 = 0;
for (i, operand) in self
.get_element_values(bytes, elem_type, len)
.into_iter()
.enumerate()
{
last_index = i as u128;
let index_value = self.get_u128_const_val(last_index);
let index_path = Path::new_index(array_path.clone(), index_value)
.refine_paths(&self.bv.current_environment); //todo: maybe not needed?
self.bv
.current_environment
.update_value_at(index_path, operand);
if i < k_limits::MAX_BYTE_ARRAY_LENGTH {
let index_value = self.get_u128_const_val(last_index);
let index_path = Path::new_index(array_path.clone(), index_value)
.refine_paths(&self.bv.current_environment); //todo: maybe not needed?
self.bv
.current_environment
.update_value_at(index_path, operand);
}
}
let length_path = Path::new_length(array_path.clone());
let length_value = self.get_u128_const_val(last_index + 1);
Expand Down
66 changes: 41 additions & 25 deletions checker/src/body_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,10 @@ impl<'analysis, 'compilation, 'tcx, E> BodyVisitor<'analysis, 'compilation, 'tcx
{
return Rc::new(abstract_value::TRUE);
}
if result_type != ExpressionType::Reference
&& result.expression.infer_type() == ExpressionType::Reference
if (result_type != ExpressionType::ThinPointer
&& result_type != ExpressionType::NonPrimitive)
&& (result.expression.infer_type() == ExpressionType::ThinPointer
|| result.expression.infer_type() == ExpressionType::NonPrimitive)
{
result.dereference(result_type)
} else {
Expand Down Expand Up @@ -1303,9 +1305,12 @@ impl<'analysis, 'compilation, 'tcx, E> BodyVisitor<'analysis, 'compilation, 'tcx
}
let target_type: ExpressionType = (&target_rustc_type.kind).into();
if target_type != ExpressionType::NonPrimitive || no_children {
let value = self.lookup_path_and_refine_result(source_path.clone(), target_rustc_type);
value_map =
self.expand_aliased_string_literals(&target_path, target_type, value_map, &value);
value_map = self.expand_aliased_string_literals_if_appropriate(
&target_path,
target_rustc_type,
value_map,
&value,
);
// Just copy/move (rpath, value) itself.
if move_elements {
trace!("moving {:?} to {:?}", value, target_path);
Expand Down Expand Up @@ -1451,31 +1456,42 @@ impl<'analysis, 'compilation, 'tcx, E> BodyVisitor<'analysis, 'compilation, 'tcx
None
}

// Check for assignment of a string literal to a byte array reference
pub fn expand_aliased_string_literals(
// Most of the time, references to string values are just moved around from one memory location
// to another and sometimes their lengths are taken. For such things there is no need to track
// the value of each character as a separate (path, char) tuple in the environment. That just
// slows things down and makes debugging more of chore.
// As soon as a string value manages to flow into a variable of type &[u8], however, we can
// expect expressions that look up individual characters and so we must enhance the current
// environment with (path, char) tuples. The root of the path part of such a tuple is the
// string literal itself, not the variable, which holds a pointer to the string.
#[logfn_inputs(DEBUG)]
pub fn expand_aliased_string_literals_if_appropriate(
&mut self,
target_path: &Rc<Path>,
rtype: ExpressionType,
target_path_rustc_ty: Ty<'tcx>,
mut value_map: HashTrieMap<Rc<Path>, Rc<AbstractValue>>,
value: &Rc<AbstractValue>,
) -> HashTrieMap<Rc<Path>, Rc<AbstractValue>> {
if rtype == ExpressionType::Reference {
if let Expression::CompileTimeConstant(ConstantDomain::Str(s)) = &value.expression {
if let PathEnum::LocalVariable { .. } = &target_path.value {
let lh_type = self
.type_visitor
.get_path_rustc_type(&target_path, self.current_span);
if let TyKind::Ref(_, ty, _) = lh_type.kind {
if let TyKind::Slice(elem_ty) = ty.kind {
if let TyKind::Uint(rustc_ast::ast::UintTy::U8) = elem_ty.kind {
let collection_path = Path::new_alias(value.clone());
for (i, ch) in s.as_bytes().iter().enumerate() {
let index = Rc::new((i as u128).into());
let ch_const = Rc::new((*ch as u128).into());
let path = Path::new_index(collection_path.clone(), index)
.refine_paths(&self.current_environment);
value_map = value_map.insert(path, ch_const);
}
if let Expression::CompileTimeConstant(ConstantDomain::Str(s)) = &value.expression {
// The value is a string literal
if let PathEnum::LocalVariable { .. } = &target_path.value {
// It is being copied into a local variable.
if let TyKind::Ref(_, ty, _) = target_path_rustc_ty.kind {
if let TyKind::Slice(elem_ty) = ty.kind {
if let TyKind::Uint(rustc_ast::ast::UintTy::U8) = elem_ty.kind {
// The type of the local variable is &[u8], so the local variable now
// provides a way to look at individual characters. The value of the
// local variable will be a fat pointer that points to the string literal,
// so we need to augment the environment with character information for
// the string literal. We could have done this at the time we created
// the string literal, but doing it on demand works out better for us.
let collection_path = Path::new_alias(value.clone());
for (i, ch) in s.as_bytes().iter().enumerate() {
let index = Rc::new((i as u128).into());
let ch_const = Rc::new((*ch as u128).into());
let path = Path::new_index(collection_path.clone(), index)
.refine_paths(&self.current_environment);
value_map = value_map.insert(path, ch_const);
}
}
}
Expand Down
Loading

0 comments on commit 213d1a3

Please sign in to comment.