@@ -163,6 +163,12 @@ predicate isSourceImpl(DataFlow::Node source, Class state) {
163163 )
164164}
165165
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+ */
166172module RelevantStateConfig implements DataFlow:: ConfigSig {
167173 predicate isSource ( DataFlow:: Node source ) { isSourceImpl ( source , _) }
168174
@@ -204,9 +210,16 @@ predicate isSinkImpl(DataFlow::Node sink, Class state, Type convertedType, boole
204210 )
205211}
206212
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+ */
207220module BadConfig implements DataFlow:: StateConfigSig {
208221 class FlowState extends Class {
209- FlowState ( ) { isSourceImpl ( _, this ) }
222+ FlowState ( ) { relevantState ( _, this ) }
210223 }
211224
212225 predicate isSource ( DataFlow:: Node source , FlowState state ) { isSourceImpl ( source , state ) }
@@ -220,6 +233,45 @@ module BadConfig implements DataFlow::StateConfigSig {
220233
221234module BadFlow = DataFlow:: GlobalWithState< BadConfig > ;
222235
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+ */
223275module GoodConfig implements DataFlow:: StateConfigSig {
224276 class FlowState = BadConfig:: FlowState ;
225277
0 commit comments