Skip to content

Commit 1440fb8

Browse files
authored
Packages Contracts1 (#49)
Package Contracts1 - ENV30-C - ENV31-C
1 parent ccc5d4d commit 1440fb8

15 files changed

+820
-2
lines changed

.vscode/tasks.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@
193193
"Classes",
194194
"Classes",
195195
"Comments",
196+
"Contracts1",
196197
"Concurrency",
197198
"Concurrency",
198199
"Concurrency1",
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# ENV30-C: Do not modify the return value of certain functions
2+
3+
This query implements the CERT-C rule ENV30-C:
4+
5+
> Do not modify the object referenced by the return value of certain functions
6+
7+
8+
## Description
9+
10+
Some functions return a pointer to an object that cannot be modified without causing [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). These functions include `getenv()`, `setlocale()`, `localeconv()`, `asctime()`, and `strerror()`. In such cases, the function call results must be treated as being `const`-qualified.
11+
12+
The C Standard, 7.22.4.6, paragraph 4 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines `getenv()` as follows:
13+
14+
> The `getenv` function returns a pointer to a string associated with the matched list member. The string pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the `getenv` function. If the specified name cannot be found, a null pointer is returned.
15+
16+
17+
If the string returned by `getenv()` must be altered, a local copy should be created. Altering the string returned by `getenv()` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 184](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_184).)
18+
19+
Similarly, subclause 7.11.1.1, paragraph 8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines `setlocale()` as follows:
20+
21+
> The pointer to string returned by the `setlocale` function is such that a subsequent call with that string value and its associated category will restore that part of the program'€™s locale. The string pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the `setlocale` function.
22+
23+
24+
And subclause 7.11.2.1, paragraph 8 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], defines `localeconv()` as follows:
25+
26+
> The `localeconv` function returns a pointer to the filled-in object. The structure pointed to by the return value shall not be modified by the program, but may be overwritten by a subsequent call to the `localeconv` function. In addition, calls to the `setlocale` function with categories `LC_ALL`, `LC_MONETARY`, or `LC_NUMERIC` may overwrite the contents of the structure.
27+
28+
29+
Altering the string returned by `setlocale()` or the structure returned by `localeconv()` are [undefined behaviors](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behaviors 120](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_120) and [121](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_121).) Furthermore, the C Standard imposes no requirements on the contents of the string by `setlocale()`. Consequently, no assumptions can be made as to the string's internal contents or structure.
30+
31+
Finally, subclause 7.24.6.2, paragraph 4 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\], states
32+
33+
> The `strerror` function returns a pointer to the string, the contents of which are locale-specific. The array pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the `strerror` function.
34+
35+
36+
Altering the string returned by `strerror()` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). (See [undefined behavior 184](https://wiki.sei.cmu.edu/confluence/display/c/CC.+Undefined+Behavior#CC.UndefinedBehavior-ub_184).)
37+
38+
## Noncompliant Code Example (getenv())
39+
40+
This noncompliant code example modifies the string returned by `getenv()` by replacing all double quotation marks (`"`) with underscores (`_`):
41+
42+
```cpp
43+
#include <stdlib.h>
44+
45+
void trstr(char *c_str, char orig, char rep) {
46+
while (*c_str != '\0') {
47+
if (*c_str == orig) {
48+
*c_str = rep;
49+
}
50+
++c_str;
51+
}
52+
}
53+
54+
void func(void) {
55+
char *env = getenv("TEST_ENV");
56+
if (env == NULL) {
57+
/* Handle error */
58+
}
59+
trstr(env,'"', '_');
60+
}
61+
62+
```
63+
64+
## Compliant Solution (getenv()) (Environment Not Modified)
65+
66+
If the programmer does not intend to modify the environment, this compliant solution demonstrates how to modify a copy of the return value:
67+
68+
```cpp
69+
#include <stdlib.h>
70+
#include <string.h>
71+
72+
void trstr(char *c_str, char orig, char rep) {
73+
while (*c_str != '\0') {
74+
if (*c_str == orig) {
75+
*c_str = rep;
76+
}
77+
++c_str;
78+
}
79+
}
80+
81+
void func(void) {
82+
const char *env;
83+
char *copy_of_env;
84+
85+
env = getenv("TEST_ENV");
86+
if (env == NULL) {
87+
/* Handle error */
88+
}
89+
90+
copy_of_env = (char *)malloc(strlen(env) + 1);
91+
if (copy_of_env == NULL) {
92+
/* Handle error */
93+
}
94+
95+
strcpy(copy_of_env, env);
96+
trstr(copy_of_env,'"', '_');
97+
/* ... */
98+
free(copy_of_env);
99+
}
100+
```
101+
102+
## Compliant Solution (getenv()) (Modifying the Environment in POSIX)
103+
104+
If the programmer's intent is to modify the environment, this compliant solution, which saves the altered string back into the environment by using the POSIX `setenv()` and `strdup()` functions, can be used:
105+
106+
```cpp
107+
#include <stdlib.h>
108+
#include <string.h>
109+
110+
void trstr(char *c_str, char orig, char rep) {
111+
while (*c_str != '\0') {
112+
if (*c_str == orig) {
113+
*c_str = rep;
114+
}
115+
++c_str;
116+
}
117+
}
118+
119+
void func(void) {
120+
const char *env;
121+
char *copy_of_env;
122+
123+
env = getenv("TEST_ENV");
124+
if (env == NULL) {
125+
/* Handle error */
126+
}
127+
128+
copy_of_env = strdup(env);
129+
if (copy_of_env == NULL) {
130+
/* Handle error */
131+
}
132+
133+
trstr(copy_of_env,'"', '_');
134+
135+
if (setenv("TEST_ENV", copy_of_env, 1) != 0) {
136+
/* Handle error */
137+
}
138+
/* ... */
139+
free(copy_of_env);
140+
}
141+
```
142+
143+
## Noncompliant Code Example (localeconv())
144+
145+
In this noncompliant example, the object returned by `localeconv()` is directly modified:
146+
147+
```cpp
148+
#include <locale.h>
149+
150+
void f2(void) {
151+
struct lconv *conv = localeconv();
152+
153+
if ('\0' == conv->decimal_point[0]) {
154+
conv->decimal_point = ".";
155+
}
156+
}
157+
158+
```
159+
160+
## Compliant Solution (localeconv()) (Copy)
161+
162+
This compliant solution modifies a copy of the object returned by `localeconv()`:
163+
164+
```cpp
165+
#include <locale.h>
166+
#include <stdlib.h>
167+
#include <string.h>
168+
169+
void f2(void) {
170+
const struct lconv *conv = localeconv();
171+
if (conv == NULL) {
172+
/* Handle error */
173+
}
174+
175+
struct lconv *copy_of_conv = (struct lconv *)malloc(
176+
sizeof(struct lconv));
177+
if (copy_of_conv == NULL) {
178+
/* Handle error */
179+
}
180+
181+
memcpy(copy_of_conv, conv, sizeof(struct lconv));
182+
183+
if ('\0' == copy_of_conv->decimal_point[0]) {
184+
copy_of_conv->decimal_point = ".";
185+
}
186+
/* ... */
187+
free(copy_of_conv);
188+
}
189+
```
190+
191+
## Risk Assessment
192+
193+
Modifying the object pointed to by the return value of `getenv()`, `setlocale()`, `localeconv()`, `asctime()`, or `strerror()` is [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior). Even if the modification succeeds, the modified object can be overwritten by a subsequent call to the same function.
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> ENV30-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>stdlib-const-pointer-assign</strong> </td> <td> Partially checked </td> </tr> <tr> <td> <a> Axivion Bauhaus Suite </a> </td> <td> 7.2.0 </td> <td> <strong>CertC-ENV30</strong> </td> <td> </td> </tr> <tr> <td> <a> CodeSonar </a> </td> <td> 7.0p0 </td> <td> <strong>BADFUNC.GETENV</strong> </td> <td> Use of getenv </td> </tr> <tr> <td> <a> Compass/ROSE </a> </td> <td> </td> <td> </td> <td> Can detect violations of this rule. In particular, it ensures that the result of <code>getenv()</code> is stored in a <code>const</code> variable </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.2 </td> <td> <strong>C1492, C1493, C1494, C4751, C4752, C4753</strong> <strong>C++4751, C++4752, C++4753</strong> </td> <td> </td> </tr> <tr> <td> <a> Klocwork </a> </td> <td> 2022.2 </td> <td> <strong>MISRA.STDLIB.CTYPE.RANGE.2012_AMD1</strong> <strong>MISRA.STDLIB.ILLEGAL_REUSE.2012_AMD1</strong> <strong>MISRA.STDLIB.ILLEGAL_WRITE.2012_AMD1</strong> </td> <td> </td> </tr> <tr> <td> <a> LDRA tool suite </a> </td> <td> 9.7.1 </td> <td> <strong>107 D</strong> </td> <td> Partially Implemented </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.1 </td> <td> <strong>CERT_C-ENV30-a</strong> </td> <td> The pointers returned by the Standard Library functions 'localeconv', 'getenv', 'setlocale' or, 'strerror' shall only be used as if they have pointer to const-qualified type </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022a </td> <td> <a> CERT C: Rule ENV30-C </a> </td> <td> Checks for modification of internal buffer returned from nonreentrant standard function (rule fully covered) </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>1492, 1493, 1494 </strong> </td> <td> </td> </tr> <tr> <td> <a> PVS-Studio </a> </td> <td> 7.20 </td> <td> <strong><a>V675</a></strong> </td> <td> </td> </tr> <tr> <td> <a> RuleChecker </a> </td> <td> 22.04 </td> <td> <strong>stdlib-const-pointer-assign</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+ENV30-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 TS 17961:2013 </a> </td> <td> Modifying the string returned by <code>getenv</code> , <code>localeconv</code> , <code>setlocale</code> , and <code>strerror</code> \[libmod\] </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> IEEE Std 1003.1:2013 </a> \] </td> <td> XSH, System Interfaces, <code>getenv</code> XSH, System Interfaces, <code>setlocale</code> XSH, System Interfaces, <code>localeconv</code> </td> </tr> <tr> <td> \[ <a> ISO/IEC 9899:2011 </a> \] </td> <td> 7.11.1.1, "The <code>setlocale</code> Function" 7.11.2.1, "The <code>localeconv</code> Function" 7.22.4.6, "The <code>getenv</code> Function" 7.24.6.2, "The <code>strerror</code> Function" </td> </tr> </tbody> </table>
217+
218+
219+
## Implementation notes
220+
221+
None
222+
223+
## References
224+
225+
* CERT-C: [ENV30-C: Do not modify the object referenced by the return value of certain functions](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* @id c/cert/do-not-modify-the-return-value-of-certain-functions
3+
* @name ENV30-C: Do not modify the return value of certain functions
4+
* @description Modification of return values of getenv and similar functions results in undefined
5+
* behaviour.
6+
* @kind path-problem
7+
* @precision very-high
8+
* @problem.severity warning
9+
* @tags external/cert/id/env30-c
10+
* correctness
11+
* external/cert/obligation/rule
12+
*/
13+
14+
import cpp
15+
import codingstandards.c.cert
16+
import semmle.code.cpp.dataflow.DataFlow
17+
import DataFlow::PathGraph
18+
19+
/*
20+
* Call to functions that return pointers to environment objects that should not be modified.
21+
*/
22+
23+
class NotModifiableCall extends FunctionCall {
24+
NotModifiableCall() {
25+
this.getTarget()
26+
.hasGlobalName(["getenv", "setlocale", "localeconv", "asctime", "strerror"])
27+
}
28+
}
29+
30+
/*
31+
* An expression that modifies an object.
32+
*/
33+
34+
class ObjectWrite extends Expr {
35+
ObjectWrite() {
36+
// the pointed object is reassigned
37+
exists(Expr e |
38+
e = [any(AssignExpr ae).getLValue(), any(CrementOperation co).getOperand()] and
39+
(
40+
this = e.(PointerDereferenceExpr).getOperand()
41+
or
42+
this = e.(PointerFieldAccess).getQualifier()
43+
)
44+
)
45+
}
46+
}
47+
48+
/**
49+
* DF configuration for flows from a `NotModifiableCall` to a object modifications.
50+
*/
51+
class DFConf extends DataFlow::Configuration {
52+
DFConf() { this = "DFConf" }
53+
54+
override predicate isSource(DataFlow::Node source) {
55+
source.asExpr() instanceof NotModifiableCall
56+
}
57+
58+
override predicate isSink(DataFlow::Node sink) { sink.asExpr() instanceof ObjectWrite }
59+
}
60+
61+
from DataFlow::PathNode source, DataFlow::PathNode sink
62+
where
63+
not isExcluded(sink.getNode().asExpr(),
64+
Contracts1Package::doNotModifyTheReturnValueOfCertainFunctionsQuery()) and
65+
// the modified object comes from a call to one of the ENV functions
66+
any(DFConf d).hasFlowPath(source, sink)
67+
select sink.getNode(), source, sink,
68+
"The object returned by the function " +
69+
source.getNode().asExpr().(FunctionCall).getTarget().getName() + " should no be modified."

0 commit comments

Comments
 (0)