Commit 773f0dff by Ben Clayton

Add support for fuzzing cppdap

Add build rules, scripts, basic corpus, and dictionary. Currently requires recent clang toolchain.
parent cc93ba97
build/
fuzz/corpus
fuzz/logs
.vs/
.vscode/settings.json
CMakeSettings.json
......@@ -18,6 +18,14 @@
"args": []
},
{
"type": "cppdbg",
"request": "launch",
"name": "fuzzer (lldb)",
"program": "${workspaceFolder}/fuzz/build/cppdap-fuzzer",
"cwd": "${workspaceRoot}",
"args": []
},
{
"name": "unittests (gdb)",
"type": "cppdbg",
"request": "launch",
......
......@@ -31,6 +31,7 @@ endfunction()
option_if_not_defined(CPPDAP_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
option_if_not_defined(CPPDAP_BUILD_EXAMPLES "Build example applications" OFF)
option_if_not_defined(CPPDAP_BUILD_TESTS "Build tests" OFF)
option_if_not_defined(CPPDAP_BUILD_FUZZER "Build fuzzer" OFF)
option_if_not_defined(CPPDAP_ASAN "Build dap with address sanitizer" OFF)
option_if_not_defined(CPPDAP_MSAN "Build dap with memory sanitizer" OFF)
option_if_not_defined(CPPDAP_TSAN "Build dap with thread sanitizer" OFF)
......@@ -223,6 +224,26 @@ if(CPPDAP_BUILD_TESTS)
target_link_libraries(cppdap-unittests cppdap "${CPPDAP_OS_LIBS}")
endif(CPPDAP_BUILD_TESTS)
# fuzzer
if(CPPDAP_BUILD_FUZZER)
if (NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(FATAL_ERROR "CPPDAP_BUILD_FUZZER can currently only be used with the clang toolchain")
endif()
set(DAP_FUZZER_LIST
${CPPDAP_LIST}
${CMAKE_CURRENT_SOURCE_DIR}/fuzz/fuzz.cpp
)
add_executable(cppdap-fuzzer ${DAP_FUZZER_LIST})
target_compile_options(cppdap-fuzzer PUBLIC "-fsanitize=fuzzer,address")
target_link_libraries(cppdap-fuzzer "-fsanitize=fuzzer,address")
target_include_directories(cppdap-fuzzer PUBLIC
${CPPDAP_INCLUDE_DIR}
${CPPDAP_SRC_DIR}
${CPPDAP_JSON_DIR}/include/
)
target_link_libraries(cppdap-fuzzer cppdap "${CPPDAP_OS_LIBS}")
endif(CPPDAP_BUILD_FUZZER)
# examples
if(CPPDAP_BUILD_EXAMPLES)
function(build_example target)
......
"accessType"
"adapterData"
"adapterID"
"additionalModuleColumns"
"address"
"addressRange"
"algorithm"
"allThreadsContinued"
"allThreadsStopped"
"args"
"arguments"
"attach"
"attributeName"
"attributes"
"breakMode"
"breakpoint"
"breakpointLocations"
"breakpoints"
"cancel"
"cancellable"
"capabilities"
"category"
"checksum"
"checksums"
"clientID"
"clientName"
"column"
"columnsStartAt1"
"command"
"completions"
"completionTriggerCharacters"
"condition"
"configurationDone"
"context"
"continue"
"continued"
"count"
"cwd"
"data"
"dataBreakpointInfo"
"dataId"
"dateTimeStamp"
"default"
"description"
"disassemble"
"disconnect"
"endColumn"
"endLine"
"env"
"evaluate"
"evaluateName"
"exceptionBreakpointFilters"
"exceptionInfo"
"exceptionOptions"
"exitCode"
"exited"
"expensive"
"expression"
"expression"
"filter"
"filter"
"filters"
"format"
"frameId"
"fullTypeName"
"goto"
"gotoTargets"
"group"
"hex"
"hitCondition"
"id"
"includeAll"
"indexedVariables"
"indexedVariables"
"initialized"
"innerException"
"instruction"
"instructionBytes"
"instructionCount"
"instructionOffset"
"instructionPointerReference"
"isLocalProcess"
"isOptimized"
"isUserCode"
"kind"
"label"
"launch"
"length"
"levels"
"line"
"lines"
"linesStartAt1"
"loadedSource"
"loadedSources"
"locale"
"location"
"logMessage"
"memoryReference"
"message"
"module"
"moduleCount"
"moduleId"
"modules"
"name"
"namedVariables"
"names"
"negate"
"next"
"noDebug"
"offset"
"origin"
"output"
"parameterNames"
"parameters"
"parameterTypes"
"parameterValues"
"path"
"pathFormat"
"pause"
"percentage"
"pointerSize"
"presentationHint"
"preserveFocusHint"
"process"
"progressEnd"
"progressId"
"progressStart"
"progressUpdate"
"readMemory"
"reason"
"requestId"
"resolveSymbols"
"restart"
"restartFrame"
"reverseContinue"
"runInTerminal"
"scopes"
"selectionLength"
"selectionStart"
"sendTelemetry"
"seq"
"setBreakpoints"
"setDataBreakpoints"
"setExceptionBreakpoints"
"setExpression"
"setFunctionBreakpoints"
"setVariable"
"showUser"
"sortText"
"source"
"sourceModified"
"sourceReference"
"sources"
"stackTrace"
"start"
"startFrame"
"startMethod"
"startModule"
"stepBack"
"stepIn"
"stepInTargets"
"stepOut"
"stopped"
"supportedChecksumAlgorithms"
"supportsBreakpointLocationsRequest"
"supportsCancelRequest"
"supportsClipboardContext"
"supportsCompletionsRequest"
"supportsConditionalBreakpoints"
"supportsConfigurationDoneRequest"
"supportsDataBreakpoints"
"supportsDelayedStackTraceLoading"
"supportsDisassembleRequest"
"supportsEvaluateForHovers"
"supportsExceptionInfoRequest"
"supportsExceptionOptions"
"supportsFunctionBreakpoints"
"supportsGotoTargetsRequest"
"supportsHitConditionalBreakpoints"
"supportsLoadedSourcesRequest"
"supportsLogPoints"
"supportsMemoryReferences"
"supportsModulesRequest"
"supportsProgressReporting"
"supportsReadMemoryRequest"
"supportsRestartFrame"
"supportsRestartRequest"
"supportsRunInTerminalRequest"
"supportsSetExpression"
"supportsSetVariable"
"supportsStepBack"
"supportsStepInTargetsRequest"
"supportsTerminateRequest"
"supportsTerminateThreadsRequest"
"supportsValueFormattingOptions"
"supportsVariablePaging"
"supportsVariableType"
"supportTerminateDebuggee"
"symbol"
"symbolFilePath"
"symbolStatus"
"systemProcessId"
"targetId"
"terminate"
"terminated"
"terminateDebuggee"
"terminateThreads"
"text"
"thread"
"threadId"
"threadIds"
"threads"
"title"
"title"
"type"
"typeName"
"url"
"urlLabel"
"value"
"variables"
"variablesReference"
"verified"
"version"
"visibility"
"width"
\ No newline at end of file
// Copyright 2020 Google LLC
//
// 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
//
// https://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.
// cppdap fuzzer program.
// Run with: ${CPPDAP_PATH}/fuzz/run.sh
// Requires modern clang toolchain.
#include "content_stream.h"
#include "string_buffer.h"
#include "dap/protocol.h"
#include "dap/session.h"
#include <condition_variable>
#include <mutex>
namespace {
// Event provides a basic wait and signal synchronization primitive.
class Event {
public:
// wait() blocks until the event is fired or the given timeout is reached.
template <typename DURATION>
inline void wait(const DURATION& duration) {
std::unique_lock<std::mutex> lock(mutex);
cv.wait_for(lock, duration, [&] { return fired; });
}
// fire() sets signals the event, and unblocks any calls to wait().
inline void fire() {
std::unique_lock<std::mutex> lock(mutex);
fired = true;
cv.notify_all();
}
private:
std::mutex mutex;
std::condition_variable cv;
bool fired = false;
};
} // namespace
// List of requests that we handle for fuzzing.
#define DAP_REQUEST_LIST() \
DAP_REQUEST(dap::AttachRequest, dap::AttachResponse) \
DAP_REQUEST(dap::BreakpointLocationsRequest, \
dap::BreakpointLocationsResponse) \
DAP_REQUEST(dap::CancelRequest, dap::CancelResponse) \
DAP_REQUEST(dap::CompletionsRequest, dap::CompletionsResponse) \
DAP_REQUEST(dap::ConfigurationDoneRequest, dap::ConfigurationDoneResponse) \
DAP_REQUEST(dap::ContinueRequest, dap::ContinueResponse) \
DAP_REQUEST(dap::DataBreakpointInfoRequest, dap::DataBreakpointInfoResponse) \
DAP_REQUEST(dap::DisassembleRequest, dap::DisassembleResponse) \
DAP_REQUEST(dap::DisconnectRequest, dap::DisconnectResponse) \
DAP_REQUEST(dap::EvaluateRequest, dap::EvaluateResponse) \
DAP_REQUEST(dap::ExceptionInfoRequest, dap::ExceptionInfoResponse) \
DAP_REQUEST(dap::GotoRequest, dap::GotoResponse) \
DAP_REQUEST(dap::GotoTargetsRequest, dap::GotoTargetsResponse) \
DAP_REQUEST(dap::InitializeRequest, dap::InitializeResponse) \
DAP_REQUEST(dap::LaunchRequest, dap::LaunchResponse) \
DAP_REQUEST(dap::LoadedSourcesRequest, dap::LoadedSourcesResponse) \
DAP_REQUEST(dap::ModulesRequest, dap::ModulesResponse) \
DAP_REQUEST(dap::NextRequest, dap::NextResponse) \
DAP_REQUEST(dap::PauseRequest, dap::PauseResponse) \
DAP_REQUEST(dap::ReadMemoryRequest, dap::ReadMemoryResponse) \
DAP_REQUEST(dap::RestartFrameRequest, dap::RestartFrameResponse) \
DAP_REQUEST(dap::RestartRequest, dap::RestartResponse) \
DAP_REQUEST(dap::ReverseContinueRequest, dap::ReverseContinueResponse) \
DAP_REQUEST(dap::RunInTerminalRequest, dap::RunInTerminalResponse) \
DAP_REQUEST(dap::ScopesRequest, dap::ScopesResponse) \
DAP_REQUEST(dap::SetBreakpointsRequest, dap::SetBreakpointsResponse) \
DAP_REQUEST(dap::SetDataBreakpointsRequest, dap::SetDataBreakpointsResponse) \
DAP_REQUEST(dap::SetExceptionBreakpointsRequest, \
dap::SetExceptionBreakpointsResponse) \
DAP_REQUEST(dap::SetExpressionRequest, dap::SetExpressionResponse) \
DAP_REQUEST(dap::SetFunctionBreakpointsRequest, \
dap::SetFunctionBreakpointsResponse) \
DAP_REQUEST(dap::SetVariableRequest, dap::SetVariableResponse) \
DAP_REQUEST(dap::SourceRequest, dap::SourceResponse) \
DAP_REQUEST(dap::StackTraceRequest, dap::StackTraceResponse) \
DAP_REQUEST(dap::StepBackRequest, dap::StepBackResponse) \
DAP_REQUEST(dap::StepInRequest, dap::StepInResponse) \
DAP_REQUEST(dap::StepInTargetsRequest, dap::StepInTargetsResponse) \
DAP_REQUEST(dap::StepOutRequest, dap::StepOutResponse) \
DAP_REQUEST(dap::TerminateRequest, dap::TerminateResponse) \
DAP_REQUEST(dap::TerminateThreadsRequest, dap::TerminateThreadsResponse) \
DAP_REQUEST(dap::ThreadsRequest, dap::ThreadsResponse) \
DAP_REQUEST(dap::VariablesRequest, dap::VariablesResponse)
// Fuzzing main function.
// See http://llvm.org/docs/LibFuzzer.html for details.
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// The first byte can optionally control fuzzing mode.
enum class ControlMode {
// Don't wrap the input data with a stream writer. Allows testing for stream
// writing.
TestStreamWriter,
// Don't append a 'done' request. This may cause the test to take longer to
// complete (it may have to block on a timeout), but exercises the
// unrecognised-message cases.
DontAppendDoneRequest,
// Number of control modes in this enum.
Count,
};
// Scan first byte for control mode.
bool useContentStreamWriter = true;
bool appendDoneRequest = true;
if (size > 0 && data[0] < static_cast<uint8_t>(ControlMode::Count)) {
useContentStreamWriter =
data[0] != static_cast<uint8_t>(ControlMode::TestStreamWriter);
appendDoneRequest =
data[0] != static_cast<uint8_t>(ControlMode::DontAppendDoneRequest);
data++;
size--;
}
// in contains the input data
auto in = std::make_shared<dap::StringBuffer>();
dap::ContentWriter writer(in);
if (useContentStreamWriter) {
writer.write(std::string(reinterpret_cast<const char*>(data), size));
} else {
in->write(data, size);
}
if (appendDoneRequest) {
writer.write(R"(
{
"seq": 10,
"type": "request",
"command": "done",
}
)");
}
// Each test is done if we receive a request, or report an error.
Event requestOrError;
#define DAP_REQUEST(REQUEST, RESPONSE) \
session->registerHandler([&](const REQUEST&) { \
requestOrError.fire(); \
return RESPONSE{}; \
});
auto session = dap::Session::create();
DAP_REQUEST_LIST();
session->onError([&](const char*) { requestOrError.fire(); });
auto out = std::make_shared<dap::StringBuffer>();
session->bind(dap::ReaderWriter::create(in, out));
// Give up after a second if we don't get a request or error reported.
requestOrError.wait(std::chrono::seconds(1));
return 0;
}
\ No newline at end of file
#!/bin/bash
set -e # Fail on any error.
FUZZ_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
cd ${FUZZ_DIR}
# Ensure we're testing with latest build
[ ! -d "build" ] && mkdir "build"
cd "build"
cmake ../.. -GNinja -DCPPDAP_BUILD_FUZZER=1 -DCMAKE_BUILD_TYPE=RelWithDebInfo
ninja
cd ${FUZZ_DIR}
[ ! -d "corpus" ] && mkdir "corpus"
[ ! -d "logs" ] && mkdir "logs"
cd "logs"
rm crash-* fuzz-* || true
${FUZZ_DIR}/build/cppdap-fuzzer ${FUZZ_DIR}/corpus ${FUZZ_DIR}/seed -dict=${FUZZ_DIR}/dictionary.txt -jobs=128
{}
\ No newline at end of file
{
"seq": 153,
"type": "request",
"command": "next",
"arguments": {
"threadId": 3
}
}
\ No newline at end of file
......@@ -52,6 +52,7 @@ class Writer : virtual public Closable {
// ReaderWriter is an interface that combines the Reader and Writer interfaces.
class ReaderWriter : public Reader, public Writer {
public:
// create() returns a ReaderWriter that delegates the interface methods on to
// the provided Reader and Writer.
// isOpen() returns true if the Reader and Writer both return true for
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment