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

systemd / systemd / 14554080340

19 Apr 2025 11:46AM UTC coverage: 72.101% (-0.03%) from 72.13%
14554080340

push

github

web-flow
Add two new paragraphs to coding style about header files (#37188)

296880 of 411754 relevant lines covered (72.1%)

687547.52 hits per line

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

56.91
/src/shared/efi-loader.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include "alloc-util.h"
4
#include "efi-api.h"
5
#include "efi-loader.h"
6
#include "env-util.h"
7
#include "log.h"
8
#include "parse-util.h"
9
#include "path-util.h"
10
#include "stat-util.h"
11
#include "strv.h"
12
#include "tpm2-pcr.h"
13
#include "utf8.h"
14

15
#if ENABLE_EFI
16

17
static int read_usec(const char *variable, usec_t *ret) {
10✔
18
        _cleanup_free_ char *j = NULL;
10✔
19
        uint64_t x = 0;
10✔
20
        int r;
10✔
21

22
        assert(variable);
10✔
23
        assert(ret);
10✔
24

25
        r = efi_get_variable_string(variable, &j);
10✔
26
        if (r < 0)
10✔
27
                return r;
28

29
        r = safe_atou64(j, &x);
×
30
        if (r < 0)
×
31
                return r;
32

33
        *ret = x;
×
34
        return 0;
×
35
}
36

37
int efi_loader_get_boot_usec(usec_t *ret_firmware, usec_t *ret_loader) {
24✔
38
        uint64_t x, y;
24✔
39
        int r;
24✔
40

41
        assert(ret_firmware);
24✔
42
        assert(ret_loader);
24✔
43

44
        if (!is_efi_boot())
24✔
45
                return -EOPNOTSUPP;
24✔
46

47
        r = read_usec(EFI_LOADER_VARIABLE_STR("LoaderTimeInitUSec"), &x);
10✔
48
        if (r < 0)
10✔
49
                return log_debug_errno(r, "Failed to read LoaderTimeInitUSec: %m");
10✔
50

51
        r = read_usec(EFI_LOADER_VARIABLE_STR("LoaderTimeExecUSec"), &y);
×
52
        if (r < 0)
×
53
                return log_debug_errno(r, "Failed to read LoaderTimeExecUSec: %m");
×
54

55
        if (y == 0 || y < x || y - x > USEC_PER_HOUR)
×
56
                return log_debug_errno(SYNTHETIC_ERRNO(EIO),
×
57
                                       "Bad LoaderTimeInitUSec=%"PRIu64", LoaderTimeExecUSec=%" PRIu64"; refusing.",
58
                                       x, y);
59

60
        *ret_firmware = x;
×
61
        *ret_loader = y;
×
62
        return 0;
×
63
}
64

65
static int get_device_part_uuid(const char *variable, sd_id128_t *ret) {
28✔
66
        if (!is_efi_boot())
28✔
67
                return -EOPNOTSUPP;
68

69
        return efi_get_variable_id128(variable, ret);
22✔
70
}
71

72
int efi_loader_get_device_part_uuid(sd_id128_t *ret) {
16✔
73
        return get_device_part_uuid(EFI_LOADER_VARIABLE_STR("LoaderDevicePartUUID"), ret);
16✔
74
}
75

76
int efi_stub_get_device_part_uuid(sd_id128_t *ret) {
12✔
77
        return get_device_part_uuid(EFI_LOADER_VARIABLE_STR("StubDevicePartUUID"), ret);
12✔
78
}
79

80
int efi_loader_get_entries(char ***ret) {
14✔
81
        _cleanup_free_ char16_t *entries = NULL;
14✔
82
        _cleanup_strv_free_ char **l = NULL;
14✔
83
        size_t size;
14✔
84
        int r;
14✔
85

86
        assert(ret);
14✔
87

88
        if (!is_efi_boot())
14✔
89
                return -EOPNOTSUPP;
90

91
        r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderEntries"), NULL, (void**) &entries, &size);
6✔
92
        if (r < 0)
6✔
93
                return r;
94

95
        /* The variable contains a series of individually NUL terminated UTF-16 strings. We gracefully
96
         * consider the final NUL byte optional (i.e. the last string may or may not end in a NUL byte). */
97

98
        for (size_t i = 0, start = 0;; i++) {
720✔
99
                _cleanup_free_ char *decoded = NULL;
24✔
100
                bool end;
726✔
101

102
                /* Is this the end of the variable's data? */
103
                end = i * sizeof(char16_t) >= size;
726✔
104

105
                /* Are we in the middle of a string? (i.e. not at the end of the variable, nor at a NUL terminator?) If
106
                 * so, let's go to the next entry. */
107
                if (!end && entries[i] != 0)
726✔
108
                        continue;
696✔
109

110
                /* Empty string at the end of variable? That's the trailer, we are done (i.e. we have a final
111
                 * NUL terminator). */
112
                if (end && start == i)
30✔
113
                        break;
114

115
                /* We reached the end of a string, let's decode it into UTF-8 */
116
                decoded = utf16_to_utf8(entries + start, (i - start) * sizeof(char16_t));
24✔
117
                if (!decoded)
24✔
118
                        return -ENOMEM;
119

120
                if (efi_loader_entry_name_valid(decoded)) {
24✔
121
                        r = strv_consume(&l, TAKE_PTR(decoded));
24✔
122
                        if (r < 0)
24✔
123
                                return r;
124
                } else
125
                        log_debug("Ignoring invalid loader entry '%s'.", decoded);
×
126

127
                /* Exit the loop if we reached the end of the variable (i.e. we do not have a final NUL
128
                 * terminator) */
129
                if (end)
24✔
130
                        break;
131

132
                /* Continue after the NUL byte */
133
                start = i + 1;
24✔
134
        }
135

136
        *ret = TAKE_PTR(l);
6✔
137
        return 0;
6✔
138
}
139

140
int efi_loader_get_features(uint64_t *ret) {
6✔
141
        _cleanup_free_ void *v = NULL;
6✔
142
        size_t s;
6✔
143
        int r;
6✔
144

145
        assert(ret);
6✔
146

147
        if (!is_efi_boot()) {
6✔
148
                *ret = 0;
×
149
                return 0;
×
150
        }
151

152
        r = efi_get_variable(EFI_LOADER_VARIABLE_STR("LoaderFeatures"), NULL, &v, &s);
6✔
153
        if (r == -ENOENT) {
6✔
154
                _cleanup_free_ char *info = NULL;
×
155

156
                /* The new (v240+) LoaderFeatures variable is not supported, let's see if it's systemd-boot at all */
157
                r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderInfo"), &info);
×
158
                if (r < 0) {
×
159
                        if (r != -ENOENT)
×
160
                                return r;
161

162
                        /* Variable not set, definitely means not systemd-boot */
163

164
                } else if (first_word(info, "systemd-boot")) {
×
165

166
                        /* An older systemd-boot version. Let's hardcode the feature set, since it was pretty
167
                         * static in all its versions. */
168

169
                        *ret = EFI_LOADER_FEATURE_CONFIG_TIMEOUT |
×
170
                                EFI_LOADER_FEATURE_ENTRY_DEFAULT |
171
                                EFI_LOADER_FEATURE_ENTRY_ONESHOT;
172

173
                        return 0;
×
174
                }
175

176
                /* No features supported */
177
                *ret = 0;
×
178
                return 0;
×
179
        }
180
        if (r < 0)
6✔
181
                return r;
182

183
        if (s != sizeof(uint64_t))
6✔
184
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
185
                                       "LoaderFeatures EFI variable doesn't have the right size.");
186

187
        memcpy(ret, v, sizeof(uint64_t));
6✔
188
        return 0;
6✔
189
}
190

