Skip to content

Commit b79b53f

Browse files
authored
Merge pull request #1103 from markshannon/python-encapsulate-builtins
Python: encapsulate extensionals dealing with 'builtin' objects.
2 parents 1da828f + eb5927a commit b79b53f

File tree

10 files changed

+250
-168
lines changed

10 files changed

+250
-168
lines changed

python/ql/src/Classes/ClassAttributes.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ class CheckClass extends ClassObject {
130130

131131

132132
private Object object_getattribute() {
133-
py_cmembers_versioned(theObjectType(), "__getattribute__", result, major_version().toString())
133+
result.asBuiltin() = theObjectType().asBuiltin().getMember("__getattribute__")
134134
}
135135

136136
private predicate auto_name(string name) {

python/ql/src/semmle/python/Import.qll

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import python
2-
2+
private import semmle.python.types.Builtins
33

44
/** An alias in an import statement, the `mod as name` part of `import mod as name`. May be artificial;
55
`import x` is transformed into `import x as x` */
@@ -14,7 +14,7 @@ class Alias extends Alias_ {
1414
private predicate valid_module_name(string name) {
1515
exists(Module m | m.getName() = name)
1616
or
17-
exists(Object cmod | py_cobjecttypes(cmod, theModuleType()) and py_cobjectnames(cmod, name))
17+
exists(Builtin cmod | cmod.getClass() = theModuleType().asBuiltin() and cmod.getName() = name)
1818
}
1919

2020
/** An artificial expression representing an import */

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

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
import python
1111
import semmle.python.dataflow.SsaDefinitions
12+
private import semmle.python.types.Builtins
1213

1314
module BasePointsTo {
1415
/** INTERNAL -- Use n.refersTo(value, _, origin) instead */
@@ -49,7 +50,7 @@ ClassObject simple_types(Object obj) {
4950
or
5051
obj.getOrigin() instanceof Module and result = theModuleType()
5152
or
52-
result = builtin_object_type(obj)
53+
result.asBuiltin() = obj.asBuiltin().getClass()
5354
or
5455
obj = unknownValue() and result = theUnknownType()
5556
}
@@ -126,34 +127,6 @@ predicate baseless_is_new_style(ClassObject cls) {
126127
* analysis.
127128
*/
128129

129-
/** Gets the base class of built-in class `cls` */
130-
pragma [noinline]
131-
ClassObject builtin_base_type(ClassObject cls) {
132-
/* The extractor uses the special name ".super." to indicate the super class of a builtin class */
133-
py_cmembers_versioned(cls, ".super.", result, _)
134-
}
135-
136-
/** Gets the `name`d attribute of built-in class `cls` */
137-
pragma [noinline]
138-
Object builtin_class_attribute(ClassObject cls, string name) {
139-
not name = ".super." and
140-
py_cmembers_versioned(cls, name, result, _)
141-
}
142-
143-
/** Holds if the `name`d attribute of built-in module `m` is `value` of `cls` */
144-
pragma [noinline]
145-
predicate builtin_module_attribute(ModuleObject m, string name, Object value, ClassObject cls) {
146-
py_cmembers_versioned(m, name, value, _) and cls = builtin_object_type(value)
147-
}
148-
149-
/** Gets the (built-in) class of the built-in object `obj` */
150-
pragma [noinline]
151-
ClassObject builtin_object_type(Object obj) {
152-
py_cobjecttypes(obj, result) and not obj = unknownValue()
153-
or
154-
obj = unknownValue() and result = theUnknownType()
155-
}
156-
157130
/** Holds if this class (not on a super-class) declares name */
158131
pragma [noinline]
159132
predicate class_declares_attribute(ClassObject cls, string name) {
@@ -162,11 +135,11 @@ predicate class_declares_attribute(ClassObject cls, string name) {
162135
class_defines_name(defn, name)
163136
)
164137
or
165-
exists(Object o |
166-
o = builtin_class_attribute(cls, name) and
167-
not exists(ClassObject sup |
168-
sup = builtin_base_type(cls) and
169-
o = builtin_class_attribute(sup, name)
138+
exists(Builtin o |
139+
o = cls.asBuiltin().getMember(name) and
140+
not exists(Builtin sup |
141+
sup = cls.asBuiltin().getBaseClass() and
142+
o = sup.getMember(name)
170143
)
171144
)
172145
}
@@ -558,11 +531,11 @@ Object undefinedVariable() {
558531

559532
/** Gets the pseudo-object representing an unknown value */
560533
Object unknownValue() {
561-
py_special_objects(result, "_1")
534+
result.asBuiltin() = Builtin::unknown()
562535
}
563536

564537
BuiltinCallable theTypeNewMethod() {
565-
py_cmembers_versioned(theTypeType(), "__new__", result, major_version().toString())
538+
result.asBuiltin() = theTypeType().asBuiltin().getMember("__new__")
566539
}
567540

568541
/** Gets the `value, cls, origin` that `f` would refer to if it has not been assigned some other value */
@@ -578,7 +551,7 @@ predicate potential_builtin_points_to(NameNode f, Object value, ClassObject cls,
578551

579552
pragma [noinline]
580553
predicate builtin_name_points_to(string name, Object value, ClassObject cls) {
581-
value = Object::builtin(name) and py_cobjecttypes(value, cls)
554+
value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass()
582555
}
583556

584557
module BaseFlow {

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

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import python
2727
private import PointsToContext
2828
private import Base
2929
private import semmle.python.types.Extensions
30+
private import semmle.python.types.Builtins
3031
private import Filters as BaseFilters
3132
import semmle.dataflow.SSA
3233
private import MRO
@@ -311,7 +312,7 @@ module PointsTo {
311312
exists(SubscriptNode sub, Object sys_modules |
312313
sub.getValue() = sys_modules_flow and
313314
points_to(sys_modules_flow, _, sys_modules, _, _) and
314-
builtin_module_attribute(theSysModuleObject(), "modules", sys_modules, _) and
315+
sys_modules.asBuiltin() = Builtin::special("sys").getMember("modules") and
315316
sub.getIndex() = n and
316317
n.getNode().(StrConst).getText() = name and
317318
sub.(DefinitionNode).getValue() = mod and
@@ -435,7 +436,7 @@ module PointsTo {
435436
}
436437

437438
private boolean module_exports_boolean(ModuleObject mod, string name) {
438-
py_cmembers_versioned(mod, name, _, major_version().toString()) and
439+
exists(mod.asBuiltin().getMember(name)) and
439440
name.charAt(0) != "_" and result = true
440441
or
441442
result = package_exports_boolean(mod, name)
@@ -494,7 +495,8 @@ module PointsTo {
494495
or
495496
package_attribute_points_to(mod, name, value, cls, origin)
496497
or
497-
builtin_module_attribute(mod, name, value, cls) and origin = CfgOrigin::unknown()
498+
value.asBuiltin() = mod.asBuiltin().getMember(name) and
499+
cls.asBuiltin() = value.asBuiltin().getClass() and origin = CfgOrigin::unknown()
498500
}
499501

500502
}
@@ -2458,7 +2460,7 @@ module PointsTo {
24582460
is_new_style(cls) and not exists(cls_expr.getBase(0)) and result = theObjectType() and n = 0
24592461
)
24602462
or
2461-
result = builtin_base_type(cls) and n = 0
2463+
result.asBuiltin() = cls.asBuiltin().getBaseClass() and n = 0
24622464
or
24632465
cls = theUnknownType() and result = theObjectType() and n = 0
24642466
}
@@ -2482,7 +2484,7 @@ module PointsTo {
24822484
or
24832485
cls = theObjectType() and result = 0
24842486
or
2485-
exists(builtin_base_type(cls)) and cls != theObjectType() and result = 1
2487+
exists(cls.asBuiltin().getBaseClass()) and cls != theObjectType() and result = 1
24862488
or
24872489
cls = theUnknownType() and result = 1
24882490
}
@@ -2646,8 +2648,8 @@ module PointsTo {
26462648
ssa_variable_points_to(var, _, value, vcls, origin)
26472649
)
26482650
or
2649-
value = builtin_class_attribute(owner, name) and class_declares_attribute(owner, name) and
2650-
origin = CfgOrigin::unknown() and vcls = builtin_object_type(value)
2651+
value.asBuiltin() = owner.asBuiltin().getMember(name) and class_declares_attribute(owner, name) and
2652+
origin = CfgOrigin::unknown() and vcls.asBuiltin() = value.asBuiltin().getClass()
26512653
}
26522654

26532655
private predicate interesting_class_attribute(ClassList mro, string name) {
@@ -2754,7 +2756,11 @@ module PointsTo {
27542756
obj = unknownValue() and result = theUnknownType()
27552757
)
27562758
or
2757-
py_cobjecttypes(cls, result) and is_c_metaclass(result)
2759+
exists(Builtin meta |
2760+
result.asBuiltin() = meta and
2761+
meta = cls.asBuiltin().getClass() and
2762+
meta.inheritsFromType()
2763+
)
27582764
or
27592765
exists(ControlFlowNode meta |
27602766
Types::six_add_metaclass(_, cls, meta) and
@@ -2777,7 +2783,7 @@ module PointsTo {
27772783
}
27782784

27792785
private boolean has_declared_metaclass(ClassObject cls) {
2780-
py_cobjecttypes(cls, _) and result = true
2786+
exists(cls.asBuiltin().getClass()) and result = true
27812787
or
27822788
result = has_six_add_metaclass(cls).booleanOr(has_metaclass_var_metaclass(cls))
27832789
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import python
2+
3+
class Builtin extends @py_cobject {
4+
5+
Builtin() {
6+
not (
7+
/* @py_cobjects for modules which have a corresponding Python module */
8+
exists(@py_cobject mod_type | py_special_objects(mod_type, "ModuleType") and py_cobjecttypes(this, mod_type)) and
9+
exists(Module m | py_cobjectnames(this, m.getName()))
10+
)
11+
and (
12+
/* Exclude unmatched builtin objects in the library trap files */
13+
py_cobjectnames(this, _) or
14+
py_cobjecttypes(this, _) or
15+
py_special_objects(this, _)
16+
)
17+
}
18+
19+
string toString() {
20+
not this = undefinedVariable().asBuiltin() and not this = Builtin::unknown() and
21+
exists(Builtin type, string typename, string objname |
22+
py_cobjecttypes(this, type) and py_cobjectnames(this, objname) and typename = type.getName() |
23+
result = typename + " " + objname
24+
)
25+
}
26+
27+
Builtin getClass() {
28+
py_cobjecttypes(this, result) and not this = Builtin::unknown()
29+
or
30+
this = Builtin::unknown() and result = Builtin::unknownType()
31+
}
32+
33+
Builtin getMember(string name) {
34+
not name = ".super." and
35+
py_cmembers_versioned(this, name, result, major_version().toString())
36+
}
37+
38+
Builtin getItem(int index) {
39+
py_citems(this, index, result)
40+
}
41+
42+
Builtin getBaseClass() {
43+
/* The extractor uses the special name ".super." to indicate the super class of a builtin class */
44+
py_cmembers_versioned(this, ".super.", result, major_version().toString())
45+
}
46+
47+
predicate inheritsFromType() {
48+
this = Builtin::special("type")
49+
or
50+
this.getBaseClass().inheritsFromType()
51+
}
52+
53+
string getName() {
54+
py_cobjectnames(this, result)
55+
}
56+
57+
predicate isClass() {
58+
py_cobjecttypes(_, this) or
59+
this = Builtin::unknownType() or
60+
exists(Builtin meta | meta.inheritsFromType() and py_cobjecttypes(this, meta))
61+
}
62+
63+
predicate isFunction() {
64+
this.getClass() = Builtin::special("BuiltinFunctionType") and
65+
exists(Builtin mod |
66+
mod.isModule() and
67+
mod.getMember(_) = this
68+
)
69+
}
70+
71+
predicate isModule() {
72+
this.getClass() = Builtin::special("ModuleType")
73+
}
74+
75+
predicate isMethod() {
76+
this.getClass() = Builtin::special("MethodDescriptorType")
77+
or
78+
this.getClass() = Builtin::special("BuiltinFunctionType") and
79+
exists(Builtin cls | cls.isClass() and cls.getMember(_) = this)
80+
or
81+
this.getClass().getName() = "wrapper_descriptor"
82+
}
83+
84+
}
85+
86+
module Builtin {
87+
88+
Builtin builtinModule() {
89+
py_special_objects(result, "builtin_module_2") and major_version() = 2
90+
or
91+
py_special_objects(result, "builtin_module_3") and major_version() = 3
92+
}
93+
94+
Builtin builtin(string name) {
95+
result = builtinModule().getMember(name)
96+
}
97+
98+
Builtin special(string name) {
99+
py_special_objects(result, name)
100+
}
101+
102+
Builtin unknown() {
103+
py_special_objects(result, "_1")
104+
}
105+
106+
Builtin unknownType() {
107+
py_special_objects(result, "_semmle_unknown_type")
108+
}
109+
110+
}

0 commit comments

Comments
 (0)