Merge branch 'fix-global-subprog-ptr_to_ctx-arg-handling'
Andrii Nakryiko says: ==================== Fix global subprog PTR_TO_CTX arg handling Fix confusing and incorrect inference of PTR_TO_CTX argument type in BPF global subprogs. For some program types (iters, tracepoint, any program type that doesn't have fixed named "canonical" context type) when user uses (in a correct and valid way) a pointer argument to user-defined anonymous struct type, verifier will incorrectly assume that it has to be PTR_TO_CTX argument. While it should be just a PTR_TO_MEM argument with allowed size calculated from user-provided (even if anonymous) struct. This did come up in practice and was very confusing to users, so let's prevent this going forward. We had to do a slight refactoring of btf_get_prog_ctx_type() to make it easy to support a special s390x KPROBE use cases. See details in respective patches. v1->v2: - special-case typedef bpf_user_pt_regs_t handling for KPROBE programs, fixing s390x after changes in patch #2. ==================== Link: https://lore.kernel.org/r/20240212233221.2575350-1-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
96adbf7125
@ -531,10 +531,9 @@ s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id);
|
||||
int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
|
||||
struct module *owner);
|
||||
struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id);
|
||||
const struct btf_type *
|
||||
btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
const struct btf_type *t, enum bpf_prog_type prog_type,
|
||||
int arg);
|
||||
bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
const struct btf_type *t, enum bpf_prog_type prog_type,
|
||||
int arg);
|
||||
int get_kern_ctx_btf_id(struct bpf_verifier_log *log, enum bpf_prog_type prog_type);
|
||||
bool btf_types_are_same(const struct btf *btf1, u32 id1,
|
||||
const struct btf *btf2, u32 id2);
|
||||
@ -574,12 +573,12 @@ static inline struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline const struct btf_member *
|
||||
btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
const struct btf_type *t, enum bpf_prog_type prog_type,
|
||||
int arg)
|
||||
static inline bool
|
||||
btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
const struct btf_type *t, enum bpf_prog_type prog_type,
|
||||
int arg)
|
||||
{
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
static inline int get_kern_ctx_btf_id(struct bpf_verifier_log *log,
|
||||
enum bpf_prog_type prog_type) {
|
||||
|
@ -5694,15 +5694,29 @@ static int find_kern_ctx_type_id(enum bpf_prog_type prog_type)
|
||||
return ctx_type->type;
|
||||
}
|
||||
|
||||
const struct btf_type *
|
||||
btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
const struct btf_type *t, enum bpf_prog_type prog_type,
|
||||
int arg)
|
||||
bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
const struct btf_type *t, enum bpf_prog_type prog_type,
|
||||
int arg)
|
||||
{
|
||||
const struct btf_type *ctx_type;
|
||||
const char *tname, *ctx_tname;
|
||||
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
|
||||
/* KPROBE programs allow bpf_user_pt_regs_t typedef, which we need to
|
||||
* check before we skip all the typedef below.
|
||||
*/
|
||||
if (prog_type == BPF_PROG_TYPE_KPROBE) {
|
||||
while (btf_type_is_modifier(t) && !btf_type_is_typedef(t))
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
|
||||
if (btf_type_is_typedef(t)) {
|
||||
tname = btf_name_by_offset(btf, t->name_off);
|
||||
if (tname && strcmp(tname, "bpf_user_pt_regs_t") == 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
while (btf_type_is_modifier(t))
|
||||
t = btf_type_by_id(btf, t->type);
|
||||
if (!btf_type_is_struct(t)) {
|
||||
@ -5711,27 +5725,30 @@ btf_get_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf,
|
||||
* is not supported yet.
|
||||
* BPF_PROG_TYPE_RAW_TRACEPOINT is fine.
|
||||
*/
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
tname = btf_name_by_offset(btf, t->name_off);
|
||||
if (!tname) {
|
||||
bpf_log(log, "arg#%d struct doesn't have a name\n", arg);
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
ctx_type = find_canonical_prog_ctx_type(prog_type);
|
||||
if (!ctx_type) {
|
||||
bpf_log(log, "btf_vmlinux is malformed\n");
|
||||
/* should not happen */
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
again:
|
||||
ctx_tname = btf_name_by_offset(btf_vmlinux, ctx_type->name_off);
|
||||
if (!ctx_tname) {
|
||||
/* should not happen */
|
||||
bpf_log(log, "Please fix kernel include/linux/bpf_types.h\n");
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
/* program types without named context types work only with arg:ctx tag */
|
||||
if (ctx_tname[0] == '\0')
|
||||
return false;
|
||||
/* only compare that prog's ctx type name is the same as
|
||||
* kernel expects. No need to compare field by field.
|
||||
* It's ok for bpf prog to do:
|
||||
@ -5740,20 +5757,20 @@ again:
|
||||
* { // no fields of skb are ever used }
|
||||
*/
|
||||
if (strcmp(ctx_tname, "__sk_buff") == 0 && strcmp(tname, "sk_buff") == 0)
|
||||
return ctx_type;
|
||||
return true;
|
||||
if (strcmp(ctx_tname, "xdp_md") == 0 && strcmp(tname, "xdp_buff") == 0)
|
||||
return ctx_type;
|
||||
return true;
|
||||
if (strcmp(ctx_tname, tname)) {
|
||||
/* bpf_user_pt_regs_t is a typedef, so resolve it to
|
||||
* underlying struct and check name again
|
||||
*/
|
||||
if (!btf_type_is_modifier(ctx_type))
|
||||
return NULL;
|
||||
return false;
|
||||
while (btf_type_is_modifier(ctx_type))
|
||||
ctx_type = btf_type_by_id(btf_vmlinux, ctx_type->type);
|
||||
goto again;
|
||||
}
|
||||
return ctx_type;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* forward declarations for arch-specific underlying types of
|
||||
@ -5905,7 +5922,7 @@ static int btf_translate_to_vmlinux(struct bpf_verifier_log *log,
|
||||
enum bpf_prog_type prog_type,
|
||||
int arg)
|
||||
{
|
||||
if (!btf_get_prog_ctx_type(log, btf, t, prog_type, arg))
|
||||
if (!btf_is_prog_ctx_type(log, btf, t, prog_type, arg))
|
||||
return -ENOENT;
|
||||
return find_kern_ctx_type_id(prog_type);
|
||||
}
|
||||
@ -7211,7 +7228,7 @@ int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)
|
||||
if (!btf_type_is_ptr(t))
|
||||
goto skip_pointer;
|
||||
|
||||
if ((tags & ARG_TAG_CTX) || btf_get_prog_ctx_type(log, btf, t, prog_type, i)) {
|
||||
if ((tags & ARG_TAG_CTX) || btf_is_prog_ctx_type(log, btf, t, prog_type, i)) {
|
||||
if (tags & ~ARG_TAG_CTX) {
|
||||
bpf_log(log, "arg#%d has invalid combination of tags\n", i);
|
||||
return -EINVAL;
|
||||
|
@ -11015,7 +11015,7 @@ get_kfunc_ptr_arg_type(struct bpf_verifier_env *env,
|
||||
* type to our caller. When a set of conditions hold in the BTF type of
|
||||
* arguments, we resolve it to a known kfunc_ptr_arg_type.
|
||||
*/
|
||||
if (btf_get_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), argno))
|
||||
if (btf_is_prog_ctx_type(&env->log, meta->btf, t, resolve_prog_type(env->prog), argno))
|
||||
return KF_ARG_PTR_TO_CTX;
|
||||
|
||||
if (is_kfunc_arg_alloc_obj(meta->btf, &args[argno]))
|
||||
|
@ -26,6 +26,23 @@ int kprobe_typedef_ctx(void *ctx)
|
||||
return kprobe_typedef_ctx_subprog(ctx);
|
||||
}
|
||||
|
||||
/* s390x defines:
|
||||
*
|
||||
* typedef user_pt_regs bpf_user_pt_regs_t;
|
||||
* typedef struct { ... } user_pt_regs;
|
||||
*
|
||||
* And so "canonical" underlying struct type is anonymous.
|
||||
* So on s390x only valid ways to have PTR_TO_CTX argument in global subprogs
|
||||
* are:
|
||||
* - bpf_user_pt_regs_t *ctx (typedef);
|
||||
* - struct bpf_user_pt_regs_t *ctx (backwards compatible struct hack);
|
||||
* - void *ctx __arg_ctx (arg:ctx tag)
|
||||
*
|
||||
* Other architectures also allow using underlying struct types (e.g.,
|
||||
* `struct pt_regs *ctx` for x86-64)
|
||||
*/
|
||||
#ifndef bpf_target_s390
|
||||
|
||||
#define pt_regs_struct_t typeof(*(__PT_REGS_CAST((struct pt_regs *)NULL)))
|
||||
|
||||
__weak int kprobe_struct_ctx_subprog(pt_regs_struct_t *ctx)
|
||||
@ -40,6 +57,8 @@ int kprobe_resolved_ctx(void *ctx)
|
||||
return kprobe_struct_ctx_subprog(ctx);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* this is current hack to make this work on old kernels */
|
||||
struct bpf_user_pt_regs_t {};
|
||||
|
||||
|
@ -115,6 +115,35 @@ int arg_tag_nullable_ptr_fail(void *ctx)
|
||||
return subprog_nullable_ptr_bad(&x);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int x;
|
||||
} user_struct_t;
|
||||
|
||||
__noinline __weak int subprog_user_anon_mem(user_struct_t *t)
|
||||
{
|
||||
return t ? t->x : 0;
|
||||
}
|
||||
|
||||
SEC("?tracepoint")
|
||||
__failure __log_level(2)
|
||||
__msg("invalid bpf_context access")
|
||||
__msg("Caller passes invalid args into func#1 ('subprog_user_anon_mem')")
|
||||
int anon_user_mem_invalid(void *ctx)
|
||||
{
|
||||
/* can't pass PTR_TO_CTX as user memory */
|
||||
return subprog_user_anon_mem(ctx);
|
||||
}
|
||||
|
||||
SEC("?tracepoint")
|
||||
__success __log_level(2)
|
||||
__msg("Func#1 ('subprog_user_anon_mem') is safe for any args that match its prototype")
|
||||
int anon_user_mem_valid(void *ctx)
|
||||
{
|
||||
user_struct_t t = { .x = 42 };
|
||||
|
||||
return subprog_user_anon_mem(&t);
|
||||
}
|
||||
|
||||
__noinline __weak int subprog_nonnull_ptr_good(int *p1 __arg_nonnull, int *p2 __arg_nonnull)
|
||||
{
|
||||
return (*p1) * (*p2); /* good, no need for NULL checks */
|
||||
|
Loading…
x
Reference in New Issue
Block a user