baryl 0.0.2

Public SDK for Baryl, a full-system emulation and introspection engine
//! Driving a fuzzing run: one case, start to end.
//!
//! Reached as `ctl.subs.fuzz`. A *harness* is a component that subscribes to
//! the `#[fuzz(...)]` events — it decides what a case means for its target:
//! `case_start` prepares the guest, `mutate` edits the bytes, `inject` plants
//! them where the target will read them, `gen_cov_hash` says what counted as
//! new behaviour, `testcase_ended` cleans up. Only the harness knows any of
//! that, so each of those slots takes exactly one owner and a second component
//! claiming one is refused at load.
//!
//! [`FuzzRef`] is the other direction — what the harness asks of the run:
//! random numbers from the seeded stream, the case's id, the two limits, and a
//! way to force the next input.
//!
//! [`Input`] is the case in flight, handed to a `#[fuzz(mutate)]` handler.

// Bindgen output cannot satisfy the workspace lints; the allow stops here.
mod generated {
    #![allow(non_camel_case_types, non_upper_case_globals, dead_code)]
    include!("generated.rs");
}
pub use generated::*;

/// A case is about to run: your state and the `Control`.
/// `#[fuzz(case_start)]` writes one for you.
pub type CaseStartCb = unsafe extern "C" fn(*mut core::ffi::c_void, *mut core::ffi::c_void);

/// Edit this case's bytes: your state, the `Control`, then the buffer, a
/// pointer to its length, and its capacity — which is what [`Input`] wraps.
/// `#[fuzz(mutate)]` writes one for you.
pub type MutateCb =
    unsafe extern "C" fn(*mut core::ffi::c_void, *mut core::ffi::c_void, *mut u8, *mut u64, u64);

/// Plant these bytes where the target will read them: your state, the
/// `Control`, and the finished case. `#[fuzz(inject)]` writes one for you.
pub type InjectCb =
    unsafe extern "C" fn(*mut core::ffi::c_void, *mut core::ffi::c_void, *const u8, u64);

/// Say what this case's behaviour hashes to, for deciding whether it was new.
/// `#[fuzz(gen_cov_hash)]` writes one for you.
pub type CovHashCb = unsafe extern "C" fn(*mut core::ffi::c_void, *mut core::ffi::c_void) -> u64;

/// The case is over: your state, the `Control`, and why it ended —
/// `BARYL_RESET_END`, `BARYL_RESET_TIMEOUT` or `BARYL_RESET_CRASH`.
/// `#[fuzz(testcase_ended)]` writes one for you.
pub type CaseEndCb = unsafe extern "C" fn(*mut core::ffi::c_void, *mut core::ffi::c_void, u32);

impl FuzzVtable {
    /// A table with no calls in it. Start from this when filling one in, so a
    /// call you did not implement reads as absent rather than as garbage.
    pub const ABSENT: FuzzVtable = FuzzVtable {
        rand: None,
        case_id: None,
        set_icount_budget: None,
        set_case_limit: None,
        set_next_input: None,
        corpus_size: None,
    };
}

/// The case in flight, borrowed for the length of one `#[fuzz(mutate)]` call.
///
/// A view over the subsystem's own scratch buffer, not a `Vec`: the bytes are
/// already there, and every edit is in place. The length may move anywhere
/// between 0 and [`cap`](Self::cap) — and nowhere above it. Everything that
/// would grow the case past `cap` clamps or drops the excess instead of
/// failing, so a mutator never has to check a size before writing.
///
/// The length is read and written *through a pointer* the subsystem also holds,
/// so a second mutator running after yours sees the length you left, not the
/// one you started with.
///
/// # Examples
///
/// ```ignore
/// #[fuzz(mutate)]
/// fn mutate(&mut self, _t: &mut Control, input: &mut Input) {
///     // Keep a fixed four-byte header and havoc the rest.
///     if input.len() < 4 {
///         input.set_len(0);
///         input.extend_from_slice(b"HDR\0");
///     }
///     havoc(input, &mut self.rng);
///
///     // Growth stops at cap; nothing errors.
///     input.set_len(u64::MAX);
///     assert_eq!(input.len(), input.cap());
/// }
/// ```
pub struct Input {
    buf: *mut u8,
    len: *mut u64,
    cap: u64,
}

impl Input {
    /// Wrap the raw triple a mutate callback is passed. `#[fuzz(mutate)]` does
    /// this for you; write it yourself only when filling a callback by hand.
    ///
    /// # Safety
    /// `buf` must be `cap` writable bytes and `len` must be live, both for the
    /// length of the call this was built for and no longer.
    pub unsafe fn new(buf: *mut u8, len: *mut u64, cap: u64) -> Input {
        Input { buf: buf, len: len, cap: cap }
    }

    /// The bytes the case holds right now.
    pub fn as_slice(&self) -> &[u8] {
        // SAFETY: the ABI's buffer is `cap` bytes and `len` never exceeds it.
        unsafe { core::slice::from_raw_parts(self.buf, self.len() as usize) }
    }

