1/*
2 * Copyright 2019, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <camkes/error.h>
14
15#define ERR_IF_BUFFER_LENGTH_EXCEEDED(size, curr_offset, desired, method,      \
16                                      param)                                   \
17  ERR_IF((desired) > ((size) - (curr_offset)), CAMKES_ERROR_HANDLER,           \
18         ((camkes_error_t){                                                    \
19             .type = CE_BUFFER_LENGTH_EXCEEDED,                                \
20             .instance = CAMKES_INSTANCE_NAME,                                 \
21             .interface = CAMKES_INTERFACE_NAME,                               \
22             .description =                                                    \
23                 "buffer exceeded while marshalling " param " in " method,     \
24             .current_length = curr_offset,                                    \
25             .target_length = curr_offset + desired,                           \
26         }),                                                                   \
27         ({ return UINT_MAX; }));
28
29#define ERR_IF_MALFORMED_RPC_PAYLOAD(size, curr_offset, desired, method,       \
30                                     param, cleanup_action)                    \
31  ERR_IF((desired) > ((size) - (curr_offset)), CAMKES_ERROR_HANDLER,           \
32         ((camkes_error_t){                                                    \
33             .type = CE_MALFORMED_RPC_PAYLOAD,                                 \
34             .instance = CAMKES_INSTANCE_NAME,                                 \
35             .interface = CAMKES_INTERFACE_NAME,                               \
36             .description =                                                    \
37                 "truncated message encountered while unmarshalling " param    \
38                 " for " method,                                               \
39             .length = (size),                                                 \
40             .current_index = (curr_offset) + (desired),                       \
41         }),                                                                   \
42         ({ cleanup_action; }));
43
44#define ERR_IF_MALFORMED_RPC_EXCESS_PAYLOAD(size, received_length, method,     \
45                                            cleanup_action)                    \
46  ERR_IF(ROUND_UP_UNSAFE((received_length), sizeof(seL4_Word)) != (size),      \
47         CAMKES_ERROR_HANDLER,                                                 \
48         ((camkes_error_t){                                                    \
49             .type = CE_MALFORMED_RPC_PAYLOAD,                                 \
50             .instance = CAMKES_INSTANCE_NAME,                                 \
51             .interface = CAMKES_INTERFACE_NAME,                               \
52             .description = "excess trailing bytes after unmarshalling "       \
53                            "parameters for " method,                          \
54             .length = (size),                                                 \
55             .current_index = (received_length),                               \
56         }),                                                                   \
57         ({ cleanup_action; }));
58
59#define ERR_IF_ALLOCATION_FAILURE(result, size, method, param, cleanup_action) \
60  ERR_IF((result) == NULL, CAMKES_ERROR_HANDLER,                               \
61         ((camkes_error_t){                                                    \
62             .type = CE_ALLOCATION_FAILURE,                                    \
63             .instance = CAMKES_INSTANCE_NAME,                                 \
64             .interface = CAMKES_INTERFACE_NAME,                               \
65             .description =                                                    \
66                 "out of memory while unmarshalling " param " for " method,    \
67             .alloc_bytes = (size),                                            \
68         }),                                                                   \
69         ({ cleanup_action; }));
70
71#define MARSHAL_ARRAY_PARAM(ptr, ptr_sz, buffer, size, length, method_name,    \
72                            param_name)                                        \
73  ({                                                                           \
74    ERR_IF_BUFFER_LENGTH_EXCEEDED(size, length, sizeof(*ptr_sz), method_name,  \
75                                  param_name);                                 \
76    memcpy(buffer + length, ptr_sz, sizeof(*ptr_sz));                          \
77    length += sizeof(*ptr_sz);                                                 \
78    ERR_IF_BUFFER_LENGTH_EXCEEDED(size, length, (sizeof(ptr[0]) * (*ptr_sz)),  \
79                                  method_name, param_name);                    \
80    memcpy(buffer + length, ptr, (sizeof(ptr[0]) * (*ptr_sz)));                \
81    length + (sizeof(ptr[0]) * (*ptr_sz));                                     \
82  })
83
84#define MARSHAL_STRING_ARRAY_PARAM(ptr, ptr_sz, buffer, size, length,          \
85                                   method_name, param_name)                    \
86  ({                                                                           \
87    ERR_IF_BUFFER_LENGTH_EXCEEDED(size, length, sizeof(*ptr_sz), method_name,  \
88                                  param_name);                                 \
89    memcpy(buffer + length, ptr_sz, sizeof(*ptr_sz));                          \
90    length += sizeof(*ptr_sz);                                                 \
91    for (int __i = 0; __i < *ptr_sz; __i++) {                                  \
92      size_t __strlen = strnlen(ptr[__i], size - length);                      \
93      ERR_IF_BUFFER_LENGTH_EXCEEDED(size, length, (__strlen + 1), method_name, \
94                                    param_name)                                \
95      /* If we didn't trigger an error, we now know this strcpy is safe. */    \
96      (void)strcpy(buffer + length, ptr[__i]);                                 \
97      length += (__strlen + 1);                                                \
98    }                                                                          \
99    length;                                                                    \
100  })
101
102#define MARSHAL_STRING_PARAM(ptr, buffer, size, length, method_name,           \
103                             param_name)                                       \
104  ({                                                                           \
105    size_t __strlen = strnlen(ptr, size - length);                             \
106    ERR_IF_BUFFER_LENGTH_EXCEEDED(size, length, (__strlen + 1), method_name,   \
107                                  param_name);                                 \
108    /* If we didn't trigger an error, we now know this strcpy is safe. */      \
109    (void)strcpy(buffer + length, ptr);                                        \
110    length + (__strlen + 1);                                                   \
111  })
112
113#define MARSHAL_PARAM(ptr, buffer, size, length, method_name, param_name)      \
114  ({                                                                           \
115    ERR_IF_BUFFER_LENGTH_EXCEEDED(size, length, sizeof(*ptr), method_name,     \
116                                  param_name)                                  \
117    memcpy(buffer + length, ptr, sizeof(*ptr));                                \
118    length + sizeof(*ptr);                                                     \
119  })
120
121#define UNMARSHAL_ARRAY_PARAM(ptr, ptr_sz, buffer, size, length, method_name,  \
122                              param_name, cleanup_action)                      \
123  ({                                                                           \
124    ERR_IF_MALFORMED_RPC_PAYLOAD(size, length, sizeof(*ptr_sz), method_name,   \
125                                 param_name, cleanup_action);                  \
126    memcpy(ptr_sz, buffer + length, sizeof(*ptr_sz));                          \
127    length += sizeof(*ptr_sz);                                                 \
128    *ptr = malloc(sizeof((*ptr)[0]) * (*ptr_sz));                              \
129    ERR_IF_ALLOCATION_FAILURE(*ptr, sizeof((*ptr)[0]) * (*ptr_sz),             \
130                              method_name, param_name, cleanup_action);        \
131    ERR_IF_MALFORMED_RPC_PAYLOAD(size, length,                                 \
132                                 (sizeof((*ptr)[0]) * (*ptr_sz)), method_name, \
133                                 param_name, ({                                \
134                                   free(*ptr);                                 \
135                                   cleanup_action;                             \
136                                 }));                                          \
137    memcpy((*ptr), buffer + length, sizeof((*ptr)[0]) * (*ptr_sz));            \
138    length + sizeof((*ptr)[0]) * (*ptr_sz);                                    \
139  })
140
141#define UNMARSHAL_STRING_ARRAY_PARAM(ptr, ptr_sz, buffer, size, length,        \
142                                     method_name, param_name, cleanup_action)  \
143  ({                                                                           \
144    ERR_IF_MALFORMED_RPC_PAYLOAD(size, length, sizeof(*ptr_sz), method_name,   \
145                                 param_name, cleanup_action);                  \
146    memcpy(ptr_sz, buffer + length, sizeof(*ptr_sz));                          \
147    length += sizeof(*ptr_sz);                                                 \
148    *ptr = malloc(sizeof(char *) * (*ptr_sz));                                 \
149    ERR_IF_ALLOCATION_FAILURE(*ptr, sizeof(char *) * (*ptr_sz), method_name,   \
150                              param_name, cleanup_action);                     \
151    for (int __i = 0; __i < *ptr_sz; __i++) {                                  \
152      size_t __strlen = strnlen(buffer + length, size - length);               \
153      ERR_IF_MALFORMED_RPC_PAYLOAD(size, length, (__strlen + 1), method_name,  \
154                                   param_name, ({                              \
155                                     for (int __j = 0; __j < __i; __j++) {     \
156                                       free((*ptr)[__j]);                      \
157                                     }                                         \
158                                     free(*ptr);                               \
159                                     cleanup_action;                           \
160                                   }));                                        \
161      /* If we didn't trigger an error, we now know this strdup is safe. */    \
162      (*ptr)[__i] = strdup(buffer + length);                                   \
163      ERR_IF_ALLOCATION_FAILURE((*ptr)[__i], __strlen + 1, method_name,        \
164                                param_name, ({                                 \
165                                  for (int __j = 0; __j < __i; __j++) {        \
166                                    free((*ptr)[__j]);                         \
167                                  }                                            \
168                                  free(*ptr);                                  \
169                                  cleanup_action;                              \
170                                }));                                           \
171      length += __strlen + 1;                                                  \
172    }                                                                          \
173    length;                                                                    \
174  })
175
176#define UNMARSHAL_STRING_PARAM(ptr, buffer, size, length, method_name,         \
177                               param_name, cleanup_action)                     \
178  ({                                                                           \
179    size_t __strlen = strnlen(buffer + length, size - length);                 \
180    ERR_IF_MALFORMED_RPC_PAYLOAD(size, length, (__strlen + 1), method_name,    \
181                                 param_name, cleanup_action);                  \
182    *ptr = strdup(buffer + length);                                            \
183    ERR_IF_ALLOCATION_FAILURE(*ptr, __strlen + 1, method_name, param_name,     \
184                              cleanup_action);                                 \
185    length + __strlen + 1;                                                     \
186  })
187
188#define UNMARSHAL_PARAM(ptr, buffer, size, length, method_name, param_name,    \
189                        cleanup_action)                                        \
190  ({                                                                           \
191    ERR_IF_MALFORMED_RPC_PAYLOAD(size, length, sizeof(*ptr), method_name,      \
192                                 param_name, cleanup_action);                  \
193    memcpy(ptr, buffer + length, sizeof(*ptr));                                \
194    length + sizeof(*ptr);                                                     \
195  })
196