• 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

75.82
/src/basic/efivars.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <fcntl.h>
5
#include <limits.h>
6
#include <stdlib.h>
7
#include <sys/stat.h>
8
#include <unistd.h>
9

10
#include "sd-id128.h"
11

12
#include "alloc-util.h"
13
#include "chattr-util.h"
14
#include "efivars.h"
15
#include "fd-util.h"
16
#include "fileio.h"
17
#include "io-util.h"
18
#include "log.h"
19
#include "macro.h"
20
#include "memory-util.h"
21
#include "missing_fs.h"
22
#include "stdio-util.h"
23
#include "strv.h"
24
#include "time-util.h"
25
#include "utf8.h"
26
#include "virt.h"
27

28
#if ENABLE_EFI
29

30
/* Reads from efivarfs sometimes fail with EINTR. Retry that many times. */
31
#define EFI_N_RETRIES_NO_DELAY 20
32
#define EFI_N_RETRIES_TOTAL 25
33
#define EFI_RETRY_DELAY (50 * USEC_PER_MSEC)
34

35
int efi_get_variable(
286✔
36
                const char *variable,
37
                uint32_t *ret_attribute,
38
                void **ret_value,
39
                size_t *ret_size) {
40

41
        _cleanup_close_ int fd = -EBADF;
286✔
42
        _cleanup_free_ void *buf = NULL;
286✔
43
        struct stat st;
286✔
44
        usec_t begin = 0; /* Unnecessary initialization to appease gcc */
286✔
45
        uint32_t a;
286✔
46
        ssize_t n;
286✔
47

48
        assert(variable);
286✔
49

50
        const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
1,430✔
51

52
        if (!ret_value && !ret_size && !ret_attribute) {
286✔
53
                /* If caller is not interested in anything, just check if the variable exists and is
54
                 * readable. */
55
                if (access(p, R_OK) < 0)
×
56
                        return -errno;
×
57

58
                return 0;
59
        }
60

61
        if (DEBUG_LOGGING) {
286✔
62
                log_debug("Reading EFI variable %s.", p);
286✔
63
                begin = now(CLOCK_MONOTONIC);
286✔
64
        }
65

66
        fd = open(p, O_RDONLY|O_NOCTTY|O_CLOEXEC);
286✔
67
        if (fd < 0)
286✔
68
                return log_debug_errno(errno, "open(\"%s\") failed: %m", p);
80✔
69

70
        if (fstat(fd, &st) < 0)
206✔
71
                return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
×
72
        if (st.st_size < 4)
206✔
73
                return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable %s is shorter than 4 bytes, refusing.", p);
×
74
        if (st.st_size > 4*1024*1024 + 4)
206✔
75
                return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable %s is ridiculously large, refusing.", p);
×
76

77
        if (ret_value || ret_attribute) {
206✔
78
                /* The kernel ratelimits reads from the efivarfs because EFI is inefficient, and we'll
79
                 * occasionally fail with EINTR here. A slowdown is better than a failure for us, so
80
                 * retry a few times and eventually fail with -EBUSY.
81
                 *
82
                 * See https://github.com/torvalds/linux/blob/master/fs/efivarfs/file.c#L75
83
                 * and
84
                 * https://github.com/torvalds/linux/commit/bef3efbeb897b56867e271cdbc5f8adaacaeb9cd.
85
                 */
86
                for (unsigned try = 0;; try++) {
×
87
                        n = read(fd, &a, sizeof(a));
203✔
88
                        if (n >= 0)
203✔
89
                                break;
90
                        log_debug_errno(errno, "Reading from \"%s\" failed: %m", p);
×
91
                        if (errno != EINTR)
×
92
                                return -errno;
×
93
                        if (try >= EFI_N_RETRIES_TOTAL)
×
94
                                return -EBUSY;
95

96
                        if (try >= EFI_N_RETRIES_NO_DELAY)
×
97
                                (void) usleep_safe(EFI_RETRY_DELAY);
×
98
                }
99

100
                /* Unfortunately kernel reports EOF if there's an inconsistency between efivarfs var list
101
                 * and what's actually stored in firmware, c.f. #34304. A zero size env var is not allowed in
102
                 * efi and hence the variable doesn't really exist in the backing store as long as it is zero
103
                 * sized, and the kernel calls this "uncommitted". Hence we translate EOF back to ENOENT here,
104
                 * as with kernel behavior before
105
                 * https://github.com/torvalds/linux/commit/3fab70c165795431f00ddf9be8b84ddd07bd1f8f
106
                 *
107
                 * If the kernel changes behaviour (to flush dentries on resume), we can drop
108
                 * this at some point in the future. But note that the commit is 11
109
                 * years old at this point so we'll need to deal with the current behaviour for
110
                 * a long time.
111
                 */
112
                if (n == 0)
203✔
113
                        return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
×
114
                                               "EFI variable %s is uncommitted", p);
115

116
                if (n != sizeof(a))
203✔
117
                        return log_debug_errno(SYNTHETIC_ERRNO(EIO),
×
118
                                               "Read %zi bytes from EFI variable %s, expected %zu.",  n, p, sizeof(a));
119
        }
