blob: 0ec77af245d092ad40b28016e5c13bd0d4f59b16 [file] [log] [blame]
Daniel McArdle72692a12020-05-11 16:47:161// 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
5#include <stddef.h>
6#include <stdint.h>
7
8#include <memory>
9
10#include "base/json/json_reader.h"
Hans Wennborg725d0432020-06-18 13:54:1611#include "base/logging.h"
Daniel McArdle72692a12020-05-11 16:47:1612#include "base/strings/string_piece_forward.h"
Daniel McArdlebbfc1452020-05-18 15:11:5513#include "base/time/tick_clock.h"
Daniel McArdle72692a12020-05-11 16:47:1614#include "base/time/time.h"
15#include "net/base/backoff_entry.h"
16#include "net/base/backoff_entry_serializer.h"
17#include "net/base/backoff_entry_serializer_fuzzer_input.pb.h"
18#include "testing/libfuzzer/proto/json_proto_converter.h"
19#include "testing/libfuzzer/proto/lpm_interface.h"
Anton Bikineev068d2912021-05-15 20:43:5220#include "third_party/abseil-cpp/absl/types/optional.h"
Daniel McArdle72692a12020-05-11 16:47:1621
22namespace net {
23
24namespace {
25struct Environment {
26 Environment() { logging::SetMinLogLevel(logging::LOG_ERROR); }
27};
28
29class ProtoTranslator {
30 public:
31 explicit ProtoTranslator(const fuzz_proto::FuzzerInput& input)
32 : input_(input) {}
33
34 BackoffEntry::Policy policy() const {
35 return PolicyFromProto(input_.policy());
36 }
Daniel McArdlebbfc1452020-05-18 15:11:5537 base::Time parse_time() const {
38 return base::Time() +
39 base::TimeDelta::FromMicroseconds(input_.parse_time());
Daniel McArdle72692a12020-05-11 16:47:1640 }
Dan McArdle82d70972021-04-12 15:54:2041 base::TimeTicks parse_time_ticks() const {
42 return base::TimeTicks() +
43 base::TimeDelta::FromMicroseconds(input_.parse_time());
44 }
Daniel McArdlebbfc1452020-05-18 15:11:5545 base::Time serialize_time() const {
46 return base::Time() +
47 base::TimeDelta::FromMicroseconds(input_.serialize_time());
48 }
49 base::TimeTicks now_ticks() const {
50 return base::TimeTicks() +
51 base::TimeDelta::FromMicroseconds(input_.now_ticks());
52 }
Anton Bikineev068d2912021-05-15 20:43:5253 absl::optional<base::Value> serialized_entry() const {
Daniel McArdle72692a12020-05-11 16:47:1654 json_proto::JsonProtoConverter converter;
55 std::string json_array = converter.Convert(input_.serialized_entry());
Anton Bikineev068d2912021-05-15 20:43:5256 absl::optional<base::Value> value = base::JSONReader::Read(json_array);
Daniel McArdle72692a12020-05-11 16:47:1657 return value;
58 }
59
60 private:
61 const fuzz_proto::FuzzerInput& input_;
62
63 static BackoffEntry::Policy PolicyFromProto(
64 const fuzz_proto::BackoffEntryPolicy& policy) {
Daniel McArdlebbfc1452020-05-18 15:11:5565 BackoffEntry::Policy new_policy;
66 new_policy.num_errors_to_ignore = policy.num_errors_to_ignore();
67 new_policy.initial_delay_ms = policy.initial_delay_ms();
68 new_policy.multiply_factor = policy.multiply_factor();
69 new_policy.jitter_factor = policy.jitter_factor();
70 new_policy.maximum_backoff_ms = policy.maximum_backoff_ms();
71 new_policy.entry_lifetime_ms = policy.entry_lifetime_ms();
72 new_policy.always_use_initial_delay = policy.always_use_initial_delay();
73 return new_policy;
Daniel McArdle72692a12020-05-11 16:47:1674 }
75};
76
Daniel McArdlebbfc1452020-05-18 15:11:5577class MockClock : public base::TickClock {
78 public:
79 MockClock() = default;
80 ~MockClock() override = default;
81
82 void SetNow(base::TimeTicks now) { now_ = now; }
83 base::TimeTicks NowTicks() const override { return now_; }
84
85 private:
86 base::TimeTicks now_;
87};
88
Daniel McArdle72692a12020-05-11 16:47:1689// Tests the "deserialize-reserialize" property. Deserializes a BackoffEntry
Daniel McArdlebbfc1452020-05-18 15:11:5590// from JSON, reserializes it, then deserializes again. Holding time constant,
91// we check that the parsed BackoffEntry values are equivalent.
Daniel McArdle72692a12020-05-11 16:47:1692void TestDeserialize(const ProtoTranslator& translator) {
93 // Attempt to convert the json_proto.ArrayValue to a base::Value.
Anton Bikineev068d2912021-05-15 20:43:5294 absl::optional<base::Value> value = translator.serialized_entry();
Daniel McArdle72692a12020-05-11 16:47:1695 if (!value)
96 return;
97 DCHECK(value->is_list());
98
99 BackoffEntry::Policy policy = translator.policy();
100
Daniel McArdlebbfc1452020-05-18 15:11:55101 MockClock clock;
Dan McArdle82d70972021-04-12 15:54:20102 clock.SetNow(translator.parse_time_ticks());
Daniel McArdlebbfc1452020-05-18 15:11:55103
Daniel McArdle72692a12020-05-11 16:47:16104 // Attempt to deserialize a BackoffEntry.
105 std::unique_ptr<BackoffEntry> entry =
Daniel McArdlebbfc1452020-05-18 15:11:55106 BackoffEntrySerializer::DeserializeFromValue(*value, &policy, &clock,
Daniel McArdle72692a12020-05-11 16:47:16107 translator.parse_time());
108 if (!entry)
109 return;
110
Maks Orlovich691496c2021-05-18 15:03:21111 base::Value reserialized =
Daniel McArdlebbfc1452020-05-18 15:11:55112 BackoffEntrySerializer::SerializeToValue(*entry, translator.parse_time());
Daniel McArdlebbfc1452020-05-18 15:11:55113
114 // Due to fuzzy interpretation in BackoffEntrySerializer::
115 // DeserializeFromValue, we cannot assert that |*reserialized == *value|.
Daniel McArdlea8799f02020-08-04 15:44:16116 // Rather, we can deserialize |reserialized| and check that some weaker
117 // properties are preserved.
Daniel McArdlebbfc1452020-05-18 15:11:55118 std::unique_ptr<BackoffEntry> entry_reparsed =
119 BackoffEntrySerializer::DeserializeFromValue(
Maks Orlovich691496c2021-05-18 15:03:21120 reserialized, &policy, &clock, translator.parse_time());
Daniel McArdlebbfc1452020-05-18 15:11:55121 CHECK(entry_reparsed);
Daniel McArdlea8799f02020-08-04 15:44:16122 CHECK_EQ(entry_reparsed->failure_count(), entry->failure_count());
123 CHECK_LE(entry_reparsed->GetReleaseTime(), entry->GetReleaseTime());
Daniel McArdle72692a12020-05-11 16:47:16124}
125
126// Tests the "serialize-deserialize" property. Serializes an arbitrary
127// BackoffEntry to JSON, deserializes to another BackoffEntry, and checks
128// equality of the two entries. Our notion of equality is *very weak* and needs
129// improvement.
130void TestSerialize(const ProtoTranslator& translator) {
131 BackoffEntry::Policy policy = translator.policy();
132
133 // Serialize the BackoffEntry.
134 BackoffEntry native_entry(&policy);
Maks Orlovich691496c2021-05-18 15:03:21135 base::Value serialized = BackoffEntrySerializer::SerializeToValue(
136 native_entry, translator.serialize_time());
137 CHECK(serialized.is_list());
Daniel McArdle72692a12020-05-11 16:47:16138
Daniel McArdlebbfc1452020-05-18 15:11:55139 MockClock clock;
140 clock.SetNow(translator.now_ticks());
141
Daniel McArdle72692a12020-05-11 16:47:16142 // Deserialize it.
143 std::unique_ptr<BackoffEntry> deserialized_entry =
Maks Orlovich691496c2021-05-18 15:03:21144 BackoffEntrySerializer::DeserializeFromValue(serialized, &policy, &clock,
Daniel McArdlebbfc1452020-05-18 15:11:55145 translator.parse_time());
Daniel McArdle72692a12020-05-11 16:47:16146 // Even though SerializeToValue was successful, we're not guaranteed to have a
147 // |deserialized_entry|. One reason deserialization may fail is if the parsed
148 // |absolute_release_time_us| is below zero.
149 if (!deserialized_entry)
150 return;
151
152 // TODO(dmcardle) Develop a stronger equality check for BackoffEntry.
153
154 // Note that while |BackoffEntry::GetReleaseTime| looks like an accessor, it
155 // returns a |value that is computed based on a random double, so it's not
156 // suitable for CHECK_EQ here. See |BackoffEntry::CalculateReleaseTime|.
157
158 CHECK_EQ(native_entry.failure_count(), deserialized_entry->failure_count());
159}
160} // namespace
161
162DEFINE_PROTO_FUZZER(const fuzz_proto::FuzzerInput& input) {
163 static Environment env;
164
165 // Print the entire |input| protobuf if asked.
166 if (getenv("LPM_DUMP_NATIVE_INPUT")) {
167 std::cout << "input: " << input.DebugString();
168 }
169
170 ProtoTranslator translator(input);
Dan McArdle22c2f642021-03-18 23:40:10171 // Skip this input if any of the time values are infinite.
172 if (translator.now_ticks().is_inf() || translator.parse_time().is_inf() ||
173 translator.serialize_time().is_inf()) {
174 return;
175 }
Daniel McArdle72692a12020-05-11 16:47:16176 TestDeserialize(translator);
Daniel McArdlebbfc1452020-05-18 15:11:55177 TestSerialize(translator);
Daniel McArdle72692a12020-05-11 16:47:16178}
179
180} // namespace net