backoff_entry_serializer_fuzzer.cc 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <stddef.h>
  5. #include <stdint.h>
  6. #include <memory>
  7. #include "base/json/json_reader.h"
  8. #include "base/logging.h"
  9. #include "base/strings/string_piece_forward.h"
  10. #include "base/time/tick_clock.h"
  11. #include "base/time/time.h"
  12. #include "net/base/backoff_entry.h"
  13. #include "net/base/backoff_entry_serializer.h"
  14. #include "net/base/backoff_entry_serializer_fuzzer_input.pb.h"
  15. #include "testing/libfuzzer/proto/json_proto_converter.h"
  16. #include "testing/libfuzzer/proto/lpm_interface.h"
  17. #include "third_party/abseil-cpp/absl/types/optional.h"
  18. namespace net {
  19. namespace {
  20. struct Environment {
  21. Environment() { logging::SetMinLogLevel(logging::LOG_ERROR); }
  22. };
  23. class ProtoTranslator {
  24. public:
  25. explicit ProtoTranslator(const fuzz_proto::FuzzerInput& input)
  26. : input_(input) {}
  27. BackoffEntry::Policy policy() const {
  28. return PolicyFromProto(input_.policy());
  29. }
  30. base::Time parse_time() const {
  31. return base::Time() + base::Microseconds(input_.parse_time());
  32. }
  33. base::TimeTicks parse_time_ticks() const {
  34. return base::TimeTicks() + base::Microseconds(input_.parse_time());
  35. }
  36. base::Time serialize_time() const {
  37. return base::Time() + base::Microseconds(input_.serialize_time());
  38. }
  39. base::TimeTicks now_ticks() const {
  40. return base::TimeTicks() + base::Microseconds(input_.now_ticks());
  41. }
  42. absl::optional<base::Value> serialized_entry() const {
  43. json_proto::JsonProtoConverter converter;
  44. std::string json_array = converter.Convert(input_.serialized_entry());
  45. absl::optional<base::Value> value = base::JSONReader::Read(json_array);
  46. return value;
  47. }
  48. private:
  49. const fuzz_proto::FuzzerInput& input_;
  50. static BackoffEntry::Policy PolicyFromProto(
  51. const fuzz_proto::BackoffEntryPolicy& policy) {
  52. BackoffEntry::Policy new_policy;
  53. new_policy.num_errors_to_ignore = policy.num_errors_to_ignore();
  54. new_policy.initial_delay_ms = policy.initial_delay_ms();
  55. new_policy.multiply_factor = policy.multiply_factor();
  56. new_policy.jitter_factor = policy.jitter_factor();
  57. new_policy.maximum_backoff_ms = policy.maximum_backoff_ms();
  58. new_policy.entry_lifetime_ms = policy.entry_lifetime_ms();
  59. new_policy.always_use_initial_delay = policy.always_use_initial_delay();
  60. return new_policy;
  61. }
  62. };
  63. class MockClock : public base::TickClock {
  64. public:
  65. MockClock() = default;
  66. ~MockClock() override = default;
  67. void SetNow(base::TimeTicks now) { now_ = now; }
  68. base::TimeTicks NowTicks() const override { return now_; }
  69. private:
  70. base::TimeTicks now_;
  71. };
  72. // Tests the "deserialize-reserialize" property. Deserializes a BackoffEntry
  73. // from JSON, reserializes it, then deserializes again. Holding time constant,
  74. // we check that the parsed BackoffEntry values are equivalent.
  75. void TestDeserialize(const ProtoTranslator& translator) {
  76. // Attempt to convert the json_proto.ArrayValue to a base::Value.
  77. absl::optional<base::Value> value = translator.serialized_entry();
  78. if (!value)
  79. return;
  80. DCHECK(value->is_list());
  81. BackoffEntry::Policy policy = translator.policy();
  82. MockClock clock;
  83. clock.SetNow(translator.parse_time_ticks());
  84. // Attempt to deserialize a BackoffEntry.
  85. std::unique_ptr<BackoffEntry> entry =
  86. BackoffEntrySerializer::DeserializeFromValue(*value, &policy, &clock,
  87. translator.parse_time());
  88. if (!entry)
  89. return;
  90. base::Value reserialized =
  91. BackoffEntrySerializer::SerializeToValue(*entry, translator.parse_time());
  92. // Due to fuzzy interpretation in BackoffEntrySerializer::
  93. // DeserializeFromValue, we cannot assert that |*reserialized == *value|.
  94. // Rather, we can deserialize |reserialized| and check that some weaker
  95. // properties are preserved.
  96. std::unique_ptr<BackoffEntry> entry_reparsed =
  97. BackoffEntrySerializer::DeserializeFromValue(
  98. reserialized, &policy, &clock, translator.parse_time());
  99. CHECK(entry_reparsed);
  100. CHECK_EQ(entry_reparsed->failure_count(), entry->failure_count());
  101. CHECK_LE(entry_reparsed->GetReleaseTime(), entry->GetReleaseTime());
  102. }
  103. // Tests the "serialize-deserialize" property. Serializes an arbitrary
  104. // BackoffEntry to JSON, deserializes to another BackoffEntry, and checks
  105. // equality of the two entries. Our notion of equality is *very weak* and needs
  106. // improvement.
  107. void TestSerialize(const ProtoTranslator& translator) {
  108. BackoffEntry::Policy policy = translator.policy();
  109. // Serialize the BackoffEntry.
  110. BackoffEntry native_entry(&policy);
  111. base::Value serialized = BackoffEntrySerializer::SerializeToValue(
  112. native_entry, translator.serialize_time());
  113. CHECK(serialized.is_list());
  114. MockClock clock;
  115. clock.SetNow(translator.now_ticks());
  116. // Deserialize it.
  117. std::unique_ptr<BackoffEntry> deserialized_entry =
  118. BackoffEntrySerializer::DeserializeFromValue(serialized, &policy, &clock,
  119. translator.parse_time());
  120. // Even though SerializeToValue was successful, we're not guaranteed to have a
  121. // |deserialized_entry|. One reason deserialization may fail is if the parsed
  122. // |absolute_release_time_us| is below zero.
  123. if (!deserialized_entry)
  124. return;
  125. // TODO(dmcardle) Develop a stronger equality check for BackoffEntry.
  126. // Note that while |BackoffEntry::GetReleaseTime| looks like an accessor, it
  127. // returns a |value that is computed based on a random double, so it's not
  128. // suitable for CHECK_EQ here. See |BackoffEntry::CalculateReleaseTime|.
  129. CHECK_EQ(native_entry.failure_count(), deserialized_entry->failure_count());
  130. }
  131. } // namespace
  132. DEFINE_PROTO_FUZZER(const fuzz_proto::FuzzerInput& input) {
  133. static Environment env;
  134. // Print the entire |input| protobuf if asked.
  135. if (getenv("LPM_DUMP_NATIVE_INPUT")) {
  136. std::cout << "input: " << input.DebugString();
  137. }
  138. ProtoTranslator translator(input);
  139. // Skip this input if any of the time values are infinite.
  140. if (translator.now_ticks().is_inf() || translator.parse_time().is_inf() ||
  141. translator.serialize_time().is_inf()) {
  142. return;
  143. }
  144. TestDeserialize(translator);
  145. TestSerialize(translator);
  146. }
  147. } // namespace net