Skip to content

Commit

Permalink
Handle void functions in get_possible_return_values_of_func
Browse files Browse the repository at this point in the history
Fixes #10.

Also add some additional checks at other callsites that previously
assumed size_in_bits() never returned 0.
  • Loading branch information
cdisselkoen committed Sep 19, 2020
1 parent d98b10f commit b69a2a6
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 4 deletions.
1 change: 1 addition & 0 deletions src/function_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ pub fn generic_stub_hook<B: Backend>(
let width = state.size_in_bits(ty).ok_or_else(|| {
Error::OtherError("Call return type is an opaque named struct".into())
})?;
assert_ne!(width, 0, "Call return type has size 0 bits but isn't void type"); // void type was handled above
let bv = state.new_bv_with_name(Name::from("generic_stub_hook_retval"), width)?;
Ok(ReturnValue::Return(bv))
},
Expand Down
3 changes: 3 additions & 0 deletions src/hooks/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@ pub fn symex_objectsize<'p, B: Backend>(
let width = state.size_in_bits(&state.type_of(call)).ok_or_else(||
Error::OtherError("symex_objectsize: return value of this call involves a struct type with no definition in the Project".into())
)?;
if width == 0 {
return Err(Error::OtherError("symex_objectsize: didn't expect return type to have size 0 bits".into()));
}
let zero = state.zero(width);
let minusone = state.ones(width);
Ok(ReturnValue::Return(arg1.cond_bv(&zero, &minusone)))
Expand Down
19 changes: 15 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,18 @@ pub fn find_zero_of_func<'p>(
}
}

