@@ -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+
641656private 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