Many UNIX/POSIX functions put certain constraints on the values of their parameters. The behavior for some of those UNIX/POSIX functions is not
defined but instead, their behavior is implementation-defined, if one passes incorrectly constrained parameters. This may lead to undefined behavior
depending on a function’s concrete implementation. The constraints include the following:
- Allocation sizes of
calloc, malloc, realloc, reallocf, alloca and
valloc must be strictly positive
-
open and openat should be called with a flag that contains one of the access modes: O_RDONLY,
O_WRONLY, or O_RDWR
-
open and openat with flag O_CREAT should be called with a third argument
- The
O_EXCL flag should be used with O_CREAT
- The first argument of
pthread_once should not have automatic storage duration and should be initialized by the constant
PTHREAD_ONCE_INIT
Failing to pass correctly constrained parameters can result in undefined behavior.
// Depending on the implementation, either NULL is returned, or the behavior is
// as if the passed size parameter were a non-zero value, except that accesses
// of the returned pointer result in undefined behavior.
void *mem = alloca(0); // May result in undefined behavior.
int fd = open("example.txt", O_ APPEND); // Access mode is missing, may result in undefined behavior.
// Third argument should be used to specify the file's access permissions.
int fd_1 = open("another_example.txt", O_CREAT); // May result in undefined behavior.
// Since `O_EXCL` prevents file creation if it already exists, the flag for
// file creation `O_CREAT` should be used in combination with `O_EXCL`.
int fd_3 = open("further_example.txt", O_EXCL); // `O_CREAT` flag is missing, may result in undefined behavior.
int counter = 0;
void inc_counter() { ++counter; }
void bar() {
// May trigger undefined behavior, because `once_control`'s storage duration
// is automatic. `counter` might be incremented with each call to `bar`.
pthread_once_t once_control = PTHREAD_ONCE_INIT;
pthread_once(&once_control, &inc_counter); // May result in undefined behavior.
}
What is the potential impact?
Using UNIX/POSIX functions with invalid arguments results in implementation-defined behavior and may lead to undefined
behavior.
When a function emits implementation-defined behavior, its behavior is unspecified and each implementation documents how the choice is made.
Implementation-defined behavior can quickly lead to undefined behavior, if the respective function is not used exactly as per the documentation.
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.
Depending on the concrete situation, the application might just crash, but in the worst case, the application may appear to execute correctly,
while losing data or producing incorrect results.