add ability to trace location information in concolic tracer (#322)

* add ability to trace location information in concolic tracer

* fix formatting

* introduce location new-type

* fix conolic smoke test

* impl From instead of custom into_inner function

* fmt

* change to use usize instead of NonZeroUsize

in order to no over-constrain the implementation
This commit is contained in:
julihoh 2021-10-11 13:31:16 +02:00 committed by GitHub
parent 721286da86
commit 6e2aa47285
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 99 additions and 23 deletions

View File

@ -15,7 +15,7 @@ export_runtime!(
CallStackCoverage::default() => CallStackCoverage; // QSym-style expression pruning CallStackCoverage::default() => CallStackCoverage; // QSym-style expression pruning
tracing::TracingRuntime::new( tracing::TracingRuntime::new(
StdShMemMessageFileWriter::from_stdshmem_default_env() StdShMemMessageFileWriter::from_stdshmem_default_env()
.expect("unable to construct tracing runtime writer. (missing env?)") .expect("unable to construct tracing runtime writer. (missing env?)"),
) false
=> tracing::TracingRuntime ) => tracing::TracingRuntime
); );

View File

@ -1,5 +1,8 @@
//! # Concolic Tracing //! # Concolic Tracing
use core::num::NonZeroUsize; use core::{
fmt::{Debug, Display, Error, Formatter},
num::NonZeroUsize,
};
#[cfg(feature = "std")] #[cfg(feature = "std")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -12,6 +15,39 @@ use serde::{Deserialize, Serialize};
/// `SymExprRef`s are not valid across traces. /// `SymExprRef`s are not valid across traces.
pub type SymExprRef = NonZeroUsize; pub type SymExprRef = NonZeroUsize;
/// [`Location`]s are code locations encountered during concolic tracing, that are constructed from pointers, but not always in a meaningful way.
/// Therefore, a location is an opague value that can only be compared against itself.
///
/// It is possible to get at the underlying value using [`Into::into`], should this restriction be too inflexible for your usecase.
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Location(usize);
impl Debug for Location {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
core::fmt::Debug::fmt(&self.0, f)
}
}
impl Display for Location {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
core::fmt::Display::fmt(&self.0, f)
}
}
impl From<Location> for usize {
fn from(l: Location) -> Self {
l.0
}
}
impl From<usize> for Location {
fn from(v: usize) -> Self {
Self(v)
}
}
/// `SymExpr` represents a message in the serialization format. /// `SymExpr` represents a message in the serialization format.
/// The messages in the format are a perfect mirror of the methods that are called on the runtime during execution. /// The messages in the format are a perfect mirror of the methods that are called on the runtime during execution.
#[cfg(feature = "std")] #[cfg(feature = "std")]
@ -297,13 +333,26 @@ pub enum SymExpr {
PathConstraint { PathConstraint {
constraint: SymExprRef, constraint: SymExprRef,
taken: bool, taken: bool,
site_id: usize, location: Location,
}, },
/// These expressions won't be referenced again /// These expressions won't be referenced again
ExpressionsUnreachable { ExpressionsUnreachable {
exprs: Vec<SymExprRef>, exprs: Vec<SymExprRef>,
}, },
/// Location information regarding a call. Tracing this information is optional.
Call {
location: Location,
},
/// Location information regarding a return. Tracing this information is optional.
Return {
location: Location,
},
/// Location information regarding a basic block. Tracing this information is optional.
BasicBlock {
location: Location,
},
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]

View File

@ -198,6 +198,7 @@ impl<R: Read> MessageFileReader<R> {
*expr = self.make_absolute(*expr); *expr = self.make_absolute(*expr);
} }
} }
SymExpr::Call { .. } | SymExpr::Return { .. } | SymExpr::BasicBlock { .. } => {}
} }
SymExprRef::new(ret).unwrap() SymExprRef::new(ret).unwrap()
} }
@ -348,6 +349,7 @@ impl<W: Write + Seek> MessageFileWriter<W> {
*expr = self.make_relative(*expr); *expr = self.make_relative(*expr);
} }
} }
SymExpr::Call { .. } | SymExpr::Return { .. } | SymExpr::BasicBlock { .. } => {}
} }
self.serialization_options self.serialization_options
.serialize_into(&mut self.writer, &message)?; .serialize_into(&mut self.writer, &message)?;

