From 75ba0a23ebdd9d446ca3466fcfbd6f67c858220f Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Tue, 14 May 2019 20:24:32 +0800 Subject: [PATCH 1/2] feat: W^X initial implementation --- definitions/src/asm.rs | 7 +- definitions/src/generate_asm_constants.rs | 36 ++- definitions/src/lib.rs | 4 +- definitions/src/memory.rs | 6 + src/jit/machine.rs | 21 +- src/lib.rs | 21 +- src/machine/asm/cdefinitions_generated.h | 18 +- src/machine/asm/execute.S | 157 ++++----- src/machine/asm/mod.rs | 54 ++-- src/machine/mod.rs | 69 ++-- src/machine/trace.rs | 167 +--------- src/memory/flat.rs | 31 +- src/memory/mmu.rs | 370 ---------------------- src/memory/mod.rs | 48 ++- src/memory/sparse.rs | 35 +- src/memory/wxorx.rs | 128 ++++++++ tests/programs/invalidexec | Bin 4324 -> 0 bytes tests/programs/invalidexec.c | 5 - tests/programs/invalidwrite | Bin 4316 -> 0 bytes tests/programs/invalidwrite.c | 7 - tests/test_asm.rs | 4 +- tests/test_misc.rs | 6 +- tests/test_mmu.rs | 28 -- 23 files changed, 394 insertions(+), 828 deletions(-) create mode 100644 definitions/src/memory.rs delete mode 100644 src/memory/mmu.rs create mode 100644 src/memory/wxorx.rs delete mode 100755 tests/programs/invalidexec delete mode 100644 tests/programs/invalidexec.c delete mode 100755 tests/programs/invalidwrite delete mode 100644 tests/programs/invalidwrite.c delete mode 100644 tests/test_mmu.rs diff --git a/definitions/src/asm.rs b/definitions/src/asm.rs index aa7e36c7..4593cb44 100644 --- a/definitions/src/asm.rs +++ b/definitions/src/asm.rs @@ -1,4 +1,6 @@ -use crate::{instructions::Instruction, RISCV_GENERAL_REGISTER_NUMBER, RISCV_MAX_MEMORY}; +use crate::{ + instructions::Instruction, RISCV_GENERAL_REGISTER_NUMBER, RISCV_MAX_MEMORY, RISCV_PAGESIZE, +}; use std::alloc::{alloc_zeroed, Layout}; use std::cmp::min; @@ -11,6 +13,7 @@ pub const RET_ECALL: u8 = 2; pub const RET_EBREAK: u8 = 3; pub const RET_MAX_CYCLES_EXCEEDED: u8 = 4; pub const RET_OUT_OF_BOUND: u8 = 5; +pub const RET_INVALID_PERMISSION: u8 = 6; #[inline(always)] pub fn calculate_slot(addr: u64) -> usize { @@ -35,7 +38,7 @@ pub struct AsmCoreMachine { pub pc: u64, pub cycles: u64, pub max_cycles: u64, - pub elf_end: usize, + pub flags: [u8; RISCV_MAX_MEMORY / RISCV_PAGESIZE], pub memory: [u8; RISCV_MAX_MEMORY], pub traces: [Trace; TRACE_SIZE], } diff --git a/definitions/src/generate_asm_constants.rs b/definitions/src/generate_asm_constants.rs index 2f50a34b..d2479864 100644 --- a/definitions/src/generate_asm_constants.rs +++ b/definitions/src/generate_asm_constants.rs @@ -1,11 +1,12 @@ use ckb_vm_definitions::{ asm::{ - AsmCoreMachine, Trace, RET_DECODE_TRACE, RET_EBREAK, RET_ECALL, RET_MAX_CYCLES_EXCEEDED, - RET_OUT_OF_BOUND, TRACE_ITEM_LENGTH, + AsmCoreMachine, Trace, RET_DECODE_TRACE, RET_EBREAK, RET_ECALL, RET_INVALID_PERMISSION, + RET_MAX_CYCLES_EXCEEDED, RET_OUT_OF_BOUND, TRACE_ITEM_LENGTH, }, instructions::{Instruction, INSTRUCTION_OPCODE_NAMES}, + memory::{FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE, FLAG_WXORR_BIT}, registers::SP, - RISCV_MAX_MEMORY, + RISCV_MAX_MEMORY, RISCV_PAGESIZE, RISCV_PAGE_SHIFTS, }; use std::mem::{size_of, zeroed}; @@ -17,7 +18,14 @@ use std::mem::{size_of, zeroed}; // of this as a workaround to the problem that build.rs cannot depend on any // of its crate contents. fn main() { - println!("#define CKB_VM_ASM_RISCV_MAX_MEMORY {}", RISCV_MAX_MEMORY,); + println!("#define CKB_VM_ASM_RISCV_MAX_MEMORY {}", RISCV_MAX_MEMORY); + println!("#define CKB_VM_ASM_RISCV_PAGE_SHIFTS {}", RISCV_PAGE_SHIFTS); + println!("#define CKB_VM_ASM_RISCV_PAGE_SIZE {}", RISCV_PAGESIZE); + println!("#define CKB_VM_ASM_RISCV_PAGE_MASK {}", RISCV_PAGESIZE - 1); + println!( + "#define CKB_VM_ASM_RISCV_PAGES {}", + RISCV_MAX_MEMORY / RISCV_PAGESIZE + ); println!(); println!( @@ -34,11 +42,27 @@ fn main() { RET_MAX_CYCLES_EXCEEDED ); println!("#define CKB_VM_ASM_RET_OUT_OF_BOUND {}", RET_OUT_OF_BOUND); + println!( + "#define CKB_VM_ASM_RET_INVALID_PERMISSION {}", + RET_INVALID_PERMISSION + ); println!(); println!("#define CKB_VM_ASM_REGISTER_SP {}", SP); println!(); + println!("#define CKB_VM_ASM_MEMORY_FLAG_FREEZED {}", FLAG_FREEZED); + println!( + "#define CKB_VM_ASM_MEMORY_FLAG_EXECUTABLE {}", + FLAG_EXECUTABLE + ); + println!( + "#define CKB_VM_ASM_MEMORY_FLAG_WXORR_BIT {}", + FLAG_WXORR_BIT + ); + println!("#define CKB_VM_ASM_MEMORY_FLAG_WRITABLE {}", FLAG_WRITABLE); + println!(); + println!( "#define CKB_VM_ASM_TRACE_STRUCT_SIZE {}", size_of::() @@ -92,8 +116,8 @@ fn main() { (&m.max_cycles as *const u64 as usize) - m_address ); println!( - "#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_ELF_END {}", - (&m.elf_end as *const usize as usize) - m_address + "#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_FLAGS {}", + (&m.flags as *const u8 as usize) - m_address ); println!( "#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY {}", diff --git a/definitions/src/lib.rs b/definitions/src/lib.rs index 373484df..540a6c13 100644 --- a/definitions/src/lib.rs +++ b/definitions/src/lib.rs @@ -1,8 +1,10 @@ pub mod asm; pub mod instructions; +pub mod memory; pub mod registers; -pub const RISCV_PAGESIZE: usize = 1 << 12; +pub const RISCV_PAGE_SHIFTS: usize = 12; +pub const RISCV_PAGESIZE: usize = 1 << RISCV_PAGE_SHIFTS; pub const RISCV_GENERAL_REGISTER_NUMBER: usize = 32; // 4 MB pub const RISCV_MAX_MEMORY: usize = 4 << 20; diff --git a/definitions/src/memory.rs b/definitions/src/memory.rs new file mode 100644 index 00000000..72b382cc --- /dev/null +++ b/definitions/src/memory.rs @@ -0,0 +1,6 @@ +pub const FLAG_FREEZED: u8 = 0b01; +// CKB VM enforces W^X logic, if this flag is set, current memory page will +// be marked as executable, otherwise the page will be writable. +pub const FLAG_EXECUTABLE: u8 = 0b10; +pub const FLAG_WXORR_BIT: u8 = 0b10; +pub const FLAG_WRITABLE: u8 = (!FLAG_EXECUTABLE) & FLAG_WXORR_BIT; diff --git a/src/jit/machine.rs b/src/jit/machine.rs index 46d8106f..aa263095 100644 --- a/src/jit/machine.rs +++ b/src/jit/machine.rs @@ -45,7 +45,6 @@ impl AsmData { struct RustData { // Machine related data, should be cleared before each run memory: SparseMemory, - elf_end: usize, cycles: u64, max_cycles: Option, @@ -73,7 +72,6 @@ impl RustData { fn new(tracer: Box) -> Self { Self { memory: SparseMemory::default(), - elf_end: 0, cycles: 0, max_cycles: None, buffer: None, @@ -308,7 +306,6 @@ impl BaselineJitMachine { fn reset(&mut self) { self.asm_data.registers = [0; ASM_DATA_REGISTERS_SLOTS]; self.rust_data.memory = SparseMemory::::default(); - self.rust_data.elf_end = 0; self.rust_data.cycles = 0; self.rust_data.max_cycles = None; } @@ -348,14 +345,6 @@ impl CoreMachine for BaselineJitMachine { } impl SupportMachine for BaselineJitMachine { - fn elf_end(&self) -> usize { - self.rust_data.elf_end - } - - fn set_elf_end(&mut self, elf_end: usize) { - self.rust_data.elf_end = elf_end; - } - fn cycles(&self) -> u64 { self.rust_data.cycles } @@ -517,21 +506,17 @@ impl Machine for JitCompilingMachine { } impl Memory for JitCompilingMachine { - fn mmap( + fn init_pages( &mut self, _addr: usize, _size: usize, - _prot: u32, + _flags: u8, _source: Option, - _offset: usize, + _offset_from_addr: usize, ) -> Result<(), Error> { Err(Error::Unimplemented) } - fn munmap(&mut self, _addr: usize, _size: usize) -> Result<(), Error> { - Err(Error::Unimplemented) - } - fn store_byte(&mut self, _addr: usize, _size: usize, _value: u8) -> Result<(), Error> { Err(Error::Unimplemented) } diff --git a/src/lib.rs b/src/lib.rs index 5dbcf60f..d72ab6ec 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ pub use crate::{ trace::TraceMachine, CoreMachine, DefaultCoreMachine, DefaultMachine, DefaultMachineBuilder, InstructionCycleFunc, Machine, SupportMachine, }, - memory::{flat::FlatMemory, mmu::Mmu, sparse::SparseMemory, Memory}, + memory::{flat::FlatMemory, sparse::SparseMemory, wxorx::WXorXMemory, Memory}, syscalls::Syscalls, }; use bytes::Bytes; @@ -57,7 +57,24 @@ pub fn run + Default>( program: &Bytes, args: &[Bytes], ) -> Result { - let mut machine = TraceMachine::new(DefaultMachine::>::default()); + let mut machine = + TraceMachine::new(DefaultMachine::>>::default()); machine.load_program(program, args)?; machine.run() } + +#[cfg(test)] +mod tests { + use super::bits::power_of_2; + use super::*; + + #[test] + fn test_max_memory_must_be_multiple_of_pages() { + assert_eq!(RISCV_MAX_MEMORY % RISCV_PAGESIZE, 0); + } + + #[test] + fn test_page_size_be_power_of_2() { + assert!(power_of_2(RISCV_PAGESIZE)); + } +} diff --git a/src/machine/asm/cdefinitions_generated.h b/src/machine/asm/cdefinitions_generated.h index 72eaee7d..ddab627b 100644 --- a/src/machine/asm/cdefinitions_generated.h +++ b/src/machine/asm/cdefinitions_generated.h @@ -1,4 +1,8 @@ #define CKB_VM_ASM_RISCV_MAX_MEMORY 4194304 +#define CKB_VM_ASM_RISCV_PAGE_SHIFTS 12 +#define CKB_VM_ASM_RISCV_PAGE_SIZE 4096 +#define CKB_VM_ASM_RISCV_PAGE_MASK 4095 +#define CKB_VM_ASM_RISCV_PAGES 1024 #define CKB_VM_ASM_MAXIMUM_TRACE_ADDRESS_LENGTH 64 @@ -7,9 +11,15 @@ #define CKB_VM_ASM_RET_EBREAK 3 #define CKB_VM_ASM_RET_MAX_CYCLES_EXCEEDED 4 #define CKB_VM_ASM_RET_OUT_OF_BOUND 5 +#define CKB_VM_ASM_RET_INVALID_PERMISSION 6 #define CKB_VM_ASM_REGISTER_SP 2 +#define CKB_VM_ASM_MEMORY_FLAG_FREEZED 1 +#define CKB_VM_ASM_MEMORY_FLAG_EXECUTABLE 2 +#define CKB_VM_ASM_MEMORY_FLAG_WXORR_BIT 2 +#define CKB_VM_ASM_MEMORY_FLAG_WRITABLE 0 + #define CKB_VM_ASM_TRACE_STRUCT_SIZE 296 #define CKB_VM_ASM_TRACE_OFFSET_ADDRESS 0 #define CKB_VM_ASM_TRACE_OFFSET_LENGTH 8 @@ -17,14 +27,14 @@ #define CKB_VM_ASM_TRACE_OFFSET_INSTRUCTIONS 24 #define CKB_VM_ASM_TRACE_OFFSET_THREAD 160 -#define CKB_VM_ASM_ASM_CORE_MACHINE_STRUCT_SIZE 6619424 +#define CKB_VM_ASM_ASM_CORE_MACHINE_STRUCT_SIZE 6620440 #define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_REGISTERS 0 #define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_PC 256 #define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_CYCLES 264 #define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MAX_CYCLES 272 -#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_ELF_END 280 -#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY 288 -#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_TRACES 4194592 +#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_FLAGS 280 +#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY 1304 +#define CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_TRACES 4195608 #define CKB_VM_ASM_OP_UNLOADED 0 #define CKB_VM_ASM_OP_ADD 1 diff --git a/src/machine/asm/execute.S b/src/machine/asm/execute.S index e2d1fa45..6dd89bc8 100644 --- a/src/machine/asm/execute.S +++ b/src/machine/asm/execute.S @@ -84,6 +84,30 @@ #define MOV_RS2r_TO_RDX movq RS2r, %rdx #define MOV_RDX_TO_RS2r movq %rdx, RS2r +#define CHECK_WRITE_PERMISSION(address_reg, temp_reg1, temp_reg2, temp_reg2d, length) \ + movq address_reg, temp_reg1; \ + shr $CKB_VM_ASM_RISCV_PAGE_SHIFTS, temp_reg1; \ + cmp $CKB_VM_ASM_RISCV_PAGES, temp_reg1; \ + jae .exit_out_of_bound; \ + movzbl CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_FLAGS(MACHINE, temp_reg1), temp_reg2d; \ + and $CKB_VM_ASM_MEMORY_FLAG_WXORR_BIT, temp_reg2d; \ + cmp $CKB_VM_ASM_MEMORY_FLAG_WRITABLE, temp_reg2d; \ + jne .exit_invalid_permission; \ + addq $1, temp_reg1; \ + shl $CKB_VM_ASM_RISCV_PAGE_SHIFTS, temp_reg1; \ + movq address_reg, temp_reg2; \ + addq $length, temp_reg2; \ + cmp temp_reg1, temp_reg2; \ + jbe 1f; \ + shr $CKB_VM_ASM_RISCV_PAGE_SHIFTS, temp_reg1; \ + cmp $CKB_VM_ASM_RISCV_PAGES, temp_reg1; \ + jae .exit_out_of_bound; \ + movzbl CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_FLAGS(MACHINE, temp_reg1), temp_reg2d; \ + and $CKB_VM_ASM_MEMORY_FLAG_WXORR_BIT, temp_reg2d; \ + cmp $CKB_VM_ASM_MEMORY_FLAG_WRITABLE, temp_reg2d; \ + jne .exit_invalid_permission; \ +1: + #define ADDRESS_TO_SLOT_ADDRESS(r) \ shr $5, r; \ andq $8191, r; \ @@ -402,7 +426,9 @@ ckb_vm_x64_execute: DECODE_I movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 + movq RS1, TEMP1 + addq $1, TEMP1 + cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, TEMP1 jae .exit_out_of_bound movsbq CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1), RS1 WRITE_RD(RS1) @@ -412,7 +438,9 @@ ckb_vm_x64_execute: DECODE_I movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 + movq RS1, TEMP1 + addq $1, TEMP1 + cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, TEMP1 jae .exit_out_of_bound movzbq CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1), RS1 WRITE_RD(RS1) @@ -423,7 +451,9 @@ ckb_vm_x64_execute: DECODE_I movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 + movq RS1, TEMP1 + addq $8, TEMP1 + cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, TEMP1 jae .exit_out_of_bound movq CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1), RS1 WRITE_RD(RS1) @@ -433,7 +463,9 @@ ckb_vm_x64_execute: DECODE_I movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 + movq RS1, TEMP1 + addq $2, TEMP1 + cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, TEMP1 jae .exit_out_of_bound movswq CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1), RS1 WRITE_RD(RS1) @@ -443,7 +475,9 @@ ckb_vm_x64_execute: DECODE_I movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 + movq RS1, TEMP1 + addq $2, TEMP1 + cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, TEMP1 jae .exit_out_of_bound movzwq CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1), RS1 WRITE_RD(RS1) @@ -462,7 +496,9 @@ ckb_vm_x64_execute: DECODE_I movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 + movq RS1, TEMP1 + addq $4, TEMP1 + cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, TEMP1 jae .exit_out_of_bound movslq CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1), RS1 WRITE_RD(RS1) @@ -472,7 +508,9 @@ ckb_vm_x64_execute: DECODE_I movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 + movq RS1, TEMP1 + addq $4, TEMP1 + cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, TEMP1 jae .exit_out_of_bound mov CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1), RS1d WRITE_RD(RS1) @@ -671,35 +709,29 @@ ckb_vm_x64_execute: DECODE_S movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 - jae .exit_out_of_bound + CHECK_WRITE_PERMISSION(RS1, TEMP1, RS2r, RS2rd, 1) movq REGISTER_ADDRESS(RS2s), RS2s mov RS2sb, CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1) - mov $1, TEMP1d - jmp .clear_trace + NEXT_INST .p2align 3 .CKB_VM_ASM_LABEL_OP_SD: .CKB_VM_ASM_LABEL_OP_RVC_SD: DECODE_S movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 - jae .exit_out_of_bound + CHECK_WRITE_PERMISSION(RS1, TEMP1, RS2r, RS2rd, 8) movq REGISTER_ADDRESS(RS2s), RS2s movq RS2s, CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1) - mov $8, TEMP1d - jmp .clear_trace + NEXT_INST .p2align 3 .CKB_VM_ASM_LABEL_OP_SH: DECODE_S movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 - jae .exit_out_of_bound + CHECK_WRITE_PERMISSION(RS1, TEMP1, RS2r, RS2rd, 2) movq REGISTER_ADDRESS(RS2s), RS2s mov RS2sh, CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1) - mov $2, TEMP1d - jmp .clear_trace + NEXT_INST .p2align 3 .CKB_VM_ASM_LABEL_OP_SLL: DECODE_R @@ -869,12 +901,10 @@ ckb_vm_x64_execute: DECODE_S movq REGISTER_ADDRESS(RS1), RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 - jae .exit_out_of_bound + CHECK_WRITE_PERMISSION(RS1, TEMP1, RS2r, RS2rd, 4) movq REGISTER_ADDRESS(RS2s), RS2s mov RS2sd, CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1) - mov $4, TEMP1d - jmp .clear_trace + NEXT_INST .p2align 3 .CKB_VM_ASM_LABEL_OP_XOR: .CKB_VM_ASM_LABEL_OP_RVC_XOR: @@ -965,7 +995,9 @@ ckb_vm_x64_execute: DECODE_U movq SP_ADDRESS, RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 + movq RS1, TEMP1 + addq $8, TEMP1 + cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, TEMP1 jae .exit_out_of_bound movq CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1), RS1 WRITE_RD(RS1) @@ -975,7 +1007,9 @@ ckb_vm_x64_execute: DECODE_U movq SP_ADDRESS, RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 + movq RS1, TEMP1 + addq $4, TEMP1 + cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, TEMP1 jae .exit_out_of_bound movslq CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1), RS1 WRITE_RD(RS1) @@ -991,81 +1025,19 @@ ckb_vm_x64_execute: DECODE_S movq SP_ADDRESS, RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 - jae .exit_out_of_bound + CHECK_WRITE_PERMISSION(RS1, TEMP1, RS2r, RS2rd, 8) movq REGISTER_ADDRESS(RS2s), RS2s movq RS2s, CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1) - mov $8, TEMP1d - jmp .clear_trace + NEXT_INST .p2align 3 .CKB_VM_ASM_LABEL_OP_RVC_SWSP: DECODE_S movq SP_ADDRESS, RS1 addq IMMEDIATE, RS1 - cmp $CKB_VM_ASM_RISCV_MAX_MEMORY, RS1 - jae .exit_out_of_bound + CHECK_WRITE_PERMISSION(RS1, TEMP1, RS2r, RS2rd, 4) movq REGISTER_ADDRESS(RS2s), RS2s mov RS2sd, CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_MEMORY(MACHINE, RS1) - mov $4, TEMP1d - jmp .clear_trace -.p2align 3 -.clear_trace: - push INST_PC - /* - * This tail function takes memory address in RS1, and length in TEMP1. - * - * When running: - * * INST_PC(originally value saved) contains the start memory address - * that is written - * * RS2r contains the end of the memory address that is written - * * RS1 contains address of current slot - * * RD contains address of last slot to process - * * IMMEDIATE contains a flag if current slot is affected - * * TEMP1 is a free variable - */ - movq RS1, INST_PC - addq RS1, TEMP1 - movq TEMP1, RS2r - movq TEMP1, RD - subq $CKB_VM_ASM_MAXIMUM_TRACE_ADDRESS_LENGTH, RS1 - xor IMMEDIATEd, IMMEDIATEd - cmovl IMMEDIATEd, RS1d - ADDRESS_TO_SLOT_ADDRESS(RS1) - ADDRESS_TO_SLOT_ADDRESS(RD) -.clear_trace_loop: - cmp RD, RS1 - ja .clear_trace_finish - movq CKB_VM_ASM_TRACE_OFFSET_ADDRESS(RS1), TEMP1 - cmp RS2r, TEMP1 - jnb .clear_trace_loop_body_end - addq CKB_VM_ASM_TRACE_OFFSET_LENGTH(RS1), TEMP1 - cmp TEMP1, INST_PC - jnb .clear_trace_loop_body_end - cmp RS1, TRACE - sete TEMP1b - or TEMP1b, IMMEDIATEb - movq $0, CKB_VM_ASM_TRACE_OFFSET_ADDRESS(RS1) -.clear_trace_loop_body_end: - addq $CKB_VM_ASM_TRACE_STRUCT_SIZE, RS1 - jmp .clear_trace_loop -.clear_trace_finish: - pop INST_PC - cmp $0, IMMEDIATEb - jne .clear_trace_current_cleared - NEXT_INST -.clear_trace_current_cleared: - /* - * Current trace is cleared, restore original PC_ADDRESS then exit - * current trace loop - */ - movq PC_ADDRESS, %rax - movzbl CKB_VM_ASM_TRACE_OFFSET_LENGTH(TRACE), %ecx - subq %rcx, %rax - movq -8(INST_ARGS), %rcx - shr $24, %ecx - addq %rcx, %rax - movq %rax, PC_ADDRESS - jmp .prepare_trace + NEXT_INST .p2align 3 .exit_out_of_bound: mov $CKB_VM_ASM_RET_OUT_OF_BOUND, %eax @@ -1074,6 +1046,9 @@ ckb_vm_x64_execute: .exit_max_cycles_exceeded: mov $CKB_VM_ASM_RET_MAX_CYCLES_EXCEEDED, %eax jmp .exit +.exit_invalid_permission: + mov $CKB_VM_ASM_RET_INVALID_PERMISSION, %eax + jmp .exit .p2align 3 .exit_trace: .CKB_VM_ASM_LABEL_OP_UNLOADED: diff --git a/src/machine/asm/mod.rs b/src/machine/asm/mod.rs index b2524428..a1fcf267 100644 --- a/src/machine/asm/mod.rs +++ b/src/machine/asm/mod.rs @@ -1,21 +1,23 @@ use crate::{ + bits::{rounddown, roundup}, decoder::build_imac_decoder, instructions::{ blank_instruction, extract_opcode, instruction_length, is_basic_block_end_instruction, }, - CoreMachine, DefaultMachine, Error, Machine, Memory, SupportMachine, + memory::{fill_page_data, FLAG_FREEZED}, + CoreMachine, DefaultMachine, Error, Machine, Memory, SupportMachine, RISCV_MAX_MEMORY, + RISCV_PAGESIZE, }; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use bytes::Bytes; use ckb_vm_definitions::{ asm::{ - calculate_slot, Trace, RET_DECODE_TRACE, RET_EBREAK, RET_ECALL, RET_MAX_CYCLES_EXCEEDED, - RET_OUT_OF_BOUND, TRACE_ITEM_LENGTH, + calculate_slot, Trace, RET_DECODE_TRACE, RET_EBREAK, RET_ECALL, RET_INVALID_PERMISSION, + RET_MAX_CYCLES_EXCEEDED, RET_OUT_OF_BOUND, TRACE_ITEM_LENGTH, }, instructions::OP_CUSTOM_TRACE_END, }; use libc::c_uchar; -use std::cmp::min; use std::io::{Cursor, Seek, SeekFrom}; use std::ptr; @@ -51,25 +53,32 @@ impl CoreMachine for Box { } impl Memory for Box { - fn mmap( + fn init_pages( &mut self, addr: usize, size: usize, - _prot: u32, + flags: u8, source: Option, - offset: usize, + offset_from_addr: usize, ) -> Result<(), Error> { - if let Some(source) = source { - let real_size = min(size, source.len() - offset); - let value = &source[offset..offset + real_size]; - return self.store_bytes(addr, value); + if rounddown(addr, RISCV_PAGESIZE) != addr || roundup(size, RISCV_PAGESIZE) != size { + return Err(Error::Unaligned); } - self.clear_traces(addr as u64, size as u64); - Ok(()) - } - - fn munmap(&mut self, addr: usize, size: usize) -> Result<(), Error> { - self.store_byte(addr, size, 0) + if addr > RISCV_MAX_MEMORY + || size > RISCV_MAX_MEMORY + || addr + size > RISCV_MAX_MEMORY + || offset_from_addr > size + { + return Err(Error::OutOfBound); + } + for page_addr in (addr..addr + size).step_by(RISCV_PAGESIZE) { + let page = page_addr / RISCV_PAGESIZE; + if self.flags[page] & FLAG_FREEZED != 0 { + return Err(Error::InvalidPermission); + } + self.flags[page] = flags; + } + fill_page_data(self, addr, size, source, offset_from_addr) } fn store_bytes(&mut self, addr: usize, value: &[u8]) -> Result<(), Error> { @@ -89,7 +98,7 @@ impl Memory for Box { } // This is essentially memset call unsafe { - let slice_ptr = self.memory[..size].as_mut_ptr(); + let slice_ptr = self.memory[addr..addr + size].as_mut_ptr(); ptr::write_bytes(slice_ptr, value, size); } self.clear_traces(addr as u64, size as u64); @@ -194,14 +203,6 @@ impl Memory for Box { } impl SupportMachine for Box { - fn elf_end(&self) -> usize { - self.elf_end - } - - fn set_elf_end(&mut self, elf_end: usize) { - self.elf_end = elf_end; - } - fn cycles(&self) -> u64 { self.cycles } @@ -291,6 +292,7 @@ impl<'a> AsmMachine<'a> { RET_EBREAK => self.machine.ebreak()?, RET_MAX_CYCLES_EXCEEDED => return Err(Error::InvalidCycles), RET_OUT_OF_BOUND => return Err(Error::OutOfBound), + RET_INVALID_PERMISSION => return Err(Error::InvalidPermission), _ => return Err(Error::Asm(result)), } } diff --git a/src/machine/mod.rs b/src/machine/mod.rs index f1505140..9dafc028 100644 --- a/src/machine/mod.rs +++ b/src/machine/mod.rs @@ -2,10 +2,10 @@ pub mod asm; pub mod trace; -use super::bits::rounddown; +use super::bits::{rounddown, roundup}; use super::decoder::build_imac_decoder; use super::instructions::{execute, Instruction, Register}; -use super::memory::{Memory, PROT_EXEC, PROT_READ, PROT_WRITE}; +use super::memory::{Memory, FLAG_EXECUTABLE, FLAG_FREEZED}; use super::syscalls::Syscalls; use super::{ registers::{A0, A7, REGISTER_ABI_NAMES, SP}, @@ -14,7 +14,6 @@ use super::{ use bytes::Bytes; use goblin::elf::program_header::{PF_R, PF_W, PF_X, PT_LOAD}; use goblin::elf::{Elf, Header}; -use std::cmp::max; use std::fmt::{self, Display}; fn elf_bits(header: &Header) -> Option { @@ -30,18 +29,18 @@ fn elf_bits(header: &Header) -> Option { } // Converts goblin's ELF flags into RISC-V flags -fn convert_flags(p_flags: u32) -> u32 { - let mut flags = 0; - if p_flags & PF_R != 0 { - flags |= PROT_READ; +fn convert_flags(p_flags: u32) -> Result { + let readable = p_flags & PF_R != 0; + let writable = p_flags & PF_W != 0; + let executable = p_flags & PF_X != 0; + if (!readable) || (writable && executable) { + return Err(Error::InvalidPermission); + } + if executable { + Ok(FLAG_EXECUTABLE | FLAG_FREEZED) + } else { + Ok(FLAG_FREEZED) } - if p_flags & PF_W != 0 { - flags |= PROT_WRITE; - } - if p_flags & PF_X != 0 { - flags |= PROT_EXEC; - } - flags } /// This is the core part of RISC-V that only deals with data part, it @@ -70,9 +69,6 @@ pub trait Machine: CoreMachine { /// such as ELF range, cycles which might be needed on Rust side of the logic, /// such as runner or syscall implementations. pub trait SupportMachine: CoreMachine { - // End address of elf segment - fn elf_end(&self) -> usize; - fn set_elf_end(&mut self, elf_end: usize); // Current execution cycles, it's up to the actual implementation to // call add_cycles for each instruction/operation to provide cycles. // The implementation might also choose not to do this to ignore this @@ -105,16 +101,19 @@ pub trait SupportMachine: CoreMachine { if program_header.p_type == PT_LOAD { let aligned_start = rounddown(program_header.p_vaddr as usize, RISCV_PAGESIZE); let padding_start = program_header.p_vaddr as usize - aligned_start; - // Like a normal mmap, we will align size to pages internally - let size = program_header.p_filesz as usize + padding_start; - let current_elf_end = self.elf_end(); - self.set_elf_end(max(aligned_start + size, current_elf_end)); - self.memory_mut().mmap( + let size = roundup( + program_header.p_memsz as usize + padding_start, + RISCV_PAGESIZE, + ); + self.memory_mut().init_pages( aligned_start, size, - convert_flags(program_header.p_flags), - Some(program.clone()), - program_header.p_offset as usize - padding_start, + convert_flags(program_header.p_flags)?, + Some(program.slice( + program_header.p_offset as usize, + (program_header.p_offset + program_header.p_filesz) as usize, + )), + padding_start, )?; self.memory_mut() .store_byte(aligned_start, padding_start, 0)?; @@ -131,7 +130,7 @@ pub trait SupportMachine: CoreMachine { stack_size: usize, ) -> Result<(), Error> { self.memory_mut() - .mmap(stack_start, stack_size, PROT_READ | PROT_WRITE, None, 0)?; + .init_pages(stack_start, stack_size, 0, None, 0)?; self.set_register(SP, Self::REG::from_usize(stack_start + stack_size)); // First value in this array is argc, then it contains the address(pointer) // of each argv object. @@ -158,7 +157,6 @@ pub trait SupportMachine: CoreMachine { } if self.registers()[SP].to_usize() < stack_start { // args exceed stack size - self.memory_mut().munmap(stack_start, stack_size)?; return Err(Error::OutOfBound); } Ok(()) @@ -170,7 +168,6 @@ pub struct DefaultCoreMachine { registers: [R; RISCV_GENERAL_REGISTER_NUMBER], pc: R, memory: M, - elf_end: usize, cycles: u64, max_cycles: Option, } @@ -204,14 +201,6 @@ impl> CoreMachine for DefaultCoreMachine { } impl> SupportMachine for DefaultCoreMachine { - fn elf_end(&self) -> usize { - self.elf_end - } - - fn set_elf_end(&mut self, elf_end: usize) { - self.elf_end = elf_end; - } - fn cycles(&self) -> u64 { self.cycles } @@ -280,14 +269,6 @@ impl CoreMachine for DefaultMachine<'_, Inner> { } impl SupportMachine for DefaultMachine<'_, Inner> { - fn elf_end(&self) -> usize { - self.inner.elf_end() - } - - fn set_elf_end(&mut self, elf_end: usize) { - self.inner.set_elf_end(elf_end) - } - fn cycles(&self) -> u64 { self.inner.cycles() } diff --git a/src/machine/trace.rs b/src/machine/trace.rs index 73e41ad6..3aad8e4b 100644 --- a/src/machine/trace.rs +++ b/src/machine/trace.rs @@ -4,13 +4,12 @@ use super::{ instructions::{ execute, instruction_length, is_basic_block_end_instruction, Instruction, Register, }, - memory::Memory, + memory::{wxorx::WXorXMemory, Memory}, Error, }, CoreMachine, DefaultMachine, Machine, SupportMachine, }; use bytes::Bytes; -use std::cmp::min; // The number of trace items to keep const TRACE_SIZE: usize = 8192; @@ -18,7 +17,6 @@ const TRACE_SIZE: usize = 8192; const TRACE_MASK: usize = (TRACE_SIZE - 1); // The maximum number of instructions to cache in a trace item const TRACE_ITEM_LENGTH: usize = 16; -const TRACE_ITEM_MAXIMAL_ADDRESS_LENGTH: usize = 4 * TRACE_ITEM_LENGTH; // Shifts to truncate a value so 2 traces has the minimal chance of sharing code. const TRACE_ADDRESS_SHIFTS: usize = 5; @@ -39,13 +37,13 @@ pub struct TraceMachine<'a, Inner> { pub machine: DefaultMachine<'a, Inner>, traces: Vec, - running_trace_slot: usize, - running_trace_cleared: bool, } -impl CoreMachine for TraceMachine<'_, Inner> { +impl, Inner: SupportMachine>> CoreMachine + for TraceMachine<'_, Inner> +{ type REG = ::REG; - type MEM = Self; + type MEM = ::MEM; fn pc(&self) -> &Self::REG { &self.machine.pc() @@ -55,12 +53,12 @@ impl CoreMachine for TraceMachine<'_, Inner> { self.machine.set_pc(next_pc) } - fn memory(&self) -> &Self { - &self + fn memory(&self) -> &Self::MEM { + self.machine.memory() } - fn memory_mut(&mut self) -> &mut Self { - self + fn memory_mut(&mut self) -> &mut Self::MEM { + self.machine.memory_mut() } fn registers(&self) -> &[Self::REG] { @@ -72,118 +70,9 @@ impl CoreMachine for TraceMachine<'_, Inner> { } } -impl Memory<::REG> for TraceMachine<'_, Inner> { - fn mmap( - &mut self, - addr: usize, - size: usize, - prot: u32, - source: Option, - offset: usize, - ) -> Result<(), Error> { - self.machine - .memory_mut() - .mmap(addr, size, prot, source, offset)?; - self.clear_traces(addr, size); - Ok(()) - } - - fn munmap(&mut self, addr: usize, size: usize) -> Result<(), Error> { - self.machine.memory_mut().munmap(addr, size)?; - self.clear_traces(addr, size); - Ok(()) - } - - fn store_byte(&mut self, addr: usize, size: usize, value: u8) -> Result<(), Error> { - self.machine.memory_mut().store_byte(addr, size, value)?; - self.clear_traces(addr, size); - Ok(()) - } - - fn store_bytes(&mut self, addr: usize, value: &[u8]) -> Result<(), Error> { - self.machine.memory_mut().store_bytes(addr, value)?; - self.clear_traces(addr, value.len()); - Ok(()) - } - - fn execute_load16(&mut self, addr: usize) -> Result { - self.machine.memory_mut().execute_load16(addr) - } - - fn load8( - &mut self, - addr: &::REG, - ) -> Result<::REG, Error> { - self.machine.memory_mut().load8(addr) - } - - fn load16( - &mut self, - addr: &::REG, - ) -> Result<::REG, Error> { - self.machine.memory_mut().load16(addr) - } - - fn load32( - &mut self, - addr: &::REG, - ) -> Result<::REG, Error> { - self.machine.memory_mut().load32(addr) - } - - fn load64( - &mut self, - addr: &::REG, - ) -> Result<::REG, Error> { - self.machine.memory_mut().load64(addr) - } - - fn store8( - &mut self, - addr: &::REG, - value: &::REG, - ) -> Result<(), Error> { - self.machine.memory_mut().store8(addr, value)?; - self.clear_traces(addr.to_usize(), 1); - Ok(()) - } - - fn store16( - &mut self, - addr: &::REG, - value: &::REG, - ) -> Result<(), Error> { - self.machine.memory_mut().store16(addr, value)?; - self.clear_traces(addr.to_usize(), 2); - Ok(()) - } - - fn store32( - &mut self, - addr: &::REG, - value: &::REG, - ) -> Result<(), Error> { - self.machine.memory_mut().store32(addr, value)?; - self.clear_traces(addr.to_usize(), 4); - Ok(()) - } - - fn store64( - &mut self, - addr: &::REG, - value: &::REG, - ) -> Result<(), Error> { - self.machine.memory_mut().store64(addr, value)?; - self.clear_traces(addr.to_usize(), 8); - Ok(()) - } -} - -// NOTE: this might look redundant, but what we need is the execute method -// below works on TraceMachine instead of DefaultMachine, so we can leverage -// CoreMachine and Memory trait implementations above to clear related cached -// traces in case of a memory write. -impl Machine for TraceMachine<'_, Inner> { +impl, Inner: SupportMachine>> Machine + for TraceMachine<'_, Inner> +{ fn ecall(&mut self) -> Result<(), Error> { self.machine.ecall() } @@ -193,13 +82,13 @@ impl Machine for TraceMachine<'_, Inner> { } } -impl<'a, Inner: SupportMachine> TraceMachine<'a, Inner> { +impl<'a, R: Register, M: Memory, Inner: SupportMachine>> + TraceMachine<'a, Inner> +{ pub fn new(machine: DefaultMachine<'a, Inner>) -> Self { Self { machine, traces: vec![], - running_trace_slot: 0, - running_trace_cleared: false, } } @@ -216,14 +105,14 @@ impl<'a, Inner: SupportMachine> TraceMachine<'a, Inner> { // larger trace item length. self.traces.resize_with(TRACE_SIZE, Trace::default); while self.machine.running() { - let pc = self.pc().to_usize(); + let pc = self.machine.pc().to_usize(); let slot = calculate_slot(pc); if pc != self.traces[slot].address || self.traces[slot].instruction_count == 0 { self.traces[slot] = Trace::default(); let mut current_pc = pc; let mut i = 0; while i < TRACE_ITEM_LENGTH { - let instruction = decoder.decode(self.memory_mut(), current_pc)?; + let instruction = decoder.decode(self.machine.memory_mut(), current_pc)?; let end_instruction = is_basic_block_end_instruction(instruction); current_pc += instruction_length(instruction); self.traces[slot].instructions[i] = instruction; @@ -236,8 +125,6 @@ impl<'a, Inner: SupportMachine> TraceMachine<'a, Inner> { self.traces[slot].length = current_pc - pc; self.traces[slot].instruction_count = i as u8; } - self.running_trace_slot = slot; - self.running_trace_cleared = false; for i in 0..self.traces[slot].instruction_count { let i = self.traces[slot].instructions[i as usize]; execute(i, self)?; @@ -248,30 +135,10 @@ impl<'a, Inner: SupportMachine> TraceMachine<'a, Inner> { .map(|f| f(i)) .unwrap_or(0); self.machine.add_cycles(cycles)?; - if self.running_trace_cleared { - break; - } } } Ok(self.machine.exit_code()) } - - fn clear_traces(&mut self, address: usize, length: usize) { - let end = address + length; - let minimal_slot = - calculate_slot(address.saturating_sub(TRACE_ITEM_MAXIMAL_ADDRESS_LENGTH)); - let maximal_slot = calculate_slot(end); - for slot in minimal_slot..=min(maximal_slot, self.traces.len()) { - let slot_address = self.traces[slot].address; - let slot_end = slot_address + self.traces[slot].length; - if !((end <= slot_address) || (slot_end <= address)) { - self.traces[slot] = Trace::default(); - if self.running_trace_slot == slot { - self.running_trace_cleared = true; - } - } - } - } } #[cfg(test)] diff --git a/src/memory/flat.rs b/src/memory/flat.rs index f2d2e87f..a4172a93 100644 --- a/src/memory/flat.rs +++ b/src/memory/flat.rs @@ -1,9 +1,8 @@ use super::super::{Error, Register, RISCV_MAX_MEMORY}; -use super::Memory; +use super::{fill_page_data, Memory}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use bytes::Bytes; -use std::cmp::min; use std::io::{Cursor, Seek, SeekFrom}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -40,35 +39,15 @@ impl DerefMut for FlatMemory { /// A flat chunk of memory used for RISC-V machine, it lacks all the permission /// checking logic. impl Memory for FlatMemory { - fn mmap( + fn init_pages( &mut self, addr: usize, size: usize, - _prot: u32, + _flags: u8, source: Option, - offset: usize, + offset_from_addr: usize, ) -> Result<(), Error> { - if addr + size > self.len() { - return Err(Error::OutOfBound); - } - if let Some(source) = source { - let real_size = min(size, source.len() - offset); - let slice = &mut self[addr..addr + real_size]; - slice.copy_from_slice(&source[offset..offset + real_size]); - } - Ok(()) - } - - fn munmap(&mut self, addr: usize, size: usize) -> Result<(), Error> { - if addr + size > self.len() { - return Err(Error::OutOfBound); - } - // This is essentially memset call - unsafe { - let slice_ptr = self[addr..addr + size].as_mut_ptr(); - ptr::write_bytes(slice_ptr, b'0', size); - } - Ok(()) + fill_page_data(self, addr, size, source, offset_from_addr) } fn execute_load16(&mut self, addr: usize) -> Result { diff --git a/src/memory/mmu.rs b/src/memory/mmu.rs deleted file mode 100644 index bceaab0b..00000000 --- a/src/memory/mmu.rs +++ /dev/null @@ -1,370 +0,0 @@ -use super::super::{Error, Register, RISCV_MAX_MEMORY, RISCV_PAGESIZE}; -use super::{round_page, Memory, Page, PROT_EXEC, PROT_READ, PROT_WRITE}; - -use bytes::Bytes; -use std::cmp::min; -use std::marker::PhantomData; -use std::ptr; - -pub const MAX_VIRTUAL_MEMORY_ENTRIES: usize = 64; - -const MAX_PAGES: usize = RISCV_MAX_MEMORY / RISCV_PAGESIZE; -const MAX_TLB_ENTRIES: usize = 16; -const INVALID_PAGE_INDEX: u8 = 0xFF; - -/// Virtual memory entry that only keeps track of established memory mapping -/// (mostly, if not all, created via mmap) -#[derive(Clone)] -pub struct VirtualMemoryEntry { - addr: usize, - size: usize, - prot: u32, - source: Option, - offset: usize, - /// This reference count value is used to implement a handy trick: - /// a virtual memory entry can store multiple pages, and it should only - /// be discarded once all memory pages are unmapped. However, parameters - /// of munmap() don't necessary need to match parameters of mmap(). In - /// other words, it's totally fine to mmap 5 pages, but only unmap 3 of - /// them later. This means we will need to keep track of how many pages - /// are unmapped in this entry. - /// That being said, tracking exactly what pages are unmapped is an expensive - /// operation, so instead of tracking what pages are unmapped, we are only - /// tracking how many pages are still mmapped using this reference count - /// value. When it reaches zero, we assume no pages are still mmapped, and - /// it's safe to destroy current virtual memory entry. - /// Obviously this doesn't cover all cases, but it's worth pointing out that - /// it's unspecified behavior to munmap() a page that's not established via - /// mmap(). So in practice it will be safe to leverage this trick here. All - /// we need to make sure is to give a hard upper bound on the number of virtual - /// memory entrys, and halt as soon as this upper bound is reached. - refcount: usize, -} - -#[derive(Clone, Copy)] -pub struct TlbEntry { - addr: usize, - prot: u32, - page_data_index: usize, -} - -/// An MMU implementation with proper protection schemes. Notice this is a correct -/// soft MMU, not necessarily a fast MMU. For the best performance, we should -/// leverage native mmap() calls instead to avoid overheads. -pub struct Mmu { - vms: Vec>, - // Page table that stores indices of VirtualMemoryEntry for each page. We are - // using u8 here to save some memory since we have a hard upper bound of 64 - // virtual memory entries, which would fit in a u8 perfectly. - // A real machine would use 2-level page table, but since here we are dealing - // with 128MB maximum memory, which is just 32K pages, we can just use a single - // linear array to boost performance. - page_table: [u8; MAX_PAGES], - // Pages that have been requested and instantiated. We are using linear array - // here since TLB entry needs to reference individual pages here, and using - // a hash map would complicate lifetime rule a lot. We can come back here later - // for optimization work if this indeed becomes a bottleneck. - // The addresses and data here are stored separately to make it more cache - // friendly when we are searching for the page via address. - page_addresses: Vec, - page_data: Vec, - // Translation lookaside buffer - tlb: [Option; MAX_TLB_ENTRIES], - _inner: PhantomData, -} - -// Generates a new page based on VM mapping, also copy mapped data to the page -// if exists. Notice every page generated here would be zero-filled first. -fn handle_page_fault(vm: &VirtualMemoryEntry, addr: usize) -> Result { - let mut page = [0; RISCV_PAGESIZE]; - if let Some(ref source) = vm.source { - let offset = vm.offset + (addr - vm.addr); - let copied_size = min(RISCV_PAGESIZE, source.len() - offset); - let (left, _) = page.split_at_mut(copied_size); - left.copy_from_slice(&source[offset..offset + copied_size]); - } - // The page is already initialized with all zeros, so there's no - // need for us to fill the remaining part as 0 even if source doesn't - // fill everything - Ok(page) -} - -impl Mmu { - pub fn new() -> Mmu { - Mmu { - vms: vec![None; MAX_VIRTUAL_MEMORY_ENTRIES], - page_table: [INVALID_PAGE_INDEX; MAX_PAGES], - page_addresses: Vec::new(), - page_data: Vec::new(), - tlb: [None; MAX_TLB_ENTRIES], - _inner: PhantomData, - } - } - - // Finds requested page index in page_addresses data structure, the same - // index could also be used on page_data vec. - fn find_page_data_index(&self, addr: usize) -> Option { - self.page_addresses.iter().position(|a| *a == addr) - } - - fn munmap_aligned(&mut self, aligned_addr: usize, aligned_size: usize) -> Result<(), Error> { - for current_addr in (aligned_addr..aligned_addr + aligned_size).step_by(RISCV_PAGESIZE) { - let current_page = current_addr / RISCV_PAGESIZE; - let vm_index = self.page_table[current_page]; - if vm_index != INVALID_PAGE_INDEX { - self.page_table[current_page] = INVALID_PAGE_INDEX; - let page_data_index = self.find_page_data_index(current_addr); - if let Some(index) = page_data_index { - // page_addresses and page_data share the same indices - // for each item, so it's totally safe to do this. - self.page_addresses.swap_remove(index); - self.page_data.swap_remove(index); - } - let tlb_index = current_page % MAX_TLB_ENTRIES; - let tlb_entry = self.tlb[tlb_index]; - if let Some(ref tlb) = tlb_entry { - if tlb.addr == current_addr { - self.tlb[tlb_index] = None; - } - } - let mut destroy_vm_entry = false; - if let Some(ref mut vm) = self.vms[vm_index as usize] { - vm.refcount -= 1; - if vm.refcount == 0 { - destroy_vm_entry = true; - } - } - if destroy_vm_entry { - self.vms[vm_index as usize] = None; - } - } - } - Ok(()) - } - - fn fetch_page(&mut self, addr: usize, prot: u32) -> Result<&mut Page, Error> { - let page = addr / RISCV_PAGESIZE; - if page > self.page_table.len() { - return Err(Error::OutOfBound); - } - // Try looking TLB first for a fast path - let tlb_index = page % MAX_TLB_ENTRIES; - if let Some(entry) = self.tlb[tlb_index] { - if entry.addr == addr { - if entry.prot & prot == prot { - return Ok(&mut self.page_data[entry.page_data_index]); - } else { - return Err(Error::InvalidPermission); - } - } - } - // If TLB entry is missing, try looking it from existing instantiated - // page data. If it still fails, do a full page fault. - let vm_index = self.page_table[addr / RISCV_PAGESIZE]; - match self.vms.get(vm_index as usize) { - Some(Some(vm)) => { - if vm.prot & prot != prot { - return Err(Error::InvalidPermission); - } - let page_data_index = match self.find_page_data_index(addr) { - Some(index) => index, - None => { - // Do a full page fault here - let page = handle_page_fault(vm, addr)?; - self.page_addresses.push(addr); - self.page_data.push(page); - self.page_data.len() - 1 - } - }; - self.tlb[tlb_index] = Some(TlbEntry { - addr, - prot: vm.prot, - page_data_index, - }); - Ok(&mut self.page_data[page_data_index]) - } - _ => Err(Error::OutOfBound), - } - } - - fn load(&mut self, addr: usize, bytes: usize, prot: u32) -> Result { - debug_assert!(bytes == 1 || bytes == 2 || bytes == 4 || bytes == 8); - let page_addr = round_page(addr); - let first_page_bytes = min(bytes, RISCV_PAGESIZE - (addr - page_addr)); - let mut shift = 0; - let mut value: u64 = 0; - { - let page = self.fetch_page(page_addr, prot)?; - for i in 0..first_page_bytes { - value |= u64::from(page[addr - page_addr + i]) << shift; - shift += 8; - } - } - let second_page_bytes = bytes - first_page_bytes; - if second_page_bytes > 0 { - let second_page = self.fetch_page(page_addr + RISCV_PAGESIZE, PROT_READ)?; - for &byte in second_page.iter().take(second_page_bytes) { - value |= u64::from(byte) << shift; - shift += 8; - } - } - Ok(value) - } -} - -impl Memory for Mmu { - fn mmap( - &mut self, - addr: usize, - size: usize, - prot: u32, - source: Option, - offset: usize, - ) -> Result<(), Error> { - if addr & (RISCV_PAGESIZE - 1) != 0 || size & (RISCV_PAGESIZE - 1) != 0 { - return Err(Error::Unaligned); - } - let position = self.vms.iter().position(Option::is_none); - if let Some(i) = position { - debug_assert!(i < MAX_VIRTUAL_MEMORY_ENTRIES); - // This one is for extra caution, even though right now - // MAX_VIRTUAL_MEMORY_ENTRIES is 64, which is less than 0xFF, this - // extra guard can help future-proofing the logic - debug_assert!(i < 0xFF); - let pages = size / RISCV_PAGESIZE; - self.vms[i] = Some(VirtualMemoryEntry { - addr, - size, - prot, - source, - offset, - refcount: pages, - }); - for current_addr in (addr..addr + size).step_by(RISCV_PAGESIZE) { - let current_page = current_addr / RISCV_PAGESIZE; - // munmap overlapped pages - if self.page_table[current_page] != INVALID_PAGE_INDEX { - self.munmap_aligned(current_addr, RISCV_PAGESIZE)?; - } - self.page_table[current_page] = i as u8; - } - Ok(()) - } else { - Err(Error::MaximumMmappingReached) - } - } - - fn munmap(&mut self, addr: usize, size: usize) -> Result<(), Error> { - if addr & (RISCV_PAGESIZE - 1) != 0 || size & (RISCV_PAGESIZE - 1) != 0 { - return Err(Error::Unaligned); - } - self.munmap_aligned(addr, size) - } - - fn load8(&mut self, addr: &R) -> Result { - let v = self.load(addr.to_usize(), 1, PROT_READ).map(|v| v as u8)?; - Ok(R::from_u8(v)) - } - - fn load16(&mut self, addr: &R) -> Result { - let v = self.load(addr.to_usize(), 2, PROT_READ).map(|v| v as u16)?; - Ok(R::from_u16(v)) - } - - fn load32(&mut self, addr: &R) -> Result { - let v = self.load(addr.to_usize(), 4, PROT_READ).map(|v| v as u32)?; - Ok(R::from_u32(v)) - } - - fn load64(&mut self, addr: &R) -> Result { - let v = self.load(addr.to_usize(), 8, PROT_READ)?; - Ok(R::from_u64(v)) - } - - fn execute_load16(&mut self, addr: usize) -> Result { - self.load(addr, 2, PROT_EXEC).map(|v| v as u16) - } - - fn store_bytes(&mut self, addr: usize, value: &[u8]) -> Result<(), Error> { - let mut remaining_data = value; - let mut current_page_addr = round_page(addr); - let mut current_page_offset = addr - current_page_addr; - while !remaining_data.is_empty() { - let page = self.fetch_page(current_page_addr, PROT_WRITE)?; - let bytes = min(RISCV_PAGESIZE - current_page_offset, remaining_data.len()); - let slice = &mut page[current_page_offset..current_page_offset + bytes]; - slice.copy_from_slice(&remaining_data[..bytes]); - - remaining_data = &remaining_data[bytes..]; - current_page_addr += RISCV_PAGESIZE; - current_page_offset = 0; - } - Ok(()) - } - - fn store_byte(&mut self, addr: usize, size: usize, value: u8) -> Result<(), Error> { - let mut current_page_addr = round_page(addr); - let mut current_page_offset = addr - current_page_addr; - let mut remaining_size = size; - while remaining_size > 0 { - let page = self.fetch_page(current_page_addr, PROT_WRITE)?; - let bytes = min(RISCV_PAGESIZE - current_page_offset, remaining_size); - unsafe { - let slice_ptr = page[current_page_offset..bytes].as_mut_ptr(); - ptr::write_bytes(slice_ptr, value, bytes); - } - remaining_size -= bytes; - current_page_addr += RISCV_PAGESIZE; - current_page_offset = 0; - } - Ok(()) - } - - fn store8(&mut self, addr: &R, value: &R) -> Result<(), Error> { - self.store_bytes(addr.to_usize(), &[value.to_u8()]) - } - - fn store16(&mut self, addr: &R, value: &R) -> Result<(), Error> { - let value = value.to_u16(); - // RISC-V is little-endian by specification - self.store_bytes(addr.to_usize(), &[(value & 0xFF) as u8, (value >> 8) as u8]) - } - - fn store32(&mut self, addr: &R, value: &R) -> Result<(), Error> { - let value = value.to_u32(); - // RISC-V is little-endian by specification - self.store_bytes( - addr.to_usize(), - &[ - (value & 0xFF) as u8, - ((value >> 8) & 0xFF) as u8, - ((value >> 16) & 0xFF) as u8, - ((value >> 24) & 0xFF) as u8, - ], - ) - } - - fn store64(&mut self, addr: &R, value: &R) -> Result<(), Error> { - let value = value.to_u64(); - // RISC-V is little-endian by specification - self.store_bytes( - addr.to_usize(), - &[ - (value & 0xFF) as u8, - ((value >> 8) & 0xFF) as u8, - ((value >> 16) & 0xFF) as u8, - ((value >> 24) & 0xFF) as u8, - ((value >> 32) & 0xFF) as u8, - ((value >> 40) & 0xFF) as u8, - ((value >> 48) & 0xFF) as u8, - ((value >> 56) & 0xFF) as u8, - ], - ) - } -} - -impl Default for Mmu { - fn default() -> Self { - Self::new() - } -} diff --git a/src/memory/mod.rs b/src/memory/mod.rs index faf05bea..672813ad 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -1,13 +1,14 @@ use super::{Error, Register, RISCV_PAGESIZE}; use bytes::Bytes; +use std::cmp::min; pub mod flat; -pub mod mmu; pub mod sparse; +pub mod wxorx; -pub const PROT_READ: u32 = 0b0001; -pub const PROT_WRITE: u32 = 0b0010; -pub const PROT_EXEC: u32 = 0b0100; +pub use ckb_vm_definitions::memory::{ + FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE, FLAG_WXORR_BIT, +}; #[inline(always)] pub fn round_page(x: usize) -> usize { @@ -17,20 +18,14 @@ pub fn round_page(x: usize) -> usize { pub type Page = [u8; RISCV_PAGESIZE]; pub trait Memory { - // Note this mmap only handles the very low level memory mapping logic. - // It only takes an aligned address and size, then maps either existing - // bytes or empty bytes to this range. It doesn't allocate addresses when - // given 0 as address value. Instead, higher level machine should be leveraged - // to manage code, heap(brk), mmap and stack regions. - fn mmap( + fn init_pages( &mut self, addr: usize, size: usize, - prot: u32, + flags: u8, source: Option, - offset: usize, + offset_from_addr: usize, ) -> Result<(), Error>; - fn munmap(&mut self, addr: usize, size: usize) -> Result<(), Error>; // This is in fact just memset fn store_byte(&mut self, addr: usize, size: usize, value: u8) -> Result<(), Error>; fn store_bytes(&mut self, addr: usize, value: &[u8]) -> Result<(), Error>; @@ -49,3 +44,30 @@ pub trait Memory { fn store32(&mut self, addr: &R, value: &R) -> Result<(), Error>; fn store64(&mut self, addr: &R, value: &R) -> Result<(), Error>; } + +#[inline(always)] +pub fn fill_page_data( + memory: &mut Memory, + addr: usize, + size: usize, + source: Option, + offset_from_addr: usize, +) -> Result<(), Error> { + let mut written_size = 0; + if offset_from_addr > 0 { + let real_size = min(size, offset_from_addr); + memory.store_byte(addr, real_size, 0)?; + written_size += real_size; + } + if let Some(source) = source { + let real_size = min(size - written_size, source.len()); + if real_size > 0 { + memory.store_bytes(addr + written_size, &source[0..real_size])?; + written_size += real_size; + } + } + if written_size < size { + memory.store_byte(addr + written_size, size - written_size, 0)?; + } + Ok(()) +} diff --git a/src/memory/sparse.rs b/src/memory/sparse.rs index a02dcbe9..d9656a8a 100644 --- a/src/memory/sparse.rs +++ b/src/memory/sparse.rs @@ -1,5 +1,5 @@ use super::super::{Error, Register, RISCV_MAX_MEMORY, RISCV_PAGESIZE}; -use super::{round_page, Memory, Page}; +use super::{fill_page_data, round_page, Memory, Page}; use bytes::Bytes; use std::cmp::min; @@ -68,40 +68,15 @@ impl SparseMemory { } impl Memory for SparseMemory { - fn mmap( + fn init_pages( &mut self, addr: usize, size: usize, - _prot: u32, + _flags: u8, source: Option, - offset: usize, + offset_from_addr: usize, ) -> Result<(), Error> { - // For simplicity, we implement this using store_bytes for now. Later - // if needed, we can change this to load page from source on demand. - if let Some(source) = source { - let real_size = min(size, source.len() - offset); - let value = &source[offset..offset + real_size]; - return self.store_bytes(addr, value); - } - Ok(()) - } - - fn munmap(&mut self, addr: usize, size: usize) -> Result<(), Error> { - let mut current_page_addr = round_page(addr); - let mut current_page_offset = addr - current_page_addr; - let mut erased_size = 0; - while erased_size < size { - let page = self.fetch_page(current_page_addr)?; - let bytes = min(RISCV_PAGESIZE - current_page_offset, size - erased_size); - unsafe { - let slice_ptr = page[current_page_offset..current_page_offset + bytes].as_mut_ptr(); - ptr::write_bytes(slice_ptr, b'0', bytes); - } - current_page_addr += RISCV_PAGESIZE; - current_page_offset = 0; - erased_size += bytes; - } - Ok(()) + fill_page_data(self, addr, size, source, offset_from_addr) } fn load8(&mut self, addr: &R) -> Result { diff --git a/src/memory/wxorx.rs b/src/memory/wxorx.rs new file mode 100644 index 00000000..00177a4d --- /dev/null +++ b/src/memory/wxorx.rs @@ -0,0 +1,128 @@ +use super::super::{ + bits::{rounddown, roundup}, + Error, Register, RISCV_MAX_MEMORY, RISCV_PAGESIZE, +}; +use super::{Memory, FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE, FLAG_WXORR_BIT}; + +use bytes::Bytes; +use std::marker::PhantomData; + +pub struct WXorXMemory> { + inner: M, + flags: Vec, + _inner: PhantomData, +} + +impl + Default> Default for WXorXMemory { + fn default() -> Self { + Self { + inner: M::default(), + flags: vec![0; RISCV_MAX_MEMORY / RISCV_PAGESIZE], + _inner: PhantomData, + } + } +} + +impl> WXorXMemory { + fn check_permission(&self, addr: usize, size: usize, flag: u8) -> Result<(), Error> { + let e = addr + size; + let mut current_addr = rounddown(addr, RISCV_PAGESIZE); + while current_addr < e { + let page = current_addr / RISCV_PAGESIZE; + self.flags + .get(page) + .ok_or(Error::OutOfBound) + .and_then(|page_flag| { + if (page_flag & FLAG_WXORR_BIT) == (flag & FLAG_WXORR_BIT) { + Ok(()) + } else { + Err(Error::InvalidPermission) + } + })?; + current_addr += RISCV_PAGESIZE; + } + Ok(()) + } +} + +impl> Memory for WXorXMemory { + fn init_pages( + &mut self, + addr: usize, + size: usize, + flags: u8, + source: Option, + offset_from_addr: usize, + ) -> Result<(), Error> { + if rounddown(addr, RISCV_PAGESIZE) != addr || roundup(size, RISCV_PAGESIZE) != size { + return Err(Error::Unaligned); + } + if addr > RISCV_MAX_MEMORY + || size > RISCV_MAX_MEMORY + || addr + size > RISCV_MAX_MEMORY + || offset_from_addr > size + { + return Err(Error::OutOfBound); + } + for page_addr in (addr..addr + size).step_by(RISCV_PAGESIZE) { + let page = page_addr / RISCV_PAGESIZE; + if self.flags[page] & FLAG_FREEZED != 0 { + return Err(Error::InvalidPermission); + } + self.flags[page] = flags; + } + self.inner + .init_pages(addr, size, flags, source, offset_from_addr) + } + + fn execute_load16(&mut self, addr: usize) -> Result { + self.check_permission(addr, 2, FLAG_EXECUTABLE)?; + self.inner.execute_load16(addr) + } + + fn load8(&mut self, addr: &R) -> Result { + self.inner.load8(addr) + } + + fn load16(&mut self, addr: &R) -> Result { + self.inner.load16(addr) + } + + fn load32(&mut self, addr: &R) -> Result { + self.inner.load32(addr) + } + + fn load64(&mut self, addr: &R) -> Result { + self.inner.load64(addr) + } + + fn store8(&mut self, addr: &R, value: &R) -> Result<(), Error> { + self.check_permission(addr.to_usize(), 1, FLAG_WRITABLE)?; + self.inner.store8(addr, value) + } + + fn store16(&mut self, addr: &R, value: &R) -> Result<(), Error> { + self.check_permission(addr.to_usize(), 2, FLAG_WRITABLE)?; + self.inner.store16(addr, value) + } + + fn store32(&mut self, addr: &R, value: &R) -> Result<(), Error> { + self.check_permission(addr.to_usize(), 4, FLAG_WRITABLE)?; + self.inner.store32(addr, value) + } + + fn store64(&mut self, addr: &R, value: &R) -> Result<(), Error> { + self.check_permission(addr.to_usize(), 8, FLAG_WRITABLE)?; + self.inner.store64(addr, value) + } + + fn store_bytes(&mut self, addr: usize, value: &[u8]) -> Result<(), Error> { + self.check_permission(addr, value.len(), FLAG_WRITABLE)?; + self.inner.store_bytes(addr, value) + } + + fn store_byte(&mut self, addr: usize, size: usize, value: u8) -> Result<(), Error> { + self.check_permission(addr, size, FLAG_WRITABLE)?; + self.inner.store_byte(addr, size, value) + } +} diff --git a/tests/programs/invalidexec b/tests/programs/invalidexec deleted file mode 100755 index a4f68cde816a8c666078b5bdea26e23b14769039..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4324 zcmeHLeQZ!V1a2K?I3RXcqd_uw% zJ~0_ss{u*1)Y(wsOMQ*#Lv^D6IaHnOFRTQ}#q4EM>bz)5$Kr;=F;~glgyAObm~pP< zl@66_3o!28tNwN@E_(Qq-=t5abD2z<`_3$MtC6Qx1E*CZKb2M`;$q3~;d7}>I)}X1 z4rNo>p0m?)sw1U`an;51;wL|_8_gGVGaHgyyC?LRO-2u~pSyUIR_iE0xjZZW@m8wG zlD;jD+ssZo6Bpy6!h7YHCEmr`jk#1RLwwy#CF0x%<2F0<+T_Sy-EYmmfo6;K#JpwA zkLDBY$$!1-u(aL1pJ=~uKVCSK*^nCFX5QiQ#>F$Kn457kt~Za{jWHKx)R8QiOMdlO zG5yT$BUAB$IrZZ;jB(oU@|!Q4^cjaKS#&Hv*mkM+V&9DJZ9_8VG-Zp9+q<~sN1@+& z$9C1Z(KGJRdT+dWHq)g}X)YBKr;6oxVS0kzxb0^y-?T56aXL&Ir(?yz-gCMcLo%0i zIv5YPEUTLK+1I;de-ix*z7<)ZO1$h;d8it%+O&ovnt0*%gwnbOvH*Ox^O#6c~EP+J3vw=(XF#DJ9ZT#Kjb=xzx{Y~7mcD3br zD>L^_;$*V9vuQPxc*6E9%F>c(0J+Yo#-4$&VzfkV$TA;nsk<`s}YDC-07C6rwV~(lE zV)zx%C)|PO#ht2CU`-PPM^Wcd|6fJPY-#5&-spNf^zL8(pv9BykvyuvqYC`5DqzFA z0BMiI@uikkJ$*Yr-_mXUf_3f3%};j_618mUE>Xew(}B{pfbmLzvTgz6WdnugB2j~M zGl79-1xq&(5Ed|A0jfj|bycSu-htqDxgPz>{0!cNK8yIc%u=CUiFl?8)7?oqBgx;3 znC7Dl7|_Emv`W4V&LASDbCrbn2hr|BeluF)e-Ewlz6`$61Y$)fzxNe@max7ul+;Uj zy2gLG246!=^;(6cW3XN~YW(lj;16rCx@NwmHMj~&`7Oha>UvfUC;3~^|7Cgo=#8F4 ztkjF}rbSo{yAUh&p%o9+V6g`05f8NB1Em3Prl+ylp<$#u>f=~}ACh0*A4Zi9-m1YR z#6k4aPBUWE?-9R`>-QXDiob>U7~1)AxlH&j;uGk1%YGeh;T>4C@5^{CV%qO(WZZ=K zj*5YP8LuY4oKFMphi=5Z=szv{Hz3}Q<16tF#MSeA5Pu8(OJx2P%x9=7|83}hFuo4f zqW`;9^L-WlFJk?QDJz`9xDed>)OP+x7)Y6*p03pPan&IkB{*F zKvdu(tPo9u}j7MPY%#=e@yzkKf00m^h7! zCJ-7Lj_?Z&*5?(xK%vsRc?Eu-+~mB$AiIF0BT-g%@sY@2SYlVo;OcW?5ipnPw0(;^ zqJlRfVArA?i8B=8|D&&}Ly22VQ+nm{4uu1QSo~HTPRb4Kj;#(>$;%fFvjg5iUyui7 zK`KRHhlB`ngMogoDlw4SO8TH@i}|eNsXP?kzX2%k<@Epn diff --git a/tests/programs/invalidexec.c b/tests/programs/invalidexec.c deleted file mode 100644 index 5e7c7695..00000000 --- a/tests/programs/invalidexec.c +++ /dev/null @@ -1,5 +0,0 @@ -int main(int argc, char *argv[]) { - int (*func)() = argv[0]; - - return func(); -} diff --git a/tests/programs/invalidwrite b/tests/programs/invalidwrite deleted file mode 100755 index dd89f420ab6a5dccd2f2f8b9e9461b3066a707de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4316 zcmeHLZ){W76+h2^W5!BcM`#d^UnR<^Sk$+d+vSrUHhl5PB+6apoj|o34{cEwxYfB5J#J~Rp>)?qQ4teC;Rgo0CI`RyQcJ{Xv!p#hNB5rxUm|Wo3tB^ zS8Crft6ZJHxc6?lcswb3`MH2eug&C!^cn8w^U$V7o>~o@R*igZMwLp6a{(`(OY1W^ z}+R}Vp3FiFTFR%yZClvF0CCRzOGPgaqh;Lt(|#gf-#yeLFIT`EB5Mf zSn3z z=JZ=ljBzI53YgzD>GclNpvSRpxc)-N`L2TQl3_66G-W-Gn|ru*FF=p;mhH0B?j7@L zeUoR;47IA$nhW{V>Di@ZerB9zTK~4oKjY60P1{YHY5V%&j&r(#VK6s1ZD+jP+N^5E z@7wB<{Tj#m;bq_YEKmlXcdERUg_mtw!!b=Ve{)xs-FW~Iwzxr$L zfNp8);ops{p6Z-hJ=;0kuD|}1kMA0#5bNBX@FwyndH2f`PYaz zS-t+bZ6nu(sV`5CV|Gt_GL5NJ!%$UrKBu3pvebQ1b=7g`u|vB#``*o#rxVgzM0&Qv_N~MaZp!i(TKJUwJ^yXXO62+ zCUE!DtJ{d@#I2IIzpk2rVblfG|5s5Wd#L5&o2?IrKl#U>w0MF&kOvufkb(bI2DacG zfYjq~e7|;+r)&3jYTK;ewQl~Z@#*eCqLwY)A&MA39Vku97_R~-o0lF&2WQLh1;l>z z(@ryDt3M*XhU?cYxBndRaqQ>L($W(3w}|*<^t)xh9=C9(nt@-+xEV3+_a+(FApX0G zfgTw*Ay)QlCGLkUi0$ZqP4@o)@lNc&PR35erQ>%XK7jsJvi}2&r?({jC(wVde;qWV z|7Y_2X}mq?7qR^}<@P^B{3XWUC}ZGaLR{$Izu(FM%eo$8-8WUzlh8{o(KcM0KXEUXH& zkg@~CbOOsuS(g$DV2uxi1z!(ZAy%}#N(om(04v{HTxhV~%M4gcBC|@}S>r{60e}^E zS{Qc^?%@AUzJWA+Eu=Go-c@S(o8qTO>HuE}NjBhkH%J{LN0jGDm*&ly3s-3H${{tk7EV3p?T2R z5JEujzak26H&#%8%7>hIMujB%$WN-|h}>(&buffer, &vec!["trace64".into()]); - assert!(result.is_ok()); - assert_eq!(result.unwrap(), 7); + assert!(result.is_err()); + assert_eq!(result.err(), Some(Error::InvalidPermission)); } #[test] @@ -96,7 +96,7 @@ pub fn test_jump0() { let result = run::>(&buffer, &vec!["jump0_64".into()]); assert!(result.is_err()); - assert_eq!(result.err(), Some(Error::InvalidInstruction(0))); + assert_eq!(result.err(), Some(Error::InvalidPermission)); } #[test] diff --git a/tests/test_mmu.rs b/tests/test_mmu.rs deleted file mode 100644 index 463e2cf7..00000000 --- a/tests/test_mmu.rs +++ /dev/null @@ -1,28 +0,0 @@ -extern crate ckb_vm; - -use bytes::Bytes; -use ckb_vm::{run, Mmu}; -use std::fs::File; -use std::io::Read; - -#[test] -pub fn test_invalid_write() { - let mut file = File::open("tests/programs/invalidwrite").unwrap(); - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer).unwrap(); - let buffer: Bytes = buffer.into(); - - let result = run::>(&buffer, &["invalidwrite".into()]); - assert!(result.is_err()); -} - -#[test] -pub fn test_invalid_exec() { - let mut file = File::open("tests/programs/invalidexec").unwrap(); - let mut buffer = Vec::new(); - file.read_to_end(&mut buffer).unwrap(); - let buffer: Bytes = buffer.into(); - - let result = run::>(&buffer, &["invalidexec".into()]); - assert!(result.is_err()); -} From bb2e75be2662cba7800450d86860004d54f1555a Mon Sep 17 00:00:00 2001 From: Xuejie Xiao Date: Wed, 15 May 2019 11:17:38 +0800 Subject: [PATCH 2/2] rename: FLAG_WXORR_BIT => FLAG_WXORX_BIT --- definitions/src/generate_asm_constants.rs | 6 +++--- definitions/src/memory.rs | 4 ++-- src/machine/asm/cdefinitions_generated.h | 2 +- src/machine/asm/execute.S | 4 ++-- src/memory/mod.rs | 2 +- src/memory/wxorx.rs | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/definitions/src/generate_asm_constants.rs b/definitions/src/generate_asm_constants.rs index d2479864..f6221a5e 100644 --- a/definitions/src/generate_asm_constants.rs +++ b/definitions/src/generate_asm_constants.rs @@ -4,7 +4,7 @@ use ckb_vm_definitions::{ RET_MAX_CYCLES_EXCEEDED, RET_OUT_OF_BOUND, TRACE_ITEM_LENGTH, }, instructions::{Instruction, INSTRUCTION_OPCODE_NAMES}, - memory::{FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE, FLAG_WXORR_BIT}, + memory::{FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE, FLAG_WXORX_BIT}, registers::SP, RISCV_MAX_MEMORY, RISCV_PAGESIZE, RISCV_PAGE_SHIFTS, }; @@ -57,8 +57,8 @@ fn main() { FLAG_EXECUTABLE ); println!( - "#define CKB_VM_ASM_MEMORY_FLAG_WXORR_BIT {}", - FLAG_WXORR_BIT + "#define CKB_VM_ASM_MEMORY_FLAG_WXORX_BIT {}", + FLAG_WXORX_BIT ); println!("#define CKB_VM_ASM_MEMORY_FLAG_WRITABLE {}", FLAG_WRITABLE); println!(); diff --git a/definitions/src/memory.rs b/definitions/src/memory.rs index 72b382cc..1699f311 100644 --- a/definitions/src/memory.rs +++ b/definitions/src/memory.rs @@ -2,5 +2,5 @@ pub const FLAG_FREEZED: u8 = 0b01; // CKB VM enforces W^X logic, if this flag is set, current memory page will // be marked as executable, otherwise the page will be writable. pub const FLAG_EXECUTABLE: u8 = 0b10; -pub const FLAG_WXORR_BIT: u8 = 0b10; -pub const FLAG_WRITABLE: u8 = (!FLAG_EXECUTABLE) & FLAG_WXORR_BIT; +pub const FLAG_WXORX_BIT: u8 = 0b10; +pub const FLAG_WRITABLE: u8 = (!FLAG_EXECUTABLE) & FLAG_WXORX_BIT; diff --git a/src/machine/asm/cdefinitions_generated.h b/src/machine/asm/cdefinitions_generated.h index ddab627b..d2281e6d 100644 --- a/src/machine/asm/cdefinitions_generated.h +++ b/src/machine/asm/cdefinitions_generated.h @@ -17,7 +17,7 @@ #define CKB_VM_ASM_MEMORY_FLAG_FREEZED 1 #define CKB_VM_ASM_MEMORY_FLAG_EXECUTABLE 2 -#define CKB_VM_ASM_MEMORY_FLAG_WXORR_BIT 2 +#define CKB_VM_ASM_MEMORY_FLAG_WXORX_BIT 2 #define CKB_VM_ASM_MEMORY_FLAG_WRITABLE 0 #define CKB_VM_ASM_TRACE_STRUCT_SIZE 296 diff --git a/src/machine/asm/execute.S b/src/machine/asm/execute.S index 6dd89bc8..e5ddcf60 100644 --- a/src/machine/asm/execute.S +++ b/src/machine/asm/execute.S @@ -90,7 +90,7 @@ cmp $CKB_VM_ASM_RISCV_PAGES, temp_reg1; \ jae .exit_out_of_bound; \ movzbl CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_FLAGS(MACHINE, temp_reg1), temp_reg2d; \ - and $CKB_VM_ASM_MEMORY_FLAG_WXORR_BIT, temp_reg2d; \ + and $CKB_VM_ASM_MEMORY_FLAG_WXORX_BIT, temp_reg2d; \ cmp $CKB_VM_ASM_MEMORY_FLAG_WRITABLE, temp_reg2d; \ jne .exit_invalid_permission; \ addq $1, temp_reg1; \ @@ -103,7 +103,7 @@ cmp $CKB_VM_ASM_RISCV_PAGES, temp_reg1; \ jae .exit_out_of_bound; \ movzbl CKB_VM_ASM_ASM_CORE_MACHINE_OFFSET_FLAGS(MACHINE, temp_reg1), temp_reg2d; \ - and $CKB_VM_ASM_MEMORY_FLAG_WXORR_BIT, temp_reg2d; \ + and $CKB_VM_ASM_MEMORY_FLAG_WXORX_BIT, temp_reg2d; \ cmp $CKB_VM_ASM_MEMORY_FLAG_WRITABLE, temp_reg2d; \ jne .exit_invalid_permission; \ 1: diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 672813ad..b3cabe88 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -7,7 +7,7 @@ pub mod sparse; pub mod wxorx; pub use ckb_vm_definitions::memory::{ - FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE, FLAG_WXORR_BIT, + FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE, FLAG_WXORX_BIT, }; #[inline(always)] diff --git a/src/memory/wxorx.rs b/src/memory/wxorx.rs index 00177a4d..7c3f46e0 100644 --- a/src/memory/wxorx.rs +++ b/src/memory/wxorx.rs @@ -2,7 +2,7 @@ use super::super::{ bits::{rounddown, roundup}, Error, Register, RISCV_MAX_MEMORY, RISCV_PAGESIZE, }; -use super::{Memory, FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE, FLAG_WXORR_BIT}; +use super::{Memory, FLAG_EXECUTABLE, FLAG_FREEZED, FLAG_WRITABLE, FLAG_WXORX_BIT}; use bytes::Bytes; use std::marker::PhantomData; @@ -33,7 +33,7 @@ impl> WXorXMemory { .get(page) .ok_or(Error::OutOfBound) .and_then(|page_flag| { - if (page_flag & FLAG_WXORR_BIT) == (flag & FLAG_WXORR_BIT) { + if (page_flag & FLAG_WXORX_BIT) == (flag & FLAG_WXORX_BIT) { Ok(()) } else { Err(Error::InvalidPermission)