Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
/dev/mouse
/dev/console
/dev/tty0
/dev/zero

A web Web browser that failed to check for these devices would allow an attacker to create a website Web site with image tags such as <IMG src="file:///dev/mouse"> that would lock the user's mouse.

Noncompliant Code Example

In this noncompliant code example, the user can specify a locked device or a FIFO file name, causing the program to hang on the call to fopen().

Code Block
bgColor#ffcccc
char *file_name;
FILE *file;

/* initialize file_name */

if (!fgets(file_name, sizeof(file_name), stdin)) {
  /* handleHandle error */
}

if ((file = fopen(file_name, "wb")) == NULL) {
  /* handleHandle error */
}

/* operate on file */

fclose(file);

...

When opening a FIFO with O_RDONLY or O_WRONLY set:

  • If O_NONBLOCK is set, an open() for reading only will return returns without delay. An open() for writing only will return returns an error if no process currently has the file open for reading.
  • If O_NONBLOCK is clear, an open() for reading only will block blocks the calling thread until a thread opens the file for writing. An open() for writing only will block blocks the calling thread until a thread opens the file for reading.

When opening a block special or character special file that supports non-blocking opens:

  • If O_NONBLOCK is set, the open() function will return returns without blocking for the device to be ready or available. Subsequent behaviour of the device is device-specific.
  • If O_NONBLOCK is clear, the open() function will block blocks the calling thread until the device is ready or available before returning.

Otherwise, the behavior of O_NONBLOCK is unspecified.

Once the file is open, programmers can use the POSIX lstat() and fstat() functions to obtain information about a named file , and the S_ISREG() macro to determine if the file is a regular file (see FIO05-C. Identify files using multiple file attributes).

...

Code Block
bgColor#ccccff
#ifdef O_NOFOLLOW
  #define OPEN_FLAGS O_NOFOLLOW | O_NONBLOCK
#else
  #define OPEN_FLAGS O_NONBLOCK
#endif

/* ... */

struct stat orig_st;
struct stat open_st;
int fd;
int flags;
char *file_name;

/* initialize file_name */

if (!fgets(file_name, sizeof(file_name), stdin)) {
  /* handleHandle error */
}

if ((lstat(file_name, &orig_st) != 0)
 || (!S_ISREG(orig_st.st_mode)))
{
  /* handleHandle error */
}

/* A TOCTOU race condition exists here, see below */

fd = open(file_name, OPEN_FLAGS | O_WRONLY);
if (fd == -1) {
  /* handleHandle error */
}

if (fstat(fd, &open_st) != 0) {
  /* handleHandle error */
}

if ((orig_st.st_mode != open_st.st_mode) ||
    (orig_st.st_ino  != open_st.st_ino) ||
    (orig_st.st_dev  != open_st.st_dev)) {
  /* file was tampered with */
}

/* Optional: drop the O_NONBLOCK now that we are sure
 * this is a good file */
if ((flags = fcntl(fd, F_GETFL)) == -1) {
  /* handleHandle error */
}

if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) != 0) {
  /* handleHandle error */
}

/* operateOperate on file */

close(fd);

The above code does contain an intractable TOCTOU race condition ; where under which an attacker can alter the file referenced by file_name following the call to lstat() but before the call to open(). The switch will be discovered after the file is opened, but opening the file cannot be prevented in the case where this action itself causes undesired behavior.

Essentially, an attacker can switch out a file for one of the file types shown in the table with the specified effect.
Table - File types and effect

Type

Note on effect

another regular file

The fstat() verification fails

FIFO

Either open() will return returns -1 and set setas errno to ENXIO or the open() will succeed succeeds and the fstat() verification will fail fails

symbolic link

open() will return returns -1 if O_NOFOLLOW is available, : otherwise the fstat() verification will fail fails

special device

Usually the fstat() verification will fail fails on st_mode. This can still be a problem if the device is one for which just opening (or closing) it causes something to happen. If st_mode compares equal, then the device is one whichthat, after opening, appears to be a regular file. It would then fail the fstat() verification on st_dev and st_ino (unless it happens to be the same file, as can happen with /dev/fd/* on Solaris, but this would not be a problem)

...

Code Block
bgColor#ccccff
HANDLE hFile = CreateFile(
  pFullPathName, 0, 0, NULL, OPEN_EXISTING, 0, NULL
);
if (hFile == INVALID_HANDLE_VALUE) {
  /* handleHandle error */
}
else {
  if (GetFileType(hFile) != FILE_TYPE_DISK) {
    /* handleHandle error */
  }
  /* operate on file */
}

...