Commit 954f1447 by Enkelmann Committed by Enkelmann

Use registers parsed from Ghidra on the Rust side.

parent 45ececd2
......@@ -4,7 +4,6 @@ GHIDRA_PATH =
all:
cargo build --release
mkdir -p ${HOME}/.config/cwe_checker
cp src/utils/registers.json ${HOME}/.config/cwe_checker/registers.json
cp src/config.json ${HOME}/.config/cwe_checker/config.json
ifdef GHIDRA_PATH
cargo install --path caller --locked
......
......@@ -291,12 +291,13 @@ impl<'a> Context<'a> {
new_state.clear_stack_parameter(extern_symbol, &self.project.stack_pointer_register),
Some(&call.tid),
);
let calling_conv = extern_symbol.get_calling_convention(&self.project);
let mut possible_referenced_ids = BTreeSet::new();
if extern_symbol.parameters.is_empty() && extern_symbol.return_values.is_empty() {
// We assume here that we do not know the parameters and approximate them by all possible parameter registers.
// This approximation is wrong if the function is known but has neither parameters nor return values.
// We cannot distinguish these two cases yet.
for parameter_register_name in self.project.parameter_registers.iter() {
for parameter_register_name in calling_conv.parameter_register.iter() {
if let Some(register_value) = state.get_register_by_name(parameter_register_name) {
possible_referenced_ids.append(&mut register_value.referenced_ids());
}
......
......@@ -12,7 +12,7 @@ fn new_id(time: &str, reg_name: &str) -> AbstractIdentifier {
}
fn mock_extern_symbol(name: &str) -> ExternSymbol {
let arg = Arg::Register(register("RAX"));
let arg = Arg::Register(register("RDX"));
ExternSymbol {
tid: Tid::new("extern_".to_string() + name),
name: name.into(),
......@@ -80,13 +80,18 @@ fn mock_project() -> (Project, Config) {
tid: Tid::new("program"),
term: program,
};
let cconv = CallingConvention {
name: "default".to_string(),
parameter_register: vec!["RDX".to_string()],
return_register: vec!["RDX".to_string()],
callee_saved_register: vec!["callee_saved_reg".to_string()],
};
(
Project {
program: program_term,
cpu_architecture: "x86_64".to_string(),
stack_pointer_register: register("RSP"),
callee_saved_registers: vec!["callee_saved_reg".to_string()],
parameter_registers: vec!["RAX".to_string()],
calling_conventions: vec![cconv],
},
Config {
allocation_symbols: vec!["malloc".into()],
......@@ -186,9 +191,9 @@ fn context_problem_implementation() {
let malloc = call_term("extern_malloc");
let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap();
assert_eq!(
state_after_malloc.get_register(&register("RAX")).unwrap(),
state_after_malloc.get_register(&register("RDX")).unwrap(),
Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RAX"),
new_id("call_extern_malloc", "RDX"),
bv(0)
))
);
......@@ -214,7 +219,7 @@ fn context_problem_implementation() {
state_after_malloc.set_register(
&register("callee_saved_reg"),
Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RAX"),
new_id("call_extern_malloc", "RDX"),
bv(0),
)),
);
......@@ -223,7 +228,7 @@ fn context_problem_implementation() {
.update_call_stub(&state_after_malloc, &free)
.unwrap();
assert!(state_after_free
.get_register(&register("RAX"))
.get_register(&register("RDX"))
.unwrap()
.is_top());
assert_eq!(state_after_free.memory.get_num_objects(), 2);
......@@ -232,7 +237,7 @@ fn context_problem_implementation() {
.get_register(&register("callee_saved_reg"))
.unwrap(),
Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RAX"),
new_id("call_extern_malloc", "RDX"),
bv(0)
))
);
......@@ -304,7 +309,7 @@ fn update_return() {
.ids_known_to_caller
.insert(other_callsite_id.clone());
state_before_return.set_register(
&register("RAX"),
&register("RDX"),
Data::Pointer(PointerDomain::new(
new_id("call_callee_other", "RSP"),
bv(-32),
......
......@@ -209,26 +209,27 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a>
Jmp::CallInd { .. } => panic!("Indirect calls to extern symbols not yet supported."),
_ => panic!("Malformed control flow graph encountered."),
};
// Clear non-callee-saved registers from the state.
new_state.clear_non_callee_saved_register(&self.project.callee_saved_registers[..]);
// On x86, remove the return address from the stack (other architectures pass the return address in a register, not on the stack).
// Note that in some calling conventions the callee also clears function parameters from the stack.
// We do not detect and handle these cases yet.
let stack_register = &self.project.stack_pointer_register;
let stack_pointer = state.get_register(stack_register).unwrap();
match self.project.cpu_architecture.as_str() {
"x86" | "x86_64" => {
let offset = Bitvector::from_u64(stack_register.size.into())
.into_truncate(apint::BitWidth::from(stack_register.size))
.unwrap();
new_state.set_register(
stack_register,
stack_pointer.bin_op(BinOpType::IntAdd, &offset.into()),
);
}
_ => new_state.set_register(stack_register, stack_pointer),
}
if let Some(extern_symbol) = self.extern_symbol_map.get(call_target) {
// Clear non-callee-saved registers from the state.
let cconv = extern_symbol.get_calling_convention(&self.project);
new_state.clear_non_callee_saved_register(&cconv.callee_saved_register[..]);
// On x86, remove the return address from the stack (other architectures pass the return address in a register, not on the stack).
// Note that in some calling conventions the callee also clears function parameters from the stack.
// We do not detect and handle these cases yet.
let stack_register = &self.project.stack_pointer_register;
let stack_pointer = state.get_register(stack_register).unwrap();
match self.project.cpu_architecture.as_str() {
"x86" | "x86_64" => {
let offset = Bitvector::from_u64(stack_register.size.into())
.into_truncate(apint::BitWidth::from(stack_register.size))
.unwrap();
new_state.set_register(
stack_register,
stack_pointer.bin_op(BinOpType::IntAdd, &offset.into()),
);
}
_ => new_state.set_register(stack_register, stack_pointer),
}
// Check parameter for possible use-after-frees
self.check_parameter_register_for_dangling_pointer(state, call, extern_symbol);
......
......@@ -203,6 +203,19 @@ impl ExternSymbol {
Err(anyhow!("Wrong number of parameter values"))
}
}
/// Get the calling convention corresponding to the extern symbol.
pub fn get_calling_convention<'a>(&self, project: &'a Project) -> &'a CallingConvention {
let cconv_name = match self.calling_convention {
Some(ref name) => name,
None => "default",
};
project
.calling_conventions
.iter()
.find(|cconv| cconv.name == cconv_name)
.unwrap()
}
}
/// The `Program` structure represents a disassembled binary.
......@@ -230,6 +243,21 @@ impl Program {
}
}
/// Calling convention related data
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct CallingConvention {
/// The name of the calling convention
#[serde(rename = "calling_convention")]
pub name: String,
/// A list of possible parameter register
pub parameter_register: Vec<String>,
/// A list of possible return register
pub return_register: Vec<String>,
/// A list of callee-saved register,
/// i.e. the values of these registers should be the same after the call as they were before the call.
pub callee_saved_register: Vec<String>,
}
/// The `Project` struct is the main data structure representing a binary.
///
/// It contains information about the disassembled binary
......@@ -242,14 +270,8 @@ pub struct Project {
pub cpu_architecture: String,
/// The stack pointer register for the given CPU architecture.
pub stack_pointer_register: Variable,
/// The names of callee-saved registers for the standard calling convention
/// for the given CPU architecture.
/// Note that this field may be removed in the future.
pub callee_saved_registers: Vec<String>,
/// The names of parameter registers for the standard calling convention
/// for the given CPU architecture.
/// Note that this field may be removed in the future.
pub parameter_registers: Vec<String>,
/// The known calling conventions that may be used for calls to extern functions.
pub calling_conventions: Vec<CallingConvention>,
}
impl Project {
......
......@@ -2,6 +2,7 @@ use super::{Expression, ExpressionType, Variable};
use crate::intermediate_representation::Arg as IrArg;
use crate::intermediate_representation::Blk as IrBlk;
use crate::intermediate_representation::ByteSize;
use crate::intermediate_representation::CallingConvention as IrCallingConvention;
use crate::intermediate_representation::Def as IrDef;
use crate::intermediate_representation::Expression as IrExpression;
use crate::intermediate_representation::ExternSymbol as IrExternSymbol;
......@@ -387,10 +388,32 @@ impl From<Program> for IrProgram {
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct CallingConvention {
#[serde(rename = "calling_convention")]
pub name: String,
parameter_register: Vec<String>,
return_register: Vec<String>,
unaffected_register: Vec<String>,
killed_by_call_register: Vec<String>,
}
impl From<CallingConvention> for IrCallingConvention {
fn from(cconv: CallingConvention) -> IrCallingConvention {
IrCallingConvention {
name: cconv.name,
parameter_register: cconv.parameter_register,
return_register: cconv.return_register,
callee_saved_register: cconv.unaffected_register,
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Project {
pub program: Term<Program>,
pub cpu_architecture: String,
pub stack_pointer_register: Variable,
pub register_calling_convention: Vec<CallingConvention>,
}
impl From<Project> for IrProject {
......@@ -400,15 +423,15 @@ impl From<Project> for IrProject {
tid: project.program.tid,
term: project.program.term.into(),
};
let (params, callee_saved) = crate::utils::get_generic_parameter_and_callee_saved_register(
&project.cpu_architecture,
);
IrProject {
program,
cpu_architecture: project.cpu_architecture,
stack_pointer_register: project.stack_pointer_register.into(),
callee_saved_registers: callee_saved,
parameter_registers: params,
calling_conventions: project
.register_calling_convention
.into_iter()
.map(|cconv| cconv.into())
.collect(),
}
}
}
......@@ -691,7 +714,16 @@ mod tests {
"size": 8,
"is_virtual": false
},
"cpu_architecture": "x86_64"
"cpu_architecture": "x86_64",
"register_calling_convention": [
{
"calling_convention": "default",
"parameter_register": [],
"return_register": [],
"unaffected_register": [],
"killed_by_call_register": []
}
]
}
"#,
)
......
use crate::bil::*;
use crate::intermediate_representation::Arg as IrArg;
use crate::intermediate_representation::Blk as IrBlk;
use crate::intermediate_representation::CallingConvention as IrCallingConvention;
use crate::intermediate_representation::Def as IrDef;
use crate::intermediate_representation::Expression as IrExpression;
use crate::intermediate_representation::Jmp as IrJmp;
......@@ -308,6 +309,7 @@ pub struct Project {
pub stack_pointer_register: Variable,
pub callee_saved_registers: Vec<String>,
pub parameter_registers: Vec<String>,
pub return_registers: Vec<String>,
}
impl Project {
......@@ -354,12 +356,17 @@ impl From<Project> for IrProject {
tid: project.program.tid,
term: project.program.term.into(),
};
let default_cconv = IrCallingConvention {
name: "default".to_string(),
parameter_register: project.parameter_registers,
return_register: project.return_registers,
callee_saved_register: project.callee_saved_registers,
};
IrProject {
program,
cpu_architecture: project.cpu_architecture,
stack_pointer_register: project.stack_pointer_register.into(),
callee_saved_registers: project.callee_saved_registers,
parameter_registers: project.parameter_registers,
calling_conventions: vec![default_cconv],
}
}
}
......
pub mod log;
/// Get the names of parameter registers and callee saved registers
/// of the standard calling convention for the given architecture.
///
/// The registers are read from a configuration file.
pub fn get_generic_parameter_and_callee_saved_register(
cpu_architecture: &str,
) -> (Vec<String>, Vec<String>) {
let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker")
.expect("Could not discern location of configuration files.");
let config_dir = project_dirs.config_dir();
let register_config_path = config_dir.join("registers.json");
let file = std::fs::read_to_string(register_config_path)
.expect("Could not read register configuration file");
let mut registers_json: serde_json::Value = serde_json::from_str(&file).unwrap();
match cpu_architecture {
"x86" | "x86_32" => registers_json = registers_json["elf"]["x86"]["cdecl"].clone(),
"ARM_32" => registers_json = registers_json["elf"]["armv7"].clone(),
_ => registers_json = registers_json["elf"][cpu_architecture].clone(),
}
let mut callee_saved: Vec<String> =
serde_json::from_value(registers_json["callee_saved"].clone()).unwrap();
let mut callee_saved_float: Vec<String> =
serde_json::from_value(registers_json["float_callee_saved"].clone()).unwrap();
callee_saved.append(&mut callee_saved_float);
let mut params: Vec<String> = serde_json::from_value(registers_json["params"].clone()).unwrap();
let mut params_float: Vec<String> =
serde_json::from_value(registers_json["float_params"].clone()).unwrap();
params.append(&mut params_float);
(params, callee_saved)
}
/// Get the contents of a configuration file.
pub fn read_config_file(filename: &str) -> serde_json::Value {
let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker")
......
......@@ -190,7 +190,6 @@ public class ParseCspecContent {
XmlElement languageEnter = parser.peek();
if(languageEnter.getAttribute("id").equals(languageId.getIdAsString())) {
cspecName = getCompilerName(parser);
break;
} else {
discardSubTree(parser);
}
......
......@@ -308,5 +308,6 @@ let of_project (project: Project.t) (extern_symbols: extern_symbol List.t) (entr
("cpu_architecture", build_string (Arch.to_string (Project.arch project)));
("stack_pointer_register", of_var (Symbol_utils.stack_register project));
("callee_saved_registers", build_array (List.map (Cconv.get_register_list project "callee_saved") ~f:(fun reg_name -> build_string reg_name) ));
("parameter_registers", build_array (List.map (Cconv.get_register_list project "params") ~f:(fun reg_name -> build_string reg_name) ))
("parameter_registers", build_array (List.map (Cconv.get_register_list project "params") ~f:(fun reg_name -> build_string reg_name) ));
("return_registers", build_array (List.map (Cconv.get_register_list project "return") ~f:(fun reg_name -> build_string reg_name) ))
]
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