seccomp: hoist out filter resolving logic

Hoist out the nth filter resolving logic that ptrace uses into a new
function. We'll use this in the next patch to implement the new
PTRACE_SECCOMP_GET_FILTER_FLAGS command.

Signed-off-by: Tycho Andersen <tycho@docker.com>
CC: Kees Cook <keescook@chromium.org>
CC: Andy Lutomirski <luto@amacapital.net>
CC: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
This commit is contained in:
Tycho Andersen 2017-10-11 09:39:20 -06:00 committed by Kees Cook
parent 4fbd8d194f
commit f06eae831f
1 changed files with 50 additions and 37 deletions

View File

@ -978,48 +978,67 @@ 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)
static struct seccomp_filter *get_nth_filter(struct task_struct *task,
unsigned long filter_off)
{
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);
if (task->seccomp.mode != SECCOMP_MODE_FILTER) {
spin_unlock_irq(&task->sighand->siglock);
return ERR_PTR(-EINVAL);
}
orig = task->seccomp.filter;
__get_seccomp_filter(orig);
spin_unlock_irq(&task->sighand->siglock);
count = 0;
for (filter = orig; filter; filter = filter->prev)
count++;
if (filter_off >= count) {
filter = ERR_PTR(-ENOENT);
goto out;
}
count -= filter_off;
for (filter = orig; filter && count > 1; filter = filter->prev)
count--;
if (WARN_ON(count != 1 || !filter)) {
filter = ERR_PTR(-ENOENT);
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, long seccomp_get_filter(struct task_struct *task, unsigned long filter_off,
void __user *data) void __user *data)
{ {
struct seccomp_filter *filter; struct seccomp_filter *filter;
struct sock_fprog_kern *fprog; struct sock_fprog_kern *fprog;
long ret; long ret;
unsigned long count = 0;
if (!capable(CAP_SYS_ADMIN) || if (!capable(CAP_SYS_ADMIN) ||
current->seccomp.mode != SECCOMP_MODE_DISABLED) { current->seccomp.mode != SECCOMP_MODE_DISABLED) {
return -EACCES; return -EACCES;
} }
spin_lock_irq(&task->sighand->siglock); filter = get_nth_filter(task, filter_off);
if (task->seccomp.mode != SECCOMP_MODE_FILTER) { if (IS_ERR(filter))
ret = -EINVAL; return PTR_ERR(filter);
goto out;
}
filter = task->seccomp.filter;
while (filter) {
filter = filter->prev;
count++;
}
if (filter_off >= count) {
ret = -ENOENT;
goto out;
}
count -= filter_off;
filter = task->seccomp.filter;
while (filter && count > 1) {
filter = filter->prev;
count--;
}
if (WARN_ON(count != 1 || !filter)) {
/* The filter tree shouldn't shrink while we're using it. */
ret = -ENOENT;
goto out;
}
fprog = filter->prog->orig_prog; fprog = filter->prog->orig_prog;
if (!fprog) { if (!fprog) {
@ -1035,17 +1054,11 @@ 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;
__put_seccomp_filter(filter);
return ret;
out: out:
spin_unlock_irq(&task->sighand->siglock); __put_seccomp_filter(filter);
return ret; return ret;
} }
#endif #endif