webrtc/net/dcsctp/packet/bounded_byte_reader.h
Victor Boivie d579e6bc7b dcsctp: Do explicit bounds checking in bounded IO
The previous approach was that the caller was responsible for ensuring
that any buffer passed in to the Bounded IO wrappers, and that any
offset from where sub-readers were created were valid. The called would
always do a validation of the data and return proper error messages
if they were not.

This didn't pan out. https://crbug.com/1216758 found an overflow that
fooled the validation logic and the fuzzer could read out-of-bounds,
although it would always crash in that particular case.

There was already bounds checking, but under DCHECKs. This CL changes
that so that any bounds checking is done with CHECKS, as would've been
done in Rust. It's better to crash than to read arbitrary memory.

Bug: chromium:1216758
Change-Id: I89b52f0758495b5fe46f926c142870a263b96314
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/221743
Commit-Queue: Victor Boivie <boivie@webrtc.org>
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#34303}
2021-06-16 13:02:32 +00:00

99 lines
3.4 KiB
C++

/*
* Copyright (c) 2021 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.
*/
#ifndef NET_DCSCTP_PACKET_BOUNDED_BYTE_READER_H_
#define NET_DCSCTP_PACKET_BOUNDED_BYTE_READER_H_
#include <cstdint>
#include "api/array_view.h"
namespace dcsctp {
// TODO(boivie): These generic functions - and possibly this entire class -
// could be a candidate to have added to rtc_base/. They should use compiler
// intrinsics as well.
namespace internal {
// Loads a 8-bit unsigned word at `data`.
inline uint8_t LoadBigEndian8(const uint8_t* data) {
return data[0];
}
// Loads a 16-bit unsigned word at `data`.
inline uint16_t LoadBigEndian16(const uint8_t* data) {
return (data[0] << 8) | data[1];
}
// Loads a 32-bit unsigned word at `data`.
inline uint32_t LoadBigEndian32(const uint8_t* data) {
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
}
} // namespace internal
// BoundedByteReader wraps an ArrayView and divides it into two parts; A fixed
// size - which is the template parameter - and a variable size, which is what
// remains in `data` after the `FixedSize`.
//
// The BoundedByteReader provides methods to load/read big endian numbers from
// the FixedSize portion of the buffer, and these are read with static bounds
// checking, to avoid out-of-bounds accesses without a run-time penalty.
//
// The variable sized portion can either be used to create sub-readers, which
// themselves would provide compile-time bounds-checking, or the entire variable
// sized portion can be retrieved as an ArrayView.
template <int FixedSize>
class BoundedByteReader {
public:
explicit BoundedByteReader(rtc::ArrayView<const uint8_t> data) : data_(data) {
RTC_CHECK(data.size() >= FixedSize);
}
template <size_t offset>
uint8_t Load8() const {
static_assert(offset + sizeof(uint8_t) <= FixedSize, "Out-of-bounds");
return internal::LoadBigEndian8(&data_[offset]);
}
template <size_t offset>
uint16_t Load16() const {
static_assert(offset + sizeof(uint16_t) <= FixedSize, "Out-of-bounds");
static_assert((offset % sizeof(uint16_t)) == 0, "Unaligned access");
return internal::LoadBigEndian16(&data_[offset]);
}
template <size_t offset>
uint32_t Load32() const {
static_assert(offset + sizeof(uint32_t) <= FixedSize, "Out-of-bounds");
static_assert((offset % sizeof(uint32_t)) == 0, "Unaligned access");
return internal::LoadBigEndian32(&data_[offset]);
}
template <size_t SubSize>
BoundedByteReader<SubSize> sub_reader(size_t variable_offset) const {
RTC_CHECK(FixedSize + variable_offset + SubSize <= data_.size());
rtc::ArrayView<const uint8_t> sub_span =
data_.subview(FixedSize + variable_offset, SubSize);
return BoundedByteReader<SubSize>(sub_span);
}
size_t variable_data_size() const { return data_.size() - FixedSize; }
rtc::ArrayView<const uint8_t> variable_data() const {
return data_.subview(FixedSize, data_.size() - FixedSize);
}
private:
const rtc::ArrayView<const uint8_t> data_;
};
} // namespace dcsctp
#endif // NET_DCSCTP_PACKET_BOUNDED_BYTE_READER_H_