1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//! Detect persistent writable state between fuzzer iterations.
//!
//! Rust bindings for the [`global-state-detector`] C library. Operates
//! on writable, non-executable ELF `PT_LOAD` segments (`.data` / `.bss`)
//! of the main binary and every loaded shared object, filtering out
//! libc / ld-linux / libpthread / libstdc++ / vDSO noise.
//!
//! # Usage
//!
//! Call [`init`] once after any one-time target initialization is done.
//! Then, on each fuzzer iteration, [`rebaseline`] just before invoking
//! the target and [`check`] just after. That pattern attributes drift
//! to the target rather than to the fuzzer's own bookkeeping between
//! callbacks.
//!
//! # Required linker flags for the final binary
//!
//! Cargo does not propagate `rustc-link-arg` from rlib dependencies, so
//! consumers must arrange for the linker to receive `-rdynamic` and
//! `-Wl,-z,now` themselves. The simplest way is a `.cargo/config.toml`
//! in the consuming project (or its `fuzz/` subdirectory):
//!
//! ```toml
//! [target.'cfg(target_os = "linux")']
//! rustflags = ["-C", "link-arg=-rdynamic", "-C", "link-arg=-Wl,-z,now"]
//! ```
//!
//! Without `-rdynamic`, symbol names in the main executable are not
//! resolvable via `dladdr` and reports show `?+0x...` instead of real
//! symbols. Without `-Wl,-z,now`, lazy PLT/GOT binding produces noise
//! on the first iteration.
//!
//! # Rust-specific caveats
//!
//! * `static` and `static mut` live in `.data`/`.bss` — fully covered.
//! * `AtomicU*`, `Mutex<T>` (the lock word), `RwLock` — covered.
//! * `OnceCell`, `OnceLock`, `LazyLock`, `lazy_static!` — only the
//! pointer / discriminant in `.bss` is visible. The actual heap-
//! allocated payload is NOT tracked. You will see "this static was
//! first-used in this iteration" but not what value it took.
//! * `thread_local!` lives in TLS, not `.data`/`.bss` — not tracked
//! here. TLS is per-thread anyway, less of a fuzzing concern.
//! * Symbols come out Rust-mangled (`_ZN8...` / `_R...`). Pipe stderr
//! through `rustfilt` to demangle:
//! ./fuzzer 2>&1 | rustfilt
//! (cargo install rustfilt)
//!
//! # Thread safety
//!
//! The underlying C implementation is not thread-safe. Use it from a
//! single-threaded harness, or add external synchronization.
//!
//! [`global-state-detector`]: https://github.com/AFLplusplus/global-state-detector
use c_int;
extern "C"
/// Snapshot all writable `PT_LOAD` segments of the main binary and
/// every currently loaded shared object.
///
/// Call once after the target has been fully initialized (i.e. after
/// any one-time setup your harness performs).
/// Diff current memory against the last snapshot.
///
/// Returns the number of pages that changed. If `rebaseline` is true,
/// the snapshot is updated to the current state so the next call only
/// shows new deltas; pass `false` to keep comparing against the
/// previous snapshot for cumulative drift.
/// Force a full re-snapshot without reporting any diffs.