libseccomp
libseccomp copied to clipboard
RFE: async-signal safe `load()` of BPF
seccomp_load() is the only way of loading seccomp filter into the kernel at the moment. As implementation of seccomp_load() is not async-signal safe, calling the function between fork() and exec is unsafe. Therefore, it's very desirable to have an async-signal safe version of a load() function. In addition, it might be handy to have the load() function that accepts a BPF program as an argument.
The initial requirements for the new load() function might be as follow:
- Async-signal safe**.
- Accepts BPF program as an argument (via
fdorstruct). - Secure in order to prevent exploits.
** Note that syscalls, that are used to load seccomp filter into the kernel, are not async-signal safe. However, the new load() can be considered as a.-s. safe iff it is implemented without calling a.-s. unsafe syscalls, except for Linux-specific syscalls required to load the filter.
As implementation of seccomp_load() is not async-signal safe, calling the function between fork() and exec is unsafe. Therefore, it's very desirable to have an async-signal safe version of a load() function.
Can you describe your use case? Are you trying to block the use of fork()?
What functions and syscalls have you found to be a problem? If you identify where they are used in libseccomp we can investigate other options. One possibility might be to offer a libseccomp API that the caller could use to pre-compute the the BPF filter such that seccomp_load() would only need to potentially call prctl() and seccomp().
In addition, it might be handy to have the load() function that accepts a BPF program as an argument.
If you already have a BPF seccomp filter you can always load it via prctl(2) and/or seccomp(2).
In our case we have a containerizer process that prepares system environment (changes process capabilities, mounts directories, changes rootfs, etc.) before launching another executable via fork()+exec(). It is unsafe to call async-signal unsafe methods after fork(). E.g. calling malloc() is unsafe because the libc might acquire the mutex at the moment when a parent process calls malloc() while forking a new process.
Our new forked child process loads filters via seccomp_load() right before calling execvp(). However,seccomp_load() calls a.-s. unsafe malloc(): see [1] and [2].
Our containerizer process uses libseccomp API to generate a BPF seccomp filter before forking a new child that will load this filter into the kernel.
One possibility might be to offer a libseccomp API that the caller could use to pre-compute the the BPF filter such that seccomp_load() would only need to potentially call prctl() and seccomp().
Yeah, this approach should solve the problem.
Thanks for the background information. We can consider a pre-compute API function, but that will probably be after the v2.4 release.
calling the function between
fork()andexecis unsafe
isn't that unsafe only with threaded programs?
In that case, couldn't you temporarily block signals?
calling the function between
fork()andexecis unsafeisn't that unsafe only with threaded programs?
In that case, couldn't you temporarily block signals?
Off the top of my head I'm not sure of the answer, but I think the request for a BPF filter pre-compute API is reasonable and would provide a solution to this problem.
@abudnik see PR #390 for an implementation of seccomp_precompute(), it would be nice if you could look it over to make sure it solves your problem.