Coverage Report

Created: 2025-12-31 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/lldpd/src/marshal.c
Line
Count
Source
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
0
#  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
0
{
83
0
  struct ref_l *refs = _refs;
84
0
  struct ref *cref;
85
0
  int size;
86
0
  size_t len;
87
0
  struct marshal_subinfo *current;
88
0
  struct marshal_serialized *new = NULL, *serialized = NULL;
89
0
  uintptr_t dummy = 1;
90
91
0
  log_debug("marshal", "start serialization of %s", mi->name);
92
93
  /* Check if we have already serialized this one. */
94
0
  if (!refs) {
95
0
    refs = calloc(1, sizeof(struct ref_l));
96
0
    if (!refs) {
97
0
      log_warnx("marshal",
98
0
          "unable to allocate memory for list of references");
99
0
      return -1;
100
0
    }
101
0
    TAILQ_INIT(refs);
102
0
  }
103
0
  TAILQ_FOREACH (cref, refs, next) {
104
0
    if (unserialized == cref->pointer) return 0;
105
    /* dummy should be higher than any existing dummy */
106
0
    if (cref->dummy >= dummy) dummy = cref->dummy + 1;
107
0
  }
108
109
  /* Handle special cases. */
110
0
  size = mi->size;
111
0
  if (!strcmp(mi->name, "null string")) /* We know we can't be called with NULL */
112
0
    size = strlen((char *)unserialized) + 1;
113
0
  else if (!strcmp(mi->name, "fixed string"))
114
0
    size = osize;
115
116
  /* Allocate serialized structure */
117
0
  len = sizeof(struct marshal_serialized) + (skip ? 0 : size);
118
0
  serialized = calloc(1, len);
119
0
  if (!serialized) {
120
0
    log_warnx("marshal",
121
0
        "unable to allocate memory to serialize structure %s", mi->name);
122
0
    len = -1;
123
0
    goto marshal_error;
124
0
  }
125
  /* We don't use the original pointer but a dummy one. */
126
0
  serialized->orig = (unsigned char *)dummy;
127
128
  /* Append the new reference */
129
0
  if (!(cref = calloc(1, sizeof(struct ref)))) {
130
0
    log_warnx("marshal",
131
0
        "unable to allocate memory for list of references");
132
0
    free(serialized);
133
0
    len = -1;
134
0
    goto marshal_error;
135
0
  }
136
0
  cref->pointer = unserialized;
137
0
  cref->dummy = dummy;
138
0
  TAILQ_INSERT_TAIL(refs, cref, next);
139
140
  /* First, serialize the main structure */
141
0
  if (!skip) memcpy(serialized->object, unserialized, size);
142
143
  /* Then, serialize inner structures */
144
0
  for (current = mi->pointers; current->mi; current++) {
145
0
    size_t sublen;
146
0
    size_t padlen;
147
0
    void *source;
148
0
    void *target = NULL;
149
0
    if (current->kind == ignore) continue;
150
0
    if (current->kind == pointer) {
151
0
      memcpy(&source, (unsigned char *)unserialized + current->offset,
152
0
          sizeof(void *));
153
0
      if (source == NULL) continue;
154
0
    } else
155
0
      source =
156
0
          (void *)((unsigned char *)unserialized + current->offset);
157
0
    if (current->offset2)
158
0
      memcpy(&osize, (unsigned char *)unserialized + current->offset2,
159
0
          sizeof(int));
160
0
    target = NULL;
161
0
    sublen = marshal_serialize_(current->mi, source, &target,
162
0
        current->kind == substruct, refs, osize);
163
0
    if (sublen == -1) {
164
0
      log_warnx("marshal",
165
0
          "unable to serialize substructure %s for %s",
166
0
          current->mi->name, mi->name);
167
0
      free(serialized);
168
0
      return -1;
169
0
    }
170
    /* We want to put the renumerated pointer instead of the real one. */
171
0
    if (current->kind == pointer && !skip) {
172
0
      TAILQ_FOREACH (cref, refs, next) {
173
0
        if (source == cref->pointer) {
174
0
          void *fakepointer =
175
0
              (unsigned char *)cref->dummy;
176
0
          memcpy((unsigned char *)serialized->object +
177
0
            current->offset,
178
0
              &fakepointer, sizeof(void *));
179
0
          break;
180
0
        }
181
0
      }
182
0
    }
183
0
    if (sublen == 0) continue; /* This was already serialized */
184
    /* Append the result, force alignment to be able to unserialize it */
185
0
    padlen = ALIGNOF(struct marshal_serialized);
186
0
    padlen = (padlen - (len % padlen)) % padlen;
187
0
    new = realloc(serialized, len + padlen + sublen);
188
0
    if (!new) {
189
0
      log_warnx("marshal",
190
0
          "unable to allocate more memory to serialize structure %s",
191
0
          mi->name);
192
0
      free(serialized);
193
0
      free(target);
194
0
      len = -1;
195
0
      goto marshal_error;
196
0
    }
197
0
    memset((unsigned char *)new + len, 0, padlen);
198
0
    memcpy((unsigned char *)new + len + padlen, target, sublen);
199
0
    free(target);
200
0
    len += sublen + padlen;
201
0
    serialized = (struct marshal_serialized *)new;
202
0
  }
203
204
0
  serialized->size = len;
205
0
  *input = serialized;
206
0
marshal_error:
207
0
  if (refs && !_refs) {
208
0
    struct ref *cref, *cref_next;
209
0
    for (cref = TAILQ_FIRST(refs); cref != NULL; cref = cref_next) {
210
0
      cref_next = TAILQ_NEXT(cref, next);
211
0
      TAILQ_REMOVE(refs, cref, next);
212
0
      free(cref);
213
0
    }
214
0
    free(refs);
215
0
  }
216
0
  return len;
217
0
}
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
0
{
230
0
  struct gc *gpointer = NULL;
231
232
0
  void *result = calloc(1, len);
233
0
  if (!result) return NULL;
234
0
  if ((gpointer = (struct gc *)calloc(1, sizeof(struct gc))) == NULL) {
235
0
    free(result);
236
0
    return NULL;
237
0
  }
238
0
  gpointer->pointer = result;
239
0
  gpointer->orig = orig;
240
0
  TAILQ_INSERT_TAIL(pointers, gpointer, next);
241
0
  return result;
242
0
}
243
static void
244
marshal_free(struct gc_l *pointers, int gconly)
245
0
{
246
0
  struct gc *pointer, *pointer_next;
247
0
  for (pointer = TAILQ_FIRST(pointers); pointer != NULL; pointer = pointer_next) {
248
0
    pointer_next = TAILQ_NEXT(pointer, next);
249
0
    TAILQ_REMOVE(pointers, pointer, next);
250
0
    if (!gconly) free(pointer->pointer);
251
0
    free(pointer);
252
0
  }
253
0
}
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
0
{
260
0
  int total_len = sizeof(struct marshal_serialized) + (skip ? 0 : mi->size);
261
0
  struct marshal_serialized *serialized = buffer;
262
0
  struct gc_l *pointers = _pointers;
263
0
  int size, already, extra = 0;
264
0
  void *new;
265
0
  struct marshal_subinfo *current;
266
0
  struct gc *apointer;
267
268
0
  log_debug("marshal", "start unserialization of %s", mi->name);
269
270
0
  if (len < sizeof(struct marshal_serialized) || len < total_len) {
271
0
    log_warnx("marshal",
272
0
        "data to deserialize is too small (%zu) for structure %s", len,
273
0
        mi->name);
274
0
    return 0;
275
0
  }
276
277
  /* Initialize garbage collection */
278
0
  if (!pointers) {
279
0
    pointers = calloc(1, sizeof(struct gc_l));
280
0
    if (!pointers) {
281
0
      log_warnx("marshal",
282
0
          "unable to allocate memory for garbage collection");
283
0
      return 0;
284
0
    }
285
0
    TAILQ_INIT(pointers);
286
0
  }
287
288
  /* Special cases */
289
0
  size = mi->size;
290
0
  if (!strcmp(mi->name, "null string") || !strcmp(mi->name, "fixed string")) {
291
0
    switch (mi->name[0]) {
292
0
    case 'n':
293
0
      size = strnlen((char *)serialized->object,
294
0
           len - sizeof(struct marshal_serialized)) +
295
0
          1;
296
0
      break;
297
0
    case 'f':
298
0
      size = osize;
299
0
      extra = 1;
300
0
      break; /* The extra byte is to ensure that
301
          the string is null terminated. */
302
0
    }
303
0
    if (size > len - sizeof(struct marshal_serialized)) {
304
0
      log_warnx("marshal",
305
0
          "data to deserialize contains a string too long");
306
0
      total_len = 0;
307
0
      goto unmarshal_error;
308
0
    }
309
0
    total_len += size;
310
0
  }
311
312
  /* First, the main structure */
313
0
  if (!skip) {
314
0
    if ((*output = marshal_alloc(pointers, size + extra,
315
0
       serialized->orig)) == NULL) {
316
0
      log_warnx("marshal",
317
0
          "unable to allocate memory to unserialize structure %s",
318
0
          mi->name);
319
0
      total_len = 0;
320
0
      goto unmarshal_error;
321
0
    }
322
0
    memcpy(*output, serialized->object, size);
323
0
  }
324
325
  /* Then, each substructure */
326
0
  for (current = mi->pointers; current->mi; current++) {
327
0
    size_t sublen;
328
0
    size_t padlen;
329
0
    new = (unsigned char *)*output + current->offset;
330
0
    if (current->kind == ignore) {
331
0
      memset((unsigned char *)*output + current->offset, 0,
332
0
          sizeof(void *));
333
0
      continue;
334
0
    }
335
0
    if (current->kind == pointer) {
336
0
      if (*(void **)new == NULL) continue;
337
338
      /* Did we already see this reference? */
339
0
      already = 0;
340
0
      TAILQ_FOREACH (apointer, pointers, next)
341
0
        if (apointer->orig == *(void **)new) {
342
0
          memcpy((unsigned char *)*output +
343
0
            current->offset,
344
0
              &apointer->pointer, sizeof(void *));
345
0
          already = 1;
346
0
          break;
347
0
        }
348
0
      if (already) continue;
349
0
    }
350
    /* Deserialize */
351
0
    if (current->offset2)
352
0
      memcpy(&osize, (unsigned char *)*output + current->offset2,
353
0
          sizeof(int));
354
0
    padlen = ALIGNOF(struct marshal_serialized);
355
0
    padlen = (padlen - (total_len % padlen)) % padlen;
356
0
    if (len < total_len + padlen ||
357
0
        ((sublen = marshal_unserialize_(current->mi,
358
0
        (unsigned char *)buffer + total_len + padlen,
359
0
        len - total_len - padlen, &new, pointers,
360
0
        current->kind == substruct, osize)) == 0)) {
361
0
      log_warnx("marshal",
362
0
          "unable to serialize substructure %s for %s",
363
0
          current->mi->name, mi->name);
364
0
      total_len = 0;
365
0
      goto unmarshal_error;
366
0
    }
367
    /* Link the result */
368
0
    if (current->kind == pointer)
369
0
      memcpy((unsigned char *)*output + current->offset, &new,
370
0
          sizeof(void *));
371
0
    total_len += sublen + padlen;
372
0
  }
373
374
0
unmarshal_error:
375
0
  if (pointers && !_pointers) {
376
0
    marshal_free(pointers, (total_len > 0));
377
0
    free(pointers);
378
0
  }
379
0
  return total_len;
380
0
}