CWE Rule 825
Description
Rule Description
The program dereferences a pointer that contains a location for memory that was previously valid, but is no longer valid.
Polyspace Implementation
The rule checker checks for these issues:
Accessing object with temporary lifetime
Deallocation of previously deallocated pointer
Environment pointer invalidated by previous operation
Pointer or reference to stack variable leaving scope
Use of automatic variable as putenv-family function argument
Use of previously freed pointer
Examples
Accessing object with temporary lifetime
This issue occurs when you attempt to read from or write to an object with temporary lifetime that is returned by a function call. In a structure or union returned by a function, and containing an array, the array members are temporary objects. The lifetime of temporary objects ends:
When the full expression or full declarator containing the call ends, as defined in the C11 Standard.
After the next sequence point, as defined in the C90 and C99 Standards. A sequence point is a point in the execution of a program where all previous evaluations are complete and no subsequent evaluation has started yet.
For C++ code, Accessing object with temporary lifetime raises a defect only when you write to an object with a temporary lifetime.
If the temporary lifetime object is returned by address, no defect is raised.
Modifying objects with temporary lifetime is undefined behavior and can cause abnormal program termination and portability issues.
Assign the object returned from the function call to a local variable. The content of the temporary lifetime object is copied to the variable. You can now modify it safely.
#include <stdio.h> #include <assert.h> #include <stdlib.h> #include <string.h> #define SIZE6 6 struct S_Array { int t; int a[SIZE6]; }; struct S_Array func_temp(void); /* func_temp() returns a struct value containing * an array with a temporary lifetime. */ int func(void) { /*Writing to temporary lifetime object is undefined behavior */ return ++(func_temp().a[0]); //Noncompliant } void main(void) { (void)func(); }
In this example, func_temp()
returns by value a structure with
an array member a
. This member has temporary lifetime.
Incrementing it is undefined behavior.
One possible correction is to assign the return of the call to
func_temp()
to a local variable. The content of the
temporary object a
is copied to the variable, which you can
safely increment.
#include <stdio.h> #include <assert.h> #include <stdlib.h> #include <string.h> #define SIZE6 6 struct S_Array { int t; int a[SIZE6]; }; struct S_Array func_temp(void); int func(void) { /* Assign object returned by function call to *local variable */ struct S_Array s = func_temp(); /* Local variable can safely be *incremented */ ++(s.a[0]); return s.a[0]; } void main(void) { (void)func(); }
Deallocation of previously deallocated pointer
This issue occurs when a block of memory is freed more than
once using the free
function without an intermediate
allocation.
When a pointer is allocated dynamic memory with malloc
,
calloc
or realloc
, it points to a memory
location on the heap. When you use the free
function on this
pointer, the associated block of memory is freed for reallocation. Trying to free
this block of memory can result in a segmentation fault.
The fix depends on the root cause of the defect. See if you intended to allocate a
memory block to the pointer between the first deallocation and the second.
Otherwise, remove the second free
statement.
As a good practice, after you free a memory block, assign the corresponding pointer to NULL. Before freeing pointers, check them for NULL values and handle the error. In this way, you are protected against freeing an already freed block.
#include <stdlib.h> void allocate_and_free(void) { int* pi = (int*)malloc(sizeof(int)); if (pi == NULL) return; *pi = 2; free(pi); free (pi); //Noncompliant /* Defect: pi has already been freed */ }
The first free
statement
releases the block of memory that pi
refers to.
The second free
statement on pi
releases
a block of memory that has been freed already.
One possible correction is to remove the second free
statement.
#include <stdlib.h> void allocate_and_free(void) { int* pi = (int*)malloc(sizeof(int)); if (pi == NULL) return; *pi = 2; free(pi); /* Fix: remove second deallocation */ }
#include <stdlib.h> void reshape(char *buf, size_t size) { char *reshaped_buf = (char *)realloc(buf, size); if (reshaped_buf == NULL) { free(buf); //Noncompliant } }
In this example, the argument size
of the reshape()
function can be zero and result in a zero-size reallocation with realloc()
.
In some implementations such as the GNU® library, zero-size reallocations free the memory leading to a double free
defect.
One possible correction is to check size argument of realloc()
for zero values before use. If the size argument is zero, you can simply free the memory instead of reallocating it.
#include <stdlib.h> void reshape(char *buf, size_t size) { if (size != 0) { char *reshaped_buf = (char *)realloc(buf, size); if (reshaped_buf == NULL) { free(buf); } } else { free(buf); } }
Environment pointer invalidated by previous operation
This issue occurs when you use the third argument of main() in a hosted environment to access the environment after an operation modifies the environment. In a hosted environment, many C implementations support the nonstandard syntax:
main (int argc, char *argv[], char *envp[])
setenv
or putenv
family function
modifies the environment pointed to by *envp
.When you modify the environment through a call to a setenv
or
putenv
family function, the environment memory can
potentially be reallocated. The hosted environment pointer is not updated and might
point to an incorrect location. A call to this pointer can return unexpected results
or cause an abnormal program termination.
Do not use the hosted environment pointer. Instead, use global external variable
environ
in Linux®, _environ
or _wenviron
in
Windows®, or their equivalent. When you modify the environment, these variables
are updated.
#include <stdio.h> #include <stdlib.h> extern int check_arguments(int argc, char **argv, char **envp); extern void use_envp(char **envp); /* envp is from main function */ int func(char **envp) { /* Call to setenv may cause environment *memory to be reallocated */ if (setenv(("MY_NEW_VAR"),("new_value"),1) != 0) { /* Handle error */ return -1; } /* envp not updated after call to setenv, and may *point to incorrect location. **/ if (envp != ((void *)0)) { //Noncompliant use_envp(envp); /* No defect on second access to *envp because defect already raised */ } return 0; } void main(int argc, char **argv, char **envp) { if (check_arguments(argc, argv, envp)) { (void)func(envp); } }
In this example, envp
is accessed inside
func()
after a call to setenv
that can
reallocate the environment memory. envp
can point to an incorrect
location because it is not updated after setenv
modifies the
environment. No defect is raised when use_envp()
is called
because the defect is already raised on the previous line of code.
environ
One possible correction is to access the environment by using a variable that
is always updated after a call to setenv
. For instance, in
the following code, the pointer envp
is still available from
main()
, but the environment is accessed in
func()
through the global external variable
environ
.
#include <stdio.h> #include <stdlib.h> extern char **environ; extern int check_arguments(int argc, char **argv, char **envp); extern void use_envp(char **envp); int func(void) { if (setenv(("MY_NEW_VAR"), ("new_value"),1) != 0) { /* Handle error */ return -1; } /* Use global external variable environ *which is always updated after a call to setenv */ if (environ != NULL) { use_envp(environ); } return 0; } void main(int argc, char **argv, char **envp) { if (check_arguments(argc, argv, envp)) { (void)func(); } }
Pointer or reference to stack variable leaving scope
This issue occurs when a pointer or reference to a local variable leaves the scope of the variable. For instance:
A function returns a pointer to a local variable.
A function performs the assignment
globPtr = &locVar
.globPtr
is a global pointer variable andlocVar
is a local variable.A function performs the assignment
*paramPtr = &locVar
.paramPtr
is a function parameter that is, for instance, anint**
pointer andlocVar
is a localint
variable.A C++ method performs the assignment
memPtr = &locVar
.memPtr
is a pointer data member of the class the method belongs to.locVar
is a variable local to the method.(C++11 and later) A function returns a lambda expression object that captures local variables of the function by reference.
The defect also applies to memory allocated using the
alloca
function. The defect does not apply to static, local
variables. Polyspace® assumes that the local objects within a function definition are in the
same scope.
Local variables are allocated an address on the stack. Once the scope of a local variable ends, this address is available for reuse. Using this address to access the local variable value outside the variable scope can cause unexpected behavior.
If a pointer to a local variable leaves the scope of the variable, Polyspace Bug Finder™ highlights the defect. The defect appears even if you do not use the address stored in the pointer. For maintainable code, it is a good practice to not allow the pointer to leave the variable scope. Even if you do not use the address in the pointer now, someone else using your function can use the address, causing undefined behavior.
Do not allow a pointer or reference to a local variable to leave the variable scope.
void func2(int *ptr) { *ptr = 0; } int* func1(void) { int ret = 0; //Noncompliant return &ret ; } void main(void) { int* ptr = func1() ; func2(ptr) ; }
In this example, func1
returns a pointer
to local variable ret
.
In main
, ptr
points to
the address of the local variable. When ptr
is
accessed in func2
, the access is illegal because
the scope of ret
is limited to func1
,
auto createAdder(int amountToAdd) { int addThis = amountToAdd; //Noncompliant auto adder = [&] (int initialAmount) { return (initialAmount + addThis); }; return adder; } void func() { auto AddByTwo = createAdder(2); int res = AddByTwo(10); }
In this example, the createAdder
function defines a lambda expression
adder
that captures the local variable addThis
by
reference. The scope of addThis
is limited to the
createAdder
function. When the object returned by
createAdder
is called, a reference to the variable
addThis
is accessed outside its scope. When accessed in this way, the value
of addThis
is undefined.
If a function returns a lambda expression object, avoid capturing local variables by reference in the lambda object. Capture the variables by copy instead.
Variables captured by copy have the same lifetime as the lambda object, but variables captured by reference often have a smaller lifetime than the lambda object itself. When the lambda object is used, these variables accessed outside scope have undefined values.
auto createAdder(int amountToAdd) { int addThis = amountToAdd; auto adder = [=] (int initialAmount) { return (initialAmount + addThis); }; return adder; } void func() { auto AddByTwo = createAdder(2); int res = AddByTwo(10); }
Use of automatic variable as putenv-family function argument
This issue occurs when the argument of a putenv
-family function
is a local variable with automatic duration.
The function putenv(char *string)
inserts a pointer to its supplied
argument into the environment array, instead of making a copy of the argument. If the
argument is an automatic variable, its memory can be overwritten after the function
containing the putenv()
call returns. A subsequent call to
getenv()
from another function returns the address of an out-of-scope
variable that cannot be dereferenced legally. This out-of-scope variable can cause
environment variables to take on unexpected values, cause the program to stop responding, or
allow arbitrary code execution vulnerabilities.
Use setenv()
/unsetenv()
to set and unset
environment variables. Alternatively, use putenv
-family function
arguments with dynamically allocated memory, or, if your application has no reentrancy
requirements, arguments with static duration. For example, a single thread execution with no
recursion or interrupts does not require reentrancy. It cannot be called (reentered) during
its execution.
#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE1024 1024 void func(int var) { char env[SIZE1024]; int retval = sprintf(env, "TEST=%s", var ? "1" : "0"); if (retval <= 0) { /* Handle error */ } /* Environment variable TEST is set using putenv(). The argument passed to putenv is an automatic variable. */ retval = putenv(env); //Noncompliant if (retval != 0) { /* Handle error */ } }
In this example, sprintf()
stores the character string
TEST=var
in env
. The value of the environment
variable TEST
is then set to var
by using
putenv()
. Because env
is an automatic variable, the
value of TEST
can change once func()
returns.
static
Variable for Argument of
putenv()
Declare env
as a static-duration variable. The memory location of
env
is not overwritten for the duration of the program, even after
func()
returns.
#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE1024 1024 void func(int var) { /* static duration variable */ static char env[SIZE1024]; int retval = sprintf(env,"TEST=%s", var ? "1" : "0"); if (retval <= 0) { /* Handle error */ } /* Environment variable TEST is set using putenv() */ retval=putenv(env); if (retval != 0) { /* Handle error */ } }
setenv()
to Set Environment Variable
ValueTo set the value of TEST
to var
, use
setenv()
.
#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE1024 1024 void func(int var) { /* Environment variable TEST is set using setenv() */ int retval = setenv("TEST", var ? "1" : "0", 1); if (retval != 0) { /* Handle error */ } }
Use of previously freed pointer
This issue occurs when you access a
block of memory after freeing the block using the free
function.
When a pointer is allocated dynamic memory with malloc
,
calloc
or realloc
, it points to a memory
location on the heap. When you use the free
function on this
pointer, the associated block of memory is freed for reallocation. Trying to access
this block of memory can result in unpredictable behavior or even a segmentation
fault.
The fix depends on the root cause of the defect. See if you intended to free the memory later or allocate another memory block to the pointer before access.
As a good practice, after you free a memory block, assign the corresponding pointer to NULL. Before dereferencing pointers, check them for NULL values and handle the error. In this way, you are protected against accessing a freed block.
#include <stdlib.h> #include <stdio.h> int increment_content_of_address(int base_val, int shift) { int j; int* pi = (int*)malloc(sizeof(int)); if (pi == NULL) return 0; *pi = base_val; free(pi); j = *pi + shift; //Noncompliant /* Defect: Reading a freed pointer */ return j; }
The free
statement releases
the block of memory that pi
refers to. Therefore,
dereferencingpi
after the free
statement
is not valid.
One possible correction is to free the pointer pi
only
after the last instance where it is accessed.
#include <stdlib.h> int increment_content_of_address(int base_val, int shift) { int j; int* pi = (int*)malloc(sizeof(int)); if (pi == NULL) return 0; *pi = base_val; j = *pi + shift; *pi = 0; /* Fix: The pointer is freed after its last use */ free(pi); return j; }
Check Information
Category: Pointer Issues |
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 (한국어)