|
@@ -978,49 +978,68 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)
|
|
}
|
|
}
|
|
|
|
|
|
#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
|
|
#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
|
|
-long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
|
|
|
|
- void __user *data)
|
|
|
|
|
|
+static struct seccomp_filter *get_nth_filter(struct task_struct *task,
|
|
|
|
+ unsigned long filter_off)
|
|
{
|
|
{
|
|
- struct seccomp_filter *filter;
|
|
|
|
- struct sock_fprog_kern *fprog;
|
|
|
|
- long ret;
|
|
|
|
- unsigned long count = 0;
|
|
|
|
-
|
|
|
|
- if (!capable(CAP_SYS_ADMIN) ||
|
|
|
|
- current->seccomp.mode != SECCOMP_MODE_DISABLED) {
|
|
|
|
- return -EACCES;
|
|
|
|
- }
|
|
|
|
|
|
+ struct seccomp_filter *orig, *filter;
|
|
|
|
+ unsigned long count;
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Note: this is only correct because the caller should be the (ptrace)
|
|
|
|
+ * tracer of the task, otherwise lock_task_sighand is needed.
|
|
|
|
+ */
|
|
spin_lock_irq(&task->sighand->siglock);
|
|
spin_lock_irq(&task->sighand->siglock);
|
|
|
|
+
|
|
if (task->seccomp.mode != SECCOMP_MODE_FILTER) {
|
|
if (task->seccomp.mode != SECCOMP_MODE_FILTER) {
|
|
- ret = -EINVAL;
|
|
|
|
- goto out;
|
|
|
|
|
|
+ spin_unlock_irq(&task->sighand->siglock);
|
|
|
|
+ return ERR_PTR(-EINVAL);
|
|
}
|
|
}
|
|
|
|
|
|
- filter = task->seccomp.filter;
|
|
|
|
- while (filter) {
|
|
|
|
- filter = filter->prev;
|
|
|
|
|
|
+ orig = task->seccomp.filter;
|
|
|
|
+ __get_seccomp_filter(orig);
|
|
|
|
+ spin_unlock_irq(&task->sighand->siglock);
|
|
|
|
+
|
|
|
|
+ count = 0;
|
|
|
|
+ for (filter = orig; filter; filter = filter->prev)
|
|
count++;
|
|
count++;
|
|
- }
|
|
|
|
|
|
|
|
if (filter_off >= count) {
|
|
if (filter_off >= count) {
|
|
- ret = -ENOENT;
|
|
|
|
|
|
+ filter = ERR_PTR(-ENOENT);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
- count -= filter_off;
|
|
|
|
|
|
|
|
- filter = task->seccomp.filter;
|
|
|
|
- while (filter && count > 1) {
|
|
|
|
- filter = filter->prev;
|
|
|
|
|
|
+ count -= filter_off;
|
|
|
|
+ for (filter = orig; filter && count > 1; filter = filter->prev)
|
|
count--;
|
|
count--;
|
|
- }
|
|
|
|
|
|
|
|
if (WARN_ON(count != 1 || !filter)) {
|
|
if (WARN_ON(count != 1 || !filter)) {
|
|
- /* The filter tree shouldn't shrink while we're using it. */
|
|
|
|
- ret = -ENOENT;
|
|
|
|
|
|
+ filter = ERR_PTR(-ENOENT);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ __get_seccomp_filter(filter);
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ __put_seccomp_filter(orig);
|
|
|
|
+ return filter;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
|
|
|
|
+ void __user *data)
|
|
|
|
+{
|
|
|
|
+ struct seccomp_filter *filter;
|
|
|
|
+ struct sock_fprog_kern *fprog;
|
|
|
|
+ long ret;
|
|
|
|
+
|
|
|
|
+ if (!capable(CAP_SYS_ADMIN) ||
|
|
|
|
+ current->seccomp.mode != SECCOMP_MODE_DISABLED) {
|
|
|
|
+ return -EACCES;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ filter = get_nth_filter(task, filter_off);
|
|
|
|
+ if (IS_ERR(filter))
|
|
|
|
+ return PTR_ERR(filter);
|
|
|
|
+
|
|
fprog = filter->prog->orig_prog;
|
|
fprog = filter->prog->orig_prog;
|
|
if (!fprog) {
|
|
if (!fprog) {
|
|
/* This must be a new non-cBPF filter, since we save
|
|
/* This must be a new non-cBPF filter, since we save
|
|
@@ -1035,17 +1054,44 @@ long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
|
|
if (!data)
|
|
if (!data)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
- __get_seccomp_filter(filter);
|
|
|
|
- spin_unlock_irq(&task->sighand->siglock);
|
|
|
|
-
|
|
|
|
if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
|
|
if (copy_to_user(data, fprog->filter, bpf_classic_proglen(fprog)))
|
|
ret = -EFAULT;
|
|
ret = -EFAULT;
|
|
|
|
|
|
|
|
+out:
|
|
__put_seccomp_filter(filter);
|
|
__put_seccomp_filter(filter);
|
|
return ret;
|
|
return ret;
|
|
|
|
+}
|
|
|
|
|
|
-out:
|
|
|
|
- spin_unlock_irq(&task->sighand->siglock);
|
|
|
|
|
|
+long seccomp_get_metadata(struct task_struct *task,
|
|
|
|
+ unsigned long size, void __user *data)
|
|
|
|
+{
|
|
|
|
+ long ret;
|
|
|
|
+ struct seccomp_filter *filter;
|
|
|
|
+ struct seccomp_metadata kmd = {};
|
|
|
|
+
|
|
|
|
+ if (!capable(CAP_SYS_ADMIN) ||
|
|
|
|
+ current->seccomp.mode != SECCOMP_MODE_DISABLED) {
|
|
|
|
+ return -EACCES;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ size = min_t(unsigned long, size, sizeof(kmd));
|
|
|
|
+
|
|
|
|
+ if (copy_from_user(&kmd, data, size))
|
|
|
|
+ return -EFAULT;
|
|
|
|
+
|
|
|
|
+ filter = get_nth_filter(task, kmd.filter_off);
|
|
|
|
+ if (IS_ERR(filter))
|
|
|
|
+ return PTR_ERR(filter);
|
|
|
|
+
|
|
|
|
+ memset(&kmd, 0, sizeof(kmd));
|
|
|
|
+ if (filter->log)
|
|
|
|
+ kmd.flags |= SECCOMP_FILTER_FLAG_LOG;
|
|
|
|
+
|
|
|
|
+ ret = size;
|
|
|
|
+ if (copy_to_user(data, &kmd, size))
|
|
|
|
+ ret = -EFAULT;
|
|
|
|
+
|
|
|
|
+ __put_seccomp_filter(filter);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|