Passing invalid arguments to standard C library functions for handling I/O streams results in undefined behavior.
Why is this an issue?
The standard C library includes a number of functions for handling I/O streams. These functions put certain constraints on the values of their
parameters. The constraints include the following:
- The value for the
FILE*
-typed parameter may not be NULL
- The third argument of
fseek
must be either of SEEK_SET
, SEEK_END
, or SEEK_CUR
Failing to pass correctly constrained parameters renders them invalid and will result in undefined behavior.
#include <stdio.h>
size_t get_file_size() {
FILE *f = fopen("example_file.txt", "r");
// `f` may be NULL if `fopen` fails.
fseek(f, 0L, SEEK_END); // Leads to undefined behavior, if `f` is NULL.
size_t size = ftell(f); // Leads to undefined behavior, if `f` is NULL.
fclose(f);
return size;
}
What is the potential impact?
Using the standard C library’s functions for handling I/O streams with invalid arguments leads to undefined behavior.
When a program comprises undefined behavior, the compiler no longer needs to adhere to the language standard, and the program has no meaning
assigned to it.
Due to the resulting NULL pointer dereferences in the C library functions, the application might just crash, but in the worst case, the application
may appear to execute correctly, while losing data or producing incorrect results.
Besides affecting the application’s availability, NULL pointer dereferences may lead to code execution, in rare circumstances. If NULL is
equivalent to the 0x0 memory address that can be accessed by privileged code, writing and reading memory is possible, which compromises the integrity
and confidentiality of the application.
Hot to fix it
Ensure that the FILE*
-typed pointer parameters passed to the standard C library’s I/O stream handling functions are
non-NULL
and also any other parameters such as the third argument of fseek
carry appropriate values, namely any of
SEEK_SET
, SEEK_END
, or SEEK_CUR
.
Code examples
Noncompliant code example
#include <stdio.h>
#include <stdlib.h>
int process_file(const char *fname) {
FILE *f = fopen(fname, "r");
fseek(f, 0L, SEEK_END);
size_t size = ftell(f);
rewind(f);
char *buf = (char *)malloc(size);
// Read file into buffer ...
fclose(f);
// Process buffer ...
free(buf);
return 0;
}
Compliant solution
#include <stdio.h>
#include <stdlib.h>
int process_file(const char *fname) {
FILE *f = fopen(fname, "r");
if (!f) {
printf("Could not open file!\n");
return 1;
}
fseek(f, 0L, SEEK_END);
size_t size = ftell(f);
rewind(f);
char *buf = (char *)malloc(size);
// Read file into buffer ...
fclose(f);
// Process buffer ...
free(buf);
return 0;
}
Noncompliant code example
#include <stdio.h>
#include <stdlib.h>
void process_tmp_file() {
FILE *f = tmpfile();
size_t pos_indicator = ftell(f); // Noncompliant: `f` could be NULL.
fseek(f, 0L, 3); // Noncompliant: 3rd argument should either be SEEK_SET, SEEK_CUR or SEEK_END.
// Further file processing ...
fclose(f);
}
Compliant solution
#include <stdio.h>
#include <stdlib.h>
int process_tmp_file() {
FILE *f = tmpfile();
if (!f) {
printf("Could not create temporary file!\n");
return 1;
}
size_t pos_indicator = ftell(f); // Compliant: `f` cannot be NULL here.
fseek(f, 0L, SEEK_END); // Compliant: 3rd argument is now a valid value.
// Further file processing ...
fclose(f);
return 0;
}
Resources
Standards
Related rules
- S3807 ensures that appropriate arguments are passed to C standard library functions
- S5488 ensures that appropriate arguments are passed to UNIX/POSIX functions