blob: 1f8836feb70d76b6c0e51c9752b0ee77cd71bbb1 [file] [edit]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub mod arbitrary;
pub mod containers;
pub mod range;
pub mod tuple_of;
pub mod utility;
use ::serde::de::DeserializeOwned;
use ::serde::Serialize;
use anyhow;
use anyhow::Context;
use rand::RngExt;
use std::any::Any;
use std::fmt;
pub trait CloneAny: Any {
fn clone_box(&self) -> Box<dyn CloneAny>;
fn as_any(&self) -> &dyn Any;
fn as_mut_any(&mut self) -> &mut dyn Any;
}
impl dyn CloneAny {
/// Returns `true` if the underlying type is of type `T`
pub fn is<T: Any>(&self) -> bool {
self.as_any().is::<T>()
}
/// Returns a reference to the concrete value if it is of type `T`, or `None` otherwise.
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
self.as_any().downcast_ref::<T>()
}
/// Returns a mutable reference to the concrete value if it is of type `T`, or `None` otherwise.
pub fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
self.as_mut_any().downcast_mut::<T>()
}
}
impl<T: Any + Clone> CloneAny for T {
fn clone_box(&self) -> Box<dyn CloneAny> {
// Prevent infinite recursion if T is a nested Box type.
if let Some(boxed) = self.as_any().downcast_ref::<Box<dyn CloneAny>>() {
return (**boxed).clone_box();
}
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_mut_any(&mut self) -> &mut dyn Any {
self
}
}
impl Clone for Box<dyn CloneAny> {
fn clone(&self) -> Self {
self.clone_box()
}
}
/// Type alias for a type-erased corpus value.
pub type GenericCorpusValue = Box<dyn CloneAny>;
/// Type alias for a type-erased user value.
pub type GenericUserValue = Box<dyn CloneAny>;
/// A trait for types that represent a set of values that an input can take.
///
/// Domain types are used to represent the domain of values that a fuzz test input can take
/// throughout the fuzzing process.
/// Domain types are used by the fuzzing engine through the APIs defined in this trait.
///
/// ## Serialization/Deserialization of CorpusValue and UserValue relationship.
///
/// The values drawn from a domain are always of type `CorpusValue`. These values are the
/// internal representation of the value and they are not directly usable by the fuzz property
/// function.
///
/// The `get_user_value` method is used to retrieve the user value from the corpus value. For
/// example, if the domain outputs an `&str` then the CorpusValue could be a `String` and the
/// `get_user_value` method would be used to retrieve the `&str` from the `String`.
///
/// The `parse_corpus` (resp. `serialize_corpus`) method is used deserialize (resp. serialize)
/// the corpus value from (to) a slice of bytes (resp. a vector of bytes).
///
/// ### Relationship diagram
///
/// The methods below are responsible for transforming between `UserValue`, `CorpusValue`, and the
/// serialized representation of `CorpusValue`. Here's a quick overview:
///
/// ```text
/// +-- get_user_value() <---+ +-- parse_corpus() <---+
/// | | | |
/// v | v |
/// UserValue<'a> CorpusValue &[u8]
/// | ^
/// | |
/// +-> serialize_corpus() +
/// ```
pub trait Domain {
/// The type of the values that the domain outputs. This should of the same type as the
/// parameter of the fuzz property function.
type UserValue<'user>;
/// The type of the corpus from which the values of type UserValue are drawn.
/// For example, if the domain should output an `&str` then the UserValue should be `&str` and
/// the CorpusValue could the owned data structured that the `&str` points to (eg: String).
/// The CorpusValue type should implement `serde::Serialize`, `serde::de::DeserializeOwned` and
/// `Clone`.
type CorpusValue: Serialize + DeserializeOwned + Clone;
/// Initializes a new value drawn from the domain.
fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<Self::CorpusValue>;
/// Mutates the value in `val` to a new value drawn from the domain.
///
/// If `only_shrink` is `true`, then the mutation must not increase the size of the corpus
/// value. Otherwise, the mutation can both shrink and grow the corpus value.
fn mutate(
&mut self,
val: &mut Self::CorpusValue,
rng: &mut dyn rand::Rng,
only_shrink: bool,
) -> anyhow::Result<()>;
/// Retrieves a UserValue from a given CorpusValue.
///
/// This is used to convert the corpus value into the user value that can then be passed to the
/// fuzz property function.
fn get_user_value<'a>(
&self,
corpus_value: &'a Self::CorpusValue,
) -> anyhow::Result<Self::UserValue<'a>>;
/// Turns a slice of bytes into `CorpusValue`.
///
/// By default, it uses Postcard to deserialize the data.
fn parse_corpus(&self, data: &[u8]) -> anyhow::Result<Self::CorpusValue> {
postcard::from_bytes(data).context("Failed to deserialize corpus value from bytes")
}
/// Serializes `corpus_value` to a Vec of bytes (ie, Vec<u8>).
///
/// By default, it uses Postcard to serialize the data.
fn serialize_corpus(&self, corpus_value: &Self::CorpusValue) -> anyhow::Result<Vec<u8>> {
postcard::to_stdvec(corpus_value).context("Failed to serialize corpus value to bytes")
}
/// Converts a user value to a corpus value.
#[allow(clippy::wrong_self_convention)]
fn from_value(&self, value: Self::UserValue<'_>) -> anyhow::Result<Self::CorpusValue>;
/// Validates that a corpus value satisfies the domain's constraints.
fn validate_corpus_value(&self, _corpus_value: &Self::CorpusValue) -> anyhow::Result<()> {
Ok(())
}
}
/// Helper struct that stores seeds and optional lazy seed provider for a domain.
pub struct DomainSeeds<C> {
seeds: Vec<C>,
seed_provider: Option<Box<dyn FnOnce() -> Vec<C> + Send + Sync>>,
}
impl<C> Default for DomainSeeds<C> {
fn default() -> Self {
Self {
seeds: Vec::new(),
seed_provider: None,
}
}
}
impl<C> DomainSeeds<C> {
pub fn new() -> Self {
Self::default()
}
}
impl<C: Clone> Clone for DomainSeeds<C> {
fn clone(&self) -> Self {
if self.seed_provider.is_some() {
panic!("DomainSeeds with a seed provider cannot be cloned before initialization");
}
Self {
seeds: self.seeds.clone(),
seed_provider: None,
}
}
}
impl<C: Clone + fmt::Debug> fmt::Debug for DomainSeeds<C> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DomainSeeds")
.field("seeds", &self.seeds)
.field("has_seed_provider", &self.seed_provider.is_some())
.finish()
}
}
impl<C: Clone> DomainSeeds<C> {
pub fn extend_seeds(&mut self, seeds: impl IntoIterator<Item = C>) {
self.seeds.extend(seeds);
}
pub fn set_provider<F>(&mut self, provider: F)
where
F: FnOnce() -> Vec<C> + Send + Sync + 'static,
{
self.seed_provider = Some(Box::new(provider));
}
/// Evaluates lazy seed provider (if present) and returns a random seed with 50% probability.
pub fn sample(&mut self, rng: &mut dyn rand::Rng) -> Option<C> {
if let Some(seed_provider) = self.seed_provider.take() {
self.extend_seeds(seed_provider());
}
if self.seeds.is_empty() || !rng.random_bool(0.5) {
None
} else {
let idx = rng.random_range(0..self.seeds.len());
Some(self.seeds[idx].clone())
}
}
}
pub trait SeedableDomain: Domain + Sized {
/// Mutable accessor to the domain's seed storage.
fn seeds_mut(&mut self) -> &mut DomainSeeds<Self::CorpusValue>;
/// Adds pre-defined seeds. Panics if any seed is invalid for this domain.
fn with_seeds(mut self, seeds: impl IntoIterator<Item = impl Into<Self::CorpusValue>>) -> Self {
for seed in seeds {
let corpus_val = seed.into();
if let Err(e) = self.validate_corpus_value(&corpus_val) {
panic!("Invalid seed value for domain: {e:?}");
}
self.seeds_mut().extend_seeds([corpus_val]);
}
self
}
/// Non-panicking version for programmatic usage.
fn try_with_seeds(
mut self,
seeds: impl IntoIterator<Item = impl Into<Self::CorpusValue>>,
) -> anyhow::Result<Self> {
for seed in seeds {
let corpus_val = seed.into();
self.validate_corpus_value(&corpus_val)?;
self.seeds_mut().extend_seeds([corpus_val]);
}
Ok(self)
}
/// Adds a lazy seed provider evaluated on first sampling.
fn with_seed_provider<F, I, S>(mut self, seed_provider: F) -> Self
where
F: FnOnce() -> I + Send + Sync + 'static,
I: IntoIterator<Item = S>,
Self::CorpusValue: From<S>,
{
self.seeds_mut().set_provider(move || {
seed_provider().into_iter().map(Self::CorpusValue::from).collect()
});
self
}
}
/// A type-erased interface for Domain types.
///
/// This trait is used to expose a common interface for Domain types to the fuzzing engine through
/// the `FuzzTest` trait.
/// The methods of this trait are similar to that of the `Domain` trait, but they operate on
/// a type-erased `GenericCorpusValue` instead of an explicit type.
pub trait GenericDomain {
/// Initializes a new value drawn from the domain.
///
/// See `Domain::init` for more details.
fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<GenericCorpusValue>;
/// Mutates the value in `val` to a new value drawn from the domain.
///
/// See `Domain::mutate` for more details.
fn mutate(
&mut self,
val: &mut GenericCorpusValue,
rng: &mut dyn rand::Rng,
only_shrink: bool,
) -> anyhow::Result<()>;
/// Parses a slice of bytes into a `GenericCorpusValue`.
///
/// See `Domain::parse_corpus` for more details.
fn parse_corpus(&self, data: &[u8]) -> anyhow::Result<GenericCorpusValue>;
/// Serializes a `GenericCorpusValue` to a Vec of bytes (ie, Vec<u8>).
///
/// See `Domain::serialize_corpus` for more details.
fn serialize_corpus(&self, val: &GenericCorpusValue) -> anyhow::Result<Vec<u8>>;
}
/// Blanket implementation of the `GenericDomain` trait for types implementing the `Domain` trait.
///
/// This implementation is a simple pass-through implementation that uses the methods of the
/// `Domain` trait to implement the `GenericDomain` trait.
impl<D> GenericDomain for D
where
D: Domain,
D::CorpusValue: 'static,
{
fn init(&mut self, rng: &mut dyn rand::Rng) -> anyhow::Result<GenericCorpusValue> {
Ok(Box::new(self.init(rng)?))
}
/// Retrieves the underlying `Domain`'s `CorpusValue` from the `GenericCorpusValue` and calls
/// the underlying `Domain::mutate` method.
///
/// If the `GenericCorpusValue` cannot be downcasted to the underlying `Domain`'s `CorpusValue`
/// then an Error is returned.
///
/// See `GenericDomain::mutate` for more details.
fn mutate(
&mut self,
val: &mut GenericCorpusValue,
rng: &mut dyn rand::Rng,
only_shrink: bool,
) -> anyhow::Result<()> {
self.mutate(
val.downcast_mut().context("Failed to retrieve the Corpus Value")?,
rng,
only_shrink,
)
}
/// Calls the underlying `Domain::parse_corpus` method with the `data` and wraps the resulting
/// `CorpusValue` in a `GenericCorpusValue`.
///
/// See `GenericDomain::parse_corpus` for more details.
fn parse_corpus(&self, data: &[u8]) -> anyhow::Result<GenericCorpusValue> {
Ok(Box::new(self.parse_corpus(data)?))
}
/// Retrieves the underlying `Domain`'s `CorpusValue` from the `GenericCorpusValue` and calls
/// the underlying `Domain::serialize_corpus` method.
///
/// If the `GenericCorpusValue` cannot be downcasted to the underlying `Domain`'s `CorpusValue`
/// then an Error is returned.
///
/// See `GenericDomain::serialize_corpus` for more details.
fn serialize_corpus(&self, val: &GenericCorpusValue) -> anyhow::Result<Vec<u8>> {
self.serialize_corpus(val.downcast_ref().context("Failed to retrieve the Corpus Value")?)
}
}