1use super::{
2 bug::{Bug as GenericBug, BugInfo},
3 symbolic_state::{Query, QueryResult, SymbolicState, SymbolicValue, Witness},
4 system::{instruction_to_str, SyscallId},
5 UninitializedMemoryAccessReason,
6};
7use crate::{
8 path_exploration::ExplorationStrategy,
9 solver::{BVOperator, Solver, SolverError},
10 BugFinder,
11};
12use byteorder::{ByteOrder, LittleEndian};
13use bytesize::ByteSize;
14use log::{debug, trace};
15use riscu::{
16 decode, types::*, DecodingError, Instruction, Program, ProgramSegment, Register,
17 INSTRUCTION_SIZE as INSTR_SIZE,
18};
19use std::{fmt, mem::size_of, sync::Arc};
20use thiserror::Error;
21
22const INSTRUCTION_SIZE: u64 = INSTR_SIZE as u64;
23
24pub mod defaults {
25 use super::*;
26
27 pub const MEMORY_SIZE: ByteSize = ByteSize(bytesize::MIB);
28 pub const MAX_EXECUTION_DEPTH: u64 = 1000;
29 pub const OPTIMISTICALLY_PRUNE_SEARCH_SPACE: bool = true;
30}
31
32pub type SymbolicExecutionBug = GenericBug<SymbolicExecutionBugInfo>;
33pub type SymbolicExecutionResult = Result<Option<SymbolicExecutionBug>, SymbolicExecutionError>;
34
35type Bug = SymbolicExecutionBug;
36
37#[derive(Clone, Copy, Debug)]
38pub struct SymbolicExecutionOptions {
39 pub memory_size: ByteSize,
40 pub max_exection_depth: u64,
41 pub optimistically_prune_search_space: bool,
42}
43
44impl Default for SymbolicExecutionOptions {
45 fn default() -> Self {
46 Self {
47 memory_size: defaults::MEMORY_SIZE,
48 max_exection_depth: defaults::MAX_EXECUTION_DEPTH,
49 optimistically_prune_search_space: defaults::OPTIMISTICALLY_PRUNE_SEARCH_SPACE,
50 }
51 }
52}
53
54pub struct SymbolicExecutionEngine<'a, E, S>
55where
56 E: ExplorationStrategy,
57 S: Solver,
58{
59 options: SymbolicExecutionOptions,
60 strategy: &'a E,
61 solver: &'a S,
62}
63
64impl<'a, E, S> SymbolicExecutionEngine<'a, E, S>
65where
66 E: ExplorationStrategy,
67 S: Solver,
68{
69 pub fn new(options: &SymbolicExecutionOptions, strategy: &'a E, solver: &'a S) -> Self {
70 Self {
71 options: *options,
72 strategy,
73 solver,
74 }
75 }
76}
77
78impl<'a, E, S> BugFinder<SymbolicExecutionBugInfo, SymbolicExecutionError>
79 for SymbolicExecutionEngine<'a, E, S>
80where
81 E: ExplorationStrategy,
82 S: Solver,
83{
84 fn search_for_bugs(
85 &self,
86 program: &Program,
87 ) -> Result<Option<GenericBug<SymbolicExecutionBugInfo>>, SymbolicExecutionError> {
88 let mut executor =
89 SymbolicExecutor::new(program, &self.options, self.strategy, self.solver);
90
91 let result = executor.run();
92
93 match result.expect_err("every run has to stop with an error") {
94 SymbolicExecutionError::ExecutionDepthReached(_)
95 | SymbolicExecutionError::ProgramExit(_)
96 | SymbolicExecutionError::NotReachable => Ok(None),
97 SymbolicExecutionError::BugFound(bug) => Ok(Some(bug)),
98 err => Err(err),
99 }
100 }
101}
102
103#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
104pub enum Value {
105 Concrete(u64),
106 Symbolic(SymbolicValue),
107 Uninitialized,
108}
109
110impl fmt::Display for Value {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112 match self {
113 Value::Concrete(c) => write!(f, "{:#x}", c),
114 Value::Symbolic(i) => write!(f, "x{}", i.index()),
115 Value::Uninitialized => write!(f, "uninit"),
116 }
117 }
118}
119
120#[derive(Debug, Clone, Error)]
121pub enum SymbolicExecutionError {
122 #[error("failed to load binary {0:#}")]
123 IoError(Arc<anyhow::Error>),
124
125 #[error("engine does not support {0}")]
126 NotSupported(String),
127
128 #[error("has reached the maximum execution depth of {0}")]
129 ExecutionDepthReached(u64),
130
131 #[error("failed to decode instruction at PC: {0:#010x}")]
132 InvalidInstructionEncoding(u64, DecodingError),
133
134 #[error("fatal error in SMT solver")]
135 Solver(SolverError),
136
137 #[error("found a bug")]
138 BugFound(Bug),
139
140 #[error("exit syscall called with code: {0}")]
141 ProgramExit(Value),
142
143 #[error("program state is not reachable")]
144 NotReachable,
145}
146
147pub struct SymbolicExecutor<'a, E, S>
148where
149 E: ExplorationStrategy,
150 S: Solver,
151{
152 symbolic_state: Box<SymbolicState<'a, S>>,
153 program_break: u64,
154 pc: u64,
155 regs: [Value; 32],
156 memory: Vec<Value>,
157 strategy: &'a E,
158 options: SymbolicExecutionOptions,
159 execution_depth: u64,
160 amount_of_file_descriptors: u64,
161}
162
163impl<'a, E, S> SymbolicExecutor<'a, E, S>
164where
165 E: ExplorationStrategy,
166 S: Solver,
167{
168 pub fn new(
170 program: &Program,
171 options: &SymbolicExecutionOptions,
172 strategy: &'a E,
173 solver: &'a S,
174 ) -> Self {
175 let mut regs = [Value::Uninitialized; 32];
176 let memory_size = options.memory_size.as_u64();
177 let mut memory = vec![Value::Uninitialized; memory_size as usize / 8];
178
179 let sp = memory_size - 8;
180 regs[Register::Sp as usize] = Value::Concrete(sp);
181 regs[Register::Zero as usize] = Value::Concrete(0);
182
183 let argc = 0;
185 memory[sp as usize / size_of::<u64>()] = Value::Concrete(argc);
186
187 load_segment(&mut memory, &program.code);
188 load_segment(&mut memory, &program.data);
189
190 let pc = program.code.address;
191
192 let program_break = program.data.address + program.data.content.len() as u64;
193
194 let symbolic_state = Box::new(SymbolicState::new(solver));
195
196 debug!(
197 "initializing new execution context with {} of main memory",
198 memory_size
199 );
200 debug!(
201 "code segment: start={:#x} length={}",
202 program.code.address,
203 program.code.content.len(),
204 );
205 debug!(
206 "data segment: start={:#x} length={}",
207 program.data.address,
208 program.data.content.len(),
209 );
210 debug!(
211 "init state: pc={:#x} brk={:#x}, argc={}",
212 pc, program_break, argc
213 );
214
215 Self {
216 symbolic_state,
217 program_break,
218 pc,
219 regs,
220 memory,
221 strategy,
222 execution_depth: 0,
223 options: *options,
224 amount_of_file_descriptors: 3,
226 }
227 }
228
229 fn decode(&self, raw: u32) -> Result<Instruction, SymbolicExecutionError> {
230 decode(raw).map_err(|e| SymbolicExecutionError::InvalidInstructionEncoding(self.pc, e))
231 }
232
233 fn run(&mut self) -> Result<(), SymbolicExecutionError> {
234 loop {
235 if self.execution_depth >= self.options.max_exection_depth {
236 trace!("maximum execution depth reached => exiting this context");
237
238 return Err(SymbolicExecutionError::ExecutionDepthReached(
239 self.execution_depth,
240 ));
241 }
242
243 self.execution_depth += 1;
244
245 self.fetch()
246 .and_then(|raw| self.decode(raw))
247 .and_then(|instr| self.execute(instr))?;
248 }
249 }
250
251 fn execute_query(&mut self, query: Query) -> Result<QueryResult, SymbolicExecutionError> {
252 self.symbolic_state
253 .execute_query(query)
254 .map_err(SymbolicExecutionError::Solver)
255 }
256
257 fn access_to_uninitialized_memory(
258 &mut self,
259 instruction: Instruction,
260 v1: Value,
261 v2: Value,
262 ) -> Result<(), SymbolicExecutionError> {
263 trace!(
264 "{}: {}, {} => computing reachability",
265 instruction_to_str(instruction),
266 v1,
267 v2
268 );
269
270 let result = self.execute_query(Query::Reachable)?;
271
272 let info = match result {
273 QueryResult::Sat(witness) => self.collect_bug_info(Some(witness)),
274 _ => self.collect_bug_info(None),
275 };
276
277 trace!("access to uninitialized memory => exiting");
278
279 Err(SymbolicExecutionError::BugFound(
280 self.access_to_uninitialized_memory_bug(
281 info,
282 UninitializedMemoryAccessReason::InstructionWithUninitializedOperand {
283 instruction,
284 operands: vec![v1, v2],
285 },
286 ),
287 ))
288 }
289
290 fn collect_bug_info(&self, witness: Option<Witness>) -> SymbolicExecutionBugInfo {
291 SymbolicExecutionBugInfo {
292 witness,
293 pc: self.pc,
294 }
295 }
296
297 fn access_to_unaligned_address_bug(&self, info: SymbolicExecutionBugInfo) -> Bug {
298 Bug::AccessToUnalignedAddress {
299 info,
300 address: self.pc,
301 }
302 }
303
304 fn access_to_uninitialized_memory_bug(
305 &self,
306 info: SymbolicExecutionBugInfo,
307 reason: UninitializedMemoryAccessReason<SymbolicExecutionBugInfo>,
308 ) -> Bug {
309 Bug::AccessToUnitializedMemory { info, reason }
310 }
311
312 fn access_to_out_of_range_address_bug(&self, info: SymbolicExecutionBugInfo) -> Bug {
313 Bug::AccessToOutOfRangeAddress { info }
314 }
315
316 fn divison_by_zero_bug(&self, info: SymbolicExecutionBugInfo) -> Bug {
317 Bug::DivisionByZero { info }
318 }
319
320 fn exit_code_greater_zero_bug(&self, info: SymbolicExecutionBugInfo, exit_code: Value) -> Bug {
321 Bug::ExitCodeGreaterZero {
322 info,
323 exit_code,
324 address: self.pc,
325 }
326 }
327
328 fn exit_anyway_with_bug<F>(
329 &self,
330 result: QueryResult,
331 info_to_bug: F,
332 ) -> Result<(), SymbolicExecutionError>
333 where
334 F: FnOnce(SymbolicExecutionBugInfo) -> Bug,
335 {
336 let info = self.collect_bug_info(match result {
337 QueryResult::Sat(witness) => Some(witness),
338 QueryResult::UnSat | QueryResult::Unknown => None,
339 });
340
341 let bug = info_to_bug(info);
342
343 Err(SymbolicExecutionError::BugFound(bug))
344 }
345
346 fn exit_anyway_with_bug_if_sat<F>(
347 &self,
348 result: QueryResult,
349 info_to_bug: F,
350 err_if_unsat: SymbolicExecutionError,
351 ) -> Result<(), SymbolicExecutionError>
352 where
353 F: FnOnce(SymbolicExecutionBugInfo) -> Bug,
354 {
355 match result {
356 QueryResult::Sat(witness) => {
357 let bug = info_to_bug(self.collect_bug_info(Some(witness)));
358
359 Err(SymbolicExecutionError::BugFound(bug))
360 }
361 QueryResult::UnSat | QueryResult::Unknown => Err(err_if_unsat),
362 }
363 }
364
365 fn exit_with_bug_if_sat<F>(
366 &self,
367 result: QueryResult,
368 info_to_bug: F,
369 ) -> Result<(), SymbolicExecutionError>
370 where
371 F: FnOnce(SymbolicExecutionBugInfo) -> Bug,
372 {
373 match result {
374 QueryResult::Sat(witness) => {
375 let bug = info_to_bug(self.collect_bug_info(Some(witness)));
376
377 Err(SymbolicExecutionError::BugFound(bug))
378 }
379 QueryResult::UnSat | QueryResult::Unknown => Ok(()),
380 }
381 }
382
383 fn is_in_vaddr_range(&self, vaddr: u64) -> bool {
384 vaddr as usize / size_of::<u64>() < self.memory.len()
385 }
386
387 fn check_for_valid_memory_address(
388 &mut self,
389 instruction: &str,
390 address: u64,
391 ) -> Result<(), SymbolicExecutionError> {
392 let is_alignment_ok = address % size_of::<u64>() as u64 == 0;
393
394 if !is_alignment_ok {
395 trace!(
396 "{}: address {:#x} is not double word aligned => computing reachability",
397 instruction,
398 address
399 );
400
401 self.execute_query(Query::Reachable).and_then(|r| {
402 self.exit_anyway_with_bug(r, |i| self.access_to_unaligned_address_bug(i))
403 })
404 } else if !self.is_in_vaddr_range(address) {
405 trace!(
406 "{}: address {:#x} out of virtual address range (0x0 - {:#x}) => computing reachability",
407 instruction,
408 address,
409 self.memory.len() * size_of::<u64>(),
410 );
411
412 self.execute_query(Query::Reachable).and_then(|r| {
413 self.exit_anyway_with_bug(r, |i| self.access_to_out_of_range_address_bug(i))
414 })
415 } else {
416 Ok(())
417 }
418 }
419
420 fn check_for_valid_memory_range(
421 &mut self,
422 instruction: &str,
423 address: u64,
424 size: u64,
425 ) -> Result<(), SymbolicExecutionError> {
426 if !self.is_in_vaddr_range(address) || !self.is_in_vaddr_range(address + size) {
427 trace!(
428 "{}: buffer {:#x} - {:#x} out of virtual address range (0x0 - {:#x}) => computing reachability",
429 instruction,
430 address,
431 address + size,
432 self.memory.len() * size_of::<u64>(),
433 );
434
435 self.execute_query(Query::Reachable).and_then(|r| {
436 self.exit_anyway_with_bug(r, |i| self.access_to_out_of_range_address_bug(i))
437 })
438 } else {
439 Ok(())
440 }
441 }
442
443 #[allow(clippy::unnecessary_wraps)]
444 fn execute_lui(&mut self, utype: UType) -> Result<(), SymbolicExecutionError> {
445 let immediate = u64::from(utype.imm()) << 12;
446
447 let result = Value::Concrete(immediate);
448
449 trace!(
450 "[{:#010x}] {}: {:?} <- {}",
451 self.pc,
452 instruction_to_str(Instruction::Lui(utype)),
453 utype.rd(),
454 result,
455 );
456
457 self.assign_rd(utype.rd(), result);
458
459 self.pc += INSTRUCTION_SIZE;
460
461 Ok(())
462 }
463
464 fn execute_divu_remu<Op>(
465 &mut self,
466 instruction: Instruction,
467 rtype: RType,
468 op: Op,
469 ) -> Result<(), SymbolicExecutionError>
470 where
471 Op: FnOnce(u64, u64) -> u64,
472 {
473 match self.regs[rtype.rs2() as usize] {
474 Value::Symbolic(divisor) => {
475 trace!(
476 "{}: symbolic divisor -> find input for divisor == 0",
477 instruction_to_str(instruction)
478 );
479
480 self.execute_query(Query::Equals((divisor, 0)))
481 .and_then(|r| self.exit_with_bug_if_sat(r, |i| self.divison_by_zero_bug(i)))?;
482 }
483 Value::Concrete(divisor) if divisor == 0 => {
484 trace!(
485 "{}: divisor == 0 -> compute reachability",
486 instruction_to_str(instruction)
487 );
488
489 return self
490 .execute_query(Query::Reachable)
491 .and_then(|r| self.exit_anyway_with_bug(r, |i| self.divison_by_zero_bug(i)));
492 }
493 _ => {}
494 };
495
496 self.execute_rtype(instruction, rtype, op)
497 }
498
499 fn execute_itype<Op>(
500 &mut self,
501 instruction: Instruction,
502 itype: IType,
503 op: Op,
504 ) -> Result<(), SymbolicExecutionError>
505 where
506 Op: FnOnce(u64, u64) -> u64,
507 {
508 let rs1_value = self.regs[itype.rs1() as usize];
509 let imm_value = Value::Concrete(itype.imm() as u64);
510
511 self.execute_binary_op(instruction, rs1_value, imm_value, itype.rd(), op)
512 }
513
514 fn execute_rtype<Op>(
515 &mut self,
516 instruction: Instruction,
517 rtype: RType,
518 op: Op,
519 ) -> Result<(), SymbolicExecutionError>
520 where
521 Op: FnOnce(u64, u64) -> u64,
522 {
523 let rs1_value = self.regs[rtype.rs1() as usize];
524 let rs2_value = self.regs[rtype.rs2() as usize];
525
526 self.execute_binary_op(instruction, rs1_value, rs2_value, rtype.rd(), op)
527 }
528
529 fn execute_binary_op<Op>(
530 &mut self,
531 instruction: Instruction,
532 lhs: Value,
533 rhs: Value,
534 rd: Register,
535 op: Op,
536 ) -> Result<(), SymbolicExecutionError>
537 where
538 Op: FnOnce(u64, u64) -> u64,
539 {
540 let result = match (lhs, rhs) {
541 (Value::Concrete(v1), Value::Concrete(v2)) => Value::Concrete(op(v1, v2)),
542 (Value::Symbolic(v1), Value::Concrete(v2)) => {
543 let v2 = self.symbolic_state.create_const(v2);
544 Value::Symbolic(self.symbolic_state.create_instruction(instruction, v1, v2))
545 }
546 (Value::Concrete(v1), Value::Symbolic(v2)) => {
547 let v1 = self.symbolic_state.create_const(v1);
548 Value::Symbolic(self.symbolic_state.create_instruction(instruction, v1, v2))
549 }
550 (Value::Symbolic(v1), Value::Symbolic(v2)) => {
551 Value::Symbolic(self.symbolic_state.create_instruction(instruction, v1, v2))
552 }
553 _ => {
554 return self.access_to_uninitialized_memory(instruction, lhs, rhs);
555 }
556 };
557
558 trace!(
559 "[{:#010x}] {}: {}, {} |- {:?} <- {}",
560 self.pc,
561 instruction_to_str(instruction),
562 lhs,
563 rhs,
564 rd,
565 result,
566 );
567
568 self.assign_rd(rd, result);
569
570 self.pc += INSTRUCTION_SIZE;
571
572 Ok(())
573 }
574
575 fn execute_brk(&mut self) -> Result<(), SymbolicExecutionError> {
576 if let Value::Concrete(new_program_break) = self.regs[Register::A0 as usize] {
577 let old_program_break = self.program_break;
578
579 if new_program_break < self.program_break || !self.is_in_vaddr_range(new_program_break)
580 {
581 self.regs[Register::A0 as usize] = Value::Concrete(self.program_break);
582 } else {
583 self.program_break = new_program_break;
584 }
585
586 trace!(
587 "brk: old={:#x} new={:#x}",
588 old_program_break,
589 new_program_break
590 );
591
592 Ok(())
593 } else {
594 not_supported("can not handle symbolic or uninitialized program break")
595 }
596 }
597
598 fn bytewise_combine(
599 &mut self,
600 old: Value,
601 n_lower_bytes: u32,
602 new_idx: SymbolicValue,
603 ) -> SymbolicValue {
604 let bits_in_a_byte = 8;
605 let low_shift_factor = 2_u64.pow(n_lower_bytes * bits_in_a_byte);
606 let high_shift_factor =
607 2_u64.pow((size_of::<u64>() as u32 - n_lower_bytes) * bits_in_a_byte);
608
609 assert!(
610 low_shift_factor != 0 && high_shift_factor != 0,
611 "no bytes to shift"
612 );
613
614 let old_idx = match old {
615 Value::Concrete(c) => {
616 let old_c = c / low_shift_factor * low_shift_factor;
617
618 self.symbolic_state.create_const(old_c)
619 }
620 Value::Symbolic(old_idx) => {
621 let low_shift_factor_idx = self.symbolic_state.create_const(low_shift_factor);
622
623 let old_idx = self.symbolic_state.create_operator(
624 BVOperator::Divu,
625 old_idx,
626 low_shift_factor_idx,
627 );
628
629 self.symbolic_state
630 .create_operator(BVOperator::Mul, old_idx, low_shift_factor_idx)
631 }
632 Value::Uninitialized => {
633 unreachable!("function should not be called for uninitialized values")
634 }
635 };
636
637 let high_shift_factor_idx = self.symbolic_state.create_const(high_shift_factor);
638
639 let new_idx =
640 self.symbolic_state
641 .create_operator(BVOperator::Mul, new_idx, high_shift_factor_idx);
642
643 let new_idx =
644 self.symbolic_state
645 .create_operator(BVOperator::Divu, new_idx, high_shift_factor_idx);
646
647 self.symbolic_state
648 .create_operator(BVOperator::Add, old_idx, new_idx)
649 }
650
651 fn execute_openat(&mut self) -> Result<(), SymbolicExecutionError> {
652 let dirfd = if let Value::Concrete(d) = self.regs[Register::A0 as usize] {
655 d
656 } else {
657 return not_supported("can only handle concrete values for dirfd in openat syscall");
658 };
659
660 let pathname = if let Value::Concrete(p) = self.regs[Register::A1 as usize] {
661 p
662 } else {
663 return not_supported("can only handle concrete values for pathname in openat syscall");
664 };
665
666 let flags = if let Value::Concrete(f) = self.regs[Register::A2 as usize] {
667 f
668 } else {
669 return not_supported("can only handle concrete values for flags in openat syscall");
670 };
671
672 let mode = if let Value::Concrete(m) = self.regs[Register::A3 as usize] {
673 m
674 } else {
675 return not_supported("can only handle concrete values for mode in openat syscall");
676 };
677
678 trace!(
680 "openat: dirfd={} pathname={} flags={} mode={}",
681 dirfd,
682 pathname,
683 flags,
684 mode
685 );
686
687 self.regs[Register::A0 as usize] = Value::Concrete(self.amount_of_file_descriptors);
688
689 self.amount_of_file_descriptors += 1;
690
691 Ok(())
692 }
693
694 fn execute_read(&mut self) -> Result<(), SymbolicExecutionError> {
695 if !matches!(self.regs[Register::A0 as usize], Value::Concrete(0)) {
696 return not_supported("can not handle fd other than stdin in read syscall");
697 }
698
699 let buffer = if let Value::Concrete(b) = self.regs[Register::A1 as usize] {
700 b
701 } else {
702 return not_supported(
703 "can not handle symbolic or uninitialized buffer address in read syscall",
704 );
705 };
706
707 let size = if let Value::Concrete(s) = self.regs[Register::A2 as usize] {
708 s
709 } else {
710 return not_supported("can not handle symbolic or uinitialized size in read syscall");
711 };
712
713 trace!("read: fd={} buffer={:#x} size={}", 0, buffer, size,);
714
715 self.check_for_valid_memory_range("read", buffer, size)?;
716
717 let size_of_u64 = size_of::<u64>() as u64;
718
719 let round_up = if size % size_of_u64 == 0 {
720 0
721 } else {
722 size_of_u64 - size % size_of_u64
723 };
724
725 let mut bytes_to_read = size;
726 let words_to_read = (bytes_to_read + round_up) / size_of_u64;
727
728 let start = buffer / size_of_u64;
729
730 for word_count in 0..words_to_read {
731 let start_byte = word_count * size_of_u64;
732 let end_byte = start_byte
733 + if bytes_to_read < size_of_u64 {
734 bytes_to_read
735 } else {
736 8
737 };
738
739 let name = format!(
740 "read({}, {}, {})[{} - {}]",
741 0, buffer, size, start_byte, end_byte,
742 );
743
744 let input_idx = self.symbolic_state.create_input(&name);
745
746 let addr = (start + word_count) as usize;
747
748 let result_idx = if bytes_to_read >= size_of_u64 {
749 bytes_to_read -= size_of_u64;
750
751 input_idx
752 } else {
753 match self.memory[addr] {
754 Value::Uninitialized => {
755 break;
758 }
759 v => self.bytewise_combine(v, bytes_to_read as u32, input_idx),
760 }
761 };
762
763 self.memory[addr] = Value::Symbolic(result_idx);
764 }
765
766 self.regs[Register::A0 as usize] = Value::Concrete(size);
767
768 Ok(())
769 }
770
771 fn execute_write(&mut self) -> Result<(), SymbolicExecutionError> {
772 if !matches!(self.regs[Register::A0 as usize], Value::Concrete(1)) {
773 return not_supported("can not handle fd other than stdout in write syscall");
774 }
775
776 let buffer = if let Value::Concrete(b) = self.regs[Register::A1 as usize] {
777 b
778 } else {
779 return not_supported(
780 "can not handle symbolic or uninitialized buffer address in write syscall",
781 );
782 };
783
784 let size = if let Value::Concrete(s) = self.regs[Register::A2 as usize] {
785 s
786 } else {
787 return not_supported("can not handle symbolic or uinitialized size in write syscall");
788 };
789
790 trace!("write: fd={} buffer={:#x} size={}", 1, buffer, size,);
791
792 self.check_for_valid_memory_range("write", buffer, size)?;
793
794 let size_of_u64 = size_of::<u64>() as u64;
795 let start = buffer / size_of_u64;
796 let bytes_to_read = size + buffer % size_of_u64;
797 let words_to_read = (bytes_to_read + size_of_u64 - 1) / size_of_u64;
798
799 for word_count in 0..words_to_read {
800 let address = start + word_count;
801
802 if self.memory[address as usize] == Value::Uninitialized {
803 trace!(
804 "write: access to uninitialized memory at {:#x} => computing reachability",
805 (start + word_count) * size_of_u64,
806 );
807
808 return self.execute_query(Query::Reachable).and_then(|r| {
809 self.exit_anyway_with_bug(r, |i| {
810 self.access_to_uninitialized_memory_bug(
811 i,
812 UninitializedMemoryAccessReason::ReadUnintializedMemoryAddress {
813 description: format!(
814 "access to uninitialized memory at address: {:#x}",
815 address
816 ),
817 address,
818 },
819 )
820 })
821 });
822 }
823 }
824
825 self.regs[Register::A0 as usize] = Value::Concrete(size);
826
827 Ok(())
828 }
829
830 fn should_execute_branch(&mut self) -> Result<(bool, &'static str), SymbolicExecutionError> {
831 Ok(match self.execute_query(Query::Reachable)? {
832 QueryResult::Sat(_) => (true, "reachable"),
833 QueryResult::Unknown if !self.options.optimistically_prune_search_space => {
834 (true, "reachability unknown")
835 }
836 _ => (false, "unreachable"),
837 })
838 }
839
840 fn with_snapshot<F, R>(&mut self, f: F) -> R
841 where
842 F: FnOnce(&mut Self) -> R,
843 {
844 let memory_snapshot = self.memory.clone();
845 let regs_snapshot = self.regs;
846 let graph_snapshot = Box::new((*self.symbolic_state).clone());
847 let brk_snapshot = self.program_break;
848 let execution_depth_snapshot = self.execution_depth;
849 let amount_of_file_descriptors_snapshot = self.amount_of_file_descriptors;
850
851 let result = f(self);
852
853 self.memory = memory_snapshot;
854 self.regs = regs_snapshot;
855 self.symbolic_state = graph_snapshot;
856 self.program_break = brk_snapshot;
857 self.execution_depth = execution_depth_snapshot;
858 self.amount_of_file_descriptors = amount_of_file_descriptors_snapshot;
859
860 result
861 }
862
863 fn execute_beq_branches(
864 &mut self,
865 true_branch: u64,
866 false_branch: u64,
867 lhs: SymbolicValue,
868 rhs: SymbolicValue,
869 ) -> Result<(), SymbolicExecutionError> {
870 let next_pc = self.strategy.choose_path(true_branch, false_branch);
871
872 let decision = next_pc == true_branch;
873
874 let result_first_branch =
875 self.with_snapshot(|this| -> Result<(), SymbolicExecutionError> {
876 this.symbolic_state
877 .create_beq_path_condition(decision, lhs, rhs);
878
879 let (execute_branch, reachability) = this.should_execute_branch()?;
880
881 if execute_branch {
882 trace!(
883 "[{:#010x}] beq: x{}, x{} |- assume {} ({}), pc <- {:#x}",
884 this.pc,
885 lhs.index(),
886 rhs.index(),
887 next_pc == false_branch,
888 reachability,
889 next_pc,
890 );
891
892 this.pc = next_pc;
893
894 this.run()
895 } else {
896 trace!(
897 "[{:#010x}] beq: x{}, x{} |- assume {} ({})",
898 this.pc,
899 lhs.index(),
900 rhs.index(),
901 next_pc == false_branch,
902 reachability
903 );
904
905 Err(SymbolicExecutionError::NotReachable)
906 }
907 });
908
909 match result_first_branch {
910 Ok(_) => panic!("every branch execution has to end with an error"),
911 Err(SymbolicExecutionError::ExecutionDepthReached(_))
912 | Err(SymbolicExecutionError::ProgramExit(_))
913 | Err(SymbolicExecutionError::NotReachable) => {}
914 err => {
915 return err;
916 }
917 };
918
919 let next_pc = if decision { false_branch } else { true_branch };
920
921 self.symbolic_state
922 .create_beq_path_condition(!decision, lhs, rhs);
923
924 let (execute_branch, reachability) = self.should_execute_branch()?;
925
926 if execute_branch {
927 trace!(
928 "[{:#010x}] beq: x{}, x{} |- assume {} ({}), pc <- {:#x}",
929 self.pc,
930 lhs.index(),
931 rhs.index(),
932 next_pc == false_branch,
933 reachability,
934 next_pc,
935 );
936
937 self.pc = next_pc;
938
939 Ok(())
940 } else {
941 trace!(
942 "[{:#010x}] beq: x{}, x{} |- assume {} ({})",
943 self.pc,
944 lhs.index(),
945 rhs.index(),
946 next_pc == false_branch,
947 reachability
948 );
949
950 result_first_branch
951 }
952 }
953
954 fn execute_beq(&mut self, btype: BType) -> Result<(), SymbolicExecutionError> {
955 let lhs = self.regs[btype.rs1() as usize];
956 let rhs = self.regs[btype.rs2() as usize];
957
958 let true_branch = self.pc.wrapping_add(btype.imm() as u64);
959 let false_branch = self.pc.wrapping_add(INSTRUCTION_SIZE);
960
961 match (lhs, rhs) {
962 (Value::Concrete(v1), Value::Concrete(v2)) => {
963 let old_pc = self.pc;
964
965 self.pc = if v1 == v2 { true_branch } else { false_branch };
966
967 trace!(
968 "[{:#010x}] beq: {}, {} |- pc <- {:#x}",
969 old_pc,
970 lhs,
971 rhs,
972 self.pc
973 );
974
975 Ok(())
976 }
977 (Value::Symbolic(v1), Value::Concrete(v2)) => {
978 let v2 = self.symbolic_state.create_const(v2);
979 self.execute_beq_branches(true_branch, false_branch, v1, v2)
980 }
981 (Value::Concrete(v1), Value::Symbolic(v2)) => {
982 let v1 = self.symbolic_state.create_const(v1);
983 self.execute_beq_branches(true_branch, false_branch, v1, v2)
984 }
985 (Value::Symbolic(v1), Value::Symbolic(v2)) => {
986 self.execute_beq_branches(true_branch, false_branch, v1, v2)
987 }
988 (v1, v2) => self.access_to_uninitialized_memory(Instruction::Beq(btype), v1, v2),
989 }
990 }
991
992 fn execute_exit(&mut self) -> Result<(), SymbolicExecutionError> {
993 match self.regs[Register::A0 as usize] {
994 Value::Symbolic(exit_code) => {
995 trace!("exit: symbolic code -> find input for exit_code != 0");
996
997 self.execute_query(Query::NotEquals((exit_code, 0)))
998 .and_then(|r| {
999 self.exit_anyway_with_bug_if_sat(
1000 r,
1001 |i| self.exit_code_greater_zero_bug(i, Value::Symbolic(exit_code)),
1002 SymbolicExecutionError::ProgramExit(Value::Symbolic(exit_code)),
1003 )
1004 })
1005 }
1006 Value::Concrete(exit_code) => {
1007 if exit_code > 0 {
1008 trace!(
1009 "exit: with code {} -> find input to satisfy path condition",
1010 exit_code
1011 );
1012
1013 self.execute_query(Query::Reachable).and_then(|r| {
1014 self.exit_anyway_with_bug(r, |i| {
1015 self.exit_code_greater_zero_bug(i, Value::Concrete(exit_code))
1016 })
1017 })
1018 } else {
1019 trace!("exiting context with exit_code 0");
1020
1021 Err(SymbolicExecutionError::ProgramExit(Value::Concrete(
1022 exit_code,
1023 )))
1024 }
1025 }
1026 _ => not_supported("exit only implemented for symbolic exit codes"),
1027 }
1028 }
1029
1030 fn execute_ecall(&mut self) -> Result<(), SymbolicExecutionError> {
1031 trace!("[{:#010x}] ecall", self.pc);
1032
1033 let result = match self.regs[Register::A7 as usize] {
1034 Value::Concrete(syscall_id) if syscall_id == (SyscallId::Brk as u64) => {
1035 self.execute_brk()
1036 }
1037 Value::Concrete(syscall_id) if syscall_id == (SyscallId::Openat as u64) => {
1038 self.execute_openat()
1039 }
1040 Value::Concrete(syscall_id) if syscall_id == (SyscallId::Read as u64) => {
1041 self.execute_read()
1042 }
1043 Value::Concrete(syscall_id) if syscall_id == (SyscallId::Write as u64) => {
1044 self.execute_write()
1045 }
1046 Value::Concrete(syscall_id) if syscall_id == (SyscallId::Exit as u64) => {
1047 self.execute_exit()
1048 }
1049 id => Err(SymbolicExecutionError::NotSupported(format!(
1050 "syscall with id ({}) is not supported",
1051 id
1052 ))),
1053 };
1054
1055 self.pc += INSTRUCTION_SIZE;
1056
1057 result
1058 }
1059
1060 fn execute_ld(
1061 &mut self,
1062 instruction: Instruction,
1063 itype: IType,
1064 ) -> Result<(), SymbolicExecutionError> {
1065 if let Value::Concrete(base_address) = self.regs[itype.rs1() as usize] {
1066 let immediate = itype.imm() as u64;
1067
1068 let address = base_address.wrapping_add(immediate);
1069
1070 self.check_for_valid_memory_address(instruction_to_str(instruction), address)?;
1071
1072 let value = self.memory[(address / 8) as usize];
1073
1074 trace!(
1075 "[{:#010x}] {}: {:#x}, {} |- {:?} <- mem[{:#x}]={}",
1076 self.pc,
1077 instruction_to_str(instruction),
1078 base_address,
1079 immediate,
1080 itype.rd(),
1081 address,
1082 value,
1083 );
1084
1085 self.assign_rd(itype.rd(), value);
1086
1087 self.pc += INSTRUCTION_SIZE;
1088
1089 Ok(())
1090 } else {
1091 not_supported("can not handle symbolic addresses in LD")
1092 }
1093 }
1094
1095 fn execute_sd(
1096 &mut self,
1097 instruction: Instruction,
1098 stype: SType,
1099 ) -> Result<(), SymbolicExecutionError> {
1100 if let Value::Concrete(base_address) = self.regs[stype.rs1() as usize] {
1101 let immediate = stype.imm();
1102
1103 let address = base_address.wrapping_add(immediate as u64);
1104
1105 self.check_for_valid_memory_address(instruction_to_str(instruction), address)?;
1106
1107 let value = self.regs[stype.rs2() as usize];
1108
1109 trace!(
1110 "[{:#010x}] {}: {:#x}, {}, {} |- mem[{:#x}] <- {}",
1111 self.pc,
1112 instruction_to_str(instruction),
1113 base_address,
1114 immediate,
1115 self.regs[stype.rs2() as usize],
1116 address,
1117 value,
1118 );
1119
1120 self.memory[(address / 8) as usize] = value;
1121
1122 self.pc += INSTRUCTION_SIZE;
1123
1124 Ok(())
1125 } else {
1126 not_supported("can not handle symbolic addresses in SD")
1127 }
1128 }
1129
1130 #[allow(clippy::unnecessary_wraps)]
1131 fn execute_jal(&mut self, jtype: JType) -> Result<(), SymbolicExecutionError> {
1132 let link = self.pc + INSTRUCTION_SIZE;
1133
1134 let new_pc = self.pc.wrapping_add(jtype.imm() as u64);
1135
1136 trace!(
1137 "[{:#010x}] jal: pc <- {:#x}, {:?} <- {:#x}",
1138 self.pc,
1139 new_pc,
1140 jtype.rd(),
1141 link,
1142 );
1143
1144 self.pc = new_pc;
1145
1146 self.assign_rd(jtype.rd(), Value::Concrete(link));
1147
1148 Ok(())
1149 }
1150
1151 fn assign_rd(&mut self, rd: Register, v: Value) {
1152 if rd != Register::Zero {
1153 self.regs[rd as usize] = v;
1154 }
1155 }
1156
1157 fn execute_jalr(&mut self, itype: IType) -> Result<(), SymbolicExecutionError> {
1158 if let Value::Concrete(dest) = self.regs[itype.rs1() as usize] {
1159 let link = self.pc + INSTRUCTION_SIZE;
1160
1161 let new_pc = dest.wrapping_add(itype.imm() as u64);
1162
1163 trace!(
1164 "[{:#010x}] jalr: {:#x}, {} |- pc <- {:#x}, {:?} <- {:#x}",
1165 self.pc,
1166 dest,
1167 itype.imm(),
1168 new_pc,
1169 itype.rd(),
1170 link
1171 );
1172
1173 self.assign_rd(itype.rd(), Value::Concrete(link));
1174
1175 self.pc = new_pc;
1176
1177 Ok(())
1178 } else {
1179 not_supported("can only handle concrete addresses in JALR")
1180 }
1181 }
1182
1183 fn fetch(&self) -> Result<u32, SymbolicExecutionError> {
1184 if let Value::Concrete(dword) = self.memory[(self.pc as usize / size_of::<u64>()) as usize]
1185 {
1186 if self.pc % size_of::<u64>() as u64 == 0 {
1187 Ok(dword as u32)
1188 } else {
1189 Ok((dword >> 32) as u32)
1190 }
1191 } else {
1192 Err(SymbolicExecutionError::NotSupported(String::from(
1193 "tried to fetch none concrete instruction",
1194 )))
1195 }
1196 }
1197
1198 fn execute(&mut self, instruction: Instruction) -> Result<(), SymbolicExecutionError> {
1199 match instruction {
1200 Instruction::Ecall(_) => self.execute_ecall(),
1201 Instruction::Lui(utype) => self.execute_lui(utype),
1202 Instruction::Addi(itype) => self.execute_itype(instruction, itype, u64::wrapping_add),
1203 Instruction::Add(rtype) => self.execute_rtype(instruction, rtype, u64::wrapping_add),
1204 Instruction::Sub(rtype) => self.execute_rtype(instruction, rtype, u64::wrapping_sub),
1205 Instruction::Mul(rtype) => self.execute_rtype(instruction, rtype, u64::wrapping_mul),
1206 Instruction::Divu(rtype) => {
1207 self.execute_divu_remu(instruction, rtype, u64::wrapping_div)
1208 }
1209 Instruction::Remu(rtype) => {
1210 self.execute_divu_remu(instruction, rtype, u64::wrapping_rem)
1211 }
1212 Instruction::Sltu(rtype) => {
1213 self.execute_rtype(instruction, rtype, |l, r| if l < r { 1 } else { 0 })
1214 }
1215 Instruction::Ld(itype) => self.execute_ld(instruction, itype),
1216 Instruction::Sd(stype) => self.execute_sd(instruction, stype),
1217 Instruction::Jal(jtype) => self.execute_jal(jtype),
1218 Instruction::Jalr(itype) => self.execute_jalr(itype),
1219 Instruction::Beq(btype) => self.execute_beq(btype),
1220 }
1221 }
1222}
1223
1224fn load_segment(memory: &mut Vec<Value>, segment: &ProgramSegment<u8>) {
1225 let start = segment.address as usize / size_of::<u64>();
1226 let end = start + segment.content.len() / size_of::<u64>();
1227
1228 segment
1229 .content
1230 .chunks(size_of::<u64>())
1231 .map(LittleEndian::read_u64)
1232 .zip(start..end)
1233 .for_each(|(x, i)| memory[i] = Value::Concrete(x));
1234}
1235
1236fn not_supported(s: &str) -> Result<(), SymbolicExecutionError> {
1237 Err(SymbolicExecutionError::NotSupported(s.to_owned()))
1238}
1239
1240#[derive(Debug, Clone)]
1241pub struct SymbolicExecutionBugInfo {
1242 pub witness: Option<Witness>,
1243 pub pc: u64,
1244}
1245
1246impl BugInfo for SymbolicExecutionBugInfo {
1247 type Value = Value;
1248}
1249
1250impl fmt::Display for SymbolicExecutionBugInfo {
1251 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1252 write!(f, "pc: {:#010x}", self.pc)?;
1253
1254 if let Some(witness) = &self.witness {
1255 write!(f, "\nwitness: {}", witness)
1256 } else {
1257 Ok(())
1258 }
1259 }
1260}