xref: /aosp_15_r20/external/cronet/net/base/backoff_entry_serializer_fuzzer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2020 The Chromium Authors
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 "net/base/backoff_entry_serializer.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include <memory>
11 #include <optional>
12 
13 #include "base/json/json_reader.h"
14 #include "base/logging.h"
15 #include "base/strings/string_piece.h"
16 #include "base/time/tick_clock.h"
17 #include "base/time/time.h"
18 #include "net/base/backoff_entry.h"
19 #include "net/base/backoff_entry_serializer_fuzzer_input.pb.h"
20 #include "testing/libfuzzer/proto/json_proto_converter.h"
21 #include "testing/libfuzzer/proto/lpm_interface.h"
22 
23 namespace net {
24 
25 namespace {
26 struct Environment {
Environmentnet::__anona7b93b900111::Environment27   Environment() { logging::SetMinLogLevel(logging::LOGGING_ERROR); }
28 };
29 
30 class ProtoTranslator {
31  public:
ProtoTranslator(const fuzz_proto::FuzzerInput & input)32   explicit ProtoTranslator(const fuzz_proto::FuzzerInput& input)
33       : input_(input) {}
34 
policy() const35   BackoffEntry::Policy policy() const {
36     return PolicyFromProto(input_->policy());
37   }
parse_time() const38   base::Time parse_time() const {
39     return base::Time() + base::Microseconds(input_->parse_time());
40   }
parse_time_ticks() const41   base::TimeTicks parse_time_ticks() const {
42     return base::TimeTicks() + base::Microseconds(input_->parse_time());
43   }
serialize_time() const44   base::Time serialize_time() const {
45     return base::Time() + base::Microseconds(input_->serialize_time());
46   }
now_ticks() const47   base::TimeTicks now_ticks() const {
48     return base::TimeTicks() + base::Microseconds(input_->now_ticks());
49   }
serialized_entry() const50   std::optional<base::Value> serialized_entry() const {
51     json_proto::JsonProtoConverter converter;
52     std::string json_array = converter.Convert(input_->serialized_entry());
53     std::optional<base::Value> value = base::JSONReader::Read(json_array);
54     return value;
55   }
56 
57  private:
58   const raw_ref<const fuzz_proto::FuzzerInput> input_;
59 
PolicyFromProto(const fuzz_proto::BackoffEntryPolicy & policy)60   static BackoffEntry::Policy PolicyFromProto(
61       const fuzz_proto::BackoffEntryPolicy& policy) {
62     BackoffEntry::Policy new_policy;
63     new_policy.num_errors_to_ignore = policy.num_errors_to_ignore();
64     new_policy.initial_delay_ms = policy.initial_delay_ms();
65     new_policy.multiply_factor = policy.multiply_factor();
66     new_policy.jitter_factor = policy.jitter_factor();
67     new_policy.maximum_backoff_ms = policy.maximum_backoff_ms();
68     new_policy.entry_lifetime_ms = policy.entry_lifetime_ms();
69     new_policy.always_use_initial_delay = policy.always_use_initial_delay();
70     return new_policy;
71   }
72 };
73 
74 class MockClock : public base::TickClock {
75  public:
76   MockClock() = default;
77   ~MockClock() override = default;
78 
SetNow(base::TimeTicks now)79   void SetNow(base::TimeTicks now) { now_ = now; }
NowTicks() const80   base::TimeTicks NowTicks() const override { return now_; }
81 
82  private:
83   base::TimeTicks now_;
84 };
85 
86 // Tests the "deserialize-reserialize" property. Deserializes a BackoffEntry
87 // from JSON, reserializes it, then deserializes again. Holding time constant,
88 // we check that the parsed BackoffEntry values are equivalent.
TestDeserialize(const ProtoTranslator & translator)89 void TestDeserialize(const ProtoTranslator& translator) {
90   // Attempt to convert the json_proto.ArrayValue to a base::Value.
91   std::optional<base::Value> value = translator.serialized_entry();
92   if (!value)
93     return;
94   DCHECK(value->is_list());
95 
96   BackoffEntry::Policy policy = translator.policy();
97 
98   MockClock clock;
99   clock.SetNow(translator.parse_time_ticks());
100 
101   // Attempt to deserialize a BackoffEntry.
102   std::unique_ptr<BackoffEntry> entry =
103       BackoffEntrySerializer::DeserializeFromList(
104           value->GetList(), &policy, &clock, translator.parse_time());
105   if (!entry)
106     return;
107 
108   base::Value::List reserialized =
109       BackoffEntrySerializer::SerializeToList(*entry, translator.parse_time());
110 
111   // Due to fuzzy interpretation in BackoffEntrySerializer::
112   // DeserializeFromList, we cannot assert that |*reserialized == *value|.
113   // Rather, we can deserialize |reserialized| and check that some weaker
114   // properties are preserved.
115   std::unique_ptr<BackoffEntry> entry_reparsed =
116       BackoffEntrySerializer::DeserializeFromList(reserialized, &policy, &clock,
117                                                   translator.parse_time());
118   CHECK(entry_reparsed);
119   CHECK_EQ(entry_reparsed->failure_count(), entry->failure_count());
120   CHECK_LE(entry_reparsed->GetReleaseTime(), entry->GetReleaseTime());
121 }
122 
123 // Tests the "serialize-deserialize" property. Serializes an arbitrary
124 // BackoffEntry to JSON, deserializes to another BackoffEntry, and checks
125 // equality of the two entries. Our notion of equality is *very weak* and needs
126 // improvement.
TestSerialize(const ProtoTranslator & translator)127 void TestSerialize(const ProtoTranslator& translator) {
128   BackoffEntry::Policy policy = translator.policy();
129 
130   // Serialize the BackoffEntry.
131   BackoffEntry native_entry(&policy);
132   base::Value::List serialized = BackoffEntrySerializer::SerializeToList(
133       native_entry, translator.serialize_time());
134 
135   MockClock clock;
136   clock.SetNow(translator.now_ticks());
137 
138   // Deserialize it.
139   std::unique_ptr<BackoffEntry> deserialized_entry =
140       BackoffEntrySerializer::DeserializeFromList(serialized, &policy, &clock,
141                                                   translator.parse_time());
142   // Even though SerializeToList was successful, we're not guaranteed to have a
143   // |deserialized_entry|. One reason deserialization may fail is if the parsed
144   // |absolute_release_time_us| is below zero.
145   if (!deserialized_entry)
146     return;
147 
148   // TODO(dmcardle) Develop a stronger equality check for BackoffEntry.
149 
150   // Note that while |BackoffEntry::GetReleaseTime| looks like an accessor, it
151   // returns a |value that is computed based on a random double, so it's not
152   // suitable for CHECK_EQ here. See |BackoffEntry::CalculateReleaseTime|.
153 
154   CHECK_EQ(native_entry.failure_count(), deserialized_entry->failure_count());
155 }
156 }  // namespace
157 
DEFINE_PROTO_FUZZER(const fuzz_proto::FuzzerInput & input)158 DEFINE_PROTO_FUZZER(const fuzz_proto::FuzzerInput& input) {
159   static Environment env;
160 
161   // Print the entire |input| protobuf if asked.
162   if (getenv("LPM_DUMP_NATIVE_INPUT")) {
163     std::cout << "input: " << input.DebugString();
164   }
165 
166   ProtoTranslator translator(input);
167   // Skip this input if any of the time values are infinite.
168   if (translator.now_ticks().is_inf() || translator.parse_time().is_inf() ||
169       translator.serialize_time().is_inf()) {
170     return;
171   }
172   TestDeserialize(translator);
173   TestSerialize(translator);
174 }
175 
176 }  // namespace net
177