libseccomp icon indicating copy to clipboard operation
libseccomp copied to clipboard

RFE: async-signal safe `load()` of BPF

Open abudnik opened this issue 7 years ago • 5 comments

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 fd or struct).
  • 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.

abudnik avatar Jun 21 '18 20:06 abudnik

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).

pcmoore avatar Jun 22 '18 18:06 pcmoore

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.

abudnik avatar Jun 23 '18 12:06 abudnik

Thanks for the background information. We can consider a pre-compute API function, but that will probably be after the v2.4 release.

pcmoore avatar Jun 25 '18 21:06 pcmoore

calling the function between fork() and exec is unsafe

isn't that unsafe only with threaded programs?

In that case, couldn't you temporarily block signals?

giuseppe avatar Apr 17 '20 12:04 giuseppe

calling the function between fork() and exec is unsafe

isn'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.

pcmoore avatar Apr 28 '22 13:04 pcmoore

@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.

pcmoore avatar Sep 20 '22 02:09 pcmoore