Python: Remove points-to to from ControlFlowNode

Moves the existing points-to predicates to the newly added class
`ControlFlowNodeWithPointsTo` which resides in the `LegacyPointsTo`
module.

(Existing code that uses these predicates should import this module, and
references to `ControlFlowNode` should be changed to
`ControlFlowNodeWithPointsTo`.)

Also updates all existing points-to based code to do just this.
This commit is contained in:
Taus
2025-10-29 22:05:43 +00:00
parent 4461be180a
commit fef08afff9
75 changed files with 410 additions and 236 deletions

View File

@@ -0,0 +1,121 @@
/**
* DEPRECATED: Using the methods in this module may lead to a degradation of performance. Use at
* your own peril.
*
* This module contains legacy points-to predicates and methods for various classes in the
* points-to analysis.
*
* Existing code that depends on, say, points-to predicates on `ControlFlowNode` should be modified
* to use `ControlFlowNodeWithPointsTo` instead. In particular, if inside a method call chain such
* as
*
* `someCallNode.getFunction().pointsTo(...)`
*
* an explicit cast should be added as follows
*
* `someCallNode.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(...)`
*
* Similarly, if a bound variable has type `ControlFlowNode`, and a points-to method is called on
* it, the type should be changed to `ControlFlowNodeWithPointsTo`.
*/
private import python
private import semmle.python.pointsto.PointsTo
/**
* An extension of `ControlFlowNode` that provides points-to predicates.
*/
class ControlFlowNodeWithPointsTo extends ControlFlowNode {
/** Gets the value that this ControlFlowNode points-to. */
predicate pointsTo(Value value) { this.pointsTo(_, value, _) }
/** Gets the value that this ControlFlowNode points-to. */
Value pointsTo() { this.pointsTo(_, result, _) }
/** Gets a value that this ControlFlowNode may points-to. */
Value inferredValue() { this.pointsTo(_, result, _) }
/** Gets the value and origin that this ControlFlowNode points-to. */
predicate pointsTo(Value value, ControlFlowNode origin) { this.pointsTo(_, value, origin) }
/** Gets the value and origin that this ControlFlowNode points-to, given the context. */
predicate pointsTo(Context context, Value value, ControlFlowNode origin) {
PointsTo::pointsTo(this, context, value, origin)
}
/**
* Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to
* analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly
* precise, but may not provide information for a significant number of flow-nodes.
* If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead.
*/
pragma[nomagic]
predicate refersTo(Object obj, ClassObject cls, ControlFlowNode origin) {
this.refersTo(_, obj, cls, origin)
}
/** Gets what this expression might "refer-to" in the given `context`. */
pragma[nomagic]
predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) {
not obj = unknownValue() and
not cls = theUnknownType() and
PointsTo::points_to(this, context, obj, cls, origin)
}
/**
* Whether this flow node might "refer-to" to `value` which is from `origin`
* Unlike `this.refersTo(value, _, origin)` this predicate includes results
* where the class cannot be inferred.
*/
pragma[nomagic]
predicate refersTo(Object obj, ControlFlowNode origin) {
not obj = unknownValue() and
PointsTo::points_to(this, _, obj, _, origin)
}
/** Equivalent to `this.refersTo(value, _)` */
predicate refersTo(Object obj) { this.refersTo(obj, _) }
/**
* Check whether this control-flow node has complete points-to information.
* This would mean that the analysis managed to infer an over approximation
* of possible values at runtime.
*/
predicate hasCompletePointsToSet() {
// If the tracking failed, then `this` will be its own "origin". In that
// case, we want to exclude nodes for which there is also a different
// origin, as that would indicate that some paths failed and some did not.
this.refersTo(_, _, this) and
not exists(ControlFlowNode other | other != this and this.refersTo(_, _, other))
or
// If `this` is a use of a variable, then we must have complete points-to
// for that variable.
exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v))
}
}
/**
* Check whether a SSA variable has complete points-to information.
* This would mean that the analysis managed to infer an overapproximation
* of possible values at runtime.
*/
private predicate varHasCompletePointsToSet(SsaVariable var) {
// Global variables may be modified non-locally or concurrently.
not var.getVariable() instanceof GlobalVariable and
(
// If we have complete points-to information on the definition of
// this variable, then the variable has complete information.
var.getDefinition()
.(DefinitionNode)
.getValue()
.(ControlFlowNodeWithPointsTo)
.hasCompletePointsToSet()
or
// If this variable is a phi output, then we have complete
// points-to information about it if all phi inputs had complete
// information.
forex(SsaVariable phiInput | phiInput = var.getAPhiInput() |
varHasCompletePointsToSet(phiInput)
)
)
}

