Details of BPF
Published:
How seccomp works in kernel
el0_sync
–> el0_sync_handler
–> el0_svc
–> do_el0_svc
–> el0_svc_common
–> syscall_trace_enter
–> secure_computing
–> __secure_computing
–> __seccomp_filter
–> seccomp_run_filters
static inline int secure_computing(void)
{
if (unlikely(test_thread_flag(TIF_SECCOMP)))
return __secure_computing(NULL);
return 0;
}
这里面可以看到seccomp开关被thread_info
中的一个flag控制。
static u32 seccomp_run_filters(const struct seccomp_data *sd)
{
struct seccomp_filter *f =
lockless_dereference(current->seccomp.filter);
...
u32 cur_ret = BPF_PROG_RUN(f->prog, (void *)sd);
}
struct seccomp_filter {
atomic_t usage;
struct seccomp_filter *prev;
struct bpf_prog *prog;
};
struct bpf_prog {
u16 pages; /* Number of allocated pages */
...
unsigned int (*bpf_func)(const struct sk_buff *skb,
const struct bpf_insn *filter);
/* Instructions for interpreter */
union {
struct sock_filter insns[0];
struct bpf_insn insnsi[0];
};
};
#define BPF_PROG_RUN(filter, ctx) (*filter->bpf_func)(ctx, filter->insnsi)
可以看到bpf_func运行bpf_prog->insnsi;bpf_func指向 ___bpf_prog_run
;核心是个jump table循环。
如何定位bpf_prog->insnsi
如果不能改function pointer,只能改bpf_prog->insnsi,那么如何定位它呢?
static u32 seccomp_run_filters(const struct seccomp_data *sd)
{
struct seccomp_filter *f =
lockless_dereference(current->seccomp.filter);
...
u32 cur_ret = BPF_PROG_RUN(f->prog, (void *)sd);
}
struct task_struct {
struct seccomp seccomp;
}
struct seccomp {
int mode;
atomic_t filter_count;
struct seccomp_filter *filter;
};
struct seccomp_filter {
refcount_t refs;
refcount_t users;
bool log;
struct seccomp_filter *prev;
struct bpf_prog *prog;
struct notification *notif;
struct mutex notify_lock;
wait_queue_head_t wqh;
};
我们可以看到seccomp_filter
从task_struct->seccomp
中来。一个process可以有多个secomp_filter,每个secomp_filter都有自己的bpf_prog。因此可以简单的定位到bpf_prog->insnsi
BFP program load
Two ways, one is bpf syscall
–> bpf_prog_load
–> bpf_check
Another way is via prctl syscall
–> prctl_set_seccomp
–>do_seccomp
–>seccomp_set_mode_filter
貌似seccomp的filer没有经过bpf_check,使用qemu动态测试一下