1 // Copyright 2018 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 <ldmsg/ldmsg.h>
6 
7 #include <string.h>
8 
9 static_assert(sizeof(ldmsg_req_t) == 1024,
10               "Loader service requests can be at most 1024 bytes.");
11 
FidlAlign(uint32_t offset)12 static uint64_t FidlAlign(uint32_t offset) {
13     const uint64_t alignment_mask = FIDL_ALIGNMENT - 1;
14     return (offset + alignment_mask) & ~alignment_mask;
15 }
16 
ldmsg_req_encode(ldmsg_req_t * req,size_t * req_len_out,const char * data,size_t len)17 zx_status_t ldmsg_req_encode(ldmsg_req_t* req, size_t* req_len_out,
18                              const char* data, size_t len) {
19     size_t offset = 0;
20     switch (req->header.ordinal) {
21     case LDMSG_OP_DONE:
22         *req_len_out = sizeof(fidl_message_header_t);
23         return ZX_OK;
24     case LDMSG_OP_CLONE:
25         *req_len_out = sizeof(fidl_message_header_t) + sizeof(ldmsg_clone_t);
26         req->clone.object = FIDL_HANDLE_PRESENT;
27         return ZX_OK;
28     case LDMSG_OP_LOAD_OBJECT:
29     case LDMSG_OP_LOAD_SCRIPT_INTERPRETER:
30     case LDMSG_OP_CONFIG:
31     case LDMSG_OP_DEBUG_LOAD_CONFIG:
32         offset = sizeof(fidl_string_t);
33         break;
34     case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK:
35         req->common.object = FIDL_HANDLE_PRESENT;
36         offset = sizeof(ldmsg_common_t);
37         break;
38     default:
39         return ZX_ERR_INVALID_ARGS;
40     }
41 
42     // Reserve one byte for the null terminator on the receiving side.
43     if (LDMSG_MAX_PAYLOAD - offset - 1 < len)
44         return ZX_ERR_OUT_OF_RANGE;
45 
46     req->common.string.size = len;
47     req->common.string.data = (char*) FIDL_ALLOC_PRESENT;
48     memcpy(req->data + offset, data, len);
49 
50     *req_len_out = FidlAlign(sizeof(fidl_message_header_t) + offset + len);
51     return ZX_OK;
52 }
53 
ldmsg_req_decode(ldmsg_req_t * req,size_t req_len,const char ** data_out,size_t * len_out)54 zx_status_t ldmsg_req_decode(ldmsg_req_t* req, size_t req_len,
55                              const char** data_out, size_t* len_out) {
56     size_t offset = 0;
57     switch (req->header.ordinal) {
58     case LDMSG_OP_DONE:
59         if (req_len != sizeof(fidl_message_header_t))
60             return ZX_ERR_INVALID_ARGS;
61         *data_out = 0;
62         *len_out = 0;
63         return ZX_OK;
64     case LDMSG_OP_CLONE:
65         if (req_len != sizeof(fidl_message_header_t) + sizeof(ldmsg_clone_t)
66             || req->clone.object != FIDL_HANDLE_PRESENT)
67             return ZX_ERR_INVALID_ARGS;
68         *data_out = 0;
69         *len_out = 0;
70         return ZX_OK;
71     case LDMSG_OP_LOAD_OBJECT:
72     case LDMSG_OP_LOAD_SCRIPT_INTERPRETER:
73     case LDMSG_OP_CONFIG:
74     case LDMSG_OP_DEBUG_LOAD_CONFIG:
75         if ((uintptr_t)req->common.string.data != FIDL_ALLOC_PRESENT)
76             return ZX_ERR_INVALID_ARGS;
77         offset = sizeof(fidl_string_t);
78         break;
79     case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK:
80         if ((uintptr_t)req->common.string.data != FIDL_ALLOC_PRESENT
81             || req->common.object != FIDL_HANDLE_PRESENT)
82             return ZX_ERR_INVALID_ARGS;
83         offset = sizeof(ldmsg_common_t);
84         break;
85     default:
86         return ZX_ERR_INVALID_ARGS;
87     }
88 
89     size_t size = req->common.string.size;
90     if (LDMSG_MAX_PAYLOAD - offset - 1 < size
91         || req_len != FidlAlign(sizeof(fidl_message_header_t) + offset + size))
92         return ZX_ERR_INVALID_ARGS;
93 
94     // Null terminate the string. The message isn't required to have a null
95     // terminated string, but we have enough space in our buffer for the null
96     // terminator and adding it makes life easier for our caller.
97     req->data[offset + size] = '\0';
98 
99     *data_out = req->data + offset;
100     *len_out = size;
101     return ZX_OK;
102 }
103 
ldmsg_rsp_get_size(ldmsg_rsp_t * rsp)104 size_t ldmsg_rsp_get_size(ldmsg_rsp_t* rsp) {
105     switch (rsp->header.ordinal) {
106     case LDMSG_OP_LOAD_OBJECT:
107     case LDMSG_OP_LOAD_SCRIPT_INTERPRETER:
108     case LDMSG_OP_DEBUG_LOAD_CONFIG:
109         return sizeof(ldmsg_rsp_t);
110     case LDMSG_OP_CONFIG:
111     case LDMSG_OP_CLONE:
112     case LDMSG_OP_DEBUG_PUBLISH_DATA_SINK:
113         return sizeof(ldmsg_rsp_t) - sizeof(zx_handle_t);
114     case LDMSG_OP_DONE:
115     default:
116         return 0;
117     }
118 }
119