Hi all,
Here's v6 of the seccomp filter c/r stuff. I've dropped the concept of a
seccomp fd entirely, in favor of just dumping the bpf data. This doesn't allow
us to restore correctly in all cases, though, as noted in the patch notes.
Note that this is now based on http://patchwork.ozlabs.org/patch/525492/ and
will need to be built with that patch applied. This gets rid of two incorrect
patches in a previous series and is a nicer API.
Thoughts welcome,
Tycho
This patch adds support for dumping a process' (classic BPF) seccomp
filters via ptrace.
PTRACE_SECCOMP_GET_FILTER allows the tracer to dump the user's classic BPF
seccomp filters. addr should be an integer which represents the ith seccomp
filter (0 is the most recently installed filter). data should be a struct
sock_filter * with enough room for the ith filter, or NULL, in which case
the filter is not saved. The return value for this command is the number of
BPF instructions the program represents, or negative in the case of errors.
A command specific error is ENOENT, which indicates that there is no ith
filter in this seccomp tree.
A caveat with this approach is that there is no way to get explicitly at
the heirarchy of seccomp filters, and users need to memcmp() filters to
decide which are inherited. This means that a task which installs two of
the same filter can potentially confuse users of this interface.
Signed-off-by: Tycho Andersen <redacted>
CC: Kees Cook <redacted>
CC: Will Drewry <wad@chromium.org>
CC: Oleg Nesterov <oleg@redhat.com>
CC: Andy Lutomirski <luto@amacapital.net>
CC: Pavel Emelyanov <redacted>
CC: Serge E. Hallyn <redacted>
CC: Alexei Starovoitov <ast@kernel.org>
CC: Daniel Borkmann <daniel@iogearbox.net>
---
include/linux/seccomp.h | 11 +++++++++
include/uapi/linux/ptrace.h | 2 ++
kernel/ptrace.c | 5 ++++
kernel/seccomp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 74 insertions(+), 1 deletion(-)
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request,break;}#endif++casePTRACE_SECCOMP_GET_FILTER:+ret=seccomp_get_filter(child,addr,datavp);+break;+default:break;}
@@ -867,3 +868,57 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)/* prctl interface doesn't have flags, so they are always zero. */returndo_seccomp(op,0,uargs);}++#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)+longseccomp_get_filter(structtask_struct*task,longn,void__user*data)+{+structseccomp_filter*filter;+structsock_fprog_kern*fprog;+longret;++if(n<0)+return-EINVAL;++spin_lock_irq(¤t->sighand->siglock);+if(!capable(CAP_SYS_ADMIN)||+current->seccomp.mode!=SECCOMP_MODE_DISABLED){+ret=-EACCES;+gotoout_self;+}++spin_lock_irq(&task->sighand->siglock);+if(task->seccomp.mode!=SECCOMP_MODE_FILTER){+ret=-EINVAL;+gotoout_task;+}++filter=task->seccomp.filter;+while(n>0&&filter){+filter=filter->prev;+n--;+}++if(!filter){+ret=-ENOENT;+gotoout_task;+}++fprog=filter->prog->orig_prog;++ret=fprog->len;+if(!data)+gotoout_task;++if(copy_to_user(data,fprog->filter,bpf_classic_proglen(fprog))){+ret=-EFAULT;+gotoout_task;+}++out_task:+spin_unlock_irq(&task->sighand->siglock);++out_self:+spin_unlock_irq(¤t->sighand->siglock);+returnret;+}+#endif
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2015-10-07 10:25:51
On 10/07/2015 11:46 AM, Tycho Andersen wrote:
quoted hunk
This patch adds support for dumping a process' (classic BPF) seccomp
filters via ptrace.
PTRACE_SECCOMP_GET_FILTER allows the tracer to dump the user's classic BPF
seccomp filters. addr should be an integer which represents the ith seccomp
filter (0 is the most recently installed filter). data should be a struct
sock_filter * with enough room for the ith filter, or NULL, in which case
the filter is not saved. The return value for this command is the number of
BPF instructions the program represents, or negative in the case of errors.
A command specific error is ENOENT, which indicates that there is no ith
filter in this seccomp tree.
A caveat with this approach is that there is no way to get explicitly at
the heirarchy of seccomp filters, and users need to memcmp() filters to
decide which are inherited. This means that a task which installs two of
the same filter can potentially confuse users of this interface.
Signed-off-by: Tycho Andersen <redacted>
CC: Kees Cook <redacted>
CC: Will Drewry <wad@chromium.org>
CC: Oleg Nesterov <oleg@redhat.com>
CC: Andy Lutomirski <luto@amacapital.net>
CC: Pavel Emelyanov <redacted>
CC: Serge E. Hallyn <redacted>
CC: Alexei Starovoitov <ast@kernel.org>
CC: Daniel Borkmann <daniel@iogearbox.net>
---
include/linux/seccomp.h | 11 +++++++++
include/uapi/linux/ptrace.h | 2 ++
kernel/ptrace.c | 5 ++++
kernel/seccomp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 74 insertions(+), 1 deletion(-)
Nit: -ENOTSUP would probably be the better choice? -EINVAL might just
be confusing to users? (Would be unclear to them whether there's actual
support of dumping or whether it's just an invalid argument.)
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request,break;}#endif++casePTRACE_SECCOMP_GET_FILTER:+ret=seccomp_get_filter(child,addr,datavp);+break;+default:break;}
@@ -867,3 +868,57 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)/* prctl interface doesn't have flags, so they are always zero. */returndo_seccomp(op,0,uargs);}++#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)+longseccomp_get_filter(structtask_struct*task,longn,void__user*data)+{+structseccomp_filter*filter;+structsock_fprog_kern*fprog;+longret;++if(n<0)+return-EINVAL;
I would probably give 'n' a better name, maybe 'filter_off' to denote an
offset in the task's filter list?
So, it's called as seccomp_get_filter(child, addr, datavp), and addr is
an unsigned long in ptrace_request(). Any reasons why making this 'long n'
with adding this above check?
+ spin_lock_irq(¤t->sighand->siglock);
+ if (!capable(CAP_SYS_ADMIN) ||
The capability check should probably happen before taking the task's spinlock.
From: Daniel Borkmann <daniel@iogearbox.net> Date: 2015-10-07 10:34:35
On 10/07/2015 12:25 PM, Daniel Borkmann wrote:
On 10/07/2015 11:46 AM, Tycho Andersen wrote:
quoted
This patch adds support for dumping a process' (classic BPF) seccomp
filters via ptrace.
PTRACE_SECCOMP_GET_FILTER allows the tracer to dump the user's classic BPF
seccomp filters. addr should be an integer which represents the ith seccomp
filter (0 is the most recently installed filter). data should be a struct
sock_filter * with enough room for the ith filter, or NULL, in which case
the filter is not saved. The return value for this command is the number of
BPF instructions the program represents, or negative in the case of errors.
A command specific error is ENOENT, which indicates that there is no ith
filter in this seccomp tree.
A caveat with this approach is that there is no way to get explicitly at
the heirarchy of seccomp filters, and users need to memcmp() filters to
decide which are inherited. This means that a task which installs two of
the same filter can potentially confuse users of this interface.
Signed-off-by: Tycho Andersen <redacted>
CC: Kees Cook <redacted>
CC: Will Drewry <redacted>
CC: Oleg Nesterov <redacted>
CC: Andy Lutomirski <redacted>
CC: Pavel Emelyanov <redacted>
CC: Serge E. Hallyn <serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA@public.gmane.org>
CC: Alexei Starovoitov <redacted>
CC: Daniel Borkmann <redacted>
---
include/linux/seccomp.h | 11 +++++++++
include/uapi/linux/ptrace.h | 2 ++
kernel/ptrace.c | 5 ++++
kernel/seccomp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 74 insertions(+), 1 deletion(-)
Nit: -ENOTSUP would probably be the better choice? -EINVAL might just
be confusing to users? (Would be unclear to them whether there's actual
support of dumping or whether it's just an invalid argument.)
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request,break;}#endif++casePTRACE_SECCOMP_GET_FILTER:+ret=seccomp_get_filter(child,addr,datavp);+break;+default:break;}
@@ -867,3 +868,57 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)/* prctl interface doesn't have flags, so they are always zero. */returndo_seccomp(op,0,uargs);}++#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)+longseccomp_get_filter(structtask_struct*task,longn,void__user*data)+{+structseccomp_filter*filter;+structsock_fprog_kern*fprog;+longret;++if(n<0)+return-EINVAL;
I would probably give 'n' a better name, maybe 'filter_off' to denote an
offset in the task's filter list?
So, it's called as seccomp_get_filter(child, addr, datavp), and addr is
an unsigned long in ptrace_request(). Any reasons why making this 'long n'
with adding this above check?
quoted
+ spin_lock_irq(¤t->sighand->siglock);
+ if (!capable(CAP_SYS_ADMIN) ||
The capability check should probably happen before taking the task's spinlock.
Hi Daniel,
On Wed, Oct 07, 2015 at 12:25:39PM +0200, Daniel Borkmann wrote:
On 10/07/2015 11:46 AM, Tycho Andersen wrote:
quoted
+
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
+extern long seccomp_get_filter(struct task_struct *task, long n,
+ void __user *data);
+#else
+static inline long seccomp_get_filter(struct task_struct *task,
+ long n, void __user *data)
+{
+ return -EINVAL;
Nit: -ENOTSUP would probably be the better choice? -EINVAL might just
be confusing to users? (Would be unclear to them whether there's actual
support of dumping or whether it's just an invalid argument.)
Fine with me, the rest of the seccomp functions in this file use
-EINVAL, so I'm just copying that. Kees?
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request,break;}#endif++casePTRACE_SECCOMP_GET_FILTER:+ret=seccomp_get_filter(child,addr,datavp);+break;+default:break;}
@@ -867,3 +868,57 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)/* prctl interface doesn't have flags, so they are always zero. */returndo_seccomp(op,0,uargs);}++#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)+longseccomp_get_filter(structtask_struct*task,longn,void__user*data)+{+structseccomp_filter*filter;+structsock_fprog_kern*fprog;+longret;++if(n<0)+return-EINVAL;
I would probably give 'n' a better name, maybe 'filter_off' to denote an
offset in the task's filter list?
Ok, I can make that change.
So, it's called as seccomp_get_filter(child, addr, datavp), and addr is
an unsigned long in ptrace_request(). Any reasons why making this 'long n'
with adding this above check?
No, I think just an oversight; I'll switch it to unsigned.
quoted
+ spin_lock_irq(¤t->sighand->siglock);
+ if (!capable(CAP_SYS_ADMIN) ||
The capability check should probably happen before taking the task's spinlock.
We (probably) don't need the lock at all since we're just reading, but
seccomp_may_assign_mode() asserts that things are locked before it
checks current->seccomp.mode, so I lock here as well (and that's the
only reason to lock), so if we lock after, we don't need to lock at
all.
On Wed, Oct 07, 2015 at 12:34:26PM +0200, Daniel Borkmann wrote:
On 10/07/2015 12:25 PM, Daniel Borkmann wrote:
quoted
On 10/07/2015 11:46 AM, Tycho Andersen wrote:
quoted
This patch adds support for dumping a process' (classic BPF) seccomp
filters via ptrace.
PTRACE_SECCOMP_GET_FILTER allows the tracer to dump the user's classic BPF
seccomp filters. addr should be an integer which represents the ith seccomp
filter (0 is the most recently installed filter). data should be a struct
sock_filter * with enough room for the ith filter, or NULL, in which case
the filter is not saved. The return value for this command is the number of
BPF instructions the program represents, or negative in the case of errors.
A command specific error is ENOENT, which indicates that there is no ith
filter in this seccomp tree.
A caveat with this approach is that there is no way to get explicitly at
the heirarchy of seccomp filters, and users need to memcmp() filters to
decide which are inherited. This means that a task which installs two of
the same filter can potentially confuse users of this interface.
Signed-off-by: Tycho Andersen <redacted>
CC: Kees Cook <redacted>
CC: Will Drewry <wad@chromium.org>
CC: Oleg Nesterov <oleg@redhat.com>
CC: Andy Lutomirski <luto@amacapital.net>
CC: Pavel Emelyanov <redacted>
CC: Serge E. Hallyn <redacted>
CC: Alexei Starovoitov <ast@kernel.org>
CC: Daniel Borkmann <daniel@iogearbox.net>
---
include/linux/seccomp.h | 11 +++++++++
include/uapi/linux/ptrace.h | 2 ++
kernel/ptrace.c | 5 ++++
kernel/seccomp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 74 insertions(+), 1 deletion(-)
Nit: -ENOTSUP would probably be the better choice? -EINVAL might just
be confusing to users? (Would be unclear to them whether there's actual
support of dumping or whether it's just an invalid argument.)
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request,break;}#endif++casePTRACE_SECCOMP_GET_FILTER:+ret=seccomp_get_filter(child,addr,datavp);+break;+default:break;}
@@ -867,3 +868,57 @@ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter)/* prctl interface doesn't have flags, so they are always zero. */returndo_seccomp(op,0,uargs);}++#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)+longseccomp_get_filter(structtask_struct*task,longn,void__user*data)+{+structseccomp_filter*filter;+structsock_fprog_kern*fprog;+longret;++if(n<0)+return-EINVAL;
I would probably give 'n' a better name, maybe 'filter_off' to denote an
offset in the task's filter list?
So, it's called as seccomp_get_filter(child, addr, datavp), and addr is
an unsigned long in ptrace_request(). Any reasons why making this 'long n'
with adding this above check?
quoted
+ spin_lock_irq(¤t->sighand->siglock);
+ if (!capable(CAP_SYS_ADMIN) ||
The capability check should probably happen before taking the task's spinlock.
On Wed, Oct 7, 2015 at 2:46 AM, Tycho Andersen
[off-list ref] wrote:
quoted hunk
This patch adds support for dumping a process' (classic BPF) seccomp
filters via ptrace.
PTRACE_SECCOMP_GET_FILTER allows the tracer to dump the user's classic BPF
seccomp filters. addr should be an integer which represents the ith seccomp
filter (0 is the most recently installed filter). data should be a struct
sock_filter * with enough room for the ith filter, or NULL, in which case
the filter is not saved. The return value for this command is the number of
BPF instructions the program represents, or negative in the case of errors.
A command specific error is ENOENT, which indicates that there is no ith
filter in this seccomp tree.
A caveat with this approach is that there is no way to get explicitly at
the heirarchy of seccomp filters, and users need to memcmp() filters to
decide which are inherited. This means that a task which installs two of
the same filter can potentially confuse users of this interface.
Signed-off-by: Tycho Andersen <redacted>
CC: Kees Cook <redacted>
CC: Will Drewry <redacted>
CC: Oleg Nesterov <redacted>
CC: Andy Lutomirski <redacted>
CC: Pavel Emelyanov <redacted>
CC: Serge E. Hallyn <serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA@public.gmane.org>
CC: Alexei Starovoitov <redacted>
CC: Daniel Borkmann <redacted>
---
include/linux/seccomp.h | 11 +++++++++
include/uapi/linux/ptrace.h | 2 ++
kernel/ptrace.c | 5 ++++
kernel/seccomp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 74 insertions(+), 1 deletion(-)
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request,break;}#endif++casePTRACE_SECCOMP_GET_FILTER:+ret=seccomp_get_filter(child,addr,datavp);+break;+default:break;}
In thinking about this, I think we need to reverse the counter
(especially if we don't check for the process being stopped), since
subsequent calls could change which filter "0" points to. I think 0
should be the filter at the top of the tree. What do you think?
On Wed, Oct 07, 2015 at 03:18:09PM -0700, Kees Cook wrote:
On Wed, Oct 7, 2015 at 2:46 AM, Tycho Andersen
[off-list ref] wrote:
quoted
This patch adds support for dumping a process' (classic BPF) seccomp
filters via ptrace.
PTRACE_SECCOMP_GET_FILTER allows the tracer to dump the user's classic BPF
seccomp filters. addr should be an integer which represents the ith seccomp
filter (0 is the most recently installed filter). data should be a struct
sock_filter * with enough room for the ith filter, or NULL, in which case
the filter is not saved. The return value for this command is the number of
BPF instructions the program represents, or negative in the case of errors.
A command specific error is ENOENT, which indicates that there is no ith
filter in this seccomp tree.
A caveat with this approach is that there is no way to get explicitly at
the heirarchy of seccomp filters, and users need to memcmp() filters to
decide which are inherited. This means that a task which installs two of
the same filter can potentially confuse users of this interface.
Signed-off-by: Tycho Andersen <redacted>
CC: Kees Cook <redacted>
CC: Will Drewry <redacted>
CC: Oleg Nesterov <redacted>
CC: Andy Lutomirski <redacted>
CC: Pavel Emelyanov <redacted>
CC: Serge E. Hallyn <serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA@public.gmane.org>
CC: Alexei Starovoitov <redacted>
CC: Daniel Borkmann <redacted>
---
include/linux/seccomp.h | 11 +++++++++
include/uapi/linux/ptrace.h | 2 ++
kernel/ptrace.c | 5 ++++
kernel/seccomp.c | 57 ++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 74 insertions(+), 1 deletion(-)
@@ -1016,6 +1016,11 @@ int ptrace_request(struct task_struct *child, long request,break;}#endif++casePTRACE_SECCOMP_GET_FILTER:+ret=seccomp_get_filter(child,addr,datavp);+break;+default:break;}
In thinking about this, I think we need to reverse the counter
(especially if we don't check for the process being stopped), since
subsequent calls could change which filter "0" points to. I think 0
should be the filter at the top of the tree. What do you think?
The task does have to be stopped, but since I'm reversing it in
userspace anyway, and this would allow us to persist ids across
individual stops, and this would save everyone who uses this API a bit
of headache, I think that makes sense, so I'll make the change.
Any thoughts on Daniel's comments about error codes or whether or not
we need the current task's siglock?
Thanks,
Tycho
On Wed, Oct 7, 2015 at 3:25 AM, Daniel Borkmann [off-list ref] wrote:
On 10/07/2015 11:46 AM, Tycho Andersen wrote:
quoted
This patch adds support for dumping a process' (classic BPF) seccomp
filters via ptrace.
PTRACE_SECCOMP_GET_FILTER allows the tracer to dump the user's classic BPF
seccomp filters. addr should be an integer which represents the ith
seccomp
filter (0 is the most recently installed filter). data should be a struct
sock_filter * with enough room for the ith filter, or NULL, in which case
the filter is not saved. The return value for this command is the number
of
BPF instructions the program represents, or negative in the case of
errors.
A command specific error is ENOENT, which indicates that there is no ith
filter in this seccomp tree.
A caveat with this approach is that there is no way to get explicitly at
the heirarchy of seccomp filters, and users need to memcmp() filters to
decide which are inherited. This means that a task which installs two of
the same filter can potentially confuse users of this interface.
Signed-off-by: Tycho Andersen <redacted>
CC: Kees Cook <redacted>
CC: Will Drewry <redacted>
CC: Oleg Nesterov <redacted>
CC: Andy Lutomirski <redacted>
CC: Pavel Emelyanov <redacted>
CC: Serge E. Hallyn <serge.hallyn-GeWIH/nMZzLQT0dZR+AlfA@public.gmane.org>
CC: Alexei Starovoitov <redacted>
CC: Daniel Borkmann <redacted>
---
include/linux/seccomp.h | 11 +++++++++
include/uapi/linux/ptrace.h | 2 ++
kernel/ptrace.c | 5 ++++
kernel/seccomp.c | 57
++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 74 insertions(+), 1 deletion(-)
task_struct *tsk)
return;
}
#endif /* CONFIG_SECCOMP_FILTER */
+
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
+extern long seccomp_get_filter(struct task_struct *task, long n,
+ void __user *data);
+#else
+static inline long seccomp_get_filter(struct task_struct *task,
+ long n, void __user *data)
+{
+ return -EINVAL;
Nit: -ENOTSUP would probably be the better choice? -EINVAL might just
be confusing to users? (Would be unclear to them whether there's actual
support of dumping or whether it's just an invalid argument.)
I'm not sure what the convention should be here. Calling ptrace with
bad args returns -EINVAL, so distinguishing the new command being
missing vs the new command have invalid argument isn't obvious. I
don't think this should return -ENOTSUP since then we only know we've
got a kernel that is new enough to know what PTRACE_SECCOMP_GET_FILTER
is but that it was built without CONFIG_SECCOMP_FILTER and
CONFIG_CHECKPOINT_RESTORE. That's a narrow error condition, so
probably we should stick to -EINVAL.
At least for seccomp, the "is this supported" test has been to send
almost bad arguments and get an EFAULT out of it. That tells you the
feature exists.
In this case, I think -EINVAL makes sense, but I could be talked out of it.
@@ -867,3 +868,57 @@ long prctl_set_seccomp(unsigned long seccomp_mode,
char __user *filter)
/* prctl interface doesn't have flags, so they are always zero. */
return do_seccomp(op, 0, uargs);
}
+
+#if defined(CONFIG_SECCOMP_FILTER) && defined(CONFIG_CHECKPOINT_RESTORE)
+long seccomp_get_filter(struct task_struct *task, long n, void __user
*data)
+{
+ struct seccomp_filter *filter;
+ struct sock_fprog_kern *fprog;
+ long ret;
+
+ if (n < 0)
+ return -EINVAL;
I would probably give 'n' a better name, maybe 'filter_off' to denote an
offset in the task's filter list?
So, it's called as seccomp_get_filter(child, addr, datavp), and addr is
an unsigned long in ptrace_request(). Any reasons why making this 'long n'
with adding this above check?
quoted
+ spin_lock_irq(¤t->sighand->siglock);
+ if (!capable(CAP_SYS_ADMIN) ||
The capability check should probably happen before taking the task's
spinlock.
I don't have a strong opinion about this. Any races here are crazy
anyway, so I don't think there's a reason to optimize. I'd probably be
conservative and leave the spin lock as-is.