Coverage Report

Created: 2026-04-29 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/boot/efi-string.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include "efi-string.h"
4
#include "string-util-fundamental.h"
5
6
#if SD_BOOT
7
#  include "proto/simple-text-io.h"
8
#  include "util.h"
9
#else
10
#  include <stdlib.h>
11
12
#  include "alloc-util.h"
13
8.76k
#  define xnew(t, n) ASSERT_SE_PTR(new(t, n))
14
0
#  define xmalloc(n) ASSERT_SE_PTR(malloc(n))
15
#endif
16
17
/* String functions for both char and char16_t that should behave the same way as their respective
18
 * counterpart in userspace. Where it makes sense, these accept NULL and do something sensible whereas
19
 * userspace does not allow for this (strlen8(NULL) returns 0 like strlen_ptr(NULL) for example). To make it
20
 * easier to tell in code which kind of string they work on, we use 8/16 suffixes. This also makes is easier
21
 * to unit test them. */
22
23
#define DEFINE_STRNLEN(type, name)             \
24
11.1k
        size_t name(const type *s, size_t n) { \
25
11.1k
                if (!s)                        \
26
11.1k
                        return 0;              \
27
11.1k
                                               \
28
11.1k
                size_t len = 0;                \
29
18.8M
                while (len < n && *s) {        \
30
18.8M
                        s++;                   \
31
18.8M
                        len++;                 \
32
18.8M
                }                              \
33
11.1k
                                               \
34
11.1k
                return len;                    \
35
11.1k
        }
strnlen8
Line
Count
Source
24
11.1k
        size_t name(const type *s, size_t n) { \
25
11.1k
                if (!s)                        \
26
11.1k
                        return 0;              \
27
11.1k
                                               \
28
11.1k
                size_t len = 0;                \
29
18.8M
                while (len < n && *s) {        \
30
18.8M
                        s++;                   \
31
18.8M
                        len++;                 \
32
18.8M
                }                              \
33
11.1k
                                               \
34
11.1k
                return len;                    \
35
11.1k
        }
Unexecuted instantiation: strnlen16
36
37
DEFINE_STRNLEN(char, strnlen8);
38
DEFINE_STRNLEN(char16_t, strnlen16);
39
40
#define TOLOWER(c)                                                \
41
830k
        ({                                                        \
42
830k
                typeof(c) _c = (c);                               \
43
830k
                (_c >= 'A' && _c <= 'Z') ? _c + ('a' - 'A') : _c; \
44
830k
        })
45
46
#define DEFINE_STRTOLOWER(type, name)                \
47
0
        type* name(type *s) {                        \
48
0
                if (!s)                              \
49
0
                        return NULL;                 \
50
0
                for (type *p = s; *p; p++)           \
51
0
                        *p = TOLOWER(*p);            \
52
0
                return s;                            \
53
0
        }
54
55
0
DEFINE_STRTOLOWER(char, strtolower8);
56
0
DEFINE_STRTOLOWER(char16_t, strtolower16);
57
58
#define DEFINE_STRNCASECMP(type, name, tolower)              \
59
282k
        int name(const type *s1, const type *s2, size_t n) { \
60
282k
                if (!s1 || !s2)                              \
61
282k
                        return CMP(s1, s2);                  \
62
282k
                                                             \
63
427k
                while (n > 0) {                              \
64
415k
                        type c1 = *s1, c2 = *s2;             \
65
415k
                        if (tolower) {                       \
66
415k
                                c1 = TOLOWER(c1);            \
67
415k
                                c2 = TOLOWER(c2);            \
68
415k
                        }                                    \
69
415k
                        if (!c1 || c1 != c2)                 \
70
415k
                                return CMP(c1, c2);          \
71
415k
                                                             \
72
415k
                        s1++;                                \
73
144k
                        s2++;                                \
74
144k
                        n--;                                 \
75
144k
                }                                            \
76
282k
                                                             \
77
282k
                return 0;                                    \
78
282k
        }
Unexecuted instantiation: strncmp8
Unexecuted instantiation: strncmp16
strncasecmp8
Line
Count
Source
59
282k
        int name(const type *s1, const type *s2, size_t n) { \
60
282k
                if (!s1 || !s2)                              \
61
282k
                        return CMP(s1, s2);                  \
62
282k
                                                             \
63
427k
                while (n > 0) {                              \
64
415k
                        type c1 = *s1, c2 = *s2;             \
65
415k
                        if (tolower) {                       \
66
415k
                                c1 = TOLOWER(c1);            \
67
415k
                                c2 = TOLOWER(c2);            \
68
415k
                        }                                    \
69
415k
                        if (!c1 || c1 != c2)                 \
70
415k
                                return CMP(c1, c2);          \
71
415k
                                                             \
72
415k
                        s1++;                                \
73
144k
                        s2++;                                \
74
144k
                        n--;                                 \
75
144k
                }                                            \
76
282k
                                                             \
77
282k
                return 0;                                    \
78
282k
        }
Unexecuted instantiation: strncasecmp16
79
80
DEFINE_STRNCASECMP(char, strncmp8, false);
81
DEFINE_STRNCASECMP(char16_t, strncmp16, false);
82
DEFINE_STRNCASECMP(char, strncasecmp8, true);
83
DEFINE_STRNCASECMP(char16_t, strncasecmp16, true);
84
85
#define DEFINE_STRCPY(type, name)                                     \
86
0
        type *name(type * restrict dest, const type * restrict src) { \
87
0
                type *ret = ASSERT_PTR(dest);                         \
88
0
                                                                      \
89
0
                if (!src) {                                           \
90
0
                        *dest = '\0';                                 \
91
0
                        return ret;                                   \
92
0
                }                                                     \
93
0
                                                                      \
94
0
                while (*src) {                                        \
95
0
                        *dest = *src;                                 \
96
0
                        dest++;                                       \
97
0
                        src++;                                        \
98
0
                }                                                     \
99
0
                                                                      \
100
0
                *dest = '\0';                                         \
101
0
                return ret;                                           \
102
0
        }
