C#: Use CFG nodes instead of AST nodes in sign/modulus analysis

This commit is contained in:
Tom Hvitved
2020-10-09 11:24:10 +02:00
parent 952b2da7d4
commit 2af7e1c213
23 changed files with 906 additions and 478 deletions

View File

@@ -310,6 +310,22 @@ module ControlFlow {
Split getASplit() { result = splits.getASplit() }
}
/** A control-flow node for an expression. */
class ExprNode extends ElementNode {
Expr e;
ExprNode() { e = unique(Expr e_ | e_ = this.getElement() | e_) }
/** Gets the expression that this control-flow node belongs to. */
Expr getExpr() { result = e }
/** Gets the value of this expression node, if any. */
string getValue() { result = e.getValue() }
/** Gets the type of this expression node. */
Type getType() { result = e.getType() }
}
class Split = SplitImpl;
class FinallySplit = FinallySplitting::FinallySplitImpl;

View File

@@ -111,18 +111,6 @@ private predicate evenlyDivisibleExpr(Expr e, int factor) {
)
}
/**
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
* in an arbitrary 1-based numbering of the input edges to `phi`.
*/
private predicate rankedPhiInput(
SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge, int r
) {
edge.phiInput(phi, inp) and
edge =
rank[r](SsaReadPositionPhiInputEdge e | e.phiInput(phi, _) | e order by getId(e.getOrigBlock()))
}
/**
* Holds if `rix` is the number of input edges to `phi`.
*/

View File

@@ -6,4 +6,41 @@
* three-valued domain `{negative, zero, positive}`.
*/
import semmle.code.csharp.dataflow.internal.rangeanalysis.SignAnalysisCommon
import csharp
private import semmle.code.csharp.dataflow.internal.rangeanalysis.SignAnalysisCommon as Common
/** Holds if `e` can be positive and cannot be negative. */
predicate positiveExpr(Expr e) {
forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() |
positive(cfn) or strictlyPositive(cfn)
)
}
/** Holds if `e` can be negative and cannot be positive. */
predicate negativeExpr(Expr e) {
forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() |
negative(cfn) or strictlyNegative(cfn)
)
}
/** Holds if `e` is strictly positive. */
predicate strictlyPositiveExpr(Expr e) {
forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() | strictlyPositive(cfn))
}
/** Holds if `e` is strictly negative. */
predicate strictlyNegativeExpr(Expr e) {
forex(ControlFlow::Node cfn | cfn = e.getAControlFlowNode() | strictlyNegative(cfn))
}
/** Holds if `e` can be positive and cannot be negative. */
predicate positive(ControlFlow::Nodes::ExprNode e) { Common::positive(e) }
/** Holds if `e` can be negative and cannot be positive. */
predicate negative(ControlFlow::Nodes::ExprNode e) { Common::negative(e) }
/** Holds if `e` is strictly positive. */
predicate strictlyPositive(ControlFlow::Nodes::ExprNode e) { Common::strictlyPositive(e) }
/** Holds if `e` is strictly negative. */
predicate strictlyNegative(ControlFlow::Nodes::ExprNode e) { Common::strictlyNegative(e) }

View File

@@ -5,17 +5,16 @@
private import csharp as CS
private import semmle.code.csharp.dataflow.SSA::Ssa as Ssa
private import semmle.code.csharp.dataflow.internal.rangeanalysis.ConstantUtils as CU
private import semmle.code.csharp.dataflow.internal.rangeanalysis.RangeUtils as RU
private import semmle.code.csharp.dataflow.internal.rangeanalysis.SsaUtils as SU
class SsaVariable extends Ssa::Definition {
/** Gets a read of the source variable underlying this SSA definition. */
Expr getAUse() { result = getARead() }
}
class SsaVariable = SU::SsaVariable;
class Expr = CS::Expr;
class Expr = CS::ControlFlow::Nodes::ExprNode;
class IntegralType = CS::IntegralType;
class ConstantIntegerExpr = CU::ConstantIntegerExpr;
/** Holds if `e` is a bound expression and it is not an SSA variable read. */
predicate interestingExprBound(Expr e) { CU::systemArrayLengthAccess(e.(CS::PropertyRead)) }
predicate interestingExprBound(Expr e) { CU::systemArrayLengthAccess(e.getExpr()) }

View File

