Versions Compared

Key

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

...

In this example the programmer 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, the fact that both processes share a file descriptor means that 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. Independent of what the programmer is trying to accomplish with this code, this code is incorrect because it contains a race condition.

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

int fd = open(filename, O_RDWR);
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);
}

...

In this compliant solution, the programmer closes the file descriptor in the child after forking and then reopens it, ensuring that the file has not been modified in the meantime. See recommendation POS01-C. Check for the existence of links when dealing with files for details.

Code Block
bgColor#ccccff
langc
char c;

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);
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);

pid = fork();
if (pid == -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, &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*/
  read(fd, &c, 1);
  printf("parent:%c\n", c);
  close(fd);
}

...