diff --git a/change_notes/2026-03-13-share-deallocation-type-mismatch-query.md b/change_notes/2026-03-13-share-deallocation-type-mismatch-query.md new file mode 100644 index 0000000000..c9998d237a --- /dev/null +++ b/change_notes/2026-03-13-share-deallocation-type-mismatch-query.md @@ -0,0 +1,2 @@ + - `MEM51-CPP` - `ProperlyDeallocateDynamicallyAllocatedResources.ql`: + - Refactored query logic into a shared library (`ProperlyDeallocateDynamicallyAllocatedResourcesShared.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. diff --git a/cpp/cert/src/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.ql b/cpp/cert/src/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.ql index 70fd363c64..7d1be1e714 100644 --- a/cpp/cert/src/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.ql +++ b/cpp/cert/src/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.ql @@ -19,20 +19,14 @@ import cpp import codingstandards.cpp.cert -import codingstandards.cpp.Allocations +import codingstandards.cpp.rules.properlydeallocatedynamicallyallocatedresourcesshared.ProperlyDeallocateDynamicallyAllocatedResourcesShared -predicate matching(string allocKind, string deleteKind) { - allocKind = "new" and deleteKind = "delete" - or - allocKind = "new[]" and deleteKind = "delete[]" - or - allocKind = "malloc" and deleteKind = "free" +module ProperlyDeallocateDynamicallyAllocatedResourcesConfig implements + ProperlyDeallocateDynamicallyAllocatedResourcesSharedConfigSig +{ + Query getQuery() { + result = AllocationsPackage::properlyDeallocateDynamicallyAllocatedResourcesQuery() + } } -from Expr alloc, Expr free, Expr freed, string allocKind, string deleteKind -where - not isExcluded(freed, AllocationsPackage::properlyDeallocateDynamicallyAllocatedResourcesQuery()) and - allocReaches(freed, alloc, allocKind) and - freeExprOrIndirect(free, freed, deleteKind) and - not matching(allocKind, deleteKind) -select free, "Memory allocated with $@ but deleted with " + deleteKind + ".", alloc, allocKind +import ProperlyDeallocateDynamicallyAllocatedResourcesShared diff --git a/cpp/cert/test/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.qlref b/cpp/cert/test/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.qlref deleted file mode 100644 index eb31753964..0000000000 --- a/cpp/cert/test/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.qlref +++ /dev/null @@ -1 +0,0 @@ -rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.ql \ No newline at end of file diff --git a/cpp/cert/test/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.testref b/cpp/cert/test/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.testref new file mode 100644 index 0000000000..f1dc444999 --- /dev/null +++ b/cpp/cert/test/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.testref @@ -0,0 +1 @@ +cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.ql diff --git a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Undefined.qll b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Undefined.qll index 37ae63fa53..0eea33406f 100644 --- a/cpp/common/src/codingstandards/cpp/exclusions/cpp/Undefined.qll +++ b/cpp/common/src/codingstandards/cpp/exclusions/cpp/Undefined.qll @@ -8,7 +8,8 @@ newtype UndefinedQuery = TCriticalUnspecifiedBehaviorQuery() or TUndefinedBehaviorAuditQuery() or TCriticalUnspecifiedBehaviorAuditQuery() or - TPossibleDataRaceBetweenThreadsQuery() + TPossibleDataRaceBetweenThreadsQuery() or + TDeallocationTypeMismatchQuery() predicate isUndefinedQueryMetadata(Query query, string queryId, string ruleId, string category) { query = @@ -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 `deallocationTypeMismatch` query + UndefinedPackage::deallocationTypeMismatchQuery() and + queryId = + // `@id` for the `deallocationTypeMismatch` query + "cpp/misra/deallocation-type-mismatch" and + ruleId = "RULE-4-1-3" and + category = "required" } module UndefinedPackage { @@ -92,4 +102,11 @@ module UndefinedPackage { // `Query` type for `possibleDataRaceBetweenThreads` query TQueryCPP(TUndefinedPackageQuery(TPossibleDataRaceBetweenThreadsQuery())) } + + Query deallocationTypeMismatchQuery() { + //autogenerate `Query` type + result = + // `Query` type for `deallocationTypeMismatch` query + TQueryCPP(TUndefinedPackageQuery(TDeallocationTypeMismatchQuery())) + } } diff --git a/cpp/common/src/codingstandards/cpp/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.qll b/cpp/common/src/codingstandards/cpp/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.qll new file mode 100644 index 0000000000..a55b4262e0 --- /dev/null +++ b/cpp/common/src/codingstandards/cpp/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.qll @@ -0,0 +1,37 @@ +/** + * Provides a configurable module ProperlyDeallocateDynamicallyAllocatedResourcesShared with a + * `problems` predicate for the following issue: + * Deallocation functions should only be called on nullptr or a pointer returned by the + * corresponding allocation function, that hasn't already been deallocated. + */ + +import cpp +import codingstandards.cpp.Customizations +import codingstandards.cpp.Exclusions +import codingstandards.cpp.Allocations + +signature module ProperlyDeallocateDynamicallyAllocatedResourcesSharedConfigSig { + Query getQuery(); +} + +module ProperlyDeallocateDynamicallyAllocatedResourcesShared< + ProperlyDeallocateDynamicallyAllocatedResourcesSharedConfigSig Config> +{ + private predicate matching(string allocKind, string deleteKind) { + allocKind = "new" and deleteKind = "delete" + or + allocKind = "new[]" and deleteKind = "delete[]" + or + allocKind = "malloc" and deleteKind = "free" + } + + query predicate problems(Expr free, string message, Expr alloc, string allocKind) { + exists(Expr freed, string deleteKind | + not isExcluded(freed, Config::getQuery()) and + allocReaches(freed, alloc, allocKind) and + freeExprOrIndirect(free, freed, deleteKind) and + not matching(allocKind, deleteKind) and + message = "Memory allocated with $@ but deleted with " + deleteKind + "." + ) + } +} diff --git a/cpp/cert/test/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.expected b/cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.expected similarity index 100% rename from cpp/cert/test/rules/MEM51-CPP/ProperlyDeallocateDynamicallyAllocatedResources.expected rename to cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.expected diff --git a/cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.ql b/cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.ql new file mode 100644 index 0000000000..24b0a920fd --- /dev/null +++ b/cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.ql @@ -0,0 +1,8 @@ +// GENERATED FILE - DO NOT MODIFY +import codingstandards.cpp.rules.properlydeallocatedynamicallyallocatedresourcesshared.ProperlyDeallocateDynamicallyAllocatedResourcesShared + +module TestFileConfig implements ProperlyDeallocateDynamicallyAllocatedResourcesSharedConfigSig { + Query getQuery() { result instanceof TestQuery } +} + +import ProperlyDeallocateDynamicallyAllocatedResourcesShared diff --git a/cpp/cert/test/rules/MEM51-CPP/test.cpp b/cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/test.cpp similarity index 99% rename from cpp/cert/test/rules/MEM51-CPP/test.cpp rename to cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/test.cpp index a4a6fc355e..f5568bd361 100644 --- a/cpp/cert/test/rules/MEM51-CPP/test.cpp +++ b/cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/test.cpp @@ -33,4 +33,4 @@ void test_malloc() { int *i3 = (int *)malloc(sizeof(int)); free(i3); // COMPLIANT -} \ No newline at end of file +} diff --git a/cpp/misra/src/rules/RULE-4-1-3/DeallocationTypeMismatch.ql b/cpp/misra/src/rules/RULE-4-1-3/DeallocationTypeMismatch.ql new file mode 100644 index 0000000000..7670e0bcec --- /dev/null +++ b/cpp/misra/src/rules/RULE-4-1-3/DeallocationTypeMismatch.ql @@ -0,0 +1,26 @@ +/** + * @id cpp/misra/deallocation-type-mismatch + * @name RULE-4-1-3: Deallocation type mismatch leads to undefined behavior + * @description Using a deallocation function that does not match the allocation function results in + * undefined behavior. + * @kind problem + * @precision medium + * @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.properlydeallocatedynamicallyallocatedresourcesshared.ProperlyDeallocateDynamicallyAllocatedResourcesShared + +module DeallocationTypeMismatchConfig implements + ProperlyDeallocateDynamicallyAllocatedResourcesSharedConfigSig +{ + Query getQuery() { result = UndefinedPackage::deallocationTypeMismatchQuery() } +} + +import ProperlyDeallocateDynamicallyAllocatedResourcesShared diff --git a/cpp/misra/test/rules/RULE-4-1-3/DeallocationTypeMismatch.testref b/cpp/misra/test/rules/RULE-4-1-3/DeallocationTypeMismatch.testref new file mode 100644 index 0000000000..f1dc444999 --- /dev/null +++ b/cpp/misra/test/rules/RULE-4-1-3/DeallocationTypeMismatch.testref @@ -0,0 +1 @@ +cpp/common/test/rules/properlydeallocatedynamicallyallocatedresourcesshared/ProperlyDeallocateDynamicallyAllocatedResourcesShared.ql diff --git a/rule_packages/cpp/Allocations.json b/rule_packages/cpp/Allocations.json index 416cd3b567..7c0aec019a 100644 --- a/rule_packages/cpp/Allocations.json +++ b/rule_packages/cpp/Allocations.json @@ -194,6 +194,7 @@ "name": "Properly deallocate dynamically allocated resources", "precision": "medium", "severity": "error", + "shared_implementation_short_name": "ProperlyDeallocateDynamicallyAllocatedResourcesShared", "short_name": "ProperlyDeallocateDynamicallyAllocatedResources", "tags": [ "correctness", diff --git a/rule_packages/cpp/Undefined.json b/rule_packages/cpp/Undefined.json index bc0b10af3d..5f725f1a80 100644 --- a/rule_packages/cpp/Undefined.json +++ b/rule_packages/cpp/Undefined.json @@ -69,6 +69,19 @@ "concurrency", "scope/system" ] + }, + { + "description": "Using a deallocation function that does not match the allocation function results in undefined behavior.", + "kind": "problem", + "name": "Deallocation type mismatch leads to undefined behavior", + "precision": "medium", + "severity": "error", + "shared_implementation_short_name": "ProperlyDeallocateDynamicallyAllocatedResourcesShared", + "short_name": "DeallocationTypeMismatch", + "tags": [ + "correctness", + "scope/system" + ] } ], "title": "There shall be no occurrence of undefined or critical unspecified behaviour"