Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
9c99865
update to qemu v10.2.0
rmalmain Jan 5, 2026
9d898fe
update qemu hash
rmalmain Jan 5, 2026
1e6d09b
revert fmt
rmalmain Jan 5, 2026
1ffdd7c
add a qemu-full workflow.
rmalmain Jan 5, 2026
f4bad66
usermode adaptation.
rmalmain Jan 5, 2026
5484eb4
fix fuzzer
rmalmain Jan 5, 2026
a987211
add migration notes.
rmalmain Jan 5, 2026
c1bd4a5
ppc
rmalmain Jan 5, 2026
726e1b5
fuzzbench_qemu
rmalmain Jan 5, 2026
6a0e718
feature
rmalmain Jan 5, 2026
eefe58b
usermode type export
rmalmain Jan 5, 2026
658fe51
i386
rmalmain Jan 5, 2026
5ad0a47
handle hexagon separately in ci
rmalmain Jan 5, 2026
879ca9e
forgot to remove hexagon from full
rmalmain Jan 5, 2026
e50e524
tmin
rmalmain Jan 5, 2026
df1de67
update qemu hash
rmalmain Jan 5, 2026
dbb3e54
fix env
rmalmain Jan 5, 2026
dba98da
GuestAddr for filter ranges
rmalmain Jan 5, 2026
1c1c865
cast
rmalmain Jan 5, 2026
fe20a28
whatever
rmalmain Jan 5, 2026
c456abf
ye
rmalmain Jan 5, 2026
9edbb5d
now?
rmalmain Jan 5, 2026
5971a73
mips
rmalmain Jan 5, 2026
0430673
architecture specific test
rmalmain Jan 5, 2026
1dca46c
hexagon
rmalmain Jan 5, 2026
3f92cdd
minor fixes
rmalmain Jan 5, 2026
c88a64f
minor fixes
rmalmain Jan 5, 2026
656609c
adapt more arch
rmalmain Jan 5, 2026
3580e11
minor stuff
rmalmain Jan 5, 2026
7e1f65b
oops
rmalmain Jan 5, 2026
ce3a4b5
more fixes
rmalmain Jan 5, 2026
33d8079
fix
rmalmain Jan 5, 2026
f97af33
fix
rmalmain Jan 5, 2026
c6b0984
fix
rmalmain Jan 5, 2026
4c2ee4e
fmt
rmalmain Jan 5, 2026
736298d
fix nyx parser
rmalmain Jan 5, 2026
d02d6b6
clippy, python
rmalmain Jan 5, 2026
e74915d
python
rmalmain Jan 5, 2026
c1d74d5
final qemu hash update
rmalmain Jan 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 58 additions & 2 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ jobs:
- name: Configure Cache
uses: Swatinem/rust-cache@v2
with:
# We will have each of these fuzzers have it's own cache since these
# We will have each of these fuzzers have its own cache since these
# are some of the heaviest fuzzers to build.
shared-key: qemu-${{ steps.fuzzer_name.outputs.fuzzer_name }}-x86_64
# We want to include the commit hash to ensure the cache is replaced
Expand Down Expand Up @@ -530,7 +530,7 @@ jobs:
- name: Configure Cache
uses: Swatinem/rust-cache@v2
with:
# We will have each of these fuzzers have it's own cache since these
# We will have each of these fuzzers have its own cache since these
# are some of the heaviest fuzzers to build.
shared-key: qemu-${{ steps.fuzzer_name.outputs.fuzzer_name }}-arm
# We want to include the commit hash to ensure the cache is replaced
Expand Down Expand Up @@ -623,6 +623,62 @@ jobs:
shell: bash
run: just -d utils/${{ matrix.util }} --justfile utils/${{ matrix.util }}/Justfile test

libafl_qemu-full:
if: contains(github.event.pull_request.labels.*.name, 'qemu-full')
runs-on: ubuntu-24.04
strategy:
matrix:
mode:
- usermode
- systemmode
arch:
- aarch64
- arm
- i386
- mips
- ppc
- riscv32
- riscv64
- x86_64
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Prepare
uses: ./.github/workflows/qemu-fuzzer-tester-prepare
- name: Configure Cache
uses: Swatinem/rust-cache@v2
# ---- build normal and examples ----
- name: Build LibAFL QEMU (${{ matrix.mode }} - ${{ matrix.arch }})
env:
LLVM_CONFIG: llvm-config-${{env.MAIN_LLVM_VERSION}}
run: cargo build --verbose --package libafl_qemu --no-default-features --features ${{ matrix.mode }},${{ matrix.arch }}
- name: Test LibAFL QEMU (${{ matrix.mode }} - ${{ matrix.arch }})
env:
LLVM_CONFIG: llvm-config-${{env.MAIN_LLVM_VERSION}}
run: cargo test --package libafl_qemu --no-default-features --features ${{ matrix.mode }},${{ matrix.arch }}

