Index: webrtc/test/fuzzers/transport_feedback_packet_loss_tracker_fuzzer.cc |
diff --git a/webrtc/test/fuzzers/transport_feedback_packet_loss_tracker_fuzzer.cc b/webrtc/test/fuzzers/transport_feedback_packet_loss_tracker_fuzzer.cc |
index bb2418c81a46a29dc1e40f2dcab0c0364999a6ef..607bf66af884b290cacc8e3c37bc859a9841eea8 100644 |
--- a/webrtc/test/fuzzers/transport_feedback_packet_loss_tracker_fuzzer.cc |
+++ b/webrtc/test/fuzzers/transport_feedback_packet_loss_tracker_fuzzer.cc |
@@ -44,90 +44,201 @@ size_t FuzzInRange(const uint8_t** data, |
class TransportFeedbackGenerator { |
public: |
- explicit TransportFeedbackGenerator(rtc::ArrayView<const uint8_t> data) |
- : data_(data), ended_(false), data_idx_(0) {} |
+ explicit TransportFeedbackGenerator(const uint8_t** data, size_t* size) |
+ : data_(data), size_(size) {} |
+ |
+ bool GetNextTransportFeedback(rtcp::TransportFeedback* feedback) { |
+ constexpr int64_t kBaseTimeUs = 1234; // Irrelevant to this test. |
- void GetNextTransportFeedback(rtcp::TransportFeedback* feedback) { |
uint16_t base_seq_num = 0; |
if (!ReadData<uint16_t>(&base_seq_num)) { |
- return; |
+ return false; |
} |
- |
- const int64_t kBaseTimeUs = 1234; // Irrelevant to this test. |
feedback->SetBase(base_seq_num, kBaseTimeUs); |
- uint16_t num_statuses = 0; |
- if (!ReadData<uint16_t>(&num_statuses)) |
- return; |
- num_statuses = std::max<uint16_t>(num_statuses, 1); |
+ uint16_t remaining_packets = 0; |
+ if (!ReadData<uint16_t>(&remaining_packets)) |
+ return false; |
+ // Range is [0x00001 : 0x10000], but we keep it 0x0000 to 0xffff for now, |
+ // and add the last status as RECEIVED. That is because of a limitation |
+ // that says that the last status cannot be LOST. |
uint16_t seq_num = base_seq_num; |
- while (true) { |
+ while (remaining_packets > 0) { |
uint8_t status_byte = 0; |
- if (!ReadData<uint8_t>(&status_byte)) |
- return; |
+ if (!ReadData<uint8_t>(&status_byte)) { |
+ return false; |
+ } |
// Each status byte contains 8 statuses. |
- for (size_t j = 0; j < 8; ++j) { |
- if (status_byte & 0x01) { |
+ for (size_t i = 0; i < 8 && remaining_packets > 0; ++i) { |
+ const bool received = (status_byte & (0x01 << i)); |
+ if (received) { |
feedback->AddReceivedPacket(seq_num, kBaseTimeUs); |
} |
- seq_num++; |
- if (seq_num >= base_seq_num + num_statuses) { |
- feedback->AddReceivedPacket(seq_num, kBaseTimeUs); |
- return; |
- } |
- status_byte >>= 1; |
+ ++seq_num; |
+ --remaining_packets; |
} |
} |
- } |
- bool ended() const { return ended_; } |
+ // As mentioned above, all feedbacks must report with a received packet. |
+ feedback->AddReceivedPacket(seq_num, kBaseTimeUs); |
+ |
+ return true; |
+ } |
private: |
template <typename T> |
bool ReadData(T* value) { |
- RTC_CHECK(!ended_); |
- if (data_idx_ + sizeof(T) > data_.size()) { |
- ended_ = true; |
+ if (*size_ < sizeof(T)) { |
return false; |
+ } else { |
+ *value = FuzzInput<T>(data_, size_); |
+ return true; |
} |
- *value = ByteReader<T>::ReadBigEndian(&data_[data_idx_]); |
- data_idx_ += sizeof(T); |
- return true; |
} |
- const rtc::ArrayView<const uint8_t> data_; |
- bool ended_; |
- size_t data_idx_; |
+ const uint8_t** data_; |
+ size_t* size_; |
}; |
-} // namespace |
- |
-void FuzzOneInput(const uint8_t* data, size_t size) { |
- if (size < 3 * sizeof(uint16_t)) { |
- return; |
+bool Setup(const uint8_t** data, |
+ size_t* size, |
+ std::unique_ptr<TransportFeedbackPacketLossTracker>* tracker) { |
+ if (*size < 3 * sizeof(uint16_t)) { |
+ return false; |
} |
+ |
constexpr size_t kSeqNumHalf = 0x8000u; |
// 0x8000 >= max_window_size >= plr_min_num_packets > rplr_min_num_pairs >= 1 |
// (The distribution isn't uniform, but it's enough; more would be overkill.) |
- const size_t max_window_size = FuzzInRange(&data, &size, 2, kSeqNumHalf); |
+ const size_t max_window_size = FuzzInRange(data, size, 2, kSeqNumHalf); |
const size_t plr_min_num_packets = |
- FuzzInRange(&data, &size, 2, max_window_size); |
+ FuzzInRange(data, size, 2, max_window_size); |
const size_t rplr_min_num_pairs = |
- FuzzInRange(&data, &size, 1, plr_min_num_packets - 1); |
+ FuzzInRange(data, size, 1, plr_min_num_packets - 1); |
- TransportFeedbackPacketLossTracker tracker( |
- max_window_size, plr_min_num_packets, rplr_min_num_pairs); |
+ tracker->reset(new TransportFeedbackPacketLossTracker( |
+ max_window_size, plr_min_num_packets, rplr_min_num_pairs)); |
- TransportFeedbackGenerator feedback_generator( |
- rtc::ArrayView<const uint8_t>(data, size)); |
+ return true; |
+} |
+ |
+bool FuzzSequenceNumberDelta(const uint8_t** data, |
+ size_t* size, |
+ uint16_t* delta) { |
+ // Deltas fuzzed so that smaller deltas would be more likely, but even a |
+ // complete wrap-around would be possible. |
+ // Note: A delta of (x + 0x10000) is indistinguishable from a delta of (x), |
+ // so deltas are distributed in the range [0 : 0xffff]. |
+ // The exact distribution is: |
+ // * First seed in range [0 : 24] (~10% chance) -> delta is 1. |
+ // * First seed in range [25 : 240] (~85% chance) -> delta in range [2 : 217] |
+ // * First seed in range [241 : 255] (~5% chance) -> delta in [1 : 2^16] |
+ |
+ if (*size < sizeof(uint8_t)) { |
+ return false; |
+ } |
- while (!feedback_generator.ended()) { |
+ uint8_t first_seed = FuzzInput<uint8_t>(data, size); |
+ if (first_seed < 25) { |
+ *delta = 1; |
+ } else if (first_seed < 241) { |
+ *delta = first_seed - 24 + 1; |
+ } else if (*size < sizeof(uint16_t)) { |
+ return false; |
+ } else { |
+ *delta = FuzzInput<uint16_t>(data, size); // Note: 2^16 == 0 |
+ } |
+ |
+ return true; |
+} |
+ |
+bool FuzzPacketTransmissionBurst( |
+ std::unique_ptr<TransportFeedbackPacketLossTracker>& tracker, |
+ const uint8_t** data, |
+ size_t* size) { |
+ // We want to test with bursts lengths between 0 and 2^16, inclusive(!). |
+ // Easiest is to just disregard one potential burst size in the middle, and |
+ // assign it to be representative of 2^16. |
+ if (*size < sizeof(uint16_t)) { |
+ return false; |
+ } |
+ size_t packet_transmission_burst_len = FuzzInput<uint16_t>(data, size); |
+ constexpr size_t sentinel_for_wrap_around = 0x4321; |
+ if (packet_transmission_burst_len == sentinel_for_wrap_around) { |
+ packet_transmission_burst_len = 0xffff + 1; |
+ } |
+ |
+ if (packet_transmission_burst_len == 0) { |
+ return true; |
+ } |
+ |
+ // First sent sequence number uniformly selected. |
+ if (*size < sizeof(uint16_t)) { |
+ return false; |
+ } |
+ uint16_t seq_num = FuzzInput<uint16_t>(data, size); |
+ tracker->OnPacketAdded(seq_num); |
+ tracker->Validate(); |
+ |
+ // Fuzz subsequent sequence numbers according to a non-uniformly |
+ // distributed delta (to make sure the fuzzer-test does not end up |
+ // spending 99.9% of its time working on a mostly empty window). |
+ while (--packet_transmission_burst_len > 0) { |
+ uint16_t delta; |
+ bool may_continue = FuzzSequenceNumberDelta(data, size, &delta); |
+ if (!may_continue) |
+ return false; |
+ seq_num += delta; |
+ tracker->OnPacketAdded(seq_num); |
+ tracker->Validate(); |
+ } |
+ |
+ return true; |
+} |
+ |
+bool FuzzTransportFeedbackBurst( |
+ std::unique_ptr<TransportFeedbackPacketLossTracker>& tracker, |
+ const uint8_t** data, |
+ size_t* size) { |
+ // Fuzz the number of back-to-back feedbacks. At least one, or this would |
+ // be meaningless - we'd go straight back to fuzzing another packet |
+ // transmission burst. |
+ if (*size < sizeof(uint8_t)) { |
+ return false; |
+ } |
+ |
+ size_t feedbacks_num = 1 + (FuzzInput<uint8_t>(data, size) & 0x3f); |
+ TransportFeedbackGenerator feedback_generator(data, size); |
+ |
+ for (size_t i = 0; i < feedbacks_num; i++) { |
rtcp::TransportFeedback feedback; |
- feedback_generator.GetNextTransportFeedback(&feedback); |
- tracker.OnReceivedTransportFeedback(feedback); |
- tracker.Validate(); |
+ bool may_continue = feedback_generator.GetNextTransportFeedback(&feedback); |
+ if (!may_continue) { |
+ return false; |
+ } |
+ tracker->OnReceivedTransportFeedback(feedback); |
+ tracker->Validate(); |
+ } |
+ |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+void FuzzOneInput(const uint8_t* data, size_t size) { |
+ std::unique_ptr<TransportFeedbackPacketLossTracker> tracker; |
+ bool may_continue; |
+ |
+ may_continue = Setup(&data, &size, &tracker); |
+ |
+ while (may_continue) { |
+ may_continue = FuzzPacketTransmissionBurst(tracker, &data, &size); |
+ if (!may_continue) { |
+ return; |
+ } |
+ may_continue = FuzzTransportFeedbackBurst(tracker, &data, &size); |
} |
} |