Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qemu arm launcher #708

Merged
merged 19 commits into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ vendor
.env

*.tmp
*.swp
*.o
*.a
*.so
Expand Down
5 changes: 5 additions & 0 deletions fuzzers/qemu_arm_launcher/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
libpng-*
libpng_harness
zlib-*
crashes
target
19 changes: 19 additions & 0 deletions fuzzers/qemu_arm_launcher/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "qemu_arm_launcher"
version = "0.8.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"

[features]
default = ["std"]
std = []

[profile.release]
#lto = true
#codegen-units = 1
#opt-level = 3
debug = true

[dependencies]
libafl = { path = "../../libafl/" }
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm"] }
133 changes: 133 additions & 0 deletions fuzzers/qemu_arm_launcher/Makefile.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Variables
[env]
FUZZER_NAME='libpng_harness'
PROJECT_DIR = { script = ["pwd"] }
CROSS_CC = "arm-linux-gnueabi-gcc"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably try to patch build.rs of libafl_qemu to figure this binary out by itself

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that hardcoding the CROSS_CC is not a good idea. The problem is that this fuzzer needs a CROSS_CC to function. Figuring out which cross_cc is installed is hard and also systems might have multiple cross_cc installed.

Especially for the github workflows it needs to be set.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the scope of this PR it's more than fine, I was thinking in general

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, that would be great. I also thought about it, but there are just so many cross_cc. Already for arm on ubuntu using apt you might have arm-none-eabi-gcc, arm-linux-gnueabi-gcc and arm-linux-gnueabihf-gcc installed. Not even speaking about other targets then arm32 or manually installed cross_cc. I think it is impossible to pick the correct one for the user or do you have any idea on how to?


[tasks.unsupported]
script_runner="@shell"
script='''
echo "Qemu fuzzer not supported on windows"
'''

#zlib
[tasks.zlib]
linux_alias = "zlib_unix"
mac_alias = "zlib_unix"
windows_alias = "unsupported"

[tasks.zlib_unix_wget]
condition = { files_not_exist = [ "./zlib-1.2.12" ] }
script_runner="@shell"
script='''
wget http://www.zlib.net/zlib-1.2.12.tar.gz
tar -xvf zlib-1.2.12.tar.gz
'''

[tasks.zlib_unix]
condition = { files_not_exist = [ "./zlib-1.2.12/zlib/lib/libz.a" ] }
script_runner="@shell"
script='''
cd zlib-1.2.12 && CC=arm-linux-gnueabi-gcc AR=arm-linux-gnueabi-ar ./configure --prefix=./zlib
make install
'''
dependencies = [ "zlib_unix_wget" ]

# libpng
[tasks.libpng]
linux_alias = "libpng_unix"
mac_alias = "libpng_unix"
windows_alias = "unsupported"

[tasks.libpng_unix_wget]
condition = { files_not_exist = [ "./libpng-1.6.37" ] }
script_runner="@shell"
script='''
wget https://deac-fra.dl.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
tar -xvf libpng-1.6.37.tar.xz
'''

[tasks.libpng_unix]
condition = { files_not_exist = [ "./libpng-1.6.37/.libs/libpng16.a" ] }
script_runner="@shell"
script='''
cd libpng-1.6.37 && CC=arm-linux-gnueabi-gcc CFLAGS=-I../zlib-1.2.12/zlib/lib LDFLAGS=-L../zlib-1.2.12/zlib/lib ./configure --enable-shared=no --with-pic=yes --enable-hardware-optimizations=yes --host=arm
make
'''
dependencies = [ "zlib", "libpng_unix_wget" ]

# fuzzer
[tasks.fuzzer]
linux_alias = "fuzzer_unix"
mac_alias = "fuzzer_unix"
windows_alias = "unsupported"

[tasks.fuzzer_unix]
command = "cargo"
args = ["build", "--release"]

# Harness
[tasks.harness]
linux_alias = "harness_unix"
mac_alias = "harness_unix"
windows_alias = "unsupported"

[tasks.harness_unix]
script_runner="@shell"
script='''
# Build the libpng harness
arm-linux-gnueabi-g++ \
./harness.cc \
./libpng-1.6.37/.libs/libpng16.a \
./zlib-1.2.12/zlib/lib/libz.a \
-I./libpng-1.6.37/ \
-I../zlib-1.2.12/zlib/lib \
-L../zlib-1.2.12/zlib/lib \
-o ${FUZZER_NAME} \
-lm \
-static
'''
dependencies = [ "libpng" ]

# Run the fuzzer
[tasks.run]
linux_alias = "run_unix"
mac_alias = "run_unix"
windows_alias = "unsupported"

[tasks.run_unix]
command = "cargo"
args = ["run", "--release", "./${FUZZER_NAME}"]
dependencies = [ "harness", "fuzzer" ]

# Run the fuzzer
[tasks.test]
linux_alias = "test_unix"
mac_alias = "test_unix"
windows_alias = "unsupported"

# Short test
[tasks.test_unix]
script_runner = "@shell"
script='''
rm -rf libafl_unix_shmem_server || true
timeout 11s cargo run --release ./${FUZZER_NAME} 2>/dev/null &
'''
dependencies = [ "harness", "fuzzer" ]

# Clean up
[tasks.clean]
linux_alias = "clean_unix"
mac_alias = "clean_unix"
windows_alias = "unsupported"

[tasks.clean_unix]
# Disable default `clean` definition
clear = true
script_runner="@shell"
script='''
rm -f ./${FUZZER_NAME}
rm -rf zlib-*
rm -rf libpng-*
cargo clean
'''
18 changes: 18 additions & 0 deletions fuzzers/qemu_arm_launcher/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# LibAFL with launcher for libpng with qemu arm in usermode

This folder contains an example fuzzer for libpng using the qemu emulator in arm usermode.
To show off crash detection, we added a `ud2` instruction to the harness, edit harness.cc if you want a non-crashing example.
It has been tested on Linux.

In contrast to the normal libfuzzer libpng example, this uses the `launcher` feature, that automatically spawns `n` child processes, and binds them to a free core.

## Prerequisites
```bash
sudo apt install arm-linux-gnueabi-gcc g++-arm-linux-gnueabi
```

## Run

```bash
cargo make run
```
Binary file added fuzzers/qemu_arm_launcher/corpus/not_kitty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fuzzers/qemu_arm_launcher/corpus/not_kitty_alpha.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fuzzers/qemu_arm_launcher/corpus/not_kitty_gamma.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fuzzers/qemu_arm_launcher/corpus/not_kitty_icc.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
193 changes: 193 additions & 0 deletions fuzzers/qemu_arm_launcher/harness.cc
Original file line number Diff line number Diff line change
@@ -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
#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_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);
}
Loading