11//! [`LLVM` `PcGuard`](https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-pcs-with-guards) runtime for `LibAFL`.
22
3+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
4+ use core:: ffi:: c_void;
35#[ rustversion:: nightly]
46#[ cfg( any( feature = "sancov_ngram4" , feature = "sancov_ngram8" ) ) ]
57use core:: simd:: num:: SimdUint ;
8+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
9+ use core:: sync:: atomic:: { AtomicPtr , Ordering } ;
610use core:: { mem:: align_of, slice} ;
11+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
12+ use std:: collections:: { HashMap , HashSet } ;
13+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
14+ use std:: string:: String ;
15+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
16+ use std:: sync:: Mutex ;
717
818#[ cfg( any(
919 feature = "sancov_ngram4" ,
@@ -16,15 +26,9 @@ use libafl::executors::hooks::ExecutorHook;
1626#[ cfg( any( feature = "sancov_ngram4" , feature = "sancov_ngram8" ) ) ]
1727#[ allow( unused_imports) ] // only used in an unused function
1828use crate :: EDGES_MAP_DEFAULT_SIZE ;
19- #[ cfg( any(
20- feature = "pointer_maps" ,
21- feature = "sancov_pcguard_edges" ,
22- feature = "sancov_pcguard_hitcounts" ,
23- feature = "sancov_ctx" ,
24- feature = "sancov_ngram4" ,
25- feature = "sancov_ngram8" ,
26- ) ) ]
29+ #[ cfg( feature = "coverage" ) ]
2730use crate :: coverage:: EDGES_MAP ;
31+ #[ cfg( feature = "coverage" ) ]
2832use crate :: coverage:: MAX_EDGES_FOUND ;
2933#[ cfg( feature = "pointer_maps" ) ]
3034use crate :: { EDGES_MAP_ALLOCATED_SIZE , coverage:: EDGES_MAP_PTR } ;
@@ -69,6 +73,21 @@ pub static SHR_8: Ngram8 = Ngram8::from_array([1, 1, 1, 1, 1, 1, 1, 1]);
6973
7074static mut PC_TABLES : Vec < & ' static [ PcTableEntry ] > = Vec :: new ( ) ;
7175
76+ /// Type for the PC guard hook
77+ pub type PcGuardHook = unsafe extern "C" fn ( * mut u32 ) ;
78+
79+ /// Type for the target PC guard hook (with PC)
80+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
81+ pub type TargetPcGuardHook = unsafe extern "C" fn ( * mut u32 , usize ) ;
82+
83+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
84+ unsafe extern "C" fn nop_target_pc_guard ( _guard : * mut u32 , _pc : usize ) { }
85+
86+ /// The global hook for `__libafl_targets_trace_pc_guard`
87+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
88+ pub static LIBAFL_TARGETS_TRACE_PC_GUARD_HOOK : AtomicPtr < c_void > =
89+ AtomicPtr :: new ( nop_target_pc_guard as * mut c_void ) ;
90+
7291use alloc:: vec:: Vec ;
7392#[ cfg( any(
7493 feature = "sancov_ngram4" ,
@@ -205,14 +224,74 @@ unsafe extern "C" {
205224 pub static mut __afl_prev_ctx: u32 ;
206225}
207226
208- /// Callback for sancov `pc_guard` - usually called by `llvm` on each block or edge.
227+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
228+ static COVERED_PCS : Mutex < Option < HashSet < usize > > > = Mutex :: new ( None ) ;
229+
230+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
231+ /// Dump the covered lines
209232///
210- /// # Safety
211- /// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position.
212- /// Should usually not be called directly.
213- #[ unsafe( no_mangle) ]
214- #[ allow( unused_assignments) ] // cfg dependent
215- pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard ( guard : * mut u32 ) {
233+ /// # Arguments
234+ ///
235+ /// * `clear` - Whether to clear the covered lines
236+ ///
237+ /// # Returns
238+ ///
239+ /// * `HashMap<usize, String>` - The covered lines, location and symbol
240+ ///
241+ /// # Example
242+ ///
243+ /// ```
244+ /// # use libafl_targets::sancov_pcguard::dump_covered_lines;
245+ ///
246+ /// let map = dump_covered_lines(true);
247+ /// for (pc, sym) in map {
248+ /// println!("PC: {:x} -> {}", pc, sym);
249+ /// }
250+ /// ```
251+ pub fn dump_covered_lines ( clear : bool ) -> HashMap < usize , String > {
252+ let mut res = HashMap :: new ( ) ;
253+ if let Ok ( mut guard) = COVERED_PCS . lock ( ) {
254+ if let Some ( set) = guard. as_mut ( ) {
255+ for & pc in set. iter ( ) {
256+ let mut symbol_str = String :: new ( ) ;
257+ backtrace:: resolve ( pc as * mut _ , |symbol| {
258+ if let Some ( name) = symbol. name ( ) {
259+ symbol_str. push_str ( & format ! ( "{}" , name) ) ;
260+ }
261+ if let Some ( filename) = symbol. filename ( ) {
262+ symbol_str. push_str ( & format ! ( " at {:?}" , filename) ) ;
263+ }
264+ if let Some ( lineno) = symbol. lineno ( ) {
265+ symbol_str. push_str ( & format ! ( ":{}" , lineno) ) ;
266+ }
267+ } ) ;
268+ res. insert ( pc, symbol_str) ;
269+ }
270+ if clear {
271+ set. clear ( ) ;
272+ }
273+ }
274+ }
275+ res
276+ }
277+
278+ /// Enable coverage collection
279+ pub fn libafl_targets_enable_coverage_collection ( ) {
280+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
281+ LIBAFL_TARGETS_TRACE_PC_GUARD_HOOK . store (
282+ __libafl_targets_trace_pc_guard_impl as * mut c_void ,
283+ Ordering :: Release ,
284+ ) ;
285+ }
286+
287+ /// Disable coverage collection
288+ pub fn libafl_targets_disable_coverage_collection ( ) {
289+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
290+ LIBAFL_TARGETS_TRACE_PC_GUARD_HOOK . store ( nop_target_pc_guard as * mut c_void , Ordering :: Release ) ;
291+ }
292+
293+ #[ inline( always) ]
294+ unsafe fn handle_pc_guard_inner ( guard : * mut u32 ) {
216295 unsafe {
217296 #[ allow( unused_variables, unused_mut) ] // cfg dependent
218297 let mut pos = * guard as usize ;
@@ -260,6 +339,54 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
260339 }
261340}
262341
342+ /// Callback for sancov `pc_guard` - usually called by `llvm` on each block or edge.
343+ ///
344+ /// # Safety
345+ /// Dereferences `guard`, reads the position from there, then dereferences the [`EDGES_MAP`] at that position.
346+ /// Should usually not be called directly.
347+ #[ unsafe( no_mangle) ]
348+ #[ allow( unused_assignments) ] // cfg dependent
349+ #[ cfg( not( feature = "sancov_pcguard_dump_cov" ) ) ]
350+ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard ( guard : * mut u32 ) {
351+ unsafe {
352+ handle_pc_guard_inner ( guard) ;
353+ }
354+ }
355+
356+ #[ cfg( not( feature = "sancov_pcguard_dump_cov" ) ) ]
357+ unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_impl ( guard : * mut u32 ) {
358+ unsafe {
359+ handle_pc_guard_inner ( guard) ;
360+ }
361+ }
362+
363+ /// The C shim for `__sanitizer_cov_trace_pc_guard`
364+ #[ unsafe( no_mangle) ]
365+ #[ allow( unused_assignments) ] // cfg dependent
366+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
367+ pub unsafe extern "C" fn __libafl_targets_trace_pc_guard ( guard : * mut u32 , pc : usize ) {
368+ unsafe {
369+ let hook_ptr = LIBAFL_TARGETS_TRACE_PC_GUARD_HOOK . load ( Ordering :: Acquire ) ;
370+ let hook: TargetPcGuardHook = core:: mem:: transmute ( hook_ptr) ;
371+ hook ( guard, pc) ;
372+ }
373+ }
374+
375+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
376+ unsafe extern "C" fn __libafl_targets_trace_pc_guard_impl ( guard : * mut u32 , pc : usize ) {
377+ unsafe {
378+ if let Ok ( mut guard) = COVERED_PCS . lock ( ) {
379+ if guard. is_none ( ) {
380+ * guard = Some ( HashSet :: new ( ) ) ;
381+ }
382+ if let Some ( set) = guard. as_mut ( ) {
383+ set. insert ( pc) ;
384+ }
385+ }
386+ handle_pc_guard_inner ( guard) ;
387+ }
388+ }
389+
263390/// Initialize the sancov `pc_guard` - usually called by `llvm`.
264391///
265392/// # Safety
@@ -272,10 +399,12 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32
272399 EDGES_MAP_PTR = & raw mut EDGES_MAP as * mut u8 ;
273400 }
274401
402+ #[ cfg( feature = "coverage" ) ]
275403 if core:: ptr:: eq ( start, stop) || * start != 0 {
276404 return ;
277405 }
278406
407+ #[ cfg( feature = "coverage" ) ]
279408 while start < stop {
280409 * start = MAX_EDGES_FOUND as u32 ;
281410 start = start. offset ( 1 ) ;
@@ -355,3 +484,28 @@ pub fn sanitizer_cov_pc_table<'a>() -> impl Iterator<Item = &'a [PcTableEntry]>
355484 pc_tables. iter ( ) . copied ( )
356485 }
357486}
487+
488+ #[ cfg( test) ]
489+ #[ cfg( feature = "sancov_pcguard_dump_cov" ) ]
490+ mod tests {
491+ use super :: * ;
492+
493+ #[ test]
494+ fn test_dump_cov ( ) {
495+ unsafe extern "C" {
496+ fn __sanitizer_cov_trace_pc_guard ( guard : * mut u32 ) ;
497+ }
498+ // Simulate a call to __sanitizer_cov_trace_pc_guard
499+ let mut guard = 0 ;
500+ unsafe {
501+ __sanitizer_cov_trace_pc_guard( & mut guard) ;
502+ }
503+
504+ let map = dump_covered_lines ( false ) ;
505+ assert ! ( !map. is_empty( ) ) ;
506+ for ( pc, sym) in map {
507+ println ! ( "PC: {:x} -> {}" , pc, sym) ;
508+ assert ! ( !sym. is_empty( ) ) ;
509+ }
510+ }
511+ }
0 commit comments