Skip to content

Commit 4739a63

Browse files
committed
C#: Fix a bug and generalize guards implication logic
1 parent d25bd59 commit 4739a63

File tree

7 files changed

+310
-23
lines changed

7 files changed

+310
-23
lines changed

csharp/ql/src/semmle/code/csharp/controlflow/Guards.qll

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -956,27 +956,27 @@ module Internal {
956956
*/
957957
private predicate guardImpliesNotEqual(Expr guard, AbstractValue vGuard, PreSsa::Definition def, AbstractValue vDef) {
958958
relevantEq(def, vDef) and
959-
exists(DereferenceableExpr de |
960-
de = def.getARead() |
959+
exists(AssignableRead ar |
960+
ar = def.getARead() |
961961
// For example:
962962
// if (de == null); vGuard = TBooleanValue(false); vDef = TNullValue(true)
963963
// but not
964964
// if (de == "abc"); vGuard = TBooleanValue(false); vDef = TNullValue(false)
965-
guard = getAnEqualityCheckVal(de, vGuard.getDualValue(), vDef) and
965+
guard = getAnEqualityCheckVal(ar, vGuard.getDualValue(), vDef) and
966966
vDef.isSingleton()
967967
or
968968
// For example:
969969
// if (de != null); vGuard = TBooleanValue(true); vDef = TNullValue(true)
970970
// or
971971
// if (de == null); vGuard = TBooleanValue(true); vDef = TNullValue(false)
972972
exists(NullValue nv |
973-
guard = de.getANullCheck(vGuard, any(boolean b | nv = TNullValue(b))) |
973+
guard = ar.(DereferenceableExpr).getANullCheck(vGuard, any(boolean b | nv = TNullValue(b))) |
974974
vDef = nv.getDualValue()
975975
)
976976
or
977977
// For example:
978-
// if (de == null); vGuard = TBooleanValue(true); vDef = TNullValue(false)
979-
guard = getAnEqualityCheckVal(de, vGuard, vDef.getDualValue())
978+
// if (de == false); vGuard = TBooleanValue(true); vDef = TBooleanValue(true)
979+
guard = getAnEqualityCheckVal(ar, vGuard, vDef.getDualValue())
980980
)
981981
}
982982

@@ -1040,7 +1040,7 @@ module Internal {
10401040
* Holds if `e` has abstract value `v` and may be assigned to `def` without going
10411041
* through back edges, and all other possible ultimate definitions of `def` do not
10421042
* have abstract value `v`. The trivial case where `def` is an explicit update with
1043-
* source `e is excluded.
1043+
* source `e` is excluded.
10441044
*/
10451045
private predicate uniqueValue(PreSsa::Definition def, Expr e, AbstractValue v) {
10461046
possibleValue(def, false, e, v) and
@@ -1178,30 +1178,41 @@ module Internal {
11781178
polarity = false
11791179
)
11801180
or
1181-
exists(ConditionalExpr cond, boolean branch, BoolLiteral boolLit, boolean b |
1182-
b = boolLit.getBoolValue() and
1181+
exists(ConditionalExpr cond, boolean branch, Expr e, AbstractValue v |
1182+
e = v.getAnExpr() and
11831183
(
1184-
cond.getThen() = boolLit and branch = true
1184+
cond.getThen() = e and branch = true
11851185
or
1186-
cond.getElse() = boolLit and branch = false
1186+
cond.getElse() = e and branch = false
11871187
)
11881188
|
11891189
g1 = cond and
1190-
v1 = TBooleanValue(b.booleanNot()) and
1190+
v1 = v.getDualValue() and
11911191
(
1192+
// g1 === g2 ? e : ...;
11921193
g2 = cond.getCondition() and
11931194
v2 = TBooleanValue(branch.booleanNot())
11941195
or
1196+
// g1 === ... ? g2 : e
11951197
g2 = cond.getThen() and
11961198
branch = false and
11971199
v2 = v1
11981200
or
1201+
// g1 === g2 ? ... : e
11991202
g2 = cond.getElse() and
12001203
branch = true and
12011204
v2 = v1
12021205
)
12031206
)
12041207
or
1208+
v1 = g1.getAValue() and
1209+
v1 = any(MatchValue mv |
1210+
mv.isMatch() and
1211+
g2 = g1 and
1212+
v2.getAnExpr() = mv.getCaseStmt().(ConstCase).getExpr() and
1213+
v1 != v2
1214+
)
1215+
or
12051216
exists(boolean isNull |
12061217
g1 = g2.(DereferenceableExpr).getANullCheck(v1, isNull) |
12071218
v2 = any(NullValue nv | if nv.isNull() then isNull = true else isNull = false) and
@@ -1238,15 +1249,16 @@ module Internal {
12381249
)
12391250
or
12401251
exists(PreSsa::Definition def, AbstractValue v |
1241-
// If `def = g2 ? v : ...` or `def = g2 ? ... : v` then a guard `g1`
1242-
// proving `def != v` ensures that `g2` evaluates to `b2`.
1252+
// If for example `def = g2 ? v : ...`, then a guard `g1` proving `def != v`
1253+
// ensures that `g2` evaluates to `false`.
12431254
conditionalAssignVal(g2, v2.getDualValue(), def, v) and
12441255
guardImpliesNotEqual(g1, v1, def, v)
12451256
)
12461257
or
12471258
exists(PreSsa::Definition def, Expr e, AbstractValue v |
1248-
// If `def = g2 ? v : ...` and all other assignments to `def` are different from
1249-
// `v` then a guard proving `def == v` ensures that `g2` evaluates to `v2`.
1259+
// If for example `def = g2 ? v : ...` and all other assignments to `def` are
1260+
// different from `v`, then a guard proving `def == v` ensures that `g2`
1261+
// evaluates to `true`.
12501262
uniqueValue(def, e, v) and
12511263
guardImpliesEqual(g1, v1, def, v) and
12521264
g2.preControlsDirect(any(PreBasicBlocks::PreBasicBlock bb | e = bb.getAnElement()), v2) and

csharp/ql/src/semmle/code/csharp/dataflow/Nullness.qll

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ private predicate defMaybeNull(Ssa::Definition def, string msg, Element reason)
190190

191191
/**
192192
* Holds if `def1` being `null` in basic block `bb1` implies that `def2` might
193-
* be null in basic block `bb2`. The SSA definitions share the same source variable.
193+
* be `null` in basic block `bb2`. The SSA definitions share the same source
194+
* variable.
194195
*/
195196
private predicate defNullImpliesStep(Ssa::Definition def1, BasicBlock bb1, Ssa::Definition def2, BasicBlock bb2) {
196197
exists(Ssa::SourceVariable v |
@@ -374,10 +375,6 @@ class Dereference extends G::DereferenceableExpr {
374375
defReaches(v.getAnSsaDefinition(), this, true)
375376
}
376377

377-
/**
378-
* Holds if assignable access `aa` is the first dereference of an assignable
379-
* in a block, where it is suspected of being `null`.
380-
*/
381378
pragma[noinline]
382379
private predicate nullDerefCandidate(Ssa::Definition origin) {
383380
exists(Ssa::Definition ssa, BasicBlock bb |

csharp/ql/test/library-tests/controlflow/guards/Guards.cs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,4 +209,57 @@ void M17(object o, string[] args)
209209
}
210210
}
211211
}
212+
213+
void M18(bool b1)
214+
{
215+
var b2 = true;
216+
if (b1)
217+
b2 = false;
218+
switch (b2)
219+
{
220+
case true :
221+
return;
222+
return;
223+
}
224+
}
225+
226+
void M19(bool b1)
227+
{
228+
var b2 = false;
229+
if (b1)
230+
b2 = true;
231+
switch (b2)
232+
{
233+
case true :
234+
return;
235+
return;
236+
}
237+
}
238+
239+
void M20(bool b)
240+
{
241+
var i = 0;
242+
if (b)
243+
i = 1;
244+
switch (i)
245+
{
246+
case 1 :
247+
return;
248+
return;
249+
}
250+
}
251+
252+
enum E { A, B, C }
253+
void M21(bool b)
254+
{
255+
var e = E.A;
256+
if (b)
257+
e = E.B;
258+
switch (e)
259+
{
260+
case E.B :
261+
return;
262+
return;
263+
}
264+
}
212265
}

0 commit comments

Comments
 (0)