1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <lib/fidl/coding.h>
6 
7 #include <stdalign.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 
11 #include <lib/fidl/internal.h>
12 #include <zircon/assert.h>
13 #include <zircon/compiler.h>
14 
15 #ifdef __Fuchsia__
16 #include <zircon/syscalls.h>
17 #endif
18 
19 #include "buffer_walker.h"
20 
21 #include <stdio.h>
22 
23 // TODO(kulakowski) Design zx_status_t error values.
24 
25 namespace {
26 
27 class FidlEncoder final : public fidl::internal::BufferWalker<FidlEncoder, true, true> {
28     typedef fidl::internal::BufferWalker<FidlEncoder, true, true> Super;
29 
30 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)31     FidlEncoder(const fidl_type_t* type, void* bytes, uint32_t num_bytes, zx_handle_t* handles,
32                 uint32_t num_handles, uint32_t* out_actual_handles, const char** out_error_msg)
33         : Super(type), bytes_(static_cast<uint8_t*>(bytes)), num_bytes_(num_bytes),
34           handles_(handles), num_handles_(num_handles), out_actual_handles_(out_actual_handles),
35           out_error_msg_(out_error_msg) {}
36 
Walk()37     void Walk() {
38         if (handles_ == nullptr && num_handles_ != 0u) {
39             SetError("Cannot provide non-zero handle count and null handle pointer");
40             return;
41         }
42         if (out_actual_handles_ == nullptr) {
43             SetError("Cannot encode with null out_actual_handles");
44             return;
45         }
46         Super::Walk();
47         if (status_ == ZX_OK) {
48             *out_actual_handles_ = handle_idx();
49         }
50     }
51 
bytes() const52     uint8_t* bytes() const { return bytes_; }
num_bytes() const53     uint32_t num_bytes() const { return num_bytes_; }
num_handles() const54     uint32_t num_handles() const { return num_handles_; }
55 
ValidateOutOfLineStorageClaim(const void * a,const void * b)56     bool ValidateOutOfLineStorageClaim(const void* a, const void* b) {
57         return a == b;
58     }
59 
UnclaimedHandle(zx_handle_t * out_handle)60     void UnclaimedHandle(zx_handle_t* out_handle) {
61 #ifdef __Fuchsia__
62         // Return value intentionally ignored: this is best-effort cleanup.
63         zx_handle_close(*out_handle);
64 #endif
65     }
ClaimedHandle(zx_handle_t * out_handle,uint32_t idx)66     void ClaimedHandle(zx_handle_t* out_handle, uint32_t idx) {
67         assert(out_handle != nullptr);
68         handles_[idx] = *out_handle;
69         *out_handle = FIDL_HANDLE_PRESENT;
70     }
71 
GetPointerState(const void * ptr) const72     PointerState GetPointerState(const void* ptr) const {
73         return *static_cast<const uintptr_t*>(ptr) == 0
74                    ? PointerState::ABSENT
75                    : PointerState::PRESENT;
76     }
GetHandleState(zx_handle_t p) const77     HandleState GetHandleState(zx_handle_t p) const {
78         return p == ZX_HANDLE_INVALID
79                    ? HandleState::ABSENT
80                    : HandleState::PRESENT;
81     }
82 
83     template <class T>
UpdatePointer(T ** p,T * v)84     void UpdatePointer(T** p, T* v) {
85         assert(*p == v);
86         assert(v != nullptr);
87         *p = reinterpret_cast<T*>(FIDL_ALLOC_PRESENT);
88     }
89 
SetError(const char * error_msg)90     void SetError(const char* error_msg) {
91         if (status_ != ZX_OK) {
92             return;
93         }
94         status_ = ZX_ERR_INVALID_ARGS;
95         if (out_error_msg_ != nullptr) {
96             *out_error_msg_ = error_msg;
97         }
98 #ifdef __Fuchsia__
99         if (handles_) {
100             // Return value intentionally ignored: this is best-effort cleanup.
101             zx_handle_close_many(handles_, num_handles());
102         }
103 #endif
104     }
105 
status() const106     zx_status_t status() const { return status_; }
107 
108 private:
109     // Message state passed in to the constructor.
110     uint8_t* const bytes_;
111     const uint32_t num_bytes_;
112     zx_handle_t* const handles_;
113     const uint32_t num_handles_;
114     uint32_t* const out_actual_handles_;
115     const char** const out_error_msg_;
116     zx_status_t status_ = ZX_OK;
117 };
118 
119 } // namespace
120 
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)121 zx_status_t fidl_encode(const fidl_type_t* type, void* bytes, uint32_t num_bytes,
122                         zx_handle_t* handles, uint32_t max_handles, uint32_t* out_actual_handles,
123                         const char** out_error_msg) {
124     FidlEncoder encoder(type, bytes, num_bytes, handles, max_handles, out_actual_handles,
125                         out_error_msg);
126     encoder.Walk();
127     return encoder.status();
128 }
129 
fidl_encode_msg(const fidl_type_t * type,fidl_msg_t * msg,uint32_t * out_actual_handles,const char ** out_error_msg)130 zx_status_t fidl_encode_msg(const fidl_type_t* type, fidl_msg_t* msg,
131                             uint32_t* out_actual_handles, const char** out_error_msg) {
132     return fidl_encode(type, msg->bytes, msg->num_bytes, msg->handles, msg->num_handles,
133                        out_actual_handles, out_error_msg);
134 }
135