]> git.ipfire.org Git - thirdparty/lldpd.git/blame - src/marshal.c
priv: disable LLDP in firmware for Intel X7xx cards on FreeBSD
[thirdparty/lldpd.git] / src / marshal.c
CommitLineData
4b292b55 1/* -*- mode: c; c-file-style: "openbsd" -*- */
db323555
VB
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
6bf5e749 18#define MARSHAL_EXPORT
4b292b55
VB
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"
db323555 31
d8234294 32/* Stolen from CCAN */
dd1d33cf 33#if HAVE_ALIGNOF
8b549648 34# define ALIGNOF(t) (__alignof__(t))
dd1d33cf 35#else
8b549648
VB
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)
dd1d33cf 45#endif
d8234294 46
db323555
VB
47/* A serialized object */
48struct marshal_serialized {
8b549648
VB
49 void *orig; /* Original reference. Also enforce alignment. */
50 size_t size;
db323555
VB
51 unsigned char object[0];
52};
53
6bf5e749 54struct marshal_info marshal_info_string = {
da781141
VB
55 .name = "null string",
56 .size = 0,
8b549648 57 .pointers = { MARSHAL_SUBINFO_NULL },
da781141 58};
6bf5e749 59struct marshal_info marshal_info_fstring = {
ca4ed9da
VB
60 .name = "fixed string",
61 .size = 0,
8b549648 62 .pointers = { MARSHAL_SUBINFO_NULL },
ca4ed9da 63};
6bf5e749 64struct marshal_info marshal_info_ignore = {
305e061c
VB
65 .name = "ignored",
66 .size = 0,
8b549648 67 .pointers = { MARSHAL_SUBINFO_NULL },
305e061c 68};
da781141 69
db323555
VB
70/* List of already seen pointers */
71struct ref {
72 TAILQ_ENTRY(ref) next;
73 void *pointer;
8b549648 74 uintptr_t dummy; /* To renumerate pointers */
db323555
VB
75};
76TAILQ_HEAD(ref_l, ref);
77
78/* Serialize the given object. */
281a5cd4 79ssize_t
8b549648
VB
80marshal_serialize_(struct marshal_info *mi, void *unserialized, void **input, int skip,
81 void *_refs, int osize)
4b292b55 82{
db323555 83 struct ref_l *refs = _refs;
5fd6695c
VB
84 struct ref *cref;
85 int size;
86 size_t len;
87 struct marshal_subinfo *current;
88 struct marshal_serialized *new = NULL, *serialized = NULL;
02f4014f 89 uintptr_t dummy = 1;
5fd6695c 90
6f8925be
VB
91 log_debug("marshal", "start serialization of %s", mi->name);
92
5fd6695c 93 /* Check if we have already serialized this one. */
db323555
VB
94 if (!refs) {
95 refs = calloc(1, sizeof(struct ref_l));
96 if (!refs) {
8b549648
VB
97 log_warnx("marshal",
98 "unable to allocate memory for list of references");
db323555
VB
99 return -1;
100 }
101 TAILQ_INIT(refs);
102 }
8b549648
VB
103 TAILQ_FOREACH (cref, refs, next) {
104 if (unserialized == cref->pointer) return 0;
47862f89
VB
105 /* dummy should be higher than any existing dummy */
106 if (cref->dummy >= dummy) dummy = cref->dummy + 1;
db323555
VB
107 }
108
da781141 109 /* Handle special cases. */
5fd6695c 110 size = mi->size;
8b549648 111 if (!strcmp(mi->name, "null string")) /* We know we can't be called with NULL */
da781141 112 size = strlen((char *)unserialized) + 1;
ca4ed9da
VB
113 else if (!strcmp(mi->name, "fixed string"))
114 size = osize;
da781141
VB
115
116 /* Allocate serialized structure */
8b549648 117 len = sizeof(struct marshal_serialized) + (skip ? 0 : size);
5fd6695c 118 serialized = calloc(1, len);
db323555 119 if (!serialized) {
8b549648
VB
120 log_warnx("marshal",
121 "unable to allocate memory to serialize structure %s", mi->name);
db323555
VB
122 len = -1;
123 goto marshal_error;
124 }
47862f89 125 /* We don't use the original pointer but a dummy one. */
8b549648 126 serialized->orig = (unsigned char *)dummy;
db323555
VB
127
128 /* Append the new reference */
129 if (!(cref = calloc(1, sizeof(struct ref)))) {
8b549648
VB
130 log_warnx("marshal",
131 "unable to allocate memory for list of references");
db323555
VB
132 free(serialized);
133 len = -1;
134 goto marshal_error;
135 }
136 cref->pointer = unserialized;
47862f89 137 cref->dummy = dummy;
db323555
VB
138 TAILQ_INSERT_TAIL(refs, cref, next);
139
140 /* First, serialize the main structure */
8b549648 141 if (!skip) memcpy(serialized->object, unserialized, size);
db323555
VB
142
143 /* Then, serialize inner structures */
db323555
VB
144 for (current = mi->pointers; current->mi; current++) {
145 size_t sublen;
d8234294 146 size_t padlen;
8b549648
VB
147 void *source;
148 void *target = NULL;
305e061c 149 if (current->kind == ignore) continue;
db323555 150 if (current->kind == pointer) {
8b549648 151 memcpy(&source, (unsigned char *)unserialized + current->offset,
6578dc6a 152 sizeof(void *));
db323555
VB
153 if (source == NULL) continue;
154 } else
8b549648
VB
155 source =
156 (void *)((unsigned char *)unserialized + current->offset);
6bf5e749 157 if (current->offset2)
8b549648
VB
158 memcpy(&osize, (unsigned char *)unserialized + current->offset2,
159 sizeof(int));
3abff5ce 160 target = NULL;
8b549648 161 sublen = marshal_serialize_(current->mi, source, &target,
ca4ed9da 162 current->kind == substruct, refs, osize);
3abff5ce 163 if (sublen == -1) {
8b549648
VB
164 log_warnx("marshal",
165 "unable to serialize substructure %s for %s",
db323555
VB
166 current->mi->name, mi->name);
167 free(serialized);
168 return -1;
169 }
47862f89
VB
170 /* We want to put the renumerated pointer instead of the real one. */
171 if (current->kind == pointer && !skip) {
8b549648 172 TAILQ_FOREACH (cref, refs, next) {
47862f89 173 if (source == cref->pointer) {
8b549648
VB
174 void *fakepointer =
175 (unsigned char *)cref->dummy;
176 memcpy((unsigned char *)serialized->object +
177 current->offset,
47862f89
VB
178 &fakepointer, sizeof(void *));
179 break;
180 }
181 }
182 }
db323555 183 if (sublen == 0) continue; /* This was already serialized */
d8234294
VB
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);
db323555 188 if (!new) {
8b549648
VB
189 log_warnx("marshal",
190 "unable to allocate more memory to serialize structure %s",
db323555
VB
191 mi->name);
192 free(serialized);
193 free(target);
194 len = -1;
195 goto marshal_error;
196 }
d8234294
VB
197 memset((unsigned char *)new + len, 0, padlen);
198 memcpy((unsigned char *)new + len + padlen, target, sublen);
db323555 199 free(target);
d8234294 200 len += sublen + padlen;
db323555
VB
201 serialized = (struct marshal_serialized *)new;
202 }
203
204 serialized->size = len;
205 *input = serialized;
206marshal_error:
207 if (refs && !_refs) {
208 struct ref *cref, *cref_next;
8b549648 209 for (cref = TAILQ_FIRST(refs); cref != NULL; cref = cref_next) {
db323555
VB
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 */
220struct gc {
221 TAILQ_ENTRY(gc) next;
222 void *pointer;
8b549648 223 void *orig; /* Original reference (not valid anymore !) */
db323555
VB
224};
225TAILQ_HEAD(gc_l, gc);
226
8b549648 227static void *
db323555
VB
228marshal_alloc(struct gc_l *pointers, size_t len, void *orig)
229{
5fd6695c
VB
230 struct gc *gpointer = NULL;
231
4b292b55 232 void *result = calloc(1, len);
db323555 233 if (!result) return NULL;
8b549648 234 if ((gpointer = (struct gc *)calloc(1, sizeof(struct gc))) == NULL) {
db323555
VB
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}
243static void
244marshal_free(struct gc_l *pointers, int gconly)
245{
246 struct gc *pointer, *pointer_next;
8b549648 247 for (pointer = TAILQ_FIRST(pointers); pointer != NULL; pointer = pointer_next) {
db323555
VB
248 pointer_next = TAILQ_NEXT(pointer, next);
249 TAILQ_REMOVE(pointers, pointer, next);
8b549648 250 if (!gconly) free(pointer->pointer);
db323555
VB
251 free(pointer);
252 }
253}
254
db323555
VB
255/* Unserialize the given object. */
256size_t
6bf5e749 257marshal_unserialize_(struct marshal_info *mi, void *buffer, size_t len, void **output,
ca4ed9da 258 void *_pointers, int skip, int osize)
db323555 259{
8b549648 260 int total_len = sizeof(struct marshal_serialized) + (skip ? 0 : mi->size);
db323555 261 struct marshal_serialized *serialized = buffer;
5fd6695c 262 struct gc_l *pointers = _pointers;
4b292b55 263 int size, already, extra = 0;
5fd6695c
VB
264 void *new;
265 struct marshal_subinfo *current;
266 struct gc *apointer;
267
6f8925be
VB
268 log_debug("marshal", "start unserialization of %s", mi->name);
269
db323555 270 if (len < sizeof(struct marshal_serialized) || len < total_len) {
8b549648
VB
271 log_warnx("marshal",
272 "data to deserialize is too small (%zu) for structure %s", len,
273 mi->name);
db323555
VB
274 return 0;
275 }
276
277 /* Initialize garbage collection */
db323555
VB
278 if (!pointers) {
279 pointers = calloc(1, sizeof(struct gc_l));
280 if (!pointers) {
8b549648
VB
281 log_warnx("marshal",
282 "unable to allocate memory for garbage collection");
db323555
VB
283 return 0;
284 }
285 TAILQ_INIT(pointers);
286 }
287
da781141 288 /* Special cases */
5fd6695c 289 size = mi->size;
ca4ed9da
VB
290 if (!strcmp(mi->name, "null string") || !strcmp(mi->name, "fixed string")) {
291 switch (mi->name[0]) {
8b549648
VB
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. */
ca4ed9da
VB
302 }
303 if (size > len - sizeof(struct marshal_serialized)) {
8b549648
VB
304 log_warnx("marshal",
305 "data to deserialize contains a string too long");
da781141
VB
306 total_len = 0;
307 goto unmarshal_error;
308 }
309 total_len += size;
310 }
311
db323555
VB
312 /* First, the main structure */
313 if (!skip) {
8b549648
VB
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",
db323555
VB
318 mi->name);
319 total_len = 0;
320 goto unmarshal_error;
321 }
da781141 322 memcpy(*output, serialized->object, size);
db323555
VB
323 }
324
325 /* Then, each substructure */
db323555 326 for (current = mi->pointers; current->mi; current++) {
8b549648
VB
327 size_t sublen;
328 size_t padlen;
5fd6695c 329 new = (unsigned char *)*output + current->offset;
305e061c 330 if (current->kind == ignore) {
8b549648
VB
331 memset((unsigned char *)*output + current->offset, 0,
332 sizeof(void *));
305e061c
VB
333 continue;
334 }
db323555
VB
335 if (current->kind == pointer) {
336 if (*(void **)new == NULL) continue;
337
338 /* Did we already see this reference? */
5fd6695c 339 already = 0;
8b549648 340 TAILQ_FOREACH (apointer, pointers, next)
5fd6695c 341 if (apointer->orig == *(void **)new) {
8b549648
VB
342 memcpy((unsigned char *)*output +
343 current->offset,
5fd6695c 344 &apointer->pointer, sizeof(void *));
db323555
VB
345 already = 1;
346 break;
347 }
348 if (already) continue;
349 }
350 /* Deserialize */
6bf5e749 351 if (current->offset2)
8b549648
VB
352 memcpy(&osize, (unsigned char *)*output + current->offset2,
353 sizeof(int));
d8234294
VB
354 padlen = ALIGNOF(struct marshal_serialized);
355 padlen = (padlen - (total_len % padlen)) % padlen;
8b549648
VB
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",
db323555
VB
363 current->mi->name, mi->name);
364 total_len = 0;
365 goto unmarshal_error;
366 }
367 /* Link the result */
368 if (current->kind == pointer)
8b549648
VB
369 memcpy((unsigned char *)*output + current->offset, &new,
370 sizeof(void *));
d8234294 371 total_len += sublen + padlen;
db323555
VB
372 }
373
374unmarshal_error:
375 if (pointers && !_pointers) {
376 marshal_free(pointers, (total_len > 0));
377 free(pointers);
378 }
379 return total_len;
380}