mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Frederic Weisbecker <frederic@kernel.org>
To: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@kernel.org>,
	LKML <linux-kernel@vger.kernel.org>,
	"Cc: Hyunwoo Kim" <imv4bel@gmail.com>,
	Oleg Nesterov <oleg@redhat.com>,
	Christian Brauner <brauner@kernel.org>,
	John Stultz <jstultz@google.com>, Ingo Molnar <mingo@kernel.org>,
	Alexander Viro <viro@zeniv.linux.org.uk>,
	"Eric W. Biederman" <ebiederm@xmission.com>,
	stable@vger.kernel.org
Subject: Re: [patch V2 1/8] signal: Prevent exec() race
Date: Wed, 9 Sep 2026 14:13:11 +0200	[thread overview]
Message-ID: <aqFNV4k6DcbyxYCe@localhost.localdomain> (raw)
In-Reply-To: <20260909095518.GL776954@noisy.programming.kicks-ass.net>

Le Wed, Sep 09, 2026 at 11:55:18AM +0200, Peter Zijlstra a écrit :
> On Wed, Sep 09, 2026 at 11:08:31AM +0200, Thomas Gleixner wrote:
> > On Wed, Sep 09 2026 at 10:04, Peter Zijlstra wrote:
> > > On Tue, Sep 08, 2026 at 12:15:21PM +0200, Frederic Weisbecker wrote:
> > > Let me try and have a go :-)
> > >
> > >
> > > do_exit()				de_thread() 			posix_timer_fn()
> > >   exit_signal()				  LOCK siglock 			  posix_timer_send_sigqueue()
> > >     LOCK siglock			  UNLOCK siglock 		    t = posix_timer_get_target()
> > >     tsk->flags |= PF_EXITING; 						    LOCK siglock
> > >     UNLOCK siglock			  if (!thread_group_leader) 	      if (!list_empty(sigqueue))
> > >     					    LOCK tasklist_lock
> > >     flush_sigqueue_list();		    if (leader->exit_state)
> > > 					      break;
> > >   ...                                       transfer_pid()
> > > 					    UNLOCK tasklist_lock
> > >   exit_notify()
> > >     LOCK tasklist_lock
> > >     tsk->exit_state = EXIT_ZOMBIE;
> > >     UNLOCK tasklist_lock
> > >
> > >
> > >
> > > Then there is indeed nothing that makes sure posix_timer_fn() sees
> > > sigqueue updates done by do_exit(), because those are ordered by
> > > tasklist_lock, but posix_timer_fn() doesn't care about that.
> > 
> > That's irrelevant because in the above scenario posix_timer_fn() 't'
> > points to the exiting old leader (on the left) because the PID store has
> > not happened yet and it therefore observes PF_EXITING on it so it won't
> > touch the sigqueue. Note, that setting and checking PF_EXITING is
> > serialized by sighand lock, so this is fine.
> 
> There is nothing that constraints the 3rd column from happening before,
> it could happen after transfer_pid().
> 
> > > The easy solution would probably be to do transfer_pid() while holding
> > > siglock?
> > 
> > That'd be only relevant for the situation Frederic is concerned about,
> > i.e. the case where the third party observes the TID swap.
> 
> That is the case I was aiming at.
> 
> > Because with that visible 't' in posix_timer_send_sigqueue() won't be
> > old_leader, which has PF_EXITING set, it will be new_leader which has it
> > not set.
> 
> Same as above, there is nothing constraining the 3rd column from sliding
> up or down. If it manages to see the new_leader, I don't see why it
> would see the sigqueue flush.
> 
> > So Frederic is concerned that posix_timer_send_sigqueue() can observe
> > the PID store but not observe the sigqueue stores.
> > 
> > I argue that's not possible:
> > 
> > A:  sigqueue stores
> > 
> > B:   AQUIRE tasklist
> > 
> > C:   exit_state store
> > 
> > D:   RELEASE tasklist
> >      // sigqueue and exit_state stores become globally visible
> >  ------------------------------------------------------------------------
> > 
> > E                               ACQUIRE tasklist
> >  ------------------------------------------------------------------------
> > F                                if (exit_state)
> > 				   swap_pid()
> > G                                     STORE_PID
> > 
> > 				// The PID store can become visible in the
> > 				// system right here so F can observe them before
> > 				// RELEASE tasklist
> 
> The STORE_PID is not a STORE_RELEASE.
> 
> > H                                                             READ PID
> 
> And this READ is not LOAD_AQUIRE; although the LOCK siglock is probably
> sufficient here. The READ MUST happen before LOCK siglock by means of
> data dependency, and then the LOCK will constrain later loads.
> 
> > 							      ....
> > I                                		              ACQUIRE siglock
> > 
> > After #A the sigqueue stores are maybe visible
> > 
> > After #C the exit_state store is maybe visible
> > 
> > After #D both #A and #C are guaranteed to be visible to _ALL_ agents in
> > the system and cannot become magically become invisible after that
> > point.
> 
> No, that is not in fact how Power (or ARM) works AFAICT. Memory ordering
> is not global. It is entirely possible some CPUs see a store while
> others do not.
> 
> The only guarantee here is that IF you acquire tasklist_lock (you
> observe the store that unlocked it), you will also observe preceding
> stores.  But since the posix_timer_fn() column does not in fact observe
> or care about tasklist_lock, there is no ordering.
> 
> > The new leader cannot swap PIDs before acquiring task list lock and
> > before it observed exit_state != 0 under it. That's fully serialized
> > against the old leader as both hold task list lock for their operations.
> > 
> > #F creates a control dependency, so if the new leader acquires task list
> >    lock before the old it will observe 0, drop the lock and wait. No PID
> >    store obviously.
> 
> A control dependency only ensure *that* CPU will complete the exit_state
> load before the store, it is a local LOAD->STORE ordering.
> 
> > #G can be come visible immediately but is only guaranteed to be visible
> >    globally at the RELEASE of tasklist lock.
> 
> Nope, not at all. Can be randomly visible to random sets of CPUs.
> 
> > #H can only observe the PID store after the store actually happened in
> >    #G. So it either reads the original PID or the swapped PID.
> 
> Sure. But that has no bearing on if it sees the sigqueue stores at A.
> 
> > #I is not really relevant for this. It's only relevant for PF_EXITING
> >    and other stuff which is directly protected by it. And it does not
> >    matter whether it locks the old or the new sighand.
> > 
> > Now let's look at the full chain and what can possibly be visible or not
> > and when:
> > 
> > #A can trickle into the tasklist held section, but not after #D.
> 
> Yup.
> 
> > #C cannot be reordered against #B and #D
> 
> Agreed.
> 
> > #A is therefore guaranteed to be globally visible _before_ new leader
> >    observes exit_state != 0 in #F under task list lock
> 
> Nope, A is therefore visible if you acquire tasklist_lock, specifically,
> when you observe the store from D. And only if that matching LOAD is a
> LOAD-ACQUIRE, such that subsequent loads are forced to be later.
> 
> > #G cannot be reordered against #F and obviously not against #E either.
> 
> Indeed.
> 
> >    It can become visible at any point after the store, but as argued
> >    above that visibility can't be reordered before #A (sigqueue stores)
> >    became visible.
> 
> Let G' be the unnamed RELEASE after G.
> 
> Now, I have deleted and rewritten this tail end at least twice now. And
> I *think* I'm agreeing with you. Let me explain:
> 
> It all hinges on D-E and H-I.
> 
> D-E is a UNLOCK+LOCK hand-over, which is not quite the same as
> RELEASE+ACQUIRE. Specifically, we have:
> 
>   RELEASE+ACQUIRE: RCpc,  only the CPUs involved agree on the ordering
>   UNLOCK+LOCK:     RCtso, the hand-over is store-ordering
> 
> So while earlier I was arguing with RCpc in mind, in which case D-E
> completely goes away and we can consider B-G' to be one big critical
> section from the PoV of a third CPU (our posix_timer_fn() one). In this
> case we can push A down and G up and have them cross.
> 
> *However*, since these are locks, we actually have D-E be UNLOCK+LOCK,
> which is RCtso and that *does* impose store order, so A stores must
> happen before G stores
> 
> Combine with H-I, which has a data dependency from the LOAD to the LOCK
> and thereby constraints later LOADs, those sigqueue loads that come
> after I must in fact observe the A stores.

