1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MOJO_PUBLIC_CPP_BASE_BIG_BUFFER_H_
#define MOJO_PUBLIC_CPP_BASE_BIG_BUFFER_H_
#include <stdint.h>
#include <optional>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/component_export.h"
#include "base/containers/checked_iterators.h"
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "base/memory/raw_span.h"
#include "mojo/public/cpp/bindings/struct_traits.h"
#include "mojo/public/cpp/system/buffer.h"
namespace mojo_base {
class BigBuffer;
class BigBufferView;
namespace internal {
// Internal helper used by BigBuffer when backed by shared memory.
class COMPONENT_EXPORT(MOJO_BASE) BigBufferSharedMemoryRegion {
public:
BigBufferSharedMemoryRegion();
BigBufferSharedMemoryRegion(mojo::ScopedSharedBufferHandle buffer_handle,
size_t size);
BigBufferSharedMemoryRegion(BigBufferSharedMemoryRegion&& other);
BigBufferSharedMemoryRegion(const BigBufferSharedMemoryRegion&) = delete;
BigBufferSharedMemoryRegion& operator=(const BigBufferSharedMemoryRegion&) =
delete;
~BigBufferSharedMemoryRegion();
BigBufferSharedMemoryRegion& operator=(BigBufferSharedMemoryRegion&& other);
void* memory() const { return buffer_mapping_.get(); }
size_t size() const { return size_; }
mojo::ScopedSharedBufferHandle TakeBufferHandle();
private:
friend class mojo_base::BigBuffer;
friend class mojo_base::BigBufferView;
size_t size_;
mojo::ScopedSharedBufferHandle buffer_handle_;
mojo::ScopedSharedBufferMapping buffer_mapping_;
};
} // namespace internal
// BigBuffer represents a potentially large sequence of bytes. When passed over
// mojom (as a mojo_base::mojom::BigBuffer union), it may serialize as either an
// inlined array of bytes or as a shared buffer handle with the payload copied
// into the corresponding shared memory region. This makes it easier to send
// payloads of varying and unbounded size over IPC without fear of hitting
// message size limits.
//
// A BigBuffer may be (implicitly) constructed over any span of bytes, and it
// exposes simple |data()| and |size()| accessors akin to what common container
// types provide. Users do not need to be concerned with the actual backing
// storage used to implement this interface.
//
// SECURITY NOTE: When shmem is backing the message, it may be writable in the
// sending process while being read in the receiving process. If a BigBuffer is
// received from an untrustworthy process, you should make a copy of the data
// before processing it to avoid time-of-check time-of-use (TOCTOU) bugs.
// The |size()| of the data cannot be manipulated.
class COMPONENT_EXPORT(MOJO_BASE) BigBuffer {
public:
using value_type = uint8_t;
using iterator = base::CheckedContiguousIterator<uint8_t>;
using const_iterator = base::CheckedContiguousIterator<const uint8_t>;
static constexpr size_t kMaxInlineBytes = 64 * 1024;
enum class StorageType {
kBytes,
kSharedMemory,
kInvalidBuffer,
};
// Defaults to empty kBytes storage.
BigBuffer();
// Constructs a BigBuffer over an existing span of bytes. Intentionally
// implicit for convenience. Always copies the contents of |data| into some
// internal storage.
BigBuffer(base::span<const uint8_t> data);
// Constructs a BigBuffer from an existing shared memory region. Not intended
// for general-purpose use.
explicit BigBuffer(internal::BigBufferSharedMemoryRegion shared_memory);
// Constructs a BigBuffer with the given size. The contents of buffer memory
// are uninitialized. Buffers constructed this way must be filled completely
// before transfer to avoid leaking information to less privileged processes.
explicit BigBuffer(size_t size);
BigBuffer(BigBuffer&& other);
BigBuffer& operator=(BigBuffer&& other);
~BigBuffer();
// Returns a new BigBuffer containing a copy of this BigBuffer's contents.
// Note that the new BigBuffer may not necessarily have the same backing
// storage type as the original one, only the same contents.
BigBuffer Clone() const;
// Returns a pointer to the data stored by this BigBuffer, regardless of
// backing storage type. Prefer to use `base::span(big_buffer)` instead, or
// the implicit conversion to `base::span`.
uint8_t* data() { return const_cast<uint8_t*>(std::as_const(*this).data()); }
const uint8_t* data() const;
// Returns the size of the data stored by this BigBuffer, regardless of
// backing storage type.
size_t size() const;
StorageType storage_type() const { return storage_type_; }
// WARNING: This method does not work for buffers backed by shared memory. To
// get a span independent of the storage type, write `base::span(big_buffer)`,
// or rely on the implicit conversion.
base::span<const uint8_t> byte_span() const {
CHECK_EQ(storage_type_, StorageType::kBytes);
return bytes_.as_span();
}
internal::BigBufferSharedMemoryRegion& shared_memory() {
CHECK_EQ(storage_type_, StorageType::kSharedMemory);
return shared_memory_.value();
}
iterator begin() {
uint8_t* const ptr = data();
// SAFETY: If this is an invalid buffer, `ptr` is null and `size()` is zero,
// which results in a well-defined (null) result. Otherwise, the underlying
// storage (`bytes_` or `shared_memory_`) guarantees that `ptr` points to at
// least `size()` bytes.
return UNSAFE_BUFFERS(iterator(ptr, ptr + size()));
}
const_iterator begin() const {
const uint8_t* const ptr = data();
// SAFETY: As in the non-const version above.
return UNSAFE_BUFFERS(const_iterator(ptr, ptr + size()));
}
const_iterator cbegin() const { return begin(); }
iterator end() {
uint8_t* const ptr = data();
const size_t len = size();
// SAFETY: As in `begin()` above.
return UNSAFE_BUFFERS(iterator(ptr, ptr + len, ptr + len));
}
const_iterator end() const {
const uint8_t* const ptr = data();
const size_t len = size();
// SAFETY: As in the non-const version above.
return UNSAFE_BUFFERS(const_iterator(ptr, ptr + len, ptr + len));
}
const_iterator cend() const { return end(); }
private:
friend class BigBufferView;
StorageType storage_type_ = StorageType::kBytes;
base::HeapArray<uint8_t> bytes_;
std::optional<internal::BigBufferSharedMemoryRegion> shared_memory_;
};
// Similar to BigBuffer, but doesn't *necessarily* own the buffer storage.
// Namely, if constructed over a small enough span of memory, it will simply
// retain a reference to that memory. This is generally only safe to use for
// serialization and deserialization.
class COMPONENT_EXPORT(MOJO_BASE) BigBufferView {
public:
BigBufferView();
BigBufferView(BigBufferView&& other);
// Constructs a BigBufferView over |bytes|. If |bytes| is large enough, this
// will allocate shared memory and copy the contents there. Otherwise this
// will retain an unsafe reference to |bytes| and must therefore not outlive
// |bytes|.
explicit BigBufferView(base::span<const uint8_t> bytes);
BigBufferView(const BigBufferView&) = delete;
BigBufferView& operator=(const BigBufferView&) = delete;
~BigBufferView();
BigBufferView& operator=(BigBufferView&& other);
base::span<const uint8_t> data() const;
// Explicitly retains a reference to |bytes| as the backing storage for this
// view. Does not copy and therefore |bytes| must remain valid throughout the
// view's lifetime. Used for deserialization.
void SetBytes(base::span<const uint8_t> bytes);
// Explictly adopts |shared_memory| as the backing storage for this view. Used
// for deserialization.
void SetSharedMemory(internal::BigBufferSharedMemoryRegion shared_memory);
// Converts to a BigBuffer which owns the viewed data. May have to copy data.
[[nodiscard]] static BigBuffer ToBigBuffer(BigBufferView view);
BigBuffer::StorageType storage_type() const { return storage_type_; }
// WARNING: This method does not work for buffers backed by shared memory. To
// get a span independent of the storage type, use `data()`.
base::span<const uint8_t> bytes() const {
DCHECK_EQ(storage_type_, BigBuffer::StorageType::kBytes);
return bytes_;
}
internal::BigBufferSharedMemoryRegion& shared_memory() {
DCHECK_EQ(storage_type_, BigBuffer::StorageType::kSharedMemory);
return shared_memory_.value();
}
static BigBufferView CreateInvalidForTest();
private:
BigBuffer::StorageType storage_type_ = BigBuffer::StorageType::kBytes;
base::raw_span<const uint8_t> bytes_;
std::optional<internal::BigBufferSharedMemoryRegion> shared_memory_;
};
} // namespace mojo_base
#endif // MOJO_PUBLIC_CPP_BASE_BIG_BUFFER_H_
|