Skip to content

Commit 713ae29

Browse files
andreafioraldictfhackerdomenukk
authored
Introspection (AFLplusplus#97)
* Rework to put `ClientPerfStats` in `State` and pass that along. Still need to work on getting granular information from `Feedback` and `Observer` * Add perf_stats feature to libafl/Cargo.toml * Update feedbacks to have with_perf * Remove unneeeded print statement * cargo fmt all the things * use local llvmint vs cpu specific asm for reading cycle counter * Remove debug testing code * Stats timeout to 3 seconds * Inline smallish functions for ClientPerfStats * Remove .libs/llvmint and have the correct conditional compilation of link_llvm_intrinsics on the perf_stats feature * pub(crate) the NUM_FEEDBACK and NUM_STAGES consts * Tcp Broker to Broker Communication (AFLplusplus#66) * initial b2b implementation * no_std and clippy fixes * b2b testcase added * more correct testcases * fixed b2b * typo * fixed unused warning * clippy fixes * fallback to systemtime on non-x86 * make clippy more strict * small fixes * bump 0.2.1 * readme Co-authored-by: ctfhacker <[email protected]> Co-authored-by: Dominik Maier <[email protected]>
1 parent fb96cbc commit 713ae29

File tree

23 files changed

+934
-37
lines changed

23 files changed

+934
-37
lines changed

README.md

-6
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,6 @@ Rust directly, instructions can be found [here](https://www.rust-lang.org/tools/
4242
git clone https://github.com/AFLplusplus/LibAFL
4343
```
4444

45-
If you want to get the latest and greatest features,
46-
```
47-
git checkout dev
48-
```
49-
5045
Build the library using
5146

5247
```
@@ -65,7 +60,6 @@ cargo doc
6560
cd docs && mdbook serve
6661
```
6762

68-
6963
We collect all example fuzzers in [`./fuzzers`](./fuzzers/).
7064
Be sure to read their documentation (and source), this is *the natural way to get started!*
7165

clippy.sh

-4
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,12 @@
33
cargo clean -p libafl
44
RUST_BACKTRACE=full cargo clippy --all --all-features --tests -- \
55
-D clippy::pedantic \
6-
-W clippy::cast_sign_loss \
76
-W clippy::similar-names \
8-
-W clippy::cast_ptr_alignment \
9-
-W clippy::cast_possible_wrap \
107
-W clippy::unused_self \
118
-W clippy::too_many_lines \
129
-W clippy::option_if_let_else \
1310
-W clippy::must-use-candidate \
1411
-W clippy::if-not-else \
15-
-W clippy::doc-markdown \
1612
-A clippy::type_repetition_in_bounds \
1713
-A clippy::missing-errors-doc \
1814
-A clippy::cast-possible-truncation \

fuzzers/libfuzzer_libpng/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ which = { version = "4.0.2" }
2020
num_cpus = "1.0"
2121

2222
[dependencies]
23-
libafl = { path = "../../libafl/" }
23+
libafl = { path = "../../libafl/", features = ["default", "introspection"] }
24+
# libafl = { path = "../../libafl/", features = ["default"] }
2425
libafl_targets = { path = "../../libafl_targets/", features = ["pcguard_hitcounts", "libfuzzer"] }
2526
# TODO Include it only when building cc
2627
libafl_cc = { path = "../../libafl_cc/" }

fuzzers/libfuzzer_libpng/Makefile

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
PWD=`pwd`
2+
FUZZER_NAME="fuzzer_libpng"
3+
4+
all:
5+
# Build the libpng libfuzzer library
6+
cargo build --release
7+
8+
# Build the libpng harness
9+
$(PWD)/target/release/libafl_cxx \
10+
$(PWD)/harness.cc \
11+
$(PWD)/libpng-1.6.37/.libs/libpng16.a \
12+
-I$(PWD)/libpng-1.6.37/ \
13+
-o $(FUZZER_NAME) \
14+
-lm -lz
15+
16+
run: all
17+
./$(FUZZER_NAME) &
18+
sleep 0.2
19+
./$(FUZZER_NAME) >/dev/null 2>/dev/null &
20+
21+
short_test: all
22+
timeout 11s ./$(FUZZER_NAME) &
23+
sleep 0.2
24+
timeout 10s taskset -c 0 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
25+
timeout 10s taskset -c 1 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
26+
timeout 10s taskset -c 2 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
27+
timeout 10s taskset -c 3 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
28+
29+
test: all
30+
timeout 60s ./$(FUZZER_NAME) &
31+
sleep 0.2
32+
timeout 59s taskset 0x00000001 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
33+
timeout 59s taskset 0x00000002 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
34+
timeout 59s taskset 0x00000004 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
35+
timeout 59s taskset 0x00000008 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
36+
# timeout 59s taskset 0x00000010 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
37+
# timeout 59s taskset 0x00000020 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
38+
# timeout 59s taskset 0x00000040 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
39+
# timeout 59s taskset 0x00000080 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
40+
# timeout 59s taskset 0x00000100 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
41+
# timeout 59s taskset 0x00000200 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
42+
# timeout 59s taskset 0x00000400 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
43+
# timeout 59s taskset 0x00000800 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
44+
# timeout 59s taskset 0x00001000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
45+
# timeout 59s taskset 0x00002000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
46+
# timeout 59s taskset 0x00004000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
47+
# timeout 59s taskset 0x00008000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
48+
# timeout 59s taskset 0x00010000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
49+
# timeout 59s taskset 0x00020000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
50+
# timeout 59s taskset 0x00040000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
51+
# timeout 59s taskset 0x00080000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
52+
# timeout 59s taskset 0x00100000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
53+
# timeout 59s taskset 0x00200000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
54+
# timeout 59s taskset 0x00400000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
55+
# timeout 59s taskset 0x00800000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
56+
# timeout 59s taskset 0x01000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
57+
# timeout 59s taskset 0x02000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
58+
# timeout 59s taskset 0x04000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
59+
# timeout 59s taskset 0x08000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
60+
# timeout 59s taskset 0x10000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
61+
# timeout 59s taskset 0x20000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
62+
# timeout 59s taskset 0x40000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &
63+
# timeout 59s taskset 0x80000000 ./$(FUZZER_NAME) >/dev/null 2>/dev/null &

fuzzers/libfuzzer_libpng/README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ In contrast to other fuzzer examples, this setup uses `fuzz_loop_for`, to occasi
66
While this costs performance, it can be useful for targets with memory leaks or other instabilities.
77
If your target is really instable, however, consider exchanging the `InProcessExecutor` for a `ForkserverExecutor` instead.
88

9+
It also uses the `introspection` feature, printing fuzzer stats during execution.
10+
911
To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example.
1012
It has been tested on Linux.
1113

@@ -51,7 +53,7 @@ This allows you to run multiple different builds of the same fuzzer alongside, f
5153

5254
## Run
5355

54-
The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. Currently you must run the clients from the libfuzzer_libpng directory for them to be able to access the PNG corpus.
56+
The first time you run the binary, the broker will open a tcp port (currently on port `1337`), waiting for fuzzer clients to connect. This port is local and only used for the initial handshake. All further communication happens via shared map, to be independent of the kernel. Currently, you must run the clients from the libfuzzer_libpng directory for them to be able to access the PNG corpus.
5557

5658
```
5759
./fuzzer_libpng

fuzzers/libfuzzer_libpng/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
6363
}
6464
},
6565
};
66-
66+
6767
// Create an observation channel using the coverage map
6868
let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
6969
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("edges", edges));

