5 std::vector<ProgramBlock*> blocks;
10 while (!stack.empty()) {
20 stack.push_front(predecessor);
24 stack.push_back(successor);
52 auto target_instruction_block =
68 auto target_then_instruction_block =
70 auto target_else_instruction_block =
86 if (possible_target_blocks.size() == 0) {
90 possible_target_blocks.at(
instruction.target_block_idx % possible_target_blocks.size());
93 if (non_terminated_blocks.size() == 0) {
105 if (possible_target_blocks.size() == 0) {
109 possible_target_blocks.at(
instruction.target_then_block_idx % possible_target_blocks.size());
111 possible_target_blocks.at(
instruction.target_else_block_idx % possible_target_blocks.size());
115 if (non_terminated_blocks.size() == 0) {
128 instruction.return_options.return_value_offset_index);
134 if (non_terminated_blocks.size() == 0) {
147 instruction.revert_options.return_value_offset_index);
149 if (non_terminated_blocks.size() == 0) {
158 if (non_terminated_blocks.size() == 0) {
169 auto target_instruction_block =
181 std::vector<ProgramBlock*> non_terminated_blocks;
183 return block->terminator_type != TerminatorType::NONE;
185 return non_terminated_blocks;
192 std::vector<ProgramBlock*> forbidden_blocks =
dfs_traverse(block,
true);
195 std::vector<ProgramBlock*> reachable_blocks;
203 return std::find(forbidden_blocks.begin(), forbidden_blocks.end(), block_iter) ==
204 forbidden_blocks.end() &&
205 block_iter->caller == block->caller;
207 return reachable_blocks;
212 if (
std::getenv(
"AVM_FUZZER_LOGGING") !=
nullptr) {
229std::vector<uint8_t>
create_bytecode(
const std::vector<bb::avm2::simulation::Instruction>& instructions)
233 auto serialized_instruction =
instruction.serialize();
243 const int JMP_SIZE = 1 + 4;
244 const int JMP_IF_SIZE = 1 + 1 + 2 + 4;
249 return bytecode_length;
251 return bytecode_length + JMP_SIZE;
258 return bytecode_length + JMP_IF_SIZE + JMP_SIZE;
261 throw std::runtime_error(
262 "Predict block size: Every block should be terminated with return, revert, jump, or jumpi, got " +
265 throw std::runtime_error(
"Unreachable");
270 for (
size_t i = 0; i < blocks.size(); i++) {
271 if (blocks[i] == block) {
275 throw std::runtime_error(
"Block not found in the list");
286 block->finalize_with_return(
296 block->offset = last_offset;
302 block->patch_internal_calls();
308 std::vector<bb::avm2::simulation::Instruction> instructions = block->get_instructions();
309 switch (block->terminator_type) {
319 uint32_t jump_offset =
static_cast<uint32_t
>(blocks.at(target_block_idx)->offset);
320 auto jump_instruction =
322 instructions.push_back(jump_instruction);
332 auto conditional_offset = block->get_terminating_condition_value();
333 if (!conditional_offset.has_value()) {
334 throw std::runtime_error(
"Condition value not found, should not happen");
337 .
operand(conditional_offset.value())
340 auto jump_else_instruction =
342 instructions.push_back(jumpi_instruction);
343 instructions.push_back(jump_else_instruction);
347 throw std::runtime_error(
348 "Inject terminators: Every block should be terminated with return, revert, jump, or jumpi");
355 for (
const auto& block_bytecode : block_bytecodes) {
356 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< 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...
std::vector< InstructionBlock > * instruction_blocks
void process_finalize_with_revert(FinalizeWithRevert instruction)
terminates the current block with Revert and switches to the first non-terminated block
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
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 finalize_with_revert(uint8_t revert_size, MemoryTagWrapper revert_value_tag, uint16_t revert_value_offset_index)
finalize the program block with a revert instruction Similar to finalize_with_return but uses REVERT ...
std::vector< ProgramBlock * > successors
void process_write_terminating_condition_value()
std::optional< uint16_t > get_terminating_condition_value()
void process_instruction_block(InstructionBlock &instruction_block)
process the instruction block
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, FinalizeWithRevert, 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
finalizes the current block with Revert and switches to the first non-terminated block
ReturnOptions revert_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
switches to the non-terminated block with the chosen index