[dart/fuzzer] Add build rule to link Dart with libFuzzer

Rationale:
First step towards integrating libFuzzer with Dart.
The initial target function is proof-of-concept.


Change-Id: I21f6ebf70fec05719423fef61fa5a9609500dc95
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/96987
Commit-Queue: Aart Bik <ajcbik@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn
index 9ef4444..03fddb6 100644
--- a/runtime/BUILD.gn
+++ b/runtime/BUILD.gn
@@ -202,6 +202,12 @@
   }
 }
 
+config("dart_libfuzzer_config") {
+  defines = [ "FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION" ]
+  cflags = [ "-fsanitize=address,fuzzer-no-link" ]
+  ldflags = [ "-fsanitize=address,fuzzer" ]
+}
+
 source_set("dart_api") {
   public_configs = [ ":dart_public_config" ]
   sources = [
diff --git a/runtime/bin/BUILD.gn b/runtime/bin/BUILD.gn
index f8c3141..7f04fed1 100644
--- a/runtime/bin/BUILD.gn
+++ b/runtime/bin/BUILD.gn
@@ -674,6 +674,10 @@
   if (defined(invoker.extra_sources)) {
     extra_sources += invoker.extra_sources
   }
+  extra_ldflags = []
+  if (defined(invoker.extra_ldflags)) {
+    extra_ldflags = invoker.extra_ldflags
+  }
   target_type = "executable"
   if (defined(invoker.target_type)) {
     target_type = invoker.target_type
@@ -722,7 +726,6 @@
                 "dart_embedder_api_impl.cc",
                 "error_exit.cc",
                 "error_exit.h",
-                "main.cc",
                 "main_options.cc",
                 "main_options.h",
                 "options.cc",
@@ -743,6 +746,8 @@
       ldflags = [ "-rdynamic" ]
     }
 
+    ldflags += extra_ldflags
+
     if (is_win) {
       libs = [
         "iphlpapi.lib",
@@ -768,10 +773,11 @@
     "builtin.cc",
     "dfe.cc",
     "dfe.h",
-    "loader.cc",
-    "loader.h",
     "gzip.cc",
     "gzip.h",
+    "loader.cc",
+    "loader.h",
+    "main.cc",
   ]
   if (dart_runtime_mode == "release") {
     extra_sources += [ "observatory_assets_empty.cc" ]
@@ -789,11 +795,12 @@
   }
   extra_sources = [
     "builtin.cc",
-    "snapshot_empty.cc",
-    "loader.cc",
-    "loader.h",
     "gzip.cc",
     "gzip.h",
+    "loader.cc",
+    "loader.h",
+    "main.cc",
+    "snapshot_empty.cc",
   ]
   if (dart_runtime_mode == "release") {
     extra_sources += [ "observatory_assets_empty.cc" ]
@@ -808,11 +815,12 @@
   }
   extra_sources = [
     "builtin.cc",
-    "snapshot_empty.cc",
-    "loader.cc",
-    "loader.h",
     "gzip.cc",
     "gzip.h",
+    "loader.cc",
+    "loader.h",
+    "main.cc",
+    "snapshot_empty.cc",
   ]
   if (dart_runtime_mode == "release") {
     extra_sources += [ "observatory_assets_empty.cc" ]
@@ -826,12 +834,13 @@
   extra_deps = [ "..:libdart_precompiled_runtime" ]
   extra_sources = [
     "builtin.cc",
-    "snapshot_empty.cc",
-    "loader.cc",
-    "loader.h",
     "gzip.cc",
     "gzip.h",
+    "loader.cc",
+    "loader.h",
+    "main.cc",
     "observatory_assets_empty.cc",
+    "snapshot_empty.cc",
   ]
 }
 
@@ -1065,3 +1074,17 @@
     ldflags = [ "/LIBPATH:$abs_root_out_dir" ]
   }
 }
+
+dart_executable("dart_libfuzzer") {
+  extra_ldflags = [ "-fsanitize=address,fuzzer" ]
+  extra_deps = [ "..:libdart_libfuzzer" ]
+  extra_sources = [
+    "../vm/libfuzzer/dart_libfuzzer.cc",
+    "builtin.cc",
+    "dfe.cc",
+    "dfe.h",
+  ]
+  if (!exclude_kernel_service) {
+    extra_deps += [ ":dart_kernel_platform_cc" ]
+  }
+}
diff --git a/runtime/configs.gni b/runtime/configs.gni
index dacaa9c..a638887 100644
--- a/runtime/configs.gni
+++ b/runtime/configs.gni
@@ -24,6 +24,10 @@
 
 _jit_product_config = _base_config + _product
 
+_base_libfuzzer_config = [ "$_dart_runtime:dart_libfuzzer_config" ]
+
+_libfuzzer_config = _base_config + _base_libfuzzer_config
+
 _precompiled_runtime_config =
     _base_config + [
       "$_dart_runtime:dart_maybe_product_config",
@@ -94,6 +98,11 @@
     configs = _nosnapshot_with_precompiler_product_fuchsia_config
     snapshot = false
   },
+  {
+    suffix = "_libfuzzer"
+    configs = _libfuzzer_config
+    snapshot = true
+  },
 ]
 
 # This template creates a target for each of the configurations listed above.
diff --git a/runtime/vm/libfuzzer/README.md b/runtime/vm/libfuzzer/README.md
new file mode 100644
index 0000000..8ef964f
--- /dev/null
+++ b/runtime/vm/libfuzzer/README.md
@@ -0,0 +1,24 @@
+DartLibFuzzer
+=============
+
+DartLibFuzzer is a fuzzing tool built with LibFuzzer, which
+is an in-process, coverage-guided, evolutionary fuzzing engine
+(https://llvm.org/docs/LibFuzzer.html). The tool consists of a
+collection of "target functions", each of which stresses a
+particular part of the Dart runtime and compiler.
+
+How to build and run DartLibFuzzer
+==================================
+Build the dart_libfuzzer binary as follows (first either export
+DART_USE_ASAN=1 or run ./tools/gn.py --mode=debug --asan):
+
+./tools/build.py --mode debug dart_libfuzzer
+
+Then, to start a blank fuzzing session, run:
+
+dart_libfuzzer
+
+To start a fuzzing session with an initial corpus inside
+the directory CORPUS, run:
+
+dart_libfuzzer CORPUS
diff --git a/runtime/vm/libfuzzer/dart_libfuzzer.cc b/runtime/vm/libfuzzer/dart_libfuzzer.cc
new file mode 100644
index 0000000..546a620
--- /dev/null
+++ b/runtime/vm/libfuzzer/dart_libfuzzer.cc
@@ -0,0 +1,24 @@
+// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "platform/unicode.h"
+
+// Libfuzzer target function.
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, size_t Size) {
+  // Proof-of-concept: stresses unicode methods.
+  // NOTE: already found http://dartbug.com/36235
+  dart::Utf8::Type type = dart::Utf8::kLatin1;
+  dart::Utf8::CodeUnitCount(Data, Size, &type);
+  dart::Utf8::IsValid(Data, Size);
+  int32_t dst = 0;
+  dart::Utf8::Decode(Data, Size, &dst);
+  uint16_t dst16[1024];
+  dart::Utf8::DecodeToUTF16(Data, Size, dst16, 1024);
+  int32_t dst32[1024];
+  dart::Utf8::DecodeToUTF32(Data, Size, dst32, 1024);
+  return 0;
+}