Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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: 1 addition & 1 deletion c/common/src/codingstandards/c/Objects.qll
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import cpp
import codingstandards.c.StorageDuration
import codingstandards.cpp.lifetimes.StorageDuration
import codingstandards.c.IdentifierLinkage
import semmle.code.cpp.valuenumbering.HashCons
import codingstandards.cpp.Clvalues
Expand Down
4 changes: 4 additions & 0 deletions change_notes/2026-01-31-move-iterators-shared-library.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
- `A-23-0-1`, `A-23-0-2`, `CTR-51-CPP`, `CTR-52-CPP`, `CTR-53-CPP`, `CTR-54-CPP`, `CTR-55-CPP`, `STR-52-CPP` - `IteratorImplicitlyConvertedToConstIterator.ql`, `ValidContainerElementAccess.ql`, `UsesValidContainerElementAccess.ql`, `GuaranteeGenericCppLibraryFunctionsDoNotOverflow.ql`, `UseValidIteratorRanges.ql`, `DoNotSubtractIteratorsForDifferentContainers.ql`, `DoNotUseAnAdditiveOperatorOnAnIterator.ql`, `UseValidReferencesForElementsOfString.ql`:
- Iterator access methods `rbegin`, `rend`, `crbegin`, `crend` are now recognized on containers.
- Shared library `Iterators.qll` has been refactored by splitting out container type logic into a separate library and add logic to differentiate types of containers, such as associative, indexed, and strings.
- Shared library `Iterators.qll`, used by many queries, has been moved.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import cpp
import codingstandards.cpp.autosar
import codingstandards.cpp.Iterators
import codingstandards.cpp.standardlibrary.Iterators

/*
* Due to inconsistent typedefs across STL containers the way this is parsed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import cpp
import codingstandards.cpp.cert
import codingstandards.cpp.Iterators
import codingstandards.cpp.standardlibrary.Iterators
import codingstandards.cpp.rules.containeraccesswithoutrangecheck.ContainerAccessWithoutRangeCheck as ContainerAccessWithoutRangeCheck
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.dataflow.TaintTracking
Expand Down
2 changes: 1 addition & 1 deletion cpp/cert/src/rules/CTR53-CPP/UseValidIteratorRanges.ql
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import cpp
import codingstandards.cpp.cert
import codingstandards.cpp.Iterators
import codingstandards.cpp.standardlibrary.Iterators
import semmle.code.cpp.dataflow.DataFlow

predicate startEndArgumentsDoNotPointToTheSameContainer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import cpp
import codingstandards.cpp.cert
import codingstandards.cpp.Iterators
import codingstandards.cpp.standardlibrary.Iterators

/** Models the subtraction operator */
class SubtractionOperator extends Function {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import cpp
import codingstandards.cpp.cert
import codingstandards.cpp.Iterators
import codingstandards.cpp.standardlibrary.Iterators
import semmle.code.cpp.controlflow.Dominance
import semmle.code.cpp.dataflow.DataFlow

Expand Down
79 changes: 0 additions & 79 deletions cpp/common/src/codingstandards/cpp/Iterators.qll
Original file line number Diff line number Diff line change
Expand Up @@ -252,78 +252,6 @@ class AdditiveOperatorFunctionCall extends FunctionCall {
}
}

