Commit 954f1447 by Enkelmann Committed by Enkelmann

Use registers parsed from Ghidra on the Rust side.

parent 45ececd2
...@@ -4,7 +4,6 @@ GHIDRA_PATH = ...@@ -4,7 +4,6 @@ GHIDRA_PATH =
all: all:
cargo build --release cargo build --release
mkdir -p ${HOME}/.config/cwe_checker 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 cp src/config.json ${HOME}/.config/cwe_checker/config.json
ifdef GHIDRA_PATH ifdef GHIDRA_PATH
cargo install --path caller --locked cargo install --path caller --locked
......
...@@ -291,12 +291,13 @@ impl<'a> Context<'a> { ...@@ -291,12 +291,13 @@ impl<'a> Context<'a> {
new_state.clear_stack_parameter(extern_symbol, &self.project.stack_pointer_register), new_state.clear_stack_parameter(extern_symbol, &self.project.stack_pointer_register),
Some(&call.tid), Some(&call.tid),
); );
let calling_conv = extern_symbol.get_calling_convention(&self.project);
let mut possible_referenced_ids = BTreeSet::new(); let mut possible_referenced_ids = BTreeSet::new();
if extern_symbol.parameters.is_empty() && extern_symbol.return_values.is_empty() { 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. // 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. // This approximation is wrong if the function is known but has neither parameters nor return values.
// We cannot distinguish these two cases yet. // 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) { if let Some(register_value) = state.get_register_by_name(parameter_register_name) {
possible_referenced_ids.append(&mut register_value.referenced_ids()); possible_referenced_ids.append(&mut register_value.referenced_ids());
} }
......
...@@ -12,7 +12,7 @@ fn new_id(time: &str, reg_name: &str) -> AbstractIdentifier { ...@@ -12,7 +12,7 @@ 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("RAX")); let arg = Arg::Register(register("RDX"));
ExternSymbol { ExternSymbol {
tid: Tid::new("extern_".to_string() + name), tid: Tid::new("extern_".to_string() + name),
name: name.into(), name: name.into(),
...@@ -80,13 +80,18 @@ fn mock_project() -> (Project, Config) { ...@@ -80,13 +80,18 @@ fn mock_project() -> (Project, Config) {
tid: Tid::new("program"), tid: Tid::new("program"),
term: 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 { Project {
program: program_term, program: program_term,
cpu_architecture: "x86_64".to_string(), cpu_architecture: "x86_64".to_string(),
stack_pointer_register: register("RSP"), stack_pointer_register: register("RSP"),
callee_saved_registers: vec!["callee_saved_reg".to_string()], calling_conventions: vec![cconv],
parameter_registers: vec!["RAX".to_string()],
}, },
Config { Config {
allocation_symbols: vec!["malloc".into()], allocation_symbols: vec!["malloc".into()],
...@@ -186,9 +191,9 @@ fn context_problem_implementation() { ...@@ -186,9 +191,9 @@ fn context_problem_implementation() {
let malloc = call_term("extern_malloc"); let malloc = call_term("extern_malloc");
let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap(); let mut state_after_malloc = context.update_call_stub(&state, &malloc).unwrap();
assert_eq!( assert_eq!(
state_after_malloc.get_register(&register("RAX")).unwrap(), state_after_malloc.get_register(&register("RDX")).unwrap(),
Data::Pointer(PointerDomain::new( Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RAX"), new_id("call_extern_malloc", "RDX"),
bv(0) bv(0)
)) ))
); );
...@@ -214,7 +219,7 @@ fn context_problem_implementation() { ...@@ -214,7 +219,7 @@ fn context_problem_implementation() {
state_after_malloc.set_register( state_after_malloc.set_register(
&register("callee_saved_reg"), &register("callee_saved_reg"),
Data::Pointer(PointerDomain::new( Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RAX"), new_id("call_extern_malloc", "RDX"),
bv(0), bv(0),
)), )),
); );
...@@ -223,7 +228,7 @@ fn context_problem_implementation() { ...@@ -223,7 +228,7 @@ fn context_problem_implementation() {
.update_call_stub(&state_after_malloc, &free) .update_call_stub(&state_after_malloc, &free)
.unwrap(); .unwrap();
assert!(state_after_free assert!(state_after_free
.get_register(&register("RAX")) .get_register(&register("RDX"))
.unwrap() .unwrap()
.is_top()); .is_top());
assert_eq!(state_after_free.memory.get_num_objects(), 2); assert_eq!(state_after_free.memory.get_num_objects(), 2);
...@@ -232,7 +237,7 @@ fn context_problem_implementation() { ...@@ -232,7 +237,7 @@ fn context_problem_implementation() {
.get_register(&register("callee_saved_reg")) .get_register(&register("callee_saved_reg"))
.unwrap(), .unwrap(),
Data::Pointer(PointerDomain::new( Data::Pointer(PointerDomain::new(
new_id("call_extern_malloc", "RAX"), new_id("call_extern_malloc", "RDX"),
bv(0) bv(0)
)) ))
); );
...@@ -304,7 +309,7 @@ fn update_return() { ...@@ -304,7 +309,7 @@ fn update_return() {
.ids_known_to_caller .ids_known_to_caller
.insert(other_callsite_id.clone()); .insert(other_callsite_id.clone());
state_before_return.set_register( state_before_return.set_register(
&register("RAX"), &register("RDX"),
Data::Pointer(PointerDomain::new( Data::Pointer(PointerDomain::new(
new_id("call_callee_other", "RSP"), new_id("call_callee_other", "RSP"),
bv(-32), bv(-32),
......
...@@ -209,26 +209,27 @@ impl<'a> crate::analysis::interprocedural_fixpoint::Context<'a> for Context<'a> ...@@ -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."), Jmp::CallInd { .. } => panic!("Indirect calls to extern symbols not yet supported."),
_ => panic!("Malformed control flow graph encountered."), _ => 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) { 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 // Check parameter for possible use-after-frees
self.check_parameter_register_for_dangling_pointer(state, call, extern_symbol); self.check_parameter_register_for_dangling_pointer(state, call, extern_symbol);
......
...@@ -203,6 +203,19 @@ impl ExternSymbol { ...@@ -203,6 +203,19 @@ impl ExternSymbol {
Err(anyhow!("Wrong number of parameter values")) 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. /// The `Program` structure represents a disassembled binary.
...@@ -230,6 +243,21 @@ impl Program { ...@@ -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. /// The `Project` struct is the main data structure representing a binary.
/// ///
/// It contains information about the disassembled binary /// It contains information about the disassembled binary
...@@ -242,14 +270,8 @@ pub struct Project { ...@@ -242,14 +270,8 @@ pub struct Project {
pub cpu_architecture: String, pub cpu_architecture: String,
/// The stack pointer register for the given CPU architecture. /// The stack pointer register for the given CPU architecture.
pub stack_pointer_register: Variable, pub stack_pointer_register: Variable,
/// The names of callee-saved registers for the standard calling convention /// The known calling conventions that may be used for calls to extern functions.
/// for the given CPU architecture. pub calling_conventions: Vec<CallingConvention>,
/// 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>,
} }
impl Project { impl Project {
......
...@@ -2,6 +2,7 @@ use super::{Expression, ExpressionType, Variable}; ...@@ -2,6 +2,7 @@ use super::{Expression, ExpressionType, Variable};
use crate::intermediate_representation::Arg as IrArg; use crate::intermediate_representation::Arg as IrArg;
use crate::intermediate_representation::Blk as IrBlk; use crate::intermediate_representation::Blk as IrBlk;
use crate::intermediate_representation::ByteSize; use crate::intermediate_representation::ByteSize;
use crate::intermediate_representation::CallingConvention as IrCallingConvention;
use crate::intermediate_representation::Def as IrDef; use crate::intermediate_representation::Def as IrDef;
use crate::intermediate_representation::Expression as IrExpression; use crate::intermediate_representation::Expression as IrExpression;
use crate::intermediate_representation::ExternSymbol as IrExternSymbol; use crate::intermediate_representation::ExternSymbol as IrExternSymbol;
...@@ -387,10 +388,32 @@ impl From<Program> for IrProgram { ...@@ -387,10 +388,32 @@ impl From<Program> for IrProgram {
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)] #[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 struct Project {
pub program: Term<Program>, pub program: Term<Program>,
pub cpu_architecture: String, pub cpu_architecture: String,
pub stack_pointer_register: Variable, pub stack_pointer_register: Variable,
pub register_calling_convention: Vec<CallingConvention>,
} }
impl From<Project> for IrProject { impl From<Project> for IrProject {
...@@ -400,15 +423,15 @@ impl From<Project> for IrProject { ...@@ -400,15 +423,15 @@ impl From<Project> for IrProject {
tid: project.program.tid, tid: project.program.tid,
term: project.program.term.into(), term: project.program.term.into(),
}; };
let (params, callee_saved) = crate::utils::get_generic_parameter_and_callee_saved_register(
&project.cpu_architecture,
);
IrProject { IrProject {
program, program,
cpu_architecture: project.cpu_architecture, cpu_architecture: project.cpu_architecture,
stack_pointer_register: project.stack_pointer_register.into(), stack_pointer_register: project.stack_pointer_register.into(),
callee_saved_registers: callee_saved, calling_conventions: project
parameter_registers: params, .register_calling_convention
.into_iter()
.map(|cconv| cconv.into())
.collect(),
} }
} }
} }
...@@ -691,7 +714,16 @@ mod tests { ...@@ -691,7 +714,16 @@ mod tests {
"size": 8, "size": 8,
"is_virtual": false "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::bil::*;
use crate::intermediate_representation::Arg as IrArg; use crate::intermediate_representation::Arg as IrArg;
use crate::intermediate_representation::Blk as IrBlk; 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::Def as IrDef;
use crate::intermediate_representation::Expression as IrExpression; use crate::intermediate_representation::Expression as IrExpression;
use crate::intermediate_representation::Jmp as IrJmp; use crate::intermediate_representation::Jmp as IrJmp;
...@@ -308,6 +309,7 @@ pub struct Project { ...@@ -308,6 +309,7 @@ pub struct Project {
pub stack_pointer_register: Variable, pub stack_pointer_register: Variable,
pub callee_saved_registers: Vec<String>, pub callee_saved_registers: Vec<String>,
pub parameter_registers: Vec<String>, pub parameter_registers: Vec<String>,
pub return_registers: Vec<String>,
} }
impl Project { impl Project {
...@@ -354,12 +356,17 @@ impl From<Project> for IrProject { ...@@ -354,12 +356,17 @@ impl From<Project> for IrProject {
tid: project.program.tid, tid: project.program.tid,
term: project.program.term.into(), 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 { IrProject {
program, program,
cpu_architecture: project.cpu_architecture, cpu_architecture: project.cpu_architecture,
stack_pointer_register: project.stack_pointer_register.into(), stack_pointer_register: project.stack_pointer_register.into(),
callee_saved_registers: project.callee_saved_registers, calling_conventions: vec![default_cconv],
parameter_registers: project.parameter_registers,
} }
} }
} }
......
pub mod log; 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. /// Get the contents of a configuration file.
pub fn read_config_file(filename: &str) -> serde_json::Value { pub fn read_config_file(filename: &str) -> serde_json::Value {
let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker") let project_dirs = directories::ProjectDirs::from("", "", "cwe_checker")
......
...@@ -190,7 +190,6 @@ public class ParseCspecContent { ...@@ -190,7 +190,6 @@ public class ParseCspecContent {
XmlElement languageEnter = parser.peek(); XmlElement languageEnter = parser.peek();
if(languageEnter.getAttribute("id").equals(languageId.getIdAsString())) { if(languageEnter.getAttribute("id").equals(languageId.getIdAsString())) {
cspecName = getCompilerName(parser); cspecName = getCompilerName(parser);
break;
} else { } else {
discardSubTree(parser); discardSubTree(parser);
} }
......
...@@ -308,5 +308,6 @@ let of_project (project: Project.t) (extern_symbols: extern_symbol List.t) (entr ...@@ -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))); ("cpu_architecture", build_string (Arch.to_string (Project.arch project)));
("stack_pointer_register", of_var (Symbol_utils.stack_register 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) )); ("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