[fidl] Implement validator using visitor.
At this point, the migration from buffer_walker to visitor is complete.
There should be no external behavioral change.
TEST: CQ, and tryjobs in higher layers
Change-Id: I6859316f03b9cd61e17f1374b003741cd2caaff1
diff --git a/system/ulib/fidl/buffer_walker.h b/system/ulib/fidl/buffer_walker.h
deleted file mode 100644
index 93d9484..0000000
--- a/system/ulib/fidl/buffer_walker.h
+++ /dev/null
@@ -1,976 +0,0 @@
-// Copyright 2017 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 ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
-#define ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
-
-#include <lib/fidl/internal.h>
-#include <cstdlib>
-
-#include <cstdint>
-#include <cstdlib>
-#include <stdalign.h>
-#include <type_traits>
-
-#include <lib/fidl/coding.h>
-#include <lib/fidl/internal.h>
-#include <zircon/assert.h>
-#include <zircon/compiler.h>
-
-namespace fidl {
-namespace internal {
-
-// Some assumptions about data type layout.
-static_assert(offsetof(fidl_string_t, size) == 0u, "");
-static_assert(offsetof(fidl_string_t, data) == 8u, "");
-
-static_assert(offsetof(fidl_vector_t, count) == 0u, "");
-static_assert(offsetof(fidl_vector_t, data) == 8u, "");
-
-static_assert(ZX_HANDLE_INVALID == FIDL_HANDLE_ABSENT, "");
-
-template <bool kConst, class U>
-struct SetPtrConst;
-
-template <class U>
-struct SetPtrConst<false, U> {
- typedef U* type;
-};
-
-template <class U>
-struct SetPtrConst<true, U> {
- typedef const U* type;
-};
-
-// Walks over a FIDL buffer and validates/encodes/decodes it per-Derived.
-//
-// kMutating controls whether this deals with mutable bytes or immutable bytes
-// (validation wants immutable, encode/decode wants mutable)
-//
-// kContinueAfterErrors controls whether parsing is continued upon failure (encode needs this to
-// see all available handles).
-//
-// Derived should offer the following methods:
-//
-// const? uint8_t* bytes() - returns the start of the buffer of bytes
-// uint32_t num_bytes() - returns the number of bytes in said buffer
-// uint32_t num_handles() - returns the number of handles that are claimable
-// bool ValidateOutOfLineStorageClaim(const void* a, const void* b)
-// - returns true if a legally points to b
-// void UnclaimedHandle(zx_handle_t*) - notes that a handle was skipped
-// void ClaimedHandle(zx_handle_t*, uint32_t idx) - notes that a handle was claimed
-// PointerState GetPointerState(const void* ptr) - returns whether a pointer is present or not
-// HandleState GetHandleState(zx_handle_t) - returns if a handle is present or not
-// void UpdatePointer(T**p, T*v) - mutates a pointer representation for a present pointer
-// void SetError(const char* error_msg) - flags that an error occurred
-template <class Derived, bool kMutating, bool kContinueAfterErrors>
-class BufferWalker {
-public:
- explicit BufferWalker(const fidl_type* type)
- : type_(type) {}
-
- void Walk() {
- // The first decode is special. It must be a struct or a table.
- // We need to know the size of the first element to compute the start of
- // the out-of-line allocations.
-
- if (type_ == nullptr) {
- SetError("Cannot decode a null fidl type");
- return;
- }
-
- if (bytes() == nullptr) {
- SetError("Cannot decode null bytes");
- return;
- }
-
- switch (type_->type_tag) {
- case fidl::kFidlTypeStruct:
- if (num_bytes() < type_->coded_struct.size) {
- SetError("Message size is smaller than expected");
- return;
- }
- out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(type_->coded_struct.size));
- break;
- case fidl::kFidlTypeTable:
- if (num_bytes() < sizeof(fidl_vector_t)) {
- SetError("Message size is smaller than expected");
- return;
- }
- out_of_line_offset_ = static_cast<uint32_t>(fidl::FidlAlign(sizeof(fidl_vector_t)));
- break;
- default:
- SetError("Message must be a struct or a table");
- return;
- }
-
- Push(Frame::DoneSentinel());
- Push(Frame(type_, 0u));
-
-// Macro to insert the relevant goop required to support two control flows here:
-// one where we keep reading after error, and another where we return immediately.
-// No runtime overhead thanks to if constexpr magic.
-#define FIDL_POP_AND_CONTINUE_OR_RETURN \
- if (kContinueAfterErrors) { \
- Pop(); \
- continue; \
- } else { \
- return; \
- }
-
- for (;;) {
- Frame* frame = Peek();
-
- switch (frame->state) {
- case Frame::kStateStruct: {
- const uint32_t field_index = frame->NextStructField();
- if (field_index == frame->struct_state.field_count) {
- Pop();
- continue;
- }
- const fidl::FidlStructField& field = frame->struct_state.fields[field_index];
- const fidl_type_t* field_type = field.type;
- const uint32_t field_offset = frame->offset + field.offset;
- if (!Push(Frame(field_type, field_offset))) {
- SetError("recursion depth exceeded processing struct");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- continue;
- }
- case Frame::kStateStructPointer: {
- switch (GetPointerState(TypedAt<void>(frame->offset))) {
- case PointerState::PRESENT:
- break;
- case PointerState::ABSENT:
- Pop();
- continue;
- default:
- SetError("Tried to decode a bad struct pointer");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- auto struct_ptr_ptr = TypedAt<void*>(frame->offset);
- if (!ClaimOutOfLineStorage(frame->struct_pointer_state.struct_type->size,
- *struct_ptr_ptr, &frame->offset)) {
- SetError("message wanted to store too large of a nullable struct");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- UpdatePointer(struct_ptr_ptr, TypedAt<void>(frame->offset));
- const fidl::FidlCodedStruct* coded_struct = frame->struct_pointer_state.struct_type;
- *frame = Frame(coded_struct, frame->offset);
- continue;
- }
- case Frame::kStateTable: {
- if (frame->field == 0u) {
- auto envelope_vector_ptr = TypedAt<fidl_vector_t>(frame->offset);
- switch (GetPointerState(&envelope_vector_ptr->data)) {
- case PointerState::PRESENT:
- break;
- case PointerState::ABSENT:
- SetError("Table data cannot be absent");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- default:
- SetError("message tried to decode a non-present vector");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- uint32_t size;
- if (mul_overflow(envelope_vector_ptr->count, 2 * sizeof(uint64_t), &size)) {
- SetError("integer overflow calculating table size");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (!ClaimOutOfLineStorage(size, envelope_vector_ptr->data, &frame->offset)) {
- SetError("message wanted to store too large of a table");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- UpdatePointer(&envelope_vector_ptr->data, TypedAt<void>(frame->offset));
- frame->field = 1;
- frame->table_state.known_index = 0;
- frame->table_state.present_count = static_cast<uint32_t>(envelope_vector_ptr->count);
- frame->table_state.end_offset = out_of_line_offset_;
- frame->table_state.end_handle = handle_idx_;
- continue;
- }
- if (frame->table_state.end_offset != out_of_line_offset_) {
- SetError("Table field was mis-sized");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (frame->table_state.end_handle != handle_idx_) {
- SetError("Table handles were mis-sized");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (frame->field > frame->table_state.present_count) {
- Pop();
- continue;
- }
- const fidl::FidlTableField* known_field = nullptr;
- if (frame->table_state.known_index < frame->table_state.field_count) {
- const fidl::FidlTableField* field =
- &frame->table_state.fields[frame->table_state.known_index];
- if (field->ordinal == frame->field) {
- known_field = field;
- frame->table_state.known_index++;
- }
- }
- const uint32_t envelope_hdr_offset = static_cast<uint32_t>(
- frame->offset + (frame->field - 1) * 2 * sizeof(uint64_t));
- const uint32_t data_offset = static_cast<uint32_t>(
- envelope_hdr_offset + sizeof(uint64_t));
- const uint64_t packed_sizes = *TypedAt<uint64_t>(envelope_hdr_offset);
- frame->field++;
- switch (GetPointerState(TypedAt<void>(data_offset))) {
- case PointerState::PRESENT:
- if (packed_sizes != 0)
- break; // expected
-
- SetError("Table envelope has present data pointer, but no data, and no handles");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- case PointerState::ABSENT:
- if (packed_sizes == 0)
- continue; // skip
-
- SetError("Table envelope has absent data pointer, yet has data and/or handles");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- default:
- SetError("Table envelope has bad data pointer");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- uint32_t offset;
- uint32_t handles;
- const uint32_t table_bytes = static_cast<uint32_t>(packed_sizes & 0xffffffffu);
- const uint32_t table_handles = static_cast<uint32_t>(packed_sizes >> 32);
- if (add_overflow(out_of_line_offset_, table_bytes, &offset) || offset > num_bytes()) {
- SetError("table is larger than expected");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (add_overflow(handle_idx_, table_handles, &handles) ||
- handles > num_handles()) {
- SetError("table has more handles than expected");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- frame->table_state.end_offset = offset;
- frame->table_state.end_handle = handles;
- if (known_field != nullptr) {
- const fidl_type_t* field_type = known_field->type;
- uint32_t field_offset;
- if (!ClaimOutOfLineStorage(TypeSize(field_type), TypedAt<void*>(data_offset), &field_offset)) {
- SetError("table wanted too many bytes in field");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset));
- if (!Push(Frame(field_type, field_offset))) {
- SetError("recursion depth exceeded decoding table");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- } else {
- // Table data will not be processed: discard it.
- uint32_t field_offset;
- if (!ClaimOutOfLineStorage(table_bytes, TypedAt<void*>(data_offset), &field_offset)) {
- SetError("table wanted too many bytes in field");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- UpdatePointer(TypedAt<void*>(data_offset), TypedAt<void>(field_offset));
- for (uint32_t i = 0; i < table_handles; i++) {
- if (!ClaimHandle(nullptr)) {
- SetError("expected handle not present");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- }
- }
- continue;
- }
- case Frame::kStateTablePointer: {
- switch (GetPointerState(TypedAt<void>(frame->offset))) {
- case PointerState::PRESENT:
- break;
- case PointerState::ABSENT:
- Pop();
- continue;
- default:
- SetError("Tried to decode a bad table pointer");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- auto table_ptr_ptr = TypedAt<void*>(frame->offset);
- if (!ClaimOutOfLineStorage(sizeof(fidl_vector_t), *table_ptr_ptr, &frame->offset)) {
- SetError("message wanted to store too large of a nullable table");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- UpdatePointer(table_ptr_ptr, TypedAt<void>(frame->offset));
- const fidl::FidlCodedTable* coded_table = frame->table_pointer_state.table_type;
- *frame = Frame(coded_table, frame->offset);
- continue;
- }
- case Frame::kStateXUnion: {
- auto xunion = TypedAt<fidl_xunion_t>(frame->offset);
-
- if (xunion->padding != 0) {
- SetError("xunion padding after discriminant are non-zero");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
-
- uint32_t end_offset;
- if (add_overflow(out_of_line_offset_, xunion->envelope.num_bytes, &end_offset) || end_offset > num_bytes()) {
- SetError("xunion size is larger than expected");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
-
- uint32_t total_handle_count;
- if (add_overflow(handle_idx_, xunion->envelope.num_handles, &total_handle_count) ||
- total_handle_count > num_handles()) {
- SetError("xunion has more handles than expected");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
-
- switch (GetPointerState(&xunion->envelope.data)) {
- case PointerState::PRESENT:
- if (xunion->tag == 0) {
- SetError("xunion has zero discriminant but envelope is present");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- break;
- case PointerState::ABSENT:
- if (xunion->tag != 0) {
- SetError("xunion has non-zero discriminant but no data");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
-
- // Empty xunion.
- frame->offset += static_cast<uint32_t>(sizeof(fidl_xunion_t));
- Pop();
- continue;
- case PointerState::INVALID:
- default:
- SetError("xunion has invalid envelope pointer");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- break;
- }
-
- const FidlXUnionField* known_field = nullptr;
- for (size_t i = 0; i < frame->xunion_state.field_count; i++) {
- const auto field = frame->xunion_state.fields + i;
-
- if (field->ordinal == xunion->tag) {
- known_field = field;
- break;
- }
- }
-
- auto claim_data = [&](uint32_t known_size, uint32_t* envelope_offset) {
- if (!ClaimOutOfLineStorage(known_size, xunion->envelope.data, envelope_offset)) {
- SetError("xunion out-of-line storage claim couldn't be validated");
- return false;
- }
-
- if (*envelope_offset + xunion->envelope.num_bytes != end_offset) {
- SetError("expected xunion end offset doesn't match envelope offset + recursive size");
- return false;
- }
-
- UpdatePointer(&xunion->envelope.data, TypedAt<void>(*envelope_offset));
-
- return true;
- };
-
- uint32_t envelope_offset;
- if (known_field != nullptr) {
- if (!claim_data(TypeSize(known_field->type), &envelope_offset)) {
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
-
- frame->offset = envelope_offset;
- *frame = Frame(known_field->type, envelope_offset);
- } else {
- if (!claim_data(xunion->envelope.num_bytes, &envelope_offset)) {
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
-
- for (uint32_t i = 0; i < xunion->envelope.num_handles; i++) {
- if (!ClaimHandle(nullptr)) {
- SetError("expected handle not present");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- }
-
- frame->offset = end_offset;
- Pop();
- }
-
- continue;
- }
- case Frame::kStateXUnionPointer: {
- auto xunion_ptr_ptr = TypedAt<fidl_xunion_tag_t*>(frame->offset);
- switch (GetPointerState(TypedAt<void>(frame->offset))) {
- case PointerState::PRESENT:
- break;
- case PointerState::ABSENT:
- Pop();
- continue;
- default:
- SetError("Tried to decode a bad xunion pointer");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (!ClaimOutOfLineStorage(sizeof(fidl_xunion_t), *xunion_ptr_ptr,
- &frame->offset)) {
- SetError("message wanted to store too large of a nullable xunion");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- UpdatePointer(xunion_ptr_ptr, TypedAt<fidl_xunion_tag_t>(frame->offset));
- const fidl::FidlCodedXUnion* coded_xunion = frame->xunion_pointer_state.xunion_type;
- *frame = Frame(coded_xunion, frame->offset);
- continue;
- }
- case Frame::kStateUnion: {
- fidl_union_tag_t union_tag = *TypedAt<fidl_union_tag_t>(frame->offset);
- if (union_tag >= frame->union_state.type_count) {
- SetError("Tried to decode a bad union discriminant");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- const fidl_type_t* member = frame->union_state.types[union_tag];
- if (!member) {
- Pop();
- continue;
- }
- frame->offset += frame->union_state.data_offset;
- *frame = Frame(member, frame->offset);
- continue;
- }
- case Frame::kStateUnionPointer: {
- auto union_ptr_ptr = TypedAt<fidl_union_tag_t*>(frame->offset);
- switch (GetPointerState(TypedAt<void>(frame->offset))) {
- case PointerState::PRESENT:
- break;
- case PointerState::ABSENT:
- Pop();
- continue;
- default:
- SetError("Tried to decode a bad union pointer");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (!ClaimOutOfLineStorage(frame->union_pointer_state.union_type->size, *union_ptr_ptr,
- &frame->offset)) {
- SetError("message wanted to store too large of a nullable union");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- UpdatePointer(union_ptr_ptr, TypedAt<fidl_union_tag_t>(frame->offset));
- const fidl::FidlCodedUnion* coded_union = frame->union_pointer_state.union_type;
- *frame = Frame(coded_union, frame->offset);
- continue;
- }
- case Frame::kStateArray: {
- const uint32_t element_offset = frame->NextArrayOffset();
- if (element_offset == frame->array_state.array_size) {
- Pop();
- continue;
- }
- const fidl_type_t* element_type = frame->array_state.element;
- const uint32_t offset = frame->offset + element_offset;
- if (!Push(Frame(element_type, offset))) {
- SetError("recursion depth exceeded decoding array");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- continue;
- }
- case Frame::kStateString: {
- auto string_ptr = TypedAt<fidl_string_t>(frame->offset);
- // The string storage may be Absent for nullable strings and must
- // otherwise be Present. No other values are allowed.
- switch (GetPointerState(&string_ptr->data)) {
- case PointerState::PRESENT:
- break;
- case PointerState::ABSENT:
- if (!frame->string_state.nullable) {
- SetError("message tried to decode an absent non-nullable string");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (string_ptr->size != 0u) {
- SetError("message tried to decode an absent string of non-zero length");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- Pop();
- continue;
- default:
- SetError(
- "message tried to decode a string that is neither present nor absent");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- uint64_t bound = frame->string_state.max_size;
- uint64_t size = string_ptr->size;
- if (size > bound) {
- SetError("message tried to decode too large of a bounded string");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- uint32_t string_data_offset = 0u;
- if (!ClaimOutOfLineStorage(static_cast<uint32_t>(size), string_ptr->data, &string_data_offset)) {
- SetError("decoding a string overflowed buffer");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- UpdatePointer(&string_ptr->data, TypedAt<char>(string_data_offset));
- Pop();
- continue;
- }
- case Frame::kStateHandle: {
- auto handle_ptr = TypedAt<zx_handle_t>(frame->offset);
- // The handle storage may be Absent for nullable handles and must
- // otherwise be Present. No other values are allowed.
- switch (GetHandleState(*handle_ptr)) {
- case HandleState::ABSENT:
- if (frame->handle_state.nullable) {
- Pop();
- continue;
- }
- SetError("message tried to decode a non-present handle");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- case HandleState::PRESENT:
- if (!ClaimHandle(handle_ptr)) {
- SetError("message decoded too many handles");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- Pop();
- continue;
- case HandleState::INVALID:
- default: {
- // The value at the handle was garbage.
- SetError("message tried to decode a garbage handle");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- }
- }
- case Frame::kStateVector: {
- auto vector_ptr = TypedAt<fidl_vector_t>(frame->offset);
- // The vector storage may be Absent for nullable vectors and must
- // otherwise be Present. No other values are allowed.
- switch (GetPointerState(&vector_ptr->data)) {
- case PointerState::PRESENT:
- break;
- case PointerState::ABSENT:
- if (!frame->vector_state.nullable) {
- SetError("message tried to decode an absent non-nullable vector");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (vector_ptr->count != 0u) {
- SetError("message tried to decode an absent vector of non-zero elements");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- Pop();
- continue;
- default:
- SetError("message tried to decode a non-present vector");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (vector_ptr->count > frame->vector_state.max_count) {
- SetError("message tried to decode too large of a bounded vector");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- uint32_t size;
- if (mul_overflow(vector_ptr->count, frame->vector_state.element_size, &size)) {
- SetError("integer overflow calculating vector size");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- if (!ClaimOutOfLineStorage(size, vector_ptr->data, &frame->offset)) {
- SetError("message wanted to store too large of a vector");
- FIDL_POP_AND_CONTINUE_OR_RETURN;
- }
- UpdatePointer(&vector_ptr->data, TypedAt<void>(frame->offset));
- if (frame->vector_state.element) {
- // Continue by decoding the vector elements as an array.
- *frame = Frame(frame->vector_state.element, size,
- frame->vector_state.element_size, frame->offset);
- } else {
- // If there is no element type pointer, there is
- // nothing to decode in the vector secondary
- // payload. So just continue.
- Pop();
- }
- continue;
- }
- case Frame::kStateDone: {
- if (out_of_line_offset_ != num_bytes()) {
- SetError("message did not decode all provided bytes");
- }
- return;
- }
- }
- }
-
-#undef FIDL_POP_AND_CONTINUE_OR_RETURN
- }
-
-protected:
- void SetError(const char* error_msg) {
- derived()->SetError(error_msg);
- }
-
- template <typename T>
- typename SetPtrConst<!kMutating, T>::type TypedAt(uint32_t offset) const {
- return reinterpret_cast<typename SetPtrConst<!kMutating, T>::type>(bytes() + offset);
- }
-
- enum class PointerState : uintptr_t {
- PRESENT = FIDL_ALLOC_PRESENT,
- ABSENT = FIDL_ALLOC_ABSENT,
- INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value.
- };
-
- enum class HandleState : zx_handle_t {
- PRESENT = FIDL_HANDLE_PRESENT,
- ABSENT = FIDL_HANDLE_ABSENT,
- INVALID = 1 // *OR* *ANY* non PRESENT/ABSENT value.
- };
-
- uint32_t handle_idx() const { return handle_idx_; }
-
-private:
- Derived* derived() {
- return static_cast<Derived*>(this);
- }
-
- const Derived* derived() const {
- return static_cast<const Derived*>(this);
- }
-
- // Returns a pointer to the bytes in the message.
- auto bytes() const {
- return derived()->bytes();
- }
-
- // Returns the number of bytes in the message.
- auto num_bytes() const {
- return derived()->num_bytes();
- }
-
- // Returns the number of handles in the message (encoding: the max number of handles in the message).
- auto num_handles() const {
- return derived()->num_handles();
- }
-
- // Returns PRESENT/ABSENT/INVALID for a given pointer value.
- PointerState GetPointerState(const void* ptr) const {
- return derived()->GetPointerState(ptr);
- }
-
- // Returns PRESENT/ABSENT/INVALID for a given handle value.
- HandleState GetHandleState(zx_handle_t p) const {
- return derived()->GetHandleState(p);
- }
-
- // If required: mutate a pointer to the dual representation.
- template <class T2, class T1>
- void UpdatePointer(T2 p, T1 v) {
- derived()->UpdatePointer(p, v);
- }
-
- // Returns true when a handle was claimed, and false when the
- // handles are exhausted.
- template <class ZxHandleTPointer>
- bool ClaimHandle(ZxHandleTPointer out_handle) {
- if (handle_idx_ == num_handles()) {
- derived()->UnclaimedHandle(out_handle);
- return false;
- }
- derived()->ClaimedHandle(out_handle, handle_idx_);
- ++handle_idx_;
- return true;
- }
-
- // Returns true when the buffer space is claimed, and false when
- // the requested claim is too large for bytes_.
- bool ClaimOutOfLineStorage(uint32_t size, const void* storage, uint32_t* out_offset) {
- if (!derived()->ValidateOutOfLineStorageClaim(storage, &bytes()[out_of_line_offset_])) {
- return false;
- }
-
- // We have to manually maintain alignment here. For example, a pointer
- // to a struct that is 4 bytes still needs to advance the next
- // out-of-line offset by 8 to maintain the aligned-to-FIDL_ALIGNMENT
- // property.
- static constexpr uint32_t mask = FIDL_ALIGNMENT - 1;
- uint32_t offset = out_of_line_offset_;
- if (add_overflow(offset, size, &offset) ||
- add_overflow(offset, mask, &offset)) {
- return false;
- }
- offset &= ~mask;
-
- if (offset > num_bytes()) {
- return false;
- }
- *out_offset = out_of_line_offset_;
- out_of_line_offset_ = offset;
- return true;
- }
-
- uint32_t TypeSize(const fidl_type_t* type) {
- switch (type->type_tag) {
- case fidl::kFidlTypeStructPointer:
- case fidl::kFidlTypeTablePointer:
- case fidl::kFidlTypeUnionPointer:
- case fidl::kFidlTypeXUnionPointer:
- return sizeof(uint64_t);
- case fidl::kFidlTypeHandle:
- return sizeof(zx_handle_t);
- case fidl::kFidlTypeStruct:
- return type->coded_struct.size;
- case fidl::kFidlTypeTable:
- return sizeof(fidl_vector_t);
- case fidl::kFidlTypeUnion:
- return type->coded_union.size;
- case fidl::kFidlTypeXUnion:
- return sizeof(fidl_xunion_t);
- case fidl::kFidlTypeString:
- return sizeof(fidl_string_t);
- case fidl::kFidlTypeArray:
- return type->coded_array.array_size;
- case fidl::kFidlTypeVector:
- return sizeof(fidl_vector_t);
- }
- abort();
- return 0;
- }
-
- // Functions that manipulate the decoding stack frames.
- struct Frame {
- Frame(const fidl_type_t* fidl_type, uint32_t offset)
- : offset(offset) {
- switch (fidl_type->type_tag) {
- case fidl::kFidlTypeStruct:
- state = kStateStruct;
- struct_state.fields = fidl_type->coded_struct.fields;
- struct_state.field_count = fidl_type->coded_struct.field_count;
- break;
- case fidl::kFidlTypeStructPointer:
- state = kStateStructPointer;
- struct_pointer_state.struct_type = fidl_type->coded_struct_pointer.struct_type;
- break;
- case fidl::kFidlTypeTable:
- state = kStateTable;
- table_state.fields = fidl_type->coded_table.fields;
- table_state.field_count = fidl_type->coded_table.field_count;
- table_state.present_count = 0;
- break;
- case fidl::kFidlTypeTablePointer:
- state = kStateTablePointer;
- table_pointer_state.table_type = fidl_type->coded_table_pointer.table_type;
- break;
- case fidl::kFidlTypeUnion:
- state = kStateUnion;
- union_state.types = fidl_type->coded_union.types;
- union_state.type_count = fidl_type->coded_union.type_count;
- union_state.data_offset = fidl_type->coded_union.data_offset;
- break;
- case fidl::kFidlTypeUnionPointer:
- state = kStateUnionPointer;
- union_pointer_state.union_type = fidl_type->coded_union_pointer.union_type;
- break;
- case fidl::kFidlTypeXUnion:
- state = kStateXUnion;
- xunion_state.fields = fidl_type->coded_xunion.fields;
- xunion_state.field_count = fidl_type->coded_xunion.field_count;
- break;
- case fidl::kFidlTypeXUnionPointer:
- state = kStateXUnionPointer;
- xunion_pointer_state.xunion_type = fidl_type->coded_xunion_pointer.xunion_type;
- break;
- case fidl::kFidlTypeArray:
- state = kStateArray;
- array_state.element = fidl_type->coded_array.element;
- array_state.array_size = fidl_type->coded_array.array_size;
- array_state.element_size = fidl_type->coded_array.element_size;
- break;
- case fidl::kFidlTypeString:
- state = kStateString;
- string_state.max_size = fidl_type->coded_string.max_size;
- string_state.nullable = fidl_type->coded_string.nullable;
- break;
- case fidl::kFidlTypeHandle:
- state = kStateHandle;
- handle_state.nullable = fidl_type->coded_handle.nullable;
- break;
- case fidl::kFidlTypeVector:
- state = kStateVector;
- vector_state.element = fidl_type->coded_vector.element;
- vector_state.max_count = fidl_type->coded_vector.max_count;
- vector_state.element_size = fidl_type->coded_vector.element_size;
- vector_state.nullable = fidl_type->coded_vector.nullable;
- break;
- }
- }
-
- Frame(const fidl::FidlCodedStruct* coded_struct, uint32_t offset)
- : offset(offset) {
- state = kStateStruct;
- struct_state.fields = coded_struct->fields;
- struct_state.field_count = coded_struct->field_count;
- }
-
- Frame(const fidl::FidlCodedTable* coded_table, uint32_t offset)
- : offset(offset) {
- state = kStateStruct;
- table_state.fields = coded_table->fields;
- table_state.field_count = coded_table->field_count;
- }
-
- Frame(const fidl::FidlCodedUnion* coded_union, uint32_t offset)
- : offset(offset) {
- state = kStateUnion;
- union_state.types = coded_union->types;
- union_state.type_count = coded_union->type_count;
- union_state.data_offset = coded_union->data_offset;
- }
-
- Frame(const fidl::FidlCodedXUnion* coded_xunion, uint32_t offset)
- : state(kStateXUnion), offset(offset) {
- // This initialization is done in the ctor body instead of in an
- // initialization list since we need to set fields in unions, which
- // is much more involved in a ctor initialization list.
- xunion_state.fields = coded_xunion->fields;
- xunion_state.field_count = coded_xunion->field_count;
- }
-
- Frame(const fidl_type_t* element, uint32_t array_size, uint32_t element_size,
- uint32_t offset)
- : offset(offset) {
- state = kStateArray;
- array_state.element = element;
- array_state.array_size = array_size;
- array_state.element_size = element_size;
- }
-
- // The default constructor does nothing when initializing the stack of frames.
- Frame() {}
-
- static Frame DoneSentinel() {
- Frame frame;
- frame.state = kStateDone;
- return frame;
- }
-
- uint32_t NextStructField() {
- ZX_DEBUG_ASSERT(state == kStateStruct);
-
- uint32_t current = field;
- field += 1;
- return current;
- }
-
- uint32_t NextArrayOffset() {
- ZX_DEBUG_ASSERT(state == kStateArray);
-
- uint32_t current = field;
- field += array_state.element_size;
- return current;
- }
-
- enum : int {
- kStateStruct,
- kStateStructPointer,
- kStateTable,
- kStateTablePointer,
- kStateUnion,
- kStateUnionPointer,
- kStateArray,
- kStateString,
- kStateHandle,
- kStateVector,
- kStateXUnion,
- kStateXUnionPointer,
-
- kStateDone,
- } state;
- // A byte offset into bytes_;
- uint32_t offset;
-
- // This is a subset of the information recorded in the
- // fidl_type structures needed for decoding state. For
- // example, struct sizes do not need to be present here.
- union {
- struct {
- const fidl::FidlStructField* fields;
- uint32_t field_count;
- } struct_state;
- struct {
- const fidl::FidlCodedStruct* struct_type;
- } struct_pointer_state;
- struct {
- const fidl::FidlTableField* fields;
- uint32_t known_index;
- uint32_t field_count;
- uint32_t present_count;
- uint32_t end_offset;
- uint32_t end_handle;
- } table_state;
- struct {
- const fidl::FidlCodedTable* table_type;
- } table_pointer_state;
- struct {
- const fidl_type_t* const* types;
- uint32_t type_count;
- uint32_t data_offset;
- } union_state;
- struct {
- const fidl::FidlCodedUnion* union_type;
- } union_pointer_state;
- struct {
- const fidl::FidlXUnionField* fields;
- uint32_t field_count;
- } xunion_state;
- struct {
- const fidl::FidlCodedXUnion* xunion_type;
- } xunion_pointer_state;
- struct {
- const fidl_type_t* element;
- uint32_t array_size;
- uint32_t element_size;
- } array_state;
- struct {
- uint32_t max_size;
- bool nullable;
- } string_state;
- struct {
- bool nullable;
- } handle_state;
- struct {
- const fidl_type* element;
- uint32_t max_count;
- uint32_t element_size;
- bool nullable;
- } vector_state;
- };
-
- uint32_t field = 0u;
- };
-
- // Returns true on success and false on recursion overflow.
- bool Push(Frame frame) {
- if (depth_ == FIDL_RECURSION_DEPTH) {
- return false;
- }
- decoding_frames_[depth_] = frame;
- ++depth_;
- return true;
- }
-
- void Pop() {
- ZX_DEBUG_ASSERT(depth_ != 0u);
- --depth_;
- }
-
- Frame* Peek() {
- ZX_DEBUG_ASSERT(depth_ != 0u);
- return &decoding_frames_[depth_ - 1];
- }
-
- // Message state passed in to the constructor.
- const fidl_type_t* const type_;
-
- // Internal state.
- uint32_t handle_idx_ = 0u;
- uint32_t out_of_line_offset_ = 0u;
-
- // Decoding stack state.
- uint32_t depth_ = 0u;
- Frame decoding_frames_[FIDL_RECURSION_DEPTH];
-};
-
-} // namespace internal
-} // namespace fidl
-
-#endif // ZIRCON_SYSTEM_ULIB_FIDL_BUFFER_WALKER_H_
diff --git a/system/ulib/fidl/decoding.cpp b/system/ulib/fidl/decoding.cpp
index 732e779..656280e 100644
--- a/system/ulib/fidl/decoding.cpp
+++ b/system/ulib/fidl/decoding.cpp
@@ -16,6 +16,7 @@
#include <zircon/syscalls.h>
#endif
+#include "envelope_frames.h"
#include "visitor.h"
#include "walker.h"
@@ -52,6 +53,8 @@
constexpr uintptr_t kAllocPresenceMarker = FIDL_ALLOC_PRESENT;
constexpr uintptr_t kAllocAbsenceMarker = FIDL_ALLOC_ABSENT;
+using EnvelopeState = ::fidl::EnvelopeFrames::EnvelopeState;
+
class FidlDecoder final : public fidl::Visitor<
fidl::MutatingVisitorTrait, StartingPoint, Position> {
public:
@@ -75,19 +78,11 @@
SetError("decoder encountered invalid pointer");
return Status::kConstraintViolationError;
}
- // We have to manually maintain alignment here. For example, a pointer
- // to a struct that is 4 bytes still needs to advance the next
- // out-of-line offset by 8 to maintain the aligned-to-FIDL_ALIGNMENT
- // property.
- static constexpr uint32_t mask = FIDL_ALIGNMENT - 1;
- uint32_t new_offset = next_out_of_line_;
- if (add_overflow(new_offset, inline_size, &new_offset)
- || add_overflow(new_offset, mask, &new_offset)) {
+ uint32_t new_offset;
+ if (!fidl::AddOutOfLine(next_out_of_line_, inline_size, &new_offset)) {
SetError("overflow updating out-of-line offset");
return Status::kMemoryError;
}
- new_offset &= ~mask;
-
if (new_offset > num_bytes_) {
SetError("message tried to decode more than provided number of bytes");
return Status::kMemoryError;
@@ -143,7 +138,7 @@
}
// Remember the current watermark of bytes and handles, so that after processing
// the envelope, we can validate that the claimed num_bytes/num_handles matches the reality.
- if (!Push(next_out_of_line_, handle_idx_)) {
+ if (!envelope_frames_.Push(EnvelopeState(next_out_of_line_, handle_idx_))) {
SetError("Overly deep nested envelopes");
return Status::kConstraintViolationError;
}
@@ -161,7 +156,7 @@
Status LeaveEnvelope(Position envelope_position, EnvelopePointer envelope) {
// Now that the envelope has been consumed, check the correctness of the envelope header.
- auto& starting_state = Pop();
+ auto& starting_state = envelope_frames_.Pop();
uint32_t num_bytes = next_out_of_line_ - starting_state.bytes_so_far;
uint32_t num_handles = handle_idx_ - starting_state.handles_so_far;
if (envelope->num_bytes != num_bytes) {
@@ -197,29 +192,6 @@
*out_error_msg_ = error;
}
- struct EnvelopeState {
- uint32_t bytes_so_far;
- uint32_t handles_so_far;
- };
-
- const EnvelopeState& Pop() {
- ZX_ASSERT(envelope_depth_ != 0);
- envelope_depth_--;
- return envelope_states_[envelope_depth_];
- }
-
- bool Push(uint32_t num_bytes, uint32_t num_handles) {
- if (envelope_depth_ == FIDL_RECURSION_DEPTH) {
- return false;
- }
- envelope_states_[envelope_depth_] = (EnvelopeState) {
- .bytes_so_far = num_bytes,
- .handles_so_far = num_handles,
- };
- envelope_depth_++;
- return true;
- }
-
// Message state passed in to the constructor.
uint8_t* const bytes_;
const uint32_t num_bytes_;
@@ -231,8 +203,7 @@
// Decoder state
zx_status_t status_ = ZX_OK;
uint32_t handle_idx_ = 0;
- uint32_t envelope_depth_ = 0;
- EnvelopeState envelope_states_[FIDL_RECURSION_DEPTH];
+ fidl::EnvelopeFrames envelope_frames_;
};
} // namespace
@@ -251,28 +222,17 @@
set_error("Cannot provide non-zero handle count and null handle pointer");
return ZX_ERR_INVALID_ARGS;
}
- if (type == nullptr) {
- set_error("Cannot decode a null fidl type");
- return ZX_ERR_INVALID_ARGS;
- }
- size_t primary_size;
+ uint32_t next_out_of_line;
zx_status_t status;
- if ((status = fidl::GetPrimaryObjectSize(type, &primary_size, out_error_msg)) != ZX_OK) {
+ if ((status = fidl::StartingOutOfLineOffset(type,
+ num_bytes,
+ &next_out_of_line,
+ out_error_msg)) != ZX_OK) {
return status;
}
- if (primary_size > num_bytes) {
- set_error("Buffer is too small for first inline object");
- return ZX_ERR_INVALID_ARGS;
- }
- uint64_t next_out_of_line = fidl::FidlAlign(static_cast<uint32_t>(primary_size));
- if (next_out_of_line > std::numeric_limits<uint32_t>::max()) {
- set_error("Out of line starting offset overflows");
- return ZX_ERR_INVALID_ARGS;
- }
- FidlDecoder decoder(bytes, num_bytes, handles, num_handles,
- static_cast<uint32_t>(next_out_of_line), out_error_msg);
+ FidlDecoder decoder(bytes, num_bytes, handles, num_handles, next_out_of_line, out_error_msg);
fidl::Walk(decoder,
type,
StartingPoint { reinterpret_cast<uint8_t*>(bytes) });
diff --git a/system/ulib/fidl/encoding.cpp b/system/ulib/fidl/encoding.cpp
index f94a65c..81a5ee2 100644
--- a/system/ulib/fidl/encoding.cpp
+++ b/system/ulib/fidl/encoding.cpp
@@ -17,6 +17,7 @@
#include <zircon/syscalls.h>
#endif
+#include "envelope_frames.h"
#include "visitor.h"
#include "walker.h"
@@ -50,6 +51,8 @@
return Position { 0 };
}
+using EnvelopeState = ::fidl::EnvelopeFrames::EnvelopeState;
+
class FidlEncoder final : public fidl::Visitor<
fidl::MutatingVisitorTrait, StartingPoint, Position> {
public:
@@ -93,7 +96,7 @@
}
handles_[handle_idx_] = *handle;
*handle = FIDL_HANDLE_PRESENT;
- handle_idx_ += 1;
+ handle_idx_++;
return Status::kSuccess;
}
@@ -101,11 +104,10 @@
EnvelopePointer envelope,
const fidl_type_t* payload_type) {
// Validate envelope data/bytes invariants
- if (envelope->data == nullptr) {
- if (!(envelope->num_bytes == 0 && envelope->num_handles == 0)) {
- SetError("Envelope has absent data pointer, yet has data and/or handles");
- return Status::kConstraintViolationError;
- }
+ if (envelope->data == nullptr &&
+ (envelope->num_bytes != 0 || envelope->num_handles != 0)) {
+ SetError("Envelope has absent data pointer, yet has data and/or handles");
+ return Status::kConstraintViolationError;
}
if (envelope->data != nullptr && envelope->num_bytes == 0) {
SetError("Envelope has present data pointer, but zero byte count");
@@ -119,7 +121,7 @@
}
// Remember the current watermark of bytes and handles, so that after processing
// the envelope, we can validate that the claimed num_bytes/num_handles matches the reality.
- if (!Push(next_out_of_line_, handle_idx_)) {
+ if (!envelope_frames_.Push(EnvelopeState(next_out_of_line_, handle_idx_))) {
SetError("Overly deep nested envelopes");
return Status::kConstraintViolationError;
}
@@ -128,7 +130,7 @@
Status LeaveEnvelope(Position envelope_position, EnvelopePointer envelope) {
// Now that the envelope has been consumed, check the correctness of the envelope header.
- auto& starting_state = Pop();
+ auto& starting_state = envelope_frames_.Pop();
uint32_t num_bytes = next_out_of_line_ - starting_state.bytes_so_far;
uint32_t num_handles = handle_idx_ - starting_state.handles_so_far;
if (envelope->num_bytes != num_bytes) {
@@ -174,19 +176,11 @@
SetError("noncontiguous out of line storage during encode");
return false;
}
- // We have to manually maintain alignment here. For example, a pointer
- // to a struct that is 4 bytes still needs to advance the next
- // out-of-line offset by 8 to maintain the aligned-to-FIDL_ALIGNMENT
- // property.
- static constexpr uint32_t mask = FIDL_ALIGNMENT - 1;
- uint32_t new_offset = next_out_of_line_;
- if (add_overflow(new_offset, size, &new_offset)
- || add_overflow(new_offset, mask, &new_offset)) {
+ uint32_t new_offset;
+ if (!fidl::AddOutOfLine(next_out_of_line_, size, &new_offset)) {
SetError("overflow updating out-of-line offset");
return false;
}
- new_offset &= ~mask;
-
if (new_offset > num_bytes_) {
SetError("message tried to encode more than provided number of bytes");
return false;
@@ -196,29 +190,6 @@
return true;
}
- struct EnvelopeState {
- uint32_t bytes_so_far;
- uint32_t handles_so_far;
- };
-
- const EnvelopeState& Pop() {
- ZX_ASSERT(envelope_depth_ != 0);
- envelope_depth_ -= 1;
- return envelope_states_[envelope_depth_];
- }
-
- bool Push(uint32_t num_bytes, uint32_t num_handles) {
- if (envelope_depth_ == FIDL_RECURSION_DEPTH) {
- return false;
- }
- envelope_states_[envelope_depth_] = (EnvelopeState) {
- .bytes_so_far = num_bytes,
- .handles_so_far = num_handles,
- };
- envelope_depth_ += 1;
- return true;
- }
-
// Message state passed in to the constructor.
uint8_t* const bytes_;
const uint32_t num_bytes_;
@@ -230,8 +201,7 @@
// Encoder state
zx_status_t status_ = ZX_OK;
uint32_t handle_idx_ = 0;
- uint32_t envelope_depth_ = 0;
- EnvelopeState envelope_states_[FIDL_RECURSION_DEPTH];
+ fidl::EnvelopeFrames envelope_frames_;
};
} // namespace
@@ -254,28 +224,17 @@
set_error("Cannot encode with null out_actual_handles");
return ZX_ERR_INVALID_ARGS;
}
- if (type == nullptr) {
- set_error("Cannot encode a null fidl type");
- return ZX_ERR_INVALID_ARGS;
- }
- size_t primary_size;
+ uint32_t next_out_of_line;
zx_status_t status;
- if ((status = fidl::GetPrimaryObjectSize(type, &primary_size, out_error_msg)) != ZX_OK) {
+ if ((status = fidl::StartingOutOfLineOffset(type,
+ num_bytes,
+ &next_out_of_line,
+ out_error_msg)) != ZX_OK) {
return status;
}
- if (primary_size > num_bytes) {
- set_error("Buffer is too small for first inline object");
- return ZX_ERR_INVALID_ARGS;
- }
- uint64_t next_out_of_line = fidl::FidlAlign(static_cast<uint32_t>(primary_size));
- if (next_out_of_line > std::numeric_limits<uint32_t>::max()) {
- set_error("Out of line starting offset overflows");
- return ZX_ERR_INVALID_ARGS;
- }
- FidlEncoder encoder(bytes, num_bytes, handles, max_handles,
- static_cast<uint32_t>(next_out_of_line), out_error_msg);
+ FidlEncoder encoder(bytes, num_bytes, handles, max_handles, next_out_of_line, out_error_msg);
fidl::Walk(encoder,
type,
StartingPoint { reinterpret_cast<uint8_t*>(bytes) });
diff --git a/system/ulib/fidl/envelope_frames.h b/system/ulib/fidl/envelope_frames.h
new file mode 100644
index 0000000..5dbb27d
--- /dev/null
+++ b/system/ulib/fidl/envelope_frames.h
@@ -0,0 +1,53 @@
+// Copyright 2019 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.
+
+#pragma once
+
+#include <stdalign.h>
+#include <cstdint>
+#include <cstdlib>
+
+#include <lib/fidl/coding.h>
+#include <zircon/assert.h>
+#include <zircon/compiler.h>
+
+namespace fidl {
+
+class EnvelopeFrames {
+public:
+ struct EnvelopeState {
+ uint32_t bytes_so_far;
+ uint32_t handles_so_far;
+
+ EnvelopeState(uint32_t bytes_so_far, uint32_t handles_so_far)
+ : bytes_so_far(bytes_so_far), handles_so_far(handles_so_far) {}
+
+ private:
+ // Default constructor used by |EnvelopeFrames| to avoid unnecessarily zeroing
+ // the |envelope_states_| array.
+ EnvelopeState() = default;
+ friend class EnvelopeFrames;
+ };
+
+ const EnvelopeState& Pop() {
+ ZX_ASSERT(envelope_depth_ != 0);
+ envelope_depth_--;
+ return envelope_states_[envelope_depth_];
+ }
+
+ bool Push(const EnvelopeState& state) {
+ if (envelope_depth_ == FIDL_RECURSION_DEPTH) {
+ return false;
+ }
+ envelope_states_[envelope_depth_] = state;
+ envelope_depth_++;
+ return true;
+ }
+
+private:
+ uint32_t envelope_depth_ = 0;
+ EnvelopeState envelope_states_[FIDL_RECURSION_DEPTH];
+};
+
+} // namespace fidl
diff --git a/system/ulib/fidl/include/lib/fidl/coding.h b/system/ulib/fidl/include/lib/fidl/coding.h
index f3add07..3768597 100644
--- a/system/ulib/fidl/include/lib/fidl/coding.h
+++ b/system/ulib/fidl/include/lib/fidl/coding.h
@@ -38,7 +38,7 @@
zx_status_t fidl_validate_msg(const fidl_type_t* type, const fidl_msg_t* msg,
const char** out_error_msg);
-// Traverses a FIDL message starting at |value|, closing all handles within it.
+// Traverses a decoded FIDL message starting at |value|, closing all handles within it.
// If the message is non-contiguous in memory, the function will follow pointers and close handles
// in any scattered out-of-line objects.
//
diff --git a/system/ulib/fidl/include/lib/fidl/internal.h b/system/ulib/fidl/include/lib/fidl/internal.h
index b814fd9..036757c 100644
--- a/system/ulib/fidl/include/lib/fidl/internal.h
+++ b/system/ulib/fidl/include/lib/fidl/internal.h
@@ -30,11 +30,27 @@
kNullable = 1u,
};
-inline uint64_t FidlAlign(uint32_t offset) {
+constexpr inline uint64_t FidlAlign(uint32_t offset) {
constexpr uint64_t alignment_mask = FIDL_ALIGNMENT - 1;
return (offset + alignment_mask) & ~alignment_mask;
}
+// Add |size| to out-of-line |offset|, maintaining alignment. For example, a pointer to a struct
+// that is 4 bytes still needs to advance the next out-of-line offset by 8 to maintain
+// the aligned-to-FIDL_ALIGNMENT property.
+// Returns false on overflow. Otherwise, resulting offset is stored in |out_offset|.
+inline bool AddOutOfLine(uint32_t offset, uint32_t size, uint32_t* out_offset) {
+ constexpr uint32_t kMask = FIDL_ALIGNMENT - 1;
+ uint32_t new_offset = offset;
+ if (add_overflow(new_offset, size, &new_offset)
+ || add_overflow(new_offset, kMask, &new_offset)) {
+ return false;
+ }
+ new_offset &= ~kMask;
+ *out_offset = new_offset;
+ return true;
+}
+
struct FidlStructField {
const fidl_type* type;
uint32_t offset;
diff --git a/system/ulib/fidl/linearizing.cpp b/system/ulib/fidl/linearizing.cpp
index 9e1f087..3713745 100644
--- a/system/ulib/fidl/linearizing.cpp
+++ b/system/ulib/fidl/linearizing.cpp
@@ -14,6 +14,7 @@
#include <zircon/assert.h>
#include <zircon/compiler.h>
+#include "envelope_frames.h"
#include "visitor.h"
#include "walker.h"
@@ -63,6 +64,8 @@
};
}
+using EnvelopeState = ::fidl::EnvelopeFrames::EnvelopeState;
+
class FidlLinearizer final : public fidl::Visitor<
fidl::MutatingVisitorTrait, StartingPoint, Position> {
public:
@@ -83,18 +86,11 @@
ObjectPointerPointer object_ptr_ptr,
uint32_t inline_size,
Position* out_position) {
- // We have to manually maintain alignment here. For example, a pointer
- // to a struct that is 4 bytes still needs to advance the next
- // out-of-line offset by 8 to maintain the aligned-to-FIDL_ALIGNMENT
- // property.
- static constexpr uint32_t mask = FIDL_ALIGNMENT - 1;
- uint32_t new_offset = next_out_of_line_;
- if (add_overflow(new_offset, inline_size, &new_offset) ||
- add_overflow(new_offset, mask, &new_offset)) {
+ uint32_t new_offset;
+ if (!fidl::AddOutOfLine(next_out_of_line_, inline_size, &new_offset)) {
SetError("out-of-line offset overflow trying to linearize");
return Status::kMemoryError;
}
- new_offset &= ~mask;
if (new_offset > num_bytes_) {
SetError("object is too big to linearize into provided buffer",
@@ -144,7 +140,7 @@
}
// Remember the current watermark of bytes and handles, so that after processing
// the envelope, we can validate that the claimed num_bytes/num_handles matches the reality.
- if (!Push(next_out_of_line_, handle_idx_)) {
+ if (!envelope_frames_.Push(EnvelopeState(next_out_of_line_, handle_idx_))) {
SetError("Overly deep nested envelopes");
return Status::kConstraintViolationError;
}
@@ -154,7 +150,7 @@
Status LeaveEnvelope(Position envelope_position, EnvelopePointer envelope) {
// Now that the envelope has been consumed, go back and update the envelope header with
// the correct num_bytes and num_handles values
- auto& starting_state = Pop();
+ auto& starting_state = envelope_frames_.Pop();
uint32_t num_bytes = next_out_of_line_ - starting_state.bytes_so_far;
uint32_t num_handles = handle_idx_ - starting_state.handles_so_far;
envelope->num_bytes = num_bytes;
@@ -187,29 +183,6 @@
}
}
- struct EnvelopeState {
- uint32_t bytes_so_far;
- uint32_t handles_so_far;
- };
-
- const EnvelopeState& Pop() {
- ZX_ASSERT(envelope_depth_ != 0);
- envelope_depth_ -= 1;
- return envelope_states_[envelope_depth_];
- }
-
- bool Push(uint32_t num_bytes, uint32_t num_handles) {
- if (envelope_depth_ == FIDL_RECURSION_DEPTH) {
- return false;
- }
- envelope_states_[envelope_depth_] = (EnvelopeState) {
- .bytes_so_far = num_bytes,
- .handles_so_far = num_handles,
- };
- envelope_depth_ += 1;
- return true;
- }
-
// Message state passed into the constructor.
uint8_t* const bytes_;
const uint32_t num_bytes_;
@@ -220,8 +193,7 @@
zx_status_t status_ = ZX_OK;
uint32_t handle_idx_ = 0;
zx_handle_t* original_handles_[ZX_CHANNEL_MAX_MSG_HANDLES];
- uint32_t envelope_depth_ = 0;
- EnvelopeState envelope_states_[FIDL_RECURSION_DEPTH];
+ fidl::EnvelopeFrames envelope_frames_;
};
} // namespace
@@ -240,14 +212,10 @@
set_error("Cannot linearize with null destination buffer");
return ZX_ERR_INVALID_ARGS;
}
- if (type == nullptr) {
- set_error("Cannot linearize a null fidl type");
- return ZX_ERR_INVALID_ARGS;
- }
size_t primary_size;
zx_status_t status;
- if ((status = fidl::GetPrimaryObjectSize(type, &primary_size, out_error_msg)) != ZX_OK) {
+ if ((status = fidl::PrimaryObjectSize(type, &primary_size, out_error_msg)) != ZX_OK) {
return status;
}
if (primary_size > num_bytes) {
diff --git a/system/ulib/fidl/validating.cpp b/system/ulib/fidl/validating.cpp
index 342face..e3ca4a8 100644
--- a/system/ulib/fidl/validating.cpp
+++ b/system/ulib/fidl/validating.cpp
@@ -12,74 +12,211 @@
#include <zircon/assert.h>
#include <zircon/compiler.h>
-#include "buffer_walker.h"
+#include "envelope_frames.h"
+#include "visitor.h"
+#include "walker.h"
// TODO(kulakowski) Design zx_status_t error values.
namespace {
-class FidlValidator final : public fidl::internal::BufferWalker<FidlValidator, false, false> {
- typedef fidl::internal::BufferWalker<FidlValidator, false, false> Super;
+struct Position;
+struct StartingPoint {
+ const uint8_t* const addr;
+ Position ToPosition() const;
+};
+
+struct Position {
+ uint32_t offset;
+ Position operator+(uint32_t size) const {
+ return Position { offset + size };
+ }
+ Position& operator+=(uint32_t size) {
+ offset += size;
+ return *this;
+ }
+ template <typename T>
+ constexpr const T* Get(StartingPoint start) const {
+ return reinterpret_cast<const T*>(start.addr + offset);
+ }
+};
+
+Position StartingPoint::ToPosition() const {
+ return Position { 0 };
+}
+
+constexpr uintptr_t kAllocPresenceMarker = FIDL_ALLOC_PRESENT;
+constexpr uintptr_t kAllocAbsenceMarker = FIDL_ALLOC_ABSENT;
+
+using EnvelopeState = ::fidl::EnvelopeFrames::EnvelopeState;
+
+class FidlValidator final : public fidl::Visitor<
+ fidl::NonMutatingVisitorTrait, StartingPoint, Position> {
public:
- FidlValidator(const fidl_type_t* type, const void* bytes, uint32_t num_bytes,
- uint32_t num_handles, const char** out_error_msg)
- : Super(type), bytes_(static_cast<const uint8_t*>(bytes)), num_bytes_(num_bytes),
- num_handles_(num_handles), out_error_msg_(out_error_msg) {}
+ FidlValidator(uint32_t num_bytes, uint32_t num_handles, uint32_t next_out_of_line,
+ const char** out_error_msg)
+ : num_bytes_(num_bytes), num_handles_(num_handles), next_out_of_line_(next_out_of_line),
+ out_error_msg_(out_error_msg) {}
- void Walk() {
- Super::Walk();
- if (status_ == ZX_OK && handle_idx() != num_handles()) {
- SetError("message did not contain the specified number of handles");
- return;
+ using StartingPoint = StartingPoint;
+
+ using Position = Position;
+
+ static constexpr bool kContinueAfterConstraintViolation = true;
+
+ Status VisitPointer(Position ptr_position,
+ ObjectPointerPointer object_ptr_ptr,
+ uint32_t inline_size,
+ Position* out_position) {
+ if (reinterpret_cast<uintptr_t>(*object_ptr_ptr) != kAllocPresenceMarker) {
+ SetError("validator encountered invalid pointer");
+ return Status::kConstraintViolationError;
}
- }
-
- const uint8_t* bytes() const { return bytes_; }
- uint32_t num_bytes() const { return num_bytes_; }
- uint32_t num_handles() const { return num_handles_; }
-
- bool ValidateOutOfLineStorageClaim(const void* a, const void* b) {
- return true;
- }
-
- void UnclaimedHandle(const zx_handle_t* out_handle) {}
- void ClaimedHandle(const zx_handle_t* out_handle, uint32_t idx) {}
-
- template <class T>
- void UpdatePointer(const T* const* p, const T* v) {}
-
- PointerState GetPointerState(const void* ptr) const {
- return static_cast<PointerState>(*static_cast<const uintptr_t*>(ptr));
- }
- HandleState GetHandleState(zx_handle_t p) const {
- return static_cast<HandleState>(p);
- }
-
- void SetError(const char* error_msg) {
- status_ = ZX_ERR_INVALID_ARGS;
- if (out_error_msg_ != nullptr) {
- *out_error_msg_ = error_msg;
+ uint32_t new_offset;
+ if (!fidl::AddOutOfLine(next_out_of_line_, inline_size, &new_offset)) {
+ SetError("overflow updating out-of-line offset");
+ return Status::kMemoryError;
}
+ if (new_offset > num_bytes_) {
+ SetError("message tried to access more than provided number of bytes");
+ return Status::kMemoryError;
+ }
+ *out_position = Position { next_out_of_line_ };
+ next_out_of_line_ = new_offset;
+ return Status::kSuccess;
+ }
+
+ Status VisitHandle(Position handle_position, HandlePointer handle) {
+ if (*handle != FIDL_HANDLE_PRESENT) {
+ SetError("message contains a garbage handle");
+ return Status::kConstraintViolationError;
+ }
+ if (handle_idx_ == num_handles_) {
+ SetError("message has too many handles");
+ return Status::kConstraintViolationError;
+ }
+ handle_idx_++;
+ return Status::kSuccess;
+ }
+
+ Status EnterEnvelope(Position envelope_position,
+ EnvelopePointer envelope,
+ const fidl_type_t* payload_type) {
+ if (envelope->presence == kAllocAbsenceMarker &&
+ (envelope->num_bytes != 0 || envelope->num_handles != 0)) {
+ SetError("Envelope has absent data pointer, yet has data and/or handles");
+ return Status::kConstraintViolationError;
+ }
+ if (envelope->presence != kAllocAbsenceMarker && envelope->num_bytes == 0) {
+ SetError("Envelope has present data pointer, but zero byte count");
+ return Status::kConstraintViolationError;
+ }
+ uint32_t expected_handle_count;
+ if (add_overflow(handle_idx_, envelope->num_handles, &expected_handle_count) ||
+ expected_handle_count > num_handles_) {
+ SetError("Envelope has more handles than expected");
+ return Status::kConstraintViolationError;
+ }
+ // Remember the current watermark of bytes and handles, so that after processing
+ // the envelope, we can validate that the claimed num_bytes/num_handles matches the reality.
+ if (!envelope_frames_.Push(EnvelopeState(next_out_of_line_, handle_idx_))) {
+ SetError("Overly deep nested envelopes");
+ return Status::kConstraintViolationError;
+ }
+ // If we do not have the coding table for this payload,
+ // treat it as unknown and add its contained handles
+ if (envelope->presence != kAllocAbsenceMarker && payload_type == nullptr) {
+ handle_idx_ += envelope->num_handles;
+ }
+ return Status::kSuccess;
+ }
+
+ Status LeaveEnvelope(Position envelope_position, EnvelopePointer envelope) {
+ // Now that the envelope has been consumed, check the correctness of the envelope header.
+ auto& starting_state = envelope_frames_.Pop();
+ uint32_t num_bytes = next_out_of_line_ - starting_state.bytes_so_far;
+ uint32_t num_handles = handle_idx_ - starting_state.handles_so_far;
+ if (envelope->num_bytes != num_bytes) {
+ SetError("Envelope num_bytes was mis-sized");
+ return Status::kConstraintViolationError;
+ }
+ if (envelope->num_handles != num_handles) {
+ SetError("Envelope num_handles was mis-sized");
+ return Status::kConstraintViolationError;
+ }
+ return Status::kSuccess;
+ }
+
+ void OnError(const char* error) {
+ SetError(error);
}
zx_status_t status() const { return status_; }
+ bool DidConsumeAllBytes() const { return next_out_of_line_ == num_bytes_; }
+
+ bool DidConsumeAllHandles() const { return handle_idx_ == num_handles_; }
+
private:
+ void SetError(const char* error) {
+ if (status_ == ZX_OK) {
+ status_ = ZX_ERR_INVALID_ARGS;
+ if (out_error_msg_ != nullptr) {
+ *out_error_msg_ = error;
+ }
+ }
+ }
+
// Message state passed in to the constructor.
- const uint8_t* const bytes_;
const uint32_t num_bytes_;
const uint32_t num_handles_;
+ uint32_t next_out_of_line_;
const char** const out_error_msg_;
+
+ // Validator state
zx_status_t status_ = ZX_OK;
+ uint32_t handle_idx_ = 0;
+ fidl::EnvelopeFrames envelope_frames_;
};
} // namespace
zx_status_t fidl_validate(const fidl_type_t* type, const void* bytes, uint32_t num_bytes,
uint32_t num_handles, const char** out_error_msg) {
- FidlValidator validator(type, bytes, num_bytes, num_handles, out_error_msg);
- validator.Walk();
+ auto set_error = [&out_error_msg] (const char* msg) {
+ if (out_error_msg) *out_error_msg = msg;
+ };
+ if (bytes == nullptr) {
+ set_error("Cannot validate null bytes");
+ return ZX_ERR_INVALID_ARGS;
+ }
+
+ uint32_t next_out_of_line;
+ zx_status_t status;
+ if ((status = fidl::StartingOutOfLineOffset(type,
+ num_bytes,
+ &next_out_of_line,
+ out_error_msg)) != ZX_OK) {
+ return status;
+ }
+
+ FidlValidator validator(num_bytes, num_handles, next_out_of_line, out_error_msg);
+ fidl::Walk(validator,
+ type,
+ StartingPoint { reinterpret_cast<const uint8_t*>(bytes) });
+
+ if (validator.status() == ZX_OK) {
+ if (!validator.DidConsumeAllBytes()) {
+ set_error("message did not consume all provided bytes");
+ return ZX_ERR_INVALID_ARGS;
+ }
+ if (!validator.DidConsumeAllHandles()) {
+ set_error("message did not reference all provided handles");
+ return ZX_ERR_INVALID_ARGS;
+ }
+ }
+
return validator.status();
}
diff --git a/system/ulib/fidl/visitor.h b/system/ulib/fidl/visitor.h
index ea3f46a..fea0cf6 100644
--- a/system/ulib/fidl/visitor.h
+++ b/system/ulib/fidl/visitor.h
@@ -23,7 +23,7 @@
static constexpr bool kIsConst = true;
// Message is const
- using ObjectPointerPointer = const void** const;
+ using ObjectPointerPointer = const void* const* const;
};
struct MutatingVisitorTrait {
@@ -31,7 +31,7 @@
static constexpr bool kIsConst = false;
// Message is mutable
- using ObjectPointerPointer = void**;
+ using ObjectPointerPointer = void** const;
};
namespace {
@@ -59,7 +59,8 @@
using MutationTrait = MutationTrait_;
template <typename T>
- using Ptr = typename std::conditional<MutationTrait::kIsConst, std::add_const<T*>, T*>::type;
+ using Ptr = typename std::conditional<MutationTrait::kIsConst,
+ typename std::add_const<T>::type, T>::type*;
// A type encapsulating the starting point of message traversal.
//
diff --git a/system/ulib/fidl/walker.cpp b/system/ulib/fidl/walker.cpp
index f06e491..5039ef0 100644
--- a/system/ulib/fidl/walker.cpp
+++ b/system/ulib/fidl/walker.cpp
@@ -2,13 +2,23 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <cstdint>
+#include <limits>
+
#include "walker.h"
namespace fidl {
-zx_status_t GetPrimaryObjectSize(const fidl_type_t* type,
- size_t* out_size,
- const char** out_error) {
+zx_status_t PrimaryObjectSize(const fidl_type_t* type,
+ size_t* out_size,
+ const char** out_error) {
+ auto set_error = [&out_error] (const char* msg) {
+ if (out_error) *out_error = msg;
+ };
+ if (type == nullptr) {
+ set_error("fidl type cannot be null");
+ return ZX_ERR_INVALID_ARGS;
+ }
switch (type->type_tag) {
case fidl::kFidlTypeStruct:
*out_size = type->coded_struct.size;
@@ -17,11 +27,34 @@
*out_size = sizeof(fidl_vector_t);
return ZX_OK;
default:
- if (out_error) {
- *out_error = "Message must be a struct or a table";
- }
+ set_error("Message must be a struct or a table");
return ZX_ERR_INVALID_ARGS;
}
}
+zx_status_t StartingOutOfLineOffset(const fidl_type_t* type,
+ uint32_t buffer_size,
+ uint32_t* out_first_out_of_line,
+ const char** out_error) {
+ auto set_error = [&out_error] (const char* msg) {
+ if (out_error) *out_error = msg;
+ };
+ size_t primary_size;
+ zx_status_t status;
+ if ((status = PrimaryObjectSize(type, &primary_size, out_error)) != ZX_OK) {
+ return status;
+ }
+ if (primary_size > buffer_size) {
+ set_error("Buffer is too small for first inline object");
+ return ZX_ERR_INVALID_ARGS;
+ }
+ uint64_t first_out_of_line = fidl::FidlAlign(static_cast<uint32_t>(primary_size));
+ if (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;
+}
+
}
diff --git a/system/ulib/fidl/walker.h b/system/ulib/fidl/walker.h
index 1b1cec0..bc1cab2 100644
--- a/system/ulib/fidl/walker.h
+++ b/system/ulib/fidl/walker.h
@@ -64,7 +64,7 @@
__builtin_unreachable();
}
-// The Walker class traverses through a FIDL message by following its encoding table and
+// 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>
@@ -230,7 +230,7 @@
ZX_DEBUG_ASSERT(state == kStateStruct);
uint32_t current = struct_state.field;
- struct_state.field += 1;
+ struct_state.field++;
return current;
}
@@ -263,7 +263,7 @@
Position position;
// This is a subset of the information recorded in the
- // fidl_type structures needed for decoding state. For
+ // fidl_type structures needed for coding state. For
// example, struct sizes do not need to be present here.
union {
struct {
@@ -348,7 +348,7 @@
if (depth_ == FIDL_RECURSION_DEPTH) {
return false;
}
- decoding_frames_[depth_] = frame;
+ coding_frames_[depth_] = frame;
++depth_;
return true;
}
@@ -360,7 +360,7 @@
Frame* Peek() {
ZX_DEBUG_ASSERT(depth_ != 0u);
- return &decoding_frames_[depth_ - 1];
+ return &coding_frames_[depth_ - 1];
}
const fidl_type_t* const type_;
@@ -368,7 +368,7 @@
// Decoding stack state.
uint32_t depth_ = 0u;
- Frame decoding_frames_[FIDL_RECURSION_DEPTH];
+ Frame coding_frames_[FIDL_RECURSION_DEPTH];
};
template <typename VisitorImpl>
@@ -416,12 +416,12 @@
continue;
}
case Frame::kStateStructPointer: {
- if (*PtrTo<void*>(frame->position) == nullptr) {
+ if (*PtrTo<Ptr<void>>(frame->position) == nullptr) {
Pop();
continue;
}
auto status = visitor.VisitPointer(frame->position,
- PtrTo<void*>(frame->position),
+ PtrTo<Ptr<void>>(frame->position),
frame->struct_pointer_state.struct_type->size,
&frame->position);
FIDL_STATUS_GUARD(status);
@@ -478,14 +478,14 @@
const fidl::FidlTableField* field = table_frame.field;
if (field->ordinal == table_frame.ordinal) {
known_field = field;
- table_frame.field += 1;
- table_frame.remaining_fields -= 1;
+ table_frame.field++;
+ table_frame.remaining_fields--;
}
}
Position envelope_pos = envelope_position(table_frame.ordinal);
auto envelope_ptr = PtrTo<fidl_envelope_t>(envelope_pos);
// Process the next ordinal in the following state machine iteration
- table_frame.ordinal += 1;
+ table_frame.ordinal++;
// Make sure we don't process a malformed envelope
const fidl_type_t* payload_type = known_field ? known_field->type : nullptr;
auto status = visitor.EnterEnvelope(envelope_pos, envelope_ptr, payload_type);
@@ -523,12 +523,12 @@
continue;
}
case Frame::kStateTablePointer: {
- if (*PtrTo<fidl_vector_t*>(frame->position) == nullptr) {
+ if (*PtrTo<Ptr<fidl_vector_t>>(frame->position) == nullptr) {
Pop();
continue;
}
auto status = visitor.VisitPointer(frame->position,
- PtrTo<void*>(frame->position),
+ PtrTo<Ptr<void>>(frame->position),
static_cast<uint32_t>(sizeof(fidl_vector_t)),
&frame->position);
FIDL_STATUS_GUARD(status);
@@ -552,12 +552,12 @@
continue;
}
case Frame::kStateUnionPointer: {
- if (*PtrTo<fidl_union_tag_t*>(frame->position) == nullptr) {
+ if (*PtrTo<Ptr<fidl_union_tag_t>>(frame->position) == nullptr) {
Pop();
continue;
}
auto status = visitor.VisitPointer(frame->position,
- PtrTo<void*>(frame->position),
+ PtrTo<Ptr<void>>(frame->position),
frame->union_pointer_state.union_type->size,
&frame->position);
FIDL_STATUS_GUARD(status);
@@ -625,12 +625,12 @@
continue;
}
case Frame::kStateXUnionPointer: {
- if (*PtrTo<fidl_xunion_t*>(frame->position) == nullptr) {
+ if (*PtrTo<Ptr<fidl_xunion_t>>(frame->position) == nullptr) {
Pop();
continue;
}
auto status = visitor.VisitPointer(frame->position,
- PtrTo<void*>(frame->position),
+ PtrTo<Ptr<void>>(frame->position),
static_cast<uint32_t>(sizeof(fidl_xunion_t)),
&frame->position);
FIDL_STATUS_GUARD(status);
@@ -678,8 +678,9 @@
}
Position position;
auto status = visitor.VisitPointer(position,
- &const_cast<Ptr<void>&>(
- reinterpret_cast<void*&>(string_ptr->data)),
+ &reinterpret_cast<Ptr<void>&>(
+ const_cast<Ptr<char>&>(
+ string_ptr->data)),
static_cast<uint32_t>(size),
&position);
FIDL_STATUS_GUARD(status);
@@ -730,7 +731,7 @@
&frame->position);
FIDL_STATUS_GUARD(status);
if (frame->vector_state.element) {
- // Continue by decoding the vector elements as an array.
+ // Continue by visiting the vector elements as an array.
*frame = Frame(frame->vector_state.element, size,
frame->vector_state.element_size, frame->position);
} else {
@@ -763,13 +764,27 @@
walker.Walk(visitor);
}
-// Given a FIDL coding table, first ensure that the primary object is of one of the expected types,
-// then return the size of the primary object.
+// 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.
//
-// Currently the primary object must be either a struct or a table. An error is returned if this is
-// not the case.
-zx_status_t GetPrimaryObjectSize(const fidl_type_t* type,
- size_t* out_size,
- const char** out_error);
+// An error is returned if:
+// - |type| is null
+// - The primary object is neither a struct nor a table.
+zx_status_t PrimaryObjectSize(const fidl_type_t* type,
+ size_t* out_size,
+ const char** out_error);
+
+// Calculate the offset of the first out-of-line object, from the coding table in |type|.
+// Ensures that the primary object is of one of the expected types, and the offset falls within the
+// |buffer_size| constraints.
+//
+// An error is returned if:
+// - |type| is null
+// - The primary object is neither a struct nor a table.
+// - The offset overflows, or is larger than |buffer_size|.
+zx_status_t StartingOutOfLineOffset(const fidl_type_t* type,
+ uint32_t buffer_size,
+ uint32_t* out_first_out_of_line,
+ const char** out_error);
} // namespace fidl