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:
parent
721286da86
commit
6e2aa47285
@ -15,7 +15,7 @@ export_runtime!(
|
||||
CallStackCoverage::default() => CallStackCoverage; // QSym-style expression pruning
|
||||
tracing::TracingRuntime::new(
|
||||
StdShMemMessageFileWriter::from_stdshmem_default_env()
|
||||
.expect("unable to construct tracing runtime writer. (missing env?)")
|
||||
)
|
||||
=> tracing::TracingRuntime
|
||||
.expect("unable to construct tracing runtime writer. (missing env?)"),
|
||||
false
|
||||
) => tracing::TracingRuntime
|
||||
);
|
||||
|
@ -1,5 +1,8 @@
|
||||
//! # Concolic Tracing
|
||||
use core::num::NonZeroUsize;
|
||||
use core::{
|
||||
fmt::{Debug, Display, Error, Formatter},
|
||||
num::NonZeroUsize,
|
||||
};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -12,6 +15,39 @@ use serde::{Deserialize, Serialize};
|
||||
/// `SymExprRef`s are not valid across traces.
|
||||
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.
|
||||
/// The messages in the format are a perfect mirror of the methods that are called on the runtime during execution.
|
||||
#[cfg(feature = "std")]
|
||||
@ -297,13 +333,26 @@ pub enum SymExpr {
|
||||
PathConstraint {
|
||||
constraint: SymExprRef,
|
||||
taken: bool,
|
||||
site_id: usize,
|
||||
location: Location,
|
||||
},
|
||||
|
||||
/// These expressions won't be referenced again
|
||||
ExpressionsUnreachable {
|
||||
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")]
|
||||
|
@ -198,6 +198,7 @@ impl<R: Read> MessageFileReader<R> {
|
||||
*expr = self.make_absolute(*expr);
|
||||
}
|
||||
}
|
||||
SymExpr::Call { .. } | SymExpr::Return { .. } | SymExpr::BasicBlock { .. } => {}
|
||||
}
|
||||
SymExprRef::new(ret).unwrap()
|
||||
}
|
||||
@ -348,6 +349,7 @@ impl<W: Write + Seek> MessageFileWriter<W> {
|
||||
*expr = self.make_relative(*expr);
|
||||
}
|
||||
}
|
||||
SymExpr::Call { .. } | SymExpr::Return { .. } | SymExpr::BasicBlock { .. } => {}
|
||||
}
|
||||
self.serialization_options
|
||||
.serialize_into(&mut self.writer, &message)?;
|
||||
|
@ -280,9 +280,7 @@ fn generate_mutations(iter: impl Iterator<Item = (SymExprRef, SymExpr)>) -> Vec<
|
||||
if let Some(expr) = z3_expr {
|
||||
translation.insert(id, expr);
|
||||
} else if let SymExpr::PathConstraint {
|
||||
constraint,
|
||||
site_id: _,
|
||||
taken,
|
||||
constraint, taken, ..
|
||||
} = msg
|
||||
{
|
||||
let op = translation[&constraint].as_bool().unwrap();
|
||||
|
@ -253,7 +253,7 @@ where
|
||||
/// ```no_run
|
||||
/// # #[macro_use] extern crate symcc_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
|
||||
@ -267,7 +267,7 @@ where
|
||||
/// ```no_run
|
||||
/// # #[macro_use] extern crate symcc_runtime;
|
||||
/// # 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.
|
||||
///
|
||||
|
@ -1,5 +1,7 @@
|
||||
//! Tracing of expressions in a serialized form.
|
||||
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
pub use libafl::observers::concolic::serialization_format::StdShMemMessageFileWriter;
|
||||
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.
|
||||
pub struct TracingRuntime {
|
||||
writer: StdShMemMessageFileWriter,
|
||||
trace_locations: bool,
|
||||
}
|
||||
|
||||
impl TracingRuntime {
|
||||
/// 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]
|
||||
pub fn new(writer: StdShMemMessageFileWriter) -> Self {
|
||||
Self { writer }
|
||||
pub fn new(writer: StdShMemMessageFileWriter, trace_locations: bool) -> Self {
|
||||
Self {
|
||||
writer,
|
||||
trace_locations,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::unnecessary_wraps)]
|
||||
@ -143,11 +151,29 @@ impl Runtime for TracingRuntime {
|
||||
binary_expression_builder!(concat_helper, Concat);
|
||||
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]) {
|
||||
self.write_message(SymExpr::ExpressionsUnreachable {
|
||||
@ -159,7 +185,7 @@ impl Runtime for TracingRuntime {
|
||||
self.write_message(SymExpr::PathConstraint {
|
||||
constraint,
|
||||
taken,
|
||||
site_id,
|
||||
location: site_id.into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@
|
||||
19 Mul { a: 18, b: 17 }
|
||||
20 Integer { value: 7, bits: 32 }
|
||||
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 }
|
||||
23 Concat { a: 13, b: 22 }
|
||||
24 Concat { a: 14, b: 23 }
|
||||
@ -27,4 +27,4 @@
|
||||
26 SignedRem { a: 24, b: 25 }
|
||||
27 Integer { value: 0, bits: 32 }
|
||||
28 NotEqual { a: 26, b: 27 }
|
||||
29 PathConstraint { constraint: 28, taken: true, site_id: 11122032 }
|
||||
29 PathConstraint { constraint: 28, taken: true, location: 11122032 }
|
||||
|
@ -13,7 +13,8 @@ export_runtime!(
|
||||
NoFloat => NoFloat;
|
||||
tracing::TracingRuntime::new(
|
||||
StdShMemMessageFileWriter::from_stdshmem_default_env()
|
||||
.expect("unable to construct tracing runtime writer. (missing env?)")
|
||||
)
|
||||
=> tracing::TracingRuntime
|
||||
.expect("unable to construct tracing runtime writer. (missing env?)"),
|
||||
false
|
||||
)
|
||||
=> tracing::TracingRuntime
|
||||
);
|
||||
|
@ -46,7 +46,7 @@ echo "constraints: "
|
||||
cat constraints.txt
|
||||
|
||||
# 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/, site_id: .* / /' < expected_constraints.txt > expected_constraints_filtered.txt
|
||||
sed 's/, location: .* / /' < constraints.txt > constraints_filtered.txt
|
||||
sed 's/, location: .* / /' < expected_constraints.txt > expected_constraints_filtered.txt
|
||||
|
||||
diff constraints_filtered.txt expected_constraints_filtered.txt
|
Loading…
x
Reference in New Issue
Block a user