| // 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 = FIDL_ALIGN(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_ |