@@ -78,6 +78,113 @@ predicate isSinkPairImpl(
7878 )
7979}
8080
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+
81188module StringSizeConfig implements ProductFlow:: StateConfigSig {
82189 class FlowState1 = Unit ;
83190
@@ -100,7 +207,7 @@ module StringSizeConfig implements ProductFlow::StateConfigSig {
100207 DataFlow:: Node bufSink , FlowState1 state1 , DataFlow:: Node sizeSink , FlowState2 state2
101208 ) {
102209 exists ( state1 ) and
103- state2 = [ - 32 .. 32 ] and // An arbitrary bound because we need to bound `state2`
210+ validState ( sizeSink , state2 ) and
104211 exists ( int delta |
105212 isSinkPairImpl ( _, bufSink , sizeSink , delta , _) and
106213 delta > state2
@@ -120,8 +227,8 @@ module StringSizeConfig implements ProductFlow::StateConfigSig {
120227 predicate isAdditionalFlowStep2 (
121228 DataFlow:: Node node1 , FlowState2 state1 , DataFlow:: Node node2 , FlowState2 state2
122229 ) {
230+ validState ( node2 , state2 ) and
123231 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`
125232 state1 = s1 and
126233 state2 = s2 and
127234 add .hasOperands ( node1 .asOperand ( ) , op ) and
0 commit comments