Import CS systemd-252-46.el9

This commit is contained in:
eabdullin 2024-10-04 16:28:39 +03:00
parent 83748cd7ed
commit a34de4e57d
394 changed files with 3476252 additions and 6 deletions

2
.gitignore vendored
View File

@ -1,2 +1,2 @@
SOURCES/rhel-net-naming-sysattrs-v0.4.tar.gz
SOURCES/rhel-net-naming-sysattrs-v0.5.tar.gz
SOURCES/systemd-252.tar.gz

View File

@ -1,2 +1,2 @@
89b6d8ec519fd7d8b64f8d96b6cd0a65af97822b SOURCES/rhel-net-naming-sysattrs-v0.4.tar.gz
9ce6834429dbb9cb049de1bdf77bc8c84763709c SOURCES/rhel-net-naming-sysattrs-v0.5.tar.gz
7c961dc6e8bb950825b85129f59dc80f4536cabb SOURCES/systemd-252.tar.gz

View File

@ -0,0 +1,47 @@
From b433a32f0d4328afc7a3dddb7dbab82d206663f7 Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Wed, 19 Oct 2022 15:27:04 +0200
Subject: [PATCH] random-seed: shorten a bit may_credit()
No functional change.
(cherry picked from commit 249d31b00a42aa016639bc0e9d708803d26f8f8f)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 82c29d0d7f..569b916f4a 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -87,20 +87,17 @@ static CreditEntropy may_credit(int seed_fd) {
/* Don't credit the random seed if we are in first-boot mode, because we are supposed to start from
* scratch. This is a safety precaution for cases where we people ship "golden" images with empty
* /etc but populated /var that contains a random seed. */
- if (access("/run/systemd/first-boot", F_OK) < 0) {
-
- if (errno != ENOENT) {
- log_warning_errno(errno, "Failed to check whether we are in first-boot mode, not crediting entropy: %m");
- return CREDIT_ENTROPY_NO_WAY;
- }
-
- /* If ENOENT all is good, we are not in first-boot mode. */
- } else {
- log_debug("Not crediting entropy, since booted in first-boot mode.");
+ r = RET_NERRNO(access("/run/systemd/first-boot", F_OK));
+ if (r == -ENOENT)
+ /* All is good, we are not in first-boot mode. */
+ return CREDIT_ENTROPY_YES_PLEASE;
+ if (r < 0) {
+ log_warning_errno(r, "Failed to check whether we are in first-boot mode, not crediting entropy: %m");
return CREDIT_ENTROPY_NO_WAY;
}
- return CREDIT_ENTROPY_YES_PLEASE;
+ log_debug("Not crediting entropy, since booted in first-boot mode.");
+ return CREDIT_ENTROPY_NO_WAY;
}
static int run(int argc, char *argv[]) {

View File

@ -0,0 +1,27 @@
From b8859311bceb0cd63a64c2fcc8967f0c6466d736 Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Wed, 19 Oct 2022 15:28:27 +0200
Subject: [PATCH] random-seed: make one more use of random_write_entropy()
No functional change.
(cherry picked from commit 141d1da021514be2cc7e7a903fa83b11f6054db6)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 569b916f4a..d94005bdde 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -197,7 +197,7 @@ static int run(int argc, char *argv[]) {
if (r < 0)
log_debug_errno(r, "Failed to get machine ID, ignoring: %m");
else {
- r = loop_write(random_fd, &mid, sizeof(mid), false);
+ r = random_write_entropy(random_fd, &mid, sizeof(mid), /* credit= */ false);
if (r < 0)
log_debug_errno(r, "Failed to write machine ID to /dev/urandom, ignoring: %m");
}

View File

@ -0,0 +1,207 @@
From f6b55583600b4f8bfa2e7883d60685e2fb6c6b9d Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Wed, 19 Oct 2022 15:49:24 +0200
Subject: [PATCH] random-seed: use getopt()
It's not really necessary since systemd-random-seed is an internal tool for the
moment but this might change in future (to allow system installers to
initialize a random seed file for example).
Also introducing new options will be easier.
(cherry picked from commit 0d0c6639d4d61ff6cee43bc059c56a5170a0d280)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 116 ++++++++++++++++++++++++++++++----
1 file changed, 103 insertions(+), 13 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index d94005bdde..2ca2181ddb 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -2,6 +2,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <getopt.h>
#include <linux/random.h>
#include <sys/ioctl.h>
#if USE_SYS_RANDOM_H
@@ -22,20 +23,34 @@
#include "missing_random.h"
#include "missing_syscall.h"
#include "mkdir.h"
+#include "parse-argument.h"
#include "parse-util.h"
+#include "pretty-print.h"
#include "random-util.h"
+#include "string-table.h"
#include "string-util.h"
+#include "strv.h"
#include "sync-util.h"
#include "sha256.h"
+#include "terminal-util.h"
#include "util.h"
#include "xattr-util.h"
+typedef enum SeedAction {
+ ACTION_LOAD,
+ ACTION_SAVE,
+ _ACTION_MAX,
+ _ACTION_INVALID = -EINVAL,
+} SeedAction;
+
typedef enum CreditEntropy {
CREDIT_ENTROPY_NO_WAY,
CREDIT_ENTROPY_YES_PLEASE,
CREDIT_ENTROPY_YES_FORCED,
} CreditEntropy;
+static SeedAction arg_action = _ACTION_INVALID;
+
static CreditEntropy may_credit(int seed_fd) {
_cleanup_free_ char *creditable = NULL;
const char *e;
@@ -100,6 +115,78 @@ static CreditEntropy may_credit(int seed_fd) {
return CREDIT_ENTROPY_NO_WAY;
}
+static int help(int argc, char *argv[], void *userdata) {
+ _cleanup_free_ char *link = NULL;
+ int r;
+
+ r = terminal_urlify_man("systemd-random-seed", "8", &link);
+ if (r < 0)
+ return log_oom();
+
+ printf("%1$s [OPTIONS...] COMMAND\n"
+ "\n%5$sLoad and save the system random seed at boot and shutdown.%6$s\n"
+ "\n%3$sCommands:%4$s\n"
+ " load Load a random seed saved on disk into the kernel entropy pool\n"
+ " save Save a new random seed on disk\n"
+ "\n%3$sOptions:%4$s\n"
+ " -h --help Show this help\n"
+ " --version Show package version\n"
+ "\nSee the %2$s for details.\n",
+ program_invocation_short_name,
+ link,
+ ansi_underline(),
+ ansi_normal(),
+ ansi_highlight(),
+ ansi_normal());
+
+ return 0;
+}
+
+static const char* const seed_action_table[_ACTION_MAX] = {
+ [ACTION_LOAD] = "load",
+ [ACTION_SAVE] = "save",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(seed_action, SeedAction);
+
+static int parse_argv(int argc, char *argv[]) {
+ enum {
+ ARG_VERSION = 0x100,
+ };
+
+ static const struct option options[] = {
+ { "help", no_argument, NULL, 'h' },
+ { "version", no_argument, NULL, ARG_VERSION },
+ };
+
+ int c;
+
+ assert(argc >= 0);
+ assert(argv);
+
+ while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
+ switch (c) {
+ case 'h':
+ return help(0, NULL, NULL);
+ case ARG_VERSION:
+ return version();
+ case '?':
+ return -EINVAL;
+
+ default:
+ assert_not_reached();
+ }
+
+ if (optind + 1 != argc)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This program requires one argument.");
+
+ arg_action = seed_action_from_string(argv[optind]);
+ if (arg_action < 0)
+ return log_error_errno(arg_action, "Unknown action '%s'", argv[optind]);
+
+ return 1;
+}
+
static int run(int argc, char *argv[]) {
bool read_seed_file, write_seed_file, synchronous, hashed_old_seed = false;
_cleanup_close_ int seed_fd = -1, random_fd = -1;
@@ -112,9 +199,9 @@ static int run(int argc, char *argv[]) {
log_setup();
- if (argc != 2)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "This program requires one argument.");
+ r = parse_argv(argc, argv);
+ if (r <= 0)
+ return r;
umask(0022);
@@ -124,11 +211,11 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m");
- /* When we load the seed we read it and write it to the device and then immediately update the saved seed with
- * new data, to make sure the next boot gets seeded differently. */
-
- if (streq(argv[1], "load")) {
+ /* When we load the seed we read it and write it to the device and then immediately update the saved
+ * seed with new data, to make sure the next boot gets seeded differently. */
+ switch (arg_action) {
+ case ACTION_LOAD:
seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
if (seed_fd < 0) {
int open_rw_error = -errno;
@@ -154,9 +241,9 @@ static int run(int argc, char *argv[]) {
read_seed_file = true;
synchronous = true; /* make this invocation a synchronous barrier for random pool initialization */
+ break;
- } else if (streq(argv[1], "save")) {
-
+ case ACTION_SAVE:
random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (random_fd < 0)
return log_error_errno(errno, "Failed to open /dev/urandom: %m");
@@ -168,14 +255,17 @@ static int run(int argc, char *argv[]) {
read_seed_file = false;
write_seed_file = true;
synchronous = false;
- } else
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "Unknown verb '%s'.", argv[1]);
+ break;
+
+ default:
+ assert_not_reached();
+ }
if (fstat(seed_fd, &st) < 0)
return log_error_errno(errno, "Failed to stat() seed file " RANDOM_SEED ": %m");
- /* If the seed file is larger than what we expect, then honour the existing size and save/restore as much as it says */
+ /* If the seed file is larger than what we expect, then honour the existing size and save/restore as
+ * much as it says */
if ((uint64_t) st.st_size > buf_size)
buf_size = MIN(st.st_size, RANDOM_POOL_SIZE_MAX);

View File

@ -0,0 +1,79 @@
From b16d2d2da2d84552cfe7437d728ab8d65bacb03c Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Thu, 20 Oct 2022 08:45:02 +0200
Subject: [PATCH] random-seed: make the logic to calculate the number of bytes
read from the random seed file clearer
We want the size to lie within [/proc/sys/kernel/random/poolsize,RANDOM_POOL_SIZE_MAX]
interval. Let's make it more obvious.
Also move the logic in a dedicated function.
(cherry picked from commit 205138d88abf2e087440803ee046128092b722c6)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 29 +++++++++++++++++++----------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 2ca2181ddb..5b5629d817 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -115,6 +115,22 @@ static CreditEntropy may_credit(int seed_fd) {
return CREDIT_ENTROPY_NO_WAY;
}
+static int random_seed_size(int seed_fd, size_t *ret_size) {
+ struct stat st;
+
+ assert(ret_size);
+ assert(seed_fd >= 0);
+
+ if (fstat(seed_fd, &st) < 0)
+ return log_error_errno(errno, "Failed to stat() seed file " RANDOM_SEED ": %m");
+
+ /* If the seed file is larger than what the kernel expects, then honour the existing size and
+ * save/restore as much as it says */
+
+ *ret_size = CLAMP((uint64_t)st.st_size, random_pool_size(), RANDOM_POOL_SIZE_MAX);
+ return 0;
+}
+
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@@ -193,7 +209,6 @@ static int run(int argc, char *argv[]) {
_cleanup_free_ void* buf = NULL;
struct sha256_ctx hash_state;
size_t buf_size;
- struct stat st;
ssize_t k, l;
int r;
@@ -205,8 +220,6 @@ static int run(int argc, char *argv[]) {
umask(0022);
- buf_size = random_pool_size();
-
r = mkdir_parents(RANDOM_SEED, 0755);
if (r < 0)
return log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m");
@@ -261,13 +274,9 @@ static int run(int argc, char *argv[]) {
assert_not_reached();
}
- if (fstat(seed_fd, &st) < 0)
- return log_error_errno(errno, "Failed to stat() seed file " RANDOM_SEED ": %m");
-
- /* If the seed file is larger than what we expect, then honour the existing size and save/restore as
- * much as it says */
- if ((uint64_t) st.st_size > buf_size)
- buf_size = MIN(st.st_size, RANDOM_POOL_SIZE_MAX);
+ r = random_seed_size(seed_fd, &buf_size);
+ if (r < 0)
+ return r;
buf = malloc(buf_size);
if (!buf)

View File

@ -0,0 +1,30 @@
From 494ebc9dc491ab378851ee75562796b32e9a98ea Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Thu, 20 Oct 2022 08:52:10 +0200
Subject: [PATCH] random-seed: no need to pass 'mode' argument when opening
/dev/urandom
The open() call is not supposed to create /dev/urandom.
No functional change.
(cherry picked from commit 4620c0af5dc7a46ed3e213568e99d8a82c44553d)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 5b5629d817..4b8138ca03 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -248,7 +248,7 @@ static int run(int argc, char *argv[]) {
} else
write_seed_file = true;
- random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
+ random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY);
if (random_fd < 0)
return log_error_errno(errno, "Failed to open /dev/urandom: %m");

View File

@ -0,0 +1,372 @@
From d424c00790f478790be7388827113853b968023e Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Thu, 20 Oct 2022 09:39:12 +0200
Subject: [PATCH] random-seed: split out run()
No functional change.
(cherry picked from commit d3fa881aa1f4bffc097d63ed68d2e2a8ada813d0)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 327 +++++++++++++++++++---------------
1 file changed, 184 insertions(+), 143 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 4b8138ca03..991e4b8ddd 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -131,6 +131,180 @@ static int random_seed_size(int seed_fd, size_t *ret_size) {
return 0;
}
+static int load_seed_file(
+ int seed_fd,
+ int urandom_fd,
+ size_t seed_size,
+ struct sha256_ctx **ret_hash_state) {
+
+ _cleanup_free_ void *buf = NULL;
+ CreditEntropy lets_credit;
+ sd_id128_t mid;
+ ssize_t k;
+ int r;
+
+ assert(seed_fd >= 0);
+ assert(urandom_fd >= 0);
+
+ /* First, let's write the machine ID into /dev/urandom, not crediting entropy. Why? As an extra
+ * protection against "golden images" that are put together sloppily, i.e. images which are
+ * duplicated on multiple systems but where the random seed file is not properly reset. Frequently
+ * the machine ID is properly reset on those systems however (simply because it's easier to notice,
+ * if it isn't due to address clashes and so on, while random seed equivalence is generally not
+ * noticed easily), hence let's simply write the machined ID into the random pool too. */
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ log_debug_errno(r, "Failed to get machine ID, ignoring: %m");
+ else {
+ r = random_write_entropy(urandom_fd, &mid, sizeof(mid), /* credit= */ false);
+ if (r < 0)
+ log_debug_errno(r, "Failed to write machine ID to /dev/urandom, ignoring: %m");
+ }
+
+ buf = malloc(seed_size);
+ if (!buf)
+ return log_oom();
+
+ k = loop_read(seed_fd, buf, seed_size, false);
+ if (k < 0) {
+ log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m");
+ return 0;
+ }
+ if (k == 0) {
+ log_debug("Seed file " RANDOM_SEED " not yet initialized, proceeding.");
+ return 0;
+ }
+
+ /* If we're going to later write out a seed file, initialize a hash state with the contents of the
+ * seed file we just read, so that the new one can't regress in entropy. */
+ if (ret_hash_state) {
+ struct sha256_ctx *hash_state;
+
+ hash_state = malloc(sizeof(struct sha256_ctx));
+ if (!hash_state)
+ return log_oom();
+
+ sha256_init_ctx(hash_state);
+ sha256_process_bytes(&k, sizeof(k), hash_state); /* Hash length to distinguish from new seed. */
+ sha256_process_bytes(buf, k, hash_state);
+
+ *ret_hash_state = hash_state;
+ }
+
+ (void) lseek(seed_fd, 0, SEEK_SET);
+
+ lets_credit = may_credit(seed_fd);
+
+ /* Before we credit or use the entropy, let's make sure to securely drop the creditable xattr from
+ * the file, so that we never credit the same random seed again. Note that further down we'll write a
+ * new seed again, and likely mark it as credible again, hence this is just paranoia to close the
+ * short time window between the time we upload the random seed into the kernel and download the new
+ * one from it. */
+
+ if (fremovexattr(seed_fd, "user.random-seed-creditable") < 0) {
+ if (!ERRNO_IS_XATTR_ABSENT(errno))
+ log_warning_errno(errno, "Failed to remove extended attribute, ignoring: %m");
+
+ /* Otherwise, there was no creditable flag set, which is OK. */
+ } else {
+ r = fsync_full(seed_fd);
+ if (r < 0) {
+ log_warning_errno(r, "Failed to synchronize seed to disk, not crediting entropy: %m");
+
+ if (lets_credit == CREDIT_ENTROPY_YES_PLEASE)
+ lets_credit = CREDIT_ENTROPY_NO_WAY;
+ }
+ }
+
+ r = random_write_entropy(urandom_fd, buf, k,
+ IN_SET(lets_credit, CREDIT_ENTROPY_YES_PLEASE, CREDIT_ENTROPY_YES_FORCED));
+ if (r < 0)
+ log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
+
+ return 0;
+}
+
+static int save_seed_file(
+ int seed_fd,
+ int urandom_fd,
+ size_t seed_size,
+ bool synchronous,
+ struct sha256_ctx *hash_state) {
+
+ _cleanup_free_ void *buf = NULL;
+ bool getrandom_worked = false;
+ ssize_t k, l;
+ int r;
+
+ assert(seed_fd >= 0);
+ assert(urandom_fd >= 0);
+
+ /* This is just a safety measure. Given that we are root and most likely created the file ourselves
+ * the mode and owner should be correct anyway. */
+ r = fchmod_and_chown(seed_fd, 0600, 0, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to adjust seed file ownership and access mode: %m");
+
+ buf = malloc(seed_size);
+ if (!buf)
+ return log_oom();
+
+ /* Let's make this whole job asynchronous, i.e. let's make ourselves a barrier for proper
+ * initialization of the random pool. */
+ k = getrandom(buf, seed_size, GRND_NONBLOCK);
+ if (k < 0 && errno == EAGAIN && synchronous) {
+ log_notice("Kernel entropy pool is not initialized yet, waiting until it is.");
+ k = getrandom(buf, seed_size, 0); /* retry synchronously */
+ }
+ if (k < 0)
+ log_debug_errno(errno, "Failed to read random data with getrandom(), falling back to /dev/urandom: %m");
+ else if ((size_t) k < seed_size)
+ log_debug("Short read from getrandom(), falling back to /dev/urandom.");
+ else
+ getrandom_worked = true;
+
+ if (!getrandom_worked) {
+ /* Retry with classic /dev/urandom */
+ k = loop_read(urandom_fd, buf, seed_size, false);
+ if (k < 0)
+ return log_error_errno(k, "Failed to read new seed from /dev/urandom: %m");
+ if (k == 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EIO), "Got EOF while reading from /dev/urandom.");
+ }
+
+ /* If we previously read in a seed file, then hash the new seed into the old one, and replace the
+ * last 32 bytes of the seed with the hash output, so that the new seed file can't regress in
+ * entropy. */
+ if (hash_state) {
+ uint8_t hash[SHA256_DIGEST_SIZE];
+
+ sha256_process_bytes(&k, sizeof(k), hash_state); /* Hash length to distinguish from old seed. */
+ sha256_process_bytes(buf, k, hash_state);
+ sha256_finish_ctx(hash_state, hash);
+ l = MIN((size_t)k, sizeof(hash));
+ memcpy((uint8_t *)buf + k - l, hash, l);
+ }
+
+ r = loop_write(seed_fd, buf, (size_t) k, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write new random seed file: %m");
+
+ if (ftruncate(seed_fd, k) < 0)
+ return log_error_errno(r, "Failed to truncate random seed file: %m");
+
+ r = fsync_full(seed_fd);
+ if (r < 0)
+ return log_error_errno(r, "Failed to synchronize seed file: %m");
+
+ /* If we got this random seed data from getrandom() the data is suitable for crediting entropy later
+ * on. Let's keep that in mind by setting an extended attribute. on the file */
+ if (getrandom_worked)
+ if (fsetxattr(seed_fd, "user.random-seed-creditable", "1", 1, 0) < 0)
+ log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno,
+ "Failed to mark seed file as creditable, ignoring: %m");
+ return 0;
+}
+
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@@ -204,12 +378,10 @@ static int parse_argv(int argc, char *argv[]) {
}
static int run(int argc, char *argv[]) {
- bool read_seed_file, write_seed_file, synchronous, hashed_old_seed = false;
+ _cleanup_free_ struct sha256_ctx *hash_state = NULL;
_cleanup_close_ int seed_fd = -1, random_fd = -1;
- _cleanup_free_ void* buf = NULL;
- struct sha256_ctx hash_state;
- size_t buf_size;
- ssize_t k, l;
+ bool read_seed_file, write_seed_file, synchronous;
+ size_t seed_size;
int r;
log_setup();
@@ -274,149 +446,18 @@ static int run(int argc, char *argv[]) {
assert_not_reached();
}
- r = random_seed_size(seed_fd, &buf_size);
+ r = random_seed_size(seed_fd, &seed_size);
if (r < 0)
return r;
- buf = malloc(buf_size);
- if (!buf)
- return log_oom();
-
- if (read_seed_file) {
- sd_id128_t mid;
-
- /* First, let's write the machine ID into /dev/urandom, not crediting entropy. Why? As an
- * extra protection against "golden images" that are put together sloppily, i.e. images which
- * are duplicated on multiple systems but where the random seed file is not properly
- * reset. Frequently the machine ID is properly reset on those systems however (simply
- * because it's easier to notice, if it isn't due to address clashes and so on, while random
- * seed equivalence is generally not noticed easily), hence let's simply write the machined
- * ID into the random pool too. */
- r = sd_id128_get_machine(&mid);
- if (r < 0)
- log_debug_errno(r, "Failed to get machine ID, ignoring: %m");
- else {
- r = random_write_entropy(random_fd, &mid, sizeof(mid), /* credit= */ false);
- if (r < 0)
- log_debug_errno(r, "Failed to write machine ID to /dev/urandom, ignoring: %m");
- }
-
- k = loop_read(seed_fd, buf, buf_size, false);
- if (k < 0)
- log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m");
- else if (k == 0)
- log_debug("Seed file " RANDOM_SEED " not yet initialized, proceeding.");
- else {
- CreditEntropy lets_credit;
-
- /* If we're going to later write out a seed file, initialize a hash state with
- * the contents of the seed file we just read, so that the new one can't regress
- * in entropy. */
- if (write_seed_file) {
- sha256_init_ctx(&hash_state);
- sha256_process_bytes(&k, sizeof(k), &hash_state); /* Hash length to distinguish from new seed. */
- sha256_process_bytes(buf, k, &hash_state);
- hashed_old_seed = true;
- }
-
- (void) lseek(seed_fd, 0, SEEK_SET);
-
- lets_credit = may_credit(seed_fd);
-
- /* Before we credit or use the entropy, let's make sure to securely drop the
- * creditable xattr from the file, so that we never credit the same random seed
- * again. Note that further down we'll write a new seed again, and likely mark it as
- * credible again, hence this is just paranoia to close the short time window between
- * the time we upload the random seed into the kernel and download the new one from
- * it. */
-
- if (fremovexattr(seed_fd, "user.random-seed-creditable") < 0) {
- if (!ERRNO_IS_XATTR_ABSENT(errno))
- log_warning_errno(errno, "Failed to remove extended attribute, ignoring: %m");
+ if (read_seed_file)
+ r = load_seed_file(seed_fd, random_fd, seed_size,
+ write_seed_file ? &hash_state : NULL);
- /* Otherwise, there was no creditable flag set, which is OK. */
- } else {
- r = fsync_full(seed_fd);
- if (r < 0) {
- log_warning_errno(r, "Failed to synchronize seed to disk, not crediting entropy: %m");
+ if (r >= 0 && write_seed_file)
+ r = save_seed_file(seed_fd, random_fd, seed_size, synchronous, hash_state);
- if (lets_credit == CREDIT_ENTROPY_YES_PLEASE)
- lets_credit = CREDIT_ENTROPY_NO_WAY;
- }
- }
-
- r = random_write_entropy(random_fd, buf, k,
- IN_SET(lets_credit, CREDIT_ENTROPY_YES_PLEASE, CREDIT_ENTROPY_YES_FORCED));
- if (r < 0)
- log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
- }
- }
-
- if (write_seed_file) {
- bool getrandom_worked = false;
-
- /* This is just a safety measure. Given that we are root and most likely created the file
- * ourselves the mode and owner should be correct anyway. */
- r = fchmod_and_chown(seed_fd, 0600, 0, 0);
- if (r < 0)
- return log_error_errno(r, "Failed to adjust seed file ownership and access mode: %m");
-
- /* Let's make this whole job asynchronous, i.e. let's make ourselves a barrier for
- * proper initialization of the random pool. */
- k = getrandom(buf, buf_size, GRND_NONBLOCK);
- if (k < 0 && errno == EAGAIN && synchronous) {
- log_notice("Kernel entropy pool is not initialized yet, waiting until it is.");
- k = getrandom(buf, buf_size, 0); /* retry synchronously */
- }
- if (k < 0)
- log_debug_errno(errno, "Failed to read random data with getrandom(), falling back to /dev/urandom: %m");
- else if ((size_t) k < buf_size)
- log_debug("Short read from getrandom(), falling back to /dev/urandom.");
- else
- getrandom_worked = true;
-
- if (!getrandom_worked) {
- /* Retry with classic /dev/urandom */
- k = loop_read(random_fd, buf, buf_size, false);
- if (k < 0)
- return log_error_errno(k, "Failed to read new seed from /dev/urandom: %m");
- if (k == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EIO),
- "Got EOF while reading from /dev/urandom.");
- }
-
- /* If we previously read in a seed file, then hash the new seed into the old one,
- * and replace the last 32 bytes of the seed with the hash output, so that the
- * new seed file can't regress in entropy. */
- if (hashed_old_seed) {
- uint8_t hash[SHA256_DIGEST_SIZE];
- sha256_process_bytes(&k, sizeof(k), &hash_state); /* Hash length to distinguish from old seed. */
- sha256_process_bytes(buf, k, &hash_state);
- sha256_finish_ctx(&hash_state, hash);
- l = MIN((size_t)k, sizeof(hash));
- memcpy((uint8_t *)buf + k - l, hash, l);
- }
-
- r = loop_write(seed_fd, buf, (size_t) k, false);
- if (r < 0)
- return log_error_errno(r, "Failed to write new random seed file: %m");
-
- if (ftruncate(seed_fd, k) < 0)
- return log_error_errno(r, "Failed to truncate random seed file: %m");
-
- r = fsync_full(seed_fd);
- if (r < 0)
- return log_error_errno(r, "Failed to synchronize seed file: %m");
-
- /* If we got this random seed data from getrandom() the data is suitable for crediting
- * entropy later on. Let's keep that in mind by setting an extended attribute. on the file */
- if (getrandom_worked)
- if (fsetxattr(seed_fd, "user.random-seed-creditable", "1", 1, 0) < 0)
- log_full_errno(ERRNO_IS_NOT_SUPPORTED(errno) ? LOG_DEBUG : LOG_WARNING, errno,
- "Failed to mark seed file as creditable, ignoring: %m");
- }
-
- return 0;
+ return r;
}
DEFINE_MAIN_FUNCTION(run);

View File

@ -0,0 +1,34 @@
From 20eeade12a2e914d9b5451dbb1f8807cd6719eac Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Thu, 20 Oct 2022 15:03:20 +0200
Subject: [PATCH] random_seed: minor improvement in run()
(cherry picked from commit 3f6fbfe6f1ae62b080c70dad6de5a65108e3d538)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 991e4b8ddd..05fb5bb157 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -410,12 +410,12 @@ static int run(int argc, char *argv[]) {
seed_fd = open(RANDOM_SEED, O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (seed_fd < 0) {
bool missing = errno == ENOENT;
+ int level = missing ? LOG_DEBUG : LOG_ERR;
- log_full_errno(missing ? LOG_DEBUG : LOG_ERR,
- open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m");
- r = log_full_errno(missing ? LOG_DEBUG : LOG_ERR,
- errno, "Failed to open " RANDOM_SEED " for reading: %m");
- return missing ? 0 : r;
+ log_full_errno(level, open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m");
+ log_full_errno(level, errno, "Failed to open " RANDOM_SEED " for reading: %m");
+
+ return missing ? 0 : -errno;
}
} else
write_seed_file = true;

View File

@ -0,0 +1,37 @@
From 8990010d76cd48f8c166f586a8c6ae07cb1a749d Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Fri, 21 Oct 2022 15:08:43 +0200
Subject: [PATCH] random-seed: downgrade some messages
In these cases, we eat up the error and propagate success so we should log at
warning level only.
(cherry picked from commit ea37e1edf9bdaa5a90050d69454a132dc5d60360)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 05fb5bb157..7782509572 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -167,7 +167,7 @@ static int load_seed_file(
k = loop_read(seed_fd, buf, seed_size, false);
if (k < 0) {
- log_error_errno(k, "Failed to read seed from " RANDOM_SEED ": %m");
+ log_warning_errno(k, "Failed to read seed from " RANDOM_SEED ": %m");
return 0;
}
if (k == 0) {
@@ -219,7 +219,7 @@ static int load_seed_file(
r = random_write_entropy(urandom_fd, buf, k,
IN_SET(lets_credit, CREDIT_ENTROPY_YES_PLEASE, CREDIT_ENTROPY_YES_FORCED));
if (r < 0)
- log_error_errno(r, "Failed to write seed to /dev/urandom: %m");
+ log_warning_errno(r, "Failed to write seed to /dev/urandom: %m");
return 0;
}

View File

@ -0,0 +1,33 @@
From 22a598fa626bf440127c1dd2a6b116514869752a Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Tue, 25 Oct 2022 13:54:10 +0200
Subject: [PATCH] random-seed: clarify one comment
(cherry picked from commit 46e0b5dca7fa5368bccbf30a7d2569d93d994a44)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 7782509572..22ddf659ae 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -249,12 +249,13 @@ static int save_seed_file(
if (!buf)
return log_oom();
- /* Let's make this whole job asynchronous, i.e. let's make ourselves a barrier for proper
- * initialization of the random pool. */
k = getrandom(buf, seed_size, GRND_NONBLOCK);
if (k < 0 && errno == EAGAIN && synchronous) {
+ /* If we're asked to make ourselves a barrier for proper initialization of the random pool
+ * make this whole job synchronous by asking getrandom() to wait until the requested number
+ * of random bytes is available. */
log_notice("Kernel entropy pool is not initialized yet, waiting until it is.");
- k = getrandom(buf, seed_size, 0); /* retry synchronously */
+ k = getrandom(buf, seed_size, 0);
}
if (k < 0)
log_debug_errno(errno, "Failed to read random data with getrandom(), falling back to /dev/urandom: %m");

View File

@ -0,0 +1,100 @@
From 3dcd5325c72f656fbf97b71331bea1edc486f2d2 Mon Sep 17 00:00:00 2001
From: Franck Bui <fbui@suse.com>
Date: Mon, 24 Oct 2022 11:30:29 +0200
Subject: [PATCH] random-seed: make sure to load machine id even if the seed
file is missing
(cherry picked from commit a2f0dbb81004685d17f71fed48dc50027ccadb82)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 49 +++++++++++++++++++++--------------
1 file changed, 29 insertions(+), 20 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 22ddf659ae..b548f92bbe 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -131,6 +131,27 @@ static int random_seed_size(int seed_fd, size_t *ret_size) {
return 0;
}
+static void load_machine_id(int urandom_fd) {
+ sd_id128_t mid;
+ int r;
+
+ assert(urandom_fd >= 0);
+
+ /* As an extra protection against "golden images" that are put together sloppily, i.e. images which
+ * are duplicated on multiple systems but where the random seed file is not properly
+ * reset. Frequently the machine ID is properly reset on those systems however (simply because it's
+ * easier to notice, if it isn't due to address clashes and so on, while random seed equivalence is
+ * generally not noticed easily), hence let's simply write the machined ID into the random pool
+ * too. */
+ r = sd_id128_get_machine(&mid);
+ if (r < 0)
+ return (void) log_debug_errno(r, "Failed to get machine ID, ignoring: %m");
+
+ r = random_write_entropy(urandom_fd, &mid, sizeof(mid), /* credit= */ false);
+ if (r < 0)
+ log_debug_errno(r, "Failed to write machine ID to /dev/urandom, ignoring: %m");
+}
+
static int load_seed_file(
int seed_fd,
int urandom_fd,
@@ -139,28 +160,12 @@ static int load_seed_file(
_cleanup_free_ void *buf = NULL;
CreditEntropy lets_credit;
- sd_id128_t mid;
ssize_t k;
int r;
assert(seed_fd >= 0);
assert(urandom_fd >= 0);
- /* First, let's write the machine ID into /dev/urandom, not crediting entropy. Why? As an extra
- * protection against "golden images" that are put together sloppily, i.e. images which are
- * duplicated on multiple systems but where the random seed file is not properly reset. Frequently
- * the machine ID is properly reset on those systems however (simply because it's easier to notice,
- * if it isn't due to address clashes and so on, while random seed equivalence is generally not
- * noticed easily), hence let's simply write the machined ID into the random pool too. */
- r = sd_id128_get_machine(&mid);
- if (r < 0)
- log_debug_errno(r, "Failed to get machine ID, ignoring: %m");
- else {
- r = random_write_entropy(urandom_fd, &mid, sizeof(mid), /* credit= */ false);
- if (r < 0)
- log_debug_errno(r, "Failed to write machine ID to /dev/urandom, ignoring: %m");
- }
-
buf = malloc(seed_size);
if (!buf)
return log_oom();
@@ -402,6 +407,14 @@ static int run(int argc, char *argv[]) {
switch (arg_action) {
case ACTION_LOAD:
+ random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (random_fd < 0)
+ return log_error_errno(errno, "Failed to open /dev/urandom: %m");
+
+ /* First, let's write the machine ID into /dev/urandom, not crediting entropy. See
+ * load_machine_id() for an explanation why. */
+ load_machine_id(random_fd);
+
seed_fd = open(RANDOM_SEED, O_RDWR|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
if (seed_fd < 0) {
int open_rw_error = -errno;
@@ -421,10 +434,6 @@ static int run(int argc, char *argv[]) {
} else
write_seed_file = true;
- random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY);
- if (random_fd < 0)
- return log_error_errno(errno, "Failed to open /dev/urandom: %m");
-
read_seed_file = true;
synchronous = true; /* make this invocation a synchronous barrier for random pool initialization */
break;

View File

@ -0,0 +1,87 @@
From 99294ed904d04eff1b1f05390e64d92f9d824853 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 11 Nov 2022 17:31:34 +0100
Subject: [PATCH] chase-symlinks: add new flag for prohibiting any following of
symlinks
This is useful when operating in the ESP, which is untrusted territory,
and where under no circumstances we should be tricked by symlinks into
doing anything we don't want to.
(cherry picked from commit d43e78b643535da398345d5ae680a96d7b65940e)
Related: RHEL-16952
---
src/basic/chase-symlinks.c | 18 ++++++++++++++++++
src/basic/chase-symlinks.h | 1 +
src/test/test-fs-util.c | 9 +++++++++
3 files changed, 28 insertions(+)
diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c
index ac55311f4d..e10370d0d2 100644
--- a/src/basic/chase-symlinks.c
+++ b/src/basic/chase-symlinks.c
@@ -57,6 +57,21 @@ static int log_autofs_mount_point(int fd, const char *path, ChaseSymlinksFlags f
strna(n1), path);
}
+static int log_prohibited_symlink(int fd, ChaseSymlinksFlags flags) {
+ _cleanup_free_ char *n1 = NULL;
+
+ assert(fd >= 0);
+
+ if (!FLAGS_SET(flags, CHASE_WARN))
+ return -EREMCHG;
+
+ (void) fd_get_path(fd, &n1);
+
+ return log_warning_errno(SYNTHETIC_ERRNO(EREMCHG),
+ "Detected symlink where not symlink is allowed at %s, refusing.",
+ strna(n1));
+}
+
int chase_symlinks(
const char *path,
const char *original_root,
@@ -302,6 +317,9 @@ int chase_symlinks(
if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
_cleanup_free_ char *destination = NULL;
+ if (flags & CHASE_PROHIBIT_SYMLINKS)
+ return log_prohibited_symlink(child, flags);
+
/* This is a symlink, in this case read the destination. But let's make sure we
* don't follow symlinks without bounds. */
if (--max_follow <= 0)
diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h
index a9ee58f9f7..8f69bf3eed 100644
--- a/src/basic/chase-symlinks.h
+++ b/src/basic/chase-symlinks.h
@@ -17,6 +17,7 @@ typedef enum ChaseSymlinksFlags {
* right-most component refers to symlink, return O_PATH fd of the symlink. */
CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered.
* Note: this may do an NSS lookup, hence this flag cannot be used in PID 1. */
+ CHASE_PROHIBIT_SYMLINKS = 1 << 8, /* Refuse all symlinks */
} ChaseSymlinksFlags;
bool unsafe_transition(const struct stat *a, const struct stat *b);
diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c
index 9c1ced7591..16f04d6889 100644
--- a/src/test/test-fs-util.c
+++ b/src/test/test-fs-util.c
@@ -387,6 +387,15 @@ TEST(chase_symlinks) {
assert_se(path_equal(path_startswith(result, p), "usr"));
result = mfree(result);
+ /* Test CHASE_PROHIBIT_SYMLINKS */
+
+ assert_se(chase_symlinks("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+ assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+
cleanup:
assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
}

View File

@ -0,0 +1,141 @@
From 466da97e11cebf84a293789d0f4f38779244a5b3 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 11 Nov 2022 17:36:29 +0100
Subject: [PATCH] bootctl,bootspec: make use of CHASE_PROHIBIT_SYMLINKS
whenever we access the ESP/XBOOTLDR
Let's make use of the new flag whenever we access the ESP or XBOOTLDR.
The resources we make use of in these partitions can't possibly use
symlinks (because UEFI knows no symlink concept), and they are untrusted
territory, hence under no circumstances we should be tricked into
following symlinks that shouldn't be there in the first place.
Of course, you might argue thta ESP/XBOOTLDR are VFAT and thus don#t
know symlinks. But the thing is, they don#t have to be. Firmware can
support other file systems too, and people can use efifs to gain access
to arbitrary Linux file systems from EFI. Hence, let's better be safe
than sorry.
(cherry picked from commit b353d5eee9e8df0aa2f4cbb1bfb0a46a963ba78f)
Related: RHEL-16952
---
src/boot/bootctl.c | 18 +++++++++---------
src/shared/bootspec.c | 8 ++++----
2 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index a0ca2afec2..e830d45486 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -485,7 +485,7 @@ static int enumerate_binaries(
assert(previous);
assert(is_first);
- r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT, &p, &d);
+ r = chase_symlinks_and_opendir(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
@@ -918,10 +918,10 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
if (!p)
return log_oom();
- r = chase_symlinks(p, root, CHASE_PREFIX_ROOT, &source_path, NULL);
+ r = chase_symlinks(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &source_path, NULL);
/* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
- r = chase_symlinks(p, NULL, CHASE_PREFIX_ROOT, &source_path, NULL);
+ r = chase_symlinks(p, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &source_path, NULL);
if (r < 0)
return log_error_errno(r,
"Failed to resolve path %s%s%s: %m",
@@ -933,7 +933,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
if (!q)
return log_oom();
- r = chase_symlinks(q, esp_path, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &dest_path, NULL);
+ r = chase_symlinks(q, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_NONEXISTENT, &dest_path, NULL);
if (r < 0)
return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", q, esp_path);
@@ -950,7 +950,7 @@ static int copy_one_file(const char *esp_path, const char *name, bool force) {
v = strjoina("/EFI/BOOT/BOOT", e);
ascii_strupper(strrchr(v, '/') + 1);
- r = chase_symlinks(v, esp_path, CHASE_PREFIX_ROOT | CHASE_NONEXISTENT, &default_dest_path, NULL);
+ r = chase_symlinks(v, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_NONEXISTENT, &default_dest_path, NULL);
if (r < 0)
return log_error_errno(r, "Failed to resolve path %s under directory %s: %m", v, esp_path);
@@ -968,10 +968,10 @@ static int install_binaries(const char *esp_path, const char *arch, bool force)
_cleanup_free_ char *path = NULL;
int r;
- r = chase_symlinks_and_opendir(BOOTLIBDIR, root, CHASE_PREFIX_ROOT, &path, &d);
+ r = chase_symlinks_and_opendir(BOOTLIBDIR, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &path, &d);
/* If we had a root directory to try, we didn't find it and we are in auto mode, retry on the host */
if (r == -ENOENT && root && arg_install_source == ARG_INSTALL_SOURCE_AUTO)
- r = chase_symlinks_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT, &path, &d);
+ r = chase_symlinks_and_opendir(BOOTLIBDIR, NULL, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &path, &d);
if (r < 0)
return log_error_errno(r, "Failed to open boot loader directory %s%s: %m", strempty(root), BOOTLIBDIR);
@@ -1141,7 +1141,7 @@ static int install_variables(
return 0;
}
- r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT, F_OK, NULL, NULL);
+ r = chase_symlinks_and_access(path, esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
if (r == -ENOENT)
return 0;
if (r < 0)
@@ -1172,7 +1172,7 @@ static int remove_boot_efi(const char *esp_path) {
_cleanup_free_ char *p = NULL;
int r, c = 0;
- r = chase_symlinks_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT, &p, &d);
+ r = chase_symlinks_and_opendir("/EFI/BOOT", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &p, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index fe44b5e9d2..9352416af5 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -507,7 +507,7 @@ static int boot_loader_read_conf_path(BootConfig *config, const char *root, cons
assert(config);
assert(path);
- r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT, "re", &full, &f);
+ r = chase_symlinks_and_fopen_unlocked(path, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, "re", &full, &f);
if (r == -ENOENT)
return 0;
if (r < 0)
@@ -609,7 +609,7 @@ static int boot_entries_find_type1(
assert(root);
assert(dir);
- dir_fd = chase_symlinks_and_open(dir, root, CHASE_PREFIX_ROOT, O_DIRECTORY|O_CLOEXEC, &full);
+ dir_fd = chase_symlinks_and_open(dir, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, O_DIRECTORY|O_CLOEXEC, &full);
if (dir_fd == -ENOENT)
return 0;
if (dir_fd < 0)
@@ -869,7 +869,7 @@ static int boot_entries_find_unified(
assert(config);
assert(dir);
- r = chase_symlinks_and_opendir(dir, root, CHASE_PREFIX_ROOT, &full, &d);
+ r = chase_symlinks_and_opendir(dir, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, &full, &d);
if (r == -ENOENT)
return 0;
if (r < 0)
@@ -1282,7 +1282,7 @@ static void boot_entry_file_list(
assert(p);
assert(ret_status);
- int status = chase_symlinks_and_access(p, root, CHASE_PREFIX_ROOT, F_OK, NULL, NULL);
+ int status = chase_symlinks_and_access(p, root, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, F_OK, NULL, NULL);
printf("%13s%s ", strempty(field), field ? ":" : " ");
if (status < 0) {

View File

@ -0,0 +1,863 @@
From bd5fbc8566124744d94d5f040016b9f4404dc2dc Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Wed, 9 Nov 2022 12:44:37 +0100
Subject: [PATCH] boot: implement kernel EFI RNG seed protocol with proper
hashing
Rather than passing seeds up to userspace via EFI variables, pass seeds
directly to the kernel's EFI stub loader, via LINUX_EFI_RANDOM_SEED_TABLE_GUID.
EFI variables can potentially leak and suffer from forward secrecy
issues, and processing these with userspace means that they are
initialized much too late in boot to be useful. In contrast,
LINUX_EFI_RANDOM_SEED_TABLE_GUID uses EFI configuration tables, and so
is hidden from userspace entirely, and is parsed extremely early on by
the kernel, so that every single call to get_random_bytes() by the
kernel is seeded.
In order to do this properly, we use a bit more robust hashing scheme,
and make sure that each input is properly memzeroed out after use. The
scheme is:
key = HASH(LABEL || sizeof(input1) || input1 || ... || sizeof(inputN) || inputN)
new_disk_seed = HASH(key || 0)
seed_for_linux = HASH(key || 1)
The various inputs are:
- LINUX_EFI_RANDOM_SEED_TABLE_GUID from prior bootloaders
- 256 bits of seed from EFI's RNG
- The (immutable) system token, from its EFI variable
- The prior on-disk seed
- The UEFI monotonic counter
- A timestamp
This also adjusts the secure boot semantics, so that the operation is
only aborted if it's not possible to get random bytes from EFI's RNG or
a prior boot stage. With the proper hashing scheme, this should make
boot seeds safe even on secure boot.
There is currently a bug in Linux's EFI stub in which if the EFI stub
manages to generate random bytes on its own using EFI's RNG, it will
ignore what the bootloader passes. That's annoying, but it means that
either way, via systemd-boot or via EFI stub's mechanism, the RNG *does*
get initialized in a good safe way. And this bug is now fixed in the
efi.git tree, and will hopefully be backported to older kernels.
As the kernel recommends, the resultant seeds are 256 bits and are
allocated using pool memory of type EfiACPIReclaimMemory, so that it
gets freed at the right moment in boot.
(cherry picked from commit 0be72218f1c90af5755ab40f94d047ee6864aea8)
Related: RHEL-16952
---
.../UninitializedVariableWithCleanup.ql | 2 +-
docs/BOOT_LOADER_INTERFACE.md | 9 +-
docs/RANDOM_SEEDS.md | 51 +--
man/systemd-boot.xml | 22 --
src/basic/random-util.h | 1 +
src/boot/bootctl.c | 30 +-
src/boot/efi/efi-string.h | 1 +
src/boot/efi/random-seed.c | 305 +++++++++---------
src/core/efi-random.c | 82 +----
src/core/efi-random.h | 2 +-
src/core/main.c | 4 +-
units/systemd-boot-system-token.service | 3 -
12 files changed, 218 insertions(+), 294 deletions(-)
diff --git a/.github/codeql-queries/UninitializedVariableWithCleanup.ql b/.github/codeql-queries/UninitializedVariableWithCleanup.ql
index e514111f28..dadc6cb1b5 100644
--- a/.github/codeql-queries/UninitializedVariableWithCleanup.ql
+++ b/.github/codeql-queries/UninitializedVariableWithCleanup.ql
@@ -20,7 +20,7 @@ import semmle.code.cpp.controlflow.StackVariableReachability
* since they don't do anything illegal even when the variable is uninitialized
*/
predicate cleanupFunctionDenyList(string fun) {
- fun = "erase_char"
+ fun = "erase_char" or fun = "erase_obj"
}
/**
diff --git a/docs/BOOT_LOADER_INTERFACE.md b/docs/BOOT_LOADER_INTERFACE.md
index fc9336085b..5be4d1ad17 100644
--- a/docs/BOOT_LOADER_INTERFACE.md
+++ b/docs/BOOT_LOADER_INTERFACE.md
@@ -80,12 +80,6 @@ variables. All EFI variables use the vendor UUID
* `1 << 5` → The boot loader supports looking for boot menu entries in the Extended Boot Loader Partition.
* `1 << 6` → The boot loader supports passing a random seed to the OS.
-* The EFI variable `LoaderRandomSeed` contains a binary random seed if set. It
- is set by the boot loader to pass an entropy seed read from the ESP to the OS.
- The system manager then credits this seed to the kernel's entropy pool. It is
- the responsibility of the boot loader to ensure the quality and integrity of
- the random seed.
-
* The EFI variable `LoaderSystemToken` contains binary random data,
persistently set by the OS installer. Boot loaders that support passing
random seeds to the OS should use this data and combine it with the random
@@ -107,8 +101,7 @@ that directory is empty, and only if no other file systems are mounted
there. The `systemctl reboot --boot-loader-entry=…` and `systemctl reboot
--boot-loader-menu=…` commands rely on the `LoaderFeatures` ,
`LoaderConfigTimeoutOneShot`, `LoaderEntries`, `LoaderEntryOneShot`
-variables. `LoaderRandomSeed` is read by PID during early boot and credited to
-the kernel's random pool.
+variables.
## Boot Loader Entry Identifiers
diff --git a/docs/RANDOM_SEEDS.md b/docs/RANDOM_SEEDS.md
index 3dc27f5552..b7240f0d89 100644
--- a/docs/RANDOM_SEEDS.md
+++ b/docs/RANDOM_SEEDS.md
@@ -197,28 +197,39 @@ boot, in order to ensure the entropy pool is filled up quickly.
generate sufficient data), to generate a new random seed file to store in
the ESP as well as a random seed to pass to the OS kernel. The new random
seed file for the ESP is then written to the ESP, ensuring this is completed
- before the OS is invoked. Very early during initialization PID 1 will read
- the random seed provided in the EFI variable and credit it fully to the
- kernel's entropy pool.
-
- This mechanism is able to safely provide an initialized entropy pool already
- in the `initrd` and guarantees that different seeds are passed from the boot
- loader to the OS on every boot (in a way that does not allow regeneration of
- an old seed file from a new seed file). Moreover, when an OS image is
- replicated between multiple images and the random seed is not reset, this
- will still result in different random seeds being passed to the OS, as the
- per-machine 'system token' is specific to the physical host, and not
- included in OS disk images. If the 'system token' is properly initialized
- and kept sufficiently secret it should not be possible to regenerate the
- entropy pool of different machines, even if this seed is the only source of
- entropy.
+ before the OS is invoked.
+
+ The kernel then reads the random seed that the boot loader passes to it, via
+ the EFI configuration table entry, `LINUX_EFI_RANDOM_SEED_TABLE_GUID`
+ (1ce1e5bc-7ceb-42f2-81e5-8aadf180f57b), which is allocated with pool memory
+ of type `EfiACPIReclaimMemory`. Its contents have the form:
+ ```
+ struct linux_efi_random_seed {
+ u32 size; // of the 'seed' array in bytes
+ u8 seed[];
+ };
+ ```
+ The size field is generally set to 32 bytes, and the seed field includes a
+ hashed representation of any prior seed in `LINUX_EFI_RANDOM_SEED_TABLE_GUID`
+ together with the new seed.
+
+ This mechanism is able to safely provide an initialized entropy pool before
+ userspace even starts and guarantees that different seeds are passed from
+ the boot loader to the OS on every boot (in a way that does not allow
+ regeneration of an old seed file from a new seed file). Moreover, when an OS
+ image is replicated between multiple images and the random seed is not
+ reset, this will still result in different random seeds being passed to the
+ OS, as the per-machine 'system token' is specific to the physical host, and
+ not included in OS disk images. If the 'system token' is properly
+ initialized and kept sufficiently secret it should not be possible to
+ regenerate the entropy pool of different machines, even if this seed is the
+ only source of entropy.
Note that the writes to the ESP needed to maintain the random seed should be
- minimal. The size of the random seed file is directly derived from the Linux
- kernel's entropy pool size, which defaults to 512 bytes. This means updating
- the random seed in the ESP should be doable safely with a single sector
- write (since hard-disk sectors typically happen to be 512 bytes long, too),
- which should be safe even with FAT file system drivers built into
+ minimal. Because the size of the random seed file is generally set to 32 bytes,
+ updating the random seed in the ESP should be doable safely with a single
+ sector write (since hard-disk sectors typically happen to be 512 bytes long,
+ too), which should be safe even with FAT file system drivers built into
low-quality EFI firmwares.
As a special restriction: in virtualized environments PID 1 will refrain
diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml
index 57b66803fa..f96c4c6512 100644
--- a/man/systemd-boot.xml
+++ b/man/systemd-boot.xml
@@ -435,28 +435,6 @@
to view this data. </para></listitem>
</varlistentry>
- <varlistentry>
- <term><varname>LoaderRandomSeed</varname></term>
-
- <listitem><para>A binary random seed <command>systemd-boot</command> may optionally pass to the
- OS. This is a volatile EFI variable that is hashed at boot from the combination of a random seed
- stored in the ESP (in <filename>/loader/random-seed</filename>) and a "system token" persistently
- stored in the EFI variable <varname>LoaderSystemToken</varname> (see below). During early OS boot the
- system manager reads this variable and passes it to the OS kernel's random pool, crediting the full
- entropy it contains. This is an efficient way to ensure the system starts up with a fully initialized
- kernel random pool — as early as the initrd phase. <command>systemd-boot</command> reads
- the random seed from the ESP, combines it with the "system token", and both derives a new random seed
- to update in-place the seed stored in the ESP, and the random seed to pass to the OS from it via
- SHA256 hashing in counter mode. This ensures that different physical systems that boot the same
- "golden" OS image — i.e. containing the same random seed file in the ESP — will still pass a
- different random seed to the OS. It is made sure the random seed stored in the ESP is fully
- overwritten before the OS is booted, to ensure different random seed data is used between subsequent
- boots.</para>
-
- <para>See <ulink url="https://systemd.io/RANDOM_SEEDS">Random Seeds</ulink> for
- further information.</para></listitem>
- </varlistentry>
-
<varlistentry>
<term><varname>LoaderSystemToken</varname></term>
diff --git a/src/basic/random-util.h b/src/basic/random-util.h
index 7e6f66df4d..08b1a3599a 100644
--- a/src/basic/random-util.h
+++ b/src/basic/random-util.h
@@ -23,6 +23,7 @@ static inline uint32_t random_u32(void) {
/* Some limits on the pool sizes when we deal with the kernel random pool */
#define RANDOM_POOL_SIZE_MIN 1024U
#define RANDOM_POOL_SIZE_MAX (10U*1024U*1024U)
+#define RANDOM_EFI_SEED_SIZE 32U
size_t random_pool_size(void);
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index e830d45486..e23a72fd38 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -1894,8 +1894,6 @@ static int verb_status(int argc, char *argv[], void *userdata) {
printf("\n");
printf("%sRandom Seed:%s\n", ansi_underline(), ansi_normal());
- have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)), F_OK) >= 0;
- printf(" Passed to OS: %s\n", yes_no(have));
have = access(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), F_OK) >= 0;
printf(" System Token: %s\n", have ? "set" : "not set");
@@ -1985,10 +1983,10 @@ static int verb_list(int argc, char *argv[], void *userdata) {
static int install_random_seed(const char *esp) {
_cleanup_(unlink_and_freep) char *tmp = NULL;
- _cleanup_free_ void *buffer = NULL;
+ unsigned char buffer[RANDOM_EFI_SEED_SIZE];
_cleanup_free_ char *path = NULL;
_cleanup_close_ int fd = -1;
- size_t sz, token_size;
+ size_t token_size;
ssize_t n;
int r;
@@ -1998,13 +1996,7 @@ static int install_random_seed(const char *esp) {
if (!path)
return log_oom();
- sz = random_pool_size();
-
- buffer = malloc(sz);
- if (!buffer)
- return log_oom();
-
- r = crypto_random_bytes(buffer, sz);
+ r = crypto_random_bytes(buffer, sizeof(buffer));
if (r < 0)
return log_error_errno(r, "Failed to acquire random seed: %m");
@@ -2025,10 +2017,10 @@ static int install_random_seed(const char *esp) {
return log_error_errno(fd, "Failed to open random seed file for writing: %m");
}
- n = write(fd, buffer, sz);
+ n = write(fd, buffer, sizeof(buffer));
if (n < 0)
return log_error_errno(errno, "Failed to write random seed file: %m");
- if ((size_t) n != sz)
+ if ((size_t) n != sizeof(buffer))
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing random seed file.");
if (rename(tmp, path) < 0)
@@ -2036,7 +2028,7 @@ static int install_random_seed(const char *esp) {
tmp = mfree(tmp);
- log_info("Random seed file %s successfully written (%zu bytes).", path, sz);
+ log_info("Random seed file %s successfully written (%zu bytes).", path, sizeof(buffer));
if (!arg_touch_variables)
return 0;
@@ -2088,16 +2080,16 @@ static int install_random_seed(const char *esp) {
if (r != -ENOENT)
return log_error_errno(r, "Failed to test system token validity: %m");
} else {
- if (token_size >= sz) {
+ if (token_size >= sizeof(buffer)) {
/* Let's avoid writes if we can, and initialize this only once. */
log_debug("System token already written, not updating.");
return 0;
}
- log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sz);
+ log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sizeof(buffer));
}
- r = crypto_random_bytes(buffer, sz);
+ r = crypto_random_bytes(buffer, sizeof(buffer));
if (r < 0)
return log_error_errno(r, "Failed to acquire random seed: %m");
@@ -2105,7 +2097,7 @@ static int install_random_seed(const char *esp) {
* and possibly get identification information or too much insight into the kernel's entropy pool
* state. */
RUN_WITH_UMASK(0077) {
- r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sz);
+ r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
if (r < 0) {
if (!arg_graceful)
return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
@@ -2115,7 +2107,7 @@ static int install_random_seed(const char *esp) {
else
log_warning_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
} else
- log_info("Successfully initialized system token in EFI variable with %zu bytes.", sz);
+ log_info("Successfully initialized system token in EFI variable with %zu bytes.", sizeof(buffer));
}
return 0;
diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h
index 9b2a9ad1c5..25931a7d6e 100644
--- a/src/boot/efi/efi-string.h
+++ b/src/boot/efi/efi-string.h
@@ -124,6 +124,7 @@ static inline void *mempcpy(void * restrict dest, const void * restrict src, siz
memcpy(dest, src, n);
return (uint8_t *) dest + n;
}
+
#else
/* For unit testing. */
int efi_memcmp(const void *p1, const void *p2, size_t n);
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index 3c9df5bb54..c723160c0f 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -15,11 +15,24 @@
#define EFI_RNG_GUID &(const EFI_GUID) EFI_RNG_PROTOCOL_GUID
+struct linux_efi_random_seed {
+ uint32_t size;
+ uint8_t seed[];
+};
+
+#define LINUX_EFI_RANDOM_SEED_TABLE_GUID \
+ { 0x1ce1e5bc, 0x7ceb, 0x42f2, { 0x81, 0xe5, 0x8a, 0xad, 0xf1, 0x80, 0xf5, 0x7b } }
+
/* SHA256 gives us 256/8=32 bytes */
#define HASH_VALUE_SIZE 32
-static EFI_STATUS acquire_rng(UINTN size, void **ret) {
- _cleanup_free_ void *data = NULL;
+/* Linux's RNG is 256 bits, so let's provide this much */
+#define DESIRED_SEED_SIZE 32
+
+/* Some basic domain separation in case somebody uses this data elsewhere */
+#define HASH_LABEL "systemd-boot random seed label v1"
+
+static EFI_STATUS acquire_rng(void *ret, UINTN size) {
EFI_RNG_PROTOCOL *rng;
EFI_STATUS err;
@@ -33,126 +46,9 @@ static EFI_STATUS acquire_rng(UINTN size, void **ret) {
if (!rng)
return EFI_UNSUPPORTED;
- data = xmalloc(size);
-
- err = rng->GetRNG(rng, NULL, size, data);
+ err = rng->GetRNG(rng, NULL, size, ret);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to acquire RNG data: %r", err);
-
- *ret = TAKE_PTR(data);
- return EFI_SUCCESS;
-}
-
-static void hash_once(
- const void *old_seed,
- const void *rng,
- UINTN size,
- const void *system_token,
- UINTN system_token_size,
- uint64_t uefi_monotonic_counter,
- UINTN counter,
- uint8_t ret[static HASH_VALUE_SIZE]) {
-
- /* This hashes together:
- *
- * 1. The contents of the old seed file
- * 2. Some random data acquired from the UEFI RNG (optional)
- * 3. Some 'system token' the installer installed as EFI variable (optional)
- * 4. The UEFI "monotonic counter" that increases with each boot
- * 5. A supplied counter value
- *
- * And writes the result to the specified buffer.
- */
-
- struct sha256_ctx hash;
-
- assert(old_seed);
- assert(system_token_size == 0 || system_token);
-
- sha256_init_ctx(&hash);
- sha256_process_bytes(old_seed, size, &hash);
- if (rng)
- sha256_process_bytes(rng, size, &hash);
- if (system_token_size > 0)
- sha256_process_bytes(system_token, system_token_size, &hash);
- sha256_process_bytes(&uefi_monotonic_counter, sizeof(uefi_monotonic_counter), &hash);
- sha256_process_bytes(&counter, sizeof(counter), &hash);
- sha256_finish_ctx(&hash, ret);
-}
-
-static EFI_STATUS hash_many(
- const void *old_seed,
- const void *rng,
- UINTN size,
- const void *system_token,
- UINTN system_token_size,
- uint64_t uefi_monotonic_counter,
- UINTN counter_start,
- UINTN n,
- void **ret) {
-
- _cleanup_free_ void *output = NULL;
-
- assert(old_seed);
- assert(system_token_size == 0 || system_token);
- assert(ret);
-
- /* Hashes the specified parameters in counter mode, generating n hash values, with the counter in the
- * range counter_start…counter_start+n-1. */
-
- output = xmalloc_multiply(HASH_VALUE_SIZE, n);
-
- for (UINTN i = 0; i < n; i++)
- hash_once(old_seed, rng, size,
- system_token, system_token_size,
- uefi_monotonic_counter,
- counter_start + i,
- (uint8_t*) output + (i * HASH_VALUE_SIZE));
-
- *ret = TAKE_PTR(output);
- return EFI_SUCCESS;
-}
-
-static EFI_STATUS mangle_random_seed(
- const void *old_seed,
- const void *rng,
- UINTN size,
- const void *system_token,
- UINTN system_token_size,
- uint64_t uefi_monotonic_counter,
- void **ret_new_seed,
- void **ret_for_kernel) {
-
- _cleanup_free_ void *new_seed = NULL, *for_kernel = NULL;
- EFI_STATUS err;
- UINTN n;
-
- assert(old_seed);
- assert(system_token_size == 0 || system_token);
- assert(ret_new_seed);
- assert(ret_for_kernel);
-
- /* This takes the old seed file contents, an (optional) random number acquired from the UEFI RNG, an
- * (optional) system 'token' installed once by the OS installer in an EFI variable, and hashes them
- * together in counter mode, generating a new seed (to replace the file on disk) and the seed for the
- * kernel. To keep things simple, the new seed and kernel data have the same size as the old seed and
- * RNG data. */
-
- n = (size + HASH_VALUE_SIZE - 1) / HASH_VALUE_SIZE;
-
- /* Begin hashing in counter mode at counter 0 for the new seed for the disk */
- err = hash_many(old_seed, rng, size, system_token, system_token_size, uefi_monotonic_counter, 0, n, &new_seed);
- if (err != EFI_SUCCESS)
- return err;
-
- /* Continue counting at 'n' for the seed for the kernel */
- err = hash_many(old_seed, rng, size, system_token, system_token_size, uefi_monotonic_counter, n, n, &for_kernel);
- if (err != EFI_SUCCESS)
- return err;
-
- *ret_new_seed = TAKE_PTR(new_seed);
- *ret_for_kernel = TAKE_PTR(for_kernel);
-
return EFI_SUCCESS;
}
@@ -164,6 +60,7 @@ static EFI_STATUS acquire_system_token(void **ret, UINTN *ret_size) {
assert(ret);
assert(ret_size);
+ *ret_size = 0;
err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size);
if (err != EFI_SUCCESS) {
if (err != EFI_NOT_FOUND)
@@ -221,32 +118,88 @@ static void validate_sha256(void) {
#endif
}
-EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
- _cleanup_free_ void *seed = NULL, *new_seed = NULL, *rng = NULL, *for_kernel = NULL, *system_token = NULL;
+EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
+ uint8_t random_bytes[DESIRED_SEED_SIZE], hash_key[HASH_VALUE_SIZE];
+ _cleanup_free_ struct linux_efi_random_seed *new_seed_table = NULL;
+ struct linux_efi_random_seed *previous_seed_table = NULL;
+ _cleanup_free_ void *seed = NULL, *system_token = NULL;
_cleanup_(file_closep) EFI_FILE *handle = NULL;
- UINTN size, rsize, wsize, system_token_size = 0;
_cleanup_free_ EFI_FILE_INFO *info = NULL;
+ struct sha256_ctx hash;
uint64_t uefi_monotonic_counter = 0;
+ size_t size, rsize, wsize;
+ bool seeded_by_efi = false;
EFI_STATUS err;
+ EFI_TIME now;
+
+ CLEANUP_ERASE(random_bytes);
+ CLEANUP_ERASE(hash_key);
+ CLEANUP_ERASE(hash);
assert(root_dir);
+ assert_cc(DESIRED_SEED_SIZE == HASH_VALUE_SIZE);
validate_sha256();
if (mode == RANDOM_SEED_OFF)
return EFI_NOT_FOUND;
- /* Let's better be safe than sorry, and for now disable this logic in SecureBoot mode, so that we
- * don't credit a random seed that is not authenticated. */
- if (secure_boot_enabled())
- return EFI_NOT_FOUND;
+ /* hash = LABEL || sizeof(input1) || input1 || ... || sizeof(inputN) || inputN */
+ sha256_init_ctx(&hash);
+
+ /* Some basic domain separation in case somebody uses this data elsewhere */
+ sha256_process_bytes(HASH_LABEL, sizeof(HASH_LABEL) - 1, &hash);
+
+ for (size_t i = 0; i < ST->NumberOfTableEntries; ++i)
+ if (memcmp(&(const EFI_GUID)LINUX_EFI_RANDOM_SEED_TABLE_GUID,
+ &ST->ConfigurationTable[i].VendorGuid, sizeof(EFI_GUID)) == 0) {
+ previous_seed_table = ST->ConfigurationTable[i].VendorTable;
+ break;
+ }
+ if (!previous_seed_table) {
+ size = 0;
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ } else {
+ size = previous_seed_table->size;
+ seeded_by_efi = size >= DESIRED_SEED_SIZE;
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(previous_seed_table->seed, size, &hash);
+
+ /* Zero and free the previous seed table only at the end after we've managed to install a new
+ * one, so that in case this function fails or aborts, Linux still receives whatever the
+ * previous bootloader chain set. So, the next line of this block is not an explicit_bzero()
+ * call. */
+ }
+
+ /* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
+ * idea to use it because it helps us for cases where users mistakenly include a random seed in
+ * golden master images that are replicated many times. */
+ err = acquire_rng(random_bytes, sizeof(random_bytes));
+ if (err != EFI_SUCCESS) {
+ size = 0;
+ /* If we can't get any randomness from EFI itself, then we'll only be relying on what's in
+ * ESP. But ESP is mutable, so if secure boot is enabled, we probably shouldn't trust that
+ * alone, in which case we bail out early. */
+ if (!seeded_by_efi && secure_boot_enabled())
+ return EFI_NOT_FOUND;
+ } else {
+ seeded_by_efi = true;
+ size = sizeof(random_bytes);
+ }
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(random_bytes, size, &hash);
/* Get some system specific seed that the installer might have placed in an EFI variable. We include
* it in our hash. This is protection against golden master image sloppiness, and it remains on the
* system, even when disk images are duplicated or swapped out. */
- err = acquire_system_token(&system_token, &system_token_size);
- if (mode != RANDOM_SEED_ALWAYS && err != EFI_SUCCESS)
+ err = acquire_system_token(&system_token, &size);
+ if (mode != RANDOM_SEED_ALWAYS && (err != EFI_SUCCESS || size < DESIRED_SEED_SIZE) && !seeded_by_efi)
return err;
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ if (system_token) {
+ sha256_process_bytes(system_token, size, &hash);
+ explicit_bzero_safe(system_token, size);
+ }
err = root_dir->Open(
root_dir,
@@ -262,7 +215,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
err = get_file_info_harder(handle, &info, NULL);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to get file info for random seed: %r");
+ return log_error_status_stall(err, L"Failed to get file info for random seed: %r", err);
size = info->FileSize;
if (size < RANDOM_MAX_SIZE_MIN)
@@ -272,51 +225,105 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode) {
return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
seed = xmalloc(size);
-
rsize = size;
err = handle->Read(handle, &rsize, seed);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to read random seed file: %r", err);
- if (rsize != size)
+ if (rsize != size) {
+ explicit_bzero_safe(seed, rsize);
return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short read on random seed file.");
+ }
+
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(seed, size, &hash);
+ explicit_bzero_safe(seed, size);
err = handle->SetPosition(handle, 0);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
- /* Request some random data from the UEFI RNG. We don't need this to work safely, but it's a good
- * idea to use it because it helps us for cases where users mistakenly include a random seed in
- * golden master images that are replicated many times. */
- (void) acquire_rng(size, &rng); /* It's fine if this fails */
-
/* Let's also include the UEFI monotonic counter (which is supposedly increasing on every single
* boot) in the hash, so that even if the changes to the ESP for some reason should not be
* persistent, the random seed we generate will still be different on every single boot. */
err = BS->GetNextMonotonicCount(&uefi_monotonic_counter);
- if (err != EFI_SUCCESS)
+ if (err != EFI_SUCCESS && !seeded_by_efi)
return log_error_status_stall(err, L"Failed to acquire UEFI monotonic counter: %r", err);
-
- /* Calculate new random seed for the disk and what to pass to the kernel */
- err = mangle_random_seed(seed, rng, size, system_token, system_token_size, uefi_monotonic_counter, &new_seed, &for_kernel);
- if (err != EFI_SUCCESS)
- return err;
-
+ size = sizeof(uefi_monotonic_counter);
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(&uefi_monotonic_counter, size, &hash);
+ err = RT->GetTime(&now, NULL);
+ size = err == EFI_SUCCESS ? sizeof(now) : 0; /* Known to be flaky, so don't bark on error. */
+ sha256_process_bytes(&size, sizeof(size), &hash);
+ sha256_process_bytes(&now, size, &hash);
+
+ /* hash_key = HASH(hash) */
+ sha256_finish_ctx(&hash, hash_key);
+
+ /* hash = hash_key || 0 */
+ sha256_init_ctx(&hash);
+ sha256_process_bytes(hash_key, sizeof(hash_key), &hash);
+ sha256_process_bytes(&(const uint8_t){ 0 }, sizeof(uint8_t), &hash);
+ /* random_bytes = HASH(hash) */
+ sha256_finish_ctx(&hash, random_bytes);
+
+ size = sizeof(random_bytes);
+ /* If the file size is too large, zero out the remaining bytes on disk, and then truncate. */
+ if (size < info->FileSize) {
+ err = handle->SetPosition(handle, size);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to seek to offset of random seed file: %r", err);
+ wsize = info->FileSize - size;
+ err = handle->Write(handle, &wsize, seed /* All zeros now */);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
+ if (wsize != info->FileSize - size)
+ return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
+ err = handle->Flush(handle);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
+ err = handle->SetPosition(handle, 0);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
+ info->FileSize = size;
+ err = handle->SetInfo(handle, &GenericFileInfo, info->Size, info);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to truncate random seed file: %r", err);
+ }
/* Update the random seed on disk before we use it */
wsize = size;
- err = handle->Write(handle, &wsize, new_seed);
+ err = handle->Write(handle, &wsize, random_bytes);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
if (wsize != size)
return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
-
err = handle->Flush(handle);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
- /* We are good to go */
- err = efivar_set_raw(LOADER_GUID, L"LoaderRandomSeed", for_kernel, size, 0);
+ err = BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*new_seed_table) + DESIRED_SEED_SIZE,
+ (void **) &new_seed_table);
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(err, L"Failed to allocate EFI table for random seed: %r", err);
+ new_seed_table->size = DESIRED_SEED_SIZE;
+
+ /* hash = hash_key || 1 */
+ sha256_init_ctx(&hash);
+ sha256_process_bytes(hash_key, sizeof(hash_key), &hash);
+ sha256_process_bytes(&(const uint8_t){ 1 }, sizeof(uint8_t), &hash);
+ /* new_seed_table->seed = HASH(hash) */
+ sha256_finish_ctx(&hash, new_seed_table->seed);
+
+ err = BS->InstallConfigurationTable(&(EFI_GUID)LINUX_EFI_RANDOM_SEED_TABLE_GUID, new_seed_table);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to write random seed to EFI variable: %r", err);
+ return log_error_status_stall(err, L"Failed to install EFI table for random seed: %r", err);
+ TAKE_PTR(new_seed_table);
+
+ if (previous_seed_table) {
+ /* Now that we've succeeded in installing the new table, we can safely nuke the old one. */
+ explicit_bzero_safe(previous_seed_table->seed, previous_seed_table->size);
+ explicit_bzero_safe(previous_seed_table, sizeof(*previous_seed_table));
+ free(previous_seed_table);
+ }
return EFI_SUCCESS;
}
diff --git a/src/core/efi-random.c b/src/core/efi-random.c
index 4086b12739..61516775fc 100644
--- a/src/core/efi-random.c
+++ b/src/core/efi-random.c
@@ -12,79 +12,23 @@
#include "random-util.h"
#include "strv.h"
-/* If a random seed was passed by the boot loader in the LoaderRandomSeed EFI variable, let's credit it to
- * the kernel's random pool, but only once per boot. If this is run very early during initialization we can
- * instantly boot up with a filled random pool.
- *
- * This makes no judgement on the entropy passed, it's the job of the boot loader to only pass us a seed that
- * is suitably validated. */
-
-static void lock_down_efi_variables(void) {
+void lock_down_efi_variables(void) {
+ _cleanup_close_ int fd = -1;
int r;
+ fd = open(EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken)), O_RDONLY|O_CLOEXEC);
+ if (fd < 0) {
+ if (errno != ENOENT)
+ log_warning_errno(errno, "Unable to open LoaderSystemToken EFI variable, ignoring: %m");
+ return;
+ }
+
/* Paranoia: let's restrict access modes of these a bit, so that unprivileged users can't use them to
* identify the system or gain too much insight into what we might have credited to the entropy
* pool. */
- FOREACH_STRING(path,
- EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderRandomSeed)),
- EFIVAR_PATH(EFI_LOADER_VARIABLE(LoaderSystemToken))) {
-
- r = chattr_path(path, 0, FS_IMMUTABLE_FL, NULL);
- if (r == -ENOENT)
- continue;
- if (r < 0)
- log_warning_errno(r, "Failed to drop FS_IMMUTABLE_FL from %s, ignoring: %m", path);
-
- if (chmod(path, 0600) < 0)
- log_warning_errno(errno, "Failed to reduce access mode of %s, ignoring: %m", path);
- }
-}
-
-int efi_take_random_seed(void) {
- _cleanup_free_ void *value = NULL;
- size_t size;
- int r;
-
- /* Paranoia comes first. */
- lock_down_efi_variables();
-
- if (access("/run/systemd/efi-random-seed-taken", F_OK) < 0) {
- if (errno != ENOENT) {
- log_warning_errno(errno, "Failed to determine whether we already used the random seed token, not using it.");
- return 0;
- }
-
- /* ENOENT means we haven't used it yet. */
- } else {
- log_debug("EFI random seed already used, not using again.");
- return 0;
- }
-
- r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderRandomSeed), NULL, &value, &size);
- if (r == -EOPNOTSUPP) {
- log_debug_errno(r, "System lacks EFI support, not initializing random seed from EFI variable.");
- return 0;
- }
- if (r == -ENOENT) {
- log_debug_errno(r, "Boot loader did not pass LoaderRandomSeed EFI variable, not crediting any entropy.");
- return 0;
- }
+ r = chattr_fd(fd, 0, FS_IMMUTABLE_FL, NULL);
if (r < 0)
- return log_warning_errno(r, "Failed to read LoaderRandomSeed EFI variable, ignoring: %m");
-
- if (size == 0)
- return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Random seed passed from boot loader has zero size? Ignoring.");
-
- /* Before we use the seed, let's mark it as used, so that we never credit it twice. Also, it's a nice
- * way to let users known that we successfully acquired entropy from the boot loader. */
- r = touch("/run/systemd/efi-random-seed-taken");
- if (r < 0)
- return log_warning_errno(r, "Unable to mark EFI random seed as used, not using it: %m");
-
- r = random_write_entropy(-1, value, size, true);
- if (r < 0)
- return log_warning_errno(errno, "Failed to credit entropy, ignoring: %m");
-
- log_info("Successfully credited entropy passed from boot loader.");
- return 1;
+ log_warning_errno(r, "Failed to drop FS_IMMUTABLE_FL from LoaderSystemToken EFI variable, ignoring: %m");
+ if (fchmod(fd, 0600) < 0)
+ log_warning_errno(errno, "Failed to reduce access mode of LoaderSystemToken EFI variable, ignoring: %m");
}
diff --git a/src/core/efi-random.h b/src/core/efi-random.h
index 7d20fff57d..87166c9e3f 100644
--- a/src/core/efi-random.h
+++ b/src/core/efi-random.h
@@ -1,4 +1,4 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-int efi_take_random_seed(void);
+void lock_down_efi_variables(void);
diff --git a/src/core/main.c b/src/core/main.c
index 126a4bce8c..e7b8e98bca 100644
--- a/src/core/main.c
+++ b/src/core/main.c
@@ -2840,8 +2840,8 @@ int main(int argc, char *argv[]) {
goto finish;
}
- /* The efivarfs is now mounted, let's read the random seed off it */
- (void) efi_take_random_seed();
+ /* The efivarfs is now mounted, let's lock down the system token. */
+ lock_down_efi_variables();
/* Cache command-line options passed from EFI variables */
if (!skip_setup)
diff --git a/units/systemd-boot-system-token.service b/units/systemd-boot-system-token.service
index 662a1fda04..5a56d7c331 100644
--- a/units/systemd-boot-system-token.service
+++ b/units/systemd-boot-system-token.service
@@ -26,9 +26,6 @@ ConditionPathExists=/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-
# Only run this if there is no system token defined yet, or …
ConditionPathExists=|!/sys/firmware/efi/efivars/LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
-# … if the boot loader didn't pass the OS a random seed (and thus probably was missing the random seed file)
-ConditionPathExists=|!/sys/firmware/efi/efivars/LoaderRandomSeed-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
-
[Service]
Type=oneshot
RemainAfterExit=yes

View File

@ -0,0 +1,213 @@
From 461ba7436d539258744d03400490ef100095e093 Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Fri, 11 Nov 2022 15:22:35 +0100
Subject: [PATCH] random-seed: refresh EFI boot seed when writing a new seed
Since this runs at shutdown to write a new seed, we should also keep the
bootloader's seed maximally fresh by doing the same. So we follow the
same pattern - hash some new random bytes with the old seed to make a
new seed. We let this fail without warning, because it's just an
opportunistic thing. If the user happens to have set up the random seed
with bootctl, and the RNG is initialized, then things should be fine. If
not, we create a new seed if systemd-boot is in use. And if not, then we
just don't do anything.
(cherry picked from commit f913c784ad4c93894fd6cb2590738113dff5a694)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 123 +++++++++++++++++++++++++++++++---
1 file changed, 112 insertions(+), 11 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index b548f92bbe..54ec3aa7d5 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -15,7 +15,11 @@
#include "sd-id128.h"
#include "alloc-util.h"
+#include "build.h"
+#include "chase-symlinks.h"
+#include "efi-loader.h"
#include "fd-util.h"
+#include "find-esp.h"
#include "fs-util.h"
#include "io-util.h"
#include "log.h"
@@ -25,6 +29,7 @@
#include "mkdir.h"
#include "parse-argument.h"
#include "parse-util.h"
+#include "path-util.h"
#include "pretty-print.h"
#include "random-util.h"
#include "string-table.h"
@@ -185,7 +190,7 @@ static int load_seed_file(
if (ret_hash_state) {
struct sha256_ctx *hash_state;
- hash_state = malloc(sizeof(struct sha256_ctx));
+ hash_state = new(struct sha256_ctx, 1);
if (!hash_state)
return log_oom();
@@ -311,6 +316,101 @@ static int save_seed_file(
return 0;
}
+static int refresh_boot_seed(void) {
+ uint8_t buffer[RANDOM_EFI_SEED_SIZE];
+ struct sha256_ctx hash_state;
+ _cleanup_free_ void *seed_file_bytes = NULL;
+ _cleanup_free_ char *esp_path = NULL;
+ _cleanup_close_ int seed_fd = -1;
+ size_t len;
+ ssize_t r;
+
+ assert_cc(RANDOM_EFI_SEED_SIZE == SHA256_DIGEST_SIZE);
+
+ r = find_esp_and_warn(NULL, NULL, /* unprivileged_mode= */ false, &esp_path,
+ NULL, NULL, NULL, NULL, NULL);
+ if (r < 0) {
+ if (r == -ENOKEY) {
+ log_debug_errno(r, "Couldn't find any ESP, so not updating ESP random seed.");
+ return 0;
+ }
+ return r; /* find_esp_and_warn() already logged */
+ }
+
+ seed_fd = chase_symlinks_and_open("/loader/random-seed", esp_path,
+ CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS,
+ O_RDWR|O_CLOEXEC|O_NOCTTY, NULL);
+ if (seed_fd == -ENOENT) {
+ uint64_t features;
+
+ r = efi_loader_get_features(&features);
+ if (r == 0 && FLAGS_SET(features, EFI_LOADER_FEATURE_RANDOM_SEED)) {
+ int dir_fd = chase_symlinks_and_open("/loader", esp_path,
+ CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS,
+ O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL);
+ if (dir_fd >= 0) {
+ seed_fd = openat(dir_fd, "random-seed", O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
+ close(dir_fd);
+ }
+ }
+ }
+ if (seed_fd < 0) {
+ log_debug_errno(seed_fd, "Failed to open EFI seed path: %m");
+ return 0;
+ }
+ r = random_seed_size(seed_fd, &len);
+ if (r < 0)
+ return log_error_errno(r, "Failed to determine EFI seed path length: %m");
+ seed_file_bytes = malloc(len);
+ if (!seed_file_bytes)
+ return log_oom();
+ r = loop_read(seed_fd, seed_file_bytes, len, false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to read EFI seed file: %m");
+
+ /* Hash the old seed in so that we never regress in entropy. */
+ sha256_init_ctx(&hash_state);
+ sha256_process_bytes(&r, sizeof(r), &hash_state);
+ sha256_process_bytes(seed_file_bytes, r, &hash_state);
+
+ /* We're doing this opportunistically, so if the seeding dance before didn't manage to initialize the
+ * RNG, there's no point in doing it here. Secondly, getrandom(GRND_NONBLOCK) has been around longer
+ * than EFI seeding anyway, so there's no point in having non-getrandom() fallbacks here. So if this
+ * fails, just return early to cut our losses. */
+ r = getrandom(buffer, sizeof(buffer), GRND_NONBLOCK);
+ if (r < 0) {
+ if (errno == EAGAIN) {
+ log_debug_errno(errno, "Random pool not initialized yet, so skipping EFI seed update");
+ return 0;
+ }
+ if (errno == ENOSYS) {
+ log_debug_errno(errno, "getrandom() not available, so skipping EFI seed update");
+ return 0;
+ }
+ return log_error_errno(errno, "Failed to generate random bytes for EFI seed: %m");
+ }
+ assert(r == sizeof(buffer));
+
+ /* Hash the new seed into the state containing the old one to generate our final seed. */
+ sha256_process_bytes(&r, sizeof(r), &hash_state);
+ sha256_process_bytes(buffer, r, &hash_state);
+ sha256_finish_ctx(&hash_state, buffer);
+
+ if (lseek(seed_fd, 0, SEEK_SET) < 0)
+ return log_error_errno(errno, "Failed to seek to beginning of EFI seed file: %m");
+ r = loop_write(seed_fd, buffer, sizeof(buffer), false);
+ if (r < 0)
+ return log_error_errno(r, "Failed to write new EFI seed file: %m");
+ if (ftruncate(seed_fd, sizeof(buffer)) < 0)
+ return log_error_errno(errno, "Failed to truncate EFI seed file: %m");
+ r = fsync_full(seed_fd);
+ if (r < 0)
+ return log_error_errno(errno, "Failed to fsync EFI seed file: %m");
+
+ log_debug("Updated random seed in ESP");
+ return 0;
+}
+
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@@ -402,15 +502,15 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m");
+ random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (random_fd < 0)
+ return log_error_errno(errno, "Failed to open /dev/urandom: %m");
+
/* When we load the seed we read it and write it to the device and then immediately update the saved
* seed with new data, to make sure the next boot gets seeded differently. */
switch (arg_action) {
case ACTION_LOAD:
- random_fd = open("/dev/urandom", O_RDWR|O_CLOEXEC|O_NOCTTY);
- if (random_fd < 0)
- return log_error_errno(errno, "Failed to open /dev/urandom: %m");
-
/* First, let's write the machine ID into /dev/urandom, not crediting entropy. See
* load_machine_id() for an explanation why. */
load_machine_id(random_fd);
@@ -428,8 +528,10 @@ static int run(int argc, char *argv[]) {
log_full_errno(level, open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m");
log_full_errno(level, errno, "Failed to open " RANDOM_SEED " for reading: %m");
+ r = -errno;
- return missing ? 0 : -errno;
+ (void) refresh_boot_seed();
+ return missing ? 0 : r;
}
} else
write_seed_file = true;
@@ -439,10 +541,7 @@ static int run(int argc, char *argv[]) {
break;
case ACTION_SAVE:
- random_fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
- if (random_fd < 0)
- return log_error_errno(errno, "Failed to open /dev/urandom: %m");
-
+ (void) refresh_boot_seed();
seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
if (seed_fd < 0)
return log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
@@ -460,9 +559,11 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
- if (read_seed_file)
+ if (read_seed_file) {
r = load_seed_file(seed_fd, random_fd, seed_size,
write_seed_file ? &hash_state : NULL);
+ (void) refresh_boot_seed();
+ }
if (r >= 0 && write_seed_file)
r = save_seed_file(seed_fd, random_fd, seed_size, synchronous, hash_state);

View File

@ -0,0 +1,181 @@
From 4c15a3931701cca73d78bb09953e439e7125e020 Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Wed, 16 Nov 2022 19:27:50 +0100
Subject: [PATCH] random-seed: handle post-merge review nits
These are various misc things that came up after merging.
(cherry picked from commit 3daeef088410cdddef622007f95b0a1b4a439532)
Related: RHEL-16952
---
src/boot/bootctl.c | 2 +-
src/boot/efi/random-seed.c | 6 ++--
src/random-seed/random-seed.c | 61 ++++++++++++++++++-----------------
3 files changed, 36 insertions(+), 33 deletions(-)
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index e23a72fd38..8d45e11c2b 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -1983,7 +1983,7 @@ static int verb_list(int argc, char *argv[], void *userdata) {
static int install_random_seed(const char *esp) {
_cleanup_(unlink_and_freep) char *tmp = NULL;
- unsigned char buffer[RANDOM_EFI_SEED_SIZE];
+ uint8_t buffer[RANDOM_EFI_SEED_SIZE];
_cleanup_free_ char *path = NULL;
_cleanup_close_ int fd = -1;
size_t token_size;
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index c723160c0f..e11e345e88 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -60,7 +60,6 @@ static EFI_STATUS acquire_system_token(void **ret, UINTN *ret_size) {
assert(ret);
assert(ret_size);
- *ret_size = 0;
err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size);
if (err != EFI_SUCCESS) {
if (err != EFI_NOT_FOUND)
@@ -192,6 +191,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
/* Get some system specific seed that the installer might have placed in an EFI variable. We include
* it in our hash. This is protection against golden master image sloppiness, and it remains on the
* system, even when disk images are duplicated or swapped out. */
+ size = 0;
err = acquire_system_token(&system_token, &size);
if (mode != RANDOM_SEED_ALWAYS && (err != EFI_SUCCESS || size < DESIRED_SEED_SIZE) && !seeded_by_efi)
return err;
@@ -251,6 +251,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
size = sizeof(uefi_monotonic_counter);
sha256_process_bytes(&size, sizeof(size), &hash);
sha256_process_bytes(&uefi_monotonic_counter, size, &hash);
+
err = RT->GetTime(&now, NULL);
size = err == EFI_SUCCESS ? sizeof(now) : 0; /* Known to be flaky, so don't bark on error. */
sha256_process_bytes(&size, sizeof(size), &hash);
@@ -300,7 +301,8 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
- err = BS->AllocatePool(EfiACPIReclaimMemory, sizeof(*new_seed_table) + DESIRED_SEED_SIZE,
+ err = BS->AllocatePool(EfiACPIReclaimMemory,
+ offsetof(struct linux_efi_random_seed, seed) + DESIRED_SEED_SIZE,
(void **) &new_seed_table);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to allocate EFI table for random seed: %r", err);
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 54ec3aa7d5..ab1f942289 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -321,9 +321,10 @@ static int refresh_boot_seed(void) {
struct sha256_ctx hash_state;
_cleanup_free_ void *seed_file_bytes = NULL;
_cleanup_free_ char *esp_path = NULL;
- _cleanup_close_ int seed_fd = -1;
+ _cleanup_close_ int seed_fd = -1, dir_fd = -1;
size_t len;
- ssize_t r;
+ ssize_t n;
+ int r;
assert_cc(RANDOM_EFI_SEED_SIZE == SHA256_DIGEST_SIZE);
@@ -337,48 +338,48 @@ static int refresh_boot_seed(void) {
return r; /* find_esp_and_warn() already logged */
}
- seed_fd = chase_symlinks_and_open("/loader/random-seed", esp_path,
- CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS,
- O_RDWR|O_CLOEXEC|O_NOCTTY, NULL);
- if (seed_fd == -ENOENT) {
+ r = chase_symlinks("/loader", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, &dir_fd);
+ if (r < 0) {
+ if (r == -ENOENT) {
+ log_debug_errno(r, "Couldn't find ESP loader directory, so not updating ESP random seed.");
+ return 0;
+ }
+ return log_error_errno(r, "Failed to open ESP loader directory: %m");
+ }
+ seed_fd = openat(dir_fd, "random-seed", O_NOFOLLOW|O_RDWR|O_CLOEXEC|O_NOCTTY);
+ if (seed_fd < 0 && errno == ENOENT) {
uint64_t features;
-
r = efi_loader_get_features(&features);
- if (r == 0 && FLAGS_SET(features, EFI_LOADER_FEATURE_RANDOM_SEED)) {
- int dir_fd = chase_symlinks_and_open("/loader", esp_path,
- CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS,
- O_DIRECTORY|O_CLOEXEC|O_NOCTTY, NULL);
- if (dir_fd >= 0) {
- seed_fd = openat(dir_fd, "random-seed", O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
- close(dir_fd);
- }
+ if (r == 0 && FLAGS_SET(features, EFI_LOADER_FEATURE_RANDOM_SEED))
+ seed_fd = openat(dir_fd, "random-seed", O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
+ else {
+ log_debug_errno(seed_fd, "Couldn't find ESP random seed, and not booted with systemd-boot, so not updating ESP random seed.");
+ return 0;
}
}
- if (seed_fd < 0) {
- log_debug_errno(seed_fd, "Failed to open EFI seed path: %m");
- return 0;
- }
+ if (seed_fd < 0)
+ return log_error_errno(errno, "Failed to open EFI seed path: %m");
r = random_seed_size(seed_fd, &len);
if (r < 0)
return log_error_errno(r, "Failed to determine EFI seed path length: %m");
seed_file_bytes = malloc(len);
if (!seed_file_bytes)
return log_oom();
- r = loop_read(seed_fd, seed_file_bytes, len, false);
- if (r < 0)
- return log_error_errno(r, "Failed to read EFI seed file: %m");
+ n = loop_read(seed_fd, seed_file_bytes, len, false);
+ if (n < 0)
+ return log_error_errno(n, "Failed to read EFI seed file: %m");
/* Hash the old seed in so that we never regress in entropy. */
sha256_init_ctx(&hash_state);
- sha256_process_bytes(&r, sizeof(r), &hash_state);
- sha256_process_bytes(seed_file_bytes, r, &hash_state);
+ sha256_process_bytes(&n, sizeof(n), &hash_state);
+ sha256_process_bytes(seed_file_bytes, n, &hash_state);
/* We're doing this opportunistically, so if the seeding dance before didn't manage to initialize the
* RNG, there's no point in doing it here. Secondly, getrandom(GRND_NONBLOCK) has been around longer
* than EFI seeding anyway, so there's no point in having non-getrandom() fallbacks here. So if this
* fails, just return early to cut our losses. */
- r = getrandom(buffer, sizeof(buffer), GRND_NONBLOCK);
- if (r < 0) {
+ n = getrandom(buffer, sizeof(buffer), GRND_NONBLOCK);
+ if (n < 0) {
if (errno == EAGAIN) {
log_debug_errno(errno, "Random pool not initialized yet, so skipping EFI seed update");
return 0;
@@ -389,11 +390,11 @@ static int refresh_boot_seed(void) {
}
return log_error_errno(errno, "Failed to generate random bytes for EFI seed: %m");
}
- assert(r == sizeof(buffer));
+ assert(n == sizeof(buffer));
/* Hash the new seed into the state containing the old one to generate our final seed. */
- sha256_process_bytes(&r, sizeof(r), &hash_state);
- sha256_process_bytes(buffer, r, &hash_state);
+ sha256_process_bytes(&n, sizeof(n), &hash_state);
+ sha256_process_bytes(buffer, n, &hash_state);
sha256_finish_ctx(&hash_state, buffer);
if (lseek(seed_fd, 0, SEEK_SET) < 0)
@@ -405,7 +406,7 @@ static int refresh_boot_seed(void) {
return log_error_errno(errno, "Failed to truncate EFI seed file: %m");
r = fsync_full(seed_fd);
if (r < 0)
- return log_error_errno(errno, "Failed to fsync EFI seed file: %m");
+ return log_error_errno(r, "Failed to fsync EFI seed file: %m");
log_debug("Updated random seed in ESP");
return 0;

View File

@ -0,0 +1,50 @@
From 2d6ce79b1d727cd85d3504706da9c2eca6dc72fe Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Wed, 16 Nov 2022 19:34:53 +0100
Subject: [PATCH] boot: do not truncate random seed file
There are concerns about the FAT file system driver exploding if we try
to do this, so just leave the bytes zeroed out instead.
(cherry picked from commit 5d29d07b342397a8ecc4bea96f53595a03dd94f1)
Related: RHEL-16952
---
src/boot/efi/random-seed.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index e11e345e88..471398fbf1 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -268,7 +268,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
sha256_finish_ctx(&hash, random_bytes);
size = sizeof(random_bytes);
- /* If the file size is too large, zero out the remaining bytes on disk, and then truncate. */
+ /* If the file size is too large, zero out the remaining bytes on disk. */
if (size < info->FileSize) {
err = handle->SetPosition(handle, size);
if (err != EFI_SUCCESS)
@@ -285,10 +285,17 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
err = handle->SetPosition(handle, 0);
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
- info->FileSize = size;
- err = handle->SetInfo(handle, &GenericFileInfo, info->Size, info);
- if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to truncate random seed file: %r", err);
+
+ /* We could truncate the file here with something like:
+ *
+ * info->FileSize = size;
+ * err = handle->SetInfo(handle, &GenericFileInfo, info->Size, info);
+ * if (err != EFI_SUCCESS)
+ * return log_error_status_stall(err, L"Failed to truncate random seed file: %r", err);
+ *
+ * But this is considered slightly risky, because EFI filesystem drivers are a little bit
+ * flimsy. So instead we rely on userspace eventually truncating this when it writes a new
+ * seed. For now the best we do is zero it. */
}
/* Update the random seed on disk before we use it */
wsize = size;

View File

@ -0,0 +1,117 @@
From 36e71ba28d490bb3dba34df8eef472c1560e3772 Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Thu, 17 Nov 2022 16:11:44 +0100
Subject: [PATCH] bootctl: install system token on virtualized systems
Removing the virtualization check might not be the worst thing in the
world, and would potentially get many, many more systems properly seeded
rather than not seeded. There are a few reasons to consider this:
- In most QEMU setups and most guides on how to setup QEMU, a separate
pflash file is used for nvram variables, and this generally isn't
copied around.
- We're now hashing in a timestamp, which should provide some level of
differentiation, given that EFI_TIME has a nanoseconds field.
- The kernel itself will additionally hash in: a high resolution time
stamp, a cycle counter, RDRAND output, the VMGENID uniquely
identifying the virtual machine, any other seeds from the hypervisor
(like from FDT or setup_data).
- During early boot, the RNG is reseeded quite frequently to account for
the importance of early differentiation.
So maybe the mitigating factors make the actual feared problem
significantly less likely and therefore the pros of having file-based
seeding might outweigh the cons of weird misconfigured setups having a
hypothetical problem on first boot.
(cherry picked from commit a4eea6038c1c7f88adc6d6584d18ea60ea11b08f)
Related: RHEL-16952
---
docs/RANDOM_SEEDS.md | 15 ++++-----------
src/boot/bootctl.c | 20 --------------------
units/systemd-boot-system-token.service | 8 ++------
3 files changed, 6 insertions(+), 37 deletions(-)
diff --git a/docs/RANDOM_SEEDS.md b/docs/RANDOM_SEEDS.md
index b7240f0d89..a1134d6417 100644
--- a/docs/RANDOM_SEEDS.md
+++ b/docs/RANDOM_SEEDS.md
@@ -232,17 +232,10 @@ boot, in order to ensure the entropy pool is filled up quickly.
too), which should be safe even with FAT file system drivers built into
low-quality EFI firmwares.
- As a special restriction: in virtualized environments PID 1 will refrain
- from using this mechanism, for safety reasons. This is because on VM
- environments the EFI variable space and the disk space is generally not
- maintained physically separate (for example, `qemu` in EFI mode stores the
- variables in the ESP itself). The robustness towards sloppy OS image
- generation is the main purpose of maintaining the 'system token' however,
- and if the EFI variable storage is not kept physically separate from the OS
- image there's no point in it. That said, OS builders that know that they are
- not going to replicate the built image on multiple systems may opt to turn
- off the 'system token' concept by setting `random-seed-mode always` in the
- ESP's
+ If the system token is not desired but this seeding mechanism still is, OS
+ builders that know that they are not going to replicate the built image on
+ multiple systems may opt to turn off the 'system token' concept by setting
+ `random-seed-mode always` in the ESP's
[`/loader/loader.conf`](https://www.freedesktop.org/software/systemd/man/loader.conf.html)
file. If done, `systemd-boot` will use the random seed file even if no
system token is found in EFI variables.
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 8d45e11c2b..d495c72bdd 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -2048,26 +2048,6 @@ static int install_random_seed(const char *esp) {
if (r < 0) {
if (r != -ENXIO)
log_warning_errno(r, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
-
- if (detect_vm() > 0) {
- /* Let's not write a system token if we detect we are running in a VM
- * environment. Why? Our default security model for the random seed uses the system
- * token as a mechanism to ensure we are not vulnerable to golden master sloppiness
- * issues, i.e. that people initialize the random seed file, then copy the image to
- * many systems and end up with the same random seed in each that is assumed to be
- * valid but in reality is the same for all machines. By storing a system token in
- * the EFI variable space we can make sure that even though the random seeds on disk
- * are all the same they will be different on each system under the assumption that
- * the EFI variable space is maintained separate from the random seed storage. That
- * is generally the case on physical systems, as the ESP is stored on persistent
- * storage, and the EFI variables in NVRAM. However in virtualized environments this
- * is generally not true: the EFI variable set is typically stored along with the
- * disk image itself. For example, using the OVMF EFI firmware the EFI variables are
- * stored in a file in the ESP itself. */
-
- log_notice("Not installing system token, since we are running in a virtualized environment.");
- return 0;
- }
} else if (r == 0) {
log_notice("Not writing system token, because $SYSTEMD_WRITE_SYSTEM_TOKEN is set to false.");
return 0;
diff --git a/units/systemd-boot-system-token.service b/units/systemd-boot-system-token.service
index 5a56d7c331..689b902000 100644
--- a/units/systemd-boot-system-token.service
+++ b/units/systemd-boot-system-token.service
@@ -16,15 +16,11 @@ After=local-fs.target systemd-random-seed.service
Conflicts=shutdown.target initrd-switch-root.target
Before=shutdown.target initrd-switch-root.target
-# Don't run this in a VM environment, because there EFI variables are not
-# actually stored in NVRAM, independent of regular storage.
-ConditionVirtualization=no
-
# Only run this if the boot loader can support random seed initialization.
ConditionPathExists=/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
-# Only run this if there is no system token defined yet, or …
-ConditionPathExists=|!/sys/firmware/efi/efivars/LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+# Only run this if there is no system token defined yet
+ConditionPathExists=!/sys/firmware/efi/efivars/LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
[Service]
Type=oneshot

View File

@ -0,0 +1,221 @@
From 44f9f0e2e416b67c2ca46e9e36184f4a0bf1f1b0 Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Mon, 21 Nov 2022 16:40:24 +0100
Subject: [PATCH] boot: remove random-seed-mode
Now that the random seed is used on virtualized systems, there's no
point in having a random-seed-mode toggle switch. Let's just always
require it now, with the existing logic already being there to allow not
having it if EFI itself has an RNG. In other words, the logic for this
can now be automatic.
(cherry picked from commit 47b3e96647e18e8ca219c4792ab769344eea11bb)
Related: RHEL-16952
---
docs/RANDOM_SEEDS.md | 8 --------
man/loader.conf.xml | 19 -------------------
src/boot/efi/boot.c | 26 +-------------------------
src/boot/efi/random-seed.c | 5 +----
src/boot/efi/random-seed.h | 18 +-----------------
src/shared/bootspec.c | 3 +--
src/shared/bootspec.h | 1 -
7 files changed, 4 insertions(+), 76 deletions(-)
diff --git a/docs/RANDOM_SEEDS.md b/docs/RANDOM_SEEDS.md
index a1134d6417..4cb2bb9cfa 100644
--- a/docs/RANDOM_SEEDS.md
+++ b/docs/RANDOM_SEEDS.md
@@ -232,14 +232,6 @@ boot, in order to ensure the entropy pool is filled up quickly.
too), which should be safe even with FAT file system drivers built into
low-quality EFI firmwares.
- If the system token is not desired but this seeding mechanism still is, OS
- builders that know that they are not going to replicate the built image on
- multiple systems may opt to turn off the 'system token' concept by setting
- `random-seed-mode always` in the ESP's
- [`/loader/loader.conf`](https://www.freedesktop.org/software/systemd/man/loader.conf.html)
- file. If done, `systemd-boot` will use the random seed file even if no
- system token is found in EFI variables.
-
4. A kernel command line option `systemd.random_seed=` may be used to pass in a
base64 encoded seed to initialize the kernel's entropy pool from during
early service manager initialization. This option is only safe in testing
diff --git a/man/loader.conf.xml b/man/loader.conf.xml
index 7f173aec61..d937583da9 100644
--- a/man/loader.conf.xml
+++ b/man/loader.conf.xml
@@ -309,25 +309,6 @@ sign-efi-sig-list -c KEK.crt -k KEK.key db db.esl db.auth
encrypted drive to change. If PCR 4 is not measured, this setting can be disabled to speed
up booting into Windows.</para></listitem>
</varlistentry>
-
- <varlistentry>
- <term>random-seed-mode</term>
-
- <listitem><para>Takes one of <literal>off</literal>, <literal>with-system-token</literal> and
- <literal>always</literal>. If <literal>off</literal> no random seed data is read off the ESP, nor
- passed to the OS. If <literal>with-system-token</literal> (the default)
- <command>systemd-boot</command> will read a random seed from the ESP (from the file
- <filename>/loader/random-seed</filename>) only if the <varname>LoaderSystemToken</varname> EFI
- variable is set, and then derive the random seed to pass to the OS from the combination. If
- <literal>always</literal> the boot loader will do so even if <varname>LoaderSystemToken</varname> is
- not set. This mode is useful in environments where protection against OS image reuse is not a
- concern, and the random seed shall be used even with no further setup in place. Use <command>bootctl
- random-seed</command> to initialize both the random seed file in the ESP and the system token EFI
- variable.</para>
-
- <para>See <ulink url="https://systemd.io/RANDOM_SEEDS">Random Seeds</ulink> for further
- information.</para></listitem>
- </varlistentry>
</variablelist>
</refsect1>
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 5944451e6a..4a64c2402d 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -97,7 +97,6 @@ typedef struct {
bool beep;
int64_t console_mode;
int64_t console_mode_efivar;
- RandomSeedMode random_seed_mode;
} Config;
/* These values have been chosen so that the transitions the user sees could
@@ -529,7 +528,6 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
ps_bool(L" auto-firmware: %s\n", config->auto_firmware);
ps_bool(L" beep: %s\n", config->beep);
ps_bool(L" reboot-for-bitlocker: %s\n", config->reboot_for_bitlocker);
- ps_string(L" random-seed-mode: %s\n", random_seed_modes_table[config->random_seed_mode]);
switch (config->secure_boot_enroll) {
case ENROLL_OFF:
@@ -1273,27 +1271,6 @@ static void config_defaults_load_from_file(Config *config, char *content) {
}
continue;
}
-
- if (streq8(key, "random-seed-mode")) {
- if (streq8(value, "off"))
- config->random_seed_mode = RANDOM_SEED_OFF;
- else if (streq8(value, "with-system-token"))
- config->random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN;
- else if (streq8(value, "always"))
- config->random_seed_mode = RANDOM_SEED_ALWAYS;
- else {
- bool on;
-
- err = parse_boolean(value, &on);
- if (err != EFI_SUCCESS) {
- log_error_stall(L"Error parsing 'random-seed-mode' config option: %a", value);
- continue;
- }
-
- config->random_seed_mode = on ? RANDOM_SEED_ALWAYS : RANDOM_SEED_OFF;
- }
- continue;
- }
}
}
@@ -1584,7 +1561,6 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
.auto_firmware = true,
.reboot_for_bitlocker = false,
.secure_boot_enroll = ENROLL_MANUAL,
- .random_seed_mode = RANDOM_SEED_WITH_SYSTEM_TOKEN,
.idx_default_efivar = IDX_INVALID,
.console_mode = CONSOLE_MODE_KEEP,
.console_mode_efivar = CONSOLE_MODE_KEEP,
@@ -2735,7 +2711,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
save_selected_entry(&config, entry);
/* Optionally, read a random seed off the ESP and pass it to the OS */
- (void) process_random_seed(root_dir, config.random_seed_mode);
+ (void) process_random_seed(root_dir);
err = image_start(image, entry);
if (err != EFI_SUCCESS)
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index 471398fbf1..4fc56d9356 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -140,9 +140,6 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
validate_sha256();
- if (mode == RANDOM_SEED_OFF)
- return EFI_NOT_FOUND;
-
/* hash = LABEL || sizeof(input1) || input1 || ... || sizeof(inputN) || inputN */
sha256_init_ctx(&hash);
@@ -193,7 +190,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
* system, even when disk images are duplicated or swapped out. */
size = 0;
err = acquire_system_token(&system_token, &size);
- if (mode != RANDOM_SEED_ALWAYS && (err != EFI_SUCCESS || size < DESIRED_SEED_SIZE) && !seeded_by_efi)
+ if ((err != EFI_SUCCESS || size < DESIRED_SEED_SIZE) && !seeded_by_efi)
return err;
sha256_process_bytes(&size, sizeof(size), &hash);
if (system_token) {
diff --git a/src/boot/efi/random-seed.h b/src/boot/efi/random-seed.h
index 6aa1cc5288..40aaf85860 100644
--- a/src/boot/efi/random-seed.h
+++ b/src/boot/efi/random-seed.h
@@ -2,21 +2,5 @@
#pragma once
#include <efi.h>
-#include <errno.h>
-#include <uchar.h>
-typedef enum RandomSeedMode {
- RANDOM_SEED_OFF,
- RANDOM_SEED_WITH_SYSTEM_TOKEN,
- RANDOM_SEED_ALWAYS,
- _RANDOM_SEED_MODE_MAX,
- _RANDOM_SEED_MODE_INVALID = -EINVAL,
-} RandomSeedMode;
-
-static const char16_t * const random_seed_modes_table[_RANDOM_SEED_MODE_MAX] = {
- [RANDOM_SEED_OFF] = L"off",
- [RANDOM_SEED_WITH_SYSTEM_TOKEN] = L"with-system-token",
- [RANDOM_SEED_ALWAYS] = L"always",
-};
-
-EFI_STATUS process_random_seed(EFI_FILE *root_dir, RandomSeedMode mode);
+EFI_STATUS process_random_seed(EFI_FILE *root_dir);
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index 9352416af5..61e20c40a8 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -418,7 +418,6 @@ void boot_config_free(BootConfig *config) {
free(config->auto_entries);
free(config->auto_firmware);
free(config->console_mode);
- free(config->random_seed_mode);
free(config->beep);
free(config->entry_oneshot);
@@ -485,7 +484,7 @@ int boot_loader_read_conf(BootConfig *config, FILE *file, const char *path) {
else if (streq(field, "console-mode"))
r = free_and_strdup(&config->console_mode, p);
else if (streq(field, "random-seed-mode"))
- r = free_and_strdup(&config->random_seed_mode, p);
+ log_syntax(NULL, LOG_WARNING, path, line, 0, "'random-seed-mode' has been deprecated, ignoring.");
else if (streq(field, "beep"))
r = free_and_strdup(&config->beep, p);
else {
diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h
index 7f5d496b95..ac4d1890b0 100644
--- a/src/shared/bootspec.h
+++ b/src/shared/bootspec.h
@@ -57,7 +57,6 @@ typedef struct BootConfig {
char *auto_entries;
char *auto_firmware;
char *console_mode;
- char *random_seed_mode;
char *beep;
char *entry_oneshot;

View File

@ -0,0 +1,302 @@
From 41f56ba9788c24ef66a5ca99ee1a92af697670ec Mon Sep 17 00:00:00 2001
From: "Jason A. Donenfeld" <Jason@zx2c4.com>
Date: Fri, 18 Nov 2022 02:49:16 +0100
Subject: [PATCH] stub: handle random seed like sd-boot does
sd-stub has an opportunity to handle the seed the same way sd-boot does,
which would have benefits for UKIs when sd-boot is not in use. This
commit wires that up.
It refactors the XBOOTLDR partition discovery to also find the ESP
partition, so that it access the random seed there.
(cherry picked from commit 0a1d8ac77a21ae0741bdf4af08f3a71354805ff1)
Related: RHEL-16952
---
src/boot/bootctl.c | 1 +
src/boot/efi/boot.c | 4 ++--
src/boot/efi/meson.build | 8 ++++----
src/boot/efi/{xbootldr.c => part-discovery.c} | 20 ++++++++++---------
src/boot/efi/part-discovery.h | 11 ++++++++++
src/boot/efi/stub.c | 13 ++++++++++++
src/boot/efi/xbootldr.h | 9 ---------
src/fundamental/efivars-fundamental.h | 1 +
units/systemd-boot-system-token.service | 3 ++-
9 files changed, 45 insertions(+), 25 deletions(-)
rename src/boot/efi/{xbootldr.c => part-discovery.c} (94%)
create mode 100644 src/boot/efi/part-discovery.h
delete mode 100644 src/boot/efi/xbootldr.h
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index d495c72bdd..c994be272b 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -1812,6 +1812,7 @@ static int verb_status(int argc, char *argv[], void *userdata) {
{ EFI_STUB_FEATURE_PICK_UP_CREDENTIALS, "Picks up credentials from boot partition" },
{ EFI_STUB_FEATURE_PICK_UP_SYSEXTS, "Picks up system extension images from boot partition" },
{ EFI_STUB_FEATURE_THREE_PCRS, "Measures kernel+command line+sysexts" },
+ { EFI_STUB_FEATURE_RANDOM_SEED, "Support for passing random seed to OS" },
};
_cleanup_free_ char *fw_type = NULL, *fw_info = NULL, *loader = NULL, *loader_path = NULL, *stub = NULL;
sd_id128_t loader_part_uuid = SD_ID128_NULL;
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 4a64c2402d..2e657a8bf9 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -15,6 +15,7 @@
#include "initrd.h"
#include "linux.h"
#include "measure.h"
+#include "part-discovery.h"
#include "pe.h"
#include "vmm.h"
#include "random-seed.h"
@@ -22,7 +23,6 @@
#include "shim.h"
#include "ticks.h"
#include "util.h"
-#include "xbootldr.h"
#ifndef GNU_EFI_USE_MS_ABI
/* We do not use uefi_call_wrapper() in systemd-boot. As such, we rely on the
@@ -2239,7 +2239,7 @@ static void config_load_xbootldr(
assert(config);
assert(device);
- err = xbootldr_open(device, &new_device, &root_dir);
+ err = partition_open(XBOOTLDR_GUID, device, &new_device, &root_dir);
if (err != EFI_SUCCESS)
return;
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index 00f3361d66..8e96a33119 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -361,6 +361,7 @@ efi_headers = files(
'linux.h',
'measure.h',
'missing_efi.h',
+ 'part-discovery.h',
'pe.h',
'random-seed.h',
'secure-boot.h',
@@ -368,7 +369,6 @@ efi_headers = files(
'splash.h',
'ticks.h',
'util.h',
- 'xbootldr.h',
)
common_sources = files(
@@ -380,7 +380,9 @@ common_sources = files(
'graphics.c',
'initrd.c',
'measure.c',
+ 'part-discovery.c',
'pe.c',
+ 'random-seed.c',
'secure-boot.c',
'ticks.c',
'util.c',
@@ -389,10 +391,8 @@ common_sources = files(
systemd_boot_sources = files(
'boot.c',
'drivers.c',
- 'random-seed.c',
- 'vmm.c',
'shim.c',
- 'xbootldr.c',
+ 'vmm.c',
)
stub_sources = files(
diff --git a/src/boot/efi/xbootldr.c b/src/boot/efi/part-discovery.c
similarity index 94%
rename from src/boot/efi/xbootldr.c
rename to src/boot/efi/part-discovery.c
index 7fef909312..14479c06ea 100644
--- a/src/boot/efi/xbootldr.c
+++ b/src/boot/efi/part-discovery.c
@@ -4,8 +4,8 @@
#include <efigpt.h>
#include <efilib.h>
+#include "part-discovery.h"
#include "util.h"
-#include "xbootldr.h"
union GptHeaderBuffer {
EFI_PARTITION_TABLE_HEADER gpt_header;
@@ -81,6 +81,7 @@ static bool verify_gpt(union GptHeaderBuffer *gpt_header_buffer, EFI_LBA lba_exp
}
static EFI_STATUS try_gpt(
+ const EFI_GUID *type,
EFI_BLOCK_IO_PROTOCOL *block_io,
EFI_LBA lba,
EFI_LBA *ret_backup_lba, /* May be changed even on error! */
@@ -133,7 +134,7 @@ static EFI_STATUS try_gpt(
EFI_PARTITION_ENTRY *entry =
(EFI_PARTITION_ENTRY *) ((uint8_t *) entries + gpt.gpt_header.SizeOfPartitionEntry * i);
- if (memcmp(&entry->PartitionTypeGUID, XBOOTLDR_GUID, sizeof(entry->PartitionTypeGUID)) != 0)
+ if (memcmp(&entry->PartitionTypeGUID, type, sizeof(entry->PartitionTypeGUID)) != 0)
continue;
if (entry->EndingLBA < entry->StartingLBA) /* Bogus? */
@@ -165,7 +166,7 @@ static EFI_STATUS try_gpt(
return EFI_NOT_FOUND;
}
-static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_path) {
+static EFI_STATUS find_device(const EFI_GUID *type, EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_path) {
EFI_STATUS err;
assert(device);
@@ -235,8 +236,7 @@ static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_p
continue;
HARDDRIVE_DEVICE_PATH hd;
- err = try_gpt(
- block_io, lba,
+ err = try_gpt(type, block_io, lba,
nr == 0 ? &backup_lba : NULL, /* Only get backup LBA location from first GPT header. */
&hd);
if (err != EFI_SUCCESS) {
@@ -256,17 +256,18 @@ static EFI_STATUS find_device(EFI_HANDLE *device, EFI_DEVICE_PATH **ret_device_p
return EFI_NOT_FOUND;
}
-EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir) {
+EFI_STATUS partition_open(const EFI_GUID *type, EFI_HANDLE *device, EFI_HANDLE *ret_device,
+ EFI_FILE **ret_root_dir) {
_cleanup_free_ EFI_DEVICE_PATH *partition_path = NULL;
EFI_HANDLE new_device;
EFI_FILE *root_dir;
EFI_STATUS err;
+ assert(type);
assert(device);
- assert(ret_device);
assert(ret_root_dir);
- err = find_device(device, &partition_path);
+ err = find_device(type, device, &partition_path);
if (err != EFI_SUCCESS)
return err;
@@ -279,7 +280,8 @@ EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **
if (err != EFI_SUCCESS)
return err;
- *ret_device = new_device;
+ if (ret_device)
+ *ret_device = new_device;
*ret_root_dir = root_dir;
return EFI_SUCCESS;
}
diff --git a/src/boot/efi/part-discovery.h b/src/boot/efi/part-discovery.h
new file mode 100644
index 0000000000..5cc17f6b3b
--- /dev/null
+++ b/src/boot/efi/part-discovery.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <efi.h>
+
+#define XBOOTLDR_GUID \
+ &(const EFI_GUID) { 0xbc13c2ff, 0x59e6, 0x4262, { 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 } }
+#define ESP_GUID \
+ &(const EFI_GUID) { 0xc12a7328, 0xf81f, 0x11d2, { 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } }
+
+EFI_STATUS partition_open(const EFI_GUID *type, EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir);
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 7c42a16c70..023f8ae255 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -9,7 +9,9 @@
#include "graphics.h"
#include "linux.h"
#include "measure.h"
+#include "part-discovery.h"
#include "pe.h"
+#include "random-seed.h"
#include "secure-boot.h"
#include "splash.h"
#include "tpm-pcr.h"
@@ -84,6 +86,7 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
EFI_STUB_FEATURE_PICK_UP_CREDENTIALS | /* We pick up credentials from the boot partition */
EFI_STUB_FEATURE_PICK_UP_SYSEXTS | /* We pick up system extensions from the boot partition */
EFI_STUB_FEATURE_THREE_PCRS | /* We can measure kernel image, parameters and sysext */
+ EFI_STUB_FEATURE_RANDOM_SEED | /* We pass a random seed to the kernel */
0;
char16_t uuid[37];
@@ -188,6 +191,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
_cleanup_free_ char16_t *cmdline = NULL;
int sections_measured = -1, parameters_measured = -1;
bool sysext_measured = false, m;
+ uint64_t loader_features = 0;
EFI_STATUS err;
InitializeLib(image, sys_table);
@@ -205,6 +209,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
if (err != EFI_SUCCESS)
return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
+ if (efivar_get_uint64_le(LOADER_GUID, L"LoaderFeatures", &loader_features) != EFI_SUCCESS ||
+ !FLAGS_SET(loader_features, EFI_LOADER_FEATURE_RANDOM_SEED)) {
+ _cleanup_(file_closep) EFI_FILE *esp_dir = NULL;
+
+ err = partition_open(ESP_GUID, loaded_image->DeviceHandle, NULL, &esp_dir);
+ if (err == EFI_SUCCESS) /* Non-fatal on failure, so that we still boot without it. */
+ (void) process_random_seed(esp_dir);
+ }
+
err = pe_memory_locate_sections(loaded_image->ImageBase, unified_sections, addrs, szs);
if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) {
if (err == EFI_SUCCESS)
diff --git a/src/boot/efi/xbootldr.h b/src/boot/efi/xbootldr.h
deleted file mode 100644
index 205ce71edf..0000000000
--- a/src/boot/efi/xbootldr.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#pragma once
-
-#include <efi.h>
-
-#define XBOOTLDR_GUID \
- &(const EFI_GUID) { 0xbc13c2ff, 0x59e6, 0x4262, { 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 } }
-
-EFI_STATUS xbootldr_open(EFI_HANDLE *device, EFI_HANDLE *ret_device, EFI_FILE **ret_root_dir);
diff --git a/src/fundamental/efivars-fundamental.h b/src/fundamental/efivars-fundamental.h
index fe34e6c714..cf785f8b7d 100644
--- a/src/fundamental/efivars-fundamental.h
+++ b/src/fundamental/efivars-fundamental.h
@@ -22,6 +22,7 @@
#define EFI_STUB_FEATURE_PICK_UP_CREDENTIALS (UINT64_C(1) << 1)
#define EFI_STUB_FEATURE_PICK_UP_SYSEXTS (UINT64_C(1) << 2)
#define EFI_STUB_FEATURE_THREE_PCRS (UINT64_C(1) << 3)
+#define EFI_STUB_FEATURE_RANDOM_SEED (UINT64_C(1) << 4)
typedef enum SecureBootMode {
SECURE_BOOT_UNSUPPORTED,
diff --git a/units/systemd-boot-system-token.service b/units/systemd-boot-system-token.service
index 689b902000..63e523bb3e 100644
--- a/units/systemd-boot-system-token.service
+++ b/units/systemd-boot-system-token.service
@@ -17,7 +17,8 @@ Conflicts=shutdown.target initrd-switch-root.target
Before=shutdown.target initrd-switch-root.target
# Only run this if the boot loader can support random seed initialization.
-ConditionPathExists=/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+ConditionPathExists|=/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+ConditionPathExists|=/sys/firmware/efi/efivars/StubFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
# Only run this if there is no system token defined yet
ConditionPathExists=!/sys/firmware/efi/efivars/LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f

View File

@ -0,0 +1,90 @@
From a30f647ef87e352502dde5ca67ad99927611108a Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Fri, 11 Nov 2022 16:05:03 +0100
Subject: [PATCH] efi: add efi_guid_equal() helper
(cherry picked from commit 50b0b0d351e892d57a562a28dd8362b1e8cd76a9)
Related: RHEL-16952
---
src/boot/efi/devicetree.c | 2 +-
src/boot/efi/part-discovery.c | 2 +-
src/boot/efi/random-seed.c | 3 +--
src/boot/efi/util.h | 4 ++++
src/boot/efi/vmm.c | 4 ++--
5 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/src/boot/efi/devicetree.c b/src/boot/efi/devicetree.c
index 0312670613..daba5582aa 100644
--- a/src/boot/efi/devicetree.c
+++ b/src/boot/efi/devicetree.c
@@ -10,7 +10,7 @@
static void *get_dtb_table(void) {
for (UINTN i = 0; i < ST->NumberOfTableEntries; i++)
- if (memcmp(&EfiDtbTableGuid, &ST->ConfigurationTable[i].VendorGuid, sizeof(EfiDtbTableGuid)) == 0)
+ if (efi_guid_equal(&ST->ConfigurationTable[i].VendorGuid, &EfiDtbTableGuid))
return ST->ConfigurationTable[i].VendorTable;
return NULL;
}
diff --git a/src/boot/efi/part-discovery.c b/src/boot/efi/part-discovery.c
index 14479c06ea..2659a5b6b4 100644
--- a/src/boot/efi/part-discovery.c
+++ b/src/boot/efi/part-discovery.c
@@ -134,7 +134,7 @@ static EFI_STATUS try_gpt(
EFI_PARTITION_ENTRY *entry =
(EFI_PARTITION_ENTRY *) ((uint8_t *) entries + gpt.gpt_header.SizeOfPartitionEntry * i);
- if (memcmp(&entry->PartitionTypeGUID, type, sizeof(entry->PartitionTypeGUID)) != 0)
+ if (!efi_guid_equal(&entry->PartitionTypeGUID, type))
continue;
if (entry->EndingLBA < entry->StartingLBA) /* Bogus? */
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index 4fc56d9356..6070145778 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -147,8 +147,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
sha256_process_bytes(HASH_LABEL, sizeof(HASH_LABEL) - 1, &hash);
for (size_t i = 0; i < ST->NumberOfTableEntries; ++i)
- if (memcmp(&(const EFI_GUID)LINUX_EFI_RANDOM_SEED_TABLE_GUID,
- &ST->ConfigurationTable[i].VendorGuid, sizeof(EFI_GUID)) == 0) {
+ if (efi_guid_equal(&ST->ConfigurationTable[i].VendorGuid, &(const EFI_GUID) LINUX_EFI_RANDOM_SEED_TABLE_GUID)) {
previous_seed_table = ST->ConfigurationTable[i].VendorTable;
break;
}
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index f58d24fce1..a076f48dba 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -217,3 +217,7 @@ static inline bool in_hypervisor(void) {
return false;
}
#endif
+
+static inline bool efi_guid_equal(const EFI_GUID *a, const EFI_GUID *b) {
+ return memcmp(a, b, sizeof(EFI_GUID)) == 0;
+}
diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c
index 2260b217b7..10d4a75ab2 100644
--- a/src/boot/efi/vmm.c
+++ b/src/boot/efi/vmm.c
@@ -18,7 +18,7 @@
/* detect direct boot */
bool is_direct_boot(EFI_HANDLE device) {
EFI_STATUS err;
- VENDOR_DEVICE_PATH *dp;
+ VENDOR_DEVICE_PATH *dp; /* NB: Alignment of this structure might be quirky! */
err = BS->HandleProtocol(device, &DevicePathProtocol, (void **) &dp);
if (err != EFI_SUCCESS)
@@ -27,7 +27,7 @@ bool is_direct_boot(EFI_HANDLE device) {
/* 'qemu -kernel systemd-bootx64.efi' */
if (dp->Header.Type == MEDIA_DEVICE_PATH &&
dp->Header.SubType == MEDIA_VENDOR_DP &&
- memcmp(&dp->Guid, &(EFI_GUID)QEMU_KERNEL_LOADER_FS_MEDIA_GUID, sizeof(EFI_GUID)) == 0)
+ memcmp(&dp->Guid, &(EFI_GUID)QEMU_KERNEL_LOADER_FS_MEDIA_GUID, sizeof(EFI_GUID)) == 0) /* Don't change to efi_guid_equal() because EFI device path objects are not necessarily aligned! */
return true;
/* loaded from firmware volume (sd-boot added to ovmf) */

View File

@ -0,0 +1,95 @@
From bbf8235eb297047521ac83b594389d70ecfb38df Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 14 Dec 2022 18:48:52 +0100
Subject: [PATCH] efi: add common implementation for loop finding EFI
configuration tables
(cherry picked from commit a04709c1ac81b28b1a4144166991ac56be94cfcd)
Related: RHEL-16952
---
src/boot/efi/devicetree.c | 11 ++---------
src/boot/efi/random-seed.c | 6 +-----
src/boot/efi/util.c | 8 ++++++++
src/boot/efi/util.h | 2 ++
4 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/src/boot/efi/devicetree.c b/src/boot/efi/devicetree.c
index daba5582aa..12015fce6b 100644
--- a/src/boot/efi/devicetree.c
+++ b/src/boot/efi/devicetree.c
@@ -8,13 +8,6 @@
#define FDT_V1_SIZE (7*4)
-static void *get_dtb_table(void) {
- for (UINTN i = 0; i < ST->NumberOfTableEntries; i++)
- if (efi_guid_equal(&ST->ConfigurationTable[i].VendorGuid, &EfiDtbTableGuid))
- return ST->ConfigurationTable[i].VendorTable;
- return NULL;
-}
-
static EFI_STATUS devicetree_allocate(struct devicetree_state *state, UINTN size) {
UINTN pages = DIV_ROUND_UP(size, EFI_PAGE_SIZE);
EFI_STATUS err;
@@ -81,7 +74,7 @@ EFI_STATUS devicetree_install(struct devicetree_state *state, EFI_FILE *root_dir
assert(root_dir);
assert(name);
- state->orig = get_dtb_table();
+ state->orig = find_configuration_table(&EfiDtbTableGuid);
if (!state->orig)
return EFI_UNSUPPORTED;
@@ -121,7 +114,7 @@ EFI_STATUS devicetree_install_from_memory(struct devicetree_state *state,
assert(state);
assert(dtb_buffer && dtb_length > 0);
- state->orig = get_dtb_table();
+ state->orig = find_configuration_table(&EfiDtbTableGuid);
if (!state->orig)
return EFI_UNSUPPORTED;
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index 6070145778..332f537d91 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -146,11 +146,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
/* Some basic domain separation in case somebody uses this data elsewhere */
sha256_process_bytes(HASH_LABEL, sizeof(HASH_LABEL) - 1, &hash);
- for (size_t i = 0; i < ST->NumberOfTableEntries; ++i)
- if (efi_guid_equal(&ST->ConfigurationTable[i].VendorGuid, &(const EFI_GUID) LINUX_EFI_RANDOM_SEED_TABLE_GUID)) {
- previous_seed_table = ST->ConfigurationTable[i].VendorTable;
- break;
- }
+ previous_seed_table = find_configuration_table(&(const EFI_GUID) LINUX_EFI_RANDOM_SEED_TABLE_GUID);
if (!previous_seed_table) {
size = 0;
sha256_process_bytes(&size, sizeof(size), &hash);
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index 1f07fbc38c..0a6bb59dce 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -747,3 +747,11 @@ bool in_hypervisor(void) {
return !!(ecx & 0x80000000U);
}
#endif
+
+void *find_configuration_table(const EFI_GUID *guid) {
+ for (UINTN i = 0; i < ST->NumberOfTableEntries; i++)
+ if (efi_guid_equal(&ST->ConfigurationTable[i].VendorGuid, guid))
+ return ST->ConfigurationTable[i].VendorTable;
+
+ return NULL;
+}
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index a076f48dba..88fc60c17e 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -221,3 +221,5 @@ static inline bool in_hypervisor(void) {
static inline bool efi_guid_equal(const EFI_GUID *a, const EFI_GUID *b) {
return memcmp(a, b, sizeof(EFI_GUID)) == 0;
}
+
+void *find_configuration_table(const EFI_GUID *guid);

View File

@ -0,0 +1,310 @@
From 4fad2ae3115ee9cedc61fffaf920fbd08fabc267 Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Tue, 10 Jan 2023 14:44:29 +0100
Subject: [PATCH] boot: Detect hypervisors using SMBIOS info
This allows skipping secure boot enrollment wait time on other arches.
(cherry picked from commit ba2793927461b82216f56aa8a800cf53fac28d37)
Related: RHEL-16952
---
src/boot/efi/meson.build | 4 +-
src/boot/efi/secure-boot.c | 3 +-
src/boot/efi/ticks.c | 1 +
src/boot/efi/util.c | 17 ----
src/boot/efi/util.h | 8 --
src/boot/efi/vmm.c | 156 +++++++++++++++++++++++++++++++++++++
src/boot/efi/vmm.h | 2 +
7 files changed, 163 insertions(+), 28 deletions(-)
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index 8e96a33119..ab2d7595f3 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -375,6 +375,7 @@ common_sources = files(
'assert.c',
'console.c',
'devicetree.c',
+ 'drivers.c',
'disk.c',
'efi-string.c',
'graphics.c',
@@ -386,13 +387,12 @@ common_sources = files(
'secure-boot.c',
'ticks.c',
'util.c',
+ 'vmm.c',
)
systemd_boot_sources = files(
'boot.c',
- 'drivers.c',
'shim.c',
- 'vmm.c',
)
stub_sources = files(
diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c
index 6212868134..55c9ba5d4c 100644
--- a/src/boot/efi/secure-boot.c
+++ b/src/boot/efi/secure-boot.c
@@ -1,9 +1,10 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#include "console.h"
#include "sbat.h"
#include "secure-boot.h"
-#include "console.h"
#include "util.h"
+#include "vmm.h"
bool secure_boot_enabled(void) {
bool secure = false; /* avoid false maybe-uninitialized warning */
diff --git a/src/boot/efi/ticks.c b/src/boot/efi/ticks.c
index 1b74ba15d0..2f6ff878ca 100644
--- a/src/boot/efi/ticks.c
+++ b/src/boot/efi/ticks.c
@@ -5,6 +5,7 @@
#include "ticks.h"
#include "util.h"
+#include "vmm.h"
#ifdef __x86_64__
static uint64_t ticks_read(void) {
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index 0a6bb59dce..dfe2fe791d 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -2,9 +2,6 @@
#include <efi.h>
#include <efilib.h>
-#if defined(__i386__) || defined(__x86_64__)
-# include <cpuid.h>
-#endif
#include "ticks.h"
#include "util.h"
@@ -734,20 +731,6 @@ EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret) {
return EFI_SUCCESS;
}
-#if defined(__i386__) || defined(__x86_64__)
-bool in_hypervisor(void) {
- uint32_t eax, ebx, ecx, edx;
-
- /* This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
- * environment. */
-
- if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
- return false;
-
- return !!(ecx & 0x80000000U);
-}
-#endif
-
void *find_configuration_table(const EFI_GUID *guid) {
for (UINTN i = 0; i < ST->NumberOfTableEntries; i++)
if (efi_guid_equal(&ST->ConfigurationTable[i].VendorGuid, guid))
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index 88fc60c17e..d688f392fc 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -210,14 +210,6 @@ EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file);
EFI_STATUS make_file_device_path(EFI_HANDLE device, const char16_t *file, EFI_DEVICE_PATH **ret_dp);
EFI_STATUS device_path_to_str(const EFI_DEVICE_PATH *dp, char16_t **ret);
-#if defined(__i386__) || defined(__x86_64__)
-bool in_hypervisor(void);
-#else
-static inline bool in_hypervisor(void) {
- return false;
-}
-#endif
-
static inline bool efi_guid_equal(const EFI_GUID *a, const EFI_GUID *b) {
return memcmp(a, b, sizeof(EFI_GUID)) == 0;
}
diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c
index 10d4a75ab2..3dfa92b58d 100644
--- a/src/boot/efi/vmm.c
+++ b/src/boot/efi/vmm.c
@@ -3,6 +3,9 @@
#include <efi.h>
#include <efilib.h>
#include <stdbool.h>
+#if defined(__i386__) || defined(__x86_64__)
+# include <cpuid.h>
+#endif
#include "drivers.h"
#include "efi-string.h"
@@ -132,3 +135,156 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
}
assert_not_reached();
}
+
+static bool cpuid_in_hypervisor(void) {
+#if defined(__i386__) || defined(__x86_64__)
+ unsigned eax, ebx, ecx, edx;
+
+ /* This is a dumbed down version of src/basic/virt.c's detect_vm() that safely works in the UEFI
+ * environment. */
+
+ if (__get_cpuid(1, &eax, &ebx, &ecx, &edx) == 0)
+ return false;
+
+ if (FLAGS_SET(ecx, 0x80000000U))
+ return true;
+#endif
+
+ return false;
+}
+
+typedef struct {
+ uint8_t anchor_string[4];
+ uint8_t entry_point_structure_checksum;
+ uint8_t entry_point_length;
+ uint8_t major_version;
+ uint8_t minor_version;
+ uint16_t max_structure_size;
+ uint8_t entry_point_revision;
+ uint8_t formatted_area[5];
+ uint8_t intermediate_anchor_string[5];
+ uint8_t intermediate_checksum;
+ uint16_t table_length;
+ uint32_t table_address;
+ uint16_t number_of_smbios_structures;
+ uint8_t smbios_bcd_revision;
+} _packed_ SmbiosEntryPoint;
+
+typedef struct {
+ uint8_t anchor_string[5];
+ uint8_t entry_point_structure_checksum;
+ uint8_t entry_point_length;
+ uint8_t major_version;
+ uint8_t minor_version;
+ uint8_t docrev;
+ uint8_t entry_point_revision;
+ uint8_t reserved;
+ uint32_t table_maximum_size;
+ uint64_t table_address;
+} _packed_ Smbios3EntryPoint;
+
+typedef struct {
+ uint8_t type;
+ uint8_t length;
+ uint8_t handle[2];
+} _packed_ SmbiosHeader;
+
+typedef struct {
+ SmbiosHeader header;
+ uint8_t vendor;
+ uint8_t bios_version;
+ uint16_t bios_segment;
+ uint8_t bios_release_date;
+ uint8_t bios_size;
+ uint64_t bios_characteristics;
+ uint8_t bios_characteristics_ext[2];
+} _packed_ SmbiosTableType0;
+
+static void *find_smbios_configuration_table(uint64_t *ret_size) {
+ assert(ret_size);
+
+ Smbios3EntryPoint *entry3 = find_configuration_table(&(EFI_GUID) SMBIOS3_TABLE_GUID);
+ if (entry3 && memcmp(entry3->anchor_string, "_SM3_", 5) == 0 &&
+ entry3->entry_point_length <= sizeof(*entry3)) {
+ *ret_size = entry3->table_maximum_size;
+ return PHYSICAL_ADDRESS_TO_POINTER(entry3->table_address);
+ }
+
+ SmbiosEntryPoint *entry = find_configuration_table(&(EFI_GUID) SMBIOS_TABLE_GUID);
+ if (entry && memcmp(entry->anchor_string, "_SM_", 4) == 0 &&
+ entry->entry_point_length <= sizeof(*entry)) {
+ *ret_size = entry->table_length;
+ return PHYSICAL_ADDRESS_TO_POINTER(entry->table_address);
+ }
+
+ return NULL;
+}
+
+static SmbiosHeader *get_smbios_table(uint8_t type) {
+ uint64_t size = 0;
+ uint8_t *p = find_smbios_configuration_table(&size);
+ if (!p)
+ return false;
+
+ for (;;) {
+ if (size < sizeof(SmbiosHeader))
+ return NULL;
+
+ SmbiosHeader *header = (SmbiosHeader *) p;
+
+ /* End of table. */
+ if (header->type == 127)
+ return NULL;
+
+ if (size < header->length)
+ return NULL;
+
+ if (header->type == type)
+ return header; /* Yay! */
+
+ /* Skip over formatted area. */
+ size -= header->length;
+ p += header->length;
+
+ /* Skip over string table. */
+ for (;;) {
+ while (size > 0 && *p != '\0') {
+ p++;
+ size--;
+ }
+ if (size == 0)
+ return NULL;
+ p++;
+ size--;
+
+ /* Double NUL terminates string table. */
+ if (*p == '\0') {
+ if (size == 0)
+ return NULL;
+ p++;
+ break;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static bool smbios_in_hypervisor(void) {
+ /* Look up BIOS Information (Type 0). */
+ SmbiosTableType0 *type0 = (SmbiosTableType0 *) get_smbios_table(0);
+ if (!type0 || type0->header.length < sizeof(SmbiosTableType0))
+ return false;
+
+ /* Bit 4 of 2nd BIOS characteristics extension bytes indicates virtualization. */
+ return FLAGS_SET(type0->bios_characteristics_ext[1], 1 << 4);
+}
+
+bool in_hypervisor(void) {
+ static int cache = -1;
+ if (cache >= 0)
+ return cache;
+
+ cache = cpuid_in_hypervisor() || smbios_in_hypervisor();
+ return cache;
+}
diff --git a/src/boot/efi/vmm.h b/src/boot/efi/vmm.h
index 7bac1a324a..e98ec74af1 100644
--- a/src/boot/efi/vmm.h
+++ b/src/boot/efi/vmm.h
@@ -6,3 +6,5 @@
bool is_direct_boot(EFI_HANDLE device);
EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir);
+
+bool in_hypervisor(void);

View File

@ -0,0 +1,83 @@
From 72089d78ba9e0c0d4c4b83c8f447adbfb32809ed Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Mon, 16 Jan 2023 16:22:17 +0100
Subject: [PATCH] boot: Skip soft-brick warning when in a VM
This part of the warning is annoying to look at not really true when
running inside of a VM.
(cherry picked from commit 3e87a057a796b57bf9540b948823fbefef6693d7)
Related: RHEL-16952
---
src/boot/efi/secure-boot.c | 56 ++++++++++++++++++++------------------
1 file changed, 29 insertions(+), 27 deletions(-)
diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c
index 55c9ba5d4c..3f3a222b5e 100644
--- a/src/boot/efi/secure-boot.c
+++ b/src/boot/efi/secure-boot.c
@@ -44,34 +44,36 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
clear_screen(COLOR_NORMAL);
- Print(L"Enrolling secure boot keys from directory: %s\n"
- L"Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n",
- path);
-
- unsigned timeout_sec = 15;
- for(;;) {
- /* Enrolling secure boot keys is safe to do in virtualized environments as there is nothing
- * we can brick there. */
- if (in_hypervisor())
- break;
-
- PrintAt(0, ST->ConOut->Mode->CursorRow, L"Enrolling in %2u s, press any key to abort.", timeout_sec);
-
- uint64_t key;
- err = console_key_read(&key, 1000 * 1000);
- if (err == EFI_NOT_READY)
- continue;
- if (err == EFI_TIMEOUT) {
- if (timeout_sec == 0) /* continue enrolling keys */
- break;
- timeout_sec--;
- continue;
+ Print(u"Enrolling secure boot keys from directory: %s\n");
+
+ /* Enrolling secure boot keys is safe to do in virtualized environments as there is nothing
+ * we can brick there. */
+ if (!in_hypervisor()) {
+ Print(u"Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n", path);
+
+ unsigned timeout_sec = 15;
+ for (;;) {
+ Print(u"\rEnrolling in %2u s, press any key to abort.", timeout_sec);
+
+ uint64_t key;
+ err = console_key_read(&key, 1000 * 1000);
+ if (err == EFI_NOT_READY)
+ continue;
+ if (err == EFI_TIMEOUT) {
+ if (timeout_sec == 0) /* continue enrolling keys */
+ break;
+ timeout_sec--;
+ continue;
+ }
+ if (err != EFI_SUCCESS)
+ return log_error_status_stall(
+ err,
+ L"Error waiting for user input to enroll Secure Boot keys: %r",
+ err);
+
+ /* user aborted, returning EFI_SUCCESS here allows the user to go back to the menu */
+ return EFI_SUCCESS;
}
- if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error waiting for user input to enroll Secure Boot keys: %r", err);
-
- /* user aborted, returning EFI_SUCCESS here allows the user to go back to the menu */
- return EFI_SUCCESS;
}
_cleanup_(file_closep) EFI_FILE *dir = NULL;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
From a8a487d886f701b1a1e8991074d84ddacf41fcd0 Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Wed, 25 Jan 2023 15:23:49 +0100
Subject: [PATCH] boot: Use unsigned for beep counting
(cherry picked from commit 54d9ecc380360e925ef5ca7886c8546424ddf4fe)
Related: RHEL-16952
---
src/boot/efi/util.c | 2 +-
src/boot/efi/util.h | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index 2be18936b0..7596bc3edc 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -587,7 +587,7 @@ static inline void outb(uint16_t port, uint8_t value) {
asm volatile("outb %0, %1" : : "a"(value), "Nd"(port));
}
-void beep(UINTN beep_count) {
+void beep(unsigned beep_count) {
enum {
PITCH = 500,
BEEP_DURATION_USEC = 100 * 1000,
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index 33472a5b6a..b97dc9768c 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -201,9 +201,9 @@ void hexdump(const char16_t *prefix, const void *data, UINTN size);
#endif
#if defined(__i386__) || defined(__x86_64__)
-void beep(UINTN beep_count);
+void beep(unsigned beep_count);
#else
-static inline void beep(UINTN beep_count) {}
+static inline void beep(unsigned beep_count) {}
#endif
EFI_STATUS open_volume(EFI_HANDLE device, EFI_FILE **ret_file);

View File

@ -0,0 +1,316 @@
From 4280dc5179071758294e661600b37e3d5c7658c9 Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Fri, 9 Dec 2022 11:15:41 +0100
Subject: [PATCH] boot: Use unicode literals
No changes in behavior.
(cherry picked from commit a083aed03fc4a2bf64b2c41df508a47e3ceba91c)
Related: RHEL-16952
---
src/boot/efi/boot.c | 54 +++++++++++++++++++-------------------
src/boot/efi/drivers.c | 4 +--
src/boot/efi/random-seed.c | 2 +-
src/boot/efi/stub.c | 18 ++++++-------
4 files changed, 39 insertions(+), 39 deletions(-)
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 385cfd563a..a39c356158 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -427,7 +427,7 @@ static bool unicode_supported(void) {
if (cache < 0)
/* Basic unicode box drawing support is mandated by the spec, but it does
* not hurt to make sure it works. */
- cache = ST->ConOut->TestString(ST->ConOut, (char16_t *) L"─") == EFI_SUCCESS;
+ cache = ST->ConOut->TestString(ST->ConOut, (char16_t *) u"─") == EFI_SUCCESS;
return cache;
}
@@ -728,7 +728,7 @@ static bool menu_run(
print_at(x_start,
y_start + i - idx_first,
i == idx_highlight ? COLOR_HIGHLIGHT : COLOR_ENTRY,
- unicode_supported() ? L" ►" : L"=>");
+ unicode_supported() ? u" ►" : u"=>");
}
refresh = false;
} else if (highlight) {
@@ -738,12 +738,12 @@ static bool menu_run(
print_at(x_start,
y_start + idx_highlight_prev - idx_first,
COLOR_ENTRY,
- unicode_supported() ? L" ►" : L"=>");
+ unicode_supported() ? u" ►" : u"=>");
if (idx_highlight == config->idx_default_efivar)
print_at(x_start,
y_start + idx_highlight - idx_first,
COLOR_HIGHLIGHT,
- unicode_supported() ? L" ►" : L"=>");
+ unicode_supported() ? u" ►" : u"=>");
highlight = false;
}
@@ -1504,7 +1504,7 @@ static void config_entry_add_type1(
config_add_entry(config, entry);
- config_entry_parse_tries(entry, path, file, L".conf");
+ config_entry_parse_tries(entry, path, file, u".conf");
TAKE_PTR(entry);
}
@@ -1556,7 +1556,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
.timeout_sec_efivar = TIMEOUT_UNSET,
};
- err = file_read(root_dir, L"\\loader\\loader.conf", 0, 0, &content, NULL);
+ err = file_read(root_dir, u"\\loader\\loader.conf", 0, 0, &content, NULL);
if (err == EFI_SUCCESS)
config_defaults_load_from_file(config, content);
@@ -1591,8 +1591,8 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
strtolower16(config->entry_oneshot);
strtolower16(config->entry_saved);
- config->use_saved_entry = streq16(config->entry_default_config, L"@saved");
- config->use_saved_entry_efivar = streq16(config->entry_default_efivar, L"@saved");
+ config->use_saved_entry = streq16(config->entry_default_config, u"@saved");
+ config->use_saved_entry_efivar = streq16(config->entry_default_efivar, u"@saved");
if (config->use_saved_entry || config->use_saved_entry_efivar)
(void) efivar_get(LOADER_GUID, L"LoaderEntryLastBooted", &config->entry_saved);
}
@@ -1614,7 +1614,7 @@ static void config_load_entries(
/* Adds Boot Loader Type #1 entries (i.e. /loader/entries/….conf) */
- err = open_directory(root_dir, L"\\loader\\entries", &entries_dir);
+ err = open_directory(root_dir, u"\\loader\\entries", &entries_dir);
if (err != EFI_SUCCESS)
return;
@@ -1630,14 +1630,14 @@ static void config_load_entries(
if (FLAGS_SET(f->Attribute, EFI_FILE_DIRECTORY))
continue;
- if (!endswith_no_case(f->FileName, L".conf"))
+ if (!endswith_no_case(f->FileName, u".conf"))
continue;
- if (startswith(f->FileName, L"auto-"))
+ if (startswith(f->FileName, u"auto-"))
continue;
err = file_read(entries_dir, f->FileName, 0, 0, &content, NULL);
if (err == EFI_SUCCESS)
- config_entry_add_type1(config, device, root_dir, L"\\loader\\entries", f->FileName, content, loaded_image_path);
+ config_entry_add_type1(config, device, root_dir, u"\\loader\\entries", f->FileName, content, loaded_image_path);
}
}
@@ -1876,7 +1876,7 @@ static ConfigEntry *config_entry_add_loader_auto(
return NULL;
if (!loader) {
- loader = L"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi";
+ loader = u"\\EFI\\BOOT\\BOOT" EFI_MACHINE_TYPE_NAME ".efi";
/* We are trying to add the default EFI loader here,
* but we do not want to do that if that would be us.
@@ -1885,7 +1885,7 @@ static ConfigEntry *config_entry_add_loader_auto(
* chainload GRUBX64.EFI in that case, which might be us.*/
if (strcaseeq16(loader, loaded_image_path) ||
is_sd_boot(root_dir, loader) ||
- is_sd_boot(root_dir, L"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME L".EFI"))
+ is_sd_boot(root_dir, u"\\EFI\\BOOT\\GRUB" EFI_MACHINE_TYPE_NAME u".EFI"))
return NULL;
}
@@ -1936,10 +1936,10 @@ static void config_entry_add_osx(Config *config) {
handles[i],
root,
NULL,
- L"auto-osx",
+ u"auto-osx",
'a',
- L"macOS",
- L"\\System\\Library\\CoreServices\\boot.efi"))
+ u"macOS",
+ u"\\System\\Library\\CoreServices\\boot.efi"))
break;
}
}
@@ -2008,7 +2008,7 @@ static EFI_STATUS boot_windows_bitlocker(void) {
if (buf_size < offset + sizeof(char16_t))
continue;
- if (streq16((char16_t *) (buf + offset), L"Windows Boot Manager")) {
+ if (streq16((char16_t *) (buf + offset), u"Windows Boot Manager")) {
err = efivar_set_raw(
EFI_GLOBAL_GUID,
L"BootNext",
@@ -2040,13 +2040,13 @@ static void config_entry_add_windows(Config *config, EFI_HANDLE *device, EFI_FIL
return;
/* Try to find a better title. */
- err = file_read(root_dir, L"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd, &len);
+ err = file_read(root_dir, u"\\EFI\\Microsoft\\Boot\\BCD", 0, 100*1024, &bcd, &len);
if (err == EFI_SUCCESS)
title = get_bcd_title((uint8_t *) bcd, len);
ConfigEntry *e = config_entry_add_loader_auto(config, device, root_dir, NULL,
- L"auto-windows", 'w', title ?: L"Windows Boot Manager",
- L"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
+ u"auto-windows", 'w', title ?: u"Windows Boot Manager",
+ u"\\EFI\\Microsoft\\Boot\\bootmgfw.efi");
if (config->reboot_for_bitlocker)
e->call = boot_windows_bitlocker;
@@ -2069,7 +2069,7 @@ static void config_entry_add_unified(
assert(device);
assert(root_dir);
- err = open_directory(root_dir, L"\\EFI\\Linux", &linux_dir);
+ err = open_directory(root_dir, u"\\EFI\\Linux", &linux_dir);
if (err != EFI_SUCCESS)
return;
@@ -2101,9 +2101,9 @@ static void config_entry_add_unified(
continue;
if (FLAGS_SET(f->Attribute, EFI_FILE_DIRECTORY))
continue;
- if (!endswith_no_case(f->FileName, L".efi"))
+ if (!endswith_no_case(f->FileName, u".efi"))
continue;
- if (startswith(f->FileName, L"auto-"))
+ if (startswith(f->FileName, u"auto-"))
continue;
/* look for .osrel and .cmdline sections in the .efi binary */
@@ -2196,7 +2196,7 @@ static void config_entry_add_unified(
strtolower16(entry->id);
config_add_entry(config, entry);
- config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi");
+ config_entry_parse_tries(entry, u"\\EFI\\Linux", f->FileName, u".efi");
if (szs[SECTION_CMDLINE] == 0)
continue;
@@ -2571,9 +2571,9 @@ static void config_load_all_entries(
config_entry_add_osx(config);
config_entry_add_windows(config, loaded_image->DeviceHandle, root_dir);
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, NULL,
- L"auto-efi-shell", 's', L"EFI Shell", L"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
+ u"auto-efi-shell", 's', u"EFI Shell", u"\\shell" EFI_MACHINE_TYPE_NAME ".efi");
config_entry_add_loader_auto(config, loaded_image->DeviceHandle, root_dir, loaded_image_path,
- L"auto-efi-default", '\0', L"EFI Default Loader", NULL);
+ u"auto-efi-default", '\0', u"EFI Default Loader", NULL);
if (config->auto_firmware && FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) {
ConfigEntry *entry = xnew(ConfigEntry, 1);
diff --git a/src/boot/efi/drivers.c b/src/boot/efi/drivers.c
index c073e1a4f2..41a7d8fe15 100644
--- a/src/boot/efi/drivers.c
+++ b/src/boot/efi/drivers.c
@@ -82,7 +82,7 @@ EFI_STATUS load_drivers(
err = open_directory(
root_dir,
- L"\\EFI\\systemd\\drivers",
+ u"\\EFI\\systemd\\drivers",
&drivers_dir);
if (err == EFI_NOT_FOUND)
return EFI_SUCCESS;
@@ -100,7 +100,7 @@ EFI_STATUS load_drivers(
continue;
if (FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY))
continue;
- if (!endswith_no_case(dirent->FileName, EFI_MACHINE_TYPE_NAME L".efi"))
+ if (!endswith_no_case(dirent->FileName, EFI_MACHINE_TYPE_NAME u".efi"))
continue;
err = load_one_driver(parent_image, loaded_image, dirent->FileName);
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index 5d7459d87e..a52934a901 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -196,7 +196,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
err = root_dir->Open(
root_dir,
&handle,
- (char16_t *) L"\\loader\\random-seed",
+ (char16_t *) u"\\loader\\random-seed",
EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
0);
if (err != EFI_SUCCESS) {
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index e3b97164a8..433fef548c 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -286,27 +286,27 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
if (pack_cpio(loaded_image,
NULL,
- L".cred",
+ u".cred",
".extra/credentials",
/* dir_mode= */ 0500,
/* access_mode= */ 0400,
/* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
/* n_tpm_pcr= */ 2,
- L"Credentials initrd",
+ u"Credentials initrd",
&credential_initrd,
&credential_initrd_size,
&m) == EFI_SUCCESS)
parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
if (pack_cpio(loaded_image,
- L"\\loader\\credentials",
- L".cred",
+ u"\\loader\\credentials",
+ u".cred",
".extra/global_credentials",
/* dir_mode= */ 0500,
/* access_mode= */ 0400,
/* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_KERNEL_PARAMETERS, TPM_PCR_INDEX_KERNEL_PARAMETERS_COMPAT },
/* n_tpm_pcr= */ 2,
- L"Global credentials initrd",
+ u"Global credentials initrd",
&global_credential_initrd,
&global_credential_initrd_size,
&m) == EFI_SUCCESS)
@@ -314,13 +314,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
if (pack_cpio(loaded_image,
NULL,
- L".raw",
+ u".raw",
".extra/sysext",
/* dir_mode= */ 0555,
/* access_mode= */ 0444,
/* tpm_pcr= */ (uint32_t[]) { TPM_PCR_INDEX_INITRD_SYSEXTS },
/* n_tpm_pcr= */ 1,
- L"System extension initrd",
+ u"System extension initrd",
&sysext_initrd,
&sysext_initrd_size,
&m) == EFI_SUCCESS)
@@ -341,7 +341,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
(uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRSIG],
szs[UNIFIED_SECTION_PCRSIG],
".extra",
- L"tpm2-pcr-signature.json",
+ u"tpm2-pcr-signature.json",
/* dir_mode= */ 0555,
/* access_mode= */ 0444,
/* tpm_pcr= */ NULL,
@@ -360,7 +360,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
(uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_PCRPKEY],
szs[UNIFIED_SECTION_PCRPKEY],
".extra",
- L"tpm2-pcr-public-key.pem",
+ u"tpm2-pcr-public-key.pem",
/* dir_mode= */ 0555,
/* access_mode= */ 0444,
/* tpm_pcr= */ NULL,

View File

@ -0,0 +1,106 @@
From 925a5adafed2d17c4f2c50460d4e571ca509f54e Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Wed, 7 Dec 2022 18:31:27 +0100
Subject: [PATCH] macro: add generic IS_ALIGNED32() anf friends
Let's generalize (and invert) the UNALIGNED32_P() macro from the sha256
code, and let's add a test for it.
(cherry picked from commit 4f07388360a3513b9fc8d2773568b8def941f4a4)
Related: RHEL-16952
---
src/fundamental/macro-fundamental.h | 9 +++++
src/test/test-macro.c | 53 +++++++++++++++++++++++++++++
2 files changed, 62 insertions(+)
diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h
index e0665d9dcb..dd0de328cb 100644
--- a/src/fundamental/macro-fundamental.h
+++ b/src/fundamental/macro-fundamental.h
@@ -334,14 +334,23 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
return ((l + ali - 1) & ~(ali - 1));
}
+#define ALIGN2(l) ALIGN_TO(l, 2)
#define ALIGN4(l) ALIGN_TO(l, 4)
#define ALIGN8(l) ALIGN_TO(l, 8)
+#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
+#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
+#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
#ifndef SD_BOOT
/* libefi also provides ALIGN, and we do not use them in sd-boot explicitly. */
#define ALIGN(l) ALIGN_TO(l, sizeof(void*))
#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
#endif
+/* Checks if the specified pointer is aligned as appropriate for the specific type */
+#define IS_ALIGNED16(p) (((uintptr_t) p) % __alignof__(uint16_t) == 0)
+#define IS_ALIGNED32(p) (((uintptr_t) p) % __alignof__(uint32_t) == 0)
+#define IS_ALIGNED64(p) (((uintptr_t) p) % __alignof__(uint64_t) == 0)
+
/* Same as ALIGN_TO but callable in constant contexts. */
#define CONST_ALIGN_TO(l, ali) \
__builtin_choose_expr( \
diff --git a/src/test/test-macro.c b/src/test/test-macro.c
index bb79ea0dbe..a1618c3105 100644
--- a/src/test/test-macro.c
+++ b/src/test/test-macro.c
@@ -755,4 +755,57 @@ TEST(FOREACH_ARRAY) {
assert_se(n == 0);
}
+TEST(ALIGNED) {
+ assert_se(IS_ALIGNED16(NULL));
+ assert_se(IS_ALIGNED32(NULL));
+ assert_se(IS_ALIGNED64(NULL));
+
+ uint64_t u64;
+ uint32_t u32;
+ uint16_t u16;
+
+ assert_se(IS_ALIGNED16(&u16));
+ assert_se(IS_ALIGNED16(&u32));
+ assert_se(IS_ALIGNED16(&u64));
+ assert_se(IS_ALIGNED32(&u32));
+ assert_se(IS_ALIGNED32(&u64));
+ assert_se(IS_ALIGNED64(&u64));
+
+ _align_(32) uint8_t ua256;
+ _align_(8) uint8_t ua64;
+ _align_(4) uint8_t ua32;
+ _align_(2) uint8_t ua16;
+
+ assert_se(IS_ALIGNED16(&ua256));
+ assert_se(IS_ALIGNED32(&ua256));
+ assert_se(IS_ALIGNED64(&ua256));
+
+ assert_se(IS_ALIGNED16(&ua64));
+ assert_se(IS_ALIGNED32(&ua64));
+ assert_se(IS_ALIGNED64(&ua64));
+
+ assert_se(IS_ALIGNED16(&ua32));
+ assert_se(IS_ALIGNED32(&ua32));
+
+ assert_se(IS_ALIGNED16(&ua16));
+
+#ifdef __x86_64__
+ /* Conditionalized on x86-64, since there we know for sure that all three types are aligned to
+ * their size. Too lazy to figure it out for other archs */
+ void *p = UINT_TO_PTR(1); /* definitely not aligned */
+ assert_se(!IS_ALIGNED16(p));
+ assert_se(!IS_ALIGNED32(p));
+ assert_se(!IS_ALIGNED64(p));
+
+ assert_se(IS_ALIGNED16(ALIGN2_PTR(p)));
+ assert_se(IS_ALIGNED32(ALIGN4_PTR(p)));
+ assert_se(IS_ALIGNED64(ALIGN8_PTR(p)));
+
+ p = UINT_TO_PTR(-1); /* also definitely not aligned */
+ assert_se(!IS_ALIGNED16(p));
+ assert_se(!IS_ALIGNED32(p));
+ assert_se(!IS_ALIGNED64(p));
+#endif
+}
+
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -0,0 +1,214 @@
From 1b03cc4e54f74c075e177b57e04cd6f9338540fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Tue, 10 Jan 2023 14:25:57 +0100
Subject: [PATCH] meson: use 0|1 for SD_BOOT
We converted to not using #ifdef for most of our defines because the syntax is
nicer and we are protected against typos and can set -Werror=undef. Let's do
the same for SD_BOOT. The define is nicely hidden in build.h for normal builds,
and for EFI builds we were already setting SD_BOOT on the commandline.
(cherry picked from commit 493cd5034c3eb091e7163ea1e744a4e07b410710)
Related: RHEL-16952
---
meson.build | 3 +++
src/boot/efi/efi-string.c | 8 ++++----
src/boot/efi/efi-string.h | 2 +-
src/boot/efi/meson.build | 2 +-
src/fundamental/macro-fundamental.h | 6 +++---
src/fundamental/memory-util-fundamental.h | 2 +-
src/fundamental/sha256.c | 2 +-
src/fundamental/string-util-fundamental.c | 4 ++--
src/fundamental/string-util-fundamental.h | 6 +++---
9 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/meson.build b/meson.build
index 54155eee1f..843d823e3e 100644
--- a/meson.build
+++ b/meson.build
@@ -55,6 +55,9 @@ fuzzer_build = want_ossfuzz or want_libfuzzer
# limits).
conf.set10('FUZZ_USE_SIZE_LIMIT', fuzzer_build)
+# We'll set this to '1' for EFI builds in a different place.
+conf.set10('SD_BOOT', false)
+
# Create a title-less summary section early, so it ends up first in the output.
# More items are added later after they have been detected.
summary({'build mode' : get_option('mode')})
diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c
index 2ba15673c9..79c296eda3 100644
--- a/src/boot/efi/efi-string.c
+++ b/src/boot/efi/efi-string.c
@@ -5,7 +5,7 @@
#include "efi-string.h"
-#ifdef SD_BOOT
+#if SD_BOOT
# include "util.h"
#else
# include <stdlib.h>
@@ -378,7 +378,7 @@ bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack) {
DEFINE_PARSE_NUMBER(char, parse_number8);
DEFINE_PARSE_NUMBER(char16_t, parse_number16);
-#ifdef SD_BOOT
+#if SD_BOOT
/* To provide the actual implementation for these we need to remove the redirection to the builtins. */
# undef memcmp
# undef memcpy
@@ -414,7 +414,7 @@ _used_ _weak_ void *memcpy(void * restrict dest, const void * restrict src, size
if (!dest || !src || n == 0)
return dest;
-#ifdef SD_BOOT
+#if SD_BOOT
/* The firmware-provided memcpy is likely optimized, so use that. The function is guaranteed to be
* available by the UEFI spec. We still make it depend on the boot services pointer being set just in
* case the compiler emits a call before it is available. */
@@ -441,7 +441,7 @@ _used_ _weak_ void *memset(void *p, int c, size_t n) {
if (!p || n == 0)
return p;
-#ifdef SD_BOOT
+#if SD_BOOT
/* See comment in efi_memcpy. Note that the signature has c and n swapped! */
if (_likely_(BS)) {
BS->SetMem(p, n, c);
diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h
index 25931a7d6e..aaa9b399c8 100644
--- a/src/boot/efi/efi-string.h
+++ b/src/boot/efi/efi-string.h
@@ -109,7 +109,7 @@ bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack);
bool parse_number8(const char *s, uint64_t *ret_u, const char **ret_tail);
bool parse_number16(const char16_t *s, uint64_t *ret_u, const char16_t **ret_tail);
-#ifdef SD_BOOT
+#if SD_BOOT
/* The compiler normally has knowledge about standard functions such as memcmp, but this is not the case when
* compiling with -ffreestanding. By referring to builtins, the compiler can check arguments and do
* optimizations again. Note that we still need to provide implementations as the compiler is free to not
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index ab2d7595f3..bba3b62d3c 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -176,7 +176,7 @@ efi_config_h = configure_file(
efi_cflags = [
'-DGNU_EFI_USE_MS_ABI',
- '-DSD_BOOT',
+ '-DSD_BOOT=1',
'-ffreestanding',
'-fshort-wchar',
'-fvisibility=hidden',
diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h
index dd0de328cb..1c4c445e4e 100644
--- a/src/fundamental/macro-fundamental.h
+++ b/src/fundamental/macro-fundamental.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#ifndef SD_BOOT
+#if !SD_BOOT
# include <assert.h>
#endif
@@ -66,7 +66,7 @@
#define XCONCATENATE(x, y) x ## y
#define CONCATENATE(x, y) XCONCATENATE(x, y)
-#ifdef SD_BOOT
+#if SD_BOOT
_noreturn_ void efi_assert(const char *expr, const char *file, unsigned line, const char *function);
#ifdef NDEBUG
@@ -340,7 +340,7 @@ static inline size_t ALIGN_TO(size_t l, size_t ali) {
#define ALIGN2_PTR(p) ((void*) ALIGN2((uintptr_t) p))
#define ALIGN4_PTR(p) ((void*) ALIGN4((uintptr_t) p))
#define ALIGN8_PTR(p) ((void*) ALIGN8((uintptr_t) p))
-#ifndef SD_BOOT
+#if !SD_BOOT
/* libefi also provides ALIGN, and we do not use them in sd-boot explicitly. */
#define ALIGN(l) ALIGN_TO(l, sizeof(void*))
#define ALIGN_PTR(p) ((void*) ALIGN((uintptr_t) (p)))
diff --git a/src/fundamental/memory-util-fundamental.h b/src/fundamental/memory-util-fundamental.h
index 9015300ae8..8f50d8b8e1 100644
--- a/src/fundamental/memory-util-fundamental.h
+++ b/src/fundamental/memory-util-fundamental.h
@@ -3,7 +3,7 @@
#include <stddef.h>
-#ifdef SD_BOOT
+#if SD_BOOT
# include "efi-string.h"
#else
# include <string.h>
diff --git a/src/fundamental/sha256.c b/src/fundamental/sha256.c
index 9b717645b3..39029de93c 100644
--- a/src/fundamental/sha256.c
+++ b/src/fundamental/sha256.c
@@ -22,7 +22,7 @@
<https://www.gnu.org/licenses/>. */
#include <stdbool.h>
-#ifdef SD_BOOT
+#if SD_BOOT
# include "efi-string.h"
#else
# include <string.h>
diff --git a/src/fundamental/string-util-fundamental.c b/src/fundamental/string-util-fundamental.c
index 11701ebe52..484131d72a 100644
--- a/src/fundamental/string-util-fundamental.c
+++ b/src/fundamental/string-util-fundamental.c
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef SD_BOOT
+#if !SD_BOOT
# include <ctype.h>
#endif
@@ -20,7 +20,7 @@ sd_char *startswith(const sd_char *s, const sd_char *prefix) {
return (sd_char*) s + l;
}
-#ifndef SD_BOOT
+#if !SD_BOOT
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) {
size_t l;
diff --git a/src/fundamental/string-util-fundamental.h b/src/fundamental/string-util-fundamental.h
index ecf32e519f..523c612a17 100644
--- a/src/fundamental/string-util-fundamental.h
+++ b/src/fundamental/string-util-fundamental.h
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#ifdef SD_BOOT
+#if SD_BOOT
# include <efi.h>
# include <efilib.h>
# include "efi-string.h"
@@ -11,7 +11,7 @@
#include "macro-fundamental.h"
-#ifdef SD_BOOT
+#if SD_BOOT
# define strlen strlen16
# define strcmp strcmp16
# define strncmp strncmp16
@@ -59,7 +59,7 @@ static inline size_t strlen_ptr(const sd_char *s) {
}
sd_char *startswith(const sd_char *s, const sd_char *prefix) _pure_;
-#ifndef SD_BOOT
+#if !SD_BOOT
sd_char *startswith_no_case(const sd_char *s, const sd_char *prefix) _pure_;
#endif
sd_char *endswith(const sd_char *s, const sd_char *postfix) _pure_;

View File

@ -0,0 +1,843 @@
From 6f7f7bb71af6047458a41c0f7135a8d31df840c4 Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Fri, 10 Jun 2022 18:55:24 +0200
Subject: [PATCH] boot: Add printf functions
(cherry picked from commit 7c4536a9af986332eaac8db292b22d59b4977f04)
Related: RHEL-16952
---
src/boot/efi/efi-string.c | 491 +++++++++++++++++++++++++++++++++
src/boot/efi/efi-string.h | 26 ++
src/boot/efi/fuzz-efi-printf.c | 76 +++++
src/boot/efi/meson.build | 1 +
src/boot/efi/missing_efi.h | 12 +
src/boot/efi/test-efi-string.c | 142 ++++++++++
6 files changed, 748 insertions(+)
create mode 100644 src/boot/efi/fuzz-efi-printf.c
diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c
index 79c296eda3..860dfc00b2 100644
--- a/src/boot/efi/efi-string.c
+++ b/src/boot/efi/efi-string.c
@@ -2,10 +2,12 @@
#include <stdbool.h>
#include <stdint.h>
+#include <wchar.h>
#include "efi-string.h"
#if SD_BOOT
+# include "missing_efi.h"
# include "util.h"
#else
# include <stdlib.h>
@@ -378,6 +380,495 @@ bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack) {
DEFINE_PARSE_NUMBER(char, parse_number8);
DEFINE_PARSE_NUMBER(char16_t, parse_number16);
+static const char * const warn_table[] = {
+ [EFI_SUCCESS] = "Success",
+#if SD_BOOT
+ [EFI_WARN_UNKNOWN_GLYPH] = "Unknown glyph",
+ [EFI_WARN_DELETE_FAILURE] = "Delete failure",
+ [EFI_WARN_WRITE_FAILURE] = "Write failure",
+ [EFI_WARN_BUFFER_TOO_SMALL] = "Buffer too small",
+ [EFI_WARN_STALE_DATA] = "Stale data",
+ [EFI_WARN_FILE_SYSTEM] = "File system",
+ [EFI_WARN_RESET_REQUIRED] = "Reset required",
+#endif
+};
+
+/* Errors have MSB set, remove it to keep the table compact. */
+#define NOERR(err) ((err) & ~EFI_ERROR_MASK)
+
+static const char * const err_table[] = {
+ [NOERR(EFI_ERROR_MASK)] = "Error",
+ [NOERR(EFI_LOAD_ERROR)] = "Load error",
+#if SD_BOOT
+ [NOERR(EFI_INVALID_PARAMETER)] = "Invalid parameter",
+ [NOERR(EFI_UNSUPPORTED)] = "Unsupported",
+ [NOERR(EFI_BAD_BUFFER_SIZE)] = "Bad buffer size",
+ [NOERR(EFI_BUFFER_TOO_SMALL)] = "Buffer too small",
+ [NOERR(EFI_NOT_READY)] = "Not ready",
+ [NOERR(EFI_DEVICE_ERROR)] = "Device error",
+ [NOERR(EFI_WRITE_PROTECTED)] = "Write protected",
+ [NOERR(EFI_OUT_OF_RESOURCES)] = "Out of resources",
+ [NOERR(EFI_VOLUME_CORRUPTED)] = "Volume corrupt",
+ [NOERR(EFI_VOLUME_FULL)] = "Volume full",
+ [NOERR(EFI_NO_MEDIA)] = "No media",
+ [NOERR(EFI_MEDIA_CHANGED)] = "Media changed",
+ [NOERR(EFI_NOT_FOUND)] = "Not found",
+ [NOERR(EFI_ACCESS_DENIED)] = "Access denied",
+ [NOERR(EFI_NO_RESPONSE)] = "No response",
+ [NOERR(EFI_NO_MAPPING)] = "No mapping",
+ [NOERR(EFI_TIMEOUT)] = "Time out",
+ [NOERR(EFI_NOT_STARTED)] = "Not started",
+ [NOERR(EFI_ALREADY_STARTED)] = "Already started",
+ [NOERR(EFI_ABORTED)] = "Aborted",
+ [NOERR(EFI_ICMP_ERROR)] = "ICMP error",
+ [NOERR(EFI_TFTP_ERROR)] = "TFTP error",
+ [NOERR(EFI_PROTOCOL_ERROR)] = "Protocol error",
+ [NOERR(EFI_INCOMPATIBLE_VERSION)] = "Incompatible version",
+ [NOERR(EFI_SECURITY_VIOLATION)] = "Security violation",
+ [NOERR(EFI_CRC_ERROR)] = "CRC error",
+ [NOERR(EFI_END_OF_MEDIA)] = "End of media",
+ [29] = "Reserved (29)",
+ [30] = "Reserved (30)",
+ [NOERR(EFI_END_OF_FILE)] = "End of file",
+ [NOERR(EFI_INVALID_LANGUAGE)] = "Invalid language",
+ [NOERR(EFI_COMPROMISED_DATA)] = "Compromised data",
+ [NOERR(EFI_IP_ADDRESS_CONFLICT)] = "IP address conflict",
+ [NOERR(EFI_HTTP_ERROR)] = "HTTP error",
+#endif
+};
+
+static const char *status_to_string(EFI_STATUS status) {
+ if (status <= ELEMENTSOF(warn_table) - 1)
+ return warn_table[status];
+ if (status >= EFI_ERROR_MASK && status <= ((ELEMENTSOF(err_table) - 1) | EFI_ERROR_MASK))
+ return err_table[NOERR(status)];
+ return NULL;
+}
+
+typedef struct {
+ size_t padded_len; /* Field width in printf. */
+ size_t len; /* Precision in printf. */
+ bool pad_zero;
+ bool align_left;
+ bool alternative_form;
+ bool long_arg;
+ bool longlong_arg;
+ bool have_field_width;
+
+ const char *str;
+ const wchar_t *wstr;
+
+ /* For numbers. */
+ bool is_signed;
+ bool lowercase;
+ int8_t base;
+ char sign_pad; /* For + and (space) flags. */
+} SpecifierContext;
+
+typedef struct {
+ char16_t stack_buf[128]; /* We use stack_buf first to avoid allocations in most cases. */
+ char16_t *dyn_buf; /* Allocated buf or NULL if stack_buf is used. */
+ char16_t *buf; /* Points to the current active buf. */
+ size_t n_buf; /* Len of buf (in char16_t's, not bytes!). */
+ size_t n; /* Used len of buf (in char16_t's). This is always <n_buf. */
+
+ EFI_STATUS status;
+ const char *format;
+ va_list ap;
+} FormatContext;
+
+static void grow_buf(FormatContext *ctx, size_t need) {
+ assert(ctx);
+
+ assert_se(!__builtin_add_overflow(ctx->n, need, &need));
+
+ if (need < ctx->n_buf)
+ return;
+
+ /* Greedily allocate if we can. */
+ if (__builtin_mul_overflow(need, 2, &ctx->n_buf))
+ ctx->n_buf = need;
+
+ /* We cannot use realloc here as ctx->buf may be ctx->stack_buf, which we cannot free. */
+ char16_t *new_buf = xnew(char16_t, ctx->n_buf);
+ memcpy(new_buf, ctx->buf, ctx->n * sizeof(*ctx->buf));
+
+ free(ctx->dyn_buf);
+ ctx->buf = ctx->dyn_buf = new_buf;
+}
+
+static void push_padding(FormatContext *ctx, char pad, size_t len) {
+ assert(ctx);
+ while (len > 0) {
+ len--;
+ ctx->buf[ctx->n++] = pad;
+ }
+}
+
+static bool push_str(FormatContext *ctx, SpecifierContext *sp) {
+ assert(ctx);
+ assert(sp);
+
+ sp->padded_len = LESS_BY(sp->padded_len, sp->len);
+
+ grow_buf(ctx, sp->padded_len + sp->len);
+
+ if (!sp->align_left)
+ push_padding(ctx, ' ', sp->padded_len);
+
+ /* In userspace unit tests we cannot just memcpy() the wide string. */
+ if (sp->wstr && sizeof(wchar_t) == sizeof(char16_t)) {
+ memcpy(ctx->buf + ctx->n, sp->wstr, sp->len * sizeof(*sp->wstr));
+ ctx->n += sp->len;
+ } else
+ for (size_t i = 0; i < sp->len; i++)
+ ctx->buf[ctx->n++] = sp->str ? sp->str[i] : sp->wstr[i];
+
+ if (sp->align_left)
+ push_padding(ctx, ' ', sp->padded_len);
+
+ assert(ctx->n < ctx->n_buf);
+ return true;
+}
+
+static bool push_num(FormatContext *ctx, SpecifierContext *sp, uint64_t u) {
+ const char *digits = sp->lowercase ? "0123456789abcdef" : "0123456789ABCDEF";
+ char16_t tmp[32];
+ size_t n = 0;
+
+ assert(ctx);
+ assert(sp);
+ assert(IN_SET(sp->base, 10, 16));
+
+ /* "%.0u" prints nothing if value is 0. */
+ if (u == 0 && sp->len == 0)
+ return true;
+
+ if (sp->is_signed && (int64_t) u < 0) {
+ /* We cannot just do "u = -(int64_t)u" here because -INT64_MIN overflows. */
+
+ uint64_t rem = -((int64_t) u % sp->base);
+ u = (int64_t) u / -sp->base;
+ tmp[n++] = digits[rem];
+ sp->sign_pad = '-';
+ }
+
+ while (u > 0 || n == 0) {
+ uint64_t rem = u % sp->base;
+ u /= sp->base;
+ tmp[n++] = digits[rem];
+ }
+
+ /* Note that numbers never get truncated! */
+ size_t prefix = (sp->sign_pad != 0 ? 1 : 0) + (sp->alternative_form ? 2 : 0);
+ size_t number_len = prefix + MAX(n, sp->len);
+ grow_buf(ctx, MAX(sp->padded_len, number_len));
+
+ size_t padding = 0;
+ if (sp->pad_zero)
+ /* Leading zeroes go after the sign or 0x prefix. */
+ number_len = MAX(number_len, sp->padded_len);
+ else
+ padding = LESS_BY(sp->padded_len, number_len);
+
+ if (!sp->align_left)
+ push_padding(ctx, ' ', padding);
+
+ if (sp->sign_pad != 0)
+ ctx->buf[ctx->n++] = sp->sign_pad;
+ if (sp->alternative_form) {
+ ctx->buf[ctx->n++] = '0';
+ ctx->buf[ctx->n++] = sp->lowercase ? 'x' : 'X';
+ }
+
+ push_padding(ctx, '0', LESS_BY(number_len, n + prefix));
+
+ while (n > 0)
+ ctx->buf[ctx->n++] = tmp[--n];
+
+ if (sp->align_left)
+ push_padding(ctx, ' ', padding);
+
+ assert(ctx->n < ctx->n_buf);
+ return true;
+}
+
+/* This helps unit testing. */
+#if SD_BOOT
+# define NULLSTR "(null)"
+# define wcsnlen strnlen16
+#else
+# define NULLSTR "(nil)"
+#endif
+
+static bool handle_format_specifier(FormatContext *ctx, SpecifierContext *sp) {
+ /* Parses one item from the format specifier in ctx and put the info into sp. If we are done with
+ * this specifier returns true, otherwise this function should be called again. */
+
+ /* This implementation assumes 32bit ints. Also note that all types smaller than int are promoted to
+ * int in vararg functions, which is why we fetch only ints for any such types. The compiler would
+ * otherwise warn about fetching smaller types. */
+ assert_cc(sizeof(int) == 4);
+ assert_cc(sizeof(wchar_t) <= sizeof(int));
+ assert_cc(sizeof(intmax_t) <= sizeof(long long));
+
+ assert(ctx);
+ assert(sp);
+
+ switch (*ctx->format) {
+ case '#':
+ sp->alternative_form = true;
+ return false;
+ case '.':
+ sp->have_field_width = true;
+ return false;
+ case '-':
+ sp->align_left = true;
+ return false;
+ case '+':
+ case ' ':
+ sp->sign_pad = *ctx->format;
+ return false;
+
+ case '0':
+ if (!sp->have_field_width) {
+ sp->pad_zero = true;
+ return false;
+ }
+
+ /* If field width has already been provided then 0 is part of precision (%.0s). */
+ _fallthrough_;
+
+ case '*':
+ case '1' ... '9': {
+ int64_t i;
+
+ if (*ctx->format == '*')
+ i = va_arg(ctx->ap, int);
+ else {
+ uint64_t u;
+ if (!parse_number8(ctx->format, &u, &ctx->format) || u > INT_MAX)
+ assert_not_reached();
+ ctx->format--; /* Point it back to the last digit. */
+ i = u;
+ }
+
+ if (sp->have_field_width) {
+ /* Negative precision is ignored. */
+ if (i >= 0)
+ sp->len = (size_t) i;
+ } else {
+ /* Negative field width is treated as positive field width with '-' flag. */
+ if (i < 0) {
+ i *= -1;
+ sp->align_left = true;
+ }
+ sp->padded_len = i;
+ }
+
+ return false;
+ }
+
+ case 'h':
+ if (*(ctx->format + 1) == 'h')
+ ctx->format++;
+ /* char/short gets promoted to int, nothing to do here. */
+ return false;
+
+ case 'l':
+ if (*(ctx->format + 1) == 'l') {
+ ctx->format++;
+ sp->longlong_arg = true;
+ } else
+ sp->long_arg = true;
+ return false;
+
+ case 'z':
+ sp->long_arg = sizeof(size_t) == sizeof(long);
+ sp->longlong_arg = !sp->long_arg && sizeof(size_t) == sizeof(long long);
+ return false;
+
+ case 'j':
+ sp->long_arg = sizeof(intmax_t) == sizeof(long);
+ sp->longlong_arg = !sp->long_arg && sizeof(intmax_t) == sizeof(long long);
+ return false;
+
+ case 't':
+ sp->long_arg = sizeof(ptrdiff_t) == sizeof(long);
+ sp->longlong_arg = !sp->long_arg && sizeof(ptrdiff_t) == sizeof(long long);
+ return false;
+
+ case '%':
+ sp->str = "%";
+ sp->len = 1;
+ return push_str(ctx, sp);
+
+ case 'c':
+ sp->wstr = &(wchar_t){ va_arg(ctx->ap, int) };
+ sp->len = 1;
+ return push_str(ctx, sp);
+
+ case 's':
+ if (sp->long_arg) {
+ sp->wstr = va_arg(ctx->ap, const wchar_t *) ?: L"(null)";
+ sp->len = wcsnlen(sp->wstr, sp->len);
+ } else {
+ sp->str = va_arg(ctx->ap, const char *) ?: "(null)";
+ sp->len = strnlen8(sp->str, sp->len);
+ }
+ return push_str(ctx, sp);
+
+ case 'd':
+ case 'i':
+ case 'u':
+ case 'x':
+ case 'X':
+ sp->lowercase = *ctx->format == 'x';
+ sp->is_signed = IN_SET(*ctx->format, 'd', 'i');
+ sp->base = IN_SET(*ctx->format, 'x', 'X') ? 16 : 10;
+ if (sp->len == SIZE_MAX)
+ sp->len = 1;
+
+ uint64_t v;
+ if (sp->longlong_arg)
+ v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long long) :
+ va_arg(ctx->ap, unsigned long long);
+ else if (sp->long_arg)
+ v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, long) : va_arg(ctx->ap, unsigned long);
+ else
+ v = sp->is_signed ? (uint64_t) va_arg(ctx->ap, int) : va_arg(ctx->ap, unsigned);
+
+ return push_num(ctx, sp, v);
+
+ case 'p': {
+ const void *ptr = va_arg(ctx->ap, const void *);
+ if (!ptr) {
+ sp->str = NULLSTR;
+ sp->len = STRLEN(NULLSTR);
+ return push_str(ctx, sp);
+ }
+
+ sp->base = 16;
+ sp->lowercase = true;
+ sp->alternative_form = true;
+ sp->len = 0; /* Precision is ignored for %p. */
+ return push_num(ctx, sp, (uintptr_t) ptr);
+ }
+
+ case 'm': {
+ sp->str = status_to_string(ctx->status);
+ if (sp->str) {
+ sp->len = strlen8(sp->str);
+ return push_str(ctx, sp);
+ }
+
+ sp->base = 16;
+ sp->lowercase = true;
+ sp->alternative_form = true;
+ sp->len = 0;
+ return push_num(ctx, sp, ctx->status);
+ }
+
+ default:
+ assert_not_reached();
+ }
+}
+
+/* printf_internal is largely compatible to userspace vasprintf. Any features omitted should trigger asserts.
+ *
+ * Supported:
+ * - Flags: #, 0, +, -, space
+ * - Lengths: h, hh, l, ll, z, j, t
+ * - Specifiers: %, c, s, u, i, d, x, X, p, m
+ * - Precision and width (inline or as int arg using *)
+ *
+ * Notable differences:
+ * - Passing NULL to %s is permitted and will print "(null)"
+ * - %p will also use "(null)"
+ * - The provided EFI_STATUS is used for %m instead of errno
+ * - "\n" is translated to "\r\n" */
+_printf_(2, 0) static char16_t *printf_internal(EFI_STATUS status, const char *format, va_list ap, bool ret) {
+ assert(format);
+
+ FormatContext ctx = {
+ .buf = ctx.stack_buf,
+ .n_buf = ELEMENTSOF(ctx.stack_buf),
+ .format = format,
+ .status = status,
+ };
+
+ /* We cannot put this into the struct without making a copy. */
+ va_copy(ctx.ap, ap);
+
+ while (*ctx.format != '\0') {
+ SpecifierContext sp = { .len = SIZE_MAX };
+
+ switch (*ctx.format) {
+ case '%':
+ ctx.format++;
+ while (!handle_format_specifier(&ctx, &sp))
+ ctx.format++;
+ ctx.format++;
+ break;
+ case '\n':
+ ctx.format++;
+ sp.str = "\r\n";
+ sp.len = 2;
+ push_str(&ctx, &sp);
+ break;
+ default:
+ sp.str = ctx.format++;
+ while (!IN_SET(*ctx.format, '%', '\n', '\0'))
+ ctx.format++;
+ sp.len = ctx.format - sp.str;
+ push_str(&ctx, &sp);
+ }
+ }
+
+ va_end(ctx.ap);
+
+ assert(ctx.n < ctx.n_buf);
+ ctx.buf[ctx.n++] = '\0';
+
+ if (ret) {
+ if (ctx.dyn_buf)
+ return TAKE_PTR(ctx.dyn_buf);
+
+ char16_t *ret_buf = xnew(char16_t, ctx.n);
+ memcpy(ret_buf, ctx.buf, ctx.n * sizeof(*ctx.buf));
+ return ret_buf;
+ }
+
+#if SD_BOOT
+ ST->ConOut->OutputString(ST->ConOut, ctx.buf);
+#endif
+
+ return mfree(ctx.dyn_buf);
+}
+
+void printf_status(EFI_STATUS status, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ printf_internal(status, format, ap, false);
+ va_end(ap);
+}
+
+void vprintf_status(EFI_STATUS status, const char *format, va_list ap) {
+ printf_internal(status, format, ap, false);
+}
+
+char16_t *xasprintf_status(EFI_STATUS status, const char *format, ...) {
+ va_list ap;
+ va_start(ap, format);
+ char16_t *ret = printf_internal(status, format, ap, true);
+ va_end(ap);
+ return ret;
+}
+
+char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) {
+ return printf_internal(status, format, ap, true);
+}
+
#if SD_BOOT
/* To provide the actual implementation for these we need to remove the redirection to the builtins. */
# undef memcmp
diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h
index aaa9b399c8..2a28db3593 100644
--- a/src/boot/efi/efi-string.h
+++ b/src/boot/efi/efi-string.h
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
+#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <uchar.h>
@@ -109,7 +110,32 @@ bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack);
bool parse_number8(const char *s, uint64_t *ret_u, const char **ret_tail);
bool parse_number16(const char16_t *s, uint64_t *ret_u, const char16_t **ret_tail);
+typedef size_t EFI_STATUS;
+
+#if !SD_BOOT
+/* Provide these for unit testing. */
+enum {
+ EFI_ERROR_MASK = ((EFI_STATUS) 1 << (sizeof(EFI_STATUS) * CHAR_BIT - 1)),
+ EFI_SUCCESS = 0,
+ EFI_LOAD_ERROR = 1 | EFI_ERROR_MASK,
+};
+#endif
+
+#ifdef __clang__
+# define _gnu_printf_(a, b) _printf_(a, b)
+#else
+# define _gnu_printf_(a, b) __attribute__((format(gnu_printf, a, b)))
+#endif
+
+_gnu_printf_(2, 3) void printf_status(EFI_STATUS status, const char *format, ...);
+_gnu_printf_(2, 0) void vprintf_status(EFI_STATUS status, const char *format, va_list ap);
+_gnu_printf_(2, 3) _warn_unused_result_ char16_t *xasprintf_status(EFI_STATUS status, const char *format, ...);
+_gnu_printf_(2, 0) _warn_unused_result_ char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap);
+
#if SD_BOOT
+# define printf(...) printf_status(EFI_SUCCESS, __VA_ARGS__)
+# define xasprintf(...) xasprintf_status(EFI_SUCCESS, __VA_ARGS__)
+
/* The compiler normally has knowledge about standard functions such as memcmp, but this is not the case when
* compiling with -ffreestanding. By referring to builtins, the compiler can check arguments and do
* optimizations again. Note that we still need to provide implementations as the compiler is free to not
diff --git a/src/boot/efi/fuzz-efi-printf.c b/src/boot/efi/fuzz-efi-printf.c
new file mode 100644
index 0000000000..218a427cef
--- /dev/null
+++ b/src/boot/efi/fuzz-efi-printf.c
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "alloc-util.h"
+#include "efi-string.h"
+#include "fuzz.h"
+#include "utf8.h"
+
+typedef struct {
+ EFI_STATUS status;
+ int16_t field_width;
+ int16_t precision;
+ const void *ptr;
+ char c;
+ unsigned char uchar;
+ signed char schar;
+ unsigned short ushort;
+ signed short sshort;
+ unsigned int uint;
+ signed int sint;
+ unsigned long ulong;
+ signed long slong;
+ unsigned long long ulonglong;
+ signed long long slonglong;
+ size_t size;
+ ssize_t ssize;
+ intmax_t intmax;
+ uintmax_t uintmax;
+ ptrdiff_t ptrdiff;
+ char str[];
+} Input;
+
+#define PRINTF_ONE(...) \
+ ({ \
+ _cleanup_free_ char16_t *_ret = xasprintf_status(__VA_ARGS__); \
+ DO_NOT_OPTIMIZE(_ret); \
+ })
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (outside_size_range(size, sizeof(Input), 1024 * 1024))
+ return 0;
+
+ const Input *i = (const Input *) data;
+ size_t len = size - offsetof(Input, str);
+
+ PRINTF_ONE(i->status, "%*.*s", i->field_width, (int) len, i->str);
+ PRINTF_ONE(i->status, "%*.*ls", i->field_width, (int) (len / sizeof(wchar_t)), (const wchar_t *) i->str);
+
+ PRINTF_ONE(i->status, "%% %*.*m", i->field_width, i->precision);
+ PRINTF_ONE(i->status, "%*p", i->field_width, i->ptr);
+ PRINTF_ONE(i->status, "%*c %12340c %56789c", i->field_width, i->c, i->c, i->c);
+
+ PRINTF_ONE(i->status, "%*.*hhu", i->field_width, i->precision, i->uchar);
+ PRINTF_ONE(i->status, "%*.*hhi", i->field_width, i->precision, i->schar);
+ PRINTF_ONE(i->status, "%*.*hu", i->field_width, i->precision, i->ushort);
+ PRINTF_ONE(i->status, "%*.*hi", i->field_width, i->precision, i->sshort);
+ PRINTF_ONE(i->status, "%*.*u", i->field_width, i->precision, i->uint);
+ PRINTF_ONE(i->status, "%*.*i", i->field_width, i->precision, i->sint);
+ PRINTF_ONE(i->status, "%*.*lu", i->field_width, i->precision, i->ulong);
+ PRINTF_ONE(i->status, "%*.*li", i->field_width, i->precision, i->slong);
+ PRINTF_ONE(i->status, "%*.*llu", i->field_width, i->precision, i->ulonglong);
+ PRINTF_ONE(i->status, "%*.*lli", i->field_width, i->precision, i->slonglong);
+
+ PRINTF_ONE(i->status, "%+*.*hhi", i->field_width, i->precision, i->schar);
+ PRINTF_ONE(i->status, "%-*.*hi", i->field_width, i->precision, i->sshort);
+ PRINTF_ONE(i->status, "% *.*i", i->field_width, i->precision, i->sint);
+ PRINTF_ONE(i->status, "%0*li", i->field_width, i->slong);
+ PRINTF_ONE(i->status, "%#*.*llx", i->field_width, i->precision, i->ulonglong);
+
+ PRINTF_ONE(i->status, "%-*.*zx", i->field_width, i->precision, i->size);
+ PRINTF_ONE(i->status, "% *.*zi", i->field_width, i->precision, i->ssize);
+ PRINTF_ONE(i->status, "%0*ji", i->field_width, i->intmax);
+ PRINTF_ONE(i->status, "%#0*jX", i->field_width, i->uintmax);
+ PRINTF_ONE(i->status, "%*.*ti", i->field_width, i->precision, i->ptrdiff);
+
+ return 0;
+}
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index bba3b62d3c..ed332262e8 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -423,6 +423,7 @@ if efi_arch[1] in ['ia32', 'x86_64', 'arm', 'aarch64']
fuzzers += [
[files('fuzz-bcd.c', 'bcd.c', 'efi-string.c')],
[files('fuzz-efi-string.c', 'efi-string.c')],
+ [files('fuzz-efi-printf.c', 'efi-string.c')],
]
endif
diff --git a/src/boot/efi/missing_efi.h b/src/boot/efi/missing_efi.h
index b446e0399f..a71c8fa7e2 100644
--- a/src/boot/efi/missing_efi.h
+++ b/src/boot/efi/missing_efi.h
@@ -417,3 +417,15 @@ struct EFI_BOOT_MANAGER_POLICY_PROTOCOL {
EFI_GUID *Class);
};
#endif
+
+#ifndef EFI_WARN_UNKNOWN_GLYPH
+# define EFI_WARN_UNKNOWN_GLYPH 1
+#endif
+
+#ifndef EFI_WARN_RESET_REQUIRED
+# define EFI_WARN_STALE_DATA 5
+# define EFI_WARN_FILE_SYSTEM 6
+# define EFI_WARN_RESET_REQUIRED 7
+# define EFI_IP_ADDRESS_CONFLICT EFIERR(34)
+# define EFI_HTTP_ERROR EFIERR(35)
+#endif
diff --git a/src/boot/efi/test-efi-string.c b/src/boot/efi/test-efi-string.c
index 7b43e1d629..c26973d8bd 100644
--- a/src/boot/efi/test-efi-string.c
+++ b/src/boot/efi/test-efi-string.c
@@ -468,6 +468,148 @@ TEST(parse_number16) {
assert_se(streq16(tail, u"rest"));
}
+_printf_(1, 2) static void test_printf_one(const char *format, ...) {
+ va_list ap, ap_efi;
+ va_start(ap, format);
+ va_copy(ap_efi, ap);
+
+ _cleanup_free_ char *buf = NULL;
+ int r = vasprintf(&buf, format, ap);
+ assert_se(r >= 0);
+ log_info("/* %s(%s) -> \"%.100s\" */", __func__, format, buf);
+
+ _cleanup_free_ char16_t *buf_efi = xvasprintf_status(0, format, ap_efi);
+
+ bool eq = true;
+ for (size_t i = 0; i <= (size_t) r; i++) {
+ if (buf[i] != buf_efi[i])
+ eq = false;
+ buf[i] = buf_efi[i];
+ }
+
+ log_info("%.100s", buf);
+ assert_se(eq);
+
+ va_end(ap);
+ va_end(ap_efi);
+}
+
+TEST(xvasprintf_status) {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-zero-length"
+ test_printf_one("");
+#pragma GCC diagnostic pop
+ test_printf_one("string");
+ test_printf_one("%%-%%%%");
+
+ test_printf_one("%p %p %32p %*p %*p", NULL, (void *) 0xF, &errno, 0, &saved_argc, 20, &saved_argv);
+ test_printf_one("%-10p %-32p %-*p %-*p", NULL, &errno, 0, &saved_argc, 20, &saved_argv);
+
+ test_printf_one("%c %3c %*c %*c %-8c", '1', '!', 0, 'a', 9, '_', '>');
+
+ test_printf_one("%s %s %s", "012345", "6789", "ab");
+ test_printf_one("%.4s %.4s %.4s %.0s", "cdefgh", "ijkl", "mn", "@");
+ test_printf_one("%8s %8s %8s", "opqrst", "uvwx", "yz");
+ test_printf_one("%8.4s %8.4s %8.4s %8.0s", "ABCDEF", "GHIJ", "KL", "$");
+ test_printf_one("%4.8s %4.8s %4.8s", "ABCDEFGHIJ", "ABCDEFGH", "ABCD");
+
+ test_printf_one("%.*s %.*s %.*s %.*s", 4, "012345", 4, "6789", 4, "ab", 0, "&");
+ test_printf_one("%*s %*s %*s", 8, "cdefgh", 8, "ijkl", 8, "mn");
+ test_printf_one("%*.*s %*.*s %*.*s %*.*s", 8, 4, "opqrst", 8, 4, "uvwx", 8, 4, "yz", 8, 0, "#");
+ test_printf_one("%*.*s %*.*s %*.*s", 3, 8, "OPQRST", 3, 8, "UVWX", 3, 8, "YZ");
+
+ test_printf_one("%ls %ls %ls", L"012345", L"6789", L"ab");
+ test_printf_one("%.4ls %.4ls %.4ls %.0ls", L"cdefgh", L"ijkl", L"mn", L"@");
+ test_printf_one("%8ls %8ls %8ls", L"opqrst", L"uvwx", L"yz");
+ test_printf_one("%8.4ls %8.4ls %8.4ls %8.0ls", L"ABCDEF", L"GHIJ", L"KL", L"$");
+ test_printf_one("%4.8ls %4.8ls %4.8ls", L"ABCDEFGHIJ", L"ABCDEFGH", L"ABCD");
+
+ test_printf_one("%.*ls %.*ls %.*ls %.*ls", 4, L"012345", 4, L"6789", 4, L"ab", 0, L"&");
+ test_printf_one("%*ls %*ls %*ls", 8, L"cdefgh", 8, L"ijkl", 8, L"mn");
+ test_printf_one("%*.*ls %*.*ls %*.*ls %*.*ls", 8, 4, L"opqrst", 8, 4, L"uvwx", 8, 4, L"yz", 8, 0, L"#");
+ test_printf_one("%*.*ls %*.*ls %*.*ls", 3, 8, L"OPQRST", 3, 8, L"UVWX", 3, 8, L"YZ");
+
+ test_printf_one("%-14s %-10.0s %-10.3s", "left", "", "chopped");
+ test_printf_one("%-14ls %-10.0ls %-10.3ls", L"left", L"", L"chopped");
+
+ test_printf_one("%.6s", (char[]){ 'n', 'o', ' ', 'n', 'u', 'l' });
+ test_printf_one("%.6ls", (wchar_t[]){ 'n', 'o', ' ', 'n', 'u', 'l' });
+
+ test_printf_one("%u %u %u", 0U, 42U, 1234567890U);
+ test_printf_one("%i %i %i", 0, -42, -1234567890);
+ test_printf_one("%x %x %x", 0x0U, 0x42U, 0x123ABCU);
+ test_printf_one("%X %X %X", 0x1U, 0x43U, 0x234BCDU);
+ test_printf_one("%#x %#x %#x", 0x2U, 0x44U, 0x345CDEU);
+ test_printf_one("%#X %#X %#X", 0x3U, 0x45U, 0x456FEDU);
+
+ test_printf_one("%u %lu %llu %zu", UINT_MAX, ULONG_MAX, ULLONG_MAX, SIZE_MAX);
+ test_printf_one("%i %i %zi", INT_MIN, INT_MAX, SSIZE_MAX);
+ test_printf_one("%li %li %lli %lli", LONG_MIN, LONG_MAX, LLONG_MIN, LLONG_MAX);
+ test_printf_one("%x %#lx %llx %#zx", UINT_MAX, ULONG_MAX, ULLONG_MAX, SIZE_MAX);
+ test_printf_one("%X %#lX %llX %#zX", UINT_MAX, ULONG_MAX, ULLONG_MAX, SIZE_MAX);
+ test_printf_one("%ju %ji %ji", UINTMAX_MAX, INTMAX_MIN, INTMAX_MAX);
+ test_printf_one("%ti %ti", PTRDIFF_MIN, PTRDIFF_MAX);
+
+ test_printf_one("%" PRIu32 " %" PRIi32 " %" PRIi32, UINT32_MAX, INT32_MIN, INT32_MAX);
+ test_printf_one("%" PRIx32 " %" PRIX32, UINT32_MAX, UINT32_MAX);
+ test_printf_one("%#" PRIx32 " %#" PRIX32, UINT32_MAX, UINT32_MAX);
+
+ test_printf_one("%" PRIu64 " %" PRIi64 " %" PRIi64, UINT64_MAX, INT64_MIN, INT64_MAX);
+ test_printf_one("%" PRIx64 " %" PRIX64, UINT64_MAX, UINT64_MAX);
+ test_printf_one("%#" PRIx64 " %#" PRIX64, UINT64_MAX, UINT64_MAX);
+
+ test_printf_one("%.11u %.11i %.11x %.11X %#.11x %#.11X", 1U, -2, 3U, 0xA1U, 0xB2U, 0xC3U);
+ test_printf_one("%13u %13i %13x %13X %#13x %#13X", 4U, -5, 6U, 0xD4U, 0xE5U, 0xF6U);
+ test_printf_one("%9.5u %9.5i %9.5x %9.5X %#9.5x %#9.5X", 7U, -8, 9U, 0xA9U, 0xB8U, 0xC7U);
+ test_printf_one("%09u %09i %09x %09X %#09x %#09X", 4U, -5, 6U, 0xD6U, 0xE5U, 0xF4U);
+
+ test_printf_one("%*u %.*u %*i %.*i", 15, 42U, 15, 43U, 15, -42, 15, -43);
+ test_printf_one("%*.*u %*.*i", 14, 10, 13U, 14, 10, -14);
+ test_printf_one("%*x %*X %.*x %.*X", 15, 0x1AU, 15, 0x2BU, 15, 0x3CU, 15, 0x4DU);
+ test_printf_one("%#*x %#*X %#.*x %#.*X", 15, 0xA1U, 15, 0xB2U, 15, 0xC3U, 15, 0xD4U);
+ test_printf_one("%*.*x %*.*X", 14, 10, 0x1AU, 14, 10, 0x2BU);
+ test_printf_one("%#*.*x %#*.*X", 14, 10, 0x3CU, 14, 10, 0x4DU);
+
+ test_printf_one("%+.5i %+.5i % .7i % .7i", -15, 51, -15, 51);
+ test_printf_one("%+5.i %+5.i % 7.i % 7.i", -15, 51, -15, 51);
+
+ test_printf_one("%-10u %-10i %-10x %#-10X %- 10i", 1u, -2, 0xA2D2u, 0XB3F4u, -512);
+ test_printf_one("%-10.6u %-10.6i %-10.6x %#-10.6X %- 10.6i", 1u, -2, 0xA2D2u, 0XB3F4u, -512);
+ test_printf_one("%-6.10u %-6.10i %-6.10x %#-6.10X %- 6.10i", 3u, -4, 0x2A2Du, 0X3B4Fu, -215);
+ test_printf_one("%*.u %.*i %.*i", -4, 9u, -4, 8, -4, -6);
+
+ test_printf_one("%.0u %.0i %.0x %.0X", 0u, 0, 0u, 0u);
+ test_printf_one("%.*u %.*i %.*x %.*X", 0, 0u, 0, 0, 0, 0u, 0, 0u);
+ test_printf_one("%*u %*i %*x %*X", -1, 0u, -1, 0, -1, 0u, -1, 0u);
+
+ test_printf_one("%*s%*s%*s", 256, "", 256, "", 4096, ""); /* Test buf growing. */
+ test_printf_one("%0*i%0*i%0*i", 256, 0, 256, 0, 4096, 0); /* Test buf growing. */
+ test_printf_one("%0*i", INT16_MAX, 0); /* Poor programmer's memzero. */
+
+ /* Non printf-compatible behavior tests below. */
+ char16_t *s;
+
+ assert_se(s = xasprintf_status(0, "\n \r \r\n"));
+ assert_se(streq16(s, u"\r\n \r \r\r\n"));
+ s = mfree(s);
+
+ assert_se(s = xasprintf_status(EFI_SUCCESS, "%m"));
+ assert_se(streq16(s, u"Success"));
+ s = mfree(s);
+
+ assert_se(s = xasprintf_status(EFI_SUCCESS, "%15m"));
+ assert_se(streq16(s, u" Success"));
+ s = mfree(s);
+
+ assert_se(s = xasprintf_status(EFI_LOAD_ERROR, "%m"));
+ assert_se(streq16(s, u"Load error"));
+ s = mfree(s);
+
+ assert_se(s = xasprintf_status(0x42, "%m"));
+ assert_se(streq16(s, u"0x42"));
+ s = mfree(s);
+}
+
TEST(efi_memcmp) {
assert_se(efi_memcmp(NULL, NULL, 0) == 0);
assert_se(efi_memcmp(NULL, NULL, 1) == 0);

View File

@ -0,0 +1,965 @@
From e5dea043b9c20f648bffffd581a0e624f5622a16 Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Fri, 10 Jun 2022 19:06:57 +0200
Subject: [PATCH] boot: Use printf for error logging
This also drops the _stall suffix in anticipation of the next commit.
(cherry picked from commit c2c6203556f842820ca09e0653c123305f2ba6d2)
Related: RHEL-16952
---
src/boot/efi/assert.c | 12 ---------
src/boot/efi/boot.c | 54 +++++++++++++++++++-------------------
src/boot/efi/console.c | 6 ++---
src/boot/efi/cpio.c | 23 ++++++++--------
src/boot/efi/devicetree.c | 3 +--
src/boot/efi/drivers.c | 16 +++++------
src/boot/efi/linux.c | 10 +++----
src/boot/efi/linux_x86.c | 15 +++++------
src/boot/efi/log.c | 34 ++++++++++++++++++++++++
src/boot/efi/log.h | 9 +++++++
src/boot/efi/measure.c | 2 +-
src/boot/efi/meson.build | 3 ++-
src/boot/efi/pe.c | 2 +-
src/boot/efi/random-seed.c | 44 +++++++++++++++----------------
src/boot/efi/secure-boot.c | 11 ++++----
src/boot/efi/stub.c | 6 ++---
src/boot/efi/util.c | 30 +--------------------
src/boot/efi/util.h | 12 +--------
18 files changed, 141 insertions(+), 151 deletions(-)
delete mode 100644 src/boot/efi/assert.c
create mode 100644 src/boot/efi/log.c
create mode 100644 src/boot/efi/log.h
diff --git a/src/boot/efi/assert.c b/src/boot/efi/assert.c
deleted file mode 100644
index bb16d2bf93..0000000000
--- a/src/boot/efi/assert.c
+++ /dev/null
@@ -1,12 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-
-#include <efi.h>
-#include <efilib.h>
-
-#include "util.h"
-
-void efi_assert(const char *expr, const char *file, unsigned line, const char *function) {
- log_error_stall(L"systemd-boot assertion '%a' failed at %a:%u, function %a(). Halting.", expr, file, line, function);
- for (;;)
- BS->Stall(60 * 1000 * 1000);
-}
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index a39c356158..1e7b7a0fa7 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -587,14 +587,14 @@ static EFI_STATUS reboot_into_firmware(void) {
EFI_STATUS err;
if (!FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI))
- return log_error_status_stall(EFI_UNSUPPORTED, L"Reboot to firmware interface not supported.");
+ return log_error_status(EFI_UNSUPPORTED, "Reboot to firmware interface not supported.");
(void) efivar_get_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", &osind);
osind |= EFI_OS_INDICATIONS_BOOT_TO_FW_UI;
err = efivar_set_uint64_le(EFI_GLOBAL_GUID, L"OsIndications", osind, EFI_VARIABLE_NON_VOLATILE);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error setting OsIndications: %r", err);
+ return log_error_status(err, "Error setting OsIndications: %m");
RT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
assert_not_reached();
@@ -634,7 +634,7 @@ static bool menu_run(
config->console_mode_efivar : config->console_mode);
if (err != EFI_SUCCESS) {
clear_screen(COLOR_NORMAL);
- log_error_stall(L"Error switching console mode: %r", err);
+ log_error_status(err, "Error switching console mode: %m");
}
size_t line_width = 0, entry_padding = 3;
@@ -1177,7 +1177,7 @@ static void config_defaults_load_from_file(Config *config, char *content) {
else {
uint64_t u;
if (!parse_number8(value, &u, NULL) || u > TIMEOUT_TYPE_MAX) {
- log_error_stall(L"Error parsing 'timeout' config option: %a", value);
+ log_error("Error parsing 'timeout' config option: %s", value);
continue;
}
config->timeout_sec_config = u;
@@ -1188,7 +1188,7 @@ static void config_defaults_load_from_file(Config *config, char *content) {
if (streq8(key, "default")) {
if (value[0] == '@' && !strcaseeq8(value, "@saved")) {
- log_error_stall(L"Unsupported special entry identifier: %a", value);
+ log_error("Unsupported special entry identifier: %s", value);
continue;
}
free(config->entry_default_config);
@@ -1199,35 +1199,35 @@ static void config_defaults_load_from_file(Config *config, char *content) {
if (streq8(key, "editor")) {
err = parse_boolean(value, &config->editor);
if (err != EFI_SUCCESS)
- log_error_stall(L"Error parsing 'editor' config option: %a", value);
+ log_error("Error parsing 'editor' config option: %s", value);
continue;
}
if (streq8(key, "auto-entries")) {
err = parse_boolean(value, &config->auto_entries);
if (err != EFI_SUCCESS)
- log_error_stall(L"Error parsing 'auto-entries' config option: %a", value);
+ log_error("Error parsing 'auto-entries' config option: %s", value);
continue;
}
if (streq8(key, "auto-firmware")) {
err = parse_boolean(value, &config->auto_firmware);
if (err != EFI_SUCCESS)
- log_error_stall(L"Error parsing 'auto-firmware' config option: %a", value);
+ log_error("Error parsing 'auto-firmware' config option: %s", value);
continue;
}
if (streq8(key, "beep")) {
err = parse_boolean(value, &config->beep);
if (err != EFI_SUCCESS)
- log_error_stall(L"Error parsing 'beep' config option: %a", value);
+ log_error("Error parsing 'beep' config option: %s", value);
continue;
}
if (streq8(key, "reboot-for-bitlocker")) {
err = parse_boolean(value, &config->reboot_for_bitlocker);
if (err != EFI_SUCCESS)
- log_error_stall(L"Error parsing 'reboot-for-bitlocker' config option: %a", value);
+ log_error("Error parsing 'reboot-for-bitlocker' config option: %s", value);
}
if (streq8(key, "secure-boot-enroll")) {
@@ -1238,7 +1238,7 @@ static void config_defaults_load_from_file(Config *config, char *content) {
else if (streq8(value, "off"))
config->secure_boot_enroll = ENROLL_OFF;
else
- log_error_stall(L"Error parsing 'secure-boot-enroll' config option: %a", value);
+ log_error("Error parsing 'secure-boot-enroll' config option: %s", value);
continue;
}
@@ -1252,7 +1252,7 @@ static void config_defaults_load_from_file(Config *config, char *content) {
else {
uint64_t u;
if (!parse_number8(value, &u, NULL) || u > CONSOLE_MODE_RANGE_MAX) {
- log_error_stall(L"Error parsing 'console-mode' config option: %a", value);
+ log_error("Error parsing 'console-mode' config option: %s", value);
continue;
}
config->console_mode = u;
@@ -1356,7 +1356,7 @@ static void config_entry_bump_counters(ConfigEntry *entry, EFI_FILE *root_dir) {
strcpy16(file_info->FileName, entry->next_name);
err = handle->SetInfo(handle, &GenericFileInfo, file_info_size, file_info);
if (err != EFI_SUCCESS) {
- log_error_stall(L"Failed to rename '%s' to '%s', ignoring: %r", old_path, entry->next_name, err);
+ log_error_status(err, "Failed to rename '%ls' to '%ls', ignoring: %m", old_path, entry->next_name);
return;
}
@@ -1564,7 +1564,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
if (err == EFI_SUCCESS)
config->timeout_sec = config->timeout_sec_efivar;
else if (err != EFI_NOT_FOUND)
- log_error_stall(u"Error reading LoaderConfigTimeout EFI variable: %r", err);
+ log_error_status(err, "Error reading LoaderConfigTimeout EFI variable: %m");
err = efivar_get_timeout(u"LoaderConfigTimeoutOneShot", &config->timeout_sec);
if (err == EFI_SUCCESS) {
@@ -1573,7 +1573,7 @@ static void config_load_defaults(Config *config, EFI_FILE *root_dir) {
config->force_menu = true; /* force the menu when this is set */
} else if (err != EFI_NOT_FOUND)
- log_error_stall(u"Error reading LoaderConfigTimeoutOneShot EFI variable: %r", err);
+ log_error_status(err, "Error reading LoaderConfigTimeoutOneShot EFI variable: %m");
err = efivar_get_uint_string(LOADER_GUID, L"LoaderConfigConsoleMode", &value);
if (err == EFI_SUCCESS)
@@ -2327,38 +2327,38 @@ static EFI_STATUS image_start(
_cleanup_(file_closep) EFI_FILE *image_root = NULL;
err = open_volume(entry->device, &image_root);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error opening root path: %r", err);
+ return log_error_status(err, "Error opening root path: %m");
err = make_file_device_path(entry->device, entry->loader, &path);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error making file device path: %r", err);
+ return log_error_status(err, "Error making file device path: %m");
size_t initrd_size = 0;
_cleanup_free_ void *initrd = NULL;
_cleanup_free_ char16_t *options_initrd = NULL;
err = initrd_prepare(image_root, entry, &options_initrd, &initrd, &initrd_size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error preparing initrd: %r", err);
+ return log_error_status(err, "Error preparing initrd: %m");
err = shim_load_image(parent_image, path, &image);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error loading %s: %r", entry->loader, err);
+ return log_error_status(err, "Error loading %ls: %m", entry->loader);
if (entry->devicetree) {
err = devicetree_install(&dtstate, image_root, entry->devicetree);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error loading %s: %r", entry->devicetree, err);
+ return log_error_status(err, "Error loading %ls: %m", entry->devicetree);
}
_cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
err = initrd_register(initrd, initrd_size, &initrd_handle);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error registering initrd: %r", err);
+ return log_error_status(err, "Error registering initrd: %m");
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **) &loaded_image);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error getting LoadedImageProtocol handle: %r", err);
+ return log_error_status(err, "Error getting LoadedImageProtocol handle: %m");
char16_t *options = options_initrd ?: entry->options;
if (options) {
@@ -2382,7 +2382,7 @@ static EFI_STATUS image_start(
err = pe_kernel_info(loaded_image->ImageBase, &compat_address);
if (err != EFI_SUCCESS) {
if (err != EFI_UNSUPPORTED)
- return log_error_status_stall(err, L"Error finding kernel compat entry address: %r", err);
+ return log_error_status(err, "Error finding kernel compat entry address: %m");
} else if (compat_address > 0) {
EFI_IMAGE_ENTRY_POINT kernel_entry =
(EFI_IMAGE_ENTRY_POINT) ((uint8_t *) loaded_image->ImageBase + compat_address);
@@ -2395,7 +2395,7 @@ static EFI_STATUS image_start(
err = EFI_UNSUPPORTED;
}
- return log_error_status_stall(err, L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err);
+ return log_error_status(err, "Failed to execute %ls (%ls): %m", entry->title_show, entry->loader);
}
static void config_free(Config *config) {
@@ -2634,7 +2634,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
+ return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m");
(void) device_path_to_str(loaded_image->FilePath, &loaded_image_path);
@@ -2642,14 +2642,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = discover_root_dir(loaded_image, &root_dir);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Unable to open root directory: %r", err);
+ return log_error_status(err, "Unable to open root directory: %m");
(void) load_drivers(image, loaded_image, root_dir);
config_load_all_entries(&config, loaded_image, loaded_image_path, root_dir);
if (config.entry_count == 0) {
- log_error_stall(L"No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
+ log_error("No loader found. Configuration files in \\loader\\entries\\*.conf are needed.");
goto out;
}
diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c
index c3d9ff0e82..b876ff2bd7 100644
--- a/src/boot/efi/console.c
+++ b/src/boot/efi/console.c
@@ -83,7 +83,7 @@ EFI_STATUS console_key_read(uint64_t *key, uint64_t timeout_usec) {
err = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &timer);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error creating timer event: %r", err);
+ return log_error_status(err, "Error creating timer event: %m");
EFI_EVENT events[] = {
timer,
@@ -104,14 +104,14 @@ EFI_STATUS console_key_read(uint64_t *key, uint64_t timeout_usec) {
TimerRelative,
MIN(timeout_usec, watchdog_ping_usec) * 10);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error arming timer event: %r", err);
+ return log_error_status(err, "Error arming timer event: %m");
(void) BS->SetWatchdogTimer(watchdog_timeout_sec, 0x10000, 0, NULL);
err = BS->WaitForEvent(n_events, events, &index);
(void) BS->SetWatchdogTimer(watchdog_timeout_sec, 0x10000, 0, NULL);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error waiting for events: %r", err);
+ return log_error_status(err, "Error waiting for events: %m");
/* We have keyboard input, process it after this loop. */
if (timer != events[index])
diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c
index bb424a44d5..62773ded9a 100644
--- a/src/boot/efi/cpio.c
+++ b/src/boot/efi/cpio.c
@@ -326,7 +326,7 @@ static EFI_STATUS measure_cpio(
tpm_description,
&m);
if (err != EFI_SUCCESS) {
- log_error_stall(L"Unable to add initrd TPM measurement for PCR %u (%s), ignoring: %r", tpm_pcr[i], tpm_description, err);
+ log_error_status(err, "Unable to add initrd TPM measurement for PCR %u (%ls), ignoring: %m", tpm_pcr[i], tpm_description);
measured = false;
continue;
}
@@ -401,8 +401,7 @@ EFI_STATUS pack_cpio(
* its file handles. */
goto nothing;
if (err != EFI_SUCCESS)
- return log_error_status_stall(
- err, L"Unable to open root directory: %r", err);
+ return log_error_status(err, "Unable to open root directory: %m");
if (!dropin_dir)
dropin_dir = rel_dropin_dir = get_dropin_dir(loaded_image->FilePath);
@@ -412,14 +411,14 @@ EFI_STATUS pack_cpio(
/* No extra subdir, that's totally OK */
goto nothing;
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to open extra directory of loaded image: %r", err);
+ return log_error_status(err, "Failed to open extra directory of loaded image: %m");
for (;;) {
_cleanup_free_ char16_t *d = NULL;
err = readdir_harder(extra_dir, &dirent, &dirent_size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
+ return log_error_status(err, "Failed to read extra directory of loaded image: %m");
if (!dirent) /* End of directory */
break;
@@ -462,7 +461,7 @@ EFI_STATUS pack_cpio(
* archive. Otherwise the cpio archive cannot be unpacked, since the leading dirs won't exist. */
err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err);
+ return log_error_status(err, "Failed to pack cpio prefix: %m");
for (size_t i = 0; i < n_items; i++) {
_cleanup_free_ char *content = NULL;
@@ -470,7 +469,7 @@ EFI_STATUS pack_cpio(
err = file_read(extra_dir, items[i], 0, 0, &content, &contentsize);
if (err != EFI_SUCCESS) {
- log_error_status_stall(err, L"Failed to read %s, ignoring: %r", items[i], err);
+ log_error_status(err, "Failed to read %ls, ignoring: %m", items[i]);
continue;
}
@@ -482,12 +481,12 @@ EFI_STATUS pack_cpio(
&inode,
&buffer, &buffer_size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", dirent->FileName, err);
+ return log_error_status(err, "Failed to pack cpio file %ls: %m", dirent->FileName);
}
err = pack_cpio_trailer(&buffer, &buffer_size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
+ return log_error_status(err, "Failed to pack cpio trailer: %m");
err = measure_cpio(buffer, buffer_size, tpm_pcr, n_tpm_pcr, tpm_description, ret_measured);
if (err != EFI_SUCCESS)
@@ -539,7 +538,7 @@ EFI_STATUS pack_cpio_literal(
err = pack_cpio_prefix(target_dir_prefix, dir_mode, &inode, &buffer, &buffer_size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to pack cpio prefix: %r", err);
+ return log_error_status(err, "Failed to pack cpio prefix: %m");
err = pack_cpio_one(
target_filename,
@@ -549,11 +548,11 @@ EFI_STATUS pack_cpio_literal(
&inode,
&buffer, &buffer_size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to pack cpio file %s: %r", target_filename, err);
+ return log_error_status(err, "Failed to pack cpio file %ls: %m", target_filename);
err = pack_cpio_trailer(&buffer, &buffer_size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to pack cpio trailer: %r");
+ return log_error_status(err, "Failed to pack cpio trailer: %m");
err = measure_cpio(buffer, buffer_size, tpm_pcr, n_tpm_pcr, tpm_description, ret_measured);
if (err != EFI_SUCCESS)
diff --git a/src/boot/efi/devicetree.c b/src/boot/efi/devicetree.c
index f3c2e47e58..52f64a6e2f 100644
--- a/src/boot/efi/devicetree.c
+++ b/src/boot/efi/devicetree.c
@@ -36,8 +36,7 @@ static EFI_STATUS devicetree_fixup(struct devicetree_state *state, size_t len) {
err = BS->LocateProtocol(&EfiDtFixupProtocol, NULL, (void **) &fixup);
if (err != EFI_SUCCESS)
- return log_error_status_stall(EFI_SUCCESS,
- L"Could not locate device tree fixup protocol, skipping.");
+ return log_error_status(EFI_SUCCESS, "Could not locate device tree fixup protocol, skipping.");
size = devicetree_allocated(state);
err = fixup->Fixup(fixup, PHYSICAL_ADDRESS_TO_POINTER(state->addr), &size,
diff --git a/src/boot/efi/drivers.c b/src/boot/efi/drivers.c
index 41a7d8fe15..c76f8e0903 100644
--- a/src/boot/efi/drivers.c
+++ b/src/boot/efi/drivers.c
@@ -23,26 +23,26 @@ static EFI_STATUS load_one_driver(
spath = xpool_print(L"\\EFI\\systemd\\drivers\\%s", fname);
err = make_file_device_path(loaded_image->DeviceHandle, spath, &path);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error making file device path: %r", err);
+ return log_error_status(err, "Error making file device path: %m");
err = BS->LoadImage(false, parent_image, path, NULL, 0, &image);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to load image %s: %r", fname, err);
+ return log_error_status(err, "Failed to load image %ls: %m", fname);
err = BS->HandleProtocol(image, &LoadedImageProtocol, (void **)&loaded_image);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to find protocol in driver image %s: %r", fname, err);
+ return log_error_status(err, "Failed to find protocol in driver image %ls: %m", fname);
if (loaded_image->ImageCodeType != EfiBootServicesCode &&
loaded_image->ImageCodeType != EfiRuntimeServicesCode)
- return log_error_status_stall(EFI_INVALID_PARAMETER, L"Image %s is not a driver, refusing.", fname);
+ return log_error("Image %ls is not a driver, refusing.", fname);
err = BS->StartImage(image, NULL, NULL);
if (err != EFI_SUCCESS) {
/* EFI_ABORTED signals an initializing driver. It uses this error code on success
* so that it is unloaded after. */
if (err != EFI_ABORTED)
- log_error_stall(L"Failed to start image %s: %r", fname, err);
+ log_error_status(err, "Failed to start image %ls: %m", fname);
return err;
}
@@ -59,7 +59,7 @@ EFI_STATUS reconnect_all_drivers(void) {
err = BS->LocateHandleBuffer(AllHandles, NULL, NULL, &n_handles, &handles);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to get list of handles: %r", err);
+ return log_error_status(err, "Failed to get list of handles: %m");
for (size_t i = 0; i < n_handles; i++)
/* Some firmware gives us some bogus handles (or they might become bad due to
@@ -87,12 +87,12 @@ EFI_STATUS load_drivers(
if (err == EFI_NOT_FOUND)
return EFI_SUCCESS;
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to open \\EFI\\systemd\\drivers: %r", err);
+ return log_error_status(err, "Failed to open \\EFI\\systemd\\drivers: %m");
for (;;) {
err = readdir_harder(drivers_dir, &dirent, &dirent_size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to read extra directory of loaded image: %r", err);
+ return log_error_status(err, "Failed to read extra directory of loaded image: %m");
if (!dirent) /* End of directory */
break;
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
index 48801f9dd8..2ae68ec295 100644
--- a/src/boot/efi/linux.c
+++ b/src/boot/efi/linux.c
@@ -120,17 +120,17 @@ EFI_STATUS linux_exec(
initrd_length);
#endif
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, u"Bad kernel image: %r", err);
+ return log_error_status(err, "Bad kernel image: %m");
_cleanup_(unload_imagep) EFI_HANDLE kernel_image = NULL;
err = load_image(parent, linux_buffer, linux_length, &kernel_image);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, u"Error loading kernel image: %r", err);
+ return log_error_status(err, "Error loading kernel image: %m");
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
err = BS->HandleProtocol(kernel_image, &LoadedImageProtocol, (void **) &loaded_image);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, u"Error getting kernel loaded image protocol: %r", err);
+ return log_error_status(err, "Error getting kernel loaded image protocol: %m");
if (cmdline) {
loaded_image->LoadOptions = (void *) cmdline;
@@ -140,7 +140,7 @@ EFI_STATUS linux_exec(
_cleanup_(cleanup_initrd) EFI_HANDLE initrd_handle = NULL;
err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, u"Error registering initrd: %r", err);
+ return log_error_status(err, "Error registering initrd: %m");
err = BS->StartImage(kernel_image, NULL, NULL);
@@ -151,5 +151,5 @@ EFI_STATUS linux_exec(
err = compat_entry(kernel_image, ST);
}
- return log_error_status_stall(err, u"Error starting kernel image: %r", err);
+ return log_error_status(err, "Error starting kernel image: %m");
}
diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c
index 6a5e431107..cbd92201b6 100644
--- a/src/boot/efi/linux_x86.c
+++ b/src/boot/efi/linux_x86.c
@@ -141,28 +141,27 @@ EFI_STATUS linux_exec_efi_handover(
const BootParams *image_params = (const BootParams *) linux_buffer;
if (image_params->hdr.header != SETUP_MAGIC || image_params->hdr.boot_flag != BOOT_FLAG_MAGIC)
- return log_error_status_stall(EFI_UNSUPPORTED, u"Unsupported kernel image.");
+ return log_error_status(EFI_UNSUPPORTED, "Unsupported kernel image.");
if (image_params->hdr.version < SETUP_VERSION_2_11)
- return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel too old.");
+ return log_error_status(EFI_UNSUPPORTED, "Kernel too old.");
if (!image_params->hdr.relocatable_kernel)
- return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel is not relocatable.");
+ return log_error_status(EFI_UNSUPPORTED, "Kernel is not relocatable.");
/* The xloadflags were added in version 2.12+ of the boot protocol but the handover support predates
* that, so we cannot safety-check this for 2.11. */
if (image_params->hdr.version >= SETUP_VERSION_2_12 &&
!FLAGS_SET(image_params->hdr.xloadflags, XLF_EFI_HANDOVER))
- return log_error_status_stall(EFI_UNSUPPORTED, u"Kernel does not support EFI handover protocol.");
+ return log_error_status(EFI_UNSUPPORTED, "Kernel does not support EFI handover protocol.");
bool can_4g = image_params->hdr.version >= SETUP_VERSION_2_12 &&
FLAGS_SET(image_params->hdr.xloadflags, XLF_CAN_BE_LOADED_ABOVE_4G);
if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + linux_length > UINT32_MAX)
- return log_error_status_stall(
+ return log_error_status(
EFI_UNSUPPORTED,
- u"Unified kernel image was loaded above 4G, but kernel lacks support.");
+ "Unified kernel image was loaded above 4G, but kernel lacks support.");
if (!can_4g && POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer) + initrd_length > UINT32_MAX)
- return log_error_status_stall(
- EFI_UNSUPPORTED, u"Initrd is above 4G, but kernel lacks support.");
+ return log_error_status(EFI_UNSUPPORTED, "Initrd is above 4G, but kernel lacks support.");
_cleanup_pages_ Pages boot_params_page = xmalloc_pages(
can_4g ? AllocateAnyPages : AllocateMaxAddress,
diff --git a/src/boot/efi/log.c b/src/boot/efi/log.c
new file mode 100644
index 0000000000..38e7c5a8a8
--- /dev/null
+++ b/src/boot/efi/log.c
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include "log.h"
+
+void efi_assert(const char *expr, const char *file, unsigned line, const char *function) {
+ log_error("systemd-boot assertion '%s' failed at %s:%u@%s. Halting.", expr, file, line, function);
+ for (;;)
+ BS->Stall(60 * 1000 * 1000);
+}
+
+EFI_STATUS log_internal(EFI_STATUS status, const char *format, ...) {
+ assert(format);
+
+ int32_t attr = ST->ConOut->Mode->Attribute;
+
+ if (ST->ConOut->Mode->CursorColumn > 0)
+ ST->ConOut->OutputString(ST->ConOut, (char16_t *) u"\r\n");
+ ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTRED | EFI_BACKGROUND_BLACK);
+
+ va_list ap;
+ va_start(ap, format);
+ vprintf_status(status, format, ap);
+ va_end(ap);
+
+ ST->ConOut->OutputString(ST->ConOut, (char16_t *) u"\r\n");
+ ST->ConOut->SetAttribute(ST->ConOut, attr);
+
+ /* Give the user a chance to see the message. */
+ BS->Stall(3 * 1000 * 1000);
+ return status;
+}
diff --git a/src/boot/efi/log.h b/src/boot/efi/log.h
new file mode 100644
index 0000000000..c6e8d626ce
--- /dev/null
+++ b/src/boot/efi/log.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include "efi-string.h"
+
+_gnu_printf_(2, 3) EFI_STATUS log_internal(EFI_STATUS status, const char *format, ...);
+#define log_error_status(status, ...) log_internal(status, __VA_ARGS__)
+#define log_error(...) log_internal(EFI_INVALID_PARAMETER, __VA_ARGS__)
+#define log_oom() log_internal(EFI_OUT_OF_RESOURCES, "Out of memory.")
diff --git a/src/boot/efi/measure.c b/src/boot/efi/measure.c
index 27a0f06475..52c5cd7ae8 100644
--- a/src/boot/efi/measure.c
+++ b/src/boot/efi/measure.c
@@ -207,7 +207,7 @@ EFI_STATUS tpm_log_load_options(const char16_t *load_options, bool *ret_measured
err = tpm_log_event(pcr, POINTER_TO_PHYSICAL_ADDRESS(load_options), strsize16(load_options), load_options, &m);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Unable to add load options (i.e. kernel command) line measurement to PCR %u: %r", pcr, err);
+ return log_error_status(err, "Unable to add load options (i.e. kernel command) line measurement to PCR %u: %m", pcr);
measured = measured < 0 ? m : (measured && m);
}
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index ed332262e8..09c40a280b 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -359,6 +359,7 @@ efi_headers = files(
'graphics.h',
'initrd.h',
'linux.h',
+ 'log.h',
'measure.h',
'missing_efi.h',
'part-discovery.h',
@@ -372,7 +373,6 @@ efi_headers = files(
)
common_sources = files(
- 'assert.c',
'console.c',
'devicetree.c',
'drivers.c',
@@ -380,6 +380,7 @@ common_sources = files(
'efi-string.c',
'graphics.c',
'initrd.c',
+ 'log.c',
'measure.c',
'part-discovery.c',
'pe.c',
diff --git a/src/boot/efi/pe.c b/src/boot/efi/pe.c
index 65308639f6..c946ce2b0a 100644
--- a/src/boot/efi/pe.c
+++ b/src/boot/efi/pe.c
@@ -158,7 +158,7 @@ static void locate_sections(
if (in_memory) {
if (prev_section_addr > sect->VirtualAddress)
- log_error_stall(u"Overlapping PE sections detected. Boot may fail due to image memory corruption!");
+ log_error("Overlapping PE sections detected. Boot may fail due to image memory corruption!");
prev_section_addr = sect->VirtualAddress + sect->VirtualSize;
}
diff --git a/src/boot/efi/random-seed.c b/src/boot/efi/random-seed.c
index a52934a901..e971f48097 100644
--- a/src/boot/efi/random-seed.c
+++ b/src/boot/efi/random-seed.c
@@ -48,7 +48,7 @@ static EFI_STATUS acquire_rng(void *ret, size_t size) {
err = rng->GetRNG(rng, NULL, size, ret);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to acquire RNG data: %r", err);
+ return log_error_status(err, "Failed to acquire RNG data: %m");
return EFI_SUCCESS;
}
@@ -63,12 +63,12 @@ static EFI_STATUS acquire_system_token(void **ret, size_t *ret_size) {
err = efivar_get_raw(LOADER_GUID, L"LoaderSystemToken", &data, &size);
if (err != EFI_SUCCESS) {
if (err != EFI_NOT_FOUND)
- log_error_stall(L"Failed to read LoaderSystemToken EFI variable: %r", err);
+ log_error_status(err, "Failed to read LoaderSystemToken EFI variable: %m");
return err;
}
if (size <= 0)
- return log_error_status_stall(EFI_NOT_FOUND, L"System token too short, ignoring.");
+ return log_error_status(EFI_NOT_FOUND, "System token too short, ignoring.");
*ret = TAKE_PTR(data);
*ret_size = size;
@@ -201,29 +201,29 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
0);
if (err != EFI_SUCCESS) {
if (err != EFI_NOT_FOUND && err != EFI_WRITE_PROTECTED)
- log_error_stall(L"Failed to open random seed file: %r", err);
+ log_error_status(err, "Failed to open random seed file: %m");
return err;
}
err = get_file_info_harder(handle, &info, NULL);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to get file info for random seed: %r", err);
+ return log_error_status(err, "Failed to get file info for random seed: %m");
size = info->FileSize;
if (size < RANDOM_MAX_SIZE_MIN)
- return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too short.");
+ return log_error("Random seed file is too short.");
if (size > RANDOM_MAX_SIZE_MAX)
- return log_error_status_stall(EFI_INVALID_PARAMETER, L"Random seed file is too large.");
+ return log_error("Random seed file is too large.");
seed = xmalloc(size);
rsize = size;
err = handle->Read(handle, &rsize, seed);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to read random seed file: %r", err);
+ return log_error_status(err, "Failed to read random seed file: %m");
if (rsize != size) {
explicit_bzero_safe(seed, rsize);
- return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short read on random seed file.");
+ return log_error_status(EFI_PROTOCOL_ERROR, "Short read on random seed file.");
}
sha256_process_bytes(&size, sizeof(size), &hash);
@@ -232,14 +232,14 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
err = handle->SetPosition(handle, 0);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
+ return log_error_status(err, "Failed to seek to beginning of random seed file: %m");
/* Let's also include the UEFI monotonic counter (which is supposedly increasing on every single
* boot) in the hash, so that even if the changes to the ESP for some reason should not be
* persistent, the random seed we generate will still be different on every single boot. */
err = BS->GetNextMonotonicCount(&uefi_monotonic_counter);
if (err != EFI_SUCCESS && !seeded_by_efi)
- return log_error_status_stall(err, L"Failed to acquire UEFI monotonic counter: %r", err);
+ return log_error_status(err, "Failed to acquire UEFI monotonic counter: %m");
size = sizeof(uefi_monotonic_counter);
sha256_process_bytes(&size, sizeof(size), &hash);
sha256_process_bytes(&uefi_monotonic_counter, size, &hash);
@@ -264,26 +264,26 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
if (size < info->FileSize) {
err = handle->SetPosition(handle, size);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to seek to offset of random seed file: %r", err);
+ return log_error_status(err, "Failed to seek to offset of random seed file: %m");
wsize = info->FileSize - size;
err = handle->Write(handle, &wsize, seed /* All zeros now */);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
+ return log_error_status(err, "Failed to write random seed file: %m");
if (wsize != info->FileSize - size)
- return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
+ return log_error_status(EFI_PROTOCOL_ERROR, "Short write on random seed file.");
err = handle->Flush(handle);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
+ return log_error_status(err, "Failed to flush random seed file: %m");
err = handle->SetPosition(handle, 0);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to seek to beginning of random seed file: %r", err);
+ return log_error_status(err, "Failed to seek to beginning of random seed file: %m");
/* We could truncate the file here with something like:
*
* info->FileSize = size;
* err = handle->SetInfo(handle, &GenericFileInfo, info->Size, info);
* if (err != EFI_SUCCESS)
- * return log_error_status_stall(err, L"Failed to truncate random seed file: %r", err);
+ * return log_error_status(err, "Failed to truncate random seed file: %u");
*
* But this is considered slightly risky, because EFI filesystem drivers are a little bit
* flimsy. So instead we rely on userspace eventually truncating this when it writes a new
@@ -293,18 +293,18 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
wsize = size;
err = handle->Write(handle, &wsize, random_bytes);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to write random seed file: %r", err);
+ return log_error_status(err, "Failed to write random seed file: %m");
if (wsize != size)
- return log_error_status_stall(EFI_PROTOCOL_ERROR, L"Short write on random seed file.");
+ return log_error_status(EFI_PROTOCOL_ERROR, "Short write on random seed file.");
err = handle->Flush(handle);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to flush random seed file: %r", err);
+ return log_error_status(err, "Failed to flush random seed file: %m");
err = BS->AllocatePool(EfiACPIReclaimMemory,
offsetof(struct linux_efi_random_seed, seed) + DESIRED_SEED_SIZE,
(void **) &new_seed_table);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to allocate EFI table for random seed: %r", err);
+ return log_error_status(err, "Failed to allocate EFI table for random seed: %m");
new_seed_table->size = DESIRED_SEED_SIZE;
/* hash = hash_key || 1 */
@@ -316,7 +316,7 @@ EFI_STATUS process_random_seed(EFI_FILE *root_dir) {
err = BS->InstallConfigurationTable(&(EFI_GUID)LINUX_EFI_RANDOM_SEED_TABLE_GUID, new_seed_table);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed to install EFI table for random seed: %r", err);
+ return log_error_status(err, "Failed to install EFI table for random seed: %m");
TAKE_PTR(new_seed_table);
if (previous_seed_table) {
diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c
index 3f3a222b5e..6b6d48277e 100644
--- a/src/boot/efi/secure-boot.c
+++ b/src/boot/efi/secure-boot.c
@@ -66,10 +66,9 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
continue;
}
if (err != EFI_SUCCESS)
- return log_error_status_stall(
+ return log_error_status(
err,
- L"Error waiting for user input to enroll Secure Boot keys: %r",
- err);
+ "Error waiting for user input to enroll Secure Boot keys: %m");
/* user aborted, returning EFI_SUCCESS here allows the user to go back to the menu */
return EFI_SUCCESS;
@@ -80,7 +79,7 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
err = open_directory(root_dir, path, &dir);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Failed opening keys directory %s: %r", path, err);
+ return log_error_status(err, "Failed opening keys directory %ls: %m", path);
struct {
const char16_t *name;
@@ -98,7 +97,7 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
for (size_t i = 0; i < ELEMENTSOF(sb_vars); i++) {
err = file_read(dir, sb_vars[i].filename, 0, 0, &sb_vars[i].buffer, &sb_vars[i].size);
if (err != EFI_SUCCESS) {
- log_error_stall(L"Failed reading file %s\\%s: %r", path, sb_vars[i].filename, err);
+ log_error_status(err, "Failed reading file %ls\\%ls: %m", path, sb_vars[i].filename);
goto out_deallocate;
}
}
@@ -112,7 +111,7 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
err = efivar_set_raw(&sb_vars[i].vendor, sb_vars[i].name, sb_vars[i].buffer, sb_vars[i].size, sb_vars_opts);
if (err != EFI_SUCCESS) {
- log_error_stall(L"Failed to write %s secure boot variable: %r", sb_vars[i].name, err);
+ log_error_status(err, "Failed to write %ls secure boot variable: %m", sb_vars[i].name);
goto out_deallocate;
}
}
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 433fef548c..f9c023e11c 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -207,7 +207,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
NULL,
EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (err != EFI_SUCCESS)
- return log_error_status_stall(err, L"Error getting a LoadedImageProtocol handle: %r", err);
+ return log_error_status(err, "Error getting a LoadedImageProtocol handle: %m");
if (efivar_get_uint64_le(LOADER_GUID, L"LoaderFeatures", &loader_features) != EFI_SUCCESS ||
!FLAGS_SET(loader_features, EFI_LOADER_FEATURE_RANDOM_SEED)) {
@@ -222,7 +222,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_LINUX] == 0) {
if (err == EFI_SUCCESS)
err = EFI_NOT_FOUND;
- return log_error_status_stall(err, L"Unable to locate embedded .linux section: %r", err);
+ return log_error_status(err, "Unable to locate embedded .linux section: %m");
}
/* Measure all "payload" of this PE image into a separate PCR (i.e. where nothing else is written
@@ -417,7 +417,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
err = devicetree_install_from_memory(
&dt_state, PHYSICAL_ADDRESS_TO_POINTER(dt_base), dt_size);
if (err != EFI_SUCCESS)
- log_error_stall(L"Error loading embedded devicetree: %r", err);
+ log_error_status(err, "Error loading embedded devicetree: %m");
}
err = linux_exec(image, cmdline,
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index 7596bc3edc..320bddec1b 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -325,34 +325,6 @@ EFI_STATUS file_read(EFI_FILE *dir, const char16_t *name, size_t off, size_t siz
return err;
}
-void log_error_stall(const char16_t *fmt, ...) {
- va_list args;
-
- assert(fmt);
-
- int32_t attr = ST->ConOut->Mode->Attribute;
- ST->ConOut->SetAttribute(ST->ConOut, EFI_LIGHTRED|EFI_BACKGROUND_BLACK);
-
- if (ST->ConOut->Mode->CursorColumn > 0)
- Print(L"\n");
-
- va_start(args, fmt);
- VPrint(fmt, args);
- va_end(args);
-
- Print(L"\n");
-
- ST->ConOut->SetAttribute(ST->ConOut, attr);
-
- /* Give the user a chance to see the message. */
- BS->Stall(3 * 1000 * 1000);
-}
-
-EFI_STATUS log_oom(void) {
- log_error_stall(L"Out of memory.");
- return EFI_OUT_OF_RESOURCES;
-}
-
void print_at(size_t x, size_t y, size_t attr, const char16_t *str) {
assert(str);
ST->ConOut->SetCursorPosition(ST->ConOut, x, y);
@@ -572,7 +544,7 @@ void hexdump(const char16_t *prefix, const void *data, size_t size) {
buf[size*2] = 0;
- log_error_stall(L"%s[%" PRIuN "]: %s", prefix, size, buf);
+ log_error("%ls[%zu]: %ls", prefix, size, buf);
}
#endif
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index b97dc9768c..771f11c8bd 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -5,6 +5,7 @@
#include <efilib.h>
#include <stddef.h>
+#include "log.h"
#include "string-util-fundamental.h"
#define UINTN_MAX (~(UINTN)0)
@@ -139,17 +140,6 @@ static inline void unload_imagep(EFI_HANDLE *image) {
&(const EFI_GUID) { 0x4a67b082, 0x0a4c, 0x41cf, { 0xb6, 0xc7, 0x44, 0x0b, 0x29, 0xbb, 0x8c, 0x4f } }
#define EFI_GLOBAL_GUID &(const EFI_GUID) EFI_GLOBAL_VARIABLE
-void log_error_stall(const char16_t *fmt, ...);
-EFI_STATUS log_oom(void);
-
-/* This works just like log_error_errno() from userspace, but requires you
- * to provide err a second time if you want to use %r in the message! */
-#define log_error_status_stall(err, fmt, ...) \
- ({ \
- log_error_stall(fmt, ##__VA_ARGS__); \
- err; \
- })
-
void print_at(size_t x, size_t y, size_t attr, const char16_t *str);
void clear_screen(size_t attr);

View File

@ -0,0 +1,220 @@
From e2493416cd85725a1198a391f9b1f93e1e9db88e Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Sun, 5 Jun 2022 13:19:21 +0200
Subject: [PATCH] boot: Introduce log_wait
Instead of stalling for every log message as it appears we now wait for
several messages at strategic locations.
(cherry picked from commit 6ac54809deefddccc7861b5a2cfa4d766cf1aa3b)
Related: RHEL-16952
---
src/boot/efi/boot.c | 18 +++++++++++++-----
src/boot/efi/console.c | 1 +
src/boot/efi/graphics.c | 7 ++++---
src/boot/efi/linux.c | 1 +
src/boot/efi/linux_x86.c | 1 +
src/boot/efi/log.c | 13 +++++++++++--
src/boot/efi/log.h | 1 +
src/boot/efi/stub.c | 19 +++++++++++++------
src/boot/efi/util.c | 1 +
9 files changed, 46 insertions(+), 16 deletions(-)
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 1e7b7a0fa7..1e94aa57b1 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -2612,7 +2612,7 @@ static EFI_STATUS discover_root_dir(EFI_LOADED_IMAGE_PROTOCOL *loaded_image, EFI
return open_volume(loaded_image->DeviceHandle, ret_dir);
}
-EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+static EFI_STATUS real_main(EFI_HANDLE image) {
EFI_LOADED_IMAGE_PROTOCOL *loaded_image;
_cleanup_(file_closep) EFI_FILE *root_dir = NULL;
_cleanup_(config_free) Config config = {};
@@ -2621,11 +2621,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
uint64_t init_usec;
bool menu = false;
- InitializeLib(image, sys_table);
init_usec = time_usec();
- debug_hook(L"systemd-boot");
- /* Uncomment the next line if you need to wait for debugger. */
- // debug_break();
err = BS->OpenProtocol(image,
&LoadedImageProtocol,
@@ -2714,3 +2710,15 @@ out:
BS->CloseProtocol(image, &LoadedImageProtocol, image, NULL);
return err;
}
+
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ InitializeLib(image, sys_table);
+
+ debug_hook(L"systemd-boot");
+ /* Uncomment the next line if you need to wait for debugger. */
+ // debug_break();
+
+ EFI_STATUS err = real_main(image);
+ log_wait();
+ return err;
+}
diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c
index b876ff2bd7..001b82854b 100644
--- a/src/boot/efi/console.c
+++ b/src/boot/efi/console.c
@@ -188,6 +188,7 @@ static EFI_STATUS change_mode(int64_t mode) {
mode = CLAMP(mode, CONSOLE_MODE_RANGE_MIN, CONSOLE_MODE_RANGE_MAX);
old_mode = MAX(CONSOLE_MODE_RANGE_MIN, ST->ConOut->Mode->Mode);
+ log_wait();
err = ST->ConOut->SetMode(ST->ConOut, mode);
if (err == EFI_SUCCESS)
return EFI_SUCCESS;
diff --git a/src/boot/efi/graphics.c b/src/boot/efi/graphics.c
index dc646bce1f..350d1bc434 100644
--- a/src/boot/efi/graphics.c
+++ b/src/boot/efi/graphics.c
@@ -25,16 +25,17 @@ EFI_STATUS graphics_mode(bool on) {
return err == EFI_NOT_FOUND ? EFI_SUCCESS : err;
/* check current mode */
- err =ConsoleControl->GetMode(ConsoleControl, &current, &uga_exists, &stdin_locked);
+ err = ConsoleControl->GetMode(ConsoleControl, &current, &uga_exists, &stdin_locked);
if (err != EFI_SUCCESS)
return err;
/* do not touch the mode */
- new = on ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText;
+ new = on ? EfiConsoleControlScreenGraphics : EfiConsoleControlScreenText;
if (new == current)
return EFI_SUCCESS;
- err =ConsoleControl->SetMode(ConsoleControl, new);
+ log_wait();
+ err = ConsoleControl->SetMode(ConsoleControl, new);
/* some firmware enables the cursor when switching modes */
ST->ConOut->EnableCursor(ST->ConOut, false);
diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c
index 2ae68ec295..727e507101 100644
--- a/src/boot/efi/linux.c
+++ b/src/boot/efi/linux.c
@@ -142,6 +142,7 @@ EFI_STATUS linux_exec(
if (err != EFI_SUCCESS)
return log_error_status(err, "Error registering initrd: %m");
+ log_wait();
err = BS->StartImage(kernel_image, NULL, NULL);
/* Try calling the kernel compat entry point if one exists. */
diff --git a/src/boot/efi/linux_x86.c b/src/boot/efi/linux_x86.c
index cbd92201b6..eaae988d97 100644
--- a/src/boot/efi/linux_x86.c
+++ b/src/boot/efi/linux_x86.c
@@ -209,6 +209,7 @@ EFI_STATUS linux_exec_efi_handover(
boot_params->hdr.ramdisk_size = initrd_length;
boot_params->ext_ramdisk_size = ((uint64_t) initrd_length) >> 32;
+ log_wait();
linux_efi_handover(parent, (uintptr_t) linux_buffer, boot_params);
return EFI_LOAD_ERROR;
}
diff --git a/src/boot/efi/log.c b/src/boot/efi/log.c
index 38e7c5a8a8..b1a613e4e5 100644
--- a/src/boot/efi/log.c
+++ b/src/boot/efi/log.c
@@ -5,6 +5,8 @@
#include "log.h"
+static unsigned log_count = 0;
+
void efi_assert(const char *expr, const char *file, unsigned line, const char *function) {
log_error("systemd-boot assertion '%s' failed at %s:%u@%s. Halting.", expr, file, line, function);
for (;;)
@@ -28,7 +30,14 @@ EFI_STATUS log_internal(EFI_STATUS status, const char *format, ...) {
ST->ConOut->OutputString(ST->ConOut, (char16_t *) u"\r\n");
ST->ConOut->SetAttribute(ST->ConOut, attr);
- /* Give the user a chance to see the message. */
- BS->Stall(3 * 1000 * 1000);
+ log_count++;
return status;
}
+
+void log_wait(void) {
+ if (log_count == 0)
+ return;
+
+ BS->Stall(MIN(4u, log_count) * 2500 * 1000);
+ log_count = 0;
+}
diff --git a/src/boot/efi/log.h b/src/boot/efi/log.h
index c6e8d626ce..f24034fd78 100644
--- a/src/boot/efi/log.h
+++ b/src/boot/efi/log.h
@@ -3,6 +3,7 @@
#include "efi-string.h"
+void log_wait(void);
_gnu_printf_(2, 3) EFI_STATUS log_internal(EFI_STATUS status, const char *format, ...);
#define log_error_status(status, ...) log_internal(status, __VA_ARGS__)
#define log_error(...) log_internal(EFI_INVALID_PARAMETER, __VA_ARGS__)
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index f9c023e11c..f71f041a2f 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -180,7 +180,7 @@ static bool use_load_options(
return true;
}
-EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+static EFI_STATUS real_main(EFI_HANDLE image) {
_cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
size_t linux_size, initrd_size, dt_size;
@@ -194,11 +194,6 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
uint64_t loader_features = 0;
EFI_STATUS err;
- InitializeLib(image, sys_table);
- debug_hook(L"systemd-stub");
- /* Uncomment the next line if you need to wait for debugger. */
- // debug_break();
-
err = BS->OpenProtocol(
image,
&LoadedImageProtocol,
@@ -426,3 +421,15 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
graphics_mode(false);
return err;
}
+
+EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
+ InitializeLib(image, sys_table);
+
+ debug_hook(L"systemd-stub");
+ /* Uncomment the next line if you need to wait for debugger. */
+ // debug_break();
+
+ EFI_STATUS err = real_main(image);
+ log_wait();
+ return err;
+}
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index 320bddec1b..aa7b1fa1a2 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -333,6 +333,7 @@ void print_at(size_t x, size_t y, size_t attr, const char16_t *str) {
}
void clear_screen(size_t attr) {
+ log_wait();
ST->ConOut->SetAttribute(ST->ConOut, attr);
ST->ConOut->ClearScreen(ST->ConOut);
}

View File

@ -0,0 +1,21 @@
From ee1dfadec7be7c64a2bc2b34a1e4195b9048c46f Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Thu, 18 Aug 2022 13:41:49 +0200
Subject: [PATCH] boot: Add log_trace debugging helper
(cherry picked from commit 5966c54df4668abc17ae12c40fb0c30d31e80998)
Related: RHEL-16952
---
src/boot/efi/log.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/boot/efi/log.h b/src/boot/efi/log.h
index f24034fd78..9bdcfad923 100644
--- a/src/boot/efi/log.h
+++ b/src/boot/efi/log.h
@@ -8,3 +8,4 @@ _gnu_printf_(2, 3) EFI_STATUS log_internal(EFI_STATUS status, const char *format
#define log_error_status(status, ...) log_internal(status, __VA_ARGS__)
#define log_error(...) log_internal(EFI_INVALID_PARAMETER, __VA_ARGS__)
#define log_oom() log_internal(EFI_OUT_OF_RESOURCES, "Out of memory.")
+#define log_trace() log_internal(EFI_SUCCESS, "%s:%i@%s", __FILE__, __LINE__, __func__)

View File

@ -0,0 +1,78 @@
From b3fb286a49f0a4254f49ff88d1ed520e878c5cca Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Thu, 18 Aug 2022 13:43:19 +0200
Subject: [PATCH] tree-wide: Use __func__ in asserts
clang puts the whole function signature in __PRETTY_FUNCTION__, which is
a bit excessive for something that can already be figured out by using
the line number.
(cherry picked from commit 5a9b91576630f82ca72a932b5195654dbb04d67e)
Related: RHEL-16952
---
src/basic/macro.h | 6 +++---
src/fundamental/macro-fundamental.h | 6 +++---
src/journal/test-journal-interleaving.c | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/basic/macro.h b/src/basic/macro.h
index 9cb7ae5077..2d378454a2 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -194,12 +194,12 @@ static inline int __coverity_check_and_return__(int condition) {
#define assert_message_se(expr, message) \
do { \
if (_unlikely_(!(expr))) \
- log_assert_failed(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
+ log_assert_failed(message, PROJECT_FILE, __LINE__, __func__); \
} while (false)
#define assert_log(expr, message) ((_likely_(expr)) \
? (true) \
- : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__), false))
+ : (log_assert_failed_return(message, PROJECT_FILE, __LINE__, __func__), false))
#endif /* __COVERITY__ */
@@ -214,7 +214,7 @@ static inline int __coverity_check_and_return__(int condition) {
#endif
#define assert_not_reached() \
- log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__)
+ log_assert_failed_unreachable(PROJECT_FILE, __LINE__, __func__)
#define assert_return(expr, r) \
do { \
diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h
index 1c4c445e4e..e226d8d411 100644
--- a/src/fundamental/macro-fundamental.h
+++ b/src/fundamental/macro-fundamental.h
@@ -73,11 +73,11 @@
#define assert(expr)
#define assert_not_reached() __builtin_unreachable()
#else
- #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
- #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __PRETTY_FUNCTION__)
+ #define assert(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
+ #define assert_not_reached() efi_assert("Code should not be reached", __FILE__, __LINE__, __func__)
#endif
#define static_assert _Static_assert
- #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __PRETTY_FUNCTION__); })
+ #define assert_se(expr) ({ _likely_(expr) ? VOID_0 : efi_assert(#expr, __FILE__, __LINE__, __func__); })
#endif
/* This passes the argument through after (if asserts are enabled) checking that it is not null. */
diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c
index b3ae4b8143..fb38cc7e82 100644
--- a/src/journal/test-journal-interleaving.c
+++ b/src/journal/test-journal-interleaving.c
@@ -30,7 +30,7 @@ _noreturn_ static void log_assert_errno(const char *text, int error, const char
do { \
int _r_ = (expr); \
if (_unlikely_(_r_ < 0)) \
- log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __PRETTY_FUNCTION__); \
+ log_assert_errno(#expr, -_r_, PROJECT_FILE, __LINE__, __func__); \
} while (false)
static ManagedJournalFile *test_open(const char *name) {

View File

@ -0,0 +1,326 @@
From a15ea7473b6e54c3019daf2a894d681c0928a132 Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Sun, 5 Jun 2022 15:08:07 +0200
Subject: [PATCH] boot: Drop use of xpool_print/SPrint
(cherry picked from commit 2f3c3b0bee5534f2338439f04b0aa517479f8b76)
Related: RHEL-16952
---
src/boot/efi/boot.c | 73 +++++++++++++++++++++---------------------
src/boot/efi/cpio.c | 2 +-
src/boot/efi/drivers.c | 2 +-
src/boot/efi/stub.c | 6 ++--
src/boot/efi/util.h | 1 -
src/boot/efi/vmm.c | 3 +-
6 files changed, 43 insertions(+), 44 deletions(-)
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 1e94aa57b1..64a9eda24e 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -3,6 +3,7 @@
#include <efi.h>
#include <efigpt.h>
#include <efilib.h>
+#include <inttypes.h>
#include "bcd.h"
#include "bootspec-fundamental.h"
@@ -417,7 +418,7 @@ static char16_t *update_timeout_efivar(uint32_t *t, bool inc) {
case TIMEOUT_MENU_HIDDEN:
return xstrdup16(u"Menu disabled. Hold down key at bootup to show menu.");
default:
- return xpool_print(L"Menu timeout set to %u s.", *t);
+ return xasprintf("Menu timeout set to %u s.", *t);
}
}
@@ -749,7 +750,7 @@ static bool menu_run(
if (timeout_remain > 0) {
free(status);
- status = xpool_print(L"Boot in %u s.", timeout_remain);
+ status = xasprintf("Boot in %u s.", timeout_remain);
}
if (status) {
@@ -928,9 +929,9 @@ static bool menu_run(
break;
case KEYPRESS(0, 0, 'v'):
- status = xpool_print(
- L"systemd-boot " GIT_VERSION L" (" EFI_MACHINE_TYPE_NAME L"), "
- L"UEFI Specification %u.%02u, Vendor %s %u.%02u",
+ status = xasprintf(
+ "systemd-boot " GIT_VERSION " (" EFI_MACHINE_TYPE_NAME "), "
+ "UEFI Specification %u.%02u, Vendor %ls %u.%02u",
ST->Hdr.Revision >> 16,
ST->Hdr.Revision & 0xffff,
ST->FirmwareVendor,
@@ -952,10 +953,12 @@ static bool menu_run(
case KEYPRESS(0, 0, 'r'):
err = console_set_mode(CONSOLE_MODE_NEXT);
if (err != EFI_SUCCESS)
- status = xpool_print(L"Error changing console mode: %r", err);
+ status = xasprintf_status(err, "Error changing console mode: %m");
else {
config->console_mode_efivar = ST->ConOut->Mode->Mode;
- status = xpool_print(L"Console mode changed to %ld.", config->console_mode_efivar);
+ status = xasprintf(
+ "Console mode changed to %" PRIi64 ".",
+ config->console_mode_efivar);
}
new_mode = true;
break;
@@ -965,10 +968,13 @@ static bool menu_run(
err = console_set_mode(config->console_mode == CONSOLE_MODE_KEEP ?
console_mode_initial : config->console_mode);
if (err != EFI_SUCCESS)
- status = xpool_print(L"Error resetting console mode: %r", err);
+ status = xasprintf_status(err, "Error resetting console mode: %m");
else
- status = xpool_print(L"Console mode reset to %s default.",
- config->console_mode == CONSOLE_MODE_KEEP ? L"firmware" : L"configuration file");
+ status = xasprintf(
+ "Console mode reset to %s default.",
+ config->console_mode == CONSOLE_MODE_KEEP ?
+ "firmware" :
+ "configuration file");
new_mode = true;
break;
@@ -981,9 +987,9 @@ static bool menu_run(
if (FLAGS_SET(get_os_indications_supported(), EFI_OS_INDICATIONS_BOOT_TO_FW_UI)) {
firmware_setup = true;
/* Let's make sure the user really wants to do this. */
- status = xpool_print(L"Press Enter to reboot into firmware interface.");
+ status = xstrdup16(u"Press Enter to reboot into firmware interface.");
} else
- status = xpool_print(L"Reboot into firmware interface not supported.");
+ status = xstrdup16(u"Reboot into firmware interface not supported.");
break;
default:
@@ -1317,9 +1323,9 @@ static void config_entry_parse_tries(
entry->tries_done = tries_done;
entry->path = xstrdup16(path);
entry->current_name = xstrdup16(file);
- entry->next_name = xpool_print(
- L"%.*s%u-%u%s",
- prefix_len,
+ entry->next_name = xasprintf(
+ "%.*ls%" PRIu64 "-%" PRIu64 "%ls",
+ (int) prefix_len,
file,
LESS_BY(tries_left, 1u),
MIN(tries_done + 1, (uint64_t) INT_MAX),
@@ -1342,7 +1348,7 @@ static void config_entry_bump_counters(ConfigEntry *entry, EFI_FILE *root_dir) {
if (!entry->path || !entry->current_name || !entry->next_name)
return;
- old_path = xpool_print(L"%s\\%s", entry->path, entry->current_name);
+ old_path = xasprintf("%ls\\%ls", entry->path, entry->current_name);
err = root_dir->Open(root_dir, &handle, old_path, EFI_FILE_MODE_READ|EFI_FILE_MODE_WRITE, 0ULL);
if (err != EFI_SUCCESS)
@@ -1365,7 +1371,7 @@ static void config_entry_bump_counters(ConfigEntry *entry, EFI_FILE *root_dir) {
/* Let's tell the OS that we renamed this file, so that it knows what to rename to the counter-less name on
* success */
- new_path = xpool_print(L"%s\\%s", entry->path, entry->next_name);
+ new_path = xasprintf("%ls\\%ls", entry->path, entry->next_name);
efivar_set(LOADER_GUID, L"LoaderBootCountPath", new_path, 0);
/* If the file we just renamed is the loader path, then let's update that. */
@@ -1479,7 +1485,7 @@ static void config_entry_add_type1(
new = xstr8_to_16(value);
if (entry->options) {
- char16_t *s = xpool_print(L"%s %s", entry->options, new);
+ char16_t *s = xasprintf("%ls %ls", entry->options, new);
free(entry->options);
entry->options = s;
} else
@@ -1796,7 +1802,7 @@ static void config_title_generate(Config *config) {
continue;
_cleanup_free_ char16_t *t = config->entries[i]->title_show;
- config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->version);
+ config->entries[i]->title_show = xasprintf("%ls (%ls)", t, config->entries[i]->version);
}
if (entries_unique(config->entries, unique, config->entry_count))
@@ -1813,11 +1819,7 @@ static void config_title_generate(Config *config) {
continue;
_cleanup_free_ char16_t *t = config->entries[i]->title_show;
- config->entries[i]->title_show = xpool_print(
- L"%s (%.*s)",
- t,
- strnlen16(config->entries[i]->machine_id, 8),
- config->entries[i]->machine_id);
+ config->entries[i]->title_show = xasprintf("%ls (%.8ls)", t, config->entries[i]->machine_id);
}
if (entries_unique(config->entries, unique, config->entry_count))
@@ -1829,7 +1831,7 @@ static void config_title_generate(Config *config) {
continue;
_cleanup_free_ char16_t *t = config->entries[i]->title_show;
- config->entries[i]->title_show = xpool_print(L"%s (%s)", t, config->entries[i]->id);
+ config->entries[i]->title_show = xasprintf("%ls (%ls)", t, config->entries[i]->id);
}
}
@@ -1994,10 +1996,9 @@ static EFI_STATUS boot_windows_bitlocker(void) {
for (size_t i = 0; i < boot_order_size / sizeof(uint16_t); i++) {
_cleanup_free_ char *buf = NULL;
- char16_t name[sizeof(L"Boot0000")];
size_t buf_size;
- SPrint(name, sizeof(name), L"Boot%04x", (uint32_t) boot_order[i]);
+ _cleanup_free_ char16_t *name = xasprintf("Boot%04x", boot_order[i]);
err = efivar_get_raw(EFI_GLOBAL_GUID, name, &buf, &buf_size);
if (err != EFI_SUCCESS)
continue;
@@ -2187,7 +2188,7 @@ static void config_entry_add_unified(
.title = xstrdup16(good_name),
.version = xstrdup16(good_version),
.device = device,
- .loader = xpool_print(L"\\EFI\\Linux\\%s", f->FileName),
+ .loader = xasprintf("\\EFI\\Linux\\%ls", f->FileName),
.sort_key = xstrdup16(good_sort_key),
.key = 'l',
.tries_done = -1,
@@ -2266,9 +2267,9 @@ static EFI_STATUS initrd_prepare(
STRV_FOREACH(i, entry->initrd) {
_cleanup_free_ char16_t *o = options;
if (o)
- options = xpool_print(L"%s initrd=%s", o, *i);
+ options = xasprintf("%ls initrd=%ls", o, *i);
else
- options = xpool_print(L"initrd=%s", *i);
+ options = xasprintf("initrd=%ls", *i);
_cleanup_(file_closep) EFI_FILE *handle = NULL;
err = root->Open(root, &handle, *i, EFI_FILE_MODE_READ, 0);
@@ -2300,7 +2301,7 @@ static EFI_STATUS initrd_prepare(
if (entry->options) {
_cleanup_free_ char16_t *o = options;
- options = xpool_print(L"%s %s", o, entry->options);
+ options = xasprintf("%ls %ls", o, entry->options);
}
*ret_options = TAKE_PTR(options);
@@ -2482,9 +2483,9 @@ static EFI_STATUS secure_boot_discover_keys(Config *config, EFI_FILE *root_dir)
entry = xnew(ConfigEntry, 1);
*entry = (ConfigEntry) {
- .id = xpool_print(L"secure-boot-keys-%s", dirent->FileName),
- .title = xpool_print(L"Enroll Secure Boot keys: %s", dirent->FileName),
- .path = xpool_print(L"\\loader\\keys\\%s", dirent->FileName),
+ .id = xasprintf("secure-boot-keys-%ls", dirent->FileName),
+ .title = xasprintf("Enroll Secure Boot keys: %ls", dirent->FileName),
+ .path = xasprintf("\\loader\\keys\\%ls", dirent->FileName),
.type = LOADER_SECURE_BOOT_KEYS,
.tries_done = -1,
.tries_left = -1,
@@ -2527,10 +2528,10 @@ static void export_variables(
efivar_set_time_usec(LOADER_GUID, L"LoaderTimeInitUSec", init_usec);
efivar_set(LOADER_GUID, L"LoaderInfo", L"systemd-boot " GIT_VERSION, 0);
- infostr = xpool_print(L"%s %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ infostr = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", infostr, 0);
- typestr = xpool_print(L"UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ typestr = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
efivar_set(LOADER_GUID, L"LoaderFirmwareType", typestr, 0);
(void) efivar_set_uint64_le(LOADER_GUID, L"LoaderFeatures", loader_features, 0);
diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c
index 62773ded9a..0d95d40183 100644
--- a/src/boot/efi/cpio.c
+++ b/src/boot/efi/cpio.c
@@ -360,7 +360,7 @@ static char16_t *get_dropin_dir(const EFI_DEVICE_PATH *file_path) {
return NULL;
convert_efi_path(file_path_str);
- return xpool_print(u"%s.extra.d", file_path_str);
+ return xasprintf("%ls.extra.d", file_path_str);
}
EFI_STATUS pack_cpio(
diff --git a/src/boot/efi/drivers.c b/src/boot/efi/drivers.c
index c76f8e0903..4abb3fbd82 100644
--- a/src/boot/efi/drivers.c
+++ b/src/boot/efi/drivers.c
@@ -20,7 +20,7 @@ static EFI_STATUS load_one_driver(
assert(loaded_image);
assert(fname);
- spath = xpool_print(L"\\EFI\\systemd\\drivers\\%s", fname);
+ spath = xasprintf("\\EFI\\systemd\\drivers\\%ls", fname);
err = make_file_device_path(loaded_image->DeviceHandle, spath, &path);
if (err != EFI_SUCCESS)
return log_error_status(err, "Error making file device path: %m");
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index f71f041a2f..552660eb07 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -114,14 +114,14 @@ static void export_variables(EFI_LOADED_IMAGE_PROTOCOL *loaded_image) {
/* if LoaderFirmwareInfo is not set, let's set it */
if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) {
_cleanup_free_ char16_t *s = NULL;
- s = xpool_print(L"%s %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ s = xasprintf("%ls %u.%02u", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
efivar_set(LOADER_GUID, L"LoaderFirmwareInfo", s, 0);
}
/* ditto for LoaderFirmwareType */
if (efivar_get_raw(LOADER_GUID, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) {
_cleanup_free_ char16_t *s = NULL;
- s = xpool_print(L"UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ s = xasprintf("UEFI %u.%02u", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
efivar_set(LOADER_GUID, L"LoaderFirmwareType", s, 0);
}
@@ -173,7 +173,7 @@ static bool use_load_options(
*ret = xstrdup16(shell->Argv[1]);
for (size_t i = 2; i < shell->Argc; i++) {
_cleanup_free_ char16_t *old = *ret;
- *ret = xpool_print(u"%s %s", old, shell->Argv[i]);
+ *ret = xasprintf("%ls %ls", old, shell->Argv[i]);
}
mangle_stub_cmdline(*ret);
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index 771f11c8bd..e0c3b408f2 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -68,7 +68,6 @@ static inline void *xrealloc(void *p, size_t old_size, size_t new_size) {
return r;
}
-#define xpool_print(fmt, ...) ((char16_t *) ASSERT_SE_PTR(PoolPrint((fmt), ##__VA_ARGS__)))
#define xnew(type, n) ((type *) xmalloc_multiply(sizeof(type), (n)))
typedef struct {
diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c
index 3dfa92b58d..b24d556700 100644
--- a/src/boot/efi/vmm.c
+++ b/src/boot/efi/vmm.c
@@ -97,9 +97,8 @@ EFI_STATUS vmm_open(EFI_HANDLE *ret_vmm_dev, EFI_FILE **ret_vmm_dir) {
for (size_t order = 0;; order++) {
_cleanup_free_ EFI_DEVICE_PATH *dp = NULL;
- char16_t order_str[STRLEN("VMMBootOrder") + 4 + 1];
- SPrint(order_str, sizeof(order_str), u"VMMBootOrder%04x", order);
+ _cleanup_free_ char16_t *order_str = xasprintf("VMMBootOrder%04zx", order);
dp_err = efivar_get_raw(&(EFI_GUID)VMM_BOOT_ORDER_GUID, order_str, (char**)&dp, NULL);
for (size_t i = 0; i < n_handles; i++) {

View File

@ -0,0 +1,323 @@
From 013b84264db5b2062840f0ff04df776fa144c586 Mon Sep 17 00:00:00 2001
From: Jan Janssen <medhefgo@web.de>
Date: Tue, 23 Aug 2022 10:51:36 +0200
Subject: [PATCH] boot: Drop use of Print
The custom print helpers have been replaced with explicit checks at the
call site to keep this in line with the way it is done in userspace. Any
calls where the check has been ommited should not need them as the value
is expected to alawys be around.
(cherry picked from commit 9220b2c46bfbdf759b5a777a8bb3109a4d873039)
Related: RHEL-16952
---
src/boot/efi/boot.c | 176 ++++++++++++++++++++-----------------
src/boot/efi/secure-boot.c | 6 +-
src/boot/efi/stub.c | 2 +-
src/boot/efi/util.h | 2 +-
4 files changed, 102 insertions(+), 84 deletions(-)
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index 64a9eda24e..cb237675ec 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -433,22 +433,9 @@ static bool unicode_supported(void) {
return cache;
}
-static void ps_string(const char16_t *fmt, const void *value) {
- assert(fmt);
- if (value)
- Print(fmt, value);
-}
-
-static void ps_bool(const char16_t *fmt, bool value) {
- assert(fmt);
- Print(fmt, yes_no(value));
-}
-
static bool ps_continue(void) {
- if (unicode_supported())
- Print(L"\n─── Press any key to continue, ESC or q to quit. ───\n\n");
- else
- Print(L"\n--- Press any key to continue, ESC or q to quit. ---\n\n");
+ const char16_t *sep = unicode_supported() ? u"───" : u"---";
+ printf("\n%ls Press any key to continue, ESC or q to quit. %ls\n\n", sep, sep);
uint64_t key;
return console_key_read(&key, UINT64_MAX) == EFI_SUCCESS &&
@@ -470,112 +457,143 @@ static void print_status(Config *config, char16_t *loaded_image_path) {
secure = secure_boot_mode();
(void) efivar_get(LOADER_GUID, L"LoaderDevicePartUUID", &device_part_uuid);
- /* We employ some unusual indentation here for readability. */
-
- ps_string(L" systemd-boot version: %a\n", GIT_VERSION);
- ps_string(L" loaded image: %s\n", loaded_image_path);
- ps_string(L" loader partition UUID: %s\n", device_part_uuid);
- ps_string(L" architecture: %a\n", EFI_MACHINE_TYPE_NAME);
- Print(L" UEFI specification: %u.%02u\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
- ps_string(L" firmware vendor: %s\n", ST->FirmwareVendor);
- Print(L" firmware version: %u.%02u\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
- Print(L" OS indications: %lu\n", get_os_indications_supported());
- Print(L" secure boot: %s (%s)\n", yes_no(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)), secure_boot_mode_to_string(secure));
- ps_bool(L" shim: %s\n", shim_loaded());
- ps_bool(L" TPM: %s\n", tpm_present());
- Print(L" console mode: %d/%ld (%" PRIuN L"x%" PRIuN L" @%ux%u)\n", ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - INT64_C(1), x_max, y_max, screen_width, screen_height);
+ printf(" systemd-boot version: " GIT_VERSION "\n");
+ if (loaded_image_path)
+ printf(" loaded image: %ls\n", loaded_image_path);
+ if (device_part_uuid)
+ printf(" loader partition UUID: %ls\n", device_part_uuid);
+ printf(" architecture: " EFI_MACHINE_TYPE_NAME "\n");
+ printf(" UEFI specification: %u.%02u\n", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff);
+ printf(" firmware vendor: %ls\n", ST->FirmwareVendor);
+ printf(" firmware version: %u.%02u\n", ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff);
+ printf(" OS indications: %#" PRIx64 "\n", get_os_indications_supported());
+ printf(" secure boot: %ls (%ls)\n",
+ yes_no(IN_SET(secure, SECURE_BOOT_USER, SECURE_BOOT_DEPLOYED)),
+ secure_boot_mode_to_string(secure));
+ printf(" shim: %ls\n", yes_no(shim_loaded()));
+ printf(" TPM: %ls\n", yes_no(tpm_present()));
+ printf(" console mode: %i/%" PRIi64 " (%zux%zu @%ux%u)\n",
+ ST->ConOut->Mode->Mode, ST->ConOut->Mode->MaxMode - INT64_C(1),
+ x_max, y_max, screen_width, screen_height);
if (!ps_continue())
return;
switch (config->timeout_sec_config) {
case TIMEOUT_UNSET:
- break;
+ break;
case TIMEOUT_MENU_FORCE:
- Print(L" timeout (config): menu-force\n"); break;
+ printf(" timeout (config): menu-force\n");
+ break;
case TIMEOUT_MENU_HIDDEN:
- Print(L" timeout (config): menu-hidden\n"); break;
+ printf(" timeout (config): menu-hidden\n");
+ break;
default:
- Print(L" timeout (config): %u s\n", config->timeout_sec_config);
+ printf(" timeout (config): %u s\n", config->timeout_sec_config);
}
switch (config->timeout_sec_efivar) {
case TIMEOUT_UNSET:
- break;
+ break;
case TIMEOUT_MENU_FORCE:
- Print(L" timeout (EFI var): menu-force\n"); break;
+ printf(" timeout (EFI var): menu-force\n");
+ break;
case TIMEOUT_MENU_HIDDEN:
- Print(L" timeout (EFI var): menu-hidden\n"); break;
+ printf(" timeout (EFI var): menu-hidden\n");
+ break;
default:
- Print(L" timeout (EFI var): %u s\n", config->timeout_sec_efivar);
+ printf(" timeout (EFI var): %u s\n", config->timeout_sec_efivar);
}
- ps_string(L" default (config): %s\n", config->entry_default_config);
- ps_string(L" default (EFI var): %s\n", config->entry_default_efivar);
- ps_string(L" default (one-shot): %s\n", config->entry_oneshot);
- ps_string(L" saved entry: %s\n", config->entry_saved);
- ps_bool(L" editor: %s\n", config->editor);
- ps_bool(L" auto-entries: %s\n", config->auto_entries);
- ps_bool(L" auto-firmware: %s\n", config->auto_firmware);
- ps_bool(L" beep: %s\n", config->beep);
- ps_bool(L" reboot-for-bitlocker: %s\n", config->reboot_for_bitlocker);
+ if (config->entry_default_config)
+ printf(" default (config): %ls\n", config->entry_default_config);
+ if (config->entry_default_efivar)
+ printf(" default (EFI var): %ls\n", config->entry_default_efivar);
+ if (config->entry_oneshot)
+ printf(" default (one-shot): %ls\n", config->entry_oneshot);
+ if (config->entry_saved)
+ printf(" saved entry: %ls\n", config->entry_saved);
+ printf(" editor: %ls\n", yes_no(config->editor));
+ printf(" auto-entries: %ls\n", yes_no(config->auto_entries));
+ printf(" auto-firmware: %ls\n", yes_no(config->auto_firmware));
+ printf(" beep: %ls\n", yes_no(config->beep));
+ printf(" reboot-for-bitlocker: %ls\n", yes_no(config->reboot_for_bitlocker));
switch (config->secure_boot_enroll) {
case ENROLL_OFF:
- Print(L" secure-boot-enroll: off\n"); break;
+ printf(" secure-boot-enroll: off\n");
+ break;
case ENROLL_MANUAL:
- Print(L" secure-boot-enroll: manual\n"); break;
+ printf(" secure-boot-enroll: manual\n");
+ break;
case ENROLL_FORCE:
- Print(L" secure-boot-enroll: force\n"); break;
+ printf(" secure-boot-enroll: force\n");
+ break;
default:
assert_not_reached();
}
switch (config->console_mode) {
case CONSOLE_MODE_AUTO:
- Print(L" console-mode (config): %s\n", L"auto"); break;
+ printf(" console-mode (config): auto\n");
+ break;
case CONSOLE_MODE_KEEP:
- Print(L" console-mode (config): %s\n", L"keep"); break;
+ printf(" console-mode (config): keep\n");
+ break;
case CONSOLE_MODE_FIRMWARE_MAX:
- Print(L" console-mode (config): %s\n", L"max"); break;
+ printf(" console-mode (config): max\n");
+ break;
default:
- Print(L" console-mode (config): %ld\n", config->console_mode); break;
+ printf(" console-mode (config): %" PRIi64 "\n", config->console_mode);
+ break;
}
/* EFI var console mode is always a concrete value or unset. */
if (config->console_mode_efivar != CONSOLE_MODE_KEEP)
- Print(L"console-mode (EFI var): %ld\n", config->console_mode_efivar);
+ printf("console-mode (EFI var): %" PRIi64 "\n", config->console_mode_efivar);
if (!ps_continue())
return;
for (size_t i = 0; i < config->entry_count; i++) {
ConfigEntry *entry = config->entries[i];
-
- _cleanup_free_ char16_t *dp = NULL;
- if (entry->device)
- (void) device_path_to_str(DevicePathFromHandle(entry->device), &dp);
-
- Print(L" config entry: %" PRIuN L"/%" PRIuN L"\n", i + 1, config->entry_count);
- ps_string(L" id: %s\n", entry->id);
- ps_string(L" title: %s\n", entry->title);
- ps_string(L" title show: %s\n", streq16(entry->title, entry->title_show) ? NULL : entry->title_show);
- ps_string(L" sort key: %s\n", entry->sort_key);
- ps_string(L" version: %s\n", entry->version);
- ps_string(L" machine-id: %s\n", entry->machine_id);
- ps_string(L" device: %s\n", dp);
- ps_string(L" loader: %s\n", entry->loader);
+ EFI_DEVICE_PATH *dp = NULL;
+ _cleanup_free_ char16_t *dp_str = NULL;
+
+ if (entry->device &&
+ BS->HandleProtocol(entry->device, &(EFI_GUID) EFI_DEVICE_PATH_PROTOCOL_GUID, (void **) &dp) ==
+ EFI_SUCCESS)
+ (void) device_path_to_str(dp, &dp_str);
+
+ printf(" config entry: %zu/%zu\n", i + 1, config->entry_count);
+ printf(" id: %ls\n", entry->id);
+ if (entry->title)
+ printf(" title: %ls\n", entry->title);
+ if (entry->title_show && !streq16(entry->title, entry->title_show))
+ printf(" title show: %ls\n", entry->title_show);
+ if (entry->sort_key)
+ printf(" sort key: %ls\n", entry->sort_key);
+ if (entry->version)
+ printf(" version: %ls\n", entry->version);
+ if (entry->machine_id)
+ printf(" machine-id: %ls\n", entry->machine_id);
+ if (dp_str)
+ printf(" device: %ls\n", dp_str);
+ if (entry->loader)
+ printf(" loader: %ls\n", entry->loader);
STRV_FOREACH(initrd, entry->initrd)
- Print(L" initrd: %s\n", *initrd);
- ps_string(L" devicetree: %s\n", entry->devicetree);
- ps_string(L" options: %s\n", entry->options);
- ps_bool(L" internal call: %s\n", !!entry->call);
-
- ps_bool(L"counting boots: %s\n", entry->tries_left >= 0);
+ printf(" initrd: %ls\n", *initrd);
+ if (entry->devicetree)
+ printf(" devicetree: %ls\n", entry->devicetree);
+ if (entry->options)
+ printf(" options: %ls\n", entry->options);
+ printf(" internal call: %ls\n", yes_no(!!entry->call));
+
+ printf("counting boots: %ls\n", yes_no(entry->tries_left >= 0));
if (entry->tries_left >= 0) {
- Print(L" tries: %u left, %u done\n", entry->tries_left, entry->tries_done);
- Print(L" current path: %s\\%s\n", entry->path, entry->current_name);
- Print(L" next path: %s\\%s\n", entry->path, entry->next_name);
+ printf(" tries: %i left, %i done\n", entry->tries_left, entry->tries_done);
+ printf(" current path: %ls\\%ls\n", entry->path, entry->current_name);
+ printf(" next path: %ls\\%ls\n", entry->path, entry->next_name);
}
if (!ps_continue())
@@ -629,7 +647,7 @@ static bool menu_run(
ST->ConOut->EnableCursor(ST->ConOut, false);
/* draw a single character to make ClearScreen work on some firmware */
- Print(L" ");
+ ST->ConOut->OutputString(ST->ConOut, (char16_t *) u" ");
err = console_set_mode(config->console_mode_efivar != CONSOLE_MODE_KEEP ?
config->console_mode_efivar : config->console_mode);
@@ -2715,7 +2733,7 @@ out:
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
InitializeLib(image, sys_table);
- debug_hook(L"systemd-boot");
+ debug_hook("systemd-boot");
/* Uncomment the next line if you need to wait for debugger. */
// debug_break();
diff --git a/src/boot/efi/secure-boot.c b/src/boot/efi/secure-boot.c
index 6b6d48277e..2594c8798f 100644
--- a/src/boot/efi/secure-boot.c
+++ b/src/boot/efi/secure-boot.c
@@ -44,16 +44,16 @@ EFI_STATUS secure_boot_enroll_at(EFI_FILE *root_dir, const char16_t *path) {
clear_screen(COLOR_NORMAL);
- Print(u"Enrolling secure boot keys from directory: %s\n");
+ printf("Enrolling secure boot keys from directory: %ls\n", path);
/* Enrolling secure boot keys is safe to do in virtualized environments as there is nothing
* we can brick there. */
if (!in_hypervisor()) {
- Print(u"Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n", path);
+ printf("Warning: Enrolling custom Secure Boot keys might soft-brick your machine!\n");
unsigned timeout_sec = 15;
for (;;) {
- Print(u"\rEnrolling in %2u s, press any key to abort.", timeout_sec);
+ printf("\rEnrolling in %2u s, press any key to abort.", timeout_sec);
uint64_t key;
err = console_key_read(&key, 1000 * 1000);
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 552660eb07..69e6a0b07f 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -425,7 +425,7 @@ static EFI_STATUS real_main(EFI_HANDLE image) {
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
InitializeLib(image, sys_table);
- debug_hook(L"systemd-stub");
+ debug_hook("systemd-stub");
/* Uncomment the next line if you need to wait for debugger. */
// debug_break();
diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h
index e0c3b408f2..3247694014 100644
--- a/src/boot/efi/util.h
+++ b/src/boot/efi/util.h
@@ -180,7 +180,7 @@ void debug_break(void);
extern uint8_t _text, _data;
/* Report the relocated position of text and data sections so that a debugger
* can attach to us. See debug-sd-boot.sh for how this can be done. */
-# define debug_hook(identity) Print(identity L"@0x%lx,0x%lx\n", POINTER_TO_PHYSICAL_ADDRESS(&_text), POINTER_TO_PHYSICAL_ADDRESS(&_data))
+# define debug_hook(identity) printf(identity "@%p,%p\n", &_text, &_data)
#else
# define debug_hook(identity)
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,51 @@
From 50a254def1c98a34ee5fdb52dcfbb1ed59b1250a Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Mon, 30 Jan 2023 16:22:10 +0100
Subject: [PATCH] efi-string: Fix strchr() null byte handling
strchr() should be able to search for the terminating null byte,
our implementation doesn't, let's fix that.
(cherry picked from commit bbef5a9617e91b4b1bc30266eb9dcbda395a8c61)
Related: RHEL-16952
---
src/boot/efi/efi-string.c | 2 +-
src/boot/efi/test-efi-string.c | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c
index 860dfc00b2..cf0d71e986 100644
--- a/src/boot/efi/efi-string.c
+++ b/src/boot/efi/efi-string.c
@@ -116,7 +116,7 @@ DEFINE_STRCPY(char16_t, strcpy16);
s++; \
} \
\
- return NULL; \
+ return c ? NULL : (type *) s; \
}
DEFINE_STRCHR(char, strchr8);
diff --git a/src/boot/efi/test-efi-string.c b/src/boot/efi/test-efi-string.c
index c26973d8bd..c7e42c7b94 100644
--- a/src/boot/efi/test-efi-string.c
+++ b/src/boot/efi/test-efi-string.c
@@ -229,6 +229,8 @@ TEST(strchr8) {
assert_se(strchr8(str, 'a') == &str[0]);
assert_se(strchr8(str, 'c') == &str[2]);
assert_se(strchr8(str, 'B') == &str[4]);
+
+ assert_se(strchr8(str, 0) == str + strlen8(str));
}
TEST(strchr16) {
@@ -240,6 +242,8 @@ TEST(strchr16) {
assert_se(strchr16(str, 'a') == &str[0]);
assert_se(strchr16(str, 'c') == &str[2]);
assert_se(strchr16(str, 'B') == &str[4]);
+
+ assert_se(strchr16(str, 0) == str + strlen16(str));
}
TEST(xstrndup8) {

View File

@ -0,0 +1,79 @@
From 3684d9be497c5cb5164435238b970931c93b41e6 Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Mon, 30 Jan 2023 16:25:23 +0100
Subject: [PATCH] efi-string: Add startswith8()
startswith() from string-util-fundamental.h is defined for sd_char
which is char16_t, so let's add an implementation for char as well.
(cherry picked from commit ad36d31ea578622883c3b5297c971374096a504a)
Related: RHEL-16952
---
src/boot/efi/efi-string.c | 15 +++++++++++++++
src/boot/efi/efi-string.h | 2 ++
src/boot/efi/test-efi-string.c | 12 ++++++++++++
3 files changed, 29 insertions(+)
diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c
index cf0d71e986..6b84af69e6 100644
--- a/src/boot/efi/efi-string.c
+++ b/src/boot/efi/efi-string.c
@@ -216,6 +216,21 @@ char16_t *xstrn8_to_16(const char *str8, size_t n) {
return str16;
}
+char *startswith8(const char *s, const char *prefix) {
+ size_t l;
+
+ assert(prefix);
+
+ if (!s)
+ return NULL;
+
+ l = strlen8(prefix);
+ if (!strneq8(s, prefix, l))
+ return NULL;
+
+ return (char*) s + l;
+}
+
static bool efi_fnmatch_prefix(const char16_t *p, const char16_t *h, const char16_t **ret_p, const char16_t **ret_h) {
assert(p);
assert(h);
diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h
index 2a28db3593..477229bf60 100644
--- a/src/boot/efi/efi-string.h
+++ b/src/boot/efi/efi-string.h
@@ -105,6 +105,8 @@ static inline char16_t *xstr8_to_16(const char *str8) {
return xstrn8_to_16(str8, strlen8(str8));
}
+char *startswith8(const char *s, const char *prefix);
+
bool efi_fnmatch(const char16_t *pattern, const char16_t *haystack);
bool parse_number8(const char *s, uint64_t *ret_u, const char **ret_tail);
diff --git a/src/boot/efi/test-efi-string.c b/src/boot/efi/test-efi-string.c
index c7e42c7b94..be7f8f9b1c 100644
--- a/src/boot/efi/test-efi-string.c
+++ b/src/boot/efi/test-efi-string.c
@@ -355,6 +355,18 @@ TEST(xstrn8_to_16) {
free(s);
}
+TEST(startswith8) {
+ assert_se(streq8(startswith8("", ""), ""));
+ assert_se(streq8(startswith8("x", ""), "x"));
+ assert_se(!startswith8("", "x"));
+ assert_se(!startswith8("", "xxxxxxxx"));
+ assert_se(streq8(startswith8("xxx", "x"), "xx"));
+ assert_se(streq8(startswith8("xxx", "xx"), "x"));
+ assert_se(streq8(startswith8("xxx", "xxx"), ""));
+ assert_se(!startswith8("xxx", "xxxx"));
+ assert_se(!startswith8(NULL, ""));
+}
+
#define TEST_FNMATCH_ONE(pattern, haystack, expect) \
({ \
assert_se(fnmatch(pattern, haystack, 0) == (expect ? 0 : FNM_NOMATCH)); \

View File

@ -0,0 +1,93 @@
From 9461c9b524c2f3bf19b86dbcda24f57acde67852 Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Tue, 31 Jan 2023 15:39:40 +0100
Subject: [PATCH] efi-string: Add efi_memchr()
(cherry picked from commit e71f0f63da87fb8043f665a142261bc393fe0216)
Related: RHEL-16952
---
src/boot/efi/efi-string.c | 14 ++++++++++++++
src/boot/efi/efi-string.h | 2 ++
src/boot/efi/test-efi-string.c | 13 +++++++++++++
3 files changed, 29 insertions(+)
diff --git a/src/boot/efi/efi-string.c b/src/boot/efi/efi-string.c
index 6b84af69e6..60b5d0f712 100644
--- a/src/boot/efi/efi-string.c
+++ b/src/boot/efi/efi-string.c
@@ -886,16 +886,30 @@ char16_t *xvasprintf_status(EFI_STATUS status, const char *format, va_list ap) {
#if SD_BOOT
/* To provide the actual implementation for these we need to remove the redirection to the builtins. */
+# undef memchr
# undef memcmp
# undef memcpy
# undef memset
#else
/* And for userspace unit testing we need to give them an efi_ prefix. */
+# define memchr efi_memchr
# define memcmp efi_memcmp
# define memcpy efi_memcpy
# define memset efi_memset
#endif
+_used_ void *memchr(const void *p, int c, size_t n) {
+ if (!p || n == 0)
+ return NULL;
+
+ const uint8_t *q = p;
+ for (size_t i = 0; i < n; i++)
+ if (q[i] == (unsigned char) c)
+ return (void *) (q + i);
+
+ return NULL;
+}
+
_used_ int memcmp(const void *p1, const void *p2, size_t n) {
const uint8_t *up1 = p1, *up2 = p2;
int r;
diff --git a/src/boot/efi/efi-string.h b/src/boot/efi/efi-string.h
index 477229bf60..3d035d7ead 100644
--- a/src/boot/efi/efi-string.h
+++ b/src/boot/efi/efi-string.h
@@ -142,6 +142,7 @@ _gnu_printf_(2, 0) _warn_unused_result_ char16_t *xvasprintf_status(EFI_STATUS s
* compiling with -ffreestanding. By referring to builtins, the compiler can check arguments and do
* optimizations again. Note that we still need to provide implementations as the compiler is free to not
* inline its own implementation and instead issue a library call. */
+# define memchr __builtin_memchr
# define memcmp __builtin_memcmp
# define memcpy __builtin_memcpy
# define memset __builtin_memset
@@ -155,6 +156,7 @@ static inline void *mempcpy(void * restrict dest, const void * restrict src, siz
#else
/* For unit testing. */
+void *efi_memchr(const void *p, int c, size_t n);
int efi_memcmp(const void *p1, const void *p2, size_t n);
void *efi_memcpy(void * restrict dest, const void * restrict src, size_t n);
void *efi_memset(void *p, int c, size_t n);
diff --git a/src/boot/efi/test-efi-string.c b/src/boot/efi/test-efi-string.c
index be7f8f9b1c..d214b1536e 100644
--- a/src/boot/efi/test-efi-string.c
+++ b/src/boot/efi/test-efi-string.c
@@ -626,6 +626,19 @@ TEST(xvasprintf_status) {
s = mfree(s);
}
+TEST(efi_memchr) {
+ assert_se(streq8(efi_memchr("abcde", 'c', 5), "cde"));
+ assert_se(streq8(efi_memchr("abcde", 'c', 3), "cde"));
+ assert_se(streq8(efi_memchr("abcde", 'c', 2), NULL));
+ assert_se(streq8(efi_memchr("abcde", 'c', 7), "cde"));
+ assert_se(streq8(efi_memchr("abcde", 'q', 5), NULL));
+ assert_se(streq8(efi_memchr("abcde", 'q', 0), NULL));
+ /* Test that the character is interpreted as unsigned char. */
+ assert_se(streq8(efi_memchr("abcde", 'a', 6), efi_memchr("abcde", 'a' + 0x100, 6)));
+ assert_se(streq8(efi_memchr("abcde", 0, 6), ""));
+ assert_se(efi_memchr(NULL, 0, 0) == NULL);
+}
+
TEST(efi_memcmp) {
assert_se(efi_memcmp(NULL, NULL, 0) == 0);
assert_se(efi_memcmp(NULL, NULL, 1) == 0);

View File

@ -0,0 +1,70 @@
From 139d725cb2b293443e7b0db263401b588373a7cb Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Mon, 30 Jan 2023 21:15:12 +0100
Subject: [PATCH] vmm: Add more const
SMBIOS tables are immutable, so let's access it via const pointers
where possible.
(cherry picked from commit 761f62fe98cab82a3742bdae49f79626ede2ceaf)
Related: RHEL-16952
---
src/boot/efi/vmm.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c
index 6bd440f032..6b684e1bf4 100644
--- a/src/boot/efi/vmm.c
+++ b/src/boot/efi/vmm.c
@@ -201,17 +201,17 @@ typedef struct {
uint8_t bios_characteristics_ext[2];
} _packed_ SmbiosTableType0;
-static void *find_smbios_configuration_table(uint64_t *ret_size) {
+static const void *find_smbios_configuration_table(uint64_t *ret_size) {
assert(ret_size);
- Smbios3EntryPoint *entry3 = find_configuration_table(MAKE_GUID_PTR(SMBIOS3_TABLE));
+ const Smbios3EntryPoint *entry3 = find_configuration_table(MAKE_GUID_PTR(SMBIOS3_TABLE));
if (entry3 && memcmp(entry3->anchor_string, "_SM3_", 5) == 0 &&
entry3->entry_point_length <= sizeof(*entry3)) {
*ret_size = entry3->table_maximum_size;
return PHYSICAL_ADDRESS_TO_POINTER(entry3->table_address);
}
- SmbiosEntryPoint *entry = find_configuration_table(MAKE_GUID_PTR(SMBIOS_TABLE));
+ const SmbiosEntryPoint *entry = find_configuration_table(MAKE_GUID_PTR(SMBIOS_TABLE));
if (entry && memcmp(entry->anchor_string, "_SM_", 4) == 0 &&
entry->entry_point_length <= sizeof(*entry)) {
*ret_size = entry->table_length;
@@ -221,9 +221,9 @@ static void *find_smbios_configuration_table(uint64_t *ret_size) {
return NULL;
}
-static SmbiosHeader *get_smbios_table(uint8_t type) {
+static const SmbiosHeader *get_smbios_table(uint8_t type) {
uint64_t size = 0;
- uint8_t *p = find_smbios_configuration_table(&size);
+ const uint8_t *p = find_smbios_configuration_table(&size);
if (!p)
return false;
@@ -231,7 +231,7 @@ static SmbiosHeader *get_smbios_table(uint8_t type) {
if (size < sizeof(SmbiosHeader))
return NULL;
- SmbiosHeader *header = (SmbiosHeader *) p;
+ const SmbiosHeader *header = (const SmbiosHeader *) p;
/* End of table. */
if (header->type == 127)
@@ -273,7 +273,7 @@ static SmbiosHeader *get_smbios_table(uint8_t type) {
static bool smbios_in_hypervisor(void) {
/* Look up BIOS Information (Type 0). */
- SmbiosTableType0 *type0 = (SmbiosTableType0 *) get_smbios_table(0);
+ const SmbiosTableType0 *type0 = (const SmbiosTableType0 *) get_smbios_table(0);
if (!type0 || type0->header.length < sizeof(SmbiosTableType0))
return false;

View File

@ -0,0 +1,107 @@
From c7c166f2dd636418bfa25ea9c69ebfc45c618d8f Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Mon, 30 Jan 2023 16:26:14 +0100
Subject: [PATCH] vmm: Add smbios_find_oem_string()
This function can be used to find SMBIOS strings in the SMBIOS Type 11
table.
(cherry picked from commit a885188b3ab71c222cbcc42b083ba671884aa651)
Related: RHEL-16952
---
src/boot/efi/vmm.c | 44 +++++++++++++++++++++++++++++++++++++++++---
src/boot/efi/vmm.h | 2 ++
2 files changed, 43 insertions(+), 3 deletions(-)
diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c
index 6b684e1bf4..19b66a3974 100644
--- a/src/boot/efi/vmm.c
+++ b/src/boot/efi/vmm.c
@@ -201,6 +201,12 @@ typedef struct {
uint8_t bios_characteristics_ext[2];
} _packed_ SmbiosTableType0;
+typedef struct {
+ SmbiosHeader header;
+ uint8_t count;
+ char contents[];
+} _packed_ SmbiosTableType11;
+
static const void *find_smbios_configuration_table(uint64_t *ret_size) {
assert(ret_size);
@@ -221,7 +227,7 @@ static const void *find_smbios_configuration_table(uint64_t *ret_size) {
return NULL;
}
-static const SmbiosHeader *get_smbios_table(uint8_t type) {
+static const SmbiosHeader *get_smbios_table(uint8_t type, uint64_t *ret_size_left) {
uint64_t size = 0;
const uint8_t *p = find_smbios_configuration_table(&size);
if (!p)
@@ -240,8 +246,11 @@ static const SmbiosHeader *get_smbios_table(uint8_t type) {
if (size < header->length)
return NULL;
- if (header->type == type)
+ if (header->type == type) {
+ if (ret_size_left)
+ *ret_size_left = size;
return header; /* Yay! */
+ }
/* Skip over formatted area. */
size -= header->length;
@@ -273,7 +282,7 @@ static const SmbiosHeader *get_smbios_table(uint8_t type) {
static bool smbios_in_hypervisor(void) {
/* Look up BIOS Information (Type 0). */
- const SmbiosTableType0 *type0 = (const SmbiosTableType0 *) get_smbios_table(0);
+ const SmbiosTableType0 *type0 = (const SmbiosTableType0 *) get_smbios_table(0, NULL);
if (!type0 || type0->header.length < sizeof(SmbiosTableType0))
return false;
@@ -289,3 +298,32 @@ bool in_hypervisor(void) {
cache = cpuid_in_hypervisor() || smbios_in_hypervisor();
return cache;
}
+
+const char* smbios_find_oem_string(const char *name) {
+ uint64_t left;
+
+ assert(name);
+
+ const SmbiosTableType11 *type11 = (const SmbiosTableType11 *) get_smbios_table(11, &left);
+ if (!type11 || type11->header.length < sizeof(SmbiosTableType11))
+ return NULL;
+
+ assert(left >= type11->header.length);
+
+ const char *s = type11->contents;
+ left -= type11->header.length;
+
+ for (const char *p = s; p < s + left; ) {
+ const char *e = memchr(p, 0, s + left - p);
+ if (!e || e == p) /* Double NUL byte means we've reached the end of the OEM strings. */
+ break;
+
+ const char *eq = startswith8(p, name);
+ if (eq && *eq == '=')
+ return eq + 1;
+
+ p = e + 1;
+ }
+
+ return NULL;
+}
diff --git a/src/boot/efi/vmm.h b/src/boot/efi/vmm.h
index e98ec74af1..2002f32bec 100644
--- a/src/boot/efi/vmm.h
+++ b/src/boot/efi/vmm.h
@@ -8,3 +8,5 @@ bool is_direct_boot(EFI_HANDLE device);
EFI_STATUS vmm_open(EFI_HANDLE *ret_qemu_dev, EFI_FILE **ret_qemu_dir);
bool in_hypervisor(void);
+
+const char* smbios_find_oem_string(const char *name);

View File

@ -0,0 +1,71 @@
From dc742ee6411f4f7bd5d447cd29dc17a0025843e0 Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Mon, 30 Jan 2023 16:26:50 +0100
Subject: [PATCH] stub: Read extra kernel command line items from SMBIOS
Let's read more kernel command line arguments from SMBIOS OEM string
io.systemd.stub.kernel-cmdline-extra. This allows adding debug kernel
command line arguments when booting in qemy without having to modify
the UKI.
(cherry picked from commit 717af0de4648ccc223f06683a6baf73d64271e02)
Related: RHEL-16952
---
man/systemd-stub.xml | 17 +++++++++++++++++
src/boot/efi/stub.c | 7 +++++++
2 files changed, 24 insertions(+)
diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml
index 85d30129d6..66c158c44d 100644
--- a/man/systemd-stub.xml
+++ b/man/systemd-stub.xml
@@ -382,6 +382,23 @@
default, this is done for the TPM2 PCR signature and public key files.</para>
</refsect1>
+ <refsect1>
+ <title>SMBIOS Type 11 Strings</title>
+
+ <para><command>systemd-stub</command> can be configured using SMBIOS Type 11 strings. Applicable strings
+ consist of a name, followed by <literal>=</literal>, followed by the value.
+ <command>systemd-stub</command> will search the table for a string with a specific name, and if found,
+ use its value. The following strings are read:</para>
+
+ <variablelist>
+ <varlistentry>
+ <term><varname>io.systemd.stub.kernel-cmdline-extra</varname></term>
+ <listitem><para>If set, the value of this string is added to the list of kernel command line
+ arguments that are passed to the kernel.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect1>
+
<refsect1>
<title>Assembling Kernel Images</title>
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index dac1bb0606..86eae2e350 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -16,6 +16,7 @@
#include "splash.h"
#include "tpm-pcr.h"
#include "util.h"
+#include "vmm.h"
/* magic string to find in the binary image */
_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-stub " GIT_VERSION " ####";
@@ -277,6 +278,12 @@ static EFI_STATUS real_main(EFI_HANDLE image) {
mangle_stub_cmdline(cmdline);
}
+ const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
+ if (extra) {
+ _cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra);
+ cmdline = xasprintf("%ls %ls", tmp, extra16);
+ }
+
export_variables(loaded_image);
if (pack_cpio(loaded_image,

View File

@ -0,0 +1,46 @@
From 47fd30b95f506beaef5640ad61b40b180c7ac47b Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Wed, 22 Feb 2023 17:04:58 +0100
Subject: [PATCH] vmm: Modernize get_smbios_table()
(cherry picked from commit c8e5d82c97a1478b15d2f97ffebd9591e81663ba)
Related: RHEL-16952
---
src/boot/efi/vmm.c | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/src/boot/efi/vmm.c b/src/boot/efi/vmm.c
index 19b66a3974..f9a59dca0a 100644
--- a/src/boot/efi/vmm.c
+++ b/src/boot/efi/vmm.c
@@ -258,22 +258,18 @@ static const SmbiosHeader *get_smbios_table(uint8_t type, uint64_t *ret_size_lef
/* Skip over string table. */
for (;;) {
- while (size > 0 && *p != '\0') {
- p++;
- size--;
- }
- if (size == 0)
+ const uint8_t *e = memchr(p, 0, size);
+ if (!e)
return NULL;
- p++;
- size--;
- /* Double NUL terminates string table. */
- if (*p == '\0') {
- if (size == 0)
- return NULL;
+ if (e == p) {/* Double NUL byte means we've reached the end of the string table. */
p++;
+ size--;
break;
}
+
+ size -= e + 1 - p;
+ p = e + 1;
}
}

View File

@ -0,0 +1,59 @@
From ee8090db1c2fe4df29fd0b134d80791eced6434d Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Sun, 21 May 2023 15:18:21 +0100
Subject: [PATCH] stub: measure SMBIOS kernel-cmdline-extra in PCR12
PCR1, where SMBIOS strings are measured, is filled with data that is not
under the control of the machine owner. Measure cmdline extensions in
PCR12 too, where we measure other optional addons that are loaded by
sd-stub.
(cherry picked from commit 2c90b5ec63ab420d074ebe4f5c6881737c9bc155)
Related: RHEL-16952
---
man/systemd-stub.xml | 5 ++++-
src/boot/efi/stub.c | 7 +++++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml
index 66c158c44d..edfc299bc2 100644
--- a/man/systemd-stub.xml
+++ b/man/systemd-stub.xml
@@ -66,6 +66,9 @@
<listitem><para>A compiled binary DeviceTree will be looked for in the <literal>.dtb</literal> PE
section.</para></listitem>
+ <listitem><para>Kernel version information, i.e. the output of <command>uname -r</command> for the
+ kernel included in the UKI, in the <literal>.uname</literal> PE section.</para></listitem>
+
<listitem><para>The kernel command line to pass to the invoked kernel will be looked for in the
<literal>.cmdline</literal> PE section.</para></listitem>
@@ -394,7 +397,7 @@
<varlistentry>
<term><varname>io.systemd.stub.kernel-cmdline-extra</varname></term>
<listitem><para>If set, the value of this string is added to the list of kernel command line
- arguments that are passed to the kernel.</para></listitem>
+ arguments that are measured in PCR12 and passed to the kernel.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index 86eae2e350..a195612f0e 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -282,6 +282,13 @@ static EFI_STATUS real_main(EFI_HANDLE image) {
if (extra) {
_cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra);
cmdline = xasprintf("%ls %ls", tmp, extra16);
+
+ /* SMBIOS strings are measured in PCR1, but we also want to measure them in our specific
+ * PCR12, as firmware-owned PCRs are very difficult to use as they'll contain unpredictable
+ * measurements that are not under control of the machine owner. */
+ m = false;
+ (void) tpm_log_load_options(extra16, &m);
+ parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
}
export_variables(loaded_image);

View File

@ -0,0 +1,28 @@
From 3a31a48e1bda8d8799695a878a872fd30d5c3e45 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Fri, 12 May 2023 00:49:57 +0100
Subject: [PATCH] efi: support passing empty cmdline to mangle_stub_cmdline()
Just return instead of crashing
(cherry picked from commit e715d82de6694d82a17921b5ccbcf47398604068)
Related: RHEL-16952
---
src/boot/efi/util.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c
index bd821a5afb..b8befb4b0c 100644
--- a/src/boot/efi/util.c
+++ b/src/boot/efi/util.c
@@ -272,6 +272,9 @@ char16_t *xstr8_to_path(const char *str8) {
}
void mangle_stub_cmdline(char16_t *cmdline) {
+ if (!cmdline)
+ return;
+
for (; *cmdline != '\0'; cmdline++)
/* Convert ASCII control characters to spaces. */
if (*cmdline <= 0x1F)

View File

@ -0,0 +1,61 @@
From d0e7305306407992bebbf6785a03cf2062d8359b Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Fri, 12 May 2023 00:51:19 +0100
Subject: [PATCH] efi: set EFIVAR to stop Shim from uninstalling its protocol
We'll use it from the stub to validate files. Requires Shim 5.18.
By default, Shim uninstalls its protocol when calling StartImage(),
so when loading systemd-boot via shim and then loading an UKI, the
UKI's sd-stub will no longer be able to use the shim verification
protocol by default.
(cherry picked from commit e1f1b5fc62f721a3a4c14d97ad01447b2ac07d6d)
Related: RHEL-16952
---
src/boot/efi/boot.c | 4 ++++
src/boot/efi/shim.c | 9 +++++++++
src/boot/efi/shim.h | 1 +
3 files changed, 14 insertions(+)
diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c
index d859ffe0b8..5a9bfc9646 100644
--- a/src/boot/efi/boot.c
+++ b/src/boot/efi/boot.c
@@ -2644,6 +2644,10 @@ static EFI_STATUS real_main(EFI_HANDLE image) {
init_usec = time_usec();
+ /* Ask Shim to leave its protocol around, so that the stub can use it to validate PEs.
+ * By default, Shim uninstalls its protocol when calling StartImage(). */
+ shim_retain_protocol();
+
err = BS->OpenProtocol(
image,
MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL),
diff --git a/src/boot/efi/shim.c b/src/boot/efi/shim.c
index 5da298c10a..d2fd680bbc 100644
--- a/src/boot/efi/shim.c
+++ b/src/boot/efi/shim.c
@@ -100,3 +100,12 @@ EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path
return ret;
}
+
+void shim_retain_protocol(void) {
+ uint8_t value = 1;
+
+ /* Ask Shim to avoid uninstalling its security protocol, so that we can use it from sd-stub to
+ * validate PE addons. By default, Shim uninstalls its protocol when calling StartImage().
+ * Requires Shim 15.8. */
+ (void) efivar_set_raw(MAKE_GUID_PTR(SHIM_LOCK), u"ShimRetainProtocol", &value, sizeof(value), 0);
+}
diff --git a/src/boot/efi/shim.h b/src/boot/efi/shim.h
index 6d213f5efa..23fdc0923f 100644
--- a/src/boot/efi/shim.h
+++ b/src/boot/efi/shim.h
@@ -14,3 +14,4 @@
bool shim_loaded(void);
EFI_STATUS shim_load_image(EFI_HANDLE parent, const EFI_DEVICE_PATH *device_path, EFI_HANDLE *ret_image);
+void shim_retain_protocol(void);

View File

@ -0,0 +1,78 @@
From 7307ab86351846cb750f3fcd35db7d9de9aefdf0 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Tue, 23 May 2023 01:45:40 +0100
Subject: [PATCH] ukify: use empty stub for addons
Instead of picking up sd-stub, which is runnable, add an empty
addon stub that just returns an error if executed
(cherry picked from commit f644ea3ed7ec22c28814b194e4e5bbbf2fa98560)
Related: RHEL-16952
---
src/boot/efi/addon.c | 15 +++++++++++++++
src/boot/efi/meson.build | 13 ++++++++++---
2 files changed, 25 insertions(+), 3 deletions(-)
create mode 100644 src/boot/efi/addon.c
diff --git a/src/boot/efi/addon.c b/src/boot/efi/addon.c
new file mode 100644
index 0000000000..959e54b5cc
--- /dev/null
+++ b/src/boot/efi/addon.c
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "efi.h"
+#include "macro-fundamental.h"
+
+/* Magic string for recognizing our own binaries */
+_used_ _section_(".sdmagic") static const char magic[] =
+ "#### LoaderInfo: systemd-addon " GIT_VERSION " ####";
+
+/* This is intended to carry data, not to be executed */
+
+EFIAPI EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *system_table);
+EFIAPI EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *system_table) {
+ return EFI_UNSUPPORTED;
+}
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index 09c40a280b..9e5d535b5b 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -403,6 +403,8 @@ stub_sources = files(
'stub.c',
)
+addon_sources = files('addon.c')
+
if efi_arch[1] in ['ia32', 'x86_64']
stub_sources += files('linux_x86.c')
endif
@@ -430,7 +432,8 @@ endif
systemd_boot_objects = []
stub_objects = []
-foreach file : fundamental_source_paths + common_sources + systemd_boot_sources + stub_sources
+addon_objects = []
+foreach file : fundamental_source_paths + common_sources + systemd_boot_sources + stub_sources + addon_sources
# FIXME: replace ''.format(file) with fs.name(file) when meson_version requirement is >= 0.59.0
o_file = custom_target('@0@.o'.format(file).split('/')[-1],
input : file,
@@ -443,10 +446,14 @@ foreach file : fundamental_source_paths + common_sources + systemd_boot_sources
if (fundamental_source_paths + common_sources + stub_sources).contains(file)
stub_objects += o_file
endif
+ if (fundamental_source_paths + common_sources + addon_sources).contains(file)
+ addon_objects += o_file
+ endif
endforeach
-foreach tuple : [['systemd-boot@0@.@1@', systemd_boot_objects, false, 'systemd-boot'],
- ['linux@0@.@1@.stub', stub_objects, true, 'systemd-stub']]
+foreach tuple : [['systemd-boot@0@.@1@', systemd_boot_objects, false, 'systemd-boot',],
+ ['linux@0@.@1@.stub', stub_objects, true, 'systemd-stub'],
+ ['addon@0@.@1@.stub', addon_objects, true, 'addon']]
elf = custom_target(
tuple[0].format(efi_arch[0], 'elf'),
input : tuple[1],

View File

@ -0,0 +1,389 @@
From 2f2729382327bde136559a0d0ac15740d76108f9 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Fri, 12 May 2023 00:55:58 +0100
Subject: [PATCH] stub: allow loading and verifying cmdline addons
Files placed in /EFI/Linux/UKI.efi.extra.d/ and /loader/addons/ are
opened and verified using the LoadImage protocol, and will thus get
verified via shim/firmware.
If they are valid signed PE files, the .cmdline section will be
extracted and appended. If there are multiple addons in each directory,
they will be parsed in alphanumerical order.
Optionally the .uname sections are also matched if present, so
that they can be used to filter out addons as well if needed, and only
addons that correspond exactly to the UKI being loaded are used.
It is recommended to also always add a .sbat section to addons, so
that they can be mass-revoked with just a policy update.
The files must have a .addon.efi suffix.
Files in the per-UKI directory are parsed, sorted, measured and
appended first. Then, files in the generic directory are processed.
(cherry picked from commit 05c9f9c2517c54b98d55f08f8afa67c79be861e8)
Related: RHEL-16952
---
man/systemd-stub.xml | 32 ++++++
src/boot/efi/cpio.c | 2 +-
src/boot/efi/cpio.h | 2 +
src/boot/efi/meson.build | 2 +-
src/boot/efi/stub.c | 216 +++++++++++++++++++++++++++++++++++++++
5 files changed, 252 insertions(+), 2 deletions(-)
diff --git a/man/systemd-stub.xml b/man/systemd-stub.xml
index edfc299bc2..21035cc944 100644
--- a/man/systemd-stub.xml
+++ b/man/systemd-stub.xml
@@ -28,8 +28,10 @@
<para><filename>/usr/lib/systemd/boot/efi/linuxx64.efi.stub</filename></para>
<para><filename>/usr/lib/systemd/boot/efi/linuxia32.efi.stub</filename></para>
<para><filename>/usr/lib/systemd/boot/efi/linuxaa64.efi.stub</filename></para>
+ <para><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename></para>
<para><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.cred</filename></para>
<para><filename><replaceable>ESP</replaceable>/.../<replaceable>foo</replaceable>.efi.extra.d/*.raw</filename></para>
+ <para><filename><replaceable>ESP</replaceable>/loader/addons/*.addon.efi</filename></para>
<para><filename><replaceable>ESP</replaceable>/loader/credentials/*.cred</filename></para>
</refsynopsisdiv>
@@ -148,11 +150,41 @@
details on system extension images. The generated <command>cpio</command> archive containing these
system extension images is measured into TPM PCR 13 (if a TPM is present).</para></listitem>
+ <listitem><para>Similarly, files
+ <filename><replaceable>foo</replaceable>.efi.extra.d/*.addon.efi</filename>
+ are loaded and verified as PE binaries, and a <literal>.cmdline</literal> section is parsed from them.
+ In case Secure Boot is enabled, these files will be validated using keys in UEFI DB, Shim's DB or
+ Shim's MOK, and will be rejected otherwise. Additionally, if the both the addon and the UKI contain a
+ a <literal>.uname</literal> section, the addon will be rejected if they do not exactly match. It is
+ recommended to always add a <literal>.sbat</literal> section to all signed addons, so that they may be
+ revoked with a SBAT policy update, without requiring blocklisting via DBX/MOKX. The
+ <citerefentry><refentrytitle>ukify</refentrytitle><manvolnum>1</manvolnum></citerefentry> tool will
+ add a SBAT policy by default if none is passed when building addons. For more information on SBAT see
+ <ulink url="https://github.com/rhboot/shim/blob/main/SBAT.md">Shim's documentation.</ulink>
+ Addons are supposed to be used to pass additional kernel command line parameters, regardless of the
+ kernel image being booted, for example to allow platform vendors to ship platform-specific
+ configuration. The loaded command line addon files are sorted, loaded, measured into TPM PCR 12 (if a
+ TPM is present) and appended to the kernel command line. UKI command line options are listed first,
+ then options from addons in <filename>/loader/addons/*.addon.efi</filename> are appended next, and
+ finally UKI-specific addons are appended last. Addons are always loaded in the same order based on the
+ filename, so that, given the same set of addons, the same set of measurements can be expected in
+ PCR12, however note that the filename is not protected by the PE signature, and as such an attacker
+ with write access to the ESP could potentially rename these files to change the order in which they
+ are loaded, in a way that could alter the functionality of the kernel, as some options might be order
+ dependent. If you sign such addons, you should pay attention to the PCR12 values and make use of an
+ attestation service so that improper use of your signed addons can be detected and dealt with using
+ one of the aforementioned revocation mechanisms.</para></listitem>
+
<listitem><para>Files <filename>/loader/credentials/*.cred</filename> are packed up in a
<command>cpio</command> archive and placed in the <filename>/.extra/global_credentials/</filename>
directory of the initrd file hierarchy. This is supposed to be used to pass additional credentials to
the initrd, regardless of the kernel being booted. The generated <command>cpio</command> archive is
measured into TPM PCR 12 (if a TPM is present)</para></listitem>
+
+ <listitem><para>Additionally, files <filename>/loader/addons/*.addon.efi</filename> are loaded and
+ verified as PE binaries, and a <literal>.cmdline</literal> section is parsed from them. This is
+ supposed to be used to pass additional command line parameters to the kernel, regardless of the kernel
+ being booted.</para></listitem>
</itemizedlist>
<para>These mechanisms may be used to parameterize and extend trusted (i.e. signed), immutable initrd
diff --git a/src/boot/efi/cpio.c b/src/boot/efi/cpio.c
index 0d95d40183..741c11f7ae 100644
--- a/src/boot/efi/cpio.c
+++ b/src/boot/efi/cpio.c
@@ -341,7 +341,7 @@ static EFI_STATUS measure_cpio(
return EFI_SUCCESS;
}
-static char16_t *get_dropin_dir(const EFI_DEVICE_PATH *file_path) {
+char16_t *get_dropin_dir(const EFI_DEVICE_PATH *file_path) {
if (!file_path)
return NULL;
diff --git a/src/boot/efi/cpio.h b/src/boot/efi/cpio.h
index e80e06723c..0f14edbf5f 100644
--- a/src/boot/efi/cpio.h
+++ b/src/boot/efi/cpio.h
@@ -32,3 +32,5 @@ EFI_STATUS pack_cpio_literal(
void **ret_buffer,
size_t *ret_buffer_size,
bool *ret_measured);
+
+char16_t *get_dropin_dir(const EFI_DEVICE_PATH *file_path);
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index 9e5d535b5b..b84ceb8c9f 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -386,6 +386,7 @@ common_sources = files(
'pe.c',
'random-seed.c',
'secure-boot.c',
+ 'shim.c',
'ticks.c',
'util.c',
'vmm.c',
@@ -393,7 +394,6 @@ common_sources = files(
systemd_boot_sources = files(
'boot.c',
- 'shim.c',
)
stub_sources = files(
diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c
index a195612f0e..2c7c56de3e 100644
--- a/src/boot/efi/stub.c
+++ b/src/boot/efi/stub.c
@@ -13,6 +13,7 @@
#include "pe.h"
#include "random-seed.h"
#include "secure-boot.h"
+#include "shim.h"
#include "splash.h"
#include "tpm-pcr.h"
#include "util.h"
@@ -181,6 +182,189 @@ static bool use_load_options(
return true;
}
+static EFI_STATUS load_addons_from_dir(
+ EFI_FILE *root,
+ const char16_t *prefix,
+ char16_t ***items,
+ size_t *n_items,
+ size_t *n_allocated) {
+
+ _cleanup_(file_closep) EFI_FILE *extra_dir = NULL;
+ _cleanup_free_ EFI_FILE_INFO *dirent = NULL;
+ size_t dirent_size = 0;
+ EFI_STATUS err;
+
+ assert(root);
+ assert(prefix);
+ assert(items);
+ assert(n_items);
+ assert(n_allocated);
+
+ err = open_directory(root, prefix, &extra_dir);
+ if (err == EFI_NOT_FOUND)
+ /* No extra subdir, that's totally OK */
+ return EFI_SUCCESS;
+ if (err != EFI_SUCCESS)
+ return log_error_status(err, "Failed to open addons directory '%ls': %m", prefix);
+
+ for (;;) {
+ _cleanup_free_ char16_t *d = NULL;
+
+ err = readdir_harder(extra_dir, &dirent, &dirent_size);
+ if (err != EFI_SUCCESS)
+ return log_error_status(err, "Failed to read addons directory of loaded image: %m");
+ if (!dirent) /* End of directory */
+ break;
+
+ if (dirent->FileName[0] == '.')
+ continue;
+ if (FLAGS_SET(dirent->Attribute, EFI_FILE_DIRECTORY))
+ continue;
+ if (!is_ascii(dirent->FileName))
+ continue;
+ if (strlen16(dirent->FileName) > 255) /* Max filename size on Linux */
+ continue;
+ if (!endswith_no_case(dirent->FileName, u".addon.efi"))
+ continue;
+
+ d = xstrdup16(dirent->FileName);
+
+ if (*n_items + 2 > *n_allocated) {
+ /* We allocate 16 entries at a time, as a matter of optimization */
+ if (*n_items > (SIZE_MAX / sizeof(uint16_t)) - 16) /* Overflow check, just in case */
+ return log_oom();
+
+ size_t m = *n_items + 16;
+ *items = xrealloc(*items, *n_allocated * sizeof(uint16_t *), m * sizeof(uint16_t *));
+ *n_allocated = m;
+ }
+
+ (*items)[(*n_items)++] = TAKE_PTR(d);
+ (*items)[*n_items] = NULL; /* Let's always NUL terminate, to make freeing via strv_free() easy */
+ }
+
+ return EFI_SUCCESS;
+
+}
+
+static EFI_STATUS cmdline_append_and_measure_addons(
+ EFI_HANDLE stub_image,
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_image,
+ const char16_t *prefix,
+ const char *uname,
+ bool *ret_parameters_measured,
+ char16_t **cmdline_append) {
+
+ _cleanup_(strv_freep) char16_t **items = NULL;
+ _cleanup_(file_closep) EFI_FILE *root = NULL;
+ _cleanup_free_ char16_t *buffer = NULL;
+ size_t n_items = 0, n_allocated = 0;
+ EFI_STATUS err;
+
+ assert(stub_image);
+ assert(loaded_image);
+ assert(prefix);
+ assert(ret_parameters_measured);
+ assert(cmdline_append);
+
+ if (!loaded_image->DeviceHandle)
+ return EFI_SUCCESS;
+
+ err = open_volume(loaded_image->DeviceHandle, &root);
+ if (err == EFI_UNSUPPORTED)
+ /* Error will be unsupported if the bootloader doesn't implement the file system protocol on
+ * its file handles. */
+ return EFI_SUCCESS;
+ if (err != EFI_SUCCESS)
+ return log_error_status(err, "Unable to open root directory: %m");
+
+ err = load_addons_from_dir(root, prefix, &items, &n_items, &n_allocated);
+ if (err != EFI_SUCCESS)
+ return err;
+
+ if (n_items == 0)
+ return EFI_SUCCESS; /* Empty directory */
+
+ /* Now, sort the files we found, to make this uniform and stable (and to ensure the TPM measurements
+ * are not dependent on read order) */
+ sort_pointer_array((void**) items, n_items, (compare_pointer_func_t) strcmp16);
+
+ for (size_t i = 0; i < n_items; i++) {
+ size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
+ _cleanup_free_ EFI_DEVICE_PATH *addon_path = NULL;
+ _cleanup_(unload_imagep) EFI_HANDLE addon = NULL;
+ EFI_LOADED_IMAGE_PROTOCOL *loaded_addon = NULL;
+ _cleanup_free_ char16_t *addon_spath = NULL;
+
+ addon_spath = xasprintf("%ls\\%ls", prefix, items[i]);
+ err = make_file_device_path(loaded_image->DeviceHandle, addon_spath, &addon_path);
+ if (err != EFI_SUCCESS)
+ return log_error_status(err, "Error making device path for %ls: %m", addon_spath);
+
+ /* By using shim_load_image, we cover both the case where the PE files are signed with MoK
+ * and with DB, and running with or without shim. */
+ err = shim_load_image(stub_image, addon_path, &addon);
+ if (err != EFI_SUCCESS) {
+ log_error_status(err,
+ "Failed to read '%ls' from '%ls', ignoring: %m",
+ items[i],
+ addon_spath);
+ continue;
+ }
+
+ err = BS->HandleProtocol(addon,
+ MAKE_GUID_PTR(EFI_LOADED_IMAGE_PROTOCOL),
+ (void **) &loaded_addon);
+ if (err != EFI_SUCCESS)
+ return log_error_status(err, "Failed to find protocol in %ls: %m", items[i]);
+
+ err = pe_memory_locate_sections(loaded_addon->ImageBase, unified_sections, addrs, szs);
+ if (err != EFI_SUCCESS || szs[UNIFIED_SECTION_CMDLINE] == 0) {
+ if (err == EFI_SUCCESS)
+ err = EFI_NOT_FOUND;
+ log_error_status(err,
+ "Unable to locate embedded .cmdline section in %ls, ignoring: %m",
+ items[i]);
+ continue;
+ }
+
+ /* We want to enforce that addons are not UKIs, i.e.: they must not embed a kernel. */
+ if (szs[UNIFIED_SECTION_LINUX] > 0) {
+ log_error_status(EFI_INVALID_PARAMETER, "%ls is a UKI, not an addon, ignoring: %m", items[i]);
+ continue;
+ }
+
+ /* Also enforce that, in case it is specified, .uname matches as a quick way to allow
+ * enforcing compatibility with a specific UKI only */
+ if (uname && szs[UNIFIED_SECTION_UNAME] > 0 &&
+ !strneq8(uname,
+ (char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_UNAME],
+ szs[UNIFIED_SECTION_UNAME])) {
+ log_error(".uname mismatch between %ls and UKI, ignoring", items[i]);
+ continue;
+ }
+
+ _cleanup_free_ char16_t *tmp = TAKE_PTR(buffer),
+ *extra16 = xstrn8_to_16((char *)loaded_addon->ImageBase + addrs[UNIFIED_SECTION_CMDLINE],
+ szs[UNIFIED_SECTION_CMDLINE]);
+ buffer = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", extra16);
+ }
+
+ mangle_stub_cmdline(buffer);
+
+ if (!isempty(buffer)) {
+ _cleanup_free_ char16_t *tmp = TAKE_PTR(*cmdline_append);
+ bool m = false;
+
+ (void) tpm_log_load_options(buffer, &m);
+ *ret_parameters_measured = m;
+
+ *cmdline_append = xasprintf("%ls%ls%ls", strempty(tmp), isempty(tmp) ? u"" : u" ", buffer);
+ }
+
+ return EFI_SUCCESS;
+}
+
static EFI_STATUS real_main(EFI_HANDLE image) {
_cleanup_free_ void *credential_initrd = NULL, *global_credential_initrd = NULL, *sysext_initrd = NULL, *pcrsig_initrd = NULL, *pcrpkey_initrd = NULL;
size_t credential_initrd_size = 0, global_credential_initrd_size = 0, sysext_initrd_size = 0, pcrsig_initrd_size = 0, pcrpkey_initrd_size = 0;
@@ -191,6 +375,7 @@ static EFI_STATUS real_main(EFI_HANDLE image) {
size_t addrs[_UNIFIED_SECTION_MAX] = {}, szs[_UNIFIED_SECTION_MAX] = {};
_cleanup_free_ char16_t *cmdline = NULL;
int sections_measured = -1, parameters_measured = -1;
+ _cleanup_free_ char *uname = NULL;
bool sysext_measured = false, m;
uint64_t loader_features = 0;
EFI_STATUS err;
@@ -263,6 +448,10 @@ static EFI_STATUS real_main(EFI_HANDLE image) {
/* Show splash screen as early as possible */
graphics_splash((const uint8_t*) loaded_image->ImageBase + addrs[UNIFIED_SECTION_SPLASH], szs[UNIFIED_SECTION_SPLASH]);
+ if (szs[UNIFIED_SECTION_UNAME] > 0)
+ uname = xstrndup8((char *)loaded_image->ImageBase + addrs[UNIFIED_SECTION_UNAME],
+ szs[UNIFIED_SECTION_UNAME]);
+
if (use_load_options(image, loaded_image, szs[UNIFIED_SECTION_CMDLINE] > 0, &cmdline)) {
/* Let's measure the passed kernel command line into the TPM. Note that this possibly
* duplicates what we already did in the boot menu, if that was already used. However, since
@@ -278,6 +467,33 @@ static EFI_STATUS real_main(EFI_HANDLE image) {
mangle_stub_cmdline(cmdline);
}
+ /* If we have any extra command line to add via PE addons, load them now and append, and
+ * measure the additions separately, after the embedded options, but before the smbios ones,
+ * so that the order is reversed from "most hardcoded" to "most dynamic". The global addons are
+ * loaded first, and the image-specific ones later, for the same reason. */
+ err = cmdline_append_and_measure_addons(
+ image,
+ loaded_image,
+ u"\\loader\\addons",
+ uname,
+ &m,
+ &cmdline);
+ if (err != EFI_SUCCESS)
+ log_error_status(err, "Error loading global addons, ignoring: %m");
+ parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
+
+ _cleanup_free_ char16_t *dropin_dir = get_dropin_dir(loaded_image->FilePath);
+ err = cmdline_append_and_measure_addons(
+ image,
+ loaded_image,
+ dropin_dir,
+ uname,
+ &m,
+ &cmdline);
+ if (err != EFI_SUCCESS)
+ log_error_status(err, "Error loading UKI-specific addons, ignoring: %m");
+ parameters_measured = parameters_measured < 0 ? m : (parameters_measured && m);
+
const char *extra = smbios_find_oem_string("io.systemd.stub.kernel-cmdline-extra");
if (extra) {
_cleanup_free_ char16_t *tmp = TAKE_PTR(cmdline), *extra16 = xstr8_to_16(extra);

View File

@ -0,0 +1,29 @@
From a84abc0caba660444d8210107da4c03c03150cd4 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Wed, 24 May 2023 11:18:18 +0100
Subject: [PATCH] TODO: remove fixed item
(cherry picked from commit f19b62756071cd6fc28b800062f591a3af88fe6a)
Related: RHEL-16952
---
TODO | 6 ------
1 file changed, 6 deletions(-)
diff --git a/TODO b/TODO
index c512bedb92..abe8faf5e8 100644
--- a/TODO
+++ b/TODO
@@ -249,12 +249,6 @@ Features:
parametrization, if needed. This matches our usual rule that admin config
should win over vendor defaults.
-* sd-stub: optionally allow users to configure manual kernel command line even
- in SecureBoot by authenticating it via shim's APIs, integrating with MOK and
- similar: instead of authenticating just PE code shim should be capable of
- authenticating any kind of data for us, including files containing kernel
- command lines.
-
* write a "search path" spec, that documents the prefixes to search in
(i.e. the usual /etc/, /run/, /usr/lib/ dance, potentially /usr/etc/), how to
sort found entries, how masking works and overriding.

View File

@ -0,0 +1,78 @@
From 529df7ab684596d7dae85da8360138647fa0c46b Mon Sep 17 00:00:00 2001
From: Maanya Goenka <maanyagoenka@microsoft.com>
Date: Wed, 27 Sep 2023 15:44:04 +0000
Subject: [PATCH] fix: do not check/verify slice units if recursive errors are
to be ignored
Before this fix, when recursive-errors was set to 'no' during a systemd-analyze
verification, the parent slice was checked regardless. The 'no' setting means that,
only the specified unit should be looked at and verified and errors in the slices should be
ignored. This commit fixes that issue.
Example:
Say we have a sample.service file:
[Unit]
Description=Sample Service
[Service]
ExecStart=/bin/echo "a"
Slice=support.slice
Before Change:
systemd-analyze verify --recursive-errors=no maanya/sample.service
Assertion 'u' failed at src/core/unit.c:153, function unit_has_name(). Aborting.
Aborted (core dumped)
After Change:
systemd-analyze verify --recursive-errors=no maanya/sample.service
{No errors}
(cherry picked from commit f660c7fa56b247c278fdb2ebcfea37912f249524)
Related: RHEL-1086
---
src/core/slice.c | 4 ++++
test/units/testsuite-65.sh | 12 ++++++++++++
2 files changed, 16 insertions(+)
diff --git a/src/core/slice.c b/src/core/slice.c
index c453aa033e..8f913a8d45 100644
--- a/src/core/slice.c
+++ b/src/core/slice.c
@@ -96,6 +96,10 @@ static int slice_verify(Slice *s) {
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to determine parent slice: %m");
+ /* If recursive errors are to be ignored, the parent slice should not be verified */
+ if (UNIT(s)->manager && FLAGS_SET(UNIT(s)->manager->test_run_flags, MANAGER_TEST_RUN_IGNORE_DEPENDENCIES))
+ return 0;
+
if (parent ? !unit_has_name(UNIT_GET_SLICE(UNIT(s)), parent) : !!UNIT_GET_SLICE(UNIT(s)))
return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(ENOEXEC), "Located outside of parent slice. Refusing.");
diff --git a/test/units/testsuite-65.sh b/test/units/testsuite-65.sh
index 4093c5a2a7..7c34948f82 100755
--- a/test/units/testsuite-65.sh
+++ b/test/units/testsuite-65.sh
@@ -217,6 +217,18 @@ set -e
rm /tmp/testfile.service
rm /tmp/testfile2.service
+cat <<EOF >/tmp/sample.service
+[Unit]
+Description = A Sample Service
+
+[Service]
+ExecStart = echo hello
+Slice=support.slice
+EOF
+
+# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
+systemd-analyze verify --recursive-errors=no /tmp/sample.service
+
cat <<EOF >/tmp/testfile.service
[Service]
ExecStart = echo hello

View File

@ -0,0 +1,31 @@
From 1db73293a8a69372a0cc020855f2402ab49600dd Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Thu, 24 Nov 2022 10:01:59 +0000
Subject: [PATCH] units: fix typo in Condition in systemd-boot-system-token
/lib/systemd/system/systemd-boot-system-token.service:20: Unknown key name 'ConditionPathExists|' in section 'Unit', ignoring
Follow-up for 0a1d8ac77a21ae0741bdf4af08f3a71354805ff1
(cherry picked from commit 0f6d54ca47662f1386ada65ed179a1afd6e727e4)
Related: RHEL-16952
---
units/systemd-boot-system-token.service | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/units/systemd-boot-system-token.service b/units/systemd-boot-system-token.service
index 63e523bb3e..ef5577549e 100644
--- a/units/systemd-boot-system-token.service
+++ b/units/systemd-boot-system-token.service
@@ -17,8 +17,8 @@ Conflicts=shutdown.target initrd-switch-root.target
Before=shutdown.target initrd-switch-root.target
# Only run this if the boot loader can support random seed initialization.
-ConditionPathExists|=/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
-ConditionPathExists|=/sys/firmware/efi/efivars/StubFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+ConditionPathExists=|/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
+ConditionPathExists=|/sys/firmware/efi/efivars/StubFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
# Only run this if there is no system token defined yet
ConditionPathExists=!/sys/firmware/efi/efivars/LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f

View File

@ -0,0 +1,184 @@
From bcb4e65f7ab49fd94b002ff9e1bba24237082726 Mon Sep 17 00:00:00 2001
From: Jacek Migacz <jmigacz@redhat.com>
Date: Mon, 26 Feb 2024 14:05:37 +0100
Subject: [PATCH] resolved: limit the number of signature validations in a
transaction
It has been demonstrated that tolerating an unbounded number of dnssec
signature validations is a bad idea. It is easy for a maliciously
crafted DNS reply to contain as many keytag collisions as desired,
causing us to iterate every dnskey and signature combination in vain.
The solution is to impose a maximum number of validations we will
tolerate. While collisions are not hard to craft, I still expect they
are unlikely in the wild so it should be safe to pick fairly small
values.
Here two limits are imposed: one on the maximum number of invalid
signatures encountered per rrset, and another on the total number of
validations performed per transaction.
(cherry picked from commit 67d0ce8843d612a2245d0966197d4f528b911b66)
Resolves: RHEL-26643
---
src/resolve/resolved-dns-dnssec.c | 16 ++++++++++++++--
src/resolve/resolved-dns-dnssec.h | 9 ++++++++-
src/resolve/resolved-dns-transaction.c | 19 ++++++++++++++++---
3 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index 426ea945ca..de2660e317 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -1176,6 +1176,7 @@ int dnssec_verify_rrset_search(
DnsResourceRecord **ret_rrsig) {
bool found_rrsig = false, found_invalid = false, found_expired_rrsig = false, found_unsupported_algorithm = false;
+ unsigned nvalidations = 0;
DnsResourceRecord *rrsig;
int r;
@@ -1221,6 +1222,14 @@ int dnssec_verify_rrset_search(
if (realtime == USEC_INFINITY)
realtime = now(CLOCK_REALTIME);
+ /* Have we seen an unreasonable number of invalid signaures? */
+ if (nvalidations > DNSSEC_INVALID_MAX) {
+ if (ret_rrsig)
+ *ret_rrsig = NULL;
+ *result = DNSSEC_TOO_MANY_VALIDATIONS;
+ return (int) nvalidations;
+ }
+
/* Yay, we found a matching RRSIG with a matching
* DNSKEY, awesome. Now let's verify all entries of
* the RRSet against the RRSIG and DNSKEY
@@ -1230,6 +1239,8 @@ int dnssec_verify_rrset_search(
if (r < 0)
return r;
+ nvalidations++;
+
switch (one_result) {
case DNSSEC_VALIDATED:
@@ -1240,7 +1251,7 @@ int dnssec_verify_rrset_search(
*ret_rrsig = rrsig;
*result = one_result;
- return 0;
+ return (int) nvalidations;
case DNSSEC_INVALID:
/* If the signature is invalid, let's try another
@@ -1287,7 +1298,7 @@ int dnssec_verify_rrset_search(
if (ret_rrsig)
*ret_rrsig = NULL;
- return 0;
+ return (int) nvalidations;
}
int dnssec_has_rrsig(DnsAnswer *a, const DnsResourceKey *key) {
@@ -2571,6 +2582,7 @@ static const char* const dnssec_result_table[_DNSSEC_RESULT_MAX] = {
[DNSSEC_FAILED_AUXILIARY] = "failed-auxiliary",
[DNSSEC_NSEC_MISMATCH] = "nsec-mismatch",
[DNSSEC_INCOMPATIBLE_SERVER] = "incompatible-server",
+ [DNSSEC_TOO_MANY_VALIDATIONS] = "too-many-validations",
};
DEFINE_STRING_TABLE_LOOKUP(dnssec_result, DnssecResult);
diff --git a/src/resolve/resolved-dns-dnssec.h b/src/resolve/resolved-dns-dnssec.h
index 954bb3ef9d..29b90130a3 100644
--- a/src/resolve/resolved-dns-dnssec.h
+++ b/src/resolve/resolved-dns-dnssec.h
@@ -9,12 +9,13 @@ typedef enum DnssecVerdict DnssecVerdict;
#include "resolved-dns-rr.h"
enum DnssecResult {
- /* These five are returned by dnssec_verify_rrset() */
+ /* These six are returned by dnssec_verify_rrset() */
DNSSEC_VALIDATED,
DNSSEC_VALIDATED_WILDCARD, /* Validated via a wildcard RRSIG, further NSEC/NSEC3 checks necessary */
DNSSEC_INVALID,
DNSSEC_SIGNATURE_EXPIRED,
DNSSEC_UNSUPPORTED_ALGORITHM,
+ DNSSEC_TOO_MANY_VALIDATIONS,
/* These two are added by dnssec_verify_rrset_search() */
DNSSEC_NO_SIGNATURE,
@@ -45,6 +46,12 @@ enum DnssecVerdict {
/* The longest digest we'll ever generate, of all digest algorithms we support */
#define DNSSEC_HASH_SIZE_MAX (MAX(20, 32))
+/* The most invalid signatures we will tolerate for a single rrset */
+#define DNSSEC_INVALID_MAX 5
+
+/* The total number of signature validations we will tolerate for a single transaction */
+#define DNSSEC_VALIDATION_MAX 64
+
int dnssec_rrsig_match_dnskey(DnsResourceRecord *rrsig, DnsResourceRecord *dnskey, bool revoked_ok);
int dnssec_key_match_rrsig(const DnsResourceKey *key, DnsResourceRecord *rrsig);
diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c
index 0306af84a2..ccca49d399 100644
--- a/src/resolve/resolved-dns-transaction.c
+++ b/src/resolve/resolved-dns-transaction.c
@@ -3140,11 +3140,14 @@ static int dnssec_validate_records(
DnsTransaction *t,
Phase phase,
bool *have_nsec,
+ unsigned *nvalidations,
DnsAnswer **validated) {
DnsResourceRecord *rr;
int r;
+ assert(nvalidations);
+
/* Returns negative on error, 0 if validation failed, 1 to restart validation, 2 when finished. */
DNS_ANSWER_FOREACH(rr, t->answer) {
@@ -3186,6 +3189,7 @@ static int dnssec_validate_records(
&rrsig);
if (r < 0)
return r;
+ *nvalidations += r;
log_debug("Looking at %s: %s", strna(dns_resource_record_to_string(rr)), dnssec_result_to_string(result));
@@ -3383,7 +3387,8 @@ static int dnssec_validate_records(
DNSSEC_SIGNATURE_EXPIRED,
DNSSEC_NO_SIGNATURE))
manager_dnssec_verdict(t->scope->manager, DNSSEC_BOGUS, rr->key);
- else /* DNSSEC_MISSING_KEY or DNSSEC_UNSUPPORTED_ALGORITHM */
+ else /* DNSSEC_MISSING_KEY, DNSSEC_UNSUPPORTED_ALGORITHM,
+ or DNSSEC_TOO_MANY_VALIDATIONS */
manager_dnssec_verdict(t->scope->manager, DNSSEC_INDETERMINATE, rr->key);
/* This is a primary response to our question, and it failed validation.
@@ -3476,13 +3481,21 @@ int dns_transaction_validate_dnssec(DnsTransaction *t) {
return r;
phase = DNSSEC_PHASE_DNSKEY;
- for (;;) {
+ for (unsigned nvalidations = 0;;) {
bool have_nsec = false;
- r = dnssec_validate_records(t, phase, &have_nsec, &validated);
+ r = dnssec_validate_records(t, phase, &have_nsec, &nvalidations, &validated);
if (r <= 0)
return r;
+ if (nvalidations > DNSSEC_VALIDATION_MAX) {
+ /* This reply requires an onerous number of signature validations to verify. Let's
+ * not waste our time trying, as this shouldn't happen for well-behaved domains
+ * anyway. */
+ t->answer_dnssec_result = DNSSEC_TOO_MANY_VALIDATIONS;
+ return 0;
+ }
+
/* Try again as long as we managed to achieve something */
if (r == 1)
continue;

View File

@ -0,0 +1,34 @@
From f7b027e1a0dcdd2c92a5f3b1bcd488912389dca4 Mon Sep 17 00:00:00 2001
From: Jacek Migacz <jmigacz@redhat.com>
Date: Mon, 26 Feb 2024 14:07:37 +0100
Subject: [PATCH] resolved: reduce the maximum nsec3 iterations to 100
According to RFC9267, the 2500 value is not helpful, and in fact it can
be harmful to permit a large number of iterations. Combined with limits
on the number of signature validations, I expect this will mitigate the
impact of maliciously crafted domains designed to cause excessive
cryptographic work.
(cherry picked from commit eba291124bc11f03732d1fc468db3bfac069f9cb)
Related: RHEL-26643
---
src/resolve/resolved-dns-dnssec.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index de2660e317..df25b7f619 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -27,8 +27,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(EC_KEY*, EC_KEY_free, NULL);
/* Permit a maximum clock skew of 1h 10min. This should be enough to deal with DST confusion */
#define SKEW_MAX (1*USEC_PER_HOUR + 10*USEC_PER_MINUTE)
-/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value */
-#define NSEC3_ITERATIONS_MAX 2500
+/* Maximum number of NSEC3 iterations we'll do. RFC5155 says 2500 shall be the maximum useful value, but
+ * RFC9276 § 3.2 says that we should reduce the acceptable iteration count */
+#define NSEC3_ITERATIONS_MAX 100
/*
* The DNSSEC Chain of trust:

View File

@ -0,0 +1,28 @@
From 4d3b9819a24f233f66f46a8d153f56e7d73cc809 Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Thu, 29 Feb 2024 17:51:33 +0100
Subject: [PATCH] efi: alignment of the PE file has to be at least 512 bytes
https://learn.microsoft.com/en-us/windows/win32/debug/pe-format?redirectedfrom=MSDN#optional-header-windows-specific-fields-image-only
Resolves: RHEL-26133
RHEL-only
[msekleta: this is RHEL-only because upstream no longer uses objcopy to create PE files]
---
src/boot/efi/meson.build | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build
index b84ceb8c9f..c4eb471451 100644
--- a/src/boot/efi/meson.build
+++ b/src/boot/efi/meson.build
@@ -485,6 +485,7 @@ foreach tuple : [['systemd-boot@0@.@1@', systemd_boot_objects, false, 'systemd-b
'-j', '.sdata',
'-j', '.sdmagic',
'-j', '.text',
+ '--file-alignment=512',
'--section-alignment=512',
efi_format,
'@INPUT@', '@OUTPUT@'],

View File

@ -0,0 +1,101 @@
From 1c4cb49d13264fe1e3de51d64b293b964439fee5 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Wed, 8 Feb 2023 23:06:27 +0000
Subject: [PATCH] units: change assert to condition to skip running in
initrd/os
These units are also present in the initrd, so instead of an assert,
just use a condition so they are skipped where they need to be skipped.
Fixes https://github.com/systemd/systemd/issues/26358
(cherry picked from commit 7ef09e2099a4f97ad40748d6b7c735b45aa4c990)
Related: RHEL-16182
---
units/systemd-pcrfs-root.service.in | 2 +-
units/systemd-pcrfs@.service.in | 2 +-
units/systemd-pcrmachine.service.in | 2 +-
units/systemd-pcrphase-initrd.service.in | 2 +-
units/systemd-pcrphase-sysinit.service.in | 2 +-
units/systemd-pcrphase.service.in | 2 +-
6 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/units/systemd-pcrfs-root.service.in b/units/systemd-pcrfs-root.service.in
index b0da413bb4..432eb9fd8c 100644
--- a/units/systemd-pcrfs-root.service.in
+++ b/units/systemd-pcrfs-root.service.in
@@ -14,7 +14,7 @@ DefaultDependencies=no
Conflicts=shutdown.target
After=systemd-pcrmachine.service
Before=shutdown.target
-AssertPathExists=!/etc/initrd-release
+ConditionPathExists=!/etc/initrd-release
ConditionSecurity=tpm2
ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
diff --git a/units/systemd-pcrfs@.service.in b/units/systemd-pcrfs@.service.in
index ec1ff118c3..6bbd4b72a5 100644
--- a/units/systemd-pcrfs@.service.in
+++ b/units/systemd-pcrfs@.service.in
@@ -15,7 +15,7 @@ BindsTo=%i.mount
Conflicts=shutdown.target
After=%i.mount systemd-pcrfs-root.service
Before=shutdown.target
-AssertPathExists=!/etc/initrd-release
+ConditionPathExists=!/etc/initrd-release
ConditionSecurity=tpm2
ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
diff --git a/units/systemd-pcrmachine.service.in b/units/systemd-pcrmachine.service.in
index e154a7eec1..f1c6ce9f26 100644
--- a/units/systemd-pcrmachine.service.in
+++ b/units/systemd-pcrmachine.service.in
@@ -13,7 +13,7 @@ Documentation=man:systemd-pcrmachine.service(8)
DefaultDependencies=no
Conflicts=shutdown.target
Before=sysinit.target shutdown.target
-AssertPathExists=!/etc/initrd-release
+ConditionPathExists=!/etc/initrd-release
ConditionSecurity=tpm2
ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
diff --git a/units/systemd-pcrphase-initrd.service.in b/units/systemd-pcrphase-initrd.service.in
index e437c7e1ce..6320dccf27 100644
--- a/units/systemd-pcrphase-initrd.service.in
+++ b/units/systemd-pcrphase-initrd.service.in
@@ -13,7 +13,7 @@ Documentation=man:systemd-pcrphase-initrd.service(8)
DefaultDependencies=no
Conflicts=shutdown.target initrd-switch-root.target
Before=sysinit.target cryptsetup-pre.target cryptsetup.target shutdown.target initrd-switch-root.target systemd-sysext.service
-AssertPathExists=/etc/initrd-release
+ConditionPathExists=/etc/initrd-release
ConditionSecurity=tpm2
ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
diff --git a/units/systemd-pcrphase-sysinit.service.in b/units/systemd-pcrphase-sysinit.service.in
index a22fbbe935..f00ad61257 100644
--- a/units/systemd-pcrphase-sysinit.service.in
+++ b/units/systemd-pcrphase-sysinit.service.in
@@ -14,7 +14,7 @@ DefaultDependencies=no
Conflicts=shutdown.target
After=sysinit.target
Before=basic.target shutdown.target
-AssertPathExists=!/etc/initrd-release
+ConditionPathExists=!/etc/initrd-release
ConditionSecurity=tpm2
ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
diff --git a/units/systemd-pcrphase.service.in b/units/systemd-pcrphase.service.in
index 5ba437e5b1..558f268857 100644
--- a/units/systemd-pcrphase.service.in
+++ b/units/systemd-pcrphase.service.in
@@ -12,7 +12,7 @@ Description=TPM2 PCR Barrier (User)
Documentation=man:systemd-pcrphase.service(8)
After=remote-fs.target remote-cryptsetup.target
Before=systemd-user-sessions.service
-AssertPathExists=!/etc/initrd-release
+ConditionPathExists=!/etc/initrd-release
ConditionSecurity=tpm2
ConditionPathExists=/sys/firmware/efi/efivars/StubPcrKernelImage-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f

View File

@ -0,0 +1,20 @@
From afb45c747f66526ec015372155c08baf74f4b988 Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
Date: Mon, 4 Mar 2024 13:37:41 +0100
Subject: [PATCH] ci: add configuration for regression sniffer GA
rhel-only
Related: RHEL-1086
---
.github/regression-sniffer.yml | 1 +
1 file changed, 1 insertion(+)
create mode 100644 .github/regression-sniffer.yml
diff --git a/.github/regression-sniffer.yml b/.github/regression-sniffer.yml
new file mode 100644
index 0000000000..3824028e92
--- /dev/null
+++ b/.github/regression-sniffer.yml
@@ -0,0 +1 @@
+upstream: systemd/systemd

View File

@ -0,0 +1,120 @@
From e12d41f584e33c0183a47d6c7211ccbf23f3e6a4 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Mon, 19 Dec 2022 22:26:30 +0100
Subject: [PATCH] bootctl: rework random seed logic to use open_mkdir_at() and
openat()
This doesn't really fix anything, but in general we should put stronger
emphasis on operating via dir fds rather than paths more (in particular
when writing files as opposed to consuming them).
No real change in behaviour.
(cherry picked from commit 6b97b267bf990b2ec553efae229b7996dc262996)
Related: RHEL-16952
---
src/boot/bootctl.c | 57 +++++++++++++++++++++++-----------------------
1 file changed, 29 insertions(+), 28 deletions(-)
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index c994be272b..9bb99eeec1 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -31,6 +31,7 @@
#include "fileio.h"
#include "find-esp.h"
#include "fs-util.h"
+#include "io-util.h"
#include "glyph-util.h"
#include "main-func.h"
#include "mkdir.h"
@@ -1983,53 +1984,47 @@ static int verb_list(int argc, char *argv[], void *userdata) {
}
static int install_random_seed(const char *esp) {
- _cleanup_(unlink_and_freep) char *tmp = NULL;
+ _cleanup_close_ int esp_fd = -EBADF, loader_dir_fd = -EBADF, fd = -EBADF;
+ _cleanup_free_ char *tmp = NULL;
uint8_t buffer[RANDOM_EFI_SEED_SIZE];
- _cleanup_free_ char *path = NULL;
- _cleanup_close_ int fd = -1;
size_t token_size;
- ssize_t n;
int r;
assert(esp);
- path = path_join(esp, "/loader/random-seed");
- if (!path)
- return log_oom();
+ esp_fd = open(esp, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
+ if (esp_fd < 0)
+ return log_error_errno(errno, "Failed to open ESP directory '%s': %m", esp);
+
+ loader_dir_fd = open_mkdir_at(esp_fd, "loader", O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOFOLLOW, 0775);
+ if (loader_dir_fd < 0)
+ return log_error_errno(loader_dir_fd, "Failed to open loader directory '%s/loader': %m", esp);
r = crypto_random_bytes(buffer, sizeof(buffer));
if (r < 0)
return log_error_errno(r, "Failed to acquire random seed: %m");
- /* Normally create_subdirs() should already have created everything we need, but in case "bootctl
- * random-seed" is called we want to just create the minimum we need for it, and not the full
- * list. */
- r = mkdir_parents(path, 0755);
- if (r < 0)
- return log_error_errno(r, "Failed to create parent directory for %s: %m", path);
-
- r = tempfn_random(path, "bootctl", &tmp);
- if (r < 0)
+ if (tempfn_random("random-seed", "bootctl", &tmp) < 0)
return log_oom();
- fd = open(tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
- if (fd < 0) {
- tmp = mfree(tmp);
+ fd = openat(loader_dir_fd, tmp, O_CREAT|O_EXCL|O_NOFOLLOW|O_NOCTTY|O_WRONLY|O_CLOEXEC, 0600);
+ if (fd < 0)
return log_error_errno(fd, "Failed to open random seed file for writing: %m");
- }
- n = write(fd, buffer, sizeof(buffer));
- if (n < 0)
- return log_error_errno(errno, "Failed to write random seed file: %m");
- if ((size_t) n != sizeof(buffer))
- return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing random seed file.");
+ r = loop_write(fd, buffer, sizeof(buffer), /* do_poll= */ false);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write random seed file: %m");
+ goto fail;
+ }
- if (rename(tmp, path) < 0)
- return log_error_errno(errno, "Failed to move random seed file into place: %m");
+ if (renameat(loader_dir_fd, tmp, loader_dir_fd, "random-seed") < 0) {
+ r = log_error_errno(errno, "Failed to move random seed file into place: %m");
+ goto fail;
+ }
tmp = mfree(tmp);
- log_info("Random seed file %s successfully written (%zu bytes).", path, sizeof(buffer));
+ log_info("Random seed file %s/loader/random-seed successfully written (%zu bytes).", esp, sizeof(buffer));
if (!arg_touch_variables)
return 0;
@@ -2092,6 +2087,12 @@ static int install_random_seed(const char *esp) {
}
return 0;
+
+fail:
+ if (tmp)
+ (void) unlinkat(loader_dir_fd, tmp, 0);
+
+ return r;
}
static int sync_everything(void) {

View File

@ -0,0 +1,51 @@
From 30aa0b51b3edba2cda99abf32e7965afb4ea311c Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 20 Dec 2022 11:15:51 +0100
Subject: [PATCH] bootctl: properly sync fs before/after moving random seed
file into place
Let's do a careful, focussed sync at the right places instead of a
blanket sync at the end. After all we want to run this on every boot
soon.
(cherry picked from commit 60315d59534fe59aacae26e2c497359a409af0b6)
Related: RHEL-16952
---
src/boot/bootctl.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 9bb99eeec1..5edcf0fc32 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -2017,6 +2017,11 @@ static int install_random_seed(const char *esp) {
goto fail;
}
+ if (fsync(fd) < 0 || fsync(loader_dir_fd) < 0) {
+ r = log_error_errno(errno, "Failed to sync random seed file: %m");
+ goto fail;
+ }
+
if (renameat(loader_dir_fd, tmp, loader_dir_fd, "random-seed") < 0) {
r = log_error_errno(errno, "Failed to move random seed file into place: %m");
goto fail;
@@ -2024,6 +2029,9 @@ static int install_random_seed(const char *esp) {
tmp = mfree(tmp);
+ if (syncfs(fd) < 0)
+ return log_error_errno(errno, "Failed to sync ESP file system: %m");
+
log_info("Random seed file %s/loader/random-seed successfully written (%zu bytes).", esp, sizeof(buffer));
if (!arg_touch_variables)
@@ -2468,7 +2476,6 @@ static int verb_random_seed(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
- (void) sync_everything();
return 0;
}

View File

@ -0,0 +1,90 @@
From a698bb3a2dd4fec2302e0aebef4d8359d8d4cf40 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 20 Dec 2022 11:48:21 +0100
Subject: [PATCH] bootctl: when updating EFI random seed file, hash old seed
with new one
Let's not regress in entropy in any case.
This does what f913c784ad4c93894fd6cb2590738113dff5a694 also does.
(cherry picked from commit 114172fbe75b247883dd873cafb9209e4a2bd778)
Related: RHEL-16952
---
src/boot/bootctl.c | 36 +++++++++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 5edcf0fc32..fe8d7e83a1 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -43,6 +43,7 @@
#include "pretty-print.h"
#include "random-util.h"
#include "rm-rf.h"
+#include "sha256.h"
#include "stat-util.h"
#include "stdio-util.h"
#include "string-table.h"
@@ -1987,11 +1988,15 @@ static int install_random_seed(const char *esp) {
_cleanup_close_ int esp_fd = -EBADF, loader_dir_fd = -EBADF, fd = -EBADF;
_cleanup_free_ char *tmp = NULL;
uint8_t buffer[RANDOM_EFI_SEED_SIZE];
+ struct sha256_ctx hash_state;
size_t token_size;
+ bool refreshed;
int r;
assert(esp);
+ assert_cc(RANDOM_EFI_SEED_SIZE == SHA256_DIGEST_SIZE);
+
esp_fd = open(esp, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
if (esp_fd < 0)
return log_error_errno(errno, "Failed to open ESP directory '%s': %m", esp);
@@ -2004,6 +2009,35 @@ static int install_random_seed(const char *esp) {
if (r < 0)
return log_error_errno(r, "Failed to acquire random seed: %m");
+ sha256_init_ctx(&hash_state);
+ sha256_process_bytes(&(const size_t) { sizeof(buffer) }, sizeof(size_t), &hash_state);
+ sha256_process_bytes(buffer, sizeof(buffer), &hash_state);
+
+ fd = openat(loader_dir_fd, "random-seed", O_NOFOLLOW|O_CLOEXEC|O_RDONLY|O_NOCTTY);
+ if (fd < 0) {
+ if (errno != ENOENT)
+ return log_error_errno(errno, "Failed to open old random seed file: %m");
+
+ sha256_process_bytes(&(const ssize_t) { 0 }, sizeof(ssize_t), &hash_state);
+ refreshed = false;
+ } else {
+ ssize_t n;
+
+ /* Hash the old seed in so that we never regress in entropy. */
+
+ n = read(fd, buffer, sizeof(buffer));
+ if (n < 0)
+ return log_error_errno(errno, "Failed to read old random seed file: %m");
+
+ sha256_process_bytes(&n, sizeof(n), &hash_state);
+ sha256_process_bytes(buffer, n, &hash_state);
+
+ fd = safe_close(fd);
+ refreshed = n > 0;
+ }
+
+ sha256_finish_ctx(&hash_state, buffer);
+
if (tempfn_random("random-seed", "bootctl", &tmp) < 0)
return log_oom();
@@ -2032,7 +2066,7 @@ static int install_random_seed(const char *esp) {
if (syncfs(fd) < 0)
return log_error_errno(errno, "Failed to sync ESP file system: %m");
- log_info("Random seed file %s/loader/random-seed successfully written (%zu bytes).", esp, sizeof(buffer));
+ log_info("Random seed file %s/loader/random-seed successfully %s (%zu bytes).", esp, refreshed ? "refreshed" : "written", sizeof(buffer));
if (!arg_touch_variables)
return 0;

View File

@ -0,0 +1,101 @@
From 7b9e71d4f8d01557da700f2da11870f6246abdf2 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 20 Dec 2022 11:53:37 +0100
Subject: [PATCH] sha256: add helper than hashes a buffer *and* its size
We use this pattern all the time in order to thward extension attacks,
add a helper to make it shorter.
(cherry picked from commit a16c65f3c4c93e24eda9cf7f14d5da4062c6ca10)
Related: RHEL-16952
---
src/boot/bootctl.c | 6 ++----
src/fundamental/sha256.h | 5 +++++
src/random-seed/random-seed.c | 12 ++++--------
3 files changed, 11 insertions(+), 12 deletions(-)
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index fe8d7e83a1..3e9a89a759 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -2010,8 +2010,7 @@ static int install_random_seed(const char *esp) {
return log_error_errno(r, "Failed to acquire random seed: %m");
sha256_init_ctx(&hash_state);
- sha256_process_bytes(&(const size_t) { sizeof(buffer) }, sizeof(size_t), &hash_state);
- sha256_process_bytes(buffer, sizeof(buffer), &hash_state);
+ sha256_process_bytes_and_size(buffer, sizeof(buffer), &hash_state);
fd = openat(loader_dir_fd, "random-seed", O_NOFOLLOW|O_CLOEXEC|O_RDONLY|O_NOCTTY);
if (fd < 0) {
@@ -2029,8 +2028,7 @@ static int install_random_seed(const char *esp) {
if (n < 0)
return log_error_errno(errno, "Failed to read old random seed file: %m");
- sha256_process_bytes(&n, sizeof(n), &hash_state);
- sha256_process_bytes(buffer, n, &hash_state);
+ sha256_process_bytes_and_size(buffer, n, &hash_state);
fd = safe_close(fd);
refreshed = n > 0;
diff --git a/src/fundamental/sha256.h b/src/fundamental/sha256.h
index 31790c2ebd..2857900c80 100644
--- a/src/fundamental/sha256.h
+++ b/src/fundamental/sha256.h
@@ -28,6 +28,11 @@ void sha256_init_ctx(struct sha256_ctx *ctx);
uint8_t *sha256_finish_ctx(struct sha256_ctx *ctx, uint8_t resbuf[static SHA256_DIGEST_SIZE]);
void sha256_process_bytes(const void *buffer, size_t len, struct sha256_ctx *ctx);
+static inline void sha256_process_bytes_and_size(const void *buffer, size_t len, struct sha256_ctx *ctx) {
+ sha256_process_bytes(&len, sizeof(len), ctx);
+ sha256_process_bytes(buffer, len, ctx);
+}
+
uint8_t* sha256_direct(const void *buffer, size_t sz, uint8_t result[static SHA256_DIGEST_SIZE]);
#define SHA256_DIRECT(buffer, sz) sha256_direct(buffer, sz, (uint8_t[SHA256_DIGEST_SIZE]) {})
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index ab1f942289..3bb78200c9 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -195,8 +195,7 @@ static int load_seed_file(
return log_oom();
sha256_init_ctx(hash_state);
- sha256_process_bytes(&k, sizeof(k), hash_state); /* Hash length to distinguish from new seed. */
- sha256_process_bytes(buf, k, hash_state);
+ sha256_process_bytes_and_size(buf, k, hash_state); /* Hash with length to distinguish from new seed. */
*ret_hash_state = hash_state;
}
@@ -289,8 +288,7 @@ static int save_seed_file(
if (hash_state) {
uint8_t hash[SHA256_DIGEST_SIZE];
- sha256_process_bytes(&k, sizeof(k), hash_state); /* Hash length to distinguish from old seed. */
- sha256_process_bytes(buf, k, hash_state);
+ sha256_process_bytes_and_size(buf, k, hash_state); /* Hash with length to distinguish from old seed. */
sha256_finish_ctx(hash_state, hash);
l = MIN((size_t)k, sizeof(hash));
memcpy((uint8_t *)buf + k - l, hash, l);
@@ -371,8 +369,7 @@ static int refresh_boot_seed(void) {
/* Hash the old seed in so that we never regress in entropy. */
sha256_init_ctx(&hash_state);
- sha256_process_bytes(&n, sizeof(n), &hash_state);
- sha256_process_bytes(seed_file_bytes, n, &hash_state);
+ sha256_process_bytes_and_size(seed_file_bytes, n, &hash_state);
/* We're doing this opportunistically, so if the seeding dance before didn't manage to initialize the
* RNG, there's no point in doing it here. Secondly, getrandom(GRND_NONBLOCK) has been around longer
@@ -393,8 +390,7 @@ static int refresh_boot_seed(void) {
assert(n == sizeof(buffer));
/* Hash the new seed into the state containing the old one to generate our final seed. */
- sha256_process_bytes(&n, sizeof(n), &hash_state);
- sha256_process_bytes(buffer, n, &hash_state);
+ sha256_process_bytes_and_size(buffer, n, &hash_state);
sha256_finish_ctx(&hash_state, buffer);
if (lseek(seed_fd, 0, SEEK_SET) < 0)

View File

@ -0,0 +1,191 @@
From 5c3c932aeef27dcc0b4cb91aeb7e52974add6998 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 20 Dec 2022 16:18:11 +0100
Subject: [PATCH] random-seed: don't refresh EFI random seed from random-seed.c
anymore
The ESP is simply not mounted early enough for this. We want that the
regular random seed handling runs as early as we possibly could, but we
don't want to delay this until the ESP is actually mounted.
Hence, let's remove this from random-seed.c here. A follow-up commit
will then add this back in, in a separate service which just calls
"bootctl random-seed".
Effectively reverts: f913c784ad4c93894fd6cb2590738113dff5a694
Fixes: #25769
(cherry picked from commit 29d487adb4ce70cc87a09ce2003d29789b2b4c3f)
Related: RHEL-16952
---
src/random-seed/random-seed.c | 111 +---------------------------------
1 file changed, 2 insertions(+), 109 deletions(-)
diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c
index 3bb78200c9..79544c1027 100644
--- a/src/random-seed/random-seed.c
+++ b/src/random-seed/random-seed.c
@@ -16,10 +16,7 @@
#include "alloc-util.h"
#include "build.h"
-#include "chase-symlinks.h"
-#include "efi-loader.h"
#include "fd-util.h"
-#include "find-esp.h"
#include "fs-util.h"
#include "io-util.h"
#include "log.h"
@@ -27,17 +24,13 @@
#include "missing_random.h"
#include "missing_syscall.h"
#include "mkdir.h"
-#include "parse-argument.h"
#include "parse-util.h"
-#include "path-util.h"
#include "pretty-print.h"
#include "random-util.h"
#include "string-table.h"
#include "string-util.h"
-#include "strv.h"
#include "sync-util.h"
#include "sha256.h"
-#include "terminal-util.h"
#include "util.h"
#include "xattr-util.h"
@@ -314,100 +307,6 @@ static int save_seed_file(
return 0;
}
-static int refresh_boot_seed(void) {
- uint8_t buffer[RANDOM_EFI_SEED_SIZE];
- struct sha256_ctx hash_state;
- _cleanup_free_ void *seed_file_bytes = NULL;
- _cleanup_free_ char *esp_path = NULL;
- _cleanup_close_ int seed_fd = -1, dir_fd = -1;
- size_t len;
- ssize_t n;
- int r;
-
- assert_cc(RANDOM_EFI_SEED_SIZE == SHA256_DIGEST_SIZE);
-
- r = find_esp_and_warn(NULL, NULL, /* unprivileged_mode= */ false, &esp_path,
- NULL, NULL, NULL, NULL, NULL);
- if (r < 0) {
- if (r == -ENOKEY) {
- log_debug_errno(r, "Couldn't find any ESP, so not updating ESP random seed.");
- return 0;
- }
- return r; /* find_esp_and_warn() already logged */
- }
-
- r = chase_symlinks("/loader", esp_path, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, &dir_fd);
- if (r < 0) {
- if (r == -ENOENT) {
- log_debug_errno(r, "Couldn't find ESP loader directory, so not updating ESP random seed.");
- return 0;
- }
- return log_error_errno(r, "Failed to open ESP loader directory: %m");
- }
- seed_fd = openat(dir_fd, "random-seed", O_NOFOLLOW|O_RDWR|O_CLOEXEC|O_NOCTTY);
- if (seed_fd < 0 && errno == ENOENT) {
- uint64_t features;
- r = efi_loader_get_features(&features);
- if (r == 0 && FLAGS_SET(features, EFI_LOADER_FEATURE_RANDOM_SEED))
- seed_fd = openat(dir_fd, "random-seed", O_CREAT|O_EXCL|O_RDWR|O_CLOEXEC|O_NOCTTY, 0600);
- else {
- log_debug_errno(seed_fd, "Couldn't find ESP random seed, and not booted with systemd-boot, so not updating ESP random seed.");
- return 0;
- }
- }
- if (seed_fd < 0)
- return log_error_errno(errno, "Failed to open EFI seed path: %m");
- r = random_seed_size(seed_fd, &len);
- if (r < 0)
- return log_error_errno(r, "Failed to determine EFI seed path length: %m");
- seed_file_bytes = malloc(len);
- if (!seed_file_bytes)
- return log_oom();
- n = loop_read(seed_fd, seed_file_bytes, len, false);
- if (n < 0)
- return log_error_errno(n, "Failed to read EFI seed file: %m");
-
- /* Hash the old seed in so that we never regress in entropy. */
- sha256_init_ctx(&hash_state);
- sha256_process_bytes_and_size(seed_file_bytes, n, &hash_state);
-
- /* We're doing this opportunistically, so if the seeding dance before didn't manage to initialize the
- * RNG, there's no point in doing it here. Secondly, getrandom(GRND_NONBLOCK) has been around longer
- * than EFI seeding anyway, so there's no point in having non-getrandom() fallbacks here. So if this
- * fails, just return early to cut our losses. */
- n = getrandom(buffer, sizeof(buffer), GRND_NONBLOCK);
- if (n < 0) {
- if (errno == EAGAIN) {
- log_debug_errno(errno, "Random pool not initialized yet, so skipping EFI seed update");
- return 0;
- }
- if (errno == ENOSYS) {
- log_debug_errno(errno, "getrandom() not available, so skipping EFI seed update");
- return 0;
- }
- return log_error_errno(errno, "Failed to generate random bytes for EFI seed: %m");
- }
- assert(n == sizeof(buffer));
-
- /* Hash the new seed into the state containing the old one to generate our final seed. */
- sha256_process_bytes_and_size(buffer, n, &hash_state);
- sha256_finish_ctx(&hash_state, buffer);
-
- if (lseek(seed_fd, 0, SEEK_SET) < 0)
- return log_error_errno(errno, "Failed to seek to beginning of EFI seed file: %m");
- r = loop_write(seed_fd, buffer, sizeof(buffer), false);
- if (r < 0)
- return log_error_errno(r, "Failed to write new EFI seed file: %m");
- if (ftruncate(seed_fd, sizeof(buffer)) < 0)
- return log_error_errno(errno, "Failed to truncate EFI seed file: %m");
- r = fsync_full(seed_fd);
- if (r < 0)
- return log_error_errno(r, "Failed to fsync EFI seed file: %m");
-
- log_debug("Updated random seed in ESP");
- return 0;
-}
-
static int help(int argc, char *argv[], void *userdata) {
_cleanup_free_ char *link = NULL;
int r;
@@ -525,10 +424,7 @@ static int run(int argc, char *argv[]) {
log_full_errno(level, open_rw_error, "Failed to open " RANDOM_SEED " for writing: %m");
log_full_errno(level, errno, "Failed to open " RANDOM_SEED " for reading: %m");
- r = -errno;
-
- (void) refresh_boot_seed();
- return missing ? 0 : r;
+ return missing ? 0 : -errno;
}
} else
write_seed_file = true;
@@ -538,7 +434,6 @@ static int run(int argc, char *argv[]) {
break;
case ACTION_SAVE:
- (void) refresh_boot_seed();
seed_fd = open(RANDOM_SEED, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_CREAT, 0600);
if (seed_fd < 0)
return log_error_errno(errno, "Failed to open " RANDOM_SEED ": %m");
@@ -556,11 +451,9 @@ static int run(int argc, char *argv[]) {
if (r < 0)
return r;
- if (read_seed_file) {
+ if (read_seed_file)
r = load_seed_file(seed_fd, random_fd, seed_size,
write_seed_file ? &hash_state : NULL);
- (void) refresh_boot_seed();
- }
if (r >= 0 && write_seed_file)
r = save_seed_file(seed_fd, random_fd, seed_size, synchronous, hash_state);

View File

@ -0,0 +1,34 @@
From 825d1d4535a7aafd7549bc7a5de7d72b5ec2cdbd Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 20 Dec 2022 16:34:36 +0100
Subject: [PATCH] bootctl: downgrade graceful messages to LOG_NOTICE
(cherry picked from commit 5019b0cb15d788e5e1f3c15eb7cdca6ee18a847c)
Related: RHEL-16952
---
src/boot/bootctl.c | 4 ++--
...-system-token.service => systemd-boot-random-seed.service} | 0
2 files changed, 2 insertions(+), 2 deletions(-)
rename units/{systemd-boot-system-token.service => systemd-boot-random-seed.service} (100%)
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 3e9a89a759..3833e755b1 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -2119,9 +2119,9 @@ static int install_random_seed(const char *esp) {
return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
if (r == -EINVAL)
- log_warning_errno(r, "Unable to write 'LoaderSystemToken' EFI variable (firmware problem?), ignoring: %m");
+ log_notice_errno(r, "Unable to write 'LoaderSystemToken' EFI variable (firmware problem?), ignoring: %m");
else
- log_warning_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
+ log_notice_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
} else
log_info("Successfully initialized system token in EFI variable with %zu bytes.", sizeof(buffer));
}
diff --git a/units/systemd-boot-system-token.service b/units/systemd-boot-random-seed.service
similarity index 100%
rename from units/systemd-boot-system-token.service
rename to units/systemd-boot-random-seed.service

View File

@ -0,0 +1,392 @@
From b7f74506b4a479edf2d7c5b9c08fb105e3fd7b29 Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 20 Dec 2022 17:16:47 +0100
Subject: [PATCH] =?UTF-8?q?units:=20rename/rework=20systemd-boot-system-to?=
=?UTF-8?q?ken.service=20=E2=86=92=20systemd-boot-random-seed.service?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This renames systemd-boot-system-token.service to
systemd-boot-random-seed.service and conditions it less strictly.
Previously, the job of the service was to write a "system token" EFI
variable if it was missing. It called "bootctl --graceful random-seed"
for that. With this change we condition it more liberally: instead of
calling it only when the "system token" EFI variable isn't set, we call
it whenever a boot loader interface compatible boot loader is used. This
means, previously it was invoked on the first boot only: now it is
invoked at every boot.
This doesn#t change the command that is invoked. That's because
previously already the "bootctl --graceful random-seed" did two things:
set the system token if not set yet *and* refresh the random seed in the
ESP. Previousy we put the focus on the former, now we shift the focus to
the latter.
With this simple change we can replace the logic
f913c784ad4c93894fd6cb2590738113dff5a694 added, but from a service that
can run much later and doesn't keep the ESP pinned.
(cherry picked from commit 921fc451cb7ce29467c5d87346db2b8bb72fdf18)
Related: RHEL-16952
---
man/bootctl.xml | 4 +-
man/rules/meson.build | 2 +-
man/systemd-boot-random-seed.service.xml | 99 +++++++++++++++++++++++
man/systemd-boot-system-token.service.xml | 76 -----------------
man/systemd-boot.xml | 2 +-
man/systemd-random-seed.service.xml | 6 +-
units/meson.build | 2 +-
units/systemd-boot-random-seed.service | 15 ++--
units/systemd-boot-update.service | 3 +-
units/systemd-random-seed.service.in | 4 +-
10 files changed, 120 insertions(+), 93 deletions(-)
create mode 100644 man/systemd-boot-random-seed.service.xml
delete mode 100644 man/systemd-boot-system-token.service.xml
diff --git a/man/bootctl.xml b/man/bootctl.xml
index d82f12d5bb..27b45c06d3 100644
--- a/man/bootctl.xml
+++ b/man/bootctl.xml
@@ -208,7 +208,7 @@
OS and a new seed to store in the ESP from the combination of both. The random seed passed to the OS
is credited to the kernel's entropy pool by the system manager during early boot, and permits
userspace to boot up with an entropy pool fully initialized very early on. Also see
- <citerefentry><refentrytitle>systemd-boot-system-token.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
+ <citerefentry><refentrytitle>systemd-boot-random-seed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>.</para>
<para>See <ulink url="https://systemd.io/RANDOM_SEEDS">Random Seeds</ulink> for further
information.</para></listitem>
@@ -550,7 +550,7 @@ Boot Loader Entries:
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
<ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink>,
- <citerefentry><refentrytitle>systemd-boot-system-token.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd-boot-random-seed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
</refentry>
diff --git a/man/rules/meson.build b/man/rules/meson.build
index 9c0d773e51..beecc893fd 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -863,7 +863,7 @@ manpages = [
'8',
['systemd-boot-check-no-failures'],
''],
- ['systemd-boot-system-token.service', '8', [], 'HAVE_GNU_EFI'],
+ ['systemd-boot-random-seed.service', '8', [], 'HAVE_GNU_EFI'],
['systemd-boot', '7', ['sd-boot'], 'HAVE_GNU_EFI'],
['systemd-cat', '1', [], ''],
['systemd-cgls', '1', [], ''],
diff --git a/man/systemd-boot-random-seed.service.xml b/man/systemd-boot-random-seed.service.xml
new file mode 100644
index 0000000000..86ce639828
--- /dev/null
+++ b/man/systemd-boot-random-seed.service.xml
@@ -0,0 +1,99 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="systemd-boot-random-seed.service" conditional='HAVE_GNU_EFI'
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+
+ <refentryinfo>
+ <title>systemd-boot-random-seed.service</title>
+ <productname>systemd</productname>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-boot-random-seed.service</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-boot-random-seed.service</refname>
+ <refpurpose>Refresh boot loader random seed at boot</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>systemd-boot-random-seed.service</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><filename>systemd-boot-random-seed.service</filename> is a system service that automatically
+ refreshes the boot loader random seed stored in the EFI System Partition (ESP), from the Linux kernel
+ entropy pool. The boot loader random seed is primarily consumed and updated by
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> from the
+ UEFI environemnt (or
+ <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry> if the
+ former is not used, but the latter is), and passed as initial RNG seed to the OS. It is an effective way
+ to ensure the OS comes up with a random pool that is fully initialized.</para>
+
+ <para>The service also automatically generates a 'system token' to store in an EFI variable in the
+ system's NVRAM. The boot loader may then combine the on-disk random seed and the system token by
+ cryptographic hashing, and pass it to the OS it boots as initialization seed for its entropy pool. Note:
+ the random seed stored in the ESP is refreshed on <emphasis>every</emphasis> reboot ensuring that
+ multiple subsequent boots will boot with different seeds. On the other hand, the system token is
+ generated randomly <emphasis>once</emphasis>, and then persistently stored in the system's EFI variable
+ storage, ensuring the same disk image won't result in the same series of boot loader seed values if used
+ on multiple systems in parallel.</para>
+
+ <para>The <filename>systemd-boot-random-seed.service</filename> unit invokes the <command>bootctl
+ random-seed</command> command, which updates the random seed in the ESP, and initializes the system
+ token if it's not initialized yet. The service is conditionalized so that it is run only when a boot
+ loader is used that implements the <ulink url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader
+ Interface</ulink>.</para> <para>For further details see
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, regarding
+ the command this service invokes.</para>
+
+ <para>Note the relationship between <filename>systemd-boot-random-seed.service</filename> and
+ <citerefentry><refentrytitle>systemd-random-seed</refentrytitle><manvolnum>8</manvolnum></citerefentry>. The
+ former maintains the random seed consumed and updated by the boot environment (i.e. by
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry> or
+ <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>), the
+ latter maintains a random seed consumed and updated by the OS itself. The former ensures that the OS has
+ a filled entropy pool already during earliest boot when regular disk access is not available yet
+ (i.e. when the OS random seed cannot be loaded yet). The latter is processed much later, once writable
+ disk access is available. Thus it cannot be used to seed the initial boot phase, but typically has much
+ higher quality of entropy. Both files are consumed and updated at boot, but at different
+ times. Specifically:</para>
+
+ <orderedlist>
+ <listitem><para>In UEFI mode, the <filename>systemd-boot</filename> or
+ <filename>systemd-stub</filename> components load the boot loader random seed off the ESP, hash it with
+ available entropy and the system token, and then update it on disk. A derived seed is passed to the
+ kernel which writes it to its entropy pool.</para></listitem>
+
+ <listitem><para>In userspace the <filename>systemd-random-seed.service</filename> service loads the OS
+ random seed, writes it to the kernel entropy pool, and then updates it on disk with a new value derived
+ from the kernel entropy pool.</para></listitem>
+
+ <listitem><para>In userspace the <filename>systemd-boot-random-seed.service</filename> service updates
+ the boot loader random seed with a new value derived from the kernel kernel entropy pool.</para></listitem>
+ </orderedlist>
+
+ <para>This logic should ensure that the kernel's entropy pool is seeded during earliest bool already, if
+ possible, but the highest quality entropy is propagated back to both on-disk seeds.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-random-seed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/systemd-boot-system-token.service.xml b/man/systemd-boot-system-token.service.xml
deleted file mode 100644
index f2e30a9b13..0000000000
--- a/man/systemd-boot-system-token.service.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version='1.0'?> <!--*-nxml-*-->
-<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
- "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
-<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
-
-<refentry id="systemd-boot-system-token.service" conditional='HAVE_GNU_EFI'
- xmlns:xi="http://www.w3.org/2001/XInclude">
-
- <refentryinfo>
- <title>systemd-boot-system-token.service</title>
- <productname>systemd</productname>
- </refentryinfo>
-
- <refmeta>
- <refentrytitle>systemd-boot-system-token.service</refentrytitle>
- <manvolnum>8</manvolnum>
- </refmeta>
-
- <refnamediv>
- <refname>systemd-boot-system-token.service</refname>
- <refpurpose>Generate an initial boot loader system token and random seed</refpurpose>
- </refnamediv>
-
- <refsynopsisdiv>
- <para><filename>systemd-boot-system-token.service</filename></para>
- </refsynopsisdiv>
-
- <refsect1>
- <title>Description</title>
-
- <para><filename>systemd-boot-system-token.service</filename> is a system service that automatically
- generates a 'system token' to store in an EFI variable in the system's NVRAM and a random seed to store
- on the EFI System Partition ESP on disk. The boot loader may then combine these two randomized data
- fields by cryptographic hashing, and pass it to the OS it boots as initialization seed for its entropy
- pool. The random seed stored in the ESP is refreshed on each reboot ensuring that multiple subsequent
- boots will boot with different seeds. The 'system token' is generated randomly once, and then
- persistently stored in the system's EFI variable storage.</para>
-
- <para>The <filename>systemd-boot-system-token.service</filename> unit invokes the <command>bootctl
- random-seed</command> command, which updates the random seed in the ESP, and initializes the 'system
- token' if it's not initialized yet. The service is conditionalized so that it is run only when all of the
- below apply:</para>
-
- <itemizedlist>
- <listitem><para>A boot loader is used that implements the <ulink
- url="https://systemd.io/BOOT_LOADER_INTERFACE">Boot Loader Interface</ulink> (which defines the 'system
- token' concept).</para></listitem>
-
- <listitem><para>Either a 'system token' was not set yet, or the boot loader has not passed the OS a
- random seed yet (and thus most likely has been missing the random seed file in the
- ESP).</para></listitem>
-
- <listitem><para>The system is not running in a VM environment. This case is explicitly excluded since
- on VM environments the ESP backing storage and EFI variable storage is typically not physically
- separated and hence booting the same OS image in multiple instances would replicate both, thus reusing
- the same random seed and 'system token' among all instances, which defeats its purpose. Note that it's
- still possible to use boot loader random seed provisioning in this mode, but the automatic logic
- implemented by this service has no effect then, and the user instead has to manually invoke the
- <command>bootctl random-seed</command> acknowledging these restrictions.</para></listitem>
- </itemizedlist>
-
- <para>For further details see
- <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>, regarding
- the command this service invokes.</para>
- </refsect1>
-
- <refsect1>
- <title>See Also</title>
- <para>
- <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>
- </para>
- </refsect1>
-
-</refentry>
diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml
index f96c4c6512..773d6988e3 100644
--- a/man/systemd-boot.xml
+++ b/man/systemd-boot.xml
@@ -526,7 +526,7 @@
<citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>loader.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-bless-boot.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>systemd-boot-system-token.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-boot-random-seed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>kernel-install</refentrytitle><manvolnum>8</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
<ulink url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification</ulink>,
diff --git a/man/systemd-random-seed.service.xml b/man/systemd-random-seed.service.xml
index a1e31cd460..bc8cf50a39 100644
--- a/man/systemd-random-seed.service.xml
+++ b/man/systemd-random-seed.service.xml
@@ -18,7 +18,7 @@
<refnamediv>
<refname>systemd-random-seed.service</refname>
<refname>systemd-random-seed</refname>
- <refpurpose>Load and save the system random seed at boot and shutdown</refpurpose>
+ <refpurpose>Load and save the OS system random seed at boot and shutdown</refpurpose>
</refnamediv>
<refsynopsisdiv>
@@ -86,7 +86,9 @@
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
<citerefentry><refentrytitle>random</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
<citerefentry><refentrytitle>systemd-boot</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
- <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>4</manvolnum></citerefentry>
+ <citerefentry><refentrytitle>systemd-stub</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>bootctl</refentrytitle><manvolnum>4</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-boot-random-seed.service</refentrytitle><manvolnum>8</manvolnum></citerefentry>
</para>
</refsect1>
diff --git a/units/meson.build b/units/meson.build
index 3a1f5229a0..cfc96a9111 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -105,7 +105,7 @@ units = [
['systemd-ask-password-wall.path', '',
'multi-user.target.wants/'],
['systemd-ask-password-wall.service', ''],
- ['systemd-boot-system-token.service', 'HAVE_GNU_EFI',
+ ['systemd-boot-random-seed.service', 'HAVE_GNU_EFI',
'sysinit.target.wants/'],
['systemd-boot-update.service', 'HAVE_GNU_EFI'],
['systemd-coredump.socket', 'ENABLE_COREDUMP',
diff --git a/units/systemd-boot-random-seed.service b/units/systemd-boot-random-seed.service
index ef5577549e..4fa286071d 100644
--- a/units/systemd-boot-random-seed.service
+++ b/units/systemd-boot-random-seed.service
@@ -8,22 +8,21 @@
# (at your option) any later version.
[Unit]
-Description=Store a System Token in an EFI Variable
-Documentation=man:systemd-boot-system-token.service(8)
+Description=Update Boot Loader Random Seed
+Documentation=man:systemd-boot-random-seed.service(8) man:random(4)
DefaultDependencies=no
After=local-fs.target systemd-random-seed.service
-Conflicts=shutdown.target initrd-switch-root.target
-Before=shutdown.target initrd-switch-root.target
+Conflicts=shutdown.target
+Before=sysinit.target shutdown.target
+ConditionVirtualization=!container
+ConditionPathExists=!/etc/initrd-release
# Only run this if the boot loader can support random seed initialization.
ConditionPathExists=|/sys/firmware/efi/efivars/LoaderFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
ConditionPathExists=|/sys/firmware/efi/efivars/StubFeatures-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
-# Only run this if there is no system token defined yet
-ConditionPathExists=!/sys/firmware/efi/efivars/LoaderSystemToken-4a67b082-0a4c-41cf-b6c7-440b29bb8c4f
-
[Service]
Type=oneshot
RemainAfterExit=yes
-ExecStart=bootctl random-seed --graceful
+ExecStart=bootctl --graceful random-seed
diff --git a/units/systemd-boot-update.service b/units/systemd-boot-update.service
index 61ff12762a..fe63fde35a 100644
--- a/units/systemd-boot-update.service
+++ b/units/systemd-boot-update.service
@@ -10,9 +10,10 @@
[Unit]
Description=Automatic Boot Loader Update
Documentation=man:bootctl(1)
+
DefaultDependencies=no
-Conflicts=shutdown.target
After=local-fs.target
+Conflicts=shutdown.target
Before=sysinit.target shutdown.target systemd-update-done.service
[Service]
diff --git a/units/systemd-random-seed.service.in b/units/systemd-random-seed.service.in
index 1aa9af9710..d57b2d1269 100644
--- a/units/systemd-random-seed.service.in
+++ b/units/systemd-random-seed.service.in
@@ -8,14 +8,16 @@
# (at your option) any later version.
[Unit]
-Description=Load/Save Random Seed
+Description=Load/Save OS Random Seed
Documentation=man:systemd-random-seed.service(8) man:random(4)
+
DefaultDependencies=no
RequiresMountsFor={{RANDOM_SEED}}
Conflicts=shutdown.target
After=systemd-remount-fs.service
Before=first-boot-complete.target shutdown.target
Wants=first-boot-complete.target
+
ConditionVirtualization=!container
ConditionPathExists=!/etc/initrd-release

View File

@ -0,0 +1,171 @@
From 6fb21c25c859d950c1d9ab3b954573e87e87e64a Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 20 Dec 2022 18:03:06 +0100
Subject: [PATCH] bootctl: split out setting of system token into function of
its own
Let's break a huge function in two. No code change, just some
refactoring.
(cherry picked from commit 54978e3f3b5394d26f53f4753bb1c9e3e5811408)
Related: RHEL-16952
---
src/boot/bootctl.c | 132 +++++++++++++++++++++++----------------------
1 file changed, 69 insertions(+), 63 deletions(-)
diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c
index 3833e755b1..00e8eda992 100644
--- a/src/boot/bootctl.c
+++ b/src/boot/bootctl.c
@@ -1984,12 +1984,79 @@ static int verb_list(int argc, char *argv[], void *userdata) {
return show_boot_entries(&config, arg_json_format_flags);
}
+static int set_system_token(void) {
+ uint8_t buffer[RANDOM_EFI_SEED_SIZE];
+ size_t token_size;
+ int r;
+
+ if (!arg_touch_variables)
+ return 0;
+
+ if (arg_root) {
+ log_warning("Acting on %s, skipping EFI variable setup.",
+ arg_image ? "image" : "root directory");
+ return 0;
+ }
+
+ if (!is_efi_boot()) {
+ log_notice("Not booted with EFI, skipping EFI variable setup.");
+ return 0;
+ }
+
+ r = getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
+ if (r < 0) {
+ if (r != -ENXIO)
+ log_warning_errno(r, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
+ } else if (r == 0) {
+ log_notice("Not writing system token, because $SYSTEMD_WRITE_SYSTEM_TOKEN is set to false.");
+ return 0;
+ }
+
+ r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), NULL, NULL, &token_size);
+ if (r == -ENODATA)
+ log_debug_errno(r, "LoaderSystemToken EFI variable is invalid (too short?), replacing.");
+ else if (r < 0) {
+ if (r != -ENOENT)
+ return log_error_errno(r, "Failed to test system token validity: %m");
+ } else {
+ if (token_size >= sizeof(buffer)) {
+ /* Let's avoid writes if we can, and initialize this only once. */
+ log_debug("System token already written, not updating.");
+ return 0;
+ }
+
+ log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sizeof(buffer));
+ }
+
+ r = crypto_random_bytes(buffer, sizeof(buffer));
+ if (r < 0)
+ return log_error_errno(r, "Failed to acquire random seed: %m");
+
+ /* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
+ * and possibly get identification information or too much insight into the kernel's entropy pool
+ * state. */
+ RUN_WITH_UMASK(0077) {
+ r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
+ if (r < 0) {
+ if (!arg_graceful)
+ return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
+
+ if (r == -EINVAL)
+ log_notice_errno(r, "Unable to write 'LoaderSystemToken' EFI variable (firmware problem?), ignoring: %m");
+ else
+ log_notice_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
+ } else
+ log_info("Successfully initialized system token in EFI variable with %zu bytes.", sizeof(buffer));
+ }
+
+ return 0;
+}
+
static int install_random_seed(const char *esp) {
_cleanup_close_ int esp_fd = -EBADF, loader_dir_fd = -EBADF, fd = -EBADF;
_cleanup_free_ char *tmp = NULL;
uint8_t buffer[RANDOM_EFI_SEED_SIZE];
struct sha256_ctx hash_state;
- size_t token_size;
bool refreshed;
int r;
@@ -2066,68 +2133,7 @@ static int install_random_seed(const char *esp) {
log_info("Random seed file %s/loader/random-seed successfully %s (%zu bytes).", esp, refreshed ? "refreshed" : "written", sizeof(buffer));
- if (!arg_touch_variables)
- return 0;
-
- if (!is_efi_boot()) {
- log_notice("Not booted with EFI, skipping EFI variable setup.");
- return 0;
- }
-
- if (arg_root) {
- log_warning("Acting on %s, skipping EFI variable setup.",
- arg_image ? "image" : "root directory");
- return 0;
- }
-
- r = getenv_bool("SYSTEMD_WRITE_SYSTEM_TOKEN");
- if (r < 0) {
- if (r != -ENXIO)
- log_warning_errno(r, "Failed to parse $SYSTEMD_WRITE_SYSTEM_TOKEN, ignoring.");
- } else if (r == 0) {
- log_notice("Not writing system token, because $SYSTEMD_WRITE_SYSTEM_TOKEN is set to false.");
- return 0;
- }
-
- r = efi_get_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), NULL, NULL, &token_size);
- if (r == -ENODATA)
- log_debug_errno(r, "LoaderSystemToken EFI variable is invalid (too short?), replacing.");
- else if (r < 0) {
- if (r != -ENOENT)
- return log_error_errno(r, "Failed to test system token validity: %m");
- } else {
- if (token_size >= sizeof(buffer)) {
- /* Let's avoid writes if we can, and initialize this only once. */
- log_debug("System token already written, not updating.");
- return 0;
- }
-
- log_debug("Existing system token size (%zu) does not match our expectations (%zu), replacing.", token_size, sizeof(buffer));
- }
-
- r = crypto_random_bytes(buffer, sizeof(buffer));
- if (r < 0)
- return log_error_errno(r, "Failed to acquire random seed: %m");
-
- /* Let's write this variable with an umask in effect, so that unprivileged users can't see the token
- * and possibly get identification information or too much insight into the kernel's entropy pool
- * state. */
- RUN_WITH_UMASK(0077) {
- r = efi_set_variable(EFI_LOADER_VARIABLE(LoaderSystemToken), buffer, sizeof(buffer));
- if (r < 0) {
- if (!arg_graceful)
- return log_error_errno(r, "Failed to write 'LoaderSystemToken' EFI variable: %m");
-
- if (r == -EINVAL)
- log_notice_errno(r, "Unable to write 'LoaderSystemToken' EFI variable (firmware problem?), ignoring: %m");
- else
- log_notice_errno(r, "Unable to write 'LoaderSystemToken' EFI variable, ignoring: %m");
- } else
- log_info("Successfully initialized system token in EFI variable with %zu bytes.", sizeof(buffer));
- }
-
- return 0;
-
+ return set_system_token();
fail:
if (tmp)
(void) unlinkat(loader_dir_fd, tmp, 0);

View File

@ -0,0 +1,36 @@
From 7d3b9e98e22f92561c98f6bf838cc830324834e3 Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Tue, 13 Dec 2022 10:50:01 +0000
Subject: [PATCH] execute: Pass AT_FDCWD instead of -1
Let's enforce that callers pass AT_FDCWD as read_dfd to load_credential()
to avoid an assert() in read_full_file_full() if read_dfd is -1.
(cherry picked from commit 661e4251a5b157d1aee1df98fbd2f0c95285ebba)
Resolves: RHEL-31783
---
src/core/execute.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/core/execute.c b/src/core/execute.c
index ea36254241..13222ddea3 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -2662,6 +2662,7 @@ static int load_credential(
assert(id);
assert(path);
assert(unit);
+ assert(read_dfd >= 0 || read_dfd == AT_FDCWD);
assert(write_dfd >= 0);
assert(left);
@@ -2888,7 +2889,7 @@ static int acquire_credentials(
lc->path,
lc->encrypted,
unit,
- -1,
+ AT_FDCWD,
dfd,
uid,
ownership_ok,

View File

@ -0,0 +1,48 @@
From 06a6d5c5d5c6f3a9eb7ae55a7635d69158593181 Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
Date: Thu, 11 Apr 2024 10:01:04 +0200
Subject: [PATCH] ci(src-git): update list of supported products
rhel-only
Related: RHEL-30372
---
.github/tracker-validator.yml | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/.github/tracker-validator.yml b/.github/tracker-validator.yml
index f88cc0a572..31ef28f6ea 100644
--- a/.github/tracker-validator.yml
+++ b/.github/tracker-validator.yml
@@ -12,17 +12,17 @@ products:
- rhel-9.2.0.z
- rhel-9.3.0
- rhel-9.3.0.z
- - rhel-9.4.0
- - rhel-9.4.0.z
- - rhel-9.5.0
- - rhel-9.5.0.z
- - rhel-9.6.0
- - rhel-9.6.0.z
- - rhel-9.7.0
- - rhel-9.7.0.z
- - rhel-9.8.0
- - rhel-9.8.0.z
- - rhel-9.9.0
- - rhel-9.9.0.z
- - rhel-9.10.0
- - rhel-9.10.0.z
+ - rhel-9.4
+ - rhel-9.4.z
+ - rhel-9.5
+ - rhel-9.5.z
+ - rhel-9.6
+ - rhel-9.6.z
+ - rhel-9.7
+ - rhel-9.7.z
+ - rhel-9.8
+ - rhel-9.8.z
+ - rhel-9.9
+ - rhel-9.9.z
+ - rhel-9.10
+ - rhel-9.10.z

View File

@ -0,0 +1,46 @@
From f387005b548bee7695c663167f9bd54e45636f6b Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Fri, 5 Apr 2024 15:56:58 +0200
Subject: [PATCH] coredump: by default process and store core files up to 1GiB
This is a major departure from our previous policy of soft core file
limit set to 0, i.e. core file processing and storage by
systemd-coredump was disabled. However, that policy made very difficult
for people to debug sporadic crashes with no known reproducer.
RHEL-only
Resolves: RHEL-15501
---
src/core/system.conf.in | 2 +-
src/coredump/coredump.conf | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/core/system.conf.in b/src/core/system.conf.in
index 624746e512..5d1f6d24f0 100644
--- a/src/core/system.conf.in
+++ b/src/core/system.conf.in
@@ -61,7 +61,7 @@
#DefaultLimitFSIZE=
#DefaultLimitDATA=
#DefaultLimitSTACK=
-DefaultLimitCORE=0:infinity
+#DefaultLimitCORE=
#DefaultLimitRSS=
#DefaultLimitNOFILE=1024:{{HIGH_RLIMIT_NOFILE}}
#DefaultLimitAS=
diff --git a/src/coredump/coredump.conf b/src/coredump/coredump.conf
index 1f75d48d33..b934c2afb7 100644
--- a/src/coredump/coredump.conf
+++ b/src/coredump/coredump.conf
@@ -17,8 +17,8 @@
[Coredump]
#Storage=external
#Compress=yes
-#ProcessSizeMax=2G
-#ExternalSizeMax=2G
+ProcessSizeMax=1G
+ExternalSizeMax=1G
#JournalSizeMax=767M
#MaxUse=
#KeepFree=

View File

@ -0,0 +1,35 @@
From e100e3855305a86367c690689833a460fa166428 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Tue, 5 Dec 2023 15:56:54 +0100
Subject: [PATCH] coredump: keep core files for two weeks
We have two mechanisms that remove old coredumps: systemd-coredump has
parameters based on disk use / remaining disk free, and systemd-tmpfiles does
cleanup based on time. The first mechanism should prevent us from using too much
disk space in case something is crashing continuously or there are very large
core files.
The limit of 3 days makes it likely that the core file will be gone by the time
the admin looks at the issue. E.g. if something crashes on Friday, the coredump
would likely be gone before people are back on Monday to look at it.
(cherry picked from commit f8d67130b8b492a1f2eedd07a3189051f98db648)
Related: RHEL-15501
---
tmpfiles.d/systemd.conf.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tmpfiles.d/systemd.conf.in b/tmpfiles.d/systemd.conf.in
index fa838d8d06..958935a04f 100644
--- a/tmpfiles.d/systemd.conf.in
+++ b/tmpfiles.d/systemd.conf.in
@@ -59,7 +59,7 @@ a+ /var/log/journal/%m/system.journal - - - - group:wheel:r--
{% endif %}
d /var/lib/systemd 0755 root root -
-d /var/lib/systemd/coredump 0755 root root 3d
+d /var/lib/systemd/coredump 0755 root root 2w
d /var/lib/private 0700 root root -
d /var/log/private 0700 root root -

View File

@ -0,0 +1,42 @@
From af01ea4040e2d6fdd15641793db688d01f2da046 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Sat, 23 Dec 2023 12:20:03 +0100
Subject: [PATCH] ukify: make the test happy with the latest OpenSSL
Which dropped some whitespaces in the output:
$ openssl version
OpenSSL 3.2.0 23 Nov 2023 (Library: OpenSSL 3.2.0 23 Nov 2023)
$ openssl x509 -in cert.pem -text -noout | grep Issuer
Issuer: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
$ openssl version
OpenSSL 3.0.9 30 May 2023 (Library: OpenSSL 3.0.9 30 May 2023)
$ openssl x509 -in cert.pem -text -noout | grep Issuer
Issuer: C = XX, L = Default City, O = Default Company Ltd
Making test-ukify unhappy:
> assert 'Issuer: CN = SecureBoot signing key on host' in out
E AssertionError: assert 'Issuer: CN = SecureBoot signing key on host' in '<...snip...>Issuer: CN=SecureBoot signing key on host archlinux2\n...'
(cherry picked from commit 338ed5bea4fcd0b5b1cdcfb96a789edf6251bbdd)
Related: RHEL-30372
---
src/ukify/test/test_ukify.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py
index 5a42a94799..31054eabea 100755
--- a/src/ukify/test/test_ukify.py
+++ b/src/ukify/test/test_ukify.py
@@ -850,7 +850,7 @@ def test_key_cert_generation(tmpdir):
'-noout',
], text = True)
assert 'Certificate' in out
- assert 'Issuer: CN = SecureBoot signing key on host' in out
+ assert re.search('Issuer: CN\s?=\s?SecureBoot signing key on host', out)
if __name__ == '__main__':
sys.exit(pytest.main(sys.argv))

View File

@ -0,0 +1,32 @@
From 1c4640a859937b84d3f31dd2fa054f7d744d65f4 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Sat, 3 Feb 2024 15:46:26 +0100
Subject: [PATCH] test_ukify: use raw string for the regex
To get rid of the "invalid escape sequence" warning:
=============================== warnings summary ===============================
../src/ukify/test/test_ukify.py:876
../src/ukify/test/test_ukify.py:876: SyntaxWarning: invalid escape sequence '\s'
assert re.search('Issuer: CN\s?=\s?SecureBoot signing key on host', out)
(cherry picked from commit a0485e07b38b3b1195a92ba86a173742f2bb867a)
Related: RHEL-30372
---
src/ukify/test/test_ukify.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/ukify/test/test_ukify.py b/src/ukify/test/test_ukify.py
index 31054eabea..f233e25cf7 100755
--- a/src/ukify/test/test_ukify.py
+++ b/src/ukify/test/test_ukify.py
@@ -850,7 +850,7 @@ def test_key_cert_generation(tmpdir):
'-noout',
], text = True)
assert 'Certificate' in out
- assert re.search('Issuer: CN\s?=\s?SecureBoot signing key on host', out)
+ assert re.search(r'Issuer: CN\s?=\s?SecureBoot signing key on host', out)
if __name__ == '__main__':
sys.exit(pytest.main(sys.argv))

View File

@ -0,0 +1,530 @@
From d0427b44ecb56cdbc40ca156af8e013b64cce74d Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Mon, 18 Mar 2024 13:01:40 +0100
Subject: [PATCH] coredump: generate stacktraces also for processes running in
containers w/o coredump forwarding
Note that entering container namespace has to be explicitly enabled by
setting SYSTEMD_COREDUMP_ALLOW_NAMESPACE_CHANGE environment variable.
RHEL-only
Resolves: RHEL-29430
---
src/analyze/analyze-inspect-elf.c | 2 +-
src/basic/socket-util.c | 52 +++++++++++
src/basic/socket-util.h | 15 ++++
src/coredump/coredump.c | 143 +++++++++++++++++++-----------
src/shared/elf-util.c | 46 +++++++++-
src/shared/elf-util.h | 4 +-
6 files changed, 202 insertions(+), 60 deletions(-)
diff --git a/src/analyze/analyze-inspect-elf.c b/src/analyze/analyze-inspect-elf.c
index 155c611c71..b8074100a5 100644
--- a/src/analyze/analyze-inspect-elf.c
+++ b/src/analyze/analyze-inspect-elf.c
@@ -30,7 +30,7 @@ static int analyze_elf(char **filenames, JsonFormatFlags json_flags) {
if (fd < 0)
return log_error_errno(fd, "Could not open \"%s\": %m", abspath);
- r = parse_elf_object(fd, abspath, /* fork_disable_dump= */false, NULL, &package_metadata);
+ r = parse_elf_object(fd, -EBADF, UID_NOBODY, GID_NOBODY, abspath, /* fork_disable_dump= */false, NULL, &package_metadata);
if (r < 0)
return log_error_errno(r, "Parsing \"%s\" as ELF object failed: %m", abspath);
diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c
index f39be19a59..1d86ca3f55 100644
--- a/src/basic/socket-util.c
+++ b/src/basic/socket-util.c
@@ -41,6 +41,11 @@
# define IDN_FLAGS 0
#endif
+/* From the kernel's include/net/scm.h */
+#ifndef SCM_MAX_FD
+# define SCM_MAX_FD 253
+#endif
+
static const char* const socket_address_type_table[] = {
[SOCK_STREAM] = "Stream",
[SOCK_DGRAM] = "Datagram",
@@ -951,6 +956,53 @@ int getpeergroups(int fd, gid_t **ret) {
return (int) n;
}
+ssize_t send_many_fds_iov_sa(
+ int transport_fd,
+ int *fds_array, size_t n_fds_array,
+ const struct iovec *iov, size_t iovlen,
+ const struct sockaddr *sa, socklen_t len,
+ int flags) {
+
+ _cleanup_free_ struct cmsghdr *cmsg = NULL;
+ struct msghdr mh = {
+ .msg_name = (struct sockaddr*) sa,
+ .msg_namelen = len,
+ .msg_iov = (struct iovec *)iov,
+ .msg_iovlen = iovlen,
+ };
+ ssize_t k;
+
+ assert(transport_fd >= 0);
+ assert(fds_array || n_fds_array == 0);
+
+ /* The kernel will reject sending more than SCM_MAX_FD FDs at once */
+ if (n_fds_array > SCM_MAX_FD)
+ return -E2BIG;
+
+ /* We need either an FD array or data to send. If there's nothing, return an error. */
+ if (n_fds_array == 0 && !iov)
+ return -EINVAL;
+
+ if (n_fds_array > 0) {
+ mh.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds_array);
+ mh.msg_control = cmsg = malloc(mh.msg_controllen);
+ if (!cmsg)
+ return -ENOMEM;
+
+ *cmsg = (struct cmsghdr) {
+ .cmsg_len = CMSG_LEN(sizeof(int) * n_fds_array),
+ .cmsg_level = SOL_SOCKET,
+ .cmsg_type = SCM_RIGHTS,
+ };
+ memcpy(CMSG_DATA(cmsg), fds_array, sizeof(int) * n_fds_array);
+ }
+ k = sendmsg(transport_fd, &mh, MSG_NOSIGNAL | flags);
+ if (k < 0)
+ return (ssize_t) -errno;
+
+ return k;
+}
+
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h
index 2e36e1a56b..61bf8ff32b 100644
--- a/src/basic/socket-util.h
+++ b/src/basic/socket-util.h
@@ -153,6 +153,13 @@ int getpeercred(int fd, struct ucred *ucred);
int getpeersec(int fd, char **ret);
int getpeergroups(int fd, gid_t **ret);
+ssize_t send_many_fds_iov_sa(
+ int transport_fd,
+ int *fds_array, size_t n_fds_array,
+ const struct iovec *iov, size_t iovlen,
+ const struct sockaddr *sa, socklen_t len,
+ int flags);
+
ssize_t send_one_fd_iov_sa(
int transport_fd,
int fd,
@@ -163,6 +170,14 @@ int send_one_fd_sa(int transport_fd,
int fd,
const struct sockaddr *sa, socklen_t len,
int flags);
+static inline int send_many_fds(
+ int transport_fd,
+ int *fds_array,
+ size_t n_fds_array,
+ int flags) {
+
+ return send_many_fds_iov_sa(transport_fd, fds_array, n_fds_array, NULL, 0, NULL, 0, flags);
+}
#define send_one_fd_iov(transport_fd, fd, iov, iovlen, flags) send_one_fd_iov_sa(transport_fd, fd, iov, iovlen, NULL, 0, flags)
#define send_one_fd(transport_fd, fd, flags) send_one_fd_iov_sa(transport_fd, fd, NULL, 0, NULL, 0, flags)
ssize_t receive_one_fd_iov(int transport_fd, struct iovec *iov, size_t iovlen, int flags, int *ret_fd);
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
index b9c5f3ad04..dca78fa72c 100644
--- a/src/coredump/coredump.c
+++ b/src/coredump/coredump.c
@@ -24,6 +24,7 @@
#include "coredump-vacuum.h"
#include "dirent-util.h"
#include "elf-util.h"
+#include "env-util.h"
#include "escape.h"
#include "fd-util.h"
#include "fileio.h"
@@ -36,6 +37,7 @@
#include "main-func.h"
#include "memory-util.h"
#include "mkdir-label.h"
+#include "namespace-util.h"
#include "parse-util.h"
#include "process-util.h"
#include "signal-util.h"
@@ -130,6 +132,8 @@ typedef struct Context {
const char *meta[_META_MAX];
size_t meta_size[_META_MAX];
pid_t pid;
+ uid_t uid;
+ gid_t gid;
bool is_pid1;
bool is_journald;
} Context;
@@ -866,36 +870,11 @@ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) {
return 1;
}
-static int change_uid_gid(const Context *context) {
- uid_t uid;
- gid_t gid;
- int r;
-
- r = parse_uid(context->meta[META_ARGV_UID], &uid);
- if (r < 0)
- return r;
-
- if (uid_is_system(uid)) {
- const char *user = "systemd-coredump";
-
- r = get_user_creds(&user, &uid, &gid, NULL, NULL, 0);
- if (r < 0) {
- log_warning_errno(r, "Cannot resolve %s user. Proceeding to dump core as root: %m", user);
- uid = gid = 0;
- }
- } else {
- r = parse_gid(context->meta[META_ARGV_GID], &gid);
- if (r < 0)
- return r;
- }
-
- return drop_privileges(uid, gid, 0);
-}
-
static int submit_coredump(
const Context *context,
struct iovec_wrapper *iovw,
- int input_fd) {
+ int input_fd,
+ int mntns_fd) {
_cleanup_(json_variant_unrefp) JsonVariant *json_metadata = NULL;
_cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
@@ -938,15 +917,6 @@ static int submit_coredump(
/* Vacuum again, but exclude the coredump we just created */
(void) coredump_vacuum(coredump_node_fd >= 0 ? coredump_node_fd : coredump_fd, arg_keep_free, arg_max_use);
- /* Now, let's drop privileges to become the user who owns the segfaulted process
- * and allocate the coredump memory under the user's uid. This also ensures that
- * the credentials journald will see are the ones of the coredumping user, thus
- * making sure the user gets access to the core dump. Let's also get rid of all
- * capabilities, if we run as root, we won't need them anymore. */
- r = change_uid_gid(context);
- if (r < 0)
- return log_error_errno(r, "Failed to drop privileges: %m");
-
/* Try to get a stack trace if we can */
if (coredump_size > arg_process_size_max)
log_debug("Not generating stack trace: core size %"PRIu64" is greater "
@@ -956,12 +926,23 @@ static int submit_coredump(
bool skip = startswith(context->meta[META_COMM], "systemd-coredum"); /* COMM is 16 bytes usually */
(void) parse_elf_object(coredump_fd,
+ mntns_fd,
+ context->uid,
+ context->gid,
context->meta[META_EXE],
/* fork_disable_dump= */ skip, /* avoid loops */
&stacktrace,
&json_metadata);
}
+ /* Now, let's drop privileges to become the user who owns the segfaulted process. This also ensures
+ * that the credentials journald will see are the ones of the coredumping user, thus making sure
+ * the user gets access to the core dump. Let's also get rid of all capabilities, if we run as root,
+ * we won't need them anymore. */
+ r = drop_privileges(context->uid, context->gid, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to drop privileges: %m");
+
log:
core_message = strjoina("Process ", context->meta[META_ARGV_PID],
" (", context->meta[META_COMM], ") of user ",
@@ -1094,6 +1075,15 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) {
if (r < 0)
return log_error_errno(r, "Failed to parse PID \"%s\": %m", context->meta[META_ARGV_PID]);
+ r = parse_uid(context->meta[META_ARGV_UID], &context->uid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse UID \"%s\": %m", context->meta[META_ARGV_UID]);
+
+ r = parse_gid(context->meta[META_ARGV_GID], &context->gid);
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse GID \"%s\": %m", context->meta[META_ARGV_GID]);
+
+
unit = context->meta[META_UNIT];
context->is_pid1 = streq(context->meta[META_ARGV_PID], "1") || streq_ptr(unit, SPECIAL_INIT_SCOPE);
context->is_journald = streq_ptr(unit, SPECIAL_JOURNALD_SERVICE);
@@ -1102,11 +1092,11 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) {
}
static int process_socket(int fd) {
- _cleanup_close_ int input_fd = -1;
+ _cleanup_close_ int input_fd = -EBADF, mntns_fd = -EBADF;
Context context = {};
struct iovec_wrapper iovw = {};
struct iovec iovec;
- int r;
+ int iterations = 0, r;
assert(fd >= 0);
@@ -1146,23 +1136,39 @@ static int process_socket(int fd) {
goto finish;
}
- /* The final zero-length datagram carries the file descriptor and tells us
+ /* The final zero-length datagram carries the file descriptors and tells us
* that we're done. */
if (n == 0) {
struct cmsghdr *found;
free(iovec.iov_base);
- found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
- if (!found) {
- cmsg_close_all(&mh);
- r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
- "Coredump file descriptor missing.");
- goto finish;
+ found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int) * 2));
+ if (found) {
+ int fds[2] = { -EBADF, -EBADF };
+
+ memcpy(fds, CMSG_DATA(found), sizeof(int) * 2);
+
+ assert(mntns_fd < 0);
+
+ /* Maybe we already got coredump FD in previous iteration? */
+ safe_close(input_fd);
+
+ input_fd = fds[0];
+ mntns_fd = fds[1];
+
+ /* We have all FDs we need let's take a shortcut here. */
+ break;
+ } else {
+ found = cmsg_find(&mh, SOL_SOCKET, SCM_RIGHTS, CMSG_LEN(sizeof(int)));
+ if (found)
+ input_fd = *CMSG_DATA(found);
}
- assert(input_fd < 0);
- input_fd = *(int*) CMSG_DATA(found);
+ /* This is the first message that carries file descriptors, maybe there will be one more that actually contains array of descriptors. */
+ if (iterations++ == 0)
+ continue;
+
break;
} else
cmsg_close_all(&mh);
@@ -1177,7 +1183,11 @@ static int process_socket(int fd) {
}
/* Make sure we got all data we really need */
- assert(input_fd >= 0);
+ if (input_fd < 0) {
+ r = log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
+ "Coredump file descriptor missing.");
+ goto finish;
+ }
r = save_context(&context, &iovw);
if (r < 0)
@@ -1192,15 +1202,15 @@ static int process_socket(int fd) {
goto finish;
}
- r = submit_coredump(&context, &iovw, input_fd);
+ r = submit_coredump(&context, &iovw, input_fd, mntns_fd);
finish:
iovw_free_contents(&iovw, true);
return r;
}
-static int send_iovec(const struct iovec_wrapper *iovw, int input_fd) {
- _cleanup_close_ int fd = -1;
+static int send_iovec(const struct iovec_wrapper *iovw, int input_fd, int mntns_fd) {
+ _cleanup_close_ int fd = -EBADF;
int r;
assert(iovw);
@@ -1256,6 +1266,12 @@ static int send_iovec(const struct iovec_wrapper *iovw, int input_fd) {
if (r < 0)
return log_error_errno(r, "Failed to send coredump fd: %m");
+ if (mntns_fd >= 0) {
+ r = send_many_fds(fd, (int[]) { input_fd, mntns_fd }, 2, 0);
+ if (r < 0)
+ return log_error_errno(r, "Failed to send coredump fds: %m");
+ }
+
return 0;
}
@@ -1428,7 +1444,7 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) {
static int process_kernel(int argc, char* argv[]) {
Context context = {};
struct iovec_wrapper *iovw;
- int r;
+ int r, mntns_fd = -EBADF;
/* When we're invoked by the kernel, stdout/stderr are closed which is dangerous because the fds
* could get reallocated. To avoid hard to debug issues, let's instead bind stdout/stderr to
@@ -1462,6 +1478,25 @@ static int process_kernel(int argc, char* argv[]) {
log_open();
}
+ r = in_same_namespace(getpid_cached(), context.pid, NAMESPACE_PID);
+ if (r < 0)
+ log_debug_errno(r, "Failed to check pidns of crashing process, ignoring: %m");
+
+ if (r == 0 && getenv_bool("SYSTEMD_COREDUMP_ALLOW_NAMESPACE_CHANGE") > 0) {
+ r = namespace_open(context.pid, NULL, &mntns_fd, NULL, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open mntns of crashing process: %m");
+ } else {
+ /* Crashing process is not running in the container or changing namespace is disabled, but we
+ still need to send mount namespace fd along side coredump fd so let's just open our own
+ mount namespace. Entering it will be NOP but that is OK. */
+ r = namespace_open(getpid_cached(), NULL, &mntns_fd, NULL, NULL, NULL);
+ if (r < 0)
+ return log_error_errno(r, "Failed to open our mntns: %m");
+ }
+
+ assert(mntns_fd >= 0 && fd_is_ns(mntns_fd, CLONE_NEWNS) > 0);
+
/* If this is PID 1 disable coredump collection, we'll unlikely be able to process
* it later on.
*
@@ -1474,9 +1509,9 @@ static int process_kernel(int argc, char* argv[]) {
}
if (context.is_journald || context.is_pid1)
- r = submit_coredump(&context, iovw, STDIN_FILENO);
+ r = submit_coredump(&context, iovw, STDIN_FILENO, mntns_fd);
else
- r = send_iovec(iovw, STDIN_FILENO);
+ r = send_iovec(iovw, STDIN_FILENO, mntns_fd);
finish:
iovw = iovw_free_free(iovw);
diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c
index bde5013b92..6a2969732d 100644
--- a/src/shared/elf-util.c
+++ b/src/shared/elf-util.c
@@ -12,6 +12,7 @@
#include <unistd.h>
#include "alloc-util.h"
+#include "capability-util.h"
#include "dlfcn-util.h"
#include "elf-util.h"
#include "errno-util.h"
@@ -21,9 +22,12 @@
#include "hexdecoct.h"
#include "io-util.h"
#include "macro.h"
+#include "namespace-util.h"
#include "process-util.h"
#include "rlimit-util.h"
#include "string-util.h"
+#include "uid-alloc-range.h"
+#include "user-util.h"
#include "util.h"
#define FRAMES_MAX 64
@@ -752,11 +756,27 @@ static int parse_elf(int fd, const char *executable, char **ret, JsonVariant **r
return 0;
}
-int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata) {
+static int core_change_uid_gid(uid_t uid, gid_t gid) {
+ uid_t u = uid;
+ gid_t g = gid;
+ int r;
+
+ if (uid_is_system(u)) {
+ const char *user = "systemd-coredump";
+
+ r = get_user_creds(&user, &u, &g, NULL, NULL, 0);
+ if (r < 0)
+ log_warning_errno(r, "Cannot resolve %s user, ignoring: %m", user);
+ }
+
+ return drop_privileges(u, g, 0);
+}
+
+int parse_elf_object(int fd, int mntns_fd, uid_t uid, gid_t gid, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata) {
_cleanup_close_pair_ int error_pipe[2] = { -1, -1 }, return_pipe[2] = { -1, -1 }, json_pipe[2] = { -1, -1 };
_cleanup_(json_variant_unrefp) JsonVariant *package_metadata = NULL;
_cleanup_free_ char *buf = NULL;
- int r;
+ int flags, r;
assert(fd >= 0);
@@ -784,6 +804,10 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha
return r;
}
+ flags = FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS|FORK_WAIT|FORK_REOPEN_LOG;
+ if (mntns_fd >= 0)
+ flags &= ~(FORK_CLOSE_ALL_FDS|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS);
+
/* Parsing possibly malformed data is crash-happy, so fork. In case we crash,
* the core file will not be lost, and the messages will still be attached to
* the journal. Reading the elf object might be slow, but it still has an upper
@@ -793,7 +817,7 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha
r = safe_fork_full("(sd-parse-elf)",
(int[]){ fd, error_pipe[1], return_pipe[1], json_pipe[1] },
4,
- FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_NEW_MOUNTNS|FORK_MOUNTNS_SLAVE|FORK_NEW_USERNS|FORK_WAIT|FORK_REOPEN_LOG,
+ flags,
NULL);
if (r < 0) {
if (r == -EPROTO) { /* We should have the errno from the child, but don't clobber original error */
@@ -811,6 +835,22 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha
return r;
}
if (r == 0) {
+ if (mntns_fd >= 0) {
+ r = namespace_enter(/* pidns_fd = */ -EBADF,
+ mntns_fd,
+ /* netns_fd = */ -EBADF,
+ /* userns_fd = */ -EBADF,
+ /* root_fd = */ -EBADF);
+ if (r < 0)
+ log_notice_errno(r, "Failed to enter mount namespace of crashing process, ignoring: %m");
+ }
+
+ if (uid != UID_NOBODY && gid != GID_NOBODY) {
+ r = core_change_uid_gid(uid, gid);
+ if (r < 0)
+ log_notice_errno(r, "Failed to drop privileges, ignoring: %m");
+ }
+
/* We want to avoid loops, given this can be called from systemd-coredump */
if (fork_disable_dump) {
r = RET_NERRNO(prctl(PR_SET_DUMPABLE, 0));
diff --git a/src/shared/elf-util.h b/src/shared/elf-util.h
index b28e64cea6..350464941f 100644
--- a/src/shared/elf-util.h
+++ b/src/shared/elf-util.h
@@ -10,9 +10,9 @@ int dlopen_elf(void);
/* Parse an ELF object in a forked process, so that errors while iterating over
* untrusted and potentially malicious data do not propagate to the main caller's process.
* If fork_disable_dump, the child process will not dump core if it crashes. */
-int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata);
+int parse_elf_object(int fd, int mntns_fd, uid_t uid, gid_t gid, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata);
#else
-static inline int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata) {
+static inline int parse_elf_object(int fd, int mntns_fd, uid_t uid, gid_t gid, const char *executable, bool fork_disable_dump, char **ret, JsonVariant **ret_package_metadata) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "elfutils disabled, parsing ELF objects not supported");
}
#endif

View File

@ -0,0 +1,194 @@
From 6307dbb9cb25fd5a2131c043b89a52f032817178 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Tue, 21 Mar 2023 23:19:41 +0100
Subject: [PATCH] test: add a couple of tests for systemd-coredump
(cherry picked from commit aadbd81f7ffbc313d0541c15455211dddeedbfde)
Related: RHEL-29430
---
test/units/testsuite-74.coredump.sh | 175 ++++++++++++++++++++++++++++
1 file changed, 175 insertions(+)
create mode 100755 test/units/testsuite-74.coredump.sh
diff --git a/test/units/testsuite-74.coredump.sh b/test/units/testsuite-74.coredump.sh
new file mode 100755
index 0000000000..6a299ecbfb
--- /dev/null
+++ b/test/units/testsuite-74.coredump.sh
@@ -0,0 +1,175 @@
+#!/usr/bin/env bash
+# SPDX-License-Identifier: LGPL-2.1-or-later
+set -eux
+set -o pipefail
+
+# Make sure the binary name fits into 15 characters
+CORE_TEST_BIN="/tmp/test-dump"
+CORE_TEST_UNPRIV_BIN="/tmp/test-usr-dump"
+MAKE_DUMP_SCRIPT="/tmp/make-dump"
+# Unset $PAGER so we don't have to use --no-pager everywhere
+export PAGER=
+
+at_exit() {
+ rm -fv -- "$CORE_TEST_BIN" "$CORE_TEST_UNPRIV_BIN" "$MAKE_DUMP_SCRIPT"
+}
+
+trap at_exit EXIT
+
+if systemd-detect-virt -cq; then
+ echo "Running in a container, skipping the systemd-coredump test..."
+ exit 0
+fi
+
+# Check that we're the ones to receive coredumps
+sysctl kernel.core_pattern | grep systemd-coredump
+
+# Prepare "fake" binaries for coredumps, so we can properly exercise
+# the matching stuff too
+cp -vf /bin/sleep "${CORE_TEST_BIN:?}"
+cp -vf /bin/sleep "${CORE_TEST_UNPRIV_BIN:?}"
+# Simple script that spawns given "fake" binary and then kills it with
+# given signal
+cat >"${MAKE_DUMP_SCRIPT:?}" <<\EOF
+#!/bin/bash -ex
+
+bin="${1:?}"
+sig="${2:?}"
+
+ulimit -c unlimited
+"$bin" infinity &
+pid=$!
+sleep 1
+kill -s "$sig" "$pid"
+# This should always fail
+! wait "$pid"
+EOF
+chmod +x "$MAKE_DUMP_SCRIPT"
+
+# Privileged stuff
+[[ "$(id -u)" -eq 0 ]]
+# Trigger a couple of coredumps
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
+# In the tests we store the coredumps in journals, so let's generate a couple
+# with Storage=external as well
+mkdir -p /run/systemd/coredump.conf.d/
+printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGTRAP"
+"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
+rm -fv /run/systemd/coredump.conf.d/99-external.conf
+# Wait a bit for the coredumps to get processed
+timeout 30 bash -c "while [[ $(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
+
+coredumpctl
+SYSTEMD_LOG_LEVEL=debug coredumpctl
+coredumpctl --help
+coredumpctl --version
+coredumpctl --no-pager --no-legend
+coredumpctl --all
+coredumpctl -1
+coredumpctl -n 1
+coredumpctl --reverse
+coredumpctl -F COREDUMP_EXE
+coredumpctl --json=short | jq
+coredumpctl --json=pretty | jq
+coredumpctl --json=off
+coredumpctl --root=/
+coredumpctl --directory=/var/log/journal
+coredumpctl --file="/var/log/journal/$(</etc/machine-id)/system.journal"
+coredumpctl --since=@0
+coredumpctl --since=yesterday --until=tomorrow
+# We should have a couple of externally stored coredumps
+coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
+grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
+rm -f /tmp/coredumpctl.out
+
+coredumpctl info
+coredumpctl info "$CORE_TEST_BIN"
+coredumpctl info /foo /bar/ /baz "$CORE_TEST_BIN"
+coredumpctl info "${CORE_TEST_BIN##*/}"
+coredumpctl info foo bar baz "${CORE_TEST_BIN##*/}"
+coredumpctl info COREDUMP_EXE="$CORE_TEST_BIN"
+coredumpctl info COREDUMP_EXE=aaaaa COREDUMP_EXE= COREDUMP_EXE="$CORE_TEST_BIN"
+
+coredumpctl debug --debugger=/bin/true "$CORE_TEST_BIN"
+SYSTEMD_DEBUGGER=/bin/true coredumpctl debug "$CORE_TEST_BIN"
+coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_BIN##*/}"
+
+coredumpctl dump "$CORE_TEST_BIN" >/tmp/core.redirected
+test -s /tmp/core.redirected
+coredumpctl dump -o /tmp/core.output "${CORE_TEST_BIN##*/}"
+test -s /tmp/core.output
+rm -f /tmp/core.{output,redirected}
+
+# Unprivileged stuff
+# Related issue: https://github.com/systemd/systemd/issues/26912
+UNPRIV_CMD=(systemd-run --user --wait --pipe -M "testuser@.host" --)
+# Trigger a couple of coredumps as an unprivileged user
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
+# In the tests we store the coredumps in journals, so let's generate a couple
+# with Storage=external as well
+mkdir -p /run/systemd/coredump.conf.d/
+printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.conf
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGTRAP"
+"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
+rm -fv /run/systemd/coredump.conf.d/99-external.conf
+# Wait a bit for the coredumps to get processed
+timeout 30 bash -c "while [[ $(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
+
+# root should see coredumps from both binaries
+coredumpctl info "$CORE_TEST_UNPRIV_BIN"
+coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
+# The test user should see only their own coredumps
+"${UNPRIV_CMD[@]}" coredumpctl
+"${UNPRIV_CMD[@]}" coredumpctl info "$CORE_TEST_UNPRIV_BIN"
+"${UNPRIV_CMD[@]}" coredumpctl info "${CORE_TEST_UNPRIV_BIN##*/}"
+(! "${UNPRIV_CMD[@]}" coredumpctl info --all "$CORE_TEST_BIN")
+(! "${UNPRIV_CMD[@]}" coredumpctl info --all "${CORE_TEST_BIN##*/}")
+# We should have a couple of externally stored coredumps
+"${UNPRIV_CMD[@]}" coredumpctl --field=COREDUMP_FILENAME | tee /tmp/coredumpctl.out
+grep "/var/lib/systemd/coredump/core" /tmp/coredumpctl.out
+rm -f /tmp/coredumpctl.out
+
+"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true "$CORE_TEST_UNPRIV_BIN"
+"${UNPRIV_CMD[@]}" coredumpctl debug --debugger=/bin/true --debugger-arguments="-this --does --not 'do anything' -a -t --all" "${CORE_TEST_UNPRIV_BIN##*/}"
+
+"${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_UNPRIV_BIN" >/tmp/core.redirected
+test -s /tmp/core.redirected
+"${UNPRIV_CMD[@]}" coredumpctl dump -o /tmp/core.output "${CORE_TEST_UNPRIV_BIN##*/}"
+test -s /tmp/core.output
+rm -f /tmp/core.{output,redirected}
+(! "${UNPRIV_CMD[@]}" coredumpctl dump "$CORE_TEST_BIN" >/dev/null)
+
+# --backtrace mode
+# Pass one of the existing journal coredump records to systemd-coredump and
+# use our PID as the source to make matching the coredump later easier
+# systemd-coredump args: PID UID GID SIGNUM TIMESTAMP CORE_SOFT_RLIMIT HOSTNAME
+journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" |
+ /usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509994 12345 mymachine
+# Wait a bit for the coredump to get processed
+timeout 30 bash -c "while [[ $(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done"
+coredumpctl info "$$"
+coredumpctl info COREDUMP_HOSTNAME="mymachine"
+
+
+(! coredumpctl --hello-world)
+(! coredumpctl -n 0)
+(! coredumpctl -n -1)
+(! coredumpctl --file=/dev/null)
+(! coredumpctl --since=0)
+(! coredumpctl --until='')
+(! coredumpctl --since=today --until=yesterday)
+(! coredumpctl --directory=/ --root=/)
+(! coredumpctl --json=foo)
+(! coredumpctl -F foo -F bar)
+(! coredumpctl list 0)
+(! coredumpctl list -- -1)
+(! coredumpctl list '')
+(! coredumpctl info /../.~=)
+(! coredumpctl info '')
+(! coredumpctl dump --output=/dev/full "$CORE_TEST_BIN")
+(! coredumpctl dump --output=/dev/null --output=/dev/null "$CORE_TEST_BIN")
+(! coredumpctl debug --debugger=/bin/false)
+(! coredumpctl debug --debugger=/bin/true --debugger-arguments='"')

View File

@ -0,0 +1,54 @@
From 687c8da38e766fe35a2710b0539576710c345f34 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Sat, 25 Mar 2023 12:02:15 +0100
Subject: [PATCH] test: don't expand the subshell expression prematurely
We need to expand the subshell expression during the `bash -c`
invocation, not before, to take the desired effect, as now it expands to:
timeout 30 bash -c 'while [[ 0 -eq 0 ]]; do sleep 1; done'
instead of the expected:
timeout 30 bash -c 'while [[ $(coredumpctl list -q --no-legend 770 | wc -l) -eq 0 ]]; do sleep 1; done'
Follow-up to aadbd81f7f.
(cherry picked from commit 0b189ac84c432a085a1f10139260cec6b5032523)
Related: RHEL-29430
---
test/units/testsuite-74.coredump.sh | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/test/units/testsuite-74.coredump.sh b/test/units/testsuite-74.coredump.sh
index 6a299ecbfb..3910abe0ec 100755
--- a/test/units/testsuite-74.coredump.sh
+++ b/test/units/testsuite-74.coredump.sh
@@ -59,7 +59,7 @@ printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.
"$MAKE_DUMP_SCRIPT" "$CORE_TEST_BIN" "SIGABRT"
rm -fv /run/systemd/coredump.conf.d/99-external.conf
# Wait a bit for the coredumps to get processed
-timeout 30 bash -c "while [[ $(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
+timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
coredumpctl
SYSTEMD_LOG_LEVEL=debug coredumpctl
@@ -116,7 +116,7 @@ printf '[Coredump]\nStorage=external' >/run/systemd/coredump.conf.d/99-external.
"${UNPRIV_CMD[@]}" "$MAKE_DUMP_SCRIPT" "$CORE_TEST_UNPRIV_BIN" "SIGABRT"
rm -fv /run/systemd/coredump.conf.d/99-external.conf
# Wait a bit for the coredumps to get processed
-timeout 30 bash -c "while [[ $(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
+timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_UNPRIV_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
# root should see coredumps from both binaries
coredumpctl info "$CORE_TEST_UNPRIV_BIN"
@@ -149,7 +149,7 @@ rm -f /tmp/core.{output,redirected}
journalctl -b -n 1 --output=export --output-fields=MESSAGE,COREDUMP COREDUMP_EXE="/usr/bin/test-dump" |
/usr/lib/systemd/systemd-coredump --backtrace $$ 0 0 6 1679509994 12345 mymachine
# Wait a bit for the coredump to get processed
-timeout 30 bash -c "while [[ $(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done"
+timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -eq 0 ]]; do sleep 1; done"
coredumpctl info "$$"
coredumpctl info COREDUMP_HOSTNAME="mymachine"

View File

@ -0,0 +1,63 @@
From f46d65bba43c519d8d2ed8fab86ea765166c0e72 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Wed, 26 Apr 2023 14:18:04 +0100
Subject: [PATCH] coredump filter: fix stack overflow with =all
We translate 'all' to UNIT64_MAX, which has a lot more 'f's. Use the
helper macro, since a decimal uint64_t will always be >> than a hex
representation.
root@image:~# systemd-run -t --property CoredumpFilter=all ls /tmp
Running as unit: run-u13.service
Press ^] three times within 1s to disconnect TTY.
*** stack smashing detected ***: terminated
[137256.320511] systemd[1]: run-u13.service: Main process exited, code=dumped, status=6/ABRT
[137256.320850] systemd[1]: run-u13.service: Failed with result 'core-dump'.
(cherry picked from commit 37232d55a7bcace37280e28b207c85f5ca9b3f6b)
Related: RHEL-29430
---
src/basic/macro.h | 4 ++++
src/shared/coredump-util.c | 5 +++--
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/basic/macro.h b/src/basic/macro.h
index 2d378454a2..6893a1ff32 100644
--- a/src/basic/macro.h
+++ b/src/basic/macro.h
@@ -268,6 +268,10 @@ static inline int __coverity_check_and_return__(int condition) {
#define sizeof_field(struct_type, member) sizeof(((struct_type *) 0)->member)
+/* Maximum buffer size needed for formatting an unsigned integer type as hex, including space for '0x'
+ * prefix and trailing NUL suffix. */
+#define HEXADECIMAL_STR_MAX(type) (2 + sizeof(type) * 2 + 1)
+
/* Returns the number of chars needed to format variables of the specified type as a decimal string. Adds in
* extra space for a negative '-' prefix for signed types. Includes space for the trailing NUL. */
#define DECIMAL_STR_MAX(type) \
diff --git a/src/shared/coredump-util.c b/src/shared/coredump-util.c
index a0b648bf79..aaf3e16eff 100644
--- a/src/shared/coredump-util.c
+++ b/src/shared/coredump-util.c
@@ -3,6 +3,7 @@
#include "coredump-util.h"
#include "extract-word.h"
#include "fileio.h"
+#include "stdio-util.h"
#include "string-table.h"
static const char *const coredump_filter_table[_COREDUMP_FILTER_MAX] = {
@@ -65,9 +66,9 @@ int coredump_filter_mask_from_string(const char *s, uint64_t *ret) {
}
int set_coredump_filter(uint64_t value) {
- char t[STRLEN("0xFFFFFFFF")];
+ char t[HEXADECIMAL_STR_MAX(uint64_t)];
- sprintf(t, "0x%"PRIx64, value);
+ xsprintf(t, "0x%"PRIx64, value);
return write_string_file("/proc/self/coredump_filter", t,
WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER);

View File

@ -0,0 +1,59 @@
From 8db17468f2c0dd07d5e9618e3b49fddda26724c6 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Wed, 26 Apr 2023 14:19:33 +0100
Subject: [PATCH] coredump filter: add mask for 'all' using UINT32_MAX, not
UINT64_MAX
The kernel returns ERANGE when UINT64_MAX is passed. Create a mask
and use UINT32_max, which is accepted, so that future bits will also
be set.
(cherry picked from commit 7f3bb8f20dcccaceea8b1ee05f0560b81162037b)
Related: RHEL-29430
---
src/shared/coredump-util.c | 2 +-
src/shared/coredump-util.h | 3 +++
src/test/test-coredump-util.c | 2 ++
3 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/shared/coredump-util.c b/src/shared/coredump-util.c
index aaf3e16eff..7a44816834 100644
--- a/src/shared/coredump-util.c
+++ b/src/shared/coredump-util.c
@@ -43,7 +43,7 @@ int coredump_filter_mask_from_string(const char *s, uint64_t *ret) {
}
if (streq(n, "all")) {
- m = UINT64_MAX;
+ m = COREDUMP_FILTER_MASK_ALL;
continue;
}
diff --git a/src/shared/coredump-util.h b/src/shared/coredump-util.h
index 09e7ed443f..f4d4098136 100644
--- a/src/shared/coredump-util.h
+++ b/src/shared/coredump-util.h
@@ -22,6 +22,9 @@ typedef enum CoredumpFilter {
1u << COREDUMP_FILTER_ELF_HEADERS | \
1u << COREDUMP_FILTER_PRIVATE_HUGE)
+/* The kernel doesn't like UINT64_MAX and returns ERANGE, use UINT32_MAX to support future new flags */
+#define COREDUMP_FILTER_MASK_ALL UINT32_MAX
+
const char* coredump_filter_to_string(CoredumpFilter i) _const_;
CoredumpFilter coredump_filter_from_string(const char *s) _pure_;
int coredump_filter_mask_from_string(const char *s, uint64_t *ret);
diff --git a/src/test/test-coredump-util.c b/src/test/test-coredump-util.c
index 40b68df9f4..87dc371a88 100644
--- a/src/test/test-coredump-util.c
+++ b/src/test/test-coredump-util.c
@@ -23,6 +23,8 @@ TEST(coredump_filter_mask_from_string) {
uint64_t f;
assert_se(coredump_filter_mask_from_string("default", &f) == 0);
assert_se(f == COREDUMP_FILTER_MASK_DEFAULT);
+ assert_se(coredump_filter_mask_from_string("all", &f) == 0);
+ assert_se(f == COREDUMP_FILTER_MASK_ALL);
assert_se(coredump_filter_mask_from_string(" default\tdefault\tdefault ", &f) == 0);
assert_se(f == COREDUMP_FILTER_MASK_DEFAULT);

View File

@ -0,0 +1,26 @@
From 27538bb6224cdcd2ee04284b496449e0d2755e7b Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Wed, 26 Apr 2023 14:32:04 +0100
Subject: [PATCH] test: add coverage for CoredumpFilter=all
(cherry picked from commit cf636aa59eb8c848ed04d5b08aac0acf3f6683d9)
Related: RHEL-29430
---
test/units/testsuite-74.coredump.sh | 3 +++
1 file changed, 3 insertions(+)
diff --git a/test/units/testsuite-74.coredump.sh b/test/units/testsuite-74.coredump.sh
index 3910abe0ec..0e5d050f45 100755
--- a/test/units/testsuite-74.coredump.sh
+++ b/test/units/testsuite-74.coredump.sh
@@ -153,6 +153,9 @@ timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $$ | wc -l) -eq
coredumpctl info "$$"
coredumpctl info COREDUMP_HOSTNAME="mymachine"
+# This used to cause a stack overflow
+systemd-run -t --property CoredumpFilter=all ls /tmp
+systemd-run -t --property CoredumpFilter=default ls /tmp
(! coredumpctl --hello-world)
(! coredumpctl -n 0)

View File

@ -0,0 +1,52 @@
From cada47a50ea898f092c430508656ed717ab78dba Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Wed, 24 May 2023 10:31:41 +0900
Subject: [PATCH] test: rotate journal before storing coredumps
Hopefully fixes the failure like
https://jenkins-systemd.apps.ocp.cloud.ci.centos.org/job/upstream-vagrant-archlinux-sanitizers/2558/
---
[ 66.708894] testsuite-74.sh[728]: + coredumpctl --json=off
[ 66.709344] testsuite-74.sh[826]: TIME PID UID GID SIG COREFILE EXE SIZE
[ 66.709773] testsuite-74.sh[826]: Tue 2023-05-23 22:10:17 UTC 739 0 0 SIGTRAP journal /tmp/test-dump -
[ 66.711134] testsuite-74.sh[826]: Tue 2023-05-23 22:10:18 UTC 747 0 0 SIGABRT journal /tmp/test-dump -
[ 66.711789] testsuite-74.sh[826]: Tue 2023-05-23 22:10:19 UTC 763 0 0 SIGTRAP present /tmp/test-dump 53.5K
[ 66.712460] testsuite-74.sh[826]: Tue 2023-05-23 22:10:20 UTC 776 0 0 SIGABRT present /tmp/test-dump 53.3K
[ 66.713505] testsuite-74.sh[728]: + coredumpctl --root=/
[ 66.714144] testsuite-74.sh[828]: TIME PID UID GID SIG COREFILE EXE SIZE
[ 66.714535] testsuite-74.sh[828]: Tue 2023-05-23 22:10:17 UTC 739 0 0 SIGTRAP journal /tmp/test-dump -
[ 66.715208] testsuite-74.sh[828]: Tue 2023-05-23 22:10:18 UTC 747 0 0 SIGABRT journal /tmp/test-dump -
[ 66.715907] testsuite-74.sh[828]: Tue 2023-05-23 22:10:19 UTC 763 0 0 SIGTRAP present /tmp/test-dump 53.5K
[ 66.716565] testsuite-74.sh[828]: Tue 2023-05-23 22:10:20 UTC 776 0 0 SIGABRT present /tmp/test-dump 53.3K
[ 66.717494] testsuite-74.sh[728]: + coredumpctl --directory=/var/log/journal
[ 66.718188] testsuite-74.sh[830]: TIME PID UID GID SIG COREFILE EXE SIZE
[ 66.882072] testsuite-74.sh[830]: Tue 2023-05-23 22:10:17 UTC 739 0 0 SIGTRAP journal /tmp/test-dump -
[ 66.882642] testsuite-74.sh[830]: Tue 2023-05-23 22:10:18 UTC 747 0 0 SIGABRT journal /tmp/test-dump -
[ 66.883450] testsuite-74.sh[830]: Tue 2023-05-23 22:10:19 UTC 763 0 0 SIGTRAP present /tmp/test-dump 53.5K
[ 66.883944] testsuite-74.sh[830]: Tue 2023-05-23 22:10:20 UTC 776 0 0 SIGABRT present /tmp/test-dump 53.3K
[ 66.885448] testsuite-74.sh[728]: + coredumpctl --file=/var/log/journal/2e1ed84be19a4e22adfc99ad849be1f6/system.journal
[ 66.885989] testsuite-74.sh[728]: + at_exit
[ 66.894162] coredumpctl[833]: No coredumps found.
---
(cherry picked from commit 5c4e96c28c4a2193ba0dd459ea3366614f9b262f)
Related: RHEL-29430
---
test/units/testsuite-74.coredump.sh | 3 +++
1 file changed, 3 insertions(+)
diff --git a/test/units/testsuite-74.coredump.sh b/test/units/testsuite-74.coredump.sh
index 0e5d050f45..d5039b70f4 100755
--- a/test/units/testsuite-74.coredump.sh
+++ b/test/units/testsuite-74.coredump.sh
@@ -21,6 +21,9 @@ if systemd-detect-virt -cq; then
exit 0
fi
+# To make all coredump entries stored in system.journal.
+journalctl --rotate
+
# Check that we're the ones to receive coredumps
sysctl kernel.core_pattern | grep systemd-coredump

View File

@ -0,0 +1,77 @@
From 803900bb830163c13539a70ea56d82d27b06f52b Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Fri, 2 Jun 2023 13:24:32 +0200
Subject: [PATCH] test: sync with the fake binary before killing it
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
On faster machines we might be too fast and kill the fake binary during
fork() which then makes kernel report a "wrong" binary in the coredump,
e.g.:
[ 31.408078] testsuite-74.sh[548]: + /tmp/make-dump /tmp/test-dump SIGTRAP
[ 31.409720] testsuite-74.sh[560]: + bin=/tmp/test-dump
[ 31.409720] testsuite-74.sh[560]: + sig=SIGTRAP
[ 31.409720] testsuite-74.sh[560]: + ulimit -c unlimited
[ 31.409720] testsuite-74.sh[560]: + pid=561
[ 31.409720] testsuite-74.sh[560]: + sleep 1
[ 31.409720] testsuite-74.sh[560]: + kill -s SIGTRAP 561
[ 31.409720] testsuite-74.sh[560]: + wait 561
[ 31.491757] systemd[1]: Created slice system-systemd\x2dcoredump.slice.
[ 31.524488] systemd[1]: Started systemd-coredump@0-563-0.service.
[ 31.616372] systemd-coredump[564]: [🡕] Process 561 (make-dump) of user 0 dumped core.
Stack trace of thread 561:
#0 0x00007ff86bb49af7 _Fork (libc.so.6 + 0xd4af7)
#1 0x00007ff86bb4965f __libc_fork (libc.so.6 + 0xd465f)
#2 0x000055e88011b0ad make_child (bash + 0x550ad)
#3 0x000055e8800fd05f n/a (bash + 0x3705f)
#4 0x000055e880100116 execute_command_internal (bash + 0x3a116)
#5 0x000055e8801011f2 execute_command_internal (bash + 0x3b1f2)
#6 0x000055e8801025b6 execute_command (bash + 0x3c5b6)
#7 0x000055e8800f134b reader_loop (bash + 0x2b34b)
#8 0x000055e8800e757d main (bash + 0x2157d)
#9 0x00007ff86ba98850 n/a (libc.so.6 + 0x23850)
#10 0x00007ff86ba9890a __libc_start_main (libc.so.6 + 0x2390a)
#11 0x000055e8800e83b5 _start (bash + 0x223b5)
ELF object binary architecture: AMD x86-64
[ 31.666617] testsuite-74.sh[560]: /tmp/make-dump: line 12: 561 Trace/breakpoint trap (core dumped) "$bin" infinity
...
$ coredumpctl list --file system.journal
TIME PID UID GID SIG COREFILE EXE SIZE
Fri 2023-06-02 10:42:10 CEST 561 0 0 SIGTRAP journal /usr/bin/bash -
Fri 2023-06-02 10:42:11 CEST 570 0 0 SIGABRT journal /tmp/test-dump -
Fri 2023-06-02 10:42:12 CEST 582 0 0 SIGTRAP missing /tmp/test-dump -
Fri 2023-06-02 10:42:13 CEST 593 0 0 SIGABRT missing /tmp/test-dump -
(cherry picked from commit 1326d2dd059132760b40acb7a715ecc9ff08bd35)
Related: RHEL-29430
---
test/units/testsuite-74.coredump.sh | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/test/units/testsuite-74.coredump.sh b/test/units/testsuite-74.coredump.sh
index d5039b70f4..d30fd73717 100755
--- a/test/units/testsuite-74.coredump.sh
+++ b/test/units/testsuite-74.coredump.sh
@@ -42,7 +42,17 @@ sig="${2:?}"
ulimit -c unlimited
"$bin" infinity &
pid=$!
-sleep 1
+# Sync with the "fake" binary, so we kill it once it's fully forked off,
+# otherwise we might kill it during fork and kernel would then report
+# "wrong" binary name (i.e. $MAKE_DUMP_SCRIPT instead of $CORE_TEST_BIN).
+# In this case, wait until the "fake" binary (sleep in this case) enters
+# the "interruptible sleep" state, at which point it should be ready
+# to be sacrificed.
+for _ in {0..9}; do
+ read -ra self_stat <"/proc/$pid/stat"
+ [[ "${self_stat[2]}" == S ]] && break
+ sleep .5
+done
kill -s "$sig" "$pid"
# This should always fail
! wait "$pid"

View File

@ -0,0 +1,112 @@
From 623e09d910fffd6824d77203b8d9b016c0dd6208 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Thu, 25 Apr 2024 19:50:10 +0200
Subject: [PATCH] test: check coredump handling in containers & namespaces
This is partially based on upstream's 097e28736a, which tests coredump
forwarding (that we don't have in RHEL 9). It also provides basic
coverage for RHEL-29430 (generating stack traces for processes in
containers without coredump fowarding).
rhel-only
Related: RHEL-29430
---
test/test-functions | 2 +-
test/units/testsuite-74.coredump.sh | 64 ++++++++++++++++++++++++++++-
2 files changed, 64 insertions(+), 2 deletions(-)
diff --git a/test/test-functions b/test/test-functions
index 1608644cbb..947f8589c5 100644
--- a/test/test-functions
+++ b/test/test-functions
@@ -2619,7 +2619,7 @@ inst_binary() {
# ls, stat - pulls in nss_systemd with certain options (like ls -l) when
# nsswitch.conf uses [SUCCESS=merge] (like on Arch Linux)
# tar - called by machinectl in TEST-25
- if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$bin" =~ /(chown|getent|login|ls|stat|su|tar|useradd|userdel)$ ]]; then
+ if get_bool "$IS_BUILT_WITH_ASAN" && [[ "$bin" =~ /(chown|getent|login|id|ls|stat|su|tar|useradd|userdel)$ ]]; then
wrap_binary=1
fi
diff --git a/test/units/testsuite-74.coredump.sh b/test/units/testsuite-74.coredump.sh
index d30fd73717..1093cad8a9 100755
--- a/test/units/testsuite-74.coredump.sh
+++ b/test/units/testsuite-74.coredump.sh
@@ -74,6 +74,68 @@ rm -fv /run/systemd/coredump.conf.d/99-external.conf
# Wait a bit for the coredumps to get processed
timeout 30 bash -c "while [[ \$(coredumpctl list -q --no-legend $CORE_TEST_BIN | wc -l) -lt 4 ]]; do sleep 1; done"
+# RHEL9: following part is taken out of 097e28736aed9280dfac0f8e8096deca71bac813 but slightly tweaked, since
+# in RHEL9 we don't have the support for coredump forwarding
+CONTAINER="testsuite-74-container"
+TESTUSER_UID="$(id -u testuser)"
+TESTUSER_GID="$(id -g testuser)"
+
+mkdir -p "/var/lib/machines/$CONTAINER"
+mkdir -p "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d"
+# Bind-mounting /etc into the container kinda defeats the purpose of --volatile=,
+# but we need the ASan-related overrides scattered across /etc
+cat > "/run/systemd/system/systemd-nspawn@$CONTAINER.service.d/override.conf" << EOF
+[Service]
+ExecStart=
+ExecStart=systemd-nspawn --quiet --link-journal=try-guest --keep-unit --machine=%i --boot \
+ --volatile=yes --directory=/ --bind-ro=/etc --inaccessible=/etc/machine-id
+EOF
+systemctl daemon-reload
+
+machinectl start "$CONTAINER"
+timeout 60 bash -xec "until systemd-run -M '$CONTAINER' -q --wait --pipe true; do sleep .5; done"
+machinectl copy-to "$CONTAINER" "$MAKE_DUMP_SCRIPT"
+
+run_namespaced_coredump_tests() {
+ local TS
+
+ # Make a couple of coredumps in a full-fleged container
+ TS="$(date +"%s.%N")"
+ [[ "$(coredumpctl list --since="@$TS" -q --no-legend /usr/bin/sleep | wc -l)" -eq 0 ]]
+ [[ "$(coredumpctl list --since="@$TS" -q --no-legend /usr/bin/sleep _UID="$TESTUSER_UID" | wc -l)" -eq 0 ]]
+ systemd-run -M "testuser@$CONTAINER" --user -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGABRT"
+ systemd-run -M "$CONTAINER" -q --wait --pipe "$MAKE_DUMP_SCRIPT" "/usr/bin/sleep" "SIGTRAP"
+ # Wait a bit for the coredumps to get processed
+ timeout 30 bash -c "while [[ \$(coredumpctl list --since=@$TS -q --no-legend /usr/bin/sleep | wc -l) -ne 2 ]]; do sleep 1; done"
+ coredumpctl list
+ [[ "$(coredumpctl list --since="@$TS" -q --no-legend /usr/bin/sleep _UID="$TESTUSER_UID" _GID="$TESTUSER_GID" | wc -l)" -eq 1 ]]
+
+ # Simplified version of the above - not a full container, just a mount & pid namespace
+ TS="$(date +"%s.%N")"
+ unshare --mount --pid --fork --mount-proc /bin/bash -xec "$MAKE_DUMP_SCRIPT /usr/bin/sleep SIGABRT"
+ timeout 30 bash -c "while [[ \$(coredumpctl list --since=@$TS -q --no-legend /usr/bin/sleep | wc -l) -ne 1 ]]; do sleep 1; done"
+ TS="$(date +"%s.%N")"
+ unshare --setuid="$TESTUSER_UID" --setgid="$TESTUSER_GID" --mount --pid --fork --mount-proc /bin/bash -xec "$MAKE_DUMP_SCRIPT /usr/bin/sleep SIGABRT"
+ timeout 30 bash -c "while [[ \$(coredumpctl list --since=@$TS -q --no-legend /usr/bin/sleep _UID=$TESTUSER_UID _GID=$TESTUSER_GID | wc -l) -ne 1 ]]; do sleep 1; done"
+}
+
+# First, run the tests with default systemd-coredumpd settings
+run_namespaced_coredump_tests
+
+# And now with SYSTEMD_COREDUMP_ALLOW_NAMESPACE_CHANGE=1 (RHEL-only)
+cat >/tmp/coredump-handler.sh <<EOF
+#!/bin/bash
+export SYSTEMD_COREDUMP_ALLOW_NAMESPACE_CHANGE=1
+exec /usr/lib/systemd/systemd-coredump "\$@"
+EOF
+chmod +x /tmp/coredump-handler.sh
+sysctl -w kernel.core_pattern="|/tmp/coredump-handler.sh %P %u %g %s %t %c %h"
+run_namespaced_coredump_tests
+
+# Restore the original coredump handler
+sysctl -p /usr/lib/sysctl.d/50-coredump.conf
+sysctl kernel.core_pattern
+
coredumpctl
SYSTEMD_LOG_LEVEL=debug coredumpctl
coredumpctl --help
@@ -89,7 +151,7 @@ coredumpctl --json=pretty | jq
coredumpctl --json=off
coredumpctl --root=/
coredumpctl --directory=/var/log/journal
-coredumpctl --file="/var/log/journal/$(</etc/machine-id)/system.journal"
+coredumpctl --file="/var/log/journal/$(</etc/machine-id)"/*.journal
coredumpctl --since=@0
coredumpctl --since=yesterday --until=tomorrow
# We should have a couple of externally stored coredumps

View File

@ -0,0 +1,29 @@
From ddf2ccf36bb804d666da37ce12d00123550d85f1 Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
Date: Thu, 25 Apr 2024 15:06:03 +0200
Subject: [PATCH] ci: update actions/upload-artifact to v4
`v3` will be deprecated soon, so update to `v4`.
https://github.blog/changelog/2024-04-16-deprecation-notice-v3-of-the-artifact-actions/
rhel-only
Related: RHEL-30372
---
.github/workflows/gather-metadata.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/gather-metadata.yml b/.github/workflows/gather-metadata.yml
index 635708a71f..59659d9bc5 100644
--- a/.github/workflows/gather-metadata.yml
+++ b/.github/workflows/gather-metadata.yml
@@ -22,7 +22,7 @@ jobs:
uses: redhat-plumbers-in-action/gather-pull-request-metadata@v1
- name: Upload artifact with gathered metadata
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@v4
with:
name: pr-metadata
path: ${{ steps.Metadata.outputs.metadata-file }}

View File

@ -0,0 +1,33 @@
From da0298596af24d1da92eb748b0a56065a9c041d9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cristian=20Rodr=C3=ADguez?= <crodriguez@owncloud.com>
Date: Fri, 11 Nov 2022 15:28:51 +0000
Subject: [PATCH] journal-remote: code is of type enum
MHD_RequestTerminationCode
Fixes gcc 13 -Wenum-int-mismatch which are enabled by default.
(cherry picked from commit aa70dd624bff6280ab6f2871f62d313bdb1e1bcc)
Related: RHEL-30372
---
src/journal-remote/microhttpd-util.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/journal-remote/microhttpd-util.h b/src/journal-remote/microhttpd-util.h
index 7e7d1b56b1..df18335469 100644
--- a/src/journal-remote/microhttpd-util.h
+++ b/src/journal-remote/microhttpd-util.h
@@ -64,11 +64,11 @@ void microhttpd_logger(void *arg, const char *fmt, va_list ap) _printf_(2, 0);
int mhd_respondf(struct MHD_Connection *connection,
int error,
- unsigned code,
+ enum MHD_RequestTerminationCode code,
const char *format, ...) _printf_(4,5);
int mhd_respond(struct MHD_Connection *connection,
- unsigned code,
+ enum MHD_RequestTerminationCode code,
const char *message);
int mhd_respond_oom(struct MHD_Connection *connection);

View File

@ -0,0 +1,30 @@
From cc1a9f1a9a74dd8f5491a3a0fd9734fbca731378 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cristian=20Rodr=C3=ADguez?= <crodriguez@owncloud.com>
Date: Fri, 11 Nov 2022 15:31:18 +0000
Subject: [PATCH] resolve: dns_server_feature_level_*_string type is
DnsServerFeatureLevel
gcc 13 -Wenum-int-mismatch reminds us that enum != int
(cherry picked from commit e14afe31c3e8380496dc85b57103b2f648bc7d43)
Related: RHEL-30372
---
src/resolve/resolved-dns-server.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/resolve/resolved-dns-server.h b/src/resolve/resolved-dns-server.h
index be9efb0a79..f939b534c3 100644
--- a/src/resolve/resolved-dns-server.h
+++ b/src/resolve/resolved-dns-server.h
@@ -44,8 +44,8 @@ typedef enum DnsServerFeatureLevel {
#define DNS_SERVER_FEATURE_LEVEL_IS_DNSSEC(x) ((x) >= DNS_SERVER_FEATURE_LEVEL_DO)
#define DNS_SERVER_FEATURE_LEVEL_IS_UDP(x) IN_SET(x, DNS_SERVER_FEATURE_LEVEL_UDP, DNS_SERVER_FEATURE_LEVEL_EDNS0, DNS_SERVER_FEATURE_LEVEL_DO)
-const char* dns_server_feature_level_to_string(int i) _const_;
-int dns_server_feature_level_from_string(const char *s) _pure_;
+const char* dns_server_feature_level_to_string(DnsServerFeatureLevel i) _const_;
+DnsServerFeatureLevel dns_server_feature_level_from_string(const char *s) _pure_;
struct DnsServer {
Manager *manager;

View File

@ -0,0 +1,36 @@
From 9f0967eb61b1889c97da705abaf0b0e905d117f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cristian=20Rodr=C3=ADguez?= <crodriguez@owncloud.com>
Date: Fri, 11 Nov 2022 15:34:32 +0000
Subject: [PATCH] shared|install: Use InstallChangeType consistently
gcc 13 -Wenum-int-mismatch, enabled by default, reminds us enum ! = int
(cherry picked from commit 9264db1a0ac6034ab5b40ef3f5914d8dc7d77aba)
Related: RHEL-30372
---
src/shared/install.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/shared/install.h b/src/shared/install.h
index 9bb412ba06..0abc73897e 100644
--- a/src/shared/install.h
+++ b/src/shared/install.h
@@ -197,7 +197,7 @@ int unit_file_exists(LookupScope scope, const LookupPaths *paths, const char *na
int unit_file_get_list(LookupScope scope, const char *root_dir, Hashmap *h, char **states, char **patterns);
Hashmap* unit_file_list_free(Hashmap *h);
-InstallChangeType install_changes_add(InstallChange **changes, size_t *n_changes, int type, const char *path, const char *source);
+InstallChangeType install_changes_add(InstallChange **changes, size_t *n_changes, InstallChangeType type, const char *path, const char *source);
void install_changes_free(InstallChange *changes, size_t n_changes);
void install_changes_dump(int r, const char *verb, const InstallChange *changes, size_t n_changes, bool quiet);
@@ -224,7 +224,7 @@ UnitFileState unit_file_state_from_string(const char *s) _pure_;
/* from_string conversion is unreliable because of the overlap between -EPERM and -1 for error. */
const char *install_change_type_to_string(InstallChangeType t) _const_;
-int install_change_type_from_string(const char *s) _pure_;
+InstallChangeType install_change_type_from_string(const char *s) _pure_;
const char *unit_file_preset_mode_to_string(UnitFilePresetMode m) _const_;
UnitFilePresetMode unit_file_preset_mode_from_string(const char *s) _pure_;

View File

@ -0,0 +1,78 @@
From 54c44b19c1018400c38da8f8be597536d14e7afa Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Thu, 18 Apr 2024 22:39:31 +0200
Subject: [PATCH] test: temporarily disable coredumps in testsuite-17.03.sh
Since f387005b54 we started generating coredumps by default (up to
certain size). This change has one unintentional effect on our test
suite - if a sanitized binary (udevd worker here) crashes and ASan is
instructed to allow core dumping (via disable_coredump=0 and
use_madv_dontdump=1), we try to dump a relatively big core file (~80
MiB), and since the test suite configures systemd-coredumpd to dump the
cores into the journal, we try to append it to the journal message about
the crash. However, journal complains that the message with the coredump
is too big so the crash report is not written, and we end up with
coredumpctl not showing the crash, which the test in this case uses to
monitor if the udevd worker's job timed out:
[ 17.873463] systemd-udevd[1617]: null: Worker [1625] processing SEQNUM=3588 is taking a long time
[ 17.876823] systemd-udevd[1625]: null: Spawned process '/bin/sleep 60' [1626] is taking longer than 3s to complete
...
[ 24.223459] systemd-udevd[1617]: null: Worker [1625] processing SEQNUM=3588 killed
[ 24.265141] systemd[1]: Created slice system-systemd\x2dcoredump.slice.
[ 24.284960] systemd[1]: Started systemd-coredump@0-1707-0.service.
[ 27.545120] systemd-journald[1225]: Failed to write entry to /var/log/journal/6da99a97048e4f08abd4ddabcf92bbdd/system.journal (51 items, 89252196 bytes) despite vacuuming, ignoring: Argument list too long
[ 27.551759] systemd-coredump[1709]: ==1709==LeakSanitizer has encountered a fatal error.
[ 27.551759] systemd-coredump[1709]: ==1709==HINT: For debugging, try setting environment variable LSAN_OPTIONS=verbosity=1:log_threads=1
[ 27.551759] systemd-coredump[1709]: ==1709==HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc)
The upstream version of this test doesn't suffer from this because it
was recently-ish rewritten to not check for the crash event
(5592608bdcb, but we're missing some udevd prerequisites for that to
work in RHEL9), and we also started instructing ASan to allow coredumps
after that change, so the issue was never encountered there in the first
place.
Since we don't really care about the actual coredump in this case, let's
just temporarily override the core rlimit to 0 for the udevd process.
Related: RHEL-30372
rhel-only
---
test/units/testsuite-17.03.sh | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/test/units/testsuite-17.03.sh b/test/units/testsuite-17.03.sh
index 318afdcb5a..8fa58e2f08 100755
--- a/test/units/testsuite-17.03.sh
+++ b/test/units/testsuite-17.03.sh
@@ -17,9 +17,16 @@ event_timeout=10
timeout_signal=SIGABRT
EOF
+ mkdir -p /run/systemd/system/systemd-udevd.service.d/
+ cat >/run/systemd/system/systemd-udevd.service.d/99-disable-coredumps.conf <<EOF
+[Service]
+LimitCORE=0
+EOF
+ systemctl daemon-reload
systemctl restart systemd-udevd.service
}
+# shellcheck disable=SC2317
teardown() {
set +e
@@ -27,10 +34,11 @@ teardown() {
kill "$KILL_PID"
fi
- rm -rf "$TMPDIR"
+ rm -rf "$TMPDIR" /run/systemd/system/systemd-udevd.service.d
mv -f /etc/udev/udev.conf.bckp /etc/udev/udev.conf
rm -f "$test_rule"
+ systemctl daemon-reload
systemctl restart systemd-udevd.service
}

View File

@ -0,0 +1,80 @@
From 365a74eef2463a011fbe7413ab5479b4fbd60650 Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
Date: Thu, 25 Apr 2024 15:46:35 +0200
Subject: [PATCH] ci: update manpage deployment workflow
rhel-only
Related: RHEL-30372
---
.github/workflows/deploy-man-pages.yml | 52 +-------------------------
1 file changed, 2 insertions(+), 50 deletions(-)
diff --git a/.github/workflows/deploy-man-pages.yml b/.github/workflows/deploy-man-pages.yml
index 08c3d6e322..9739228a87 100644
--- a/.github/workflows/deploy-man-pages.yml
+++ b/.github/workflows/deploy-man-pages.yml
@@ -37,61 +37,13 @@ jobs:
- name: Install dependencies
run: |
- RELEASE="$(lsb_release -cs)"
- sudo bash -c "echo 'deb-src http://archive.ubuntu.com/ubuntu/ $RELEASE main restricted universe multiverse' >>/etc/apt/sources.list"
- sudo add-apt-repository -y ppa:upstream-systemd-ci/systemd-ci
+ sudo add-apt-repository -y --no-update --enable-source
sudo apt-get -y update
sudo apt-get -y build-dep systemd
- sudo apt-get install -y \
- cryptsetup-bin \
- expect \
- fdisk \
- gettext \
- iputils-ping \
- isc-dhcp-client \
- itstool \
- kbd \
- libblkid-dev \
- libbpf-dev \
- libc6-dev-i386 \
- libcap-dev \
- libcurl4-gnutls-dev \
- libfdisk-dev \
- libfido2-dev \
- libgpg-error-dev \
- liblz4-dev \
- liblzma-dev \
- libmicrohttpd-dev \
- libmount-dev \
- libp11-kit-dev \
- libpwquality-dev \
- libqrencode-dev \
- libssl-dev \
- libtss2-dev \
- libxkbcommon-dev \
- libxtables-dev \
- libzstd-dev \
- meson \
- mold \
- mount \
- net-tools \
- ninja-build \
- perl \
- python3-evdev \
- python3-jinja2 \
- python3-lxml \
- python3-pip \
- python3-pyparsing \
- python3-setuptools \
- quota \
- strace \
- unifont \
- util-linux \
- zstd \
- name: Build HTML man pages
run: |
- meson build
+ meson setup build
ninja -C build man/html
- name: Setup Pages

View File

@ -0,0 +1,38 @@
From 41d2e7fbb87a99e80e9be1873775c79879f8b821 Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Fri, 2 Dec 2022 14:30:22 +0900
Subject: [PATCH] bootspec: fix null-dereference-read
Fixes [oss-fuzz#53578](https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=53578).
Fixes #25450.
(cherry picked from commit 46dc071985ff487f5ccf20808531168a6add73d3)
Resolves: RHEL-36284
---
src/shared/bootspec.c | 2 ++
...lusterfuzz-testcase-minimized-fuzz-bootspec-5731869371269120 | 1 +
2 files changed, 3 insertions(+)
create mode 100644 test/fuzz/fuzz-bootspec/clusterfuzz-testcase-minimized-fuzz-bootspec-5731869371269120
diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c
index 61e20c40a8..7fe8845429 100644
--- a/src/shared/bootspec.c
+++ b/src/shared/bootspec.c
@@ -996,6 +996,8 @@ static int boot_config_find(const BootConfig *config, const char *id) {
if (id[0] == '@') {
if (!strcaseeq(id, "@saved"))
return -1;
+ if (!config->entry_selected)
+ return -1;
id = config->entry_selected;
}
diff --git a/test/fuzz/fuzz-bootspec/clusterfuzz-testcase-minimized-fuzz-bootspec-5731869371269120 b/test/fuzz/fuzz-bootspec/clusterfuzz-testcase-minimized-fuzz-bootspec-5731869371269120
new file mode 100644
index 0000000000..8804abd094
--- /dev/null
+++ b/test/fuzz/fuzz-bootspec/clusterfuzz-testcase-minimized-fuzz-bootspec-5731869371269120
@@ -0,0 +1 @@
+{"config":"default @saved","loader":[""]}
\ No newline at end of file

View File

@ -0,0 +1,40 @@
From 0947147008c9b2cb56b40616fccccf64a6534f07 Mon Sep 17 00:00:00 2001
From: Frantisek Sumsal <frantisek@sumsal.cz>
Date: Tue, 17 Jan 2023 12:14:13 +0100
Subject: [PATCH] units: don't install pcrphase-related units without gnu-efi
since we don't have systemd-pcrphase built anyway, which breaks the tests:
...
I: Attempting to install /usr/lib/systemd/systemd-networkd-wait-online (based on unit file reference)
I: Attempting to install /usr/lib/systemd/systemd-network-generator (based on unit file reference)
I: Attempting to install /usr/lib/systemd/systemd-oomd (based on unit file reference)
I: Attempting to install /usr/lib/systemd/systemd-pcrphase (based on unit file reference)
W: Failed to install '/usr/lib/systemd/systemd-pcrphase'
make: *** [Makefile:4: setup] Error 1
make: Leaving directory '/root/systemd/test/TEST-01-BASIC'
Follow-up to 04959faa632272a8fc9cdac3121b2e4af721c1b6.
(cherry picked from commit 0eb635ef4bc11792cd4ef384ae252a2c7fd4122a)
Related: RHEL-33384
---
units/meson.build | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/units/meson.build b/units/meson.build
index cfc96a9111..39e6a9bb65 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -264,8 +264,8 @@ in_units = [
'sysinit.target.wants/'],
['systemd-pcrphase.service', 'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2',
'sysinit.target.wants/'],
- ['systemd-pcrfs-root.service', ''],
- ['systemd-pcrfs@.service', ''],
+ ['systemd-pcrfs-root.service', 'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2'],
+ ['systemd-pcrfs@.service', 'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2'],
['systemd-growfs-root.service', ''],
['systemd-growfs@.service', ''],
['systemd-pcrmachine.service', 'HAVE_GNU_EFI HAVE_OPENSSL HAVE_TPM2',

View File

@ -0,0 +1,44 @@
From 4ab2df57c79a923fba74b2cf48fd56c6a0756413 Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Mon, 18 Mar 2024 17:04:22 +0100
Subject: [PATCH] kernel-install: fix uki-copy deinstall
For "kernel-install remove ..." only the kernel version is passed, not
the kernel image. So auto-detecting KERNEL_INSTALL_IMAGE_TYPE and
setting KERNEL_INSTALL_LAYOUT does not work for uninstall.
The 90-uki-copy.install plugin must consider this and *not* exit early
for the "remove" command, otherwise $BOOT_ROOT will be filled with stale
kernel images.
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
(cherry picked from commit 3037616d8ed68f3263746e3c6399d4a05242068b)
Resolves: RHEL-36505
---
src/kernel-install/90-uki-copy.install | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/kernel-install/90-uki-copy.install b/src/kernel-install/90-uki-copy.install
index c66c09719c..d443c4b401 100755
--- a/src/kernel-install/90-uki-copy.install
+++ b/src/kernel-install/90-uki-copy.install
@@ -26,8 +26,6 @@ KERNEL_VERSION="${2:?}"
ENTRY_DIR_ABS="$3"
KERNEL_IMAGE="$4"
-[ "$KERNEL_INSTALL_LAYOUT" = "uki" ] || exit 0
-
ENTRY_TOKEN="$KERNEL_INSTALL_ENTRY_TOKEN"
BOOT_ROOT="$KERNEL_INSTALL_BOOT_ROOT"
@@ -48,6 +46,8 @@ case "$COMMAND" in
;;
esac
+[ "$KERNEL_INSTALL_LAYOUT" = "uki" ] || exit 0
+
if ! [ -d "$UKI_DIR" ]; then
[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && echo "creating $UKI_DIR"
mkdir -p "$UKI_DIR"

View File

@ -0,0 +1,27 @@
From 16eace42619860cbcfedca8c93e4ea20bfb0f98b Mon Sep 17 00:00:00 2001
From: Jan Macku <jamacku@redhat.com>
Date: Fri, 17 May 2024 14:02:07 +0200
Subject: [PATCH] ci(packit): explicitly clone `c9s` branch
Once default branch is changed to `c10s` the current configuration could stop working.
rhel-only
Related: RHEL-30372
---
.packit.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.packit.yml b/.packit.yml
index 35938f3586..9697a0df84 100644
--- a/.packit.yml
+++ b/.packit.yml
@@ -18,7 +18,7 @@ srpm_build_deps: []
actions:
post-upstream-clone:
# Use the CentOS Stream specfile
- - "git clone https://gitlab.com/redhat/centos-stream/rpms/systemd.git .packit_rpm --depth=1"
+ - "git clone -b c9s https://gitlab.com/redhat/centos-stream/rpms/systemd.git .packit_rpm --depth=1"
# Drop the "sources" file so rebase-helper doesn't think we're a dist-git
- "rm -fv .packit_rpm/sources"
# Drop all patches, since they're already included in the tarball

View File

@ -0,0 +1,25 @@
From 43373d851c9f6222f4523b9db7006fbfe14c3d70 Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Thu, 23 May 2024 18:07:45 +0200
Subject: [PATCH] ci(src-git): add RHEL-9.1 and RHEL-9.1.z to allowed versions
rhel-only
Related: RHEL-30372
---
.github/tracker-validator.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/tracker-validator.yml b/.github/tracker-validator.yml
index 31ef28f6ea..21cbedd8b0 100644
--- a/.github/tracker-validator.yml
+++ b/.github/tracker-validator.yml
@@ -8,6 +8,8 @@ products:
- CentOS Stream 9
- rhel-9.0.0
- rhel-9.0.0.z
+ - rhel-9.1.0
+ - rhel-9.1.0.z
- rhel-9.2.0
- rhel-9.2.0.z
- rhel-9.3.0

View File

@ -0,0 +1,30 @@
From aa18f6b2b2cad6977f39e1e323705e2b11a7829c Mon Sep 17 00:00:00 2001
From: Michal Sekletar <msekleta@redhat.com>
Date: Thu, 23 May 2024 17:35:51 +0200
Subject: [PATCH] libsystemd: link with '-z nodelete'
We want to avoid reinitialization of our global variables with static
storage duration in case we get dlopened multiple times by the same
application. This will avoid potential resource leaks that could have
happened otherwise (e.g. leaking journal socket fd).
(cherry picked from commit 9d8533b7152daf792356c601516b57c6412d3e52)
Resolves: RHEL-6589
---
meson.build | 2 ++
1 file changed, 2 insertions(+)
diff --git a/meson.build b/meson.build
index 843d823e3e..274e43ba9e 100644
--- a/meson.build
+++ b/meson.build
@@ -2004,6 +2004,8 @@ libsystemd = shared_library(
version : libsystemd_version,
include_directories : libsystemd_includes,
link_args : ['-shared',
+ # Make sure our library is never deleted from memory, so that our open logging fds don't leak on dlopen/dlclose cycles.
+ '-z', 'nodelete',
'-Wl,--version-script=' + libsystemd_sym_path],
link_with : [libbasic,
libbasic_gcrypt,

View File

@ -0,0 +1,54 @@
From d26e1ba4539a3a33224ca1019b9e6cf5590744f5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Fri, 23 Jun 2023 11:10:42 -0600
Subject: [PATCH] basic/utf8: make utf8_encoded_to_unichar() return length of
the codepoint
(cherry picked from commit 9579e9a5308573c3c9c82f1978456cc71f68760c)
Related: RHEL-31219
---
src/basic/utf8.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/src/basic/utf8.c b/src/basic/utf8.c
index 2532fcf81a..9d9e76904e 100644
--- a/src/basic/utf8.c
+++ b/src/basic/utf8.c
@@ -90,7 +90,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
switch (len) {
case 1:
*ret_unichar = (char32_t)str[0];
- return 0;
+ return 1;
case 2:
unichar = str[0] & 0x1f;
break;
@@ -119,15 +119,14 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) {
}
*ret_unichar = unichar;
-
- return 0;
+ return len;
}
bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newline) {
assert(str);
for (const char *p = str; length > 0;) {
- int encoded_len, r;
+ int encoded_len;
char32_t val;
encoded_len = utf8_encoded_valid_unichar(p, length);
@@ -135,8 +134,7 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool allow_newlin
return false;
assert(encoded_len > 0 && (size_t) encoded_len <= length);
- r = utf8_encoded_to_unichar(p, &val);
- if (r < 0 ||
+ if (utf8_encoded_to_unichar(p, &val) < 0 ||
unichar_is_control(val) ||
(!allow_newline && val == '\n'))
return false;

View File

@ -0,0 +1,67 @@
From 0fc377b76f10eb283d4de76b8fe7c083b95f70b7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Fri, 23 Jun 2023 17:24:11 -0600
Subject: [PATCH] test-gunicode: add new test to show that unichar_iswide() is
borked
I discovered this while looking at the tests with wide characters in the next
patch. It's something to fix, but not directly relevant to the issue of
skipping ANSI in ellipsization. We will generate output that is wider than
expected in some cases, but wide characters are used very rarely so this isn't
such a big problem.
(cherry picked from commit d9c72e54190db2a0845d1558b5beb734e9f629ff)
Related: RHEL-31219
---
src/test/meson.build | 2 ++
src/test/test-gunicode.c | 27 +++++++++++++++++++++++++++
2 files changed, 29 insertions(+)
create mode 100644 src/test/test-gunicode.c
diff --git a/src/test/meson.build b/src/test/meson.build
index 5430e72ab5..1d61dc343f 100644
--- a/src/test/meson.build
+++ b/src/test/meson.build
@@ -469,6 +469,8 @@ tests += [
[files('test-gpt.c')],
+ [files('test-gunicode.c')],
+
[files('test-log.c')],
[files('test-ipcrm.c'),
diff --git a/src/test/test-gunicode.c b/src/test/test-gunicode.c
new file mode 100644
index 0000000000..1836cdc04a
--- /dev/null
+++ b/src/test/test-gunicode.c
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include "gunicode.h"
+#include "tests.h"
+#include "utf8.h"
+
+TEST(unichar_iswide) {
+ char32_t c;
+ int r;
+
+ /* FIXME: the cats are wide, but we get this wrong */
+ for (const char *narrow = "abX_…ąęµ!" "😼😿🙀😸😻"; *narrow; narrow += r) {
+ r = utf8_encoded_to_unichar(narrow, &c);
+ bool w = unichar_iswide(c);
+ assert_se(r > 0);
+ assert_se(!w);
+ }
+
+ for (const char *wide = "🐱/¥"; *wide; wide += r) {
+ r = utf8_encoded_to_unichar(wide, &c);
+ bool w = unichar_iswide(c);
+ assert_se(r > 0);
+ assert_se(w);
+ }
+}
+
+DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -0,0 +1,320 @@
From cec4cc86486d3e212b5e919595feb39c6cee4c2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
Date: Fri, 23 Jun 2023 18:40:14 -0600
Subject: [PATCH] string-util: pass ANSI sequences through unchanged
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Cutting off in the middle may leave the terminal in a bad state, breaking
further output. But we don't know what a given ANSI sequence does, e.g.
ANSI_NORMAL should not be skipped. But it is also nice to keep various
sequences intact, so that if we had part of the string in blue, and we cut out
the beginning of the blue part, we still want to keep the remainder in color.
So let's just pass them through, stripping out the characters that take up
actual space.
Also, use memcpy_safe as we may end up copying zero bytes when ellipsizing at
the start/end of a string.
Fixes: #24502
This also fixes an ugliness where we would ellipsize string with ANSI
sequences too much, leading to output that was narrower on screen than the
requested length:
Starting AAAAAAAAAAAAAAAAAAAAA.service
Starting BBBBBBBBBBBBBBBBBBBBB.service
Starting LONG…ER.service
Co-authored-by: Jan Janssen <medhefgo@web.de>
(cherry picked from commit cb558ab222f0dbda3afd985c2190f35693963ffa)
Resolves: RHEL-31219
---
src/basic/string-util.c | 163 ++++++++++++++++++++++++++++++--------
src/test/test-ellipsize.c | 41 ++++++++++
2 files changed, 172 insertions(+), 32 deletions(-)
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
index 17d35fe1a4..fe6e9e94ad 100644
--- a/src/basic/string-util.c
+++ b/src/basic/string-util.c
@@ -288,6 +288,62 @@ static int write_ellipsis(char *buf, bool unicode) {
return 3;
}
+static size_t ansi_sequence_length(const char *s, size_t len) {
+ assert(s);
+
+ if (len < 2)
+ return 0;
+
+ if (s[0] != 0x1B) /* ASCII 27, aka ESC, aka Ctrl-[ */
+ return 0; /* Not the start of a sequence */
+
+ if (s[1] == 0x5B) { /* [, start of CSI sequence */
+ size_t i = 2;
+
+ if (i == len)
+ return 0;
+
+ while (s[i] >= 0x30 && s[i] <= 0x3F) /* Parameter bytes */
+ if (++i == len)
+ return 0;
+ while (s[i] >= 0x20 && s[i] <= 0x2F) /* Intermediate bytes */
+ if (++i == len)
+ return 0;
+ if (s[i] >= 0x40 && s[i] <= 0x7E) /* Final byte */
+ return i + 1;
+ return 0; /* Bad sequence */
+
+ } else if (s[1] >= 0x40 && s[1] <= 0x5F) /* other non-CSI Fe sequence */
+ return 2;
+
+ return 0; /* Bad escape? */
+}
+
+static bool string_has_ansi_sequence(const char *s, size_t len) {
+ const char *t = s;
+
+ while ((t = memchr(s, 0x1B, len - (t - s))))
+ if (ansi_sequence_length(t, len - (t - s)) > 0)
+ return true;
+ return false;
+}
+
+static size_t previous_ansi_sequence(const char *s, size_t length, const char **ret_where) {
+ /* Locate the previous ANSI sequence and save its start in *ret_where and return length. */
+
+ for (size_t i = length - 2; i > 0; i--) { /* -2 because at least two bytes are needed */
+ size_t slen = ansi_sequence_length(s + (i - 1), length - (i - 1));
+ if (slen == 0)
+ continue;
+
+ *ret_where = s + (i - 1);
+ return slen;
+ }
+
+ *ret_where = NULL;
+ return 0;
+}
+
static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
size_t x, need_space, suffix_len;
char *t;
@@ -347,7 +403,6 @@ static char *ascii_ellipsize_mem(const char *s, size_t old_length, size_t new_le
char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigned percent) {
size_t x, k, len, len2;
const char *i, *j;
- char *e;
int r;
/* Note that 'old_length' refers to bytes in the string, while 'new_length' refers to character cells taken up
@@ -371,73 +426,117 @@ char *ellipsize_mem(const char *s, size_t old_length, size_t new_length, unsigne
if (new_length == 0)
return strdup("");
- /* If no multibyte characters use ascii_ellipsize_mem for speed */
- if (ascii_is_valid_n(s, old_length))
+ bool has_ansi_seq = string_has_ansi_sequence(s, old_length);
+
+ /* If no multibyte characters or ANSI sequences, use ascii_ellipsize_mem for speed */
+ if (!has_ansi_seq && ascii_is_valid_n(s, old_length))
return ascii_ellipsize_mem(s, old_length, new_length, percent);
- x = ((new_length - 1) * percent) / 100;
+ x = (new_length - 1) * percent / 100;
assert(x <= new_length - 1);
k = 0;
- for (i = s; i < s + old_length; i = utf8_next_char(i)) {
- char32_t c;
- int w;
+ for (i = s; i < s + old_length; ) {
+ size_t slen = has_ansi_seq ? ansi_sequence_length(i, old_length - (i - s)) : 0;
+ if (slen > 0) {
+ i += slen;
+ continue; /* ANSI sequences don't take up any space in output */
+ }
+ char32_t c;
r = utf8_encoded_to_unichar(i, &c);
if (r < 0)
return NULL;
- w = unichar_iswide(c) ? 2 : 1;
- if (k + w <= x)
- k += w;
- else
+ int w = unichar_iswide(c) ? 2 : 1;
+ if (k + w > x)
break;
+
+ k += w;
+ i += r;
}
- for (j = s + old_length; j > i; ) {
+ const char *ansi_start = s + old_length;
+ size_t ansi_len = 0;
+
+ for (const char *t = j = s + old_length; t > i && k < new_length; ) {
char32_t c;
int w;
- const char *jj;
+ const char *tt;
+
+ if (has_ansi_seq && ansi_start >= t)
+ /* Figure out the previous ANSI sequence, if any */
+ ansi_len = previous_ansi_sequence(s, t - s, &ansi_start);
- jj = utf8_prev_char(j);
- r = utf8_encoded_to_unichar(jj, &c);
+ /* If the sequence extends all the way to the current position, skip it. */
+ if (has_ansi_seq && ansi_len > 0 && ansi_start + ansi_len == t) {
+ t = ansi_start;
+ continue;
+ }
+
+ tt = utf8_prev_char(t);
+ r = utf8_encoded_to_unichar(tt, &c);
if (r < 0)
return NULL;
w = unichar_iswide(c) ? 2 : 1;
- if (k + w <= new_length) {
- k += w;
- j = jj;
- } else
+ if (k + w > new_length)
break;
+
+ k += w;
+ j = t = tt; /* j should always point to the first "real" character */
}
- assert(i <= j);
- /* we don't actually need to ellipsize */
- if (i == j)
+ /* We don't actually need to ellipsize */
+ if (i >= j)
return memdup_suffix0(s, old_length);
- /* make space for ellipsis, if possible */
- if (j < s + old_length)
- j = utf8_next_char(j);
- else if (i > s)
- i = utf8_prev_char(i);
+ if (k >= new_length) {
+ /* Make space for ellipsis, if required and possible. We know that the edge character is not
+ * part of an ANSI sequence (because then we'd skip it). If the last character we looked at
+ * was wide, we don't need to make space. */
+ if (j < s + old_length)
+ j = utf8_next_char(j);
+ else if (i > s)
+ i = utf8_prev_char(i);
+ }
len = i - s;
len2 = s + old_length - j;
- e = new(char, len + 3 + len2 + 1);
+
+ /* If we have ANSI, allow the same length as the source string + ellipsis. It'd be too involved to
+ * figure out what exact space is needed. Strings with ANSI sequences are most likely to be fairly
+ * short anyway. */
+ size_t alloc_len = has_ansi_seq ? old_length + 3 + 1 : len + 3 + len2 + 1;
+
+ char *e = new(char, alloc_len);
if (!e)
return NULL;
/*
- printf("old_length=%zu new_length=%zu x=%zu len=%u len2=%u k=%u\n",
+ printf("old_length=%zu new_length=%zu x=%zu len=%zu len2=%zu k=%zu\n",
old_length, new_length, x, len, len2, k);
*/
- memcpy(e, s, len);
+ memcpy_safe(e, s, len);
write_ellipsis(e + len, true);
- memcpy(e + len + 3, j, len2);
- *(e + len + 3 + len2) = '\0';
+
+ char *dst = e + len + 3;
+
+ if (has_ansi_seq)
+ /* Copy over any ANSI sequences in full */
+ for (const char *p = s + len; p < j; ) {
+ size_t slen = ansi_sequence_length(p, j - p);
+ if (slen > 0) {
+ memcpy(dst, p, slen);
+ dst += slen;
+ p += slen;
+ } else
+ p = utf8_next_char(p);
+ }
+
+ memcpy_safe(dst, j, len2);
+ dst[len2] = '\0';
return e;
}
diff --git a/src/test/test-ellipsize.c b/src/test/test-ellipsize.c
index 7317193363..8f7e17bfe9 100644
--- a/src/test/test-ellipsize.c
+++ b/src/test/test-ellipsize.c
@@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "def.h"
+#include "escape.h"
#include "string-util.h"
#include "strv.h"
#include "terminal-util.h"
@@ -116,4 +117,44 @@ TEST(ellipsize) {
test_ellipsize_one("shórt");
}
+TEST(ellipsize_ansi) {
+ const char *s = ANSI_HIGHLIGHT_YELLOW_UNDERLINE "yęllow"
+ ANSI_HIGHLIGHT_GREY_UNDERLINE "grěy"
+ ANSI_HIGHLIGHT_BLUE_UNDERLINE "blue"
+ ANSI_NORMAL "nórmął";
+ size_t len = strlen(s);
+
+ for (unsigned percent = 0; percent <= 100; percent += 15)
+ for (ssize_t x = 21; x >= 0; x--) {
+ _cleanup_free_ char *t = ellipsize_mem(s, len, x, percent);
+ printf("%02zd: \"%s\"\n", x, t);
+ assert_se(utf8_is_valid(t));
+
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *e = cescape(t);
+ printf(" : \"%s\"\n", e);
+ }
+ }
+}
+
+TEST(ellipsize_ansi_cats) {
+ _cleanup_free_ char *e, *f, *g, *h;
+
+ /* Make sure we don't cut off in the middle of an ANSI escape sequence. */
+
+ e = ellipsize("01" ANSI_NORMAL "23", 4, 0);
+ puts(e);
+ assert_se(streq(e, "01" ANSI_NORMAL "23"));
+ f = ellipsize("ab" ANSI_NORMAL "cd", 4, 90);
+ puts(f);
+ assert_se(streq(f, "ab" ANSI_NORMAL "cd"));
+
+ g = ellipsize("🐱🐱" ANSI_NORMAL "🐱🐱" ANSI_NORMAL, 5, 0);
+ puts(g);
+ assert_se(streq(g, "…" ANSI_NORMAL "🐱🐱" ANSI_NORMAL));
+ h = ellipsize("🐱🐱" ANSI_NORMAL "🐱🐱" ANSI_NORMAL, 5, 90);
+ puts(h);
+ assert_se(streq(h, "🐱…" ANSI_NORMAL "🐱" ANSI_NORMAL));
+}
+
DEFINE_TEST_MAIN(LOG_INFO);

View File

@ -0,0 +1,75 @@
From eac6ff660b32656d2a39bdc13d729e7eb0288596 Mon Sep 17 00:00:00 2001
From: Luca Boccassi <bluca@debian.org>
Date: Wed, 15 Feb 2023 00:44:01 +0000
Subject: [PATCH] cryptsetup: do not assert when unsealing token without salt
Salt was added in v253. We are not checking whether it was actually found
(non-zero size), so when an old tpm+pin enrollment is opened things go boom.
For good measure, check both the buffer and the size in both places.
Assertion 'saltlen > 0' failed at src/shared/tpm2-util.c:2490, function tpm2_util_pbkdf2_hmac_sha256(). Aborting.
(cherry picked from commit 504d0acf61c8472bc93c2a927e858074873b2eaf)
Resolves: RHEL-38864
---
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c | 3 ++-
src/cryptsetup/cryptsetup-tpm2.c | 4 +++-
src/shared/tpm2-util.c | 1 +
3 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
index 630a2d8d3e..e353e947aa 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
@@ -40,6 +40,7 @@ int acquire_luks2_key(
_cleanup_(erase_and_freep) char *b64_salted_pin = NULL;
int r;
+ assert(salt || salt_size == 0);
assert(ret_decrypted_key);
assert(ret_decrypted_key_size);
@@ -60,7 +61,7 @@ int acquire_luks2_key(
if ((flags & TPM2_FLAGS_USE_PIN) && salt && !pin)
return -ENOANO;
- if (pin) {
+ if (pin && salt_size > 0) {
uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
CLEANUP_ERASE(salted_pin);
r = tpm2_util_pbkdf2_hmac_sha256(pin, strlen(pin), salt, salt_size, salted_pin);
diff --git a/src/cryptsetup/cryptsetup-tpm2.c b/src/cryptsetup/cryptsetup-tpm2.c
index c049b8a313..036f3d3a00 100644
--- a/src/cryptsetup/cryptsetup-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tpm2.c
@@ -88,6 +88,8 @@ int acquire_tpm2_key(
const void *blob;
int r;
+ assert(salt || salt_size == 0);
+
if (!device) {
r = tpm2_find_device_auto(&auto_device);
if (r == -ENODEV)
@@ -165,7 +167,7 @@ int acquire_tpm2_key(
if (r < 0)
return r;
- if (salt) {
+ if (salt_size > 0) {
uint8_t salted_pin[SHA256_DIGEST_SIZE] = {};
CLEANUP_ERASE(salted_pin);
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index 4e382f691e..1d2d4ddda4 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -6041,6 +6041,7 @@ int tpm2_util_pbkdf2_hmac_sha256(const void *pass,
*/
static const uint8_t block_cnt[] = { 0, 0, 0, 1 };
+ assert (salt);
assert (saltlen > 0);
assert (saltlen <= (SIZE_MAX - sizeof(block_cnt)));
assert (passlen > 0);

View File

@ -0,0 +1,29 @@
From 661e3758451dc504eeb176194293c87f238d55dd Mon Sep 17 00:00:00 2001
From: Yu Watanabe <watanabe.yu+github@gmail.com>
Date: Fri, 17 Feb 2023 08:24:54 +0900
Subject: [PATCH] cryptsetup: check the existence of salt by salt_size > 0
Follow-up for 504d0acf61c8472bc93c2a927e858074873b2eaf.
The function may be called with non-NULL salt and salt_size == 0.
(cherry picked from commit 8c2264abb9c16bc2933f95be299f15ee66c21181)
Related: RHEL-38864
---
src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
index e353e947aa..5230a84025 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-tpm2.c
@@ -58,7 +58,7 @@ int acquire_luks2_key(
return -ENOANO;
/* If we're using a PIN, and the luks header has a salt, it better have a pin too */
- if ((flags & TPM2_FLAGS_USE_PIN) && salt && !pin)
+ if ((flags & TPM2_FLAGS_USE_PIN) && salt_size > 0 && !pin)
return -ENOANO;
if (pin && salt_size > 0) {

View File

@ -0,0 +1,83 @@
From 3e65e8111f7cc30ac38901dced3ed0defbd90206 Mon Sep 17 00:00:00 2001
From: Mike Yuan <me@yhndnzj.com>
Date: Sat, 17 Feb 2024 03:03:50 +0800
Subject: [PATCH] core/mount: if umount(8) fails but mount disappeared, assume
success
Fixes #31337
(cherry picked from commit 8e94bb62a5c1309c56c57e0a505aae13a2ac5f4f)
Resolves: RHEL-13159
---
src/core/mount.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/core/mount.c b/src/core/mount.c
index a46ac804d8..cfe3f40302 100644
--- a/src/core/mount.c
+++ b/src/core/mount.c
@@ -1439,7 +1439,8 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
if (IN_SET(m->state, MOUNT_REMOUNTING, MOUNT_REMOUNTING_SIGKILL, MOUNT_REMOUNTING_SIGTERM))
mount_set_reload_result(m, f);
- else if (m->result == MOUNT_SUCCESS)
+ else if (m->result == MOUNT_SUCCESS && !IN_SET(m->state, MOUNT_MOUNTING, MOUNT_UNMOUNTING))
+ /* MOUNT_MOUNTING and MOUNT_UNMOUNTING states need to be patched, see below. */
m->result = f;
if (m->control_command) {
@@ -1462,11 +1463,11 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
switch (m->state) {
case MOUNT_MOUNTING:
- /* Our mount point has not appeared in mountinfo. Something went wrong. */
+ /* Our mount point has not appeared in mountinfo. Something went wrong. */
if (f == MOUNT_SUCCESS) {
- /* Either /bin/mount has an unexpected definition of success,
- * or someone raced us and we lost. */
+ /* Either /bin/mount has an unexpected definition of success, or someone raced us
+ * and we lost. */
log_unit_warning(UNIT(m), "Mount process finished, but there is no mount.");
f = MOUNT_FAILURE_PROTOCOL;
}
@@ -1484,9 +1485,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
case MOUNT_UNMOUNTING:
-
if (f == MOUNT_SUCCESS && m->from_proc_self_mountinfo) {
-
/* Still a mount point? If so, let's try again. Most likely there were multiple mount points
* stacked on top of each other. We might exceed the timeout specified by the user overall,
* but we will stop as soon as any one umount times out. */
@@ -1499,13 +1498,18 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
log_unit_warning(u, "Mount still present after %u attempts to unmount, giving up.", m->n_retry_umount);
mount_enter_mounted(m, f);
}
+ } else if (f == MOUNT_FAILURE_EXIT_CODE && !m->from_proc_self_mountinfo) {
+ /* Hmm, umount process spawned by us failed, but the mount disappeared anyway?
+ * Maybe someone else is trying to unmount at the same time. */
+ log_unit_notice(u, "Mount disappeared even though umount process failed, continuing.");
+ mount_enter_dead(m, MOUNT_SUCCESS);
} else
mount_enter_dead_or_mounted(m, f);
break;
- case MOUNT_UNMOUNTING_SIGKILL:
case MOUNT_UNMOUNTING_SIGTERM:
+ case MOUNT_UNMOUNTING_SIGKILL:
mount_enter_dead_or_mounted(m, f);
break;
@@ -2040,7 +2044,7 @@ static int mount_process_proc_self_mountinfo(Manager *m) {
* then remove it because of an internal error. E.g., fuse.sshfs seems
* to do that when the connection fails. See #17617. To handle such the
* case, let's once set the state back to mounting. Then, the unit can
- * correctly enter the failed state later in mount_sigchld(). */
+ * correctly enter the failed state later in mount_sigchld_event(). */
mount_set_state(mount, MOUNT_MOUNTING);
break;

View File

@ -0,0 +1,31 @@
From 82231e1834a5936216c666e3488bccb8b82de258 Mon Sep 17 00:00:00 2001
From: Daan De Meyer <daan.j.demeyer@gmail.com>
Date: Mon, 24 Apr 2023 20:55:15 +0200
Subject: [PATCH] Drop log level of header limits log message
Especially when using in-memory logging, these are too noisy so
let's drop them back to debug level.
(cherry picked from commit afc47ee2af456d12670df862457dcc7f6b864d79)
Related: RHEL-33890
---
src/journal/journald-server.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/journal/journald-server.c b/src/journal/journald-server.c
index cbcf1e9d9e..56f2ea8583 100644
--- a/src/journal/journald-server.c
+++ b/src/journal/journald-server.c
@@ -848,8 +848,9 @@ static void write_to_journal(Server *s, uid_t uid, struct iovec *iovec, size_t n
if (!f)
return;
- if (journal_file_rotate_suggested(f->file, s->max_file_usec, LOG_INFO)) {
- log_info("%s: Journal header limits reached or header out-of-date, rotating.", f->file->path);
+ if (journal_file_rotate_suggested(f->file, s->max_file_usec, LOG_DEBUG)) {
+ log_debug("%s: Journal header limits reached or header out-of-date, rotating.",
+ f->file->path);
rotate = true;
}
}

Some files were not shown because too many files have changed in this diff Show More