103
104
0
DEFINE_STRCPY(char, strcpy8);
105
0
DEFINE_STRCPY(char16_t, strcpy16);
106
107
#define DEFINE_STRCHR(type, name)                  \
108
400k
        type *name(const type *s, type c) {        \
109
400k
                if (!s)                            \
110
400k
                        return NULL;               \
111
400k
                                                   \
112
1.12M
                while (*s) {                       \
113
770k
                        if (*s == c)               \
114
770k
                                return (type *) s; \
115
770k
                        s++;                       \
116
723k
                }                                  \
117
400k
                                                   \
118
400k
                return c ? NULL : (type *) s;      \
119
400k
        }
120
121
400k
DEFINE_STRCHR(char, strchr8);
122
0
DEFINE_STRCHR(char16_t, strchr16);
123
124
#define DEFINE_STRNDUP(type, name, len_func)              \
125
0
        type *name(const type *s, size_t n) {             \
126
0
                if (!s)                                   \
127
0
                        return NULL;                      \
128
0
                                                          \
129
0
                size_t len = len_func(s, n);              \
130
0
                size_t size = len * sizeof(type);         \
131
0
                                                          \
132
0
                type *dup = xmalloc(size + sizeof(type)); \
133
0
                if (size > 0)                             \
134
0
                        memcpy(dup, s, size);             \
135
0
                dup[len] = '\0';                          \
136
0
                                                          \
137
0
                return dup;                               \
138
0
        }
