Skip to content

Commit 992b672

Browse files
Merge pull request #1088 from github/michaelrfairhurst/package-undefined-behavior-cast-enum-out-of-range
Import INT50-CPP into 4-1-3
2 parents 458e649 + de08d26 commit 992b672

File tree

13 files changed

+139
-42
lines changed

13 files changed

+139
-42
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- `INT50-CPP` - `DoNotCastToAnOutOfRangeEnumerationValue.ql`:
2+
- Refactored query logic into a shared library (`DoNotCastToAnOutOfRangeEnumerationValueShared.qll`) to enable reuse by MISRA C++ `RULE-4-1-3`. The query logic is unchanged and no visible changes to results or performance are expected.

cpp/cert/src/rules/INT50-CPP/DoNotCastToAnOutOfRangeEnumerationValue.ql

Lines changed: 8 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,44 +18,12 @@
1818

1919
import cpp
2020
import codingstandards.cpp.cert
21-
import codingstandards.cpp.Enums
22-
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
23-
import codingstandards.cpp.SimpleRangeAnalysisCustomizations
21+
import codingstandards.cpp.rules.donotcasttoanoutofrangeenumerationvalueshared.DoNotCastToAnOutOfRangeEnumerationValueShared
2422

25-
from Cast c, Enum e, string description
26-
where
27-
not isExcluded(c, TypeRangesPackage::doNotCastToAnOutOfRangeEnumerationValueQuery()) and
28-
// Conversion from an integral type to an enum type
29-
c.getExpr().getType().getUnspecifiedType() instanceof IntegralType and
30-
c.getType().getUnspecifiedType() = e and
31-
not (
32-
// The deduced bound for the expression is within the type range for the explicit type
33-
upperBound(c.getExpr()) <= Enums::getValueRangeUpperBound(e) and
34-
lowerBound(c.getExpr()) >= Enums::getValueRangeLowerBound(e)
35-
) and
36-
// Not a compile time constant with the same value as an existing enum constant
37-
not exists(float enumConstantValue |
38-
enumConstantValue = Enums::getEnumConstantValue(e.getAnEnumConstant())
39-
|
40-
// Expression is a constant
41-
c.getExpr().getValue().toFloat() = enumConstantValue
42-
or
43-
// Range analysis has precise bounds
44-
enumConstantValue = upperBound(c.getExpr()) and
45-
enumConstantValue = lowerBound(c.getExpr())
46-
) and
47-
(
48-
if exists(upperBound(c.getExpr()))
49-
then
50-
description =
51-
"Cast to enum $@ with value range " + Enums::getValueRangeLowerBound(e) + "..." +
52-
Enums::getValueRangeUpperBound(e) + " from expression with wider value range " +
53-
lowerBound(c.getExpr()) + "..." + upperBound(c.getExpr()) + " in function " +
54-
c.getEnclosingFunction().getName() + "."
55-
else
56-
description =
57-
"Cast to enum $@ with value range " + Enums::getValueRangeLowerBound(e) + "..." +
58-
Enums::getValueRangeUpperBound(e) +
59-
" from expression with a potentially wider value range."
60-
)
61-
select c, description, e, e.getName()
23+
module DoNotCastToAnOutOfRangeEnumerationValueConfig implements
24+
DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig
25+
{
26+
Query getQuery() { result = TypeRangesPackage::doNotCastToAnOutOfRangeEnumerationValueQuery() }
27+
}
28+
29+
import DoNotCastToAnOutOfRangeEnumerationValueShared<DoNotCastToAnOutOfRangeEnumerationValueConfig>

cpp/cert/test/rules/INT50-CPP/DoNotCastToAnOutOfRangeEnumerationValue.qlref

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cpp/common/test/rules/donotcasttoanoutofrangeenumerationvalueshared/DoNotCastToAnOutOfRangeEnumerationValueShared.ql

