@@ -13,6 +13,284 @@ private import semmle.code.csharp.frameworks.System
1313private import semmle.code.csharp.frameworks.system.Linq
1414private import semmle.code.csharp.frameworks.system.Collections
1515private import semmle.code.csharp.frameworks.system.collections.Generic
16+ private import codeql.controlflow.Guards as SharedGuards
17+
18+ private module GuardsInput implements
19+ SharedGuards:: InputSig< Location , ControlFlow:: Node , ControlFlow:: BasicBlock >
20+ {
21+ private import csharp as CS
22+
23+ class NormalExitNode = ControlFlow:: Nodes:: NormalExitNode ;
24+
25+ class AstNode = ControlFlowElement ;
26+
27+ class Expr = CS:: Expr ;
28+
29+ private newtype TConstantValue =
30+ TStringValue ( string value ) { any ( StringLiteral s ) .getValue ( ) = value }
31+
32+ class ConstantValue extends TConstantValue {
33+ string toString ( ) { this = TStringValue ( result ) }
34+ }
35+
36+ abstract class ConstantExpr extends Expr {
37+ predicate isNull ( ) { none ( ) }
38+
39+ boolean asBooleanValue ( ) { none ( ) }
40+
41+ int asIntegerValue ( ) { none ( ) }
42+
43+ ConstantValue asConstantValue ( ) { none ( ) }
44+ }
45+
46+ private class NullConstant extends ConstantExpr {
47+ NullConstant ( ) { nullValueImplied ( this ) }
48+
49+ override predicate isNull ( ) { any ( ) }
50+ }
51+
52+ private class BooleanConstant extends ConstantExpr instanceof BoolLiteral {
53+ override boolean asBooleanValue ( ) { result = super .getBoolValue ( ) }
54+ }
55+
56+ private predicate intConst ( Expr e , int i ) {
57+ e .getValue ( ) .toInt ( ) = i and
58+ (
59+ e .getType ( ) instanceof Enum
60+ or
61+ e .getType ( ) instanceof IntegralType
62+ )
63+ }
64+
65+ private class IntegerConstant extends ConstantExpr {
66+ IntegerConstant ( ) { intConst ( this , _) }
67+
68+ override int asIntegerValue ( ) { intConst ( this , result ) }
69+ }
70+
71+ private class EnumConst extends ConstantExpr {
72+ EnumConst ( ) { this .getType ( ) instanceof Enum and this .hasValue ( ) }
73+
74+ override int asIntegerValue ( ) { result = this .getValue ( ) .toInt ( ) }
75+ }
76+
77+ private class StringConstant extends ConstantExpr instanceof StringLiteral {
78+ override ConstantValue asConstantValue ( ) { result = TStringValue ( this .getValue ( ) ) }
79+ }
80+
81+ class NonNullExpr extends Expr {
82+ NonNullExpr ( ) { nonNullValueImplied ( this ) }
83+ }
84+
85+ class Case extends AstNode instanceof CS:: Case {
86+ Expr getSwitchExpr ( ) { super .getExpr ( ) = result }
87+
88+ predicate isDefaultCase ( ) { this instanceof DefaultCase }
89+
90+ ConstantExpr asConstantCase ( ) { super .getPattern ( ) = result }
91+
92+ private predicate hasEdge ( BasicBlock bb1 , BasicBlock bb2 , MatchingCompletion c ) {
93+ exists ( PatternExpr pe |
94+ super .getPattern ( ) = pe and
95+ c .isValidFor ( pe ) and
96+ bb1 .getLastNode ( ) = pe .getAControlFlowNode ( ) and
97+ bb1 .getASuccessor ( c .getAMatchingSuccessorType ( ) ) = bb2
98+ )
99+ }
100+
101+ predicate matchEdge ( BasicBlock bb1 , BasicBlock bb2 ) {
102+ exists ( MatchingCompletion c | this .hasEdge ( bb1 , bb2 , c ) and c .isMatch ( ) )
103+ }
104+
105+ predicate nonMatchEdge ( BasicBlock bb1 , BasicBlock bb2 ) {
106+ exists ( MatchingCompletion c | this .hasEdge ( bb1 , bb2 , c ) and c .isNonMatch ( ) )
107+ }
108+ }
109+
110+ abstract private class BinExpr extends BinaryOperation { }
111+
112+ class AndExpr extends BinExpr {
113+ AndExpr ( ) {
114+ this instanceof LogicalAndExpr or
115+ this instanceof BitwiseAndExpr
116+ }
117+ }
118+
119+ class OrExpr extends BinExpr {
120+ OrExpr ( ) {
121+ this instanceof LogicalOrExpr or
122+ this instanceof BitwiseOrExpr
123+ }
124+ }
125+
126+ class NotExpr = LogicalNotExpr ;
127+
128+ class IdExpr extends Expr {
129+ IdExpr ( ) { this instanceof AssignExpr or this instanceof CastExpr }
130+
131+ Expr getEqualChildExpr ( ) {
132+ result = this .( AssignExpr ) .getRValue ( )
133+ or
134+ result = this .( CastExpr ) .getExpr ( )
135+ }
136+ }
137+
138+ predicate equalityTest ( Expr eqtest , Expr left , Expr right , boolean polarity ) {
139+ exists ( ComparisonTest ct |
140+ ct .getExpr ( ) = eqtest and
141+ ct .getFirstArgument ( ) = left and
142+ ct .getSecondArgument ( ) = right
143+ |
144+ ct .getComparisonKind ( ) .isEquality ( ) and polarity = true
145+ or
146+ ct .getComparisonKind ( ) .isInequality ( ) and polarity = false
147+ )
148+ or
149+ exists ( IsExpr ie , PatternExpr pat |
150+ ie = eqtest and
151+ ie .getExpr ( ) = left and
152+ ie .getPattern ( ) = pat
153+ |
154+ right = pat .( ConstantPatternExpr ) and
155+ polarity = true
156+ or
157+ right = pat .( NotPatternExpr ) .getPattern ( ) .( ConstantPatternExpr ) and
158+ polarity = false
159+ )
160+ }
161+
162+ class ConditionalExpr = CS:: ConditionalExpr ;
163+
164+ class Parameter = CS:: Parameter ;
165+
166+ private int parameterPosition ( ) { result in [ - 1 , any ( Parameter p ) .getPosition ( ) ] }
167+
168+ class ParameterPosition extends int {
169+ ParameterPosition ( ) { this = parameterPosition ( ) }
170+ }
171+
172+ class ArgumentPosition extends int {
173+ ArgumentPosition ( ) { this = parameterPosition ( ) }
174+ }
175+
176+ pragma [ inline]
177+ predicate parameterMatch ( ParameterPosition ppos , ArgumentPosition apos ) { ppos = apos }
178+
179+ final private class FinalCallable = Callable ;
180+
181+ class NonOverridableMethod extends FinalCallable {
182+ NonOverridableMethod ( ) { not this .( Overridable ) .isOverridableOrImplementable ( ) }
183+
184+ Parameter getParameter ( ParameterPosition ppos ) {
185+ super .getParameter ( ppos ) = result and
186+ not result .isParams ( )
187+ }
188+
189+ Expr getAReturnExpr ( ) { this .canReturn ( result ) }
190+ }
191+
192+ class NonOverridableMethodCall extends Expr instanceof Call {
193+ NonOverridableMethod getMethod ( ) { super .getTarget ( ) .getUnboundDeclaration ( ) = result }
194+
195+ Expr getArgument ( ArgumentPosition apos ) {
196+ result = super .getArgumentForParameter ( any ( Parameter p | p .getPosition ( ) = apos ) )
197+ }
198+ }
199+ }
200+
201+ private module GuardsImpl = SharedGuards:: Make< Location , Cfg , GuardsInput > ;
202+
203+ class GuardValue = GuardsImpl:: GuardValue ;
204+
205+ private module LogicInput implements GuardsImpl:: LogicInputSig {
206+ class SsaDefinition extends Ssa:: Definition {
207+ Expr getARead ( ) { super .getARead ( ) = result }
208+ }
209+
210+ class SsaWriteDefinition extends SsaDefinition instanceof Ssa:: ExplicitDefinition {
211+ Expr getDefinition ( ) { result = super .getADefinition ( ) .getSource ( ) }
212+ }
213+
214+ class SsaPhiNode extends SsaDefinition instanceof Ssa:: PhiNode {
215+ predicate hasInputFromBlock ( SsaDefinition inp , BasicBlock bb ) {
216+ super .hasInputFromBlock ( inp , bb )
217+ }
218+ }
219+
220+ predicate parameterDefinition ( Parameter p , SsaDefinition def ) {
221+ def .( Ssa:: ImplicitParameterDefinition ) .getParameter ( ) = p
222+ }
223+
224+ predicate additionalNullCheck ( GuardsImpl:: PreGuard guard , GuardValue val , Expr e , boolean isNull ) {
225+ // Comparison with a non-`null` value, for example `x?.Length > 0`
226+ exists ( ComparisonTest ct , ComparisonKind ck , Expr arg | ct .getExpr ( ) = guard |
227+ e instanceof DereferenceableExpr and
228+ ct .getAnArgument ( ) = e and
229+ ct .getAnArgument ( ) = arg and
230+ arg = any ( NullValue nv | nv .isNonNull ( ) ) .getAnExpr ( ) and
231+ ck = ct .getComparisonKind ( ) and
232+ e != arg and
233+ isNull = false and
234+ not ck .isEquality ( ) and
235+ not ck .isInequality ( ) and
236+ val .asBooleanValue ( ) = true
237+ )
238+ or
239+ // Call to `string.IsNullOrEmpty()` or `string.IsNullOrWhiteSpace()`
240+ exists ( MethodCall mc , string name | guard = mc |
241+ mc .getTarget ( ) = any ( SystemStringClass c ) .getAMethod ( name ) and
242+ name .regexpMatch ( "IsNullOr(Empty|WhiteSpace)" ) and
243+ mc .getArgument ( 0 ) = e and
244+ val .asBooleanValue ( ) = false and
245+ isNull = false
246+ )
247+ or
248+ guard =
249+ any ( PatternMatch pm |
250+ e instanceof DereferenceableExpr and
251+ e = pm .getExpr ( ) and
252+ (
253+ val .asBooleanValue ( ) .booleanNot ( ) = patternMatchesNull ( pm .getPattern ( ) ) and
254+ isNull = false
255+ or
256+ exists ( TypePatternExpr tpe |
257+ // E.g. `x is string` where `x` has type `string`
258+ typePattern ( guard , tpe , tpe .getCheckedType ( ) ) and
259+ val .asBooleanValue ( ) = false and
260+ isNull = true
261+ )
262+ )
263+ )
264+ or
265+ e .( DereferenceableExpr ) .hasNullableType ( ) and
266+ guard =
267+ any ( PropertyAccess pa |
268+ pa .getQualifier ( ) = e and
269+ pa .getTarget ( ) .hasName ( "HasValue" ) and
270+ val .asBooleanValue ( ) .booleanNot ( ) = isNull
271+ )
272+ }
273+
274+ predicate additionalImpliesStep (
275+ GuardsImpl:: PreGuard g1 , GuardValue v1 , GuardsImpl:: PreGuard g2 , GuardValue v2
276+ ) {
277+ g1 instanceof DereferenceableExpr and
278+ g1 = getNullEquivParent ( g2 ) and
279+ v1 .isNullness ( _) and
280+ v2 = v1
281+ or
282+ g1 instanceof DereferenceableExpr and
283+ g2 = getANullImplyingChild ( g1 ) and
284+ v1 .isNonNullValue ( ) and
285+ v2 = v1
286+ or
287+ g2 = g1 .( NullCoalescingExpr ) .getAnOperand ( ) and
288+ v1 .isNullValue ( ) and
289+ v2 = v1
290+ }
291+ }
292+
293+ module Guards = GuardsImpl:: Logic< LogicInput > ;
16294
17295/** An expression whose value may control the execution of another element. */
18296class Guard extends Expr {
0 commit comments