CERT C++: EXP54-CPP
Do not access an object outside of its lifetime
Description
Rule Definition
Do not access an object outside of its lifetime.1
Polyspace Implementation
The rule checker checks for these issues:
Non-initialized pointer.
Non-initialized variable.
Use of previously freed pointer.
Pointer or reference to stack variable leaving scope.
Accessing object with temporary lifetime.
Extend Checker
You can extend the checker in the following ways:
Polyspace® does not flag passing pointers to noninitialized variables to functions. To detect noninitialized variables that are passed to functions by pointers, extend the checker by using the option
-code-behavior-specification
. See Extend Checkers for Initialization to Check Function Arguments Passed by Pointers.If a variable in your code is non-initialized only for certain system input values, you can see one possible combination of input values causing the defect. See Extend Bug Finder Checkers to Find Defects from Specific System Input Values.
Examples
Non-initialized pointer
Non-initialized pointer occurs when a pointer is not assigned an address before dereference.
Unless a pointer is explicitly assigned an address, it points to an unpredictable location.
The fix depends on the root cause of the defect. For instance, you assigned an address to the pointer but the assignment is unreachable.
Often the result details show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show the event history, you can trace back using right-click options in the source code and see previous related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface.
See examples of fixes below. It is a good practice to initialize a pointer to NULL when declaring the pointer.
If you do not want to fix the issue, add comments to your result or code to avoid another review. See:
Address Results in Polyspace User Interface Through Bug Fixes or Justifications if you review results in the Polyspace user interface.
Address Results in Polyspace Access Through Bug Fixes or Justifications (Polyspace Access) if you review results in a web browser.
Annotate Code and Hide Known or Acceptable Results if you review results in an IDE.
#include <stdlib.h> int* assign_pointer(int* prev) { int j = 42; int* pi; if (prev == nullptr) { pi = new int; if (pi == nullptr) return NULL; } *pi = j; //Noncompliant return pi; }
If prev
is not nullptr
, the pointer
pi
is not assigned an address. However,
pi
is dereferenced on every execution paths,
irrespective of whether prev
is
nullptr
or not.
One possible correction is to assign an address to pi
when
prev
is not nullptr
.
Alternatively, initialize pi
as a
nullptr
during its declaration.
#include <cstdlib> int* assign_pointer(int* prev) { int j = 42; /*Fix: Initialize pointers by using nullptr during declaration*/ int* pi = nullptr; if (prev == NULL) { pi = new int; if (pi == nullptr) return NULL; } /* Fix: Initialize pi in branches of if statement */ else pi = prev; *pi = j; return pi; }
Non-initialized variable
Non-initialized variable occurs when a variable is not initialized before its value is read.
Unless a variable is explicitly initialized, the variable value is unpredictable. You cannot rely on the variable having a specific value.
The fix depends on the root cause of the defect. For instance, you assigned a value to the variable but the assignment is unreachable or you assigned a value to the variable in one of two branches of a conditional statement. Fix the unreachable code or missing assignment.
Often the result details show a sequence of events that led to the defect. You can implement the fix on any event in the sequence. If the result details do not show the event history, you can trace back using right-click options in the source code and see previous related events. See also Interpret Bug Finder Results in Polyspace Desktop User Interface.
See examples of fixes below. It is a good practice to initialize a variable at declaration.
If you do not want to fix the issue, add comments to your result or code to avoid another review. See:
Address Results in Polyspace User Interface Through Bug Fixes or Justifications if you review results in the Polyspace user interface.
Address Results in Polyspace Access Through Bug Fixes or Justifications (Polyspace Access) if you review results in a web browser.
Annotate Code and Hide Known or Acceptable Results if you review results in an IDE.
int get_sensor_value(void) { extern int getsensor(void); int command; int val; command = getsensor(); if (command == 2) { val = getsensor(); } return val; //Noncompliant }
If command
is not 2, the
variable val
is unassigned. In this case, the return
value of function get_sensor_value
is undetermined.
One possible correction is to initialize val
during
declaration so that the initialization is not bypassed on some execution paths.
int get_sensor_value(void) { extern int getsensor(void); int command; /* Fix: Initialize val */ int val=0; command = getsensor(); if (command == 2) { val = getsensor(); } return val; }
val
is assigned an initial value of 0. When command
is
not equal to 2, the function get_sensor_value
returns
this value.
Use of previously freed pointer
Use of previously freed
pointer occurs when you access a block of memory after
deallocating the block, for instance, by using the free
function or the delete
operator.
When a pointer is allocated dynamic memory
by using the functions malloc
, calloc
,
realloc
or the operator new
, it points
to a memory location on the heap. When you use the free
function or the delete
operator on this pointer, the
associated block of memory is deallocated. 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 deallocate the memory later or allocate another memory block to the pointer before access.
As a good practice, after you deallocate a
memory block, assign the corresponding pointer to nullptr
.
Before dereferencing pointers, check if they are nullptr
and
handle the error. In this way, you are protected against accessing a deallocated
block.
#include <cstdlib> int increment_content_of_address(int base_val, int shift) { int j; int* pi = new int; if (pi == NULL) return 0; *pi = base_val; delete pi; j = *pi + shift; //Noncompliant return j; }
The delete
operator deallocates the block of memory that
pi
refers to. Therefore,
dereferencingpi
after the delete
pi;
statement is not valid.
One possible correction is to deallocate the pointer pi
only after the last instance where it is accessed.
#include <cstdlib> int increment_content_of_address(int base_val, int shift) { int j; int* pi = new int; if (pi == NULL) return 0; *pi = base_val; j = *pi + shift; *pi = 0; /* Fix: The pointer is deallocated after its last use */ delete pi; return j; }
std::unique_ptr
Another possible correction is to use a
std::unique_ptr
instead of a raw pointer. Smart
pointers such as std::unique_ptr
manages their own
resources. because you don't have to deallocate smart pointers explicitly,
they are not inadvertently accessed after deallocation.
#include <cstdlib> #include <memory> int increment_content_of_address(int base_val, int shift) { int j; /* Fix: A smart pointer is used*/ std::unique_ptr<int> pi(new int(3)); if (pi == nullptr) return 0; *pi = base_val; j = *pi + shift; *pi = 0; return j; }
Pointer or reference to stack variable leaving scope
Pointer or reference to stack variable leaving scope 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.
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); }
Accessing object with temporary lifetime
Accessing object with temporary lifetime 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(); }
Check Information
Group: 02. Expressions (EXP) |
Version History
Introduced in R2019aR2022a: Checker considers that all objects local to function are in same scope
The rule checker assumes that all local objects within a function definition are in the same scope. Consider this code:
void foo(){ int* p; { int tmp = 4; p = &tmp; } int q = *p;//Compliant }
tmp
, p
and q
are in the same scope.1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.
ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.
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 (한국어)