Merge pull request #223 from max/update-dataflow

Add support for taint-getter/setter summaries in data flow.
This commit is contained in:
Sauyon Lee
2020-01-23 16:03:05 -08:00
committed by GitHub Enterprise
3 changed files with 674 additions and 237 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -112,9 +112,6 @@ private module ImplCommon {
enclosing = arg.getEnclosingCallable()
}
pragma[noinline]
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
pragma[noinline]
private predicate viableParamArg0(
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
@@ -123,9 +120,9 @@ private module ImplCommon {
(
outercc = TAnyCallContext()
or
outercc = TSomeCall(getAParameter(c), _)
outercc = TSomeCall()
or
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
exists(DataFlowCall other | outercc = TSpecificCall(other) |
recordDataFlowCallSite(other, c)
)
) and
@@ -156,17 +153,17 @@ private module ImplCommon {
viableParamArg1(p, callable, i, arg, outercc, call)
|
if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, true)
else innercc = TSomeCall(p, true)
then innercc = TSpecificCall(call)
else innercc = TSomeCall()
)
}
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
result = TSomeCall(p, _)
result = TSomeCall()
or
exists(DataFlowCall call, int i, DataFlowCallable callable |
result = TSpecificCall(call, i, _) and
p.isParameterOf(callable, i) and
exists(DataFlowCall call, DataFlowCallable callable |
result = TSpecificCall(call) and
p.isParameterOf(callable, _) and
recordDataFlowCallSite(call, callable)
)
}
@@ -460,9 +457,6 @@ private module ImplCommon {
enclosing = arg.getEnclosingCallable()
}
pragma[noinline]
private ParameterNode getAParameter(DataFlowCallable c) { result.getEnclosingCallable() = c }
pragma[noinline]
private predicate viableParamArg0(
int i, ArgumentNode arg, CallContext outercc, DataFlowCall call
@@ -471,9 +465,9 @@ private module ImplCommon {
(
outercc = TAnyCallContext()
or
outercc = TSomeCall(getAParameter(c), _)
outercc = TSomeCall()
or
exists(DataFlowCall other | outercc = TSpecificCall(other, _, _) |
exists(DataFlowCall other | outercc = TSpecificCall(other) |
recordDataFlowCallSite(other, c)
)
) and
@@ -504,17 +498,17 @@ private module ImplCommon {
viableParamArg1(p, callable, i, arg, outercc, call)
|
if recordDataFlowCallSite(call, callable)
then innercc = TSpecificCall(call, i, true)
else innercc = TSomeCall(p, true)
then innercc = TSpecificCall(call)
else innercc = TSomeCall()
)
}
private CallContextCall getAValidCallContextForParameter(ParameterNode p) {
result = TSomeCall(p, _)
result = TSomeCall()
or
exists(DataFlowCall call, int i, DataFlowCallable callable |
result = TSpecificCall(call, i, _) and
p.isParameterOf(callable, i) and
exists(DataFlowCall call, DataFlowCallable callable |
result = TSpecificCall(call) and
p.isParameterOf(callable, _) and
recordDataFlowCallSite(call, callable)
)
}
@@ -579,14 +573,6 @@ private module ImplCommon {
}
}
/**
* Holds if `call` passes an implicit or explicit instance argument, i.e., an
* expression that reaches a `this` parameter.
*/
private predicate callHasInstanceArgument(DataFlowCall call) {
exists(ArgumentNode arg | arg.argumentOf(call, -1))
}
/**
* Holds if the call context `call` either improves virtual dispatch in
* `callable` or if it allows us to prune unreachable nodes in `callable`.
@@ -601,16 +587,8 @@ private module ImplCommon {
cached
newtype TCallContext =
TAnyCallContext() or
TSpecificCall(DataFlowCall call, int i, boolean emptyAp) {
recordDataFlowCallSite(call, _) and
(emptyAp = true or emptyAp = false) and
(
exists(call.getArgument(i))
or
i = -1 and callHasInstanceArgument(call)
)
} or
TSomeCall(ParameterNode p, boolean emptyAp) { emptyAp = true or emptyAp = false } or
TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or
TSomeCall() or
TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) }
cached
@@ -635,11 +613,11 @@ private module ImplCommon {
*
* There are four cases:
* - `TAnyCallContext()` : No restrictions on method flow.
* - `TSpecificCall(DataFlowCall call, int i)` : Flow entered through the `i`th
* parameter at the given `call`. This call improves the set of viable
* - `TSpecificCall(DataFlowCall call)` : Flow entered through the
* given `call`. This call improves the set of viable
* dispatch targets for at least one method call in the current callable
* or helps prune unreachable nodes in the current callable.
* - `TSomeCall(ParameterNode p)` : Flow entered through parameter `p`. The
* - `TSomeCall()` : Flow entered through a parameter. The
* originating call does not improve the set of dispatch targets for any
* method call in the current callable and was therefore not recorded.
* - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and
@@ -663,23 +641,21 @@ private module ImplCommon {
class CallContextSpecificCall extends CallContextCall, TSpecificCall {
override string toString() {
exists(DataFlowCall call, int i | this = TSpecificCall(call, i, _) |
result = "CcCall(" + call + ", " + i + ")"
)
exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")")
}
override predicate relevantFor(DataFlowCallable callable) {
recordDataFlowCallSite(getCall(), callable)
}
DataFlowCall getCall() { this = TSpecificCall(result, _, _) }
DataFlowCall getCall() { this = TSpecificCall(result) }
}
class CallContextSomeCall extends CallContextCall, TSomeCall {
override string toString() { result = "CcSomeCall" }
override predicate relevantFor(DataFlowCallable callable) {
exists(ParameterNode p | this = TSomeCall(p, _) and p.getEnclosingCallable() = callable)
exists(ParameterNode p | p.getEnclosingCallable() = callable)
}
}
@@ -848,7 +824,7 @@ private module ImplCommon {
bindingset[call, cc]
DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) {
exists(DataFlowCall ctx | cc = TSpecificCall(ctx, _, _) |
exists(DataFlowCall ctx | cc = TSpecificCall(ctx) |
if reducedViableImplInCallContext(call, _, ctx)
then result = prunedViableImplInCallContext(call, ctx)
else result = viableCallable(call)
@@ -861,6 +837,76 @@ private module ImplCommon {
result = viableCallable(call) and cc instanceof CallContextReturn
}
newtype TSummary =
TSummaryVal() or
TSummaryTaint() or
TSummaryReadVal(Content f) or
TSummaryReadTaint(Content f) or
TSummaryTaintStore(Content f)
/**
* A summary of flow through a callable. This can either be value-preserving
* if no additional steps are used, taint-flow if at least one additional step
* is used, or any one of those combined with a store or a read. Summaries
* recorded at a return node are restricted to include at least one additional
* step, as the value-based summaries are calculated independent of the
* configuration.
*/
class Summary extends TSummary {
string toString() {
result = "Val" and this = TSummaryVal()
or
result = "Taint" and this = TSummaryTaint()
or
exists(Content f |
result = "ReadVal " + f.toString() and this = TSummaryReadVal(f)
or
result = "ReadTaint " + f.toString() and this = TSummaryReadTaint(f)
or
result = "TaintStore " + f.toString() and this = TSummaryTaintStore(f)
)
}
/** Gets the summary that results from extending this with an additional step. */
Summary additionalStep() {
this = TSummaryVal() and result = TSummaryTaint()
or
this = TSummaryTaint() and result = TSummaryTaint()
or
exists(Content f | this = TSummaryReadVal(f) and result = TSummaryReadTaint(f))
or
exists(Content f | this = TSummaryReadTaint(f) and result = TSummaryReadTaint(f))
}
/** Gets the summary that results from extending this with a read. */
Summary readStep(Content f) { this = TSummaryVal() and result = TSummaryReadVal(f) }
/** Gets the summary that results from extending this with a store. */
Summary storeStep(Content f) { this = TSummaryTaint() and result = TSummaryTaintStore(f) }
/** Gets the summary that results from extending this with `step`. */
bindingset[this, step]
Summary compose(Summary step) {
this = TSummaryVal() and result = step
or
this = TSummaryTaint() and
(step = TSummaryTaint() or step = TSummaryTaintStore(_)) and
result = step
or
exists(Content f |
this = TSummaryReadVal(f) and step = TSummaryTaint() and result = TSummaryReadTaint(f)
)
or
this = TSummaryReadTaint(_) and step = TSummaryTaint() and result = this
}
/** Holds if this summary does not include any taint steps. */
predicate isPartial() {
this = TSummaryVal() or
this = TSummaryReadVal(_)
}
}
pragma[noinline]
DataFlowType getErasedNodeType(Node n) { result = getErasedRepr(n.getType()) }

View File

@@ -97,10 +97,10 @@ class Content extends TContent {
}
/** Gets the type of the object containing this content. */
abstract Type getContainerType();
abstract DataFlowType getContainerType();
/** Gets the type of this content. */
abstract Type getType();
abstract DataFlowType getType();
}
private class FieldContent extends Content, TFieldContent {
@@ -114,33 +114,33 @@ private class FieldContent extends Content, TFieldContent {
f.getDeclaration().hasLocationInfo(path, sl, sc, el, ec)
}
override Type getContainerType() { result = f.getDeclaringType() }
override DataFlowType getContainerType() { result = f.getDeclaringType() }
override Type getType() { result = f.getType() }
override DataFlowType getType() { result = f.getType() }
}
private class CollectionContent extends Content, TCollectionContent {
override string toString() { result = "collection" }
override Type getContainerType() { none() }
override DataFlowType getContainerType() { none() }
override Type getType() { none() }
override DataFlowType getType() { none() }
}
private class ArrayContent extends Content, TArrayContent {
override string toString() { result = "array" }
override Type getContainerType() { none() }
override DataFlowType getContainerType() { none() }
override Type getType() { none() }
override DataFlowType getType() { none() }
}
private class PointerContent extends Content, TPointerContent {
override string toString() { result = "pointer" }
override Type getContainerType() { this = TPointerContent(result) }
override DataFlowType getContainerType() { this = TPointerContent(result) }
override Type getType() { result = getContainerType().(PointerType).getBaseType() }
override DataFlowType getType() { result = getContainerType().(PointerType).getBaseType() }
}
/**
@@ -186,7 +186,7 @@ predicate readStep(Node node1, Content f, Node node2) {
* possible flow. A single type is used for all numeric types to account for
* numeric conversions, and otherwise the erasure is used.
*/
Type getErasedRepr(Type t) {
DataFlowType getErasedRepr(Type t) {
result = t // stub implementation
}