AFLplusplus / qemu-libafl-bridge

A patched QEMU that exposes an interface for LibAFL-based fuzzers
Other
59 stars 33 forks source link

Systemmode instruction hook causes a crash #27

Closed alwinber closed 1 year ago

alwinber commented 1 year ago

Describe the bug The instruction hook in QEMU systemmode causes a failed assertion in QEMU.

To Reproduce Steps to reproduce the behavior (in LibAFL):

  1. Build the qemu_systemmode example.elf.
  2. Determine a valid instruction address inside using e.g. arm-linux-gnueabi-nm ./example/example.elf | grep main
  3. Add a QemuHelper with an instruction hook on a valid address within the control flow. Minimal Example:
    
    use libafl_qemu::QemuHelper;
    use libafl::prelude::UsesInput;
    use libafl::prelude::HasMetadata;
    use libafl_qemu::QemuHelperTuple;
    use libafl_qemu::QemuHooks;

[derive(Debug,Default)]

pub struct QemuInsHelper {}

impl QemuHelper for QemuInsHelper where S: UsesInput + HasMetadata, { fn firstexec(&self, hooks: &QemuHooks<', QT, S>) where QT: QemuHelperTuple, { hooks.instruction(0x0000012a, exec_ins_hook::<QT, S>, false) // some valid address } }

pub fn exec_ins_hook<QT, S>( hooks: &mut QemuHooks<', QT, S>, _state: Option<&mut S>, _pc: u32, ) where S: UsesInput, QT: QemuHelperTuple, {}

Use it inside ``fuzzer.rs``:
```Rust
        let mut hooks = QemuHooks::new(
            &emu,
            tuple_list!(
                QemuInsHelper::default(),
                ...
  1. Start the fuzzer and observe an error message.

Expected behavior Hooks should be executed without crashing.

Screen output/Screenshots

$ KERNEL=./example/example.elf target/debug/qemu_systemmode -icount shift=4,align=off,sleep=off -machine mps2-an385 -monitor null -kernel ./example/example.elf -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S
FUZZ_INPUT @ 0x290
main address = 0x12a
Breakpoint address = 0x78

**
ERROR:../tcg/tcg.c:2207:tcg_gen_callN: code should not be reached
Bail out! ERROR:../tcg/tcg.c:2207:tcg_gen_callN: code should not be reached
[Objective   #1]  (GLOBAL) run time: 0h-0m-0s, clients: 2, corpus: 0, objectives: 1, executions: 0, exec/sec: 0.000
                  (CLIENT) corpus: 0, objectives: 1, executions: 0, exec/sec: 0.000

Additional context I bisected the issue in the LibAFl repo and found 59bf11 (between 0.9.0 and 0.10.0) to be the last fully working revision, after which a different error from the current one prevented me from further analysis.

andreafioraldi commented 1 year ago

Is the current main branch still affected? I merged an update of the helpers calls in the last few days. Btw, the diff between the commit w/o and w/ the bug is https://github.com/AFLplusplus/qemu-libafl-bridge/compare/f6a2e732e8e225ebb8d1a9399561af7330af31b3..0dc52ed6f3915f727aaec8648706760f278f0571 and the instruction hooks were not touched, so probably a bug introduced with the merge from upstream

alwinber commented 1 year ago

Yes, i took the current main branch at eae6f04, which has QEMU_REVISION = 3a774d.

andreafioraldi commented 1 year ago

for future reference: TCGHelperInfo for the libafl_hook strict was partially not initialized and it worked till a specific commit of QEMU because the code was just writing to it, while it checks the values and so it was keeping the dirty memory from malloc. Specifically, the assert was triggered because info->nr_in was trash (85 in my debugger).