Versions Compared

Key

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

When using the fork() commandforking a child process, file descriptors are copied between the two new processes and will cause concurrent operations to occur to the child process, which can result in concurrent operations on the file. Concurrent operations on the same file . This can cause can cause data to be read or written in a non-deterministic order to or from a file.

...

nondeterministic order, creating race conditions and unpredictable behavior.

Noncompliant Code Example

In this non compliant code example, the programmer incorrectly accesses the file descriptor after the call to fork(). Independent on wishes to open a file, read a character, fork, and then have both parent and child process read the second character of the file independently. However, because both processes share a file descriptor, one process might get the second character, and one might get the third. Furthermore, there is no guarantee the reads are atomic—the processes might get unpredictable results. Regardless of what the programmer is trying to accomplish with this code, it this code is still incorrect because it contains a race condition.

Code Block
bgColor#FFcccc
langc
char c;
intpid_t pid;

int fd = open(filename, O_RDWR,0);
if (fd == -1) {
  /* Handle error */
}
read(fd, &c, 1);
printf("root process:%c\n",c);

pid = fork();
if (pid == -1) {
  /* Handle error */
}

if (pid == 0) { /*child*/
  read(fd, &c, 1);
  printf("child:%c\n",c);
}
else { /*parent*/
  read(fd, &c, 1);
  printf("parent:%c\n",c);
}

If the file accessed has contents "abc", the output of this program could be either

Code Block
root process:a

...


parent: b

...


child: c

or

Code Block
root process: a

...


child: b

...


parent: c

This code's output cannot reliably be determined , and therefore should and should not be used.  Instead, file descriptors should not be passed through a fork() call and should be closed beforehand to prevent this possible error.

...

.

Compliant Solution

In this compliant code examplesolution, the programmer correctly closes the file descriptor before in the child after forking and then reopening reopens it, ensuring that the file descriptor in each child. In this example the programmer wanted to read the second character of the file twice, but they could have read any number of any character in the file deterministicallyhas not been modified in the meantime. See POS01-C. Check for the existence of links when dealing with files for details.

Code Block
bgColor#ccccff
langc
char c;

int pid;
pid_t pid;

/* Open file and remember file status  */
struct stat orig_st;
if (lstat( filename, &orig_st) != 0) {
  /* handle error */
}
int fd = open(filename, O_RDWR,0);
if (fd == -1) {
  /* Handle error */
}
struct stat new_st;
if (fstat(fd, &new_st) != 0) {
  /* handle error */
}
if (orig_st.st_dev != new_st.st_dev ||
    orig_st.st_ino != new_st.st_ino) {
  /* file was tampered with while opening */
}

/* file is good, operate on fd */

read(fd,&c,1);
printf("root process:%c\n",c);

closepid = fork(fd);

if (pid == fork();-1) {
  /* Handle error */
}

if (pid == 0){ /*child*/
  close(fd);

  /* Reopen file, creating new file descriptor */
  fd = open(filename, O_RDONLY);
  if (fd == -1) {
    /* Handle error */
  }
  if (fstat(fd,0); &new_st) != 0) {
    /* handle error */
  }
  if (orig_st.st_dev != new_st.st_dev ||
      orig_st.st_ino != new_st.st_ino) {
    /* file was tampered with between opens */
  }

  read(fd, &c, 1);
  read(fd, &c, 1);
  printf("child:%c\n", c);
  close(fd);
}

else { /*parent*/
  fd = open(filename,O_RDWR,0);
  read(fd, &c,1);
  read(fd,&c,1);
  printf("parent:%c\n", c);
  close(fd);
}

The output of this code is :

Code Block
root process:a

...


child:b

...


parent:b

...

Because file descriptors access files in a sequential manner, the order in which the parent and child run can affect the order of access to the file, and because file descriptors are shared across multiple processes, this allows multiple processes to read/write from a file concurrently in a semi-random order. 


Risk Assessment

Because race conditions in code are extremely hard to find, this problem might not appear during standard debugging stages of development. However, depending on what file is being read from and how important the order of read operations is, this problem can be particular dangerous.

Rule

Severity

Likelihood

Detectable

Remediation Cost

Repairable

Priority

Level

POS38-C

Medium

medium

Unlikely

unlikely

No

medium

No

P2

L3

References

Automated Detection

Tool

Version

Checker

Description

CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

BADFUNC.FORK

Use of fork

Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

DF4951, DF4952
Parasoft C/C++test

Include Page
Parasoft_V
Parasoft_V

CERT_C-POS38-a

Avoid race conditions when using fork and file descriptors

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule POS38-CChecks for file descriptor exposure to child process (rule fully covered)

Bibliography

TODO

...

Image Added Image Added Image AddedPOS32-C When data must be accessed by multiple threads, provide a mutex and guarantee no adjacent data is also accessed.