From nobody Fri Apr 3 22:12:25 2026 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 4106B18E025; Sun, 22 Mar 2026 20:31:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=217.140.110.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774211490; cv=none; b=usnnFcmgOfEmo2ZUbq90EA+vjn9OrK/grAv+KO8nYuRoRwX9M4FPk3jmFiJw3RLP8tejxo3bR85WrIvDmZGF7o93f/WYMKB1Ow+kOgwnuj8A6QM4BIOJqybFhzp7sQVFtOy0fbMw9sNdd64CMvvEroz4eGSDB+cEGaUKgXxUsqI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774211490; c=relaxed/simple; bh=JsC7JL15dAqpzUYWbsrLq+eLKrqpYxyBY87wQ0U2hf4=; h=Date:From:To:Cc:Subject:Message-ID:MIME-Version:Content-Type: Content-Disposition; b=DCqzzzG/QbOEa1uwmEOVgwty39zmAd9zBSi8O6Z9mTcY7NVdkCENHYGGmlD+EPLvaecbnG7T/mUu5XtrsYIF3yMXaTNfDXmKZ+sZ4wwmUQjwPyUOMJ0/QqxslO8aht9Pi+QC0P/Z1C9cYbt30VAgo4GFI0mBVA1oJKDITXOYej8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com; spf=pass smtp.mailfrom=arm.com; arc=none smtp.client-ip=217.140.110.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 697621570; Sun, 22 Mar 2026 13:31:14 -0700 (PDT) Received: from NH27D9T0LF (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 074D13F73B; Sun, 22 Mar 2026 13:31:18 -0700 (PDT) Date: Sun, 22 Mar 2026 21:31:11 +0100 From: Emanuele Rocca To: linux-kernel@vger.kernel.org Cc: Christian Brauner , Jan Kara , Alexander Viro , linux-fsdevel@vger.kernel.org, Mark Brown , Jann Horn , Oleg Nesterov Subject: [PATCH v3] pidfds: add coredump_code field to pidfd_info Message-ID: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The struct pidfd_info currently exposes in a field called coredump_signal t= he signal number (si_signo) that triggered the dump (for example, 11 for SIGSE= GV). However, it is also valuable to understand the reason why that signal was s= ent. This additional context is provided by the signal code (si_code), such as 2= for SEGV_ACCERR. Add a new field to struct pidfd_info called coredump_code with the value of si_code for the benefit of sysadmins who pipe core dumps to user-space prog= rams for later analysis. The following snippet illustrates a simplified C program that consumes coredump_signal and coredump_code, and then logs core dump signals and codes to a file: int pidfd =3D (int)atoi(argv[1]); struct pidfd_info info =3D { .mask =3D PIDFD_INFO_EXIT | PIDFD_INFO_COREDUMP, }; if (ioctl(pidfd, PIDFD_GET_INFO, &info) =3D=3D 0) if (info.mask & PIDFD_INFO_COREDUMP) fprintf(f, "PID=3D%d, si_signo: %d si_code: %d\n", info.pid, info.coredump_signal, info.coredump_code); Assuming the program is installed under /usr/local/bin/core-logger, core du= mp processing can be enabled by setting /proc/sys/kernel/core_pattern to '|/usr/local/bin/dumpstuff %F'. systemd-coredump(8) already uses pidfds to process core dumps, and it could= be extended to include the values of coredump_code too. Signed-off-by: Emanuele Rocca Acked-by: Oleg Nesterov --- Resending as a new thread, apologies for sending v2 as a follow-up to the kernel test robot instead! V1 -> V2: Add coredump_pad to struct pidfd_info to ensure the struct has the same size on both 64 bit and 32 bit systems. The issue was spotted by the kernel test robot. v1: https://lore.kernel.org/lkml/ab29J6KsQm8Xg3LR@NH27D9T0LF/ Link: https://lore.kernel.org/lkml/202603211842.JCwUVYTI-lkp@intel.com/ V2 -> V3: Sending as a new thread, same as v2 otherwise. v2: https://lore.kernel.org/lkml/ab68fUmCK4An1UH-@NH27D9T0LF/ fs/pidfs.c | 12 ++++--- include/uapi/linux/pidfd.h | 4 +++ .../coredump/coredump_socket_protocol_test.c | 26 +++++++++++++++ .../selftests/coredump/coredump_socket_test.c | 32 +++++++++++++++++++ .../coredump/coredump_test_helpers.c | 4 +-- tools/testing/selftests/pidfd/pidfd.h | 5 +++ .../testing/selftests/pidfd/pidfd_info_test.c | 1 + 7 files changed, 78 insertions(+), 6 deletions(-) diff --git a/fs/pidfs.c b/fs/pidfs.c index e3825ee246be..49b46be6c413 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -55,6 +55,7 @@ struct pidfs_attr { }; __u32 coredump_mask; __u32 coredump_signal; + __u32 coredump_code; }; =20 static struct rhashtable pidfs_ino_ht; @@ -331,7 +332,8 @@ static __u32 pidfs_coredump_mask(unsigned long mm_flags) PIDFD_INFO_EXIT | \ PIDFD_INFO_COREDUMP | \ PIDFD_INFO_SUPPORTED_MASK | \ - PIDFD_INFO_COREDUMP_SIGNAL) + PIDFD_INFO_COREDUMP_SIGNAL | \ + PIDFD_INFO_COREDUMP_CODE) =20 static long pidfd_info(struct file *file, unsigned int cmd, unsigned long = arg) { @@ -345,7 +347,7 @@ static long pidfd_info(struct file *file, unsigned int = cmd, unsigned long arg) const struct cred *c; __u64 mask; =20 - BUILD_BUG_ON(sizeof(struct pidfd_info) !=3D PIDFD_INFO_SIZE_VER2); + BUILD_BUG_ON(sizeof(struct pidfd_info) !=3D PIDFD_INFO_SIZE_VER3); =20 if (!uinfo) return -EINVAL; @@ -378,9 +380,10 @@ static long pidfd_info(struct file *file, unsigned int= cmd, unsigned long arg) if (mask & PIDFD_INFO_COREDUMP) { if (test_bit(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask)) { smp_rmb(); - kinfo.mask |=3D PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL; + kinfo.mask |=3D PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL | PIDF= D_INFO_COREDUMP_CODE; kinfo.coredump_mask =3D attr->coredump_mask; kinfo.coredump_signal =3D attr->coredump_signal; + kinfo.coredump_code =3D attr->coredump_code; } } =20 @@ -730,8 +733,9 @@ void pidfs_coredump(const struct coredump_params *cprm) PIDFD_COREDUMPED; /* If coredumping is set to skip we should never end up here. */ VFS_WARN_ON_ONCE(attr->coredump_mask & PIDFD_COREDUMP_SKIP); - /* Expose the signal number that caused the coredump. */ + /* Expose the signal number and code that caused the coredump. */ attr->coredump_signal =3D cprm->siginfo->si_signo; + attr->coredump_code =3D cprm->siginfo->si_code; smp_wmb(); set_bit(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask); } diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h index ea9a6811fc76..db3e95635c4d 100644 --- a/include/uapi/linux/pidfd.h +++ b/include/uapi/linux/pidfd.h @@ -28,10 +28,12 @@ #define PIDFD_INFO_COREDUMP (1UL << 4) /* Only returned if requested. */ #define PIDFD_INFO_SUPPORTED_MASK (1UL << 5) /* Want/got supported mask fl= ags */ #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_IN= FO_COREDUMP is requested. */ =20 #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 */ =20 /* * Values for @coredump_mask in pidfd_info. @@ -98,6 +100,8 @@ struct pidfd_info { struct /* coredump info */ { __u32 coredump_mask; __u32 coredump_signal; + __u32 coredump_code; + __u32 coredump_pad; /* align supported_mask to 8 bytes */ }; __u64 supported_mask; /* Mask flags that this kernel supports */ }; diff --git a/tools/testing/selftests/coredump/coredump_socket_protocol_test= .c b/tools/testing/selftests/coredump/coredump_socket_protocol_test.c index d19b6717c53e..d9fa6239b5a9 100644 --- a/tools/testing/selftests/coredump/coredump_socket_protocol_test.c +++ b/tools/testing/selftests/coredump/coredump_socket_protocol_test.c @@ -1004,6 +1004,8 @@ TEST_F(coredump, socket_request_invalid_size_large) * * Verify that when using socket-based coredump protocol, * the coredump_signal field is correctly exposed as SIGSEGV. + * Also check that the coredump_code field is correctly exposed + * as SEGV_MAPERR. */ TEST_F(coredump, socket_coredump_signal_sigsegv) { @@ -1079,6 +1081,18 @@ TEST_F(coredump, socket_coredump_signal_sigsegv) goto out; } =20 + /* Verify coredump_code is available and correct */ + if (!(info.mask & PIDFD_INFO_COREDUMP_CODE)) { + fprintf(stderr, "socket_coredump_signal_sigsegv: PIDFD_INFO_COREDUMP_CO= DE not set in mask\n"); + goto out; + } + + if (info.coredump_code !=3D SEGV_MAPERR) { + fprintf(stderr, "socket_coredump_signal_sigsegv: coredump_code=3D%d, ex= pected SEGV_MAPERR=3D%d\n", + info.coredump_code, SEGV_MAPERR); + goto out; + } + if (!read_coredump_req(fd_coredump, &req)) { fprintf(stderr, "socket_coredump_signal_sigsegv: read_coredump_req fail= ed\n"); goto out; @@ -1128,6 +1142,8 @@ TEST_F(coredump, socket_coredump_signal_sigsegv) ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP)); ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP_SIGNAL)); ASSERT_EQ(info.coredump_signal, SIGSEGV); + ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP_CODE)); + ASSERT_EQ(info.coredump_code, SEGV_MAPERR); =20 wait_and_check_coredump_server(pid_coredump_server, _metadata, self); } @@ -1137,6 +1153,8 @@ TEST_F(coredump, socket_coredump_signal_sigsegv) * * Verify that when using socket-based coredump protocol, * the coredump_signal field is correctly exposed as SIGABRT. + * Also check that the coredump_code field is correctly exposed + * as SI_TKILL. */ TEST_F(coredump, socket_coredump_signal_sigabrt) { @@ -1212,6 +1230,12 @@ TEST_F(coredump, socket_coredump_signal_sigabrt) goto out; } =20 + if (info.coredump_code !=3D SI_TKILL) { + fprintf(stderr, "socket_coredump_signal_sigabrt: coredump_code=3D%d, ex= pected SI_TKILL=3D%d\n", + info.coredump_code, SI_TKILL); + goto out; + } + if (!read_coredump_req(fd_coredump, &req)) { fprintf(stderr, "socket_coredump_signal_sigabrt: read_coredump_req fail= ed\n"); goto out; @@ -1261,6 +1285,8 @@ TEST_F(coredump, socket_coredump_signal_sigabrt) ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP)); ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP_SIGNAL)); ASSERT_EQ(info.coredump_signal, SIGABRT); + ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP_CODE)); + ASSERT_EQ(info.coredump_code, SI_TKILL); =20 wait_and_check_coredump_server(pid_coredump_server, _metadata, self); } diff --git a/tools/testing/selftests/coredump/coredump_socket_test.c b/tool= s/testing/selftests/coredump/coredump_socket_test.c index 7e26d4a6a15d..422728f632ca 100644 --- a/tools/testing/selftests/coredump/coredump_socket_test.c +++ b/tools/testing/selftests/coredump/coredump_socket_test.c @@ -435,6 +435,8 @@ TEST_F(coredump, socket_no_listener) * * Verify that when using simple socket-based coredump (@ pattern), * the coredump_signal field is correctly exposed as SIGSEGV. + * Also check that the coredump_code field is correctly exposed + * as SEGV_MAPERR. */ TEST_F(coredump, socket_coredump_signal_sigsegv) { @@ -509,6 +511,18 @@ TEST_F(coredump, socket_coredump_signal_sigsegv) goto out; } =20 + /* Verify coredump_code is available and correct */ + if (!(info.mask & PIDFD_INFO_COREDUMP_CODE)) { + fprintf(stderr, "socket_coredump_signal_sigsegv: PIDFD_INFO_COREDUMP_CO= DE not set in mask\n"); + goto out; + } + + if (info.coredump_code !=3D SEGV_MAPERR) { + fprintf(stderr, "socket_coredump_signal_sigsegv: coredump_code=3D%d, ex= pected SEGV_MAPERR=3D%d\n", + info.coredump_code, SEGV_MAPERR); + goto out; + } + fd_core_file =3D open_coredump_tmpfile(self->fd_tmpfs_detached); if (fd_core_file < 0) { fprintf(stderr, "socket_coredump_signal_sigsegv: open_coredump_tmpfile = failed: %m\n"); @@ -572,6 +586,8 @@ TEST_F(coredump, socket_coredump_signal_sigsegv) ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP)); ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP_SIGNAL)); ASSERT_EQ(info.coredump_signal, SIGSEGV); + ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP_CODE)); + ASSERT_EQ(info.coredump_code, SEGV_MAPERR); =20 wait_and_check_coredump_server(pid_coredump_server, _metadata, self); } @@ -581,6 +597,8 @@ TEST_F(coredump, socket_coredump_signal_sigsegv) * * Verify that when using simple socket-based coredump (@ pattern), * the coredump_signal field is correctly exposed as SIGABRT. + * Also check that the coredump_code field is correctly exposed + * as SI_TKILL. */ TEST_F(coredump, socket_coredump_signal_sigabrt) { @@ -655,6 +673,18 @@ TEST_F(coredump, socket_coredump_signal_sigabrt) goto out; } =20 + /* Verify coredump_code is available and correct */ + if (!(info.mask & PIDFD_INFO_COREDUMP_CODE)) { + fprintf(stderr, "socket_coredump_signal_sigabrt: PIDFD_INFO_COREDUMP_CO= DE not set in mask\n"); + goto out; + } + + if (info.coredump_code !=3D SI_TKILL) { + fprintf(stderr, "socket_coredump_signal_sigabrt: coredump_code=3D%d, ex= pected SI_TKILL=3D%d\n", + info.coredump_code, SI_TKILL); + goto out; + } + fd_core_file =3D open_coredump_tmpfile(self->fd_tmpfs_detached); if (fd_core_file < 0) { fprintf(stderr, "socket_coredump_signal_sigabrt: open_coredump_tmpfile = failed: %m\n"); @@ -718,6 +748,8 @@ TEST_F(coredump, socket_coredump_signal_sigabrt) ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP)); ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP_SIGNAL)); ASSERT_EQ(info.coredump_signal, SIGABRT); + ASSERT_TRUE(!!(info.mask & PIDFD_INFO_COREDUMP_CODE)); + ASSERT_EQ(info.coredump_code, SI_TKILL); =20 wait_and_check_coredump_server(pid_coredump_server, _metadata, self); } diff --git a/tools/testing/selftests/coredump/coredump_test_helpers.c b/too= ls/testing/selftests/coredump/coredump_test_helpers.c index 2c850e0b1b57..2a20faf9cb0a 100644 --- a/tools/testing/selftests/coredump/coredump_test_helpers.c +++ b/tools/testing/selftests/coredump/coredump_test_helpers.c @@ -148,8 +148,8 @@ bool get_pidfd_info(int fd_peer_pidfd, struct pidfd_inf= o *info) fprintf(stderr, "get_pidfd_info: ioctl(PIDFD_GET_INFO) failed: %m\n"); return false; } - fprintf(stderr, "get_pidfd_info: mask=3D0x%llx, coredump_mask=3D0x%x, cor= edump_signal=3D%d\n", - (unsigned long long)info->mask, info->coredump_mask, info->coredump_sign= al); + fprintf(stderr, "get_pidfd_info: mask=3D0x%llx, coredump_mask=3D0x%x, cor= edump_signal=3D%d, coredump_code=3D%d\n", + (unsigned long long)info->mask, info->coredump_mask, info->coredump_sign= al, info->coredump_code); return true; } =20 diff --git a/tools/testing/selftests/pidfd/pidfd.h b/tools/testing/selftest= s/pidfd/pidfd.h index 9085c1a3c005..5a4e78c10f43 100644 --- a/tools/testing/selftests/pidfd/pidfd.h +++ b/tools/testing/selftests/pidfd/pidfd.h @@ -156,6 +156,10 @@ #define PIDFD_INFO_COREDUMP_SIGNAL (1UL << 6) #endif =20 +#ifndef PIDFD_INFO_COREDUMP_CODE +#define PIDFD_INFO_COREDUMP_CODE (1UL << 7) +#endif + #ifndef PIDFD_COREDUMPED #define PIDFD_COREDUMPED (1U << 0) /* Did crash and... */ #endif @@ -194,6 +198,7 @@ struct pidfd_info { struct { __u32 coredump_mask; __u32 coredump_signal; + __u32 coredump_code; }; __u64 supported_mask; }; diff --git a/tools/testing/selftests/pidfd/pidfd_info_test.c b/tools/testin= g/selftests/pidfd/pidfd_info_test.c index 8bed951e06a0..597012ed195f 100644 --- a/tools/testing/selftests/pidfd/pidfd_info_test.c +++ b/tools/testing/selftests/pidfd/pidfd_info_test.c @@ -724,6 +724,7 @@ TEST(supported_mask_field) ASSERT_TRUE(!!(info.supported_mask & PIDFD_INFO_COREDUMP)); ASSERT_TRUE(!!(info.supported_mask & PIDFD_INFO_SUPPORTED_MASK)); ASSERT_TRUE(!!(info.supported_mask & PIDFD_INFO_COREDUMP_SIGNAL)); + ASSERT_TRUE(!!(info.supported_mask & PIDFD_INFO_COREDUMP_CODE)); =20 /* Clean up */ sys_pidfd_send_signal(pidfd, SIGKILL, NULL, 0); --=20 2.47.3