/**
* Models a collection of STL container classes.
*/
class STLContainer extends Class {
STLContainer() {
getNamespace() instanceof StdNS and
getSimpleName() in [
"vector", "list", "deque", "set", "multiset", "map", "multimap", "stack", "queue",
"priority_queue", "string", "forward_list", "unordered_set", "unordered_multiset",
"unordered_map", "unordered_multimap", "valarray", "string", "basic_string"
]
or
getSimpleName() = "string"
or
getSimpleName() = "basic_string"
}

/**
* Get a call to a named function of this class.
*/
FunctionCall getACallTo(string name) {
exists(Function f |
f = getAMemberFunction() and
f.hasName(name) and
result = f.getACallToThisFunction()
)
}

/**
* Gets all calls to all functions of this class.
*/
FunctionCall getACallToAFunction() {
exists(Function f |
f = getAMemberFunction() and
result = f.getACallToThisFunction()
)
}

FunctionCall getACallToSize() { result = getACallTo("size") }

FunctionCall getANonConstIteratorBeginFunctionCall() { result = getACallTo("begin") }

IteratorSource getAConstIteratorBeginFunctionCall() { result = getACallTo("cbegin") }

IteratorSource getANonConstIteratorEndFunctionCall() { result = getACallTo("end") }

IteratorSource getAConstIteratorEndFunctionCall() { result = getACallTo("cend") }

IteratorSource getANonConstIteratorFunctionCall() {
result = getACallToAFunction() and
result.getTarget().getType() instanceof NonConstIteratorType
}

IteratorSource getAConstIteratorFunctionCall() {
result = getACallToAFunction() and
result.getTarget().getType() instanceof ConstIteratorType
}

IteratorSource getAnIteratorFunctionCall() {
result = getANonConstIteratorFunctionCall() or result = getAConstIteratorFunctionCall()
}

IteratorSource getAnIteratorBeginFunctionCall() {
result = getANonConstIteratorBeginFunctionCall() or
result = getAConstIteratorBeginFunctionCall()
}

IteratorSource getAnIteratorEndFunctionCall() {
result = getANonConstIteratorEndFunctionCall() or result = getAConstIteratorEndFunctionCall()
}
}

/**
* Models the set of iterator sources. Useful for encapsulating dataflow coming
* from a function call producing an iterator.
Expand All @@ -338,13 +266,6 @@ class IteratorSource extends FunctionCall {
Variable getOwner() { result = getQualifier().(VariableAccess).getTarget() }
}

/**
* Models a variable that is a `STLContainer`
*/
class STLContainerVariable extends Variable {
STLContainerVariable() { getType() instanceof STLContainer }
}

/**
* Usually an iterator range consists of two sequential iterator arguments, for
* example, the start and end. However, there are exceptions to this rule so
Expand Down
108 changes: 108 additions & 0 deletions cpp/common/src/codingstandards/cpp/dominance/SuccessorUnless.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import cpp

/**
* The configuration signature for the SuccessorUnless module.
*
* Typically, the `Node` type should be a `ControlFlowNode`, but it can be overridden to enable
* other kinds of graphs.
*/
signature module SuccessorUnlessConfigSig {
/** The state type used to carry context through the CFG exploration. */
class State;

/** The node type used to represent CFG nodes. Overridable. */
class Node {
Node getASuccessor();
}

predicate isStart(State state, Node node);

predicate isUnless(State state, Node node);

predicate isEnd(State state, Node node);
}

