]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/libsystemd/sd-bus/bus-error.c
fuzz: skip bus error map in bus_error_name_to_errno()
[thirdparty/systemd.git] / src / libsystemd / sd-bus / bus-error.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
de1c301e
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2013 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
21#include <errno.h>
de1c301e
LP
22#include <stdarg.h>
23#include <stdbool.h>
de1c301e 24#include <stdio.h>
07630cea
LP
25#include <stdlib.h>
26#include <string.h>
de1c301e
LP
27
28#include "sd-bus.h"
07630cea 29
b5efdb8a 30#include "alloc-util.h"
cf0fbc49 31#include "bus-error.h"
07630cea
LP
32#include "errno-list.h"
33#include "string-util.h"
34#include "util.h"
de1c301e 35
96aad8d1 36BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_standard_errors[] = {
5f86c1f4
LP
37 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Failed", EACCES),
38 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoMemory", ENOMEM),
39 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ServiceUnknown", EHOSTUNREACH),
40 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NameHasNoOwner", ENXIO),
41 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoReply", ETIMEDOUT),
42 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.IOError", EIO),
43 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.BadAddress", EADDRNOTAVAIL),
15411c0c 44 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NotSupported", EOPNOTSUPP),
5f86c1f4
LP
45 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.LimitsExceeded", ENOBUFS),
46 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AccessDenied", EACCES),
47 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AuthFailed", EACCES),
48 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InteractiveAuthorizationRequired", EACCES),
49 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoServer", EHOSTDOWN),
50 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Timeout", ETIMEDOUT),
51 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.NoNetwork", ENONET),
52 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.AddressInUse", EADDRINUSE),
53 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.Disconnected", ECONNRESET),
54 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidArgs", EINVAL),
55 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileNotFound", ENOENT),
56 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.FileExists", EEXIST),
57 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownMethod", EBADR),
58 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownObject", EBADR),
59 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownInterface", EBADR),
60 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnknownProperty", EBADR),
61 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.PropertyReadOnly", EROFS),
62 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.UnixProcessIdUnknown", ESRCH),
63 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidSignature", EINVAL),
64 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InconsistentMessage", EBADMSG),
65 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.TimedOut", ETIMEDOUT),
66 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleInvalid", EINVAL),
67 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.InvalidFileContent", EINVAL),
68 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.MatchRuleNotFound", ENOENT),
69 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown", ESRCH),
70 SD_BUS_ERROR_MAP("org.freedesktop.DBus.Error.ObjectPathInUse", EBUSY),
71 SD_BUS_ERROR_MAP_END
4a0a7417
ZJS
72};
73
780950a2
DH
74/* GCC maps this magically to the beginning and end of the BUS_ERROR_MAP section */
75extern const sd_bus_error_map __start_BUS_ERROR_MAP[];
76extern const sd_bus_error_map __stop_BUS_ERROR_MAP[];
4a0a7417 77
5f86c1f4
LP
78/* Additional maps registered with sd_bus_error_add_map() are in this
79 * NULL terminated array */
80static const sd_bus_error_map **additional_error_maps = NULL;
4a0a7417 81
780896a4 82static int bus_error_name_to_errno(const char *name) {
5f86c1f4 83 const sd_bus_error_map **map, *m;
780896a4
LP
84 const char *p;
85 int r;
86
35460afc
LP
87 if (!name)
88 return EINVAL;
780896a4 89
763e20e6 90 p = startswith(name, "System.Error.");
780896a4
LP
91 if (p) {
92 r = errno_from_name(p);
d469dd44 93 if (r < 0)
780896a4
LP
94 return EIO;
95
96 return r;
97 }
98
d469dd44
ZJS
99 if (additional_error_maps)
100 for (map = additional_error_maps; *map; map++)
5f86c1f4
LP
101 for (m = *map;; m++) {
102 /* For additional error maps the end marker is actually the end marker */
103 if (m->code == BUS_ERROR_MAP_END_MARKER)
104 break;
105
106 if (streq(m->name, name))
107 return m->code;
108 }
5f86c1f4
LP
109
110 m = __start_BUS_ERROR_MAP;
af7bce41 111#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
5f86c1f4
LP
112 while (m < __stop_BUS_ERROR_MAP) {
113 /* For magic ELF error maps, the end marker might
114 * appear in the middle of things, since multiple maps
115 * might appear in the same section. Hence, let's skip
d469dd44 116 * over it, but realign the pointer to the next 8 byte
5f86c1f4
LP
117 * boundary, which is the selected alignment for the
118 * arrays. */
119 if (m->code == BUS_ERROR_MAP_END_MARKER) {
120 m = ALIGN8_PTR(m+1);
121 continue;
122 }
123
124 if (streq(m->name, name))
125 return m->code;
126
127 m++;
128 }
af7bce41 129#endif
5f86c1f4
LP
130
131 return EIO;
780896a4
LP
132}
133
134static sd_bus_error errno_to_bus_error_const(int error) {
135
136 if (error < 0)
137 error = -error;
138
139 switch (error) {
140
141 case ENOMEM:
142 return BUS_ERROR_OOM;
143
144 case EPERM:
145 case EACCES:
146 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ACCESS_DENIED, "Access denied");
147
148 case EINVAL:
149 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INVALID_ARGS, "Invalid argument");
150
151 case ESRCH:
152 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_UNIX_PROCESS_ID_UNKNOWN, "No such process");
153
154 case ENOENT:
155 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_NOT_FOUND, "File not found");
156
157 case EEXIST:
158 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_FILE_EXISTS, "File exists");
159
160 case ETIMEDOUT:
161 case ETIME:
162 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_TIMEOUT, "Timed out");
163
164 case EIO:
165 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_IO_ERROR, "Input/output error");
166
167 case ENETRESET:
168 case ECONNABORTED:
169 case ECONNRESET:
170 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_DISCONNECTED, "Disconnected");
171
15411c0c 172 case EOPNOTSUPP:
780896a4
LP
173 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_NOT_SUPPORTED, "Not supported");
174
175 case EADDRNOTAVAIL:
176 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_BAD_ADDRESS, "Address not available");
177
178 case ENOBUFS:
179 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_LIMITS_EXCEEDED, "Limits exceeded");
180
181 case EADDRINUSE:
182 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_ADDRESS_IN_USE, "Address in use");
183
184 case EBADMSG:
185 return SD_BUS_ERROR_MAKE_CONST(SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Inconsistent message");
186 }
187
188 return SD_BUS_ERROR_NULL;
189}
190
191static int errno_to_bus_error_name_new(int error, char **ret) {
192 const char *name;
193 char *n;
194
195 if (error < 0)
196 error = -error;
197
198 name = errno_to_name(error);
199 if (!name)
200 return 0;
201
763e20e6 202 n = strappend("System.Error.", name);
780896a4
LP
203 if (!n)
204 return -ENOMEM;
205
206 *ret = n;
207 return 1;
208}
209
fbfa72b0
LP
210bool bus_error_is_dirty(sd_bus_error *e) {
211 if (!e)
780896a4 212 return false;
fbfa72b0 213
79f8d3d2 214 return e->name || e->message || e->_need_free != 0;
fbfa72b0
LP
215}
216
d9f644e2 217_public_ void sd_bus_error_free(sd_bus_error *e) {
de1c301e
LP
218 if (!e)
219 return;
220
79f8d3d2 221 if (e->_need_free > 0) {
de1c301e
LP
222 free((void*) e->name);
223 free((void*) e->message);
224 }
225
226 e->name = e->message = NULL;
79f8d3d2 227 e->_need_free = 0;
de1c301e
LP
228}
229
d9f644e2 230_public_ int sd_bus_error_set(sd_bus_error *e, const char *name, const char *message) {
29ddb38f 231
780896a4 232 if (!name)
29ddb38f 233 return 0;
780896a4
LP
234 if (!e)
235 goto finish;
40ca29a1
LP
236
237 assert_return(!bus_error_is_dirty(e), -EINVAL);
29ddb38f 238
780896a4
LP
239 e->name = strdup(name);
240 if (!e->name) {
241 *e = BUS_ERROR_OOM;
29ddb38f 242 return -ENOMEM;
29ddb38f
LP
243 }
244
780896a4
LP
245 if (message)
246 e->message = strdup(message);
247
79f8d3d2 248 e->_need_free = 1;
29ddb38f 249
780896a4
LP
250finish:
251 return -bus_error_name_to_errno(name);
29ddb38f
LP
252}
253
40ca29a1 254int bus_error_setfv(sd_bus_error *e, const char *name, const char *format, va_list ap) {
de1c301e 255
780896a4 256 if (!name)
de1c301e 257 return 0;
40ca29a1 258
d469dd44
ZJS
259 if (e) {
260 assert_return(!bus_error_is_dirty(e), -EINVAL);
de1c301e 261
d469dd44
ZJS
262 e->name = strdup(name);
263 if (!e->name) {
264 *e = BUS_ERROR_OOM;
265 return -ENOMEM;
266 }
de1c301e 267
d469dd44
ZJS
268 /* If we hit OOM on formatting the pretty message, we ignore
269 * this, since we at least managed to write the error name */
270 if (format)
271 (void) vasprintf((char**) &e->message, format, ap);
780896a4 272
d469dd44
ZJS
273 e->_need_free = 1;
274 }
de1c301e 275
780896a4 276 return -bus_error_name_to_errno(name);
de1c301e
LP
277}
278
d9f644e2 279_public_ int sd_bus_error_setf(sd_bus_error *e, const char *name, const char *format, ...) {
40ca29a1
LP
280
281 if (format) {
282 int r;
283 va_list ap;
284
285 va_start(ap, format);
286 r = bus_error_setfv(e, name, format, ap);
287 va_end(ap);
288
289 return r;
290 }
291
292 return sd_bus_error_set(e, name, NULL);
293}
294
d9f644e2 295_public_ int sd_bus_error_copy(sd_bus_error *dest, const sd_bus_error *e) {
89ffcd2a 296
de1c301e
LP
297 if (!sd_bus_error_is_set(e))
298 return 0;
780896a4
LP
299 if (!dest)
300 goto finish;
de1c301e 301
40ca29a1
LP
302 assert_return(!bus_error_is_dirty(dest), -EINVAL);
303
79f8d3d2
LP
304 /*
305 * _need_free < 0 indicates that the error is temporarily const, needs deep copying
306 * _need_free == 0 indicates that the error is perpetually const, needs no deep copying
307 * _need_free > 0 indicates that the error is fully dynamic, needs deep copying
308 */
309
310 if (e->_need_free == 0)
780896a4
LP
311 *dest = *e;
312 else {
313 dest->name = strdup(e->name);
314 if (!dest->name) {
315 *dest = BUS_ERROR_OOM;
de1c301e 316 return -ENOMEM;
de1c301e 317 }
780896a4
LP
318
319 if (e->message)
320 dest->message = strdup(e->message);
321
79f8d3d2 322 dest->_need_free = 1;
89ffcd2a 323 }
de1c301e 324
780896a4
LP
325finish:
326 return -bus_error_name_to_errno(e->name);
de1c301e
LP
327}
328
d9f644e2 329_public_ int sd_bus_error_set_const(sd_bus_error *e, const char *name, const char *message) {
780896a4 330 if (!name)
40ca29a1 331 return 0;
780896a4
LP
332 if (!e)
333 goto finish;
40ca29a1
LP
334
335 assert_return(!bus_error_is_dirty(e), -EINVAL);
de1c301e 336
14c24659 337 *e = SD_BUS_ERROR_MAKE_CONST(name, message);
780896a4
LP
338
339finish:
340 return -bus_error_name_to_errno(name);
de1c301e
LP
341}
342
d9f644e2 343_public_ int sd_bus_error_is_set(const sd_bus_error *e) {
de1c301e
LP
344 if (!e)
345 return 0;
346
fbfa72b0 347 return !!e->name;
de1c301e
LP
348}
349
d9f644e2 350_public_ int sd_bus_error_has_name(const sd_bus_error *e, const char *name) {
de1c301e
LP
351 if (!e)
352 return 0;
353
354 return streq_ptr(e->name, name);
355}
356
d9f644e2 357_public_ int sd_bus_error_get_errno(const sd_bus_error* e) {
eb01ba5d 358 if (!e)
780896a4 359 return 0;
40ca29a1 360
bc6a6232
LP
361 if (!e->name)
362 return 0;
363
780896a4 364 return bus_error_name_to_errno(e->name);
de1c301e
LP
365}
366
780896a4 367static void bus_error_strerror(sd_bus_error *e, int error) {
40ca29a1 368 size_t k = 64;
780896a4 369 char *m;
40ca29a1 370
780896a4 371 assert(e);
eb01ba5d 372
40ca29a1
LP
373 for (;;) {
374 char *x;
eb01ba5d 375
40ca29a1
LP
376 m = new(char, k);
377 if (!m)
780896a4 378 return;
40ca29a1
LP
379
380 errno = 0;
381 x = strerror_r(error, m, k);
382 if (errno == ERANGE || strlen(x) >= k - 1) {
383 free(m);
384 k *= 2;
385 continue;
386 }
eb01ba5d 387
04c553e3 388 if (errno) {
40ca29a1 389 free(m);
780896a4 390 return;
40ca29a1 391 }
eb01ba5d 392
780896a4 393 if (x == m) {
79f8d3d2 394 if (e->_need_free > 0) {
780896a4
LP
395 /* Error is already dynamic, let's just update the message */
396 free((char*) e->message);
397 e->message = x;
398
399 } else {
400 char *t;
401 /* Error was const so far, let's make it dynamic, if we can */
402
403 t = strdup(e->name);
404 if (!t) {
405 free(m);
406 return;
407 }
408
79f8d3d2 409 e->_need_free = 1;
780896a4
LP
410 e->name = t;
411 e->message = x;
412 }
413 } else {
40ca29a1 414 free(m);
eb01ba5d 415
79f8d3d2 416 if (e->_need_free > 0) {
780896a4 417 char *t;
eb01ba5d 418
780896a4
LP
419 /* Error is dynamic, let's hence make the message also dynamic */
420 t = strdup(x);
421 if (!t)
422 return;
eb01ba5d 423
780896a4
LP
424 free((char*) e->message);
425 e->message = t;
426 } else {
427 /* Error is const, hence we can just override */
428 e->message = x;
429 }
430 }
40ca29a1 431
780896a4
LP
432 return;
433 }
40ca29a1
LP
434}
435
780896a4 436_public_ int sd_bus_error_set_errno(sd_bus_error *e, int error) {
40ca29a1
LP
437
438 if (error < 0)
439 error = -error;
440
780896a4
LP
441 if (!e)
442 return -error;
443 if (error == 0)
444 return -error;
40ca29a1 445
780896a4 446 assert_return(!bus_error_is_dirty(e), -EINVAL);
40ca29a1 447
780896a4
LP
448 /* First, try a const translation */
449 *e = errno_to_bus_error_const(error);
40ca29a1 450
780896a4
LP
451 if (!sd_bus_error_is_set(e)) {
452 int k;
40ca29a1 453
780896a4 454 /* If that didn't work, try a dynamic one. */
40ca29a1 455
780896a4
LP
456 k = errno_to_bus_error_name_new(error, (char**) &e->name);
457 if (k > 0)
79f8d3d2 458 e->_need_free = 1;
780896a4
LP
459 else if (k < 0) {
460 *e = BUS_ERROR_OOM;
461 return -error;
462 } else
463 *e = BUS_ERROR_FAILED;
40ca29a1
LP
464 }
465
780896a4
LP
466 /* Now, fill in the message from strerror() if we can */
467 bus_error_strerror(e, error);
468 return -error;
40ca29a1
LP
469}
470
07a0d22f 471_public_ int sd_bus_error_set_errnofv(sd_bus_error *e, int error, const char *format, va_list ap) {
17972801 472 PROTECT_ERRNO;
40ca29a1
LP
473 int r;
474
475 if (error < 0)
476 error = -error;
477
478 if (!e)
80c19740 479 return -error;
780896a4
LP
480 if (error == 0)
481 return 0;
40ca29a1
LP
482
483 assert_return(!bus_error_is_dirty(e), -EINVAL);
484
780896a4
LP
485 /* First, try a const translation */
486 *e = errno_to_bus_error_const(error);
487
488 if (!sd_bus_error_is_set(e)) {
489 int k;
490
491 /* If that didn't work, try a dynamic one */
492
493 k = errno_to_bus_error_name_new(error, (char**) &e->name);
494 if (k > 0)
79f8d3d2 495 e->_need_free = 1;
780896a4
LP
496 else if (k < 0) {
497 *e = BUS_ERROR_OOM;
498 return -ENOMEM;
499 } else
500 *e = BUS_ERROR_FAILED;
501 }
40ca29a1
LP
502
503 if (format) {
780896a4 504 char *m;
40ca29a1 505
17972801 506 /* Then, let's try to fill in the supplied message */
40ca29a1 507
17972801 508 errno = error; /* Make sure that %m resolves to the specified error */
780896a4
LP
509 r = vasprintf(&m, format, ap);
510 if (r >= 0) {
511
79f8d3d2 512 if (e->_need_free <= 0) {
780896a4
LP
513 char *t;
514
515 t = strdup(e->name);
516 if (t) {
79f8d3d2 517 e->_need_free = 1;
780896a4
LP
518 e->name = t;
519 e->message = m;
520 return -error;
521 }
522
523 free(m);
524 } else {
525 free((char*) e->message);
526 e->message = m;
527 return -error;
528 }
40ca29a1 529 }
eb01ba5d 530 }
de1c301e 531
780896a4
LP
532 /* If that didn't work, use strerror() for the message */
533 bus_error_strerror(e, error);
534 return -error;
40ca29a1
LP
535}
536
d9f644e2 537_public_ int sd_bus_error_set_errnof(sd_bus_error *e, int error, const char *format, ...) {
40ca29a1
LP
538 int r;
539
80c19740
LP
540 if (error < 0)
541 error = -error;
542
40ca29a1 543 if (!e)
80c19740 544 return -error;
780896a4
LP
545 if (error == 0)
546 return 0;
40ca29a1
LP
547
548 assert_return(!bus_error_is_dirty(e), -EINVAL);
549
550 if (format) {
551 va_list ap;
552
553 va_start(ap, format);
07a0d22f 554 r = sd_bus_error_set_errnofv(e, error, format, ap);
40ca29a1
LP
555 va_end(ap);
556
557 return r;
558 }
559
560 return sd_bus_error_set_errno(e, error);
de1c301e 561}
e3017af9
LP
562
563const char *bus_error_message(const sd_bus_error *e, int error) {
40ca29a1 564
c9b6cb28 565 if (e) {
b938cb90 566 /* Sometimes, the D-Bus server is a little bit too verbose with
c9b6cb28
LP
567 * its error messages, so let's override them here */
568 if (sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED))
569 return "Access denied";
570
571 if (e->message)
572 return e->message;
573 }
574
40ca29a1
LP
575 if (error < 0)
576 error = -error;
577
e3017af9
LP
578 return strerror(error);
579}
5f86c1f4 580
45ea6583
ZJS
581static bool map_ok(const sd_bus_error_map *map) {
582 for (; map->code != BUS_ERROR_MAP_END_MARKER; map++)
583 if (!map->name || map->code <=0)
584 return false;
585 return true;
586}
587
5f86c1f4
LP
588_public_ int sd_bus_error_add_map(const sd_bus_error_map *map) {
589 const sd_bus_error_map **maps = NULL;
590 unsigned n = 0;
591
592 assert_return(map, -EINVAL);
45ea6583 593 assert_return(map_ok(map), -EINVAL);
5f86c1f4 594
45ea6583
ZJS
595 if (additional_error_maps)
596 for (; additional_error_maps[n] != NULL; n++)
5f86c1f4
LP
597 if (additional_error_maps[n] == map)
598 return 0;
5f86c1f4 599
aa484f35 600 maps = reallocarray(additional_error_maps, n + 2, sizeof(struct sd_bus_error_map*));
5f86c1f4
LP
601 if (!maps)
602 return -ENOMEM;
603
5f86c1f4
LP
604 maps[n] = map;
605 maps[n+1] = NULL;
606
607 additional_error_maps = maps;
608 return 1;
609}