setuid() before calling execv() in vfork() / clone

2019-08-16 15:04发布

I need to fork an exec from a server. Since my servers memory foot print is large, I intend to use vfork() / linux clone(). I also need to open pipes for stdin / stdout / stderr. Is this allowed with clone() / vfork()?

标签: c linux
2条回答
孤傲高冷的网名
2楼-- · 2019-08-16 15:43

I'd use clone() instead, using CLONE_VFORK|CLONE_VM flags; see man 2 clone for details.

Because CLONE_FILES is not set, the child process has its own file descriptors, and can close and open standard descriptors without affecting the parent at all.

Because the cloned process is a separate process, it has its own user and group ids, so setting them via setresgid() and setresuid() (perhaps calling setgroups() or initgroups() first to set the additional groups -- see man 2 setresuid, man 2 setgroups, and man 3 initgroups for details) will not affect the parent at all.

The CLONE_VFORK|CLONE_VM flags mean this clone() should behave like vfork(), with the child process running in the same memory space as the parent process up till the execve() call.

This approach avoids the latency when using an intermediate executable -- it is pretty significant --, but the approach completely Linux-specific.

查看更多
家丑人穷心不美
3楼-- · 2019-08-16 15:55

From the standard:

[..] the behaviour is undefined if the process created by vfork() either modifies any data other than a variable of type pid_t used to store the return value from vfork(), or returns from the function in which vfork() was called, or calls any other function before successfully calling _exit() or one of the exec family of functions.

The problem with calling functions like setuid or pipe is that they could affect memory in the address space shared between the parent and child processes. If you need to do anything before exec, the best way is to write a small shim process that does whatever you need it to and then execs to the eventual child process (perhaps arguments supplied through argv).

shim.c
======

enum {
    /* initial arguments */
    ARGV_FILE = 5, ARGV_ARGS
};
int main(int argc, char *argv[]) {
    /* consume instructions from argv */
    /* setuid, pipe() etc. */
    return execvp(argv[ARGV_FILE], argv + ARGV_ARGS);
}
查看更多
登录 后发表回答