mbox series

[bpf-next,v1,0/3] bpf: allow bpf_for_each_map_elem() helper with different input maps

Message ID 20240405025536.18113-1-lulie@linux.alibaba.com (mailing list archive)
Headers show
Series bpf: allow bpf_for_each_map_elem() helper with different input maps | expand

Message

Philo Lu April 5, 2024, 2:55 a.m. UTC
Currently, taking different maps within a single bpf_for_each_map_elem
call is not allowed. For example the following codes cannot pass the
verifier (with error "tail_call abusing map_ptr"):
```
static void test_by_pid(int pid)
{
	if (pid <= 100)
		bpf_for_each_map_elem(&map1, map_elem_cb, NULL, 0);
	else
		bpf_for_each_map_elem(&map2, map_elem_cb, NULL, 0);
}
```

This is because during bpf_for_each_map_elem verifying,
bpf_insn_aux_data->map_ptr_state is expected as map_ptr (instead of poison
state), which is then needed by set_map_elem_callback_state. However, as
there are two different map ptr input, map_ptr_state is marked as
BPF_MAP_PTR_POISON, and thus the second map_ptr would be lost.
BPF_MAP_PTR_POISON is also needed by bpf_for_each_map_elem to skip
retpoline optimization in do_misc_fixups(). Therefore, map_ptr_state and
map_ptr are both needed for bpf_for_each_map_elem.

This patchset solves it by transform bpf_insn_aux_data->map_ptr_state as a
new struct, storing poison/unpriv state and map pointer together without
additional memory overhead. Then bpf_for_each_map_elem works well with
different input maps. It also makes map_ptr_state logic clearer.

A test case is added to selftest, which would fail to load without this
patchset.

Changelogs
-> v1:
- PATCH 1/3:
  - make the commit log clearer
  - change poison and unpriv to bool in struct bpf_map_ptr_state, also the
    return value in bpf_map_ptr_poisoned() and bpf_map_ptr_unpriv()
- PATCH 2/3:
  - change the comments in set_map_elem_callback_state()
- PATCH 3/3:
  - remove the "skipping the last element" logic during map updating
  - change if() to ASSERT_OK()

Please review, thanks.

Philo Lu (3):
  bpf: store both map ptr and state in bpf_insn_aux_data
  bpf: allow invoking bpf_for_each_map_elem with different maps
  selftests/bpf: add test for bpf_for_each_map_elem() with different
    maps

 include/linux/bpf_verifier.h                  |  9 ++-
 kernel/bpf/verifier.c                         | 42 +++++--------
 .../selftests/bpf/prog_tests/for_each.c       | 62 +++++++++++++++++++
 .../selftests/bpf/progs/for_each_multi_maps.c | 49 +++++++++++++++
 4 files changed, 136 insertions(+), 26 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/for_each_multi_maps.c

--
2.32.0.3.g01195cf9f

Comments

patchwork-bot+netdevbpf@kernel.org April 5, 2024, 5:40 p.m. UTC | #1
Hello:

This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Fri,  5 Apr 2024 10:55:33 +0800 you wrote:
> Currently, taking different maps within a single bpf_for_each_map_elem
> call is not allowed. For example the following codes cannot pass the
> verifier (with error "tail_call abusing map_ptr"):
> ```
> static void test_by_pid(int pid)
> {
> 	if (pid <= 100)
> 		bpf_for_each_map_elem(&map1, map_elem_cb, NULL, 0);
> 	else
> 		bpf_for_each_map_elem(&map2, map_elem_cb, NULL, 0);
> }
> ```
> 
> [...]

Here is the summary with links:
  - [bpf-next,v1,1/3] bpf: store both map ptr and state in bpf_insn_aux_data
    https://git.kernel.org/bpf/bpf-next/c/0a525621b7e5
  - [bpf-next,v1,2/3] bpf: allow invoking bpf_for_each_map_elem with different maps
    https://git.kernel.org/bpf/bpf-next/c/9d482da9e17a
  - [bpf-next,v1,3/3] selftests/bpf: add test for bpf_for_each_map_elem() with different maps
    https://git.kernel.org/bpf/bpf-next/c/fecb1597cc11

You are awesome, thank you!