]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/coredump/coredump.c
Merge pull request #25789 from yuwata/EBADF
[thirdparty/systemd.git] / src / coredump / coredump.c
index b270ba39e099724e7b7bb87ca086f2cb91a4fa80..192dc4c654b044dbcba6cc2740c1a5e952b5629c 100644 (file)
@@ -4,14 +4,10 @@
 #include <stdio.h>
 #include <sys/prctl.h>
 #include <sys/statvfs.h>
+#include <sys/auxv.h>
 #include <sys/xattr.h>
 #include <unistd.h>
 
-#if HAVE_ELFUTILS
-#include <dwarf.h>
-#include <elfutils/libdwfl.h>
-#endif
-
 #include "sd-daemon.h"
 #include "sd-journal.h"
 #include "sd-login.h"
 #include "compress.h"
 #include "conf-parser.h"
 #include "copy.h"
+#include "coredump-util.h"
 #include "coredump-vacuum.h"
 #include "dirent-util.h"
+#include "elf-util.h"
 #include "escape.h"
 #include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
 #include "io-util.h"
 #include "journal-importer.h"
+#include "journal-send.h"
 #include "log.h"
 #include "macro.h"
 #include "main-func.h"
 #include "memory-util.h"
-#include "mkdir.h"
+#include "mkdir-label.h"
 #include "parse-util.h"
 #include "process-util.h"
 #include "signal-util.h"
 #include "socket-util.h"
 #include "special.h"
-#include "stacktrace.h"
 #include "stat-util.h"
 #include "string-table.h"
 #include "string-util.h"
 #include "uid-alloc-range.h"
 #include "user-util.h"
 
-/* The maximum size up to which we process coredumps */
-#define PROCESS_SIZE_MAX ((uint64_t) (2LLU*1024LLU*1024LLU*1024LLU))
+/* The maximum size up to which we process coredumps. We use 1G on 32bit systems, and 32G on 64bit systems */
+#if __SIZEOF_POINTER__ == 4
+#define PROCESS_SIZE_MAX ((uint64_t) (1LLU*1024LLU*1024LLU*1024LLU))
+#elif __SIZEOF_POINTER__ == 8
+#define PROCESS_SIZE_MAX ((uint64_t) (32LLU*1024LLU*1024LLU*1024LLU))
+#else
+#error "Unexpected pointer size"
+#endif
 
 /* The maximum size up to which we leave the coredump around on disk */
 #define EXTERNAL_SIZE_MAX PROCESS_SIZE_MAX
@@ -104,24 +108,27 @@ enum {
 
         META_EXE = _META_MANDATORY_MAX,
         META_UNIT,
+        META_PROC_AUXV,
         _META_MAX
 };
 
 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=",
+        [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;
@@ -136,9 +143,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);
@@ -154,13 +161,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,        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           },
                 {}
         };
 
@@ -183,13 +190,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;
@@ -206,15 +216,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;
@@ -249,7 +259,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;
 
