CertC-POS33

Do not use vfork()

Required inputs: IR

This Cert Rule and the following description is deprecated!

Using the vfork function introduces many portability and security issues. There are many cases in which undefined and implementation-specific behavior can occur, leading to a denial-of-service vulnerability.

According to the vfork man page,

The vfork() function has the same effect as fork(), except that the behavior 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.

Furthermore, older versions of Linux are vulnerable to a race condition, occurring when a privileged process calls vfork(), and then the child process lowers its privileges and calls execve(). The child process is executed with the unprivileged user's UID before it calls execve().

Because of the implementation of the vfork() function, the parent process is suspended while the child process executes. If a user sends a signal to the child process, delaying its execution, the parent process (which is privileged) is also blocked. This means that an unprivileged process can cause a privileged process to halt, which is a privilege inversion resulting in a denial of service.

This code example shows how difficult it is to use vfork() without triggering undefined behavior. The lowering of privileges in this case requires a call to setuid(), the behavior of which is undefined because it occurs between the vfork() and the execve().

pid_t pid = vfork();
    if (pid == 0) /* child */ {
      setuid(unprivileged_user);  /* undefined behavior */
      /*
       * Window of vulnerability to privilege inversion on
       * older versions of Linux
       */
      if (execve(filename, NULL, NULL) == -1) {
        /* Handle error */
      }

      /*
       * In normal operations, execve() might fail; if it does,
       * vfork() behavior is undefined.
       */
      _exit(1);  /* in case execve() fails */
    }
    

Use fork() instead of vfork() in all circumstances.

Noncompliant Code Example

This noncompliant code example calls vfork() and then execve(). As previously discussed, a vfork()/execve() pair contains an inherent race window on some implementations.

char *filename = /* something */;

    pid_t pid = vfork();
     if (pid == 0 )  /* child */ {
       if (execve(filename, NULL, NULL) == -1) {
         /* Handle error */
       }
       _exit(1);  /* in case execve() fails */
    }
    
Compliant Solution

This compliant solution replaces the call to vfork() with a call to fork(), which does not contain a race condition, and eliminates the denial-of-service vulnerability:

char *filename = /* something */;

    pid_t pid = fork();
    if (pid == 0) /* child */ {
      if (execve(filename, NULL, NULL) == -1) {
        /* Handle error */
      }
      _exit(1);  /* in case execve() fails */
    }
    
Risk Assessment

Using the vfork function can result in a denial-of-service vulnerability.

RuleSeverityLikelihoodRemediation CostPriorityLevel
POS33-ClowprobablelowP6L2
Related Guidelines
MITRE CWECWE-242, Use of inherently dangerous function
Bibliography
[Wheeler 2003]Section 8.6
Excerpt from SEI CERT C Coding Standard: Rules for Developing Safe, Reliable, and Secure Systems (2016 Edition) and SEI CERT C Coding Standard [], Copyright (C) 1995-2026 Carnegie Mellon University. See section 9.4. "3rd-Party Licenses" in the documentation for full details.

Possible Messages

Key

Text

Severity

Disabled

forbidden_libfunc_call

Do not use vfork().

None

False

Options

blacklist

blacklist

Type: dict[bauhaus.analysis.config.FileGlobPattern, list[bauhaus.analysis.config.GlobPattern]]

Default:

{
   'io.h': ['vfork'],
   'unistd.h': ['vfork']
}
Dictionary of header globbing to (list of) function name globbing(s) of forbidden functions.