Merge pull request #10378 from erik-krogh/aliasFlow

JS: expand localFieldStep to use access-paths, and build access-paths in more cases
This commit is contained in:
Erik Krogh Kristensen
2022-11-08 14:26:12 +01:00
committed by GitHub
7 changed files with 297 additions and 3 deletions

View File

@@ -233,6 +233,8 @@ module AccessPath {
baseName = fromReference(write.getBase(), root)
or
baseName = fromRhs(write.getBase(), root)
or
baseName = fromRhs(GetLaterAccess::getLaterBaseAccess(write), root)
)
or
exists(GlobalVariable var |
@@ -266,6 +268,100 @@ module AccessPath {
)
}
/** A module for computing an access to a variable that happens after a property has been written onto it */
private module GetLaterAccess {
/**
* Gets an access to a variable that is written to in `write`, where the access is after the write.
*
* This allows `fromRhs` to compute an access path for e.g. the below example:
* ```JavaScript
* function foo(x) {
* var obj = {
* bar: x // `x` has the access path "foo.bar" starting from the root `this`.
* };
* this.foo = obj;
* }
* ```
*/
pragma[noopt]
DataFlow::Node getLaterBaseAccess(DataFlow::PropWrite write) {
exists(
ControlFlowNode writeNode, BindingPattern access, VarRef otherAccess, Variable variable,
StmtContainer container
|
access = getBaseVar(write) and
writeNode = write.getWriteNode() and
access = getAnAccessInContainer(variable, container, true) and
variable = getARelevantVariable() and // manual magic
otherAccess = getAnAccessInContainer(variable, container, false) and
access != otherAccess and
result.asExpr() = otherAccess
|
exists(BasicBlock bb, int i, int j |
bb.getNode(i) = writeNode and
bb.getNode(j) = otherAccess and
i < j
)
or
otherAccess.getBasicBlock() = getASuccessorBBThatReadsVar(write) // more manual magic - outlined into a helper predicate.
)
}
/** Gets a variable ref that `write` writes a property to. */
VarRef getBaseVar(DataFlow::PropWrite write) {
result = write.getBase().asExpr()
or
exists(Assignment assign |
write.getBase().asExpr() = assign.getRhs() and
result = assign.getLhs()
)
or
exists(VariableDeclarator decl |
write.getBase().asExpr() = decl.getInit() and
result = decl.getBindingPattern()
)
}
/** Gets an access to `var` inside `container` where `usedInWrite` indicates whether the access is the base of a property write. */
private VarRef getAnAccessInContainer(Variable var, StmtContainer container, boolean usedInWrite) {
result.getVariable() = var and
result.getContainer() = container and
var.isLocal() and
if result = getBaseVar(_) then usedInWrite = true else usedInWrite = false
}
/** Gets a variable that is relevant for the computations in the `GetLaterAccess` module. */
private Variable getARelevantVariable() {
// The variable might be used where `getLaterBaseAccess()` is called.
exists(DataFlow::Node node |
exists(fromRhs(node, _)) and
node.asExpr().(VarAccess).getVariable() = result
) and
// There is a write that writes to the variable.
getBaseVar(_).getVariable() = result and
// It's local.
result.isLocal() and // we skip global variables, because that turns messy quick.
// There is both a "write" and "read" in the same container of the variable.
exists(StmtContainer container |
exists(getAnAccessInContainer(result, container, true)) and // a "write", an access to the variable that is the base of a property reference.
exists(getAnAccessInContainer(result, container, false)) // a "read", an access to the variable that is not the base of a property reference.
)
}
/** Gets a basic-block that has a read of the variable that is written to by `write`, where the basicblock occurs after `start`. */
private ReachableBasicBlock getASuccessorBBThatReadsVar(DataFlow::PropWrite write) {
exists(VarAccess baseExpr, Variable var, ControlFlowNode writeNode |
baseExpr = getBaseVar(write) and
var = baseExpr.getVariable() and
var = getARelevantVariable() and
writeNode = write.getWriteNode() and
writeNode.getBasicBlock().(ReachableBasicBlock).strictlyDominates(result) and
// manual magic.
result = getAnAccessInContainer(getARelevantVariable(), _, false).getBasicBlock()
)
}
}
/**
* Gets a node that refers to the given access path relative to the given `root` node,
* or `root` itself if the access path is empty.

View File

@@ -1692,10 +1692,10 @@ module DataFlow {
*/
predicate localFieldStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(ClassNode cls, string prop |
pred = cls.getADirectSuperClass*().getAReceiverNode().getAPropertyWrite(prop).getRhs() or
pred = AccessPath::getAnAssignmentTo(cls.getADirectSuperClass*().getAReceiverNode(), prop) or
pred = cls.getInstanceMethod(prop)
|
succ = cls.getAReceiverNode().getAPropertyRead(prop)
succ = AccessPath::getAReferenceTo(cls.getAReceiverNode(), prop)
)
}