Python: Add ADTs for ints and strings. Add some global data-flow.

This commit is contained in:
Mark Shannon
2019-03-19 16:26:55 +00:00
parent 051683fadf
commit 5a46df2132
12 changed files with 1452 additions and 71 deletions

View File

@@ -0,0 +1,111 @@
import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.types.Builtins
abstract class ClassObjectInternal extends ObjectInternal {
override boolean booleanValue() {
result = true
}
override predicate maybe() { none() }
override predicate isClass() { any() }
override predicate notClass() { none() }
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should (in most cases) be an instance
none()
}
}
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {
Class getScope() {
exists(ClassDef def |
this = TPythonClassObject(def.getAFlowNode()) and
result = def.getDefinedClass()
)
}
override string toString() {
result = this.getScope().toString()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
exists(DefinitionNode def |
this = TPythonClassObject(def) and
node = def.getValue() and
context.appliesTo(node)
)
}
override ClassDecl getClassDeclaration() {
this = TPythonClassObject(result)
}
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("FunctionType"))
}
override Builtin getBuiltin() {
none()
}
override ControlFlowNode getOrigin() {
this = TPythonClassObject(result)
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
exists(PythonFunctionObjectInternal init |
// TO DO... Lookup init...
none() |
init.getScope() = scope and paramOffset = 1
)
}
}
class BuiltinClassObjectInternal extends ClassObjectInternal, TBuiltinClassObject {
override Builtin getBuiltin() {
this = TBuiltinClassObject(result)
}
override string toString() {
result = "builtin class " + this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override ClassDecl getClassDeclaration() {
this = TBuiltinClassObject(result)
}
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override ControlFlowNode getOrigin() {
none()
}
}

View File

@@ -0,0 +1,256 @@
import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.types.Builtins
abstract class BooleanObjectInternal extends ObjectInternal {
BooleanObjectInternal() {
this = TTrue() or this = TFalse()
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("bool"))
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Builtin getBuiltin() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// Booleans aren't callable
none()
}
override ControlFlowNode getOrigin() {
none()
}
}
class TrueObjectInternal extends BooleanObjectInternal, TTrue {
override string toString() {
result = "True"
}
override boolean booleanValue() {
result = true
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
node.(NameNode).getId() = "True" and context.appliesTo(node)
}
}
class FalseObjectInternal extends BooleanObjectInternal, TFalse {
override string toString() {
result = "False"
}
override boolean booleanValue() {
result = false
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
node.(NameNode).getId() = "False" and context.appliesTo(node)
}
}
class NoneObjectInternal extends ObjectInternal, TNone {
override string toString() {
result = "None"
}
override boolean booleanValue() {
result = false
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("NoneType"))
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
node.(NameNode).getId() = "None" and context.appliesTo(node)
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Builtin getBuiltin() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// None isn't callable
none()
}
override ControlFlowNode getOrigin() {
none()
}
}
class IntObjectInternal extends ObjectInternal, TInt {
override string toString() {
result = this.intValue().toString()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
context.appliesTo(node) and
node.getNode().(IntegerLiteral).getValue() = this.intValue()
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("int"))
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Builtin getBuiltin() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// ints aren't callable
none()
}
override ControlFlowNode getOrigin() {
none()
}
override int intValue() {
this = TInt(result)
}
override boolean booleanValue() {
this.intValue() = 0 and result = false
or
this.intValue() != 0 and result = true
}
}
class StringObjectInternal extends ObjectInternal, TString {
override string toString() {
result = "'" + this.strValue().toString() + "'"
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
context.appliesTo(node) and
node.getNode().(StrConst).getText() = this.strValue()
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("str"))
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Builtin getBuiltin() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// strings aren't callable
none()
}
override ControlFlowNode getOrigin() {
none()
}
override string strValue() {
this = TString(result)
}
override boolean booleanValue() {
this.strValue() = "" and result = false
or
this.strValue() != "" and result = true
}
}

View File

