From the standard:
[..] the behavior is undefined if the process created vfork()either changes any data other than a type variable pid_tused to store the return value from vfork(), either returns from the function in which it was called vfork(), or calls any other function until a successful call _exit()or one of the families functions exec.
The problem with calling functions, such as setuidor pipe, is that they can affect memory in the address space shared between the parent and child processes. If you need to do something before exec, the best way is to write a small laying process that will do everything you need, and then execfor the subsequent child process (maybe the arguments provided through argv).
shim.c
======
enum {
ARGV_FILE = 5, ARGV_ARGS
};
int main(int argc, char *argv[]) {
return execvp(argv[ARGV_FILE], argv + ARGV_ARGS);
}