Skip to content

Commit 35ac085

Browse files
author
Alexei Starovoitov
committed
Merge branch 'tighten-up-arg-ctx-type-enforcement'
Andrii Nakryiko says: ==================== Tighten up arg:ctx type enforcement Follow up fixes for kernel-side and libbpf-side logic around handling arg:ctx (__arg_ctx) tagged arguments of BPF global subprogs. Patch #1 adds libbpf feature detection of kernel-side __arg_ctx support to avoid unnecessary rewriting BTF types. With stricter kernel-side type enforcement this is now mandatory to avoid problems with using `struct bpf_user_pt_regs_t` instead of actual typedef. For __arg_ctx tagged arguments verifier is now supporting either `bpf_user_pt_regs_t` typedef or resolves it down to the actual struct (pt_regs/user_pt_regs/user_regs_struct), depending on architecture), but for old kernels without __arg_ctx support it's more backwards compatible for libbpf to use `struct bpf_user_pt_regs_t` rewrite which will work on wider range of kernels. So feature detection prevent libbpf accidentally breaking global subprogs on new kernels. We also adjust selftests to do similar feature detection (much simpler, but potentially breaking due to kernel source code refactoring, which is fine for selftests), and skip tests expecting libbpf's BTF type rewrites. Patch #2 is preparatory refactoring for patch #3 which adds type enforcement for arg:ctx tagged global subprog args. See the patch for specifics. Patch #4 adds many new cases to ensure type logic works as expected. Finally, patch #5 adds a relevant subset of kernel-side type checks to __arg_ctx cases that libbpf supports rewrite of. In libbpf's case, type violations are reported as warnings and BTF rewrite is not performed, which will eventually lead to BPF verifier complaining at program verification time. Good care was taken to avoid conflicts between bpf and bpf-next tree (which has few follow up refactorings in the same code area). Once trees converge some of the code will be moved around a bit (and some will be deleted), but with no change to functionality or general shape of the code. v2->v3: - support `bpf_user_pt_regs_t` typedef for KPROBE and PERF_EVENT (CI); v1->v2: - add user_pt_regs and user_regs_struct support for PERF_EVENT (CI); - drop FEAT_ARG_CTX_TAG enum leftover from patch #1; - fix warning about default: without break in the switch (CI). ==================== Link: https://lore.kernel.org/r/20240118033143.3384355-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
2 parents 33772ff + 76ec90a commit 35ac085

File tree

5 files changed

+513
-39
lines changed

5 files changed

+513
-39
lines changed

include/linux/btf.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -512,7 +512,7 @@ s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id);
512512
int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
513513
struct module *owner);
514514
struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id);
515-
const struct btf_member *
515+
const struct btf_type *
516516
btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
517517
const struct btf_type *t, enum bpf_prog_type prog_type,
518518
int arg);

kernel/bpf/btf.c

