Inheriting file descriptors

Updated: May 06, 2022

The documentation in the QNX Neutrino C Library Reference for each function describes in detail what the child inherits from the parent. One thing that we should talk about here, however, is file-descriptor inheritance.

With many of the process-creation functions, the child inherits the file descriptors of the parent. For example, if the parent had file descriptor 5 in use for a particular file when the parent creates the child, the child will also have file descriptor 5 in use for that same file. The child's file descriptor will have been duplicated from the parent's. This means that at the filesystem manager level, the parent and child have the same open control block (OCB) for the file, so if the child seeks to some position in the file, then that changes the parent's seek position as well. It also means that the child can do a write(5, buf, nbytes) without having previously called open().

If you don't want the child to inherit a particular file descriptor, then you can use fcntl() to prevent it. Note that this won't prevent inheritance of a file descriptor during a fork(). The call to fcntl() would be:

fcntl(fd, F_SETFD, FD_CLOEXEC);

If you want the parent to set up exactly which files will be open for the child, then you can use the fd_count and fd_map parameters with spawn(). Note that in this case, only the file descriptors you specify will be inherited. This is especially useful for redirecting the child's standard input (file descriptor 0), standard output (file descriptor 1), and standard error (file descriptor 2) to places where the parent wants them to go.

Alternatively this file descriptor inheritance can also be done through use of fork(), one or more calls to dup(), dup2(), and close(), and then exec*(). The call to fork() creates a child that inherits all the of the parent's file descriptors. dup(), dup2() and close() are then used by the child to rearrange its file descriptors. Lastly, exec*() is called to replace the child with the process to be created. Though more complicated, this method of setting up file descriptors is portable whereas the spawn() method is not.

Note: Inheriting file descriptors can be a security problem for setuid or setgid processes. For example, a malicious programmer might close stdin, stdout, or stderr before spawning the process. If the process opens a file, it can receive file descriptor 0, 1, or 2. If the process then uses stdin, stdout, or stderr, it might unintentionally corrupt the file. To help prevent such a situation, you can use set_lowest_fd() to make sure that your process never gets a file descriptor lower than what you expect.