diff --git a/fuzzers/libfuzzer_libpng/Cargo.toml b/fuzzers/libfuzzer_libpng/Cargo.toml index b4892d868c..a6dab10d39 100644 --- a/fuzzers/libfuzzer_libpng/Cargo.toml +++ b/fuzzers/libfuzzer_libpng/Cargo.toml @@ -18,13 +18,10 @@ debug = true [dependencies] libafl = { path = "../../libafl/" } +libafl_targets = { path = "../../libafl_targets/" } # TODO Include it only when building cc libafl_cc = { path = "../../libafl_cc/" } [lib] name = "libfuzzer_libpng" -path = "./src/fuzzer.rs" -test = false -bench = false crate-type = ["staticlib"] - diff --git a/fuzzers/libfuzzer_libpng/harness.cc b/fuzzers/libfuzzer_libpng/harness.cc new file mode 100644 index 0000000000..65faff685d --- /dev/null +++ b/fuzzers/libfuzzer_libpng/harness.cc @@ -0,0 +1,197 @@ +// 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 " 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 +#include +#include + +#include + +#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(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 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 + #ifdef __aarch64__ + 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_handler.row_ptr), nullptr); + } + } + + png_read_end(png_handler.png_ptr, png_handler.end_info_ptr); + + PNG_CLEANUP + return 0; +} + diff --git a/fuzzers/libfuzzer_libpng/src/fuzzer.rs b/fuzzers/libfuzzer_libpng/src/lib.rs similarity index 87% rename from fuzzers/libfuzzer_libpng/src/fuzzer.rs rename to fuzzers/libfuzzer_libpng/src/lib.rs index 163a4c43ed..8f7c53a243 100644 --- a/fuzzers/libfuzzer_libpng/src/fuzzer.rs +++ b/fuzzers/libfuzzer_libpng/src/lib.rs @@ -5,7 +5,6 @@ use core::time::Duration; use std::{env, path::PathBuf}; -#[cfg(unix)] use libafl::{ bolts::{shmem::UnixShMem, tuples::tuple_list}, corpus::{ @@ -25,21 +24,10 @@ use libafl::{ Error, }; -/// We will interact with a C++ target, so use external c functionality -#[cfg(unix)] -extern "C" { - /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) - fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; +use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM}; - // afl_libfuzzer_init calls LLVMFUzzerInitialize() - fn afl_libfuzzer_init() -> i32; - - static __lafl_edges_map: *mut u8; - static __lafl_cmp_map: *mut u8; - static __lafl_max_edges_size: u32; -} - -/// The main fn, usually parsing parameters, and starting the fuzzer +/// The main fn, no_mangle as it is a C main +#[no_mangle] pub fn main() { // Registry the metadata types used in this fuzzer // Needed only on no_std @@ -85,7 +73,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // Create an observation channel using the coverage map let edges_observer = HitcountsMapObserver::new(unsafe { - StdMapObserver::new_from_ptr("edges", __lafl_edges_map, __lafl_max_edges_size as usize) + StdMapObserver::new("edges", &mut EDGES_MAP, MAX_EDGES_NUM) }); // If not restarting, create a State from scratch @@ -131,7 +119,7 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The wrapped harness function, calling out to the LLVM-style harness let mut harness = |buf: &[u8]| { - unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) }; + libfuzzer_test_one_input(buf); ExitKind::Ok }; @@ -150,10 +138,8 @@ fn fuzz(corpus_dirs: Vec, objective_dir: PathBuf, broker_port: u16) -> // The actual target run starts here. // Call LLVMFUzzerInitialize() if present. - unsafe { - if afl_libfuzzer_init() == -1 { - println!("Warning: LLVMFuzzerInitialize failed with -1") - } + if libfuzzer_initialize() == -1 { + println!("Warning: LLVMFuzzerInitialize failed with -1") } // In case the corpus is empty (on first run), reset diff --git a/libafl/src/observers/map.rs b/libafl/src/observers/map.rs index 85d5d2d6be..d1e8f13a80 100644 --- a/libafl/src/observers/map.rs +++ b/libafl/src/observers/map.rs @@ -120,10 +120,11 @@ where T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, { /// Creates a new MapObserver - pub fn new(name: &'static str, map: &'static mut [T]) -> Self { + pub fn new(name: &'static str, map: &'static mut [T], len: usize) -> Self { + assert!(map.len() >= len); let initial = if map.is_empty() { T::default() } else { map[0] }; Self { - map: ArrayMut::Cptr((map.as_mut_ptr(), map.len())), + map: ArrayMut::Cptr((map.as_mut_ptr(), len)), name: name.to_string(), initial, } diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index 78dcf55923..6f4232f69c 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -1,5 +1,12 @@ #[cfg(feature = "sancov")] pub mod sancov; +#[cfg(feature = "sancov")] +pub use sancov::*; + +#[cfg(feature = "libfuzzer_compatibility")] +pub mod libfuzzer_compatibility; +#[cfg(feature = "libfuzzer_compatibility")] +pub use libfuzzer_compatibility::*; #[cfg(test)] mod tests { diff --git a/libafl_targets/src/libfuzzer_compatibility.rs b/libafl_targets/src/libfuzzer_compatibility.rs new file mode 100644 index 0000000000..a4a29fd362 --- /dev/null +++ b/libafl_targets/src/libfuzzer_compatibility.rs @@ -0,0 +1,16 @@ +/// We will interact with a C++ target, so use external c functionality +extern "C" { + /// int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) + fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32; + + // libafl_targets_libfuzzer_init calls LLVMFUzzerInitialize() + fn libafl_targets_libfuzzer_init() -> i32; +} + +pub fn libfuzzer_initialize() -> i32 { + unsafe { libafl_targets_libfuzzer_init() } +} + +pub fn libfuzzer_test_one_input(buf: &[u8]) -> i32 { + unsafe { LLVMFuzzerTestOneInput(buf.as_ptr(), buf.len()) } +}