mirror of
https://github.com/github/codeql.git
synced 2025-12-24 12:46:34 +01:00
365 lines
11 KiB
Plaintext
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()
|
|
}
|