[re-send with smaller CC list]
This adds the ability for threads to request seccomp filter
synchronization across their thread group (at filter attach time).
For example, for Chrome to make sure graphic driver threads are fully
confined after seccomp filters have been attached.
To support this, locking on seccomp changes is introduced, along with
refactoring of no_new_privs. Races with thread creation/death are handled
via tasklist_lock.
This includes a new syscall (instead of adding a new prctl option),
as suggested by Andy Lutomirski and Michael Kerrisk.
Thanks!
-Kees
v6:
- switch from seccomp-specific lock to thread-group lock to gain atomicity
- implement seccomp syscall across all architectures with seccomp filter
- clean up sparse warnings around locking
v5:
- move includes around (drysdale)
- drop set_nnp return value (luto)
- use smp_load_acquire/store_release (luto)
- merge nnp changes to seccomp always, fewer ifdef (luto)
v4:
- cleaned up locking further, as noticed by David Drysdale
v3:
- added SECCOMP_EXT_ACT_FILTER for new filter install options
v2:
- reworked to avoid clone races
In preparation for having other callers of the seccomp mode setting
logic, split the prctl entry point away from the core logic that performs
seccomp mode setting.
Signed-off-by: Kees Cook <redacted>
---
kernel/seccomp.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
Signed-off-by: Kees Cook <redacted>
Cc: linux-api@vger.kernel.org
---
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 1 +
include/linux/syscalls.h | 2 ++
include/uapi/asm-generic/unistd.h | 4 ++-
include/uapi/linux/seccomp.h | 4 +++
kernel/seccomp.c | 63 ++++++++++++++++++++++++++++++++-----
kernel/sys_ni.c | 3 ++
7 files changed, 69 insertions(+), 9 deletions(-)
@@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2+317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -569,7 +578,7 @@ static long seccomp_set_mode_filter(char __user *filter)if(!seccomp_check_mode(current,seccomp_mode))gotoout;-ret=seccomp_attach_filter(prepared);+ret=seccomp_attach_filter(flags,prepared);if(ret)gotoout;/* Do not free the successfully attached filter. */
@@ -583,12 +592,35 @@ out_free:returnret;}#else-staticinlinelongseccomp_set_mode_filter(char__user*filter)+staticinlinelongseccomp_set_mode_filter(unsignedintflags,+constchar__user*filter){return-EINVAL;}#endif+/* Common entry point for both prctl and syscall. */+staticlongdo_seccomp(unsignedintop,unsignedintflags,+constchar__user*uargs)+{+switch(op){+caseSECCOMP_SET_MODE_STRICT:+if(flags!=0||uargs!=NULL)+return-EINVAL;+returnseccomp_set_mode_strict();+caseSECCOMP_SET_MODE_FILTER:+returnseccomp_set_mode_filter(flags,uargs);+default:+return-EINVAL;+}+}++SYSCALL_DEFINE3(seccomp,unsignedint,op,unsignedint,flags,+constchar__user*,uargs)+{+returndo_seccomp(op,flags,uargs);+}+/***prctl_set_seccomp:configurescurrent->seccomp.mode*@seccomp_mode:requestedmodetouse
@@ -598,12 +630,27 @@ static inline long seccomp_set_mode_filter(char __user *filter)*/longprctl_set_seccomp(unsignedlongseccomp_mode,char__user*filter){+unsignedintop;+char__user*uargs;+switch(seccomp_mode){caseSECCOMP_MODE_STRICT:-returnseccomp_set_mode_strict();+op=SECCOMP_SET_MODE_STRICT;+/*+*Settingstrictmodethroughprctlalwaysignoredfilter,+*somakesureitisalwaysNULLheretopasstheinternal+*checkindo_seccomp().+*/+uargs=NULL;+break;caseSECCOMP_MODE_FILTER:-returnseccomp_set_mode_filter(filter);+op=SECCOMP_SET_MODE_FILTER;+uargs=filter;+break;default:return-EINVAL;}++/* prctl interface doesn't have flags, so they are always zero. */+returndo_seccomp(op,0,uargs);}
From: Andy Lutomirski <luto@amacapital.net> Date: 2014-06-13 20:41:25
On Tue, Jun 10, 2014 at 8:25 PM, Kees Cook [off-list ref] wrote:
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
Question for the linux-abi people:
What's the preferred way to do this these days? This syscall is a
general purpose "adjust the seccomp state" thing. The alternative
would be a specific new syscall to add a filter with a flags argument.
--Andy
On Tue, Jun 10, 2014 at 8:25 PM, Kees Cook [off-list ref] wrote:
quoted hunk
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
Signed-off-by: Kees Cook <redacted>
Cc: linux-api@vger.kernel.org
---
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 1 +
include/linux/syscalls.h | 2 ++
include/uapi/asm-generic/unistd.h | 4 ++-
include/uapi/linux/seccomp.h | 4 +++
kernel/seccomp.c | 63 ++++++++++++++++++++++++++++++++-----
kernel/sys_ni.c | 3 ++
7 files changed, 69 insertions(+), 9 deletions(-)
@@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2+317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid,asmlinkagelongsys_kcmp(pid_tpid1,pid_tpid2,inttype,unsignedlongidx1,unsignedlongidx2);asmlinkagelongsys_finit_module(intfd,constchar__user*uargs,intflags);+asmlinkagelongsys_seccomp(unsignedintop,unsignedintflags,+constchar__user*uargs);
It looks odd to add 'flags' argument to syscall that is not even used.
It don't think it will be extensible this way.
'uargs' is used only in 2nd command as well and it's not 'char __user *'
but rather 'struct sock_fprog __user *'
I think it makes more sense to define only first argument as 'int op' and the
rest as variable length array.
Something like:
long sys_seccomp(unsigned int op, struct nlattr *attrs, int len);
then different commands can interpret 'attrs' differently.
if op == mode_strict, then attrs == NULL, len == 0
if op == mode_filter, then attrs->nla_type == seccomp_bpf_filter
and nla_data(attrs) is 'struct sock_fprog'
If we decide to add new types of filters or new commands, the syscall prototype
won't need to change. New commands can be added preserving backward
compatibility.
The basic TLV concept has been around forever in netlink world. imo makes
sense to use it with new syscalls. Passing 'struct xxx' into syscalls
is the thing
of the past. TLV style is more extensible. Fields of structures can become
optional in the future, new fields added, etc.
'struct nlattr' brings the same benefits to kernel api as protobuf did
to user land.
@@ -569,7 +578,7 @@ static long seccomp_set_mode_filter(char __user *filter)if(!seccomp_check_mode(current,seccomp_mode))gotoout;-ret=seccomp_attach_filter(prepared);+ret=seccomp_attach_filter(flags,prepared);if(ret)gotoout;/* Do not free the successfully attached filter. */
@@ -583,12 +592,35 @@ out_free:returnret;}#else-staticinlinelongseccomp_set_mode_filter(char__user*filter)+staticinlinelongseccomp_set_mode_filter(unsignedintflags,+constchar__user*filter){return-EINVAL;}#endif+/* Common entry point for both prctl and syscall. */+staticlongdo_seccomp(unsignedintop,unsignedintflags,+constchar__user*uargs)+{+switch(op){+caseSECCOMP_SET_MODE_STRICT:+if(flags!=0||uargs!=NULL)+return-EINVAL;+returnseccomp_set_mode_strict();+caseSECCOMP_SET_MODE_FILTER:+returnseccomp_set_mode_filter(flags,uargs);+default:+return-EINVAL;+}+}++SYSCALL_DEFINE3(seccomp,unsignedint,op,unsignedint,flags,+constchar__user*,uargs)+{+returndo_seccomp(op,flags,uargs);+}+/***prctl_set_seccomp:configurescurrent->seccomp.mode*@seccomp_mode:requestedmodetouse
@@ -598,12 +630,27 @@ static inline long seccomp_set_mode_filter(char __user *filter)*/longprctl_set_seccomp(unsignedlongseccomp_mode,char__user*filter){+unsignedintop;+char__user*uargs;+switch(seccomp_mode){caseSECCOMP_MODE_STRICT:-returnseccomp_set_mode_strict();+op=SECCOMP_SET_MODE_STRICT;+/*+*Settingstrictmodethroughprctlalwaysignoredfilter,+*somakesureitisalwaysNULLheretopasstheinternal+*checkindo_seccomp().+*/+uargs=NULL;+break;caseSECCOMP_MODE_FILTER:-returnseccomp_set_mode_filter(filter);+op=SECCOMP_SET_MODE_FILTER;+uargs=filter;+break;default:return-EINVAL;}++/* prctl interface doesn't have flags, so they are always zero. */+returndo_seccomp(op,0,uargs);}
From: Andy Lutomirski <luto@amacapital.net> Date: 2014-06-13 21:26:00
On Fri, Jun 13, 2014 at 2:22 PM, Alexei Starovoitov [off-list ref] wrote:
On Tue, Jun 10, 2014 at 8:25 PM, Kees Cook [off-list ref] wrote:
quoted
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
Signed-off-by: Kees Cook <redacted>
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 1 +
include/linux/syscalls.h | 2 ++
include/uapi/asm-generic/unistd.h | 4 ++-
include/uapi/linux/seccomp.h | 4 +++
kernel/seccomp.c | 63 ++++++++++++++++++++++++++++++++-----
kernel/sys_ni.c | 3 ++
7 files changed, 69 insertions(+), 9 deletions(-)
@@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2+317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid,asmlinkagelongsys_kcmp(pid_tpid1,pid_tpid2,inttype,unsignedlongidx1,unsignedlongidx2);asmlinkagelongsys_finit_module(intfd,constchar__user*uargs,intflags);+asmlinkagelongsys_seccomp(unsignedintop,unsignedintflags,+constchar__user*uargs);
It looks odd to add 'flags' argument to syscall that is not even used.
It don't think it will be extensible this way.
'uargs' is used only in 2nd command as well and it's not 'char __user *'
but rather 'struct sock_fprog __user *'
I think it makes more sense to define only first argument as 'int op' and the
rest as variable length array.
Something like:
long sys_seccomp(unsigned int op, struct nlattr *attrs, int len);
then different commands can interpret 'attrs' differently.
if op == mode_strict, then attrs == NULL, len == 0
if op == mode_filter, then attrs->nla_type == seccomp_bpf_filter
and nla_data(attrs) is 'struct sock_fprog'
Eww. If the operation doesn't imply the type, then I think we've
totally screwed up.
If we decide to add new types of filters or new commands, the syscall prototype
won't need to change. New commands can be added preserving backward
compatibility.
The basic TLV concept has been around forever in netlink world. imo makes
sense to use it with new syscalls. Passing 'struct xxx' into syscalls
is the thing
of the past. TLV style is more extensible. Fields of structures can become
optional in the future, new fields added, etc.
'struct nlattr' brings the same benefits to kernel api as protobuf did
to user land.
I see no reason to bring nl_attr into this.
Admittedly, I've never dealt with nl_attr, but everything
netlink-related I've even been involved in has involved some sort of
API atrocity.
--Andy
On Fri, Jun 13, 2014 at 2:25 PM, Andy Lutomirski [off-list ref] wrote:
On Fri, Jun 13, 2014 at 2:22 PM, Alexei Starovoitov [off-list ref] wrote:
quoted
On Tue, Jun 10, 2014 at 8:25 PM, Kees Cook [off-list ref] wrote:
quoted
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
Signed-off-by: Kees Cook <redacted>
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 1 +
include/linux/syscalls.h | 2 ++
include/uapi/asm-generic/unistd.h | 4 ++-
include/uapi/linux/seccomp.h | 4 +++
kernel/seccomp.c | 63 ++++++++++++++++++++++++++++++++-----
kernel/sys_ni.c | 3 ++
7 files changed, 69 insertions(+), 9 deletions(-)
@@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2+317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid,asmlinkagelongsys_kcmp(pid_tpid1,pid_tpid2,inttype,unsignedlongidx1,unsignedlongidx2);asmlinkagelongsys_finit_module(intfd,constchar__user*uargs,intflags);+asmlinkagelongsys_seccomp(unsignedintop,unsignedintflags,+constchar__user*uargs);
It looks odd to add 'flags' argument to syscall that is not even used.
It don't think it will be extensible this way.
'uargs' is used only in 2nd command as well and it's not 'char __user *'
but rather 'struct sock_fprog __user *'
I think it makes more sense to define only first argument as 'int op' and the
rest as variable length array.
Something like:
long sys_seccomp(unsigned int op, struct nlattr *attrs, int len);
then different commands can interpret 'attrs' differently.
if op == mode_strict, then attrs == NULL, len == 0
if op == mode_filter, then attrs->nla_type == seccomp_bpf_filter
and nla_data(attrs) is 'struct sock_fprog'
Eww. If the operation doesn't imply the type, then I think we've
totally screwed up.
quoted
If we decide to add new types of filters or new commands, the syscall prototype
won't need to change. New commands can be added preserving backward
compatibility.
The basic TLV concept has been around forever in netlink world. imo makes
sense to use it with new syscalls. Passing 'struct xxx' into syscalls
is the thing
of the past. TLV style is more extensible. Fields of structures can become
optional in the future, new fields added, etc.
'struct nlattr' brings the same benefits to kernel api as protobuf did
to user land.
I see no reason to bring nl_attr into this.
Admittedly, I've never dealt with nl_attr, but everything
netlink-related I've even been involved in has involved some sort of
API atrocity.
netlink has a lot of legacy and there is genetlink which is not pretty
either because of extra socket creation, binding, dealing with packet
loss issues, but the key concept of variable length encoding is sound.
Right now seccomp has two commands and they already don't fit
into single syscall neatly. Are you saying there should be two syscalls
here? What about another seccomp related command? Another syscall?
imo all seccomp related commands needs to be mux/demux-ed under
one syscall. What is the way to mux/demux potentially very different
commands under one syscall? I cannot think of anything better than
TLV style. 'struct nlattr' is what we have today and I think it works fine.
I'm not suggesting to bring the whole netlink into the picture, but rather
TLV style of encoding different arguments for different commands.
From: Andy Lutomirski <luto@amacapital.net> Date: 2014-06-13 21:42:27
On Fri, Jun 13, 2014 at 2:37 PM, Alexei Starovoitov [off-list ref] wrote:
On Fri, Jun 13, 2014 at 2:25 PM, Andy Lutomirski [off-list ref] wrote:
quoted
On Fri, Jun 13, 2014 at 2:22 PM, Alexei Starovoitov [off-list ref] wrote:
quoted
On Tue, Jun 10, 2014 at 8:25 PM, Kees Cook [off-list ref] wrote:
quoted
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
Signed-off-by: Kees Cook <redacted>
Cc: linux-api@vger.kernel.org
---
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 1 +
include/linux/syscalls.h | 2 ++
include/uapi/asm-generic/unistd.h | 4 ++-
include/uapi/linux/seccomp.h | 4 +++
kernel/seccomp.c | 63 ++++++++++++++++++++++++++++++++-----
kernel/sys_ni.c | 3 ++
7 files changed, 69 insertions(+), 9 deletions(-)
@@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2+317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid,asmlinkagelongsys_kcmp(pid_tpid1,pid_tpid2,inttype,unsignedlongidx1,unsignedlongidx2);asmlinkagelongsys_finit_module(intfd,constchar__user*uargs,intflags);+asmlinkagelongsys_seccomp(unsignedintop,unsignedintflags,+constchar__user*uargs);
It looks odd to add 'flags' argument to syscall that is not even used.
It don't think it will be extensible this way.
'uargs' is used only in 2nd command as well and it's not 'char __user *'
but rather 'struct sock_fprog __user *'
I think it makes more sense to define only first argument as 'int op' and the
rest as variable length array.
Something like:
long sys_seccomp(unsigned int op, struct nlattr *attrs, int len);
then different commands can interpret 'attrs' differently.
if op == mode_strict, then attrs == NULL, len == 0
if op == mode_filter, then attrs->nla_type == seccomp_bpf_filter
and nla_data(attrs) is 'struct sock_fprog'
Eww. If the operation doesn't imply the type, then I think we've
totally screwed up.
quoted
If we decide to add new types of filters or new commands, the syscall prototype
won't need to change. New commands can be added preserving backward
compatibility.
The basic TLV concept has been around forever in netlink world. imo makes
sense to use it with new syscalls. Passing 'struct xxx' into syscalls
is the thing
of the past. TLV style is more extensible. Fields of structures can become
optional in the future, new fields added, etc.
'struct nlattr' brings the same benefits to kernel api as protobuf did
to user land.
I see no reason to bring nl_attr into this.
Admittedly, I've never dealt with nl_attr, but everything
netlink-related I've even been involved in has involved some sort of
API atrocity.
netlink has a lot of legacy and there is genetlink which is not pretty
either because of extra socket creation, binding, dealing with packet
loss issues, but the key concept of variable length encoding is sound.
Right now seccomp has two commands and they already don't fit
into single syscall neatly. Are you saying there should be two syscalls
here? What about another seccomp related command? Another syscall?
imo all seccomp related commands needs to be mux/demux-ed under
one syscall. What is the way to mux/demux potentially very different
commands under one syscall? I cannot think of anything better than
TLV style. 'struct nlattr' is what we have today and I think it works fine.
I'm not suggesting to bring the whole netlink into the picture, but rather
TLV style of encoding different arguments for different commands.
I'm unconvinced. These are simple commands, and I think the interface
should be simple. Syscalls are cheap.
As an example, the interface could be:
int seccomp_add_filter(const struct sock_fprog *filter, unsigned int flags);
The "tsync" operation would be seccomp_add_filter(NULL,
SECCOMP_ADD_FILTER_TSYNC) -- it's equivalent to adding an
always-accept filter and syncing threads.
But, frankly, this kind of stuff should probably be "do operation X".
IIUC nl_attr is more like "do something, with these tags and values",
which results in oddities like whatever should happen of more than one
tag is set.
--Andy
--
Andy Lutomirski
AMA Capital Management, LLC
On Fri, Jun 13, 2014 at 2:42 PM, Andy Lutomirski [off-list ref] wrote:
On Fri, Jun 13, 2014 at 2:37 PM, Alexei Starovoitov [off-list ref] wrote:
quoted
On Fri, Jun 13, 2014 at 2:25 PM, Andy Lutomirski [off-list ref] wrote:
quoted
On Fri, Jun 13, 2014 at 2:22 PM, Alexei Starovoitov [off-list ref] wrote:
quoted
On Tue, Jun 10, 2014 at 8:25 PM, Kees Cook [off-list ref] wrote:
quoted
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
Signed-off-by: Kees Cook <redacted>
Cc: linux-api@vger.kernel.org
---
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 1 +
include/linux/syscalls.h | 2 ++
include/uapi/asm-generic/unistd.h | 4 ++-
include/uapi/linux/seccomp.h | 4 +++
kernel/seccomp.c | 63 ++++++++++++++++++++++++++++++++-----
kernel/sys_ni.c | 3 ++
7 files changed, 69 insertions(+), 9 deletions(-)
@@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2+317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid,asmlinkagelongsys_kcmp(pid_tpid1,pid_tpid2,inttype,unsignedlongidx1,unsignedlongidx2);asmlinkagelongsys_finit_module(intfd,constchar__user*uargs,intflags);+asmlinkagelongsys_seccomp(unsignedintop,unsignedintflags,+constchar__user*uargs);
It looks odd to add 'flags' argument to syscall that is not even used.
It don't think it will be extensible this way.
'uargs' is used only in 2nd command as well and it's not 'char __user *'
but rather 'struct sock_fprog __user *'
I think it makes more sense to define only first argument as 'int op' and the
rest as variable length array.
Something like:
long sys_seccomp(unsigned int op, struct nlattr *attrs, int len);
then different commands can interpret 'attrs' differently.
if op == mode_strict, then attrs == NULL, len == 0
if op == mode_filter, then attrs->nla_type == seccomp_bpf_filter
and nla_data(attrs) is 'struct sock_fprog'
Eww. If the operation doesn't imply the type, then I think we've
totally screwed up.
quoted
If we decide to add new types of filters or new commands, the syscall prototype
won't need to change. New commands can be added preserving backward
compatibility.
The basic TLV concept has been around forever in netlink world. imo makes
sense to use it with new syscalls. Passing 'struct xxx' into syscalls
is the thing
of the past. TLV style is more extensible. Fields of structures can become
optional in the future, new fields added, etc.
'struct nlattr' brings the same benefits to kernel api as protobuf did
to user land.
I see no reason to bring nl_attr into this.
Admittedly, I've never dealt with nl_attr, but everything
netlink-related I've even been involved in has involved some sort of
API atrocity.
netlink has a lot of legacy and there is genetlink which is not pretty
either because of extra socket creation, binding, dealing with packet
loss issues, but the key concept of variable length encoding is sound.
Right now seccomp has two commands and they already don't fit
into single syscall neatly. Are you saying there should be two syscalls
here? What about another seccomp related command? Another syscall?
imo all seccomp related commands needs to be mux/demux-ed under
one syscall. What is the way to mux/demux potentially very different
commands under one syscall? I cannot think of anything better than
TLV style. 'struct nlattr' is what we have today and I think it works fine.
I'm not suggesting to bring the whole netlink into the picture, but rather
TLV style of encoding different arguments for different commands.
I'm unconvinced. These are simple commands, and I think the interface
should be simple. Syscalls are cheap.
As an example, the interface could be:
int seccomp_add_filter(const struct sock_fprog *filter, unsigned int flags);
The "tsync" operation would be seccomp_add_filter(NULL,
SECCOMP_ADD_FILTER_TSYNC) -- it's equivalent to adding an
always-accept filter and syncing threads.
I think you convinced me that tsync should be part of adding a filter
(since now there are no failure side-effects), so this specific
example I would expect EFAULT from. But ...
But, frankly, this kind of stuff should probably be "do operation X".
IIUC nl_attr is more like "do something, with these tags and values",
which results in oddities like whatever should happen of more than one
tag is set.
I have no objection to eliminating seccomp-strict from the syscall,
and just making this the "add seccomp filter" syscall. My only
hesitation would be that if we need something besides adding a filter
in the future, we'd be back to extending this awkwardly or adding
another syscall. That's why I went with the "operation" argument. I'm
not opposed to passing attrs and len, but seccomp_add_filter does feel
cleaner.
-Kees
--
Kees Cook
Chrome OS Security
On Fri, Jun 13, 2014 at 2:42 PM, Andy Lutomirski [off-list ref] wrote:
On Fri, Jun 13, 2014 at 2:37 PM, Alexei Starovoitov [off-list ref] wrote:
quoted
On Fri, Jun 13, 2014 at 2:25 PM, Andy Lutomirski [off-list ref] wrote:
quoted
On Fri, Jun 13, 2014 at 2:22 PM, Alexei Starovoitov [off-list ref] wrote:
quoted
On Tue, Jun 10, 2014 at 8:25 PM, Kees Cook [off-list ref] wrote:
quoted
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
Signed-off-by: Kees Cook <redacted>
Cc: linux-api@vger.kernel.org
---
arch/x86/syscalls/syscall_32.tbl | 1 +
arch/x86/syscalls/syscall_64.tbl | 1 +
include/linux/syscalls.h | 2 ++
include/uapi/asm-generic/unistd.h | 4 ++-
include/uapi/linux/seccomp.h | 4 +++
kernel/seccomp.c | 63 ++++++++++++++++++++++++++++++++-----
kernel/sys_ni.c | 3 ++
7 files changed, 69 insertions(+), 9 deletions(-)
@@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2+317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid,asmlinkagelongsys_kcmp(pid_tpid1,pid_tpid2,inttype,unsignedlongidx1,unsignedlongidx2);asmlinkagelongsys_finit_module(intfd,constchar__user*uargs,intflags);+asmlinkagelongsys_seccomp(unsignedintop,unsignedintflags,+constchar__user*uargs);
It looks odd to add 'flags' argument to syscall that is not even used.
It don't think it will be extensible this way.
'uargs' is used only in 2nd command as well and it's not 'char __user *'
but rather 'struct sock_fprog __user *'
I think it makes more sense to define only first argument as 'int op' and the
rest as variable length array.
Something like:
long sys_seccomp(unsigned int op, struct nlattr *attrs, int len);
then different commands can interpret 'attrs' differently.
if op == mode_strict, then attrs == NULL, len == 0
if op == mode_filter, then attrs->nla_type == seccomp_bpf_filter
and nla_data(attrs) is 'struct sock_fprog'
Eww. If the operation doesn't imply the type, then I think we've
totally screwed up.
quoted
If we decide to add new types of filters or new commands, the syscall prototype
won't need to change. New commands can be added preserving backward
compatibility.
The basic TLV concept has been around forever in netlink world. imo makes
sense to use it with new syscalls. Passing 'struct xxx' into syscalls
is the thing
of the past. TLV style is more extensible. Fields of structures can become
optional in the future, new fields added, etc.
'struct nlattr' brings the same benefits to kernel api as protobuf did
to user land.
I see no reason to bring nl_attr into this.
Admittedly, I've never dealt with nl_attr, but everything
netlink-related I've even been involved in has involved some sort of
API atrocity.
netlink has a lot of legacy and there is genetlink which is not pretty
either because of extra socket creation, binding, dealing with packet
loss issues, but the key concept of variable length encoding is sound.
Right now seccomp has two commands and they already don't fit
into single syscall neatly. Are you saying there should be two syscalls
here? What about another seccomp related command? Another syscall?
imo all seccomp related commands needs to be mux/demux-ed under
one syscall. What is the way to mux/demux potentially very different
commands under one syscall? I cannot think of anything better than
TLV style. 'struct nlattr' is what we have today and I think it works fine.
I'm not suggesting to bring the whole netlink into the picture, but rather
TLV style of encoding different arguments for different commands.
I'm unconvinced. These are simple commands, and I think the interface
should be simple. Syscalls are cheap.
As an example, the interface could be:
int seccomp_add_filter(const struct sock_fprog *filter, unsigned int flags);
The "tsync" operation would be seccomp_add_filter(NULL,
SECCOMP_ADD_FILTER_TSYNC) -- it's equivalent to adding an
always-accept filter and syncing threads.
But, frankly, this kind of stuff should probably be "do operation X".
IIUC nl_attr is more like "do something, with these tags and values",
which results in oddities like whatever should happen of more than one
tag is set.
TLV is a price of extensibility vs simplicity.
Say we have a syscall_foo(struct XX __user *x); that takes
struct XX {
int flag;
int var1;
};
now we want to add another variable to the structure that will be used
only when certain flag is set. You cannot do it easily without
breaking old user binaries, since new structure will be:
struct XX2 {
int flag;
int var1;
int var2;
};
if we do copy_from_user(,, sizeof(struct XX2)) it will brake old programs.
Potentially we can do it the ugly way:
copy_from_user(,, sizeof(struct XX)), then check flag and do another
copy_from_user(,, sizeof(struct XX2)) just to fetch extra argument.
but then another day passes and yet another new flag means that both
var1 and var2 are unused and it needs 'u64 var3' instead.
Now kernel looks very ugly by doing multiple copy_from_user() of different
structures.
It would be much cleaner with nlattr:
syscall_foo(struct nlattr *attrs, int len);
kernel fetches 'len' bytes onces and then picks var[123] fields from nlattr
array. In other words nlattr array is a way to represent flexible structure
where fields can come and go in the future.
This was a simple example. Consider the case where var1 and var2
are arrays of things.
imo the old:
struct sock_fprog {
unsigned short len; /* Number of filter blocks */
struct sock_filter __user *filter;
};
is the example of inflexible user interface.
It could have been single 'struct nlattr'
nlattr->nla_len == length of filter program in bytes
nlattr->nla_type == ARRAY_OF_SOCK_FILTER constant
nla_data(nlattr) - variable length array of 'struct sock_filter' bpf
instructions.
Right now I'd like to extend it for the work I'm doing around eBPF, but
I cannot, since it's rigid. If it was TLV, I could have easily added new flags,
new types, new sections to bpf programs.
For user space it would have been just as easy to populate such
'struct nlattr' as to populate 'struct sock_fprog'.
On Fri, Jun 13, 2014 at 2:22 PM, Alexei Starovoitov [off-list ref] wrote:
On Tue, Jun 10, 2014 at 8:25 PM, Kees Cook [off-list ref] wrote:
quoted
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
Signed-off-by: Kees Cook <redacted>
Cc: linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
From: Michael Kerrisk (man-pages) <hidden> Date: 2014-06-16 06:09:05
Hi Kees,
On Wed, Jun 11, 2014 at 5:25 AM, Kees Cook [off-list ref] wrote:
This adds the new "seccomp" syscall with both an "operation" and "flags"
parameter for future expansion. The third argument is a pointer value,
used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must
be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...).
I assume there'll be another iteration of these patches. With that
next iteration, could you write a man page (or at least free text
structured like a man page) that comprehensively documents the
user-space API?
Thanks,
Michael
@@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2+317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact
@@ -569,7 +578,7 @@ static long seccomp_set_mode_filter(char __user *filter)if(!seccomp_check_mode(current,seccomp_mode))gotoout;-ret=seccomp_attach_filter(prepared);+ret=seccomp_attach_filter(flags,prepared);if(ret)gotoout;/* Do not free the successfully attached filter. */
@@ -583,12 +592,35 @@ out_free:returnret;}#else-staticinlinelongseccomp_set_mode_filter(char__user*filter)+staticinlinelongseccomp_set_mode_filter(unsignedintflags,+constchar__user*filter){return-EINVAL;}#endif+/* Common entry point for both prctl and syscall. */+staticlongdo_seccomp(unsignedintop,unsignedintflags,+constchar__user*uargs)+{+switch(op){+caseSECCOMP_SET_MODE_STRICT:+if(flags!=0||uargs!=NULL)+return-EINVAL;+returnseccomp_set_mode_strict();+caseSECCOMP_SET_MODE_FILTER:+returnseccomp_set_mode_filter(flags,uargs);+default:+return-EINVAL;+}+}++SYSCALL_DEFINE3(seccomp,unsignedint,op,unsignedint,flags,+constchar__user*,uargs)+{+returndo_seccomp(op,flags,uargs);+}+/***prctl_set_seccomp:configurescurrent->seccomp.mode*@seccomp_mode:requestedmodetouse
@@ -598,12 +630,27 @@ static inline long seccomp_set_mode_filter(char __user *filter)*/longprctl_set_seccomp(unsignedlongseccomp_mode,char__user*filter){+unsignedintop;+char__user*uargs;+switch(seccomp_mode){caseSECCOMP_MODE_STRICT:-returnseccomp_set_mode_strict();+op=SECCOMP_SET_MODE_STRICT;+/*+*Settingstrictmodethroughprctlalwaysignoredfilter,+*somakesureitisalwaysNULLheretopasstheinternal+*checkindo_seccomp().+*/+uargs=NULL;+break;caseSECCOMP_MODE_FILTER:-returnseccomp_set_mode_filter(filter);+op=SECCOMP_SET_MODE_FILTER;+uargs=filter;+break;default:return-EINVAL;}++/* prctl interface doesn't have flags, so they are always zero. */+returndo_seccomp(op,0,uargs);}
@@ -213,3 +213,6 @@ cond_syscall(compat_sys_open_by_handle_at);/* compare kernel pointers */cond_syscall(sys_kcmp);++/* operate on Secure Computing state */+cond_syscall(sys_seccomp);--
1.7.9.5
--
To unsubscribe from this list: send the line "unsubscribe linux-api" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Normally, task_struct.seccomp.filter is only ever read or modified by
the task that owns it (current). This property aids in fast access
during system call filtering as read access is lockless.
Updating the pointer from another task, however, opens up race
conditions. To allow cross-thread filter pointer updates, writes to
the seccomp fields are now protected by the sighand spinlock (which
is unique to the thread group). Read access remains lockless because
pointer updates themselves are atomic. However, writes (or cloning)
often entail additional checking (like maximum instruction counts)
which require locking to perform safely.
In the case of cloning threads, the child is invisible to the system
until it enters the task list. To make sure a child can't be cloned from
a thread and left in a prior state, seccomp duplication is additionally
moved under the tasklist_lock. Then parent and child are certain have
the same seccomp state when they exit the lock.
Based on patches by Will Drewry and David Drysdale.
Signed-off-by: Kees Cook <redacted>
---
include/linux/seccomp.h | 6 +++---
kernel/fork.c | 40 ++++++++++++++++++++++++++++++++++++----
kernel/seccomp.c | 22 ++++++++++++++++------
3 files changed, 55 insertions(+), 13 deletions(-)
@@ -324,7 +328,7 @@ static long seccomp_attach_filter(struct seccomp_filter *filter)*taskreference.*/filter->prev=current->seccomp.filter;-current->seccomp.filter=filter;+smp_store_release(¤t->seccomp.filter,filter);return0;}
@@ -332,7 +336,7 @@ static long seccomp_attach_filter(struct seccomp_filter *filter)/* get_seccomp_filter - increments the reference count of the filter on @tsk */voidget_seccomp_filter(structtask_struct*tsk){-structseccomp_filter*orig=tsk->seccomp.filter;+structseccomp_filter*orig=smp_load_acquire(&tsk->seccomp.filter);if(!orig)return;/* Reference count is bounded by the number of total processes. */
Applying restrictive seccomp filter programs to large or diverse
codebases often requires handling threads which may be started early in
the process lifetime (e.g., by code that is linked in). While it is
possible to apply permissive programs prior to process start up, it is
difficult to further restrict the kernel ABI to those threads after that
point.
This change adds a new seccomp syscall flag to SECCOMP_SET_MODE_FILTER for
synchronizing thread group seccomp filters at filter installation time.
When calling seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC,
filter) an attempt will be made to synchronize all threads in current's
threadgroup to its new seccomp filter program. This is possible iff all
threads are using a filter that is an ancestor to the filter current is
attempting to synchronize to. NULL filters (where the task is running as
SECCOMP_MODE_NONE) are also treated as ancestors allowing threads to be
transitioned into SECCOMP_MODE_FILTER. If prctrl(PR_SET_NO_NEW_PRIVS,
...) has been set on the calling thread, no_new_privs will be set for
all synchronized threads too. On success, 0 is returned. On failure,
the pid of one of the failing threads will be returned and no filters
will have been applied.
The race conditions are against another thread requesting TSYNC, another
thread performing a clone, and another thread changing its filter. The
sighand lock is sufficient for these cases, though the clone case is
assisted by the tasklist_lock so that new threads must have a duplicate
of its parent seccomp state when it appears on the tasklist.
Based on patches by Will Drewry.
Suggested-by: Julien Tinnes <redacted>
Signed-off-by: Kees Cook <redacted>
---
arch/Kconfig | 1 +
include/uapi/linux/seccomp.h | 4 ++
kernel/seccomp.c | 135 +++++++++++++++++++++++++++++++++++++++++-
3 files changed, 138 insertions(+), 2 deletions(-)
@@ -219,6 +220,107 @@ static inline void seccomp_assign_mode(struct task_struct *task,}#ifdef CONFIG_SECCOMP_FILTER+/* Returns 1 if the candidate is an ancestor. */+staticintis_ancestor(structseccomp_filter*candidate,+structseccomp_filter*child)+{+/* NULL is the root ancestor. */+if(candidate==NULL)+return1;+for(;child;child=child->prev)+if(child==candidate)+return1;+return0;+}++/**+*seccomp_can_sync_threads:checksifallthreadscanbesynchronized+*+*Expectsbothtasklist_lockandcurrent->sighand->siglocktobeheld.+*+*Returns0onsuccess,-veonerror,orthepidofathreadwhichwas+*eithernotinthecorrectseccompmodeoritdidnothaveanancestral+*seccompfilter.+*/+staticpid_tseccomp_can_sync_threads(void)+{+structtask_struct*thread,*caller;++BUG_ON(write_can_lock(&tasklist_lock));+BUG_ON(!spin_is_locked(¤t->sighand->siglock));++if(current->seccomp.mode!=SECCOMP_MODE_FILTER)+return-EACCES;++/* Validate all threads being eligible for synchronization. */+thread=caller=current;+for_each_thread(caller,thread){+pid_tfailed;++if(thread->seccomp.mode==SECCOMP_MODE_DISABLED||+(thread->seccomp.mode==SECCOMP_MODE_FILTER&&+is_ancestor(thread->seccomp.filter,+caller->seccomp.filter)))+continue;++/* Return the first thread that cannot be synchronized. */+failed=task_pid_vnr(thread);+/* If the pid cannot be resolved, then return -ESRCH */+if(failed==0)+failed=-ESRCH;+returnfailed;+}++return0;+}++/**+*seccomp_sync_threads:setsallthreadstousecurrent'sfilter+*+*Expectsbothtasklist_lockandcurrent->sighand->siglocktobeheld,+*andforseccomp_can_sync_threads()tohavereturnedsuccessalready+*withoutdroppingthelocks.+*+*/+staticvoidseccomp_sync_threads(void)+{+structtask_struct*thread,*caller;++BUG_ON(write_can_lock(&tasklist_lock));+BUG_ON(!spin_is_locked(¤t->sighand->siglock));++/* Synchronize all threads. */+thread=caller=current;+for_each_thread(caller,thread){+/* Get a task reference for the new leaf node. */+get_seccomp_filter(caller);+/*+*Dropthetaskreferencetothesharedancestorsince+*current'spathwillholdareference.(Thisalso+*allowsaputbeforetheassignment.)+*/+put_seccomp_filter(thread);+thread->seccomp.filter=caller->seccomp.filter;+/* Opt the other thread into seccomp if needed.+*Asthreadsareconsideredtobetrust-realm+*equivalent(seeptrace_may_access),itissafeto+*allowonethreadtotransitiontheother.+*/+if(thread->seccomp.mode==SECCOMP_MODE_DISABLED){+/*+*Don'tletanunprivilegedtaskworkaround+*theno_new_privsrestrictionbycreating+*athreadthatsetsitup,entersseccomp,+*thendies.+*/+if(task_no_new_privs(caller))+task_set_no_new_privs(thread);++seccomp_assign_mode(thread,SECCOMP_MODE_FILTER);+}+}+}+/***seccomp_prepare_filter:Preparesaseccompfilterforuse.*@fprog:BPFprogramtoinstall
@@ -342,7 +444,7 @@ static long seccomp_attach_filter(unsigned int flags,BUG_ON(!spin_is_locked(¤t->sighand->siglock));/* Validate flags. */-if(flags!=0)+if(flags&SECCOMP_FILTER_FLAG_MASK)return-EINVAL;/* Validate resulting filter length. */
@@ -352,6 +454,15 @@ static long seccomp_attach_filter(unsigned int flags,if(total_insns>MAX_INSNS_PER_PATH)return-ENOMEM;+/* If thread sync has been requested, check that it is possible. */+if(flags&SECCOMP_FILTER_FLAG_TSYNC){+intret;++ret=seccomp_can_sync_threads();+if(ret)+returnret;+}+/**Ifthereisanexistingfilter,makeittheprevanddon'tdropits*taskreference.
@@ -359,6 +470,10 @@ static long seccomp_attach_filter(unsigned int flags,filter->prev=current->seccomp.filter;smp_store_release(¤t->seccomp.filter,filter);+/* Now that the new filter is in place, synchronize to all threads. */+if(flags&SECCOMP_FILTER_FLAG_TSYNC)+seccomp_sync_threads();+return0;}
@@ -564,7 +679,7 @@ static long seccomp_set_mode_filter(unsigned int flags,{constunsignedlongseccomp_mode=SECCOMP_MODE_FILTER;structseccomp_filter*prepared=NULL;-unsignedlongirqflags;+unsignedlongirqflags,taskflags;longret=-EINVAL;/* Prepare the new filter outside of any locking. */
@@ -572,6 +687,17 @@ static long seccomp_set_mode_filter(unsigned int flags,if(IS_ERR(prepared))returnPTR_ERR(prepared);+/*+*Ifwe'redoingthreadsync,wemustholdtasklist_lock+*tomakesureseccompfilterchangesarestableonthreads+*enteringorleavingthetasklist.Andwemusttakeit+*beforethesighandlocktoavoiddeadlocking.+*/+if(flags&SECCOMP_FILTER_FLAG_TSYNC)+write_lock_irqsave(&tasklist_lock,taskflags);+else+__acquire(&tasklist_lock);/* keep sparse happy */+if(unlikely(!lock_task_sighand(current,&irqflags)))gotoout_free;
@@ -588,6 +714,11 @@ static long seccomp_set_mode_filter(unsigned int flags,out:unlock_task_sighand(current,&irqflags);out_free:+if(flags&SECCOMP_FILTER_FLAG_TSYNC)+write_unlock_irqrestore(&tasklist_lock,taskflags);+else+__release(&tasklist_lock);/* keep sparse happy */+kfree(prepared);returnret;}
Since seccomp transitions between threads requires updates to the
no_new_privs flag to be atomic, changes must be atomic. This moves the nnp
flag into the seccomp field as a separate unsigned long for atomic access.
Signed-off-by: Kees Cook <redacted>
Acked-by: Andy Lutomirski <luto@amacapital.net>
---
fs/exec.c | 4 ++--
include/linux/sched.h | 13 ++++++++++---
include/linux/seccomp.h | 8 +++++++-
kernel/seccomp.c | 2 +-
kernel/sys.c | 4 ++--
security/apparmor/domain.c | 4 ++--
6 files changed, 24 insertions(+), 11 deletions(-)
@@ -1307,9 +1307,6 @@ struct task_struct {*execve*/unsignedin_iowait:1;-/* task may not gain privileges */-unsignedno_new_privs:1;-/* Revert to default priority/policy when forking */unsignedsched_reset_on_fork:1;unsignedsched_contributes_to_load:1;
@@ -3,6 +3,8 @@#include<uapi/linux/seccomp.h>+#define SECCOMP_FLAG_NO_NEW_PRIVS 0 /* task may not gain privs */+#ifdef CONFIG_SECCOMP#include<linux/thread_info.h>
Extracts the common check/assign logic, and separates the two mode
setting paths to make things more readable with fewer #ifdefs within
function bodies.
Signed-off-by: Kees Cook <redacted>
---
kernel/seccomp.c | 124 +++++++++++++++++++++++++++++++++++++-----------------
1 file changed, 85 insertions(+), 39 deletions(-)
@@ -486,69 +508,86 @@ long prctl_get_seccomp(void)}/**-*seccomp_set_mode:internalfunctionforsettingseccompmode-*@seccomp_mode:requestedmodetouse-*@filter:optionalstructsock_fprogforusewithSECCOMP_MODE_FILTER+*seccomp_set_mode_strict:internalfunctionforsettingstrictseccomp*-*Thisfunctionmaybecalledrepeatedlywitha@seccomp_modeof-*SECCOMP_MODE_FILTERtoinstalladditionalfilters.Everyfilter-*successfullyinstalledwillbeevaluated(inreverseorder)foreachsystem-*callthetaskmakes.+*Oncecurrent->seccomp.modeisnon-zero,itmaynotbechanged.+*+*Returns0onsuccessor-EINVALonfailure.+*/+staticlongseccomp_set_mode_strict(void)+{+constunsignedlongseccomp_mode=SECCOMP_MODE_STRICT;+unsignedlongirqflags;+intret=-EINVAL;++if(unlikely(!lock_task_sighand(current,&irqflags)))+return-EINVAL;++if(!seccomp_check_mode(current,seccomp_mode))+gotoout;++#ifdef TIF_NOTSC+disable_TSC();+#endif+seccomp_assign_mode(current,seccomp_mode);+ret=0;++out:+unlock_task_sighand(current,&irqflags);++returnret;+}++#ifdef CONFIG_SECCOMP_FILTER+/**+*seccomp_set_mode_filter:internalfunctionforsettingseccompfilter+*@filter:structsock_fprogcontainingfilter+*+*Thisfunctionmaybecalledrepeatedlytoinstalladditionalfilters.+*Everyfiltersuccessfullyinstalledwillbeevaluated(inreverseorder)+*foreachsystemcallthetaskmakes.**Oncecurrent->seccomp.modeisnon-zero,itmaynotbechanged.**Returns0onsuccessor-EINVALonfailure.*/-staticlongseccomp_set_mode(unsignedlongseccomp_mode,char__user*filter)+staticlongseccomp_set_mode_filter(char__user*filter){+constunsignedlongseccomp_mode=SECCOMP_MODE_FILTER;structseccomp_filter*prepared=NULL;unsignedlongirqflags;longret=-EINVAL;-#ifdef CONFIG_SECCOMP_FILTER-/* Prepare the new filter outside of the seccomp lock. */-if(seccomp_mode==SECCOMP_MODE_FILTER){-prepared=seccomp_prepare_user_filter(filter);-if(IS_ERR(prepared))-returnPTR_ERR(prepared);-}-#endif+/* Prepare the new filter outside of any locking. */+prepared=seccomp_prepare_user_filter(filter);+if(IS_ERR(prepared))+returnPTR_ERR(prepared);if(unlikely(!lock_task_sighand(current,&irqflags)))gotoout_free;-if(current->seccomp.mode&&-current->seccomp.mode!=seccomp_mode)+if(!seccomp_check_mode(current,seccomp_mode))gotoout;-switch(seccomp_mode){-caseSECCOMP_MODE_STRICT:-ret=0;-#ifdef TIF_NOTSC-disable_TSC();-#endif-break;-#ifdef CONFIG_SECCOMP_FILTER-caseSECCOMP_MODE_FILTER:-ret=seccomp_attach_filter(prepared);-if(ret)-gotoout;-/* Do not free the successfully attached filter. */-prepared=NULL;-break;-#endif-default:+ret=seccomp_attach_filter(prepared);+if(ret)gotoout;-}+/* Do not free the successfully attached filter. */+prepared=NULL;-current->seccomp.mode=seccomp_mode;-set_thread_flag(TIF_SECCOMP);+seccomp_assign_mode(current,seccomp_mode);out:unlock_task_sighand(current,&irqflags);out_free:kfree(prepared);returnret;}+#else+staticinlinelongseccomp_set_mode_filter(char__user*filter)+{+return-EINVAL;+}+#endif/***prctl_set_seccomp:configurescurrent->seccomp.mode
In preparation for adding seccomp locking, move filter creation away
from where it is checked and applied. This will allow for locking where
no memory allocation is happening. The validation, filter attachment,
and seccomp mode setting can all happen under the future locks.
Signed-off-by: Kees Cook <redacted>
---
kernel/seccomp.c | 86 ++++++++++++++++++++++++++++++++++++------------------
1 file changed, 58 insertions(+), 28 deletions(-)
@@ -197,27 +197,21 @@ static u32 seccomp_run_filters(int syscall)}/**-*seccomp_attach_filter:Attachesaseccompfiltertocurrent.+*seccomp_prepare_filter:Preparesaseccompfilterforuse.*@fprog:BPFprogramtoinstall*-*Returns0onsuccessoranerrnoonfailure.+*ReturnsfilteronsuccessoranERR_PTRonfailure.*/-staticlongseccomp_attach_filter(structsock_fprog*fprog)+staticstructseccomp_filter*seccomp_prepare_filter(structsock_fprog*fprog){structseccomp_filter*filter;unsignedlongfp_size=fprog->len*sizeof(structsock_filter);-unsignedlongtotal_insns=fprog->len;structsock_filter*fp;intnew_len;longret;if(fprog->len==0||fprog->len>BPF_MAXINSNS)-return-EINVAL;--for(filter=current->seccomp.filter;filter;filter=filter->prev)-total_insns+=filter->len+4;/* include a 4 instr penalty */-if(total_insns>MAX_INSNS_PER_PATH)-return-ENOMEM;+returnERR_PTR(-EINVAL);/**Installingaseccompfilterrequiresthatthetaskhas
@@ -228,11 +222,11 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)if(!current->no_new_privs&&security_capable_noaudit(current_cred(),current_user_ns(),CAP_SYS_ADMIN)!=0)-return-EACCES;+returnERR_PTR(-EACCES);fp=kzalloc(fp_size,GFP_KERNEL|__GFP_NOWARN);if(!fp)-return-ENOMEM;+returnERR_PTR(-ENOMEM);/* Copy the instructions from fprog. */ret=-EFAULT;
@@ -270,31 +264,26 @@ static long seccomp_attach_filter(struct sock_fprog *fprog)atomic_set(&filter->usage,1);filter->len=new_len;-/*-*Ifthereisanexistingfilter,makeittheprevanddon'tdropits-*taskreference.-*/-filter->prev=current->seccomp.filter;-current->seccomp.filter=filter;-return0;+returnfilter;free_filter:kfree(filter);free_prog:kfree(fp);-returnret;+returnERR_PTR(ret);}/**-*seccomp_attach_user_filter-attachesauser-suppliedsock_fprog+*seccomp_prepare_user_filter-preparesauser-suppliedsock_fprog*@user_filter:pointertotheuserdatacontainingasock_fprog.*-*Returns0onsuccessandnon-zerootherwise.+*ReturnsfilteronsuccessandERR_PTRotherwise.*/-staticlongseccomp_attach_user_filter(char__user*user_filter)+static+structseccomp_filter*seccomp_prepare_user_filter(char__user*user_filter){structsock_fprogfprog;-longret=-EFAULT;+structseccomp_filter*filter=ERR_PTR(-EFAULT);#ifdef CONFIG_COMPATif(is_compat_task()){
@@ -307,9 +296,37 @@ static long seccomp_attach_user_filter(char __user *user_filter)#endifif(copy_from_user(&fprog,user_filter,sizeof(fprog)))gotoout;-ret=seccomp_attach_filter(&fprog);+filter=seccomp_prepare_filter(&fprog);out:-returnret;+returnfilter;+}++/**+*seccomp_attach_filter:validateandattachfilter+*@filter:seccompfiltertoaddtothecurrentprocess+*+*Returns0onsuccess,-veonerror.+*/+staticlongseccomp_attach_filter(structseccomp_filter*filter)+{+unsignedlongtotal_insns;+structseccomp_filter*walker;++/* Validate resulting filter length. */+total_insns=filter->len;+for(walker=current->seccomp.filter;walker;walker=filter->prev)+total_insns+=walker->len+4;/* include a 4 instr penalty */+if(total_insns>MAX_INSNS_PER_PATH)+return-ENOMEM;++/*+*Ifthereisanexistingfilter,makeittheprevanddon'tdropits+*taskreference.+*/+filter->prev=current->seccomp.filter;+current->seccomp.filter=filter;++return0;}/* get_seccomp_filter - increments the reference count of the filter on @tsk */
@@ -480,8 +497,18 @@ long prctl_get_seccomp(void)*/staticlongseccomp_set_mode(unsignedlongseccomp_mode,char__user*filter){+structseccomp_filter*prepared=NULL;longret=-EINVAL;+#ifdef CONFIG_SECCOMP_FILTER+/* Prepare the new filter outside of the seccomp lock. */+if(seccomp_mode==SECCOMP_MODE_FILTER){+prepared=seccomp_prepare_user_filter(filter);+if(IS_ERR(prepared))+returnPTR_ERR(prepared);+}+#endif+if(current->seccomp.mode&¤t->seccomp.mode!=seccomp_mode)gotoout;
@@ -495,9 +522,11 @@ static long seccomp_set_mode(unsigned long seccomp_mode, char __user *filter)break;#ifdef CONFIG_SECCOMP_FILTERcaseSECCOMP_MODE_FILTER:-ret=seccomp_attach_user_filter(filter);+ret=seccomp_attach_filter(prepared);if(ret)gotoout;+/* Do not free the successfully attached filter. */+prepared=NULL;break;#endifdefault:
@@ -507,6 +536,7 @@ static long seccomp_set_mode(unsigned long seccomp_mode, char __user *filter)current->seccomp.mode=seccomp_mode;set_thread_flag(TIF_SECCOMP);out:+kfree(prepared);returnret;}