Skip to content

Commit bf97331

Browse files
committed
CTR55-CPP: model precise end calculation checks and switch to use of size bounds check only
1 parent e948e39 commit bf97331

File tree

5 files changed

+47
-18
lines changed

5 files changed

+47
-18
lines changed
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
- `CTR55-CPP` - `DoNotUseAnAdditiveOperatorOnAnIterator.ql`:
2-
- Address reported FP in #374. Improve logic on valid end checks on iterators.
2+
- Address reported FP in #374. Improve logic on valid end checks and size checks on iterators.

cpp/cert/src/rules/CTR55-CPP/DoNotUseAnAdditiveOperatorOnAnIterator.ql

+30-14
Original file line numberDiff line numberDiff line change
@@ -17,37 +17,53 @@ import codingstandards.cpp.Iterators
1717
import semmle.code.cpp.controlflow.Dominance
1818

1919
/**
20-
* any `.size()` check above our access
20+
* something like:
21+
* `end = begin() + size()`
2122
*/
22-
predicate sizeCheckedAbove(ContainerIteratorAccess it, IteratorSource source) {
23-
exists(ContainerAccessWithoutRangeCheck::ContainerSizeCall guardCall |
24-
strictlyDominates(guardCall, it) and
25-
//make sure its the same container providing its size as giving the iterator
26-
globalValueNumber(guardCall.getQualifier()) = globalValueNumber(source.getQualifier()) and
27-
// and the size call we match must be after the assignment call
28-
source.getASuccessor*() = guardCall
23+
Expr calculatedEndCheck(AdditiveOperatorFunctionCall calc) {
24+
exists(
25+
ContainerAccessWithoutRangeCheck::ContainerSizeCall size,
26+
ContainerAccessWithoutRangeCheck::ContainerBeginCall begin
27+
|
28+
calc.getTarget().hasName("operator+") and
29+
DataFlow::localFlow(DataFlow::exprNode(size), DataFlow::exprNode(calc.getAChild*())) and
30+
DataFlow::localFlow(DataFlow::exprNode(begin), DataFlow::exprNode(calc.getAChild*())) and
31+
//make sure its the same container providing its size as giving the begin
32+
globalValueNumber(begin.getQualifier()) = globalValueNumber(size.getQualifier()) and
33+
result = begin.getQualifier()
2934
)
3035
}
3136

37+
Expr validEndCheck(FunctionCall end) {
38+
end instanceof ContainerAccessWithoutRangeCheck::ContainerEndCall and
39+
result = end.getQualifier()
40+
or
41+
result = calculatedEndCheck(end)
42+
}
43+
3244
/**
3345
* some guard exists like: `iterator != end`
3446
* where a relevant`.end()` call flowed into end
3547
*/
3648
predicate validEndBoundCheck(ContainerIteratorAccess it, IteratorSource source) {
3749
exists(
38-
ContainerAccessWithoutRangeCheck::ContainerEndCall end, BasicBlock b, GuardCondition l,
39-
ContainerIteratorAccess otherAccess
50+
FunctionCall end, BasicBlock b, GuardCondition l, ContainerIteratorAccess otherAccess,
51+
Expr qualifierToCheck
4052
|
53+
//sufficient end guard
54+
qualifierToCheck = validEndCheck(end) and
4155
//guard controls the access
4256
l.controls(b, _) and
4357
b.contains(it) and
44-
//guard is comprised of (anything flowing to) end check and an iterator access
58+
//guard is comprised of end check and an iterator access
4559
DataFlow::localFlow(DataFlow::exprNode(end), DataFlow::exprNode(l.getChild(_))) and
4660
l.getChild(_) = otherAccess and
4761
//make sure its the same iterator being checked in the guard as accessed
4862
otherAccess.getOwningContainer() = it.getOwningContainer() and
49-
//make sure its the same container providing its end as giving the iterator
50-
globalValueNumber(end.getQualifier()) = globalValueNumber(source.getQualifier())
63+
//if its the end call itself (or its parts), make sure its the same container providing its end as giving the iterator
64+
globalValueNumber(qualifierToCheck) = globalValueNumber(source.getQualifier()) and
65+
// and the guard call we match must be after the assignment call (to avoid valid guards protecting new iterator accesses further down)
66+
source.getASuccessor*() = l
5167
)
5268
}
5369

@@ -58,5 +74,5 @@ where
5874
not exists(RangeBasedForStmt fs | fs.getUpdate().getAChild*() = it) and
5975
source = it.getANearbyAssigningIteratorCall() and
6076
not validEndBoundCheck(it, source) and
61-
not sizeCheckedAbove(it, source)
77+
not sizeCompareBoundsChecked(source, it)
6278
select it, "Increment of iterator may overflow since its bounds are not checked."
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
| test.cpp:8:7:8:7 | i | Increment of iterator may overflow since its bounds are not checked. |
22
| test.cpp:9:9:9:9 | i | Increment of iterator may overflow since its bounds are not checked. |
33
| test.cpp:10:9:10:9 | i | Increment of iterator may overflow since its bounds are not checked. |
4+
| test.cpp:22:18:22:18 | i | Increment of iterator may overflow since its bounds are not checked. |
45
| test.cpp:27:31:27:31 | i | Increment of iterator may overflow since its bounds are not checked. |
6+
| test.cpp:40:5:40:8 | end2 | Increment of iterator may overflow since its bounds are not checked. |

cpp/cert/test/rules/CTR55-CPP/test.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ void f1(std::vector<int> &v) {
2020
}
2121
for (auto i = v.begin(),
2222
l = (i + std::min(static_cast<std::vector<int>::size_type>(10),
23-
v.size()));
24-
i != l; ++i) { // COMPLIANT
23+
v.size())); // NON_COMPLIANT - technically in the
24+
// calculation
25+
i != l; ++i) { // COMPLIANT
2526
}
2627

2728
for (auto i = v.begin();; ++i) { // NON_COMPLIANT
@@ -37,7 +38,7 @@ void test_fp_reported_in_374(std::vector<int> &v) {
3738

3839
{
3940
auto end2 = v.end();
40-
end2++; // NON_COMPLIANT[FALSE_NEGATIVE]
41+
end2++; // NON_COMPLIANT
4142
for (auto i = v.begin(); i != end2; ++i) { // NON_COMPLIANT[FALSE_NEGATIVE]
4243
}
4344
}

cpp/common/src/codingstandards/cpp/rules/containeraccesswithoutrangecheck/ContainerAccessWithoutRangeCheck.qll

+10
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ class ContainerEmptyCall extends FunctionCall {
8585
}
8686
}
8787

88+
/**
89+
* A call to either `begin()` on a container.
90+
*/
91+
class ContainerBeginCall extends FunctionCall {
92+
ContainerBeginCall() {
93+
getTarget().getDeclaringType() instanceof ContainerType and
94+
getTarget().getName() = "begin"
95+
}
96+
}
97+
8898
/**
8999
* A call to either `end()` on a container.
90100
*/

0 commit comments

Comments
 (0)