Skip to content

Commit 212c405

Browse files
committed
Merge tag 'vfs-6.19-rc1.coredump' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull pidfd and coredump updates from Christian Brauner: "Features: - Expose coredump signal via pidfd Expose the signal that caused the coredump through the pidfd interface. The recent changes to rework coredump handling to rely on unix sockets are in the process of being used in systemd. The previous systemd coredump container interface requires the coredump file descriptor and basic information including the signal number to be sent to the container. This means the signal number needs to be available before sending the coredump to the container. - Add supported_mask field to pidfd Add a new supported_mask field to struct pidfd_info that indicates which information fields are supported by the running kernel. This allows userspace to detect feature availability without relying on error codes or kernel version checks. Cleanups: - Drop struct pidfs_exit_info and prepare to drop exit_info pointer, simplifying the internal publication mechanism for exit and coredump information retrievable via the pidfd ioctl - Use guard() for task_lock in pidfs - Reduce wait_pidfd lock scope - Add missing PIDFD_INFO_SIZE_VER1 constant - Add missing BUILD_BUG_ON() assert on struct pidfd_info Fixes: - Fix PIDFD_INFO_COREDUMP handling Selftests: - Split out coredump socket tests and common helpers into separate files for better organization - Fix userspace coredump client detection issues - Handle edge-triggered epoll correctly - Ignore ENOSPC errors in tests - Add debug logging to coredump socket tests, socket protocol tests, and test helpers - Add tests for PIDFD_INFO_COREDUMP_SIGNAL - Add tests for supported_mask field - Update pidfd header for selftests" * tag 'vfs-6.19-rc1.coredump' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (23 commits) pidfs: reduce wait_pidfd lock scope selftests/coredump: add second PIDFD_INFO_COREDUMP_SIGNAL test selftests/coredump: add first PIDFD_INFO_COREDUMP_SIGNAL test selftests/coredump: ignore ENOSPC errors selftests/coredump: add debug logging to coredump socket protocol tests selftests/coredump: add debug logging to coredump socket tests selftests/coredump: add debug logging to test helpers selftests/coredump: handle edge-triggered epoll correctly selftests/coredump: fix userspace coredump client detection selftests/coredump: fix userspace client detection selftests/coredump: split out coredump socket tests selftests/coredump: split out common helpers selftests/pidfd: add second supported_mask test selftests/pidfd: add first supported_mask test selftests/pidfd: update pidfd header pidfs: expose coredump signal pidfs: drop struct pidfs_exit_info pidfs: prepare to drop exit_info pointer pidfd: add a new supported_mask field pidfs: add missing BUILD_BUG_ON() assert on struct pidfd_info ...
2 parents 415d34b + 390d967 commit 212c405

11 files changed

Lines changed: 2927 additions & 1711 deletions

File tree

fs/pidfs.c

Lines changed: 67 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,20 @@ void pidfs_get_root(struct path *path)
3939
path_get(path);
4040
}
4141

42-
/*
43-
* Stashes information that userspace needs to access even after the
44-
* process has been reaped.
45-
*/
46-
struct pidfs_exit_info {
47-
__u64 cgroupid;
48-
__s32 exit_code;
49-
__u32 coredump_mask;
42+
enum pidfs_attr_mask_bits {
43+
PIDFS_ATTR_BIT_EXIT = 0,
44+
PIDFS_ATTR_BIT_COREDUMP = 1,
5045
};
5146

5247
struct pidfs_attr {
48+
unsigned long attr_mask;
5349
struct simple_xattrs *xattrs;
54-
struct pidfs_exit_info __pei;
55-
struct pidfs_exit_info *exit_info;
50+
struct /* exit info */ {
51+
__u64 cgroupid;
52+
__s32 exit_code;
53+
};
54+
__u32 coredump_mask;
55+
__u32 coredump_signal;
5656
};
5757

5858
static struct rb_root pidfs_ino_tree = RB_ROOT;
@@ -293,19 +293,29 @@ static __u32 pidfs_coredump_mask(unsigned long mm_flags)
293293
return 0;
294294
}
295295

