Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: REM Cost Reform

Standard FILE objects and their underlying representation (file descriptors on POSIX platforms or handles elsewhere) are a finite resource that must be carefully managed. The number of files that an implementation guarantees may be open simultaneously is bounded by the FOPEN_MAX macro defined in <stdio.h>. The value of the macro is guaranteed to be at least 8. Consequently, portable programs must either avoid keeping more than FOPEN_MAX files at the same time or be prepared for functions such as fopen() to fail due to resource exhaustion.

Failing to close files when they are no longer needed may allow attackers to exhaust, and possibly manipulate, system resources. This phenomenon is typically referred to as sometimes called file descriptor leakage, although file pointers may also be used as an attack vector. In addition, keeping files open longer than necessary increases the risk that data written into in-memory file buffers will not be flushed in the event of abnormal program termination. To prevent file descriptor leaks , file pointers and file descriptors should and to guarantee that any buffered data will be flushed into permanent storage, files must be closed when they are no longer needed.

Non-Compliant Code Example

Wiki Markup
In this non-compliant example inspired by a vulnerability in OpenBSD's {{chpass}} program \[[Openbsd 98|http://seclists.org/bugtraq/1998/Aug/0071.html]\], a file containing sensitive data is opened for reading. Before closing this file, the registered editor retrieved from the environment and executed using the {{system()}} command. Internally, the {{system()}} command spawns a child process to run the editor. This child process inherits the file descriptors of the parent process. As a result, the editor will be able to access the contents of {{Sensitive.txt}}.

The behavior of a program is undefined when it uses the value of a pointer to a FILE object after the associated file is closed (see undefined behavior 153.) Programs that close the standard streams (especially stdout but also stderr and stdin) must be careful not to use the stream objects in subsequent function calls, particularly those that implicitly operate on such objects (such as printf(), perror(), and getc()).

Noncompliant Code Example

In this noncompliant code example, derived from a vulnerability in OpenBSD's chpass program [NAI 1998], a file containing sensitive data is opened for reading. The get_validated_editor() function retrieves the registered editor from the EDITOR environment variable and sanitizes it to be a valid editor in accordance with FIO02-C. Canonicalize path names originating from tainted sources. The function returns a command for invoking the editor  which is subsequently passed as a command system() function. If the system() function is implemented in a way that spawns a child process, then the child process could inherit the file descriptors opened by its parent. If this happens, as it does in POSIX systems, the child process will be able to access the contents of the potentially sensitive file called file_name.

Code Block
bgColor#FFcccc
langc
#include <stdio.h>
#include <stdlib.h>
 
extern const char *get_validated_editor(void);
 
void func(const char *file_name) {
  FILE *f;
  const char *editor;

  f = fopen(file_name, "r");
  if (f == NULL) {
    /* Handle error */
  }
 
  editor = get_validated_editor();
  if (editor == NULL) {
    /* Handle error */
  }
 
  if (system(editor) == -1) {
    /* Handle error */
  }
}

If the command returned by get_validated_editor() will always be a simple path (such as /usr/bin/vim), and runs on a POSIX system, this program could be strengthened by using a call to execve() rather than system(), in accordance with ENV33-C. Do not call system().

On UNIX-based systems, child processes are typically spawned using a form of fork() and exec(), and the child process always inherits from its parent any file descriptors that do not have the close-on-exec flag set. Under Microsoft Windows, file-handle inheritance is determined on a per-file and per-spawned process basis. See WIN03-C. Understand HANDLE inheritance for more information.

Compliant Solution

In this compliant solution, file_name is closed before launching the editor:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <stdlib.h>
 
extern const char *get_validated_editor(void);
 
void func(const char *file_name) {
  FILE *f;
  const char *editor;

  f = fopen(file_name, "r");
  if (f == NULL) {
    /* Handle error */
  }
  
  fclose(f);
  f = NULL;
  
  editor = get_validated_editor();
  if (editor == NULL) {
    /* Handle error */
  }
 
  /* Sanitize environment before calling system() */
  if (system(editor) == -1) {
    /* Handle error */
  }
}

Compliant Solution (POSIX)

Sometimes it is not practical for a program to close all active file descriptors before issuing a system call such as system() or exec(). An alternative on POSIX systems is to use the FD_CLOEXEC flag, or O_CLOEXEC when available, to set the close-on-exec flag for the file descriptor:

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
extern const char *get_validated_editor(void);
 
void func(const char *file_name) {
  int flags;
  char *editor;

  int fd = open(file_name, O_RDONLY);
  if (fd == -1) {
    /* Handle error */
  }

  flags = fcntl(fd, F_GETFD);
  if (flags == -1) {
    /* Handle error */
  }

  if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
    /* Handle error */
  }

  editor = get_validated_editor();
  
Code Block
bgColor#FFcccc

FILE* f;
char *editor;

f = fopen("Sensitive.txt", "r");
if (fd == NULL) {
  /* Handle fopen() error */
}

editor = getenv("EDITOR");
if (editor == NULL) {
    /* Handle getenv() error */
  }
 
  if (system(editor);
}

...

) == -1) {
    /* Handle error */
  }
}

Compliant Solution (Linux)

Some systems (such as those with Linux kernel versions 2.6.23 and later) have an O_CLOEXEC flag that provides the close-on-exec function directly in open(). This flag is required by IEEE Std 1003.1 [IEEE Std 1003.1:2013]. In multithreaded programs, this flag should be used, if possible, because it prevents a timing hole between open() and fcntl() when using FD_CLOEXEC, during which another thread can create a child process while the file descriptor does not have close-on-exec set.

Code Block
bgColor#ccccff
langc
#include <stdio.h>
#include <stdlib.h>
 
extern const char *get_validated_editor(void);
 
void func(const char *file_name) {
  char *editor;
  int fd = open(file_name, O_RDONLY | O_CLOEXEC);
  if (fd == -1) {
    /* Handle error */
  }
 
  editor = get_validated_editor();
  if (editor == NULL) {
    /* Handle error */
  }
 
  if (system(editor) == -1) {
    /* Handle error */
  }
}

Risk Assessment

Failing to properly close files may allow unintended access to system resources, or exhaust exhaustion of, system resources.

Rule

Severity

Likelihood

Detectable

Remediation Cost

Repairable

Priority

Level

FIO42

FIO22-C

2 (medium)

1 (unlikely)

2 (medium)

P4

L3

References

Medium

Unlikely

No

No

P2

L3

Automated Detection

Tool

Version

Checker

Description

Compass/ROSE


Klocwork
Include Page
Klocwork_V
Klocwork_V

RH.LEAK


LDRA tool suite
Include Page
LDRA_V
LDRA_V

49 D

Partially implemented
Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_C-FIO22-a

Ensure resources are freed

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

Bibliography

[Dowd 2006]Chapter 10, "UNIX Processes" ("File Descriptor Leaks," pp. 582–587)
[IEEE Std 1003.1:2013]XSH, System Interfaces, open
[MSDN]Inheritance (Windows)
[NAI 1998]


...

Image Added Image Added Image Added Wiki Markup\[[Dowd 06|AA. C References#Dowd 06]\] Chapter 10, "UNIX Processes" (File Descriptor Leaks 582-587) \[[CWE 403|http://cwe.mitre.org/data/definitions/403.html]\] UNIX file descriptor leaks