// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "mojo/public/cpp/bindings/lib/buffer.h" #include "base/logging.h" #include "base/numerics/safe_math.h" #include "mojo/public/c/system/message_pipe.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" namespace mojo { namespace internal { Buffer::Buffer() = default; Buffer::Buffer(void* data, size_t size, size_t cursor) : data_(data), size_(size), cursor_(cursor) { DCHECK(IsAligned(data_)); } Buffer::Buffer(MessageHandle message, size_t message_payload_size, void* data, size_t size) : message_(message), message_payload_size_(message_payload_size), data_(data), size_(size), cursor_(0) { DCHECK(IsAligned(data_)); } Buffer::Buffer(Buffer&& other) { *this = std::move(other); } Buffer::~Buffer() = default; Buffer& Buffer::operator=(Buffer&& other) { message_ = other.message_; message_payload_size_ = other.message_payload_size_; data_ = other.data_; size_ = other.size_; cursor_ = other.cursor_; other.Reset(); return *this; } size_t Buffer::Allocate(size_t num_bytes) { const size_t aligned_num_bytes = Align(num_bytes); const size_t new_cursor = cursor_ + aligned_num_bytes; if (new_cursor < cursor_ || (new_cursor > size_ && !message_.is_valid())) { // Either we've overflowed or exceeded a fixed capacity. NOTREACHED(); return 0; } if (new_cursor > size_) { // If we have an underlying message object we can extend its payload to // obtain more storage capacity. DCHECK_LE(message_payload_size_, new_cursor); size_t additional_bytes = new_cursor - message_payload_size_; DCHECK(base::IsValueInRangeForNumericType<uint32_t>(additional_bytes)); uint32_t new_size; MojoResult rv = MojoAppendMessageData( message_.value(), static_cast<uint32_t>(additional_bytes), nullptr, 0, nullptr, &data_, &new_size); DCHECK_EQ(MOJO_RESULT_OK, rv); message_payload_size_ = new_cursor; size_ = new_size; } DCHECK_LE(new_cursor, size_); size_t block_start = cursor_; cursor_ = new_cursor; // Ensure that all the allocated space is zeroed to avoid uninitialized bits // leaking into messages. // // TODO(rockot): We should consider only clearing the alignment padding. This // means being careful about generated bindings zeroing padding explicitly, // which itself gets particularly messy with e.g. packed bool bitfields. memset(static_cast<uint8_t*>(data_) + block_start, 0, aligned_num_bytes); return block_start; } void Buffer::AttachHandles(std::vector<ScopedHandle>* handles) { DCHECK(message_.is_valid()); uint32_t new_size = 0; MojoResult rv = MojoAppendMessageData( message_.value(), 0, reinterpret_cast<MojoHandle*>(handles->data()), static_cast<uint32_t>(handles->size()), nullptr, &data_, &new_size); if (rv != MOJO_RESULT_OK) return; size_ = new_size; for (auto& handle : *handles) ignore_result(handle.release()); } void Buffer::Seal() { if (!message_.is_valid()) return; // Ensure that the backing message has the final accumulated payload size. DCHECK_LE(message_payload_size_, cursor_); size_t additional_bytes = cursor_ - message_payload_size_; DCHECK(base::IsValueInRangeForNumericType<uint32_t>(additional_bytes)); MojoAppendMessageDataOptions options; options.struct_size = sizeof(options); options.flags = MOJO_APPEND_MESSAGE_DATA_FLAG_COMMIT_SIZE; void* data; uint32_t size; MojoResult rv = MojoAppendMessageData(message_.value(), static_cast<uint32_t>(additional_bytes), nullptr, 0, &options, &data, &size); DCHECK_EQ(MOJO_RESULT_OK, rv); message_ = MessageHandle(); message_payload_size_ = cursor_; data_ = data; size_ = size; } void Buffer::Reset() { message_ = MessageHandle(); data_ = nullptr; size_ = 0; cursor_ = 0; } } // namespace internal } // namespace mojo