/**
* A module that finds successor of a node -- unless there is an intermediate node that satisfies
* a given condition.
*
* Also accepts a state parameter to allow a context to flow through the CFG.
*
* ## Usage
*
* Implement the module `ConfigSig`, with some context type:
*
* ```ql
* module MyConfig implements SuccessorUnless<SomeContext>::ConfigSig {
* predicate isStart(SomeContext state, ControlFlowNode node) {
* node = state.someStartCondition()
* }
*
* predicate isUnless(SomeContext state, ControlFlowNode node) {
* node = state.someUnlessCondition()
* }
*
* predicate isEnd(SomeContext state, ControlFlowNode node) {
* node = state.someEndCondition()
* }
* }
*
* import SuccessorUnless<SomeContext>::Make<MyConfig> as Successor
* ```
*
* ## Rationale
*
* Why does this module exist? While it may be tempting to write:
*
* ```ql
* exists(ControlFlowNode start, ControlFlowNode end) {
* isStart(start) and
* isEnd(end) and
* end = start.getASuccessor*() and
* not exists(ControlFlowNode mid |
* mid = start.getASuccessor+() and
* end = mid.getASuccessor*() and
* isUnless(mid)
* )
* }
* ```
*
* This has an unintuitive trap case in looping CFGs:
*
* ```c
* while (cond) {
* start();
* end();
* unless();
* }
* ```
*
* In the above code, `unless()` is a successor of `start()`, and `end()` is also a successor of
* `unless()` (via the loop back edge). However, there is no path from `start()` to `end()` that
* does not pass through `unless()`.
*
* This module will correctly handle this case. Forward exploration through the graph will stop
* at the `unless()` nodes, such that only paths from `start()` to `end()` that do not pass through
* `unless()` nodes will be found.
*/
module SuccessorUnless<SuccessorUnlessConfigSig Config> {
predicate isSuccessor(Config::State state, Config::Node start, Config::Node end) {
isMid(state, start, end) and
Config::isEnd(state, end)
}

private predicate isMid(Config::State state, Config::Node start, Config::Node mid) {
// TODO: Explore if forward-reverse pruning would be beneficial for performance here.
Config::isStart(state, start) and
(
mid = start
or
exists(Config::Node prevMid |
isMid(state, start, prevMid) and
mid = prevMid.getASuccessor() and
not Config::isUnless(state, mid) and
not Config::isEnd(state, prevMid)
)
)
}
}
26 changes: 26 additions & 0 deletions cpp/common/src/codingstandards/cpp/exclusions/cpp/DeadCode5.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//** THIS FILE IS AUTOGENERATED, DO NOT MODIFY DIRECTLY. **/
import cpp
import RuleMetadata
import codingstandards.cpp.exclusions.RuleMetadata

newtype DeadCode5Query = TUnnecessaryWriteToLocalObjectQuery()

predicate isDeadCode5QueryMetadata(Query query, string queryId, string ruleId, string category) {
query =
// `Query` instance for the `unnecessaryWriteToLocalObject` query
DeadCode5Package::unnecessaryWriteToLocalObjectQuery() and
queryId =
// `@id` for the `unnecessaryWriteToLocalObject` query
"cpp/misra/unnecessary-write-to-local-object" and
ruleId = "RULE-0-1-1" and
category = "advisory"
}

module DeadCode5Package {
Query unnecessaryWriteToLocalObjectQuery() {
//autogenerate `Query` type
result =
// `Query` type for `unnecessaryWriteToLocalObject` query
TQueryCPP(TDeadCode5PackageQuery(TUnnecessaryWriteToLocalObjectQuery()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Conversions
import Conversions2
import DeadCode
import DeadCode3
import DeadCode5
import Declarations
import ExceptionSafety
import Exceptions1
Expand Down Expand Up @@ -82,6 +83,7 @@ newtype TCPPQuery =
TConversions2PackageQuery(Conversions2Query q) or
TDeadCodePackageQuery(DeadCodeQuery q) or
TDeadCode3PackageQuery(DeadCode3Query q) or
TDeadCode5PackageQuery(DeadCode5Query q) or
TDeclarationsPackageQuery(DeclarationsQuery q) or
TExceptionSafetyPackageQuery(ExceptionSafetyQuery q) or
TExceptions1PackageQuery(Exceptions1Query q) or
Expand Down Expand Up @@ -147,6 +149,7 @@ predicate isQueryMetadata(Query query, string queryId, string ruleId, string cat
isConversions2QueryMetadata(query, queryId, ruleId, category) or
isDeadCodeQueryMetadata(query, queryId, ruleId, category) or
isDeadCode3QueryMetadata(query, queryId, ruleId, category) or
isDeadCode5QueryMetadata(query, queryId, ruleId, category) or
isDeclarationsQueryMetadata(query, queryId, ruleId, category) or
isExceptionSafetyQueryMetadata(query, queryId, ruleId, category) or
isExceptions1QueryMetadata(query, queryId, ruleId, category) or
Expand Down
Loading
Loading