Skip to content

Commit af7ac40

Browse files
committed
Python: Model undefinedness
Adds `maybeUndefined` to the reachability module, modelling which names/variables may be undefined at runtime. The approach is very close to the one used in points-to, though it of course relies on our new modelling of exceptions/reachability instead.
1 parent 9e2ac8b commit af7ac40

File tree

1 file changed

+60
-0
lines changed

1 file changed

+60
-0
lines changed

python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,4 +2504,64 @@ module Reachability {
25042504
)
25052505
)
25062506
}
2507+
2508+
/**
2509+
* Holds if `var` is an SSA variable that is implicitly defined (a builtin,
2510+
* VM-defined name, or `__path__` in a package init).
2511+
*/
2512+
private predicate implicitlyDefined(SsaVariable var) {
2513+
not exists(var.getDefinition()) and
2514+
not py_ssa_phi(var, _) and
2515+
exists(GlobalVariable gv | var.getVariable() = gv |
2516+
DuckTyping::globallyDefinedName(gv.getId())
2517+
or
2518+
gv.getId() = "__path__" and gv.getScope().(Module).isPackageInit()
2519+
)
2520+
}
2521+
2522+
/**
2523+
* Gets a phi input of `var`, pruned of unlikely edges.
2524+
*/
2525+
private SsaVariable getAPrunedPhiInput(SsaVariable var) {
2526+
result = var.getAPhiInput() and
2527+
exists(BasicBlock incoming | incoming = var.getPredecessorBlockForPhiArgument(result) |
2528+
not unlikelySuccessor(incoming.getLastNode(), var.getDefinition().getBasicBlock().firstNode())
2529+
)
2530+
}
2531+
2532+
/**
2533+
* Gets a predecessor block for a phi node, pruned of unlikely edges.
2534+
*/
2535+
private BasicBlock getAPrunedPredecessorBlockForPhi(SsaVariable var) {
2536+
result = var.getAPredecessorBlockForPhi() and
2537+
not unlikelySuccessor(result.getLastNode(), var.getDefinition().getBasicBlock().firstNode())
2538+
}
2539+
2540+
/**
2541+
* Holds if the SSA variable `var` may be undefined at some use.
2542+
*/
2543+
private predicate ssaMaybeUndefined(SsaVariable var) {
2544+
// No definition, not a phi, not implicitly defined
2545+
not exists(var.getDefinition()) and not py_ssa_phi(var, _) and not implicitlyDefined(var)
2546+
or
2547+
// Defined by a deletion
2548+
var.getDefinition().isDelete()
2549+
or
2550+
// A phi input may be undefined
2551+
exists(SsaVariable input | input = getAPrunedPhiInput(var) | ssaMaybeUndefined(input))
2552+
or
2553+
// A phi predecessor has no dominating definition
2554+
exists(BasicBlock incoming |
2555+
likelyReachable(incoming) and
2556+
incoming = getAPrunedPredecessorBlockForPhi(var) and
2557+
not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
2558+
)
2559+
}
2560+
2561+
/**
2562+
* Holds if the name `u` may be undefined at its use.
2563+
*/
2564+
predicate maybeUndefined(Name u) {
2565+
exists(SsaVariable var | var.getAUse().getNode() = u | ssaMaybeUndefined(var))
2566+
}
25072567
}

0 commit comments

Comments
 (0)