    /// The same bytes, editable in place.
    ///
    /// Only up to the current length — this does not reach the capacity beyond
    /// it. Call [`set_len`](Self::set_len) first to write into that space.
    pub fn as_mut_slice(&mut self) -> &mut [u8] {
        let len = self.len() as usize;
        // SAFETY: as `as_slice`, and the borrow is exclusive for its lifetime.
        unsafe { core::slice::from_raw_parts_mut(self.buf, len) }
    }

    /// How many bytes this case holds. Never above [`cap`](Self::cap), even if
    /// something wrote a larger length.
    pub fn len(&self) -> u64 {
        // SAFETY: `len` is live for the call this input was built for.
        unsafe { *self.len }.min(self.cap)
    }

    /// True for a zero-length case, which is a legitimate thing to run.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// The most this case can grow to. Fixed before the first mutator ran, so
    /// it does not change under you.
    pub fn cap(&self) -> u64 {
        self.cap
    }

    /// Set the length, **clamped to `cap`** — a length past the end is not an
    /// error, it is a shorter case.
    ///
    /// Bytes below the old length are untouched. Bytes between the old length
    /// and a longer new one are *not* cleared: they hold whatever the previous
    /// case left in the buffer. Write them before the case runs, or you are
    /// fuzzing with the last case's tail.
    pub fn set_len(&mut self, len: u64) {
        // SAFETY: as `len`.
        unsafe { *self.len = len.min(self.cap) };
    }

    /// Append what fits and drop the rest.
    ///
    /// Truncates rather than failing, so appending 100 bytes to a case with
    /// room for 10 leaves a case 10 bytes longer and reports nothing. Compare
    /// `len()` across the call where that matters.
    pub fn extend_from_slice(&mut self, bytes: &[u8]) {
        let at = self.len();
        let n = bytes.len().min((self.cap - at) as usize);
        // SAFETY: `at + n <= cap`, and the source cannot overlap the ABI buffer.
        unsafe { core::ptr::copy_nonoverlapping(bytes.as_ptr(), self.buf.add(at as usize), n) };
        self.set_len(at + n as u64);
    }
}

/// Every call the fuzz subsystem answers.
///
/// All of them are safe to make on a run that is not fuzzing at all: an unbound
/// handle reads as no capability, so a read answers 0 and a setter does
/// nothing. A harness can therefore be loaded into an ordinary boot without
/// guarding every line.
impl FuzzRef {
    /// The vtable, or `None` when nothing is bound to this handle.
    fn table(&self) -> Option<&FuzzVtable> {
        // SAFETY: `vtable` is the fuzz `.so`'s static table, valid for the process.
        unsafe { self.vtable.as_ref() }
    }

    /// A draw from the run's one seeded stream.
    ///
    /// Every random number a harness uses should come from here, directly or
    /// through an [`Rng`](crate::mutators::Rng) seeded from it. That is the
    /// whole of what makes a run reproducible: same seed, same bytes, same
    /// crash. Reach for the host's entropy anywhere in a case and the run stops
    /// reproducing.
    ///
    /// 0 on a run that is not fuzzing.
    pub fn rand(&self) -> u64 {
        self.table()
            .and_then(|v| v.rand)
            .map_or(0, |f| unsafe { f(*self) })
    }

    /// Which corpus entry this case was drawn from — the number to quote when
    /// reporting what found something.
    ///
    /// `u64::MAX` for a case supplied through
    /// [`set_next_input`](Self::set_next_input) rather than drawn. 0 on a run
    /// that is not fuzzing.
    pub fn case_id(&self) -> u64 {
        self.table()
            .and_then(|v| v.case_id)
            .map_or(0, |f| unsafe { f(*self) })
    }

    /// How many guest instructions one case may run before it counts as a
    /// timeout; 0 for no limit.
    ///
    /// Only the harness knows what "too long" means for its target, so nothing
    /// sets this for you — and without it a case that hangs the guest hangs the
    /// run.
    pub fn set_icount_budget(&self, insns: u64) {
        if let Some(f) = self.table().and_then(|v| v.set_icount_budget) {
            unsafe { f(*self, insns) };
        }
    }

    /// How many cases this run executes before it ends; 0 for no limit, which
    /// is a run that fuzzes until something stops it.
    pub fn set_case_limit(&self, cases: u64) {
        if let Some(f) = self.table().and_then(|v| v.set_case_limit) {
            unsafe { f(*self, cases) };
        }
    }

    /// Run exactly these bytes as the next case instead of drawing one from the
    /// corpus.
    ///
    /// No mutator runs over them, so what you pass is what executes — which is
    /// what makes this the way to reproduce a crash from a file, or to walk a
    /// target through a scripted sequence. Holds for one case only; the case
    /// after it is drawn normally.
    ///
    /// `bytes` is read during the call and not retained.
    pub fn set_next_input(&self, bytes: &[u8]) {
        if let Some(f) = self.table().and_then(|v| v.set_next_input) {
            unsafe { f(*self, bytes.as_ptr(), bytes.len() as u64) };
        }
    }

    /// How many inputs the corpus holds right now. Rises as cases find new
    /// behaviour, so it is the number to watch to see whether a run is still
    /// making progress. 0 on a run that is not fuzzing.
    pub fn corpus_size(&self) -> u64 {
        self.table()
            .and_then(|v| v.corpus_size)
            .map_or(0, |f| unsafe { f(*self) })
    }
}