Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions change_notes/2026-03-17-share-out-of-range-enum-cast-query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `INT50-CPP` - `DoNotCastToAnOutOfRangeEnumerationValue.ql`:
- 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.
Original file line number Diff line number Diff line change
Expand Up @@ -18,44 +18,12 @@

import cpp
import codingstandards.cpp.cert
import codingstandards.cpp.Enums
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import codingstandards.cpp.SimpleRangeAnalysisCustomizations
import codingstandards.cpp.rules.donotcasttoanoutofrangeenumerationvalueshared.DoNotCastToAnOutOfRangeEnumerationValueShared

from Cast c, Enum e, string description
where
not isExcluded(c, TypeRangesPackage::doNotCastToAnOutOfRangeEnumerationValueQuery()) and
// Conversion from an integral type to an enum type
c.getExpr().getType().getUnspecifiedType() instanceof IntegralType and
c.getType().getUnspecifiedType() = e and
not (
// The deduced bound for the expression is within the type range for the explicit type
upperBound(c.getExpr()) <= Enums::getValueRangeUpperBound(e) and
lowerBound(c.getExpr()) >= Enums::getValueRangeLowerBound(e)
) and
// Not a compile time constant with the same value as an existing enum constant
not exists(float enumConstantValue |
enumConstantValue = Enums::getEnumConstantValue(e.getAnEnumConstant())
|
// Expression is a constant
c.getExpr().getValue().toFloat() = enumConstantValue
or
// Range analysis has precise bounds
enumConstantValue = upperBound(c.getExpr()) and
enumConstantValue = lowerBound(c.getExpr())
) and
(
if exists(upperBound(c.getExpr()))
then
description =
"Cast to enum $@ with value range " + Enums::getValueRangeLowerBound(e) + "..." +
Enums::getValueRangeUpperBound(e) + " from expression with wider value range " +
lowerBound(c.getExpr()) + "..." + upperBound(c.getExpr()) + " in function " +
c.getEnclosingFunction().getName() + "."
else
description =
"Cast to enum $@ with value range " + Enums::getValueRangeLowerBound(e) + "..." +
Enums::getValueRangeUpperBound(e) +
" from expression with a potentially wider value range."
)
select c, description, e, e.getName()
module DoNotCastToAnOutOfRangeEnumerationValueConfig implements
DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig
{
Query getQuery() { result = TypeRangesPackage::doNotCastToAnOutOfRangeEnumerationValueQuery() }
}

import DoNotCastToAnOutOfRangeEnumerationValueShared<DoNotCastToAnOutOfRangeEnumerationValueConfig>

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cpp/common/test/rules/donotcasttoanoutofrangeenumerationvalueshared/DoNotCastToAnOutOfRangeEnumerationValueShared.ql
19 changes: 18 additions & 1 deletion cpp/common/src/codingstandards/cpp/exclusions/cpp/Undefined.qll
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ newtype UndefinedQuery =
TCriticalUnspecifiedBehaviorQuery() or
TUndefinedBehaviorAuditQuery() or
TCriticalUnspecifiedBehaviorAuditQuery() or
TPossibleDataRaceBetweenThreadsQuery()
TPossibleDataRaceBetweenThreadsQuery() or
TOutOfRangeEnumCastCriticalUnspecifiedBehaviorQuery()

