| // 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. |
| |
| #include <lib/fidl/internal.h> |
| |
| #ifdef __Fuchsia__ |
| #include <zircon/syscalls.h> |
| #include <zircon/syscalls/object.h> |
| #endif |
| |
| // Coding tables for primitives are predefined and interned here. |
| // This file must be a .c to guarantee that these types are stored directly in |
| // .rodata, rather than requiring global ctors to have been run (https://fxbug.dev/42115897). |
| const struct FidlCodedPrimitive fidl_internal_kBoolTable = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Bool}; |
| const struct FidlCodedPrimitive fidl_internal_kInt8Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Int8}; |
| const struct FidlCodedPrimitive fidl_internal_kInt16Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Int16}; |
| const struct FidlCodedPrimitive fidl_internal_kInt32Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Int32}; |
| const struct FidlCodedPrimitive fidl_internal_kInt64Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Int64}; |
| const struct FidlCodedPrimitive fidl_internal_kUint8Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Uint8}; |
| const struct FidlCodedPrimitive fidl_internal_kUint16Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Uint16}; |
| const struct FidlCodedPrimitive fidl_internal_kUint32Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Uint32}; |
| const struct FidlCodedPrimitive fidl_internal_kUint64Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Uint64}; |
| const struct FidlCodedPrimitive fidl_internal_kFloat32Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Float32}; |
| const struct FidlCodedPrimitive fidl_internal_kFloat64Table = { |
| .tag = kFidlTypePrimitive, .type = kFidlCodedPrimitiveSubtype_Float64}; |
| |
| static bool EnumValidatorForFrameworkErr(uint64_t v) { |
| switch (v) { |
| // C99 6.3.1.3(2): conversion to unsigned should produce the two's |
| // complement. |
| case (uint64_t)FIDL_FRAMEWORK_ERR_UNKNOWN_METHOD: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| const struct FidlCodedEnum fidl_internal_kFrameworkErrTable = { |
| .tag = kFidlTypeEnum, |
| .underlying_type = kFidlCodedPrimitiveSubtype_Int32, |
| .strictness = kFidlStrictness_Strict, |
| .validate = &EnumValidatorForFrameworkErr, |
| .name = "fidl/FrameworkErr"}; |
| |
| zx_rights_t subtract_rights(zx_rights_t minuend, zx_rights_t subtrahend) { |
| return minuend & ~subtrahend; |
| } |
| |
| zx_status_t FidlEnsureHandleRights(zx_handle_t* handle_ptr, zx_obj_type_t actual_type, |
| zx_obj_type_t actual_rights, zx_obj_type_t required_object_type, |
| zx_rights_t required_rights, const char** error) { |
| // Note: objects returned from the kernel should never have type |
| // ZX_OBJ_TYPE_NONE, however this is used for backwards compatibility in |
| // some places. |
| if (unlikely(required_object_type != actual_type && required_object_type != ZX_OBJ_TYPE_NONE && |
| actual_type != ZX_OBJ_TYPE_NONE)) { |
| #ifdef __Fuchsia__ |
| zx_handle_close(*handle_ptr); |
| #endif |
| *handle_ptr = ZX_HANDLE_INVALID; |
| if (error) { |
| *error = "object type does not match expected type"; |
| } |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| // Special case: ZX_HANDLE_SAME_RIGHTS allows all handles through unchanged. |
| // Note: objects returned from the kernel should never have rights |
| // ZX_RIGHT_SAME_RIGHTS, however this is used for backwards compatibility |
| // in some places. |
| if (required_rights == ZX_RIGHT_SAME_RIGHTS || actual_rights == ZX_RIGHT_SAME_RIGHTS) { |
| return ZX_OK; |
| } |
| |
| // Check that |actual_rights| contain all of the |required_rights|. |
| if (unlikely(subtract_rights(required_rights, actual_rights) != 0)) { |
| #ifdef __Fuchsia__ |
| zx_handle_close(*handle_ptr); |
| #endif |
| *handle_ptr = ZX_HANDLE_INVALID; |
| if (error) { |
| *error = "missing required rights"; |
| } |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| // Check if |actual_rights| has more rights than |required_rights|. |
| // If so, the rights need to be reduced. |
| if (unlikely(subtract_rights(actual_rights, required_rights))) { |
| #ifdef __Fuchsia__ |
| zx_status_t status = zx_handle_replace(*handle_ptr, required_rights, handle_ptr); |
| if (unlikely(status != ZX_OK)) { |
| if (error) |
| *error = "zx_handle_replace failed"; |
| return status; |
| } |
| #else |
| ZX_PANIC("zx_handle_replace only supported on Fuchsia"); |
| #endif |
| } |
| return ZX_OK; |
| } |
| |
| zx_status_t FidlEnsureActualHandleRights(zx_handle_t* handle_ptr, |
| zx_obj_type_t required_object_type, |
| zx_rights_t required_rights, const char** error) { |
| #ifdef __Fuchsia__ |
| zx_info_handle_basic_t info; |
| zx_status_t status = |
| zx_object_get_info(*handle_ptr, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), NULL, NULL); |
| if (status != ZX_OK) { |
| zx_handle_close(*handle_ptr); |
| *handle_ptr = ZX_HANDLE_INVALID; |
| *error = "zx_object_get_info failed"; |
| return status; |
| } |
| return FidlEnsureHandleRights(handle_ptr, info.type, info.rights, required_object_type, |
| required_rights, error); |
| #else |
| ZX_PANIC("FidlEnsureActualHandleRights only supported on Fuchsia"); |
| #endif |
| } |
| |
| zx_status_t FidlHandleDispositionsToHandleInfos(zx_handle_disposition_t* handle_dispositions, |
| zx_handle_info_t* handle_infos, |
| uint32_t num_handles) { |
| for (size_t i = 0; i < num_handles; i++) { |
| zx_handle_disposition_t* handle_disposition = &handle_dispositions[i]; |
| if (handle_disposition->operation != ZX_HANDLE_OP_MOVE) { |
| FidlHandleDispositionCloseMany(handle_dispositions, num_handles); |
| FidlHandleInfoCloseMany(handle_infos, i); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (handle_disposition->result != ZX_OK) { |
| FidlHandleDispositionCloseMany(handle_dispositions, num_handles); |
| FidlHandleInfoCloseMany(handle_infos, i); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| #ifdef __Fuchsia__ |
| zx_status_t status = FidlEnsureActualHandleRights( |
| &handle_disposition->handle, handle_disposition->type, handle_disposition->rights, NULL); |
| if (status != ZX_OK) { |
| FidlHandleDispositionCloseMany(handle_dispositions, num_handles); |
| FidlHandleInfoCloseMany(handle_infos, i); |
| return status; |
| } |
| handle_infos[i].handle = handle_disposition->handle; |
| handle_infos[i].type = handle_disposition->type; |
| handle_infos[i].rights = handle_disposition->rights; |
| handle_disposition->handle = ZX_HANDLE_INVALID; |
| #else |
| ZX_PANIC("zx_object_get_info unsupported on host"); |
| #endif |
| } |
| return ZX_OK; |
| } |