# hexagon systemmode is not available yet in qemu upstream.
# in the meantime, we only test usermode
libafl_qemu-hexagon:
if: contains(github.event.pull_request.labels.*.name, 'qemu-full')
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Prepare
uses: ./.github/workflows/qemu-fuzzer-tester-prepare
- name: Configure Cache
uses: Swatinem/rust-cache@v2
# ---- build normal and examples ----
- name: Build LibAFL QEMU (usermode - hexagon)
env:
LLVM_CONFIG: llvm-config-${{env.MAIN_LLVM_VERSION}}
run: cargo build --verbose --package libafl_qemu --no-default-features --features usermode,hexagon
- name: Test LibAFL QEMU (usermode - hexagon)
env:
LLVM_CONFIG: llvm-config-${{env.MAIN_LLVM_VERSION}}
run: cargo test --package libafl_qemu --no-default-features --features usermode,hexagon

libafl_asan:
name: 🔧 libafl_asan
runs-on: ubuntu-24.04
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ RUN apt-get update && \

# Install a modern version of QEMU
WORKDIR /root
ENV QEMU_VER=10.0.0
ENV QEMU_VER=10.2.0
RUN wget https://download.qemu.org/qemu-${QEMU_VER}.tar.xz && \
tar xvJf qemu-${QEMU_VER}.tar.xz && \
cd /root/qemu-${QEMU_VER} && \
Expand Down
9 changes: 8 additions & 1 deletion MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

## 0.15.0 -> 0.16.0

### LibAFL

- `EventManager` is refactored to avoid calling function from `Fuzzer`, thus we do not evaluate testcases in `EventManager` anymore.
- Now we have `EventReceiver` in `events` module, and `EventProcessor` in `fuzzer` module.
- `EventReceiver` is responsible for receiving testcases and delegates its evaluation to `EventProcessor`.
- `EventProcessor` is responsible for evaluating the testcases passed by the `EventReceiver`.
- Since we don't evaluate testcases in the `EventManager` anymore. `on_fire` and `post_exec` have been deleted from `EventManagerHook`.
- Similarly `pre_exec` has been renamed to `pre_receive`.
- `AsanModule` now uses a `builder()` method for constructing its instances.
- `Monitor` is refactored. Most statistics have been extracted into an individual `stats` module under `monitors`.
- There is a `ClientStatsManager` to manage client statistics, and is owned by `EventManager`. Most of previous `Monitor`'s trait methods have been moved to the `ClientStatsManager`.
- `user_monitor` has been renamed to `user_stats`, `introspection_monitor` has been renamed to `introspection_stats`, perf-related structure definitions have been renamed, and all were moved to the `stats` module.
Expand All @@ -18,6 +19,12 @@
- If you don't need the keys to identify individual parts, consider using `ListInput` directly.
- `StdScheduledMutator` has been renamed to `HavocScheduledMutator`.

### LibAFL QEMU
- `AsanModule` now uses a `builder()` method for constructing its instances.

- `GuestAddr` changed its internal type alias, causing some minor type mismatches.
It should be safe to cast to `GuestAddr` in most cases where it was used before.

## 0.14.1 -> 0.15.0

