Commit 39d726b7 authored by David S. Miller's avatar David S. Miller

Merge branch 'bpf_seccomp'

Daniel Borkmann says:

====================
BPF updates

This set gets rid of BPF special handling in seccomp filter preparation
and provides generic infrastructure from BPF side, which eventually also
allows for classic BPF JITs to add support for seccomp filters.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 0e00a0f7 ac67eb2c
...@@ -363,9 +363,6 @@ int sk_filter(struct sock *sk, struct sk_buff *skb); ...@@ -363,9 +363,6 @@ int sk_filter(struct sock *sk, struct sk_buff *skb);
void bpf_prog_select_runtime(struct bpf_prog *fp); void bpf_prog_select_runtime(struct bpf_prog *fp);
void bpf_prog_free(struct bpf_prog *fp); void bpf_prog_free(struct bpf_prog *fp);
int bpf_convert_filter(struct sock_filter *prog, int len,
struct bpf_insn *new_prog, int *new_len);
struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags); struct bpf_prog *bpf_prog_alloc(unsigned int size, gfp_t gfp_extra_flags);
struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size, struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
gfp_t gfp_extra_flags); gfp_t gfp_extra_flags);
...@@ -377,14 +374,17 @@ static inline void bpf_prog_unlock_free(struct bpf_prog *fp) ...@@ -377,14 +374,17 @@ static inline void bpf_prog_unlock_free(struct bpf_prog *fp)
__bpf_prog_free(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(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); void bpf_prog_destroy(struct bpf_prog *fp);
int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk); int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk);
int sk_attach_bpf(u32 ufd, struct sock *sk); int sk_attach_bpf(u32 ufd, struct sock *sk);
int sk_detach_filter(struct sock *sk); int sk_detach_filter(struct sock *sk);
int bpf_check_classic(const struct sock_filter *filter, unsigned int flen);
int sk_get_filter(struct sock *sk, struct sock_filter __user *filter, int sk_get_filter(struct sock *sk, struct sock_filter __user *filter,
unsigned int len); unsigned int len);
......
...@@ -346,16 +346,13 @@ static inline void seccomp_sync_threads(void) ...@@ -346,16 +346,13 @@ static inline void seccomp_sync_threads(void)
*/ */
static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
{ {
struct seccomp_filter *filter; struct seccomp_filter *sfilter;
unsigned long fp_size; int ret;
struct sock_filter *fp;
int new_len;
long ret;
if (fprog->len == 0 || fprog->len > BPF_MAXINSNS) if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
BUG_ON(INT_MAX / fprog->len < sizeof(struct sock_filter)); BUG_ON(INT_MAX / fprog->len < sizeof(struct sock_filter));
fp_size = fprog->len * sizeof(struct sock_filter);
/* /*
* Installing a seccomp filter requires that the task has * Installing a seccomp filter requires that the task has
...@@ -368,60 +365,21 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog) ...@@ -368,60 +365,21 @@ static struct seccomp_filter *seccomp_prepare_filter(struct sock_fprog *fprog)
CAP_SYS_ADMIN) != 0) CAP_SYS_ADMIN) != 0)
return ERR_PTR(-EACCES); return ERR_PTR(-EACCES);
fp = kzalloc(fp_size, GFP_KERNEL|__GFP_NOWARN);
if (!fp)
return ERR_PTR(-ENOMEM);
/* Copy the instructions from fprog. */
ret = -EFAULT;
if (copy_from_user(fp, fprog->filter, fp_size))
goto free_prog;
/* Check and rewrite the fprog via the skb checker */
ret = bpf_check_classic(fp, fprog->len);
if (ret)
goto free_prog;
/* Check and rewrite the fprog for seccomp use */
ret = seccomp_check_filter(fp, fprog->len);
if (ret)
goto free_prog;
/* Convert 'sock_filter' insns to 'bpf_insn' insns */
ret = bpf_convert_filter(fp, fprog->len, NULL, &new_len);
if (ret)
goto free_prog;
/* Allocate a new seccomp_filter */ /* Allocate a new seccomp_filter */
ret = -ENOMEM; sfilter = kzalloc(sizeof(*sfilter), GFP_KERNEL | __GFP_NOWARN);
filter = kzalloc(sizeof(struct seccomp_filter), if (!sfilter)
GFP_KERNEL|__GFP_NOWARN); return ERR_PTR(-ENOMEM);
if (!filter)
goto free_prog;
filter->prog = bpf_prog_alloc(bpf_prog_size(new_len), __GFP_NOWARN);
if (!filter->prog)
goto free_filter;
ret = bpf_convert_filter(fp, fprog->len, filter->prog->insnsi, &new_len);
if (ret)
goto free_filter_prog;
kfree(fp);
atomic_set(&filter->usage, 1);
filter->prog->len = new_len;
bpf_prog_select_runtime(filter->prog); ret = bpf_prog_create_from_user(&sfilter->prog, fprog,
seccomp_check_filter);
if (ret < 0) {
kfree(sfilter);
return ERR_PTR(ret);
}
return filter; atomic_set(&sfilter->usage, 1);
free_filter_prog: return sfilter;
__bpf_prog_free(filter->prog);
free_filter:
kfree(filter);
free_prog:
kfree(fp);
return ERR_PTR(ret);
} }
/** /**
......
...@@ -355,8 +355,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp, ...@@ -355,8 +355,8 @@ static bool convert_bpf_extensions(struct sock_filter *fp,
* for socket filters: ctx == 'struct sk_buff *', for seccomp: * for socket filters: ctx == 'struct sk_buff *', for seccomp:
* ctx == 'struct seccomp_data *'. * ctx == 'struct seccomp_data *'.
*/ */
int bpf_convert_filter(struct sock_filter *prog, int len, static int bpf_convert_filter(struct sock_filter *prog, int len,
struct bpf_insn *new_prog, int *new_len) struct bpf_insn *new_prog, int *new_len)
{ {
int new_flen = 0, pass = 0, target, i; int new_flen = 0, pass = 0, target, i;
struct bpf_insn *new_insn; struct bpf_insn *new_insn;
...@@ -371,7 +371,8 @@ int bpf_convert_filter(struct sock_filter *prog, int len, ...@@ -371,7 +371,8 @@ int bpf_convert_filter(struct sock_filter *prog, int len,
return -EINVAL; return -EINVAL;
if (new_prog) { if (new_prog) {
addrs = kcalloc(len, sizeof(*addrs), GFP_KERNEL); addrs = kcalloc(len, sizeof(*addrs),
GFP_KERNEL | __GFP_NOWARN);
if (!addrs) if (!addrs)
return -ENOMEM; return -ENOMEM;
} }
...@@ -751,7 +752,8 @@ static bool chk_code_allowed(u16 code_to_probe) ...@@ -751,7 +752,8 @@ static bool chk_code_allowed(u16 code_to_probe)
* *
* Returns 0 if the rule set is legal or -EINVAL if not. * Returns 0 if the rule set is legal or -EINVAL if not.
*/ */
int bpf_check_classic(const struct sock_filter *filter, unsigned int flen) static int bpf_check_classic(const struct sock_filter *filter,
unsigned int flen)
{ {
bool anc_found; bool anc_found;
int pc; int pc;
...@@ -825,7 +827,6 @@ int bpf_check_classic(const struct sock_filter *filter, unsigned int flen) ...@@ -825,7 +827,6 @@ int bpf_check_classic(const struct sock_filter *filter, unsigned int flen)
return -EINVAL; return -EINVAL;
} }
EXPORT_SYMBOL(bpf_check_classic);
static int bpf_prog_store_orig_filter(struct bpf_prog *fp, static int bpf_prog_store_orig_filter(struct bpf_prog *fp,
const struct sock_fprog *fprog) const struct sock_fprog *fprog)
...@@ -839,7 +840,9 @@ static int bpf_prog_store_orig_filter(struct bpf_prog *fp, ...@@ -839,7 +840,9 @@ static int bpf_prog_store_orig_filter(struct bpf_prog *fp,
fkprog = fp->orig_prog; fkprog = fp->orig_prog;
fkprog->len = fprog->len; fkprog->len = fprog->len;
fkprog->filter = kmemdup(fp->insns, fsize, GFP_KERNEL);
fkprog->filter = kmemdup(fp->insns, fsize,
GFP_KERNEL | __GFP_NOWARN);
if (!fkprog->filter) { if (!fkprog->filter) {
kfree(fp->orig_prog); kfree(fp->orig_prog);
return -ENOMEM; return -ENOMEM;
...@@ -941,7 +944,7 @@ static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp) ...@@ -941,7 +944,7 @@ static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp)
* pass. At this time, the user BPF is stored in fp->insns. * pass. At this time, the user BPF is stored in fp->insns.
*/ */
old_prog = kmemdup(fp->insns, old_len * sizeof(struct sock_filter), old_prog = kmemdup(fp->insns, old_len * sizeof(struct sock_filter),
GFP_KERNEL); GFP_KERNEL | __GFP_NOWARN);
if (!old_prog) { if (!old_prog) {
err = -ENOMEM; err = -ENOMEM;
goto out_err; goto out_err;
...@@ -988,7 +991,8 @@ static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp) ...@@ -988,7 +991,8 @@ static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp)
return ERR_PTR(err); return ERR_PTR(err);
} }
static 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; int err;
...@@ -1001,6 +1005,17 @@ static struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp) ...@@ -1001,6 +1005,17 @@ static struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp)
return ERR_PTR(err); return ERR_PTR(err);
} }
/* There might be additional checks and transformations
* needed on classic filters, f.e. in case of seccomp.
*/
if (trans) {
err = trans(fp->insns, fp->len);
if (err) {
__bpf_prog_release(fp);
return ERR_PTR(err);
}
}
/* Probe if we can JIT compile the filter and if so, do /* Probe if we can JIT compile the filter and if so, do
* the compilation of the filter. * the compilation of the filter.
*/ */
...@@ -1050,7 +1065,7 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog) ...@@ -1050,7 +1065,7 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog)
/* bpf_prepare_filter() already takes care of freeing /* bpf_prepare_filter() already takes care of freeing
* memory in case something goes wrong. * memory in case something goes wrong.
*/ */
fp = bpf_prepare_filter(fp); fp = bpf_prepare_filter(fp, NULL);
if (IS_ERR(fp)) if (IS_ERR(fp))
return PTR_ERR(fp); return PTR_ERR(fp);
...@@ -1059,6 +1074,53 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog) ...@@ -1059,6 +1074,53 @@ int bpf_prog_create(struct bpf_prog **pfp, struct sock_fprog_kern *fprog)
} }
EXPORT_SYMBOL_GPL(bpf_prog_create); 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) void bpf_prog_destroy(struct bpf_prog *fp)
{ {
__bpf_prog_release(fp); __bpf_prog_release(fp);
...@@ -1135,7 +1197,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk) ...@@ -1135,7 +1197,7 @@ int sk_attach_filter(struct sock_fprog *fprog, struct sock *sk)
/* bpf_prepare_filter() already takes care of freeing /* bpf_prepare_filter() already takes care of freeing
* memory in case something goes wrong. * memory in case something goes wrong.
*/ */
prog = bpf_prepare_filter(prog); prog = bpf_prepare_filter(prog, NULL);
if (IS_ERR(prog)) if (IS_ERR(prog))
return PTR_ERR(prog); return PTR_ERR(prog);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment