Versions Compared

Key

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

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

Section Subclause 7.1921.9.2 of C99 the C Standard [ISO/IEC 9899:1999 states 2011] specifies 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 footnote 268 of Section subclause 7.1921.3 of C99 ISO/IEC 9899:1999 has this to say: says:

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 Seeking to the end of a binary stream opened in binary mode with fseek() is not meaningfully supported , thereby resulting in undefined behavior and should be avoided.and is not a recommended method for computing the size of a file.

Subclause 7.21Section 7.19.9.4 of C99 the C Standard [ISO/IEC 9899:1999 states 2011] specifies 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 Consequently, the return value of ftell() for streams opened in text mode is useful only should never be used for offset calculations other than in calls to fseek() and should not be used for any other purpose.

POSIX [IEEE Std 1003.1:2013] provides several guarantees that the problems described in the C Standard do not occur on POSIX systems.

First, the fopen page says:

The character 'b' shall have no effect, but is allowed for ISO C standard conformance.

This guarantees that binary files are treated the same as text files in POSIX.

Second, the fwrite page says:

For each object, size calls shall be made to the fputc() function, taking the values (in order) from an array of unsigned char exactly overlaying the object. The file-position indicator for the stream (if defined) shall be advanced by the number of bytes successfully written.

This means that the file position indicator, and consequently the file size, is directly based on the number of bytes actually written to a file.

Noncompliant Code Example (Binary File)

This noncompliant code example attempts to open a binary file in binary mode and use fseek() and ftell() to obtain the file size. This code is noncompliant on systems that do not provide the same guarantees as POSIX. On these systems, setting the file position indicator to the end of the file using fseek() is not guaranteed to work for a binary stream, and consequently, the amount of memory allocated may be incorrect, leading to a potential vulnerability.

Code Block
bgColor#FFCCCC
borderStylelangdashedc

FILE *fp;
long file_size;
char *buffer;

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

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

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

/* allocate memory */
buffer = (char*)malloc(file_size);
if (buffer == NULL) {
  /* Handle error */
}

/* 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)

Compliant Solution (POSIX ftello())

If the code needs to handle large files, it is preferable to use fseeko() and ftello() because, for some implementations, they can handle larger file offsets than fseek() and ftell() can handle. If they are used, the file_size variable should have type off_t to avoid the possibility of overflow when assigning the return value of ftello() to it. This solution works only with regular filesThis compliant solution uses fstat() instead to obtain the size of the binary file.

Code Block
bgColor#CCCCFF
borderStylelangdashedc

FILE* *fp;
longint fd;
off_t file_size;
char *buffer;
struct stat stbufst;
int  fd;

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

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

/* Ensure that the file is a regular file */
if ((fstat(fd, &stbufst) !== -1 0) || (!S_ISREG(st.st_mode))) {
  /* Handle error */
}
 
if (fseeko(fp, 0 , SEEK_END) != 0) {
  /* Handle Errorerror */
}
  
file_size = stbuf.st_size;

 ftello(fp);
if (file_size == -1) {
  /* allocateHandle memoryerror */
}
 
buffer = (char*)malloc(file_size);
if (buffer == NULL) {
  /* continueHandle error */
}

/* ... */ 

Noncompliant Code Example

Compliant Solution (POSIX fstat())

This compliant solution uses the size provided by the POSIX fstat() function, rather than by This noncompliant code example attempts to open a text file in text mode and use fseek() and ftell(), to obtain the file sizesize of the binary file. This solution works only with regular files.

Code Block
bgColor#FFCCCC#CCCCFF
borderStylelangdashedc
off_t 
FILE *fp;
long file_size;
char *buffer;

/*struct initialize file_name */

fpstat stbuf;
int fd;
 
fd = fopenopen("foo.txtbin", "r"O_RDONLY);
if (fpfd == NULL-1) {
  /* Handle Errorerror */
}
 
if (fseek(fstat(fpfd, 0 , SEEK_END&stbuf) != 0) || (!S_ISREG(stbuf.st_mode))) {
  /* Handle Errorerror */
}
 
