Skip to content

Commit f347505

Browse files
authored
Merge pull request #277 from github/hvitved/flow-summaries
Add support for flow summaries
2 parents 83705c5 + 68d41f9 commit f347505

File tree

17 files changed

+1541
-93
lines changed

17 files changed

+1541
-93
lines changed

ql/lib/codeql/ruby/ast/Call.qll

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,8 @@ class Call extends Expr, TCall {
5353

5454
/** Gets a potential target of this call, if any. */
5555
final Callable getATarget() {
56-
exists(DataFlowCall c | this = c.getExpr() |
57-
result = viableCallable(c)
58-
or
59-
result = viableCallableLambda(c, _)
56+
exists(DataFlowCall c | this = c.asCall().getExpr() |
57+
TCfgScope(result) = [viableCallable(c), viableCallableLambda(c, _)]
6058
)
6159
}
6260

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/** Provides classes and predicates for defining flow summaries. */
2+
3+
import ruby
4+
import codeql.ruby.DataFlow
5+
private import internal.FlowSummaryImpl as Impl
6+
private import internal.DataFlowDispatch
7+
8+
// import all instances below
9+
private module Summaries { }
10+
11+
class SummaryComponent = Impl::Public::SummaryComponent;
12+
13+
/** Provides predicates for constructing summary components. */
14+
module SummaryComponent {
15+
private import Impl::Public::SummaryComponent as SC
16+
17+
predicate parameter = SC::parameter/1;
18+
19+
predicate argument = SC::argument/1;
20+
21+
predicate content = SC::content/1;
22+
23+
/** Gets a summary component that represents a qualifier. */
24+
SummaryComponent qualifier() { result = argument(-1) }
25+
26+
/** Gets a summary component that represents a block argument. */
27+
SummaryComponent block() { result = argument(-2) }
28+
29+
/** Gets a summary component that represents the return value of a call. */
30+
SummaryComponent return() { result = SC::return(any(NormalReturnKind rk)) }
31+
}
32+
33+
class SummaryComponentStack = Impl::Public::SummaryComponentStack;
34+
35+
/** Provides predicates for constructing stacks of summary components. */
36+
module SummaryComponentStack {
37+
private import Impl::Public::SummaryComponentStack as SCS
38+
39+
predicate singleton = SCS::singleton/1;
40+
41+
predicate push = SCS::push/2;
42+
43+
predicate argument = SCS::argument/1;
44+
45+
/** Gets a singleton stack representing a qualifier. */
46+
SummaryComponentStack qualifier() { result = singleton(SummaryComponent::qualifier()) }
47+
48+
/** Gets a singleton stack representing a block argument. */
49+
SummaryComponentStack block() { result = singleton(SummaryComponent::block()) }
50+
51+
/** Gets a singleton stack representing the return value of a call. */
52+
SummaryComponentStack return() { result = singleton(SummaryComponent::return()) }
53+
}
54+
55+
/** A callable with a flow summary, identified by a unique string. */
56+
abstract class SummarizedCallable extends LibraryCallable {
57+
bindingset[this]
58+
SummarizedCallable() { any() }
59+
60+
/**
61+
* Holds if data may flow from `input` to `output` through this callable.
62+
*
63+
* `preservesValue` indicates whether this is a value-preserving step
64+
* or a taint-step.
65+
*
66+
* Input specifications are restricted to stacks that end with
67+
* `SummaryComponent::argument(_)`, preceded by zero or more
68+
* `SummaryComponent::return()` or `SummaryComponent::content(_)` components.
69+
*
70+
* Output specifications are restricted to stacks that end with
71+
* `SummaryComponent::return()` or `SummaryComponent::argument(_)`.
72+
*
73+
* Output stacks ending with `SummaryComponent::return()` can be preceded by zero
74+
* or more `SummaryComponent::content(_)` components.
75+
*
76+
* Output stacks ending with `SummaryComponent::argument(_)` can be preceded by an
77+
* optional `SummaryComponent::parameter(_)` component, which in turn can be preceded
78+
* by zero or more `SummaryComponent::content(_)` components.
79+
*/
80+
pragma[nomagic]
81+
predicate propagatesFlow(
82+
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
83+
) {
84+
none()
85+
}
86+
87+
/**
88+
* Same as
89+
*
90+
* ```ql
91+
* propagatesFlow(
92+
* SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
93+
* )
94+
* ```
95+
*
96+
* but uses an external (string) representation of the input and output stacks.
97+
*/
98+
pragma[nomagic]
99+
predicate propagatesFlowExt(string input, string output, boolean preservesValue) { none() }
100+
101+
/**
102+
* Holds if values stored inside `content` are cleared on objects passed as
103+
* the `i`th argument to this callable.
104+
*/
105+
pragma[nomagic]
106+
predicate clearsContent(int i, DataFlow::Content content) { none() }
107+
}
108+
109+
private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable {
110+
private SummarizedCallable sc;
111+
112+
SummarizedCallableAdapter() { this = TLibraryCallable(sc) }
113+
114+
final override predicate propagatesFlow(
115+
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
116+
) {
117+
sc.propagatesFlow(input, output, preservesValue)
118+
}
119+
120+
final override predicate clearsContent(int i, DataFlow::Content content) {
121+
sc.clearsContent(i, content)
122+
}
123+
}
124+
125+
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;

0 commit comments

Comments
 (0)