| // 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. |
| |
| #include <lib/fidl/coding.h> |
| #include <lib/fidl/internal.h> |
| #include <lib/fidl/visitor.h> |
| #include <lib/fidl/walker.h> |
| #include <lib/stdcompat/variant.h> |
| #include <stdalign.h> |
| #include <zircon/assert.h> |
| #include <zircon/compiler.h> |
| |
| #include <cstdint> |
| #include <cstdlib> |
| #include <cstring> |
| |
| #ifdef __Fuchsia__ |
| #include <zircon/syscalls.h> |
| #endif |
| |
| // TODO(kulakowski) Design zx_status_t error values. |
| |
| namespace { |
| |
| template <typename Byte> |
| struct DecodingPosition { |
| Byte* addr; |
| DecodingPosition operator+(uint32_t size) const { return DecodingPosition{addr + size}; } |
| DecodingPosition& operator+=(uint32_t size) { |
| addr += size; |
| return *this; |
| } |
| template <typename T, typename U = std::conditional_t<std::is_const<Byte>::value, const T, T>> |
| constexpr U* Get() const { |
| return reinterpret_cast<U*>(addr); |
| } |
| }; |
| |
| struct EnvelopeCheckpoint { |
| uint32_t num_bytes; |
| uint32_t num_handles; |
| }; |
| |
| zx_status_t channel_decode_process_handle(fidl_handle_t* handle, |
| fidl::internal::HandleAttributes attr, |
| uint32_t metadata_index, const void* metadata_array, |
| const char** error) { |
| fidl_channel_handle_metadata_t v = |
| reinterpret_cast<const fidl_channel_handle_metadata_t*>(metadata_array)[metadata_index]; |
| return FidlEnsureHandleRights(handle, v.obj_type, v.rights, attr.obj_type, attr.rights, error); |
| } |
| const fidl::internal::CodingConfig default_channel_encoding_configuration{ |
| .decode_process_handle = channel_decode_process_handle, |
| }; |
| |
| constexpr zx_rights_t subtract_rights(zx_rights_t minuend, zx_rights_t subtrahend) { |
| return minuend & ~subtrahend; |
| } |
| static_assert(subtract_rights(0b011, 0b101) == 0b010, "ensure rights subtraction works correctly"); |
| |
| enum class Mode { Decode, Validate }; |
| |
| template <Mode mode, typename T, typename U> |
| void AssignInDecode(T* ptr, U value) { |
| static_assert(mode == Mode::Decode, "only assign if decode"); |
| *ptr = value; |
| } |
| |
| template <Mode mode, typename T, typename U> |
| void AssignInDecode(const T* ptr, U value) { |
| static_assert(mode == Mode::Validate, "don't assign if validate"); |
| // nothing in validate mode |
| } |
| |
| template <Mode mode> |
| zx_status_t DecodeProcessHandle(const fidl::internal::CodingConfig& encoding_configuration, |
| fidl_handle_t* handle, fidl::internal::HandleAttributes attr, |
| uint32_t metadata_index, const void* metadata_array, |
| const char** error) { |
| static_assert(mode == Mode::Decode, "process handles during decode"); |
| if (!encoding_configuration.decode_process_handle) { |
| return ZX_OK; |
| } |
| return encoding_configuration.decode_process_handle(handle, attr, metadata_index, metadata_array, |
| error); |
| } |
| |
| template <Mode mode> |
| zx_status_t DecodeProcessHandle(const fidl::internal::CodingConfig& encoding_configuration, |
| const fidl_handle_t* handle, fidl::internal::HandleAttributes attr, |
| uint32_t metadata_index, const void* metadata_array, |
| const char** error) { |
| static_assert(mode == Mode::Validate, "never used during validate"); |
| __builtin_unreachable(); |
| } |
| |
| void ConvertEnvelopeToDecodedRepresentation(const void* bytes_base_ptr, |
| fidl_envelope_v2_t envelope_copy, |
| const fidl_envelope_v2_t* envelope_ptr) { |
| // No conversion is needed for v2 validate. |
| } |
| void ConvertEnvelopeToDecodedRepresentation(const void* bytes_base_ptr, |
| fidl_envelope_v2_t envelope_copy, |
| fidl_envelope_v2_t* envelope_ptr) { |
| if ((envelope_copy.flags & FIDL_ENVELOPE_FLAGS_INLINING_MASK) != 0) { |
| fidl_envelope_v2_unknown_data_t unknown_data_envelope = { |
| .num_handles = envelope_copy.num_handles, |
| .flags = envelope_copy.flags, |
| }; |
| memcpy(unknown_data_envelope.inline_value, envelope_ptr->inline_value, |
| sizeof(envelope_ptr->inline_value)); |
| memcpy(envelope_ptr, &unknown_data_envelope, sizeof(unknown_data_envelope)); |
| return; |
| } |
| |
| uintptr_t data_ptr = *reinterpret_cast<uintptr_t*>(envelope_ptr); |
| uintptr_t base_ptr = reinterpret_cast<uintptr_t>(bytes_base_ptr); |
| uintptr_t offset = data_ptr - base_ptr; |
| ZX_ASSERT(offset <= std::numeric_limits<uint16_t>::max()); |
| ZX_ASSERT(envelope_copy.num_bytes <= std::numeric_limits<uint16_t>::max()); |
| fidl_envelope_v2_unknown_data_t unknown_data_envelope = { |
| .out_of_line = |
| { |
| .num_bytes = static_cast<uint16_t>(envelope_copy.num_bytes), |
| .offset = static_cast<uint16_t>(offset), |
| }, |
| .num_handles = envelope_copy.num_handles, |
| .flags = envelope_copy.flags, |
| }; |
| memcpy(envelope_ptr, &unknown_data_envelope, sizeof(unknown_data_envelope)); |
| } |
| |
| template <FidlWireFormatVersion WireFormatVersion, typename Byte> |
| using BaseVisitor = |
| fidl::Visitor<WireFormatVersion, |
| std::conditional_t<std::is_const<Byte>::value, fidl::NonMutatingVisitorTrait, |
| fidl::MutatingVisitorTrait>, |
| DecodingPosition<Byte>, EnvelopeCheckpoint>; |
| |
| template <Mode mode, FidlWireFormatVersion WireFormatVersion, typename Byte> |
| class FidlDecoder final : public BaseVisitor<WireFormatVersion, Byte> { |
| public: |
| FidlDecoder(const fidl::internal::CodingConfig& encoding_configuration, Byte* bytes, |
| uint32_t num_bytes, const fidl_handle_t* handles, const void* handle_metadata, |
| uint32_t num_handles, uint32_t next_out_of_line, const char** out_error_msg, |
| bool hlcpp_mode) |
| : encoding_configuration_(encoding_configuration), |
| bytes_(bytes), |
| num_bytes_(num_bytes), |
| handles_(handles), |
| handle_metadata_(handle_metadata), |
| num_handles_(num_handles), |
| next_out_of_line_(next_out_of_line), |
| out_error_msg_(out_error_msg), |
| hlcpp_mode_(hlcpp_mode) {} |
| using Position = typename BaseVisitor<WireFormatVersion, Byte>::Position; |
| using Status = typename BaseVisitor<WireFormatVersion, Byte>::Status; |
| using PointeeType = typename BaseVisitor<WireFormatVersion, Byte>::PointeeType; |
| using ObjectPointerPointer = typename BaseVisitor<WireFormatVersion, Byte>::ObjectPointerPointer; |
| using HandlePointer = typename BaseVisitor<WireFormatVersion, Byte>::HandlePointer; |
| using CountPointer = typename BaseVisitor<WireFormatVersion, Byte>::CountPointer; |
| using EnvelopeType = typename BaseVisitor<WireFormatVersion, Byte>::EnvelopeType; |
| using EnvelopePointer = typename BaseVisitor<WireFormatVersion, Byte>::EnvelopePointer; |
| |
| static constexpr bool kOnlyWalkResources = false; |
| static constexpr bool kContinueAfterConstraintViolation = false; |
| static constexpr bool kValidateEnvelopeInlineBit = true; |
| |
| Status VisitAbsentPointerInNonNullableCollection(ObjectPointerPointer object_ptr_ptr) { |
| SetError("absent pointer disallowed in non-nullable collection"); |
| return Status::kConstraintViolationError; |
| } |
| |
| Status VisitPointer(Position ptr_position, PointeeType pointee_type, |
| ObjectPointerPointer object_ptr_ptr, uint32_t inline_size, |
| FidlMemcpyCompatibility pointee_memcpy_compatibility, |
| Position* out_position) { |
| if (unlikely(pointee_type != PointeeType::kEnvelope && |
| reinterpret_cast<uintptr_t>(*object_ptr_ptr) != FIDL_ALLOC_PRESENT)) { |
| SetError("invalid presence marker"); |
| return Status::kMemoryError; |
| } |
| uint32_t new_offset; |
| if (unlikely(!FidlAddOutOfLine(next_out_of_line_, inline_size, &new_offset))) { |
| SetError("overflow updating out-of-line offset"); |
| return Status::kMemoryError; |
| } |
| if (unlikely(new_offset > num_bytes_)) { |
| SetError("message tried to access more than provided number of bytes"); |
| return Status::kMemoryError; |
| } |
| { |
| if (inline_size % FIDL_ALIGNMENT != 0) { |
| // Validate the last 8-byte block. |
| const uint64_t* block_end = reinterpret_cast<const uint64_t*>(&bytes_[new_offset]) - 1; |
| uint64_t padding_len = new_offset - next_out_of_line_ - inline_size; |
| uint64_t padding_mask = ~0ull << (64 - 8 * padding_len); |
| auto status = ValidatePadding(block_end, padding_mask); |
| if (status != Status::kSuccess) { |
| return status; |
| } |
| } |
| } |
| if (unlikely(pointee_type == PointeeType::kString)) { |
| bool valid = fidl_validate_string(reinterpret_cast<const char*>(&bytes_[next_out_of_line_]), |
| inline_size); |
| if (!valid) { |
| SetError("encountered invalid UTF8 string"); |
| return Status::kConstraintViolationError; |
| } |
| } |
| *out_position = Position{bytes_ + next_out_of_line_}; |
| AssignInDecode<mode>( |
| object_ptr_ptr, |
| reinterpret_cast<std::remove_pointer_t<ObjectPointerPointer>>(&bytes_[next_out_of_line_])); |
| |
| next_out_of_line_ = new_offset; |
| return Status::kSuccess; |
| } |
| |
| Status VisitHandle(Position handle_position, HandlePointer handle, |
| zx_rights_t required_handle_rights, zx_obj_type_t required_handle_subtype) { |
| if (unlikely(*handle != FIDL_HANDLE_PRESENT)) { |
| SetError("message tried to decode a garbage handle"); |
| return Status::kConstraintViolationError; |
| } |
| if (unlikely(handle_idx_ == num_handles_)) { |
| SetError("message decoded too many handles"); |
| return Status::kConstraintViolationError; |
| } |
| |
| if (mode == Mode::Validate) { |
| handle_idx_++; |
| return Status::kSuccess; |
| } |
| |
| if (unlikely(handles_[handle_idx_] == ZX_HANDLE_INVALID)) { |
| SetError("invalid handle detected in handle table"); |
| return Status::kConstraintViolationError; |
| } |
| if (mode == Mode::Decode) { |
| AssignInDecode<mode>(handle, handles_[handle_idx_]); |
| |
| fidl::internal::HandleAttributes attr = { |
| .obj_type = required_handle_subtype, |
| .rights = required_handle_rights, |
| }; |
| const char* error; |
| zx_status_t status = DecodeProcessHandle<mode>(encoding_configuration_, handle, attr, |
| handle_idx_, handle_metadata_, &error); |
| if (status != ZX_OK) { |
| SetError(error); |
| return Status::kConstraintViolationError; |
| } |
| } |
| handle_idx_++; |
| return Status::kSuccess; |
| } |
| |
| Status VisitVectorOrStringCount(CountPointer ptr) { return Status::kSuccess; } |
| |
| template <typename MaskType> |
| Status VisitInternalPadding(Position padding_position, MaskType mask) { |
| return ValidatePadding(padding_position.template Get<const MaskType>(), mask); |
| } |
| |
| EnvelopeCheckpoint EnterEnvelope() { |
| return { |
| .num_bytes = next_out_of_line_, |
| .num_handles = handle_idx_, |
| }; |
| } |
| |
| Status LeaveEnvelope(EnvelopeType in_envelope, EnvelopePointer out_envelope, |
| EnvelopeCheckpoint prev_checkpoint) { |
| // Now that the envelope has been consumed, check the correctness of the envelope header. |
| uint32_t num_bytes = next_out_of_line_ - prev_checkpoint.num_bytes; |
| uint32_t num_handles = handle_idx_ - prev_checkpoint.num_handles; |
| if (unlikely(in_envelope.num_bytes != num_bytes)) { |
| SetError("Envelope num_bytes was mis-sized"); |
| return Status::kConstraintViolationError; |
| } |
| if (unlikely(in_envelope.num_handles != num_handles)) { |
| SetError("Envelope num_handles was mis-sized"); |
| return Status::kConstraintViolationError; |
| } |
| return Status::kSuccess; |
| } |
| |
| Status LeaveInlinedEnvelope(EnvelopeType in_envelope, EnvelopePointer out_envelope, |
| EnvelopeCheckpoint prev_checkpoint) { |
| // Now that the envelope has been consumed, check the correctness of the envelope header. |
| uint32_t num_handles = handle_idx_ - prev_checkpoint.num_handles; |
| if (unlikely(in_envelope.num_handles != num_handles)) { |
| SetError("Envelope num_handles was mis-sized"); |
| return Status::kConstraintViolationError; |
| } |
| return Status::kSuccess; |
| } |
| |
| Status VisitUnknownEnvelope(EnvelopeType envelope_copy, EnvelopePointer envelope_ptr, |
| FidlIsResource is_resource) { |
| if (mode == Mode::Validate) { |
| handle_idx_ += envelope_copy.num_handles; |
| return Status::kSuccess; |
| } |
| |
| if (hlcpp_mode_) { |
| ConvertEnvelopeToDecodedRepresentation(bytes_, envelope_copy, envelope_ptr); |
| } |
| |
| // If we do not have the coding table for this payload, |
| // treat it as unknown and close its contained handles |
| if (unlikely(envelope_copy.num_handles > 0)) { |
| uint32_t total_unknown_handles; |
| if (add_overflow(unknown_handle_idx_, envelope_copy.num_handles, &total_unknown_handles)) { |
| SetError("number of unknown handles overflows"); |
| return Status::kConstraintViolationError; |
| } |
| if (total_unknown_handles > ZX_CHANNEL_MAX_MSG_HANDLES) { |
| SetError("number of unknown handles exceeds unknown handle array size"); |
| return Status::kConstraintViolationError; |
| } |
| uint32_t end_incoming_handle; |
| if (add_overflow(handle_idx_, envelope_copy.num_handles, &end_incoming_handle)) { |
| SetError("number of incoming handles overflows"); |
| return Status::kConstraintViolationError; |
| } |
| if (end_incoming_handle > num_handles_) { |
| SetError("number of incoming handles exceeds incoming handle array size"); |
| return Status::kConstraintViolationError; |
| } |
| // If hlcpp_mode_ is true, leave the unknown handles intact |
| // for something else to process (e.g. HLCPP Decode) |
| if (hlcpp_mode_ && is_resource == kFidlIsResource_Resource) { |
| handle_idx_ += envelope_copy.num_handles; |
| return Status::kSuccess; |
| } |
| memcpy(&unknown_handles_[unknown_handle_idx_], &handles_[handle_idx_], |
| envelope_copy.num_handles * sizeof(fidl_handle_t)); |
| handle_idx_ = end_incoming_handle; |
| unknown_handle_idx_ = total_unknown_handles; |
| } |
| 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_; } |
| |
| uint32_t unknown_handle_idx() const { return unknown_handle_idx_; } |
| |
| const fidl_handle_t* unknown_handles() const { return unknown_handles_; } |
| |
| private: |
| void SetError(const char* error) { |
| if (status_ != ZX_OK) { |
| return; |
| } |
| status_ = ZX_ERR_INVALID_ARGS; |
| if (!out_error_msg_) { |
| return; |
| } |
| *out_error_msg_ = error; |
| } |
| |
| template <typename MaskType> |
| Status ValidatePadding(const MaskType* padding_ptr, MaskType mask) { |
| if ((*padding_ptr & mask) != 0) { |
| SetError("non-zero padding bytes detected"); |
| return Status::kConstraintViolationError; |
| } |
| return Status::kSuccess; |
| } |
| |
| // Message state passed in to the constructor. |
| const fidl::internal::CodingConfig& encoding_configuration_; |
| Byte* const bytes_; |
| const uint32_t num_bytes_; |
| const fidl_handle_t* handles_; |
| const void* handle_metadata_; |
| const uint32_t num_handles_; |
| uint32_t next_out_of_line_; |
| const char** const out_error_msg_; |
| // HLCPP first uses FidlDecoder to do an in-place decode, then extracts data |
| // out into domain objects. Since HLCPP stores unknown handles |
| // (and LLCPP does not), this field allows HLCPP to use the decoder while |
| // keeping unknown handles in flexible resource unions intact. |
| bool hlcpp_mode_; |
| |
| // Decoder state |
| zx_status_t status_ = ZX_OK; |
| uint32_t handle_idx_ = 0; |
| uint32_t unknown_handle_idx_ = 0; |
| fidl_handle_t unknown_handles_[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| }; |
| |
| } // namespace |
| |
| namespace { |
| template <FidlWireFormatVersion WireFormatVersion> |
| zx_status_t fidl_decode_impl(const fidl::internal::CodingConfig& encoding_configuration, |
| const fidl_type_t* type, void* bytes, uint32_t num_bytes, |
| const fidl_handle_t* handles, const void* handle_metadata, |
| uint32_t num_handles, const char** out_error_msg, bool hlcpp_mode) { |
| auto drop_all_handles = [&]() { FidlHandleCloseMany(handles, num_handles); }; |
| auto set_error = [&out_error_msg](const char* msg) { |
| if (out_error_msg) |
| *out_error_msg = msg; |
| }; |
| if (unlikely(type == nullptr)) { |
| set_error("fidl type cannot be null"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (unlikely(handles == nullptr && num_handles != 0)) { |
| set_error("Cannot provide non-zero handle count and null handle pointer"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (unlikely(bytes == nullptr)) { |
| set_error("Cannot decode null bytes"); |
| drop_all_handles(); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (unlikely(!FidlIsAligned(reinterpret_cast<uint8_t*>(bytes)))) { |
| set_error("Bytes must be aligned to FIDL_ALIGNMENT"); |
| drop_all_handles(); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx_status_t status; |
| uint32_t primary_size; |
| uint32_t next_out_of_line; |
| if (unlikely((status = fidl::PrimaryObjectSize<WireFormatVersion>( |
| type, num_bytes, &primary_size, &next_out_of_line, out_error_msg)) != ZX_OK)) { |
| drop_all_handles(); |
| return status; |
| } |
| |
| uint8_t* b = reinterpret_cast<uint8_t*>(bytes); |
| for (uint32_t i = primary_size; i < next_out_of_line; i++) { |
| if (b[i] != 0) { |
| set_error("non-zero padding bytes detected"); |
| drop_all_handles(); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } |
| |
| FidlDecoder<Mode::Decode, WireFormatVersion, uint8_t> decoder( |
| encoding_configuration, b, num_bytes, handles, handle_metadata, num_handles, next_out_of_line, |
| out_error_msg, hlcpp_mode); |
| fidl::Walk<WireFormatVersion>(decoder, type, DecodingPosition<uint8_t>{b}); |
| |
| if (unlikely(decoder.status() != ZX_OK)) { |
| drop_all_handles(); |
| return decoder.status(); |
| } |
| if (unlikely(!decoder.DidConsumeAllBytes())) { |
| set_error("message did not decode all provided bytes"); |
| drop_all_handles(); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (unlikely(!decoder.DidConsumeAllHandles())) { |
| set_error("message did not decode all provided handles"); |
| drop_all_handles(); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| (void)FidlHandleCloseMany(decoder.unknown_handles(), decoder.unknown_handle_idx()); |
| return ZX_OK; |
| } |
| |
| template <FidlWireFormatVersion WireFormatVersion> |
| zx_status_t fidl_decode_impl_handle_info(const fidl::internal::CodingConfig& encoding_configuration, |
| const fidl_type_t* type, void* bytes, uint32_t num_bytes, |
| const zx_handle_info_t* handle_infos, uint32_t num_handles, |
| const char** out_error_msg, bool hlcpp_mode) { |
| if (!handle_infos) { |
| return fidl_decode_impl<WireFormatVersion>(encoding_configuration, type, bytes, num_bytes, |
| nullptr, nullptr, num_handles, out_error_msg, |
| hlcpp_mode); |
| } |
| fidl_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| fidl_channel_handle_metadata_t handle_metadata[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| for (uint32_t i = 0; i < num_handles; i++) { |
| handles[i] = handle_infos[i].handle; |
| handle_metadata[i] = { |
| .obj_type = handle_infos[i].type, |
| .rights = handle_infos[i].rights, |
| }; |
| } |
| return fidl_decode_impl<WireFormatVersion>(encoding_configuration, type, bytes, num_bytes, |
| handles, handle_metadata, num_handles, out_error_msg, |
| hlcpp_mode); |
| } |
| } // namespace |
| |
| zx_status_t internal__fidl_decode_etc_hlcpp__v2__may_break(const fidl_type_t* type, void* bytes, |
| uint32_t num_bytes, |
| const zx_handle_info_t* handle_infos, |
| uint32_t num_handle_infos, |
| const char** error_msg_out) { |
| return fidl_decode_impl_handle_info<FIDL_WIRE_FORMAT_VERSION_V2>( |
| default_channel_encoding_configuration, type, bytes, num_bytes, handle_infos, |
| num_handle_infos, error_msg_out, true); |
| } |
| |
| zx_status_t fidl_decode_etc(const fidl_type_t* type, void* bytes, uint32_t num_bytes, |
| const zx_handle_info_t* handle_infos, uint32_t num_handle_infos, |
| const char** error_msg_out) { |
| return fidl_decode_impl_handle_info<FIDL_WIRE_FORMAT_VERSION_V2>( |
| default_channel_encoding_configuration, type, bytes, num_bytes, handle_infos, |
| num_handle_infos, error_msg_out, false); |
| } |
| |
| zx_status_t fidl_decode_msg(const fidl_type_t* type, fidl_incoming_msg_t* msg, |
| const char** out_error_msg) { |
| zx_handle_info_t handle_infos[ZX_CHANNEL_MAX_MSG_HANDLES]; |
| fidl_channel_handle_metadata_t* metadata = |
| reinterpret_cast<fidl_channel_handle_metadata_t*>(msg->handle_metadata); |
| for (uint32_t i = 0; i < msg->num_handles; i++) { |
| handle_infos[i] = { |
| .handle = msg->handles[i], |
| .type = metadata[i].obj_type, |
| .rights = metadata[i].rights, |
| }; |
| msg->handles[i] = ZX_HANDLE_INVALID; |
| } |
| |
| uint8_t* trimmed_bytes; |
| uint32_t trimmed_num_bytes; |
| zx_status_t trim_status = ::fidl::internal::fidl_exclude_header_bytes( |
| msg->bytes, msg->num_bytes, &trimmed_bytes, &trimmed_num_bytes, out_error_msg); |
| if (unlikely(trim_status != ZX_OK)) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (trimmed_num_bytes == 0) { |
| return ZX_OK; |
| } |
| |
| return fidl_decode_etc(type, trimmed_bytes, trimmed_num_bytes, handle_infos, msg->num_handles, |
| out_error_msg); |
| } |
| |
| template <FidlWireFormatVersion WireFormatVersion> |
| zx_status_t fidl_validate_impl(const fidl_type_t* type, const void* bytes, uint32_t num_bytes, |
| uint32_t num_handles, const char** out_error_msg) { |
| auto set_error = [&out_error_msg](const char* msg) { |
| if (out_error_msg) |
| *out_error_msg = msg; |
| }; |
| if (unlikely(type == nullptr)) { |
| set_error("fidl type cannot be null"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (bytes == nullptr) { |
| set_error("Cannot validate null bytes"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| if (unlikely(!FidlIsAligned(reinterpret_cast<const uint8_t*>(bytes)))) { |
| set_error("Bytes must be aligned to FIDL_ALIGNMENT"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| zx_status_t status; |
| uint32_t primary_size; |
| uint32_t next_out_of_line; |
| if (unlikely((status = fidl::PrimaryObjectSize<WireFormatVersion>( |
| type, num_bytes, &primary_size, &next_out_of_line, out_error_msg)) != ZX_OK)) { |
| return status; |
| } |
| |
| const uint8_t* b = reinterpret_cast<const uint8_t*>(bytes); |
| for (uint32_t i = primary_size; i < next_out_of_line; i++) { |
| if (b[i] != 0) { |
| set_error("non-zero padding bytes detected"); |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } |
| |
| FidlDecoder<Mode::Validate, WireFormatVersion, const uint8_t> validator( |
| default_channel_encoding_configuration, b, num_bytes, nullptr, nullptr, num_handles, |
| next_out_of_line, out_error_msg, false); |
| fidl::Walk<WireFormatVersion>(validator, type, DecodingPosition<const uint8_t>{b}); |
| |
| 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(); |
| } |
| |
| zx_status_t internal__fidl_validate__v2__may_break(const fidl_type_t* type, const void* bytes, |
| uint32_t num_bytes, uint32_t num_handles, |
| const char** out_error_msg) { |
| return fidl_validate_impl<FIDL_WIRE_FORMAT_VERSION_V2>(type, bytes, num_bytes, num_handles, |
| out_error_msg); |
| } |