mirror of
https://github.com/github/codeql.git
synced 2025-12-20 02:44:30 +01:00
rewrite parts of the DeadStoreOfProperty query
This commit is contained in:
@@ -67,18 +67,25 @@ int getRank(ReachableBasicBlock bb, ControlFlowNode ref, string name) {
|
|||||||
unambiguousPropWrite(write)
|
unambiguousPropWrite(write)
|
||||||
)
|
)
|
||||||
or
|
or
|
||||||
e.(PropAccess).getPropertyName() = name and e instanceof RValue
|
isAPropertyRead(e, name)
|
||||||
|
|
|
|
||||||
e order by any(int i | e = bb.getNode(i))
|
e order by any(int i | e = bb.getNode(i))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `e` is a property read of a property `name`.
|
||||||
|
*/
|
||||||
|
predicate isAPropertyRead(Expr e, string name) {
|
||||||
|
exists(DataFlow::PropRead read | read.asExpr() = e and read.getPropertyName() = name)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `e` may access a property named `name`.
|
* Holds if `e` may access a property named `name`.
|
||||||
*/
|
*/
|
||||||
bindingset[name]
|
bindingset[name]
|
||||||
predicate maybeAccessesProperty(Expr e, string name) {
|
predicate maybeAccessesProperty(Expr e, string name) {
|
||||||
e.(PropAccess).getPropertyName() = name and e instanceof RValue
|
isAPropertyRead(e, name)
|
||||||
or
|
or
|
||||||
// conservatively reject all side-effects
|
// conservatively reject all side-effects
|
||||||
e.isImpure()
|
e.isImpure()
|
||||||
@@ -89,7 +96,8 @@ predicate maybeAccessesProperty(Expr e, string name) {
|
|||||||
*/
|
*/
|
||||||
predicate isDeadAssignment(string name, DataFlow::PropWrite assign1, DataFlow::PropWrite assign2) {
|
predicate isDeadAssignment(string name, DataFlow::PropWrite assign1, DataFlow::PropWrite assign2) {
|
||||||
(
|
(
|
||||||
noPropAccessBetweenLocal(name, assign1, assign2)
|
assign2.getWriteNode() = getANodeWithNoPropAccessBetweenInsideBlock(name, assign1) and
|
||||||
|
postDominatedPropWrite(name, assign1, assign2, true)
|
||||||
or
|
or
|
||||||
noPropAccessBetweenGlobal(name, assign1, assign2)
|
noPropAccessBetweenGlobal(name, assign1, assign2)
|
||||||
) and
|
) and
|
||||||
@@ -110,8 +118,7 @@ predicate maybeAssignsAccessedPropInBlock(DataFlow::PropWrite assign, boolean af
|
|||||||
exists(ReachableBasicBlock block, int i, int j, Expr e, string name |
|
exists(ReachableBasicBlock block, int i, int j, Expr e, string name |
|
||||||
i = getRank(block, assign.getWriteNode(), name) and
|
i = getRank(block, assign.getWriteNode(), name) and
|
||||||
j = getRank(block, e, name) and
|
j = getRank(block, e, name) and
|
||||||
e instanceof PropAccess and
|
isAPropertyRead(e, name)
|
||||||
e instanceof RValue
|
|
||||||
|
|
|
|
||||||
after = true and i < j
|
after = true and i < j
|
||||||
or
|
or
|
||||||
@@ -176,71 +183,38 @@ ControlFlowNode getANodeBeforeWrite(DataFlow::PropWrite write, ReachableBasicBlo
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a successor inside `bb` in the control-flow graph that does not pass through an impure expression (except for writes to the same property).
|
* Holds if `write` and `result` are inside the same basicblock, and `write` assigns property `name`, and `result` is a (transitive) successor of `write`, and `name` is not accessed between `write` and `result`.
|
||||||
* Stops after the first write to same property that happens after `node`.
|
*
|
||||||
*
|
* The predicate is computed recursively by computing transitive successors of `write` while removing the successors that could access `name`.
|
||||||
* `node` always corresponds to the CFG node of a `DataFlow::PropWrite` with a known name.
|
* Stops at the first write to `name` that occours after `write`.
|
||||||
*/
|
*/
|
||||||
ControlFlowNode getAPureSuccessor(ControlFlowNode node) {
|
ControlFlowNode getANodeWithNoPropAccessBetweenInsideBlock(string name, DataFlow::PropWrite write) {
|
||||||
// stop at reads of `name` and at impure expressions (except writes to `name`)
|
|
||||||
not (
|
|
||||||
maybeAccessesProperty(result, getPropertyWriteName(node)) and
|
|
||||||
not result =
|
|
||||||
any(DataFlow::PropWrite write | write.getPropertyName() = getPropertyWriteName(node))
|
|
||||||
.getWriteNode()
|
|
||||||
) and
|
|
||||||
(
|
(
|
||||||
// base case.
|
// base case.
|
||||||
exists(DataFlow::PropWrite write |
|
result = write.getWriteNode().getASuccessor() and
|
||||||
node = write.getWriteNode() and
|
postDominatedPropWrite(name, write, _, true) // manual magic - cheap check that there might exist a result we are interrested in,
|
||||||
result = node.getASuccessor() and
|
|
||||||
// cheap check that there might exist a result we are interrested in,
|
|
||||||
postDominatedPropWrite(_, write, _, true)
|
|
||||||
)
|
|
||||||
or
|
or
|
||||||
// recursive case
|
// recursive case
|
||||||
result = getAPureSuccessor(node).getASuccessor() and
|
result = getANodeWithNoPropAccessBetweenInsideBlock(name, write).getASuccessor()
|
||||||
// stop at basic-block boundaries
|
) and
|
||||||
not result instanceof BasicBlock and
|
// stop at basic-block boundaries
|
||||||
// make sure we stop after the first write to the same property that comes after `node`.
|
not result instanceof BasicBlock and
|
||||||
(
|
// stop at reads of `name` and at impure expressions (except writes to `name`)
|
||||||
not result.getAPredecessor() =
|
not (
|
||||||
any(DataFlow::PropWrite write | write.getPropertyName() = getPropertyWriteName(node))
|
maybeAccessesProperty(result, name) and
|
||||||
.getWriteNode()
|
not result = any(DataFlow::PropWrite w | w.getPropertyName() = name).getWriteNode()
|
||||||
or
|
) and
|
||||||
result.getAPredecessor() = node
|
// stop at the first write to `name` that comes after `write`.
|
||||||
)
|
(
|
||||||
)
|
not result.getAPredecessor() =
|
||||||
}
|
any(DataFlow::PropWrite w | w.getPropertyName() = name).getWriteNode()
|
||||||
|
or
|
||||||
/**
|
result.getAPredecessor() = write.getWriteNode()
|
||||||
* Gets the property name that is written to by the control-flow-node `writeNode`.
|
|
||||||
*/
|
|
||||||
private string getPropertyWriteName(ControlFlowNode writeNode) {
|
|
||||||
exists(DataFlow::PropWrite write |
|
|
||||||
write.getWriteNode() = writeNode and result = write.getPropertyName()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Holds if `assign1` and `assign2` are inside the same basicblock and both assign property `name`, and the assigned property is not accessed between the two assignments.
|
|
||||||
*/
|
|
||||||
predicate noPropAccessBetweenLocal(
|
|
||||||
string name, DataFlow::PropWrite assign1, DataFlow::PropWrite assign2
|
|
||||||
) {
|
|
||||||
exists(ControlFlowNode write1, ControlFlowNode write2 |
|
|
||||||
postDominatedPropWrite(name, assign1, assign2, true) and
|
|
||||||
write1 = assign1.getWriteNode() and
|
|
||||||
write2 = assign2.getWriteNode() and
|
|
||||||
getRank(_, write1, name) < getRank(_, write2, name) and
|
|
||||||
write2 = getAPureSuccessor(write1)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds if `assign1` and `assign2` are in different basicblocks and both assign property `name`, and the assigned property is not accessed between the two assignments.
|
* Holds if `assign1` and `assign2` are in different basicblocks and both assign property `name`, and the assigned property is not accessed between the two assignments.
|
||||||
*
|
|
||||||
* Much of this predicate is copy-pasted from `noPropAccessBetweenLocal`, but the predicates are separate to avoid join-order issues.
|
|
||||||
*/
|
*/
|
||||||
pragma[nomagic]
|
pragma[nomagic]
|
||||||
predicate noPropAccessBetweenGlobal(
|
predicate noPropAccessBetweenGlobal(
|
||||||
@@ -250,7 +224,7 @@ predicate noPropAccessBetweenGlobal(
|
|||||||
ControlFlowNode write1, ControlFlowNode write2, ReachableBasicBlock block1,
|
ControlFlowNode write1, ControlFlowNode write2, ReachableBasicBlock block1,
|
||||||
ReachableBasicBlock block2
|
ReachableBasicBlock block2
|
||||||
|
|
|
|
||||||
postDominatedPropWrite(name, assign1, assign2, false) and // early pruning
|
postDominatedPropWrite(name, assign1, assign2, false) and // manual magic - early pruning
|
||||||
write1 = assign1.getWriteNode() and
|
write1 = assign1.getWriteNode() and
|
||||||
not maybeAssignsAccessedPropInBlock(assign1, true) and
|
not maybeAssignsAccessedPropInBlock(assign1, true) and
|
||||||
write2 = assign2.getWriteNode() and
|
write2 = assign2.getWriteNode() and
|
||||||
@@ -258,7 +232,6 @@ predicate noPropAccessBetweenGlobal(
|
|||||||
write1.getBasicBlock() = block1 and
|
write1.getBasicBlock() = block1 and
|
||||||
write2.getBasicBlock() = block2 and
|
write2.getBasicBlock() = block2 and
|
||||||
not block1 = block2 and
|
not block1 = block2 and
|
||||||
// other block:
|
|
||||||
// check for an access between the two write blocks
|
// check for an access between the two write blocks
|
||||||
not exists(ReachableBasicBlock mid |
|
not exists(ReachableBasicBlock mid |
|
||||||
block1.getASuccessor+() = mid and
|
block1.getASuccessor+() = mid and
|
||||||
|
|||||||
Reference in New Issue
Block a user