Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

syscall: support 'mode 2 seccomp' on Linux #3405

Open
krasin opened this issue Mar 27, 2012 · 29 comments

Comments

@krasin
Copy link

commented Mar 27, 2012

Ubuntu 12.04 LTS comes with "mode 2 seccomp" and the mainline kernel is
currenly in the process of accepting seccomp patches.

In short, "mode 2 seccomp" adds an ability to apply syscall filters to the
current process.

A good tutorial is http://outflux.net/teach-seccomp/
I have tested it with the daily build of Ubuntu 12.04 LTS,
$ uname -a
Linux krasin-seccomp 3.2.0-20-generic #32-Ubuntu SMP Thu Mar 22 02:22:46 UTC 2012 x86_64
x86_64 x86_64 GNU/Linux

I understand that this feature is not on top of the priority, but when Go 1 is here, it
would be probably time to add this support.
@rsc

This comment has been minimized.

Copy link
Contributor

commented Mar 27, 2012

Comment 1:

What does this mean?  Are you asking for a system call or two?

Labels changed: added priority-later, removed priority-triage.

Status changed to WaitingForReply.

@krasin

This comment has been minimized.

Copy link
Author

commented Mar 28, 2012

Comment 2:

Hi Russ,
first of all, here is the example of using 'mode 2 seccomp' from Go:
https://github.com/krasin/seccomp/blob/master/example/example.go
It has been tested on x86-64 Ubuntu 12.04, but is not guaranteed to work on 32-bit and
will not work on other / older kernels. The good news that 'mode 2 seccomp' is going to
be included into Linux 3.5 kernel. See https://lkml.org/lkml/2012/3/25/81
Currently, the example is only half-way working. seccomp applies policies to the current
thread, not the process or all the threads. It means that I had to
runtime.LockOSThread() to avoid switching the system thread while executing the
goroutine and it's only that thread is killed, which makes the whole program just hang
(Go runtime does not handle the situation when one of threads has received SIGSYS)
In order to get 'mode 2 seccomp' to work properly with Go, it should make it possible to
apply the policy to all system threads at once and make it possible to disable thread
spawning by the runtime (since clone(2) will likely be disabled by any self-respecting
seccomp policy).
@krasin

This comment has been minimized.

Copy link
Author

commented Mar 28, 2012

Comment 3:

The output I see on Ubuntu 12.04:
$ go run example.go 
Applying syscall policy...
And now, let's make a 'bad' syscall
Note: due to lack of seccomp support from the Go runtime,the example will stuck instead
of crashing. Use Ctrl+C to exit.
@krasin

This comment has been minimized.

Copy link
Author

commented Mar 28, 2012

Comment 4:

The output on Ubuntu 11.10 (which does not support 'mode 2 seccomp'):
$ go get github.com/krasin/seccomp/example
$ example
Applying syscall policy...
2012/03/28 00:31:45 Prctl(PR_SET_NO_NEW_PRIVS): invalid argument
@bradfitz

This comment has been minimized.

Copy link
Member

commented Mar 28, 2012

Comment 5:

There have been a dozen "seccomp v2" proposals over the past many years.
Until one of them is finally upstream (and not in a vendor kernel) and trickles down to
a few notable distros, *then* this gets more interesting.
@krasin

This comment has been minimized.

Copy link
Author

commented Mar 28, 2012

Comment 6:

ok, makes sense. Anyway, it does not make sense to do anything before Ubuntu 12.04
release and I have a strong feeling that Will Drew's 'mode 2 seccomp' will be integrated
into the mainline -next branch by that time.
@rsc

This comment has been minimized.

Copy link
Contributor

commented Mar 28, 2012

Comment 7:

Labels changed: added priority-someday, removed priority-later.

Status changed to LongTerm.

@gopherbot

This comment has been minimized.

Copy link

commented Mar 30, 2012

Comment 8 by ColinTrexob:

Simply use the command flag --enable-seccomp for what is effectively the mode 2. What
mode 2 adds is the ability to do more than just read, write, end - this is already
what's supported by the Chrome seccomp sandbox.
@krasin

This comment has been minimized.

Copy link
Author

commented Mar 30, 2012

Comment 9:

Hi Colin,
I'm not sure I understand your suggestion. Would you like to elaborate?
@gopherbot

This comment has been minimized.

Copy link

commented Mar 31, 2012

Comment 10 by ColinTrexob:

To elaborate: I had a code.google.com for Chromium (filing a bug) and got very very
confused lol
@krasin

This comment has been minimized.

Copy link
Author

commented Jul 15, 2012

Comment 11:

Seccomp mode 2 is not in the mainline kernel:
https://github.com/torvalds/linux/blob/master/kernel/seccomp.c
@krasin

This comment has been minimized.

Copy link
Author

commented Jul 15, 2012

Comment 12:

Seccomp mode 2 is now in the mainline kernel:
https://github.com/torvalds/linux/blob/master/kernel/seccomp.c
@krasin

This comment has been minimized.

Copy link
Author

commented Jul 22, 2012

Comment 13:

And, finally, Linux 3.5 is released: http://kernelnewbies.org/Linux_3.5
From the Release Notes:
1.3. Seccomp-based system call filtering
Seccomp (alias for "secure computing") is a simple sandboxing mechanism added back in
2.6.12 that allows to transition to a state where it cannot make any system calls except
a very restricted set (exit, sigreturn, read and write to already open file
descriptors). Seccomp has now been extended: instead of a fixed and very limited set of
system calls, seccomp has evolved into a filtering mechanism that allows processes to
specify an arbitrary filter of system calls (expressed as a Berkeley Packet Filter
program) that should be forbidden. This can be used to implement different types of
security mechanisms; for example, the Linux port of the Chromium web browser supports
this feature to run plugins in a sandbox.
The systemd init daemon has added support for this feature. A Unit file can use the
SystemCallFilter to specify a list with the syscalls that will be allowed to run, any
other syscall will not be allowed:
 [Service]  ExecStart=/bin/echo "I am in a sandbox" SystemCallFilter=brk mmap access open fstat close read fstat mprotect arch_prctl munmap write
Recommended links: Documentation and Samples).
Recommended LWN article: Yet another new approach to seccomp
Code: (commit 1, 2, 3, 4, 5)
@alberts

This comment has been minimized.

Copy link
Contributor

commented Jul 22, 2012

Comment 14:

As with unshare, it might only make sense to call this system call before the Go runtime
starts making threads, which is what systemd is very good at.
@krasin

This comment has been minimized.

Copy link
Author

commented Jul 22, 2012

Comment 15:

why not at any point later?
@krasin

This comment has been minimized.

Copy link
Author

commented Jul 22, 2012

Comment 16:

For example, if I want to apply the sandbox after the initialization has been completed,
but before I have started to handle the untrusted data or code, it perfectly makes
sense. Go runtime should support two things in order to make it happen:
1. Make it possible to apply the rule to all threads (even if not immediately, but
provide a blocking call)
2. Exit the program if any thread was killed (for example, because of sandbox violation)
@alberts

This comment has been minimized.

Copy link
Contributor

commented Jul 22, 2012

Comment 17:

It's not impossible, it's just harder. I don't speak for the Go team, but I'm guessing:
"patches welcome".
@rsc

This comment has been minimized.

Copy link
Contributor

commented Dec 4, 2013

Comment 18:

Labels changed: added repo-main.

@rsc

This comment has been minimized.

Copy link
Contributor

commented Mar 3, 2014

Comment 19:

Adding Release=None to all Priority=Someday bugs.

Labels changed: added release-none.

@gopherbot

This comment has been minimized.

Copy link

commented Sep 8, 2014

Comment 20 by rahul.chaudhry:

Hi All,
I did some research on this issue. Here's a doc with some of my thoughts and a rough
proposal:
https://docs.google.com/document/d/12jRIrlFYKe3EyBLgtrkZelC-aGnLXnsHzx0ZVDF75Go/pub
Feedback welcome. Let me know if the basic approach seems workable and acceptable from
the Go runtime point of view.
@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Sep 8, 2014

Comment 21:

Thanks for looking at this.
There are two different issues to discuss: the API, and the implementation.  The API
doesn't belong in the runtime package.  In the old days it would have been put in the
syscall package, but since we've frozen the syscall package it should now go into the
go.sys package.  It does sound like the implementation needs to involve the runtime
package, but that should happen behind the scenes--programs should call something in
go.sys, not something in runtime.
In go.sys I think it's fine to have an interface that is specific to the Linux kernel. 
We already have examples of that in syscall--e.g., the Cloneflags field in SysProcAttr. 
I don't think we should aim for a system-independent interface to system call blocking;
I think there will be too much variance between systems.
The case of SYS_CLONE is an interesting one.  I don't think the Go runtime can function
without the ability to clone a new thread.  You suggest creating some new threads ahead
of time, but it would not be hard for a program to run out of threads.  Every blocking
file access call uses up a thread.  That is independent of GOMAXPROCS, which controls
the number of running threads, not the total number of threads.  I think that at least
initially we should fail the seccomp call if SYS_CLONE is not permitted.  We can see if
anybody sees this as a problem--it allows an evil program to fork-bomb the system but it
doesn't open any other holes that I can see.
@gopherbot

This comment has been minimized.

Copy link

commented Sep 9, 2014

Comment 22 by rahul.chaudhry:

Thanks for your comments.
Your API recommendations make this much simpler. No exported API from package runtime,
and a linux (and seccomp) specific API in go.sys package. I'll update the doc to reflect
these changes.
I also agree that we should start with an implementation that requires SYS_CLONE to be
whitelisted. This will make the runtime support for the implementation much simpler as
well.
However, for the longer term, and from sandboxing point of view, we should still
consider the option of working with a strictly limited number of threads in the runtime.
Quoting from the reporter of this issue above: "clone(2) will likely be disabled by any
self-respecting seccomp policy". Moreover, read() and write() will probably need to be
whitelisted by most seccomp policies. If excessive file IO from a sandboxed program may
result in the creation of an arbitrarily large number of system threads, it is not a
very effective scheme for sandboxing.
I was under the impression that the runtime already supports this mode (simply block on
blocking system calls if no more threads can be created). Now I see that it causes the
program to crash if the number of threads exceeds the threshold set by
runtime/debug/SetMaxThreads().
For my curiosity, is the model of working with a strictly limited number of threads
fundamentally incompatible with the Go semantics? My understanding is that it is
possible to have a fully compliant Go compiler and runtime system that does not use any
multi-threading capabilities of the operating system it is running on (and does not have
to resort to interpreting either). In other words, using a different system thread for
every blocking system call is an optimization in the runtime, and not a requirement for
a correct Go implementation. Please correct me if I'm wrong.
@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Sep 9, 2014

Comment 23:

Conceptually, you are right: the number of threads used by a Go program is purely an
implementation detail, and it should be possible in principle to write a Go
implementation that uses a fixed number of threads.
This can even be done in practice.  When a Go program is about to enter a system call,
or call a C/C++ function, we can check whether there are more threads available to run
goroutines.  If there are not, we can suspend the goroutine until there are threads
available.
That approach would work for most programs.  The problem is that there are valid Go
programs that would work normally, but would fail if this policy were adopted, simply
because the other suspended threads might be unblocked by the C function that would have
been called by the goroutine that the runtime suspended.  The Go programmer has to
change from a model in which goroutines can be created casually and can run any
operation to a different model in which all potentially blocking calls have to be
counted, with semaphores or other mechanisms to prevent a deadlock of blocking calls. 
Perhaps this is acceptable for people who use seccomp, but it hardly seems desirable. 
And this is not something that can be implemented only in the seccomp support code.  It
goes against the grain to modify the Go runtime to support a more complex programming
model.
@gopherbot

This comment has been minimized.

Copy link

commented Sep 18, 2014

Comment 24 by rahul.chaudhry:

Ah.. I see. I can have a program with two goroutines that communicate using a system
pipe (horror). This program would end up in a deadlock if restricted to a single thread.
The read/write system calls on the pipe have to be made simultaneously from different
threads for the goroutines to make progress.
I have updated the draft based on the feedback above. The new version is here:
https://docs.google.com/document/d/1nh1hub2wJdYzoLVUkCPS7MoUIFRZRP5PbrtOO0WajII/pub
Please take another look.
@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Sep 19, 2014

Comment 25:

I'm sure some details will arise, but this plan looks basically sound to me.  Thanks.
@shawnl

This comment has been minimized.

Copy link

commented Mar 22, 2015

SecComp can only be called once.

Linux allow seccomp mode 2 to be called multiple times, with each filter stacked. This restriction is unneccesary.

@shawnl

This comment has been minimized.

Copy link

commented Mar 22, 2015

This is basically the plan for bug #1435 too.

@rsc rsc added this to the Unplanned milestone Apr 10, 2015

@rahulchaudhry

This comment has been minimized.

Copy link
Contributor

commented Jun 5, 2015

If your kernel has the new seccomp syscall and supports SECCOMP_FILTER_FLAG_TSYNC, a seccomp filter can be installed from a Go program without special help from the runtime.

For details on this flag: https://git.kernel.org/linus/c2e1f2e30daa551db3c670c0ccfeab20a540b9e1

seccomp-tsync support is available in 3.17 or newer kernels. It might also have been backported to the distribution/kernel you're using. One way to find out if your kernel supports this is to test for it (see below).

A Go package for seccomp support was added recently to ChromiumOS tree: https://chromium.googlesource.com/chromiumos/platform/go-seccomp/

The package includes a CheckSupport() function to check for seccomp-tsync support in the kernel.
See the main package file for details: https://chromium.googlesource.com/chromiumos/platform/go-seccomp/+/master/src/chromiumos/seccomp/seccomp.go

@jessfraz

This comment has been minimized.

Copy link
Contributor

commented Jun 22, 2016

There are a few packages that allow for setting seccomp filters already, the one from chromium linked above and https://github.com/seccomp/libseccomp-golang which is used by https://github.com/opencontainers/runc . Is this something that needs to be a part of the standard library? There was even a pure go implementation of a seccomp package (https://github.com/docker/libcontainer/pull/613/files), but the problem with that is constantly having to stay in sync with libseccomp and the kernel. So I guess I'm wondering what the goal is here? Because I'm willing to help.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
9 participants
You can’t perform that action at this time.