191
int efi_stub_get_features(uint64_t *ret) {
12✔
192
        _cleanup_free_ void *v = NULL;
12✔
193
        size_t s;
12✔
194
        int r;
12✔
195

196
        assert(ret);
12✔
197

198
        if (!is_efi_boot()) {
12✔
199
                *ret = 0;
×
200
                return 0;
×
201
        }
202

203
        r = efi_get_variable(EFI_LOADER_VARIABLE_STR("StubFeatures"), NULL, &v, &s);
12✔
204
        if (r == -ENOENT) {
12✔
205
                _cleanup_free_ char *info = NULL;
×
206

207
                /* The new (v252+) StubFeatures variable is not supported, let's see if it's systemd-stub at all */
208
                r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("StubInfo"), &info);
×
209
                if (r < 0) {
×
210
                        if (r != -ENOENT)
×
211
                                return r;
212

213
                        /* Variable not set, definitely means not systemd-stub */
214

215
                } else if (first_word(info, "systemd-stub")) {
×
216

217
                        /* An older systemd-stub version. Let's hardcode the feature set, since it was pretty
218
                         * static in all its versions. */
219

220
                        *ret = EFI_STUB_FEATURE_REPORT_BOOT_PARTITION;
×
221
                        return 0;
×
222
                }
223

224
                /* No features supported */
225
                *ret = 0;
×
226
                return 0;
×
227
        }
228
        if (r < 0)
12✔
229
                return r;
230

231
        if (s != sizeof(uint64_t))
12✔
232
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
×
233
                                       "StubFeatures EFI variable doesn't have the right size.");
234

235
        memcpy(ret, v, sizeof(uint64_t));
12✔
236
        return 0;
12✔
237
}
238

