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 {
}
fn mock_extern_symbol(name: &str) -> ExternSymbol {
let arg = Arg::Register(register("RDX"));
let arg = Arg::Register {
var: register("RDX"),
data_type: None,
};
ExternSymbol {
tid: Tid::new("extern_".to_string() + name),
addresses: vec![],
......
......@@ -238,8 +238,8 @@ impl State {
global_memory: &RuntimeMemoryImage,
) -> Result<Data, Error> {
match parameter {
Arg::Register(var) => Ok(self.eval(&Expression::Var(var.clone()))),
Arg::Stack { offset, size } => self.load_value(
Arg::Register { var, .. } => Ok(self.eval(&Expression::Var(var.clone()))),
Arg::Stack { offset, size, .. } => self.load_value(
&Expression::Var(stack_pointer.clone()).plus_const(*offset),
*size,
global_memory,
......
......@@ -95,8 +95,8 @@ impl State {
let mut result_log = Ok(());
for arg in &extern_call.parameters {
match arg {
Arg::Register(_) => (),
Arg::Stack { offset, size } => {
Arg::Register { .. } => (),
Arg::Stack { offset, size, .. } => {
let data_top = Data::new_top(*size);
let location_expression =
Expression::Var(stack_pointer_register.clone()).plus_const(*offset);
......
......@@ -290,6 +290,7 @@ fn clear_parameters_on_the_stack_on_extern_calls() {
let stack_param = Arg::Stack {
offset: 8,
size: ByteSize::new(8),
data_type: None,
};
let extern_symbol = ExternSymbol {
tid: Tid::new("symbol"),
......
......@@ -184,7 +184,7 @@ impl<'a> Context<'a> {
) -> bool {
// 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() {
if let Arg::Register(var) = parameter {
if let Arg::Register { var, .. } = parameter {
if state.eval(&Expression::Var(var.clone())).is_tainted() {
return true;
}
......@@ -196,13 +196,13 @@ impl<'a> Context<'a> {
// Check stack parameters and collect referenced memory object that need to be checked for taint.
for parameter in extern_symbol.parameters.iter() {
match parameter {
Arg::Register(var) => {
Arg::Register { var, .. } => {
let data = pi_state.eval(&Expression::Var(var.clone()));
if state.check_if_address_points_to_taint(data, pi_state) {
return true;
}
}
Arg::Stack { offset, size } => {
Arg::Stack { offset, size, .. } => {
let stack_address = pi_state.eval(
&Expression::Var(self.project.stack_pointer_register.clone())
.plus_const(*offset),
......
......@@ -92,12 +92,12 @@ impl State {
};
for return_arg in taint_source.return_values.iter() {
match return_arg {
Arg::Register(var) => {
Arg::Register { var, .. } => {
state
.register_taint
.insert(var.clone(), Taint::Tainted(var.size));
}
Arg::Stack { offset, size } => {
Arg::Stack { offset, size, .. } => {
if let Some(pi_state) = pi_state {
let address_exp =
Expression::Var(stack_pointer_register.clone()).plus_const(*offset);
......@@ -395,10 +395,14 @@ mod tests {
}
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 {
offset: 0,
size: ByteSize::new(8),
data_type: None,
};
let pi_state = PointerInferenceState::new(&register("RSP"), Tid::new("func"));
let symbol = ExternSymbol {
......
......@@ -74,7 +74,7 @@ impl<'a> Context<'a> {
.pointer_inference_results
.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,
pi_state,
user_input_symbol,
......@@ -228,7 +228,7 @@ impl<'a> Context<'a> {
if self.is_relevant_string_function_call(string_symbol, pi_state, &mut new_state) {
let mut parameters = string_symbol.parameters.clone();
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,
pi_state,
string_symbol,
......@@ -272,7 +272,9 @@ impl<'a> Context<'a> {
) {
for parameter in parameters.iter() {
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, .. } => {
if let Ok(address) = pi_state.eval_parameter_arg(
parameter,
......
......@@ -246,6 +246,7 @@ fn processing_scanf() {
let string_arg = Arg::Stack {
offset: 0,
size: ByteSize::new(8),
data_type: None,
};
let (cwe_sender, cwe_receiver) = crossbeam_channel::unbounded::<CweWarning>();
......@@ -307,6 +308,7 @@ fn processing_sscanf() {
let string_arg = Arg::Stack {
offset: 0,
size: ByteSize::new(8),
data_type: None,
};
let mem_image = RuntimeMemoryImage::mock();
......@@ -352,7 +354,10 @@ fn processing_sscanf() {
&mut setup.state,
&setup.pi_state,
vec![string_arg],
&Arg::Register(rdi_reg),
&Arg::Register {
var: rdi_reg,
data_type: None,
},
);
assert!(setup
......@@ -368,10 +373,14 @@ fn tainting_function_arguments() {
let mut setup = Setup::new();
let rdi_reg = Variable::mock("RDI", 8);
let args = vec![
Arg::Register(rdi_reg.clone()),
Arg::Register {
var: rdi_reg.clone(),
data_type: None,
},
Arg::Stack {
offset: 24,
size: ByteSize::from(8),
data_type: None,
},
];
......
......@@ -284,7 +284,10 @@ fn first_param_pointing_to_memory_taint() {
&mem_image,
);
let arg = Arg::Register(rdi_reg);
let arg = Arg::Register {
var: rdi_reg,
data_type: None,
};
assert_eq!(
context.first_param_points_to_memory_taint(&setup.pi_state, &mut setup.state, &arg),
true
......
......@@ -108,12 +108,12 @@ impl State {
};
for parameter in taint_source.parameters.iter() {
match parameter {
Arg::Register(var) => {
Arg::Register { var, .. } => {
state
.register_taint
.insert(var.clone(), Taint::Tainted(var.size));
}
Arg::Stack { offset, size } => {
Arg::Stack { offset, size, .. } => {
if let Some(pi_state) = pi_state {
let address_exp =
Expression::Var(stack_pointer_register.clone()).plus_const(*offset);
......
......@@ -25,7 +25,10 @@ fn bv(value: i64) -> ValueDomain {
impl State {
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 =
PointerInferenceState::new(&Variable::mock("RSP", 8 as u64), Tid::new("func"));
let symbol = extern_symbol("system", vec![arg], false);
......
......@@ -110,6 +110,29 @@ pub struct DatatypeProperties {
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)]
mod tests {
use apint::BitWidth;
......
use super::{ByteSize, CastOpType, DatatypeProperties, Expression, Variable};
use super::{ByteSize, CastOpType, Datatype, DatatypeProperties, Expression, Variable};
use crate::prelude::*;
use crate::utils::log::LogMessage;
use std::collections::HashSet;
......@@ -455,7 +455,12 @@ pub struct Sub {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub enum Arg {
/// 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.
/// It is positioned at the given offset (in bytes) relative to the stack pointer on function entry
/// and has the given size.
......@@ -465,6 +470,8 @@ pub enum Arg {
offset: i64,
/// The size in bytes of the argument.
size: ByteSize,
/// An optional data type indicator.
data_type: Option<Datatype>,
},
}
......@@ -497,7 +504,7 @@ impl ExternSymbol {
pub fn get_unique_return_register(&self) -> Result<&Variable, Error> {
if self.return_values.len() == 1 {
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")),
}
} else {
......@@ -814,7 +821,10 @@ mod tests {
impl 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 {
}
for arg in symbol.arguments.iter() {
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() {
if expr.mnemonic == ExpressionType::LOAD {
IrArg::Stack {
......@@ -539,6 +542,7 @@ impl ExternSymbol {
)
.unwrap(),
size: expr.input0.unwrap().size,
data_type: None,
}
} else {
panic!()
......
......@@ -2,7 +2,7 @@
use std::collections::HashMap;
use crate::prelude::*;
use crate::{intermediate_representation::Datatype, prelude::*};
use regex::Regex;
......@@ -22,7 +22,7 @@ pub fn get_return_registers_from_symbol(symbol: &ExternSymbol) -> Vec<String> {
.return_values
.iter()
.filter_map(|ret| match ret {
Arg::Register(var) => Some(var.name.clone()),
Arg::Register { var, .. } => Some(var.name.clone()),
_ => None,
})
.collect::<Vec<String>>()
......@@ -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.
pub fn get_variable_number_parameters(
pub fn get_variable_parameters(
project: &Project,
pi_state: &PointerInferenceState,
extern_symbol: &ExternSymbol,
......@@ -209,16 +209,20 @@ pub fn create_string_stack_arg(size: ByteSize, stack_offset: i64) -> Arg {
Arg::Stack {
offset: stack_offset,
size,
data_type: Some(Datatype::Pointer),
}
}
/// Creates a string register parameter given a register name.
pub fn create_string_register_arg(size: ByteSize, register_name: String) -> Arg {
Arg::Register(Variable {
Arg::Register {
var: Variable {
name: register_name,
size,
is_temp: false,
})
},
data_type: Some(Datatype::Pointer),
}
}
/// Checks whether the format specifier is of type int.
......
......@@ -36,7 +36,7 @@ fn test_get_variable_number_parameters() {
let mut output: Vec<Arg> = Vec::new();
assert_eq!(
output,
get_variable_number_parameters(
get_variable_parameters(
&project,
&pi_state,
&sprintf_symbol,
......@@ -49,6 +49,7 @@ fn test_get_variable_number_parameters() {
output.push(Arg::Stack {
offset: 0,
size: ByteSize::new(8),
data_type: Some(Datatype::Pointer),
});
let global_address = Bitvector::from_str_radix(16, "500c").unwrap();
......@@ -59,7 +60,7 @@ fn test_get_variable_number_parameters() {
assert_eq!(
output,
get_variable_number_parameters(
get_variable_parameters(
&project,
&pi_state,
&sprintf_symbol,
......@@ -187,7 +188,10 @@ fn test_calculate_parameter_locations() {
parameters.push(("f".to_string(), ByteSize::new(8)));
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.
assert_eq!(
......@@ -199,6 +203,7 @@ fn test_calculate_parameter_locations() {
expected_args.push(Arg::Stack {
offset: 0,
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.
......@@ -214,6 +219,7 @@ fn test_create_string_stack_arg() {
Arg::Stack {
size: ByteSize::new(8),
offset: 8,
data_type: Some(Datatype::Pointer),
},
create_string_stack_arg(ByteSize::new(8), 8),
)
......@@ -222,7 +228,10 @@ fn test_create_string_stack_arg() {
#[test]
fn test_create_string_register_arg() {
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()),
);
}
......
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