@@ -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`. */
@@ -170,6 +182,9 @@ module LocalFlow {
170182 )
171183 or
172184 nodeTo .( ImplicitBlockArgumentNode ) .getParameterNode ( true ) = nodeFrom
185+ or
186+ nodeTo .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( ) =
187+ getPostUpdateReverseStep ( nodeFrom .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( ) )
173188 }
174189
175190 predicate flowSummaryLocalStep (
@@ -486,7 +501,9 @@ private module Cached {
486501 // filter out nodes that clearly don't need post-update nodes
487502 isNonConstantExpr ( n ) and
488503 (
489- n = getAPostUpdateNodeForArg ( _)
504+ n instanceof Argument
505+ or
506+ n = getPostUpdateReverseStep ( any ( PostUpdateNode p ) .getPreUpdateNode ( ) .asExpr ( ) )
490507 or
491508 n = any ( CfgNodes:: ExprNodes:: InstanceVariableAccessCfgNode v ) .getReceiver ( )
492509 )
@@ -2018,18 +2035,7 @@ private module PostUpdateNodes {
20182035
20192036 ExprPostUpdateNode ( ) { this = TExprPostUpdateNode ( e ) }
20202037
2021- override ExprNode getPreUpdateNode ( ) {
2022- // For compound arguments, such as `m(if b then x else y)`, we want the leaf nodes
2023- // `[post] x` and `[post] y` to have two pre-update nodes: (1) the compound argument,
2024- // `if b then x else y`; and the (2) the underlying expressions; `x` and `y`,
2025- // respectively.
2026- //
2027- // This ensures that we get flow out of the call into both leafs (1), while still
2028- // maintaining the invariant that the underlying expression is a pre-update node (2).
2029- e = getAPostUpdateNodeForArg ( result .getExprNode ( ) )
2030- or
2031- e = result .getExprNode ( )
2032- }
2038+ override ExprNode getPreUpdateNode ( ) { e = result .getExprNode ( ) }
20332039
20342040 override CfgScope getCfgScope ( ) { result = e .getExpr ( ) .getCfgScope ( ) }
20352041
0 commit comments