|
16 | 16 | import csharp
|
17 | 17 | import semmle.code.csharp.commons.Assertions
|
18 | 18 | import semmle.code.csharp.commons.Constants
|
| 19 | +import semmle.code.csharp.controlflow.BasicBlocks |
| 20 | +import semmle.code.csharp.controlflow.Guards as Guards |
| 21 | +import codeql.controlflow.queries.ConstantCondition as ConstCond |
| 22 | + |
| 23 | +module ConstCondInput implements ConstCond::InputSig<ControlFlow::BasicBlock> { |
| 24 | + class SsaDefinition = Ssa::Definition; |
| 25 | + |
| 26 | + class GuardValue = Guards::GuardValue; |
| 27 | + |
| 28 | + class Guard = Guards::Guards::Guard; |
| 29 | + |
| 30 | + predicate ssaControlsBranchEdge(SsaDefinition def, BasicBlock bb1, BasicBlock bb2, GuardValue v) { |
| 31 | + Guards::Guards::ssaControlsBranchEdge(def, bb1, bb2, v) |
| 32 | + } |
| 33 | + |
| 34 | + import Guards::Guards::InternalUtil |
| 35 | +} |
| 36 | + |
| 37 | +module ConstCondImpl = ConstCond::Make<Location, Cfg, ConstCondInput>; |
| 38 | + |
| 39 | +predicate constantGuard( |
| 40 | + Guards::Guards::Guard g, string msg, Guards::Guards::Guard reason, string reasonMsg |
| 41 | +) { |
| 42 | + ConstCondImpl::problems(g, msg, reason, reasonMsg) and |
| 43 | + // conditional qualified expressions sit at an akward place in the CFG, which |
| 44 | + // leads to FPs |
| 45 | + not g.(QualifiableExpr).getQualifier() = reason and |
| 46 | + // if a logical connective is constant, one of its operands is constant, so |
| 47 | + // we report that instead |
| 48 | + not g instanceof LogicalNotExpr and |
| 49 | + not g instanceof LogicalAndExpr and |
| 50 | + not g instanceof LogicalOrExpr and |
| 51 | + // if a logical connective is a reason for another condition to be constant, |
| 52 | + // then one of its operands is a more precise reason |
| 53 | + not reason instanceof LogicalNotExpr and |
| 54 | + not reason instanceof LogicalAndExpr and |
| 55 | + not reason instanceof LogicalOrExpr and |
| 56 | + // don't report double-checked locking |
| 57 | + not exists(LockStmt ls, BasicBlock bb | |
| 58 | + bb = ls.getBasicBlock() and |
| 59 | + reason.getBasicBlock().strictlyDominates(bb) and |
| 60 | + bb.dominates(g.getBasicBlock()) |
| 61 | + ) |
| 62 | +} |
19 | 63 |
|
20 | 64 | /** A constant condition. */
|
21 |
| -abstract class ConstantCondition extends Expr { |
| 65 | +abstract class ConstantCondition extends Guards::Guards::Guard { |
22 | 66 | /** Gets the alert message for this constant condition. */
|
23 | 67 | abstract string getMessage();
|
24 | 68 |
|
| 69 | + predicate hasReason(Guards::Guards::Guard reason, string reasonMsg) { |
| 70 | + // dummy value, overridden when message has a placeholder |
| 71 | + reason = this and reasonMsg = "dummy" |
| 72 | + } |
| 73 | + |
25 | 74 | /** Holds if this constant condition is white-listed. */
|
26 | 75 | predicate isWhiteListed() { none() }
|
27 | 76 | }
|
28 | 77 |
|
| 78 | +/** A constant guard. */ |
| 79 | +class ConstantGuard extends ConstantCondition { |
| 80 | + ConstantGuard() { constantGuard(this, _, _, _) } |
| 81 | + |
| 82 | + override string getMessage() { constantGuard(this, result, _, _) } |
| 83 | + |
| 84 | + override predicate hasReason(Guards::Guards::Guard reason, string reasonMsg) { |
| 85 | + constantGuard(this, _, reason, reasonMsg) |
| 86 | + } |
| 87 | +} |
| 88 | + |
29 | 89 | /** A constant Boolean condition. */
|
30 | 90 | class ConstantBooleanCondition extends ConstantCondition {
|
31 | 91 | boolean b;
|
@@ -138,9 +198,10 @@ class ConstantMatchingCondition extends ConstantCondition {
|
138 | 198 | }
|
139 | 199 | }
|
140 | 200 |
|
141 |
| -from ConstantCondition c, string msg |
| 201 | +from ConstantCondition c, string msg, Guards::Guards::Guard reason, string reasonMsg |
142 | 202 | where
|
143 | 203 | msg = c.getMessage() and
|
| 204 | + c.hasReason(reason, reasonMsg) and |
144 | 205 | not c.isWhiteListed() and
|
145 | 206 | not isExprInAssertion(c)
|
146 |
| -select c, msg |
| 207 | +select c, msg, reason, reasonMsg |
0 commit comments