blob: d5385ec876b126f837a6f4802c63652d222fd70e [file] [log] [blame]
// Copyright 2018 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 LIB_FIDL_WALKER_H_
#define LIB_FIDL_WALKER_H_
#include <lib/fidl/coding.h>
#include <lib/fidl/internal.h>
#include <lib/fidl/visitor.h>
#include <stdalign.h>
#include <zircon/assert.h>
#include <zircon/compiler.h>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <type_traits>
namespace fidl {
namespace internal {
// Some assumptions about data type layout.
static_assert(offsetof(fidl_string_t, size) == 0u, "fidl_string_t layout");
static_assert(offsetof(fidl_string_t, data) == 8u, "fidl_string_t layout");
static_assert(sizeof(fidl_string_t) == 16u, "fidl_string_t layout");
static_assert(offsetof(fidl_vector_t, count) == 0u, "fidl_vector_t layout");
static_assert(offsetof(fidl_vector_t, data) == 8u, "fidl_vector_t layout");
static_assert(sizeof(fidl_vector_t) == 16u, "fidl_vector_t layout");
static_assert(offsetof(fidl_envelope_t, num_bytes) == 0u, "fidl_envelope_t layout");
static_assert(offsetof(fidl_envelope_t, inline_value) == 0u, "fidl_envelope_t layout");
static_assert(offsetof(fidl_envelope_t, num_handles) == 4u, "fidl_envelope_t layout");
static_assert(offsetof(fidl_envelope_t, flags) == 6u, "fidl_envelope_t layout");
static_assert(sizeof(fidl_envelope_t) == 8u, "fidl_envelope_t layout");
static_assert(ZX_HANDLE_INVALID == FIDL_HANDLE_ABSENT, "invalid handle equals absence marker");
constexpr uint32_t PrimitiveSize(const FidlCodedPrimitiveSubtype primitive) {
switch (primitive) {
case kFidlCodedPrimitiveSubtype_Bool:
case kFidlCodedPrimitiveSubtype_Int8:
case kFidlCodedPrimitiveSubtype_Uint8:
return 1;
case kFidlCodedPrimitiveSubtype_Int16:
case kFidlCodedPrimitiveSubtype_Uint16:
return 2;
case kFidlCodedPrimitiveSubtype_Int32:
case kFidlCodedPrimitiveSubtype_Uint32:
case kFidlCodedPrimitiveSubtype_Float32:
return 4;
case kFidlCodedPrimitiveSubtype_Int64:
case kFidlCodedPrimitiveSubtype_Uint64:
case kFidlCodedPrimitiveSubtype_Float64:
return 8;
}
__builtin_unreachable();
}
template <FidlWireFormatVersion WireFormatVersion>
constexpr uint32_t TypeSize(const fidl_type_t* type) {
switch (type->type_tag()) {
case kFidlTypePrimitive:
return PrimitiveSize(type->coded_primitive().type);
case kFidlTypeEnum:
return PrimitiveSize(type->coded_enum().underlying_type);
case kFidlTypeBits:
return PrimitiveSize(type->coded_bits().underlying_type);
case kFidlTypeStructPointer:
return sizeof(uint64_t);
case kFidlTypeHandle:
return sizeof(zx_handle_t);
case kFidlTypeStruct:
return type->coded_struct().size_v2;
case kFidlTypeTable:
return sizeof(fidl_vector_t);
case kFidlTypeXUnion:
return sizeof(fidl_union_t);
case kFidlTypeString:
return sizeof(fidl_string_t);
case kFidlTypeArray:
return type->coded_array().array_size_v2;
case kFidlTypeVector:
return sizeof(fidl_vector_t);
}
__builtin_unreachable();
}
enum Result { kContinue, kExit };
// Macro to insert the relevant goop required to support two control flows here in case of error:
// one where we keep reading after error, and another where we return immediately.
#define FIDL_STATUS_GUARD(status) \
if (unlikely((status) != Status::kSuccess)) { \
switch ((status)) { \
case Status::kSuccess: \
__builtin_unreachable(); \
case Status::kConstraintViolationError: \
if (VisitorImpl::kContinueAfterConstraintViolation) { \
return Result::kContinue; \
} else { \
return Result::kExit; \
} \
case Status::kMemoryError: \
return Result::kExit; \
} \
}
// Macro to handle exiting if called function signaled exit.
#define FIDL_RESULT_GUARD(result) \
if (unlikely((result) == Result::kExit)) { \
return Result::kExit; \
}
typedef uint8_t OutOfLineDepth;
#define INCREASE_DEPTH(depth) OutOfLineDepth(depth + 1)
#define FIDL_DEPTH_GUARD(depth) \
if (unlikely((depth) > FIDL_MAX_DEPTH)) { \
visitor_->OnError("recursion depth exceeded"); \
if (VisitorImpl::kContinueAfterConstraintViolation) { \
return Result::kContinue; \
} else { \
return Result::kExit; \
} \
}
// The Walker class traverses through a FIDL message by following its coding table and
// calling the visitor implementation. VisitorImpl must be a concrete implementation of the
// fidl::Visitor interface. The concrete type is used to eliminate dynamic dispatch.
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
class Walker final {
private:
using MutationTrait = typename VisitorImpl::MutationTrait;
using Position = typename VisitorImpl::Position;
using EnvelopeCheckpoint = typename VisitorImpl::EnvelopeCheckpoint;
using EnvelopeType = typename VisitorImpl::EnvelopeType;
using EnvelopePointer = typename VisitorImpl::EnvelopePointer;
using VisitorSuper = Visitor<WireFormatVersion, MutationTrait, Position, EnvelopeCheckpoint>;
using Status = typename VisitorSuper::Status;
static_assert(CheckVisitorInterface<VisitorSuper, VisitorImpl>(), "");
public:
Walker(VisitorImpl* visitor) : visitor_(visitor) {}
Result Walk(const fidl_type_t* type, Position position);
private:
VisitorImpl* visitor_;
// Optionally uses non-const pointers depending on if the visitor
// is declared as mutating or not.
template <typename T>
using Ptr = typename VisitorImpl::template Ptr<T>;
// Wrapper around Position::Get with friendlier syntax.
template <typename T>
Ptr<T> PtrTo(Position position) {
return position.template Get<T>();
}
Result WalkInternal(const fidl_type_t* type, Position position, OutOfLineDepth depth);
Result WalkIterableInternal(const fidl_type_t* type,
Walker<VisitorImpl, WireFormatVersion>::Position position,
uint32_t stride, uint32_t end_offset, OutOfLineDepth depth);
static bool NeedWalkPrimitive(const FidlCodedPrimitiveSubtype subtype);
Result WalkPrimitive(const FidlCodedPrimitive* fidl_coded_primitive, Position position);
Result WalkEnum(const FidlCodedEnum* fidl_coded_enum, Position position);
Result WalkBits(const FidlCodedBits* coded_bits, Position position);
Result WalkStruct(const FidlCodedStruct* coded_struct, Position position, OutOfLineDepth depth);
Result WalkStructPointer(const FidlCodedStructPointer* coded_struct, Position position,
OutOfLineDepth depth);
Result WalkEnvelope(Position envelope_position, const fidl_type_t* payload_type,
OutOfLineDepth depth, FidlIsResource is_resource);
Result WalkTable(const FidlCodedTable* coded_table, Position position, OutOfLineDepth depth);
Result WalkXUnion(const FidlCodedXUnion* coded_table, Position position, OutOfLineDepth depth);
Result WalkArray(const FidlCodedArray* coded_array, Position position, OutOfLineDepth depth);
Result WalkString(const FidlCodedString* coded_string, Position position, OutOfLineDepth depth);
Result WalkVector(const FidlCodedVector* coded_vector, Position position, OutOfLineDepth depth);
Result WalkHandle(const FidlCodedHandle* coded_handle, Position position);
};
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::Walk(
const fidl_type_t* type, Walker<VisitorImpl, WireFormatVersion>::Position position) {
return WalkInternal(type, position, 0);
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkInternal(
const fidl_type_t* type, Walker<VisitorImpl, WireFormatVersion>::Position position,
OutOfLineDepth depth) {
switch (type->type_tag()) {
case kFidlTypePrimitive:
return WalkPrimitive(&type->coded_primitive(), position);
case kFidlTypeEnum:
return WalkEnum(&type->coded_enum(), position);
case kFidlTypeBits:
return WalkBits(&type->coded_bits(), position);
case kFidlTypeStruct:
return WalkStruct(&type->coded_struct(), position, depth);
case kFidlTypeStructPointer:
return WalkStructPointer(&type->coded_struct_pointer(), position, depth);
case kFidlTypeTable:
return WalkTable(&type->coded_table(), position, depth);
case kFidlTypeXUnion:
return WalkXUnion(&type->coded_union(), position, depth);
case kFidlTypeArray:
return WalkArray(&type->coded_array(), position, depth);
case kFidlTypeString:
return WalkString(&type->coded_string(), position, depth);
case kFidlTypeHandle:
return WalkHandle(&type->coded_handle(), position);
case kFidlTypeVector:
return WalkVector(&type->coded_vector(), position, depth);
}
assert(false && "unhandled type");
return Result::kContinue;
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkIterableInternal(
const fidl_type_t* elem_type, Walker<VisitorImpl, WireFormatVersion>::Position position,
uint32_t stride, uint32_t end_offset, OutOfLineDepth depth) {
if (unlikely(!elem_type)) {
// TODO(https://fxbug.dev/42132833) Remove this case - it is only for tests.
return Result::kContinue;
}
if (elem_type->type_tag() == kFidlTypePrimitive &&
!NeedWalkPrimitive(elem_type->coded_primitive().type)) {
return Result::kContinue;
}
for (uint32_t offset = 0; offset < end_offset; offset += stride) {
auto result = WalkInternal(elem_type, position + offset, depth);
FIDL_RESULT_GUARD(result);
}
return Result::kContinue;
}
// Keep in sync with WalkPrimitive.
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
bool Walker<VisitorImpl, WireFormatVersion>::NeedWalkPrimitive(
const FidlCodedPrimitiveSubtype subtype) {
switch (subtype) {
case kFidlCodedPrimitiveSubtype_Bool:
return true;
default:
return false;
}
}
// Keep in sync with NeedWalkPrimitive.
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkPrimitive(
const FidlCodedPrimitive* fidl_coded_primitive,
Walker<VisitorImpl, WireFormatVersion>::Position position) {
switch (fidl_coded_primitive->type) {
case kFidlCodedPrimitiveSubtype_Bool: {
uint8_t value = *PtrTo<uint8_t>(position);
if (unlikely(value != 0 && value != 1)) {
visitor_->OnError("not a valid bool value");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
break;
}
default:
break;
}
return Result::kContinue;
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkEnum(
const FidlCodedEnum* coded_enum, Walker<VisitorImpl, WireFormatVersion>::Position position) {
if (coded_enum->strictness == kFidlStrictness_Flexible) {
return Result::kContinue;
}
uint64_t value;
switch (coded_enum->underlying_type) {
case kFidlCodedPrimitiveSubtype_Uint8:
value = *PtrTo<uint8_t>(position);
break;
case kFidlCodedPrimitiveSubtype_Uint16:
value = *PtrTo<uint16_t>(position);
break;
case kFidlCodedPrimitiveSubtype_Uint32:
value = *PtrTo<uint32_t>(position);
break;
case kFidlCodedPrimitiveSubtype_Uint64:
value = *PtrTo<uint64_t>(position);
break;
case kFidlCodedPrimitiveSubtype_Int8:
value = static_cast<uint64_t>(*PtrTo<int8_t>(position));
break;
case kFidlCodedPrimitiveSubtype_Int16:
value = static_cast<uint64_t>(*PtrTo<int16_t>(position));
break;
case kFidlCodedPrimitiveSubtype_Int32:
value = static_cast<uint64_t>(*PtrTo<int32_t>(position));
break;
case kFidlCodedPrimitiveSubtype_Int64:
value = static_cast<uint64_t>(*PtrTo<int64_t>(position));
break;
default:
__builtin_unreachable();
}
if (unlikely(!coded_enum->validate(value))) {
visitor_->OnError("not a valid enum member");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
return Result::kContinue;
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkBits(
const FidlCodedBits* coded_bits, Walker<VisitorImpl, WireFormatVersion>::Position position) {
if (coded_bits->strictness == kFidlStrictness_Flexible) {
return Result::kContinue;
}
uint64_t value;
switch (coded_bits->underlying_type) {
case kFidlCodedPrimitiveSubtype_Uint8:
value = *PtrTo<uint8_t>(position);
break;
case kFidlCodedPrimitiveSubtype_Uint16:
value = *PtrTo<uint16_t>(position);
break;
case kFidlCodedPrimitiveSubtype_Uint32:
value = *PtrTo<uint32_t>(position);
break;
case kFidlCodedPrimitiveSubtype_Uint64:
value = *PtrTo<uint64_t>(position);
break;
default:
__builtin_unreachable();
}
if (unlikely(value & ~coded_bits->mask)) {
visitor_->OnError("not a valid bits member");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
return Result::kContinue;
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkStruct(
const FidlCodedStruct* coded_struct, Walker<VisitorImpl, WireFormatVersion>::Position position,
OutOfLineDepth depth) {
if (unlikely(coded_struct->is_empty)) {
ZX_DEBUG_ASSERT(coded_struct->size_v2 == 1);
uint8_t value = *PtrTo<uint8_t>(position);
if (value != 0) {
visitor_->OnError("invalid empty struct");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
}
for (uint32_t i = 0; i < coded_struct->element_count; i++) {
const FidlStructElement& element = coded_struct->elements[i];
if (VisitorImpl::kOnlyWalkResources) {
if (!element.header.is_resource)
continue;
}
switch (element.header.element_type) {
case kFidlStructElementType_Field: {
const FidlStructField& field = element.field;
Position field_position = position + field.offset_v2;
Result result = WalkInternal(field.field_type, field_position, depth);
FIDL_RESULT_GUARD(result);
break;
}
case kFidlStructElementType_Padding64: {
const FidlStructPadding& padding = element.padding;
auto status = visitor_->VisitInternalPadding(position + padding.offset_v2, padding.mask_64);
FIDL_STATUS_GUARD(status);
break;
}
case kFidlStructElementType_Padding32: {
const FidlStructPadding& padding = element.padding;
auto status = visitor_->VisitInternalPadding(position + padding.offset_v2, padding.mask_32);
FIDL_STATUS_GUARD(status);
break;
}
case kFidlStructElementType_Padding16: {
const FidlStructPadding& padding = element.padding;
auto status = visitor_->VisitInternalPadding(position + padding.offset_v2, padding.mask_16);
FIDL_STATUS_GUARD(status);
break;
}
}
}
return Result::kContinue;
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkStructPointer(
const FidlCodedStructPointer* coded_struct_pointer,
Walker<VisitorImpl, WireFormatVersion>::Position position, OutOfLineDepth depth) {
if (*PtrTo<Ptr<void>>(position) == nullptr) {
return Result::kContinue;
}
OutOfLineDepth inner_depth = INCREASE_DEPTH(depth);
FIDL_DEPTH_GUARD(inner_depth);
Position obj_position;
auto status =
visitor_->VisitPointer(position, VisitorImpl::PointeeType::kOther, PtrTo<Ptr<void>>(position),
coded_struct_pointer->struct_type->size_v2, &obj_position);
FIDL_STATUS_GUARD(status);
return WalkStruct(coded_struct_pointer->struct_type, obj_position, inner_depth);
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkEnvelope(Position envelope_position,
const fidl_type_t* payload_type,
OutOfLineDepth depth,
bool is_resource) {
auto envelope = PtrTo<EnvelopeType>(envelope_position);
auto envelope_copy = *envelope;
auto v2_envelope = PtrTo<const fidl_envelope_t>(envelope_position);
EnvelopeCheckpoint checkpoint = visitor_->EnterEnvelope();
bool is_inline_bit_set;
switch (v2_envelope->flags) {
case FIDL_ENVELOPE_FLAGS_INLINING_MASK:
is_inline_bit_set = true;
break;
case 0:
is_inline_bit_set = false;
break;
default:
visitor_->OnError("Invalid inline marker in envelope");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
break;
}
bool is_inlined;
uint32_t type_size;
if (FidlIsZeroEnvelope(v2_envelope)) {
is_inlined = false;
} else if (payload_type != nullptr) {
type_size = TypeSize<WireFormatVersion>(payload_type);
is_inlined = type_size <= FIDL_ENVELOPE_INLINING_SIZE_THRESHOLD;
if (unlikely(VisitorImpl::kValidateEnvelopeInlineBit && is_inlined != is_inline_bit_set)) {
visitor_->OnError("Invalid inline bit in envelope");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
} else {
is_inlined = is_inline_bit_set;
}
if (is_inlined) {
OutOfLineDepth obj_depth = INCREASE_DEPTH(depth);
FIDL_DEPTH_GUARD(obj_depth);
if (payload_type != nullptr) {
uint32_t padding;
switch (type_size) {
case 1:
padding = 0xffffff00;
break;
case 2:
padding = 0xffff0000;
break;
case 3:
padding = 0xff000000;
break;
case 4:
padding = 0x00000000;
break;
default:
__builtin_unreachable();
}
auto status = visitor_->VisitInternalPadding(envelope_position, padding);
FIDL_STATUS_GUARD(status);
auto result = WalkInternal(payload_type, envelope_position, obj_depth);
FIDL_RESULT_GUARD(result);
} else {
auto status = visitor_->VisitUnknownEnvelope(envelope_copy, envelope, is_resource);
FIDL_STATUS_GUARD(status);
}
auto status = visitor_->LeaveInlinedEnvelope(envelope_copy, envelope, checkpoint);
FIDL_STATUS_GUARD(status);
return Result::kContinue;
}
if (!FidlIsZeroEnvelope(v2_envelope)) {
OutOfLineDepth obj_depth = INCREASE_DEPTH(depth);
FIDL_DEPTH_GUARD(obj_depth);
uint32_t num_bytes = payload_type != nullptr ? type_size : v2_envelope->num_bytes;
Position obj_position;
auto status = visitor_->VisitPointer(envelope_position, VisitorImpl::PointeeType::kEnvelope,
PtrTo<void*>(envelope_position), num_bytes, &obj_position);
FIDL_STATUS_GUARD(status);
if (likely(payload_type != nullptr)) {
auto result = WalkInternal(payload_type, obj_position, obj_depth);
FIDL_RESULT_GUARD(result);
} else {
status = visitor_->VisitUnknownEnvelope(envelope_copy, envelope, is_resource);
FIDL_STATUS_GUARD(status);
}
}
auto status = visitor_->LeaveEnvelope(envelope_copy, envelope, checkpoint);
FIDL_STATUS_GUARD(status);
return Result::kContinue;
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkTable(
const FidlCodedTable* coded_table, Walker<VisitorImpl, WireFormatVersion>::Position position,
OutOfLineDepth depth) {
uint32_t envelope_size = sizeof(fidl_envelope_t);
auto envelope_vector_ptr = PtrTo<fidl_vector_t>(position);
if (envelope_vector_ptr->data == nullptr) {
if (unlikely(envelope_vector_ptr->count != 0)) {
visitor_->OnError("Table envelope vector data absent but non-zero count");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
OutOfLineDepth array_depth = INCREASE_DEPTH(depth);
FIDL_DEPTH_GUARD(array_depth);
auto status = visitor_->VisitAbsentPointerInNonNullableCollection(&envelope_vector_ptr->data);
FIDL_STATUS_GUARD(status);
return Result::kContinue;
}
uint32_t size;
if (unlikely(mul_overflow(envelope_vector_ptr->count, envelope_size, &size))) {
visitor_->OnError("integer overflow calculating table size");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
OutOfLineDepth envelope_vector_depth = INCREASE_DEPTH(depth);
FIDL_DEPTH_GUARD(envelope_vector_depth);
Position envelope_vector_position;
auto status = visitor_->VisitPointer(position, VisitorImpl::PointeeType::kOther,
&envelope_vector_ptr->data, size, &envelope_vector_position);
FIDL_STATUS_GUARD(status);
const FidlTableField* next_field = coded_table->fields;
const FidlTableField* end_field = coded_table->fields + coded_table->field_count;
for (uint32_t field_index = 0; field_index < envelope_vector_ptr->count; field_index++) {
const uint32_t ordinal = field_index + 1;
const FidlTableField* known_field = nullptr;
if (next_field < end_field && next_field->ordinal == ordinal) {
known_field = next_field;
next_field++;
}
Position envelope_position = envelope_vector_position + field_index * envelope_size;
const fidl_type_t* payload_type = known_field ? known_field->type : nullptr;
auto result = WalkEnvelope(envelope_position, payload_type, envelope_vector_depth,
coded_table->is_resource);
FIDL_RESULT_GUARD(result);
}
return Result::kContinue;
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkXUnion(
const FidlCodedXUnion* coded_xunion, Walker<VisitorImpl, WireFormatVersion>::Position position,
OutOfLineDepth depth) {
auto xunion = PtrTo<fidl_union_t>(position);
const auto envelope_pos = position + offsetof(fidl_union_t, envelope);
auto envelope_ptr = &xunion->envelope;
// Validate zero-ordinal invariants
if (xunion->tag == 0) {
if (unlikely(envelope_ptr->num_bytes != 0 || envelope_ptr->num_handles != 0 ||
envelope_ptr->flags != 0)) {
visitor_->OnError("xunion with zero as ordinal must be empty");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
if (unlikely(!coded_xunion->nullable)) {
visitor_->OnError("non-nullable xunion is absent");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
return Result::kContinue;
// kValidateEnvelopeInlineBit indicates to the walker that the inline bit should have a valid
// value. In the case where the envelope is a pointer coming from an encoder this isn't the
// case. It is only possible to check if the envelope is zero if the envelope has valid bits for
// all of its bits.
} else if (unlikely(VisitorImpl::kValidateEnvelopeInlineBit &&
FidlIsZeroEnvelope(envelope_ptr))) {
visitor_->OnError("empty xunion must have zero as ordinal");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
const fidl_type_t* payload_type = nullptr;
if (xunion->tag <= coded_xunion->field_count) {
payload_type = coded_xunion->fields[xunion->tag - 1].type;
}
if (unlikely(payload_type == nullptr && coded_xunion->strictness == kFidlStrictness_Strict)) {
visitor_->OnError("strict xunion has unknown ordinal");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
return WalkEnvelope(envelope_pos, payload_type, depth, coded_xunion->is_resource);
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkArray(
const FidlCodedArray* coded_array, Walker<VisitorImpl, WireFormatVersion>::Position position,
OutOfLineDepth depth) {
return WalkIterableInternal(coded_array->element, position, coded_array->element_size_v2,
coded_array->array_size_v2, depth);
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkString(
const FidlCodedString* coded_string, Walker<VisitorImpl, WireFormatVersion>::Position position,
OutOfLineDepth depth) {
auto string_ptr = PtrTo<fidl_string_t>(position);
const uint64_t size = string_ptr->size;
auto status = visitor_->VisitVectorOrStringCount(&string_ptr->size);
FIDL_STATUS_GUARD(status);
if (string_ptr->data == nullptr) {
if (unlikely(size != 0)) {
visitor_->OnError("string is absent but length is not zero");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
if (unlikely(!coded_string->nullable)) {
OutOfLineDepth array_depth = INCREASE_DEPTH(depth);
FIDL_DEPTH_GUARD(array_depth);
status = visitor_->VisitAbsentPointerInNonNullableCollection(
&reinterpret_cast<Ptr<void>&>(const_cast<Ptr<char>&>(string_ptr->data)));
FIDL_STATUS_GUARD(status);
}
return Result::kContinue;
}
uint64_t bound = coded_string->max_size;
if (unlikely(size > std::numeric_limits<uint32_t>::max())) {
visitor_->OnError("string size overflows 32 bits");
FIDL_STATUS_GUARD(Status::kMemoryError);
}
if (unlikely(size > bound)) {
visitor_->OnError("message tried to access too large of a bounded string");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
OutOfLineDepth array_depth = INCREASE_DEPTH(depth);
FIDL_DEPTH_GUARD(array_depth);
Position array_position;
status = visitor_->VisitPointer(
position, VisitorImpl::PointeeType::kString,
&reinterpret_cast<Ptr<void>&>(const_cast<Ptr<char>&>(string_ptr->data)),
static_cast<uint32_t>(size), &array_position);
FIDL_STATUS_GUARD(status);
return Result::kContinue;
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkHandle(
const FidlCodedHandle* coded_handle,
Walker<VisitorImpl, WireFormatVersion>::Position position) {
auto handle_ptr = PtrTo<zx_handle_t>(position);
if (*handle_ptr == ZX_HANDLE_INVALID) {
if (unlikely(!coded_handle->nullable)) {
visitor_->OnError("message is missing a non-nullable handle");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
return Result::kContinue;
}
auto status = visitor_->VisitHandle(position, handle_ptr, coded_handle->handle_rights,
coded_handle->handle_subtype);
FIDL_STATUS_GUARD(status);
return Result::kContinue;
}
template <typename VisitorImpl, FidlWireFormatVersion WireFormatVersion>
Result Walker<VisitorImpl, WireFormatVersion>::WalkVector(
const FidlCodedVector* coded_vector, Walker<VisitorImpl, WireFormatVersion>::Position position,
OutOfLineDepth depth) {
auto vector_ptr = PtrTo<fidl_vector_t>(position);
const uint64_t count = vector_ptr->count;
auto status = visitor_->VisitVectorOrStringCount(&vector_ptr->count);
FIDL_STATUS_GUARD(status);
if (unlikely(vector_ptr->data == nullptr)) {
if (unlikely(count != 0)) {
visitor_->OnError("absent vector of non-zero elements");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
if (unlikely(!coded_vector->nullable)) {
OutOfLineDepth array_depth = INCREASE_DEPTH(depth);
FIDL_DEPTH_GUARD(array_depth);
status = visitor_->VisitAbsentPointerInNonNullableCollection(&vector_ptr->data);
FIDL_STATUS_GUARD(status);
}
return Result::kContinue;
}
if (unlikely(count > coded_vector->max_count)) {
visitor_->OnError("message tried to access too large of a bounded vector");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
uint32_t size;
if (unlikely(mul_overflow(count, coded_vector->element_size_v2, &size))) {
visitor_->OnError("integer overflow calculating vector size");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
OutOfLineDepth array_depth = INCREASE_DEPTH(depth);
FIDL_DEPTH_GUARD(array_depth);
Position array_position;
status = visitor_->VisitPointer(position, VisitorImpl::PointeeType::kVector, &vector_ptr->data,
size, &array_position);
FIDL_STATUS_GUARD(status);
uint32_t stride = coded_vector->element_size_v2;
ZX_ASSERT(count <= std::numeric_limits<uint32_t>::max());
uint32_t end_offset = uint32_t(count) * stride;
return WalkIterableInternal(coded_vector->element, array_position, stride, end_offset,
array_depth);
}
} // namespace internal
// Walks the FIDL message, calling hooks in the concrete VisitorImpl.
//
// |visitor| is an implementation of the fidl::Visitor interface.
// |type| is the coding table for the FIDL type. It cannot be null.
// |start| is the starting point for the walk.
template <FidlWireFormatVersion WireFormatVersion, typename VisitorImpl>
void Walk(VisitorImpl& visitor, const fidl_type_t* type, typename VisitorImpl::Position start) {
// It is pointless to walk in cases where |type == nullptr|, so ensure that such walkers are never
// constructed.
ZX_ASSERT(type != nullptr);
internal::Walker<VisitorImpl, WireFormatVersion> walker(&visitor);
walker.Walk(type, start);
}
// Infer the size of the primary object, from the coding table in |type|.
// Ensures that the primary object is of one of the expected types.
// This outputs both the primary size of the first inline object but also the
// position of the out of line object (if any) that follows the inline object.
//
// This assumes the following properties of the input parameters:
// - |type|, |out_primary_size|, |out_next_out_of_line| are non-null
// - |out_err| can optionally be null
//
// An error is returned if:
// - The primary object is neither a struct nor a table.
// - The first out-of-line offset is larger than the size of the buffer.
// - The aligned first out-of-line offset overflows 32 bits.
template <FidlWireFormatVersion WireFormatVersion>
zx_status_t PrimaryObjectSize(const fidl_type_t* type, uint32_t buffer_size,
uint32_t* out_primary_size, uint32_t* out_first_out_of_line,
const char** out_error) {
ZX_DEBUG_ASSERT(type != nullptr);
ZX_DEBUG_ASSERT(out_primary_size != nullptr);
ZX_DEBUG_ASSERT(out_first_out_of_line != nullptr);
auto set_error = [&out_error](const char* msg) {
if (out_error)
*out_error = msg;
};
// The struct case is "likely" because overhead for tables is less of a relative cost.
uint32_t primary_size;
if (likely(type->type_tag() == kFidlTypeStruct)) {
primary_size = type->coded_struct().size_v2;
} else if (likely(type->type_tag() == kFidlTypeTable)) {
primary_size = sizeof(fidl_table_t);
} else if (likely(type->type_tag() == kFidlTypeXUnion)) {
primary_size = sizeof(fidl_union_t);
} else {
set_error("Message must be a struct, table, or union");
return ZX_ERR_INVALID_ARGS;
}
*out_primary_size = static_cast<uint32_t>(primary_size);
uint64_t first_out_of_line = FidlAlign(static_cast<uint32_t>(primary_size));
if (unlikely(first_out_of_line > buffer_size)) {
set_error("Buffer is too small for first inline object");
return ZX_ERR_BUFFER_TOO_SMALL;
}
if (unlikely(first_out_of_line > std::numeric_limits<uint32_t>::max())) {
set_error("Out of line starting offset overflows");
return ZX_ERR_INVALID_ARGS;
}
*out_first_out_of_line = static_cast<uint32_t>(first_out_of_line);
return ZX_OK;
}
} // namespace fidl
#endif // LIB_FIDL_WALKER_H_