file_size = ftell(fpstbuf.st_size;
 
buffer = (char*)malloc(file_size);
if (file_sizebuffer == -1NULL) {
  /* Handle Errorerror */
}

/* 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 Solution (Windows)

This compliant solution uses fstatthe Windows _filelength() instead function to obtain determine the size of the text file on a 32-bit operating system. For a 64-bit operating system, consider using _filelengthi64 instead.

Code Block
bgColor#CCCCFF
borderStylelangdashedc

FILEint *fpfd;
long file_size;
char *buffer;
struct stat stbuf;
int fd;

/* initialize file_name */

fd = open("foo.txt", O_RDONLY
_sopen_s(&fd, "foo.bin", _O_RDONLY, _SH_DENYRW, _S_IREAD);
if (fd == -1) {
  /* Handle Errorerror */
}

fpfile_size = fdopen_filelength(fd, "r");
if (fpfile_size == NULL-1) {
  /* Handle Errorerror */
}

ifbuffer = (fstat(fd, &stbuf)char*)malloc(file_size);
if (buffer == -1NULL) {
  /* Handle Errorerror */
}
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().

Compliant Solution (Windows)

This compliant solution uses the Windows GetFileSizeEx() function to determine the size of the file on a 32- or 64-bit operating systemThe code was compiled with Microsoft Visual Studio 2008 SP1:

Code Block
bgColor#F0F0F0#CCCCFF
borderStylelangdashedc

FILE *fpHANDLE file;
LARGE_INTEGER file_size;
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" *buffer;

 
file = CreateFile(TEXT("foo.bin"), GENERIC_READ, 0, NULL, 
                   OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (fpINVALID_FILE_HANDLE == NULLfile) {
  /* Handle Errorerror */
}

/* Read 10 (n-1) bytes */
 
if (fgets!GetFileSizeEx(afile, 11, fp) == NULL&file_size)) {
  /* Handle Errorerror */
}
 
offset = ftell(fp);
if (offset == -1) {
 /* Handle Error */
}

printf("offset = %ld\n", offset); /* 
 * Note: 32-bit portability issue with LARGE_INTEGER
 * truncating to a size_t.
 */
buffer = (char*)malloc(file_size);
if (buffer == NULL) {
  /* Prints out 0.Handle error */ 
}

/* continue... */ 

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

offset = 0

Noncompliant Code Example (Text File)

This noncompliant code example attempts to open a text file in text mode and use fseek() and ftell() to obtain the file size: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.

Code Block
bgColor#F0F0F0#FFCCCC
borderStylelangdashedc

FILE *fp;
char a[11]long file_size;
longchar offset*buffer;

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

/* Read 10 (n-1) bytes */
if (fgetsfseek(afp, 0 11, fpSEEK_END) !== NULL0) {
  /* Handle Errorerror */
}

offsetfile_size = ftell(fp);
if (offsetfile_size == -1) {
  /* Handle Errorerror */
}

printf("offset = %ld\n", offset); buffer = (char*)malloc(file_size);
if (buffer == NULL) {
  /* PrintsHandle outerror 10. */ 
}

/* continue... */ 

However, the file position indicator returned by ftell() with a file opened in text mode is useful only 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.

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.

Compliant Solution (Windows)

The compliant solution used for binary files on Windows can also be used for text files.

Compliant Solution (POSIX)

Because binary files are treated the same as text files in POSIX, either compliant solution can be used for determining the size of a binary file under POSIX to determine the size of a text file as well.

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 for determining 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

Detectable

Repairable

Priority

Level

FIO19-C

low

Low

Unlikely

unlikely

Yes

medium

Yes

P2

P3

L3

References

Automated Detection

Tool

Version

Checker

Description

LDRA tool suite
Include Page
LDRA_V
LDRA_V
44 SEnhanced Enforcement

Bibliography

[IEEE Std 1003.1:2013]XSH, System Interfaces, fopen
XSH, System Interfaces, fwrite
[ISO/IEC 9899:

...

2011]Section 7.

...

21.

...

3, "

...

Files"

...

Section 7.

...

21.9.2, "The fseek Function"
Section 7.

...

21.9.4, "The ftell

...

Function"
[MSDN]"ftell"


...

Image Added Image Added Image Added