@@ -147,6 +147,15 @@ private ControlFlowElement getANullCheck(Ssa::Definition def, SuccessorTypes::Co
147147 )
148148}
149149
150+ private predicate isMaybeNullArgument ( Ssa:: ExplicitDefinition def , MaybeNullExpr arg ) {
151+ exists ( AssignableDefinitions:: ImplicitParameterDefinition pdef , Parameter p |
152+ pdef = def .getADefinition ( ) |
153+ p = pdef .getParameter ( ) .getSourceDeclaration ( ) and
154+ p .getAnAssignedArgument ( ) = arg and
155+ not arg .getEnclosingCallable ( ) .getEnclosingCallable * ( ) instanceof TestMethod
156+ )
157+ }
158+
150159/** Holds if `def` is an SSA definition that may be `null`. */
151160private predicate defMaybeNull ( Ssa:: Definition def , string msg , Element reason ) {
152161 // A variable compared to `null` might be `null`
@@ -160,18 +169,14 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
160169 ) = 1 and
161170 not nonNullDef ( def ) and
162171 // Don't use a check as reason if there is a `null` assignment
163- not def .( Ssa:: ExplicitDefinition ) .getADefinition ( ) .getSource ( ) instanceof MaybeNullExpr
172+ // or argument
173+ not def .( Ssa:: ExplicitDefinition ) .getADefinition ( ) .getSource ( ) instanceof MaybeNullExpr and
174+ not isMaybeNullArgument ( def , _)
164175 )
165176 or
166177 // A parameter might be `null` if there is a `null` argument somewhere
167- exists ( AssignableDefinitions:: ImplicitParameterDefinition pdef , Parameter p , MaybeNullExpr arg |
168- pdef = def .( Ssa:: ExplicitDefinition ) .getADefinition ( ) |
169- p = pdef .getParameter ( ) .getSourceDeclaration ( ) and
170- p .getAnAssignedArgument ( ) = arg and
171- reason = arg and
172- msg = "because of $@ null argument" and
173- not arg .getEnclosingCallable ( ) .getEnclosingCallable * ( ) instanceof TestMethod
174- )
178+ isMaybeNullArgument ( def , reason ) and
179+ msg = "because of $@ null argument"
175180 or
176181 // If the source of a variable is `null` then the variable may be `null`
177182 exists ( AssignableDefinition adef |
@@ -202,12 +207,12 @@ private predicate defNullImpliesStep(Ssa::Definition def1, BasicBlock bb1, Ssa::
202207 def1 .getSourceVariable ( ) = v
203208 |
204209 def2 .( Ssa:: PseudoDefinition ) .getAnInput ( ) = def1 and
205- def2 .definesAt ( bb2 , _ )
210+ bb2 = def2 .getBasicBlock ( )
206211 or
207212 def2 = def1 and
208213 not exists ( Ssa:: PseudoDefinition def |
209214 def .getSourceVariable ( ) = v and
210- def .definesAt ( bb2 , _ )
215+ bb2 = def .getBasicBlock ( )
211216 )
212217 ) and
213218 def1 .isLiveAtEndOfBlock ( bb1 ) and
@@ -224,13 +229,12 @@ private predicate defNullImpliesStep(Ssa::Definition def1, BasicBlock bb1, Ssa::
224229 * The transitive closure of `defNullImpliesStep()` originating from `defMaybeNull()`.
225230 * That is, those basic blocks for which the SSA definition is suspected of being `null`.
226231 */
227- private predicate defMaybeNullInBlock ( Ssa:: Definition def , Ssa :: SourceVariable v , BasicBlock bb ) {
232+ private predicate defMaybeNullInBlock ( Ssa:: Definition def , BasicBlock bb ) {
228233 defMaybeNull ( def , _, _) and
229- def .definesAt ( bb , _) and
230- v = def .getSourceVariable ( )
234+ bb = def .getBasicBlock ( )
231235 or
232236 exists ( BasicBlock mid , Ssa:: Definition midDef |
233- defMaybeNullInBlock ( midDef , v , mid ) |
237+ defMaybeNullInBlock ( midDef , mid ) |
234238 defNullImpliesStep ( midDef , mid , def , bb )
235239 )
236240}
@@ -242,20 +246,167 @@ private predicate defMaybeNullInBlock(Ssa::Definition def, Ssa::SourceVariable v
242246private predicate nullDerefCandidateVariable ( Ssa:: SourceVariable v ) {
243247 exists ( Ssa:: Definition def , BasicBlock bb |
244248 potentialNullDereferenceAt ( bb , _, def , _) |
245- defMaybeNullInBlock ( def , v , bb )
249+ defMaybeNullInBlock ( def , bb ) and
250+ v = def .getSourceVariable ( )
246251 )
247252}
248253
249- private predicate defMaybeNullInBlockOrigin ( Ssa:: Definition origin , Ssa:: Definition def , BasicBlock bb ) {
250- nullDerefCandidateVariable ( def .getSourceVariable ( ) ) and
251- defMaybeNull ( def , _, _) and
252- def .definesAt ( bb , _) and
253- origin = def
254+ private predicate succStep ( PathNode pred , Ssa:: Definition def , BasicBlock bb ) {
255+ defNullImpliesStep ( pred .getSsaDefinition ( ) , pred .getBasicBlock ( ) , def , bb )
256+ }
257+
258+ private predicate succNullArgument ( SourcePathNode pred , Ssa:: Definition def , BasicBlock bb ) {
259+ pred = TSourcePathNode ( def , _, _, true ) and
260+ bb = def .getBasicBlock ( )
261+ }
262+
263+ private predicate succSourceSink ( SourcePathNode source , Ssa:: Definition def , BasicBlock bb ) {
264+ source = TSourcePathNode ( def , _, _, false ) and
265+ bb = def .getBasicBlock ( )
266+ }
267+
268+ private newtype TPathNode =
269+ TSourcePathNode ( Ssa:: Definition def , string msg , Element reason , boolean isNullArgument ) {
270+ nullDerefCandidateVariable ( def .getSourceVariable ( ) ) and
271+ defMaybeNull ( def , msg , reason ) and
272+ if isMaybeNullArgument ( def , reason ) then isNullArgument = true else isNullArgument = false
273+ }
254274 or
255- exists ( BasicBlock mid , Ssa:: Definition midDef |
256- defMaybeNullInBlockOrigin ( origin , midDef , mid ) and
257- defNullImpliesStep ( midDef , mid , def , bb )
258- )
275+ TInternalPathNode ( Ssa:: Definition def , BasicBlock bb ) {
276+ succStep ( _, def , bb )
277+ or
278+ succNullArgument ( _, def , bb )
279+ }
280+ or
281+ TSinkPathNode ( Ssa:: Definition def , BasicBlock bb , int i , Dereference d ) {
282+ potentialNullDereferenceAt ( bb , i , def , d ) and
283+ (
284+ succStep ( _, def , bb )
285+ or
286+ succNullArgument ( _, def , bb )
287+ or
288+ succSourceSink ( _, def , bb )
289+ )
290+ }
291+
292+ /**
293+ * An SSA definition, which may be `null`, augmented with at basic block which can
294+ * be reached without passing through a `null` check.
295+ */
296+ abstract class PathNode extends TPathNode {
297+ /** Gets the SSA definition. */
298+ abstract Ssa:: Definition getSsaDefinition ( ) ;
299+
300+ /** Gets the basic block that can be reached without passing through a `null` check. */
301+ abstract BasicBlock getBasicBlock ( ) ;
302+
303+ /** Gets another node that can be reached from this node. */
304+ abstract PathNode getASuccessor ( ) ;
305+
306+ /** Gets a textual representation of this node. */
307+ abstract string toString ( ) ;
308+
309+ /** Gets the location of this node. */
310+ abstract Location getLocation ( ) ;
311+ }
312+
313+ private class SourcePathNode extends PathNode , TSourcePathNode {
314+ private Ssa:: Definition def ;
315+ private string msg ;
316+ private Element reason ;
317+ private boolean isNullArgument ;
318+
319+ SourcePathNode ( ) { this = TSourcePathNode ( def , msg , reason , isNullArgument ) }
320+
321+ override Ssa:: Definition getSsaDefinition ( ) { result = def }
322+
323+ override BasicBlock getBasicBlock ( ) {
324+ isNullArgument = false and
325+ result = def .getBasicBlock ( )
326+ }
327+
328+ string getMessage ( ) { result = msg }
329+
330+ Element getReason ( ) { result = reason }
331+
332+ override PathNode getASuccessor ( ) {
333+ succStep ( this , result .getSsaDefinition ( ) , result .getBasicBlock ( ) )
334+ or
335+ succNullArgument ( this , result .getSsaDefinition ( ) , result .getBasicBlock ( ) )
336+ or
337+ result instanceof SinkPathNode and
338+ succSourceSink ( this , result .getSsaDefinition ( ) , result .getBasicBlock ( ) )
339+ }
340+
341+ override string toString ( ) {
342+ if isNullArgument = true then
343+ result = reason .toString ( )
344+ else
345+ result = def .toString ( )
346+ }
347+
348+ override Location getLocation ( ) {
349+ if isNullArgument = true then
350+ result = reason .getLocation ( )
351+ else
352+ result = def .getLocation ( )
353+ }
354+ }
355+
356+ private class InternalPathNode extends PathNode , TInternalPathNode {
357+ private Ssa:: Definition def ;
358+ private BasicBlock bb ;
359+
360+ InternalPathNode ( ) { this = TInternalPathNode ( def , bb ) }
361+
362+ override Ssa:: Definition getSsaDefinition ( ) { result = def }
363+
364+ override BasicBlock getBasicBlock ( ) { result = bb }
365+
366+ override PathNode getASuccessor ( ) {
367+ succStep ( this , result .getSsaDefinition ( ) , result .getBasicBlock ( ) )
368+ }
369+
370+ override string toString ( ) { result = bb .getFirstNode ( ) .toString ( ) }
371+
372+ override Location getLocation ( ) { result = bb .getFirstNode ( ) .getLocation ( ) }
373+ }
374+
375+ private class SinkPathNode extends PathNode , TSinkPathNode {
376+ private Ssa:: Definition def ;
377+ private BasicBlock bb ;
378+ private int i ;
379+ private Dereference d ;
380+
381+ SinkPathNode ( ) { this = TSinkPathNode ( def , bb , i , d ) }
382+
383+ override Ssa:: Definition getSsaDefinition ( ) { result = def }
384+
385+ override BasicBlock getBasicBlock ( ) { result = bb }
386+
387+ override PathNode getASuccessor ( ) { none ( ) }
388+
389+ Dereference getDereference ( ) { result = d }
390+
391+ override string toString ( ) { result = d .toString ( ) }
392+
393+ override Location getLocation ( ) { result = d .getLocation ( ) }
394+ }
395+
396+ /**
397+ * Provides the query predicates needed to include a graph in a path-problem query
398+ * for `Dereference::is[First]MaybeNull()`.
399+ */
400+ module PathGraph {
401+ query predicate nodes ( PathNode n ) {
402+ n .getASuccessor * ( ) instanceof SinkPathNode
403+ }
404+
405+ query predicate edges ( PathNode pred , PathNode succ ) {
406+ nodes ( pred ) and
407+ nodes ( succ ) and
408+ succ = pred .getASuccessor ( )
409+ }
259410}
260411
261412private Ssa:: Definition getAPseudoInput ( Ssa:: Definition def ) {
@@ -282,7 +433,7 @@ private predicate defReaches(Ssa::Definition def, AssignableRead ar, boolean alw
282433 defReaches ( def , mid , always ) |
283434 ar = mid .getANextRead ( ) and
284435 not mid = any ( Dereference d |
285- if always = true then d .isAlwaysNull ( def .getSourceVariable ( ) ) else d .isMaybeNull ( def , _, _)
436+ if always = true then d .isAlwaysNull ( def .getSourceVariable ( ) ) else d .isMaybeNull ( def , _, _, _ , _ )
286437 )
287438 )
288439}
@@ -378,24 +529,16 @@ class Dereference extends G::DereferenceableExpr {
378529 defReaches ( v .getAnSsaDefinition ( ) , this , true )
379530 }
380531
381- pragma [ noinline]
382- private predicate nullDerefCandidate ( Ssa:: Definition origin ) {
383- exists ( Ssa:: Definition ssa , BasicBlock bb |
384- potentialNullDereferenceAt ( bb , _, ssa , this ) |
385- defMaybeNullInBlockOrigin ( origin , ssa , bb )
386- )
387- }
388-
389532 /**
390533 * Holds if this expression dereferences SSA definition `def`, which may
391534 * be `null`.
392535 */
393- predicate isMaybeNull ( Ssa:: Definition def , string msg , Element reason ) {
394- exists ( Ssa :: Definition origin , BasicBlock bb |
395- this . nullDerefCandidate ( origin ) and
396- defMaybeNull ( origin , msg , reason ) and
397- potentialNullDereferenceAt ( bb , _ , def , this )
398- ) and
536+ predicate isMaybeNull ( Ssa:: Definition def , SourcePathNode source , SinkPathNode sink , string msg , Element reason ) {
537+ source . getASuccessor * ( ) = sink and
538+ msg = source . getMessage ( ) and
539+ reason = source . getReason ( ) and
540+ def = sink . getSsaDefinition ( ) and
541+ this = sink . getDereference ( ) and
399542 not this .isAlwaysNull ( def .getSourceVariable ( ) )
400543 }
401544
@@ -404,8 +547,8 @@ class Dereference extends G::DereferenceableExpr {
404547 * be `null`, and this expression can be reached from `def` without passing
405548 * through another such dereference.
406549 */
407- predicate isFirstMaybeNull ( Ssa:: Definition def , string msg , Element reason ) {
408- this .isMaybeNull ( def , msg , reason ) and
550+ predicate isFirstMaybeNull ( Ssa:: Definition def , SourcePathNode source , SinkPathNode sink , string msg , Element reason ) {
551+ this .isMaybeNull ( def , source , sink , msg , reason ) and
409552 defReaches ( def , this , false )
410553 }
411554}
0 commit comments