Skip to content

Commit 4cd58e9

Browse files
Yonghong SongAlexei Starovoitov
Yonghong Song
authored and
Alexei Starovoitov
committed
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 <[email protected]> Signed-off-by: Yonghong Song <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent 7058e3a commit 4cd58e9

File tree

3 files changed

+56
-23
lines changed

3 files changed

+56
-23
lines changed

arch/x86/net/bpf_jit_comp.c

+18-10
Original file line numberDiff line numberDiff line change
@@ -1815,16 +1815,24 @@ st: if (is_imm8(insn->off))
18151815
break;
18161816

18171817
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+
}
18281836

18291837
if (!jmp_offset) {
18301838
/*

kernel/bpf/core.c

+16-3
Original file line numberDiff line numberDiff line change
@@ -373,16 +373,25 @@ static int bpf_adj_delta_to_off(struct bpf_insn *insn, u32 pos, s32 end_old,
373373
{
374374
const s32 off_min = S16_MIN, off_max = S16_MAX;
375375
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;
377382

378383
if (curr < pos && curr + off + 1 >= end_old)
379384
off += delta;
380385
else if (curr >= end_new && curr + off + 1 < end_new)
381386
off -= delta;
382387
if (off < off_min || off > off_max)
383388
return -ERANGE;
384-
if (!probe_pass)
385-
insn->off = off;
389+
if (!probe_pass) {
390+
if (insn->code == (BPF_JMP32 | BPF_JA))
391+
insn->imm = off;
392+
else
393+
insn->off = off;
394+
}
386395
return 0;
387396
}
388397

@@ -1593,6 +1602,7 @@ EXPORT_SYMBOL_GPL(__bpf_call_base);
15931602
INSN_3(JMP, JSLE, K), \
15941603
INSN_3(JMP, JSET, K), \
15951604
INSN_2(JMP, JA), \
1605+
INSN_2(JMP32, JA), \
15961606
/* Store instructions. */ \
15971607
/* Register based. */ \
15981608
INSN_3(STX, MEM, B), \
@@ -1989,6 +1999,9 @@ static u64 ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn)
19891999
JMP_JA:
19902000
insn += insn->off;
19912001
CONT;
2002+
JMP32_JA:
2003+
insn += insn->imm;
2004+
CONT;
19922005
JMP_EXIT:
19932006
return BPF_R0;
19942007
/* JMP */

kernel/bpf/verifier.c

+22-10
Original file line numberDiff line numberDiff line change
@@ -2855,7 +2855,10 @@ static int check_subprogs(struct bpf_verifier_env *env)
28552855
goto next;
28562856
if (BPF_OP(code) == BPF_EXIT || BPF_OP(code) == BPF_CALL)
28572857
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;
28592862
if (off < subprog_start || off >= subprog_end) {
28602863
verbose(env, "jump out of range from insn %d to %d\n", i, off);
28612864
return -EINVAL;
@@ -2867,6 +2870,7 @@ static int check_subprogs(struct bpf_verifier_env *env)
28672870
* or unconditional jump back
28682871
*/
28692872
if (code != (BPF_JMP | BPF_EXIT) &&
2873+
code != (BPF_JMP32 | BPF_JA) &&
28702874
code != (BPF_JMP | BPF_JA)) {
28712875
verbose(env, "last insn is not an exit or jmp\n");
28722876
return -EINVAL;
@@ -14792,7 +14796,7 @@ static int visit_func_call_insn(int t, struct bpf_insn *insns,
1479214796
static int visit_insn(int t, struct bpf_verifier_env *env)
1479314797
{
1479414798
struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t];
14795-
int ret;
14799+
int ret, off;
1479614800

1479714801
if (bpf_pseudo_func(insn))
1479814802
return visit_func_call_insn(t, insns, env, true);
@@ -14840,14 +14844,19 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
1484014844
if (BPF_SRC(insn->code) != BPF_K)
1484114845
return -EINVAL;
1484214846

14847+
if (BPF_CLASS(insn->code) == BPF_JMP)
14848+
off = insn->off;
14849+
else
14850+
off = insn->imm;
14851+
1484314852
/* unconditional jump with single edge */
14844-
ret = push_insn(t, t + insn->off + 1, FALLTHROUGH, env,
14853+
ret = push_insn(t, t + off + 1, FALLTHROUGH, env,
1484514854
true);
1484614855
if (ret)
1484714856
return ret;
1484814857

14849-
mark_prune_point(env, t + insn->off + 1);
14850-
mark_jmp_point(env, t + insn->off + 1);
14858+
mark_prune_point(env, t + off + 1);
14859+
mark_jmp_point(env, t + off + 1);
1485114860

1485214861
return ret;
1485314862

@@ -16643,15 +16652,18 @@ static int do_check(struct bpf_verifier_env *env)
1664316652
mark_reg_scratched(env, BPF_REG_0);
1664416653
} else if (opcode == BPF_JA) {
1664516654
if (BPF_SRC(insn->code) != BPF_K ||
16646-
insn->imm != 0 ||
1664716655
insn->src_reg != BPF_REG_0 ||
1664816656
insn->dst_reg != BPF_REG_0 ||
16649-
class == BPF_JMP32) {
16657+
(class == BPF_JMP && insn->imm != 0) ||
16658+
(class == BPF_JMP32 && insn->off != 0)) {
1665016659
verbose(env, "BPF_JA uses reserved fields\n");
1665116660
return -EINVAL;
1665216661
}
1665316662

16654-
env->insn_idx += insn->off + 1;
16663+
if (class == BPF_JMP)
16664+
env->insn_idx += insn->off + 1;
16665+
else
16666+
env->insn_idx += insn->imm + 1;
1665516667
continue;
1665616668

1665716669
} else if (opcode == BPF_EXIT) {
@@ -17498,13 +17510,13 @@ static bool insn_is_cond_jump(u8 code)
1749817510
{
1749917511
u8 op;
1750017512

17513+
op = BPF_OP(code);
1750117514
if (BPF_CLASS(code) == BPF_JMP32)
17502-
return true;
17515+
return op != BPF_JA;
1750317516

1750417517
if (BPF_CLASS(code) != BPF_JMP)
1750517518
return false;
1750617519

17507-
op = BPF_OP(code);
1750817520
return op != BPF_JA && op != BPF_EXIT && op != BPF_CALL;
1750917521
}
1751017522

0 commit comments

Comments
 (0)