Skip to content

Commit 36a4e3f

Browse files
committed
Declarations2: add rule DCL41-C
1 parent 16af634 commit 36a4e3f

File tree

9 files changed

+229
-1
lines changed

9 files changed

+229
-1
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# DCL41-C: Do not declare variables inside a switch statement before the first case label
2+
3+
This query implements the CERT-C rule DCL41-C:
4+
5+
> Do not declare variables inside a switch statement before the first case label
6+
7+
8+
## Description
9+
10+
According to the C Standard, 6.8.4.2, paragraph 4 \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO-IEC9899-2011)\],
11+
12+
> A switch statement causes control to jump to, into, or past the statement that is the switch body, depending on the value of a controlling expression, and on the presence of a default label and the values of any case labels on or in the switch body.
13+
14+
15+
If a programmer declares variables, initializes them before the first case statement, and then tries to use them inside any of the case statements, those variables will have scope inside the `switch` block but will not be initialized and will consequently contain indeterminate values. Reading such values also violates [EXP33-C. Do not read uninitialized memory](https://wiki.sei.cmu.edu/confluence/display/c/EXP33-C.+Do+not+read+uninitialized+memory).
16+
17+
## Noncompliant Code Example
18+
19+
This noncompliant code example declares variables and contains executable statements before the first case label within the `switch` statement:
20+
21+
```cpp
22+
#include <stdio.h>
23+
24+
extern void f(int i);
25+
26+
void func(int expr) {
27+
switch (expr) {
28+
int i = 4;
29+
f(i);
30+
case 0:
31+
i = 17;
32+
/* Falls through into default code */
33+
default:
34+
printf("%d\n", i);
35+
}
36+
}
37+
38+
```
39+
**Implementation Details**
40+
41+
When the preceding example is executed on GCC 4.8.1, the variable `i` is instantiated with automatic storage duration within the block, but it is not initialized. Consequently, if the controlling expression `expr` has a nonzero value, the call to `printf()` will access an indeterminate value of `i`. Similarly, the call to `f()` is not executed.
42+
43+
<table> <tbody> <tr> <th> Value of <code>expr</code> </th> <th> <code>Output</code> </th> </tr> <tr> <td> 0 </td> <td> 17 </td> </tr> <tr> <td> Nonzero </td> <td> Indeterminate </td> </tr> </tbody> </table>
44+
45+
46+
## Compliant Solution
47+
48+
In this compliant solution, the statements before the first case label occur before the `switch` statement:
49+
50+
```cpp
51+
#include <stdio.h>
52+
53+
extern void f(int i);
54+
55+
int func(int expr) {
56+
/*
57+
* Move the code outside the switch block; now the statements
58+
* will get executed.
59+
*/
60+
int i = 4;
61+
f(i);
62+
63+
switch (expr) {
64+
case 0:
65+
i = 17;
66+
/* Falls through into default code */
67+
default:
68+
printf("%d\n", i);
69+
}
70+
return 0;
71+
}
72+
73+
```
74+
75+
## Risk Assessment
76+
77+
Using test conditions or initializing variables before the first case statement in a `switch` block can result in [unexpected behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior) and [undefined behavior](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-undefinedbehavior).
78+
79+
<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> DCL41-C </td> <td> Medium </td> <td> Unlikely </td> <td> Medium </td> <td> <strong>P4</strong> </td> <td> <strong>L3</strong> </td> </tr> </tbody> </table>
80+
81+
82+
## Automated Detection
83+
84+
<table> <tbody> <tr> <td> <strong>Tool</strong> </td> <td> Version </td> <td> <strong>Checker</strong> </td> <td> <strong>Description</strong> </td> </tr> <tr> <td> <a> Astrée </a> </td> <td> 22.04 </td> <td> <strong>switch-skipped-code</strong> </td> <td> Fully checked </td> </tr> <tr> <td> <a> Axivion Bauhaus Suite </a> </td> <td> 7.2.0 </td> <td> <strong>CertC-DCL41</strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> Clang </a> </td> <td> 3.9 </td> <td> <code>-Wsometimes-uninitialized</code> </td> <td> </td> </tr> <tr> <td> <a> CodeSonar </a> </td> <td> 7.1p0 </td> <td> <strong>LANG.STRUCT.SW.BAD</strong> </td> <td> Malformed switch Statement </td> </tr> <tr> <td> <a> Coverity </a> </td> <td> 2017.07 </td> <td> <strong>MISRA C 2004 Rule 15.0</strong> <strong>MISRA C 2012 Rule 16.1</strong> </td> <td> Implemented </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.2 </td> <td> <strong>C2008, C2882, C3234</strong> </td> <td> </td> </tr> <tr> <td> <a> Klocwork </a> </td> <td> 2022.2 </td> <td> <strong>CERT.DCL.SWITCH.VAR_BEFORE_CASE</strong> </td> <td> </td> </tr> <tr> <td> <a> LDRA tool suite </a> </td> <td> 9.7.1 </td> <td> <strong>385 S</strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.1 </td> <td> <strong>CERT_C-DCL41-a</strong> </td> <td> A switch statement shall only contain switch labels and switch clauses, and no other code </td> </tr> <tr> <td> <a> PC-lint Plus </a> </td> <td> 1.4 </td> <td> <strong>527</strong> </td> <td> Assistance provided </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022a </td> <td> <a> CERT C: Rule DCL41-C </a> </td> <td> Checks for ill-formed switch statements (rule partially covered) </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>3234</strong> <strong>2008</strong> <strong>2882</strong> </td> <td> Partially implemented </td> </tr> <tr> <td> <a> PVS-Studio </a> </td> <td> 7.20 </td> <td> <strong><a>V622</a></strong> </td> <td> </td> </tr> <tr> <td> <a> RuleChecker </a> </td> <td> 22.04 </td> <td> <strong>switch-skipped-code</strong> </td> <td> Fully checked </td> </tr> <tr> <td> <a> TrustInSoft Analyzer </a> </td> <td> 1.38 </td> <td> <strong>initialisation</strong> </td> <td> Exhaustively detects undefined behavior (see <a> the compliant and the non-compliant example </a> ). </td> </tr> </tbody> </table>
85+
86+
87+
## Related Vulnerabilities
88+
89+
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+DCL41-C).
90+
91+
## Related Guidelines
92+
93+
[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
94+
95+
<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> MISRA C:2012 </a> </td> <td> Rule 16.1 (required) </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> </tbody> </table>
96+
97+
98+
## Bibliography
99+
100+
<table> <tbody> <tr> <td> \[ <a> ISO/IEC 9899:2011 </a> \] </td> <td> 6.8.4.2, "The <code>switch</code> Statement" </td> </tr> </tbody> </table>
101+
102+
103+
## Implementation notes
104+
105+
None
106+
107+
## References
108+
109+
* CERT-C: [DCL41-C: Do not declare variables inside a switch statement before the first case label](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @id c/cert/variables-inside-switch-statement
3+
* @name DCL41-C: Do not declare variables inside a switch statement before the first case label
4+
* @description Declaring a variable in a switch statement before the first case label can result in
5+
* reading uninitialized memory which is undefined behaviour.
6+
* @kind problem
7+
* @precision very-high
8+
* @problem.severity error
9+
* @tags external/cert/id/dcl41-c
10+
* correctness
11+
* maintainability
12+
* readability
13+
* external/cert/obligation/rule
14+
*/
15+
16+
import cpp
17+
import codingstandards.c.cert
18+
19+
from SwitchCase case, SwitchStmt stmt, VariableDeclarationEntry d
20+
where
21+
not isExcluded(d, Declarations2Package::variablesInsideSwitchStatementQuery()) and
22+
case.getSwitchStmt() = stmt and
23+
//first case
24+
not exists(case.getPreviousSwitchCase()) and
25+
exists(string filepath, int declarationLine, int caseLine, int stmtLine |
26+
d.getLocation().hasLocationInfo(filepath, declarationLine, _, _, _) and
27+
stmt.getLocation().hasLocationInfo(filepath, stmtLine, _, _, _) and
28+
case.getLocation().hasLocationInfo(filepath, caseLine, _, _, _) and
29+
declarationLine > stmtLine and
30+
declarationLine < caseLine
31+
)
32+
select d, "Declaration is located in switch $@.", stmt, "statement"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| test.c:5:9:5:9 | definition of i | Declaration is located in switch $@. | test.c:4:3:10:3 | switch (...) ... | statement |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/DCL41-C/VariablesInsideSwitchStatement.ql

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <stdio.h>
2+
3+
void f(int expr) {
4+
switch (expr) {
5+
int i = 4; // NON_COMPLIANT
6+
case 0:
7+
i = 17;
8+
default:
9+
printf("%d\n", i);
10+
}
11+
}
12+
13+
void f1(int expr) {
14+
int i = 4; // COMPLIANT
15+
switch (expr) {
16+
case 0:
17+
i = 17;
18+
default:
19+
printf("%d\n", i);
20+
}
21+
}
22+
23+
void f2(int expr) {
24+
switch (expr) {
25+
case 0:
26+
int i = 4; // COMPLIANT
27+
case 1:
28+
i = 6; // COMPLIANT
29+
default:
30+
printf("%d\n", i);
31+
}
32+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype Declarations2Query = TVariablesInsideSwitchStatementQuery()
7+
8+
predicate isDeclarations2QueryMetadata(Query query, string queryId, string ruleId) {
9+
query =
10+
// `Query` instance for the `variablesInsideSwitchStatement` query
11+
Declarations2Package::variablesInsideSwitchStatementQuery() and
12+
queryId =
13+
// `@id` for the `variablesInsideSwitchStatement` query
14+
"c/cert/variables-inside-switch-statement" and
15+
ruleId = "DCL41-C"
16+
}
17+
18+
module Declarations2Package {
19+
Query variablesInsideSwitchStatementQuery() {
20+
//autogenerate `Query` type
21+
result =
22+
// `Query` type for `variablesInsideSwitchStatement` query
23+
TQueryC(TDeclarations2PackageQuery(TVariablesInsideSwitchStatementQuery()))
24+
}
25+
}

cpp/common/src/codingstandards/cpp/exclusions/c/RuleMetadata.qll

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Concurrency2
88
import Concurrency3
99
import Contracts1
1010
import Declarations1
11+
import Declarations2
1112
import Expressions
1213
import IO1
1314
import IO2
@@ -36,6 +37,7 @@ newtype TCQuery =
3637
TConcurrency3PackageQuery(Concurrency3Query q) or
3738
TContracts1PackageQuery(Contracts1Query q) or
3839
TDeclarations1PackageQuery(Declarations1Query q) or
40+
TDeclarations2PackageQuery(Declarations2Query q) or
3941
TExpressionsPackageQuery(ExpressionsQuery q) or
4042
TIO1PackageQuery(IO1Query q) or
4143
TIO2PackageQuery(IO2Query q) or
@@ -64,6 +66,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId) {
6466
isConcurrency3QueryMetadata(query, queryId, ruleId) or
6567
isContracts1QueryMetadata(query, queryId, ruleId) or
6668
isDeclarations1QueryMetadata(query, queryId, ruleId) or
69+
isDeclarations2QueryMetadata(query, queryId, ruleId) or
6770
isExpressionsQueryMetadata(query, queryId, ruleId) or
6871
isIO1QueryMetadata(query, queryId, ruleId) or
6972
isIO2QueryMetadata(query, queryId, ruleId) or

rule_packages/c/Declarations2.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"CERT-C": {
3+
"DCL41-C": {
4+
"properties": {
5+
"obligation": "rule"
6+
},
7+
"queries": [
8+
{
9+
"description": "Declaring a variable in a switch statement before the first case label can result in reading uninitialized memory which is undefined behaviour.",
10+
"kind": "problem",
11+
"name": "Do not declare variables inside a switch statement before the first case label",
12+
"precision": "very-high",
13+
"severity": "error",
14+
"short_name": "VariablesInsideSwitchStatement",
15+
"tags": [
16+
"correctness",
17+
"maintainability",
18+
"readability"
19+
]
20+
}
21+
],
22+
"title": "Do not declare variables inside a switch statement before the first case label"
23+
}
24+
}
25+
}

rules.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,7 @@ c,CERT-C,DCL37-C,Yes,Rule,,,Do not declare or define a reserved identifier,,Decl
505505
c,CERT-C,DCL38-C,Yes,Rule,,,Use the correct syntax when declaring a flexible array member,,Declarations,Easy,
506506
c,CERT-C,DCL39-C,Yes,Rule,,,Avoid information leakage when passing a structure across a trust boundary,,Declarations,Hard,
507507
c,CERT-C,DCL40-C,Yes,Rule,,,Do not create incompatible declarations of the same function or object,,Declarations,Hard,
508-
c,CERT-C,DCL41-C,Yes,Rule,,,Do not declare variables inside a switch statement before the first case label,,Declarations,Medium,
508+
c,CERT-C,DCL41-C,Yes,Rule,,,Do not declare variables inside a switch statement before the first case label,,Declarations2,Medium,
509509
c,CERT-C,ENV30-C,Yes,Rule,,,Do not modify the object referenced by the return value of certain functions,RULE-21-19,Contracts1,Medium,
510510
c,CERT-C,ENV31-C,Yes,Rule,,,Do not rely on an environment pointer following an operation that may invalidate it,RULE-21-20,Contracts1,Hard,
511511
c,CERT-C,ENV32-C,Yes,Rule,,,All exit handlers must return normally,,Contracts,Medium,

0 commit comments

Comments
 (0)