Skip to content

Commit 4eacbd1

Browse files
committed
Ruby: Sync files
1 parent 6d58dd2 commit 4eacbd1

File tree

1 file changed

+82
-23
lines changed

1 file changed

+82
-23
lines changed

ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImpl.qll

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ module Public {
8585
/** Holds if this stack contains summary component `c`. */
8686
predicate contains(SummaryComponent c) { c = this.drop(_).head() }
8787

88+
/** Gets the bottom element of this stack. */
89+
SummaryComponent bottom() { result = this.drop(this.length() - 1).head() }
90+
8891
/** Gets a textual representation of this stack. */
8992
string toString() {
9093
exists(SummaryComponent head, SummaryComponentStack tail |
@@ -197,6 +200,8 @@ module Private {
197200
or
198201
tail.(RequiredSummaryComponentStack).required(TParameterSummaryComponent(_)) and
199202
head = thisParam()
203+
or
204+
derivedFluentFlowPush(_, _, _, head, tail, _)
200205
}
201206

202207
pragma[nomagic]
@@ -210,7 +215,7 @@ module Private {
210215
c.propagatesFlow(output, input, preservesValue) and
211216
preservesValue = true and
212217
isCallbackParameter(input) and
213-
isContentOfArgument(output)
218+
isContentOfArgument(output, _)
214219
or
215220
// flow from the receiver of a callback into the instance-parameter
216221
exists(SummaryComponentStack s, SummaryComponentStack callbackRef |
@@ -222,16 +227,81 @@ module Private {
222227
output = TConsSummaryComponentStack(thisParam(), input) and
223228
preservesValue = true
224229
)
230+
or
231+
exists(SummaryComponentStack arg, SummaryComponentStack return |
232+
derivedFluentFlow(c, input, arg, return, preservesValue)
233+
|
234+
arg.length() = 1 and
235+
output = return
236+
or
237+
exists(SummaryComponent head, SummaryComponentStack tail |
238+
derivedFluentFlowPush(c, input, arg, head, tail, 0) and
239+
output = SummaryComponentStack::push(head, tail)
240+
)
241+
)
242+
or
243+
// Chain together summaries where values get passed into callbacks along the way
244+
exists(SummaryComponentStack mid, boolean preservesValue1, boolean preservesValue2 |
245+
c.propagatesFlow(input, mid, preservesValue1) and
246+
c.propagatesFlow(mid, output, preservesValue2) and
247+
mid.drop(mid.length() - 2) =
248+
SummaryComponentStack::push(TParameterSummaryComponent(_),
249+
SummaryComponentStack::singleton(TArgumentSummaryComponent(_))) and
250+
preservesValue = preservesValue1.booleanAnd(preservesValue2)
251+
)
252+
}
253+
254+
/**
255+
* Holds if `c` has a flow summary from `input` to `arg`, where `arg`
256+
* writes to (contents of) the `i`th argument, and `c` has a
257+
* value-preserving flow summary from the `i`th argument to a return value
258+
* (`return`).
259+
*
260+
* In such a case, we derive flow from `input` to (contents of) the return
261+
* value.
262+
*
263+
* As an example, this simplifies modeling of fluent methods:
264+
* for `StringBuilder.append(x)` with a specified value flow from qualifier to
265+
* return value and taint flow from argument 0 to the qualifier, then this
266+
* allows us to infer taint flow from argument 0 to the return value.
267+
*/
268+
pragma[nomagic]
269+
private predicate derivedFluentFlow(
270+
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
271+
SummaryComponentStack return, boolean preservesValue
272+
) {
273+
exists(int i |
274+
summary(c, input, arg, preservesValue) and
275+
isContentOfArgument(arg, i) and
276+
summary(c, SummaryComponentStack::singleton(TArgumentSummaryComponent(i)), return, true) and
277+
return.bottom() = TReturnSummaryComponent(_)
278+
)
279+
}
280+
281+
pragma[nomagic]
282+
private predicate derivedFluentFlowPush(
283+
SummarizedCallable c, SummaryComponentStack input, SummaryComponentStack arg,
284+
SummaryComponent head, SummaryComponentStack tail, int i
285+
) {
286+
derivedFluentFlow(c, input, arg, tail, _) and
287+
head = arg.drop(i).head() and
288+
i = arg.length() - 2
289+
or
290+
exists(SummaryComponent head0, SummaryComponentStack tail0 |
291+
derivedFluentFlowPush(c, input, arg, head0, tail0, i + 1) and
292+
head = arg.drop(i).head() and
293+
tail = SummaryComponentStack::push(head0, tail0)
294+
)
225295
}
226296

227297
private predicate isCallbackParameter(SummaryComponentStack s) {
228298
s.head() = TParameterSummaryComponent(_) and exists(s.tail())
229299
}
230300

231-
private predicate isContentOfArgument(SummaryComponentStack s) {
232-
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail())
301+
private predicate isContentOfArgument(SummaryComponentStack s, int i) {
302+
s.head() = TContentSummaryComponent(_) and isContentOfArgument(s.tail(), i)
233303
or
234-
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(_))
304+
s = TSingletonSummaryComponentStack(TArgumentSummaryComponent(i))
235305
}
236306

237307
private predicate outputState(SummarizedCallable c, SummaryComponentStack s) {
@@ -508,9 +578,14 @@ module Private {
508578
* node, and back out to `p`.
509579
*/
510580
predicate summaryAllowParameterReturnInSelf(ParamNode p) {
511-
exists(SummarizedCallable c, int i |
512-
c.clearsContent(i, _) and
513-
p.isParameterOf(c, i)
581+
exists(SummarizedCallable c, int i | p.isParameterOf(c, i) |
582+
c.clearsContent(i, _)
583+
or
584+
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
585+
summary(c, inputContents, outputContents, _) and
586+
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i)) and
587+
outputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(i))
588+
)
514589
)
515590
}
516591

@@ -534,22 +609,6 @@ module Private {
534609
preservesValue = false and not summary(c, inputContents, outputContents, true)
535610
)
536611
or
537-
// If flow through a method updates a parameter from some input A, and that
538-
// parameter also is returned through B, then we'd like a combined flow from A
539-
// to B as well. As an example, this simplifies modeling of fluent methods:
540-
// for `StringBuilder.append(x)` with a specified value flow from qualifier to
541-
// return value and taint flow from argument 0 to the qualifier, then this
542-
// allows us to infer taint flow from argument 0 to the return value.
543-
succ instanceof ParamNode and
544-
summaryPostUpdateNode(pred, succ) and
545-
preservesValue = true
546-
or
547-
// Similarly we would like to chain together summaries where values get passed
548-
// into callbacks along the way.
549-
pred instanceof ArgNode and
550-
summaryPostUpdateNode(succ, pred) and
551-
preservesValue = true
552-
or
553612
exists(SummarizedCallable c, int i |
554613
pred.(ParamNode).isParameterOf(c, i) and
555614
succ = summaryNode(c, TSummaryNodeClearsContentState(i, _)) and

0 commit comments

Comments
 (0)