I didn't know about all those UNLOCK+LOCK properties. Well,
I know that UNLOCK+LOCK on the same lock, or on different locks
but the same CPU, equals smp_mb() except on powerpc. Which is why
we have smp_mb__after_unlock_lock(). But what you describe is quite
different.

Is this something that we should expect litmus to modelize?

Because the following doesn't verify that:
---
C MP+farfetched

{}

P0(int *next, int *prev, int *exit_state, spinlock_t *tasklist_lock)
{
	// list_del_init()
	WRITE_ONCE(*next, 1);
	WRITE_ONCE(*prev, 1);
	// exit_notify()
	spin_lock(tasklist_lock);
	WRITE_ONCE(*exit_state, 1);
	spin_unlock(tasklist_lock);
}

P1(int *exit_state, int *pid, spinlock_t *tasklist_lock)
{
	int r0;

	// de_thread()
	spin_lock(tasklist_lock);
	r0 = READ_ONCE(*exit_state);
	if (r0 == 1) {
		// exchange_tids()
		WRITE_ONCE(*pid, 1);
	}
	spin_unlock(tasklist_lock);
}

P2(int *next, int *prev, int *pid, spinlock_t *sighand)
{
	int r0;
	int r1;
	// get target
	r0 = READ_ONCE(*pid);
	spin_lock(sighand);
	// queue signal
	r1 = READ_ONCE(*next);
	if (r1 == 0)
		WRITE_ONCE(*prev, 2);
	spin_unlock(sighand);
}

