Skip to content

Commit a64bfe6

Browse files
anakryikoAlexei Starovoitov
authored andcommitted
bpf: add support for passing dynptr pointer to global subprog
Add ability to pass a pointer to dynptr into global functions. This allows to have global subprogs that accept and work with generic dynptrs that are created by caller. Dynptr argument is detected based on the name of a struct type, if it's "bpf_dynptr", it's assumed to be a proper dynptr pointer. Both actual struct and forward struct declaration types are supported. This is conceptually exactly the same semantics as bpf_user_ringbuf_drain()'s use of dynptr to pass a variable-sized pointer to ringbuf record. So we heavily rely on CONST_PTR_TO_DYNPTR bits of already existing logic in the verifier. During global subprog validation, we mark such CONST_PTR_TO_DYNPTR as having LOCAL type, as that's the most unassuming type of dynptr and it doesn't have any special helpers that can try to free or acquire extra references (unlike skb, xdp, or ringbuf dynptr). So that seems like a safe "choice" to make from correctness standpoint. It's still possible to pass any type of dynptr to such subprog, though, because generic dynptr helpers, like getting data/slice pointers, read/write memory copying routines, dynptr adjustment and getter routines all work correctly with any type of dynptr. 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 94e1c70 commit a64bfe6

File tree

2 files changed

+30
-0
lines changed

2 files changed

+30
-0
lines changed

kernel/bpf/btf.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6765,6 +6765,25 @@ int btf_check_type_match(struct bpf_verifier_log *log, const struct bpf_prog *pr
67656765
return btf_check_func_type_match(log, btf1, t1, btf2, t2);
67666766
}
67676767

6768+
static bool btf_is_dynptr_ptr(const struct btf *btf, const struct btf_type *t)
6769+
{
6770+
const char *name;
6771+
6772+
t = btf_type_by_id(btf, t->type); /* skip PTR */
6773+
6774+
while (btf_type_is_modifier(t))
6775+
t = btf_type_by_id(btf, t->type);
6776+
6777+
/* allow either struct or struct forward declaration */
6778+
if (btf_type_is_struct(t) ||
6779+
(btf_type_is_fwd(t) && btf_type_kflag(t) == 0)) {
6780+
name = btf_str_by_offset(btf, t->name_off);
6781+
return name && strcmp(name, "bpf_dynptr") == 0;
6782+
}
6783+
6784+
return false;
6785+
}
6786+
67686787
/* Process BTF of a function to produce high-level expectation of function
67696788
* arguments (like ARG_PTR_TO_CTX, or ARG_PTR_TO_MEM, etc). This information
67706789
* is cached in subprog info for reuse.
@@ -6885,6 +6904,10 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
68856904
sub->args[i].arg_type = ARG_PTR_TO_CTX;
68866905
continue;
68876906
}
6907+
if (btf_type_is_ptr(t) && btf_is_dynptr_ptr(btf, t)) {
6908+
sub->args[i].arg_type = ARG_PTR_TO_DYNPTR | MEM_RDONLY;
6909+
continue;
6910+
}
68886911
if (is_global && btf_type_is_ptr(t)) {
68896912
u32 mem_size;
68906913

kernel/bpf/verifier.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9296,6 +9296,10 @@ static int btf_check_func_arg_match(struct bpf_verifier_env *env, int subprog,
92969296
bpf_log(log, "arg#%d is expected to be non-NULL\n", i);
92979297
return -EINVAL;
92989298
}
9299+
} else if (arg->arg_type == (ARG_PTR_TO_DYNPTR | MEM_RDONLY)) {
9300+
ret = process_dynptr_func(env, regno, -1, arg->arg_type, 0);
9301+
if (ret)
9302+
return ret;
92999303
} else {
93009304
bpf_log(log, "verifier bug: unrecognized arg#%d type %d\n",
93019305
i, arg->arg_type);
@@ -20052,6 +20056,9 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
2005220056
} else if (arg->arg_type == ARG_ANYTHING) {
2005320057
reg->type = SCALAR_VALUE;
2005420058
mark_reg_unknown(env, regs, i);
20059+
} else if (arg->arg_type == (ARG_PTR_TO_DYNPTR | MEM_RDONLY)) {
20060+
/* assume unspecial LOCAL dynptr type */
20061+
__mark_dynptr_reg(reg, BPF_DYNPTR_TYPE_LOCAL, true, ++env->id_gen);
2005520062
} else if (base_type(arg->arg_type) == ARG_PTR_TO_MEM) {
2005620063
reg->type = PTR_TO_MEM;
2005720064
if (arg->arg_type & PTR_MAYBE_NULL)

0 commit comments

Comments
 (0)