]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/marshal.c
c6a25f7b4cdce8b4b7f8ff83a68d420996ed348d
[thirdparty/lldpd.git] / src / marshal.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2012 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #define MARSHAL_EXPORT
19 #include "marshal.h"
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/queue.h>
25 #include <string.h>
26
27 #include "compat/compat.h"
28 #include "log.h"
29
30 #include "lldpd-structs.h"
31
32 /* Stolen from CCAN */
33 #if HAVE_ALIGNOF
34 # define ALIGNOF(t) (__alignof__(t))
35 #else
36 # define ALIGNOF(t) \
37 ((sizeof(t) > 1) ? \
38 ((char *)(&((struct { \
39 char c; \
40 t _h; \
41 } *)0) \
42 ->_h) - \
43 (char *)0) : \
44 1)
45 #endif
46
47 /* A serialized object */
48 struct marshal_serialized {
49 void *orig; /* Original reference. Also enforce alignment. */
50 size_t size;
51 unsigned char object[0];
52 };
53
54 struct marshal_info marshal_info_string = {
55 .name = "null string",
56 .size = 0,
57 .pointers = { MARSHAL_SUBINFO_NULL },
58 };
59 struct marshal_info marshal_info_fstring = {
60 .name = "fixed string",
61 .size = 0,
62 .pointers = { MARSHAL_SUBINFO_NULL },
63 };
64 struct marshal_info marshal_info_ignore = {
65 .name = "ignored",
66 .size = 0,
67 .pointers = { MARSHAL_SUBINFO_NULL },
68 };
69
70 /* List of already seen pointers */
71 struct ref {
72 TAILQ_ENTRY(ref) next;
73 void *pointer;
74 uintptr_t dummy; /* To renumerate pointers */
75 };
76 TAILQ_HEAD(ref_l, ref);
77
78 /* Serialize the given object. */
79 ssize_t
80 marshal_serialize_(struct marshal_info *mi, void *unserialized, void **input, int skip,
81 void *_refs, int osize)
82 {
83 struct ref_l *refs = _refs;
84 struct ref *cref;
85 int size;
86 size_t len;
87 struct marshal_subinfo *current;
88 struct marshal_serialized *new = NULL, *serialized = NULL;
89 uintptr_t dummy = 1;
90
91 log_debug("marshal", "start serialization of %s", mi->name);
92
93 /* Check if we have already serialized this one. */
94 if (!refs) {
95 refs = calloc(1, sizeof(struct ref_l));
96 if (!refs) {
97 log_warnx("marshal",
98 "unable to allocate memory for list of references");
99 return -1;
100 }
101 TAILQ_INIT(refs);
102 }
103 TAILQ_FOREACH (cref, refs, next) {
104 if (unserialized == cref->pointer) return 0;
105 /* dummy should be higher than any existing dummy */
106 if (cref->dummy >= dummy) dummy = cref->dummy + 1;
107 }
108
109 /* Handle special cases. */
110 size = mi->size;
111 if (!strcmp(mi->name, "null string")) /* We know we can't be called with NULL */
112 size = strlen((char *)unserialized) + 1;
113 else if (!strcmp(mi->name, "fixed string"))
114 size = osize;
115
116 /* Allocate serialized structure */
117 len = sizeof(struct marshal_serialized) + (skip ? 0 : size);
118 serialized = calloc(1, len);
119 if (!serialized) {
120 log_warnx("marshal",
121 "unable to allocate memory to serialize structure %s", mi->name);
122 len = -1;
123 goto marshal_error;
124 }
125 /* We don't use the original pointer but a dummy one. */
126 serialized->orig = (unsigned char *)dummy;
127
128 /* Append the new reference */
129 if (!(cref = calloc(1, sizeof(struct ref)))) {
130 log_warnx("marshal",
131 "unable to allocate memory for list of references");
132 free(serialized);
133 len = -1;
134 goto marshal_error;
135 }
136 cref->pointer = unserialized;
137 cref->dummy = dummy;
138 TAILQ_INSERT_TAIL(refs, cref, next);
139
140 /* First, serialize the main structure */
141 if (!skip) memcpy(serialized->object, unserialized, size);
142
143 /* Then, serialize inner structures */
144 for (current = mi->pointers; current->mi; current++) {
145 size_t sublen;
146 size_t padlen;
147 void *source;
148 void *target = NULL;
149 if (current->kind == ignore) continue;
150 if (current->kind == pointer) {
151 memcpy(&source, (unsigned char *)unserialized + current->offset,
152 sizeof(void *));
153 if (source == NULL) continue;
154 } else
155 source =
156 (void *)((unsigned char *)unserialized + current->offset);
157 if (current->offset2)
158 memcpy(&osize, (unsigned char *)unserialized + current->offset2,
159 sizeof(int));
160 target = NULL;
161 sublen = marshal_serialize_(current->mi, source, &target,
162 current->kind == substruct, refs, osize);
163 if (sublen == -1) {
164 log_warnx("marshal",
165 "unable to serialize substructure %s for %s",
166 current->mi->name, mi->name);
167 free(serialized);
168 return -1;
169 }
170 /* We want to put the renumerated pointer instead of the real one. */
171 if (current->kind == pointer && !skip) {
172 TAILQ_FOREACH (cref, refs, next) {
173 if (source == cref->pointer) {
174 void *fakepointer =
175 (unsigned char *)cref->dummy;
176 memcpy((unsigned char *)serialized->object +
177 current->offset,
178 &fakepointer, sizeof(void *));
179 break;
180 }
181 }
182 }
183 if (sublen == 0) continue; /* This was already serialized */
184 /* Append the result, force alignment to be able to unserialize it */
185 padlen = ALIGNOF(struct marshal_serialized);
186 padlen = (padlen - (len % padlen)) % padlen;
187 new = realloc(serialized, len + padlen + sublen);
188 if (!new) {
189 log_warnx("marshal",
190 "unable to allocate more memory to serialize structure %s",
191 mi->name);
192 free(serialized);
193 free(target);
194 len = -1;
195 goto marshal_error;
196 }
197 memset((unsigned char *)new + len, 0, padlen);
198 memcpy((unsigned char *)new + len + padlen, target, sublen);
199 free(target);
200 len += sublen + padlen;
201 serialized = (struct marshal_serialized *)new;
202 }
203
204 serialized->size = len;
205 *input = serialized;
206 marshal_error:
207 if (refs && !_refs) {
208 struct ref *cref, *cref_next;
209 for (cref = TAILQ_FIRST(refs); cref != NULL; cref = cref_next) {
210 cref_next = TAILQ_NEXT(cref, next);
211 TAILQ_REMOVE(refs, cref, next);
212 free(cref);
213 }
214 free(refs);
215 }
216 return len;
217 }
218
219 /* This structure is used to track memory allocation when serializing */
220 struct gc {
221 TAILQ_ENTRY(gc) next;
222 void *pointer;
223 void *orig; /* Original reference (not valid anymore !) */
224 };
225 TAILQ_HEAD(gc_l, gc);
226
227 static void *
228 marshal_alloc(struct gc_l *pointers, size_t len, void *orig)
229 {
230 struct gc *gpointer = NULL;
231
232 void *result = calloc(1, len);
233 if (!result) return NULL;
234 if ((gpointer = (struct gc *)calloc(1, sizeof(struct gc))) == NULL) {
235 free(result);
236 return NULL;
237 }
238 gpointer->pointer = result;
239 gpointer->orig = orig;
240 TAILQ_INSERT_TAIL(pointers, gpointer, next);
241 return result;
242 }
243 static void
244 marshal_free(struct gc_l *pointers, int gconly)
245 {
246 struct gc *pointer, *pointer_next;
247 for (pointer = TAILQ_FIRST(pointers); pointer != NULL; pointer = pointer_next) {
248 pointer_next = TAILQ_NEXT(pointer, next);
249 TAILQ_REMOVE(pointers, pointer, next);
250 if (!gconly) free(pointer->pointer);
251 free(pointer);
252 }
253 }
254
255 /* Unserialize the given object. */
256 size_t
257 marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **output,
258 void *_pointers, int skip, int osize)
259 {
260 int total_len = sizeof(struct marshal_serialized) + (skip ? 0 : mi->size);
261 struct marshal_serialized *serialized = buffer;
262 struct gc_l *pointers = _pointers;
263 int size, already, extra = 0;
264 void *new;
265 struct marshal_subinfo *current;
266 struct gc *apointer;
267
268 log_debug("marshal", "start unserialization of %s", mi->name);
269
270 if (len < sizeof(struct marshal_serialized) || len < total_len) {
271 log_warnx("marshal",
272 "data to deserialize is too small (%zu) for structure %s", len,
273 mi->name);
274 return 0;
275 }
276
277 /* Initialize garbage collection */
278 if (!pointers) {
279 pointers = calloc(1, sizeof(struct gc_l));
280 if (!pointers) {
281 log_warnx("marshal",
282 "unable to allocate memory for garbage collection");
283 return 0;
284 }
285 TAILQ_INIT(pointers);
286 }
287
288 /* Special cases */
289 size = mi->size;
290 if (!strcmp(mi->name, "null string") || !strcmp(mi->name, "fixed string")) {
291 switch (mi->name[0]) {
292 case 'n':
293 size = strnlen((char *)serialized->object,
294 len - sizeof(struct marshal_serialized)) +
295 1;
296 break;
297 case 'f':
298 size = osize;
299 extra = 1;
300 break; /* The extra byte is to ensure that
301 the string is null terminated. */
302 }
303 if (size > len - sizeof(struct marshal_serialized)) {
304 log_warnx("marshal",
305 "data to deserialize contains a string too long");
306 total_len = 0;
307 goto unmarshal_error;
308 }
309 total_len += size;
310 }
311
312 /* First, the main structure */
313 if (!skip) {
314 if ((*output = marshal_alloc(pointers, size + extra,
315 serialized->orig)) == NULL) {
316 log_warnx("marshal",
317 "unable to allocate memory to unserialize structure %s",
318 mi->name);
319 total_len = 0;
320 goto unmarshal_error;
321 }
322 memcpy(*output, serialized->object, size);
323 }
324
325 /* Then, each substructure */
326 for (current = mi->pointers; current->mi; current++) {
327 size_t sublen;
328 size_t padlen;
329 new = (unsigned char *)*output + current->offset;
330 if (current->kind == ignore) {
331 memset((unsigned char *)*output + current->offset, 0,
332 sizeof(void *));
333 continue;
334 }
335 if (current->kind == pointer) {
336 if (*(void **)new == NULL) continue;
337
338 /* Did we already see this reference? */
339 already = 0;
340 TAILQ_FOREACH (apointer, pointers, next)
341 if (apointer->orig == *(void **)new) {
342 memcpy((unsigned char *)*output +
343 current->offset,
344 &apointer->pointer, sizeof(void *));
345 already = 1;
346 break;
347 }
348 if (already) continue;
349 }
350 /* Deserialize */
351 if (current->offset2)
352 memcpy(&osize, (unsigned char *)*output + current->offset2,
353 sizeof(int));
354 padlen = ALIGNOF(struct marshal_serialized);
355 padlen = (padlen - (total_len % padlen)) % padlen;
356 if (len < total_len + padlen ||
357 ((sublen = marshal_unserialize_(current->mi,
358 (unsigned char *)buffer + total_len + padlen,
359 len - total_len - padlen, &new, pointers,
360 current->kind == substruct, osize)) == 0)) {
361 log_warnx("marshal",
362 "unable to serialize substructure %s for %s",
363 current->mi->name, mi->name);
364 total_len = 0;
365 goto unmarshal_error;
366 }
367 /* Link the result */
368 if (current->kind == pointer)
369 memcpy((unsigned char *)*output + current->offset, &new,
370 sizeof(void *));
371 total_len += sublen + padlen;
372 }
373
374 unmarshal_error:
375 if (pointers && !_pointers) {
376 marshal_free(pointers, (total_len > 0));
377 free(pointers);
378 }
379 return total_len;
380 }