diff --git a/mojo/public/tools/fuzzers/mojolpm.cc b/mojo/public/tools/fuzzers/mojolpm.cc index 32005fe48af377..4863593e1d82bd 100644 --- a/mojo/public/tools/fuzzers/mojolpm.cc +++ b/mojo/public/tools/fuzzers/mojolpm.cc @@ -10,6 +10,10 @@ namespace mojolpm { +const uint32_t kPipeElementMaxSize = 0x1000u; +const uint32_t kPipeCapacityMaxSize = 0x100000u; +const uint32_t kPipeActionMaxSize = 0x100000u; + Context::Context() : message_(0, 0, 0, 0, nullptr) {} Context::~Context() = default; @@ -209,8 +213,10 @@ bool FromProto(const ::mojolpm::DataPipeConsumerHandle& input, options.struct_size = sizeof(MojoCreateDataPipeOptions); options.flags = input.new_().flags(); - options.element_num_bytes = input.new_().element_num_bytes(); - options.capacity_num_bytes = input.new_().capacity_num_bytes(); + options.element_num_bytes = + std::min(input.new_().element_num_bytes(), kPipeElementMaxSize); + options.capacity_num_bytes = + std::min(input.new_().capacity_num_bytes(), kPipeCapacityMaxSize); if (MOJO_RESULT_OK == mojo::CreateDataPipe(&options, &producer, &consumer)) { @@ -246,8 +252,10 @@ bool FromProto(const ::mojolpm::DataPipeProducerHandle& input, options.struct_size = sizeof(MojoCreateDataPipeOptions); options.flags = input.new_().flags(); - options.element_num_bytes = input.new_().element_num_bytes(); - options.capacity_num_bytes = input.new_().capacity_num_bytes(); + options.element_num_bytes = + std::min(input.new_().element_num_bytes(), kPipeElementMaxSize); + options.capacity_num_bytes = + std::min(input.new_().capacity_num_bytes(), kPipeCapacityMaxSize); if (MOJO_RESULT_OK == mojo::CreateDataPipe(&options, &producer, &consumer)) { @@ -310,8 +318,10 @@ void HandleDataPipeRead(const ::mojolpm::DataPipeRead& input) { options.struct_size = sizeof(MojoCreateDataPipeOptions); options.flags = input.handle().new_().flags(); - options.element_num_bytes = input.handle().new_().element_num_bytes(); - options.capacity_num_bytes = input.handle().new_().capacity_num_bytes(); + options.element_num_bytes = std::min( + input.handle().new_().element_num_bytes(), kPipeElementMaxSize); + options.capacity_num_bytes = std::min( + input.handle().new_().capacity_num_bytes(), kPipeCapacityMaxSize); if (MOJO_RESULT_OK == mojo::CreateDataPipe(&options, &producer, &consumer)) { @@ -323,7 +333,10 @@ void HandleDataPipeRead(const ::mojolpm::DataPipeRead& input) { } if (consumer_ptr) { - uint32_t size = input.size(); + unsigned int size = input.size(); + if (size > kPipeActionMaxSize) { + size = kPipeActionMaxSize; + } std::vector data(size); consumer_ptr->get().ReadData(data.data(), &size, 0); } @@ -344,8 +357,10 @@ void HandleDataPipeWrite(const ::mojolpm::DataPipeWrite& input) { options.struct_size = sizeof(MojoCreateDataPipeOptions); options.flags = input.handle().new_().flags(); - options.element_num_bytes = input.handle().new_().element_num_bytes(); - options.capacity_num_bytes = input.handle().new_().capacity_num_bytes(); + options.element_num_bytes = std::min( + input.handle().new_().element_num_bytes(), kPipeElementMaxSize); + options.capacity_num_bytes = std::min( + input.handle().new_().capacity_num_bytes(), kPipeCapacityMaxSize); if (MOJO_RESULT_OK == mojo::CreateDataPipe(&options, &producer, &consumer)) { @@ -357,7 +372,10 @@ void HandleDataPipeWrite(const ::mojolpm::DataPipeWrite& input) { } if (producer_ptr) { - uint32_t size = static_cast(input.data().size()); + unsigned int size = input.data().size(); + if (size > kPipeActionMaxSize) { + size = kPipeActionMaxSize; + } producer_ptr->get().WriteData(input.data().data(), &size, 0); } } diff --git a/mojo/public/tools/fuzzers/mojolpm.gni b/mojo/public/tools/fuzzers/mojolpm.gni new file mode 100644 index 00000000000000..5e2750418f76bb --- /dev/null +++ b/mojo/public/tools/fuzzers/mojolpm.gni @@ -0,0 +1,153 @@ +# Copyright 2020 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. + +import("//testing/libfuzzer/fuzzer_test.gni") +import("//third_party/protobuf/proto_library.gni") +import("//tools/ipc_fuzzer/ipc_fuzzer.gni") + +# Generate a MojoLPM-based fuzzer test. +# +# This rule will copy the proto file defining the fuzzer testcases into the +# output directory so that it can be compiled against the generated MojoLPM +# protos. It then adds a rule to compile that proto, and finally a fuzzer +# test target which uses the compiled proto. +# +# Optionally it can also handle converting a seed corpus of text protos into +# a binary corpus as part of the build. +# +# Parameters: +# sources +# List of source .cc files to compile. +# +# deps +# List of dependencies to compile this target. +# +# proto_source +# Single source .proto file defining the structure of a testcase. +# +# proto_deps +# List of additional dependencies for compiling proto_source. +# +# testcase_proto_kind (optional, required if seed_corpus_sources provided) +# Name of proto message type representing a testcase. +# +# seed_corpus_sources (optional) +# List of source .textproto files used to build a seed corpus. +# +# Example: +# mojolpm_fuzzer_test("foo_mojolpm_fuzzer") { +# sources = [ "foo_mojolpm_fuzzer.cc" ] +# +# deps = [ +# "//content/browser/foo:foo_mojolpm_fuzzer_proto", +# "//content/browser:for_content_tests", +# "//content/public/browser:browser_sources", +# "//content/test:test_support", +# "//mojo/core/embedder", +# "//mojo/public/tools/fuzzers:mojolpm", +# "//third_party/libprotobuf-mutator", +# ] +# +# proto_deps = [ +# "//content/browser/bar/mojom:mojom_mojolpm"," +# ] +# +# testcase_proto = "foo_mojolpm_fuzzer.proto" +# testcase_proto_kind = "foo.mojolpm.proto.Testcase" +# +# seed_corpus_sources = [ +# "foo_mojolpm_fuzzer_corpus/seed_one.textproto", +# "foo_mojolpm_fuzzer_corpus/seed_two.textproto", +# ] +# } +template("mojolpm_fuzzer_test") { + assert(defined(invoker.sources) && defined(invoker.proto_source), + "\"sources\" and \"proto_source\" must be defined for $target_name") + + assert( + !defined(invoker.seed_corpus_sources) || + defined(invoker.testcase_proto_kind), + "\"testcase_proto_kind\" must be defined for $target_name since \"seed_corpus_sources\" is defined.") + + if (enable_ipc_fuzzer) { + proto_copy_target_name = "${target_name}_proto_copy" + proto_target_name = "${target_name}_proto" + + proto_file_name = get_path_info(invoker.proto_source, "file") + proto_source_path = "$root_gen_dir/${proto_file_name}" + + copy(proto_copy_target_name) { + sources = [ invoker.proto_source ] + outputs = [ proto_source_path ] + testonly = true + } + + proto_library(proto_target_name) { + sources = [ proto_source_path ] + generate_python = false + + proto_deps = [ + ":${proto_copy_target_name}", + "//mojo/public/tools/fuzzers:mojolpm_proto_copy", + ] + + link_deps = [] + + if (defined(invoker.proto_deps)) { + proto_deps += invoker.proto_deps + link_deps += invoker.proto_deps + } + + testonly = true + } + + if (defined(invoker.seed_corpus_sources)) { + protoc_convert_target_name = "${target_name}_protoc_convert" + seed_corpus_path = "${target_gen_dir}/${target_name}_seed_corpus" + + protoc_convert(protoc_convert_target_name) { + sources = invoker.seed_corpus_sources + + inputs = [ proto_source_path ] + + output_pattern = "${seed_corpus_path}/{{source_name_part}}.binarypb" + + args = [ + "--encode=${invoker.testcase_proto_kind}", + "-I", + rebase_path("$root_gen_dir"), + rebase_path(inputs[0]), + ] + + deps = [ ":${proto_copy_target_name}" ] + + if (defined(invoker.proto_deps)) { + deps += invoker.proto_deps + } + + testonly = true + } + } + + fuzzer_test(target_name) { + sources = invoker.sources + deps = [ + ":${proto_target_name}", + "//mojo/core/embedder", + "//mojo/public/tools/fuzzers:mojolpm", + "//third_party/libprotobuf-mutator", + ] + if (defined(invoker.deps)) { + deps += invoker.deps + } + + if (defined(invoker.seed_corpus_sources)) { + seed_corpus = seed_corpus_path + seed_corpus_deps = [ ":${protoc_convert_target_name}" ] + } + } + } else { + not_needed(invoker, "*") + } +} diff --git a/testing/libfuzzer/fuzzer_test.gni b/testing/libfuzzer/fuzzer_test.gni index b46b653564ad01..9ea466b86929e2 100644 --- a/testing/libfuzzer/fuzzer_test.gni +++ b/testing/libfuzzer/fuzzer_test.gni @@ -63,6 +63,8 @@ template("fuzzer_test") { action(target_name + "_seed_corpus") { script = "//testing/libfuzzer/archive_corpus.py" + testonly = true + args = [ "--output", rebase_path(out, root_build_dir), diff --git a/third_party/protobuf/proto_library.gni b/third_party/protobuf/proto_library.gni index 44c7fefa25d714..87a94d386f1823 100644 --- a/third_party/protobuf/proto_library.gni +++ b/third_party/protobuf/proto_library.gni @@ -509,6 +509,10 @@ template("protoc_convert") { deps += invoker.deps } + if (defined(invoker.testonly)) { + testonly = invoker.testonly + } + outputs = [ invoker.output_pattern ] args = [