Skip to content

Commit 755fc46

Browse files
committed
added FLP30-C
1 parent 34fb54a commit 755fc46

File tree

9 files changed

+319
-3
lines changed

9 files changed

+319
-3
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: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
from Loop loop
20+
where
21+
not isExcluded(loop, Statements4Package::floatingPointLoopCountersQuery()) and
22+
exists(WhileStmt while |
23+
while.getCondition().getType() instanceof FloatType and
24+
loop = while
25+
)
26+
or
27+
exists(ForStmt for, Variable counter |
28+
isForLoopWithFloatingPointCounters(for, counter) and for = loop
29+
)
30+
select loop, "Loop $@ has a floating-point type.", loop.getControllingExpr(), "counter"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
| test.c:3:3:4:3 | for(...;...;...) ... | Loop $@ has a floating-point type. | test.c:3:18:3:26 | ... < ... | counter |
2+
| test.c:5:3:6:3 | while (...) ... | Loop $@ has a floating-point type. | test.c:5:10:5:17 | ... - ... | 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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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 - 0.0F) { // NON_COMPLIANT
6+
}
7+
}
8+
9+
void f2() {
10+
11+
for (int i = 0; i < 10; i++) { // COMPLIANT
12+
}
13+
while (4 - 4) { // COMPLIANT
14+
}
15+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import SideEffects2
4545
import Statements1
4646
import Statements2
4747
import Statements3
48+
import Statements4
4849
import Strings1
4950
import Strings2
5051
import Strings3
@@ -95,6 +96,7 @@ newtype TCQuery =
9596
TStatements1PackageQuery(Statements1Query q) or
9697
TStatements2PackageQuery(Statements2Query q) or
9798
TStatements3PackageQuery(Statements3Query q) or
99+
TStatements4PackageQuery(Statements4Query q) or
98100
TStrings1PackageQuery(Strings1Query q) or
99101
TStrings2PackageQuery(Strings2Query q) or
100102
TStrings3PackageQuery(Strings3Query q) or
@@ -145,6 +147,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
145147
isStatements1QueryMetadata(query, queryId, ruleId, category) or
146148
isStatements2QueryMetadata(query, queryId, ruleId, category) or
147149
isStatements3QueryMetadata(query, queryId, ruleId, category) or
150+
isStatements4QueryMetadata(query, queryId, ruleId, category) or
148151
isStrings1QueryMetadata(query, queryId, ruleId, category) or
149152
isStrings2QueryMetadata(query, queryId, ruleId, category) or
150153
isStrings3QueryMetadata(query, queryId, ruleId, category) or
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
2+
import cpp
3+
import RuleMetadata
4+
import codingstandards.cpp.exclusions.RuleMetadata
5+
6+
newtype Statements4Query =
7+
TFloatingPointLoopCountersQuery() or
8+
TForLoopNotWellFormedQuery() or
9+
TNonBooleanIfConditionQuery() or
10+
TNonBooleanIterationConditionQuery()
11+
12+
predicate isStatements4QueryMetadata(Query query, string queryId, string ruleId, string category) {
13+
query =
14+
// `Query` instance for the `floatingPointLoopCounters` query
15+
Statements4Package::floatingPointLoopCountersQuery() and
16+
queryId =
17+
// `@id` for the `floatingPointLoopCounters` query
18+
"c/cert/floating-point-loop-counters" and
19+
ruleId = "FLP30-C" and
20+
category = "rule"
21+
or
22+
query =
23+
// `Query` instance for the `forLoopNotWellFormed` query
24+
Statements4Package::forLoopNotWellFormedQuery() and
25+
queryId =
26+
// `@id` for the `forLoopNotWellFormed` query
27+
"c/misra/for-loop-not-well-formed" and
28+
ruleId = "RULE-14-2" and
29+
category = "required"
30+
or
31+
query =
32+
// `Query` instance for the `nonBooleanIfCondition` query
33+
Statements4Package::nonBooleanIfConditionQuery() and
34+
queryId =
35+
// `@id` for the `nonBooleanIfCondition` query
36+
"c/misra/non-boolean-if-condition" and
37+
ruleId = "RULE-14-4" and
38+
category = "required"
39+
or
40+
query =
41+
// `Query` instance for the `nonBooleanIterationCondition` query
42+
Statements4Package::nonBooleanIterationConditionQuery() and
43+
queryId =
44+
// `@id` for the `nonBooleanIterationCondition` query
45+
"c/misra/non-boolean-iteration-condition" and
46+
ruleId = "RULE-14-4" and
47+
category = "required"
48+
}
49+
50+
module Statements4Package {
51+
Query floatingPointLoopCountersQuery() {
52+
//autogenerate `Query` type
53+
result =
54+
// `Query` type for `floatingPointLoopCounters` query
55+
TQueryC(TStatements4PackageQuery(TFloatingPointLoopCountersQuery()))
56+
}
57+
58+
Query forLoopNotWellFormedQuery() {
59+
//autogenerate `Query` type
60+
result =
61+
// `Query` type for `forLoopNotWellFormed` query
62+
TQueryC(TStatements4PackageQuery(TForLoopNotWellFormedQuery()))
63+
}
64+
65+
Query nonBooleanIfConditionQuery() {
66+
//autogenerate `Query` type
67+
result =
68+
// `Query` type for `nonBooleanIfCondition` query
69+
TQueryC(TStatements4PackageQuery(TNonBooleanIfConditionQuery()))
70+
}
71+
72+
Query nonBooleanIterationConditionQuery() {
73+
//autogenerate `Query` type
74+
result =
75+
// `Query` type for `nonBooleanIterationCondition` query
76+
TQueryC(TStatements4PackageQuery(TNonBooleanIterationConditionQuery()))
77+
}
78+
}

rule_packages/c/Statements4.json

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{
2+
"CERT-C": {
3+
"FLP30-C": {
4+
"properties": {
5+
"obligation": "rule"
6+
},
7+
"queries": [
8+
{
9+
"description": "Loop counters should not use floating-point variables to keep code portable.",
10+
"kind": "problem",
11+
"name": "Do not use floating-point variables as loop counters",
12+
"precision": "very-high",
13+
"severity": "recommendation",
14+
"short_name": "FloatingPointLoopCounters",
15+
"tags": [
16+
"maintainability",
17+
"readability",
18+
"correctness"
19+
]
20+
}
21+
],
22+
"title": "Do not use floating-point variables as loop counters"
23+
}
24+
},
25+
"MISRA-C-2012": {
26+
"RULE-14-2": {
27+
"properties": {
28+
"obligation": "required"
29+
},
30+
"queries": [
31+
{
32+
"description": "A well-formed for loop makes code easier to review.",
33+
"kind": "problem",
34+
"name": "A for loop shall be well-formed",
35+
"precision": "very-high",
36+
"severity": "recommendation",
37+
"short_name": "ForLoopNotWellFormed",
38+
"tags": [
39+
"readability",
40+
"maintainability"
41+
]
42+
}
43+
],
44+
"title": "A for loop shall be well-formed"
45+
},
46+
"RULE-14-4": {
47+
"properties": {
48+
"obligation": "required"
49+
},
50+
"queries": [
51+
{
52+
"description": "Non boolean conditions can be confusing for developers.",
53+
"kind": "problem",
54+
"name": "The condition of an if-statement shall have type bool",
55+
"precision": "very-high",
56+
"severity": "recommendation",
57+
"short_name": "NonBooleanIfCondition",
58+
"shared_implementation_short_name": "NonBooleanIfStmt",
59+
"tags": [
60+
"maintainability",
61+
"readability"
62+
]
63+
},
64+
{
65+
"description": "Non boolean conditions can be confusing for developers.",
66+
"kind": "problem",
67+
"name": "The condition of an iteration statement shall have type bool",
68+
"precision": "very-high",
69+
"severity": "recommendation",
70+
"short_name": "NonBooleanIterationCondition",
71+
"shared_implementation_short_name": "NonBooleanIterationStmt",
72+
"tags": [
73+
"maintainability",
74+
"readability"
75+
]
76+
}
77+
],
78+
"title": "The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type"
79+
}
80+
}
81+
}

rules.csv

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ c,CERT-C,FIO44-C,Yes,Rule,,,Only use values for fsetpos() that are returned from
543543
c,CERT-C,FIO45-C,Yes,Rule,,,Avoid TOCTOU race conditions while accessing files,,IO4,Medium,
544544
c,CERT-C,FIO46-C,Yes,Rule,,,Do not access a closed file,FIO51-CPP,IO1,Hard,
545545
c,CERT-C,FIO47-C,Yes,Rule,,,Use valid format strings,,IO4,Hard,
546-
c,CERT-C,FLP30-C,Yes,Rule,,,Do not use floating-point variables as loop counters,,Statements,Easy,
546+
c,CERT-C,FLP30-C,Yes,Rule,,,Do not use floating-point variables as loop counters,,Statements4,Easy,
547547
c,CERT-C,FLP32-C,Yes,Rule,,,Prevent or detect domain and range errors in math functions,A0-4-4,Types,Medium,
548548
c,CERT-C,FLP34-C,Yes,Rule,,,Ensure that floating-point conversions are within range of the new type,,Types,Medium,
549549
c,CERT-C,FLP36-C,Yes,Rule,,,Preserve precision when converting integral values to floating-point type,,Types,Medium,
@@ -694,9 +694,9 @@ c,MISRA-C-2012,RULE-13-4,Yes,Advisory,,,The result of an assignment operator sho
694694
c,MISRA-C-2012,RULE-13-5,Yes,Required,,,The right hand operand of a logical && or || operator shall not contain persistent side effects,M5-14-1,SideEffects1,Import,
695695
c,MISRA-C-2012,RULE-13-6,Yes,Mandatory,,,The operand of the sizeof operator shall not contain any expressiosn which has potential side effects,M5-3-4,SideEffects1,Import,
696696
c,MISRA-C-2012,RULE-14-1,Yes,Required,,,A loop counter shall not have essentially floating type,FLP30-C A6-5-2,Types,Hard,
697-
c,MISRA-C-2012,RULE-14-2,Yes,Required,,,A for loop shall be well-formed,M6-5-1...M6-5-6,Statements,Medium,
697+
c,MISRA-C-2012,RULE-14-2,Yes,Required,,,A for loop shall be well-formed,M6-5-1...M6-5-6,Statements4,Medium,
698698
c,MISRA-C-2012,RULE-14-3,Yes,Required,,,Controlling expressions shall not be invariant,,Statements,Medium,
699-
c,MISRA-C-2012,RULE-14-4,Yes,Required,,,The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type,A5-0-2,Statements,Medium,
699+
c,MISRA-C-2012,RULE-14-4,Yes,Required,,,The controlling expression of an if statement and the controlling expression of an iteration-statement shall have essentially Boolean type,A5-0-2,Statements4,Medium,
700700
c,MISRA-C-2012,RULE-15-1,No,Advisory,,,The goto statement should not be used,A6-6-1,,Import,
701701
c,MISRA-C-2012,RULE-15-2,Yes,Required,,,The goto statement shall jump to a label declared later in the same function,M6-6-2,Statements2,Import,
702702
c,MISRA-C-2012,RULE-15-3,Yes,Required,,,"Any label referenced by a goto statement shall be declared in the same block, or in any block enclosing the goto statement",M6-6-1,Statements2,Import,

0 commit comments

Comments
 (0)