Fuzz BackoffEntrySerializer with net_backoff_entry_serializer_fuzzer.
This fuzzer tests both the "serialize-then-deserialize" and
"deserialize-then-serialize" directions.
Change-Id: I571de9e67377bb811262febd37378f1b06f190b3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2185797
Reviewed-by: Jonathan Metzman <[email protected]>
Reviewed-by: Maksim Orlovich <[email protected]>
Commit-Queue: Dan McArdle <[email protected]>
Cr-Commit-Position: refs/heads/master@{#767349}
diff --git a/net/BUILD.gn b/net/BUILD.gn
index eabb49ce..9712bc3 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -4918,6 +4918,26 @@
]
}
+proto_library("backoff_entry_serializer_fuzzer_input") {
+ proto_in_dir = "//"
+ sources = [ "base/backoff_entry_serializer_fuzzer_input.proto" ]
+ link_deps = [ "//testing/libfuzzer/proto:json_proto" ]
+}
+
+fuzzer_test("net_backoff_entry_serializer_fuzzer") {
+ sources = [ "base/backoff_entry_serializer_fuzzer.cc" ]
+ deps = [
+ ":backoff_entry_serializer_fuzzer_input",
+ ":net_fuzzer_test_support",
+ ":test_support",
+ "//base",
+ "//net",
+ "//testing/libfuzzer/proto:json_proto",
+ "//testing/libfuzzer/proto:json_proto_converter",
+ "//third_party/libprotobuf-mutator",
+ ]
+}
+
fuzzer_test("net_data_url_fuzzer") {
sources = [ "base/data_url_fuzzer.cc" ]
deps = [
diff --git a/net/base/backoff_entry_serializer_fuzzer.cc b/net/base/backoff_entry_serializer_fuzzer.cc
new file mode 100644
index 0000000..d66eacbd
--- /dev/null
+++ b/net/base/backoff_entry_serializer_fuzzer.cc
@@ -0,0 +1,141 @@
+// 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.
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/json/json_reader.h"
+#include "base/optional.h"
+#include "base/strings/string_piece_forward.h"
+#include "base/time/time.h"
+#include "net/base/backoff_entry.h"
+#include "net/base/backoff_entry_serializer.h"
+#include "net/base/backoff_entry_serializer_fuzzer_input.pb.h"
+#include "testing/libfuzzer/proto/json_proto_converter.h"
+#include "testing/libfuzzer/proto/lpm_interface.h"
+
+namespace net {
+
+namespace {
+struct Environment {
+ Environment() { logging::SetMinLogLevel(logging::LOG_ERROR); }
+};
+
+class ProtoTranslator {
+ public:
+ explicit ProtoTranslator(const fuzz_proto::FuzzerInput& input)
+ : input_(input) {}
+
+ BackoffEntry::Policy policy() const {
+ return PolicyFromProto(input_.policy());
+ }
+ base::Time parse_time() const { return TimeFromProto(input_.parse_time()); }
+ base::Time serialize_time() const {
+ return TimeFromProto(input_.serialize_time());
+ }
+
+ base::Optional<base::Value> serialized_entry() const {
+ json_proto::JsonProtoConverter converter;
+ std::string json_array = converter.Convert(input_.serialized_entry());
+ base::Optional<base::Value> value = base::JSONReader::Read(json_array);
+ return value;
+ }
+
+ private:
+ const fuzz_proto::FuzzerInput& input_;
+
+ static BackoffEntry::Policy PolicyFromProto(
+ const fuzz_proto::BackoffEntryPolicy& policy) {
+ return BackoffEntry::Policy{
+ .num_errors_to_ignore = policy.num_errors_to_ignore(),
+ .initial_delay_ms = policy.initial_delay_ms(),
+ .multiply_factor = policy.multiply_factor(),
+ .jitter_factor = policy.jitter_factor(),
+ .maximum_backoff_ms = policy.maximum_backoff_ms(),
+ .entry_lifetime_ms = policy.entry_lifetime_ms(),
+ .always_use_initial_delay = policy.always_use_initial_delay(),
+ };
+ }
+
+ static base::Time TimeFromProto(uint64_t raw_time) {
+ return base::Time() + base::TimeDelta::FromMicroseconds(raw_time);
+ }
+};
+
+// Tests the "deserialize-reserialize" property. Deserializes a BackoffEntry
+// from JSON, reserializes it, and checks that the JSON values match.
+void TestDeserialize(const ProtoTranslator& translator) {
+ // Attempt to convert the json_proto.ArrayValue to a base::Value.
+ base::Optional<base::Value> value = translator.serialized_entry();
+ if (!value)
+ return;
+ DCHECK(value->is_list());
+
+ BackoffEntry::Policy policy = translator.policy();
+
+ // Attempt to deserialize a BackoffEntry.
+ std::unique_ptr<BackoffEntry> entry =
+ BackoffEntrySerializer::DeserializeFromValue(*value, &policy, nullptr,
+ translator.parse_time());
+ if (!entry)
+ return;
+
+ // Serializing |entry| it should recreate the original JSON input!
+ std::unique_ptr<base::Value> reserialized =
+ BackoffEntrySerializer::SerializeToValue(*entry,
+ translator.serialize_time());
+ CHECK(reserialized);
+ CHECK_EQ(*reserialized, *value);
+}
+
+// Tests the "serialize-deserialize" property. Serializes an arbitrary
+// BackoffEntry to JSON, deserializes to another BackoffEntry, and checks
+// equality of the two entries. Our notion of equality is *very weak* and needs
+// improvement.
+void TestSerialize(const ProtoTranslator& translator) {
+ BackoffEntry::Policy policy = translator.policy();
+
+ // Serialize the BackoffEntry.
+ BackoffEntry native_entry(&policy);
+ std::unique_ptr<base::Value> serialized =
+ BackoffEntrySerializer::SerializeToValue(native_entry,
+ translator.serialize_time());
+ CHECK(serialized);
+
+ // Deserialize it.
+ std::unique_ptr<BackoffEntry> deserialized_entry =
+ BackoffEntrySerializer::DeserializeFromValue(
+ *serialized, &policy, nullptr, translator.parse_time());
+ // Even though SerializeToValue was successful, we're not guaranteed to have a
+ // |deserialized_entry|. One reason deserialization may fail is if the parsed
+ // |absolute_release_time_us| is below zero.
+ if (!deserialized_entry)
+ return;
+
+ // TODO(dmcardle) Develop a stronger equality check for BackoffEntry.
+
+ // Note that while |BackoffEntry::GetReleaseTime| looks like an accessor, it
+ // returns a |value that is computed based on a random double, so it's not
+ // suitable for CHECK_EQ here. See |BackoffEntry::CalculateReleaseTime|.
+
+ CHECK_EQ(native_entry.failure_count(), deserialized_entry->failure_count());
+}
+} // namespace
+
+DEFINE_PROTO_FUZZER(const fuzz_proto::FuzzerInput& input) {
+ static Environment env;
+
+ // Print the entire |input| protobuf if asked.
+ if (getenv("LPM_DUMP_NATIVE_INPUT")) {
+ std::cout << "input: " << input.DebugString();
+ }
+
+ ProtoTranslator translator(input);
+ TestSerialize(translator);
+ TestDeserialize(translator);
+}
+
+} // namespace net
diff --git a/net/base/backoff_entry_serializer_fuzzer_input.proto b/net/base/backoff_entry_serializer_fuzzer_input.proto
new file mode 100644
index 0000000..d92f72e
--- /dev/null
+++ b/net/base/backoff_entry_serializer_fuzzer_input.proto
@@ -0,0 +1,29 @@
+// 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.
+
+syntax = "proto2";
+
+package fuzz_proto;
+
+import "testing/libfuzzer/proto/json.proto";
+
+message FuzzerInput {
+ // Using int64 to match base::Time's internal representation.
+ required int64 parse_time = 1;
+ required int64 serialize_time = 2;
+ required BackoffEntryPolicy policy = 3;
+ required json_proto.ArrayValue serialized_entry = 4;
+}
+
+// Input for the fuzzer to try serializing a BackoffEntry.
+// Keep aligned with |net::BackoffEntry::Policy|.
+message BackoffEntryPolicy {
+ required int64 num_errors_to_ignore = 1;
+ required int64 initial_delay_ms = 2;
+ required double multiply_factor = 3;
+ required double jitter_factor = 4;
+ required int64 maximum_backoff_ms = 5;
+ required int64 entry_lifetime_ms = 6;
+ required bool always_use_initial_delay = 7;
+}
\ No newline at end of file
diff --git a/testing/libfuzzer/proto/json_proto_converter.cc b/testing/libfuzzer/proto/json_proto_converter.cc
index 155a3800..1013be4 100644
--- a/testing/libfuzzer/proto/json_proto_converter.cc
+++ b/testing/libfuzzer/proto/json_proto_converter.cc
@@ -66,4 +66,10 @@
return data_.str();
}
+std::string JsonProtoConverter::Convert(
+ const json_proto::ArrayValue& json_array) {
+ AppendArray(json_array);
+ return data_.str();
+}
+
} // namespace json_proto
diff --git a/testing/libfuzzer/proto/json_proto_converter.h b/testing/libfuzzer/proto/json_proto_converter.h
index 5dedb938..d63eec0c 100644
--- a/testing/libfuzzer/proto/json_proto_converter.h
+++ b/testing/libfuzzer/proto/json_proto_converter.h
@@ -15,6 +15,7 @@
class JsonProtoConverter {
public:
std::string Convert(const json_proto::JsonObject&);
+ std::string Convert(const json_proto::ArrayValue&);
private:
std::stringstream data_;