diff --git a/man/systemd-coredump.xml b/man/systemd-coredump.xml
index d499600dc1..c27981bd89 100644
--- a/man/systemd-coredump.xml
+++ b/man/systemd-coredump.xml
@@ -201,6 +201,7 @@ COREDUMP_UID=1000
COREDUMP_GID=1000
COREDUMP_SIGNAL_NAME=SIGSEGV
COREDUMP_SIGNAL=11
+COREDUMP_CODE=2
COREDUMP_TIMESTAMP=1614342930000000
COREDUMP_COMM=Web Content
COREDUMP_EXE=/usr/lib64/firefox/firefox
@@ -327,6 +328,19 @@ COREDUMP_FILENAME=/var/lib/systemd/coredump/core.Web….552351.….zst
+
+ COREDUMP_CODE=
+
+ The reason why the signal was sent as a numerical value. The code is defined as
+ field si_code in the siginfo_t structure and
+ documented in sigaction2.
+
+
+
+
+
+
COREDUMP_CWD=
COREDUMP_ROOT=
diff --git a/src/basic/signal-util.c b/src/basic/signal-util.c
index ae7bfa6bd3..aceafd35e2 100644
--- a/src/basic/signal-util.c
+++ b/src/basic/signal-util.c
@@ -223,6 +223,154 @@ int signal_from_string(const char *s) {
return -EINVAL;
}
+static const char *const sigill_code_table[] = {
+ [ILL_ILLOPC] = "ILL_ILLOPC",
+ [ILL_ILLOPN] = "ILL_ILLOPN",
+ [ILL_ILLADR] = "ILL_ILLADR",
+ [ILL_ILLTRP] = "ILL_ILLTRP",
+ [ILL_PRVOPC] = "ILL_PRVOPC",
+ [ILL_PRVREG] = "ILL_PRVREG",
+ [ILL_COPROC] = "ILL_COPROC",
+ [ILL_BADSTK] = "ILL_BADSTK",
+ [ILL_BADIADDR] = "ILL_BADIADDR",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(sigill_code, int);
+
+static const char *const sigfpe_code_table[] = {
+ [FPE_INTDIV] = "FPE_INTDIV",
+ [FPE_INTOVF] = "FPE_INTOVF",
+ [FPE_FLTDIV] = "FPE_FLTDIV",
+ [FPE_FLTOVF] = "FPE_FLTOVF",
+ [FPE_FLTUND] = "FPE_FLTUND",
+ [FPE_FLTRES] = "FPE_FLTRES",
+ [FPE_FLTINV] = "FPE_FLTINV",
+ [FPE_FLTSUB] = "FPE_FLTSUB",
+ [FPE_FLTUNK] = "FPE_FLTUNK",
+ [FPE_CONDTRAP] = "FPE_CONDTRAP",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(sigfpe_code, int);
+
+static const char *const sigsegv_code_table[] = {
+ [SEGV_MAPERR] = "SEGV_MAPERR",
+ [SEGV_ACCERR] = "SEGV_ACCERR",
+ [SEGV_BNDERR] = "SEGV_BNDERR",
+ [SEGV_PKUERR] = "SEGV_PKUERR",
+ [SEGV_ACCADI] = "SEGV_ACCADI",
+ [SEGV_ADIDERR] = "SEGV_ADIDERR",
+ [SEGV_ADIPERR] = "SEGV_ADIPERR",
+ [SEGV_MTEAERR] = "SEGV_MTEAERR",
+ [SEGV_MTESERR] = "SEGV_MTESERR",
+ [SEGV_CPERR] = "SEGV_CPERR",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(sigsegv_code, int);
+
+static const char *const sigbus_code_table[] = {
+ [BUS_ADRALN] = "BUS_ADRALN",
+ [BUS_ADRERR] = "BUS_ADRERR",
+ [BUS_OBJERR] = "BUS_OBJERR",
+ [BUS_MCEERR_AR] = "BUS_MCEERR_AR",
+ [BUS_MCEERR_AO] = "BUS_MCEERR_AO",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(sigbus_code, int);
+
+static const char *const sigtrap_code_table[] = {
+ [TRAP_BRKPT] = "TRAP_BRKPT",
+ [TRAP_TRACE] = "TRAP_TRACE",
+ [TRAP_BRANCH] = "TRAP_BRANCH",
+ [TRAP_HWBKPT] = "TRAP_HWBKPT",
+ [TRAP_UNK] = "TRAP_UNK",
+ [TRAP_PERF] = "TRAP_PERF",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(sigtrap_code, int);
+
+static const char *const sigsys_code_table[] = {
+ [SYS_SECCOMP] = "SYS_SECCOMP",
+ [SYS_USER_DISPATCH] = "SYS_USER_DISPATCH",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(sigsys_code, int);
+
+/* sigchld_code_table is already defined in src/basic/process-util.c with
+ * decoded, lower-case values (CLD_EXITED -> "exited"). For consistency with
+ * all other values decoded here, provide an uppercase variant. */
+static const char *const sigchld_uppercase_code_table[] = {
+ [CLD_EXITED] = "CLD_EXITED",
+ [CLD_KILLED] = "CLD_KILLED",
+ [CLD_DUMPED] = "CLD_DUMPED",
+ [CLD_TRAPPED] = "CLD_TRAPPED",
+ [CLD_STOPPED] = "CLD_STOPPED",
+ [CLD_CONTINUED] = "CLD_CONTINUED",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(sigchld_uppercase_code, int);
+
+static const char *const sigpoll_code_table[] = {
+ [POLL_IN] = "POLL_IN",
+ [POLL_OUT] = "POLL_OUT",
+ [POLL_MSG] = "POLL_MSG",
+ [POLL_ERR] = "POLL_ERR",
+ [POLL_PRI] = "POLL_PRI",
+ [POLL_HUP] = "POLL_HUP",
+};
+
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(sigpoll_code, int);
+
+const char* signal_code_to_string(int signo, int code) {
+ switch (code) {
+ /* SI_KERNEL is 0x80 (128). Defining a table just for SI_KERNEL and
+ * SI_USER would be a waste of .rodata space: special case them instead. */
+ case SI_KERNEL:
+ return "SI_KERNEL";
+ case SI_USER:
+ return "SI_USER";
+ /* The following values are all negatives. SI_ASYNCNL is -60. Similarly
+ * to the special case for SI_KERNEL above, avoid using a table for negative
+ * values as well. */
+ case SI_ASYNCNL:
+ return "SI_ASYNCNL";
+ case SI_DETHREAD:
+ return "SI_DETHREAD";
+ case SI_TKILL:
+ return "SI_TKILL";
+ case SI_SIGIO:
+ return "SI_SIGIO";
+ case SI_ASYNCIO:
+ return "SI_ASYNCIO";
+ case SI_MESGQ:
+ return "SI_MESGQ";
+ case SI_TIMER:
+ return "SI_TIMER";
+ case SI_QUEUE:
+ return "SI_QUEUE";
+ }
+
+ switch (signo) {
+ case SIGILL:
+ return sigill_code_to_string(code);
+ case SIGFPE:
+ return sigfpe_code_to_string(code);
+ case SIGSEGV:
+ return sigsegv_code_to_string(code);
+ case SIGBUS:
+ return sigbus_code_to_string(code);
+ case SIGTRAP:
+ return sigtrap_code_to_string(code);
+ case SIGCHLD:
+ return sigchld_uppercase_code_to_string(code);
+ case SIGPOLL:
+ return sigpoll_code_to_string(code);
+ case SIGSYS:
+ return sigsys_code_to_string(code);
+ default:
+ return NULL;
+ }
+}
+
void nop_signal_handler(int sig) {
/* nothing here */
}
diff --git a/src/basic/signal-util.h b/src/basic/signal-util.h
index c022da6295..0c6aa6898d 100644
--- a/src/basic/signal-util.h
+++ b/src/basic/signal-util.h
@@ -32,6 +32,7 @@ int sigprocmask_many_internal(int how, sigset_t *ret_old_mask, ...);
#define sigprocmask_many(...) sigprocmask_many_internal(__VA_ARGS__, -1)
DECLARE_STRING_TABLE_LOOKUP(signal, int);
+const char* signal_code_to_string(int signo, int code) _const_;
void nop_signal_handler(int sig);
diff --git a/src/coredump/coredump-context.c b/src/coredump/coredump-context.c
index 6cacae4eff..24d273499a 100644
--- a/src/coredump/coredump-context.c
+++ b/src/coredump/coredump-context.c
@@ -14,6 +14,7 @@
#include "memstream-util.h"
#include "namespace-util.h"
#include "parse-util.h"
+#include "pidfd-util.h"
#include "process-util.h"
#include "signal-util.h"
#include "special.h"
@@ -38,6 +39,7 @@ static const char * const metadata_field_table[_META_MAX] = {
[META_UNIT] = "COREDUMP_UNIT=",
[META_PROC_AUXV] = "COREDUMP_PROC_AUXV=",
[META_THREAD_NAME] = "COREDUMP_THREAD_NAME=",
+ [META_CODE] = "COREDUMP_CODE=",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(metadata_field, MetadataField);
@@ -176,6 +178,34 @@ static int get_process_container_parent_cmdline(PidRef *pid, char** ret_cmdline)
return 1;
}
+/* The kernel passes si_signo through core_pattern (%s). Starting with v7.1,
+ * si_code is reported as well via pidfd. */
+static int coredump_context_read_pidfd_info(CoredumpContext *context) {
+ struct pidfd_info info = {
+ .mask = PIDFD_INFO_COREDUMP,
+ };
+ int r;
+
+ assert(context);
+ assert(pidref_is_set(&context->pidref));
+
+ if (!context->got_pidfd || context->pidref.fd < 0)
+ return 0;
+
+ r = pidfd_get_info(context->pidref.fd, &info);
+ if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
+ return log_debug_errno(r, "PIDFD_INFO_COREDUMP not supported, ignoring: %m");
+ if (r < 0)
+ return log_debug_errno(r, "Failed to get pidfd coredump info, ignoring: %m");
+
+ if (FLAGS_SET(info.mask, PIDFD_INFO_COREDUMP_CODE)) {
+ context->code = (int) info.coredump_code;
+ context->got_code = true;
+ }
+
+ return 0;
+}
+
int coredump_context_build_iovw(CoredumpContext *context) {
char *t;
int r;
@@ -213,6 +243,10 @@ int coredump_context_build_iovw(CoredumpContext *context) {
return log_error_errno(r, "Failed to add COREDUMP_SIGNAL= field: %m");
(void) iovw_put_string_field(&context->iovw, "COREDUMP_SIGNAL_NAME=SIG", signal_to_string(context->signo));
+
+ /* Emit si_code if we learned it from pidfd_info */
+ if (context->got_code)
+ (void) iovw_put_string_fieldf(&context->iovw, "COREDUMP_CODE=", "%i", context->code);
}
r = iovw_put_string_fieldf(&context->iovw, "COREDUMP_TIMESTAMP=", USEC_FMT, context->timestamp);
@@ -367,6 +401,8 @@ static int coredump_context_parse_from_procfs(CoredumpContext *context) {
if (r < 0)
log_warning_errno(r, "Failed to get auxv, ignoring: %m");
+ (void) coredump_context_read_pidfd_info(context);
+
r = pidref_verify(&context->pidref);
if (r < 0)
return log_error_errno(r, "PIDFD validation failed: %m");
@@ -499,6 +535,17 @@ static int context_parse_one(CoredumpContext *context, MetadataField meta, bool
case META_THREAD_NAME:
return free_and_strdup_warn(&context->thread_name, s);
+ case META_CODE:
+ /* We must accept both positive and negative values. The former
+ * are reserved for kernel-generated signals, the latter for signals requested
+ * from userspace. See /usr/include/bits/siginfo-consts.h. */
+ r = safe_atoi(s, &context->code);
+ if (r < 0)
+ log_warning_errno(r, "Failed to parse code \"%s\", ignoring: %m", s);
+ else
+ context->got_code = true;
+ return 0;
+
case META_PROC_AUXV: {
char *t = memdup_suffix0(s, size);
if (!t)
diff --git a/src/coredump/coredump-context.h b/src/coredump/coredump-context.h
index 7fedfde2ad..a2ce16b853 100644
--- a/src/coredump/coredump-context.h
+++ b/src/coredump/coredump-context.h
@@ -42,6 +42,7 @@ typedef enum MetadataField {
META_UNIT,
META_PROC_AUXV,
META_THREAD_NAME,
+ META_CODE, /* code of signal causing dump (eg: 2 for SEGV_ACCERR). Since v7.1. */
_META_MAX,
_META_INVALID = -EINVAL,
} MetadataField;
@@ -51,6 +52,7 @@ struct CoredumpContext {
uid_t uid; /* META_ARGV_UID */
gid_t gid; /* META_ARGV_GID */
int signo; /* META_ARGV_SIGNAL */
+ int code; /* META_CODE */
usec_t timestamp; /* META_ARGV_TIMESTAMP */
uint64_t rlimit; /* META_ARGV_RLIMIT */
char *hostname; /* META_ARGV_HOSTNAME */
@@ -63,6 +65,7 @@ struct CoredumpContext {
size_t auxv_size; /* META_PROC_AUXV */
char *thread_name; /* META_THREAD_NAME */
bool got_pidfd; /* META_ARGV_PIDFD */
+ bool got_code; /* META_CODE */
bool same_pidns;
bool forwarded;
int input_fd;
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index 3b31471ab4..9b26e23d62 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -585,6 +585,7 @@ typedef enum CoredumpField {
COREDUMP_FIELD_UID,
COREDUMP_FIELD_GID,
COREDUMP_FIELD_SGNL,
+ COREDUMP_FIELD_CODE,
COREDUMP_FIELD_EXE,
COREDUMP_FIELD_COMM,
COREDUMP_FIELD_CMDLINE,
@@ -615,6 +616,7 @@ static const char* const coredump_field_table[_COREDUMP_FIELD_MAX] = {
[COREDUMP_FIELD_UID] = "COREDUMP_UID",
[COREDUMP_FIELD_GID] = "COREDUMP_GID",
[COREDUMP_FIELD_SGNL] = "COREDUMP_SIGNAL",
+ [COREDUMP_FIELD_CODE] = "COREDUMP_CODE",
[COREDUMP_FIELD_EXE] = "COREDUMP_EXE",
[COREDUMP_FIELD_COMM] = "COREDUMP_COMM",
[COREDUMP_FIELD_CMDLINE] = "COREDUMP_CMDLINE",
@@ -770,9 +772,23 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
int sig;
const char *name = f.normal_coredump ? "Signal" : "Reason";
- if (f.normal_coredump && safe_atoi(f.fields[COREDUMP_FIELD_SGNL], &sig) >= 0)
- fprintf(file, " %s: %s (%s)\n", name, f.fields[COREDUMP_FIELD_SGNL], signal_to_string(sig));
- else
+ if (f.normal_coredump && safe_atoi(f.fields[COREDUMP_FIELD_SGNL], &sig) >= 0) {
+ fprintf(file, " %s: %s (%s)", name, f.fields[COREDUMP_FIELD_SGNL], signal_to_string(sig));
+
+ if (f.fields[COREDUMP_FIELD_CODE]) {
+ int n;
+ const char *s;
+
+ if (safe_atoi(f.fields[COREDUMP_FIELD_CODE], &n) >= 0)
+ s = signal_code_to_string(sig, n);
+ else
+ s = NULL;
+
+ fprintf(file, " si_code: %s", s ?: f.fields[COREDUMP_FIELD_CODE]);
+ }
+
+ fputc('\n', file);
+ } else
fprintf(file, " %s: %s\n", name, f.fields[COREDUMP_FIELD_SGNL]);
}
@@ -893,7 +909,8 @@ static int print_info_json(FILE *file, sd_journal *j) {
pid_t pid_as_int = 0, tid_as_int = 0;
uid_t uid_as_int = UID_INVALID, owner_uid_as_int = UID_INVALID;
gid_t gid_as_int = GID_INVALID;
- int sig_as_int = 0;
+ int sig_as_int = 0, code_as_int = 0;
+ bool code_is_valid = false;
usec_t ts = USEC_INFINITY;
int r;
@@ -916,6 +933,8 @@ static int print_info_json(FILE *file, sd_journal *j) {
(void) parse_uid(f.fields[COREDUMP_FIELD_OWNER_UID], &owner_uid_as_int);
if (f.normal_coredump && f.fields[COREDUMP_FIELD_SGNL])
(void) safe_atoi(f.fields[COREDUMP_FIELD_SGNL], &sig_as_int);
+ if (f.normal_coredump && f.fields[COREDUMP_FIELD_CODE])
+ code_is_valid = safe_atoi(f.fields[COREDUMP_FIELD_CODE], &code_as_int) >= 0;
if (f.fields[COREDUMP_FIELD_TIMESTAMP])
(void) safe_atou64(f.fields[COREDUMP_FIELD_TIMESTAMP], &ts);
@@ -932,6 +951,8 @@ static int print_info_json(FILE *file, sd_journal *j) {
SD_JSON_BUILD_PAIR_CONDITION(f.normal_coredump && sig_as_int > 0, "Signal", SD_JSON_BUILD_INTEGER(sig_as_int)),
SD_JSON_BUILD_PAIR_CONDITION(f.normal_coredump && sig_as_int > 0 && !!signal_to_string(sig_as_int), "SignalName", SD_JSON_BUILD_STRING(signal_to_string(sig_as_int))),
SD_JSON_BUILD_PAIR_CONDITION(f.normal_coredump && sig_as_int <= 0 && !!f.fields[COREDUMP_FIELD_SGNL], "Signal", SD_JSON_BUILD_STRING(f.fields[COREDUMP_FIELD_SGNL])),
+ SD_JSON_BUILD_PAIR_CONDITION(f.normal_coredump && code_is_valid, "SignalCode", SD_JSON_BUILD_INTEGER(code_as_int)),
+ SD_JSON_BUILD_PAIR_CONDITION(f.normal_coredump && !code_is_valid && !!f.fields[COREDUMP_FIELD_CODE], "SignalCode", SD_JSON_BUILD_STRING(f.fields[COREDUMP_FIELD_CODE])),
SD_JSON_BUILD_PAIR_CONDITION(!f.normal_coredump && !!f.fields[COREDUMP_FIELD_SGNL], "Reason", SD_JSON_BUILD_STRING(f.fields[COREDUMP_FIELD_SGNL])),
SD_JSON_BUILD_PAIR_CONDITION(ts != USEC_INFINITY, "Timestamp", SD_JSON_BUILD_UNSIGNED(ts)),
SD_JSON_BUILD_PAIR_CONDITION(ts == USEC_INFINITY && !!f.fields[COREDUMP_FIELD_TIMESTAMP], "Timestamp", SD_JSON_BUILD_STRING(f.fields[COREDUMP_FIELD_TIMESTAMP])),
diff --git a/src/include/override/signal.h b/src/include/override/signal.h
index 6e596adacc..64ddacc4c9 100644
--- a/src/include/override/signal.h
+++ b/src/include/override/signal.h
@@ -3,5 +3,52 @@
#include_next /* IWYU pragma: export */
+#ifndef ILL_BADIADDR
+#define ILL_BADIADDR 9
+#endif
+
+#ifndef FPE_FLTUNK
+#define FPE_FLTUNK 14
+#endif
+
+#ifndef FPE_CONDTRAP
+#define FPE_CONDTRAP 15
+#endif
+
+#ifndef SEGV_ACCADI
+#define SEGV_ACCADI 5
+#endif
+
+#ifndef SEGV_ADIDERR
+#define SEGV_ADIDERR 6
+#endif
+
+#ifndef SEGV_ADIPERR
+#define SEGV_ADIPERR 7
+#endif
+
+/* Defined since glibc-2.39. */
+#ifndef SEGV_CPERR
+#define SEGV_CPERR 10
+#endif
+
+#ifndef SI_DETHREAD
+#define SI_DETHREAD -7
+#endif
+
+/* Defined since glibc-2.43. */
+#ifndef TRAP_PERF
+#define TRAP_PERF 6
+#endif
+
+/* Defined since glibc-2.33. */
+#ifndef SYS_SECCOMP
+#define SYS_SECCOMP 1
+#endif
+
+#ifndef SYS_USER_DISPATCH
+#define SYS_USER_DISPATCH 2
+#endif
+
int rt_tgsigqueueinfo_shim(pid_t tgid, pid_t tid, int sig, siginfo_t *info);
#define rt_tgsigqueueinfo rt_tgsigqueueinfo_shim
diff --git a/src/include/override/sys/pidfd.h b/src/include/override/sys/pidfd.h
index 0e9b2f3998..28fb75698e 100644
--- a/src/include/override/sys/pidfd.h
+++ b/src/include/override/sys/pidfd.h
@@ -1,15 +1,6 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
-#include
-
-/* since glibc-2.36. Only descend into the next on glibc — musl ships no such header,
- * and musl-gcc's -idirafter /usr/include would otherwise pull in glibc's copy (which depends on
- * __THROW and other glibc-isms) from the fallback path. */
-#if defined(__GLIBC__) && __has_include_next()
-#include_next /* IWYU pragma: export */
-#endif
-
#include
#include
#include
@@ -49,8 +40,14 @@ int pidfd_send_signal_shim(int fd, int sig, siginfo_t *info, unsigned flags);
#define PIDFD_INFO_CGROUPID (1UL << 2) /* Always returned if available, even if not requested */
#define PIDFD_INFO_EXIT (1UL << 3) /* Only returned if requested. */
#define PIDFD_INFO_COREDUMP (1UL << 4) /* Only returned if requested. */
+#define PIDFD_INFO_SUPPORTED_MASK (1UL << 5) /* Want/got supported mask flags */
+#define PIDFD_INFO_COREDUMP_SIGNAL (1UL << 6) /* Always returned if PIDFD_INFO_COREDUMP is requested. */
+#define PIDFD_INFO_COREDUMP_CODE (1UL << 7) /* Always returned if PIDFD_INFO_COREDUMP is requested. */
#define PIDFD_INFO_SIZE_VER0 64 /* sizeof first published struct */
+#define PIDFD_INFO_SIZE_VER1 72 /* sizeof second published struct */
+#define PIDFD_INFO_SIZE_VER2 80 /* sizeof third published struct */
+#define PIDFD_INFO_SIZE_VER3 88 /* sizeof fourth published struct */
/*
* Values for @coredump_mask in pidfd_info.
@@ -105,8 +102,13 @@ struct pidfd_info {
__u32 fsuid;
__u32 fsgid;
__s32 exit_code; /* since kernel v6.15 (7477d7dce48a996ae4e4f0b5f7bd82de7ec9131b) */
- __u32 coredump_mask; /* since kernel v6.16 (1d8db6fd698de1f73b1a7d72aea578fdd18d9a87) */
- __u32 __spare1;
+ struct { /* coredump info */
+ __u32 coredump_mask; /* since kernel v6.16 (1d8db6fd698de1f73b1a7d72aea578fdd18d9a87) */
+ __u32 coredump_signal; /* since kernel v6.19 (036375522be8425874e9e0f907c7127e315c7a52) */
+ __u32 coredump_code; /* since kernel v7.1 (701f7f4fbabbf4989ba6fbf033b160dd943221d5) */
+ __u32 coredump_pad; /* since kernel v7.1 (701f7f4fbabbf4989ba6fbf033b160dd943221d5) */
+ };
+ __u64 supported_mask; /* Mask flags that this kernel supports */
};
#define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info)
diff --git a/src/test/test-signal-util.c b/src/test/test-signal-util.c
index 822556022d..541ff8c7fc 100644
--- a/src/test/test-signal-util.c
+++ b/src/test/test-signal-util.c
@@ -103,6 +103,17 @@ TEST(signal_from_string) {
test_signal_from_string_number("-2", -ERANGE);
}
+TEST(signal_code_to_string) {
+ assert_se(streq_ptr(signal_code_to_string(SIGSEGV, SEGV_MAPERR), "SEGV_MAPERR"));
+ assert_se(streq_ptr(signal_code_to_string(SIGILL, ILL_ILLOPC), "ILL_ILLOPC"));
+ assert_se(streq_ptr(signal_code_to_string(SIGCHLD, CLD_CONTINUED), "CLD_CONTINUED"));
+ assert_se(streq_ptr(signal_code_to_string(SIGABRT, SI_TKILL), "SI_TKILL"));
+ assert_se(streq_ptr(signal_code_to_string(SIGABRT, SI_USER), "SI_USER"));
+ assert_se(streq_ptr(signal_code_to_string(SIGABRT, SI_KERNEL), "SI_KERNEL"));
+ assert_se(streq_ptr(signal_code_to_string(SIGSYS, SYS_SECCOMP), "SYS_SECCOMP"));
+ assert_se(signal_code_to_string(SIGABRT, 99) == NULL);
+}
+
TEST(block_signals) {
assert_se(signal_is_blocked(SIGUSR1) == 0);
assert_se(signal_is_blocked(SIGALRM) == 0);
diff --git a/test/units/TEST-87-AUX-UTILS-VM.coredump.sh b/test/units/TEST-87-AUX-UTILS-VM.coredump.sh
index 30252132ff..675d37184b 100755
--- a/test/units/TEST-87-AUX-UTILS-VM.coredump.sh
+++ b/test/units/TEST-87-AUX-UTILS-VM.coredump.sh
@@ -166,6 +166,12 @@ coredumpctl info "$CORE_TEST_BIN" | grep "TID:" >/dev/null
# Check the field is queryable in the journal
coredumpctl -F COREDUMP_TID
+# If COREDUMP_CODE= is present, check that the expected code is SI_USER (0).
+if coredumpctl -F COREDUMP_CODE | grep "^0$" >/dev/null; then
+ coredumpctl info "$CORE_TEST_BIN" | grep --fixed-strings "Signal: 5 (TRAP) si_code: SI_USER" >/dev/null
+ coredumpctl info --json=short "$CORE_TEST_BIN" | jq -se 'any(.[]; .SignalCode == 0)'
+fi
+
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##*/}"