139
140
0
DEFINE_STRNDUP(char, xstrndup8, strnlen8);
141
0
DEFINE_STRNDUP(char16_t, xstrndup16, strnlen16);
142
143
0
static unsigned utf8_to_unichar(const char *utf8, size_t n, char32_t *c) {
144
0
        char32_t unichar;
145
0
        unsigned len;
146
147
0
        assert(utf8);
148
0
        assert(c);
149
150
0
        if (!(utf8[0] & 0x80)) {
151
0
                *c = utf8[0];
152
0
                return 1;
153
0
        } else if ((utf8[0] & 0xe0) == 0xc0) {
154
0
                len = 2;
155
0
                unichar = utf8[0] & 0x1f;
156
0
        } else if ((utf8[0] & 0xf0) == 0xe0) {
157
0
                len = 3;
158
0
                unichar = utf8[0] & 0x0f;
159
0
        } else if ((utf8[0] & 0xf8) == 0xf0) {
160
0
                len = 4;
161
0
                unichar = utf8[0] & 0x07;
162
0
        } else if ((utf8[0] & 0xfc) == 0xf8) {
163
0
                len = 5;
164
0
                unichar = utf8[0] & 0x03;
165
0
        } else if ((utf8[0] & 0xfe) == 0xfc) {
166
0
                len = 6;
167
0
                unichar = utf8[0] & 0x01;
168
0
        } else {
169
0
                *c = UINT32_MAX;
170
0
                return 1;
171
0
        }
172
173
0
        if (len > n) {
174
0
                *c = UINT32_MAX;
175
0
                return len;
176
0
        }
177
178
0
        for (unsigned i = 1; i < len; i++) {
179
0
                if ((utf8[i] & 0xc0) != 0x80) {
180
0
                        *c = UINT32_MAX;
181
0
                        return len;
182
0
                }
183
0
                unichar <<= 6;
184
0
                unichar |= utf8[i] & 0x3f;
185
0
        }
186
187
0
        *c = unichar;
188
0
        return len;
189
0
}
190
191
/* Convert UTF-8 to UCS-2, skipping any invalid or short byte sequences. */
192
0
char16_t *xstrn8_to_16(const char *str8, size_t n) {
193
0
        assert(str8 || n == 0);
194
195
0
        if (n == SIZE_MAX)
196
0
                n = strlen8(str8);
197
198
0
        size_t i = 0;
199
0
        char16_t *str16 = xnew(char16_t, n + 1);
200
201
0
        while (n > 0 && *str8 != '\0') {
202
0
                char32_t unichar;
203
204
0
                size_t utf8len = utf8_to_unichar(str8, n, &unichar);
205
0
                str8 += utf8len;
206
0
                n = LESS_BY(n, utf8len);
207
208
0
                switch (unichar) {
209
0
                case 0 ... 0xd7ffU:
210
0
                case 0xe000U ... 0xffffU:
211
0
                        str16[i++] = unichar;
212
0
                        break;
213
0
                }
214
0
        }
215
216
0
        str16[i] = u'\0';
217
0
        return str16;
218
0
}
219
220
0
char *xstrn16_to_ascii(const char16_t *str16, size_t n) {
221
0
        assert(str16 || n == 0);
222
223
0
        if (n == SIZE_MAX)
224
0
                n = strlen16(str16);
225
226
0
        _cleanup_free_ char *str8 = xnew(char, n + 1);
227
228
0
        size_t i = 0;
229
0
        while (n > 0 && *str16 != u'\0') {
230
231
0
                if ((uint16_t) *str16 > 127U) /* Not ASCII? Fail! */
232
0
                        return NULL;
233
234
0
                str8[i++] = (char) (uint16_t) *str16;
235
236
0
                str16++;
237
0
                n--;
238
0
        }
239
240
0
        str8[i] = '\0';
241
0
        return TAKE_PTR(str8);
242
0
}
243
244
0
char* startswith8(const char *s, const char *prefix) {
245
0
        size_t l;
246
247
0
        assert(prefix);
248
249
0
        if (!s)
250
0
                return NULL;
251
252
0
        l = strlen8(prefix);
253
0
        if (!strneq8(s, prefix, l))
254
0
                return NULL;
255
256
0
        return (char*) s + l;
257
0
}
258
259
4.99k
static bool efi_fnmatch_prefix(const char16_t *p, const char16_t *h, const char16_t **ret_p, const char16_t **ret_h) {
260
4.99k
        assert(p);
261
4.99k
        assert(h);
262
4.99k
        assert(ret_p);
263
4.99k
        assert(ret_h);
264
265
19.0k
        for (;; p++, h++)
266
24.0k
                switch (*p) {
267
734
                case '\0':
268
                        /* End of pattern. Check that haystack is now empty. */
269
734
                        return *h == '\0';
270
271
706
                case '\\':
272
706
                        p++;
273
706
                        if (*p == '\0' || *p != *h)
274
                                /* Trailing escape or no match. */
275
506
                                return false;
276
200
                        break;
277
278
9.26k
                case '?':
279
9.26k
                        if (*h == '\0')
280
                                /* Early end of haystack. */
281
207
                                return false;
282
9.05k
                        break;
283
284
9.05k
                case '*':
285
                        /* Point ret_p at the remainder of the pattern. */
286
1.05k
                        while (*p == '*')
287
629
                                p++;
288
428
                        *ret_p = p;
289
428
                        *ret_h = h;
290
428
                        return true;
291
292
11.7k
                case '[':
293
11.7k
                        if (*h == '\0')
294
                                /* Early end of haystack. */
295
337
                                return false;
296
297
11.7k
                        bool first = true, can_range = true, match = false;
298
37.0k
                        for (;; first = false) {
299
37.0k
                                p++;
300
37.0k
                                if (*p == '\0')
301
1.12k
                                        return false;
302
303
35.8k
                                if (*p == '\\') {
304
661
                                        p++;
305
661
                                        if (*p == '\0')
306
210
                                                return false;
307
451
                                        if (*p == *h)
308
194
                                                match = true;
309
451
                                        can_range = true;
310
451
                                        continue;
311
661
                                }
312
313
                                /* End of set unless it's the first char. */
314
35.2k
                                if (*p == ']' && !first)
315
9.76k
                                        break;
316
317
                                /* Range pattern if '-' is not first or last in set. */
318
25.4k
                                if (*p == '-' && can_range && !first && *(p + 1) != ']') {
319
1.65k
                                        char16_t low = *(p - 1);
320
1.65k
                                        p++;
321
1.65k
                                        if (*p == '\\')
322
215
                                                p++;
323
1.65k
                                        if (*p == '\0')
324
289
                                                return false;
325
326
1.36k
                                        if (low <= *h && *h <= *p)
327
417
                                                match = true;
328
329
                                        /* Ranges cannot be chained: [a-c-f] == [-abcf] */
330
1.36k
                                        can_range = false;
331
1.36k
                                        continue;
332
1.65k
                                }
333
334
23.8k
                                if (*p == *h)
335
10.5k
                                        match = true;
336
23.8k
                                can_range = true;
337
23.8k
                        }
338
339
9.76k
                        if (!match)
340
324
                                return false;
341
9.43k
                        break;
342
343
9.43k
                default:
344
1.18k
                        if (*p != *h)
345
                                /* Single char mismatch. */
346
833
                                return false;
347
24.0k
                }
348
4.99k
}
349
350
/* Patterns are fnmatch-compatible (with reduced feature support). */
351
619
bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack) {
352
619
        assert(haystack);
353
354
        /* Patterns can be considered as simple patterns (without '*') concatenated by '*'. By doing so we
355
         * simply have to make sure the very first simple pattern matches the start of haystack. Then we just
356
         * look for the remaining simple patterns *somewhere* within the haystack (in order) as any extra
357
         * characters in between would be matches by the '*'. We then only have to ensure that the very last
358
         * simple pattern matches at the actual end of the haystack.
359
         *
360
         * This means we do not need to use backtracking which could have catastrophic runtimes with the
361
         * right input data. */
362
363
4.99k
        for (bool first = true;;) {
364
4.99k
                const char16_t *pattern_tail = NULL, *haystack_tail = NULL;
365
4.99k
                bool match = efi_fnmatch_prefix(pattern, haystack, &pattern_tail, &haystack_tail);
366
4.99k
                if (first) {
367
619
                        if (!match)
368
                                /* Initial simple pattern must match. */
369
299
                                return false;
370
320
                        if (!pattern_tail)
371
                                /* No '*' was in pattern, we can return early. */
372
78
                                return true;
373
242
                        first = false;
374
242
                }
375
376
4.61k
                if (pattern_tail) {
377
428
                        assert(match);
378
428
                        pattern = pattern_tail;
379
428
                        haystack = haystack_tail;
380
4.18k
                } else {
381
                        /* If we have a match this must be at the end of the haystack. Note that
382
                         * efi_fnmatch_prefix compares the NUL-bytes at the end, so we cannot match the end
383
                         * of pattern in the middle of haystack). */
384
4.18k
                        if (match || *haystack == '\0')
385
242
                                return match;
386
387
                        /* Match one character using '*'. */
388
3.94k
                        haystack++;
389
3.94k
                }
390
4.61k
        }
391
619
}
392
393
#define DEFINE_PARSE_NUMBER(type, name)                                    \
394
1.90k
        bool name(const type *s, uint64_t *ret_u, const type **ret_tail) { \
395
1.90k
                assert(ret_u);                                             \
396
1.90k
                                                                           \
397
1.90k
                if (!s)                                                    \
398
1.90k
                        return false;                                      \
399
1.90k
                                                                           \
400
1.90k
                /* Need at least one digit. */                             \
401
1.90k
                if (*s < '0' || *s > '9')                                  \
402
1.90k
                        return false;                                      \
403
1.90k
                                                                           \
404
1.90k
                uint64_t u = 0;                                            \
405
4.77k
                while (*s >= '0' && *s <= '9') {                           \
406
3.95k
                        if (!MUL_ASSIGN_SAFE(&u, 10))                      \
407
3.95k
                                return false;                              \
408
3.95k
                        if (!INC_SAFE(&u, *s - '0'))                       \
409
3.94k
                                return false;                              \
410
3.94k
                        s++;                                               \
411
3.94k
                }                                                          \
412
828
                                                                           \
413
828
                if (!ret_tail && *s != '\0')                               \
414
825
                        return false;                                      \
415
825
                                                                           \
416
825
                *ret_u = u;                                                \
417
765
                if (ret_tail)                                              \
418
765
                        *ret_tail = s;                                     \
419
765
                return true;                                               \
420
825
        }
