Skip to content

Commit 0be61be

Browse files
committed
C++: Handle flow out of post-update nodes when there's another use of the variable in the call that we need to skip.
1 parent d5442ec commit 0be61be

File tree

1 file changed

+69
-10
lines changed

1 file changed

+69
-10
lines changed

cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -638,12 +638,24 @@ private predicate adjustForPointerArith(PostUpdateNode pun, UseOrPhi use) {
638638
)
639639
}
640640

641+
/**
642+
* Holds if `nodeFrom` flows to `nodeTo` because there is `def-use` or
643+
* `use-use` flow from `defOrUse` to `use`.
644+
*
645+
* `uncertain` is `true` if the `defOrUse` is an uncertain definition.
646+
*/
647+
private predicate localSsaFlow(
648+
SsaDefOrUse defOrUse, Node nodeFrom, UseOrPhi use, Node nodeTo, boolean uncertain
649+
) {
650+
nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and
651+
adjacentDefRead(defOrUse, use) and
652+
useToNode(use, nodeTo) and
653+
nodeFrom != nodeTo
654+
}
655+
641656
private predicate ssaFlowImpl(SsaDefOrUse defOrUse, Node nodeFrom, Node nodeTo, boolean uncertain) {
642657
exists(UseOrPhi use |
643-
nodeToDefOrUse(nodeFrom, defOrUse, uncertain) and
644-
adjacentDefRead(defOrUse, use) and
645-
useToNode(use, nodeTo) and
646-
nodeFrom != nodeTo
658+
localSsaFlow(defOrUse, nodeFrom, use, nodeTo, uncertain)
647659
or
648660
// Initial global variable value to a first use
649661
nodeFrom.(InitialGlobalValue).getGlobalDef() = defOrUse and
@@ -721,15 +733,62 @@ private predicate isArgumentOfCallable(DataFlowCall call, Node n) {
721733
)
722734
}
723735

724-
/** Holds if there is def-use or use-use flow from `pun` to `nodeTo`. */
725-
predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
726-
exists(UseOrPhi use, Node preUpdate |
736+
/**
737+
* Holds if there is use-use flow from `pun`'s pre-update node to `n`.
738+
*/
739+
private predicate postUpdateNodeToFirstUse(PostUpdateNode pun, Node n) {
740+
exists(UseOrPhi use |
727741
adjustForPointerArith(pun, use) and
728-
useToNode(use, nodeTo) and
742+
useToNode(use, n)
743+
)
744+
}
745+
746+
private predicate stepUntilNotInCall(DataFlowCall call, Node n1, Node n2) {
747+
isArgumentOfCallable(call, n1) and
748+
exists(Node mid | localSsaFlow(_, n1, _, mid, _) |
749+
isArgumentOfCallable(call, mid) and
750+
stepUntilNotInCall(call, mid, n2)
751+
or
752+
not isArgumentOfCallable(call, mid) and
753+
mid = n2
754+
)
755+
}
756+
757+
bindingset[n1, n2]
758+
pragma[inline_late]
759+
private predicate isArgumentOfSameCall(DataFlowCall call, Node n1, Node n2) {
760+
isArgumentOfCallable(call, n1) and isArgumentOfCallable(call, n2)
761+
}
762+
763+
/**
764+
* Holds if there is def-use or use-use flow from `pun` to `nodeTo`.
765+
*
766+
* Note: This is more complex than it sounds. Consider a call such as:
767+
* ```cpp
768+
* write_first_argument(x, x);
769+
* sink(x);
770+
* ```
771+
* Assume flow comes out of the first argument to `write_first_argument`. We
772+
* don't want flow to go to the `x` that's also an argument to
773+
* `write_first_argument` (because we just flowed out of that function, and we
774+
* don't want to flow back into it again).
775+
*
776+
* We do, however, want flow from the output argument to `x` on the next line, and
777+
* similarly we want flow from the second argument of `write_first_argument` to `x`
778+
* on the next line.
779+
*/
780+
predicate postUpdateFlow(PostUpdateNode pun, Node nodeTo) {
781+
exists(Node preUpdate, Node mid |
729782
preUpdate = pun.getPreUpdateNode() and
730-
not exists(DataFlowCall call |
731-
isArgumentOfCallable(call, preUpdate) and isArgumentOfCallable(call, nodeTo)
783+
postUpdateNodeToFirstUse(pun, mid)
784+
|
785+
exists(DataFlowCall call |
786+
isArgumentOfSameCall(call, preUpdate, mid) and
787+
stepUntilNotInCall(call, mid, nodeTo)
732788
)
789+
or
790+
not isArgumentOfSameCall(_, preUpdate, mid) and
791+
nodeTo = mid
733792
)
734793
}
735794

0 commit comments

Comments
 (0)