Commit dbd7eb14 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by Alexei Starovoitov

bpf, selftests: Replicate tailcall limit test for indirect call case

The tailcall_3 test program uses bpf_tail_call_static() where the JIT
would patch a direct jump. Add a new tailcall_6 test program replicating
exactly the same test just ensuring that bpf_tail_call() uses a map
index where the verifier cannot make assumptions this time.

In other words, this will now cover both on x86-64 JIT, meaning, JIT
images with emit_bpf_tail_call_direct() emission as well as JIT images
with emit_bpf_tail_call_indirect() emission.

  # echo 1 > /proc/sys/net/core/bpf_jit_enable
  # ./test_progs -t tailcalls
  #136/1 tailcalls/tailcall_1:OK
  #136/2 tailcalls/tailcall_2:OK
  #136/3 tailcalls/tailcall_3:OK
  #136/4 tailcalls/tailcall_4:OK
  #136/5 tailcalls/tailcall_5:OK
  #136/6 tailcalls/tailcall_6:OK
  #136/7 tailcalls/tailcall_bpf2bpf_1:OK
  #136/8 tailcalls/tailcall_bpf2bpf_2:OK
  #136/9 tailcalls/tailcall_bpf2bpf_3:OK
  #136/10 tailcalls/tailcall_bpf2bpf_4:OK
  #136/11 tailcalls/tailcall_bpf2bpf_5:OK
  #136 tailcalls:OK
  Summary: 1/11 PASSED, 0 SKIPPED, 0 FAILED

  # echo 0 > /proc/sys/net/core/bpf_jit_enable
  # ./test_progs -t tailcalls
  #136/1 tailcalls/tailcall_1:OK
  #136/2 tailcalls/tailcall_2:OK
  #136/3 tailcalls/tailcall_3:OK
  #136/4 tailcalls/tailcall_4:OK
  #136/5 tailcalls/tailcall_5:OK
  #136/6 tailcalls/tailcall_6:OK
  [...]

For interpreter, the tailcall_1-6 tests are passing as well. The later
tailcall_bpf2bpf_* are failing due lack of bpf2bpf + tailcall support
in interpreter, so this is expected.

Also, manual inspection shows that both loaded programs from tailcall_3
and tailcall_6 test case emit the expected opcodes:

* tailcall_3 disasm, emit_bpf_tail_call_direct():

  [...]
   b:   push   %rax
   c:   push   %rbx
   d:   push   %r13
   f:   mov    %rdi,%rbx
  12:   movabs $0xffff8d3f5afb0200,%r13
  1c:   mov    %rbx,%rdi
  1f:   mov    %r13,%rsi
  22:   xor    %edx,%edx                 _
  24:   mov    -0x4(%rbp),%eax          |  limit check
  2a:   cmp    $0x20,%eax               |
  2d:   ja     0x0000000000000046       |
  2f:   add    $0x1,%eax                |
  32:   mov    %eax,-0x4(%rbp)          |_
  38:   nopl   0x0(%rax,%rax,1)
  3d:   pop    %r13
  3f:   pop    %rbx
  40:   pop    %rax
  41:   jmpq   0xffffffffffffe377
  [...]

* tailcall_6 disasm, emit_bpf_tail_call_indirect():

  [...]
  47:   movabs $0xffff8d3f59143a00,%rsi
  51:   mov    %edx,%edx
  53:   cmp    %edx,0x24(%rsi)
  56:   jbe    0x0000000000000093        _
  58:   mov    -0x4(%rbp),%eax          |  limit check
  5e:   cmp    $0x20,%eax               |
  61:   ja     0x0000000000000093       |
  63:   add    $0x1,%eax                |
  66:   mov    %eax,-0x4(%rbp)          |_
  6c:   mov    0x110(%rsi,%rdx,8),%rcx
  74:   test   %rcx,%rcx
  77:   je     0x0000000000000093
  79:   pop    %rax
  7a:   mov    0x30(%rcx),%rcx
  7e:   add    $0xb,%rcx
  82:   callq  0x000000000000008e
  87:   pause
  89:   lfence
  8c:   jmp    0x0000000000000087
  8e:   mov    %rcx,(%rsp)
  92:   retq
  [...]
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Tested-by: default avatarTiezhu Yang <yangtiezhu@loongson.cn>
Acked-by: default avatarYonghong Song <yhs@fb.com>
Acked-by: default avatarJohan Almbladh <johan.almbladh@anyfinetworks.com>
Acked-by: default avatarPaul Chaignon <paul@cilium.io>
Link: https://lore.kernel.org/bpf/CAM1=_QRyRVCODcXo_Y6qOm1iT163HoiSj8U2pZ8Rj3hzMTT=HQ@mail.gmail.com
Link: https://lore.kernel.org/bpf/20210910091900.16119-1-daniel@iogearbox.net
parent 14bef1ab
...@@ -219,10 +219,7 @@ static void test_tailcall_2(void) ...@@ -219,10 +219,7 @@ static void test_tailcall_2(void)
bpf_object__close(obj); bpf_object__close(obj);
} }
/* test_tailcall_3 checks that the count value of the tail call limit static void test_tailcall_count(const char *which)
* enforcement matches with expectations.
*/
static void test_tailcall_3(void)
{ {
int err, map_fd, prog_fd, main_fd, data_fd, i, val; int err, map_fd, prog_fd, main_fd, data_fd, i, val;
struct bpf_map *prog_array, *data_map; struct bpf_map *prog_array, *data_map;
...@@ -231,7 +228,7 @@ static void test_tailcall_3(void) ...@@ -231,7 +228,7 @@ static void test_tailcall_3(void)
__u32 retval, duration; __u32 retval, duration;
char buff[128] = {}; char buff[128] = {};
err = bpf_prog_load("tailcall3.o", BPF_PROG_TYPE_SCHED_CLS, &obj, err = bpf_prog_load(which, BPF_PROG_TYPE_SCHED_CLS, &obj,
&prog_fd); &prog_fd);
if (CHECK_FAIL(err)) if (CHECK_FAIL(err))
return; return;
...@@ -296,6 +293,22 @@ static void test_tailcall_3(void) ...@@ -296,6 +293,22 @@ static void test_tailcall_3(void)
bpf_object__close(obj); bpf_object__close(obj);
} }
/* test_tailcall_3 checks that the count value of the tail call limit
* enforcement matches with expectations. JIT uses direct jump.
*/
static void test_tailcall_3(void)
{
test_tailcall_count("tailcall3.o");
}
/* test_tailcall_6 checks that the count value of the tail call limit
* enforcement matches with expectations. JIT uses indirect jump.
*/
static void test_tailcall_6(void)
{
test_tailcall_count("tailcall6.o");
}
/* test_tailcall_4 checks that the kernel properly selects indirect jump /* test_tailcall_4 checks that the kernel properly selects indirect jump
* for the case where the key is not known. Latter is passed via global * for the case where the key is not known. Latter is passed via global
* data to select different targets we can compare return value of. * data to select different targets we can compare return value of.
...@@ -822,6 +835,8 @@ void test_tailcalls(void) ...@@ -822,6 +835,8 @@ void test_tailcalls(void)
test_tailcall_4(); test_tailcall_4();
if (test__start_subtest("tailcall_5")) if (test__start_subtest("tailcall_5"))
test_tailcall_5(); test_tailcall_5();
if (test__start_subtest("tailcall_6"))
test_tailcall_6();
if (test__start_subtest("tailcall_bpf2bpf_1")) if (test__start_subtest("tailcall_bpf2bpf_1"))
test_tailcall_bpf2bpf_1(); test_tailcall_bpf2bpf_1();
if (test__start_subtest("tailcall_bpf2bpf_2")) if (test__start_subtest("tailcall_bpf2bpf_2"))
......
// SPDX-License-Identifier: GPL-2.0
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__uint(key_size, sizeof(__u32));
__uint(value_size, sizeof(__u32));
} jmp_table SEC(".maps");
int count, which;
SEC("classifier/0")
int bpf_func_0(struct __sk_buff *skb)
{
count++;
if (__builtin_constant_p(which))
__bpf_unreachable();
bpf_tail_call(skb, &jmp_table, which);
return 1;
}
SEC("classifier")
int entry(struct __sk_buff *skb)
{
if (__builtin_constant_p(which))
__bpf_unreachable();
bpf_tail_call(skb, &jmp_table, which);
return 0;
}
char __license[] SEC("license") = "GPL";
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment