Extending Fuzzamoto IR
This guide explains how to add a new IR primitive end to end. Refer to the IR docs for a high level design overview.
Model the primitive
- Add new variable types in
fuzzamoto-ir/src/variable.rs. - Teach
fuzzamoto-ir/src/operation.rsabout the operation: specify its type signature (get_input_variables,get_output_variables,get_inner_output_variables), scope rules (is_block_begin,is_block_end,is_matching_block_begin,allow_insertion_in_block), and mutation/minimization behaviour (is_operation_mutable,mutates_nth_input,is_noppable). - Update
fuzzamoto-ir/src/instruction.rsso the builder and mutators can spot context changes (entered_context_after_execution) and know whether the operation can be mutated or nopped out.
Snapshot context & scenario
- If the new instruction needs extra snapshot data, extend
FullProgramContextinfuzzamoto-ir/src/lib.rs. - Populate that data inside the IR scenario by extending the relevant helpers in
fuzzamoto-scenarios/bin/ir.rs(build_*,dump_context, etc.). - Whenever context data changes, re-run
scenario-irto refreshir.contextfor generators and tests.
Compiler
- Update
fuzzamoto-ir/src/compiler.rsso the new operations lower into the appropriateCompiledActions. Make use of consensus encoders frombitcoinwhere possible. - Add focused unit tests in the same file that compile a tiny program and assert on the serialized bytes.
Generators & mutators
- Implement a generator under
fuzzamoto-ir/src/generators/that emits the new instructions in a valid SSA context. - Export it via
generators/mod.rsand wire it into:fuzzamoto-libafl/src/instance.rsso LibAFL schedules it.fuzzamoto-cli/src/commands/ir.rssoir generate --generatorscan use it.
Examples
A few PRs that show how IR primitives were added end-to-end: