Skip to content

Commit 3d09627

Browse files
committed
FloatingTypes: Implement FLP37-C
Add a query for finding float values compared by memcmp.
1 parent 53d50ae commit 3d09627

File tree

5 files changed

+182
-0
lines changed

5 files changed

+182
-0
lines changed
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# FLP37-C: Do not use object representations to compare floating-point values
2+
3+
This query implements the CERT-C rule FLP37-C:
4+
5+
> Do not use object representations to compare floating-point values
6+
7+
8+
## Description
9+
10+
The object representation for floating-point values is implementation defined. However, an implementation that defines the `__STDC_IEC_559__` macro shall conform to the IEC 60559 floating-point standard and uses what is frequently referred to as IEEE 754 floating-point arithmetic \[[ISO/IEC 9899:2011](https://wiki.sei.cmu.edu/confluence/display/c/AA.+Bibliography#AA.Bibliography-ISO%2FIEC9899-2011)\]. The floating-point object representation used by IEC 60559 is one of the most common floating-point object representations in use today.
11+
12+
All floating-point object representations use specific bit patterns to encode the value of the floating-point number being represented. However, equivalence of floating-point values is not encoded solely by the bit pattern used to represent the value. For instance, if the floating-point format supports negative zero values (as IEC 60559 does), the values `-0.0` and `0.0` are equivalent and will compare as equal, but the bit patterns used in the object representation are not identical. Similarly, if two floating-point values are both (the same) NaN, they will not compare as equal, despite the bit patterns being identical, because they are not equivalent.
13+
14+
Do not compare floating-point object representations directly, such as by calling `memcmp()`or its moral equivalents. Instead, the equality operators (`==` and `!=`) should be used to determine if two floating-point values are equivalent.
15+
16+
## Noncompliant Code Example
17+
18+
In this noncompliant code example, `memcmp()` is used to compare two structures for equality. However, since the structure contains a floating-point object, this code may not behave as the programmer intended.
19+
20+
```cpp
21+
#include <stdbool.h>
22+
#include <string.h>
23+
24+
struct S {
25+
int i;
26+
float f;
27+
};
28+
29+
bool are_equal(const struct S *s1, const struct S *s2) {
30+
if (!s1 && !s2)
31+
return true;
32+
else if (!s1 || !s2)
33+
return false;
34+
return 0 == memcmp(s1, s2, sizeof(struct S));
35+
}
36+
```
37+
38+
## Compliant Solution
39+
40+
In this compliant solution, the structure members are compared individually:
41+
42+
```cpp
43+
#include <stdbool.h>
44+
#include <string.h>
45+
46+
struct S {
47+
int i;
48+
float f;
49+
};
50+
51+
bool are_equal(const struct S *s1, const struct S *s2) {
52+
if (!s1 && !s2)
53+
return true;
54+
else if (!s1 || !s2)
55+
return false;
56+
return s1->i == s2->i &&
57+
s1->f == s2->f;
58+
}
59+
```
60+
61+
## Risk Assessment
62+
63+
Using the object representation of a floating-point value for comparisons can lead to incorrect equality results, which can lead to unexpected behavior.
64+
65+
<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> FLP37-C </td> <td> Low </td> <td> Unlikely </td> <td> Medium </td> <td> <strong>P2</strong> </td> <td> <strong>L3</strong> </td> </tr> </tbody> </table>
66+
67+
68+
## Automated Detection
69+
70+
<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>memcmp-with-float</strong> </td> <td> Partially checked </td> </tr> <tr> <td> <a> Axivion Bauhaus Suite </a> </td> <td> 7.2.0 </td> <td> <strong>CertC-FLP37</strong> </td> <td> Fully implemented </td> </tr> <tr> <td> <a> Helix QAC </a> </td> <td> 2022.4 </td> <td> <strong>C5026</strong> <strong>C++3118</strong> </td> <td> </td> </tr> <tr> <td> <a> Klocwork </a> </td> <td> 2022.4 </td> <td> <strong>MISRA.STDLIB.MEMCMP.PTR_ARG_TYPES</strong> <strong>CERT.MEMCMP.FLOAT_MEMBER</strong> </td> <td> </td> </tr> <tr> <td> <a> LDRA tool suite </a> </td> <td> 9.7.1 </td> <td> <strong>618 S</strong> </td> <td> Enhanced Enforcement </td> </tr> <tr> <td> <a> Parasoft C/C++test </a> </td> <td> 2022.2 </td> <td> <strong>CERT_C-FLP37-c</strong> </td> <td> Do not use object representations to compare floating-point values </td> </tr> <tr> <td> <a> PC-lint Plus </a> </td> <td> 1.4 </td> <td> <strong>2498, 2499</strong> </td> <td> Fully supported </td> </tr> <tr> <td> <a> Polyspace Bug Finder </a> </td> <td> R2022b </td> <td> <a> CERT C: Rule FLP37-C </a> </td> <td> Checks for memory comparison of floating-point values (rule fully covered) </td> </tr> <tr> <td> <a> PRQA QA-C </a> </td> <td> 9.7 </td> <td> <strong>5026</strong> </td> <td> </td> </tr> <tr> <td> <a> PVS-Studio </a> </td> <td> 7.23 </td> <td> <strong><a>V1014</a></strong> </td> <td> </td> </tr> <tr> <td> <a> RuleChecker </a> </td> <td> 22.04 </td> <td> <strong>memcmp-with-float</strong> </td> <td> Partially checked </td> </tr> <tr> <td> <a> TrustInSoft Analyzer </a> </td> <td> 1.38 </td> <td> </td> <td> Exhaustively verified. </td> </tr> </tbody> </table>
71+
72+
73+
## Related Vulnerabilities
74+
75+
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+FLP37-C).
76+
77+
## Bibliography
78+
79+
<table> <tbody> <tr> <td> \[ <a> ISO/IEC 9899:2011 </a> \] </td> <td> Annex F, " IEC 60559 floating-point arithmetic" </td> </tr> </tbody> </table>
80+
81+
82+
## Implementation notes
83+
84+
None
85+
86+
## References
87+
88+
* CERT-C: [FLP37-C: Do not use object representations to compare floating-point values](https://wiki.sei.cmu.edu/confluence/display/c)
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @id c/cert/memcmp-used-to-compare-floats
3+
* @name FLP37-C: Do not use object representations to compare floating-point values
4+
* @description
5+
* @kind problem
6+
* @precision very-high
7+
* @problem.severity error
8+
* @tags external/cert/id/flp37-c
9+
* correctness
10+
* external/cert/obligation/rule
11+
*/
12+
13+
import cpp
14+
import codingstandards.c.cert
15+
import semmle.code.cpp.security.BufferAccess
16+
17+
/**
18+
* A type which contains, directly or indirectly, a floating-point type.
19+
*/
20+
class FloatContainingType extends Type {
21+
FloatContainingType() {
22+
this instanceof FloatingPointType
23+
or
24+
this.(Class).getAField().getType().getUnspecifiedType() instanceof FloatContainingType
25+
}
26+
}
27+
28+
from MemcmpBA cmp, string buffDesc, Expr arg, FloatContainingType type
29+
where
30+
not isExcluded(cmp, FloatingTypesPackage::memcmpUsedToCompareFloatsQuery()) and
31+
arg = cmp.getBuffer(buffDesc, _) and
32+
arg.getUnconverted().getUnspecifiedType().(PointerType).getBaseType() = type
33+
select cmp,
34+
"memcmp is used to compare a floating-point value in the $@ which is of type " + type + ".", arg,
35+
buffDesc
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
| test.c:27:3:27:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type float. | test.c:27:10:27:12 | & ... | first buffer |
2+
| test.c:27:3:27:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type float. | test.c:27:15:27:17 | & ... | second buffer |
3+
| test.c:35:3:35:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S2. | test.c:35:10:35:13 | & ... | first buffer |
4+
| test.c:35:3:35:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S2. | test.c:35:16:35:19 | & ... | second buffer |
5+
| test.c:39:3:39:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S3. | test.c:39:10:39:13 | & ... | first buffer |
6+
| test.c:39:3:39:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S3. | test.c:39:16:39:19 | & ... | second buffer |
7+
| test.c:43:3:43:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S4. | test.c:43:10:43:13 | & ... | first buffer |
8+
| test.c:43:3:43:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S4. | test.c:43:16:43:19 | & ... | second buffer |
9+
| test.c:47:3:47:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S5. | test.c:47:10:47:13 | & ... | first buffer |
10+
| test.c:47:3:47:8 | call to memcmp | memcmp is used to compare a floating-point value in the $@ which is of type S5. | test.c:47:16:47:19 | & ... | second buffer |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
rules/FLP37-C/MemcmpUsedToCompareFloats.ql

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <string.h>
2+
3+
struct S1 {
4+
int i;
5+
};
6+
7+
struct S2 {
8+
float f;
9+
};
10+
11+
struct S3 {
12+
struct S2 s2;
13+
};
14+
15+
struct S4 {
16+
struct S3 s3;
17+
};
18+
19+
struct S5 {
20+
union {
21+
float f1;
22+
int i1;
23+
};
24+
};
25+
26+
void test_float_memcmp(float f1, float f2) {
27+
memcmp(&f1, &f2, sizeof(float)); // NON_COMPLIANT
28+
}
29+
30+
void test_struct_int_memcmp(struct S1 s1a, struct S1 s1b) {
31+
memcmp(&s1a, &s1b, sizeof(struct S1)); // COMPLIANT
32+
}
33+
34+
void test_struct_float_memcmp(struct S2 s2a, struct S2 s2b) {
35+
memcmp(&s2a, &s2b, sizeof(struct S2)); // NON_COMPLIANT
36+
}
37+
38+
void test_struct_nested_float_memcmp(struct S3 s3a, struct S3 s3b) {
39+
memcmp(&s3a, &s3b, sizeof(struct S3)); // NON_COMPLIANT
40+
}
41+
42+
void test_struct_nested_nested_float_memcmp(struct S4 s4a, struct S4 s4b) {
43+
memcmp(&s4a, &s4b, sizeof(struct S4)); // NON_COMPLIANT
44+
}
45+
46+
void test_union_nested_float_memcmp(struct S5 s5a, struct S5 s5b) {
47+
memcmp(&s5a, &s5b, sizeof(struct S5)); // NON_COMPLIANT
48+
}

0 commit comments

Comments
 (0)