kfree_nolock() skips free hooks that may acquire spinlocks because it can be called from any context. debug_check_no_locks_freed() and __kcsan_check_access() are skipped as well. However, they acquire a spinlock only when they report a bug. debug_check_no_locks_freed() disables interrupts and walks current->held_locks, and reaches printk only when a held lock is inside the freed object. KCSAN takes report_lock only when it observes a race. Just like KASAN, take the risk of potential deadlock for bug reporting. Call debug_check_no_locks_freed() and __kcsan_check_access() regardless of allow_spin. Signed-off-by: Harry Yoo (Meta) --- mm/slub.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/mm/slub.c b/mm/slub.c index fd3de3bee1de..fad19ec6af4b 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2708,19 +2708,25 @@ bool slab_free_hook(struct kmem_cache *s, void *x, unsigned int free_flags) kmsan_slab_free(s, x); + /* + * debug_locks, KCSAN, KASAN may acquire a spinlock to report a bug, + * but otherwise cannot deadlock. Take the risk of deadlock for + * reporting. + */ + debug_check_no_locks_freed(x, s->object_size); + + /* Use KCSAN to help debug racy use-after-free. */ + if (!still_accessible) + __kcsan_check_access(x, s->object_size, + KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT); + /* Hooks that acquire locks or kmalloc_nolock() skips alloc hook counterpart */ if (likely(allow_spin)) { kmemleak_free_recursive(x, s->flags); - debug_check_no_locks_freed(x, s->object_size); if (!(s->flags & SLAB_DEBUG_OBJECTS)) debug_check_no_obj_freed(x, s->object_size); - /* Use KCSAN to help debug racy use-after-free. */ - if (!still_accessible) - __kcsan_check_access(x, s->object_size, - KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ASSERT); - if (kfence_free(x)) return false; } @@ -2728,13 +2734,6 @@ bool slab_free_hook(struct kmem_cache *s, void *x, unsigned int free_flags) /* * Give KASAN a chance to notice an invalid free operation before we * modify the object. - * - * If KASAN finds a bug it will do kasan_report_invalid_free() which - * will call raw_spin_lock_irqsave() which is technically unsafe from - * NMI, but take chance and report kernel bug. The sequence of - * kasan_report_invalid_free() -> raw_spin_lock_irqsave() -> NMI - * -> kfree_nolock() -> kasan_report_invalid_free() on the same CPU - * is double buggy and deserves to deadlock. */ if (kasan_slab_pre_free(s, x)) return false; -- 2.54.0