Skip to content

Commit 4ba1d0f

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
bpf: abstract away global subprog arg preparation logic from reg state setup
btf_prepare_func_args() is used to understand expectations and restrictions on global subprog arguments. But current implementation is hard to extend, as it intermixes BTF-based func prototype parsing and interpretation logic with setting up register state at subprog entry. Worse still, those registers are not completely set up inside btf_prepare_func_args(), requiring some more logic later in do_check_common(). Like calling mark_reg_unknown() and similar initialization operations. This intermixing of BTF interpretation and register state setup is problematic. First, it causes duplication of BTF parsing logic for global subprog verification (to set up initial state of global subprog) and global subprog call sites analysis (when we need to check that whatever is being passed into global subprog matches expectations), performed in btf_check_subprog_call(). Given we want to extend global func argument with tags later, this duplication is problematic. So refactor btf_prepare_func_args() to do only BTF-based func proto and args parsing, returning high-level argument "expectations" only, with no regard to specifics of register state. I.e., if it's a context argument, instead of setting register state to PTR_TO_CTX, we return ARG_PTR_TO_CTX enum for that argument as "an argument specification" for further processing inside do_check_common(). Similarly for SCALAR arguments, PTR_TO_MEM, etc. This allows to reuse btf_prepare_func_args() in following patches at global subprog call site analysis time. It also keeps register setup code consistently in one place, do_check_common(). Besides all this, we cache this argument specs information inside env->subprog_info, eliminating the need to redo these potentially expensive BTF traversals, especially if BPF program's BTF is big and/or there are lots of global subprog calls. Acked-by: Eduard Zingerman <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
1 parent c337f23 commit 4ba1d0f

File tree

4 files changed

+64
-36
lines changed

4 files changed

+64
-36
lines changed

include/linux/bpf.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2470,8 +2470,7 @@ int btf_check_subprog_arg_match(struct bpf_verifier_env *env, int subprog,
24702470
struct bpf_reg_state *regs);
24712471
int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,
24722472
struct bpf_reg_state *regs);
2473-
int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
2474-
struct bpf_reg_state *reg, u32 *nargs);
2473+
int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog);
24752474
int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *prog,
24762475
struct btf *btf, const struct btf_type *t);
24772476
const char *btf_find_decl_tag_value(const struct btf *btf, const struct btf_type *pt,

include/linux/bpf_verifier.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,13 @@ static inline bool bpf_verifier_log_needed(const struct bpf_verifier_log *log)
606606

607607
#define BPF_MAX_SUBPROGS 256
608608

609+
struct bpf_subprog_arg_info {
610+
enum bpf_arg_type arg_type;
611+
union {
612+
u32 mem_size;
613+
};
614+
};
615+
609616
struct bpf_subprog_info {
610617
/* 'start' has to be the first field otherwise find_subprog() won't work */
611618
u32 start; /* insn idx of function entry point */
@@ -617,6 +624,10 @@ struct bpf_subprog_info {
617624
bool is_cb: 1;
618625
bool is_async_cb: 1;
619626
bool is_exception_cb: 1;
627+
bool args_cached: 1;
628+
629+
u8 arg_cnt;
630+
struct bpf_subprog_arg_info args[MAX_BPF_FUNC_REG_ARGS];
620631
};
621632

622633
struct bpf_verifier_env;
@@ -727,6 +738,11 @@ struct bpf_verifier_env {
727738
char tmp_str_buf[TMP_STR_BUF_LEN];
728739
};
729740

741+
static inline struct bpf_subprog_info *subprog_info(struct bpf_verifier_env *env, int subprog)
742+
{
743+
return &env->subprog_info[subprog];
744+
}
745+
730746
__printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log,
731747
const char *fmt, va_list args);
732748
__printf(2, 3) void bpf_verifier_log_write(struct bpf_verifier_env *env,

kernel/bpf/btf.c

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6948,16 +6948,17 @@ int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,
69486948
return err;
69496949
}
69506950

6951-
/* Convert BTF of a function into bpf_reg_state if possible
6951+
/* Process BTF of a function to produce high-level expectation of function
6952+
* arguments (like ARG_PTR_TO_CTX, or ARG_PTR_TO_MEM, etc). This information
6953+
* is cached in subprog info for reuse.
69526954
* Returns:
69536955
* EFAULT - there is a verifier bug. Abort verification.
69546956
* EINVAL - cannot convert BTF.
6955-
* 0 - Successfully converted BTF into bpf_reg_state
6956-
* (either PTR_TO_CTX or SCALAR_VALUE).
6957+
* 0 - Successfully processed BTF and constructed argument expectations.
69576958
*/
6958-
int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
6959-
struct bpf_reg_state *regs, u32 *arg_cnt)
6959+
int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
69606960
{
6961+
struct bpf_subprog_info *sub = subprog_info(env, subprog);
69616962
struct bpf_verifier_log *log = &env->log;
69626963
struct bpf_prog *prog = env->prog;
69636964
enum bpf_prog_type prog_type = prog->type;
@@ -6967,6 +6968,9 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
69676968
u32 i, nargs, btf_id;
69686969
const char *tname;
69696970

6971+
if (sub->args_cached)
6972+
return 0;
6973+
69706974
if (!prog->aux->func_info ||
69716975
prog->aux->func_info_aux[subprog].linkage != BTF_FUNC_GLOBAL) {
69726976
bpf_log(log, "Verifier bug\n");
@@ -6990,10 +6994,6 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
69906994
}
69916995
tname = btf_name_by_offset(btf, t->name_off);
69926996

6993-
if (log->level & BPF_LOG_LEVEL)
6994-
bpf_log(log, "Validating %s() func#%d...\n",
6995-
tname, subprog);
6996-
69976997
if (prog->aux->func_info_aux[subprog].unreliable) {
69986998
bpf_log(log, "Verifier bug in function %s()\n", tname);
69996999
return -EFAULT;
@@ -7013,7 +7013,6 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
70137013
tname, nargs, MAX_BPF_FUNC_REG_ARGS);
70147014
return -EINVAL;
70157015
}
7016-
*arg_cnt = nargs;
70177016
/* check that function returns int, exception cb also requires this */
70187017
t = btf_type_by_id(btf, t->type);
70197018
while (btf_type_is_modifier(t))
@@ -7028,24 +7027,24 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
70287027
* Only PTR_TO_CTX and SCALAR are supported atm.
70297028
*/
70307029
for (i = 0; i < nargs; i++) {
7031-
struct bpf_reg_state *reg = &regs[i + 1];
7032-
70337030
t = btf_type_by_id(btf, args[i].type);
70347031
while (btf_type_is_modifier(t))
70357032
t = btf_type_by_id(btf, t->type);
70367033
if (btf_type_is_int(t) || btf_is_any_enum(t)) {
7037-
reg->type = SCALAR_VALUE;
7034+
sub->args[i].arg_type = ARG_ANYTHING;
70387035
continue;
70397036
}
70407037
if (btf_type_is_ptr(t)) {
7038+
u32 mem_size;
7039+
70417040
if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
7042-
reg->type = PTR_TO_CTX;
7041+
sub->args[i].arg_type = ARG_PTR_TO_CTX;
70437042
continue;
70447043
}
70457044

70467045
t = btf_type_skip_modifiers(btf, t->type, NULL);
70477046

7048-
ref_t = btf_resolve_size(btf, t, &reg->mem_size);
7047+
ref_t = btf_resolve_size(btf, t, &mem_size);
70497048
if (IS_ERR(ref_t)) {
70507049
bpf_log(log,
70517050
"arg#%d reference type('%s %s') size cannot be determined: %ld\n",
@@ -7054,15 +7053,18 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog,
70547053
return -EINVAL;
70557054
}
70567055

7057-
reg->type = PTR_TO_MEM | PTR_MAYBE_NULL;
7058-
reg->id = ++env->id_gen;
7059-
7056+
sub->args[i].arg_type = ARG_PTR_TO_MEM_OR_NULL;
7057+
sub->args[i].mem_size = mem_size;
70607058
continue;
70617059
}
70627060
bpf_log(log, "Arg#%d type %s in %s() is not supported yet.\n",
70637061
i, btf_type_str(t), tname);
70647062
return -EINVAL;
70657063
}
7064+
7065+
sub->arg_cnt = nargs;
7066+
sub->args_cached = true;
7067+
70667068
return 0;
70677069
}
70687070

