Python points-to: Fix up test-evaluate for ABCs and tests involving type().

This commit is contained in:
Mark Shannon
2019-04-17 18:04:19 +01:00
parent 8a2fb54c49
commit f51a2d9ec4
12 changed files with 203 additions and 52 deletions

View File

@@ -53,6 +53,9 @@ class Value extends TObject {
result = this.(ObjectInternal).getSource()
}
predicate isBuiltin() {
this.(ObjectInternal).isBuiltin()
}
}
class ModuleValue extends Value {

View File

@@ -365,5 +365,15 @@ library class ClassDecl extends @py_object {
this instanceof Builtin
}
predicate isAbstractBaseClass(string name) {
exists(Module m |
m.getName() = "_abcoll"
or
m.getName() = "_collections_abc"
|
this.getClass().getScope() = m and
this.getName() = name
)
}
}

View File

@@ -817,24 +817,7 @@ module InterProceduralPointsTo {
PointsToInternal::pointsTo(f.getArg(0), context, value, origin)
)
or
value = call_to_type(f, context) and
(
value.isBuiltin() and origin = f
or
origin = value.getOrigin()
or
value = ObjectInternal::unknownClass() and origin = f
)
}
pragma [noinline]
private ObjectInternal call_to_type(CallNode f, PointsToContext context) {
count(f.getArg(_)) = 1 and
call(f, context, ObjectInternal::builtin("type")) and
exists(ObjectInternal arg |
PointsToInternal::pointsTo(f.getArg(0), context, arg, _) and
result = arg.getClass()
)
Expressions::typeCallPointsTo(f, context, value, origin, _, _)
}
/** Points-to for parameter. `def foo(param): ...`. */
@@ -1174,6 +1157,16 @@ module Expressions {
origin = call
}
pragma [noinline]
predicate typeCallPointsTo(CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, ControlFlowNode arg, ObjectInternal argvalue) {
not exists(call.getArg(1)) and
arg = call.getArg(0) and
InterProceduralPointsTo::call(call, context, ObjectInternal::builtin("type")) and
PointsToInternal::pointsTo(arg, context, argvalue, _) and
value = argvalue.getClass() and
origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(call)
}
pragma [noinline]
private predicate lenCallPointsTo(CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, ControlFlowNode arg, ObjectInternal argvalue) {
len_call(call, arg, context, argvalue) and
@@ -1317,6 +1310,8 @@ module Expressions {
or
lenCallPointsTo(expr, context, value, origin, subexpr, subvalue)
or
typeCallPointsTo(expr, context, value, origin, subexpr, subvalue)
or
getattrPointsTo(expr, context, value, origin, subexpr, subvalue)
or
value = ObjectInternal::bool(evaluatesTo(expr, context, subexpr, subvalue)) and origin = expr
@@ -1754,7 +1749,9 @@ cached module Types {
cached boolean improperSubclass(ObjectInternal sub, ObjectInternal sup) {
sub = sup and result = true
or
result = mroContains(Types::getMro(sub), sup, 0)
result = true and mroContains(Types::getMro(sub), sup)
or
result = false and mroDoesnotContain(Types::getMro(sub), sup, 0)
or
result = tupleSubclass(sub, sup, 0)
}
@@ -1768,32 +1765,47 @@ cached module Types {
)
}
private boolean mroContains(ClassList mro, ClassObjectInternal sup, int n) {
private predicate mroContains(ClassList mro, ClassObjectInternal sup) {
mro.contains(sup)
or
exists(ClassDecl item, ClassDecl sdecl |
item = mro.getAnItem().getClassDeclaration() and
sdecl = sup.getClassDeclaration() and
is_abstract_subclass(item, sdecl)
)
}
private predicate mroDoesnotContain(ClassList mro, ClassObjectInternal sup, int n) {
exists(ClassObjectInternal cls |
Expressions::requireSubClass(cls, sup) and
mro = getMro(cls)
)
and
(
n = mro.length() and result = false
n = mro.length()
or
mro.getItem(n) = sup and result = true
or
mro.getItem(n) = abc_to_concrete(sup) and result = true
or
mro.getItem(n) != sup and sup != AbstractBaseClass::named("Iterable") and
mro.getItem(n) != abc_to_concrete(sup) and result = mroContains(mro, sup, n+1)
or
sup = AbstractBaseClass::named("Iterable") and result = mro.getItem(n).isIterableSubclass()
mroDoesnotContain(mro, sup, n+1) and
mro.getItem(n) != sup and
exists(ClassDecl item, ClassDecl sdecl |
item = mro.getItem(n).getClassDeclaration() and
sdecl = sup.getClassDeclaration() and
not is_abstract_subclass(item, sdecl)
)
)
}
private ClassObjectInternal abc_to_concrete(ClassObjectInternal c) {
c = AbstractBaseClass::named("Sequence") and result = ObjectInternal::builtin("list")
private predicate is_abstract_subclass(ClassDecl cls, ClassDecl sup) {
cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Sequence")
or
c = AbstractBaseClass::named("Set") and result = ObjectInternal::builtin("set")
cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Set")
or
c = AbstractBaseClass::named("Mapping") and result = ObjectInternal::builtin("dict")
cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Mapping")
or
cls = Builtin::builtin("list") and sup.isAbstractBaseClass("Iterable")
or
cls = Builtin::builtin("set") and sup.isAbstractBaseClass("Iterable")
or
cls = Builtin::builtin("dict") and sup.isAbstractBaseClass("Iterable")
}
cached boolean hasAttr(ObjectInternal cls, string name) {
@@ -1822,19 +1834,6 @@ cached module Types {
}
module AbstractBaseClass {
ClassObjectInternal named(string name) {
exists(ModuleObjectInternal m |
m.getName() = "_abcoll"
or
m.getName() = "_collections_abc"
|
m.attribute(name, result, _)
)
}
}
module AttributePointsTo {
predicate pointsTo(AttrNode f, Context context, ObjectInternal value, ControlFlowNode origin) {

View File

@@ -0,0 +1,13 @@
| builtin-class dict | builtin-class dict |
| builtin-class dict | builtin-class list |
| builtin-class int | (builtin-class float, builtin-class dict) |
| builtin-class int | (builtin-class list, builtin-class int) |
| builtin-class int | (builtin-class list, builtin-class int) |
| builtin-class int | builtin-class dict |
| builtin-class int | builtin-class float |
| builtin-class int | builtin-class int |
| builtin-class int | builtin-class list |
| builtin-class int | builtin-class object |
| builtin-class int | builtin-class tuple |
| builtin-class tuple | builtin-class int |
| builtin-class tuple | builtin-class tuple |

View File

@@ -0,0 +1,8 @@
import python
import semmle.python.pointsto.PointsTo
import semmle.python.pointsto.PointsTo
from Value sup, Value cls
where Expressions::requireSubClass(cls, sup)
select cls, sup

View File

@@ -0,0 +1,11 @@
| 3 | isinstance() | true | x | int 7 |
| 4 | issubclass() | true | x | int 7 |
| 6 | issubclass() | true | d | builtin-class dict |
| 7 | UnaryExpr | true | d | builtin-class dict |
| 10 | isinstance() | false | x | int 0 |
| 10 | isinstance() | true | x | () |
| 13 | isinstance() | false | x | () |
| 13 | isinstance() | true | x | int 0 |
| 14 | isinstance() | true | x | int 0 |
| 15 | issubclass() | true | x | int 0 |
| 16 | issubclass() | false | x | int 0 |

View File

@@ -0,0 +1,12 @@
import python
import semmle.python.pointsto.PointsTo
import semmle.python.objects.ObjectInternal
import semmle.python.pointsto.PointsToContext
from ControlFlowNode test, ControlFlowNode use, ObjectInternal val, boolean eval, PointsToContext ctx
where
PointsTo::pointsTo(use, ctx, val, _) and
eval = Conditionals::testEvaluates(test, use, ctx, val, _)
select test.getLocation().getStartLine(), test.getNode().toString(), eval.toString(), use.getNode().toString(), val.toString()

View File

@@ -0,0 +1,66 @@
| 2 | ControlFlowNode for IntegerLiteral | int 7 | 2 |
| 3 | ControlFlowNode for int | builtin-class int | 3 |
| 3 | ControlFlowNode for isinstance | Builtin-function isinstance | 3 |
| 3 | ControlFlowNode for isinstance() | bool True | 3 |
| 3 | ControlFlowNode for x | int 7 | 2 |
| 4 | ControlFlowNode for issubclass | Builtin-function issubclass | 4 |
| 4 | ControlFlowNode for issubclass() | bool True | 4 |
| 4 | ControlFlowNode for object | builtin-class object | 4 |
| 4 | ControlFlowNode for type | builtin-class type | 4 |
| 4 | ControlFlowNode for type() | builtin-class int | 4 |
| 4 | ControlFlowNode for x | int 7 | 2 |
| 5 | ControlFlowNode for dict | builtin-class dict | 5 |
| 6 | ControlFlowNode for d | builtin-class dict | 5 |
| 6 | ControlFlowNode for dict | builtin-class dict | 6 |
| 6 | ControlFlowNode for issubclass | Builtin-function issubclass | 6 |
| 6 | ControlFlowNode for issubclass() | bool True | 6 |
| 7 | ControlFlowNode for UnaryExpr | bool True | 7 |
| 7 | ControlFlowNode for d | builtin-class dict | 5 |
| 7 | ControlFlowNode for issubclass | Builtin-function issubclass | 7 |
| 7 | ControlFlowNode for issubclass() | bool False | 7 |
| 7 | ControlFlowNode for list | builtin-class list | 7 |
| 9 | ControlFlowNode for IfExp | () | 9 |
| 9 | ControlFlowNode for IfExp | int 0 | 9 |
| 9 | ControlFlowNode for IntegerLiteral | int 0 | 9 |
| 9 | ControlFlowNode for Tuple | () | 9 |
| 9 | ControlFlowNode for condition | Unknown value | 9 |
| 10 | ControlFlowNode for isinstance | Builtin-function isinstance | 10 |
| 10 | ControlFlowNode for isinstance() | bool False | 10 |
| 10 | ControlFlowNode for isinstance() | bool True | 10 |
| 10 | ControlFlowNode for tuple | builtin-class tuple | 10 |
| 10 | ControlFlowNode for x | () | 9 |
| 10 | ControlFlowNode for x | int 0 | 9 |
| 12 | ControlFlowNode for IntegerLiteral | int 3 | 12 |
| 12 | ControlFlowNode for isinstance | Builtin-function isinstance | 12 |
| 12 | ControlFlowNode for isinstance() | bool False | 12 |
| 12 | ControlFlowNode for isinstance() | bool True | 12 |
| 12 | ControlFlowNode for unknown | Unknown value | 12 |
| 12 | ControlFlowNode for unknown() | Unknown value | 12 |
| 13 | ControlFlowNode for int | builtin-class int | 13 |
| 13 | ControlFlowNode for isinstance | Builtin-function isinstance | 13 |
| 13 | ControlFlowNode for isinstance() | bool False | 13 |
| 13 | ControlFlowNode for isinstance() | bool True | 13 |
| 13 | ControlFlowNode for x | () | 9 |
| 13 | ControlFlowNode for x | int 0 | 9 |
| 14 | ControlFlowNode for Tuple | (builtin-class list, builtin-class int) | 14 |
| 14 | ControlFlowNode for int | builtin-class int | 14 |
| 14 | ControlFlowNode for isinstance | Builtin-function isinstance | 14 |
| 14 | ControlFlowNode for isinstance() | bool True | 14 |
| 14 | ControlFlowNode for list | builtin-class list | 14 |
| 14 | ControlFlowNode for x | int 0 | 9 |
| 15 | ControlFlowNode for Tuple | (builtin-class list, builtin-class int) | 15 |
| 15 | ControlFlowNode for int | builtin-class int | 15 |
| 15 | ControlFlowNode for issubclass | Builtin-function issubclass | 15 |
| 15 | ControlFlowNode for issubclass() | bool True | 15 |
| 15 | ControlFlowNode for list | builtin-class list | 15 |
| 15 | ControlFlowNode for type | builtin-class type | 15 |
| 15 | ControlFlowNode for type() | builtin-class int | 15 |
| 15 | ControlFlowNode for x | int 0 | 9 |
| 16 | ControlFlowNode for Tuple | (builtin-class float, builtin-class dict) | 16 |
| 16 | ControlFlowNode for dict | builtin-class dict | 16 |
| 16 | ControlFlowNode for float | builtin-class float | 16 |
| 16 | ControlFlowNode for issubclass | Builtin-function issubclass | 16 |
| 16 | ControlFlowNode for issubclass() | bool False | 16 |
| 16 | ControlFlowNode for type | builtin-class type | 16 |
| 16 | ControlFlowNode for type() | builtin-class int | 16 |
| 16 | ControlFlowNode for x | int 0 | 9 |

View File

@@ -0,0 +1,10 @@
import python
import semmle.python.pointsto.PointsTo
import semmle.python.objects.ObjectInternal
from ControlFlowNode f, ObjectInternal v, ControlFlowNode x
where PointsTo::pointsTo(f, _, v, x)
select f.getLocation().getStartLine(), f.toString(), v, x.getLocation().getStartLine()

View File

@@ -0,0 +1,18 @@
x = 7
assert isinstance(x, int)
assert issubclass(type(x), object)
d = dict
assert issubclass(d, dict)
assert not issubclass(d, list)
x = 0 if condition else ()
if isinstance(x, tuple):
pass
isinstance(3, unknown())
assert isinstance(x, int)
assert isinstance(x, (list, int))
assert issubclass(type(x), (list, int))
if issubclass(type(x), (float, dict)):
pass

View File

@@ -2,8 +2,9 @@
import python
import semmle.python.pointsto.PointsTo
import semmle.python.pointsto.PointsToContext
import semmle.python.objects.ObjectInternal
from CallNode call, FunctionObject method
where PointsTo::Test::super_method_call(_, call, _, method)
select call.getLocation().getStartLine(), call.toString(), method.getQualifiedName()
from CallNode call, SuperInstance sup, BoundMethodObjectInternal bm
where call.getFunction().inferredValue() = bm and
call.getFunction().(AttrNode).getObject().inferredValue() = sup
select call.getLocation().getStartLine(), call.toString(), bm.getFunction().getSource().(FunctionObject).getQualifiedName()

View File

@@ -2,8 +2,8 @@
import python
import semmle.python.pointsto.MRO
from ClassObject cls
from ClassValue cls
where not cls.isBuiltin()
select cls.toString(), new_style_mro(cls)
select cls.toString(), Mro::newStyleMro(cls)