296+
/* This must be updated whenever a new flag is added */
297+
#define PIDFD_INFO_SUPPORTED (PIDFD_INFO_PID | \
298+
PIDFD_INFO_CREDS | \
299+
PIDFD_INFO_CGROUPID | \
300+
PIDFD_INFO_EXIT | \
301+
PIDFD_INFO_COREDUMP | \
302+
PIDFD_INFO_SUPPORTED_MASK | \
303+
PIDFD_INFO_COREDUMP_SIGNAL)
304+
296305
static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
297306
{
298307
struct pidfd_info __user *uinfo = (struct pidfd_info __user *)arg;
299308
struct task_struct *task __free(put_task) = NULL;
300309
struct pid *pid = pidfd_pid(file);
301310
size_t usize = _IOC_SIZE(cmd);
302311
struct pidfd_info kinfo = {};
303-
struct pidfs_exit_info *exit_info;
304312
struct user_namespace *user_ns;
305313
struct pidfs_attr *attr;
306314
const struct cred *c;
307315
__u64 mask;
308316

317+
BUILD_BUG_ON(sizeof(struct pidfd_info) != PIDFD_INFO_SIZE_VER2);
318+
309319
if (!uinfo)
310320
return -EINVAL;
311321
if (usize < PIDFD_INFO_SIZE_VER0)
@@ -323,20 +333,24 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
323333

324334
attr = READ_ONCE(pid->attr);
325335
if (mask & PIDFD_INFO_EXIT) {
326-
exit_info = READ_ONCE(attr->exit_info);
327-
if (exit_info) {
336+
if (test_bit(PIDFS_ATTR_BIT_EXIT, &attr->attr_mask)) {
337+
smp_rmb();
328338
kinfo.mask |= PIDFD_INFO_EXIT;
329339
#ifdef CONFIG_CGROUPS
330-
kinfo.cgroupid = exit_info->cgroupid;
340+
kinfo.cgroupid = attr->cgroupid;
331341
kinfo.mask |= PIDFD_INFO_CGROUPID;
332342
#endif
333-
kinfo.exit_code = exit_info->exit_code;
343+
kinfo.exit_code = attr->exit_code;
334344
}
335345
}
336346

337347
if (mask & PIDFD_INFO_COREDUMP) {
338-
kinfo.mask |= PIDFD_INFO_COREDUMP;
339-
kinfo.coredump_mask = READ_ONCE(attr->__pei.coredump_mask);
348+
if (test_bit(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask)) {
349+
smp_rmb();
350+
kinfo.mask |= PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL;
351+
kinfo.coredump_mask = attr->coredump_mask;
352+
kinfo.coredump_signal = attr->coredump_signal;
353+
}
340354
}
341355

342356
task = get_pid_task(pid, PIDTYPE_PID);
@@ -355,14 +369,15 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
355369
if (!c)
356370
return -ESRCH;
357371

358-
if ((kinfo.mask & PIDFD_INFO_COREDUMP) && !(kinfo.coredump_mask)) {
359-
task_lock(task);
372+
if ((mask & PIDFD_INFO_COREDUMP) && !kinfo.coredump_mask) {
373+
guard(task_lock)(task);
360374
if (task->mm) {
361375
unsigned long flags = __mm_flags_get_dumpable(task->mm);
362376

363377
kinfo.coredump_mask = pidfs_coredump_mask(flags);
378+
kinfo.mask |= PIDFD_INFO_COREDUMP;
379+
/* No coredump actually took place, so no coredump signal. */
364380
}
365-
task_unlock(task);
366381
}
367382

368383
/* Unconditionally return identifiers and credentials, the rest only on request */
@@ -409,6 +424,13 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
409424
return -ESRCH;
410425

411426
copy_out:
427+
if (mask & PIDFD_INFO_SUPPORTED_MASK) {
428+
kinfo.mask |= PIDFD_INFO_SUPPORTED_MASK;
429+
kinfo.supported_mask = PIDFD_INFO_SUPPORTED;
430+
}
431+
432+
/* Are there bits in the return mask not present in PIDFD_INFO_SUPPORTED? */
433+
WARN_ON_ONCE(~PIDFD_INFO_SUPPORTED & kinfo.mask);
412434
/*
413435
* If userspace and the kernel have the same struct size it can just
414436
* be copied. If userspace provides an older struct, only the bits that
@@ -603,24 +625,25 @@ void pidfs_exit(struct task_struct *tsk)
603625
{
604626
struct pid *pid = task_pid(tsk);
605627
struct pidfs_attr *attr;
606-
struct pidfs_exit_info *exit_info;
607628
#ifdef CONFIG_CGROUPS
608629
struct cgroup *cgrp;
609630
#endif
610631

611632
might_sleep();
612633

613-
guard(spinlock_irq)(&pid->wait_pidfd.lock);
614-
attr = pid->attr;
615-
if (!attr) {
616-
/*
617-
* No one ever held a pidfd for this struct pid.
618-
* Mark it as dead so no one can add a pidfs
619-
* entry anymore. We're about to be reaped and
620-
* so no exit information would be available.
621-
*/
622-
pid->attr = PIDFS_PID_DEAD;
623-
return;
634+
/* Synchronize with pidfs_register_pid(). */
635+
scoped_guard(spinlock_irq, &pid->wait_pidfd.lock) {
636+
attr = pid->attr;
637+
if (!attr) {
638+
/*
639+
* No one ever held a pidfd for this struct pid.
640+
* Mark it as dead so no one can add a pidfs
641+
* entry anymore. We're about to be reaped and
642+
* so no exit information would be available.
643+
*/
644+
pid->attr = PIDFS_PID_DEAD;
645+
return;
646+
}
624647
}
625648

626649
/*
@@ -631,41 +654,39 @@ void pidfs_exit(struct task_struct *tsk)
631654
* is put
632655
*/
633656

634-
exit_info = &attr->__pei;
635-
636657
#ifdef CONFIG_CGROUPS
637658
rcu_read_lock();
638659
cgrp = task_dfl_cgroup(tsk);
639-
exit_info->cgroupid = cgroup_id(cgrp);
660+
attr->cgroupid = cgroup_id(cgrp);
640661
rcu_read_unlock();
641662
#endif
642-
exit_info->exit_code = tsk->exit_code;
663+
attr->exit_code = tsk->exit_code;
643664

644665
/* Ensure that PIDFD_GET_INFO sees either all or nothing. */
645-
smp_store_release(&attr->exit_info, &attr->__pei);
666+
smp_wmb();
667+
set_bit(PIDFS_ATTR_BIT_EXIT, &attr->attr_mask);
646668
}
647669

648670
#ifdef CONFIG_COREDUMP
649671
void pidfs_coredump(const struct coredump_params *cprm)
650672
{
651673
struct pid *pid = cprm->pid;
652-
struct pidfs_exit_info *exit_info;
653674
struct pidfs_attr *attr;
654-
__u32 coredump_mask = 0;
655675

656676
attr = READ_ONCE(pid->attr);
657677

658678
VFS_WARN_ON_ONCE(!attr);
659679
VFS_WARN_ON_ONCE(attr == PIDFS_PID_DEAD);
660680

661-
exit_info = &attr->__pei;
662-
/* Note how we were coredumped. */
663-
coredump_mask = pidfs_coredump_mask(cprm->mm_flags);
664-
/* Note that we actually did coredump. */
665-
coredump_mask |= PIDFD_COREDUMPED;
681+
/* Note how we were coredumped and that we coredumped. */
682+
attr->coredump_mask = pidfs_coredump_mask(cprm->mm_flags) |
683+
PIDFD_COREDUMPED;
666684
/* If coredumping is set to skip we should never end up here. */
667-
VFS_WARN_ON_ONCE(coredump_mask & PIDFD_COREDUMP_SKIP);
668-
smp_store_release(&exit_info->coredump_mask, coredump_mask);
685+
VFS_WARN_ON_ONCE(attr->coredump_mask & PIDFD_COREDUMP_SKIP);
686+
/* Expose the signal number that caused the coredump. */
687+
attr->coredump_signal = cprm->siginfo->si_signo;
688+
smp_wmb();
689+
set_bit(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask);
669690
}
670691
#endif
671692

include/uapi/linux/pidfd.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@
2626
#define PIDFD_INFO_CGROUPID (1UL << 2) /* Always returned if available, even if not requested */
2727
#define PIDFD_INFO_EXIT (1UL << 3) /* Only returned if requested. */
2828
#define PIDFD_INFO_COREDUMP (1UL << 4) /* Only returned if requested. */
29+
#define PIDFD_INFO_SUPPORTED_MASK (1UL << 5) /* Want/got supported mask flags */
30+
#define PIDFD_INFO_COREDUMP_SIGNAL (1UL << 6) /* Always returned if PIDFD_INFO_COREDUMP is requested. */
2931

3032
#define PIDFD_INFO_SIZE_VER0 64 /* sizeof first published struct */
33+
#define PIDFD_INFO_SIZE_VER1 72 /* sizeof second published struct */
34+
#define PIDFD_INFO_SIZE_VER2 80 /* sizeof third published struct */
3135

3236
/*
3337
* Values for @coredump_mask in pidfd_info.
@@ -91,8 +95,11 @@ struct pidfd_info {
9195
__u32 fsuid;
9296
__u32 fsgid;
9397
__s32 exit_code;
94-
__u32 coredump_mask;
95-
__u32 __spare1;
98+
struct /* coredump info */ {
99+
__u32 coredump_mask;
100+
__u32 coredump_signal;
101+
};
102+
__u64 supported_mask; /* Mask flags that this kernel supports */
96103
};
97104

98105
#define PIDFS_IOCTL_MAGIC 0xFF
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
stackdump_test
3+
coredump_socket_test
4+
coredump_socket_protocol_test
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
CFLAGS += -Wall -O0 -g $(KHDR_INCLUDES) $(TOOLS_INCLUDES)
33

4-
TEST_GEN_PROGS := stackdump_test
4+
TEST_GEN_PROGS := stackdump_test \
5+
coredump_socket_test \
6+
coredump_socket_protocol_test
57
TEST_FILES := stackdump
68

79
include ../lib.mk
10+
11+
$(OUTPUT)/stackdump_test: coredump_test_helpers.c
12+
$(OUTPUT)/coredump_socket_test: coredump_test_helpers.c
13+
$(OUTPUT)/coredump_socket_protocol_test: coredump_test_helpers.c

0 commit comments

Comments
 (0)