@@ -4,18 +4,10 @@
private import csharp
private import Ssa
private import SsaUtils
private import RangeUtils
/**
* Holds if property `p` matches `property` in `baseClass` or any overrides.
*/
predicate propertyOverrides(Property p, string baseClass, string property) {
exists(Property p2 |
p2.getSourceDeclaration().getDeclaringType().hasQualifiedName(baseClass) and
p2.hasName(property)
|
p.overridesOrImplementsOrEquals(p2)
)
}
private class ExprNode = ControlFlow::Nodes::ExprNode;
/**
* Holds if `pa` is an access to the `Length` property of an array.
@@ -30,40 +22,42 @@ predicate systemArrayLengthAccess(PropertyAccess pa) {
* - a read of a compile time constant with integer value `val`, or
* - a read of the `Length` of an array with `val` lengths.
*/
private predicate constantIntegerExpr(Expr e, int val) {
private predicate constantIntegerExpr(ExprNode e, int val) {
e.getValue().toInt() = val
or
exists(ExplicitDefinition v, Expr src |
e = v.getARead() and
src = v.getADefinition().getSource() and
exists(ExprNode src |
e = getAnExplicitDefinitionRead(src) and
constantIntegerExpr(src, val)
)
or
isArrayLengthAccess(e, val)
}
private int getArrayLength(ArrayCreation arrCreation, int index) {
constantIntegerExpr(arrCreation.getLengthArgument(index), result)
private int getArrayLength(ExprNode e, int index) {
exists(ArrayCreation arrCreation, ExprNode length |
hasChild(arrCreation, arrCreation.getLengthArgument(index), e, length) and
constantIntegerExpr(length, result)
)
}
private int getArrayLengthRec(ArrayCreation arrCreation, int index) {
private int getArrayLengthRec(ExprNode arrCreation, int index) {
index = 0 and result = getArrayLength(arrCreation, 0)
or
index > 0 and
result = getArrayLength(arrCreation, index) * getArrayLengthRec(arrCreation, index - 1)
}
private predicate isArrayLengthAccess(PropertyAccess pa, int length) {
systemArrayLengthAccess(pa) and
exists(ExplicitDefinition arr, ArrayCreation arrCreation |
getArrayLengthRec(arrCreation, arrCreation.getNumberOfLengthArguments() - 1) = length and
arrCreation = arr.getADefinition().getSource() and
pa.getQualifier() = arr.getARead()
private predicate isArrayLengthAccess(ExprNode e, int length) {
exists(PropertyAccess pa, ExprNode arrCreation |
systemArrayLengthAccess(pa) and
getArrayLengthRec(arrCreation,
arrCreation.getExpr().(ArrayCreation).getNumberOfLengthArguments() - 1) = length and
hasChild(pa, pa.getQualifier(), e, getAnExplicitDefinitionRead(arrCreation))
)
}
/** An expression that always has the same integer value. */
class ConstantIntegerExpr extends Expr {
class ConstantIntegerExpr extends ExprNode {
ConstantIntegerExpr() { constantIntegerExpr(this, _) }
/** Gets the integer value of this expression. */

View File

@@ -1,98 +1,39 @@
module Private {
private import csharp as CS
private import ConstantUtils as CU
private import semmle.code.csharp.controlflow.Guards as G
private import semmle.code.csharp.dataflow.internal.rangeanalysis.RangeUtils as RU
private import SsaUtils as SU
private import SignAnalysisSpecific::Private as SA
private import SsaReadPositionCommon
class BasicBlock = CS::Ssa::BasicBlock;
class SsaVariable extends CS::Ssa::Definition {
CS::AssignableRead getAUse() { result = this.getARead() }
}
class SsaVariable = SU::SsaVariable;
class SsaPhiNode = CS::Ssa::PhiNode;
class Expr = CS::Expr;
class Expr = CS::ControlFlow::Nodes::ExprNode;
class Guard = G::Guard;
class Guard = RU::Guard;
class ConstantIntegerExpr = CU::ConstantIntegerExpr;
class ConditionalExpr extends CS::ConditionalExpr {
/** Gets the "then" expression of this conditional expression. */
Expr getTrueExpr() { result = this.getThen() }
class ConditionalExpr = RU::ExprNode::ConditionalExpr;
/** Gets the "else" expression of this conditional expression. */
Expr getFalseExpr() { result = this.getElse() }
}
class AddExpr = RU::ExprNode::AddExpr;
/** Represent an addition expression. */
class AddExpr extends CS::AddExpr {
/** Gets the LHS operand of this add expression. */
Expr getLhs() { result = this.getLeftOperand() }
class SubExpr = RU::ExprNode::SubExpr;
/** Gets the RHS operand of this add expression. */
Expr getRhs() { result = this.getRightOperand() }
}
class RemExpr = RU::ExprNode::RemExpr;
/** Represent a subtraction expression. */
class SubExpr extends CS::SubExpr {
/** Gets the LHS operand of this subtraction expression. */
Expr getLhs() { result = this.getLeftOperand() }
class BitwiseAndExpr = RU::ExprNode::BitwiseAndExpr;
/** Gets the RHS operand of this subtraction expression. */
Expr getRhs() { result = this.getRightOperand() }
}
class MulExpr = RU::ExprNode::MulExpr;
class RemExpr = CS::RemExpr;
class LShiftExpr = RU::ExprNode::LShiftExpr;
/** Represent a bitwise and or an assign-and expression. */
class BitwiseAndExpr extends CS::Expr {
BitwiseAndExpr() { this instanceof CS::BitwiseAndExpr or this instanceof CS::AssignAndExpr }
predicate guardDirectlyControlsSsaRead = RU::guardControlsSsaRead/3;
/** Gets an operand of this bitwise and operation. */
Expr getAnOperand() {
result = this.(CS::BitwiseAndExpr).getAnOperand() or
result = this.(CS::AssignAndExpr).getRValue() or
result = this.(CS::AssignAndExpr).getLValue()
}
/** Holds if this expression has operands `e1` and `e2`. */
predicate hasOperands(Expr e1, Expr e2) {
this.getAnOperand() = e1 and
this.getAnOperand() = e2 and
e1 != e2
}
}
/** Represent a multiplication or an assign-mul expression. */
class MulExpr extends CS::Expr {
MulExpr() { this instanceof CS::MulExpr or this instanceof CS::AssignMulExpr }
/** Gets an operand of this multiplication. */
Expr getAnOperand() {
result = this.(CS::MulExpr).getAnOperand() or
result = this.(CS::AssignMulExpr).getRValue() or
result = this.(CS::AssignMulExpr).getLValue()
}
}
/** Represent a left shift or an assign-lshift expression. */
class LShiftExpr extends CS::Expr {
LShiftExpr() { this instanceof CS::LShiftExpr or this instanceof CS::AssignLShiftExpr }
/** Gets the RHS operand of this shift. */
Expr getRhs() {
result = this.(CS::LShiftExpr).getRightOperand() or
result = this.(CS::AssignLShiftExpr).getRValue()
}
}
predicate guardDirectlyControlsSsaRead = SA::guardControlsSsaRead/3;
predicate guardControlsSsaRead = SA::guardControlsSsaRead/3;
predicate guardControlsSsaRead = RU::guardControlsSsaRead/3;
predicate valueFlowStep = RU::valueFlowStep/3;
@@ -100,11 +41,44 @@ module Private {
predicate ssaUpdateStep = RU::ssaUpdateStep/3;
Expr getABasicBlockExpr(BasicBlock bb) { result = bb.getANode().getElement() }
Expr getABasicBlockExpr(BasicBlock bb) { result = bb.getANode() }
private predicate id(CS::ControlFlowElement x, CS::ControlFlowElement y) { x = y }
private class CallableOrCFE extends CS::Element {
CallableOrCFE() { this instanceof CS::Callable or this instanceof CS::ControlFlowElement }
}
private predicate idOf(CS::ControlFlowElement x, int y) = equivalenceRelation(id/2)(x, y)
private predicate id(CallableOrCFE x, CallableOrCFE y) { x = y }
int getId(BasicBlock bb) { idOf(bb.getFirstNode().getElement(), result) }
private predicate idOf(CallableOrCFE x, int y) = equivalenceRelation(id/2)(x, y)
private class PhiInputEdgeBlock extends BasicBlock {
PhiInputEdgeBlock() { this = any(SsaReadPositionPhiInputEdge edge).getOrigBlock() }
}
int getId(PhiInputEdgeBlock bb) {
idOf(bb.getFirstNode().getElement(), result)
or
idOf(bb.(CS::ControlFlow::BasicBlocks::EntryBlock).getCallable(), result)
}
private string getSplitString(PhiInputEdgeBlock bb) {
result = bb.getFirstNode().(CS::ControlFlow::Nodes::ElementNode).getSplitsString()
or
not exists(bb.getFirstNode().(CS::ControlFlow::Nodes::ElementNode).getSplitsString()) and
result = ""
}
/**
* Holds if `inp` is an input to `phi` along `edge` and this input has index `r`
* in an arbitrary 1-based numbering of the input edges to `phi`.
*/
predicate rankedPhiInput(SsaPhiNode phi, SsaVariable inp, SsaReadPositionPhiInputEdge edge, int r) {
edge.phiInput(phi, inp) and
edge =
rank[r](SsaReadPositionPhiInputEdge e |
e.phiInput(phi, _)
|
e order by getId(e.getOrigBlock()), getSplitString(e.getOrigBlock())
)
}
}

View File

@@ -1,98 +1,406 @@
/**
* Provides predicates for range and modulus analysis.
*/
private module Impl {
private import csharp
private import Ssa
private import SsaUtils
private import ConstantUtils
private import SsaReadPositionCommon
private import semmle.code.csharp.controlflow.Guards as G
private import ControlFlowReachability
private import csharp
private import Ssa
private import SsaUtils
private import ConstantUtils
private import SsaReadPositionCommon
private import semmle.code.csharp.controlflow.Guards as G
private class BooleanValue = G::AbstractValues::BooleanValue;
private class BooleanValue = G::AbstractValues::BooleanValue;
private class ExprNode = ControlFlow::Nodes::ExprNode;
/**
* Holds if `v` is an `ExplicitDefinition` that equals `e + delta`.
*/
predicate ssaUpdateStep(ExplicitDefinition v, Expr e, int delta) {
v.getADefinition().getExpr().(Assignment).getRValue() = e and delta = 0
or
v.getADefinition().getExpr().(PostIncrExpr).getOperand() = e and delta = 1
or
v.getADefinition().getExpr().(PreIncrExpr).getOperand() = e and delta = 1
or
v.getADefinition().getExpr().(PostDecrExpr).getOperand() = e and delta = -1
or
v.getADefinition().getExpr().(PreDecrExpr).getOperand() = e and delta = -1
}
private class ExprChildReachability extends ControlFlowReachabilityConfiguration {
ExprChildReachability() { this = "ExprChildReachability" }
private G::Guard eqFlowCondAbs(Definition def, Expr e, int delta, boolean isEq, G::AbstractValue v) {
exists(boolean eqpolarity |
result.isEquality(ssaRead(def, delta), e, eqpolarity) and
eqpolarity.booleanXor(v.(BooleanValue).getValue()).booleanNot() = isEq
)
or
exists(G::AbstractValue v0 |
G::Internal::impliesSteps(result, v, eqFlowCondAbs(def, e, delta, isEq, v0), v0)
)
}
override predicate candidate(
Expr e1, Expr e2, ControlFlowElement scope, boolean exactScope, boolean isSuccessor
) {
e2 = e1.getAChild() and
scope = e1 and
exactScope = false and
isSuccessor in [false, true]
}
}
/**
* Gets a condition that tests whether `def` equals `e + delta`.
*
* If the condition evaluates to `testIsTrue`:
* - `isEq = true` : `def == e + delta`
* - `isEq = false` : `def != e + delta`
*/
G::Guard eqFlowCond(Definition def, Expr e, int delta, boolean isEq, boolean testIsTrue) {
exists(BooleanValue v |
result = eqFlowCondAbs(def, e, delta, isEq, v) and
testIsTrue = v.getValue()
)
}
/** Holds if `parent` having child `child` implies `parentNode` having child `childNode`. */
predicate hasChild(Expr parent, Expr child, ExprNode parentNode, ExprNode childNode) {
any(ExprChildReachability x).hasExprPath(parent, parentNode, child, childNode)
}
/**
* Holds if `e1 + delta` equals `e2`.
*/
predicate valueFlowStep(Expr e2, Expr e1, int delta) {
valueFlowStep(e2.(AssignOperation).getExpandedAssignment(), e1, delta)
or
e2.(AssignExpr).getRValue() = e1 and delta = 0
or
e2.(UnaryPlusExpr).getOperand() = e1 and delta = 0
or
e2.(PostIncrExpr).getOperand() = e1 and delta = 0
or
e2.(PostDecrExpr).getOperand() = e1 and delta = 0
or
e2.(PreIncrExpr).getOperand() = e1 and delta = 1
or
e2.(PreDecrExpr).getOperand() = e1 and delta = -1
or
exists(ConstantIntegerExpr x |
e2.(AddExpr).getAnOperand() = e1 and
e2.(AddExpr).getAnOperand() = x and
not e1 = x
|
x.getIntValue() = delta
)
or
exists(ConstantIntegerExpr x |
exists(SubExpr sub |
e2 = sub and
sub.getLeftOperand() = e1 and
sub.getRightOperand() = x
/** Holds if SSA definition `def` equals `e + delta`. */
predicate ssaUpdateStep(ExplicitDefinition def, ExprNode e, int delta) {
exists(ControlFlow::Node cfn | cfn = def.getControlFlowNode() |
e = cfn.(ExprNode::Assignment).getRValue() and delta = 0
or
e = cfn.(ExprNode::PostIncrExpr).getOperand() and delta = 1
or
e = cfn.(ExprNode::PreIncrExpr).getOperand() and delta = 1
or
e = cfn.(ExprNode::PostDecrExpr).getOperand() and delta = -1
or
e = cfn.(ExprNode::PreDecrExpr).getOperand() and delta = -1
)
|
x.getIntValue() = -delta
)
}
/** Holds if `e1 + delta` equals `e2`. */
predicate valueFlowStep(ExprNode e2, ExprNode e1, int delta) {
e2.(ExprNode::AssignExpr).getRValue() = e1 and delta = 0
or
e2.(ExprNode::UnaryPlusExpr).getOperand() = e1 and delta = 0
or
e2.(ExprNode::PostIncrExpr).getOperand() = e1 and delta = 0
or
e2.(ExprNode::PostDecrExpr).getOperand() = e1 and delta = 0
or
e2.(ExprNode::PreIncrExpr).getOperand() = e1 and delta = 1
or
e2.(ExprNode::PreDecrExpr).getOperand() = e1 and delta = -1
or
exists(ConstantIntegerExpr x |
e2.(ExprNode::AddExpr).getAnOperand() = e1 and
e2.(ExprNode::AddExpr).getAnOperand() = x and
e1 != x and
x.getIntValue() = delta
)
or
exists(ConstantIntegerExpr x |
e2.(ExprNode::SubExpr).getLeftOperand() = e1 and
e2.(ExprNode::SubExpr).getRightOperand() = x and
x.getIntValue() = -delta
)
}
/** An expression whose value may control the execution of another element. */
class Guard extends Expr {
Guard() { this instanceof G::Guard }
/**
* Holds if basic block `bb` is guarded by this guard having value `v`.
*/
predicate controlsBasicBlock(BasicBlock bb, G::AbstractValue v) {
this.(G::Guard).controlsBasicBlock(bb, v)
}
/**
* Holds if this guard is an equality test between `e1` and `e2`. If the test is
* negated, that is `!=`, then `polarity` is false, otherwise `polarity` is
* true.
*/
predicate isEquality(ExprNode e1, ExprNode e2, boolean polarity) {
exists(Expr e1_, Expr e2_ |
e1 = unique(ExprNode cfn | hasChild(this, e1_, _, cfn) | cfn) and
e2 = unique(ExprNode cfn | hasChild(this, e2_, _, cfn) | cfn) and
this.(G::Guard).isEquality(e1_, e2_, polarity)
)
}
}
private Guard eqFlowCondAbs(
Definition def, ExprNode e, int delta, boolean isEq, G::AbstractValue v
) {
exists(boolean eqpolarity |
result.isEquality(ssaRead(def, delta), e, eqpolarity) and
eqpolarity.booleanXor(v.(BooleanValue).getValue()).booleanNot() = isEq
)
or
exists(G::AbstractValue v0 |
G::Internal::impliesSteps(result, v, eqFlowCondAbs(def, e, delta, isEq, v0), v0)
)
}
/**
* Gets a condition that tests whether `def` equals `e + delta`.
*
* If the condition evaluates to `testIsTrue`:
* - `isEq = true` : `def == e + delta`
* - `isEq = false` : `def != e + delta`
*/
Guard eqFlowCond(Definition def, ExprNode e, int delta, boolean isEq, boolean testIsTrue) {
exists(BooleanValue v |
result = eqFlowCondAbs(def, e, delta, isEq, v) and
testIsTrue = v.getValue()
)
}
/**
* Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
*/
predicate guardControlsSsaRead(Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
exists(BooleanValue b | b.getValue() = testIsTrue |
guard.controlsBasicBlock(controlled.(SsaReadPositionBlock).getBlock(), b)
)
}
/**
* Holds if property `p` matches `property` in `baseClass` or any overrides.
*/
predicate propertyOverrides(Property p, string baseClass, string property) {
exists(Property p2 |
p2.getSourceDeclaration().getDeclaringType().hasQualifiedName(baseClass) and
p2.hasName(property)
|
p.overridesOrImplementsOrEquals(p2)
)
}
}
import Impl
/**
* Holds if `guard` controls the position `controlled` with the value `testIsTrue`.
* Provides classes for mapping CFG nodes to AST nodes, in a way that respects
* control-flow splitting. For example, in
*
* ```csharp
* int M(bool b)
* {
* var i = b ? 1 : -1;
* i = i - 1;
* if (b)
* return 0;
* return i;
* }
* ```
* the subtraction `i - 1` exists in two copies in the CFG. The class `SubExpr`
* contains each copy, with split-respecting `getLeft/RightOperand()` predicates.
*/
predicate guardControlsSsaRead(G::Guard guard, SsaReadPosition controlled, boolean testIsTrue) {
exists(BooleanValue b | b.getValue() = testIsTrue |
guard.controlsBasicBlock(controlled.(SsaReadPositionBlock).getBlock(), b)
)
module ExprNode {
private import csharp as CS
private class ExprNode = CS::ControlFlow::Nodes::ExprNode;
private import Sign
/** An assignable access. */
class AssignableAccess extends ExprNode {
override CS::AssignableAccess e;
}
/** A field access. */
class FieldAccess extends ExprNode {
override CS::FieldAccess e;
}
/** A character literal. */
class CharLiteral extends ExprNode {
override CS::CharLiteral e;
}
/** An integer literal. */
class IntegerLiteral extends ExprNode {
override CS::IntegerLiteral e;
}
/** A long literal. */
class LongLiteral extends ExprNode {
override CS::LongLiteral e;
}
/** A cast. */
class CastExpr extends ExprNode {
override CS::CastExpr e;
/** Gets the source type of this cast. */
CS::Type getSourceType() { result = e.getSourceType() }
}
/** A floating point literal. */
class RealLiteral extends ExprNode {
override CS::RealLiteral e;
}
/** An assignment. */
class Assignment extends ExprNode {
override CS::Assignment e;
/** Gets the left operand of this assignment. */
ExprNode getLValue() {
result = unique(ExprNode res | hasChild(e, e.getLValue(), this, res) | res)
}
/** Gets the right operand of this assignment. */
ExprNode getRValue() {
result = unique(ExprNode res | hasChild(e, e.getRValue(), this, res) | res)
}
}
/** A simple assignment. */
class AssignExpr extends Assignment {
override CS::AssignExpr e;
}
/** A unary operation. */
class UnaryOperation extends ExprNode {
override CS::UnaryOperation e;
/** Returns the operand of this unary operation. */
ExprNode getOperand() {
result = unique(ExprNode res | hasChild(e, e.getOperand(), this, res) | res)
}
/** Returns the operation representing this unary operation. */
TUnarySignOperation getOp() { none() }
}
/** A prefix increment operation. */
class PreIncrExpr extends UnaryOperation {
override CS::PreIncrExpr e;
override TIncOp getOp() { any() }
}
/** A postfix increment operation. */
class PostIncrExpr extends UnaryOperation {
override CS::PostIncrExpr e;
}
/** A prefix decrement operation. */
class PreDecrExpr extends UnaryOperation {
override CS::PreDecrExpr e;
override TDecOp getOp() { any() }
}
/** A postfix decrement operation. */
class PostDecrExpr extends UnaryOperation {
override CS::PostDecrExpr e;
}
/** A unary plus operation. */
class UnaryPlusExpr extends UnaryOperation {
override CS::UnaryPlusExpr e;
}
/** A unary minus operation. */
class UnaryMinusExpr extends UnaryOperation {
override CS::UnaryMinusExpr e;
override TNegOp getOp() { any() }
}
/** A bitwise complement operation. */
class ComplementExpr extends UnaryOperation {
override CS::ComplementExpr e;
override TBitNotOp getOp() { any() }
}
/** A binary operation. */
class BinaryOperation extends ExprNode {
override CS::BinaryOperation e;
/** Returns the operation representing this binary operation. */
TBinarySignOperation getOp() { none() }
/** Gets the left operand of this binary operation. */
ExprNode getLeftOperand() {
result = unique(ExprNode res | hasChild(e, e.getLeftOperand(), this, res) | res)
}
/** Gets the right operand of this binary operation. */
ExprNode getRightOperand() {
result = unique(ExprNode res | hasChild(e, e.getRightOperand(), this, res) | res)
}
/** Gets the left operand of this binary operation. */
ExprNode getLhs() { result = this.getLeftOperand() }
/** Gets the right operand of this binary operation. */
ExprNode getRhs() { result = this.getRightOperand() }
/** Gets an operand of this binary operation. */
ExprNode getAnOperand() { hasChild(e, e.getAnOperand(), this, result) }
/** Holds if this binary operation has operands `e1` and `e2`. */
predicate hasOperands(ExprNode e1, ExprNode e2) {
this.getAnOperand() = e1 and
this.getAnOperand() = e2 and
e1 != e2
}
}
/** An addition operation. */
class AddExpr extends BinaryOperation {
override CS::AddExpr e;
override TAddOp getOp() { any() }
}
/** A subtraction operation. */
class SubExpr extends BinaryOperation {
override CS::SubExpr e;
override TSubOp getOp() { any() }
}
/** A multiplication operation. */
class MulExpr extends BinaryOperation {
override CS::MulExpr e;
override TMulOp getOp() { any() }
}
/** A division operation. */
class DivExpr extends BinaryOperation {
override CS::DivExpr e;
override TDivOp getOp() { any() }
}
/** A remainder operation. */
class RemExpr extends BinaryOperation {
override CS::RemExpr e;
override TRemOp getOp() { any() }
}
/** A bitwise-and operation. */
class BitwiseAndExpr extends BinaryOperation {
override CS::BitwiseAndExpr e;
override TBitAndOp getOp() { any() }
}
/** A bitwise-or operation. */
class BitwiseOrExpr extends BinaryOperation {
override CS::BitwiseOrExpr e;
override TBitOrOp getOp() { any() }
}
/** A bitwise-xor operation. */
class BitwiseXorExpr extends BinaryOperation {
override CS::BitwiseXorExpr e;
override TBitXorOp getOp() { any() }
}
/** A left-shift operation. */
class LShiftExpr extends BinaryOperation {
override CS::LShiftExpr e;
override TLShiftOp getOp() { any() }
}
/** A right-shift operation. */
class RShiftExpr extends BinaryOperation {
override CS::RShiftExpr e;
override TRShiftOp getOp() { any() }
}
/** A conditional expression. */
class ConditionalExpr extends ExprNode {
override CS::ConditionalExpr e;
/** Gets the "then" expression of this conditional expression. */
ExprNode getTrueExpr() { hasChild(e, e.getThen(), this, result) }
/** Gets the "else" expression of this conditional expression. */
ExprNode getFalseExpr() { hasChild(e, e.getElse(), this, result) }
}
}

View File

@@ -51,7 +51,7 @@ private predicate unknownSign(Expr e) {
or
exists(CastExpr cast, Type fromtyp |
cast = e and
fromtyp = cast.getExpr().getType() and
fromtyp = cast.getSourceType() and
not fromtyp instanceof NumericOrCharType
)
or

View File

@@ -6,11 +6,10 @@ module Private {
private import SsaUtils as SU
private import ConstantUtils as CU
private import RangeUtils as RU
private import semmle.code.csharp.controlflow.Guards as G
private import Sign
import Impl
class Guard = G::Guard;
class Guard = RU::Guard;
class ConstantIntegerExpr = CU::ConstantIntegerExpr;
@@ -18,97 +17,33 @@ module Private {
class SsaPhiNode = CS::Ssa::PhiNode;
class VarAccess = CS::AssignableAccess;
class VarAccess = RU::ExprNode::AssignableAccess;
class FieldAccess = CS::FieldAccess;
class FieldAccess = RU::ExprNode::FieldAccess;
class CharacterLiteral = CS::CharLiteral;
class CharacterLiteral = RU::ExprNode::CharLiteral;
class IntegerLiteral = CS::IntegerLiteral;
class IntegerLiteral = RU::ExprNode::IntegerLiteral;
class LongLiteral = CS::LongLiteral;
class LongLiteral = RU::ExprNode::LongLiteral;
class CastExpr = CS::CastExpr;
class CastExpr = RU::ExprNode::CastExpr;
class Type = CS::Type;
class Expr = CS::Expr;
class Expr = CS::ControlFlow::Nodes::ExprNode;
class VariableUpdate = CS::AssignableDefinition;
class VariableUpdate = CS::Ssa::ExplicitDefinition;
class Field = CS::Field;
class RealLiteral = CS::RealLiteral;
class RealLiteral = RU::ExprNode::RealLiteral;
class DivExpr = CS::DivExpr;
class DivExpr = RU::ExprNode::DivExpr;
/** Class to represent unary operation. */
class UnaryOperation extends Expr {
UnaryOperation() {
this instanceof CS::PreIncrExpr or
this instanceof CS::PreDecrExpr or
this instanceof CS::UnaryMinusExpr or
this instanceof CS::ComplementExpr
}
class UnaryOperation = RU::ExprNode::UnaryOperation;
/** Returns the operand of this expression. */
Expr getOperand() {
result = this.(CS::PreIncrExpr).getOperand() or
result = this.(CS::PreDecrExpr).getOperand() or
result = this.(CS::UnaryMinusExpr).getOperand() or
result = this.(CS::ComplementExpr).getOperand()
}
/** Returns the operation representing this expression. */
TUnarySignOperation getOp() {
this instanceof CS::PreIncrExpr and result = TIncOp()
or
this instanceof CS::PreDecrExpr and result = TDecOp()
or
this instanceof CS::UnaryMinusExpr and result = TNegOp()
or
this instanceof CS::ComplementExpr and result = TBitNotOp()
}
}
/** Class to represent binary operation. */
class BinaryOperation extends CS::BinaryOperation {
BinaryOperation() {
this instanceof CS::AddExpr or
this instanceof CS::SubExpr or
this instanceof CS::MulExpr or
this instanceof CS::DivExpr or
this instanceof CS::RemExpr or
this instanceof CS::BitwiseAndExpr or
this instanceof CS::BitwiseOrExpr or
this instanceof CS::BitwiseXorExpr or
this instanceof CS::LShiftExpr or
this instanceof CS::RShiftExpr
}
/** Returns the operation representing this expression. */
TBinarySignOperation getOp() {
this instanceof CS::AddExpr and result = TAddOp()
or
this instanceof CS::SubExpr and result = TSubOp()
or
this instanceof CS::MulExpr and result = TMulOp()
or
this instanceof CS::DivExpr and result = TDivOp()
or
this instanceof CS::RemExpr and result = TRemOp()
or
this instanceof CS::BitwiseAndExpr and result = TBitAndOp()
or
this instanceof CS::BitwiseOrExpr and result = TBitOrOp()
or
this instanceof CS::BitwiseXorExpr and result = TBitXorOp()
or
this instanceof CS::LShiftExpr and result = TLShiftOp()
or
this instanceof CS::RShiftExpr and result = TRShiftOp()
}
}
class BinaryOperation = RU::ExprNode::BinaryOperation;
predicate ssaRead = SU::ssaRead/2;
@@ -118,21 +53,21 @@ module Private {
private module Impl {
private import csharp
private import SsaUtils
private import RangeUtils
private import ConstantUtils
private import semmle.code.csharp.controlflow.Guards
private import Linq.Helpers
private import Sign
private import SignAnalysisCommon
private import SsaReadPositionCommon
private import semmle.code.csharp.commons.ComparisonTest
private class BooleanValue = AbstractValues::BooleanValue;
private class ExprNode = ControlFlow::Nodes::ExprNode;
/** Gets the character value of expression `e`. */
string getCharValue(Expr e) { result = e.getValue() and e.getType() instanceof CharType }
string getCharValue(ExprNode e) { result = e.getValue() and e.getType() instanceof CharType }
/** Gets the constant `float` value of non-`ConstantIntegerExpr` expressions. */
float getNonIntegerValue(Expr e) {
float getNonIntegerValue(ExprNode e) {
exists(string s |
s = e.getValue() and
result = s.toFloat() and
@@ -144,19 +79,19 @@ private module Impl {
* Holds if `e` is an access to the size of a container (`string`, `Array`,
* `IEnumerable`, or `ICollection`).
*/
predicate containerSizeAccess(Expr e) {
exists(Property p | p = e.(PropertyAccess).getTarget() |
predicate containerSizeAccess(ExprNode e) {
exists(Property p | p = e.getExpr().(PropertyAccess).getTarget() |
propertyOverrides(p, "System.Collections.Generic.IEnumerable<>", "Count") or
propertyOverrides(p, "System.Collections.ICollection", "Count") or
propertyOverrides(p, "System.String", "Length") or
propertyOverrides(p, "System.Array", "Length")
)
or
e instanceof CountCall
e.getExpr() instanceof CountCall
}
/** Holds if `e` is by definition strictly positive. */
predicate positiveExpression(Expr e) { e instanceof SizeofExpr }
predicate positiveExpression(ExprNode e) { e.getExpr() instanceof SizeofExpr }
abstract class NumericOrCharType extends Type { }
@@ -185,43 +120,48 @@ private module Impl {
}
/** Returns the underlying variable update of the explicit SSA variable `v`. */
AssignableDefinition getExplicitSsaAssignment(Ssa::ExplicitDefinition v) {
result = v.getADefinition()
}
Ssa::ExplicitDefinition getExplicitSsaAssignment(Ssa::ExplicitDefinition v) { result = v }
/** Returns the assignment of the variable update `def`. */
Expr getExprFromSsaAssignment(AssignableDefinition def) { result = def.getSource() }
ExprNode getExprFromSsaAssignment(Ssa::ExplicitDefinition def) {
exists(AssignableDefinition adef |
adef = def.getADefinition() and
hasChild(adef.getExpr(), adef.getSource(), def.getControlFlowNode(), result)
)
}
/** Holds if `def` can have any sign. */
predicate explicitSsaDefWithAnySign(AssignableDefinition def) {
not exists(def.getSource()) and
predicate explicitSsaDefWithAnySign(Ssa::ExplicitDefinition def) {
not exists(def.getADefinition().getSource()) and
not def.getElement() instanceof MutatorOperation
}
/** Returns the operand of the operation if `def` is a decrement. */
Expr getDecrementOperand(AssignableDefinition def) {
result = def.getElement().(DecrementOperation).getOperand()
ExprNode getDecrementOperand(Ssa::ExplicitDefinition def) {
hasChild(def.getElement(), def.getElement().(DecrementOperation).getOperand(),
def.getControlFlowNode(), result)
}
/** Returns the operand of the operation if `def` is an increment. */
Expr getIncrementOperand(AssignableDefinition def) {
result = def.getElement().(IncrementOperation).getOperand()
ExprNode getIncrementOperand(Ssa::ExplicitDefinition def) {
hasChild(def.getElement(), def.getElement().(IncrementOperation).getOperand(),
def.getControlFlowNode(), result)
}
/** Gets the variable underlying the implicit SSA variable `v`. */
Declaration getImplicitSsaDeclaration(Ssa::ImplicitDefinition v) {
result = v.getSourceVariable().getAssignable()
/** Gets the variable underlying the implicit SSA variable `def`. */
Declaration getImplicitSsaDeclaration(Ssa::ImplicitDefinition def) {
result = def.getSourceVariable().getAssignable()
}
/** Holds if the variable underlying the implicit SSA variable `v` is not a field. */
predicate nonFieldImplicitSsaDefinition(Ssa::ImplicitDefinition v) {
not getImplicitSsaDeclaration(v) instanceof Field
/** Holds if the variable underlying the implicit SSA variable `def` is not a field. */
predicate nonFieldImplicitSsaDefinition(Ssa::ImplicitDefinition def) {
not getImplicitSsaDeclaration(def) instanceof Field
}
/** Returned an expression that is assigned to `f`. */
Expr getAssignedValueToField(Field f) {
result = f.getAnAssignedValue() or
result = any(AssignOperation a | a.getLValue() = f.getAnAccess())
ExprNode getAssignedValueToField(Field f) {
result.getExpr() in [f.getAnAssignedValue(),
any(AssignOperation a | a.getLValue() = f.getAnAccess())]
}
/** Holds if `f` can have any sign. */
@@ -244,71 +184,78 @@ private module Impl {
/**
* Holds if `e` has type `NumericOrCharType`, but the sign of `e` is unknown.
*/
predicate numericExprWithUnknownSign(Expr e) {
predicate numericExprWithUnknownSign(ExprNode e) {
e.getType() instanceof NumericOrCharType and
not e = getARead(_) and
not e instanceof FieldAccess and
not e instanceof TypeAccess and
not e.getExpr() instanceof FieldAccess and
not e.getExpr() instanceof TypeAccess and
// The expression types that are listed here are the ones handled in `specificSubExprSign`.
// Keep them in sync.
not e instanceof AssignExpr and
not e instanceof AssignOperation and
not e instanceof UnaryPlusExpr and
not e instanceof PostIncrExpr and
not e instanceof PostDecrExpr and
not e instanceof PreIncrExpr and
not e instanceof PreDecrExpr and
not e instanceof UnaryMinusExpr and
not e instanceof ComplementExpr and
not e instanceof AddExpr and
not e instanceof SubExpr and
not e instanceof MulExpr and
not e instanceof DivExpr and
not e instanceof RemExpr and
not e instanceof BitwiseAndExpr and
not e instanceof BitwiseOrExpr and
not e instanceof BitwiseXorExpr and
not e instanceof LShiftExpr and
not e instanceof RShiftExpr and
not e instanceof ConditionalExpr and
not e instanceof RefExpr and
not e instanceof LocalVariableDeclAndInitExpr and
not e instanceof SwitchCaseExpr and
not e instanceof CastExpr and
not e instanceof SwitchExpr and
not e instanceof NullCoalescingExpr
not e.getExpr() instanceof AssignExpr and
not e.getExpr() instanceof AssignOperation and
not e.getExpr() instanceof UnaryPlusExpr and
not e.getExpr() instanceof PostIncrExpr and
not e.getExpr() instanceof PostDecrExpr and
not e.getExpr() instanceof PreIncrExpr and
not e.getExpr() instanceof PreDecrExpr and
not e.getExpr() instanceof UnaryMinusExpr and
not e.getExpr() instanceof ComplementExpr and
not e.getExpr() instanceof AddExpr and
not e.getExpr() instanceof SubExpr and
not e.getExpr() instanceof MulExpr and
not e.getExpr() instanceof DivExpr and
not e.getExpr() instanceof RemExpr and
not e.getExpr() instanceof BitwiseAndExpr and
not e.getExpr() instanceof BitwiseOrExpr and
not e.getExpr() instanceof BitwiseXorExpr and
not e.getExpr() instanceof LShiftExpr and
not e.getExpr() instanceof RShiftExpr and
not e.getExpr() instanceof ConditionalExpr and
not e.getExpr() instanceof RefExpr and
not e.getExpr() instanceof LocalVariableDeclAndInitExpr and
not e.getExpr() instanceof SwitchCaseExpr and
not e.getExpr() instanceof CastExpr and
not e.getExpr() instanceof SwitchExpr and
not e.getExpr() instanceof NullCoalescingExpr
}
/** Returns a sub expression of `e` for expression types where the sign depends on the child. */
Expr getASubExprWithSameSign(Expr e) {
result = e.(AssignExpr).getRValue() or
result = e.(AssignOperation).getExpandedAssignment() or
result = e.(UnaryPlusExpr).getOperand() or
result = e.(PostIncrExpr).getOperand() or
result = e.(PostDecrExpr).getOperand() or
result = e.(ConditionalExpr).getAChild() or
result = e.(NullCoalescingExpr).getAChild() or
result = e.(SwitchExpr).getACase().getBody() or
result = e.(SwitchCaseExpr).getBody() or
result = e.(LocalVariableDeclAndInitExpr).getInitializer() or
result = e.(RefExpr).getExpr() or
result = e.(CastExpr).getExpr()
ExprNode getASubExprWithSameSign(ExprNode e) {
exists(Expr e_, Expr child | hasChild(e_, child, e, result) |
child = e_.(AssignExpr).getRValue() or
child = e_.(UnaryPlusExpr).getOperand() or
child = e_.(PostIncrExpr).getOperand() or
child = e_.(PostDecrExpr).getOperand() or
child = e_.(ConditionalExpr).getAChild() or
child = e_.(NullCoalescingExpr).getAChild() or
child = e_.(SwitchExpr).getACase() or
child = e_.(SwitchCaseExpr).getBody() or
child = e_.(LocalVariableDeclAndInitExpr).getInitializer() or
child = e_.(RefExpr).getExpr() or
child = e_.(CastExpr).getExpr()
)
}
Expr getARead(Ssa::Definition v) { result = v.getARead() }
ExprNode getARead(Ssa::Definition v) { exists(v.getAReadAtNode(result)) }
Field getField(FieldAccess fa) { result = fa.getTarget() }
Field getField(ExprNode fa) { result = fa.getExpr().(FieldAccess).getTarget() }
Expr getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode().getElement() }
ExprNode getAnExpression(SsaReadPositionBlock bb) { result = bb.getBlock().getANode() }
Guard getComparisonGuard(ComparisonExpr ce) { result = ce.getExpr() }
private newtype TComparisonExpr =
MkComparisonExpr(ComparisonTest ct, ExprNode e) { e = ct.getExpr().getAControlFlowNode() }
/** A relational comparison */
class ComparisonExpr extends ComparisonTest {
class ComparisonExpr extends MkComparisonExpr {
private ComparisonTest ct;
private ExprNode e;
private boolean strict;
ComparisonExpr() {
this.getComparisonKind() =
this = MkComparisonExpr(ct, e) and
ct.getComparisonKind() =
any(ComparisonKind ck |
ck.isLessThan() and strict = true
or
@@ -317,13 +264,22 @@ private module Impl {
)
}
/** Gets the underlying expression. */
Expr getExpr() { result = ct.getExpr() }
/** Gets a textual representation of this comparison test. */
string toString() { result = ct.toString() }
/** Gets the location of this comparison test. */
Location getLocation() { result = ct.getLocation() }
/**
* Gets the operand on the "greater" (or "greater-or-equal") side
* of this relational expression, that is, the side that is larger
* if the overall expression evaluates to `true`; for example on
* `x <= 20` this is the `20`, and on `y > 0` it is `y`.
*/
Expr getGreaterOperand() { result = this.getSecondArgument() }
ExprNode getGreaterOperand() { hasChild(ct.getExpr(), ct.getSecondArgument(), e, result) }
/**
* Gets the operand on the "lesser" (or "lesser-or-equal") side
@@ -331,7 +287,7 @@ private module Impl {
* if the overall expression evaluates to `true`; for example on
* `x <= 20` this is `x`, and on `y > 0` it is the `0`.
*/
Expr getLesserOperand() { result = this.getFirstArgument() }
ExprNode getLesserOperand() { hasChild(ct.getExpr(), ct.getFirstArgument(), e, result) }
/** Holds if this comparison is strict, i.e. `<` or `>`. */
predicate isStrict() { strict = true }

View File

@@ -4,15 +4,32 @@
private import csharp
private import Ssa
private import RangeUtils
private import ConstantUtils
private class ExprNode = ControlFlow::Nodes::ExprNode;
/** An SSA variable. */
class SsaVariable extends Definition {
/** Gets a read of this SSA variable. */
ExprNode getAUse() { exists(this.getAReadAtNode(result)) }
}
/** Gets a node that reads `src` via an SSA explicit definition. */
ExprNode getAnExplicitDefinitionRead(ExprNode src) {
exists(ExplicitDefinition def |
exists(def.getAReadAtNode(result)) and
hasChild(def.getElement(), def.getADefinition().getSource(), def.getControlFlowNode(), src)
)
}
/**
* Gets an expression that equals `v - delta`.
*/
Expr ssaRead(Definition v, int delta) {
result = v.getARead() and delta = 0
ExprNode ssaRead(Definition v, int delta) {
exists(v.getAReadAtNode(result)) and delta = 0
or
exists(AddExpr add, int d1, ConstantIntegerExpr c |
exists(ExprNode::AddExpr add, int d1, ConstantIntegerExpr c |
result = add and
delta = d1 - c.getIntValue()
|
@@ -21,22 +38,22 @@ Expr ssaRead(Definition v, int delta) {
add.getRightOperand() = ssaRead(v, d1) and add.getLeftOperand() = c
)
or
exists(SubExpr sub, int d1, ConstantIntegerExpr c |
exists(ExprNode::SubExpr sub, int d1, ConstantIntegerExpr c |
result = sub and
sub.getLeftOperand() = ssaRead(v, d1) and
sub.getRightOperand() = c and
delta = d1 + c.getIntValue()
)
or
v.(ExplicitDefinition).getADefinition().getExpr().(PreIncrExpr) = result and delta = 0
v.(ExplicitDefinition).getControlFlowNode().(ExprNode::PreIncrExpr) = result and delta = 0
or
v.(ExplicitDefinition).getADefinition().getExpr().(PreDecrExpr) = result and delta = 0
v.(ExplicitDefinition).getControlFlowNode().(ExprNode::PreDecrExpr) = result and delta = 0
or
v.(ExplicitDefinition).getADefinition().getExpr().(PostIncrExpr) = result and delta = 1 // x++ === ++x - 1
v.(ExplicitDefinition).getControlFlowNode().(ExprNode::PostIncrExpr) = result and delta = 1 // x++ === ++x - 1
or
v.(ExplicitDefinition).getADefinition().getExpr().(PostDecrExpr) = result and delta = -1 // x-- === --x + 1
v.(ExplicitDefinition).getControlFlowNode().(ExprNode::PostDecrExpr) = result and delta = -1 // x-- === --x + 1
or
v.(ExplicitDefinition).getADefinition().getExpr().(Assignment) = result and delta = 0
v.(ExplicitDefinition).getControlFlowNode().(ExprNode::Assignment) = result and delta = 0
or
result.(AssignExpr).getRValue() = ssaRead(v, delta)
result.(ExprNode::AssignExpr).getRValue() = ssaRead(v, delta)
}