* [PATCH] bpf: Reject offloaded maps as map-in-map inner maps
@ 2026-11-12 10:56 Shihuang Liu
2026-11-12 11:31 ` bot+bpf-ci
2026-11-12 19:53 ` Alexei Starovoitov
0 siblings, 2 replies; 3+ messages in thread
From: Shihuang Liu @ 2026-11-12 10:56 UTC (permalink / raw)
To: bpf
Cc: netdev, linux-kernel, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi,
Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Ihor Solodrai, Jakub Kicinski, Quentin Monnet,
Shihuang Liu
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@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] bpf: Reject offloaded maps as map-in-map inner maps
2026-11-12 10:56 [PATCH] bpf: Reject offloaded maps as map-in-map inner maps Shihuang Liu
@ 2026-11-12 11:31 ` bot+bpf-ci
2026-11-12 19:53 ` Alexei Starovoitov
1 sibling, 0 replies; 3+ messages in thread
From: bot+bpf-ci @ 2026-11-12 11:31 UTC (permalink / raw)
To: shlomojune6, bpf
Cc: netdev, linux-kernel, ast, daniel, andrii, eddyz87, memxor,
martin.lau, song, yonghong.song, jolsa, emil, ihor.solodrai, kuba,
qmo, shlomojune6, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 1442 bytes --]
> bpf: Reject offloaded maps as map-in-map inner maps
TL;DR: Nit only: the comment in bpf_map_meta_equal() saying ops need no
comparison now contradicts the new ops check in bpf_map_fd_get_ptr(); suggests
updating it or moving the check there.
> diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c
> --- a/kernel/bpf/map_in_map.c
> +++ b/kernel/bpf/map_in_map.c
[ ... ]
> @@ -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)) {
This isn't a bug, but the comment in bpf_map_meta_equal() still says:
/* No need to compare ops because it is covered by map_type */
Offloaded maps share map_type with software maps but use
bpf_map_offload_ops, so that comment now contradicts the new ops check
here. Could it be updated or dropped?
Another option is to move the ops comparison into bpf_map_meta_equal(),
which array_map_meta_equal() and xsk_map_meta_equal() also call, and
replace the comment there.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/43208166255
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] bpf: Reject offloaded maps as map-in-map inner maps
2026-11-12 10:56 [PATCH] bpf: Reject offloaded maps as map-in-map inner maps Shihuang Liu
2026-11-12 11:31 ` bot+bpf-ci
@ 2026-11-12 19:53 ` Alexei Starovoitov
1 sibling, 0 replies; 3+ messages in thread
From: Alexei Starovoitov @ 2026-11-12 19:53 UTC (permalink / raw)
To: Shihuang Liu, bpf
Cc: netdev, linux-kernel, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
Song Liu, Yonghong Song, Jiri Olsa, Emil Tsalapatis,
Ihor Solodrai, Jakub Kicinski, Quentin Monnet
On Thu, Nov 12, 2026 at 02:56 AM Shihuang Liu <shlomojune6@gmail.com> wrote:
> + if (bpf_map_is_offloaded(inner_map))
> + return ERR_PTR(-EOPNOTSUPP);
> if (!inner_map->ops->map_meta_equal)
> return ERR_PTR(-ENOTSUPP);
No need for a new check.
.map_meta_equal in the ops means that the map can be an inner map.
See the comment in struct bpf_map_ops.
Drop it from bpf_map_offload_ops. Then the next check rejects the map.
> - 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_iter_attach_map() has the same problem. It checks map_type only:
BPF_LINK_CREATE
bpf_iter_attach_map() accepts an offloaded ARRAY or HASH
BPF_ITER_CREATE
prepare_seq_file()
__get_seq_info() bpf_map_offload_ops.iter_seq_info is NULL
bpf_map_elem_reg_info.seq_info is NULL
seq_info->seq_priv_size <- NULL pointer dereference
Something like this (compile tested only):
diff --git a/kernel/bpf/map_in_map.c b/kernel/bpf/map_in_map.c
index 0fa3f2a..9ec3bbf 100644
--- a/kernel/bpf/map_in_map.c
+++ b/kernel/bpf/map_in_map.c
@@ -38,6 +38,7 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
if (!inner_map_meta)
return ERR_PTR(-ENOMEM);
+ inner_map_meta->ops = inner_map->ops;
inner_map_meta->map_type = inner_map->map_type;
inner_map_meta->key_size = inner_map->key_size;
inner_map_meta->value_size = inner_map->value_size;
@@ -65,7 +66,6 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
}
/* Misc members not needed in bpf_map_meta_equal() check. */
- inner_map_meta->ops = inner_map->ops;
if (inner_map->ops == &array_map_ops || inner_map->ops == &percpu_array_map_ops) {
struct bpf_array *inner_array_meta =
container_of(inner_map_meta, struct bpf_array, map);
@@ -88,8 +88,9 @@ void bpf_map_meta_free(struct bpf_map *map_meta)
bool bpf_map_meta_equal(const struct bpf_map *meta0,
const struct bpf_map *meta1)
{
- /* No need to compare ops because it is covered by map_type */
- return meta0->map_type == meta1->map_type &&
+ /* An offloaded map has the map_type of a host map and its own ops */
+ return meta0->ops == meta1->ops &&
+ meta0->map_type == meta1->map_type &&
meta0->key_size == meta1->key_size &&
meta0->value_size == meta1->value_size &&
meta0->map_flags == meta1->map_flags &&
diff --git a/kernel/bpf/map_iter.c b/kernel/bpf/map_iter.c
index a58c148..9e459a2 100644
--- a/kernel/bpf/map_iter.c
+++ b/kernel/bpf/map_iter.c
@@ -176,6 +176,10 @@ static int bpf_iter_attach_map(struct bpf_prog *prog,
map->map_type != BPF_MAP_TYPE_RHASH)
goto put_map;
+ /* An offloaded map has the map_type of a host map and its own ops */
+ if (!map->ops->iter_seq_info)
+ goto put_map;
+
aux->map = map;
err = bpf_iter_map_check_prog(prog, aux);
if (err)
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index 04a3b3c..c8146db 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -112,7 +112,6 @@ int bpf_check_uarg_tail_zero(bpfptr_t uaddr,
}
const struct bpf_map_ops bpf_map_offload_ops = {
- .map_meta_equal = bpf_map_meta_equal,
.map_alloc = bpf_map_offload_map_alloc,
.map_free = bpf_map_offload_map_free,
.map_check_btf = map_check_no_btf,
Pls tag the respin as [PATCH bpf-next v2].
pw-bot: cr
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-12 19:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-12 10:56 [PATCH] bpf: Reject offloaded maps as map-in-map inner maps Shihuang Liu
2026-11-12 11:31 ` bot+bpf-ci
2026-11-12 19:53 ` Alexei Starovoitov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox