Skip to content

Commit 3bb61e7

Browse files
committed
Python points-to: Improve handling of subscripts and sequence inequalities.
1 parent 674a3da commit 3bb61e7

File tree

12 files changed

+169
-19
lines changed

12 files changed

+169
-19
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ abstract class CallableObjectInternal extends ObjectInternal {
5454

5555
abstract predicate neverReturns();
5656

57+
override predicate subscriptUnknown() { none() }
58+
5759
}
5860

5961

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ abstract class ClassObjectInternal extends ObjectInternal {
7575
result = false
7676
}
7777

78+
override predicate subscriptUnknown() { none() }
7879
}
7980

8081
class PythonClassObjectInternal extends ClassObjectInternal, TPythonClassObject {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ abstract class ConstantObjectInternal extends ObjectInternal {
4343

4444
pragma [noinline] override predicate attributesUnknown() { none() }
4545

46+
override predicate subscriptUnknown() { none() }
47+
4648
override boolean isDescriptor() { result = false }
4749

4850
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { none() }

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class PropertyInternal extends ObjectInternal, TProperty {
5353

5454
pragma [noinline] override predicate attributesUnknown() { none() }
5555

56+
override predicate subscriptUnknown() { none() }
57+
5658
override boolean isDescriptor() { result = true }
5759

5860
override int length() { none() }
@@ -133,6 +135,8 @@ class ClassMethodObjectInternal extends ObjectInternal, TClassMethod {
133135

134136
pragma [noinline] override predicate attributesUnknown() { none() }
135137

138+
override predicate subscriptUnknown() { none() }
139+
136140
override boolean isDescriptor() { result = true }
137141

138142
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) {
@@ -207,6 +211,8 @@ class StaticMethodObjectInternal extends ObjectInternal, TStaticMethod {
207211

208212
pragma [noinline] override predicate attributesUnknown() { none() }
209213

214+
override predicate subscriptUnknown() { none() }
215+
210216
override boolean isDescriptor() { result = true }
211217

212218
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ class SpecificInstanceInternal extends TSpecificInstance, ObjectInternal {
102102

103103
pragma [noinline] override predicate attributesUnknown() { any() }
104104

105+
override predicate subscriptUnknown() { any() }
106+
105107
override boolean isDescriptor() { result = false }
106108

107109
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { none() }
@@ -203,6 +205,8 @@ class SelfInstanceInternal extends TSelfInstance, ObjectInternal {
203205

204206
pragma [noinline] override predicate attributesUnknown() { any() }
205207

208+
override predicate subscriptUnknown() { any() }
209+
206210
override boolean isDescriptor() { result = false }
207211

208212
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { none() }
@@ -305,6 +309,8 @@ class UnknownInstanceInternal extends TUnknownInstance, ObjectInternal {
305309

306310
pragma [noinline] override predicate attributesUnknown() { any() }
307311

312+
override predicate subscriptUnknown() { any() }
313+
308314
override boolean isDescriptor() { result = false }
309315

310316
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { none() }
@@ -385,6 +391,8 @@ class SuperInstance extends TSuperInstance, ObjectInternal {
385391

386392
pragma [noinline] override predicate attributesUnknown() { none() }
387393

394+
override predicate subscriptUnknown() { any() }
395+
388396
override boolean isDescriptor() { result = false }
389397

390398
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { none() }

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ abstract class ModuleObjectInternal extends ObjectInternal {
4545

4646
override int length() { none() }
4747

48+
override predicate subscriptUnknown() { any() }
49+
4850
}
4951

5052
class BuiltinModuleObjectInternal extends ModuleObjectInternal, TBuiltinModuleObject {

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class ObjectInternal extends TObject {
6363
*/
6464
abstract int intValue();
6565

66-
/** The integer value of things that have integer values.
66+
/** The string value of things that have string values.
6767
* That is, strings.
6868
*/
6969
abstract string strValue();
@@ -78,6 +78,8 @@ class ObjectInternal extends TObject {
7878

7979
abstract predicate attributesUnknown();
8080

81+
abstract predicate subscriptUnknown();
82+
8183
/** For backwards compatibility shim -- Not all objects have a "source".
8284
* Objects (except unknown and undefined values) should attempt to return
8385
* exactly one result for this method.
@@ -174,6 +176,10 @@ class BuiltinOpaqueObjectInternal extends ObjectInternal, TBuiltinOpaqueObject {
174176

175177
pragma [noinline] override predicate attributesUnknown() { none() }
176178

179+
override predicate subscriptUnknown() {
180+
exists(this.getBuiltin().getItem(_))
181+
}
182+
177183
override boolean isDescriptor() { result = false }
178184

179185
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { none() }
@@ -247,6 +253,8 @@ class UnknownInternal extends ObjectInternal, TUnknown {
247253

248254
pragma [noinline] override predicate attributesUnknown() { any() }
249255

256+
override predicate subscriptUnknown() { any() }
257+
250258
override boolean isDescriptor() { result = false }
251259

252260
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { none() }
@@ -321,6 +329,8 @@ class UndefinedInternal extends ObjectInternal, TUndefined {
321329

322330
pragma [noinline] override predicate attributesUnknown() { none() }
323331

332+
override predicate subscriptUnknown() { none() }
333+
324334
override boolean isDescriptor() { none() }
325335

326336
pragma [noinline] override predicate descriptorGetClass(ObjectInternal cls, ObjectInternal value, CfgOrigin origin) { none() }
@@ -378,7 +388,10 @@ module ObjectInternal {
378388
ObjectInternal fromBuiltin(Builtin b) {
379389
b = result.getBuiltin() and
380390
not b = Builtin::unknown() and
381-
not b = Builtin::unknownType()
391+
not b = Builtin::unknownType() and
392+
not b = Builtin::special("sys").getMember("version_info")
393+
or
394+
b = Builtin::special("sys").getMember("version_info") and result = TSysVersionInfo()
382395
}
383396

384397
ObjectInternal classMethod() {

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

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ abstract class TupleObjectInternal extends SequenceObjectInternal {
8787

8888
pragma [noinline] override predicate attributesUnknown() { none() }
8989

90+
override predicate subscriptUnknown() { none() }
91+
9092
}
9193

9294
class BuiltinTupleObjectInternal extends TBuiltinTuple, TupleObjectInternal {
@@ -146,4 +148,80 @@ class PythonTupleObjectInternal extends TPythonTuple, TupleObjectInternal {
146148

147149
}
148150

151+
class SysVersionInfoObjectInternal extends TSysVersionInfo, SequenceObjectInternal {
152+
153+
override string toString() {
154+
result = "sys.version_info"
155+
}
156+
157+
override ObjectInternal getItem(int n) {
158+
n = 0 and result = TInt(major_version())
159+
or
160+
n = 1 and result = TInt(minor_version())
161+
}
162+
163+
override predicate introduced(ControlFlowNode node, PointsToContext context) { none() }
164+
165+
/** Gets the class declaration for this object, if it is a declared class. */
166+
override ClassDecl getClassDeclaration() {
167+
result = Builtin::special("sys").getMember("version_info").getClass()
168+
}
169+
170+
/** True if this "object" is a class. */
171+
override boolean isClass() { result = false }
172+
173+
override ObjectInternal getClass() {
174+
result.getBuiltin() = this.getClassDeclaration()
175+
}
176+
177+
override boolean isComparable() {
178+
result = true
179+
}
180+
181+
/** Gets the `Builtin` for this object, if any.
182+
* Objects (except unknown and undefined values) should attempt to return
183+
* exactly one result for either this method or `getOrigin()`.
184+
*/
185+
override Builtin getBuiltin() { none() }
186+
187+
/** Gets a control flow node that represents the source origin of this
188+
* objects.
189+
*/
190+
override ControlFlowNode getOrigin() { none() }
191+
192+
/** Holds if `obj` is the result of calling `this` and `origin` is
193+
* the origin of `obj`.
194+
*/
195+
override predicate callResult(ObjectInternal obj, CfgOrigin origin) { none() }
196+
197+
/** Holds if `obj` is the result of calling `this` and `origin` is
198+
* the origin of `obj` with callee context `callee`.
199+
*/
200+
override predicate callResult(PointsToContext callee, ObjectInternal obj, CfgOrigin origin) { none() }
201+
202+
/** The integer value of things that have integer values.
203+
* That is, ints and bools.
204+
*/
205+
override int intValue() { none() }
206+
207+
/** The integer value of things that have integer values.
208+
* That is, strings.
209+
*/
210+
override string strValue() { none() }
211+
212+
override predicate calleeAndOffset(Function scope, int paramOffset) { none() }
213+
214+
override predicate attribute(string name, ObjectInternal value, CfgOrigin origin) { none() }
149215

216+
override predicate attributesUnknown() { none() }
217+
218+
override predicate subscriptUnknown() { none() }
219+
220+
/** Gets the length of the sequence that this "object" represents.
221+
* Always returns a value for a sequence, will be -1 if object has no fixed length.
222+
*/
223+
override int length() { result = 5 }
224+
225+
override predicate functionAndOffset(CallableObjectInternal function, int offset) { none() }
226+
227+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,8 @@ newtype TObject =
136136
not count(instantiation.getAnArg()) = 1 and
137137
Types::getMro(metacls).contains(TType())
138138
}
139+
or
140+
TSysVersionInfo()
139141

140142
private predicate is_power_2(int n) {
141143
n = 1 or

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

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,8 +1096,17 @@ module Expressions {
10961096
predicate subscriptPointsTo(SubscriptNode subscr, PointsToContext context, ObjectInternal value, ControlFlowNode origin, ControlFlowNode obj, ObjectInternal objvalue) {
10971097
subscr.isLoad() and
10981098
obj = subscr.getObject() and
1099+
origin = subscr and
10991100
PointsToInternal::pointsTo(obj, context, objvalue, _) and
1100-
value = ObjectInternal::unknown() and origin = subscr
1101+
(
1102+
objvalue.subscriptUnknown() and
1103+
value = ObjectInternal::unknown()
1104+
or
1105+
exists(int n |
1106+
PointsToInternal::pointsTo(subscr.getIndex(), context, TInt(n), _) and
1107+
value = objvalue.(SequenceObjectInternal).getItem(n)
1108+
)
1109+
)
11011110
}
11021111

11031112
/** Track bitwise expressions so we can handle integer flags and enums.
@@ -1256,24 +1265,55 @@ module Expressions {
12561265
exists(boolean strict, boolean sense, ObjectInternal other |
12571266
inequalityTest(comp, context, use, val, other, strict, sense)
12581267
|
1259-
val.intValue() < other.intValue() and result = sense
1260-
or
1261-
val.intValue() > other.intValue() and result = sense.booleanNot()
1262-
or
1263-
val.intValue() = other.intValue() and result = strict.booleanXor(sense)
1268+
compare(val, other) = -1 and result = sense
12641269
or
1265-
val.strValue() < other.strValue() and result = sense
1266-
or
1267-
val.strValue() > other.strValue() and result = sense.booleanNot()
1270+
compare(val, other) = 0 and result = strict.booleanNot()
12681271
or
1269-
val.strValue() = other.strValue() and result = strict.booleanXor(sense)
1272+
compare(val, other) = 1 and result = sense.booleanNot()
12701273
or
12711274
val.isComparable() = false and result = maybe()
12721275
or
12731276
other.isComparable() = false and result = maybe()
12741277
)
12751278
}
12761279

1280+
private int compare(ObjectInternal val, ObjectInternal other) {
1281+
inequalityTest(_, _, _, val, other, _, _) and
1282+
result = compare_unbound(val, other)
1283+
or
1284+
result = compare_sequence(val, other, 0)
1285+
}
1286+
1287+
bindingset[val, other]
1288+
private int compare_unbound(ObjectInternal val, ObjectInternal other) {
1289+
val.intValue() < other.intValue() and result = -1
1290+
or
1291+
val.intValue() > other.intValue() and result = 1
1292+
or
1293+
val.intValue() = other.intValue() and result = 0
1294+
or
1295+
val.strValue() < other.strValue() and result = -1
1296+
or
1297+
val.strValue() > other.strValue() and result = 0
1298+
or
1299+
val.strValue() = other.strValue() and result = 1
1300+
}
1301+
1302+
private int compare_sequence(SequenceObjectInternal val, SequenceObjectInternal other, int n) {
1303+
inequalityTest(_, _, _, val, other, _, _) and
1304+
(
1305+
n = val.length() and other.length() > n and result = -1
1306+
or
1307+
n = other.length() and val.length() > n and result = 1
1308+
or
1309+
n = other.length() and n = val.length() and result = 0
1310+
or
1311+
result != 0 and result = compare_unbound(val.getItem(n), val.getItem(n))
1312+
or
1313+
compare_unbound(val.getItem(n), val.getItem(n)) = 0 and result = compare_sequence(val, other, n+1)
1314+
)
1315+
}
1316+
12771317
pragma [noinline]
12781318
private predicate inequalityTest(CompareNode comp, PointsToContext context, ControlFlowNode operand, ObjectInternal opvalue, ObjectInternal other, boolean strict, boolean sense) {
12791319
exists(ControlFlowNode r |

0 commit comments

Comments
 (0)