diff options
Diffstat (limited to 'tools/fuzzing/nyx')
-rw-r--r-- | tools/fuzzing/nyx/Nyx.cpp | 230 | ||||
-rw-r--r-- | tools/fuzzing/nyx/Nyx.h | 57 | ||||
-rw-r--r-- | tools/fuzzing/nyx/NyxWrapper.h | 33 | ||||
-rw-r--r-- | tools/fuzzing/nyx/moz.build | 16 |
4 files changed, 336 insertions, 0 deletions
diff --git a/tools/fuzzing/nyx/Nyx.cpp b/tools/fuzzing/nyx/Nyx.cpp new file mode 100644 index 0000000000..3fbb520b11 --- /dev/null +++ b/tools/fuzzing/nyx/Nyx.cpp @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/Unused.h" +#include "mozilla/Vector.h" +#include "mozilla/fuzzing/Nyx.h" +#include "prinrval.h" +#include "prthread.h" + +#include <algorithm> +#include <fstream> + +#include <unistd.h> + +namespace mozilla { +namespace fuzzing { + +Nyx::Nyx() { + char* testFilePtr = getenv("MOZ_FUZZ_TESTFILE"); + if (testFilePtr) { + mReplayMode = true; + } +} + +// static +Nyx& Nyx::instance() { + static Nyx nyx; + return nyx; +} + +extern "C" { +MOZ_EXPORT __attribute__((weak)) void nyx_start(void); +MOZ_EXPORT __attribute__((weak)) uint32_t nyx_get_next_fuzz_data(void*, + uint32_t); +MOZ_EXPORT __attribute__((weak)) uint32_t nyx_get_raw_fuzz_data(void*, + uint32_t); +MOZ_EXPORT __attribute__((weak)) void nyx_release(uint32_t); +MOZ_EXPORT __attribute__((weak)) void nyx_handle_event(const char*, const char*, + int, const char*); +MOZ_EXPORT __attribute__((weak)) void nyx_puts(const char*); +MOZ_EXPORT __attribute__((weak)) void nyx_dump_file(void* buffer, size_t len, + const char* filename); +} + +/* + * In this macro, we must avoid calling MOZ_CRASH and friends, as these + * calls will redirect to the Nyx event handler routines. If the library + * is not properly preloaded, we will crash in the process. Instead, emit + * a descriptive error and then force a crash that won't be redirected. + */ +#define NYX_CHECK_API(func) \ + if (!func) { \ + fprintf( \ + stderr, \ + "Error: Nyx library must be in LD_PRELOAD. Missing function \"%s\"\n", \ + #func); \ + MOZ_REALLY_CRASH(__LINE__); \ + } + +void Nyx::start(void) { + MOZ_RELEASE_ASSERT(!mInited); + mInited = true; + + // Check if we are in replay mode. + char* testFilePtr = getenv("MOZ_FUZZ_TESTFILE"); + if (testFilePtr) { + MOZ_FUZZING_NYX_PRINT("[Replay Mode] Reading data file...\n"); + + std::string testFile(testFilePtr); + std::ifstream is; + is.open(testFile, std::ios::binary); + + // The data chunks we receive through Nyx are stored in the data + // section of the testfile as chunks prefixed with a 16-bit data + // length that we mask down to 11-bit. We read all chunks and + // store them away to simulate how we originally received the data + // via Nyx. + + if (is.good()) { + mRawReplayBuffer = new Vector<uint8_t>(); + is.seekg(0, is.end); + int rawLength = is.tellg(); + mozilla::Unused << mRawReplayBuffer->initLengthUninitialized(rawLength); + is.seekg(0, is.beg); + is.read(reinterpret_cast<char*>(mRawReplayBuffer->begin()), rawLength); + is.seekg(0, is.beg); + } + + while (is.good()) { + uint16_t pktsize; + is.read(reinterpret_cast<char*>(&pktsize), sizeof(uint16_t)); + + pktsize &= 0x7ff; + + if (!is.good()) { + break; + } + + auto buffer = new Vector<uint8_t>(); + + mozilla::Unused << buffer->initLengthUninitialized(pktsize); + is.read(reinterpret_cast<char*>(buffer->begin()), buffer->length()); + + MOZ_FUZZING_NYX_PRINTF("[Replay Mode] Read data packet of size %zu\n", + buffer->length()); + + mReplayBuffers.push_back(buffer); + } + + if (!mReplayBuffers.size()) { + MOZ_FUZZING_NYX_PRINT("[Replay Mode] Error: No buffers read.\n"); + _exit(1); + } + + is.close(); + + if (!!getenv("MOZ_FUZZ_WAIT_BEFORE_REPLAY")) { + // This can be useful in some cases to reproduce intermittent issues. + PR_Sleep(PR_MillisecondsToInterval(5000)); + } + + return; + } + + NYX_CHECK_API(nyx_start); + NYX_CHECK_API(nyx_get_next_fuzz_data); + NYX_CHECK_API(nyx_get_raw_fuzz_data); + NYX_CHECK_API(nyx_release); + NYX_CHECK_API(nyx_handle_event); + NYX_CHECK_API(nyx_puts); + NYX_CHECK_API(nyx_dump_file); + + nyx_start(); +} + +bool Nyx::started(void) { return mInited; } + +bool Nyx::is_enabled(const char* identifier) { + static char* fuzzer = getenv("NYX_FUZZER"); + if (!fuzzer || strcmp(fuzzer, identifier)) { + return false; + } + return true; +} + +bool Nyx::is_replay() { return mReplayMode; } + +uint32_t Nyx::get_data(uint8_t* data, uint32_t size) { + MOZ_RELEASE_ASSERT(mInited); + + if (mReplayMode) { + if (!mReplayBuffers.size()) { + return 0xFFFFFFFF; + } + + Vector<uint8_t>* buffer = mReplayBuffers.front(); + mReplayBuffers.pop_front(); + + size = std::min(size, (uint32_t)buffer->length()); + memcpy(data, buffer->begin(), size); + + delete buffer; + + return size; + } + + return nyx_get_next_fuzz_data(data, size); +} + +uint32_t Nyx::get_raw_data(uint8_t* data, uint32_t size) { + MOZ_RELEASE_ASSERT(mInited); + + if (mReplayMode) { + size = std::min(size, (uint32_t)mRawReplayBuffer->length()); + memcpy(data, mRawReplayBuffer->begin(), size); + return size; + } + + return nyx_get_raw_fuzz_data(data, size); +} + +void Nyx::release(uint32_t iterations) { + MOZ_RELEASE_ASSERT(mInited); + + if (mReplayMode) { + MOZ_FUZZING_NYX_PRINT("[Replay Mode] Nyx::release() called.\n"); + + // If we reach this point in replay mode, we are essentially done. + // Let's wait a bit further for things to settle and then exit. + PR_Sleep(PR_MillisecondsToInterval(5000)); + _exit(1); + } + + MOZ_FUZZING_NYX_DEBUG("[DEBUG] Nyx::release() called.\n"); + + nyx_release(iterations); +} + +void Nyx::handle_event(const char* type, const char* file, int line, + const char* reason) { + if (mReplayMode) { + MOZ_FUZZING_NYX_PRINTF( + "[Replay Mode] Nyx::handle_event() called: %s at %s:%d : %s\n", type, + file, line, reason); + return; + } + + if (mInited) { + nyx_handle_event(type, file, line, reason); + } else { + // We can have events such as MOZ_CRASH even before we snapshot. + // Output some useful information to make it clear where it happened. + MOZ_FUZZING_NYX_PRINTF( + "[ERROR] PRE SNAPSHOT Nyx::handle_event() called: %s at %s:%d : %s\n", + type, file, line, reason); + } +} + +void Nyx::dump_file(void* buffer, size_t len, const char* filename) { + if (!mReplayMode) { + nyx_dump_file(buffer, len, filename); + } +} + +} // namespace fuzzing +} // namespace mozilla diff --git a/tools/fuzzing/nyx/Nyx.h b/tools/fuzzing/nyx/Nyx.h new file mode 100644 index 0000000000..0489b2088e --- /dev/null +++ b/tools/fuzzing/nyx/Nyx.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_fuzzing_Nyx_h +#define mozilla_fuzzing_Nyx_h + +#include <stdint.h> +#include <atomic> +#include <list> + +#ifndef NYX_DISALLOW_COPY_AND_ASSIGN +# define NYX_DISALLOW_COPY_AND_ASSIGN(T) \ + T(const T&); \ + void operator=(const T&) +#endif + +namespace mozilla { + +class MallocAllocPolicy; +template <class T, size_t MinInlineCapacity, class AllocPolicy> +class Vector; + +namespace fuzzing { + +class Nyx { + public: + static Nyx& instance(); + + void start(void); + bool started(void); + bool is_enabled(const char* identifier); + bool is_replay(); + uint32_t get_data(uint8_t* data, uint32_t size); + uint32_t get_raw_data(uint8_t* data, uint32_t size); + void release(uint32_t iterations = 1); + void handle_event(const char* type, const char* file, int line, + const char* reason); + void dump_file(void* buffer, size_t len, const char* filename); + + private: + std::atomic<bool> mInited; + + std::atomic<bool> mReplayMode; + std::list<Vector<uint8_t, 0, MallocAllocPolicy>*> mReplayBuffers; + Vector<uint8_t, 0, MallocAllocPolicy>* mRawReplayBuffer; + + Nyx(); + NYX_DISALLOW_COPY_AND_ASSIGN(Nyx); +}; + +} // namespace fuzzing +} // namespace mozilla + +#endif /* mozilla_fuzzing_Nyx_h */ diff --git a/tools/fuzzing/nyx/NyxWrapper.h b/tools/fuzzing/nyx/NyxWrapper.h new file mode 100644 index 0000000000..0bb6e0fc46 --- /dev/null +++ b/tools/fuzzing/nyx/NyxWrapper.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_fuzzing_NyxWrapper_h +#define mozilla_fuzzing_NyxWrapper_h + +#include "mozilla/Types.h" + +/* + * We need this event handler definition both in C and C++ to differentiate + * the various flavors of controlled aborts (e.g. MOZ_DIAGNOSTIC_ASSERT + * vs. MOZ_RELEASE_ASSERT). Hence we can't use the higher level C++ API + * for this and directly redirect to the Nyx event handler in the preloaded + * library. + */ + +#ifdef __cplusplus +extern "C" { +#endif +MOZ_EXPORT __attribute__((weak)) void nyx_handle_event(const char* type, + const char* file, + int line, + const char* reason); + +MOZ_EXPORT __attribute__((weak)) void nyx_puts(const char*); +#ifdef __cplusplus +} +#endif + +#endif /* mozilla_fuzzing_NyxWrapper_h */ diff --git a/tools/fuzzing/nyx/moz.build b/tools/fuzzing/nyx/moz.build new file mode 100644 index 0000000000..74c8ec8515 --- /dev/null +++ b/tools/fuzzing/nyx/moz.build @@ -0,0 +1,16 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +SOURCES += [ + "Nyx.cpp", +] + +EXPORTS.mozilla.fuzzing += [ + "Nyx.h", + "NyxWrapper.h", +] + +FINAL_LIBRARY = "xul" |