Unverified Commit 29b2e1a0 by Melvin Klimke Committed by GitHub

Add optional data type param to arg (#198)

parent 094e9643
...@@ -15,7 +15,10 @@ fn new_id(time: &str, reg_name: &str) -> AbstractIdentifier { ...@@ -15,7 +15,10 @@ fn new_id(time: &str, reg_name: &str) -> AbstractIdentifier {
} }
fn mock_extern_symbol(name: &str) -> ExternSymbol { fn mock_extern_symbol(name: &str) -> ExternSymbol {
let arg = Arg::Register(register("RDX")); let arg = Arg::Register {
var: register("RDX"),
data_type: None,
};
ExternSymbol { ExternSymbol {
tid: Tid::new("extern_".to_string() + name), tid: Tid::new("extern_".to_string() + name),
addresses: vec![], addresses: vec![],
......
...@@ -238,8 +238,8 @@ impl State { ...@@ -238,8 +238,8 @@ impl State {
global_memory: &RuntimeMemoryImage, global_memory: &RuntimeMemoryImage,
) -> Result<Data, Error> { ) -> Result<Data, Error> {
match parameter { match parameter {
Arg::Register(var) => Ok(self.eval(&Expression::Var(var.clone()))), Arg::Register { var, .. } => Ok(self.eval(&Expression::Var(var.clone()))),
Arg::Stack { offset, size } => self.load_value( Arg::Stack { offset, size, .. } => self.load_value(
&Expression::Var(stack_pointer.clone()).plus_const(*offset), &Expression::Var(stack_pointer.clone()).plus_const(*offset),
*size, *size,
global_memory, global_memory,
......
...@@ -95,8 +95,8 @@ impl State { ...@@ -95,8 +95,8 @@ impl State {
let mut result_log = Ok(()); let mut result_log = Ok(());
for arg in &extern_call.parameters { for arg in &extern_call.parameters {
match arg { match arg {
Arg::Register(_) => (), Arg::Register { .. } => (),
Arg::Stack { offset, size } => { Arg::Stack { offset, size, .. } => {
let data_top = Data::new_top(*size); let data_top = Data::new_top(*size);
let location_expression = let location_expression =
Expression::Var(stack_pointer_register.clone()).plus_const(*offset); Expression::Var(stack_pointer_register.clone()).plus_const(*offset);
......
...@@ -290,6 +290,7 @@ fn clear_parameters_on_the_stack_on_extern_calls() { ...@@ -290,6 +290,7 @@ fn clear_parameters_on_the_stack_on_extern_calls() {
let stack_param = Arg::Stack { let stack_param = Arg::Stack {
offset: 8, offset: 8,
size: ByteSize::new(8), size: ByteSize::new(8),
data_type: None,
}; };
let extern_symbol = ExternSymbol { let extern_symbol = ExternSymbol {
tid: Tid::new("symbol"), tid: Tid::new("symbol"),
......
...@@ -184,7 +184,7 @@ impl<'a> Context<'a> { ...@@ -184,7 +184,7 @@ impl<'a> Context<'a> {
) -> bool { ) -> bool {
// First check for taint directly in parameter registers (we don't need a pointer inference state for that) // First check for taint directly in parameter registers (we don't need a pointer inference state for that)
for parameter in extern_symbol.parameters.iter() { for parameter in extern_symbol.parameters.iter() {
if let Arg::Register(var) = parameter { if let Arg::Register { var, .. } = parameter {
if state.eval(&Expression::Var(var.clone())).is_tainted() { if state.eval(&Expression::Var(var.clone())).is_tainted() {
return true; return true;
} }
...@@ -196,13 +196,13 @@ impl<'a> Context<'a> { ...@@ -196,13 +196,13 @@ impl<'a> Context<'a> {
// Check stack parameters and collect referenced memory object that need to be checked for taint. // Check stack parameters and collect referenced memory object that need to be checked for taint.
for parameter in extern_symbol.parameters.iter() { for parameter in extern_symbol.parameters.iter() {
match parameter { match parameter {
Arg::Register(var) => { Arg::Register { var, .. } => {
let data = pi_state.eval(&Expression::Var(var.clone())); let data = pi_state.eval(&Expression::Var(var.clone()));
if state.check_if_address_points_to_taint(data, pi_state) { if state.check_if_address_points_to_taint(data, pi_state) {
return true; return true;
} }
} }
Arg::Stack { offset, size } => { Arg::Stack { offset, size, .. } => {
let stack_address = pi_state.eval( let stack_address = pi_state.eval(
&Expression::Var(self.project.stack_pointer_register.clone()) &Expression::Var(self.project.stack_pointer_register.clone())
.plus_const(*offset), .plus_const(*offset),
......
...@@ -92,12 +92,12 @@ impl State { ...@@ -92,12 +92,12 @@ impl State {
}; };
for return_arg in taint_source.return_values.iter() { for return_arg in taint_source.return_values.iter() {
match return_arg { match return_arg {
Arg::Register(var) => { Arg::Register { var, .. } => {
state state
.register_taint .register_taint
.insert(var.clone(), Taint::Tainted(var.size)); .insert(var.clone(), Taint::Tainted(var.size));
} }
Arg::Stack { offset, size } => { Arg::Stack { offset, size, .. } => {
if let Some(pi_state) = pi_state { if let Some(pi_state) = pi_state {
let address_exp = let address_exp =
Expression::Var(stack_pointer_register.clone()).plus_const(*offset); Expression::Var(stack_pointer_register.clone()).plus_const(*offset);
...@@ -395,10 +395,14 @@ mod tests { ...@@ -395,10 +395,14 @@ mod tests {
} }
pub fn mock_with_pi_state() -> (State, PointerInferenceState) { pub fn mock_with_pi_state() -> (State, PointerInferenceState) {
let arg1 = Arg::Register(register("RAX")); let arg1 = Arg::Register {
var: register("RAX"),
data_type: None,
};
let arg2 = Arg::Stack { let arg2 = Arg::Stack {
offset: 0, offset: 0,
size: ByteSize::new(8), size: ByteSize::new(8),
data_type: None,
}; };
let pi_state = PointerInferenceState::new(&register("RSP"), Tid::new("func")); let pi_state = PointerInferenceState::new(&register("RSP"), Tid::new("func"));
let symbol = ExternSymbol { let symbol = ExternSymbol {
......
...@@ -74,7 +74,7 @@ impl<'a> Context<'a> { ...@@ -74,7 +74,7 @@ impl<'a> Context<'a> {
.pointer_inference_results .pointer_inference_results
.get_node_value(call_source_node) .get_node_value(call_source_node)
{ {
if let Ok(parameters) = arguments::get_variable_number_parameters( if let Ok(parameters) = arguments::get_variable_parameters(
self.project, self.project,
pi_state, pi_state,
user_input_symbol, user_input_symbol,
...@@ -228,7 +228,7 @@ impl<'a> Context<'a> { ...@@ -228,7 +228,7 @@ impl<'a> Context<'a> {
if self.is_relevant_string_function_call(string_symbol, pi_state, &mut new_state) { if self.is_relevant_string_function_call(string_symbol, pi_state, &mut new_state) {
let mut parameters = string_symbol.parameters.clone(); let mut parameters = string_symbol.parameters.clone();
if string_symbol.has_var_args { if string_symbol.has_var_args {
if let Ok(args) = arguments::get_variable_number_parameters( if let Ok(args) = arguments::get_variable_parameters(
self.project, self.project,
pi_state, pi_state,
string_symbol, string_symbol,
...@@ -272,7 +272,9 @@ impl<'a> Context<'a> { ...@@ -272,7 +272,9 @@ impl<'a> Context<'a> {
) { ) {
for parameter in parameters.iter() { for parameter in parameters.iter() {
match parameter { match parameter {
Arg::Register(var) => state.set_register_taint(var, Taint::Tainted(var.size)), Arg::Register { var, .. } => {
state.set_register_taint(var, Taint::Tainted(var.size))
}
Arg::Stack { size, .. } => { Arg::Stack { size, .. } => {
if let Ok(address) = pi_state.eval_parameter_arg( if let Ok(address) = pi_state.eval_parameter_arg(
parameter, parameter,
......
...@@ -246,6 +246,7 @@ fn processing_scanf() { ...@@ -246,6 +246,7 @@ fn processing_scanf() {
let string_arg = Arg::Stack { let string_arg = Arg::Stack {
offset: 0, offset: 0,
size: ByteSize::new(8), size: ByteSize::new(8),
data_type: None,
}; };
let (cwe_sender, cwe_receiver) = crossbeam_channel::unbounded::<CweWarning>(); let (cwe_sender, cwe_receiver) = crossbeam_channel::unbounded::<CweWarning>();
...@@ -307,6 +308,7 @@ fn processing_sscanf() { ...@@ -307,6 +308,7 @@ fn processing_sscanf() {
let string_arg = Arg::Stack { let string_arg = Arg::Stack {
offset: 0, offset: 0,
size: ByteSize::new(8), size: ByteSize::new(8),
data_type: None,
}; };
let mem_image = RuntimeMemoryImage::mock(); let mem_image = RuntimeMemoryImage::mock();
...@@ -352,7 +354,10 @@ fn processing_sscanf() { ...@@ -352,7 +354,10 @@ fn processing_sscanf() {
&mut setup.state, &mut setup.state,
&setup.pi_state, &setup.pi_state,
vec![string_arg], vec![string_arg],
&Arg::Register(rdi_reg), &Arg::Register {
var: rdi_reg,
data_type: None,
},
); );
assert!(setup assert!(setup
...@@ -368,10 +373,14 @@ fn tainting_function_arguments() { ...@@ -368,10 +373,14 @@ fn tainting_function_arguments() {
let mut setup = Setup::new(); let mut setup = Setup::new();
let rdi_reg = Variable::mock("RDI", 8); let rdi_reg = Variable::mock("RDI", 8);
let args = vec![ let args = vec![
Arg::Register(rdi_reg.clone()), Arg::Register {
var: rdi_reg.clone(),
data_type: None,
},
Arg::Stack { Arg::Stack {
offset: 24, offset: 24,
size: ByteSize::from(8), size: ByteSize::from(8),
data_type: None,
}, },
]; ];
......
...@@ -284,7 +284,10 @@ fn first_param_pointing_to_memory_taint() { ...@@ -284,7 +284,10 @@ fn first_param_pointing_to_memory_taint() {
&mem_image, &mem_image,
); );
let arg = Arg::Register(rdi_reg); let arg = Arg::Register {
var: rdi_reg,
data_type: None,
};
assert_eq!( assert_eq!(
context.first_param_points_to_memory_taint(&setup.pi_state, &mut setup.state, &arg), context.first_param_points_to_memory_taint(&setup.pi_state, &mut setup.state, &arg),
true true
......
...@@ -108,12 +108,12 @@ impl State { ...@@ -108,12 +108,12 @@ impl State {
}; };
for parameter in taint_source.parameters.iter() { for parameter in taint_source.parameters.iter() {
match parameter { match parameter {
Arg::Register(var) => { Arg::Register { var, .. } => {
state state
.register_taint .register_taint
.insert(var.clone(), Taint::Tainted(var.size)); .insert(var.clone(), Taint::Tainted(var.size));
} }
Arg::Stack { offset, size } => { Arg::Stack { offset, size, .. } => {
if let Some(pi_state) = pi_state { if let Some(pi_state) = pi_state {
let address_exp = let address_exp =
Expression::Var(stack_pointer_register.clone()).plus_const(*offset); Expression::Var(stack_pointer_register.clone()).plus_const(*offset);
......
...@@ -25,7 +25,10 @@ fn bv(value: i64) -> ValueDomain { ...@@ -25,7 +25,10 @@ fn bv(value: i64) -> ValueDomain {
impl State { impl State {
pub fn mock_with_pi_state() -> (State, PointerInferenceState) { pub fn mock_with_pi_state() -> (State, PointerInferenceState) {
let arg = Arg::Register(Variable::mock("RAX", 8 as u64)); let arg = Arg::Register {
var: Variable::mock("RAX", 8 as u64),
data_type: None,
};
let pi_state = let pi_state =
PointerInferenceState::new(&Variable::mock("RSP", 8 as u64), Tid::new("func")); PointerInferenceState::new(&Variable::mock("RSP", 8 as u64), Tid::new("func"));
let symbol = extern_symbol("system", vec![arg], false); let symbol = extern_symbol("system", vec![arg], false);
......
...@@ -110,6 +110,29 @@ pub struct DatatypeProperties { ...@@ -110,6 +110,29 @@ pub struct DatatypeProperties {
pub short_size: ByteSize, pub short_size: ByteSize,
} }
/// C/C++ data types.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum Datatype {
/// C char data type
Char,
/// C double data type
Double,
/// C float data type
Float,
/// C integer data type
Integer,
/// C long double data type
LongDouble,
/// C long long data type
LongLong,
/// C long data type
Long,
/// C pointer data type
Pointer,
/// C short data type
Short,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use apint::BitWidth; use apint::BitWidth;
......
use super::{ByteSize, CastOpType, DatatypeProperties, Expression, Variable}; use super::{ByteSize, CastOpType, Datatype, DatatypeProperties, Expression, Variable};
use crate::prelude::*; use crate::prelude::*;
use crate::utils::log::LogMessage; use crate::utils::log::LogMessage;
use std::collections::HashSet; use std::collections::HashSet;
...@@ -455,7 +455,12 @@ pub struct Sub { ...@@ -455,7 +455,12 @@ pub struct Sub {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum Arg { pub enum Arg {
/// The argument is passed in the given register /// The argument is passed in the given register
Register(Variable), Register {
/// The variable object representing the register.
var: Variable,
/// An optional data type indicator.
data_type: Option<Datatype>,
},
/// The argument is passed on the stack. /// The argument is passed on the stack.
/// It is positioned at the given offset (in bytes) relative to the stack pointer on function entry /// It is positioned at the given offset (in bytes) relative to the stack pointer on function entry
/// and has the given size. /// and has the given size.
...@@ -465,6 +470,8 @@ pub enum Arg { ...@@ -465,6 +470,8 @@ pub enum Arg {
offset: i64, offset: i64,
/// The size in bytes of the argument. /// The size in bytes of the argument.
size: ByteSize, size: ByteSize,
/// An optional data type indicator.
data_type: Option<Datatype>,
}, },
} }
...@@ -497,7 +504,7 @@ impl ExternSymbol { ...@@ -497,7 +504,7 @@ impl ExternSymbol {
pub fn get_unique_return_register(&self) -> Result<&Variable, Error> { pub fn get_unique_return_register(&self) -> Result<&Variable, Error> {
if self.return_values.len() == 1 { if self.return_values.len() == 1 {
match self.return_values[0] { match self.return_values[0] {
Arg::Register(ref var) => Ok(var), Arg::Register { ref var, .. } => Ok(var),
Arg::Stack { .. } => Err(anyhow!("Return value is passed on the stack")), Arg::Stack { .. } => Err(anyhow!("Return value is passed on the stack")),
} }
} else { } else {
...@@ -814,7 +821,10 @@ mod tests { ...@@ -814,7 +821,10 @@ mod tests {
impl Arg { impl Arg {
pub fn mock_register(name: impl ToString, size_in_bytes: impl Into<ByteSize>) -> Arg { pub fn mock_register(name: impl ToString, size_in_bytes: impl Into<ByteSize>) -> Arg {
Arg::Register(Variable::mock(name.to_string(), size_in_bytes)) Arg::Register {
var: Variable::mock(name.to_string(), size_in_bytes),
data_type: None,
}
} }
} }
......
...@@ -524,7 +524,10 @@ impl ExternSymbol { ...@@ -524,7 +524,10 @@ impl ExternSymbol {
} }
for arg in symbol.arguments.iter() { for arg in symbol.arguments.iter() {
let ir_arg = if let Some(var) = arg.var.clone() { let ir_arg = if let Some(var) = arg.var.clone() {
IrArg::Register(var.into()) IrArg::Register {
var: var.into(),
data_type: None,
}
} else if let Some(expr) = arg.location.clone() { } else if let Some(expr) = arg.location.clone() {
if expr.mnemonic == ExpressionType::LOAD { if expr.mnemonic == ExpressionType::LOAD {
IrArg::Stack { IrArg::Stack {
...@@ -539,6 +542,7 @@ impl ExternSymbol { ...@@ -539,6 +542,7 @@ impl ExternSymbol {
) )
.unwrap(), .unwrap(),
size: expr.input0.unwrap().size, size: expr.input0.unwrap().size,
data_type: None,
} }
} else { } else {
panic!() panic!()
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::prelude::*; use crate::{intermediate_representation::Datatype, prelude::*};
use regex::Regex; use regex::Regex;
...@@ -22,7 +22,7 @@ pub fn get_return_registers_from_symbol(symbol: &ExternSymbol) -> Vec<String> { ...@@ -22,7 +22,7 @@ pub fn get_return_registers_from_symbol(symbol: &ExternSymbol) -> Vec<String> {
.return_values .return_values
.iter() .iter()
.filter_map(|ret| match ret { .filter_map(|ret| match ret {
Arg::Register(var) => Some(var.name.clone()), Arg::Register { var, .. } => Some(var.name.clone()),
_ => None, _ => None,
}) })
.collect::<Vec<String>>() .collect::<Vec<String>>()
...@@ -117,7 +117,7 @@ pub fn map_format_specifier_to_bytesize( ...@@ -117,7 +117,7 @@ pub fn map_format_specifier_to_bytesize(
} }
/// Returns an argument vector of detected variable parameters if they are of type string. /// Returns an argument vector of detected variable parameters if they are of type string.
pub fn get_variable_number_parameters( pub fn get_variable_parameters(
project: &Project, project: &Project,
pi_state: &PointerInferenceState, pi_state: &PointerInferenceState,
extern_symbol: &ExternSymbol, extern_symbol: &ExternSymbol,
...@@ -209,16 +209,20 @@ pub fn create_string_stack_arg(size: ByteSize, stack_offset: i64) -> Arg { ...@@ -209,16 +209,20 @@ pub fn create_string_stack_arg(size: ByteSize, stack_offset: i64) -> Arg {
Arg::Stack { Arg::Stack {
offset: stack_offset, offset: stack_offset,
size, size,
data_type: Some(Datatype::Pointer),
} }
} }
/// Creates a string register parameter given a register name. /// Creates a string register parameter given a register name.
pub fn create_string_register_arg(size: ByteSize, register_name: String) -> Arg { pub fn create_string_register_arg(size: ByteSize, register_name: String) -> Arg {
Arg::Register(Variable { Arg::Register {
name: register_name, var: Variable {
size, name: register_name,
is_temp: false, size,
}) is_temp: false,
},
data_type: Some(Datatype::Pointer),
}
} }
/// Checks whether the format specifier is of type int. /// Checks whether the format specifier is of type int.
......
...@@ -36,7 +36,7 @@ fn test_get_variable_number_parameters() { ...@@ -36,7 +36,7 @@ fn test_get_variable_number_parameters() {
let mut output: Vec<Arg> = Vec::new(); let mut output: Vec<Arg> = Vec::new();
assert_eq!( assert_eq!(
output, output,
get_variable_number_parameters( get_variable_parameters(
&project, &project,
&pi_state, &pi_state,
&sprintf_symbol, &sprintf_symbol,
...@@ -49,6 +49,7 @@ fn test_get_variable_number_parameters() { ...@@ -49,6 +49,7 @@ fn test_get_variable_number_parameters() {
output.push(Arg::Stack { output.push(Arg::Stack {
offset: 0, offset: 0,
size: ByteSize::new(8), size: ByteSize::new(8),
data_type: Some(Datatype::Pointer),
}); });
let global_address = Bitvector::from_str_radix(16, "500c").unwrap(); let global_address = Bitvector::from_str_radix(16, "500c").unwrap();
...@@ -59,7 +60,7 @@ fn test_get_variable_number_parameters() { ...@@ -59,7 +60,7 @@ fn test_get_variable_number_parameters() {
assert_eq!( assert_eq!(
output, output,
get_variable_number_parameters( get_variable_parameters(
&project, &project,
&pi_state, &pi_state,
&sprintf_symbol, &sprintf_symbol,
...@@ -187,7 +188,10 @@ fn test_calculate_parameter_locations() { ...@@ -187,7 +188,10 @@ fn test_calculate_parameter_locations() {
parameters.push(("f".to_string(), ByteSize::new(8))); parameters.push(("f".to_string(), ByteSize::new(8)));
parameters.push(("s".to_string(), ByteSize::new(4))); parameters.push(("s".to_string(), ByteSize::new(4)));
let mut expected_args = vec![Arg::Register(Variable::mock("R9", ByteSize::new(8)))]; let mut expected_args = vec![Arg::Register {
var: Variable::mock("R9", ByteSize::new(8)),
data_type: Some(Datatype::Pointer),
}];
// Test Case 1: The string parameter is still written in the R9 register since 'f' is contained in the float register. // Test Case 1: The string parameter is still written in the R9 register since 'f' is contained in the float register.
assert_eq!( assert_eq!(
...@@ -199,6 +203,7 @@ fn test_calculate_parameter_locations() { ...@@ -199,6 +203,7 @@ fn test_calculate_parameter_locations() {
expected_args.push(Arg::Stack { expected_args.push(Arg::Stack {
offset: 0, offset: 0,
size: ByteSize::new(4), size: ByteSize::new(4),
data_type: Some(Datatype::Pointer),
}); });
// Test Case 2: A second string parameter does not fit into the registers anymore and is written into the stack. // Test Case 2: A second string parameter does not fit into the registers anymore and is written into the stack.
...@@ -214,6 +219,7 @@ fn test_create_string_stack_arg() { ...@@ -214,6 +219,7 @@ fn test_create_string_stack_arg() {
Arg::Stack { Arg::Stack {
size: ByteSize::new(8), size: ByteSize::new(8),
offset: 8, offset: 8,
data_type: Some(Datatype::Pointer),
}, },
create_string_stack_arg(ByteSize::new(8), 8), create_string_stack_arg(ByteSize::new(8), 8),
) )
...@@ -222,7 +228,10 @@ fn test_create_string_stack_arg() { ...@@ -222,7 +228,10 @@ fn test_create_string_stack_arg() {
#[test] #[test]
fn test_create_string_register_arg() { fn test_create_string_register_arg() {
assert_eq!( assert_eq!(
Arg::Register(Variable::mock("R9", ByteSize::new(8))), Arg::Register {
var: Variable::mock("R9", ByteSize::new(8)),
data_type: Some(Datatype::Pointer),
},
create_string_register_arg(ByteSize::new(8), "R9".to_string()), create_string_register_arg(ByteSize::new(8), "R9".to_string()),
); );
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment