-
Notifications
You must be signed in to change notification settings - Fork 113
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
Protection against fork #735
base: main
Are you sure you want to change the base?
Conversation
If a thread forks, while another thread is holding an snmalloc lock, then the allocator could stop working. This patch attempts to protect against the cases of this. There is one case that is not covered. If a fork occurs during the very first allocation. This can result in the installation of the fork handler racing with the fork, and all bets are off.
Looking into it today |
Need to adjust header order. The change looks good to me but I may need to double check whether snmalloc is calling any non-async-signal-safe function (which would be UB if used after fork). |
Calling malloc after fork is undefined behaviour, so I’m not too worried about this as a problem (which is why I didn’t fix it three years ago). Using the atfork hooks may be problematic because the exact time at which they’re called is a bit loosely defined and we’re unordered with respect to other users of these APIs. I am not sure what happens in your code if another user calls malloc or free in their pre/post fork code. FreeBSD libc provides two hooks: These hooks exist because jemalloc determined that malloc being fork safe was not possible with the standard pthread APIs. We should probably expose this as a PAL hook as we do with some of the other libc-specific things. |
@davidchisnall, I just want to check. The original issue that you raised said:
But I think from your more recent message on this PR, this is UB, and not something we should address.
This is a great observation, I hadn't consider multiple handlers. This code could deadlock if an allocation occurs in a later prefork, or earlier postfork than the ones supplied by snmalloc. So this code makes things worse, than doing nothing. I am going to drop this PR, and close the underlying issue as I believe we are as safe as the standard requires. |
FYI, according to POSIX.1-2024,
So, only a very limited list of posix functions could be invoked post fork. ( I think direct syscall wrappers that are not in POSIX are also safe to call). However, Hence, I think a better idea is not to promise anything to users on our post-fork or signal-frame usability. |
Note, in particular, that That said, FreeBSD libc does provide hooks that allow the bundled jemalloc to make this work. Snmalloc would have to acquire all of the locks for chunk allocators in the pre hook and release them in the post hook. I don’t think it’s worth trying to make this work on platforms that don’t expose equivalent hooks (I think most libcs do, perhaps with different spellings). |
This allows for the thread that is executing the prefork and postfork handlers to continue to allocate right up to the point of fork.
I have fixed the issue with other prefork and postfork calls. I am still concerned about the first call to allocation being in parallel with another thread call ing If this has a sensible semantics, then I think this code could work as is. Looking at glibc there is a lock guarding both addition of a handler and running of all the handlers, so this would give the sensible semantics. |
Sadly, looking at the more recent code that locks was made more complex, and they broke this property. https://sourceware.org/git/?p=glibc.git;a=commit;h=52a103e237329b9f88a28513fe7506ffc3bd8ced This drops the lock during each call to a handler. This was required to handle re-entrancy. But this means a parallel call can no longer work correctly. |
I feel I have descended down a rabbit hole on this one. So the following code on my 22.04 installation terminates, and on a 24.04 deadlocks: https://github.com/mjp41/pthread_atfork/blob/main/main_reentrant.cpp @SchrodingerZhu @davidchisnall does this make sense to you? Releasing the lock during fork pre/post process alters the semantics. |
Submitted a bug report to glibc |
you can also send the bug to libc-coord if you consider it important to discuss about. That mailing list should be quite active. |
Sent a message CCing you. |
If a thread forks, while another thread is holding an snmalloc lock, then the allocator could stop working.
This patch attempts to protect against the cases of this. There is one case that is not covered. If a fork occurs during the very first allocation. This can result in the installation of the fork handler racing with the fork, and all bets are off.
Address #630