// 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 <stdalign.h> #include <stdint.h> #include <stdlib.h> #include <lib/fidl/internal.h> #include <zircon/assert.h> #include <zircon/compiler.h> #ifdef __Fuchsia__ #include <zircon/syscalls.h> #endif #include "buffer_walker.h" #include <stdio.h> // TODO(kulakowski) Design zx_status_t error values. namespace { class FidlEncoder final : public fidl::internal::BufferWalker<FidlEncoder, true, true> { typedef fidl::internal::BufferWalker<FidlEncoder, true, true> Super; public: FidlEncoder(const fidl_type_t* type, void* bytes, uint32_t num_bytes, zx_handle_t* handles, uint32_t num_handles, uint32_t* out_actual_handles, const char** out_error_msg) : Super(type), bytes_(static_cast<uint8_t*>(bytes)), num_bytes_(num_bytes), handles_(handles), num_handles_(num_handles), out_actual_handles_(out_actual_handles), out_error_msg_(out_error_msg) {} void Walk() { if (handles_ == nullptr && num_handles_ != 0u) { SetError("Cannot provide non-zero handle count and null handle pointer"); return; } if (out_actual_handles_ == nullptr) { SetError("Cannot encode with null out_actual_handles"); return; } Super::Walk(); if (status_ == ZX_OK) { *out_actual_handles_ = handle_idx(); } } uint8_t* bytes() const { return bytes_; } uint32_t num_bytes() const { return num_bytes_; } uint32_t num_handles() const { return num_handles_; } bool ValidateOutOfLineStorageClaim(const void* a, const void* b) { return a == b; } void UnclaimedHandle(zx_handle_t* out_handle) { #ifdef __Fuchsia__ // Return value intentionally ignored: this is best-effort cleanup. zx_handle_close(*out_handle); #endif } void ClaimedHandle(zx_handle_t* out_handle, uint32_t idx) { assert(out_handle != nullptr); handles_[idx] = *out_handle; *out_handle = FIDL_HANDLE_PRESENT; } PointerState GetPointerState(const void* ptr) const { return *static_cast<const uintptr_t*>(ptr) == 0 ? PointerState::ABSENT : PointerState::PRESENT; } HandleState GetHandleState(zx_handle_t p) const { return p == ZX_HANDLE_INVALID ? HandleState::ABSENT : HandleState::PRESENT; } template <class T> void UpdatePointer(T** p, T* v) { assert(*p == v); assert(v != nullptr); *p = reinterpret_cast<T*>(FIDL_ALLOC_PRESENT); } void SetError(const char* error_msg) { if (status_ != ZX_OK) { return; } status_ = ZX_ERR_INVALID_ARGS; if (out_error_msg_ != nullptr) { *out_error_msg_ = error_msg; } #ifdef __Fuchsia__ if (handles_) { // Return value intentionally ignored: this is best-effort cleanup. zx_handle_close_many(handles_, num_handles()); } #endif } zx_status_t status() const { return status_; } private: // Message state passed in to the constructor. uint8_t* const bytes_; const uint32_t num_bytes_; zx_handle_t* const handles_; const uint32_t num_handles_; uint32_t* const out_actual_handles_; const char** const out_error_msg_; zx_status_t status_ = ZX_OK; }; } // namespace zx_status_t fidl_encode(const fidl_type_t* type, void* bytes, uint32_t num_bytes, zx_handle_t* handles, uint32_t max_handles, uint32_t* out_actual_handles, const char** out_error_msg) { FidlEncoder encoder(type, bytes, num_bytes, handles, max_handles, out_actual_handles, out_error_msg); encoder.Walk(); return encoder.status(); } zx_status_t fidl_encode_msg(const fidl_type_t* type, fidl_msg_t* msg, uint32_t* out_actual_handles, const char** out_error_msg) { return fidl_encode(type, msg->bytes, msg->num_bytes, msg->handles, msg->num_handles, out_actual_handles, out_error_msg); }