diff --git a/src/rarity.rs b/src/rarity.rs index 2d6f45a6..52a94bd6 100644 --- a/src/rarity.rs +++ b/src/rarity.rs @@ -577,7 +577,7 @@ impl Engine { fn execute(&mut self, instruction: Instruction) -> Result, EngineError> { match instruction { - Instruction::Ecall(_) => self.execute_ecall(), + Instruction::Ecall(_) => self.execute_ecall(instruction), Instruction::Lui(utype) => self.execute_lui(utype), Instruction::Addi(itype) => self.execute_itype(instruction, itype, u64::wrapping_add), Instruction::Add(rtype) => self.execute_rtype(instruction, rtype, u64::wrapping_add), @@ -672,6 +672,34 @@ impl Engine { } } + fn check_for_valid_memory_range( + &mut self, + instruction: &str, + address: u64, + size: u64, + ) -> Result, EngineError> { + if !self.is_in_vaddr_range(address) || !self.is_in_vaddr_range(address + size) { + trace!( + "{}: buffer {:#x} - {:#x} out of virtual address range (0x0 - {:#x}) => computing reachability", + instruction, + address, + address + size, + self.state.memory.len() * 8, + ); + + self.is_running = false; + + Ok(Some(Bug::AccessToOutOfRangeAddress { + info: RarityBugInfo { + witness: self.concrete_inputs.clone(), + pc: self.state.pc, + }, + })) + } else { + Ok(None) + } + } + fn execute_lui(&mut self, utype: UType) -> Result, EngineError> { let immediate = u64::from(utype.imm()) << 12; @@ -765,7 +793,7 @@ impl Engine { _ => { let bug = self.check_for_uninitialized_memory(instruction, lhs, rhs)?; - trace!("could not find input assignment => exeting this context"); + trace!("could not find input assignment => exiting this context"); self.is_running = false; @@ -834,8 +862,9 @@ impl Engine { trace!("read: fd={} buffer={:#x} size={}", 0, buffer, size,); - if !self.is_in_vaddr_range(buffer) || !self.is_in_vaddr_range(buffer + size) { - return not_supported("read syscall failed to"); + let bug = self.check_for_valid_memory_range("read", buffer, size)?; + if bug.is_some() { + return Ok(bug); } let size_of_u64 = size_of::() as u64; @@ -894,6 +923,60 @@ impl Engine { Ok(None) } + fn execute_write(&mut self, instruction: Instruction) -> Result, EngineError> { + if !matches!(self.state.regs[Register::A0 as usize], Value::Concrete(1)) { + return not_supported("can not handle other fd than stdout in write syscall"); + } + + let buffer = if let Value::Concrete(b) = self.state.regs[Register::A1 as usize] { + b + } else { + return not_supported( + "can not handle symbolic or uninitialized buffer address in write syscall", + ); + }; + + let size = if let Value::Concrete(s) = self.state.regs[Register::A2 as usize] { + s + } else { + return not_supported("can not handle symbolic or uinitialized size in write syscall"); + }; + + trace!("write: fd={} buffer={:#x} size={}", 1, buffer, size,); + + let bug = self.check_for_valid_memory_range("write", buffer, size)?; + if bug.is_some() { + return Ok(bug); + } + + let size_of_u64 = size_of::() as u64; + let start = buffer / size_of_u64; + let bytes_to_read = size + buffer % size_of_u64; + let words_to_read = (bytes_to_read + size_of_u64 - 1) / size_of_u64; + + for word_count in 0..words_to_read { + if self.state.memory[(start + word_count) as usize] == Value::Uninitialized { + trace!( + "write: access to uninitialized memory at {:#x} => computing reachability", + (start + word_count) * size_of_u64, + ); + + return Ok(Some(Bug::AccessToUnitializedMemory { + info: RarityBugInfo { + witness: self.concrete_inputs.clone(), + pc: self.state.pc, + }, + instruction, + operands: vec![], + })); + } + } + + self.state.regs[Register::A0 as usize] = Value::Concrete(size); + + Ok(None) + } + fn execute_beq(&mut self, btype: BType) -> Result, EngineError> { let lhs = self.state.regs[btype.rs1() as usize]; let rhs = self.state.regs[btype.rs2() as usize]; @@ -922,7 +1005,7 @@ impl Engine { let result = self.check_for_uninitialized_memory(Instruction::Beq(btype), v1, v2); - trace!("access to uninitialized memory => exeting this context"); + trace!("access to uninitialized memory => exiting this context"); result } @@ -956,7 +1039,7 @@ impl Engine { } } - fn execute_ecall(&mut self) -> Result, EngineError> { + fn execute_ecall(&mut self, instruction: Instruction) -> Result, EngineError> { trace!("[{:#010x}] ecall", self.state.pc); let result = match self.state.regs[Register::A7 as usize] { @@ -966,6 +1049,9 @@ impl Engine { Value::Concrete(syscall_id) if syscall_id == (SyscallId::Read as u64) => { self.execute_read() } + Value::Concrete(syscall_id) if syscall_id == (SyscallId::Write as u64) => { + self.execute_write(instruction) + } Value::Concrete(syscall_id) if syscall_id == (SyscallId::Exit as u64) => { self.execute_exit() } diff --git a/tests/engine.rs b/tests/engine.rs index 975638f0..baa30fb8 100644 --- a/tests/engine.rs +++ b/tests/engine.rs @@ -15,7 +15,7 @@ use std::{ }; use utils::{compile_riscu, init, with_temp_dir}; -const TEST_FILES: [&str; 18] = [ +const TEST_FILES: [&str; 19] = [ "arithmetic.c", "echo-line.c", "if-else.c", // needs timeout