123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- // 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/logging.h"
- #include "base/strings/string_piece_forward.h"
- #include "base/time/tick_clock.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"
- #include "third_party/abseil-cpp/absl/types/optional.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 base::Time() + base::Microseconds(input_.parse_time());
- }
- base::TimeTicks parse_time_ticks() const {
- return base::TimeTicks() + base::Microseconds(input_.parse_time());
- }
- base::Time serialize_time() const {
- return base::Time() + base::Microseconds(input_.serialize_time());
- }
- base::TimeTicks now_ticks() const {
- return base::TimeTicks() + base::Microseconds(input_.now_ticks());
- }
- absl::optional<base::Value> serialized_entry() const {
- json_proto::JsonProtoConverter converter;
- std::string json_array = converter.Convert(input_.serialized_entry());
- absl::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) {
- BackoffEntry::Policy new_policy;
- new_policy.num_errors_to_ignore = policy.num_errors_to_ignore();
- new_policy.initial_delay_ms = policy.initial_delay_ms();
- new_policy.multiply_factor = policy.multiply_factor();
- new_policy.jitter_factor = policy.jitter_factor();
- new_policy.maximum_backoff_ms = policy.maximum_backoff_ms();
- new_policy.entry_lifetime_ms = policy.entry_lifetime_ms();
- new_policy.always_use_initial_delay = policy.always_use_initial_delay();
- return new_policy;
- }
- };
- class MockClock : public base::TickClock {
- public:
- MockClock() = default;
- ~MockClock() override = default;
- void SetNow(base::TimeTicks now) { now_ = now; }
- base::TimeTicks NowTicks() const override { return now_; }
- private:
- base::TimeTicks now_;
- };
- // Tests the "deserialize-reserialize" property. Deserializes a BackoffEntry
- // from JSON, reserializes it, then deserializes again. Holding time constant,
- // we check that the parsed BackoffEntry values are equivalent.
- void TestDeserialize(const ProtoTranslator& translator) {
- // Attempt to convert the json_proto.ArrayValue to a base::Value.
- absl::optional<base::Value> value = translator.serialized_entry();
- if (!value)
- return;
- DCHECK(value->is_list());
- BackoffEntry::Policy policy = translator.policy();
- MockClock clock;
- clock.SetNow(translator.parse_time_ticks());
- // Attempt to deserialize a BackoffEntry.
- std::unique_ptr<BackoffEntry> entry =
- BackoffEntrySerializer::DeserializeFromValue(*value, &policy, &clock,
- translator.parse_time());
- if (!entry)
- return;
- base::Value reserialized =
- BackoffEntrySerializer::SerializeToValue(*entry, translator.parse_time());
- // Due to fuzzy interpretation in BackoffEntrySerializer::
- // DeserializeFromValue, we cannot assert that |*reserialized == *value|.
- // Rather, we can deserialize |reserialized| and check that some weaker
- // properties are preserved.
- std::unique_ptr<BackoffEntry> entry_reparsed =
- BackoffEntrySerializer::DeserializeFromValue(
- reserialized, &policy, &clock, translator.parse_time());
- CHECK(entry_reparsed);
- CHECK_EQ(entry_reparsed->failure_count(), entry->failure_count());
- CHECK_LE(entry_reparsed->GetReleaseTime(), entry->GetReleaseTime());
- }
- // 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);
- base::Value serialized = BackoffEntrySerializer::SerializeToValue(
- native_entry, translator.serialize_time());
- CHECK(serialized.is_list());
- MockClock clock;
- clock.SetNow(translator.now_ticks());
- // Deserialize it.
- std::unique_ptr<BackoffEntry> deserialized_entry =
- BackoffEntrySerializer::DeserializeFromValue(serialized, &policy, &clock,
- 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);
- // Skip this input if any of the time values are infinite.
- if (translator.now_ticks().is_inf() || translator.parse_time().is_inf() ||
- translator.serialize_time().is_inf()) {
- return;
- }
- TestDeserialize(translator);
- TestSerialize(translator);
- }
- } // namespace net
|