seccomp, filter: add and use bpf_prog_create_from_user from seccomp
Seccomp has always been a special candidate when it comes to preparation of its filters in seccomp_prepare_filter(). Due to the extra checks and filter rewrite it partially duplicates code and has BPF internals exposed. This patch adds a generic API inside the BPF code code that seccomp can use and thus keep it's filter preparation code minimal and better maintainable. The other side-effect is that now classic JITs can add seccomp support as well by only providing a BPF_LDX | BPF_W | BPF_ABS translation. Tested with seccomp and BPF test suites. Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Cc: Nicolas Schichan <nschichan@freebox.fr> Cc: Alexei Starovoitov <ast@plumgrid.com> Cc: Kees Cook <keescook@chromium.org> Acked-by: Alexei Starovoitov <ast@plumgrid.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
658da9379d
commit
ac67eb2c53
3 changed files with 66 additions and 39 deletions
|
@ -374,19 +374,17 @@ static inline void bpf_prog_unlock_free(struct bpf_prog *fp)
|
|||
__bpf_prog_free(fp);
|
||||
}
|
||||
|
||||
typedef int (*bpf_aux_classic_check_t)(struct sock_filter *filter,
|
||||
unsigned int flen);
|
||||
|
||||
int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog);
|
||||
int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
|
||||
bpf_aux_classic_check_t trans);
|
||||
void bpf_prog_destroy(struct bpf_prog *fp);
|
||||
|
||||
int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
|
||||
int sk_attach_bpf(u32 ufd, struct sock *sk);
|
||||
int sk_detach_filter(struct sock *sk);
|
||||
|
||||
typedef int (*bpf_aux_classic_check_t)(struct sock_filter *filter,
|
||||
unsigned int flen);
|
||||
|
||||
struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp,
|
||||
bpf_aux_classic_check_t trans);
|
||||
|
||||
int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
|
||||
unsigned int len);
|
||||
|
||||
|
|
|
@ -346,15 +346,13 @@ static inline void seccomp_sync_threads(void)
|
|||
*/
|
||||
static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
|
||||
{
|
||||
struct seccomp_filter *filter;
|
||||
struct bpf_prog *prog;
|
||||
unsigned long fsize;
|
||||
struct seccomp_filter *sfilter;
|
||||
int ret;
|
||||
|
||||
if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
BUG_ON(INT_MAX / fprog->len < sizeof(struct sock_filter));
|
||||
fsize = bpf_classic_proglen(fprog);
|
||||
|
||||
/*
|
||||
* Installing a seccomp filter requires that the task has
|
||||
|
@ -367,37 +365,21 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
|
|||
CAP_SYS_ADMIN) != 0)
|
||||
return ERR_PTR(-EACCES);
|
||||
|
||||
prog = bpf_prog_alloc(bpf_prog_size(fprog->len), 0);
|
||||
if (!prog)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* Copy the instructions from fprog. */
|
||||
if (copy_from_user(prog->insns, fprog->filter, fsize)) {
|
||||
__bpf_prog_free(prog);
|
||||
return ERR_PTR(-EFAULT);
|
||||
}
|
||||
|
||||
prog->len = fprog->len;
|
||||
|
||||
/* bpf_prepare_filter() already takes care of freeing
|
||||
* memory in case something goes wrong.
|
||||
*/
|
||||
prog = bpf_prepare_filter(prog, seccomp_check_filter);
|
||||
if (IS_ERR(prog))
|
||||
return ERR_CAST(prog);
|
||||
|
||||
/* Allocate a new seccomp_filter */
|
||||
filter = kzalloc(sizeof(struct seccomp_filter),
|
||||
GFP_KERNEL|__GFP_NOWARN);
|
||||
if (!filter) {
|
||||
bpf_prog_destroy(prog);
|
||||
sfilter = kzalloc(sizeof(*sfilter), GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!sfilter)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = bpf_prog_create_from_user(&sfilter->prog, fprog,
|
||||
seccomp_check_filter);
|
||||
if (ret < 0) {
|
||||
kfree(sfilter);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
filter->prog = prog;
|
||||
atomic_set(&filter->usage, 1);
|
||||
atomic_set(&sfilter->usage, 1);
|
||||
|
||||
return filter;
|
||||
return sfilter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -991,7 +991,7 @@ static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp)
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp,
|
||||
static struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp,
|
||||
bpf_aux_classic_check_t trans)
|
||||
{
|
||||
int err;
|
||||
|
@ -1074,6 +1074,53 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_prog_create);
|
||||
|
||||
/**
|
||||
* bpf_prog_create_from_user - create an unattached filter from user buffer
|
||||
* @pfp: the unattached filter that is created
|
||||
* @fprog: the filter program
|
||||
* @trans: post-classic verifier transformation handler
|
||||
*
|
||||
* This function effectively does the same as bpf_prog_create(), only
|
||||
* that it builds up its insns buffer from user space provided buffer.
|
||||
* It also allows for passing a bpf_aux_classic_check_t handler.
|
||||
*/
|
||||
int bpf_prog_create_from_user(struct bpf_prog **pfp, struct sock_fprog *fprog,
|
||||
bpf_aux_classic_check_t trans)
|
||||
{
|
||||
unsigned int fsize = bpf_classic_proglen(fprog);
|
||||
struct bpf_prog *fp;
|
||||
|
||||
/* Make sure new filter is there and in the right amounts. */
|
||||
if (fprog->filter == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
fp = bpf_prog_alloc(bpf_prog_size(fprog->len), 0);
|
||||
if (!fp)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(fp->insns, fprog->filter, fsize)) {
|
||||
__bpf_prog_free(fp);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
fp->len = fprog->len;
|
||||
/* Since unattached filters are not copied back to user
|
||||
* space through sk_get_filter(), we do not need to hold
|
||||
* a copy here, and can spare us the work.
|
||||
*/
|
||||
fp->orig_prog = NULL;
|
||||
|
||||
/* bpf_prepare_filter() already takes care of freeing
|
||||
* memory in case something goes wrong.
|
||||
*/
|
||||
fp = bpf_prepare_filter(fp, trans);
|
||||
if (IS_ERR(fp))
|
||||
return PTR_ERR(fp);
|
||||
|
||||
*pfp = fp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bpf_prog_destroy(struct bpf_prog *fp)
|
||||
{
|
||||
__bpf_prog_release(fp);
|
||||
|
|
Loading…
Reference in a new issue