exists (prev=1 /\ 2:r0=1) (* Bad outcome. *)
---
herd7 -conf linux-kernel.cfg ~/farfetched.litmus 
Test MP+farfetched Allowed
States 4
2:r0=0; [prev]=1;
2:r0=0; [prev]=2;
2:r0=1; [prev]=1;
2:r0=1; [prev]=2;
Ok
Witnesses
Positive: 2 Negative: 7
Condition exists ([prev]=1 /\ 2:r0=1)
Observation MP+farfetched Sometimes 2 7
Time MP+farfetched 0.02
Hash=a44733c870613a81ae096a93babe215

  parent reply	other threads:[~2026-09-09 12:13 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-05 18:58 [patch V2 0/8] exec/exit: POSIX timer related bugfixes and related cleanups Thomas Gleixner
2026-09-05 18:59 ` [patch V2 1/8] signal: Prevent exec() race Thomas Gleixner
2026-09-06 13:17   ` Oleg Nesterov
2026-09-06 22:39   ` Eric W. Biederman
2026-09-06 23:28     ` Oleg Nesterov
2026-09-07 11:26     ` Thomas Gleixner
2026-09-07 12:31   ` Frederic Weisbecker
2026-09-07 15:26     ` Thomas Gleixner
2026-09-07 20:15       ` Frederic Weisbecker
2026-09-07 22:28         ` Thomas Gleixner
2026-09-08 10:15           ` Frederic Weisbecker
2026-09-09  0:03             ` Oleg Nesterov
2026-09-09  9:17               ` Frederic Weisbecker
2026-09-09  8:04             ` Peter Zijlstra
2026-09-09  9:08               ` Thomas Gleixner
2026-09-09  9:55                 ` Peter Zijlstra
2026-09-09 10:20                   ` Peter Zijlstra
2026-09-09 11:31                   ` Thomas Gleixner
2026-09-09 12:13                   ` Frederic Weisbecker [this message]
2026-09-09 12:45                     ` Peter Zijlstra
2026-09-09 12:51                       ` Peter Zijlstra
2026-09-09 13:45                         ` Thomas Gleixner
2026-09-09 15:48                           ` Frederic Weisbecker
2026-09-09 16:00                           ` Frederic Weisbecker
2026-09-09 14:33                       ` Alan Stern
2026-09-09 14:45                       ` Frederic Weisbecker
2026-09-09 19:28                         ` Alan Stern
2026-09-09 20:49                           ` Thomas Gleixner
2026-09-09 21:11                             ` Alan Stern
2026-09-10 13:21                               ` Frederic Weisbecker
2026-09-10 13:28                                 ` Peter Zijlstra
2026-09-10 15:26                                 ` Alan Stern
2026-09-09 10:18                 ` Frederic Weisbecker
2026-09-09  9:11               ` Frederic Weisbecker
2026-09-05 18:59 ` [patch V2 2/8] exec: Cleanup POSIX timers right after de_thread() Thomas Gleixner
2026-09-06 13:21   ` Oleg Nesterov
2026-09-07 22:13   ` Frederic Weisbecker
2026-09-05 18:59 ` [patch V2 3/8] posix-timers: Move posixtimer_exec_cleanup() out of exec.c Thomas Gleixner
2026-09-10 13:50   ` Frederic Weisbecker
2026-09-05 18:59 ` [patch V2 4/8] posix-timers: Move POSIX timer group exit related code out of do_exit() Thomas Gleixner
2026-09-10 13:59   ` Frederic Weisbecker
2026-09-05 18:59 ` [patch V2 5/8] posix-cpu-timers: Move inlines out of public header Thomas Gleixner
2026-09-10 14:00   ` Frederic Weisbecker
2026-09-05 18:59 ` [patch V2 6/8] posix-cpu-timers: Use PF_EXITING to indicate exit Thomas Gleixner
2026-09-05 18:59 ` [patch V2 7/8] posix-cpu-timers: Prevent enqueueing when PF_EXITING is set Thomas Gleixner
2026-09-06 16:26   ` Oleg Nesterov
2026-09-07 12:20     ` Thomas Gleixner
2026-09-05 18:59 ` [patch V2 8/8] posix-timers: Handle exit in do_exit() completely Thomas Gleixner
2026-09-06 16:40   ` Oleg Nesterov
2026-09-07 12:27     ` Thomas Gleixner

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=aqFNV4k6DcbyxYCe@localhost.localdomain \
    --to=frederic@kernel.org \
    --cc=brauner@kernel.org \
    --cc=ebiederm@xmission.com \
    --cc=imv4bel@gmail.com \
    --cc=jstultz@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@kernel.org \
    --cc=oleg@redhat.com \
    --cc=peterz@infradead.org \
    --cc=stable@vger.kernel.org \
    --cc=tglx@kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    /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

all inboxes | Powered by JetHome®