Merge pull request #768 from xiemaisi/js/call-summaries

Approved by asger-semmle
This commit is contained in:
semmle-qlci
2019-01-16 08:35:31 +00:00
committed by GitHub
12 changed files with 243 additions and 20 deletions

View File

@@ -471,7 +471,11 @@ private predicate exploratoryFlowStep(
) {
basicFlowStep(pred, succ, _, cfg) or
basicStoreStep(pred, succ, _) or
loadStep(pred, succ, _)
loadStep(pred, succ, _) or
// the following two disjuncts taken together over-approximate flow through
// higher-order calls
callback(pred, succ) or
succ = pred.(DataFlow::FunctionNode).getAParameter()
}
/**
@@ -536,10 +540,7 @@ private predicate callInputStep(
) {
(
isRelevant(pred, cfg) and
exists(Parameter parm |
argumentPassing(invk, pred, f, parm) and
succ = DataFlow::parameterNode(parm)
)
argumentPassing(invk, pred, f, succ)
or
isRelevant(pred, cfg) and
exists(SsaDefinition prevDef, SsaDefinition def |
@@ -655,6 +656,57 @@ private predicate flowThroughProperty(
)
}
/**
* Holds if `arg` and `cb` are passed as arguments to a function which in turn
* invokes `cb`, passing `arg` as its `i`th argument.
*
* All of this is done under configuration `cfg`, and `arg` flows along a path
* summarized by `summary`, while `cb` is only tracked locally.
*/
private predicate higherOrderCall(
DataFlow::Node arg, DataFlow::Node cb, int i, DataFlow::Configuration cfg, PathSummary summary
) {
exists (Function f, DataFlow::InvokeNode outer, DataFlow::InvokeNode inner, int j,
DataFlow::Node innerArg, DataFlow::ParameterNode cbParm, PathSummary oldSummary |
reachableFromInput(f, outer, arg, innerArg, cfg, oldSummary) and
argumentPassing(outer, cb, f, cbParm) and
innerArg = inner.getArgument(j) |
// direct higher-order call
cbParm.flowsTo(inner.getCalleeNode()) and
i = j and
summary = oldSummary
or
// indirect higher-order call
exists (DataFlow::Node cbArg, PathSummary newSummary |
cbParm.flowsTo(cbArg) and
higherOrderCall(innerArg, cbArg, i, cfg, newSummary) and
summary = oldSummary.append(PathSummary::call()).append(newSummary)
)
)
}
/**
* Holds if `pred` is passed as an argument to a function `f` which also takes a
* callback parameter `cb` and then invokes `cb`, passing `pred` into parameter `succ`
* of `cb`.
*
* All of this is done under configuration `cfg`, and `arg` flows along a path
* summarized by `summary`, while `cb` is only tracked locally.
*/
private predicate flowIntoHigherOrderCall(
DataFlow::Node pred, DataFlow::Node succ, DataFlow::Configuration cfg, PathSummary summary
) {
exists(
DataFlow::Node fArg, DataFlow::FunctionNode cb,
int i, PathSummary oldSummary
|
higherOrderCall(pred, fArg, i, cfg, oldSummary) and
cb = fArg.getALocalSource() and
succ = cb.getParameter(i) and
summary = oldSummary.append(PathSummary::call())
)
}
/**
* Holds if there is a flow step from `pred` to `succ` described by `summary`
* under configuration `cfg`.
@@ -671,6 +723,9 @@ private predicate flowStep(
or
// Flow through a property write/read pair
flowThroughProperty(pred, succ, cfg, summary)
or
// Flow into higher-order call
flowIntoHigherOrderCall(pred, succ, cfg, summary)
) and
not cfg.isBarrier(succ) and
not cfg.isBarrier(pred, succ) and

View File

@@ -17,6 +17,9 @@ class ParameterNode extends DataFlow::SourceNode {
/** Gets the name of this parameter. */
string getName() { result = p.getName() }
/** Holds if this parameter is a rest parameter. */
predicate isRestParameter() { p.isRestParameter() }
}
/** A data flow node corresponding to a function invocation (with or without `new`). */

View File

