Ruby: Refactor SummarizedCallable.

This commit is contained in:
Michael Nebel
2022-05-19 11:04:18 +02:00
parent be79f20ef1
commit 73802cbd6d
5 changed files with 35 additions and 61 deletions

View File

@@ -84,37 +84,10 @@ module SummaryComponentStack {
}
/** A callable with a flow summary, identified by a unique string. */
abstract class SummarizedCallable extends LibraryCallable {
abstract class SummarizedCallable extends LibraryCallable, Impl::Public::SummarizedCallable {
bindingset[this]
SummarizedCallable() { any() }
/**
* Holds if data may flow from `input` to `output` through this callable.
*
* `preservesValue` indicates whether this is a value-preserving step
* or a taint-step.
*
* Input specifications are restricted to stacks that end with
* `SummaryComponent::argument(_)`, preceded by zero or more
* `SummaryComponent::return()` or `SummaryComponent::content(_)` components.
*
* Output specifications are restricted to stacks that end with
* `SummaryComponent::return()` or `SummaryComponent::argument(_)`.
*
* Output stacks ending with `SummaryComponent::return()` can be preceded by zero
* or more `SummaryComponent::content(_)` components.
*
* Output stacks ending with `SummaryComponent::argument(_)` can be preceded by an
* optional `SummaryComponent::parameter(_)` component, which in turn can be preceded
* by zero or more `SummaryComponent::content(_)` components.
*/
pragma[nomagic]
predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
none()
}
/**
* Same as
*
@@ -143,18 +116,6 @@ abstract class SimpleSummarizedCallable extends SummarizedCallable {
final override MethodCall getACall() { result = mc }
}
private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable {
private SummarizedCallable sc;
SummarizedCallableAdapter() { this = TLibraryCallable(sc) }
final override predicate propagatesFlow(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
sc.propagatesFlow(input, output, preservesValue)
}
}
class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack;
private class SummarizedCallableFromModel extends SummarizedCallable {

View File

@@ -120,11 +120,11 @@ class SummaryCall extends DataFlowCall, TSummaryCall {
/** Gets the data flow node that this call targets. */
DataFlow::Node getReceiver() { result = receiver }
override DataFlowCallable getEnclosingCallable() { result = c }
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
override string toString() { result = "[summary] call to " + receiver + " in " + c }
override Location getLocation() { result = c.getLocation() }
override EmptyLocation getLocation() { any() }
}
private class NormalCall extends DataFlowCall, TNormalCall {

View File

@@ -530,12 +530,12 @@ private module ParameterNodes {
override predicate isSourceParameterOf(Callable c, ParameterPosition pos) { none() }
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
sc = c and pos = pos_
sc = c.asLibraryCallable() and pos = pos_
}
override CfgScope getCfgScope() { none() }
override DataFlowCallable getEnclosingCallable() { result = sc }
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = sc }
override EmptyLocation getLocationImpl() { any() }
@@ -554,7 +554,7 @@ class SummaryNode extends NodeImpl, TSummaryNode {
override CfgScope getCfgScope() { none() }
override DataFlowCallable getEnclosingCallable() { result = c }
override DataFlowCallable getEnclosingCallable() { result.asLibraryCallable() = c }
override EmptyLocation getLocationImpl() { any() }

View File

@@ -195,7 +195,10 @@ module Public {
}
/** A callable with a flow summary. */
abstract class SummarizedCallable extends DataFlowCallable {
abstract class SummarizedCallable extends SummarizedCallableBase {
bindingset[this]
SummarizedCallable() { any() }
/**
* Holds if data may flow from `input` to `output` through this callable.
*
@@ -493,7 +496,7 @@ module Private {
or
exists(ParameterPosition pos |
parameterReadState(c, state, pos) and
result.(ParamNode).isParameterOf(c, pos)
result.(ParamNode).isParameterOf(inject(c), pos)
)
)
}
@@ -621,7 +624,7 @@ module Private {
predicate summaryPostUpdateNode(Node post, Node pre) {
exists(SummarizedCallable c, ParameterPosition pos |
isParameterPostUpdate(post, c, pos) and
pre.(ParamNode).isParameterOf(c, pos)
pre.(ParamNode).isParameterOf(inject(c), pos)
)
or
exists(SummarizedCallable callable, SummaryComponentStack s |
@@ -644,7 +647,7 @@ module Private {
* node, and back out to `p`.
*/
predicate summaryAllowParameterReturnInSelf(ParamNode p) {
exists(SummarizedCallable c, ParameterPosition ppos | p.isParameterOf(c, ppos) |
exists(SummarizedCallable c, ParameterPosition ppos | p.isParameterOf(inject(c), ppos) |
exists(SummaryComponentStack inputContents, SummaryComponentStack outputContents |
summary(c, inputContents, outputContents, _) and
inputContents.bottom() = pragma[only_bind_into](TArgumentSummaryComponent(ppos)) and
@@ -748,8 +751,11 @@ module Private {
private predicate viableParam(
DataFlowCall call, SummarizedCallable sc, ParameterPosition ppos, ParamNode p
) {
p.isParameterOf(sc, ppos) and
sc = viableCallable(call)
exists(DataFlowCallable c |
c = inject(sc) and
p.isParameterOf(c, ppos) and
c = viableCallable(call)
)
}
pragma[nomagic]
@@ -1067,7 +1073,7 @@ module Private {
/** Provides a query predicate for outputting a set of relevant flow summaries. */
module TestOutput {
/** A flow summary to include in the `summary/3` query predicate. */
abstract class RelevantSummarizedCallable extends SummarizedCallable {
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
/** Gets the string representation of this callable used by `summary/1`. */
abstract string getCallableCsv();
@@ -1075,8 +1081,10 @@ module Private {
predicate relevantSummary(
SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
) {
this.propagatesFlow(input, output, preservesValue)
super.propagatesFlow(input, output, preservesValue)
}
string toString() { result = super.toString() }
}
/** Render the kind in the format used in flow summaries. */
@@ -1087,7 +1095,7 @@ module Private {
}
private string renderGenerated(RelevantSummarizedCallable c) {
if c.isAutoGenerated() then result = "generated:" else result = ""
if c.(SummarizedCallable).isAutoGenerated() then result = "generated:" else result = ""
}
/**
@@ -1117,19 +1125,21 @@ module Private {
*/
module RenderSummarizedCallable {
/** A summarized callable to include in the graph. */
abstract class RelevantSummarizedCallable extends SummarizedCallable { }
abstract class RelevantSummarizedCallable instanceof SummarizedCallable {
string toString() { result = super.toString() }
}
private newtype TNodeOrCall =
MkNode(Node n) {
exists(RelevantSummarizedCallable c |
n = summaryNode(c, _)
or
n.(ParamNode).isParameterOf(c, _)
n.(ParamNode).isParameterOf(inject(c), _)
)
} or
MkCall(DataFlowCall call) {
call = summaryDataFlowCall(_) and
call.getEnclosingCallable() instanceof RelevantSummarizedCallable
call.getEnclosingCallable() = inject(any(RelevantSummarizedCallable c))
}
private class NodeOrCall extends TNodeOrCall {

View File

@@ -11,6 +11,10 @@ private import FlowSummaryImpl::Private
private import FlowSummaryImpl::Public
private import codeql.ruby.dataflow.FlowSummary as FlowSummary
class SummarizedCallableBase = string;
DataFlowCallable inject(SummarizedCallable c) { result.asLibraryCallable() = c }
/** Gets the parameter position of the instance parameter. */
ArgumentPosition instanceParameterPosition() { none() } // disables implicit summary flow to `self` for callbacks
@@ -46,11 +50,10 @@ DataFlowType getCallbackReturnType(DataFlowType t, ReturnKind rk) { any() }
* stating whether the summary is autogenerated.
*/
predicate summaryElement(
DataFlowCallable c, string input, string output, string kind, boolean generated
FlowSummary::SummarizedCallable c, string input, string output, string kind, boolean generated
) {
exists(FlowSummary::SummarizedCallable sc, boolean preservesValue |
sc.propagatesFlowExt(input, output, preservesValue) and
c.asLibraryCallable() = sc and
exists(boolean preservesValue |
c.propagatesFlowExt(input, output, preservesValue) and
(if preservesValue = true then kind = "value" else kind = "taint") and
generated = false
)