@@ -259,7 +270,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);
@@ -329,6 +340,153 @@ 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_or_else(EIO),
+                                         "Failed to pread from coredump fd: %s", STRERROR_OR_EOF(errno));
+
+        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,
@@ -341,7 +499,7 @@ static int save_external_coredump(
 
         _cleanup_(unlink_and_freep) char *tmp = NULL;
         _cleanup_free_ char *fn = NULL;
-        _cleanup_close_ int fd = -1;
+        _cleanup_close_ int fd = -EBADF;
         uint64_t rlimit, process_limit, max_size;
         bool truncated, storage_on_tmpfs;
         struct stat st;
@@ -451,17 +609,19 @@ 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;
                 _cleanup_free_ char *fn_compressed = NULL;
-                _cleanup_close_ int fd_compressed = -1;
+                _cleanup_close_ int fd_compressed = -EBADF;
                 uint64_t uncompressed_size = 0;
 
                 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
                         return log_error_errno(errno, "Failed to seek on coredump %s: %m", fn);
 
-                fn_compressed = strjoin(fn, COMPRESSED_EXT);
+                fn_compressed = strjoin(fn, default_compression_extension());
                 if (!fn_compressed)
                         return log_oom();
 
@@ -488,7 +648,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;
 
@@ -511,11 +671,11 @@ static int save_external_coredump(
 
         if (truncated)
                 log_struct(LOG_INFO,
-                           LOG_MESSAGE("Core file was truncated to %zu bytes.", max_size),
-                           "SIZE_LIMIT=%zu", max_size,
+                           LOG_MESSAGE("Core file was truncated to %"PRIu64" bytes.", max_size),
+                           "SIZE_LIMIT=%"PRIu64, 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);
 
@@ -581,11 +741,10 @@ static int allocate_journal_field(int fd, size_t size, char **ret, size_t *ret_s
  */
 static int compose_open_fds(pid_t pid, char **open_fds) {
         _cleanup_closedir_ DIR *proc_fd_dir = NULL;
-        _cleanup_close_ int proc_fdinfo_fd = -1;
+        _cleanup_close_ int proc_fdinfo_fd = -EBADF;
         _cleanup_free_ char *buffer = NULL;
         _cleanup_fclose_ FILE *stream = NULL;
         const char *fddelim = "", *path;
-        struct dirent *dent = NULL;
         size_t size = 0;
         int r;
 
@@ -605,20 +764,20 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
         if (!stream)
                 return -ENOMEM;
 
-        FOREACH_DIRENT(dent, proc_fd_dir, return -errno) {
+        FOREACH_DIRENT(de, proc_fd_dir, return -errno) {
                 _cleanup_fclose_ FILE *fdinfo = NULL;
                 _cleanup_free_ char *fdname = NULL;
-                _cleanup_close_ int fd = -1;
+                _cleanup_close_ int fd = -EBADF;
 
-                r = readlinkat_malloc(dirfd(proc_fd_dir), dent->d_name, &fdname);
+                r = readlinkat_malloc(dirfd(proc_fd_dir), de->d_name, &fdname);
                 if (r < 0)
                         return r;
 
-                fprintf(stream, "%s%s:%s\n", fddelim, dent->d_name, fdname);
+                fprintf(stream, "%s%s:%s\n", fddelim, de->d_name, fdname);
                 fddelim = "\n";
 
                 /* Use the directory entry from /proc/[pid]/fd with /proc/[pid]/fdinfo */
-                fd = openat(proc_fdinfo_fd, dent->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY);
+                fd = openat(proc_fdinfo_fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_RDONLY);
                 if (fd < 0)
                         continue;
 
@@ -654,7 +813,7 @@ static int compose_open_fds(pid_t pid, char **open_fds) {
 static int get_process_ns(pid_t pid, const char *namespace, ino_t *ns) {
         const char *p;
         struct stat stbuf;
-        _cleanup_close_ int proc_ns_dir_fd = -1;
+        _cleanup_close_ int proc_ns_dir_fd = -EBADF;
 
         p = procfs_file_alloca(pid, "ns");
 
@@ -707,10 +866,10 @@ static int get_mount_namespace_leader(pid_t pid, pid_t *ret) {
  * Returns a negative number on errors.
  */
 static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) {
-        int r = 0;
         pid_t container_pid;
         const char *proc_root_path;
         struct stat root_stat, proc_root_stat;
+        int r;
 
         /* To compare inodes of / and /proc/[pid]/root */
         if (stat("/", &root_stat) < 0)
@@ -721,7 +880,7 @@ static int get_process_container_parent_cmdline(pid_t pid, char** cmdline) {
                 return -errno;
 
         /* The process uses system root. */
-        if (proc_root_stat.st_ino == root_stat.st_ino) {
+        if (stat_inode_same(&proc_root_stat, &root_stat)) {
                 *cmdline = NULL;
                 return 0;
         }
@@ -764,12 +923,12 @@ static int change_uid_gid(const Context *context) {
 }
 
 static int submit_coredump(
-                Context *context,
+                const Context *context,
                 struct iovec_wrapper *iovw,
                 int input_fd) {
 
         _cleanup_(json_variant_unrefp) JsonVariant *json_metadata = NULL;
-        _cleanup_close_ int coredump_fd = -1, coredump_node_fd = -1;
+        _cleanup_close_ int coredump_fd = -EBADF, coredump_node_fd = -EBADF;
         _cleanup_free_ char *filename = NULL, *coredump_data = NULL;
         _cleanup_free_ char *stacktrace = NULL;
         char *core_message;
@@ -778,6 +937,7 @@ static int submit_coredump(
         bool truncated = false;
         JsonVariant *module_json;
         int r;
+
         assert(context);
         assert(iovw);
         assert(input_fd >= 0);
@@ -799,10 +959,9 @@ static int submit_coredump(
         r = maybe_remove_external_coredump(filename, coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size);
         if (r < 0)
                 return r;
-        if (r == 0) {
+        if (r == 0)
                 (void) iovw_put_string_field(iovw, "COREDUMP_FILENAME=", filename);
-
-        } else if (arg_storage == COREDUMP_STORAGE_EXTERNAL)
+        else if (arg_storage == COREDUMP_STORAGE_EXTERNAL)
                 log_info("The core will not be stored: size %"PRIu64" is greater than %"PRIu64" (the configured maximum)",
                          coredump_node_fd >= 0 ? coredump_compressed_size : coredump_size, arg_external_size_max);
 
@@ -818,15 +977,20 @@ static int submit_coredump(
         if (r < 0)
                 return log_error_errno(r, "Failed to drop privileges: %m");
 
-#if HAVE_ELFUTILS
         /* Try to get a stack trace if we can */
-        if (coredump_size > arg_process_size_max) {
+        if (coredump_size > arg_process_size_max)
                 log_debug("Not generating stack trace: core size %"PRIu64" is greater "
                           "than %"PRIu64" (the configured maximum)",
                           coredump_size, arg_process_size_max);
-        } else if (coredump_fd >= 0)
-                coredump_parse_core(coredump_fd, context->meta[META_EXE], &stacktrace, &json_metadata);
-#endif
+        else if (coredump_fd >= 0) {
+                bool skip = startswith(context->meta[META_COMM], "systemd-coredum"); /* COMM is 16 bytes usually */
+
+                (void) parse_elf_object(coredump_fd,
+                                        context->meta[META_EXE],
+                                        /* fork_disable_dump= */ skip, /* avoid loops */
+                                        &stacktrace,
+                                        &json_metadata);
+        }
 
 log:
         core_message = strjoina("Process ", context->meta[META_ARGV_PID],
@@ -837,12 +1001,10 @@ log:
 
         core_message = strjoina(core_message, stacktrace ? "\n\n" : NULL, stacktrace);
 
-        if (context->is_journald) {
-                /* We cannot log to the journal, so just print the message.
-                 * The target was set previously to something safe. */
+        if (context->is_journald)
+                /* We might not be able to log to the journal, so let's always print the message to another
+                 * log target. The target was set previously to something safe. */
                 log_dispatch(LOG_ERR, 0, core_message);
-                return 0;
-        }
 
         (void) iovw_put_string_field(iovw, "MESSAGE=", core_message);
 
@@ -861,21 +1023,24 @@ log:
                 (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_JSON=", formatted_json);
         }
 
-        JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, json_metadata) {
-                JsonVariant *package_name, *package_version;
+        /* In the unlikely scenario that context->meta[META_EXE] is not available,
+         * let's avoid guessing the module name and skip the loop. */
+        if (context->meta[META_EXE])
+                JSON_VARIANT_OBJECT_FOREACH(module_name, module_json, json_metadata) {
+                        JsonVariant *t;
 
-                /* We only add structured fields for the 'main' ELF module */
-                if (!path_equal_filename(module_name, context->meta[META_EXE]))
-                        continue;
+                        /* We only add structured fields for the 'main' ELF module, and only if we can identify it. */
+                        if (!path_equal_filename(module_name, context->meta[META_EXE]))
+                                continue;
 
-                package_name = json_variant_by_key(module_json, "name");
-                if (package_name)
-                        (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_NAME=", json_variant_string(package_name));
+                        t = json_variant_by_key(module_json, "name");
+                        if (t)
+                                (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_NAME=", json_variant_string(t));
 
-                package_version = json_variant_by_key(module_json, "version");
-                if (package_version)
-                        (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_VERSION=", json_variant_string(package_version));
-        }
+                        t = json_variant_by_key(module_json, "version");
+                        if (t)
+                                (void) iovw_put_string_field(iovw, "COREDUMP_PACKAGE_VERSION=", json_variant_string(t));
+                }
 
         /* Optionally store the entire coredump in the journal */
         if (arg_storage == COREDUMP_STORAGE_JOURNAL && coredump_fd >= 0) {
@@ -895,15 +1060,35 @@ log:
                                  coredump_size, arg_journal_size_max);
         }
 
+        /* If journald is coredumping, we have to be careful that we don't deadlock when trying to write the
+         * coredump to the journal, so we put the journal socket in nonblocking mode before trying to write
+         * the coredump to the socket. */
+
+        if (context->is_journald) {
+                r = journal_fd_nonblock(true);
+                if (r < 0)
+                        return log_error_errno(r, "Failed to make journal socket non-blocking: %m");
+        }
+
         r = sd_journal_sendv(iovw->iovec, iovw->count);
-        if (r < 0)
+
+        if (context->is_journald) {
+                int k;
+
+                k = journal_fd_nonblock(false);
+                if (k < 0)
+                        return log_error_errno(k, "Failed to make journal socket blocking: %m");
+        }
+
+        if (r == -EAGAIN && context->is_journald)
+                log_warning_errno(r, "Failed to log journal coredump, ignoring: %m");
+        else if (r < 0)
                 return log_error_errno(r, "Failed to log coredump: %m");
 
         return 0;
 }
 
 static int save_context(Context *context, const struct iovec_wrapper *iovw) {
-        unsigned count = 0;
         const char *unit;
         int r;
 
@@ -917,17 +1102,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;
-                                count++;
+                                context->meta_size[i] = iovec->iov_len - strlen(meta_field_names[i]);
                                 break;
                         }
                 }
@@ -949,7 +1132,7 @@ 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;
         Context context = {};
         struct iovec_wrapper iovw = {};
         struct iovec iovec;
@@ -1047,12 +1230,7 @@ finish:
 }
 
 static int send_iovec(const struct iovec_wrapper *iovw, int input_fd) {
-
-        static const union sockaddr_union sa = {
-                .un.sun_family = AF_UNIX,
-                .un.sun_path = "/run/systemd/coredump",
-        };
-        _cleanup_close_ int fd = -1;
+        _cleanup_close_ int fd = -EBADF;
         int r;
 
         assert(iovw);
@@ -1062,8 +1240,9 @@ static int send_iovec(const struct iovec_wrapper *iovw, int input_fd) {
         if (fd < 0)
                 return log_error_errno(errno, "Failed to create coredump socket: %m");
 
-        if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0)
-                return log_error_errno(errno, "Failed to connect to coredump service: %m");
+        r = connect_unix_path(fd, AT_FDCWD, "/run/systemd/coredump");
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect to coredump service: %m");
 
         for (size_t i = 0; i < iovw->count; i++) {
                 struct msghdr mh = {
@@ -1168,6 +1347,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;
 
@@ -1185,7 +1365,7 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) {
         if (r < 0)
                 return r;
 
-        /* The following are optional but we used them if present */
+        /* The following are optional, but we use them if present. */
         r = get_process_exe(pid, &t);
         if (r >= 0)
                 r = iovw_put_string_field_free(iovw, "COREDUMP_EXE=", t);
@@ -1195,7 +1375,6 @@ static int gather_pid_metadata(struct iovec_wrapper *iovw, Context *context) {
         if (cg_pid_get_unit(pid, &t) >= 0)
                 (void) iovw_put_string_field_free(iovw, "COREDUMP_UNIT=", t);
 
-        /* The next are optional */
         if (cg_pid_get_user_unit(pid, &t) >= 0)
                 (void) iovw_put_string_field_free(iovw, "COREDUMP_USER_UNIT=", t);
 
@@ -1233,13 +1412,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);
 
@@ -1268,6 +1460,13 @@ static int process_kernel(int argc, char* argv[]) {
         struct iovec_wrapper *iovw;
         int r;
 
+        /* 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
+         * /dev/null. */
+        r = rearrange_stdio(STDIN_FILENO, -EBADF, -EBADF);
+        if (r < 0)
+                return log_error_errno(r, "Failed to connect stdout/stderr to /dev/null: %m");
+
         log_debug("Processing coredump received from the kernel...");
 
         iovw = iovw_new();