efivarfs readv() size fixes and more (#39715)

Fixes: #39695
This commit is contained in:
Yu Watanabe
2025-11-14 00:38:29 +09:00
committed by GitHub
7 changed files with 50 additions and 37 deletions

View File

@@ -14,6 +14,7 @@
#include "io-util.h"
#include "log.h"
#include "memory-util.h"
#include "stat-util.h"
#include "string-util.h"
#include "time-util.h"
#include "utf8.h"
@@ -32,6 +33,7 @@ int efi_get_variable(
void **ret_value,
size_t *ret_size) {
int r;
usec_t begin = 0; /* Unnecessary initialization to appease gcc */
assert(variable);
@@ -66,13 +68,16 @@ int efi_get_variable(
if (fstat(fd, &st) < 0)
return log_debug_errno(errno, "fstat(\"%s\") failed: %m", p);
if (st.st_size == 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
"EFI variable %s is uncommitted", p);
if (st.st_size < 4)
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable %s is shorter than 4 bytes, refusing.", p);
if (st.st_size > 4*1024*1024 + 4)
return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable %s is ridiculously large, refusing.", p);
r = stat_verify_regular(&st);
if (r < 0)
return log_debug_errno(r, "EFI variable '%s' is not a regular file, refusing: %m", p);
if (st.st_size == 0) /* for uncommited variables, see below */
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT), "EFI variable '%s' is uncommitted", p);
if ((uint64_t) st.st_size < sizeof(attr))
return log_debug_errno(SYNTHETIC_ERRNO(ENODATA), "EFI variable '%s' is shorter than %zu bytes, refusing.", p, sizeof(attr));
if ((uint64_t) st.st_size > sizeof(attr) + 4 * U64_MB)
return log_debug_errno(SYNTHETIC_ERRNO(E2BIG), "EFI variable '%s' is ridiculously large, refusing.", p);
if (!ret_attribute && !ret_value) {
/* No need to read anything, return the reported size. */
@@ -81,31 +86,37 @@ int efi_get_variable(
}
/* We want +1 for the read call, and +3 for the additional terminating bytes added below. */
char *t = realloc(buf, (size_t) st.st_size + MAX(1, 3));
if (!t)
free(buf);
buf = malloc((size_t) st.st_size - sizeof(attr) + CONST_MAX(1, 3));
if (!buf)
return -ENOMEM;
buf = t;
const struct iovec iov[] = {
{ &attr, sizeof(attr) },
{ buf, (size_t) st.st_size + 1 },
struct iovec iov[] = {
{ &attr, sizeof(attr) },
{ buf, (size_t) st.st_size - sizeof(attr) + 1 },
};
n = readv(fd, iov, 2);
assert(n <= st.st_size + 1);
if (n == st.st_size + 1)
/* We need to try again with a bigger buffer. */
continue;
if (n >= 0)
break;
if (n < 0) {
if (errno != EINTR)
return log_debug_errno(errno, "Reading from '%s' failed: %m", p);
log_debug("Reading from '%s' failed with EINTR, retrying.", p);
} else if ((size_t) n == sizeof(attr) + st.st_size + 1)
/* We need to try again with a bigger buffer, the variable was apparently changed concurrently? */
log_debug("EFI variable '%s' larger than expected, retrying.", p);
else {
assert((size_t) n < sizeof(attr) + st.st_size + 1);
break;
}
log_debug_errno(errno, "Reading from \"%s\" failed: %m", p);
if (errno != EINTR)
return -errno;
if (try >= EFI_N_RETRIES_TOTAL)
return -EBUSY;
return log_debug_errno(SYNTHETIC_ERRNO(EBUSY), "Reading EFI variable '%s' failed even after %u tries, giving up.", p, try);
if (try >= EFI_N_RETRIES_NO_DELAY)
(void) usleep_safe(EFI_RETRY_DELAY);
/* Start from the beginning */
(void) lseek(fd, 0, SEEK_SET);
}
/* Unfortunately kernel reports EOF if there's an inconsistency between efivarfs var list and
@@ -122,19 +133,21 @@ int efi_get_variable(
if (n == 0)
return log_debug_errno(SYNTHETIC_ERRNO(ENOENT),
"EFI variable %s is uncommitted", p);
if (n < 4)
if ((size_t) n < sizeof(attr))
return log_debug_errno(SYNTHETIC_ERRNO(EIO),
"Read %zi bytes from EFI variable %s, expected >= 4", n, p);
"Read %zi bytes from EFI variable %s, expected >= %zu", n, p, sizeof(attr));
size_t value_size = n - sizeof(attr);
if (ret_attribute)
*ret_attribute = attr;
if (ret_value) {
assert(buf);
/* Always NUL-terminate (3 bytes, to properly protect UTF-16, even if truncated in
* the middle of a character) */
buf[n - 4] = 0;
buf[n - 4 + 1] = 0;
buf[n - 4 + 2] = 0;
buf[value_size] = 0;
buf[value_size + 1] = 0;
buf[value_size + 2] = 0;
*ret_value = TAKE_PTR(buf);
}
@@ -149,7 +162,7 @@ int efi_get_variable(
* with a smaller value. */
if (ret_size)
*ret_size = n - 4;
*ret_size = value_size;
return 0;
}

View File

@@ -570,7 +570,7 @@ static int home_parse_worker_stdout(int _fd, UserRecord **ret) {
return 0;
}
if (lseek(fd, SEEK_SET, 0) < 0)
if (lseek(fd, 0, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to beginning of memfd: %m");
f = take_fdopen(&fd, "r");

View File

@@ -365,7 +365,7 @@ static int raw_pull_make_local_copy(RawPull *i) {
assert(i->raw_job->disk_fd >= 0);
assert(i->offset == UINT64_MAX);
if (lseek(i->raw_job->disk_fd, SEEK_SET, 0) < 0)
if (lseek(i->raw_job->disk_fd, 0, SEEK_SET) < 0)
return log_error_errno(errno, "Failed to seek to beginning of vendor image: %m");
}

View File

@@ -570,8 +570,8 @@ int seat_read_active_vt(Seat *s) {
if (!seat_has_vts(s))
return 0;
if (lseek(s->manager->console_active_fd, SEEK_SET, 0) < 0)
return log_error_errno(errno, "lseek on console_active_fd failed: %m");
if (lseek(s->manager->console_active_fd, 0, SEEK_SET) < 0)
return log_error_errno(errno, "lseek() on console_active_fd failed: %m");
errno = 0;
k = read(s->manager->console_active_fd, t, sizeof(t)-1);

View File

@@ -124,7 +124,7 @@ TEST(copy_file_fd) {
assert_se(write_string_file(in_fn, text, WRITE_STRING_FILE_CREATE) == 0);
assert_se(copy_file_fd("/a/file/which/does/not/exist/i/guess", out_fd, COPY_REFLINK) < 0);
assert_se(copy_file_fd(in_fn, out_fd, COPY_REFLINK) >= 0);
assert_se(lseek(out_fd, SEEK_SET, 0) == 0);
assert_se(lseek(out_fd, 0, SEEK_SET) == 0);
assert_se(read(out_fd, buf, sizeof buf) == (ssize_t) strlen(text));
ASSERT_STREQ(buf, text);

View File

@@ -525,7 +525,7 @@ TEST(pid_get_cmdline_harder) {
#define EXPECT1p "foo $'\\'bar\\'' $'\"bar$\"' $'x y z' $'!``'"
#define EXPECT1v STRV_MAKE("foo", "'bar'", "\"bar$\"", "x y z", "!``")
ASSERT_OK_ZERO_ERRNO(lseek(fd, SEEK_SET, 0));
ASSERT_OK_ZERO_ERRNO(lseek(fd, 0, SEEK_SET));
ASSERT_OK_EQ_ERRNO(write(fd, CMDLINE1, sizeof(CMDLINE1)), (ssize_t) sizeof(CMDLINE1));
ASSERT_OK_ZERO_ERRNO(ftruncate(fd, sizeof(CMDLINE1)));
@@ -550,7 +550,7 @@ TEST(pid_get_cmdline_harder) {
#define EXPECT2p "foo $'\\001\\002\\003'"
#define EXPECT2v STRV_MAKE("foo", "\1\2\3")
ASSERT_OK_ZERO_ERRNO(lseek(fd, SEEK_SET, 0));
ASSERT_OK_ZERO_ERRNO(lseek(fd, 0, SEEK_SET));
ASSERT_OK_EQ_ERRNO(write(fd, CMDLINE2, sizeof(CMDLINE2)), (ssize_t) sizeof(CMDLINE2));
ASSERT_OK_ZERO_ERRNO(ftruncate(fd, sizeof CMDLINE2));

View File

@@ -69,7 +69,7 @@ int probe_smart_media(int mtd_fd, mtd_info_t* info) {
}
for (offset = 0; offset < block_size * spare_count; offset += sector_size) {
(void) lseek(mtd_fd, SEEK_SET, offset);
(void) lseek(mtd_fd, offset, SEEK_SET);
if (read(mtd_fd, cis_buffer, SM_SECTOR_SIZE) == SM_SECTOR_SIZE) {
cis_found = 1;