Lines changed: 205 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5615,21 +5615,46 @@ static u8 bpf_ctx_convert_map[] = {
56155615
#undef BPF_MAP_TYPE
56165616
#undef BPF_LINK_TYPE
56175617

5618-
const struct btf_member *
5619-
btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
5620-
const struct btf_type *t, enum bpf_prog_type prog_type,
5621-
int arg)
5618+
static const struct btf_type *find_canonical_prog_ctx_type(enum bpf_prog_type prog_type)
56225619
{
56235620
const struct btf_type *conv_struct;
5624-
const struct btf_type *ctx_struct;
56255621
const struct btf_member *ctx_type;
5626-
const char *tname, *ctx_tname;
56275622

56285623
conv_struct = bpf_ctx_convert.t;
5629-
if (!conv_struct) {
5630-
bpf_log(log, "btf_vmlinux is malformed\n");
5624+
if (!conv_struct)
56315625
return NULL;
5632-
}
5626+
/* prog_type is valid bpf program type. No need for bounds check. */
5627+
ctx_type = btf_type_member(conv_struct) + bpf_ctx_convert_map[prog_type] * 2;
5628+
/* ctx_type is a pointer to prog_ctx_type in vmlinux.
5629+
* Like 'struct __sk_buff'
5630+
*/
5631+
return btf_type_by_id(btf_vmlinux, ctx_type->type);
5632+
}
5633+
5634+
static int find_kern_ctx_type_id(enum bpf_prog_type prog_type)
5635+
{
5636+
const struct btf_type *conv_struct;
5637+
const struct btf_member *ctx_type;
5638+
5639+
conv_struct = bpf_ctx_convert.t;
5640+
if (!conv_struct)
5641+
return -EFAULT;
5642+
/* prog_type is valid bpf program type. No need for bounds check. */
5643+
ctx_type = btf_type_member(conv_struct) + bpf_ctx_convert_map[prog_type] * 2 + 1;
5644+
/* ctx_type is a pointer to prog_ctx_type in vmlinux.
5645+
* Like 'struct sk_buff'
5646+
*/
5647+
return ctx_type->type;
5648+
}
5649+
5650+
const struct btf_type *
5651+
btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
5652+
const struct btf_type *t, enum bpf_prog_type prog_type,
5653+
int arg)
5654+
{
5655+
const struct btf_type *ctx_type;
5656+
const char *tname, *ctx_tname;
5657+
56335658
t = btf_type_by_id(btf, t->type);
56345659
while (btf_type_is_modifier(t))
56355660
t = btf_type_by_id(btf, t->type);
@@ -5646,17 +5671,15 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
56465671
bpf_log(log, "arg#%d struct doesn't have a name\n", arg);
56475672
return NULL;
56485673
}
5649-
/* prog_type is valid bpf program type. No need for bounds check. */
5650-
ctx_type = btf_type_member(conv_struct) + bpf_ctx_convert_map[prog_type] * 2;
5651-
/* ctx_struct is a pointer to prog_ctx_type in vmlinux.
5652-
* Like 'struct __sk_buff'
5653-
*/
5654-
ctx_struct = btf_type_by_id(btf_vmlinux, ctx_type->type);
5655-
if (!ctx_struct)
5674+
5675+
ctx_type = find_canonical_prog_ctx_type(prog_type);
5676+
if (!ctx_type) {
5677+
bpf_log(log, "btf_vmlinux is malformed\n");
56565678
/* should not happen */
56575679
return NULL;
5680+
}
56585681
again:
5659-
ctx_tname = btf_name_by_offset(btf_vmlinux, ctx_struct->name_off);
5682+
ctx_tname = btf_name_by_offset(btf_vmlinux, ctx_type->name_off);
56605683
if (!ctx_tname) {
56615684
/* should not happen */
56625685
bpf_log(log, "Please fix kernel include/linux/bpf_types.h\n");
@@ -5677,28 +5700,167 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
56775700
/* bpf_user_pt_regs_t is a typedef, so resolve it to
56785701
* underlying struct and check name again
56795702
*/
5680-
if (!btf_type_is_modifier(ctx_struct))
5703+
if (!btf_type_is_modifier(ctx_type))
56815704
return NULL;
5682-
while (btf_type_is_modifier(ctx_struct))
5683-
ctx_struct = btf_type_by_id(btf_vmlinux, ctx_struct->type);
5705+
while (btf_type_is_modifier(ctx_type))
5706+
ctx_type = btf_type_by_id(btf_vmlinux, ctx_type->type);
56845707
goto again;
56855708
}
56865709
return ctx_type;
56875710
}
56885711

