diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl1.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl1.qll index ce964917e970..17def0c431db 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl1.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl1.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll index ce964917e970..17def0c431db 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl2.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll index ce964917e970..17def0c431db 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl3.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll index ce964917e970..17def0c431db 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImpl4.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll index ce964917e970..17def0c431db 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/DataFlowImplLocal.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll index 3075d3974575..493b531a49a9 100644 --- a/cpp/ql/lib/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/dataflow/internal/TaintTrackingUtil.qll @@ -281,3 +281,9 @@ private predicate exprToPartialDefinitionStep(Expr exprIn, Expr exprOut) { } private predicate iteratorDereference(Call c) { c.getTarget() instanceof IteratorReferenceFunction } + +/** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ +predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { none() } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl1.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl1.qll index ce964917e970..17def0c431db 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl1.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl1.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll index ce964917e970..17def0c431db 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll index ce964917e970..17def0c431db 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll index ce964917e970..17def0c431db 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll index e570ecb05423..b6d332e3d4c2 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll @@ -212,3 +212,30 @@ predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut, string nodeOut = callOutput(call, modelOut) ) } + +import SpeculativeTaintFlow + +private module SpeculativeTaintFlow { + private import semmle.code.cpp.ir.dataflow.internal.DataFlowDispatch as DataFlowDispatch + private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate as DataFlowPrivate + + /** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ + predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { + exists(DataFlowCall call, ArgumentPosition argpos | + // TODO: exclude neutrals and anything that has QL modeling. + not exists(DataFlowDispatch::viableCallable(call)) and + src.(DataFlowPrivate::ArgumentNode).argumentOf(call, argpos) + | + not argpos.(DirectPosition).getIndex() = -1 and + sink.(PostUpdateNode) + .getPreUpdateNode() + .(DataFlowPrivate::ArgumentNode) + .argumentOf(call, any(DirectPosition qualpos | qualpos.getIndex() = -1)) + or + sink.(DataFlowPrivate::OutNode).getCall() = call + ) + } +} diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected index d97abde482eb..8507be51a523 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-consistency.expected @@ -186,3 +186,4 @@ identityLocalStep missingArgumentCall multipleArgumentCall lambdaCallEnclosingCallableMismatch +speculativeStepAlreadyHasModel diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected index 064a53e8d12b..d0d49db4a21f 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/dataflow-ir-consistency.expected @@ -38,3 +38,4 @@ identityLocalStep missingArgumentCall multipleArgumentCall lambdaCallEnclosingCallableMismatch +speculativeStepAlreadyHasModel diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected index 0af54dc85709..f50d9f15de5a 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -195,3 +195,4 @@ identityLocalStep missingArgumentCall multipleArgumentCall lambdaCallEnclosingCallableMismatch +speculativeStepAlreadyHasModel diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index e6de5b4991e5..364c6549fe5e 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -28,3 +28,4 @@ identityLocalStep missingArgumentCall multipleArgumentCall lambdaCallEnclosingCallableMismatch +speculativeStepAlreadyHasModel diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected index 597d096e7694..9fbef1167a4f 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-consistency.expected @@ -100,3 +100,4 @@ identityLocalStep missingArgumentCall multipleArgumentCall lambdaCallEnclosingCallableMismatch +speculativeStepAlreadyHasModel diff --git a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected index e154e42247be..984335d12515 100644 --- a/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/syntax-zoo/dataflow-ir-consistency.expected @@ -37,3 +37,4 @@ identityLocalStep missingArgumentCall multipleArgumentCall lambdaCallEnclosingCallableMismatch +speculativeStepAlreadyHasModel diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl1.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl1.qll index ce964917e970..17def0c431db 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl1.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl1.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll index ce964917e970..17def0c431db 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl2.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll index ce964917e970..17def0c431db 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl3.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll index ce964917e970..17def0c431db 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl4.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll index ce964917e970..17def0c431db 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowImpl5.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll index 25de2681862c..1a044a77777d 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/TaintTrackingPrivate.qll @@ -169,3 +169,43 @@ private module Cached { } import Cached +import SpeculativeTaintFlow + +private module SpeculativeTaintFlow { + private import semmle.code.csharp.dataflow.internal.ExternalFlow as ExternalFlow + private import semmle.code.csharp.dataflow.internal.FlowSummaryImpl as Impl + + private predicate hasTarget(Call call) { + exists(Impl::Public::SummarizedCallable sc | sc.getACall() = call) + or + exists(Impl::Public::NeutralSummaryCallable nc | nc.getACall() = call) + or + call.getTarget().getUnboundDeclaration() instanceof ExternalFlow::SinkCallable + or + exists(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.getACall() = call) + } + + /** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ + predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { + exists(DataFlowCall call, Call srcCall, ArgumentPosition argpos | + not exists(viableCallable(call)) and + not hasTarget(srcCall) and + call.(NonDelegateDataFlowCall).getDispatchCall().getCall() = srcCall and + (srcCall instanceof ConstructorInitializer or srcCall instanceof MethodCall) and + src.(ArgumentNode).argumentOf(call, argpos) and + not src instanceof PostUpdateNodes::ObjectInitializerNode and + not src instanceof MallocNode + | + not argpos.isQualifier() and + sink.(PostUpdateNode) + .getPreUpdateNode() + .(ArgumentNode) + .argumentOf(call, any(ArgumentPosition qualpos | qualpos.isQualifier())) + or + sink.(OutNode).getCall(_) = call + ) + } +} diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll index ce964917e970..17def0c431db 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl1.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll index ce964917e970..17def0c431db 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowImpl2.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/go/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll b/go/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll index 85f6bb0874f5..5365228e2310 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/TaintTrackingUtil.qll @@ -428,3 +428,29 @@ private class ClearSanitizer extends DefaultTaintSanitizer { ) } } + +import SpeculativeTaintFlow + +private module SpeculativeTaintFlow { + private import semmle.go.dataflow.internal.DataFlowDispatch as DataFlowDispatch + + /** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ + predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { + exists(DataFlowPrivate::DataFlowCall call, DataFlowDispatch::ArgumentPosition argpos | + // TODO: exclude neutrals and anything that has QL modeling. + not exists(DataFlowDispatch::viableCallable(call)) and + src.(DataFlow::ArgumentNode).argumentOf(call, argpos) + | + argpos != -1 and + sink.(DataFlow::PostUpdateNode) + .getPreUpdateNode() + .(DataFlow::ArgumentNode) + .argumentOf(call, -1) + or + sink.(DataFlowPrivate::OutNode).getCall() = call + ) + } +} diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll index ce964917e970..17def0c431db 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl1.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll index ce964917e970..17def0c431db 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl2.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll index ce964917e970..17def0c431db 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl3.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll index ce964917e970..17def0c431db 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl4.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll index ce964917e970..17def0c431db 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl5.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll index ce964917e970..17def0c431db 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowImpl6.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll index 4984b8b050fd..ad770b75a3eb 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll @@ -658,3 +658,53 @@ private predicate entrypointFieldStep(DataFlow::Node src, DataFlow::Node sink) { ) and src.getType().(RefType).getSourceDeclaration() = entrypointType() } + +import SpeculativeTaintFlow + +private module SpeculativeTaintFlow { + private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow + private import semmle.code.java.dataflow.internal.DataFlowNodes + private import semmle.code.java.dataflow.internal.FlowSummaryImpl as Impl + private import semmle.code.java.dispatch.VirtualDispatch + private import semmle.code.java.security.Sanitizers + + private predicate hasTarget(Call call) { + exists(Impl::Public::SummarizedCallable sc | sc.getACall() = call) + or + exists(Impl::Public::NeutralSummaryCallable nc | nc.getACall() = call) + or + call.getCallee().getSourceDeclaration() instanceof ExternalFlow::SinkCallable + or + exists(FlowSummaryImpl::Public::NeutralSinkCallable sc | sc.getACall() = call) + or + exists(viableCallable(call)) + or + call.getQualifier().getType() instanceof Array + or + call.getCallee().getSourceDeclaration() instanceof CloneMethod + or + call.getCallee() + .getSourceDeclaration() + .getDeclaringType() + .getPackage() + .hasName("java.util.function") + } + + /** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ + predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { + exists(DataFlowCall call, Call srcCall, int argpos | + not hasTarget(srcCall) and + call.asCall() = srcCall and + src.(ArgumentNode).argumentOf(call, argpos) and + not src instanceof SimpleTypeSanitizer + | + argpos != -1 and + sink.(DataFlow::PostUpdateNode).getPreUpdateNode() = Public::getInstanceArgument(srcCall) + or + sink.(OutNode).getCall() = call + ) + } +} diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll index ce964917e970..17def0c431db 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl1.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll index ce964917e970..17def0c431db 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl2.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll index ce964917e970..17def0c431db 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl3.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll index ce964917e970..17def0c431db 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowImpl4.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll index fb46f7d6892a..62f5a76309b4 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TaintTrackingPrivate.qll @@ -219,3 +219,36 @@ predicate asyncWithStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { contextManager.strictlyDominates(var) ) } + +import SpeculativeTaintFlow + +private module SpeculativeTaintFlow { + private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch + private import semmle.python.dataflow.new.internal.DataFlowPublic as DataFlowPublic + + /** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ + predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { + exists(DataFlowDispatch::DataFlowCall call, DataFlowDispatch::ArgumentPosition argpos | + // TODO: exclude neutrals and anything that has QL modeling. + not exists(DataFlowDispatch::DataFlowCall call0 | + // Workaround for the fact that python currently associates several + // DataFlowCalls with a single call. + src.(DataFlowPublic::ArgumentNode).argumentOf(call0, _) and + exists(DataFlowDispatch::viableCallable(call0)) + ) and + call instanceof DataFlowDispatch::PotentialLibraryCall and + src.(DataFlowPublic::ArgumentNode).argumentOf(call, argpos) + | + not argpos.isSelf() and + sink.(DataFlowPublic::PostUpdateNode) + .getPreUpdateNode() + .(DataFlowPublic::ArgumentNode) + .argumentOf(call, any(DataFlowDispatch::ArgumentPosition qualpos | qualpos.isSelf())) + or + sink.(DataFlowDispatch::OutNode).getCall(_) = call + ) + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll index ce964917e970..17def0c431db 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl1.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll index ce964917e970..17def0c431db 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImpl2.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll index 565fea34dbc0..3a3eddea63ff 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/TaintTrackingPrivate.qll @@ -149,3 +149,37 @@ private module Cached { } import Cached +import SpeculativeTaintFlow + +private module SpeculativeTaintFlow { + private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch + private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic + + /** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ + predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { + exists( + DataFlowDispatch::DataFlowCall call, MethodCall srcCall, + DataFlowDispatch::ArgumentPosition argpos, MethodCall mc + | + // TODO: exclude neutrals and anything that has QL modeling. + not exists(DataFlowDispatch::viableCallable(call)) and + call.asCall().getExpr() = srcCall and + src.(ArgumentNode).argumentOf(call, argpos) and + call.asCall().getExpr() = mc and + not mc instanceof Operation and + not mc instanceof SetterMethodCall and + not mc instanceof ElementReference + | + not argpos.isSelf() and + sink.(DataFlowPublic::PostUpdateNode) + .getPreUpdateNode() + .(ArgumentNode) + .argumentOf(call, any(DataFlowDispatch::ArgumentPosition qualpos | qualpos.isSelf())) + or + sink.(OutNode).getCall(_) = call + ) + } +} diff --git a/shared/dataflow/codeql/dataflow/DataFlow.qll b/shared/dataflow/codeql/dataflow/DataFlow.qll index 01bd0e5ea963..7a2f78089778 100644 --- a/shared/dataflow/codeql/dataflow/DataFlow.qll +++ b/shared/dataflow/codeql/dataflow/DataFlow.qll @@ -676,6 +676,12 @@ module DataFlowMake Lang> { predicate isAdditionalFlowStep(Node node1, Node node2, string model) { Config::isAdditionalFlowStep(node1, node2) and model = "Config" } + + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { + Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" + } } import Impl diff --git a/shared/dataflow/codeql/dataflow/TaintTracking.qll b/shared/dataflow/codeql/dataflow/TaintTracking.qll index 343f8be041f5..8247255038c0 100644 --- a/shared/dataflow/codeql/dataflow/TaintTracking.qll +++ b/shared/dataflow/codeql/dataflow/TaintTracking.qll @@ -29,6 +29,12 @@ signature module InputSig Lang> { */ bindingset[node] predicate defaultImplicitTaintRead(Lang::Node node, Lang::ContentSet c); + + /** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ + predicate speculativeTaintStep(Lang::Node src, Lang::Node sink); } /** @@ -63,7 +69,7 @@ module TaintFlowMake< Config::isSink(node) or Config::isSink(node, _) or Config::isAdditionalFlowStep(node, _, _) or - Config::isAdditionalFlowStep(node, _, _, _) + Config::isAdditionalFlowStep(node, _, _, _, _) ) and defaultImplicitTaintRead(node, c) } @@ -108,6 +114,13 @@ module TaintFlowMake< ) { Config::isAdditionalFlowStep(node1, node2) and model = "Config" } + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, FlowState state1, DataFlowLang::Node node2, FlowState state2, + string model + ) { + Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" + } } private module C implements DataFlowInternal::FullStateConfigSig { @@ -123,4 +136,132 @@ module TaintFlowMake< { import GlobalWithState } + + signature int speculationLimitSig(); + + private module AddSpeculativeTaintSteps< + DataFlowInternal::FullStateConfigSig Config, speculationLimitSig/0 speculationLimit> implements + DataFlowInternal::FullStateConfigSig + { + import Config + + private predicate relevantState(Config::FlowState state) { + Config::isSource(_, state) + or + exists(Config::FlowState state0 | + relevantState(state0) and Config::isAdditionalFlowStep(_, state0, _, state, _) + ) + } + + private newtype TFlowState = + TMkFlowState(Config::FlowState state, int spec) { + relevantState(state) and spec = [0 .. speculationLimit()] + } + + class FlowState extends TFlowState { + private Config::FlowState state; + private int spec; + + FlowState() { this = TMkFlowState(state, spec) } + + string toString() { result = "FlowState" } + + Config::FlowState getState() { result = state } + + int getSpec() { result = spec } + } + + predicate isSource(DataFlowLang::Node source, FlowState state) { + Config::isSource(source, state.getState()) and state.getSpec() = 0 + } + + predicate isSink(DataFlowLang::Node sink, FlowState state) { + Config::isSink(sink, state.getState()) + } + + predicate isBarrier(DataFlowLang::Node node, FlowState state) { + Config::isBarrier(node, state.getState()) + } + + predicate isBarrierIn(DataFlowLang::Node node, FlowState state) { + Config::isBarrierIn(node, state.getState()) + } + + predicate isBarrierOut(DataFlowLang::Node node, FlowState state) { + Config::isBarrierOut(node, state.getState()) + } + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, FlowState state1, DataFlowLang::Node node2, FlowState state2, + string model + ) { + Config::isAdditionalFlowStep(node1, state1.getState(), node2, state2.getState(), model) and + state1.getSpec() = state2.getSpec() + or + speculativeTaintStep(node1, node2) and + not defaultAdditionalTaintStep(node1, node2, _) and + not Config::isAdditionalFlowStep(node1, _, node2, _, _) and + not Config::isAdditionalFlowStep(node1, node2, _) and + model = "Speculative" and + state1.getSpec() + 1 = state2.getSpec() and + state1.getState() = state2.getState() + } + } + + /** + * Constructs a global taint tracking computation that also allows a given + * maximum number of speculative taint steps. + */ + module SpeculativeGlobal + implements DataFlow::GlobalFlowSig + { + private module Config0 implements DataFlowInternal::FullStateConfigSig { + import DataFlowInternal::DefaultState + import Config + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, DataFlowLang::Node node2, string model + ) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + } + + private module C implements DataFlowInternal::FullStateConfigSig { + import AddTaintDefaults> + } + + import DataFlowInternal::Impl + } + + /** + * Constructs a global taint tracking computation using flow state that also + * allows a given maximum number of speculative taint steps. + */ + module SpeculativeGlobalWithState< + DataFlow::StateConfigSig Config, speculationLimitSig/0 speculationLimit> implements + DataFlow::GlobalFlowSig + { + private module Config0 implements DataFlowInternal::FullStateConfigSig { + import Config + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, DataFlowLang::Node node2, string model + ) { + Config::isAdditionalFlowStep(node1, node2) and model = "Config" + } + + predicate isAdditionalFlowStep( + DataFlowLang::Node node1, FlowState state1, DataFlowLang::Node node2, FlowState state2, + string model + ) { + Config::isAdditionalFlowStep(node1, state1, node2, state2) and model = "Config" + } + } + + private module C implements DataFlowInternal::FullStateConfigSig { + import AddTaintDefaults> + } + + import DataFlowInternal::Impl + } } diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll index 1bebea93c486..5d8d8bd293ca 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImpl.qll @@ -73,7 +73,9 @@ module MakeImpl Lang> { * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. * This step is only applicable in `state1` and updates the flow state to `state2`. */ - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2); + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ); /** * Holds if an arbitrary number of implicit read steps of content `c` may be @@ -153,7 +155,9 @@ module MakeImpl Lang> { predicate isBarrierOut(Node node, FlowState state) { none() } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { none() } } @@ -357,12 +361,13 @@ module MakeImpl Lang> { } private predicate additionalLocalStateStep( - NodeEx node1, FlowState s1, NodeEx node2, FlowState s2 + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, string model ) { exists(Node n1, Node n2 | node1.asNodeOrImplicitRead() = n1 and node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2, + model) and getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and stateStepFilter(node1, s1, node2, s2) ) @@ -395,11 +400,14 @@ module MakeImpl Lang> { ) } - private predicate additionalJumpStateStep(NodeEx node1, FlowState s1, NodeEx node2, FlowState s2) { + private predicate additionalJumpStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, string model + ) { exists(Node n1, Node n2 | node1.asNodeOrImplicitRead() = n1 and node2.asNode() = n2 and - Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + Config::isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2, + model) and getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and stateStepFilter(node1, s1, node2, s2) and not Config::getAFeature() instanceof FeatureEqualSourceSinkCallContext @@ -530,13 +538,13 @@ module MakeImpl Lang> { exists(NodeEx mid | fwdFlow(mid, cc) | localFlowStepEx(mid, node, _) or additionalLocalFlowStep(mid, node, _) or - additionalLocalStateStep(mid, _, node, _) + additionalLocalStateStep(mid, _, node, _, _) ) or exists(NodeEx mid | fwdFlow(mid, _) and cc = false | jumpStepEx(mid, node) or additionalJumpStep(mid, node, _) or - additionalJumpStateStep(mid, _, node, _) + additionalJumpStateStep(mid, _, node, _, _) ) or // store @@ -677,8 +685,8 @@ module MakeImpl Lang> { private predicate stateStepFwd(FlowState state1, FlowState state2) { exists(NodeEx node1 | - additionalLocalStateStep(node1, state1, _, state2) or - additionalJumpStateStep(node1, state1, _, state2) + additionalLocalStateStep(node1, state1, _, state2, _) or + additionalJumpStateStep(node1, state1, _, state2, _) | fwdFlow(node1) ) @@ -723,13 +731,13 @@ module MakeImpl Lang> { exists(NodeEx mid | revFlow(mid, toReturn) | localFlowStepEx(node, mid, _) or additionalLocalFlowStep(node, mid, _) or - additionalLocalStateStep(node, _, mid, _) + additionalLocalStateStep(node, _, mid, _, _) ) or exists(NodeEx mid | revFlow(mid, _) and toReturn = false | jumpStepEx(node, mid) or additionalJumpStep(node, mid, _) or - additionalJumpStateStep(node, _, mid, _) + additionalJumpStateStep(node, _, mid, _, _) ) or // store @@ -847,8 +855,8 @@ module MakeImpl Lang> { private predicate stateStepRev(FlowState state1, FlowState state2) { exists(NodeEx node1, NodeEx node2 | - additionalLocalStateStep(node1, state1, node2, state2) or - additionalJumpStateStep(node1, state1, node2, state2) + additionalLocalStateStep(node1, state1, node2, state2, _) or + additionalJumpStateStep(node1, state1, node2, state2, _) | revFlow(node1, _) and revFlow(node2, _) and @@ -1068,8 +1076,7 @@ module MakeImpl Lang> { ) { Stage1::revFlow(node1) and Stage1::revFlow(node2) and - additionalLocalStateStep(node1, state1, node2, state2) and - label = "Config" and + additionalLocalStateStep(node1, state1, node2, state2, label) and t = node2.getDataFlowType() and lcc.relevantFor(node1.getEnclosingCallable()) and not isUnreachableInCall1(node1, lcc) and @@ -1621,7 +1628,7 @@ module MakeImpl Lang> { or exists(NodeEx mid, FlowState state0 | fwdFlow(mid, state0, _, _, _, ap, apa) and - additionalJumpStateStep(mid, state0, node, state) and + additionalJumpStateStep(mid, state0, node, state, _) and t = getNodeTyp(node) and ap instanceof ApNil ) @@ -2286,7 +2293,7 @@ module MakeImpl Lang> { ) or exists(NodeEx mid, FlowState state0 | - additionalJumpStateStep(node, state, mid, state0) and + additionalJumpStateStep(node, state, mid, state0, _) and revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, ap) and ap instanceof ApNil ) @@ -2651,7 +2658,7 @@ module MakeImpl Lang> { or additionalJumpStep(_, node, _) or - additionalJumpStateStep(_, _, node, state) + additionalJumpStateStep(_, _, node, state, _) or node instanceof ParamNodeEx or @@ -2699,9 +2706,9 @@ module MakeImpl Lang> { exists(NodeEx next, FlowState s | revFlow(next, s, pragma[only_bind_into](ap)) and ap instanceof ApNil | - additionalJumpStateStep(node, state, next, s) + additionalJumpStateStep(node, state, next, s, _) or - additionalLocalStateStep(node, state, next, s) and + additionalLocalStateStep(node, state, next, s, _) and s != state ) or @@ -3246,10 +3253,9 @@ module MakeImpl Lang> { t = getNodeTyp(node) and ap instanceof ApNil or - additionalJumpStateStep(mid, state0, node, state) and + additionalJumpStateStep(mid, state0, node, state, label) and t = getNodeTyp(node) and - ap instanceof ApNil and - label = "Config" + ap instanceof ApNil ) or // flow into a callable @@ -4799,7 +4805,7 @@ module MakeImpl Lang> { or additionalJumpStep(node1, node2, _) or - additionalJumpStateStep(node1, _, node2, _) + additionalJumpStateStep(node1, _, node2, _, _) or // flow into callable viableParamArgEx(_, node2, node1) @@ -4913,10 +4919,10 @@ module MakeImpl Lang> { private predicate relevantState(FlowState state) { sourceNode(_, state) or sinkNodeWithState(_, state) or - additionalLocalStateStep(_, state, _, _) or - additionalLocalStateStep(_, _, _, state) or - additionalJumpStateStep(_, state, _, _) or - additionalJumpStateStep(_, _, _, state) + additionalLocalStateStep(_, state, _, _, _) or + additionalLocalStateStep(_, _, _, state, _) or + additionalJumpStateStep(_, state, _, _, _) or + additionalJumpStateStep(_, _, _, state, _) } private predicate revSinkNode(NodeEx node, FlowState state) { @@ -5252,7 +5258,7 @@ module MakeImpl Lang> { t = node.getDataFlowType() and ap = TPartialNil() or - additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state) and + additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state, _) and cc = mid.getCallContext() and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and @@ -5287,7 +5293,7 @@ module MakeImpl Lang> { ap = TPartialNil() and isStoreStep = false or - additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state) and + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, _) and cc = callContextNone() and sc1 = TSummaryCtx1None() and sc2 = TSummaryCtx2None() and @@ -5559,7 +5565,7 @@ module MakeImpl Lang> { ap = TPartialNil() and isStoreStep = false or - additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState()) and + additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState(), _) and sc1 = mid.getSummaryCtx1() and sc2 = mid.getSummaryCtx2() and sc3 = mid.getSummaryCtx3() and @@ -5584,7 +5590,7 @@ module MakeImpl Lang> { ap = TPartialNil() and isStoreStep = false or - additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState()) and + additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState(), _) and sc1 = TRevSummaryCtx1None() and sc2 = TRevSummaryCtx2None() and sc3 = TRevSummaryCtx3None() and diff --git a/shared/dataflow/codeql/dataflow/internal/DataFlowImplConsistency.qll b/shared/dataflow/codeql/dataflow/internal/DataFlowImplConsistency.qll index 48c4d42daaad..ca523e179c47 100644 --- a/shared/dataflow/codeql/dataflow/internal/DataFlowImplConsistency.qll +++ b/shared/dataflow/codeql/dataflow/internal/DataFlowImplConsistency.qll @@ -323,4 +323,18 @@ module MakeConsistency< lambdaCall(call, _, receiver) and not nodeGetEnclosingCallable(receiver) = call.getEnclosingCallable() } + + query predicate speculativeStepAlreadyHasModel(Node n1, Node n2, string model) { + speculativeTaintStep(n1, n2) and + not defaultAdditionalTaintStep(n1, n2, _) and + ( + simpleLocalFlowStep(n1, n2, _) and model = "SimpleLocalFlowStep" + or + exists(DataFlowCall call | + exists(viableCallable(call)) and + isArgumentNode(n1, call, _) and + model = "dispatch" + ) + ) + } } diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll index ce964917e970..17def0c431db 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/DataFlowImpl1.qll @@ -261,13 +261,17 @@ deprecated private module Config implements FullStateConfigSig { model = "" } - predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + predicate isAdditionalFlowStep( + Node node1, FlowState state1, Node node2, FlowState state2, string model + ) { getConfig(state1).isAdditionalFlowStep(node1, getState(state1), node2, getState(state2)) and - getConfig(state2) = getConfig(state1) + getConfig(state2) = getConfig(state1) and + model = "" or not singleConfiguration() and getConfig(state1).isAdditionalFlowStep(node1, node2) and - state2 = state1 + state2 = state1 and + model = "" } predicate allowImplicitRead(Node node, ContentSet c) { diff --git a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll index a6acdbc25ede..c3f14b03f835 100644 --- a/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll +++ b/swift/ql/lib/codeql/swift/dataflow/internal/TaintTrackingPrivate.qll @@ -99,3 +99,33 @@ private module Cached { } import Cached +import SpeculativeTaintFlow + +private module SpeculativeTaintFlow { + private import codeql.swift.dataflow.internal.DataFlowDispatch as DataFlowDispatch + private import codeql.swift.dataflow.internal.DataFlowPublic as DataFlowPublic + private import codeql.swift.dataflow.internal.DataFlowPrivate as DataFlowPrivate + + /** + * Holds if the additional step from `src` to `sink` should be considered in + * speculative taint flow exploration. + */ + predicate speculativeTaintStep(DataFlow::Node src, DataFlow::Node sink) { + exists(DataFlowDispatch::DataFlowCall call, DataFlowDispatch::ArgumentPosition argpos | + // TODO: exclude neutrals and anything that has QL modeling. + not exists(DataFlowDispatch::viableCallable(call)) and + src.(DataFlowPrivate::ArgumentNode).argumentOf(call, argpos) + | + not argpos instanceof DataFlowDispatch::ThisArgumentPosition and + sink.(DataFlowPublic::PostUpdateNode) + .getPreUpdateNode() + .(DataFlowPrivate::ArgumentNode) + .argumentOf(call, + any(DataFlowDispatch::ArgumentPosition qualpos | + qualpos instanceof DataFlowDispatch::ThisArgumentPosition + )) + or + sink.(DataFlowPrivate::OutNode).getCall(_) = call + ) + } +}