cassanova created this revision. cassanova added reviewers: JDevlieghere, mib. cassanova added a project: LLDB. Herald added a subscriber: mgorny. Herald added a project: All. cassanova requested review of this revision. Herald added a subscriber: lldb-commits.
This commit adds a fuzzer for LLDB's expression evaluator. The fuzzer takes a different approach than the current fuzzers present, and uses an approach that is currently being used for clang fuzzers. Instead of fuzzing the evaluator with randomly mutated characters, protobufs are used to generate a subset of C++. This is then converted to valid C++ code and sent to the expression evaluator. In addition, libprotobuf_mutator is used to mutate the fuzzer's inputs from valid C++ code to valid C++ code, rather than mutating from valid code to total nonsense. In order to use libprotobuf_mutator, a CMake module is added to LLDB's CMake modules. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D129377 Files: lldb/cmake/modules/ProtobufMutator.cmake lldb/tools/lldb-fuzzer/CMakeLists.txt lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/CMakeLists.txt lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/cxx_proto.proto lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/lldb-expression-fuzzer.cpp
Index: lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/lldb-expression-fuzzer.cpp =================================================================== --- /dev/null +++ lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/lldb-expression-fuzzer.cpp @@ -0,0 +1,73 @@ +//===-- lldb-expression-fuzzer.cpp ---------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===---------------------------------------------------------------------===// +// +// \file +// This file is a fuzzer for LLDB's expression evaluator. It uses protobufs +// and the libprotobuf-mutator to create valid C-like inputs for the +// expression evaluator. +// +//===---------------------------------------------------------------------===// + +#include <string> + +#include "cxx_proto.pb.h" +#include "handle-cxx/handle_cxx.h" +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBError.h" +#include "lldb/API/SBLaunchInfo.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include "proto-to-cxx/proto_to_cxx.h" +#include "src/libfuzzer/libfuzzer_macro.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +using namespace lldb; +using namespace llvm; +using namespace clang_fuzzer; + +char **originalargv; + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + SBDebugger::Initialize(); + + // The path for a simple compiled program is needed to create a + // target for the debugger and that path is passed in through argv + originalargv = *argv; + return 0; +} + +DEFINE_BINARY_PROTO_FUZZER(const clang_fuzzer::Function &input) { + auto S = clang_fuzzer::FunctionToString(input); + + // Get the second argument from argv and strip the '--' from it. + // This will be used as the path for the object file to create a target from + std::string rawpath = originalargv[2]; + StringRef objpath = rawpath.erase(0, 2); + + // Create a debugger and a target + SBDebugger debugger = SBDebugger::Create(false); + SBTarget target = debugger.CreateTarget(objpath.str().c_str()); + + // Create a breakpoint on the only line in the program + SBBreakpoint bp = target.BreakpointCreateByLocation(objpath.str().c_str(), 1); + + // Create launch info and error for launching the process + SBLaunchInfo li = target.GetLaunchInfo(); + SBError error; + + // Launch the process and evaluate the fuzzer's input data + // as an expression + SBProcess process = target.Launch(li, error); + target.EvaluateExpression(S.c_str()); + + debugger.DeleteTarget(target); + SBDebugger::Destroy(debugger); + SBModule::GarbageCollectAllocatedModules(); +} Index: lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/cxx_proto.proto =================================================================== --- /dev/null +++ lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/cxx_proto.proto @@ -0,0 +1,92 @@ +//===-- cxx_proto.proto - Protobuf description of C++ ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file describes a subset of C++ as a protobuf. It is used to +/// more easily find interesting inputs for fuzzing Clang. +/// +//===----------------------------------------------------------------------===// + +syntax = "proto2"; + +message VarRef { + required int32 varnum = 1; +} + +message Lvalue { + required VarRef varref = 1; +} + +message Const { + required int32 val = 1; +} + +message BinaryOp { + enum Op { + PLUS = 0; + MINUS = 1; + MUL = 2; + DIV = 3; + MOD = 4; + XOR = 5; + AND = 6; + OR = 7; + EQ = 8; + NE = 9; + LE = 10; + GE = 11; + LT = 12; + GT = 13; + }; + required Op op = 1; + required Rvalue left = 2; + required Rvalue right = 3; +} + +message Rvalue { + oneof rvalue_oneof { + VarRef varref = 1; + Const cons = 2; + BinaryOp binop = 3; + } +} + +message AssignmentStatement { + required Lvalue lvalue = 1; + required Rvalue rvalue = 2; +} + + +message IfElse { + required Rvalue cond = 1; + required StatementSeq if_body = 2; + required StatementSeq else_body = 3; +} + +message While { + required Rvalue cond = 1; + required StatementSeq body = 2; +} + +message Statement { + oneof stmt_oneof { + AssignmentStatement assignment = 1; + IfElse ifelse = 2; + While while_loop = 3; + } +} + +message StatementSeq { + repeated Statement statements = 1; +} + +message Function { + required StatementSeq statements = 1; +} + +package clang_fuzzer; Index: lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/CMakeLists.txt =================================================================== --- /dev/null +++ lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/CMakeLists.txt @@ -0,0 +1,89 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_llvm_fuzzer(lldb-expression-fuzzer + EXCLUDE_FROM_ALL + lldb-expression-fuzzer.cpp + ) + +if(TARGET lldb-expression-fuzzer) + target_include_directories(lldb-expression-fuzzer PRIVATE ..) + + # Generate the necessary source and header files for using protobufs + find_package(Protobuf REQUIRED) + add_definitions(-DGOOGLE_PROTOBUF_NO_RTTI) + include_directories(${PROTOBUF_INCLUDE_DIRS}) + include_directories(${CMAKE_CURRENT_BINARY_DIR}) + protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS cxx_proto.proto) + protobuf_generate_cpp(LOOP_PROTO_SRCS LOOP_PROTO_HDRS cxx_loop_proto.proto) + set(LLVM_OPTIONAL_SOURCES ${LLVM_OPTIONAL_SOURCES} ${PROTO_SRCS}) + + # Place the source and header files into a library for use by LLDB's expression fuzzer + # FIXME: It would be better to use add_lldb_library, but using this will cause an error + # during cmake's file generation phase + add_library(lldbCXXProto + ${PROTO_SRCS} + ${PROTO_HDRS} + ) + + # Build and include the libprotobuf-mutator repository + include(ProtobufMutator) + include_directories(${ProtobufMutator_INCLUDE_DIRS}) + + # Create a variable for the libraries generated by protobuf and protobuf mutator + set(COMMON_PROTO_FUZZ_LIBRARIES + ${ProtobufMutator_LIBRARIES} + ${PROTOBUF_LIBRARIES} + ) + + # Link the protobuf libraries as well as the clang libraries used to + # convert protobufs to C/C++ + target_link_libraries(lldb-expression-fuzzer + PRIVATE + ${COMMON_PROTO_FUZZ_LIBRARIES} + clangHandleCXX + lldbCXXProto + clangProtoToCXX + liblldb + ) + + # The target for this fuzzer needs to depend on the protobuf mutator + # repository + add_dependencies(lldb-expression-fuzzer lldb_protobuf_mutator) + + add_custom_command(TARGET lldb-expression-fuzzer PRE_BUILD + + # FIXME: Copying the source and header files is not the preferred way to implement these libraries + # on the LLDB side. It would be preferable to have the libraries for protobuf fuzzers be located + # in a more central location + + # Create directories to store the files for handle-cxx and proto-to-cxx since the protobuf mutator + # depends on them + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/handle-cxx + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/proto-to-cxx + + # Copy the header and source files for handle-cxx from clang + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../../../../clang/tools/clang-fuzzer/handle-cxx/handle_cxx.h ${CMAKE_CURRENT_BINARY_DIR}/handle-cxx + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../../../../clang/tools/clang-fuzzer/handle-cxx/handle_cxx.cpp ${CMAKE_CURRENT_BINARY_DIR}/handle-cxx + + # Copy the header and source files for proto-to-cxx from clang + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../../../../clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.h ${CMAKE_CURRENT_BINARY_DIR}/proto-to-cxx + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../../../../clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx.cpp ${CMAKE_CURRENT_BINARY_DIR}/proto-to-cxx + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../../../../clang/tools/clang-fuzzer/proto-to-cxx/proto_to_cxx_main.cpp ${CMAKE_CURRENT_BINARY_DIR}/proto-to-cxx + + # Create and compile a simple C program using the command line. This is + # needed because LLDB's expression evaluator needs a legitmate target + # instead of a dummy target + COMMAND echo 'int main (int argc, char** argv) { return 0\; }' | clang -o main.out -xc - + ) + + # Create a directory for storing the fuzzer's artifacts and run the fuzzer with arguments that will + # not attempt to reduce the size of the inputs being generated + add_custom_target(fuzz-lldb-expression + COMMENT "Running the LLDB expression evaluator fuzzer..." + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/fuzzer-artifacts/expression-artifacts + COMMAND $<TARGET_FILE:lldb-expression-fuzzer> -artifact_prefix=expression- -reduce_inputs=0 + USES_TERMINAL + ) +endif() Index: lldb/tools/lldb-fuzzer/CMakeLists.txt =================================================================== --- lldb/tools/lldb-fuzzer/CMakeLists.txt +++ lldb/tools/lldb-fuzzer/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(lldb-commandinterpreter-fuzzer) +add_subdirectory(lldb-expression-fuzzer) add_subdirectory(lldb-target-fuzzer) add_subdirectory(utils) Index: lldb/cmake/modules/ProtobufMutator.cmake =================================================================== --- /dev/null +++ lldb/cmake/modules/ProtobufMutator.cmake @@ -0,0 +1,20 @@ +include(ExternalProject) +set(PBM_PREFIX lldb_protobuf_mutator) +set(PBM_PATH ${CMAKE_CURRENT_BINARY_DIR}/${PBM_PREFIX}/src/${PBM_PREFIX}) +set(PBM_LIB_PATH ${PBM_PATH}-build/src/libprotobuf-mutator.a) +set(PBM_FUZZ_LIB_PATH ${PBM_PATH}-build/src/libfuzzer/libprotobuf-mutator-libfuzzer.a) + +ExternalProject_Add(${PBM_PREFIX} + PREFIX ${PBM_PREFIX} + GIT_REPOSITORY https://github.com/google/libprotobuf-mutator.git + GIT_TAG master + CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + CMAKE_CACHE_ARGS -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} + BUILD_BYPRODUCTS ${PBM_LIB_PATH} ${PBM_FUZZ_LIB_PATH} + UPDATE_COMMAND "" + INSTALL_COMMAND "" + ) + +set(ProtobufMutator_INCLUDE_DIRS ${PBM_PATH}) +set(ProtobufMutator_LIBRARIES ${PBM_FUZZ_LIB_PATH} ${PBM_LIB_PATH})
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits