• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 17992912793

24 Sep 2025 07:15PM UTC coverage: 72.205% (-0.08%) from 72.283%
17992912793

push

github

web-flow
libblkid → turn into dlopen() dependency (#39084)

Split out of #38861

153 of 207 new or added lines in 10 files covered. (73.91%)

1717 existing lines in 53 files now uncovered.

302842 of 419419 relevant lines covered (72.21%)

1052332.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.46
/src/boot/efi-string.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "efi-string.h"
4

5
#if SD_BOOT
6
#  include "proto/simple-text-io.h"
7
#  include "util.h"
8
#else
9
#  include <stdlib.h>
10

11
#  include "alloc-util.h"
12
#  define xnew(t, n) ASSERT_SE_PTR(new(t, n))
13
#  define xmalloc(n) ASSERT_SE_PTR(malloc(n))
14
#endif
15

16
/* String functions for both char and char16_t that should behave the same way as their respective
17
 * counterpart in userspace. Where it makes sense, these accept NULL and do something sensible whereas
18
 * userspace does not allow for this (strlen8(NULL) returns 0 like strlen_ptr(NULL) for example). To make it
19
 * easier to tell in code which kind of string they work on, we use 8/16 suffixes. This also makes is easier
20
 * to unit test them. */
21

22
#define DEFINE_STRNLEN(type, name)             \
23
        size_t name(const type *s, size_t n) { \
24
                if (!s)                        \
25
                        return 0;              \
26
                                               \
27
                size_t len = 0;                \
28
                while (len < n && *s) {        \
29
                        s++;                   \
30
                        len++;                 \
31
                }                              \
32
                                               \
33
                return len;                    \
34
        }
35

36
DEFINE_STRNLEN(char, strnlen8);
1,726✔
37
DEFINE_STRNLEN(char16_t, strnlen16);
140✔
38

39
#define TOLOWER(c)                                                \
40
        ({                                                        \
41
                typeof(c) _c = (c);                               \
42
                (_c >= 'A' && _c <= 'Z') ? _c + ('a' - 'A') : _c; \
43
        })
44

45
#define DEFINE_STRTOLOWER(type, name)                \
46
        type* name(type *s) {                        \
47
                if (!s)                              \
48
                        return NULL;                 \
49
                for (type *p = s; *p; p++)           \
50
                        *p = TOLOWER(*p);            \
51
                return s;                            \
52
        }
53

54
DEFINE_STRTOLOWER(char, strtolower8);
15✔
55
DEFINE_STRTOLOWER(char16_t, strtolower16);
15✔
56

57
#define DEFINE_STRNCASECMP(type, name, tolower)              \
58
        int name(const type *s1, const type *s2, size_t n) { \
59
                if (!s1 || !s2)                              \
60
                        return CMP(s1, s2);                  \
61
                                                             \
62
                while (n > 0) {                              \
63
                        type c1 = *s1, c2 = *s2;             \
64
                        if (tolower) {                       \
65
                                c1 = TOLOWER(c1);            \
66
                                c2 = TOLOWER(c2);            \
67
                        }                                    \
68
                        if (!c1 || c1 != c2)                 \
69
                                return CMP(c1, c2);          \
70
                                                             \
71
                        s1++;                                \
72
                        s2++;                                \
73
                        n--;                                 \
74
                }                                            \
75
                                                             \
76
                return 0;                                    \
77
        }
78

79
DEFINE_STRNCASECMP(char, strncmp8, false);
425✔
80
DEFINE_STRNCASECMP(char16_t, strncmp16, false);
204✔
81
DEFINE_STRNCASECMP(char, strncasecmp8, true);
1,703✔
82
DEFINE_STRNCASECMP(char16_t, strncasecmp16, true);
45✔
83

84
#define DEFINE_STRCPY(type, name)                                     \
85
        type *name(type * restrict dest, const type * restrict src) { \
86
                type *ret = ASSERT_PTR(dest);                         \
87
                                                                      \
88
                if (!src) {                                           \
89
                        *dest = '\0';                                 \
90
                        return ret;                                   \
91
                }                                                     \
92
                                                                      \
93
                while (*src) {                                        \
94
                        *dest = *src;                                 \
95
                        dest++;                                       \
96
                        src++;                                        \
97
                }                                                     \
98
                                                                      \
99
                *dest = '\0';                                         \
100
                return ret;                                           \
101
        }
102

103
DEFINE_STRCPY(char, strcpy8);
8✔
104
DEFINE_STRCPY(char16_t, strcpy16);
8✔
105

106
#define DEFINE_STRCHR(type, name)                  \
107
        type *name(const type *s, type c) {        \
108
                if (!s)                            \
109
                        return NULL;               \
110
                                                   \
111
                while (*s) {                       \
112
                        if (*s == c)               \
113
                                return (type *) s; \
114
                        s++;                       \
115
                }                                  \
116
                                                   \
117
                return c ? NULL : (type *) s;      \
118
        }
119

120
DEFINE_STRCHR(char, strchr8);
2,139✔
121
DEFINE_STRCHR(char16_t, strchr16);
167✔
122

123
#define DEFINE_STRNDUP(type, name, len_func)              \
124
        type *name(const type *s, size_t n) {             \
125
                if (!s)                                   \
126
                        return NULL;                      \
127
                                                          \
128
                size_t len = len_func(s, n);              \
129
                size_t size = len * sizeof(type);         \
130
                                                          \
131
                type *dup = xmalloc(size + sizeof(type)); \
132
                if (size > 0)                             \
133
                        memcpy(dup, s, size);             \
134
                dup[len] = '\0';                          \
135
                                                          \
136
                return dup;                               \
137
        }
138

139
DEFINE_STRNDUP(char, xstrndup8, strnlen8);
10✔
140
DEFINE_STRNDUP(char16_t, xstrndup16, strnlen16);
11✔
141

142
static unsigned utf8_to_unichar(const char *utf8, size_t n, char32_t *c) {
227✔
143
        char32_t unichar;
227✔
144
        unsigned len;
227✔
145

146
        assert(utf8);
227✔
147
        assert(c);
227✔
148

149
        if (!(utf8[0] & 0x80)) {
227✔
150
                *c = utf8[0];
220✔
151
                return 1;
220✔
152
        } else if ((utf8[0] & 0xe0) == 0xc0) {
7✔
153
                len = 2;
4✔
154
                unichar = utf8[0] & 0x1f;
4✔
155
        } else if ((utf8[0] & 0xf0) == 0xe0) {
3✔
156
                len = 3;
1✔
157
                unichar = utf8[0] & 0x0f;
1✔
158
        } else if ((utf8[0] & 0xf8) == 0xf0) {
2✔
159
                len = 4;
2✔
160
                unichar = utf8[0] & 0x07;
2✔
161
        } else if ((utf8[0] & 0xfc) == 0xf8) {
×
162
                len = 5;
×
163
                unichar = utf8[0] & 0x03;
×
164
        } else if ((utf8[0] & 0xfe) == 0xfc) {
×
165
                len = 6;
×
166
                unichar = utf8[0] & 0x01;
×
167
        } else {
168
                *c = UINT32_MAX;
×
169
                return 1;
×
170
        }
171

172
        if (len > n) {
7✔
173
                *c = UINT32_MAX;
1✔
174
                return len;
1✔
175
        }
176

177
        for (unsigned i = 1; i < len; i++) {
17✔
178
                if ((utf8[i] & 0xc0) != 0x80) {
11✔
179
                        *c = UINT32_MAX;
×
180
                        return len;
×
181
                }
182
                unichar <<= 6;
11✔
183
                unichar |= utf8[i] & 0x3f;
11✔
184
        }
185

186
        *c = unichar;
6✔
187
        return len;
6✔
188
}
189

190
/* Convert UTF-8 to UCS-2, skipping any invalid or short byte sequences. */
191
char16_t *xstrn8_to_16(const char *str8, size_t n) {
32✔
192
        assert(str8 || n == 0);
32✔
193

194
        if (n == SIZE_MAX)
32✔
195
                n = strlen8(str8);
2✔
196

197
        size_t i = 0;
32✔
198
        char16_t *str16 = xnew(char16_t, n + 1);
32✔
199

200
        while (n > 0 && *str8 != '\0') {
259✔
201
                char32_t unichar;
227✔
202

203
                size_t utf8len = utf8_to_unichar(str8, n, &unichar);
227✔
204
                str8 += utf8len;
227✔
205
                n = LESS_BY(n, utf8len);
227✔
206

207
                switch (unichar) {
227✔
208
                case 0 ... 0xd7ffU:
224✔
209
                case 0xe000U ... 0xffffU:
210
                        str16[i++] = unichar;
224✔
211
                        break;
224✔
212
                }
213
        }
214

215
        str16[i] = u'\0';
32✔
216
        return str16;
32✔
217
}
218

219
char *xstrn16_to_ascii(const char16_t *str16, size_t n) {
8✔
220
        assert(str16 || n == 0);
8✔
221

222
        if (n == SIZE_MAX)
8✔
223
                n = strlen16(str16);
3✔
224

225
        _cleanup_free_ char *str8 = xnew(char, n + 1);
8✔
226

227
        size_t i = 0;
228
        while (n > 0 && *str16 != u'\0') {
42✔
229

230
                if ((uint16_t) *str16 > 127U) /* Not ASCII? Fail! */
36✔
231
                        return NULL;
232

233
                str8[i++] = (char) (uint16_t) *str16;
34✔
234

235
                str16++;
34✔
236
                n--;
34✔
237
        }
238

239
        str8[i] = '\0';
6✔
240
        return TAKE_PTR(str8);
6✔
241
}
242

243
char* startswith8(const char *s, const char *prefix) {
9✔
244
        size_t l;
9✔
245

246
        assert(prefix);
9✔
247

248
        if (!s)
9✔
249
                return NULL;
250

251
        l = strlen8(prefix);
8✔
252
        if (!strneq8(s, prefix, l))
8✔
253
                return NULL;
254

255
        return (char*) s + l;
5✔
256
}
257

258
static bool efi_fnmatch_prefix(const char16_t *p, const char16_t *h, const char16_t **ret_p, const char16_t **ret_h) {
374✔
259
        assert(p);
374✔
260
        assert(h);
374✔
261
        assert(ret_p);
374✔
262
        assert(ret_h);
374✔
263

264
        for (;; p++, h++)
213✔
265
                switch (*p) {
587✔
266
                case '\0':
69✔
267
                        /* End of pattern. Check that haystack is now empty. */
268
                        return *h == '\0';
69✔
269

270
                case '\\':
3✔
271
                        p++;
3✔
272
                        if (*p == '\0' || *p != *h)
3✔
273
                                /* Trailing escape or no match. */
274
                                return false;
275
                        break;
276

277
                case '?':
6✔
278
                        if (*h == '\0')
6✔
279
                                /* Early end of haystack. */
280
                                return false;
281
                        break;
282

283
                case '*':
284
                        /* Point ret_p at the remainder of the pattern. */
285
                        while (*p == '*')
147✔
286
                                p++;
75✔
287
                        *ret_p = p;
72✔
288
                        *ret_h = h;
72✔
289
                        return true;
72✔
290

291
                case '[':
37✔
292
                        if (*h == '\0')
37✔
293
                                /* Early end of haystack. */
294
                                return false;
295

296
                        bool first = true, can_range = true, match = false;
297
                        for (;; first = false) {
135✔
298
                                p++;
135✔
299
                                if (*p == '\0')
135✔
300
                                        return false;
301

302
                                if (*p == '\\') {
134✔
303
                                        p++;
3✔
304
                                        if (*p == '\0')
3✔
305
                                                return false;
306
                                        if (*p == *h)
3✔
307
                                                match = true;
1✔
308
                                        can_range = true;
3✔
309
                                        continue;
3✔
310
                                }
311

312
                                /* End of set unless it's the first char. */
313
                                if (*p == ']' && !first)
131✔
314
                                        break;
315

316
                                /* Range pattern if '-' is not first or last in set. */
317
                                if (*p == '-' && can_range && !first && *(p + 1) != ']') {
95✔
318
                                        char16_t low = *(p - 1);
25✔
319
                                        p++;
25✔
320
                                        if (*p == '\\')
25✔
321
                                                p++;
1✔
322
                                        if (*p == '\0')
25✔
323
                                                return false;
324

325
                                        if (low <= *h && *h <= *p)
25✔
326
                                                match = true;
19✔
327

328
                                        /* Ranges cannot be chained: [a-c-f] == [-abcf] */
329
                                        can_range = false;
25✔
330
                                        continue;
25✔
331
                                }
332

333
                                if (*p == *h)
70✔
334
                                        match = true;
17✔
335
                                can_range = true;
336
                        }
337

338
                        if (!match)
36✔
339
                                return false;
340
                        break;
341

342
                default:
400✔
343
                        if (*p != *h)
400✔
344
                                /* Single char mismatch. */
345
                                return false;
346
                }
347
}
348

349
/* Patterns are fnmatch-compatible (with reduced feature support). */
350
bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack) {
46✔
351
        /* Patterns can be considered as simple patterns (without '*') concatenated by '*'. By doing so we
352
         * simply have to make sure the very first simple pattern matches the start of haystack. Then we just
353
         * look for the remaining simple patterns *somewhere* within the haystack (in order) as any extra
354
         * characters in between would be matches by the '*'. We then only have to ensure that the very last
355
         * simple pattern matches at the actual end of the haystack.
356
         *
357
         * This means we do not need to use backtracking which could have catastrophic runtimes with the
358
         * right input data. */
359

360
        for (bool first = true;;) {
374✔
361
                const char16_t *pattern_tail = NULL, *haystack_tail = NULL;
374✔
362
                bool match = efi_fnmatch_prefix(pattern, haystack, &pattern_tail, &haystack_tail);
374✔
363
                if (first) {
374✔
364
                        if (!match)
46✔
365
                                /* Initial simple pattern must match. */
366
                                return false;
46✔
367
                        if (!pattern_tail)
35✔
368
                                /* No '*' was in pattern, we can return early. */
369
                                return true;
370
                        first = false;
371
                }
372

373
                if (pattern_tail) {
346✔
374
                        assert(match);
72✔
375
                        pattern = pattern_tail;
72✔
376
                        haystack = haystack_tail;
72✔
377
                } else {
378
                        /* If we have a match this must be at the end of the haystack. Note that
379
                         * efi_fnmatch_prefix compares the NUL-bytes at the end, so we cannot match the end
380
                         * of pattern in the middle of haystack). */
381
                        if (match || *haystack == '\0')
274✔
382
                                return match;
383

384
                        /* Match one character using '*'. */
385
                        haystack++;
256✔
386
                }
387
        }
388
}
389

