LibAFL icon indicating copy to clipboard operation
LibAFL copied to clipboard

libafl-qemu: multiple consecutive `load_snapshot` calls cause segmentation fault

Open samcowger opened this issue 1 year ago • 1 comments

Like @langston-barrett in #2628, I'm trying to fuzz an EDKII image while using snapshots. I'm running into an issue where restoring a snapshot twice in a row, without starting/restarting QEMU in between those restorations, will reliably cause a segmentation fault once QEMU restarts. (Also like Langston, I can't share the compiled artifacts used here, but I strongly suspect the issue is independent of them, and should be replicable with another systemmode target.) Consider this example:

// cargo init
// cargo add --no-default-features --features=systemmode --git https://github.com/AFLplusplus/LibAFL libafl_qemu
// cargo run

use std::error;

const QEMU_FLAGS: &[&str] = &[
    "-machine",
    "q35",
    "-kernel",
    "./impl/bzImage",
    "-append",
    "'rootwait root=/dev/vda console=tty1 console=ttyS0 keep_bootcon'",
    "-drive",
    "file=./impl/rootfs.ext2,if=virtio,format=raw,readonly=on",
    "-global",
    "driver=cfi.pflash01,property=secure,value=on",
    "-drive",
    "if=pflash,format=raw,unit=1,file=./impl/OVMF_VARS.fd",
    "-drive",
    "if=pflash,format=raw,unit=0,readonly=on,file=./impl/OVMF_CODE.fd",
    "-smp",
    "2",
    "-m",
    "4G",
    "-bios",
    "./impl/OVMF.fd",
    "-snapshot",
    "-S",
    "-nodefaults",
];

const SNAPSHOT_NAME: &'static str = "snapshot";
const SYNC: bool = true;

fn main() -> Result<(), Box<dyn error::Error>> {
    let mut args = vec!["qemu".to_owned()];
    args.extend(QEMU_FLAGS.iter().map(|s| (*s).to_owned()));
    let qemu = libafl_qemu::Qemu::init(args.as_slice())?;

    let entry = 0x7FFA3C01;

    println!("Saving snapshot...");
    qemu.save_snapshot(SNAPSHOT_NAME, SYNC);

    println!("Loading snapshot...");
    qemu.load_snapshot(SNAPSHOT_NAME, SYNC);

    // println!("Loading snapshot again...");
    // qemu.load_snapshot(SNAPSHOT_NAME, SYNC);

    println!("Running to {entry:#x}...");
    qemu.entry_break(entry);
    println!("...finished.");

    Ok(())
}

Without the second load_snapshot, I see this run to completion. With the second load_snapshot, I get a segmentation fault when QEMU restarts:

rom: file kvmvapic.bin        : error Failed to open file “kvmvapic.bin”: No such file or directory
rom: file linuxboot_dma.bin   : error Failed to open file “linuxboot_dma.bin”: No such file or directory
Saving snapshot...
Loading snapshot...
Loading snapshot again...
Running to 0x7ffa3c01...
Segmentation fault (core dumped)

Here's a backtrace:

Saving snapshot...
Loading snapshot...
Loading snapshot again...
Running to 0x7ffa3c01...

Thread 1 "scratch" received signal SIGSEGV, Segmentation fault.
0x00005555571c68c0 in ?? ()
(gdb) bt
#0  0x00005555571c68c0 in ?? ()
#1  0x0000555555a6e604 in vm_state_notify (running=running@entry=true, state=state@entry=RUN_STATE_RUNNING) at ../system/runstate.c:399
#2  0x0000555555a64fca in vm_prepare_start (step_pending=step_pending@entry=false) at ../system/cpus.c:778
#3  0x0000555555a65034 in vm_start () at ../system/cpus.c:799
#4  0x00005555558609db in libafl_qemu::qemu::Qemu::run_inner (self=0x7fffffffd917) at src/qemu/systemmode.rs:219
#5  0x000055555586116b in libafl_qemu::qemu::Qemu::run (self=0x7fffffffd917) at src/qemu/mod.rs:668
#6  0x000055555586147e in libafl_qemu::qemu::Qemu::entry_break (self=0x7fffffffd917, addr=2147105793) at src/qemu/mod.rs:864
#7  0x000055555585f3c5 in scratch::main () at src/main.rs:53
(gdb)

I've done some digging and hypothesize that this might be due to a bug in qemu-libafl-bridge, so let me know if I ought to move discussion to an issue there instead.

samcowger avatar Dec 05 '24 21:12 samcowger

thank you for the report, i'll have a look this week.

rmalmain avatar Jan 02 '25 08:01 rmalmain