12
12
13
13
import cpp
14
14
import semmle.code.cpp.dataflow.new.DataFlow
15
- import BadFlow :: PathGraph
15
+ import Flow :: PathGraph
16
16
17
17
/**
18
18
* Holds if `f` is a field located at byte offset `offset` in `c`.
@@ -179,23 +179,28 @@ class UnsafeCast extends Cast {
179
179
}
180
180
181
181
/**
182
- * Holds if `source` is an allocation that allocates a value of type `state `.
182
+ * Holds if `source` is an allocation that allocates a value of type `type `.
183
183
*/
184
- predicate isSourceImpl ( DataFlow:: Node source , Class state ) {
185
- state = source .asExpr ( ) .( AllocationExpr ) .getAllocatedElementType ( ) .stripType ( ) and
184
+ predicate isSourceImpl ( DataFlow:: Node source , Class type ) {
185
+ exists ( AllocationExpr alloc |
186
+ alloc = source .asExpr ( ) and
187
+ type = alloc .getAllocatedElementType ( ) .stripType ( ) and
188
+ not exists (
189
+ alloc
190
+ .( NewOrNewArrayExpr )
191
+ .getAllocator ( )
192
+ .( OperatorNewAllocationFunction )
193
+ .getPlacementArgument ( )
194
+ )
195
+ ) and
186
196
exists ( TypeDeclarationEntry tde |
187
- tde = state .getDefinition ( ) and
197
+ tde = type .getDefinition ( ) and
188
198
not tde .isFromUninstantiatedTemplate ( _)
189
199
)
190
200
}
191
201
192
- /**
193
- * The `RelevantStateConfig` configuration is used to find the set of
194
- * states for the `BadConfig` and `GoodConfig`. The flow computed by
195
- * `RelevantStateConfig` is used to implement the `relevantState` predicate
196
- * which is used to avoid a cartesian product in `isSinkImpl`.
197
- */
198
- module RelevantStateConfig implements DataFlow:: ConfigSig {
202
+ /** A configuration describing flow from an allocation to a potentially unsafe cast. */
203
+ module Config implements DataFlow:: ConfigSig {
199
204
predicate isSource ( DataFlow:: Node source ) { isSourceImpl ( source , _) }
200
205
201
206
predicate isBarrier ( DataFlow:: Node node ) {
@@ -212,122 +217,47 @@ module RelevantStateConfig implements DataFlow::ConfigSig {
212
217
)
213
218
}
214
219
215
- predicate isSink ( DataFlow:: Node sink ) {
216
- exists ( UnsafeCast cast | sink .asExpr ( ) = cast .getUnconverted ( ) )
217
- }
220
+ predicate isSink ( DataFlow:: Node sink ) { sink .asExpr ( ) = any ( UnsafeCast cast ) .getUnconverted ( ) }
218
221
219
222
int fieldFlowBranchLimit ( ) { result = 0 }
220
223
}
221
224
222
- module RelevantStateFlow = DataFlow:: Global< RelevantStateConfig > ;
225
+ module Flow = DataFlow:: Global< Config > ;
223
226
224
- predicate relevantState ( DataFlow:: Node source , DataFlow:: Node sink , Class state ) {
225
- RelevantStateFlow:: flow ( source , sink ) and
226
- isSourceImpl ( source , state )
227
+ predicate relevantType ( DataFlow:: Node sink , Class allocatedType ) {
228
+ exists ( DataFlow:: Node source |
229
+ Flow:: flow ( source , sink ) and
230
+ isSourceImpl ( source , allocatedType )
231
+ )
227
232
}
228
233
229
- predicate isSinkImpl ( DataFlow:: Node sink , Class state , Type convertedType , boolean compatible ) {
234
+ predicate isSinkImpl (
235
+ DataFlow:: Node sink , Class allocatedType , Type convertedType , boolean compatible
236
+ ) {
230
237
exists ( UnsafeCast cast |
231
- relevantState ( _ , sink , state ) and
238
+ relevantType ( sink , allocatedType ) and
232
239
sink .asExpr ( ) = cast .getUnconverted ( ) and
233
240
convertedType = cast .getConvertedType ( )
234
241
|
235
- if cast .compatibleWith ( state ) then compatible = true else compatible = false
242
+ if cast .compatibleWith ( allocatedType ) then compatible = true else compatible = false
236
243
)
237
244
}
238
245
239
- /**
240
- * The `BadConfig` configuration tracks flow from an allocation to an
241
- * incompatible cast.
242
- *
243
- * We use `FlowState` to track the type of the source, and compare the
244
- * flow state to the target of the cast in the `isSink` definition.
245
- */
246
- module BadConfig implements DataFlow:: StateConfigSig {
247
- class FlowState extends Class {
248
- FlowState ( ) { relevantState ( _, _, this ) }
249
- }
250
-
251
- predicate isSource ( DataFlow:: Node source , FlowState state ) { relevantState ( source , _, state ) }
252
-
253
- predicate isBarrier ( DataFlow:: Node node ) { RelevantStateConfig:: isBarrier ( node ) }
254
-
255
- predicate isSink ( DataFlow:: Node sink , FlowState state ) { isSinkImpl ( sink , state , _, false ) }
256
-
257
- predicate isBarrierOut ( DataFlow:: Node sink , FlowState state ) { isSink ( sink , state ) }
258
-
259
- int fieldFlowBranchLimit ( ) { result = 0 }
260
- }
261
-
262
- module BadFlow = DataFlow:: GlobalWithState< BadConfig > ;
263
-
264
- /**
265
- * The `GoodConfig` configuration tracks flow from an allocation to a
266
- * compatible cast.
267
- *
268
- * We use `GoodConfig` to reduce the number of FPs from infeasible paths.
269
- * For example, consider the following example:
270
- * ```cpp
271
- * struct Animal { virtual ~Animal(); };
272
- *
273
- * struct Cat : public Animal {
274
- * Cat();
275
- * ~Cat();
276
- * };
277
- *
278
- * struct Dog : public Animal {
279
- * Dog();
280
- * ~Dog();
281
- * };
282
- *
283
- * void test9(bool b) {
284
- * Animal* a;
285
- * if(b) {
286
- * a = new Cat;
287
- * } else {
288
- * a = new Dog;
289
- * }
290
- * if(b) {
291
- * Cat* d = static_cast<Cat*>(a);
292
- * }
293
- * }
294
- * ```
295
- * Here, `BadConfig` finds a flow from `a = new Dog` to `static_cast<Cat*>(a)`.
296
- * However, that path is never realized in an actual execution path. So in
297
- * order to remove this result we exclude results where there exists an
298
- * allocation of a type that's compatible with `static_cast<Cat*>(a)`.
299
- *
300
- * We use `FlowState` to track the type of the source, and compare the
301
- * flow state to the target of the cast in the `isSink` definition.
302
- */
303
- module GoodConfig implements DataFlow:: StateConfigSig {
304
- class FlowState = BadConfig:: FlowState ;
305
-
306
- predicate isSource ( DataFlow:: Node source , FlowState state ) { BadConfig:: isSource ( source , state ) }
307
-
308
- predicate isBarrier ( DataFlow:: Node node ) { BadConfig:: isBarrier ( node ) }
309
-
310
- predicate isSink ( DataFlow:: Node sink , FlowState state ) {
311
- isSinkImpl ( sink , state , _, true ) and
312
- BadFlow:: flowTo ( sink )
313
- }
314
-
315
- int fieldFlowBranchLimit ( ) { result = 0 }
316
- }
317
-
318
- module GoodFlow = DataFlow:: GlobalWithState< GoodConfig > ;
319
-
320
246
from
321
- BadFlow :: PathNode source , BadFlow :: PathNode sink , Type sourceType , Type sinkType ,
247
+ Flow :: PathNode source , Flow :: PathNode sink , Type badSourceType , Type sinkType ,
322
248
DataFlow:: Node sinkNode
323
249
where
324
- BadFlow :: flowPath ( source , sink ) and
250
+ Flow :: flowPath ( source , sink ) and
325
251
sinkNode = sink .getNode ( ) and
252
+ isSourceImpl ( source .getNode ( ) , badSourceType ) and
253
+ isSinkImpl ( sinkNode , badSourceType , sinkType , false ) and
326
254
// If there is any flow that would result in a valid cast then we don't
327
255
// report an alert here. This reduces the number of FPs from infeasible paths
328
256
// significantly.
329
- not GoodFlow:: flowTo ( sinkNode ) and
330
- isSourceImpl ( source .getNode ( ) , sourceType ) and
331
- isSinkImpl ( sinkNode , _, sinkType , false )
332
- select sinkNode , source , sink , "Conversion from $@ to $@ is invalid." , sourceType ,
333
- sourceType .toString ( ) , sinkType , sinkType .toString ( )
257
+ not exists ( DataFlow:: Node goodSource , Type goodSourceType |
258
+ isSourceImpl ( goodSource , goodSourceType ) and
259
+ isSinkImpl ( sinkNode , goodSourceType , sinkType , true ) and
260
+ Flow:: flow ( goodSource , sinkNode )
261
+ )
262
+ select sinkNode , source , sink , "Conversion from $@ to $@ is invalid." , badSourceType ,
263
+ badSourceType .toString ( ) , sinkType , sinkType .toString ( )
0 commit comments