390
#define DEFINE_PARSE_NUMBER(type, name)                                    \
391
        bool name(const type *s, uint64_t *ret_u, const type **ret_tail) { \
392
                assert(ret_u);                                             \
393
                                                                           \
394
                if (!s)                                                    \
395
                        return false;                                      \
396
                                                                           \
397
                /* Need at least one digit. */                             \
398
                if (*s < '0' || *s > '9')                                  \
399
                        return false;                                      \
400
                                                                           \
401
                uint64_t u = 0;                                            \
402
                while (*s >= '0' && *s <= '9') {                           \
403
                        if (!MUL_ASSIGN_SAFE(&u, 10))                      \
404
                                return false;                              \
405
                        if (!INC_SAFE(&u, *s - '0'))                       \
406
                                return false;                              \
407
                        s++;                                               \
408
                }                                                          \
409
                                                                           \
410
                if (!ret_tail && *s != '\0')                               \
411
                        return false;                                      \
412
                                                                           \
413
                *ret_u = u;                                                \
414
                if (ret_tail)                                              \
415
                        *ret_tail = s;                                     \
416
                return true;                                               \
417
        }
418

419
DEFINE_PARSE_NUMBER(char, parse_number8);
355✔
420
DEFINE_PARSE_NUMBER(char16_t, parse_number16);
64✔
421

