...
Section 7.21.9.2 of the C standard Standard [ISO/IEC 9899:2011] specifies the following behavior for fseek() when opening a binary file in binary mode:
...
Seeking to the end of a binary stream in binary mode with fseek() is not meaningfully supported and , as a result, is not a recommended method for computing the size of a file.
Section 7.21.9.4 of the C standard Standard [ISO/IEC 9899:2011] specifies the following behavior for ftell() when opening a text file in text mode:
...
POSIX [Open Group 2008] provides several guarantees that the problems described in the C standard Standard do not occur on POSIX systems.
...
This indicates that the file position indicator, and consequently the file size, is directly based on the number of bytes actually written to a file.
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
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 */
}
buffer = (char*)malloc(file_size);
if (buffer == NULL) {
/* Handle Errorerror */
}
/* ... */
|
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(). 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 works with regular files.
| Code Block | ||||
|---|---|---|---|---|
| ||||
FILE* fp;
int fd;
off_t file_size;
char *buffer;
struct stat st;
fd = open("foo.bin", O_RDONLY);
if (fd == -1) {
/* Handle Errorerror */
}
fp = fdopen(fd, "r");
if (fp == NULL) {
/* Handle Errorerror */
}
/* Ensure that the file is a regular file */
if ((fstat(fd, &st) != 0) || (!S_ISREG(st.st_mode))) {
/* Handle Errorerror */
}
if (fseeko(fp, 0 , SEEK_END) != 0) {
/* Handle Errorerror */
}
file_size = ftello(fp);
if (file_size == -1) {
/* Handle Errorerror */
}
buffer = (char*)malloc(file_size);
if (buffer == NULL) {
/* Handle Errorerror */
}
/* ... */ |
Compliant Solution (POSIX fstat())
This compliant solution uses the size provided by the POSIX fstat() function, rather than by fseek() and ftell(), to obtain the size of the binary file. This solution works only works with regular files.
| Code Block | ||||
|---|---|---|---|---|
| ||||
off_t file_size;
char *buffer;
struct stat stbuf;
int fd;
fd = open("foo.bin", O_RDONLY);
if (fd == -1) {
/* Handle Errorerror */
}
if ((fstat(fd, &stbuf) != 0) || (!S_ISREG(stbuf.st_mode))) {
/* Handle Errorerror */
}
file_size = stbuf.st_size;
buffer = (char*)malloc(file_size);
if (buffer == NULL) {
/* Handle Errorerror */
}
/* ... */
|
Compliant Solution (Windows)
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
int fd;
long file_size;
char *buffer;
_sopen_s(&fd, "foo.bin", _O_RDONLY, _SH_DENYRW, _S_IREAD);
if (fd == -1) {
/* Handle Errorerror */
}
file_size = _filelength(fd);
if (file_size == -1) {
/* Handle Errorerror */
}
buffer = (char*)malloc(file_size);
if (buffer == NULL) {
/* Handle Errorerror */
}
/* ... */
|
Noncompliant Code Example (Text File)
...
| Code Block | ||||
|---|---|---|---|---|
| ||||
FILE *fp;
long file_size;
char *buffer;
fp = fopen("foo.txt", "r");
if (fp == NULL) {
/* Handle Errorerror */
}
if (fseek(fp, 0 , SEEK_END) != 0) {
/* Handle Errorerror */
}
file_size = ftell(fp);
if (file_size == -1) {
/* Handle Errorerror */
}
buffer = (char*)malloc(file_size);
if (buffer == NULL) {
/* Handle Errorerror */
}
/* ... */
|
However, the file position indicator returned by ftell() with a file opened in text mode is useful 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.
...
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(), not 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 | Priority | Level |
|---|---|---|---|---|---|
FIO19-C | low | unlikely | medium | P2 | L3 |
...
Bibliography
...
| ] | Section 7.21. |
...
| 3, " |
...
| Files" |
...
Section 7.21. |
...
| 9.2, " |
...
The fseek Function"Section 7.21.9.4, "The ftell |
...
| Function" |
Bibliography
| [MSDN] | "ftell" |
| [Open Group 2008] |