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
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
);

View File

@ -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")]

View File

@ -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)?;

View File

@ -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();

View File

@ -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.
///

View File

@ -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(),
});
}
}

View File

@ -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 }

View File

@ -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
);

View File

@ -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