[libc] Lay out framework for fuzzing libc functions.
authorPaula Toth <paulatoth@google.com>
Sat, 22 Feb 2020 03:14:51 +0000 (19:14 -0800)
committerPaula Toth <paulatoth@google.com>
Sat, 22 Feb 2020 03:15:46 +0000 (19:15 -0800)
Summary:
Added fuzzing test for strcpy and some documentation related to fuzzing.
This will be the first step in integrating this with oss-fuzz.

Reviewers: sivachandra, abrachet

Reviewed By: sivachandra, abrachet

Subscribers: gchatelet, abrachet, mgorny, MaskRay, tschuett, libc-commits

Tags: #libc-project

Differential Revision: https://reviews.llvm.org/D74091

libc/CMakeLists.txt
libc/cmake/modules/LLVMLibCRules.cmake
libc/docs/fuzzing.rst [new file with mode: 0644]
libc/docs/source_layout.rst
libc/fuzzing/CMakeLists.txt [new file with mode: 0644]
libc/fuzzing/string/CMakeLists.txt [new file with mode: 0644]
libc/fuzzing/string/strcpy_fuzz.cpp [new file with mode: 0644]

index a4c95ee..fd750a0 100644 (file)
@@ -32,3 +32,4 @@ add_subdirectory(utils)
 # of the other directories.
 add_subdirectory(lib)
 add_subdirectory(test)
+add_subdirectory(fuzzing)
index f5bf3f8..ae4a144 100644 (file)
@@ -300,7 +300,7 @@ function(add_libc_unittest target_name)
   if(NOT LLVM_INCLUDE_TESTS)
     return()
   endif()
