Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions java/ql/lib/semmle/code/java/Expr.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1808,6 +1808,52 @@ class VariableAssign extends VariableUpdate {
}
}

private newtype TVariableWrite =
TParamInit(Parameter p) or
TVarWriteExpr(VariableUpdate u)

/**
* A write to a variable. This is either a local variable declaration,
* including parameter declarations, or an update to a variable.
*/
class VariableWrite extends TVariableWrite {
/** Gets the expression representing this write, if any. */
Expr asExpr() { this = TVarWriteExpr(result) }

/**
* Gets the expression with the value being written, if any.
*
* This can be the same expression as returned by `asExpr()`, which is the
* case for, for example, `++x` and `x += e`. For simple assignments like
* `x = e`, `asExpr()` gets the whole assignment expression while
* `getValue()` gets the right-hand side `e`. Post-crement operations like
* `x++` do not have an expression with the value being written.
*/
Expr getValue() {
this.asExpr().(VariableAssign).getSource() = result or
this.asExpr().(AssignOp) = result or
this.asExpr().(PreIncExpr) = result or
this.asExpr().(PreDecExpr) = result
}

/** Holds if this write is an initialization of parameter `p`. */
predicate isParameterInit(Parameter p) { this = TParamInit(p) }

/** Gets a textual representation of this write. */
string toString() {
exists(Parameter p | this = TParamInit(p) and result = p.toString())
or
result = this.asExpr().toString()
}

/** Gets the location of this write. */
Location getLocation() {
exists(Parameter p | this = TParamInit(p) and result = p.getLocation())
or
result = this.asExpr().getLocation()
}
}

