Force the linking of the UBSAN runtime in OSS-Fuzz builds. (#130)

* Force the linking of the UBSAN runtime in OSS-Fuzz builds.

* Fix llvm-config version.

* Make the code architecture-independent.

* Bzl formatter fix.
diff --git a/fuzzing/private/oss_fuzz/repository.bzl b/fuzzing/private/oss_fuzz/repository.bzl
index 80286d0..8f812f6 100644
--- a/fuzzing/private/oss_fuzz/repository.bzl
+++ b/fuzzing/private/oss_fuzz/repository.bzl
@@ -17,9 +17,38 @@
 def _to_list_repr(elements):
     return ", ".join([repr(element) for element in elements])
 
+def _get_machine_arch(repository_ctx):
+    result = repository_ctx.execute(["uname", "-m"])
+    if result.return_code != 0:
+        fail("Could not obtain machine architecture: %s" % result.stderr)
+    return result.stdout.strip()
+
+def _ubsan_standalone_cxx_lib_name(arch):
+    return "libclang_rt.ubsan_standalone_cxx-%s.a" % arch
+
+def _find_llvm_lib(repository_ctx, target_file):
+    result = repository_ctx.execute([
+        repository_ctx.which("bash"),
+        "-c",
+        """
+            set -euf -o pipefail
+            set -x
+            find "$({llvm_config} --libdir)" -name {target_file} | head -1
+        """.format(
+            llvm_config = "llvm-config",
+            target_file = target_file,
+        ),
+    ], quiet = False)
+    file_path = result.stdout.strip()
+
+    if result.return_code != 0 or not file_path:
+        fail("Could not find LLVM library '%s'" % target_file)
+    return file_path
+
 def _extract_build_params(
         repository_ctx,
         fuzzing_engine_library,
+        sanitizer,
         cflags,
         cxxflags):
     stub_srcs = []
@@ -27,6 +56,18 @@
     instrum_conlyopts = []
     instrum_cxxopts = []
 
+    if sanitizer == "undefined":
+        ubsan_lib_base_name = _ubsan_standalone_cxx_lib_name(_get_machine_arch(repository_ctx))
+
+        # The Clang linker does not link the UBSAN runtime library by default.
+        # We force an explicit linking here.
+        ubsan_lib_path = _find_llvm_lib(
+            repository_ctx,
+            ubsan_lib_base_name,
+        )
+        repository_ctx.symlink(repository_ctx.path(ubsan_lib_path), ubsan_lib_base_name)
+        stub_srcs.append(ubsan_lib_base_name)
+
     if fuzzing_engine_library:
         if fuzzing_engine_library.startswith("-"):
             # This is actually a flag, add it to the linker flags.
@@ -39,6 +80,7 @@
             stub_srcs.append("oss_fuzz_engine.a")
         else:
             fail("Unsupported $LIB_FUZZING_ENGINE value '%s'" % fuzzing_engine_library)
+
     for cflag in cflags:
         # Skip the fuzzing build more flag, since it is separately controlled
         # by the --//fuzzing:cc_fuzzing_build_mode configuration flag.
@@ -64,12 +106,14 @@
 def _oss_fuzz_repository(repository_ctx):
     environ = repository_ctx.os.environ
     fuzzing_engine_library = environ.get("LIB_FUZZING_ENGINE")
+    sanitizer = environ.get("SANITIZER")
     cflags = environ.get("FUZZING_CFLAGS") or environ.get("CFLAGS", "")
     cxxflags = environ.get("FUZZING_CXXFLAGS") or environ.get("CXXFLAGS", "")
 
     build_params = _extract_build_params(
         repository_ctx,
         fuzzing_engine_library,
+        sanitizer,
         cflags.split(" "),
         cxxflags.split(" "),
     )