Add builder and tests for QASAN (#2898)
* Add tests for QASAN from aflplusplus * refactor asan module to use the builder pattern * move injection tests to the new tests directory
This commit is contained in:
parent
37fc43f53c
commit
75feedd1a0
@ -1,3 +1,14 @@
|
|||||||
|
env_scripts = ['''
|
||||||
|
#!@duckscript
|
||||||
|
profile = get_env PROFILE
|
||||||
|
|
||||||
|
if eq ${profile} "dev"
|
||||||
|
set_env PROFILE_DIR debug
|
||||||
|
else
|
||||||
|
set_env PROFILE_DIR ${profile}
|
||||||
|
end
|
||||||
|
''']
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
PROFILE = { value = "release", condition = { env_not_set = ["PROFILE"] } }
|
PROFILE = { value = "release", condition = { env_not_set = ["PROFILE"] } }
|
||||||
PROFILE_DIR = { source = "${PROFILE}", default_value = "release", mapping = { "release" = "release", "dev" = "debug" }, condition = { env_not_set = [
|
PROFILE_DIR = { source = "${PROFILE}", default_value = "release", mapping = { "release" = "release", "dev" = "debug" }, condition = { env_not_set = [
|
||||||
@ -360,21 +371,11 @@ windows_alias = "unsupported"
|
|||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script = '''
|
script = '''
|
||||||
echo "Profile: ${PROFILE}"
|
echo "Profile: ${PROFILE}"
|
||||||
cd injection_test || exit 1
|
|
||||||
make
|
export QEMU_LAUNCHER=${TARGET_DIR}/${PROFILE_DIR}/qemu_launcher
|
||||||
mkdir in || true
|
|
||||||
echo aaaaaaaaaa > in/a
|
./tests/injection/test.sh || exit 1
|
||||||
timeout 10s "$(find ${TARGET_DIR} -name 'qemu_launcher')" -o out -i in -j ../injections.toml -v -- ./static >/dev/null 2>fuzz.log || true
|
./tests/qasan/test.sh || exit 1
|
||||||
if [ -z "$(grep -Ei "found.*injection" fuzz.log)" ]; then
|
|
||||||
echo "Fuzzer does not generate any testcases or any crashes"
|
|
||||||
echo "Logs:"
|
|
||||||
cat fuzz.log
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "Fuzzer is working"
|
|
||||||
fi
|
|
||||||
make clean
|
|
||||||
#rm -rf in out fuzz.log || true
|
|
||||||
'''
|
'''
|
||||||
dependencies = ["build_unix"]
|
dependencies = ["build_unix"]
|
||||||
|
|
||||||
|
@ -94,6 +94,8 @@ impl Client<'_> {
|
|||||||
|
|
||||||
let is_cmplog = self.options.is_cmplog_core(core_id);
|
let is_cmplog = self.options.is_cmplog_core(core_id);
|
||||||
|
|
||||||
|
let is_drcov = self.options.drcov.is_some();
|
||||||
|
|
||||||
let extra_tokens = if cfg!(feature = "injections") {
|
let extra_tokens = if cfg!(feature = "injections") {
|
||||||
injection_module
|
injection_module
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -109,24 +111,47 @@ impl Client<'_> {
|
|||||||
.client_description(client_description)
|
.client_description(client_description)
|
||||||
.extra_tokens(extra_tokens);
|
.extra_tokens(extra_tokens);
|
||||||
|
|
||||||
if self.options.rerun_input.is_some() && self.options.drcov.is_some() {
|
if self.options.rerun_input.is_some() {
|
||||||
// Special code path for re-running inputs with DrCov.
|
if is_drcov {
|
||||||
// TODO: Add ASan support, injection support
|
// Special code path for re-running inputs with DrCov and Asan.
|
||||||
let drcov = self.options.drcov.as_ref().unwrap();
|
// TODO: Add injection support
|
||||||
let drcov = DrCovModule::builder()
|
let drcov = self.options.drcov.as_ref().unwrap();
|
||||||
.filename(drcov.clone())
|
|
||||||
.full_trace(true)
|
if is_asan {
|
||||||
.build();
|
let modules = tuple_list!(
|
||||||
instance_builder
|
DrCovModule::builder()
|
||||||
.build()
|
.filename(drcov.clone())
|
||||||
.run(args, tuple_list!(drcov), state)
|
.full_trace(true)
|
||||||
|
.build(),
|
||||||
|
unsafe { AsanModule::builder().env(&env).asan_report().build() }
|
||||||
|
);
|
||||||
|
|
||||||
|
instance_builder.build().run(args, modules, state)
|
||||||
|
} else {
|
||||||
|
let modules = tuple_list!(DrCovModule::builder()
|
||||||
|
.filename(drcov.clone())
|
||||||
|
.full_trace(true)
|
||||||
|
.build(),);
|
||||||
|
|
||||||
|
instance_builder.build().run(args, modules, state)
|
||||||
|
}
|
||||||
|
} else if is_asan {
|
||||||
|
let modules =
|
||||||
|
tuple_list!(unsafe { AsanModule::builder().env(&env).asan_report().build() });
|
||||||
|
|
||||||
|
instance_builder.build().run(args, modules, state)
|
||||||
|
} else {
|
||||||
|
let modules = tuple_list!();
|
||||||
|
|
||||||
|
instance_builder.build().run(args, modules, state)
|
||||||
|
}
|
||||||
} else if is_asan && is_cmplog {
|
} else if is_asan && is_cmplog {
|
||||||
if let Some(injection_module) = injection_module {
|
if let Some(injection_module) = injection_module {
|
||||||
instance_builder.build().run(
|
instance_builder.build().run(
|
||||||
args,
|
args,
|
||||||
tuple_list!(
|
tuple_list!(
|
||||||
CmpLogModule::default(),
|
CmpLogModule::default(),
|
||||||
AsanModule::default(&env),
|
AsanModule::builder().env(&env).build(),
|
||||||
injection_module,
|
injection_module,
|
||||||
),
|
),
|
||||||
state,
|
state,
|
||||||
@ -134,7 +159,10 @@ impl Client<'_> {
|
|||||||
} else {
|
} else {
|
||||||
instance_builder.build().run(
|
instance_builder.build().run(
|
||||||
args,
|
args,
|
||||||
tuple_list!(CmpLogModule::default(), AsanModule::default(&env),),
|
tuple_list!(
|
||||||
|
CmpLogModule::default(),
|
||||||
|
AsanModule::builder().env(&env).build()
|
||||||
|
),
|
||||||
state,
|
state,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -160,13 +188,15 @@ impl Client<'_> {
|
|||||||
if let Some(injection_module) = injection_module {
|
if let Some(injection_module) = injection_module {
|
||||||
instance_builder.build().run(
|
instance_builder.build().run(
|
||||||
args,
|
args,
|
||||||
tuple_list!(AsanModule::default(&env), injection_module),
|
tuple_list!(AsanModule::builder().env(&env).build(), injection_module),
|
||||||
state,
|
state,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
instance_builder
|
instance_builder.build().run(
|
||||||
.build()
|
args,
|
||||||
.run(args, tuple_list!(AsanModule::default(&env),), state)
|
tuple_list!(AsanModule::builder().env(&env).build()),
|
||||||
|
state,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else if is_asan_guest {
|
} else if is_asan_guest {
|
||||||
instance_builder
|
instance_builder
|
||||||
|
30
fuzzers/binary_only/qemu_launcher/tests/injection/test.sh
Executable file
30
fuzzers/binary_only/qemu_launcher/tests/injection/test.sh
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
|
if [[ ! -x "$QEMU_LAUNCHER" ]]; then
|
||||||
|
echo "env variable QEMU_LAUNCHER does not point to a valid executable"
|
||||||
|
echo "QEMU_LAUNCHER should point to qemu_launcher location, but points to ${QEMU_LAUNCHER} instead."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
make
|
||||||
|
|
||||||
|
mkdir in || true
|
||||||
|
|
||||||
|
echo aaaaaaaaaa > in/a
|
||||||
|
|
||||||
|
timeout 10s "$QEMU_LAUNCHER" -o out -i in -j ../../injections.toml -v -- ./static >/dev/null 2>fuzz.log || true
|
||||||
|
if ! grep -Ei "found.*injection" fuzz.log; then
|
||||||
|
echo "Fuzzer does not generate any testcases or any crashes"
|
||||||
|
echo "Logs:"
|
||||||
|
cat fuzz.log
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "Fuzzer is working"
|
||||||
|
fi
|
||||||
|
|
||||||
|
make clean
|
7
fuzzers/binary_only/qemu_launcher/tests/qasan/Makefile
Normal file
7
fuzzers/binary_only/qemu_launcher/tests/qasan/Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
all: qasan
|
||||||
|
|
||||||
|
qasan: qasan.c
|
||||||
|
gcc qasan.c -o qasan
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf qasan out stats.txt
|
@ -0,0 +1 @@
|
|||||||
|
D
|
@ -0,0 +1 @@
|
|||||||
|
M
|
@ -0,0 +1 @@
|
|||||||
|
O
|
@ -0,0 +1 @@
|
|||||||
|
T
|
@ -0,0 +1 @@
|
|||||||
|
A
|
@ -0,0 +1 @@
|
|||||||
|
U
|
85
fuzzers/binary_only/qemu_launcher/tests/qasan/qasan.c
Normal file
85
fuzzers/binary_only/qemu_launcher/tests/qasan/qasan.c
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// taken from
|
||||||
|
// https://github.com/AFLplusplus/AFLplusplus/blob/da2d4d8258d725f79c2daa22bf3b1a59c593e472/frida_mode/test/fasan/test.c
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define UNUSED_PARAMETER(x) (void)(x)
|
||||||
|
|
||||||
|
#define LOG(x) \
|
||||||
|
do { \
|
||||||
|
char buf[] = x; \
|
||||||
|
write(STDOUT_FILENO, buf, sizeof(buf)); \
|
||||||
|
\
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
void LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
|
char *buf = malloc(10);
|
||||||
|
|
||||||
|
if (buf == NULL) return;
|
||||||
|
|
||||||
|
switch (*data) {
|
||||||
|
/* Underflow */
|
||||||
|
case 'U':
|
||||||
|
LOG("Underflow\n");
|
||||||
|
buf[-1] = '\0';
|
||||||
|
free(buf);
|
||||||
|
break;
|
||||||
|
/* Overflow */
|
||||||
|
case 'O':
|
||||||
|
LOG("Overflow\n");
|
||||||
|
buf[10] = '\0';
|
||||||
|
free(buf);
|
||||||
|
break;
|
||||||
|
/* Double free */
|
||||||
|
case 'D':
|
||||||
|
LOG("Double free\n");
|
||||||
|
free(buf);
|
||||||
|
free(buf);
|
||||||
|
break;
|
||||||
|
/* Use after free */
|
||||||
|
case 'A':
|
||||||
|
LOG("Use after free\n");
|
||||||
|
free(buf);
|
||||||
|
buf[0] = '\0';
|
||||||
|
break;
|
||||||
|
/* Test Limits (OK) */
|
||||||
|
case 'T':
|
||||||
|
LOG("Test-Limits - No Error\n");
|
||||||
|
buf[0] = 'A';
|
||||||
|
buf[9] = 'I';
|
||||||
|
free(buf);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
LOG("Memset too many\n");
|
||||||
|
memset(buf, '\0', 11);
|
||||||
|
free(buf);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG("Nop - No Error\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
UNUSED_PARAMETER(argc);
|
||||||
|
UNUSED_PARAMETER(argv);
|
||||||
|
|
||||||
|
char input = '\0';
|
||||||
|
|
||||||
|
// if (read(STDIN_FILENO, &input, 1) < 0) {
|
||||||
|
|
||||||
|
// LOG("Failed to read stdin\n");
|
||||||
|
// return 1;
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
LLVMFuzzerTestOneInput(&input, 1);
|
||||||
|
|
||||||
|
LOG("DONE\n");
|
||||||
|
return 0;
|
||||||
|
}
|
69
fuzzers/binary_only/qemu_launcher/tests/qasan/test.sh
Executable file
69
fuzzers/binary_only/qemu_launcher/tests/qasan/test.sh
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
||||||
|
|
||||||
|
if [[ ! -x "$QEMU_LAUNCHER" ]]; then
|
||||||
|
echo "env variable QEMU_LAUNCHER does not point to a valid executable"
|
||||||
|
echo "QEMU_LAUNCHER should point to qemu_launcher"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
make
|
||||||
|
|
||||||
|
tests=(
|
||||||
|
"overflow"
|
||||||
|
"underflow"
|
||||||
|
"double_free"
|
||||||
|
"memset"
|
||||||
|
"uaf"
|
||||||
|
"test_limits"
|
||||||
|
)
|
||||||
|
|
||||||
|
tests_expected=(
|
||||||
|
"is 0 bytes to the right of the 10-byte chunk"
|
||||||
|
"is 1 bytes to the left of the 10-byte chunk"
|
||||||
|
"is 0 bytes inside the 10-byte chunk"
|
||||||
|
"is 0 bytes to the right of the 10-byte chunk"
|
||||||
|
"is 0 bytes inside the 10-byte chunk"
|
||||||
|
"Test-Limits - No Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
tests_not_expected=(
|
||||||
|
"dummy"
|
||||||
|
"dummy"
|
||||||
|
"dummy"
|
||||||
|
"dummy"
|
||||||
|
"dummy"
|
||||||
|
"Context:"
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in "${!tests[@]}"
|
||||||
|
do
|
||||||
|
test="${tests[i]}"
|
||||||
|
expected="${tests_expected[i]}"
|
||||||
|
not_expected="${tests_not_expected[i]}"
|
||||||
|
|
||||||
|
echo "Running $test detection test..."
|
||||||
|
OUT=$("$QEMU_LAUNCHER" \
|
||||||
|
-r "inputs/$test.txt" \
|
||||||
|
--input dummy \
|
||||||
|
--output out \
|
||||||
|
--asan-cores 0 \
|
||||||
|
-- qasan 2>&1 | tr -d '\0')
|
||||||
|
|
||||||
|
if ! echo "$OUT" | grep -q "$expected"; then
|
||||||
|
echo "ERROR: Expected: $expected."
|
||||||
|
echo "Output is:"
|
||||||
|
echo "$OUT"
|
||||||
|
exit 1
|
||||||
|
elif echo "$OUT" | grep -q "$not_expected"; then
|
||||||
|
echo "ERROR: Did not expect: $not_expected."
|
||||||
|
echo "Output is:"
|
||||||
|
echo "$OUT"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "OK."
|
||||||
|
fi
|
||||||
|
done
|
@ -572,16 +572,17 @@ where
|
|||||||
static mut CALLSTACKS: Option<ThreadLocal<UnsafeCell<Vec<GuestAddr>>>> = None;
|
static mut CALLSTACKS: Option<ThreadLocal<UnsafeCell<Vec<GuestAddr>>>> = None;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FullBacktraceCollector {}
|
pub struct FullBacktraceCollector;
|
||||||
|
|
||||||
impl FullBacktraceCollector {
|
impl FullBacktraceCollector {
|
||||||
/// # Safety
|
/// # Safety
|
||||||
|
///
|
||||||
/// This accesses the global [`CALLSTACKS`] variable and may not be called concurrently.
|
/// This accesses the global [`CALLSTACKS`] variable and may not be called concurrently.
|
||||||
#[expect(rustdoc::private_intra_doc_links)]
|
#[expect(rustdoc::private_intra_doc_links)]
|
||||||
pub unsafe fn new() -> Self {
|
pub unsafe fn new() -> Self {
|
||||||
let callstacks_ptr = &raw mut CALLSTACKS;
|
let callstacks_ptr = &raw mut CALLSTACKS;
|
||||||
unsafe { (*callstacks_ptr) = Some(ThreadLocal::new()) };
|
unsafe { (*callstacks_ptr) = Some(ThreadLocal::new()) };
|
||||||
Self {}
|
Self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
|
@ -1,22 +1,38 @@
|
|||||||
#![allow(clippy::cast_possible_wrap)]
|
#![allow(clippy::cast_possible_wrap)]
|
||||||
#![allow(clippy::needless_pass_by_value)] // default compiler complains about Option<&mut T> otherwise, and this is used extensively.
|
#![allow(clippy::needless_pass_by_value)] // default compiler complains about Option<&mut T> otherwise, and this is used extensively.
|
||||||
use std::{borrow::Cow, env, fs, path::PathBuf, sync::Mutex};
|
|
||||||
|
use core::{fmt, slice};
|
||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
env,
|
||||||
|
fmt::{Debug, Display},
|
||||||
|
fs,
|
||||||
|
path::PathBuf,
|
||||||
|
pin::Pin,
|
||||||
|
process,
|
||||||
|
sync::Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use libafl::{executors::ExitKind, observers::ObserversTuple};
|
use libafl::{executors::ExitKind, observers::ObserversTuple};
|
||||||
|
use libafl_qemu_sys::GuestAddr;
|
||||||
use libc::{
|
use libc::{
|
||||||
c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE,
|
c_void, MAP_ANON, MAP_FAILED, MAP_FIXED, MAP_NORESERVE, MAP_PRIVATE, PROT_READ, PROT_WRITE,
|
||||||
};
|
};
|
||||||
use meminterval::{Interval, IntervalTree};
|
use meminterval::{Interval, IntervalTree};
|
||||||
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
use object::{Object, ObjectSection};
|
||||||
use rangemap::RangeMap;
|
use rangemap::RangeMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
emu::EmulatorModules,
|
||||||
modules::{
|
modules::{
|
||||||
calls::FullBacktraceCollector, snapshot::SnapshotModule, utils::filters::HasAddressFilter,
|
calls::FullBacktraceCollector,
|
||||||
EmulatorModule, EmulatorModuleTuple,
|
snapshot::SnapshotModule,
|
||||||
|
utils::filters::{HasAddressFilter, StdAddressFilter},
|
||||||
|
AddressFilter, EmulatorModule, EmulatorModuleTuple,
|
||||||
},
|
},
|
||||||
qemu::MemAccessInfo,
|
qemu::{Hook, MemAccessInfo, QemuHooks, SyscallHookResult},
|
||||||
sys::TCGTemp,
|
sys::TCGTemp,
|
||||||
Qemu, QemuParams, Regs,
|
Qemu, QemuParams, Regs,
|
||||||
};
|
};
|
||||||
@ -40,6 +56,25 @@ pub const SHADOW_PAGE_MASK: GuestAddr = !(SHADOW_PAGE_SIZE as GuestAddr - 1);
|
|||||||
|
|
||||||
pub const DEFAULT_REDZONE_SIZE: usize = 128;
|
pub const DEFAULT_REDZONE_SIZE: usize = 128;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AsanModule {
|
||||||
|
env: Vec<(String, String)>,
|
||||||
|
enabled: bool,
|
||||||
|
detect_leaks: bool,
|
||||||
|
empty: bool,
|
||||||
|
rt: Pin<Box<AsanGiovese>>,
|
||||||
|
filter: StdAddressFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AsanGiovese {
|
||||||
|
pub alloc_tree: Mutex<IntervalTree<GuestAddr, AllocTreeItem>>,
|
||||||
|
pub saved_tree: IntervalTree<GuestAddr, AllocTreeItem>,
|
||||||
|
pub error_callback: Option<AsanErrorCallback>,
|
||||||
|
pub dirty_shadow: Mutex<HashSet<GuestAddr>>,
|
||||||
|
pub saved_shadow: HashMap<GuestAddr, Vec<i8>>,
|
||||||
|
pub snapshot_shadow: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy)]
|
||||||
#[repr(u64)]
|
#[repr(u64)]
|
||||||
pub enum QasanAction {
|
pub enum QasanAction {
|
||||||
@ -56,14 +91,6 @@ pub enum QasanAction {
|
|||||||
SwapState,
|
SwapState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<u32> for QasanAction {
|
|
||||||
type Error = num_enum::TryFromPrimitiveError<QasanAction>;
|
|
||||||
|
|
||||||
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
|
||||||
QasanAction::try_from(u64::from(value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, PartialEq)]
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, PartialEq)]
|
||||||
#[repr(i8)]
|
#[repr(i8)]
|
||||||
pub enum PoisonKind {
|
pub enum PoisonKind {
|
||||||
@ -105,8 +132,65 @@ pub enum AsanError {
|
|||||||
Signal(i32),
|
Signal(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::fmt::Display for AsanError {
|
pub struct AsanModuleBuilder {
|
||||||
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
env: Option<Vec<(String, String)>>,
|
||||||
|
detect_leaks: bool,
|
||||||
|
snapshot: bool,
|
||||||
|
filter: StdAddressFilter,
|
||||||
|
error_callback: Option<AsanErrorCallback>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AllocTreeItem {
|
||||||
|
backtrace: Vec<GuestAddr>,
|
||||||
|
free_backtrace: Vec<GuestAddr>,
|
||||||
|
allocated: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
type AsanErrorFn = Box<dyn FnMut(&AsanGiovese, Qemu, GuestAddr, AsanError)>;
|
||||||
|
|
||||||
|
pub struct AsanErrorCallback(AsanErrorFn);
|
||||||
|
|
||||||
|
impl AsanErrorCallback {
|
||||||
|
/// Initialize a new [`AsanErrorCallback`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(error_callback: AsanErrorFn) -> Self {
|
||||||
|
Self(error_callback)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Special [`AsanErrorCallback`] providing a full report in case of QASAN trigger.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `ASan` error report accesses [`FullBacktraceCollector`]
|
||||||
|
#[must_use]
|
||||||
|
pub unsafe fn report() -> Self {
|
||||||
|
Self::new(Box::new(|rt, qemu, pc, err| {
|
||||||
|
asan_report(rt, qemu, pc, &err);
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call(
|
||||||
|
&mut self,
|
||||||
|
asan_giovese: &AsanGiovese,
|
||||||
|
qemu: Qemu,
|
||||||
|
pc: GuestAddr,
|
||||||
|
error: AsanError,
|
||||||
|
) {
|
||||||
|
self.0(asan_giovese, qemu, pc, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<u32> for QasanAction {
|
||||||
|
type Error = num_enum::TryFromPrimitiveError<QasanAction>;
|
||||||
|
|
||||||
|
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||||
|
QasanAction::try_from(u64::from(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for AsanError {
|
||||||
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
||||||
match self {
|
match self {
|
||||||
AsanError::Read(addr, len) => write!(fmt, "Invalid {len} bytes read at {addr:#x}"),
|
AsanError::Read(addr, len) => write!(fmt, "Invalid {len} bytes read at {addr:#x}"),
|
||||||
AsanError::Write(addr, len) => {
|
AsanError::Write(addr, len) => {
|
||||||
@ -122,15 +206,6 @@ impl core::fmt::Display for AsanError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type AsanErrorCallback = Box<dyn FnMut(&AsanGiovese, Qemu, GuestAddr, AsanError)>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct AllocTreeItem {
|
|
||||||
backtrace: Vec<GuestAddr>,
|
|
||||||
free_backtrace: Vec<GuestAddr>,
|
|
||||||
allocated: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AllocTreeItem {
|
impl AllocTreeItem {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn alloc(backtrace: Vec<GuestAddr>) -> Self {
|
pub fn alloc(backtrace: Vec<GuestAddr>) -> Self {
|
||||||
@ -146,27 +221,8 @@ impl AllocTreeItem {
|
|||||||
self.allocated = false;
|
self.allocated = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use libafl_qemu_sys::GuestAddr;
|
impl Debug for AsanGiovese {
|
||||||
use object::{Object, ObjectSection};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
emu::EmulatorModules,
|
|
||||||
modules::{utils::filters::StdAddressFilter, AddressFilter},
|
|
||||||
qemu::{Hook, QemuHooks, SyscallHookResult},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct AsanGiovese {
|
|
||||||
pub alloc_tree: Mutex<IntervalTree<GuestAddr, AllocTreeItem>>,
|
|
||||||
pub saved_tree: IntervalTree<GuestAddr, AllocTreeItem>,
|
|
||||||
pub error_callback: Option<AsanErrorCallback>,
|
|
||||||
pub dirty_shadow: Mutex<HashSet<GuestAddr>>,
|
|
||||||
pub saved_shadow: HashMap<GuestAddr, Vec<i8>>,
|
|
||||||
pub snapshot_shadow: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::fmt::Debug for AsanGiovese {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
f.debug_struct("AsanGiovese")
|
f.debug_struct("AsanGiovese")
|
||||||
.field("alloc_tree", &self.alloc_tree)
|
.field("alloc_tree", &self.alloc_tree)
|
||||||
@ -175,6 +231,211 @@ impl core::fmt::Debug for AsanGiovese {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsanModuleBuilder {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
env: Option<Vec<(String, String)>>,
|
||||||
|
detect_leaks: bool,
|
||||||
|
snapshot: bool,
|
||||||
|
filter: StdAddressFilter,
|
||||||
|
error_callback: Option<AsanErrorCallback>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
env,
|
||||||
|
detect_leaks,
|
||||||
|
snapshot,
|
||||||
|
filter,
|
||||||
|
error_callback,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn env(self, env: &[(String, String)]) -> Self {
|
||||||
|
Self::new(
|
||||||
|
Some(env.to_vec()),
|
||||||
|
self.detect_leaks,
|
||||||
|
self.snapshot,
|
||||||
|
self.filter,
|
||||||
|
self.error_callback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn detect_leaks(self, detect_leaks: bool) -> Self {
|
||||||
|
Self::new(
|
||||||
|
self.env,
|
||||||
|
detect_leaks,
|
||||||
|
self.snapshot,
|
||||||
|
self.filter,
|
||||||
|
self.error_callback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn snapshot(self, snapshot: bool) -> Self {
|
||||||
|
Self::new(
|
||||||
|
self.env,
|
||||||
|
self.detect_leaks,
|
||||||
|
snapshot,
|
||||||
|
self.filter,
|
||||||
|
self.error_callback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn filter(self, filter: StdAddressFilter) -> Self {
|
||||||
|
Self::new(
|
||||||
|
self.env,
|
||||||
|
self.detect_leaks,
|
||||||
|
self.snapshot,
|
||||||
|
filter,
|
||||||
|
self.error_callback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn error_callback(self, callback: AsanErrorCallback) -> Self {
|
||||||
|
Self::new(
|
||||||
|
self.env,
|
||||||
|
self.detect_leaks,
|
||||||
|
self.snapshot,
|
||||||
|
self.filter,
|
||||||
|
Some(callback),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an ASAN report in case of problem.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The `ASan` error report accesses [`FullBacktraceCollector`].
|
||||||
|
/// Check its safety note for more details.
|
||||||
|
#[must_use]
|
||||||
|
pub unsafe fn asan_report(self) -> Self {
|
||||||
|
Self::new(
|
||||||
|
self.env,
|
||||||
|
self.detect_leaks,
|
||||||
|
self.snapshot,
|
||||||
|
self.filter,
|
||||||
|
Some(AsanErrorCallback::report()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn build(self) -> AsanModule {
|
||||||
|
AsanModule::new(
|
||||||
|
self.env.unwrap().as_ref(),
|
||||||
|
self.detect_leaks,
|
||||||
|
self.snapshot,
|
||||||
|
self.filter,
|
||||||
|
self.error_callback,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AsanModuleBuilder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new(None, false, true, StdAddressFilter::default(), None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsanModule {
|
||||||
|
#[must_use]
|
||||||
|
pub fn builder() -> AsanModuleBuilder {
|
||||||
|
AsanModuleBuilder::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
env: &[(String, String)],
|
||||||
|
detect_leaks: bool,
|
||||||
|
snapshot: bool,
|
||||||
|
filter: StdAddressFilter,
|
||||||
|
error_callback: Option<AsanErrorCallback>,
|
||||||
|
) -> Self {
|
||||||
|
let mut rt = AsanGiovese::new();
|
||||||
|
|
||||||
|
rt.set_snapshot_shadow(snapshot);
|
||||||
|
if let Some(cb) = error_callback {
|
||||||
|
rt.set_error_callback(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
env: env.to_vec(),
|
||||||
|
enabled: true,
|
||||||
|
detect_leaks,
|
||||||
|
empty: true,
|
||||||
|
rt,
|
||||||
|
filter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn must_instrument(&self, addr: GuestAddr) -> bool {
|
||||||
|
self.filter.allowed(&addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn enabled(&self) -> bool {
|
||||||
|
self.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_enabled(&mut self, enabled: bool) {
|
||||||
|
self.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc(&mut self, pc: GuestAddr, start: GuestAddr, end: GuestAddr) {
|
||||||
|
self.rt.allocation(pc, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dealloc(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
|
self.rt.deallocation(qemu, pc, addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_poisoned(&self, qemu: Qemu, addr: GuestAddr, size: usize) -> bool {
|
||||||
|
AsanGiovese::is_invalid_access_n(qemu, addr, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<const N: usize>(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
|
if self.enabled() && AsanGiovese::is_invalid_access::<N>(qemu, addr) {
|
||||||
|
self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, N));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
||||||
|
if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) {
|
||||||
|
self.rt
|
||||||
|
.report_or_crash(qemu, pc, AsanError::Read(addr, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<const N: usize>(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
||||||
|
if self.enabled() && AsanGiovese::is_invalid_access::<N>(qemu, addr) {
|
||||||
|
self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, N));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
||||||
|
if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) {
|
||||||
|
self.rt
|
||||||
|
.report_or_crash(qemu, pc, AsanError::Write(addr, size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poison(&mut self, qemu: Qemu, addr: GuestAddr, size: usize, poison: PoisonKind) {
|
||||||
|
self.rt.poison(qemu, addr, size, poison.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unpoison(&mut self, qemu: Qemu, addr: GuestAddr, size: usize) {
|
||||||
|
AsanGiovese::unpoison(qemu, addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset(&mut self, qemu: Qemu) -> AsanRollback {
|
||||||
|
self.rt.rollback(qemu, self.detect_leaks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl AsanGiovese {
|
impl AsanGiovese {
|
||||||
unsafe fn init(self: &mut Pin<Box<Self>>, qemu_hooks: QemuHooks) {
|
unsafe fn init(self: &mut Pin<Box<Self>>, qemu_hooks: QemuHooks) {
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
@ -435,22 +696,22 @@ impl AsanGiovese {
|
|||||||
unsafe {
|
unsafe {
|
||||||
let h = qemu.g2h::<*const c_void>(page) as isize;
|
let h = qemu.g2h::<*const c_void>(page) as isize;
|
||||||
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
let shadow_addr = ((h >> 3) as *mut i8).offset(SHADOW_OFFSET);
|
||||||
std::slice::from_raw_parts_mut(shadow_addr, SHADOW_PAGE_SIZE)
|
slice::from_raw_parts_mut(shadow_addr, SHADOW_PAGE_SIZE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report_or_crash(&mut self, qemu: Qemu, pc: GuestAddr, error: AsanError) {
|
pub fn report_or_crash(&mut self, qemu: Qemu, pc: GuestAddr, error: AsanError) {
|
||||||
if let Some(mut cb) = self.error_callback.take() {
|
if let Some(mut cb) = self.error_callback.take() {
|
||||||
cb(self, qemu, pc, error);
|
cb.call(self, qemu, pc, error);
|
||||||
self.error_callback = Some(cb);
|
self.error_callback = Some(cb);
|
||||||
} else {
|
} else {
|
||||||
std::process::abort();
|
process::abort();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn report(&mut self, qemu: Qemu, pc: GuestAddr, error: AsanError) {
|
pub fn report(&mut self, qemu: Qemu, pc: GuestAddr, error: AsanError) {
|
||||||
if let Some(mut cb) = self.error_callback.take() {
|
if let Some(mut cb) = self.error_callback.take() {
|
||||||
cb(self, qemu, pc, error);
|
cb.call(self, qemu, pc, error);
|
||||||
self.error_callback = Some(cb);
|
self.error_callback = Some(cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -649,167 +910,6 @@ impl AsanGiovese {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum QemuAsanOptions {
|
|
||||||
None,
|
|
||||||
Snapshot,
|
|
||||||
DetectLeaks,
|
|
||||||
SnapshotDetectLeaks,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type AsanChildModule = AsanModule;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AsanModule {
|
|
||||||
env: Vec<(String, String)>,
|
|
||||||
enabled: bool,
|
|
||||||
detect_leaks: bool,
|
|
||||||
empty: bool,
|
|
||||||
rt: Pin<Box<AsanGiovese>>,
|
|
||||||
filter: StdAddressFilter,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsanModule {
|
|
||||||
#[must_use]
|
|
||||||
pub fn default(env: &[(String, String)]) -> Self {
|
|
||||||
Self::new(StdAddressFilter::default(), &QemuAsanOptions::Snapshot, env)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(
|
|
||||||
filter: StdAddressFilter,
|
|
||||||
options: &QemuAsanOptions,
|
|
||||||
env: &[(String, String)],
|
|
||||||
) -> Self {
|
|
||||||
let (snapshot, detect_leaks) = match options {
|
|
||||||
QemuAsanOptions::None => (false, false),
|
|
||||||
QemuAsanOptions::Snapshot => (true, false),
|
|
||||||
QemuAsanOptions::DetectLeaks => (false, true),
|
|
||||||
QemuAsanOptions::SnapshotDetectLeaks => (true, true),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut rt = AsanGiovese::new();
|
|
||||||
rt.set_snapshot_shadow(snapshot);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
env: env.to_vec(),
|
|
||||||
enabled: true,
|
|
||||||
detect_leaks,
|
|
||||||
empty: true,
|
|
||||||
rt,
|
|
||||||
filter,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_error_callback(
|
|
||||||
filter: StdAddressFilter,
|
|
||||||
error_callback: AsanErrorCallback,
|
|
||||||
options: &QemuAsanOptions,
|
|
||||||
env: &[(String, String)],
|
|
||||||
) -> Self {
|
|
||||||
let (snapshot, detect_leaks) = match options {
|
|
||||||
QemuAsanOptions::None => (false, false),
|
|
||||||
QemuAsanOptions::Snapshot => (true, false),
|
|
||||||
QemuAsanOptions::DetectLeaks => (false, true),
|
|
||||||
QemuAsanOptions::SnapshotDetectLeaks => (true, true),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut rt = AsanGiovese::new();
|
|
||||||
rt.set_snapshot_shadow(snapshot);
|
|
||||||
rt.set_error_callback(error_callback);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
env: env.to_vec(),
|
|
||||||
enabled: true,
|
|
||||||
detect_leaks,
|
|
||||||
empty: true,
|
|
||||||
rt,
|
|
||||||
filter,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// # Safety
|
|
||||||
/// The `ASan` error report accesses [`FullBacktraceCollector`]
|
|
||||||
#[must_use]
|
|
||||||
pub unsafe fn with_asan_report(
|
|
||||||
filter: StdAddressFilter,
|
|
||||||
options: &QemuAsanOptions,
|
|
||||||
env: &[(String, String)],
|
|
||||||
) -> Self {
|
|
||||||
Self::with_error_callback(
|
|
||||||
filter,
|
|
||||||
Box::new(|rt, qemu, pc, err| unsafe { asan_report(rt, qemu, pc, &err) }),
|
|
||||||
options,
|
|
||||||
env,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn must_instrument(&self, addr: GuestAddr) -> bool {
|
|
||||||
self.filter.allowed(&addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn enabled(&self) -> bool {
|
|
||||||
self.enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_enabled(&mut self, enabled: bool) {
|
|
||||||
self.enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn alloc(&mut self, pc: GuestAddr, start: GuestAddr, end: GuestAddr) {
|
|
||||||
self.rt.allocation(pc, start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dealloc(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
self.rt.deallocation(qemu, pc, addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn is_poisoned(&self, qemu: Qemu, addr: GuestAddr, size: usize) -> bool {
|
|
||||||
AsanGiovese::is_invalid_access_n(qemu, addr, size)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<const N: usize>(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access::<N>(qemu, addr) {
|
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Read(addr, N));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) {
|
|
||||||
self.rt
|
|
||||||
.report_or_crash(qemu, pc, AsanError::Read(addr, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write<const N: usize>(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access::<N>(qemu, addr) {
|
|
||||||
self.rt.report_or_crash(qemu, pc, AsanError::Write(addr, N));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_n(&mut self, qemu: Qemu, pc: GuestAddr, addr: GuestAddr, size: usize) {
|
|
||||||
if self.enabled() && AsanGiovese::is_invalid_access_n(qemu, addr, size) {
|
|
||||||
self.rt
|
|
||||||
.report_or_crash(qemu, pc, AsanError::Write(addr, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn poison(&mut self, qemu: Qemu, addr: GuestAddr, size: usize, poison: PoisonKind) {
|
|
||||||
self.rt.poison(qemu, addr, size, poison.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unpoison(&mut self, qemu: Qemu, addr: GuestAddr, size: usize) {
|
|
||||||
AsanGiovese::unpoison(qemu, addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn reset(&mut self, qemu: Qemu) -> AsanRollback {
|
|
||||||
self.rt.rollback(qemu, self.detect_leaks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<I, S> EmulatorModule<I, S> for AsanModule
|
impl<I, S> EmulatorModule<I, S> for AsanModule
|
||||||
where
|
where
|
||||||
I: Unpin,
|
I: Unpin,
|
||||||
@ -1203,7 +1303,7 @@ fn load_file_section<'input, 'arena, Endian: addr2line::gimli::Endianity>(
|
|||||||
/// has been removed in version v0.23 for some reason.
|
/// has been removed in version v0.23 for some reason.
|
||||||
/// TODO: find another cleaner solution.
|
/// TODO: find another cleaner solution.
|
||||||
mod addr2line_legacy {
|
mod addr2line_legacy {
|
||||||
use std::{borrow::Cow, ffi::OsString, fs::File, path::PathBuf, sync::Arc};
|
use std::{borrow::Cow, env, ffi::OsString, fs::File, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use addr2line::{gimli, LookupContinuation, LookupResult};
|
use addr2line::{gimli, LookupContinuation, LookupResult};
|
||||||
use object::Object;
|
use object::Object;
|
||||||
@ -1223,7 +1323,7 @@ mod addr2line_legacy {
|
|||||||
r: &R,
|
r: &R,
|
||||||
) -> Result<PathBuf, gimli::Error> {
|
) -> Result<PathBuf, gimli::Error> {
|
||||||
let bytes = r.to_slice()?;
|
let bytes = r.to_slice()?;
|
||||||
let s = std::str::from_utf8(&bytes).map_err(|_| gimli::Error::BadUtf8)?;
|
let s = str::from_utf8(&bytes).map_err(|_| gimli::Error::BadUtf8)?;
|
||||||
Ok(PathBuf::from(s))
|
Ok(PathBuf::from(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1269,7 +1369,7 @@ mod addr2line_legacy {
|
|||||||
loader: &mut F,
|
loader: &mut F,
|
||||||
path: Option<PathBuf>,
|
path: Option<PathBuf>,
|
||||||
) -> Option<gimli::DwarfPackage<R>> {
|
) -> Option<gimli::DwarfPackage<R>> {
|
||||||
let mut path = path.map_or_else(std::env::current_exe, Ok).ok()?;
|
let mut path = path.map_or_else(env::current_exe, Ok).ok()?;
|
||||||
let dwp_extension = path.extension().map_or_else(
|
let dwp_extension = path.extension().map_or_else(
|
||||||
|| OsString::from("dwp"),
|
|| OsString::from("dwp"),
|
||||||
|previous_extension| {
|
|previous_extension| {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user