From 0fc649c745663826e5068cd6b803bc6a566d9879 Mon Sep 17 00:00:00 2001 From: Mike Fairhurst Date: Fri, 21 Nov 2025 17:38:00 -0800 Subject: [PATCH] Implement RULE-8-2-9 - PolymorphicClassTypeExpressionInTypeid Detects use of polymorphic class type expressions as operands to typeid, which may cause unclear evaluation behavior and potential std::bad_typeid exceptions. --- .../cpp/exclusions/cpp/Preconditions1.qll | 26 +++++++ .../cpp/exclusions/cpp/RuleMetadata.qll | 3 + .../PolymorphicClassTypeExpressionInTypeid.ql | 28 +++++++ ...orphicClassTypeExpressionInTypeid.expected | 6 ++ ...lymorphicClassTypeExpressionInTypeid.qlref | 1 + cpp/misra/test/rules/RULE-8-2-9/test.cpp | 75 +++++++++++++++++++ rule_packages/cpp/Preconditions1.json | 27 +++++++ rules.csv | 2 +- 8 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions1.qll create mode 100644 cpp/misra/src/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.ql create mode 100644 cpp/misra/test/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.expected create mode 100644 cpp/misra/test/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.qlref create mode 100644 cpp/misra/test/rules/RULE-8-2-9/test.cpp create mode 100644 rule_packages/cpp/Preconditions1.json diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions1.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions1.qll new file mode 100644 index 000000000..9f4902af7 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Preconditions1.qll @@ -0,0 +1,26 @@ +//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/ +import cpp +import RuleMetadata +import codingstandards.cpp.exclusions.RuleMetadata + +newtype Preconditions1Query = TPolymorphicClassTypeExpressionInTypeidQuery() + +predicate isPreconditions1QueryMetadata(Query query, string queryId, string ruleId, string category) { + query = + // `Query` instance for the `polymorphicClassTypeExpressionInTypeid` query + Preconditions1Package::polymorphicClassTypeExpressionInTypeidQuery() and + queryId = + // `@id` for the `polymorphicClassTypeExpressionInTypeid` query + "cpp/misra/polymorphic-class-type-expression-in-typeid" and + ruleId = "RULE-8-2-9" and + category = "required" +} + +module Preconditions1Package { + Query polymorphicClassTypeExpressionInTypeidQuery() { + //autogenerate `Query` type + result = + // `Query` type for `polymorphicClassTypeExpressionInTypeid` query + TQueryCPP(TPreconditions1PackageQuery(TPolymorphicClassTypeExpressionInTypeidQuery())) + } +} diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll index 2e2403165..6d44eb693 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/RuleMetadata.qll @@ -44,6 +44,7 @@ import Operators import OrderOfEvaluation import OutOfBounds import Pointers +import Preconditions1 import Representation import Scope import SideEffects1 @@ -103,6 +104,7 @@ newtype TCPPQuery = TOrderOfEvaluationPackageQuery(OrderOfEvaluationQuery q) or TOutOfBoundsPackageQuery(OutOfBoundsQuery q) or TPointersPackageQuery(PointersQuery q) or + TPreconditions1PackageQuery(Preconditions1Query q) or TRepresentationPackageQuery(RepresentationQuery q) or TScopePackageQuery(ScopeQuery q) or TSideEffects1PackageQuery(SideEffects1Query q) or @@ -162,6 +164,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat isOrderOfEvaluationQueryMetadata(query, queryId, ruleId, category) or isOutOfBoundsQueryMetadata(query, queryId, ruleId, category) or isPointersQueryMetadata(query, queryId, ruleId, category) or + isPreconditions1QueryMetadata(query, queryId, ruleId, category) or isRepresentationQueryMetadata(query, queryId, ruleId, category) or isScopeQueryMetadata(query, queryId, ruleId, category) or isSideEffects1QueryMetadata(query, queryId, ruleId, category) or diff --git a/cpp/misra/src/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.ql b/cpp/misra/src/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.ql new file mode 100644 index 000000000..373a19f5f --- /dev/null +++ b/cpp/misra/src/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.ql @@ -0,0 +1,28 @@ +/** + * @id cpp/misra/polymorphic-class-type-expression-in-typeid + * @name RULE-8-2-9: The operand to typeid shall not be an expression of polymorphic class type + * @description It is unclear whether a typeid expression will be evaluated at runtime or compile + * time when its static type is a polymorphic class type. + * @kind problem + * @precision very-high + * @problem.severity error + * @tags external/misra/id/rule-8-2-9 + * scope/single-translation-unit + * readability + * correctness + * performance + * external/misra/enforcement/decidable + * external/misra/obligation/required + */ + +import cpp +import codingstandards.cpp.misra + +from TypeidOperator typeid, Expr operand, Class polymorphicClass +where + not isExcluded(typeid, Preconditions1Package::polymorphicClassTypeExpressionInTypeidQuery()) and + operand = typeid.getExpr().getFullyConverted() and + polymorphicClass = operand.getType().getUnderlyingType() and + polymorphicClass.isPolymorphic() +select typeid, "Use of 'typeid' with $@ of polymorphic class type $@.", operand, "expression", + polymorphicClass, polymorphicClass.getName() diff --git a/cpp/misra/test/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.expected b/cpp/misra/test/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.expected new file mode 100644 index 000000000..c438655a2 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.expected @@ -0,0 +1,6 @@ +| test.cpp:41:30:41:39 | typeid ... | Use of 'typeid' with $@ of polymorphic class type $@. | test.cpp:41:37:41:38 | l1 | expression | test.cpp:7:8:7:24 | PolymorphicStruct | PolymorphicStruct | +| test.cpp:42:30:42:40 | typeid ... | Use of 'typeid' with $@ of polymorphic class type $@. | test.cpp:42:37:42:39 | * ... | expression | test.cpp:7:8:7:24 | PolymorphicStruct | PolymorphicStruct | +| test.cpp:43:30:43:39 | typeid ... | Use of 'typeid' with $@ of polymorphic class type $@. | test.cpp:43:37:43:38 | l3 | expression | test.cpp:11:8:11:25 | DerivedPolymorphic | DerivedPolymorphic | +| test.cpp:44:30:44:39 | typeid ... | Use of 'typeid' with $@ of polymorphic class type $@. | test.cpp:44:37:44:38 | l4 | expression | test.cpp:15:7:15:22 | PolymorphicClass | PolymorphicClass | +| test.cpp:57:30:57:39 | typeid ... | Use of 'typeid' with $@ of polymorphic class type $@. | test.cpp:57:37:57:38 | (reference dereference) | expression | test.cpp:7:8:7:24 | PolymorphicStruct | PolymorphicStruct | +| test.cpp:61:30:61:56 | typeid ... | Use of 'typeid' with $@ of polymorphic class type $@. | test.cpp:61:37:61:55 | temporary object | expression | test.cpp:7:8:7:24 | PolymorphicStruct | PolymorphicStruct | diff --git a/cpp/misra/test/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.qlref b/cpp/misra/test/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.qlref new file mode 100644 index 000000000..44eb632a7 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.qlref @@ -0,0 +1 @@ +rules/RULE-8-2-9/PolymorphicClassTypeExpressionInTypeid.ql \ No newline at end of file diff --git a/cpp/misra/test/rules/RULE-8-2-9/test.cpp b/cpp/misra/test/rules/RULE-8-2-9/test.cpp new file mode 100644 index 000000000..ac0db6403 --- /dev/null +++ b/cpp/misra/test/rules/RULE-8-2-9/test.cpp @@ -0,0 +1,75 @@ +#include + +struct NonPolymorphic { + void foo() {} +}; + +struct PolymorphicStruct { + virtual void foo() {} +}; + +struct DerivedPolymorphic : public PolymorphicStruct { + void foo() override {} +}; + +class PolymorphicClass { +public: + virtual ~PolymorphicClass() = default; + virtual void method() {} +}; + +void test_typeid_with_type_id() { + const std::type_info &l1 = typeid(NonPolymorphic); // COMPLIANT + const std::type_info &l2 = typeid(PolymorphicStruct); // COMPLIANT + const std::type_info &l3 = typeid(PolymorphicClass); // COMPLIANT +} + +void test_typeid_with_non_polymorphic_expression() { + NonPolymorphic l1; + NonPolymorphic *l2 = &l1; + + const std::type_info &l3 = typeid(l1); // COMPLIANT + const std::type_info &l4 = typeid(*l2); // COMPLIANT +} + +void test_typeid_with_polymorphic_expression() { + PolymorphicStruct l1; + PolymorphicStruct *l2 = &l1; + DerivedPolymorphic l3; + PolymorphicClass l4; + + const std::type_info &l5 = typeid(l1); // NON_COMPLIANT + const std::type_info &l6 = typeid(*l2); // NON_COMPLIANT + const std::type_info &l7 = typeid(l3); // NON_COMPLIANT + const std::type_info &l8 = typeid(l4); // NON_COMPLIANT +} + +void test_typeid_with_polymorphic_function_call() { + PolymorphicStruct l1; + + const std::type_info &l2 = typeid(l1.foo()); // COMPLIANT +} + +void test_typeid_with_reference_to_polymorphic() { + PolymorphicStruct l1; + PolymorphicStruct &l2 = l1; + + const std::type_info &l3 = typeid(l2); // NON_COMPLIANT +} + +void test_typeid_with_polymorphic_temporary() { + const std::type_info &l1 = typeid(PolymorphicStruct{}); // NON_COMPLIANT +} + +void test_typeid_with_polymorphic_pointer() { + NonPolymorphic *l1 = new NonPolymorphic(); + PolymorphicStruct *l2 = new PolymorphicStruct(); + DerivedPolymorphic *l3 = new DerivedPolymorphic(); + PolymorphicClass *l4 = new PolymorphicClass(); + + // Pointer types are not polymorphic themselves + const std::type_info &l5 = typeid(l1); // COMPLIANT + const std::type_info &l6 = typeid(l2); // COMPLIANT + const std::type_info &l7 = typeid(l3); // COMPLIANT + const std::type_info &l8 = typeid(l4); // COMPLIANT +} \ No newline at end of file diff --git a/rule_packages/cpp/Preconditions1.json b/rule_packages/cpp/Preconditions1.json new file mode 100644 index 000000000..d1d96a476 --- /dev/null +++ b/rule_packages/cpp/Preconditions1.json @@ -0,0 +1,27 @@ +{ + "MISRA-C++-2023": { + "RULE-8-2-9": { + "properties": { + "enforcement": "decidable", + "obligation": "required" + }, + "queries": [ + { + "description": "It is unclear whether a typeid expression will be evaluated at runtime or compile time when its static type is a polymorphic class type.", + "kind": "problem", + "name": "The operand to typeid shall not be an expression of polymorphic class type", + "precision": "very-high", + "severity": "error", + "short_name": "PolymorphicClassTypeExpressionInTypeid", + "tags": [ + "scope/single-translation-unit", + "readability", + "correctness", + "performance" + ] + } + ], + "title": "The operand to typeid shall not be an expression of polymorphic class type" + } + } +} \ No newline at end of file diff --git a/rules.csv b/rules.csv index 3cb64a0ba..25c287ecc 100644 --- a/rules.csv +++ b/rules.csv @@ -890,7 +890,7 @@ cpp,MISRA-C++-2023,RULE-8-2-5,Yes,Required,Decidable,Single Translation Unit,rei cpp,MISRA-C++-2023,RULE-8-2-6,Yes,Required,Decidable,Single Translation Unit,"An object with integral, enumerated, or pointer to void type shall not be cast to a pointer type","RULE-11-6, INT36-C",Conversions2,Easy, cpp,MISRA-C++-2023,RULE-8-2-7,Yes,Advisory,Decidable,Single Translation Unit,A cast should not convert a pointer type to an integral type,"RULE-11-6, INT36-C",Conversions2,Easy, cpp,MISRA-C++-2023,RULE-8-2-8,Yes,Required,Decidable,Single Translation Unit,An object pointer type shall not be cast to an integral type other than std::uintptr_t or std::intptr_t,"RULE-11-6, INT36-C",Conversions2,Easy, -cpp,MISRA-C++-2023,RULE-8-2-9,Yes,Required,Decidable,Single Translation Unit,The operand to typeid shall not be an expression of polymorphic class type,,Preconditions,Easy, +cpp,MISRA-C++-2023,RULE-8-2-9,Yes,Required,Decidable,Single Translation Unit,The operand to typeid shall not be an expression of polymorphic class type,,Preconditions1,Easy, cpp,MISRA-C++-2023,RULE-8-2-10,Yes,Required,Undecidable,System,"Functions shall not call themselves, either directly or indirectly",A7-5-2,ImportMisra23,Import, cpp,MISRA-C++-2023,RULE-8-2-11,Yes,Required,Decidable,Single Translation Unit,An argument passed via ellipsis shall have an appropriate type,,Preconditions,Easy, cpp,MISRA-C++-2023,RULE-8-3-1,Yes,Advisory,Decidable,Single Translation Unit,The built-in unary - operator should not be applied to an expression of unsigned type,M5-3-2,ImportMisra23,Import,