422
bool parse_boolean(const char *v, bool *ret) {
15✔
423
        assert(ret);
15✔
424

425
        if (!v)
15✔
426
                return false;
427

428
        if (streq8(v, "1") || streq8(v, "yes") || streq8(v, "y") || streq8(v, "true") || streq8(v, "t") ||
14✔
429
            streq8(v, "on")) {
9✔
430
                *ret = true;
6✔
431
                return true;
6✔
432
        }
433

434
        if (streq8(v, "0") || streq8(v, "no") || streq8(v, "n") || streq8(v, "false") || streq8(v, "f") ||
8✔
435
            streq8(v, "off")) {
3✔
436
                *ret = false;
6✔
437
                return true;
6✔
438
        }
439

440
        return false;
441
}
442

443
char* line_get_key_value(char *s, const char *sep, size_t *pos, char **ret_key, char **ret_value) {
24✔
444
        char *line, *value;
24✔
445
        size_t linelen;
24✔
446

447
        assert(s);
24✔
448
        assert(sep);
24✔
449
        assert(pos);
24✔
450
        assert(ret_key);
24✔
451
        assert(ret_value);
24✔
452

453
        for (;;) {
29✔
454
                line = s + *pos;
29✔
455
                if (*line == '\0')
29✔
456
                        return NULL;
457

458
                linelen = 0;
459
                while (line[linelen] && !strchr8("\n\r", line[linelen]))
551✔
460
                        linelen++;
527✔
461

462
                /* move pos to next line */
463
                *pos += linelen;
24✔
464
                if (s[*pos])
24✔
465
                        (*pos)++;
21✔
466

467
                /* empty line */
468
                if (linelen == 0)
24✔
469
                        continue;
2✔
470

471
                /* terminate line */
472
                line[linelen] = '\0';
22✔
473

474
                /* remove leading whitespace */
475
                while (linelen > 0 && strchr8(" \t", *line)) {
34✔
476
                        line++;
12✔
477
                        linelen--;
12✔
478
                }
479

480
                /* remove trailing whitespace */
481
                while (linelen > 0 && strchr8(" \t", line[linelen - 1]))
29✔
482
                        linelen--;
483
                line[linelen] = '\0';
22✔
484

485
                if (*line == '#')
22✔
486
                        continue;
2✔
487

488
                /* split key/value */
489
                value = line;
490
                while (*value && !strchr8(sep, *value))
160✔
491
                        value++;
140✔
492
                if (*value == '\0')
20✔
493
                        continue;
1✔
494
                *value = '\0';
19✔
495
                value++;
19✔
496
                while (*value && strchr8(sep, *value))
20✔
497
                        value++;
1✔
498

499
                /* unquote */
500
                if (value[0] == '"' && line[linelen - 1] == '"') {
19✔
501
                        value++;
10✔
502
                        line[linelen - 1] = '\0';
10✔
503
                }
504

505
                *ret_key = line;
19✔
506
                *ret_value = value;
19✔
507
                return line;
19✔
508
        }
509
}
510