421
422
1.28k
DEFINE_PARSE_NUMBER(char, parse_number8);
423
619
DEFINE_PARSE_NUMBER(char16_t, parse_number16);
424
425
0
bool parse_boolean(const char *v, bool *ret) {
426
0
        assert(ret);
427
428
0
        if (!v)
429
0
                return false;
430
431
0
        if (streq8(v, "1") || streq8(v, "yes") || streq8(v, "y") || streq8(v, "true") || streq8(v, "t") ||
432
0
            streq8(v, "on")) {
433
0
                *ret = true;
434
0
                return true;
435
0
        }
436
437
0
        if (streq8(v, "0") || streq8(v, "no") || streq8(v, "n") || streq8(v, "false") || streq8(v, "f") ||
438
0
            streq8(v, "off")) {
439
0
                *ret = false;
440
0
                return true;
441
0
        }
442
443
0
        return false;
444
0
}
445
446
7.60k
char* line_get_key_value(char *s, const char *sep, size_t *pos, char **ret_key, char **ret_value) {
447
7.60k
        char *line, *value;
448
7.60k
        size_t linelen;
449
450
7.60k
        assert(s);
451
7.60k
        assert(sep);
452
7.60k
        assert(pos);
453
7.60k
        assert(ret_key);
454
7.60k
        assert(ret_value);
455
456
30.7k
        for (;;) {
457
30.7k
                line = s + *pos;
458
30.7k
                if (*line == '\0')
459
213
                        return NULL;
460
461
30.5k
                linelen = 0;
462
243k
                while (line[linelen] && !strchr8("\n\r", line[linelen]))
463
213k
                        linelen++;
464
465
                /* move pos to next line */
466
30.5k
                *pos += linelen;
467
30.5k
                if (s[*pos])
468
30.3k
                        (*pos)++;
469
470
                /* empty line */
471
30.5k
                if (linelen == 0)
472
5.18k
                        continue;
473
474
                /* terminate line */
475
25.3k
                line[linelen] = '\0';
476
477
                /* remove leading whitespace */
478
27.8k
                while (linelen > 0 && strchr8(" \t", *line)) {
479
2.52k
                        line++;
480
2.52k
                        linelen--;
481
2.52k
                }
482
483
                /* remove trailing whitespace */
484
26.2k
                while (linelen > 0 && strchr8(" \t", line[linelen - 1]))
485
895
                        linelen--;
486
25.3k
                line[linelen] = '\0';
487
488
25.3k
                if (*line == '#')
489
11.1k
                        continue;
490
491
                /* split key/value */
492
14.1k
                value = line;
493
96.9k
                while (*value && !strchr8(sep, *value))
494
82.8k
                        value++;
495
14.1k
                if (*value == '\0')
496
6.77k
                        continue;
497
7.38k
                *value = '\0';
498
7.38k
                value++;
499
9.54k
                while (*value && strchr8(sep, *value))
500
2.15k
                        value++;
501
502
                /* unquote */
503
7.38k
                if (strchr8(QUOTES, value[0]) && line[linelen - 1] == value[0]) {
504
2.03k
                        value++;
505
2.03k
                        line[linelen - 1] = '\0';
506
2.03k
                }
507
508
7.38k
                *ret_key = line;
509
7.38k
                *ret_value = value;
510
7.38k
                return line;
511
14.1k
        }
512
7.60k
}
513
514
char16_t *hexdump(const void *data, size_t size) {
515
        const char *hex = LOWERCASE_HEXDIGITS;
516
        const uint8_t *d = data;
517
518
        assert(data || size == 0);
519
520
        char16_t *buf = xnew(char16_t, size * 2 + 1);
521
522
        for (size_t i = 0; i < size; i++) {
523
                buf[i * 2] = hex[d[i] >> 4];
524
                buf[i * 2 + 1] = hex[d[i] & 0x0F];
525
        }
526
527
        buf[size * 2] = 0;
528
        return buf;
529
}
530
531
static const char * const warn_table[] = {
532
        [EFI_SUCCESS]               = "Success",
533
        [EFI_WARN_UNKNOWN_GLYPH]    = "Unknown glyph",
534
        [EFI_WARN_DELETE_FAILURE]   = "Delete failure",
535
        [EFI_WARN_WRITE_FAILURE]    = "Write failure",
536
        [EFI_WARN_BUFFER_TOO_SMALL] = "Buffer too small",
537
        [EFI_WARN_STALE_DATA]       = "Stale data",
538
        [EFI_WARN_FILE_SYSTEM]      = "File system",
539
        [EFI_WARN_RESET_REQUIRED]   = "Reset required",
540
};
541
542
/* Errors have MSB set, remove it to keep the table compact. */
543
28
#define NOERR(err) ((err) & ~EFI_ERROR_MASK)
544
545
static const char * const err_table[] = {
546
        [NOERR(EFI_ERROR_MASK)]           = "Error",
547
        [NOERR(EFI_LOAD_ERROR)]           = "Load error",
548
        [NOERR(EFI_INVALID_PARAMETER)]    = "Invalid parameter",
549
        [NOERR(EFI_UNSUPPORTED)]          = "Unsupported",
550
        [NOERR(EFI_BAD_BUFFER_SIZE)]      = "Bad buffer size",
551
        [NOERR(EFI_BUFFER_TOO_SMALL)]     = "Buffer too small",
552
        [NOERR(EFI_NOT_READY)]            = "Not ready",
553
        [NOERR(EFI_DEVICE_ERROR)]         = "Device error",
554
        [NOERR(EFI_WRITE_PROTECTED)]      = "Write protected",
555
        [NOERR(EFI_OUT_OF_RESOURCES)]     = "Out of resources",
556
        [NOERR(EFI_VOLUME_CORRUPTED)]     = "Volume corrupt",
557
        [NOERR(EFI_VOLUME_FULL)]          = "Volume full",
558
        [NOERR(EFI_NO_MEDIA)]             = "No media",
559
        [NOERR(EFI_MEDIA_CHANGED)]        = "Media changed",
560
        [NOERR(EFI_NOT_FOUND)]            = "Not found",
561
        [NOERR(EFI_ACCESS_DENIED)]        = "Access denied",
562
        [NOERR(EFI_NO_RESPONSE)]          = "No response",
563
        [NOERR(EFI_NO_MAPPING)]           = "No mapping",
564
        [NOERR(EFI_TIMEOUT)]              = "Time out",
565
        [NOERR(EFI_NOT_STARTED)]          = "Not started",
566
        [NOERR(EFI_ALREADY_STARTED)]      = "Already started",
567
        [NOERR(EFI_ABORTED)]              = "Aborted",
568
        [NOERR(EFI_ICMP_ERROR)]           = "ICMP error",
569
        [NOERR(EFI_TFTP_ERROR)]           = "TFTP error",
570
        [NOERR(EFI_PROTOCOL_ERROR)]       = "Protocol error",
571
        [NOERR(EFI_INCOMPATIBLE_VERSION)] = "Incompatible version",
572
        [NOERR(EFI_SECURITY_VIOLATION)]   = "Security violation",
573
        [NOERR(EFI_CRC_ERROR)]            = "CRC error",
574
        [NOERR(EFI_END_OF_MEDIA)]         = "End of media",
575
        [NOERR(EFI_ERROR_RESERVED_29)]    = "Reserved (29)",
576
        [NOERR(EFI_ERROR_RESERVED_30)]    = "Reserved (30)",
577
        [NOERR(EFI_END_OF_FILE)]          = "End of file",
578
        [NOERR(EFI_INVALID_LANGUAGE)]     = "Invalid language",
579
        [NOERR(EFI_COMPROMISED_DATA)]     = "Compromised data",
580
        [NOERR(EFI_IP_ADDRESS_CONFLICT)]  = "IP address conflict",
581
        [NOERR(EFI_HTTP_ERROR)]           = "HTTP error",
582
};
583
584
333
static const char *status_to_string(EFI_STATUS status) {
585
333
        if (status <= ELEMENTSOF(warn_table) - 1)
586
14
                return warn_table[status];
587
319
        if (status >= EFI_ERROR_MASK && status <= ((ELEMENTSOF(err_table) - 1) | EFI_ERROR_MASK))
588
28
                return err_table[NOERR(status)];
589
291
        return NULL;
590
319
}
591
592
typedef struct {
593
        size_t padded_len; /* Field width in printf. */
594
        size_t len;        /* Precision in printf. */
595
        bool pad_zero;
596
        bool align_left;
597
        bool alternative_form;
598
        bool long_arg;
599
        bool longlong_arg;
600
        bool have_field_width;
601
602
        const char *str;
603
        const wchar_t *wstr;
604
605
        /* For numbers. */
606
        bool is_signed;
607
        bool lowercase;
608
        int8_t base;
609
        char sign_pad; /* For + and (space) flags. */
610
} SpecifierContext;
611
612
typedef struct {
613
        char16_t stack_buf[128]; /* We use stack_buf first to avoid allocations in most cases. */
614
        char16_t *dyn_buf;       /* Allocated buf or NULL if stack_buf is used. */
615
        char16_t *buf;           /* Points to the current active buf. */
616
        size_t n_buf;            /* Len of buf (in char16_t's, not bytes!). */
617
        size_t n;                /* Used len of buf (in char16_t's). This is always <n_buf. */
618
619
        EFI_STATUS status;
620
        const char *format;
621
        va_list ap;
622
} FormatContext;
623
624
9.78k
static void grow_buf(FormatContext *ctx, size_t need) {
625
9.78k
        assert(ctx);
626
627
9.78k
        assert_se(INC_SAFE(&need, ctx->n));
628
629
9.78k
        if (need < ctx->n_buf)
630
3.91k
                return;
631
632
        /* Greedily allocate if we can. */
633
5.86k
        if (!MUL_SAFE(&ctx->n_buf, need, 2))
634
0
                ctx->n_buf = need;
635
636
        /* We cannot use realloc here as ctx->buf may be ctx->stack_buf, which we cannot free. */
637
5.86k
        char16_t *new_buf = xnew(char16_t, ctx->n_buf);
638
5.86k
        memcpy(new_buf, ctx->buf, ctx->n * sizeof(*ctx->buf));
639
640
5.86k
        free(ctx->dyn_buf);
641
5.86k
        ctx->buf = ctx->dyn_buf = new_buf;
642
5.86k
}
643
644
16.4k
static void push_padding(FormatContext *ctx, char pad, size_t len) {
645
16.4k
        assert(ctx);
646
111M
        while (len > 0) {
647
111M
                len--;
648
111M
                ctx->buf[ctx->n++] = pad;
649
111M
        }
650
16.4k
}
651
652
3.08k
static bool push_str(FormatContext *ctx, SpecifierContext *sp) {
653
3.08k
        assert(ctx);
654
3.08k
        assert(sp);
655
656
3.08k
        sp->padded_len = LESS_BY(sp->padded_len, sp->len);
657
658
3.08k
        grow_buf(ctx, sp->padded_len + sp->len);
659
660
3.08k
        if (!sp->align_left)
661
2.54k
                push_padding(ctx, ' ', sp->padded_len);
662
663
        /* In userspace unit tests we cannot just memcpy() the wide string. */
664
3.08k
        if (sp->wstr && sizeof(wchar_t) == sizeof(char16_t)) {
665
0
                memcpy(ctx->buf + ctx->n, sp->wstr, sp->len * sizeof(*sp->wstr));
666
0
                ctx->n += sp->len;
667
3.08k
        } else {
668
3.08k
                assert(sp->str || sp->wstr);
669
24.1M
                for (size_t i = 0; i < sp->len; i++)
670
24.1M
                        ctx->buf[ctx->n++] = sp->str ? sp->str[i] : sp->wstr[i];
671
3.08k
        }
672
673
3.08k
        if (sp->align_left)
674
540
                push_padding(ctx, ' ', sp->padded_len);
675
676
3.08k
        assert(ctx->n < ctx->n_buf);
677
3.08k
        return true;
678
3.08k
}
679
680
7.23k
static bool push_num(FormatContext *ctx, SpecifierContext *sp, uint64_t u) {
681
7.23k
        const char *digits = sp->lowercase ? LOWERCASE_HEXDIGITS : UPPERCASE_HEXDIGITS;
682
7.23k
        char16_t tmp[32];
683
7.23k
        size_t n = 0;
684
685
7.23k
        assert(ctx);
686
7.23k
        assert(sp);
687
7.23k
        assert(IN_SET(sp->base, 10, 16));
688
689
        /* "%.0u" prints nothing if value is 0. */
690
7.23k
        if (u == 0 && sp->len == 0)
691
543
                return true;
692
693
6.69k
        if (sp->is_signed && (int64_t) u < 0) {
694
                /* We cannot just do "u = -(int64_t)u" here because -INT64_MIN overflows. */
695
696
1.53k
                uint64_t rem = -((int64_t) u % sp->base);
697
1.53k
                u = (int64_t) u / -sp->base;
698
1.53k
                tmp[n++] = digits[rem];
699
1.53k
                sp->sign_pad = '-';
700
1.53k
        }
701
702
75.6k
        while (u > 0 || n == 0) {
703
68.9k
                uint64_t rem = u % sp->base;
704
68.9k
                u /= sp->base;
705
68.9k
                tmp[n++] = digits[rem];
706
68.9k
        }
707
708
        /* Note that numbers never get truncated! */
709
6.69k
        size_t prefix = (sp->sign_pad != 0 ? 1 : 0) + (sp->alternative_form ? 2 : 0);
710
6.69k
        size_t number_len = prefix + MAX(n, sp->len);
711
6.69k
        grow_buf(ctx, MAX(sp->padded_len, number_len));
712
713
6.69k
        size_t padding = 0;
714
6.69k
        if (sp->pad_zero)
715
                /* Leading zeroes go after the sign or 0x prefix. */
716
999
                number_len = MAX(number_len, sp->padded_len);
717
5.69k
        else
718
5.69k
                padding = LESS_BY(sp->padded_len, number_len);
719
720
6.69k
        if (!sp->align_left)
721
2.99k
                push_padding(ctx, ' ', padding);
722
723
6.69k
        if (sp->sign_pad != 0)
724
2.04k
                ctx->buf[ctx->n++] = sp->sign_pad;
725
6.69k
        if (sp->alternative_form) {
726
1.21k
                ctx->buf[ctx->n++] = '0';
727
1.21k
                ctx->buf[ctx->n++] = sp->lowercase ? 'x' : 'X';
728
1.21k
        }
729
730
6.69k
        push_padding(ctx, '0', LESS_BY(number_len, n + prefix));
731
732
77.1k
        while (n > 0)
733
70.4k
                ctx->buf[ctx->n++] = tmp[--n];
734
735
6.69k
        if (sp->align_left)
736
3.70k
                push_padding(ctx, ' ', padding);
737
738
6.69k
        assert(ctx->n < ctx->n_buf);
739
6.69k
        return true;
740
7.23k
}
741
742
/* This helps unit testing. */
743
#if SD_BOOT
744
#  define NULLSTR "(null)"
745
#  define wcsnlen strnlen16
746
#else
747
49
#  define NULLSTR "(nil)"
748
#endif
749
750
40.9k
static bool handle_format_specifier(FormatContext *ctx, SpecifierContext *sp) {
751
        /* Parses one item from the format specifier in ctx and put the info into sp. If we are done with
752
         * this specifier returns true, otherwise this function should be called again. */
753
754
        /* This implementation assumes 32-bit ints. Also note that all types smaller than int are promoted to
755
         * int in vararg functions, which is why we fetch only ints for any such types. The compiler would
756
         * otherwise warn about fetching smaller types. */
757
40.9k
        assert_cc(sizeof(int) == 4);
758
40.9k
        assert_cc(sizeof(wchar_t) <= sizeof(int));
759
40.9k
        assert_cc(sizeof(long long) == sizeof(intmax_t));
760
761
40.9k
        assert(ctx);
762
40.9k
        assert(sp);
763
764
40.9k
        switch (*ctx->format) {
765
666
        case '#':
766
666
                sp->alternative_form = true;
767
666
                return false;
768
6.66k
        case '.':
769
6.66k
                sp->have_field_width = true;
770
6.66k
                return false;
771
666
        case '-':
772
666
                sp->align_left = true;
773
666
                return false;
774
333
        case '+':
775
999
        case ' ':
776
999
                sp->sign_pad = *ctx->format;
777
999
                return false;
778
779
999
        case '0':
780
999
                if (!sp->have_field_width) {
781
999
                        sp->pad_zero = true;
782
999
                        return false;
783
999
                }
784
785
                /* If field width has already been provided then 0 is part of precision (%.0s). */
786
0
                _fallthrough_;
787
788
14.9k
        case '*':
789
15.6k
        case '1' ... '9': {
790
15.6k
                int64_t i;
791
792
15.6k
                if (*ctx->format == '*')
793
14.9k
                        i = va_arg(ctx->ap, int);
794
666
                else {
795
666
                        uint64_t u;
796
666
                        if (!parse_number8(ctx->format, &u, &ctx->format) || u > INT_MAX)
797
0
                                assert_not_reached();
798
666
                        ctx->format--; /* Point it back to the last digit. */
799
666
                        i = u;
800
666
                }
801
802
15.6k
                if (sp->have_field_width) {
803
                        /* Negative precision is ignored. */
804
6.66k
                        if (i >= 0)
805
4.46k
                                sp->len = (size_t) i;
806
8.99k
                } else {
807
                        /* Negative field width is treated as positive field width with '-' flag. */
808
8.99k
                        if (i < 0) {
809
4.17k
                                i *= -1;
810
4.17k
                                sp->align_left = true;
811
4.17k
                        }
812
8.99k
                        sp->padded_len = i;
813
8.99k
                }
814
815
15.6k
                return false;
816
14.9k
        }
817
818
1.99k
        case 'h':
819
1.99k
                if (*(ctx->format + 1) == 'h')
820
999
                        ctx->format++;
821
                /* char/short gets promoted to int, nothing to do here. */
822
1.99k
                return false;
823
824
2.33k
        case 'l':
825
2.33k
                if (*(ctx->format + 1) == 'l') {
826
999
                        ctx->format++;
827
999
                        sp->longlong_arg = true;
828
999
                } else
829
1.33k
                        sp->long_arg = true;
830
2.33k
                return false;
831
832
666
        case 'z':
833
666
                sp->long_arg = sizeof(size_t) == sizeof(long);
834
666
                sp->longlong_arg = !sp->long_arg && sizeof(size_t) == sizeof(long long);
835
666
                return false;
836
837
666
        case 'j':
838
666
                sp->long_arg = sizeof(intmax_t) == sizeof(long);
839
666
                sp->longlong_arg = !sp->long_arg && sizeof(intmax_t) == sizeof(long long);
840
666
                return false;
841
842
333
        case 't':
843
333
                sp->long_arg = sizeof(ptrdiff_t) == sizeof(long);
844
333
                sp->longlong_arg = !sp->long_arg && sizeof(ptrdiff_t) == sizeof(long long);
845
333
                return false;
846
847
333
        case '%':
848
333
                sp->str = "%";
849
333
                sp->len = 1;
850
333
                return push_str(ctx, sp);
851
852
999
        case 'c':
853
999
                sp->wstr = &(wchar_t){ va_arg(ctx->ap, int) };
854
999
                sp->len = 1;
855
999
                return push_str(ctx, sp);
856
857
666
        case 's':
858
666
                if (sp->long_arg) {
859
333
                        sp->wstr = va_arg(ctx->ap, const wchar_t *) ?: L"(null)";
860
333
                        sp->len = wcsnlen(sp->wstr, sp->len);
861
333
                } else {
862
333
                        sp->str = va_arg(ctx->ap, const char *) ?: "(null)";
863
333
                        sp->len = strnlen8(sp->str, sp->len);
864
333
                }
865
666
                return push_str(ctx, sp);
866
867
0
        case 'd':
868
3.99k
        case 'i':
869
5.66k
        case 'u':
870
6.32k
        case 'x':
871
6.66k
        case 'X':
872
6.66k
                sp->lowercase = *ctx->format == 'x';
873
6.66k
                sp->is_signed = IN_SET(*ctx->format, 'd', 'i');
874
6.66k
                sp->base = IN_SET(*ctx->format, 'x', 'X') ? 16 : 10;
875
6.66k
                if (sp->len == SIZE_MAX)
876
3.07k
                        sp->len = 1;
877
878
6.66k
                uint64_t v;
879
6.66k
                if (sp->longlong_arg)
880
999
                        v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long long) :
881
999
                                            va_arg(ctx->ap, unsigned long long);
882
5.66k
                else if (sp->long_arg)
883
2.66k
                        v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long) : va_arg(ctx->ap, unsigned long);
