-
Notifications
You must be signed in to change notification settings - Fork 690
Allocation in execv, execvp, and execve #555
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
Comments
Thanks for raising this. I need to read up a bit more on the restrictions post-fork. |
I was writing a shell last weekend, and came up against this. The shell was single-threaded, but I found it quite uncomfortable to be doing allocation after Do you know if unsafety can result from allocating after |
If the process has only one thread, then you have nothing to worry about. The problem arises if and only if The problem is this issue only concerns deadlock, no Rust-level unsafety is present. Again, third-party C libraries can do what they like - for example, libdispatch, which can spawn its own threads, is known to use |
Note that |
On Cirrus-CI, these tests frequently segfault. It's probably indicative of a real bug, not just a problem in the tests. But for now we need to skip them to get CI working. Issue nix-rust#555
The
exec
family of functions replace the current process with the new one, thus programs that wish to spawn a child normally callexec*()
soon after afork()
. Unfortunately, fork interacts badly with threads, because of which POSIX restricts the functions that are allowed to be called afterfork()
to a small set of async-signal-safe functions. The part that affects Rust is that, in particular, calls tomalloc()
are not allowed, which means any heap allocation afterfork()
is forbidden. Seestd::process
documentation and source for additional details.The
nix
interface toexecv
,execve
, andexecvp
callsto_exec_array
to allocate the kind of array that the corresponding C functions expect. It makes sense that this is done insideexec
; theargv
andenvp
pointers are laid out in a way completely alien to Rust, and in violation to its safety rules. However, sinceexec*
functions are typically invoked after a fork, it would be very nice to make the allocation optional, while still providing a fully safe wrapper.One way to avoid allocation after fork and retain safety is allocate the needed buffers before forking. For example, a type that encapsulates an owning vector of C strings could be instantiated before
fork()
, and passed to actualexec
. Since it would always store both the vector ofCString
objects and a vector of pointers to their content, itsas_c_vec()
method could produce the layout needed byexec*
without further allocation:A safe variant of
unistd::execvp()
would only acceptCVec
objects:Another option, which is more of a hassle to implement, but perhaps more convenient to use, is to completely encapsulate the allocation done inside the exec wrapper. It would effectively support staged execution, where first stage would prepare the needed data and return a closure that carries out the actual call to
libc::exec
, to be called after forking:String arguments are supported by accepting
AsRef<OsStr>
, again converted to C strings beforefork()
. Here is an example of a library that implements this approach to wrapexec*
and implement the equivalent ofexecvpe
. (While developing this library, the code exhibited real hangs before its own exec wrapper was switched to the approach described above.)The text was updated successfully, but these errors were encountered: