@@ -66,10 +66,22 @@ private CfgNodes::ExprCfgNode getALastEvalNode(CfgNodes::ExprCfgNode n) {
6666 )
6767}
6868
69- /** Gets a node for which to construct a post-update node for argument `arg`. */
70- CfgNodes:: ExprCfgNode getAPostUpdateNodeForArg ( Argument arg ) {
71- result = getALastEvalNode * ( arg ) and
72- not exists ( getALastEvalNode ( result ) )
69+ /**
70+ * Holds if a reverse local flow step should be added from the post-update node
71+ * for `e` to the post-update node for the result.
72+ *
73+ * This is needed to allow for side-effects on compound expressions to propagate
74+ * to sub components. For example, in
75+ *
76+ * ```ruby
77+ * (foo1; foo2).set_field(taint)
78+ * ```
79+ *
80+ * we add a reverse flow step from `[post] (foo1; foo2)` to `[post] foo2`,
81+ * in order for the side-effect of `set_field` to reach `foo2`.
82+ */
83+ CfgNodes:: ExprCfgNode getPostUpdateReverseStep ( CfgNodes:: ExprCfgNode e ) {
84+ result = getALastEvalNode ( e )
7385}
7486
7587/** Gets the SSA definition node corresponding to parameter `p`. */
@@ -165,6 +177,9 @@ module LocalFlow {
165177 )
166178 or
167179 nodeTo .( ImplicitBlockArgumentNode ) .getParameterNode ( true ) = nodeFrom
180+ or
181+ nodeTo .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( ) =
182+ getPostUpdateReverseStep ( nodeFrom .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( ) )
168183 }
169184
170185 predicate flowSummaryLocalStep (
@@ -481,7 +496,9 @@ private module Cached {
481496 // filter out nodes that clearly don't need post-update nodes
482497 isNonConstantExpr ( n ) and
483498 (
484- n = getAPostUpdateNodeForArg ( _)
499+ n instanceof Argument
500+ or
501+ n = getPostUpdateReverseStep ( any ( PostUpdateNode p ) .getPreUpdateNode ( ) .asExpr ( ) )
485502 or
486503 n = any ( CfgNodes:: ExprNodes:: InstanceVariableAccessCfgNode v ) .getReceiver ( )
487504 )
@@ -2013,18 +2030,7 @@ private module PostUpdateNodes {
20132030
20142031 ExprPostUpdateNode ( ) { this = TExprPostUpdateNode ( e ) }
20152032
2016- override ExprNode getPreUpdateNode ( ) {
2017- // For compound arguments, such as `m(if b then x else y)`, we want the leaf nodes
2018- // `[post] x` and `[post] y` to have two pre-update nodes: (1) the compound argument,
2019- // `if b then x else y`; and the (2) the underlying expressions; `x` and `y`,
2020- // respectively.
2021- //
2022- // This ensures that we get flow out of the call into both leafs (1), while still
2023- // maintaining the invariant that the underlying expression is a pre-update node (2).
2024- e = getAPostUpdateNodeForArg ( result .getExprNode ( ) )
2025- or
2026- e = result .getExprNode ( )
2027- }
2033+ override ExprNode getPreUpdateNode ( ) { e = result .getExprNode ( ) }
20282034
20292035 override CfgScope getCfgScope ( ) { result = e .getExpr ( ) .getCfgScope ( ) }
20302036
0 commit comments