Merge pull request #4362 from tamasvajk/feature/sign-analysis-cleanup

Sign analysis cleanup
This commit is contained in:
Anders Schack-Mulligen
2020-10-08 09:10:04 +02:00
committed by GitHub
7 changed files with 682 additions and 279 deletions

View File

@@ -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)
}
}

View File

@@ -11,7 +11,7 @@ private import SsaReadPositionCommon
private import Sign
/** Gets the sign of `e` if this can be directly determined. */
Sign certainExprSign(Expr e) {
private Sign certainExprSign(Expr e) {
exists(int i | e.(ConstantIntegerExpr).getIntValue() = i |
i < 0 and result = TNeg()
or
@@ -42,7 +42,7 @@ Sign certainExprSign(Expr e) {
}
/** Holds if the sign of `e` is too complicated to determine. */
predicate unknownSign(Expr e) {
private predicate unknownSign(Expr e) {
not exists(certainExprSign(e)) and
(
exists(IntegerLiteral lit | lit = e and not exists(lit.getValue().toInt()))
@@ -55,7 +55,7 @@ predicate unknownSign(Expr e) {
not fromtyp instanceof NumericOrCharType
)
or
unknownIntegerAccess(e)
numericExprWithUnknownSign(e)
)
}
@@ -185,29 +185,32 @@ private predicate hasGuard(SsaVariable v, SsaReadPosition pos, Sign s) {
s = TZero() and zeroBound(_, v, pos)
}
/**
* Gets a possible sign of `v` at `pos` based on its definition, where the sign
* might be ruled out by a guard.
*/
pragma[noinline]
private Sign guardedSsaSign(SsaVariable v, SsaReadPosition pos) {
// SSA variable can have sign `result`
result = ssaDefSign(v) and
pos.hasReadOfVar(v) and
// there are guards at this position on `v` that might restrict it to be sign `result`.
// (So we need to check if they are satisfied)
hasGuard(v, pos, result)
}
/**
* Gets a possible sign of `v` at `pos` based on its definition, where no guard
* can rule it out.
*/
pragma[noinline]
private Sign unguardedSsaSign(SsaVariable v, SsaReadPosition pos) {
// SSA variable can have sign `result`
result = ssaDefSign(v) and
pos.hasReadOfVar(v) and
// there's no guard at this position on `v` that might restrict it to be sign `result`.
not hasGuard(v, pos, result)
}
/**
* Gets the sign of `v` at read position `pos`, when there's at least one guard
* on `v` at position `pos`. Each bound corresponding to a given sign must be met
* in order for `v` to be of that sign.
* Gets a possible sign of `v` at read position `pos`, where a guard could have
* ruled out the sign but does not.
* This does not check that the definition of `v` also allows the sign.
*/
private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) {
result = TPos() and
@@ -221,7 +224,7 @@ private Sign guardedSsaSignOk(SsaVariable v, SsaReadPosition pos) {
}
/** Gets a possible sign for `v` at `pos`. */
Sign ssaSign(SsaVariable v, SsaReadPosition pos) {
private Sign ssaSign(SsaVariable v, SsaReadPosition pos) {
result = unguardedSsaSign(v, pos)
or
result = guardedSsaSign(v, pos) and
@@ -230,7 +233,7 @@ Sign ssaSign(SsaVariable v, SsaReadPosition pos) {
/** Gets a possible sign for `v`. */
pragma[nomagic]
Sign ssaDefSign(SsaVariable v) {
private Sign ssaDefSign(SsaVariable v) {
result = explicitSsaDefSign(v)
or
result = implicitSsaDefSign(v)
@@ -242,6 +245,40 @@ Sign ssaDefSign(SsaVariable v) {
)
}
/** Returns the sign of explicit SSA definition `v`. */
private Sign explicitSsaDefSign(SsaVariable v) {
exists(VariableUpdate def | def = getExplicitSsaAssignment(v) |
result = exprSign(getExprFromSsaAssignment(def))
or
anySign(result) and explicitSsaDefWithAnySign(def)
or
result = exprSign(getIncrementOperand(def)).inc()
or
result = exprSign(getDecrementOperand(def)).dec()
)
}
/** Returns the sign of implicit SSA definition `v`. */
private Sign implicitSsaDefSign(SsaVariable v) {
result = fieldSign(getImplicitSsaDeclaration(v))
or
anySign(result) and nonFieldImplicitSsaDefinition(v)
}
/** Gets a possible sign for `f`. */
private Sign fieldSign(Field f) {
if not fieldWithUnknownSign(f)
then
result = exprSign(getAssignedValueToField(f))
or
fieldIncrementOperationOperand(f) and result = fieldSign(f).inc()
or
fieldDecrementOperationOperand(f) and result = fieldSign(f).dec()
or
result = specificFieldSign(f)
else anySign(result)
}
/** Gets a possible sign for `e`. */
cached
Sign exprSign(Expr e) {
@@ -250,18 +287,23 @@ Sign exprSign(Expr e) {
or
not exists(certainExprSign(e)) and
(
unknownSign(e)
anySign(s) and unknownSign(e)
or
exists(SsaVariable v | getARead(v) = e | s = ssaVariableSign(v, e))
exists(SsaVariable v | getARead(v) = e |
s = ssaSign(v, any(SsaReadPositionBlock bb | getAnExpression(bb) = e))
or
not exists(SsaReadPositionBlock bb | getAnExpression(bb) = e) and
s = ssaDefSign(v)
)
or
e =
any(VarAccess access |
not exists(SsaVariable v | getARead(v) = access) and
(
s = fieldSign(getField(access.(FieldAccess))) or
not access instanceof FieldAccess
)
exists(VarAccess access | access = e |
not exists(SsaVariable v | getARead(v) = access) and
(
s = fieldSign(getField(access.(FieldAccess)))
or
anySign(s) and not access instanceof FieldAccess
)
)
or
s = specificSubExprSign(e)
)
@@ -272,6 +314,41 @@ Sign exprSign(Expr e) {
)
}
/** Gets a possible sign for `e` from the signs of its child nodes. */
private Sign specificSubExprSign(Expr e) {
result = exprSign(getASubExprWithSameSign(e))
or
exists(DivExpr div | div = e |
result = exprSign(div.getLeftOperand()) and
result != TZero() and
div.getRightOperand().(RealLiteral).getValue().toFloat() = 0
)
or
exists(UnaryOperation 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.(BinaryOperation).getOp())
)
}
pragma[noinline]
private predicate binaryOpSigns(Expr e, Sign lhs, Sign rhs) {
lhs = binaryOpLhsSign(e) and
rhs = binaryOpRhsSign(e)
}
private Sign binaryOpLhsSign(BinaryOperation e) { result = exprSign(e.getLeftOperand()) }
private 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.
*/
predicate anySign(Sign s) { any() }
/** Holds if `e` can be positive and cannot be negative. */
predicate positive(Expr e) {
exprSign(e) = TPos() and

View File

@@ -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;
@@ -36,6 +37,132 @@ module Private {
class NumericOrCharType = J::NumericOrCharType;
class VariableUpdate = J::VariableUpdate;
class Field = J::Field;
class DivExpr = J::DivExpr;
/** 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 operation. */
class UnaryOperation extends J::Expr {
UnaryOperation() {
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 operation. */
class BinaryOperation extends J::Expr {
BinaryOperation() {
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()
}
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()
}
}
predicate ssaRead = RU::ssaRead/2;
predicate guardControlsSsaRead = RU::guardControlsSsaRead/3;
@@ -55,14 +182,20 @@ private module Impl {
class UnsignedNumericType = CharacterType;
/** Gets the character value of expression `e`. */
string getCharValue(Expr e) { result = e.(CharacterLiteral).getValue() }
/** Gets the constant `float` value of non-`ConstantIntegerExpr` expressions. */
float getNonIntegerValue(Expr e) {
result = e.(LongLiteral).getValue().toFloat() or
result = e.(FloatingPointLiteral).getValue().toFloat() or
result = e.(DoubleLiteral).getValue().toFloat()
}
string getCharValue(Expr e) { result = e.(CharacterLiteral).getValue() }
/**
* Holds if `e` is an access to the size of a container (`string`, `Map`, or
* `Collection`).
*/
predicate containerSizeAccess(Expr e) {
e.(MethodAccess).getMethod() instanceof StringLengthMethod
or
@@ -71,9 +204,15 @@ private module Impl {
e.(MethodAccess).getMethod() instanceof MapSizeMethod
}
/** Holds if `e` is by definition strictly positive. */
predicate positiveExpression(Expr e) { none() }
predicate unknownIntegerAccess(Expr e) {
/**
* Holds if `e` has type `NumericOrCharType`, but the sign of `e` is unknown.
*/
predicate numericExprWithUnknownSign(Expr e) {
// The expression types handled in the predicate complements the expression
// types handled in `specificSubExprSign`.
e instanceof ArrayAccess and e.getType() instanceof NumericOrCharType
or
e instanceof MethodAccess and e.getType() instanceof NumericOrCharType
@@ -81,56 +220,69 @@ private module Impl {
e instanceof ClassInstanceExpr and e.getType() instanceof NumericOrCharType
}
Sign explicitSsaDefSign(SsaVariable v) {
exists(VariableUpdate def | def = v.(SsaExplicitUpdate).getDefiningExpr() |
result = exprSign(def.(VariableAssign).getSource())
or
exists(EnhancedForStmt for | def = for.getVariable())
or
result = exprSign(def.(PostIncExpr).getExpr()).inc()
or
result = exprSign(def.(PreIncExpr).getExpr()).inc()
or
result = exprSign(def.(PostDecExpr).getExpr()).dec()
or
result = exprSign(def.(PreDecExpr).getExpr()).dec()
or
exists(AssignOp a | a = def and result = exprSign(a))
)
/** Returns the underlying variable update of the explicit SSA variable `v`. */
VariableUpdate getExplicitSsaAssignment(SsaVariable v) {
result = v.(SsaExplicitUpdate).getDefiningExpr()
}
Sign implicitSsaDefSign(SsaVariable v) {
result = fieldSign(v.(SsaImplicitUpdate).getSourceVariable().getVariable())
/** Returns the assignment of the variable update `def`. */
Expr getExprFromSsaAssignment(VariableUpdate def) {
result = def.(VariableAssign).getSource()
or
result = fieldSign(v.(SsaImplicitInit).getSourceVariable().getVariable())
or
exists(Parameter p | v.(SsaImplicitInit).isParameterDefinition(p))
exists(AssignOp a | a = def and result = a)
}
pragma[inline]
Sign ssaVariableSign(SsaVariable v, Expr e) {
result = ssaSign(v, any(SsaReadPositionBlock bb | getAnExpression(bb) = e))
or
not exists(SsaReadPositionBlock bb | getAnExpression(bb) = e) and
result = ssaDefSign(v)
/** Holds if `def` can have any sign. */
predicate explicitSsaDefWithAnySign(VariableUpdate def) {
exists(EnhancedForStmt for | def = for.getVariable())
}
/** Gets a possible sign for `f`. */
Sign fieldSign(Field f) {
result = exprSign(f.getAnAssignedValue())
or
exists(PostIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc())
or
exists(PreIncExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).inc())
or
exists(PostDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec())
or
exists(PreDecExpr inc | inc.getExpr() = f.getAnAccess() and result = fieldSign(f).dec())
or
exists(AssignOp a | a.getDest() = f.getAnAccess() | result = exprSign(a))
or
/** Returns the operand of the operation if `def` is a decrement. */
Expr getDecrementOperand(Element e) {
result = e.(PostDecExpr).getExpr() or result = e.(PreDecExpr).getExpr()
}
/** Returns the operand of the operation if `def` is an increment. */
Expr getIncrementOperand(Element e) {
result = e.(PostIncExpr).getExpr() or result = e.(PreIncExpr).getExpr()
}
/** Gets the variable underlying the implicit SSA variable `v`. */
Variable getImplicitSsaDeclaration(SsaVariable v) {
result = v.(SsaImplicitUpdate).getSourceVariable().getVariable() or
result = v.(SsaImplicitInit).getSourceVariable().getVariable()
}
/** Holds if the variable underlying the implicit SSA variable `v` is not a field. */
predicate nonFieldImplicitSsaDefinition(SsaImplicitInit v) {
exists(Parameter p | v.isParameterDefinition(p))
}
/** Returned an expression that is assigned to `f`. */
Expr getAssignedValueToField(Field f) {
result = f.getAnAssignedValue() or
result = any(AssignOp a | a.getDest() = f.getAnAccess())
}
/** Holds if `f` can have any sign. */
predicate fieldWithUnknownSign(Field f) {
exists(ReflectiveFieldAccess rfa | rfa.inferAccessedField() = f)
or
}
/** Holds if `f` is accessed in an increment operation. */
predicate fieldIncrementOperationOperand(Field f) {
any(PostIncExpr inc).getExpr() = f.getAnAccess() or
any(PreIncExpr inc).getExpr() = f.getAnAccess()
}
/** Holds if `f` is accessed in a decrement operation. */
predicate fieldDecrementOperationOperand(Field f) {
any(PostDecExpr dec).getExpr() = f.getAnAccess() or
any(PreDecExpr dec).getExpr() = f.getAnAccess()
}
/** Returns possible signs of `f` based on the declaration. */
Sign specificFieldSign(Field f) {
if f.fromSource()
then not exists(f.getInitializer()) and result = TZero()
else
@@ -142,89 +294,17 @@ private module Impl {
else
if f.hasName("MIN_VALUE")
then result = TNeg()
else any()
else anySign(result)
}
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 getASubExprWithSameSign(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() }

View File

@@ -18,4 +18,5 @@ string getASignString(Expr e) {
}
from Expr e
where e.getEnclosingCallable().fromSource()
select e, strictconcat(string s | s = getASignString(e) | s, " ")