[fidl] Enforce zero-ordinal xunion invariants.
For extensible unions, zero is an allowed ordinal, and indicates that
the union is empty. It is a validation error for a xunion to be zero and
non-empty, or non-zero and empty.
This patch adds tests for that and updates the walker to enforce that.
TEST: /boot/test/sys/fidl-test
Change-Id: I2ece2b1ca6a6821cfe1d73cf85da902dd5d51180
diff --git a/system/ulib/fidl/walker.h b/system/ulib/fidl/walker.h
index bc1cab2..188fce5 100644
--- a/system/ulib/fidl/walker.h
+++ b/system/ulib/fidl/walker.h
@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#pragma once
+#ifndef ZIRCON_SYSTEM_ULIB_FIDL_WALKER_H_
+#define ZIRCON_SYSTEM_ULIB_FIDL_WALKER_H_
#include <cstdint>
#include <cstdlib>
@@ -39,27 +40,27 @@
constexpr 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);
+ 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);
}
__builtin_unreachable();
}
@@ -83,7 +84,8 @@
static_assert(CheckVisitorInterface<VisitorSuper, VisitorImpl>(), "");
public:
- Walker(const fidl_type_t* type, StartingPoint start) : type_(type), start_(start) {}
+ Walker(const fidl_type_t* type, StartingPoint start)
+ : type_(type), start_(start) {}
// Walk the object/buffer located at |start_|.
void Walk(VisitorImpl& visitor);
@@ -105,70 +107,70 @@
Frame(const fidl_type_t* fidl_type, Position position)
: position(position) {
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;
- struct_state.field = 0;
- 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.field = fidl_type->coded_table.fields;
- table_state.remaining_fields = fidl_type->coded_table.field_count;
- table_state.present_count = 0;
- table_state.ordinal = 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;
- xunion_state.inside_envelope = false;
- 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;
- array_state.element_offset = 0;
- 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;
+ case fidl::kFidlTypeStruct:
+ state = kStateStruct;
+ struct_state.fields = fidl_type->coded_struct.fields;
+ struct_state.field_count = fidl_type->coded_struct.field_count;
+ struct_state.field = 0;
+ 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.field = fidl_type->coded_table.fields;
+ table_state.remaining_fields = fidl_type->coded_table.field_count;
+ table_state.present_count = 0;
+ table_state.ordinal = 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;
+ xunion_state.inside_envelope = false;
+ 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;
+ array_state.element_offset = 0;
+ 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;
}
}
@@ -378,20 +380,22 @@
// 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_IMPL(status, pop) \
- switch ((status)) { \
- case Status::kSuccess: \
- break; \
- case Status::kConstraintViolationError: \
- if (VisitorImpl::kContinueAfterConstraintViolation) { \
- if ((pop)) { Pop(); } \
- continue; \
- } else { \
- return; \
- } \
- case Status::kMemoryError: \
- return; \
- } \
+#define FIDL_STATUS_GUARD_IMPL(status, pop) \
+ switch ((status)) { \
+ case Status::kSuccess: \
+ break; \
+ case Status::kConstraintViolationError: \
+ if (VisitorImpl::kContinueAfterConstraintViolation) { \
+ if ((pop)) { \
+ Pop(); \
+ } \
+ continue; \
+ } else { \
+ return; \
+ } \
+ case Status::kMemoryError: \
+ return; \
+ }
#define FIDL_STATUS_GUARD(status) FIDL_STATUS_GUARD_IMPL(status, true)
#define FIDL_STATUS_GUARD_NO_POP(status) FIDL_STATUS_GUARD_IMPL(status, false)
@@ -400,351 +404,365 @@
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;
- Position field_position = frame->position + field.offset;
- if (!Push(Frame(field_type, field_position))) {
- visitor.OnError("recursion depth exceeded processing struct");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- continue;
- }
- case Frame::kStateStructPointer: {
- if (*PtrTo<Ptr<void>>(frame->position) == nullptr) {
- Pop();
- continue;
- }
- auto status = visitor.VisitPointer(frame->position,
- PtrTo<Ptr<void>>(frame->position),
- frame->struct_pointer_state.struct_type->size,
- &frame->position);
- FIDL_STATUS_GUARD(status);
- const fidl::FidlCodedStruct* coded_struct = frame->struct_pointer_state.struct_type;
- *frame = Frame(coded_struct, frame->position);
- continue;
- }
- case Frame::kStateTable: {
- auto& table_frame = frame->table_state;
- // Utility to locate the position of the Nth-ordinal envelope header
- auto envelope_position = [&frame] (uint32_t ordinal) -> Position {
- return frame->position
- + (ordinal - 1) * static_cast<uint32_t>(sizeof(fidl_envelope_t));
- };
- if (table_frame.ordinal == 0) {
- // Process the vector part of the table
- auto envelope_vector_ptr = PtrTo<fidl_vector_t>(frame->position);
- if (envelope_vector_ptr->data == nullptr) {
- visitor.OnError("Table data cannot be absent");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- uint32_t size;
- if (mul_overflow(envelope_vector_ptr->count, sizeof(fidl_envelope_t), &size)) {
- visitor.OnError("integer overflow calculating table size");
- return;
- }
- auto status = visitor.VisitPointer(frame->position,
- &envelope_vector_ptr->data,
- size,
- &frame->position);
- FIDL_STATUS_GUARD(status);
- table_frame.ordinal = 1;
- table_frame.present_count = static_cast<uint32_t>(envelope_vector_ptr->count);
- table_frame.inside_envelope = false;
- continue;
- }
- if (table_frame.inside_envelope) {
- // Leave the envelope that was entered during the last iteration
- uint32_t last_ordinal = table_frame.ordinal - 1;
- ZX_DEBUG_ASSERT(last_ordinal >= 1);
- Position envelope_pos = envelope_position(last_ordinal);
- auto envelope_ptr = PtrTo<fidl_envelope_t>(envelope_pos);
- table_frame.inside_envelope = false;
- auto status = visitor.LeaveEnvelope(envelope_pos, envelope_ptr);
- FIDL_STATUS_GUARD(status);
- }
- if (table_frame.ordinal > table_frame.present_count) {
- // Processed last stored field in table. Done with this table.
- Pop();
- continue;
- }
- const fidl::FidlTableField* known_field = nullptr;
- if (table_frame.remaining_fields > 0) {
- const fidl::FidlTableField* field = table_frame.field;
- if (field->ordinal == table_frame.ordinal) {
- known_field = field;
- 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++;
- // 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);
- FIDL_STATUS_GUARD(status);
- table_frame.inside_envelope = true;
- // Skip empty envelopes
- if (envelope_ptr->data == nullptr) {
- continue;
- }
- if (payload_type != nullptr) {
- Position position;
- auto status =
- visitor.VisitPointer(frame->position,
- // casting since |envelope_ptr->data| is always void*
- &const_cast<Ptr<void>&>(envelope_ptr->data),
- TypeSize(payload_type),
- &position);
- // Do not pop the table frame, to guarantee calling |LeaveEnvelope|
- FIDL_STATUS_GUARD_NO_POP(status);
- if (!Push(Frame(payload_type, position))) {
- visitor.OnError("recursion depth exceeded processing table");
- FIDL_STATUS_GUARD_NO_POP(Status::kConstraintViolationError);
- }
- } else {
- // No coding table for this ordinal.
- // Still patch pointers, but cannot recurse into the payload.
- Position position;
- auto status =
- visitor.VisitPointer(frame->position,
- &const_cast<Ptr<void>&>(envelope_ptr->data),
- envelope_ptr->num_bytes,
- &position);
- FIDL_STATUS_GUARD_NO_POP(status);
- }
- continue;
- }
- case Frame::kStateTablePointer: {
- if (*PtrTo<Ptr<fidl_vector_t>>(frame->position) == nullptr) {
- Pop();
- continue;
- }
- auto status = visitor.VisitPointer(frame->position,
- PtrTo<Ptr<void>>(frame->position),
- static_cast<uint32_t>(sizeof(fidl_vector_t)),
- &frame->position);
- FIDL_STATUS_GUARD(status);
- const fidl::FidlCodedTable* coded_table = frame->table_pointer_state.table_type;
- *frame = Frame(coded_table, frame->position);
- continue;
- }
- case Frame::kStateUnion: {
- auto union_tag = *PtrTo<fidl_union_tag_t>(frame->position);
- if (union_tag >= frame->union_state.type_count) {
- visitor.OnError("Bad union discriminant");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- const fidl_type_t* member = frame->union_state.types[union_tag];
- if (!member) {
- Pop();
- continue;
- }
- frame->position += frame->union_state.data_offset;
- *frame = Frame(member, frame->position);
- continue;
- }
- case Frame::kStateUnionPointer: {
- if (*PtrTo<Ptr<fidl_union_tag_t>>(frame->position) == nullptr) {
- Pop();
- continue;
- }
- auto status = visitor.VisitPointer(frame->position,
- PtrTo<Ptr<void>>(frame->position),
- frame->union_pointer_state.union_type->size,
- &frame->position);
- FIDL_STATUS_GUARD(status);
- const fidl::FidlCodedUnion* coded_union = frame->union_pointer_state.union_type;
- *frame = Frame(coded_union, frame->position);
- continue;
- }
- case Frame::kStateXUnion: {
- auto xunion = PtrTo<fidl_xunion_t>(frame->position);
- const auto envelope_pos = frame->position + offsetof(fidl_xunion_t, envelope);
- auto envelope_ptr = &xunion->envelope;
- // |inside_envelope| is always false when first encountering an xunion.
- if (frame->xunion_state.inside_envelope) {
- // Finished processing the xunion field, and is in clean-up state
- auto status = visitor.LeaveEnvelope(envelope_pos, envelope_ptr);
- FIDL_STATUS_GUARD(status);
- Pop();
- continue;
- }
- if (xunion->padding != 0) {
- visitor.OnError("xunion padding after discriminant are non-zero");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- // Find coding table corresponding to the ordinal via linear search
- 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;
- }
- }
- // 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);
- FIDL_STATUS_GUARD(status);
- frame->xunion_state.inside_envelope = true;
- // Skip empty envelopes
- if (envelope_ptr->data == nullptr) {
- continue;
- }
- if (payload_type != nullptr) {
- Position position;
- auto status =
- visitor.VisitPointer(frame->position,
- &const_cast<Ptr<void>&>(envelope_ptr->data),
- TypeSize(payload_type),
- &position);
- FIDL_STATUS_GUARD_NO_POP(status);
- if (!Push(Frame(payload_type, position))) {
- visitor.OnError("recursion depth exceeded processing xunion");
- FIDL_STATUS_GUARD_NO_POP(Status::kConstraintViolationError);
- }
- } else {
- // No coding table for this ordinal.
- // Still patch pointers, but cannot recurse into the payload.
- Position position;
- auto status =
- visitor.VisitPointer(frame->position,
- &const_cast<Ptr<void>&>(envelope_ptr->data),
- envelope_ptr->num_bytes,
- &position);
- FIDL_STATUS_GUARD_NO_POP(status);
- }
- continue;
- }
- case Frame::kStateXUnionPointer: {
- if (*PtrTo<Ptr<fidl_xunion_t>>(frame->position) == nullptr) {
- Pop();
- continue;
- }
- auto status = visitor.VisitPointer(frame->position,
- PtrTo<Ptr<void>>(frame->position),
- static_cast<uint32_t>(sizeof(fidl_xunion_t)),
- &frame->position);
- FIDL_STATUS_GUARD(status);
- const fidl::FidlCodedXUnion* coded_xunion = frame->xunion_pointer_state.xunion_type;
- *frame = Frame(coded_xunion, frame->position);
- 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;
- Position position = frame->position + element_offset;
- if (!Push(Frame(element_type, position))) {
- visitor.OnError("recursion depth exceeded processing array");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- continue;
- }
- case Frame::kStateString: {
- auto string_ptr = PtrTo<fidl_string_t>(frame->position);
- if (string_ptr->data == nullptr) {
- if (!frame->string_state.nullable) {
- visitor.OnError("non-nullable string is absent");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- if (string_ptr->size != 0) {
- visitor.OnError("string is absent but length is not zero");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- Pop();
- continue;
- }
- uint64_t bound = frame->string_state.max_size;
- uint64_t size = string_ptr->size;
- if (size > std::numeric_limits<uint32_t>::max()) {
- visitor.OnError("string size overflows 32 bits");
- FIDL_STATUS_GUARD(Status::kMemoryError);
- }
- if (size > bound) {
- visitor.OnError("message tried to access too large of a bounded string");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- Position position;
- auto status = visitor.VisitPointer(position,
- &reinterpret_cast<Ptr<void>&>(
- const_cast<Ptr<char>&>(
- string_ptr->data)),
- static_cast<uint32_t>(size),
- &position);
- FIDL_STATUS_GUARD(status);
+ case Frame::kStateStruct: {
+ const uint32_t field_index = frame->NextStructField();
+ if (field_index == frame->struct_state.field_count) {
Pop();
continue;
}
- case Frame::kStateHandle: {
- auto handle_ptr = PtrTo<zx_handle_t>(frame->position);
- if (*handle_ptr == ZX_HANDLE_INVALID) {
- if (!frame->handle_state.nullable) {
- visitor.OnError("message is missing a non-nullable handle");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- Pop();
- continue;
- }
- auto status = visitor.VisitHandle(frame->position, handle_ptr);
- FIDL_STATUS_GUARD(status);
+ const fidl::FidlStructField& field = frame->struct_state.fields[field_index];
+ const fidl_type_t* field_type = field.type;
+ Position field_position = frame->position + field.offset;
+ if (!Push(Frame(field_type, field_position))) {
+ visitor.OnError("recursion depth exceeded processing struct");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ continue;
+ }
+ case Frame::kStateStructPointer: {
+ if (*PtrTo<Ptr<void>>(frame->position) == nullptr) {
Pop();
continue;
}
- case Frame::kStateVector: {
- auto vector_ptr = PtrTo<fidl_vector_t>(frame->position);
- if (vector_ptr->data == nullptr) {
- if (!frame->vector_state.nullable) {
- visitor.OnError("non-nullable vector is absent");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- if (vector_ptr->count != 0) {
- visitor.OnError("absent vector of non-zero elements");
- FIDL_STATUS_GUARD(Status::kConstraintViolationError);
- }
- Pop();
- continue;
- }
- if (vector_ptr->count > frame->vector_state.max_count) {
- visitor.OnError("message tried to access too large of a bounded vector");
+ auto status = visitor.VisitPointer(frame->position,
+ PtrTo<Ptr<void>>(frame->position),
+ frame->struct_pointer_state.struct_type->size,
+ &frame->position);
+ FIDL_STATUS_GUARD(status);
+ const fidl::FidlCodedStruct* coded_struct = frame->struct_pointer_state.struct_type;
+ *frame = Frame(coded_struct, frame->position);
+ continue;
+ }
+ case Frame::kStateTable: {
+ auto& table_frame = frame->table_state;
+ // Utility to locate the position of the Nth-ordinal envelope header
+ auto envelope_position = [&frame](uint32_t ordinal) -> Position {
+ return frame->position +
+ (ordinal - 1) * static_cast<uint32_t>(sizeof(fidl_envelope_t));
+ };
+ if (table_frame.ordinal == 0) {
+ // Process the vector part of the table
+ auto envelope_vector_ptr = PtrTo<fidl_vector_t>(frame->position);
+ if (envelope_vector_ptr->data == nullptr) {
+ visitor.OnError("Table data cannot be absent");
FIDL_STATUS_GUARD(Status::kConstraintViolationError);
}
uint32_t size;
- if (mul_overflow(vector_ptr->count, frame->vector_state.element_size, &size)) {
- visitor.OnError("integer overflow calculating vector size");
+ if (mul_overflow(envelope_vector_ptr->count, sizeof(fidl_envelope_t), &size)) {
+ visitor.OnError("integer overflow calculating table size");
return;
}
auto status = visitor.VisitPointer(frame->position,
- &vector_ptr->data,
+ &envelope_vector_ptr->data,
size,
&frame->position);
FIDL_STATUS_GUARD(status);
- if (frame->vector_state.element) {
- // Continue by visiting the vector elements as an array.
- *frame = Frame(frame->vector_state.element, size,
- frame->vector_state.element_size, frame->position);
- } else {
- // If there is no element type pointer, there is
- // nothing to process in the vector secondary
- // payload. So just continue.
- Pop();
+ table_frame.ordinal = 1;
+ table_frame.present_count = static_cast<uint32_t>(envelope_vector_ptr->count);
+ table_frame.inside_envelope = false;
+ continue;
+ }
+ if (table_frame.inside_envelope) {
+ // Leave the envelope that was entered during the last iteration
+ uint32_t last_ordinal = table_frame.ordinal - 1;
+ ZX_DEBUG_ASSERT(last_ordinal >= 1);
+ Position envelope_pos = envelope_position(last_ordinal);
+ auto envelope_ptr = PtrTo<fidl_envelope_t>(envelope_pos);
+ table_frame.inside_envelope = false;
+ auto status = visitor.LeaveEnvelope(envelope_pos, envelope_ptr);
+ FIDL_STATUS_GUARD(status);
+ }
+ if (table_frame.ordinal > table_frame.present_count) {
+ // Processed last stored field in table. Done with this table.
+ Pop();
+ continue;
+ }
+ const fidl::FidlTableField* known_field = nullptr;
+ if (table_frame.remaining_fields > 0) {
+ const fidl::FidlTableField* field = table_frame.field;
+ if (field->ordinal == table_frame.ordinal) {
+ known_field = field;
+ 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++;
+ // 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);
+ FIDL_STATUS_GUARD(status);
+ table_frame.inside_envelope = true;
+ // Skip empty envelopes
+ if (envelope_ptr->data == nullptr) {
+ continue;
+ }
+ if (payload_type != nullptr) {
+ Position position;
+ auto status =
+ visitor.VisitPointer(frame->position,
+ // casting since |envelope_ptr->data| is always void*
+ &const_cast<Ptr<void>&>(envelope_ptr->data),
+ TypeSize(payload_type),
+ &position);
+ // Do not pop the table frame, to guarantee calling |LeaveEnvelope|
+ FIDL_STATUS_GUARD_NO_POP(status);
+ if (!Push(Frame(payload_type, position))) {
+ visitor.OnError("recursion depth exceeded processing table");
+ FIDL_STATUS_GUARD_NO_POP(Status::kConstraintViolationError);
+ }
+ } else {
+ // No coding table for this ordinal.
+ // Still patch pointers, but cannot recurse into the payload.
+ Position position;
+ auto status =
+ visitor.VisitPointer(frame->position,
+ &const_cast<Ptr<void>&>(envelope_ptr->data),
+ envelope_ptr->num_bytes,
+ &position);
+ FIDL_STATUS_GUARD_NO_POP(status);
+ }
+ continue;
+ }
+ case Frame::kStateTablePointer: {
+ if (*PtrTo<Ptr<fidl_vector_t>>(frame->position) == nullptr) {
+ Pop();
+ continue;
+ }
+ auto status = visitor.VisitPointer(frame->position,
+ PtrTo<Ptr<void>>(frame->position),
+ static_cast<uint32_t>(sizeof(fidl_vector_t)),
+ &frame->position);
+ FIDL_STATUS_GUARD(status);
+ const fidl::FidlCodedTable* coded_table = frame->table_pointer_state.table_type;
+ *frame = Frame(coded_table, frame->position);
+ continue;
+ }
+ case Frame::kStateUnion: {
+ auto union_tag = *PtrTo<fidl_union_tag_t>(frame->position);
+ if (union_tag >= frame->union_state.type_count) {
+ visitor.OnError("Bad union discriminant");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ const fidl_type_t* member = frame->union_state.types[union_tag];
+ if (!member) {
+ Pop();
+ continue;
+ }
+ frame->position += frame->union_state.data_offset;
+ *frame = Frame(member, frame->position);
+ continue;
+ }
+ case Frame::kStateUnionPointer: {
+ if (*PtrTo<Ptr<fidl_union_tag_t>>(frame->position) == nullptr) {
+ Pop();
+ continue;
+ }
+ auto status = visitor.VisitPointer(frame->position,
+ PtrTo<Ptr<void>>(frame->position),
+ frame->union_pointer_state.union_type->size,
+ &frame->position);
+ FIDL_STATUS_GUARD(status);
+ const fidl::FidlCodedUnion* coded_union = frame->union_pointer_state.union_type;
+ *frame = Frame(coded_union, frame->position);
+ continue;
+ }
+ case Frame::kStateXUnion: {
+ auto xunion = PtrTo<fidl_xunion_t>(frame->position);
+ const auto envelope_pos = frame->position + offsetof(fidl_xunion_t, envelope);
+ auto envelope_ptr = &xunion->envelope;
+ // |inside_envelope| is always false when first encountering an xunion.
+ if (frame->xunion_state.inside_envelope) {
+ // Finished processing the xunion field, and is in clean-up state
+ auto status = visitor.LeaveEnvelope(envelope_pos, envelope_ptr);
+ FIDL_STATUS_GUARD(status);
+ Pop();
+ continue;
+ }
+ if (xunion->padding != 0) {
+ visitor.OnError("xunion padding after discriminant are non-zero");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ // Validate zero-ordinal invariants
+ if (xunion->tag == 0) {
+ if (envelope_ptr->data != nullptr || envelope_ptr->num_bytes != 0 ||
+ envelope_ptr->num_handles != 0) {
+ visitor.OnError("xunion with zero as ordinal must be empty");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ Pop();
+ continue;
+ }
+ // Find coding table corresponding to the ordinal via linear search
+ 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;
+ }
+ }
+ // 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);
+ FIDL_STATUS_GUARD(status);
+ frame->xunion_state.inside_envelope = true;
+ // Skip empty envelopes
+ if (envelope_ptr->data == nullptr) {
+ if (xunion->tag != 0) {
+ visitor.OnError("empty xunion must have zero as ordinal");
+ FIDL_STATUS_GUARD_NO_POP(Status::kConstraintViolationError);
}
continue;
}
- case Frame::kStateDone: {
+ if (payload_type != nullptr) {
+ Position position;
+ auto status =
+ visitor.VisitPointer(frame->position,
+ &const_cast<Ptr<void>&>(envelope_ptr->data),
+ TypeSize(payload_type),
+ &position);
+ FIDL_STATUS_GUARD_NO_POP(status);
+ if (!Push(Frame(payload_type, position))) {
+ visitor.OnError("recursion depth exceeded processing xunion");
+ FIDL_STATUS_GUARD_NO_POP(Status::kConstraintViolationError);
+ }
+ } else {
+ // No coding table for this ordinal.
+ // Still patch pointers, but cannot recurse into the payload.
+ Position position;
+ auto status =
+ visitor.VisitPointer(frame->position,
+ &const_cast<Ptr<void>&>(envelope_ptr->data),
+ envelope_ptr->num_bytes,
+ &position);
+ FIDL_STATUS_GUARD_NO_POP(status);
+ }
+ continue;
+ }
+ case Frame::kStateXUnionPointer: {
+ if (*PtrTo<Ptr<fidl_xunion_t>>(frame->position) == nullptr) {
+ Pop();
+ continue;
+ }
+ auto status = visitor.VisitPointer(frame->position,
+ PtrTo<Ptr<void>>(frame->position),
+ static_cast<uint32_t>(sizeof(fidl_xunion_t)),
+ &frame->position);
+ FIDL_STATUS_GUARD(status);
+ const fidl::FidlCodedXUnion* coded_xunion = frame->xunion_pointer_state.xunion_type;
+ *frame = Frame(coded_xunion, frame->position);
+ 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;
+ Position position = frame->position + element_offset;
+ if (!Push(Frame(element_type, position))) {
+ visitor.OnError("recursion depth exceeded processing array");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ continue;
+ }
+ case Frame::kStateString: {
+ auto string_ptr = PtrTo<fidl_string_t>(frame->position);
+ if (string_ptr->data == nullptr) {
+ if (!frame->string_state.nullable) {
+ visitor.OnError("non-nullable string is absent");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ if (string_ptr->size != 0) {
+ visitor.OnError("string is absent but length is not zero");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ Pop();
+ continue;
+ }
+ uint64_t bound = frame->string_state.max_size;
+ uint64_t size = string_ptr->size;
+ if (size > std::numeric_limits<uint32_t>::max()) {
+ visitor.OnError("string size overflows 32 bits");
+ FIDL_STATUS_GUARD(Status::kMemoryError);
+ }
+ if (size > bound) {
+ visitor.OnError("message tried to access too large of a bounded string");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ Position position;
+ auto status = visitor.VisitPointer(position,
+ &reinterpret_cast<Ptr<void>&>(
+ const_cast<Ptr<char>&>(
+ string_ptr->data)),
+ static_cast<uint32_t>(size),
+ &position);
+ FIDL_STATUS_GUARD(status);
+ Pop();
+ continue;
+ }
+ case Frame::kStateHandle: {
+ auto handle_ptr = PtrTo<zx_handle_t>(frame->position);
+ if (*handle_ptr == ZX_HANDLE_INVALID) {
+ if (!frame->handle_state.nullable) {
+ visitor.OnError("message is missing a non-nullable handle");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ Pop();
+ continue;
+ }
+ auto status = visitor.VisitHandle(frame->position, handle_ptr);
+ FIDL_STATUS_GUARD(status);
+ Pop();
+ continue;
+ }
+ case Frame::kStateVector: {
+ auto vector_ptr = PtrTo<fidl_vector_t>(frame->position);
+ if (vector_ptr->data == nullptr) {
+ if (!frame->vector_state.nullable) {
+ visitor.OnError("non-nullable vector is absent");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ if (vector_ptr->count != 0) {
+ visitor.OnError("absent vector of non-zero elements");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ Pop();
+ continue;
+ }
+ if (vector_ptr->count > frame->vector_state.max_count) {
+ visitor.OnError("message tried to access too large of a bounded vector");
+ FIDL_STATUS_GUARD(Status::kConstraintViolationError);
+ }
+ uint32_t size;
+ if (mul_overflow(vector_ptr->count, frame->vector_state.element_size, &size)) {
+ visitor.OnError("integer overflow calculating vector size");
return;
}
+ auto status = visitor.VisitPointer(frame->position,
+ &vector_ptr->data,
+ size,
+ &frame->position);
+ FIDL_STATUS_GUARD(status);
+ if (frame->vector_state.element) {
+ // Continue by visiting the vector elements as an array.
+ *frame = Frame(frame->vector_state.element, size,
+ frame->vector_state.element_size, frame->position);
+ } else {
+ // If there is no element type pointer, there is
+ // nothing to process in the vector secondary
+ // payload. So just continue.
+ Pop();
+ }
+ continue;
+ }
+ case Frame::kStateDone: {
+ return;
+ }
}
}
}
@@ -788,3 +806,5 @@
const char** out_error);
} // namespace fidl
+
+#endif // ZIRCON_SYSTEM_ULIB_FIDL_WALKER_H_
diff --git a/system/utest/fidl/fidl/extra_messages.cpp b/system/utest/fidl/fidl/extra_messages.cpp
index db66efa..243e4d2 100644
--- a/system/utest/fidl/fidl/extra_messages.cpp
+++ b/system/utest/fidl/fidl/extra_messages.cpp
@@ -11,13 +11,15 @@
extern const fidl_type_t fidl_test_coding_OlderSimpleTableTable;
extern const fidl_type_t fidl_test_coding_NewerSimpleTableTable;
extern const fidl_type_t fidl_test_coding_SimpleTableTable;
+extern const fidl_type_t fidl_test_coding_SampleXUnionTable;
+extern const fidl_type_t fidl_test_coding_SampleXUnionStructTable;
static const fidl_type_t Vectoruint324294967295nonnullableTable = fidl_type_t(::fidl::FidlCodedVector(nullptr, 4294967295, 4, ::fidl::kNonnullable));
extern const fidl_type_t fidl_test_coding_LinearizerTestVectorOfUint32RequestTable;
-static const ::fidl::FidlField fidl_test_coding_LinearizerTestVectorOfUint32RequestFields[] = {
- ::fidl::FidlField(&Vectoruint324294967295nonnullableTable, 16)
+static const ::fidl::FidlStructField fidl_test_coding_LinearizerTestVectorOfUint32RequestFields[] = {
+ ::fidl::FidlStructField(&Vectoruint324294967295nonnullableTable, 16)
};
const fidl_type_t fidl_test_coding_LinearizerTestVectorOfUint32RequestTable = fidl_type_t(::fidl::FidlCodedStruct(fidl_test_coding_LinearizerTestVectorOfUint32RequestFields, 1, 32, "fidl.test.coding/LinearizerTestVectorOfUint32Request"));
@@ -26,8 +28,8 @@
static const fidl_type_t VectorString4294967295nonnullable4294967295nonnullableTable = fidl_type_t(::fidl::FidlCodedVector(&String4294967295nonnullableTable, 4294967295, 16, ::fidl::kNonnullable));
extern const fidl_type_t fidl_test_coding_LinearizerTestVectorOfStringRequestTable;
-static const ::fidl::FidlField fidl_test_coding_LinearizerTestVectorOfStringRequestFields[] = {
- ::fidl::FidlField(&VectorString4294967295nonnullable4294967295nonnullableTable, 16)
+static const ::fidl::FidlStructField fidl_test_coding_LinearizerTestVectorOfStringRequestFields[] = {
+ ::fidl::FidlStructField(&VectorString4294967295nonnullable4294967295nonnullableTable, 16)
};
const fidl_type_t fidl_test_coding_LinearizerTestVectorOfStringRequestTable = fidl_type_t(::fidl::FidlCodedStruct(fidl_test_coding_LinearizerTestVectorOfStringRequestFields, 1, 32, "fidl.test.coding/LinearizerTestVectorOfStringRequest"));
@@ -35,15 +37,15 @@
static const fidl_type_t VectorHandlehandlenonnullable2nonnullableTable = fidl_type_t(::fidl::FidlCodedVector(&HandlehandlenonnullableTable, 2, 4, ::fidl::kNonnullable));
-static const ::fidl::FidlField fidl_test_coding_StructWithManyHandlesFields[] = {
- ::fidl::FidlField(&HandlehandlenonnullableTable, 0),
- ::fidl::FidlField(&HandlehandlenonnullableTable, 4),
- ::fidl::FidlField(&VectorHandlehandlenonnullable2nonnullableTable, 8)
+static const ::fidl::FidlStructField fidl_test_coding_StructWithManyHandlesFields[] = {
+ ::fidl::FidlStructField(&HandlehandlenonnullableTable, 0),
+ ::fidl::FidlStructField(&HandlehandlenonnullableTable, 4),
+ ::fidl::FidlStructField(&VectorHandlehandlenonnullable2nonnullableTable, 8)
};
const fidl_type_t fidl_test_coding_StructWithManyHandlesTable = fidl_type_t(::fidl::FidlCodedStruct(fidl_test_coding_StructWithManyHandlesFields, 3, 24, "fidl.test.coding/StructWithManyHandles"));
-static const ::fidl::FidlField fidl_test_coding_StructWithHandleFields[] = {
- ::fidl::FidlField(&HandlehandlenonnullableTable, 0)
+static const ::fidl::FidlStructField fidl_test_coding_StructWithHandleFields[] = {
+ ::fidl::FidlStructField(&HandlehandlenonnullableTable, 0)
};
const fidl_type_t fidl_test_coding_StructWithHandleTable = fidl_type_t(::fidl::FidlCodedStruct(fidl_test_coding_StructWithHandleFields, 1, 8, "fidl.test.coding/StructWithHandle"));
@@ -53,7 +55,7 @@
};
const fidl_type_t fidl_test_coding_TableOfStructWithHandleTable = fidl_type_t(::fidl::FidlCodedTable(fidl_test_coding_TableOfStructWithHandleFields, 2, "fidl.test.coding/TableOfStructWithHandle"));
-static const ::fidl::FidlField fidl_test_coding_IntStructFields[] = {};
+static const ::fidl::FidlStructField fidl_test_coding_IntStructFields[] = {};
const fidl_type_t fidl_test_coding_IntStructTable = fidl_type_t(::fidl::FidlCodedStruct(fidl_test_coding_IntStructFields, 0, 8, "fidl.test.coding/IntStruct"));
static const ::fidl::FidlTableField fidl_test_coding_OlderSimpleTableFields[] = {
@@ -74,4 +76,15 @@
};
const fidl_type_t fidl_test_coding_SimpleTableTable = fidl_type_t(::fidl::FidlCodedTable(fidl_test_coding_SimpleTableFields, 2, "fidl.test.coding/SimpleTable"));
+static const ::fidl::FidlXUnionField fidl_test_coding_SampleXUnionFields[] = {
+ ::fidl::FidlXUnionField(&fidl_test_coding_IntStructTable,376675050),
+ ::fidl::FidlXUnionField(&fidl_test_coding_SimpleTableTable,586453270)
+};
+const fidl_type_t fidl_test_coding_SampleXUnionTable = fidl_type_t(::fidl::FidlCodedXUnion(2, fidl_test_coding_SampleXUnionFields, "fidl.test.coding/SampleXUnion"));
+
+static const ::fidl::FidlStructField fidl_test_coding_SampleXUnionStructFields[] = {
+ ::fidl::FidlStructField(&fidl_test_coding_SampleXUnionTable, 0)
+};
+const fidl_type_t fidl_test_coding_SampleXUnionStructTable = fidl_type_t(::fidl::FidlCodedStruct(fidl_test_coding_SampleXUnionStructFields, 1, 24, "fidl.test.coding/SampleXUnionStruct"));
+
} // extern "C"
diff --git a/system/utest/fidl/fidl/extra_messages.fidl b/system/utest/fidl/fidl/extra_messages.fidl
index b8b5fb3..3d14fe1 100644
--- a/system/utest/fidl/fidl/extra_messages.fidl
+++ b/system/utest/fidl/fidl/extra_messages.fidl
@@ -56,3 +56,12 @@
1: StructWithHandle a;
2: StructWithManyHandles b;
};
+
+xunion SampleXUnion {
+ IntStruct i;
+ SimpleTable st;
+};
+
+struct SampleXUnionStruct {
+ SampleXUnion xu;
+};
diff --git a/system/utest/fidl/fidl/extra_messages.h b/system/utest/fidl/fidl/extra_messages.h
index 9fdda5f..9877c53 100644
--- a/system/utest/fidl/fidl/extra_messages.h
+++ b/system/utest/fidl/fidl/extra_messages.h
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <lib/fidl/coding.h>
+#include <lib/fidl/cpp/string_view.h>
+#include <lib/fidl/cpp/vector_view.h>
#include <lib/fidl/internal.h>
// "extern" definitions copied from extra_messages.cpp
@@ -16,10 +19,46 @@
extern const fidl_type_t fidl_test_coding_OlderSimpleTableTable;
extern const fidl_type_t fidl_test_coding_NewerSimpleTableTable;
extern const fidl_type_t fidl_test_coding_SimpleTableTable;
+extern const fidl_type_t fidl_test_coding_SampleXUnionTable;
+extern const fidl_type_t fidl_test_coding_SampleXUnionStructTable;
extern const fidl_type_t fidl_test_coding_LinearizerTestVectorOfUint32RequestTable;
extern const fidl_type_t fidl_test_coding_LinearizerTestVectorOfStringRequestTable;
+using SimpleTable = fidl::VectorView<fidl_envelope_t>;
+struct SimpleTableEnvelopes {
+ alignas(FIDL_ALIGNMENT)
+ fidl_envelope_t x;
+ fidl_envelope_t reserved1;
+ fidl_envelope_t reserved2;
+ fidl_envelope_t reserved3;
+ fidl_envelope_t y;
+};
+struct IntStruct {
+ alignas(FIDL_ALIGNMENT)
+ int64_t v;
+};
+
+struct SampleXUnion {
+ FIDL_ALIGNDECL
+ fidl_xunion_t header;
+
+ // Representing out-of-line part
+ union {
+ FIDL_ALIGNDECL
+ IntStruct i;
+
+ FIDL_ALIGNDECL
+ SimpleTable st;
+ };
+};
+constexpr uint32_t kSampleXUnionIntStructOrdinal = 376675050;
+
+struct SampleXUnionStruct {
+ FIDL_ALIGNDECL
+ SampleXUnion xu;
+};
+
#if defined(__cplusplus)
}
#endif
diff --git a/system/utest/fidl/linearizing_tests.cpp b/system/utest/fidl/linearizing_tests.cpp
index fc8a4db..c2d865b 100644
--- a/system/utest/fidl/linearizing_tests.cpp
+++ b/system/utest/fidl/linearizing_tests.cpp
@@ -338,21 +338,6 @@
bool linearize_simple_table() {
BEGIN_TEST;
- // Define the memory-layout of the inline object
- using SimpleTable = fidl::VectorView<fidl_envelope_t>;
- struct SimpleTableEnvelopes {
- alignas(FIDL_ALIGNMENT)
- fidl_envelope_t x;
- fidl_envelope_t reserved1;
- fidl_envelope_t reserved2;
- fidl_envelope_t reserved3;
- fidl_envelope_t y;
- };
- struct IntStruct {
- alignas(FIDL_ALIGNMENT)
- int64_t v;
- };
-
SimpleTableEnvelopes envelopes = {};
SimpleTable simple_table;
simple_table.set_count(5);
@@ -587,6 +572,69 @@
} // namespace
+bool linearize_xunion_empty_invariant_empty() {
+ BEGIN_TEST;
+
+ // Non-zero ordinal with empty envelope is an error
+ SampleXUnionStruct xunion = {};
+ xunion.xu.header = (fidl_xunion_t) {
+ .tag = kSampleXUnionIntStructOrdinal,
+ .padding = 0,
+ .envelope = {}
+ };
+ constexpr uint32_t buf_size = 512;
+ uint8_t buffer[buf_size];
+ const char* error = nullptr;
+ zx_status_t status;
+ uint32_t actual_num_bytes = 0;
+ status = fidl_linearize(&fidl_test_coding_SampleXUnionStructTable,
+ &xunion,
+ buffer,
+ buf_size,
+ &actual_num_bytes,
+ &error);
+ EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
+ EXPECT_NONNULL(error);
+ EXPECT_STR_EQ(error, "empty xunion must have zero as ordinal");
+
+ END_TEST;
+}
+
+bool linearize_xunion_empty_invariant_zero_ordinal() {
+ BEGIN_TEST;
+
+ // Zero ordinal with non-empty envelope is an error
+ IntStruct int_struct = {
+ .v = 100
+ };
+ SampleXUnionStruct xunion = {};
+ xunion.xu.header = (fidl_xunion_t) {
+ .tag = 0,
+ .padding = 0,
+ .envelope = (fidl_envelope_t) {
+ .num_bytes = 8,
+ .num_handles = 0,
+ .data = &int_struct
+ }
+ };
+ constexpr uint32_t buf_size = 512;
+ uint8_t buffer[buf_size];
+ const char* error = nullptr;
+ zx_status_t status;
+ uint32_t actual_num_bytes = 0;
+ status = fidl_linearize(&fidl_test_coding_SampleXUnionStructTable,
+ &xunion,
+ buffer,
+ buf_size,
+ &actual_num_bytes,
+ &error);
+ EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
+ EXPECT_NONNULL(error);
+ EXPECT_STR_EQ(error, "xunion with zero as ordinal must be empty");
+
+ END_TEST;
+}
+
BEGIN_TEST_CASE(strings)
RUN_TEST(linearize_present_nonnullable_string)
RUN_TEST(linearize_present_nonnullable_longer_string)
@@ -608,5 +656,11 @@
RUN_TEST(linearize_table_field_2)
END_TEST_CASE(tables)
+BEGIN_TEST_CASE(xunions)
+RUN_TEST(linearize_xunion_empty_invariant_empty)
+RUN_TEST(linearize_xunion_empty_invariant_zero_ordinal)
+END_TEST_CASE(xunions)
+
+
} // namespace
} // namespace fidl
diff --git a/system/utest/fidl/validating_tests.cpp b/system/utest/fidl/validating_tests.cpp
index 5da5312..d1efc7a 100644
--- a/system/utest/fidl/validating_tests.cpp
+++ b/system/utest/fidl/validating_tests.cpp
@@ -10,6 +10,7 @@
#include "fidl_coded_types.h"
#include "fidl_structs.h"
+#include "fidl/extra_messages.h"
namespace fidl {
namespace {
@@ -1554,6 +1555,56 @@
END_TEST;
}
+bool validate_valid_empty_xunion() {
+ BEGIN_TEST;
+
+ SampleXUnionStruct message = {};
+
+ const char* error = nullptr;
+ auto status = fidl_validate(&fidl_test_coding_SampleXUnionStructTable, &message,
+ sizeof(fidl_xunion_t), 0, &error);
+ EXPECT_EQ(status, ZX_OK);
+ EXPECT_NULL(error, error);
+
+ END_TEST;
+}
+
+bool validate_empty_xunion_nonzero_ordinal() {
+ BEGIN_TEST;
+
+ SampleXUnionStruct message = {};
+ message.xu.header.tag = kSampleXUnionIntStructOrdinal;
+
+ const char* error = nullptr;
+ auto status = fidl_validate(&fidl_test_coding_SampleXUnionStructTable, &message,
+ sizeof(fidl_xunion_t), 0, &error);
+ EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
+ EXPECT_NONNULL(error, error);
+ EXPECT_STR_EQ(error, "empty xunion must have zero as ordinal");
+
+ END_TEST;
+}
+
+bool validate_nonempty_xunion_zero_ordinal() {
+ BEGIN_TEST;
+
+ SampleXUnionStruct message = {};
+ message.xu.header.envelope = (fidl_envelope_t) {
+ .num_bytes = 8,
+ .num_handles = 0,
+ .presence = FIDL_ALLOC_PRESENT
+ };
+
+ const char* error = nullptr;
+ auto status = fidl_validate(&fidl_test_coding_SampleXUnionStructTable, &message,
+ sizeof(SampleXUnionStruct), 0, &error);
+ EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
+ EXPECT_NONNULL(error, error);
+ EXPECT_STR_EQ(error, "xunion with zero as ordinal must be empty");
+
+ END_TEST;
+}
+
BEGIN_TEST_CASE(null_parameters)
RUN_TEST(validate_null_validate_parameters)
END_TEST_CASE(null_parameters)
@@ -1630,5 +1681,11 @@
RUN_TEST(validate_nested_struct_recursion_too_deep_error)
END_TEST_CASE(structs)
+BEGIN_TEST_CASE(xunions)
+RUN_TEST(validate_valid_empty_xunion)
+RUN_TEST(validate_empty_xunion_nonzero_ordinal)
+RUN_TEST(validate_nonempty_xunion_zero_ordinal)
+END_TEST_CASE(xunions)
+
} // namespace
} // namespace fidl