511
char16_t *hexdump(const void *data, size_t size) {
4✔
512
        static const char hex[] = "0123456789abcdef";
4✔
513
        const uint8_t *d = data;
4✔
514

515
        assert(data || size == 0);
4✔
516

517
        char16_t *buf = xnew(char16_t, size * 2 + 1);
4✔
518

519
        for (size_t i = 0; i < size; i++) {
15✔
520
                buf[i * 2] = hex[d[i] >> 4];
11✔
521
                buf[i * 2 + 1] = hex[d[i] & 0x0F];
11✔
522
        }
523

524
        buf[size * 2] = 0;
4✔
525
        return buf;
4✔
526
}
527

528
static const char * const warn_table[] = {
529
        [EFI_SUCCESS]               = "Success",
530
        [EFI_WARN_UNKNOWN_GLYPH]    = "Unknown glyph",
531
        [EFI_WARN_DELETE_FAILURE]   = "Delete failure",
532
        [EFI_WARN_WRITE_FAILURE]    = "Write failure",
533
        [EFI_WARN_BUFFER_TOO_SMALL] = "Buffer too small",
534
        [EFI_WARN_STALE_DATA]       = "Stale data",
535
        [EFI_WARN_FILE_SYSTEM]      = "File system",
536
        [EFI_WARN_RESET_REQUIRED]   = "Reset required",
537
};
538

539
/* Errors have MSB set, remove it to keep the table compact. */
540
#define NOERR(err) ((err) & ~EFI_ERROR_MASK)
541

542
static const char * const err_table[] = {
543
        [NOERR(EFI_ERROR_MASK)]           = "Error",
544
        [NOERR(EFI_LOAD_ERROR)]           = "Load error",
545
        [NOERR(EFI_INVALID_PARAMETER)]    = "Invalid parameter",
546
        [NOERR(EFI_UNSUPPORTED)]          = "Unsupported",
547
        [NOERR(EFI_BAD_BUFFER_SIZE)]      = "Bad buffer size",
548
        [NOERR(EFI_BUFFER_TOO_SMALL)]     = "Buffer too small",
549
        [NOERR(EFI_NOT_READY)]            = "Not ready",
550
        [NOERR(EFI_DEVICE_ERROR)]         = "Device error",
551
        [NOERR(EFI_WRITE_PROTECTED)]      = "Write protected",
552
        [NOERR(EFI_OUT_OF_RESOURCES)]     = "Out of resources",
553
        [NOERR(EFI_VOLUME_CORRUPTED)]     = "Volume corrupt",
554
        [NOERR(EFI_VOLUME_FULL)]          = "Volume full",
555
        [NOERR(EFI_NO_MEDIA)]             = "No media",
556
        [NOERR(EFI_MEDIA_CHANGED)]        = "Media changed",
557
        [NOERR(EFI_NOT_FOUND)]            = "Not found",
558
        [NOERR(EFI_ACCESS_DENIED)]        = "Access denied",
559
        [NOERR(EFI_NO_RESPONSE)]          = "No response",
560
        [NOERR(EFI_NO_MAPPING)]           = "No mapping",
561
        [NOERR(EFI_TIMEOUT)]              = "Time out",
562
        [NOERR(EFI_NOT_STARTED)]          = "Not started",
563
        [NOERR(EFI_ALREADY_STARTED)]      = "Already started",
564
        [NOERR(EFI_ABORTED)]              = "Aborted",
565
        [NOERR(EFI_ICMP_ERROR)]           = "ICMP error",
566
        [NOERR(EFI_TFTP_ERROR)]           = "TFTP error",
567
        [NOERR(EFI_PROTOCOL_ERROR)]       = "Protocol error",
568
        [NOERR(EFI_INCOMPATIBLE_VERSION)] = "Incompatible version",
569
        [NOERR(EFI_SECURITY_VIOLATION)]   = "Security violation",
570
        [NOERR(EFI_CRC_ERROR)]            = "CRC error",
571
        [NOERR(EFI_END_OF_MEDIA)]         = "End of media",
572
        [NOERR(EFI_ERROR_RESERVED_29)]    = "Reserved (29)",
573
        [NOERR(EFI_ERROR_RESERVED_30)]    = "Reserved (30)",
574
        [NOERR(EFI_END_OF_FILE)]          = "End of file",
575
        [NOERR(EFI_INVALID_LANGUAGE)]     = "Invalid language",
576
        [NOERR(EFI_COMPROMISED_DATA)]     = "Compromised data",
577
        [NOERR(EFI_IP_ADDRESS_CONFLICT)]  = "IP address conflict",
578
        [NOERR(EFI_HTTP_ERROR)]           = "HTTP error",
579
};
580

