Skip to content

Commit 622f69e

Browse files
authored
Merge pull request #16238 from MathiasVP/fix-terator-to-expired-container-fp
2 parents 77d0df4 + 592ca06 commit 622f69e

File tree

3 files changed

+26
-19
lines changed

3 files changed

+26
-19
lines changed

cpp/ql/src/experimental/Security/CWE/CWE-416/IteratorToExpiredContainer.ql

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@
1111
* external/cwe/cwe-664
1212
*/
1313

14-
// IMPORTANT: This query does not currently find anything since it relies on extractor and analysis improvements that hasn't yet been released
1514
import cpp
1615
import semmle.code.cpp.ir.IR
1716
import semmle.code.cpp.dataflow.new.DataFlow
1817
import semmle.code.cpp.models.implementations.StdContainer
1918
import semmle.code.cpp.models.implementations.StdMap
2019
import semmle.code.cpp.models.implementations.Iterator
2120

21+
private predicate tempToDestructorSink(DataFlow::Node sink, CallInstruction call) {
22+
call = sink.asOperand().(ThisArgumentOperand).getCall() and
23+
call.getStaticCallTarget() instanceof Destructor
24+
}
25+
2226
/**
2327
* A configuration to track flow from a temporary variable to the qualifier of
2428
* a destructor call
@@ -28,13 +32,16 @@ module TempToDestructorConfig implements DataFlow::ConfigSig {
2832
source.asInstruction().(VariableAddressInstruction).getIRVariable() instanceof IRTempVariable
2933
}
3034

31-
predicate isSink(DataFlow::Node sink) {
32-
sink.asOperand().(ThisArgumentOperand).getCall().getStaticCallTarget() instanceof Destructor
33-
}
35+
predicate isSink(DataFlow::Node sink) { tempToDestructorSink(sink, _) }
3436
}
3537

3638
module TempToDestructorFlow = DataFlow::Global<TempToDestructorConfig>;
3739

40+
/** Holds if `pun` is the post-update node of the qualifier of `Call`. */
41+
private predicate isPostUpdateOfQualifier(CallInstruction call, DataFlow::PostUpdateNode pun) {
42+
call.getThisArgumentOperand() = pun.getPreUpdateNode().asOperand()
43+
}
44+
3845
/**
3946
* Gets a `DataFlow::Node` that represents a temporary that will be destroyed
4047
* by a call to a destructor, or a `DataFlow::Node` that will transitively be
@@ -51,21 +58,26 @@ module TempToDestructorFlow = DataFlow::Global<TempToDestructorConfig>;
5158
* and thus the result of `get_2d_vector()[0]` is also an invalid reference.
5259
*/
5360
DataFlow::Node getADestroyedNode() {
54-
exists(TempToDestructorFlow::PathNode destroyedTemp | destroyedTemp.isSource() |
55-
result = destroyedTemp.getNode()
61+
exists(DataFlow::Node n | TempToDestructorFlow::flowTo(n) |
62+
// Case 1: The pointer that goes into the destructor call is destroyed
63+
exists(CallInstruction destructorCall |
64+
tempToDestructorSink(n, destructorCall) and
65+
isPostUpdateOfQualifier(destructorCall, result)
66+
)
5667
or
68+
// Case 2: Anything that was derived from the temporary that is now destroyed
69+
// is also destroyed.
5770
exists(CallInstruction call |
5871
result.asInstruction() = call and
59-
DataFlow::localFlow(destroyedTemp.getNode(),
60-
DataFlow::operandNode(call.getThisArgumentOperand()))
72+
DataFlow::localFlow(DataFlow::operandNode(call.getThisArgumentOperand()), n)
6173
|
6274
call.getStaticCallTarget() instanceof StdSequenceContainerAt or
6375
call.getStaticCallTarget() instanceof StdMapAt
6476
)
6577
)
6678
}
6779

68-
predicate isSinkImpl(DataFlow::Node sink, FunctionCall fc) {
80+
predicate destroyedToBeginSink(DataFlow::Node sink, FunctionCall fc) {
6981
exists(CallInstruction call |
7082
call = sink.asOperand().(ThisArgumentOperand).getCall() and
7183
fc = call.getUnconvertedResultExpression() and
@@ -79,7 +91,7 @@ predicate isSinkImpl(DataFlow::Node sink, FunctionCall fc) {
7991
module DestroyedToBeginConfig implements DataFlow::ConfigSig {
8092
predicate isSource(DataFlow::Node source) { source = getADestroyedNode() }
8193

82-
predicate isSink(DataFlow::Node sink) { isSinkImpl(sink, _) }
94+
predicate isSink(DataFlow::Node sink) { destroyedToBeginSink(sink, _) }
8395

8496
DataFlow::FlowFeature getAFeature() {
8597
// By blocking argument-to-parameter flow we ensure that we don't enter a
@@ -99,5 +111,5 @@ module DestroyedToBeginConfig implements DataFlow::ConfigSig {
99111
module DestroyedToBeginFlow = DataFlow::Global<DestroyedToBeginConfig>;
100112

101113
from DataFlow::Node source, DataFlow::Node sink, FunctionCall beginOrEnd
102-
where DestroyedToBeginFlow::flow(source, sink) and isSinkImpl(sink, beginOrEnd)
114+
where DestroyedToBeginFlow::flow(source, sink) and destroyedToBeginSink(sink, beginOrEnd)
103115
select source, "This object is destroyed before $@ is called.", beginOrEnd, beginOrEnd.toString()

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/IteratorToExpiredContainer.expected

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,10 @@
22
| test.cpp:680:30:680:30 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:680:17:680:17 | call to end | call to end |
33
| test.cpp:683:31:683:32 | call to at | This object is destroyed before $@ is called. | test.cpp:683:17:683:17 | call to begin | call to begin |
44
| test.cpp:683:31:683:32 | call to at | This object is destroyed before $@ is called. | test.cpp:683:17:683:17 | call to end | call to end |
5-
| test.cpp:689:17:689:29 | temporary object | This object is destroyed before $@ is called. | test.cpp:689:31:689:35 | call to begin | call to begin |
6-
| test.cpp:689:46:689:58 | temporary object | This object is destroyed before $@ is called. | test.cpp:689:60:689:62 | call to end | call to end |
5+
| test.cpp:689:46:689:58 | pointer to ~vector output argument | This object is destroyed before $@ is called. | test.cpp:689:60:689:62 | call to end | call to end |
76
| test.cpp:702:27:702:27 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:703:19:703:23 | call to begin | call to begin |
87
| test.cpp:702:27:702:27 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:703:36:703:38 | call to end | call to end |
9-
| test.cpp:716:36:716:48 | temporary object | This object is destroyed before $@ is called. | test.cpp:716:17:716:17 | call to begin | call to begin |
10-
| test.cpp:716:36:716:48 | temporary object | This object is destroyed before $@ is called. | test.cpp:716:17:716:17 | call to end | call to end |
118
| test.cpp:727:23:727:23 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:750:17:750:17 | call to begin | call to begin |
129
| test.cpp:727:23:727:23 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:750:17:750:17 | call to end | call to end |
1310
| test.cpp:735:23:735:23 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:759:17:759:17 | call to begin | call to begin |
1411
| test.cpp:735:23:735:23 | call to operator[] | This object is destroyed before $@ is called. | test.cpp:759:17:759:17 | call to end | call to end |
15-
| test.cpp:771:44:771:56 | temporary object | This object is destroyed before $@ is called. | test.cpp:772:35:772:35 | call to begin | call to begin |
16-
| test.cpp:771:44:771:56 | temporary object | This object is destroyed before $@ is called. | test.cpp:772:35:772:35 | call to end | call to end |

cpp/ql/test/experimental/query-tests/Security/CWE/CWE-416/test.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ void test() {
713713
for(auto it = v.begin(); it != v.end(); ++it) {} // GOOD
714714
}
715715

716-
for (auto x : return_self_by_ref(returnValue())) {} // BAD
716+
for (auto x : return_self_by_ref(returnValue())) {} // BAD [NOT DETECTED]
717717

718718
for (auto x : return_self_by_value(returnValue())) {} // GOOD
719719
}
@@ -768,6 +768,6 @@ void test2() {
768768
}
769769

770770
void test3() {
771-
const std::vector<std::vector<int>>& v = returnValue(); // GOOD [FALSE POSITIVE]
771+
const std::vector<std::vector<int>>& v = returnValue(); // GOOD
772772
for(const std::vector<int>& x : v) {}
773773
}

0 commit comments

Comments
 (0)