On OS X, the man page for fork says this:
There are limits to what you can do in the child process. To be totally safe you should restrict yourself to only executing async-signal safe operations until such time as one of the exec functions is called. All APIs, including global data symbols, in any framework or library should be assumed to be unsafe after a fork() unless explicitly documented to be safe or async-signal safe. If you need to use these frameworks in the child process, you must exec. In this situation it is reasonable to exec yourself.
Based on the footer of the man page, this has probably been there a long time:
4th Berkeley Distribution June 4, 1993 4th Berkeley Distribution
I'd have thought that chdir(2) would be safe to call between fork() and exec(), but its man page does not say that it's safe for async calls. Is it, in fact, unsafe? If so, am I really expected to change directory before fork()? Seems unreasonable to me.
Same goes for setenv(3). Considering that it calls malloc(), I guess that it's probably not safe. But there is no equivalent of execvp that allows me to pass an environment. Specifically, execvp searches the PATH. The closest equivalent I could find that takes an environment arg, execve(), does not search the PATH.