All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/3] panic: fix the panic_force_cpu redirect races
@ 2026-07-14 17:30 Bradley Morgan
  2026-07-14 17:31 ` [PATCH v4 1/3] panic: fix redirect CPU race in panic_try_force_cpu() Bradley Morgan
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Bradley Morgan @ 2026-07-14 17:30 UTC (permalink / raw)
  To: akpm; +Cc: pmladek, feng.tang, sashiko-bot, stable, linux-kernel, include

Sorry for sending this before the earlier threads fully settled. I am
posting the series now because everything is in one place, a single
thread of three patches, instead of replies spread across the old
threads, which is easier to follow than a few separate discussions.

The panic_force_cpu= parameter redirects a panic to a specific CPU so
the crash kernel runs there. The redirect code in
panic_try_force_cpu() had two races and one ordering bug, all found by
Sashiko. This series closes them.

Patch 1: fix the redirect CPU race.

The redirect is gated by an atomic cmpxchg on panic_redirect_cpu, so
only one CPU sends the redirect IPI. The cmpxchg loser used to return
false and fall through into vpanic(), where it could win panic_try_start()
and run crash_kexec on the wrong CPU before the target ever received the
IPI. The loser has to stop. It cannot just return true, though, because
panic_try_force_cpu() can be called twice on the same CPU (nested NMI
during the message formatting, before the IPI is sent), and a blind
stop on that second call would abandon the panic with no IPI sent. The
loser now returns true to stop, unless it is reentering on the same CPU
(old_cpu == this_cpu), in which case it returns false and falls through.

Patch 1 also fixes the panic_in_progress() guard. We must never redirect
when panic_cpu is already taken, so the guard stays. But it now returns
true (stop) when the panic is on another CPU and false (proceed) when it
is on this CPU, instead of returning false either way.

The two races side by side, two non target CPUs A and B (target is C),
then a reentry on the redirect winner:

             cpu A                          cpu B
          ----------                     ----------
    panic_try_force_cpu()                panic_try_force_cpu()
        cmpxchg wins                         cmpxchg fails
        IPI -> C                             return false  <- old BUG
        return true                      panic_try_start() wins
    panic_smp_self_stop()                 __crash_kexec() on B
    (A stops)                             (target C bypassed)

             cpu A (1st)                  cpu A (nested NMI)
          ----------                     ----------
    panic_try_force_cpu()
        cmpxchg wins (redirect = A)
        vsnprintf(msg) ...
            <-- NMI -->
                                     panic_try_force_cpu()
                                         cmpxchg fails
                                         old_cpu == A == this_cpu
                                         return true  <- would abandon
                                     self_stop (IPI never sent)

Patch 2: flatten nmi_panic control flow.

A behavior preserving cleanup. panic() is noreturn, so the else after it
is dropped and the body flattened, ready for patch 3 to add the redirect
step without piling more onto the if else chain. Split out on its own so
patch 3 only adds new behavior and does not also reshape the function.

Patch 3: allow force_cpu redirect from an NMI.

A panic from an NMI used to bypass the redirect entirely. nmi_panic()
called panic_try_start() first, which claims panic_cpu, so by the time
panic() reached panic_try_force_cpu() the panic_in_progress() check saw
panic_cpu set, returned false, and never sent the redirect IPI. The
crash kernel ran on the CPU that took the NMI instead of the requested
one.

The buggy call order, on a CPU X that is not the target (target is C):

  nmi_panic()
    panic_try_start()              wins, panic_cpu = X
    panic("%s", msg)
      vpanic()
        panic_try_force_cpu()
          panic_in_progress()      true, panic_cpu is X
          return false             redirect bypassed
        panic_try_start()          already won
        __crash_kexec()            on X, not C

The fix tries the redirect before claiming panic_cpu. nmi_panic() calls
panic_try_force_cpu_fmt() first and only calls panic_try_start() when no
redirect happens. The requested CPU then claims panic_cpu itself when
its panic() runs, so panic_cpu is never handed off.

nmi_panic() holds an already formatted string, not a va_list. A variadic
wrapper, panic_try_force_cpu_fmt(), builds the va_list and calls the
existing panic_try_force_cpu(), which still copies and formats under the
redirect cmpxchg. This keeps the static panic_force_buf safe, it is only
written by the cmpxchg winner, never before ownership.

The redirect IPI goes out via smp_call_function_single_async(). This is
safe from NMI: the _async variant is documented as callable with
interrupts disabled, and kgdb_roundup_cpus() already uses it from NMI
context (kernel/debug/debug_core.c).

Changes since v3:
  - Patch 1 now also fixes the panic_in_progress() guard to return
    true or false depending on which CPU owns panic_cpu, and drops the
    recursion framing in the comment per Petr's review.
  - Patch 3 no longer changes the panic_try_force_cpu() signature or
    formats the message before the redirect cmpxchg. Petr pointed out
    the static buf is only safe under panic_cpu ownership, so the
    formatting stays inside the cmpxchg guarded path. nmi_panic() now
    reaches it via a variadic wrapper.
  - Patch 3 adds the NMI safety justification for
    smp_call_function_single_async(), answering Petr's v1 question.
  - The nmi_panic() control flow cleanup is split into its own patch
    (patch 2), per Petr's request to split changes.

Bradley Morgan (3):
  panic: fix redirect CPU race in panic_try_force_cpu()
  panic: flatten nmi_panic control flow
  panic: allow force_cpu redirect from an NMI

 kernel/panic.c | 48 +++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 41 insertions(+), 7 deletions(-)

-- 
2.53.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-07-14 17:31 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-14 17:30 [PATCH v4 0/3] panic: fix the panic_force_cpu redirect races Bradley Morgan
2026-07-14 17:31 ` [PATCH v4 1/3] panic: fix redirect CPU race in panic_try_force_cpu() Bradley Morgan
2026-07-14 17:31 ` [PATCH v4 2/3] panic: flatten nmi_panic control flow Bradley Morgan
2026-07-14 17:31 ` [PATCH v4 3/3] panic: allow force_cpu redirect from an NMI Bradley Morgan

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.