libafl/Cargo.toml

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "libafl"
3-
version = "0.2.0"
3+
version = "0.2.1"
44
authors = ["Andrea Fioraldi <[email protected]>", "Dominik Maier <[email protected]>"]
55
description = "Slot your own fuzzers together and extend their features using Rust"
66
documentation = "https://docs.rs/libafl"
@@ -40,10 +40,11 @@ default = ["std", "anymap_debug", "derive", "llmp_compression"]
4040
std = [] # print, sharedmap, ... support
4141
anymap_debug = ["serde_json"] # uses serde_json to Debug the anymap trait. Disable for smaller footprint.
4242
derive = ["libafl_derive"] # provide derive(SerdeAny) macro.
43-
llmp_small_maps = [] # reduces initial map size for llmp
44-
llmp_debug = ["backtrace"] # Enables debug output for LLMP
45-
llmp_compression = [] # llmp compression using GZip
4643
llmp_bind_public = [] # If set, llmp will bind to 0.0.0.0, allowing cross-device communication. Binds to localhost by default.
44+
llmp_compression = [] # llmp compression using GZip
45+
llmp_debug = ["backtrace"] # Enables debug output for LLMP
46+
llmp_small_maps = [] # reduces initial map size for llmp
47+
introspection = [] # Include performance statistics of the fuzzing pipeline
4748

