Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
[sfrench/cifs-2.6.git] / kernel / seccomp.c
index 0e004a70f63a572d751b95cbdef8316ce1e22053..fd609bd9d6dd7b5c40ec637aa288ae55f88d4505 100644 (file)
@@ -55,60 +55,33 @@ struct seccomp_filter {
        atomic_t usage;
        struct seccomp_filter *prev;
        unsigned short len;  /* Instruction count */
-       struct sock_filter insns[];
+       struct sock_filter_int insnsi[];
 };
 
 /* Limit any path through the tree to 256KB worth of instructions. */
 #define MAX_INSNS_PER_PATH ((1 << 18) / sizeof(struct sock_filter))
 
-/**
- * get_u32 - returns a u32 offset into data
- * @data: a unsigned 64 bit value
- * @index: 0 or 1 to return the first or second 32-bits
- *
- * This inline exists to hide the length of unsigned long.  If a 32-bit
- * unsigned long is passed in, it will be extended and the top 32-bits will be
- * 0. If it is a 64-bit unsigned long, then whatever data is resident will be
- * properly returned.
- *
+/*
  * Endianness is explicitly ignored and left for BPF program authors to manage
  * as per the specific architecture.
  */
-static inline u32 get_u32(u64 data, int index)
+static void populate_seccomp_data(struct seccomp_data *sd)
 {
-       return ((u32 *)&data)[index];
-}
+       struct task_struct *task = current;
+       struct pt_regs *regs = task_pt_regs(task);
 
-/* Helper for bpf_load below. */
-#define BPF_DATA(_name) offsetof(struct seccomp_data, _name)
-/**
- * bpf_load: checks and returns a pointer to the requested offset
- * @off: offset into struct seccomp_data to load from
- *
- * Returns the requested 32-bits of data.
- * seccomp_check_filter() should assure that @off is 32-bit aligned
- * and not out of bounds.  Failure to do so is a BUG.
- */
-u32 seccomp_bpf_load(int off)
-{
-       struct pt_regs *regs = task_pt_regs(current);
-       if (off == BPF_DATA(nr))
-               return syscall_get_nr(current, regs);
-       if (off == BPF_DATA(arch))
-               return syscall_get_arch(current, regs);
-       if (off >= BPF_DATA(args[0]) && off < BPF_DATA(args[6])) {
-               unsigned long value;
-               int arg = (off - BPF_DATA(args[0])) / sizeof(u64);
-               int index = !!(off % sizeof(u64));
-               syscall_get_arguments(current, regs, arg, 1, &value);
-               return get_u32(value, index);
-       }
-       if (off == BPF_DATA(instruction_pointer))
-               return get_u32(KSTK_EIP(current), 0);
-       if (off == BPF_DATA(instruction_pointer) + sizeof(u32))
-               return get_u32(KSTK_EIP(current), 1);
-       /* seccomp_check_filter should make this impossible. */
-       BUG();
+       sd->nr = syscall_get_nr(task, regs);
+       sd->arch = syscall_get_arch(task, regs);
+
+       /* Unroll syscall_get_args to help gcc on arm. */
+       syscall_get_arguments(task, regs, 0, 1, (unsigned long *) &sd->args[0]);
+       syscall_get_arguments(task, regs, 1, 1, (unsigned long *) &sd->args[1]);
+       syscall_get_arguments(task, regs, 2, 1, (unsigned long *) &sd->args[2]);
+       syscall_get_arguments(task, regs, 3, 1, (unsigned long *) &sd->args[3]);
+       syscall_get_arguments(task, regs, 4, 1, (unsigned long *) &sd->args[4]);
+       syscall_get_arguments(task, regs, 5, 1, (unsigned long *) &sd->args[5]);
+
+       sd->instruction_pointer = KSTK_EIP(task);
 }
 
 /**
@@ -133,17 +106,17 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
 
                switch (code) {
                case BPF_S_LD_W_ABS:
-                       ftest->code = BPF_S_ANC_SECCOMP_LD_W;
+                       ftest->code = BPF_LDX | BPF_W | BPF_ABS;
                        /* 32-bit aligned and not out of bounds. */
                        if (k >= sizeof(struct seccomp_data) || k & 3)
                                return -EINVAL;
                        continue;
                case BPF_S_LD_W_LEN:
-                       ftest->code = BPF_S_LD_IMM;
+                       ftest->code = BPF_LD | BPF_IMM;
                        ftest->k = sizeof(struct seccomp_data);
                        continue;
                case BPF_S_LDX_W_LEN:
-                       ftest->code = BPF_S_LDX_IMM;
+                       ftest->code = BPF_LDX | BPF_IMM;
                        ftest->k = sizeof(struct seccomp_data);
                        continue;
                /* Explicitly include allowed calls. */
@@ -185,6 +158,7 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
                case BPF_S_JMP_JGT_X:
                case BPF_S_JMP_JSET_K:
                case BPF_S_JMP_JSET_X:
+                       sk_decode_filter(ftest, ftest);
                        continue;
                default:
                        return -EINVAL;
@@ -202,18 +176,21 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
 static u32 seccomp_run_filters(int syscall)
 {
        struct seccomp_filter *f;
+       struct seccomp_data sd;
        u32 ret = SECCOMP_RET_ALLOW;
 
        /* Ensure unexpected behavior doesn't result in failing open. */
        if (WARN_ON(current->seccomp.filter == NULL))
                return SECCOMP_RET_KILL;
 
+       populate_seccomp_data(&sd);
+
        /*
         * All filters in the list are evaluated and the lowest BPF return
         * value always takes priority (ignoring the DATA).
         */
        for (f = current->seccomp.filter; f; f = f->prev) {
-               u32 cur_ret = sk_run_filter(NULL, f->insns);
+               u32 cur_ret = sk_run_filter_int_seccomp(&sd, f->insnsi);
                if ((cur_ret & SECCOMP_RET_ACTION) < (ret & SECCOMP_RET_ACTION))
                        ret = cur_ret;
        }
@@ -231,6 +208,8 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
        struct seccomp_filter *filter;
        unsigned long fp_size = fprog->len * sizeof(struct sock_filter);
        unsigned long total_insns = fprog->len;
+       struct sock_filter *fp;
+       int new_len;
        long ret;
 
        if (fprog->len == 0 || fprog->len > BPF_MAXINSNS)
@@ -252,28 +231,43 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
                                     CAP_SYS_ADMIN) != 0)
                return -EACCES;
 
-       /* Allocate a new seccomp_filter */
-       filter = kzalloc(sizeof(struct seccomp_filter) + fp_size,
-                        GFP_KERNEL|__GFP_NOWARN);
-       if (!filter)
+       fp = kzalloc(fp_size, GFP_KERNEL|__GFP_NOWARN);
+       if (!fp)
                return -ENOMEM;
-       atomic_set(&filter->usage, 1);
-       filter->len = fprog->len;
 
        /* Copy the instructions from fprog. */
        ret = -EFAULT;
-       if (copy_from_user(filter->insns, fprog->filter, fp_size))
-               goto fail;
+       if (copy_from_user(fp, fprog->filter, fp_size))
+               goto free_prog;
 
        /* Check and rewrite the fprog via the skb checker */
-       ret = sk_chk_filter(filter->insns, filter->len);
+       ret = sk_chk_filter(fp, fprog->len);
        if (ret)
-               goto fail;
+               goto free_prog;
 
        /* Check and rewrite the fprog for seccomp use */
-       ret = seccomp_check_filter(filter->insns, filter->len);
+       ret = seccomp_check_filter(fp, fprog->len);
+       if (ret)
+               goto free_prog;
+
+       /* Convert 'sock_filter' insns to 'sock_filter_int' insns */
+       ret = sk_convert_filter(fp, fprog->len, NULL, &new_len);
+       if (ret)
+               goto free_prog;
+
+       /* Allocate a new seccomp_filter */
+       filter = kzalloc(sizeof(struct seccomp_filter) +
+                        sizeof(struct sock_filter_int) * new_len,
+                        GFP_KERNEL|__GFP_NOWARN);
+       if (!filter)
+               goto free_prog;
+
+       ret = sk_convert_filter(fp, fprog->len, filter->insnsi, &new_len);
        if (ret)
-               goto fail;
+               goto free_filter;
+
+       atomic_set(&filter->usage, 1);
+       filter->len = new_len;
 
        /*
         * If there is an existing filter, make it the prev and don't drop its
@@ -282,8 +276,11 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)
        filter->prev = current->seccomp.filter;
        current->seccomp.filter = filter;
        return 0;
-fail:
+
+free_filter:
        kfree(filter);
+free_prog:
+       kfree(fp);
        return ret;
 }