@@ -90,8 +90,6 @@ final class DataFlowCall extends TDataFlowCall {
9090 * Holds if `arg` is an argument of `call` at the position `pos`.
9191 */
9292predicate isArgumentForCall ( Expr arg , Call call , RustDataFlow:: ArgumentPosition pos ) {
93- // TODO: Handle index expressions as calls in data flow.
94- not call instanceof IndexExpr and
9593 arg = pos .getArgument ( call )
9694}
9795
@@ -261,6 +259,30 @@ private module Aliases {
261259 class LambdaCallKindAlias = LambdaCallKind ;
262260}
263261
262+ /**
263+ * Index assignments like `a[i] = rhs` are treated as `*a.index_mut(i) = rhs`,
264+ * so they should in principle be handled by `referenceAssignment`.
265+ *
266+ * However, this would require support for [generalized reverse flow][1], which
267+ * is not yet implemented, so instead we simulate reverse flow where it would
268+ * have applied via the model for `<_ as core::ops::index::IndexMut>::index_mut`.
269+ *
270+ * The same is the case for compound assignments like `a[i] += rhs`, which are
271+ * treated as `(*a.index_mut(i)).add_assign(rhs)`.
272+ *
273+ * [1]: https://github.com/github/codeql/pull/18109
274+ */
275+ predicate indexAssignment (
276+ AssignmentOperation assignment , IndexExpr index , Node rhs , PostUpdateNode base , Content c
277+ ) {
278+ assignment .getLhs ( ) = index and
279+ rhs .asExpr ( ) = assignment .getRhs ( ) and
280+ base .getPreUpdateNode ( ) .asExpr ( ) = index .getBase ( ) and
281+ c instanceof ElementContent and
282+ // simulate that the flow summary applies
283+ not index .getResolvedTarget ( ) .fromSource ( )
284+ }
285+
264286module RustDataFlow implements InputSig< Location > {
265287 private import Aliases
266288 private import codeql.rust.dataflow.DataFlow
@@ -360,6 +382,7 @@ module RustDataFlow implements InputSig<Location> {
360382 node instanceof ClosureParameterNode or
361383 node instanceof DerefBorrowNode or
362384 node instanceof DerefOutNode or
385+ node instanceof IndexOutNode or
363386 node .asExpr ( ) instanceof ParenExpr or
364387 nodeIsHidden ( node .( PostUpdateNode ) .getPreUpdateNode ( ) )
365388 }
@@ -552,12 +575,6 @@ module RustDataFlow implements InputSig<Location> {
552575 access = c .( FieldContent ) .getAnAccess ( )
553576 )
554577 or
555- exists ( IndexExpr arr |
556- c instanceof ElementContent and
557- node1 .asExpr ( ) = arr .getBase ( ) and
558- node2 .asExpr ( ) = arr
559- )
560- or
561578 exists ( ForExpr for |
562579 c instanceof ElementContent and
563580 node1 .asExpr ( ) = for .getIterable ( ) and
@@ -583,6 +600,12 @@ module RustDataFlow implements InputSig<Location> {
583600 node2 .asExpr ( ) = deref
584601 )
585602 or
603+ exists ( IndexExpr index |
604+ c instanceof ReferenceContent and
605+ node1 .( IndexOutNode ) .getIndexExpr ( ) = index and
606+ node2 .asExpr ( ) = index
607+ )
608+ or
586609 // Read from function return
587610 exists ( DataFlowCall call |
588611 lambdaCall ( call , _, node1 ) and
@@ -644,13 +667,27 @@ module RustDataFlow implements InputSig<Location> {
644667 }
645668
646669 pragma [ nomagic]
647- private predicate referenceAssignment ( Node node1 , Node node2 , ReferenceContent c ) {
648- exists ( AssignmentExpr assignment , PrefixExpr deref |
649- assignment .getLhs ( ) = deref and
650- deref .getOperatorName ( ) = "*" and
670+ private predicate referenceAssignment (
671+ Node node1 , Node node2 , Expr e , boolean clears , ReferenceContent c
672+ ) {
673+ exists ( AssignmentExpr assignment , Expr lhs |
674+ assignment .getLhs ( ) = lhs and
651675 node1 .asExpr ( ) = assignment .getRhs ( ) and
652- node2 .asExpr ( ) = deref .getExpr ( ) and
653676 exists ( c )
677+ |
678+ lhs =
679+ any ( DerefExpr de |
680+ de = node2 .( DerefOutNode ) .getDerefExpr ( ) and
681+ e = de .getExpr ( )
682+ ) and
683+ clears = true
684+ or
685+ lhs =
686+ any ( IndexExpr ie |
687+ ie = node2 .( IndexOutNode ) .getIndexExpr ( ) and
688+ e = ie .getBase ( ) and
689+ clears = false
690+ )
654691 )
655692 }
656693
@@ -694,14 +731,14 @@ module RustDataFlow implements InputSig<Location> {
694731 or
695732 fieldAssignment ( node1 , node2 .( PostUpdateNode ) .getPreUpdateNode ( ) , c )
696733 or
697- referenceAssignment ( node1 , node2 .( PostUpdateNode ) .getPreUpdateNode ( ) , c )
734+ referenceAssignment ( node1 , node2 .( PostUpdateNode ) .getPreUpdateNode ( ) , _ , _ , c )
698735 or
699- exists ( AssignmentExpr assignment , IndexExpr index |
700- c instanceof ElementContent and
701- assignment . getLhs ( ) = index and
702- node1 . asExpr ( ) = assignment . getRhs ( ) and
703- node2 . ( PostUpdateNode ) . getPreUpdateNode ( ) . asExpr ( ) = index . getBase ( )
704- )
736+ indexAssignment ( any ( AssignmentExpr ae ) , _ , node1 , node2 , c )
737+ or
738+ // Compund assignment like `a[i] += rhs` are modeled as a store step from `rhs`
739+ // to `[post] a[i]`, followed by a taint step into `[post] a`.
740+ indexAssignment ( any ( CompoundAssignmentExpr cae ) ,
741+ node2 . ( PostUpdateNode ) . getPreUpdateNode ( ) . asExpr ( ) , node1 , _ , c )
705742 or
706743 referenceExprToExpr ( node1 , node2 , c )
707744 or
@@ -738,7 +775,7 @@ module RustDataFlow implements InputSig<Location> {
738775 predicate clearsContent ( Node n , ContentSet cs ) {
739776 fieldAssignment ( _, n , cs .( SingletonContentSet ) .getContent ( ) )
740777 or
741- referenceAssignment ( _, n , cs .( SingletonContentSet ) .getContent ( ) )
778+ referenceAssignment ( _, _ , n . asExpr ( ) , true , cs .( SingletonContentSet ) .getContent ( ) )
742779 or
743780 FlowSummaryImpl:: Private:: Steps:: summaryClearsContent ( n .( FlowSummaryNode ) .getSummaryNode ( ) , cs )
744781 or
@@ -982,9 +1019,7 @@ private module Cached {
9821019 newtype TDataFlowCall =
9831020 TCall ( Call call ) {
9841021 Stages:: DataFlowStage:: ref ( ) and
985- call .hasEnclosingCfgScope ( ) and
986- // TODO: Handle index expressions as calls in data flow.
987- not call instanceof IndexExpr
1022+ call .hasEnclosingCfgScope ( )
9881023 } or
9891024 TSummaryCall (
9901025 FlowSummaryImpl:: Public:: SummarizedCallable c , FlowSummaryImpl:: Private:: SummaryNode receiver
0 commit comments