blob: ebb79896a63b1d13024c6a2f172f3f02e4afae98 [file] [log] [blame]
// 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 LIB_FIDL_INTERNAL_H_
#define LIB_FIDL_INTERNAL_H_
#include <assert.h>
#include <lib/fidl/coding.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <zircon/assert.h>
#include <zircon/fidl.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#ifdef __Fuchsia__
#ifdef __cplusplus
#include <zircon/syscalls.h>
#endif
#endif
#ifdef __cplusplus
#include <type_traits>
#endif // __cplusplus
#ifdef __cplusplus
namespace fidl {
namespace internal {
// Certain encoding/decoding/validating functions expect their input byte buffers to not contain a
// transactional message header. This function trims the first 16 bytes from a buffer that
// previously contained space for the header, to prepare it to be used as an input for such
// functions.
inline zx_status_t fidl_exclude_header_bytes(const void* bytes, uint32_t num_bytes,
uint8_t** out_bytes, uint32_t* out_num_bytes,
const char** out_error_msg) {
if (unlikely(num_bytes < sizeof(fidl_message_header_t))) {
if (out_error_msg) {
*out_error_msg = "Message too short to contain header";
}
return ZX_ERR_INVALID_ARGS;
}
*out_num_bytes = num_bytes - (uint32_t)(sizeof(fidl_message_header_t));
if (unlikely(bytes == NULL)) {
if (out_error_msg) {
*out_error_msg = "Cannot decode null bytes";
}
return ZX_ERR_INVALID_ARGS;
}
*out_bytes = (uint8_t*)bytes + sizeof(fidl_message_header_t);
return ZX_OK;
}
} // namespace internal
} // namespace fidl
#endif
__BEGIN_CDECLS
zx_status_t FidlHandleCloseMany(const zx_handle_t* handles, size_t num_handles);
zx_status_t FidlHandleDispositionCloseMany(const zx_handle_disposition_t* handle_dispositions,
size_t num_handles);
zx_status_t FidlHandleInfoCloseMany(const zx_handle_info_t* handle_infos, size_t num_handles);
// All sizes here are given as uint32_t. Fidl message sizes are bounded to well below UINT32_MAX.
// This also applies to arrays and vectors. For arrays, element_count * element_size will always fit
// with 32 bits. For vectors, max_count * element_size will always fit within 32 bits.
// Pointers to other type tables within a type are always nonnull, with the exception of vectors.
// In that case, a null pointer indicates that the element type of the vector has no interesting
// information to be decoded (i.e. no pointers or handles). The vector type still needs to be
// emitted as it contains the information about the size of its secondary object. Contrast this with
// arrays: being inline, ones with no interesting coding information can be elided, just like a
// uint32 field in a struct is elided.
typedef bool FidlNullability;
static const FidlNullability kFidlNullability_Nonnullable = false;
static const FidlNullability kFidlNullability_Nullable = true;
typedef bool FidlStrictness;
static const FidlStrictness kFidlStrictness_Flexible = false;
static const FidlStrictness kFidlStrictness_Strict = true;
typedef bool FidlIsResource;
static const FidlIsResource kFidlIsResource_Resource = true;
static const FidlIsResource kFidlIsResource_NotResource = false;
// Indicates if a struct is empty.
typedef bool FidlEmpty;
static const FidlEmpty kFidlEmpty_IsNotEmpty = false;
static const FidlEmpty kFidlEmpty_IsEmpty = true;
// Determine if the pointer is aligned to |FIDL_ALIGNMENT|.
static inline bool FidlIsAligned(const uint8_t* ptr) {
uintptr_t uintptr = (uintptr_t)(ptr);
const uint64_t alignment_mask = FIDL_ALIGNMENT - 1;
return (uintptr & alignment_mask) == 0;
}
// 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|.
static inline bool FidlAddOutOfLine(uint32_t offset, uint32_t size, uint32_t* out_offset) {
const 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;
}
inline bool FidlIsZeroEnvelope(const fidl_envelope_t* envelope) {
static_assert(sizeof(*envelope) == sizeof(uint64_t), "");
uint64_t uval;
memcpy(&uval, envelope, sizeof(*envelope));
return uval == 0;
}
// Checks that the handle meets specified type and rights requirements. If the
// handle has execess rights, the rights will be reduced to the required rights.
//
// The handle pointed to by |handle_ptr| may be overwritten. If it is overwritten,
// the original handle will be closed.
//
// For details on rules used for these checks, see the implementation.
zx_status_t FidlEnsureHandleRights(zx_handle_t* handle_ptr, zx_rights_t actual_type,
zx_obj_type_t actual_rights, zx_obj_type_t required_object_type,
zx_rights_t required_rights, const char** error);
// Checks that the handle's actual rights meet specified type and rights requirements.
// If the handle has execess rights, the rights will be reduced to the required rights.
//
// The handle pointed to by |handle_ptr| may be overwritten. If it is overwritten,
// the original handle will be closed.
//
// For details on rules used for these checks, see the implementation.
zx_status_t FidlEnsureActualHandleRights(zx_handle_t* handle_ptr,
zx_obj_type_t required_object_type,
zx_rights_t required_rights, const char** error);
// Converts an array of |zx_handle_disposition_t| to an array of |zx_handle_info_t|.
//
// This behaves similarly to what happens when a handle is written using
// zx_channel_write_etc and then read using zx_channel_read_etc.
// The handle type and rights are checked and the output handle may have reduced
// rights if the input has excess rights.
//
// This takes ownership of the input handles.
zx_status_t FidlHandleDispositionsToHandleInfos(zx_handle_disposition_t* handle_dispositions,
zx_handle_info_t* handle_infos,
uint32_t num_handles);
typedef uint8_t FidlStructElementType;
static const FidlStructElementType kFidlStructElementType_Field = (uint8_t)1u;
static const FidlStructElementType kFidlStructElementType_Padding64 = (uint8_t)2u;
static const FidlStructElementType kFidlStructElementType_Padding32 = (uint8_t)3u;
static const FidlStructElementType kFidlStructElementType_Padding16 = (uint8_t)4u;
struct FidlStructElementHeader {
FidlStructElementType element_type;
FidlIsResource is_resource;
};
struct FidlStructField {
struct FidlStructElementHeader header;
uint32_t offset_v2;
const fidl_type_t* field_type;
};
struct FidlStructPadding {
struct FidlStructElementHeader header;
uint32_t offset_v2;
// Masks with 0xff on bytes with padding and 0x00 otherwise.
// They are used by VisitInternalPadding to zero (encoding) and validate (decoding)
// padding bytes.
union {
uint16_t mask_16;
uint32_t mask_32;
uint64_t mask_64;
};
};
// A struct element is either a field or padding.
struct FidlStructElement {
union {
struct FidlStructElementHeader header;
struct FidlStructField field;
struct FidlStructPadding padding;
};
#ifdef __cplusplus
static constexpr FidlStructElement Field(const fidl_type* type, uint32_t offset_v2,
FidlIsResource is_resource) {
return FidlStructElement{
.field =
FidlStructField{
.header =
FidlStructElementHeader{
.element_type = kFidlStructElementType_Field,
.is_resource = is_resource,
},
.offset_v2 = offset_v2,
.field_type = type,
},
};
}
static constexpr FidlStructElement Padding64(uint32_t offset_v2, uint64_t mask) {
return FidlStructElement{
.padding =
FidlStructPadding{
.header =
FidlStructElementHeader{
.element_type = kFidlStructElementType_Padding64,
.is_resource = kFidlIsResource_NotResource,
},
.offset_v2 = offset_v2,
.mask_64 = mask,
},
};
}
static constexpr FidlStructElement Padding32(uint32_t offset_v2, uint32_t mask) {
return FidlStructElement{
.padding =
FidlStructPadding{
.header =
FidlStructElementHeader{
.element_type = kFidlStructElementType_Padding32,
.is_resource = kFidlIsResource_NotResource,
},
.offset_v2 = offset_v2,
.mask_32 = mask,
},
};
}
static constexpr FidlStructElement Padding16(uint32_t offset_v2, uint16_t mask) {
return FidlStructElement{
.padding =
FidlStructPadding{
.header =
FidlStructElementHeader{
.element_type = kFidlStructElementType_Padding16,
.is_resource = kFidlIsResource_NotResource,
},
.offset_v2 = offset_v2,
.mask_16 = mask,
},
};
}
#endif // __cplusplus
};
struct FidlTableField {
const fidl_type_t* type;
uint32_t ordinal;
};
struct FidlUnionField {
const fidl_type_t* type;
};
// TODO(https://fxbug.dev/42119025): Consider starting enum values for FidlTypeTag from 1, not 0.
typedef uint8_t FidlTypeTag;
static const uint8_t kFidlTypePrimitive = 0;
static const uint8_t kFidlTypeEnum = 1;
static const uint8_t kFidlTypeBits = 2;
static const uint8_t kFidlTypeStruct = 3;
static const uint8_t kFidlTypeStructPointer = 4;
static const uint8_t kFidlTypeArray = 5;
static const uint8_t kFidlTypeString = 6;
static const uint8_t kFidlTypeHandle = 7;
static const uint8_t kFidlTypeVector = 8;
static const uint8_t kFidlTypeTable = 9;
static const uint8_t kFidlTypeXUnion = 10;
static const uint8_t kFidlTypeUnion = 10;
// TODO(https://fxbug.dev/42119025): Consider starting enum values for FidlCodedPrimitive from 1,
// not 0.
typedef uint8_t FidlCodedPrimitiveSubtype;
static const uint8_t kFidlCodedPrimitiveSubtype_Bool = 0;
static const uint8_t kFidlCodedPrimitiveSubtype_Int8 = 1;
static const uint8_t kFidlCodedPrimitiveSubtype_Int16 = 2;
static const uint8_t kFidlCodedPrimitiveSubtype_Int32 = 3;
static const uint8_t kFidlCodedPrimitiveSubtype_Int64 = 4;
static const uint8_t kFidlCodedPrimitiveSubtype_Uint8 = 5;
static const uint8_t kFidlCodedPrimitiveSubtype_Uint16 = 6;
static const uint8_t kFidlCodedPrimitiveSubtype_Uint32 = 7;
static const uint8_t kFidlCodedPrimitiveSubtype_Uint64 = 8;
static const uint8_t kFidlCodedPrimitiveSubtype_Float32 = 9;
static const uint8_t kFidlCodedPrimitiveSubtype_Float64 = 10;
typedef bool (*EnumValidationPredicate)(uint64_t);
// Coding Table Definitions
//
// FIDL coding tables describe the layout and constraints of the messages.
// Each coding table must start with a `tag`, to identify the kind of the
// coding table at runtime. For improved convenience working with these types,
// we provide an empty C++ type `fidl_type_t`, which is inherited by the
// coding tables in C++ mode, and dispatches to one of the subclasses based
// on the tag.
//
// Coding tables are generated in C files to avoid delayed-initialization
// issues, but are meant to be consumed by C++ files such as the walker.
// Hence parts of below are ifdef'ed with C++-specific blocks.
struct FidlCodedPrimitive;
struct FidlCodedEnum;
struct FidlCodedBits;
struct FidlCodedStruct;
struct FidlCodedStructPointer;
struct FidlCodedTable;
struct FidlCodedUnion;
struct FidlCodedArray;
struct FidlCodedHandle;
struct FidlCodedString;
struct FidlCodedVector;
#ifdef __cplusplus
// Empty struct containing helper functions for casting to derived
// coding table types. C++ empty base class optimization ensure that this
// struct shares the same starting address with any of its subclasses.
struct fidl_type {
constexpr FidlTypeTag type_tag() const;
constexpr const FidlCodedPrimitive& coded_primitive() const;
constexpr const FidlCodedEnum& coded_enum() const;
constexpr const FidlCodedBits& coded_bits() const;
constexpr const FidlCodedStruct& coded_struct() const;
constexpr const FidlCodedStructPointer& coded_struct_pointer() const;
constexpr const FidlCodedTable& coded_table() const;
constexpr const FidlCodedUnion& coded_union() const;
constexpr const FidlCodedArray& coded_array() const;
constexpr const FidlCodedHandle& coded_handle() const;
constexpr const FidlCodedString& coded_string() const;
constexpr const FidlCodedVector& coded_vector() const;
// This prevents designated initializers from working in C++20
#if __cplusplus <= 201703L
private:
// Prevent instances of this class from being accidentally used standalone
// as a value.
constexpr fidl_type() = default;
#endif
};
#define FIDL_INTERNAL_INHERIT_TYPE_T final : fidl_type
#else // __cplusplus
// No inheritance in C mode. This is okay because inheriting
// from an empty class does not affect the object layout at all.
#define FIDL_INTERNAL_INHERIT_TYPE_T
#endif // __cplusplus
struct FidlCodedPrimitive FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlCodedPrimitiveSubtype type;
};
struct FidlCodedEnum FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlCodedPrimitiveSubtype underlying_type;
const FidlStrictness strictness;
// The validate predicate is only used for strict enums, and is NULL for
// flexible enums.
const EnumValidationPredicate validate;
const char* name; // may be nullptr if omitted at compile time
};
struct FidlCodedBits FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlCodedPrimitiveSubtype underlying_type;
const FidlStrictness strictness;
const uint64_t mask;
const char* name; // may be nullptr if omitted at compile time
};
// Though the |size| is implied by the fields, computing that information is not
// the purview of this library. It's easier for the compiler to stash it.
struct FidlCodedStruct FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlEmpty is_empty;
// element_count should be a uint32_t, but for the sake of binary size
// a uint16_t is used (all existing values fit within this size).
// If a larger size is needed, replace FidlCodedStruct or add a second
// variant that supports the larger size.
const uint16_t element_count;
const uint32_t size_v2;
const struct FidlStructElement* const elements;
const char* name; // may be nullptr if omitted at compile time
};
struct FidlCodedStructPointer FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const struct FidlCodedStruct* const struct_type;
};
struct FidlCodedTable FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlIsResource is_resource;
const uint32_t field_count;
const struct FidlTableField* const fields;
const char* name; // may be nullptr if omitted at compile time
};
struct FidlCodedUnion FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlNullability nullable;
const FidlStrictness strictness;
const FidlIsResource is_resource;
const uint32_t field_count;
// The fields are in ordinal order, with ordinal 1 at index 0.
const struct FidlUnionField* const fields;
const char* name; // may be nullptr if omitted at compile time
};
// An array is essentially a struct with |array_size / element_size| of the same field, named at
// |element|.
struct FidlCodedArray FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
// element_size should be a uint32_t, but for the sake of binary size
// a uint16_t is used (all existing values fit within this size).
// If a larger size is needed, replace FidlCodedArray or add a second
// variant that supports the larger size.
const uint16_t element_size_v2;
const uint32_t array_size_v2;
const fidl_type_t* const element;
};
struct FidlCodedHandle FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlNullability nullable;
const zx_obj_type_t handle_subtype;
const zx_rights_t handle_rights;
static_assert(ZX_OBJ_TYPE_UPPER_BOUND <= UINT32_MAX, "");
};
struct FidlCodedString FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlNullability nullable;
const uint32_t max_size;
};
// Note that:
// - |max_count * element_size| is guaranteed to fit into a uint32_t.
// - |element| will always be non-null.
struct FidlCodedVector FIDL_INTERNAL_INHERIT_TYPE_T {
const FidlTypeTag tag;
const FidlNullability nullable;
const uint32_t max_count;
const uint32_t element_size_v2;
const fidl_type_t* const element;
};
#ifdef __cplusplus
struct FidlHasTypeTag final : fidl_type {
const FidlTypeTag tag;
FidlHasTypeTag() = delete;
};
__ALWAYS_INLINE constexpr FidlTypeTag fidl_type::type_tag() const {
return static_cast<const FidlHasTypeTag*>(this)->tag;
}
__ALWAYS_INLINE constexpr const FidlCodedPrimitive& fidl_type::coded_primitive() const {
return *static_cast<const FidlCodedPrimitive*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedEnum& fidl_type::coded_enum() const {
return *static_cast<const FidlCodedEnum*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedBits& fidl_type::coded_bits() const {
return *static_cast<const FidlCodedBits*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedStruct& fidl_type::coded_struct() const {
return *static_cast<const FidlCodedStruct*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedStructPointer& fidl_type::coded_struct_pointer() const {
return *static_cast<const FidlCodedStructPointer*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedTable& fidl_type::coded_table() const {
return *static_cast<const FidlCodedTable*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedUnion& fidl_type::coded_union() const {
return *static_cast<const FidlCodedUnion*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedArray& fidl_type::coded_array() const {
return *static_cast<const FidlCodedArray*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedHandle& fidl_type::coded_handle() const {
return *static_cast<const FidlCodedHandle*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedString& fidl_type::coded_string() const {
return *static_cast<const FidlCodedString*>(this);
}
__ALWAYS_INLINE constexpr const FidlCodedVector& fidl_type::coded_vector() const {
return *static_cast<const FidlCodedVector*>(this);
}
#endif // __cplusplus
extern const struct FidlCodedPrimitive fidl_internal_kBoolTable;
extern const struct FidlCodedPrimitive fidl_internal_kInt8Table;
extern const struct FidlCodedPrimitive fidl_internal_kInt16Table;
extern const struct FidlCodedPrimitive fidl_internal_kInt32Table;
extern const struct FidlCodedPrimitive fidl_internal_kInt64Table;
extern const struct FidlCodedPrimitive fidl_internal_kUint8Table;
extern const struct FidlCodedPrimitive fidl_internal_kUint16Table;
extern const struct FidlCodedPrimitive fidl_internal_kUint32Table;
extern const struct FidlCodedPrimitive fidl_internal_kUint64Table;
extern const struct FidlCodedPrimitive fidl_internal_kFloat32Table;
extern const struct FidlCodedPrimitive fidl_internal_kFloat64Table;
extern const struct FidlCodedEnum fidl_internal_kFrameworkErrTable;
// Backwards-compatible names
typedef struct FidlCodedUnion FidlCodedXUnion;
typedef struct FidlUnionField FidlXUnionField;
__END_CDECLS
#ifdef __cplusplus
// All the data in coding tables should be pure data.
static_assert(std::is_standard_layout<FidlTypeTag>::value, "");
static_assert(std::is_standard_layout<FidlStructField>::value, "");
static_assert(std::is_standard_layout<FidlTableField>::value, "");
static_assert(std::is_standard_layout<FidlCodedStruct>::value, "");
static_assert(std::is_standard_layout<FidlCodedStructPointer>::value, "");
static_assert(std::is_standard_layout<FidlCodedUnion>::value, "");
static_assert(std::is_standard_layout<FidlCodedArray>::value, "");
static_assert(std::is_standard_layout<FidlCodedVector>::value, "");
static_assert(std::is_standard_layout<FidlCodedString>::value, "");
static_assert(std::is_standard_layout<FidlCodedHandle>::value, "");
static_assert(std::is_standard_layout<FidlStructElement>::value, "");
#endif // __cplusplus
static_assert(offsetof(struct FidlCodedStruct, tag) == 0, "");
static_assert(offsetof(struct FidlCodedStructPointer, tag) == 0, "");
static_assert(offsetof(struct FidlCodedUnion, tag) == 0, "");
static_assert(offsetof(struct FidlCodedArray, tag) == 0, "");
static_assert(offsetof(struct FidlCodedVector, tag) == 0, "");
static_assert(offsetof(struct FidlCodedString, tag) == 0, "");
static_assert(offsetof(struct FidlCodedHandle, tag) == 0, "");
// Take caution when increasing the size numbers below. While they
// can be changed as needed when the structure evolves, these growing
// has a large impact on binary size and memory footprint.
static_assert(sizeof(struct FidlCodedPrimitive) == 2, "");
static_assert(sizeof(struct FidlCodedEnum) == 24, "");
static_assert(sizeof(struct FidlCodedBits) == 24, "");
static_assert(sizeof(struct FidlCodedStruct) == 24, "");
static_assert(sizeof(struct FidlCodedStructPointer) == 16, "");
static_assert(sizeof(struct FidlCodedUnion) == 24, "");
static_assert(sizeof(struct FidlCodedArray) == 16, "");
static_assert(sizeof(struct FidlCodedVector) == 24, "");
static_assert(sizeof(struct FidlCodedString) == 8, "");
static_assert(sizeof(struct FidlCodedHandle) == 12, "");
static_assert(sizeof(struct FidlStructField) == 16, "");
static_assert(sizeof(struct FidlTableField) == 16, "");
static_assert(sizeof(struct FidlUnionField) == 8, "");
static_assert(sizeof(struct FidlStructElement) == 16, "");
#endif // LIB_FIDL_INTERNAL_H_