From: "Eric W. Biederman" <ebiederm@xmission.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Andy Lutomirski <luto@kernel.org>, Kees Cook <kees@kernel.org>,
Kusaram Devineni <kusaram@devineni.in>,
Peter Zijlstra <peterz@infradead.org>,
Thomas Gleixner <tglx@kernel.org>,
Will Drewry <wad@chromium.org>,
linux-kernel@vger.kernel.org, Oleg Nesterov <oleg@redhat.com>
Subject: [PATCH 04/11] signal: Move stopping for the coredump from do_exit into get_signal
Date: Fri, 26 Jun 2026 11:55:38 -0500 [thread overview]
Message-ID: <871pdt9r79.fsf_-_@email.froward.int.ebiederm.org> (raw)
In-Reply-To: <87o6gx9rc4.fsf@email.froward.int.ebiederm.org> (Eric W. Biederman's message of "Fri, 26 Jun 2026 11:52:43 -0500")
Stopping to participate in a coredump from a kernel oops makes no
sense and is actively dangerous because the kernel is known to be
broken. Considering to stop in a coredump from a kernel thread exit
is silly because userspace coredumps are not generated from kernel
threads. Not stopping for a coredump in exit(2) and exit_group(2) and
related userspace exits that call do_exit or do_group_exit directly is
the current behavior of the code as the PF_SIGNALED test in
coredump_task_exit attests.
Since only tasks that pass through get_signal and set PF_SIGNALED can
join coredumps move stopping for coredumps into get_signal, where the
PF_SIGNALED test is unnecessary. This avoids even the potential of
stopping for coredumps in the silly or dangerous places.
This can be seen to be safe by examining the few places that call do_exit:
- get_signal calling do_group_exit
Called by get_signal to terminate the userspace process. As stopping
for the coredump happens now happens in get_signal the code will
continue to participate in the coredump.
- exit_group(2) calling do_group_exit
If a thread calls exit_group(2) while another thread in the same process
is performing a coredump there is a race. The thread that wins the
race will take the lock and set SIGNAL_GROUP_EXIT. If it is the
thread that called do_group_exit then zap_threads will return -EAGAIN
and no coredump will be generated. If it is the thread that is
coredumping that wins the race, the task that called do_group_exit
will exit gracefully with an error code before the coredump begins.
Having a single thread exit just before the coredump starts is not
ideal as the semantics make no sense. (Did the group exit happen
before the coredump or did the coredump happen before the group
exit?).
Eventually I intend for group exits to flow through get_signal and
this silliness will no longer be possible. Until then the current
behavior when this race occurs is maintained.
- io_uring
Called after get_signal returns to terminate the I/O worker thread
(essentially a userspace thread that only runs kernel code) so that
additional cleanup code can be run before do_exit. As get_signal is
called the prior to do_exit code will continue to participate in the
coredump.
- make_task_dead
Called on an unhandled kernel or hardware failure. As the failure
is unhandled any extra work has the potential to make the failure worse
so being part of a coredump is not appropriate.
- kthread_exit
Called to terminate a kernel thread as such coredumps do not exist.
- call_usermodehelper_exec_async
Called to terminate a kernel thread if kerenel_execve fails, as it is a
kernel thread coredumps do not exist.
- reboot, seeccomp
For these calls of do_exit() they are semantically direct calls of
exit(2) today. As do_exit() does not synchronize with siglock there
is no logical race between a coredump killing the thread and these
threads exiting. These threads logically exit before the coredump
happens. This is also the current behavior so there is nothing to
be concerned about with respect to userspsace semantics or
regresssions.
Moving the coredump stop for userspace threads that did not dequeue
the coredumping signal from from do_exit into get_signal in general is
safe, because the coredump in the single threaded case completely
happens in get_signal. The code movement ensures that a
multi-threaded coredump will not have any issues because the
additional threads stop after some amount of cleanup has been done.
The coredump code is robust to all kinds of userspace changes
happening in parallel as multiple processes can share a mm. This
makes the it safe to perform the coredump before the io_uring cleanup
happens as io_uring can't do anything another process sharing the mm
would not be doing.
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
---
fs/coredump.c | 25 ++++++++++++++++++++++++-
include/linux/coredump.h | 2 ++
kernel/exit.c | 35 +++++++----------------------------
kernel/signal.c | 5 +++++
mm/oom_kill.c | 2 +-
5 files changed, 39 insertions(+), 30 deletions(-)
diff --git a/fs/coredump.c b/fs/coredump.c
index bb6fdb1f458e..96801792a80e 100644
--- a/fs/coredump.c
+++ b/fs/coredump.c
@@ -521,6 +521,29 @@ static int zap_threads(struct task_struct *tsk,
return nr;
}
+void coredump_join(struct core_state *core_state)
+{
+ /* Stop and join the in-progress coredump */
+ struct core_thread self;
+
+ self.task = current;
+ self.next = xchg(&core_state->dumper.next, &self);
+ /*
+ * Implies mb(), the result of xchg() must be visible
+ * to core_state->dumper.
+ */
+ if (atomic_dec_and_test(&core_state->nr_threads))
+ complete(&core_state->startup);
+
+ for (;;) {
+ set_current_state(TASK_IDLE|TASK_FREEZABLE);
+ if (!self.task) /* see coredump_finish() */
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+}
+
static int coredump_wait(int exit_code, struct core_state *core_state)
{
struct task_struct *tsk = current;
@@ -567,7 +590,7 @@ static void coredump_finish(bool core_dumped)
next = curr->next;
task = curr->task;
/*
- * see coredump_task_exit(), curr->task must not see
+ * see coredump_join(), curr->task must not see
* ->task == NULL before we read ->next.
*/
smp_mb();
diff --git a/include/linux/coredump.h b/include/linux/coredump.h
index 68861da4cf7c..c183c95f9063 100644
--- a/include/linux/coredump.h
+++ b/include/linux/coredump.h
@@ -43,6 +43,7 @@ extern int dump_emit(struct coredump_params *cprm, const void *addr, int nr);
extern int dump_align(struct coredump_params *cprm, int align);
int dump_user_range(struct coredump_params *cprm, unsigned long start,
unsigned long len);
+extern void coredump_join(struct core_state *core_state);
extern void vfs_coredump(const kernel_siginfo_t *siginfo);
/*
@@ -63,6 +64,7 @@ extern void vfs_coredump(const kernel_siginfo_t *siginfo);
#define coredump_report_failure(fmt, ...) __COREDUMP_PRINTK(KERN_WARNING, fmt, ##__VA_ARGS__)
#else
+extern inline void coredump_join(struct core_state *core_state) {}
static inline void vfs_coredump(const kernel_siginfo_t *siginfo) {}
#define coredump_report(...)
diff --git a/kernel/exit.c b/kernel/exit.c
index 4bfecf2a510d..20dfa8b2101f 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -421,32 +421,6 @@ kill_orphaned_pgrp(struct task_struct *tsk, struct task_struct *parent)
}
}
-static void coredump_task_exit(struct task_struct *tsk,
- struct core_state *core_state)
-{
- struct core_thread self;
-
- self.task = tsk;
- if (self.task->flags & PF_SIGNALED)
- self.next = xchg(&core_state->dumper.next, &self);
- else
- self.task = NULL;
- /*
- * Implies mb(), the result of xchg() must be visible
- * to core_state->dumper.
- */
- if (atomic_dec_and_test(&core_state->nr_threads))
- complete(&core_state->startup);
-
- for (;;) {
- set_current_state(TASK_IDLE|TASK_FREEZABLE);
- if (!self.task) /* see coredump_finish() */
- break;
- schedule();
- }
- __set_current_state(TASK_RUNNING);
-}
-
#ifdef CONFIG_MEMCG
/* drops tasklist_lock if succeeds */
static bool __try_to_set_owner(struct task_struct *tsk, struct mm_struct *mm)
@@ -889,8 +863,13 @@ static void synchronize_group_exit(struct task_struct *tsk, long code)
core_state = signal->core_state;
spin_unlock_irq(&sighand->siglock);
- if (unlikely(core_state))
- coredump_task_exit(tsk, core_state);
+ /*
+ * Decrement ->nr_threads and possibly complete
+ * core_state->startup to politely skip participating in any
+ * pending coredumps.
+ */
+ if (unlikely(core_state) && atomic_dec_and_test(&core_state->nr_threads))
+ complete(&core_state->startup);
}
void __noreturn do_exit(long code)
diff --git a/kernel/signal.c b/kernel/signal.c
index d111b779cbdb..c211b520982f 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -2864,6 +2864,7 @@ bool get_signal(struct ksignal *ksig)
for (;;) {
bool group_exit_needed = false;
+ struct core_state *core_state;
struct k_sigaction *ka;
enum pid_type type;
int exit_code = 0;
@@ -3022,6 +3023,7 @@ bool get_signal(struct ksignal *ksig)
}
}
fatal:
+ core_state = signal->core_state;
spin_unlock_irq(&sighand->siglock);
if (unlikely(cgroup_task_frozen(current)))
cgroup_leave_frozen(true);
@@ -3041,6 +3043,9 @@ bool get_signal(struct ksignal *ksig)
* that value and ignore the one we pass it.
*/
vfs_coredump(&ksig->info);
+ } else if (core_state) {
+ /* Wait for the coredump to happen */
+ coredump_join(core_state);
}
/*
diff --git a/mm/oom_kill.c b/mm/oom_kill.c
index 5f372f6e26fa..ff9d59963561 100644
--- a/mm/oom_kill.c
+++ b/mm/oom_kill.c
@@ -840,7 +840,7 @@ static inline bool __task_will_free_mem(struct task_struct *task)
/*
* A coredumping process may sleep for an extended period in
- * coredump_task_exit(), so the oom killer cannot assume that
+ * get_signal(), so the oom killer cannot assume that
* the process will promptly exit and release memory.
*/
if (sig->core_state)
--
2.41.0
next prev parent reply other threads:[~2026-06-26 16:55 UTC|newest]
Thread overview: 65+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-19 13:27 [PATCH v2 1/3] signal: change force_sig_info_to_task() to call __send_signal_locked() Oleg Nesterov
2026-06-19 13:27 ` [PATCH v2 2/3] signal: turn the "bool force" arg of __send_signal_locked() into "int flags" Oleg Nesterov
2026-07-06 13:23 ` Bradley Morgan
2026-06-19 13:28 ` [PATCH v2 3/3] signal: fix evasion of SA_IMMUTABLE signals Oleg Nesterov
2026-07-06 13:23 ` Bradley Morgan
2026-06-26 16:52 ` [PATCH 0/11] Short circuit delivery for coredump signals Eric W. Biederman
2026-06-26 16:54 ` [PATCH 01/11] signal: Compute the exit_code in get_signal Eric W. Biederman
2026-06-26 16:54 ` [PATCH 02/11] signal: In get_signal call do_exit when it is unnecessary to shoot down threads Eric W. Biederman
2026-06-26 16:55 ` [PATCH 03/11] signal: Bring down all threads when handling a non-coredump fatal signal Eric W. Biederman
2026-06-26 16:55 ` Eric W. Biederman [this message]
2026-06-26 16:56 ` [PATCH 05/11] signal: Move audit_core_dumps from do_coredump into get_signal Eric W. Biederman
2026-06-26 16:57 ` [PATCH 06/11] coredump: In zap_threads complete startup if there is no need to wait Eric W. Biederman
2026-06-26 16:57 ` [PATCH 07/11] signal: Use the thread killing in get_signal for coredumps Eric W. Biederman
2026-06-26 16:58 ` [PATCH 08/11] exit: Make do_group_exit static Eric W. Biederman
2026-06-26 16:59 ` [PATCH 09/11] signal: Dequeue fatal signals Eric W. Biederman
2026-06-26 16:59 ` [PATCH 10/11] signal: Short circuit deliver coredump signals Eric W. Biederman
2026-06-26 17:00 ` [PATCH 11/11] signal: Remove SA_IMMUTABLE Eric W. Biederman
2026-06-28 14:29 ` [PATCH 0/11] Short circuit delivery for coredump signals Oleg Nesterov
2026-06-29 6:22 ` Eric W. Biederman
2026-06-29 17:45 ` Eric W. Biederman
2026-07-02 10:36 ` Oleg Nesterov
2026-07-03 20:16 ` Eric W. Biederman
2026-07-05 15:54 ` Oleg Nesterov
2026-07-06 9:21 ` Christian Brauner
2026-07-06 12:30 ` Eric W. Biederman
2026-07-06 15:58 ` Christian Brauner
2026-07-03 21:35 ` [PATCH v2 00/14] " Eric W. Biederman
2026-07-03 21:36 ` [PATCH 01/14] signal: Generalize posixtimer_queue_sigqueue into enqueue_signal Eric W. Biederman
2026-07-06 12:54 ` Bradley Morgan
2026-07-07 11:21 ` Oleg Nesterov
2026-07-07 12:23 ` Eric W. Biederman
2026-07-03 21:37 ` [PATCH 02/14] signal: Factor out sig_blocked from sig_ignored Eric W. Biederman
2026-07-06 13:01 ` Bradley Morgan
2026-07-03 21:37 ` [PATCH 03/14] signal: More accurate ignoring of signals based on sig_can_short_circuit Eric W. Biederman
2026-07-06 13:02 ` Bradley Morgan
2026-07-03 21:38 ` [PATCH 04/14] signal: Use sig_can_short_circuit to improve fatal signal delivery Eric W. Biederman
2026-07-06 13:03 ` Bradley Morgan
2026-07-03 21:39 ` [PATCH 05/14] signal: Compute the exit_code in get_signal Eric W. Biederman
2026-07-06 13:03 ` Bradley Morgan
2026-07-03 21:39 ` Eric W. Biederman
2026-07-03 21:40 ` [PATCH 06/14] signal: In get_signal call do_exit when it is unnecessary to shoot down threads Eric W. Biederman
2026-07-06 13:04 ` Bradley Morgan
2026-07-03 21:40 ` [PATCH 07/14] signal: Bring down all threads when handling a non-coredump fatal signal Eric W. Biederman
2026-07-06 13:05 ` Bradley Morgan
2026-07-03 21:41 ` [PATCH 08/14] signal: Move stopping for the coredump from do_exit into get_signal Eric W. Biederman
2026-07-06 13:08 ` Bradley Morgan
2026-07-03 21:41 ` [PATCH 09/14] signal: Move audit_core_dumps from do_coredump " Eric W. Biederman
2026-07-06 13:08 ` Bradley Morgan
2026-07-03 21:42 ` [PATCH 10/14] coredump: In zap_threads complete startup if there is no need to wait Eric W. Biederman
2026-07-06 13:09 ` Bradley Morgan
2026-07-03 21:43 ` [PATCH 11/14] signal: Use the thread killing in get_signal for coredumps Eric W. Biederman
2026-07-06 13:13 ` Bradley Morgan
2026-07-03 21:43 ` [PATCH 12/14] exit: Make do_group_exit static Eric W. Biederman
2026-07-06 13:14 ` Bradley Morgan
2026-07-03 21:44 ` [PATCH 13/14] signal: Dequeue fatal signals Eric W. Biederman
2026-07-06 13:16 ` Bradley Morgan
2026-07-03 21:44 ` [PATCH 14/14] signal: Short circuit deliver coredump signals Eric W. Biederman
2026-07-06 13:17 ` Bradley Morgan
2026-07-07 11:13 ` [PATCH v2 00/14] Short circuit delivery for " Oleg Nesterov
2026-07-07 12:15 ` Oleg Nesterov
2026-07-09 11:59 ` Eric W. Biederman
2026-07-10 14:58 ` Oleg Nesterov
2026-07-09 12:28 ` Eric W. Biederman
2026-07-06 13:18 ` [PATCH 0/11] " Bradley Morgan
2026-07-06 13:23 ` [PATCH v2 1/3] signal: change force_sig_info_to_task() to call __send_signal_locked() Bradley Morgan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=871pdt9r79.fsf_-_@email.froward.int.ebiederm.org \
--to=ebiederm@xmission.com \
--cc=akpm@linux-foundation.org \
--cc=kees@kernel.org \
--cc=kusaram@devineni.in \
--cc=linux-kernel@vger.kernel.org \
--cc=luto@kernel.org \
--cc=oleg@redhat.com \
--cc=peterz@infradead.org \
--cc=tglx@kernel.org \
--cc=wad@chromium.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome