blob: 8431cd67b1ecf69aa99b78740c23f1d38c31b9b8 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_NATURAL_DECODER_H_
#define SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_NATURAL_DECODER_H_
#include <lib/fidl/cpp/natural_coding_errors.h>
#include <lib/fidl/llcpp/message.h>
#include <zircon/fidl.h>
#ifdef __Fuchsia__
#include <lib/fidl/llcpp/internal/transport_channel.h>
#include <lib/zx/handle.h>
#include <lib/zx/object.h>
#else
#include <lib/fidl/llcpp/internal/transport_channel_host.h>
#endif
namespace fidl::internal {
class NaturalDecoder final {
public:
explicit NaturalDecoder(fidl::IncomingMessage message,
fidl::internal::WireFormatVersion wire_format_version);
~NaturalDecoder();
template <typename T>
T* GetPtr(size_t offset) {
ZX_DEBUG_ASSERT(offset <= body_.byte_actual());
return reinterpret_cast<T*>((body_.bytes() - body_offset_) + offset);
}
size_t GetOffset(const void* ptr) const { return GetOffset(reinterpret_cast<uintptr_t>(ptr)); }
size_t GetOffset(uintptr_t ptr) const {
// The |ptr| value comes from the message buffer, which we've already
// validated. That means it should correspond to a valid offset within the
// message.
size_t offset = ptr - reinterpret_cast<uintptr_t>(body_.bytes() - body_offset_);
ZX_DEBUG_ASSERT(offset <= body_.byte_actual());
return offset;
}
[[nodiscard]] bool Alloc(size_t size, size_t* offset) {
if (size > std::numeric_limits<uint32_t>::max()) {
SetError(kCodingErrorAllocationSizeExceeds32Bits);
return false;
}
size_t old = next_out_of_line_;
size_t next_unaligned = next_out_of_line_ + size;
size_t next = FIDL_ALIGN(next_unaligned);
if (next > body_.byte_actual()) {
SetError(kCodingErrorOutOfLineObjectExceedsMessageBounds);
return false;
}
uint64_t padding;
switch (next - next_unaligned) {
case 0:
padding = 0x0000000000000000;
break;
case 1:
padding = 0xff00000000000000;
break;
case 2:
padding = 0xffff000000000000;
break;
case 3:
padding = 0xffffff0000000000;
break;
case 4:
padding = 0xffffffff00000000;
break;
case 5:
padding = 0xffffffffff000000;
break;
case 6:
padding = 0xffffffffffff0000;
break;
case 7:
padding = 0xffffffffffffff00;
break;
default:
__builtin_unreachable();
}
if (*GetPtr<uint64_t>(next - 8) & padding) {
SetError(kCodingErrorInvalidPaddingBytes);
return false;
}
next_out_of_line_ = next;
*offset = old;
return true;
}
void DecodeHandle(fidl_handle_t* value, HandleAttributes attr, size_t offset, bool is_optional) {
zx_handle_t* handle = GetPtr<zx_handle_t>(offset);
switch (*handle) {
case FIDL_HANDLE_PRESENT: {
if (handle_index_ >= body_.handle_actual()) {
SetError(kCodingErrorTooManyHandlesConsumed);
return;
}
zx_handle_t& body_handle = body_.handles()[handle_index_];
if (body_.transport_vtable_->encoding_configuration->decode_process_handle) {
const char* error;
zx_status_t status =
body_.transport_vtable_->encoding_configuration->decode_process_handle(
&body_handle, attr, handle_index_, body_.message_.handle_metadata, &error);
if (status != ZX_OK) {
SetError(error);
return;
}
}
*value = body_handle;
body_handle = FIDL_HANDLE_INVALID;
++handle_index_;
return;
}
case FIDL_HANDLE_ABSENT: {
if (is_optional) {
*value = FIDL_HANDLE_INVALID;
} else {
SetError(kCodingErrorAbsentNonNullableHandle);
}
return;
}
default: {
SetError(kCodingErrorInvalidPresenceIndicator);
return;
}
}
}
struct EnvelopeUnknownDataInfoResult {
size_t value_offset;
uint32_t num_bytes;
uint16_t num_handles;
uint16_t flags;
};
EnvelopeUnknownDataInfoResult EnvelopeUnknownDataInfo(const fidl_envelope_v2_t* envelope) const {
const auto* unknown_data_envelope =
reinterpret_cast<const fidl_envelope_v2_unknown_data_t*>(envelope);
EnvelopeUnknownDataInfoResult result;
if ((unknown_data_envelope->flags & FIDL_ENVELOPE_FLAGS_INLINING_MASK) != 0) {
result.value_offset = GetOffset(&envelope->inline_value);
result.num_bytes = 4;
} else {
result.value_offset = unknown_data_envelope->out_of_line.offset;
result.num_bytes = unknown_data_envelope->out_of_line.num_bytes;
}
result.num_handles = unknown_data_envelope->num_handles;
result.flags = unknown_data_envelope->flags;
return result;
}
void SetError(const char* error) {
if (status_ != ZX_OK)
return;
status_ = ZX_ERR_INVALID_ARGS;
error_ = error;
}
WireFormatVersion wire_format() { return wire_format_version_; }
size_t CurrentLength() const { return next_out_of_line_; }
size_t CurrentHandleCount() const { return handle_index_; }
zx_status_t status() { return status_; }
const char* error() { return error_; }
private:
fidl::IncomingMessage body_;
// The body_offset_ is either 16 (when decoding the body of a transactional message, which is
// itself a concatenation of 2 FIDL messages, the header and body), or 0 (when decoding a
// standalone "at-rest" message body).
uint32_t body_offset_ = 0;
uint32_t handle_index_ = 0;
size_t next_out_of_line_ = 0;
WireFormatVersion wire_format_version_;
zx_status_t status_ = ZX_OK;
const char* error_ = nullptr;
};
} // namespace fidl::internal
#endif // SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_NATURAL_DECODER_H_