[PATCH bpf-next v9 3/9] bpf: clear list node owner and unlink before drop
From: Chengkaitao
Date: Sun Mar 29 2026 - 10:06:41 EST
From: Kaitao Cheng <chengkaitao@xxxxxxxxxx>
When draining a BPF list_head, clear each node's owner pointer while still
holding the spinlock, so concurrent readers always see a consistent owner.
Delink each node with list_del_init() before calling __bpf_obj_drop_impl(),
preventing subsequent users who hold a reference count to the node from
acquiring an invalid next node.
Signed-off-by: Kaitao Cheng <chengkaitao@xxxxxxxxxx>
---
kernel/bpf/helpers.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 94fcd4ab39e9..8abb99712043 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -2232,7 +2232,7 @@ EXPORT_SYMBOL_GPL(bpf_base_func_proto);
void bpf_list_head_free(const struct btf_field *field, void *list_head,
struct bpf_spin_lock *spin_lock)
{
- struct list_head *head = list_head, *orig_head = list_head;
+ struct list_head *head = list_head, *orig_head = list_head, *pos;
BUILD_BUG_ON(sizeof(struct list_head) > sizeof(struct bpf_list_head));
BUILD_BUG_ON(__alignof__(struct list_head) > __alignof__(struct bpf_list_head));
@@ -2247,6 +2247,9 @@ void bpf_list_head_free(const struct btf_field *field, void *list_head,
if (!head->next || list_empty(head))
goto unlock;
head = head->next;
+ /* Clear owner under spinlock to ensure the owner is always valid */
+ for (pos = head; pos != orig_head; pos = pos->next)
+ WRITE_ONCE(container_of(pos, struct bpf_list_node_kern, list_head)->owner, NULL);
unlock:
INIT_LIST_HEAD(orig_head);
__bpf_spin_unlock_irqrestore(spin_lock);
@@ -2255,7 +2258,9 @@ void bpf_list_head_free(const struct btf_field *field, void *list_head,
void *obj = head;
obj -= field->graph_root.node_offset;
+ pos = head;
head = head->next;
+ list_del_init(pos);
/* The contained type can also have resources, including a
* bpf_list_head which needs to be freed.
*/
--
2.50.1 (Apple Git-155)