@@ -23,29 +23,15 @@ private predicate tempToDestructorSink(DataFlow::Node sink, CallInstruction call
2323 call .getStaticCallTarget ( ) instanceof Destructor
2424}
2525
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-
4026/** Holds if `pun` is the post-update node of the qualifier of `Call`. */
4127private predicate isPostUpdateOfQualifier ( CallInstruction call , DataFlow:: PostUpdateNode pun ) {
4228 call .getThisArgumentOperand ( ) = pun .getPreUpdateNode ( ) .asOperand ( )
4329}
4430
4531/**
4632 * 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.
4935 *
5036 * For the latter case, consider something like:
5137 * ```
@@ -57,23 +43,21 @@ private predicate isPostUpdateOfQualifier(CallInstruction call, DataFlow::PostUp
5743 * destroyed by a call to `std::vector<std::vector<int>>::~vector`,
5844 * and thus the result of `get_2d_vector()[0]` is also an invalid reference.
5945 */
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
7761 )
7862}
7963
@@ -86,12 +70,39 @@ predicate destroyedToBeginSink(DataFlow::Node sink, FunctionCall fc) {
8670}
8771
8872/**
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`.
9076 */
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+ }
93100
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+ }
95106
96107 DataFlow:: FlowFeature getAFeature ( ) {
97108 // By blocking argument-to-parameter flow we ensure that we don't enter a
@@ -108,8 +119,11 @@ module DestroyedToBeginConfig implements DataFlow::ConfigSig {
108119 }
109120}
110121
111- module DestroyedToBeginFlow = DataFlow:: Global < DestroyedToBeginConfig > ;
122+ module Flow = DataFlow:: GlobalWithState < Config > ;
112123
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