From: Thomas Gleixner <tglx@kernel.org>
To: Oleg Nesterov <oleg@redhat.com>
Cc: Frederic Weisbecker <frederic@kernel.org>,
Hyunwoo Kim <imv4bel@gmail.com>,
brauner@kernel.org, peterz@infradead.org,
anna-maria@linutronix.de, ebiederm@xmission.com,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH] signal: Use list_del_init_careful() in flush_sigqueue()
Date: Thu, 27 Aug 2026 19:51:45 +0200 [thread overview]
Message-ID: <87h5kffo3y.ffs@fw13> (raw)
In-Reply-To: <87pkz3g39u.ffs@fw13>
On Thu, Aug 27 2026 at 14:24, Thomas Gleixner wrote:
> I'm actually tempted to move the posix timer cleanup _before_
> de_thread() and just make sure that no new timers can be created anymore.
Something like that:
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1148,6 +1148,16 @@ int begin_new_exec(struct linux_binprm *
*/
bprm->point_of_no_return = true;
+ /*
+ * This sets SIGNAL_EXEC in current::signal::flags, which prevents new
+ * POSIX timers from being created and further POSIX timer signals from
+ * being queued. It also deletes all existing POSIX timers and flushs
+ * the corresponding signals from current's and the shared pending list.
+ */
+ retval = signal_exec_start();
+ if (retval)
+ goto out;
+
/* Make this the only thread in the thread group */
retval = de_thread(me);
if (retval)
@@ -1192,14 +1202,6 @@ int begin_new_exec(struct linux_binprm *
if (retval)
goto out_unlock;
-#ifdef CONFIG_POSIX_TIMERS
- spin_lock_irq(&me->sighand->siglock);
- posix_cpu_timers_exit(me);
- spin_unlock_irq(&me->sighand->siglock);
- exit_itimers(me);
- flush_itimer_signals();
-#endif
-
/*
* Make the signal table private.
*/
@@ -1324,6 +1326,8 @@ int begin_new_exec(struct linux_binprm *
}
bprm->execfd = retval;
}
+
+ signal_exec_done();
return 0;
out_unlock:
--- a/include/linux/posix-timers.h
+++ b/include/linux/posix-timers.h
@@ -119,6 +119,7 @@ bool posixtimer_init_sigqueue(struct sig
void posixtimer_send_sigqueue(struct k_itimer *tmr);
bool posixtimer_deliver_signal(struct kernel_siginfo *info, struct sigqueue *timer_sigq);
void posixtimer_free_timer(struct k_itimer *timer);
+void posixtimer_flush_exec(void);
long posixtimer_create_prctl(unsigned long ctrl);
/* Init task static initializer */
@@ -146,6 +147,7 @@ static inline void posixtimer_rearm_itim
static inline bool posixtimer_deliver_signal(struct kernel_siginfo *info,
struct sigqueue *timer_sigq) { return false; }
static inline void posixtimer_free_timer(struct k_itimer *timer) { }
+static inline void posixtimer_flush_exec(void) { }
static inline long posixtimer_create_prctl(unsigned long ctrl) { return -EINVAL; }
#endif
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -261,6 +261,8 @@ struct signal_struct {
#define SIGNAL_STOP_STOPPED 0x00000001 /* job control stop in effect */
#define SIGNAL_STOP_CONTINUED 0x00000002 /* SIGCONT since WCONTINUED reap */
#define SIGNAL_GROUP_EXIT 0x00000004 /* group exit in progress */
+#define SIGNAL_EXEC 0x00000008 /* exec in progress */
+
/*
* Pending notifications to parent.
*/
@@ -285,6 +287,24 @@ extern void ignore_signals(struct task_s
extern void flush_signal_handlers(struct task_struct *, int force_default);
extern int dequeue_signal(sigset_t *mask, kernel_siginfo_t *info, enum pid_type *type);
+static inline int signal_exec_start(void)
+{
+ scoped_guard(spinlock_irq, ¤t->sighand->siglock) {
+ /* Is a group action in progress already? */
+ if (current->signal->flags & (SIGNAL_GROUP_EXIT | SIGNAL_EXEC))
+ return -EAGAIN;
+ current->signal->flags |= SIGNAL_EXEC;
+ }
+ posixtimer_flush_exec();
+ return 0;
+}
+
+static inline void signal_exec_done(void)
+{
+ guard(spinlock_irq)(¤t->sighand->siglock);
+ current->signal->flags &= SIGNAL_EXEC;
+}
+
static inline int kernel_dequeue_signal(void)
{
struct task_struct *task = current;
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1991,6 +1991,23 @@ void posixtimer_send_sigqueue(struct k_i
return;
/*
+ * If the process is in the middle of exec(), don't queue signals as the
+ * posix timers of this process are not longer accessible and about to
+ * be removed. This prevents a race between queueing the signal on a
+ * exiting former thread group leader in case of a non-leader exec().
+ * Aside of that it makes no sense to queue anything now when it has to
+ * be flushed a split second later anyway.
+ *
+ * As this conditional is required just use the opportunity and check
+ * for a group exit too, where queueing signals is equally pointless.
+ *
+ * If the signal is already pending or on the ignore list, then nothing
+ * changes and the final posix timer and signal cleanup will handle them.
+ */
+ if (unlikely(t->signal->flags & (SIGNAL_GROUP_EXIT | SIGNAL_EXEC)))
+ goto unlock;
+
+ /*
* Update @tmr::sigqueue_seq for posix timer signals with sighand
* locked to prevent a race against dequeue_signal().
*/
@@ -2081,6 +2098,7 @@ void posixtimer_send_sigqueue(struct k_i
result = TRACE_SIGNAL_DELIVERED;
out:
trace_signal_generate(sig, &q->info, t, tmr->it_pid_type != PIDTYPE_PID, result);
+unlock:
unlock_task_sighand(t, &flags);
}
--- a/kernel/time/posix-timers.c
+++ b/kernel/time/posix-timers.c
@@ -462,6 +462,24 @@ static int common_timer_create(struct k_
return 0;
}
+static bool timer_set_valid(struct k_itimer *new_timer)
+{
+ guard(spinlock)(¤t->sighand->siglock);
+
+ /* If there is a group action in progress, fail */
+ if (current->signal->flags & (SIGNAL_GROUP_EXIT | SIGNAL_EXEC))
+ return false;
+
+ /*
+ * new_timer::it_signal contains the signal pointer with
+ * bit 0 set, which makes it invalid for syscall operations.
+ * Store the unmodified signal pointer to make it valid.
+ */
+ WRITE_ONCE(new_timer->it_signal, current->signal);
+ hlist_add_head_rcu(&new_timer->list, ¤t->signal->posix_timers);
+ return true;
+}
+
/* Create a POSIX.1b interval timer. */
static int do_timer_create(clockid_t which_clock, struct sigevent *event,
timer_t __user *created_timer_id)
@@ -552,20 +570,33 @@ static int do_timer_create(clockid_t whi
* sighand::siglock is required to protect signal::posix_timers.
*/
scoped_guard (spinlock_irq, &new_timer->it_lock) {
- guard(spinlock)(¤t->sighand->siglock);
+ if (timer_set_valid(new_timer)) {
+ /*
+ * After unlocking @new_timer is subject to concurrent removal and
+ * cannot be touched anymore
+ */
+ return 0;
+ }
+
/*
- * new_timer::it_signal contains the signal pointer with
- * bit 0 set, which makes it invalid for syscall operations.
- * Store the unmodified signal pointer to make it valid.
+ * A group exit or exec() is in progress. The timer has not been
+ * marked valid for syscall operations, so it can't be armed or
+ * firing and the sigqueue is guaranteed to be not queued
+ * anywhere.
+ *
+ * This still needs to invoke kc::timer_del() so that the
+ * underlying clock implementation can do their cleanups if
+ * required. E.g. POSIX CPU timers need to put the reference on
+ * timer::it::cpu::pid.
+ *
+ * As the timer cannot be firing kc::timer_del() cannot fail
+ * with TIMER_RETRY.
*/
- WRITE_ONCE(new_timer->it_signal, current->signal);
- hlist_add_head_rcu(&new_timer->list, ¤t->signal->posix_timers);
+ WARN_ON_ONCE(kc->timer_del(new_timer));
+ /* Fall through and unhash the timer */
+ error = -ESRCH;
}
- /*
- * After unlocking @new_timer is subject to concurrent removal and
- * cannot be touched anymore
- */
- return 0;
+
out:
posix_timer_unhash_and_free(new_timer);
return error;
@@ -1120,6 +1151,25 @@ void exit_itimers(struct task_struct *ts
}
}
+/* Invoked by the task which runs exec() via signal_exec_start() */
+void posixtimer_flush_exec(void)
+{
+ /*
+ * Contrary to do_exit() don't invoke posix_cpu_timers_exit(). The
+ * timers are all mopped up in exit_itimers() right away and the
+ * SIGNAL_EXEC flag ensures that no new ones can be created.
+ */
+ exit_itimers(current);
+
+ /*
+ * Now that all timers are gone flush queued POSIX timer signals in
+ * current::pending and current::signal::shared_pending. If this is a
+ * multi-threaded exec() then the other tasks will flush their
+ * task::pending signals in release_task().
+ */
+ flush_itimer_signals();
+}
+
SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock,
const struct __kernel_timespec __user *, tp)
{
next prev parent reply other threads:[~2026-08-27 17:51 UTC|newest]
Thread overview: 49+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-22 5:37 [PATCH] signal: Use list_del_init_careful() in flush_sigqueue() Hyunwoo Kim
2026-08-22 10:27 ` Bradley Morgan
2026-08-23 12:47 ` Oleg Nesterov
2026-08-24 2:53 ` Hyunwoo Kim
2026-08-24 8:28 ` Oleg Nesterov
2026-08-24 8:04 ` Thomas Gleixner
2026-08-24 9:45 ` Thomas Gleixner
2026-08-24 11:02 ` Oleg Nesterov
2026-08-24 11:54 ` Oleg Nesterov
2026-08-24 13:59 ` Frederic Weisbecker
2026-08-24 14:29 ` Oleg Nesterov
2026-08-25 16:58 ` Thomas Gleixner
2026-08-25 18:53 ` Oleg Nesterov
2026-08-25 19:58 ` Thomas Gleixner
2026-08-26 9:36 ` Oleg Nesterov
2026-08-26 19:19 ` Thomas Gleixner
2026-08-26 19:32 ` Oleg Nesterov
2026-08-27 3:29 ` Eric W. Biederman
2026-08-27 9:35 ` Thomas Gleixner
2026-08-27 18:43 ` Eric W. Biederman
2026-08-27 22:56 ` Thomas Gleixner
2026-08-30 18:19 ` Thomas Gleixner
2026-08-30 22:04 ` Eric W. Biederman
2026-08-31 9:53 ` Thomas Gleixner
2026-08-31 10:50 ` [PATCH] signal: Prevent exec() race Thomas Gleixner
2026-08-31 11:35 ` David Laight
2026-08-31 12:44 ` Oleg Nesterov
2026-09-01 12:49 ` Thomas Gleixner
2026-08-31 12:52 ` Frederic Weisbecker
2026-09-01 12:55 ` Thomas Gleixner
2026-09-01 13:27 ` Frederic Weisbecker
2026-09-01 15:14 ` Thomas Gleixner
2026-08-31 15:26 ` Eric W. Biederman
2026-09-01 13:35 ` Thomas Gleixner
2026-09-01 17:21 ` Eric W. Biederman
2026-09-01 18:40 ` [PATCH V2] " Thomas Gleixner
2026-09-02 10:28 ` Oleg Nesterov
2026-09-02 10:45 ` Oleg Nesterov
2026-09-03 6:09 ` Thomas Gleixner
2026-09-02 11:23 ` Oleg Nesterov
2026-09-02 14:19 ` Oleg Nesterov
2026-09-02 15:39 ` Eric W. Biederman
2026-09-02 17:08 ` Oleg Nesterov
2026-09-03 6:42 ` Thomas Gleixner
2026-09-03 7:29 ` Oleg Nesterov
2026-08-27 12:24 ` [PATCH] signal: Use list_del_init_careful() in flush_sigqueue() Thomas Gleixner
2026-08-27 17:51 ` Thomas Gleixner [this message]
2026-08-24 12:11 ` Thomas Gleixner
2026-08-24 16:31 ` Frederic Weisbecker
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=87h5kffo3y.ffs@fw13 \
--to=tglx@kernel.org \
--cc=anna-maria@linutronix.de \
--cc=brauner@kernel.org \
--cc=ebiederm@xmission.com \
--cc=frederic@kernel.org \
--cc=imv4bel@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=oleg@redhat.com \
--cc=peterz@infradead.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.