@@ -63,17 +63,26 @@ private module Cached {
6363 newtype TNode =
6464 TExprNode ( CfgNode n , Expr e ) { hasExprNode ( n , e ) } or
6565 TSsaDefinitionNode ( Ssa:: Definition def ) or
66- TInoutReturnNode ( ParamDecl param ) { param .isInout ( ) } or
67- TInOutUpdateNode ( Argument arg ) { arg .getExpr ( ) instanceof InOutExpr } or
68- TSummaryNode ( FlowSummary:: SummarizedCallable c , FlowSummaryImpl:: Private:: SummaryNodeState state )
69-
70- private predicate hasExprNode ( CfgNode n , Expr e ) {
71- n .( ExprCfgNode ) .getExpr ( ) = e
72- or
73- n .( PropertyGetterCfgNode ) .getRef ( ) = e
74- or
75- n .( PropertySetterCfgNode ) .getAssignExpr ( ) = e
76- }
66+ TInoutReturnNode ( ParamDecl param ) { modifiableParam ( param ) } or
67+ TSummaryNode ( FlowSummary:: SummarizedCallable c , FlowSummaryImpl:: Private:: SummaryNodeState state ) or
68+ TExprPostUpdateNode ( CfgNode n ) {
69+ // Obviously, the base of setters needs a post-update node
70+ n = any ( PropertySetterCfgNode setter ) .getBase ( )
71+ or
72+ // The base of getters and observers needs a post-update node to support reverse reads.
73+ n = any ( PropertyGetterCfgNode getter ) .getBase ( )
74+ or
75+ n = any ( PropertyObserverCfgNode getter ) .getBase ( )
76+ or
77+ // Arguments that are `inout` expressions needs a post-update node,
78+ // as well as any class-like argument (since a field can be modified).
79+ // Finally, qualifiers and bases of member reference need post-update nodes to support reverse reads.
80+ hasExprNode ( n ,
81+ [
82+ any ( Argument arg | modifiable ( arg ) ) .getExpr ( ) , any ( MemberRefExpr ref ) .getBase ( ) ,
83+ any ( ApplyExpr apply ) .getQualifier ( )
84+ ] )
85+ }
7786
7887 private predicate localSsaFlowStepUseUse ( Ssa:: Definition def , Node nodeFrom , Node nodeTo ) {
7988 def .adjacentReadPair ( nodeFrom .getCfgNode ( ) , nodeTo .getCfgNode ( ) ) and
@@ -102,18 +111,12 @@ private module Cached {
102111 // use-use flow
103112 localSsaFlowStepUseUse ( def , nodeFrom , nodeTo )
104113 or
105- // localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo)
106- // or
114+ localSsaFlowStepUseUse ( def , nodeFrom .( PostUpdateNode ) .getPreUpdateNode ( ) , nodeTo )
115+ or
107116 // step from previous read to Phi node
108117 localFlowSsaInput ( nodeFrom , def , nodeTo .asDefinition ( ) )
109118 )
110119 or
111- // flow through writes to inout parameters
112- exists ( ParamReturnKind kind , ExprCfgNode arg |
113- arg = nodeFrom .( InOutUpdateNode ) .getCall ( kind ) .asCall ( ) .getArgument ( kind .getIndex ( ) ) and
114- nodeTo .asDefinition ( ) .( Ssa:: WriteDefinition ) .isInoutDef ( arg )
115- )
116- or
117120 // flow through `&` (inout argument)
118121 nodeFrom .asExpr ( ) = nodeTo .asExpr ( ) .( InOutExpr ) .getSubExpr ( )
119122 or
@@ -138,10 +141,36 @@ private module Cached {
138141 predicate localFlowStepImpl ( Node nodeFrom , Node nodeTo ) { localFlowStepCommon ( nodeFrom , nodeTo ) }
139142
140143 cached
141- newtype TContentSet = TODO_TContentSet ( )
144+ newtype TContentSet = TSingletonContent ( Content c )
142145
143146 cached
144- newtype TContent = TODO_Content ( )
147+ newtype TContent = TFieldContent ( FieldDecl f )
148+ }
149+
150+ /**
151+ * Holds if `arg` can be modified (by overwriting the content completely),
152+ * or if any of its fields can be overwritten by a function call.
153+ */
154+ private predicate modifiable ( Argument arg ) {
155+ arg .getExpr ( ) instanceof InOutExpr
156+ or
157+ arg .getExpr ( ) .getType ( ) instanceof NominalType
158+ }
159+
160+ predicate modifiableParam ( ParamDecl param ) {
161+ param .isInout ( )
162+ or
163+ param instanceof SelfParamDecl
164+ }
165+
166+ private predicate hasExprNode ( CfgNode n , Expr e ) {
167+ n .( ExprCfgNode ) .getExpr ( ) = e
168+ or
169+ n .( PropertyGetterCfgNode ) .getRef ( ) = e
170+ or
171+ n .( PropertySetterCfgNode ) .getAssignExpr ( ) = e
172+ or
173+ n .( PropertyObserverCfgNode ) .getAssignExpr ( ) = e
145174}
146175
147176import Cached
@@ -341,40 +370,92 @@ private module OutNodes {
341370 }
342371 }
343372
344- class InOutUpdateNode extends OutNode , TInOutUpdateNode , NodeImpl {
373+ class InOutUpdateArgNode extends OutNode , ExprPostUpdateNode {
345374 Argument arg ;
346375
347- InOutUpdateNode ( ) { this = TInOutUpdateNode ( arg ) }
376+ InOutUpdateArgNode ( ) {
377+ modifiable ( arg ) and
378+ hasExprNode ( n , arg .getExpr ( ) )
379+ }
348380
349381 override DataFlowCall getCall ( ReturnKind kind ) {
350- result .asCall ( ) . getExpr ( ) = arg . getApplyExpr ( ) and
382+ result .getAnArgument ( ) = n and
351383 kind .( ParamReturnKind ) .getIndex ( ) = arg .getIndex ( )
352384 }
385+ }
353386
354- override DataFlowCallable getEnclosingCallable ( ) {
355- result = this .getCall ( _) .getEnclosingCallable ( )
387+ class InOutUpdateQualifierNode extends OutNode , ExprPostUpdateNode {
388+ InOutUpdateQualifierNode ( ) { hasExprNode ( n , any ( ApplyExpr apply ) .getQualifier ( ) ) }
389+
390+ override DataFlowCall getCall ( ReturnKind kind ) {
391+ result .getAnArgument ( ) = n and
392+ kind .( ParamReturnKind ) .getIndex ( ) = - 1
356393 }
394+ }
395+
396+ class PropertySetterOutNode extends OutNode , ExprNodeImpl {
397+ PropertySetterCfgNode setter ;
357398
358- override Location getLocationImpl ( ) { result = arg . getLocation ( ) }
399+ PropertySetterOutNode ( ) { setter = this . getCfgNode ( ) }
359400
360- override string toStringImpl ( ) { result = arg .toString ( ) }
401+ override DataFlowCall getCall ( ReturnKind kind ) {
402+ result .( PropertySetterCall ) .getSetter ( ) = setter and kind .( ParamReturnKind ) .getIndex ( ) = - 1
403+ }
404+ }
405+
406+ class PropertyGetterOutNode extends OutNode , ExprNodeImpl {
407+ PropertyGetterCfgNode getter ;
408+
409+ PropertyGetterOutNode ( ) { getter = this .getCfgNode ( ) }
410+
411+ override DataFlowCall getCall ( ReturnKind kind ) {
412+ result .( PropertyGetterCall ) .getGetter ( ) = getter and kind instanceof NormalReturnKind
413+ }
414+ }
415+
416+ class PropertyObserverOutNode extends OutNode , ExprNodeImpl {
417+ PropertyObserverCfgNode observer ;
418+
419+ PropertyObserverOutNode ( ) { observer = this .getCfgNode ( ) }
420+
421+ override DataFlowCall getCall ( ReturnKind kind ) {
422+ result .( PropertyGetterCall ) .getGetter ( ) = observer and kind .( ParamReturnKind ) .getIndex ( ) = - 1
423+ }
361424 }
362425}
363426
364427import OutNodes
365428
366429predicate jumpStep ( Node pred , Node succ ) { none ( ) }
367430
368- predicate storeStep ( Node node1 , ContentSet c , Node node2 ) { none ( ) }
431+ predicate storeStep ( Node node1 , ContentSet c , Node node2 ) {
432+ exists ( MemberRefExpr ref , AssignExpr assign |
433+ ref = assign .getDest ( ) and
434+ node1 .asExpr ( ) = assign .getSource ( ) and
435+ node2 .( PostUpdateNode ) .getPreUpdateNode ( ) .asExpr ( ) = ref .getBase ( ) and
436+ c .isSingleton ( any ( Content:: FieldContent ct | ct .getField ( ) = ref .getMember ( ) ) )
437+ )
438+ }
369439
370- predicate readStep ( Node node1 , ContentSet c , Node node2 ) { none ( ) }
440+ predicate isLValue ( Expr e ) { any ( AssignExpr assign ) .getDest ( ) = e }
441+
442+ predicate readStep ( Node node1 , ContentSet c , Node node2 ) {
443+ exists ( MemberRefExpr ref |
444+ not isLValue ( ref ) and
445+ node1 .asExpr ( ) = ref .getBase ( ) and
446+ node2 .asExpr ( ) = ref and
447+ c .isSingleton ( any ( Content:: FieldContent ct | ct .getField ( ) = ref .getMember ( ) ) )
448+ )
449+ }
371450
372451/**
373452 * Holds if values stored inside content `c` are cleared at node `n`. For example,
374453 * any value stored inside `f` is cleared at the pre-update node associated with `x`
375454 * in `x.f = newValue`.
376455 */
377- predicate clearsContent ( Node n , ContentSet c ) { none ( ) }
456+ predicate clearsContent ( Node n , ContentSet c ) {
457+ n = any ( PostUpdateNode pun | storeStep ( _, c , pun ) ) .getPreUpdateNode ( )
458+ }
378459
379460/**
380461 * Holds if the value that is being tracked is expected to be stored inside content `c`
@@ -408,7 +489,21 @@ abstract class PostUpdateNodeImpl extends Node {
408489 abstract Node getPreUpdateNode ( ) ;
409490}
410491
411- private module PostUpdateNodes { }
492+ private module PostUpdateNodes {
493+ class ExprPostUpdateNode extends PostUpdateNodeImpl , NodeImpl , TExprPostUpdateNode {
494+ CfgNode n ;
495+
496+ ExprPostUpdateNode ( ) { this = TExprPostUpdateNode ( n ) }
497+
498+ override ExprNode getPreUpdateNode ( ) { n = result .getCfgNode ( ) }
499+
500+ override Location getLocationImpl ( ) { result = n .getLocation ( ) }
501+
502+ override string toStringImpl ( ) { result = "[post] " + n .toString ( ) }
503+
504+ override DataFlowCallable getEnclosingCallable ( ) { result = TDataFlowFunc ( n .getScope ( ) ) }
505+ }
506+ }
412507
413508private import PostUpdateNodes
414509
0 commit comments