2020import csharp
2121private import ControlFlow
2222private import internal.CallableReturns
23- private import semmle.code.csharp.commons.Assertions
2423private import semmle.code.csharp.controlflow.Guards as G
2524private import semmle.code.csharp.controlflow.Guards:: AbstractValues
2625private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl
@@ -119,49 +118,12 @@ private predicate nonNullDef(Ssa::ExplicitDefinition def) {
119118}
120119
121120/**
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`.
124122 */
125- private predicate dereferenceAt ( BasicBlock bb , int i , Ssa:: Definition def , Dereference d ) {
126- d = def .getAReadAtNode ( bb .getNode ( i ) )
127- }
128-
129123private predicate dereferenceAt ( ControlFlow:: Node node , Ssa:: Definition def , Dereference d ) {
130124 d = def .getAReadAtNode ( node )
131125}
132126
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-
165127private predicate isMaybeNullArgument ( Ssa:: ImplicitParameterDefinition def , MaybeNullExpr arg ) {
166128 exists ( AssignableDefinitions:: ImplicitParameterDefinition pdef , Parameter p |
167129 p = def .getParameter ( )
@@ -263,7 +225,7 @@ private predicate defMaybeNull(
263225 )
264226 or
265227 // A variable of nullable type may be null
266- exists ( Dereference d | dereferenceAt ( _, _ , def , d ) |
228+ exists ( Dereference d | dereferenceAt ( _, def , d ) |
267229 node = def .getControlFlowNode ( ) and
268230 d .hasNullableType ( ) and
269231 not def instanceof Ssa:: PhiNode and
@@ -273,219 +235,6 @@ private predicate defMaybeNull(
273235 )
274236}
275237
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-
489238private Ssa:: Definition getAPseudoInput ( Ssa:: Definition def ) {
490239 result = def .( Ssa:: PhiNode ) .getAnInput ( )
491240}
@@ -499,21 +248,15 @@ private Ssa::Definition getAnUltimateDefinition(Ssa::Definition def) {
499248
500249/**
501250 * 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.
504253 */
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 ) )
508256 or
509- exists ( ControlFlow:: Node mid | defReaches ( def , mid , always ) |
257+ exists ( ControlFlow:: Node mid | defReaches ( def , mid ) |
510258 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 ( )
517260 )
518261}
519262
@@ -534,7 +277,7 @@ private module NullnessConfig implements Cf::ControlFlowReachability::ConfigSig
534277
535278private module NullnessFlow = Cf:: ControlFlowReachability:: Flow< NullnessConfig > ;
536279
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 ) {
538281 exists (
539282 Ssa:: Definition origin , Ssa:: Definition ssa , ControlFlow:: Node src , ControlFlow:: Node sink
540283 |
@@ -546,11 +289,6 @@ predicate debug_nullness_new(Dereference d, Ssa::SourceVariable v, string msg, E
546289 )
547290}
548291
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-
554292/**
555293 * An expression that dereferences a value. That is, an expression that may
556294 * result in a `NullReferenceException` if the value is `null`.
@@ -645,33 +383,6 @@ class Dereference extends G::DereferenceableExpr {
645383 */
646384 predicate isFirstAlwaysNull ( Ssa:: SourceVariable v ) {
647385 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 ( ) )
676387 }
677388}
0 commit comments