1#[cfg(feature = "casr")]
4use alloc::string::ToString;
5use alloc::{borrow::Cow, string::String, vec::Vec};
6use core::fmt::Debug;
7#[cfg(feature = "casr")]
8use core::hash::{Hash, Hasher};
9#[cfg(feature = "casr")]
10use std::collections::hash_map::DefaultHasher;
11use std::{
12 fs::{self, File},
13 io::Read,
14 path::Path,
15 process::ChildStderr,
16};
17
18use backtrace::Backtrace;
19use libafl_bolts::{Named, ownedref::OwnedRefMut};
20#[allow(unused_imports)] #[cfg(feature = "casr")]
22use libcasr::{
23 asan::AsanStacktrace,
24 constants::{
25 STACK_FRAME_FILEPATH_IGNORE_REGEXES_CPP, STACK_FRAME_FILEPATH_IGNORE_REGEXES_GO,
26 STACK_FRAME_FILEPATH_IGNORE_REGEXES_JAVA, STACK_FRAME_FILEPATH_IGNORE_REGEXES_PYTHON,
27 STACK_FRAME_FILEPATH_IGNORE_REGEXES_RUST, STACK_FRAME_FUNCTION_IGNORE_REGEXES_CPP,
28 STACK_FRAME_FUNCTION_IGNORE_REGEXES_GO, STACK_FRAME_FUNCTION_IGNORE_REGEXES_JAVA,
29 STACK_FRAME_FUNCTION_IGNORE_REGEXES_PYTHON, STACK_FRAME_FUNCTION_IGNORE_REGEXES_RUST,
30 },
31 init_ignored_frames,
32 stacktrace::{
33 Filter, ParseStacktrace, STACK_FRAME_FILEPATH_IGNORE_REGEXES,
34 STACK_FRAME_FUNCTION_IGNORE_REGEXES, Stacktrace, StacktraceEntry,
35 },
36};
37#[cfg(not(feature = "casr"))]
38use regex::Regex;
39use serde::{Deserialize, Serialize};
40
41use super::ObserverWithHashField;
42use crate::{Error, executors::ExitKind, observers::Observer};
43
44#[cfg(not(feature = "casr"))]
45#[must_use]
48pub fn collect_backtrace() -> u64 {
49 let b = Backtrace::new_unresolved();
50 if b.frames().is_empty() {
51 return 0;
52 }
53 let mut hash = 0;
54 for frame in &b.frames()[1..] {
55 hash ^= frame.ip() as u64;
56 }
57 hash
66}
67
68#[cfg(feature = "casr")]
69#[must_use]
71pub fn collect_backtrace() -> u64 {
72 let mut b = Backtrace::new_unresolved();
73 if b.frames().is_empty() {
74 return 0;
75 }
76 b.resolve();
77 let mut strace = Stacktrace::new();
78 for frame in &b.frames()[1..] {
79 let mut strace_entry = StacktraceEntry::default();
80 let symbols = frame.symbols();
81 if symbols.len() > 1 {
82 let symbol = &symbols[0];
83 if let Some(name) = symbol.name() {
84 strace_entry.function = name.as_str().map_or_else(String::new, str::to_string);
85 }
86 if let Some(file) = symbol.filename() {
87 strace_entry.debug.file = file.to_string_lossy().to_string();
88 }
89 strace_entry.debug.line = u64::from(symbol.lineno().unwrap_or(0));
90 strace_entry.debug.column = u64::from(symbol.colno().unwrap_or(0));
91 }
92 strace_entry.address = frame.ip() as u64;
93 strace.push(strace_entry);
94 }
95
96 strace.filter();
97 let mut s = DefaultHasher::new();
98 strace.hash(&mut s);
99 s.finish()
100}
101
102#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
104pub enum HarnessType {
105 InProcess,
107 Child,
109 External,
111}
112
113#[derive(Serialize, Deserialize, Debug)]
115pub struct BacktraceObserver<'a> {
116 observer_name: Cow<'static, str>,
117 hash: OwnedRefMut<'a, Option<u64>>,
118 harness_type: HarnessType,
119}
120
121impl<'a> BacktraceObserver<'a> {
122 #[cfg(not(feature = "casr"))]
123 #[must_use]
125 pub fn new<S>(
126 observer_name: S,
127 backtrace_hash: OwnedRefMut<'a, Option<u64>>,
128 harness_type: HarnessType,
129 ) -> Self
130 where
131 S: Into<Cow<'static, str>>,
132 {
133 Self {
134 observer_name: observer_name.into(),
135 hash: backtrace_hash,
136 harness_type,
137 }
138 }
139
140 #[cfg(feature = "casr")]
141 #[must_use]
143 pub fn new<S>(
144 observer_name: S,
145 backtrace_hash: OwnedRefMut<'a, Option<u64>>,
146 harness_type: HarnessType,
147 ) -> Self
148 where
149 S: Into<Cow<'static, str>>,
150 {
151 init_ignored_frames!("rust", "cpp", "go");
152 Self {
153 observer_name: observer_name.into(),
154 hash: backtrace_hash,
155 harness_type,
156 }
157 }
158
159 #[must_use]
161 pub fn owned<S>(observer_name: S, harness_type: HarnessType) -> Self
162 where
163 S: Into<Cow<'static, str>>,
164 {
165 Self::new(observer_name, OwnedRefMut::owned(None), harness_type)
166 }
167
168 fn update_hash(&mut self, hash: u64) {
170 *self.hash.as_mut() = Some(hash);
171 }
172
173 fn clear_hash(&mut self) {
175 *self.hash.as_mut() = None;
176 }
177
178 pub fn fill_external(&mut self, hash: u64, exit_kind: &ExitKind) {
180 if self.harness_type == HarnessType::External {
181 if *exit_kind == ExitKind::Crash {
182 self.update_hash(hash);
183 } else {
184 self.clear_hash();
185 }
186 }
187 }
188}
189
190impl ObserverWithHashField for BacktraceObserver<'_> {
191 fn hash(&self) -> Option<u64> {
193 *self.hash.as_ref()
194 }
195}
196
197impl<I, S> Observer<I, S> for BacktraceObserver<'_> {
198 fn post_exec(&mut self, _state: &mut S, _input: &I, exit_kind: &ExitKind) -> Result<(), Error> {
199 if self.harness_type == HarnessType::InProcess {
200 if *exit_kind == ExitKind::Crash {
201 self.update_hash(collect_backtrace());
202 } else {
203 self.clear_hash();
204 }
205 }
206 Ok(())
207 }
208
209 fn post_exec_child(
210 &mut self,
211 state: &mut S,
212 input: &I,
213 exit_kind: &ExitKind,
214 ) -> Result<(), Error> {
215 self.post_exec(state, input, exit_kind)
216 }
217}
218
219impl Named for BacktraceObserver<'_> {
220 fn name(&self) -> &Cow<'static, str> {
221 &self.observer_name
222 }
223}
224
225pub static ASAN_LOG_PATH: &str = "./asanlog"; #[must_use]
230pub fn get_asan_runtime_flags_with_log_path() -> String {
231 let mut flags = get_asan_runtime_flags();
232 flags.push_str(":log_path=");
233 flags.push_str(ASAN_LOG_PATH);
234 flags
235}
236
237#[must_use]
239pub fn get_asan_runtime_flags() -> String {
240 let flags = [
241 "exitcode=0",
242 "abort_on_error=1",
243 "handle_abort=1",
244 "handle_segv=1",
245 "handle_sigbus=1",
246 "handle_sigill=1",
247 "handle_sigfpe=1",
248 ];
249
250 flags.join(":")
251}
252
253#[derive(Serialize, Deserialize, Debug, Clone)]
255pub struct AsanBacktraceObserver {
256 observer_name: Cow<'static, str>,
257 hash: Option<u64>,
258}
259
260impl AsanBacktraceObserver {
261 #[cfg(not(feature = "casr"))]
262 #[must_use]
264 pub fn new<S>(observer_name: S) -> Self
265 where
266 S: Into<Cow<'static, str>>,
267 {
268 Self {
269 observer_name: observer_name.into(),
270 hash: None,
271 }
272 }
273
274 #[cfg(feature = "casr")]
275 #[must_use]
277 pub fn new<S>(observer_name: S) -> Self
278 where
279 S: Into<Cow<'static, str>>,
280 {
281 init_ignored_frames!("rust", "cpp", "go");
282 Self {
283 observer_name: observer_name.into(),
284 hash: None,
285 }
286 }
287
288 pub fn parse_asan_output_from_childstderr(
290 &mut self,
291 stderr: &mut ChildStderr,
292 ) -> Result<(), Error> {
293 let mut buf = Vec::new();
294 stderr.read_to_end(&mut buf)?;
295 self.parse_asan_output(&String::from_utf8_lossy(&buf));
296 Ok(())
297 }
298
299 pub fn parse_asan_output_from_asan_log_file(&mut self, pid: i32) -> Result<(), Error> {
301 let log_path = format!("{ASAN_LOG_PATH}.{pid}");
302 let mut asan_output = File::open(Path::new(&log_path))?;
303
304 let mut buf = String::new();
305 asan_output.read_to_string(&mut buf)?;
306 fs::remove_file(&log_path)?;
307
308 self.parse_asan_output(&buf);
309 Ok(())
310 }
311
312 #[cfg(not(feature = "casr"))]
313 pub fn parse_asan_output(&mut self, output: &str) {
315 let mut hash = 0;
316 let matcher = Regex::new("\\s*#[0-9]*\\s0x([0-9a-f]*)\\s.*").unwrap();
317 matcher.captures_iter(output).for_each(|m| {
318 let g = m.get(1).unwrap();
319 hash ^= u64::from_str_radix(g.as_str(), 16).unwrap();
320 });
321 self.update_hash(hash);
322 }
323
324 #[cfg(feature = "casr")]
325 pub fn parse_asan_output(&mut self, output: &str) {
327 let mut hash = 0;
328 if let Ok(st_vec) = AsanStacktrace::extract_stacktrace(output) {
329 if let Ok(mut stacktrace) = AsanStacktrace::parse_stacktrace(&st_vec) {
330 stacktrace.filter();
331 let mut s = DefaultHasher::new();
332 stacktrace.hash(&mut s);
333 hash = s.finish();
334 }
335 }
336 self.update_hash(hash);
337 }
338
339 fn update_hash(&mut self, hash: u64) {
341 self.hash = Some(hash);
342 }
343}
344
345impl ObserverWithHashField for AsanBacktraceObserver {
346 fn hash(&self) -> Option<u64> {
348 self.hash
349 }
350}
351
352impl Default for AsanBacktraceObserver {
353 fn default() -> Self {
354 Self::new("AsanBacktraceObserver")
355 }
356}
357
358impl<I, S> Observer<I, S> for AsanBacktraceObserver {}
359
360impl Named for AsanBacktraceObserver {
361 fn name(&self) -> &Cow<'static, str> {
362 &self.observer_name
363 }
364}