You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

First, proper understanding of the difference between text mode and binary mode is important when using functions that operate on file streams (see FIO14-C. Understand the difference between text mode and binary mode with file streams).

Section 7.19.9.2 of C99 ISO/IEC 9899:1999 states the following specific behavior for fseek() when opening a binary file in binary mode:

A binary stream need not meaningfully support fseek calls with a whence value of SEEK_END.

In addition, footnote 234 of Section 7.19.3 of C99 ISO/IEC 9899:1999 has this to say:

Setting the file position indicator to end-of-file, as with fseek(file, 0, SEEK_END), has undefined behavior for a binary stream (because of possible trailing null characters) or for any stream with state-dependent encoding that does not assuredly end in the initial shift state.

These statements indicate that setting the file position indicator to end-of-file of a binary stream opened in binary mode with fseek() is not meaningfully supported, thereby resulting in undefined behavior and should be avoided.

Section 7.19.9.4 of C99 ISO/IEC 9899:1999 states the following specific behavior for ftell() when opening a text file in text mode:

For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call.

This statement indicates that the return value of ftell() for streams opened in text mode is useful only in calls to fseek() and should not be used for any other purpose.

Noncompliant Code Example

This noncompliant code example attempts to open a binary file in binary mode and use fseek() and ftell() to obtain the file size.

FILE *fp;
long file_size;
char *buffer;

fp = fopen("foo.bin", "rb");
if (fp == NULL) {
 /* Handle Error */
}

if (fseek(fp, 0 , SEEK_END) != 0) {
 /* Handle Error */
}

file_size = ftell(fp);
if (file_size == -1) {
  /* Handle Error */
}

/* allocate memory */
buffer = (char*)malloc(file_size);

/* continue */ 

However, setting the file position indicator to the end of the file with fseek() has undefined behavior for a binary stream and consequently the amount of memory allocated may be incorrect, leading to a potential vulnerability.

Compliant Code Example (UNIX and Windows)

This compliant solution uses fstat() instead to obtain the size of the binary file.

FILE *fp;
long file_size;
char *buffer;
struct stat stbuf;
int fd;

fd = open("foo.bin", O_RDONLY);
if (fd == -1) {
 /* Handle Error */
}

fp = fdopen(fd, "rb");
if (fp == NULL) {
 /* Handle Error */
}

if (fstat(fd, &stbuf) == -1) {
 /* Handle Error */
}

file_size = stbuf.st_size;

/* allocate memory */
buffer = (char*)malloc(file_size);

/* continue */ 

Noncompliant Code Example

This noncompliant code example attempts to open a text file in text mode and use fseek() and ftell() to obtain the file size.

FILE *fp;
long file_size;
char *buffer;

/* initialize file_name */

fp = fopen("foo.txt", "r");
if (fp == NULL) {
  /* Handle Error */
}

if (fseek(fp, 0 , SEEK_END) != 0) {
  /* Handle Error */
}

file_size = ftell(fp);
if (file_size == -1) {
  /* Handle Error */
}

/* allocate memory */
buffer = (char*)malloc(file_size);

/* continue */ 

However, the file position indicator returned by ftell() with a file opened in text mode is only useful in calls to fseek(). As such, the value of file_size may not necessarily be a meaningful measure of the number of characters in the file and consequently the amount of memory allocated may be incorrect, leading to a potential vulnerability.

Compliant Code Example (UNIX and Windows)

This compliant solution uses fstat() instead to obtain the size of the text file.

FILE *fp;
long file_size;
char *buffer;
struct stat stbuf;
int fd;

/* initialize file_name */

fd = open("foo.txt", O_RDONLY);
if (fd == -1) {
  /* Handle Error */
}

fp = fdopen(fd, "r");
if (fp == NULL) {
  /* Handle Error */
}

if (fstat(fd, &stbuf) == -1) {
 /* Handle Error */
}
file_size = stbuf.st_size;

/* allocate memory */
buffer = (char*)malloc(file_size);

/* continue */ 

Implementation Details

MSDN MSDN has the following to say about ftell():

The value returned by ftell may not reflect the physical byte offset for streams opened in text mode, because text mode causes carriage return-linefeed translation. Use ftell with fseek to return to file locations correctly.

Again, this indicates that the return value of ftell() for streams opened in text mode is useful only in calls to fseek() and should not be used for any other purpose.

The following code example attempts to read in 10 bytes from a text file opened in text mode. It obtains the current value of the file-position indicator by using ftell().

The code was compiled with Microsoft Visual Studio 2008 SP1:

FILE *fp;
char a[11];
long offset;

/* The contents of foo.txt are: 0123456789 repeated 10 times separated by line feeds (ie \n)*/
fp = fopen( "foo.txt", "r" );
if (fp == NULL) {
  /* Handle Error */
}

/* Read 10 (n-1) bytes */
if (fgets(a, 11, fp) == NULL) {
  /* Handle Error */
}

offset = ftell(fp);
if (offset == -1) {
 /* Handle Error */
}

printf("offset = %ld\n", offset);   /* Prints out 0. */ 

/* continue */ 

The following is printed when this is run with the above setup:

offset = 0

However, 0 is incorrect and the correct value should be 10, as evident in the compliant example below which opens the file in binary mode.

FILE *fp;
char a[11];
long offset;

/* The contents of foo.txt are: 0123456789 repeated 10 times separated by line feeds (ie \n)*/
fp = fopen("foo.txt", "rb");
if (fp == NULL) {
  /* Handle Error */
}

/* Read 10 (n-1) bytes */
if (fgets(a, 11, fp) == NULL) {
  /* Handle Error */
}

offset = ftell(fp);
if (offset == -1) {
 /* Handle Error */
}

printf("offset = %ld\n", offset);   /* Prints out 10. */ 

/* continue */ 

Risk Assessment

Understanding the difference between text mode and binary mode with file streams is critical when working with functions that operate on them. Setting the file position indicator to end-of-file with fseek() has undefined behavior for a binary stream. In addition, the return value of ftell() for streams opened in text mode is useful only in calls to fseek(), and not to determine file sizes or for any other use. As such, fstat() or other platform equivalent functions should be used to determine the size of a file.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

FIO19-C

low

unlikely

medium

P2

L3

References

ISO/IEC 9899:1999 Section 7.19.9.2, "The fseek function"
ISO/IEC 9899:1999 Section 7.19.3, footnote 234, "Files"
ISO/IEC 9899:1999 Section 7.19.9.4, "The ftell function"
MSDN "ftell"


      09. Input Output (FIO)      

  • No labels