mirror of https://github.com/google/oss-fuzz.git
Adding libpng-proto, an example of proto-based fuzzer (#2048)
* Adding libpng-proto, an example of proto-based fuzzer * fix year * remove redundant line * address comments * simplify names * small update in build.sh
This commit is contained in:
parent
8858ee1de6
commit
e47326293d
|
@ -0,0 +1,25 @@
|
|||
# Copyright 2018 Google Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
################################################################################
|
||||
|
||||
FROM gcr.io/oss-fuzz-base/base-builder
|
||||
MAINTAINER kcc@google.com
|
||||
RUN apt-get update && \
|
||||
apt-get install -y make autoconf automake libtool zlib1g-dev ninja-build cmake
|
||||
|
||||
RUN git clone --depth 1 https://github.com/glennrp/libpng.git
|
||||
RUN git clone --depth 1 https://github.com/google/libprotobuf-mutator.git
|
||||
RUN (mkdir LPM && cd LPM && cmake ../libprotobuf-mutator -GNinja -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON -DLIB_PROTO_MUTATOR_TESTING=OFF && ninja)
|
||||
COPY build.sh png_fuzz_proto.proto png_proto_fuzzer_example.cc $SRC/
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash
|
||||
|
||||
# build libpng using the upstream-provided build.sh.
|
||||
# it will also build the vanilla (non-proto) fuzz target,
|
||||
# but we discard it.
|
||||
(cd libpng/ && contrib/oss-fuzz/build.sh && rm $OUT/*)
|
||||
|
||||
# Compile png_fuzz_proto.proto; should produce two files in genfiles/:
|
||||
# png_fuzz_proto.pb.cc png_fuzz_proto.pb.h
|
||||
rm -rf genfiles && mkdir genfiles && LPM/external.protobuf/bin/protoc png_fuzz_proto.proto --cpp_out=genfiles
|
||||
|
||||
# compile the upstream-provided vanilla fuzz target
|
||||
# but replace LLVMFuzzerTestOneInput with FuzzPNG so that
|
||||
# png_proto_fuzzer_example.cc can call FuzzPNG from its own
|
||||
# LLVMFuzzerTestOneInput.
|
||||
$CXX $CXXFLAGS -c -DLLVMFuzzerTestOneInput=FuzzPNG libpng/contrib/oss-fuzz/libpng_read_fuzzer.cc -I libpng
|
||||
|
||||
# compile & link the rest
|
||||
$CXX $CXXFLAGS png_proto_fuzzer_example.cc libpng_read_fuzzer.o genfiles/png_fuzz_proto.pb.cc \
|
||||
-I genfiles -I. -I libprotobuf-mutator/ -I LPM/external.protobuf/include \
|
||||
-lz \
|
||||
LPM/src/libfuzzer/libprotobuf-mutator-libfuzzer.a \
|
||||
LPM/src/libprotobuf-mutator.a \
|
||||
LPM/external.protobuf/lib/libprotobuf.a \
|
||||
libpng/.libs/libpng16.a \
|
||||
$LIB_FUZZING_ENGINE \
|
||||
-o $OUT/png_proto_fuzzer_example
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
syntax = "proto2";
|
||||
// Very simple proto description of the PNG format,
|
||||
// described at https://en.wikipedia.org/wiki/Portable_Network_Graphics
|
||||
|
||||
message IHDR {
|
||||
required uint32 width = 1;
|
||||
required uint32 height = 2;
|
||||
required uint32 other1 = 3;
|
||||
required uint32 other2 = 4; // Only 1 byte used.
|
||||
}
|
||||
|
||||
message PLTE {
|
||||
required bytes data = 1;
|
||||
}
|
||||
|
||||
message IDAT {
|
||||
required bytes data = 1;
|
||||
}
|
||||
|
||||
message OtherChunk {
|
||||
oneof type {
|
||||
uint32 known_type = 1;
|
||||
uint32 unknown_type = 2;
|
||||
}
|
||||
required bytes data = 3;
|
||||
}
|
||||
|
||||
message PngChunk {
|
||||
oneof chunk {
|
||||
PLTE plte = 1;
|
||||
IDAT idat = 2;
|
||||
OtherChunk other_chunk = 10000;
|
||||
}
|
||||
}
|
||||
|
||||
message PngProto {
|
||||
required IHDR ihdr = 1;
|
||||
repeated PngChunk chunks = 2;
|
||||
}
|
||||
|
||||
// package fuzzer_examples;
|
|
@ -0,0 +1,98 @@
|
|||
// Example fuzzer for PNG using protos.
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <zlib.h> // for crc32
|
||||
|
||||
#include "libprotobuf-mutator/src/libfuzzer/libfuzzer_macro.h"
|
||||
#include "png_fuzz_proto.pb.h"
|
||||
|
||||
static void WriteInt(std::stringstream &out, uint32_t x) {
|
||||
x = __builtin_bswap32(x);
|
||||
out.write((char *)&x, sizeof(x));
|
||||
}
|
||||
|
||||
static void WriteByte(std::stringstream &out, uint8_t x) {
|
||||
out.write((char *)&x, sizeof(x));
|
||||
}
|
||||
|
||||
// Chunk is written as:
|
||||
// * 4-byte length
|
||||
// * 4-byte type
|
||||
// * the data itself
|
||||
// * 4-byte crc (of type and data)
|
||||
static void WriteChunk(std::stringstream &out, const char *type,
|
||||
const std::string &chunk) {
|
||||
uint32_t len = chunk.size();
|
||||
uint32_t crc = crc32(crc32(0, (const unsigned char *)type, 4),
|
||||
(const unsigned char *)chunk.data(), chunk.size());
|
||||
WriteInt(out, len);
|
||||
out.write(type, 4);
|
||||
// TODO: IDAT chunks are compressed, so we better compress them here.
|
||||
out.write(chunk.data(), chunk.size());
|
||||
WriteInt(out, crc);
|
||||
}
|
||||
|
||||
std::string ProtoToPng(const PngProto &png_proto) {
|
||||
std::stringstream all;
|
||||
const unsigned char header[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
|
||||
all.write((const char*)header, sizeof(header));
|
||||
std::stringstream ihdr_str;
|
||||
auto &ihdr = png_proto.ihdr();
|
||||
// Avoid large images.
|
||||
// They may have interesting bugs, but OOMs are going to kill fuzzing.
|
||||
uint32_t w = std::min(ihdr.width(), 4096U);
|
||||
uint32_t h = std::min(ihdr.height(), 4096U);
|
||||
WriteInt(ihdr_str, w);
|
||||
WriteInt(ihdr_str, h);
|
||||
WriteInt(ihdr_str, ihdr.other1());
|
||||
WriteByte(ihdr_str, ihdr.other2());
|
||||
WriteChunk(all, "IHDR", ihdr_str.str());
|
||||
|
||||
for (size_t i = 0, n = png_proto.chunks_size(); i < n; i++) {
|
||||
auto &chunk = png_proto.chunks(i);
|
||||
if (chunk.has_plte()) {
|
||||
WriteChunk(all, "PLTE", chunk.plte().data());
|
||||
} else if (chunk.has_idat()) {
|
||||
WriteChunk(all, "IDAT", chunk.idat().data());
|
||||
} else if (chunk.has_other_chunk()) {
|
||||
auto &other_chunk = chunk.other_chunk();
|
||||
char type[5] = {0};
|
||||
if (other_chunk.has_known_type()) {
|
||||
static const std::vector<const char *> known_chunks = {
|
||||
"bKGD", "cHRM", "dSIG", "eXIf", "gAMA", "hIST", "iCCP",
|
||||
"iTXt", "pHYs", "sBIT", "sPLT", "sRGB", "sTER", "tEXt",
|
||||
"tIME", "tRNS", "zTXt", "sCAL", "pCAL", "oFFs",
|
||||
};
|
||||
size_t chunk_idx = other_chunk.known_type() % known_chunks.size();
|
||||
memcpy(type, known_chunks[chunk_idx], 4);
|
||||
} else if (other_chunk.has_unknown_type()) {
|
||||
uint32_t unknown_type_int = other_chunk.unknown_type();
|
||||
memcpy(type, &unknown_type_int, 4);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
type[4] = 0;
|
||||
WriteChunk(all, type, other_chunk.data());
|
||||
}
|
||||
}
|
||||
WriteChunk(all, "IEND", "");
|
||||
|
||||
std::string res = all.str();
|
||||
if (const char *dump_path = getenv("PROTO_FUZZER_DUMP_PATH")) {
|
||||
// With libFuzzer binary run this to generate a PNG file x.png:
|
||||
// PROTO_FUZZER_DUMP_PATH=x.png ./a.out proto-input
|
||||
std::ofstream of(dump_path);
|
||||
of.write(res.data(), res.size());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// The actual fuzz target that consumes the PNG data.
|
||||
extern "C" int FuzzPNG(const uint8_t* data, size_t size);
|
||||
|
||||
DEFINE_PROTO_FUZZER(const PngProto &png_proto) {
|
||||
auto s = ProtoToPng(png_proto);
|
||||
FuzzPNG((const uint8_t*)s.data(), s.size());
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
homepage: "http://www.libpng.org/pub/png/libpng.html"
|
||||
primary_contact: "kcc@google.com"
|
||||
sanitizers:
|
||||
- address
|
||||
- undefined
|
||||
fuzzing_engines:
|
||||
- libfuzzer
|
Loading…
Reference in New Issue