Re: [PATCH 1/2] kernel/cobalt: Address SIGSHADOW_ACTION_HARDEN and self-hardening race condition

Florian Bezdeka <[email protected]>
Newsgroups dev.linux.lists.xenomai
Message-ID <[email protected]>
On Sat, 2026-05-16 at 17:48 +0200, Philippe Gerum wrote:
> Jan Kiszka <[email protected]> writes:
> 
> > From: Jan Kiszka <[email protected]>
> > 
> > When a remote thread is suspended, xnthread_suspend checks if it is
> > currently relaxed and then calls it into hardened state via SIGSHADOW.
> > However, this signal may race with the thread entering primary mode on
> > its own, e.g. via a syscall. In that case, the thread will be kicked out
> > of its suspension state by the pending signal, and the migrate syscall
> > it then executes will not perform the expected suspension anymore.
> > 
> > Plug this race by introducing two thread state flags, one for pending
> > XNSUSP and another one for XNHELD. After having performed the requested
> > hardening on SIGSHADOW_ACTION_HARDEN, we are now testing for those
> > pending suspension states and replay them via self-suspension as needed.
> > If a resume took place in the meantime, the state flags were cleared
> > again, and nothing will happen after the migration step.
> > 
> > Reported-by: Richard Weinberger <[email protected]>
> > Signed-off-by: Jan Kiszka <[email protected]>
> > ---
> > 
> > Didn't check yet if EVL solves this topic differently or is exposed to 
> > similar race. But given that Richard found the pattern even in Xenomai 
> > 2, it's likely worth to check EVL carefully.
> > 
> 
> Unlike v3, v4 does not use signals for internal synchronization, so it
> does not suffer that particular issue related to suspending a thread
> (no SIGSHADOW in evl). However, this issue is only a symptom of a much
> broader problem both v3 and v4 have when it comes to dealing with
> signals pending for suspended threads, and your patch won't solve it.
> 
> To illustrate this, let's say (A) and (B) are Cobalt threads running
> oob, (X) may be any in-band context:
> 
> CPU0                                          CPU1
> ----                                          ----
> 
> A: xnthread_signal(B, SIGxx)                  B: core_syscall()
>    <schedule irq_work>                              core_suspend(B, XNPEND)
> A: core_suspend(B, XNSUSP)                    B: <schedules out>
>       core_add_state(B, T_SUSP)
>    core_reschedule()
>       core_resched_ipi(CPU1)                  
> X: irq_work_handler()
>       send_sig(B, SIGxx)
>          handle_inband_event(B, INBAND_TASK_SIGNAL)
>             core_resume(B, XNPEND)
>             core_resume(B, XNSUSP)
>             core_add_state(B, XNKICKED)
>          core_reschedule()
>             core_resched_ipi(CPU1)                    
>                                               X: <ipi-handler>
>                                                     core_reschedule()
>                                               B: <resuming oob>
>                                               B: XNKICKED: prepare_for_signal()
>                                                     xnthread_relax()
>                                               B: <running in-band>
>                                               B: migrate_syscall()
>                                                  nop (XNPNDSUSP was not set)
>                                               B: <still running in-band>
> 
> Remoteness of CPUs does not even matter, e.g. with prio(A) > prio(B),
> we might observe:
> 
> CPU0
> ----
> 
> B: core_syscall()
>       core_suspend(B, XNPEND)
> B: <schedules out>
> X: send_sig(B, SIGxx)
>       handle_inband_event(B, INBAND_TASK_SIGNAL)
>          core_resume(B, XNPEND) /* B is now fully unblocked */
>          core_add_state(B, XNKICKED)
> <IRQ>: core_resume(A)
> A: <resuming oob>
> A: core_suspend(B, XNSUSP)
>       unblocked + XNKICKED set -> abort: XNSUSP ignored
> B: <resuming oob>
> B: XNKICKED: prepare_for_signal()
>       xnthread_relax()
> B: <still running in-band>
> 
> There is clearly no shortage of possible variations of the scenarios
> above especially if we bring up more complex multi-CPU configurations,
> all of them causing some sort of breakage. IOW, there is a general issue
> with signals wrt forcible suspension, not just with SIGSHADOW-based
> synchronization in xnthread_suspend() vs migrate() which in addition,
> has its own set of flaws.
> 
> A way to address this properly may require to break an ancient
> invariant, which states that a thread with any blocking bit set must not
> be linked to any runqueue. On the contrary, a way to make sure that a
> suspend request remains pending across a stage migration in order to
> deal with an in-band signal is precisely to allow that request to linger
> as long as necessary in a thread state mask, only to be considered once
> the 'kicked' flag is cleared, ignored and left pending
> otherwise. Meanwhile, that thread should be allowed to run.
> 
> I need to have a closer look, but at first glance it seems fairly
> straightforward to implement this in v4 at least.
> 
> -- 
> Philippe.

Uh... All of that sounds a bit scary...

There was a reprocuder provided by Richard on the list as well, right?
Please make sure that we integrate a test case at the same time as the
fixes. Otherwise the testcase might never be added.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.