581
static const char *status_to_string(EFI_STATUS status) {
4✔
582
        if (status <= ELEMENTSOF(warn_table) - 1)
4✔
583
                return warn_table[status];
2✔
584
        if (status >= EFI_ERROR_MASK && status <= ((ELEMENTSOF(err_table) - 1) | EFI_ERROR_MASK))
2✔
585
                return err_table[NOERR(status)];
1✔
586
        return NULL;
587
}
588

589
typedef struct {
590
        size_t padded_len; /* Field width in printf. */
591
        size_t len;        /* Precision in printf. */
592
        bool pad_zero;
593
        bool align_left;
594
        bool alternative_form;
595
        bool long_arg;
596
        bool longlong_arg;
597
        bool have_field_width;
598

599
        const char *str;
600
        const wchar_t *wstr;
601

602
        /* For numbers. */
603
        bool is_signed;
604
        bool lowercase;
605
        int8_t base;
606
        char sign_pad; /* For + and (space) flags. */
607
} SpecifierContext;
608

609
typedef struct {
610
        char16_t stack_buf[128]; /* We use stack_buf first to avoid allocations in most cases. */
611
        char16_t *dyn_buf;       /* Allocated buf or NULL if stack_buf is used. */
612
        char16_t *buf;           /* Points to the current active buf. */
613
        size_t n_buf;            /* Len of buf (in char16_t's, not bytes!). */
614
        size_t n;                /* Used len of buf (in char16_t's). This is always <n_buf. */
615

616
        EFI_STATUS status;
617
        const char *format;
618
        va_list ap;
619
} FormatContext;
620

621
static void grow_buf(FormatContext *ctx, size_t need) {
388✔
622
        assert(ctx);
388✔
623

624
        assert_se(INC_SAFE(&need, ctx->n));
388✔
625

626
        if (need < ctx->n_buf)
388✔
627
                return;
628

629
        /* Greedily allocate if we can. */
630
        if (!MUL_SAFE(&ctx->n_buf, need, 2))
7✔
631
                ctx->n_buf = need;
×
632

633
        /* We cannot use realloc here as ctx->buf may be ctx->stack_buf, which we cannot free. */
634
        char16_t *new_buf = xnew(char16_t, ctx->n_buf);
7✔
635
        memcpy(new_buf, ctx->buf, ctx->n * sizeof(*ctx->buf));
7✔
636

637
        free(ctx->dyn_buf);
7✔
638
        ctx->buf = ctx->dyn_buf = new_buf;
7✔
639
}
640

641
static void push_padding(FormatContext *ctx, char pad, size_t len) {
528✔
642
        assert(ctx);
528✔
643
        while (len > 0) {
43,330✔
644
                len--;
42,802✔
645
                ctx->buf[ctx->n++] = pad;
42,802✔
646
        }
647
}
528✔
648

649
static bool push_str(FormatContext *ctx, SpecifierContext *sp) {
248✔
650
        assert(ctx);
248✔
651
        assert(sp);
248✔
652

653
        sp->padded_len = LESS_BY(sp->padded_len, sp->len);
248✔
654

655
        grow_buf(ctx, sp->padded_len + sp->len);
248✔
656

657
        if (!sp->align_left)
248✔
658
                push_padding(ctx, ' ', sp->padded_len);
240✔
659

660
        /* In userspace unit tests we cannot just memcpy() the wide string. */
661
        if (sp->wstr && sizeof(wchar_t) == sizeof(char16_t)) {
248✔
662
                memcpy(ctx->buf + ctx->n, sp->wstr, sp->len * sizeof(*sp->wstr));
663
                ctx->n += sp->len;
664
        } else {
665
                assert(sp->str || sp->wstr);
248✔
666
                for (size_t i = 0; i < sp->len; i++)
704✔
667
                        ctx->buf[ctx->n++] = sp->str ? sp->str[i] : sp->wstr[i];
456✔
668
        }
669

670
        if (sp->align_left)
248✔
671
                push_padding(ctx, ' ', sp->padded_len);
8✔
672

673
        assert(ctx->n < ctx->n_buf);
248✔
674
        return true;
248✔
675
}
676

677
static bool push_num(FormatContext *ctx, SpecifierContext *sp, uint64_t u) {
148✔
678
        const char *digits = sp->lowercase ? "0123456789abcdef" : "0123456789ABCDEF";
148✔
679
        char16_t tmp[32];
148✔
680
        size_t n = 0;
148✔
681

682
        assert(ctx);
148✔
683
        assert(sp);
148✔
684
        assert(IN_SET(sp->base, 10, 16));
148✔
685

686
        /* "%.0u" prints nothing if value is 0. */
687
        if (u == 0 && sp->len == 0)
148✔
688
                return true;
148✔
689

690
        if (sp->is_signed && (int64_t) u < 0) {
140✔
691
                /* We cannot just do "u = -(int64_t)u" here because -INT64_MIN overflows. */
692

693
                uint64_t rem = -((int64_t) u % sp->base);
27✔
694
                u = (int64_t) u / -sp->base;
27✔
695
                tmp[n++] = digits[rem];
27✔
696
                sp->sign_pad = '-';
27✔
697
        }
698

699
        while (u > 0 || n == 0) {
965✔
700
                uint64_t rem = u % sp->base;
825✔
701
                u /= sp->base;
825✔
702
                tmp[n++] = digits[rem];
825✔
703
        }
704

705
        /* Note that numbers never get truncated! */
706
        size_t prefix = (sp->sign_pad != 0 ? 1 : 0) + (sp->alternative_form ? 2 : 0);
140✔
707
        size_t number_len = prefix + MAX(n, sp->len);
140✔
708
        grow_buf(ctx, MAX(sp->padded_len, number_len));
140✔
709

710
        size_t padding = 0;
140✔
711
        if (sp->pad_zero)
140✔
712
                /* Leading zeroes go after the sign or 0x prefix. */
713
                number_len = MAX(number_len, sp->padded_len);
10✔
714
        else
715
                padding = LESS_BY(sp->padded_len, number_len);
130✔
716

717
        if (!sp->align_left)
140✔
718
                push_padding(ctx, ' ', padding);
117✔
719

720
        if (sp->sign_pad != 0)
140✔
721
                ctx->buf[ctx->n++] = sp->sign_pad;
31✔
722
        if (sp->alternative_form) {
140✔
723
                ctx->buf[ctx->n++] = '0';
39✔
724
                ctx->buf[ctx->n++] = sp->lowercase ? 'x' : 'X';
56✔
725
        }
726

727
        push_padding(ctx, '0', LESS_BY(number_len, n + prefix));
140✔
728

729
        while (n > 0)
992✔
730
                ctx->buf[ctx->n++] = tmp[--n];
852✔
731

732
        if (sp->align_left)
140✔
733
                push_padding(ctx, ' ', padding);
23✔
734

735
        assert(ctx->n < ctx->n_buf);
140✔
736
        return true;
737
}
738

