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/2024-03-22-fix-fp-ctr55-cpp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- `CTR55-CPP` - `DoNotUseAnAdditiveOperatorOnAnIterator.ql`:
- Address reported FP in #374. Improve logic on valid end checks on iterators.
Original file line number Diff line number Diff line change
Expand Up @@ -80,16 +80,7 @@ where
iteratorCreationCall = outputContainer.getAnIteratorFunctionCall() and
iteratorCreationCall = c.getOutputIteratorSource()
|
// Guarded by a bounds check that ensures our destination is larger than "some" value
exists(
GuardCondition guard, ContainerAccessWithoutRangeCheck::ContainerSizeCall sizeCall,
boolean branch
|
globalValueNumber(sizeCall.getQualifier()) =
globalValueNumber(iteratorCreationCall.getQualifier()) and
guard.controls(c.getBasicBlock(), branch) and
relOpWithSwapAndNegate(guard, sizeCall, _, Greater(), _, branch)
)
size_compare_bounds_checked(iteratorCreationCall, c)
Comment thread
knewbury01 marked this conversation as resolved.
Outdated
or
// Container created with sufficient size for the input
exists(ContainerAccessWithoutRangeCheck::ContainerConstructorCall outputIteratorConstructor |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,44 @@ import cpp
import codingstandards.cpp.cert
import codingstandards.cpp.Iterators

from ContainerIteratorAccess it
/**
* any `.size()` check above our access
*/
Comment thread
knewbury01 marked this conversation as resolved.
predicate size_checked_above(ContainerIteratorAccess it, IteratorSource source) {
Comment thread
knewbury01 marked this conversation as resolved.
Outdated
exists(STLContainer c, FunctionCall guardCall |
Comment thread
knewbury01 marked this conversation as resolved.
Outdated
c.getACallToSize() = guardCall and
guardCall = it.getAPredecessor*() and
Comment thread
knewbury01 marked this conversation as resolved.
Outdated
//make sure its the same container providing its size as giving the iterator
globalValueNumber(guardCall.getQualifier()) = globalValueNumber(source.getQualifier()) and
// and the size call we match must be after the assignment call
source.getASuccessor*() = guardCall
)
}

/**
* some loop check exists like: `iterator != end`
* where a relevant`.end()` call flowed into end
*/
predicate valid_end_bound_check(ContainerIteratorAccess it, IteratorSource source) {
Comment thread
knewbury01 marked this conversation as resolved.
Outdated
exists(STLContainer c, Loop l, ContainerIteratorAccess otherAccess, IteratorSource end |
end = c.getAnIteratorEndFunctionCall() and
Comment thread
knewbury01 marked this conversation as resolved.
Outdated
//flow exists between end() and the loop condition
DataFlow::localFlow(DataFlow::exprNode(end), DataFlow::exprNode(l.getCondition().getAChild())) and
l.getCondition().getAChild() = otherAccess and
Comment thread
knewbury01 marked this conversation as resolved.
Outdated
//make sure its the same iterator being checked as incremented
otherAccess.getOwningContainer() = it.getOwningContainer() and
Comment thread
rvermeulen marked this conversation as resolved.
Outdated
//make sure its the same container providing its end as giving the iterator
globalValueNumber(end.getQualifier()) = globalValueNumber(source.getQualifier())
)
}

from ContainerIteratorAccess it, IteratorSource source
where
not isExcluded(it, IteratorsPackage::doNotUseAnAdditiveOperatorOnAnIteratorQuery()) and
it.isAdditiveOperation() and
not exists(RangeBasedForStmt fs | fs.getUpdate().getAChild*() = it) and
// we get the neraby assignment
not exists(STLContainer c, FunctionCall nearbyAssigningIteratorCall, FunctionCall guardCall |
nearbyAssigningIteratorCall = it.getANearbyAssigningIteratorCall() and
// we look for calls to size or end
(guardCall = c.getACallToSize() or guardCall = c.getAnIteratorEndFunctionCall()) and
// such that the call to size is before this
// access
guardCall = it.getAPredecessor*() and
// and it uses the same qualifier as the one we were just assigned
nearbyAssigningIteratorCall.getQualifier().(VariableAccess).getTarget() =
guardCall.getQualifier().(VariableAccess).getTarget() and
// and the size call we match must be after the assignment call
nearbyAssigningIteratorCall.getASuccessor*() = guardCall
)
source = it.getANearbyAssigningIteratorCall() and
not size_compare_bounds_checked(source, it) and
not valid_end_bound_check(it, source) and
not size_checked_above(it, source)
select it, "Increment of iterator may overflow since its bounds are not checked."
15 changes: 15 additions & 0 deletions cpp/cert/test/rules/CTR55-CPP/test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,19 @@ void f1(std::vector<int> &v) {

for (auto i = v.begin();; ++i) { // NON_COMPLIANT
}
}

void test_fp_reported_in_374(std::vector<int> &v) {
{
auto end = v.end();
for (auto i = v.begin(); i != end; ++i) { // COMPLIANT
}
}

{
auto end2 = v.end();
end2++; // NON_COMPLIANT[FALSE_NEGATIVE]
for (auto i = v.begin(); i != end2; ++i) { // NON_COMPLIANT[FALSE_NEGATIVE]
Comment thread
rvermeulen marked this conversation as resolved.
Outdated
}
}
}
27 changes: 24 additions & 3 deletions cpp/common/src/codingstandards/cpp/Iterators.qll
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import cpp
import codingstandards.cpp.dataflow.DataFlow
import codingstandards.cpp.dataflow.TaintTracking
import codingstandards.cpp.StdNamespace
import codingstandards.cpp.rules.containeraccesswithoutrangecheck.ContainerAccessWithoutRangeCheck as ContainerAccessWithoutRangeCheck
import semmle.code.cpp.controlflow.Guards
import semmle.code.cpp.valuenumbering.GlobalValueNumbering
import semmle.code.cpp.rangeanalysis.RangeAnalysisUtils

abstract class ContainerAccess extends VariableAccess {
abstract Variable getOwningContainer();
Expand Down Expand Up @@ -63,9 +67,11 @@ class ContainerIteratorAccess extends ContainerAccess {
)
}

// get a function call to cbegin/begin that
// assigns its value to the iterator represented by this
// access
/**
* gets a function call to cbegin/begin that
* assigns its value to the iterator represented by this
* access
*/
FunctionCall getANearbyAssigningIteratorCall() {
// the underlying container for this variable is one wherein
// there is an assigned value of cbegin/cend
Expand Down Expand Up @@ -462,3 +468,18 @@ ControlFlowNode getANonInvalidatedSuccessor(ContainerInvalidationOperation op) {
not result instanceof ContainerInvalidationOperation
)
}

/**
* Guarded by a bounds check that ensures our destination is larger than "some" value
*/
predicate size_compare_bounds_checked(IteratorSource iteratorCreationCall, Expr guarded) {
exists(
GuardCondition guard, ContainerAccessWithoutRangeCheck::ContainerSizeCall sizeCall,
boolean branch
|
globalValueNumber(sizeCall.getQualifier()) =
globalValueNumber(iteratorCreationCall.getQualifier()) and
guard.controls(guarded.getBasicBlock(), branch) and
relOpWithSwapAndNegate(guard, sizeCall, _, Greater(), _, branch)
)
}