View File

@ -280,9 +280,7 @@ fn generate_mutations(iter: impl Iterator<Item = (SymExprRef, SymExpr)>) -> Vec<
if let Some(expr) = z3_expr { if let Some(expr) = z3_expr {
translation.insert(id, expr); translation.insert(id, expr);
} else if let SymExpr::PathConstraint { } else if let SymExpr::PathConstraint {
constraint, constraint, taken, ..
site_id: _,
taken,
} = msg } = msg
{ {
let op = translation[&constraint].as_bool().unwrap(); let op = translation[&constraint].as_bool().unwrap();

View File

@ -253,7 +253,7 @@ where
/// ```no_run /// ```no_run
/// # #[macro_use] extern crate symcc_runtime; /// # #[macro_use] extern crate symcc_runtime;
/// # use symcc_runtime::{tracing::TracingRuntime, Runtime}; /// # use symcc_runtime::{tracing::TracingRuntime, Runtime};
/// export_runtime!(TracingRuntime::new(todo!()) => TracingRuntime); /// export_runtime!(TracingRuntime::new(todo!(), todo!()) => TracingRuntime);
/// ``` /// ```
/// ///
/// ## Runtime composition using `Filter`s /// ## Runtime composition using `Filter`s
@ -267,7 +267,7 @@ where
/// ```no_run /// ```no_run
/// # #[macro_use] extern crate symcc_runtime; /// # #[macro_use] extern crate symcc_runtime;
/// # use symcc_runtime::{tracing::TracingRuntime, Runtime, filter::NoFloat}; /// # use symcc_runtime::{tracing::TracingRuntime, Runtime, filter::NoFloat};
/// export_runtime!(NoFloat => NoFloat; TracingRuntime::new(todo!()) => TracingRuntime); /// export_runtime!(NoFloat => NoFloat; TracingRuntime::new(todo!(), todo!()) => TracingRuntime);
/// ``` /// ```
/// This will construct a runtime that is first filtered by [`filter::NoFloat`] and then traced by the tracing runtime. /// This will construct a runtime that is first filtered by [`filter::NoFloat`] and then traced by the tracing runtime.
/// ///

View File

@ -1,5 +1,7 @@
//! Tracing of expressions in a serialized form. //! Tracing of expressions in a serialized form.
use std::num::NonZeroUsize;
pub use libafl::observers::concolic::serialization_format::StdShMemMessageFileWriter; pub use libafl::observers::concolic::serialization_format::StdShMemMessageFileWriter;
use libafl::observers::concolic::SymExpr; use libafl::observers::concolic::SymExpr;
@ -9,13 +11,19 @@ use crate::{RSymExpr, Runtime};
/// The format can be read from elsewhere to perform processing of the expressions outside of the runtime. /// The format can be read from elsewhere to perform processing of the expressions outside of the runtime.
pub struct TracingRuntime { pub struct TracingRuntime {
writer: StdShMemMessageFileWriter, writer: StdShMemMessageFileWriter,
trace_locations: bool,
} }
impl TracingRuntime { impl TracingRuntime {
/// Creates the runtime, tracing using the given writer. /// Creates the runtime, tracing using the given writer.
/// When `trace_locations` is true, location information for calls, returns and basic blocks will also be part of the trace.
/// Tracing location information can drastically increase trace size. It is therefore recommended to not active this if not needed.
#[must_use] #[must_use]
pub fn new(writer: StdShMemMessageFileWriter) -> Self { pub fn new(writer: StdShMemMessageFileWriter, trace_locations: bool) -> Self {
Self { writer } Self {
writer,
trace_locations,
}
} }
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
@ -143,11 +151,29 @@ impl Runtime for TracingRuntime {
binary_expression_builder!(concat_helper, Concat); binary_expression_builder!(concat_helper, Concat);
expression_builder!(extract_helper(op: RSymExpr, first_bit:usize, last_bit:usize) => Extract); expression_builder!(extract_helper(op: RSymExpr, first_bit:usize, last_bit:usize) => Extract);
fn notify_call(&mut self, _site_id: usize) {} fn notify_call(&mut self, site_id: usize) {
if self.trace_locations {
self.write_message(SymExpr::Call {
location: site_id.into(),
});
}
}
fn notify_ret(&mut self, _site_id: usize) {} fn notify_ret(&mut self, site_id: usize) {
if self.trace_locations {
self.write_message(SymExpr::Return {
location: site_id.into(),
});
}
}
fn notify_basic_block(&mut self, _site_id: usize) {} fn notify_basic_block(&mut self, site_id: usize) {
if self.trace_locations {
self.write_message(SymExpr::BasicBlock {
location: site_id.into(),
});
}
}
fn expression_unreachable(&mut self, exprs: &[RSymExpr]) { fn expression_unreachable(&mut self, exprs: &[RSymExpr]) {
self.write_message(SymExpr::ExpressionsUnreachable { self.write_message(SymExpr::ExpressionsUnreachable {
@ -159,7 +185,7 @@ impl Runtime for TracingRuntime {
self.write_message(SymExpr::PathConstraint { self.write_message(SymExpr::PathConstraint {
constraint, constraint,
taken, taken,
site_id, location: site_id.into(),
}); });
} }
} }

View File

@ -19,7 +19,7 @@
19 Mul { a: 18, b: 17 } 19 Mul { a: 18, b: 17 }
20 Integer { value: 7, bits: 32 } 20 Integer { value: 7, bits: 32 }
21 SignedLessThan { a: 19, b: 20 } 21 SignedLessThan { a: 19, b: 20 }
22 PathConstraint { constraint: 21, taken: false, site_id: 11229456 } 22 PathConstraint { constraint: 21, taken: false, location: 11229456 }
22 Concat { a: 12, b: 11 } 22 Concat { a: 12, b: 11 }
23 Concat { a: 13, b: 22 } 23 Concat { a: 13, b: 22 }
24 Concat { a: 14, b: 23 } 24 Concat { a: 14, b: 23 }
@ -27,4 +27,4 @@
26 SignedRem { a: 24, b: 25 } 26 SignedRem { a: 24, b: 25 }
27 Integer { value: 0, bits: 32 } 27 Integer { value: 0, bits: 32 }
28 NotEqual { a: 26, b: 27 } 28 NotEqual { a: 26, b: 27 }
29 PathConstraint { constraint: 28, taken: true, site_id: 11122032 } 29 PathConstraint { constraint: 28, taken: true, location: 11122032 }

View File

@ -13,7 +13,8 @@ export_runtime!(
NoFloat => NoFloat; NoFloat => NoFloat;
tracing::TracingRuntime::new( tracing::TracingRuntime::new(
StdShMemMessageFileWriter::from_stdshmem_default_env() StdShMemMessageFileWriter::from_stdshmem_default_env()
.expect("unable to construct tracing runtime writer. (missing env?)") .expect("unable to construct tracing runtime writer. (missing env?)"),
) false
=> tracing::TracingRuntime )
=> tracing::TracingRuntime
); );

View File

@ -46,7 +46,7 @@ echo "constraints: "
cat constraints.txt cat constraints.txt
# site_id's in the constraints trace will differ for every run. we therefore filter those. # site_id's in the constraints trace will differ for every run. we therefore filter those.
sed 's/, site_id: .* / /' < constraints.txt > constraints_filtered.txt sed 's/, location: .* / /' < constraints.txt > constraints_filtered.txt
sed 's/, site_id: .* / /' < expected_constraints.txt > expected_constraints_filtered.txt sed 's/, location: .* / /' < expected_constraints.txt > expected_constraints_filtered.txt
diff constraints_filtered.txt expected_constraints_filtered.txt diff constraints_filtered.txt expected_constraints_filtered.txt