739
/* This helps unit testing. */
740
#if SD_BOOT
741
#  define NULLSTR "(null)"
742
#  define wcsnlen strnlen16
743
#else
744
#  define NULLSTR "(nil)"
745
#endif
746

747
static bool handle_format_specifier(FormatContext *ctx, SpecifierContext *sp) {
693✔
748
        /* Parses one item from the format specifier in ctx and put the info into sp. If we are done with
749
         * this specifier returns true, otherwise this function should be called again. */
750

751
        /* This implementation assumes 32-bit ints. Also note that all types smaller than int are promoted to
752
         * int in vararg functions, which is why we fetch only ints for any such types. The compiler would
753
         * otherwise warn about fetching smaller types. */
754
        assert_cc(sizeof(int) == 4);
693✔
755
        assert_cc(sizeof(wchar_t) <= sizeof(int));
693✔
756
        assert_cc(sizeof(long long) == sizeof(intmax_t));
693✔
757

758
        assert(ctx);
693✔
759
        assert(sp);
693✔
760

761
        switch (*ctx->format) {
693✔
762
        case '#':
31✔
763
                sp->alternative_form = true;
31✔
764
                return false;
31✔
765
        case '.':
103✔
766
                sp->have_field_width = true;
103✔
767
                return false;
103✔
768
        case '-':
26✔
769
                sp->align_left = true;
26✔
770
                return false;
26✔
771
        case '+':
11✔
772
        case ' ':
773
                sp->sign_pad = *ctx->format;
11✔
774
                return false;
11✔
775

776
        case '0':
20✔
777
                if (!sp->have_field_width) {
20✔
778
                        sp->pad_zero = true;
10✔
779
                        return false;
10✔
780
                }
781

782
                /* If field width has already been provided then 0 is part of precision (%.0s). */
783
                _fallthrough_;
217✔
784

785
        case '*':
786
        case '1' ... '9': {
787
                int64_t i;
217✔
788

789
                if (*ctx->format == '*')
217✔
790
                        i = va_arg(ctx->ap, int);
90✔
791
                else {
792
                        uint64_t u;
127✔
793
                        if (!parse_number8(ctx->format, &u, &ctx->format) || u > INT_MAX)
127✔
794
                                assert_not_reached();
×
795
                        ctx->format--; /* Point it back to the last digit. */
127✔
796
                        i = u;
127✔
797
                }
798

799
                if (sp->have_field_width) {
217✔
800
                        /* Negative precision is ignored. */
801
                        if (i >= 0)
98✔
802
                                sp->len = (size_t) i;
96✔
803
                } else {
804
                        /* Negative field width is treated as positive field width with '-' flag. */
805
                        if (i < 0) {
119✔
806
                                i *= -1;
5✔
807
                                sp->align_left = true;
5✔
808
                        }
809
                        sp->padded_len = i;
119✔
810
                }
811

812
                return false;
813
        }
814

815
        case 'h':
×
816
                if (*(ctx->format + 1) == 'h')
×
817
                        ctx->format++;
×
818
                /* char/short gets promoted to int, nothing to do here. */
819
                return false;
820

821
        case 'l':
52✔
822
                if (*(ctx->format + 1) == 'l') {
52✔
823
                        ctx->format++;
5✔
824
                        sp->longlong_arg = true;
5✔
825
                } else
826
                        sp->long_arg = true;
47✔
827
                return false;
828

829
        case 'z':
4✔
830
                sp->long_arg = sizeof(size_t) == sizeof(long);
4✔
831
                sp->longlong_arg = !sp->long_arg && sizeof(size_t) == sizeof(long long);
4✔
832
                return false;
4✔
833

834
        case 'j':
3✔
835
                sp->long_arg = sizeof(intmax_t) == sizeof(long);
3✔
836
                sp->longlong_arg = !sp->long_arg && sizeof(intmax_t) == sizeof(long long);
3✔
837
                return false;
3✔
838

839
        case 't':
2✔
840
                sp->long_arg = sizeof(ptrdiff_t) == sizeof(long);
2✔
841
                sp->longlong_arg = !sp->long_arg && sizeof(ptrdiff_t) == sizeof(long long);
2✔
842
                return false;
2✔
843

844
        case '%':
3✔
845
                sp->str = "%";
3✔
846
                sp->len = 1;
3✔
847
                return push_str(ctx, sp);
3✔
848

849
        case 'c':
5✔
850
                sp->wstr = &(wchar_t){ va_arg(ctx->ap, int) };
5✔
851
                sp->len = 1;
5✔
852
                return push_str(ctx, sp);
5✔
853

854
        case 's':
73✔
855
                if (sp->long_arg) {
73✔
856
                        sp->wstr = va_arg(ctx->ap, const wchar_t *) ?: L"(null)";
35✔
857
                        sp->len = wcsnlen(sp->wstr, sp->len);
35✔
858
                } else {
859
                        sp->str = va_arg(ctx->ap, const char *) ?: "(null)";
38✔
860
                        sp->len = strnlen8(sp->str, sp->len);
38✔
861
                }
862
                return push_str(ctx, sp);
73✔
863

864
        case 'd':
140✔
865
        case 'i':
866
        case 'u':
867
        case 'x':
868
        case 'X':
869
                sp->lowercase = *ctx->format == 'x';
140✔
870
                sp->is_signed = IN_SET(*ctx->format, 'd', 'i');
140✔
871
                sp->base = IN_SET(*ctx->format, 'x', 'X') ? 16 : 10;
140✔
872
                if (sp->len == SIZE_MAX)
140✔
873
                        sp->len = 1;
94✔
874

875
                uint64_t v;
140✔
876
                if (sp->longlong_arg)
140✔
877
                        v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long long) :
5✔
878
                                            va_arg(ctx->ap, unsigned long long);
3✔
879
                else if (sp->long_arg)
135✔
880
                        v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long) : va_arg(ctx->ap, unsigned long);
