diff --git a/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll b/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll index 0bbac21c684..a67e35873e7 100644 --- a/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll +++ b/ql/src/semmle/go/dataflow/internal/DataFlowPrivate.qll @@ -216,20 +216,29 @@ private class ConstantBooleanArgumentNode extends ArgumentNode, ExprNode { boolean getBooleanValue() { constantBooleanExpr(this.getExpr(), result) } } +/** + * Returns a guard that will certainly not hold in calling context `call`. + * + * In particular it does not hold because it checks that `param` has value `b`, but + * in context `call` it is known to have value `!b`. Note this is `noinline`d in order + * to avoid a bad join order in `isUnreachableInCall`. + */ +pragma[noinline] +private ControlFlow::ConditionGuardNode getAFalsifiedGuard(DataFlowCall call) { + exists(ParameterNode param, ConstantBooleanArgumentNode arg | + // get constant bool argument and parameter for this call + viableParamArg(call, param, arg) and + // which is used in a guard controlling `n` with the opposite value of `arg` + result.ensures(param.getAUse(), arg.getBooleanValue().booleanNot()) + ) +} + /** * Holds if the node `n` is unreachable when the call context is `call`. */ cached predicate isUnreachableInCall(Node n, DataFlowCall call) { - exists( - ParameterNode param, ConstantBooleanArgumentNode arg, ControlFlow::ConditionGuardNode guard - | - // get constant bool argument and parameter for this call - viableParamArg(call, param, arg) and - // which is used in a guard controlling `n` with the opposite value of `arg` - guard.ensures(param.getAUse(), arg.getBooleanValue().booleanNot()) and - guard.dominates(n.getBasicBlock()) - ) + getAFalsifiedGuard(call).dominates(n.getBasicBlock()) } int accessPathLimit() { result = 5 }