Skip to content

Commit 8531174

Browse files
authored
Merge pull request #333 from github/hvitved/api-graphs-non-linear-rec
API graphs: Avoid non-linear recursion
2 parents 80ebfed + 98d1ee5 commit 8531174

File tree

1 file changed

+31
-14
lines changed

1 file changed

+31
-14
lines changed

ql/lib/codeql/ruby/ApiGraphs.qll

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ module API {
188188
/** A node corresponding to the use of an API component. */
189189
class Use extends Node, Impl::MkUse {
190190
override string toString() {
191-
exists(string type | this = Impl::MkUse(_) and type = "Use " |
191+
exists(string type | type = "Use " |
192192
result = type + getPath()
193193
or
194194
not exists(this.getPath()) and result = type + "with no path"
@@ -239,20 +239,19 @@ module API {
239239
/** The root of the API graph. */
240240
MkRoot() or
241241
/** A use of an API member at the node `nd`. */
242-
MkUse(DataFlow::Node nd) { use(_, _, nd) }
242+
MkUse(DataFlow::Node nd) { isUse(nd) }
243243

244244
private string resolveTopLevel(ConstantReadAccess read) {
245245
TResolved(result) = resolveScopeExpr(read) and
246246
not result.matches("%::%")
247247
}
248248

249249
/**
250-
* Holds if `ref` is a use of a node that should have an incoming edge from `base` labeled
251-
* `lbl` in the API graph.
250+
* Holds if `ref` is a use of a node that should have an incoming edge from the root
251+
* node labeled `lbl` in the API graph.
252252
*/
253253
cached
254-
predicate use(TApiNode base, string lbl, DataFlow::Node ref) {
255-
base = MkRoot() and
254+
predicate useRoot(string lbl, DataFlow::Node ref) {
256255
exists(string name, ExprNodes::ConstantAccessCfgNode access, ConstantReadAccess read |
257256
access = ref.asExpr() and
258257
lbl = Label::member(read.getName()) and
@@ -264,7 +263,14 @@ module API {
264263
not exists(resolveTopLevel(read)) and
265264
not exists(read.getScopeExpr())
266265
)
267-
or
266+
}
267+
268+
/**
269+
* Holds if `ref` is a use of a node that should have an incoming edge from use node
270+
* `base` labeled `lbl` in the API graph.
271+
*/
272+
cached
273+
predicate useUse(DataFlow::LocalSourceNode base, string lbl, DataFlow::Node ref) {
268274
exists(ExprCfgNode node |
269275
// First, we find a predecessor of the node `ref` that we want to determine. The predecessor
270276
// is any node that is a type-tracked use of a data flow node (`src`), which is itself a
@@ -307,9 +313,15 @@ module API {
307313
}
308314

309315
pragma[nomagic]
310-
private predicate useExpr(ExprCfgNode node, TApiNode base) {
311-
exists(DataFlow::LocalSourceNode src, DataFlow::LocalSourceNode pred |
312-
use(base, src) and
316+
private predicate isUse(DataFlow::Node nd) {
317+
useRoot(_, nd)
318+
or
319+
useUse(_, _, nd)
320+
}
321+
322+
pragma[nomagic]
323+
private predicate useExpr(ExprCfgNode node, DataFlow::LocalSourceNode src) {
324+
exists(DataFlow::LocalSourceNode pred |
313325
pred = trackUseNode(src) and
314326
pred.flowsTo(any(DataFlow::ExprNode n | n.getExprNode() = node))
315327
)
@@ -331,7 +343,7 @@ module API {
331343
// recursive case, so instead we check it explicitly here.
332344
src instanceof DataFlow::LocalSourceNode and
333345
t.start() and
334-
use(_, src) and
346+
isUse(src) and
335347
result = src
336348
or
337349
exists(TypeTracker t2 | result = trackUseNode(src, t2).track(t2, t))
@@ -353,9 +365,14 @@ module API {
353365
cached
354366
predicate edge(TApiNode pred, string lbl, TApiNode succ) {
355367
/* Every node that is a use of an API component is itself added to the API graph. */
356-
exists(DataFlow::LocalSourceNode ref |
357-
use(pred, lbl, ref) and
358-
succ = MkUse(ref)
368+
exists(DataFlow::LocalSourceNode ref | succ = MkUse(ref) |
369+
pred = MkRoot() and
370+
useRoot(lbl, ref)
371+
or
372+
exists(DataFlow::Node nd |
373+
pred = MkUse(nd) and
374+
useUse(nd, lbl, ref)
375+
)
359376
)
360377
}
361378

0 commit comments

Comments
 (0)