seccomp, filter: add and use bpf_prog_create_from_user from seccomp
authorDaniel Borkmann <daniel@iogearbox.net>
Wed, 6 May 2015 14:12:30 +0000 (16:12 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sat, 9 May 2015 21:35:05 +0000 (17:35 -0400)
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>
include/linux/filter.h
kernel/seccomp.c
net/core/filter.c

index 0dcb44bcfc5f7769619845518a9a5e48a798dd07..3c03a6085b82b7a9bf5c38037407e3bbe24045a9 100644 (file)
@@ -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);
 
index 93d40f7f368315049391bf33cd9baba767592002..245df6b32b81f8eef778a203c2edb8432a52abd6 100644 (file)
@@ -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;
 }
 
 /**
index 45c015d6b9740f51e47216f5b7cb51268910abe0..a831f193e2c77184fa3a116aed97c9b4b7a62ff7 100644 (file)
@@ -991,8 +991,8 @@ out_err:
        return ERR_PTR(err);
 }
 
-struct bpf_prog *bpf_prepare_filter(struct bpf_prog *fp,
-                                   bpf_aux_classic_check_t trans)
+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);