JavaScript: Add support for sanitising dynamic property accesses.

This generalises our previous handling of sanitisers operating on property accesses to support dynamic property accesses where the property name is an SSA variable by representing them as access paths.
This commit is contained in:
Max Schaefer
2018-11-22 17:46:40 +00:00
parent f4ec168666
commit fb78e14db1
6 changed files with 153 additions and 34 deletions

View File

@@ -70,6 +70,7 @@
import javascript
private import internal.FlowSteps
private import internal.AccessPaths
/**
* A data flow tracking configuration for finding inter-procedural paths from
@@ -291,26 +292,12 @@ abstract class BarrierGuardNode extends DataFlow::Node {
)
)
or
// 2) `nd` is a use of an SSA variable `ssa`, and dominated by a barrier for `ssa`
exists (SsaVariable ssa, BasicBlock bb |
nd = DataFlow::valueNode(ssa.getAUseIn(bb)) and
exists (ConditionGuardNode cond |
asExpr() = cond.getTest() and
blocks(cond.getOutcome(), ssa.getAUse()) and
cond.dominates(bb)
)
)
or
// 3) `nd` is a property access `ssa.p.q` on an SSA variable `ssa`, and dominated by
// a barrier for `ssa.p.q`
exists (SsaVariable ssa, string props, BasicBlock bb |
nd = DataFlow::valueNode(nestedPropAccessOnSsaVar(ssa, props)) and
bb = nd.getBasicBlock() |
exists (ConditionGuardNode cond |
asExpr() = cond.getTest() and
blocks(cond.getOutcome(), nestedPropAccessOnSsaVar(ssa, props)) and
cond.dominates(bb)
)
// 2) `nd` is an instance of an access path `p`, and dominated by a barrier for `p`
exists (AccessPath p, BasicBlock bb, ConditionGuardNode cond |
nd = DataFlow::valueNode(p.getAnInstanceIn(bb)) and
asExpr() = cond.getTest() and
blocks(cond.getOutcome(), p.getAnInstance()) and
cond.dominates(bb)
)
}
@@ -330,20 +317,6 @@ abstract class LabeledBarrierGuardNode extends BarrierGuardNode {
abstract FlowLabel getALabel();
}
/**
* Holds if `props` is a string of the form `p.q.r`, and the result is a property access
* of the form `v.p.q.r`.
*/
private DotExpr nestedPropAccessOnSsaVar(SsaVariable v, string props) {
exists (Expr base, string prop | result.accesses(base, prop) |
base = v.getAUse() and props = prop
or
exists (string prevProps |
base = nestedPropAccessOnSsaVar(v, prevProps) and
props = prevProps + "." + prop
)
)
}
/**
* A data flow edge that should be added to all data flow configurations in

View File

@@ -0,0 +1,117 @@
/**
* Provides classes for reasoning about certain property accesses as access paths.
*
* Access paths represent SSA variables with zero or more property accesses applied to them.
* Each property name in the access must either be constant or itself be a use of an SSA
* variable.
*
* If `x` and `y` are SSA variables, then `x`, `x.p`, `x.p["q"]`, `x[y]` and `x[y].q` are
* access paths, but `x[x.y]` is not an access path.
*
* Access paths can be used to identify expressions that have the same value, disregarding
* any heap modifications. In general, expressions that are instances of the same access
* path are not guaranteed to evaluate to the same value nor do all expressions that evaluate
* to the same value have the same access paths, so access paths are neither sound nor
* complete as an approximation of expression semantics.
*/
import javascript
/**
* A representation of a property name that is either statically known or is
* the value of an SSA variable.
*/
private newtype PropertyName =
StaticPropertyName(string name) {
exists (PropAccess pa | name = pa.getPropertyName())
}
or
DynamicPropertyName(SsaVariable var) {
exists (PropAccess pa | pa.getPropertyNameExpr() = var.getAUse())
}
/**
* Gets the representation of the property name of `pacc`, if any.
*/
private PropertyName getPropertyName(PropAccess pacc) {
result = StaticPropertyName(pacc.getPropertyName())
or
exists (SsaVariable var |
pacc.getPropertyNameExpr() = var.getAUse() and
result = DynamicPropertyName(var)
)
}
/**
* A representation of a (nested) property access on an SSA variable
* where each property name is either constant or itself an SSA variable.
*/
private newtype TAccessPath =
MkRoot(SsaVariable var)
or
MkAccessStep(AccessPath base, PropertyName name) {
exists (PropAccess pacc |
pacc.getBase() = base.getAnInstance() and
getPropertyName(pacc) = name
)
}
/**
* A representation of a (nested) property access on an SSA variable
* where each property name is either constant or itself an SSA variable.
*/
class AccessPath extends TAccessPath {
/**
* Gets an expression in `bb` represented by this access path.
*/
Expr getAnInstanceIn(BasicBlock bb) {
exists (SsaVariable var |
this = MkRoot(var) and
result = var.getAUseIn(bb)
)
or
exists (PropertyName name |
result = getABaseInstanceIn(bb, name) and
getPropertyName(result) = name
)
}
/**
* Gets a property access in `bb` whose base is represented by the
* base of this access path, and where `name` is bound to the last
* component of this access path.
*
* This is an auxiliary predicate that's needed to enforce a better
* join order in `getAnInstanceIn` above.
*/
pragma[noinline]
private PropAccess getABaseInstanceIn(BasicBlock bb, PropertyName name) {
exists (AccessPath base | this = MkAccessStep(base, name) |
result.getBase() = base.getAnInstanceIn(bb)
)
}
/**
* Gets an expression represented by this access path.
*/
Expr getAnInstance() {
result = getAnInstanceIn(_)
}
/**
* Gets a textual representation of this access path.
*/
string toString() {
exists (SsaVariable var | this = MkRoot(var) |
result = var.getSourceVariable().getName()
)
or
exists (AccessPath base, PropertyName name, string rest |
rest = "." + any(string s | name = StaticPropertyName(s))
or
rest = "[" + any(SsaVariable var | name = DynamicPropertyName(var)).getSourceVariable().getName() + "]" |
result = base.toString() + rest and
this = MkAccessStep(base, name)
)
}
}