Files
codeql/python/ql/lib/semmle/python/essa/Definitions.qll

365 lines
11 KiB
Plaintext

import python
/*
* Classification of variables. These should be non-overlapping and complete.
*
* Function local variables - Non escaping variables in a function, except 'self'
* Self variables - The 'self' variable for a method.
* Class local variables - Local variables declared in a class
* Non-local variables - Escaping variables in a function
* Built-in variables - Global variables with no definition
* Non-escaping globals -- Global variables that have definitions and all of those definitions are in the module scope
* Escaping globals -- Global variables that have definitions and at least one of those definitions is in another scope.
*/
/** A source language variable, to be converted into a set of SSA variables. */
abstract class SsaSourceVariable extends @py_variable {
SsaSourceVariable() {
/* Exclude `True`, `False` and `None` */
not this.(Variable).getALoad() instanceof NameConstant
}
/** Gets the name of this variable */
string getName() { variable(this, _, result) }
Scope getScope() { variable(this, result, _) }
/** Gets an implicit use of this variable */
abstract ControlFlowNode getAnImplicitUse();
abstract ControlFlowNode getScopeEntryDefinition();
/** Gets a textual representation of this element. */
string toString() { result = "SsaSourceVariable " + this.getName() }
/** Gets a use of this variable, either explicit or implicit. */
ControlFlowNode getAUse() {
result = this.getASourceUse()
or
result = this.getAnImplicitUse()
or
/*
* `import *` is a definition of *all* variables, so must be a use as well, for pass-through
* once we have established that a variable is not redefined.
*/
SsaSource::import_star_refinement(this, result, _)
or
/*
* Add a use at the end of scope for all variables to keep them live
* This is necessary for taint-tracking.
*/
result = this.getScope().getANormalExit()
}
/** Holds if `def` defines an ESSA variable for this variable. */
predicate hasDefiningNode(ControlFlowNode def) {
def = this.getScopeEntryDefinition()
or
SsaSource::assignment_definition(this, def, _)
or
SsaSource::multi_assignment_definition(this, def, _, _)
or
SsaSource::deletion_definition(this, def)
or
SsaSource::init_module_submodule_defn(this, def)
or
SsaSource::parameter_definition(this, def)
or
SsaSource::exception_capture(this, def)
or
SsaSource::exception_group_capture(this, def)
or
SsaSource::with_definition(this, def)
or
SsaSource::pattern_capture_definition(this, def)
or
SsaSource::pattern_alias_definition(this, def)
}
/**
* Holds if `def` defines an ESSA variable for this variable in such a way
* that the new variable is a refinement in some way of the variable used at `use`.
*/
predicate hasRefinement(ControlFlowNode use, ControlFlowNode def) {
this.hasDefiningNode(_) and
/* Can't have a refinement unless there is a definition */
refinement(this, use, def)
}
/**
* Holds if the edge `pred`->`succ` defines an ESSA variable for this variable in such a way
* that the new variable is a refinement in some way of the variable used at `use`.
*/
predicate hasRefinementEdge(ControlFlowNode use, BasicBlock pred, BasicBlock succ) {
test_contains(pred.getLastNode(), use) and
use.(NameNode).uses(this) and
(pred.getAFalseSuccessor() = succ or pred.getATrueSuccessor() = succ) and
/* There is a store to this variable -- We don't want to refine builtins */
exists(this.(Variable).getAStore())
}
/** Gets a use of this variable that corresponds to an explicit use in the source. */
ControlFlowNode getASourceUse() {
result.(NameNode).uses(this)
or
result.(NameNode).deletes(this)
}
abstract CallNode redefinedAtCallSite();
}
private predicate refinement(SsaSourceVariable v, ControlFlowNode use, ControlFlowNode def) {
SsaSource::import_star_refinement(v, use, def)
or
SsaSource::attribute_assignment_refinement(v, use, def)
or
SsaSource::argument_refinement(v, use, def)
or
SsaSource::attribute_deletion_refinement(v, use, def)
or
SsaSource::test_refinement(v, use, def)
or
SsaSource::method_call_refinement(v, use, def)
or
def = v.redefinedAtCallSite() and def = use
}
class FunctionLocalVariable extends SsaSourceVariable {
FunctionLocalVariable() {
this.(LocalVariable).getScope() instanceof Function and
not this instanceof NonLocalVariable
}
override ControlFlowNode getAnImplicitUse() {
this.(Variable).isSelf() and this.(Variable).getScope().getANormalExit() = result
}
override ControlFlowNode getScopeEntryDefinition() {
exists(Scope s | s.getEntryNode() = result |
s = this.(LocalVariable).getScope() and
not this.(LocalVariable).isParameter()
or
s != this.(LocalVariable).getScope() and
s = this.(LocalVariable).getALoad().getScope()
)
}
override CallNode redefinedAtCallSite() { none() }
}
class NonLocalVariable extends SsaSourceVariable {
NonLocalVariable() {
exists(Function f |
this.(LocalVariable).getScope() = f and
this.(LocalVariable).getAStore().getScope() != f
)
}
override ControlFlowNode getAnImplicitUse() {
result.(CallNode).getScope().getScope*() = this.scope_as_local_variable()
}
override ControlFlowNode getScopeEntryDefinition() {
exists(Function f |
f.getScope+() = this.scope_as_local_variable() and
f.getEntryNode() = result
)
or
not this.(LocalVariable).isParameter() and
this.scope_as_local_variable().getEntryNode() = result
}
pragma[noinline]
Scope scope_as_local_variable() { result = this.(LocalVariable).getScope() }
override CallNode redefinedAtCallSite() {
result.getScope().getScope*() = this.scope_as_local_variable()
}
}
class ClassLocalVariable extends SsaSourceVariable {
ClassLocalVariable() { this.(LocalVariable).getScope() instanceof Class }
override ControlFlowNode getAnImplicitUse() { none() }
override ControlFlowNode getScopeEntryDefinition() {
result = this.(LocalVariable).getScope().getEntryNode()
}
override CallNode redefinedAtCallSite() { none() }
}
class BuiltinVariable extends SsaSourceVariable {
BuiltinVariable() {
this instanceof GlobalVariable and
not exists(this.(Variable).getAStore()) and
not this.(Variable).getId() = "__name__" and
not this.(Variable).getId() = "__package__" and
not exists(ImportStar is | is.getScope() = this.(Variable).getScope())
}
override ControlFlowNode getAnImplicitUse() { none() }
override ControlFlowNode getScopeEntryDefinition() { none() }
override CallNode redefinedAtCallSite() { none() }
}
class ModuleVariable extends SsaSourceVariable instanceof GlobalVariable {
ModuleVariable() {
exists(this.(Variable).getAStore())
or
this.(Variable).getId() = "__name__"
or
this.(Variable).getId() = "__package__"
or
exists(ImportStar is | is.getScope() = this.(Variable).getScope())
}
pragma[nomagic]
private Scope scope_as_global_variable() { result = GlobalVariable.super.getScope() }
pragma[noinline]
CallNode global_variable_callnode() { result.getScope() = this.scope_as_global_variable() }
pragma[noinline]
ImportMemberNode global_variable_import() {
result.getScope() = this.scope_as_global_variable() and
import_from_dot_in_init(result.getModule(this.getName()))
}
override ControlFlowNode getAnImplicitUse() {
result = this.global_variable_callnode()
or
result = this.global_variable_import()
or
exists(ImportTimeScope scope | scope.entryEdge(result, _) |
this = scope.getOuterVariable(_) or
this.(Variable).getAUse().getScope() = scope
)
or
/* For implicit use of __metaclass__ when constructing class */
exists(Class c |
class_with_global_metaclass(c, this) and
c.(ImportTimeScope).entryEdge(result, _)
)
or
exists(ImportTimeScope s |
result = s.getANormalExit() and
this.(Variable).getScope() = s and
implicit_definition(this)
)
}
override ControlFlowNode getScopeEntryDefinition() {
exists(Scope s | s.getEntryNode() = result |
/* Module entry point */
this.scope_as_global_variable() = s
or
/* For implicit use of __metaclass__ when constructing class */
class_with_global_metaclass(s, this)
or
/* Variable is used in scope */
GlobalVariable.super.getAUse().getScope() = s
)
or
exists(ImportTimeScope scope | scope.entryEdge(_, result) |
this = scope.getOuterVariable(_) or
this.(Variable).getAUse().getScope() = scope
)
}
override CallNode redefinedAtCallSite() { none() }
}
class NonEscapingGlobalVariable extends ModuleVariable {
NonEscapingGlobalVariable() {
this instanceof GlobalVariable and
exists(this.(Variable).getAStore()) and
not variable_or_attribute_defined_out_of_scope(this)
}
}
class EscapingGlobalVariable extends ModuleVariable {
EscapingGlobalVariable() {
this instanceof GlobalVariable and
exists(this.(Variable).getAStore()) and
variable_or_attribute_defined_out_of_scope(this)
}
override ControlFlowNode getAnImplicitUse() {
result = ModuleVariable.super.getAnImplicitUse()
or
result.(CallNode).getScope().getScope+() = this.scope_as_global_variable()
or
result = this.innerScope().getANormalExit()
}
private Scope innerScope() {
result.getScope+() = this.scope_as_global_variable() and
not result instanceof ImportTimeScope
}
override ControlFlowNode getScopeEntryDefinition() {
result = ModuleVariable.super.getScopeEntryDefinition()
or
result = this.innerScope().getEntryNode()
}
pragma[noinline]
Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() }
override CallNode redefinedAtCallSite() {
result.getScope().getScope*() = this.scope_as_global_variable()
}
}
class EscapingAssignmentGlobalVariable extends EscapingGlobalVariable {
EscapingAssignmentGlobalVariable() {
exists(NameNode n | n.defines(this) and not n.getScope() = this.getScope())
}
}
class SpecialSsaSourceVariable extends SsaSourceVariable {
SpecialSsaSourceVariable() { variable(this, _, "*") or variable(this, _, "$") }
override ControlFlowNode getAnImplicitUse() {
exists(ImportTimeScope s | result = s.getANormalExit() and this.getScope() = s)
}
override ControlFlowNode getScopeEntryDefinition() {
/* Module entry point */
this.getScope().getEntryNode() = result
}
pragma[noinline]
Scope scope_as_global_variable() { result = this.(GlobalVariable).getScope() }
override CallNode redefinedAtCallSite() {
result.getScope().getScope*() = this.scope_as_global_variable()
}
}
/** Holds if this variable is implicitly defined */
private predicate implicit_definition(Variable v) {
v.getId() = "*" or
v.getId() = "$" or
exists(ImportStar is | is.getScope() = v.getScope())
}
private predicate variable_or_attribute_defined_out_of_scope(Variable v) {
exists(NameNode n | n.defines(v) and not n.getScope() = v.getScope())
or
exists(AttrNode a |
a.isStore() and a.getObject() = v.getAUse() and not a.getScope() = v.getScope()
)
}
private predicate class_with_global_metaclass(Class cls, GlobalVariable metaclass) {
metaclass.getId() = "__metaclass__" and
major_version() = 2 and
cls.getEnclosingModule() = metaclass.getScope()
}