2026-09-13
Everybody's heard of AFL. Fewer people know radamsa, and that's a shame. AFL is a coverage-guided fuzzer — instrument the binary, mutate inputs to reach new code paths. Powerful, but it wants a build you control. Sometimes you just want to answer: does this parser survive 10,000 slightly-broken variations of a valid input, tonight?
That's radamsa. Reads samples, emits mutants. Language-independent, format-independent, no instrumentation, no build step. If you have a valid input and a program that reads bytes, you can fuzz it. Written by OUSPG at the University of Oulu — the same folks who've been fuzzing since before it was fashionable — it found bugs in libraries most people had forgotten existed.
# Mutate stdin
echo "hello world" | radamsa
# hello worldhello wor��ld
# 100 mutants to disk, named by counter
radamsa -n 100 -o "fuzz-%n.pdf" original.pdf
# Combine multiple seed samples
radamsa a.xml b.xml c.xml > frankenstein.xml
Pipe mutants at the target until something segfaults:
ulimit -c unlimited
mkdir -p crashes
n=0
while :; do
n=$((n+1))
radamsa sample.json | ./parser > /dev/null 2>&1
rc=$?
if [ $rc -gt 127 ]; then
radamsa -s $n sample.json > "crashes/crash-$n.json"
echo "signal $((rc-128)) at iter $n"
fi
done
Exit status > 127 means killed by signal. Save the seed (-s $n) instead of the mutant and you can regenerate the exact bytes six weeks later — reproducibility that "just save the crashing input" doesn't give you when the sample set changes.
Radamsa's -o speaks TCP. Serve infinite mutants of a captured packet on a port:
radamsa -o :12345 -n inf sample.pkt
# ... then hammer localhost:12345 from the target
Or reverse it — -o host:port connects out and blasts a listening daemon. No wrapper script, no netcat gymnastics.
Radamsa isn't a byte-flipper. It parses samples as trees where it can (parens, brackets, XML tags, JSON) and performs swaps, repetitions, and truncations at structural boundaries. That's why it hits recursion limits, unbalanced-delimiter bugs, and encoding edge cases that pure random mutation misses. List the mutators it used:
radamsa -v -s 42 -n 3 sample.json
# ... "-p od -m bf,bp,tr2 ..." ← printed to stderr, reproducible
AFL/libFuzzer want an instrumented binary. Radamsa doesn't care what's on the other end of the pipe. It fuzzes:
Anywhere bytes go in, radamsa fuzzes.
No coverage feedback. It won't discover that flipping byte 42 unlocks a new branch — it's blind. For deep exploration of open-source code, reach for AFL++ or libFuzzer. For "here's a valid input, find me a crash before lunch on a binary I can't rebuild," radamsa gets you fuzzing in a single command instead of a weekend of build config and harness code.
Install: apt install radamsa, brew install radamsa, or clone gitlab.com/akihe/radamsa and make. It's ~3000 lines of Owl Lisp and compiles to a single static binary.
