From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C68315678E4; Wed, 9 Sep 2026 14:18:02 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788963484; cv=none; b=NVSCKoYUyEGIJJ27D+2xLHmjcjQcPzFW4xyU0OCWuiqNGPoVjQqwKoWggvvNzP5oiUkC60E5t3VxpMm/s3ApxxwbxahzAtablHiOmXFchv5Y4zQ7s+88qdRA1ygLfwyLWUdGaishQnXqzDxJNJez+3sSsnAGMLxYT+wonu1tBWg= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1788963484; c=relaxed/simple; bh=TFvVQF5Wk9l4c0S37GZiG4HgL+jH5Qcop4J8RzdEVkI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=kP8avskMV9/qxl/lb1IL1N3Q04BN+hjVelh7jol1kcfJdtcII5c/tJAhaZ2bpNmEqyGMUoO98uwdOAFjY88OZhGPQ3U6twDaxLeptj22rH4aQfNb1v9KOzWFv9fqRLeqxk2+fE92noGSpHs1Xj5mTLu4fv/PeY7tT/sXAlEaAUM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=EOwfrHZH; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="EOwfrHZH" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 577621F00A3A; Wed, 9 Sep 2026 14:18:02 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1788963482; bh=feDho9wOr/4QgFpEsz7/ed26A79BQsVrwuCbWVzD/pY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=EOwfrHZH+b62mhoGgi6h7jItmCZgTHXXWeMSuKkuk4bj0RwGKVEd31275jLYjAsUU uO7pHB+0pHU4jbF25+1/DJ4NgI65TyXYlCX91jzJUTIwmLuUzzlr34uKGLbxWx47p/ M3mZAujuE5D3pph9NVUVEfiom4Or8D5M9FfDayKg= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Hui Su , Kumar Kartikeya Dwivedi Subject: [PATCH 6.18 077/583] bpf: Fix infinite loop in pcpu_freelist push with one possible CPU Date: Wed, 9 Sep 2026 15:36:02 +0200 Message-ID: <20260909134240.757439867@linuxfoundation.org> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260909134237.773280130@linuxfoundation.org> References: <20260909134237.773280130@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Hui Su commit efebf6496685c93150df5bb0794363ae70c5f58a upstream. __pcpu_freelist_push() can loop forever when only one CPU is possible and an NMI re-enters pcpu_freelist_push() while the interrupted context holds that CPU's freelist lock. After the current-CPU fast path fails, the fallback loop walks cpu_possible_mask while skipping the current CPU. With CONFIG_SMP=n, or when an SMP kernel is limited to one possible CPU with nr_cpus=1 or possible_cpus=1, there are no other possible CPUs to examine. The loop therefore makes no lock acquisition attempt and can never make progress. The following stack was observed on a UP system: NMI context: pcpu_freelist_push free_htab_elem htab_map_delete_elem [perf-event BPF program] __perf_event_overflow perf_event_nmi_handler exc_nmi Interrupted context: __pcpu_freelist_push pcpu_freelist_push free_htab_elem htab_map_delete_elem [raw_tp/sys_enter BPF program] __bpf_trace_sys_enter do_syscall_64 raw_res_spin_lock() detects the same-CPU recursive acquisition and returns -EDEADLK, but the subsequent fallback loop has no candidate head on a system with one possible CPU. Restore the extra fallback head that existed before the rqspinlock conversion. Keep the current-CPU fast path, then try the other possible CPUs and finally the extra head. The additional head lets a push, which cannot fail without losing a preallocated element, make progress when the only per-CPU head is held by the interrupted context. Also check the extra head from the pop path so that nodes placed there can be reused. Fixes: f2ac0e5d1c4d ("bpf: Convert percpu_freelist.c to rqspinlock") Signed-off-by: Hui Su Cc: stable@vger.kernel.org Link: https://lore.kernel.org/bpf/20260806175600.1993595-1-sh_def@163.com Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Greg Kroah-Hartman --- kernel/bpf/percpu_freelist.c | 35 +++++++++++++++++++++++++++-------- kernel/bpf/percpu_freelist.h | 1 + 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/kernel/bpf/percpu_freelist.c b/kernel/bpf/percpu_freelist.c index 632762b57299..06ce588d13a3 100644 --- a/kernel/bpf/percpu_freelist.c +++ b/kernel/bpf/percpu_freelist.c @@ -17,6 +17,8 @@ int pcpu_freelist_init(struct pcpu_freelist *s) raw_res_spin_lock_init(&head->lock); head->first = NULL; } + raw_res_spin_lock_init(&s->extralist.lock); + s->extralist.first = NULL; return 0; } @@ -46,22 +48,28 @@ void __pcpu_freelist_push(struct pcpu_freelist *s, struct pcpu_freelist_node *node) { struct pcpu_freelist_head *head; - int cpu; + int cpu, this_cpu; if (___pcpu_freelist_push(this_cpu_ptr(s->freelist), node)) return; + this_cpu = raw_smp_processor_id(); while (true) { - for_each_cpu_wrap(cpu, cpu_possible_mask, raw_smp_processor_id()) { - if (cpu == raw_smp_processor_id()) + for_each_cpu_wrap(cpu, cpu_possible_mask, this_cpu) { + if (cpu == this_cpu) continue; + head = per_cpu_ptr(s->freelist, cpu); - if (raw_res_spin_lock(&head->lock)) - continue; - pcpu_freelist_push_node(head, node); - raw_res_spin_unlock(&head->lock); - return; + if (___pcpu_freelist_push(head, node)) + return; } + + /* + * Push cannot fail. Use the extra list when none of the + * per-CPU freelists can accept the node. + */ + if (___pcpu_freelist_push(&s->extralist, node)) + return; } } @@ -117,6 +125,17 @@ static struct pcpu_freelist_node *___pcpu_freelist_pop(struct pcpu_freelist *s) } raw_res_spin_unlock(&head->lock); } + + /* Per-CPU lists are empty or unavailable, try the extra list. */ + head = &s->extralist; + if (!READ_ONCE(head->first)) + return NULL; + if (raw_res_spin_lock(&head->lock)) + return NULL; + node = head->first; + if (node) + WRITE_ONCE(head->first, node->next); + raw_res_spin_unlock(&head->lock); return node; } diff --git a/kernel/bpf/percpu_freelist.h b/kernel/bpf/percpu_freelist.h index 914798b74967..980cf2884fd2 100644 --- a/kernel/bpf/percpu_freelist.h +++ b/kernel/bpf/percpu_freelist.h @@ -14,6 +14,7 @@ struct pcpu_freelist_head { struct pcpu_freelist { struct pcpu_freelist_head __percpu *freelist; + struct pcpu_freelist_head extralist; }; struct pcpu_freelist_node { -- 2.55.0