diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRAliasUtil.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRAliasUtil.kt index 35de0c4e1..753f92bb5 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRAliasUtil.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRAliasUtil.kt @@ -7,7 +7,6 @@ import org.opentaint.dataflow.ap.ifds.ElementAccessor import org.opentaint.dataflow.ap.ifds.FieldAccessor import org.opentaint.dataflow.ap.ifds.access.FinalFactAp import org.opentaint.dataflow.ap.ifds.access.InitialFactAp -import org.opentaint.dataflow.ap.ifds.analysis.alias.applyAlias import org.opentaint.dataflow.ap.ifds.analysis.alias.forEachAliasAtStatement import org.opentaint.dataflow.ap.ifds.analysis.alias.forEachAliasAtStatementAmongBases import org.opentaint.dataflow.ap.ifds.analysis.alias.forEachHeapAliasBeforeStatement @@ -25,17 +24,6 @@ fun JIRLocalAliasAnalysis.forEachAliasAtStatement(statement: JIRInst, fact: Fina fun JIRLocalAliasAnalysis.forEachAliasAtStatement(statement: JIRInst, fact: InitialFactAp, body: (InitialFactAp) -> Unit) = forEachAliasAtStatement(statement, fact, AliasInfo::relevantApInfo, AliasAccessor::apAccessor, body) -fun JIRLocalAliasAnalysis.forEachAliasAfterCallStatement(statement: JIRInst, fact: FinalFactAp, body: (FinalFactAp) -> Unit) { - val base = fact.base as? AccessPathBase.LocalVar ?: return - val aliasesBefore = findAlias(base, statement) ?: return - val aliasesAfter = findAliasAfterStatement(base, statement)?.toSet() ?: return - val aliasesPersistedThroughCall = aliasesBefore.filter { it in aliasesAfter } - - aliasesPersistedThroughCall - .filterIsInstance() - .filterNot { alias -> alias.base is AccessPathBase.Constant } - .forEach { alias -> applyAlias(fact, alias, AliasAccessor::apAccessor, body) } -} fun JIRLocalAliasAnalysis.forEachHeapAliasBeforeStatement(statement: JIRInst, fact: FinalFactAp, body: (FinalFactAp) -> Unit) = forEachHeapAliasBeforeStatement(statement, fact, Accessor::aliasAccessor, AliasInfo::relevantApInfo, AliasAccessor::apAccessor, body) diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt index 28743e77e..2c4c53633 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallFlowFunction.kt @@ -381,7 +381,7 @@ class JIRMethodCallFlowFunction( return } - analysisContext.aliasAnalysis?.forEachAliasAfterCallStatement(statement, this) { aliased -> + analysisContext.aliasAnalysis?.forEachHeapAliasBeforeStatement(statement, this) { aliased -> body(aliased) } } diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallSummaryHandler.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallSummaryHandler.kt index 865093a7b..de4d86d1e 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallSummaryHandler.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/analysis/JIRMethodCallSummaryHandler.kt @@ -90,7 +90,7 @@ class JIRMethodCallSummaryHandler( } private fun applyCallAliases(fact: FinalFactAp, body: (FinalFactAp) -> Unit) { - analysisContext.aliasAnalysis?.forEachAliasAfterCallStatement(statement, fact) { aliased -> + analysisContext.aliasAnalysis?.forEachHeapAliasBeforeStatement(statement, fact) { aliased -> body(aliased) } } diff --git a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt index 70fb78cf4..08642618b 100644 --- a/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt +++ b/core/opentaint-dataflow-core/opentaint-jvm-dataflow/src/main/kotlin/org/opentaint/dataflow/jvm/ap/ifds/trace/JIRMethodCallPrecondition.kt @@ -20,6 +20,7 @@ import org.opentaint.dataflow.jvm.ap.ifds.JIRMethodCallFactMapper.factIsRelevant import org.opentaint.dataflow.jvm.ap.ifds.MethodFlowFunctionUtils import org.opentaint.dataflow.jvm.ap.ifds.TaintConfigUtils.accept import org.opentaint.dataflow.jvm.ap.ifds.analysis.JIRMethodAnalysisContext +import org.opentaint.dataflow.jvm.ap.ifds.analysis.forEachAliasAtStatement import org.opentaint.dataflow.jvm.ap.ifds.analysis.forEachPossibleAliasAtStatement import org.opentaint.dataflow.jvm.ap.ifds.taint.TaintRulesProvider import org.opentaint.dataflow.jvm.ap.ifds.taint.resolveAp @@ -58,6 +59,11 @@ class JIRMethodCallPrecondition( preconditionForFact(aliasedFact)?.let { results += PreconditionFactsForInitialFact(aliasedFact, it) } } + // todo: do we need to explore all accessors? + analysisContext.aliasAnalysis?.forEachAliasAtStatement(statement, fact) { aliasedFact -> + preconditionForFact(aliasedFact)?.let { results += PreconditionFactsForInitialFact(aliasedFact, it) } + } + return results } diff --git a/core/opentaint-java-querylang/samples/src/main/java/issues/i98/User_i98_deep.java b/core/opentaint-java-querylang/samples/src/main/java/issues/i98/User_i98_deep.java new file mode 100644 index 000000000..58b149f23 --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/issues/i98/User_i98_deep.java @@ -0,0 +1,81 @@ +package issues.i98; + +public class User_i98_deep { + private class Caller { + void call(Depth2 r, String b) { + r.depth1.data = b; + } + + void call(Depth3 r, String b) { + r.depth2.depth1.data = b; + } + + void call(Depth4 r, String b) { + r.depth3.depth2.depth1.data = b; + } + } + + private class Depth1 { + String data = ""; + } + + private class Depth2 { + Depth1 depth1 = new Depth1(); + } + + private class Depth3 { + Depth2 depth2 = new Depth2(); + } + + private class Depth4 { + Depth3 depth3 = new Depth3(); + } + + public String badUser(String badString) { + Depth2 d2 = new Depth2(); + Depth1 d = d2.depth1; + Caller k = new Caller(); + k.call(d2, badString); + return d.data; + } + + public String badUserDepth4(String badString) { + Depth4 d4 = new Depth4(); + Depth3 d3 = d4.depth3; + Caller k = new Caller(); + k.call(d4, badString); + return d3.depth2.depth1.data; + } + + public String badUserDepth3(String badString) { + Depth4 d4 = new Depth4(); + Depth2 d2 = d4.depth3.depth2; + Caller k = new Caller(); + k.call(d4, badString); + return d2.depth1.data; + } + + public String badUserDepth2(String badString) { + Depth4 d4 = new Depth4(); + Depth1 d1 = d4.depth3.depth2.depth1; + Caller k = new Caller(); + k.call(d4, badString); + return d1.data; + } + + public String badUserDepth4Call2(String badString) { + Depth4 d4 = new Depth4(); + Depth3 d3 = d4.depth3; + Caller k = new Caller(); + k.call(d4.depth3.depth2, badString); + return d3.depth2.depth1.data; + } + + public String badUserDepth4Call3(String badString) { + Depth4 d4 = new Depth4(); + Depth2 d2 = d4.depth3.depth2; + Caller k = new Caller(); + k.call(d4.depth3, badString); + return d2.depth1.data; + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/java/issues/issue98_deep.java b/core/opentaint-java-querylang/samples/src/main/java/issues/issue98_deep.java new file mode 100644 index 000000000..35b96052d --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/java/issues/issue98_deep.java @@ -0,0 +1,45 @@ +package issues; + +import base.RuleSample; +import base.RuleSet; +import issues.i98.User_i98_deep; + +@RuleSet("issues/issue98.yaml") +public abstract class issue98_deep implements RuleSample { + static String badString() { + return "42"; + } + + static void sink(String data) { + } + + static class PositiveTaint extends issue98_deep { + @Override + public void entrypoint() { sink((new User_i98_deep()).badUser(badString())); } + } + + static class PositiveDepth4 extends issue98_deep { + @Override + public void entrypoint() { sink((new User_i98_deep()).badUserDepth4(badString())); } + } + + static class PositiveDepth3 extends issue98_deep { + @Override + public void entrypoint() { sink((new User_i98_deep()).badUserDepth3(badString())); } + } + + static class PositiveDepth2 extends issue98_deep { + @Override + public void entrypoint() { sink((new User_i98_deep()).badUserDepth2(badString())); } + } + + static class PositiveDepth4Call2 extends issue98_deep { + @Override + public void entrypoint() { sink((new User_i98_deep()).badUserDepth4Call2(badString())); } + } + + static class PositiveDepth4Call3 extends issue98_deep { + @Override + public void entrypoint() { sink((new User_i98_deep()).badUserDepth4Call3(badString())); } + } +} diff --git a/core/opentaint-java-querylang/samples/src/main/resources/issues/issue98.yaml b/core/opentaint-java-querylang/samples/src/main/resources/issues/issue98.yaml new file mode 100644 index 000000000..f2dfc648c --- /dev/null +++ b/core/opentaint-java-querylang/samples/src/main/resources/issues/issue98.yaml @@ -0,0 +1,11 @@ +rules: + - id: i97 + languages: + - java + severity: ERROR + message: badMethod + patterns: + - pattern: | + $A = badString(); + ... + sink($A); diff --git a/core/opentaint-java-querylang/src/test/kotlin/org/opentaint/semgrep/IssuesTest.kt b/core/opentaint-java-querylang/src/test/kotlin/org/opentaint/semgrep/IssuesTest.kt index 9bcce3a57..b6266eb3c 100644 --- a/core/opentaint-java-querylang/src/test/kotlin/org/opentaint/semgrep/IssuesTest.kt +++ b/core/opentaint-java-querylang/src/test/kotlin/org/opentaint/semgrep/IssuesTest.kt @@ -18,6 +18,7 @@ import issues.issue96 import issues.issue97 import issues.issueChain import issues.issueChainSplitBuilder +import issues.issue98_deep import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.TestInstance @@ -113,6 +114,9 @@ class IssuesTest : SampleBasedTest() { @Test fun `issue chain-pattern split builder`() = runTest(EXPECT_STATE_VAR) + @Test + fun `issue 98 deep`() = runTest() + @AfterAll fun close() { closeRunner() diff --git a/core/opentaint-jvm-sast-dataflow/dataflow-approximations/src/main/java/org/opentaint/jvm/dataflow/approximations/stdlib/Iterable.java b/core/opentaint-jvm-sast-dataflow/dataflow-approximations/src/main/java/org/opentaint/jvm/dataflow/approximations/stdlib/Iterable.java new file mode 100644 index 000000000..fdb9a3544 --- /dev/null +++ b/core/opentaint-jvm-sast-dataflow/dataflow-approximations/src/main/java/org/opentaint/jvm/dataflow/approximations/stdlib/Iterable.java @@ -0,0 +1,17 @@ +package org.opentaint.jvm.dataflow.approximations.stdlib; + +import org.opentaint.ir.approximation.annotation.Approximate; + +import java.util.Iterator; +import java.util.function.Consumer; + +@Approximate(java.lang.Iterable.class) +public class Iterable { + public void forEach(Consumer action) { + java.lang.Iterable t = (java.lang.Iterable) (Object) this; + Iterator it = t.iterator(); + if (it.hasNext()) { + action.accept(it.next()); + } + } +}