monster/engine/
symbolic_execution.rs

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    // creates a machine state with a specific memory size
169    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        // TODO: Init main function arguments
184        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            // stdin (0), stdout (1), stderr (2) are already opened by the system
225            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        // C signature: int openat(int dirfd, const char *pathname, int flags, mode_t mode)
653
654        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        // TODO: read actual C-string from virtual memory
679        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                        // we do not partially overwrite words with concrete values
756                        // if at least one byte in a word is uninitialized, the whole word is uninitialized
757                        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}