21✔
881
                else
882
                        v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, int) : va_arg(ctx->ap, unsigned);
114✔
883

884
                return push_num(ctx, sp, v);
140✔
885

886
        case 'p': {
9✔
887
                const void *ptr = va_arg(ctx->ap, const void *);
9✔
888
                if (!ptr) {
9✔
889
                        sp->str = NULLSTR;
2✔
890
                        sp->len = STRLEN(NULLSTR);
2✔
891
                        return push_str(ctx, sp);
2✔
892
                }
893

894
                sp->base = 16;
7✔
895
                sp->lowercase = true;
7✔
896
                sp->alternative_form = true;
7✔
897
                sp->len = 0; /* Precision is ignored for %p. */
7✔
898
                return push_num(ctx, sp, (uintptr_t) ptr);
7✔
899
        }
900

901
        case 'm': {
4✔
902
                sp->str = status_to_string(ctx->status);
4✔
903
                if (sp->str) {
4✔
904
                        sp->len = strlen8(sp->str);
3✔
905
                        return push_str(ctx, sp);
3✔
906
                }
907

908
                sp->base = 16;
1✔
909
                sp->lowercase = true;
1✔
910
                sp->alternative_form = true;
1✔
911
                sp->len = 0;
1✔
912
                return push_num(ctx, sp, ctx->status);
1✔
913
        }
914

915
        default:
×
916
                assert_not_reached();
×
917
        }
918
}
919

920
#if SD_BOOT
921
static void output_string_safe(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *this, const char16_t *s) {
922
        assert(this);
923
        assert(s);
924

925
        /* This is a color-conscious version of ST->ConOut->OutputString(). Whenever it encounters a newline
926
         * character, it will reset the color to our default, because some UEFI implementations/terminals
927
         * reset the color in that case, and we want our default color to remain in effect */
928

929
        int32_t saved_attribute = ST->ConOut->Mode->Attribute;
930
        for (;;) {
931
                const char16_t *nl = strchr16(s, '\n');
932
                if (!nl) /* No further newline */
933
                        return (void) ST->ConOut->OutputString(ST->ConOut, (char16_t*) s);
934

935
                if (nl[1] == 0) { /* Newline is at the end of the string */
936
                        (void) ST->ConOut->OutputString(ST->ConOut, (char16_t*) s);
937
                        set_attribute_safe(saved_attribute);
938
                        return;
939
                }
940

941
                /* newline is in the middle of the string */
942
                _cleanup_free_ char16_t *x = xstrndup16(s, nl - s + 1);
943
                (void) ST->ConOut->OutputString(ST->ConOut, x);
944
                set_attribute_safe(saved_attribute);
945

946
                s = nl + 1;
947
        }
948
}
949
#endif
950

951
/* printf_internal is largely compatible to userspace vasprintf. Any features omitted should trigger asserts.
952
 *
953
 * Supported:
954
 *  - Flags: #, 0, +, -, space
955
 *  - Lengths: h, hh, l, ll, z, j, t
956
 *  - Specifiers: %, c, s, u, i, d, x, X, p, m
957
 *  - Precision and width (inline or as int arg using *)
958
 *
959
 * Notable differences:
960
 *  - Passing NULL to %s is permitted and will print "(null)"
961
 *  - %p will also use "(null)"
962
 *  - The provided EFI_STATUS is used for %m instead of errno
963
 *  - "\n" is translated to "\r\n" */
964
_printf_(2, 0) static char16_t *printf_internal(EFI_STATUS status, const char *format, va_list ap, bool ret) {
74✔
965
        assert(format);
74✔
966

967
        FormatContext ctx = {
74✔
968
                .buf = ctx.stack_buf,
969
                .n_buf = ELEMENTSOF(ctx.stack_buf),
970
                .format = format,
971
                .status = status,
972
        };
973

974
        /* We cannot put this into the struct without making a copy. */
975
        va_copy(ctx.ap, ap);
74✔
976

977
        while (*ctx.format != '\0') {
470✔
978
                SpecifierContext sp = { .len = SIZE_MAX };
396✔
979

980
                switch (*ctx.format) {
396✔
981
                case '%':
234✔
982
                        ctx.format++;
234✔
983
                        while (!handle_format_specifier(&ctx, &sp))
693✔
984
                                ctx.format++;
459✔
985
                        ctx.format++;
234✔
986
                        break;
234✔
987
                case '\n':
2✔
988
                        ctx.format++;
2✔
989
                        sp.str = "\r\n";
2✔
990
                        sp.len = 2;
2✔
991
                        push_str(&ctx, &sp);
2✔
992
                        break;
993
                default:
160✔
994
                        sp.str = ctx.format++;
160✔
995
                        while (!IN_SET(*ctx.format, '%', '\n', '\0'))
168✔
996
                                ctx.format++;
8✔
997
                        sp.len = ctx.format - sp.str;
160✔
998
                        push_str(&ctx, &sp);
160✔
999
                }
1000
        }
1001

1002
        va_end(ctx.ap);
74✔
1003

1004
        assert(ctx.n < ctx.n_buf);
74✔
1005
        ctx.buf[ctx.n++] = '\0';
74✔
1006

1007
        if (ret) {
74✔
1008
                if (ctx.dyn_buf)
74✔
1009
                        return TAKE_PTR(ctx.dyn_buf);
3✔
1010

1011
                char16_t *ret_buf = xnew(char16_t, ctx.n);
71✔
1012
                memcpy(ret_buf, ctx.buf, ctx.n * sizeof(*ctx.buf));
71✔
1013
                return ret_buf;
71✔
1014
        }
1015

1016
#if SD_BOOT
1017
        output_string_safe(ST->ConOut, ctx.buf);
1018
#endif
1019

UNCOV
1020
        return mfree(ctx.dyn_buf);
×
1021
}
1022

