@@ -163,6 +163,12 @@ predicate isSourceImpl(DataFlow::Node source, Class state) {
163
163
)
164
164
}
165
165
166
+ /**
167
+ * The `RelevantStateConfig` configuration is used to find the set of
168
+ * states for the `BadConfig` and `GoodConfig`. The flow computed by
169
+ * `RelevantStateConfig` is used to implement the `relevantState` predicate
170
+ * which is used to avoid a cartesian product in `isSinkImpl`.
171
+ */
166
172
module RelevantStateConfig implements DataFlow:: ConfigSig {
167
173
predicate isSource ( DataFlow:: Node source ) { isSourceImpl ( source , _) }
168
174
@@ -204,9 +210,16 @@ predicate isSinkImpl(DataFlow::Node sink, Class state, Type convertedType, boole
204
210
)
205
211
}
206
212
213
+ /**
214
+ * The `BadConfig` configuration tracks flow from an allocation to an
215
+ * incompatible cast.
216
+ *
217
+ * We use `FlowState` to track the type of the source, and compare the
218
+ * flow state to the target of the cast in the `isSink` definition.
219
+ */
207
220
module BadConfig implements DataFlow:: StateConfigSig {
208
221
class FlowState extends Class {
209
- FlowState ( ) { isSourceImpl ( _, this ) }
222
+ FlowState ( ) { relevantState ( _, this ) }
210
223
}
211
224
212
225
predicate isSource ( DataFlow:: Node source , FlowState state ) { isSourceImpl ( source , state ) }
@@ -220,6 +233,45 @@ module BadConfig implements DataFlow::StateConfigSig {
220
233
221
234
module BadFlow = DataFlow:: GlobalWithState< BadConfig > ;
222
235
236
+ /**
237
+ * The `GoodConfig` configuration tracks flow from an allocation to a
238
+ * compatible cast.
239
+ *
240
+ * We use `GoodConfig` to reduce the number of FPs from infeasible paths.
241
+ * For example, consider the following example:
242
+ * ```cpp
243
+ * struct Animal { virtual ~Animal(); };
244
+ *
245
+ * struct Cat : public Animal {
246
+ * Cat();
247
+ * ~Cat();
248
+ * };
249
+ *
250
+ * struct Dog : public Animal {
251
+ * Dog();
252
+ * ~Dog();
253
+ * };
254
+ *
255
+ * void test9(bool b) {
256
+ * Animal* a;
257
+ * if(b) {
258
+ * a = new Cat;
259
+ * } else {
260
+ * a = new Dog;
261
+ * }
262
+ * if(b) {
263
+ * Cat* d = static_cast<Cat*>(a);
264
+ * }
265
+ * }
266
+ * ```
267
+ * Here, `BadConfig` finds a flow from `a = new Dog` to `static_cast<Cat*>(a)`.
268
+ * However, that path is never realized in an actual execution path. So in
269
+ * order to remove this result we exclude results where there exists an
270
+ * allocation of a type that's compatible with `static_cast<Cat*>(a)`.
271
+ *
272
+ * We use `FlowState` to track the type of the source, and compare the
273
+ * flow state to the target of the cast in the `isSink` definition.
274
+ */
223
275
module GoodConfig implements DataFlow:: StateConfigSig {
224
276
class FlowState = BadConfig:: FlowState ;
225
277
0 commit comments