884
2.99k
                else
885
2.99k
                        v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, int) : va_arg(ctx->ap, unsigned);
886
887
6.66k
                return push_num(ctx, sp, v);
888
889
333
        case 'p': {
890
333
                const void *ptr = va_arg(ctx->ap, const void *);
891
333
                if (!ptr) {
892
49
                        sp->str = NULLSTR;
893
49
                        sp->len = STRLEN(NULLSTR);
894
49
                        return push_str(ctx, sp);
895
49
                }
896
897
284
                sp->base = 16;
898
284
                sp->lowercase = true;
899
284
                sp->alternative_form = true;
900
284
                sp->len = 0; /* Precision is ignored for %p. */
901
284
                return push_num(ctx, sp, (uintptr_t) ptr);
902
333
        }
903
904
333
        case 'm': {
905
333
                sp->str = status_to_string(ctx->status);
906
333
                if (sp->str) {
907
42
                        sp->len = strlen8(sp->str);
908
42
                        return push_str(ctx, sp);
909
42
                }
910
911
291
                sp->base = 16;
912
291
                sp->lowercase = true;
913
291
                sp->alternative_form = true;
914
291
                sp->len = 0;
915
291
                return push_num(ctx, sp, ctx->status);
916
333
        }
917
918
0
        default:
