]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
LoongArch: BPF: Fix off-by-one error in tail call
authorTiezhu Yang <yangtiezhu@loongson.cn>
Thu, 25 Jun 2026 05:03:53 +0000 (13:03 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Thu, 25 Jun 2026 05:03:53 +0000 (13:03 +0800)
The current code updates the tail call counter (TCC) using a pre-increment
approach, it stores the incremented value back to memory before performing
any boundary or target validation checks.

This causes two major issues:
1. When a tail call fails because the target program is NULL, the TCC is
   incorrectly incremented and saved in memory anyway.
2. This dummy increment implicitly consumes one slot of the allowed tail
   call budget. As a result, the subsequent loop reaches the maximum limit
   prematurely, leading to a test failure where the actual loop count is
   32 instead of the expected 33.

Fix this by deferring the counter update. Change the branch condition to
BPF_JSGE (greater or equal) so that we check the boundary first. The TCC
is only incremented and stored back to memory after the boundary check
and the NULL-target check both pass.

Before:

  $ sudo ./test_progs -t tailcalls/tailcall_3
  ...
  test_tailcall_count:FAIL:tailcall count unexpected tailcall count: actual 32 != expected 33
  ...
  #465/3   tailcalls/tailcall_3:FAIL
  #465     tailcalls:FAIL

After:

  $ sudo ./test_progs -t tailcalls/tailcall_3
  #465/3   tailcalls/tailcall_3:OK
  #465     tailcalls:OK
  Summary: 1/1 PASSED, 0 SKIPPED, 0 FAILED

Fixes: c0fcc955ff82 ("LoongArch: BPF: Fix the tailcall hierarchy")
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/net/bpf_jit.c

index a2a42c0a0f9d533572d073c60f4b39dc97054637..99294f93091489d67657b212f03b444282a206a3 100644 (file)
@@ -324,12 +324,12 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)
         */
        emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off);
        emit_insn(ctx, ldd, t3, REG_TCC, 0);
-       emit_insn(ctx, addid, t3, t3, 1);
-       emit_insn(ctx, std, t3, REG_TCC, 0);
        emit_insn(ctx, addid, t2, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT);
-       if (emit_tailcall_jmp(ctx, BPF_JSGT, t3, t2, jmp_offset) < 0)
+       if (emit_tailcall_jmp(ctx, BPF_JSGE, t3, t2, jmp_offset) < 0)
                goto toofar;
 
+       emit_insn(ctx, addid, t3, t3, 1);
+
        /*
         * prog = array->ptrs[index];
         * if (!prog)
@@ -342,6 +342,8 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)
        if (emit_tailcall_jmp(ctx, BPF_JEQ, t2, LOONGARCH_GPR_ZERO, jmp_offset) < 0)
                goto toofar;
 
+       emit_insn(ctx, std, t3, REG_TCC, 0);
+
        /* goto *(prog->bpf_func + 4); */
        off = offsetof(struct bpf_prog, bpf_func);
        emit_insn(ctx, ldd, t3, t2, off);