5 std::vector<ProgramBlock*> blocks;
10 while (!stack.empty()) {
20 stack.push_front(predecessor);
24 stack.push_back(successor);
41 for (
const auto& instr : instruction_block) {
54 auto target_instruction_block =
58 for (
const auto& instr : target_instruction_block) {
72 auto target_then_instruction_block =
74 auto target_else_instruction_block =
79 for (
const auto& instr : target_then_instruction_block) {
82 for (
const auto& instr : target_else_instruction_block) {
94 if (possible_target_blocks.size() == 0) {
98 possible_target_blocks.at(
instruction.target_block_idx % possible_target_blocks.size());
101 if (non_terminated_blocks.size() == 0) {
113 if (possible_target_blocks.size() == 0) {
117 possible_target_blocks.at(
instruction.target_then_block_idx % possible_target_blocks.size());
119 possible_target_blocks.at(
instruction.target_else_block_idx % possible_target_blocks.size());
123 if (non_terminated_blocks.size() == 0) {
136 instruction.return_options.return_value_offset_index);
142 if (non_terminated_blocks.size() == 0) {
151 if (non_terminated_blocks.size() == 0) {
162 auto target_instruction_block =
166 for (
const auto& instr : target_instruction_block) {
176 std::vector<ProgramBlock*> non_terminated_blocks;
178 return block->terminator_type != TerminatorType::NONE;
180 return non_terminated_blocks;
187 std::vector<ProgramBlock*> forbidden_blocks =
dfs_traverse(block,
true);
190 std::vector<ProgramBlock*> reachable_blocks;
198 return std::find(forbidden_blocks.begin(), forbidden_blocks.end(), block_iter) ==
199 forbidden_blocks.end() &&
200 block_iter->caller == block->caller;
202 return reachable_blocks;
207 if (
std::getenv(
"AVM_FUZZER_LOGGING") !=
nullptr) {
223std::vector<uint8_t>
create_bytecode(
const std::vector<bb::avm2::simulation::Instruction>& instructions)
227 auto serialized_instruction =
instruction.serialize();
237 const int JMP_SIZE = 1 + 4;
238 const int JMP_IF_SIZE = 1 + 1 + 2 + 4;
242 return bytecode_length;
244 return bytecode_length + JMP_SIZE;
253 auto set_16_instruction =
263 return bytecode_length + JMP_IF_SIZE + JMP_SIZE;
266 throw std::runtime_error(
"Predict block size: Every block should be terminated with return, jump, or jumpi, "
270 throw std::runtime_error(
"Unreachable");
275 for (
size_t i = 0; i < blocks.size(); i++) {
276 if (blocks[i] == block) {
280 throw std::runtime_error(
"Block not found in the list");
291 block->finalize_with_return(
301 block->offset = last_offset;
307 block->patch_internal_calls();
313 std::vector<bb::avm2::simulation::Instruction> instructions = block->get_instructions();
314 switch (block->terminator_type) {
321 uint32_t jump_offset =
static_cast<uint32_t
>(blocks.at(target_block_idx)->offset);
322 auto jump_instruction =
324 instructions.push_back(jump_instruction);
334 auto conditional_offset = block->get_terminating_condition_value();
335 if (!conditional_offset.has_value()) {
336 throw std::runtime_error(
"Condition value not found, should not happen");
339 .
operand(conditional_offset.value())
342 auto jump_else_instruction =
344 instructions.push_back(jumpi_instruction);
345 instructions.push_back(jump_else_instruction);
349 throw std::runtime_error(
350 "Inject terminators: Every block should be terminated with return, jump, or jumpi");
357 for (
const auto& block_bytecode : block_bytecodes) {
358 bytecode.insert(
bytecode.end(), block_bytecode.begin(), block_bytecode.end());
std::shared_ptr< Napi::ThreadSafeFunction > bytecode
void process_jump_to_block(JumpToBlock instruction)
terminates the current block with a jump to the block, which does not create a loop in the graph (def...
ProgramBlock * current_block
std::vector< std::vector< FuzzInstruction > > * instruction_blocks
std::vector< ProgramBlock * > get_reachable_blocks(ProgramBlock *block)
get the list of blocks which are can be reached from the given block without creating a loop in the g...
ProgramBlock * start_block
the entry block of the program
void process_cfg_instruction(CFGInstruction instruction)
void process_insert_simple_instruction_block(InsertSimpleInstructionBlock instruction)
add instructions to the current block from the instruction block at the given index taken modulo leng...
void process_insert_internal_call(InsertInternalCall instruction)
inserts INTERNALCALL instruction to the current block creates a new block and sets it as the current ...
void process_switch_to_non_terminated_block(SwitchToNonTerminatedBlock instruction)
switches to the non-terminated block with the chosen index
std::vector< ProgramBlock * > get_non_terminated_blocks()
get the list of non-terminated blocks
void process_finalize_with_return(FinalizeWithReturn instruction)
terminates the current block with Return and switches to the first non-terminated block
void process_jump_to_new_block(JumpToNewBlock instruction)
terminates the current block with a jump and creates a new block
std::vector< uint8_t > build_bytecode(const ReturnOptions &return_options)
build the bytecode, finalizing the current block with return
void process_jump_if_to_new_block(JumpIfToNewBlock instruction)
terminates the current block with a jump if and creates two new blocks, sets the first as the then bl...
void process_jump_if_to_block(JumpIfToBlock instruction)
terminates the current block with a jumpi and jump instructions to the blocks, which does not create ...
static std::vector< ProgramBlock * > dfs_traverse(ProgramBlock *start_block, bool reverse=false)
traverse the control flow graph using DFS
std::vector< ProgramBlock * > predecessors
void finalize_with_return(uint8_t return_size, MemoryTagWrapper return_value_tag, uint16_t return_value_offset_index)
finalize the program block with a return instruction Tries to find memory address with the given retu...
std::vector< bb::avm2::simulation::Instruction > get_instructions()
void insert_internal_call(ProgramBlock *target_block)
insert INTERNALCALL instruction with 0 offset
bool is_memory_address_set(uint16_t address)
ProgramBlock * caller
the block that called this block by INTERNALCALL This field is copied to predecessors on every CFG in...
void finalize_with_jump(ProgramBlock *target_block, bool copy_memory_manager=true)
finalize the block with a jump Sets the terminator type to JUMP, adds the target block to the success...
uint16_t condition_offset_index
the offset index of the condition variable (for JUMP_IF)
void process_instruction(FuzzInstruction instruction)
process the instruction
std::vector< ProgramBlock * > successors
std::optional< uint16_t > get_terminating_condition_value()
void finalize_with_jump_if(ProgramBlock *target_then_block, ProgramBlock *target_else_block, uint16_t condition_offset, bool copy_memory_manager=true)
finalize the block with a jump if Sets the terminator type to JUMP_IF, adds the target blocks to the ...
TerminatorType terminator_type
simulation::Instruction build() const
InstructionBuilder & operand(OperandBuilder operand)
size_t find_block_idx(ProgramBlock *block, const std::vector< ProgramBlock * > &blocks)
std::vector< uint8_t > create_bytecode(const std::vector< bb::avm2::simulation::Instruction > &instructions)
int predict_block_size(ProgramBlock *block)
std::variant< InsertSimpleInstructionBlock, JumpToNewBlock, JumpIfToNewBlock, JumpToBlock, JumpIfToBlock, FinalizeWithReturn, SwitchToNonTerminatedBlock, InsertInternalCall > CFGInstruction
constexpr decltype(auto) get(::tuplet::tuple< T... > &&t) noexcept
std::string to_string(bb::avm2::ValueTag tag)
finalizes the current block with Return and switches to the first non-terminated block
ReturnOptions return_options
inserts INTERNALCALL instruction to the current block creates a new block and sets it as the current ...
insert instruction block to the current block
finalizes the current block with a JumpI and Jump instructions to the block, which does not create a ...
finalizes the current block with jump if, creates two new blocks, sets the first as the then block an...
finalizes the current block with a jump to the block, which does not create a loop in the graph (defi...
finalizes the current block with jump, creates a new block and sets it as the current block
MemoryTagWrapper return_value_tag
uint16_t return_value_offset_index
MemoryTagWrapper value_tag
switches to the non-terminated block with the chosen index