mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-25 22:30:49 +08:00
[client] Clean up types and code style in LengthDelimitedRingBuffer
This CL cleans up types and code style comments from post-submit code review comments on https://crrev.com/c/4023618 . I also added fixes for potential overflows in varint length decoding and included new tests. Bug: crashpad:437 Change-Id: I0a3585036028d81f42d0d36e87cce4264f4ed9ad Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4199705 Commit-Queue: Justin Cohen <justincohen@chromium.org> Reviewed-by: Justin Cohen <justincohen@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
c11d49db88
commit
8071d3019e
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 The Crashpad Authors
|
||||
// Copyright 2023 The Crashpad Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -15,32 +15,48 @@
|
||||
#ifndef CRASHPAD_CLIENT_LENGTH_DELIMITED_RING_BUFFER_H_
|
||||
#define CRASHPAD_CLIENT_LENGTH_DELIMITED_RING_BUFFER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "base/numerics/safe_math.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Capacity of a `RingBufferData`, in bytes.
|
||||
using RingBufferCapacity = uint32_t;
|
||||
|
||||
namespace internal {
|
||||
|
||||
//! \brief Default capacity of `RingBufferData`, in bytes.
|
||||
inline constexpr uint32_t kDefaultRingBufferCapacity = 8192;
|
||||
inline constexpr RingBufferCapacity kDefaultRingBufferDataCapacity = 8192;
|
||||
|
||||
//! \brief A tuple holding the current range of bytes which can be read from or
|
||||
//! have been written to.
|
||||
struct Range final {
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
//! \brief The offset into a `RingBufferData` at which a `Range` begins.
|
||||
using Offset = uint32_t;
|
||||
|
||||
//! \brief The length inside a `RingBufferData` of a `Range` of data.
|
||||
using Length = uint32_t;
|
||||
|
||||
Offset offset;
|
||||
Length length;
|
||||
};
|
||||
|
||||
// This struct is persisted to disk, so its size must not change.
|
||||
static_assert(sizeof(Range) == 8,
|
||||
"struct Range is not packed on this platform");
|
||||
|
||||
//! \brief The number of bits encoded in each byte of a Base 128-encoded varint.
|
||||
inline constexpr int kBase128ByteValueBits = 7;
|
||||
|
||||
//! \!brief Calculates the length in bytes of `value` encoded using
|
||||
//! little-endian Base 128 varint encoding.
|
||||
//! \sa https://developers.google.com/protocol-buffers/docs/encoding#varints
|
||||
@ -59,24 +75,28 @@ static_assert(sizeof(Range) == 8,
|
||||
//!
|
||||
//! \!param[in] value Value to be encoded in Base 128 varint encoding.
|
||||
//! \!return The length in bytes of `value` in Base 128 varint encoding.
|
||||
constexpr uint32_t Base128VarintUint32EncodedLength(uint32_t value) {
|
||||
uint32_t size = 1;
|
||||
template <typename IntegerType>
|
||||
constexpr Range::Length Base128VarintEncodedLength(IntegerType value) {
|
||||
static_assert(std::is_unsigned<IntegerType>::value);
|
||||
|
||||
Range::Length size = 1;
|
||||
while (value >= 0x80) {
|
||||
value >>= 7;
|
||||
value >>= kBase128ByteValueBits;
|
||||
size++;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
template <uint32_t RingBufferCapacity>
|
||||
using RingBufferArray = std::array<uint8_t, RingBufferCapacity>;
|
||||
// Note that std::array capacity is a size_t, not a RingBufferCapacity.
|
||||
template <size_t ArrayCapacity>
|
||||
using RingBufferArray = std::array<uint8_t, ArrayCapacity>;
|
||||
|
||||
//! \return The size of the `RingBufferArray` as a `uint32_t`.
|
||||
template <size_t RingBufferCapacity>
|
||||
constexpr uint32_t RingBufferArraySize(
|
||||
const RingBufferArray<RingBufferCapacity>& ring_buffer_data) {
|
||||
static_assert(RingBufferCapacity <= std::numeric_limits<uint32_t>::max());
|
||||
return static_cast<uint32_t>(ring_buffer_data.size());
|
||||
//! \return The size of the `RingBufferArray` as a `Range::Length`.
|
||||
template <size_t ArrayCapacity>
|
||||
constexpr Range::Length RingBufferArraySize(
|
||||
const RingBufferArray<ArrayCapacity>& ring_buffer_data) {
|
||||
static_assert(ArrayCapacity <= std::numeric_limits<Range::Length>::max());
|
||||
return static_cast<Range::Length>(ring_buffer_data.size());
|
||||
}
|
||||
|
||||
//! \brief Reads data from the ring buffer into a target buffer.
|
||||
@ -98,21 +118,21 @@ template <typename RingBufferArrayType>
|
||||
bool ReadBytesFromRingBuffer(const RingBufferArrayType& ring_buffer_data,
|
||||
internal::Range& ring_buffer_read_range,
|
||||
uint8_t* target_buffer,
|
||||
uint32_t target_buffer_length) {
|
||||
Range::Length target_buffer_length) {
|
||||
if (target_buffer_length > ring_buffer_read_range.length) {
|
||||
return false;
|
||||
}
|
||||
if (target_buffer_length == 0) {
|
||||
return true;
|
||||
}
|
||||
const uint32_t initial_read_length = std::min(
|
||||
const Range::Length initial_read_length = std::min(
|
||||
target_buffer_length,
|
||||
RingBufferArraySize(ring_buffer_data) - ring_buffer_read_range.offset);
|
||||
memcpy(target_buffer,
|
||||
&ring_buffer_data[ring_buffer_read_range.offset],
|
||||
initial_read_length);
|
||||
if (initial_read_length < target_buffer_length) {
|
||||
const uint32_t remaining_read_length =
|
||||
const Range::Length remaining_read_length =
|
||||
target_buffer_length - initial_read_length;
|
||||
memcpy(target_buffer + initial_read_length,
|
||||
&ring_buffer_data[0],
|
||||
@ -125,8 +145,8 @@ bool ReadBytesFromRingBuffer(const RingBufferArrayType& ring_buffer_data,
|
||||
return true;
|
||||
}
|
||||
|
||||
//! \brief Reads a single little-endian Base 128 varint-encoded uint32 from the
|
||||
//! ring buffer.
|
||||
//! \brief Reads a single little-endian Base 128 varint-encoded integer from
|
||||
//! the ring buffer.
|
||||
//! \param[in] ring_buffer_data The ring buffer to read.
|
||||
//! \param[in,out] ring_buffer_read_range The range of the data available
|
||||
//! to read. Upon return, set to the remaining range of data available
|
||||
@ -141,23 +161,44 @@ bool ReadBytesFromRingBuffer(const RingBufferArrayType& ring_buffer_data,
|
||||
//! The varint can wrap around the end of the ring buffer, in which case the
|
||||
//! read continues at the beginning of the ring buffer (if the ring buffer is
|
||||
//! long enough).
|
||||
template <typename RingBufferArrayType>
|
||||
std::optional<int> ReadBase128VarintUint32FromRingBuffer(
|
||||
template <typename RingBufferArrayType, typename IntegerType>
|
||||
std::optional<Range::Length> ReadBase128VarintFromRingBuffer(
|
||||
const RingBufferArrayType& ring_buffer_data,
|
||||
internal::Range& ring_buffer_read_range,
|
||||
uint32_t& result) {
|
||||
IntegerType& result) {
|
||||
static_assert(std::is_unsigned<IntegerType>::value);
|
||||
|
||||
result = 0;
|
||||
uint8_t cur_varint_byte = 0;
|
||||
constexpr uint8_t kValueMask = 0x7f;
|
||||
constexpr uint8_t kContinuationMask = 0x80;
|
||||
int length = 0;
|
||||
Range::Length length = 0;
|
||||
do {
|
||||
if (!ReadBytesFromRingBuffer(
|
||||
ring_buffer_data, ring_buffer_read_range, &cur_varint_byte, 1)) {
|
||||
// No capacity remaining in `ring_buffer_read_range` to read the varint.
|
||||
return std::nullopt;
|
||||
}
|
||||
result |= static_cast<uint32_t>(cur_varint_byte & kValueMask)
|
||||
<< (length * 7);
|
||||
IntegerType cur_varint_value =
|
||||
static_cast<IntegerType>(cur_varint_byte & kValueMask);
|
||||
|
||||
// This is equivalent to:
|
||||
//
|
||||
// result |= (cur_varint_value << (length * kBase128ByteValueBits));
|
||||
//
|
||||
// but checks the result at each step for overflow, which handles two types
|
||||
// of invalid input:
|
||||
//
|
||||
// 1) Too many bytes with kContinuationMask set (e.g., trying to encode 6
|
||||
// bytes worth of data in a 32-bit value)
|
||||
// 2) Too many bits in the final byte (e.g., the 5th byte for a 32-bit value
|
||||
// has bits 33 and 34 set)
|
||||
IntegerType next_result_bits;
|
||||
if (!base::CheckLsh(cur_varint_value, length * kBase128ByteValueBits)
|
||||
.AssignIfValid(&next_result_bits)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
result |= next_result_bits;
|
||||
++length;
|
||||
} while ((cur_varint_byte & kContinuationMask) == kContinuationMask);
|
||||
return length;
|
||||
@ -180,22 +221,22 @@ std::optional<int> ReadBase128VarintUint32FromRingBuffer(
|
||||
//! long enough).
|
||||
template <typename RingBufferArrayType>
|
||||
bool WriteBytesToRingBuffer(const uint8_t* const source_buffer,
|
||||
uint32_t source_buffer_length,
|
||||
Range::Length source_buffer_length,
|
||||
RingBufferArrayType& ring_buffer_data,
|
||||
internal::Range& ring_buffer_write_range) {
|
||||
const uint32_t ring_buffer_bytes_remaining =
|
||||
const Range::Length ring_buffer_bytes_remaining =
|
||||
RingBufferArraySize(ring_buffer_data) - ring_buffer_write_range.length;
|
||||
if (source_buffer_length > ring_buffer_bytes_remaining) {
|
||||
return false;
|
||||
}
|
||||
const uint32_t initial_write_length = std::min(
|
||||
const Range::Length initial_write_length = std::min(
|
||||
source_buffer_length,
|
||||
RingBufferArraySize(ring_buffer_data) - ring_buffer_write_range.offset);
|
||||
memcpy(&ring_buffer_data[ring_buffer_write_range.offset],
|
||||
source_buffer,
|
||||
initial_write_length);
|
||||
if (initial_write_length < source_buffer_length) {
|
||||
const uint32_t remaining_write_length =
|
||||
const Range::Length remaining_write_length =
|
||||
source_buffer_length - initial_write_length;
|
||||
memcpy(&ring_buffer_data[0],
|
||||
source_buffer + initial_write_length,
|
||||
@ -208,8 +249,8 @@ bool WriteBytesToRingBuffer(const uint8_t* const source_buffer,
|
||||
return true;
|
||||
}
|
||||
|
||||
//! \brief Writes a single Base 128 varint-encoded little-endian uint32 into the
|
||||
//! ring buffer.
|
||||
//! \brief Writes a single Base 128 varint-encoded little-endian unsigned
|
||||
//! integer into the ring buffer.
|
||||
//! \param[in] value The value to encode and write into the ring buffer.
|
||||
//! \param[in] ring_buffer_data The ring buffer into which to write.
|
||||
//! \param[in,out] ring_buffer_write_range The range of the data available
|
||||
@ -223,16 +264,20 @@ bool WriteBytesToRingBuffer(const uint8_t* const source_buffer,
|
||||
//! The varint can wrap around the end of the ring buffer, in which case the
|
||||
//! write continues at the beginning of the ring buffer (if the ring buffer is
|
||||
//! long enough).
|
||||
template <typename RingBufferArrayType>
|
||||
std::optional<int> WriteBase128VarintUint32ToRingBuffer(
|
||||
uint32_t value,
|
||||
template <typename RingBufferArrayType, typename IntegerType>
|
||||
std::optional<int> WriteBase128VarintToRingBuffer(
|
||||
IntegerType value,
|
||||
RingBufferArrayType& ring_buffer_data,
|
||||
internal::Range& ring_buffer_write_range) {
|
||||
static_assert(std::is_unsigned<IntegerType>::value);
|
||||
|
||||
uint8_t cur_varint_byte;
|
||||
constexpr uint8_t kValueMask = 0x7f;
|
||||
constexpr uint8_t kContinuationMask = 0x80;
|
||||
|
||||
// Every varint encodes to at least 1 byte of data.
|
||||
int length = 1;
|
||||
|
||||
while (value > kValueMask) {
|
||||
cur_varint_byte =
|
||||
(static_cast<uint8_t>(value) & kValueMask) | kContinuationMask;
|
||||
@ -240,7 +285,7 @@ std::optional<int> WriteBase128VarintUint32ToRingBuffer(
|
||||
&cur_varint_byte, 1, ring_buffer_data, ring_buffer_write_range)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
value >>= 7;
|
||||
value >>= kBase128ByteValueBits;
|
||||
++length;
|
||||
}
|
||||
cur_varint_byte = static_cast<uint8_t>(value);
|
||||
@ -253,7 +298,8 @@ std::optional<int> WriteBase128VarintUint32ToRingBuffer(
|
||||
|
||||
} // namespace internal
|
||||
|
||||
//! \brief Storage for a ring buffer which can hold up to `RingBufferCapacity`
|
||||
//! \brief Storage for a ring buffer which can hold up to
|
||||
//! `RingBufferCapacity`
|
||||
//! bytes of Base 128-varint delimited variable-length items.
|
||||
//!
|
||||
//! This struct contains a header immediately followed by the ring buffer
|
||||
@ -268,12 +314,18 @@ std::optional<int> WriteBase128VarintUint32ToRingBuffer(
|
||||
//!
|
||||
//! The bytes of this structure are suitable for direct serialization from
|
||||
//! memory to disk, e.g. as a crashpad::Annotation.
|
||||
template <uint32_t RingBufferCapacity>
|
||||
template <RingBufferCapacity Capacity>
|
||||
struct RingBufferData final {
|
||||
RingBufferData() = default;
|
||||
RingBufferData(RingBufferData&) = delete;
|
||||
RingBufferData& operator=(RingBufferData&) = delete;
|
||||
|
||||
//! \brief The type of the array holding the data in this object.
|
||||
using RingBufferArrayType = internal::RingBufferArray<Capacity>;
|
||||
|
||||
//! \brief The type of the size in bytes of operations on this object.
|
||||
using SizeType = internal::Range::Length;
|
||||
|
||||
//! \brief Attempts to overwrite the contents of this object by deserializing
|
||||
//! the buffer into this object.
|
||||
//! \param[in] buffer The bytes to deserialize into this object.
|
||||
@ -281,7 +333,7 @@ struct RingBufferData final {
|
||||
//!
|
||||
//! \return `true` if the buffer was a valid RingBufferData and this object
|
||||
//! has enough capacity to store its bytes, `false` otherwise.
|
||||
bool DeserializeFromBuffer(const void* buffer, uint32_t length) {
|
||||
bool DeserializeFromBuffer(const void* buffer, SizeType length) {
|
||||
if (length < sizeof(header) || length > sizeof(header) + sizeof(data)) {
|
||||
return false;
|
||||
}
|
||||
@ -290,15 +342,16 @@ struct RingBufferData final {
|
||||
return false;
|
||||
}
|
||||
header.data_range = other_header->data_range;
|
||||
const uint8_t* other_ring_buffer_bytes = reinterpret_cast<const uint8_t*>(buffer) + sizeof(*other_header);
|
||||
const uint32_t other_ring_buffer_len = length - sizeof(*other_header);
|
||||
const uint8_t* other_ring_buffer_bytes =
|
||||
reinterpret_cast<const uint8_t*>(buffer) + sizeof(*other_header);
|
||||
const SizeType other_ring_buffer_len = length - sizeof(*other_header);
|
||||
memcpy(&data[0], other_ring_buffer_bytes, other_ring_buffer_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
//! \return The current length in bytes of the data written to the ring
|
||||
//! buffer.
|
||||
uint32_t GetRingBufferLength() const {
|
||||
SizeType GetRingBufferLength() const {
|
||||
internal::Range data_range = header.data_range;
|
||||
return sizeof(header) + std::min(internal::RingBufferArraySize(data),
|
||||
data_range.offset + data_range.length);
|
||||
@ -330,19 +383,22 @@ struct RingBufferData final {
|
||||
Header header;
|
||||
|
||||
//! \brief The bytes of the ring buffer data.
|
||||
internal::RingBufferArray<RingBufferCapacity> data;
|
||||
RingBufferArrayType data;
|
||||
|
||||
// This struct is persisted to disk, so its size must not change.
|
||||
static_assert(sizeof(Header) == 16);
|
||||
static_assert(RingBufferCapacity <= std::numeric_limits<uint32_t>::max());
|
||||
static_assert(Capacity <= std::numeric_limits<uint32_t>::max());
|
||||
};
|
||||
|
||||
// Ensure the ring buffer is packed correctly at its default capacity.
|
||||
static_assert(sizeof(RingBufferData<internal::kDefaultRingBufferCapacity>) ==
|
||||
16 + internal::kDefaultRingBufferCapacity);
|
||||
static_assert(
|
||||
sizeof(RingBufferData<internal::kDefaultRingBufferDataCapacity>) ==
|
||||
16 + internal::kDefaultRingBufferDataCapacity);
|
||||
|
||||
// Allow just `RingBufferData foo;` to be declared without template arguments
|
||||
// using CTAD.
|
||||
template <uint32_t Capacity = internal::kDefaultRingBufferCapacity>
|
||||
// using C++17 class template argument deduction.
|
||||
template <
|
||||
RingBufferCapacity Capacity = internal::kDefaultRingBufferDataCapacity>
|
||||
RingBufferData() -> RingBufferData<Capacity>;
|
||||
|
||||
//! \brief Reads variable-length data buffers from a `RingBufferData`,
|
||||
@ -392,8 +448,8 @@ class LengthDelimitedRingBufferReader final {
|
||||
//! past the end of the buffer read. Otherwise, returns `false`.
|
||||
bool PopWithRange(std::vector<uint8_t>& target_buffer,
|
||||
internal::Range& data_range) {
|
||||
uint32_t buffer_length;
|
||||
if (!ReadBase128VarintUint32FromRingBuffer(
|
||||
internal::Range::Length buffer_length;
|
||||
if (!ReadBase128VarintFromRingBuffer(
|
||||
ring_buffer_.data, data_range, buffer_length)) {
|
||||
return false;
|
||||
}
|
||||
@ -420,7 +476,7 @@ class LengthDelimitedRingBufferReader final {
|
||||
};
|
||||
|
||||
// Allow just `LengthDelimitedRingBufferReader reader(foo);` to be declared
|
||||
// without template arguments using CTAD.
|
||||
// without template arguments using C++17 class template argument deduction.
|
||||
template <typename RingBufferDataType>
|
||||
LengthDelimitedRingBufferReader(RingBufferDataType&)
|
||||
-> LengthDelimitedRingBufferReader<RingBufferDataType>;
|
||||
@ -460,28 +516,30 @@ class LengthDelimitedRingBufferWriter final {
|
||||
//! to reflect the remaining data available to read, and updates
|
||||
//! `ring_buffer_write_offset_` to reflec the current write positionl.
|
||||
//! Otherwise, returns `false`.
|
||||
bool Push(const void* const buffer, uint32_t buffer_length) {
|
||||
bool Push(const void* const buffer,
|
||||
typename RingBufferDataType::SizeType buffer_length) {
|
||||
if (buffer_length == 0) {
|
||||
// Pushing a zero-length buffer is not allowed
|
||||
// (`LengthDelimitedRingBufferWriter` reserves that to represent a
|
||||
// temporarily truncated item below).
|
||||
return false;
|
||||
}
|
||||
const uint32_t buffer_varint_encoded_length =
|
||||
internal::Base128VarintUint32EncodedLength(buffer_length);
|
||||
const uint32_t bytes_needed = buffer_varint_encoded_length + buffer_length;
|
||||
const internal::Range::Length buffer_varint_encoded_length =
|
||||
internal::Base128VarintEncodedLength(buffer_length);
|
||||
const internal::Range::Length bytes_needed =
|
||||
buffer_varint_encoded_length + buffer_length;
|
||||
if (bytes_needed > ring_buffer_.data.size()) {
|
||||
return false;
|
||||
}
|
||||
// If needed, move the readable region forward one buffer at a time to make
|
||||
// room for `buffer_length` bytes of new data.
|
||||
auto readable_data_range = ring_buffer_.header.data_range;
|
||||
uint32_t bytes_available =
|
||||
internal::Range::Length bytes_available =
|
||||
internal::RingBufferArraySize(ring_buffer_.data) -
|
||||
readable_data_range.length;
|
||||
while (bytes_available < bytes_needed) {
|
||||
uint32_t bytes_to_skip;
|
||||
std::optional<int> varint_length = ReadBase128VarintUint32FromRingBuffer(
|
||||
internal::Range::Length bytes_to_skip;
|
||||
auto varint_length = ReadBase128VarintFromRingBuffer(
|
||||
ring_buffer_.data, readable_data_range, bytes_to_skip);
|
||||
if (!varint_length.has_value()) {
|
||||
return false;
|
||||
@ -500,7 +558,7 @@ class LengthDelimitedRingBufferWriter final {
|
||||
bytes_needed,
|
||||
};
|
||||
|
||||
internal::WriteBase128VarintUint32ToRingBuffer(
|
||||
internal::WriteBase128VarintToRingBuffer(
|
||||
buffer_length, ring_buffer_.data, write_range);
|
||||
// Next, write the bytes from `buffer`.
|
||||
internal::WriteBytesToRingBuffer(
|
||||
@ -531,11 +589,11 @@ class LengthDelimitedRingBufferWriter final {
|
||||
RingBufferDataType& ring_buffer_;
|
||||
|
||||
// \brief Current write position next time `Push()` is invoked.
|
||||
uint32_t ring_buffer_write_offset_;
|
||||
internal::Range::Offset ring_buffer_write_offset_;
|
||||
};
|
||||
|
||||
// Allow just `LengthDelimitedRingBufferWriter writer(foo);` to be declared
|
||||
// without template arguments using CTAD.
|
||||
// without template arguments using C++17 class template argument deduction.
|
||||
template <typename RingBufferDataType>
|
||||
LengthDelimitedRingBufferWriter(RingBufferDataType&)
|
||||
-> LengthDelimitedRingBufferWriter<RingBufferDataType>;
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2022 The Crashpad Authors
|
||||
// Copyright 2023 The Crashpad Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -14,24 +14,22 @@
|
||||
|
||||
#include "client/length_delimited_ring_buffer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
using ::crashpad::LengthDelimitedRingBufferReader;
|
||||
using ::crashpad::LengthDelimitedRingBufferWriter;
|
||||
using ::crashpad::RingBufferData;
|
||||
|
||||
using ::testing::Eq;
|
||||
using ::testing::IsFalse;
|
||||
using ::testing::IsTrue;
|
||||
using testing::Eq;
|
||||
using testing::IsFalse;
|
||||
using testing::IsTrue;
|
||||
|
||||
// Buffer with magic 0xcab00d1e, version 1, read_pos 0, length 3, and 3 bytes of
|
||||
// data (1 varint length, 2 bytes data)
|
||||
@ -44,8 +42,8 @@ constexpr size_t kValidBufferSize3Len =
|
||||
// Buffer with magic 0xcab00d1e, version 8, read_pos 0, length 3, and 3 bytes of
|
||||
// data (1 varint length, 2 bytes data).
|
||||
constexpr char kInvalidVersionBuffer[] =
|
||||
"\x1e\x0d\xb0\xca\x08\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x02\xAB"
|
||||
"\xCD";
|
||||
"\x1e\x0d\xb0\xca\x08\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x02\xab"
|
||||
"\xcd";
|
||||
constexpr size_t kInvalidVersionBufferLen =
|
||||
sizeof(kInvalidVersionBuffer) - 1; // Remove trailing NUL.
|
||||
|
||||
@ -60,6 +58,32 @@ constexpr size_t kMidCrashBufferLen =
|
||||
|
||||
constexpr uint8_t kHello[] = {0x68, 0x65, 0x6c, 0x6c, 0x6f};
|
||||
|
||||
// Invalid buffer containing malformed varint in data payload (Base 128 varint
|
||||
// with length 6, which would represent a data length > 32 bits).
|
||||
constexpr char kInvalidBase128VarintBuffer[] =
|
||||
"\x1e\x0d\xb0\xca\x01\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x80\x80"
|
||||
"\x80\x80\x80\x01";
|
||||
constexpr size_t kInvalidBase128VarintBufferLen =
|
||||
sizeof(kInvalidBase128VarintBuffer) - 1; // Remove trailing NUL.
|
||||
|
||||
// Invalid buffer containing malformed varint in data payload (Base 128 varint
|
||||
// with length 5 but bits 33 and 34 set, which would represent a data length >
|
||||
// 32 bits).
|
||||
constexpr char kInvalidBase128VarintBits33And34SetBuffer[] =
|
||||
"\x1e\x0d\xb0\xca\x01\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x80\x80"
|
||||
"\x80\x80\x60";
|
||||
constexpr size_t kInvalidBase128VarintBits33And34SetBufferLen =
|
||||
sizeof(kInvalidBase128VarintBits33And34SetBuffer) -
|
||||
1; // Remove trailing NUL.
|
||||
|
||||
// Invalid buffer containing too-short data payload (varint length indicates
|
||||
// payload length is 4 but payload only contains 3 bytes).
|
||||
constexpr char kInvalidPayloadBufferTooShort[] =
|
||||
"\x1e\x0d\xb0\xca\x01\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x04"
|
||||
"\x42\x42\x42";
|
||||
constexpr size_t kInvalidPayloadBufferTooShortLen =
|
||||
sizeof(kInvalidPayloadBufferTooShort) - 1; // Remove trailing NUL.
|
||||
|
||||
TEST(LengthDelimitedRingBufferTest,
|
||||
RingBufferDataShouldStartWithMagicAndVersion) {
|
||||
RingBufferData ring_buffer;
|
||||
@ -218,10 +242,10 @@ TEST(LengthDelimitedRingBufferDataTest,
|
||||
|
||||
TEST(LengthDelimitedRingBufferDataTest, PushThenPopWithLengthVarintTwoBytes) {
|
||||
RingBufferData ring_buffer;
|
||||
std::string s(150, 'X');
|
||||
decltype(ring_buffer)::SizeType size = 150;
|
||||
std::string s(size, 'X');
|
||||
LengthDelimitedRingBufferWriter writer(ring_buffer);
|
||||
ASSERT_THAT(writer.Push(reinterpret_cast<const uint8_t*>(s.c_str()),
|
||||
static_cast<uint32_t>(s.length())),
|
||||
ASSERT_THAT(writer.Push(reinterpret_cast<const uint8_t*>(s.c_str()), size),
|
||||
IsTrue());
|
||||
|
||||
LengthDelimitedRingBufferReader reader(ring_buffer);
|
||||
@ -254,6 +278,44 @@ TEST(LengthDelimitedRingBufferDataTest,
|
||||
IsFalse());
|
||||
}
|
||||
|
||||
TEST(LengthDelimitedRingBufferDataTest,
|
||||
DeserializeFromInvalidVarintLengthShouldSucceedButPopShouldFail) {
|
||||
RingBufferData ring_buffer;
|
||||
EXPECT_THAT(ring_buffer.DeserializeFromBuffer(
|
||||
reinterpret_cast<const uint8_t*>(kInvalidBase128VarintBuffer),
|
||||
kInvalidBase128VarintBufferLen),
|
||||
IsTrue());
|
||||
LengthDelimitedRingBufferReader reader(ring_buffer);
|
||||
std::vector<uint8_t> data;
|
||||
EXPECT_THAT(reader.Pop(data), IsFalse());
|
||||
}
|
||||
|
||||
TEST(LengthDelimitedRingBufferDataTest,
|
||||
DeserializeFromInvalidVarintBitsShouldSucceedButPopShouldFail) {
|
||||
RingBufferData ring_buffer;
|
||||
EXPECT_THAT(ring_buffer.DeserializeFromBuffer(
|
||||
reinterpret_cast<const uint8_t*>(
|
||||
kInvalidBase128VarintBits33And34SetBuffer),
|
||||
kInvalidBase128VarintBits33And34SetBufferLen),
|
||||
IsTrue());
|
||||
LengthDelimitedRingBufferReader reader(ring_buffer);
|
||||
std::vector<uint8_t> data;
|
||||
EXPECT_THAT(reader.Pop(data), IsFalse());
|
||||
}
|
||||
|
||||
TEST(LengthDelimitedRingBufferDataTest,
|
||||
DeserializeFromInvalidPayloadBufferTooShortShouldSucceedButPopShouldFail) {
|
||||
RingBufferData ring_buffer;
|
||||
EXPECT_THAT(
|
||||
ring_buffer.DeserializeFromBuffer(
|
||||
reinterpret_cast<const uint8_t*>(kInvalidPayloadBufferTooShort),
|
||||
kInvalidPayloadBufferTooShortLen),
|
||||
IsTrue());
|
||||
LengthDelimitedRingBufferReader reader(ring_buffer);
|
||||
std::vector<uint8_t> data;
|
||||
EXPECT_THAT(reader.Pop(data), IsFalse());
|
||||
}
|
||||
|
||||
TEST(LengthDelimitedRingBufferDataTest,
|
||||
DeserializeFromFullBufferShouldSucceed) {
|
||||
RingBufferData<3> ring_buffer;
|
||||
@ -282,3 +344,5 @@ TEST(LengthDelimitedRingBufferDataTest,
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
Loading…
x
Reference in New Issue
Block a user