[PATCH] bpf: Reject offloaded maps as map-in-map inner maps
From: Shihuang Liu
Date: Thu Nov 12 2026 - 05:57:42 EST
bpf_map_update_elem() on an ARRAY_OF_MAPS or HASH_OF_MAPS map accepts
the fd of an offloaded map. A program that then looks up the inner map
gets a pointer computed with the software map layout: with an ARRAY
template the write lands in struct bpf_offloaded_map.dev_priv, with a
HASH template __htab_map_lookup_elem() reads bpf_htab fields past the
320-byte offloaded map allocation (KASAN slab-out-of-bounds). An
offloaded map used as inner_map_fd is accepted as well, and the first
inner lookup jumps to an invalid address because bpf_map_offload_ops
has no map_lookup_elem.
The root cause is that bpf_map_meta_equal() compares map_type,
key_size, value_size, map_flags and the btf record only, and
bpf_map_fd_get_ptr() relies on the template's map_meta_equal() without
comparing ops. Offloaded maps share map_type (ARRAY/HASH) with the
software maps, but use bpf_map_offload_ops and a different object
layout, while the verifier and the JIT keep using the template's ops
and layout.
Reject offloaded maps in bpf_map_meta_alloc() so they cannot be an
inner-map template, and require inner_map_meta->ops == inner_map->ops
in bpf_map_fd_get_ptr().
Fixes: a38845729ea398 ("bpf: offload: add map offload infrastructure")
Signed-off-by: Shihuang Liu <shlomojune6@xxxxxxxxx>
---
kernel/bpf/map_in_map.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c
index 0fa3f2ac0f992f..5aa1f8249feb51 100644
--- a/kernel/bpf/map_in_map.c
+++ b/kernel/bpf/map_in_map.c
@@ -22,6 +22,8 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
return ERR_PTR(-EINVAL);
if (inner_map->excl_prog_sha)
return ERR_PTR(-ENOTSUPP);
+ if (bpf_map_is_offloaded(inner_map))
+ return ERR_PTR(-EOPNOTSUPP);
if (!inner_map->ops->map_meta_equal)
return ERR_PTR(-ENOTSUPP);
@@ -110,7 +112,8 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map,
return ERR_PTR(-ENOTSUPP);
inner_map_meta = map->inner_map_meta;
- if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map)) {
+ if (inner_map_meta->ops == inner_map->ops &&
+ inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map)) {
bpf_map_inc(inner_map);
/* Its users are those of the outer maps from now on */
WRITE_ONCE(inner_map->was_inner_map, true);
--
2.43.0