Qemu tmin (#3118)
* Start on qemu_tmin * WIP * qemu_tmin working for single testcase. Also some comment improvements. * Add env_logger to baby_fuzzer * Remove old reference. * Added comment doc for qemu_tmin. * Slight reorder for parallelisation * Finished single-thread qemu_tmin * Finishing touches to single thread version. * A pre_commit.sh change I didn't notice. * Duplicate to attempt multi-threaded version * Fix taplo whine. Mark "fork" mode as broken. * Launcher for parallelisation implemented, but for one core. * Running in parallel. Now need tidy up. * Parallel version complete. * Add comment * Merged single-core/multi-core qemu_tmin into one crate * Removed forkexecutor mode. * Precommit fixes * Add qemu_tmin to build_and_test.yml * Clippy fixes * Change tmin test cores to 0.
This commit is contained in:
parent
6bbff51951
commit
c3475cd577
1
.github/workflows/build_and_test.yml
vendored
1
.github/workflows/build_and_test.yml
vendored
@ -384,6 +384,7 @@ jobs:
|
|||||||
fuzzer:
|
fuzzer:
|
||||||
# Binary only
|
# Binary only
|
||||||
- ./fuzzers/binary_only/qemu_cmin
|
- ./fuzzers/binary_only/qemu_cmin
|
||||||
|
- ./fuzzers/binary_only/qemu_tmin
|
||||||
- ./fuzzers/binary_only/qemu_coverage
|
- ./fuzzers/binary_only/qemu_coverage
|
||||||
- ./fuzzers/binary_only/qemu_launcher
|
- ./fuzzers/binary_only/qemu_launcher
|
||||||
arch:
|
arch:
|
||||||
|
@ -23,6 +23,7 @@ opt-level = 3
|
|||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
env_logger = "0.11.7"
|
||||||
libafl = { path = "../../../libafl", features = ["tui_monitor"] }
|
libafl = { path = "../../../libafl", features = ["tui_monitor"] }
|
||||||
libafl_bolts = { path = "../../../libafl_bolts" }
|
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||||
log = { version = "0.4.22", features = ["release_max_level_info"] }
|
log = { version = "0.4.22", features = ["release_max_level_info"] }
|
||||||
|
@ -35,6 +35,7 @@ fn signals_set(idx: usize) {
|
|||||||
|
|
||||||
#[expect(clippy::manual_assert)]
|
#[expect(clippy::manual_assert)]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
env_logger::init();
|
||||||
// The closure that we want to fuzz
|
// The closure that we want to fuzz
|
||||||
let mut harness = |input: &BytesInput| {
|
let mut harness = |input: &BytesInput| {
|
||||||
let target = input.target_bytes();
|
let target = input.target_bytes();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
//! A binary-only corpus minimizer using qemu, similar to AFL++ afl-cmin
|
||||||
#[cfg(feature = "i386")]
|
#[cfg(feature = "i386")]
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
#[cfg(feature = "snapshot")]
|
#[cfg(feature = "snapshot")]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
//! A binary-only corpus minimizer using qemu, similar to AFL++ afl-cmin
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod fuzzer;
|
mod fuzzer;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
//! A qemu test case runner to generate drcov coverage outputs
|
||||||
#[cfg(feature = "i386")]
|
#[cfg(feature = "i386")]
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
//! A qemu test case runner to generate drcov coverage outputs
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod fuzzer;
|
mod fuzzer;
|
||||||
|
|
||||||
|
8
fuzzers/binary_only/qemu_tmin/.gitignore
vendored
Normal file
8
fuzzers/binary_only/qemu_tmin/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
libpng-*
|
||||||
|
libpng_harness
|
||||||
|
libpng_harness_crashing
|
||||||
|
zlib-*
|
||||||
|
crashes
|
||||||
|
target
|
||||||
|
output
|
||||||
|
corpus/
|
3598
fuzzers/binary_only/qemu_tmin/Cargo.lock
generated
Normal file
3598
fuzzers/binary_only/qemu_tmin/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
51
fuzzers/binary_only/qemu_tmin/Cargo.toml
Normal file
51
fuzzers/binary_only/qemu_tmin/Cargo.toml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
[package]
|
||||||
|
name = "qemu_tmin"
|
||||||
|
version = "0.15.1"
|
||||||
|
authors = [
|
||||||
|
"Andrea Fioraldi <andreafioraldi@gmail.com>",
|
||||||
|
"Dominik Maier <domenukk@gmail.com>",
|
||||||
|
"WorksButNotTested",
|
||||||
|
"forky2",
|
||||||
|
]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
#lto = true
|
||||||
|
#codegen-units = 1
|
||||||
|
#opt-level = 3
|
||||||
|
debug = true
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "tmin_single_core"
|
||||||
|
path = "src/tmin_single_core.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "tmin_multi_core"
|
||||||
|
path = "src/tmin_multi_core.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std", "snapshot"]
|
||||||
|
fork = []
|
||||||
|
snapshot = []
|
||||||
|
std = []
|
||||||
|
be = ["libafl_qemu/be"]
|
||||||
|
arm = ["libafl_qemu/arm"]
|
||||||
|
x86_64 = ["libafl_qemu/x86_64"]
|
||||||
|
i386 = ["libafl_qemu/i386"]
|
||||||
|
aarch64 = ["libafl_qemu/aarch64"]
|
||||||
|
mips = ["libafl_qemu/mips"]
|
||||||
|
ppc = ["libafl_qemu/ppc", "be"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
vergen = { version = "9.0.1", features = ["build", "cargo", "rustc", "si"] }
|
||||||
|
vergen-git2 = "1.0.1"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.18", features = ["derive", "string"] }
|
||||||
|
env_logger = { version = "0.11.5" }
|
||||||
|
libafl = { path = "../../../libafl" }
|
||||||
|
libafl_bolts = { path = "../../../libafl_bolts" }
|
||||||
|
libafl_qemu = { path = "../../../libafl_qemu", features = ["usermode"] }
|
||||||
|
libafl_targets = { path = "../../../libafl_targets" }
|
||||||
|
log = { version = "0.4.22", features = ["release_max_level_info"] }
|
||||||
|
rangemap = { version = "1.5.1" }
|
58
fuzzers/binary_only/qemu_tmin/Justfile
Normal file
58
fuzzers/binary_only/qemu_tmin/Justfile
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import "../../../just/libafl-qemu-libpng.just"
|
||||||
|
|
||||||
|
FUZZER_NAME := ""
|
||||||
|
FUZZER_SINGLE := BUILD_DIR / "tmin_single_core" + FUZZER_EXTENSION
|
||||||
|
FUZZER_MULTI := BUILD_DIR / "tmin_multi_core" + FUZZER_EXTENSION
|
||||||
|
HARNESS := TARGET_DIR / ("libpng-harness-" + PROFILE)
|
||||||
|
|
||||||
|
[unix]
|
||||||
|
build:
|
||||||
|
cargo build \
|
||||||
|
--profile {{ PROFILE }} \
|
||||||
|
--features {{ ARCH }} \
|
||||||
|
--target-dir {{ TARGET_DIR }}
|
||||||
|
|
||||||
|
[unix]
|
||||||
|
harness: libpng
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
source {{ DOTENV }}
|
||||||
|
|
||||||
|
$CROSS_CXX \
|
||||||
|
./harness.cc \
|
||||||
|
$CROSS_CFLAGS \
|
||||||
|
"{{TARGET_DIR}}/build-png/.libs/libpng16.a" \
|
||||||
|
"{{TARGET_DIR}}/build-zlib/libz.a" \
|
||||||
|
-I"{{TARGET_DIR}}/build-png" \
|
||||||
|
-I"{{TARGET_DIR}}/build-zlib/zlib/lib" \
|
||||||
|
-L"{{TARGET_DIR}}/build-zlib/zlib/lib" \
|
||||||
|
-o"{{ HARNESS }}" \
|
||||||
|
-lm -static
|
||||||
|
|
||||||
|
[unix]
|
||||||
|
run_single: harness build
|
||||||
|
{{ FUZZER_SINGLE }} \
|
||||||
|
--output ./output \
|
||||||
|
--input ./corpus \
|
||||||
|
--verbose \
|
||||||
|
-- {{ HARNESS }}
|
||||||
|
|
||||||
|
[unix]
|
||||||
|
run_multi: harness build
|
||||||
|
{{ FUZZER_MULTI }} \
|
||||||
|
--output ./output \
|
||||||
|
--input ./corpus \
|
||||||
|
--cores 0 \
|
||||||
|
--verbose \
|
||||||
|
-- {{ HARNESS }}
|
||||||
|
|
||||||
|
[unix]
|
||||||
|
test:
|
||||||
|
ARCH=x86_64 just run_single
|
||||||
|
ARCH=x86_64 just run_multi
|
||||||
|
ARCH=arm just run_single
|
||||||
|
ARCH=arm just run_multi
|
||||||
|
|
||||||
|
[unix]
|
||||||
|
clean:
|
||||||
|
cargo clean
|
45
fuzzers/binary_only/qemu_tmin/README.md
Normal file
45
fuzzers/binary_only/qemu_tmin/README.md
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# qemu_tmin
|
||||||
|
|
||||||
|
QEMU testcase minimizer.
|
||||||
|
|
||||||
|
This folder contains an example fuzzer which runs each entry in the input corpus
|
||||||
|
and minimizes the input, ensuring that coverage map remains the same. The output
|
||||||
|
is a new corpus that may or may not be smaller than the original inputs, but
|
||||||
|
will not be larger.
|
||||||
|
|
||||||
|
If some input files are idential, only one of each duplicate set will be kept
|
||||||
|
for minimization.
|
||||||
|
|
||||||
|
The following architectures are supported:
|
||||||
|
* arm
|
||||||
|
* aarch64
|
||||||
|
* i386
|
||||||
|
* x86_64
|
||||||
|
* mips
|
||||||
|
* ppc
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
```bash
|
||||||
|
sudo apt install \
|
||||||
|
gcc-arm-linux-gnueabi \
|
||||||
|
g++-arm-linux-gnueabi \
|
||||||
|
gcc-aarch64-linux-gnu \
|
||||||
|
g++-aarch64-linux-gnu \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
gcc-mipsel-linux-gnu \
|
||||||
|
g++-mipsel-linux-gnu \
|
||||||
|
gcc-powerpc-linux-gnu \
|
||||||
|
g++-powerpc-linux-gnu
|
||||||
|
```
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
Defaults to `x86_64` architecture
|
||||||
|
```bash
|
||||||
|
just run
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
just <arch>
|
||||||
|
```
|
59
fuzzers/binary_only/qemu_tmin/build.rs
Normal file
59
fuzzers/binary_only/qemu_tmin/build.rs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
use vergen::{BuildBuilder, CargoBuilder, Emitter, RustcBuilder, SysinfoBuilder};
|
||||||
|
use vergen_git2::Git2Builder;
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! assert_unique_feature {
|
||||||
|
() => {};
|
||||||
|
($first:tt $(,$rest:tt)*) => {
|
||||||
|
$(
|
||||||
|
#[cfg(all(not(doc), feature = $first, feature = $rest))]
|
||||||
|
compile_error!(concat!("features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
|
||||||
|
)*
|
||||||
|
assert_unique_feature!($($rest),*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let build = BuildBuilder::all_build().unwrap();
|
||||||
|
let cargo = CargoBuilder::all_cargo().unwrap();
|
||||||
|
let git = Git2Builder::all_git().unwrap();
|
||||||
|
let rustc = RustcBuilder::all_rustc().unwrap();
|
||||||
|
let sysinfo = SysinfoBuilder::all_sysinfo().unwrap();
|
||||||
|
|
||||||
|
Emitter::default()
|
||||||
|
.add_instructions(&build)
|
||||||
|
.unwrap()
|
||||||
|
.add_instructions(&cargo)
|
||||||
|
.unwrap()
|
||||||
|
.add_instructions(&git)
|
||||||
|
.unwrap()
|
||||||
|
.add_instructions(&rustc)
|
||||||
|
.unwrap()
|
||||||
|
.add_instructions(&sysinfo)
|
||||||
|
.unwrap()
|
||||||
|
.emit()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_unique_feature!("arm", "aarch64", "i386", "x86_64", "mips", "ppc");
|
||||||
|
|
||||||
|
let cpu_target = if cfg!(feature = "x86_64") {
|
||||||
|
"x86_64".to_string()
|
||||||
|
} else if cfg!(feature = "arm") {
|
||||||
|
"arm".to_string()
|
||||||
|
} else if cfg!(feature = "aarch64") {
|
||||||
|
"aarch64".to_string()
|
||||||
|
} else if cfg!(feature = "i386") {
|
||||||
|
"i386".to_string()
|
||||||
|
} else if cfg!(feature = "mips") {
|
||||||
|
"mips".to_string()
|
||||||
|
} else if cfg!(feature = "ppc") {
|
||||||
|
"ppc".to_string()
|
||||||
|
} else {
|
||||||
|
println!("cargo:warning=No architecture specified defaulting to x86_64...");
|
||||||
|
println!("cargo:rustc-cfg=feature=\"x86_64\"");
|
||||||
|
println!("cargo:rustc-cfg=feature=\"64bit\"");
|
||||||
|
"x86_64".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("cargo:rustc-env=CPU_TARGET={cpu_target}");
|
||||||
|
}
|
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty.png
Normal file
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 218 B |
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty_alpha.png
Normal file
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty_alpha.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 376 B |
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty_alpha_long.png
Normal file
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty_alpha_long.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 107 KiB |
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty_gamma.png
Normal file
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty_gamma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 B |
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty_icc.png
Normal file
BIN
fuzzers/binary_only/qemu_tmin/corpus/not_kitty_icc.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 427 B |
193
fuzzers/binary_only/qemu_tmin/harness.cc
Normal file
193
fuzzers/binary_only/qemu_tmin/harness.cc
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// libpng_read_fuzzer.cc
|
||||||
|
// Copyright 2017-2018 Glenn Randers-Pehrson
|
||||||
|
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that may
|
||||||
|
// be found in the LICENSE file https://cs.chromium.org/chromium/src/LICENSE
|
||||||
|
|
||||||
|
// Last changed in libpng 1.6.35 [July 15, 2018]
|
||||||
|
|
||||||
|
// The modifications in 2017 by Glenn Randers-Pehrson include
|
||||||
|
// 1. addition of a PNG_CLEANUP macro,
|
||||||
|
// 2. setting the option to ignore ADLER32 checksums,
|
||||||
|
// 3. adding "#include <string.h>" which is needed on some platforms
|
||||||
|
// to provide memcpy().
|
||||||
|
// 4. adding read_end_info() and creating an end_info structure.
|
||||||
|
// 5. adding calls to png_set_*() transforms commonly used by browsers.
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define PNG_INTERNAL
|
||||||
|
#include "png.h"
|
||||||
|
|
||||||
|
#define PNG_CLEANUP \
|
||||||
|
if (png_handler.png_ptr) { \
|
||||||
|
if (png_handler.row_ptr) \
|
||||||
|
png_free(png_handler.png_ptr, png_handler.row_ptr); \
|
||||||
|
if (png_handler.end_info_ptr) \
|
||||||
|
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \
|
||||||
|
&png_handler.end_info_ptr); \
|
||||||
|
else if (png_handler.info_ptr) \
|
||||||
|
png_destroy_read_struct(&png_handler.png_ptr, &png_handler.info_ptr, \
|
||||||
|
nullptr); \
|
||||||
|
else \
|
||||||
|
png_destroy_read_struct(&png_handler.png_ptr, nullptr, nullptr); \
|
||||||
|
png_handler.png_ptr = nullptr; \
|
||||||
|
png_handler.row_ptr = nullptr; \
|
||||||
|
png_handler.info_ptr = nullptr; \
|
||||||
|
png_handler.end_info_ptr = nullptr; \
|
||||||
|
}
|
||||||
|
|
||||||
|
struct BufState {
|
||||||
|
const uint8_t *data;
|
||||||
|
size_t bytes_left;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PngObjectHandler {
|
||||||
|
png_infop info_ptr = nullptr;
|
||||||
|
png_structp png_ptr = nullptr;
|
||||||
|
png_infop end_info_ptr = nullptr;
|
||||||
|
png_voidp row_ptr = nullptr;
|
||||||
|
BufState *buf_state = nullptr;
|
||||||
|
|
||||||
|
~PngObjectHandler() {
|
||||||
|
if (row_ptr) { png_free(png_ptr, row_ptr); }
|
||||||
|
if (end_info_ptr)
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
|
||||||
|
else if (info_ptr)
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, nullptr);
|
||||||
|
else
|
||||||
|
png_destroy_read_struct(&png_ptr, nullptr, nullptr);
|
||||||
|
delete buf_state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void user_read_data(png_structp png_ptr, png_bytep data, size_t length) {
|
||||||
|
BufState *buf_state = static_cast<BufState *>(png_get_io_ptr(png_ptr));
|
||||||
|
if (length > buf_state->bytes_left) { png_error(png_ptr, "read error"); }
|
||||||
|
memcpy(data, buf_state->data, length);
|
||||||
|
buf_state->bytes_left -= length;
|
||||||
|
buf_state->data += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const int kPngHeaderSize = 8;
|
||||||
|
|
||||||
|
// Entry point for LibFuzzer.
|
||||||
|
// Roughly follows the libpng book example:
|
||||||
|
// http://www.libpng.org/pub/png/book/chapter13.html
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
|
if (size < kPngHeaderSize) { return 0; }
|
||||||
|
|
||||||
|
std::vector<unsigned char> v(data, data + size);
|
||||||
|
if (png_sig_cmp(v.data(), 0, kPngHeaderSize)) {
|
||||||
|
// not a PNG.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PngObjectHandler png_handler;
|
||||||
|
png_handler.png_ptr = nullptr;
|
||||||
|
png_handler.row_ptr = nullptr;
|
||||||
|
png_handler.info_ptr = nullptr;
|
||||||
|
png_handler.end_info_ptr = nullptr;
|
||||||
|
|
||||||
|
png_handler.png_ptr =
|
||||||
|
png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
||||||
|
if (!png_handler.png_ptr) { return 0; }
|
||||||
|
|
||||||
|
png_handler.info_ptr = png_create_info_struct(png_handler.png_ptr);
|
||||||
|
if (!png_handler.info_ptr) {
|
||||||
|
PNG_CLEANUP
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_handler.end_info_ptr = png_create_info_struct(png_handler.png_ptr);
|
||||||
|
if (!png_handler.end_info_ptr) {
|
||||||
|
PNG_CLEANUP
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_set_crc_action(png_handler.png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
||||||
|
#ifdef PNG_IGNORE_ADLER32
|
||||||
|
png_set_option(png_handler.png_ptr, PNG_IGNORE_ADLER32, PNG_OPTION_ON);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Setting up reading from buffer.
|
||||||
|
png_handler.buf_state = new BufState();
|
||||||
|
png_handler.buf_state->data = data + kPngHeaderSize;
|
||||||
|
png_handler.buf_state->bytes_left = size - kPngHeaderSize;
|
||||||
|
png_set_read_fn(png_handler.png_ptr, png_handler.buf_state, user_read_data);
|
||||||
|
png_set_sig_bytes(png_handler.png_ptr, kPngHeaderSize);
|
||||||
|
|
||||||
|
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
|
||||||
|
PNG_CLEANUP
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading.
|
||||||
|
png_read_info(png_handler.png_ptr, png_handler.info_ptr);
|
||||||
|
|
||||||
|
// reset error handler to put png_deleter into scope.
|
||||||
|
if (setjmp(png_jmpbuf(png_handler.png_ptr))) {
|
||||||
|
PNG_CLEANUP
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
png_uint_32 width, height;
|
||||||
|
int bit_depth, color_type, interlace_type, compression_type;
|
||||||
|
int filter_type;
|
||||||
|
|
||||||
|
if (!png_get_IHDR(png_handler.png_ptr, png_handler.info_ptr, &width, &height,
|
||||||
|
&bit_depth, &color_type, &interlace_type, &compression_type,
|
||||||
|
&filter_type)) {
|
||||||
|
PNG_CLEANUP
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is going to be too slow.
|
||||||
|
if (width && height > 100000000 / width) {
|
||||||
|
PNG_CLEANUP
|
||||||
|
#ifdef HAS_DUMMY_CRASH
|
||||||
|
#if defined(__aarch64__) || defined(__arm__)
|
||||||
|
asm volatile(".word 0xf7f0a000\n");
|
||||||
|
#else
|
||||||
|
asm("ud2");
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set several transforms that browsers typically use:
|
||||||
|
png_set_gray_to_rgb(png_handler.png_ptr);
|
||||||
|
png_set_expand(png_handler.png_ptr);
|
||||||
|
png_set_packing(png_handler.png_ptr);
|
||||||
|
png_set_scale_16(png_handler.png_ptr);
|
||||||
|
png_set_tRNS_to_alpha(png_handler.png_ptr);
|
||||||
|
|
||||||
|
int passes = png_set_interlace_handling(png_handler.png_ptr);
|
||||||
|
|
||||||
|
png_read_update_info(png_handler.png_ptr, png_handler.info_ptr);
|
||||||
|
|
||||||
|
png_handler.row_ptr =
|
||||||
|
png_malloc(png_handler.png_ptr,
|
||||||
|
png_get_rowbytes(png_handler.png_ptr, png_handler.info_ptr));
|
||||||
|
|
||||||
|
for (int pass = 0; pass < passes; ++pass) {
|
||||||
|
for (png_uint_32 y = 0; y < height; ++y) {
|
||||||
|
png_read_row(png_handler.png_ptr,
|
||||||
|
static_cast<png_bytep>(png_handler.row_ptr), nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
png_read_end(png_handler.png_ptr, png_handler.end_info_ptr);
|
||||||
|
|
||||||
|
PNG_CLEANUP
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
uint8_t buf[10] = {0};
|
||||||
|
LLVMFuzzerTestOneInput(buf, 10);
|
||||||
|
}
|
372
fuzzers/binary_only/qemu_tmin/src/tmin_multi_core.rs
Normal file
372
fuzzers/binary_only/qemu_tmin/src/tmin_multi_core.rs
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
//! A binary-only testcase minimizer using qemu, similar to AFL++ afl-tmin
|
||||||
|
#[cfg(feature = "i386")]
|
||||||
|
use core::mem::size_of;
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::{env, fmt::Write, io, path::PathBuf, process, ptr::NonNull};
|
||||||
|
|
||||||
|
use clap::{builder::Str, Parser};
|
||||||
|
use libafl::{
|
||||||
|
corpus::{Corpus, CorpusId, HasCurrentCorpusId, InMemoryCorpus, InMemoryOnDiskCorpus},
|
||||||
|
events::{
|
||||||
|
launcher::Launcher, ClientDescription, EventConfig, LlmpRestartingEventManager, SendExiting,
|
||||||
|
},
|
||||||
|
executors::ExitKind,
|
||||||
|
feedbacks::MaxMapFeedback,
|
||||||
|
fuzzer::StdFuzzer,
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
monitors::MultiMonitor,
|
||||||
|
mutators::{havoc_mutations, HavocScheduledMutator},
|
||||||
|
observers::{ConstMapObserver, HitcountsMapObserver},
|
||||||
|
schedulers::QueueScheduler,
|
||||||
|
stages::{ObserverEqualityFactory, StagesTuple, StdTMinMutationalStage},
|
||||||
|
state::{HasCorpus, StdState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use libafl_bolts::{
|
||||||
|
core_affinity::Cores,
|
||||||
|
os::unix_signals::Signal,
|
||||||
|
rands::StdRand,
|
||||||
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
|
tuples::tuple_list,
|
||||||
|
AsSlice, AsSliceMut,
|
||||||
|
};
|
||||||
|
use libafl_qemu::{
|
||||||
|
elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, Emulator, GuestAddr,
|
||||||
|
GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuShutdownCause, Regs,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
use libafl_qemu::{modules::SnapshotModule, QemuExecutor};
|
||||||
|
use libafl_targets::{EDGES_MAP_DEFAULT_SIZE, EDGES_MAP_PTR};
|
||||||
|
|
||||||
|
#[cfg(feature = "fork")]
|
||||||
|
compile_error!("'fork' feature is currently not implemented; pending forkserver PR.");
|
||||||
|
|
||||||
|
#[cfg(all(feature = "fork", feature = "snapshot"))]
|
||||||
|
compile_error!("Cannot enable both 'fork' and 'snapshot' features at the same time.");
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Version;
|
||||||
|
|
||||||
|
impl From<Version> for Str {
|
||||||
|
fn from(_: Version) -> Str {
|
||||||
|
let version = [
|
||||||
|
("Architecture:", env!("CPU_TARGET")),
|
||||||
|
("Build Timestamp:", env!("VERGEN_BUILD_TIMESTAMP")),
|
||||||
|
("Describe:", env!("VERGEN_GIT_DESCRIBE")),
|
||||||
|
("Commit SHA:", env!("VERGEN_GIT_SHA")),
|
||||||
|
("Commit Date:", env!("VERGEN_RUSTC_COMMIT_DATE")),
|
||||||
|
("Commit Branch:", env!("VERGEN_GIT_BRANCH")),
|
||||||
|
("Rustc Version:", env!("VERGEN_RUSTC_SEMVER")),
|
||||||
|
("Rustc Channel:", env!("VERGEN_RUSTC_CHANNEL")),
|
||||||
|
("Rustc Host Triple:", env!("VERGEN_RUSTC_HOST_TRIPLE")),
|
||||||
|
("Rustc Commit SHA:", env!("VERGEN_RUSTC_COMMIT_HASH")),
|
||||||
|
("Cargo Target Triple", env!("VERGEN_CARGO_TARGET_TRIPLE")),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.fold(String::new(), |mut output, (k, v)| {
|
||||||
|
let _ = writeln!(output, "{k:25}: {v}");
|
||||||
|
output
|
||||||
|
});
|
||||||
|
|
||||||
|
format!("\n{version:}").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[clap(author, version, about, long_about = None)]
|
||||||
|
#[command(
|
||||||
|
name = format ! ("qemu_tmin-{}", env ! ("CPU_TARGET")),
|
||||||
|
version = Version::default(),
|
||||||
|
about,
|
||||||
|
long_about = "Module for minimizing test cases using QEMU instrumentation"
|
||||||
|
)]
|
||||||
|
pub struct FuzzerOptions {
|
||||||
|
#[arg(long, help = "Output directory")]
|
||||||
|
output: String,
|
||||||
|
|
||||||
|
#[arg(long, help = "Input directory")]
|
||||||
|
input: String,
|
||||||
|
|
||||||
|
#[arg(long, help = "Timeout in seconds", default_value_t = 1_u64)]
|
||||||
|
timeout: u64,
|
||||||
|
|
||||||
|
#[arg(long = "port", help = "Broker port", default_value_t = 1337_u16)]
|
||||||
|
port: u16,
|
||||||
|
|
||||||
|
#[arg(long, help = "Cpu cores to use", default_value = "all", value_parser = Cores::from_cmdline)]
|
||||||
|
cores: Cores,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Number of iterations for minimization",
|
||||||
|
default_value_t = 1024_usize
|
||||||
|
)]
|
||||||
|
iterations: usize,
|
||||||
|
|
||||||
|
#[clap(short, long, help = "Enable output from the fuzzer clients")]
|
||||||
|
verbose: bool,
|
||||||
|
|
||||||
|
#[arg(last = true, help = "Arguments passed to the target")]
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MAX_INPUT_SIZE: usize = 1048576; // 1MB
|
||||||
|
|
||||||
|
pub fn fuzz() {
|
||||||
|
// Initialise env_logger
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
// Parse user options
|
||||||
|
let mut options = FuzzerOptions::parse();
|
||||||
|
let program = env::args().next().unwrap();
|
||||||
|
log::info!("Program: {program:}");
|
||||||
|
options.args.insert(0, program);
|
||||||
|
log::info!("ARGS: {:#?}", options.args);
|
||||||
|
|
||||||
|
// Get all of the files supplied from the input corpus
|
||||||
|
let corpus_dir = PathBuf::from(options.input);
|
||||||
|
let files = corpus_dir
|
||||||
|
.read_dir()
|
||||||
|
.expect("Failed to read corpus dir")
|
||||||
|
.map(|x| Ok(x?.path()))
|
||||||
|
.collect::<Result<Vec<PathBuf>, io::Error>>()
|
||||||
|
.expect("Failed to read dir entry");
|
||||||
|
|
||||||
|
// To run parallelised, we work out number of files to process per core.
|
||||||
|
let num_files = files.len();
|
||||||
|
let num_cores = options.cores.ids.len();
|
||||||
|
let files_per_core = (num_files as f64 / num_cores as f64).ceil() as usize;
|
||||||
|
|
||||||
|
// Create a shared memory region for sharing coverage map between fuzzer and target
|
||||||
|
// In snapshot mode, this is only required for the SimpleRestartingEventManager.
|
||||||
|
// However, fork mode requires it to share memory between parent and child,
|
||||||
|
// so we use it in both cases.
|
||||||
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
|
// Clear LD_LIBRARY_PATH
|
||||||
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
|
|
||||||
|
// The client closure is a 'fuzzing' process for a single core.
|
||||||
|
// Because it is used inside the Launcher (below) it is a fork()ed separate
|
||||||
|
// process from our main process here.
|
||||||
|
let mut run_client = |state: Option<_>,
|
||||||
|
mut mgr: LlmpRestartingEventManager<_, _, _, _, _>,
|
||||||
|
client_description: ClientDescription| {
|
||||||
|
let core_id = client_description.core_id();
|
||||||
|
let core_idx = options
|
||||||
|
.cores
|
||||||
|
.position(core_id)
|
||||||
|
.expect("Failed to get core index");
|
||||||
|
|
||||||
|
let files: Vec<PathBuf> = files
|
||||||
|
.iter()
|
||||||
|
.skip(files_per_core * core_idx)
|
||||||
|
.take(files_per_core)
|
||||||
|
.cloned()
|
||||||
|
.collect::<Vec<PathBuf>>();
|
||||||
|
|
||||||
|
if files.is_empty() {
|
||||||
|
mgr.send_exiting()?;
|
||||||
|
Err(Error::ShuttingDown)?
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut edges_shmem = shmem_provider
|
||||||
|
.clone()
|
||||||
|
.new_shmem(EDGES_MAP_DEFAULT_SIZE)
|
||||||
|
.unwrap();
|
||||||
|
let edges = edges_shmem.as_slice_mut();
|
||||||
|
unsafe { EDGES_MAP_PTR = edges.as_mut_ptr() };
|
||||||
|
|
||||||
|
// We use a HitcountsMapObserver to observe the coverage map
|
||||||
|
let mut edges_observer = unsafe {
|
||||||
|
HitcountsMapObserver::new(ConstMapObserver::from_mut_ptr(
|
||||||
|
"edges",
|
||||||
|
NonNull::new(edges.as_mut_ptr())
|
||||||
|
.expect("The edge map pointer is null.")
|
||||||
|
.cast::<[u8; EDGES_MAP_DEFAULT_SIZE]>(),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
// In either fork/snapshot mode, we link the observer to QEMU
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
let modules = tuple_list!(
|
||||||
|
StdEdgeCoverageChildModule::builder()
|
||||||
|
.const_map_observer(edges_observer.as_mut())
|
||||||
|
.build()?,
|
||||||
|
SnapshotModule::new()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create our QEMU emulator
|
||||||
|
let emulator = Emulator::empty()
|
||||||
|
.qemu_parameters(options.args.clone())
|
||||||
|
.modules(modules)
|
||||||
|
.build()?;
|
||||||
|
let qemu = emulator.qemu();
|
||||||
|
|
||||||
|
// Use ELF tools to get the target function
|
||||||
|
let mut elf_buffer = Vec::new();
|
||||||
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
|
||||||
|
let test_one_input_ptr = elf
|
||||||
|
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||||
|
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
||||||
|
log::info!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
||||||
|
|
||||||
|
// Run target until the target function, and store important registers.
|
||||||
|
// Set a breakpoint on target function return.
|
||||||
|
qemu.entry_break(test_one_input_ptr);
|
||||||
|
let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap();
|
||||||
|
let ret_addr: GuestAddr = qemu.read_return_address().unwrap();
|
||||||
|
let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
|
log::info!("Break at {pc:#x}");
|
||||||
|
log::info!("Return address = {ret_addr:#x}");
|
||||||
|
qemu.set_breakpoint(ret_addr);
|
||||||
|
|
||||||
|
// Map a private region for input buffer
|
||||||
|
let input_addr = qemu
|
||||||
|
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
||||||
|
.unwrap();
|
||||||
|
log::info!("Placing input at {input_addr:#x}");
|
||||||
|
|
||||||
|
// Rust harness: this closure copies an input buffer to our private region
|
||||||
|
// for target function input and updates registers to a single iteration
|
||||||
|
// before telling QEMU to resume execution.
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
let mut harness =
|
||||||
|
|_emulator: &mut Emulator<_, _, _, _, _, _, _>, _state: &mut _, input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let mut buf = target.as_slice();
|
||||||
|
let mut len = buf.len();
|
||||||
|
if len > MAX_INPUT_SIZE {
|
||||||
|
buf = &buf[0..MAX_INPUT_SIZE];
|
||||||
|
len = MAX_INPUT_SIZE;
|
||||||
|
}
|
||||||
|
let len = len as GuestReg;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
qemu.write_mem(input_addr, buf).expect("qemu write failed.");
|
||||||
|
|
||||||
|
qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
|
||||||
|
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
||||||
|
qemu.write_return_address(ret_addr).unwrap();
|
||||||
|
qemu.write_function_argument(0, input_addr).unwrap();
|
||||||
|
qemu.write_function_argument(1, len).unwrap();
|
||||||
|
|
||||||
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(
|
||||||
|
Signal::SigInterrupt,
|
||||||
|
))) => process::exit(0),
|
||||||
|
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
|
|
||||||
|
// Our fuzzer is a simple queue scheduler (FIFO), and has no corpus feedback
|
||||||
|
// or objective feedback. This is important as we need the MaxMapFeedback
|
||||||
|
// on the observer to be constrained by ObserverEqualityFactory which will
|
||||||
|
// ensure interestingness is only true for identical coverage.
|
||||||
|
let scheduler = QueueScheduler::new();
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, (), ());
|
||||||
|
|
||||||
|
// We define the stages that will be performed by the fuzzer. We have one
|
||||||
|
// stage of the StdTMinMutationalStage which will run for n iterations.
|
||||||
|
// The havoc mutator will generate mutations; only those shorter than the
|
||||||
|
// current input will be tested; and the ObservreEqualityFactory will
|
||||||
|
// provide an observer that ensures additions to the corpus have the same
|
||||||
|
// coverage.
|
||||||
|
let minimizer = HavocScheduledMutator::new(havoc_mutations());
|
||||||
|
let factory = ObserverEqualityFactory::new(&edges_observer);
|
||||||
|
let mut stages = tuple_list!(StdTMinMutationalStage::new(
|
||||||
|
minimizer,
|
||||||
|
factory,
|
||||||
|
options.iterations
|
||||||
|
),);
|
||||||
|
|
||||||
|
// Create a state instance. Unlike a typical fuzzer, we start with an empty
|
||||||
|
// input corpus, and we don't care about 'solutions' so store in an
|
||||||
|
// InMemoryCorpus.
|
||||||
|
let mut feedback = MaxMapFeedback::new(&edges_observer);
|
||||||
|
let mut objective = ();
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
StdState::new(
|
||||||
|
StdRand::new(),
|
||||||
|
InMemoryOnDiskCorpus::new(PathBuf::from(options.output.clone())).unwrap(),
|
||||||
|
InMemoryCorpus::new(),
|
||||||
|
&mut feedback,
|
||||||
|
&mut objective,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
// The executor. Nothing exciting here.
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
let mut executor = QemuExecutor::new(
|
||||||
|
emulator,
|
||||||
|
&mut harness,
|
||||||
|
tuple_list!(edges_observer),
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
Duration::from_millis(5000),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Load the input corpus
|
||||||
|
state.load_initial_inputs_by_filenames_forced(
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut executor,
|
||||||
|
&mut mgr,
|
||||||
|
&files,
|
||||||
|
)?;
|
||||||
|
log::info!("Processed {} inputs from disk.", files.len());
|
||||||
|
|
||||||
|
// Iterate over initial corpus_ids and minimize each.
|
||||||
|
let corpus_ids: Vec<CorpusId> = state.corpus().ids().collect();
|
||||||
|
for corpus_id in corpus_ids {
|
||||||
|
state.set_corpus_id(corpus_id)?;
|
||||||
|
stages.perform_all(&mut fuzzer, &mut executor, &mut state, &mut mgr)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr.send_exiting()?;
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
// The Launcher creates forks on the specified list of cores, and for each
|
||||||
|
// one runs the run_client closure which performs the work.
|
||||||
|
// It links the forks to a MultiMonitor which just prints out the events
|
||||||
|
// it receives.
|
||||||
|
match Launcher::builder()
|
||||||
|
.shmem_provider(shmem_provider.clone())
|
||||||
|
.broker_port(options.port)
|
||||||
|
.configuration(EventConfig::from_build_id())
|
||||||
|
.monitor(MultiMonitor::new(|s| println!("{s}")))
|
||||||
|
.run_client(&mut run_client)
|
||||||
|
.cores(&options.cores)
|
||||||
|
.build()
|
||||||
|
.launch()
|
||||||
|
{
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(Error::ShuttingDown) => {
|
||||||
|
println!("Run finished successfully.");
|
||||||
|
}
|
||||||
|
Err(err) => panic!("Failed to run launcher: {err:?}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here it would be nice to sum the number of cases processed by each fork
|
||||||
|
// to provide an exit summary (as is done for the "snapshot" version), but
|
||||||
|
// to do so would require more communication between child and parent. So
|
||||||
|
// it is left undone.
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn main() {
|
||||||
|
fuzz();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn main() {
|
||||||
|
panic!("qemu-user and libafl_qemu is only supported on linux!");
|
||||||
|
}
|
319
fuzzers/binary_only/qemu_tmin/src/tmin_single_core.rs
Normal file
319
fuzzers/binary_only/qemu_tmin/src/tmin_single_core.rs
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
//! A binary-only testcase minimizer using qemu, similar to AFL++ afl-tmin
|
||||||
|
#[cfg(feature = "i386")]
|
||||||
|
use core::mem::size_of;
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::{env, fmt::Write, io, path::PathBuf, process, ptr::NonNull};
|
||||||
|
|
||||||
|
use clap::{builder::Str, Parser};
|
||||||
|
use libafl::{
|
||||||
|
corpus::{Corpus, CorpusId, HasCurrentCorpusId, InMemoryCorpus, InMemoryOnDiskCorpus},
|
||||||
|
events::{SendExiting, SimpleRestartingEventManager},
|
||||||
|
executors::ExitKind,
|
||||||
|
feedbacks::MaxMapFeedback,
|
||||||
|
fuzzer::StdFuzzer,
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
monitors::SimpleMonitor,
|
||||||
|
mutators::{havoc_mutations, HavocScheduledMutator},
|
||||||
|
observers::{ConstMapObserver, HitcountsMapObserver},
|
||||||
|
schedulers::QueueScheduler,
|
||||||
|
stages::{ObserverEqualityFactory, StagesTuple, StdTMinMutationalStage},
|
||||||
|
state::{HasCorpus, StdState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use libafl_bolts::{
|
||||||
|
os::unix_signals::Signal,
|
||||||
|
rands::StdRand,
|
||||||
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
|
tuples::tuple_list,
|
||||||
|
AsSlice, AsSliceMut,
|
||||||
|
};
|
||||||
|
use libafl_qemu::{
|
||||||
|
elf::EasyElf, modules::edges::StdEdgeCoverageChildModule, ArchExtras, Emulator, GuestAddr,
|
||||||
|
GuestReg, MmapPerms, QemuExitError, QemuExitReason, QemuShutdownCause, Regs,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
use libafl_qemu::{modules::SnapshotModule, QemuExecutor};
|
||||||
|
use libafl_targets::{EDGES_MAP_DEFAULT_SIZE, EDGES_MAP_PTR};
|
||||||
|
|
||||||
|
#[cfg(feature = "fork")]
|
||||||
|
compile_error!("'fork' feature is currently not implemented; pending forkserver PR.");
|
||||||
|
|
||||||
|
#[cfg(all(feature = "fork", feature = "snapshot"))]
|
||||||
|
compile_error!("Cannot enable both 'fork' and 'snapshot' features at the same time.");
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Version;
|
||||||
|
|
||||||
|
impl From<Version> for Str {
|
||||||
|
fn from(_: Version) -> Str {
|
||||||
|
let version = [
|
||||||
|
("Architecture:", env!("CPU_TARGET")),
|
||||||
|
("Build Timestamp:", env!("VERGEN_BUILD_TIMESTAMP")),
|
||||||
|
("Describe:", env!("VERGEN_GIT_DESCRIBE")),
|
||||||
|
("Commit SHA:", env!("VERGEN_GIT_SHA")),
|
||||||
|
("Commit Date:", env!("VERGEN_RUSTC_COMMIT_DATE")),
|
||||||
|
("Commit Branch:", env!("VERGEN_GIT_BRANCH")),
|
||||||
|
("Rustc Version:", env!("VERGEN_RUSTC_SEMVER")),
|
||||||
|
("Rustc Channel:", env!("VERGEN_RUSTC_CHANNEL")),
|
||||||
|
("Rustc Host Triple:", env!("VERGEN_RUSTC_HOST_TRIPLE")),
|
||||||
|
("Rustc Commit SHA:", env!("VERGEN_RUSTC_COMMIT_HASH")),
|
||||||
|
("Cargo Target Triple", env!("VERGEN_CARGO_TARGET_TRIPLE")),
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.fold(String::new(), |mut output, (k, v)| {
|
||||||
|
let _ = writeln!(output, "{k:25}: {v}");
|
||||||
|
output
|
||||||
|
});
|
||||||
|
|
||||||
|
format!("\n{version:}").into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
#[clap(author, version, about, long_about = None)]
|
||||||
|
#[command(
|
||||||
|
name = format ! ("qemu_tmin-{}", env ! ("CPU_TARGET")),
|
||||||
|
version = Version::default(),
|
||||||
|
about,
|
||||||
|
long_about = "Module for minimizing test cases using QEMU instrumentation"
|
||||||
|
)]
|
||||||
|
pub struct FuzzerOptions {
|
||||||
|
#[arg(long, help = "Output directory")]
|
||||||
|
output: String,
|
||||||
|
|
||||||
|
#[arg(long, help = "Input directory")]
|
||||||
|
input: String,
|
||||||
|
|
||||||
|
#[arg(long, help = "Timeout in seconds", default_value_t = 1_u64)]
|
||||||
|
timeout: u64,
|
||||||
|
|
||||||
|
#[arg(
|
||||||
|
long,
|
||||||
|
help = "Number of iterations for minimization",
|
||||||
|
default_value_t = 1024_usize
|
||||||
|
)]
|
||||||
|
iterations: usize,
|
||||||
|
|
||||||
|
#[clap(short, long, help = "Enable output from the fuzzer clients")]
|
||||||
|
verbose: bool,
|
||||||
|
|
||||||
|
#[arg(last = true, help = "Arguments passed to the target")]
|
||||||
|
args: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MAX_INPUT_SIZE: usize = 1048576; // 1MB
|
||||||
|
|
||||||
|
pub fn fuzz() -> Result<(), Error> {
|
||||||
|
// Initialise env_logger
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
// Parse user options
|
||||||
|
let mut options = FuzzerOptions::parse();
|
||||||
|
let program = env::args().next().unwrap();
|
||||||
|
log::info!("Program: {program:}");
|
||||||
|
options.args.insert(0, program);
|
||||||
|
log::info!("ARGS: {:#?}", options.args);
|
||||||
|
|
||||||
|
// Get all of the files supplied from the input corpus
|
||||||
|
let corpus_dir = PathBuf::from(options.input);
|
||||||
|
let files = corpus_dir
|
||||||
|
.read_dir()
|
||||||
|
.expect("Failed to read corpus dir")
|
||||||
|
.map(|x| Ok(x?.path()))
|
||||||
|
.collect::<Result<Vec<PathBuf>, io::Error>>()
|
||||||
|
.expect("Failed to read dir entry");
|
||||||
|
|
||||||
|
// Clear LD_LIBRARY_PATH
|
||||||
|
env::remove_var("LD_LIBRARY_PATH");
|
||||||
|
|
||||||
|
// Create a shared memory region for sharing coverage map between fuzzer and target
|
||||||
|
// In snapshot mode, this is only required for the SimpleRestartingEventManager.
|
||||||
|
// However, fork mode requires it to share memory between parent and child,
|
||||||
|
// so we use it in both cases.
|
||||||
|
let mut shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
let mut edges_shmem = shmem_provider.new_shmem(EDGES_MAP_DEFAULT_SIZE).unwrap();
|
||||||
|
let edges = edges_shmem.as_slice_mut();
|
||||||
|
unsafe { EDGES_MAP_PTR = edges.as_mut_ptr() };
|
||||||
|
|
||||||
|
// We use a HitcountsMapObserver to observe the coverage map
|
||||||
|
let mut edges_observer = unsafe {
|
||||||
|
HitcountsMapObserver::new(ConstMapObserver::from_mut_ptr(
|
||||||
|
"edges",
|
||||||
|
NonNull::new(edges.as_mut_ptr())
|
||||||
|
.expect("The edge map pointer is null.")
|
||||||
|
.cast::<[u8; EDGES_MAP_DEFAULT_SIZE]>(),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
let modules = tuple_list!(
|
||||||
|
StdEdgeCoverageChildModule::builder()
|
||||||
|
.const_map_observer(edges_observer.as_mut())
|
||||||
|
.build()?,
|
||||||
|
SnapshotModule::new()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Create our QEMU emulator
|
||||||
|
let emulator = Emulator::empty()
|
||||||
|
.qemu_parameters(options.args)
|
||||||
|
.modules(modules)
|
||||||
|
.build()?;
|
||||||
|
let qemu = emulator.qemu();
|
||||||
|
|
||||||
|
// Use ELF tools to get the target function
|
||||||
|
let mut elf_buffer = Vec::new();
|
||||||
|
let elf = EasyElf::from_file(qemu.binary_path(), &mut elf_buffer).unwrap();
|
||||||
|
let test_one_input_ptr = elf
|
||||||
|
.resolve_symbol("LLVMFuzzerTestOneInput", qemu.load_addr())
|
||||||
|
.expect("Symbol LLVMFuzzerTestOneInput not found");
|
||||||
|
log::info!("LLVMFuzzerTestOneInput @ {test_one_input_ptr:#x}");
|
||||||
|
|
||||||
|
// Run target until the target function, and store important registers.
|
||||||
|
// Set a breakpoint on target function return.
|
||||||
|
qemu.entry_break(test_one_input_ptr);
|
||||||
|
let stack_ptr: GuestAddr = qemu.read_reg(Regs::Sp).unwrap();
|
||||||
|
let ret_addr: GuestAddr = qemu.read_return_address().unwrap();
|
||||||
|
let pc: GuestReg = qemu.read_reg(Regs::Pc).unwrap();
|
||||||
|
log::info!("Break at {pc:#x}");
|
||||||
|
log::info!("Return address = {ret_addr:#x}");
|
||||||
|
qemu.set_breakpoint(ret_addr);
|
||||||
|
|
||||||
|
// Map a private region for input buffer
|
||||||
|
let input_addr = qemu
|
||||||
|
.map_private(0, MAX_INPUT_SIZE, MmapPerms::ReadWrite)
|
||||||
|
.unwrap();
|
||||||
|
log::info!("Placing input at {input_addr:#x}");
|
||||||
|
|
||||||
|
// Rust harness: this closure copies an input buffer to our private region
|
||||||
|
// for target function input and updates registers to a single iteration
|
||||||
|
// before telling QEMU to resume execution.
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
let mut harness =
|
||||||
|
|_emulator: &mut Emulator<_, _, _, _, _, _, _>, _state: &mut _, input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let mut buf = target.as_slice();
|
||||||
|
let mut len = buf.len();
|
||||||
|
if len > MAX_INPUT_SIZE {
|
||||||
|
buf = &buf[0..MAX_INPUT_SIZE];
|
||||||
|
len = MAX_INPUT_SIZE;
|
||||||
|
}
|
||||||
|
let len = len as GuestReg;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
qemu.write_mem(input_addr, buf).expect("qemu write failed.");
|
||||||
|
|
||||||
|
qemu.write_reg(Regs::Pc, test_one_input_ptr).unwrap();
|
||||||
|
qemu.write_reg(Regs::Sp, stack_ptr).unwrap();
|
||||||
|
qemu.write_return_address(ret_addr).unwrap();
|
||||||
|
qemu.write_function_argument(0, input_addr).unwrap();
|
||||||
|
qemu.write_function_argument(1, len).unwrap();
|
||||||
|
|
||||||
|
match qemu.run() {
|
||||||
|
Ok(QemuExitReason::Breakpoint(_)) => {}
|
||||||
|
Ok(QemuExitReason::End(QemuShutdownCause::HostSignal(
|
||||||
|
Signal::SigInterrupt,
|
||||||
|
))) => process::exit(0),
|
||||||
|
Err(QemuExitError::UnexpectedExit) => return ExitKind::Crash,
|
||||||
|
_ => panic!("Unexpected QEMU exit."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set up the most basic monitor possible.
|
||||||
|
let monitor = SimpleMonitor::with_user_monitor(|s| {
|
||||||
|
println!("{s}");
|
||||||
|
});
|
||||||
|
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
|
||||||
|
{
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => match err {
|
||||||
|
Error::ShuttingDown => {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Failed to setup the restarter: {err}");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Our fuzzer is a simple queue scheduler (FIFO), and has no corpus feedback
|
||||||
|
// or objective feedback. This is important as we need the MaxMapFeedback
|
||||||
|
// on the observer to be constrained by ObserverEqualityFactory which will
|
||||||
|
// ensure interestingness is only true for identical coverage.
|
||||||
|
let scheduler = QueueScheduler::new();
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, (), ());
|
||||||
|
|
||||||
|
// We define the stages that will be performed by the fuzzer. We have one
|
||||||
|
// stage of the StdTMinMutationalStage which will run for n iterations.
|
||||||
|
// The havoc mutator will generate mutations; only those shorter than the
|
||||||
|
// current input will be tested; and the ObservreEqualityFactory will
|
||||||
|
// provide an observer that ensures additions to the corpus have the same
|
||||||
|
// coverage.
|
||||||
|
let minimizer = HavocScheduledMutator::new(havoc_mutations());
|
||||||
|
let factory = ObserverEqualityFactory::new(&edges_observer);
|
||||||
|
let mut stages = tuple_list!(StdTMinMutationalStage::new(
|
||||||
|
minimizer,
|
||||||
|
factory,
|
||||||
|
options.iterations
|
||||||
|
),);
|
||||||
|
|
||||||
|
// Create a state instance. Unlike a typical fuzzer, we start with an empty
|
||||||
|
// input corpus, and we don't care about 'solutions' so store in an
|
||||||
|
// InMemoryCorpus.
|
||||||
|
let mut feedback = MaxMapFeedback::new(&edges_observer);
|
||||||
|
let mut objective = ();
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
StdState::new(
|
||||||
|
StdRand::new(),
|
||||||
|
InMemoryOnDiskCorpus::new(PathBuf::from(options.output)).unwrap(),
|
||||||
|
InMemoryCorpus::new(),
|
||||||
|
&mut feedback,
|
||||||
|
&mut objective,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
// The executor. Nothing exciting here.
|
||||||
|
#[cfg(feature = "snapshot")]
|
||||||
|
let mut executor = QemuExecutor::new(
|
||||||
|
emulator,
|
||||||
|
&mut harness,
|
||||||
|
tuple_list!(edges_observer),
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
Duration::from_millis(5000),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Load the input corpus
|
||||||
|
state.load_initial_inputs_by_filenames_forced(&mut fuzzer, &mut executor, &mut mgr, &files)?;
|
||||||
|
log::info!("Processed {} inputs from disk.", files.len());
|
||||||
|
|
||||||
|
// Iterate over initial corpus_ids and minimize each.
|
||||||
|
let corpus_ids: Vec<CorpusId> = state.corpus().ids().collect();
|
||||||
|
for corpus_id in corpus_ids {
|
||||||
|
state.set_corpus_id(corpus_id)?;
|
||||||
|
stages.perform_all(&mut fuzzer, &mut executor, &mut state, &mut mgr)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We end up with equivalent output corpus, hopefully smaller than the
|
||||||
|
// input, but certainly no larger.
|
||||||
|
let size = state.corpus().count();
|
||||||
|
println!("Corpus size: {size}");
|
||||||
|
|
||||||
|
mgr.send_exiting()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn main() {
|
||||||
|
fuzz().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn main() {
|
||||||
|
panic!("qemu-user and libafl_qemu is only supported on linux!");
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
//! A libfuzzer-like fuzzer using Nyx for binary-only coverage
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod client;
|
mod client;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
//! A binary-only systemmode fuzzer using qemu for binary-only coverage
|
||||||
#[cfg(all(target_os = "linux", feature = "low_level"))]
|
#[cfg(all(target_os = "linux", feature = "low_level"))]
|
||||||
mod fuzzer_low_level;
|
mod fuzzer_low_level;
|
||||||
|
|
||||||
|
@ -574,7 +574,7 @@ where
|
|||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(e) => log::error!("Failed to send tcp message {e:#?}"),
|
Err(e) => log::error!("Failed to send tcp message {e:#?}"),
|
||||||
}
|
}
|
||||||
log::debug!("Asking he broker to be disconnected");
|
log::debug!("Asking the broker to be disconnected");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* 1.87.0-nightly */
|
/* 1.88.0-nightly */
|
||||||
/* qemu git hash: 97bef506eed24ee8d0eda4a07c4419c55dae4acb */
|
/* qemu git hash: 97bef506eed24ee8d0eda4a07c4419c55dae4acb */
|
||||||
/* automatically generated by rust-bindgen 0.71.1 */
|
/* automatically generated by rust-bindgen 0.71.1 */
|
||||||
|
|
||||||
@ -305,6 +305,7 @@ impl Default for __pthread_mutex_s {
|
|||||||
pub struct __pthread_cond_s {
|
pub struct __pthread_cond_s {
|
||||||
pub __wseq: __atomic_wide_counter,
|
pub __wseq: __atomic_wide_counter,
|
||||||
pub __g1_start: __atomic_wide_counter,
|
pub __g1_start: __atomic_wide_counter,
|
||||||
|
pub __g_refs: [::std::os::raw::c_uint; 2usize],
|
||||||
pub __g_size: [::std::os::raw::c_uint; 2usize],
|
pub __g_size: [::std::os::raw::c_uint; 2usize],
|
||||||
pub __g1_orig_size: ::std::os::raw::c_uint,
|
pub __g1_orig_size: ::std::os::raw::c_uint,
|
||||||
pub __wrefs: ::std::os::raw::c_uint,
|
pub __wrefs: ::std::os::raw::c_uint,
|
||||||
@ -312,20 +313,22 @@ pub struct __pthread_cond_s {
|
|||||||
}
|
}
|
||||||
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
|
#[allow(clippy::unnecessary_operation, clippy::identity_op)]
|
||||||
const _: () = {
|
const _: () = {
|
||||||
["Size of __pthread_cond_s"][::std::mem::size_of::<__pthread_cond_s>() - 40usize];
|
["Size of __pthread_cond_s"][::std::mem::size_of::<__pthread_cond_s>() - 48usize];
|
||||||
["Alignment of __pthread_cond_s"][::std::mem::align_of::<__pthread_cond_s>() - 8usize];
|
["Alignment of __pthread_cond_s"][::std::mem::align_of::<__pthread_cond_s>() - 8usize];
|
||||||
["Offset of field: __pthread_cond_s::__wseq"]
|
["Offset of field: __pthread_cond_s::__wseq"]
|
||||||
[::std::mem::offset_of!(__pthread_cond_s, __wseq) - 0usize];
|
[::std::mem::offset_of!(__pthread_cond_s, __wseq) - 0usize];
|
||||||
["Offset of field: __pthread_cond_s::__g1_start"]
|
["Offset of field: __pthread_cond_s::__g1_start"]
|
||||||
[::std::mem::offset_of!(__pthread_cond_s, __g1_start) - 8usize];
|
[::std::mem::offset_of!(__pthread_cond_s, __g1_start) - 8usize];
|
||||||
|
["Offset of field: __pthread_cond_s::__g_refs"]
|
||||||
|
[::std::mem::offset_of!(__pthread_cond_s, __g_refs) - 16usize];
|
||||||
["Offset of field: __pthread_cond_s::__g_size"]
|
["Offset of field: __pthread_cond_s::__g_size"]
|
||||||
[::std::mem::offset_of!(__pthread_cond_s, __g_size) - 16usize];
|
[::std::mem::offset_of!(__pthread_cond_s, __g_size) - 24usize];
|
||||||
["Offset of field: __pthread_cond_s::__g1_orig_size"]
|
["Offset of field: __pthread_cond_s::__g1_orig_size"]
|
||||||
[::std::mem::offset_of!(__pthread_cond_s, __g1_orig_size) - 24usize];
|
[::std::mem::offset_of!(__pthread_cond_s, __g1_orig_size) - 32usize];
|
||||||
["Offset of field: __pthread_cond_s::__wrefs"]
|
["Offset of field: __pthread_cond_s::__wrefs"]
|
||||||
[::std::mem::offset_of!(__pthread_cond_s, __wrefs) - 28usize];
|
[::std::mem::offset_of!(__pthread_cond_s, __wrefs) - 36usize];
|
||||||
["Offset of field: __pthread_cond_s::__g_signals"]
|
["Offset of field: __pthread_cond_s::__g_signals"]
|
||||||
[::std::mem::offset_of!(__pthread_cond_s, __g_signals) - 32usize];
|
[::std::mem::offset_of!(__pthread_cond_s, __g_signals) - 40usize];
|
||||||
};
|
};
|
||||||
impl Default for __pthread_cond_s {
|
impl Default for __pthread_cond_s {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -340,9 +343,10 @@ impl ::std::fmt::Debug for __pthread_cond_s {
|
|||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"__pthread_cond_s {{ __wseq: {:?}, __g1_start: {:?}, __g_size: {:?}, __g1_orig_size: {:?}, __wrefs: {:?}, __g_signals: {:?} }}",
|
"__pthread_cond_s {{ __wseq: {:?}, __g1_start: {:?}, __g_refs: {:?}, __g_size: {:?}, __g1_orig_size: {:?}, __wrefs: {:?}, __g_signals: {:?} }}",
|
||||||
self.__wseq,
|
self.__wseq,
|
||||||
self.__g1_start,
|
self.__g1_start,
|
||||||
|
self.__g_refs,
|
||||||
self.__g_size,
|
self.__g_size,
|
||||||
self.__g1_orig_size,
|
self.__g1_orig_size,
|
||||||
self.__wrefs,
|
self.__wrefs,
|
||||||
@ -450,9 +454,7 @@ pub struct _IO_FILE {
|
|||||||
pub _markers: *mut _IO_marker,
|
pub _markers: *mut _IO_marker,
|
||||||
pub _chain: *mut _IO_FILE,
|
pub _chain: *mut _IO_FILE,
|
||||||
pub _fileno: ::std::os::raw::c_int,
|
pub _fileno: ::std::os::raw::c_int,
|
||||||
pub _bitfield_align_1: [u32; 0],
|
pub _flags2: ::std::os::raw::c_int,
|
||||||
pub _bitfield_1: __BindgenBitfieldUnit<[u8; 3usize]>,
|
|
||||||
pub _short_backupbuf: [::std::os::raw::c_char; 1usize],
|
|
||||||
pub _old_offset: __off_t,
|
pub _old_offset: __off_t,
|
||||||
pub _cur_column: ::std::os::raw::c_ushort,
|
pub _cur_column: ::std::os::raw::c_ushort,
|
||||||
pub _vtable_offset: ::std::os::raw::c_schar,
|
pub _vtable_offset: ::std::os::raw::c_schar,
|
||||||
@ -463,7 +465,7 @@ pub struct _IO_FILE {
|
|||||||
pub _wide_data: *mut _IO_wide_data,
|
pub _wide_data: *mut _IO_wide_data,
|
||||||
pub _freeres_list: *mut _IO_FILE,
|
pub _freeres_list: *mut _IO_FILE,
|
||||||
pub _freeres_buf: *mut ::std::os::raw::c_void,
|
pub _freeres_buf: *mut ::std::os::raw::c_void,
|
||||||
pub _prevchain: *mut *mut _IO_FILE,
|
pub __pad5: usize,
|
||||||
pub _mode: ::std::os::raw::c_int,
|
pub _mode: ::std::os::raw::c_int,
|
||||||
pub _unused2: [::std::os::raw::c_char; 20usize],
|
pub _unused2: [::std::os::raw::c_char; 20usize],
|
||||||
}
|
}
|
||||||
@ -497,8 +499,7 @@ const _: () = {
|
|||||||
["Offset of field: _IO_FILE::_markers"][::std::mem::offset_of!(_IO_FILE, _markers) - 96usize];
|
["Offset of field: _IO_FILE::_markers"][::std::mem::offset_of!(_IO_FILE, _markers) - 96usize];
|
||||||
["Offset of field: _IO_FILE::_chain"][::std::mem::offset_of!(_IO_FILE, _chain) - 104usize];
|
["Offset of field: _IO_FILE::_chain"][::std::mem::offset_of!(_IO_FILE, _chain) - 104usize];
|
||||||
["Offset of field: _IO_FILE::_fileno"][::std::mem::offset_of!(_IO_FILE, _fileno) - 112usize];
|
["Offset of field: _IO_FILE::_fileno"][::std::mem::offset_of!(_IO_FILE, _fileno) - 112usize];
|
||||||
["Offset of field: _IO_FILE::_short_backupbuf"]
|
["Offset of field: _IO_FILE::_flags2"][::std::mem::offset_of!(_IO_FILE, _flags2) - 116usize];
|
||||||
[::std::mem::offset_of!(_IO_FILE, _short_backupbuf) - 119usize];
|
|
||||||
["Offset of field: _IO_FILE::_old_offset"]
|
["Offset of field: _IO_FILE::_old_offset"]
|
||||||
[::std::mem::offset_of!(_IO_FILE, _old_offset) - 120usize];
|
[::std::mem::offset_of!(_IO_FILE, _old_offset) - 120usize];
|
||||||
["Offset of field: _IO_FILE::_cur_column"]
|
["Offset of field: _IO_FILE::_cur_column"]
|
||||||
@ -516,8 +517,7 @@ const _: () = {
|
|||||||
[::std::mem::offset_of!(_IO_FILE, _freeres_list) - 168usize];
|
[::std::mem::offset_of!(_IO_FILE, _freeres_list) - 168usize];
|
||||||
["Offset of field: _IO_FILE::_freeres_buf"]
|
["Offset of field: _IO_FILE::_freeres_buf"]
|
||||||
[::std::mem::offset_of!(_IO_FILE, _freeres_buf) - 176usize];
|
[::std::mem::offset_of!(_IO_FILE, _freeres_buf) - 176usize];
|
||||||
["Offset of field: _IO_FILE::_prevchain"]
|
["Offset of field: _IO_FILE::__pad5"][::std::mem::offset_of!(_IO_FILE, __pad5) - 184usize];
|
||||||
[::std::mem::offset_of!(_IO_FILE, _prevchain) - 184usize];
|
|
||||||
["Offset of field: _IO_FILE::_mode"][::std::mem::offset_of!(_IO_FILE, _mode) - 192usize];
|
["Offset of field: _IO_FILE::_mode"][::std::mem::offset_of!(_IO_FILE, _mode) - 192usize];
|
||||||
["Offset of field: _IO_FILE::_unused2"][::std::mem::offset_of!(_IO_FILE, _unused2) - 196usize];
|
["Offset of field: _IO_FILE::_unused2"][::std::mem::offset_of!(_IO_FILE, _unused2) - 196usize];
|
||||||
};
|
};
|
||||||
@ -530,50 +530,6 @@ impl Default for _IO_FILE {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl _IO_FILE {
|
|
||||||
#[inline]
|
|
||||||
pub fn _flags2(&self) -> ::std::os::raw::c_int {
|
|
||||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 24u8) as u32) }
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn set__flags2(&mut self, val: ::std::os::raw::c_int) {
|
|
||||||
unsafe {
|
|
||||||
let val: u32 = ::std::mem::transmute(val);
|
|
||||||
self._bitfield_1.set(0usize, 24u8, val as u64)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn _flags2_raw(this: *const Self) -> ::std::os::raw::c_int {
|
|
||||||
unsafe {
|
|
||||||
::std::mem::transmute(<__BindgenBitfieldUnit<[u8; 3usize]>>::raw_get(
|
|
||||||
::std::ptr::addr_of!((*this)._bitfield_1),
|
|
||||||
0usize,
|
|
||||||
24u8,
|
|
||||||
) as u32)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub unsafe fn set__flags2_raw(this: *mut Self, val: ::std::os::raw::c_int) {
|
|
||||||
unsafe {
|
|
||||||
let val: u32 = ::std::mem::transmute(val);
|
|
||||||
<__BindgenBitfieldUnit<[u8; 3usize]>>::raw_set(
|
|
||||||
::std::ptr::addr_of_mut!((*this)._bitfield_1),
|
|
||||||
0usize,
|
|
||||||
24u8,
|
|
||||||
val as u64,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
pub fn new_bitfield_1(_flags2: ::std::os::raw::c_int) -> __BindgenBitfieldUnit<[u8; 3usize]> {
|
|
||||||
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 3usize]> = Default::default();
|
|
||||||
__bindgen_bitfield_unit.set(0usize, 24u8, {
|
|
||||||
let _flags2: u32 = unsafe { ::std::mem::transmute(_flags2) };
|
|
||||||
_flags2 as u64
|
|
||||||
});
|
|
||||||
__bindgen_bitfield_unit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub type __jmp_buf = [::std::os::raw::c_long; 8usize];
|
pub type __jmp_buf = [::std::os::raw::c_long; 8usize];
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Default, Copy, Clone)]
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
|
@ -661,19 +661,17 @@ impl Default for _IO_FILE {
|
|||||||
impl _IO_FILE {
|
impl _IO_FILE {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn _flags2(&self) -> ::std::os::raw::c_int {
|
pub fn _flags2(&self) -> ::std::os::raw::c_int {
|
||||||
unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 24u8) as u32) }
|
u32::cast_signed(self._bitfield_1.get(0usize, 24u8) as u32)
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn set__flags2(&mut self, val: ::std::os::raw::c_int) {
|
pub fn set__flags2(&mut self, val: ::std::os::raw::c_int) {
|
||||||
unsafe {
|
let val: u32 = i32::cast_unsigned(val);
|
||||||
let val: u32 = ::std::mem::transmute(val);
|
self._bitfield_1.set(0usize, 24u8, val as u64)
|
||||||
self._bitfield_1.set(0usize, 24u8, val as u64)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn _flags2_raw(this: *const Self) -> ::std::os::raw::c_int {
|
pub unsafe fn _flags2_raw(this: *const Self) -> ::std::os::raw::c_int {
|
||||||
unsafe {
|
unsafe {
|
||||||
::std::mem::transmute(<__BindgenBitfieldUnit<[u8; 3usize]>>::raw_get(
|
u32::cast_signed(<__BindgenBitfieldUnit<[u8; 3usize]>>::raw_get(
|
||||||
::std::ptr::addr_of!((*this)._bitfield_1),
|
::std::ptr::addr_of!((*this)._bitfield_1),
|
||||||
0usize,
|
0usize,
|
||||||
24u8,
|
24u8,
|
||||||
@ -683,7 +681,7 @@ impl _IO_FILE {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn set__flags2_raw(this: *mut Self, val: ::std::os::raw::c_int) {
|
pub unsafe fn set__flags2_raw(this: *mut Self, val: ::std::os::raw::c_int) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let val: u32 = ::std::mem::transmute(val);
|
let val: u32 = i32::cast_unsigned(val);
|
||||||
<__BindgenBitfieldUnit<[u8; 3usize]>>::raw_set(
|
<__BindgenBitfieldUnit<[u8; 3usize]>>::raw_set(
|
||||||
::std::ptr::addr_of_mut!((*this)._bitfield_1),
|
::std::ptr::addr_of_mut!((*this)._bitfield_1),
|
||||||
0usize,
|
0usize,
|
||||||
@ -696,7 +694,7 @@ impl _IO_FILE {
|
|||||||
pub fn new_bitfield_1(_flags2: ::std::os::raw::c_int) -> __BindgenBitfieldUnit<[u8; 3usize]> {
|
pub fn new_bitfield_1(_flags2: ::std::os::raw::c_int) -> __BindgenBitfieldUnit<[u8; 3usize]> {
|
||||||
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 3usize]> = Default::default();
|
let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 3usize]> = Default::default();
|
||||||
__bindgen_bitfield_unit.set(0usize, 24u8, {
|
__bindgen_bitfield_unit.set(0usize, 24u8, {
|
||||||
let _flags2: u32 = unsafe { ::std::mem::transmute(_flags2) };
|
let _flags2: u32 = i32::cast_unsigned(_flags2);
|
||||||
_flags2 as u64
|
_flags2 as u64
|
||||||
});
|
});
|
||||||
__bindgen_bitfield_unit
|
__bindgen_bitfield_unit
|
||||||
|
@ -40,7 +40,7 @@ pub fn debug_print(emu: &Unicorn<()>, thumb_mode: bool) {
|
|||||||
|
|
||||||
let pc = emu.pc_read().unwrap();
|
let pc = emu.pc_read().unwrap();
|
||||||
|
|
||||||
log::debug!("PC: {:X}", pc);
|
log::debug!("PC: {pc:X}");
|
||||||
let arch = emu.get_arch();
|
let arch = emu.get_arch();
|
||||||
match arch {
|
match arch {
|
||||||
Arch::ARM => {
|
Arch::ARM => {
|
||||||
@ -132,7 +132,7 @@ pub fn debug_print(emu: &Unicorn<()>, thumb_mode: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i in insns.as_ref() {
|
for i in insns.as_ref() {
|
||||||
log::debug!("{}", i);
|
log::debug!("{i}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user