Passing NULL
to a C library function that requires non-null pointer arguments results in undefined behavior.
Why is this an issue?
The standard C library includes a variety of functions for string and general memory manipulations. Many of these functions require valid
parameters and when given values outside a function’s domain, the behavior is undefined. Functions like strlen
, memset
,
memcpy
, qsort
, fread
, etc., for instance, require non-NULL
parameters, and passing a
NULL
value introduces undefined behavior. In that case, the application may crash or, even worse, silently lose data or produce incorrect
results.
Consider the following code. If the pointer-typed variable buf
is NULL
due to a failed memory allocation, for instance,
the call to memset()
will cause undefined behavior.
#include <string.h>
void process_buffer(char *buf, size_t size) {
// Call to `memset()` may lead to undefined behavior, if `buf` is NULL.
memset(buf, 0, size);
}
Similarly, if for some reason a NULL
pointer slips through and reaches the string manipulation code shown in the following, the
application’s behavior is undefined.
#include <string.h>
int compare_strings(const char *str_a, const char *str_b) {
// Both functions `strlen()` and `strncmp()` may not receive a `NULL` pointer.
size_t len_a = strlen(str_a);
size_t len_b = strlen(str_b);
size_t min = (len_a <= len_b) ? len_a : len_b;
return strncmp(str_a, str_b, min);
}
Hence, users have to ensure that the C standard library functions' preconditions are met and valid parameters are passed.
The C library functions that are considered by this rule and that require non-NULL
pointer parameters are listed in this rule’s
Resources section.
What is the potential impact?
If an argument to one of the C library functions mentioned by this rule is a NULL
pointer, the behavior of the application is
undefined.
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.
How to fix it
Ensure that any pointer passed to any of the C library functions mentioned by this rule is not NULL
.
Code examples
Noncompliant code example
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int process_string() {
char buffer[] = "Hello, World!";
char *str = (char *)malloc(sizeof(buffer));
if (!str) {
printf("Memory allocation failed!\n");
}
// Noncompliant: 1st parameter in the subsequent call to `memcpy()` might be
// NULL due to insufficient handling of a potentially failed memory
// allocation.
memcpy(str, buffer, sizeof(buffer));
// Process dynamically alloacted `str` variable.
// ...
// Free memory, if it is no longer in use.
free(str);
return 0;
}
Compliant solution
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int process_string() {
char buffer[] = "Hello, World!";
char *str = (char *)malloc(sizeof(buffer));
if (!str) {
printf("Memory allocation failed!\n");
return 1;
}
// Compliant: 1st parameter will always denote a non-null pointer.
memcpy(str, buffer, sizeof(buffer));
// Process dynamically alloacted `str` variable.
// ...
// Free memory, if it is no longer in use.
free(str);
return 0;
}
Resources
Documentation
The list of functions considered by this rule is shown in the following:
aio_suspend, bcmp, bcopy, bsearch, bzero, confstr, encrypt, erand48,
explicit_bzero, explicit_memset, fgetpwent_r, fgets, fgetws, fread, fwrite,
getdelim, getgrgid_r, getgrnam_r, gethostbyaddr_r, gethostbyname2_r,
gethostbyname_r, gethostent_r, gethostname, getline, getlogin_r,
getnetbyaddr_r, getnetbyname_r, getnetent_r, getnetgrent_r, getopt,
getopt_long, getopt_long_only, getprotobyname_r, getprotobynumber_r,
getprotoent_r, getpwent_r, getpwnam_r, getpwuid_r, getservbyname_r,
getservbyport_r, getservent_r, iconv, initstate, jrand48, lcong48, lfind,
lio_listio, lsearch, mblen, mbrlen, mbrtowc, mbsnrtowcs, mbsrtowcs,
mbstowcs, mbtowc, memccpy, memchr, memcmp, memcpy, memmove, mempcpy,
memset, mq_receive, mq_send, mq_timedreceive, mq_timedsend, nrand48,
posix_trace_event, posix_trace_trygetnext_event, pread, preadv, preadv2,
pthread_attr_setstack, pwrite, pwritev, pwritev2, qsort, qsort_r, read,
readlink, readlinkat, readv, realpath, recv, recvfrom, regerror, regexec,
seed48, semop, semtimedop, send, sendto, setbuf, setbuffer, setvbuf,
snprintf, socketpair, std::copy (for C++), std::copy_backward (for C++),
stpcpy, strcasecmp, strcat, strcmp, strcpy, strfmon, strfmon_l, strftime,
strftime_l, strlcat, strlcpy, strlen, strncasecmp, strncat, strncmp, strncpy,
strnlen, strsep, swab, swprintf, ttyname_r, utimes, vsnprintf, vswprintf,
wcpncpy, wcsftime, wcslen, wcsncasecmp, wcsncasecmp_l, wcsncmp, wcsncpy,
wcsnlen, wcsnrtombs, wcsrtombs, wcstombs, wcswidth, wcsxfrm, wcsxfrm_l,
wmemchr, wmemcmp, wmemcpy, wmemmove, wmemcpy, wmemset, write, writev
Standards