Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Support new 32bit offset jmp instruction

Add interpreter/jit/verifier support for 32bit offset jmp instruction.
If a conditional jmp instruction needs more than 16bit offset,
it can be simulated with a conditional jmp + a 32bit jmp insn.

Acked-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
Link: https://lore.kernel.org/r/20230728011231.3716103-1-yonghong.song@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Yonghong Song and committed by
Alexei Starovoitov
4cd58e9a 7058e3a3

+56 -23
+18 -10
arch/x86/net/bpf_jit_comp.c
··· 1815 1815 break; 1816 1816 1817 1817 case BPF_JMP | BPF_JA: 1818 - if (insn->off == -1) 1819 - /* -1 jmp instructions will always jump 1820 - * backwards two bytes. Explicitly handling 1821 - * this case avoids wasting too many passes 1822 - * when there are long sequences of replaced 1823 - * dead code. 1824 - */ 1825 - jmp_offset = -2; 1826 - else 1827 - jmp_offset = addrs[i + insn->off] - addrs[i]; 1818 + case BPF_JMP32 | BPF_JA: 1819 + if (BPF_CLASS(insn->code) == BPF_JMP) { 1820 + if (insn->off == -1) 1821 + /* -1 jmp instructions will always jump 1822 + * backwards two bytes. Explicitly handling 1823 + * this case avoids wasting too many passes 1824 + * when there are long sequences of replaced 1825 + * dead code. 1826 + */ 1827 + jmp_offset = -2; 1828 + else 1829 + jmp_offset = addrs[i + insn->off] - addrs[i]; 1830 + } else { 1831 + if (insn->imm == -1) 1832 + jmp_offset = -2; 1833 + else 1834 + jmp_offset = addrs[i + insn->imm] - addrs[i]; 1835 + } 1828 1836 1829 1837 if (!jmp_offset) { 1830 1838 /*
+16 -3
kernel/bpf/core.c
··· 373 373 { 374 374 const s32 off_min = S16_MIN, off_max = S16_MAX; 375 375 s32 delta = end_new - end_old; 376 - s32 off = insn->off; 376 + s32 off; 377 + 378 + if (insn->code == (BPF_JMP32 | BPF_JA)) 379 + off = insn->imm; 380 + else 381 + off = insn->off; 377 382 378 383 if (curr < pos && curr + off + 1 >= end_old) 379 384 off += delta; ··· 386 381 off -= delta; 387 382 if (off < off_min || off > off_max) 388 383 return -ERANGE; 389 - if (!probe_pass) 390 - insn->off = off; 384 + if (!probe_pass) { 385 + if (insn->code == (BPF_JMP32 | BPF_JA)) 386 + insn->imm = off; 387 + else 388 + insn->off = off; 389 + } 391 390 return 0; 392 391 } 393 392 ··· 1602 1593 INSN_3(JMP, JSLE, K), \ 1603 1594 INSN_3(JMP, JSET, K), \ 1604 1595 INSN_2(JMP, JA), \ 1596 + INSN_2(JMP32, JA), \ 1605 1597 /* Store instructions. */ \ 1606 1598 /* Register based. */ \ 1607 1599 INSN_3(STX, MEM, B), \ ··· 1998 1988 } 1999 1989 JMP_JA: 2000 1990 insn += insn->off; 1991 + CONT; 1992 + JMP32_JA: 1993 + insn += insn->imm; 2001 1994 CONT; 2002 1995 JMP_EXIT: 2003 1996 return BPF_R0;
+22 -10
kernel/bpf/verifier.c
··· 2855 2855 goto next; 2856 2856 if (BPF_OP(code) == BPF_EXIT || BPF_OP(code) == BPF_CALL) 2857 2857 goto next; 2858 - off = i + insn[i].off + 1; 2858 + if (code == (BPF_JMP32 | BPF_JA)) 2859 + off = i + insn[i].imm + 1; 2860 + else 2861 + off = i + insn[i].off + 1; 2859 2862 if (off < subprog_start || off >= subprog_end) { 2860 2863 verbose(env, "jump out of range from insn %d to %d\n", i, off); 2861 2864 return -EINVAL; ··· 2870 2867 * or unconditional jump back 2871 2868 */ 2872 2869 if (code != (BPF_JMP | BPF_EXIT) && 2870 + code != (BPF_JMP32 | BPF_JA) && 2873 2871 code != (BPF_JMP | BPF_JA)) { 2874 2872 verbose(env, "last insn is not an exit or jmp\n"); 2875 2873 return -EINVAL; ··· 14796 14792 static int visit_insn(int t, struct bpf_verifier_env *env) 14797 14793 { 14798 14794 struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t]; 14799 - int ret; 14795 + int ret, off; 14800 14796 14801 14797 if (bpf_pseudo_func(insn)) 14802 14798 return visit_func_call_insn(t, insns, env, true); ··· 14844 14840 if (BPF_SRC(insn->code) != BPF_K) 14845 14841 return -EINVAL; 14846 14842 14843 + if (BPF_CLASS(insn->code) == BPF_JMP) 14844 + off = insn->off; 14845 + else 14846 + off = insn->imm; 14847 + 14847 14848 /* unconditional jump with single edge */ 14848 - ret = push_insn(t, t + insn->off + 1, FALLTHROUGH, env, 14849 + ret = push_insn(t, t + off + 1, FALLTHROUGH, env, 14849 14850 true); 14850 14851 if (ret) 14851 14852 return ret; 14852 14853 14853 - mark_prune_point(env, t + insn->off + 1); 14854 - mark_jmp_point(env, t + insn->off + 1); 14854 + mark_prune_point(env, t + off + 1); 14855 + mark_jmp_point(env, t + off + 1); 14855 14856 14856 14857 return ret; 14857 14858 ··· 16652 16643 mark_reg_scratched(env, BPF_REG_0); 16653 16644 } else if (opcode == BPF_JA) { 16654 16645 if (BPF_SRC(insn->code) != BPF_K || 16655 - insn->imm != 0 || 16656 16646 insn->src_reg != BPF_REG_0 || 16657 16647 insn->dst_reg != BPF_REG_0 || 16658 - class == BPF_JMP32) { 16648 + (class == BPF_JMP && insn->imm != 0) || 16649 + (class == BPF_JMP32 && insn->off != 0)) { 16659 16650 verbose(env, "BPF_JA uses reserved fields\n"); 16660 16651 return -EINVAL; 16661 16652 } 16662 16653 16663 - env->insn_idx += insn->off + 1; 16654 + if (class == BPF_JMP) 16655 + env->insn_idx += insn->off + 1; 16656 + else 16657 + env->insn_idx += insn->imm + 1; 16664 16658 continue; 16665 16659 16666 16660 } else if (opcode == BPF_EXIT) { ··· 17510 17498 { 17511 17499 u8 op; 17512 17500 17501 + op = BPF_OP(code); 17513 17502 if (BPF_CLASS(code) == BPF_JMP32) 17514 - return true; 17503 + return op != BPF_JA; 17515 17504 17516 17505 if (BPF_CLASS(code) != BPF_JMP) 17517 17506 return false; 17518 17507 17519 - op = BPF_OP(code); 17520 17508 return op != BPF_JA && op != BPF_EXIT && op != BPF_CALL; 17521 17509 } 17522 17510