diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /tools/fuzzing/nyx/Nyx.cpp | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tools/fuzzing/nyx/Nyx.cpp')
-rw-r--r-- | tools/fuzzing/nyx/Nyx.cpp | 230 |
1 files changed, 230 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 |