// SafeC Standard Library — Fuzz Harness
#pragma once
#include <std/stddef.h>
#include <std/test/fuzz.h>
namespace std {
extern unsigned char* malloc(size_t size);
extern void free(unsigned char* ptr);
extern unsigned char* memcpy(unsigned char* d, const unsigned char* s, size_t n);
// ── XorShift64 PRNG ───────────────────────────────────────────────────────────
static unsigned long fuzz_rng_(unsigned long* state) {
unsafe {
unsigned long x = *state;
x = x ^ (x << (unsigned long)13);
x = x ^ (x >> (unsigned long)7);
x = x ^ (x << (unsigned long)17);
*state = x;
return x;
}
}
struct FuzzTarget FuzzTarget::new(fn void([]const unsigned char) func, unsigned long iters) {
struct FuzzTarget t;
t.func = func;
t.seed = (unsigned long)0xDEADBEEFCAFEBABE;
t.iters = iters;
return t;
}
void FuzzTarget::run([]const unsigned char corpus_s) {
unsigned long corpus_size = corpus_s.len;
const &unsigned char corpus;
unsafe { corpus = (const &unsigned char)corpus_s.ptr; }
if (corpus_size == (unsigned long)0) { return; }
unsigned long state = self.seed;
// Working buffer: start as a copy of the corpus.
unsafe {
unsigned char* buf = (unsigned char*)malloc(corpus_size);
if (buf == (unsigned char*)0) { return; }
memcpy((unsigned char*)buf, (const unsigned char*)corpus, corpus_size);
// Call target on the original corpus first.
fn void([]const unsigned char) fuzz_fn = self.func;
fuzz_fn(((const unsigned char*)buf)[0..corpus_size]);
unsigned long i = (unsigned long)0;
while (i < self.iters) {
// Pick a random mutation: bit flip, byte set, truncate, or extend.
unsigned long r = fuzz_rng_((unsigned long*)&state);
unsigned long mut = r & (unsigned long)3;
unsigned long pos = fuzz_rng_((unsigned long*)&state) % corpus_size;
if (mut == (unsigned long)0) {
// Bit flip at random position.
unsigned long bit = fuzz_rng_((unsigned long*)&state) & (unsigned long)7;
buf[pos] = buf[pos] ^ (unsigned char)(1 << bit);
} else if (mut == (unsigned long)1) {
// Random byte set.
buf[pos] = (unsigned char)(fuzz_rng_((unsigned long*)&state) & (unsigned long)0xFF);
} else if (mut == (unsigned long)2) {
// Restore to original corpus.
memcpy((unsigned char*)buf, (const unsigned char*)corpus, corpus_size);
} else {
// Overwrite two adjacent bytes.
buf[pos] = (unsigned char)(fuzz_rng_((unsigned long*)&state) & (unsigned long)0xFF);
unsigned long pos2 = (pos + (unsigned long)1) % corpus_size;
buf[pos2] = (unsigned char)(fuzz_rng_((unsigned long*)&state) & (unsigned long)0xFF);
}
// Vary the size: sometimes pass a prefix, sometimes full buffer.
unsigned long sz = corpus_size;
if ((fuzz_rng_((unsigned long*)&state) & (unsigned long)3) == (unsigned long)0) {
sz = (fuzz_rng_((unsigned long*)&state) % corpus_size) + (unsigned long)1;
}
fuzz_fn(((const unsigned char*)buf)[0..sz]);
i = i + (unsigned long)1;
}
free((unsigned char*)buf);
}
}
} // namespace std