predicate isUndefinedQueryMetadata(Query query, string queryId, string ruleId, string category) {
query =
Expand Down Expand Up @@ -55,6 +56,15 @@ predicate isUndefinedQueryMetadata(Query query, string queryId, string ruleId, s
"cpp/misra/possible-data-race-between-threads" and
ruleId = "RULE-4-1-3" and
category = "required"
or
query =
// `Query` instance for the `outOfRangeEnumCastCriticalUnspecifiedBehavior` query
UndefinedPackage::outOfRangeEnumCastCriticalUnspecifiedBehaviorQuery() and
queryId =
// `@id` for the `outOfRangeEnumCastCriticalUnspecifiedBehavior` query
"cpp/misra/out-of-range-enum-cast-critical-unspecified-behavior" and
ruleId = "RULE-4-1-3" and
category = "required"
}

module UndefinedPackage {
Expand Down Expand Up @@ -92,4 +102,11 @@ module UndefinedPackage {
// `Query` type for `possibleDataRaceBetweenThreads` query
TQueryCPP(TUndefinedPackageQuery(TPossibleDataRaceBetweenThreadsQuery()))
}

Query outOfRangeEnumCastCriticalUnspecifiedBehaviorQuery() {
//autogenerate `Query` type
result =
// `Query` type for `outOfRangeEnumCastCriticalUnspecifiedBehavior` query
TQueryCPP(TUndefinedPackageQuery(TOutOfRangeEnumCastCriticalUnspecifiedBehaviorQuery()))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/**
* Provides a configurable module DoNotCastToAnOutOfRangeEnumerationValueShared with a `problems` predicate
* for the following issue:
* Casting to an out-of-range enumeration value leads to unspecified or undefined
* behavior.
*/

import cpp
import codingstandards.cpp.Customizations
import codingstandards.cpp.Exclusions
import codingstandards.cpp.Enums
import semmle.code.cpp.rangeanalysis.SimpleRangeAnalysis
import codingstandards.cpp.SimpleRangeAnalysisCustomizations

signature module DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig {
Query getQuery();
}

module DoNotCastToAnOutOfRangeEnumerationValueShared<
DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig Config>
{
query predicate problems(Cast c, string description, Enum e, string enumName) {
not isExcluded(c, Config::getQuery()) and
// Conversion from an integral type to an enum type
c.getExpr().getType().getUnspecifiedType() instanceof IntegralType and
c.getType().getUnspecifiedType() = e and
not (
// The deduced bound for the expression is within the type range for the explicit type
upperBound(c.getExpr()) <= Enums::getValueRangeUpperBound(e) and
lowerBound(c.getExpr()) >= Enums::getValueRangeLowerBound(e)
) and
// Not a compile time constant with the same value as an existing enum constant
not exists(float enumConstantValue |
enumConstantValue = Enums::getEnumConstantValue(e.getAnEnumConstant())
|
// Expression is a constant
c.getExpr().getValue().toFloat() = enumConstantValue
or
// Range analysis has precise bounds
enumConstantValue = upperBound(c.getExpr()) and
enumConstantValue = lowerBound(c.getExpr())
) and
enumName = e.getName() and
(
if exists(upperBound(c.getExpr()))
then
description =
"Cast to enum $@ with value range " + Enums::getValueRangeLowerBound(e) + "..." +
Enums::getValueRangeUpperBound(e) + " from expression with wider value range " +
lowerBound(c.getExpr()) + "..." + upperBound(c.getExpr()) + " in function " +
c.getEnclosingFunction().getName() + "."
else
description =
"Cast to enum $@ with value range " + Enums::getValueRangeLowerBound(e) + "..." +
Enums::getValueRangeUpperBound(e) +
" from expression with a potentially wider value range."
)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// GENERATED FILE - DO NOT MODIFY
import codingstandards.cpp.rules.donotcasttoanoutofrangeenumerationvalueshared.DoNotCastToAnOutOfRangeEnumerationValueShared

module TestFileConfig implements DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig {
Query getQuery() { result instanceof TestQuery }
}

import DoNotCastToAnOutOfRangeEnumerationValueShared<TestFileConfig>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @id cpp/misra/out-of-range-enum-cast-critical-unspecified-behavior
* @name RULE-4-1-3: Out-of-range enumeration cast leads to critical unspecified behavior
* @description Casting to an enumeration value outside the range of the enumeration's values
* results in critical unspecified behavior.
* @kind problem
* @precision high
* @problem.severity error
* @tags external/misra/id/rule-4-1-3
* correctness
* scope/system
* external/misra/enforcement/undecidable
* external/misra/obligation/required
*/

import cpp
import codingstandards.cpp.misra
import codingstandards.cpp.rules.donotcasttoanoutofrangeenumerationvalueshared.DoNotCastToAnOutOfRangeEnumerationValueShared

module OutOfRangeEnumCastCriticalUnspecifiedBehaviorConfig implements
DoNotCastToAnOutOfRangeEnumerationValueSharedConfigSig
{
Query getQuery() {
result = UndefinedPackage::outOfRangeEnumCastCriticalUnspecifiedBehaviorQuery()
}
}

import DoNotCastToAnOutOfRangeEnumerationValueShared<OutOfRangeEnumCastCriticalUnspecifiedBehaviorConfig>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cpp/common/test/rules/donotcasttoanoutofrangeenumerationvalueshared/DoNotCastToAnOutOfRangeEnumerationValueShared.ql
1 change: 1 addition & 0 deletions rule_packages/cpp/TypeRanges.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@
"name": "Do not cast to an out-of-range enumeration value",
"precision": "high",
"severity": "error",
"shared_implementation_short_name": "DoNotCastToAnOutOfRangeEnumerationValueShared",
"short_name": "DoNotCastToAnOutOfRangeEnumerationValue",
"tags": [
"correctness",
Expand Down
13 changes: 13 additions & 0 deletions rule_packages/cpp/Undefined.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,19 @@
"concurrency",
"scope/system"
]
},
{
"description": "Casting to an enumeration value outside the range of the enumeration's values results in critical unspecified behavior.",
"kind": "problem",
"name": "Out-of-range enumeration cast leads to critical unspecified behavior",
"precision": "high",
"severity": "error",
"shared_implementation_short_name": "DoNotCastToAnOutOfRangeEnumerationValueShared",
"short_name": "OutOfRangeEnumCastCriticalUnspecifiedBehavior",
"tags": [
"correctness",
"scope/system"
]
}
],
"title": "There shall be no occurrence of undefined or critical unspecified behaviour"
Expand Down
Loading