Ruby: Implement ContentSet

This commit is contained in:
Tom Hvitved
2022-04-04 11:37:40 +02:00
parent c4fbc618a9
commit 725d76e934
7 changed files with 106 additions and 80 deletions

View File

@@ -32,7 +32,9 @@ module SummaryComponent {
SummaryComponent block() { result = argument(any(ParameterPosition pos | pos.isBlock())) }
/** Gets a summary component that represents an element in an array at an unknown index. */
SummaryComponent arrayElementUnknown() { result = SC::content(TUnknownArrayElementContent()) }
SummaryComponent arrayElementUnknown() {
result = SC::content(TSingletonContent(TUnknownArrayElementContent()))
}
/**
* Gets a summary component that represents an element in an array at a known index.
@@ -42,7 +44,7 @@ module SummaryComponent {
*/
bindingset[i]
SummaryComponent arrayElementKnown(int i) {
result = SC::content(TKnownArrayElementContent(i))
result = SC::content(TSingletonContent(TKnownArrayElementContent(i)))
or
// `i` may be out of range
i >= 0 and
@@ -134,7 +136,7 @@ abstract class SummarizedCallable extends LibraryCallable {
* arguments at position `pos` to this callable.
*/
pragma[nomagic]
predicate clearsContent(ParameterPosition pos, DataFlow::Content content) { none() }
predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) { none() }
}
/**
@@ -161,7 +163,7 @@ private class SummarizedCallableAdapter extends Impl::Public::SummarizedCallable
sc.propagatesFlow(input, output, preservesValue)
}
final override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
final override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
sc.clearsContent(pos, content)
}
}

View File

@@ -316,11 +316,15 @@ private module Cached {
entrySsaDefinition(n)
}
cached
newtype TContentSet =
TSingletonContent(Content c) or
TAnyArrayElementContent()
cached
newtype TContent =
TKnownArrayElementContent(int i) { i in [0 .. 10] } or
TUnknownArrayElementContent() or
TAnyArrayElementContent()
TUnknownArrayElementContent()
/**
* Holds if `e` is an `ExprNode` that may be returned by a call to `c`.
@@ -776,18 +780,12 @@ predicate jumpStep(Node pred, Node succ) {
succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr()
}
predicate storeStep(Node node1, Content c, Node node2) {
predicate storeStep(Node node1, ContentSet c, Node node2) {
FlowSummaryImpl::Private::Steps::summaryStoreStep(node1, c, node2)
}
predicate readStep(Node node1, Content c, Node node2) {
exists(Content c0 | FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c0, node2) |
if c0 = TAnyArrayElementContent()
then
c instanceof TUnknownArrayElementContent or
c instanceof TKnownArrayElementContent
else c = c0
)
predicate readStep(Node node1, ContentSet c, Node node2) {
FlowSummaryImpl::Private::Steps::summaryReadStep(node1, c, node2)
}
/**
@@ -795,7 +793,7 @@ predicate readStep(Node node1, Content c, Node node2) {
* any value stored inside `f` is cleared at the pre-update node associated with `x`
* in `x.f = newValue`.
*/
predicate clearsContent(Node n, Content c) {
predicate clearsContent(Node n, ContentSet c) {
FlowSummaryImpl::Private::Steps::summaryClearsContent(n, c)
}
@@ -875,7 +873,7 @@ int accessPathLimit() { result = 5 }
* Holds if access paths with `c` at their head always should be tracked at high
* precision. This disables adaptive access path precision for such access paths.
*/
predicate forceHighPrecision(Content c) { none() }
predicate forceHighPrecision(Content c) { c instanceof Content::ArrayElementContent }
/** The unit type. */
private newtype TUnit = TMkUnit()

View File

@@ -194,14 +194,46 @@ module Content {
class UnknownArrayElementContent extends ArrayElementContent, TUnknownArrayElementContent {
override string toString() { result = "array element" }
}
}
/**
* Used internally only, to represent the union of `KnownArrayElementContent`
* and `UnknownArrayElementContent`, to avoid combinatorial explosions in
* `SummaryComponentStack`s in flow summaries.
*/
private class AnyArrayElementContent extends Content, TAnyArrayElementContent {
override string toString() { result = "any array element" }
/**
* An entity that represents a set of `Content`s.
*
* The set may be interpreted differently depending on whether it is
* stored into (`getAStoreContent`) or read from (`getAReadContent`).
*/
class ContentSet extends TContentSet {
/** Holds if this content set is the singleton `{c}`. */
predicate isSingleton(Content c) { this = TSingletonContent(c) }
/** Holds if this content set represent all `ArrayElementContent`s. */
predicate isAnyArrayElement() { this = TAnyArrayElementContent() }
/** Gets a textual representation of this content set. */
string toString() {
exists(Content c |
this.isSingleton(c) and
result = c.toString()
)
or
this.isAnyArrayElement() and
result = "any array element"
}
/** Gets a content that may be stored into when storing into this set. */
Content getAStoreContent() {
this.isSingleton(result)
or
this.isAnyArrayElement() and
result = TUnknownArrayElementContent()
}
/** Gets a content that may be read from when reading from this set. */
Content getAReadContent() {
this.isSingleton(result)
or
this.isAnyArrayElement() and
result instanceof Content::ArrayElementContent
}
}

View File

@@ -21,7 +21,7 @@ Node summaryNode(SummarizedCallable c, SummaryNodeState state) { result = TSumma
SummaryCall summaryDataFlowCall(Node receiver) { receiver = result.getReceiver() }
/** Gets the type of content `c`. */
DataFlowType getContentType(Content c) { any() }
DataFlowType getContentType(ContentSet c) { any() }
/** Gets the return type of kind `rk` for callable `c`. */
bindingset[c, rk]

View File

@@ -22,7 +22,7 @@ predicate defaultTaintSanitizerGuard(DataFlow::BarrierGuard guard) { none() }
* of `c` at sinks and inputs to additional taint steps.
*/
bindingset[node]
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() }
predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::ContentSet c) { none() }
private CfgNodes::ExprNodes::VariableWriteAccessCfgNode variablesInPattern(
CfgNodes::ExprNodes::CasePatternCfgNode p
@@ -95,10 +95,14 @@ private module Cached {
or
FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom, nodeTo, false)
or
// Although flow through arrays is modelled precisely using stores/reads, we still
// allow flow out of a _tainted_ array. This is needed in order to support taint-
// tracking configurations where the source is an array.
readStep(nodeFrom, any(DataFlow::Content::ArrayElementContent c), nodeTo)
// Although flow through collections is modelled precisely using stores/reads, we still
// allow flow out of a _tainted_ collection. This is needed in order to support taint-
// tracking configurations where the source is a collection.
exists(DataFlow::ContentSet c | readStep(nodeFrom, c, nodeTo) |
c.isSingleton(any(DataFlow::Content::ArrayElementContent aec))
or
c.isAnyArrayElement()
)
}
/**

View File

@@ -334,9 +334,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content = c
content.isSingleton(c)
}
}
@@ -380,9 +380,9 @@ module Array {
)
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::KnownArrayElementContent
content.isAnyArrayElement()
}
}
@@ -458,9 +458,9 @@ module Array {
private class ClearSummary extends SimpleSummarizedCallable {
ClearSummary() { this = "clear" }
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -540,9 +540,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -552,9 +552,9 @@ module Array {
bindingset[this]
DeleteAtSummary() { mc.getMethodName() = "delete_at" }
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
override MethodCall getACall() { result = mc }
@@ -612,9 +612,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -798,9 +798,9 @@ module Array {
if exists(mc.getBlock()) then mc.getNumberOfArguments() = 0 else mc.getNumberOfArguments() = 1
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -848,9 +848,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -910,9 +910,9 @@ module Array {
)
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::KnownArrayElementContent
content.isAnyArrayElement()
}
}
@@ -966,9 +966,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -1089,9 +1089,9 @@ module Array {
)
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::KnownArrayElementContent
content.isAnyArrayElement()
}
}
@@ -1147,9 +1147,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -1168,9 +1168,9 @@ module Array {
)
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -1253,9 +1253,9 @@ module Array {
override MethodCall getACall() { result = mc }
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -1314,9 +1314,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -1328,9 +1328,9 @@ module Array {
override MethodCall getACall() { result = mc }
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
}
@@ -1415,9 +1415,9 @@ module Array {
bindingset[this]
SliceBangSummary() { mc.getMethodName() = "slice!" }
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::ArrayElementContent
content.isAnyArrayElement()
}
override Call getACall() { result = mc }
@@ -1564,9 +1564,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::KnownArrayElementContent
content.isAnyArrayElement()
}
}
@@ -1583,9 +1583,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::KnownArrayElementContent
content.isAnyArrayElement()
}
}
@@ -1619,9 +1619,9 @@ module Array {
preservesValue = true
}
override predicate clearsContent(ParameterPosition pos, DataFlow::Content content) {
override predicate clearsContent(ParameterPosition pos, DataFlow::ContentSet content) {
pos.isSelf() and
content instanceof DataFlow::Content::KnownArrayElementContent
content.isAnyArrayElement()
}
}

View File

@@ -3401,18 +3401,12 @@ edges
| array_flow.rb:1604:10:1604:10 | c [array element] : | array_flow.rb:1604:10:1604:13 | ...[...] |
| array_flow.rb:1605:10:1605:10 | c [array element] : | array_flow.rb:1605:10:1605:13 | ...[...] |
| array_flow.rb:1605:10:1605:10 | c [array element] : | array_flow.rb:1605:10:1605:13 | ...[...] |
| array_flow.rb:1610:5:1610:5 | [post] a [array element 0, array element 0] : | array_flow.rb:1611:10:1611:10 | a [array element 0, array element 0] : |
| array_flow.rb:1610:5:1610:5 | [post] a [array element 0, array element 0] : | array_flow.rb:1611:10:1611:10 | a [array element 0, array element 0] : |
| array_flow.rb:1610:5:1610:5 | [post] a [array element, array element 0] : | array_flow.rb:1611:10:1611:10 | a [array element, array element 0] : |
| array_flow.rb:1610:5:1610:5 | [post] a [array element, array element 0] : | array_flow.rb:1611:10:1611:10 | a [array element, array element 0] : |
| array_flow.rb:1610:5:1610:8 | [post] ...[...] [array element 0] : | array_flow.rb:1610:5:1610:5 | [post] a [array element 0, array element 0] : |
| array_flow.rb:1610:5:1610:8 | [post] ...[...] [array element 0] : | array_flow.rb:1610:5:1610:5 | [post] a [array element 0, array element 0] : |
| array_flow.rb:1610:5:1610:8 | [post] ...[...] [array element 0] : | array_flow.rb:1610:5:1610:5 | [post] a [array element, array element 0] : |
| array_flow.rb:1610:5:1610:8 | [post] ...[...] [array element 0] : | array_flow.rb:1610:5:1610:5 | [post] a [array element, array element 0] : |
| array_flow.rb:1610:15:1610:27 | call to source : | array_flow.rb:1610:5:1610:8 | [post] ...[...] [array element 0] : |
| array_flow.rb:1610:15:1610:27 | call to source : | array_flow.rb:1610:5:1610:8 | [post] ...[...] [array element 0] : |
| array_flow.rb:1611:10:1611:10 | a [array element 0, array element 0] : | array_flow.rb:1611:10:1611:13 | ...[...] [array element 0] : |
| array_flow.rb:1611:10:1611:10 | a [array element 0, array element 0] : | array_flow.rb:1611:10:1611:13 | ...[...] [array element 0] : |
| array_flow.rb:1611:10:1611:10 | a [array element, array element 0] : | array_flow.rb:1611:10:1611:13 | ...[...] [array element 0] : |
| array_flow.rb:1611:10:1611:10 | a [array element, array element 0] : | array_flow.rb:1611:10:1611:13 | ...[...] [array element 0] : |
| array_flow.rb:1611:10:1611:13 | ...[...] [array element 0] : | array_flow.rb:1611:10:1611:16 | ...[...] |
@@ -7065,16 +7059,12 @@ nodes
| array_flow.rb:1605:10:1605:10 | c [array element] : | semmle.label | c [array element] : |
| array_flow.rb:1605:10:1605:13 | ...[...] | semmle.label | ...[...] |
| array_flow.rb:1605:10:1605:13 | ...[...] | semmle.label | ...[...] |
| array_flow.rb:1610:5:1610:5 | [post] a [array element 0, array element 0] : | semmle.label | [post] a [array element 0, array element 0] : |
| array_flow.rb:1610:5:1610:5 | [post] a [array element 0, array element 0] : | semmle.label | [post] a [array element 0, array element 0] : |
| array_flow.rb:1610:5:1610:5 | [post] a [array element, array element 0] : | semmle.label | [post] a [array element, array element 0] : |
| array_flow.rb:1610:5:1610:5 | [post] a [array element, array element 0] : | semmle.label | [post] a [array element, array element 0] : |
| array_flow.rb:1610:5:1610:8 | [post] ...[...] [array element 0] : | semmle.label | [post] ...[...] [array element 0] : |
| array_flow.rb:1610:5:1610:8 | [post] ...[...] [array element 0] : | semmle.label | [post] ...[...] [array element 0] : |
| array_flow.rb:1610:15:1610:27 | call to source : | semmle.label | call to source : |
| array_flow.rb:1610:15:1610:27 | call to source : | semmle.label | call to source : |
| array_flow.rb:1611:10:1611:10 | a [array element 0, array element 0] : | semmle.label | a [array element 0, array element 0] : |
| array_flow.rb:1611:10:1611:10 | a [array element 0, array element 0] : | semmle.label | a [array element 0, array element 0] : |
| array_flow.rb:1611:10:1611:10 | a [array element, array element 0] : | semmle.label | a [array element, array element 0] : |
| array_flow.rb:1611:10:1611:10 | a [array element, array element 0] : | semmle.label | a [array element, array element 0] : |
| array_flow.rb:1611:10:1611:13 | ...[...] [array element 0] : | semmle.label | ...[...] [array element 0] : |