Main Content

Alternating input and output from a stream without flush or positioning call

Undefined behavior for input or output stream operations

Description

This defect occurs when you perform an output operation on a file stream following an input operation or vice versa, without an intermediate repositioning of the file stream. The defect checker detects issues in file operations using one of these file pointers:

  • A FILE* pointer (for files opened using functions such as fopen()).

  • An std::basic_filebuf object (for files opened using functions such as std::fstream())

Risk

If you open a file stream for both reading and writing, you must perform an intermediate stream repositioning operation between read and write operations on the stream.

When you open a file stream using a function such as std::fstream(), an std::basic_filebuf object is created to track the position in the file stream where the next read or write operation will occur. The implementation of this object uses the C FILE* abstraction. According to the C standard, a FILE* pointer can be used to perform both input and output operations on a file stream. However, before you perform an output operation following an input (or vice versa), you must first reposition the pointer in the file stream; otherwise, the behavior is undefined.

Fix

If you open a file stream for both reading and writing, you must perform an intermediate stream repositioning operation between read and write operations on the stream.

  • For std::basic_filebuf objects, you can reposition a file stream using methods such as std::fstream<>::seekp() or std::fstream<>::seekg().

  • For FILE* pointers, you can call a file positioning function such as fseek() or fsetpos() between output and input operations (or call the fflush() function to flush the output buffer between an output and a subsequent input operation).

Examples

expand all

#include <stdio.h>
#define SIZE20 20

void initialize_data(char* data, size_t s) {};
const char *temp_filename = "/tmp/demo.txt";

void func()
{
    char data[SIZE20];
    char append_data[SIZE20];
    FILE *file;

    file = fopen(temp_filename, "a+");
    if (file == NULL)
      {
        /* Handle error. */;
      }

    initialize_data(append_data, SIZE20);

    if (fwrite(append_data, 1, SIZE20, file) != SIZE20)
      {
        (void)fclose(file);
        /* Handle error. */;
      }
	/* Read operation after write without 
	intervening flush. */
    if (fread(data, 1, SIZE20, file) < SIZE20)  
      {
          (void)fclose(file);
          /* Handle error. */;
      }

    if (fclose(file) == EOF)
      {
        /* Handle error. */;
      }
}
        
      

In this example, the file demo.txt is opened for reading and appending. After the call to fwrite(), a call to fread() without an intervening flush operation is undefined behavior.

Correction — Call fflush() Before the Read Operation

After writing data to the file, before calling fread(), perform a flush call.

#include <stdio.h>
#define SIZE20 20

void initialize_data(char* data, size_t s) {};
const char *temp_filename = "/tmp/demo.txt";


void func()
{
    char data[SIZE20];
    char append_data[SIZE20];
    FILE *file;

    file = fopen(temp_filename, "a+");
    if (file == NULL)
      {
        /* Handle error. */;
      }

    initialize_data(append_data, SIZE20);

    if (fwrite(append_data, 1, SIZE20, file) != SIZE20)
      {
        (void)fclose(file);
        /* Handle error. */;
      }
	/* Buffer flush after write and before read */
    if (fflush(file) != 0)  
      {
        (void)fclose(file);
        /* Handle error. */;
      }
    if (fread(data, 1, SIZE20, file) < SIZE20)
      {
        (void)fclose(file);
        /* Handle error. */;
      }

    if (fclose(file) == EOF)
      {
        /* Handle error. */;
      }
} 

Result Information

Group:Programming
Language: C | C++
Default: On for handwritten code, off for generated code
Command-Line Syntax: IO_INTERLEAVING
Impact: Low

Version History

Introduced in R2017b

expand all