diff --git a/testing/libfuzzer/libprotobuf-mutator.md b/testing/libfuzzer/libprotobuf-mutator.md index 94d7d284d5c006..7837d7028de7f7 100644 --- a/testing/libfuzzer/libprotobuf-mutator.md +++ b/testing/libfuzzer/libprotobuf-mutator.md @@ -46,9 +46,9 @@ url_parse_proto_fuzzer. ## Write a fuzz target for code that accepts protobufs This is almost as easy as writing a standard libFuzzer-based fuzzer. You can -look at [override_lite_runtime_plugin_test_fuzzer] for an example of a working -example of this (don't copy the line adding "//testing/libfuzzer:no_clusterfuzz" -to additional_configs). Or you can follow this walkthrough: +look at [lpm_test_fuzzer] for an example of a working example of this (don't +copy the line adding "//testing/libfuzzer:no_clusterfuzz" to +additional_configs). Or you can follow this walkthrough: Start by creating a fuzz target. This is what the .cc file will look like: @@ -365,5 +365,5 @@ fuzzer). [this]: https://github.com/google/libprotobuf-mutator/tree/master/examples/libfuzzer/libfuzzer_example.cc [existing proto fuzzers]: https://cs.chromium.org/search/?q=DEFINE_(BINARY_%7CTEXT_)?PROTO_FUZZER+-file:src/third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h+lang:cpp&sq=package:chromium&type=cs [here]: https://github.com/google/libprotobuf-mutator/blob/master/README.md#utf-8-strings -[override_lite_runtime_plugin_test_fuzzer]: https://cs.chromium.org/#search&q=override_lite_runtime_plugin_test_fuzzer+file:%5Esrc/third_party/libprotobuf-mutator/BUILD.gn +[lpm_test_fuzzer]: https://cs.chromium.org/#search&q=lpm_test_fuzzer+file:%5Esrc/third_party/libprotobuf-mutator/BUILD.gn [mojo_parse_messages_proto_fuzzer]: https://cs.chromium.org/chromium/src/mojo/public/tools/fuzzers/mojo_parse_message_proto_fuzzer.cc?l=25 diff --git a/third_party/libprotobuf-mutator/BUILD.gn b/third_party/libprotobuf-mutator/BUILD.gn index 25a7e9dbbf71d6..0a8da68db61b75 100644 --- a/third_party/libprotobuf-mutator/BUILD.gn +++ b/third_party/libprotobuf-mutator/BUILD.gn @@ -40,39 +40,18 @@ source_set("libprotobuf-mutator") { } } -# This protoc plugin, like the compiler, should only be built for the host -# architecture. -if (current_toolchain == host_toolchain) { - # This plugin will be needed to fuzz most protobuf code in Chromium. That's - # because production protobuf code must contain the line: - # "option optimize_for = LITE_RUNTIME", which instructs the proto compiler not - # to compile the proto using the full protobuf runtime. This allows Chromium - # not to depend on the full protobuf library, but prevents - # libprotobuf-mutator from fuzzing because the lite runtime lacks needed - # features (such as reflection). The plugin simply compiles a proto library - # as normal but ensures that is compiled with the full protobuf runtime. - executable("override_lite_runtime_plugin") { - sources = [ "protoc_plugin/protoc_plugin.cc" ] - deps = [ "//third_party/protobuf:protoc_lib" ] - public_configs = [ "//third_party/protobuf:protobuf_config" ] - } - # To use the plugin in a proto_library you want to fuzz, change the build - # target to fuzzable_proto_library (defined in - # //third_party/libprotobuf-mutator/fuzzable_proto_library.gni) -} - # The CQ will try building this target without "use_libfuzzer" if it is defined. # That will cause the build to fail, so don't define it when "use_libfuzzer" is # is false. if (use_libfuzzer) { - # Test that override_lite_runtime_plugin is working when built. This target - # contains files that are optimized for LITE_RUNTIME and which import other - # files that are also optimized for LITE_RUNTIME. - fuzzer_test("override_lite_runtime_plugin_test_fuzzer") { - sources = [ "protoc_plugin/test_fuzzer.cc" ] + # Test that fuzzable_proto_library works. This target contains files that are + # optimized for LITE_RUNTIME and which import other files that are also + # optimized for LITE_RUNTIME. + fuzzer_test("lpm_test_fuzzer") { + sources = [ "test_fuzzer/test_fuzzer.cc" ] deps = [ ":libprotobuf-mutator", - ":override_lite_runtime_plugin_test_fuzzer_proto", + ":lpm_test_fuzzer_proto", ] # Don't actually run this on CF. It's only a test to ensure builds work. @@ -80,12 +59,12 @@ if (use_libfuzzer) { } } -# Proto library for override_lite_runtime_plugin_test_fuzzer -fuzzable_proto_library("override_lite_runtime_plugin_test_fuzzer_proto") { +# Proto library for lpm_test_fuzzer +fuzzable_proto_library("lpm_test_fuzzer_proto") { sources = [ - "protoc_plugin/imported.proto", - "protoc_plugin/imported_publicly.proto", - "protoc_plugin/test_fuzzer_input.proto", + "test_fuzzer/imported.proto", + "test_fuzzer/imported_publicly.proto", + "test_fuzzer/test_fuzzer_input.proto", ] } diff --git a/third_party/libprotobuf-mutator/fuzzable_proto_library.gni b/third_party/libprotobuf-mutator/fuzzable_proto_library.gni index c13d91be7c93e6..b9c44537d834fb 100644 --- a/third_party/libprotobuf-mutator/fuzzable_proto_library.gni +++ b/third_party/libprotobuf-mutator/fuzzable_proto_library.gni @@ -6,7 +6,7 @@ # non-fuzzer builds (ie: use_libfuzzer=false). However, in fuzzer builds, the # proto_library is built with the full protobuf runtime and any "optimize_for = # LITE_RUNTIME" options are ignored. This is done because libprotobuf-mutator -# needs the full protobuf runtime, but proto_libraries shipped in chrome must +# needs the full protobuf runtime, but proto_libraries shipped in Chrome must # use the optimize for LITE_RUNTIME option which is incompatible with the full # protobuf runtime. tl;dr: A fuzzable_proto_library is a proto_library that can # be fuzzed with libprotobuf-mutator and shipped in Chrome. @@ -14,6 +14,7 @@ import("//testing/libfuzzer/fuzzer_test.gni") import("//third_party/protobuf/proto_library.gni") +# TODO(https://crbug.com/1197634): Fold this into proto_library. template("fuzzable_proto_library") { # Only make the proto library fuzzable if we are doing a build that we can # use LPM on (i.e. libFuzzer not on Chrome OS). @@ -21,20 +22,9 @@ template("fuzzable_proto_library") { proto_library("proto_library_" + target_name) { forward_variables_from(invoker, "*") assert(current_toolchain == host_toolchain) - if (!defined(proto_deps)) { - proto_deps = [] - } - proto_deps += - [ "//third_party/libprotobuf-mutator:override_lite_runtime_plugin" ] - generator_plugin_label = - "//third_party/libprotobuf-mutator:override_lite_runtime_plugin" - generator_plugin_suffix = ".pb" - # The plugin will generate cc, so don't ask for it to be done by protoc. - generate_cc = false - if (!defined(invoker.generate_python)) { - generate_python = false - } + # Override LITE_RUNTIME settings in the protobuf files. + cc_generator_options = "speed" extra_configs = [ "//third_party/protobuf:protobuf_config" ] } diff --git a/third_party/libprotobuf-mutator/protoc_plugin/protoc_plugin.cc b/third_party/libprotobuf-mutator/protoc_plugin/protoc_plugin.cc deleted file mode 100644 index 92f94f4c637171..00000000000000 --- a/third_party/libprotobuf-mutator/protoc_plugin/protoc_plugin.cc +++ /dev/null @@ -1,124 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Plugin for the protobuf compiler (protoc) that ensures proto definitions are -// compiled in a way that they can be used with libprotobuf-mutator. Compiles -// protobufs to C++ like the normal protoc (using the cpp plugin). - -#include -#include -#include -#include - -#include "third_party/protobuf/src/google/protobuf/compiler/cpp/cpp_generator.h" -#include "third_party/protobuf/src/google/protobuf/compiler/plugin.h" -#include "third_party/protobuf/src/google/protobuf/descriptor.h" -#include "third_party/protobuf/src/google/protobuf/descriptor.pb.h" - -using google::protobuf::FileDescriptor; -using google::protobuf::compiler::GeneratorContext; -using google::protobuf::compiler::cpp::CppGenerator; - -// Class that generates C++ code that can be used by LPM from proto libraries. -class LpmCppCodeGenerator : public CppGenerator { - public: - // Overrides the GenerateAll method on CppGenerator. This method essentially - // does the same thing except it ensures that files are not optimized for - // LITE_RUNTIME. - virtual bool GenerateAll(const std::vector& files, - const std::string& parameter, - GeneratorContext* generator_context, - std::string* error) const { - if (files.size() == 0) - return true; - - // Created a DescriptorPool once here so that modified files will use the - // modified versions when importing. - google::protobuf::DescriptorPool descriptor_pool(files[0]->pool()); - - // Keep a list of files we have generated already, so that - // GenerateFileAndDependencies won't generate the same file twice. - std::unordered_set prev_generated; - - // Mostly copied from GenerateAll from - // //third_party/protobuf/src/google/protobuf/compiler/code_generator.cc - bool succeeded = true; - for (size_t idx = 0; idx < files.size(); idx++) { - const FileDescriptor* file = files[idx]; - succeeded = - GenerateFileAndDependencies(file, parameter, generator_context, error, - &descriptor_pool, &prev_generated); - - if (!succeeded && error && error->empty()) { - *error = - "Code generator returned false but provided no error " - "description."; - } - if (error && !error->empty()) { - *error = file->name() + ": " + *error; - break; - } - if (!succeeded) - break; - } - return succeeded; - } - - // Ensures that file and its dependancies are optimized for LPM by making them - // optimized for speed (as opposed to LITE_RUNTIME which would make file - // usable for LPM) then returns the result of a call to Generate on the - // modified file and the other arguments to this method. Needs to modify - // dependencies before file because protobuf doesn't allow a file to import - // another if file is not optimized_for LITE_RUNTIME but the dependency is. - // Returns true if file is in prev_generated. - virtual bool GenerateFileAndDependencies( - const FileDescriptor* file, - const std::string& parameter, - GeneratorContext* generator_context, - std::string* error, - google::protobuf::DescriptorPool* descriptor_pool, - std::unordered_set* prev_generated) const { - if (prev_generated->find(file) != prev_generated->end()) - return true; - - // Make a copy of the file that we can modify. - google::protobuf::FileDescriptorProto file_proto; - file->CopyTo(&file_proto); - - // Fix all dependencies before fixing this file (A file must be optimized - // for the lite runtime if it imports files that are. - for (int idx = 0; idx < file->dependency_count(); idx++) { - const FileDescriptor* dependent_file = file->dependency(idx); - assert(dependent_file); - bool result = GenerateFileAndDependencies( - dependent_file, parameter, generator_context, error, descriptor_pool, - prev_generated); - assert(result); - if (!result) - return result; - } - - // Base case: - // Now make sure we aren't using the LITE_RUNTIME. - file_proto.mutable_options()->set_optimize_for( - google::protobuf::FileOptions::SPEED); - - // Convert it back to a FileDescriptor and pass it to the parent Generate - // method. - const FileDescriptor* modified_file = - descriptor_pool->BuildFile(file_proto); - assert(modified_file); - - // Ensure we only generate code once for file. - prev_generated->insert(file); - return CppGenerator::Generate(modified_file, parameter, generator_context, - error); - } -}; - -int main(int argc, char** argv) { - // Invoke our lightly modified C++ code generator on the inputs. - LpmCppCodeGenerator generator; - return PluginMain(argc, argv, &generator); -} diff --git a/third_party/libprotobuf-mutator/protoc_plugin/test_fuzzer.cc b/third_party/libprotobuf-mutator/protoc_plugin/test_fuzzer.cc deleted file mode 100644 index 43083982a42ca7..00000000000000 --- a/third_party/libprotobuf-mutator/protoc_plugin/test_fuzzer.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Test fuzzer that when built successfully proves that lpm_protoc_plugin is -// working. Building this fuzzer without using lpm_protoc_plugin will fail -// because of test_fuzzer_input.proto - -#include - -#include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h" - -#include "third_party/libprotobuf-mutator/protoc_plugin/test_fuzzer_input.pb.h" - -DEFINE_PROTO_FUZZER( - const lpm_protoc_plugin_test_fuzzer::TestFuzzerInput& input) { - std::cout << input.imported().imported_publicly().input() << std::endl; -} diff --git a/third_party/libprotobuf-mutator/protoc_plugin/imported.proto b/third_party/libprotobuf-mutator/test_fuzzer/imported.proto similarity index 92% rename from third_party/libprotobuf-mutator/protoc_plugin/imported.proto rename to third_party/libprotobuf-mutator/test_fuzzer/imported.proto index fd9c783bce301e..f347c366b1986c 100644 --- a/third_party/libprotobuf-mutator/protoc_plugin/imported.proto +++ b/third_party/libprotobuf-mutator/test_fuzzer/imported.proto @@ -7,11 +7,11 @@ syntax = "proto2"; option optimize_for = LITE_RUNTIME; -package lpm_protoc_plugin_test_fuzzer; +package lpm_test_fuzzer; // Test public imported files are handled properly. import public "imported_publicly.proto"; message Imported { required ImportedPublicly imported_publicly = 1; -} \ No newline at end of file +} diff --git a/third_party/libprotobuf-mutator/protoc_plugin/imported_publicly.proto b/third_party/libprotobuf-mutator/test_fuzzer/imported_publicly.proto similarity index 91% rename from third_party/libprotobuf-mutator/protoc_plugin/imported_publicly.proto rename to third_party/libprotobuf-mutator/test_fuzzer/imported_publicly.proto index 0af1a2d2156439..107684953f3f22 100644 --- a/third_party/libprotobuf-mutator/protoc_plugin/imported_publicly.proto +++ b/third_party/libprotobuf-mutator/test_fuzzer/imported_publicly.proto @@ -7,7 +7,7 @@ syntax = "proto2"; option optimize_for = LITE_RUNTIME; -package lpm_protoc_plugin_test_fuzzer; +package lpm_test_fuzzer; message ImportedPublicly { required int32 input = 1; diff --git a/third_party/libprotobuf-mutator/test_fuzzer/test_fuzzer.cc b/third_party/libprotobuf-mutator/test_fuzzer/test_fuzzer.cc new file mode 100644 index 00000000000000..a40167a2d1fb0b --- /dev/null +++ b/third_party/libprotobuf-mutator/test_fuzzer/test_fuzzer.cc @@ -0,0 +1,17 @@ +// Copyright 2018 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Test fuzzer that when built successfully proves that fuzzable_proto_library +// is working. Building this fuzzer without using fuzzable_proto_library will +// fail because of test_fuzzer_input.proto + +#include + +#include "third_party/libprotobuf-mutator/src/src/libfuzzer/libfuzzer_macro.h" + +#include "third_party/libprotobuf-mutator/test_fuzzer/test_fuzzer_input.pb.h" + +DEFINE_PROTO_FUZZER(const lpm_test_fuzzer::TestFuzzerInput& input) { + std::cout << input.imported().imported_publicly().input() << std::endl; +} diff --git a/third_party/libprotobuf-mutator/protoc_plugin/test_fuzzer_input.proto b/third_party/libprotobuf-mutator/test_fuzzer/test_fuzzer_input.proto similarity index 56% rename from third_party/libprotobuf-mutator/protoc_plugin/test_fuzzer_input.proto rename to third_party/libprotobuf-mutator/test_fuzzer/test_fuzzer_input.proto index cdc965fcb0a154..716734050bd945 100644 --- a/third_party/libprotobuf-mutator/protoc_plugin/test_fuzzer_input.proto +++ b/third_party/libprotobuf-mutator/test_fuzzer/test_fuzzer_input.proto @@ -2,20 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Depended on by override_lite_runtime_plugin_test_fuzzer. Tests whether -// override_lite_runtime_plugin is working since without it -// builds will fail because of the optimize_for LITE_RUNTIME option this -// file has set. Also imports a file that does the same thing which complicates -// things for the plugin. +// Depended on by lpm_test_fuzzer. Tests whether fuzzable_proto_library is +// working since without it builds will fail because of the optimize_for +// LITE_RUNTIME option this file has set. Also imports a file that does the same +// thing. syntax = "proto2"; -// This line is essentially the purpose of this test fuzzer. The plugin, if +// This line is essentially the purpose of this test fuzzer. The build rule, if // working, ignores this line. If it is not working or isn't used, then this // build will fail. option optimize_for = LITE_RUNTIME; -package lpm_protoc_plugin_test_fuzzer; +package lpm_test_fuzzer; import "imported.proto"; message TestFuzzerInput {