CWE Rule 690
Description
Rule Description
The product does not check for an error after calling a function that can return with a NULL pointer if the function fails, which leads to a resultant NULL pointer dereference.
Polyspace Implementation
The rule checker checks for these issues:
Returned value of a sensitive function not checked
Tainted NULL or non-null-terminated string
Unprotected dynamic memory allocation
Examples
Returned value of a sensitive function not checked
This issue occurs when you call sensitive standard functions that return information about possible errors and you do one of the following:
Ignore the return value.
You simply do not assign the return value to a variable, or explicitly cast the return value to
void
.Use an output from the function (return value or argument passed by reference) without testing the return value for errors.
The checker considers a function as sensitive if the function call is prone to failure because of reasons such as:
Exhausted system resources (for example, when allocating resources).
Changed privileges or permissions.
Tainted sources when reading, writing, or converting data from external sources.
Unsupported features despite an existing API.
The checker only considers functions where the return value indicates if the function completed without errors.
Some of these functions can perform critical tasks such as:
Set privileges (for example,
setuid
)Create a jail (for example,
chroot
)Create a process (for example,
fork
)Create a thread (for example,
pthread_create
)Lock or unlock mutex (for example,
pthread_mutex_lock
)Lock or unlock memory segments (for example,
mlock
)
If you do not check the return value of functions that perform sensitive tasks and indicate error information through their return values, your program can behave unexpectedly. Errors from these functions can propagate throughout the program causing incorrect output, security vulnerabilities, and possibly system failures.
Before continuing with the program, test the return value of critical sensitive functions.
For sensitive functions that are not critical, you can explicitly ignore a return value by
casting the function to void
. Polyspace® does not raise this defect for sensitive functions cast to void. This
resolution is not accepted for critical sensitive functions
because they perform more vulnerable tasks.
#include <pthread.h> #include <string.h> #include <stddef.h> #include <stdio.h> void initialize() { pthread_attr_t attr; pthread_attr_init(&attr);//Noncompliant } int read_file(int argc, char *argv[]) { FILE *in; if (argc != 2) { /* Handle error */ } in = fmemopen (argv[1], strlen (argv[1]), "r"); return 0; //Noncompliant }
This example shows calls to the sensitive POSIX functions
pthread_attr_init
and fmemopen
. Their return
values are ignored, causing defect.
(void)
One possible correction is to cast the function to void. This fix informs Polyspace and any reviewers that you are explicitly ignoring the return value of the sensitive function.
#include <pthread.h> #include <string.h> #include <stddef.h> #include <stdio.h> void initialize() { pthread_attr_t attr; (void)pthread_attr_init(&attr);//Compliant } int read_file(int argc, char *argv[]) { FILE *in; if (argc != 2) { /* Handle error */ } (void)fmemopen (argv[1], strlen (argv[1]), "r"); //Compliant return 0; }
One possible correction is to test the return value of
pthread_attr_init
and fmemopen
to check for
errors.
#include <pthread.h> #include <string.h> #include <stddef.h> #include <stdio.h> void initialize() { pthread_attr_t attr; int result = pthread_attr_init(&attr);//Compliant if(result != 0){ //Handle fatal error } } int read_file(int argc, char *argv[]) { FILE *in; if (argc != 2) { /* Handle error */ } in = fmemopen (argv[1], strlen (argv[1]), "r"); if (in==NULL){ // Handle error } return 0;//Compliant }
#include <pthread.h> extern void *start_routine(void *); void returnnotchecked() { pthread_t thread_id; pthread_attr_t attr; void *res; (void)pthread_attr_init(&attr); (void)pthread_create(&thread_id, &attr, &start_routine, ((void *)0)); //Noncompliant pthread_join(thread_id, &res); //Noncompliant }
In this example, two critical functions are called: pthread_create
and pthread_join
.
The return value of the pthread_create
is ignored
by casting to void, but because pthread_create
is
a critical function (not just a sensitive function), Polyspace does
not ignore this Return value of a sensitive function not
checked defect. The other critical function, pthread_join
,
returns value that is ignored implicitly. pthread_join
uses
the return value of pthread_create
, which was not
checked.
The correction for this defect is to check the return value of these critical functions to verify the function performed as expected.
#include <pthread.h> #include <stdlib.h> #define fatal_error() abort() extern void *start_routine(void *); void returnnotchecked() { pthread_t thread_id; pthread_attr_t attr; void *res; int result; (void)pthread_attr_init(&attr); result = pthread_create(&thread_id, &attr, &start_routine, NULL); if (result != 0) { /* Handle error */ fatal_error(); } result = pthread_join(thread_id, &res); if (result != 0) { /* Handle error */ fatal_error(); } }
Tainted NULL or non-null-terminated string
This issue occurs when strings from unsecure sources are used in string manipulation routines
that implicitly dereference the string buffer, for instance, strcpy
or
sprintf
.
Tainted NULL or non-null-terminated string raises no
defect for a string returned from a call to scanf
-family variadic
functions. Similarly, no defect is raised when you pass the string with a
%s
specifier to printf
-family variadic
functions.
If a string is from an unsecure source, it is possible that an attacker manipulated the string or pointed the string pointer to a different memory location.
If the string is NULL, the string routine cannot dereference the string, causing the program to crash. If the string is not null-terminated, the string routine might not know when the string ends. This error can cause you to write out of bounds, causing a buffer overflow.
Validate the string before you use it. Check that:
The string is not NULL.
The string is null-terminated
The size of the string matches the expected size.
By default, Polyspace assumes that data from external sources are tainted. See Sources of Tainting in a Polyspace Analysis. To consider any data
that does not originate in the current scope of Polyspace analysis as tainted, use the
command line option -consider-analysis-perimeter-as-trust-boundary
.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define SIZE128 128 #define MAX 40 extern void print_str(const char*); void warningMsg(void) { char userstr[MAX]; int n = read(0,userstr,MAX); char str[SIZE128] = "Warning: "; if (n != -1) strncat(str, userstr, SIZE128-(strlen(str)+1));//Noncompliant print_str(str); }
In this example, the string str
is concatenated
with the argument userstr
. The value of userstr
is
unknown. If the size of userstr
is greater than
the space available, the concatenation overflows.
One possible correction is to check the size of userstr
and
make sure that the string is null-terminated before using it in strncat
.
This example uses a helper function, sansitize_str
,
to validate the string. The defects are concentrated in this function.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define SIZE128 128 #define MAX 40 extern void print_str(const char*); int sanitize_str(char* s) { int res = 0; if (s && (strlen(s) > 0)) { // Noncompliant // - string is not null // - string has a positive and limited size // - TAINTED_STRING on strlen used as a firewall res = 1; } return res; } void warningMsg(void) { char userstr[MAX]; int n = read(0,userstr,MAX); char str[SIZE128] = "Warning: "; if (n != -1 && sanitize_str(userstr)) strncat(str, userstr, SIZE128-(strlen(str)+1)); print_str(str); }
Another possible correction is to call function errorMsg
and
warningMsg
with specific strings.
#include <stdio.h> #include <string.h> #include <stdlib.h> #define SIZE128 128 extern void print_str(const char*); void warningMsg(char* userstr) { char str[SIZE128] = "Warning: "; strncat(str, userstr, SIZE128-(strlen(str)+1)); print_str(str); } void errorMsg(char* userstr) { char str[SIZE128] = "Error: "; strncat(str, userstr, SIZE128-(strlen(str)+1)); print_str(str); } int manageSensorValue(int sensorValue) { int ret = sensorValue; if ( sensorValue < 0 ) { errorMsg("sensor value should be positive"); exit(1); } else if ( sensorValue > 50 ) { warningMsg("sensor value greater than 50 (applying threshold)..."); sensorValue = 50; } return sensorValue; }
Unprotected dynamic memory allocation
This issue occurs when you access dynamically allocated memory without first checking if the prior memory allocation succeeded.
When memory is dynamically allocated using malloc
,
calloc
, or realloc
, it returns a value
NULL
if the requested memory is not available. If the code
following the allocation accesses the memory block without checking for this
NULL
value, this access is not protected from
failures.
Check the return value of malloc
, calloc
, or realloc
for NULL before accessing the allocated memory location.
int *ptr = malloc(size * sizeof(int)); if(ptr) /* Check for NULL */ { /* Memory access through ptr */ }
#include <stdlib.h> void Assign_Value(void) { int* p = (int*)calloc(5, sizeof(int)); *p = 2; //Noncompliant /* Defect: p is not checked for NULL value */ free(p); }
If the memory allocation fails, the function calloc
returns NULL
to p
.
Before accessing the memory through p
, the code
does not check whether p
is NULL
One possible correction is to check whether p
has
value NULL
before dereference.
#include <stdlib.h> void Assign_Value(void) { int* p = (int*)calloc(5, sizeof(int)); /* Fix: Check if p is NULL */ if(p!=NULL) *p = 2; free(p); }
#include <stdlib.h> #include<string.h> typedef struct recordType { const char* id; const char* data; } RECORD; RECORD* MakerecordType(const char *id,unsigned int size){ RECORD *rec = (RECORD *)calloc(1, sizeof(RECORD)); rec->id = strdup(id); //Noncompliant const char *newData = (char *)calloc(1, size); rec->data = newData; return rec; }
In this example, the checker raises a defect when you dereference the pointer rec
without checking for a NULL
value from the prior dynamic memory allocation.
A similar issue happens with the pointer newData
. However, a defect is not
raised because the pointer is not dereferenced but simply copied over to
rec->data
. Simply copying over a possibly null pointer is not an
issue by itself. For instance, callers of the recordType_new
function
might check for NULL
value of rec->data
before
dereferencing, thereby avoiding a null pointer dereference.
Check Information
Category: Others |
Version History
Introduced in R2023a
See Also
External Websites
MATLAB Command
You clicked a link that corresponds to this MATLAB command:
Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.
Select a Web Site
Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)
- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)