mirror of
https://github.com/github/codeql.git
synced 2026-04-30 03:05:15 +02:00
Merge pull request #768 from xiemaisi/js/call-summaries
Approved by asger-semmle
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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`). */
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user