1

I am using vfork() in glibc and according to vfork()'s man page:

Fork handlers established using pthread_atfork(3) are not called when a multithreaded program employing the NPTL threading library calls vfork(). Fork handlers are called in this case in a program using the LinuxThreads threading library.

On NPTL fork handlers are not being called. In my specific case I need this protection to be engaged so fork handlers will be called the same way as when calling fork().

Is there a way to cause pthread library to call registered handlers or even call them manually?

I thought of using clone() as it gives more precise control of the cloned process but it also avoids fork handlers:

Handlers registered using pthread_atfork(3) are not executed during a clone call.

Also read how to reset handlers registered by pthread_atfork - in my case I don't want to remove handlers but only call them.

Thanks.

joepol
  • 744
  • 6
  • 22
  • What is your reason to call `vfork` instead of `fork`? – Employed Russian Jan 04 '23 at 06:02
  • @EmployedRussian it's quite complex (-some method of code injection). I rely on the fact that both parent-child processes share the virtual address space. – joepol Jan 04 '23 at 07:49
  • Are you in control of the code that is registering `atfork` handlers? Because the best solution is probably to make it stop doing that. `atfork` handlers that actually serve the intended purpose are *extremely* hard to get right for non-trivial cases, and they aren't necessary for trivial cases. – John Bollinger Jan 08 '23 at 14:46
  • @JohnBollinger I have control over the fork handlers I register, and there are also fork handlers registered by libc which I want them to be called but I don't have direct access to. – joepol Jan 10 '23 at 13:38
  • @joepol, if libc registers any fork handlers then those should be considered an internal implementation detail, primarily of the implementation of `fork()`. I am not aware of any such being documented. That such handlers do not run during a `vfork()` is a matter of `vfork()` being different from `fork()`. If the libc implementation thought it was appropriate for those behaviors to happen at a `vfork()` then it would have chosen a different means than fork handlers to implement them. – John Bollinger Jan 10 '23 at 14:29

1 Answers1

3

Is there a way to cause pthread library to call registered handlers or even call them manually?

If you succeeded in doing this, your program would become corrupt. There is a reason pthread_atfork handlers aren't called on vfork.


A typical usage of pthread_atfork handlers is to guarantee that any locks held in the parent process are in a consistent state in both the child and the parent after the fork.

Without handlers, suppose thread T1 is holding lock L, and thread T2 calls fork. Since only T2 will be replicated into the child, L will remain locked forever. If T2 needs that lock (say it was the malloc lock), T2 will deadlock (with T1 which doesn't exist in the child).

The solution: pthread_atfork handlers. In the parent process prepare() will acquire L, and both parent() and child() handlers will unlock their own instance of L in the parent and child processes respectively. Everything is happy1.


Now consider what happens if you do the same after vfork. prepare() acquires L as expected, but when you call parent() and child(), they unlock the same instance of L (because parent and child processes share memory). Thus L gets unlocked twice, corrupting it!


1 In practice calling either fork or vfork in a multithreaded program is fraught with peril, and doing this safely in the presence of arbitrary libraries is near impossible.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • If `pthread_atfork()` will be called with all handlers when running `vfork()` a corruption will happen as you describe, but - in the `LinuxThreads`(the GLIBC library that preceded the current - `NPTL` implementation) *fork handlers were called* and - I found a glibc function : `__run_fork_handlers()` which allows calling only part of the handlers so I can call only the parent/child handler. its in `fork.h` file but it's not in `/usr/include` so I am not sure how to use it yet.. – joepol Jan 10 '23 at 13:53