4849
[[example]]
4950
name = "llmp_test"
@@ -60,7 +61,7 @@ erased-serde = "0.3.12"
6061
postcard = { version = "0.5.1", features = ["alloc"] } # no_std compatible serde serialization fromat
6162
static_assertions = "1.1.0"
6263
ctor = "0.1.20"
63-
libafl_derive = { version = "0.1.0", optional = true, path = "../libafl_derive" }
64+
libafl_derive = { optional = true, path = "../libafl_derive", version = "0.2.1" }
6465
serde_json = { version = "1.0", optional = true, default-features = false, features = ["alloc"] } # an easy way to debug print SerdeAnyMap
6566
compression = { version = "0.1.5" }
6667
num_enum = "0.5.1"

libafl/src/bolts/llmp.rs

+2
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ For broker2broker communication, all messages are forwarded via network sockets.
5555
5656
Check out the `llmp_test` example in ./examples, or build it with `cargo run --example llmp_test`.
5757
58+
For broker2broker communication, all messages are forwarded via network sockets.
59+
5860
*/
5961

6062
use alloc::{string::String, vec::Vec};

libafl/src/cpu.rs

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//! Architecture agnostic processor features
2+
3+
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
4+
use crate::utils::current_nanos;
5+
6+
// TODO: Add more architectures, using C code, see
7+
// https://github.com/google/benchmark/blob/master/src/cycleclock.h
8+
// Or using llvm intrinsics (if they ever should become available in stable rust?)
9+
10+
/// Read a timestamp for measurements.
11+
///
12+
/// This function is a wrapper around different ways to get a timestamp, fast
13+
/// In this way, an experiment only has to
14+
/// change this implementation rather than every instead of [`cpu::read_time_counter`]
15+
/// It is using [`rdtsc`] on `x86_64` and `x86`.
16+
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
17+
#[must_use]
18+
pub fn read_time_counter() -> u64 {
19+
unsafe { core::arch::x86_64::_rdtsc() }
20+
}
21+
22+
/// Read a timestamp for measurements.
23+
///
24+
/// This function is a wrapper around different ways to get a timestamp, fast
25+
/// In this way, an experiment only has to
26+
/// change this implementation rather than every instead of [`cpu::read_time_counter`]
27+
/// On unsupported architectures, it's falling back to normal system time, in millis.
28+
#[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))]
29+
pub fn read_time_counter() -> u64 {
30+
current_nanos()
31+
}

libafl/src/events/llmp.rs

+30-2
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ where
230230
let client = stats.client_stats_mut_for(sender_id);
231231
client.update_corpus_size(*corpus_size as u64);
232232
client.update_executions(*executions as u64, *time);
233-
stats.display(event.name().to_string() + " #" + &sender_id.to_string());
233+
// stats.display(event.name().to_string() + " #" + &sender_id.to_string());
234234
Ok(BrokerEventResult::Forward)
235235
}
236236
Event::UpdateStats {
@@ -241,7 +241,35 @@ where
241241
// TODO: The stats buffer should be added on client add.
242242
let client = stats.client_stats_mut_for(sender_id);
243243
client.update_executions(*executions as u64, *time);
244-
stats.display(event.name().to_string() + " #" + &sender_id.to_string());
244+
if sender_id == 1 {
245+
stats.display(event.name().to_string() + " #" + &sender_id.to_string());
246+
}
247+
Ok(BrokerEventResult::Handled)
248+
}
249+
#[cfg(feature = "introspection")]
250+
Event::UpdatePerfStats {
251+
time,
252+
executions,
253+
introspection_stats,
254+
phantom: _,
255+
} => {
256+
// TODO: The stats buffer should be added on client add.
257+
258+
// Get the client for the sender ID
259+
let client = stats.client_stats_mut_for(sender_id);
260+
261+
// Update the normal stats for this client
262+
client.update_executions(*executions as u64, *time);
263+
264+
// Update the performance stats for this client
265+
client.update_introspection_stats(**introspection_stats);
266+
267+
// Display the stats via `.display` only on core #1
268+
if sender_id == 1 {
269+
stats.display(event.name().to_string() + " #" + &sender_id.to_string());
270+
}
271+
272+
// Correctly handled the event
245273
Ok(BrokerEventResult::Handled)
246274
}
247275
Event::Objective { objective_size } => {

libafl/src/events/mod.rs

+24
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ use crate::{
1717
Error,
1818
};
1919

20+
#[cfg(feature = "introspection")]
21+
use crate::stats::ClientPerfStats;
22+
2023
/// The log event severity
2124
#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
2225
pub enum LogSeverity {
@@ -98,6 +101,20 @@ where
98101
/// [`PhantomData`]
99102
phantom: PhantomData<I>,
100103
},
104+
/// New stats with performance stats.
105+
#[cfg(feature = "introspection")]
106+
UpdatePerfStats {
107+
/// The time of generation of the event
108+
time: Duration,
109+
110+
/// The executions of this client
111+
executions: usize,
112+
113+
/// Current performance statistics
114+
introspection_stats: Box<ClientPerfStats>,
115+
116+
phantom: PhantomData<I>,
117+
},
101118
/// A new objective was found
102119
Objective {
103120
/// Objective corpus size
@@ -138,6 +155,13 @@ where
138155
executions: _,
139156
phantom: _,
140157
} => "Stats",
158+
#[cfg(feature = "introspection")]
159+
Event::UpdatePerfStats {
160+
time: _,
161+
executions: _,
162+
introspection_stats: _,
163+
phantom: _,
164+
} => "PerfStats",
141165
Event::Objective { objective_size: _ } => "Objective",
142166
Event::Log {
143167
severity_level: _,

libafl/src/events/simple.rs

+13
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,19 @@ where
106106
stats.display(event.name().to_string());
107107
Ok(BrokerEventResult::Handled)
108108
}
109+
#[cfg(feature = "introspection")]
110+
Event::UpdatePerfStats {
111+
time,
112+
executions,
113+
introspection_stats,
114+
phantom: _,
115+
} => {
116+
// TODO: The stats buffer should be added on client add.
117+
stats.client_stats_mut()[0].update_executions(*executions as u64, *time);
118+
stats.client_stats_mut()[0].update_introspection_stats(**introspection_stats);
119+
stats.display(event.name().to_string());
120+
Ok(BrokerEventResult::Handled)
121+
}
109122
Event::Objective { objective_size } => {
110123
stats
111124
.client_stats_mut_for(0)

0 commit comments

Comments
 (0)