20
20
import csharp
21
21
private import ControlFlow
22
22
private import internal.CallableReturns
23
- private import semmle.code.csharp.commons.Assertions
24
23
private import semmle.code.csharp.controlflow.Guards as G
25
24
private import semmle.code.csharp.controlflow.Guards:: AbstractValues
26
25
private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl
@@ -119,49 +118,12 @@ private predicate nonNullDef(Ssa::ExplicitDefinition def) {
119
118
}
120
119
121
120
/**
122
- * Holds if the `i`th node of basic block `bb` is a dereference `d` of SSA
123
- * definition `def`.
121
+ * Holds if the `node` is a dereference `d` of SSA definition `def`.
124
122
*/
125
- private predicate dereferenceAt ( BasicBlock bb , int i , Ssa:: Definition def , Dereference d ) {
126
- d = def .getAReadAtNode ( bb .getNode ( i ) )
127
- }
128
-
129
123
private predicate dereferenceAt ( ControlFlow:: Node node , Ssa:: Definition def , Dereference d ) {
130
124
d = def .getAReadAtNode ( node )
131
125
}
132
126
133
- /**
134
- * Holds if `e` having abstract value `vExpr` implies that SSA definition `def`
135
- * has abstract value `vDef`.
136
- */
137
- private predicate exprImpliesSsaDef (
138
- G:: Guard e , G:: AbstractValue vExpr , Ssa:: Definition def , G:: AbstractValue vDef
139
- ) {
140
- vExpr = e .getAValue ( ) and
141
- vExpr = vDef and
142
- (
143
- e = def .getARead ( )
144
- or
145
- e = def .( Ssa:: ExplicitDefinition ) .getADefinition ( ) .getTargetAccess ( )
146
- )
147
- or
148
- exists ( Expr e0 , G:: AbstractValue vExpr0 |
149
- exprImpliesSsaDef ( e0 , vExpr0 , def , vDef ) and
150
- G:: Internal:: impliesStep ( e , vExpr , e0 , vExpr0 )
151
- )
152
- }
153
-
154
- /**
155
- * Gets an element that tests whether a given SSA definition, `def`, is
156
- * `null` or not.
157
- *
158
- * If the returned element takes the `s` branch, then `def` is guaranteed to be
159
- * `null` if `nv.isNull()` holds, and non-`null` otherwise.
160
- */
161
- private ControlFlowElement getANullCheck ( Ssa:: Definition def , ConditionalSuccessor s , NullValue nv ) {
162
- exists ( Expr e , G:: AbstractValue v | v .branch ( result , s , e ) | exprImpliesSsaDef ( e , v , def , nv ) )
163
- }
164
-
165
127
private predicate isMaybeNullArgument ( Ssa:: ImplicitParameterDefinition def , MaybeNullExpr arg ) {
166
128
exists ( AssignableDefinitions:: ImplicitParameterDefinition pdef , Parameter p |
167
129
p = def .getParameter ( )
@@ -263,7 +225,7 @@ private predicate defMaybeNull(
263
225
)
264
226
or
265
227
// A variable of nullable type may be null
266
- exists ( Dereference d | dereferenceAt ( _, _ , def , d ) |
228
+ exists ( Dereference d | dereferenceAt ( _, def , d ) |
267
229
node = def .getControlFlowNode ( ) and
268
230
d .hasNullableType ( ) and
269
231
not def instanceof Ssa:: PhiNode and
@@ -273,219 +235,6 @@ private predicate defMaybeNull(
273
235
)
274
236
}
275
237
276
- pragma [ noinline]
277
- private predicate sourceVariableMaybeNull ( Ssa:: SourceVariable v ) {
278
- defMaybeNull ( v .getAnSsaDefinition ( ) , _, _, _)
279
- }
280
-
281
- pragma [ noinline]
282
- private predicate defNullImpliesStep0 (
283
- Ssa:: SourceVariable v , Ssa:: Definition def1 , BasicBlock bb1 , BasicBlock bb2
284
- ) {
285
- sourceVariableMaybeNull ( v ) and
286
- def1 .getSourceVariable ( ) = v and
287
- def1 .isLiveAtEndOfBlock ( bb1 ) and
288
- bb2 = bb1 .getASuccessor ( )
289
- }
290
-
291
- /**
292
- * Holds if `def1` being `null` in basic block `bb1` implies that `def2` might
293
- * be `null` in basic block `bb2`. The SSA definitions share the same source
294
- * variable.
295
- */
296
- private predicate defNullImpliesStep (
297
- Ssa:: Definition def1 , BasicBlock bb1 , Ssa:: Definition def2 , BasicBlock bb2
298
- ) {
299
- exists ( Ssa:: SourceVariable v | defNullImpliesStep0 ( v , def1 , bb1 , bb2 ) |
300
- def2 .( Ssa:: PhiNode ) .getAnInput ( ) = def1 and
301
- bb2 = def2 .getBasicBlock ( )
302
- or
303
- def2 = def1 and
304
- not exists ( Ssa:: PhiNode phi |
305
- phi .getSourceVariable ( ) = v and
306
- bb2 = phi .getBasicBlock ( )
307
- )
308
- ) and
309
- not exists ( ConditionalSuccessor s , NullValue nv |
310
- bb1 .getLastNode ( ) = getANullCheck ( def1 , s , nv ) .getAControlFlowNode ( )
311
- |
312
- bb2 = bb1 .getASuccessor ( s ) and
313
- nv .isNonNull ( )
314
- )
315
- }
316
-
317
- /**
318
- * The transitive closure of `defNullImpliesStep()` originating from `defMaybeNull()`.
319
- * That is, those basic blocks for which the SSA definition is suspected of being `null`.
320
- */
321
- private predicate defMaybeNullInBlock ( Ssa:: Definition def , BasicBlock bb ) {
322
- defMaybeNull ( def , _, _, _) and
323
- bb = def .getBasicBlock ( )
324
- or
325
- exists ( BasicBlock mid , Ssa:: Definition midDef | defMaybeNullInBlock ( midDef , mid ) |
326
- defNullImpliesStep ( midDef , mid , def , bb )
327
- )
328
- }
329
-
330
- /**
331
- * Holds if `v` is a source variable that might reach a potential `null`
332
- * dereference.
333
- */
334
- private predicate nullDerefCandidateVariable ( Ssa:: SourceVariable v ) {
335
- exists ( Ssa:: Definition def , BasicBlock bb | dereferenceAt ( bb , _, def , _) |
336
- defMaybeNullInBlock ( def , bb ) and
337
- v = def .getSourceVariable ( )
338
- )
339
- }
340
-
341
- private predicate succStep ( PathNode pred , Ssa:: Definition def , BasicBlock bb ) {
342
- defNullImpliesStep ( pred .getSsaDefinition ( ) , pred .getBasicBlock ( ) , def , bb )
343
- }
344
-
345
- private predicate succNullArgument ( SourcePathNode pred , Ssa:: Definition def , BasicBlock bb ) {
346
- pred = TSourcePathNode ( def , _, _, true ) and
347
- bb = def .getBasicBlock ( )
348
- }
349
-
350
- private predicate succSourceSink ( SourcePathNode source , Ssa:: Definition def , BasicBlock bb ) {
351
- source = TSourcePathNode ( def , _, _, false ) and
352
- bb = def .getBasicBlock ( )
353
- }
354
-
355
- private newtype TPathNode =
356
- TSourcePathNode ( Ssa:: Definition def , string msg , Element reason , boolean isNullArgument ) {
357
- nullDerefCandidateVariable ( def .getSourceVariable ( ) ) and
358
- defMaybeNull ( def , _, msg , reason ) and
359
- if isMaybeNullArgument ( def , reason ) then isNullArgument = true else isNullArgument = false
360
- } or
361
- TInternalPathNode ( Ssa:: Definition def , BasicBlock bb ) {
362
- succStep ( _, def , bb )
363
- or
364
- succNullArgument ( _, def , bb )
365
- } or
366
- TSinkPathNode ( Ssa:: Definition def , BasicBlock bb , int i , Dereference d ) {
367
- dereferenceAt ( bb , i , def , d ) and
368
- (
369
- succStep ( _, def , bb )
370
- or
371
- succNullArgument ( _, def , bb )
372
- or
373
- succSourceSink ( _, def , bb )
374
- )
375
- }
376
-
377
- /**
378
- * An SSA definition, which may be `null`, augmented with at basic block which can
379
- * be reached without passing through a `null` check.
380
- */
381
- abstract class PathNode extends TPathNode {
382
- /** Gets the SSA definition. */
383
- abstract Ssa:: Definition getSsaDefinition ( ) ;
384
-
385
- /** Gets the basic block that can be reached without passing through a `null` check. */
386
- abstract BasicBlock getBasicBlock ( ) ;
387
-
388
- /** Gets another node that can be reached from this node. */
389
- abstract PathNode getASuccessor ( ) ;
390
-
391
- /** Gets a textual representation of this node. */
392
- abstract string toString ( ) ;
393
-
394
- /** Gets the location of this node. */
395
- abstract Location getLocation ( ) ;
396
- }
397
-
398
- private class SourcePathNode extends PathNode , TSourcePathNode {
399
- private Ssa:: Definition def ;
400
- private string msg ;
401
- private Element reason ;
402
- private boolean isNullArgument ;
403
-
404
- SourcePathNode ( ) { this = TSourcePathNode ( def , msg , reason , isNullArgument ) }
405
-
406
- override Ssa:: Definition getSsaDefinition ( ) { result = def }
407
-
408
- override BasicBlock getBasicBlock ( ) {
409
- isNullArgument = false and
410
- result = def .getBasicBlock ( )
411
- }
412
-
413
- string getMessage ( ) { result = msg }
414
-
415
- Element getReason ( ) { result = reason }
416
-
417
- override PathNode getASuccessor ( ) {
418
- succStep ( this , result .getSsaDefinition ( ) , result .getBasicBlock ( ) )
419
- or
420
- succNullArgument ( this , result .getSsaDefinition ( ) , result .getBasicBlock ( ) )
421
- or
422
- result instanceof SinkPathNode and
423
- succSourceSink ( this , result .getSsaDefinition ( ) , result .getBasicBlock ( ) )
424
- }
425
-
426
- override string toString ( ) {
427
- if isNullArgument = true then result = reason .toString ( ) else result = def .toString ( )
428
- }
429
-
430
- override Location getLocation ( ) {
431
- if isNullArgument = true then result = reason .getLocation ( ) else result = def .getLocation ( )
432
- }
433
- }
434
-
435
- private class InternalPathNode extends PathNode , TInternalPathNode {
436
- private Ssa:: Definition def ;
437
- private BasicBlock bb ;
438
-
439
- InternalPathNode ( ) { this = TInternalPathNode ( def , bb ) }
440
-
441
- override Ssa:: Definition getSsaDefinition ( ) { result = def }
442
-
443
- override BasicBlock getBasicBlock ( ) { result = bb }
444
-
445
- override PathNode getASuccessor ( ) {
446
- succStep ( this , result .getSsaDefinition ( ) , result .getBasicBlock ( ) )
447
- }
448
-
449
- override string toString ( ) { result = bb .getFirstNode ( ) .toString ( ) }
450
-
451
- override Location getLocation ( ) { result = bb .getFirstNode ( ) .getLocation ( ) }
452
- }
453
-
454
- private class SinkPathNode extends PathNode , TSinkPathNode {
455
- private Ssa:: Definition def ;
456
- private BasicBlock bb ;
457
- private int i ;
458
- private Dereference d ;
459
-
460
- SinkPathNode ( ) { this = TSinkPathNode ( def , bb , i , d ) }
461
-
462
- override Ssa:: Definition getSsaDefinition ( ) { result = def }
463
-
464
- override BasicBlock getBasicBlock ( ) { result = bb }
465
-
466
- override PathNode getASuccessor ( ) { none ( ) }
467
-
468
- Dereference getDereference ( ) { result = d }
469
-
470
- override string toString ( ) { result = d .toString ( ) }
471
-
472
- override Location getLocation ( ) { result = d .getLocation ( ) }
473
- }
474
-
475
- /**
476
- * Provides the query predicates needed to include a graph in a path-problem query
477
- * for `Dereference::is[First]MaybeNull()`.
478
- */
479
- module PathGraph {
480
- query predicate nodes ( PathNode n ) { n .getASuccessor * ( ) instanceof SinkPathNode }
481
-
482
- query predicate edges ( PathNode pred , PathNode succ ) {
483
- nodes ( pred ) and
484
- nodes ( succ ) and
485
- succ = pred .getASuccessor ( )
486
- }
487
- }
488
-
489
238
private Ssa:: Definition getAPseudoInput ( Ssa:: Definition def ) {
490
239
result = def .( Ssa:: PhiNode ) .getAnInput ( )
491
240
}
@@ -499,21 +248,15 @@ private Ssa::Definition getAnUltimateDefinition(Ssa::Definition def) {
499
248
500
249
/**
501
250
* Holds if SSA definition `def` can reach a read at `cfn`, without passing
502
- * through an intermediate dereference that always (`always = true`) or
503
- * maybe (`always = false`) throws a null reference exception.
251
+ * through an intermediate dereference that always throws a null reference
252
+ * exception.
504
253
*/
505
- private predicate defReaches ( Ssa:: Definition def , ControlFlow:: Node cfn , boolean always ) {
506
- exists ( def .getAFirstReadAtNode ( cfn ) ) and
507
- ( always = true or always = false )
254
+ private predicate defReaches ( Ssa:: Definition def , ControlFlow:: Node cfn ) {
255
+ exists ( def .getAFirstReadAtNode ( cfn ) )
508
256
or
509
- exists ( ControlFlow:: Node mid | defReaches ( def , mid , always ) |
257
+ exists ( ControlFlow:: Node mid | defReaches ( def , mid ) |
510
258
SsaImpl:: adjacentReadPairSameVar ( _, mid , cfn ) and
511
- not mid =
512
- any ( Dereference d |
513
- if always = true
514
- then d .isAlwaysNull ( def .getSourceVariable ( ) )
515
- else d .isMaybeNull ( def , _, _, _, _)
516
- ) .getAControlFlowNode ( )
259
+ not mid = any ( Dereference d | d .isAlwaysNull ( def .getSourceVariable ( ) ) ) .getAControlFlowNode ( )
517
260
)
518
261
}
519
262
@@ -534,7 +277,7 @@ private module NullnessConfig implements Cf::ControlFlowReachability::ConfigSig
534
277
535
278
private module NullnessFlow = Cf:: ControlFlowReachability:: Flow< NullnessConfig > ;
536
279
537
- predicate debug_nullness_new ( Dereference d , Ssa:: SourceVariable v , string msg , Element reason ) {
280
+ predicate maybeNullDeref ( Dereference d , Ssa:: SourceVariable v , string msg , Element reason ) {
538
281
exists (
539
282
Ssa:: Definition origin , Ssa:: Definition ssa , ControlFlow:: Node src , ControlFlow:: Node sink
540
283
|
@@ -546,11 +289,6 @@ predicate debug_nullness_new(Dereference d, Ssa::SourceVariable v, string msg, E
546
289
)
547
290
}
548
291
549
- predicate debug_nullness_orig ( Dereference d , Ssa:: SourceVariable v , string msg , Element reason ) {
550
- d .isFirstMaybeNull ( v .getAnSsaDefinition ( ) , _, _, msg , reason ) and
551
- not d instanceof NonNullExpr
552
- }
553
-
554
292
/**
555
293
* An expression that dereferences a value. That is, an expression that may
556
294
* result in a `NullReferenceException` if the value is `null`.
@@ -645,33 +383,6 @@ class Dereference extends G::DereferenceableExpr {
645
383
*/
646
384
predicate isFirstAlwaysNull ( Ssa:: SourceVariable v ) {
647
385
this .isAlwaysNull ( v ) and
648
- defReaches ( v .getAnSsaDefinition ( ) , this .getAControlFlowNode ( ) , true )
649
- }
650
-
651
- /**
652
- * Holds if this expression dereferences SSA definition `def`, which may
653
- * be `null`.
654
- */
655
- predicate isMaybeNull (
656
- Ssa:: Definition def , SourcePathNode source , SinkPathNode sink , string msg , Element reason
657
- ) {
658
- source .getASuccessor * ( ) = sink and
659
- msg = source .getMessage ( ) and
660
- reason = source .getReason ( ) and
661
- def = sink .getSsaDefinition ( ) and
662
- this = sink .getDereference ( ) and
663
- not this .isAlwaysNull ( def .getSourceVariable ( ) )
664
- }
665
-
666
- /**
667
- * Holds if this expression dereferences SSA definition `def`, which may
668
- * be `null`, and this expression can be reached from `def` without passing
669
- * through another such dereference.
670
- */
671
- predicate isFirstMaybeNull (
672
- Ssa:: Definition def , SourcePathNode source , SinkPathNode sink , string msg , Element reason
673
- ) {
674
- this .isMaybeNull ( def , source , sink , msg , reason ) and
675
- defReaches ( def , this .getAControlFlowNode ( ) , false )
386
+ defReaches ( v .getAnSsaDefinition ( ) , this .getAControlFlowNode ( ) )
676
387
}
677
388
}
0 commit comments