@@ -660,7 +660,7 @@ private predicate flowThroughProperty(
660660 * All of this is done under configuration `cfg`, and `arg` flows along a path
661661 * summarized by `summary`, while `cb` is only tracked locally.
662662 */
663- private predicate higherOrderCall (
663+ private predicate summarizedHigherOrderCall (
664664 DataFlow:: Node arg , DataFlow:: Node cb , int i , DataFlow:: Configuration cfg , PathSummary summary
665665) {
666666 exists ( Function f , DataFlow:: InvokeNode outer , DataFlow:: InvokeNode inner , int j ,
@@ -676,12 +676,52 @@ private predicate higherOrderCall(
676676 // indirect higher-order call
677677 exists ( DataFlow:: Node cbArg , PathSummary newSummary |
678678 cbParm .flowsTo ( cbArg ) and
679- higherOrderCall ( innerArg , cbArg , i , cfg , newSummary ) and
679+ summarizedHigherOrderCall ( innerArg , cbArg , i , cfg , newSummary ) and
680680 summary = oldSummary .append ( PathSummary:: call ( ) ) .append ( newSummary )
681681 )
682682 )
683683}
684684
685+ /**
686+ * Holds if `arg` is passed as the `i`th argument to `callback` through a callback invocation.
687+ *
688+ * This can be a summarized call, that is, `arg` and `callback` flow into a call,
689+ * `f(arg, callback)`, which performs the invocation.
690+ *
691+ * Alternatively, the callback can flow into a call `f(callback)` which itself provides the `arg`.
692+ * That is, `arg` refers to a value defined in `f` or one of its callees.
693+ */
694+ predicate higherOrderCall (
695+ DataFlow:: Node arg , DataFlow:: SourceNode callback , int i , DataFlow:: Configuration cfg ,
696+ PathSummary summary
697+ ) {
698+ // Summarized call
699+ exists ( DataFlow:: Node cb |
700+ summarizedHigherOrderCall ( arg , cb , i , cfg , summary ) and
701+ callback .flowsTo ( cb )
702+ )
703+ or
704+ // Local invocation of a parameter
705+ isRelevant ( arg , cfg ) and
706+ exists ( DataFlow:: InvokeNode invoke |
707+ arg = invoke .getArgument ( i ) and
708+ invoke = callback .( DataFlow:: ParameterNode ) .getACall ( ) and
709+ summary = PathSummary:: call ( )
710+ )
711+ or
712+ // Forwarding of the callback parameter (but not the argument).
713+ // We use a return summary since flow moves back towards the call site.
714+ // This ensures that an argument that is only tainted in some contexts cannot flow
715+ // out to every callback.
716+ exists ( DataFlow:: Node cbArg , DataFlow:: SourceNode innerCb , PathSummary oldSummary |
717+ higherOrderCall ( arg , innerCb , i , cfg , oldSummary ) and
718+ callStep ( cbArg , innerCb ) and
719+ callback .flowsTo ( cbArg ) and
720+ summary = PathSummary:: return ( ) .append ( oldSummary )
721+ )
722+ }
723+
724+
685725/**
686726 * Holds if `pred` is passed as an argument to a function `f` which also takes a
687727 * callback parameter `cb` and then invokes `cb`, passing `pred` into parameter `succ`
@@ -693,12 +733,8 @@ private predicate higherOrderCall(
693733private predicate flowIntoHigherOrderCall (
694734 DataFlow:: Node pred , DataFlow:: Node succ , DataFlow:: Configuration cfg , PathSummary summary
695735) {
696- exists (
697- DataFlow:: Node fArg , DataFlow:: FunctionNode cb ,
698- int i , PathSummary oldSummary
699- |
700- higherOrderCall ( pred , fArg , i , cfg , oldSummary ) and
701- cb = fArg .getALocalSource ( ) and
736+ exists ( DataFlow:: FunctionNode cb , int i , PathSummary oldSummary |
737+ higherOrderCall ( pred , cb , i , cfg , oldSummary ) and
702738 succ = cb .getParameter ( i ) and
703739 summary = oldSummary .append ( PathSummary:: call ( ) )
704740 )
0 commit comments