Skip to content

Commit 6082b6c

Browse files
Alexei Starovoitovanakryiko
authored andcommitted
bpf: Recognize addr_space_cast instruction in the verifier.
rY = addr_space_cast(rX, 0, 1) tells the verifier that rY->type = PTR_TO_ARENA. Any further operations on PTR_TO_ARENA register have to be in 32-bit domain. The verifier will mark load/store through PTR_TO_ARENA with PROBE_MEM32. JIT will generate them as kern_vm_start + 32bit_addr memory accesses. rY = addr_space_cast(rX, 1, 0) tells the verifier that rY->type = unknown scalar. If arena->map_flags has BPF_F_NO_USER_CONV set then convert cast_user to mov32 as well. Otherwise JIT will convert it to: rY = (u32)rX; if (rY) rY |= arena->user_vm_start & ~(u64)~0U; Signed-off-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/bpf/20240308010812.89848-6-alexei.starovoitov@gmail.com
1 parent 142fd4d commit 6082b6c

5 files changed

Lines changed: 109 additions & 9 deletions

File tree

include/linux/bpf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,7 @@ enum bpf_reg_type {
883883
* an explicit null check is required for this struct.
884884
*/
885885
PTR_TO_MEM, /* reg points to valid memory region */
886+
PTR_TO_ARENA,
886887
PTR_TO_BUF, /* reg points to a read/write buffer */
887888
PTR_TO_FUNC, /* reg points to a bpf program function */
888889
CONST_PTR_TO_DYNPTR, /* reg points to a const struct bpf_dynptr */

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ struct bpf_insn_aux_data {
548548
u32 seen; /* this insn was processed by the verifier at env->pass_cnt */
549549
bool sanitize_stack_spill; /* subject to Spectre v4 sanitation */
550550
bool zext_dst; /* this insn zero extends dst reg */
551+
bool needs_zext; /* alu op needs to clear upper bits */
551552
bool storage_get_func_atomic; /* bpf_*_storage_get() with atomic memory alloc */
552553
bool is_iter_next; /* bpf_iter_<type>_next() kfunc call */
553554
bool call_with_percpu_alloc_ptr; /* {this,per}_cpu_ptr() with prog percpu alloc */

kernel/bpf/log.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,7 @@ const char *reg_type_str(struct bpf_verifier_env *env, enum bpf_reg_type type)
458458
[PTR_TO_XDP_SOCK] = "xdp_sock",
459459
[PTR_TO_BTF_ID] = "ptr_",
460460
[PTR_TO_MEM] = "mem",
461+
[PTR_TO_ARENA] = "arena",
461462
[PTR_TO_BUF] = "buf",
462463
[PTR_TO_FUNC] = "func",
463464
[PTR_TO_MAP_KEY] = "map_key",
@@ -693,6 +694,8 @@ static void print_reg_state(struct bpf_verifier_env *env,
693694
}
694695

695696
verbose(env, "%s", reg_type_str(env, t));
697+
if (t == PTR_TO_ARENA)
698+
return;
696699
if (t == PTR_TO_STACK) {
697700
if (state->frameno != reg->frameno)
698701
verbose(env, "[%d]", reg->frameno);

kernel/bpf/syscall.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4453,6 +4453,12 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog,
44534453
continue;
44544454
}
44554455

4456+
if ((BPF_CLASS(code) == BPF_LDX || BPF_CLASS(code) == BPF_STX ||
4457+
BPF_CLASS(code) == BPF_ST) && BPF_MODE(code) == BPF_PROBE_MEM32) {
4458+
insns[i].code = BPF_CLASS(code) | BPF_SIZE(code) | BPF_MEM;
4459+
continue;
4460+
}
4461+
44564462
if (code != (BPF_LD | BPF_IMM | BPF_DW))
44574463
continue;
44584464

kernel/bpf/verifier.c

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4386,6 +4386,7 @@ static bool is_spillable_regtype(enum bpf_reg_type type)
43864386
case PTR_TO_MEM:
43874387
case PTR_TO_FUNC:
43884388
case PTR_TO_MAP_KEY:
4389+
case PTR_TO_ARENA:
43894390
return true;
43904391
default:
43914392
return false;
@@ -5828,6 +5829,8 @@ static int check_ptr_alignment(struct bpf_verifier_env *env,
58285829
case PTR_TO_XDP_SOCK:
58295830
pointer_desc = "xdp_sock ";
58305831
break;
5832+
case PTR_TO_ARENA:
5833+
return 0;
58315834
default:
58325835
break;
58335836
}
@@ -6937,6 +6940,9 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
69376940

69386941
if (!err && value_regno >= 0 && (rdonly_mem || t == BPF_READ))
69396942
mark_reg_unknown(env, regs, value_regno);
6943+
} else if (reg->type == PTR_TO_ARENA) {
6944+
if (t == BPF_READ && value_regno >= 0)
6945+
mark_reg_unknown(env, regs, value_regno);
69406946
} else {
69416947
verbose(env, "R%d invalid mem access '%s'\n", regno,
69426948
reg_type_str(env, reg->type));
@@ -8408,6 +8414,7 @@ static int check_func_arg_reg_off(struct bpf_verifier_env *env,
84088414
case PTR_TO_MEM | MEM_RINGBUF:
84098415
case PTR_TO_BUF:
84108416
case PTR_TO_BUF | MEM_RDONLY:
8417+
case PTR_TO_ARENA:
84118418
case SCALAR_VALUE:
84128419
return 0;
84138420
/* All the rest must be rejected, except PTR_TO_BTF_ID which allows
@@ -13852,6 +13859,21 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,
1385213859

1385313860
dst_reg = &regs[insn->dst_reg];
1385413861
src_reg = NULL;
13862+
13863+
if (dst_reg->type == PTR_TO_ARENA) {
13864+
struct bpf_insn_aux_data *aux = cur_aux(env);
13865+
13866+
if (BPF_CLASS(insn->code) == BPF_ALU64)
13867+
/*
13868+
* 32-bit operations zero upper bits automatically.
13869+
* 64-bit operations need to be converted to 32.
13870+
*/
13871+
aux->needs_zext = true;
13872+
13873+
/* Any arithmetic operations are allowed on arena pointers */
13874+
return 0;
13875+
}
13876+
1385513877
if (dst_reg->type != SCALAR_VALUE)
1385613878
ptr_reg = dst_reg;
1385713879
else
@@ -13969,19 +13991,20 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
1396913991
} else if (opcode == BPF_MOV) {
1397013992

1397113993
if (BPF_SRC(insn->code) == BPF_X) {
13972-
if (insn->imm != 0) {
13973-
verbose(env, "BPF_MOV uses reserved fields\n");
13974-
return -EINVAL;
13975-
}
13976-
1397713994
if (BPF_CLASS(insn->code) == BPF_ALU) {
13978-
if (insn->off != 0 && insn->off != 8 && insn->off != 16) {
13995+
if ((insn->off != 0 && insn->off != 8 && insn->off != 16) ||
13996+
insn->imm) {
1397913997
verbose(env, "BPF_MOV uses reserved fields\n");
1398013998
return -EINVAL;
1398113999
}
14000+
} else if (insn->off == BPF_ADDR_SPACE_CAST) {
14001+
if (insn->imm != 1 && insn->imm != 1u << 16) {
14002+
verbose(env, "addr_space_cast insn can only convert between address space 1 and 0\n");
14003+
return -EINVAL;
14004+
}
1398214005
} else {
13983-
if (insn->off != 0 && insn->off != 8 && insn->off != 16 &&
13984-
insn->off != 32) {
14006+
if ((insn->off != 0 && insn->off != 8 && insn->off != 16 &&
14007+
insn->off != 32) || insn->imm) {
1398514008
verbose(env, "BPF_MOV uses reserved fields\n");
1398614009
return -EINVAL;
1398714010
}
@@ -14008,7 +14031,12 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
1400814031
struct bpf_reg_state *dst_reg = regs + insn->dst_reg;
1400914032

1401014033
if (BPF_CLASS(insn->code) == BPF_ALU64) {
14011-
if (insn->off == 0) {
14034+
if (insn->imm) {
14035+
/* off == BPF_ADDR_SPACE_CAST */
14036+
mark_reg_unknown(env, regs, insn->dst_reg);
14037+
if (insn->imm == 1) /* cast from as(1) to as(0) */
14038+
dst_reg->type = PTR_TO_ARENA;
14039+
} else if (insn->off == 0) {
1401214040
/* case: R1 = R2
1401314041
* copy register state to dest reg
1401414042
*/
@@ -15182,6 +15210,10 @@ static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)
1518215210

1518315211
if (insn->src_reg == BPF_PSEUDO_MAP_VALUE ||
1518415212
insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE) {
15213+
if (map->map_type == BPF_MAP_TYPE_ARENA) {
15214+
__mark_reg_unknown(env, dst_reg);
15215+
return 0;
15216+
}
1518515217
dst_reg->type = PTR_TO_MAP_VALUE;
1518615218
dst_reg->off = aux->map_off;
1518715219
WARN_ON_ONCE(map->max_entries != 1);
@@ -16568,6 +16600,8 @@ static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
1656816600
* the same stack frame, since fp-8 in foo != fp-8 in bar
1656916601
*/
1657016602
return regs_exact(rold, rcur, idmap) && rold->frameno == rcur->frameno;
16603+
case PTR_TO_ARENA:
16604+
return true;
1657116605
default:
1657216606
return regs_exact(rold, rcur, idmap);
1657316607
}
@@ -17443,6 +17477,7 @@ static bool reg_type_mismatch_ok(enum bpf_reg_type type)
1744317477
case PTR_TO_TCP_SOCK:
1744417478
case PTR_TO_XDP_SOCK:
1744517479
case PTR_TO_BTF_ID:
17480+
case PTR_TO_ARENA:
1744617481
return false;
1744717482
default:
1744817483
return true;
@@ -18296,6 +18331,31 @@ static int resolve_pseudo_ldimm64(struct bpf_verifier_env *env)
1829618331
fdput(f);
1829718332
return -EBUSY;
1829818333
}
18334+
if (map->map_type == BPF_MAP_TYPE_ARENA) {
18335+
if (env->prog->aux->arena) {
18336+
verbose(env, "Only one arena per program\n");
18337+
fdput(f);
18338+
return -EBUSY;
18339+
}
18340+
if (!env->allow_ptr_leaks || !env->bpf_capable) {
18341+
verbose(env, "CAP_BPF and CAP_PERFMON are required to use arena\n");
18342+
fdput(f);
18343+
return -EPERM;
18344+
}
18345+
if (!env->prog->jit_requested) {
18346+
verbose(env, "JIT is required to use arena\n");
18347+
return -EOPNOTSUPP;
18348+
}
18349+
if (!bpf_jit_supports_arena()) {
18350+
verbose(env, "JIT doesn't support arena\n");
18351+
return -EOPNOTSUPP;
18352+
}
18353+
env->prog->aux->arena = (void *)map;
18354+
if (!bpf_arena_get_user_vm_start(env->prog->aux->arena)) {
18355+
verbose(env, "arena's user address must be set via map_extra or mmap()\n");
18356+
return -EINVAL;
18357+
}
18358+
}
1829918359

1830018360
fdput(f);
1830118361
next_insn:
@@ -18917,6 +18977,14 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
1891718977
env->prog->aux->num_exentries++;
1891818978
}
1891918979
continue;
18980+
case PTR_TO_ARENA:
18981+
if (BPF_MODE(insn->code) == BPF_MEMSX) {
18982+
verbose(env, "sign extending loads from arena are not supported yet\n");
18983+
return -EOPNOTSUPP;
18984+
}
18985+
insn->code = BPF_CLASS(insn->code) | BPF_PROBE_MEM32 | BPF_SIZE(insn->code);
18986+
env->prog->aux->num_exentries++;
18987+
continue;
1892018988
default:
1892118989
continue;
1892218990
}
@@ -19102,13 +19170,19 @@ static int jit_subprogs(struct bpf_verifier_env *env)
1910219170
func[i]->aux->nr_linfo = prog->aux->nr_linfo;
1910319171
func[i]->aux->jited_linfo = prog->aux->jited_linfo;
1910419172
func[i]->aux->linfo_idx = env->subprog_info[i].linfo_idx;
19173+
func[i]->aux->arena = prog->aux->arena;
1910519174
num_exentries = 0;
1910619175
insn = func[i]->insnsi;
1910719176
for (j = 0; j < func[i]->len; j++, insn++) {
1910819177
if (BPF_CLASS(insn->code) == BPF_LDX &&
1910919178
(BPF_MODE(insn->code) == BPF_PROBE_MEM ||
19179+
BPF_MODE(insn->code) == BPF_PROBE_MEM32 ||
1911019180
BPF_MODE(insn->code) == BPF_PROBE_MEMSX))
1911119181
num_exentries++;
19182+
if ((BPF_CLASS(insn->code) == BPF_STX ||
19183+
BPF_CLASS(insn->code) == BPF_ST) &&
19184+
BPF_MODE(insn->code) == BPF_PROBE_MEM32)
19185+
num_exentries++;
1911219186
}
1911319187
func[i]->aux->num_exentries = num_exentries;
1911419188
func[i]->aux->tail_call_reachable = env->subprog_info[i].tail_call_reachable;
@@ -19507,6 +19581,21 @@ static int do_misc_fixups(struct bpf_verifier_env *env)
1950719581
}
1950819582

1950919583
for (i = 0; i < insn_cnt;) {
19584+
if (insn->code == (BPF_ALU64 | BPF_MOV | BPF_X) && insn->imm) {
19585+
if ((insn->off == BPF_ADDR_SPACE_CAST && insn->imm == 1) ||
19586+
(((struct bpf_map *)env->prog->aux->arena)->map_flags & BPF_F_NO_USER_CONV)) {
19587+
/* convert to 32-bit mov that clears upper 32-bit */
19588+
insn->code = BPF_ALU | BPF_MOV | BPF_X;
19589+
/* clear off, so it's a normal 'wX = wY' from JIT pov */
19590+
insn->off = 0;
19591+
} /* cast from as(0) to as(1) should be handled by JIT */
19592+
goto next_insn;
19593+
}
19594+
19595+
if (env->insn_aux_data[i + delta].needs_zext)
19596+
/* Convert BPF_CLASS(insn->code) == BPF_ALU64 to 32-bit ALU */
19597+
insn->code = BPF_ALU | BPF_OP(insn->code) | BPF_SRC(insn->code);
19598+
1951019599
/* Make divide-by-zero exceptions impossible. */
1951119600
if (insn->code == (BPF_ALU64 | BPF_MOD | BPF_X) ||
1951219601
insn->code == (BPF_ALU64 | BPF_DIV | BPF_X) ||

0 commit comments

Comments
 (0)