diff --git a/.bazelrc b/.bazelrc index e9aea36b6852f..58a2321fc1456 100644 --- a/.bazelrc +++ b/.bazelrc @@ -56,6 +56,9 @@ build:asan --features=asan # Configuration for enabling LibFuzzer (along with ASan). build:fuzzer --features=fuzzer +# Proto fuzzer specific configuration. +build:proto-fuzzer --features=proto-fuzzer + # Always allow tests to symbolize themselves with whatever `llvm-symbolize` is # in the users environment. build --test_env=ASAN_SYMBOLIZER_PATH diff --git a/WORKSPACE b/WORKSPACE index c781ede63b82f..e21fbd72390e2 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -248,6 +248,20 @@ rules_proto_dependencies() rules_proto_toolchains() +############################################################################### +# libprotobuf_mutator - for structured fuzzer testing. +############################################################################### + +libprotobuf_mutator_version = "1.0" + +http_archive( + name = "com_google_libprotobuf_mutator", + build_file = "@//:third_party/libprotobuf_mutator/BUILD.txt", + sha256 = "792f250fb546bde8590e72d64311ea00a70c175fd77df6bb5e02328fa15fe28e", + strip_prefix = "libprotobuf-mutator-%s" % libprotobuf_mutator_version, + urls = ["https://github.com/google/libprotobuf-mutator/archive/v%s.tar.gz" % libprotobuf_mutator_version], +) + ############################################################################### # Example conversion repositories ############################################################################### diff --git a/bazel/cc_toolchains/clang_cc_toolchain_config.bzl b/bazel/cc_toolchains/clang_cc_toolchain_config.bzl index 8e5ab99b115fc..e6ccfa51984ba 100644 --- a/bazel/cc_toolchains/clang_cc_toolchain_config.bzl +++ b/bazel/cc_toolchains/clang_cc_toolchain_config.bzl @@ -482,6 +482,17 @@ def _impl(ctx): )], ) + proto_fuzzer = feature( + name = "proto-fuzzer", + enabled = False, + requires = [feature_set(["nonhost"])], + + # TODO: this should really be `fuzzer`, but `-fsanitize=fuzzer` triggers + # a clang crash when running `bazel test --config=fuzzer ...`. See + # https://github.com/carbon-language/carbon-lang/issues/1173 + implies = ["asan"], + ) + linux_flags_feature = feature( name = "linux_flags", enabled = True, @@ -528,7 +539,12 @@ def _impl(ctx): "-D_LIBCPP_DEBUG=1", ])], with_features = [ - with_feature_set(not_features = ["opt"]), + # _LIBCPP_DEBUG=1 causes protobuf code to crash when linked + # with `-fsanitize=fuzzer`, possibly because of ODR + # violations caused by Carbon source and pre-compiled llvm + # Fuzzer driver library built with different _LIBCPP_DEBUG + # values. + with_feature_set(not_features = ["opt", "proto-fuzzer"]), ], ), ], @@ -751,6 +767,7 @@ def _impl(ctx): asan, enable_asan_in_fastbuild, fuzzer, + proto_fuzzer, layering_check, module_maps, use_module_maps, diff --git a/executable_semantics/BUILD b/executable_semantics/BUILD index 28e810677440c..1405b195d14e8 100644 --- a/executable_semantics/BUILD +++ b/executable_semantics/BUILD @@ -17,13 +17,13 @@ filegroup( cc_binary( name = "executable_semantics", srcs = ["main.cpp"], - data = [":standard_libraries"], deps = [ "//common:error", "//executable_semantics/common:arena", "//executable_semantics/common:nonnull", "//executable_semantics/interpreter:exec_program", "//executable_semantics/syntax", + "//executable_semantics/syntax:prelude", "@llvm-project//llvm:Support", ], ) diff --git a/executable_semantics/fuzzing/BUILD b/executable_semantics/fuzzing/BUILD index f231592ccbdeb..315775bee1aa5 100644 --- a/executable_semantics/fuzzing/BUILD +++ b/executable_semantics/fuzzing/BUILD @@ -2,6 +2,8 @@ # Exceptions. See /LICENSE for license information. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +load("//bazel/fuzzing:rules.bzl", "cc_fuzz_test") + cc_library( name = "ast_to_proto_lib", srcs = ["ast_to_proto.cpp"], @@ -15,6 +17,18 @@ cc_library( ], ) +cc_library( + name = "fuzzer_util", + srcs = ["fuzzer_util.cpp"], + hdrs = ["fuzzer_util.h"], + deps = [ + "//common:check", + "//common/fuzzing:carbon_cc_proto", + "//common/fuzzing:proto_to_carbon_lib", + "@llvm-project//llvm:Support", + ], +) + cc_test( name = "ast_to_proto_test", srcs = ["ast_to_proto_test.cpp"], @@ -36,6 +50,22 @@ cc_test( ], ) +cc_binary( + name = "fuzzverter", + srcs = ["fuzzverter.cpp"], + deps = [ + ":ast_to_proto_lib", + ":fuzzer_util", + "//common:error", + "//common/fuzzing:carbon_cc_proto", + "//executable_semantics/common:error", + "//executable_semantics/common:nonnull", + "//executable_semantics/syntax", + "@com_google_protobuf//:protobuf_headers", + "@llvm-project//llvm:Support", + ], +) + cc_test( name = "proto_to_carbon_test", srcs = ["proto_to_carbon_test.cpp"], @@ -57,3 +87,22 @@ cc_test( "@llvm-project//llvm:Support", ], ) + +# Needs `--config=proto-fuzzer` for `bazel build` / `bazel test`. +cc_fuzz_test( + name = "executable_semantics_fuzzer", + size = "small", + srcs = ["executable_semantics_fuzzer.cpp"], + corpus = glob(["fuzzer_corpus/**"]), + tags = ["manual"], + deps = [ + ":fuzzer_util", + "//common/fuzzing:carbon_cc_proto", + "//executable_semantics/interpreter:exec_program", + "//executable_semantics/syntax", + "//executable_semantics/syntax:prelude", + "@com_google_libprotobuf_mutator//:libprotobuf_mutator", + "@com_google_protobuf//:protobuf_headers", + "@llvm-project//llvm:Support", + ], +) diff --git a/executable_semantics/fuzzing/README.md b/executable_semantics/fuzzing/README.md new file mode 100644 index 0000000000000..96baf9d273f04 --- /dev/null +++ b/executable_semantics/fuzzing/README.md @@ -0,0 +1,89 @@ +# Executable semantics structured fuzzer + + + +## Overview + +Fuzz testing is based on generating a large amount of random inputs for a +software component in order to trigger bugs and unexpected behavior. Basic +fuzzing uses randomly generated arrays of bytes as inputs, which works great for +some applications but is problematic for testing the logic that operates on +highly structured data, as most random inputs are immediately rejected as +invalid before any interesting parts of the code get a chance to run. + +Structured fuzzing addresses this issue by ensuring the randomly generated data +is itself structured, and as such has a high chance of presenting a valid input. + +`executable_semantics_fuzzer` is a structured fuzzer based on +[libprotobuf-mutator](https://github.com/google/libprotobuf-mutator), which is a +library to randomly mutate +[protobuffers](https://github.com/protocolbuffers/protobuf). + +The input to the fuzzer is an instance of `Carbon::Fuzzing::Carbon` proto +randomly generated by the `libprotobuf-mutator` framework. +`executable_semantics_fuzzer` converts the proto to a Carbon source code string, +and tries to parse and execute the code using `executable_semantics` +implementation. + +## Fuzzer data format + +`libprotobuf-mutator` supports fuzzer inputs in either text or binary protocol +buffer format. `executable_semantics_fuzzer` uses text proto format with +`Carbon` proto message definition in `common/fuzzing/carbon.proto`. + +## Running the fuzzer + +The fuzzer can be run in 'unit test' mode, where the fuzzer executes on each +input file from the `fuzzer_corpus/` folder, or in 'fuzzing' mode, where the +fuzzer will keep generating random inputs and executing the logic on them until +a crash is triggered, or forever in a bug-free program ;). + +To run in 'unit test' mode: + +```bash +bazel test --config=proto-fuzzer --test_output=all //executable_semantics/fuzzing:executable_semantics_fuzzer +``` + +To run in 'fuzzing' mode: + +```bash +bazel build --config=proto-fuzzer //executable_semantics/fuzzing:executable_semantics_fuzzer + +bazel-bin/executable_semantics/fuzzing/executable_semantics_fuzzer +``` + +It's also possible to run the fuzzer on a single input: + +```bash +bazel-bin/executable_semantics/fuzzing/executable_semantics_fuzzer /tmp/crash.textproto +``` + +## Investigating a crash + +To reproduce a crash, run the fuzzer on the crashing input as described above. + +A separate tool called `fuzzverter` can be used for things like converting a +crashing input to Carbon source code for running `executable_semantics` on the +code directly. + +To convert a `Fuzzing::Carbon` text proto to Carbon source: + +```bash +bazel-bin/executable_semantics/fuzzing/fuzzverter --mode proto_to_carbon --input /tmp/crash.textproto +``` + +## Generating new fuzzer corpus entries + +The ability of the fuzzing framework to generate 'interesting' inputs can be +improved by providing 'seed' inputs known as the fuzzer corpus. The inputs need +to be a `Fuzzing::Carbon` text proto. + +To generate a text proto from Carbon source: + +```bash +bazel-bin/executable_semantics/fuzzing/fuzzverter --mode carbon_to_proto --input /tmp/crash.carbon --output /tmp/crash.textproto +``` diff --git a/executable_semantics/fuzzing/executable_semantics_fuzzer.cpp b/executable_semantics/fuzzing/executable_semantics_fuzzer.cpp new file mode 100644 index 0000000000000..b17a0e420425f --- /dev/null +++ b/executable_semantics/fuzzing/executable_semantics_fuzzer.cpp @@ -0,0 +1,42 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include + +#include "common/fuzzing/carbon.pb.h" +#include "executable_semantics/fuzzing/fuzzer_util.h" +#include "executable_semantics/interpreter/exec_program.h" +#include "executable_semantics/syntax/parse.h" +#include "executable_semantics/syntax/prelude.h" +#include "llvm/Support/raw_ostream.h" + +namespace Carbon { + +// Parses and executes a fuzzer-generated program. +void ParseAndExecute(const Fuzzing::CompilationUnit& compilation_unit) { + const std::string source = ProtoToCarbonWithMain(compilation_unit); + + Arena arena; + ErrorOr ast = ParseFromString(&arena, "Fuzzer.carbon", source, + /*trace=*/false); + if (!ast.ok()) { + llvm::errs() << "Parsing failed: " << ast.error().message() << "\n"; + return; + } + AddPrelude("executable_semantics/data/prelude.carbon", &arena, + &ast->declarations); + const ErrorOr result = ExecProgram(&arena, *ast, /*trace=*/false); + if (!result.ok()) { + llvm::errs() << "Execution failed: " << result.error().message() << "\n"; + return; + } + llvm::outs() << "Executed OK: " << *result << "\n"; +} + +} // namespace Carbon + +DEFINE_TEXT_PROTO_FUZZER(const Carbon::Fuzzing::Carbon& input) { + Carbon::ParseAndExecute(input.compilation_unit()); +} diff --git a/executable_semantics/fuzzing/fuzzer_corpus/empty.textproto b/executable_semantics/fuzzing/fuzzer_corpus/empty.textproto new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/executable_semantics/fuzzing/fuzzer_util.cpp b/executable_semantics/fuzzing/fuzzer_util.cpp new file mode 100644 index 0000000000000..0b832716dfc5c --- /dev/null +++ b/executable_semantics/fuzzing/fuzzer_util.cpp @@ -0,0 +1,32 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "executable_semantics/fuzzing/fuzzer_util.h" + +#include "common/check.h" +#include "common/fuzzing/proto_to_carbon.h" + +namespace Carbon { + +// Appended to fuzzer-generated Carbon source when the source is missing +// `Main()` definition, to prevent early error return in semantic analysis. +static constexpr char EmptyMain[] = R"( +fn Main() -> i32 { + return 0; +} +)"; + +auto ProtoToCarbonWithMain(const Fuzzing::CompilationUnit& compilation_unit) + -> std::string { + const bool has_main = std::any_of( + compilation_unit.declarations().begin(), + compilation_unit.declarations().end(), + [](const Fuzzing::Declaration& decl) { + return decl.kind_case() == Fuzzing::Declaration::kFunction && + decl.function().name() == "Main"; + }); + return Carbon::ProtoToCarbon(compilation_unit) + (has_main ? "" : EmptyMain); +} + +} // namespace Carbon diff --git a/executable_semantics/fuzzing/fuzzer_util.h b/executable_semantics/fuzzing/fuzzer_util.h new file mode 100644 index 0000000000000..931f9f07e9ec3 --- /dev/null +++ b/executable_semantics/fuzzing/fuzzer_util.h @@ -0,0 +1,19 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef EXECUTABLE_SEMANTICS_FUZZING_FUZZER_UTIL_H_ +#define EXECUTABLE_SEMANTICS_FUZZING_FUZZER_UTIL_H_ + +#include "common/fuzzing/carbon.pb.h" + +namespace Carbon { + +// Converts `compilation_unit` to Carbon. Adds an default `Main()` +// definition if one is not present in the proto. +auto ProtoToCarbonWithMain(const Fuzzing::CompilationUnit& compilation_unit) + -> std::string; + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_FUZZING_FUZZER_UTIL_H_ diff --git a/executable_semantics/fuzzing/fuzzverter.cpp b/executable_semantics/fuzzing/fuzzverter.cpp new file mode 100644 index 0000000000000..47575eda2a3a5 --- /dev/null +++ b/executable_semantics/fuzzing/fuzzverter.cpp @@ -0,0 +1,136 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// An utility for converting between fuzzer protos and Carbon sources. +// +// For example, to convert a crashing input in text proto to carbon source: +// `fuzzverter --mode=proto_to_carbon --input file.textproto` +// +// To generate a new text proto from carbon source for seeding the corpus: +// `fuzzverter --mode=carbon_to_proto --input file.carbon` + +#include + +#include +#include +#include +#include + +#include "common/error.h" +#include "common/fuzzing/carbon.pb.h" +#include "executable_semantics/common/error.h" +#include "executable_semantics/fuzzing/ast_to_proto.h" +#include "executable_semantics/fuzzing/fuzzer_util.h" +#include "executable_semantics/syntax/parse.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" + +namespace Carbon { + +// Reads a file and returns its contents as a string. +static auto ReadFile(std::string_view file_name) -> ErrorOr { + std::ifstream file(file_name, std::ios::in); + if (!file.is_open()) { + return ErrorBuilder() << "Could not open " << file_name << " for reading"; + } + std::stringstream ss; + ss << file.rdbuf(); + return ss.str(); +} + +// Writes string `s` to `file_name`. +static auto WriteFile(std::string_view s, std::string_view file_name) + -> ErrorOr { + std::ofstream file(file_name, std::ios::out); + if (!file.is_open()) { + return ErrorBuilder() << "Could not open " << file_name << " for writing"; + } + file << s; + return Success(); +} + +// Converts text proto to Carbon source. +static auto TextProtoToCarbon(std::string_view input_file_name, + std::string_view output_file_name) + -> ErrorOr { + ASSIGN_OR_RETURN(const std::string input_contents, ReadFile(input_file_name)); + Fuzzing::Carbon carbon_proto; + if (!google::protobuf::TextFormat::ParseFromString(input_contents, + &carbon_proto)) { + return Error("Could not parse text proto"); + } + const std::string carbon_source = + ProtoToCarbonWithMain(carbon_proto.compilation_unit()); + return WriteFile(carbon_source, output_file_name); +} + +// Converts Carbon source to text proto. +static auto CarbonToTextProto(std::string_view input_file_name, + std::string_view output_file_name) + -> ErrorOr { + Carbon::Arena arena; + const ErrorOr ast = Carbon::Parse(&arena, input_file_name, + /*trace=*/false); + if (!ast.ok()) { + return ErrorBuilder() << "Parsing failed: " << ast.error().message(); + } + Fuzzing::Carbon carbon_proto; + *carbon_proto.mutable_compilation_unit() = AstToProto(*ast); + + std::string proto_string; + google::protobuf::TextFormat::Printer p; + if (!p.PrintToString(carbon_proto, &proto_string)) { + return Error("Failed to convert to text proto"); + } + return WriteFile(proto_string, output_file_name); +} + +// Command line options for defining input/output format. +enum class ConversionMode { TextProtoToCarbon, CarbonToTextProto }; + +// Returns string representation of an enum option. +static auto GetEnumString(llvm::cl::opt& o) -> llvm::StringRef { + // TODO: is there a better way? + return o.getParser().getOption(static_cast(ConversionMode(o))); +} + +// Performs the conversion specified by `mode`. +auto Convert(const ConversionMode mode, std::string_view input_file_name, + std::string_view output_file_name) -> ErrorOr { + switch (mode) { + case ConversionMode::TextProtoToCarbon: + return TextProtoToCarbon(input_file_name, output_file_name); + case ConversionMode::CarbonToTextProto: + return CarbonToTextProto(input_file_name, output_file_name); + } +} + +} // namespace Carbon + +auto main(int argc, char* argv[]) -> int { + llvm::InitLLVM init_llvm(argc, argv); + + llvm::cl::opt mode( + "mode", llvm::cl::desc("Conversion mode"), + llvm::cl::values( + clEnumValN(Carbon::ConversionMode::TextProtoToCarbon, + "proto_to_carbon", "Convert text proto to Carbon source"), + clEnumValN(Carbon::ConversionMode::CarbonToTextProto, + "carbon_to_proto", + "Convert Carbon source to text proto"))); + llvm::cl::opt input_file_name( + "input", llvm::cl::desc(""), llvm::cl::init("/dev/stdin")); + llvm::cl::opt output_file_name( + "output", llvm::cl::desc(""), llvm::cl::init("/dev/stdout")); + llvm::cl::ParseCommandLineOptions(argc, argv); + + if (const auto result = + Carbon::Convert(mode, input_file_name, output_file_name); + !result.ok()) { + llvm::errs() << GetEnumString(mode) + << " conversion failed: " << result.error().message() << "\n"; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/executable_semantics/interpreter/BUILD b/executable_semantics/interpreter/BUILD index 412992e61c8e3..5cc22a7f34b00 100644 --- a/executable_semantics/interpreter/BUILD +++ b/executable_semantics/interpreter/BUILD @@ -68,6 +68,10 @@ cc_library( name = "exec_program", srcs = ["exec_program.cpp"], hdrs = ["exec_program.h"], + visibility = [ + "//executable_semantics:__pkg__", + "//executable_semantics/fuzzing:__pkg__", + ], deps = [ ":interpreter", ":resolve_control_flow", diff --git a/executable_semantics/main.cpp b/executable_semantics/main.cpp index 61e9a14ef1bcc..bff3f2646b037 100644 --- a/executable_semantics/main.cpp +++ b/executable_semantics/main.cpp @@ -15,27 +15,10 @@ #include "executable_semantics/common/nonnull.h" #include "executable_semantics/interpreter/exec_program.h" #include "executable_semantics/syntax/parse.h" +#include "executable_semantics/syntax/prelude.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" -// Adds the Carbon prelude to `declarations`. -static void AddPrelude( - std::string_view prelude_file_name, Carbon::Nonnull arena, - std::vector>* declarations) { - Carbon::ErrorOr parse_result = - Carbon::Parse(arena, prelude_file_name, false); - if (!parse_result.ok()) { - // Try again with tracing, to help diagnose the problem. - Carbon::ErrorOr trace_parse_result = - Carbon::Parse(arena, prelude_file_name, true); - FATAL() << "Failed to parse prelude: " - << trace_parse_result.error().message(); - } - const auto& prelude = *parse_result; - declarations->insert(declarations->begin(), prelude.declarations.begin(), - prelude.declarations.end()); -} - // Prints an error message and returns error code value. auto PrintError(const Carbon::Error& error) -> int { llvm::errs() << error.message() << "\n"; diff --git a/executable_semantics/syntax/BUILD b/executable_semantics/syntax/BUILD index 36e12fda8e52b..f22fa09e23fbf 100644 --- a/executable_semantics/syntax/BUILD +++ b/executable_semantics/syntax/BUILD @@ -37,6 +37,20 @@ cc_library( ], ) +cc_library( + name = "prelude", + srcs = ["prelude.cpp"], + hdrs = ["prelude.h"], + data = ["//executable_semantics:standard_libraries"], + deps = [ + ":syntax", + "//common:error", + "//executable_semantics/ast:declaration", + "//executable_semantics/common:arena", + "//executable_semantics/common:nonnull", + ], +) + cc_library( name = "syntax", srcs = [ diff --git a/executable_semantics/syntax/prelude.cpp b/executable_semantics/syntax/prelude.cpp new file mode 100644 index 0000000000000..266719f3929dd --- /dev/null +++ b/executable_semantics/syntax/prelude.cpp @@ -0,0 +1,26 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "executable_semantics/syntax/prelude.h" + +#include "executable_semantics/syntax/parse.h" + +namespace Carbon { + +// Adds the Carbon prelude to `declarations`. +void AddPrelude(std::string_view prelude_file_name, Nonnull arena, + std::vector>* declarations) { + ErrorOr parse_result = Parse(arena, prelude_file_name, false); + if (!parse_result.ok()) { + // Try again with tracing, to help diagnose the problem. + ErrorOr trace_parse_result = Parse(arena, prelude_file_name, true); + FATAL() << "Failed to parse prelude: " + << trace_parse_result.error().message(); + } + const auto& prelude = *parse_result; + declarations->insert(declarations->begin(), prelude.declarations.begin(), + prelude.declarations.end()); +} + +} // namespace Carbon diff --git a/executable_semantics/syntax/prelude.h b/executable_semantics/syntax/prelude.h new file mode 100644 index 0000000000000..ffb6daf60e43d --- /dev/null +++ b/executable_semantics/syntax/prelude.h @@ -0,0 +1,22 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifndef EXECUTABLE_SEMANTICS_SYNTAX_PRELUDE_H_ +#define EXECUTABLE_SEMANTICS_SYNTAX_PRELUDE_H_ + +#include + +#include "executable_semantics/ast/declaration.h" +#include "executable_semantics/common/arena.h" +#include "executable_semantics/common/nonnull.h" + +namespace Carbon { + +// Adds the Carbon prelude to `declarations`. +void AddPrelude(std::string_view prelude_file_name, Nonnull arena, + std::vector>* declarations); + +} // namespace Carbon + +#endif // EXECUTABLE_SEMANTICS_SYNTAX_PRELUDE_H_ diff --git a/scripts/fix_cc_deps.py b/scripts/fix_cc_deps.py index 55dd641adfc4a..b0ce4c6e45b21 100755 --- a/scripts/fix_cc_deps.py +++ b/scripts/fix_cc_deps.py @@ -31,6 +31,11 @@ # @com_google_protobuf//:src/google/protobuf/descriptor.h -> # google/protobuf/descriptor.h "@com_google_protobuf": lambda x: re.sub("^(.*:src)/", "", x), + # @com_google_libprotobuf_mutator//:src/libfuzzer/libfuzzer_macro.h -> + # libprotobuf_mutator/src/libfuzzer/libfuzzer_macro.h + "@com_google_libprotobuf_mutator": lambda x: re.sub( + "^(.*:)", "libprotobuf_mutator/", x + ), } # TODO: proto rules are aspect-based and their generated files don't show up in diff --git a/third_party/libprotobuf_mutator/BUILD.txt b/third_party/libprotobuf_mutator/BUILD.txt new file mode 100644 index 0000000000000..02bf90e50ab61 --- /dev/null +++ b/third_party/libprotobuf_mutator/BUILD.txt @@ -0,0 +1,22 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# libprotobuf_mutator uses cmake and doesn't provide a bazel BUILD file. +# See https://github.com/google/libprotobuf-mutator/issues/91. + +cc_library( + name = "libprotobuf_mutator", + srcs = glob( + [ + "src/**/*.cc", + "src/**/*.h", + "port/protobuf.h", + ], + exclude = ["**/*_test.cc"], + ), + hdrs = ["src/libfuzzer/libfuzzer_macro.h"], + include_prefix = "libprotobuf_mutator", + visibility = ["//visibility:public"], + deps = ["@com_google_protobuf//:protobuf"], +)