Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b182abd

Browse files
committed
Python points-to: Fix up a number of overly slow predicates.
1 parent 8e2d6c4 commit b182abd

3 files changed

Lines changed: 106 additions & 80 deletions

File tree

python/ql/src/semmle/python/objects/Instances.qll

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,36 @@ abstract class InstanceObject extends ObjectInternal {
1313

1414
pragma [nomagic]
1515
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) {
16-
PointsToInternal::attributeRequired(this, name) and
17-
(
18-
exists(ObjectInternal cls_attr |
19-
this.getClass().(ClassObjectInternal).lookup(name, cls_attr, _)
20-
|
21-
/* If class attribute is not a descriptor, that usually means it is some sort of
22-
* default value and likely overridden by an instance attribute. In that case
23-
* use `unknown` to signal that an attribute exists but to avoid false positives
24-
* f using the default value.
25-
*/
26-
cls_attr.isDescriptor() = false and value = ObjectInternal::unknown() and origin = CfgOrigin::unknown()
27-
or
28-
cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin)
29-
)
16+
exists(ObjectInternal cls_attr |
17+
this.classAttribute(name, cls_attr)
18+
|
19+
/* If class attribute is not a descriptor, that usually means it is some sort of
20+
* default value and likely overridden by an instance attribute. In that case
21+
* use `unknown` to signal that an attribute exists but to avoid false positives
22+
* f using the default value.
23+
*/
24+
cls_attr.isDescriptor() = false and value = ObjectInternal::unknown() and origin = CfgOrigin::unknown()
3025
or
31-
exists(EssaVariable self, PythonFunctionObjectInternal init, Context callee |
32-
this.initializer(init, callee) and
33-
self_variable_reaching_init_exit(self) and
34-
self.getScope() = init.getScope() and
35-
AttributePointsTo::variableAttributePointsTo(self, callee, name, value, origin)
36-
)
26+
cls_attr.isDescriptor() = true and cls_attr.descriptorGetInstance(this, value, origin)
27+
)
28+
or
29+
this.selfAttribute(name, value, origin)
30+
}
31+
32+
pragma [noinline]
33+
private predicate classAttribute(string name, ObjectInternal cls_attr) {
34+
PointsToInternal::attributeRequired(this, name) and
35+
this.getClass().(ClassObjectInternal).lookup(name, cls_attr, _)
36+
}
37+
38+
pragma [noinline]
39+
private predicate selfAttribute(string name, ObjectInternal value, CfgOrigin origin) {
40+
PointsToInternal::attributeRequired(this, name) and
41+
exists(EssaVariable self, PythonFunctionObjectInternal init, Context callee |
42+
this.initializer(init, callee) and
43+
self_variable_reaching_init_exit(self) and
44+
self.getScope() = init.getScope() and
45+
AttributePointsTo::variableAttributePointsTo(self, callee, name, value, origin)
3746
)
3847
}
3948

python/ql/src/semmle/python/pointsto/Base.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,12 @@ class SingleSuccessorGuard extends PyNodeRefinement {
417417
ControlFlowNode getTest() {
418418
result = this.getDefiningNode()
419419
}
420+
421+
predicate useAndTest(ControlFlowNode use, ControlFlowNode test) {
422+
test = this.getDefiningNode() and
423+
SsaSource::test_refinement(this.getSourceVariable(), use, test)
424+
}
425+
420426
}
421427

422428
/** Implicit definition of the names of sub-modules in a package.

python/ql/src/semmle/python/pointsto/PointsTo.qll

Lines changed: 71 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -417,7 +417,7 @@ cached module PointsToInternal {
417417
or
418418
attribute_delete_points_to(def, context, value, origin)
419419
or
420-
uni_edged_phi_points_to(def, context, value, origin)
420+
uni_edged_pi_points_to(def, context, value, origin)
421421
}
422422

423423
/** Pass through for `self` for the implicit re-definition of `self` in `self.foo()`. */
@@ -460,16 +460,16 @@ cached module PointsToInternal {
460460
result = Conditionals::testEvaluates(def.getTest(), def.getInput().getASourceUse(), context, value, origin)
461461
}
462462

463-
/** Holds if ESSA definition, `uniphi`, refers to `(value, origin)`. */
463+
/** Holds if ESSA definition, `unipi`, refers to `(value, origin)`. */
464464
pragma [noinline]
465-
private predicate uni_edged_phi_points_to(SingleSuccessorGuard uniphi, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
466-
exists(ControlFlowNode test, ControlFlowNode use |
465+
private predicate uni_edged_pi_points_to(SingleSuccessorGuard unipi, PointsToContext context, ObjectInternal value, CfgOrigin origin) {
466+
exists(ControlFlowNode test, ControlFlowNode use, ControlFlowNode orig |
467467
/* Because calls such as `len` may create a new variable, we need to go via the source variable
468468
* That is perfectly safe as we are only dealing with calls that do not mutate their arguments.
469469
*/
470-
use = uniphi.getInput().getASourceUse() and
471-
test = uniphi.getTest() and
472-
uniphi.getSense() = Conditionals::testEvaluates(test, use, context, value, origin.toCfgNode())
470+
unipi.useAndTest(use, test) and
471+
unipi.getSense() = Conditionals::testEvaluates(test, use, context, value, orig) and
472+
origin = CfgOrigin::fromCfgNode(orig)
473473
)
474474
}
475475

@@ -785,7 +785,28 @@ module InterProceduralPointsTo {
785785
}
786786

787787
pragma [noinline]
788-
private predicate call_points_to_simple(CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
788+
predicate call_points_to(CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
789+
/* Either not a decorator, or we understand the return value */
790+
(value != ObjectInternal::unknown() or not f.isDecoratorCall()) and
791+
call_points_to_from_callee(f, context, value, origin)
792+
or
793+
call_result_is_first_argument(f, context) and
794+
PointsToInternal::pointsTo(f.getArg(0), context, value, origin)
795+
or
796+
Expressions::typeCallPointsTo(f, context, value, origin, _, _)
797+
}
798+
799+
/** Helper for call_points_to to improve join-order */
800+
private predicate call_result_is_first_argument(CallNode f, PointsToContext context) {
801+
Types::six_add_metaclass(f, context, _, _)
802+
or
803+
/* A decorator and we don't understand it. Use the original, undecorated value */
804+
f.isDecoratorCall() and call_points_to_from_callee(f, context, ObjectInternal::unknown(), _)
805+
}
806+
807+
/** Helper for call_points_to to improve join-order */
808+
pragma [noinline]
809+
private predicate call_points_to_from_callee(CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
789810
exists(ObjectInternal func |
790811
call(f, context, func)
791812
|
@@ -806,27 +827,6 @@ module InterProceduralPointsTo {
806827
)
807828
}
808829

809-
pragma [noinline]
810-
predicate call_points_to(CallNode f, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
811-
/* Either not a decorator, or we understand the return value */
812-
(value != ObjectInternal::unknown() or not f.isDecoratorCall()) and
813-
call_points_to_simple(f, context, value, origin)
814-
or
815-
call_result_is_first_argument(f, context) and
816-
PointsToInternal::pointsTo(f.getArg(0), context, value, origin)
817-
or
818-
Expressions::typeCallPointsTo(f, context, value, origin, _, _)
819-
}
820-
821-
/** Helper for call_points_to to improve join-order */
822-
private predicate call_result_is_first_argument(CallNode f, PointsToContext context) {
823-
Types::six_add_metaclass(f, context, _, _)
824-
or
825-
/* A decorator and we don't understand it. Use the original, undecorated value */
826-
f.isDecoratorCall() and call_points_to_simple(f, context, ObjectInternal::unknown(), _)
827-
}
828-
829-
830830
/** Points-to for parameter. `def foo(param): ...`. */
831831
pragma [noinline]
832832
predicate parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
@@ -898,26 +898,27 @@ module InterProceduralPointsTo {
898898
/** Helper for parameter_points_to */
899899
pragma [noinline]
900900
private predicate special_parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
901-
(
902-
def.isVarargs() and value = TUnknownInstance(ObjectInternal::builtin("tuple"))
903-
or
904-
def.isKwargs() and value = TUnknownInstance(ObjectInternal::builtin("dict"))
905-
)
906-
and
901+
special_parameter_value(def, value) and
907902
(
908903
context.isRuntime()
909904
or
910-
exists(PointsToContext caller, CallNode call, Parameter p |
905+
exists(PointsToContext caller, CallNode call |
911906
context.fromCall(call, caller) and
912907
context.appliesToScope(def.getScope()) and
913-
p = def.getParameter() and
914-
not exists(call.getArg(p.getPosition())) and
915-
not exists(call.getArgByName(p.getName()))
908+
not exists(call.getArg(def.getParameter().getPosition())) and
909+
not exists(call.getArgByName(def.getParameter().getName()))
916910
)
917911
) and
918912
origin = def.getDefiningNode()
919913
}
920914

915+
/** Helper predicate for special_parameter_points_to */
916+
private predicate special_parameter_value(ParameterDefinition p, ObjectInternal value) {
917+
p.isVarargs() and value = TUnknownInstance(ObjectInternal::builtin("tuple"))
918+
or
919+
p.isKwargs() and value = TUnknownInstance(ObjectInternal::builtin("dict"))
920+
}
921+
921922
/** Holds if the `(argument, caller)` pair matches up with `(param, callee)` pair across call. */
922923
cached predicate callsite_argument_transfer(ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, PointsToContext callee) {
923924
exists(CallNode call, Function func, int offset |
@@ -1178,10 +1179,7 @@ module Expressions {
11781179

11791180
pragma [noinline]
11801181
predicate typeCallPointsTo(CallNode call, PointsToContext context, ObjectInternal value, ControlFlowNode origin, ControlFlowNode arg, ObjectInternal argvalue) {
1181-
not exists(call.getArg(1)) and
1182-
arg = call.getArg(0) and
1183-
InterProceduralPointsTo::call(call, context, ObjectInternal::builtin("type")) and
1184-
PointsToInternal::pointsTo(arg, context, argvalue, _) and
1182+
type_call1(call, arg, context, argvalue) and
11851183
value = argvalue.getClass() and
11861184
origin = CfgOrigin::fromObject(value).asCfgNodeOrHere(call)
11871185
}
@@ -1467,6 +1465,13 @@ module Expressions {
14671465
PointsToInternal::pointsTo(use, context, val, _)
14681466
}
14691467

1468+
private predicate type_call1(CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val) {
1469+
PointsToInternal::pointsTo(call.getFunction(), context, ObjectInternal::builtin("type"), _) and
1470+
use = call.getArg(0) and
1471+
not exists(call.getArg(1)) and
1472+
PointsToInternal::pointsTo(use, context, val, _)
1473+
}
1474+
14701475
private predicate hasattr_call(CallNode call, ControlFlowNode use, PointsToContext context, ObjectInternal val, string name) {
14711476
exists(ControlFlowNode arg1 |
14721477
call_to_hasattr(call, context, use, arg1) and
@@ -1580,11 +1585,7 @@ module Conditionals {
15801585
test.getAChild*() = use
15811586
)
15821587
or
1583-
exists(SingleSuccessorGuard unipi |
1584-
unipi.getInput().getASourceUse() = use and
1585-
unipi.getTest() = test and
1586-
test.getAChild*() = use
1587-
)
1588+
any(SingleSuccessorGuard ssg).useAndTest(use, test)
15881589
}
15891590

15901591
private predicate pinode_test_part(ControlFlowNode outer, ControlFlowNode inner) {
@@ -1780,6 +1781,7 @@ cached module Types {
17801781
)
17811782
}
17821783

1784+
pragma [nomagic]
17831785
private ClassObjectInternal getInheritedMetaclass(ClassObjectInternal cls) {
17841786
result = getInheritedMetaclass(cls, 0)
17851787
or
@@ -2026,8 +2028,8 @@ module AttributePointsTo {
20262028
variableAttributePointsTo(def.getInput(), context, name, value, origin)
20272029
}
20282030

2029-
private predicate uniEdgedPhiAttributePointsTo(SingleSuccessorGuard uniphi, PointsToContext context, string name, ObjectInternal value, CfgOrigin origin) {
2030-
variableAttributePointsTo(uniphi.getInput(), context, name, value, origin)
2031+
private predicate uniEdgedPhiAttributePointsTo(SingleSuccessorGuard unipi, PointsToContext context, string name, ObjectInternal value, CfgOrigin origin) {
2032+
variableAttributePointsTo(unipi.getInput(), context, name, value, origin)
20312033
}
20322034

20332035
private predicate piNodeAttributePointsTo(PyEdgeRefinement pi, PointsToContext context, string name, ObjectInternal value, CfgOrigin origin) {
@@ -2108,24 +2110,33 @@ module ModuleAttributes {
21082110
pragma [nomagic]
21092111
private predicate importStarPointsTo(ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin) {
21102112
def.getVariable().isMetaVariable() and
2111-
exists(ImportStarNode imp, ModuleObjectInternal mod |
2112-
imp = def.getDefiningNode() and
2113-
PointsToInternal::pointsTo(imp.getModule(), any(Context ctx | ctx.isImport()), mod, _)
2114-
|
2113+
/* Attribute from imported module */
2114+
exists(ModuleObjectInternal mod |
2115+
importStarDef(def, _, mod)
2116+
and
21152117
/* Attribute from imported module */
21162118
exists(CfgOrigin orig |
21172119
InterModulePointsTo::moduleExportsBoolean(mod, name) = true and
2118-
mod.attribute(name, value, orig) and origin = orig.fix(imp) and
2119-
not exists(Variable v | v.getId() = name and v.getScope() = imp.getScope())
2120+
mod.attribute(name, value, orig) and origin = orig.fix(def.getDefiningNode()) and
2121+
not exists(Variable v | v.getId() = name and v.getScope() = def.getScope())
21202122
)
2121-
or
2122-
/* Retain value held before import */
2123-
(InterModulePointsTo::moduleExportsBoolean(mod, name) = false or name.charAt(0) = "_")
2124-
and
2123+
)
2124+
or
2125+
/* Retain value held before import */
2126+
exists(ModuleObjectInternal mod, EssaVariable input |
2127+
importStarDef(def, input, mod) and
2128+
(InterModulePointsTo::moduleExportsBoolean(mod, name) = false or name.charAt(0) = "_") and
21252129
attributePointsTo(def.getInput(), name, value, origin)
21262130
)
21272131
}
21282132

2133+
private predicate importStarDef(ImportStarRefinement def, EssaVariable input, ModuleObjectInternal mod) {
2134+
exists(ImportStarNode imp |
2135+
def.getVariable().getName() = "$" and imp = def.getDefiningNode() and
2136+
input = def.getInput() and PointsToInternal::pointsTo(imp.getModule(), any(Context ctx | ctx.isImport()), mod, _)
2137+
)
2138+
}
2139+
21292140
/** Points-to for a variable (possibly) redefined by a call:
21302141
* `var = ...; foo(); use(var)`
21312142
* Where var may be redefined in call to `foo` if `var` escapes (is global or non-local).

0 commit comments

Comments
 (0)