@@ -23,29 +23,15 @@ private predicate tempToDestructorSink(DataFlow::Node sink, CallInstruction call
23
23
call .getStaticCallTarget ( ) instanceof Destructor
24
24
}
25
25
26
- /**
27
- * A configuration to track flow from a temporary variable to the qualifier of
28
- * a destructor call
29
- */
30
- module TempToDestructorConfig implements DataFlow:: ConfigSig {
31
- predicate isSource ( DataFlow:: Node source ) {
32
- source .asInstruction ( ) .( VariableAddressInstruction ) .getIRVariable ( ) instanceof IRTempVariable
33
- }
34
-
35
- predicate isSink ( DataFlow:: Node sink ) { tempToDestructorSink ( sink , _) }
36
- }
37
-
38
- module TempToDestructorFlow = DataFlow:: Global< TempToDestructorConfig > ;
39
-
40
26
/** Holds if `pun` is the post-update node of the qualifier of `Call`. */
41
27
private predicate isPostUpdateOfQualifier ( CallInstruction call , DataFlow:: PostUpdateNode pun ) {
42
28
call .getThisArgumentOperand ( ) = pun .getPreUpdateNode ( ) .asOperand ( )
43
29
}
44
30
45
31
/**
46
32
* Gets a `DataFlow::Node` that represents a temporary that will be destroyed
47
- * by a call to a destructor, or a `DataFlow::Node` that will transitively be
48
- * destroyed by a call to a destructor.
33
+ * by a call to a destructor when `n` is destroyed , or a `DataFlow::Node` that
34
+ * will transitively be destroyed by a call to a destructor.
49
35
*
50
36
* For the latter case, consider something like:
51
37
* ```
@@ -57,23 +43,21 @@ private predicate isPostUpdateOfQualifier(CallInstruction call, DataFlow::PostUp
57
43
* destroyed by a call to `std::vector<std::vector<int>>::~vector`,
58
44
* and thus the result of `get_2d_vector()[0]` is also an invalid reference.
59
45
*/
60
- DataFlow:: Node getADestroyedNode ( ) {
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
- )
67
- or
68
- // Case 2: Anything that was derived from the temporary that is now destroyed
69
- // is also destroyed.
70
- exists ( CallInstruction call |
71
- result .asInstruction ( ) = call and
72
- DataFlow:: localFlow ( DataFlow:: operandNode ( call .getThisArgumentOperand ( ) ) , n )
73
- |
74
- call .getStaticCallTarget ( ) instanceof StdSequenceContainerAt or
75
- call .getStaticCallTarget ( ) instanceof StdMapAt
76
- )
46
+ DataFlow:: Node getADestroyedNode ( DataFlow:: Node n ) {
47
+ // Case 1: The pointer that goes into the destructor call is destroyed
48
+ exists ( CallInstruction destructorCall |
49
+ tempToDestructorSink ( n , destructorCall ) and
50
+ isPostUpdateOfQualifier ( destructorCall , result )
51
+ )
52
+ or
53
+ // Case 2: Anything that was derived from the temporary that is now destroyed
54
+ // is also destroyed.
55
+ exists ( CallInstruction call |
56
+ result .asInstruction ( ) = call and
57
+ DataFlow:: localFlow ( DataFlow:: operandNode ( call .getThisArgumentOperand ( ) ) , n )
58
+ |
59
+ call .getStaticCallTarget ( ) instanceof StdSequenceContainerAt or
60
+ call .getStaticCallTarget ( ) instanceof StdMapAt
77
61
)
78
62
}
79
63
@@ -86,12 +70,39 @@ predicate destroyedToBeginSink(DataFlow::Node sink, FunctionCall fc) {
86
70
}
87
71
88
72
/**
89
- * Flow from any destroyed object to the qualifier of a `begin` or `end` call
73
+ * A configuration to track flow from a temporary variable to the qualifier of
74
+ * a destructor call, and subsequently to a qualifier of a call to `begin` or
75
+ * `end`.
90
76
*/
91
- module DestroyedToBeginConfig implements DataFlow:: ConfigSig {
92
- predicate isSource ( DataFlow:: Node source ) { source = getADestroyedNode ( ) }
77
+ module Config implements DataFlow:: StateConfigSig {
78
+ newtype FlowState =
79
+ additional TempToDestructor ( ) or
80
+ additional DestroyedToBegin ( DataFlow:: Node n ) {
81
+ exists ( DataFlow:: Node thisOperand |
82
+ tempToDestructorSink ( thisOperand , _) and
83
+ n = getADestroyedNode ( thisOperand )
84
+ )
85
+ }
86
+
87
+ predicate isSource ( DataFlow:: Node source , FlowState state ) {
88
+ source .asInstruction ( ) .( VariableAddressInstruction ) .getIRVariable ( ) instanceof IRTempVariable and
89
+ state = TempToDestructor ( )
90
+ }
91
+
92
+ predicate isAdditionalFlowStep (
93
+ DataFlow:: Node node1 , FlowState state1 , DataFlow:: Node node2 , FlowState state2
94
+ ) {
95
+ tempToDestructorSink ( node1 , _) and
96
+ state1 = TempToDestructor ( ) and
97
+ state2 = DestroyedToBegin ( node2 ) and
98
+ node2 = getADestroyedNode ( node1 )
99
+ }
93
100
94
- predicate isSink ( DataFlow:: Node sink ) { destroyedToBeginSink ( sink , _) }
101
+ predicate isSink ( DataFlow:: Node sink , FlowState state ) {
102
+ // Note: This is a non-trivial cartesian product!
103
+ // Hopefully, both of these sets are quite small in practice
104
+ destroyedToBeginSink ( sink , _) and state instanceof DestroyedToBegin
105
+ }
95
106
96
107
DataFlow:: FlowFeature getAFeature ( ) {
97
108
// By blocking argument-to-parameter flow we ensure that we don't enter a
@@ -108,8 +119,11 @@ module DestroyedToBeginConfig implements DataFlow::ConfigSig {
108
119
}
109
120
}
110
121
111
- module DestroyedToBeginFlow = DataFlow:: Global < DestroyedToBeginConfig > ;
122
+ module Flow = DataFlow:: GlobalWithState < Config > ;
112
123
113
- from DataFlow:: Node source , DataFlow:: Node sink , FunctionCall beginOrEnd
114
- where DestroyedToBeginFlow:: flow ( source , sink ) and destroyedToBeginSink ( sink , beginOrEnd )
115
- select source , "This object is destroyed before $@ is called." , beginOrEnd , beginOrEnd .toString ( )
124
+ from Flow:: PathNode source , Flow:: PathNode sink , FunctionCall beginOrEnd , DataFlow:: Node mid
125
+ where
126
+ Flow:: flowPath ( source , sink ) and
127
+ destroyedToBeginSink ( sink .getNode ( ) , beginOrEnd ) and
128
+ sink .getState ( ) = Config:: DestroyedToBegin ( mid )
129
+ select mid , "This object is destroyed before $@ is called." , beginOrEnd , beginOrEnd .toString ( )
0 commit comments