- `MmapShMem::new` and `MmapShMemProvider::new_shmem_with_id` now take `AsRef<Path>` instead of a byte array for the filename/id.
Expand Down
25 changes: 14 additions & 11 deletions crates/libafl_cc/src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -368,23 +368,26 @@ mod tests {
assert_eq!(entry.calling_func, "main");
assert_eq!(entry.successor_edges.len(), 2);
assert_eq!(entry.node_loc, 41864);
assert_eq!(entry.successor_edges[0], (41864 >> 1) ^ 52706);
assert_eq!(entry.successor_edges[1], (41864 >> 1) ^ 26911);
assert_eq!(entry.successor_edges[0], (41864 >> 1) ^ 0xcde2);
assert_eq!(entry.successor_edges[1], (41864 >> 1) ^ 0x691f);

let mut edge = cfg.get_edge((50306 >> 1) ^ 19123).unwrap();
let mut edge = cfg.get_edge((50306 >> 1) ^ 0x4ab3).unwrap();
assert_eq!(edge.calling_func, "_ZN7MyClass1VEi");
assert_eq!(edge.successor_edges.len(), 0);
assert_eq!(edge.successor_basic_blocks.len(), 0);

edge = cfg.get_edge((26911 >> 1) ^ 52706).unwrap();
edge = cfg.get_edge((26911 >> 1) ^ 0xcde2).unwrap();
assert_eq!(edge.calling_func, "main");
assert_eq!(edge.successor_edges.len(), 0);
assert_eq!(edge.successor_basic_blocks.len(), 0);

edge = cfg.get_edge((41864 >> 1) ^ 26911).unwrap();
edge = cfg.get_edge((41864 >> 1) ^ 0x691f).unwrap();
assert_eq!(edge.calling_func, "main");
assert_eq!(edge.successor_edges.len(), 2);
assert_eq!(*edge.successor_edges.first().unwrap(), (26911 >> 1) ^ 52706);
assert_eq!(
*edge.successor_edges.first().unwrap(),
(26911 >> 1) ^ 0xcde2
);

assert!(cfg.get_edge(26911).is_none());
assert!(cfg.get_edge(41864).is_some());
Expand All @@ -394,10 +397,10 @@ mod tests {
#[cfg_attr(miri, ignore)] // Testcase takes too long in miri. :/
fn test_shortest_path() {
let cfg: ControlFlowGraph<TestMetadata> = ControlFlowGraph::from_content(TEST_GRAPH_STR);
let distances = cfg.calculate_distances_to_all_edges((41864 >> 1) ^ 26911);
assert_eq!(*distances.get(&((41864 >> 1) ^ 26911)).unwrap(), 1);
assert_eq!(*distances.get(&((26911 >> 1) ^ 52706)).unwrap(), 2);
assert_eq!(*distances.get(&((26911 >> 1) ^ 41925)).unwrap(), 2);
assert!(!distances.contains_key(&((41864 >> 1) ^ 52706)));
let distances = cfg.calculate_distances_to_all_edges((41864 >> 1) ^ 0x691f);
assert_eq!(*distances.get(&((41864 >> 1) ^ 0x691f)).unwrap(), 1);
assert_eq!(*distances.get(&((26911 >> 1) ^ 0xcde2)).unwrap(), 2);
assert_eq!(*distances.get(&((26911 >> 1) ^ 0xa3c5)).unwrap(), 2);
assert!(!distances.contains_key(&((41864 >> 1) ^ 0xcde2)));
}
}
42 changes: 21 additions & 21 deletions crates/libafl_frida/src/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -919,31 +919,31 @@ fn check_shadow() {
assert!(allocator.check_shadow(allocation, 8));
assert!(!allocator.check_shadow(allocation, 9));
assert!(!allocator.check_shadow(allocation, 10));
assert!(allocator.check_shadow(unsafe { allocation.offset(1) }, 7));
assert!(allocator.check_shadow(unsafe { allocation.offset(2) }, 6));
assert!(allocator.check_shadow(unsafe { allocation.offset(3) }, 5));
assert!(allocator.check_shadow(unsafe { allocation.offset(4) }, 4));
assert!(allocator.check_shadow(unsafe { allocation.offset(5) }, 3));
assert!(allocator.check_shadow(unsafe { allocation.offset(6) }, 2));
assert!(allocator.check_shadow(unsafe { allocation.offset(7) }, 1));
assert!(allocator.check_shadow(unsafe { allocation.offset(8) }, 0));
assert!(!allocator.check_shadow(unsafe { allocation.offset(9) }, 1));
assert!(!allocator.check_shadow(unsafe { allocation.offset(9) }, 8));
assert!(!allocator.check_shadow(unsafe { allocation.offset(1) }, 9));
assert!(!allocator.check_shadow(unsafe { allocation.offset(1) }, 8));
assert!(!allocator.check_shadow(unsafe { allocation.offset(2) }, 8));
assert!(!allocator.check_shadow(unsafe { allocation.offset(3) }, 8));
assert!(allocator.check_shadow(unsafe { allocation.add(1) }, 7));
assert!(allocator.check_shadow(unsafe { allocation.add(2) }, 6));
assert!(allocator.check_shadow(unsafe { allocation.add(3) }, 5));
assert!(allocator.check_shadow(unsafe { allocation.add(4) }, 4));
assert!(allocator.check_shadow(unsafe { allocation.add(5) }, 3));
assert!(allocator.check_shadow(unsafe { allocation.add(6) }, 2));
assert!(allocator.check_shadow(unsafe { allocation.add(7) }, 1));
assert!(allocator.check_shadow(unsafe { allocation.add(8) }, 0));
assert!(!allocator.check_shadow(unsafe { allocation.add(9) }, 1));
assert!(!allocator.check_shadow(unsafe { allocation.add(9) }, 8));
assert!(!allocator.check_shadow(unsafe { allocation.add(1) }, 9));
assert!(!allocator.check_shadow(unsafe { allocation.add(1) }, 8));
assert!(!allocator.check_shadow(unsafe { allocation.add(2) }, 8));
assert!(!allocator.check_shadow(unsafe { allocation.add(3) }, 8));
let allocation = unsafe { allocator.alloc(0xc, 0) };
assert!(allocator.check_shadow(unsafe { allocation.offset(4) }, 8));
assert!(allocator.check_shadow(unsafe { allocation.add(4) }, 8));
//subqword access
assert!(allocator.check_shadow(unsafe { allocation.offset(3) }, 2));
assert!(allocator.check_shadow(unsafe { allocation.add(3) }, 2));
//unaligned access
assert!(allocator.check_shadow(unsafe { allocation.offset(3) }, 8));
assert!(allocator.check_shadow(unsafe { allocation.add(3) }, 8));
let allocation = unsafe { allocator.alloc(0x20, 0) };
//access with unaligned parts at the beginning and end
assert!(allocator.check_shadow(unsafe { allocation.offset(10) }, 21));
assert!(allocator.check_shadow(unsafe { allocation.add(10) }, 21));
//invalid, unaligned access
assert!(!allocator.check_shadow(unsafe { allocation.offset(10) }, 29));
assert!(!allocator.check_shadow(unsafe { allocation.add(10) }, 29));
let allocation = unsafe { allocator.alloc(4, 0) };
assert!(!allocation.is_null());
assert!(allocator.check_shadow(allocation, 1));
Expand All @@ -955,7 +955,7 @@ fn check_shadow() {
assert!(!allocator.check_shadow(allocation, 7));
assert!(!allocator.check_shadow(allocation, 8));
let allocation = unsafe { allocator.alloc(0xc, 0) };
assert!(allocator.check_shadow(unsafe { allocation.offset(4) }, 8));
assert!(allocator.check_shadow(unsafe { allocation.add(4) }, 8));
let allocation = unsafe { allocator.alloc(0x3c, 0) };
assert!(allocator.check_shadow(unsafe { allocation.offset(0x3a) }, 2));
assert!(allocator.check_shadow(unsafe { allocation.add(0x3a) }, 2));
}
10 changes: 0 additions & 10 deletions crates/libafl_frida/src/asan/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,6 @@ impl AsanErrors {
);
}));