-
+  
   cmake_parse_arguments(
     "LIBC_UNITTEST"
     "" # No optional arguments
@@ -375,6 +375,71 @@ function(add_libc_testsuite suite_name)
   add_dependencies(check-libc ${suite_name})
 endfunction(add_libc_testsuite)
 
+# Rule to add a fuzzer test.
+# Usage
+#    add_libc_fuzzer(
+#      <target name>
+#      SRCS  <list of .cpp files for the test>
+#      HDRS  <list of .h files for the test>
+#      DEPENDS <list of dependencies>
+#    )
+function(add_libc_fuzzer target_name)
+  cmake_parse_arguments(
+    "LIBC_FUZZER"
+    "" # No optional arguments
+    "" # Single value arguments
+    "SRCS;HDRS;DEPENDS" # Multi-value arguments
+    ${ARGN}
+  )
+  if(NOT LIBC_FUZZER_SRCS)
+    message(FATAL_ERROR "'add_libc_fuzzer' target requires a SRCS list of .cpp files.")
+  endif()
+  if(NOT LIBC_FUZZER_DEPENDS)
+    message(FATAL_ERROR "'add_libc_fuzzer' target requires a DEPENDS list of 'add_entrypoint_object' targets.")
+  endif()
+
+  set(library_deps "")
+  foreach(dep IN LISTS LIBC_FUZZER_DEPENDS)
+    get_target_property(dep_type ${dep} "TARGET_TYPE")
+    if (dep_type)
+      string(COMPARE EQUAL ${dep_type} ${ENTRYPOINT_OBJ_TARGET_TYPE} dep_is_entrypoint)
+      if(dep_is_entrypoint)
+        get_target_property(obj_file ${dep} "OBJECT_FILE_RAW")
+        list(APPEND library_deps ${obj_file})
+        continue()
+      endif()
+    endif()
+    # TODO: Check if the dep is a normal CMake library target. If yes, then add it
+    # to the list of library_deps.
+  endforeach(dep)
+
+  add_executable(
+    ${target_name}
+    EXCLUDE_FROM_ALL
+    ${LIBC_FUZZER_SRCS}
+    ${LIBC_FUZZER_HDRS}
+  )
+  target_include_directories(
+    ${target_name}
+    PRIVATE
+      ${LIBC_SOURCE_DIR}
+      ${LIBC_BUILD_DIR}
+      ${LIBC_BUILD_DIR}/include
+  )
+
+  if(library_deps)
+    target_link_libraries(${target_name} PRIVATE ${library_deps})
+  endif()
+
+  set_target_properties(${target_name} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+  add_dependencies(
+    ${target_name}
+    ${LIBC_FUZZER_DEPENDS}
+  )
+  add_dependencies(libc-fuzzer ${target_name})
+endfunction(add_libc_fuzzer)
+
 # Rule to add header only libraries.
 # Usage
 #    add_header_library(
diff --git a/libc/docs/fuzzing.rst b/libc/docs/fuzzing.rst
new file mode 100644 (file)
index 0000000..a16cac6
--- /dev/null
@@ -0,0 +1,15 @@
+Fuzzing for LLVM-libc
+---------------------
+
+Fuzzing tests are used to ensure quality and security of LLVM-libc
+implementations. 
+
+Each fuzzing test lives under the fuzzing directory in a subdirectory
+corresponding with the src layout. 
+
+Currently we use system libc for functions that have yet to be implemented,
+however as they are implemented the fuzzers will be changed to use our 
+implementation to increase coverage for testing. 
+
+Fuzzers will be run on `oss-fuzz <https://github.com/google/oss-fuzz>`_ and the
+check-libc target will ensure that they build correctly. 
index 685798c..9bf6352 100644 (file)
@@ -7,6 +7,7 @@ directories::
    + libc
         - cmake
         - docs
+        - fuzzing
         - include
         - lib
         - loader
@@ -31,6 +32,13 @@ The ``docs`` directory
 The ``docs`` directory contains design docs and also informative documents like
 this document on source layout.
 
+The ``fuzzing`` directory
+----------------------
+
+This directory contains fuzzing tests for the various components of llvm-libc. The
+directory structure within this directory mirrors the directory structure of the
+top-level ``libc`` directory itself. For more details, see :doc:`fuzzing`.
+
 The ``include`` directory
 -------------------------
 
@@ -62,7 +70,7 @@ The ``src`` directory
 This directory contains the implementations of the llvm-libc entrypoints. It is
 further organized as follows:
 
-1. There is a toplevel CMakeLists.txt file.
+1. There is a top-level CMakeLists.txt file.
 2. For every public header file provided by llvm-libc, there exists a
    corresponding directory in the ``src`` directory. The name of the directory
    is same as the base name of the header file. For example, the directory
diff --git a/libc/fuzzing/CMakeLists.txt b/libc/fuzzing/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1b950ad
--- /dev/null
@@ -0,0 +1,5 @@
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=fuzzer")
+add_custom_target(libc-fuzzer)
+add_dependencies(check-libc libc-fuzzer)
+
+add_subdirectory(string)
diff --git a/libc/fuzzing/string/CMakeLists.txt b/libc/fuzzing/string/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0482b96
--- /dev/null
@@ -0,0 +1,7 @@
+add_libc_fuzzer(
+  strcpy_fuzz
+  SRCS
+    strcpy_fuzz.cpp
+  DEPENDS
+    strcpy
+)
diff --git a/libc/fuzzing/string/strcpy_fuzz.cpp b/libc/fuzzing/string/strcpy_fuzz.cpp
new file mode 100644 (file)
index 0000000..51a85d6
--- /dev/null
@@ -0,0 +1,38 @@
+//===--------------------- strcpy_fuzz.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
+//
+//===----------------------------------------------------------------------===//
+///
+/// Fuzzing test for llvm-libc strcpy implementation.
+///
+//===----------------------------------------------------------------------===//
+#include "src/string/strcpy.h"
+#include <stdint.h>
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  // Validate input
+  if (!size) return 0;
+  if (data[size - 1] != '\0') return 0;
+  const char *src = (const char *)data;
+
+  char *dest = new char[size];
+  if (!dest) __builtin_trap();
+
+  __llvm_libc::strcpy(dest, src);
+
+  size_t i;
+  for (i = 0; src[i] != '\0'; i++) {
+    // Ensure correctness of strcpy
+    if (dest[i] != src[i]) __builtin_trap();
+  }
+  // Ensure strcpy null terminates dest
+  if (dest[i] != src[i]) __builtin_trap();
+
+  delete[] dest;
+
+  return 0;
+}
+