let returnwidth = project
.size_in_bits(&func.return_type)
.expect("Function return type shouldn't be an opaque struct type");
let returnwidth = match func.return_type.as_ref() {
Type::VoidType => {
return Err("find_zero_of_func: function has void type".into());
},
ty => {
let width = project
.size_in_bits(&ty)
.expect("Function return type shouldn't be an opaque struct type");
assert_ne!(width, 0, "Function return type has width 0 bits but isn't void type"); // void type was handled above
width
},
};
let zero = em.state().zero(returnwidth);
let mut found = false;
while let Some(bvretval) = em.next() {
Expand Down Expand Up @@ -228,6 +237,7 @@ pub fn get_possible_return_values_of_func<'p>(
let param_size_bits = project
.size_in_bits(&param.ty)
.expect("Parameter type shouldn't be opaque struct type");
assert_ne!(param_size_bits, 0, "Parameter {} shouldn't have size 0 bits", &param.name);
let val = em.state().bv_from_u64(val, param_size_bits);
em.mut_state()
.overwrite_latest_version_of_bv(&param.name, val);
Expand Down Expand Up @@ -255,6 +265,7 @@ pub fn get_possible_return_values_of_func<'p>(
}
},
Ok(ReturnValue::Return(bvretval)) => {
assert_eq!(bvretval.get_width(), return_width);
let state = em.mut_state();
// rule out all the returned values we already have - we're interested in new values
for candidate in candidate_values.iter() {
Expand Down Expand Up @@ -301,7 +312,7 @@ pub fn get_possible_return_values_of_func<'p>(
for candidate in candidate_values.iter() {
if let ReturnValue::Throw(candidate) = candidate {
thrown_value
._ne(&state.bv_from_u64(*candidate, return_width))
._ne(&state.bv_from_u64(*candidate, thrown_size))
.assert();
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ impl Project {
///
/// Accounts for the `Project`'s pointer size and named struct definitions.
///
/// Note that some types have size 0 bits, and this may return `Some(0)`.
///
/// Returns `None` for structs which have no definition in the entire `Project`,
/// or for structs/arrays/vectors where one of the elements is a struct with no
/// definition in the entire `Project`.
Expand All @@ -350,6 +352,7 @@ impl Project {
(NamedStructDef::Opaque, _) => None,
(NamedStructDef::Defined(ty), _) => self.size_in_bits(&ty),
},
Type::VoidType => Some(0),
ty => panic!("Not sure how to get the size of {:?}", ty),
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,7 @@ where
c
))
})?;
assert_ne!(size_bits, 0, "const_to_bv: can't convert constant of size 0 to a BV; use const_to_bv_maybe_zerowidth() instead");
Ok(self.zero(size_bits))
},
Constant::Struct {
Expand Down Expand Up @@ -1108,6 +1109,7 @@ where
c
))
})?;
assert_ne!(to_size_bits, 0, "const_to_bv: can't convert constant of size 0 to a BV; use const_to_bv_maybe_zerowidth() instead");
self.const_to_bv(&t.operand)
.map(|bv| bv.slice(to_size_bits - 1, 0))
},
Expand All @@ -1118,6 +1120,7 @@ where
c
))
})?;
assert_ne!(to_size_bits, 0, "const_to_bv: can't convert constant of size 0 to a BV; use const_to_bv_maybe_zerowidth() instead");
self.const_to_bv(&z.operand)
.map(|bv| bv.zero_extend_to_bits(to_size_bits))
},
Expand All @@ -1128,6 +1131,7 @@ where
c
))
})?;
assert_ne!(to_size_bits, 0, "const_to_bv: can't convert constant of size 0 to a BV; use const_to_bv_maybe_zerowidth() instead");
self.const_to_bv(&s.operand)
.map(|bv| bv.sign_extend_to_bits(to_size_bits))
},
Expand Down Expand Up @@ -1582,6 +1586,8 @@ where
///
/// Accounts for the `Project`'s pointer size and named struct definitions.
///
/// Note that some types have size 0 bits, and this may return `0`.
///
/// Panics if `ty` is a struct which has no definition in the entire `Project`,
/// or if it is a struct/array/vector where one of the elements is a struct with no
/// definition in the entire `Project`.
Expand All @@ -1596,6 +1602,8 @@ where
///
/// Accounts for the `Project`'s pointer size and named struct definitions.
///
/// Note that some types have size 0 bits, and this may return `Some(0)`.
///
/// Returns `None` for structs which have no definition in the entire `Project`,
/// or for structs/arrays/vectors where one of the elements is a struct with no
/// definition in the entire `Project`.
Expand All @@ -1608,6 +1616,8 @@ where
///
/// Accounts for the `Project`'s pointer size and named struct definitions.
///
/// Note that some types have size 0 bits, and this may return `Some(0)`.
///
/// Returns `None` for structs which have no definition in the entire `Project`,
/// or for structs/arrays/vectors where one of the elements is a struct with no
/// definition in the entire `Project`.
Expand Down
6 changes: 6 additions & 0 deletions src/symex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ pub fn symex_function<'p, B: Backend>(
let param_size = state
.size_in_bits(&param.ty)
.expect("Parameter type is a struct opaque in the entire Project");
assert_ne!(param_size, 0, "Parameter {} shouldn't have size 0 bits", &param.name);
state
.new_bv_with_name(param.name.clone(), param_size)
.unwrap()
Expand Down Expand Up @@ -732,6 +733,9 @@ where
.ok_or_else(|| {
Error::MalformedInstruction("Load result type is an opaque struct type".into())
})?;
if dest_size == 0 {
return Err(Error::MalformedInstruction("Shouldn't be loading a value of size 0 bits".into()));
}
self.state
.record_bv_result(load, self.state.read(&bvaddr, dest_size)?)
}
Expand Down Expand Up @@ -1237,6 +1241,7 @@ where
"Call return type is an opaque struct type".into(),
)
})?;
assert_ne!(width, 0, "Function return type has size 0 bits but isn't void type"); // void type was handled above
let bv = self.state.new_bv_with_name(
Name::from(format!("{}_retval", called_funcname)),
width,
Expand Down Expand Up @@ -1881,6 +1886,7 @@ where
"Invoke return type is an opaque struct type".into(),
)
})?;
assert_ne!(width, 0, "Invoke return type has size 0 bits but isn't void type"); // void type was handled above
let bv = self.state.new_bv_with_name(
Name::from(format!("{}_retval", called_funcname)),
width,
Expand Down
25 changes: 25 additions & 0 deletions tests/basic_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ fn get_issue_9_project() -> Project {
.unwrap_or_else(|e| panic!("Failed to parse module {:?}: {}", modname, e))
}

fn get_issue_10_project() -> Project {
let modname = "tests/bcfiles/issue_10.bc";
Project::from_bc_path(modname)
.unwrap_or_else(|e| panic!("Failed to parse module {:?}: {}", modname, e))
}

#[test]
fn no_args_nozero() {
let funcname = "no_args_nozero";
Expand Down Expand Up @@ -342,3 +348,22 @@ fn issue_9() {
PossibleSolutions::exactly_two(ReturnValue::Return(1), ReturnValue::Abort)
);
}

#[test]
fn issue_10() {
let funcname = "issue_10::panic_if_not_zero";
init_logging();
let proj = get_issue_10_project();
let ret = get_possible_return_values_of_func(
funcname,
vec![None],
&proj,
Config::default(),
None,
10,
);
assert_eq!(
ret,
PossibleSolutions::exactly_two(ReturnValue::ReturnVoid, ReturnValue::Abort)
);
}
1 change: 1 addition & 0 deletions tests/bcfiles/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ RUST32BIT=--target i686-unknown-linux-gnu
all: basic.bc basic.ll \
issue_4.bc issue_4.ll \
issue_9.bc issue_9.ll \
issue_10.bc issue_10.ll \
memory.bc memory.ll \
loop.bc loop.ll \
struct.bc struct.ll \
Expand Down
Binary file added tests/bcfiles/issue_10.bc
Binary file not shown.
Loading

0 comments on commit b69a2a6

Please sign in to comment.