@@ -141,8 +141,6 @@ final class ArgumentPosition extends ParameterPosition {
141141 * Holds if `arg` is an argument of `call` at the position `pos`.
142142 */
143143predicate isArgumentForCall ( Expr arg , Call call , ArgumentPosition pos ) {
144- // TODO: Handle index expressions as calls in data flow.
145- not call instanceof IndexExpr and
146144 arg = pos .getArgument ( call )
147145}
148146
@@ -316,6 +314,30 @@ private module Aliases {
316314 class LambdaCallKindAlias = LambdaCallKind ;
317315}
318316
317+ /**
318+ * Index assignments like `a[i] = rhs` are treated as `*a.index_mut(i) = rhs`,
319+ * so they should in principle be handled by `referenceAssignment`.
320+ *
321+ * However, this would require support for [generalized reverse flow][1], which
322+ * is not yet implemented, so instead we simulate reverse flow where it would
323+ * have applied via the model for `<_ as core::ops::index::IndexMut>::index_mut`.
324+ *
325+ * The same is the case for compound assignments like `a[i] += rhs`, which are
326+ * treated as `(*a.index_mut(i)).add_assign(rhs)`.
327+ *
328+ * [1]: https://github.com/github/codeql/pull/18109
329+ */
330+ predicate indexAssignment (
331+ AssignmentOperation assignment , IndexExpr index , Node rhs , PostUpdateNode base , Content c
332+ ) {
333+ assignment .getLhs ( ) = index and
334+ rhs .asExpr ( ) = assignment .getRhs ( ) and
335+ base .getPreUpdateNode ( ) .asExpr ( ) = index .getBase ( ) and
336+ c instanceof ElementContent and
337+ // simulate that the flow summary applies
338+ not index .getResolvedTarget ( ) .fromSource ( )
339+ }
340+
319341module RustDataFlow implements InputSig< Location > {
320342 private import Aliases
321343 private import codeql.rust.dataflow.DataFlow
@@ -363,6 +385,7 @@ module RustDataFlow implements InputSig<Location> {
363385 node instanceof ClosureParameterNode or
364386 node instanceof DerefBorrowNode or
365387 node instanceof DerefOutNode or
388+ node instanceof IndexOutNode or
366389 node .asExpr ( ) instanceof ParenExpr or
367390 nodeIsHidden ( node .( PostUpdateNode ) .getPreUpdateNode ( ) )
368391 }
@@ -559,12 +582,6 @@ module RustDataFlow implements InputSig<Location> {
559582 access = c .( FieldContent ) .getAnAccess ( )
560583 )
561584 or
562- exists ( IndexExpr arr |
563- c instanceof ElementContent and
564- node1 .asExpr ( ) = arr .getBase ( ) and
565- node2 .asExpr ( ) = arr
566- )
567- or
568585 exists ( ForExpr for |
569586 c instanceof ElementContent and
570587 node1 .asExpr ( ) = for .getIterable ( ) and
@@ -590,6 +607,12 @@ module RustDataFlow implements InputSig<Location> {
590607 node2 .asExpr ( ) = deref
591608 )
592609 or
610+ exists ( IndexExpr index |
611+ c instanceof ReferenceContent and
612+ node1 .( IndexOutNode ) .getIndexExpr ( ) = index and
613+ node2 .asExpr ( ) = index
614+ )
615+ or
593616 // Read from function return
594617 exists ( DataFlowCall call |
595618 lambdaCall ( call , _, node1 ) and
@@ -651,13 +674,27 @@ module RustDataFlow implements InputSig<Location> {
651674 }
652675
653676 pragma [ nomagic]
654- private predicate referenceAssignment ( Node node1 , Node node2 , ReferenceContent c ) {
655- exists ( AssignmentExpr assignment , PrefixExpr deref |
656- assignment .getLhs ( ) = deref and
657- deref .getOperatorName ( ) = "*" and
677+ private predicate referenceAssignment (
678+ Node node1 , Node node2 , Expr e , boolean clears , ReferenceContent c
679+ ) {
680+ exists ( AssignmentExpr assignment , Expr lhs |
681+ assignment .getLhs ( ) = lhs and
658682 node1 .asExpr ( ) = assignment .getRhs ( ) and
659- node2 .asExpr ( ) = deref .getExpr ( ) and
660683 exists ( c )
684+ |
685+ lhs =
686+ any ( DerefExpr de |
687+ de = node2 .( DerefOutNode ) .getDerefExpr ( ) and
688+ e = de .getExpr ( )
689+ ) and
690+ clears = true
691+ or
692+ lhs =
693+ any ( IndexExpr ie |
694+ ie = node2 .( IndexOutNode ) .getIndexExpr ( ) and
695+ e = ie .getBase ( ) and
696+ clears = false
697+ )
661698 )
662699 }
663700
@@ -701,14 +738,14 @@ module RustDataFlow implements InputSig<Location> {
701738 or
702739 fieldAssignment ( node1 , node2 .( PostUpdateNode ) .getPreUpdateNode ( ) , c )
703740 or
704- referenceAssignment ( node1 , node2 .( PostUpdateNode ) .getPreUpdateNode ( ) , c )
741+ referenceAssignment ( node1 , node2 .( PostUpdateNode ) .getPreUpdateNode ( ) , _ , _ , c )
705742 or
706- exists ( AssignmentExpr assignment , IndexExpr index |
707- c instanceof ElementContent and
708- assignment . getLhs ( ) = index and
709- node1 . asExpr ( ) = assignment . getRhs ( ) and
710- node2 . ( PostUpdateNode ) . getPreUpdateNode ( ) . asExpr ( ) = index . getBase ( )
711- )
743+ indexAssignment ( any ( AssignmentExpr ae ) , _ , node1 , node2 , c )
744+ or
745+ // Compund assignment like `a[i] += rhs` are modeled as a store step from `rhs`
746+ // to `[post] a[i]`, followed by a taint step into `[post] a`.
747+ indexAssignment ( any ( CompoundAssignmentExpr cae ) ,
748+ node2 . ( PostUpdateNode ) . getPreUpdateNode ( ) . asExpr ( ) , node1 , _ , c )
712749 or
713750 referenceExprToExpr ( node1 , node2 , c )
714751 or
@@ -745,7 +782,7 @@ module RustDataFlow implements InputSig<Location> {
745782 predicate clearsContent ( Node n , ContentSet cs ) {
746783 fieldAssignment ( _, n , cs .( SingletonContentSet ) .getContent ( ) )
747784 or
748- referenceAssignment ( _, n , cs .( SingletonContentSet ) .getContent ( ) )
785+ referenceAssignment ( _, _ , n . asExpr ( ) , true , cs .( SingletonContentSet ) .getContent ( ) )
749786 or
750787 FlowSummaryImpl:: Private:: Steps:: summaryClearsContent ( n .( FlowSummaryNode ) .getSummaryNode ( ) , cs )
751788 or
@@ -989,9 +1026,7 @@ private module Cached {
9891026 newtype TDataFlowCall =
9901027 TCall ( Call call ) {
9911028 Stages:: DataFlowStage:: ref ( ) and
992- call .hasEnclosingCfgScope ( ) and
993- // TODO: Handle index expressions as calls in data flow.
994- not call instanceof IndexExpr
1029+ call .hasEnclosingCfgScope ( )
9951030 } or
9961031 TSummaryCall (
9971032 FlowSummaryImpl:: Public:: SummarizedCallable c , FlowSummaryImpl:: Private:: SummaryNode receiver
0 commit comments