diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll index 10ca946a044..b2058a27114 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/Sign.qll @@ -3,6 +3,25 @@ newtype TSign = TZero() or TPos() +newtype TUnarySignOperation = + TNegOp() or + TIncOp() or + TDecOp() or + TBitNotOp() + +newtype TBinarySignOperation = + TAddOp() or + TSubOp() or + TMulOp() or + TDivOp() or + TRemOp() or + TBitAndOp() or + TBitOrOp() or + TBitXorOp() or + TLShiftOp() or + TRShiftOp() or + TURShiftOp() + /** Class representing expression signs (+, -, 0). */ class Sign extends TSign { /** Gets the string representation of this sign. */ @@ -67,6 +86,12 @@ class Sign extends TSign { this = TNeg() and s = TPos() } + /** + * Gets a possible sign after subtracting an expression with sign `s` from an expression + * that has this sign. + */ + Sign sub(Sign s) { result = add(s.neg()) } + /** * Gets a possible sign after multiplying an expression with sign `s` to an expression * that has this sign. @@ -216,4 +241,40 @@ class Sign extends TSign { or result != TNeg() and this = TPos() and s != TZero() } + + /** Perform `op` on this sign. */ + Sign applyUnaryOp(TUnarySignOperation op) { + op = TIncOp() and result = inc() + or + op = TDecOp() and result = dec() + or + op = TNegOp() and result = neg() + or + op = TBitNotOp() and result = bitnot() + } + + /** Perform `op` on this sign and sign `s`. */ + Sign applyBinaryOp(Sign s, TBinarySignOperation op) { + op = TAddOp() and result = add(s) + or + op = TSubOp() and result = sub(s) + or + op = TMulOp() and result = mul(s) + or + op = TDivOp() and result = div(s) + or + op = TRemOp() and result = rem(s) + or + op = TBitAndOp() and result = bitand(s) + or + op = TBitOrOp() and result = bitor(s) + or + op = TBitXorOp() and result = bitxor(s) + or + op = TLShiftOp() and result = lshift(s) + or + op = TRShiftOp() and result = rshift(s) + or + op = TURShiftOp() and result = urshift(s) + } } diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll index a33f2ae65f3..bf09dbee83f 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll @@ -326,6 +326,36 @@ Sign exprSign(Expr e) { ) } +/** Gets a possible sign for `e` from the signs of its child nodes. */ +Sign specificSubExprSign(Expr e) { + result = exprSign(getASubExpr(e)) + or + e = + any(DivExpr div | + result = exprSign(div.getLeftOperand()) and + result != TZero() and + div.getRightOperand().(RealLiteral).getValue().toFloat() = 0 + ) + or + exists(UnaryExpr unary | unary = e | + result = exprSign(unary.getOperand()).applyUnaryOp(unary.getOp()) + ) + or + exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) | + result = s1.applyBinaryOp(s2, e.(BinaryExpr).getOp()) + ) +} + +pragma[noinline] +private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) { + lhs = binaryOpLhsSign(e) and + rhs = binaryOpRhsSign(e) +} + +Sign binaryOpLhsSign(BinaryOperation e) { result = exprSign(e.getLeftOperand()) } + +Sign binaryOpRhsSign(BinaryOperation e) { result = exprSign(e.getRightOperand()) } + /** * Dummy predicate that holds for any sign. This is added to improve readability * of cases where the sign is unrestricted. diff --git a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll index bae757447da..ef1bff62f40 100644 --- a/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll +++ b/csharp/ql/src/semmle/code/csharp/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -38,6 +38,12 @@ module Private { class Field = CS::Field; + class RealLiteral = CS::RealLiteral; + + class DivExpr = CS::DivExpr; + + class BinaryOperation = CS::BinaryOperation; + predicate ssaRead = SU::ssaRead/2; } @@ -196,80 +202,88 @@ private module Impl { not e instanceof NullCoalescingExpr } - /** Gets a possible sign for `e` from the signs of its child nodes. */ - Sign specificSubExprSign(Expr e) { - // The expression types that are handled here should be excluded in `unknownIntegerAccess`. - // Keep them in sync. - result = exprSign(e.(AssignExpr).getRValue()) - or - result = exprSign(e.(AssignOperation).getExpandedAssignment()) - or - result = exprSign(e.(UnaryPlusExpr).getOperand()) - or - result = exprSign(e.(PostIncrExpr).getOperand()) - or - result = exprSign(e.(PostDecrExpr).getOperand()) - or - result = exprSign(e.(PreIncrExpr).getOperand()).inc() - or - result = exprSign(e.(PreDecrExpr).getOperand()).dec() - or - result = exprSign(e.(UnaryMinusExpr).getOperand()).neg() - or - result = exprSign(e.(ComplementExpr).getOperand()).bitnot() - or - e = - any(DivExpr div | - result = exprSign(div.getLeftOperand()) and - result != TZero() and - div.getRightOperand().(RealLiteral).getValue().toFloat() = 0 - ) - or - exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) | - e instanceof AddExpr and result = s1.add(s2) - or - e instanceof SubExpr and result = s1.add(s2.neg()) - or - e instanceof MulExpr and result = s1.mul(s2) - or - e instanceof DivExpr and result = s1.div(s2) - or - e instanceof RemExpr and result = s1.rem(s2) - or - e instanceof BitwiseAndExpr and result = s1.bitand(s2) - or - e instanceof BitwiseOrExpr and result = s1.bitor(s2) - or - e instanceof BitwiseXorExpr and result = s1.bitxor(s2) - or - e instanceof LShiftExpr and result = s1.lshift(s2) - or - e instanceof RShiftExpr and result = s1.rshift(s2) - ) - or - result = exprSign(e.(ConditionalExpr).getAChild()) - or - result = exprSign(e.(NullCoalescingExpr).getAChild()) - or - result = exprSign(e.(SwitchExpr).getACase().getBody()) - or - result = exprSign(e.(CastExpr).getExpr()) - or - result = exprSign(e.(SwitchCaseExpr).getBody()) - or - result = exprSign(e.(LocalVariableDeclAndInitExpr).getInitializer()) - or - result = exprSign(e.(RefExpr).getExpr()) + /** Returns a sub expression of `e` for expression types where the sign depends on the child. */ + Expr getASubExpr(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() } - private Sign binaryOpLhsSign(BinaryOperation e) { result = exprSign(e.getLeftOperand()) } + /** Class to represent unary expressions. */ + class UnaryExpr extends Expr { + UnaryExpr() { + this instanceof PreIncrExpr or + this instanceof PreDecrExpr or + this instanceof UnaryMinusExpr or + this instanceof ComplementExpr + } - private Sign binaryOpRhsSign(BinaryOperation e) { result = exprSign(e.getRightOperand()) } + /** Returns the operand of this expression. */ + Expr getOperand() { + result = this.(PreIncrExpr).getOperand() or + result = this.(PreDecrExpr).getOperand() or + result = this.(UnaryMinusExpr).getOperand() or + result = this.(ComplementExpr).getOperand() + } - pragma[noinline] - private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) { - lhs = binaryOpLhsSign(e) and - rhs = binaryOpRhsSign(e) + /** Returns the operation representing this expression. */ + TUnarySignOperation getOp() { + this instanceof PreIncrExpr and result = TIncOp() + or + this instanceof PreDecrExpr and result = TDecOp() + or + this instanceof UnaryMinusExpr and result = TNegOp() + or + this instanceof ComplementExpr and result = TBitNotOp() + } + } + + /** Class to represent binary expressions. */ + class BinaryExpr extends Expr { + BinaryExpr() { + this instanceof AddExpr or + this instanceof SubExpr or + this instanceof MulExpr or + this instanceof DivExpr or + this instanceof RemExpr or + this instanceof BitwiseAndExpr or + this instanceof BitwiseOrExpr or + this instanceof BitwiseXorExpr or + this instanceof LShiftExpr or + this instanceof RShiftExpr + } + + /** Returns the operation representing this expression. */ + TBinarySignOperation getOp() { + this instanceof AddExpr and result = TAddOp() + or + this instanceof SubExpr and result = TSubOp() + or + this instanceof MulExpr and result = TMulOp() + or + this instanceof DivExpr and result = TDivOp() + or + this instanceof RemExpr and result = TRemOp() + or + this instanceof BitwiseAndExpr and result = TBitAndOp() + or + this instanceof BitwiseOrExpr and result = TBitOrOp() + or + this instanceof BitwiseXorExpr and result = TBitXorOp() + or + this instanceof LShiftExpr and result = TLShiftOp() + or + this instanceof RShiftExpr and result = TRShiftOp() + } } Expr getARead(Ssa::Definition v) { result = v.getARead() } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll index 10ca946a044..b2058a27114 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/Sign.qll @@ -3,6 +3,25 @@ newtype TSign = TZero() or TPos() +newtype TUnarySignOperation = + TNegOp() or + TIncOp() or + TDecOp() or + TBitNotOp() + +newtype TBinarySignOperation = + TAddOp() or + TSubOp() or + TMulOp() or + TDivOp() or + TRemOp() or + TBitAndOp() or + TBitOrOp() or + TBitXorOp() or + TLShiftOp() or + TRShiftOp() or + TURShiftOp() + /** Class representing expression signs (+, -, 0). */ class Sign extends TSign { /** Gets the string representation of this sign. */ @@ -67,6 +86,12 @@ class Sign extends TSign { this = TNeg() and s = TPos() } + /** + * Gets a possible sign after subtracting an expression with sign `s` from an expression + * that has this sign. + */ + Sign sub(Sign s) { result = add(s.neg()) } + /** * Gets a possible sign after multiplying an expression with sign `s` to an expression * that has this sign. @@ -216,4 +241,40 @@ class Sign extends TSign { or result != TNeg() and this = TPos() and s != TZero() } + + /** Perform `op` on this sign. */ + Sign applyUnaryOp(TUnarySignOperation op) { + op = TIncOp() and result = inc() + or + op = TDecOp() and result = dec() + or + op = TNegOp() and result = neg() + or + op = TBitNotOp() and result = bitnot() + } + + /** Perform `op` on this sign and sign `s`. */ + Sign applyBinaryOp(Sign s, TBinarySignOperation op) { + op = TAddOp() and result = add(s) + or + op = TSubOp() and result = sub(s) + or + op = TMulOp() and result = mul(s) + or + op = TDivOp() and result = div(s) + or + op = TRemOp() and result = rem(s) + or + op = TBitAndOp() and result = bitand(s) + or + op = TBitOrOp() and result = bitor(s) + or + op = TBitXorOp() and result = bitxor(s) + or + op = TLShiftOp() and result = lshift(s) + or + op = TRShiftOp() and result = rshift(s) + or + op = TURShiftOp() and result = urshift(s) + } } diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll index a33f2ae65f3..bf09dbee83f 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisCommon.qll @@ -326,6 +326,36 @@ Sign exprSign(Expr e) { ) } +/** Gets a possible sign for `e` from the signs of its child nodes. */ +Sign specificSubExprSign(Expr e) { + result = exprSign(getASubExpr(e)) + or + e = + any(DivExpr div | + result = exprSign(div.getLeftOperand()) and + result != TZero() and + div.getRightOperand().(RealLiteral).getValue().toFloat() = 0 + ) + or + exists(UnaryExpr unary | unary = e | + result = exprSign(unary.getOperand()).applyUnaryOp(unary.getOp()) + ) + or + exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) | + result = s1.applyBinaryOp(s2, e.(BinaryExpr).getOp()) + ) +} + +pragma[noinline] +private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) { + lhs = binaryOpLhsSign(e) and + rhs = binaryOpRhsSign(e) +} + +Sign binaryOpLhsSign(BinaryOperation e) { result = exprSign(e.getLeftOperand()) } + +Sign binaryOpRhsSign(BinaryOperation e) { result = exprSign(e.getRightOperand()) } + /** * Dummy predicate that holds for any sign. This is added to improve readability * of cases where the sign is unrestricted. diff --git a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll index 616433a4502..6f2c1026ea6 100644 --- a/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll +++ b/java/ql/src/semmle/code/java/dataflow/internal/rangeanalysis/SignAnalysisSpecific.qll @@ -6,6 +6,7 @@ module Private { private import semmle.code.java.dataflow.SSA as Ssa private import semmle.code.java.controlflow.Guards as G private import java as J + private import Sign import Impl class ConstantIntegerExpr = RU::ConstantIntegerExpr; @@ -42,6 +43,135 @@ module Private { class Field = J::Field; + class DivExpr = J::DivExpr; + + class BinaryOperation extends J::Expr { + BinaryOperation() { + this instanceof J::BinaryExpr or + this instanceof J::AssignOp + } + + Expr getLeftOperand() { + result = this.(J::BinaryExpr).getLeftOperand() or result = this.(J::AssignOp).getDest() + } + + Expr getRightOperand() { + result = this.(J::BinaryExpr).getRightOperand() or result = this.(J::AssignOp).getRhs() + } + } + + /** Class to represent float and double literals. */ + class RealLiteral extends J::Literal { + RealLiteral() { + this instanceof J::FloatingPointLiteral or + this instanceof J::DoubleLiteral + } + } + + /** Class to represent unary expressions. */ + class UnaryExpr extends J::Expr { + UnaryExpr() { + this instanceof J::PreIncExpr or + this instanceof J::PreDecExpr or + this instanceof J::MinusExpr or + this instanceof J::BitNotExpr + } + + /** Returns the operand of this expression. */ + Expr getOperand() { + result = this.(J::PreIncExpr).getExpr() or + result = this.(J::PreDecExpr).getExpr() or + result = this.(J::MinusExpr).getExpr() or + result = this.(J::BitNotExpr).getExpr() + } + + /** Returns the operation representing this expression. */ + TUnarySignOperation getOp() { + this instanceof J::PreIncExpr and result = TIncOp() + or + this instanceof J::PreDecExpr and result = TDecOp() + or + this instanceof J::MinusExpr and result = TNegOp() + or + this instanceof J::BitNotExpr and result = TBitNotOp() + } + } + + /** Class to represent binary expressions. */ + class BinaryExpr extends J::Expr { + BinaryExpr() { + this instanceof J::AddExpr or + this instanceof J::AssignAddExpr or + this instanceof J::SubExpr or + this instanceof J::AssignSubExpr or + this instanceof J::MulExpr or + this instanceof J::AssignMulExpr or + this instanceof J::DivExpr or + this instanceof J::AssignDivExpr or + this instanceof J::RemExpr or + this instanceof J::AssignRemExpr or + this instanceof J::AndBitwiseExpr or + this instanceof J::AssignAndExpr or + this instanceof J::OrBitwiseExpr or + this instanceof J::AssignOrExpr or + this instanceof J::XorBitwiseExpr or + this instanceof J::AssignXorExpr or + this instanceof J::LShiftExpr or + this instanceof J::AssignLShiftExpr or + this instanceof J::RShiftExpr or + this instanceof J::AssignRShiftExpr or + this instanceof J::URShiftExpr or + this instanceof J::AssignURShiftExpr + } + + /** Returns the operation representing this expression. */ + TBinarySignOperation getOp() { + this instanceof J::AddExpr and result = TAddOp() + or + this instanceof J::AssignAddExpr and result = TAddOp() + or + this instanceof J::SubExpr and result = TSubOp() + or + this instanceof J::AssignSubExpr and result = TSubOp() + or + this instanceof J::MulExpr and result = TMulOp() + or + this instanceof J::AssignMulExpr and result = TMulOp() + or + this instanceof J::DivExpr and result = TDivOp() + or + this instanceof J::AssignDivExpr and result = TDivOp() + or + this instanceof J::RemExpr and result = TRemOp() + or + this instanceof J::AssignRemExpr and result = TRemOp() + or + this instanceof J::AndBitwiseExpr and result = TBitAndOp() + or + this instanceof J::AssignAndExpr and result = TBitAndOp() + or + this instanceof J::OrBitwiseExpr and result = TBitOrOp() + or + this instanceof J::AssignOrExpr and result = TBitOrOp() + or + this instanceof J::XorBitwiseExpr and result = TBitXorOp() + or + this instanceof J::AssignXorExpr and result = TBitXorOp() + or + this instanceof J::LShiftExpr and result = TLShiftOp() + or + this instanceof J::AssignLShiftExpr and result = TLShiftOp() + or + this instanceof J::RShiftExpr and result = TRShiftOp() + or + this instanceof J::AssignRShiftExpr and result = TRShiftOp() + or + this instanceof J::URShiftExpr and result = TURShiftOp() + or + this instanceof J::AssignURShiftExpr and result = TURShiftOp() + } + } + predicate ssaRead = RU::ssaRead/2; predicate guardControlsSsaRead = RU::guardControlsSsaRead/3; @@ -169,87 +299,14 @@ private module Impl { else anySign(result) } - /** Gets a possible sign for `e` from the signs of its child nodes. */ - Sign specificSubExprSign(Expr e) { - result = exprSign(e.(AssignExpr).getSource()) - or - result = exprSign(e.(PlusExpr).getExpr()) - or - result = exprSign(e.(PostIncExpr).getExpr()) - or - result = exprSign(e.(PostDecExpr).getExpr()) - or - result = exprSign(e.(PreIncExpr).getExpr()).inc() - or - result = exprSign(e.(PreDecExpr).getExpr()).dec() - or - result = exprSign(e.(MinusExpr).getExpr()).neg() - or - result = exprSign(e.(BitNotExpr).getExpr()).bitnot() - or - exists(DivExpr div | - div = e and - result = exprSign(div.getLeftOperand()) and - result != TZero() - | - div.getRightOperand().(FloatingPointLiteral).getValue().toFloat() = 0 or - div.getRightOperand().(DoubleLiteral).getValue().toFloat() = 0 - ) - or - exists(Sign s1, Sign s2 | binaryOpSigns(e, s1, s2) | - (e instanceof AssignAddExpr or e instanceof AddExpr) and - result = s1.add(s2) - or - (e instanceof AssignSubExpr or e instanceof SubExpr) and - result = s1.add(s2.neg()) - or - (e instanceof AssignMulExpr or e instanceof MulExpr) and - result = s1.mul(s2) - or - (e instanceof AssignDivExpr or e instanceof DivExpr) and - result = s1.div(s2) - or - (e instanceof AssignRemExpr or e instanceof RemExpr) and - result = s1.rem(s2) - or - (e instanceof AssignAndExpr or e instanceof AndBitwiseExpr) and - result = s1.bitand(s2) - or - (e instanceof AssignOrExpr or e instanceof OrBitwiseExpr) and - result = s1.bitor(s2) - or - (e instanceof AssignXorExpr or e instanceof XorBitwiseExpr) and - result = s1.bitxor(s2) - or - (e instanceof AssignLShiftExpr or e instanceof LShiftExpr) and - result = s1.lshift(s2) - or - (e instanceof AssignRShiftExpr or e instanceof RShiftExpr) and - result = s1.rshift(s2) - or - (e instanceof AssignURShiftExpr or e instanceof URShiftExpr) and - result = s1.urshift(s2) - ) - or - result = exprSign(e.(ChooseExpr).getAResultExpr()) - or - result = exprSign(e.(CastExpr).getExpr()) - } - - private Sign binaryOpLhsSign(Expr e) { - result = exprSign(e.(BinaryExpr).getLeftOperand()) or - result = exprSign(e.(AssignOp).getDest()) - } - - private Sign binaryOpRhsSign(Expr e) { - result = exprSign(e.(BinaryExpr).getRightOperand()) or - result = exprSign(e.(AssignOp).getRhs()) - } - - pragma[noinline] - private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) { - lhs = binaryOpLhsSign(e) and - rhs = binaryOpRhsSign(e) + /** Returns a sub expression of `e` for expression types where the sign depends on the child. */ + Expr getASubExpr(Expr e) { + result = e.(AssignExpr).getSource() or + result = e.(PlusExpr).getExpr() or + result = e.(PostIncExpr).getExpr() or + result = e.(PostDecExpr).getExpr() or + result = e.(ChooseExpr).getAResultExpr() or + result = e.(CastExpr).getExpr() } Expr getARead(SsaVariable v) { result = v.getAUse() }