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

Skip to content

Commit 55eac7d

Browse files
committed
Python points-to: Fix up handling of recursive 'from ... import *'.
1 parent 0b2421e commit 55eac7d

5 files changed

Lines changed: 110 additions & 72 deletions

File tree

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,11 @@ class IntObjectInternal extends ConstantObjectInternal, TInt {
191191
class FloatObjectInternal extends ConstantObjectInternal, TFloat {
192192

193193
override string toString() {
194-
result = "float " + this.floatValue().toString()
194+
if this.floatValue() = this.floatValue().floor() then (
195+
result = "float " + this.floatValue().toString() + ".0"
196+
) else (
197+
result = "float " + this.floatValue().toString()
198+
)
195199
}
196200

197201
override predicate introduced(ControlFlowNode node, PointsToContext context) {

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,6 @@ class PackageObjectInternal extends ModuleObjectInternal, TPackageObject {
189189

190190
}
191191

192-
/** Get the ESSA pseudo-variable used to retain module state
193-
* during module initialization. Module attributes are handled
194-
* as attributes of this variable, allowing the SSA form to track
195-
* mutations of the module during its creation.
196-
*/
197-
private predicate isModuleStateVariable(EssaVariable var) {
198-
var.getName() = "$" and var.getScope() instanceof Module
199-
}
200-
201192
class PythonModuleObjectInternal extends ModuleObjectInternal, TPythonModule {
202193

203194
override Builtin getBuiltin() {

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,6 @@ class Value extends TObject {
2222
PointsToInternal::pointsTo(result, _, this, _)
2323
}
2424

25-
predicate pointsTo(ControlFlowNode node, PointsToContext context, ControlFlowNode origin) {
26-
PointsToInternal::pointsTo(node, context, this, origin)
27-
}
28-
2925
Value getClass() {
3026
result = this.(ObjectInternal).getClass()
3127
}

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ newtype TObject =
8484
or
8585
TSpecificInstance(ControlFlowNode instantiation, ClassObjectInternal cls, PointsToContext context) {
8686
PointsToInternal::pointsTo(instantiation.(CallNode).getFunction(), context, cls, _) and
87-
cls.isSpecial() = false
87+
cls.isSpecial() = false and cls.getClassDeclaration().callReturnsInstance()
8888
or
8989
literal_instantiation(instantiation, cls, context)
9090
}
@@ -275,8 +275,6 @@ private predicate neither_class_nor_static_method(Function f) {
275275
)
276276
}
277277

278-
279-
280278
library class ClassDecl extends @py_object {
281279

282280
ClassDecl() {
@@ -330,5 +328,22 @@ library class ClassDecl extends @py_object {
330328
this = Builtin::builtin("float")
331329
}
332330

331+
predicate callReturnsInstance() {
332+
exists(Class pycls |
333+
pycls = this.getClass() |
334+
/* Django does this, so we need to account for it */
335+
not exists(Function init, LocalVariable self |
336+
/* `self.__class__ = ...` in the `__init__` method */
337+
pycls.getInitMethod() = init and
338+
self.isSelf() and self.getScope() = init and
339+
exists(AttrNode a | a.isStore() and a.getObject("__class__") = self.getAUse())
340+
)
341+
and
342+
not exists(Function new | new.getName() = "__new__" and new.getScope() = pycls)
343+
)
344+
or
345+
this instanceof Builtin
346+
}
347+
333348
}
334349

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

Lines changed: 87 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -661,7 +661,7 @@ module InterModulePointsTo {
661661
imp = def.getDefiningNode() and
662662
PointsToInternal::pointsTo(imp.getModule(), context, mod, _) and
663663
name = def.getSourceVariable().getName() and
664-
module_exports_boolean(mod, name) = true and
664+
moduleExportsBoolean(mod, name) = true and
665665
mod.attribute(name, value, orig) and
666666
origin = orig.fix(imp)
667667
)
@@ -679,7 +679,7 @@ module InterModulePointsTo {
679679
var = def.getInput() and
680680
exists(ModuleObjectInternal mod |
681681
PointsToInternal::pointsTo(def.getDefiningNode().(ImportStarNode).getModule(), context, mod, _) |
682-
module_exports_boolean(mod, var.getSourceVariable().getName()) = false
682+
moduleExportsBoolean(mod, var.getSourceVariable().getName()) = false
683683
or
684684
exists(Module m, string name |
685685
m = mod.getSourceModule() and name = var.getSourceVariable().getName() |
@@ -698,52 +698,77 @@ module InterModulePointsTo {
698698
)
699699
}
700700

701-
private predicate ofInterestInModule(ModuleObjectInternal mod, string name) {
702-
exists(ImportStarNode isn, Module m |
703-
m = mod.getSourceModule() and
704-
isn.getScope() = m and
705-
exists(EssaVariable var | var.getAUse() = isn and var.getName() = name)
701+
predicate ofInterestInExports(ModuleObjectInternal mod, string name) {
702+
exists(ImportStarNode imp, ImportStarRefinement def |
703+
imp = def.getDefiningNode() and
704+
PointsToInternal::pointsTo(imp.getModule(), any(Context ctx | ctx.isImport()), mod, _) |
705+
def.getVariable().getName() = "$" and
706+
ModuleAttributes::attributePointsTo(def.getInput().getDefinition(), name, _, _)
707+
or
708+
def.getVariable().getName() = name and not name = "$" and not name = "*"
706709
)
707-
}
708-
709-
private predicate ofInterestInExports(ModuleObjectInternal mod, string name) {
710-
exists(ModuleObjectInternal importer |
711-
importsByImportStar(importer, mod) and
712-
ofInterestInModule(importer, name)
710+
or
711+
exists(PackageObjectInternal package |
712+
ofInterestInExports(package, name) and
713+
package.getInitModule() = mod
713714
)
714715
}
715716

716-
boolean module_exports_boolean(ModuleObjectInternal mod, string name) {
717-
ofInterestInExports(mod, name) and
717+
private boolean pythonModuleExportsBoolean(PythonModuleObjectInternal mod, string name) {
718718
exists(Module src |
719719
src = mod.getSourceModule()
720720
|
721-
if exists(SsaVariable var | name = var.getId() and var.getAUse() = src.getANormalExit()) then
722-
result = true
723-
else (
724-
exists(ImportStarNode isn, ModuleObjectInternal imported |
725-
isn.getScope() = src and
726-
PointsToInternal::pointsTo(isn.getModule(), _, imported, _) and
727-
result = module_exports_boolean(imported, name)
728-
)
721+
src.declaredInAll(name) and result = true
722+
or
723+
src.declaredInAll(_) and not src.declaredInAll(name) and
724+
ofInterestInExports(mod, name) and result = false
725+
or
726+
not src.declaredInAll(_) and
727+
exists(ObjectInternal val |
728+
ModuleAttributes::pointsToAtExit(src, name, val, _) |
729+
val = ObjectInternal::undefined() and result = false
729730
or
730-
not exists(ImportStarNode isn |isn.getScope() = src) and result = false
731+
val != ObjectInternal::undefined() and result = true
731732
)
732733
)
733-
or
734-
ofInterestInExports(mod, name) and
734+
}
735+
736+
private boolean packageExportsBoolean(PackageObjectInternal mod, string name) {
735737
exists(Folder folder |
736-
mod.(PackageObjectInternal).hasNoInitModule() and
737-
folder = mod.(PackageObjectInternal).getFolder() |
738-
if (exists(folder.getChildContainer(name)) or exists(folder.getFile(name + ".py"))) then
739-
result = true
740-
else
741-
result = false
738+
folder = mod.getFolder() |
739+
exportsSubmodule(folder, name) and result = true
740+
or
741+
not exportsSubmodule(folder, name) and result = moduleExportsBoolean(mod.getInitModule(), name)
742+
or
743+
mod.hasNoInitModule() and not exportsSubmodule(folder, name) and
744+
ofInterestInExports(mod, name) and result = false
745+
)
746+
}
747+
748+
private predicate exportsSubmodule(Folder folder, string name) {
749+
name.regexpMatch("\\p{L}(\\p{L}|\\d|_)*") and
750+
(
751+
exists(Folder child | child = folder.getChildContainer(name))
752+
or
753+
exists(folder.getFile(name + ".py"))
742754
)
755+
}
756+
757+
boolean builtinModuleExportsBoolean(BuiltinModuleObjectInternal mod, string name) {
758+
exists(Builtin bltn |
759+
bltn = mod.getBuiltin() |
760+
exists(bltn.getMember(name)) and result = true
761+
or
762+
ofInterestInExports(mod, name) and not exists(bltn.getMember(name)) and result = false
763+
)
764+
}
765+
766+
boolean moduleExportsBoolean(ModuleObjectInternal mod, string name) {
767+
result = pythonModuleExportsBoolean(mod, name)
743768
or
744-
name = "__name__" and result = true
769+
result = packageExportsBoolean(mod, name)
745770
or
746-
exists(mod.(BuiltinModuleObjectInternal).getBuiltin().getMember(name)) and result = true
771+
result = builtinModuleExportsBoolean(mod, name)
747772
}
748773

749774
}
@@ -1678,11 +1703,11 @@ module ModuleAttributes {
16781703
if exists(varAtExit(mod, name)) then (
16791704
PointsToInternal::variablePointsTo(varAtExit(mod, name), any(Context c | c.isImport()), value, origin)
16801705
) else (
1681-
moduleAttributePointsTo(moduleStateVarAtExit(mod), name, value, origin)
1706+
attributePointsTo(moduleStateVarAtExit(mod), name, value, origin)
16821707
)
16831708
}
16841709

1685-
private predicate moduleAttributePointsTo(EssaVariable var, string name, ObjectInternal value, CfgOrigin origin) {
1710+
predicate attributePointsTo(EssaVariable var, string name, ObjectInternal value, CfgOrigin origin) {
16861711
importStarPointsTo(var.getDefinition(), name, value, origin)
16871712
or
16881713
callsitePointsTo(var.getDefinition(), name, value, origin)
@@ -1693,18 +1718,20 @@ module ModuleAttributes {
16931718
pragma [noinline]
16941719
private predicate importStarPointsTo(ImportStarRefinement def, string name, ObjectInternal value, CfgOrigin origin) {
16951720
def.getVariable().getName() = "$" and
1696-
exists(ImportStarNode imp, ModuleObjectInternal mod, CfgOrigin orig |
1721+
exists(ImportStarNode imp, ModuleObjectInternal mod |
16971722
imp = def.getDefiningNode() and
1698-
origin = orig.fix(imp) and
1699-
PointsToInternal::pointsTo(imp.getModule(), any(Context ctx | ctx.isImport()), mod, _) |
1723+
PointsToInternal::pointsTo(imp.getModule(), any(Context ctx | ctx.isImport()), mod, _)
1724+
|
17001725
/* Attribute from imported module */
1701-
InterModulePointsTo::module_exports_boolean(mod, name) = true and
1702-
mod.attribute(name, value, orig) and
1703-
not exists(Variable v | v.getId() = name and v.getScope() = imp.getScope())
1726+
exists(CfgOrigin orig |
1727+
InterModulePointsTo::moduleExportsBoolean(mod, name) = true and
1728+
mod.attribute(name, value, orig) and origin = orig.fix(imp) and
1729+
not exists(Variable v | v.getId() = name and v.getScope() = imp.getScope())
1730+
)
17041731
or
17051732
/* Retain value held before import */
1706-
InterModulePointsTo::module_exports_boolean(mod, name) = false and
1707-
moduleAttributePointsTo(def.getInput().getDefinition(), name, value, origin)
1733+
InterModulePointsTo::moduleExportsBoolean(mod, name) = false and
1734+
attributePointsTo(def.getInput(), name, value, origin)
17081735
)
17091736
}
17101737

@@ -1718,25 +1745,30 @@ module ModuleAttributes {
17181745
exists(EssaVariable var, Function func, PointsToContext callee |
17191746
InterProceduralPointsTo::callsite_calls_function(def.getCall(), _, func, callee, _) and
17201747
var = moduleStateVariable(func.getANormalExit()) and
1721-
moduleAttributePointsTo(var, name, value, origin)
1748+
attributePointsTo(var, name, value, origin)
17221749
)
17231750
}
17241751

1752+
/** Holds if the attribute name of the implicit '$' variable refers to `value` at the start of the scope.
1753+
* Since it cannot refer to any actual value, it is set to "undefined" for sub module names.
1754+
*/
17251755
pragma [noinline]
17261756
predicate scopeEntryPointsTo(ScopeEntryDefinition def, string name, ObjectInternal value, CfgOrigin origin) {
17271757
def.getVariable().getName() = "$" and
1728-
/* Transfer from another scope */
1729-
exists(EssaVariable var, PointsToContext outer |
1730-
InterProceduralPointsTo::scope_entry_value_transfer(var, outer, def, _) and
1731-
moduleAttributePointsTo(var, name, value, origin)
1732-
)
1733-
or
1734-
def.getVariable().getName() = "$" and
1735-
exists(PackageObjectInternal package |
1736-
package.getSourceModule() = def.getScope() and
1737-
exists(package.submodule(name)) and
1758+
exists(Module m |
1759+
def.getScope() = m and
1760+
not exists(EssaVariable named | named.getName() = name and named.getScope() = m) and
1761+
not name = "$" and not name = "*" and
17381762
value = ObjectInternal::undefined() and
17391763
origin = CfgOrigin::unknown()
1764+
|
1765+
m.isPackageInit() and exists(m.getPackage().getSubModule(name))
1766+
or
1767+
not m.declaredInAll(_) and
1768+
exists(PythonModuleObjectInternal mod |
1769+
mod.getSourceModule() = m and
1770+
InterModulePointsTo::ofInterestInExports(mod, name)
1771+
)
17401772
)
17411773
}
17421774

0 commit comments

Comments
 (0)