5712+
/* forward declarations for arch-specific underlying types of
5713+
* bpf_user_pt_regs_t; this avoids the need for arch-specific #ifdef
5714+
* compilation guards below for BPF_PROG_TYPE_PERF_EVENT checks, but still
5715+
* works correctly with __builtin_types_compatible_p() on respective
5716+
* architectures
5717+
*/
5718+
struct user_regs_struct;
5719+
struct user_pt_regs;
5720+
5721+
static int btf_validate_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
5722+
const struct btf_type *t, int arg,
5723+
enum bpf_prog_type prog_type,
5724+
enum bpf_attach_type attach_type)
5725+
{
5726+
const struct btf_type *ctx_type;
5727+
const char *tname, *ctx_tname;
5728+
5729+
if (!btf_is_ptr(t)) {
5730+
bpf_log(log, "arg#%d type isn't a pointer\n", arg);
5731+
return -EINVAL;
5732+
}
5733+
t = btf_type_by_id(btf, t->type);
5734+
5735+
/* KPROBE and PERF_EVENT programs allow bpf_user_pt_regs_t typedef */
5736+
if (prog_type == BPF_PROG_TYPE_KPROBE || prog_type == BPF_PROG_TYPE_PERF_EVENT) {
5737+
while (btf_type_is_modifier(t) && !btf_type_is_typedef(t))
5738+
t = btf_type_by_id(btf, t->type);
5739+
5740+
if (btf_type_is_typedef(t)) {
5741+
tname = btf_name_by_offset(btf, t->name_off);
5742+
if (tname && strcmp(tname, "bpf_user_pt_regs_t") == 0)
5743+
return 0;
5744+
}
5745+
}
5746+
5747+
/* all other program types don't use typedefs for context type */
5748+
while (btf_type_is_modifier(t))
5749+
t = btf_type_by_id(btf, t->type);
5750+
5751+
/* `void *ctx __arg_ctx` is always valid */
5752+
if (btf_type_is_void(t))
5753+
return 0;
5754+
5755+
tname = btf_name_by_offset(btf, t->name_off);
5756+
if (str_is_empty(tname)) {
5757+
bpf_log(log, "arg#%d type doesn't have a name\n", arg);
5758+
return -EINVAL;
5759+
}
5760+
5761+
/* special cases */
5762+
switch (prog_type) {
5763+
case BPF_PROG_TYPE_KPROBE:
5764+
if (__btf_type_is_struct(t) && strcmp(tname, "pt_regs") == 0)
5765+
return 0;
5766+
break;
5767+
case BPF_PROG_TYPE_PERF_EVENT:
5768+
if (__builtin_types_compatible_p(bpf_user_pt_regs_t, struct pt_regs) &&
5769+
__btf_type_is_struct(t) && strcmp(tname, "pt_regs") == 0)
5770+
return 0;
5771+
if (__builtin_types_compatible_p(bpf_user_pt_regs_t, struct user_pt_regs) &&
5772+
__btf_type_is_struct(t) && strcmp(tname, "user_pt_regs") == 0)
5773+
return 0;
5774+
if (__builtin_types_compatible_p(bpf_user_pt_regs_t, struct user_regs_struct) &&
5775+
__btf_type_is_struct(t) && strcmp(tname, "user_regs_struct") == 0)
5776+
return 0;
5777+
break;
5778+
case BPF_PROG_TYPE_RAW_TRACEPOINT:
5779+
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
5780+
/* allow u64* as ctx */
5781+
if (btf_is_int(t) && t->size == 8)
5782+
return 0;
5783+
break;
5784+
case BPF_PROG_TYPE_TRACING:
5785+
switch (attach_type) {
5786+
case BPF_TRACE_RAW_TP:
5787+
/* tp_btf program is TRACING, so need special case here */
5788+
if (__btf_type_is_struct(t) &&
5789+
strcmp(tname, "bpf_raw_tracepoint_args") == 0)
5790+
return 0;
5791+
/* allow u64* as ctx */
5792+
if (btf_is_int(t) && t->size == 8)
5793+
return 0;
5794+
break;
5795+
case BPF_TRACE_ITER:
5796+
/* allow struct bpf_iter__xxx types only */
5797+
if (__btf_type_is_struct(t) &&
5798+
strncmp(tname, "bpf_iter__", sizeof("bpf_iter__") - 1) == 0)
5799+
return 0;
5800+
break;
5801+
case BPF_TRACE_FENTRY:
5802+
case BPF_TRACE_FEXIT:
5803+
case BPF_MODIFY_RETURN:
5804+
/* allow u64* as ctx */
5805+
if (btf_is_int(t) && t->size == 8)
5806+
return 0;
5807+
break;
5808+
default:
5809+
break;
5810+
}
5811+
break;
5812+
case BPF_PROG_TYPE_LSM:
5813+
case BPF_PROG_TYPE_STRUCT_OPS:
5814+
/* allow u64* as ctx */
5815+
if (btf_is_int(t) && t->size == 8)
5816+
return 0;
5817+
break;
5818+
case BPF_PROG_TYPE_TRACEPOINT:
5819+
case BPF_PROG_TYPE_SYSCALL:
5820+
case BPF_PROG_TYPE_EXT:
5821+
return 0; /* anything goes */
5822+
default:
5823+
break;
5824+
}
5825+
5826+
ctx_type = find_canonical_prog_ctx_type(prog_type);
5827+
if (!ctx_type) {
5828+
/* should not happen */
5829+
bpf_log(log, "btf_vmlinux is malformed\n");
5830+
return -EINVAL;
5831+
}
5832+
5833+
/* resolve typedefs and check that underlying structs are matching as well */
5834+
while (btf_type_is_modifier(ctx_type))
5835+
ctx_type = btf_type_by_id(btf_vmlinux, ctx_type->type);
5836+
5837+
/* if program type doesn't have distinctly named struct type for
5838+
* context, then __arg_ctx argument can only be `void *`, which we
5839+
* already checked above
5840+
*/
5841+
if (!__btf_type_is_struct(ctx_type)) {
5842+
bpf_log(log, "arg#%d should be void pointer\n", arg);
5843+
return -EINVAL;
5844+
}
5845+
5846+
ctx_tname = btf_name_by_offset(btf_vmlinux, ctx_type->name_off);
5847+
if (!__btf_type_is_struct(t) || strcmp(ctx_tname, tname) != 0) {
5848+
bpf_log(log, "arg#%d should be `struct %s *`\n", arg, ctx_tname);
5849+
return -EINVAL;
5850+
}
5851+
5852+
return 0;
5853+
}
5854+
56895855
static int btf_translate_to_vmlinux(struct bpf_verifier_log *log,
56905856
struct btf *btf,
56915857
const struct btf_type *t,
56925858
enum bpf_prog_type prog_type,
56935859
int arg)
56945860
{
5695-
const struct btf_member *prog_ctx_type, *kern_ctx_type;
5696-
5697-
prog_ctx_type = btf_get_prog_ctx_type(log, btf, t, prog_type, arg);
5698-
if (!prog_ctx_type)
5861+
if (!btf_get_prog_ctx_type(log, btf, t, prog_type, arg))
56995862
return -ENOENT;
5700-
kern_ctx_type = prog_ctx_type + 1;
5701-
return kern_ctx_type->type;
5863+
return find_kern_ctx_type_id(prog_type);
57025864
}
57035865

57045866
int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_type)
@@ -6934,6 +7096,23 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
69347096
return -EINVAL;
69357097
}
69367098

7099+
for (i = 0; i < nargs; i++) {
7100+
const char *tag;
7101+
7102+
if (sub->args[i].arg_type != ARG_PTR_TO_CTX)
7103+
continue;
7104+
7105+
/* check if arg has "arg:ctx" tag */
7106+
t = btf_type_by_id(btf, args[i].type);
7107+
tag = btf_find_decl_tag_value(btf, fn_t, i, "arg:");
7108+
if (IS_ERR_OR_NULL(tag) || strcmp(tag, "ctx") != 0)
7109+
continue;
7110+
7111+
if (btf_validate_prog_ctx_type(log, btf, t, i, prog_type,
7112+
prog->expected_attach_type))
7113+
return -EINVAL;
7114+
}
7115+
69377116
sub->arg_cnt = nargs;
69387117
sub->args_cached = true;
69397118

0 commit comments

Comments
 (0)