919
0
                assert_not_reached();
920
40.9k
        }
921
40.9k
}
922
923
#if SD_BOOT
924
static void output_string_safe(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *this, const char16_t *s) {
925
        assert(this);
926
        assert(s);
927
928
        /* This is a color-conscious version of ST->ConOut->OutputString(). Whenever it encounters a newline
929
         * character, it will reset the color to our default, because some UEFI implementations/terminals
930
         * reset the color in that case, and we want our default color to remain in effect */
931
932
        int32_t saved_attribute = ST->ConOut->Mode->Attribute;
933
        for (;;) {
934
                const char16_t *nl = strchr16(s, '\n');
935
                if (!nl) /* No further newline */
936
                        return (void) ST->ConOut->OutputString(ST->ConOut, (char16_t*) s);
937
938
                if (nl[1] == 0) { /* Newline is at the end of the string */
939
                        (void) ST->ConOut->OutputString(ST->ConOut, (char16_t*) s);
940
                        set_attribute_safe(saved_attribute);
941
                        return;
942
                }
943
944
                /* newline is in the middle of the string */
945
                _cleanup_free_ char16_t *x = xstrndup16(s, nl - s + 1);
946
                (void) ST->ConOut->OutputString(ST->ConOut, x);
947
                set_attribute_safe(saved_attribute);
948
949
                s = nl + 1;
950
        }
951
}
952
#endif
953
954
/* printf_internal is largely compatible to userspace vasprintf. Any features omitted should trigger asserts.
955
 *
956
 * Supported:
957
 *  - Flags: #, 0, +, -, space
958
 *  - Lengths: h, hh, l, ll, z, j, t
959
 *  - Specifiers: %, c, s, u, i, d, x, X, p, m
960
 *  - Precision and width (inline or as int arg using *)
961
 *
962
 * Notable differences:
963
 *  - Passing NULL to %s is permitted and will print "(null)"
964
 *  - %p will also use "(null)"
965
 *  - The provided EFI_STATUS is used for %m instead of errno
966
 *  - "\n" is translated to "\r\n" */
