Skip to content

Commit 9e15db6

Browse files
Alexei Starovoitovborkmann
authored andcommitted
bpf: Implement accurate raw_tp context access via BTF
libbpf analyzes bpf C program, searches in-kernel BTF for given type name and stores it into expected_attach_type. The kernel verifier expects this btf_id to point to something like: typedef void (*btf_trace_kfree_skb)(void *, struct sk_buff *skb, void *loc); which represents signature of raw_tracepoint "kfree_skb". Then btf_ctx_access() matches ctx+0 access in bpf program with 'skb' and 'ctx+8' access with 'loc' arguments of "kfree_skb" tracepoint. In first case it passes btf_id of 'struct sk_buff *' back to the verifier core and 'void *' in second case. Then the verifier tracks PTR_TO_BTF_ID as any other pointer type. Like PTR_TO_SOCKET points to 'struct bpf_sock', PTR_TO_TCP_SOCK points to 'struct bpf_tcp_sock', and so on. PTR_TO_BTF_ID points to in-kernel structs. If 1234 is btf_id of 'struct sk_buff' in vmlinux's BTF then PTR_TO_BTF_ID#1234 points to one of in kernel skbs. When PTR_TO_BTF_ID#1234 is dereferenced (like r2 = *(u64 *)r1 + 32) the btf_struct_access() checks which field of 'struct sk_buff' is at offset 32. Checks that size of access matches type definition of the field and continues to track the dereferenced type. If that field was a pointer to 'struct net_device' the r2's type will be PTR_TO_BTF_ID#456. Where 456 is btf_id of 'struct net_device' in vmlinux's BTF. Such verifier analysis prevents "cheating" in BPF C program. The program cannot cast arbitrary pointer to 'struct sk_buff *' and access it. C compiler would allow type cast, of course, but the verifier will notice type mismatch based on BPF assembly and in-kernel BTF. Signed-off-by: Alexei Starovoitov <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Andrii Nakryiko <[email protected]> Acked-by: Martin KaFai Lau <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent f75a697 commit 9e15db6

File tree

5 files changed

+296
-5
lines changed

5 files changed

+296
-5
lines changed

include/linux/bpf.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <linux/u64_stats_sync.h>
1717

1818
struct bpf_verifier_env;
19+
struct bpf_verifier_log;
1920
struct perf_event;
2021
struct bpf_prog;
2122
struct bpf_map;
@@ -281,14 +282,19 @@ enum bpf_reg_type {
281282
PTR_TO_TCP_SOCK_OR_NULL, /* reg points to struct tcp_sock or NULL */
282283
PTR_TO_TP_BUFFER, /* reg points to a writable raw tp's buffer */
283284
PTR_TO_XDP_SOCK, /* reg points to struct xdp_sock */
285+
PTR_TO_BTF_ID, /* reg points to kernel struct */
284286
};
285287

286288
/* The information passed from prog-specific *_is_valid_access
287289
* back to the verifier.
288290
*/
289291
struct bpf_insn_access_aux {
290292
enum bpf_reg_type reg_type;
291-
int ctx_field_size;
293+
union {
294+
int ctx_field_size;
295+
u32 btf_id;
296+
};
297+
struct bpf_verifier_log *log; /* for verbose logs */
292298
};
293299