cpp/common/src/codingstandards/cpp/exclusions/cpp/Undefined.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ newtype UndefinedQuery =
99
TDivisionByZeroUndefinedBehaviorQuery() or
1010
TDeallocationTypeMismatchQuery() or
1111
TStringLiteralPossiblyModifiedAuditQuery() or
12+
TOutOfRangeEnumCastCriticalUnspecifiedBehaviorQuery() or
1213
TNullPointerToMemberAccessUndefinedBehaviorQuery() or
1314
TUninitializedStaticPointerToMemberUndefinedBehaviorQuery() or
1415
TNonExistentMemberAccessUndefinedBehaviorQuery()
@@ -59,6 +60,15 @@ predicate isUndefinedQueryMetadata(Query query, string queryId, string ruleId, s
5960
ruleId = "RULE-4-1-3" and
6061
category = "required"
6162
or
63+
query =
64+
// `Query` instance for the `outOfRangeEnumCastCriticalUnspecifiedBehavior` query
65+
UndefinedPackage::outOfRangeEnumCastCriticalUnspecifiedBehaviorQuery() and
66+
queryId =
67+
// `@id` for the `outOfRangeEnumCastCriticalUnspecifiedBehavior` query
68+
"cpp/misra/out-of-range-enum-cast-critical-unspecified-behavior" and
69+
ruleId = "RULE-4-1-3" and
70+
category = "required"
71+
or
6272
query =
6373
// `Query` instance for the `nullPointerToMemberAccessUndefinedBehavior` query
6474
UndefinedPackage::nullPointerToMemberAccessUndefinedBehaviorQuery() and
@@ -123,6 +133,13 @@ module UndefinedPackage {
123133
TQueryCPP(TUndefinedPackageQuery(TStringLiteralPossiblyModifiedAuditQuery()))
124134
}
125135

136+
Query outOfRangeEnumCastCriticalUnspecifiedBehaviorQuery() {
137+
//autogenerate `Query` type
138+
result =
139+
// `Query` type for `outOfRangeEnumCastCriticalUnspecifiedBehavior` query
140+
TQueryCPP(TUndefinedPackageQuery(TOutOfRangeEnumCastCriticalUnspecifiedBehaviorQuery()))
141+
}
142+
126143
Query nullPointerToMemberAccessUndefinedBehaviorQuery() {
127144
//autogenerate `Query` type
128145
result =
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Provides a configurable module DoNotCastToAnOutOfRangeEnumerationValueShared with a `problems` predicate
3+
* for the following issue:
4+
* Casting to an out-of-range enumeration value leads to unspecified or undefined
5+
* behavior.
6+
*/
7+
8+
import cpp
9+
import codingstandards.cpp.Customizations
10+
import codingstandards.cpp.Exclusions
11+
import codingstandards.cpp.Enums
12+
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
13+
import codingstandards.cpp.SimpleRangeAnalysisCustomizations
14+
15+
signature module DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig {
16+
Query getQuery();
17+
}
18+
19+
module DoNotCastToAnOutOfRangeEnumerationValueShared<
20+
DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig Config>
21+
{
22+
query predicate problems(Cast c, string description, Enum e, string enumName) {
23+
not isExcluded(c, Config::getQuery()) and
24+
// Conversion from an integral type to an enum type
25+
c.getExpr().getType().getUnspecifiedType() instanceof IntegralType and
26+
c.getType().getUnspecifiedType() = e and
27+
not (
28+
// The deduced bound for the expression is within the type range for the explicit type
29+
upperBound(c.getExpr()) <= Enums::getValueRangeUpperBound(e) and
30+
lowerBound(c.getExpr()) >= Enums::getValueRangeLowerBound(e)
31+
) and
32+
// Not a compile time constant with the same value as an existing enum constant
33+
not exists(float enumConstantValue |
34+
enumConstantValue = Enums::getEnumConstantValue(e.getAnEnumConstant())
35+
|
36+
// Expression is a constant
37+
c.getExpr().getValue().toFloat() = enumConstantValue
38+
or
39+
// Range analysis has precise bounds
40+
enumConstantValue = upperBound(c.getExpr()) and
41+
enumConstantValue = lowerBound(c.getExpr())
42+
) and
43+
enumName = e.getName() and
44+
(
45+
if exists(upperBound(c.getExpr()))
46+
then
47+
description =
48+
"Cast to enum $@ with value range " + Enums::getValueRangeLowerBound(e) + "..." +
49+
Enums::getValueRangeUpperBound(e) + " from expression with wider value range " +
50+
lowerBound(c.getExpr()) + "..." + upperBound(c.getExpr()) + " in function " +
51+
c.getEnclosingFunction().getName() + "."
52+
else
53+
description =
54+
"Cast to enum $@ with value range " + Enums::getValueRangeLowerBound(e) + "..." +
55+
Enums::getValueRangeUpperBound(e) +
56+
" from expression with a potentially wider value range."
57+
)
58+
}
59+
}

cpp/cert/test/rules/INT50-CPP/DoNotCastToAnOutOfRangeEnumerationValue.expected renamed to cpp/common/test/rules/donotcasttoanoutofrangeenumerationvalueshared/DoNotCastToAnOutOfRangeEnumerationValueShared.expected

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
| test.cpp:9:11:9:16 | (Foo)... | Cast to enum $@ with value range 0...3 from expression with wider value range -2147483648...2147483647 in function test_unconstrained_cast. | test.cpp:3:6:3:8 | Foo | Foo |
22
| test.cpp:15:13:15:18 | (Foo)... | Cast to enum $@ with value range 0...3 from expression with wider value range 0...2147483647 in function test_range_check. | test.cpp:3:6:3:8 | Foo | Foo |
33
| test.cpp:20:16:20:21 | (Foo)... | Cast to enum $@ with value range 0...3 from expression with wider value range 0...4 in function test_range_check. | test.cpp:3:6:3:8 | Foo | Foo |
4-
| test.cpp:27:12:27:25 | (Foo)... | Cast to enum $@ with value range 0...3 from expression with wider value range 0...7 in function test_unsanitize. | test.cpp:3:6:3:8 | Foo | Foo |
4+
| test.cpp:27:12:27:25 | (Foo)... | Cast to enum $@ with value range 0...3 from expression with wider value range 0...7 in function test_unsanitize. | test.cpp:3:6:3:8 | Foo | Foo |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// GENERATED FILE - DO NOT MODIFY
2+
import codingstandards.cpp.rules.donotcasttoanoutofrangeenumerationvalueshared.DoNotCastToAnOutOfRangeEnumerationValueShared
3+
4+
module TestFileConfig implements DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig {
5+
Query getQuery() { result instanceof TestQuery }
6+
}
7+
8+
import DoNotCastToAnOutOfRangeEnumerationValueShared<TestFileConfig>

cpp/cert/test/rules/INT50-CPP/test.cpp renamed to cpp/common/test/rules/donotcasttoanoutofrangeenumerationvalueshared/test.cpp

File renamed without changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* @id cpp/misra/out-of-range-enum-cast-critical-unspecified-behavior
3+
* @name RULE-4-1-3: Out-of-range enumeration cast leads to critical unspecified behavior
4+
* @description Casting to an enumeration value outside the range of the enumeration's values
5+
* results in critical unspecified behavior.
6+
* @kind problem
7+
* @precision high
8+
* @problem.severity error
9+
* @tags external/misra/id/rule-4-1-3
10+
* correctness
11+
* scope/system
12+
* external/misra/enforcement/undecidable
13+
* external/misra/obligation/required
14+
*/
15+
16+
import cpp
17+
import codingstandards.cpp.misra
18+
import codingstandards.cpp.rules.donotcasttoanoutofrangeenumerationvalueshared.DoNotCastToAnOutOfRangeEnumerationValueShared
19+
20+
module OutOfRangeEnumCastCriticalUnspecifiedBehaviorConfig implements
21+
DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig
22+
{
23+
Query getQuery() {
24+
result = UndefinedPackage::outOfRangeEnumCastCriticalUnspecifiedBehaviorQuery()
25+
}
26+
}
27+
28+
import DoNotCastToAnOutOfRangeEnumerationValueShared<OutOfRangeEnumCastCriticalUnspecifiedBehaviorConfig>

0 commit comments

Comments
 (0)