[lldb/Fuzzer] Add fuzzer for expression evaluator
authorChelsea Cassanova <chelsea@cassanova.me>
Mon, 11 Jul 2022 21:24:50 +0000 (17:24 -0400)
committerChelsea Cassanova <chelsea@cassanova.me>
Fri, 22 Jul 2022 21:32:00 +0000 (17:32 -0400)
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.

Differential revision: https://reviews.llvm.org/D129377

clang/cmake/modules/ProtobufMutator.cmake
clang/tools/clang-fuzzer/handle-cxx/CMakeLists.txt
clang/tools/clang-fuzzer/proto-to-cxx/CMakeLists.txt
lldb/tools/lldb-fuzzer/CMakeLists.txt
lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/CMakeLists.txt [new file with mode: 0644]
lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/lldb-expression-fuzzer.cpp [new file with mode: 0644]

index 15fe95e..071f11b 100644 (file)
@@ -1,5 +1,9 @@
 include(ExternalProject)
-set(PBM_PREFIX protobuf_mutator)
+
+if (NOT PBM_PREFIX)
+  set (PBM_PREFIX protobuf_mutator)
+endif()
+
 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)
index 6d62421..469b88c 100644 (file)
@@ -11,3 +11,5 @@ add_clang_library(clangHandleCXX
   clangSerialization
   clangTooling
   )
+
+target_include_directories(clangHandleCXX PUBLIC .)
index 339959b..45f51c9 100644 (file)
@@ -14,6 +14,8 @@ add_clang_library(clangLoopProtoToCXX loop_proto_to_cxx.cpp
                   DEPENDS clangCXXLoopProto
                   LINK_LIBS clangCXXLoopProto ${PROTOBUF_LIBRARIES}
                   )
+target_include_directories(clangProtoToCXX PUBLIC .)
+target_include_directories(clangLoopProtoToCXX PUBLIC .)
 
 add_clang_executable(clang-proto-to-cxx proto_to_cxx_main.cpp)
 add_clang_executable(clang-loop-proto-to-cxx loop_proto_to_cxx_main.cpp)
index 867a419..4c081a9 100644 (file)
@@ -1,3 +1,4 @@
 add_subdirectory(lldb-commandinterpreter-fuzzer)
+add_subdirectory(lldb-expression-fuzzer)
 add_subdirectory(lldb-target-fuzzer)
 add_subdirectory(utils)
diff --git a/lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/CMakeLists.txt b/lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/CMakeLists.txt
new file mode 100644 (file)
index 0000000..40606f1
--- /dev/null
@@ -0,0 +1,57 @@
+if(CLANG_ENABLE_PROTO_FUZZER)
+  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 ..)
+    find_package(Protobuf REQUIRED)
+    add_definitions(-DGOOGLE_PROTOBUF_NO_RTTI)
+    include_directories(${PROTOBUF_INCLUDE_DIRS})
+    include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../../clang/tools/clang-fuzzer PRIVATE ..)
+
+    set(CLANG_CMAKE_MODULE_PATH
+      ${CMAKE_CURRENT_SOURCE_DIR}/../../../../clang/cmake/modules)
+
+    set(CMAKE_MODULE_PATH
+      ${CMAKE_MODULE_PATH}
+      ${CLANG_CMAKE_MODULE_PATH})
+
+
+    set (PBM_PREFIX lldb_protobuf_mutator)
+    include(ProtobufMutator)
+    include_directories(${ProtobufMutator_INCLUDE_DIRS})
+
+    target_link_libraries(lldb-expression-fuzzer
+      PRIVATE
+      ${ProtobufMutator_LIBRARIES}
+      ${LLVM_LIB_FUZZING_ENGINE}
+      clangHandleCXX
+      clangCXXProto
+      clangProtoToCXX
+      liblldb
+      )
+
+    add_custom_command(TARGET lldb-expression-fuzzer PRE_BUILD
+      COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/fuzzer-artifacts/expression-artifacts
+      # 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()
+endif()
diff --git a/lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/lldb-expression-fuzzer.cpp b/lldb/tools/lldb-fuzzer/lldb-expression-fuzzer/lldb-expression-fuzzer.cpp
new file mode 100644 (file)
index 0000000..03750e4
--- /dev/null
@@ -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.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.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 input_string = 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 raw_path = originalargv[2];
+  StringRef obj_path = raw_path.erase(0, 2);
+
+  // Create a debugger and a target
+  SBDebugger debugger = SBDebugger::Create(false);
+  SBTarget target = debugger.CreateTarget(obj_path.str().c_str());
+
+  // Create a breakpoint on the only line in the program
+  SBBreakpoint breakpoint = target.BreakpointCreateByLocation(obj_path.str().c_str(), 1);
+
+  // Create launch info and error for launching the process
+  SBLaunchInfo launch_info = target.GetLaunchInfo();
+  SBError error;
+
+  // Launch the process and evaluate the fuzzer's input data
+  // as an expression
+  SBProcess process = target.Launch(launch_info, error);
+  target.EvaluateExpression(input_string.c_str());
+
+  debugger.DeleteTarget(target);
+  SBDebugger::Destroy(debugger);
+  SBModule::GarbageCollectAllocatedModules();
+}