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
|
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
|
||||||
);
|
);
|
||||||
|
@ -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")]
|
||||||
|
@ -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)?;
|
||||||
|
@ -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();
|
||||||
|
@ -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.
|
||||||
///
|
///
|
||||||
|
@ -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(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 }
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
|
@ -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
|
Loading…
x
Reference in New Issue
Block a user