Skip to content

Commit 7174d4c

Browse files
committed
Overlay.qll: discard predicates
for dbscheme elements with direct or indirect location links in dbscheme. - Unify discardable entities under one Discardable superclass. - Two discard predicates depending on TRAP ID type. - Future-proof the XML and Yaml discard predicates for when their extractors become incremental.
1 parent 1a9683f commit 7174d4c

File tree

2 files changed

+356
-0
lines changed

2 files changed

+356
-0
lines changed

python/ql/lib/python.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import semmle.python.pointsto.CallGraph
3737
import semmle.python.objects.ObjectAPI
3838
import semmle.python.Unit
3939
import site
40+
private import semmle.python.Overlay
4041
// Removing this import perturbs the compilation process enough that the points-to analysis gets
4142
// compiled -- and cached -- differently depending on whether the data flow library is imported. By
4243
// importing it privately here, we ensure that the points-to analysis is compiled the same way.
Lines changed: 355 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,355 @@
1+
/**
2+
* Defines entity discard predicates for Python overlay analysis.
3+
*/
4+
5+
/*- Predicates -*/
6+
/**
7+
* Holds always for the overlay variant and never for the base variant.
8+
* This local predicate is used to define local predicates that behave
9+
* differently for the base and overlay variant.
10+
*/
11+
overlay[local]
12+
predicate isOverlay() { databaseMetadata("isOverlay", "true") }
13+
14+
overlay[local]
15+
private string getPathForLocation(@location loc) {
16+
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
17+
or
18+
exists(@py_Module mod | locations_ast(loc, mod, _, _, _, _) | result = getPathForModule(mod))
19+
}
20+
21+
overlay[local]
22+
private string getPathForModule(@py_Module mod) {
23+
exists(@container fileOrFolder | py_module_path(mod, fileOrFolder) |
24+
result = getPathForContainer(fileOrFolder)
25+
)
26+
}
27+
28+
overlay[local]
29+
private string getPathForContainer(@container fileOrFolder) {
30+
files(fileOrFolder, result) or folders(fileOrFolder, result)
31+
}
32+
33+
/*- Discardable entities and their discard predicates -*/
34+
/** Python database entities that use named TRAP IDs; the rest use *-ids. */
35+
overlay[local]
36+
private class NamedEntity = @py_Module or @container or @py_cobject;
37+
38+
overlay[discard_entity]
39+
private predicate discardNamedEntity(@top el) {
40+
el instanceof NamedEntity and
41+
// Entities with named IDs can exist both in base, overlay, or both.
42+
exists(Discardable d | d = el |
43+
overlayChangedFiles(d.getPath()) and
44+
not d.existsInOverlay()
45+
)
46+
}
47+
48+
overlay[discard_entity]
49+
private predicate discardStarEntity(@top el) {
50+
not el instanceof NamedEntity and
51+
// Entities with *-ids can exist either in base or overlay, but not both.
52+
exists(Discardable d | d = el |
53+
overlayChangedFiles(d.getPath()) and
54+
d.existsInBase()
55+
)
56+
}
57+
58+
/**
59+
* An abstract base class for all elements that can be discarded from the base.
60+
*/
61+
overlay[local]
62+
abstract class Discardable extends @top {
63+
/** Gets the path to the file in which this element occurs. */
64+
abstract string getPath();
65+
66+
/** Holds if this element exists in the base variant. */
67+
predicate existsInBase() { not isOverlay() and exists(this) }
68+
69+
/** Holds if this element exists in the overlay variant. */
70+
predicate existsInOverlay() { isOverlay() and exists(this) }
71+
72+
/** Gets a textual representation of this discardable element. */
73+
string toString() { none() }
74+
}
75+
76+
/**
77+
* Discardable locatable AST nodes (`@py_location_parent`).
78+
*/
79+
overlay[local]
80+
final private class DiscardableLocatable extends Discardable instanceof @py_location_parent {
81+
override string getPath() {
82+
exists(@location loc | py_locations(loc, this) | result = getPathForLocation(loc))
83+
}
84+
}
85+
86+
/**
87+
* Discardable scopes (classes, functions, modules).
88+
*/
89+
overlay[local]
90+
final private class DiscardableScope extends Discardable instanceof @py_scope {
91+
override string getPath() {
92+
exists(@location loc | py_scope_location(loc, this) | result = getPathForLocation(loc))
93+
or
94+
result = getPathForModule(this)
95+
}
96+
}
97+
98+
/**
99+
* Discardable files and folders.
100+
*/
101+
overlay[local]
102+
final private class DiscardableContainer extends Discardable instanceof @container {
103+
override string getPath() { result = getPathForContainer(this) }
104+
}
105+
106+
/** Discardable control flow nodes */
107+
overlay[local]
108+
final private class DiscardableCfgNode extends Discardable instanceof @py_flow_node {
109+
override string getPath() {
110+
exists(Discardable d | result = d.getPath() | py_flow_bb_node(this, d.(@py_ast_node), _, _))
111+
}
112+
}
113+
114+
/** Discardable Python variables. */
115+
overlay[local]
116+
final private class DiscardableVar extends Discardable instanceof @py_variable {
117+
override string getPath() {
118+
exists(Discardable parent | result = parent.getPath() | variable(this, parent.(@py_scope), _))
119+
}
120+
}
121+
122+
/** Discardable SSA variables. */
123+
overlay[local]
124+
final private class DiscardableSsaVar extends Discardable instanceof @py_ssa_var {
125+
override string getPath() {
126+
exists(DiscardableVar other | result = other.getPath() | py_ssa_var(this, other))
127+
}
128+
}
129+
130+
/** Discardable locations. */
131+
overlay[local]
132+
final private class DiscardableLocation extends Discardable instanceof @location {
133+
override string getPath() { result = getPathForLocation(this) }
134+
}
135+
136+
/** Discardable lines. */
137+
overlay[local]
138+
final private class DiscardableLine extends Discardable instanceof @py_line {
139+
override string getPath() {
140+
exists(Discardable d | result = d.getPath() | py_line_lengths(this, d.(@py_Module), _, _))
141+
}
142+
}
143+
144+
/** Discardable string part lists. */
145+
overlay[local]
146+
final private class DiscardableStringPartList extends Discardable instanceof @py_StringPart_list {
147+
override string getPath() {
148+
exists(Discardable d | result = d.getPath() | py_StringPart_lists(this, d.(@py_Bytes_or_Str)))
149+
}
150+
}
151+
152+
/** Discardable alias */
153+
overlay[local]
154+
final private class DiscardableAlias extends Discardable instanceof @py_alias {
155+
override string getPath() {
156+
exists(DiscardableAliasList d | result = d.getPath() | py_aliases(this, d, _))
157+
}
158+
}
159+
160+
/** Discardable alias list */
161+
overlay[local]
162+
final private class DiscardableAliasList extends Discardable instanceof @py_alias_list {
163+
override string getPath() {
164+
exists(Discardable d | result = d.getPath() | py_alias_lists(this, d.(@py_Import)))
165+
}
166+
}
167+
168+
/** Discardable arguments */
169+
overlay[local]
170+
final private class DiscardableArguments extends Discardable instanceof @py_arguments {
171+
override string getPath() {
172+
exists(Discardable d | result = d.getPath() | py_arguments(this, d.(@py_arguments_parent)))
173+
}
174+
}
175+
176+
/** Discardable boolop */
177+
overlay[local]
178+
final private class DiscardableBoolOp extends Discardable instanceof @py_boolop {
179+
override string getPath() {
180+
exists(Discardable d | result = d.getPath() | py_boolops(this, _, d.(@py_BoolExpr)))
181+
}
182+
}
183+
184+
/** Discardable cmpop */
185+
overlay[local]
186+
final private class DiscardableCmpOp extends Discardable instanceof @py_cmpop {
187+
override string getPath() {
188+
exists(DiscardableCmpOpList d | result = d.getPath() | py_cmpops(this, _, d, _))
189+
}
190+
}
191+
192+
/** Discardable cmpop list */
193+
overlay[local]
194+
final private class DiscardableCmpOpList extends Discardable instanceof @py_cmpop_list {
195+
override string getPath() {
196+
exists(Discardable d | result = d.getPath() | py_cmpop_lists(this, d.(@py_Compare)))
197+
}
198+
}
199+
200+
/** Discardable comprehension list */
201+
overlay[local]
202+
final private class DiscardableComprehensionList extends Discardable instanceof @py_comprehension_list
203+
{
204+
override string getPath() {
205+
exists(Discardable d | result = d.getPath() | py_comprehension_lists(this, d.(@py_ListComp)))
206+
}
207+
}
208+
209+
/** Discardable dict item list */
210+
overlay[local]
211+
final private class DiscardableDictItemList extends Discardable instanceof @py_dict_item_list {
212+
override string getPath() {
213+
exists(Discardable d | result = d.getPath() |
214+
py_dict_item_lists(this, d.(@py_dict_item_list_parent))
215+
)
216+
}
217+
}
218+
219+
/** Discardable expr context */
220+
overlay[local]
221+
final private class DiscardableExprContext extends Discardable instanceof @py_expr_context {
222+
override string getPath() {
223+
exists(Discardable d | result = d.getPath() |
224+
py_expr_contexts(this, _, d.(@py_expr_context_parent))
225+
)
226+
}
227+
}
228+
229+
/** Discardable expr list */
230+
overlay[local]
231+
final private class DiscardableExprList extends Discardable instanceof @py_expr_list {
232+
override string getPath() {
233+
exists(Discardable d | result = d.getPath() | py_expr_lists(this, d.(@py_expr_list_parent), _))
234+
}
235+
}
236+
237+
/** Discardable operator */
238+
overlay[local]
239+
final private class DiscardableOperator extends Discardable instanceof @py_operator {
240+
override string getPath() {
241+
exists(Discardable d | result = d.getPath() | py_operators(this, _, d.(@py_BinaryExpr)))
242+
}
243+
}
244+
245+
/** Discardable parameter list */
246+
overlay[local]
247+
final private class DiscardableParameterList extends Discardable instanceof @py_parameter_list {
248+
override string getPath() {
249+
exists(Discardable d | result = d.getPath() | py_parameter_lists(this, d.(@py_Function)))
250+
}
251+
}
252+
253+
/** Discardable pattern list */
254+
overlay[local]
255+
final private class DiscardablePatternList extends Discardable instanceof @py_pattern_list {
256+
override string getPath() {
257+
exists(Discardable d | result = d.getPath() |
258+
py_pattern_lists(this, d.(@py_pattern_list_parent), _)
259+
)
260+
}
261+
}
262+
263+
/** Discardable stmt list */
264+
overlay[local]
265+
final private class DiscardableStmtList extends Discardable instanceof @py_stmt_list {
266+
override string getPath() {
267+
exists(Discardable d | result = d.getPath() | py_stmt_lists(this, d.(@py_stmt_list_parent), _))
268+
}
269+
}
270+
271+
/** Discardable str list */
272+
overlay[local]
273+
final private class DiscardableStrList extends Discardable instanceof @py_str_list {
274+
override string getPath() {
275+
exists(Discardable d | result = d.getPath() | py_str_lists(this, d.(@py_str_list_parent)))
276+
}
277+
}
278+
279+
/** Discardable type parameter list */
280+
overlay[local]
281+
final private class DiscardableTypeParameterList extends Discardable instanceof @py_type_parameter_list
282+
{
283+
override string getPath() {
284+
exists(Discardable d | result = d.getPath() |
285+
py_type_parameter_lists(this, d.(@py_type_parameter_list_parent))
286+
)
287+
}
288+
}
289+
290+
/** Discardable unaryop */
291+
overlay[local]
292+
final private class DiscardableUnaryOp extends Discardable instanceof @py_unaryop {
293+
override string getPath() {
294+
exists(Discardable d | result = d.getPath() | py_unaryops(this, _, d.(@py_UnaryExpr)))
295+
}
296+
}
297+
298+
/** Discardable comment */
299+
overlay[local]
300+
final private class DiscardableComment extends Discardable instanceof @py_comment {
301+
override string getPath() {
302+
exists(DiscardableLocation d | result = d.getPath() | py_comments(this, _, d))
303+
}
304+
}
305+
306+
/*- XML -*/
307+
overlay[local]
308+
final private class DiscardableXmlLocatable extends Discardable instanceof @xmllocatable {
309+
override string getPath() {
310+
exists(@location loc | xmllocations(this, loc) | result = getPathForLocation(loc))
311+
}
312+
}
313+
314+
overlay[local]
315+
private predicate overlayXmlExtracted(string path) {
316+
exists(DiscardableXmlLocatable d | not files(d, _) and not xmlNs(d, _, _, _) |
317+
d.existsInOverlay() and
318+
path = d.getPath()
319+
)
320+
}
321+
322+
overlay[discard_entity]
323+
private predicate discardXmlLocatable(@xmllocatable el) {
324+
exists(DiscardableXmlLocatable d | d = el |
325+
// The XML extractor is currently not incremental and may extract more
326+
// XML files than those included in `overlayChangedFiles`, so this discard predicate
327+
// handles those files alongside the normal `discardStarEntity` logic.
328+
overlayXmlExtracted(d.getPath()) and
329+
d.existsInBase()
330+
)
331+
}
332+
333+
/*- YAML -*/
334+
overlay[local]
335+
final private class DiscardableYamlLocatable extends Discardable instanceof @yaml_locatable {
336+
override string getPath() {
337+
exists(@location loc | yaml_locations(this, loc) | result = getPathForLocation(loc))
338+
}
339+
}
340+
341+
overlay[local]
342+
private predicate overlayYamlExtracted(string path) {
343+
exists(DiscardableYamlLocatable l | l.existsInOverlay() | path = l.getPath())
344+
}
345+
346+
overlay[discard_entity]
347+
private predicate discardBaseYamlLocatable(@yaml_locatable el) {
348+
exists(DiscardableYamlLocatable d | d = el |
349+
// The Yaml extractor is currently not incremental and may extract more
350+
// Yaml files than those included in `overlayChangedFiles`, so this discard predicate
351+
// handles those files alongside the normal `discardStarEntity` logic.
352+
overlayYamlExtracted(d.getPath()) and
353+
d.existsInBase()
354+
)
355+
}

0 commit comments

Comments
 (0)