Skip to content

Commit 888b054

Browse files
author
Nikita Kraiouchkine
committed
InvalidMemory2: Implement EXP35-C query
1 parent 371c778 commit 888b054

File tree

5 files changed

+339
-0
lines changed

5 files changed

+339
-0
lines changed
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# EXP35-C: Do not modify objects with temporary lifetime
2+
3+
This query implements the CERT-C rule EXP35-C:
4+
5+
> Do not modify objects with temporary lifetime
6+
7+
8+
## Description
9+
10+
The C11 Standard \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\] introduced a new term: *temporary lifetime*. Modifying an object with temporary lifetime is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). According to subclause 6.2.4, paragraph 8
11+
12+
> 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 or full declarator ends. Any attempt to modify an object with temporary lifetime results in undefined behavior.
13+
14+
15+
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++.
16+
17+
C functions may not return arrays; however, 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 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.
18+
19+
## Noncompliant Code Example
20+
21+
This noncompliant code example [conforms](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-conformingprogram) to the C11 Standard; however, it fails to conform to C99. If compiled with a C99-conforming implementation, this code has [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) 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.
22+
23+
```cpp
24+
#include <stdio.h>
25+
26+
struct X { char a[8]; };
27+
28+
struct X salutation(void) {
29+
struct X result = { "Hello" };
30+
return result;
31+
}
32+
33+
struct X addressee(void) {
34+
struct X result = { "world" };
35+
return result;
36+
}
37+
38+
int main(void) {
39+
printf("%s, %s!\n", salutation().a, addressee().a);
40+
return 0;
41+
}
42+
43+
```
44+
45+
## Compliant Solution (C11 and newer)
46+
47+
This compliant solution checks `__STDC_VERSION__` to ensure that a pre-C11 compiler will fail to compile the code, rather than invoking undefined behavior.
48+
49+
```cpp
50+
#include <stdio.h>
51+
52+
#if __STDC_VERSION__ < 201112L
53+
#error This code requires a compiler supporting the C11 standard or newer
54+
#endif
55+
56+
struct X { char a[8]; };
57+
58+
struct X salutation(void) {
59+
struct X result = { "Hello" };
60+
return result;
61+
}
62+
63+
struct X addressee(void) {
64+
struct X result = { "world" };
65+
return result;
66+
}
67+
68+
int main(void) {
69+
printf("%s, %s!\n", salutation().a, addressee().a);
70+
return 0;
71+
}
72+
```
73+
74+
## Compliant Solution
75+
76+
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.
77+
78+
```cpp
79+
#include <stdio.h>
80+
81+
struct X { char a[8]; };
82+
83+
struct X salutation(void) {
84+
struct X result = { "Hello" };
85+
return result;
86+
}
87+
88+
struct X addressee(void) {
89+
struct X result = { "world" };
90+
return result;
91+
}
92+
93+
int main(void) {
94+
struct X my_salutation = salutation();
95+
struct X my_addressee = addressee();
96+
97+
printf("%s, %s!\n", my_salutation.a, my_addressee.a);
98+
return 0;
99+
}
100+
101+
```
102+
103+
## Noncompliant Code Example
104+
105+
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](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) in both C99 and C11.
106+
107+
```cpp
108+
#include <stdio.h>
109+
110+
struct X { int a[6]; };
111+
112+
struct X addressee(void) {
113+
struct X result = { { 1, 2, 3, 4, 5, 6 } };
114+
return result;
115+
}
116+
117+
int main(void) {
118+
printf("%x", ++(addressee().a[0]));
119+
return 0;
120+
}
121+
122+
```
123+
124+
## Compliant Solution
125+
126+
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()`.
127+
128+
```cpp
129+
#include <stdio.h>
130+
131+
struct X { int a[6]; };
132+
133+
struct X addressee(void) {
134+
struct X result = { { 1, 2, 3, 4, 5, 6 } };
135+
return result;
136+
}
137+
138+
int main(void) {
139+
struct X my_x = addressee();
140+
printf("%x", ++(my_x.a[0]));
141+
return 0;
142+
}
143+
144+
```
145+
146+
## Noncompliant Code Example
147+
148+
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](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior) in both C99 and C11.
149+
150+
```cpp
151+
#include <stdio.h>
152+
153+
struct X { int a[6]; };
154+
155+
struct X addressee(void) {
156+
struct X result = { { 1, 2, 3, 4, 5, 6 } };
157+
return result;
158+
}
159+
160+
int main(void) {
161+
int *my_a = addressee().a;
162+
printf("%x", my_a[0]);
163+
return 0;
164+
}
165+
166+
```
167+
168+
## Compliant Solution
169+
170+
This compliant solution stores the structure returned by the call to `addressee()` as `my_x` before saving 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()`.
171+
172+
```cpp
173+
#include <stdio.h>
174+
175+
struct X { int a[6]; };
176+
177+
struct X addressee(void) {
178+
struct X result = { { 1, 2, 3, 4, 5, 6 } };
179+
return result;
180+
}
181+
182+
int main(void) {
183+
struct X my_x = addressee();
184+
int *my_a = my_x.a;
185+
printf("%x", my_a[0]);
186+
return 0;
187+
}
188+
189+
```
190+
191+
## Risk Assessment
192+
193+
Attempting to modify an array or access it after its lifetime expires may result in erroneous program behavior.
194+
195+
<table> <tbody> <tr> <th> Rule </th> <th> Severity </th> <th> Likelihood </th> <th> Remediation Cost </th> <th> Priority </th> <th> Level </th> </tr> <tr> <td> EXP35-C </td> <td> Low </td> <td> Probable </td> <td> Medium </td> <td> <strong>P4</strong> </td> <td> <strong>L3</strong> </td> </tr> </tbody> </table>
196+
197+
198+
## Automated Detection
199+
200+
<table> <tbody> <tr> <th> Tool </th> <th> Version </th> <th> Checker </th> <th> Description </th> </tr> <tr> <td> <a> Astrée </a> </td> <td> 22.04 </td> <td> <strong>temporary-object-modification</strong> </td> <td> Partially checked </td> </tr> <tr> <td> <a> Axivion Bauhaus Suite </a> </td> <td> 7.2.0 </td> <td> <strong>CertC-EXP35</strong> </td> <td> </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.4 </td> <td> <strong>C0450, C0455, C0459, C0464, C0465</strong> <strong>C++3807, C++3808</strong> </td> <td> </td> </tr> <tr> <td> <a> LDRA tool suite </a> </td> <td> 9.7.1 </td> <td> <strong>642 S, 42 D, 77 D</strong> </td> <td> Enhanced Enforcement </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.2 </td> <td> <strong>CERT_C-EXP35-a</strong> </td> <td> Do not modify objects with temporary lifetime </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2023a </td> <td> <a> CERT-C: Rule EXP35-C </a> </td> <td> Checks for accesses on objects with temporary lifetime (rule fully covered) </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>0450 \[U\], 0455 \[U\], 0459 \[U\],</strong> <strong>0464 \[U\], 0465 \[U\]</strong> </td> <td> </td> </tr> <tr> <td> <a> Splint </a> </td> <td> 3.1.1 </td> <td> </td> <td> </td> </tr> <tr> <td> <a> RuleChecker </a> </td> <td> 22.04 </td> <td> <strong>temporary-object-modification</strong> </td> <td> Partially checked </td> </tr> </tbody> </table>
201+
202+
203+
## Related Vulnerabilities
204+
205+
Search for [vulnerabilities](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-vulnerability) resulting from the violation of this rule on the [CERT website](https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+EXP35-C).
206+
207+
## Related Guidelines
208+
209+
[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
210+
211+
<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> ISO/IEC TR 24772:2013 </a> </td> <td> Dangling References to Stack Frames \[DCM\] </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> ISO/IEC TR 24772:2013 </a> </td> <td> Side-effects and Order of Evaluation \[SAM\] </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> </tbody> </table>
212+
213+
214+
## Bibliography
215+
216+
<table> <tbody> <tr> <td> \[ <a> ISO/IEC 9899:2011 </a> \] </td> <td> 6.2.4, "Storage Durations of Objects" </td> </tr> </tbody> </table>
217+
218+
219+
## Implementation notes
220+
221+
This implementation does not cover modification or access of the result of a function call after the next sequence point, which is undefined behavior only pre-C11.
222+
223+
## References
224+
225+
* CERT-C: [EXP35-C: Do not modify objects with temporary lifetime](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* @id c/cert/do-not-modify-objects-with-temporary-lifetime
3+
* @name EXP35-C: Do not modify objects with temporary lifetime
4+
* @description Attempting to modify an object with temporary lifetime results in undefined
5+
* behavior.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/cert/id/exp35-c
10+
* correctness
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
17+
/**
18+
* A struct or union type that contains an array type
19+
*/
20+
class StructOrUnionTypeWithArrayField extends Struct {
21+
StructOrUnionTypeWithArrayField() {
22+
this.getAField().getUnspecifiedType() instanceof ArrayType
23+
or
24+
// nested struct or union containing an array type
25+
this.getAField().getUnspecifiedType().(Struct) instanceof StructOrUnionTypeWithArrayField
26+
}
27+
}
28+
29+
// Note: Undefined behavior is possible regardless of whether the accessed field from the returned
30+
// struct is an array or a scalar (i.e. arithmetic and pointer types) member, according to the standard.
31+
from FieldAccess fa, FunctionCall fc
32+
where
33+
not isExcluded(fa, InvalidMemory2Package::doNotModifyObjectsWithTemporaryLifetimeQuery()) and
34+
not fa.getQualifier().isLValue() and
35+
fa.getQualifier().getUnconverted() = fc and
36+
fa.getQualifier().getUnconverted().getUnspecifiedType() instanceof StructOrUnionTypeWithArrayField
37+
select fa, "Field access on $@ qualifier occurs after its temporary object lifetime.", fc,
38+
"function call"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| test.c:65:18:65:18 | a | Field access on $@ qualifier occurs after its temporary object lifetime. | test.c:65:9:65:14 | call to get_s1 | function call |
2+
| test.c:67:18:67:19 | s1 | Field access on $@ qualifier occurs after its temporary object lifetime. | test.c:67:9:67:14 | call to get_s3 | function call |
3+
| test.c:68:18:68:19 | i1 | Field access on $@ qualifier occurs after its temporary object lifetime. | test.c:68:9:68:14 | call to get_s3 | function call |
4+
| test.c:69:18:69:21 | af12 | Field access on $@ qualifier occurs after its temporary object lifetime. | test.c:69:9:69:14 | call to get_s4 | function call |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/EXP35-C/DoNotModifyObjectsWithTemporaryLifetime.ql

c/cert/test/rules/EXP35-C/test.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#include <stdlib.h>
2+
3+
typedef float AF12[12];
4+
5+
struct S1 {
6+
char a[8];
7+
};
8+
struct S2 {
9+
struct S1 *s1;
10+
};
11+
struct S3 {
12+
struct S1 s1;
13+
int i1;
14+
};
15+
struct S4 {
16+
AF12 af12;
17+
};
18+
19+
struct S5 {
20+
int i1;
21+
struct S1 *s1;
22+
};
23+
24+
struct S1 get_s1(void) {
25+
struct S1 s1;
26+
return s1;
27+
}
28+
29+
struct S1 *get_s1_ptr(void) {
30+
struct S1 *s1 = malloc(sizeof(struct S1));
31+
return s1;
32+
}
33+
34+
struct S2 get_s2(void) {
35+
struct S2 s2;
36+
return s2;
37+
}
38+
39+
struct S3 get_s3(void) {
40+
struct S3 s3;
41+
return s3;
42+
}
43+
44+
struct S4 get_s4(void) {
45+
struct S4 s4;
46+
return s4;
47+
}
48+
49+
struct S5 get_s5(void) {
50+
struct S5 s5;
51+
return s5;
52+
}
53+
54+
void test_field_access(void) {
55+
struct S1 s1 = get_s1();
56+
struct S2 s2 = get_s2();
57+
struct S3 s3 = get_s3();
58+
struct S4 s4 = get_s4();
59+
60+
s1.a[0] = 'a'; // COMPLIANT
61+
s2.s1->a[0] = 'a'; // COMPLIANT
62+
s3.s1.a[0] = 'a'; // COMPLIANT
63+
s4.af12[0] = 0.0f; // COMPLIANT
64+
65+
(void)get_s1().a; // NON_COMPLIANT
66+
(void)get_s2().s1->a; // COMPLIANT
67+
(void)get_s3().s1.a; // NON_COMPLIANT
68+
(void)get_s3().i1; // NON_COMPLIANT - even if scalar type accessed
69+
(void)get_s4().af12; // NON_COMPLIANT
70+
(void)get_s5().s1->a; // COMPLIANT
71+
}

0 commit comments

Comments
 (0)