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 asfork(), except that the behavior is undefined if the process created byvfork()either modifies any data other than a variable of typepid_tused to store the return value fromvfork(), or returns from the function in whichvfork()was called, or calls any other function before successfully calling_exit()or one of theexecfamily 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.
| Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| POS33-C | low | probable | low | P6 | L2 |
Related Guidelines
| MITRE CWE | CWE-242, Use of inherently dangerous function |
Bibliography
| [Wheeler 2003] | Section 8.6 |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
forbidden_libfunc_call |
Do not use vfork(). |
None |
False |
Options¶
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
blacklist¶
blacklist
Dictionary of header globbing to (list of) function name globbing(s) of forbidden functions.Type: dict[bauhaus.analysis.config.FileGlobPattern, list[bauhaus.analysis.config.GlobPattern]]
Default:
{ 'io.h': ['vfork'], 'unistd.h': ['vfork'] }