summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNuoqi Gui <gnq25@mails.tsinghua.edu.cn>2026-09-04 12:41:54 +0200
committerAlexei Starovoitov <ast@kernel.org>2026-09-04 12:24:24 -0700
commit65cc95eba9e8b46312cac38c227473605a4b996a (patch)
tree2c65f05450aecf22f75545509677cb94ec226500
parentdbf6806dc81553edbab72fcec9a6d637dedff2f4 (diff)
downloadlinux-stable-65cc95eba9e8b46312cac38c227473605a4b996a.tar.gz
linux-stable-65cc95eba9e8b46312cac38c227473605a4b996a.zip
bpf: Cancel special fields when recycling rhtab elements
rhtab_map_update_existing() and rhtab_delete_elem() call bpf_obj_free_fields() when replacing or deleting a value. These map operations can run from BPF programs in NMI context, where releasing a referenced kptr or another complex field is not generally safe. Array and hash maps avoid that problem by cancelling only the asynchronous fields which can be stopped safely in the caller context. Other ownership state remains attached to the allocation until its memory allocator destructor performs the final cleanup. Use bpf_obj_cancel_fields() for the corresponding rhtab paths as well. This cancels timers, workqueues, and task work while allowing rhtab_mem_dtor() to release referenced kptrs when the allocation is eventually destroyed. Fixes: 6905f8601298 ("bpf: Allow special fields in resizable hashtab") Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn> Acked-by: Mykyta Yatsenko <yatsenko@meta.com> [ kkd: Rebased, used direct helper calls, and rewrote the commit log ] Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Link: https://lore.kernel.org/r/20260904104203.345917-4-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
-rw-r--r--kernel/bpf/hashtab.c17
1 files changed, 3 insertions, 14 deletions
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 527cc5716ee8..cc60e99ffbe9 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -2868,16 +2868,6 @@ static int rhtab_map_alloc_check(union bpf_attr *attr)
return htab_map_alloc_check(attr);
}
-static void rhtab_check_and_free_fields(struct bpf_rhtab *rhtab,
- struct rhtab_elem *elem)
-{
- if (IS_ERR_OR_NULL(rhtab->map.record))
- return;
-
- bpf_obj_free_fields(rhtab->map.record,
- rhtab_elem_value(elem, rhtab->map.key_size));
-}
-
static void rhtab_mem_dtor(void *obj, void *ctx)
{
struct htab_btf_record *hrec = ctx;
@@ -2967,8 +2957,8 @@ static int rhtab_delete_elem(struct bpf_rhtab *rhtab, struct rhtab_elem *elem, v
rhtab_read_elem_value(&rhtab->map, copy, elem, flags);
check_and_init_map_value(&rhtab->map, copy);
}
- /* Release internal structs: kptr, bpf_timer, task_work, wq */
- rhtab_check_and_free_fields(rhtab, elem);
+ bpf_obj_cancel_fields(&rhtab->map,
+ rhtab_elem_value(elem, rhtab->map.key_size));
bpf_mem_cache_free_rcu(&rhtab->ma, elem);
return 0;
}
@@ -3009,7 +2999,6 @@ static int rhtab_map_lookup_and_delete_elem(struct bpf_map *map, void *key, void
static long rhtab_map_update_existing(struct bpf_map *map, struct rhtab_elem *elem, void *value,
u64 map_flags)
{
- struct bpf_rhtab *rhtab = container_of(map, struct bpf_rhtab, map);
void *old_val = rhtab_elem_value(elem, map->key_size);
if (map_flags & BPF_NOEXIST)
@@ -3029,7 +3018,7 @@ static long rhtab_map_update_existing(struct bpf_map *map, struct rhtab_elem *el
* kptrs/etc. still sit in the slot. Cancel them after the copy
* to match arraymap's update semantics.
*/
- rhtab_check_and_free_fields(rhtab, elem);
+ bpf_obj_cancel_fields(map, old_val);
return 0;
}