diff --git a/csharp/ql/lib/semmle/code/csharp/Assignable.qll b/csharp/ql/lib/semmle/code/csharp/Assignable.qll index 7bd432d48ce4..fcc160627832 100644 --- a/csharp/ql/lib/semmle/code/csharp/Assignable.qll +++ b/csharp/ql/lib/semmle/code/csharp/Assignable.qll @@ -117,6 +117,63 @@ class AssignableRead extends AssignableAccess { AssignableRead getANextRead() { result.getControlFlowNode() = this.getAnAdjacentReadSameVar() } } +private newtype TMutationOperationAssignment = + TBuiltInMutationOperation(MutatorOperation mo) or + TUserMutatorOperatorCall(MutatorOperatorCall moc) { + any() + // not moc instanceof InstanceMutatorOperatorCall + } + +/** + * A mutation operation that implicitly assigns the result to its operand. For example, `a++` in + * line 7 in + * + * ```csharp + * class A { + * public static A operator++(A a) { + * return a; + * } + * + * public static A Increment(A a) { + * return a++; + * } + * } + * ``` + */ +private class MutationOperationAssignment extends TMutationOperationAssignment { + string toString() { none() } + + Expr getOperand() { none() } + + Expr getMutationOperation() { none() } +} + +private class BuiltInMutationOperation extends MutationOperationAssignment, + TBuiltInMutationOperation +{ + private MutatorOperation mo; + + BuiltInMutationOperation() { this = TBuiltInMutationOperation(mo) } + + override string toString() { result = mo.toString() } + + override Expr getOperand() { result = mo.getOperand() } + + override Expr getMutationOperation() { result = mo } +} + +private class UserMutatorOperatorCall extends MutationOperationAssignment, TUserMutatorOperatorCall { + private MutatorOperatorCall moc; + + UserMutatorOperatorCall() { this = TUserMutatorOperatorCall(moc) } + + override string toString() { result = moc.toString() } + + override Expr getOperand() { result = moc.getArgument(0) } + + override Expr getMutationOperation() { result = moc } +} + /** * An access to an assignable that updates the underlying value. Either a * variable write (`VariableWrite`), a property write (`PropertyWrite`), @@ -262,7 +319,8 @@ module AssignableInternal { or def = TOutRefDefinition(any(AssignableAccess aa | result = aa.getParent())) or - def = TMutationDefinition(result) + def = + TMutationDefinition(any(MutationOperationAssignment moa | result = moa.getMutationOperation())) or def = TLocalVariableDefinition(result) or @@ -299,7 +357,7 @@ module AssignableInternal { or aa.(RefArg).isPotentialAssignment() } or - TMutationDefinition(MutatorOperation mo) or + TMutationDefinition(MutationOperationAssignment mo) or TLocalVariableDefinition(LocalVariableDeclExpr lvde) { not lvde.hasInitializer() and not exists(getTupleSource(TTupleAssignmentDefinition(_, lvde))) and @@ -372,7 +430,7 @@ module AssignableInternal { or def = TOutRefDefinition(result) or - def = TMutationDefinition(any(MutatorOperation mo | mo.getOperand() = result)) + def = TMutationDefinition(any(MutationOperationAssignment mo | mo.getOperand() = result)) or def = TAddressOfDefinition(any(AddressOfExpr aoe | aoe.getOperand() = result)) or @@ -645,14 +703,26 @@ module AssignableDefinitions { * A definition by mutation, for example `x++`. */ class MutationDefinition extends AssignableDefinition, TMutationDefinition { - MutatorOperation mo; + MutationOperationAssignment moa; + + MutationDefinition() { this = TMutationDefinition(moa) } + + /** + * DEPRECATED: Use `getMutationOperation()` instead. + * + * Gets the underlying mutator operation. + */ + deprecated MutatorOperation getMutatorOperation() { moa = TBuiltInMutationOperation(result) } - MutationDefinition() { this = TMutationDefinition(mo) } + /** + * Gets the underlying mutation operation. + */ + Expr getMutationOperation() { result = moa.getMutationOperation() } - /** Gets the underlying mutator operation. */ - MutatorOperation getMutatorOperation() { result = mo } + // TODO: Removing this below + override Expr getSource() { result = this.getMutationOperation() } - override string toString() { result = mo.toString() } + override string toString() { result = moa.toString() } } /** diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll index 48ed00858a0d..be2a1294ccfd 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -125,7 +125,9 @@ private module Impl { SsaExplicitWrite getExplicitSsaAssignment(SsaExplicitWrite v) { result = v } /** Returns the assignment of the variable update `def`. */ - ExprNode getExprFromSsaAssignment(SsaExplicitWrite def) { result.getExpr() = def.getValue() } + ExprNode getExprFromSsaAssignment(SsaExplicitWrite def) { + result.getExpr() = def.getValue() and not def.getDefiningExpr() instanceof MutatorOperation + } /** Holds if `def` can have any sign. */ predicate explicitSsaDefWithAnySign(SsaExplicitWrite def) { diff --git a/csharp/ql/test/library-tests/dataflow/operators/Operator.cs b/csharp/ql/test/library-tests/dataflow/operators/Operator.cs index 5db1a82b9a4b..63c3f724e0c6 100644 --- a/csharp/ql/test/library-tests/dataflow/operators/Operator.cs +++ b/csharp/ql/test/library-tests/dataflow/operators/Operator.cs @@ -120,3 +120,49 @@ public void M1() Sink(x.Field); // $ hasValueFlow=1 } } + +public class MutatorOperators +{ + static void Sink(object o) { } + static T Source(object source) => throw null; + + public class C1 + { + public object Field { get; private set; } + + public C1() + { + Field = new object(); + } + + public C1(object o) + { + Field = o; + } + + public void operator ++() + { + Field = Source(1); + } + + public static C1 operator --(C1 x) + { + var f = Source(2); + return new C1(f); + } + + public void M1() + { + var x = new C1(); + x++; + Sink(x.Field); // $ hasValueFlow=1 + } + + public void M2() + { + var x = new C1(); + x--; + Sink(x.Field); // $ hasValueFlow=2 + } + } +} diff --git a/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.expected b/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.expected index 8fd12f1c2a8f..ac180359a9f9 100644 --- a/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.expected +++ b/csharp/ql/test/library-tests/dataflow/operators/operatorFlow.expected @@ -130,6 +130,38 @@ edges | Operator.cs:119:14:119:14 | access to local variable y : C [property Field] : Object | Operator.cs:119:9:119:9 | [post] access to local variable x : C [property Field] : Object | provenance | | | Operator.cs:120:14:120:14 | access to local variable x : C [property Field] : Object | Operator.cs:120:14:120:20 | access to property Field | provenance | | | Operator.cs:120:14:120:14 | access to local variable x : C [property Field] : Object | Operator.cs:120:14:120:20 | access to property Field | provenance | | +| Operator.cs:138:26:138:26 | o : Object | Operator.cs:140:21:140:21 | access to parameter o : Object | provenance | | +| Operator.cs:138:26:138:26 | o : Object | Operator.cs:140:21:140:21 | access to parameter o : Object | provenance | | +| Operator.cs:140:13:140:17 | [post] this access : C1 [property Field] : Object | Operator.cs:138:16:138:17 | this [Return] : C1 [property Field] : Object | provenance | | +| Operator.cs:140:13:140:17 | [post] this access : C1 [property Field] : Object | Operator.cs:138:16:138:17 | this [Return] : C1 [property Field] : Object | provenance | | +| Operator.cs:140:21:140:21 | access to parameter o : Object | Operator.cs:140:13:140:17 | [post] this access : C1 [property Field] : Object | provenance | | +| Operator.cs:140:21:140:21 | access to parameter o : Object | Operator.cs:140:13:140:17 | [post] this access : C1 [property Field] : Object | provenance | | +| Operator.cs:143:30:143:31 | this [Return] : C1 [property Field] : Object | Operator.cs:157:13:157:13 | [post] access to local variable x : C1 [property Field] : Object | provenance | | +| Operator.cs:143:30:143:31 | this [Return] : C1 [property Field] : Object | Operator.cs:157:13:157:13 | [post] access to local variable x : C1 [property Field] : Object | provenance | | +| Operator.cs:145:13:145:17 | [post] this access : C1 [property Field] : Object | Operator.cs:143:30:143:31 | this [Return] : C1 [property Field] : Object | provenance | | +| Operator.cs:145:13:145:17 | [post] this access : C1 [property Field] : Object | Operator.cs:143:30:143:31 | this [Return] : C1 [property Field] : Object | provenance | | +| Operator.cs:145:21:145:37 | call to method Source : Object | Operator.cs:145:13:145:17 | [post] this access : C1 [property Field] : Object | provenance | | +| Operator.cs:145:21:145:37 | call to method Source : Object | Operator.cs:145:13:145:17 | [post] this access : C1 [property Field] : Object | provenance | | +| Operator.cs:150:17:150:17 | access to local variable f : Object | Operator.cs:151:27:151:27 | access to local variable f : Object | provenance | | +| Operator.cs:150:17:150:17 | access to local variable f : Object | Operator.cs:151:27:151:27 | access to local variable f : Object | provenance | | +| Operator.cs:150:21:150:37 | call to method Source : Object | Operator.cs:150:17:150:17 | access to local variable f : Object | provenance | | +| Operator.cs:150:21:150:37 | call to method Source : Object | Operator.cs:150:17:150:17 | access to local variable f : Object | provenance | | +| Operator.cs:151:20:151:28 | object creation of type C1 : C1 [property Field] : Object | Operator.cs:164:13:164:15 | call to operator -- : C1 [property Field] : Object | provenance | | +| Operator.cs:151:20:151:28 | object creation of type C1 : C1 [property Field] : Object | Operator.cs:164:13:164:15 | call to operator -- : C1 [property Field] : Object | provenance | | +| Operator.cs:151:27:151:27 | access to local variable f : Object | Operator.cs:138:26:138:26 | o : Object | provenance | | +| Operator.cs:151:27:151:27 | access to local variable f : Object | Operator.cs:138:26:138:26 | o : Object | provenance | | +| Operator.cs:151:27:151:27 | access to local variable f : Object | Operator.cs:151:20:151:28 | object creation of type C1 : C1 [property Field] : Object | provenance | | +| Operator.cs:151:27:151:27 | access to local variable f : Object | Operator.cs:151:20:151:28 | object creation of type C1 : C1 [property Field] : Object | provenance | | +| Operator.cs:157:13:157:13 | [post] access to local variable x : C1 [property Field] : Object | Operator.cs:158:18:158:18 | access to local variable x : C1 [property Field] : Object | provenance | | +| Operator.cs:157:13:157:13 | [post] access to local variable x : C1 [property Field] : Object | Operator.cs:158:18:158:18 | access to local variable x : C1 [property Field] : Object | provenance | | +| Operator.cs:158:18:158:18 | access to local variable x : C1 [property Field] : Object | Operator.cs:158:18:158:24 | access to property Field | provenance | | +| Operator.cs:158:18:158:18 | access to local variable x : C1 [property Field] : Object | Operator.cs:158:18:158:24 | access to property Field | provenance | | +| Operator.cs:164:13:164:13 | access to local variable x : C1 [property Field] : Object | Operator.cs:165:18:165:18 | access to local variable x : C1 [property Field] : Object | provenance | | +| Operator.cs:164:13:164:13 | access to local variable x : C1 [property Field] : Object | Operator.cs:165:18:165:18 | access to local variable x : C1 [property Field] : Object | provenance | | +| Operator.cs:164:13:164:15 | call to operator -- : C1 [property Field] : Object | Operator.cs:164:13:164:13 | access to local variable x : C1 [property Field] : Object | provenance | | +| Operator.cs:164:13:164:15 | call to operator -- : C1 [property Field] : Object | Operator.cs:164:13:164:13 | access to local variable x : C1 [property Field] : Object | provenance | | +| Operator.cs:165:18:165:18 | access to local variable x : C1 [property Field] : Object | Operator.cs:165:18:165:24 | access to property Field | provenance | | +| Operator.cs:165:18:165:18 | access to local variable x : C1 [property Field] : Object | Operator.cs:165:18:165:24 | access to property Field | provenance | | nodes | Operator.cs:9:39:9:39 | x : C | semmle.label | x : C | | Operator.cs:9:39:9:39 | x : C | semmle.label | x : C | @@ -275,6 +307,42 @@ nodes | Operator.cs:120:14:120:14 | access to local variable x : C [property Field] : Object | semmle.label | access to local variable x : C [property Field] : Object | | Operator.cs:120:14:120:20 | access to property Field | semmle.label | access to property Field | | Operator.cs:120:14:120:20 | access to property Field | semmle.label | access to property Field | +| Operator.cs:138:16:138:17 | this [Return] : C1 [property Field] : Object | semmle.label | this [Return] : C1 [property Field] : Object | +| Operator.cs:138:16:138:17 | this [Return] : C1 [property Field] : Object | semmle.label | this [Return] : C1 [property Field] : Object | +| Operator.cs:138:26:138:26 | o : Object | semmle.label | o : Object | +| Operator.cs:138:26:138:26 | o : Object | semmle.label | o : Object | +| Operator.cs:140:13:140:17 | [post] this access : C1 [property Field] : Object | semmle.label | [post] this access : C1 [property Field] : Object | +| Operator.cs:140:13:140:17 | [post] this access : C1 [property Field] : Object | semmle.label | [post] this access : C1 [property Field] : Object | +| Operator.cs:140:21:140:21 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| Operator.cs:140:21:140:21 | access to parameter o : Object | semmle.label | access to parameter o : Object | +| Operator.cs:143:30:143:31 | this [Return] : C1 [property Field] : Object | semmle.label | this [Return] : C1 [property Field] : Object | +| Operator.cs:143:30:143:31 | this [Return] : C1 [property Field] : Object | semmle.label | this [Return] : C1 [property Field] : Object | +| Operator.cs:145:13:145:17 | [post] this access : C1 [property Field] : Object | semmle.label | [post] this access : C1 [property Field] : Object | +| Operator.cs:145:13:145:17 | [post] this access : C1 [property Field] : Object | semmle.label | [post] this access : C1 [property Field] : Object | +| Operator.cs:145:21:145:37 | call to method Source : Object | semmle.label | call to method Source : Object | +| Operator.cs:145:21:145:37 | call to method Source : Object | semmle.label | call to method Source : Object | +| Operator.cs:150:17:150:17 | access to local variable f : Object | semmle.label | access to local variable f : Object | +| Operator.cs:150:17:150:17 | access to local variable f : Object | semmle.label | access to local variable f : Object | +| Operator.cs:150:21:150:37 | call to method Source : Object | semmle.label | call to method Source : Object | +| Operator.cs:150:21:150:37 | call to method Source : Object | semmle.label | call to method Source : Object | +| Operator.cs:151:20:151:28 | object creation of type C1 : C1 [property Field] : Object | semmle.label | object creation of type C1 : C1 [property Field] : Object | +| Operator.cs:151:20:151:28 | object creation of type C1 : C1 [property Field] : Object | semmle.label | object creation of type C1 : C1 [property Field] : Object | +| Operator.cs:151:27:151:27 | access to local variable f : Object | semmle.label | access to local variable f : Object | +| Operator.cs:151:27:151:27 | access to local variable f : Object | semmle.label | access to local variable f : Object | +| Operator.cs:157:13:157:13 | [post] access to local variable x : C1 [property Field] : Object | semmle.label | [post] access to local variable x : C1 [property Field] : Object | +| Operator.cs:157:13:157:13 | [post] access to local variable x : C1 [property Field] : Object | semmle.label | [post] access to local variable x : C1 [property Field] : Object | +| Operator.cs:158:18:158:18 | access to local variable x : C1 [property Field] : Object | semmle.label | access to local variable x : C1 [property Field] : Object | +| Operator.cs:158:18:158:18 | access to local variable x : C1 [property Field] : Object | semmle.label | access to local variable x : C1 [property Field] : Object | +| Operator.cs:158:18:158:24 | access to property Field | semmle.label | access to property Field | +| Operator.cs:158:18:158:24 | access to property Field | semmle.label | access to property Field | +| Operator.cs:164:13:164:13 | access to local variable x : C1 [property Field] : Object | semmle.label | access to local variable x : C1 [property Field] : Object | +| Operator.cs:164:13:164:13 | access to local variable x : C1 [property Field] : Object | semmle.label | access to local variable x : C1 [property Field] : Object | +| Operator.cs:164:13:164:15 | call to operator -- : C1 [property Field] : Object | semmle.label | call to operator -- : C1 [property Field] : Object | +| Operator.cs:164:13:164:15 | call to operator -- : C1 [property Field] : Object | semmle.label | call to operator -- : C1 [property Field] : Object | +| Operator.cs:165:18:165:18 | access to local variable x : C1 [property Field] : Object | semmle.label | access to local variable x : C1 [property Field] : Object | +| Operator.cs:165:18:165:18 | access to local variable x : C1 [property Field] : Object | semmle.label | access to local variable x : C1 [property Field] : Object | +| Operator.cs:165:18:165:24 | access to property Field | semmle.label | access to property Field | +| Operator.cs:165:18:165:24 | access to property Field | semmle.label | access to property Field | subpaths | Operator.cs:29:17:29:17 | access to local variable x : C | Operator.cs:16:38:16:38 | x : C | Operator.cs:16:49:16:49 | access to parameter x : C | Operator.cs:29:17:29:21 | call to operator + : C | | Operator.cs:29:17:29:17 | access to local variable x : C | Operator.cs:16:38:16:38 | x : C | Operator.cs:16:49:16:49 | access to parameter x : C | Operator.cs:29:17:29:21 | call to operator + : C | @@ -292,6 +360,8 @@ subpaths | Operator.cs:118:23:118:29 | access to local variable tainted : Object | Operator.cs:103:25:103:25 | o : Object | Operator.cs:103:16:103:16 | this [Return] : C [property Field] : Object | Operator.cs:118:17:118:30 | object creation of type C : C [property Field] : Object | | Operator.cs:119:14:119:14 | access to local variable y : C [property Field] : Object | Operator.cs:108:35:108:35 | x : C [property Field] : Object | Operator.cs:108:30:108:31 | this [Return] : C [property Field] : Object | Operator.cs:119:9:119:9 | [post] access to local variable x : C [property Field] : Object | | Operator.cs:119:14:119:14 | access to local variable y : C [property Field] : Object | Operator.cs:108:35:108:35 | x : C [property Field] : Object | Operator.cs:108:30:108:31 | this [Return] : C [property Field] : Object | Operator.cs:119:9:119:9 | [post] access to local variable x : C [property Field] : Object | +| Operator.cs:151:27:151:27 | access to local variable f : Object | Operator.cs:138:26:138:26 | o : Object | Operator.cs:138:16:138:17 | this [Return] : C1 [property Field] : Object | Operator.cs:151:20:151:28 | object creation of type C1 : C1 [property Field] : Object | +| Operator.cs:151:27:151:27 | access to local variable f : Object | Operator.cs:138:26:138:26 | o : Object | Operator.cs:138:16:138:17 | this [Return] : C1 [property Field] : Object | Operator.cs:151:20:151:28 | object creation of type C1 : C1 [property Field] : Object | testFailures #select | Operator.cs:30:14:30:14 | access to local variable z | Operator.cs:27:17:27:28 | call to method Source : C | Operator.cs:30:14:30:14 | access to local variable z | $@ | Operator.cs:27:17:27:28 | call to method Source : C | call to method Source : C | @@ -308,3 +378,7 @@ testFailures | Operator.cs:78:14:78:14 | (...) ... | Operator.cs:84:17:84:29 | call to method Source : C | Operator.cs:78:14:78:14 | (...) ... | $@ | Operator.cs:84:17:84:29 | call to method Source : C | call to method Source : C | | Operator.cs:120:14:120:20 | access to property Field | Operator.cs:116:23:116:39 | call to method Source : Object | Operator.cs:120:14:120:20 | access to property Field | $@ | Operator.cs:116:23:116:39 | call to method Source : Object | call to method Source : Object | | Operator.cs:120:14:120:20 | access to property Field | Operator.cs:116:23:116:39 | call to method Source : Object | Operator.cs:120:14:120:20 | access to property Field | $@ | Operator.cs:116:23:116:39 | call to method Source : Object | call to method Source : Object | +| Operator.cs:158:18:158:24 | access to property Field | Operator.cs:145:21:145:37 | call to method Source : Object | Operator.cs:158:18:158:24 | access to property Field | $@ | Operator.cs:145:21:145:37 | call to method Source : Object | call to method Source : Object | +| Operator.cs:158:18:158:24 | access to property Field | Operator.cs:145:21:145:37 | call to method Source : Object | Operator.cs:158:18:158:24 | access to property Field | $@ | Operator.cs:145:21:145:37 | call to method Source : Object | call to method Source : Object | +| Operator.cs:165:18:165:24 | access to property Field | Operator.cs:150:21:150:37 | call to method Source : Object | Operator.cs:165:18:165:24 | access to property Field | $@ | Operator.cs:150:21:150:37 | call to method Source : Object | call to method Source : Object | +| Operator.cs:165:18:165:24 | access to property Field | Operator.cs:150:21:150:37 | call to method Source : Object | Operator.cs:165:18:165:24 | access to property Field | $@ | Operator.cs:150:21:150:37 | call to method Source : Object | call to method Source : Object |