Unverified Commit aeeea7f8 by Enkelmann Committed by GitHub

Remove BAP parsing code (#149)

parent 11ca1728
......@@ -48,50 +48,12 @@ struct CmdlineArgs {
/// The current behavior of this flag is unstable and subject to change.
#[structopt(long, hidden = true)]
debug: bool,
/// Use BAP as backend (instead of Ghidra). Requires BAP and the cwe_checker-BAP-plugin to be installed.
#[structopt(long, hidden = true)]
bap: bool,
}
fn main() {
let cmdline_args = CmdlineArgs::from_args();
if cmdline_args.bap {
// Use BAP as backend
if let Some(exit_code) = build_bap_command(&cmdline_args).status().unwrap().code() {
std::process::exit(exit_code);
}
} else {
// Use Ghidra as backend
run_with_ghidra(cmdline_args);
}
}
/// Build the BAP command corresponding to the given command line arguments.
fn build_bap_command(args: &CmdlineArgs) -> Command {
let mut command = Command::new("bap");
command.arg(args.binary.as_ref().unwrap());
command.arg("--pass=cwe-checker");
if let Some(ref string) = args.config {
command.arg("--cwe-checker-config=".to_string() + string);
}
if let Some(ref string) = args.out {
command.arg("--cwe-checker-out=".to_string() + string);
}
if let Some(ref string) = args.partial {
command.arg("--cwe-checker-partial=".to_string() + string);
}
if args.json {
command.arg("--cwe-checker-json");
}
if args.quiet {
command.arg("--cwe-checker-no-logging");
}
if args.module_versions {
command.arg("--cwe-checker-module-versions");
}
command
}
/// Check the existence of a file
......
......@@ -9,7 +9,6 @@ apint = "0.2"
serde = {version = "1.0", features = ["derive", "rc"]}
serde_json = "1.0"
serde_yaml = "0.8"
ocaml = "0.9.2"
petgraph = { version = "0.5", features = ["default", "serde-1"] }
fnv = "1.0" # a faster hash function for small keys like integers
anyhow = "1.0" # for easy error types
......
use super::{AbstractDomain, HasTop, RegisterDomain, SizedDomain};
use crate::bil::BitSize;
use crate::intermediate_representation::*;
use crate::prelude::*;
......@@ -64,17 +63,14 @@ impl RegisterDomain for BitvectorDomain {
match (self, rhs) {
(BitvectorDomain::Value(lhs_bitvec), BitvectorDomain::Value(rhs_bitvec)) => match op {
Piece => {
let new_bitwidth = BitSize::from(self.bytesize() + rhs.bytesize());
let new_bitwidth = (self.bytesize() + rhs.bytesize()).as_bit_length();
let upper_bits = lhs_bitvec
.clone()
.into_zero_extend(new_bitwidth as usize)
.into_zero_extend(new_bitwidth)
.unwrap()
.into_checked_shl(BitSize::from(rhs.bytesize()) as usize)
.unwrap();
let lower_bits = rhs_bitvec
.clone()
.into_zero_extend(new_bitwidth as usize)
.into_checked_shl(rhs.bytesize().as_bit_length())
.unwrap();
let lower_bits = rhs_bitvec.clone().into_zero_extend(new_bitwidth).unwrap();
BitvectorDomain::Value(upper_bits | &lower_bits)
}
IntAdd => BitvectorDomain::Value(lhs_bitvec + rhs_bitvec),
......@@ -259,9 +255,9 @@ impl RegisterDomain for BitvectorDomain {
BitvectorDomain::Value(
bitvec
.clone()
.into_checked_lshr(BitSize::from(low_byte) as usize)
.into_checked_lshr(low_byte.as_bit_length())
.unwrap()
.into_truncate(BitSize::from(size) as usize)
.into_truncate(size.as_bit_length())
.unwrap(),
)
} else {
......@@ -342,7 +338,7 @@ impl std::convert::TryFrom<&BitvectorDomain> for Bitvector {
impl std::fmt::Display for BitvectorDomain {
fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Top(bytesize) => write!(formatter, "Top:u{}", BitSize::from(*bytesize)),
Self::Top(bytesize) => write!(formatter, "Top:u{}", bytesize.as_bit_length()),
Self::Value(bitvector) => write!(
formatter,
"0x{:016x}:u{:?}",
......
use super::{AbstractDomain, HasTop, SizedDomain};
use crate::bil::Bitvector;
use crate::intermediate_representation::ByteSize;
use crate::prelude::*;
use apint::{Int, Width};
use derive_more::Deref;
use serde::{Deserialize, Serialize};
......@@ -238,7 +238,6 @@ impl<T: AbstractDomain + SizedDomain + HasTop + std::fmt::Debug> MemRegionData<T
mod tests {
use super::*;
use crate::abstract_domain::RegisterDomain;
use crate::bil::Bitvector;
use crate::intermediate_representation::*;
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash, PartialOrd, Ord)]
......
use crate::{bil::Bitvector, intermediate_representation::*};
use crate::intermediate_representation::*;
use super::{create_computation, mock_context, NodeValue};
......
use super::Data;
use crate::abstract_domain::*;
use crate::bil::Bitvector;
use crate::prelude::*;
use derive_more::Deref;
use serde::{Deserialize, Serialize};
......
use super::object::*;
use super::Data;
use crate::abstract_domain::*;
use crate::bil::Bitvector;
use crate::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
......
use super::BitSize;
use crate::intermediate_representation::Variable as IrVariable;
use crate::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
pub struct Variable {
pub name: String,
pub type_: Type,
pub is_temp: bool,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
pub enum Type {
Immediate(BitSize),
Memory {
addr_size: BitSize,
elem_size: BitSize,
},
Unknown,
}
impl Type {
pub fn bitsize(&self) -> Result<BitSize, Error> {
if let Type::Immediate(bitsize) = self {
Ok(*bitsize)
} else {
Err(anyhow!("Not a register type"))
}
}
}
impl Variable {
pub fn bitsize(&self) -> Result<BitSize, Error> {
self.type_.bitsize()
}
}
impl From<Variable> for IrVariable {
fn from(var: Variable) -> IrVariable {
let size = if let Type::Immediate(bitsize) = var.type_ {
bitsize.into()
} else {
panic!()
};
IrVariable {
name: var.name,
size,
is_temp: var.is_temp,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn type_deserialization() {
let typ = Type::Immediate(64);
let string = serde_json::to_string_pretty(&typ).expect("Serialization failed");
println!("{}", &string);
let _: Type = serde_json::from_str(&string).expect("Deserialization failed");
let typ = Type::Memory {
addr_size: 64,
elem_size: 8,
};
let string = serde_json::to_string_pretty(&typ).expect("Serialization failed");
println!("{}", &string);
let _: Type = serde_json::from_str(&string).expect("Deserialization failed");
let typ = Type::Unknown;
let string = serde_json::to_string_pretty(&typ).expect("Serialization failed");
println!("{}", &string);
let _: Type = serde_json::from_str(&string).expect("Deserialization failed");
}
#[test]
fn var_type_from_ocaml() {
let json_string = "{\"Memory\":{\"addr_size\":64,\"elem_size\":8}}";
let typ = Type::Memory {
addr_size: 64,
elem_size: 8,
};
assert_eq!(typ, serde_json::from_str(json_string).unwrap())
}
#[test]
fn var_from_ocaml() {
let json_string = "{\"is_temp\":false,\"name\":\"RAX\",\"type_\":{\"Memory\":{\"addr_size\":64,\"elem_size\":8}}}";
let var = Variable {
name: "RAX".to_string(),
type_: Type::Memory {
addr_size: 64,
elem_size: 8,
},
is_temp: false,
};
assert_eq!(var, serde_json::from_str(json_string).unwrap())
}
}
......@@ -4,7 +4,6 @@ use crate::analysis::backward_interprocedural_fixpoint::Context as BackwardConte
use crate::{
abstract_domain::{BitvectorDomain, DataDomain, PointerDomain, SizedDomain},
analysis::pointer_inference::{Data, State as PointerInferenceState},
bil::Bitvector,
intermediate_representation::{Expression, Variable},
};
......
use super::serde::JsonBuilder;
use super::OcamlSendable;
use crate::utils::log::CweWarning;
use crate::{analysis::pointer_inference::PointerInference, term::*};
use super::failwith_on_panic;
#[allow(unreachable_code)]
#[allow(unused_variables)]
fn run_pointer_inference(program_jsonbuilder_val: ocaml::Value) -> (Vec<CweWarning>, Vec<String>) {
let json_builder = unsafe { JsonBuilder::from_ocaml(&program_jsonbuilder_val) };
let program_json = serde_json::Value::from(json_builder);
let mut project: Project =
serde_json::from_value(program_json).expect("Project deserialization failed");
project.replace_let_bindings();
let mut project: crate::intermediate_representation::Project = project.into();
let all_logs = project.normalize();
let config: crate::analysis::pointer_inference::Config =
serde_json::from_value(crate::utils::read_config_file("config.json")["Memory"].clone())
.unwrap();
// let pi_analysis = crate::analysis::pointer_inference::run(&project, config, false);
let pi_analysis: PointerInference = panic!("Running the pointer inference analysis with the BAP backend is deprecated. Please use the Ghidra backend for this analysis instead.");
let (mut logs, cwes) = pi_analysis.collected_logs;
all_logs.append(&mut logs);
(
cwes,
all_logs
.into_iter()
.map(|log| format! {"{}", log})
.collect(),
)
}
caml!(rs_run_pointer_inference(program_jsonbuilder_val) {
failwith_on_panic( || {
let cwe_warnings_and_log = run_pointer_inference(program_jsonbuilder_val);
let cwe_warnings_and_log_json = serde_json::to_string(&cwe_warnings_and_log).unwrap();
let ocaml_string = ocaml::Str::from(&cwe_warnings_and_log_json as &str);
ocaml::Value::from(ocaml_string)
})
});
#[allow(unused_variables)]
fn run_pointer_inference_and_print_debug(program_jsonbuilder_val: ocaml::Value) {
let json_builder = unsafe { JsonBuilder::from_ocaml(&program_jsonbuilder_val) };
let program_json = serde_json::Value::from(json_builder);
let mut project: Project =
serde_json::from_value(program_json).expect("Project deserialization failed");
project.replace_let_bindings();
let mut project: crate::intermediate_representation::Project = project.into();
let _ = project.normalize();
let config: crate::analysis::pointer_inference::Config =
serde_json::from_value(crate::utils::read_config_file("config.json")["Memory"].clone())
.unwrap();
panic!("Running the pointer inference analysis with the BAP backend is deprecated. Please use the Ghidra backend for this analysis instead.");
// crate::analysis::pointer_inference::run(&project, config, true); // Note: This discard all CweWarnings and log messages.
}
caml!(rs_run_pointer_inference_and_print_debug(program_jsonbuilder_val) {
failwith_on_panic( || {
run_pointer_inference_and_print_debug(program_jsonbuilder_val);
ocaml::Value::unit()
})
});
/*!
# Foreign Function Interface
This module contains all functions that interact with Ocaml via the foreign function interface.
*/
use std::rc::Rc;
pub mod analysis;
pub mod serde;
/// Helper function for catching panics at the ffi-border.
/// If a panic occurs while executing F and that panic unwinds the stack,
/// the panic is caught and an Ocaml failwith exception is thrown instead.
///
/// Stack unwinding through a panic across a ffi-boundary is undefined behaviour.
/// As of Rust 1.41 catching panics at ffi-borders is still not the default behaviour,
/// since it would break backwards compatibility with some crates depending on this undefined behaviour.
/// Throwing an Ocaml failwith exception instead allows stack unwinding and better error messages.
/// Note that the Ocaml exception should *not* be caught,
/// since recovering from it may lead to undefined behavior on the Rust side.
fn failwith_on_panic<F, T>(closure: F) -> T
where
F: FnOnce() -> T,
{
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(closure)) {
Ok(value) => value,
Err(_) => {
// Throw an Ocaml failwith-exception.
// This may not be safe if the exception is caught and recovered from on the Ocaml side!
// We assume that these errors are only caught for error printing but not for recovering from it.
ocaml::runtime::failwith("Rust-Panic catched at FFI-boundary");
std::process::abort();
}
}
}
/// This is a convenience trait for objects that may be sent as opaque objects across the ffi-boundary to Ocaml.
/// For that they are wrapped as Rc<T>.
/// Note that this trait does not prevent memory leaks in itself!
/// Whenever such an object is created and sent across the ffi-boundary,
/// the finalizer must be attached to it on the Ocaml side!
trait OcamlSendable: std::marker::Sized {
/// Pack the object into an Ocaml value
fn into_ocaml(self) -> ocaml::Value {
let boxed_val = Rc::new(self);
ocaml::Value::nativeint(Rc::into_raw(boxed_val) as isize)
}
/// Unpack an object that is stored as a `Rc<T>` wrapped in an Ocaml value.
///
/// Note that the caller has to ensure that the wrapped object has the correct type.
unsafe fn from_ocaml(ocaml_val: &ocaml::Value) -> &Self {
let ptr: *const Self = ocaml_val.nativeint_val() as *const Self;
ptr.as_ref().unwrap()
}
/// Unpack a `Rc<T>` object wrapped in an Ocaml value and return a clone of it.
///
/// Note that the caller has to ensure that the wrapped object has the correct type.
unsafe fn from_ocaml_rc(ocaml_val: &ocaml::Value) -> Rc<Self> {
let ptr: *const Self = ocaml_val.nativeint_val() as *const Self;
let rc_box = Rc::from_raw(ptr);
let rc_clone = rc_box.clone(); // Increasing the reference count by 1
let _ = Rc::into_raw(rc_box); // Do not decrease the reference count when rc_box goes out of scope!
rc_clone
}
fn ocaml_finalize(ocaml_val: ocaml::Value) {
let ptr: *const Self = ocaml_val.nativeint_val() as *const Self;
let _ = unsafe { Rc::from_raw(ptr) };
}
}
/*!
# FFI-functions for generating serde_json objects
This module defines functions for generating opaque serde_json::Value objects in Ocaml
which can then be deserialized with Serde on the Rust side. Signatures of the provided functions:
```Ocaml
type serde_json = nativeint (* This stores pointers, so treat this as an opaque type! *)
external build_null: unit -> serde_json = "rs_build_serde_null"
external build_bool: bool -> serde_json = "rs_build_serde_bool"
external build_number: int -> serde_json = "rs_build_serde_number"
external build_string: string -> serde_json = "rs_build_serde_string"
external build_array: serde_json list -> serde_json = "rs_build_serde_array_from_list"
external build_object: (string * serde_json) list -> serde_json = "rs_build_serde_object"
external build_bitvector: string -> serde_json = "rs_build_serde_bitvector"
(* Convert a serde_json object to a json string (used for unit tests). *)
external to_string: serde_json -> string = "rs_convert_json_to_string"
```
*/
use super::OcamlSendable;
use ocaml::{FromValue, ToValue};
use std::rc::Rc;
use std::str::FromStr;
use super::failwith_on_panic;
/// A builder type for serde_json::Value objects.
///
/// Hiding the recursive nature of the data type behind reference counts prevents unneccessary
/// deep copies when creating json objects from Ocaml, which would lead to a runtime quadratic in the size of the json object.
/// However, when converting to serde_json::Value, one deep copy is still necessary.
#[derive(Clone, Debug)]
pub enum JsonBuilder {
Null,
Bool(bool),
Number(isize),
PositiveNumber(u64), // currently used only for deserialization of bitvector
String(String),
Array(Vec<Rc<JsonBuilder>>),
Object(Vec<(String, Rc<JsonBuilder>)>),
}
impl OcamlSendable for JsonBuilder {}
/// Creating a serde_json::Value performing deep copy.
impl From<&JsonBuilder> for serde_json::Value {
fn from(builder: &JsonBuilder) -> serde_json::Value {
match builder {
JsonBuilder::Null => serde_json::Value::Null,
JsonBuilder::Bool(val) => serde_json::Value::Bool(*val),
JsonBuilder::Number(val) => serde_json::Value::Number(serde_json::Number::from(*val)),
JsonBuilder::PositiveNumber(val) => {
serde_json::Value::Number(serde_json::Number::from(*val))
}
JsonBuilder::String(val) => serde_json::Value::String(val.to_string()),
JsonBuilder::Array(elem_vec) => elem_vec
.iter()
.map(|rc_elem| serde_json::Value::from(&**rc_elem))
.collect(),
JsonBuilder::Object(tuple_vec) => serde_json::Value::Object(
tuple_vec
.iter()
.map(|(string_ref, json_builder)| {
(
string_ref.to_string(),
serde_json::Value::from(&**json_builder),
)
})
.collect(),
),
}
}
}
caml!(rs_finalize_json_builder(builder_val) {
failwith_on_panic( || {
JsonBuilder::ocaml_finalize(builder_val);
ocaml::Value::unit()
})
});
/// Build JsonBuilder::Null as Ocaml value
fn build_serde_null() -> ocaml::Value {
JsonBuilder::Null.into_ocaml()
}
caml!(rs_build_serde_null(_unit) {
failwith_on_panic( || {
build_serde_null()
})
});
/// Build JsonBuilder::Bool as Ocaml value
fn build_serde_bool(bool_val: ocaml::Value) -> ocaml::Value {
let boolean: bool = bool::from_value(bool_val);
JsonBuilder::Bool(boolean).into_ocaml()
}
caml!(rs_build_serde_bool(bool_val) {
failwith_on_panic( || {
build_serde_bool(bool_val)
})
});
/// Build JsonBuilder::Number as Ocaml value
fn build_serde_number(num: ocaml::Value) -> ocaml::Value {
let num: isize = ocaml::Value::isize_val(&num);
JsonBuilder::Number(num).into_ocaml()
}
caml!(rs_build_serde_number(number) {
failwith_on_panic( || {
build_serde_number(number)
})
});
/// Build JsonBuilder::Object representing a bitvector from a string generated by `Bitvector.to_string` in Ocaml
fn build_serde_bitvector(bitvector_string_val: ocaml::Value) -> ocaml::Value {
let string = <&str>::from_value(bitvector_string_val);
let elements: Vec<&str> = string.split(':').collect();
let width = usize::from_str(&elements[1][0..(elements[1].len() - 1)])
.expect("Bitvector width parsing failed");
assert!(width > 0);
let mut num_list = Vec::new();
let mut number_slice: &str = elements[0];
if number_slice.starts_with("0x") {
number_slice = &number_slice[2..];
}
while !number_slice.is_empty() {
if number_slice.len() > 16 {
let digit = u64::from_str_radix(&number_slice[(number_slice.len() - 16)..], 16)
.expect("Bitvector value parsing failed");
num_list.push(Rc::new(JsonBuilder::PositiveNumber(digit)));
number_slice = &number_slice[..(number_slice.len() - 16)];
} else {
let digit =
u64::from_str_radix(&number_slice, 16).expect("Bitvector value parsing failed");
num_list.push(Rc::new(JsonBuilder::PositiveNumber(digit)));
number_slice = "";
};
}
while num_list.len() <= (width - 1) / 64 {
num_list.push(Rc::new(JsonBuilder::PositiveNumber(0)));
}
num_list.reverse(); // since the digits were parsed in reverse order
let mut width_list = Vec::new();
width_list.push(Rc::new(JsonBuilder::Number(width as isize)));
let result = JsonBuilder::Object(vec![
("digits".to_string(), Rc::new(JsonBuilder::Array(num_list))),
("width".to_string(), Rc::new(JsonBuilder::Array(width_list))),
]);
result.into_ocaml()
}
caml!(rs_build_serde_bitvector(bitvector_string) {
failwith_on_panic( || {
build_serde_bitvector(bitvector_string)
})
});
/// Build JsonBuilder::String as Ocaml value
fn build_serde_string(string_val: ocaml::Value) -> ocaml::Value {
let string = String::from_value(string_val);
JsonBuilder::String(string).into_ocaml()
}
caml!(rs_build_serde_string(string_val) {
failwith_on_panic( || {
build_serde_string(string_val)
})
});
/// Build JsonBuilder::Array as Ocaml value from an Ocaml list
fn build_serde_array_from_list(list_val: ocaml::Value) -> ocaml::Value {
let ocaml_list = ocaml::List::from(list_val);
let value_vec = ocaml_list.to_vec();
let vec = value_vec
.into_iter()
.map(|ocaml_val| unsafe { JsonBuilder::from_ocaml_rc(&ocaml_val) })
.collect();
JsonBuilder::Array(vec).into_ocaml()
}
caml!(rs_build_serde_array_from_list(list_val) {
failwith_on_panic( || {
build_serde_array_from_list(list_val)
})
});
/// Build JsonBuilder::Object as Ocaml value from an Ocaml list of tuples
fn build_serde_object(tuple_list_val: ocaml::Value) -> ocaml::Value {
let ocaml_list = ocaml::List::from(tuple_list_val);
let pairs_vec = ocaml_list.to_vec();
let pairs = pairs_vec
.into_iter()
.map(|ocaml_tuple| {
let tuple = ocaml::Tuple::from(ocaml_tuple);
let key_ocaml = tuple
.get(0)
.expect("Error: Ocaml tuple contains no element");
let key = String::from_value(key_ocaml);
let value_ocaml: ocaml::Value = tuple
.get(1)
.expect("Error: Ocaml tuple contains not enough elements");
let data = unsafe { JsonBuilder::from_ocaml_rc(&value_ocaml) };
(key, data)
})
.collect();
JsonBuilder::Object(pairs).into_ocaml()
}
caml!(rs_build_serde_object(tuple_list_val) {
failwith_on_panic( || {
build_serde_object(tuple_list_val)
})
});
/// Get the Json string corresponding to a JsonBuilder object and return it as an Ocaml value.
fn get_json_string(builder_val: ocaml::Value) -> ocaml::Value {
let builder = unsafe { JsonBuilder::from_ocaml(&builder_val) };
let json_string = serde_json::Value::from(builder).to_string();
ocaml::Str::from(&json_string as &str).to_value()
}
caml!(rs_convert_json_to_string(builder_val) {
failwith_on_panic( || {
get_json_string(builder_val)
})
});
......@@ -9,7 +9,6 @@
use crate::prelude::*;
use derive_more::*;
use std::convert::TryFrom;
mod variable;
pub use variable::*;
......@@ -18,6 +17,13 @@ pub use expression::*;
mod term;
pub use term::*;
/// A bitvector is a fixed-length vector of bits
/// with the semantics of a CPU register,
/// i.e. it supports two's complement modulo arithmetic.
///
/// Bitvector is just an alias for the [`apint::ApInt`] type.
pub type Bitvector = apint::ApInt;
/// An unsigned number of bytes.
///
/// Used to represent sizes of values in registers or in memory.
......@@ -61,12 +67,6 @@ pub use term::*;
#[serde(transparent)]
pub struct ByteSize(u64);
impl From<ByteSize> for BitSize {
fn from(bytesize: ByteSize) -> BitSize {
u16::try_from(u64::from(bytesize) * 8).unwrap()
}
}
impl From<ByteSize> for apint::BitWidth {
fn from(bytesize: ByteSize) -> apint::BitWidth {
apint::BitWidth::from((u64::from(bytesize) * 8) as usize)
......@@ -81,21 +81,31 @@ impl From<apint::BitWidth> for ByteSize {
}
impl ByteSize {
/// Create a new `ByteSize` object
pub fn new(value: u64) -> ByteSize {
ByteSize(value)
}
/// Convert to the equivalent size in bits (by multiplying with 8).
pub fn as_bit_length(self) -> usize {
(u64::from(self) * 8) as usize
}
}
#[cfg(test)]
mod tests {
use apint::BitWidth;
use super::*;
#[test]
fn check_bit_to_byte_conversion() {
let bits: BitSize = 8;
let bits: BitWidth = BitWidth::new(8).unwrap();
let bytes: ByteSize = bits.into();
assert_eq!(u64::from(bytes), 1);
let bits: BitSize = bytes.into();
assert_eq!(bits, 8);
let bits: BitWidth = bytes.into();
assert_eq!(bits.to_usize(), 8);
assert_eq!(ByteSize::new(2).as_bit_length(), 16);
}
}
......@@ -4,9 +4,6 @@
Parts of the cwe_checker that are written in Rust.
*/
#[macro_use]
extern crate ocaml;
use crate::analysis::graph::Graph;
use crate::analysis::pointer_inference::PointerInference;
use crate::intermediate_representation::Project;
......@@ -15,20 +12,16 @@ use crate::utils::log::{CweWarning, LogMessage};
pub mod abstract_domain;
pub mod analysis;
pub mod bil;
pub mod checkers;
pub mod ffi;
pub mod intermediate_representation;
pub mod pcode;
pub mod term;
pub mod utils;
mod prelude {
pub use apint::Width;
pub use serde::{Deserialize, Serialize};
pub use crate::bil::{BitSize, Bitvector};
pub use crate::intermediate_representation::ByteSize;
pub use crate::intermediate_representation::{Bitvector, ByteSize};
pub use crate::intermediate_representation::{Term, Tid};
pub use crate::AnalysisResults;
pub use anyhow::{anyhow, Error};
......
use super::{Arg, ArgIntent};
use crate::bil::*;
use crate::intermediate_representation::ExternSymbol as IrExternSymbol;
use crate::prelude::*;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct ExternSymbol {
pub tid: Tid,
pub address: String,
pub name: String,
pub calling_convention: Option<String>,
pub arguments: Vec<Arg>,
}
impl ExternSymbol {
/// Returns the return register of an extern symbol.
/// Returns an error if the function has not exactly one return argument
/// or if the return argument is not a register.
pub fn get_unique_return_register(&self) -> Result<&crate::bil::variable::Variable, Error> {
let return_args: Vec<_> = self
.arguments
.iter()
.filter(|arg| arg.intent.is_output())
.collect();
if return_args.len() != 1 {
return Err(anyhow!(
"Wrong number of return register: Got {}, expected 1",
return_args.len()
));
}
match &return_args[0].location {
Expression::Var(var) => Ok(var),
_ => Err(anyhow!("Return location is not a register")),
}
}
/// Returns the parameter expression of an extern symbol.
/// Returns an error if the function has not exactly one parameter argument.
pub fn get_unique_parameter(&self) -> Result<&crate::bil::Expression, Error> {
let param_args: Vec<_> = self
.arguments
.iter()
.filter(|arg| arg.intent.is_input())
.collect();
if param_args.len() != 1 {
return Err(anyhow!(
"Wrong number of return register: Got {}, expected 1",
param_args.len()
));
}
Ok(&param_args[0].location)
}
}
impl From<ExternSymbol> for IrExternSymbol {
fn from(symbol: ExternSymbol) -> IrExternSymbol {
let mut parameters = Vec::new();
let mut return_values = Vec::new();
for arg in symbol.arguments.into_iter() {
if matches!(
arg.intent,
ArgIntent::Input | ArgIntent::Both | ArgIntent::Unknown
) {
for ir_arg in arg.clone().into_ir_args() {
parameters.push(ir_arg);
}
}
if matches!(
arg.intent,
ArgIntent::Output | ArgIntent::Both | ArgIntent::Unknown
) {
for ir_arg in arg.into_ir_args() {
return_values.push(ir_arg);
}
}
}
IrExternSymbol {
tid: symbol.tid,
addresses: vec![symbol.address],
name: symbol.name,
calling_convention: None, // We do not parse more than one calling convention from BAP at the moment. So we assume everything uses the standard one.
parameters,
return_values,
no_return: false, // Last time I checked BAP had an attribute for non-returning functions, but did not actually set it.
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extern_symbol_serialization() {
let symbol = ExternSymbol {
tid: Tid::new("Tid"),
address: "Somewhere".to_string(),
name: "extern_fn".to_string(),
calling_convention: Some("cconv".to_string()),
arguments: Vec::new(),
};
let json: String = serde_json::to_string_pretty(&symbol).unwrap();
println!("{}", json);
let _symbol: ExternSymbol = serde_json::from_str(&json).unwrap();
}
}
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