Hi, I'd like to propose adding fuzzing capabilities into C3 language. I made some preliminary research and POC that confirms that LLVM libFuzzer is viable with C3. I will post a WIP PR for this, see #3506.
But currently I'd like to discuss the bigger picture and benefits of native fuzzing can bring on the table in C3.
Rationale
I think fuzzing capabilities is a must for system languages, especially for those who allow unsafe operations with pointers, system libs, and other C code. C3 standard library can get a lot of edge case coverage especially when dealing with user input processing: regex, JSON/xml, and other file formats.
With that said, LLVM provides tools and capabilities out of the box, it looks like the fuzzing integration is relatively straightforward and requires small changes. I made a throwaway AI driven prototype that confirms that fuzzing is possible in C3, but I can definitely miss some knock-on effects of it. So I'd like to start discussion about fuzzing here.
Levels of fuzzing support
Stage 1: Bare minimum
Just implementing a compiler flag than enables fuzzers (same path as sanitizers): c3c compile --sanitize=fuzzer --no-entry fuzz_target.c3 -o fuzz
This flag can allows user to run hackish fuzzing of the c3 programs, like this:
module fuzz_target;
extern fn void abort() @cname("abort");
// Called by libFuzzer for every input. Returns 0 on success; a crash or
// abort() reports a finding.
fn int llvm_fuzzer_test_one_input(char* data, usz size) @export("LLVMFuzzerTestOneInput")
{
// Protocol: [0..2] = "GET", [3] = '/', [4] = body length, [5..] = body bytes.
if (size < 5) return 0;
if (data[0] != 'G' || data[1] != 'E' || data[2] != 'T') return 0;
if (data[3] != '/') return 0;
usz len = (usz)data[4];
if (size >= 5 + len) return 0; // body complete: input is fine.
// Planted bug: truncated body accepted. In real code this would be an
// out-of-bounds read; here it is simulated so the fuzzer terminates
// with a reportable crash.
abort();
}
c3c compile --sanitize=fuzzer --cc=clang --no-entry fuzz_target.c3 -o fuzz
mkdir corpus
printf 'GET /abc\n' > corpus/seed1
printf 'hello\n' > corpus/seed2
./fuzz -max_total_time=30 ./corpus # coverage-guided run, flags provided by libFuzzer
./fuzz <crash-file> # replay a finding, one shot, exact what triggered crash
Stage 2: Fuzz runner
Implementing machinery for running fuzz testing similar to unit tests and benchmarks. It could be simplified command that prepares fuzz binary and fuzz corpus.
CLI examples:
c3c fuzz test/fuzz/fuzz_foo.c3 '<libFuzzer args>' # runs individual fuzz app indefinitely
c3c fuzz # runs full fuzz suite, with time limits, say 1 min per test
Follow up notes and ideas
- LibFuzzer ships its own
main() function, so the main convention there that each fuzz case has to be a standalone executable. So the C3 program should be compiled with --no-entry.
- Not all platform support fuzzing, but mainly Linux/Windows/MacOS do
- LLVM libFuzzer uses branch tracking for testing all function code paths. It's not just randomly generated input, it's deliberately shaped input.
- Fuzz data is not just a random stream of bytes, but it can be used as source of deterministic randomization. Which can open a whole new page of deterministic simulation testing in C3.
- Unlike the AFL++ fuzzer, libFuzzer provides mode focused fuzzing, right into the weak spots of the project, instead end-to-end fuzzing.
Hi, I'd like to propose adding fuzzing capabilities into C3 language. I made some preliminary research and POC that confirms that LLVM libFuzzer is viable with C3. I will post a WIP PR for this, see #3506.
But currently I'd like to discuss the bigger picture and benefits of native fuzzing can bring on the table in C3.
Rationale
I think fuzzing capabilities is a must for system languages, especially for those who allow unsafe operations with pointers, system libs, and other C code. C3 standard library can get a lot of edge case coverage especially when dealing with user input processing: regex, JSON/xml, and other file formats.
With that said, LLVM provides tools and capabilities out of the box, it looks like the fuzzing integration is relatively straightforward and requires small changes. I made a throwaway AI driven prototype that confirms that fuzzing is possible in C3, but I can definitely miss some knock-on effects of it. So I'd like to start discussion about fuzzing here.
Levels of fuzzing support
Stage 1: Bare minimum
Just implementing a compiler flag than enables fuzzers (same path as sanitizers):
c3c compile --sanitize=fuzzer --no-entry fuzz_target.c3 -o fuzzThis flag can allows user to run hackish fuzzing of the c3 programs, like this:
Stage 2: Fuzz runner
Implementing machinery for running fuzz testing similar to unit tests and benchmarks. It could be simplified command that prepares fuzz binary and fuzz corpus.
CLI examples:
Follow up notes and ideas
main()function, so the main convention there that each fuzz case has to be a standalone executable. So the C3 program should be compiled with--no-entry.