Skip to content

Commit

Permalink
Java: Adopt shared SSA data-flow integration (WIP)
Browse files Browse the repository at this point in the history
  • Loading branch information
hvitved committed Jul 8, 2024
1 parent dfda6e8 commit 4479e03
Show file tree
Hide file tree
Showing 20 changed files with 925 additions and 958 deletions.
873 changes: 49 additions & 824 deletions java/ql/lib/semmle/code/java/dataflow/SSA.qll

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions java/ql/lib/semmle/code/java/dataflow/internal/BaseSSA.qll
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class BaseSsaSourceVariable extends TBaseSsaSourceVariable {
}

cached
private module SsaImpl {
private module BaseSsaImpl {
/** Gets the destination variable of an update of a tracked variable. */
cached
BaseSsaSourceVariable getDestVar(VariableUpdate upd) {
Expand Down Expand Up @@ -436,7 +436,7 @@ private module SsaImpl {
}
}

private import SsaImpl
private import BaseSsaImpl
private import SsaDefReaches
import SsaPublic

Expand Down
49 changes: 48 additions & 1 deletion java/ql/lib/semmle/code/java/dataflow/internal/DataFlowNodes.qll
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ private import FlowSummaryImpl as FlowSummaryImpl
private import DataFlowImplCommon as DataFlowImplCommon
private import semmle.code.java.controlflow.Guards
private import semmle.code.java.dataflow.RangeUtils
private import semmle.code.java.dataflow.SSA
private import SsaImpl as SsaImpl

/** Gets a string for approximating the name of a field. */
string approximateFieldContent(FieldContent fc) { result = fc.getField().getName().prefix(1) }
Expand All @@ -21,6 +23,30 @@ private predicate deadcode(Expr e) {
)
}

module SsaFlow {
module Impl = SsaImpl::DataFlowIntegration;

Impl::Node asNode(Node n) {
n = TSsaNode(result)
or
result.(Impl::ExprNode).getExpr() = n.asExpr()
or
result.(Impl::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr()
or
TExplicitParameterNode(result.(Impl::ParameterNode).getParameter()) = n
}

predicate localFlowStep(
SsaImpl::Impl::DefinitionExt def, Node nodeFrom, Node nodeTo, boolean isUseStep
) {
Impl::localFlowStep(def, asNode(nodeFrom), asNode(nodeTo), isUseStep)
}

predicate localMustFlowStep(SsaImpl::Impl::DefinitionExt def, Node nodeFrom, Node nodeTo) {
Impl::localMustFlowStep(def, asNode(nodeFrom), asNode(nodeTo))
}
}

cached
private module Cached {
cached
Expand All @@ -31,6 +57,7 @@ private module Cached {
not e.getType() instanceof VoidType and
not e.getParent*() instanceof Annotation
} or
TSsaNode(SsaFlow::Impl::SsaNode node) or
TExplicitParameterNode(Parameter p) { exists(p.getCallable().getBody()) } or
TImplicitVarargsArray(Call c) {
c.getCallee().isVarargs() and
Expand Down Expand Up @@ -79,6 +106,11 @@ private module Cached {
TMapValueContentApprox() or
TCapturedVariableContentApprox(CapturedVariable v) or
TSyntheticFieldApproxContent()

cached
Node getABarrierNode(Guard guard, SsaVariable def, boolean branch) {
SsaFlow::asNode(result) = SsaImpl::DataFlowIntegration::getABarrierNode(guard, def, branch)
}
}

import Cached
Expand Down Expand Up @@ -137,6 +169,8 @@ module Public {
result = this.(FieldValueNode).getField().getType()
or
result instanceof TypeObject and this instanceof AdditionalNode
or
result = this.(SsaNode).getDefinitionExt().getSourceVariable().getType()
}

/** Gets the callable in which this node occurs. */
Expand Down Expand Up @@ -358,6 +392,18 @@ module Public {

private import Public

class SsaNode extends Node, TSsaNode {
private SsaFlow::Impl::SsaNode node;

SsaNode() { this = TSsaNode(node) }

SsaImpl::Impl::DefinitionExt getDefinitionExt() { result = node.getDefinitionExt() }

override Location getLocation() { result = node.getLocation() }

override string toString() { result = node.toString() }
}

private class NewExpr extends PostUpdateNode, TExprNode {
NewExpr() { exists(ClassInstanceExpr cie | this = TExprNode(cie)) }

Expand Down Expand Up @@ -398,7 +444,8 @@ module Private {
result.asSummarizedCallable() = n.(FlowSummaryNode).getSummarizedCallable() or
result.asCallable() = n.(CaptureNode).getSynthesizedCaptureNode().getEnclosingCallable() or
result.asFieldScope() = n.(FieldValueNode).getField() or
result.asCallable() = any(Expr e | n.(AdditionalNode).nodeAt(e, _)).getEnclosingCallable()
result.asCallable() = any(Expr e | n.(AdditionalNode).nodeAt(e, _)).getEnclosingCallable() or
result.asCallable() = n.(SsaNode).getDefinitionExt().getBasicBlock().getEnclosingCallable()
}

/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,11 @@ predicate forceHighPrecision(Content c) {
}

/** Holds if `n` should be hidden from path explanations. */
predicate nodeIsHidden(Node n) { n instanceof FlowSummaryNode }
predicate nodeIsHidden(Node n) {
n instanceof FlowSummaryNode
or
n instanceof SsaNode
}

class LambdaCallKind = Method; // the "apply" method in the functional interface

Expand Down
72 changes: 28 additions & 44 deletions java/ql/lib/semmle/code/java/dataflow/internal/DataFlowUtil.qll
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ private import semmle.code.java.dataflow.FlowSummary
private import semmle.code.java.dataflow.InstanceAccess
private import FlowSummaryImpl as FlowSummaryImpl
private import TaintTrackingUtil as TaintTrackingUtil
private import SsaImpl as SsaImpl
private import DataFlowNodes
import DataFlowNodes::Public

Expand Down Expand Up @@ -99,6 +100,10 @@ predicate hasNonlocalValue(FieldRead fr) {
)
}

private predicate capturedVariableRead(Node n) {
n.asExpr().(VarRead).getVariable() instanceof CapturedVariable
}

cached
private module Cached {
/**
Expand All @@ -108,7 +113,7 @@ private module Cached {
predicate localFlowStep(Node node1, Node node2) {
simpleLocalFlowStep0(node1, node2, _)
or
adjacentUseUse(node1.asExpr(), node2.asExpr())
SsaFlow::localFlowStep(_, node1, node2, _)
or
// Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural
Expand All @@ -125,6 +130,19 @@ private module Cached {
predicate simpleLocalFlowStep(Node node1, Node node2, string model) {
simpleLocalFlowStep0(node1, node2, model)
or
exists(boolean isUseStep |
SsaFlow::localFlowStep(_, node1, node2, isUseStep) and
not capturedVariableRead(node2) and
model = ""
|
isUseStep = false
or
not exists(FieldRead fr |
hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr()
) and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(node1, _)
)
or
any(AdditionalValueStep a).step(node1, node2) and
pragma[only_bind_out](node1.getEnclosingCallable()) =
pragma[only_bind_out](node2.getEnclosingCallable()) and
Expand All @@ -147,14 +165,7 @@ predicate localMustFlowStep(Node node1, Node node2) {
node2.(ImplicitInstanceAccess).getInstanceAccess().(OwnInstanceAccess).getEnclosingCallable()
)
or
exists(SsaImplicitInit init |
init.isParameterDefinition(node1.asParameter()) and init.getAUse() = node2.asExpr()
)
or
exists(SsaExplicitUpdate upd |
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() and
upd.getAUse() = node2.asExpr()
)
SsaFlow::localMustFlowStep(_, node1, node2)
or
node2.asExpr().(CastingExpr).getExpr() = node1.asExpr()
or
Expand All @@ -169,10 +180,6 @@ predicate localMustFlowStep(Node node1, Node node2) {

import Cached

private predicate capturedVariableRead(Node n) {
n.asExpr().(VarRead).getVariable() instanceof CapturedVariable
}

/**
* Holds if there is a data flow step from `e1` to `e2` that only steps from
* child to parent in the AST.
Expand Down Expand Up @@ -214,34 +221,8 @@ predicate simpleAstFlowStep(Expr e1, Expr e2) {
private predicate simpleLocalFlowStep0(Node node1, Node node2, string model) {
(
TaintTrackingUtil::forceCachingInSameStage() and
// Variable flow steps through adjacent def-use and use-use pairs.
exists(SsaExplicitUpdate upd |
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
upd.getDefiningExpr().(AssignOp) = node1.asExpr() or
upd.getDefiningExpr().(RecordBindingVariableExpr) = node1.asExpr()
|
node2.asExpr() = upd.getAFirstUse() and
not capturedVariableRead(node2)
)
or
exists(SsaImplicitInit init |
init.isParameterDefinition(node1.asParameter()) and
node2.asExpr() = init.getAFirstUse() and
not capturedVariableRead(node2)
)
or
adjacentUseUse(node1.asExpr(), node2.asExpr()) and
not exists(FieldRead fr |
hasNonlocalValue(fr) and fr.getField().isStatic() and fr = node1.asExpr()
) and
not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(node1, _) and
not capturedVariableRead(node2)
or
ThisFlow::adjacentThisRefs(node1, node2)
or
adjacentUseUse(node1.(PostUpdateNode).getPreUpdateNode().asExpr(), node2.asExpr()) and
not capturedVariableRead(node2)
or
ThisFlow::adjacentThisRefs(node1.(PostUpdateNode).getPreUpdateNode(), node2)
or
simpleAstFlowStep(node1.asExpr(), node2.asExpr())
Expand Down Expand Up @@ -401,13 +382,16 @@ signature predicate guardChecksSig(Guard g, Expr e, boolean branch);
* in data flow and taint tracking.
*/
module BarrierGuard<guardChecksSig/3 guardChecks> {
pragma[nomagic]
private predicate guardChecksSsaDef(Guard g, SsaVariable v, boolean branch) {
guardChecks(g, v.getAUse(), branch)
}

/** Gets a node that is safely guarded by the given guard check. */
Node getABarrierNode() {
exists(Guard g, SsaVariable v, boolean branch, VarRead use |
guardChecks(g, v.getAUse(), branch) and
use = v.getAUse() and
g.controls(use.getBasicBlock(), branch) and
result.asExpr() = use
exists(Guard g, SsaVariable v, boolean branch |
guardChecksSsaDef(g, v, branch) and
result = getABarrierNode(g, v, branch)
)
}
}
Loading

0 comments on commit 4479e03

Please sign in to comment.