Python points-to: Better handling of *args, **kwargs and procedures.

This commit is contained in:
Mark Shannon
2019-03-25 17:33:01 +00:00
parent f5c32421f4
commit dbf228d005
4 changed files with 43 additions and 7 deletions

View File

@@ -75,12 +75,25 @@ class PythonFunctionObjectInternal extends CallableObjectInternal, TPythonFuncti
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {
exists(Function func, ControlFlowNode rval |
func = this.getScope() and
callee.appliesToScope(func) and
callee.appliesToScope(func) |
rval = func.getAReturnValueFlowNode() and
PointsTo2::points_to(rval, callee, obj, origin)
or
exists(Return ret |
ret.getScope() = func and
PointsTo2::reachableBlock(ret.getAFlowNode().getBasicBlock(), callee) and
not exists(ret.getValue()) and
obj = ObjectInternal::none_() and
origin = CfgOrigin::unknown()
)
)
}
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
override predicate callResult(ObjectInternal obj, CfgOrigin origin) {
this.getScope().isProcedure() and
obj = ObjectInternal::none_() and
origin = CfgOrigin::unknown()
}
override predicate calleeAndOffset(Function scope, int paramOffset) {
scope = this.getScope() and paramOffset = 0

View File

@@ -140,7 +140,7 @@ class NoneObjectInternal extends ObjectInternal, TNone {
}
override Builtin getBuiltin() {
none()
result = Builtin::special("None")
}
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) {

View File

@@ -292,7 +292,6 @@ module ObjectInternal {
result = TNone()
}
ObjectInternal unknown() {
result = TUnknown()
}

View File

@@ -520,7 +520,7 @@ module InterModulePointsTo {
exists(string name, ModuleObjectInternal mod, CfgOrigin orig |
from_import_imports(f, context, mod, name) and
(mod.getSourceModule() != f.getEnclosingModule() or mod.isBuiltin()) and
mod.attribute(name, value, origin) and
mod.attribute(name, value, orig) and
origin = orig.asCfgNodeOrHere(f)
// TO DO... $ variables.
//mod.getSourceModule() = f.getEnclosingModule() and
@@ -697,8 +697,8 @@ module InterProceduralPointsTo {
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)
or
special_parameter_points_to(def, context, value, origin)
}
/** Helper for `parameter_points_to` */
@@ -745,6 +745,30 @@ module InterProceduralPointsTo {
)
}
/** Helper for parameter_points_to */
pragma [noinline]
private predicate special_parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
context.isRuntime() and
origin = def.getDefiningNode() and
exists(ControlFlowNode param |
param = def.getDefiningNode() |
exists(Function func | func.getVararg() = param.getNode()) and value = TUnknownInstance(ObjectInternal::builtin("tuple"))
or
exists(Function func | func.getKwarg() = param.getNode()) and value = TUnknownInstance(ObjectInternal::builtin("dict"))
)
or
exists(PointsToContext caller, CallNode call, Function f, Parameter p |
context.fromCall(call, caller) and
context.appliesToScope(f) and
f.getAnArg() = p and p = def.getParameter() and
not p.isSelf() and
not exists(call.getArg(p.getPosition())) and
not exists(call.getArgByName(p.getName())) and
(exists(call.getNode().getKwargs()) or exists(call.getNode().getStarargs())) and
value = ObjectInternal::unknown() and origin = def.getDefiningNode()
)
}
/** Holds if the `(argument, caller)` pair matches up with `(param, callee)` pair across call. */
cached predicate callsite_argument_transfer(ControlFlowNode argument, PointsToContext caller, ParameterDefinition param, PointsToContext callee) {
exists(CallNode call, Function func, int n, int offset |