parent
335ec71169
commit
b26cc82258
@ -0,0 +1,101 @@
|
||||
From 6415f270c3733d3c80e4d5bdc2e67f9ffb108308 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
||||
Date: Tue, 29 Nov 2022 09:00:16 +0100
|
||||
Subject: [PATCH] coredump: adjust whitespace
|
||||
|
||||
(cherry picked from commit 510a146634f3e095b34e2a26023b1b1f99dcb8c0)
|
||||
|
||||
Related: #2155516
|
||||
---
|
||||
src/coredump/coredump.c | 56 ++++++++++++++++++++---------------------
|
||||
1 file changed, 28 insertions(+), 28 deletions(-)
|
||||
|
||||
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
|
||||
index fd156370b2..5f315b9a79 100644
|
||||
--- a/src/coredump/coredump.c
|
||||
+++ b/src/coredump/coredump.c
|
||||
@@ -109,16 +109,16 @@ enum {
|
||||
};
|
||||
|
||||
static const char * const meta_field_names[_META_MAX] = {
|
||||
- [META_ARGV_PID] = "COREDUMP_PID=",
|
||||
- [META_ARGV_UID] = "COREDUMP_UID=",
|
||||
- [META_ARGV_GID] = "COREDUMP_GID=",
|
||||
- [META_ARGV_SIGNAL] = "COREDUMP_SIGNAL=",
|
||||
- [META_ARGV_TIMESTAMP] = "COREDUMP_TIMESTAMP=",
|
||||
- [META_ARGV_RLIMIT] = "COREDUMP_RLIMIT=",
|
||||
- [META_ARGV_HOSTNAME] = "COREDUMP_HOSTNAME=",
|
||||
- [META_COMM] = "COREDUMP_COMM=",
|
||||
- [META_EXE] = "COREDUMP_EXE=",
|
||||
- [META_UNIT] = "COREDUMP_UNIT=",
|
||||
+ [META_ARGV_PID] = "COREDUMP_PID=",
|
||||
+ [META_ARGV_UID] = "COREDUMP_UID=",
|
||||
+ [META_ARGV_GID] = "COREDUMP_GID=",
|
||||
+ [META_ARGV_SIGNAL] = "COREDUMP_SIGNAL=",
|
||||
+ [META_ARGV_TIMESTAMP] = "COREDUMP_TIMESTAMP=",
|
||||
+ [META_ARGV_RLIMIT] = "COREDUMP_RLIMIT=",
|
||||
+ [META_ARGV_HOSTNAME] = "COREDUMP_HOSTNAME=",
|
||||
+ [META_COMM] = "COREDUMP_COMM=",
|
||||
+ [META_EXE] = "COREDUMP_EXE=",
|
||||
+ [META_UNIT] = "COREDUMP_UNIT=",
|
||||
};
|
||||
|
||||
typedef struct Context {
|
||||
@@ -137,9 +137,9 @@ typedef enum CoredumpStorage {
|
||||
} CoredumpStorage;
|
||||
|
||||
static const char* const coredump_storage_table[_COREDUMP_STORAGE_MAX] = {
|
||||
- [COREDUMP_STORAGE_NONE] = "none",
|
||||
+ [COREDUMP_STORAGE_NONE] = "none",
|
||||
[COREDUMP_STORAGE_EXTERNAL] = "external",
|
||||
- [COREDUMP_STORAGE_JOURNAL] = "journal",
|
||||
+ [COREDUMP_STORAGE_JOURNAL] = "journal",
|
||||
};
|
||||
|
||||
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(coredump_storage, CoredumpStorage);
|
||||
@@ -155,13 +155,13 @@ static uint64_t arg_max_use = UINT64_MAX;
|
||||
|
||||
static int parse_config(void) {
|
||||
static const ConfigTableItem items[] = {
|
||||
- { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
|
||||
- { "Coredump", "Compress", config_parse_bool, 0, &arg_compress },
|
||||
- { "Coredump", "ProcessSizeMax", config_parse_iec_uint64, 0, &arg_process_size_max },
|
||||
- { "Coredump", "ExternalSizeMax", config_parse_iec_uint64_infinity, 0, &arg_external_size_max },
|
||||
- { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
|
||||
- { "Coredump", "KeepFree", config_parse_iec_uint64, 0, &arg_keep_free },
|
||||
- { "Coredump", "MaxUse", config_parse_iec_uint64, 0, &arg_max_use },
|
||||
+ { "Coredump", "Storage", config_parse_coredump_storage, 0, &arg_storage },
|
||||
+ { "Coredump", "Compress", config_parse_bool, 0, &arg_compress },
|
||||
+ { "Coredump", "ProcessSizeMax", config_parse_iec_uint64, 0, &arg_process_size_max },
|
||||
+ { "Coredump", "ExternalSizeMax", config_parse_iec_uint64_infinity, 0, &arg_external_size_max },
|
||||
+ { "Coredump", "JournalSizeMax", config_parse_iec_size, 0, &arg_journal_size_max },
|
||||
+ { "Coredump", "KeepFree", config_parse_iec_uint64, 0, &arg_keep_free },
|
||||
+ { "Coredump", "MaxUse", config_parse_iec_uint64, 0, &arg_max_use },
|
||||
{}
|
||||
};
|
||||
|
||||
@@ -207,15 +207,15 @@ static int fix_acl(int fd, uid_t uid) {
|
||||
static int fix_xattr(int fd, const Context *context) {
|
||||
|
||||
static const char * const xattrs[_META_MAX] = {
|
||||
- [META_ARGV_PID] = "user.coredump.pid",
|
||||
- [META_ARGV_UID] = "user.coredump.uid",
|
||||
- [META_ARGV_GID] = "user.coredump.gid",
|
||||
- [META_ARGV_SIGNAL] = "user.coredump.signal",
|
||||
- [META_ARGV_TIMESTAMP] = "user.coredump.timestamp",
|
||||
- [META_ARGV_RLIMIT] = "user.coredump.rlimit",
|
||||
- [META_ARGV_HOSTNAME] = "user.coredump.hostname",
|
||||
- [META_COMM] = "user.coredump.comm",
|
||||
- [META_EXE] = "user.coredump.exe",
|
||||
+ [META_ARGV_PID] = "user.coredump.pid",
|
||||
+ [META_ARGV_UID] = "user.coredump.uid",
|
||||
+ [META_ARGV_GID] = "user.coredump.gid",
|
||||
+ [META_ARGV_SIGNAL] = "user.coredump.signal",
|
||||
+ [META_ARGV_TIMESTAMP] = "user.coredump.timestamp",
|
||||
+ [META_ARGV_RLIMIT] = "user.coredump.rlimit",
|
||||
+ [META_ARGV_HOSTNAME] = "user.coredump.hostname",
|
||||
+ [META_COMM] = "user.coredump.comm",
|
||||
+ [META_EXE] = "user.coredump.exe",
|
||||
};
|
||||
|
||||
int r = 0;
|
@ -0,0 +1,99 @@
|
||||
From f42e15fdfba61fe6dee0bfb0a6a7f44fd9ca9dd3 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
||||
Date: Fri, 7 Oct 2022 12:28:31 +0200
|
||||
Subject: [PATCH] basic: add STRERROR() wrapper for strerror_r()
|
||||
|
||||
(cherry picked from commit 2c5d05b3cd986568105d67891e4010b868dea24f)
|
||||
|
||||
Related: #2155516
|
||||
---
|
||||
src/basic/errno-util.h | 10 +++++++++
|
||||
src/test/meson.build | 2 ++
|
||||
src/test/test-errno-util.c | 44 ++++++++++++++++++++++++++++++++++++++
|
||||
3 files changed, 56 insertions(+)
|
||||
create mode 100644 src/test/test-errno-util.c
|
||||
|
||||
diff --git a/src/basic/errno-util.h b/src/basic/errno-util.h
|
||||
index 09abf0b751..a2d9876c15 100644
|
||||
--- a/src/basic/errno-util.h
|
||||
+++ b/src/basic/errno-util.h
|
||||
@@ -6,6 +6,16 @@
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
+/* strerror(3) says that glibc uses a maximum length of 1024 bytes. */
|
||||
+#define ERRNO_BUF_LEN 1024
|
||||
+
|
||||
+/* Note: the lifetime of the compound literal is the immediately surrounding block,
|
||||
+ * see C11 §6.5.2.5, and
|
||||
+ * https://stackoverflow.com/questions/34880638/compound-literal-lifetime-and-if-blocks
|
||||
+ *
|
||||
+ * Note that we use the GNU variant of strerror_r() here. */
|
||||
+#define STRERROR(errnum) strerror_r(abs(errnum), (char[ERRNO_BUF_LEN]){}, ERRNO_BUF_LEN)
|
||||
+
|
||||
static inline void _reset_errno_(int *saved_errno) {
|
||||
if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */
|
||||
return;
|
||||
diff --git a/src/test/meson.build b/src/test/meson.build
|
||||
index 14b7939b1f..032800dd85 100644
|
||||
--- a/src/test/meson.build
|
||||
+++ b/src/test/meson.build
|
||||
@@ -580,6 +580,8 @@ tests += [
|
||||
[files('test-errno-list.c') +
|
||||
generated_gperf_headers],
|
||||
|
||||
+ [files('test-errno-util.c')],
|
||||
+
|
||||
[files('test-ip-protocol-list.c') +
|
||||
shared_generated_gperf_headers],
|
||||
|
||||
diff --git a/src/test/test-errno-util.c b/src/test/test-errno-util.c
|
||||
new file mode 100644
|
||||
index 0000000000..284f451002
|
||||
--- /dev/null
|
||||
+++ b/src/test/test-errno-util.c
|
||||
@@ -0,0 +1,44 @@
|
||||
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
+
|
||||
+#include "errno-util.h"
|
||||
+#include "stdio-util.h"
|
||||
+#include "string-util.h"
|
||||
+#include "tests.h"
|
||||
+
|
||||
+TEST(strerror_not_threadsafe) {
|
||||
+ /* Just check that strerror really is not thread-safe. */
|
||||
+ log_info("strerror(%d) → %s", 200, strerror(200));
|
||||
+ log_info("strerror(%d) → %s", 201, strerror(201));
|
||||
+ log_info("strerror(%d) → %s", INT_MAX, strerror(INT_MAX));
|
||||
+
|
||||
+ log_info("strerror(%d), strerror(%d) → %p, %p", 200, 201, strerror(200), strerror(201));
|
||||
+
|
||||
+ /* This call is not allowed, because the first returned string becomes invalid when
|
||||
+ * we call strerror the second time:
|
||||
+ *
|
||||
+ * log_info("strerror(%d), strerror(%d) → %s, %s", 200, 201, strerror(200), strerror(201));
|
||||
+ */
|
||||
+}
|
||||
+
|
||||
+TEST(STRERROR) {
|
||||
+ /* Just check that STRERROR really is thread-safe. */
|
||||
+ log_info("STRERROR(%d) → %s", 200, STRERROR(200));
|
||||
+ log_info("STRERROR(%d) → %s", 201, STRERROR(201));
|
||||
+ log_info("STRERROR(%d), STRERROR(%d) → %s, %s", 200, 201, STRERROR(200), STRERROR(201));
|
||||
+
|
||||
+ const char *a = STRERROR(200), *b = STRERROR(201);
|
||||
+ assert_se(strstr(a, "200"));
|
||||
+ assert_se(strstr(b, "201"));
|
||||
+
|
||||
+ /* Check with negative values */
|
||||
+ assert_se(streq(a, STRERROR(-200)));
|
||||
+ assert_se(streq(b, STRERROR(-201)));
|
||||
+
|
||||
+ const char *c = STRERROR(INT_MAX);
|
||||
+ char buf[DECIMAL_STR_MAX(int)];
|
||||
+ xsprintf(buf, "%d", INT_MAX); /* INT_MAX is hexadecimal, use printf to convert to decimal */
|
||||
+ log_info("STRERROR(%d) → %s", INT_MAX, c);
|
||||
+ assert_se(strstr(c, buf));
|
||||
+}
|
||||
+
|
||||
+DEFINE_TEST_MAIN(LOG_INFO);
|
@ -0,0 +1,384 @@
|
||||
From d133e7f71d4530a25971eb99cd1a108f3ede72b9 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
||||
Date: Mon, 28 Nov 2022 12:12:55 +0100
|
||||
Subject: [PATCH] coredump: do not allow user to access coredumps with changed
|
||||
uid/gid/capabilities
|
||||
|
||||
When the user starts a program which elevates its permissions via setuid,
|
||||
setgid, or capabilities set on the file, it may access additional information
|
||||
which would then be visible in the coredump. We shouldn't make the the coredump
|
||||
visible to the user in such cases.
|
||||
|
||||
Reported-by: Matthias Gerstner <mgerstner@suse.de>
|
||||
|
||||
This reads the /proc/<pid>/auxv file and attaches it to the process metadata as
|
||||
PROC_AUXV. Before the coredump is submitted, it is parsed and if either
|
||||
at_secure was set (which the kernel will do for processes that are setuid,
|
||||
setgid, or setcap), or if the effective uid/gid don't match uid/gid, the file
|
||||
is not made accessible to the user. If we can't access this data, we assume the
|
||||
file should not be made accessible either. In principle we could also access
|
||||
the auxv data from a note in the core file, but that is much more complex and
|
||||
it seems better to use the stand-alone file that is provided by the kernel.
|
||||
|
||||
Attaching auxv is both convient for this patch (because this way it's passed
|
||||
between the stages along with other fields), but I think it makes sense to save
|
||||
it in general.
|
||||
|
||||
We use the information early in the core file to figure out if the program was
|
||||
32-bit or 64-bit and its endianness. This way we don't need heuristics to guess
|
||||
whether the format of the auxv structure. This test might reject some cases on
|
||||
fringe architecutes. But the impact would be limited: we just won't grant the
|
||||
user permissions to view the coredump file. If people report that we're missing
|
||||
some cases, we can always enhance this to support more architectures.
|
||||
|
||||
I tested auxv parsing on amd64, 32-bit program on amd64, arm64, arm32, and
|
||||
ppc64el, but not the whole coredump handling.
|
||||
|
||||
(cherry picked from commit 3e4d0f6cf99f8677edd6a237382a65bfe758de03)
|
||||
|
||||
Resolves: #2155516
|
||||
---
|
||||
src/basic/io-util.h | 9 ++
|
||||
src/coredump/coredump.c | 197 +++++++++++++++++++++++++++++++++++++---
|
||||
2 files changed, 193 insertions(+), 13 deletions(-)
|
||||
|
||||
diff --git a/src/basic/io-util.h b/src/basic/io-util.h
|
||||
index 39728e06bc..3afb134266 100644
|
||||
--- a/src/basic/io-util.h
|
||||
+++ b/src/basic/io-util.h
|
||||
@@ -91,7 +91,16 @@ struct iovec_wrapper *iovw_new(void);
|
||||
struct iovec_wrapper *iovw_free(struct iovec_wrapper *iovw);
|
||||
struct iovec_wrapper *iovw_free_free(struct iovec_wrapper *iovw);
|
||||
void iovw_free_contents(struct iovec_wrapper *iovw, bool free_vectors);
|
||||
+
|
||||
int iovw_put(struct iovec_wrapper *iovw, void *data, size_t len);
|
||||
+static inline int iovw_consume(struct iovec_wrapper *iovw, void *data, size_t len) {
|
||||
+ /* Move data into iovw or free on error */
|
||||
+ int r = iovw_put(iovw, data, len);
|
||||
+ if (r < 0)
|
||||
+ free(data);
|
||||
+ return r;
|
||||
+}
|
||||
+
|
||||
int iovw_put_string_field(struct iovec_wrapper *iovw, const char *field, const char *value);
|
||||
int iovw_put_string_field_free(struct iovec_wrapper *iovw, const char *field, char *value);
|
||||
void iovw_rebase(struct iovec_wrapper *iovw, char *old, char *new);
|
||||
diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c
|
||||
index 5f315b9a79..48911522f2 100644
|
||||
--- a/src/coredump/coredump.c
|
||||
+++ b/src/coredump/coredump.c
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/statvfs.h>
|
||||
+#include <sys/auxv.h>
|
||||
#include <sys/xattr.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -105,6 +106,7 @@ enum {
|
||||
|
||||
META_EXE = _META_MANDATORY_MAX,
|
||||
META_UNIT,
|
||||
+ META_PROC_AUXV,
|
||||
_META_MAX
|
||||
};
|
||||
|
||||
@@ -119,10 +121,12 @@ static const char * const meta_field_names[_META_MAX] = {
|
||||
[META_COMM] = "COREDUMP_COMM=",
|
||||
[META_EXE] = "COREDUMP_EXE=",
|
||||
[META_UNIT] = "COREDUMP_UNIT=",
|
||||
+ [META_PROC_AUXV] = "COREDUMP_PROC_AUXV=",
|
||||
};
|
||||
|
||||
typedef struct Context {
|
||||
const char *meta[_META_MAX];
|
||||
+ size_t meta_size[_META_MAX];
|
||||
pid_t pid;
|
||||
bool is_pid1;
|
||||
bool is_journald;
|
||||
@@ -184,13 +188,16 @@ static uint64_t storage_size_max(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int fix_acl(int fd, uid_t uid) {
|
||||
+static int fix_acl(int fd, uid_t uid, bool allow_user) {
|
||||
+ assert(fd >= 0);
|
||||
+ assert(uid_is_valid(uid));
|
||||
|
||||
#if HAVE_ACL
|
||||
int r;
|
||||
|
||||
- assert(fd >= 0);
|
||||
- assert(uid_is_valid(uid));
|
||||
+ /* We don't allow users to read coredumps if the uid or capabilities were changed. */
|
||||
+ if (!allow_user)
|
||||
+ return 0;
|
||||
|
||||
if (uid_is_system(uid) || uid_is_dynamic(uid) || uid == UID_NOBODY)
|
||||
return 0;
|
||||
@@ -250,7 +257,8 @@ static int fix_permissions(
|
||||
const char *filename,
|
||||
const char *target,
|
||||
const Context *context,
|
||||
- uid_t uid) {
|
||||
+ uid_t uid,
|
||||
+ bool allow_user) {
|
||||
|
||||
int r;
|
||||
|
||||
@@ -260,7 +268,7 @@ static int fix_permissions(
|
||||
|
||||
/* Ignore errors on these */
|
||||
(void) fchmod(fd, 0640);
|
||||
- (void) fix_acl(fd, uid);
|
||||
+ (void) fix_acl(fd, uid, allow_user);
|
||||
(void) fix_xattr(fd, context);
|
||||
|
||||
r = fsync_full(fd);
|
||||
@@ -330,6 +338,154 @@ static int make_filename(const Context *context, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int parse_auxv64(
|
||||
+ const uint64_t *auxv,
|
||||
+ size_t size_bytes,
|
||||
+ int *at_secure,
|
||||
+ uid_t *uid,
|
||||
+ uid_t *euid,
|
||||
+ gid_t *gid,
|
||||
+ gid_t *egid) {
|
||||
+
|
||||
+ assert(auxv || size_bytes == 0);
|
||||
+
|
||||
+ if (size_bytes % (2 * sizeof(uint64_t)) != 0)
|
||||
+ return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes);
|
||||
+
|
||||
+ size_t words = size_bytes / sizeof(uint64_t);
|
||||
+
|
||||
+ /* Note that we set output variables even on error. */
|
||||
+
|
||||
+ for (size_t i = 0; i + 1 < words; i += 2)
|
||||
+ switch (auxv[i]) {
|
||||
+ case AT_SECURE:
|
||||
+ *at_secure = auxv[i + 1] != 0;
|
||||
+ break;
|
||||
+ case AT_UID:
|
||||
+ *uid = auxv[i + 1];
|
||||
+ break;
|
||||
+ case AT_EUID:
|
||||
+ *euid = auxv[i + 1];
|
||||
+ break;
|
||||
+ case AT_GID:
|
||||
+ *gid = auxv[i + 1];
|
||||
+ break;
|
||||
+ case AT_EGID:
|
||||
+ *egid = auxv[i + 1];
|
||||
+ break;
|
||||
+ case AT_NULL:
|
||||
+ if (auxv[i + 1] != 0)
|
||||
+ goto error;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ error:
|
||||
+ return log_warning_errno(SYNTHETIC_ERRNO(ENODATA),
|
||||
+ "AT_NULL terminator not found, cannot parse auxv structure.");
|
||||
+}
|
||||
+
|
||||
+static int parse_auxv32(
|
||||
+ const uint32_t *auxv,
|
||||
+ size_t size_bytes,
|
||||
+ int *at_secure,
|
||||
+ uid_t *uid,
|
||||
+ uid_t *euid,
|
||||
+ gid_t *gid,
|
||||
+ gid_t *egid) {
|
||||
+
|
||||
+ assert(auxv || size_bytes == 0);
|
||||
+
|
||||
+ size_t words = size_bytes / sizeof(uint32_t);
|
||||
+
|
||||
+ if (size_bytes % (2 * sizeof(uint32_t)) != 0)
|
||||
+ return log_warning_errno(SYNTHETIC_ERRNO(EIO), "Incomplete auxv structure (%zu bytes).", size_bytes);
|
||||
+
|
||||
+ /* Note that we set output variables even on error. */
|
||||
+
|
||||
+ for (size_t i = 0; i + 1 < words; i += 2)
|
||||
+ switch (auxv[i]) {
|
||||
+ case AT_SECURE:
|
||||
+ *at_secure = auxv[i + 1] != 0;
|
||||
+ break;
|
||||
+ case AT_UID:
|
||||
+ *uid = auxv[i + 1];
|
||||
+ break;
|
||||
+ case AT_EUID:
|
||||
+ *euid = auxv[i + 1];
|
||||
+ break;
|
||||
+ case AT_GID:
|
||||
+ *gid = auxv[i + 1];
|
||||
+ break;
|
||||
+ case AT_EGID:
|
||||
+ *egid = auxv[i + 1];
|
||||
+ break;
|
||||
+ case AT_NULL:
|
||||
+ if (auxv[i + 1] != 0)
|
||||
+ goto error;
|
||||
+ return 0;
|
||||
+ }
|
||||
+ error:
|
||||
+ return log_warning_errno(SYNTHETIC_ERRNO(ENODATA),
|
||||
+ "AT_NULL terminator not found, cannot parse auxv structure.");
|
||||
+}
|
||||
+
|
||||
+static int grant_user_access(int core_fd, const Context *context) {
|
||||
+ int at_secure = -1;
|
||||
+ uid_t uid = UID_INVALID, euid = UID_INVALID;
|
||||
+ uid_t gid = GID_INVALID, egid = GID_INVALID;
|
||||
+ int r;
|
||||
+
|
||||
+ assert(core_fd >= 0);
|
||||
+ assert(context);
|
||||
+
|
||||
+ if (!context->meta[META_PROC_AUXV])
|
||||
+ return log_warning_errno(SYNTHETIC_ERRNO(ENODATA), "No auxv data, not adjusting permissions.");
|
||||
+
|
||||
+ uint8_t elf[EI_NIDENT];
|
||||
+ errno = 0;
|
||||
+ if (pread(core_fd, &elf, sizeof(elf), 0) != sizeof(elf))
|
||||
+ return log_warning_errno(errno > 0 ? -errno : -EIO,
|
||||
+ "Failed to pread from coredump fd: %s",
|
||||
+ errno > 0 ? STRERROR(errno) : "Unexpected EOF");
|
||||
+
|
||||
+ if (elf[EI_MAG0] != ELFMAG0 ||
|
||||
+ elf[EI_MAG1] != ELFMAG1 ||
|
||||
+ elf[EI_MAG2] != ELFMAG2 ||
|
||||
+ elf[EI_MAG3] != ELFMAG3 ||
|
||||
+ elf[EI_VERSION] != EV_CURRENT)
|
||||
+ return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN),
|
||||
+ "Core file does not have ELF header, not adjusting permissions.");
|
||||
+ if (!IN_SET(elf[EI_CLASS], ELFCLASS32, ELFCLASS64) ||
|
||||
+ !IN_SET(elf[EI_DATA], ELFDATA2LSB, ELFDATA2MSB))
|
||||
+ return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN),
|
||||
+ "Core file has strange ELF class, not adjusting permissions.");
|
||||
+
|
||||
+ if ((elf[EI_DATA] == ELFDATA2LSB) != (__BYTE_ORDER == __LITTLE_ENDIAN))
|
||||
+ return log_info_errno(SYNTHETIC_ERRNO(EUCLEAN),
|
||||
+ "Core file has non-native endianness, not adjusting permissions.");
|
||||
+
|
||||
+ if (elf[EI_CLASS] == ELFCLASS64)
|
||||
+ r = parse_auxv64((const uint64_t*) context->meta[META_PROC_AUXV],
|
||||
+ context->meta_size[META_PROC_AUXV],
|
||||
+ &at_secure, &uid, &euid, &gid, &egid);
|
||||
+ else
|
||||
+ r = parse_auxv32((const uint32_t*) context->meta[META_PROC_AUXV],
|
||||
+ context->meta_size[META_PROC_AUXV],
|
||||
+ &at_secure, &uid, &euid, &gid, &egid);
|
||||
+ if (r < 0)
|
||||
+ return r;
|
||||
+
|
||||
+ /* We allow access if we got all the data and at_secure is not set and
|
||||
+ * the uid/gid matches euid/egid. */
|
||||
+ bool ret =
|
||||
+ at_secure == 0 &&
|
||||
+ uid != UID_INVALID && euid != UID_INVALID && uid == euid &&
|
||||
+ gid != GID_INVALID && egid != GID_INVALID && gid == egid;
|
||||
+ log_debug("Will %s access (uid="UID_FMT " euid="UID_FMT " gid="GID_FMT " egid="GID_FMT " at_secure=%s)",
|
||||
+ ret ? "permit" : "restrict",
|
||||
+ uid, euid, gid, egid, yes_no(at_secure));
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int save_external_coredump(
|
||||
const Context *context,
|
||||
int input_fd,
|
||||
@@ -452,6 +608,8 @@ static int save_external_coredump(
|
||||
context->meta[META_ARGV_PID], context->meta[META_COMM]);
|
||||
truncated = r == 1;
|
||||
|
||||
+ bool allow_user = grant_user_access(fd, context) > 0;
|
||||
+
|
||||
#if HAVE_COMPRESSION
|
||||
if (arg_compress) {
|
||||
_cleanup_(unlink_and_freep) char *tmp_compressed = NULL;
|
||||
@@ -489,7 +647,7 @@ static int save_external_coredump(
|
||||
uncompressed_size += partial_uncompressed_size;
|
||||
}
|
||||
|
||||
- r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid);
|
||||
+ r = fix_permissions(fd_compressed, tmp_compressed, fn_compressed, context, uid, allow_user);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -516,7 +674,7 @@ static int save_external_coredump(
|
||||
"SIZE_LIMIT=%zu", max_size,
|
||||
"MESSAGE_ID=" SD_MESSAGE_TRUNCATED_CORE_STR);
|
||||
|
||||
- r = fix_permissions(fd, tmp, fn, context, uid);
|
||||
+ r = fix_permissions(fd, tmp, fn, context, uid, allow_user);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to fix permissions and finalize coredump %s into %s: %m", coredump_tmpfile_name(tmp), fn);
|
||||
|
||||
@@ -764,7 +922,7 @@ static int change_uid_gid(const Context *context) {
|
||||
}
|
||||
|
||||
static int submit_coredump(
|
||||
- Context *context,
|
||||
+ const Context *context,
|
||||
struct iovec_wrapper *iovw,
|
||||
int input_fd) {
|
||||
|
||||
@@ -925,16 +1083,15 @@ static int save_context(Context *context, const struct iovec_wrapper *iovw) {
|
||||
struct iovec *iovec = iovw->iovec + n;
|
||||
|
||||
for (size_t i = 0; i < ELEMENTSOF(meta_field_names); i++) {
|
||||
- char *p;
|
||||
-
|
||||
/* Note that these strings are NUL terminated, because we made sure that a
|
||||
* trailing NUL byte is in the buffer, though not included in the iov_len
|
||||
* count (see process_socket() and gather_pid_metadata_*()) */
|
||||
assert(((char*) iovec->iov_base)[iovec->iov_len] == 0);
|
||||
|
||||
- p = startswith(iovec->iov_base, meta_field_names[i]);
|
||||
+ const char *p = startswith(iovec->iov_base, meta_field_names[i]);
|
||||
if (p) {
|
||||
context->meta[i] = p;
|
||||
+ context->meta_size[i] = iovec->iov_len - strlen(meta_field_names[i]);
|
||||
count++;
|
||||
break;
|
||||
}
|
||||
@@ -1176,6 +1333,7 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) {
|
||||
uid_t owner_uid;
|
||||
pid_t pid;
|
||||
char *t;
|
||||
+ size_t size;
|
||||
const char *p;
|
||||
int r;
|
||||
|
||||
@@ -1240,13 +1398,26 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) {
|
||||
(void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_LIMITS=", t);
|
||||
|
||||
p = procfs_file_alloca(pid, "cgroup");
|
||||
- if (read_full_virtual_file(p, &t, NULL) >=0)
|
||||
+ if (read_full_virtual_file(p, &t, NULL) >= 0)
|
||||
(void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_CGROUP=", t);
|
||||
|
||||
p = procfs_file_alloca(pid, "mountinfo");
|
||||
- if (read_full_virtual_file(p, &t, NULL) >=0)
|
||||
+ if (read_full_virtual_file(p, &t, NULL) >= 0)
|
||||
(void) iovw_put_string_field_free(iovw, "COREDUMP_PROC_MOUNTINFO=", t);
|
||||
|
||||
+ /* We attach /proc/auxv here. ELF coredumps also contain a note for this (NT_AUXV), see elf(5). */
|
||||
+ p = procfs_file_alloca(pid, "auxv");
|
||||
+ if (read_full_virtual_file(p, &t, &size) >= 0) {
|
||||
+ char *buf = malloc(strlen("COREDUMP_PROC_AUXV=") + size + 1);
|
||||
+ if (buf) {
|
||||
+ /* Add a dummy terminator to make save_context() happy. */
|
||||
+ *((uint8_t*) mempcpy(stpcpy(buf, "COREDUMP_PROC_AUXV="), t, size)) = '\0';
|
||||
+ (void) iovw_consume(iovw, buf, size + strlen("COREDUMP_PROC_AUXV="));
|
||||
+ }
|
||||
+
|
||||
+ free(t);
|
||||
+ }
|
||||
+
|
||||
if (get_process_cwd(pid, &t) >= 0)
|
||||
(void) iovw_put_string_field_free(iovw, "COREDUMP_CWD=", t);
|
||||
|
@ -0,0 +1,27 @@
|
||||
From 43ca4cb2cb81e4107710e8a8c48df31d022a1136 Mon Sep 17 00:00:00 2001
|
||||
From: Laura Barcziova <lbarczio@redhat.com>
|
||||
Date: Wed, 9 Mar 2022 07:50:29 +0100
|
||||
Subject: [PATCH] Packit: build SRPMs in Copr
|
||||
|
||||
Add srpm_build_deps key to the Packit config to specify needed dependencies for SRPM build
|
||||
and indicate to build SRPM in Copr.
|
||||
|
||||
(cherry picked from commit d15e1a29e3aab04ee79d5e3ec8e1e65fca78e165)
|
||||
|
||||
Related: #2155516
|
||||
---
|
||||
.packit.yml | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/.packit.yml b/.packit.yml
|
||||
index ce8782aae2..e1cbea3b3a 100644
|
||||
--- a/.packit.yml
|
||||
+++ b/.packit.yml
|
||||
@@ -13,6 +13,7 @@ downstream_package_name: systemd
|
||||
# `git describe` returns in systemd's case 'v245-xxx' which breaks RPM version
|
||||
# detection (that expects 245-xxxx'). Let's tweak the version string accordingly
|
||||
upstream_tag_template: "v{version}"
|
||||
+srpm_build_deps: []
|
||||
|
||||
actions:
|
||||
post-upstream-clone:
|
@ -0,0 +1,27 @@
|
||||
From 1e034561de36b0eda6356b15b5433bf8571d4067 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Watanabe <watanabe.yu+github@gmail.com>
|
||||
Date: Sun, 30 Oct 2022 11:59:10 +0900
|
||||
Subject: [PATCH] test: support non-summer time
|
||||
|
||||
Follow-up for 759ed0a2533da8840dea315d07f92e6bb0272cdd.
|
||||
|
||||
(cherry picked from commit 59ab79a73d030a49bfdffd85897b6b30a2b132c5)
|
||||
|
||||
Related: #2155516
|
||||
---
|
||||
test/units/testsuite-45.sh | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/test/units/testsuite-45.sh b/test/units/testsuite-45.sh
|
||||
index 75e07bc09a..38017a43ea 100755
|
||||
--- a/test/units/testsuite-45.sh
|
||||
+++ b/test/units/testsuite-45.sh
|
||||
@@ -39,7 +39,7 @@ test_timezone() {
|
||||
if [[ -f /etc/timezone ]]; then
|
||||
assert_eq "$(cat /etc/timezone)" "Europe/Kiev"
|
||||
fi
|
||||
- assert_in "Time zone: Europe/Kiev \(EEST, \+0[0-9]00\)" "$(timedatectl)"
|
||||
+ assert_in "Time zone: Europe/Kiev \(EES*T, \+0[0-9]00\)" "$(timedatectl)"
|
||||
|
||||
if [[ -n "$ORIG_TZ" ]]; then
|
||||
echo 'reset timezone to original'
|
@ -0,0 +1,42 @@
|
||||
From c518597dddb9b8a0b8d895b28ebb4792c8a32c6e Mon Sep 17 00:00:00 2001
|
||||
From: Frantisek Sumsal <frantisek@sumsal.cz>
|
||||
Date: Thu, 25 Aug 2022 14:52:30 +0200
|
||||
Subject: [PATCH] test: bump the base VM memory to 768M
|
||||
|
||||
as with 512M some tests occasionally trip off OOM-killer (e.g.
|
||||
TEST-64 + multipath).
|
||||
|
||||
(cherry picked from commit 6a9c4977683a30fcd36baf64e35255e9846028c6)
|
||||
|
||||
Related: #2155516
|
||||
---
|
||||
test/TEST-36-NUMAPOLICY/test.sh | 2 +-
|
||||
test/test-functions | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/test/TEST-36-NUMAPOLICY/test.sh b/test/TEST-36-NUMAPOLICY/test.sh
|
||||
index 7909b1dce3..a0bfd4079a 100755
|
||||
--- a/test/TEST-36-NUMAPOLICY/test.sh
|
||||
+++ b/test/TEST-36-NUMAPOLICY/test.sh
|
||||
@@ -9,7 +9,7 @@ TEST_NO_NSPAWN=1
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
||||
|
||||
if qemu_min_version "5.2.0"; then
|
||||
- QEMU_OPTIONS="-object memory-backend-ram,id=mem0,size=${QEMU_MEM:-512M} -numa node,memdev=mem0,nodeid=0"
|
||||
+ QEMU_OPTIONS="-object memory-backend-ram,id=mem0,size=${QEMU_MEM:-768M} -numa node,memdev=mem0,nodeid=0"
|
||||
else
|
||||
QEMU_OPTIONS="-numa node,nodeid=0"
|
||||
fi
|
||||
diff --git a/test/test-functions b/test/test-functions
|
||||
index 8ea2f97b71..f9a80884e8 100644
|
||||
--- a/test/test-functions
|
||||
+++ b/test/test-functions
|
||||
@@ -450,7 +450,7 @@ run_qemu() {
|
||||
qemu_options+=(
|
||||
-smp "$QEMU_SMP"
|
||||
-net none
|
||||
- -m "${QEMU_MEM:-512M}"
|
||||
+ -m "${QEMU_MEM:-768M}"
|
||||
-nographic
|
||||
-kernel "$KERNEL_BIN"
|
||||
-drive "format=raw,cache=unsafe,file=$image"
|
@ -0,0 +1,42 @@
|
||||
From 58b82abb17fd5c40b5c990a36c43ad558ef3cae1 Mon Sep 17 00:00:00 2001
|
||||
From: Frantisek Sumsal <frantisek@sumsal.cz>
|
||||
Date: Tue, 20 Sep 2022 19:12:18 +0200
|
||||
Subject: [PATCH] test: don't overwrite existing $QEMU_OPTIONS
|
||||
|
||||
(cherry picked from commit 761b1d83145a6f9f41ad9aafcb5f28d452582864)
|
||||
|
||||
Related: #2155516
|
||||
---
|
||||
test/TEST-36-NUMAPOLICY/test.sh | 4 ++--
|
||||
test/TEST-53-ISSUE-16347/test.sh | 2 +-
|
||||
2 files changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/test/TEST-36-NUMAPOLICY/test.sh b/test/TEST-36-NUMAPOLICY/test.sh
|
||||
index a0bfd4079a..5c39b7c6d8 100755
|
||||
--- a/test/TEST-36-NUMAPOLICY/test.sh
|
||||
+++ b/test/TEST-36-NUMAPOLICY/test.sh
|
||||
@@ -9,9 +9,9 @@ TEST_NO_NSPAWN=1
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
||||
|
||||
if qemu_min_version "5.2.0"; then
|
||||
- QEMU_OPTIONS="-object memory-backend-ram,id=mem0,size=${QEMU_MEM:-768M} -numa node,memdev=mem0,nodeid=0"
|
||||
+ QEMU_OPTIONS+=" -object memory-backend-ram,id=mem0,size=${QEMU_MEM:-768M} -numa node,memdev=mem0,nodeid=0"
|
||||
else
|
||||
- QEMU_OPTIONS="-numa node,nodeid=0"
|
||||
+ QEMU_OPTIONS+=" -numa node,nodeid=0"
|
||||
fi
|
||||
|
||||
do_test "$@"
|
||||
diff --git a/test/TEST-53-ISSUE-16347/test.sh b/test/TEST-53-ISSUE-16347/test.sh
|
||||
index 7f44c66bff..6d4821d2c1 100755
|
||||
--- a/test/TEST-53-ISSUE-16347/test.sh
|
||||
+++ b/test/TEST-53-ISSUE-16347/test.sh
|
||||
@@ -5,7 +5,7 @@ set -e
|
||||
TEST_DESCRIPTION="test timer units when initial clock is ahead"
|
||||
TEST_NO_NSPAWN=1
|
||||
|
||||
-QEMU_OPTIONS="-rtc base=$(date -u +%Y-%m-%dT%H:%M:%S -d '+3 days')"
|
||||
+QEMU_OPTIONS+=" -rtc base=$(date -u +%Y-%m-%dT%H:%M:%S -d '+3 days')"
|
||||
|
||||
# shellcheck source=test/test-functions
|
||||
. "${TEST_BASE_DIR:?}/test-functions"
|
@ -0,0 +1,52 @@
|
||||
From 022cb8bb2028571b9119fd4ae95c87c96f816d6c Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
||||
Date: Tue, 18 Oct 2022 18:09:06 +0200
|
||||
Subject: [PATCH] shared/json: allow json_variant_dump() to return an error
|
||||
|
||||
(cherry picked from commit 7922ead507e0d83e4ec72a8cbd2b67194766e58c)
|
||||
|
||||
Related: #2149074
|
||||
---
|
||||
src/shared/json.c | 7 ++++---
|
||||
src/shared/json.h | 2 +-
|
||||
2 files changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/src/shared/json.c b/src/shared/json.c
|
||||
index bcc109abc2..f91738227b 100644
|
||||
--- a/src/shared/json.c
|
||||
+++ b/src/shared/json.c
|
||||
@@ -1768,9 +1768,9 @@ int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
|
||||
return (int) sz - 1;
|
||||
}
|
||||
|
||||
-void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix) {
|
||||
+int json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix) {
|
||||
if (!v)
|
||||
- return;
|
||||
+ return 0;
|
||||
|
||||
if (!f)
|
||||
f = stdout;
|
||||
@@ -1796,7 +1796,8 @@ void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const cha
|
||||
fputc('\n', f); /* In case of SSE add a second newline */
|
||||
|
||||
if (flags & JSON_FORMAT_FLUSH)
|
||||
- fflush(f);
|
||||
+ return fflush_and_check(f);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
int json_variant_filter(JsonVariant **v, char **to_remove) {
|
||||
diff --git a/src/shared/json.h b/src/shared/json.h
|
||||
index dd73c1e497..e4bfeae8f5 100644
|
||||
--- a/src/shared/json.h
|
||||
+++ b/src/shared/json.h
|
||||
@@ -195,7 +195,7 @@ typedef enum JsonFormatFlags {
|
||||
} JsonFormatFlags;
|
||||
|
||||
int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
|
||||
-void json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix);
|
||||
+int json_variant_dump(JsonVariant *v, JsonFormatFlags flags, FILE *f, const char *prefix);
|
||||
|
||||
int json_variant_filter(JsonVariant **v, char **to_remove);
|
||||
|
@ -0,0 +1,89 @@
|
||||
From ffed186cce8e5a20187f6f652be94e3135b74eed Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
||||
Date: Wed, 19 Oct 2022 08:41:13 +0200
|
||||
Subject: [PATCH] shared/json: use different return code for empty input
|
||||
|
||||
It is useful to distinguish if json_parse_file() got no input or invalid input.
|
||||
Use different return codes for the two cases.
|
||||
|
||||
(cherry picked from commit 87a16eb8b54002a49f12944fc09ce45d0cbadf45)
|
||||
|
||||
Related: #2149074
|
||||
---
|
||||
src/shared/elf-util.c | 2 +-
|
||||
src/shared/json.c | 6 ++++--
|
||||
src/test/test-json.c | 18 ++++++++++++++++++
|
||||
3 files changed, 23 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c
|
||||
index 6d9fcfbbf2..392ed9f31b 100644
|
||||
--- a/src/shared/elf-util.c
|
||||
+++ b/src/shared/elf-util.c
|
||||
@@ -800,7 +800,7 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha
|
||||
return -errno;
|
||||
|
||||
r = json_parse_file(json_in, NULL, 0, &package_metadata, NULL, NULL);
|
||||
- if (r < 0 && r != -EINVAL) /* EINVAL: json was empty, so we got nothing, but that's ok */
|
||||
+ if (r < 0 && r != -ENODATA) /* ENODATA: json was empty, so we got nothing, but that's ok */
|
||||
return r;
|
||||
}
|
||||
|
||||
diff --git a/src/shared/json.c b/src/shared/json.c
|
||||
index f91738227b..70e46fabb1 100644
|
||||
--- a/src/shared/json.c
|
||||
+++ b/src/shared/json.c
|
||||
@@ -3170,7 +3170,6 @@ int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret,
|
||||
int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) {
|
||||
_cleanup_(json_source_unrefp) JsonSource *source = NULL;
|
||||
_cleanup_free_ char *text = NULL;
|
||||
- const char *p;
|
||||
int r;
|
||||
|
||||
if (f)
|
||||
@@ -3182,13 +3181,16 @@ int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags fla
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
+ if (isempty(text))
|
||||
+ return -ENODATA;
|
||||
+
|
||||
if (path) {
|
||||
source = json_source_new(path);
|
||||
if (!source)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
- p = text;
|
||||
+ const char *p = text;
|
||||
return json_parse_internal(&p, source, flags, ret, ret_line, ret_column, false);
|
||||
}
|
||||
|
||||
diff --git a/src/test/test-json.c b/src/test/test-json.c
|
||||
index b385edc269..2256492fb2 100644
|
||||
--- a/src/test/test-json.c
|
||||
+++ b/src/test/test-json.c
|
||||
@@ -346,6 +346,24 @@ TEST(build) {
|
||||
assert_se(json_variant_equal(a, b));
|
||||
}
|
||||
|
||||
+TEST(json_parse_file_empty) {
|
||||
+ _cleanup_fclose_ FILE *f = NULL;
|
||||
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
+
|
||||
+ assert_se(fopen_unlocked("/dev/null", "re", &f) >= 0);
|
||||
+ assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) == -ENODATA);
|
||||
+ assert_se(v == NULL);
|
||||
+}
|
||||
+
|
||||
+TEST(json_parse_file_invalid) {
|
||||
+ _cleanup_fclose_ FILE *f = NULL;
|
||||
+ _cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
+
|
||||
+ assert_se(f = fmemopen_unlocked((void*) "kookoo", 6, "r"));
|
||||
+ assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) == -EINVAL);
|
||||
+ assert_se(v == NULL);
|
||||
+}
|
||||
+
|
||||
TEST(source) {
|
||||
static const char data[] =
|
||||
"\n"
|
@ -0,0 +1,121 @@
|
||||
From 35a233228ff0105892b3edc86a0cdda06282a9ac Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
|
||||
Date: Tue, 18 Oct 2022 18:23:53 +0200
|
||||
Subject: [PATCH] coredump: avoid deadlock when passing processed backtrace
|
||||
data
|
||||
|
||||
We would deadlock when passing the data back from the forked-off process that
|
||||
was doing backtrace generation back to the coredump parent. This is because we
|
||||
fork the child and wait for it to exit. The child tries to write too much data
|
||||
to the output pipe, and and after the first 64k blocks on the parent because
|
||||
the pipe is full. The bug surfaced in Fedora because of a combination of four
|
||||
factors:
|
||||
- 87707784c70dc9894ec613df0a6e75e732a362a3 was backported to v251.5, which
|
||||
allowed coredump processing to be successful.
|
||||
- 1a0281a3ebf4f8c16d40aa9e63103f16cd23bb2a was NOT backported, so the output
|
||||
was very verbose.
|
||||
- Fedora has the ELF package metadata available, so a lot of output can be
|
||||
generated. Most other distros just don't have the information.
|
||||
- gnome-calendar crashes and has a bazillion modules and 69596 bytes of output
|
||||
are generated for it.
|
||||
|
||||
Fixes https://bugzilla.redhat.com/show_bug.cgi?id=2135778.
|
||||
|
||||
The code is changed to try to write data opportunistically. If we get partial
|
||||
information, that is still logged. In is generally better to log partial
|
||||
backtrace information than nothing at all.
|
||||
|
||||
(cherry picked from commit 076b807be472630692c5348c60d0c2b7b28ad437)
|
||||
|
||||
Resolves: #2149074
|
||||
---
|
||||
src/shared/elf-util.c | 37 +++++++++++++++++++++++++++++++------
|
||||
1 file changed, 31 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/src/shared/elf-util.c b/src/shared/elf-util.c
|
||||
index 392ed9f31b..644fbae9ce 100644
|
||||
--- a/src/shared/elf-util.c
|
||||
+++ b/src/shared/elf-util.c
|
||||
@@ -30,6 +30,9 @@
|
||||
#define THREADS_MAX 64
|
||||
#define ELF_PACKAGE_METADATA_ID 0xcafe1a7e
|
||||
|
||||
+/* The amount of data we're willing to write to each of the output pipes. */
|
||||
+#define COREDUMP_PIPE_MAX (1024*1024U)
|
||||
+
|
||||
static void *dw_dl = NULL;
|
||||
static void *elf_dl = NULL;
|
||||
|
||||
@@ -700,13 +703,13 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha
|
||||
return r;
|
||||
|
||||
if (ret) {
|
||||
- r = RET_NERRNO(pipe2(return_pipe, O_CLOEXEC));
|
||||
+ r = RET_NERRNO(pipe2(return_pipe, O_CLOEXEC|O_NONBLOCK));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_package_metadata) {
|
||||
- r = RET_NERRNO(pipe2(json_pipe, O_CLOEXEC));
|
||||
+ r = RET_NERRNO(pipe2(json_pipe, O_CLOEXEC|O_NONBLOCK));
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
@@ -750,8 +753,24 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha
|
||||
goto child_fail;
|
||||
|
||||
if (buf) {
|
||||
- r = loop_write(return_pipe[1], buf, strlen(buf), false);
|
||||
- if (r < 0)
|
||||
+ size_t len = strlen(buf);
|
||||
+
|
||||
+ if (len > COREDUMP_PIPE_MAX) {
|
||||
+ /* This is iffy. A backtrace can be a few hundred kilobytes, but too much is
|
||||
+ * too much. Let's log a warning and ignore the rest. */
|
||||
+ log_warning("Generated backtrace is %zu bytes (more than the limit of %u bytes), backtrace will be truncated.",
|
||||
+ len, COREDUMP_PIPE_MAX);
|
||||
+ len = COREDUMP_PIPE_MAX;
|
||||
+ }
|
||||
+
|
||||
+ /* Bump the space for the returned string.
|
||||
+ * Failure is ignored, because partial output is still useful. */
|
||||
+ (void) fcntl(return_pipe[1], F_SETPIPE_SZ, len);
|
||||
+
|
||||
+ r = loop_write(return_pipe[1], buf, len, false);
|
||||
+ if (r == -EAGAIN)
|
||||
+ log_warning("Write failed, backtrace will be truncated.");
|
||||
+ else if (r < 0)
|
||||
goto child_fail;
|
||||
|
||||
return_pipe[1] = safe_close(return_pipe[1]);
|
||||
@@ -760,13 +779,19 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha
|
||||
if (package_metadata) {
|
||||
_cleanup_fclose_ FILE *json_out = NULL;
|
||||
|
||||
+ /* Bump the space for the returned string. We don't know how much space we'll need in
|
||||
+ * advance, so we'll just try to write as much as possible and maybe fail later. */
|
||||
+ (void) fcntl(json_pipe[1], F_SETPIPE_SZ, COREDUMP_PIPE_MAX);
|
||||
+
|
||||
json_out = take_fdopen(&json_pipe[1], "w");
|
||||
if (!json_out) {
|
||||
r = -errno;
|
||||
goto child_fail;
|
||||
}
|
||||
|
||||
- json_variant_dump(package_metadata, JSON_FORMAT_FLUSH, json_out, NULL);
|
||||
+ r = json_variant_dump(package_metadata, JSON_FORMAT_FLUSH, json_out, NULL);
|
||||
+ if (r < 0)
|
||||
+ log_warning_errno(r, "Failed to write JSON package metadata, ignoring: %m");
|
||||
}
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
@@ -801,7 +826,7 @@ int parse_elf_object(int fd, const char *executable, bool fork_disable_dump, cha
|
||||
|
||||
r = json_parse_file(json_in, NULL, 0, &package_metadata, NULL, NULL);
|
||||
if (r < 0 && r != -ENODATA) /* ENODATA: json was empty, so we got nothing, but that's ok */
|
||||
- return r;
|
||||
+ log_warning_errno(r, "Failed to read or parse json metadata, ignoring: %m");
|
||||
}
|
||||
|
||||
if (ret)
|
@ -0,0 +1,34 @@
|
||||
From a71653b20b889a71c6643171603e65b0ca945ea4 Mon Sep 17 00:00:00 2001
|
||||
From: Frantisek Sumsal <fsumsal@redhat.com>
|
||||
Date: Wed, 30 Nov 2022 15:24:33 +0100
|
||||
Subject: [PATCH] test: disable flaky subtests that require udevadm wait/lock
|
||||
|
||||
Certain TEST-64-UDEV-STORAGE tests require `udevadm wait` and `udevadm
|
||||
lock` verbs to work reliably. Since we don't plan to backport the verbs
|
||||
to RHEL 9.1 and older, let's skip the tests to make CIs a bit more
|
||||
reliable when dealing with z-streams.
|
||||
|
||||
Necessary only on RHEL 9.1 and 9.0.
|
||||
|
||||
rhel-only
|
||||
Related: #2149074
|
||||
---
|
||||
test/TEST-64-UDEV-STORAGE/test.sh | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
diff --git a/test/TEST-64-UDEV-STORAGE/test.sh b/test/TEST-64-UDEV-STORAGE/test.sh
|
||||
index 2a5b5b2dd3..659b03e07b 100755
|
||||
--- a/test/TEST-64-UDEV-STORAGE/test.sh
|
||||
+++ b/test/TEST-64-UDEV-STORAGE/test.sh
|
||||
@@ -310,6 +310,11 @@ EOF
|
||||
|
||||
# Test case for issue https://github.com/systemd/systemd/issues/19946
|
||||
testcase_simultaneous_events() {
|
||||
+ if ! "$BUILD_DIR/udevadm" lock --version >/dev/null; then
|
||||
+ echo "This test is flaky without udevadm wait/lock, skipping the test..."
|
||||
+ return 77
|
||||
+ fi
|
||||
+
|
||||
local qemu_opts=("-device virtio-scsi-pci,id=scsi")
|
||||
local partdisk="${TESTDIR:?}/simultaneousevents.img"
|
||||
|
Loading…
Reference in new issue