View File

@@ -3,6 +3,7 @@
*/
import python
private import LegacyPointsTo
import semmle.python.pointsto.PointsTo
import IDEContextual
@@ -36,22 +37,22 @@ private predicate jump_to_defn(ControlFlowNode use, Definition defn) {
)
or
exists(PythonModuleObject mod |
use.(ImportExprNode).refersTo(mod) and
use.(ImportExprNode).(ControlFlowNodeWithPointsTo).refersTo(mod) and
defn.getAstNode() = mod.getModule()
)
or
exists(PythonModuleObject mod, string name |
use.(ImportMemberNode).getModule(name).refersTo(mod) and
use.(ImportMemberNode).getModule(name).(ControlFlowNodeWithPointsTo).refersTo(mod) and
scope_jump_to_defn_attribute(mod.getModule(), name, defn)
)
or
exists(PackageObject package |
use.(ImportExprNode).refersTo(package) and
use.(ImportExprNode).(ControlFlowNodeWithPointsTo).refersTo(package) and
defn.getAstNode() = package.getInitModule().getModule()
)
or
exists(PackageObject package, string name |
use.(ImportMemberNode).getModule(name).refersTo(package) and
use.(ImportMemberNode).getModule(name).(ControlFlowNodeWithPointsTo).refersTo(package) and
scope_jump_to_defn_attribute(package.getInitModule().getModule(), name, defn)
)
or
@@ -230,7 +231,7 @@ private predicate module_and_name_for_import_star_helper(
ModuleObject mod, string name, ImportStarNode im_star, ImportStarRefinement def
) {
im_star = def.getDefiningNode() and
im_star.getModule().refersTo(mod) and
im_star.getModule().(ControlFlowNodeWithPointsTo).refersTo(mod) and
name = def.getSourceVariable().getName()
}
@@ -239,7 +240,7 @@ pragma[noinline]
private predicate variable_not_redefined_by_import_star(EssaVariable var, ImportStarRefinement def) {
var = def.getInput() and
exists(ModuleObject mod |
def.getDefiningNode().(ImportStarNode).getModule().refersTo(mod) and
def.getDefiningNode().(ImportStarNode).getModule().(ControlFlowNodeWithPointsTo).refersTo(mod) and
not mod.exports(var.getSourceVariable().getName())
)
}
@@ -352,7 +353,9 @@ private predicate scope_jump_to_defn_attribute(ImportTimeScope s, string name, D
)
}
private predicate jump_to_defn_attribute(ControlFlowNode use, string name, Definition defn) {
private predicate jump_to_defn_attribute(
ControlFlowNodeWithPointsTo use, string name, Definition defn
) {
/* Local attribute */
exists(EssaVariable var |
use = var.getASourceUse() and
@@ -367,7 +370,7 @@ private predicate jump_to_defn_attribute(ControlFlowNode use, string name, Defin
/* Super attributes */
exists(AttrNode f, SuperBoundMethod sbm, Object function |
use = f.getObject(name) and
f.refersTo(sbm) and
f.(ControlFlowNodeWithPointsTo).refersTo(sbm) and
function = sbm.getFunction(_) and
function.getOrigin() = defn.getAstNode()
)
@@ -408,7 +411,7 @@ private predicate attribute_assignment_jump_to_defn_attribute(
private predicate sets_attribute(ArgumentRefinement def, string name) {
exists(CallNode call |
call = def.getDefiningNode() and
call.getFunction().refersTo(Object::builtin("setattr")) and
call.getFunction().(ControlFlowNodeWithPointsTo).refersTo(Object::builtin("setattr")) and
def.getInput().getAUse() = call.getArg(0) and
call.getArg(1).getNode().(StringLiteral).getText() = name
)

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
private import semmle.python.pointsto.PointsTo
private import semmle.python.objects.ObjectInternal
private import semmle.python.internal.CachedStages
@@ -71,7 +72,9 @@ class Expr extends Expr_, AstNode {
* Gets what this expression might "refer-to" in the given `context`.
*/
predicate refersTo(Context context, Object obj, ClassObject cls, AstNode origin) {
this.getAFlowNode().refersTo(context, obj, cls, origin.getAFlowNode())
this.getAFlowNode()
.(ControlFlowNodeWithPointsTo)
.refersTo(context, obj, cls, origin.getAFlowNode())
}
/**
@@ -82,7 +85,7 @@ class Expr extends Expr_, AstNode {
*/
pragma[nomagic]
predicate refersTo(Object obj, AstNode origin) {
this.getAFlowNode().refersTo(obj, origin.getAFlowNode())
this.getAFlowNode().(ControlFlowNodeWithPointsTo).refersTo(obj, origin.getAFlowNode())
}
/**
@@ -96,14 +99,16 @@ class Expr extends Expr_, AstNode {
* in the given `context`.
*/
predicate pointsTo(Context context, Value value, AstNode origin) {
this.getAFlowNode().pointsTo(context, value, origin.getAFlowNode())
this.getAFlowNode()
.(ControlFlowNodeWithPointsTo)
.pointsTo(context, value, origin.getAFlowNode())
}
/**
* Holds if this expression might "point-to" to `value` which is from `origin`.
*/
predicate pointsTo(Value value, AstNode origin) {
this.getAFlowNode().pointsTo(value, origin.getAFlowNode())
this.getAFlowNode().(ControlFlowNodeWithPointsTo).pointsTo(value, origin.getAFlowNode())
}
/**

View File

@@ -1,5 +1,4 @@
import python
private import semmle.python.pointsto.PointsTo
private import semmle.python.internal.CachedStages
private import codeql.controlflow.BasicBlock as BB
@@ -144,56 +143,6 @@ class ControlFlowNode extends @py_flow_node {
/** Whether this flow node is the first in its scope */
predicate isEntryNode() { py_scope_flow(this, _, -1) }
/** Gets the value that this ControlFlowNode points-to. */
predicate pointsTo(Value value) { this.pointsTo(_, value, _) }
/** Gets the value that this ControlFlowNode points-to. */
Value pointsTo() { this.pointsTo(_, result, _) }
/** Gets a value that this ControlFlowNode may points-to. */
Value inferredValue() { this.pointsTo(_, result, _) }
/** Gets the value and origin that this ControlFlowNode points-to. */
predicate pointsTo(Value value, ControlFlowNode origin) { this.pointsTo(_, value, origin) }
/** Gets the value and origin that this ControlFlowNode points-to, given the context. */
predicate pointsTo(Context context, Value value, ControlFlowNode origin) {
PointsTo::pointsTo(this, context, value, origin)
}
/**
* Gets what this flow node might "refer-to". Performs a combination of localized (intra-procedural) points-to
* analysis and global module-level analysis. This points-to analysis favours precision over recall. It is highly
* precise, but may not provide information for a significant number of flow-nodes.
* If the class is unimportant then use `refersTo(value)` or `refersTo(value, origin)` instead.
*/
pragma[nomagic]
predicate refersTo(Object obj, ClassObject cls, ControlFlowNode origin) {
this.refersTo(_, obj, cls, origin)
}
/** Gets what this expression might "refer-to" in the given `context`. */
pragma[nomagic]
predicate refersTo(Context context, Object obj, ClassObject cls, ControlFlowNode origin) {
not obj = unknownValue() and
not cls = theUnknownType() and
PointsTo::points_to(this, context, obj, cls, origin)
}
/**
* Whether this flow node might "refer-to" to `value` which is from `origin`
* Unlike `this.refersTo(value, _, origin)` this predicate includes results
* where the class cannot be inferred.
*/
pragma[nomagic]
predicate refersTo(Object obj, ControlFlowNode origin) {
not obj = unknownValue() and
PointsTo::points_to(this, _, obj, _, origin)
}
/** Equivalent to `this.refersTo(value, _)` */
predicate refersTo(Object obj) { this.refersTo(obj, _) }
/** Gets the basic block containing this flow node */
BasicBlock getBasicBlock() { result.contains(this) }
@@ -259,23 +208,6 @@ class ControlFlowNode extends @py_flow_node {
)
}
/**
* Check whether this control-flow node has complete points-to information.
* This would mean that the analysis managed to infer an over approximation
* of possible values at runtime.
*/
predicate hasCompletePointsToSet() {
// If the tracking failed, then `this` will be its own "origin". In that
// case, we want to exclude nodes for which there is also a different
// origin, as that would indicate that some paths failed and some did not.
this.refersTo(_, _, this) and
not exists(ControlFlowNode other | other != this and this.refersTo(_, _, other))
or
// If `this` is a use of a variable, then we must have complete points-to
// for that variable.
exists(SsaVariable v | v.getAUse() = this | varHasCompletePointsToSet(v))
}
/** Whether this strictly dominates other. */
pragma[inline]
predicate strictlyDominates(ControlFlowNode other) {
@@ -332,28 +264,6 @@ private class AnyNode extends ControlFlowNode {
override AstNode getNode() { result = super.getNode() }
}
/**
* Check whether a SSA variable has complete points-to information.
* This would mean that the analysis managed to infer an overapproximation
* of possible values at runtime.
*/
private predicate varHasCompletePointsToSet(SsaVariable var) {
// Global variables may be modified non-locally or concurrently.
not var.getVariable() instanceof GlobalVariable and
(
// If we have complete points-to information on the definition of
// this variable, then the variable has complete information.
var.getDefinition().(DefinitionNode).getValue().hasCompletePointsToSet()
or
// If this variable is a phi output, then we have complete
// points-to information about it if all phi inputs had complete
// information.
forex(SsaVariable phiInput | phiInput = var.getAPhiInput() |
varHasCompletePointsToSet(phiInput)
)
)
}
/** A control flow node corresponding to a call expression, such as `func(...)` */
class CallNode extends ControlFlowNode {
CallNode() { toAst(this) instanceof Call }

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
/** The metrics for a function */
class FunctionMetrics extends Function {
@@ -59,7 +60,7 @@ class FunctionMetrics extends Function {
not non_coupling_method(result) and
exists(Call call | call.getScope() = this |
exists(FunctionObject callee | callee.getFunction() = result |
call.getAFlowNode().getFunction().refersTo(callee)
call.getAFlowNode().getFunction().(ControlFlowNodeWithPointsTo).refersTo(callee)
)
or
exists(Attribute a | call.getFunc() = a |

View File

@@ -9,6 +9,7 @@
*/
private import python
private import LegacyPointsTo
/** A control flow node which might correspond to a special method call. */
class PotentialSpecialMethodCallNode extends ControlFlowNode instanceof SpecialMethod::Potential { }
@@ -106,7 +107,11 @@ class SpecialMethodCallNode extends PotentialSpecialMethodCallNode {
SpecialMethodCallNode() {
exists(SpecialMethod::Potential pot |
this = pot and
pot.getSelf().pointsTo().getClass().lookup(pot.getSpecialMethodName()) = resolvedSpecialMethod
pot.getSelf()
.(ControlFlowNodeWithPointsTo)
.pointsTo()
.getClass()
.lookup(pot.getSpecialMethodName()) = resolvedSpecialMethod
)
}

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
import semmle.python.dataflow.TaintTracking
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.Filters as Filters
@@ -374,7 +375,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
exists(ModuleValue m, string name |
src = TTaintTrackingNode_(_, context, path, kind, this) and
this.moduleAttributeTainted(m, name, src) and
node.asCfgNode().(ImportMemberNode).getModule(name).pointsTo(m)
node.asCfgNode().(ImportMemberNode).getModule(name).(ControlFlowNodeWithPointsTo).pointsTo(m)
)
}
@@ -408,7 +409,9 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
src = TTaintTrackingNode_(srcnode, context, srcpath, srckind, this) and
exists(CallNode call, ControlFlowNode arg |
call = node.asCfgNode() and
call.getFunction().pointsTo(ObjectInternal::builtin("getattr")) and
call.getFunction()
.(ControlFlowNodeWithPointsTo)
.pointsTo(ObjectInternal::builtin("getattr")) and
arg = call.getArg(0) and
attrname = call.getArg(1).getNode().(StringLiteral).getText() and
arg = srcnode.asCfgNode()
@@ -515,7 +518,7 @@ class TaintTrackingImplementation extends string instanceof TaintTracking::Confi
TaintTrackingContext caller, TaintTrackingContext callee
) {
exists(ClassValue cls |
call.getFunction().pointsTo(cls) and
call.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(cls) and
cls.lookup("__init__") = init
|
exists(int arg, TaintKind callerKind, AttributePath callerPath, DataFlow::Node argument |
@@ -878,7 +881,7 @@ private class EssaTaintTracking extends string instanceof TaintTracking::Configu
const.getNode() instanceof ImmutableLiteral
)
or
exists(ControlFlowNode c, ClassValue cls |
exists(ControlFlowNodeWithPointsTo c, ClassValue cls |
Filters::isinstance(test, c, use) and
c.pointsTo(cls)
|
@@ -978,7 +981,7 @@ module Implementation {
tonode.getArg(0) = fromnode
)
or
tonode.getFunction().pointsTo(ObjectInternal::builtin("reversed")) and
tonode.getFunction().(ControlFlowNodeWithPointsTo).pointsTo(ObjectInternal::builtin("reversed")) and
tonode.getArg(0) = fromnode
}
}

View File

@@ -87,6 +87,7 @@
*/
import python
private import LegacyPointsTo
private import semmle.python.pointsto.Filters as Filters
private import semmle.python.objects.ObjectInternal
private import semmle.python.dataflow.Implementation
@@ -267,7 +268,11 @@ module DictKind {
Implementation::copyCall(fromnode, tonode) and
edgeLabel = "dict copy"
or
tonode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and
tonode
.(CallNode)
.getFunction()
.(ControlFlowNodeWithPointsTo)
.pointsTo(ObjectInternal::builtin("dict")) and
tonode.(CallNode).getArg(0) = fromnode and
edgeLabel = "dict() call"
}
@@ -615,7 +620,7 @@ module DataFlow {
TCfgNode(ControlFlowNode node)
abstract class Node extends TDataFlowNode {
abstract ControlFlowNode asCfgNode();
abstract ControlFlowNodeWithPointsTo asCfgNode();
abstract EssaVariable asVariable();
@@ -632,7 +637,7 @@ module DataFlow {
}
class CfgNode extends Node, TCfgNode {
override ControlFlowNode asCfgNode() { this = TCfgNode(result) }
override ControlFlowNodeWithPointsTo asCfgNode() { this = TCfgNode(result) }
override EssaVariable asVariable() { none() }
@@ -647,7 +652,7 @@ module DataFlow {
}
class EssaNode extends Node, TEssaNode {
override ControlFlowNode asCfgNode() { none() }
override ControlFlowNodeWithPointsTo asCfgNode() { none() }
override EssaVariable asVariable() { this = TEssaNode(result) }
@@ -668,7 +673,11 @@ pragma[noinline]
private predicate dict_construct(ControlFlowNode itemnode, ControlFlowNode dictnode) {
dictnode.(DictNode).getAValue() = itemnode
or
dictnode.(CallNode).getFunction().pointsTo(ObjectInternal::builtin("dict")) and
dictnode
.(CallNode)
.getFunction()
.(ControlFlowNodeWithPointsTo)
.pointsTo(ObjectInternal::builtin("dict")) and
dictnode.(CallNode).getArgByName(_) = itemnode
}
@@ -688,7 +697,7 @@ private predicate sequence_construct(ControlFlowNode itemnode, ControlFlowNode s
pragma[noinline]
private predicate sequence_call(ControlFlowNode fromnode, CallNode tonode) {
tonode.getArg(0) = fromnode and
exists(ControlFlowNode cls | cls = tonode.getFunction() |
exists(ControlFlowNodeWithPointsTo cls | cls = tonode.getFunction() |
cls.pointsTo(ObjectInternal::builtin("list"))
or
cls.pointsTo(ObjectInternal::builtin("tuple"))

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
import semmle.python.dependencies.DependencyKind
private predicate importDependency(Object target, AstNode source) {
@@ -59,7 +60,7 @@ class PythonUse extends DependencyKind {
interesting(target) and
this = this and
source != target.(ControlFlowNode).getNode() and
exists(ControlFlowNode use, Object obj |
exists(ControlFlowNodeWithPointsTo use, Object obj |
use.getNode() = source and
use.refersTo(obj) and
use.isLoad()
@@ -114,12 +115,14 @@ private predicate attribute_access_dependency(Object target, AstNode source) {
private predicate use_of_attribute(Attribute attr, Scope s, string name) {
exists(AttrNode cfg | cfg.isLoad() and cfg.getNode() = attr |
exists(Object obj | cfg.getObject(name).refersTo(obj) |
exists(Object obj | cfg.getObject(name).(ControlFlowNodeWithPointsTo).refersTo(obj) |
s = obj.(PythonModuleObject).getModule() or
s = obj.(ClassObject).getPyClass()
)
or
exists(ClassObject cls | cfg.getObject(name).refersTo(_, cls, _) | s = cls.getPyClass())
exists(ClassObject cls | cfg.getObject(name).(ControlFlowNodeWithPointsTo).refersTo(_, cls, _) |
s = cls.getPyClass()
)
)
or
exists(SelfAttributeRead sar | sar = attr |

View File

@@ -4,6 +4,7 @@
*/
import python
private import LegacyPointsTo
private import TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo
@@ -704,12 +705,14 @@ abstract class FunctionValue extends CallableValue {
abstract ClassValue getARaisedType();
/** Gets a call-site from where this function is called as a function */
CallNode getAFunctionCall() { result.getFunction().pointsTo() = this }
CallNode getAFunctionCall() {
result.getFunction().(ControlFlowNodeWithPointsTo).pointsTo() = this
}
/** Gets a call-site from where this function is called as a method */
CallNode getAMethodCall() {
exists(BoundMethodObjectInternal bm |
result.getFunction().pointsTo() = bm and
result.getFunction().(ControlFlowNodeWithPointsTo).pointsTo() = bm and
bm.getFunction() = this
)
}
@@ -753,7 +756,7 @@ class PythonFunctionValue extends FunctionValue {
* explicit return nodes that we can query and get the class of.
*/
result = this.getAReturnedNode().pointsTo().getClass()
result = this.getAReturnedNode().(ControlFlowNodeWithPointsTo).pointsTo().getClass()
}
}

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
private import semmle.python.objects.Classes
private import semmle.python.objects.Instances
private import semmle.python.pointsto.PointsTo
@@ -193,7 +194,9 @@ class ClassObject extends Object {
* It is guaranteed that getProbableSingletonInstance() returns at most one Object for each ClassObject.
*/
Object getProbableSingletonInstance() {
exists(ControlFlowNode use, Expr origin | use.refersTo(result, this, origin.getAFlowNode()) |
exists(ControlFlowNodeWithPointsTo use, Expr origin |
use.refersTo(result, this, origin.getAFlowNode())
|
this.hasStaticallyUniqueInstance() and
/* Ensure that original expression will be executed only one. */
origin.getScope() instanceof ImportTimeScope and
@@ -351,7 +354,7 @@ class ClassObject extends Object {
* Gets a call to this class. Note that the call may not create a new instance of
* this class, as that depends on the `__new__` method of this class.
*/
CallNode getACall() { result.getFunction().refersTo(this) }
CallNode getACall() { result.getFunction().(ControlFlowNodeWithPointsTo).refersTo(this) }
override predicate notClass() { none() }
}

View File

@@ -12,6 +12,7 @@
*/
import python
private import LegacyPointsTo
/** The subset of ControlFlowNodes which might raise an exception */
class RaisingNode extends ControlFlowNode {
@@ -30,7 +31,9 @@ class RaisingNode extends ControlFlowNode {
)
}
private predicate quits() { this.(CallNode).getFunction().refersTo(Object::quitter(_)) }
private predicate quits() {
this.(CallNode).getFunction().(ControlFlowNodeWithPointsTo).refersTo(Object::quitter(_))
}
/**
* Gets the type of an exception that may be raised
@@ -68,7 +71,7 @@ class RaisingNode extends ControlFlowNode {
private ClassObject localRaisedType_objectapi() {
result.isSubclassOf(theBaseExceptionType()) and
(
exists(ControlFlowNode ex |
exists(ControlFlowNodeWithPointsTo ex |
ex = this.getExceptionNode() and
(ex.refersTo(result) or ex.refersTo(_, result, _))
)
@@ -95,7 +98,7 @@ class RaisingNode extends ControlFlowNode {
private ClassValue localRaisedType() {
result.getASuperType() = ClassValue::baseException() and
(
exists(ControlFlowNode ex |
exists(ControlFlowNodeWithPointsTo ex |
ex = this.getExceptionNode() and
(ex.pointsTo(result) or ex.pointsTo().getClass() = result)
)
@@ -153,7 +156,9 @@ class RaisingNode extends ControlFlowNode {
/* Call to an unknown object */
this.getNode() instanceof Call and
not exists(FunctionObject func | this = func.getACall()) and
not exists(ClassObject known | this.(CallNode).getFunction().refersTo(known))
not exists(ClassObject known |
this.(CallNode).getFunction().(ControlFlowNodeWithPointsTo).refersTo(known)
)
or
this.getNode() instanceof Exec
or
@@ -371,7 +376,7 @@ class ExceptFlowNode extends ControlFlowNode {
* Gets the type handled by this exception handler.
* `ExceptionType` in `except ExceptionType as e:`
*/
ControlFlowNode getType() {
ControlFlowNodeWithPointsTo getType() {
exists(ExceptStmt ex |
this.getBasicBlock().dominates(result.getBasicBlock()) and
ex = this.getNode() and
@@ -470,7 +475,7 @@ class ExceptGroupFlowNode extends ControlFlowNode {
}
}
private ControlFlowNode element_from_tuple_objectapi(Object tuple) {
private ControlFlowNodeWithPointsTo element_from_tuple_objectapi(Object tuple) {
exists(Tuple t | t = tuple.getOrigin() and result = t.getAnElt().getAFlowNode())
}

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
import semmle.python.types.Exceptions
private import semmle.python.pointsto.PointsTo
private import semmle.python.objects.Callables
@@ -32,27 +33,31 @@ abstract class FunctionObject extends Object {
abstract string descriptiveString();
/** Gets a call-site from where this function is called as a function */
CallNode getAFunctionCall() { result.getFunction().inferredValue() = this.theCallable() }
CallNode getAFunctionCall() {
result.getFunction().(ControlFlowNodeWithPointsTo).inferredValue() = this.theCallable()
}
/** Gets a call-site from where this function is called as a method */
CallNode getAMethodCall() {
exists(BoundMethodObjectInternal bm |
result.getFunction().inferredValue() = bm and
result.getFunction().(ControlFlowNodeWithPointsTo).inferredValue() = bm and
bm.getFunction() = this.theCallable()
)
}
/** Gets a call-site from where this function is called */
ControlFlowNode getACall() { result = this.theCallable().getACall() }
ControlFlowNodeWithPointsTo getACall() { result = this.theCallable().getACall() }
/** Gets a call-site from where this function is called, given the `context` */
ControlFlowNode getACall(Context context) { result = this.theCallable().getACall(context) }
ControlFlowNodeWithPointsTo getACall(Context context) {
result = this.theCallable().getACall(context)
}
/**
* Gets the `ControlFlowNode` that will be passed as the nth argument to `this` when called at `call`.
* This predicate will correctly handle `x.y()`, treating `x` as the zeroth argument.
*/
ControlFlowNode getArgumentForCall(CallNode call, int n) {
ControlFlowNodeWithPointsTo getArgumentForCall(CallNode call, int n) {
result = this.theCallable().getArgumentForCall(call, n)
}
@@ -60,7 +65,7 @@ abstract class FunctionObject extends Object {
* Gets the `ControlFlowNode` that will be passed as the named argument to `this` when called at `call`.
* This predicate will correctly handle `x.y()`, treating `x` as the self argument.
*/
ControlFlowNode getNamedArgumentForCall(CallNode call, string name) {
ControlFlowNodeWithPointsTo getNamedArgumentForCall(CallNode call, string name) {
result = this.theCallable().getNamedArgumentForCall(call, name)
}
@@ -134,7 +139,9 @@ class PyFunctionObject extends FunctionObject {
override predicate raisesUnknownType() { scope_raises_unknown(this.getFunction()) }
/** Gets a control flow node corresponding to the value of a return statement */
ControlFlowNode getAReturnedNode() { result = this.getFunction().getAReturnValueFlowNode() }
ControlFlowNodeWithPointsTo getAReturnedNode() {
result = this.getFunction().getAReturnValueFlowNode()
}
override string descriptiveString() {
if this.getFunction().isMethod()
@@ -216,7 +223,7 @@ abstract class BuiltinCallable extends FunctionObject {
abstract override string getQualifiedName();
override ControlFlowNode getArgumentForCall(CallNode call, int n) {
override ControlFlowNodeWithPointsTo getArgumentForCall(CallNode call, int n) {
call = this.getACall() and result = call.getArg(n)
}
}

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
private import semmle.python.objects.ObjectInternal
private import semmle.python.types.Builtins
private import semmle.python.internal.CachedStages
@@ -41,7 +42,7 @@ class Object extends @py_object {
* for a control flow node 'f'
*/
ClassObject getAnInferredType() {
exists(ControlFlowNode somewhere | somewhere.refersTo(this, result, _))
exists(ControlFlowNodeWithPointsTo somewhere | somewhere.refersTo(this, result, _))
or
this.asBuiltin().getClass() = result.asBuiltin() and not this = unknownValue()
or
@@ -327,7 +328,7 @@ abstract class SequenceObject extends Object {
Object getInferredElement(int n) {
result = this.getBuiltinElement(n)
or
this.getSourceElement(n).refersTo(result)
this.getSourceElement(n).(ControlFlowNodeWithPointsTo).refersTo(result)
}
}
@@ -438,7 +439,8 @@ class SuperBoundMethod extends Object {
string name;
SuperBoundMethod() {
this.(AttrNode).getObject(name).inferredValue().getClass() = Value::named("super")
this.(AttrNode).getObject(name).(ControlFlowNodeWithPointsTo).inferredValue().getClass() =
Value::named("super")
}
override string toString() { result = "super()." + name }
@@ -446,7 +448,7 @@ class SuperBoundMethod extends Object {
Object getFunction(string fname) {
fname = name and
exists(SuperInstance sup, BoundMethodObjectInternal m |
sup = this.(AttrNode).getObject(name).inferredValue() and
sup = this.(AttrNode).getObject(name).(ControlFlowNodeWithPointsTo).inferredValue() and
sup.attribute(name, m, _) and
result = m.getFunction().getSource()
)

View File

@@ -1,4 +1,5 @@
import python
private import LegacyPointsTo
/**
* A Python property:
@@ -77,32 +78,32 @@ class BuiltinPropertyObject extends PropertyObject {
}
private predicate property_getter(CallNode decorated, FunctionObject getter) {
decorated.getFunction().refersTo(thePropertyType()) and
decorated.getArg(0).refersTo(getter)
decorated.getFunction().(ControlFlowNodeWithPointsTo).refersTo(thePropertyType()) and
decorated.getArg(0).(ControlFlowNodeWithPointsTo).refersTo(getter)
}
private predicate property_setter(CallNode decorated, FunctionObject setter) {
property_getter(decorated, _) and
exists(CallNode setter_call, AttrNode prop_setter |
prop_setter.getObject("setter").refersTo(decorated)
prop_setter.getObject("setter").(ControlFlowNodeWithPointsTo).refersTo(decorated)
|
setter_call.getArg(0).refersTo(setter) and
setter_call.getArg(0).(ControlFlowNodeWithPointsTo).refersTo(setter) and
setter_call.getFunction() = prop_setter
)
or
decorated.getFunction().refersTo(thePropertyType()) and
decorated.getArg(1).refersTo(setter)
decorated.getFunction().(ControlFlowNodeWithPointsTo).refersTo(thePropertyType()) and
decorated.getArg(1).(ControlFlowNodeWithPointsTo).refersTo(setter)
}
private predicate property_deleter(CallNode decorated, FunctionObject deleter) {
property_getter(decorated, _) and
exists(CallNode deleter_call, AttrNode prop_deleter |
prop_deleter.getObject("deleter").refersTo(decorated)
prop_deleter.getObject("deleter").(ControlFlowNodeWithPointsTo).refersTo(decorated)
|
deleter_call.getArg(0).refersTo(deleter) and
deleter_call.getArg(0).(ControlFlowNodeWithPointsTo).refersTo(deleter) and
deleter_call.getFunction() = prop_deleter
)
or
decorated.getFunction().refersTo(thePropertyType()) and
decorated.getArg(2).refersTo(deleter)
decorated.getFunction().(ControlFlowNodeWithPointsTo).refersTo(thePropertyType()) and
decorated.getArg(2).(ControlFlowNodeWithPointsTo).refersTo(deleter)
}

View File

@@ -1,6 +1,10 @@
import python
private import LegacyPointsTo
private import semmle.python.types.Object
private import semmle.python.types.ClassObject
private import semmle.python.types.FunctionObject
predicate string_attribute_all(ControlFlowNode n, string attr) {
predicate string_attribute_all(ControlFlowNodeWithPointsTo n, string attr) {
(n.getNode() instanceof Unicode or n.getNode() instanceof Bytes) and
attr = "const"
or
@@ -19,18 +23,27 @@ predicate tracked_object(ControlFlowNode obj, string attr) {
tracked_object_any(obj, attr)
}
predicate open_file(Object obj) { obj.(CallNode).getFunction().refersTo(Object::builtin("open")) }
predicate open_file(Object obj) {
obj.(CallNode).getFunction().(ControlFlowNodeWithPointsTo).refersTo(Object::builtin("open"))
}
predicate string_attribute_any(ControlFlowNode n, string attr) {
predicate string_attribute_any(ControlFlowNodeWithPointsTo n, string attr) {
attr = "user-input" and
exists(Object input | n.(CallNode).getFunction().refersTo(input) |
exists(Object input | n.(CallNode).getFunction().(ControlFlowNodeWithPointsTo).refersTo(input) |
if major_version() = 2
then input = Object::builtin("raw_input")
else input = Object::builtin("input")
)
or
attr = "file-input" and
exists(Object fd | n.(CallNode).getFunction().(AttrNode).getObject("read").refersTo(fd) |
exists(Object fd |
n.(CallNode)
.getFunction()
.(AttrNode)
.getObject("read")
.(ControlFlowNodeWithPointsTo)
.refersTo(fd)
|
open_file(fd)
)
or
@@ -65,7 +78,7 @@ ControlFlowNode sequence_for_iterator(ControlFlowNode f) {
}
pragma[noinline]
private predicate tracking_step(ControlFlowNode src, ControlFlowNode dest) {
private predicate tracking_step(ControlFlowNode src, ControlFlowNodeWithPointsTo dest) {
src = dest.(BinaryExprNode).getAnOperand()
or
src = dest.(UnaryExprNode).getOperand()