120

121
        if (ret_value) {
203✔
122
                buf = malloc(st.st_size - 4 + 3);
203✔
123
                if (!buf)
203✔
124
                        return -ENOMEM;
125

126
                n = read(fd, buf, (size_t) st.st_size - 4);
203✔
127
                if (n < 0)
203✔
128
                        return log_debug_errno(errno, "Failed to read value of EFI variable %s: %m", p);
×
129
                assert(n <= st.st_size - 4);
203✔
130

131
                /* Always NUL-terminate (3 bytes, to properly protect UTF-16, even if truncated in the middle
132
                 * of a character) */
133
                ((char*) buf)[n] = 0;
203✔
134
                ((char*) buf)[n + 1] = 0;
203✔
135
                ((char*) buf)[n + 2] = 0;
203✔
136
        } else
137
                /* Assume that the reported size is accurate */
138
                n = st.st_size - 4;
3✔
139

140
        if (DEBUG_LOGGING) {
206✔
141
                usec_t end = now(CLOCK_MONOTONIC);
206✔
142
                if (end > begin + EFI_RETRY_DELAY)
206✔
143
                        log_debug("Detected slow EFI variable read access on %s: %s",
×
144
                                  variable, FORMAT_TIMESPAN(end - begin, 1));
145
        }
146

147
        /* Note that efivarfs interestingly doesn't require ftruncate() to update an existing EFI variable
148
         * with a smaller value. */
149

150
        if (ret_attribute)
206✔
151
                *ret_attribute = a;
2✔
152

153
        if (ret_value)
206✔
154
                *ret_value = TAKE_PTR(buf);
203✔
155

156
        if (ret_size)
206✔
157
                *ret_size = n;
206✔
158

159
        return 0;
160
}
161

162
int efi_get_variable_string(const char *variable, char **ret) {
209✔
163
        _cleanup_free_ void *s = NULL;
209✔
164
        size_t ss = 0;
209✔
165
        char *x;
209✔
166
        int r;
209✔
167

168
        assert(variable);
209✔
169

170
        r = efi_get_variable(variable, NULL, &s, &ss);
209✔
171
        if (r < 0)
209✔
172
                return r;
173

174
        x = utf16_to_utf8(s, ss);
147✔
175
        if (!x)
147✔
176
                return -ENOMEM;
177

178
        if (ret)
147✔
179
                *ret = x;
147✔
180

181
        return 0;
182
}
183

184
int efi_get_variable_path(const char *variable, char **ret) {
18✔
185
        int r;
18✔
186

187
        assert(variable);
18✔
188

189
        r = efi_get_variable_string(variable, ret);
18✔
190
        if (r < 0)
18✔
191
                return r;
192

193
        if (ret)
18✔
194
                efi_tilt_backslashes(*ret);
18✔
195

196
        return r;
197
}
198

