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动态测试一下