C99 \[[The C11 Standard [ISO/IEC 9899:1999|AA. C References#ISO/IEC 9899-1999]\] Section 6.5.2.2 says,Wiki Markup
...
9899:2011] introduced a new term: temporary lifetime. This term still remains in the C23 Standard. Modifying an object with temporary lifetime is undefined behavior. According to subclause 6.2.4, paragraph 8 [ISO/IEC 9899:2024]
A non-lvalue expression with structure or union type, where the structure or union contains a member with array type (including, recursively, members of all contained structures and unions) refers to an object with automatic storage duration and temporary lifetime. Its lifetime begins when the expression is evaluated and its initial value is the value of the expression. Its lifetime ends when the evaluation of the containing full expression ends. Any attempt to modify an object with temporary lifetime results in undefined behavior.
This definition differs from the C99 Standard (which defines modifying the result of a function call or
...
accessing it after the next sequence point as undefined behavior) because a temporary object's lifetime ends when the evaluation containing the full expression or full declarator ends,
...
so the result of a function call can be accessed. This extension to the lifetime of a temporary also removes a quiet change to C90 and improves compatibility with C++.
C functions may not return arrays; however they may return structs or unions that contain arrays.Consequently, functions can return a pointer to an array or a struct or union that contains arrays. Consequently, in any version of C, if a function call 's return value contains an array, that array should never be accessed or modified returns by value a struct or union containing an array, do not modify those arrays within the expression containing the function call.In C99 and older, do not access an array returned by a function after the next sequence point or after the evaluation of the containing full expression or full declarator ends.
Noncompliant Code Example
The following This noncompliant code attempts to retrieve an array from a struct that is returned by a function callexample conforms to the C11 Standard; however, it fails to conform to C99. If compiled with a C99-conforming implementation, this code has undefined behavior 34 because the sequence point preceding the call to printf() comes between the call and the access by printf() of the string in the returned object.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h> struct X { char a[68]; }; struct X salutation(void) { struct X result = { "Hello" }; return result; } struct X addressee(void) { struct X result = { "world" }; return result; } int main(void) { printf("Hello%s, %s!\n", salutation().a, addressee().a); return 0; } |
Compliant Solution (C11 and newer)
This solution is problematic because of three inherent properties of C:
- In C, the lifetime of a return value ends at the next sequence point. Consequently by the time
printf()is called, thestructreturned by theaddressee()call is no longer considered valid, and may have been overwritten. - C function arguments are passed by value. As a result, copies are made of all objects generated by the arguments. For example, a copy is made of the pointer to
"Hello, %s!\n". Under most circumstances, these copies protect you from the effects of sequence points described earlier. - Finally, C implicitly converts arrays to pointers when passing them as function arguments. This means that a copy is made of the pointer to the
addresee().aarray, and that pointer copy is passed toprintf(). But the array data itself is not copied, and may no longer exist whenprintf()is called.
Consequently when printf() tries to dereference the pointer passed as its 2nd argument, it is likely to find garbage.
Implementation Details
This code compiles cleanly and runs without error under Microsoft Visual C++ Version 8.0. On GCC Compiler Version 4.2, the program compiles with a warning when the -Wall switch is used, and execution on Linux results in a segmentation fault. However, if one passes the flag --std=c99 to GCC, the program compiles with no warning and runs with no error.
Compliant Solution
compliant solution checks __STDC_VERSION__ to ensure that a pre-C11 compiler will fail to compile the code, rather than invoking undefined behavior.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h>
#if __STDC_VERSION__ < 201112L
#error This code requires a compiler supporting the C11 standard or newer
#endif
struct X { char a[8]; };
struct X salutation(void) {
struct X result = { "Hello" };
return result;
}
struct X addressee(void) {
struct X result = { "world" };
return result;
}
int main(void) {
printf("%s, %s!\n", salutation().a, addressee().a);
return 0;
} |
Compliant Solution
This compliant solution stores the structures returned by the call to addressee() before calling the printf() function. Consequently, this program conforms to both C99 and C11.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h>
struct X { char a[8]; };
struct X salutation(void) {
struct X result = { "Hello" };
return result;
}
struct X addressee(void) {
struct X result = { "world" };
return result;
}
int main(void) {
struct X my_salutation = salutation();
struct X my_addressee = addressee();
printf("%s, %s!\n", my_salutation.a, my_addressee.a);
return 0;
}
|
Noncompliant Code Example
This noncompliant code example attempts to retrieve an array and increment the array's first value. The array is part of a struct that is returned by a function call. Consequently, the array has temporary lifetime, and modifying the array is undefined behavior in both C99 and C11.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h>
struct X { int a[6]; };
struct X addressee(void) {
struct X result = { { 1, 2, 3, 4, 5, 6 } };
return result;
}
int main(void) {
printf("%x", ++(addressee().a[0]));
return 0;
}
|
Compliant Solution
This compliant solution stores the structure returned by the call to addressee() as my_x before calling the printf() function. When the array is modified, its lifetime is no longer temporary but matches the lifetime of the block in main().
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h>
struct X { int a[6]; };
struct X addressee(void) {
struct X result = { { 1, 2, 3, 4, 5, 6 } };
return result;
}
int main(void) {
struct X my_x = addressee();
printf("%x", ++(my_x.a[0]));
return 0;
}
|
Noncompliant Code Example
This noncompliant code example attempts to save a pointer to an array that is part of a struct that is returned by a function call. Consequently, the array has temporary lifetime, and using the pointer to it outside of the full expression is undefined behavior in both C99 and C11.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h>
struct X { int a[6]; };
struct X addressee(void) {
struct X result = { { 1, 2, 3, 4, 5, 6 } };
return result;
}
int main(void) {
int *my_a = addressee().a;
printf("%x", my_a[0]);
return 0;
}
|
Compliant Solution
This compliant solution stores This compliant solution store the structure returned by the call to addressee() as my_x before calling the printf() functionsaving a pointer to its array member. When the pointer is used, its lifetime is no longer temporary but matches the lifetime of the block in main().
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdio.h> struct X { charint a[6]; }; struct X addressee(void) { struct X result = { "world" { 1, 2, 3, 4, 5, 6 } }; return result; } int main(void) { struct X my_x = addressee(); int *my_a = my_x.a; printf("Hello, %s!\n%x", my_x.a[0]); return 0; } |
Risk Assessment
Attempting to access or modify an array within a function after a subsequent sequence point array or access it after its lifetime expires may result in unexpected and perhaps unintended erroneous program behavior.
Rule | Severity | Likelihood | Detectable |
|---|
Repairable | Priority | Level |
|---|---|---|
EXP35-C |
Low | Probable |
Yes |
Yes |
P6 |
L2 |
Automated Detection
Splint Version 3.1.1 can detect violations of this rule.
...
Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Astrée |
| temporary-object-modification | Partially checked | ||||||
| Axivion Bauhaus Suite |
| CertC-EXP35 | |||||||
| CodeSonar |
| LANG.CAST.ARRAY.TEMP | Array to Pointer Conversion on Temporary Object | ||||||
| Cppcheck Premium |
| premium-cert-exp35-c | |||||||
| Helix QAC |
| C0450, C0455, C0459, C0464, C0465 C++3807, C++3808 | Fully implemented | ||||||
| LDRA tool suite |
| 642 S, 42 D, 77 D | Enhanced Enforcement | ||||||
| Parasoft C/C++test |
| CERT_C-EXP35-a | Do not modify objects with temporary lifetime | ||||||
| CERT-C: Rule EXP35-C | Checks for accesses on objects with temporary lifetime (rule fully covered) | |||||||
| Splint |
| ||||||||
| RuleChecker |
| temporary-object-modification | Partially checked |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| ISO/IEC TR 24772:2013 | Dangling References |
...
| to Stack Frames [DCM] | Prior to 2018-01-12: CERT: Unspecified Relationship | |
| ISO/IEC TR 24772:2013 | Side-effects and Order of Evaluation [SAM] | Prior to 2018-01-12: CERT: Unspecified Relationship |
Bibliography
...
| 9899:2024] | 6.2.4, "Storage Durations of Objects" |
...
1999|AA. C References#ISO/IEC 9899-1999]\] Section 6.5.2.2, "Function calls" \[[ISO/IEC PDTR 24772|AA. C References#ISO/IEC PDTR 24772]\] "DCM Dangling references to stack frames" and "SAM Side-effects and order of evaluation"EXP34-C. Ensure a null pointer is not dereferenced 03. Expressions (EXP) EXP36-C. Do not convert pointers into more strictly aligned pointer types