Add support for fuzzing cppdap
Add build rules, scripts, basic corpus, and dictionary. Currently requires recent clang toolchain.
This commit is contained in:
parent
cc93ba9747
commit
773f0dff68
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,6 @@
|
||||
build/
|
||||
fuzz/corpus
|
||||
fuzz/logs
|
||||
.vs/
|
||||
.vscode/settings.json
|
||||
CMakeSettings.json
|
||||
|
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@ -17,6 +17,14 @@
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"name": "fuzzer (lldb)",
|
||||
"program": "${workspaceFolder}/fuzz/build/cppdap-fuzzer",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": []
|
||||
},
|
||||
{
|
||||
"name": "unittests (gdb)",
|
||||
"type": "cppdbg",
|
||||
|
@ -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)
|
||||
|
225
fuzz/dictionary.txt
Normal file
225
fuzz/dictionary.txt
Normal file
@ -0,0 +1,225 @@
|
||||
"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"
|
173
fuzz/fuzz.cpp
Normal file
173
fuzz/fuzz.cpp
Normal file
@ -0,0 +1,173 @@
|
||||
// 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;
|
||||
}
|
19
fuzz/run.sh
Executable file
19
fuzz/run.sh
Executable file
@ -0,0 +1,19 @@
|
||||
#!/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
|
1
fuzz/seed/empty_json
Normal file
1
fuzz/seed/empty_json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
8
fuzz/seed/request
Normal file
8
fuzz/seed/request
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"seq": 153,
|
||||
"type": "request",
|
||||
"command": "next",
|
||||
"arguments": {
|
||||
"threadId": 3
|
||||
}
|
||||
}
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user