Skip to content

Commit 5e528f9

Browse files
committed
Discard predicates for dbscheme elements
with direct or indirect location links in dbscheme.
1 parent 78b213f commit 5e528f9

File tree

2 files changed

+225
-0
lines changed

2 files changed

+225
-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: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
/**
2+
* Defines entity discard predicates for Python overlay analysis.
3+
*/
4+
overlay[local?]
5+
module;
6+
7+
import python
8+
9+
/*- Predicates -*/
10+
/**
11+
* Holds always for the overlay variant and never for the base variant.
12+
* This local predicate is used to define local predicates that behave
13+
* differently for the base and overlay variant.
14+
*/
15+
overlay[local]
16+
predicate isOverlay() { databaseMetadata("isOverlay", "true") }
17+
18+
overlay[local]
19+
private string getRawPathForLocation(@location loc) {
20+
exists(@file file | locations_default(loc, file, _, _, _, _) | files(file, result))
21+
or
22+
exists(@py_Module mod | locations_ast(loc, mod, _, _, _, _) | result = getRawPathForModule(mod))
23+
}
24+
25+
overlay[local]
26+
private string getRawPathForModule(@py_Module mod) {
27+
exists(@container fileOrFolder | py_module_path(mod, fileOrFolder) |
28+
result = getRawPathForContainer(fileOrFolder)
29+
)
30+
}
31+
32+
overlay[local]
33+
private string getRawPathForContainer(@container fileOrFolder) {
34+
files(fileOrFolder, result) or folders(fileOrFolder, result)
35+
}
36+
37+
/*- Source elements -*/
38+
/**
39+
* An abstract base class for all elements that can be discarded from the base.
40+
*/
41+
overlay[local]
42+
abstract private class Discardable extends @py_source_element {
43+
/** Gets the path to the file in which this element occurs. */
44+
abstract string getPath();
45+
46+
/** Holds if this element exists in the base variant. */
47+
predicate existsInBase() { not isOverlay() and exists(this) }
48+
49+
/** Holds if this element exists in the overlay variant. */
50+
predicate existsInOverlay() { isOverlay() and exists(this) }
51+
52+
/** Gets a textual representation of this discardable element. */
53+
string toString() { none() }
54+
}
55+
56+
overlay[discard_entity]
57+
private predicate discardEntity(@py_source_element el) {
58+
exists(Discardable d | d = el |
59+
overlayChangedFiles(d.getPath()) and
60+
d.existsInBase() and
61+
not d.existsInOverlay()
62+
)
63+
}
64+
65+
/**
66+
* Discard all locatable AST nodes (`@py_location_parent`) in modified files
67+
* since they use *-ids and hence cannot be referenced across TRAP files.
68+
*/
69+
overlay[local]
70+
final private class DiscardableLocatable extends Discardable instanceof @py_location_parent {
71+
override string getPath() {
72+
exists(@location loc | py_locations(loc, this) | result = getRawPathForLocation(loc))
73+
}
74+
}
75+
76+
/**
77+
* Discard scopes (classes, functions, modules) that were deleted in the overlay.
78+
*/
79+
overlay[local]
80+
final private class DiscardableScope extends Discardable instanceof @py_scope {
81+
override string getPath() {
82+
exists(@location loc | py_scope_location(loc, this) | result = getRawPathForLocation(loc))
83+
or
84+
result = getRawPathForModule(this)
85+
}
86+
}
87+
88+
/**
89+
* Discard files and folders that were deleted in the overlay.
90+
*/
91+
overlay[local]
92+
final private class DiscardableContainer extends Discardable instanceof @container {
93+
override string getPath() { result = getRawPathForContainer(this) }
94+
}
95+
96+
/*- CFG Nodes -*/
97+
/** Discardable control flow nodes */
98+
overlay[local]
99+
final private class DiscardableCfgNode instanceof @py_flow_node {
100+
string getPath() {
101+
exists(Discardable d | result = d.getPath() |
102+
py_flow_bb_node(this, d.(@py_ast_node), _, _)
103+
or
104+
py_scope_flow(this, d.(@py_scope), _)
105+
)
106+
}
107+
108+
predicate existsInBase() { not isOverlay() and exists(this) }
109+
110+
predicate existsInOverlay() { isOverlay() and exists(this) }
111+
112+
string toString() { none() }
113+
}
114+
115+
overlay[discard_entity]
116+
private predicate discardCfgNode(@py_flow_node n) {
117+
exists(DiscardableCfgNode d | d = n |
118+
overlayChangedFiles(d.getPath()) and
119+
d.existsInBase() and
120+
not d.existsInOverlay()
121+
)
122+
}
123+
124+
/*- Variables -*/
125+
/** Discardable (normal and SSA) variables */
126+
overlay[local]
127+
abstract private class DiscardableBaseVar instanceof @py_base_var {
128+
abstract string getPath();
129+
130+
predicate existsInBase() { not isOverlay() and exists(this) }
131+
132+
predicate existsInOverlay() { isOverlay() and exists(this) }
133+
134+
string toString() { none() }
135+
}
136+
137+
overlay[discard_entity]
138+
private predicate discardVar(@py_base_var n) {
139+
exists(DiscardableVar d | d = n |
140+
overlayChangedFiles(d.getPath()) and
141+
d.existsInBase() and
142+
not d.existsInOverlay()
143+
)
144+
}
145+
146+
final private class DiscardableVar extends DiscardableBaseVar instanceof @py_variable {
147+
override string getPath() {
148+
exists(Discardable parent | result = parent.getPath() |
149+
variable(this, parent.(@py_scope), _)
150+
or
151+
py_variables(this, parent.(@py_variable_parent))
152+
)
153+
}
154+
}
155+
156+
final private class DiscardableSsaVar extends DiscardableBaseVar instanceof @py_ssa_var {
157+
override string getPath() {
158+
exists(DiscardableSsaVar other | result = other.getPath() |
159+
py_ssa_phi(this, other.(@py_ssa_var))
160+
)
161+
or
162+
exists(DiscardableVar other | result = other.getPath() | py_ssa_var(this, other))
163+
or
164+
exists(DiscardableCfgNode node | result = node.getPath() |
165+
py_ssa_use(node, this)
166+
or
167+
py_ssa_defn(this, node)
168+
)
169+
}
170+
}
171+
172+
/*- Locations -*/
173+
overlay[local]
174+
private predicate locationExistsInBase(@location loc) { not isOverlay() and exists(loc) }
175+
176+
overlay[local]
177+
private predicate locationExistsInOverlay(@location loc) { isOverlay() and exists(loc) }
178+
179+
overlay[discard_entity]
180+
private predicate discardLocation(@location loc) {
181+
overlayChangedFiles(getRawPathForLocation(loc)) and
182+
locationExistsInBase(loc) and
183+
not locationExistsInOverlay(loc)
184+
}
185+
186+
/*- XML -*/
187+
overlay[local]
188+
private predicate baseXmlLocatable(@xmllocatable l) {
189+
not isOverlay() and not files(l, _) and not xmlNs(l, _, _, _)
190+
}
191+
192+
overlay[local]
193+
private predicate overlayHasXmlLocatable() {
194+
isOverlay() and
195+
exists(@xmllocatable l | not files(l, _) and not xmlNs(l, _, _, _))
196+
}
197+
198+
overlay[discard_entity]
199+
private predicate discardBaseXmlLocatable(@xmllocatable el) {
200+
// The XML extractor is currently not incremental, so if
201+
// the overlay contains any XML locatables, the overlay should
202+
// contain a full extraction and all XML locatables from base
203+
// should be discarded.
204+
baseXmlLocatable(el) and overlayHasXmlLocatable()
205+
}
206+
207+
/*- YAML -*/
208+
overlay[local]
209+
private predicate baseYamlLocatable(@yaml_locatable l) { not isOverlay() and exists(l) }
210+
211+
overlay[local]
212+
private predicate overlayHasYamlLocatable() {
213+
isOverlay() and
214+
exists(@yaml_locatable l)
215+
}
216+
217+
overlay[discard_entity]
218+
private predicate discardBaseYamlLocatable(@yaml_locatable el) {
219+
// The Yaml extractor is currently not incremental, so if
220+
// the overlay contains any Yaml locatables, the overlay should
221+
// contain a full extraction and all Yaml locatables from base
222+
// should be discarded.
223+
baseYamlLocatable(el) and overlayHasYamlLocatable()
224+
}

0 commit comments

Comments
 (0)