mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: syzbot <syzbot+c69a0a2c816716f1e0d5@syzkaller.appspotmail.com>
To: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com
Subject: Forwarded: Re: [PATCH RFC v2] bpf: lru: Use resilient spinlocks to prevent NMI deadlocks
Date: Tue, 19 May 2026 00:04:56 -0700	[thread overview]
Message-ID: <6a0c0b98.170a0220.39a587.0005.GAE@google.com> (raw)
In-Reply-To: <694995bf.050a0220.2fb209.01a1.GAE@google.com>

For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: Re: [PATCH RFC v2] bpf: lru: Use resilient spinlocks to prevent NMI deadlocks
Author: dvyukov@google.com

#syz upstream

On Sat, 16 May 2026 at 19:17, 'syzbot' via
syzkaller-upstream-moderation
<syzkaller-upstream-moderation@googlegroups.com> wrote:
>
> BPF LRU maps currently use standard `raw_spinlock_t` for their local and
> global list locks, which are not NMI-safe. If an NMI (such as a perf
> event hardware breakpoint) interrupts a normal context that is holding
> an LRU list lock, and a BPF program executed in the NMI context attempts
> to acquire the exact same lock (e.g., via `bpf_map_delete_elem`), it
> will spin forever waiting for the lock to be released. This results in a
> hard deadlock because the context holding the lock has been preempted by
> the NMI itself. Lockdep correctly detects this unsafe `{INITIAL USE} ->
> {IN-NMI}` transition and emits an inconsistent lock state warning.
>
> To resolve this, update the LRU list implementation to use resilient
> queued spinlocks (`rqspinlock_t`), similar to how bucket locks in
> standard BPF hash maps were previously converted. Resilient spinlocks
> are NMI-safe because they detect deadlocks (such as re-entrancy on the
> same CPU) and return an error instead of hanging. Replace all standard
> spinlock operations with their resilient counterparts in
> `bpf_lru_list.c`. Since resilient spinlock acquisitions can fail, update
> the LRU functions to handle these failures gracefully. For pop
> functions, return `NULL` when the lock cannot be acquired, which callers
> already handle by propagating an `-ENOMEM` error. When stealing nodes
> from remote CPUs, safely skip the CPU if its lock cannot be acquired.
> For push and flush functions, abort and return early if the lock fails,
> effectively leaking the LRU node, which is an acceptable trade-off to
> prevent a hard system deadlock in NMI context.
>
> Fixes: 3a08c2fd763450a927d1130de078d6f9e74944fb ("bpf: LRU List")
> Assisted-by: Gemini:gemini-3.1-pro-preview Gemini:gemini-3-flash-preview
> Reported-by: syzbot+c69a0a2c816716f1e0d5@syzkaller.appspotmail.com
> Link: https://syzkaller.appspot.com/bug?extid=c69a0a2c816716f1e0d5
> Link: https://syzkaller.appspot.com/ai_job?id=5256e80f-91f9-40da-865e-578a2aee46ad
> To: <bpf@vger.kernel.org>
> To: <martin.lau@linux.dev>
> Cc: <andrii@kernel.org>
> Cc: <ast@kernel.org>
> Cc: <daniel@iogearbox.net>
> Cc: <eddyz87@gmail.com>
> Cc: <jolsa@kernel.org>
> Cc: <linux-kernel@vger.kernel.org>
> Cc: <memxor@gmail.com>
> Cc: <song@kernel.org>
> Cc: <yonghong.song@linux.dev>
>
> ---
> v2:
> - Added missing `<asm/rqspinlock.h>` include in `kernel/bpf/bpf_lru_list.c`.
>
> v1:
> https://lore.kernel.org/all/dcebc4bd-5cc1-4dc9-b82b-6cca0fad128b@mail.kernel.org/T/
> ---
> diff --git a/kernel/bpf/bpf_lru_list.c b/kernel/bpf/bpf_lru_list.c
> index e7a2fc605..3d6613378 100644
> --- a/kernel/bpf/bpf_lru_list.c
> +++ b/kernel/bpf/bpf_lru_list.c
> @@ -4,6 +4,7 @@
>  #include <linux/cpumask.h>
>  #include <linux/spinlock.h>
>  #include <linux/percpu.h>
> +#include <asm/rqspinlock.h>
>
>  #include "bpf_lru_list.h"
>
> @@ -307,9 +308,10 @@ static void bpf_lru_list_push_free(struct bpf_lru_list *l,
>         if (WARN_ON_ONCE(IS_LOCAL_LIST_TYPE(node->type)))
>                 return;
>
> -       raw_spin_lock_irqsave(&l->lock, flags);
> +       if (raw_res_spin_lock_irqsave(&l->lock, flags))
> +               return;
>         __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_FREE);
> -       raw_spin_unlock_irqrestore(&l->lock, flags);
> +       raw_res_spin_unlock_irqrestore(&l->lock, flags);
>  }
>
>  static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru,
> @@ -319,7 +321,8 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru,
>         struct bpf_lru_node *node, *tmp_node;
>         unsigned int nfree = 0;
>
> -       raw_spin_lock(&l->lock);
> +       if (raw_res_spin_lock(&l->lock))
> +               return;
>
>         __local_list_flush(l, loc_l);
>
> @@ -338,7 +341,7 @@ static void bpf_lru_list_pop_free_to_local(struct bpf_lru *lru,
>                                       local_free_list(loc_l),
>                                       BPF_LRU_LOCAL_LIST_T_FREE);
>
> -       raw_spin_unlock(&l->lock);
> +       raw_res_spin_unlock(&l->lock);
>  }
>
>  static void __local_list_add_pending(struct bpf_lru *lru,
> @@ -404,7 +407,8 @@ static struct bpf_lru_node *bpf_percpu_lru_pop_free(struct bpf_lru *lru,
>
>         l = per_cpu_ptr(lru->percpu_lru, cpu);
>
> -       raw_spin_lock_irqsave(&l->lock, flags);
> +       if (raw_res_spin_lock_irqsave(&l->lock, flags))
> +               return NULL;
>
>         __bpf_lru_list_rotate(lru, l);
>
> @@ -420,7 +424,7 @@ static struct bpf_lru_node *bpf_percpu_lru_pop_free(struct bpf_lru *lru,
>                 __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_INACTIVE);
>         }
>
> -       raw_spin_unlock_irqrestore(&l->lock, flags);
> +       raw_res_spin_unlock_irqrestore(&l->lock, flags);
>
>         return node;
>  }
> @@ -437,7 +441,8 @@ static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru,
>
>         loc_l = per_cpu_ptr(clru->local_list, cpu);
>
> -       raw_spin_lock_irqsave(&loc_l->lock, flags);
> +       if (raw_res_spin_lock_irqsave(&loc_l->lock, flags))
> +               return NULL;
>
>         node = __local_list_pop_free(loc_l);
>         if (!node) {
> @@ -448,7 +453,7 @@ static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru,
>         if (node)
>                 __local_list_add_pending(lru, loc_l, cpu, node, hash);
>
> -       raw_spin_unlock_irqrestore(&loc_l->lock, flags);
> +       raw_res_spin_unlock_irqrestore(&loc_l->lock, flags);
>
>         if (node)
>                 return node;
> @@ -466,23 +471,26 @@ static struct bpf_lru_node *bpf_common_lru_pop_free(struct bpf_lru *lru,
>         do {
>                 steal_loc_l = per_cpu_ptr(clru->local_list, steal);
>
> -               raw_spin_lock_irqsave(&steal_loc_l->lock, flags);
> +               if (raw_res_spin_lock_irqsave(&steal_loc_l->lock, flags))
> +                       goto next_steal;
>
>                 node = __local_list_pop_free(steal_loc_l);
>                 if (!node)
>                         node = __local_list_pop_pending(lru, steal_loc_l);
>
> -               raw_spin_unlock_irqrestore(&steal_loc_l->lock, flags);
> +               raw_res_spin_unlock_irqrestore(&steal_loc_l->lock, flags);
>
> +next_steal:
>                 steal = cpumask_next_wrap(steal, cpu_possible_mask);
>         } while (!node && steal != first_steal);
>
>         loc_l->next_steal = steal;
>
>         if (node) {
> -               raw_spin_lock_irqsave(&loc_l->lock, flags);
> +               if (raw_res_spin_lock_irqsave(&loc_l->lock, flags))
> +                       return NULL;
>                 __local_list_add_pending(lru, loc_l, cpu, node, hash);
> -               raw_spin_unlock_irqrestore(&loc_l->lock, flags);
> +               raw_res_spin_unlock_irqrestore(&loc_l->lock, flags);
>         }
>
>         return node;
> @@ -511,10 +519,11 @@ static void bpf_common_lru_push_free(struct bpf_lru *lru,
>
>                 loc_l = per_cpu_ptr(lru->common_lru.local_list, node->cpu);
>
> -               raw_spin_lock_irqsave(&loc_l->lock, flags);
> +               if (raw_res_spin_lock_irqsave(&loc_l->lock, flags))
> +                       return;
>
>                 if (unlikely(node->type != BPF_LRU_LOCAL_LIST_T_PENDING)) {
> -                       raw_spin_unlock_irqrestore(&loc_l->lock, flags);
> +                       raw_res_spin_unlock_irqrestore(&loc_l->lock, flags);
>                         goto check_lru_list;
>                 }
>
> @@ -522,7 +531,7 @@ static void bpf_common_lru_push_free(struct bpf_lru *lru,
>                 bpf_lru_node_clear_ref(node);
>                 list_move(&node->list, local_free_list(loc_l));
>
> -               raw_spin_unlock_irqrestore(&loc_l->lock, flags);
> +               raw_res_spin_unlock_irqrestore(&loc_l->lock, flags);
>                 return;
>         }
>
> @@ -538,11 +547,12 @@ static void bpf_percpu_lru_push_free(struct bpf_lru *lru,
>
>         l = per_cpu_ptr(lru->percpu_lru, node->cpu);
>
> -       raw_spin_lock_irqsave(&l->lock, flags);
> +       if (raw_res_spin_lock_irqsave(&l->lock, flags))
> +               return;
>
>         __bpf_lru_node_move(l, node, BPF_LRU_LIST_T_FREE);
>
> -       raw_spin_unlock_irqrestore(&l->lock, flags);
> +       raw_res_spin_unlock_irqrestore(&l->lock, flags);
>  }
>
>  void bpf_lru_push_free(struct bpf_lru *lru, struct bpf_lru_node *node)
> @@ -625,7 +635,7 @@ static void bpf_lru_locallist_init(struct bpf_lru_locallist *loc_l, int cpu)
>
>         loc_l->next_steal = cpu;
>
> -       raw_spin_lock_init(&loc_l->lock);
> +       raw_res_spin_lock_init(&loc_l->lock);
>  }
>
>  static void bpf_lru_list_init(struct bpf_lru_list *l)
> @@ -640,7 +650,7 @@ static void bpf_lru_list_init(struct bpf_lru_list *l)
>
>         l->next_inactive_rotation = &l->lists[BPF_LRU_LIST_T_INACTIVE];
>
> -       raw_spin_lock_init(&l->lock);
> +       raw_res_spin_lock_init(&l->lock);
>  }
>
>  int bpf_lru_init(struct bpf_lru *lru, bool percpu, u32 hash_offset,
> diff --git a/kernel/bpf/bpf_lru_list.h b/kernel/bpf/bpf_lru_list.h
> index fe2661a58..ecd93c77a 100644
> --- a/kernel/bpf/bpf_lru_list.h
> +++ b/kernel/bpf/bpf_lru_list.h
> @@ -7,6 +7,7 @@
>  #include <linux/cache.h>
>  #include <linux/list.h>
>  #include <linux/spinlock_types.h>
> +#include <asm/rqspinlock.h>
>
>  #define NR_BPF_LRU_LIST_T      (3)
>  #define NR_BPF_LRU_LIST_COUNT  (2)
> @@ -34,13 +35,13 @@ struct bpf_lru_list {
>         /* The next inactive list rotation starts from here */
>         struct list_head *next_inactive_rotation;
>
> -       raw_spinlock_t lock ____cacheline_aligned_in_smp;
> +       rqspinlock_t lock ____cacheline_aligned_in_smp;
>  };
>
>  struct bpf_lru_locallist {
>         struct list_head lists[NR_BPF_LRU_LOCAL_LIST_T];
>         u16 next_steal;
> -       raw_spinlock_t lock;
> +       rqspinlock_t lock;
>  };
>
>  struct bpf_common_lru {
>
>
> base-commit: 5d6919055dec134de3c40167a490f33c74c12581
> --
> This is an AI-generated patch subject to moderation.
> Reply with '#syz upstream' to send it to the mailing list.
> Reply with '#syz reject' to reject it.
>
> See  for more information.
>
> --
> You received this message because you are subscribed to the Google Groups "syzkaller-upstream-moderation" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller-upstream-moderation+unsubscribe@googlegroups.com.
> To view this discussion visit https://groups.google.com/d/msgid/syzkaller-upstream-moderation/471284ae-0b11-43ca-ad34-e497fe2ee24e%40mail.kernel.org.

-- 
You received this message because you are subscribed to the Google Groups "syzkaller-upstream-moderation" group.
To unsubscribe from this group and stop receiving emails from it, send an email to syzkaller-upstream-moderation+unsubscribe@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/syzkaller-upstream-moderation/CACT4Y%2Bb%3DUYSxh_7Ct%2Bx4Trh8fR2R%3DuLJj4_EFzRXiZ9f2Chp6Q%40mail.gmail.com.

      parent reply	other threads:[~2026-05-19  7:04 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-22 19:02 [syzbot] [bpf?] inconsistent lock state in bpf_lru_push_free syzbot
2025-12-22 20:11 ` syzbot
2026-03-15 15:52 ` Forwarded: test: bpf lru nmi deadlock fix syzbot
2026-05-19  7:04 ` syzbot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=6a0c0b98.170a0220.39a587.0005.GAE@google.com \
    --to=syzbot+c69a0a2c816716f1e0d5@syzkaller.appspotmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=syzkaller-bugs@googlegroups.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome