]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/ctl.c
Fix unaligned memory access in ctl.c using memcpy instead of pointer
[thirdparty/lldpd.git] / src / ctl.c
1 /*
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include "lldpd.h"
18
19 #include <unistd.h>
20 #include <errno.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24
25 int
26 ctl_create(char *name)
27 {
28 int s;
29 struct sockaddr_un su;
30 int rc;
31
32 if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
33 return -1;
34 su.sun_family = AF_UNIX;
35 strlcpy(su.sun_path, name, UNIX_PATH_MAX);
36 if (bind(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) {
37 rc = errno; close(s); errno = rc;
38 return -1;
39 }
40 if (listen(s, 5) == -1) {
41 rc = errno; close(s); errno = rc;
42 return -1;
43 }
44 return s;
45 }
46
47 int
48 ctl_connect(char *name)
49 {
50 int s;
51 struct sockaddr_un su;
52 int rc;
53
54 if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
55 return -1;
56 su.sun_family = AF_UNIX;
57 strlcpy(su.sun_path, name, UNIX_PATH_MAX);
58 if (connect(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) {
59 rc = errno;
60 LLOG_WARN("unable to connect to socket " LLDPD_CTL_SOCKET);
61 errno = rc; return -1;
62 }
63 return s;
64 }
65
66 int
67 ctl_accept(struct lldpd *cfg, int c)
68 {
69 int s;
70 struct lldpd_client *lc;
71 if ((s = accept(c, NULL, NULL)) == -1) {
72 LLOG_WARN("unable to accept connection from socket");
73 return -1;
74 }
75 if ((lc = (struct lldpd_client *)malloc(sizeof(
76 struct lldpd_client))) == NULL) {
77 LLOG_WARN("failed to allocate memory for new client");
78 close(s);
79 return -1;
80 }
81 lc->fd = s;
82 TAILQ_INSERT_TAIL(&cfg->g_clients, lc, next);
83 return 1;
84 }
85
86 void
87 ctl_msg_init(struct hmsg *t, enum hmsg_type type)
88 {
89 memset(t, 0, MAX_HMSGSIZE);
90 t->hdr.type = type;
91 t->hdr.len = 0;
92 t->hdr.pid = getpid();
93 }
94
95 int
96 ctl_msg_send(int fd, struct hmsg *t)
97 {
98 return write(fd, t, t->hdr.len + sizeof(struct hmsg_hdr));
99 }
100
101 int
102 ctl_msg_recv(int fd, struct hmsg *t)
103 {
104 int n;
105 if ((n = read(fd, t, MAX_HMSGSIZE)) == -1) {
106 return -1;
107 }
108 if (n < sizeof(struct hmsg_hdr)) {
109 LLOG_WARNX("message received too short");
110 errno = 0;
111 return -1;
112 }
113 if (n != sizeof(struct hmsg_hdr) + t->hdr.len) {
114 LLOG_WARNX("message from %d seems to be truncated (or too large)",
115 t->hdr.pid);
116 errno = 0;
117 return -1;
118 }
119 return 1;
120 }
121
122 int
123 ctl_close(struct lldpd *cfg, int c)
124 {
125 struct lldpd_client *client, *client_next;
126 for (client = TAILQ_FIRST(&cfg->g_clients);
127 client != NULL;
128 client = client_next) {
129 client_next = TAILQ_NEXT(client, next);
130 if (client->fd == c) {
131 close(client->fd);
132 TAILQ_REMOVE(&cfg->g_clients, client, next);
133 free(client);
134 return 1;
135 }
136 }
137 /* Not found */
138 return -1;
139 }
140
141 void
142 ctl_cleanup(char *name)
143 {
144 if (unlink(name) == -1)
145 LLOG_WARN("unable to unlink %s", name);
146 }
147
148 /* Packing/unpacking */
149
150 /* This structure is used to track memory allocation when unpacking */
151 struct gc {
152 TAILQ_ENTRY(gc) next;
153 void *pointer;
154 };
155 TAILQ_HEAD(gc_l, gc);
156
157 typedef struct { char c; int16_t x; } st_int16;
158 typedef struct { char c; int32_t x; } st_int32;
159 typedef struct { char c; void *x; } st_void_p;
160
161 #define INT16_ALIGN (sizeof(st_int16) - sizeof(int16_t))
162 #define INT32_ALIGN (sizeof(st_int32) - sizeof(int32_t))
163 #define VOID_P_ALIGN (sizeof(st_void_p) - sizeof(void *))
164
165 struct formatdef {
166 char format;
167 int size;
168 int alignment;
169 int (*pack)(struct hmsg*, void **, void *,
170 const struct formatdef *);
171 int (*unpack)(struct hmsg*, void **, void *,
172 const struct formatdef *, struct gc_l *);
173 };
174
175 /* void** is a pointer to a pointer to the end of struct hmsg*. It should be
176 * updated. void* is a pointer to the entity to pack */
177
178 static int
179 ctl_alloc_pointer(struct gc_l *pointers, void *pointer)
180 {
181 struct gc *gpointer;
182 if (pointers != NULL) {
183 if ((gpointer = (struct gc *)calloc(1,
184 sizeof(struct gc))) == NULL) {
185 LLOG_WARN("unable to allocate memory for garbage collector");
186 return -1;
187 }
188 gpointer->pointer = pointer;
189 TAILQ_INSERT_TAIL(pointers, gpointer, next);
190 }
191 return 0;
192 }
193
194 static void
195 ctl_free_pointers(struct gc_l *pointers, int listonly)
196 {
197 struct gc *pointer, *pointer_next;
198 for (pointer = TAILQ_FIRST(pointers);
199 pointer != NULL;
200 pointer = pointer_next) {
201 pointer_next = TAILQ_NEXT(pointer, next);
202 TAILQ_REMOVE(pointers, pointer, next);
203 if (!listonly)
204 free(pointer->pointer);
205 free(pointer);
206 }
207 }
208
209 static int
210 pack_copy(struct hmsg *h, void **p, void *s,
211 const struct formatdef *ct)
212 {
213 if (h->hdr.len + ct->size > MAX_HMSGSIZE - sizeof(struct hmsg_hdr)) {
214 LLOG_WARNX("message became too large");
215 return -1;
216 }
217 memcpy(*p, s, ct->size);
218 *p += ct->size;
219 h->hdr.len += ct->size;
220 return ct->size;
221 }
222
223 static int
224 unpack_copy(struct hmsg *h, void **p, void *s,
225 const struct formatdef *ct, struct gc_l *pointers)
226 {
227 memcpy(s, *p, ct->size);
228 *p += ct->size;
229 return ct->size;
230 }
231
232 static int
233 pack_string(struct hmsg *h, void **p, void *s,
234 const struct formatdef *ct)
235 {
236 int len, ss;
237 if ((*(char**)s) == NULL)
238 len = -1;
239 else
240 len = strlen(*(char**)s);
241 if (h->hdr.len + len + sizeof(int) > MAX_HMSGSIZE -
242 sizeof(struct hmsg_hdr)) {
243 LLOG_WARNX("message became too large");
244 return -1;
245 }
246 memcpy(*p, &len, sizeof(int));
247 *p += sizeof(int);
248 ss = sizeof(int);
249 if (len != -1) {
250 memcpy(*p, *(char **)s, len);
251 *p += len;
252 ss += len;
253 }
254 h->hdr.len += ss;
255 return ss;
256 }
257
258 static int
259 unpack_string(struct hmsg *h, void **p, void *s,
260 const struct formatdef *ct, struct gc_l *pointers)
261 {
262 char *string;
263 int len;
264 memcpy(&len, *p, sizeof(int));
265 *p += sizeof(int);
266 if (len == -1) {
267 string = NULL;
268 } else {
269 if ((string = (char *)calloc(1, len + 1)) == NULL) {
270 LLOG_WARNX("unable to allocate new string");
271 return -1;
272 }
273 if (ctl_alloc_pointer(pointers, string) == -1) {
274 free(string);
275 return -1;
276 }
277 memcpy(string, *p, len);
278 *p += len;
279 }
280 memcpy(s, &string, sizeof(char *));
281 return sizeof(char*);
282 }
283
284 static int
285 pack_chars(struct hmsg *h, void **p, void *s,
286 const struct formatdef *ct)
287 {
288 char *string;
289 int string_len;
290 string = *(char **)s;
291 s += sizeof(char *);
292 memcpy(&string_len, s, sizeof(int));
293
294 if (h->hdr.len + string_len + sizeof(int) > MAX_HMSGSIZE -
295 sizeof(struct hmsg_hdr)) {
296 LLOG_WARNX("message became too large");
297 return -1;
298 }
299 memcpy(*p, &string_len, sizeof(int));
300 *p += sizeof(int);
301 memcpy(*p, string, string_len);
302 *p += string_len;
303 h->hdr.len += sizeof(int) + string_len;
304 return sizeof(int) + string_len;
305 }
306
307 static int
308 unpack_chars(struct hmsg *h, void **p, void *s,
309 const struct formatdef *ct, struct gc_l *pointers)
310 {
311 char *string;
312 struct {
313 char *string;
314 int len;
315 } reals __attribute__ ((__packed__));
316 int len;
317 memcpy(&len, *p, sizeof(int));
318 *p += sizeof(int);
319 if ((string = (char *)malloc(len)) == NULL) {
320 LLOG_WARN("unable to allocate new string");
321 return -1;
322 }
323 if (ctl_alloc_pointer(pointers, string) == -1) {
324 free(string);
325 return -1;
326 }
327 memcpy(string, *p, len);
328 *p += len;
329 reals.string = string;
330 reals.len = len;
331 memcpy(s, &reals, sizeof(reals));
332 return sizeof(char*);
333 }
334
335 static int
336 pack_zero(struct hmsg *h, void **p, void *s,
337 const struct formatdef *ct)
338 {
339 if (h->hdr.len + ct->size > MAX_HMSGSIZE - sizeof(struct hmsg_hdr)) {
340 LLOG_WARNX("message became too large");
341 return -1;
342 }
343 memset(*p, 0, ct->size);
344 *p += ct->size;
345 h->hdr.len += ct->size;
346 return ct->size;
347 }
348
349 static struct formatdef conv_table[] = {
350 {'b', 1, 0,
351 pack_copy, unpack_copy},
352 {'w', 2, INT16_ALIGN,
353 pack_copy, unpack_copy},
354 {'l', 4, INT32_ALIGN,
355 pack_copy, unpack_copy},
356 /* Null terminated string */
357 {'s', sizeof(void*), VOID_P_ALIGN,
358 pack_string, unpack_string},
359 /* Pointer (is packed with 0) */
360 {'P', sizeof(void*), VOID_P_ALIGN,
361 pack_zero, unpack_copy},
362 /* A list (same as pointer), should be at the beginning */
363 {'L', sizeof(void*)*2, VOID_P_ALIGN,
364 pack_zero, unpack_copy},
365 /* Non null terminated string, followed by an int for the size */
366 {'C', sizeof(void*) + sizeof(int), VOID_P_ALIGN,
367 pack_chars, unpack_chars},
368 {0}
369 };
370
371 /* Lists can be packed only if the "next" member is the first one of the
372 * structure! No check is done for this. */
373 struct fakelist_m {
374 TAILQ_ENTRY(fakelist_m) next;
375 void *data;
376 };
377 TAILQ_HEAD(fakelist_l, fakelist_m);
378
379 static int ctl_msg_get_alignment(char *format)
380 {
381 char *f;
382 int maxalign = 0, align;
383 int paren = 0;
384 struct formatdef *ce;
385
386 /* We just want to get the maximum required alignment for the
387 * structure. Instead of going recursive, we just count parentheses to
388 * get the end of the structure. */
389 for (f = format; *f != 0; f++) {
390 if (*f == ')') {
391 paren--;
392 if (!paren)
393 return maxalign;
394 continue;
395 } else if (*f == '(') {
396 paren++;
397 continue;
398 } else {
399 for (ce = conv_table;
400 (ce->format != 0) && (ce->format != *f);
401 ce++);
402 align = ce->alignment;
403 }
404 if (align != 0)
405 maxalign = (maxalign>align)?maxalign:align;
406 }
407 if (paren)
408 LLOG_WARNX("unbalanced parenthesis in format '%s'",
409 format);
410 return maxalign;
411 }
412
413 /* Define a stack of align values */
414 struct stack_align {
415 SLIST_ENTRY(stack_align) next;
416 int align;
417 };
418
419 static int
420 ctl_msg_packunpack_structure(char *format, void *structure, unsigned int size,
421 struct hmsg *h, void **p, struct gc_l *pointers, int pack)
422 {
423 char *f;
424 struct formatdef *ce = NULL;
425 unsigned int csize = 0;
426 uintptr_t offset;
427 struct stack_align *align, *align_next;
428 int talign;
429 SLIST_HEAD(, stack_align) aligns;
430
431 SLIST_INIT(&aligns);
432 for (f = format; *f != 0; f++) {
433 /* If we have a substructure, when entering into the structure,
434 * we get the alignment and push it to the stack. When exiting
435 * the structure, we pop the alignment from the stack and we do
436 * the padding. This means that the whole structure should be
437 * enclosed into parentheses, otherwise the padding won't
438 * occur. */
439 ce = NULL;
440 if (*f == '(') {
441 /* We need to align, compute the needed alignment */
442 if ((align = calloc(1,
443 sizeof(struct stack_align))) == NULL) {
444 LLOG_WARN("unable to allocate memory "
445 "for alignment stack");
446 goto packunpack_error;
447 }
448 talign = align->align = ctl_msg_get_alignment(f);
449 SLIST_INSERT_HEAD(&aligns, align, next);
450 } else if (*f == ')') {
451 /* We need to pad, retrieve the needed alignment */
452 align = SLIST_FIRST(&aligns);
453 talign = align->align;
454 align_next = SLIST_NEXT(align, next);
455 SLIST_REMOVE_HEAD(&aligns, next);
456 free(align);
457 } else {
458 for (ce = conv_table;
459 (ce->format != 0) && (ce->format != *f);
460 ce++);
461 if (ce->format != *f) {
462 LLOG_WARNX("unknown format char %c", *f);
463 goto packunpack_error;
464 }
465 talign = ce->alignment;
466 }
467
468 /* Align the structure member */
469 if (talign != 0) {
470 offset = (uintptr_t)structure % talign;
471 if (offset != 0) {
472 structure += talign - offset;
473 csize += talign - offset;
474 }
475 }
476
477 if (!ce) continue;
478
479 /* Check that the size is still ok */
480 csize += ce->size;
481 if (csize > size) {
482 LLOG_WARNX("size of structure is too small for given "
483 "format (%d vs %d)", size, csize);
484 goto packunpack_error;
485 }
486
487 /* Pack or unpack */
488 if (pack) {
489 if (ce->pack(h, p, structure, ce) == -1) {
490 LLOG_WARNX("error while packing %c in %s", *f,
491 format);
492 goto packunpack_error;
493 }
494 } else {
495 if (ce->unpack(h, p, structure, ce, pointers) == -1) {
496 LLOG_WARNX("error while unpacking %c", *f);
497 goto packunpack_error;
498 }
499 }
500 structure += ce->size;
501 }
502
503 if (size < csize) {
504 LLOG_WARNX("size of structure does not match its "
505 "declaration (%d vs %d)", size, csize);
506 goto packunpack_error;
507 }
508 if (!SLIST_EMPTY(&aligns)) {
509 LLOG_WARNX("format is badly balanced ('%s')", format);
510 goto packunpack_error;
511 }
512 return 0;
513
514 packunpack_error:
515 for (align = SLIST_FIRST(&aligns);
516 align != NULL;
517 align = align_next) {
518 align_next = SLIST_NEXT(align, next);
519 SLIST_REMOVE_HEAD(&aligns, next);
520 free(align);
521 }
522 return -1;
523
524 }
525
526 int
527 ctl_msg_pack_structure(char *format, void *structure, unsigned int size,
528 struct hmsg *h, void **p)
529 {
530 return ctl_msg_packunpack_structure(format, structure, size, h, p, NULL, 1);
531 }
532
533 int
534 ctl_msg_unpack_structure(char *format, void *structure, unsigned int size,
535 struct hmsg *h, void **p)
536 {
537 struct gc_l pointers;
538 int rc;
539 TAILQ_INIT(&pointers);
540 if ((rc = ctl_msg_packunpack_structure(format, structure, size,
541 h, p, &pointers, 0)) == -1) {
542 LLOG_WARNX("unable to unpack structure, freeing");
543 ctl_free_pointers(&pointers, 0);
544 return -1;
545 }
546 ctl_free_pointers(&pointers, 1);
547 return rc;
548 }
549
550 int
551 ctl_msg_pack_list(char *format, void *list, unsigned int size, struct hmsg *h, void **p)
552 {
553 struct fakelist_m *member;
554 struct fakelist_l *flist = (struct fakelist_l *)list;
555 TAILQ_FOREACH(member, flist, next) {
556 if (ctl_msg_pack_structure(format, member, size, h, p) == -1) {
557 LLOG_WARNX("error while packing list, aborting");
558 return -1;
559 }
560 }
561 return 0;
562 }
563
564 int
565 ctl_msg_unpack_list(char *format, void *list, unsigned int size, struct hmsg *h, void **p)
566 {
567 struct fakelist_m *member, *member_next;
568 struct gc_l pointers;
569 struct fakelist_l *flist = (struct fakelist_l *)list;
570 TAILQ_INIT(flist);
571 TAILQ_INIT(&pointers);
572 while (*p - (void *)h - sizeof(struct hmsg_hdr) < h->hdr.len) {
573 if ((member = calloc(1, size)) == NULL) {
574 LLOG_WARN("unable to allocate memory for structure");
575 return -1;
576 }
577 if (ctl_msg_packunpack_structure(format, member, size,
578 h, p, &pointers, 0) == -1) {
579 LLOG_WARNX("unable to unpack list, aborting");
580 free(member);
581 /* Free each list member */
582 for (member = TAILQ_FIRST(flist);
583 member != NULL;
584 member = member_next) {
585 member_next = TAILQ_NEXT(member, next);
586 TAILQ_REMOVE(flist, member, next);
587 free(member);
588 }
589 ctl_free_pointers(&pointers, 0);
590 return -1;
591 }
592 TAILQ_INSERT_TAIL(flist, member, next);
593 }
594 ctl_free_pointers(&pointers, 1);
595 return 0;
596 }