@@ -0,0 +1,219 @@
import python
private import semmle.python.objects.TObject
private import semmle.python.objects.ObjectInternal
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.types.Builtins
abstract class ModuleObjectInternal extends ObjectInternal {
abstract string getName();
abstract Module getSourceModule();
abstract predicate isBuiltin();
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// Modules aren't callable
none()
}
override ControlFlowNode getOrigin() {
result = this.getSourceModule().getEntryNode()
}
}
class BuiltinModuleObjectInternal extends ModuleObjectInternal, TBuiltinModuleObject {
override Builtin getBuiltin() {
this = TBuiltinModuleObject(result)
}
override string toString() {
result = "builtin module " + this.getBuiltin().getName()
}
override string getName() {
result = this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override boolean booleanValue() {
result = true
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Module getSourceModule() {
none()
}
override predicate isBuiltin() {
any()
}
}
class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
override Builtin getBuiltin() {
none()
}
Folder getFolder() {
this = TPackageObject(result)
}
override string toString() {
result = "package " + this.getName()
}
override string getName() {
result = moduleNameFromFile(this.getFolder())
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override boolean booleanValue() {
result = true
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Module getSourceModule() {
result.getFile() = this.getFolder().getFile("__init__.py")
}
PythonModuleObjectInternal getInitModule() {
result = TPythonModule(this.getSourceModule())
}
predicate hasNoInitModule() {
exists(Folder f |
f = this.getFolder() and
not exists(f.getFile("__init__.py"))
)
}
override predicate isBuiltin() {
none()
}
ModuleObjectInternal submodule(string name) {
result.getName() = this.getName() + "." + name
}
override predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
this.getOrigin().getLocation().hasLocationInfo(fp, bl, bc, el, ec)
or
this.hasNoInitModule() and fp = this.getFolder().getAbsolutePath() and
bl = 0 and bc = 0 and el = 0 and ec = 0
}
}
class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
override Builtin getBuiltin() {
none()
}
override string toString() {
result = "package " + this.getName()
}
override string getName() {
result = this.getSourceModule().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override boolean booleanValue() {
result = true
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Module getSourceModule() {
this = TPythonModule(result)
}
PythonModuleObjectInternal getInitModule() {
result = TPythonModule(this.getSourceModule())
}
override predicate isBuiltin() {
none()
}
}

View File

@@ -0,0 +1,518 @@
import python
private import semmle.python.objects.TObject
private import semmle.python.pointsto.PointsTo2
private import semmle.python.pointsto.PointsToContext2
private import semmle.python.types.Builtins
import semmle.python.objects.Modules
import semmle.python.objects.Classes
import semmle.python.objects.Constants
abstract class ObjectInternal extends TObject {
abstract string toString();
/** The boolean value of this object, if it has one */
abstract boolean booleanValue();
/** Holds if this object may be true or false when evaluated as a bool */
abstract predicate maybe();
abstract predicate introduced(ControlFlowNode node, PointsToContext2 context);
/** Gets the class declaration for this object, if it is a declared class. */
abstract ClassDecl getClassDeclaration();
abstract predicate isClass();
abstract predicate notClass();
abstract ObjectInternal getClass();
/** Holds if whatever this "object" represents can be meaningfully analysed for
* truth or false in comparisons. For example, `None` or `int` can be, but `int()`
* or an unknown string cannot.
*/
abstract predicate isComparable();
/** The negation of `isComparable()` */
abstract predicate notComparable();
/** Gets the `Builtin` for this object, if any.
* All objects (except unknown and undefined values) should return
* exactly one result for either this method or `getOrigin()`.
*/
abstract Builtin getBuiltin();
/** Gets a control flow node that represents the source origin of this
* objects.
* All objects (except unknown and undefined values) should return
* exactly one result for either this method or `getBuiltin()`.
*/
abstract ControlFlowNode getOrigin();
/** Holds if `obj` is the result of calling `this` and `origin` is
* the origin of `obj`.
*/
abstract predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin);
predicate hasLocationInfo(string fp, int bl, int bc, int el, int ec) {
this.getOrigin().getLocation().hasLocationInfo(fp, bl, bc, el, ec)
}
/** The integer value of things that have integer values.
* That is, ints and bools.
*/
int intValue() {
none()
}
/** The integer value of things that have integer values.
* That is, strings.
*/
string strValue() {
none()
}
predicate calleeAndOffset(Function scope, int paramOffset) { none() }
}
class PythonFunctionObjectInternal extends ObjectInternal, TPythonFunctionObject {
Function getScope() {
exists(CallableExpr expr |
this = TPythonFunctionObject(expr.getAFlowNode()) and
result = expr.getInnerScope()
)
}
override string toString() {
result = this.getScope().toString()
}
/** The boolean value of this object, if it has one */
override boolean booleanValue() {
result = true
}
/** Holds if this object may be true or false when evaluated as a bool */
override predicate maybe() { none() }
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
this = TPythonFunctionObject(node) and context.appliesTo(node)
}
/** INTERNAL */
override ClassDecl getClassDeclaration() { none() }
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(Builtin::special("FunctionType"))
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override Builtin getBuiltin() {
none()
}
override ControlFlowNode getOrigin() {
this = TPythonFunctionObject(result)
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
exists(Function func, ControlFlowNode rval |
func = this.getScope() and
callee.appliesToScope(func) and
rval = func.getAReturnValueFlowNode() and
PointsTo2::points_to(rval, callee, obj, origin)
)
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
scope = this.getScope() and paramOffset = 0
}
}
/// BOUND METHODS
///
///
///
class BuiltinFunctionObjectInternal extends ObjectInternal, TBuiltinFunctionObject {
override Builtin getBuiltin() {
this = TBuiltinFunctionObject(result)
}
override string toString() {
result = "builtin function " + this.getBuiltin().getName()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override boolean booleanValue() {
result = true
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should be be a unknown value of a known class if the return type is known or just an unknown.
none()
}
override ControlFlowNode getOrigin() {
none()
}
}
class BuiltinMethodObjectInternal extends ObjectInternal, TBuiltinMethodObject {
override Builtin getBuiltin() {
this = TBuiltinMethodObject(result)
}
override string toString() {
result = "builtin method " + this.getBuiltin().getName()
}
override boolean booleanValue() {
result = true
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate isComparable() {
any()
}
override predicate notComparable() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// TO DO .. Result should be be a unknown value of a known class if the return type is known or just an unknown.
none()
}
override ControlFlowNode getOrigin() {
none()
}
}
class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
override Builtin getBuiltin() {
this = TBuiltinOpaqueObject(result)
}
override string toString() {
none()
}
override boolean booleanValue() {
// TO DO ... Depends on class. `this.getClass().instancesAlways(result)`
none()
}
override predicate maybe() {
// TO DO ... Depends on class. `this.getClass().instancesMaybe()`
any()
}
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TBuiltinClassObject(this.getBuiltin().getClass())
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate isComparable() {
none()
}
override predicate notComparable() {
any()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee_for_object(callee, this)
}
override ControlFlowNode getOrigin() {
none()
}
}
private predicate callee_for_object(PointsToContext2 callee, ObjectInternal obj) {
exists(CallNode call, PointsToContext2 caller |
callee.fromCall(call, caller) and
PointsTo2::points_to(call.getFunction(), caller, obj, _)
)
}
class UnknownClassInternal extends ObjectInternal, TUnknownClass {
override string toString() {
none()
}
override boolean booleanValue() {
none()
}
override predicate maybe() { any() }
override ClassDecl getClassDeclaration() {
result = Builtin::unknownType()
}
override predicate isClass() { any() }
override predicate notClass() { none() }
override ObjectInternal getClass() {
result = TUnknownClass()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate isComparable() {
none()
}
override predicate notComparable() {
any()
}
override Builtin getBuiltin() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee_for_object(callee, this)
}
override ControlFlowNode getOrigin() {
none()
}
}
class UnknownInternal extends ObjectInternal, TUnknown {
override string toString() {
none()
}
override boolean booleanValue() {
none()
}
override predicate maybe() { any() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
result = TUnknownClass()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate isComparable() {
none()
}
override predicate notComparable() {
any()
}
override Builtin getBuiltin() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee_for_object(callee, this)
}
override ControlFlowNode getOrigin() {
none()
}
}
class UndefinedInternal extends ObjectInternal, TUndefined {
override string toString() {
none()
}
override boolean booleanValue() {
none()
}
override predicate maybe() { none() }
override ClassDecl getClassDeclaration() {
none()
}
override predicate isClass() { none() }
override predicate notClass() { any() }
override ObjectInternal getClass() {
none()
}
override predicate introduced(ControlFlowNode node, PointsToContext2 context) {
none()
}
override predicate isComparable() {
none()
}
override predicate notComparable() {
any()
}
override Builtin getBuiltin() {
none()
}
override predicate callResult(PointsToContext2 callee, ObjectInternal obj, CfgOrigin origin) {
// Accessing an undefined value raises a NameError, but if during import it probably
// means that we missed an import.
obj = ObjectInternal::unknown() and origin = CfgOrigin::unknown() and
callee.getOuter().isImport()
}
override ControlFlowNode getOrigin() {
none()
}
}
module ObjectInternal {
ObjectInternal bool(boolean b) {
b = true and result = TTrue()
or
b = false and result = TFalse()
}
ObjectInternal none_() {
result = TNone()
}
ObjectInternal unknown() {
result = TUnknown()
}
ObjectInternal unknownClass() {
result = TUnknownClass()
}
ObjectInternal undefined() {
result = TUndefined()
}
ObjectInternal builtin(string name) {
result = TBuiltinClassObject(Builtin::builtin(name))
or
result = TBuiltinFunctionObject(Builtin::builtin(name))
or
result = TBuiltinOpaqueObject(Builtin::builtin(name))
}
ObjectInternal sysModules() {
result = TBuiltinOpaqueObject(Builtin::special("sys").getMember("modules"))
}
ObjectInternal fromInt(int n) {
result = TInt(n)
}
}

View File

@@ -41,8 +41,39 @@ newtype TObject =
TUnknownClass()
or
TUndefined()
or
TInt(int n) {
// Powers of 2 are used for flags
is_power_2(n) or
// And all combinations of flags up to 2^8
n in [0..511] or
// Any number explicitly mentioned in the source code.
exists(Num num |
n = num.getN().toInt() or
exists(UnaryExpr neg | neg.getOp() instanceof USub and neg.getOperand() = num)
and n = -num.getN().toInt()
)
}
or
TString(string s) {
// Any string explicitly mentioned in the source code.
exists(StrConst str |
s = str.getText()
)
or
// Any string from the library put in the DB by the extractor.
exists(string quoted_string, Builtin bltn |
quoted_string = bltn.getName() and
s = quoted_string.regexpCapture("[bu]'([\\s\\S]*)'", 1)
)
}
private predicate is_power_2(int n) {
n = 1 or
exists(int half | is_power_2(half) and n = half*2)
}
library class ClassDecl extends @py_object {
ClassDecl() {

View File

@@ -101,9 +101,9 @@ module PointsTo2 {
or
origin = f and compare_expr_points_to(f, context, value)
or
origin = f and not_points_to(f, context, value)
origin = f and unary_points_to(f, context, value)
or
origin = f and value.instantiated(f, context)
origin = f and value.introduced(f, context)
or
InterModulePointsTo::import_points_to(f, context, value, origin)
or
@@ -255,7 +255,7 @@ module PointsTo2 {
pragma [nomagic]
private predicate ssa_node_definition_points_to_unpruned(EssaNodeDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
parameter_points_to(def, context, value, origin)
InterProceduralPointsTo::parameter_points_to(def, context, value, origin)
or
assignment_points_to(def, context, value, origin)
//// TO DO...
@@ -320,32 +320,6 @@ module PointsTo2 {
}
/** Points-to for parameter. `def foo(param): ...`. */
pragma [noinline]
private predicate parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
positional_parameter_points_to(def, context, value, origin)
//// TO DO...
// or
// named_parameter_points_to(def, context, value, origin)
// or
// default_parameter_points_to(def, context, value, origin)
// or
// special_parameter_points_to(def, context, value, origin)
}
/** Helper for `parameter_points_to` */
pragma [noinline]
private predicate positional_parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
/// To do...
//exists(PointsToContext2 caller, ControlFlowNode arg |
// points_to(arg, caller, value, origin) and
// Flow::callsite_argument_transfer(arg, caller, def, context)
//)
//or
not def.isSelf() and not def.getParameter().isVarargs() and not def.getParameter().isKwargs() and
context.isRuntime() and value = ObjectInternal::unknown() and origin = def.getDefiningNode()
}
/** Holds if the phi-function `phi` refers to `(value, origin)` given the context `context`. */
pragma [nomagic]
private predicate ssa_phi_points_to(PhiFunction phi, PointsToContext2 context, ObjectInternal value, CfgOrigin origin) {
@@ -365,13 +339,12 @@ module PointsTo2 {
pragma [noinline]
private predicate scope_entry_points_to(ScopeEntryDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
/* Transfer from another scope */
/// To do...
//exists(EssaVariable var, PointsToContext2 outer, CfgOrigin orig |
// Flow::scope_entry_value_transfer(var, outer, def, context) and
// ssa_variable_points_to(var, outer, value, orig) and
// origin = orig.asCfgNodeOrHere(def.getDefiningNode())
//)
//or
exists(EssaVariable var, PointsToContext2 outer, CfgOrigin orig |
InterProceduralPointsTo::scope_entry_value_transfer(var, outer, def, context) and
ssa_variable_points_to(var, outer, value, orig) and
origin = orig.asCfgNodeOrHere(def.getDefiningNode())
)
or
/* Undefined variable */
exists(Scope scope |
not def.getVariable().getName() = "__name__" and
@@ -411,48 +384,68 @@ module PointsTo2 {
private predicate compare_expr_points_to(CompareNode cmp, PointsToContext2 context, ObjectInternal value) {
exists(ControlFlowNode a, ControlFlowNode b, ObjectInternal o1, ObjectInternal o2 |
exists(boolean is |
equality_test(cmp, a, is, b) and
points_to(a, context, o1, _) and
points_to(b, context, o2, _) |
(o1.isComparable() and o2.isComparable()) and
(
o1 = o2 and value = ObjectInternal::bool(is)
or
o1 != o2 and value = ObjectInternal::bool(is.booleanNot())
)
or
(o1.notComparable() or o2.notComparable()) and
value = ObjectInternal::bool(_)
)
// TO DO -- Comparison of int and string constants. Version tests and the like.
//or
//const_compare(cmp, context, comp, strict)
// exists(int comp, boolean strict |
// const_compare(cmp, context, comp, strict)
// |
// comp = -1 and value = theTrueObject()
// or
// comp = 0 and strict = false and value = ObjectInternal::bool(true)
// or
// comp = 0 and strict = true and value = ObjectInternal::bool(false)
// or
// comp = 1 and value = ObjectInternal::bool(false)
// )
equality_test(cmp, a, is, b) and
points_to(a, context, o1, _) and
points_to(b, context, o2, _) |
(o1.isComparable() and o2.isComparable()) and
(
o1 = o2 and value = ObjectInternal::bool(is)
or
o1 != o2 and value = ObjectInternal::bool(is.booleanNot())
)
or
(o1.notComparable() or o2.notComparable()) and
value = ObjectInternal::bool(_)
)
or
exists(boolean strict |
inequality(cmp, a, b, strict) and
points_to(a, context, o1, _) and
points_to(b, context, o2, _) |
o1.intValue() < o2.intValue() and value = ObjectInternal::bool(true)
or
o1.intValue() > o2.intValue() and value = ObjectInternal::bool(false)
or
o1.intValue() = o2.intValue() and value = ObjectInternal::bool(strict.booleanNot())
or
o1.strValue() < o2.strValue() and value = ObjectInternal::bool(true)
or
o1.strValue() > o2.strValue() and value = ObjectInternal::bool(false)
or
o1.strValue() = o2.strValue() and value = ObjectInternal::bool(strict.booleanNot())
)
// or
// value = version_tuple_compare(cmp, context)
)
}
private predicate not_points_to(UnaryExprNode f, PointsToContext2 context, ObjectInternal value) {
f.getNode().getOp() instanceof Not and
exists(ObjectInternal operand |
/** Helper for comparisons. */
predicate inequality(CompareNode cmp, ControlFlowNode lesser, ControlFlowNode greater, boolean strict) {
exists(Cmpop op |
cmp.operands(lesser, op, greater) and op.getSymbol() = "<" and strict = true
or
cmp.operands(lesser, op, greater) and op.getSymbol() = "<=" and strict = false
or
cmp.operands(greater, op, lesser) and op.getSymbol() = ">" and strict = true
or
cmp.operands(greater, op, lesser) and op.getSymbol() = ">=" and strict = false
)
}
private predicate unary_points_to(UnaryExprNode f, PointsToContext2 context, ObjectInternal value) {
exists(Unaryop op, ObjectInternal operand |
op = f.getNode().getOp() and
points_to(f.getOperand(), context, operand, _)
|
operand.maybe() and value = ObjectInternal::bool(_)
op instanceof Not and operand.maybe() and value = ObjectInternal::bool(_)
or
operand.booleanValue() = false and value = ObjectInternal::bool(true)
op instanceof Not and operand.booleanValue() = false and value = ObjectInternal::bool(true)
or
operand.booleanValue() = true and value = ObjectInternal::bool(false)
op instanceof Not and operand.booleanValue() = true and value = ObjectInternal::bool(false)
or
op instanceof USub and value = ObjectInternal::fromInt(-operand.intValue())
or
operand = ObjectInternal::unknown() and value = operand
)
}
@@ -589,6 +582,122 @@ module InterProceduralPointsTo {
)
}
/** Points-to for parameter. `def foo(param): ...`. */
pragma [noinline]
predicate parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
positional_parameter_points_to(def, context, value, origin)
or
named_parameter_points_to(def, context, value, origin)
or
default_parameter_points_to(def, context, value, origin)
// or
// special_parameter_points_to(def, context, value, origin)
}
/** Helper for `parameter_points_to` */
pragma [noinline]
private predicate positional_parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(PointsToContext2 caller, ControlFlowNode arg |
PointsTo2::points_to(arg, caller, value, origin) and
callsite_argument_transfer(arg, caller, def, context)
)
or
not def.isSelf() and not def.getParameter().isVarargs() and not def.getParameter().isKwargs() and
context.isRuntime() and value = ObjectInternal::unknown() and origin = def.getDefiningNode()
}
/** Helper for `parameter_points_to` */
pragma [noinline]
private predicate named_parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(CallNode call, PointsToContext2 caller, PythonFunctionObjectInternal func, string name |
context.fromCall(call, func, caller) and
def.getParameter() = func.getScope().getArgByName(name) and
PointsTo2::points_to(call.getArgByName(name), caller, value, origin)
)
}
/** Helper for parameter_points_to */
private predicate default_parameter_points_to(ParameterDefinition def, PointsToContext2 context, ObjectInternal value, ControlFlowNode origin) {
exists(PointsToContext2 imp | imp.isImport() | PointsTo2::points_to(def.getDefault(), imp, value, origin)) and
context_for_default_value(def, context)
}
/** Helper for default_parameter_points_to */
pragma [noinline]
private predicate context_for_default_value(ParameterDefinition def, PointsToContext2 context) {
context.isRuntime()
or
exists(PointsToContext2 caller, CallNode call, PythonFunctionObjectInternal func, int n |
context.fromCall(call, func, caller) and
func.getScope().getArg(n) = def.getParameter() and
not exists(call.getArg(n)) and
not exists(call.getArgByName(def.getParameter().asName().getId())) and
not exists(call.getNode().getKwargs()) and
not exists(call.getNode().getStarargs())
)
}
/** Holds if the `(argument, caller)` pair matches up with `(param, callee)` pair across call. */
cached predicate callsite_argument_transfer(ControlFlowNode argument, PointsToContext2 caller, ParameterDefinition param, PointsToContext2 callee) {
exists(CallNode call, Function func, int n, int offset |
callsite_calls_function(call, caller, func, callee, offset) and
argument = call.getArg(n) and
param.getParameter() = func.getArg(n+offset)
)
}
cached predicate callsite_calls_function(CallNode call, PointsToContext2 caller, Function scope, PointsToContext2 callee, int parameter_offset) {
callee.fromCall(call, caller) and
exists(ObjectInternal func |
PointsTo2::points_to(call.getFunction(), caller, func, _) and
func.calleeAndOffset(scope, parameter_offset)
)
}
/** Model the transfer of values at scope-entry points. Transfer from `(pred_var, pred_context)` to `(succ_def, succ_context)`. */
cached predicate scope_entry_value_transfer(EssaVariable pred_var, PointsToContext2 pred_context, ScopeEntryDefinition succ_def, PointsToContext2 succ_context) {
scope_entry_value_transfer_from_earlier(pred_var, pred_context, succ_def, succ_context)
// TO DO...
//or
//callsite_entry_value_transfer(pred_var, pred_context, succ_def, succ_context)
//or
//pred_context.isImport() and pred_context = succ_context and
//class_entry_value_transfer(pred_var, succ_def)
}
/** Helper for `scope_entry_value_transfer`. Transfer of values from a temporally earlier scope to later scope.
* Earlier and later scopes are, for example, a module and functions in that module, or an __init__ method and another method. */
pragma [noinline]
private predicate scope_entry_value_transfer_from_earlier(EssaVariable pred_var, PointsToContext2 pred_context, ScopeEntryDefinition succ_def, PointsToContext2 succ_context) {
exists(Scope pred_scope, Scope succ_scope |
BaseFlow::scope_entry_value_transfer_from_earlier(pred_var, pred_scope, succ_def, succ_scope) and
succ_context.appliesToScope(succ_scope)
|
succ_context.isRuntime() and succ_context = pred_context
or
pred_context.isImport() and pred_scope instanceof ImportTimeScope and
(succ_context.fromRuntime() or
/* A call made at import time, but from another module. Assume this module has been fully imported. */
succ_context.isCall() and exists(CallNode call | succ_context.fromCall(call, _) and call.getEnclosingModule() != pred_scope))
or
/* If predecessor scope is main, then we assume that any global defined exactly once
* is available to all functions. Although not strictly true, this gives less surprising
* results in practice. */
pred_context.isMain() and pred_scope instanceof Module and succ_context.fromRuntime() and
exists(Variable v |
v = pred_var.getSourceVariable() and
not strictcount(v.getAStore()) > 1
)
)
or
exists(NonEscapingGlobalVariable var |
var = pred_var.getSourceVariable() and var = succ_def.getSourceVariable() and
pred_var.getAUse() = succ_context.getRootCall() and pred_context.isImport() and
succ_context.appliesToScope(succ_def.getScope())
)
}
}
/** Gets the `value, origin` that `f` would refer to if it has not been assigned some other value */
@@ -630,6 +739,8 @@ module Conditionals {
//or
result = equalityEvaluatesTo(expr, use, context, val, origin)
or
result = inequalityEvaluatesTo(expr, use, context, val, origin)
or
//result = callable_test_evaluates_boolean(expr, use, context, val, origin)
//or
//result = hasattr_test_evaluates_boolean(expr, use, context, val, origin)
@@ -677,4 +788,33 @@ module Conditionals {
)
}
pragma [noinline]
private boolean inequalityEvaluatesTo(ControlFlowNode expr, ControlFlowNode use, PointsToContext2 context, ObjectInternal val, ControlFlowNode origin) {
exists(ControlFlowNode r, boolean sense |
contains_interesting_expression_within_test(expr, use) |
exists(boolean strict, ObjectInternal other |
(
PointsTo2::inequality(expr, use, r, strict) and sense = true
or
PointsTo2::inequality(expr, r, use, strict) and sense = false
) and
PointsTo2::points_to(use, context, val, origin) and
PointsTo2::points_to(r, context, other, _)
|
val.intValue() < other.intValue() and result = sense
or
val.intValue() > other.intValue() and result = sense.booleanNot()
or
val.intValue() = other.intValue() and result = strict.booleanXor(sense)
or
val.strValue() < other.strValue() and result = sense
or
val.strValue() > other.strValue() and result = sense.booleanNot()
or
val.strValue() = other.strValue() and result = strict.booleanXor(sense)
)
)
}
}

View File

@@ -8,7 +8,7 @@ private import semmle.python.objects.ObjectInternal
*/
private int given_cost() {
exists(string depth |
exists(string depth |
py_flags_versioned("context.cost", depth, _) and
result = depth.toInt()
)