Remove {update,clear}_hash from ObserverWithHashField, add hasher (extending #1019) (#1028)

* libafl: Remove `{update,clear}_hash` from `ObserverWithHashField`

These methods aren't used by `NewHashFeedback`, so there's no compelling reason
to keep them in the interface. They preclude implementations of
`ObserverWithHashField` that calculcate a hash on-the-fly from a value. For
example, my use-case is to store the stdout of a process, and use
`NewHashFeedback` to only collect inputs that result in new messages on stdout.

Both of these methods are pretty suspicious to begin with - why should other
code be able to update the internal state of the observer? What are the
semantics of `update_hash`? If there are compelling reasons to keep these
methods, let's clarify their intent in the documentation.

* libafl: Return hash by value from `ObserverWithHashField`

This allows implementors of this trait to not store the hash, but rather to
compute it on-the-fly. Since `Option<u64>` is `Copy` (and quite small), and
this method is called once per execution of the target program, this is likely
to have negligible performance impact.

* libafl: Implement `ObserverWithHashField` for `ValueObserver`

This demonstrates the utility of the previous two commits. Now, `ValueObserver`
can be used with `NewHashFeedback`.

* Clippy, move to ahasher

* Oops :)

---------

Co-authored-by: Langston Barrett <langston.barrett@gmail.com>
This commit is contained in:
Dominik Maier 2023-01-31 10:45:42 +01:00 committed by GitHub
parent fdf579bcd5
commit cc53da85fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 35 additions and 27 deletions

View File

@ -593,6 +593,7 @@ mod apple {
} }
#[cfg(target_arch = "aarch64")] #[cfg(target_arch = "aarch64")]
#[allow(clippy::unnecessary_wraps)]
pub fn set_for_current(_core_id: CoreId) -> Result<(), Error> { pub fn set_for_current(_core_id: CoreId) -> Result<(), Error> {
// This is the best we can do, unlike on intel architecture // This is the best we can do, unlike on intel architecture
// the system does not allow to pin a process/thread to specific cpu. // the system does not allow to pin a process/thread to specific cpu.

View File

@ -109,7 +109,7 @@ where
match observer.hash() { match observer.hash() {
Some(hash) => { Some(hash) => {
let res = backtrace_state let res = backtrace_state
.update_hash_set(*hash) .update_hash_set(hash)
.expect("Failed to update the hash state"); .expect("Failed to update the hash state");
Ok(res) Ok(res)
} }

View File

@ -280,11 +280,7 @@ where
/// A trait for [`Observer`]`s` with a hash field /// A trait for [`Observer`]`s` with a hash field
pub trait ObserverWithHashField { pub trait ObserverWithHashField {
/// get the value of the hash field /// get the value of the hash field
fn hash(&self) -> &Option<u64>; fn hash(&self) -> Option<u64>;
/// update the hash field with the given value
fn update_hash(&mut self, hash: u64);
/// clears the current value of the hash and sets it to None
fn clear_hash(&mut self);
} }
/// A trait for [`Observer`]`s` which observe over differential execution. /// A trait for [`Observer`]`s` which observe over differential execution.

View File

@ -77,26 +77,26 @@ impl<'a> BacktraceObserver<'a> {
harness_type, harness_type,
} }
} }
}
impl<'a> ObserverWithHashField for BacktraceObserver<'a> {
/// Gets the hash value of this observer.
#[must_use]
fn hash(&self) -> &Option<u64> {
self.hash.as_ref()
}
/// Updates the hash value of this observer. /// Updates the hash value of this observer.
fn update_hash(&mut self, hash: u64) { fn update_hash(&mut self, hash: u64) {
*self.hash.as_mut() = Some(hash); *self.hash.as_mut() = Some(hash);
} }
/// Clears the current hash value /// Clears the current hash value (sets it to `None`)
fn clear_hash(&mut self) { fn clear_hash(&mut self) {
*self.hash.as_mut() = None; *self.hash.as_mut() = None;
} }
} }
impl<'a> ObserverWithHashField for BacktraceObserver<'a> {
/// Gets the hash value of this observer.
#[must_use]
fn hash(&self) -> Option<u64> {
*self.hash.as_ref()
}
}
impl<'a, S> Observer<S> for BacktraceObserver<'a> impl<'a, S> Observer<S> for BacktraceObserver<'a>
where where
S: UsesInput, S: UsesInput,
@ -219,23 +219,18 @@ impl AsanBacktraceObserver {
}); });
self.update_hash(hash); self.update_hash(hash);
} }
}
impl ObserverWithHashField for AsanBacktraceObserver {
/// Gets the hash value of this observer.
#[must_use]
fn hash(&self) -> &Option<u64> {
&self.hash
}
/// Updates the hash value of this observer. /// Updates the hash value of this observer.
fn update_hash(&mut self, hash: u64) { fn update_hash(&mut self, hash: u64) {
self.hash = Some(hash); self.hash = Some(hash);
} }
}
/// Clears the current hash value impl ObserverWithHashField for AsanBacktraceObserver {
fn clear_hash(&mut self) { /// Gets the hash value of this observer.
self.hash = None; #[must_use]
fn hash(&self) -> Option<u64> {
self.hash
} }
} }

View File

@ -4,14 +4,19 @@ use alloc::{
boxed::Box, boxed::Box,
string::{String, ToString}, string::{String, ToString},
}; };
use core::fmt::Debug; use core::{
fmt::Debug,
hash::{BuildHasher, Hash, Hasher},
};
use ahash::RandomState;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use super::Observer; use super::Observer;
use crate::{ use crate::{
bolts::{ownedref::OwnedRef, tuples::Named}, bolts::{ownedref::OwnedRef, tuples::Named},
inputs::UsesInput, inputs::UsesInput,
observers::ObserverWithHashField,
Error, Error,
}; };
@ -88,3 +93,14 @@ where
&self.name &self.name
} }
} }
impl<'a, T: Hash> ObserverWithHashField for ValueObserver<'a, T>
where
T: Debug + Serialize + serde::de::DeserializeOwned,
{
fn hash(&self) -> Option<u64> {
let mut s = RandomState::with_seeds(1, 2, 3, 4).build_hasher();
Hash::hash(self.value.as_ref(), &mut s);
Some(s.finish())
}
}