@@ -78,6 +78,113 @@ predicate isSinkPairImpl(
78
78
)
79
79
}
80
80
81
+ module ValidState {
82
+ /**
83
+ * In the `StringSizeConfig` configuration we use an integer as the flow state for the second
84
+ * projection of the dataflow graph. The integer represents an offset that is added to the
85
+ * size of the allocation. For example, given:
86
+ * ```cpp
87
+ * char* p = new char[size + 1];
88
+ * size += 1;
89
+ * memset(p, 0, size);
90
+ * ```
91
+ * the initial flow state is `1`. This represents the fact that `size + 1` is a valid bound
92
+ * for the size of the allocation pointed to by `p`. After updating the size using `+=`, the
93
+ * flow state changes to `0`, which represents the fact that `size + 0` is a valid bound for
94
+ * the allocation.
95
+ *
96
+ * So we need to compute a set of valid integers that represent the offset applied to the
97
+ * size. We do this in two steps:
98
+ * 1. We first perform the dataflow traversal that the second projection of the product-flow
99
+ * library will perform, and visit all the places where the size argument is modified.
100
+ * 2. Once that dataflow traversal is done, we accumulate the offsets added at each places
101
+ * where the offset is modified (see `validStateImpl`).
102
+ *
103
+ * Because we want to guarantee that each place where we modify the offset has a `PathNode`
104
+ * we "flip" a boolean flow state in each `isAdditionalFlowStep`. This ensures that the node
105
+ * has a corresponding `PathNode`.
106
+ */
107
+ private module ValidStateConfig implements DataFlow:: StateConfigSig {
108
+ class FlowState = boolean ;
109
+
110
+ predicate isSource ( DataFlow:: Node source , FlowState state ) {
111
+ hasSize ( _, source , _) and
112
+ state = false
113
+ }
114
+
115
+ predicate isSink ( DataFlow:: Node sink , FlowState state ) {
116
+ isSinkPairImpl ( _, _, sink , _, _) and
117
+ state = [ false , true ]
118
+ }
119
+
120
+ predicate isBarrier ( DataFlow:: Node node , FlowState state ) { none ( ) }
121
+
122
+ predicate isAdditionalFlowStep (
123
+ DataFlow:: Node node1 , FlowState state1 , DataFlow:: Node node2 , FlowState state2
124
+ ) {
125
+ exists ( AddInstruction add , Operand op , int delta |
126
+ add .hasOperands ( node1 .asOperand ( ) , op ) and
127
+ semBounded ( getSemanticExpr ( op .getDef ( ) ) , any ( SemZeroBound zero ) , delta , true , _) and
128
+ node2 .asInstruction ( ) = add and
129
+ state1 = [ false , true ] and
130
+ state2 = state1 .booleanNot ( )
131
+ )
132
+ }
133
+
134
+ predicate includeHiddenNodes ( ) { any ( ) }
135
+ }
136
+
137
+ private import DataFlow:: GlobalWithState< ValidStateConfig >
138
+
139
+ private predicate inLoop ( PathNode n ) { n .getASuccessor + ( ) = n }
140
+
141
+ /**
142
+ * Holds if `value` is a possible offset for `n`.
143
+ *
144
+ * To ensure termination, we limit `value` to be in the
145
+ * range `[-2, 2]` if the node is part of a loop. Without
146
+ * this restriction we wouldn't terminate on an example like:
147
+ * ```cpp
148
+ * while(unknown()) { size++; }
149
+ * ```
150
+ */
151
+ private predicate validStateImpl ( PathNode n , int value ) {
152
+ ( inLoop ( n ) implies value = [ - 2 .. 2 ] ) and
153
+ (
154
+ hasSize ( _, n .getNode ( ) , value )
155
+ or
156
+ exists ( int delta , PathNode n0 |
157
+ n0 .getASuccessor ( ) = n and
158
+ validStateImpl ( n0 , value ) and
159
+ isSinkPairImpl ( _, _, n .getNode ( ) , delta , _) and
160
+ delta > value
161
+ )
162
+ or
163
+ exists ( PathNode n0 , DataFlow:: Node node , int value0 |
164
+ n0 .getASuccessor ( ) = n and
165
+ validStateImpl ( n0 , value0 ) and
166
+ node = n .getNode ( )
167
+ |
168
+ exists ( AddInstruction add , Operand op1 , Operand op2 , int delta |
169
+ add = node .asInstruction ( ) and
170
+ add .hasOperands ( op1 , op2 ) and
171
+ value0 = value + delta and
172
+ semBounded ( getSemanticExpr ( [ op1 , op2 ] .getDef ( ) ) , any ( SemZeroBound zero ) , delta , true , _)
173
+ )
174
+ or
175
+ not node .asInstruction ( ) instanceof AddInstruction and
176
+ value = value0
177
+ )
178
+ )
179
+ }
180
+
181
+ predicate validState ( DataFlow:: Node n , int value ) {
182
+ validStateImpl ( any ( PathNode pn | pn .getNode ( ) = n ) , value )
183
+ }
184
+ }
185
+
186
+ import ValidState
187
+
81
188
module StringSizeConfig implements ProductFlow:: StateConfigSig {
82
189
class FlowState1 = Unit ;
83
190
@@ -100,7 +207,7 @@ module StringSizeConfig implements ProductFlow::StateConfigSig {
100
207
DataFlow:: Node bufSink , FlowState1 state1 , DataFlow:: Node sizeSink , FlowState2 state2
101
208
) {
102
209
exists ( state1 ) and
103
- state2 = [ - 32 .. 32 ] and // An arbitrary bound because we need to bound `state2`
210
+ validState ( sizeSink , state2 ) and
104
211
exists ( int delta |
105
212
isSinkPairImpl ( _, bufSink , sizeSink , delta , _) and
106
213
delta > state2
@@ -120,8 +227,8 @@ module StringSizeConfig implements ProductFlow::StateConfigSig {
120
227
predicate isAdditionalFlowStep2 (
121
228
DataFlow:: Node node1 , FlowState2 state1 , DataFlow:: Node node2 , FlowState2 state2
122
229
) {
230
+ validState ( node2 , state2 ) and
123
231
exists ( AddInstruction add , Operand op , int delta , int s1 , int s2 |
124
- s1 = [ - 32 .. 32 ] and // An arbitrary bound because we need to bound `state`
125
232
state1 = s1 and
126
233
state2 = s2 and
127
234
add .hasOperands ( node1 .asOperand ( ) , op ) and
0 commit comments