#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " Memory error detected! ").unwrap();
output
.set_color(ColorSpec::new().set_fg(Some(Color::Red)))
Expand Down Expand Up @@ -204,7 +203,6 @@ impl AsanErrors {
}
output.reset().unwrap();

#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " REGISTERS ").unwrap();
#[cfg(target_arch = "aarch64")]
for reg in 0..=30 {
Expand Down Expand Up @@ -253,7 +251,6 @@ impl AsanErrors {
#[cfg(target_arch = "x86")]
writeln!(output, "eip: 0x{:08x}", error.pc).unwrap();

#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " CODE ").unwrap();

#[cfg(target_arch = "aarch64")]
Expand Down Expand Up @@ -303,7 +300,6 @@ impl AsanErrors {
.print_trace(&error.backtrace, output)
.unwrap();

#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " ALLOCATION INFO ").unwrap();
let fault_address: i64 = fault_address.try_into().unwrap();
let metadata_address: i64 = error.metadata.address.try_into().unwrap();
Expand Down Expand Up @@ -336,7 +332,6 @@ impl AsanErrors {
}

if error.metadata.freed {
#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " FREE INFO ").unwrap();
if let Some(backtrace) = &mut release_site_backtrace {
writeln!(output, "free site backtrace:").unwrap();
Expand Down Expand Up @@ -400,7 +395,6 @@ impl AsanErrors {
output.reset().unwrap();
backtrace_printer.print_trace(backtrace, output).unwrap();

#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " ALLOCATION INFO ").unwrap();
writeln!(
output,
Expand All @@ -421,7 +415,6 @@ impl AsanErrors {
backtrace.resolve();
backtrace_printer.print_trace(backtrace, output).unwrap();
}
#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " FREE INFO ").unwrap();
if let Some(backtrace) = &mut release_site_backtrace {
writeln!(output, "previous free site backtrace:").unwrap();
Expand All @@ -438,7 +431,6 @@ impl AsanErrors {
writeln!(output, " of {ptr:#016x}").unwrap();
output.reset().unwrap();

#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " ALLOCATION INFO ").unwrap();
writeln!(
output,
Expand Down Expand Up @@ -481,7 +473,6 @@ impl AsanErrors {
}
output.reset().unwrap();

#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " REGISTERS ").unwrap();

#[cfg(target_arch = "aarch64")]
Expand Down Expand Up @@ -540,7 +531,6 @@ impl AsanErrors {
#[cfg(target_arch = "x86")]
writeln!(output, "Eip: 0x{pc:08x}").unwrap();

#[expect(clippy::non_ascii_literal)]
writeln!(output, "{:━^100}", " CODE ").unwrap();

#[cfg(target_arch = "aarch64")]
Expand Down
3 changes: 2 additions & 1 deletion crates/libafl_frida/src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use core::{
ffi::c_void,
fmt::{self, Debug, Formatter},
marker::PhantomData,
ptr,
};
#[cfg(all(windows, not(test)))]
use std::process::abort;
Expand Down Expand Up @@ -96,7 +97,7 @@ where
// but we need to pass the harness entry point
// so that Stalker knows to pick it despite the module being excluded
let harness_fn_ref: &H = self.base.harness();
let ptr: *const H = harness_fn_ref as *const H;
let ptr: *const H = ptr::from_ref::<H>(harness_fn_ref);
log::info!("Activating Stalker for {ptr:p}");
self.stalker.activate(NativePointer(ptr as *mut c_void));
}
Expand Down
5 changes: 2 additions & 3 deletions crates/libafl_qemu/libafl_qemu_build/src/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ const WRAPPER_HEADER: &str = r#"
#include "migration/vmstate.h"
#include "migration/savevm.h"
#include "hw/core/sysemu-cpu-ops.h"
#include "exec/address-spaces.h"
#include "exec/target_page.h"
#include "system/system.h"

Expand All @@ -65,7 +64,6 @@ const WRAPPER_HEADER: &str = r#"

#include "exec/cpu-common.h"
#include "exec/cpu-all.h"
#include "exec/exec-all.h"
#include "exec/log.h"
#include "trace/trace-root.h"
#include "qemu/accel.h"
Expand Down Expand Up @@ -155,7 +153,8 @@ pub fn generate(
.allowlist_function("target_munmap")
.allowlist_function("page_check_range")
.allowlist_function("cpu_memory_rw_debug")
.allowlist_function("cpu_physical_memory_rw")
.allowlist_function("cpu_physical_memory_read")
.allowlist_function("cpu_physical_memory_write")
.allowlist_function("cpu_reset")
.allowlist_function("cpu_synchronize_state")
.allowlist_function("cpu_get_phys_page_attrs_debug")
Expand Down
2 changes: 1 addition & 1 deletion crates/libafl_qemu/libafl_qemu_build/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::cargo_add_rpath;

pub const LIBAFL_QEMU_GIT_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
pub const LIBAFL_QEMU_DIRNAME: &str = "qemu-libafl-bridge";
pub const LIBAFL_QEMU_GIT_REV: &str = "a2180efeb068534ce2d4424b4bd97295b3d1c54a";
pub const LIBAFL_QEMU_GIT_REV: &str = "c9c6db9127509e1eeaf10daad8fa6bc12cc54f56";

pub struct BuildResult {
pub qemu_path: PathBuf,
Expand Down
Loading
Loading