@@ -26,27 +26,30 @@ private import semmle.code.csharp.controlflow.Guards::AbstractValues
2626private import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl
2727private import semmle.code.csharp.frameworks.System
2828private import semmle.code.csharp.frameworks.Test
29+ private import semmle.code.csharp.controlflow.ControlFlowReachability as Cf
30+
31+ private Expr maybeNullExpr ( Expr reason ) {
32+ G:: Internal:: nullValue ( result ) and reason = result
33+ or
34+ result instanceof AsExpr and reason = result
35+ or
36+ result .( AssignExpr ) .getRValue ( ) = maybeNullExpr ( reason )
37+ or
38+ result .( Cast ) .getExpr ( ) = maybeNullExpr ( reason )
39+ or
40+ result =
41+ any ( ConditionalExpr ce |
42+ ce .getThen ( ) = maybeNullExpr ( reason )
43+ or
44+ ce .getElse ( ) = maybeNullExpr ( reason )
45+ )
46+ or
47+ result .( NullCoalescingExpr ) .getRightOperand ( ) = maybeNullExpr ( reason )
48+ }
2949
3050/** An expression that may be `null`. */
3151class MaybeNullExpr extends Expr {
32- MaybeNullExpr ( ) {
33- G:: Internal:: nullValue ( this )
34- or
35- this instanceof AsExpr
36- or
37- this .( AssignExpr ) .getRValue ( ) instanceof MaybeNullExpr
38- or
39- this .( Cast ) .getExpr ( ) instanceof MaybeNullExpr
40- or
41- this =
42- any ( ConditionalExpr ce |
43- ce .getThen ( ) instanceof MaybeNullExpr
44- or
45- ce .getElse ( ) instanceof MaybeNullExpr
46- )
47- or
48- this .( NullCoalescingExpr ) .getRightOperand ( ) instanceof MaybeNullExpr
49- }
52+ MaybeNullExpr ( ) { this = maybeNullExpr ( _) }
5053}
5154
5255/** An expression that is always `null`. */
@@ -123,6 +126,10 @@ private predicate dereferenceAt(BasicBlock bb, int i, Ssa::Definition def, Deref
123126 d = def .getAReadAtNode ( bb .getNode ( i ) )
124127}
125128
129+ private predicate dereferenceAt ( ControlFlow:: Node node , Ssa:: Definition def , Dereference d ) {
130+ d = def .getAReadAtNode ( node )
131+ }
132+
126133/**
127134 * Holds if `e` having abstract value `vExpr` implies that SSA definition `def`
128135 * has abstract value `vDef`.
@@ -217,13 +224,16 @@ private predicate isNullDefaultArgument(Ssa::ImplicitParameterDefinition def, Al
217224}
218225
219226/** Holds if `def` is an SSA definition that may be `null`. */
220- private predicate defMaybeNull ( Ssa:: Definition def , string msg , Element reason ) {
227+ private predicate defMaybeNull (
228+ Ssa:: Definition def , ControlFlow:: Node node , string msg , Element reason
229+ ) {
221230 not nonNullDef ( def ) and
222231 (
223232 // A variable compared to `null` might be `null`
224233 exists ( G:: DereferenceableExpr de | de = def .getARead ( ) |
225234 reason = de .getANullCheck ( _, true ) and
226235 msg = "as suggested by $@ null check" and
236+ node = def .getControlFlowNode ( ) and
227237 not de = any ( Ssa:: PhiNode phi ) .getARead ( ) and
228238 strictcount ( Element e | e = any ( Ssa:: Definition def0 | de = def0 .getARead ( ) ) .getElement ( ) ) = 1 and
229239 // Don't use a check as reason if there is a `null` assignment
@@ -234,23 +244,27 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
234244 or
235245 // A parameter might be `null` if there is a `null` argument somewhere
236246 isMaybeNullArgument ( def , reason ) and
247+ node = def .getControlFlowNode ( ) and
237248 (
238249 if reason instanceof AlwaysNullExpr
239250 then msg = "because of $@ null argument"
240251 else msg = "because of $@ potential null argument"
241252 )
242253 or
243- isNullDefaultArgument ( def , reason ) and msg = "because the parameter has a null default value"
254+ isNullDefaultArgument ( def , reason ) and
255+ node = def .getControlFlowNode ( ) and
256+ msg = "because the parameter has a null default value"
244257 or
245258 // If the source of a variable is `null` then the variable may be `null`
246259 exists ( AssignableDefinition adef | adef = def .( Ssa:: ExplicitDefinition ) .getADefinition ( ) |
247- adef .getSource ( ) instanceof MaybeNullExpr and
260+ adef .getSource ( ) = maybeNullExpr ( node . getAstNode ( ) ) and
248261 reason = adef .getExpr ( ) and
249262 msg = "because of $@ assignment"
250263 )
251264 or
252265 // A variable of nullable type may be null
253266 exists ( Dereference d | dereferenceAt ( _, _, def , d ) |
267+ node = def .getControlFlowNode ( ) and
254268 d .hasNullableType ( ) and
255269 not def instanceof Ssa:: PhiNode and
256270 reason = def .getSourceVariable ( ) .getAssignable ( ) and
@@ -261,7 +275,7 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
261275
262276pragma [ noinline]
263277private predicate sourceVariableMaybeNull ( Ssa:: SourceVariable v ) {
264- defMaybeNull ( v .getAnSsaDefinition ( ) , _, _)
278+ defMaybeNull ( v .getAnSsaDefinition ( ) , _, _, _ )
265279}
266280
267281pragma [ noinline]
@@ -305,7 +319,7 @@ private predicate defNullImpliesStep(
305319 * That is, those basic blocks for which the SSA definition is suspected of being `null`.
306320 */
307321private predicate defMaybeNullInBlock ( Ssa:: Definition def , BasicBlock bb ) {
308- defMaybeNull ( def , _, _) and
322+ defMaybeNull ( def , _, _, _ ) and
309323 bb = def .getBasicBlock ( )
310324 or
311325 exists ( BasicBlock mid , Ssa:: Definition midDef | defMaybeNullInBlock ( midDef , mid ) |
@@ -341,7 +355,7 @@ private predicate succSourceSink(SourcePathNode source, Ssa::Definition def, Bas
341355private newtype TPathNode =
342356 TSourcePathNode ( Ssa:: Definition def , string msg , Element reason , boolean isNullArgument ) {
343357 nullDerefCandidateVariable ( def .getSourceVariable ( ) ) and
344- defMaybeNull ( def , msg , reason ) and
358+ defMaybeNull ( def , _ , msg , reason ) and
345359 if isMaybeNullArgument ( def , reason ) then isNullArgument = true else isNullArgument = false
346360 } or
347361 TInternalPathNode ( Ssa:: Definition def , BasicBlock bb ) {
@@ -503,6 +517,40 @@ private predicate defReaches(Ssa::Definition def, ControlFlow::Node cfn, boolean
503517 )
504518}
505519
520+ private module NullnessConfig implements Cf:: ControlFlowReachability:: ConfigSig {
521+ predicate source ( ControlFlow:: Node node , Ssa:: Definition def ) { defMaybeNull ( def , node , _, _) }
522+
523+ predicate sink ( ControlFlow:: Node node , Ssa:: Definition def ) {
524+ exists ( Dereference d |
525+ dereferenceAt ( node , def , d ) and
526+ not d instanceof NonNullExpr
527+ )
528+ }
529+
530+ predicate barrierValue ( G:: GuardValue gv ) { gv .isNullness ( false ) }
531+
532+ predicate uncertainFlow ( ) { none ( ) }
533+ }
534+
535+ private module NullnessFlow = Cf:: ControlFlowReachability:: Flow< NullnessConfig > ;
536+
537+ predicate debug_nullness_new ( Dereference d , Ssa:: SourceVariable v , string msg , Element reason ) {
538+ exists (
539+ Ssa:: Definition origin , Ssa:: Definition ssa , ControlFlow:: Node src , ControlFlow:: Node sink
540+ |
541+ defMaybeNull ( origin , src , msg , reason ) and
542+ NullnessFlow:: flow ( src , origin , sink , ssa ) and
543+ ssa .getSourceVariable ( ) = v and
544+ dereferenceAt ( sink , ssa , d ) and
545+ not d .isAlwaysNull ( v )
546+ )
547+ }
548+
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+
506554/**
507555 * An expression that dereferences a value. That is, an expression that may
508556 * result in a `NullReferenceException` if the value is `null`.
0 commit comments