Live data from Hacker News

Fork() without exec() is dangerous in large programs

evanjones.ca

31–40 of 106 posts

Re: Fork() without exec() is dangerous in large programs

#31
post #20

Earlier quoted context omitted.

Most non-UNIX OSes only have exec(), not fork(). Hence why fork() is just kind-of implemented on their POSIX compatibility layers. In UNIX it is only an issue, because IEEE doesn't want to set in stone in POSIX how threads, signals and processes should interact, due to the way threads were added to UNIX.

Non-unix systems don't have exec; they have spawn.

Windows has exec.

Re: Fork() without exec() is dangerous in large programs

#32
Hmm, consider we'd want to keep fork(), then the only safe way to deal with this would be, that critical sections were actually transactions implemented on the OS level and at after fork() in the child all transactions in flight get rolled back before returning from fork().

I see two implementation challenges with that suggestion:

1.) implementing that transaction mechanism as a kernel feature: When entering a CS mark all pages CoW, upon leaving the CS merge modified pages (problem: Whole pages are then mutual exclusive, dealing with this is the challenge)

2.) battling with user space implemented locks that use atomics.

-----

An immediate mitigation I see is, that fork() itself is a CS on _all_ the locks of a process. If we consider that only the standard locking mechanisms are used, then whenever a CS is entered (which includes the creation process of a locking primitive) it raises/posts a global fork-lock semaphore. And upon leaving that semaphore is lowered.

This still leaves the DIY-locking primitives problem open. But it should be more or less straightforward to add this to the system libc/pthread libraries' locking primitive implementation and fork() syscall wrappers.

Or did I miss something essential here? Talk is cheap, so if nobody has any obvious objections I'd actually go ahead implement it.

EDIT: Okay, one immediate problem I see is, that this would pose a challenge for calling fork inside a CS. Technically this is a situation where thread recursive locks would help, but as we all know, recursive locks are highly problematic.

Re: Fork() without exec() is dangerous in large programs

#33
post #23
post #21

Earlier quoted context omitted.

You can have your cake and eat it too -- there's no throwing away of performance gains. Libraries can use threads, but not directly or "silently". They should take threads as parameters. Then the application knows when they're being used and the application programmer can reason about it.

Yes, in theory. In practice, every library developer/maintainer uses different guidelines.

In practice, a lot of library developers/maintainers read this list, or know someone who does.

Re: Fork() without exec() is dangerous in large programs

#34
post #28

Earlier quoted context omitted.

true... ...this applies not just to threading, but other things that are not in the domain of the library such as memory allocation strategy or communication mechanism. Libraries should be small, focused and decoupled. This is the way to proper re-usability.

Like left-pad?

jeez, no.... extremes of everything are terrible.

I suppose another rule should be that there libraries should encapsulate a sufficient amount of complexity to make their existence worthwhile.... unlike left-pad.

Re: Fork() without exec() is dangerous in large programs

#35
post #21
post #10

Earlier quoted context omitted.

I completely disagree. The only part of this that seems dangerous is the fact that it can turn a single-threaded program into a multi-threaded one, and the only real problem there is it makes fork-without-exec unsafe. But fork-without-exec is basically unsafe anyway unless you can absolutely guarantee that nothing has ever spawned a thread. Meanwhile, preventing libraries from using threading under the covers basical…

You can have your cake and eat it too -- there's no throwing away of performance gains. Libraries can use threads, but not directly or "silently". They should take threads as parameters. Then the application knows when they're being used and the application programmer can reason about it.

compilers and runtimes can spawn threads implicitly when required (think of openmp, cilk+, or even autopar). Are you saying that libraries shouldn't use these facilities?

Re: Fork() without exec() is dangerous in large programs

#36
If you are in control of all the threads that an application is running, you can call fork() safely by making sure the threads are put in a safe state (no critical locks retained) when the fork() call happens. Also note that many things that should normally be unsafe, like having running threads calling malloc() while another thread is forking, are actually safe in the real world using certain implementation of fork, since there are pre/post fork "hooks" in the malloc implementation in order to fix the state of the child.

So if you control very closely the libs you link with and what they do, as well as the threads you use yourself, it is possible to use fork() in a reliable way.

Re: Fork() without exec() is dangerous in large programs

#37
post #13
post #2

In my opinion, it's libraries that secretly use threads under the covers (but still access state shared with the rest of the program like the malloc heap) that are what's dangerous.

What is dangerous is that the interactions between threads and processes aren't part of POSIX standard and its behavior is OS specific across UNIX implementations. This isn't an issue on non-UNIX OSes.

Pthreads are part of the POSIX standard. Hence the "p".

Then there is pipe() and probably some more similar ones.

Re: Fork() without exec() is dangerous in large programs

#38
post #36

If you are in control of all the threads that an application is running, you can call fork() safely by making sure the threads are put in a safe state (no critical locks retained) when the fork() call happens. Also note that many things that should normally be unsafe, like having running threads calling malloc() while another thread is forking, are actually safe in the real world using certain implementation of fork,…

[deleted]

Re: Fork() without exec() is dangerous in large programs

#39
post #36

If you are in control of all the threads that an application is running, you can call fork() safely by making sure the threads are put in a safe state (no critical locks retained) when the fork() call happens. Also note that many things that should normally be unsafe, like having running threads calling malloc() while another thread is forking, are actually safe in the real world using certain implementation of fork,…

[deleted]

Re: Fork() without exec() is dangerous in large programs

#40
post #36

If you are in control of all the threads that an application is running, you can call fork() safely by making sure the threads are put in a safe state (no critical locks retained) when the fork() call happens. Also note that many things that should normally be unsafe, like having running threads calling malloc() while another thread is forking, are actually safe in the real world using certain implementation of fork,…

glibc attempts this using the pthread_atfork() hook. it takes the global malloc (and possibly other) forks in the parent before forking, then both the parent and child release the lock before returning to the callee's code. obviously if you have locks in your own code then you may or may not need to hold these before calling fork() so that both parent and child can release them after the fork().

the apple way of just throwing its hands in the air and abort()ing sound either like giving up because the apple devs think it is too hard, or because they feel the need to impose their way in crippling code that uses fork() without calling exec()

in my opinion if you are going to write a library that uses threads and is supposed to be thread safe then remember you need to handle the issue of fork(); don't be lazy, do the job properly, even if doing it right it is hard and ugly.

Post reply on HN