Unverified Commit 2ba249af by Enkelmann Committed by GitHub

Bugfixes (#165)

parent edd68498
...@@ -113,9 +113,9 @@ fn run_with_ghidra(args: CmdlineArgs) { ...@@ -113,9 +113,9 @@ fn run_with_ghidra(args: CmdlineArgs) {
binary_file_path.display() binary_file_path.display()
) )
}); });
let mut project = get_project_from_ghidra(&binary_file_path, &binary[..], args.quiet); let (mut project, mut all_logs) = get_project_from_ghidra(&binary_file_path, &binary[..]);
// Normalize the project and gather log messages generated from it. // Normalize the project and gather log messages generated from it.
let mut all_logs = project.normalize(); all_logs.append(&mut project.normalize());
// Generate the representation of the runtime memory image of the binary // Generate the representation of the runtime memory image of the binary
let mut runtime_memory_image = RuntimeMemoryImage::new(&binary).unwrap_or_else(|err| { let mut runtime_memory_image = RuntimeMemoryImage::new(&binary).unwrap_or_else(|err| {
...@@ -206,7 +206,7 @@ fn filter_modules_for_partial_run( ...@@ -206,7 +206,7 @@ fn filter_modules_for_partial_run(
} }
/// Execute the `p_code_extractor` plugin in ghidra and parse its output into the `Project` data structure. /// Execute the `p_code_extractor` plugin in ghidra and parse its output into the `Project` data structure.
fn get_project_from_ghidra(file_path: &Path, binary: &[u8], quiet_flag: bool) -> Project { fn get_project_from_ghidra(file_path: &Path, binary: &[u8]) -> (Project, Vec<LogMessage>) {
let ghidra_path: std::path::PathBuf = let ghidra_path: std::path::PathBuf =
serde_json::from_value(read_config_file("ghidra.json")["ghidra_path"].clone()) serde_json::from_value(read_config_file("ghidra.json")["ghidra_path"].clone())
.expect("Path to Ghidra not configured."); .expect("Path to Ghidra not configured.");
...@@ -298,14 +298,11 @@ fn get_project_from_ghidra(file_path: &Path, binary: &[u8], quiet_flag: bool) -> ...@@ -298,14 +298,11 @@ fn get_project_from_ghidra(file_path: &Path, binary: &[u8], quiet_flag: bool) ->
let mut project_pcode: cwe_checker_lib::pcode::Project = let mut project_pcode: cwe_checker_lib::pcode::Project =
serde_json::from_reader(std::io::BufReader::new(file)).unwrap(); serde_json::from_reader(std::io::BufReader::new(file)).unwrap();
project_pcode.normalize(); let mut log_messages = project_pcode.normalize();
let project: Project = match cwe_checker_lib::utils::get_binary_base_address(binary) { let project: Project = match cwe_checker_lib::utils::get_binary_base_address(binary) {
Ok(binary_base_address) => project_pcode.into_ir_project(binary_base_address), Ok(binary_base_address) => project_pcode.into_ir_project(binary_base_address),
Err(_err) => { Err(_err) => {
if !quiet_flag { log_messages.push(LogMessage::new_info("Could not determine binary base address. Using base address of Ghidra output as fallback."));
let log = LogMessage::new_info("Could not determine binary base address. Using base address of Ghidra output as fallback.");
println!("{}", log);
}
let mut project = project_pcode.into_ir_project(0); let mut project = project_pcode.into_ir_project(0);
// Setting the address_base_offset to zero is a hack, which worked for the tested PE files. // Setting the address_base_offset to zero is a hack, which worked for the tested PE files.
// But this hack will probably not work in general! // But this hack will probably not work in general!
...@@ -320,5 +317,5 @@ fn get_project_from_ghidra(file_path: &Path, binary: &[u8], quiet_flag: bool) -> ...@@ -320,5 +317,5 @@ fn get_project_from_ghidra(file_path: &Path, binary: &[u8], quiet_flag: bool) ->
std::fs::remove_file(fifo_path).unwrap(); std::fs::remove_file(fifo_path).unwrap();
project (project, log_messages)
} }
...@@ -80,11 +80,14 @@ impl RegisterDomain for BitvectorDomain { ...@@ -80,11 +80,14 @@ impl RegisterDomain for BitvectorDomain {
if let BitvectorDomain::Value(bitvec) = self { if let BitvectorDomain::Value(bitvec) = self {
match bitvec.un_op(op) { match bitvec.un_op(op) {
Ok(val) => BitvectorDomain::Value(val), Ok(val) => BitvectorDomain::Value(val),
Err(_) => BitvectorDomain::new_top(self.bytesize()), Err(_) => match op {
BoolNegate | FloatNaN => BitvectorDomain::new_top(ByteSize::new(1)),
_ => BitvectorDomain::new_top(self.bytesize()),
},
} }
} else { } else {
match op { match op {
BoolNegate => BitvectorDomain::new_top(ByteSize::new(1)), BoolNegate | FloatNaN => BitvectorDomain::new_top(ByteSize::new(1)),
_ => BitvectorDomain::new_top(self.bytesize()), _ => BitvectorDomain::new_top(self.bytesize()),
} }
} }
...@@ -280,4 +283,12 @@ mod tests { ...@@ -280,4 +283,12 @@ mod tests {
BitvectorDomain::Value(Bitvector::from_i64(-1)) BitvectorDomain::Value(Bitvector::from_i64(-1))
); );
} }
#[test]
fn float_nan_bytesize() {
let top_value = BitvectorDomain::new_top(ByteSize::new(8));
let result = top_value.un_op(UnOpType::FloatNaN);
assert!(result.is_top());
assert_eq!(result.bytesize(), ByteSize::new(1));
}
} }
...@@ -201,7 +201,10 @@ impl<T: RegisterDomain> RegisterDomain for DataDomain<T> { ...@@ -201,7 +201,10 @@ impl<T: RegisterDomain> RegisterDomain for DataDomain<T> {
if let Self::Value(value) = self { if let Self::Value(value) = self {
Self::Value(value.un_op(op)) Self::Value(value.un_op(op))
} else { } else {
Self::new_top(self.bytesize()) match op {
UnOpType::BoolNegate | UnOpType::FloatNaN => Self::new_top(ByteSize::new(1)),
_ => Self::new_top(self.bytesize()),
}
} }
} }
...@@ -423,4 +426,12 @@ mod tests { ...@@ -423,4 +426,12 @@ mod tests {
data.remove_ids(&ids_to_remove); data.remove_ids(&ids_to_remove);
assert_eq!(data, bv(42).into()); assert_eq!(data, bv(42).into());
} }
#[test]
fn float_nan_bytesize() {
let top_value: DataDomain<BitvectorDomain> = DataDomain::new_top(ByteSize::new(8));
let result = top_value.un_op(UnOpType::FloatNaN);
assert!(result.is_top());
assert_eq!(result.bytesize(), ByteSize::new(1));
}
} }
...@@ -440,9 +440,10 @@ impl RegisterDomain for IntervalDomain { ...@@ -440,9 +440,10 @@ impl RegisterDomain for IntervalDomain {
IntervalDomain::new_top(self.bytesize()) IntervalDomain::new_top(self.bytesize())
} }
} }
FloatAbs | FloatCeil | FloatFloor | FloatNaN | FloatNegate | FloatRound | FloatSqrt => { FloatAbs | FloatCeil | FloatFloor | FloatNegate | FloatRound | FloatSqrt => {
IntervalDomain::new_top(self.bytesize()) IntervalDomain::new_top(self.bytesize())
} }
FloatNaN => IntervalDomain::new_top(ByteSize::new(1)),
} }
} }
......
...@@ -503,3 +503,11 @@ fn intersection() { ...@@ -503,3 +503,11 @@ fn intersection() {
); );
assert!(interval1.intersect(&IntervalDomain::mock(50, 55)).is_err()); assert!(interval1.intersect(&IntervalDomain::mock(50, 55)).is_err());
} }
#[test]
fn float_nan_bytesize() {
let top_value = IntervalDomain::new_top(ByteSize::new(8));
let result = top_value.un_op(UnOpType::FloatNaN);
assert!(result.is_top());
assert_eq!(result.bytesize(), ByteSize::new(1));
}
...@@ -13,6 +13,7 @@ use crate::intermediate_representation::Program as IrProgram; ...@@ -13,6 +13,7 @@ use crate::intermediate_representation::Program as IrProgram;
use crate::intermediate_representation::Project as IrProject; use crate::intermediate_representation::Project as IrProject;
use crate::intermediate_representation::Sub as IrSub; use crate::intermediate_representation::Sub as IrSub;
use crate::prelude::*; use crate::prelude::*;
use crate::utils::log::LogMessage;
// TODO: Handle the case where an indirect tail call is represented by CALLIND plus RETURN // TODO: Handle the case where an indirect tail call is represented by CALLIND plus RETURN
...@@ -641,7 +642,18 @@ impl Project { ...@@ -641,7 +642,18 @@ impl Project {
/// ///
/// Ghidra generates implicit loads for memory accesses, whose address is a constant. /// Ghidra generates implicit loads for memory accesses, whose address is a constant.
/// The pass converts them to explicit `LOAD` instructions. /// The pass converts them to explicit `LOAD` instructions.
pub fn normalize(&mut self) { ///
/// ### Remove basic blocks of functions without correct starting block
///
/// Sometimes Ghidra generates a (correct) function start inside another function.
/// But if the function start is not also the start of a basic block,
/// we cannot handle it correctly (yet) as this would need splitting of basic blocks.
/// So instead we generate a log message and handle the function as a function without code,
/// i.e. a dead end in the control flow graph.
#[must_use]
pub fn normalize(&mut self) -> Vec<LogMessage> {
let mut log_messages = Vec::new();
// Insert explicit `LOAD` instructions for implicit memory loads in P-Code. // Insert explicit `LOAD` instructions for implicit memory loads in P-Code.
let generic_pointer_size = self.stack_pointer_register.size; let generic_pointer_size = self.stack_pointer_register.size;
for sub in self.program.term.subs.iter_mut() { for sub in self.program.term.subs.iter_mut() {
...@@ -654,6 +666,27 @@ impl Project { ...@@ -654,6 +666,27 @@ impl Project {
} }
} }
} }
// remove all blocks from functions that have no correct starting block and generate a log-message.
for sub in self.program.term.subs.iter_mut() {
if !sub.term.blocks.is_empty()
&& sub.tid.address != sub.term.blocks[0].tid.address
&& sub
.term
.blocks
.iter()
.find(|block| block.tid.address == sub.tid.address)
.is_none()
{
log_messages.push(LogMessage::new_error(format!(
"Starting block of function {} ({}) not found.",
sub.term.name, sub.tid
)));
sub.term.blocks = Vec::new();
}
}
log_messages
} }
} }
......
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