diff --git a/fuzzers/libfuzzer_stb_image_concolic/runtime/src/lib.rs b/fuzzers/libfuzzer_stb_image_concolic/runtime/src/lib.rs index 5d1d14108a..9d8c46abdd 100644 --- a/fuzzers/libfuzzer_stb_image_concolic/runtime/src/lib.rs +++ b/fuzzers/libfuzzer_stb_image_concolic/runtime/src/lib.rs @@ -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 ); diff --git a/libafl/src/observers/concolic/mod.rs b/libafl/src/observers/concolic/mod.rs index 230ca1faa9..187697a988 100644 --- a/libafl/src/observers/concolic/mod.rs +++ b/libafl/src/observers/concolic/mod.rs @@ -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 for usize { + fn from(l: Location) -> Self { + l.0 + } +} + +impl From 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, }, + + /// 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")] diff --git a/libafl/src/observers/concolic/serialization_format.rs b/libafl/src/observers/concolic/serialization_format.rs index 9b1d6ad9c7..c27521795b 100644 --- a/libafl/src/observers/concolic/serialization_format.rs +++ b/libafl/src/observers/concolic/serialization_format.rs @@ -198,6 +198,7 @@ impl MessageFileReader { *expr = self.make_absolute(*expr); } } + SymExpr::Call { .. } | SymExpr::Return { .. } | SymExpr::BasicBlock { .. } => {} } SymExprRef::new(ret).unwrap() } @@ -348,6 +349,7 @@ impl MessageFileWriter { *expr = self.make_relative(*expr); } } + SymExpr::Call { .. } | SymExpr::Return { .. } | SymExpr::BasicBlock { .. } => {} } self.serialization_options .serialize_into(&mut self.writer, &message)?; diff --git a/libafl/src/stages/concolic.rs b/libafl/src/stages/concolic.rs index 677acbf9b9..4bc6771ffe 100644 --- a/libafl/src/stages/concolic.rs +++ b/libafl/src/stages/concolic.rs @@ -280,9 +280,7 @@ fn generate_mutations(iter: impl Iterator) -> 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(); diff --git a/libafl_concolic/symcc_runtime/src/lib.rs b/libafl_concolic/symcc_runtime/src/lib.rs index b30e2fa2ab..8a4a51bc48 100644 --- a/libafl_concolic/symcc_runtime/src/lib.rs +++ b/libafl_concolic/symcc_runtime/src/lib.rs @@ -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. /// diff --git a/libafl_concolic/symcc_runtime/src/tracing.rs b/libafl_concolic/symcc_runtime/src/tracing.rs index 4c22429c84..d3c6c98b97 100644 --- a/libafl_concolic/symcc_runtime/src/tracing.rs +++ b/libafl_concolic/symcc_runtime/src/tracing.rs @@ -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(), }); } } diff --git a/libafl_concolic/test/expected_constraints.txt b/libafl_concolic/test/expected_constraints.txt index 2eaab7ab1b..7b4f961e63 100644 --- a/libafl_concolic/test/expected_constraints.txt +++ b/libafl_concolic/test/expected_constraints.txt @@ -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 } diff --git a/libafl_concolic/test/runtime_test/src/lib.rs b/libafl_concolic/test/runtime_test/src/lib.rs index 6dd72fff0d..976fbf29d5 100644 --- a/libafl_concolic/test/runtime_test/src/lib.rs +++ b/libafl_concolic/test/runtime_test/src/lib.rs @@ -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 ); diff --git a/libafl_concolic/test/smoke_test.sh b/libafl_concolic/test/smoke_test.sh index c591e6e605..999d1688df 100755 --- a/libafl_concolic/test/smoke_test.sh +++ b/libafl_concolic/test/smoke_test.sh @@ -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 \ No newline at end of file