199
static int efi_verify_variable(const char *variable, uint32_t attr, const void *value, size_t size) {
9✔
200
        _cleanup_free_ void *buf = NULL;
9✔
201
        size_t n;
9✔
202
        uint32_t a;
9✔
203
        int r;
9✔
204

205
        assert(variable);
9✔
206
        assert(value || size == 0);
9✔
207

208
        r = efi_get_variable(variable, &a, &buf, &n);
9✔
209
        if (r < 0)
9✔
210
                return r;
211

212
        return a == attr && memcmp_nn(buf, n, value, size) == 0;
2✔
213
}
214

215
int efi_set_variable(const char *variable, const void *value, size_t size) {
9✔
216
        static const uint32_t attr = EFI_VARIABLE_NON_VOLATILE|EFI_VARIABLE_BOOTSERVICE_ACCESS|EFI_VARIABLE_RUNTIME_ACCESS;
9✔
217

218
        struct var {
9✔
219
                uint32_t attr;
220
                char buf[];
221
        } _packed_ * _cleanup_free_ buf = NULL;
9✔
222
        _cleanup_close_ int fd = -EBADF;
9✔
223
        bool saved_flags_valid = false;
9✔
224
        unsigned saved_flags;
9✔
225
        int r;
9✔
226

227
        assert(variable);
9✔
228
        assert(value || size == 0);
9✔
229

230
        /* size 0 means removal, empty variable would not be enough for that */
231
        if (size > 0 && efi_verify_variable(variable, attr, value, size) > 0) {
9✔
232
                log_debug("Variable '%s' is already in wanted state, skipping write.", variable);
×
233
                return 0;
×
234
        }
235

236
        const char *p = strjoina("/sys/firmware/efi/efivars/", variable);
45✔
237

238
        /* Newer efivarfs protects variables that are not in an allow list with FS_IMMUTABLE_FL by default,
239
         * to protect them for accidental removal and modification. We are not changing these variables
240
         * accidentally however, hence let's unset the bit first. */
241

242
        r = chattr_full(AT_FDCWD, p,
9✔
243
                        /* value = */ 0,
244
                        /* mask = */ FS_IMMUTABLE_FL,
245
                        /* ret_previous = */ &saved_flags,
246
                        /* ret_final = */ NULL,
247
                        /* flags = */ 0);
248
        if (r < 0 && r != -ENOENT)
9✔
249
                log_debug_errno(r, "Failed to drop FS_IMMUTABLE_FL flag from '%s', ignoring: %m", p);
×
250

251
        saved_flags_valid = r >= 0;
9✔
252

253
        if (size == 0) {
9✔
254
                if (unlink(p) < 0) {
×
255
                        r = -errno;
×
256
                        goto finish;
×
257
                }
258

259
                return 0;
260
        }
261

262
        fd = open(p, O_WRONLY|O_CREAT|O_NOCTTY|O_CLOEXEC, 0644);
9✔
263
        if (fd < 0) {
9✔
264
                r = -errno;
×
265
                goto finish;
×
266
        }
267

268
        buf = malloc(sizeof(uint32_t) + size);
9✔
269
        if (!buf) {
9✔
270
                r = -ENOMEM;
×
271
                goto finish;
×
272
        }
273

274
        buf->attr = attr;
9✔
275
        memcpy(buf->buf, value, size);
9✔
276

277
        r = loop_write(fd, buf, sizeof(uint32_t) + size);
9✔
278
        if (r < 0)
9✔
279
                goto finish;
×
280

281
        /* For some reason efivarfs doesn't update mtime automatically. Let's do it manually then. This is
282
         * useful for processes that cache EFI variables to detect when changes occurred. */
283
        if (futimens(fd, /* times = */ NULL) < 0)
9✔
284
                log_debug_errno(errno, "Failed to update mtime/atime on %s, ignoring: %m", p);
7✔
285

286
        r = 0;
287

288
finish:
9✔
289
        if (saved_flags_valid) {
9✔
290
                int q;
2✔
291

292
                /* Restore the original flags field, just in case */
293
                if (fd < 0)
2✔
294
                        q = chattr_path(p, saved_flags, FS_IMMUTABLE_FL);
×
295
                else
296
                        q = chattr_fd(fd, saved_flags, FS_IMMUTABLE_FL);
2✔
297
                if (q < 0)
2✔
298
                        log_debug_errno(q, "Failed to restore FS_IMMUTABLE_FL on '%s', ignoring: %m", p);
9✔
299
        }
300

301
        return r;
302
}
303