@@ -782,7 +782,7 @@ module TaintTracking {
* A function that returns the result of a sanitizer check.
*/
private class SanitizingFunction extends Function {
Parameter sanitizedParameter;
DataFlow::ParameterNode sanitizedParameter;
SanitizerGuardNode sanitizer;
@@ -806,11 +806,11 @@ module TaintTracking {
or
returnExpr = getAReturnedExpr()
) and
DataFlow::parameterNode(sanitizedParameter).flowsToExpr(e) and
sanitizedParameter.flowsToExpr(e) and
sanitizer.sanitizes(sanitizerOutcome, e)
) and
getNumParameter() = 1 and
sanitizedParameter = getParameter(0)
sanitizedParameter.getParameter() = getParameter(0)
}
/**

View File

@@ -100,6 +100,10 @@ private module NodeTracking {
basicStoreStep(mid, nd, _)
or
loadStep(mid, nd, _)
or
callback(mid, nd)
or
nd = mid.(DataFlow::FunctionNode).getAParameter()
)
}
@@ -113,10 +117,7 @@ private module NodeTracking {
) {
isRelevant(pred) and
(
exists(Parameter parm |
argumentPassing(invk, pred, f, parm) and
succ = DataFlow::parameterNode(parm)
)
argumentPassing(invk, pred, f, succ)
or
exists(SsaDefinition prevDef, SsaDefinition def |
pred = DataFlow::ssaDefinitionNode(prevDef) and
@@ -206,6 +207,53 @@ private module NodeTracking {
)
}
/**
* Holds if `arg` and `cb` are passed as arguments to a function which in turn
* invokes `cb`, passing `arg` as its `i`th argument. `arg` flows along a path summarized
* by `summary`, while `cb` is only tracked locally.
*/
private predicate higherOrderCall(
DataFlow::Node arg, DataFlow::Node cb, int i, PathSummary summary
) {
exists (Function f, DataFlow::InvokeNode outer, DataFlow::InvokeNode inner, int j,
DataFlow::Node innerArg, DataFlow::ParameterNode cbParm, PathSummary oldSummary |
reachableFromInput(f, outer, arg, innerArg, oldSummary) and
argumentPassing(outer, cb, f, cbParm) and
innerArg = inner.getArgument(j) |
// direct higher-order call
cbParm.flowsTo(inner.getCalleeNode()) and
i = j and
summary = oldSummary
or
// indirect higher-order call
exists (DataFlow::Node cbArg, PathSummary newSummary |
cbParm.flowsTo(cbArg) and
higherOrderCall(innerArg, cbArg, i, newSummary) and
summary = oldSummary.append(PathSummary::call()).append(newSummary)
)
)
}
/**
* Holds if `pred` is passed as an argument to a function `f` which also takes a
* callback parameter `cb` and then invokes `cb`, passing `pred` into parameter `succ`
* of `cb`. `arg` flows along a path summarized by `summary`, while `cb` is only tracked
* locally.
*/
private predicate flowIntoHigherOrderCall(
DataFlow::Node pred, DataFlow::Node succ, PathSummary summary
) {
exists(
DataFlow::Node fArg, DataFlow::FunctionNode cb,
int i, PathSummary oldSummary
|
higherOrderCall(pred, fArg, i, oldSummary) and
cb = fArg.getALocalSource() and
succ = cb.getParameter(i) and
summary = oldSummary.append(PathSummary::call())
)
}
/**
* Holds if there is a flow step from `pred` to `succ` described by `summary`.
*/
@@ -219,6 +267,9 @@ private module NodeTracking {
or
// Flow through a property write/read pair
flowThroughProperty(pred, succ, summary)
or
// Flow into higher-order call
flowIntoHigherOrderCall(pred, succ, summary)
}
/**

View File

@@ -88,11 +88,11 @@ predicate localFlowStep(
* through invocation `invk` of function `f`.
*/
predicate argumentPassing(
DataFlow::InvokeNode invk, DataFlow::ValueNode arg, Function f, Parameter parm
DataFlow::InvokeNode invk, DataFlow::ValueNode arg, Function f, DataFlow::ParameterNode parm
) {
calls(invk, f) and
exists(int i |
f.getParameter(i) = parm and
f.getParameter(i) = parm.getParameter() and
not parm.isRestParameter() and
arg = invk.getArgument(i)
)
@@ -100,7 +100,7 @@ predicate argumentPassing(
exists(DataFlow::Node callback, int i |
invk.(DataFlow::AdditionalPartialInvokeNode).isPartialArgument(callback, arg, i) and
partiallyCalls(invk, callback, f) and
parm = f.getParameter(i) and
parm.getParameter() = f.getParameter(i) and
not parm.isRestParameter()
)
}
@@ -110,10 +110,7 @@ predicate argumentPassing(
* to a function call.
*/
predicate callStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(Parameter parm |
argumentPassing(_, pred, _, parm) and
succ = DataFlow::parameterNode(parm)
)
argumentPassing(_, pred, _, succ)
}
/**
@@ -239,6 +236,34 @@ predicate loadStep(DataFlow::Node pred, DataFlow::PropRead succ, string prop) {
succ.accesses(pred, prop)
}
/**
* Holds if there is a higher-order call with argument `arg`, and `cb` is the local
* source of an argument that flows into the callee position of that call:
*
* ```
* function f(x, g) {
* g(
* x // arg
* );
* }
*
* function cb() { // cb
* }
*
* f(arg, cb);
*
* This is an over-approximation of a possible data flow step through a callback
* invocation.
*/
predicate callback(DataFlow::Node arg, DataFlow::SourceNode cb) {
exists (DataFlow::InvokeNode invk, DataFlow::ParameterNode cbParm, DataFlow::Node cbArg |
arg = invk.getAnArgument() and
cbParm.flowsTo(invk.getCalleeNode()) and
callStep(cbArg, cbParm) and
cb.flowsTo(cbArg)
)
}
/**
* A utility class that is equivalent to `boolean` but does not require type joining.
*/