/** A type literal. For example, `String.class`. */
class TypeLiteral extends Expr, @typeliteral {
/** Gets the access to the type whose class is accessed. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ module;

import java
private import codeql.controlflow.ControlFlowReachability
private import semmle.code.java.dataflow.SSA as SSA
private import semmle.code.java.dataflow.SSA
private import semmle.code.java.controlflow.Guards as Guards

private module ControlFlowInput implements InputSig<Location, ControlFlowNode, BasicBlock> {
private import java as J
import Ssa

AstNode getEnclosingAstNode(ControlFlowNode node) { node.getAstNode() = result }

Expand All @@ -27,23 +28,6 @@ private module ControlFlowInput implements InputSig<Location, ControlFlowNode, B

class Expr = J::Expr;

class SourceVariable = SSA::SsaSourceVariable;

class SsaDefinition = SSA::SsaVariable;

class SsaExplicitWrite extends SsaDefinition instanceof SSA::SsaExplicitUpdate {
Expr getValue() {
super.getDefiningExpr().(VariableAssign).getSource() = result or
super.getDefiningExpr().(AssignOp) = result
}
}

class SsaPhiDefinition = SSA::SsaPhiNode;

class SsaUncertainWrite extends SsaDefinition instanceof SSA::SsaUncertainImplicitUpdate {
SsaDefinition getPriorDefinition() { result = super.getPriorDef() }
}

class GuardValue = Guards::GuardValue;

predicate ssaControlsBranchEdge(SsaDefinition def, BasicBlock bb1, BasicBlock bb2, GuardValue v) {
Expand Down
58 changes: 7 additions & 51 deletions java/ql/lib/semmle/code/java/controlflow/Guards.qll
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pre

private module GuardsInput implements SharedGuards::InputSig<Location, ControlFlowNode, BasicBlock> {
private import java as J
private import semmle.code.java.dataflow.internal.BaseSSA
private import semmle.code.java.dataflow.internal.BaseSSA as Base
private import semmle.code.java.dataflow.NullGuards as NullGuards

class NormalExitNode = ControlFlow::NormalExitNode;
Expand Down Expand Up @@ -211,10 +211,10 @@ private module GuardsInput implements SharedGuards::InputSig<Location, ControlFl
f.getInitializer() = NullGuards::baseNotNullExpr()
)
or
exists(CatchClause cc, LocalVariableDeclExpr decl, BaseSsaUpdate v |
exists(CatchClause cc, LocalVariableDeclExpr decl, Base::SsaExplicitWrite v |
decl = cc.getVariable() and
decl = v.getDefiningExpr() and
this = v.getAUse()
this = v.getARead()
)
}
}
Expand Down Expand Up @@ -407,61 +407,17 @@ private module LogicInputCommon {
}

private module LogicInput_v1 implements GuardsImpl::LogicInputSig {
private import semmle.code.java.dataflow.internal.BaseSSA

final private class FinalBaseSsaVariable = BaseSsaVariable;

class SsaDefinition extends FinalBaseSsaVariable {
GuardsInput::Expr getARead() { result = this.getAUse() }
}

class SsaExplicitWrite extends SsaDefinition instanceof BaseSsaUpdate {
GuardsInput::Expr getValue() {
super.getDefiningExpr().(VariableAssign).getSource() = result or
super.getDefiningExpr().(AssignOp) = result
}
}

class SsaPhiDefinition extends SsaDefinition instanceof BaseSsaPhiNode {
predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb) {
super.hasInputFromBlock(inp, bb)
}
}

class SsaParameterInit extends SsaDefinition instanceof BaseSsaImplicitInit {
Parameter getParameter() { super.isParameterDefinition(result) }
}
private import semmle.code.java.dataflow.internal.BaseSSA as Base
import Base::Ssa

predicate additionalNullCheck = LogicInputCommon::additionalNullCheck/4;

predicate additionalImpliesStep = LogicInputCommon::additionalImpliesStep/4;
}

private module LogicInput_v2 implements GuardsImpl::LogicInputSig {
private import semmle.code.java.dataflow.SSA as SSA

final private class FinalSsaVariable = SSA::SsaVariable;

class SsaDefinition extends FinalSsaVariable {
GuardsInput::Expr getARead() { result = this.getAUse() }
}

class SsaExplicitWrite extends SsaDefinition instanceof SSA::SsaExplicitUpdate {
GuardsInput::Expr getValue() {
super.getDefiningExpr().(VariableAssign).getSource() = result or
super.getDefiningExpr().(AssignOp) = result
}
}

class SsaPhiDefinition extends SsaDefinition instanceof SSA::SsaPhiNode {
predicate hasInputFromBlock(SsaDefinition inp, BasicBlock bb) {
super.hasInputFromBlock(inp, bb)
}
}

class SsaParameterInit extends SsaDefinition instanceof SSA::SsaImplicitInit {
Parameter getParameter() { super.isParameterDefinition(result) }
}
private import semmle.code.java.dataflow.SSA
import Ssa

predicate additionalNullCheck = LogicInputCommon::additionalNullCheck/4;

Expand Down
14 changes: 10 additions & 4 deletions java/ql/lib/semmle/code/java/dataflow/DefUse.qll
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ predicate useUsePair(VarRead use1, VarRead use2) { adjacentUseUse+(use1, use2) }
* Other paths may also exist, so the SSA variables in `def` and `use` can be different.
*/
predicate defUsePair(VariableUpdate def, VarRead use) {
exists(SsaVariable v |
v.getAUse() = use and v.getAnUltimateDefinition().(SsaExplicitUpdate).getDefiningExpr() = def
exists(SsaDefinition v, SsaExplicitWrite write |
v.getARead() = use and write.getDefiningExpr() = def
|
v.getAnUltimateDefinition() = write
or
v.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = write
)
}

Expand All @@ -46,7 +50,9 @@ predicate defUsePair(VariableUpdate def, VarRead use) {
* Other paths may also exist, so the SSA variables can be different.
*/
predicate parameterDefUsePair(Parameter p, VarRead use) {
exists(SsaVariable v |
v.getAUse() = use and v.getAnUltimateDefinition().(SsaImplicitInit).isParameterDefinition(p)
exists(SsaDefinition v, SsaParameterInit init | v.getARead() = use and init.getParameter() = p |
v.getAnUltimateDefinition() = init
or
v.(SsaCapturedDefinition).getAnUltimateCapturedDefinition() = init
)
}
40 changes: 21 additions & 19 deletions java/ql/lib/semmle/code/java/dataflow/NullGuards.qll
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ Expr enumConstEquality(Expr e, boolean polarity, EnumConstant c) {
}

/** Gets an instanceof expression of `v` with type `type` */
InstanceOfExpr instanceofExpr(SsaVariable v, RefType type) {
InstanceOfExpr instanceofExpr(SsaDefinition v, RefType type) {
result.getCheckedType() = type and
result.getExpr() = v.getAUse()
result.getExpr() = v.getARead()
}

/**
Expand All @@ -37,8 +37,8 @@ InstanceOfExpr instanceofExpr(SsaVariable v, RefType type) {
*
* Note this includes Kotlin's `==` and `!=` operators, which are value-equality tests.
*/
EqualityTest varEqualityTestExpr(SsaVariable v1, SsaVariable v2, boolean isEqualExpr) {
result.hasOperands(v1.getAUse(), v2.getAUse()) and
EqualityTest varEqualityTestExpr(SsaDefinition v1, SsaDefinition v2, boolean isEqualExpr) {
result.hasOperands(v1.getARead(), v2.getARead()) and
isEqualExpr = result.polarity()
}

Expand Down Expand Up @@ -91,37 +91,37 @@ Expr clearlyNotNullExpr(Expr reason) {
(reason = r1 or reason = r2)
)
or
exists(SsaVariable v, boolean branch, VarRead rval, Guard guard |
exists(SsaDefinition v, boolean branch, VarRead rval, Guard guard |
guard = directNullGuard(v, branch, false) and
guard.controls(rval.getBasicBlock(), branch) and
reason = guard and
rval = v.getAUse() and
rval = v.getARead() and
result = rval and
not result = baseNotNullExpr()
)
or
exists(SsaVariable v |
exists(SsaDefinition v |
clearlyNotNull(v, reason) and
result = v.getAUse() and
result = v.getARead() and
not result = baseNotNullExpr()
)
}

/** Holds if `v` is an SSA variable that is provably not `null`. */
predicate clearlyNotNull(SsaVariable v, Expr reason) {
predicate clearlyNotNull(SsaDefinition v, Expr reason) {
exists(Expr src |
src = v.(SsaExplicitUpdate).getDefiningExpr().(VariableAssign).getSource() and
src = v.(SsaExplicitWrite).getValue() and
src = clearlyNotNullExpr(reason)
)
or
exists(CatchClause cc, LocalVariableDeclExpr decl |
decl = cc.getVariable() and
decl = v.(SsaExplicitUpdate).getDefiningExpr() and
decl = v.(SsaExplicitWrite).getDefiningExpr() and
reason = decl
)
or
exists(SsaVariable captured |
v.(SsaImplicitInit).captures(captured) and
exists(SsaDefinition captured |
v.(SsaCapturedDefinition).captures(captured) and
clearlyNotNull(captured, reason)
)
or
Expand All @@ -136,7 +136,7 @@ predicate clearlyNotNull(SsaVariable v, Expr reason) {
Expr clearlyNotNullExpr() { result = clearlyNotNullExpr(_) }

/** Holds if `v` is an SSA variable that is provably not `null`. */
predicate clearlyNotNull(SsaVariable v) { clearlyNotNull(v, _) }
predicate clearlyNotNull(SsaDefinition v) { clearlyNotNull(v, _) }

/**
* Holds if the evaluation of a call to `m` resulting in the value `branch`
Expand Down Expand Up @@ -207,7 +207,7 @@ deprecated Expr basicOrCustomNullGuard(Expr e, boolean branch, boolean isnull) {
* If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull`
* is true, and non-null if `isnull` is false.
*/
Expr directNullGuard(SsaVariable v, boolean branch, boolean isnull) {
Expr directNullGuard(SsaDefinition v, boolean branch, boolean isnull) {
result = basicNullGuard(sameValue(v, _), branch, isnull)
}

Expand All @@ -219,7 +219,7 @@ Expr directNullGuard(SsaVariable v, boolean branch, boolean isnull) {
* If `result` evaluates to `branch`, then `v` is guaranteed to be null if `isnull`
* is true, and non-null if `isnull` is false.
*/
deprecated Guard nullGuard(SsaVariable v, boolean branch, boolean isnull) {
deprecated Guard nullGuard(SsaDefinition v, boolean branch, boolean isnull) {
result = directNullGuard(v, branch, isnull)
}

Expand All @@ -228,7 +228,9 @@ deprecated Guard nullGuard(SsaVariable v, boolean branch, boolean isnull) {
* from `bb1` to `bb2` implies that `v` is guaranteed to be null if `isnull` is
* true, and non-null if `isnull` is false.
*/
predicate nullGuardControlsBranchEdge(SsaVariable v, boolean isnull, BasicBlock bb1, BasicBlock bb2) {
predicate nullGuardControlsBranchEdge(
SsaDefinition v, boolean isnull, BasicBlock bb1, BasicBlock bb2
) {
exists(GuardValue gv |
Guards_v3::ssaControlsBranchEdge(v, bb1, bb2, gv) and
gv.isNullness(isnull)
Expand All @@ -240,7 +242,7 @@ predicate nullGuardControlsBranchEdge(SsaVariable v, boolean isnull, BasicBlock
* `bb` `v` is guaranteed to be null if `isnull` is true, and non-null if
* `isnull` is false.
*/
predicate nullGuardControls(SsaVariable v, boolean isnull, BasicBlock bb) {
predicate nullGuardControls(SsaDefinition v, boolean isnull, BasicBlock bb) {
exists(GuardValue gv |
Guards_v3::ssaControls(v, bb, gv) and
gv.isNullness(isnull)
Expand All @@ -263,6 +265,6 @@ predicate guardSuggestsExprMaybeNull(Expr guard, Expr e) {
/**
* Holds if `guard` is a guard expression that suggests that `v` might be null.
*/
predicate guardSuggestsVarMaybeNull(Expr guard, SsaVariable v) {
predicate guardSuggestsVarMaybeNull(Expr guard, SsaDefinition v) {
guardSuggestsExprMaybeNull(guard, sameValue(v, _))
}
Loading
Loading