304
int efi_set_variable_string(const char *variable, const char *value) {
×
305
        _cleanup_free_ char16_t *u16 = NULL;
×
306

307
        u16 = utf8_to_utf16(value, SIZE_MAX);
×
308
        if (!u16)
×
309
                return -ENOMEM;
310

311
        return efi_set_variable(variable, u16, (char16_strlen(u16) + 1) * sizeof(char16_t));
×
312
}
313

314
bool is_efi_boot(void) {
567✔
315
        static int cache = -1;
567✔
316

317
        if (cache < 0) {
567✔
318
                if (detect_container() > 0)
335✔
319
                        cache = false;
63✔
320
                else {
321
                        cache = access("/sys/firmware/efi/", F_OK) >= 0;
272✔
322
                        if (!cache && errno != ENOENT)
272✔
323
                                log_debug_errno(errno, "Unable to test whether /sys/firmware/efi/ exists, assuming EFI not available: %m");
×
324
                }
325
        }
326

327
        return cache;
567✔
328
}
329

330
static int read_flag(const char *variable) {
9✔
331
        _cleanup_free_ void *v = NULL;
9✔
332
        uint8_t b;
9✔
333
        size_t s;
9✔
334
        int r;
9✔
335

336
        if (!is_efi_boot()) /* If this is not an EFI boot, assume the queried flags are zero */
9✔
337
                return 0;
338

339
        r = efi_get_variable(variable, NULL, &v, &s);
7✔
340
        if (r < 0)
7✔
341
                return r;
342

343
        if (s != 1)
×
344
                return -EINVAL;
345

346
        b = *(uint8_t *)v;
×
347
        return !!b;
×
348
}
349

350
bool is_efi_secure_boot(void) {
5✔
351
        static int cache = -1;
5✔
352
        int r;
5✔
353

354
        if (cache < 0) {
5✔
355
                r = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
3✔
356
                if (r == -ENOENT)
3✔
357
                        cache = false;
1✔
358
                else if (r < 0)
2✔
359
                        log_debug_errno(r, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
×
360
                else
361
                        cache = r;
2✔
362
        }
363

364
        return cache > 0;
5✔
365
}
366

367
SecureBootMode efi_get_secure_boot_mode(void) {
6✔
368
        static SecureBootMode cache = _SECURE_BOOT_INVALID;
6✔
369

370
        if (cache != _SECURE_BOOT_INVALID)
6✔
371
                return cache;
372

373
        int secure = read_flag(EFI_GLOBAL_VARIABLE_STR("SecureBoot"));
6✔
374
        if (secure < 0) {
6✔
375
                if (secure != -ENOENT)
6✔
376
                        log_debug_errno(secure, "Error reading SecureBoot EFI variable, assuming not in SecureBoot mode: %m");
×
377

378
                return (cache = SECURE_BOOT_UNSUPPORTED);
6✔
379
        }
380

381
        /* We can assume false for all these if they are abscent (AuditMode and
382
         * DeployedMode may not exist on older firmware). */
383
        int audit    = read_flag(EFI_GLOBAL_VARIABLE_STR("AuditMode"));
×
384
        int deployed = read_flag(EFI_GLOBAL_VARIABLE_STR("DeployedMode"));
×
385
        int setup    = read_flag(EFI_GLOBAL_VARIABLE_STR("SetupMode"));
×
386
        log_debug("Secure boot variables: SecureBoot=%d AuditMode=%d DeployedMode=%d SetupMode=%d",
×
387
                  secure, audit, deployed, setup);
388

389
        return (cache = decode_secure_boot_mode(secure, audit > 0, deployed > 0, setup > 0));
×
390
}
391
#endif
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