239
int efi_measured_uki(int log_level) {
308✔
240
        _cleanup_free_ char *pcr_string = NULL;
308✔
241
        static int cached = -1;
308✔
242
        unsigned pcr_nr;
308✔
243
        int r;
308✔
244

245
        if (cached >= 0)
308✔
246
                return cached;
247

248
        /* Checks if we are booted on a kernel with sd-stub which measured the kernel into PCR 11 on a TPM2
249
         * chip. Or in other words, if we are running on a TPM enabled UKI. (TPM 1.2 situations are ignored.)
250
         *
251
         * Returns == 0 and > 0 depending on the result of the test. Returns -EREMOTE if we detected a stub
252
         * being used, but it measured things into a different PCR than we are configured for in
253
         * userspace. (i.e. we expect PCR 11 being used for this by both sd-stub and us) */
254

255
        r = secure_getenv_bool("SYSTEMD_FORCE_MEASURE"); /* Give user a chance to override the variable test,
99✔
256
                                                          * for debugging purposes */
257
        if (r >= 0)
99✔
258
                return (cached = r);
17✔
259
        if (r != -ENXIO)
82✔
260
                log_debug_errno(r, "Failed to parse $SYSTEMD_FORCE_MEASURE, ignoring: %m");
×
261

262
        if (!efi_has_tpm2())
82✔
263
                return (cached = 0);
23✔
264

265
        r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("StubPcrKernelImage"), &pcr_string);
59✔
266
        if (r == -ENOENT)
59✔
267
                return (cached = 0);
×
268
        if (r < 0)
59✔
269
                return log_full_errno(log_level, r,
×
270
                                      "Failed to get StubPcrKernelImage EFI variable: %m");
271

272
        r = safe_atou(pcr_string, &pcr_nr);
59✔
273
        if (r < 0)
59✔
274
                return log_full_errno(log_level, r,
×
275
                                      "Failed to parse StubPcrKernelImage EFI variable: %s", pcr_string);
276
        if (pcr_nr != TPM2_PCR_KERNEL_BOOT)
59✔
277
                return log_full_errno(log_level, SYNTHETIC_ERRNO(EREMOTE),
×
278
                                      "Kernel stub measured kernel image into PCR %u, which is different than expected %i.",
279
                                      pcr_nr, TPM2_PCR_KERNEL_BOOT);
280

281
        return (cached = 1);
59✔
282
}
283

284
int efi_loader_get_config_timeout_one_shot(usec_t *ret) {
×
285
        _cleanup_free_ char *v = NULL;
×
286
        static struct stat cache_stat = {};
×
287
        struct stat new_stat;
×
288
        static usec_t cache;
×
289
        uint64_t sec;
×
290
        int r;
×
291

292
        assert(ret);
×
293

294
        /* stat() the EFI variable, to see if the mtime changed. If it did, we need to cache again. */
295
        if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot")), &new_stat) < 0)
×
296
                return -errno;
×
297

298
        if (stat_inode_unmodified(&new_stat, &cache_stat)) {
×
299
                *ret = cache;
×
300
                return 0;
×
301
        }
302

303
        r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderConfigTimeoutOneShot"), &v);
×
304
        if (r < 0)
×
305
                return r;
306

307
        r = safe_atou64(v, &sec);
×
308
        if (r < 0)
×
309
                return r;
310
        if (sec > USEC_INFINITY / USEC_PER_SEC)
×
311
                return -ERANGE;
312

313
        cache_stat = new_stat;
×
314
        *ret = cache = sec * USEC_PER_SEC; /* return in μs */
×
315
        return 0;
×
316
}
317

318
int efi_loader_update_entry_one_shot_cache(char **cache, struct stat *cache_stat) {
×
319
        _cleanup_free_ char *v = NULL;
×
320
        struct stat new_stat;
×
321
        int r;
×
322

323
        assert(cache);
×
324
        assert(cache_stat);
×
325

326
        /* stat() the EFI variable, to see if the mtime changed. If it did we need to cache again. */
327
        if (stat(EFIVAR_PATH(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot")), &new_stat) < 0)
×
328
                return -errno;
×
329

330
        if (stat_inode_unmodified(&new_stat, cache_stat))
×
331
                return 0;
332

333
        r = efi_get_variable_string(EFI_LOADER_VARIABLE_STR("LoaderEntryOneShot"), &v);
×
334
        if (r < 0)
×
335
                return r;
336

337
        if (!efi_loader_entry_name_valid(v))
×
338
                return -EINVAL;
339

340
        *cache_stat = new_stat;
×
341
        free_and_replace(*cache, v);
×
342

343
        return 0;
×
344
}
345

346
int efi_get_variable_id128(const char *variable, sd_id128_t *ret) {
22✔
347
        int r;
22✔
348

349
        assert(variable);
22✔
350

351
        /* This is placed here (rather than in basic/efivars.c) because code in basic/ is not allowed to link
352
         * against libsystemd.so */
353

354
        _cleanup_free_ char *p = NULL;
22✔
355
        r = efi_get_variable_string(variable, &p);
22✔
356
        if (r < 0)
22✔
357
                return r;
358

359
        return sd_id128_from_string(p, ret);
22✔
360
}
361

362
#endif
363

364
bool efi_loader_entry_name_valid(const char *s) {
74✔
365
        if (!filename_is_valid(s)) /* Make sure entry names fit in filenames */
74✔
366
                return false;
367

368
        return in_charset(s, ALPHANUMERICAL "+-_.@");
74✔
369
}
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