| 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);
|
| }
|
| }
|
|
|
|
|