From: Kaitao Cheng <[email protected]> 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 <[email protected]> --- 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)

