1 #define _GNU_SOURCE
2 #include <dlfcn.h>
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <signal.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <stdint.h>
10 #include <assert.h>
11 #include <stdbool.h>
12 #include <ffi.h>
13 #include <inttypes.h>
14 
15 #include "builtins.h"
16 #include "variables.h"
17 #include "arrayfunc.h"
18 #include "common.h"
19 #include "bashgetopt.h"
20 #include "make_cmd.h"
21 #include "util.h"
22 #include "types.h"
23 #include "shell.h"
24 
25 #if !defined(__GLIBC__) && !defined(__NEWLIB__) && !defined(__DragonFly__)
mempcpy(void * dest,const void * src,size_t n)26 static inline void *mempcpy(void *dest, const void *src, size_t n)
27 {
28     memcpy(dest, src, n);
29     return ((char *)dest + n);
30 }
31 #endif
32 
33 struct pack_context {
34     ffi_type *ptrtype;
35     WORD_LIST *list;
36     uint8_t *source;
37     int retval;
38 };
39 
40 
41 // Bash provides an array_walk() and an assoc_walk() to invoke a callback on
42 // each element of an array and associative array respectively. Unfortunately
43 // assoc_walk does not accept a user data parameter like array_walk, so we
44 // provide our own version.
assoc_walk_data(HASH_TABLE * table,int (* func)(BUCKET_CONTENTS *,void *),void * data)45 void assoc_walk_data(HASH_TABLE *table,
46                      int (*func)(BUCKET_CONTENTS *, void *),
47                      void *data)
48 {
49     int i;
50     BUCKET_CONTENTS *item;
51 
52     if (table == 0 || HASH_ENTRIES (table) == 0)
53         return;
54 
55     for (i = 0; i < table->nbuckets; i++)
56     {
57         for (item = hash_items (i, table); item; item = item->next)
58             if ((*func) (item, data) < 0)
59                 return;
60     }
61 }
62 
63 // Callback for each array element.
pack_decode_element(ARRAY_ELEMENT * element,void * user)64 int pack_decode_element(ARRAY_ELEMENT *element, void *user)
65 {
66     struct pack_context *ctx;
67     void **value;
68 
69     ctx = user;
70 
71     if (decode_primitive_type(element->value,
72                               (void **)&value,
73                               &ctx->ptrtype) == false) {
74 
75         // You can exit from an array_walk early by returning -1, so set
76         // failure and do that here.
77         ctx->retval = EXECUTION_FAILURE;
78 
79         // Give a hint about what failed to parse.
80         builtin_warning("aborted pack at bad type prefix %s (%s[%lu])",
81                         element->value,
82                         ctx->list->word->word,
83                         element->ind);
84 
85         return -1;
86     }
87 
88     // Extract the data into the destination buffer.
89     ctx->source = mempcpy(ctx->source, value, ctx->ptrtype->size);
90 
91     // No longer needed.
92     free(value);
93     return 0;
94 }
95 
96 // Callback for each assoc element.
pack_decode_element_assoc(BUCKET_CONTENTS * element,void * user)97 int pack_decode_element_assoc(BUCKET_CONTENTS *element, void *user)
98 {
99     struct pack_context *ctx;
100     void **value;
101 
102     ctx = user;
103 
104     // Check if we've been passed an uninitialized type (therefore 0)
105     if (strchr(element->data, ':') == NULL) {
106         if (decode_type_prefix(element->data, "0", &ctx->ptrtype, (void **)&value, NULL) == true) {
107             // Looks like that worked, skip decoding.
108             goto decode;
109         }
110     }
111 
112     if (decode_primitive_type(element->data,
113                               (void **)&value,
114                               &ctx->ptrtype) == false) {
115 
116         // You can exit from an array_walk early by returning -1, so set
117         // failure and do that here.
118         ctx->retval = EXECUTION_FAILURE;
119 
120         // Give a hint about what failed to parse.
121         builtin_warning("aborted pack at bad type prefix %s (%s[%s])",
122                         (char *) element->data,
123                         ctx->list->word->word,
124                         element->key);
125 
126         return -1;
127     }
128 
129 decode:
130     // Extract the data into the destination buffer.
131     ctx->source = mempcpy(ctx->source, value, ctx->ptrtype->size);
132 
133     // No longer needed.
134     free(value);
135 
136     return 0;
137 }
138 
pack_prefixed_array(WORD_LIST * list)139 static int pack_prefixed_array(WORD_LIST *list)
140 {
141     SHELL_VAR *dest_v;
142     ARRAY *dest_a;
143     HASH_TABLE *dest_h;
144     void **value;
145     struct pack_context ctx = { 0 };
146 
147     // Assume success by default.
148     ctx.retval = EXECUTION_SUCCESS;
149 
150     // Verify we have two parameters.
151     if (!list || !list->next) {
152         builtin_usage();
153         goto error;
154     }
155 
156     // Check if the user forgot the '$'
157     if (strchr(list->word->word, ':') == NULL) {
158         builtin_warning("%s does not have a prefix, is that what you intended?", list->word->word);
159     }
160 
161     // Fetch the source pointer.
162     if (decode_primitive_type(list->word->word,
163                               (void **)&value,
164                               &ctx.ptrtype) != true) {
165         builtin_error("the destination parameter %s could not parsed", list->word->word);
166         goto error;
167     }
168 
169     // Verify that it was a pointer.
170     if (ctx.ptrtype != &ffi_type_pointer) {
171         builtin_error("the destination parameter must be a pointer");
172         goto error;
173     }
174 
175     // Skip to next parameter.
176     list        = list->next;
177     ctx.source  = *value;
178     ctx.list    = list;
179 
180     GET_ARRAY_FROM_VAR(list->word->word, dest_v, dest_a);
181 
182     if (assoc_p(dest_v)) {
183         // Extract the hash table
184         dest_h = (HASH_TABLE *) dest_v->value;
185 
186         if (dest_h->nbuckets != 1) {
187             builtin_warning("the associative array %s will not maintain it's order", list->word->word);
188         }
189 
190         assoc_walk_data(dest_h, pack_decode_element_assoc, &ctx);
191     } else if (array_p(dest_v)) {
192         array_walk(dest_a, pack_decode_element, &ctx);
193     } else {
194         builtin_error("expected an array or associative array");
195         goto error;
196     }
197 
198     return ctx.retval;
199 
200 error:
201     return EXECUTION_FAILURE;
202 }
203 
204 struct unpack_context {
205     ffi_type *ptrtype;
206     WORD_LIST *list;
207     uint8_t *source;
208     int retval;
209 };
210 
211 // Callback for each array element.
unpack_decode_element(ARRAY_ELEMENT * element,void * user)212 int unpack_decode_element(ARRAY_ELEMENT *element, void *user)
213 {
214     struct unpack_context *ctx;
215     char *format;
216 
217     ctx = user;
218 
219     // Truncate it if there's already a value, e.g.
220     // a=(int:0 int:0) is accceptable to initialize a buffer.
221     if ((format = strchr(element->value, ':')))
222         *format = '\0';
223 
224     if (decode_type_prefix(element->value,
225                            NULL,
226                            &ctx->ptrtype,
227                            NULL,
228                            &format) == false) {
229         // You can exit from an array_walk early by returning -1, so set
230         // failure and do that here.
231         ctx->retval = EXECUTION_FAILURE;
232 
233         // Give a hint about what failed to parse.
234         builtin_warning("aborted unpack at bad type prefix %s (%s[%lu])",
235                         element->value,
236                         ctx->list->word->word,
237                         element->ind);
238 
239         return -1;
240     }
241 
242     // Discard previous value
243     FREE(element->value);
244 
245     // Decode the type.
246     element->value = encode_primitive_type(format, ctx->ptrtype, ctx->source);
247 
248     // Skip to next element.
249     ctx->source += ctx->ptrtype->size;
250 
251     return 0;
252 }
253 
254 // Callback for each assoc element.
unpack_decode_element_assoc(BUCKET_CONTENTS * element,void * user)255 int unpack_decode_element_assoc(BUCKET_CONTENTS *element, void *user)
256 {
257     struct unpack_context *ctx;
258     char *format;
259 
260     ctx = user;
261 
262     // Truncate it if there's already a value, e.g.
263     // a=(int:0 int:0) is accceptable to initialize a buffer.
264     if ((format = strchr(element->data, ':')))
265         *format = '\0';
266 
267     if (decode_type_prefix(element->data,
268                            NULL,
269                            &ctx->ptrtype,
270                            NULL,
271                            &format) == false) {
272         // You can exit from an array_walk early by returning -1, so set
273         // failure and do that here.
274         ctx->retval = EXECUTION_FAILURE;
275 
276         // Give a hint about what failed to parse.
277         builtin_warning("aborted unpack at bad type prefix %s (%s[%s])",
278                         (char *) element->data,
279                         ctx->list->word->word,
280                         element->key);
281 
282         return -1;
283     }
284 
285     // Discard previous value
286     FREE(element->data);
287 
288     // Decode the type.
289     element->data = encode_primitive_type(format, ctx->ptrtype, ctx->source);
290 
291     // Skip to next element.
292     ctx->source += ctx->ptrtype->size;
293 
294     return 0;
295 }
296 
unpack_prefixed_array(WORD_LIST * list)297 static int unpack_prefixed_array(WORD_LIST *list)
298 {
299     SHELL_VAR *dest_v;
300     ARRAY *dest_a;
301     HASH_TABLE *dest_h;
302     void **value;
303     struct unpack_context ctx = { 0 };
304 
305     // Assume success by default.
306     ctx.retval = EXECUTION_SUCCESS;
307 
308     // Verify we have two parameters.
309     if (!list || !list->next) {
310         builtin_usage();
311         goto error;
312     }
313 
314     // Check if the user forgot the '$'
315     if (strchr(list->word->word, ':') == NULL) {
316         builtin_warning("%s does not have a prefix, is that what you intended?", list->word->word);
317     }
318 
319     // Fetch the source pointer.
320     if (decode_primitive_type(list->word->word,
321                               (void **)&value,
322                               &ctx.ptrtype) != true) {
323         builtin_error("the source parameter %s could not parsed", list->word->word);
324         goto error;
325     }
326 
327     // Verify that it was a pointer.
328     if (ctx.ptrtype != &ffi_type_pointer) {
329         builtin_error("the source parameter must be a pointer");
330         goto error;
331     }
332 
333     // Skip to next parameter.
334     list        = list->next;
335     ctx.source  = *value;
336     ctx.list    = list;
337 
338     GET_ARRAY_FROM_VAR(list->word->word, dest_v, dest_a);
339 
340     if (assoc_p(dest_v)) {
341         // Extract the hash table
342         dest_h = (HASH_TABLE *) dest_v->value;
343 
344         if (dest_h->nbuckets != 1) {
345             builtin_warning("the associative array %s will not maintain it's order", list->word->word);
346         }
347 
348         assoc_walk_data(dest_h, unpack_decode_element_assoc, &ctx);
349     } else if (array_p(dest_v)) {
350         array_walk(dest_a, unpack_decode_element, &ctx);
351     } else {
352         builtin_error("expected an array or associative array");
353         goto error;
354     }
355 
356     return ctx.retval;
357 
358 error:
359     return EXECUTION_FAILURE;
360 }
361 
362 static char *unpack_usage[] = {
363     "Unpack memory into a bash array.",
364     "",
365     "Bash provides no convenient mechanism for dealing with binary data, this",
366     "interface allows for converting between prefixed, native, primitive types.",
367     "",
368     "$ struct=(pointer char int long)",
369     "$ unpack pointer:0x1234 struct",
370     "$ echo ${struct[*]}",
371     "pointer:0x1234 char:a int:1234 long:-1",
372     "  pack pointer:01234 struct",
373     "",
374     NULL,
375 };
376 
377 static char *pack_usage[] = {
378     "Convert data from a prefixed bash array into native memory.",
379     NULL,
380 };
381 
382 struct builtin __attribute__((visibility("default"))) unpack_struct = {
383     .name       = "unpack",
384     .function   = unpack_prefixed_array,
385     .flags      = BUILTIN_ENABLED,
386     .long_doc   = unpack_usage,
387     .short_doc  = "unpack pointer array",
388     .handle     = NULL,
389 };
390 
391 struct builtin __attribute__((visibility("default"))) pack_struct = {
392     .name       = "pack",
393     .function   = pack_prefixed_array,
394     .flags      = BUILTIN_ENABLED,
395     .long_doc   = pack_usage,
396     .short_doc  = "pack pointer array",
397     .handle     = NULL,
398 };
399 
400