Re: [patch V2 1/8] signal: Prevent exec() race

From: Alan Stern

Date: Fri Sep 11 2026 - 11:27:45 EST


On Fri, Sep 11, 2026 at 12:22:03PM +0200, Peter Zijlstra wrote:
> On Fri, Sep 11, 2026 at 11:58:04AM +0200, Frederic Weisbecker wrote:
> > Le Thu, Sep 10, 2026 at 03:28:43PM +0200, Peter Zijlstra a écrit :
>
> > > The way data dependencies work in this case is a LOAD->LOAD ordering.
> > > The LOAD-ACQUIRE that is part of LOCK must happen after the initial
> > > load. And then the later load is constrained by the ACQUIRE.
> > >
> > > So LOAD(B) must resolve in order to do LOAD_ACQUIRE(B->lock.value).
> >
> > Ok I must confess that's not a pattern I'm used to and therefore it's
> > still a bit counter-intuitive to me. I hope we can document that somehow
> > somewhere.
>
> One way of looking at it is the computation for the address of
> &B->lock.value.
>
> A void *r = LOAD(B); // B
> B r += offsetof(typeof(*B), lock); // &B->lock
> spin_lock(r)
> C r += offsetof(typeof(B->lock), value); // &B->lock.value
> for (;;)
> D v = LOAD_ACQUIRE(r);
> if (!v && !cmpxchg_relaxed(r, 0, 1))
> break;
> cpu_relax();
>
> Note how the LOAD_ACQUIRE() at D depends on the computation of C, and B,
> which in turn depend on the load of A.
>
> If A does not complete, B,C cannot compute the address for the load of
> D. Therefore A must come before D.

There's one subtle point worth making here, although it doesn't affect
the final result.

We have to be a little careful when talking about when a load happens,
because executing a load actually involves two separate things:

Obtaining a value from memory or cache, and

Completing (retiring) the load instruction.

These two things don't have to happen at the same time, and in the case
of speculative loads they can happen at very different times. So
whenever you talk about when a load occurs, or whether it occurs before
or after some other operation, you have to be careful about which part
of the load you're referring to.

In theory, a CPU could try to obtain a value from memory speculatively
before it knew for certain what address to load from. This would defeat
an address dependency leading to that load. As mentioned in
explanation.txt, none of the archictectures supported by Linux do this,
and the LKMM does not allow it.

However, if it did happen in the example above, the CPU could read the
value of *r at D before A had finished (say, by remembering what address
it had used the last time it executed D's load and trying to read
speculatively from that old address). But even if this did happen, the
cmpxchg_relaxed() in the line following D would not be able to execute
successfully until the CPU knew for certain what the value of *r was,
that is, until A had completed.

Alan Stern