Merge pull request #2251 from asger-semmle/barrier-guard-improvements

Approved by esbena
This commit is contained in:
semmle-qlci
2019-11-07 15:50:23 +00:00
committed by GitHub
12 changed files with 228 additions and 100 deletions

View File

@@ -147,7 +147,7 @@ abstract class Configuration extends string {
*/
predicate isBarrier(DataFlow::Node node) {
exists(BarrierGuardNode guard |
isBarrierGuard(guard) and
isBarrierGuardInternal(guard) and
guard.internalBlocks(node, "")
)
}
@@ -181,7 +181,7 @@ abstract class Configuration extends string {
*/
predicate isLabeledBarrier(DataFlow::Node node, FlowLabel lbl) {
exists(BarrierGuardNode guard |
isBarrierGuard(guard) and
isBarrierGuardInternal(guard) and
guard.internalBlocks(node, lbl)
)
or
@@ -198,6 +198,12 @@ abstract class Configuration extends string {
*/
predicate isBarrierGuard(BarrierGuardNode guard) { none() }
private predicate isBarrierGuardInternal(BarrierGuardNode guard) {
isBarrierGuard(guard)
or
guard.(AdditionalBarrierGuardNode).appliesTo(this)
}
/**
* Holds if data may flow from `source` to `sink` for this configuration.
*/
@@ -302,42 +308,27 @@ abstract class BarrierGuardNode extends DataFlow::Node {
exists(SsaRefinementNode ref, boolean outcome |
nd = DataFlow::ssaDefinitionNode(ref) and
forex(SsaVariable input | input = ref.getAnInput() |
asExpr() = ref.getGuard().getTest() and
getExpr() = ref.getGuard().getTest() and
outcome = ref.getGuard().(ConditionGuardNode).getOutcome() and
internalBlocksExpr(outcome, input.getAUse(), label)
barrierGuardBlocksExpr(this, outcome, input.getAUse(), label)
)
)
or
// 2) `nd` is an instance of an access path `p`, and dominated by a barrier for `p`
exists(AccessPath p, BasicBlock bb, ConditionGuardNode cond, boolean outcome |
nd = DataFlow::valueNode(p.getAnInstanceIn(bb)) and
asExpr() = cond.getTest() and
getExpr() = cond.getTest() and
outcome = cond.getOutcome() and
internalBlocksAccessPath(outcome, p, label) and
barrierGuardBlocksAccessPath(this, outcome, p, label) and
cond.dominates(bb)
)
}
/**
* Holds if data flow node `nd` acts as a barrier for data flow.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
private predicate internalBlocksExpr(boolean outcome, Expr test, string label) {
blocks(outcome, test) and label = ""
/** Gets the corresponding expression, including that of reflective calls. */
private Expr getExpr() {
result = asExpr()
or
blocks(outcome, test, label)
}
/**
* Holds if data flow node `nd` acts as a barrier for data flow due to aliasing through
* an access path.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
pragma[noinline]
private predicate internalBlocksAccessPath(boolean outcome, AccessPath ap, string label) {
internalBlocksExpr(outcome, ap.getAnInstance(), label)
this = DataFlow::reflectiveCallNode(result)
}
/**
@@ -353,6 +344,32 @@ abstract class BarrierGuardNode extends DataFlow::Node {
predicate blocks(boolean outcome, Expr e, FlowLabel label) { none() }
}
/**
* Holds if data flow node `nd` acts as a barrier for data flow.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
private predicate barrierGuardBlocksExpr(BarrierGuardNode guard, boolean outcome, Expr test, string label) {
guard.blocks(outcome, test) and label = ""
or
guard.blocks(outcome, test, label)
or
// Handle labelled barrier guard functions specially, to avoid negative recursion
// through the non-abstract 3-argument version of blocks().
guard.(AdditionalBarrierGuardCall).internalBlocksLabel(outcome, test, label)
}
/**
* Holds if data flow node `nd` acts as a barrier for data flow due to aliasing through
* an access path.
*
* `label` is bound to the blocked label, or the empty string if all labels should be blocked.
*/
pragma[noinline]
private predicate barrierGuardBlocksAccessPath(BarrierGuardNode guard, boolean outcome, AccessPath ap, string label) {
barrierGuardBlocksExpr(guard, outcome, ap.getAnInstance(), label)
}
/**
* A guard node that only blocks specific labels.
*/
@@ -1186,3 +1203,110 @@ module PathGraph {
not pred = finalMidNode(succ)
}
}
/**
* Gets an operand of the given `&&` operator.
*
* We use this to construct the transitive closure over a relation
* that does not include all of `BinaryExpr.getAnOperand`.
*/
private Expr getALogicalAndOperand(LogAndExpr e) {
result = e.getAnOperand()
}
/**
* Gets an operand of the given `||` operator.
*
* We use this to construct the transitive closure over a relation
* that does not include all of `BinaryExpr.getAnOperand`.
*/
private Expr getALogicalOrOperand(LogOrExpr e) {
result = e.getAnOperand()
}
/**
* A `BarrierGuardNode` that controls which data flow
* configurations it is used in.
*
* Note: For performance reasons, all subclasses of this class should be part
* of the standard library. Override `Configuration::isBarrierGuard`
* for analysis-specific barrier guards.
*/
abstract class AdditionalBarrierGuardNode extends BarrierGuardNode {
abstract predicate appliesTo(Configuration cfg);
}
/**
* A function that returns the result of a barrier guard.
*/
private class BarrierGuardFunction extends Function {
DataFlow::ParameterNode sanitizedParameter;
BarrierGuardNode guard;
boolean guardOutcome;
string label;
BarrierGuardFunction() {
exists(Expr e |
exists(Expr returnExpr |
returnExpr = guard.asExpr()
or
// ad hoc support for conjunctions:
getALogicalAndOperand+(returnExpr) = guard.asExpr() and guardOutcome = true
or
// ad hoc support for disjunctions:
getALogicalOrOperand+(returnExpr) = guard.asExpr() and guardOutcome = false
|
exists(SsaExplicitDefinition ssa |
ssa.getDef().getSource() = returnExpr and
ssa.getVariable().getAUse() = getAReturnedExpr()
)
or
returnExpr = getAReturnedExpr()
) and
sanitizedParameter.flowsToExpr(e) and
barrierGuardBlocksExpr(guard, guardOutcome, e, label)
) and
getNumParameter() = 1 and
sanitizedParameter.getParameter() = getParameter(0)
}
/**
* Holds if this function sanitizes argument `e` of call `call`, provided the call evaluates to `outcome`.
*/
predicate isBarrierCall(DataFlow::CallNode call, Expr e, boolean outcome, string lbl) {
exists(DataFlow::Node arg |
arg.asExpr() = e and
arg = call.getArgument(0) and
call.getNumArgument() = 1 and
argumentPassing(call, arg, this, sanitizedParameter) and
outcome = guardOutcome and
lbl = label
)
}
/**
* Holds if this function applies to the flow in `cfg`.
*/
predicate appliesTo(Configuration cfg) { cfg.isBarrierGuard(guard) }
}
/**
* A call that sanitizes an argument.
*/
private class AdditionalBarrierGuardCall extends AdditionalBarrierGuardNode, DataFlow::CallNode {
BarrierGuardFunction f;
AdditionalBarrierGuardCall() { f.isBarrierCall(this, _, _, _) }
override predicate blocks(boolean outcome, Expr e) {
f.isBarrierCall(this, e, outcome, "")
}
predicate internalBlocksLabel(boolean outcome, Expr e, DataFlow::FlowLabel label) {
f.isBarrierCall(this, e, outcome, label)
}
override predicate appliesTo(Configuration cfg) { f.appliesTo(cfg) }
}

View File

@@ -947,6 +947,15 @@ module DataFlow {
*/
DataFlow::Node globalAccessPathRootPseudoNode() { result instanceof TGlobalAccessPathRoot }
/**
* Gets a data flow node representing the underlying call performed by the given
* call to `Function.prototype.call` or `Function.prototype.apply`.
*
* For example, for an expression `fn.call(x, y)`, this gets a call node with `fn` as the
* callee, `x` as the receiver, and `y` as the first argument.
*/
DataFlow::InvokeNode reflectiveCallNode(InvokeExpr expr) { result = TReflectiveCallNode(expr, _) }
/**
* Provides classes representing various kinds of calls.
*

View File

@@ -133,8 +133,8 @@ module TaintTracking {
* configurations it is used in.
*
* Note: For performance reasons, all subclasses of this class should be part
* of the standard library. Override `Configuration::isSanitizer`
* for analysis-specific taint steps.
* of the standard library. Override `Configuration::isSanitizerGuard`
* for analysis-specific taint sanitizer guards.
*/
abstract class AdditionalSanitizerGuardNode extends SanitizerGuardNode {
/**
@@ -846,71 +846,6 @@ module TaintTracking {
override predicate appliesTo(Configuration cfg) { any() }
}
/**
* A function that returns the result of a sanitizer check.
*/
private class SanitizingFunction extends Function {
DataFlow::ParameterNode sanitizedParameter;
SanitizerGuardNode sanitizer;
boolean sanitizerOutcome;
SanitizingFunction() {
exists(Expr e |
exists(Expr returnExpr |
returnExpr = sanitizer.asExpr()
or
// ad hoc support for conjunctions:
returnExpr.(LogAndExpr).getAnOperand() = sanitizer.asExpr() and sanitizerOutcome = true
or
// ad hoc support for disjunctions:
returnExpr.(LogOrExpr).getAnOperand() = sanitizer.asExpr() and sanitizerOutcome = false
|
exists(SsaExplicitDefinition ssa |
ssa.getDef().getSource() = returnExpr and
ssa.getVariable().getAUse() = getAReturnedExpr()
)
or
returnExpr = getAReturnedExpr()
) and
sanitizedParameter.flowsToExpr(e) and
sanitizer.sanitizes(sanitizerOutcome, e)
) and
getNumParameter() = 1 and
sanitizedParameter.getParameter() = getParameter(0)
}
/**
* Holds if this function sanitizes argument `e` of call `call`, provided the call evaluates to `outcome`.
*/
predicate isSanitizingCall(DataFlow::CallNode call, Expr e, boolean outcome) {
exists(DataFlow::Node arg |
arg.asExpr() = e and
arg = call.getArgument(0) and
call.getNumArgument() = 1 and
FlowSteps::argumentPassing(call, arg, this, sanitizedParameter) and
outcome = sanitizerOutcome
)
}
/**
* Holds if this function applies to the flow in `cfg`.
*/
predicate appliesTo(Configuration cfg) { cfg.isBarrierGuard(sanitizer) }
}
/**
* A call that sanitizes an argument.
*/
private class AdditionalSanitizingCall extends AdditionalSanitizerGuardNode, DataFlow::CallNode {
SanitizingFunction f;
AdditionalSanitizingCall() { f.isSanitizingCall(this, _, _) }
override predicate sanitizes(boolean outcome, Expr e) { f.isSanitizingCall(this, e, outcome) }
override predicate appliesTo(Configuration cfg) { f.appliesTo(cfg) }
}
/**
* An equality test on `e.origin` or `e.source` where `e` is a `postMessage` event object,
* considered as a sanitizer for `e`.