blob: bb2418c81a46a29dc1e40f2dcab0c0364999a6ef [file] [log] [blame]
/*
* Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <algorithm>
#include "webrtc/base/array_view.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
#include "webrtc/voice_engine/transport_feedback_packet_loss_tracker.h"
namespace webrtc {
namespace {
template <typename T>
T FuzzInput(const uint8_t** data, size_t* size) {
RTC_CHECK_GE(*size, sizeof(T));
T rc = ByteReader<T>::ReadBigEndian(*data);
*data += sizeof(T);
*size -= sizeof(T);
return rc;
}
size_t FuzzInRange(const uint8_t** data,
size_t* size,
size_t lower,
size_t upper) {
// Achieve a close-to-uniform distribution.
RTC_CHECK_LE(lower, upper);
RTC_CHECK_LT(upper - lower, 1 << (8 * sizeof(uint16_t)));
const size_t range = upper - lower;
const uint16_t fuzzed = FuzzInput<uint16_t>(data, size);
const size_t offset = (static_cast<float>(fuzzed) / 0x10000) * (range + 1);
RTC_CHECK_LE(offset, range); // (fuzzed <= 0xffff) -> (offset < range + 1)
return lower + offset;
}
class TransportFeedbackGenerator {
public:
explicit TransportFeedbackGenerator(rtc::ArrayView<const uint8_t> data)
: data_(data), ended_(false), data_idx_(0) {}
void GetNextTransportFeedback(rtcp::TransportFeedback* feedback) {
uint16_t base_seq_num = 0;
if (!ReadData<uint16_t>(&base_seq_num)) {
return;
}
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 seq_num = base_seq_num;
while (true) {
uint8_t status_byte = 0;
if (!ReadData<uint8_t>(&status_byte))
return;
// Each status byte contains 8 statuses.
for (size_t j = 0; j < 8; ++j) {
if (status_byte & 0x01) {
feedback->AddReceivedPacket(seq_num, kBaseTimeUs);
}
seq_num++;
if (seq_num >= base_seq_num + num_statuses) {
feedback->AddReceivedPacket(seq_num, kBaseTimeUs);
return;
}
status_byte >>= 1;
}
}
}
bool ended() const { return ended_; }
private:
template <typename T>
bool ReadData(T* value) {
RTC_CHECK(!ended_);
if (data_idx_ + sizeof(T) > data_.size()) {
ended_ = true;
return false;
}
*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_;
};
} // namespace
void FuzzOneInput(const uint8_t* data, size_t size) {
if (size < 3 * sizeof(uint16_t)) {
return;
}
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 plr_min_num_packets =
FuzzInRange(&data, &size, 2, max_window_size);
const size_t rplr_min_num_pairs =
FuzzInRange(&data, &size, 1, plr_min_num_packets - 1);
TransportFeedbackPacketLossTracker tracker(
max_window_size, plr_min_num_packets, rplr_min_num_pairs);
TransportFeedbackGenerator feedback_generator(
rtc::ArrayView<const uint8_t>(data, size));
while (!feedback_generator.ended()) {
rtcp::TransportFeedback feedback;
feedback_generator.GetNextTransportFeedback(&feedback);
tracker.OnReceivedTransportFeedback(feedback);
tracker.Validate();
}
}
} // namespace webrtc