Skip to content

Commit 30b340d

Browse files
committed
Python points-to: Handle varargs in callee.
1 parent af08f85 commit 30b340d

File tree

10 files changed

+191
-10
lines changed

10 files changed

+191
-10
lines changed

python/ql/src/semmle/python/Exprs.qll

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ class Call extends Call_ {
227227
result = this.getKwargs().(Dict).getAKey().(StrConst).getText()
228228
}
229229

230+
int getPositionalArgumentCount() {
231+
count(this.getStarargs()) < 2 and
232+
result = count(this.getAPositionalArg())
233+
}
230234
}
231235

232236
/** A conditional expression such as, `body if test else orelse` */

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

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ abstract class TupleObjectInternal extends SequenceObjectInternal {
3939
}
4040

4141
private string contents(int n) {
42-
n = this.length() and result = ""
42+
n < 4 and n = this.length() and result = ""
43+
or
44+
n = 4 and n < this.length() and result = "... " + (this.length()-4).toString() + " more"
4345
or
4446
result = this.getItem(n).toString() + ", " + this.contents(n+1)
4547
}
@@ -145,6 +147,34 @@ class PythonTupleObjectInternal extends TPythonTuple, TupleObjectInternal {
145147

146148
}
147149

150+
class VarargsTupleObjectInternal extends TVarargsTuple, TupleObjectInternal {
151+
152+
override predicate introducedAt(ControlFlowNode node, PointsToContext context) {
153+
none()
154+
}
155+
156+
override Builtin getBuiltin() {
157+
none()
158+
}
159+
160+
override ControlFlowNode getOrigin() {
161+
none()
162+
}
163+
164+
override ObjectInternal getItem(int n) {
165+
exists(CallNode call, PointsToContext context, int offset, int length |
166+
this = TVarargsTuple(call, context, offset, length) and
167+
n < length and
168+
PointsToInternal::pointsTo(call.getArg(offset+n), context, result, _)
169+
)
170+
}
171+
172+
override int length() {
173+
this = TVarargsTuple(_, _, _, result)
174+
}
175+
}
176+
177+
148178
/** The `sys.version_info` object. We treat this specially to prevent premature pruning and
149179
* false positives when we are unsure of the actual version of Python that the code is expecting.
150180
*/

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,11 @@ cached newtype TObject =
179179
context.appliesTo(origin)
180180
}
181181
or
182+
/* Varargs tuple */
183+
TVarargsTuple(CallNode call, PointsToContext context, int offset, int length) {
184+
InterProceduralPointsTo::varargs_tuple(call, _, context, _, offset, length)
185+
}
186+
or
182187
/* `type` */
183188
TType()
184189
or

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

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -899,19 +899,40 @@ module InterProceduralPointsTo {
899899
pragma [noinline]
900900
private predicate special_parameter_points_to(ParameterDefinition def, PointsToContext context, ObjectInternal value, ControlFlowNode origin) {
901901
special_parameter_value(def, value) and
902-
(
903-
context.isRuntime()
904-
or
905-
exists(PointsToContext caller, CallNode call |
906-
context.fromCall(call, caller) and
907-
context.appliesToScope(def.getScope()) and
908-
not exists(call.getArg(def.getParameter().getPosition())) and
909-
not exists(call.getArgByName(def.getParameter().getName()))
910-
)
902+
context.isRuntime() and
903+
origin = def.getDefiningNode()
904+
or
905+
exists(CallNode call, Function scope, PointsToContext caller, int offset, int length |
906+
varargs_tuple(call, scope, caller, context, offset, length) and
907+
value = TVarargsTuple(call, caller, offset, length) and
908+
def.getScope() = scope
909+
) and
910+
origin = def.getDefiningNode()
911+
or
912+
exists(Function scope |
913+
varargs_empty_tuple(scope, context) and
914+
value.(BuiltinTupleObjectInternal).length() = 0 and
915+
def.getScope() = scope
911916
) and
912917
origin = def.getDefiningNode()
913918
}
914919

920+
predicate varargs_tuple(CallNode call, Function scope, PointsToContext caller, PointsToContext callee, int startOffset, int length) {
921+
exists(int parameter_offset |
922+
callsite_calls_function(call, caller, scope, callee, parameter_offset) and
923+
startOffset = scope.getPositionalParameterCount() - parameter_offset and
924+
length = call.getNode().getPositionalArgumentCount() - startOffset and
925+
length > 0
926+
)
927+
}
928+
929+
predicate varargs_empty_tuple(Function scope, PointsToContext callee) {
930+
exists(CallNode call, PointsToContext caller, int parameter_offset |
931+
callsite_calls_function(call, caller, scope, callee, parameter_offset) and
932+
scope.getPositionalParameterCount() - parameter_offset >= call.getNode().getPositionalArgumentCount()
933+
)
934+
}
935+
915936
/** Helper predicate for special_parameter_points_to */
916937
private predicate special_parameter_value(ParameterDefinition p, ObjectInternal value) {
917938
p.isVarargs() and value = TUnknownInstance(ObjectInternal::builtin("tuple"))

python/ql/test/library-tests/PointsTo/new/Call.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
| l_calls.py:10 | ControlFlowNode for bar() | bar |
1717
| l_calls.py:24 | ControlFlowNode for Attribute() | Owner.cm |
1818
| l_calls.py:25 | ControlFlowNode for Attribute() | Owner.cm2 |
19+
| l_calls.py:37 | ControlFlowNode for f() | f |
20+
| l_calls.py:38 | ControlFlowNode for Attribute() | E.m |
21+
| l_calls.py:39 | ControlFlowNode for Attribute() | E.m |
1922
| q_super.py:4 | ControlFlowNode for Attribute() | object.__init__ |
2023
| q_super.py:12 | ControlFlowNode for Attribute() | Base2.__init__ |
2124
| q_super.py:22 | ControlFlowNode for Attribute() | Base1.meth |

python/ql/test/library-tests/PointsTo/new/NameSpace.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,12 +112,15 @@
112112
| k_getsetattr.py:0 | Module code.k_getsetattr | k | Function k |
113113
| k_getsetattr.py:4 | Class C | meth1 | Function meth1 |
114114
| k_getsetattr.py:4 | Class C | meth2 | Function meth2 |
115+
| l_calls.py:0 | Module code.l_calls | E | class E |
115116
| l_calls.py:0 | Module code.l_calls | Owner | class Owner |
116117
| l_calls.py:0 | Module code.l_calls | bar | Function bar |
118+
| l_calls.py:0 | Module code.l_calls | f | Function f |
117119
| l_calls.py:0 | Module code.l_calls | foo | Function foo |
118120
| l_calls.py:12 | Class Owner | cm | classmethod() |
119121
| l_calls.py:12 | Class Owner | cm2 | classmethod() |
120122
| l_calls.py:12 | Class Owner | m | Function m |
123+
| l_calls.py:32 | Class E | m | Function m |
121124
| o_no_returns.py:0 | Module code.o_no_returns | bar | Function bar |
122125
| o_no_returns.py:0 | Module code.o_no_returns | fail | Function fail |
123126
| o_no_returns.py:0 | Module code.o_no_returns | foo | Function foo |

python/ql/test/library-tests/PointsTo/new/PointsToWithContext.expected

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,43 @@ WARNING: Predicate points_to has been deprecated and may be removed in future (P
598598
| l_calls.py:25 | ControlFlowNode for Attribute() | int 1 | builtin-class int | 25 | runtime |
599599
| l_calls.py:25 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 25 | runtime |
600600
| l_calls.py:25 | ControlFlowNode for a | class Owner | builtin-class type | 12 | runtime |
601+
| l_calls.py:29 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 29 | import |
602+
| l_calls.py:29 | ControlFlowNode for args | args | builtin-class tuple | 29 | runtime |
603+
| l_calls.py:29 | ControlFlowNode for f | Function f | builtin-class function | 29 | import |
604+
| l_calls.py:30 | ControlFlowNode for args | args | builtin-class tuple | 29 | code/l_calls.py:37 from import |
605+
| l_calls.py:30 | ControlFlowNode for args | args | builtin-class tuple | 29 | runtime |
606+
| l_calls.py:32 | ControlFlowNode for ClassExpr | class E | builtin-class type | 32 | import |
607+
| l_calls.py:32 | ControlFlowNode for E | class E | builtin-class type | 32 | import |
608+
| l_calls.py:32 | ControlFlowNode for object | builtin-class object | builtin-class type | 32 | import |
609+
| l_calls.py:33 | ControlFlowNode for FunctionExpr | Function m | builtin-class function | 33 | import |
610+
| l_calls.py:33 | ControlFlowNode for args | args | builtin-class tuple | 33 | runtime |
611+
| l_calls.py:33 | ControlFlowNode for m | Function m | builtin-class function | 33 | import |
612+
| l_calls.py:34 | ControlFlowNode for self | E() | class E | 38 | code/l_calls.py:38 from import |
613+
| l_calls.py:34 | ControlFlowNode for self | int 3 | builtin-class int | 39 | code/l_calls.py:39 from import |
614+
| l_calls.py:34 | ControlFlowNode for self | self | builtin-class tuple | 33 | code/l_calls.py:38 from import |
615+
| l_calls.py:34 | ControlFlowNode for self | self | builtin-class tuple | 33 | code/l_calls.py:39 from import |
616+
| l_calls.py:34 | ControlFlowNode for self | self | class E | 33 | runtime |
617+
| l_calls.py:35 | ControlFlowNode for args | args | builtin-class tuple | 33 | code/l_calls.py:38 from import |
618+
| l_calls.py:35 | ControlFlowNode for args | args | builtin-class tuple | 33 | code/l_calls.py:39 from import |
619+
| l_calls.py:35 | ControlFlowNode for args | args | builtin-class tuple | 33 | runtime |
620+
| l_calls.py:37 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 37 | import |
621+
| l_calls.py:37 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 37 | import |
622+
| l_calls.py:37 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 37 | import |
623+
| l_calls.py:37 | ControlFlowNode for f | Function f | builtin-class function | 29 | import |
624+
| l_calls.py:37 | ControlFlowNode for f() | args | builtin-class tuple | 29 | import |
625+
| l_calls.py:38 | ControlFlowNode for Attribute | Attribute | builtin-class method | 38 | import |
626+
| l_calls.py:38 | ControlFlowNode for Attribute() | args | builtin-class tuple | 33 | import |
627+
| l_calls.py:38 | ControlFlowNode for E | class E | builtin-class type | 32 | import |
628+
| l_calls.py:38 | ControlFlowNode for E() | E() | class E | 38 | import |
629+
| l_calls.py:38 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 38 | import |
630+
| l_calls.py:38 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 38 | import |
631+
| l_calls.py:38 | ControlFlowNode for IntegerLiteral | int 4 | builtin-class int | 38 | import |
632+
| l_calls.py:39 | ControlFlowNode for Attribute | Function m | builtin-class function | 33 | import |
633+
| l_calls.py:39 | ControlFlowNode for Attribute() | args | builtin-class tuple | 33 | import |
634+
| l_calls.py:39 | ControlFlowNode for E | class E | builtin-class type | 32 | import |
635+
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 39 | import |
636+
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 4 | builtin-class int | 39 | import |
637+
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 5 | builtin-class int | 39 | import |
601638
| m_attributes.py:3 | ControlFlowNode for C | class C | builtin-class type | 3 | import |
602639
| m_attributes.py:3 | ControlFlowNode for ClassExpr | class C | builtin-class type | 3 | import |
603640
| m_attributes.py:3 | ControlFlowNode for object | builtin-class object | builtin-class type | 3 | import |

python/ql/test/library-tests/PointsTo/new/PointsToWithType.expected

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,39 @@ WARNING: Predicate points_to has been deprecated and may be removed in future (P
675675
| l_calls.py:25 | ControlFlowNode for Attribute() | int 1 | builtin-class int | 25 |
676676
| l_calls.py:25 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 25 |
677677
| l_calls.py:25 | ControlFlowNode for a | class Owner | builtin-class type | 12 |
678+
| l_calls.py:29 | ControlFlowNode for FunctionExpr | Function f | builtin-class function | 29 |
679+
| l_calls.py:29 | ControlFlowNode for args | args | builtin-class tuple | 29 |
680+
| l_calls.py:29 | ControlFlowNode for f | Function f | builtin-class function | 29 |
681+
| l_calls.py:30 | ControlFlowNode for args | args | builtin-class tuple | 29 |
682+
| l_calls.py:32 | ControlFlowNode for ClassExpr | class E | builtin-class type | 32 |
683+
| l_calls.py:32 | ControlFlowNode for E | class E | builtin-class type | 32 |
684+
| l_calls.py:32 | ControlFlowNode for object | builtin-class object | builtin-class type | 32 |
685+
| l_calls.py:33 | ControlFlowNode for FunctionExpr | Function m | builtin-class function | 33 |
686+
| l_calls.py:33 | ControlFlowNode for args | args | builtin-class tuple | 33 |
687+
| l_calls.py:33 | ControlFlowNode for m | Function m | builtin-class function | 33 |
688+
| l_calls.py:34 | ControlFlowNode for self | E() | class E | 38 |
689+
| l_calls.py:34 | ControlFlowNode for self | int 3 | builtin-class int | 39 |
690+
| l_calls.py:34 | ControlFlowNode for self | self | builtin-class tuple | 33 |
691+
| l_calls.py:34 | ControlFlowNode for self | self | class E | 33 |
692+
| l_calls.py:35 | ControlFlowNode for args | args | builtin-class tuple | 33 |
693+
| l_calls.py:37 | ControlFlowNode for IntegerLiteral | int 1 | builtin-class int | 37 |
694+
| l_calls.py:37 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 37 |
695+
| l_calls.py:37 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 37 |
696+
| l_calls.py:37 | ControlFlowNode for f | Function f | builtin-class function | 29 |
697+
| l_calls.py:37 | ControlFlowNode for f() | args | builtin-class tuple | 29 |
698+
| l_calls.py:38 | ControlFlowNode for Attribute | Attribute | builtin-class method | 38 |
699+
| l_calls.py:38 | ControlFlowNode for Attribute() | args | builtin-class tuple | 33 |
700+
| l_calls.py:38 | ControlFlowNode for E | class E | builtin-class type | 32 |
701+
| l_calls.py:38 | ControlFlowNode for E() | E() | class E | 38 |
702+
| l_calls.py:38 | ControlFlowNode for IntegerLiteral | int 2 | builtin-class int | 38 |
703+
| l_calls.py:38 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 38 |
704+
| l_calls.py:38 | ControlFlowNode for IntegerLiteral | int 4 | builtin-class int | 38 |
705+
| l_calls.py:39 | ControlFlowNode for Attribute | Function m | builtin-class function | 33 |
706+
| l_calls.py:39 | ControlFlowNode for Attribute() | args | builtin-class tuple | 33 |
707+
| l_calls.py:39 | ControlFlowNode for E | class E | builtin-class type | 32 |
708+
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 3 | builtin-class int | 39 |
709+
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 4 | builtin-class int | 39 |
710+
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | int 5 | builtin-class int | 39 |
678711
| s_scopes.py:4 | ControlFlowNode for True | bool True | builtin-class bool | 4 |
679712
| s_scopes.py:4 | ControlFlowNode for float | bool True | builtin-class bool | 4 |
680713
| s_scopes.py:7 | ControlFlowNode for C2 | class C2 | builtin-class type | 7 |

python/ql/test/library-tests/PointsTo/new/Values.expected

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,38 @@
466466
| l_calls.py:25 | ControlFlowNode for Attribute() | runtime | int 1 | builtin-class int |
467467
| l_calls.py:25 | ControlFlowNode for IntegerLiteral | runtime | int 1 | builtin-class int |
468468
| l_calls.py:25 | ControlFlowNode for a | runtime | class Owner | builtin-class type |
469+
| l_calls.py:29 | ControlFlowNode for FunctionExpr | import | Function f | builtin-class function |
470+
| l_calls.py:30 | ControlFlowNode for args | code/l_calls.py:37 from import | (int 1, int 2, int 3, ) | builtin-class tuple |
471+
| l_calls.py:30 | ControlFlowNode for args | runtime | instance of tuple | builtin-class tuple |
472+
| l_calls.py:32 | ControlFlowNode for ClassExpr | import | class E | builtin-class type |
473+
| l_calls.py:32 | ControlFlowNode for object | import | builtin-class object | builtin-class type |
474+
| l_calls.py:33 | ControlFlowNode for FunctionExpr | import | Function E.m | builtin-class function |
475+
| l_calls.py:34 | ControlFlowNode for self | code/l_calls.py:38 from import | (int 2, int 3, int 4, ) | builtin-class tuple |
476+
| l_calls.py:34 | ControlFlowNode for self | code/l_calls.py:38 from import | E() | class E |
477+
| l_calls.py:34 | ControlFlowNode for self | code/l_calls.py:39 from import | (int 4, int 5, ) | builtin-class tuple |
478+
| l_calls.py:34 | ControlFlowNode for self | code/l_calls.py:39 from import | int 3 | builtin-class int |
479+
| l_calls.py:34 | ControlFlowNode for self | runtime | self instance of E | class E |
480+
| l_calls.py:35 | ControlFlowNode for args | code/l_calls.py:38 from import | (int 2, int 3, int 4, ) | builtin-class tuple |
481+
| l_calls.py:35 | ControlFlowNode for args | code/l_calls.py:39 from import | (int 4, int 5, ) | builtin-class tuple |
482+
| l_calls.py:35 | ControlFlowNode for args | runtime | instance of tuple | builtin-class tuple |
483+
| l_calls.py:37 | ControlFlowNode for IntegerLiteral | import | int 1 | builtin-class int |
484+
| l_calls.py:37 | ControlFlowNode for IntegerLiteral | import | int 2 | builtin-class int |
485+
| l_calls.py:37 | ControlFlowNode for IntegerLiteral | import | int 3 | builtin-class int |
486+
| l_calls.py:37 | ControlFlowNode for f | import | Function f | builtin-class function |
487+
| l_calls.py:37 | ControlFlowNode for f() | import | (int 1, int 2, int 3, ) | builtin-class tuple |
488+
| l_calls.py:38 | ControlFlowNode for Attribute | import | Method(Function E.m, E()) | builtin-class method |
489+
| l_calls.py:38 | ControlFlowNode for Attribute() | import | (int 2, int 3, int 4, ) | builtin-class tuple |
490+
| l_calls.py:38 | ControlFlowNode for E | import | class E | builtin-class type |
491+
| l_calls.py:38 | ControlFlowNode for E() | import | E() | class E |
492+
| l_calls.py:38 | ControlFlowNode for IntegerLiteral | import | int 2 | builtin-class int |
493+
| l_calls.py:38 | ControlFlowNode for IntegerLiteral | import | int 3 | builtin-class int |
494+
| l_calls.py:38 | ControlFlowNode for IntegerLiteral | import | int 4 | builtin-class int |
495+
| l_calls.py:39 | ControlFlowNode for Attribute | import | Function E.m | builtin-class function |
496+
| l_calls.py:39 | ControlFlowNode for Attribute() | import | (int 4, int 5, ) | builtin-class tuple |
497+
| l_calls.py:39 | ControlFlowNode for E | import | class E | builtin-class type |
498+
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | import | int 3 | builtin-class int |
499+
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | import | int 4 | builtin-class int |
500+
| l_calls.py:39 | ControlFlowNode for IntegerLiteral | import | int 5 | builtin-class int |
469501
| m_attributes.py:3 | ControlFlowNode for ClassExpr | import | class C | builtin-class type |
470502
| m_attributes.py:3 | ControlFlowNode for object | import | builtin-class object | builtin-class type |
471503
| m_attributes.py:5 | ControlFlowNode for FunctionExpr | import | Function C.__init__ | builtin-class function |

python/ql/test/library-tests/PointsTo/new/code/l_calls.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,16 @@ def m(self):
2424
a = self.cm(0)
2525
return a.cm2(1)
2626

27+
# *args
28+
29+
def f(*args):
30+
return args
31+
32+
class E(object):
33+
def m(self, *args):
34+
self
35+
return args
36+
37+
f(1, 2, 3)
38+
E().m(2, 3, 4)
39+
E.m(3, 4, 5)

0 commit comments

Comments
 (0)