oss-fuzz: Add fuzzing headers
These includes header files were taken from:
https://github.com/guidovranken/fuzzing-headers.git
with some minor changes required to make them compile cleanly
with the extra compiler warning flags used by the FLAC build
system.
diff --git a/oss-fuzz/fuzzing/Readme.md b/oss-fuzz/fuzzing/Readme.md
new file mode 100644
index 0000000..d2fbd68
--- /dev/null
+++ b/oss-fuzz/fuzzing/Readme.md
@@ -0,0 +1,6 @@
+The header files in this directory and below were taken from:
+
+ https://github.com/guidovranken/fuzzing-headers.git
+
+Some minor modifications were made to make them build with the default C++
+warning flags.
diff --git a/oss-fuzz/fuzzing/datasource/datasource.hpp b/oss-fuzz/fuzzing/datasource/datasource.hpp
new file mode 100644
index 0000000..e0699d3
--- /dev/null
+++ b/oss-fuzz/fuzzing/datasource/datasource.hpp
@@ -0,0 +1,167 @@
+#pragma once
+
+#include <fuzzing/exception.hpp>
+#include <fuzzing/types.hpp>
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <string>
+#include <vector>
+
+namespace fuzzing {
+namespace datasource {
+
+class Base
+{
+ protected:
+ virtual std::vector<uint8_t> get(const size_t min, const size_t max, const uint64_t id = 0) = 0;
+ public:
+ Base(void) = default;
+ virtual ~Base(void) = default;
+
+ template<class T> T Get(const uint64_t id = 0);
+ uint16_t GetChoice(const uint64_t id = 0);
+ std::vector<uint8_t> GetData(const uint64_t id, const size_t min = 0, const size_t max = 0);
+ template <class T> std::vector<T> GetVector(const uint64_t id = 0);
+
+ class OutOfData : public fuzzing::exception::FlowException {
+ public:
+ OutOfData() = default;
+ };
+
+ class DeserializationFailure : public fuzzing::exception::FlowException {
+ public:
+ DeserializationFailure() = default;
+ };
+};
+
+#ifndef FUZZING_HEADERS_NO_IMPL
+template<class T> T Base::Get(const uint64_t id)
+{
+ T ret;
+ const auto v = get(sizeof(ret), sizeof(ret), id);
+ memcpy(&ret, v.data(), sizeof(ret));
+ return ret;
+}
+
+template <> bool Base::Get<bool>(const uint64_t id)
+{
+ uint8_t ret;
+ const auto v = get(sizeof(ret), sizeof(ret), id);
+ memcpy(&ret, v.data(), sizeof(ret));
+ return (ret % 2) ? true : false;
+}
+
+template <> std::string Base::Get<std::string>(const uint64_t id)
+{
+ auto data = GetData(id);
+ return std::string(data.data(), data.data() + data.size());
+}
+
+template <> std::vector<std::string> Base::Get<std::vector<std::string>>(const uint64_t id)
+{
+ std::vector<std::string> ret;
+ while ( true ) {
+ auto data = GetData(id);
+ ret.push_back( std::string(data.data(), data.data() + data.size()) );
+ if ( Get<bool>(id) == false ) {
+ break;
+ }
+ }
+ return ret;
+}
+
+uint16_t Base::GetChoice(const uint64_t id)
+{
+ return Get<uint16_t>(id);
+}
+
+std::vector<uint8_t> Base::GetData(const uint64_t id, const size_t min, const size_t max)
+{
+ return get(min, max, id);
+}
+
+
+template <> types::String<> Base::Get<types::String<>>(const uint64_t id) {
+ const auto data = GetData(id);
+ types::String<> ret(data.data(), data.size());
+ return ret;
+}
+
+template <> types::Data<> Base::Get<types::Data<>>(const uint64_t id) {
+ const auto data = GetData(id);
+ types::Data<> ret(data.data(), data.size());
+ return ret;
+}
+
+template <class T>
+std::vector<T> Base::GetVector(const uint64_t id) {
+ std::vector<T> ret;
+
+ while ( Get<bool>(id) == true ) {
+ ret.push_back( Get<T>(id) );
+ }
+
+ return ret;
+}
+#endif
+
+class Datasource : public Base
+{
+ private:
+ const uint8_t* data;
+ const size_t size;
+ size_t idx;
+ size_t left;
+ std::vector<uint8_t> get(const size_t min, const size_t max, const uint64_t id = 0) override;
+
+ // Make copy constructor and assignment opertator private.
+ Datasource(const Datasource &) : data(0), size(0), idx(0), left(0) {}
+ Datasource& operator=(const Datasource &) { return *this; }
+ public:
+ Datasource(const uint8_t* _data, const size_t _size);
+};
+
+#ifndef FUZZING_HEADERS_NO_IMPL
+Datasource::Datasource(const uint8_t* _data, const size_t _size) :
+ Base(), data(_data), size(_size), idx(0), left(size)
+{
+}
+
+std::vector<uint8_t> Datasource::get(const size_t min, const size_t max, const uint64_t id) {
+ (void)id;
+
+ uint32_t getSize;
+ if ( left < sizeof(getSize) ) {
+ throw OutOfData();
+ }
+ memcpy(&getSize, data + idx, sizeof(getSize));
+ idx += sizeof(getSize);
+ left -= sizeof(getSize);
+
+ if ( getSize < min ) {
+ getSize = min;
+ }
+ if ( max && getSize > max ) {
+ getSize = max;
+ }
+
+ if ( left < getSize ) {
+ throw OutOfData();
+ }
+
+ std::vector<uint8_t> ret(getSize);
+
+ if ( getSize > 0 ) {
+ memcpy(ret.data(), data + idx, getSize);
+ }
+ idx += getSize;
+ left -= getSize;
+
+ return ret;
+}
+#endif
+
+} /* namespace datasource */
+} /* namespace fuzzing */
diff --git a/oss-fuzz/fuzzing/datasource/id.hpp b/oss-fuzz/fuzzing/datasource/id.hpp
new file mode 100644
index 0000000..3ba38d7
--- /dev/null
+++ b/oss-fuzz/fuzzing/datasource/id.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <stdio.h>
+#include <stdint.h>
+#include <utility>
+#include <map>
+
+namespace fuzzing {
+namespace datasource {
+
+/* From: https://gist.github.com/underscorediscovery/81308642d0325fd386237cfa3b44785c */
+inline uint64_t hash_64_fnv1a(const void* key, const uint64_t len) {
+
+ const char* data = (char*)key;
+ uint64_t hash = 0xcbf29ce484222325;
+ uint64_t prime = 0x100000001b3;
+
+ for(uint64_t i = 0; i < len; ++i) {
+ uint8_t value = data[i];
+ hash = hash ^ value;
+ hash *= prime;
+ }
+
+ return hash;
+
+} //hash_64_fnv1a
+
+// FNV1a c++11 constexpr compile time hash functions, 32 and 64 bit
+// str should be a null terminated string literal, value should be left out
+// e.g hash_32_fnv1a_const("example")
+// code license: public domain or equivalent
+// post: https://notes.underscorediscovery.com/constexpr-fnv1a/
+
+constexpr uint32_t val_32_const = 0x811c9dc5;
+constexpr uint32_t prime_32_const = 0x1000193;
+constexpr uint64_t val_64_const = 0xcbf29ce484222325;
+constexpr uint64_t prime_64_const = 0x100000001b3;
+
+
+inline constexpr uint64_t ID(const char* const str, const uint64_t value = val_64_const) noexcept {
+ auto ret = (str[0] == '\0') ? value : ID(&str[1], (value ^ uint64_t(str[0])) * prime_64_const);
+ return ret;
+}
+
+inline constexpr std::pair<const char*, uint64_t> IDPair(const char* const str, const uint64_t value = val_64_const) noexcept {
+ return {str, ID(str, value)};
+}
+
+using IDMap = std::map<const char*, uint64_t>;
+
+} /* namespace datasource */
+} /* namespace fuzzing */
diff --git a/oss-fuzz/fuzzing/exception.hpp b/oss-fuzz/fuzzing/exception.hpp
new file mode 100644
index 0000000..55c360d
--- /dev/null
+++ b/oss-fuzz/fuzzing/exception.hpp
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <exception>
+#include <string>
+
+namespace fuzzing {
+namespace exception {
+
+class ExceptionBase : public std::exception {
+ public:
+ ExceptionBase(void) = default;
+ /* typeid(T).name */
+};
+
+/* Recoverable exception */
+class FlowException : public ExceptionBase {
+ public:
+ FlowException(void) : ExceptionBase() { }
+};
+
+/* Error in this library, should never happen */
+class LogicException : public ExceptionBase {
+ private:
+ std::string reason;
+ public:
+ LogicException(const std::string r) : ExceptionBase(), reason(r) { }
+ virtual const char* what(void) const throw() {
+ return reason.c_str();
+ }
+};
+
+/* Error in target application */
+class TargetException : public ExceptionBase {
+ private:
+ std::string reason;
+ public:
+ TargetException(const std::string r) : ExceptionBase(), reason(r) { }
+ virtual const char* what(void) const throw() {
+ return reason.c_str();
+ }
+};
+
+} /* namespace exception */
+} /* namespace fuzzing */
diff --git a/oss-fuzz/fuzzing/memory.hpp b/oss-fuzz/fuzzing/memory.hpp
new file mode 100644
index 0000000..804e23b
--- /dev/null
+++ b/oss-fuzz/fuzzing/memory.hpp
@@ -0,0 +1,73 @@
+#pragma once
+
+#include <stdio.h>
+#include <optional>
+
+#ifndef ASAN
+#define ASAN 0
+#endif
+
+#ifndef MSAN
+#define MSAN 0
+#endif
+
+namespace fuzzing {
+namespace memory {
+
+#ifndef FUZZING_HEADERS_NO_IMPL
+#if ASAN == 1
+extern "C" void *__asan_region_is_poisoned(const void *beg, size_t size);
+#endif
+
+#if MSAN == 1
+extern "C" void __msan_check_mem_is_initialized(const volatile void *x, size_t size);
+#endif
+
+void memory_test_asan(const void* data, const size_t size)
+{
+ (void)data;
+ (void)size;
+
+#if ASAN == 1
+ if ( __asan_region_is_poisoned(data, size) != NULL ) {
+ abort();
+ }
+#endif
+}
+
+void memory_test_msan(const void* data, const size_t size)
+{
+ (void)data;
+ (void)size;
+
+#if MSAN == 1
+ __msan_check_mem_is_initialized(data, size);
+#endif
+}
+
+void memory_test(const void* data, const size_t size)
+{
+ memory_test_asan(data, size);
+ memory_test_msan(data, size);
+}
+
+template <class T>
+void memory_test(const T& t)
+{
+ (void)t;
+}
+
+template <>
+void memory_test(const std::string& s)
+{
+ (void)s;
+
+#if MSAN == 1
+ memory_test(s.data(), s.size());
+#endif
+}
+
+#endif
+
+} /* namespace memory */
+} /* namespace fuzzing */
diff --git a/oss-fuzz/fuzzing/types.hpp b/oss-fuzz/fuzzing/types.hpp
new file mode 100644
index 0000000..f2b56fc
--- /dev/null
+++ b/oss-fuzz/fuzzing/types.hpp
@@ -0,0 +1,135 @@
+#pragma once
+
+#include <cstdint>
+#include <cstdlib>
+#include <cstring>
+#include <fuzzing/memory.hpp>
+#include <vector>
+#include <string>
+
+namespace fuzzing {
+namespace types {
+
+template <typename CoreType, bool NullTerminated, bool UseMSAN = false>
+class Container {
+ private:
+ CoreType* InvalidAddress = (CoreType*)0x12;
+
+ CoreType* _data = InvalidAddress;
+ size_t _size = 0;
+
+#ifndef FUZZING_HEADERS_NO_IMPL
+ void copy(const void* data, size_t size) {
+ if ( size > 0 ) {
+ std::memcpy(_data, data, size);
+ }
+ }
+
+ void allocate(size_t size) {
+ if ( size > 0 ) {
+ _data = static_cast<CoreType*>(malloc(size * sizeof(CoreType)));
+ } else {
+ _data = InvalidAddress;
+ }
+ };
+
+ void allocate_and_copy(const void* data, size_t size) {
+ allocate(size);
+ copy(data, size);
+ }
+
+ void allocate_plus_1_and_copy(const void* data, size_t size) {
+ allocate(size+1);
+ copy(data, size);
+ }
+
+ void access_hook(void) const {
+ if ( UseMSAN == true ) {
+ memory::memory_test_msan(_data, _size);
+ }
+ }
+
+ void free(void) {
+ access_hook();
+
+ if ( _data != InvalidAddress ) {
+ std::free(_data);
+ _data = InvalidAddress;
+ _size = 0;
+ }
+ }
+
+#endif
+
+ public:
+#ifndef FUZZING_HEADERS_NO_IMPL
+ CoreType* data(void) {
+ access_hook();
+ return _data;
+ }
+
+ size_t size(void) const {
+ access_hook();
+ return _size;
+ }
+#endif
+
+ Container(void)
+#ifndef FUZZING_HEADERS_NO_IMPL
+ = default
+#endif
+ ;
+
+ Container(const void* data, const size_t size)
+#ifndef FUZZING_HEADERS_NO_IMPL
+ {
+ if ( NullTerminated == false ) {
+ allocate_and_copy(data, size);
+ } else {
+ allocate_plus_1_and_copy(data, size);
+ _data[size] = 0;
+ }
+
+ access_hook();
+ }
+#endif
+ ;
+
+ template<class T>
+ Container(const T& t)
+#ifndef FUZZING_HEADERS_NO_IMPL
+ {
+ Container(t.data(), t.size());
+ }
+#endif
+ ;
+
+ ~Container(void)
+#ifndef FUZZING_HEADERS_NO_IMPL
+ {
+ this->free();
+ }
+#endif
+ ;
+
+
+
+ // The copy constructor was not originally explicitly supplied
+ // so it must have been incorrectly just copying the pointers.
+ Container(const Container &c) {
+ InvalidAddress = c.InvalidAddress;
+ allocate_and_copy(c._data, c._size);
+ }
+
+ Container& operator=(Container &c) {
+ InvalidAddress = c.InvalidAddress;
+ allocate_and_copy(c._data, c._size);
+ }
+
+};
+
+template <bool UseMSAN = false> using String = Container<char, true, UseMSAN>;
+template <bool UseMSAN = false> using Data = Container<uint8_t, false, UseMSAN>;
+
+} /* namespace types */
+} /* namespace fuzzing */