kernel/bpf/verifier.c

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -442,11 +442,6 @@ static struct bpf_func_info_aux *subprog_aux(const struct bpf_verifier_env *env,
442442
return &env->prog->aux->func_info_aux[subprog];
443443
}
444444

445-
static struct bpf_subprog_info *subprog_info(struct bpf_verifier_env *env, int subprog)
446-
{
447-
return &env->subprog_info[subprog];
448-
}
449-
450445
static void mark_subprog_exc_cb(struct bpf_verifier_env *env, int subprog)
451446
{
452447
struct bpf_subprog_info *info = subprog_info(env, subprog);
@@ -19937,34 +19932,50 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
1993719932

1993819933
regs = state->frame[state->curframe]->regs;
1993919934
if (subprog || env->prog->type == BPF_PROG_TYPE_EXT) {
19940-
u32 nargs;
19935+
struct bpf_subprog_info *sub = subprog_info(env, subprog);
19936+
const char *sub_name = subprog_name(env, subprog);
19937+
struct bpf_subprog_arg_info *arg;
19938+
struct bpf_reg_state *reg;
1994119939

19942-
ret = btf_prepare_func_args(env, subprog, regs, &nargs);
19940+
verbose(env, "Validating %s() func#%d...\n", sub_name, subprog);
19941+
ret = btf_prepare_func_args(env, subprog);
1994319942
if (ret)
1994419943
goto out;
19944+
1994519945
if (subprog_is_exc_cb(env, subprog)) {
1994619946
state->frame[0]->in_exception_callback_fn = true;
1994719947
/* We have already ensured that the callback returns an integer, just
1994819948
* like all global subprogs. We need to determine it only has a single
1994919949
* scalar argument.
1995019950
*/
19951-
if (nargs != 1 || regs[BPF_REG_1].type != SCALAR_VALUE) {
19951+
if (sub->arg_cnt != 1 || sub->args[0].arg_type != ARG_ANYTHING) {
1995219952
verbose(env, "exception cb only supports single integer argument\n");
1995319953
ret = -EINVAL;
1995419954
goto out;
1995519955
}
1995619956
}
19957-
for (i = BPF_REG_1; i <= BPF_REG_5; i++) {
19958-
if (regs[i].type == PTR_TO_CTX)
19957+
for (i = BPF_REG_1; i <= sub->arg_cnt; i++) {
19958+
arg = &sub->args[i - BPF_REG_1];
19959+
reg = &regs[i];
19960+
19961+
if (arg->arg_type == ARG_PTR_TO_CTX) {
19962+
reg->type = PTR_TO_CTX;
1995919963
mark_reg_known_zero(env, regs, i);
19960-
else if (regs[i].type == SCALAR_VALUE)
19964+
} else if (arg->arg_type == ARG_ANYTHING) {
19965+
reg->type = SCALAR_VALUE;
1996119966
mark_reg_unknown(env, regs, i);
19962-
else if (base_type(regs[i].type) == PTR_TO_MEM) {
19963-
const u32 mem_size = regs[i].mem_size;
19964-
19967+
} else if (base_type(arg->arg_type) == ARG_PTR_TO_MEM) {
19968+
reg->type = PTR_TO_MEM;
19969+
if (arg->arg_type & PTR_MAYBE_NULL)
19970+
reg->type |= PTR_MAYBE_NULL;
1996519971
mark_reg_known_zero(env, regs, i);
19966-
regs[i].mem_size = mem_size;
19967-
regs[i].id = ++env->id_gen;
19972+
reg->mem_size = arg->mem_size;
19973+
reg->id = ++env->id_gen;
19974+
} else {
19975+
WARN_ONCE(1, "BUG: unhandled arg#%d type %d\n",
19976+
i - BPF_REG_1, arg->arg_type);
19977+
ret = -EFAULT;
19978+
goto out;
1996819979
}
1996919980
}
1997019981
} else {

0 commit comments

Comments
 (0)