Skip to content

Commit 031e04b

Browse files
melverakpm00
authored andcommitted
stackdepot: fix stack_depot_save_flags() in NMI context
Per documentation, stack_depot_save_flags() was meant to be usable from NMI context if STACK_DEPOT_FLAG_CAN_ALLOC is unset. However, it still would try to take the pool_lock in an attempt to save a stack trace in the current pool (if space is available). This could result in deadlock if an NMI is handled while pool_lock is already held. To avoid deadlock, only try to take the lock in NMI context and give up if unsuccessful. The documentation is fixed to clearly convey this. Link: https://lkml.kernel.org/r/Z0CcyfbPqmxJ9uJH@elver.google.com Link: https://lkml.kernel.org/r/20241122154051.3914732-1-elver@google.com Fixes: 4434a56 ("stackdepot: make fast paths lock-less again") Signed-off-by: Marco Elver <elver@google.com> Reported-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Cc: Alexander Potapenko <glider@google.com> Cc: Andrey Konovalov <andreyknvl@gmail.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: Oscar Salvador <osalvador@suse.de> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: <stable@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent 6a7de1b commit 031e04b

File tree

2 files changed

+12
-4
lines changed

2 files changed

+12
-4
lines changed

include/linux/stackdepot.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ static inline int stack_depot_early_init(void) { return 0; }
147147
* If the provided stack trace comes from the interrupt context, only the part
148148
* up to the interrupt entry is saved.
149149
*
150-
* Context: Any context, but setting STACK_DEPOT_FLAG_CAN_ALLOC is required if
150+
* Context: Any context, but unsetting STACK_DEPOT_FLAG_CAN_ALLOC is required if
151151
* alloc_pages() cannot be used from the current context. Currently
152152
* this is the case for contexts where neither %GFP_ATOMIC nor
153153
* %GFP_NOWAIT can be used (NMI, raw_spin_lock).
@@ -156,7 +156,7 @@ static inline int stack_depot_early_init(void) { return 0; }
156156
*/
157157
depot_stack_handle_t stack_depot_save_flags(unsigned long *entries,
158158
unsigned int nr_entries,
159-
gfp_t gfp_flags,
159+
gfp_t alloc_flags,
160160
depot_flags_t depot_flags);
161161

162162
/**
@@ -175,7 +175,7 @@ depot_stack_handle_t stack_depot_save_flags(unsigned long *entries,
175175
* Return: Handle of the stack trace stored in depot, 0 on failure
176176
*/
177177
depot_stack_handle_t stack_depot_save(unsigned long *entries,
178-
unsigned int nr_entries, gfp_t gfp_flags);
178+
unsigned int nr_entries, gfp_t alloc_flags);
179179

180180
/**
181181
* __stack_depot_get_stack_record - Get a pointer to a stack_record struct

lib/stackdepot.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,15 @@ depot_stack_handle_t stack_depot_save_flags(unsigned long *entries,
630630
prealloc = page_address(page);
631631
}
632632

633-
raw_spin_lock_irqsave(&pool_lock, flags);
633+
if (in_nmi()) {
634+
/* We can never allocate in NMI context. */
635+
WARN_ON_ONCE(can_alloc);
636+
/* Best effort; bail if we fail to take the lock. */
637+
if (!raw_spin_trylock_irqsave(&pool_lock, flags))
638+
goto exit;
639+
} else {
640+
raw_spin_lock_irqsave(&pool_lock, flags);
641+
}
634642
printk_deferred_enter();
635643

636644
/* Try to find again, to avoid concurrently inserting duplicates. */

0 commit comments

Comments
 (0)