Skip to content

Commit 4a2f35f

Browse files
authored
Merge pull request #141 from github/statements1
Statements1
2 parents 15ed053 + d5750b0 commit 4a2f35f

File tree

183 files changed

+3739
-471
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

183 files changed

+3739
-471
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# FLP30-C: Do not use floating-point variables as loop counters
2+
3+
This query implements the CERT-C rule FLP30-C:
4+
5+
> Do not use floating-point variables as loop counters
6+
7+
8+
## Description
9+
10+
Because floating-point numbers represent real numbers, it is often mistakenly assumed that they can represent any simple fraction exactly. Floating-point numbers are subject to representational limitations just as integers are, and binary floating-point numbers cannot represent all real numbers exactly, even if they can be represented in a small number of decimal digits.
11+
12+
In addition, because floating-point numbers can represent large values, it is often mistakenly assumed that they can represent all significant digits of those values. To gain a large dynamic range, floating-point numbers maintain a fixed number of precision bits (also called the significand) and an exponent, which limit the number of significant digits they can represent.
13+
14+
Different implementations have different precision limitations, and to keep code portable, floating-point variables must not be used as the loop induction variable. See Goldberg's work for an introduction to this topic \[[Goldberg 1991](https://www.securecoding.cert.org/confluence/display/java/Rule+AA.+References#RuleAA.References-Goldberg91)\].
15+
16+
For the purpose of this rule, a *loop counter* is an induction variable that is used as an operand of a comparison expression that is used as the controlling expression of a `do`, `while`, or `for` loop. An *induction variable* is a variable that gets increased or decreased by a fixed amount on every iteration of a loop \[[Aho 1986](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-Aho1986)\]. Furthermore, the change to the variable must occur directly in the loop body (rather than inside a function executed within the loop).
17+
18+
## Noncompliant Code Example
19+
20+
In this noncompliant code example, a floating-point variable is used as a loop counter. The decimal number `0.1` is a repeating fraction in binary and cannot be exactly represented as a binary floating-point number. Depending on the implementation, the loop may iterate 9 or 10 times.
21+
22+
```cpp
23+
void func(void) {
24+
for (float x = 0.1f; x <= 1.0f; x += 0.1f) {
25+
/* Loop may iterate 9 or 10 times */
26+
}
27+
}
28+
```
29+
For example, when compiled with GCC or Microsoft Visual Studio 2013 and executed on an x86 processor, the loop is evaluated only nine times.
30+
31+
## Compliant Solution
32+
33+
In this compliant solution, the loop counter is an integer from which the floating-point value is derived:
34+
35+
```cpp
36+
#include <stddef.h>
37+
38+
void func(void) {
39+
for (size_t count = 1; count <= 10; ++count) {
40+
float x = count / 10.0f;
41+
/* Loop iterates exactly 10 times */
42+
}
43+
}
44+
```
45+
46+
## Noncompliant Code Example
47+
48+
In this noncompliant code example, a floating-point loop counter is incremented by an amount that is too small to change its value given its precision:
49+
50+
```cpp
51+
void func(void) {
52+
for (float x = 100000001.0f; x <= 100000010.0f; x += 1.0f) {
53+
/* Loop may not terminate */
54+
}
55+
}
56+
```
57+
On many implementations, this produces an infinite loop.
58+
59+
## Compliant Solution
60+
61+
In this compliant solution, the loop counter is an integer from which the floating-point value is derived. The variable `x` is assigned a computed value to reduce compounded rounding errors that are present in the noncompliant code example.
62+
63+
```cpp
64+
void func(void) {
65+
for (size_t count = 1; count <= 10; ++count) {
66+
float x = 100000000.0f + (count * 1.0f);
67+
/* Loop iterates exactly 10 times */
68+
}
69+
}
70+
```
71+
72+
## Risk Assessment
73+
74+
The use of floating-point variables as loop counters can result in [unexpected behavior ](https://wiki.sei.cmu.edu/confluence/display/c/BB.+Definitions#BB.Definitions-unexpectedbehavior).
75+
76+
<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> FLP30-C </td> <td> Low </td> <td> Probable </td> <td> Low </td> <td> <strong>P6</strong> </td> <td> <strong>L2</strong> </td> </tr> </tbody> </table>
77+
78+
79+
## Automated Detection
80+
81+
<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>for-loop-float</strong> </td> <td> Fully checked </td> </tr> <tr> <td> <a> Axivion Bauhaus Suite </a> </td> <td> 7.2.0 </td> <td> <strong>CertC-FLP30</strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> Clang </a> </td> <td> 3.9 </td> <td> <code>cert-flp30-c</code> </td> <td> Checked by <code>clang-tidy</code> </td> </tr> <tr> <td> <a> CodeSonar </a> </td> <td> 7.2p0 </td> <td> <strong>LANG.STRUCT.LOOP.FPC</strong> </td> <td> Float-typed loop counter </td> </tr> <tr> <td> <a> Compass/ROSE </a> </td> <td> </td> <td> </td> <td> </td> </tr> <tr> <td> <a> Coverity </a> </td> <td> 2017.07 </td> <td> <strong>MISRA C 2004 Rule 13.4</strong> <strong><strong>MISRA C 2012 Rule 14.1</strong></strong> </td> <td> Implemented </td> </tr> <tr> <td> <a> ECLAIR </a> </td> <td> 1.2 </td> <td> <strong>CC2.FLP30</strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.4 </td> <td> <strong>C3339, C3340, C3342</strong> <strong>C++4234</strong> </td> <td> </td> </tr> <tr> <td> <a> Klocwork </a> </td> <td> 2022.4 </td> <td> <strong>MISRA.FOR.COUNTER.FLT</strong> </td> <td> </td> </tr> <tr> <td> <a> LDRA tool suite </a> </td> <td> 9.7.1 </td> <td> <strong>39 S</strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.2 </td> <td> <strong>CERT_C-FLP30-a</strong> </td> <td> Do not use floating point variables as loop counters </td> </tr> <tr> <td> <a> PC-lint Plus </a> </td> <td> 1.4 </td> <td> <strong>9009</strong> </td> <td> Fully supported </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022b </td> <td> <a> CERT C: Rule FLP30-C </a> </td> <td> Checks for use of float variable as loop counter (rule fully covered) </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>3339, 3340, 3342</strong> </td> <td> Partially implemented </td> </tr> <tr> <td> <a> PRQA QA-C++ </a> </td> <td> 4.4 </td> <td> <strong>4234 </strong> </td> <td> </td> </tr> <tr> <td> <a> PVS-Studio </a> </td> <td> 7.23 </td> <td> <strong><a>V1034</a></strong> </td> <td> </td> </tr> <tr> <td> <a> RuleChecker </a> </td> <td> 22.04 </td> <td> <strong>for-loop-float</strong> </td> <td> Fully checked </td> </tr> <tr> <td> <a> SonarQube C/C++ Plugin </a> </td> <td> 3.11 </td> <td> <strong><a>S2193</a></strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> TrustInSoft Analyzer </a> </td> <td> 1.38 </td> <td> <strong>non-terminating</strong> </td> <td> Exhaustively detects non-terminating statements (see <a> one compliant and one non-compliant example </a> ). </td> </tr> </tbody> </table>
82+
83+
84+
## Related Vulnerabilities
85+
86+
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+FLP30-C).
87+
88+
## Related Guidelines
89+
90+
[Key here](https://wiki.sei.cmu.edu/confluence/display/c/How+this+Coding+Standard+is+Organized#HowthisCodingStandardisOrganized-RelatedGuidelines) (explains table format and definitions)
91+
92+
<table> <tbody> <tr> <th> Taxonomy </th> <th> Taxonomy item </th> <th> Relationship </th> </tr> <tr> <td> <a> CERT C </a> </td> <td> <a> FLP30-CPP. Do not use floating-point variables as loop counters </a> </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> CERT Oracle Secure Coding Standard for Java </a> </td> <td> <a> NUM09-J. Do not use floating-point variables as loop counters </a> </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> ISO/IEC TR 24772:2013 </a> </td> <td> Floating-Point Arithmetic \[PLF\] </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> MISRA C:2012 </a> </td> <td> Directive 1.1 (required) </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> <tr> <td> <a> MISRA C:2012 </a> </td> <td> Rule 14.1 (required) </td> <td> Prior to 2018-01-12: CERT: Unspecified Relationship </td> </tr> </tbody> </table>
93+
94+
95+
## Bibliography
96+
97+
<table> <tbody> <tr> <td> \[ <a> Aho 1986 </a> \] </td> <td> </td> </tr> <tr> <td> \[ <a> Goldberg 1991 </a> \] </td> <td> </td> </tr> <tr> <td> \[ <a> Lockheed Martin 05 </a> \] </td> <td> AV Rule 197 </td> </tr> </tbody> </table>
98+
99+
100+
## Implementation notes
101+
102+
None
103+
104+
## References
105+
106+
* CERT-C: [FLP30-C: Do not use floating-point variables as loop counters](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* @id c/cert/floating-point-loop-counters
3+
* @name FLP30-C: Do not use floating-point variables as loop counters
4+
* @description Loop counters should not use floating-point variables to keep code portable.
5+
* @kind problem
6+
* @precision very-high
7+
* @problem.severity recommendation
8+
* @tags external/cert/id/flp30-c
9+
* maintainability
10+
* readability
11+
* correctness
12+
* external/cert/obligation/rule
13+
*/
14+
15+
import cpp
16+
import codingstandards.c.cert
17+
import codingstandards.cpp.Loops
18+
19+
/*
20+
* A variable that is increased or decreased by a fixed amount on each iteration.
21+
*/
22+
23+
class InductionVariable extends Variable {
24+
Loop loop;
25+
Expr update;
26+
27+
InductionVariable() {
28+
update.getParent+() = loop and
29+
(
30+
update.(AssignArithmeticOperation).getRValue().isConstant() and
31+
update.(AssignArithmeticOperation).getLValue() = this.getAnAccess()
32+
or
33+
exists(BinaryArithmeticOperation binop |
34+
update.(Assignment).getLValue() = this.getAnAccess() and
35+
update.(Assignment).getRValue() = binop and
36+
binop.getAnOperand() = this.getAnAccess() and
37+
binop.getAnOperand().isConstant()
38+
)
39+
or
40+
update.(CrementOperation).getOperand() = this.getAnAccess()
41+
)
42+
}
43+
}
44+
45+
from Loop loop, InductionVariable loopCounter, ComparisonOperation comparison
46+
where
47+
not isExcluded(loop, Statements4Package::floatingPointLoopCountersQuery()) and
48+
loop.getControllingExpr() = comparison and
49+
comparison.getAnOperand() = loopCounter.getAnAccess() and
50+
loopCounter.getType() instanceof FloatingPointType
51+
select loop, "Loop using a $@ of type floating-point.", loopCounter, "loop counter"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| test.c:3:3:4:3 | for(...;...;...) ... | Loop using a $@ of type floating-point. | test.c:2:9:2:9 | f | loop counter |
2+
| test.c:5:3:7:3 | while (...) ... | Loop using a $@ of type floating-point. | test.c:2:9:2:9 | f | loop counter |
3+
| test.c:9:3:11:22 | do (...) ... | Loop using a $@ of type floating-point. | test.c:2:9:2:9 | f | loop counter |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/FLP30-C/FloatingPointLoopCounters.ql

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
void f1() {
2+
float f = 0.0F;
3+
for (f = 0.0F; f < 10.0F; f += 0.2F) { // NON_COMPLIANT
4+
}
5+
while (f < 10.0F) { // NON_COMPLIANT
6+
f = f * 2.0F;
7+
}
8+
9+
do { // NON_COMPLIANT
10+
f *= 2.0F;
11+
} while (f < 10.0F);
12+
}
13+
14+
void f2() {
15+
16+
for (int i = 0; i < 10; i++) { // COMPLIANT
17+
}
18+
int j = 0;
19+
while (j < 10) { // COMPLIANT
20+
j = j * 2;
21+
}
22+
23+
do {
24+
j++;
25+
} while (j < 10); // COMPLIANT
26+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| test.c:5:3:5:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:5:3:5:10 | goto ... | L1 | test.c:2:1:2:3 | label ...: | label ...: |
2+
| test.c:14:3:14:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:14:3:14:10 | goto ... | L2 | test.c:12:1:12:3 | label ...: | label ...: |
3+
| test.c:16:3:16:10 | goto ... | The $@ statement jumps to a $@ that is not declared later in the same function. | test.c:16:3:16:10 | goto ... | L1 | test.c:11:1:11:3 | label ...: | label ...: |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// GENERATED FILE - DO NOT MODIFY
2+
import codingstandards.cpp.rules.gotostatementcondition.GotoStatementCondition
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
void f1() {
2+
L1:;
3+
goto L2; // COMPLIANT
4+
;
5+
goto L1; // NON_COMPLIANT
6+
7+
L2:;
8+
}
9+
10+
void f2() {
11+
L1:;
12+
L2:
13+
goto L3; // COMPLIANT
14+
goto L2; // NON_COMPLIANT
15+
L3:
16+
goto L1; // NON_COMPLIANT
17+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| test.c:16:3:20:3 | if (...) ... | The $@ construct does not terminate with else statement. | test.c:16:3:20:3 | if (...) ... | `if...else` |
2+
| test.c:33:5:37:5 | if (...) ... | The $@ construct does not terminate with else statement. | test.c:33:5:37:5 | if (...) ... | `if...else` |
3+
| test.c:45:3:55:3 | if (...) ... | The $@ construct does not terminate with else statement. | test.c:45:3:55:3 | if (...) ... | `if...else` |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// GENERATED FILE - DO NOT MODIFY
2+
import codingstandards.cpp.rules.ifelseterminationconstruct.IfElseTerminationConstruct
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
void f1(int p1) {
2+
3+
if (p1) { // COMPLIANT
4+
;
5+
} else if (p1) {
6+
;
7+
} else {
8+
;
9+
}
10+
}
11+
12+
void f2(int p1) {
13+
if (p1) { // COMPLIANT
14+
;
15+
}
16+
if (p1) { // NON_COMPLIANT
17+
;
18+
} else if (p1) {
19+
;
20+
}
21+
}
22+
23+
void f3(int p1) {
24+
25+
if (p1) { // COMPLIANT
26+
;
27+
} else {
28+
;
29+
}
30+
if (p1) { // COMPLIANT
31+
;
32+
} else if (p1) {
33+
if (p1) { // NON_COMPLIANT
34+
;
35+
} else if (p1) {
36+
;
37+
}
38+
} else {
39+
;
40+
}
41+
}
42+
43+
void f4(int p1) {
44+
45+
if (p1) { // NON_COMPLIANT
46+
;
47+
} else if (p1) {
48+
if (p1) { // COMPLIANT
49+
;
50+
} else if (p1) {
51+
;
52+
} else {
53+
;
54+
}
55+
}
56+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
| test.c:9:5:9:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.c:9:5:9:11 | case ...: | case ...: | test.c:6:3:17:3 | switch (...) ... | switch (...) ... |
2+
| test.c:36:5:36:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.c:36:5:36:11 | case ...: | case ...: | test.c:23:3:43:3 | switch (...) ... | switch (...) ... |
3+
| test.c:76:5:76:11 | case ...: | The case $@ does not appear at the outermost level of the compound statement forming the body of the $@ statement. | test.c:76:5:76:11 | case ...: | case ...: | test.c:73:3:79:3 | switch (...) ... | switch (...) ... |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// GENERATED FILE - DO NOT MODIFY
2+
import codingstandards.cpp.rules.nestedlabelinswitch.NestedLabelInSwitch
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
void f();
2+
3+
void f1(int p1) {
4+
int i;
5+
int j;
6+
switch (p1) {
7+
case 1: // COMPLIANT
8+
if (i) {
9+
case 2: // NON_COMPLIANT
10+
j;
11+
break;
12+
}
13+
break;
14+
default: // COMPLIANT
15+
j;
16+
break;
17+
}
18+
}
19+
20+
void f2(int p1) {
21+
int i;
22+
int j;
23+
switch (p1) {
24+
case 1: // COMPLIANT
25+
if (i) {
26+
j;
27+
}
28+
break;
29+
case 2: // COMPLIANT
30+
if (i) {
31+
j;
32+
}
33+
case 3: // COMPLIANT
34+
if (i) {
35+
j;
36+
case 4: // NON_COMPLIANT
37+
j;
38+
}
39+
break;
40+
default: // COMPLIANT
41+
j;
42+
break;
43+
}
44+
}
45+
46+
void f3(int p1) {
47+
48+
int i;
49+
int j;
50+
switch (p1) {
51+
case 1: // COMPLIANT
52+
if (i) {
53+
j;
54+
}
55+
break;
56+
case 2: // COMPLIANT
57+
if (i) {
58+
j;
59+
}
60+
break;
61+
case 3: // COMPLIANT
62+
if (i) {
63+
j;
64+
}
65+
break;
66+
default: // COMPLIANT
67+
j;
68+
break;
69+
}
70+
}
71+
72+
void f4(int p1) {
73+
switch (p1) {
74+
int i;
75+
if (i) {
76+
case 1: // NON_COMPLIANT
77+
f();
78+
}
79+
}
80+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| test.c:5:3:24:3 | switch (...) ... | $@ statement not well formed because the first statement in a well formed switch statement must be a case clause. | test.c:5:3:24:3 | switch (...) ... | Switch |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// GENERATED FILE - DO NOT MODIFY
2+
import codingstandards.cpp.rules.switchcasepositioncondition.SwitchCasePositionCondition

0 commit comments

Comments
 (0)