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

RFE: add a list of syscalls required for normal go operation #31

Open
bobrik opened this issue Mar 7, 2019 · 7 comments
Open

RFE: add a list of syscalls required for normal go operation #31

bobrik opened this issue Mar 7, 2019 · 7 comments

Comments

@bobrik
Copy link

bobrik commented Mar 7, 2019

Go runtime requires some syscalls for normal operation (like mmap for memory allocation).

It seems like it's better to provide a list from the library rather than make developers guess.

At least the following are required:

	"mmap",
	"munmap",
	"mprotect",
	"futex",
	"clone",
	"rt_sigreturn",
	"rt_sigprocmask",
	"set_robust_list",
	"sigaltstack",
@mheon
Copy link
Contributor

mheon commented Mar 7, 2019

Add to that list gettid and sched_getaffinity at a minimum, probably nanosleep and getpid. If you're using any of the Golang concurrency primitives you probably hit a sched_yield or two as well.

@bobrik
Copy link
Author

bobrik commented Mar 7, 2019

Yes, my list is for a tiny program that doesn't do much.

I'm not sure if nanosleep needs to be explicitly allowed. I followed this example and without whitelisting nanosleep I can still see it allowed in strace.

@mheon
Copy link
Contributor

mheon commented Mar 7, 2019

I'm fairly certain Go uses it internally in the runtime for scheduling Goroutines, so while you can probably get away without it for trivial programs, I wouldn't recommend leaving it out.

(I'm also quite certain blacklisting it does block it - I used it extensively as a testcase while developing the bindings. Very easy to call fork off a sleep and see whether it takes milliseconds or seconds to return)

@pjbgf
Copy link
Contributor

pjbgf commented Nov 13, 2019

I have recently built a tool that goes through the execution path of go applications and extract all syscalls it finds. On this source code:

package main

import "fmt"

func main() {
	fmt.Println("test")
}

this is what it extracts:

"sched_yield",
"futex",
"write",
"mmap",
"exit_group",
"madvise",
"rt_sigprocmask",
"getpid",
"gettid",
"tgkill",
"rt_sigaction",
"read",
"getpgrp",
"arch_prctl",

here's how to use it:

go install https://github.com/pjbgf/gosystract
gosystract --template='{{- range . }}{{printf "\"%s\",\n" .Name}}{{- end}}' application-path

@pcmoore pcmoore changed the title Add a list of syscalls required for normal go operation RFE: add a list of syscalls required for normal go operation Nov 14, 2019
@gsauthof
Copy link

A default list would certainly be useful since to come up with one isn't trivial.

Just running the program under strace and collecting the observed syscalls isn't sufficient.

In my experience, even running the program with the same input several times in a row gives a different sets of syscalls, e.g. mprotect and set_robust_list don't necessarily turn up always.

And then there are non-regular circumstances that lead to syscalls you don't see usually such as restart_syscall which is executed only if you manage to send a signal that interrupts your program during a syscall.

In can confirm that nanosleep definitely needs to be whitelisted as it's called a lot, at least if you use Go routines. Not whitelisting it definitely yields a seccomp action.

On the other hand, I haven't seen rt_sigreturn so far.

Ideally one would have a look at the Go runtime code and grep the syscalls out of it.

Since Go statically links everything (unless you import a module with C bindings), the syscalls in the resulting binary should contain all the syscalls the program and the Go runtime does. And not much else since one should expect that Go eliminates dead runtime code.

I've done this for a medium size Go program of mine (gonzofilter, cf. its whitelist) which does a bit file IO, uses Go routines and does a lot of lexing and parsing:

Get syscall table:

curl -O https://raw.githubusercontent.com/torvalds/linux/v5.6/arch/x86/entry/syscalls/syscall_64.tbl

Get all direct syscalls:

join <(objdump -d gonzofilter -Mintel | grep 'syscall ' -B1 | grep eax | cut -f2 -d, \
    | sort -u | awk '{printf("%d\n", strtonum($1));}' | sort -k1,1) \
    <(awk '/^[0-9]/ {print $1, $3}' syscall_64.tbl | sort -k1,1) | sort -k1,1 -n > l1

Get all indirect syscalls (i.e. via syscall.Syscall and syscall.Syscall6):

join <(objdump -d gonzofilter -Mintel | grep 'call .*Syscall' -B18 \
    | grep '\[rsp\]\|Syscall' | grep QWORD | cut -f2 -d, | sort -u  \
    | awk '{printf("%d\n", strtonum($1));}' | sort -k1,1) \
    <(awk '/^[0-9]/ {print $1, $3}' syscall_64.tbl | sort -k1,1) | sort -k1,1 -n > l2

Combined I get this list:

clone
close
epoll_create
epoll_create1
epoll_ctl
epoll_pwait
exit
exit_group
fcntl
fdatasync
flock
fstat
fsync
ftruncate
futex
getpid
gettid
kill
lseek
madvise
mincore
mmap
munmap
nanosleep
openat
pread64
pwrite64
read
readlinkat
rt_sigaction
rt_sigprocmask
sched_getaffinity
sched_yield
setitimer
tgkill
write

When I link against a module with C bindings (such as this libseccomp module), of course, there are some additional syscall originating from the shared libraries (e.g. libc+libpthread+libseccomp).

@kolyshkin
Copy link
Contributor

This is a moving target, meaning any new golang releases and/or Linux kernel releases can add more system calls.

@pcmoore
Copy link
Member

pcmoore commented May 27, 2022

I agree with @kolyshkin, trying to maintain a default list of syscalls is going to be extremely difficult. Maybe someday we could revisit this idea, but at this point in time I think this needs to be a WONTFIX.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants