/* Copyright (c) the Selfie Project authors. All rights reserved. Please see the AUTHORS file for details. Use of this source code is governed by a BSD license that can be found in the LICENSE file. Selfie is a project of the Computational Systems Group at the Department of Computer Sciences of the University of Salzburg in Austria. For further information and code please refer to: selfie.cs.uni-salzburg.at BEATOR is a binary translator for bounded model checking that implements a sound and complete translation of RISC-U code to BTOR2 formulae. BEATOR serves as research platform and facilitates teaching the absolute basics of bit-precise reasoning on real code. Given a RISC-U binary (or C* source code compiled to RISC-U, including all of selfie and beator itself), beator generates a BTOR2 file that models the bit-precise behavior of the binary on a 64-bit machine with 4GB of memory. The translation runs in time and space linear in the number of instructions in three iterations over all instructions for encoding the program counter, the data flow, and the control flow. The first console argument is interpreted as exit code. Value zero means that any code execution that terminates with a non-zero exit code is modeled as erroneous whereas a non-zero value means that any code execution that terminates with a different exit code is modeled as erroneous. Division by zero as well as memory access to the code segment and above the 4GB memory of the machine is also modeled as erroneous. The optional console argument --check-block-access instructs beator to generate additional checks for identifying unsafe memory access outside of malloced memory blocks. Any remaining console arguments are uninterpreted and passed on as console arguments to the modeled RISC-U binary. BEATOR is inspired by Professor Armin Biere from JKU Linz. */ // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ------------------- I N T E R F A C E ------------------- // ----------------------------------------------------------------- // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ----------------------- MIPSTER SYSCALLS ------------------------ // ----------------------------------------------------------------- uint64_t generate_ecall_address_checks(uint64_t cursor_nid, uint64_t current_ecall_nid); uint64_t generate_ecall_address_and_block_checks(uint64_t cursor_nid, uint64_t current_ecall_nid); void model_syscalls(uint64_t cursor_nid); // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ----------------- A R C H I T E C T U R E ----------------- // ----------------------------------------------------------------- // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ------------------------- INSTRUCTIONS -------------------------- // ----------------------------------------------------------------- uint64_t pc_nid(uint64_t nid, uint64_t pc); uint64_t is_procedure_call(uint64_t instruction, uint64_t link); uint64_t validate_procedure_body(uint64_t from_instruction, uint64_t from_link, uint64_t to_address); void go_to_instruction(uint64_t from_instruction, uint64_t from_link, uint64_t from_address, uint64_t to_address, uint64_t condition_nid); void model_data_flow_lui(); void model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); void model_data_flow_addi(); void model_control_flow_addi(); void model_data_flow_add(); void model_data_flow_sub(); void model_data_flow_mul(); void model_data_flow_divu(); void model_data_flow_remu(); void model_data_flow_sltu(); void model_data_flow_load(); void model_data_flow_store(); void model_data_flow_beq(); void model_control_flow_beq(); void model_data_flow_jal(); void model_control_flow_jal(); // TODO: model jalr data flow void model_control_flow_jalr(); void model_data_flow_ecall(); void model_control_flow_ecall(); // ------------------------ GLOBAL CONSTANTS ----------------------- // 4 more digits to accommodate code, data, heap, and stack with // 1000*4 lines per 32-bit instruction (pc increments by 4) and // 1000*8(4) lines per 64-bit (32-bit) machine word in memory segments uint64_t PC_NID_DIGITS = 4; // 10^4 == 10000 uint64_t PC_NID_FACTOR = 1000; // 1000*8(4) fit into 10000 // ----------------------------------------------------------------- // ---------------------------- MEMORY ----------------------------- // ----------------------------------------------------------------- void reset_bounds(); void transfer_bounds(); uint64_t record_start_bounds(uint64_t cursor_nid, uint64_t activation_nid, uint64_t reg); uint64_t record_end_bounds(uint64_t cursor_nid, uint64_t activation_nid, uint64_t reg); uint64_t model_virtual_address(); uint64_t model_physical_address_in_segment(uint64_t cursor_nid, uint64_t laddr_nid, uint64_t start_nid, uint64_t end_nid, uint64_t offset_nid, uint64_t flow_nid); uint64_t model_physical_address(uint64_t cursor_nid, uint64_t vaddr_nid); uint64_t model_RAM_access(uint64_t cursor_nid, uint64_t address_nid, uint64_t RAM_address, uint64_t RAM_word, uint64_t RAM_word_flow_nid); uint64_t compute_physical_address(uint64_t vaddr); // ----------------------------------------------------------------- // -------------------------- INTERPRETER -------------------------- // ----------------------------------------------------------------- uint64_t already_seen_instruction(uint64_t address); uint64_t is_statically_live_instruction(uint64_t address); void mark_instruction(uint64_t address, uint64_t mark); uint64_t get_return_mark(uint64_t address); void mark_return(uint64_t address, uint64_t mark); uint64_t mark_statically_live_code(uint64_t start_address, uint64_t callee_address, uint64_t entry_pc, uint64_t mark); void static_dead_code_elimination(uint64_t start_address, uint64_t entry_pc); void translate_to_model(); // ------------------------ GLOBAL CONSTANTS ----------------------- uint64_t DEAD = 0; uint64_t SEEN = 1; uint64_t LIVE = 2; // ------------------------ GLOBAL VARIABLES ----------------------- uint64_t exit_wrapper_address = 0; uint64_t* statically_live_code = (uint64_t*) 0; uint64_t* statically_live_return = (uint64_t*) 0; // ----------------------------------------------------------------- // ------------------------ MODEL GENERATOR ------------------------ // ----------------------------------------------------------------- uint64_t control_flow(uint64_t activate_nid, uint64_t control_flow_nid); uint64_t control_flow_from_beq(uint64_t from_address, uint64_t condition_nid, uint64_t control_flow_nid); uint64_t control_flow_from_jalr(uint64_t jalr_address, uint64_t condition_nid, uint64_t control_flow_nid); uint64_t control_flow_from_ecall(uint64_t from_address, uint64_t control_flow_nid); void check_syscall_id(); void check_exit_code(); void check_division_by_zero(uint64_t division, uint64_t flow_nid); uint64_t check_addresses(); void generate_address_alignment_check(uint64_t flow_nid); void generate_segmentation_faults(uint64_t flow_nid); void generate_block_access_check(uint64_t flow_nid, uint64_t lo_flow_nid, uint64_t up_flow_nid); uint64_t number_of_bits(uint64_t n); void beator(uint64_t entry_pc); uint64_t selfie_model(); // ------------------------ GLOBAL CONSTANTS ----------------------- uint64_t EXITCODE_MODELINGERROR = 1; uint64_t LO_FLOW = 32; // offset of nids of lower bounds on addresses in registers uint64_t UP_FLOW = 64; // offset of nids of upper bounds on addresses in registers // ------------------------ GLOBAL VARIABLES ----------------------- char* model_name = (char*) 0; // name of model file uint64_t model_fd = 0; // file descriptor of open model file char* no_syscall_id_check_option = (char*) 0; char* no_exit_code_check_option = (char*) 0; char* no_division_by_zero_check_option = (char*) 0; char* no_address_alignment_check_option = (char*) 0; char* no_segmentation_faults_option = (char*) 0; char* check_block_access_option = (char*) 0; char* constant_propagation_option = (char*) 0; char* linear_address_space_option = (char*) 0; char* heap_allowance_option = (char*) 0; char* stack_allowance_option = (char*) 0; char* MMU_option = (char*) 0; char* RAM_option = (char*) 0; char* MMURAM_option = (char*) 0; uint64_t syscall_id_check = 1; // flag for preventing syscall id checks uint64_t exit_code_check = 1; // flag for preventing exit code check uint64_t division_by_zero_check = 1; // flag for preventing division and remainder by zero checks uint64_t address_alignment_check = 1; // flag for preventing memory address alignment checks uint64_t segmentation_faults = 1; // flag for preventing segmentation fault checks uint64_t check_block_access = 0; // flag for generating memory access checks on malloced block level uint64_t constant_propagation = 0; // flag for constant propagation uint64_t linear_address_space = 0; // flag for 29-bit (30-bit) linear address space uint64_t fixed_heap_segment = 0; // flag for fixing size of heap segment uint64_t fixed_stack_segment = 0; // flag for fixing size of stack segment uint64_t MMU = 0; // flag for generating MMU circuit uint64_t RAM = 0; // flag for generating RAM state and circuit uint64_t MMURAM = 0; // flag for generating combined MMU and RAM state and circuit uint64_t heap_allowance = 0; // additional heap memory in bytes uint64_t stack_allowance = 0; // additional stack memory in bytes uint64_t w = 0; // number of written characters uint64_t current_nid = 0; // nid of current line uint64_t which_ecall_nid; // nid of $a7 == SYSCALL_EXIT uint64_t bad_number = 0; // bad number counter uint64_t bad_exit_code = 0; // model for this exit code uint64_t exit_ecall_nid = 0; // nid of exit ecall is active uint64_t vaddr_sort_nid = 2; // nid of virtual or linear address sort uint64_t vaddr_space_size = 64; // size of virtual address space in bits uint64_t vaddr_mask_nid = 27; // nid of bit mask for resetting LSBs in virtual addresses uint64_t vaddr_alignment = 3; // virtual address alignment in bits uint64_t laddr_space_size = 32; // size of linear address space in bits uint64_t paddr_sort_nid = 2; // nid of physical address sort uint64_t paddr_space_size = 64; // size of physical address space in bits uint64_t memory_sort_nid = 3; // nid of physical memory sort uint64_t heap_start = 0; uint64_t heap_size = 0; uint64_t stack_start = 0; uint64_t stack_size = 0; uint64_t reg_value_nids = 100; // nids of initial values of registers uint64_t reg_nids = 200; // nids of registers uint64_t reg_init_nids = 300; // nids of initialization of registers uint64_t* reg_flow_nids = (uint64_t*) 0; // nids of most recent update of registers uint64_t bump_pointer_nid = 0; // nid of brk bump pointer uint64_t reg_a7 = 0; // most recent update of $a7 register in sequential translation flow uint64_t pcs_nid = 0; // nid of first program counter flag // per-instruction list of control-flow in-edges uint64_t* control_in = (uint64_t*) 0; // per-procedure (target of procedure call jal) address of matching jalr instruction uint64_t* call_return = (uint64_t*) 0; uint64_t current_callee = 0; // address of first instruction of current callee // address of currently farthest forward branch or jump to find matching jalr instruction uint64_t estimated_return = 0; uint64_t memory_nid = 0; // nid of physical memory uint64_t memory_flow_nid = 0; // nid of most recent update of memory uint64_t lo_memory_nid = 0; // nid of lower bounds on addresses in memory uint64_t lo_memory_flow_nid = 0; // nid of most recent update of lower bounds on addresses in memory uint64_t up_memory_nid = 0; // nid of upper bounds on addresses in memory uint64_t up_memory_flow_nid = 0; // nid of most recent update of upper bounds on addresses in memory uint64_t* RAM_write_flow_nid = (uint64_t*) 0; // nids of most recent writes of RAM // for checking division and remainder by zero // 21 is nid of 1 which is ok as divisor uint64_t division_flow_nid = 21; uint64_t remainder_flow_nid = 21; // for checking address validity during state transitions with no memory access: // 30 is nid of start of data segment which must be a valid address (thus also checked) uint64_t access_flow_start_nid = 30; // 50 is nid of highest address in 32-bit virtual address space (4GB) uint64_t lo_flow_start_nid = 30; // nid of most recent update of current lower bound uint64_t up_flow_start_nid = 50; // nid of most recent update of current upper bound // for checking address validity for a whole range of addresses uint64_t access_flow_end_nid = 30; uint64_t lo_flow_end_nid = 30; // nid of most recent update of current lower bound uint64_t up_flow_end_nid = 50; // nid of most recent update of current upper bound // keep track of pc flags of ecalls, 10 is nid of 1-bit 0 uint64_t ecall_flow_nid = 10; // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ---------------------- R U N T I M E ---------------------- // ----------------------------------------------------------------- // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ---------------------------- KERNEL ----------------------------- // ----------------------------------------------------------------- uint64_t handle_propr_system_call(uint64_t* context); uint64_t handle_propr_exception(uint64_t* context); // ------------------------ GLOBAL VARIABLES ----------------------- uint64_t exited_on_timeout = 0; uint64_t exited_on_read = 0; // ----------------------------------------------------------------- // --------------------- CONSTANT PROPAGATION ---------------------- // ----------------------------------------------------------------- uint64_t propr(uint64_t* to_context); // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ------------------- I N T E R F A C E ------------------- // ----------------------------------------------------------------- // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ----------------------- MIPSTER SYSCALLS ------------------------ // ----------------------------------------------------------------- uint64_t generate_ecall_address_checks(uint64_t cursor_nid, uint64_t current_ecall_nid) { if (check_addresses()) { w = w // if read, write, or openat ecall is active record $a1 register for checking address validity + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; $a1 is start address of buffer for checking address validity\n", cursor_nid, // nid of this line current_ecall_nid, // nid of read, write, or openat ecall is active reg_nids + REG_A1, // nid of current value of $a1 register access_flow_start_nid); // nid of address of most recent memory access access_flow_start_nid = cursor_nid; cursor_nid = cursor_nid + 1; } return cursor_nid; } uint64_t generate_ecall_address_and_block_checks(uint64_t cursor_nid, uint64_t current_ecall_nid) { cursor_nid = generate_ecall_address_checks(cursor_nid, current_ecall_nid); if (check_block_access) { w = w // if read or write ecall is active record $a1 + (($a2 - 1) / 8(4)) * 8(4) if $a2 > 0, and // $a1 otherwise, as address for checking address validity + dprintf(output_fd, "%lu dec 2 %lu ; $a2 - 1\n", cursor_nid, // nid of this line reg_nids + REG_A2) // nid of current value of $a2 register + dprintf(output_fd, "%lu not 2 %lu ; not %lu\n", cursor_nid + 1, // nid of this line vaddr_mask_nid, // nid of bit mask two_to_the_power_of(vaddr_alignment) - 1) // virtual address alignment in decimal + dprintf(output_fd, "%lu and 2 %lu %lu ; reset %lu LSBs of $a2 - 1\n", cursor_nid + 2, // nid of this line cursor_nid, // nid of $a2 - 1 cursor_nid + 1, // nid of preceding line vaddr_alignment) // virtual address alignment in bits + dprintf(output_fd, "%lu add 2 %lu %lu ; $a1 + (($a2 - 1) / %lu) * %lu\n", cursor_nid + 3, // nid of this line reg_nids + REG_A1, // nid of current value of $a1 register cursor_nid + 2, // nid of (($a2 - 1) / 8(4)) * 8(4) WORDSIZE, // word size in bytes WORDSIZE) // word size in bytes + dprintf(output_fd, "%lu ugt 1 %lu 20 ; $a2 > 0\n", cursor_nid + 4, // nid of this line reg_nids + REG_A2) // nid of current value of $a2 register + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; $a1 + (($a2 - 1) / %lu) * %lu if $a2 > 0, and $a1 otherwise\n", cursor_nid + 5, // nid of this line cursor_nid + 4, // nid of $a2 > 0 cursor_nid + 3, // nid of $a1 + (($a2 - 1) / 8(4)) * 8(4) reg_nids + REG_A1, // nid of current value of $a1 register WORDSIZE, // word size in bytes WORDSIZE) // word size in bytes + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; $a1 + (($a2 - 1) / %lu) * %lu is end address of buffer for checking address validity\n", cursor_nid + 6, // nid of this line current_ecall_nid, // nid of read or write ecall is active cursor_nid + 5, // nid of $a1 + (($a2 - 1) / 8(4)) * 8(4) if $a2 > 0, and $a1 otherwise access_flow_end_nid, // nid of address of most recent memory access WORDSIZE, // word size in bytes WORDSIZE); // word size in bytes access_flow_end_nid = cursor_nid + 6; // if read or write ecall is active record $a1 bounds for checking address validity cursor_nid = record_end_bounds(record_start_bounds(cursor_nid + 7, current_ecall_nid, REG_A1), current_ecall_nid, REG_A1); } return cursor_nid; } void model_syscalls(uint64_t cursor_nid) { uint64_t current_ecall_nid; uint64_t kernel_mode_flow_nid; uint64_t increment_nid; uint64_t input_nid; uint64_t paddr_nid; uint64_t write_input_nid; uint64_t read_ecall_active_nid; uint64_t read_ecall_active_increment_nid; uint64_t RAM_address; uint64_t valid_brk_nid; uint64_t invalid_brk_nid; current_ecall_nid = cursor_nid; w = w + dprintf(output_fd, "%lu constd 2 %lu ; SYSCALL_EXIT\n", cursor_nid, SYSCALL_EXIT) + dprintf(output_fd, "%lu constd 2 %lu ; SYSCALL_READ\n", cursor_nid + 1, SYSCALL_READ) + dprintf(output_fd, "%lu constd 2 %lu ; SYSCALL_WRITE\n", cursor_nid + 2, SYSCALL_WRITE) + dprintf(output_fd, "%lu constd 2 %lu ; SYSCALL_OPENAT\n", cursor_nid + 3, SYSCALL_OPENAT) + dprintf(output_fd, "%lu constd 2 %lu ; SYSCALL_BRK\n\n", cursor_nid + 4, SYSCALL_BRK); which_ecall_nid = cursor_nid + 10; w = w + dprintf(output_fd, "%lu eq 1 %lu %lu ; $a7 == SYSCALL_EXIT\n", which_ecall_nid, // nid of this line reg_nids + REG_A7, // nid of current value of $a7 register cursor_nid) // nid of SYSCALL_EXIT + dprintf(output_fd, "%lu eq 1 %lu %lu ; $a7 == SYSCALL_READ\n", which_ecall_nid + 1, // nid of this line reg_nids + REG_A7, // nid of current value of $a7 register cursor_nid + 1) // nid of SYSCALL_READ + dprintf(output_fd, "%lu eq 1 %lu %lu ; $a7 == SYSCALL_WRITE\n", which_ecall_nid + 2, // nid of this line reg_nids + REG_A7, // nid of current value of $a7 register cursor_nid + 2) // nid of SYSCALL_WRITE + dprintf(output_fd, "%lu eq 1 %lu %lu ; $a7 == SYSCALL_OPENAT\n", which_ecall_nid + 3, // nid of this line reg_nids + REG_A7, // nid of current value of $a7 register cursor_nid + 3) // nid of SYSCALL_OPENAT + dprintf(output_fd, "%lu eq 1 %lu %lu ; $a7 == SYSCALL_BRK\n\n", which_ecall_nid + 4, // nid of this line reg_nids + REG_A7, // nid of current value of $a7 register cursor_nid + 4); // nid of SYSCALL_BRK current_ecall_nid = current_ecall_nid + pcs_nid / 10; exit_ecall_nid = current_ecall_nid; w = w + dprintf(output_fd, "%lu and 1 %lu %lu ; exit ecall is active\n", exit_ecall_nid, // nid of this line ecall_flow_nid, // nid of most recent update of ecall activation which_ecall_nid) // nid of $a7 == SYSCALL_EXIT // if exit ecall is active stay in kernel mode indefinitely + dprintf(output_fd, "%lu ite 1 60 %lu %lu ; stay in kernel mode indefinitely if exit ecall is active\n\n", exit_ecall_nid + 1, // nid of this line which_ecall_nid, // nid of $a7 == SYSCALL_EXIT exit_ecall_nid); // nid of exit ecall is active kernel_mode_flow_nid = exit_ecall_nid + 1; current_ecall_nid = current_ecall_nid + pcs_nid / 10; w = w // read ecall + dprintf(output_fd, "%lu and 1 %lu %lu ; read ecall is active\n", current_ecall_nid, // nid of this line ecall_flow_nid, // nid of most recent update of ecall activation which_ecall_nid + 1); // nid of $a7 == SYSCALL_READ cursor_nid = generate_ecall_address_and_block_checks(current_ecall_nid + 1, current_ecall_nid); // TODO: check file descriptor validity, return error codes // if read ecall is active go into kernel mode w = w + dprintf(output_fd, "%lu ite 1 %lu 11 %lu ; go into kernel mode if read ecall is active\n", cursor_nid, // nid of this line current_ecall_nid, // nid of read ecall is active kernel_mode_flow_nid); // nid of most recent update of kernel-mode flag kernel_mode_flow_nid = cursor_nid; // if read ecall is active set $a0 (number of read bytes) = 0 bytes w = w + dprintf(output_fd, "%lu ite 2 %lu 20 %lu ; set $a0 = 0 bytes if read ecall is active\n", cursor_nid + 1, // nid of this line current_ecall_nid, // nid of read ecall is active *(reg_flow_nids + REG_A0)); // nid of most recent update of $a0 register *(reg_flow_nids + REG_A0) = cursor_nid + 1; cursor_nid = cursor_nid + 2; w = w // determine number of bytes to read in next step + dprintf(output_fd, "%lu sub 2 %lu %lu ; $a2 - $a0\n", cursor_nid, // nid of this line reg_nids + REG_A2, // nid of current value of $a2 register reg_nids + REG_A0) // nid of current value of $a0 register + dprintf(output_fd, "%lu ugte 1 %lu %lu ; $a2 - $a0 >= %lu bytes\n", cursor_nid + 1, // nid of this line cursor_nid, // nid of $a2 - $a0 20 + WORDSIZE, // nid of word size WORDSIZE) // word size + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; read %lu bytes if $a2 - $a0 >= %lu bytes, or else $a2 - $a0 bytes\n", cursor_nid + 2, // nid of this line cursor_nid + 1, // nid of $a2 - $a0 >= 8 (4) bytes 20 + WORDSIZE, // nid of word size cursor_nid, // nid of $a2 - $a0 WORDSIZE, // word size WORDSIZE); // word size increment_nid = cursor_nid + 2; cursor_nid = cursor_nid + 3; w = w // compute unsigned-extended input + dprintf(output_fd, "%lu eq 1 %lu 22 ; increment == 2\n", cursor_nid, // nid of this line increment_nid) // nid of increment + dprintf(output_fd, "%lu ite 2 %lu 92 91 ; unsigned-extended 2-byte input if increment == 2, or else unsigned-extended 1-byte input\n", cursor_nid + 1, // nid of this line cursor_nid) // nid of increment == 2 + dprintf(output_fd, "%lu eq 1 %lu 23 ; increment == 3\n", cursor_nid + 2, // nid of this line increment_nid) // nid of increment + dprintf(output_fd, "%lu ite 2 %lu 93 %lu ; unsigned-extended 3-byte input if increment == 3\n", cursor_nid + 3, // nid of this line cursor_nid + 2, // nid of increment == 3 cursor_nid + 1) // nid of unsigned-extended 2-byte input + dprintf(output_fd, "%lu eq 1 %lu 24 ; increment == 4\n", cursor_nid + 4, // nid of this line increment_nid); // nid of increment if (IS64BITTARGET) { w = w + dprintf(output_fd, "%lu ite 2 %lu 94 %lu ; unsigned-extended 4-byte input if increment == 4\n", cursor_nid + 5, // nid of this line cursor_nid + 4, // nid of increment == 4 cursor_nid + 3) // nid of unsigned-extended 3-byte input + dprintf(output_fd, "%lu eq 1 %lu 25 ; increment == 5\n", cursor_nid + 6, // nid of this line increment_nid) // nid of increment + dprintf(output_fd, "%lu ite 2 %lu 95 %lu ; unsigned-extended 5-byte input if increment == 5\n", cursor_nid + 7, // nid of this line cursor_nid + 6, // nid of increment == 5 cursor_nid + 5) // nid of unsigned-extended 4-byte input + dprintf(output_fd, "%lu eq 1 %lu 26 ; increment == 6\n", cursor_nid + 8, // nid of this line increment_nid) // nid of increment + dprintf(output_fd, "%lu ite 2 %lu 96 %lu ; unsigned-extended 6-byte input if increment == 6\n", cursor_nid + 9, // nid of this line cursor_nid + 8, // nid of increment == 6 cursor_nid + 7) // nid of unsigned-extended 5-byte input + dprintf(output_fd, "%lu eq 1 %lu 27 ; increment == 7\n", cursor_nid + 10, // nid of this line increment_nid) // nid of increment + dprintf(output_fd, "%lu ite 2 %lu 97 %lu ; unsigned-extended 7-byte input if increment == 7\n", cursor_nid + 11, // nid of this line cursor_nid + 10, // nid of increment == 7 cursor_nid + 9) // nid of unsigned-extended 6-byte input + dprintf(output_fd, "%lu eq 1 %lu 28 ; increment == 8\n", cursor_nid + 12, // nid of this line increment_nid) // nid of increment + dprintf(output_fd, "%lu ite 2 %lu 98 %lu ; 8-byte input if increment == 8\n", cursor_nid + 13, // nid of this line cursor_nid + 12, // nid of increment == 8 cursor_nid + 11); // nid of unsigned-extended 7-byte input input_nid = cursor_nid + 13; cursor_nid = cursor_nid + 14; } else { w = w + dprintf(output_fd, "%lu ite 2 %lu 94 %lu ; 4-byte input if increment == 4\n", cursor_nid + 5, // nid of this line cursor_nid + 4, // nid of increment == 4 cursor_nid + 3); // nid of unsigned-extended 3-byte input input_nid = cursor_nid + 5; cursor_nid = cursor_nid + 6; } w = w // compute virtual address $a1 + $a0 + dprintf(output_fd, "%lu add 2 %lu %lu ; $a1 + $a0\n", cursor_nid, // nid of this line reg_nids + REG_A1, // nid of current value of $a1 register reg_nids + REG_A0); // nid of current value of $a0 register // compute physical address $a1 + $a0 paddr_nid = model_physical_address(cursor_nid + 1, cursor_nid); write_input_nid = paddr_nid + 1; if (RAM + MMURAM == 0) { w = w // write input to memory at physical address $a1 + $a0 + dprintf(output_fd, "%lu write %lu %lu %lu %lu ; memory[$a1 + $a0] = input\n", write_input_nid, // nid of this line memory_sort_nid, // nid of physical memory sort memory_nid, // nid of physical memory paddr_nid, // nid of physical address $a1 + $a0 input_nid); // nid of input cursor_nid = write_input_nid + 1; } else cursor_nid = write_input_nid; w = w // read ecall is in kernel mode and not done yet + dprintf(output_fd, "%lu ult 1 %lu %lu ; $a0 < $a2\n", cursor_nid, // nid of this line reg_nids + REG_A0, // nid of current value of $a0 register reg_nids + REG_A2) // nid of current value of $a2 register + dprintf(output_fd, "%lu and 1 %lu %lu ; $a7 == SYSCALL_READ and $a0 < $a2\n", cursor_nid + 1, // nid of this line which_ecall_nid + 1, // nid of $a7 == SYSCALL_READ cursor_nid) // nid of $a0 < $a2 + dprintf(output_fd, "%lu and 1 60 %lu ; read ecall is in kernel mode and not done yet\n", cursor_nid + 2, // nid of this line cursor_nid + 1); // nid of $a7 == SYSCALL_READ and $a0 < $a2 read_ecall_active_nid = cursor_nid + 2; cursor_nid = cursor_nid + 3; w = w // if read ecall is in kernel mode and not done yet write input to memory at address $a1 + $a0 + dprintf(output_fd, "%lu ugt 1 %lu 20 ; increment > 0\n", cursor_nid, // nid of this line increment_nid) // nid of increment + dprintf(output_fd, "%lu and 1 %lu %lu ; read ecall is in kernel mode and not done yet and increment > 0\n", cursor_nid + 1, // nid of this line read_ecall_active_nid, // nid of read ecall is in kernel mode and not done yet cursor_nid); // nid of increment > 0 read_ecall_active_increment_nid = cursor_nid + 1; cursor_nid = cursor_nid + 2; if (RAM + MMURAM == 0) { w = w + dprintf(output_fd, "%lu ite %lu %lu %lu %lu ; read input into memory[$a1 + $a0]\n", cursor_nid, // nid of this line memory_sort_nid, // nid of physical memory sort read_ecall_active_increment_nid, // nid of read ecall is in kernel mode and not done yet and increment > 0 write_input_nid, // nid of memory[$a1 + $a0] = input memory_flow_nid); // nid of most recent update of memory memory_flow_nid = cursor_nid; cursor_nid = cursor_nid + 1; } else { RAM_address = 0; while (RAM_address < (data_size + heap_size + stack_size) / WORDSIZE) { // if address $a1 + $a0 == RAM address write input at RAM address cursor_nid = model_RAM_access(cursor_nid, paddr_nid, RAM_address, input_nid, *(RAM_write_flow_nid + RAM_address)); w = w // if this instruction is active set RAM[$a1 + $a0] = input + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; read input into RAM[$a1 + $a0]\n", cursor_nid + 1, // nid of this line read_ecall_active_increment_nid, // read ecall is in kernel mode and not done yet and increment > 0 cursor_nid, // nid of RAM[$a1 + $a0] = input *(RAM_write_flow_nid + RAM_address)); // nid of most recent write at RAM address *(RAM_write_flow_nid + RAM_address) = cursor_nid + 1; cursor_nid = cursor_nid + 2; RAM_address = RAM_address + 1; } } w = w // if read ecall is in kernel mode and not done yet increment number of bytes read + dprintf(output_fd, "%lu add 2 %lu %lu ; $a0 + increment\n", cursor_nid, // nid of this line reg_nids + REG_A0, // nid of current value of $a0 register increment_nid) // nid of increment + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; set $a0 = $a0 + increment if read ecall is in kernel mode and not done yet\n", cursor_nid + 1, // nid of this line read_ecall_active_nid, // nid of read ecall is in kernel mode and not done yet cursor_nid, // nid of $a0 + increment *(reg_flow_nids + REG_A0)); // nid of most recent update of $a0 register *(reg_flow_nids + REG_A0) = cursor_nid + 1; // if read ecall is in kernel mode and not done yet stay in kernel mode w = w + dprintf(output_fd, "%lu ite 1 %lu 11 %lu ; stay in kernel mode if read ecall is in kernel mode and not done yet\n\n", cursor_nid + 2, // nid of this line read_ecall_active_nid, // nid of read ecall is in kernel mode and not done yet kernel_mode_flow_nid); // nid of most recent update of kernel-mode flag kernel_mode_flow_nid = cursor_nid + 2; current_ecall_nid = current_ecall_nid + pcs_nid / 10; w = w // write ecall + dprintf(output_fd, "%lu and 1 %lu %lu ; write ecall is active\n", current_ecall_nid, // nid of this line ecall_flow_nid, // nid of most recent update of ecall activation which_ecall_nid + 2); // nid of $a7 == SYSCALL_WRITE cursor_nid = generate_ecall_address_and_block_checks(current_ecall_nid + 1, current_ecall_nid); // TODO: check file descriptor validity, return error codes // if write ecall is active set $a0 (written number of bytes) = $a2 (size) w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; set $a0 = $a2 if write ecall is active\n\n", cursor_nid, // nid of this line current_ecall_nid, // nid of write ecall is active reg_nids + REG_A2, // nid of current value of $a2 register *(reg_flow_nids + REG_A0)); // nid of most recent update of $a0 register *(reg_flow_nids + REG_A0) = cursor_nid; current_ecall_nid = current_ecall_nid + pcs_nid / 10; w = w // openat ecall + dprintf(output_fd, "%lu and 1 %lu %lu ; openat ecall is active\n", current_ecall_nid, // nid of this line ecall_flow_nid, // nid of most recent update of ecall activation which_ecall_nid + 3); // nid of $a7 == SYSCALL_OPENAT cursor_nid = generate_ecall_address_checks(current_ecall_nid + 1, current_ecall_nid); if (check_block_access) // if openat ecall is active record $a1 bounds for checking address validity cursor_nid = record_start_bounds(cursor_nid, current_ecall_nid, REG_A1); // TODO: check address validity of whole filename, flags and mode arguments w = w + dprintf(output_fd, "%lu state 2 fd-bump-pointer\n", cursor_nid) + dprintf(output_fd, "%lu init 2 %lu 21 ; initial fd-bump-pointer is 1 (file descriptor bump pointer)\n", cursor_nid + 1, // nid of this line cursor_nid) // nid of fd-bump-pointer // if openat ecall is active set $a0 (file descriptor) = fd-bump-pointer + 1 (next file descriptor) + dprintf(output_fd, "%lu inc 2 %lu\n", cursor_nid + 2, // nid of this line cursor_nid) // nid of fd-bump-pointer + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; fd-bump-pointer + 1 if openat ecall is active\n", cursor_nid + 3, // nid of this line current_ecall_nid, // nid of openat ecall is active cursor_nid + 2, // nid of fd-bump-pointer + 1 cursor_nid) // nid of fd-bump-pointer + dprintf(output_fd, "%lu next 2 %lu %lu ; increment fd-bump-pointer if openat ecall is active\n", cursor_nid + 4, // nid of this line cursor_nid, // nid of fd-bump-pointer cursor_nid + 3) // nid of fd-bump-pointer + 1 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; set $a0 = fd-bump-pointer + 1 if openat ecall is active\n\n", cursor_nid + 5, // nid of this line current_ecall_nid, // nid of openat ecall is active cursor_nid + 2, // nid of fd-bump-pointer + 1 *(reg_flow_nids + REG_A0)); // nid of most recent update of $a0 register *(reg_flow_nids + REG_A0) = cursor_nid + 5; current_ecall_nid = current_ecall_nid + pcs_nid / 10; // is brk ecall is active? w = w + dprintf(output_fd, "%lu and 1 %lu %lu ; brk ecall is active\n", current_ecall_nid, // nid of this line ecall_flow_nid, // nid of most recent update of ecall activation which_ecall_nid + 4); // nid of $a7 == SYSCALL_BRK cursor_nid = current_ecall_nid + 1; bump_pointer_nid = cursor_nid; w = w + dprintf(output_fd, "%lu state 2 brk-bump-pointer\n", bump_pointer_nid) + dprintf(output_fd, "%lu init 2 %lu 33 ; current program break\n", cursor_nid + 1, // nid of this line bump_pointer_nid); // nid of brk bump pointer cursor_nid = cursor_nid + 2; w = w // $a0 is valid if brk <= $a0 < $sp and $a0 is word-aligned + dprintf(output_fd, "%lu ulte 1 %lu %lu ; brk <= $a0\n", cursor_nid, // nid of this line bump_pointer_nid, // nid of brk bump pointer reg_nids + REG_A0) // nid of current value of $a0 register + dprintf(output_fd, "%lu ult 1 %lu %lu ; $a0 < $sp\n", cursor_nid + 1, // nid of this line reg_nids + REG_A0, // nid of current value of $a0 register reg_nids + REG_SP) // nid of current value of $sp register + dprintf(output_fd, "%lu and 1 %lu %lu ; brk <= $a0 < $sp\n", cursor_nid + 2, // nid of this line cursor_nid, // nid of brk <= $a0 cursor_nid + 1) // nid of $a0 < $sp + dprintf(output_fd, "%lu and 2 %lu %lu ; reset all but %lu LSBs of $a0\n", cursor_nid + 3, // nid of this line reg_nids + REG_A0, // nid of current value of $a0 register vaddr_mask_nid, // nid of bit mask vaddr_alignment) // virtual address alignment in bits + dprintf(output_fd, "%lu eq 1 %lu 20 ; %lu LSBs of $a0 == 0 ($a0 is word-aligned)\n", cursor_nid + 4, // nid of this line cursor_nid + 3, // nid of 3 (or 2) LSBs of current value of $a0 register vaddr_alignment) // virtual address alignment in bits + dprintf(output_fd, "%lu and 1 %lu %lu ; brk <= $a0 < $sp and $a0 is word-aligned ($a0 is valid)\n", cursor_nid + 5, // nid of this line cursor_nid + 2, // nid of brk <= $a0 < $sp cursor_nid + 4) // nid of $a0 is word-aligned + dprintf(output_fd, "%lu and 1 %lu %lu ; brk ecall is active and $a0 is valid\n", cursor_nid + 6, // nid of this line current_ecall_nid, // nid of brk ecall is active cursor_nid + 5); // nid of $a0 is valid valid_brk_nid = cursor_nid + 6; w = w // if brk ecall is active and $a0 is valid set brk = $a0 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; brk = $a0 if brk ecall is active and $a0 is valid\n", cursor_nid + 7, // nid of this line valid_brk_nid, // nid of brk ecall is active and $a0 is valid reg_nids + REG_A0, // nid of current value of $a0 register bump_pointer_nid) // nid of brk bump pointer + dprintf(output_fd, "%lu next 2 %lu %lu ; set brk = $a0 if brk ecall is active and $a0 is valid\n", cursor_nid + 8, // nid of this line bump_pointer_nid, // nid of brk bump pointer cursor_nid + 7); // nid of preceding line w = w // $a0 is invalid if $a0 is not valid + dprintf(output_fd, "%lu not 1 %lu ; $a0 is invalid\n", cursor_nid + 9, // nid of this line cursor_nid + 5) // nid of $a0 is valid + dprintf(output_fd, "%lu and 1 %lu %lu ; brk ecall is active and $a0 is invalid\n", cursor_nid + 10, // nid of this line current_ecall_nid, // nid of brk ecall is active cursor_nid + 9); // nid of $a0 is invalid invalid_brk_nid = cursor_nid + 10; w = w // if brk ecall is active and $a0 is invalid set $a0 = brk + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; set $a0 = brk if brk ecall is active and $a0 is invalid\n", cursor_nid + 11, // nid of this line invalid_brk_nid, // nid of brk ecall is active and $a0 is invalid bump_pointer_nid, // nid of brk bump pointer *(reg_flow_nids + REG_A0)); // nid of most recent update of $a0 register *(reg_flow_nids + REG_A0) = cursor_nid + 11; cursor_nid = cursor_nid + 12; if (check_block_access) { w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; lower bound on $t1 = brk if brk ecall is active and $a0 is valid\n", cursor_nid, // nid of this line valid_brk_nid, // nid of brk ecall is active and $a0 is valid bump_pointer_nid, // nid of brk bump pointer *(reg_flow_nids + LO_FLOW + REG_T1)); // nid of most recent update of lower bound on $t1 register *(reg_flow_nids + LO_FLOW + REG_T1) = cursor_nid; w = w + dprintf(output_fd, "%lu sub 2 %lu %lu ; $a0 - WORDSIZE\n", cursor_nid + 1, // nid of this line reg_nids + REG_A0, // nid of current value of $a0 register 20 + WORDSIZE) // nid of word size + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; upper bound on $t1 = $a0 - WORDSIZE if brk ecall is active and $a0 is valid\n", cursor_nid + 2, // nid of this line valid_brk_nid, // nid of brk ecall is active and $a0 is valid cursor_nid + 1, // nid of current value of $a0 register - WORDSIZE *(reg_flow_nids + UP_FLOW + REG_T1)); // nid of most recent update of upper bound on $t1 register *(reg_flow_nids + UP_FLOW + REG_T1) = cursor_nid + 2; cursor_nid = cursor_nid + 3; w = w + dprintf(output_fd, "%lu ite 2 %lu 30 %lu ; lower bound on $t1 = start of data segment if brk ecall is active and $a0 is invalid\n", cursor_nid, // nid of this line invalid_brk_nid, // nid of brk ecall is active and $a0 is invalid *(reg_flow_nids + LO_FLOW + REG_T1)); // nid of most recent update of lower bound on $t1 register *(reg_flow_nids + LO_FLOW + REG_T1) = cursor_nid; w = w + dprintf(output_fd, "%lu ite 2 %lu 50 %lu ; upper bound on $t1 = highest address (4GB) if brk ecall is active and $a0 is invalid\n", cursor_nid + 1, // nid of this line invalid_brk_nid, // nid of brk ecall is active and $a0 is invalid *(reg_flow_nids + UP_FLOW + REG_T1)); // nid of most recent update of upper bound on $t1 register *(reg_flow_nids + UP_FLOW + REG_T1) = cursor_nid + 1; } w = w + dprintf(output_fd, "\n%lu next 1 60 %lu ; updating kernel-mode flag\n", current_ecall_nid + pcs_nid / 10, // nid of this line kernel_mode_flow_nid); // nid of most recent update of kernel-mode flag } // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ----------------- A R C H I T E C T U R E ----------------- // ----------------------------------------------------------------- // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ------------------------- INSTRUCTIONS -------------------------- // ----------------------------------------------------------------- uint64_t pc_nid(uint64_t nid, uint64_t pc) { return nid + pc * PC_NID_FACTOR; } uint64_t is_procedure_call(uint64_t instruction, uint64_t link) { if (instruction == JAL) if (link != REG_ZR) return 1; return 0; } uint64_t validate_procedure_body(uint64_t from_instruction, uint64_t from_link, uint64_t to_address) { if (is_procedure_call(from_instruction, from_link) == 0) { // no forward branches and jumps that are not "procedure calls" outside of "procedure body" if (to_address > estimated_return) // estimating address of jalr at the end of "procedure body" estimated_return = to_address; if (to_address < current_callee) // no backward branches and jumps that are not "procedure calls" outside of "procedure body" return 0; } return 1; } void go_to_instruction(uint64_t from_instruction, uint64_t from_link, uint64_t from_address, uint64_t to_address, uint64_t condition_nid) { uint64_t* in_edge; if (to_address % INSTRUCTIONSIZE == 0) { if (to_address < code_start + code_size) { if (validate_procedure_body(from_instruction, from_link, to_address)) { in_edge = smalloc(sizeof(uint64_t*) + 3 * sizeof(uint64_t)); *in_edge = *(control_in + (to_address - code_start) / INSTRUCTIONSIZE); *(in_edge + 1) = from_instruction; // from which instruction *(in_edge + 2) = from_address; // at which address *(in_edge + 3) = condition_nid; // under which condition are we coming *(control_in + (to_address - code_start) / INSTRUCTIONSIZE) = (uint64_t) in_edge; return; } } else if (from_address == code_start + code_size - INSTRUCTIONSIZE) // from_instruction is last instruction in binary if (*(control_in + (from_address - code_start) / INSTRUCTIONSIZE) == 0) // and unreachable return; } // the instruction at from_address proceeds to an instruction at an invalid to_address //report the error on the console output_fd = 1; printf("%s: invalid instruction address %lu[0x%lX] detected\n", selfie_name, to_address, to_address); exit(EXITCODE_MODELINGERROR); } void model_data_flow_lui() { if (rd != REG_ZR) { reset_bounds(); w = w + dprintf(output_fd, "%lu constd 2 %ld ; 0x%lX << 12\n", current_nid, // nid of this line left_shift(imm, 12), // signed immediate value left-shifted by 12 bits sign_shrink(imm, WORDSIZEINBITS)) // immediate value in hexadecimal // if this instruction is active set $rd = imm << 12 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of immediate value left-shifted by 12 bits *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid + 1; w = w + print_lui() + dprintf(output_fd, "\n"); } } void model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store() { go_to_instruction(is, REG_ZR, pc, pc + INSTRUCTIONSIZE, 0); } void model_data_flow_addi() { uint64_t result_nid; if (rd != REG_ZR) { transfer_bounds(); if (imm == 0) result_nid = reg_nids + rs1; else { w = w + dprintf(output_fd, "%lu constd 2 %ld ; 0x%lX\n", current_nid, // nid of this line imm, // signed immediate value sign_shrink(imm, WORDSIZEINBITS)); // immediate value in hexadecimal if (rs1 == REG_ZR) { result_nid = current_nid; current_nid = current_nid + 1; } else { // compute $rs1 + imm w = w + dprintf(output_fd, "%lu add 2 %lu %lu\n", current_nid + 1, // nid of this line reg_nids + rs1, // nid of current value of $rs1 register current_nid); // nid of immediate value result_nid = current_nid + 1; current_nid = current_nid + 2; } } // if this instruction is active set $rd = $rs1 + imm w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction result_nid, // nid of $rs1 + ismm *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid; w = w + print_addi() + dprintf(output_fd, "\n"); } } void model_control_flow_addi() { if (rs1 == REG_ZR) if (imm != 0) if (rd == REG_A7) // assert: next instruction is ecall reg_a7 = imm; model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); } void model_data_flow_add() { if (rd != REG_ZR) { if (check_block_access) { w = w // lower bound on $rs1 register > lower bound on $rs2 register + dprintf(output_fd, "%lu ugt 1 %lu %lu\n", current_nid, // nid of this line reg_nids + LO_FLOW + rs1, // nid of lower bound on $rs1 register reg_nids + LO_FLOW + rs2) // nid of lower bound on $rs2 register // greater lower bound of $rs1 and $rs2 registers + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", current_nid + 1, // nid of this line current_nid, // nid of lower bound on $rs1 > lower bound on $rs2 reg_nids + LO_FLOW + rs1, // nid of lower bound on $rs1 register reg_nids + LO_FLOW + rs2) // nid of lower bound on $rs2 register // if this instruction is active set lower bound on $rd = greater lower bound of $rs1 and $rs2 registers + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", current_nid + 2, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid + 1, // nid of greater lower bound of $rs1 and $rs2 registers *(reg_flow_nids + LO_FLOW + rd)); // nid of most recent update of lower bound on $rd register *(reg_flow_nids + LO_FLOW + rd) = current_nid + 2; current_nid = current_nid + 3; w = w // upper bound on $rs1 register < upper bound on $rs2 register + dprintf(output_fd, "%lu ult 1 %lu %lu\n", current_nid, // nid of this line reg_nids + UP_FLOW + rs1, // nid of upper bound on $rs1 register reg_nids + UP_FLOW + rs2) // nid of upper bound on $rs2 register // lesser upper bound of $rs1 and $rs2 registers + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", current_nid + 1, // nid of this line current_nid, // nid of upper bound on $rs1 < upper bound on $rs2 reg_nids + UP_FLOW + rs1, // nid of upper bound on $rs1 register reg_nids + UP_FLOW + rs2) // nid of upper bound on $rs2 register // if this instruction is active set upper bound on $rd = lesser upper bound of $rs1 and $rs2 registers + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", current_nid + 2, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid + 1, // nid of lesser upper bound of $rs1 and $rs2 registers *(reg_flow_nids + UP_FLOW + rd)); // nid of most recent update of upper bound on $rd register *(reg_flow_nids + UP_FLOW + rd) = current_nid + 2; current_nid = current_nid + 3; } w = w // compute $rs1 + $rs2 + dprintf(output_fd, "%lu add 2 %lu %lu\n", current_nid, // nid of this line reg_nids + rs1, // nid of current value of $rs1 register reg_nids + rs2) // nid of current value of $rs2 register // if this instruction is active set $rd = $rs1 + $rs2 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of $rs1 + $rs2 *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid + 1; w = w + print_add_sub_mul_divu_remu_sltu() + dprintf(output_fd, "\n"); } } void model_data_flow_sub() { if (rd != REG_ZR) { // TODO: check if bounds on $rs2 are really initial bounds transfer_bounds(); w = w // compute $rs1 - $rs2 + dprintf(output_fd, "%lu sub 2 %lu %lu\n", current_nid, // nid of this line reg_nids + rs1, // nid of current value of $rs1 register reg_nids + rs2) // nid of current value of $rs2 register // if this instruction is active set $rd = $rs1 - $rs2 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of $rs1 - $rs2 *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid + 1; w = w + print_add_sub_mul_divu_remu_sltu() + dprintf(output_fd, "\n"); } } void model_data_flow_mul() { if (rd != REG_ZR) { // TODO: check if bounds on $rs1 and $rs2 are really initial bounds reset_bounds(); w = w // compute $rs1 * $rs2 + dprintf(output_fd, "%lu mul 2 %lu %lu\n", current_nid, // nid of this line reg_nids + rs1, // nid of current value of $rs1 register reg_nids + rs2) // nid of current value of $rs2 register // if this instruction is active set $rd = $rs1 * $rs2 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of $rs1 * $rs2 *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid + 1; w = w + print_add_sub_mul_divu_remu_sltu() + dprintf(output_fd, "\n"); } } void model_data_flow_divu() { if (rd != REG_ZR) { // TODO: check if bounds on $rs1 and $rs2 are really initial bounds reset_bounds(); // if this instruction is active record $rs2 for checking if $rs2 == 0 w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; record %s for checking division by zero\n", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction reg_nids + rs2, // nid of current value of $rs2 register division_flow_nid, // nid of divisor of most recent division get_register_name(rs2)); // register name division_flow_nid = current_nid; current_nid = current_nid + 1; w = w // compute $rs1 / $rs2 + dprintf(output_fd, "%lu udiv 2 %lu %lu\n", current_nid, // nid of this line reg_nids + rs1, // nid of current value of $rs1 register reg_nids + rs2) // nid of current value of $rs2 register // if this instruction is active set $rd = $rs1 / $rs2 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of $rs1 / $rs2 *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid + 1; w = w + print_add_sub_mul_divu_remu_sltu() + dprintf(output_fd, "\n"); } } void model_data_flow_remu() { if (rd != REG_ZR) { // TODO: check if bounds on $rs1 and $rs2 are really initial bounds reset_bounds(); // if this instruction is active record $rs2 for checking if $rs2 == 0 w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; record %s for checking remainder by zero\n", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction reg_nids + rs2, // nid of current value of $rs2 register remainder_flow_nid, // nid of divisor of most recent remainder get_register_name(rs2)); // register name remainder_flow_nid = current_nid; current_nid = current_nid + 1; w = w // compute $rs1 % $rs2 + dprintf(output_fd, "%lu urem 2 %lu %lu\n", current_nid, // nid of this line reg_nids + rs1, // nid of current value of $rs1 register reg_nids + rs2) // nid of current value of $rs2 register // if this instruction is active set $rd = $rs1 % $rs2 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of $rs1 % $rs2 *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid + 1; w = w + print_add_sub_mul_divu_remu_sltu() + dprintf(output_fd, "\n"); } } void model_data_flow_sltu() { if (rd != REG_ZR) { reset_bounds(); w = w // compute $rs1 < $rs2 + dprintf(output_fd, "%lu ult 1 %lu %lu\n", current_nid, // nid of this line reg_nids + rs1, // nid of current value of $rs1 register reg_nids + rs2) // nid of current value of $rs2 register // cast Boolean to machine word + dprintf(output_fd, "%lu ite 2 %lu 21 20\n", current_nid + 1, // nid of this line current_nid) // nid of $rs1 < $rs2 // if this instruction is active set $rd = $rs1 < $rs2 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid + 2, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid + 1, // nid of $rs1 < $rs2 cast to machine word *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid + 2; w = w + print_add_sub_mul_divu_remu_sltu() + dprintf(output_fd, "\n"); } } void model_data_flow_load() { uint64_t vaddr_nid; uint64_t paddr_nid; uint64_t RAM_address; uint64_t RAM_read_flow_nid; if (rd != REG_ZR) { current_nid = record_start_bounds(current_nid, pc_nid(pcs_nid, pc), rs1); vaddr_nid = model_virtual_address(); paddr_nid = model_physical_address(current_nid, vaddr_nid); if (paddr_nid != vaddr_nid) // account for mapping code current_nid = paddr_nid + 1; if (check_addresses()) { // if this instruction is active record $rs1 + imm for checking address validity w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; for checking address validity\n", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction vaddr_nid, // nid of virtual address $rs1 + imm access_flow_start_nid); // nid of address of most recent memory access access_flow_start_nid = current_nid; current_nid = current_nid + 1; } if (check_block_access) { w = w // read from lower-bounds memory[$rs1 + imm] into lower bound on $rd register + dprintf(output_fd, "%lu read 2 %lu %lu\n", current_nid, // nid of this line lo_memory_nid, // nid of lower bounds on addresses in memory paddr_nid) // nid of physical address $rs1 + imm // if this instruction is active set lower bound on $rd = lower-bounds memory[$rs1 + imm] + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of lower-bounds memory[$rs1 + imm] *(reg_flow_nids + LO_FLOW + rd)); // nid of most recent update of lower bound on $rd register *(reg_flow_nids + LO_FLOW + rd) = current_nid + 1; current_nid = current_nid + 2; w = w // read from upper-bounds memory[$rs1 + imm] into upper bound on $rd register + dprintf(output_fd, "%lu read 2 %lu %lu\n", current_nid, // nid of this line up_memory_nid, // nid of upper bounds on addresses in memory paddr_nid) // nid of physical address $rs1 + imm // if this instruction is active set upper bound on $rd = upper-bounds memory[$rs1 + imm] + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of upper-bounds memory[$rs1 + imm] *(reg_flow_nids + UP_FLOW + rd)); // nid of most recent update of upper bound on $rd register *(reg_flow_nids + UP_FLOW + rd) = current_nid + 1; current_nid = current_nid + 2; } if (RAM + MMURAM == 0) { w = w // read from memory[$rs1 + imm] into $rd register + dprintf(output_fd, "%lu read 2 %lu %lu\n", current_nid, // nid of this line memory_nid, // nid of physical memory paddr_nid); // nid of physical address $rs1 + imm RAM_read_flow_nid = current_nid; current_nid = current_nid + 1; } else { RAM_address = 0; // read 0 if address $rs1 + imm does not match any RAM address (must not happen) RAM_read_flow_nid = 20; while (RAM_address < (data_size + heap_size + stack_size) / WORDSIZE) { // if address $rs1 + imm == RAM address read RAM[$rs1 + imm] at RAM address RAM_read_flow_nid = model_RAM_access(current_nid, paddr_nid, RAM_address, pc_nid(memory_nid, RAM_address) + 2, RAM_read_flow_nid); current_nid = RAM_read_flow_nid + 1; RAM_address = RAM_address + 1; } } w = w // if this instruction is active set $rd = memory[$rs1 + imm] + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction RAM_read_flow_nid, // nid of memory[$rs1 + imm] *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid; w = w + print_load() + dprintf(output_fd, "\n"); } } void model_data_flow_store() { uint64_t vaddr_nid; uint64_t paddr_nid; uint64_t RAM_address; current_nid = record_start_bounds(current_nid, pc_nid(pcs_nid, pc), rs1); vaddr_nid = model_virtual_address(); paddr_nid = model_physical_address(current_nid, vaddr_nid); if (paddr_nid != vaddr_nid) // account for mapping code current_nid = paddr_nid + 1; if (check_addresses()) { // if this instruction is active record $rs1 + imm for checking address validity w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; for checking address validity\n", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction vaddr_nid, // nid of virtual address $rs1 + imm access_flow_start_nid); // nid of address of most recent memory access access_flow_start_nid = current_nid; current_nid = current_nid + 1; } if (check_block_access) { w = w // write lower bound on $rs2 register to lower-bounds memory[$rs1 + imm] + dprintf(output_fd, "%lu write %lu %lu %lu %lu\n", current_nid, // nid of this line memory_sort_nid, // nid of physical memory sort lo_memory_nid, // nid of lower bounds on addresses in memory paddr_nid, // nid of physical address $rs1 + imm reg_nids + LO_FLOW + rs2) // nid of lower bound on $rs2 register // if this instruction is active set lower-bounds memory[$rs1 + imm] = lower bound on $rs2 + dprintf(output_fd, "%lu ite %lu %lu %lu %lu\n", current_nid + 1, // nid of this line memory_sort_nid, // nid of physical memory sort pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of lower-bounds memory[$rs1 + imm] lo_memory_flow_nid); // nid of most recent update of lower-bounds memory lo_memory_flow_nid = current_nid + 1; current_nid = current_nid + 2; w = w // write upper bound on $rs2 register to upper-bounds memory[$rs1 + imm] + dprintf(output_fd, "%lu write %lu %lu %lu %lu\n", current_nid, // nid of this line memory_sort_nid, // nid of physical memory sort up_memory_nid, // nid of upper bounds on addresses in memory paddr_nid, // nid of physical address $rs1 + imm reg_nids + UP_FLOW + rs2) // nid of upper bound on $rs2 register // if this instruction is active set upper-bounds memory[$rs1 + imm] = upper bound on $rs2 + dprintf(output_fd, "%lu ite %lu %lu %lu %lu\n", current_nid + 1, // nid of this line memory_sort_nid, // nid of physical memory sort pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of upper-bounds memory[$rs1 + imm] up_memory_flow_nid); // nid of most recent update of upper-bounds memory up_memory_flow_nid = current_nid + 1; current_nid = current_nid + 2; } if (RAM + MMURAM == 0) { w = w // write $rs2 register to memory[$rs1 + imm] + dprintf(output_fd, "%lu write %lu %lu %lu %lu\n", current_nid, // nid of this line memory_sort_nid, // nid of physical memory sort memory_nid, // nid of physical memory paddr_nid, // nid of physical address $rs1 + imm reg_nids + rs2) // nid of current value of $rs2 register // if this instruction is active set memory[$rs1 + imm] = $rs2 + dprintf(output_fd, "%lu ite %lu %lu %lu %lu ; ", current_nid + 1, // nid of this line memory_sort_nid, // nid of physical memory sort pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of memory[$rs1 + imm] = $rs2 memory_flow_nid); // nid of most recent update of memory memory_flow_nid = current_nid + 1; } else { RAM_address = 0; while (RAM_address < (data_size + heap_size + stack_size) / WORDSIZE) { // if address $rs1 + imm == RAM address write current value of $rs2 register at RAM address current_nid = model_RAM_access(current_nid, paddr_nid, RAM_address, reg_nids + rs2, *(RAM_write_flow_nid + RAM_address)); w = w // if this instruction is active set RAM[$rs1 + imm] = $rs2 + dprintf(output_fd, "%lu ite 2 %lu %lu %lu", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction current_nid, // nid of RAM[$rs1 + imm] = $rs2 *(RAM_write_flow_nid + RAM_address)); // nid of most recent write at RAM address *(RAM_write_flow_nid + RAM_address) = current_nid + 1; current_nid = current_nid + 2; RAM_address = RAM_address + 1; if (RAM_address < (data_size + heap_size + stack_size) / WORDSIZE) w = w + dprintf(output_fd, "\n"); else w = w + dprintf(output_fd, " ; "); } } w = w + print_store() + dprintf(output_fd, "\n"); } void model_data_flow_beq() { // compute if beq condition is true w = w + dprintf(output_fd, "%lu eq 1 %lu %lu ; ", current_nid, // nid of this line reg_nids + rs1, // nid of current value of $rs1 register reg_nids + rs2); // nid of current value of $rs2 register w = w + print_beq() + dprintf(output_fd, "\n"); // compute if beq condition is false w = w + dprintf(output_fd, "%lu not 1 %lu\n", current_nid + 1, // nid of this line current_nid); // nid of preceding line } void model_control_flow_beq() { // true branch go_to_instruction(is, REG_ZR, pc, pc + imm, current_nid); // false branch go_to_instruction(is, REG_ZR, pc, pc + INSTRUCTIONSIZE, current_nid + 1); } void model_data_flow_jal() { if (rd != REG_ZR) { w = w // address of next instruction used here and in returning jalr instruction + dprintf(output_fd, "%lu constd 2 %lu ; 0x%lX\n", current_nid, // nid of this line pc + INSTRUCTIONSIZE, // address of next instruction pc + INSTRUCTIONSIZE) // address of next instruction // if this instruction is active link $rd register to address of next instruction + dprintf(output_fd, "%lu ite 2 %lu %lu %lu ; ", current_nid + 1, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this jal instruction current_nid, // nid of address of next instruction *(reg_flow_nids + rd)); // nid of most recent update of $rd register *(reg_flow_nids + rd) = current_nid + 1; w = w + print_jal() + dprintf(output_fd, "\n"); } } void model_control_flow_jal() { if (rd != REG_ZR) { if (is_statically_live_instruction(pc) == 0) w = w // we still need address of next instruction used in returning jalr instruction + dprintf(output_fd, "%lu constd 2 %lu ; 0x%lX\n", current_nid, // nid of this line pc + INSTRUCTIONSIZE, // address of next instruction pc + INSTRUCTIONSIZE); // address of next instruction // link next instruction to returning jalr instruction via instruction at address pc + imm go_to_instruction(JALR, REG_ZR, pc + imm, pc + INSTRUCTIONSIZE, current_nid); } // jump from this instruction to instruction at address pc + imm go_to_instruction(is, rd, pc, pc + imm, 0); } void model_control_flow_jalr() { if (rd == REG_ZR) if (imm == 0) if (rs1 == REG_RA) if (pc >= estimated_return) // no forward branches and jumps outside of "procedure body" if (current_callee > code_start) { // assert: current_callee points to an instruction to which a jal jumps *(call_return + (current_callee - code_start) / INSTRUCTIONSIZE) = pc; // assert: next "procedure body" begins right after jalr current_callee = pc + INSTRUCTIONSIZE; estimated_return = current_callee; return; } //report the error on the console output_fd = 1; printf("%s: unsupported jalr at address %lu[0x%lX] with estimated address %lu[0x%lX] detected\n", selfie_name, pc, pc, estimated_return, estimated_return); exit(EXITCODE_MODELINGERROR); } void model_data_flow_ecall() { // keep track of whether any ecall is active w = w + dprintf(output_fd, "%lu ite 1 %lu 11 %lu ; ", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction ecall_flow_nid); // nid of most recent update of ecall activation ecall_flow_nid = current_nid; w = w + print_ecall() + dprintf(output_fd, "\n"); } void model_control_flow_ecall() { if (reg_a7 == SYSCALL_EXIT) { // assert: exit ecall is immediately followed by first procedure in code current_callee = pc + INSTRUCTIONSIZE; estimated_return = current_callee; } reg_a7 = 0; model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); } // ----------------------------------------------------------------- // ---------------------------- MEMORY ----------------------------- // ----------------------------------------------------------------- void reset_bounds() { if (check_block_access) { // if this instruction is active reset lower bound on $rd register to start of data segment w = w + dprintf(output_fd, "%lu ite 2 %lu 30 %lu\n", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction *(reg_flow_nids + LO_FLOW + rd)); // nid of most recent update of lower bound on $rd register *(reg_flow_nids + LO_FLOW + rd) = current_nid; current_nid = current_nid + 1; // if this instruction is active reset upper bound on $rd register to highest address in 32-bit virtual address space (4GB) w = w + dprintf(output_fd, "%lu ite 2 %lu 50 %lu\n", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction *(reg_flow_nids + UP_FLOW + rd)); // nid of most recent update of upper bound on $rd register *(reg_flow_nids + UP_FLOW + rd) = current_nid; current_nid = current_nid + 1; } } void transfer_bounds() { if (check_block_access) { // if this instruction is active set lower bound on $rd = lower bound on $rs1 register w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction reg_nids + LO_FLOW + rs1, // nid of lower bound on $rs1 register *(reg_flow_nids + LO_FLOW + rd)); // nid of most recent update of lower bound on $rd register *(reg_flow_nids + LO_FLOW + rd) = current_nid; current_nid = current_nid + 1; // if this instruction is active set upper bound on $rd = upper bound on $rs1 register w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of this instruction reg_nids + UP_FLOW + rs1, // nid of upper bound on $rs1 register *(reg_flow_nids + UP_FLOW + rd)); // nid of most recent update of upper bound on $rd register *(reg_flow_nids + UP_FLOW + rd) = current_nid; current_nid = current_nid + 1; } } uint64_t record_start_bounds(uint64_t cursor_nid, uint64_t activation_nid, uint64_t reg) { if (check_block_access) { // if current instruction is active record lower bound on $reg register for checking address validity w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", cursor_nid, // nid of this line activation_nid, // nid of activation condition of current instruction reg_nids + LO_FLOW + reg, // nid of current lower bound on $reg register lo_flow_start_nid); // nid of most recent update of lower bound on memory access lo_flow_start_nid = cursor_nid; cursor_nid = cursor_nid + 1; // if current instruction is active record upper bound on $reg register for checking address validity w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", cursor_nid, // nid of this line activation_nid, // nid of activation condition of current instruction reg_nids + UP_FLOW + reg, // nid of current upper bound on $reg register up_flow_start_nid); // nid of most recent update of upper bound on memory access up_flow_start_nid = cursor_nid; return cursor_nid + 1; } else return cursor_nid; } uint64_t record_end_bounds(uint64_t cursor_nid, uint64_t activation_nid, uint64_t reg) { if (check_block_access) { // if current instruction is active record lower bound on $reg register for checking address validity w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", cursor_nid, // nid of this line activation_nid, // nid of activation condition of current instruction reg_nids + LO_FLOW + reg, // nid of current lower bound on $reg register lo_flow_end_nid); // nid of most recent update of lower bound on memory access lo_flow_end_nid = cursor_nid; cursor_nid = cursor_nid + 1; // if current instruction is active record upper bound on $reg register for checking address validity w = w + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", cursor_nid, // nid of this line activation_nid, // nid of activation condition of current instruction reg_nids + UP_FLOW + reg, // nid of current upper bound on $reg register up_flow_end_nid); // nid of most recent update of upper bound on memory access up_flow_end_nid = cursor_nid; return cursor_nid + 1; } else return cursor_nid; } uint64_t model_virtual_address() { if (imm == 0) return reg_nids + rs1; // nid of current value of $rs1 register else { w = w + dprintf(output_fd, "%lu constd 2 %ld ; 0x%lX\n", current_nid, // nid of this line imm, // signed immediate value sign_shrink(imm, WORDSIZEINBITS)) // immediate value in hexadecimal // compute $rs1 + imm + dprintf(output_fd, "%lu add 2 %lu %lu\n", current_nid + 1, // nid of this line reg_nids + rs1, // nid of current value of $rs1 register current_nid); // nid of immediate value current_nid = current_nid + 2; return current_nid - 1; // nid of $rs1 + imm } } uint64_t model_physical_address_in_segment(uint64_t cursor_nid, uint64_t laddr_nid, uint64_t start_nid, uint64_t end_nid, uint64_t offset_nid, uint64_t flow_nid) { uint64_t laddr_alignment; w = w + dprintf(output_fd, "%lu ugte 1 %lu %lu ; address >= start of segment\n", cursor_nid, // nid of this line laddr_nid, // nid of virtual or linear address start_nid); // nid of start of segment // in address spaces where the highest address is the largest value // wraparound makes this constraint obsolete if (end_nid) { if (end_nid == 50) // 50 is nid of highest address in 32-bit virtual address space (4GB) w = w + dprintf(output_fd, "%lu ulte 1 %lu %lu ; address <= end of segment\n", cursor_nid + 1, // nid of this line laddr_nid, // nid of virtual address end_nid); // nid of end of segment else w = w + dprintf(output_fd, "%lu ult 1 %lu %lu ; address < end of segment\n", cursor_nid + 1, // nid of this line laddr_nid, // nid of virtual or linear address end_nid); // nid of end of segment w = w + dprintf(output_fd, "%lu and 1 %lu %lu\n", cursor_nid + 2, // nid of this line cursor_nid, // nid of >= check cursor_nid + 1); // nid of < check cursor_nid = cursor_nid + 2; } if (linear_address_space) laddr_alignment = 0; else laddr_alignment = vaddr_alignment; w = w // subtract offset of segment in virtual or linear address space + dprintf(output_fd, "%lu sub %lu %lu %lu\n", cursor_nid + 1, // nid of this line vaddr_sort_nid, // nid of virtual or linear address sort laddr_nid, // nid of virtual or linear address offset_nid) // nid of segment offset in virtual or linear address space + dprintf(output_fd, "%lu slice 6 %lu %lu %lu\n", cursor_nid + 2, // nid of this line cursor_nid + 1, // nid of mapped virtual or linear address paddr_space_size - 1 + laddr_alignment, // size of physical address space in bits - 1 + address alignment laddr_alignment) // 3 (or 2) virtual address alignment, or 0 for linear address + dprintf(output_fd, "%lu ite 6 %lu %lu %lu\n", cursor_nid + 3, // nid of this line cursor_nid, // nid of segment check cursor_nid + 2, // nid of physical address flow_nid); // nid of physical address in other segment return cursor_nid + 3; // nid of physical address } uint64_t model_physical_address(uint64_t cursor_nid, uint64_t vaddr_nid) { uint64_t laddr_nid; if (linear_address_space) { w = w + dprintf(output_fd, "%lu slice 4 %lu 31 %lu\n", cursor_nid, // nid of this line vaddr_nid, // nid of virtual address vaddr_alignment); // 3 (or 2) virtual address alignment laddr_nid = cursor_nid; cursor_nid = cursor_nid + 1; } else laddr_nid = vaddr_nid; if (MMU) { if (linear_address_space) { // use linear segment dimensions cursor_nid = model_physical_address_in_segment(cursor_nid, laddr_nid, 40, 41, 40, 8); cursor_nid = model_physical_address_in_segment(cursor_nid + 1, laddr_nid, 42, 44, 46, cursor_nid); return model_physical_address_in_segment(cursor_nid + 1, laddr_nid, 45, 0, 47, cursor_nid); } else { // use virtual segment dimensions cursor_nid = model_physical_address_in_segment(cursor_nid, laddr_nid, 30, 31, 30, 8); cursor_nid = model_physical_address_in_segment(cursor_nid + 1, laddr_nid, 32, 34, 36, cursor_nid); return model_physical_address_in_segment(cursor_nid + 1, laddr_nid, 35, 50, 37, cursor_nid); } } else return laddr_nid; } uint64_t model_RAM_access(uint64_t cursor_nid, uint64_t address_nid, uint64_t RAM_address, uint64_t RAM_word, uint64_t RAM_word_flow_nid) { w = w // address == RAM address + dprintf(output_fd, "%lu eq 1 %lu %lu\n", cursor_nid, // nid of this line address_nid, // nid of virtual, linear, or physical address pc_nid(memory_nid, RAM_address)) // nid of virtual, linear, or physical RAM address // if paddr == RAM address access RAM word + dprintf(output_fd, "%lu ite 2 %lu %lu %lu\n", cursor_nid + 1, // nid of this line cursor_nid, // address == RAM address RAM_word, // nid of RAM word RAM_word_flow_nid); // nid of most recent access of RAM word at RAM address return cursor_nid + 1; } uint64_t compute_linear_address(uint64_t vaddr) { if (linear_address_space) return vaddr / WORDSIZE; else return vaddr; } uint64_t compute_physical_address(uint64_t vaddr) { if (MMU + RAM + MMURAM > 0) { if (vaddr >= data_start) if (vaddr < data_start + data_size) // vaddr in data segment vaddr = vaddr - data_start; else if (vaddr >= heap_start) if (vaddr < heap_start + heap_size) // vaddr in heap segment vaddr = vaddr - heap_start + data_size; else if (vaddr >= stack_start) if (vaddr <= stack_start + stack_size - WORDSIZE) // vaddr in stack segment vaddr = vaddr - stack_start + data_size + heap_size; else exit(EXITCODE_MODELINGERROR); else exit(EXITCODE_MODELINGERROR); else exit(EXITCODE_MODELINGERROR); else exit(EXITCODE_MODELINGERROR); // bypass linear address space return vaddr / WORDSIZE; } return compute_linear_address(vaddr); } // ----------------------------------------------------------------- // -------------------------- INTERPRETER -------------------------- // ----------------------------------------------------------------- uint64_t already_seen_instruction(uint64_t address) { if (statically_live_code != (uint64_t*) 0) return *(statically_live_code + (address - code_start) / INSTRUCTIONSIZE) == SEEN; else return 1; } uint64_t is_statically_live_instruction(uint64_t address) { if (statically_live_code != (uint64_t*) 0) return *(statically_live_code + (address - code_start) / INSTRUCTIONSIZE) == LIVE; else return 1; } void mark_instruction(uint64_t address, uint64_t mark) { *(statically_live_code + (address - code_start) / INSTRUCTIONSIZE) = mark; } uint64_t get_return_mark(uint64_t address) { return *(statically_live_return + (address - code_start) / INSTRUCTIONSIZE); } void mark_return(uint64_t address, uint64_t mark) { *(statically_live_return + (address - code_start) / INSTRUCTIONSIZE) = mark; } uint64_t mark_statically_live_code(uint64_t start_address, uint64_t callee_address, uint64_t entry_pc, uint64_t mark) { uint64_t next_pc; pc = start_address; while (1) { if (pc < code_start) // segfaulting pc exit(EXITCODE_MODELINGERROR); else if (pc >= code_start + code_size) // segfaulting pc exit(EXITCODE_MODELINGERROR); if (pc == entry_pc) // from here on mark as live mark = LIVE; if (mark == SEEN) if (already_seen_instruction(pc)) return mark; if (mark == LIVE) if (is_statically_live_instruction(pc)) return mark; mark_instruction(pc, mark); fetch(); decode(); next_pc = pc + INSTRUCTIONSIZE; if (is == ADDI) { if (rd == REG_A7) if (rs1 == REG_ZR) if (imm != 0) // assert: next instruction is ecall reg_a7 = imm; } else if (is == BEQ) // assert: only branching forward but not beyond jalr // mark true branch first, fast forwarding to jalr mark_statically_live_code(pc + imm, callee_address, entry_pc, mark); else if (is == JAL) { if (rd == REG_RA) { // assert: procedure call if (pc + imm == exit_wrapper_address) // exit wrapper does not return mark = DEAD; else mark = mark_statically_live_code(pc + imm, pc + imm, entry_pc, mark); if (mark == DEAD) { // procedure does not return if (callee_address == 0) // not in procedure or no path yet to jalr return DEAD; else return get_return_mark(callee_address); } } else next_pc = pc + imm; } else if (is == JALR) { // assert: jalr returning from procedure call mark_return(callee_address, mark); return mark; } else if (is == ECALL) { if (reg_a7 == SYSCALL_EXIT) { reg_a7 = 0; // assert: there is only one exit wrapper if (callee_address != 0) // actual exit wrapper call exit_wrapper_address = callee_address; return DEAD; } } pc = next_pc; } } void static_dead_code_elimination(uint64_t start_address, uint64_t entry_pc) { uint64_t saved_pc; statically_live_code = zmalloc(code_size / INSTRUCTIONSIZE * sizeof(uint64_t)); statically_live_return = zmalloc(code_size / INSTRUCTIONSIZE * sizeof(uint64_t)); saved_pc = pc; mark_statically_live_code(start_address, 0, entry_pc, SEEN); pc = saved_pc; } void translate_to_model() { // assert: 1 <= is <= number of RISC-U instructions if (is_statically_live_instruction(pc)) { if (is == ADDI) model_data_flow_addi(); else if (is == LOAD) model_data_flow_load(); else if (is == STORE) model_data_flow_store(); else if (is == ADD) model_data_flow_add(); else if (is == SUB) model_data_flow_sub(); else if (is == MUL) model_data_flow_mul(); else if (is == DIVU) model_data_flow_divu(); else if (is == REMU) model_data_flow_remu(); else if (is == SLTU) model_data_flow_sltu(); else if (is == BEQ) model_data_flow_beq(); else if (is == JAL) model_data_flow_jal(); // TODO: model jalr data flow else if (is == LUI) model_data_flow_lui(); else if (is == ECALL) model_data_flow_ecall(); } if (is == ADDI) model_control_flow_addi(); else if (is == LOAD) model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); else if (is == STORE) model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); else if (is == ADD) model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); else if (is == SUB) model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); else if (is == MUL) model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); else if (is == DIVU) model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); else if (is == REMU) model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); else if (is == SLTU) model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); else if (is == BEQ) model_control_flow_beq(); else if (is == JAL) model_control_flow_jal(); else if (is == JALR) model_control_flow_jalr(); else if (is == LUI) model_control_flow_lui_add_sub_mul_divu_remu_sltu_load_store(); else if (is == ECALL) model_control_flow_ecall(); } // ----------------------------------------------------------------- // ------------------------ MODEL GENERATOR ------------------------ // ----------------------------------------------------------------- uint64_t control_flow(uint64_t activate_nid, uint64_t control_flow_nid) { if (control_flow_nid == 10) // instruction proceeding here is first instruction to do so return activate_nid; else { // activate current instruction if instruction proceeding here is active w = w + dprintf(output_fd, "%lu ite 1 %lu 11 %lu\n", current_nid, // nid of this line activate_nid, // nid of pc flag of instruction proceeding here control_flow_nid); // nid of previously processed in-edge current_nid = current_nid + 1; return current_nid - 1; } } uint64_t control_flow_from_beq(uint64_t from_address, uint64_t condition_nid, uint64_t control_flow_nid) { w = w // is beq active and its condition true or false? + dprintf(output_fd, "%lu and 1 %lu %lu ; beq %lu[0x%lX]", current_nid, // nid of this line pc_nid(pcs_nid, from_address), // nid of pc flag of instruction proceeding here condition_nid, // nid of true or false beq condition from_address, from_address) // address of instruction proceeding here + print_code_line_number_for_instruction(from_address, code_start) + dprintf(output_fd, "\n"); current_nid = current_nid + 1; // activate this instruction if beq is active and its condition is true (false) control_flow_nid = control_flow(current_nid - 1, control_flow_nid); return control_flow_nid; } uint64_t control_flow_from_jalr(uint64_t jalr_address, uint64_t condition_nid, uint64_t control_flow_nid) { w = w // is value of $ra register with LSB reset equal to address of this instruction? + dprintf(output_fd, "%lu not 2 21 ; jalr %lu[0x%lX]", current_nid, // nid of this line jalr_address, jalr_address) // address of instruction proceeding here + print_code_line_number_for_instruction(jalr_address, code_start) + dprintf(output_fd, "\n") + dprintf(output_fd, "%lu and 2 %lu %lu\n", current_nid + 1, // nid of this line reg_nids + REG_RA, // nid of current value of $ra register current_nid) // nid of not 1 + dprintf(output_fd, "%lu eq 1 %lu %lu\n", current_nid + 2, // nid of this line current_nid + 1, // nid of current value of $ra register with LSB reset condition_nid) // nid of address of this instruction (generated by jal) // is jalr active and the previous condition true or false? + dprintf(output_fd, "%lu and 1 %lu %lu\n", current_nid + 3, // nid of this line pc_nid(pcs_nid, jalr_address), // nid of pc flag of instruction proceeding here current_nid + 2); // nid of return address condition current_nid = current_nid + 4; // activate this instruction if jalr is active and its condition is true (false) control_flow_nid = control_flow(current_nid - 1, control_flow_nid); return control_flow_nid; } uint64_t control_flow_from_ecall(uint64_t from_address, uint64_t control_flow_nid) { w = w + dprintf(output_fd, "%lu state 1 kernel-mode-pc-flag-%lu[0x%lX]", current_nid, // nid of this line from_address, from_address) // address of instruction proceeding here + print_code_line_number_for_instruction(from_address, code_start) + dprintf(output_fd, "\n") + dprintf(output_fd, "%lu init 1 %lu 10 ; ecall is initially inactive\n", current_nid + 1, // nid of this line current_nid) // nid of kernel-mode pc flag of ecall + dprintf(output_fd, "%lu ite 1 %lu 60 %lu ; activate ecall and keep active while in kernel mode\n", current_nid + 2, // nid of this line current_nid, // nid of kernel-mode pc flag of ecall pc_nid(pcs_nid, from_address)) // nid of pc flag of instruction proceeding here + dprintf(output_fd, "%lu next 1 %lu %lu ; keep ecall active while in kernel mode\n", current_nid + 3, // nid of this line current_nid, // nid of kernel-mode pc flag of ecall current_nid + 2) // nid of previous line + dprintf(output_fd, "%lu and 1 %lu 62 ; ecall is active but not in kernel mode anymore\n", current_nid + 4, // nid of this line current_nid); // nid of kernel-mode pc flag of ecall current_nid = current_nid + 5; // activate this instruction if ecall is active but not in kernel mode anymore control_flow_nid = control_flow(current_nid - 1, control_flow_nid); return control_flow_nid; } void check_syscall_id() { w = w + dprintf(output_fd, "%lu not 1 %lu ; $a7 != SYSCALL_EXIT\n", current_nid, // nid of this line which_ecall_nid) // nid of $a7 == SYSCALL_EXIT + dprintf(output_fd, "%lu not 1 %lu ; $a7 != SYSCALL_READ\n", current_nid + 1, // nid of this line which_ecall_nid + 1) // nid of $a7 == SYSCALL_READ + dprintf(output_fd, "%lu not 1 %lu ; $a7 != SYSCALL_WRITE\n", current_nid + 2, // nid of this line which_ecall_nid + 2) // nid of $a7 == SYSCALL_WRITE + dprintf(output_fd, "%lu not 1 %lu ; $a7 != SYSCALL_OPENAT\n", current_nid + 3, // nid of this line which_ecall_nid + 3) // nid of $a7 == SYSCALL_OPENAT + dprintf(output_fd, "%lu not 1 %lu ; $a7 != SYSCALL_BRK\n", current_nid + 4, // nid of this line which_ecall_nid + 4) // nid of $a7 == SYSCALL_BRK + dprintf(output_fd, "%lu and 1 %lu %lu ; ... and $a7 != SYSCALL_READ\n", current_nid + 5, // nid of this line current_nid, // nid of $a7 != SYSCALL_EXIT current_nid + 1) // nid of $a7 != SYSCALL_READ + dprintf(output_fd, "%lu and 1 %lu %lu ; ... and $a7 != SYSCALL_WRITE\n", current_nid + 6, // nid of this line current_nid + 5, // nid of preceding line current_nid + 2) // nid of $a7 != SYSCALL_WRITE + dprintf(output_fd, "%lu and 1 %lu %lu ; ... and $a7 != SYSCALL_OPENAT\n", current_nid + 7, // nid of this line current_nid + 6, // nid of preceding line current_nid + 3) // nid of $a7 != SYSCALL_OPENAT + dprintf(output_fd, "%lu and 1 %lu %lu ; ... and $a7 != SYSCALL_BRK\n\n", current_nid + 8, // nid of this line current_nid + 7, // nid of preceding line current_nid + 4) // nid of $a7 != SYSCALL_BRK // if any ecall is active check if $a7 register contains invalid syscall id + dprintf(output_fd, "%lu and 1 %lu %lu ; ecall is active for invalid syscall id\n", current_nid + 9, // nid of this line ecall_flow_nid, // nid of most recent update of ecall activation current_nid + 8) // nid of invalid syscall id check + dprintf(output_fd, "%lu bad %lu b%lu ; invalid syscall id\n\n", current_nid + 10, // nid of this line current_nid + 9, // nid of preceding line bad_number); // bad number current_nid = current_nid + 11; bad_number = bad_number + 1; } void check_exit_code() { uint64_t exit_code_check_nid; if (bad_exit_code == 0) { w = w + dprintf(output_fd, "%lu neq 1 %lu 20 ; $a0 != zero exit code\n", current_nid, // nid of this line reg_nids + REG_A0); // nid of current value of $a0 register exit_code_check_nid = current_nid; current_nid = current_nid + 1; } else { w = w + dprintf(output_fd, "%lu constd 2 %ld ; non-zero exit code\n", current_nid, // nid of this line bad_exit_code) // value of non-zero exit code + dprintf(output_fd, "%lu eq 1 %lu %lu ; $a0 == non-zero exit code\n", current_nid + 1, // nid of this line reg_nids + REG_A0, // nid of current value of $a0 register current_nid); // nid of value of non-zero exit code exit_code_check_nid = current_nid + 1; current_nid = current_nid + 2; } w = w + dprintf(output_fd, "%lu and 1 %lu %lu ; exit ecall is active with non-zero exit code\n", current_nid, // nid of this line exit_ecall_nid, // nid of exit ecall is active exit_code_check_nid) // nid of exit code check + dprintf(output_fd, "%lu bad %lu b%lu ; non-zero exit code\n\n", current_nid + 1, // nid of this line current_nid, // nid of preceding line bad_number); // bad number current_nid = current_nid + 2; bad_number = bad_number + 1; } void check_division_by_zero(uint64_t division, uint64_t flow_nid) { w = w // check if divisor == 0 + dprintf(output_fd, "%lu eq 1 %lu 20\n", current_nid, // nid of this line flow_nid) // nid of divisor of most recent division or remainder + dprintf(output_fd, "%lu bad %lu b%lu ; ", current_nid + 1, // nid of this line current_nid, // nid of divisor == 0 bad_number); // bad number if (division) w = w + dprintf(output_fd, "division by zero\n\n"); else w = w + dprintf(output_fd, "remainder by zero\n\n"); current_nid = current_nid + 2; bad_number = bad_number + 1; } uint64_t check_addresses() { if (address_alignment_check) return 1; else if (segmentation_faults) return 1; else if (check_block_access) return 1; else return 0; } void generate_address_alignment_check(uint64_t flow_nid) { w = w // check if address of most recent memory access is word-aligned + dprintf(output_fd, "%lu and 2 %lu %lu ; reset all but %lu LSBs of address\n", current_nid, // nid of this line flow_nid, // nid of address of most recent memory access vaddr_mask_nid, // nid of bit mask vaddr_alignment) // virtual address alignment in bits + dprintf(output_fd, "%lu neq 1 %lu 20 ; %lu LSBs of address != 0 (address is not word-aligned)\n", current_nid + 1, // nid of this line current_nid, // nid of 3 (or 2) LSBs of address of most recent memory access vaddr_alignment) // virtual address alignment in bits + dprintf(output_fd, "%lu bad %lu b%lu ; word-unaligned memory access\n\n", current_nid + 2, // nid of this line current_nid + 1, // nid of previous check bad_number); // bad number current_nid = current_nid + 3; bad_number = bad_number + 1; } void generate_segmentation_faults(uint64_t flow_nid) { w = w + dprintf(output_fd, "%lu ult 1 %lu 30 ; address < start of data segment\n", current_nid, // nid of this line flow_nid) // nid of address of most recent memory access + dprintf(output_fd, "%lu bad %lu b%lu ; memory access below data segment\n", current_nid + 1, // nid of this line current_nid, // nid of previous check bad_number); // bad number current_nid = current_nid + 2; bad_number = bad_number + 1; w = w + dprintf(output_fd, "%lu ugte 1 %lu 31 ; address >= end of data segment\n", current_nid, // nid of this line flow_nid) // nid of address of most recent memory access + dprintf(output_fd, "%lu ult 1 %lu 32 ; address < start of heap segment\n", current_nid + 1, // nid of this line flow_nid) // nid of address of most recent memory access + dprintf(output_fd, "%lu and 1 %lu %lu\n", current_nid + 2, // nid of this line current_nid, // nid of >= check current_nid + 1) // nid of < check + dprintf(output_fd, "%lu bad %lu b%lu ; memory access in between data and heap segments\n", current_nid + 3, // nid of this line current_nid + 2, // nid of previous check bad_number); // bad number current_nid = current_nid + 4; bad_number = bad_number + 1; w = w + dprintf(output_fd, "%lu ugte 1 %lu %lu ; address >= current end of heap segment\n", current_nid, // nid of this line flow_nid, // nid of address of most recent memory access bump_pointer_nid) // nid of brk bump pointer + dprintf(output_fd, "%lu ult 1 %lu %lu ; address < current start of stack segment\n", current_nid + 1, // nid of this line flow_nid, // nid of address of most recent memory access reg_nids + REG_SP) // nid of current value of $sp register + dprintf(output_fd, "%lu and 1 %lu %lu\n", current_nid + 2, // nid of this line current_nid, // nid of >= check current_nid + 1) // nid of < check + dprintf(output_fd, "%lu bad %lu b%lu ; memory access in between current heap and stack segments\n", current_nid + 3, // nid of this line current_nid + 2, // nid of previous check bad_number); // bad number current_nid = current_nid + 4; bad_number = bad_number + 1; if (fixed_heap_segment) { w = w + dprintf(output_fd, "%lu ugte 1 %lu 34 ; address >= allowed end of heap segment\n", current_nid, // nid of this line flow_nid) // nid of address of most recent memory access + dprintf(output_fd, "%lu ult 1 %lu %lu ; address < current end of heap segment\n", current_nid + 1, // nid of this line flow_nid, // nid of address of most recent memory access bump_pointer_nid) // nid of brk bump pointer + dprintf(output_fd, "%lu and 1 %lu %lu\n", current_nid + 2, // nid of this line current_nid, // nid of >= check current_nid + 1) // nid of < check + dprintf(output_fd, "%lu bad %lu b%lu ; memory access in between allowed and current end of heap segment\n", current_nid + 3, // nid of this line current_nid + 2, // nid of previous check bad_number); // bad number current_nid = current_nid + 4; bad_number = bad_number + 1; } if (fixed_stack_segment) { w = w + dprintf(output_fd, "%lu ugte 1 %lu %lu ; address >= current start of stack segment\n", current_nid, // nid of this line flow_nid, // nid of address of most recent memory access reg_nids + REG_SP) // nid of current value of $sp register + dprintf(output_fd, "%lu ult 1 %lu 35 ; address < allowed start of stack segment\n", current_nid + 1, // nid of this line flow_nid) // nid of address of most recent memory access + dprintf(output_fd, "%lu and 1 %lu %lu\n", current_nid + 2, // nid of this line current_nid, // nid of >= check current_nid + 1) // nid of < check + dprintf(output_fd, "%lu bad %lu b%lu ; memory access in between current and allowed start of stack segment\n", current_nid + 3, // nid of this line current_nid + 2, // nid of previous check bad_number); // bad number current_nid = current_nid + 4; bad_number = bad_number + 1; } w = w + dprintf(output_fd, "%lu ugt 1 %lu 50 ; address > highest address in 32-bit virtual address space (4GB)\n", current_nid, // nid of this line flow_nid) // nid of address of most recent memory access + dprintf(output_fd, "%lu bad %lu b%lu ; memory access above stack segment\n\n", current_nid + 1, // nid of this line current_nid, // nid of previous check bad_number); // bad number current_nid = current_nid + 2; bad_number = bad_number + 1; } void generate_block_access_check(uint64_t flow_nid, uint64_t lo_flow_nid, uint64_t up_flow_nid) { w = w // check if address of most recent memory access < current lower bound + dprintf(output_fd, "%lu ult 1 %lu %lu\n", current_nid, // nid of this line flow_nid, // nid of address of most recent memory access lo_flow_nid) // nid of current lower bound on memory addresses + dprintf(output_fd, "%lu bad %lu b%lu ; memory access below lower bound\n", current_nid + 1, // nid of this line current_nid, // nid of previous check bad_number); // bad number current_nid = current_nid + 2; bad_number = bad_number + 1; w = w // check if address of most recent memory access > current upper bound + dprintf(output_fd, "%lu ugt 1 %lu %lu\n", current_nid, // nid of this line flow_nid, // nid of address of most recent memory access up_flow_nid) // nid of current upper bound on memory addresses + dprintf(output_fd, "%lu bad %lu b%lu ; memory access above upper bound\n\n", current_nid + 1, // nid of this line current_nid, // nid of previous check bad_number); // bad number current_nid = current_nid + 2; bad_number = bad_number + 1; } uint64_t number_of_bits(uint64_t n) { if (n > 1) return log_two(n * 2 - 1); else if (n > 0) return 1; else return 0; } void beator(uint64_t entry_pc) { uint64_t i; uint64_t memory_word; uint64_t code_nid; uint64_t control_nid; uint64_t condition_nid; uint64_t data_flow_nid; uint64_t control_flow_nid; uint64_t* in_edge; uint64_t from_instruction; uint64_t from_address; uint64_t jalr_address; w = w + dprintf(output_fd, "; %s\n\n", SELFIE_URL) + dprintf(output_fd, "; BTOR2 %s generated by %s\n", model_name, selfie_name); if (syscall_id_check == 0) w = w + dprintf(output_fd, "; with %s\n", no_syscall_id_check_option); if (exit_code_check == 0) w = w + dprintf(output_fd, "; with %s\n", no_exit_code_check_option); if (division_by_zero_check == 0) w = w + dprintf(output_fd, "; with %s\n", no_division_by_zero_check_option); if (address_alignment_check == 0) w = w + dprintf(output_fd, "; with %s\n", no_address_alignment_check_option); if (segmentation_faults == 0) w = w + dprintf(output_fd, "; with %s\n", no_segmentation_faults_option); if (linear_address_space) w = w + dprintf(output_fd, "; with %s\n", linear_address_space_option); if (fixed_heap_segment) w = w + dprintf(output_fd, "; with %s %lu\n", heap_allowance_option, heap_allowance); if (fixed_stack_segment) w = w + dprintf(output_fd, "; with %s %lu\n", stack_allowance_option, stack_allowance); if (MMU) w = w + dprintf(output_fd, "; with %s\n", MMU_option); if (RAM) w = w + dprintf(output_fd, "; with %s\n", RAM_option); if (MMURAM) w = w + dprintf(output_fd, "; with %s\n", MMURAM_option); if (check_block_access) w = w + dprintf(output_fd, "; with %s\n", check_block_access_option); w = w + dprintf(output_fd, "\n; RISC-V code obtained from %s and invoked as:", binary_name); i = 0; while (i < number_of_remaining_arguments()) { w = w + dprintf(output_fd, " %s", (char*) *(remaining_arguments() + i)); i = i + 1; } if (constant_propagation) w = w + dprintf(output_fd, "\n; with %s", constant_propagation_option); w = w + dprintf(output_fd, "\n\n"); w = w + dprintf(output_fd, "1 sort bitvec 1 ; Boolean\n") + dprintf(output_fd, "2 sort bitvec %lu ; %lu-bit machine word\n", WORDSIZEINBITS, WORDSIZEINBITS); if (linear_address_space + MMU + RAM + MMURAM == 0) w = w + dprintf(output_fd, "3 sort array 2 2 ; %lu-bit physical memory\n", paddr_space_size); else if (linear_address_space) { laddr_space_size = laddr_space_size - vaddr_alignment; w = w + dprintf(output_fd, "\n4 sort bitvec %lu ; %lu-bit linear address\n", laddr_space_size, laddr_space_size); vaddr_sort_nid = 4; if (MMU + RAM + MMURAM == 0) { paddr_space_size = laddr_space_size; w = w + dprintf(output_fd, "5 sort array 4 2 ; %lu-bit physical memory\n", paddr_space_size); paddr_sort_nid = 4; memory_sort_nid = 5; } } // assert: value of stack pointer is word-aligned heap_start = get_heap_seg_start(current_context); heap_size = get_program_break(current_context) - heap_start + heap_allowance; stack_start = *(registers + REG_SP) - stack_allowance; stack_size = VIRTUALMEMORYSIZE * GIGABYTE - stack_start; if (MMU + RAM + MMURAM > 0) { paddr_space_size = number_of_bits((data_size + heap_size + stack_size) / WORDSIZE); w = w + dprintf(output_fd, "\n6 sort bitvec %lu ; %lu-bit physical address\n", paddr_space_size, paddr_space_size); paddr_sort_nid = 6; if (RAM + MMURAM == 0) { w = w + dprintf(output_fd, "7 sort array 6 2 ; %lu-bit physical memory (%luB)\n", paddr_space_size, data_size + heap_size + stack_size); memory_sort_nid = 7; } else memory_sort_nid = 0; // unused if (MMURAM == 0) // nid used if MMU cannot match address w = w + dprintf(output_fd, "8 zero 6\n"); } w = w + dprintf(output_fd, "\n; %luB total memory, %luB data, %luB heap (%luB,%luB), %luB stack (%luB,%luB)\n\n", data_size + heap_size + stack_size, data_size, heap_size, heap_size - heap_allowance, heap_allowance, stack_size, stack_size - stack_allowance, stack_allowance) + dprintf(output_fd, "10 zero 1\n11 one 1\n\n") + dprintf(output_fd, "20 zero 2\n21 one 2\n22 constd 2 2\n23 constd 2 3\n24 constd 2 4\n"); if (IS64BITTARGET) w = w + dprintf(output_fd, "25 constd 2 5\n26 constd 2 6\n27 constd 2 7\n28 constd 2 8\n"); w = w + dprintf(output_fd, "\n; start of data segment in %lu-bit virtual memory\n", vaddr_space_size) + dprintf(output_fd, "30 constd 2 %lu ; 0x%lX\n", data_start, data_start) + dprintf(output_fd, "; end of data segment in %lu-bit virtual memory\n", vaddr_space_size) + dprintf(output_fd, "31 constd 2 %lu ; 0x%lX\n\n", data_start + data_size, data_start + data_size) + dprintf(output_fd, "; start of heap segment in %lu-bit virtual memory (initial program break)\n", vaddr_space_size) + dprintf(output_fd, "32 constd 2 %lu ; 0x%lX\n", heap_start, heap_start) + dprintf(output_fd, "; current end of heap segment in %lu-bit virtual memory (current program break)\n", vaddr_space_size) + dprintf(output_fd, "33 constd 2 %lu ; 0x%lX\n\n", get_program_break(current_context), get_program_break(current_context)); if (fixed_heap_segment) w = w + dprintf(output_fd, "; allowed end of heap segment in %lu-bit virtual memory (with %luB allowance)\n", vaddr_space_size, heap_allowance) + dprintf(output_fd, "34 constd 2 %lu ; 0x%lX\n", heap_start + heap_size, heap_start + heap_size) + dprintf(output_fd, "; allowed start of stack segment in %lu-bit virtual memory (with %luB allowance)\n", vaddr_space_size, stack_allowance) + dprintf(output_fd, "35 constd 2 %lu ; 0x%lX\n\n", stack_start, stack_start); if (MMU) w = w + dprintf(output_fd, "; offset of heap segment in %lu-bit virtual memory\n", vaddr_space_size) + dprintf(output_fd, "36 constd 2 %lu ; 0x%lX\n", heap_start - data_size, heap_start - data_size) + dprintf(output_fd, "; offset of stack segment in %lu-bit virtual memory\n", vaddr_space_size) + dprintf(output_fd, "37 constd 2 %lu ; 0x%lX\n\n", stack_start - data_size - heap_size, stack_start - data_size - heap_size); if (linear_address_space) { w = w + dprintf(output_fd, "; start of data segment in %lu-bit linear memory\n", laddr_space_size) + dprintf(output_fd, "40 constd 4 %lu ; 0x%lX\n", data_start / WORDSIZE, data_start / WORDSIZE) + dprintf(output_fd, "; end of data segment in %lu-bit linear memory\n", laddr_space_size) + dprintf(output_fd, "41 constd 4 %lu ; 0x%lX\n\n", (data_start + data_size) / WORDSIZE, (data_start + data_size) / WORDSIZE) + dprintf(output_fd, "; start of heap segment in %lu-bit linear memory (initial program break)\n", laddr_space_size) + dprintf(output_fd, "42 constd 4 %lu ; 0x%lX\n", heap_start / WORDSIZE, heap_start / WORDSIZE) + dprintf(output_fd, "; current end of heap segment in %lu-bit linear memory (current program break)\n", laddr_space_size) + dprintf(output_fd, "43 constd 4 %lu ; 0x%lX\n\n", get_program_break(current_context) / WORDSIZE, get_program_break(current_context) / WORDSIZE); if (fixed_heap_segment) w = w + dprintf(output_fd, "; allowed end of heap segment in %lu-bit linear memory (with %luB allowance)\n", laddr_space_size, heap_allowance) + dprintf(output_fd, "44 constd 4 %lu ; 0x%lX\n", (heap_start + heap_size) / WORDSIZE, (heap_start + heap_size) / WORDSIZE) + dprintf(output_fd, "; allowed start of stack segment in %lu-bit linear memory (with %luB allowance)\n", laddr_space_size, stack_allowance) + dprintf(output_fd, "45 constd 4 %lu ; 0x%lX\n\n", stack_start / WORDSIZE, stack_start / WORDSIZE); if (MMU) w = w + dprintf(output_fd, "; offset of heap segment in %lu-bit linear memory\n", laddr_space_size) + dprintf(output_fd, "46 constd 4 %lu ; 0x%lX\n", (heap_start - data_size) / WORDSIZE, (heap_start - data_size) / WORDSIZE) + dprintf(output_fd, "; offset of stack segment in %lu-bit linear memory\n", laddr_space_size) + dprintf(output_fd, "47 constd 4 %lu ; 0x%lX\n\n", (stack_start - data_size - heap_size) / WORDSIZE, (stack_start - data_size - heap_size) / WORDSIZE); } w = w // avoiding 2^32 for 32-bit version + dprintf(output_fd, "; highest address in 32-bit virtual address space (4GB)\n\n") + dprintf(output_fd, "50 constd 2 %lu ; 0x%lX\n\n", HIGHESTVIRTUALADDRESS, HIGHESTVIRTUALADDRESS) + dprintf(output_fd, "; kernel-mode flag\n\n") + dprintf(output_fd, "60 state 1 kernel-mode\n") + dprintf(output_fd, "61 init 1 60 10 kernel-mode ; initial value is false\n") + dprintf(output_fd, "62 not 1 60\n\n") + dprintf(output_fd, "; unsigned-extended inputs for byte-wise reading\n\n"); w = w + dprintf(output_fd, "71 sort bitvec 8 ; 1 byte\n") + dprintf(output_fd, "72 sort bitvec 16 ; 2 bytes\n") + dprintf(output_fd, "73 sort bitvec 24 ; 3 bytes\n"); if (IS64BITTARGET) w = w + dprintf(output_fd, "74 sort bitvec 32 ; 4 bytes\n") + dprintf(output_fd, "75 sort bitvec 40 ; 5 bytes\n") + dprintf(output_fd, "76 sort bitvec 48 ; 6 bytes\n") + dprintf(output_fd, "77 sort bitvec 56 ; 7 bytes\n"); w = w + dprintf(output_fd, "\n81 input 71 1-byte-input\n") + dprintf(output_fd, "82 input 72 2-byte-input\n") + dprintf(output_fd, "83 input 73 3-byte-input\n"); if (IS64BITTARGET) w = w + dprintf(output_fd, "84 input 74 4-byte-input\n") + dprintf(output_fd, "85 input 75 5-byte-input\n") + dprintf(output_fd, "86 input 76 6-byte-input\n") + dprintf(output_fd, "87 input 77 7-byte-input\n\n") + dprintf(output_fd, "91 uext 2 81 56 ; uext-1-byte-input\n") + dprintf(output_fd, "92 uext 2 82 48 ; uext-2-byte-input\n") + dprintf(output_fd, "93 uext 2 83 40 ; uext-3-byte-input\n") + dprintf(output_fd, "94 uext 2 84 32 ; uext-4-byte-input\n") + dprintf(output_fd, "95 uext 2 85 24 ; uext-5-byte-input\n") + dprintf(output_fd, "96 uext 2 86 16 ; uext-6-byte-input\n") + dprintf(output_fd, "97 uext 2 87 8 ; uext-7-byte-input\n") + dprintf(output_fd, "98 input 2 8-byte-input\n\n"); else w = w + dprintf(output_fd, "\n91 uext 2 81 24 ; uext-1-byte-input\n") + dprintf(output_fd, "92 uext 2 82 16 ; uext-2-byte-input\n") + dprintf(output_fd, "93 uext 2 83 8 ; uext-3-byte-input\n") + dprintf(output_fd, "94 input 2 4-byte-input\n\n"); w = w + dprintf(output_fd, "; 32 %lu-bit general-purpose registers\n\n", WORDSIZEINBITS) + dprintf(output_fd, "; non-zero initial register values\n"); i = 0; while (i < NUMBEROFREGISTERS) { if (i == 0) w = w + dprintf(output_fd, "\n"); else if (*(registers + i) != 0) w = w + dprintf(output_fd, "%lu constd 2 %lu ; 0x%lX for %s\n", reg_value_nids + i, // nid of this line sign_shrink(*(registers + i), WORDSIZEINBITS), // unsigned initial value *(registers + i), // initial value in hexadecimal as comment get_register_name(i)); // register name as comment i = i + 1; } w = w + dprintf(output_fd, "\n; registers\n"); reg_flow_nids = smalloc(3 * NUMBEROFREGISTERS * sizeof(uint64_t*)); i = 0; while (i < NUMBEROFREGISTERS) { *(reg_flow_nids + i) = reg_nids + i; if (i == 0) w = w + dprintf(output_fd, "\n%lu zero 2 %s ; register $0 is always 0\n", *(reg_flow_nids + i), // nid of this line get_register_name(i)); // register name else w = w + dprintf(output_fd, "%lu state 2 %s ; register $%lu\n", *(reg_flow_nids + i), // nid of this line get_register_name(i), // register name i); // register index as comment i = i + 1; } if (check_block_access) while (i < 3 * NUMBEROFREGISTERS) { *(reg_flow_nids + i) = reg_nids + i; if (i == LO_FLOW) w = w + dprintf(output_fd, "\n%lu constd 2 %lu ; 0x%lX start of data segment\n", *(reg_flow_nids + i), // nid of this line data_start, // start of data segment data_start); // start of data segment in hexadecimal else if (i == UP_FLOW) w = w + dprintf(output_fd, "\n%lu constd 2 %lu ; 0x%lX highest address in 32-bit virtual address space (4GB)\n", *(reg_flow_nids + i), // nid of this line HIGHESTVIRTUALADDRESS, // highest address in 32-bit virtual address space (4GB) HIGHESTVIRTUALADDRESS); // highest address in 32-bit virtual address space (4GB) in hexadecimal else { w = w + dprintf(output_fd, "%lu state 2 ", *(reg_flow_nids + i)); if (i < LO_FLOW + NUMBEROFREGISTERS) w = w + dprintf(output_fd, "lo-%s ; lower bound on $%lu\n", get_register_name(i % NUMBEROFREGISTERS), // register name i % NUMBEROFREGISTERS); // register index as comment else if (i < UP_FLOW + NUMBEROFREGISTERS) w = w + dprintf(output_fd, "up-%s ; upper bound on $%lu\n", get_register_name(i % NUMBEROFREGISTERS), // register name i % NUMBEROFREGISTERS); // register index as comment } i = i + 1; } w = w + dprintf(output_fd, "\n; initializing registers\n"); i = 0; while (i < NUMBEROFREGISTERS) { if (i == 0) w = w + dprintf(output_fd, "\n"); else if (*(registers + i) == 0) w = w + dprintf(output_fd, "%lu init 2 %lu 20 %s ; initial value is 0\n", reg_init_nids + i, // nid of this line reg_nids + i, // nid of to-be-initialized register get_register_name(i)); // register name as comment else w = w + dprintf(output_fd, "%lu init 2 %lu %lu %s ; initial value is %lu\n", reg_init_nids + i, // nid of this line reg_nids + i, // nid of to-be-initialized register reg_value_nids + i, // nid of unsigned initial value get_register_name(i), // register name as comment sign_shrink(*(registers + i), WORDSIZEINBITS)); // unsigned initial value i = i + 1; } if (check_block_access) while (i < 3 * NUMBEROFREGISTERS) { if (i % NUMBEROFREGISTERS == 0) w = w + dprintf(output_fd, "\n"); else if (i < LO_FLOW + NUMBEROFREGISTERS) w = w + dprintf(output_fd, "%lu init 2 %lu 30 %s ; initial value is start of data segment\n", reg_init_nids + i, // nid of this line reg_nids + i, // nid of to-be-initialized register get_register_name(i % NUMBEROFREGISTERS)); // register name as comment else if (i < UP_FLOW + NUMBEROFREGISTERS) w = w + dprintf(output_fd, "%lu init 2 %lu 50 %s ; initial value is highest address in 32-bit virtual address space (4GB)\n", reg_init_nids + i, // nid of this line reg_nids + i, // nid of to-be-initialized register get_register_name(i % NUMBEROFREGISTERS)); // register name as comment i = i + 1; } w = w + dprintf(output_fd, "\n; %lu-bit program counter encoded in Boolean flags\n\n", WORDSIZEINBITS); pcs_nid = ten_to_the_power_of(log_ten(heap_start + heap_size + stack_size) + PC_NID_DIGITS); pc = code_start; while (pc < code_start + code_size) { current_nid = pc_nid(pcs_nid, pc); if (is_statically_live_instruction(pc)) { // pc flag of current instruction w = w + dprintf(output_fd, "%lu state 1 pc=0x%lX\n", current_nid, pc); if (pc == entry_pc) // set pc here by initializing pc flag of instruction to true w = w + dprintf(output_fd, "%lu init 1 %lu 11 ; initial program counter\n", current_nid + 1, // nid of this line current_nid); // nid of pc flag of current instruction else // initialize all other pc flags to false w = w + dprintf(output_fd, "%lu init 1 %lu 10\n", current_nid + 1, // nid of this line current_nid); // nid of pc flag of current instruction } else w = w + dprintf(output_fd, "; %lu unreachable state at %lX\n", current_nid, pc); pc = pc + INSTRUCTIONSIZE; } pc = data_start; if (RAM + MMURAM == 0) { current_nid = pc_nid(pcs_nid, pc); memory_nid = current_nid; w = w + dprintf(output_fd, "\n%lu state %lu memory-dump\n\n", current_nid, memory_sort_nid); data_flow_nid = current_nid; } else { memory_nid = pcs_nid * 2; current_nid = memory_nid; w = w + dprintf(output_fd, "\n; %lu-bit physical memory\n\n", paddr_space_size); data_flow_nid = 0; RAM_write_flow_nid = smalloc((data_size + heap_size + stack_size) / WORDSIZE * sizeof(uint64_t)); } w = w + dprintf(output_fd, "; data segment\n\n"); // assert: data segment is not empty while (pc <= VIRTUALMEMORYSIZE * GIGABYTE - WORDSIZE) { if (pc == data_start + data_size) { pc = heap_start; if (heap_size > 0) w = w + dprintf(output_fd, "\n; heap segment\n\n"); } if (pc == heap_start + heap_size) { // assert: stack pointer <= HIGHESTVIRTUALADDRESS pc = stack_start; w = w + dprintf(output_fd, "\n; stack segment\n\n"); // assert: stack segment is not empty } if (RAM + MMURAM > 0) current_nid = pc_nid(memory_nid, compute_physical_address(pc)); else if (current_nid == memory_nid) // account for nid offset by 1 of memory-dump state current_nid = current_nid + 1; else if (pc < stack_start) current_nid = pc_nid(pcs_nid, pc); else current_nid = pc_nid(pcs_nid, pc - stack_start + heap_start + heap_size); // address in data, heap, or stack segment if (MMURAM == 0) w = w + dprintf(output_fd, "%lu constd %lu %lu ; 0x%lX paddr, 0x%lX vaddr\n", current_nid, // nid of this line paddr_sort_nid, // nid of physical address sort compute_physical_address(pc), // physical address of current machine word compute_physical_address(pc), // physical address of current machine word in hexadecimal as comment pc); // virtual address of current machine word in hexadecimal as comment else w = w + dprintf(output_fd, "%lu constd %lu %lu ; 0x%lX laddr, 0x%lX vaddr\n", current_nid, // nid of this line vaddr_sort_nid, // nid of virtual or linear address sort compute_linear_address(pc), // virtual or linear address of current machine word compute_linear_address(pc), // virtual or linear address of current machine word in hexadecimal as comment pc); // virtual address of current machine word in hexadecimal as comment if (is_virtual_address_mapped(get_pt(current_context), pc)) memory_word = load_virtual_memory(get_pt(current_context), pc); else // memory allocated but not yet mapped is zeroed memory_word = 0; if (memory_word == 0) { // load memory word == 0 if (RAM + MMURAM > 0) { // implementing memory word as state variable, after constd for paddr w = w // wasting one nid so that state variables have the same offset + dprintf(output_fd, "%lu state 2 RAM-word-%lu\n", current_nid + 2, // nid of this line compute_physical_address(pc)) // physical address of current machine word // initialize memory word in RAM with zero + dprintf(output_fd, "%lu init 2 %lu 20\n", current_nid + 3, // nid of this line current_nid + 2); // nid of memory word in RAM *(RAM_write_flow_nid + compute_physical_address(pc)) = current_nid + 2; // account for last declared and initialized memory word, see below data_flow_nid = current_nid + 4; } else { w = w + dprintf(output_fd, "%lu write %lu %lu %lu 20\n", current_nid + 1, // nid of this line memory_sort_nid, // nid of physical memory sort data_flow_nid, // nid of most recent update of memory current_nid); // nid of address of current memory word data_flow_nid = current_nid + 1; } } else { // load non-zero memory word, use sign if (RAM + MMURAM > 0) { w = w + dprintf(output_fd, "%lu constd 2 %lu ; 0x%lX word\n", current_nid + 1, // nid of this line memory_word, memory_word) // value of memory word at current address // implementing memory word as state variable, after constd for paddr + dprintf(output_fd, "%lu state 2 RAM-word-%lu\n", current_nid + 2, // nid of this line compute_physical_address(pc)) // physical address of current machine word // initialize memory word in RAM with non-zero value + dprintf(output_fd, "%lu init 2 %lu %lu\n", current_nid + 3, // nid of this line current_nid + 2, // nid of memory word in RAM current_nid + 1); // nid of value of memory word at current address *(RAM_write_flow_nid + compute_physical_address(pc)) = current_nid + 2; // account for last declared and initialized memory word, see below data_flow_nid = current_nid + 4; } else { w = w + dprintf(output_fd, "%lu constd 2 %lu ; 0x%lX word\n", current_nid + 1, // nid of this line memory_word, memory_word) // value of memory word at current address + dprintf(output_fd, "%lu write %lu %lu %lu %lu\n", current_nid + 2, // nid of this line memory_sort_nid, // nid of physical memory sort data_flow_nid, // nid of most recent update of memory current_nid, // nid of address of current memory word current_nid + 1); // nid of value of memory word at current address data_flow_nid = current_nid + 2; } } if (pc + WORDSIZE == 0) // check 32-bit overflow to terminate loop pc = HIGHESTVIRTUALADDRESS; else pc = pc + WORDSIZE; } if (RAM + MMURAM == 0) { w = w + dprintf(output_fd, "\n; %lu-bit physical memory\n\n", paddr_space_size); memory_nid = pcs_nid * 2; current_nid = memory_nid; w = w + dprintf(output_fd, "%lu state %lu physical-memory ; data, heap, stack segments\n", current_nid, // nid of this line memory_sort_nid) // nid of physical memory sort + dprintf(output_fd, "%lu init %lu %lu %lu ; loading data, heap, stack segments into memory\n", current_nid + 1, // nid of this line memory_sort_nid, // nid of physical memory sort current_nid, // nid of physical memory data_flow_nid); // nid of most recent update of memory memory_flow_nid = current_nid; current_nid = current_nid + 2; } else // account for last declared and initialized memory word current_nid = data_flow_nid; if (check_block_access) { lo_memory_nid = current_nid; w = w + dprintf(output_fd, "\n%lu state %lu lower-bounds ; for checking address validity\n", current_nid, // nid of this line memory_sort_nid) // nid of physical memory sort + dprintf(output_fd, "%lu init %lu %lu 30 ; initializing lower bounds to start of data segment\n", current_nid + 1, // nid of this line memory_sort_nid, // nid of physical memory sort current_nid); // nid of lower bounds on addresses in memory lo_memory_flow_nid = current_nid; current_nid = current_nid + 2; up_memory_nid = current_nid; w = w + dprintf(output_fd, "%lu state %lu upper-bounds ; for checking address validity\n", current_nid, // nid of this line memory_sort_nid) // nid of physical memory sort + dprintf(output_fd, "%lu init %lu %lu 50 ; initializing upper bounds to highest address in 32-bit virtual address space (4GB)\n", current_nid + 1, // nid of this line memory_sort_nid, // nid of physical memory sort current_nid); // nid of upper bounds on addresses in memory up_memory_flow_nid = current_nid; } w = w + dprintf(output_fd, "\n; data flow\n\n"); code_nid = pcs_nid * 3; control_in = zmalloc(code_size / INSTRUCTIONSIZE * sizeof(uint64_t)); call_return = zmalloc(code_size / INSTRUCTIONSIZE * sizeof(uint64_t)); current_callee = code_start; estimated_return = code_start; pc = code_start; while (pc < code_start + code_size) { fetch(); decode(); current_nid = pc_nid(code_nid, pc); translate_to_model(); pc = pc + INSTRUCTIONSIZE; } w = w + dprintf(output_fd, "\n; syscalls\n\n"); current_nid = pcs_nid * 4; model_syscalls(current_nid); w = w + dprintf(output_fd, "\n; control flow\n\n"); control_nid = pcs_nid * 5; pc = code_start; while (pc < code_start + code_size) { current_nid = pc_nid(control_nid, pc); in_edge = (uint64_t*) *(control_in + (pc - code_start) / INSTRUCTIONSIZE); // nid of 1-bit 0 control_flow_nid = 10; while (in_edge != (uint64_t*) 0) { from_instruction = *(in_edge + 1); from_address = *(in_edge + 2); condition_nid = *(in_edge + 3); if (from_instruction == BEQ) { if (is_statically_live_instruction(from_address)) control_flow_nid = control_flow_from_beq(from_address, condition_nid, control_flow_nid); } else if (from_instruction == JALR) { jalr_address = *(call_return + (from_address - code_start) / INSTRUCTIONSIZE); if (jalr_address != 0) { if (is_statically_live_instruction(jalr_address)) control_flow_nid = control_flow_from_jalr(jalr_address, condition_nid, control_flow_nid); } else { // no jalr returning from jal found w = w + dprintf(output_fd, "; exit ecall wrapper call or runaway jal %lu[0x%lX]", from_address, from_address) + print_code_line_number_for_instruction(from_address, code_start) + dprintf(output_fd, "\n"); // this instruction may stay deactivated if there is no more in-edges } } else if (from_instruction == ECALL) { if (is_statically_live_instruction(from_address)) control_flow_nid = control_flow_from_ecall(from_address, control_flow_nid); } else { if (is_statically_live_instruction(from_address)) { if (from_instruction == JAL) w = w + dprintf(output_fd, "; jal "); else w = w + dprintf(output_fd, "; "); w = w + dprintf(output_fd, "%lu[0x%lX]", from_address, from_address) + print_code_line_number_for_instruction(from_address, code_start) + dprintf(output_fd, "\n"); // activate this instruction if instruction proceeding here is active control_flow_nid = control_flow(pc_nid(pcs_nid, from_address), control_flow_nid); } } in_edge = (uint64_t*) *in_edge; } if (is_statically_live_instruction(pc)) { if (control_flow_nid == 10) if (pc != entry_pc) { output_fd = 1; printf("%s: no in-edges at reachable instruction address %lu[0x%lX] detected\n", selfie_name, pc, pc); exit(EXITCODE_MODELINGERROR); } w = w // update pc flag of current instruction + dprintf(output_fd, "%lu next 1 %lu %lu ; ->%lu[0x%lX]", current_nid, // nid of this line pc_nid(pcs_nid, pc), // nid of pc flag of current instruction control_flow_nid, // nid of most recently processed in-edge pc, pc) // address of current instruction + print_code_line_number_for_instruction(pc, code_start) + dprintf(output_fd, "\n"); } if (current_nid >= pc_nid(control_nid, pc) + PC_NID_FACTOR * 4) { // the instruction at pc is reachable by too many other instructions // report the error on the console output_fd = 1; printf("%s: cannot handle %lu in-edges at instruction address %lu[0x%lX]\n", selfie_name, current_nid - pc_nid(control_nid, pc), pc, pc); exit(EXITCODE_MODELINGERROR); } pc = pc + INSTRUCTIONSIZE; } w = w + dprintf(output_fd, "\n; updating registers\n"); current_nid = pcs_nid * 6; i = 0; while (i < NUMBEROFREGISTERS) { if (i == 0) w = w + dprintf(output_fd, "\n"); else w = w + dprintf(output_fd, "%lu next 2 %lu %lu %s ; register $%lu\n", current_nid + i, // nid of this line reg_nids + i, // nid of register *(reg_flow_nids + i), // nid of most recent update to register get_register_name(i), // register name i); // register index as comment i = i + 1; } if (check_block_access) while (i < 3 * NUMBEROFREGISTERS) { if (i % NUMBEROFREGISTERS == 0) w = w + dprintf(output_fd, "\n"); else { w = w + dprintf(output_fd, "%lu next 2 %lu %lu ", current_nid + i, // nid of this line reg_nids + i, // nid of register *(reg_flow_nids + i)); // nid of most recent update to register if (i < LO_FLOW + NUMBEROFREGISTERS) w = w + dprintf(output_fd, "lo-%s ; lower bound on $%lu\n", get_register_name(i % NUMBEROFREGISTERS), // register name i % NUMBEROFREGISTERS); // register index as comment else if (i < UP_FLOW + NUMBEROFREGISTERS) w = w + dprintf(output_fd, "up-%s ; upper bound on $%lu\n", get_register_name(i % NUMBEROFREGISTERS), // register name i % NUMBEROFREGISTERS); // register index as comment } i = i + 1; } w = w + dprintf(output_fd, "\n; updating %lu-bit physical memory\n\n", paddr_space_size); current_nid = pcs_nid * 7; if (RAM + MMURAM == 0) { w = w + dprintf(output_fd, "%lu next %lu %lu %lu physical-memory\n", current_nid, // nid of this line memory_sort_nid, // nid of physical memory sort memory_nid, // nid of physical memory memory_flow_nid); // nid of most recent write to memory current_nid = current_nid + 1; } else { i = 0; while (i < (data_size + heap_size + stack_size) / WORDSIZE) { w = w + dprintf(output_fd, "%lu next 2 %lu %lu RAM-word-%lu\n", current_nid, // nid of this line pc_nid(memory_nid, i) + 2, // nid of RAM *(RAM_write_flow_nid + i), // nid of most recent write to RAM i); // physical address of machine word current_nid = current_nid + 1; i = i + 1; } } w = w + dprintf(output_fd, "\n"); if (check_block_access) w = w + dprintf(output_fd, "%lu next %lu %lu %lu lower-bounds\n", current_nid, // nid of this line memory_sort_nid, // nid of physical memory sort lo_memory_nid, // nid of lower bounds on addresses in memory lo_memory_flow_nid) // nid of most recent write to lower bounds on addresses in memory + dprintf(output_fd, "%lu next %lu %lu %lu upper-bounds\n\n", current_nid + 1, // nid of this line memory_sort_nid, // nid of physical memory sort up_memory_nid, // nid of upper bounds on addresses in memory up_memory_flow_nid); // nid of most recent write to upper bounds on addresses in memory current_nid = pcs_nid * 8; bad_number = 0; if (syscall_id_check) { w = w + dprintf(output_fd, "; checking syscall id\n\n"); check_syscall_id(); } if (exit_code_check) { w = w + dprintf(output_fd, "; checking exit code\n\n"); check_exit_code(); } if (division_by_zero_check) { w = w + dprintf(output_fd, "; checking division and remainder by zero\n\n"); check_division_by_zero(1, division_flow_nid); check_division_by_zero(0, remainder_flow_nid); } if (address_alignment_check) { w = w + dprintf(output_fd, "; checking address validity\n\n") + dprintf(output_fd, "; is start address of memory access word-aligned?\n\n"); generate_address_alignment_check(access_flow_start_nid); if (check_block_access) { w = w + dprintf(output_fd, "; is end address of memory access word-aligned?\n\n"); generate_address_alignment_check(access_flow_end_nid); } } if (segmentation_faults) { w = w + dprintf(output_fd, "; checking segmentation faults\n\n") + dprintf(output_fd, "; is start address of memory access in a valid segment?\n\n"); generate_segmentation_faults(access_flow_start_nid); if (check_block_access) { w = w + dprintf(output_fd, "; is end address of memory access in a valid segment?\n\n"); generate_segmentation_faults(access_flow_end_nid); } } if (check_block_access) { w = w + dprintf(output_fd, "; is start address of memory access in memory block?\n\n"); generate_block_access_check(access_flow_start_nid, lo_flow_start_nid, up_flow_start_nid); w = w + dprintf(output_fd, "; is end address of memory access in memory block?\n\n"); generate_block_access_check(access_flow_end_nid, lo_flow_end_nid, up_flow_end_nid); } // TODO: check validity of return addresses in jalr w = w + dprintf(output_fd, "; end of BTOR2 %s\n", model_name); } uint64_t selfie_model() { uint64_t entry_pc; uint64_t model_arguments; no_syscall_id_check_option = "--no-syscall-id-check"; no_exit_code_check_option = "--no-exit-code-check"; no_division_by_zero_check_option = "--no-division-by-zero-check"; no_address_alignment_check_option = "--no-address-alignment-check"; no_segmentation_faults_option = "--no-segmentation-faults"; check_block_access_option = "--check-block-access"; constant_propagation_option = "--constant-propagation"; linear_address_space_option = "--linear-address-space"; heap_allowance_option = "--heap-allowance"; stack_allowance_option = "--stack-allowance"; MMU_option = "--MMU"; RAM_option = "--RAM"; MMURAM_option = "--MMURAM"; if (IS64BITTARGET == 0) { // assert: 32-bit system vaddr_mask_nid = 23; vaddr_alignment = 2; vaddr_space_size = WORDSIZEINBITS; paddr_space_size = vaddr_space_size; } if (string_compare(argument, "-")) { if (number_of_remaining_arguments() > 0) { bad_exit_code = atoi(peek_argument(0)); model_arguments = 0; while (model_arguments == 0) { if (number_of_remaining_arguments() > 1) { if (string_compare(peek_argument(1), no_syscall_id_check_option)) { syscall_id_check = 0; get_argument(); } else if (string_compare(peek_argument(1), no_exit_code_check_option)) { exit_code_check = 0; get_argument(); } else if (string_compare(peek_argument(1), no_division_by_zero_check_option)) { division_by_zero_check = 0; get_argument(); } else if (string_compare(peek_argument(1), no_address_alignment_check_option)) { address_alignment_check = 0; get_argument(); } else if (string_compare(peek_argument(1), no_segmentation_faults_option)) { segmentation_faults = 0; get_argument(); } else if (string_compare(peek_argument(1), check_block_access_option)) { check_block_access = 1; get_argument(); } else if (string_compare(peek_argument(1), constant_propagation_option)) { constant_propagation = 1; get_argument(); } else if (string_compare(peek_argument(1), linear_address_space_option)) { linear_address_space = 1; get_argument(); } else if (string_compare(peek_argument(1), heap_allowance_option)) { fixed_heap_segment = 1; get_argument(); if (number_of_remaining_arguments() > 1) { heap_allowance = round_up(atoi(peek_argument(1)), WORDSIZE); get_argument(); } else exit(EXITCODE_BADARGUMENTS); } else if (string_compare(peek_argument(1), stack_allowance_option)) { fixed_stack_segment = 1; get_argument(); if (number_of_remaining_arguments() > 1) { stack_allowance = round_up(atoi(peek_argument(1)), WORDSIZE); get_argument(); } else exit(EXITCODE_BADARGUMENTS); } else if (string_compare(peek_argument(1), MMU_option)) { MMU = 1; fixed_heap_segment = 1; fixed_stack_segment = 1; get_argument(); } else if (string_compare(peek_argument(1), RAM_option)) { RAM = 1; MMU = 1; fixed_heap_segment = 1; fixed_stack_segment = 1; get_argument(); } else if (string_compare(peek_argument(1), MMURAM_option)) { MMURAM = 1; fixed_heap_segment = 1; fixed_stack_segment = 1; get_argument(); } else model_arguments = 1; } else model_arguments = 1; } if (MMURAM) { MMU = 0; RAM = 0; } if (segmentation_faults == 0) { if (MMU) printf("%s: warning: %s with %s\n", selfie_name, MMU_option, no_segmentation_faults_option); if (RAM) printf("%s: warning: %s with %s\n", selfie_name, RAM_option, no_segmentation_faults_option); if (MMURAM) printf("%s: warning: %s with %s\n", selfie_name, MMURAM_option, no_segmentation_faults_option); } if (code_size == 0) { printf("%s: nothing to model\n", selfie_name); return EXITCODE_BADARGUMENTS; } // use extension ".btor2" in name of SMT-LIB file model_name = replace_extension(binary_name, "-beaten", "btor2"); // assert: model_name is mapped and not longer than MAX_FILENAME_LENGTH model_fd = open_write_only(model_name, S_IRUSR_IWUSR_IRGRP_IROTH); if (signed_less_than(model_fd, 0)) { printf("%s: could not create model file %s\n", selfie_name, model_name); exit(EXITCODE_IOERROR); } reset_library(); reset_interpreter(); reset_profiler(); reset_microkernel(); init_memory(1); current_context = create_context(MY_CONTEXT, 0); // assert: number_of_remaining_arguments() > 0 boot_loader(current_context); entry_pc = get_pc(current_context); if (constant_propagation) { printf("%s: --------------------------------------------------------------------------------\n", selfie_name); printf("%s: constant propagation with ", selfie_name); exited_on_timeout = 0; exited_on_read = 0; TIMESLICE = 10000000; run = 1; propr(current_context); run = 0; if (exited_on_timeout) printf("%s: constant propagation could not find read call\n", selfie_name); else if (exited_on_read == 0) { printf("%s: constant propagation yields empty model\n", selfie_name); exit(EXITCODE_MODELINGERROR); } } restore_context(current_context); do_switch(current_context, TIMEROFF); static_dead_code_elimination(entry_pc, get_pc(current_context)); output_name = model_name; output_fd = model_fd; run = 0; model = 1; beator(get_pc(current_context)); model = 0; output_name = (char*) 0; output_fd = 1; printf("%s: %lu characters of model formulae written into %s\n", selfie_name, w, model_name); printf("%s: ################################################################################\n", selfie_name); return EXITCODE_NOERROR; } else return EXITCODE_BADARGUMENTS; } else return EXITCODE_BADARGUMENTS; } // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ---------------------- R U N T I M E ---------------------- // ----------------------------------------------------------------- // *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ *~*~ // ----------------------------------------------------------------- // ---------------------------- KERNEL ----------------------------- // ----------------------------------------------------------------- uint64_t handle_propr_system_call(uint64_t* context) { uint64_t a7; set_exception(context, EXCEPTION_NOEXCEPTION); set_ec_syscall(context, get_ec_syscall(context) + 1); a7 = *(get_regs(context) + REG_A7); if (a7 == SYSCALL_BRK) { if (is_gc_enabled(context)) implement_gc_brk(context); else implement_brk(context); } else if (a7 == SYSCALL_READ) { exited_on_read = 1; set_exit_code(context, sign_shrink(EXITCODE_NOERROR, SYSCALL_BITWIDTH)); return EXIT; } else if (a7 == SYSCALL_WRITE) implement_write(context); else if (a7 == SYSCALL_OPENAT) implement_openat(context); else if (a7 == SYSCALL_EXIT) { implement_exit(context); // TODO: exit only if all contexts have exited return EXIT; } else { printf("%s: unknown system call %lu\n", selfie_name, a7); set_exit_code(context, EXITCODE_UNKNOWNSYSCALL); return EXIT; } return DONOTEXIT; } uint64_t handle_propr_timer(uint64_t* context) { set_exception(context, EXCEPTION_NOEXCEPTION); set_ec_timer(context, get_ec_timer(context) + 1); exited_on_timeout = 1; return EXIT; } uint64_t handle_propr_exception(uint64_t* context) { uint64_t exception; exception = get_exception(context); if (exception == EXCEPTION_SYSCALL) return handle_propr_system_call(context); else if (exception == EXCEPTION_PAGEFAULT) return handle_page_fault(context); else if (exception == EXCEPTION_DIVISIONBYZERO) return handle_division_by_zero(context); else if (exception == EXCEPTION_TIMER) return handle_propr_timer(context); else { printf("%s: context %s threw uncaught exception: ", selfie_name, get_name(context)); print_exception(exception, get_fault(context)); println(); set_exit_code(context, EXITCODE_UNCAUGHTEXCEPTION); return EXIT; } } // ----------------------------------------------------------------- // --------------------- CONSTANT PROPAGATION ---------------------- // ----------------------------------------------------------------- uint64_t propr(uint64_t* to_context) { uint64_t timeout; uint64_t* from_context; printf("propr\n"); printf("%s: --------------------------------------------------------------------------------\n", selfie_name); timeout = TIMESLICE; while (1) { from_context = mipster_switch(to_context, timeout); if (get_parent(from_context) != MY_CONTEXT) { // switch to parent which is in charge of handling exceptions to_context = get_parent(from_context); timeout = TIMEROFF; } else if (handle_propr_exception(from_context) == EXIT) return get_exit_code(from_context); else { // TODO: scheduler should go here to_context = from_context; timeout = TIMESLICE; } } } // ----------------------------------------------------------------- // ----------------------------- MAIN ------------------------------ // ----------------------------------------------------------------- int main(int argc, char** argv) { uint64_t exit_code; init_selfie((uint64_t) argc, (uint64_t*) argv); init_library(); init_system(); init_target(); init_kernel(); exit_code = selfie(1); if (exit_code == EXITCODE_MOREARGUMENTS) exit_code = selfie_model(); return exit_selfie(exit_code, " - exit-code [ --check-block-access ] ..."); }