C99 Section 6.5.2.2 says [[ISO/IEC 9899:1999]],
If an attempt is made to modify the result of a function call or to access it after the next sequence point, the behavior is undefined.
C functions to returning arrays; however it is possible to return structs or unions that contain arrays.
Consequently, if a function call's return value contains an array, that array should never be accessed or modified within the expression.
Noncompliant Code Example
The following noncompliant code attempts to retrieve an array from a struct
that is returned by a function call.
#include <stdio.h> struct X { char a[6]; }; struct X addressee(void) { struct X result = { "world" }; return result; } int main(void) { printf("Hello, %s!\n", addressee().a); return 0; }
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, thestruct
returned 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 one 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().a
array, 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 will likely 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
This compliant solution store the structure returned by the call to addressee()
as my_x
before calling the printf()
function.
#include <stdio.h> struct X { char a[6]; }; struct X addressee(void) { struct X result = { "world" }; return result; } int main(void) { struct X my_x = addressee(); printf("Hello, %s!\n", my_x.a); return 0; }
Risk Assessment
Attempting to access or modify an array within a function after a subsequent sequence point may result in unexpected and perhaps unintended program behavior.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
EXP35-C |
low |
probable |
medium |
P4 |
L3 |
Automated Detection
Splint Version 3.1.1 can detect violations of this rule.
GCC Compiler can detect violations of this rule when the -Wall
flag is used.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[ISO/IEC 9899:1999]] Section 6.5.2.2, "Function calls"
[[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