294300
static inline void
@@ -483,6 +489,7 @@ struct bpf_event_entry {
483489

484490
bool bpf_prog_array_compatible(struct bpf_array *array, const struct bpf_prog *fp);
485491
int bpf_prog_calc_tag(struct bpf_prog *fp);
492+
const char *kernel_type_name(u32 btf_type_id);
486493

487494
const struct bpf_func_proto *bpf_get_trace_printk_proto(void);
488495

@@ -748,6 +755,14 @@ int bpf_prog_test_run_skb(struct bpf_prog *prog, const union bpf_attr *kattr,
748755
int bpf_prog_test_run_flow_dissector(struct bpf_prog *prog,
749756
const union bpf_attr *kattr,
750757
union bpf_attr __user *uattr);
758+
bool btf_ctx_access(int off, int size, enum bpf_access_type type,
759+
const struct bpf_prog *prog,
760+
struct bpf_insn_access_aux *info);
761+
int btf_struct_access(struct bpf_verifier_log *log,
762+
const struct btf_type *t, int off, int size,
763+
enum bpf_access_type atype,
764+
u32 *next_btf_id);
765+
751766
#else /* !CONFIG_BPF_SYSCALL */
752767
static inline struct bpf_prog *bpf_prog_get(u32 ufd)
753768
{

include/linux/bpf_verifier.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ struct bpf_reg_state {
5252
*/
5353
struct bpf_map *map_ptr;
5454

55+
u32 btf_id; /* for PTR_TO_BTF_ID */
56+
5557
/* Max size from any of the above. */
5658
unsigned long raw;
5759
};
@@ -399,6 +401,8 @@ __printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log,
399401
const char *fmt, va_list args);
400402
__printf(2, 3) void bpf_verifier_log_write(struct bpf_verifier_env *env,
401403
const char *fmt, ...);
404+
__printf(2, 3) void bpf_log(struct bpf_verifier_log *log,
405+
const char *fmt, ...);
402406

403407
static inline struct bpf_func_state *cur_func(struct bpf_verifier_env *env)
404408
{

kernel/bpf/btf.c

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3436,6 +3436,196 @@ struct btf *btf_parse_vmlinux(void)
34363436
return ERR_PTR(err);
34373437
}
34383438

3439+
extern struct btf *btf_vmlinux;
3440+
3441+
bool btf_ctx_access(int off, int size, enum bpf_access_type type,
3442+
const struct bpf_prog *prog,
3443+
struct bpf_insn_access_aux *info)
3444+
{
3445+
struct bpf_verifier_log *log = info->log;
3446+
u32 btf_id = prog->aux->attach_btf_id;
3447+
const struct btf_param *args;
3448+
const struct btf_type *t;
3449+
const char prefix[] = "btf_trace_";
3450+
const char *tname;
3451+
u32 nr_args, arg;
3452+
3453+
if (!btf_id)
3454+
return true;
3455+
3456+
if (IS_ERR(btf_vmlinux)) {
3457+
bpf_log(log, "btf_vmlinux is malformed\n");
3458+
return false;
3459+
}
3460+
3461+
t = btf_type_by_id(btf_vmlinux, btf_id);
3462+
if (!t || BTF_INFO_KIND(t->info) != BTF_KIND_TYPEDEF) {
3463+
bpf_log(log, "btf_id is invalid\n");
3464+
return false;
3465+
}
3466+
3467+
tname = __btf_name_by_offset(btf_vmlinux, t->name_off);
3468+
if (strncmp(prefix, tname, sizeof(prefix) - 1)) {
3469+
bpf_log(log, "btf_id points to wrong type name %s\n", tname);
3470+
return false;
3471+
}
3472+
tname += sizeof(prefix) - 1;
3473+
3474+
t = btf_type_by_id(btf_vmlinux, t->type);
3475+
if (!btf_type_is_ptr(t))
3476+
return false;
3477+
t = btf_type_by_id(btf_vmlinux, t->type);
3478+
if (!btf_type_is_func_proto(t))
3479+
return false;
3480+
3481+
if (off % 8) {
3482+
bpf_log(log, "raw_tp '%s' offset %d is not multiple of 8\n",
3483+
tname, off);
3484+
return false;
3485+
}
3486+
arg = off / 8;
3487+
args = (const struct btf_param *)(t + 1);
3488+
/* skip first 'void *__data' argument in btf_trace_##name typedef */
3489+
args++;
3490+
nr_args = btf_type_vlen(t) - 1;
3491+
if (arg >= nr_args) {
3492+
bpf_log(log, "raw_tp '%s' doesn't have %d-th argument\n",
3493+
tname, arg);
3494+
return false;
3495+
}
3496+
3497+
t = btf_type_by_id(btf_vmlinux, args[arg].type);
3498+
/* skip modifiers */
3499+
while (btf_type_is_modifier(t))
3500+
t = btf_type_by_id(btf_vmlinux, t->type);
3501+
if (btf_type_is_int(t))
3502+
/* accessing a scalar */
3503+
return true;
3504+
if (!btf_type_is_ptr(t)) {
3505+
bpf_log(log,
3506+
"raw_tp '%s' arg%d '%s' has type %s. Only pointer access is allowed\n",
3507+
tname, arg,
3508+
__btf_name_by_offset(btf_vmlinux, t->name_off),
3509+
btf_kind_str[BTF_INFO_KIND(t->info)]);
3510+
return false;
3511+
}
3512+
if (t->type == 0)
3513+
/* This is a pointer to void.
3514+
* It is the same as scalar from the verifier safety pov.
3515+
* No further pointer walking is allowed.
3516+
*/
3517+
return true;
3518+
3519+
/* this is a pointer to another type */
3520+
info->reg_type = PTR_TO_BTF_ID;
3521+
info->btf_id = t->type;
3522+
3523+
t = btf_type_by_id(btf_vmlinux, t->type);
3524+
/* skip modifiers */
3525+
while (btf_type_is_modifier(t))
3526+
t = btf_type_by_id(btf_vmlinux, t->type);
3527+
if (!btf_type_is_struct(t)) {
3528+
bpf_log(log,
3529+
"raw_tp '%s' arg%d type %s is not a struct\n",
3530+
tname, arg, btf_kind_str[BTF_INFO_KIND(t->info)]);
3531+
return false;
3532+
}
3533+
bpf_log(log, "raw_tp '%s' arg%d has btf_id %d type %s '%s'\n",
3534+
tname, arg, info->btf_id, btf_kind_str[BTF_INFO_KIND(t->info)],
3535+
__btf_name_by_offset(btf_vmlinux, t->name_off));
3536+
return true;
3537+
}
3538+
3539+
int btf_struct_access(struct bpf_verifier_log *log,
3540+
const struct btf_type *t, int off, int size,
3541+
enum bpf_access_type atype,
3542+
u32 *next_btf_id)
3543+
{
3544+
const struct btf_member *member;
3545+
const struct btf_type *mtype;
3546+
const char *tname, *mname;
3547+
int i, moff = 0, msize;
3548+
3549+
again:
3550+
tname = __btf_name_by_offset(btf_vmlinux, t->name_off);
3551+
if (!btf_type_is_struct(t)) {
3552+
bpf_log(log, "Type '%s' is not a struct", tname);
3553+
return -EINVAL;
3554+
}
3555+
3556+
for_each_member(i, t, member) {
3557+
/* offset of the field in bits */
3558+
moff = btf_member_bit_offset(t, member);
3559+
3560+
if (btf_member_bitfield_size(t, member))
3561+
/* bitfields are not supported yet */
3562+
continue;
3563+
3564+
if (off + size <= moff / 8)
3565+
/* won't find anything, field is already too far */
3566+
break;
3567+
3568+
/* type of the field */
3569+
mtype = btf_type_by_id(btf_vmlinux, member->type);
3570+
mname = __btf_name_by_offset(btf_vmlinux, member->name_off);
3571+
3572+
/* skip modifiers */
3573+
while (btf_type_is_modifier(mtype))
3574+
mtype = btf_type_by_id(btf_vmlinux, mtype->type);
3575+
3576+
if (btf_type_is_array(mtype))
3577+
/* array deref is not supported yet */
3578+
continue;
3579+
3580+
if (!btf_type_has_size(mtype) && !btf_type_is_ptr(mtype)) {
3581+
bpf_log(log, "field %s doesn't have size\n", mname);
3582+
return -EFAULT;
3583+
}
3584+
if (btf_type_is_ptr(mtype))
3585+
msize = 8;
3586+
else
3587+
msize = mtype->size;
3588+
if (off >= moff / 8 + msize)
3589+
/* no overlap with member, keep iterating */
3590+
continue;
3591+
/* the 'off' we're looking for is either equal to start
3592+
* of this field or inside of this struct
3593+
*/
3594+
if (btf_type_is_struct(mtype)) {
3595+
/* our field must be inside that union or struct */
3596+
t = mtype;
3597+
3598+
/* adjust offset we're looking for */
3599+
off -= moff / 8;
3600+
goto again;
3601+
}
3602+
if (msize != size) {
3603+
/* field access size doesn't match */
3604+
bpf_log(log,
3605+
"cannot access %d bytes in struct %s field %s that has size %d\n",
3606+
size, tname, mname, msize);
3607+
return -EACCES;
3608+
}
3609+
3610+
if (btf_type_is_ptr(mtype)) {
3611+
const struct btf_type *stype;
3612+
3613+
stype = btf_type_by_id(btf_vmlinux, mtype->type);
3614+
/* skip modifiers */
3615+
while (btf_type_is_modifier(stype))
3616+
stype = btf_type_by_id(btf_vmlinux, stype->type);
3617+
if (btf_type_is_struct(stype)) {
3618+
*next_btf_id = mtype->type;
3619+
return PTR_TO_BTF_ID;
3620+
}
3621+
}
3622+
/* all other fields are treated as scalars */
3623+
return SCALAR_VALUE;
3624+
}
3625+
bpf_log(log, "struct %s doesn't have field at offset %d\n", tname, off);
3626+
return -EINVAL;
3627+
}
3628+
34393629
void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
34403630
struct seq_file *m)
34413631
{

0 commit comments

Comments
 (0)