967
8.32k
_printf_(2, 0) static char16_t *printf_internal(EFI_STATUS status, const char *format, va_list ap, bool ret) {
968
8.32k
        assert(format);
969
970
8.32k
        FormatContext ctx = {
971
8.32k
                .buf = ctx.stack_buf,
972
8.32k
                .n_buf = ELEMENTSOF(ctx.stack_buf),
973
8.32k
                .format = format,
974
8.32k
                .status = status,
975
8.32k
        };
976
977
        /* We cannot put this into the struct without making a copy. */
978
8.32k
        va_copy(ctx.ap, ap);
979
980
18.6k
        while (*ctx.format != '\0') {
981
10.3k
                SpecifierContext sp = { .len = SIZE_MAX };
982
983
10.3k
                switch (*ctx.format) {
984
9.32k
                case '%':
985
9.32k
                        ctx.format++;
986
40.9k
                        while (!handle_format_specifier(&ctx, &sp))
987
31.6k
                                ctx.format++;
988
9.32k
                        ctx.format++;
989
9.32k
                        break;
990
0
                case '\n':
991
0
                        ctx.format++;
992
0
                        sp.str = "\r\n";
993
0
                        sp.len = 2;
994
0
                        push_str(&ctx, &sp);
995
0
                        break;
996
999
                default:
997
999
                        sp.str = ctx.format++;
998
999
                        while (!IN_SET(*ctx.format, '%', '\n', '\0'))
999
0
                                ctx.format++;
1000
999
                        sp.len = ctx.format - sp.str;
1001
999
                        push_str(&ctx, &sp);
1002
10.3k
                }
1003
10.3k
        }
1004
1005
8.32k
        va_end(ctx.ap);
1006
1007
8.32k
        assert(ctx.n < ctx.n_buf);
1008
8.32k
        ctx.buf[ctx.n++] = '\0';
1009
1010
8.32k
        if (ret) {
1011
8.32k
                if (ctx.dyn_buf)
1012
5.42k
                        return TAKE_PTR(ctx.dyn_buf);
1013
1014
2.90k
                char16_t *ret_buf = xnew(char16_t, ctx.n);
1015
2.90k
                memcpy(ret_buf, ctx.buf, ctx.n * sizeof(*ctx.buf));
1016
2.90k
                return ret_buf;
1017
8.32k
        }
1018
1019
#if SD_BOOT
1020
        output_string_safe(ST->ConOut, ctx.buf);
1021
#endif
1022
1023
0
        return mfree(ctx.dyn_buf);
1024
8.32k
}
1025
1026
0
void printf_status(EFI_STATUS status, const char *format, ...) {
1027
0
        va_list ap;
1028
0
        va_start(ap, format);
1029
0
        printf_internal(status, format, ap, false);
1030
0
        va_end(ap);
1031
0
}
1032
1033
0
void vprintf_status(EFI_STATUS status, const char *format, va_list ap) {
1034
0
        printf_internal(status, format, ap, false);
1035
0
}
1036
1037
8.32k
char16_t *xasprintf_status(EFI_STATUS status, const char *format, ...) {
1038
8.32k
        va_list ap;
1039
8.32k
        va_start(ap, format);
1040
8.32k
        char16_t *ret = printf_internal(status, format, ap, true);
1041
8.32k
        va_end(ap);
1042
8.32k
        return ret;
1043
8.32k
}
1044
1045
0
char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) {
1046
0
        return printf_internal(status, format, ap, true);
1047
0
}
1048
1049
#if SD_BOOT
1050
/* To provide the actual implementation for these we need to remove the redirection to the builtins. */
1051
#  undef memchr
1052
#  undef memcmp
1053
#  undef memcpy
1054
#  undef memset
1055
// NOLINTBEGIN(misc-use-internal-linkage)
1056
_used_ void *memchr(const void *p, int c, size_t n);
1057
_used_ int memcmp(const void *p1, const void *p2, size_t n);
1058
_used_ void *memcpy(void * restrict dest, const void * restrict src, size_t n);
1059
_used_ void *memset(void *p, int c, size_t n);
1060
// NOLINTEND(misc-use-internal-linkage)
1061
#else
1062
/* And for userspace unit testing we need to give them an efi_ prefix. */
1063
#  undef memchr
1064
#  define memchr efi_memchr
1065
#  define memcmp efi_memcmp
1066
#  define memcpy efi_memcpy
1067
#  define memset efi_memset
1068
#endif
1069
1070
0
void *memchr(const void *p, int c, size_t n) {
1071
0
        if (!p || n == 0)
1072
0
                return NULL;
1073
1074
0
        const uint8_t *q = p;
1075
0
        for (size_t i = 0; i < n; i++)
1076
0
                if (q[i] == (unsigned char) c)
1077
0
                        return (void *) (q + i);
1078
1079
0
        return NULL;
1080
0
}
1081
1082
0
int memcmp(const void *p1, const void *p2, size_t n) {
1083
0
        const uint8_t *up1 = p1, *up2 = p2;
1084
0
        int r;
1085
1086
0
        if (!p1 || !p2)
1087
0
                return CMP(p1, p2);
1088
1089
0
        while (n > 0) {
1090
0
                r = CMP(*up1, *up2);
1091
0
                if (r != 0)
1092
0
                        return r;
1093
1094
0
                up1++;
1095
0
                up2++;
1096
0
                n--;
1097
0
        }
1098
1099
0
        return 0;
1100
0
}
1101
1102
0
void *memcpy(void * restrict dest, const void * restrict src, size_t n) {
1103
0
        if (!dest || !src || n == 0)
1104
0
                return dest;
1105
1106
#if SD_BOOT
1107
        /* The firmware-provided memcpy is likely optimized, so use that. The function is guaranteed to be
1108
         * available by the UEFI spec. We still make it depend on the boot services pointer being set just in
1109
         * case the compiler emits a call before it is available. */
1110
        if (_likely_(BS)) {
1111
                BS->CopyMem(dest, (void *) src, n);
1112
                return dest;
1113
        }
1114
#endif
1115
1116
0
        uint8_t *d = dest;
1117
0
        const uint8_t *s = src;
1118
1119
0
        while (n > 0) {
1120
0
                *d = *s;
1121
0
                d++;
1122
0
                s++;
1123
0
                n--;
1124
0
        }
1125
1126
0
        return dest;
1127
0
}
1128
1129
0
void *memset(void *p, int c, size_t n) {
1130
0
        if (!p || n == 0)
1131
0
                return p;
1132
1133
#if SD_BOOT
1134
        /* See comment in efi_memcpy. Note that the signature has c and n swapped! */
1135
        if (_likely_(BS)) {
1136
                BS->SetMem(p, n, c);
1137
                return p;
1138
        }
1139
#endif
1140
1141
0
        uint8_t *q = p;
1142
0
        while (n > 0) {
1143
0
                *q = c;
1144
0
                q++;
1145
0
                n--;
1146
0
        }
1147
1148
0
        return p;
1149
0
}
1150
1151
0
size_t strspn16(const char16_t *p, const char16_t *good) {
1152
0
        assert(p);
1153
0
        assert(good);
1154
1155
0
        const char16_t *i = p;
1156
0
        for (; *i != 0; i++)
1157
0
                if (!strchr16(good, *i))
1158
0
                        break;
1159
1160
0
        return i - p;
1161
0
}
1162
1163
0
size_t strcspn16(const char16_t *p, const char16_t *bad) {
1164
0
        assert(p);
1165
0
        assert(bad);
1166
1167
0
        const char16_t *i = p;
1168
0
        for (; *i != 0; i++)
1169
0
                if (strchr16(bad, *i))
1170
0
                        break;
1171
1172
0
        return i - p;
1173
0
}