diff --git a/fuzzers/binary_only/qemu_launcher/src/client.rs b/fuzzers/binary_only/qemu_launcher/src/client.rs index 9a7f762cce..b3ab792a9b 100644 --- a/fuzzers/binary_only/qemu_launcher/src/client.rs +++ b/fuzzers/binary_only/qemu_launcher/src/client.rs @@ -10,8 +10,8 @@ use libafl::{ }; use libafl_bolts::{rands::StdRand, tuples::tuple_list}; use libafl_qemu::modules::{ - asan::AsanModule, asan_guest::AsanGuestModule, cmplog::CmpLogModule, DrCovModule, - InjectionModule, + asan::AsanModule, asan_guest::AsanGuestModule, cmplog::CmpLogModule, + utils::filters::StdAddressFilter, DrCovModule, InjectionModule, }; use crate::{ @@ -111,6 +111,17 @@ impl Client<'_> { .client_description(client_description) .extra_tokens(extra_tokens); + let asan_filter = if let Some(include_asan) = &self.options.include_asan { + log::info!("ASAN includes: {include_asan:#x?}"); + StdAddressFilter::allow_list(include_asan.to_vec()) + } else if let Some(exclude_asan) = &self.options.exclude_asan { + log::info!("ASAN excludes: {exclude_asan:#x?}"); + StdAddressFilter::deny_list(exclude_asan.to_vec()) + } else { + log::info!("ASAN no additional filter"); + StdAddressFilter::default() + }; + if self.options.rerun_input.is_some() { if is_drcov { // Special code path for re-running inputs with DrCov and Asan. @@ -123,7 +134,13 @@ impl Client<'_> { .filename(drcov.clone()) .full_trace(true) .build(), - unsafe { AsanModule::builder().env(&env).asan_report().build() } + unsafe { + AsanModule::builder() + .env(&env) + .filter(asan_filter) + .asan_report() + .build() + } ); instance_builder.build().run(args, modules, state) @@ -133,7 +150,7 @@ impl Client<'_> { .filename(drcov.clone()) .full_trace(true) .build(), - AsanGuestModule::default(&env), + AsanGuestModule::new(&env, asan_filter), ); instance_builder.build().run(args, modules, state) @@ -146,12 +163,17 @@ impl Client<'_> { instance_builder.build().run(args, modules, state) } } else if is_asan { - let modules = - tuple_list!(unsafe { AsanModule::builder().env(&env).asan_report().build() }); + let modules = tuple_list!(unsafe { + AsanModule::builder() + .env(&env) + .filter(asan_filter) + .asan_report() + .build() + }); instance_builder.build().run(args, modules, state) } else if is_asan_guest { - let modules = tuple_list!(AsanGuestModule::default(&env)); + let modules = tuple_list!(AsanGuestModule::new(&env, asan_filter)); instance_builder.build().run(args, modules, state) } else { @@ -165,7 +187,7 @@ impl Client<'_> { args, tuple_list!( CmpLogModule::default(), - AsanModule::builder().env(&env).build(), + AsanModule::builder().env(&env).filter(asan_filter).build(), injection_module, ), state, @@ -175,7 +197,7 @@ impl Client<'_> { args, tuple_list!( CmpLogModule::default(), - AsanModule::builder().env(&env).build() + AsanModule::builder().env(&env).filter(asan_filter).build() ), state, ) @@ -186,7 +208,7 @@ impl Client<'_> { args, tuple_list!( CmpLogModule::default(), - AsanGuestModule::default(&env), + AsanGuestModule::new(&env, asan_filter), injection_module ), state, @@ -194,7 +216,10 @@ impl Client<'_> { } else { instance_builder.build().run( args, - tuple_list!(CmpLogModule::default(), AsanGuestModule::default(&env),), + tuple_list!( + CmpLogModule::default(), + AsanGuestModule::new(&env, asan_filter), + ), state, ) } @@ -202,20 +227,25 @@ impl Client<'_> { if let Some(injection_module) = injection_module { instance_builder.build().run( args, - tuple_list!(AsanModule::builder().env(&env).build(), injection_module), + tuple_list!( + AsanModule::builder().env(&env).filter(asan_filter).build(), + injection_module + ), state, ) } else { instance_builder.build().run( args, - tuple_list!(AsanModule::builder().env(&env).build()), + tuple_list!(AsanModule::builder().env(&env).filter(asan_filter).build()), state, ) } } else if is_asan_guest { - instance_builder - .build() - .run(args, tuple_list!(AsanGuestModule::default(&env)), state) + instance_builder.build().run( + args, + tuple_list!(AsanGuestModule::new(&env, asan_filter)), + state, + ) } else if is_cmplog { if let Some(injection_module) = injection_module { instance_builder.build().run( diff --git a/fuzzers/binary_only/qemu_launcher/src/options.rs b/fuzzers/binary_only/qemu_launcher/src/options.rs index 1590d10fb8..8ee597d99d 100644 --- a/fuzzers/binary_only/qemu_launcher/src/options.rs +++ b/fuzzers/binary_only/qemu_launcher/src/options.rs @@ -68,12 +68,18 @@ pub struct FuzzerOptions { #[arg(long = "iterations", help = "Maximum number of iterations")] pub iterations: Option, - #[arg(long = "include", help="Include address ranges", value_parser = FuzzerOptions::parse_ranges)] + #[arg(long = "include", help="Include coverage address ranges", value_parser = FuzzerOptions::parse_ranges)] pub include: Option>>, - #[arg(long = "exclude", help="Exclude address ranges", value_parser = FuzzerOptions::parse_ranges, conflicts_with="include")] + #[arg(long = "exclude", help="Exclude coverage address ranges", value_parser = FuzzerOptions::parse_ranges, conflicts_with="include")] pub exclude: Option>>, + #[arg(long = "include-asan", help="Include asan address ranges", value_parser = FuzzerOptions::parse_ranges)] + pub include_asan: Option>>, + + #[arg(long = "exclude-asan", help="Exclude asan address ranges", value_parser = FuzzerOptions::parse_ranges, conflicts_with="include_asan")] + pub exclude_asan: Option>>, + #[arg( short = 'd', help = "Write a DrCov Trace for the current input. Requires -r." diff --git a/libafl_qemu/librasan/Justfile b/libafl_qemu/librasan/Justfile index d38c12cdba..9ddb96f840 100644 --- a/libafl_qemu/librasan/Justfile +++ b/libafl_qemu/librasan/Justfile @@ -14,7 +14,7 @@ test: test_asan pretty_rust: #!/bin/sh - MAIN_LLVM_VERSION=$LLVM_VERSION cargo run --manifest-path ../../utils/libafl_fmt/Cargo.toml --release -- -v + MAIN_LLVM_VERSION=$LLVM_VERSION cargo run --manifest-path ../../utils/libafl_repo_tools/Cargo.toml --release -- -v pretty_toml: #!/bin/sh diff --git a/libafl_qemu/librasan/runner/src/fuzz.rs b/libafl_qemu/librasan/runner/src/fuzz.rs index a65a03d848..a62b94990a 100644 --- a/libafl_qemu/librasan/runner/src/fuzz.rs +++ b/libafl_qemu/librasan/runner/src/fuzz.rs @@ -1,12 +1,12 @@ -use std::{env, fmt::Write}; +use std::{env, fmt::Write, ops::Range}; use clap::{Parser, builder::Str}; use libafl_bolts::{Error, tuples::tuple_list}; use libafl_qemu::{ - Emulator, NopEmulatorDriver, NopSnapshotManager, QemuExitError, QemuInitError, + Emulator, GuestAddr, NopEmulatorDriver, NopSnapshotManager, QemuExitError, QemuInitError, command::NopCommandManager, elf::EasyElf, - modules::{AsanGuestModule, AsanModule, EmulatorModuleTuple}, + modules::{AsanGuestModule, AsanModule, EmulatorModuleTuple, utils::filters::StdAddressFilter}, }; use log::{error, info}; use thiserror::Error; @@ -60,10 +60,37 @@ pub struct FuzzerOptions { #[clap(short, long, help = "Enable output from the fuzzer clients")] pub verbose: bool, + #[arg(long = "include-asan", help="Include asan address ranges", value_parser = FuzzerOptions::parse_ranges)] + pub include_asan: Option>>, + + #[arg(long = "exclude-asan", help="Exclude asan address ranges", value_parser = FuzzerOptions::parse_ranges, conflicts_with="include_asan")] + pub exclude_asan: Option>>, + #[arg(last = true, help = "Arguments passed to the target")] pub args: Vec, } +impl FuzzerOptions { + fn parse_ranges(src: &str) -> Result, Error> { + let parts = src.split('-').collect::>(); + if parts.len() == 2 { + let start = + GuestAddr::from_str_radix(parts[0].trim_start_matches("0x"), 16).map_err(|e| { + Error::illegal_argument(format!("Invalid start address: {} ({e:})", parts[0])) + })?; + let end = + GuestAddr::from_str_radix(parts[1].trim_start_matches("0x"), 16).map_err(|e| { + Error::illegal_argument(format!("Invalid end address: {} ({e:})", parts[1])) + })?; + Ok(Range { start, end }) + } else { + Err(Error::illegal_argument(format!( + "Invalid range provided: {src:}" + ))) + } + } +} + pub fn fuzz() { env_logger::init(); let mut options = FuzzerOptions::parse(); @@ -78,14 +105,25 @@ pub fn fuzz() { .filter(|(k, _v)| k != "LD_LIBRARY_PATH") .collect::>(); + let asan_filter = if let Some(include_asan) = &options.include_asan { + info!("ASAN includes: {include_asan:#x?}"); + StdAddressFilter::allow_list(include_asan.to_vec()) + } else if let Some(exclude_asan) = &options.exclude_asan { + info!("ASAN excludes: {exclude_asan:#x?}"); + StdAddressFilter::deny_list(exclude_asan.to_vec()) + } else { + info!("ASAN no additional filter"); + StdAddressFilter::default() + }; + let ret = if options.asan { info!("Enabling ASAN"); - let modules = tuple_list!(AsanModule::builder().env(&env).build()); + let modules = tuple_list!(AsanModule::builder().env(&env).filter(asan_filter).build()); info!("Modules: {:#?}", modules); run(options, modules) } else if options.gasan { info!("Enabling Guest ASAN"); - let modules = tuple_list!(AsanGuestModule::default(&env)); + let modules = tuple_list!(AsanGuestModule::new(&env, asan_filter)); info!("Modules: {:#?}", modules); run(options, modules) } else {