UNCOV
1023
void printf_status(EFI_STATUS status, const char *format, ...) {
×
UNCOV
1024
        va_list ap;
×
UNCOV
1025
        va_start(ap, format);
×
UNCOV
1026
        printf_internal(status, format, ap, false);
×
UNCOV
1027
        va_end(ap);
×
UNCOV
1028
}
×
1029

UNCOV
1030
void vprintf_status(EFI_STATUS status, const char *format, va_list ap) {
×
UNCOV
1031
        printf_internal(status, format, ap, false);
×
UNCOV
1032
}
×
1033

1034
char16_t *xasprintf_status(EFI_STATUS status, const char *format, ...) {
5✔
1035
        va_list ap;
5✔
1036
        va_start(ap, format);
5✔
1037
        char16_t *ret = printf_internal(status, format, ap, true);
5✔
1038
        va_end(ap);
5✔
1039
        return ret;
5✔
1040
}
1041

1042
char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) {
69✔
1043
        return printf_internal(status, format, ap, true);
69✔
1044
}
1045

1046
#if SD_BOOT
1047
/* To provide the actual implementation for these we need to remove the redirection to the builtins. */
1048
#  undef memchr
1049
#  undef memcmp
1050
#  undef memcpy
1051
#  undef memset
1052
_used_ void *memchr(const void *p, int c, size_t n);
1053
_used_ int memcmp(const void *p1, const void *p2, size_t n);
1054
_used_ void *memcpy(void * restrict dest, const void * restrict src, size_t n);
1055
_used_ void *memset(void *p, int c, size_t n);
1056
#else
1057
/* And for userspace unit testing we need to give them an efi_ prefix. */
1058
#  define memchr efi_memchr
1059
#  define memcmp efi_memcmp
1060
#  define memcpy efi_memcpy
1061
#  define memset efi_memset
1062
#endif
1063

1064
void *memchr(const void *p, int c, size_t n) {
10✔
1065
        if (!p || n == 0)
10✔
1066
                return NULL;
1067

1068
        const uint8_t *q = p;
1069
        for (size_t i = 0; i < n; i++)
26✔
1070
                if (q[i] == (unsigned char) c)
24✔
1071
                        return (void *) (q + i);
1072

1073
        return NULL;
1074
}
1075

1076
int memcmp(const void *p1, const void *p2, size_t n) {
15✔
1077
        const uint8_t *up1 = p1, *up2 = p2;
15✔
1078
        int r;
15✔
1079

1080
        if (!p1 || !p2)
15✔
1081
                return CMP(p1, p2);
4✔
1082

1083
        while (n > 0) {
29✔
1084
                r = CMP(*up1, *up2);
24✔
1085
                if (r != 0)
20✔
1086
                        return r;
6✔
1087

1088
                up1++;
18✔
1089
                up2++;
18✔
1090
                n--;
18✔
1091
        }
1092

1093
        return 0;
1094
}
1095

1096
void *memcpy(void * restrict dest, const void * restrict src, size_t n) {
9✔
1097
        if (!dest || !src || n == 0)
9✔
1098
                return dest;
1099

1100
#if SD_BOOT
1101
        /* The firmware-provided memcpy is likely optimized, so use that. The function is guaranteed to be
1102
         * available by the UEFI spec. We still make it depend on the boot services pointer being set just in
1103
         * case the compiler emits a call before it is available. */
1104
        if (_likely_(BS)) {
1105
                BS->CopyMem(dest, (void *) src, n);
1106
                return dest;
1107
        }
1108
#endif
1109

1110
        uint8_t *d = dest;
1111
        const uint8_t *s = src;
1112

1113
        while (n > 0) {
18✔
1114
                *d = *s;
14✔
1115
                d++;
14✔
1116
                s++;
14✔
1117
                n--;
14✔
1118
        }
1119

1120
        return dest;
1121
}
1122

1123
void *memset(void *p, int c, size_t n) {
6✔
1124
        if (!p || n == 0)
6✔
1125
                return p;
1126

1127
#if SD_BOOT
1128
        /* See comment in efi_memcpy. Note that the signature has c and n swapped! */
1129
        if (_likely_(BS)) {
1130
                BS->SetMem(p, n, c);
1131
                return p;
1132
        }
1133
#endif
1134

1135
        uint8_t *q = p;
1136
        while (n > 0) {
18✔
1137
                *q = c;
15✔
1138
                q++;
15✔
1139
                n--;
15✔
1140
        }
1141

1142
        return p;
1143
}
1144

1145
size_t strspn16(const char16_t *p, const char16_t *good) {
7✔
1146
        assert(p);
7✔
1147
        assert(good);
7✔
1148

1149
        const char16_t *i = p;
1150
        for (; *i != 0; i++)
25✔
1151
                if (!strchr16(good, *i))
23✔
1152
                        break;
1153

1154
        return i - p;
7✔
1155
}
1156

1157
size_t strcspn16(const char16_t *p, const char16_t *bad) {
10✔
1158
        assert(p);
10✔
1159
        assert(bad);
10✔
1160

1161
        const char16_t *i = p;
1162
        for (; *i != 0; i++)
41✔
1163
                if (strchr16(bad, *i))
37✔
1164
                        break;
1165

1166
        return i - p;
10✔
1167
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc