In the current code, the actual max tail call count is 33 which is greater
than MAX_TAIL_CALL_CNT (defined as 32), the actual limit is not consistent
with the meaning of MAX_TAIL_CALL_CNT, there is some confusion and need to
spend some time to think the reason at the first glance.
We can see the historical evolution from commit 04fd61ab36ec ("bpf: allow
bpf programs to tail-call other bpf programs") and commit f9dabe016b63
("bpf: Undo off-by-one in interpreter tail call count limit").
In order to avoid changing existing behavior, the actual limit is 33 now,
this is resonable.
After commit 874be05f525e ("bpf, tests: Add tail call test suite"), we can
see there exists failed testcase.
On all archs when CONFIG_BPF_JIT_ALWAYS_ON is not set:
# echo 0 > /proc/sys/net/core/bpf_jit_enable
# modprobe test_bpf
# dmesg | grep -w FAIL
Tail call error path, max count reached jited:0 ret 34 != 33 FAIL
On some archs:
# echo 1 > /proc/sys/net/core/bpf_jit_enable
# modprobe test_bpf
# dmesg | grep -w FAIL
Tail call error path, max count reached jited:1 ret 34 != 33 FAIL
So it is necessary to change the value of MAX_TAIL_CALL_CNT from 32 to 33,
then do some small changes of the related code.
With this patch, it does not change the current limit, MAX_TAIL_CALL_CNT
can reflect the actual max tail call count, and the above failed testcase
can be fixed.
Signed-off-by: Tiezhu Yang <[email protected]>
---
arch/arm/net/bpf_jit_32.c | 11 ++++++-----
arch/arm64/net/bpf_jit_comp.c | 7 ++++---
arch/mips/net/ebpf_jit.c | 4 ++--
arch/powerpc/net/bpf_jit_comp32.c | 4 ++--
arch/powerpc/net/bpf_jit_comp64.c | 12 ++++++------
arch/riscv/net/bpf_jit_comp32.c | 4 ++--
arch/riscv/net/bpf_jit_comp64.c | 4 ++--
arch/sparc/net/bpf_jit_comp_64.c | 8 ++++----
include/linux/bpf.h | 2 +-
kernel/bpf/core.c | 4 ++--
10 files changed, 31 insertions(+), 29 deletions(-)
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
index a951276..39d9ae9 100644
--- a/arch/arm/net/bpf_jit_32.c
+++ b/arch/arm/net/bpf_jit_32.c
@@ -1180,18 +1180,19 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx)
/* tmp2[0] = array, tmp2[1] = index */
- /* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
- * goto out;
+ /*
* tail_call_cnt++;
+ * if (tail_call_cnt > MAX_TAIL_CALL_CNT)
+ * goto out;
*/
+ tc = arm_bpf_get_reg64(tcc, tmp, ctx);
+ emit(ARM_ADDS_I(tc[1], tc[1], 1), ctx);
+ emit(ARM_ADC_I(tc[0], tc[0], 0), ctx);
lo = (u32)MAX_TAIL_CALL_CNT;
hi = (u32)((u64)MAX_TAIL_CALL_CNT >> 32);
- tc = arm_bpf_get_reg64(tcc, tmp, ctx);
emit(ARM_CMP_I(tc[0], hi), ctx);
_emit(ARM_COND_EQ, ARM_CMP_I(tc[1], lo), ctx);
_emit(ARM_COND_HI, ARM_B(jmp_offset), ctx);
- emit(ARM_ADDS_I(tc[1], tc[1], 1), ctx);
- emit(ARM_ADC_I(tc[0], tc[0], 0), ctx);
arm_bpf_put_reg64(tcc, tmp, ctx);
/* prog = array->ptrs[index]
diff --git a/arch/arm64/net/bpf_jit_comp.c b/arch/arm64/net/bpf_jit_comp.c
index 41c23f4..5d6c843 100644
--- a/arch/arm64/net/bpf_jit_comp.c
+++ b/arch/arm64/net/bpf_jit_comp.c
@@ -286,14 +286,15 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx)
emit(A64_CMP(0, r3, tmp), ctx);
emit(A64_B_(A64_COND_CS, jmp_offset), ctx);
- /* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
- * goto out;
+ /*
* tail_call_cnt++;
+ * if (tail_call_cnt > MAX_TAIL_CALL_CNT)
+ * goto out;
*/
+ emit(A64_ADD_I(1, tcc, tcc, 1), ctx);
emit_a64_mov_i64(tmp, MAX_TAIL_CALL_CNT, ctx);
emit(A64_CMP(1, tcc, tmp), ctx);
emit(A64_B_(A64_COND_HI, jmp_offset), ctx);
- emit(A64_ADD_I(1, tcc, tcc, 1), ctx);
/* prog = array->ptrs[index];
* if (prog == NULL)
diff --git a/arch/mips/net/ebpf_jit.c b/arch/mips/net/ebpf_jit.c
index 3a73e93..029fc34 100644
--- a/arch/mips/net/ebpf_jit.c
+++ b/arch/mips/net/ebpf_jit.c
@@ -617,14 +617,14 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int this_idx)
b_off = b_imm(this_idx + 1, ctx);
emit_instr(ctx, bne, MIPS_R_AT, MIPS_R_ZERO, b_off);
/*
- * if (TCC-- < 0)
+ * if (--TCC < 0)
* goto out;
*/
/* Delay slot */
tcc_reg = (ctx->flags & EBPF_TCC_IN_V1) ? MIPS_R_V1 : MIPS_R_S4;
emit_instr(ctx, daddiu, MIPS_R_T5, tcc_reg, -1);
b_off = b_imm(this_idx + 1, ctx);
- emit_instr(ctx, bltz, tcc_reg, b_off);
+ emit_instr(ctx, bltz, MIPS_R_T5, b_off);
/*
* prog = array->ptrs[index];
* if (prog == NULL)
diff --git a/arch/powerpc/net/bpf_jit_comp32.c b/arch/powerpc/net/bpf_jit_comp32.c
index beb12cb..b5585ad 100644
--- a/arch/powerpc/net/bpf_jit_comp32.c
+++ b/arch/powerpc/net/bpf_jit_comp32.c
@@ -221,12 +221,12 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32
PPC_BCC(COND_GE, out);
/*
+ * tail_call_cnt++;
* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
* goto out;
*/
- EMIT(PPC_RAW_CMPLWI(_R0, MAX_TAIL_CALL_CNT));
- /* tail_call_cnt++; */
EMIT(PPC_RAW_ADDIC(_R0, _R0, 1));
+ EMIT(PPC_RAW_CMPLWI(_R0, MAX_TAIL_CALL_CNT));
PPC_BCC(COND_GT, out);
/* prog = array->ptrs[index]; */
diff --git a/arch/powerpc/net/bpf_jit_comp64.c b/arch/powerpc/net/bpf_jit_comp64.c
index b87a63d..bb15cc4 100644
--- a/arch/powerpc/net/bpf_jit_comp64.c
+++ b/arch/powerpc/net/bpf_jit_comp64.c
@@ -227,6 +227,12 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32
PPC_BCC(COND_GE, out);
/*
+ * tail_call_cnt++;
+ */
+ EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], 1));
+ PPC_BPF_STL(b2p[TMP_REG_1], 1, bpf_jit_stack_tailcallcnt(ctx));
+
+ /*
* if (tail_call_cnt > MAX_TAIL_CALL_CNT)
* goto out;
*/
@@ -234,12 +240,6 @@ static void bpf_jit_emit_tail_call(u32 *image, struct codegen_context *ctx, u32
EMIT(PPC_RAW_CMPLWI(b2p[TMP_REG_1], MAX_TAIL_CALL_CNT));
PPC_BCC(COND_GT, out);
- /*
- * tail_call_cnt++;
- */
- EMIT(PPC_RAW_ADDI(b2p[TMP_REG_1], b2p[TMP_REG_1], 1));
- PPC_BPF_STL(b2p[TMP_REG_1], 1, bpf_jit_stack_tailcallcnt(ctx));
-
/* prog = array->ptrs[index]; */
EMIT(PPC_RAW_MULI(b2p[TMP_REG_1], b2p_index, 8));
EMIT(PPC_RAW_ADD(b2p[TMP_REG_1], b2p[TMP_REG_1], b2p_bpf_array));
diff --git a/arch/riscv/net/bpf_jit_comp32.c b/arch/riscv/net/bpf_jit_comp32.c
index e649742..1608d94 100644
--- a/arch/riscv/net/bpf_jit_comp32.c
+++ b/arch/riscv/net/bpf_jit_comp32.c
@@ -800,12 +800,12 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx)
/*
* temp_tcc = tcc - 1;
- * if (tcc < 0)
+ * if (temp_tcc < 0)
* goto out;
*/
emit(rv_addi(RV_REG_T1, RV_REG_TCC, -1), ctx);
off = ninsns_rvoff(tc_ninsn - (ctx->ninsns - start_insn));
- emit_bcc(BPF_JSLT, RV_REG_TCC, RV_REG_ZERO, off, ctx);
+ emit_bcc(BPF_JSLT, RV_REG_T1, RV_REG_ZERO, off, ctx);
/*
* prog = array->ptrs[index];
diff --git a/arch/riscv/net/bpf_jit_comp64.c b/arch/riscv/net/bpf_jit_comp64.c
index 3af4131..6e9ba83 100644
--- a/arch/riscv/net/bpf_jit_comp64.c
+++ b/arch/riscv/net/bpf_jit_comp64.c
@@ -311,12 +311,12 @@ static int emit_bpf_tail_call(int insn, struct rv_jit_context *ctx)
off = ninsns_rvoff(tc_ninsn - (ctx->ninsns - start_insn));
emit_branch(BPF_JGE, RV_REG_A2, RV_REG_T1, off, ctx);
- /* if (TCC-- < 0)
+ /* if (--TCC < 0)
* goto out;
*/
emit_addi(RV_REG_T1, tcc, -1, ctx);
off = ninsns_rvoff(tc_ninsn - (ctx->ninsns - start_insn));
- emit_branch(BPF_JSLT, tcc, RV_REG_ZERO, off, ctx);
+ emit_branch(BPF_JSLT, RV_REG_T1, RV_REG_ZERO, off, ctx);
/* prog = array->ptrs[index];
* if (!prog)
diff --git a/arch/sparc/net/bpf_jit_comp_64.c b/arch/sparc/net/bpf_jit_comp_64.c
index 9a2f20c..50d914c 100644
--- a/arch/sparc/net/bpf_jit_comp_64.c
+++ b/arch/sparc/net/bpf_jit_comp_64.c
@@ -863,6 +863,10 @@ static void emit_tail_call(struct jit_ctx *ctx)
emit_branch(BGEU, ctx->idx, ctx->idx + OFFSET1, ctx);
emit_nop(ctx);
+ emit_alu_K(ADD, tmp, 1, ctx);
+ off = BPF_TAILCALL_CNT_SP_OFF;
+ emit(ST32 | IMMED | RS1(SP) | S13(off) | RD(tmp), ctx);
+
off = BPF_TAILCALL_CNT_SP_OFF;
emit(LD32 | IMMED | RS1(SP) | S13(off) | RD(tmp), ctx);
emit_cmpi(tmp, MAX_TAIL_CALL_CNT, ctx);
@@ -870,10 +874,6 @@ static void emit_tail_call(struct jit_ctx *ctx)
emit_branch(BGU, ctx->idx, ctx->idx + OFFSET2, ctx);
emit_nop(ctx);
- emit_alu_K(ADD, tmp, 1, ctx);
- off = BPF_TAILCALL_CNT_SP_OFF;
- emit(ST32 | IMMED | RS1(SP) | S13(off) | RD(tmp), ctx);
-
emit_alu3_K(SLL, bpf_index, 3, tmp, ctx);
emit_alu(ADD, bpf_array, tmp, ctx);
off = offsetof(struct bpf_array, ptrs);
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index f4c16f1..224cc7e 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1046,7 +1046,7 @@ struct bpf_array {
};
#define BPF_COMPLEXITY_LIMIT_INSNS 1000000 /* yes. 1M insns */
-#define MAX_TAIL_CALL_CNT 32
+#define MAX_TAIL_CALL_CNT 33
#define BPF_F_ACCESS_MASK (BPF_F_RDONLY | \
BPF_F_RDONLY_PROG | \
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 9f4636d..8edb1c3 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1564,10 +1564,10 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
if (unlikely(index >= array->map.max_entries))
goto out;
- if (unlikely(tail_call_cnt > MAX_TAIL_CALL_CNT))
- goto out;
tail_call_cnt++;
+ if (unlikely(tail_call_cnt > MAX_TAIL_CALL_CNT))
+ goto out;
prog = READ_ONCE(array->ptrs[index]);
if (!prog)
--
2.1.0
On Wed, Sep 8, 2021 at 8:33 PM Tiezhu Yang <[email protected]> wrote:
>
> In the current code, the actual max tail call count is 33 which is greater
> than MAX_TAIL_CALL_CNT (defined as 32), the actual limit is not consistent
> with the meaning of MAX_TAIL_CALL_CNT, there is some confusion and need to
> spend some time to think the reason at the first glance.
think *about* the reason
>
> We can see the historical evolution from commit 04fd61ab36ec ("bpf: allow
> bpf programs to tail-call other bpf programs") and commit f9dabe016b63
> ("bpf: Undo off-by-one in interpreter tail call count limit").
>
> In order to avoid changing existing behavior, the actual limit is 33 now,
> this is resonable.
typo: reasonable
>
> After commit 874be05f525e ("bpf, tests: Add tail call test suite"), we can
> see there exists failed testcase.
>
> On all archs when CONFIG_BPF_JIT_ALWAYS_ON is not set:
> # echo 0 > /proc/sys/net/core/bpf_jit_enable
> # modprobe test_bpf
> # dmesg | grep -w FAIL
> Tail call error path, max count reached jited:0 ret 34 != 33 FAIL
>
> On some archs:
> # echo 1 > /proc/sys/net/core/bpf_jit_enable
> # modprobe test_bpf
> # dmesg | grep -w FAIL
> Tail call error path, max count reached jited:1 ret 34 != 33 FAIL
>
> So it is necessary to change the value of MAX_TAIL_CALL_CNT from 32 to 33,
> then do some small changes of the related code.
>
> With this patch, it does not change the current limit, MAX_TAIL_CALL_CNT
> can reflect the actual max tail call count, and the above failed testcase
> can be fixed.
>
> Signed-off-by: Tiezhu Yang <[email protected]>
> ---
This change breaks selftests ([0]), please fix them at the same time
as you are changing the kernel behavior:
test_tailcall_2:PASS:tailcall 128 nsec
test_tailcall_2:PASS:tailcall 128 nsec
test_tailcall_2:FAIL:tailcall err 0 errno 2 retval 4
#135/2 tailcalls/tailcall_2:FAIL
test_tailcall_3:PASS:tailcall 128 nsec
test_tailcall_3:FAIL:tailcall count err 0 errno 2 count 34
test_tailcall_3:PASS:tailcall 128 nsec
#135/3 tailcalls/tailcall_3:FAIL
#135/4 tailcalls/tailcall_4:OK
#135/5 tailcalls/tailcall_5:OK
#135/6 tailcalls/tailcall_bpf2bpf_1:OK
test_tailcall_bpf2bpf_2:PASS:tailcall 128 nsec
test_tailcall_bpf2bpf_2:FAIL:tailcall count err 0 errno 2 count 34
test_tailcall_bpf2bpf_2:PASS:tailcall 128 nsec
#135/7 tailcalls/tailcall_bpf2bpf_2:FAIL
#135/8 tailcalls/tailcall_bpf2bpf_3:OK
test_tailcall_bpf2bpf_4:PASS:tailcall 54 nsec
test_tailcall_bpf2bpf_4:FAIL:tailcall count err 0 errno 2 count 32
#135/9 tailcalls/tailcall_bpf2bpf_4:FAIL
test_tailcall_bpf2bpf_4:PASS:tailcall 54 nsec
test_tailcall_bpf2bpf_4:FAIL:tailcall count err 0 errno 2 count 32
#135/10 tailcalls/tailcall_bpf2bpf_5:FAIL
#135 tailcalls:FAIL
[0] https://github.com/kernel-patches/bpf/pull/1747/checks?check_run_id=3552002906
> arch/arm/net/bpf_jit_32.c | 11 ++++++-----
> arch/arm64/net/bpf_jit_comp.c | 7 ++++---
> arch/mips/net/ebpf_jit.c | 4 ++--
> arch/powerpc/net/bpf_jit_comp32.c | 4 ++--
> arch/powerpc/net/bpf_jit_comp64.c | 12 ++++++------
> arch/riscv/net/bpf_jit_comp32.c | 4 ++--
> arch/riscv/net/bpf_jit_comp64.c | 4 ++--
> arch/sparc/net/bpf_jit_comp_64.c | 8 ++++----
> include/linux/bpf.h | 2 +-
> kernel/bpf/core.c | 4 ++--
> 10 files changed, 31 insertions(+), 29 deletions(-)
>
[...]
On 9/9/21 7:50 AM, Andrii Nakryiko wrote:
> On Wed, Sep 8, 2021 at 8:33 PM Tiezhu Yang <[email protected]> wrote:
>>
>> In the current code, the actual max tail call count is 33 which is greater
>> than MAX_TAIL_CALL_CNT (defined as 32), the actual limit is not consistent
>> with the meaning of MAX_TAIL_CALL_CNT, there is some confusion and need to
>> spend some time to think the reason at the first glance.
>
> think *about* the reason
>
>> We can see the historical evolution from commit 04fd61ab36ec ("bpf: allow
>> bpf programs to tail-call other bpf programs") and commit f9dabe016b63
>> ("bpf: Undo off-by-one in interpreter tail call count limit").
>>
>> In order to avoid changing existing behavior, the actual limit is 33 now,
>> this is resonable.
>
> typo: reasonable
>
>> After commit 874be05f525e ("bpf, tests: Add tail call test suite"), we can
>> see there exists failed testcase.
>>
>> On all archs when CONFIG_BPF_JIT_ALWAYS_ON is not set:
>> # echo 0 > /proc/sys/net/core/bpf_jit_enable
>> # modprobe test_bpf
>> # dmesg | grep -w FAIL
>> Tail call error path, max count reached jited:0 ret 34 != 33 FAIL
>>
>> On some archs:
>> # echo 1 > /proc/sys/net/core/bpf_jit_enable
>> # modprobe test_bpf
>> # dmesg | grep -w FAIL
>> Tail call error path, max count reached jited:1 ret 34 != 33 FAIL
>>
>> So it is necessary to change the value of MAX_TAIL_CALL_CNT from 32 to 33,
>> then do some small changes of the related code.
>>
>> With this patch, it does not change the current limit, MAX_TAIL_CALL_CNT
>> can reflect the actual max tail call count, and the above failed testcase
>> can be fixed.
>>
>> Signed-off-by: Tiezhu Yang <[email protected]>
>> ---
>
> This change breaks selftests ([0]), please fix them at the same time
> as you are changing the kernel behavior:
The below selftests shouldn't have to change given there is no change in
behavior intended (MAX_TAIL_CALL_CNT is bumped to 33 but counter inc'ed
prior to the comparison). It just means that /all/ JITs must be changed
and in particular properly _tested_.
> test_tailcall_2:PASS:tailcall 128 nsec
> test_tailcall_2:PASS:tailcall 128 nsec
> test_tailcall_2:FAIL:tailcall err 0 errno 2 retval 4
> #135/2 tailcalls/tailcall_2:FAIL
> test_tailcall_3:PASS:tailcall 128 nsec
> test_tailcall_3:FAIL:tailcall count err 0 errno 2 count 34
> test_tailcall_3:PASS:tailcall 128 nsec
> #135/3 tailcalls/tailcall_3:FAIL
> #135/4 tailcalls/tailcall_4:OK
> #135/5 tailcalls/tailcall_5:OK
> #135/6 tailcalls/tailcall_bpf2bpf_1:OK
> test_tailcall_bpf2bpf_2:PASS:tailcall 128 nsec
> test_tailcall_bpf2bpf_2:FAIL:tailcall count err 0 errno 2 count 34
> test_tailcall_bpf2bpf_2:PASS:tailcall 128 nsec
> #135/7 tailcalls/tailcall_bpf2bpf_2:FAIL
> #135/8 tailcalls/tailcall_bpf2bpf_3:OK
> test_tailcall_bpf2bpf_4:PASS:tailcall 54 nsec
> test_tailcall_bpf2bpf_4:FAIL:tailcall count err 0 errno 2 count 32
> #135/9 tailcalls/tailcall_bpf2bpf_4:FAIL
> test_tailcall_bpf2bpf_4:PASS:tailcall 54 nsec
> test_tailcall_bpf2bpf_4:FAIL:tailcall count err 0 errno 2 count 32
> #135/10 tailcalls/tailcall_bpf2bpf_5:FAIL
> #135 tailcalls:FAIL
>
> [0] https://github.com/kernel-patches/bpf/pull/1747/checks?check_run_id=3552002906
>
>> arch/arm/net/bpf_jit_32.c | 11 ++++++-----
>> arch/arm64/net/bpf_jit_comp.c | 7 ++++---
>> arch/mips/net/ebpf_jit.c | 4 ++--
>> arch/powerpc/net/bpf_jit_comp32.c | 4 ++--
>> arch/powerpc/net/bpf_jit_comp64.c | 12 ++++++------
>> arch/riscv/net/bpf_jit_comp32.c | 4 ++--
>> arch/riscv/net/bpf_jit_comp64.c | 4 ++--
>> arch/sparc/net/bpf_jit_comp_64.c | 8 ++++----
>> include/linux/bpf.h | 2 +-
>> kernel/bpf/core.c | 4 ++--
>> 10 files changed, 31 insertions(+), 29 deletions(-)
>>
>
> [...]
>