From 3b3e206225de842b723d323e51dd1880feca1928 Mon Sep 17 00:00:00 2001 From: Dominik Helm Date: Fri, 5 Mar 2021 17:03:09 +0100 Subject: [PATCH 01/67] Improved IFDS analysis, including demos and tests; by Mario Trageser Former-commit-id: 7511d576c182b52f5873121e83c5914907f5474e --- .../fpcf/analyses/PRATaintAnalysis.scala | 318 +++++ .../analyses/BasicIFDSTaintAnalysis.scala | 550 -------- .../tac/fpcf/analyses/IFDSEvaluation.scala | 68 + .../heros/analyses/HerosAnalysis.scala | 175 +++ .../analyses/HerosVariableTypeAnalysis.scala | 246 ++++ .../analyses/heros/analyses/VTAEquality.scala | 224 +++ .../HerosBackwardClassForNameAnalysis.scala | 285 ++++ .../HerosForwardClassForNameAnalysis.scala | 380 +++++ .../analyses/taint/HerosTaintAnalysis.scala | 22 + .../analyses/heros/cfg/OpalBackwardICFG.scala | 77 ++ .../analyses/heros/cfg/OpalForwardICFG.scala | 59 + .../fpcf/analyses/heros/cfg/OpalICFG.scala | 119 ++ .../BackwardClassForNameTaintAnalysis.scala | 161 +++ .../ForwardClassForNameTaintAnalysis.scala | 141 ++ .../taint/TaintAnalysisTestClass.java | 417 ++++++ .../opalj/fpcf/fixtures/vta/VTATestClass.java | 126 ++ .../properties/taint/BackwardFlowPath.java | 20 + .../properties/taint/ForwardFlowPath.java | 21 + .../fpcf/properties/vta/ExpectedCallee.java | 26 + .../fpcf/properties/vta/ExpectedType.java | 26 + .../BackwardTaintAnalysisFixtureTest.scala | 44 + .../ForwardTaintAnalysisFixtureTest.scala | 44 + .../scala/org/opalj/fpcf/ifds/VTATest.scala | 44 + .../taint/BackwardFlowPathMatcher.scala | 58 + .../taint/ForwardFlowPathMatcher.scala | 46 + .../vta/ExpectedCalleeMatcher.scala | 54 + .../properties/vta/ExpectedTypeMatcher.scala | 41 + .../fpcf/properties/vta/VTAMatcher.scala | 59 + .../scala/org/opalj/br/ElementValue.scala | 3 + OPAL/tac/lib/heros-trunk.jar | Bin 0 -> 4324239 bytes .../fpcf/analyses/AbstractIFDSAnalysis.scala | 954 ------------- .../analyses/ifds/AbstractIFDSAnalysis.scala | 1230 +++++++++++++++++ .../analyses/ifds/BackwardIFDSAnalysis.scala | 476 +++++++ .../analyses/ifds/ForwardIFDSAnalysis.scala | 222 +++ .../ifds/IFDSBasedVariableTypeAnalysis.scala | 425 ++++++ .../tac/fpcf/analyses/ifds/Subsumable.scala | 105 ++ .../tac/fpcf/analyses/ifds/Subsuming.scala | 90 ++ .../ifds/taint/BackwardTaintAnalysis.scala | 390 ++++++ .../ifds/taint/ForwardTaintAnalysis.scala | 372 +++++ .../analyses/ifds/taint/TaintAnalysis.scala | 117 ++ .../tac/fpcf/properties/IFDSProperty.scala | 2 +- .../taint/BackwardTaintAnalysisFixture.scala | 88 ++ .../taint/ForwardTaintAnalysisFixture.scala | 61 + 43 files changed, 6881 insertions(+), 1505 deletions(-) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PRATaintAnalysis.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/BasicIFDSTaintAnalysis.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/vta/VTATestClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/BackwardFlowPath.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/ForwardFlowPath.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedCallee.java create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedType.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala create mode 100644 OPAL/tac/lib/heros-trunk.jar delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/AbstractIFDSAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintAnalysis.scala create mode 100644 OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala create mode 100644 OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PRATaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PRATaintAnalysis.scala new file mode 100644 index 0000000000..5b77902476 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PRATaintAnalysis.scala @@ -0,0 +1,318 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +/*package org.opalj +package fpcf +package analyses + +import java.io.File + +import scala.collection.immutable.ListSet +import scala.io.Source + +import org.opalj.log.LogContext +import org.opalj.fpcf.seq.PKESequentialPropertyStore +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.DeclaredMethod +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.br.MethodDescriptor +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.SomeProject +import org.opalj.br.analyses.Project.JavaClassFileReader +import org.opalj.ai.domain.l1 +import org.opalj.ai.fpcf.analyses.LazyL0BaseAIAnalysis +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.Assignment +import org.opalj.tac.Expr +import org.opalj.tac.Var +import org.opalj.tac.ReturnValue +import org.opalj.tac.Call +import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.Statement +import org.opalj.tac.fpcf.analyses.IFDSAnalysis +import org.opalj.tac.fpcf.analyses.TACAITransformer + +trait Fact +case object NullFact extends Fact +case class Variable(index: Int) extends Fact +case class FlowFact(flow: ListSet[Method]) extends Fact { + override val hashCode: Int = { + // HERE, a foldLeft introduces a lot of overhead due to (un)boxing. + var r = 1 + flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) + r + } +} +class FinalFact(flow: ListSet[Method]) extends FlowFact(flow) +object FinalFact { + def unapply(arg: FinalFact): Some[ListSet[Method]] = Some(arg.flow) +} + +/** + * A simple IFDS taint analysis. + * + * @author Dominik Helm + */ +class PRATaintAnalysis private ( + implicit + val project: SomeProject +) extends AbstractIFDSAnalysis[Fact] { + + override val property: IFDSPropertyMetaInformation[Fact] = Taint + + override def createProperty(result: Map[Statement, Set[Fact]]): IFDSProperty[Fact] = { + new Taint(result) + } + + override def normalFlow(stmt: Statement, succ: Statement, in: Set[Fact]): Set[Fact] = + stmt.stmt.astID match { + case Assignment.ASTID ⇒ + handleAssignment(stmt, stmt.stmt.asAssignment.expr, in) + case _ ⇒ in + } + + /** + * Returns true if the expression contains a taint. + */ + def isTainted(expr: Expr[V], in: Set[Fact]): Boolean = { + expr.isVar && in.exists { + case Variable(index) ⇒ expr.asVar.definedBy.contains(index) + case _ ⇒ false + } + } + + def handleAssignment(stmt: Statement, expr: Expr[V], in: Set[Fact]): Set[Fact] = + expr.astID match { + case Var.ASTID ⇒ + val newTaint = in.collect { + case Variable(index) if expr.asVar.definedBy.contains(index) ⇒ + Some(Variable(stmt.index)) + case _ ⇒ None + }.flatten + in ++ newTaint + case _ ⇒ in + } + + override def callFlow( + stmt: Statement, + callee: DeclaredMethod, + in: Set[Fact] + ): Set[Fact] = { + val call = asCall(stmt.stmt) + val allParams = call.receiverOption ++ asCall(stmt.stmt).params + if (isSink(call)) { + Set.empty + } else { + in.collect { + case Variable(index) ⇒ // Taint formal parameter if actual parameter is tainted + allParams.zipWithIndex.collect { + case (param, pIndex) if param.asVar.definedBy.contains(index) ⇒ + Variable(paramToIndex(pIndex, !callee.definedMethod.isStatic)) + } + }.flatten + } + } + + override def returnFlow( + stmt: Statement, + callee: DeclaredMethod, + exit: Statement, + succ: Statement, + in: Set[Fact] + ): Set[Fact] = { + + /** + * Checks whether the formal parameter is of a reference type, as primitive types are + * call-by-value. + */ + def isRefTypeParam(index: Int): Boolean = + if (index == -1) true + else { + callee.descriptor.parameterType( + paramToIndex(index, includeThis = false) + ).isReferenceType + } + + val call = asCall(stmt.stmt) + if (isSource(call) && stmt.stmt.astID == Assignment.ASTID) + Set(Variable(stmt.index)) + else { + val allParams = (asCall(stmt.stmt).receiverOption ++ asCall(stmt.stmt).params).toSeq + var flows: Set[Fact] = Set.empty + for (fact ← in) { + fact match { + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + // Taint actual parameter if formal parameter is tainted + val param = + allParams(paramToIndex(index, !callee.definedMethod.isStatic)) + flows ++= param.asVar.definedBy.iterator.map(Variable) + + case FlowFact(flow) ⇒ + val newFlow = flow + stmt.method + flows += FlowFact(newFlow) + case _ ⇒ + } + } + + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { + val returnValue = exit.stmt.asReturnValue.expr.asVar + flows ++= in.collect { + case Variable(index) if returnValue.definedBy.contains(index) ⇒ + Variable(stmt.index) + } + } + + flows + } + } + + /** + * Converts a parameter origin to the index in the parameter seq (and vice-versa). + */ + def paramToIndex(param: Int, includeThis: Boolean): Int = + (if (includeThis) -1 else -2) - param + + override def callToReturnFlow(stmt: Statement, succ: Statement, in: Set[Fact]): Set[Fact] = { + val call = asCall(stmt.stmt) + if (isSink(call)) { + if (in.exists { + case Variable(index) ⇒ + asCall(stmt.stmt).params.exists(p ⇒ p.asVar.definedBy.contains(index)) + case _ ⇒ false + }) { + in ++ Set(FlowFact(ListSet(stmt.method))) + } else { + in + } + } else { + in + } + } + + def isSource(call: Call[V]): Boolean = { + PRATaintAnalysis.sources.get((call.name, call.descriptor)).exists(p.classHierarchy.isSubtypeOf(_, call.declaringClass)) + } + + def isSink(call: Call[V]): Boolean = { + PRATaintAnalysis.sinks.get((call.name, call.descriptor)).exists(p.classHierarchy.isSubtypeOf(_, call.declaringClass)) + } + + val entryPoints: Map[DeclaredMethod, Fact] = (for { + m ← p.allMethodsWithBody + } yield declaredMethods(m) → NullFact).toMap + +} + +object PRATaintAnalysis extends IFDSAnalysis[Fact] { + override def init(p: SomeProject, ps: PropertyStore) = new PRATaintAnalysis()(p) + + override def property: IFDSPropertyMetaInformation[Fact] = Taint + + var sources: Map[(String, MethodDescriptor), ObjectType] = _ + var sinks: Map[(String, MethodDescriptor), ObjectType] = _ +} + +class Taint(val flows: Map[Statement, Set[Fact]]) extends IFDSProperty[Fact] { + + override type Self = Taint + + def key: PropertyKey[Taint] = Taint.key +} + +object Taint extends IFDSPropertyMetaInformation[Fact] { + override type Self = Taint + + val key: PropertyKey[Taint] = PropertyKey.create( + "PRATaint", + new Taint(Map.empty) + ) +} + +object PRATaintAnalysisRunner { + + def main(args: Array[String]): Unit = { + + val cp = new File(args(0)+"S") + val sources = new File(args(1)) + val sinks = new File(args(2)) + + val p = Project( + JavaClassFileReader().ClassFiles(cp), + JavaClassFileReader().ClassFiles(new File("/home/dominik/Desktop/android.jar")), + libraryClassFilesAreInterfacesOnly = false, + Traversable.empty + ) + p.getOrCreateProjectInformationKeyInitializationData( + PropertyStoreKey, + (context: List[PropertyStoreContext[AnyRef]]) ⇒ { + implicit val lg: LogContext = p.logContext + val ps = PKESequentialPropertyStore.apply(context: _*) + PropertyStore.updateDebug(false) + ps + } + ) + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey)( + (i: Option[Set[Class[_ <: AnyRef]]]) ⇒ (i match { + case None ⇒ Set(classOf[l1.DefaultDomainWithCFGAndDefUse[_]]) + case Some(requirements) ⇒ requirements + classOf[l1.DefaultDomainWithCFGAndDefUse[_]] + }): Set[Class[_ <: AnyRef]] + ) + + PRATaintAnalysis.sources = getList(sources) + PRATaintAnalysis.sinks = getList(sinks) + + val ps = p.get(PropertyStoreKey) + val manager = p.get(FPCFAnalysesManagerKey) + val (_, analyses) = + manager.runAll(LazyL0BaseAIAnalysis, TACAITransformer, PRATaintAnalysis) + + val entryPoints = analyses.collect { case (_, a: PRATaintAnalysis) ⇒ a.entryPoints }.head + for { + e ← entryPoints + flows = ps(e, PRATaintAnalysis.property.key) + fact ← flows.ub.asInstanceOf[IFDSProperty[Fact]].flows.values.flatten.toSet[Fact] + } { + fact match { + case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.toJava).mkString(", ")) + case _ ⇒ + } + } + + } + + def getList(file: File): Map[(String, MethodDescriptor), ObjectType] = { + ( + for { + line ← Source.fromFile(file).getLines() + Array(declClass, returnType, signature, _) = line.split(' ') + index = signature.indexOf("(") + name = signature.substring(0, index) + parameters = signature.substring(index + 1, signature.length - 1) + jvmSignature = parameters.split(',').map(toJVMType).mkString("(", "", ")"+toJVMType(returnType)) + descriptor = MethodDescriptor(jvmSignature) + } yield (name, descriptor) → ObjectType(declClass.replace('.', '/')) + ).toMap + } + + def toJVMType(javaType: String): String = { + val trimmedType = javaType.trim + if (trimmedType.endsWith("[]")) "["+toJVMType(trimmedType.substring(0, trimmedType.length - 2)) + else trimmedType match { + case "void" ⇒ "V" + case "byte" ⇒ "B" + case "char" ⇒ "C" + case "double" ⇒ "D" + case "float" ⇒ "F" + case "int" ⇒ "I" + case "long" ⇒ "J" + case "short" ⇒ "S" + case "boolean" ⇒ "Z" + case _ ⇒ "L"+trimmedType.replace('.', '/')+";" + } + } +} +*/ \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/BasicIFDSTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/BasicIFDSTaintAnalysis.scala deleted file mode 100644 index c7f870a9f0..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/BasicIFDSTaintAnalysis.scala +++ /dev/null @@ -1,550 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package tac -package fpcf -package analyses - -import java.io.File - -import scala.collection.immutable.ListSet - -import com.typesafe.config.ConfigValueFactory - -import org.opalj.log.LogContext -import org.opalj.util.PerformanceEvaluation -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.util.Seconds -import org.opalj.collection.immutable.RefArray -import org.opalj.fpcf.PropertyKey -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.seq.PKESequentialPropertyStore -import org.opalj.fpcf.PropertyStoreContext -import org.opalj.bytecode.JRELibraryFolder -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.DeclaredMethod -import org.opalj.br.Method -import org.opalj.br.ObjectType -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.SomeProject -import org.opalj.ai.domain.l0.PrimitiveTACAIDomain -import org.opalj.ai.domain.l1 -import org.opalj.ai.domain.l2 -import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.cg.CallGraphDeserializerScheduler -import org.opalj.tac.fpcf.properties.IFDSProperty -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation - -sealed trait Fact extends AbstractIFDSFact -case class Variable(index: Int) extends Fact -//case class ArrayElement(index: Int, element: Int) extends Fact -case class StaticField(classType: ObjectType, fieldName: String) extends Fact -case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends Fact -case class FlowFact(flow: ListSet[Method]) extends Fact { - // ListSet is only meant for VERY SMALL sets, but this seems to be ok here! - - override val hashCode: Int = { - // HERE, a foldLeft introduces a lot of overhead due to (un)boxing. - var r = 1 - flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) - r - } -} -case object NullFact extends Fact with AbstractIFDSNullFact - -/** - * A simple IFDS taint analysis. - * - * @author Dominik Helm - * @author Mario Trageser - * @author Michael Eichberg - */ -class BasicIFDSTaintAnalysis private ( - implicit - val project: SomeProject -) extends AbstractIFDSAnalysis[Fact] { - - override val propertyKey: IFDSPropertyMetaInformation[Fact] = Taint - - override def createPropertyValue(result: Map[Statement, Set[Fact]]): IFDSProperty[Fact] = { - new Taint(result) - } - - override def normalFlow(stmt: Statement, succ: Statement, in: Set[Fact]): Set[Fact] = - stmt.stmt.astID match { - case Assignment.ASTID ⇒ - handleAssignment(stmt, stmt.stmt.asAssignment.expr, in) - - /*case ArrayStore.ASTID ⇒ - val store = stmt.stmt.asArrayStore - val definedBy = store.arrayRef.asVar.definedBy - val index = getConstValue(store.index, stmt.code) - if (isTainted(store.value, in)) - if (index.isDefined) // Taint known array index - // Instead of using an iterator, we are going to use internal iteration - // in ++ definedBy.iterator.map(ArrayElement(_, index.get)) - definedBy.foldLeft(in) { (c, n) ⇒ c + ArrayElement(n, index.get) } - else // Taint whole array if index is unknown - // Instead of using an iterator, we are going to use internal iteration: - // in ++ definedBy.iterator.map(Variable) - definedBy.foldLeft(in) { (c, n) ⇒ c + Variable(n) } - else in*/ - - case PutStatic.ASTID ⇒ - val put = stmt.stmt.asPutStatic - if (isTainted(put.value, in)) - in + StaticField(put.declaringClass, put.name) - else - in - - /*case PutField.ASTID ⇒ - val put = stmt.stmt.asPutField - if (isTainted(put.value, in)) in + StaticField(put.declaringClass, put.name) - else in*/ - case PutField.ASTID ⇒ - val put = stmt.stmt.asPutField - val definedBy = put.objRef.asVar.definedBy - if (isTainted(put.value, in)) - definedBy.foldLeft(in) { (in, defSite) ⇒ - in + InstanceField(defSite, put.declaringClass, put.name) - } - else - in - - case _ ⇒ in - } - - /** - * Returns true if the expression contains a taint. - */ - def isTainted(expr: Expr[V], in: Set[Fact]): Boolean = { - expr.isVar && in.exists { - case Variable(index) ⇒ expr.asVar.definedBy.contains(index) - //case ArrayElement(index, _) ⇒ expr.asVar.definedBy.contains(index) - case InstanceField(index, _, _) ⇒ expr.asVar.definedBy.contains(index) - case _ ⇒ false - } - } - - /** - * Returns the constant int value of an expression if it exists, None otherwise. - */ - /*def getConstValue(expr: Expr[V], code: Array[Stmt[V]]): Option[Int] = { - if (expr.isIntConst) Some(expr.asIntConst.value) - else if (expr.isVar) { - // TODO The following looks optimizable! - val constVals = expr.asVar.definedBy.iterator.map[Option[Int]] { idx ⇒ - if (idx >= 0) { - val stmt = code(idx) - if (stmt.astID == Assignment.ASTID && stmt.asAssignment.expr.isIntConst) - Some(stmt.asAssignment.expr.asIntConst.value) - else - None - } else None - }.toIterable - if (constVals.forall(option ⇒ option.isDefined && option.get == constVals.head.get)) - constVals.head - else None - } else None - }*/ - - def handleAssignment(stmt: Statement, expr: Expr[V], in: Set[Fact]): Set[Fact] = - expr.astID match { - case Var.ASTID ⇒ - // This path is not used if the representation is in standard SSA-like form. - // It is NOT optimized! - val newTaint = in.collect { - case Variable(index) if expr.asVar.definedBy.contains(index) ⇒ - Some(Variable(stmt.index)) - /*case ArrayElement(index, taintIndex) if expr.asVar.definedBy.contains(index) ⇒ - Some(ArrayElement(stmt.index, taintIndex))*/ - case _ ⇒ None - }.flatten - in ++ newTaint - - /*case ArrayLoad.ASTID ⇒ - val load = expr.asArrayLoad - if (in.exists { - // The specific array element may be tainted - case ArrayElement(index, taintedIndex) ⇒ - val element = getConstValue(load.index, stmt.code) - load.arrayRef.asVar.definedBy.contains(index) && - (element.isEmpty || taintedIndex == element.get) - // Or the whole array - case Variable(index) ⇒ load.arrayRef.asVar.definedBy.contains(index) - case _ ⇒ false - }) - in + Variable(stmt.index) - else - in*/ - - case GetStatic.ASTID ⇒ - val get = expr.asGetStatic - if (in.contains(StaticField(get.declaringClass, get.name))) - in + Variable(stmt.index) - else - in - - /*case GetField.ASTID ⇒ - val get = expr.asGetField - if (in.contains(StaticField(get.declaringClass, get.name))) - in + Variable(stmt.index) - else in*/ - case GetField.ASTID ⇒ - val get = expr.asGetField - if (in.exists { - // The specific field may be tainted - case InstanceField(index, _, taintedField) ⇒ - taintedField == get.name && get.objRef.asVar.definedBy.contains(index) - // Or the whole object - case Variable(index) ⇒ get.objRef.asVar.definedBy.contains(index) - case _ ⇒ false - }) - in + Variable(stmt.index) - else - in - - case _ ⇒ in - } - - override def callFlow( - stmt: Statement, - callee: DeclaredMethod, - in: Set[Fact] - ): Set[Fact] = { - val call = asCall(stmt.stmt) - val allParams = call.allParams - if (callee.name == "sink") { - if (in.exists { - case Variable(index) ⇒ allParams.exists(p ⇒ p.asVar.definedBy.contains(index)) - case _ ⇒ false - }) { - println(s"Found flow: $stmt") - } - } else if (callee.name == "forName" && (callee.declaringClassType eq ObjectType.Class) && - callee.descriptor.parameterTypes == RefArray(ObjectType.String)) { - if (in.exists { - case Variable(index) ⇒ call.params.exists(p ⇒ p.asVar.definedBy.contains(index)) - case _ ⇒ false - }) { - println(s"Found flow: $stmt") - } - } - if (true || (callee.descriptor.returnType eq ObjectType.Class) || - (callee.descriptor.returnType eq ObjectType.Object) || - (callee.descriptor.returnType eq ObjectType.String)) { - var facts = Set.empty[Fact] - in.foreach { - case Variable(index) ⇒ // Taint formal parameter if actual parameter is tainted - allParams.iterator.zipWithIndex.foreach { - case (param, pIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += Variable(paramToIndex(pIndex, !callee.definedMethod.isStatic)) - case _ ⇒ // Nothing to do - } - - /*case ArrayElement(index, taintedIndex) ⇒ - // Taint element of formal parameter if element of actual parameter is tainted - allParams.zipWithIndex.collect { - case (param, pIndex) if param.asVar.definedBy.contains(index) ⇒ - ArrayElement(paramToIndex(pIndex, !callee.definedMethod.isStatic), taintedIndex) - }*/ - - case InstanceField(index, declClass, taintedField) ⇒ - // Taint field of formal parameter if field of actual parameter is tainted - // Only if the formal parameter is of a type that may have that field! - allParams.iterator.zipWithIndex.foreach { - case (param, pIndex) if param.asVar.definedBy.contains(index) && - (paramToIndex(pIndex, !callee.definedMethod.isStatic) != -1 || - classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ - facts += InstanceField(paramToIndex(pIndex, !callee.definedMethod.isStatic), declClass, taintedField) - case _ ⇒ // Nothing to do - } - - case sf: StaticField ⇒ - facts += sf - - case _ ⇒ // Nothing to do - } - facts - } else Set.empty - } - - override def returnFlow( - stmt: Statement, - callee: DeclaredMethod, - exit: Statement, - succ: Statement, - in: Set[Fact] - ): Set[Fact] = { - if (callee.name == "source" && stmt.stmt.astID == Assignment.ASTID) - Set(Variable(stmt.index)) - else if (callee.name == "sanitize") - Set.empty - else { - val call = asCall(stmt.stmt) - val allParams = call.allParams - var flows: Set[Fact] = Set.empty - in.foreach { - /*case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ - // Taint element of actual parameter if element of formal parameter is tainted - val param = - allParams(paramToIndex(index, !callee.definedMethod.isStatic)) - flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex))*/ - - case InstanceField(index, declClass, taintedField) if index < 0 && index > -255 ⇒ - // Taint field of actual parameter if field of formal parameter is tainted - val param = allParams(paramToIndex(index, !callee.definedMethod.isStatic)) - param.asVar.definedBy.foreach { defSite ⇒ - flows += InstanceField(defSite, declClass, taintedField) - } - - case sf: StaticField ⇒ - flows += sf - - case FlowFact(flow) ⇒ - val newFlow = flow + stmt.method - if (entryPoints.contains(declaredMethods(exit.method))) { - //println(s"flow: "+newFlow.map(_.toJava).mkString(", ")) - } else { - flows += FlowFact(newFlow) - } - - case _ ⇒ - } - - // Propagate taints of the return value - if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { - val returnValue = exit.stmt.asReturnValue.expr.asVar - in.foreach { - case Variable(index) if returnValue.definedBy.contains(index) ⇒ - flows += Variable(stmt.index) - /*case ArrayElement(index, taintedIndex) if returnValue.definedBy.contains(index) ⇒ - ArrayElement(stmt.index, taintedIndex)*/ - case InstanceField(index, declClass, taintedField) if returnValue.definedBy.contains(index) ⇒ - flows += InstanceField(stmt.index, declClass, taintedField) - - case _ ⇒ // nothing to do - } - } - - flows - } - } - - /** - * Converts a parameter origin to the index in the parameter seq (and vice-versa). - */ - def paramToIndex(param: Int, includeThis: Boolean): Int = (if (includeThis) -1 else -2) - param - - override def callToReturnFlow(stmt: Statement, succ: Statement, in: Set[Fact]): Set[Fact] = { - val call = asCall(stmt.stmt) - if (call.name == "sanitize") { - // This branch is only used by the test cases and therefore NOT optimized! - in.filter { - case Variable(index) ⇒ - !(call.params ++ call.receiverOption).exists { p ⇒ - val definedBy = p.asVar.definedBy - definedBy.size == 1 && definedBy.contains(index) - } - case _ ⇒ true - } - } else if (call.name == "forName" && - (call.declaringClass eq ObjectType.Class) && - call.descriptor.parameterTypes == RefArray(ObjectType.String)) { - if (in.exists { - case Variable(index) ⇒ call.params.exists(p ⇒ p.asVar.definedBy.contains(index)) - case _ ⇒ false - }) { - /*if (entryPoints.contains(declaredMethods(stmt.method))) { - println(s"flow: "+stmt.method.toJava) - in - } else*/ - in + FlowFact(ListSet(stmt.method)) - } else { - in - } - } else { - in - } - } - - /** - * If forName is called, we add a FlowFact. - */ - override def nativeCall(statement: Statement, callee: DeclaredMethod, successor: Statement, in: Set[Fact]): Set[Fact] = { - /* val allParams = asCall(statement.stmt).allParams - if (statement.stmt.astID == Assignment.ASTID && in.exists { - case Variable(index) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - } - /*case ArrayElement(index, _) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - }*/ - case _ ⇒ false - }) Set(Variable(statement.index)) - else*/ Set.empty - } - - val entryPoints: Map[DeclaredMethod, Fact] = (for { - m ← p.allMethodsWithBody - if (m.isPublic || m.isProtected) && (m.descriptor.returnType == ObjectType.Object || m.descriptor.returnType == ObjectType.Class) - index ← m.descriptor.parameterTypes.zipWithIndex.collect { case (pType, index) if pType == ObjectType.String ⇒ index } - } //yield (declaredMethods(m), null) - yield declaredMethods(m) → Variable(-2 - index)).toMap - -} - -object BasicIFDSTaintAnalysis extends IFDSAnalysis[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new BasicIFDSTaintAnalysis()(p) - - override def property: IFDSPropertyMetaInformation[Fact] = Taint -} - -class Taint(val flows: Map[Statement, Set[Fact]]) extends IFDSProperty[Fact] { - - override type Self = Taint - - def key: PropertyKey[Taint] = Taint.key -} - -object Taint extends IFDSPropertyMetaInformation[Fact] { - override type Self = Taint - - val key: PropertyKey[Taint] = PropertyKey.create( - "TestTaint", - new Taint(Map.empty) - ) -} - -object BasicIFDSTaintAnalysisRunner { - - def main(args: Array[String]): Unit = { - if (args.contains("--help")) { - println("Potential parameters:") - println(" -cp=/Path/To/Project (to analyze the given project instead of the JDK)") - println(" -cg=/Path/To/CallGraph (to use a serialized call graph instead of a new RTA)") - println(" -seq (to use the SequentialPropertyStore)") - println(" -l2 (to use the l2 domain instead of the default l1 domain)") - println(" -primitive (to use the primitive l0 domain instead of the default l1 domain)") - println(" -delay (for a three seconds delay before the taint flow analysis is started)") - println(" -debug (to set the PropertyStore's debug flag)") - println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") - } - - if (args.contains("-debug")) { - PropertyStore.updateDebug(true) - } - - val cpArg = args.find(_.startsWith("-cp=")) - val p = - if (cpArg.isDefined) - Project(new File(cpArg.get.substring(4))) - else - Project(JRELibraryFolder) - - def evalProject(p: SomeProject): Seconds = { - if (args.contains("-l2")) { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ - Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) - case Some(requirements) ⇒ - requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] - } - } else if (args.contains("-primitive")) { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ Set(classOf[PrimitiveTACAIDomain]) - case Some(requirements) ⇒ requirements + classOf[PrimitiveTACAIDomain] - } - } else { - p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ - Set(classOf[l1.DefaultDomainWithCFGAndDefUse[_]]) - case Some(requirements) ⇒ - requirements + classOf[l1.DefaultDomainWithCFGAndDefUse[_]] - } - } - - val ps = p.get(PropertyStoreKey) - val manager = p.get(FPCFAnalysesManagerKey) - var analysisTime: Seconds = Seconds.None - - val cgArg = args.find(_.startsWith("-cg=")) - PerformanceEvaluation.time { - if (cgArg.isDefined) - manager.runAll( - new CallGraphDeserializerScheduler(new File(cgArg.get.substring(4))) - ) - else - p.get(RTACallGraphKey) - } { t ⇒ println(s"CG took ${t.toSeconds}s.") } - - println("Start: "+new java.util.Date) - val analyses = time { - manager.runAll(BasicIFDSTaintAnalysis) - }(t ⇒ analysisTime = t.toSeconds)._2 - - val entryPoints = - analyses.collect { case (_, a: BasicIFDSTaintAnalysis) ⇒ a.entryPoints }.head - for { - e ← entryPoints - flows = ps(e, BasicIFDSTaintAnalysis.property.key) - fact ← flows.ub.asInstanceOf[IFDSProperty[Fact]].flows.values.flatten.toSet[Fact] - } { - fact match { - case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.toJava).mkString(", ")) - case _ ⇒ - } - } - println(s"The analysis took $analysisTime.") - println( - ps.statistics.iterator.map(_.toString()).toList - .sorted - .mkString("PropertyStore Statistics:\n\t", "\n\t", "\n") - ) - - analysisTime - } - - p.getOrCreateProjectInformationKeyInitializationData( - PropertyStoreKey, - (context: List[PropertyStoreContext[AnyRef]]) ⇒ { - implicit val lg: LogContext = p.logContext - val ps = - if (args.contains("-seq")) - PKESequentialPropertyStore.apply(context: _*) - else - PKESequentialPropertyStore.apply(context: _*) // TODO exchange for a concurrent one once one exists - ps - } - ) - - if (args.contains("-delay")) { - println("Sleeping for three seconds.") - Thread.sleep(3000) - } - - if (args.contains("-evalSchedulingStrategies")) { - val results = for { - i ← 1 to 2 - strategy ← PKESequentialPropertyStore.Strategies - } yield { - println(s"Round: $i - $strategy") - val strategyValue = ConfigValueFactory.fromAnyRef(strategy) - val newConfig = - p.config.withValue(PKESequentialPropertyStore.TasksManagerKey, strategyValue) - val analysisTime = evalProject(Project.recreate(p, newConfig)) - (i, strategy, analysisTime) - org.opalj.util.gc() - } - println(results.mkString("AllResults:\n\t", "\n\t", "\n")) - } else { - evalProject(p) - } - } -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala new file mode 100644 index 0000000000..26a37f1cda --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala @@ -0,0 +1,68 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses + +import java.io.File + +import org.opalj.tac.fpcf.analyses.heros.analyses.taint.HerosBackwardClassForNameAnalysisRunner +import org.opalj.tac.fpcf.analyses.heros.analyses.taint.HerosForwardClassForNameAnalysisRunner +import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis +import org.opalj.tac.fpcf.analyses.heros.analyses.HerosVariableTypeAnalysisRunner +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysis +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisRunner +import org.opalj.tac.fpcf.analyses.taint.BackwardClassForNameTaintAnalysisRunner +import org.opalj.tac.fpcf.analyses.taint.ForwardClassForNameAnalysisRunner + +/** + * Generates some evaluation files related to the AbstractIFDSAnalysis. + * + * @author Mario Trageser + */ +object IFDSEvaluation { + + /** + * args should contain exactly one parameter: + * A directory, which will contain the evaluation files; terminated with a '/'. + * + */ + def main(args: Array[String]): Unit = { + val dir = args(0) + new File(dir).mkdirs + + // Evaluation of AbstractIFDSAnalysis + ForwardClassForNameAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"ForwardClassForNameAnalysis.txt")) + BackwardClassForNameTaintAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"BackwardClassForNameAnalysis.txt")) + IFDSBasedVariableTypeAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"IFDSBasedVariableTypeAnalysis.txt")) + + // Evaluation of cross product split in returnFlow + AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW = false + ForwardClassForNameAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"ForwardClassForNameAnalysisWithFullCrossProduct.txt")) + BackwardClassForNameTaintAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"BackwardClassForNameAnalysisWithFullCrossProduct.txt")) + AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW = true + + // Evaluation of subsuming + IFDSBasedVariableTypeAnalysis.SUBSUMING = false + IFDSBasedVariableTypeAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"NoSubsuming.txt")) + IFDSBasedVariableTypeAnalysis.SUBSUMING = true + + // Evaluation of Heros analyses + HerosForwardClassForNameAnalysisRunner.main(Array("-f", dir+"HerosForwardClassForNameAnalysis.txt")) + HerosBackwardClassForNameAnalysisRunner.main(Array("-f", dir+"HerosBackwardClassForNameAnalysis.txt")) + HerosVariableTypeAnalysisRunner.main(Array("-f", dir+"HerosVariableTypeAnalysis.txt")) + + // Evaluation of parallel Heros analyses + HerosAnalysis.NUM_THREADS = 6 + HerosForwardClassForNameAnalysisRunner.main(Array("-f", dir+"ParallelHerosForwardClassForNameAnalysis.txt")) + HerosBackwardClassForNameAnalysisRunner.main(Array("-f", dir+"ParallelHerosBackwardClassForNameAnalysis.txt")) + HerosVariableTypeAnalysisRunner.main(Array("-f", dir+"ParallelHerosVariableTypeAnalysis.txt")) + HerosAnalysis.NUM_THREADS = 1 + + // Evaluation of the scheduling strategies + IFDSBasedVariableTypeAnalysis.SUBSUMING = false + ForwardClassForNameAnalysisRunner.main(Array("-evalSchedulingStrategies", "-seq", "-l2", "-f", dir+"SchedulingForwardClassForNameAnalysis.txt")) + BackwardClassForNameTaintAnalysisRunner.main(Array("-evalSchedulingStrategies", "-seq", "-l2", "-f", dir+"SchedulingBackwardClassForNameAnalysis.txt")) + IFDSBasedVariableTypeAnalysisRunner.main(Array("-evalSchedulingStrategies", "-seq", "-l2", "-f", dir+"SchedulingIFDSBasedVariableTypeAnalysis.txt")) + IFDSBasedVariableTypeAnalysis.SUBSUMING = true + } + +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala new file mode 100644 index 0000000000..b069c620ec --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala @@ -0,0 +1,175 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.heros.analyses + +import java.io.File +import java.io.PrintWriter + +import heros.template.DefaultIFDSTabulationProblem +import heros.solver.IFDSSolver +import javax.swing.JOptionPane +import org.opalj.bytecode + +import org.opalj.util.Milliseconds +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.PropertyStore +import org.opalj.br.analyses.SomeProject +import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.fpcf.analyses.heros.cfg.OpalICFG +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.Assignment +import org.opalj.tac.Call +import org.opalj.tac.ExprStmt +import org.opalj.tac.Stmt +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V + +/** + * A common subclass of all Heros analyses. + * + * @param p The project, which will be analyzed. + * @param icfg The project's control flow graph. + * @tparam F The type of data flow facts of the analysis. + * @author Mario Trageser + */ +abstract class HerosAnalysis[F](p: SomeProject, icfg: OpalICFG) + extends DefaultIFDSTabulationProblem[Statement, F, Method, OpalICFG](icfg) { + + /** + * The project's property store. + */ + protected implicit val propertyStore: PropertyStore = p.get(PropertyStoreKey) + + /** + * The project's declared methods. + */ + protected implicit val declaredMethods: DeclaredMethods = p.get(DeclaredMethodsKey) + + /** + * The number of threads can be configured with NUM_THREADS. + */ + override def numThreads(): Int = HerosAnalysis.NUM_THREADS + + /** + * Gets the Call for a statement that contains a call (MethodCall Stmt or ExprStmt/Assigment + * with FunctionCall) + */ + protected def asCall(stmt: Stmt[V]): Call[V] = stmt.astID match { + case Assignment.ASTID ⇒ stmt.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID ⇒ stmt.asExprStmt.expr.asFunctionCall + case _ ⇒ stmt.asMethodCall + } +} + +object HerosAnalysis { + + /** + * The number of threads, with which analyses should run. + */ + var NUM_THREADS = 1 + + /** + * If this flag is set, a JOptionPane is displayed before and after each analysis run in + * evalProject. By doing so, it is easier to measure the memory consumption with VisualVM. + */ + var MEASURE_MEMORY = false + + /** + * Checks, if some method can be called from outside. + * + * @param method The method. + * @return True, if the method can be called from outside. + */ + def canBeCalledFromOutside(method: Method)(implicit declaredMethods: DeclaredMethods, propertyStore: PropertyStore): Boolean = { + val FinalEP(_, callers) = propertyStore(declaredMethods(method), Callers.key) + callers.hasCallersWithUnknownContext + } +} + +/** + * An abstract runner for Heros analyses. + * + * @tparam F The type of data flow facts of the analysis. + * @tparam Analysis The analysis, which will be executed. + * @author Mario Trageser + */ +abstract class HerosAnalysisRunner[F, Analysis <: HerosAnalysis[F]] { + + /** + * Creates the analysis, which will be executed. + * + * @param p The project, which will be analyzed. + * @return The analysis, which will be executed. + */ + protected def createAnalysis(p: SomeProject): Analysis + + /** + * Prints the analysis results to the console. + * + * @param analysis The analysis, which was executed. + * @param analysisTime The time, the analysis needed, in milliseconds. + */ + protected def printResultsToConsole(analysis: Analysis, analysisTime: Milliseconds): Unit + + /** + * Executes the analysis NUM_EXECUTIONS times, prints the analysis results to the console + * and saves evaluation data in evaluationFile, if present. + * + * @param evaluationFile Evaluation data will be saved in this file, if present. + */ + def run(evaluationFile: Option[File]): Unit = { + val p = Project(bytecode.RTJar) + var times = Seq.empty[Milliseconds] + for { + _ ← 1 to HerosAnalysisRunner.NUM_EXECUTIONS + } { + times :+= evalProject(Project.recreate(p)) + } + if (evaluationFile.isDefined) { + val pw = new PrintWriter(evaluationFile.get) + pw.println(s"Average time: ${times.map(_.timeSpan).sum / times.size}ms") + pw.close() + } + } + + /** + * Executes the analysis. + * + * @param p The project, which will be analyzed. + * @return The time, the analysis needed. + */ + private def evalProject(p: SomeProject): Milliseconds = { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) ⇒ requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] + } + p.get(RTACallGraphKey) + val analysis = createAnalysis(p) + val solver = new IFDSSolver(analysis) + var analysisTime: Milliseconds = Milliseconds.None + if (HerosAnalysis.MEASURE_MEMORY) + JOptionPane.showMessageDialog(null, "Call Graph finished") + time { + solver.solve() + } { t ⇒ analysisTime = t.toMilliseconds } + if (HerosAnalysis.MEASURE_MEMORY) + JOptionPane.showMessageDialog(null, "Analysis finished") + printResultsToConsole(analysis, analysisTime) + analysisTime + } +} + +object HerosAnalysisRunner { + + /** + * The number of analysis runs, which will be performed by the run method. + */ + var NUM_EXECUTIONS = 10 +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala new file mode 100644 index 0000000000..d11ed3f00a --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala @@ -0,0 +1,246 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.heros.analyses + +import scala.annotation.tailrec + +import java.io.File +import java.util +import java.util.Collections + +import scala.collection.JavaConverters._ + +import heros.FlowFunctions +import heros.FlowFunction +import heros.flowfunc.Identity +import heros.TwoElementSet +import heros.flowfunc.KillAll + +import org.opalj.util.Milliseconds +import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG +import org.opalj.collection.immutable.EmptyIntTrieSet +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.PropertyStore +import org.opalj.value.ValueInformation +import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.SomeProject +import org.opalj.br.ArrayType +import org.opalj.br.FieldType +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.DeclaredMethod +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.Assignment +import org.opalj.tac.Expr +import org.opalj.tac.GetStatic +import org.opalj.tac.New +import org.opalj.tac.DUVar +import org.opalj.tac.ArrayLoad +import org.opalj.tac.ArrayStore +import org.opalj.tac.GetField +import org.opalj.tac.Var +import org.opalj.tac.ReturnValue +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.CalleeType +import org.opalj.tac.fpcf.analyses.ifds.VariableType +import org.opalj.tac.fpcf.analyses.ifds.VTAFact +import org.opalj.tac.fpcf.analyses.ifds.VTANullFact + +/** + * An implementation of the IFDSBasedVariableTypeAnalysis in the Heros framework. + * For a documentation of the data flow functions, see IFDSBasedVariableTypeAnalysis. + * + * @author Mario Trageser + */ +class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMethods: Map[Method, util.Set[VTAFact]]) + extends HerosAnalysis[VTAFact](p, icfg) { + + override val initialSeeds: util.Map[Statement, util.Set[VTAFact]] = { + var result: Map[Statement, util.Set[VTAFact]] = Map.empty + for ((m, facts) ← initialMethods) { + result += icfg.getStartPointsOf(m).iterator().next() → facts + } + result.asJava + } + + override def createZeroValue(): VTAFact = VTANullFact + + override protected def createFlowFunctionsFactory(): FlowFunctions[Statement, VTAFact, Method] = { + + new FlowFunctions[Statement, VTAFact, Method]() { + + override def getNormalFlowFunction(statement: Statement, succ: Statement): FlowFunction[VTAFact] = { + if (!insideAnalysisContext(statement.method)) return KillAll.v() + val stmt = statement.stmt + stmt.astID match { + case Assignment.ASTID ⇒ + (source: VTAFact) ⇒ { + val fact = newFact(statement.method, statement.stmt.asAssignment.expr, + statement.index, source) + if (fact.isDefined) TwoElementSet.twoElementSet(source, fact.get) + else Collections.singleton(source) + } + case ArrayStore.ASTID ⇒ + (source: VTAFact) ⇒ { + val flow = scala.collection.mutable.Set.empty[VTAFact] + flow += source + newFact(statement.method, stmt.asArrayStore.value, statement.index, + source).foreach { + case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) ⇒ + stmt.asArrayStore.arrayRef.asVar.definedBy + .foreach(flow += VariableType(_, ArrayType(t), upperBound)) + case _ ⇒ // Nothing to do + } + flow.asJava + } + case _ ⇒ Identity.v() + } + } + + override def getCallFlowFunction(stmt: Statement, callee: Method): FlowFunction[VTAFact] = + if (!insideAnalysisContext(callee)) KillAll.v() + else { + val callObject = asCall(stmt.stmt) + val allParams = callObject.allParams + source: VTAFact ⇒ { + val flow = scala.collection.mutable.Set.empty[VTAFact] + source match { + case VariableType(definedBy, t, upperBound) ⇒ + allParams.iterator.zipWithIndex.foreach { + case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ + flow += VariableType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(parameterIndex, callee.isStatic), + t, upperBound + ) + case _ ⇒ + } + case _ ⇒ + } + flow.asJava + } + } + + override def getReturnFlowFunction(stmt: Statement, callee: Method, exit: Statement, succ: Statement): FlowFunction[VTAFact] = + if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { + val returnValue = exit.stmt.asReturnValue.expr.asVar + source: VTAFact ⇒ { + source match { + // If we know the type of the return value, we create a fact for the assigned variable. + case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) ⇒ + Collections.singleton(VariableType(stmt.index, t, upperBound)) + case _ ⇒ Collections.emptySet() + } + } + } else KillAll.v() + + override def getCallToReturnFlowFunction(statement: Statement, succ: Statement): FlowFunction[VTAFact] = { + if (!insideAnalysisContext(statement.method)) return KillAll.v() + val stmt = statement.stmt + val calleeDefinitionSites = asCall(stmt).receiverOption + .map(callee ⇒ callee.asVar.definedBy).getOrElse(EmptyIntTrieSet) + val callOutsideOfAnalysisContext = + getCallees(statement).exists(method ⇒ + !((method.hasSingleDefinedMethod || method.hasMultipleDefinedMethods) && insideAnalysisContext(method.definedMethod))) + source: VTAFact ⇒ { + val result = scala.collection.mutable.Set[VTAFact](source) + source match { + case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) ⇒ + result += CalleeType(statement.index, t, upperBound) + case _ ⇒ + } + if (callOutsideOfAnalysisContext) { + val returnType = asCall(stmt).descriptor.returnType + if (stmt.astID == Assignment.ASTID && returnType.isReferenceType) { + result += VariableType(statement.index, returnType.asReferenceType, upperBound = true) + } + } + result.asJava + } + } + } + } + + private def newFact(method: Method, expression: Expr[DUVar[ValueInformation]], + statementIndex: Int, + source: VTAFact): Option[VariableType] = expression.astID match { + case New.ASTID ⇒ source match { + case VTANullFact ⇒ + Some(VariableType(statementIndex, expression.asNew.tpe, upperBound = false)) + case _ ⇒ None + } + case Var.ASTID ⇒ source match { + case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ + Some(VariableType(statementIndex, t, upperBound)) + case _ ⇒ None + } + case ArrayLoad.ASTID ⇒ source match { + case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && + expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ + Some(VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound)) + case _ ⇒ None + } + case GetField.ASTID | GetStatic.ASTID ⇒ + val t = expression.asFieldRead.declaredFieldType + if (t.isReferenceType) + Some(VariableType(statementIndex, t.asReferenceType, upperBound = true)) + else None + case _ ⇒ None + } + + @tailrec private def isArrayOfObjectType(t: FieldType, includeObjectType: Boolean = false): Boolean = { + if (t.isArrayType) isArrayOfObjectType(t.asArrayType.elementType, includeObjectType = true) + else if (t.isObjectType && includeObjectType) true + else false + } + + private def insideAnalysisContext(callee: Method): Boolean = + callee.body.isDefined && (callee.classFile.fqn.startsWith("java/lang") || + callee.classFile.fqn.startsWith("org/opalj/fpcf/fixtures/vta")) + + private def getCallees(statement: Statement): Iterator[DeclaredMethod] = { + val FinalEP(_, callees) = propertyStore(declaredMethods(statement.method), Callees.key) + callees.directCallees(statement.stmt.pc) + } + +} + +class HerosVariableTypeAnalysisRunner extends HerosAnalysisRunner[VTAFact, HerosVariableTypeAnalysis] { + + override protected def createAnalysis(p: SomeProject): HerosVariableTypeAnalysis = { + implicit val declaredMethods: DeclaredMethods = p.get(DeclaredMethodsKey) + implicit val propertyStore: PropertyStore = p.get(PropertyStoreKey) + val initialMethods = + p.allProjectClassFiles.filter(_.fqn.startsWith("java/lang")) + .flatMap(classFile ⇒ classFile.methods) + .filter(isEntryPoint) + .map(method ⇒ method → entryPointsForMethod(method).asJava).toMap + new HerosVariableTypeAnalysis(p, new OpalForwardICFG(p), initialMethods) + } + + override protected def printResultsToConsole(analysis: HerosVariableTypeAnalysis, analysisTime: Milliseconds): Unit = {} + + private def isEntryPoint(method: Method)(implicit declaredMethods: DeclaredMethods, propertyStore: PropertyStore): Boolean = { + method.body.isDefined && HerosAnalysis.canBeCalledFromOutside(method) + } + + private def entryPointsForMethod(method: Method): Set[VTAFact] = { + (method.descriptor.parameterTypes.zipWithIndex.collect { + case (t, index) if t.isReferenceType ⇒ + VariableType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, method.isStatic), + t.asReferenceType, upperBound = true + ) + } :+ VTANullFact).toSet + } + +} + +object HerosVariableTypeAnalysisRunner { + def main(args: Array[String]): Unit = { + val fileIndex = args.indexOf("-f") + new HerosVariableTypeAnalysisRunner().run( + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala new file mode 100644 index 0000000000..5184750669 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala @@ -0,0 +1,224 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.heros.analyses + +import java.io.File +import java.net.URL + +import scala.collection.JavaConverters._ + +import com.typesafe.config.Config +import com.typesafe.config.ConfigValueFactory +import heros.solver.IFDSSolver +import org.opalj.BaseConfig + +import org.opalj.util.ScalaMajorVersion +import org.opalj.fpcf.EPS +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.PropertyStore +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.DefinedMethod +import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.cg.InitialEntryPointsKey +import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG +import org.opalj.tac.fpcf.analyses.ifds.CalleeType +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysis +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.VariableType +import org.opalj.tac.fpcf.analyses.ifds.VTAFact +import org.opalj.tac.fpcf.analyses.ifds.VTANullFact +import org.opalj.tac.fpcf.analyses.ifds.VTAResult +import org.opalj.tac.Assignment +import org.opalj.tac.New +import org.opalj.tac.Return +import org.opalj.tac.ReturnValue + +object VTAEquality { + + def main(args: Array[String]): Unit = { + val herosSolver = performHerosAnalysis + val opalResult = performOpalAnalysis + + opalResult.keys.foreach { + /* + * Heros only gives us results for call statements. + * Therefore, we cannot consider return statements. + * Heros also gives us the facts before the call statements, while Opal gives us the facts after. + * Therefore, we cannot consider assignments of objects, which were instantiated using a constructor, + * because the VariableTypeFact holds after the statement. + */ + case statement if statement.stmt.astID != Return.ASTID && statement.stmt.astID != ReturnValue.ASTID + && (statement.stmt.astID != Assignment.ASTID || statement.stmt.asAssignment.expr.astID != New.ASTID) ⇒ + val opalFacts = opalResult(statement) + /* + * ifdsResultsAt returns the facts before the statements. + * However, CalleeType facts hold after the statement and are therefore not returned. + * We do not consider them in this test. + */ + val herosFacts = + herosSolver.ifdsResultsAt(statement).asScala.filter(!_.isInstanceOf[CalleeType]) + if (opalFacts.size != herosFacts.size) { + println("Error: Different number of facts:") + println(s"Statement: $statement") + println(s"Opal: $opalFacts") + println(s"Heros: $herosFacts") + println + } else + opalFacts.filter(!herosFacts.contains(_)).foreach { fact ⇒ + println("Error: Heros fact missing:") + println(s"Statement: $statement") + println(s"Fact: $fact") + println(s"Opal: $opalFacts") + println(s"Heros: $herosFacts") + println + } + case _ ⇒ + } + } + + private def performHerosAnalysis = { + val project = FixtureProject.recreate { piKeyUnidueId ⇒ + piKeyUnidueId != PropertyStoreKey.uniqueId + } + implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) ⇒ + requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] + } + project.get(RTACallGraphKey) + val initialMethods = + project.allProjectClassFiles + .filter(_.fqn.startsWith("org/opalj/fpcf/fixtures/vta")) + .flatMap(classFile ⇒ classFile.methods) + .filter(isEntryPoint) + .map(method ⇒ method -> entryPointsForMethod(method).asJava) + .toMap + val cgf = new OpalForwardICFG(project) + val herosAnalysis = new HerosVariableTypeAnalysis(project, cgf, initialMethods) + val herosSolver = new IFDSSolver(herosAnalysis) + herosSolver.solve() + herosSolver + } + + private def performOpalAnalysis = { + val project = FixtureProject.recreate { piKeyUnidueId ⇒ + piKeyUnidueId != PropertyStoreKey.uniqueId + } + val propertyStore = project.get(PropertyStoreKey) + var result = Map.empty[Statement, Set[VTAFact]] + project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) ⇒ + requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] + } + project.get(RTACallGraphKey) + project.get(FPCFAnalysesManagerKey).runAll(IFDSBasedVariableTypeAnalysis) + propertyStore + .entities(IFDSBasedVariableTypeAnalysis.property.key) + .collect { + case EPS((m: DefinedMethod, inputFact)) ⇒ + (m, inputFact) + } + .foreach { entity ⇒ + val entityResult = propertyStore(entity, IFDSBasedVariableTypeAnalysis.property.key) match { + case FinalEP(_, VTAResult(map)) ⇒ map + case _ ⇒ throw new RuntimeException + } + entityResult.keys.foreach { statement ⇒ + /* + * Heros returns the facts before the statements. + * However, CalleeType facts hold after the statement and are therefore not returned. + * We do not consider them in this test. + * Additionally, Heros does not return null facts, so we filter them. + */ + result = result.updated( + statement, + result.getOrElse(statement, Set.empty) ++ entityResult(statement) + .filter(fact ⇒ fact != VTANullFact && !fact.isInstanceOf[CalleeType]) + ) + } + } + result + } + + private def isEntryPoint( + method: Method + )(implicit declaredMethods: DeclaredMethods, propertyStore: PropertyStore): Boolean = { + val declaredMethod = declaredMethods(method) + val FinalEP(_, callers) = propertyStore(declaredMethod, Callers.key) + method.body.isDefined && callers.hasCallersWithUnknownContext + } + + private def entryPointsForMethod(method: Method): Set[VTAFact] = { + (method.descriptor.parameterTypes.zipWithIndex.collect { + case (t, index) if t.isReferenceType ⇒ + VariableType( + switchParamAndVariableIndex(index, method.isStatic), + t.asReferenceType, + upperBound = true + ) + } :+ VTANullFact).toSet + } + + private def switchParamAndVariableIndex(index: Int, isStaticMethod: Boolean): Int = + (if (isStaticMethod) -2 else -1) - index + + final val FixtureProject: Project[URL] = { + val classFileReader = Project.JavaClassFileReader() + import classFileReader.ClassFiles + val sourceFolder = s"DEVELOPING_OPAL/validate/target/scala-$ScalaMajorVersion/test-classes" + val fixtureFiles = new File(sourceFolder) + val fixtureClassFiles = ClassFiles(fixtureFiles) + + val projectClassFiles = fixtureClassFiles.filter { cfSrc ⇒ + val (cf, _) = cfSrc + cf.thisType.packageName.startsWith("org/opalj/fpcf/fixtures") + } + + val propertiesClassFiles = fixtureClassFiles.filter { cfSrc ⇒ + val (cf, _) = cfSrc + cf.thisType.packageName.startsWith("org/opalj/fpcf/properties") + } + + val libraryClassFiles = propertiesClassFiles + + val configForEntryPoints = BaseConfig + .withValue( + InitialEntryPointsKey.ConfigKeyPrefix+"analysis", + ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllEntryPointsFinder") + ) + .withValue( + InitialEntryPointsKey.ConfigKeyPrefix+"AllEntryPointsFinder.projectMethodsOnly", + ConfigValueFactory.fromAnyRef(true) + ) + + implicit val config: Config = configForEntryPoints + .withValue( + InitialInstantiatedTypesKey.ConfigKeyPrefix+"analysis", + ConfigValueFactory.fromAnyRef("org.opalj.br.analyses.cg.AllInstantiatedTypesFinder") + ) + .withValue( + InitialInstantiatedTypesKey.ConfigKeyPrefix+ + "AllInstantiatedTypesFinder.projectClassesOnly", + ConfigValueFactory.fromAnyRef(true) + ) + + Project( + projectClassFiles, + libraryClassFiles, + libraryClassFilesAreInterfacesOnly = false, + virtualClassFiles = Traversable.empty + ) + } + +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala new file mode 100644 index 0000000000..4c16a96cd1 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -0,0 +1,285 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.heros.analyses.taint + +import java.io.File +import java.util + +import scala.collection.JavaConverters._ + +import heros.FlowFunction +import heros.FlowFunctions +import heros.flowfunc.Identity + +import org.opalj.util.Milliseconds +import org.opalj.br.analyses.SomeProject +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.tac.fpcf.analyses.heros.cfg.OpalBackwardICFG +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.Assignment +import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement +import org.opalj.tac.fpcf.analyses.ifds.taint.Variable +import org.opalj.tac.BinaryExpr +import org.opalj.tac.Compare +import org.opalj.tac.PrefixExpr +import org.opalj.tac.fpcf.analyses.ifds.taint.InstanceField +import org.opalj.tac.ArrayLength +import org.opalj.tac.ArrayLoad +import org.opalj.tac.Expr +import org.opalj.tac.GetField +import org.opalj.tac.NewArray +import org.opalj.tac.PrimitiveTypecastExpr +import org.opalj.tac.Var +import org.opalj.tac.fpcf.analyses.ifds.taint.StaticField +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.ArrayStore +import org.opalj.tac.PutField +import org.opalj.tac.PutStatic +import org.opalj.tac.ReturnValue +import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis +import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner +import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintAnalysis +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis + +/** + * An implementation of the BackwardClassForNameAnalysis in the Heros framework. + * For a documentation of the data flow functions, see BackwardClassForNameAnalysis. + * + * @author Mario Trageser + */ +class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) extends HerosTaintAnalysis(p, icfg) { + + override val initialSeeds: util.Map[Statement, util.Set[Fact]] = + p.allProjectClassFiles.filter(classFile ⇒ + classFile.thisType.fqn == "java/lang/Class") + .flatMap(classFile ⇒ classFile.methods) + .filter(_.name == "forName") + .map(method ⇒ icfg.getExitStmt(method) → Set[Fact](Variable(-2)).asJava).toMap.asJava + + var flowFacts = Map.empty[Method, Set[FlowFact]] + + override def followReturnsPastSeeds(): Boolean = true + + override def createFlowFunctionsFactory(): FlowFunctions[Statement, Fact, Method] = { + + new FlowFunctions[Statement, Fact, Method]() { + + override def getNormalFlowFunction(statement: Statement, succ: Statement): FlowFunction[Fact] = { + val method = statement.method + val stmt = statement.stmt + source: Fact ⇒ { + var result = stmt.astID match { + case Assignment.ASTID ⇒ + if (isTainted(statement.index, source)) + createNewTaints(stmt.asAssignment.expr, statement) + source + else Set(source) + case ArrayStore.ASTID ⇒ + val arrayStore = stmt.asArrayStore + val arrayIndex = TaintAnalysis.getIntConstant(arrayStore.index, statement.code) + val arrayDefinedBy = arrayStore.arrayRef.asVar.definedBy + var facts = (source match { + // In this case, we taint the whole array. + case Variable(index) if arrayDefinedBy.contains(index) ⇒ + createNewTaints(arrayStore.value, statement) + // In this case, we taint exactly the stored element. + case ArrayElement(index, taintedElement) if arrayDefinedBy.contains(index) && + (arrayIndex.isEmpty || arrayIndex.get == taintedElement) ⇒ + createNewTaints(arrayStore.value, statement) + case _ ⇒ Set.empty[Fact] + }) + source + if (arrayDefinedBy.size == 1 && arrayIndex.isDefined) + facts -= ArrayElement(arrayDefinedBy.head, arrayIndex.get) + facts + case PutField.ASTID ⇒ + val putField = stmt.asPutField + val objectDefinedBy = putField.objRef.asVar.definedBy + if (source match { + case InstanceField(index, declaringClass, name) if objectDefinedBy.contains(index) && + putField.declaringClass == declaringClass && putField.name == name ⇒ + true + case _ ⇒ false + }) createNewTaints(putField.value, statement) + source + else Set(source) + case PutStatic.ASTID ⇒ + val putStatic = stmt.asPutStatic + if (source match { + case StaticField(declaringClass, name) if putStatic.declaringClass == declaringClass && putStatic.name == name ⇒ + true + case _ ⇒ false + }) createNewTaints(putStatic.value, statement) + source + else Set(source) + case _ ⇒ Set(source) + } + if (icfg.isExitStmt(succ) && HerosAnalysis.canBeCalledFromOutside(method) && (source match { + case Variable(index) if index < 0 ⇒ true + case ArrayElement(index, _) if index < 0 ⇒ true + case InstanceField(index, _, _) if index < 0 ⇒ true + case _ ⇒ false + })) { + val fact = FlowFact(Seq(method)) + result += fact + flowFacts = flowFacts.updated(method, flowFacts.getOrElse(method, Set.empty[FlowFact]) + fact) + } + result.asJava + } + + } + + override def getCallFlowFunction(stmt: Statement, callee: Method): FlowFunction[Fact] = { + val callObject = asCall(stmt.stmt) + val staticCall = callee.isStatic + source: Fact ⇒ { + val returnValueFacts = + if (stmt.stmt.astID == Assignment.ASTID) + source match { + case Variable(index) if index == stmt.index ⇒ + createNewTaintsForCallee(callee) + case ArrayElement(index, taintedElement) if index == stmt.index ⇒ + toArrayElement(createNewTaintsForCallee(callee), taintedElement) + case InstanceField(index, declaringClass, name) if index == stmt.index ⇒ + toInstanceField(createNewTaintsForCallee(callee), declaringClass, name) + case _ ⇒ Set.empty[Fact] + } + else Set.empty + val thisOffset = if (callee.isStatic) 0 else 1 + val parameterFacts = callObject.allParams.zipWithIndex + .filter(pair ⇒ (pair._2 == 0 && !staticCall) || callObject.descriptor.parameterTypes(pair._2 - thisOffset).isReferenceType) + .flatMap { pair ⇒ + val param = pair._1.asVar + val paramIndex = pair._2 + source match { + case Variable(index) if param.definedBy.contains(index) ⇒ + Some(Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall))) + case ArrayElement(index, taintedElement) if param.definedBy.contains(index) ⇒ + Some(ArrayElement( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement + )) + case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) ⇒ + Some(InstanceField( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), + declaringClass, name + )) + case staticField: StaticField ⇒ Some(staticField) + case _ ⇒ None + } + } + (returnValueFacts ++ parameterFacts).asJava + } + } + + override def getReturnFlowFunction(statement: Statement, callee: Method, exit: Statement, succ: Statement): FlowFunction[Fact] = { + // If a method has no caller, returnFlow will be called with a null statement. + if (statement == null) return Identity.v() + val stmt = statement.stmt + val callStatement = asCall(stmt) + val staticCall = callee.isStatic + val thisOffset = if (staticCall) 0 else 1 + val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) + .map(index ⇒ AbstractIFDSAnalysis.switchParamAndVariableIndex(index + thisOffset, staticCall)) + source: Fact ⇒ + (source match { + case Variable(index) if formalParameterIndices.contains(index) ⇒ + createNewTaints( + callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), statement + ) + case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) ⇒ + toArrayElement(createNewTaints( + callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + statement + ), taintedElement) + case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) ⇒ + toInstanceField(createNewTaints( + callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + statement + ), declaringClass, name) + case staticField: StaticField ⇒ Set[Fact](staticField) + case _ ⇒ Set.empty[Fact] + }).asJava + } + + override def getCallToReturnFlowFunction(stmt: Statement, succ: Statement): FlowFunction[Fact] = + Identity.v() + } + } + + private def isTainted(index: Int, source: Fact, taintedElement: Option[Int] = None): Boolean = source match { + case Variable(variableIndex) ⇒ variableIndex == index + case ArrayElement(variableIndex, element) ⇒ + variableIndex == index && (taintedElement.isEmpty || taintedElement.get == element) + case _ ⇒ false + } + + private def createNewTaintsForCallee(callee: Method): Set[Fact] = { + icfg.getStartPointsOf(callee).asScala.flatMap { statement ⇒ + val stmt = statement.stmt + stmt.astID match { + case ReturnValue.ASTID ⇒ createNewTaints(stmt.asReturnValue.expr, statement) + case _ ⇒ Set.empty[Fact] + } + }.toSet + } + + private def createNewTaints(expression: Expr[V], statement: Statement): Set[Fact] = + expression.astID match { + case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) + case ArrayLoad.ASTID ⇒ + val arrayLoad = expression.asArrayLoad + val arrayIndex = TaintAnalysis.getIntConstant(expression.asArrayLoad.index, statement.code) + val arrayDefinedBy = arrayLoad.arrayRef.asVar.definedBy + if (arrayIndex.isDefined) arrayDefinedBy.map(ArrayElement(_, arrayIndex.get)) + else arrayDefinedBy.map(Variable) + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | + PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + acc ++ createNewTaints(expression.subExpr(subExpr), statement)) + case GetField.ASTID ⇒ + val getField = expression.asGetField + getField.objRef.asVar.definedBy + .map(InstanceField(_, getField.declaringClass, getField.name)) + /*case GetStatic.ASTID ⇒ + val getStatic = expression.asGetStatic + Set(StaticField(getStatic.declaringClass, getStatic.name))*/ + case _ ⇒ Set.empty + } + + private def toArrayElement(facts: Set[Fact], taintedElement: Int): Set[Fact] = + facts.map { + case Variable(variableIndex) ⇒ ArrayElement(variableIndex, taintedElement) + case ArrayElement(variableIndex, _) ⇒ ArrayElement(variableIndex, taintedElement) + case InstanceField(variableIndex, _, _) ⇒ ArrayElement(variableIndex, taintedElement) + } + + private def toInstanceField(facts: Set[Fact], declaringClass: ObjectType, name: String): Set[Fact] = + facts.map { + case Variable(variableIndex) ⇒ InstanceField(variableIndex, declaringClass, name) + case ArrayElement(variableIndex, _) ⇒ InstanceField(variableIndex, declaringClass, name) + case InstanceField(variableIndex, _, _) ⇒ + InstanceField(variableIndex, declaringClass, name) + } + +} + +class HerosBackwardClassForNameAnalysisRunner extends HerosAnalysisRunner[Fact, HerosBackwardClassForNameAnalysis] { + + override protected def createAnalysis(p: SomeProject): HerosBackwardClassForNameAnalysis = + new HerosBackwardClassForNameAnalysis(p, new OpalBackwardICFG(p)) + + override protected def printResultsToConsole(analysis: HerosBackwardClassForNameAnalysis, analysisTime: Milliseconds): Unit = { + for { + method ← analysis.flowFacts.keys + fact ← analysis.flowFacts(method) + } println(s"flow: "+fact.flow.map(_.toJava).mkString(", ")) + println(s"Time: $analysisTime") + } +} + +object HerosBackwardClassForNameAnalysisRunner { + def main(args: Array[String]): Unit = { + val fileIndex = args.indexOf("-f") + new HerosBackwardClassForNameAnalysisRunner().run( + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala new file mode 100644 index 0000000000..887291d061 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -0,0 +1,380 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.heros.analyses.taint + +import java.io.File +import java.util +import java.util.Collections + +import scala.collection.JavaConverters._ + +import heros.FlowFunction +import heros.FlowFunctions +import heros.TwoElementSet +import heros.flowfunc.Identity +import heros.flowfunc.KillAll + +import org.opalj.util.Milliseconds +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.PropertyStore +import org.opalj.br.ClassHierarchy +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.br.DeclaredMethod +import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG +import org.opalj.tac.Assignment +import org.opalj.tac.Expr +import org.opalj.tac.GetField +import org.opalj.tac.GetStatic +import org.opalj.tac.PutField +import org.opalj.tac.PutStatic +import org.opalj.tac.ReturnValue +import org.opalj.tac.Var +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.analyses.ifds.taint.InstanceField +import org.opalj.tac.fpcf.analyses.ifds.taint.StaticField +import org.opalj.tac.fpcf.analyses.ifds.taint.Variable +import org.opalj.tac.ArrayLength +import org.opalj.tac.ArrayLoad +import org.opalj.tac.ArrayStore +import org.opalj.tac.BinaryExpr +import org.opalj.tac.Compare +import org.opalj.tac.NewArray +import org.opalj.tac.PrefixExpr +import org.opalj.tac.PrimitiveTypecastExpr +import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis +import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintAnalysis +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V + +/** + * An implementation of the ForwardClassForNameAnalysis in the Heros framework. + * For documentation, see ForwardClassForNameAnalysis. + * + * @author Mario Trageser + */ +class HerosForwardClassForNameAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMethods: Map[Method, util.Set[Fact]]) extends HerosTaintAnalysis(p, icfg) { + + override val initialSeeds: util.Map[Statement, util.Set[Fact]] = { + var result: Map[Statement, util.Set[Fact]] = Map.empty + for ((m, facts) ← initialMethods) { + result += icfg.getStartPointsOf(m).iterator().next() → facts + } + result.asJava + } + + val classHierarchy: ClassHierarchy = p.classHierarchy + + var flowFacts = Map.empty[Method, Set[FlowFact]] + + override def createFlowFunctionsFactory(): FlowFunctions[Statement, Fact, Method] = { + + new FlowFunctions[Statement, Fact, Method]() { + + override def getNormalFlowFunction(stmt: Statement, succ: Statement): FlowFunction[Fact] = { + stmt.stmt.astID match { + case Assignment.ASTID ⇒ + handleAssignment(stmt, stmt.stmt.asAssignment.expr) + case ArrayStore.ASTID ⇒ + val store = stmt.stmt.asArrayStore + val definedBy = store.arrayRef.asVar.definedBy + val index = TaintAnalysis.getIntConstant(store.index, stmt.code) + (source: Fact) ⇒ { + if (isTainted(store.value, source)) { + if (index.isDefined) { + (definedBy.iterator.map[Fact](ArrayElement(_, index.get)).toSet + source).asJava + } else { + (definedBy.iterator.map[Fact](Variable).toSet + source).asJava + } + } else { + if (index.isDefined && definedBy.size == 1) { + val idx = index.get + val arrayDefinedBy = definedBy.head + source match { + case ArrayElement(`arrayDefinedBy`, `idx`) ⇒ Collections.emptySet() + case _ ⇒ Collections.singleton(source) + } + } else Collections.singleton(source) + } + } + case PutStatic.ASTID ⇒ + val put = stmt.stmt.asPutStatic + (source: Fact) ⇒ { + if (isTainted(put.value, source)) + TwoElementSet.twoElementSet(source, StaticField(put.declaringClass, put.name)) + else Collections.singleton(source) + } + case PutField.ASTID ⇒ + val put = stmt.stmt.asPutField + val definedBy = put.objRef.asVar.definedBy + (source: Fact) ⇒ { + if (isTainted(put.value, source)) { + (definedBy.iterator.map(InstanceField(_, put.declaringClass, put.name)).toSet[Fact] + source).asJava + } else { + Collections.singleton(source) + } + } + case _ ⇒ Identity.v() + } + } + + def handleAssignment(stmt: Statement, expr: Expr[V]): FlowFunction[Fact] = + expr.astID match { + case Var.ASTID ⇒ + (source: Fact) ⇒ { + source match { + case Variable(index) if expr.asVar.definedBy.contains(index) ⇒ + TwoElementSet.twoElementSet(source, Variable(stmt.index)) + case _ ⇒ Collections.singleton(source) + } + } + case ArrayLoad.ASTID ⇒ + val load = expr.asArrayLoad + val arrayDefinedBy = load.arrayRef.asVar.definedBy + (source: Fact) ⇒ { + source match { + // The specific array element may be tainted + case ArrayElement(index, taintedIndex) ⇒ + val arrIndex = TaintAnalysis.getIntConstant(load.index, stmt.code) + if (arrayDefinedBy.contains(index) && + (arrIndex.isEmpty || taintedIndex == arrIndex.get)) + TwoElementSet.twoElementSet(source, Variable(stmt.index)) + else + Collections.singleton(source) + // Or the whole array + case Variable(index) if arrayDefinedBy.contains(index) ⇒ + TwoElementSet.twoElementSet(source, Variable(stmt.index)) + case _ ⇒ Collections.singleton(source) + } + } + case GetStatic.ASTID ⇒ + val get = expr.asGetStatic + (source: Fact) ⇒ { + if (source == StaticField(get.declaringClass, get.name)) + TwoElementSet.twoElementSet(source, Variable(stmt.index)) + else Collections.singleton(source) + } + case GetField.ASTID ⇒ + val get = expr.asGetField + val objectDefinedBy = get.objRef.asVar.definedBy + (source: Fact) ⇒ { + source match { + // The specific field may be tainted + case InstanceField(index, _, taintedField) if taintedField == get.name && objectDefinedBy.contains(index) ⇒ + TwoElementSet.twoElementSet(source, Variable(stmt.index)) + // Or the whole object + case Variable(index) if objectDefinedBy.contains(index) ⇒ + TwoElementSet.twoElementSet(source, Variable(stmt.index)) + case _ ⇒ Collections.singleton(source) + } + } + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | + NewArray.ASTID | ArrayLength.ASTID ⇒ + (source: Fact) ⇒ { + val result = new util.HashSet[Fact] + (0 until expr.subExprCount) + .foreach(subExpression ⇒ + result.addAll( + handleAssignment(stmt, expr.subExpr(subExpression)) + .computeTargets(source) + )) + result + } + case _ ⇒ Identity.v() + } + + override def getCallFlowFunction(stmt: Statement, callee: Method): FlowFunction[Fact] = { + val callObject = asCall(stmt.stmt) + val allParams = callObject.allParams + if (relevantCallee(callee)) { + val allParamsWithIndices = allParams.zipWithIndex + source: Fact ⇒ + (source match { + case Variable(index) ⇒ // Taint formal parameter if actual parameter is tainted + allParamsWithIndices.collect { + case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ + Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic)) + }.toSet[Fact] + + case ArrayElement(index, taintedIndex) ⇒ + // Taint element of formal parameter if element of actual parameter is tainted + allParamsWithIndices.collect { + case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ + ArrayElement(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic), taintedIndex) + }.toSet[Fact] + + case InstanceField(index, declClass, taintedField) ⇒ + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.collect { + case (param, paramIdx) if param.asVar.definedBy.contains(index) && + (AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic) != -1 || + classHierarchy.isSubtypeOf(declClass, callee.classFile.thisType)) ⇒ + InstanceField(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic), declClass, taintedField) + }.toSet[Fact] + case sf: StaticField ⇒ Set(sf).asInstanceOf[Set[Fact]] + case _ ⇒ Set.empty[Fact] + }).asJava + } else KillAll.v() + } + + override def getReturnFlowFunction(stmt: Statement, callee: Method, exit: Statement, succ: Statement): FlowFunction[Fact] = { + + def isRefTypeParam(index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (callee.isStatic) 0 else 1 + callee.descriptor.parameterType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + - parameterOffset + ).isReferenceType + } + + val callStatement = asCall(stmt.stmt) + val allParams = callStatement.allParams + + source: Fact ⇒ { + val paramFacts = source match { + + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + val params = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + ) + params.asVar.definedBy.iterator.map(Variable).toSet + + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + // Taint element of actual parameter if element of formal parameter is tainted + val params = + asCall(stmt.stmt).allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) + params.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)).asInstanceOf[Iterator[Fact]].toSet + + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + // Taint field of actual parameter if field of formal parameter is tainted + val params = + allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) + params.asVar.definedBy.iterator.map(InstanceField(_, declClass, taintedField)).asInstanceOf[Iterator[Fact]].toSet + + case sf: StaticField ⇒ Set(sf) + case FlowFact(flow) if !flow.contains(stmt.method) ⇒ + val flowFact = FlowFact(stmt.method +: flow) + if (initialMethods.contains(stmt.method)) + flowFacts = flowFacts.updated( + stmt.method, + flowFacts.getOrElse(stmt.method, Set.empty[FlowFact]) + flowFact + ) + Set(flowFact) + case _ ⇒ + Set.empty + } + + val returnFact = + if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + source match { + case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + Some(Variable(stmt.index)) + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + Some(ArrayElement(stmt.index, taintedIndex)) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + Some(InstanceField(stmt.index, declClass, taintedField)) + case _ ⇒ None + } + } else None + + val flowFact = + if (isClassForName(callee) && source == Variable(-2)) { + val flowFact = FlowFact(Seq(stmt.method)) + if (initialMethods.contains(stmt.method)) + flowFacts = flowFacts.updated( + stmt.method, + flowFacts.getOrElse(stmt.method, Set.empty[FlowFact]) + flowFact + ) + Some(flowFact) + } else None + + val allFacts = paramFacts ++ returnFact.asInstanceOf[Option[Fact]] ++ + flowFact.asInstanceOf[Option[Fact]] + + allFacts.asJava + } + } + + override def getCallToReturnFlowFunction(stmt: Statement, succ: Statement): FlowFunction[Fact] = + Identity.v() + } + } + + /** + * Returns true if the expression contains a taint. + */ + private def isTainted(expr: Expr[V], in: Fact): Boolean = { + expr.isVar && (in match { + case Variable(source) ⇒ expr.asVar.definedBy.contains(source) + case ArrayElement(source, _) ⇒ expr.asVar.definedBy.contains(source) + case InstanceField(source, _, _) ⇒ expr.asVar.definedBy.contains(source) + case _ ⇒ false + }) + } + + private def relevantCallee(callee: Method): Boolean = + callee.descriptor.parameterTypes.exists { + case ObjectType.Object ⇒ true + case ObjectType.String ⇒ true + case _ ⇒ false + } && (!HerosAnalysis.canBeCalledFromOutside(callee) || isClassForName(callee)) + + private def isClassForName(method: Method): Boolean = + declaredMethods(method).declaringClassType == ObjectType.Class && method.name == "forName" +} + +class HerosForwardClassForNameAnalysisRunner extends HerosAnalysisRunner[Fact, HerosForwardClassForNameAnalysis] { + + override protected def createAnalysis(p: SomeProject): HerosForwardClassForNameAnalysis = { + val declaredMethods = p.get(DeclaredMethodsKey) + val propertyStore = p.get(PropertyStoreKey) + val initialMethods = scala.collection.mutable.Map.empty[Method, util.Set[Fact]] + for { + method ← declaredMethods.declaredMethods.filter(canBeCalledFromOutside(_, propertyStore)) + .map(_.definedMethod) + index ← method.descriptor.parameterTypes.zipWithIndex.collect { + case (pType, index) if pType == ObjectType.String ⇒ + index + } + } { + val fact = Variable(-2 - index) + if (initialMethods.contains(method)) initialMethods(method).add(fact) + else initialMethods += (method → new util.HashSet[Fact](Collections.singleton(fact))) + } + new HerosForwardClassForNameAnalysis(p, new OpalForwardICFG(p), initialMethods.toMap) + } + + override protected def printResultsToConsole(analysis: HerosForwardClassForNameAnalysis, analysisTime: Milliseconds): Unit = { + for { + method ← analysis.flowFacts.keys + fact ← analysis.flowFacts(method) + } println(s"flow: "+fact.flow.map(_.toJava).mkString(", ")) + println(s"Time: $analysisTime") + } + + private def canBeCalledFromOutside(method: DeclaredMethod, propertyStore: PropertyStore): Boolean = { + val FinalEP(_, callers) = propertyStore(method, Callers.key) + callers.hasCallersWithUnknownContext + } +} + +object HerosForwardClassForNameAnalysisRunner { + + def main(args: Array[String]): Unit = { + val fileIndex = args.indexOf("-f") + new HerosForwardClassForNameAnalysisRunner().run( + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala new file mode 100644 index 0000000000..39ad7524c6 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala @@ -0,0 +1,22 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.heros.analyses.taint + +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis +import org.opalj.tac.fpcf.analyses.heros.cfg.OpalICFG +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact + +/** + * A common subclass of all Heros taint analyses. + * + * @author Mario Trageser + */ +abstract class HerosTaintAnalysis(p: SomeProject, icfg: OpalICFG) extends HerosAnalysis[Fact](p, icfg) { + + /** + * Uses the NullFact of the TaintAnalysis. + */ + override def createZeroValue(): Fact = NullFact + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala new file mode 100644 index 0000000000..e035296909 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala @@ -0,0 +1,77 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.heros.cfg + +import java.util.{List ⇒ JList} +import java.util.{Collection ⇒ JCollection} +import java.util.{Set ⇒ JSet} +import java.util.Collections +import java.util.concurrent.ConcurrentLinkedQueue + +import scala.collection.JavaConverters._ + +import org.opalj.br.analyses.SomeProject +import org.opalj.br.Method +import org.opalj.br.cfg.BasicBlock +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.TACode + +/** + * A backward ICFG for Heros analyses. + * + * @author Mario Trageser + */ +class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { + + override def getPredsOf(stmt: Statement): JList[Statement] = super.getSuccsOf(stmt) + + override def getSuccsOf(stmt: Statement): JList[Statement] = super.getPredsOf(stmt) + + override def getStartPointsOf(m: Method): JCollection[Statement] = { + val TACode(_, code, _, cfg, _) = tacai(m) + (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).map { + case bb: BasicBlock ⇒ + val index = bb.endPC + Statement(m, bb, code(index), index, code, cfg) + }.asJava + } + + override def isExitStmt(stmt: Statement): Boolean = stmt.index == 0 + + override def isStartPoint(stmt: Statement): Boolean = { + val cfg = stmt.cfg + val index = stmt.index + (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).exists { + case bb: BasicBlock ⇒ bb.endPC == index + } + } + + override def allNonCallStartNodes(): JSet[Statement] = { + val res = new ConcurrentLinkedQueue[Statement] + project.parForeachMethodWithBody() { mi ⇒ + val m = mi.method + val TACode(_, code, _, cfg, _) = tacai(m) + val endIndex = code.length + val startIndices = + (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).map { + case bb: BasicBlock ⇒ bb.endPC + } + var index = 0 + while (index < endIndex) { + val stmt = code(index) + val statement = Statement(m, cfg.bb(index), stmt, index, code, cfg) + if (!(isCallStmt(statement) || startIndices.contains(index))) + res.add(statement) + index += 1 + } + } + new java.util.HashSet(res) + Collections.emptySet() + } + + def getExitStmt(method: Method): Statement = { + val tac = tacai(method) + val cfg = tac.cfg + val code = tac.stmts + Statement(method, cfg.startBlock, code(0), 0, code, cfg) + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala new file mode 100644 index 0000000000..a07cd1778f --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala @@ -0,0 +1,59 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.heros.cfg + +import java.util.{Collection ⇒ JCollection} +import java.util.{Set ⇒ JSet} +import java.util.Collections +import java.util.concurrent.ConcurrentLinkedQueue + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.ifds.Statement + +/** + * A forward ICFG for Heros analyses. + * + * @author Mario Trageser + */ +class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { + + override def getStartPointsOf(m: Method): JCollection[Statement] = { + val TACode(_, code, _, cfg, _) = tacai(m) + Collections.singletonList(Statement(m, cfg.startBlock, code(0), 0, code, cfg)) + } + + override def isExitStmt(stmt: Statement): Boolean = stmt.cfg.bb(stmt.index).successors.exists(_.isExitNode) + + override def isStartPoint(stmt: Statement): Boolean = stmt.index == 0 + + override def allNonCallStartNodes(): JSet[Statement] = { + val res = new ConcurrentLinkedQueue[Statement] + project.parForeachMethodWithBody() { mi ⇒ + val m = mi.method + val TACode(_, code, _, cfg, _) = tacai(m) + val endIndex = code.length + var index = 1 + while (index < endIndex) { + val stmt = code(index) + val statement = Statement(m, cfg.bb(index), stmt, index, code, cfg) + if (!isCallStmt(statement)) + res.add(statement) + index += 1 + } + } + new java.util.HashSet(res) + Collections.emptySet() + } + + def getExitStmts(method: Method): Iterator[Statement] = { + val tac = tacai(method) + val cfg = tac.cfg + val code = tac.stmts + cfg.allNodes.filter(_.isExitNode).flatMap(_.predecessors).map { bb ⇒ + val endPc = bb.asBasicBlock.endPC + Statement(method, bb, code(endPc), endPc, code, cfg) + } + } + +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala new file mode 100644 index 0000000000..ca1752e482 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala @@ -0,0 +1,119 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.heros.cfg + +import java.util.{List ⇒ JList} +import java.util.{Collection ⇒ JCollection} +import java.util.{Set ⇒ JSet} + +import scala.collection.JavaConverters._ + +import heros.InterproceduralCFG + +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.PropertyStore +import org.opalj.value.ValueInformation +import org.opalj.br.analyses.SomeProject +import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.br.DefinedMethod +import org.opalj.br.MultipleDefinedMethods +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.Assignment +import org.opalj.tac.DUVar +import org.opalj.tac.FunctionCall +import org.opalj.tac.LazyDetachedTACAIKey +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.Expr +import org.opalj.tac.ExprStmt +import org.opalj.tac.MethodCall +import org.opalj.tac.NonVirtualFunctionCall +import org.opalj.tac.NonVirtualMethodCall +import org.opalj.tac.StaticFunctionCall +import org.opalj.tac.StaticMethodCall +import org.opalj.tac.VirtualFunctionCall +import org.opalj.tac.VirtualMethodCall + +/** + * The superclass of the forward and backward ICFG for Heros analyses. + * + * @param project The project, which is analyzed. + * @author Mario Trageser + */ +abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[Statement, Method] { + + val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) + implicit val ps: PropertyStore = project.get(PropertyStoreKey) + implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + + override def getMethodOf(stmt: Statement): Method = stmt.method + + override def getPredsOf(stmt: Statement): JList[Statement] = { + stmt.cfg.predecessors(stmt.index).toChain.map { index ⇒ + Statement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) + }.toList.asJava + } + + override def getSuccsOf(stmt: Statement): JList[Statement] = { + stmt.cfg.successors(stmt.index).toChain.map { index ⇒ + Statement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) + }.toList.asJava + } + + override def getCalleesOfCallAt(callInstr: Statement): JCollection[Method] = { + val FinalEP(_, callees) = ps(declaredMethods(callInstr.method), Callees.key) + callees.directCallees(callInstr.stmt.pc).collect { + case d: DefinedMethod ⇒ List(d.definedMethod) + case md: MultipleDefinedMethods ⇒ md.definedMethods + }.flatten.filter(_.body.isDefined).toList.asJava + } + + override def getCallersOf(m: Method): JCollection[Statement] = { + val FinalEP(_, callers) = ps(declaredMethods(m), Callers.key) + callers.callers.flatMap { + case (method, pc, true) ⇒ + val TACode(_, code, pcToIndex, cfg, _) = tacai(method.definedMethod) + val index = pcToIndex(pc) + Some(Statement(method.definedMethod, cfg.bb(index), code(index), index, code, cfg)) + case _ ⇒ None + }.toSet.asJava + } + + override def getCallsFromWithin(m: Method): JSet[Statement] = { + val TACode(_, code, _, cfg, _) = tacai(m) + code.zipWithIndex.collect { + case (mc: MethodCall[V], index) ⇒ Statement(m, cfg.bb(index), mc, index, code, cfg) + case (as @ Assignment(_, _, _: FunctionCall[V]), index) ⇒ + Statement(m, cfg.bb(index), as, index, code, cfg) + case (ex @ ExprStmt(_, _: FunctionCall[V]), index) ⇒ Statement(m, cfg.bb(index), ex, index, code, cfg) + }.toSet.asJava + } + + override def getReturnSitesOfCallAt(callInstr: Statement): JCollection[Statement] = getSuccsOf(callInstr) + + override def isCallStmt(stmt: Statement): Boolean = { + def isCallExpr(expr: Expr[V]) = expr.astID match { + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | + VirtualFunctionCall.ASTID ⇒ true + case _ ⇒ false + } + + stmt.stmt.astID match { + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + true + case Assignment.ASTID ⇒ isCallExpr(stmt.stmt.asAssignment.expr) + case ExprStmt.ASTID ⇒ isCallExpr(stmt.stmt.asExprStmt.expr) + case _ ⇒ false + } + } + + override def isFallThroughSuccessor(stmt: Statement, succ: Statement): Boolean = ??? + + override def isBranchTarget(stmt: Statement, succ: Statement): Boolean = ??? + +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala new file mode 100644 index 0000000000..a5e795698c --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala @@ -0,0 +1,161 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.taint + +import java.io.File +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.EPS +import org.opalj.fpcf.FinalEP +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.SomeProject +import org.opalj.br.DefinedMethod +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysis +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.analyses.ifds.taint.Taint +import org.opalj.tac.fpcf.analyses.ifds.taint.Variable +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact +import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement +import org.opalj.tac.fpcf.analyses.ifds.taint.InstanceField +import org.opalj.tac.fpcf.analyses.ifds.AbsractIFDSAnalysisRunner +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation + +/** + * A backward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, + * * which are callable from outside the library, to calls of Class.forName. + * + * @author Mario Trageser + */ +class BackwardClassForNameTaintAnalysis private (implicit project: SomeProject) + extends BackwardTaintAnalysis { + + /** + * The string parameters of all public methods are entry points. + */ + override val entryPoints: Seq[(DeclaredMethod, Fact)] = + p.allProjectClassFiles.filter(classFile ⇒ + classFile.thisType.fqn == "java/lang/Class") + .flatMap(classFile ⇒ classFile.methods) + .filter(_.name == "forName") + .map(method ⇒ declaredMethods(method) → Variable(-2)) + + /** + * There is no sanitizing in this analysis. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = false + + /** + * There is no sanitizing in this analysis. + */ + override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * Do not perform unbalanced return for methods, which can be called from outside the library. + */ + override protected def shouldPerformUnbalancedReturn(source: (DeclaredMethod, Fact)): Boolean = { + super.shouldPerformUnbalancedReturn(source) && + (!canBeCalledFromOutside(source._1) || + // The source is callable from outside, but should create unbalanced return facts. + entryPoints.contains(source)) + } + + /** + * This analysis does not create FlowFacts at calls. + * Instead, FlowFacts are created at the start node of methods. + */ + override protected def createFlowFactAtCall(call: Statement, in: Set[Fact], + source: (DeclaredMethod, Fact)): Option[FlowFact] = None + + /** + * This analysis does not create FlowFacts at returns. + * Instead, FlowFacts are created at the start node of methods. + */ + protected def applyFlowFactFromCallee( + calleeFact: FlowFact, + source: (DeclaredMethod, Fact) + ): Option[FlowFact] = None + + /** + * If we analyzed a transitive caller of the sink, which is callable from outside the library, + * and a formal parameter is tainted, we create a FlowFact. + */ + override protected def createFlowFactAtBeginningOfMethod( + in: Set[Fact], + source: (DeclaredMethod, Fact) + ): Option[FlowFact] = { + if (source._2.isInstanceOf[UnbalancedReturnFact[Fact @unchecked]] && + canBeCalledFromOutside(source._1) && in.exists { + // index < 0 means, that it is a parameter. + case Variable(index) if index < 0 ⇒ true + case ArrayElement(index, _) if index < 0 ⇒ true + case InstanceField(index, _, _) if index < 0 ⇒ true + case _ ⇒ false + }) { + Some(FlowFact(currentCallChain(source))) + } else None + } +} + +object BackwardClassForNameTaintAnalysis extends IFDSAnalysis[Fact] { + + override def init(p: SomeProject, ps: PropertyStore): BackwardClassForNameTaintAnalysis = { + p.get(RTACallGraphKey) + new BackwardClassForNameTaintAnalysis()(p) + } + + override def property: IFDSPropertyMetaInformation[Fact] = Taint +} + +class BackwardClassForNameTaintAnalysisRunner extends AbsractIFDSAnalysisRunner { + + override def analysisClass: BackwardClassForNameTaintAnalysis.type = BackwardClassForNameTaintAnalysis + + override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = { + val propertyKey = BackwardClassForNameTaintAnalysis.property.key + val flowFactsAtSources = ps.entities(propertyKey).collect { + case EPS((m: DefinedMethod, inputFact)) if canBeCalledFromOutside(m, ps) ⇒ + (m, inputFact) + }.flatMap(ps(_, propertyKey) match { + case FinalEP(_, Taint(result)) ⇒ + result.values.fold(Set.empty)((acc, facts) ⇒ acc ++ facts).filter { + case FlowFact(_) ⇒ true + case _ ⇒ false + } + case _ ⇒ Seq.empty + }) + for { + fact ← flowFactsAtSources + } { + fact match { + case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.toJava).mkString(", ")) + case _ ⇒ + } + } + } +} + +object BackwardClassForNameTaintAnalysisRunner { + def main(args: Array[String]): Unit = { + if (args.contains("--help")) { + println("Potential parameters:") + println(" -seq (to use the SequentialPropertyStore)") + println(" -l2 (to use the l2 domain instead of the default l1 domain)") + println(" -delay (for a three seconds delay before the taint flow analysis is started)") + println(" -debug (for debugging mode in the property store)") + println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") + println(" -f (Stores the average runtime to this file)") + } else { + val fileIndex = args.indexOf("-f") + new BackwardClassForNameTaintAnalysisRunner().run( + args.contains("-debug"), + args.contains("-l2"), + args.contains("-delay"), + args.contains("-evalSchedulingStrategies"), + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala new file mode 100644 index 0000000000..24313a893e --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala @@ -0,0 +1,141 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.taint + +import java.io.File + +import org.opalj.fpcf.PropertyStore +import org.opalj.br.DeclaredMethod +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintAnalysis +import org.opalj.tac.fpcf.analyses.ifds.taint.Taint +import org.opalj.tac.fpcf.analyses.ifds.taint.Variable +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintAnalysis +import org.opalj.tac.fpcf.analyses.ifds.AbsractIFDSAnalysisRunner +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation + +/** + * A forward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, + * which are callable from outside the library, to calls of Class.forName. + * + * @author Dominik Helm + * @author Mario Trageser + * @author Michael Eichberg + */ +class ForwardClassForNameTaintAnalysis private (implicit project: SomeProject) + extends ForwardTaintAnalysis with TaintAnalysis { + + /** + * The string parameters of all public methods are entry points. + */ + override def entryPoints: Seq[(DeclaredMethod, Fact)] = for { + m ← methodsCallableFromOutside.toSeq + if !m.definedMethod.isNative + index ← m.descriptor.parameterTypes.zipWithIndex.collect { + case (pType, index) if pType == ObjectType.String ⇒ index + } + } yield (m, Variable(-2 - index)) + + /** + * There is no sanitizing in this analysis. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = false + + /** + * There is no sanitizing in this analysis. + */ + override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * This analysis does not create new taints on the fly. + * Instead, the string parameters of all public methods are tainted in the entry points. + */ + override protected def createTaints(callee: DeclaredMethod, call: Statement): Set[Fact] = + Set.empty + + /** + * Create a FlowFact, if Class.forName is called with a tainted variable for the first parameter. + */ + override protected def createFlowFact(callee: DeclaredMethod, call: Statement, + in: Set[Fact]): Option[FlowFact] = + if (isClassForName(callee) && in.contains(Variable(-2))) + Some(FlowFact(Seq(call.method))) + else None + + /** + * We only analyze methods with String parameters (and therefore also in Object parameters). + * Additionally, we have to analyze Class.forName, so that FlowFacts will be created. + */ + override protected def relevantCallee(callee: DeclaredMethod): Boolean = + callee.descriptor.parameterTypes.exists { + case ObjectType.Object ⇒ true + case ObjectType.String ⇒ true + case _ ⇒ false + } && (!canBeCalledFromOutside(callee) || isClassForName(callee)) + + /** + * Checks, if a `method` is Class.forName. + * + * @param method The method. + * @return True, if the method is Class.forName. + */ + private def isClassForName(method: DeclaredMethod): Boolean = + method.declaringClassType == ObjectType.Class && method.name == "forName" +} + +object ForwardClassForNameTaintAnalysis extends IFDSAnalysis[Fact] { + + override def init(p: SomeProject, ps: PropertyStore): ForwardClassForNameTaintAnalysis = { + p.get(RTACallGraphKey) + new ForwardClassForNameTaintAnalysis()(p) + } + + override def property: IFDSPropertyMetaInformation[Fact] = Taint +} + +class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { + + override def analysisClass: ForwardClassForNameTaintAnalysis.type = ForwardClassForNameTaintAnalysis + + override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = + for { + e ← analysis.entryPoints + flows = ps(e, ForwardClassForNameTaintAnalysis.property.key) + fact ← flows.ub.asInstanceOf[IFDSProperty[Fact]].flows.values.flatten.toSet[Fact] + } { + fact match { + case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.toJava).mkString(", ")) + case _ ⇒ + } + } +} + +object ForwardClassForNameAnalysisRunner { + def main(args: Array[String]): Unit = { + if (args.contains("--help")) { + println("Potential parameters:") + println(" -seq (to use the SequentialPropertyStore)") + println(" -l2 (to use the l2 domain instead of the default l1 domain)") + println(" -delay (for a three seconds delay before the taint flow analysis is started)") + println(" -debug (for debugging mode in the property store)") + println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") + println(" -f (Stores the average runtime to this file)") + } else { + val fileIndex = args.indexOf("-f") + new ForwardClassForNameAnalysisRunner().run( + args.contains("-debug"), + args.contains("-l2"), + args.contains("-delay"), + args.contains("-evalSchedulingStrategies"), + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java new file mode 100644 index 0000000000..e3b5427b35 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java @@ -0,0 +1,417 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.fixtures.taint; + +import org.opalj.fpcf.properties.taint.BackwardFlowPath; +import org.opalj.fpcf.properties.taint.ForwardFlowPath; + +/** + * @author Mario Trageser + */ +public class TaintAnalysisTestClass { + + private static int staticField; + + private int instanceField; + + @ForwardFlowPath({"callChainsAreConsidered", "passToSink"}) + @BackwardFlowPath({"callChainsAreConsidered", "passToSink", "sink"}) + public void callChainsAreConsidered() { + passToSink(source()); + } + + @ForwardFlowPath({"returnEdgesFromInstanceMethodsArePresent"}) + @BackwardFlowPath({"returnEdgesFromInstanceMethodsArePresent", "sink"}) + public void returnEdgesFromInstanceMethodsArePresent() { + sink(callSourcePublic()); + } + + @ForwardFlowPath({"returnEdgesFromPrivateMethodsArePresent"}) + @BackwardFlowPath({"returnEdgesFromPrivateMethodsArePresent", "sink"}) + public void returnEdgesFromPrivateMethodsArePresent() { + sink(callSourceNonStatic()); + } + + @ForwardFlowPath({"multiplePathsAreConsidered_1", "indirectPassToSink", "passToSink"}) + @BackwardFlowPath({"multiplePathsAreConsidered_1", "indirectPassToSink", "passToSink", "sink"}) + public void multiplePathsAreConsidered_1() { + int i = source(); + passToSink(i); + indirectPassToSink(i); + } + + @ForwardFlowPath({"multiplePathsAreConsidered_2", "passToSink"}) + @BackwardFlowPath({"multiplePathsAreConsidered_2", "passToSink", "sink"}) + public void multiplePathsAreConsidered_2() { + int i = source(); + passToSink(i); + indirectPassToSink(i); + } + + @ForwardFlowPath({"ifEdgesAreConsidered"}) + @BackwardFlowPath({"ifEdgesAreConsidered", "sink"}) + public void ifEdgesAreConsidered() { + int i; + if(Math.random() < .5) { + i = source(); + } else { + i = 0; + } + sink(i); + } + + @ForwardFlowPath({"elseEdgesAreConsidered"}) + @BackwardFlowPath({"elseEdgesAreConsidered", "sink"}) + public void elseEdgesAreConsidered() { + int i; + if(Math.random() < .5) { + i = 0; + } else { + i = source(); + } + sink(i); + } + + @ForwardFlowPath({"forLoopsAreConsidered"}) + @BackwardFlowPath({"forLoopsAreConsidered", "sink"}) + public void forLoopsAreConsidered() { + int[] arr = new int[2]; + for(int i = 0; i < arr.length; i++) { + sink(arr[0]); + arr[i] = source(); + } + } + + @ForwardFlowPath("returnOfIdentityFunctionIsConsidered") + @BackwardFlowPath({"returnOfIdentityFunctionIsConsidered", "sink"}) + public void returnOfIdentityFunctionIsConsidered() { + sink(identity(source())); + } + + @ForwardFlowPath({"summaryEdgesOfRecursiveFunctionsAreComputedCorrectly"}) + @BackwardFlowPath({"summaryEdgesOfRecursiveFunctionsAreComputedCorrectly", "sink"}) + public void summaryEdgesOfRecursiveFunctionsAreComputedCorrectly() { + sink(recursion(0)); + } + + public int recursion(int i) { + return i == 0 ? recursion(source()) : i; + } + + @ForwardFlowPath({"codeInCatchNodesIsConsidered"}) + @BackwardFlowPath({"codeInCatchNodesIsConsidered", "sink"}) + public void codeInCatchNodesIsConsidered() { + int i = source(); + try { + throw new RuntimeException(); + } catch(RuntimeException e) { + sink(i); + } + } + + @ForwardFlowPath({"codeInFinallyNodesIsConsidered"}) + @BackwardFlowPath({"codeInFinallyNodesIsConsidered", "sink"}) + public void codeInFinallyNodesIsConsidered() { + int i = 1; + try { + throw new RuntimeException(); + } catch(RuntimeException e) { + i = source(); + } finally { + sink(i); + } + } + + @ForwardFlowPath({"unaryExpressionsPropagateTaints"}) + @BackwardFlowPath({"unaryExpressionsPropagateTaints", "sink"}) + public void unaryExpressionsPropagateTaints() { + sink(-source()); + } + + @ForwardFlowPath({"binaryExpressionsPropagateTaints"}) + @BackwardFlowPath({"binaryExpressionsPropagateTaints", "sink"}) + public void binaryExpressionsPropagateTaints() { + sink(source() + 1); + } + + @ForwardFlowPath({"arrayLengthPropagatesTaints"}) + @BackwardFlowPath({"arrayLengthPropagatesTaints", "sink"}) + public void arrayLengthPropagatesTaints() { + sink(new Object[source()].length); + } + + @ForwardFlowPath({"singleArrayIndicesAreTainted_1"}) + @BackwardFlowPath({"singleArrayIndicesAreTainted_1", "sink"}) + public void singleArrayIndicesAreTainted_1() { + int[] arr = new int[2]; + arr[0] = source(); + sink(arr[0]); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void singleArrayIndicesAreTainted_2() { + int[] arr = new int[2]; + arr[0] = source(); + sink(arr[1]); + } + + @ForwardFlowPath({"wholeArrayTaintedIfIndexUnknown"}) + @BackwardFlowPath({"wholeArrayTaintedIfIndexUnknown", "sink"}) + public void wholeArrayTaintedIfIndexUnknown() { + int[] arr = new int[2]; + arr[(int) (Math.random() * 2)] = source(); + sink(arr[0]); + } + + @ForwardFlowPath({"arrayElementTaintsArePropagatedToCallee_1", "passFirstArrayElementToSink"}) + @BackwardFlowPath({"arrayElementTaintsArePropagatedToCallee_1", "passFirstArrayElementToSink", + "sink"}) + public void arrayElementTaintsArePropagatedToCallee_1() { + int[] arr = new int[2]; + arr[0] = source(); + passFirstArrayElementToSink(arr); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void arrayElementTaintsArePropagatedToCallee_2() { + int[] arr = new int[2]; + arr[1] = source(); + passFirstArrayElementToSink(arr); + } + + @ForwardFlowPath({"arrayElementTaintsArePropagatedBack_1", "passFirstArrayElementToSink"}) + @BackwardFlowPath({"arrayElementTaintsArePropagatedBack_1", "passFirstArrayElementToSink", + "sink"}) + public void arrayElementTaintsArePropagatedBack_1() { + int[] arr = new int[2]; + taintRandomElement(arr); + passFirstArrayElementToSink(arr); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void arrayElementTaintsArePropagatedBack_2() { + int[] arr = new int[2]; + taintFirstElement(arr); + sink(arr[1]); + } + + @ForwardFlowPath({"callerParameterIsTaintedIfCalleeTaintsFormalParameter", + "passFirstArrayElementToSink"}) + @BackwardFlowPath({"callerParameterIsTaintedIfCalleeTaintsFormalParameter", + "passFirstArrayElementToSink", "sink"}) + public void callerParameterIsTaintedIfCalleeTaintsFormalParameter() { + int[] arr = new int[2]; + taintRandomElement(arr); + passFirstArrayElementToSink(arr); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void taintDisappearsWhenReassigning() { + int[] arr = new int[2]; + arr[0] = source(); + arr[0] = 0; + sink(arr[0]); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void nativeMethodsCanBeHandeled() { + int j = nativeMethod(0); + sink(j); + } + + @ForwardFlowPath({"returnValueOfNativeMethodIsTainted"}) + @BackwardFlowPath({"returnValueOfNativeMethodIsTainted", "sink"}) + public void returnValueOfNativeMethodIsTainted() { + sink(nativeMethod(source())); + } + + @ForwardFlowPath({"analysisUsesCallGraph_1"}) + @BackwardFlowPath({"analysisUsesCallGraph_1", "sink"}) + public void analysisUsesCallGraph_1() { + A a = new B(); + sink(a.get()); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void analysisUsesCallGraph_2() { + A a = new C(); + sink(a.get()); + } + + @ForwardFlowPath({"analysisUsesCallGraph_3"}) + @BackwardFlowPath({"analysisUsesCallGraph_3", "sink"}) + public void analysisUsesCallGraph_3() { + A a; + if(Math.random() < .5) + a = new B(); + else + a = new C(); + sink(a.get()); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void sanitizeRemovesTaint() { + sink(sanitize(source())); + } + + @ForwardFlowPath({"instanceFieldsAreTainted"}) + @BackwardFlowPath({"instanceFieldsAreTainted", "sink"}) + public void instanceFieldsAreTainted() { + instanceField = source(); + sink(instanceField); + } + + @ForwardFlowPath({"staticFieldsAreTainted"}) + @BackwardFlowPath({"staticFieldsAreTainted", "sink"}) + public void staticFieldsAreTainted() { + staticField = source(); + sink(staticField); + } + + @ForwardFlowPath({"fieldTaintsArePassed", "passWrappedValueToSink"}) + @BackwardFlowPath({"fieldTaintsArePassed", "passWrappedValueToSink", "sink"}) + public void fieldTaintsArePassed() { + passWrappedValueToSink(new Wrapper(source())); + } + + @ForwardFlowPath({"fieldTaintsAreAppliedInReturnFlow"}) + @BackwardFlowPath({"fieldTaintsAreAppliedInReturnFlow", "sink"}) + public void fieldTaintsAreAppliedInReturnFlow() { + sink(createTaintedWrapper().field); + } + + @ForwardFlowPath({"fieldTaintsOfParametersAreAppliedInReturnFlow"}) + @BackwardFlowPath({"fieldTaintsOfParametersAreAppliedInReturnFlow", "sink"}) + public void fieldTaintsOfParametersAreAppliedInReturnFlow() { + Wrapper wrapper = new Wrapper(); + taintWrappedValue(wrapper); + sink(wrapper.field); + } + + @ForwardFlowPath({"fieldTaintsAreConsideredInComputations"}) + @BackwardFlowPath({"fieldTaintsAreConsideredInComputations", "sink"}) + public void fieldTaintsAreConsideredInComputations() { + Wrapper wrapper = new Wrapper(source()); + sink(wrapper.field + 1); + } + + //TODO Tests für statische Felder über Methodengrenzen + + //Does not work, because we do not know which exceptions cannot be thrown. + /*@ForwardFlowPath({}) + public void onlyThrowableExceptionsAreConsidered() { + int i = 0; + try { + divide(1, i); + } catch(IllegalArgumentException e) { + i = source(); + } + sink(i); + }*/ + + //Does not work, because the analysis does not know that there is only one iteration. + /*@ForwardFlowPath({}) + public void iterationCountIsConsidered() { + int[] arr = new int[2]; + for(int i = 0; i < 1; i++) { + sink(arr[0]); + arr[i] = source(); + } + }*/ + + public int callSourcePublic() { + return source(); + } + + private int callSourceNonStatic() { + return source(); + } + + private static void passToSink(int i) { + sink(i); + } + + private void indirectPassToSink(int i) { + passToSink(i); + } + + private void passFirstArrayElementToSink(int[] arr) { + sink(arr[0]); + } + + private void taintRandomElement(int[] arr) { + arr[Math.random() < .5 ? 0 : 1] = source(); + } + + private void taintFirstElement(int[] arr) { + arr[0] = source(); + } + + private native int nativeMethod(int i); + + private int identity(int i) {return i;} + + private void passWrappedValueToSink(Wrapper wrapper) { + sink(wrapper.field); + } + + private Wrapper createTaintedWrapper() { + return new Wrapper(source()); + } + + private void taintWrappedValue(Wrapper wrapper) { + wrapper.field = source(); + } + + //If it throws an exception, it is only an arithmetic exception. + private static int divide(int i, int j) { + return i / j; + } + + public static int source() { + return 1; + } + + private static int sanitize(int i) {return i;} + + private static void sink(int i) { + System.out.println(i); + } + +} + +abstract class A { + abstract int get(); +} + +class B extends A { + @Override + int get() { + return TaintAnalysisTestClass.source(); + } +} + +class C extends A { + @Override + int get() { + return 0; + } +} + +class Wrapper { + public int field; + + Wrapper() { + + } + + Wrapper(int field) { + this.field = field; + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/vta/VTATestClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/vta/VTATestClass.java new file mode 100644 index 0000000000..ae4c77876c --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/vta/VTATestClass.java @@ -0,0 +1,126 @@ +package org.opalj.fpcf.fixtures.vta; + +import org.opalj.fpcf.properties.vta.ExpectedCallee; +import org.opalj.fpcf.properties.vta.ExpectedType; + +public class VTATestClass { + + @ExpectedType.List({ @ExpectedType(lineNumber = 10, value = "B", upperBound = false) }) + public void instantiationsAreConsidered() { + A a = new B(); + } + + @ExpectedType.List({ + @ExpectedType(lineNumber = 17, value = "B", upperBound = false), + @ExpectedType(lineNumber = 18, value = "C", upperBound = false) }) + public void factsAreRemembered() { + A x = new B(); + A y = new C(); + } + + @ExpectedType.List({ + @ExpectedType(lineNumber = 25, value = "B[]", upperBound = false), + @ExpectedType(lineNumber = 25, value = "C[]", upperBound = false) }) + public void arrayTypesAreConsidered_1() { + A[] a = new A[2]; + a[0] = new B(); + a[1] = new C(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 33, value = "B", upperBound = false)}) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 34, value = "B", upperBound = false)}) + public void callTargetsAreConsidered() { + A a = new B(); + a.doIt(); + } + + @ExpectedType.List({ + @ExpectedType(lineNumber = 42, value = "B", upperBound = false), + @ExpectedType(lineNumber = 43, value = "C", upperBound = false) }) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 45, value = "B", upperBound = false)}) + public void variableAssignmentsAreConsidered_1() { + A b = new B(); + A c = new C(); + if(Math.random() < .5) b = c; + b.doIt(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 51, value = "B[]", upperBound = false)}) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 52, value = "B", upperBound = false)}) + public void arrayLoadsAreConsidered() { + A[] a = new A[] {new B()}; + a[0].doIt(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 57, value = "B", upperBound = false)}) + public void typesOfParametersArePassed() { + A a = new B(); + typesOfParametersArePassed_callee(a); + } + + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 63, value = "B", upperBound = false)}) + private void typesOfParametersArePassed_callee(A a) { + a.doIt(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 69, value = "B", upperBound = false)}) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 70, value = "B", upperBound = false)}) + public void returnFlowIsConsidered() { + A a = returnB(); + a.doIt(); + } + + private A returnB() { + return new B(); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 80, value = "A", upperBound = true)}) + @ExpectedCallee.List({@ExpectedCallee(lineNumber = 81, value = "A", upperBound = true)}) + public void nativeCallsAreConsidered() { + A a = nativeMethod(); + a.doIt(); + } + + public native A nativeMethod(); + + @ExpectedType.List({@ExpectedType(lineNumber = 88, value = "String", upperBound = true)}) + public void staticFieldReadsAreConsidered() { + Object o = A.STATIC_FIELD; + System.out.println(o); + } + + @ExpectedType.List({@ExpectedType(lineNumber = 94, value = "String", upperBound = true)}) + public void fieldReadsAreConsidered() { + Object o = new B().field; + System.out.println(o); + } + + @ExpectedType.List({ @ExpectedType(lineNumber = 100, value = "B", upperBound = false) }) + protected void protectedMethodsAreConsidered() { + A a = new B(); + } + +} + +abstract class A { + + public static String STATIC_FIELD = "STATIC_FIELD"; + + public String field = "field"; + + public abstract void doIt(); +} + +class B extends A { + @Override + public void doIt() { + System.out.println("B"); + } +} + +class C extends A { + @Override + public void doIt() { + System.out.println("C"); + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/BackwardFlowPath.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/BackwardFlowPath.java new file mode 100644 index 0000000000..2820c5a6f1 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/BackwardFlowPath.java @@ -0,0 +1,20 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.taint; + +import org.opalj.fpcf.properties.PropertyValidator; + +import java.lang.annotation.*; + +/** + * @author Mario Trageser + */ +@PropertyValidator(key = BackwardFlowPath.PROPERTY_VALIDATOR_KEY, validator = BackwardFlowPathMatcher.class) +@Target(ElementType.METHOD) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface BackwardFlowPath { + + String PROPERTY_VALIDATOR_KEY = "BackwardFlowPath"; + + String[] value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/ForwardFlowPath.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/ForwardFlowPath.java new file mode 100644 index 0000000000..9da140e6d4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/taint/ForwardFlowPath.java @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.taint; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.taint.ForwardFlowPathMatcher; + +import java.lang.annotation.*; + +/** + * @author Mario Trageser + */ +@PropertyValidator(key = ForwardFlowPath.PROPERTY_VALIDATOR_KEY, validator = ForwardFlowPathMatcher.class) +@Target(ElementType.METHOD) +@Documented +@Retention(RetentionPolicy.CLASS) +public @interface ForwardFlowPath { + + String PROPERTY_VALIDATOR_KEY = "ForwardFlowPath"; + + String[] value(); +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedCallee.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedCallee.java new file mode 100644 index 0000000000..c860eac1fc --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedCallee.java @@ -0,0 +1,26 @@ +package org.opalj.fpcf.properties.vta; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.vta.ExpectedCalleeMatcher; + +import java.lang.annotation.*; + +public @interface ExpectedCallee { + + String PROPERTY_VALIDATOR_KEY = "ExpectedCallee"; + + int lineNumber(); + + String value(); + + boolean upperBound(); + + @PropertyValidator(key = ExpectedCallee.PROPERTY_VALIDATOR_KEY, validator = ExpectedCalleeMatcher.class) + @Target(ElementType.METHOD) + @Documented + @Retention(RetentionPolicy.CLASS) + @interface List { + + ExpectedCallee[] value(); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedType.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedType.java new file mode 100644 index 0000000000..e0dbd93144 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/properties/vta/ExpectedType.java @@ -0,0 +1,26 @@ +package org.opalj.fpcf.properties.vta; + +import org.opalj.fpcf.properties.PropertyValidator; +import org.opalj.fpcf.properties.vta.ExpectedTypeMatcher; + +import java.lang.annotation.*; + +public @interface ExpectedType { + + String PROPERTY_VALIDATOR_KEY = "ExpectedType"; + + int lineNumber(); + + String value(); + + boolean upperBound(); + + @PropertyValidator(key = ExpectedType.PROPERTY_VALIDATOR_KEY, validator = ExpectedTypeMatcher.class) + @Target(ElementType.METHOD) + @Documented + @Retention(RetentionPolicy.CLASS) + @interface List { + + ExpectedType[] value(); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala new file mode 100644 index 0000000000..77ea0833f5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala @@ -0,0 +1,44 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import java.net.URL + +import org.opalj.fpcf.properties.taint.BackwardFlowPath +import org.opalj.fpcf.PropertiesTest +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.Project +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixture +import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact + +/** + * @author Mario Trageser + */ +class BackwardTaintAnalysisFixtureTest extends PropertiesTest { + + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData( + AIDomainFactoryKey + )( + (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ + Set[Class[_ <: AnyRef]]( + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] + ) + ) + p.get(RTACallGraphKey) + } + + describe("Test the BackwardFlowPath annotations") { + val testContext = executeAnalyses(BackwardTaintAnalysisFixture) + val project = testContext.project + val declaredMethods = project.get(DeclaredMethodsKey) + val eas = methodsWithAnnotations(project).map { + case (methods, entityString, annotations) ⇒ + ((declaredMethods(methods), NullFact), entityString, annotations) + } + testContext.propertyStore.shutdown() + validateProperties(testContext, eas, Set(BackwardFlowPath.PROPERTY_VALIDATOR_KEY)) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala new file mode 100644 index 0000000000..e1dfa3dd12 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala @@ -0,0 +1,44 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import java.net.URL + +import org.opalj.fpcf.properties.taint.ForwardFlowPath +import org.opalj.fpcf.PropertiesTest +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.Project +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintAnalysisFixture +import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact + +/** + * @author Mario Trageser + */ +class ForwardTaintAnalysisFixtureTest extends PropertiesTest { + + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData( + AIDomainFactoryKey + )( + (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ + Set[Class[_ <: AnyRef]]( + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] + ) + ) + p.get(RTACallGraphKey) + } + + describe("Test the ForwardFlowPath annotations") { + val testContext = executeAnalyses(ForwardTaintAnalysisFixture) + val project = testContext.project + val declaredMethods = project.get(DeclaredMethodsKey) + val eas = methodsWithAnnotations(project).map { + case (methods, entityString, annotations) ⇒ + ((declaredMethods(methods), NullFact), entityString, annotations) + } + testContext.propertyStore.shutdown() + validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala new file mode 100644 index 0000000000..b2ab9ad01d --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala @@ -0,0 +1,44 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import java.net.URL + +import org.opalj.fpcf.PropertiesTest +import org.opalj.fpcf.properties.vta.ExpectedCallee +import org.opalj.fpcf.properties.vta.ExpectedType +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.Project +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysis +import org.opalj.tac.fpcf.analyses.ifds.VTANullFact + +class VTATest extends PropertiesTest { + + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData( + AIDomainFactoryKey + )( + (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ + Set[Class[_ <: AnyRef]]( + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] + ) + ) + p.get(RTACallGraphKey) + } + + describe("Test the ExpectedType annotations") { + val testContext = executeAnalyses(IFDSBasedVariableTypeAnalysis) + val project = testContext.project + val declaredMethods = project.get(DeclaredMethodsKey) + val eas = methodsWithAnnotations(project).map { + case (methods, entityString, annotations) ⇒ + ((declaredMethods(methods), VTANullFact), entityString, annotations) + } + testContext.propertyStore.shutdown() + validateProperties(testContext, eas, Set(ExpectedType.PROPERTY_VALIDATOR_KEY)) + validateProperties(testContext, eas, Set(ExpectedCallee.PROPERTY_VALIDATOR_KEY)) + } + +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala new file mode 100644 index 0000000000..a531f2b382 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala @@ -0,0 +1,58 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.taint + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher +import org.opalj.fpcf.EPS +import org.opalj.fpcf.FinalEP +import org.opalj.br.AnnotationLike +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.ElementValue +import org.opalj.br.ElementValuePair +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.DefinedMethod +import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixture +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.analyses.ifds.taint.Taint + +/** + * @author Mario Trageser + */ +class BackwardFlowPathMatcher extends AbstractPropertyMatcher { + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + val method = entity.asInstanceOf[(DefinedMethod, Fact)]._1.definedMethod + val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) ⇒ + evp.value.asArrayValue.values.map((value: ElementValue) ⇒ + value.asStringValue.value)).head.toIndexedSeq + val propertyStore = p.get(PropertyStoreKey) + val propertyKey = BackwardTaintAnalysisFixture.property.key + val allReachableFlowFacts = + propertyStore.entities(propertyKey).collect { + case EPS((m: DefinedMethod, inputFact)) if m.definedMethod == method ⇒ + (m, inputFact) + }.flatMap(propertyStore(_, propertyKey) match { + case FinalEP(_, Taint(result)) ⇒ + result.values.fold(Set.empty)((acc, facts) ⇒ acc ++ facts).collect { + case FlowFact(methods) ⇒ methods.map(_.name) + } + case _ ⇒ Seq.empty + }).toIndexedSeq + if (expectedFlow.isEmpty) { + if (allReachableFlowFacts.nonEmpty) return Some(s"There should be no flow for $entity") + None + } else { + if (allReachableFlowFacts.contains(expectedFlow)) None + else Some(expectedFlow.mkString(", ")) + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala new file mode 100644 index 0000000000..82afc0ca10 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala @@ -0,0 +1,46 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.taint + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher +import org.opalj.br.AnnotationLike +import org.opalj.br.ElementValuePair +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.ElementValue +import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.analyses.ifds.taint.Taint + +/** + * @author Mario Trageser + */ +class ForwardFlowPathMatcher extends AbstractPropertyMatcher { + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) ⇒ + evp.value.asArrayValue.values.map((value: ElementValue) ⇒ + value.asStringValue.value)).head.toIndexedSeq + val flows = properties.filter(_.isInstanceOf[Taint]).head + .asInstanceOf[Taint] + .flows + .values + .fold(Set.empty)((acc, facts) ⇒ acc ++ facts) + .collect { + case FlowFact(methods) ⇒ methods.map(_.name).toIndexedSeq + } + if (expectedFlow.isEmpty) { + if (flows.nonEmpty) return Some(s"There should be no flow for $entity") + None + } else { + if (flows.contains(expectedFlow)) None + else Some(expectedFlow.mkString(", ")) + } + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala new file mode 100644 index 0000000000..345c235d14 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala @@ -0,0 +1,54 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.vta + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.EPS +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.Property +import org.opalj.value.ValueInformation +import org.opalj.br.analyses.SomeProject +import org.opalj.br.AnnotationLike +import org.opalj.br.DefinedMethod +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.Method +import org.opalj.tac.DUVar +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.ifds.CalleeType +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysis +import org.opalj.tac.fpcf.analyses.ifds.VTAResult + +class ExpectedCalleeMatcher extends VTAMatcher { + + def validateSingleAnnotation(project: SomeProject, entity: Entity, + taCode: TACode[TACMethodParameter, DUVar[ValueInformation]], + method: Method, annotation: AnnotationLike, + properties: Traversable[Property]): Option[String] = { + val elementValuePairs = annotation.elementValuePairs + val expected = ( + elementValuePairs.head.value.asIntValue.value, + elementValuePairs(1).value.asStringValue.value, + elementValuePairs(2).value.asBooleanValue.value + ) + val propertyStore = project.get(PropertyStoreKey) + val propertyKey = IFDSBasedVariableTypeAnalysis.property.key + // Get ALL the exit facts for the method for ALL input facts + val allReachableExitFacts = + propertyStore.entities(propertyKey).collect { + case EPS((m: DefinedMethod, inputFact)) if m.definedMethod == method ⇒ + (m, inputFact) + }.flatMap(propertyStore(_, propertyKey) match { + case FinalEP(_, VTAResult(result)) ⇒ + result.values.fold(Set.empty)((acc, facts) ⇒ acc ++ facts).collect { + case CalleeType(index, t, upperBound) ⇒ + Seq(( + taCode.lineNumber(method.body.get, index).get, + referenceTypeToString(t), upperBound + )) + } + case _ ⇒ Seq.empty + }).flatten + if (allReachableExitFacts.contains(expected)) None + else Some(expected.toString) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala new file mode 100644 index 0000000000..3bfedf76c5 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala @@ -0,0 +1,41 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.vta + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.Property +import org.opalj.value.ValueInformation +import org.opalj.br.analyses.SomeProject +import org.opalj.br.AnnotationLike +import org.opalj.br.Method +import org.opalj.tac.DUVar +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.ifds.VariableType +import org.opalj.tac.fpcf.analyses.ifds.VTAResult + +class ExpectedTypeMatcher extends VTAMatcher { + + def validateSingleAnnotation(project: SomeProject, entity: Entity, + taCode: TACode[TACMethodParameter, DUVar[ValueInformation]], + method: Method, annotation: AnnotationLike, + properties: Traversable[Property]): Option[String] = { + val elementValuePairs = annotation.elementValuePairs + val expected = ( + elementValuePairs.head.value.asIntValue.value, + elementValuePairs(1).value.asStringValue.value, + elementValuePairs(2).value.asBooleanValue.value + ) + val result = properties.filter(_.isInstanceOf[VTAResult]).head + .asInstanceOf[VTAResult] + .flows + .values + .fold(Set.empty)((acc, facts) ⇒ acc ++ facts) + .collect { + case VariableType(definedBy, t, upperBound) ⇒ + (taCode.lineNumber(method.body.get, definedBy).get, referenceTypeToString(t), + upperBound) + } + if (result.contains(expected)) None + else Some(expected.toString) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala new file mode 100644 index 0000000000..5f3d238a4e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala @@ -0,0 +1,59 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.properties.vta + +import org.opalj.fpcf.Entity +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.Property +import org.opalj.fpcf.properties.AbstractPropertyMatcher +import org.opalj.value.ValueInformation +import org.opalj.br.AnnotationLike +import org.opalj.br.DefinedMethod +import org.opalj.br.ObjectType +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.ArrayType +import org.opalj.br.Method +import org.opalj.br.ReferenceType +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.fpcf.properties.TheTACAI +import org.opalj.tac.DUVar +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.ifds.VTAFact + +abstract class VTAMatcher extends AbstractPropertyMatcher { + + def validateProperty( + p: SomeProject, + as: Set[ObjectType], + entity: Entity, + a: AnnotationLike, + properties: Traversable[Property] + ): Option[String] = { + val method = entity.asInstanceOf[(DefinedMethod, VTAFact)]._1.definedMethod + val taCode = p.get(PropertyStoreKey)(method, TACAI.key) match { + case FinalP(TheTACAI(tac)) ⇒ tac + case _ ⇒ + throw new IllegalStateException( + "TAC of annotated method not present after analysis" + ) + } + val result = a.elementValuePairs(0).value.asArrayValue.values + .map(annotationValue ⇒ + validateSingleAnnotation(p, entity, taCode, method, + annotationValue.asAnnotationValue.annotation, properties)).filter(_.isDefined) + if (result.isEmpty) None + else Some(result.map(_.get).mkString(", ")) + } + + def validateSingleAnnotation(project: SomeProject, entity: Entity, + taCode: TACode[TACMethodParameter, DUVar[ValueInformation]], + method: Method, annotation: AnnotationLike, + properties: Traversable[Property]): Option[String] + + def referenceTypeToString(t: ReferenceType): String = t match { + case objectType: ObjectType ⇒ objectType.simpleName + case arrayType: ArrayType ⇒ + referenceTypeToString(arrayType.elementType.asReferenceType)+"[]" + } +} \ No newline at end of file diff --git a/OPAL/br/src/main/scala/org/opalj/br/ElementValue.scala b/OPAL/br/src/main/scala/org/opalj/br/ElementValue.scala index 4bd70fca3d..5509ceb3a7 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/ElementValue.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/ElementValue.scala @@ -20,6 +20,7 @@ sealed trait ElementValue extends Attribute { def toJava: String def asIntValue: IntValue = throw new ClassCastException(); + def asBooleanValue: BooleanValue = throw new ClassCastException(); def asEnumValue: EnumValue = throw new ClassCastException(); def asAnnotationValue: AnnotationValue = throw new ClassCastException(); def asStringValue: StringValue = throw new ClassCastException(); @@ -168,6 +169,8 @@ case class BooleanValue(value: Boolean) extends BaseTypeElementValue { override def kindId: Int = BooleanValue.KindId + final override def asBooleanValue: BooleanValue = this + } object BooleanValue { diff --git a/OPAL/tac/lib/heros-trunk.jar b/OPAL/tac/lib/heros-trunk.jar new file mode 100644 index 0000000000000000000000000000000000000000..647855491f7f74821d6ce91ee935cd29898ed57e GIT binary patch literal 4324239 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 z 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 z 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%0D|kkFmyDXK=Fk$76}v6Gud6-2QKv}pvyQ1Zy& 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

Dt$$0j2!JKCugL6J$C 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

$n(w0p_aM)j21JV`R%Og%yI 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 z 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%0DL|^jLF#2AG3A>C<@p4`Hx^0E)K2R=!+5k5 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�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

;J*5n7Kr&3^g`VUN3uK#7ax~ct7MU$0< zx6(|}ysN@M4KW(iK@Cdsuc1G2SS)Jw9nRP+9+``AcjRmJL(p@oiPX%) z-Ti2io3$0Fa}Vr)To!RyD1D12!0nsCXZ9?fy?ZMCnel=4Qly+;HcxP_czq|}ap__C zSJ(L0la|#I9odU=roIFT#1#|u#V1?Bs=!Ye4X1_|U3*Eu&cmMv8+8*=kIHigx^UFeSIRXtAwO(;Q&*vUa?`9yh0lI(VGMi%WK{_orl#)cag$9F|Yw31ch4V!M19{2LvyDi>in&xOb{Q@!R(pLzQ?&6RxkXs?!;VwxelcGz zxOyP~?{bGIZq;&m2+NcsdgwuvcD`qb_(tI~%G)UG@<+-9_duqlAK0aoUPqf=bn45- zJIf1j9pz5(f}csz!B^35G;AZx@T79ynpzA9n(RNr8KpCkM4+J)e+2XR$04%7=r1zdwg|~QF zI}L8kCWkGKX2Da3g+YYfr`_pkp@@ARy+tTF?J+ygM= z{zD%7-x69CC`2QS&Ocx?k*YG7Dgd;7*C$LBSt^WTYiWppu||_h^0Q7glS!K`+0w^- ztvgLyw={kMf{0yBaN<2xxN z@ERp0IhO6M<{U0x&**D;twa2ELlW_1S~#ukMCm(+F5dFBaLj4j^PQ2NEFfA~DyfNk zkzL)$gRv>P`6s|ekO|Bakf*8~Qt_ilVdPpZ5STO)jO``Xeh(v?+KM)3Mb z?8mfv?NCwi$Tu%k5u?21>BrKCeKR9^-Q~BKc}j!Bic3A{&ytPS&q|pb-=X8o$Zmom zPD9rWwccuWKfFT}i0Nl>GDY=CJ?3_k%D5lipJPYWPV?%(f)=xoMU`<}=4#c4V`&4p zaRm5CIkR%UVEvw-Yk7<{$IpNm3`xgLu1q!nT_Se;QfBq zuJ{Kd)r7Jsde8|Y;5h^J>#}8xr}(5{s&PVYe0e>Zj+JRJYE~Q!@$nJMn#*SUV7Ej1 zHhCcwmJ&lezsnNDRwC5dT*P@r6+R-mt4#Z>havOvhzNKfhxQZg=Q2K@`#E^XG=f>mR{fWT;wViW>ZJ{h7xm6noWr``={J7MYF zbC-QIz7Ih$3`6un2HqBb5 zZa^OKuR~2_)8iWPNm;?rk0U-Xdb8z0{#5N8bs5H zlhVrroKWMM;U{aQVCM`>|*u+Nu&=Q~<@bku;>sf3) zK(z&%q`@=BcV#YSQ9@slKE^~OByvKnbF20jO^|c%MWN@BXTFCBAhg0jZjUE`ga6@L z`|>Uc!3dYLcnAL3N+50=lbCoq1cxQe2D_BjiGL|t4`NQtZla7ymv~N%x<`nZ#uA`miasX|p@E#UuuJ>- z?Rb(&X;xm@mDbE?iQShx%tn#pS^2%YtoSNx(}_M&Qn2Sk(>LljQ$A^H2NUdMpJxd=U<*JKRf*1@gk$L^EfcSY-Y19w4 z_sTEOt{v3QJ)#H3q|OZ}?NSLib^5m!jJqZy*cNq6Lk1?9-NLE1@7M2QZD8ts2S{>6 zl0088Lp3Yb4tY}Wk|h&2(xm(Xcx2De+Z1tam$A>P2Bh1prd^Kfu#QExc{`nEiX_Od zUJ0)zvk>@yfc$@vjQ%duoWJt%c)%C*f1n+SI@r7XkCthU%9cH^Fjcu0 zwNCYf%Fr+`F+C zQ@>+{L8$dtb}z>_Cfje+UU*=;Pfj$W$iD{{SUzg-qu3LLbs@$Ef)e22kSUq|>SIP& z8J!hVD22yqB6Pw51T@B;0jvzsbWaEA4_`M+ZPGN^)o`P(BhNTsb|@~{1595YGicx1 zDTCA`RUkPDV1J8wwqkSLYWZTzI9)$X3`w+Fbl$FgXNflmUX*LRy%JJa+pLC)Q`Xme zxjH#}KiR|^r)(|c%oi|C%XAY=W+NgCG zs1rj*`3|-&0ffeQp8Rv*)V5Kw2lZbx22jrTIFQu}f-BLzz+M|FPMycK6wItn375Qz zK|V0De*{hcB%S#owk(&q%}^5@%-7iXRW!LeQHnPBGurZO(XOy7 zwDnf7`6r1^ltQRFYK)=$J`vy>i6?9f8LKej(?42Nqm7@i7r@@*9@u;QhiX#6%J$!Z zlkhK((9j*}J)o|jt_YsxGs|pX4(5C$UMxuqhE#YtQOUS(65YRSDxg{fqg1O1e@%lr z{rW>>ej#QjpzvU1@tAYN`t|i}X9)?!;U-h*WxdW_oUU?;E7`c->%bcl*{HMivvv+^ zM{EhrukNLND=lZWf|FvwX5>|2i%vrYS z8hwA}Qo4f6c`7JDC?bLfbo+OC#{GV8TGH&@Ke z6xFi}XjL%D0%9N+qGv0MaFP*ra4`MZmr%HNznOMvzq)yRaHRU$eE|-&GZFg2Q3R&j ziQLM{;K(Q!SD1EN4tu;}`U92=!? zvHXOP!SX!>*DTpd49JSqydw|`WBs0z0|oQxnKRH+~!=>I=yo&O1sDSa42wWTG2 zKRp?8w=8SWpr~LF(D7zqWd5+A$@Kn>)USnH!JuCfe}s;Ckx*;{}6#h z-yNcjZq<8DzBu}LPVPOnN&&b*f_t0C7AU4R@V7Au7trn3xXgDurV@r9Un(vsWG6Ns zrF1>@RZVXbtsYd*U=DbBp?W3r<9vibSfEYeE{oPIz(U;R4q1)qZ$L~@t{XUu_nvx) z*3tcmKoPz{qjU8rq+|Q@#U1T~DgB!JZVLj-pBV=ulpuK*408X+lJKj)F_{lnJ=fr9EMsHTKC|v~~Hlr3Aa07h4go2;o$D z=-l6>fpCh-EJu=6hI_jRr-X0#?=ro`hggz*Fc+<&@MtMPUFOYJluYRfTo(8}x3Cq7ntNd{4m#u3R`~sQr>=5WUAwMwN7QBw)h0Ov3x%X^;RV2>Ida@g7y!?( z(ubs(Z)UTDE{>+pHX|U{4k^jYVJ4A5X&F~A{-Ul1n>HVCr0fZ8J0j5cxEigciq?@} z2~TP4Uz7J-A^^P;Smm1U$gcCPju7<89k~Z|XEM}gmp9y9l`Pt(qE2|`bTggK$h@TK znzqnDElNJX^1FtM%_|}U7sGdc+;7M>Y4SKu3}^vLAz^i5s0a^)-vb><&&|^v5c?Sb zvE9C3y_zmT0VyDT>iqLT`IHqKzo6FYk^HcO&vw5^oFa;?_ZQEm%1OTY@*Ub9*t_wC zjhf4JK^IW^Odl+IlX0dfT7|~LKQt3BH-97_56fTZfp@XfuR>JPW}lIqIQFcg^60_p zLxg&X9<8Da1xgf4{Hl<}QsOw4{%NWTvEpGKQuqPEH&!kf0?kzb@`nD~`C@&Y zh;KrGA6#%CA*SJIS&Oe*yhIFxoCHDe zTqC&Y8J)O?W%%)nA_fwRb@12<8kB{!jExEMX^b+6& z8nvNO-rg9^w^fn3&R5HxDw=mmOqAD=3cHhv(fcJ5Rti9kT{Yib zUY3HhZ!%(dA%wGfLJ@|3bmPeu9mb)Zu))Af9hZ)JI3$am+of}+(@8tBl$G3c%xr3P z56;?@+gi+s$^2>)A-)0y{h?JafV)FiCd#(okyZAR|4|lSp>4{-O(^fxRg{s@GbGG{ z&<)L@VDYHWf`rUBGdrq*@sdxDnD~W7m$GRTzk&7$YAk{)hs%l5sI67X#a7o;M^!a` z5^Eu1B#DnarlHtXRX`8HQ4zB?R5j2v9Xl{Aq1w~bMzXksxp7(RmVtn6@vJ`RVnhzJ zr8!J@y*;-(22M+q9M5ALo9qGiHZc!Ul z4zXQ@gMKo7vk0fFz%HH}SCFl7Z9I4%xKuuUV+_tVkrirdrS_f*ye-GZbs5IkWQI@5 zlhXs|1$1Ifaq8TNuk8L=7nrNjP1m1%l&jTA+ApOw(FZuSo@w$m% zI}R^l;u%>_(gur_wkho&r;#)pp^5NZHJ z_RaA;aSd|}yWrvzWoz8Gjrf)}Lua#5(Y=FtAWyFOJ*f&AR>IrL0@c zjT@J*@I%~h%Jl_r(JC?)^p-?6i!G#RO88b6pg_r(kjC6AHI`KeFCB*TrqMW-HSlN<{Q*C zhW?x?L#xeEBk@r=zoclR~nmCsl za~}Nw`h6Vx9ge(rR8UHwKOMXCEo%+MuaVH`Fi%7LFJn!gavTZP9SW$e37^6#{rr+4MWid)FqIQ?;Ju17E0wdzjVRR|Lw znY*zarpVW;@;a@W6-CX6Ss$KfN;O%oPK`D9vPjgxbTG~o-EzSxMzi7{;Kq3%NC-2A zA|~ZIJ{uaGmEoCuwpCmxNKmkFQuTpASdOrYRgguZBw%M|oJyYkQkt=rDgv2g?Z3zJ zFtWbfRIw0pXx=FxrC509>P?cvxbVCSY$*WuSUVyTiHAi&uEhF-t)F9ixC2kf`B6?m zlRRtiJJBDqCyjI+!3-laO=%D(GB^7)Z%%|C0EstGnt=9`>GT-C#6c$gD$(@Z=|plD z{?fs)nl8LAYc?m|1PX_3Q5K=Px^7xiV8^`~bIXTw?#(7|h7JKenfw?*@RJ~z-e2Iz<5^}j*Wi_0EFxAWz#V>@kf!pXRF z*KSy1T`XG#5*mBh?=YWs+P3`x^%ktOR^{p;Y9weo4yxzP5)k@oOdG?=2e#4>`n5@p zTokQX5XIUX9)(v~&Vb&cwKrr2&9!8t3*v;ZBTxQ(A$mzQdWn^X&TLoKDw3DRKH9G~ zNFi?^2O~CWL$HTguS{u$pkN4N5-zV@wE4?%-6{gp7se>Ft=>^|Ec7F;vb9xC`=wzxpZ2C2 zmUPkyc$g)5S;asPpyLSM5nJn5#X*HS4zsLTj`*6|K*cWgiO}ixg7U5v7BX4zv{yq% zYbe;eWMs>hXL{m@+PQOc5uO*3*@mf>uX?9d535YUZg8%TeQALjfYg#k7-TfkVb`{5>IL1KQ4Ie&5 z74e+2jbhh7N}T^N*CKLs$I2DbC|5Iz67943b3@9INkHQ)POTZ#)@DY2t0CA8Kiq%Z znBc;!0m1BzYCr~j(27?TFPMLoW4w13Ptok$U2gihGGC(>#+Zf$U_J^0&~ zcE*|%@M+8F2sl#y?FNPV2&fiVKeS@?J)W&X`yjq0#w**mY6a@r0XjbjlMwBv0r*Dg zFe~g7OZGs69K?|(M9|uq5WGOw&L_ucL{%vbCA@-$S3W!Ac>96T@KCnYkY0`s)hy%o zOx;>h{-~d)T}`=T0nS2oKx6bJYct)(MUGJMzn%rHN32V0uj|iJdA{L$N7YU9X`^fS>kA~NPe|5^xMk#IQLt7=6klC z#<)(qeyMdr*CxstI@opubOO=+EWz!o_n^TtiRJYGu*jR z=W7jV1)i^v?;k`4=l6qLspfj7Q}<&fN~=XaT$7!wcIJ)znLI_BDr@N~WZzm5-DNul4E~GdlN8iZ;KNmBnS_<2zZ7WRZW%;-6(x&>G{9j?c zZtQmg-&V+$C0E%6iuk&z?DF2Rgndl= zQ5TI%OvJ6G+O3*;hB9>)AAe>OfPWKU<_Az`;WoQ!#MJ62Bpql9Lnf@2ppi;pgCgG(Rc9<$es;ixECyanUp>otmtkRug^EO_0(hDfO$D zr^kr z(NTfbv`3Jad8nUa|MODs8_Enh8|l^-|Gw1s=#GZx6D{~Sj3nEeVeg0yagr7$1{`SH z7a6W~n$g6D4stsUR-cjWp@|yEXJcwMmm>5bL&opa$xS_A2MkKog9(hSw`^^uU8d0o zeus(d$)9C|ZheZ9P`5+cmq*KCRNSNspS-Xc@88rDb!QDK6nX$~XPY_)L$;sf6nM(HqDa>u)}J?g znV3EEtys!+ZAlggNB>>5y=MZQ_bP*u=@rUmv-W{Tf*^BSH9w47eqX-W2*x2(ueGyI zcB-AThb+JuBBLL1X(eJaW|`xO-n`i_ZG0CSs0C|#7Z{TBBs`eX0B~d=UKZxN1GHBcLC{xXqKXrt; z;-##^JHzFmd?03H0zKj5KJ z2dV@ofDHwC&KM7RNQOry-Ax0!f**KQ*ycLBr0?nmw*|kgQuhw>S+b7g8~l#I98Bxi zL-3=A&|XP!C!otAcuOVQN+w&9=y$9^1nFvS;K8G3;zC^LrZt%d+IL0kBkRTV&>5rh z3|)8OeDAO5Vb{J_#DJXsfqJB!tAi^!6M^X}xoG6BrwzY1nWqh502K9A)5ED+_oYhr z<*)%oDZ+ZrlLLt5<2=Yy3GN@#S)W}=)MJM8acB*x`~qL5;&s?&CkCY9@xdR*u_WtH#cv^xpV?xsP~@RLC1( zU?E>06(dN}s*YfqVHNid_=YIO!4pZ1$dQ*juXBT!BI9u?L_lThB-mbwdV9GN&}qm* zt_9vELW`A%jdld;PxjM7W5#og*2@{hZiv5=DYMC2GhWy1dCq-$ErRj;f`(zftWiiCq$=6I~Y@bEw=fp z%`TWs1lgJ2%wr1O$XDFj`{o<=?SkU{;v;eI=VUW;4{3@aDSco@H;;OqLJ<}Y(anPq zMQm~*TTniuKv)8?-llDS9_V4KbvVQi!9Yd|a@03^8is^lP_Wmg(2#hKD~LS)NXor6 zDC2gfbI$X%17RV4}oNHJhk=x_cY z-I)1qk5?iy89|ITK_K#s3Z;=A zqfKHw5)~54S|^L?0XsVW}N?)oQ zz!Me3GYy{XHl5Qt&a;51E3UvHgrOzGADvH;D2=+v4IoQ@1Wxo*P?C*CE4c+unkht2 z4*1guWAb!cHT0{<;f{`dZ^MdOvdWk=4vBowDfY%mqQ1^yAm$H@KdKsPmFQ?A64B9{ zQl@j`Sx0{1yOV~1%56liNNV-!} z7hC3uC0!L!NE<=9WFF%fCHpPRutu@=4#fNuqFuw_U>a+)suc+sHk0`Dh)}XzCBDB< zHE!hJwdbklU>MklE z@S3oNI-$sws$tQrp&x>2svkJ0DrQrhV<&`+lJO2<9+92jO(+XDfQT0wJb2AD;#fN9NPHSKG`3byd5SA$s4AB7R4imli@bI~ck-pN zT}JHM?(vH4MV4zdFWc^(^7m-GE{KmBm`@-$uV#+aJ0H72{=X{mUX}U>$S8vdchzQ}=ousCeCv?a#jBcSsj4V&A|QE{0A5_0DR)b{Z6{x*=Zk>Htw{w#bR1Voi567uf}s6A3J--?47Rr*PxoOAjD z^y2X4*pp@0`GTwg!XXAuI49;NT>x`|riacPiDm1ZN5|dwcwu{IZ0{=djRDAC-Lp4Z zg(5ttZ?4G@w{&1@mHyc@Io}lAkX)HY{_x|W?=Dn7RxhC3Ytw5`wy&K!Sk;(5vzmHE z8GAKE-$`T`{r+YBTbgM)sQbzX!lwv5UAY$8H5rU`949~pCBQr|wT4$@w);TCjSF)f zTTOpS-u{fD?M~=-eggU4+-O|(I!-YN0tw8#9XOj5sPRsRt-nGlvR~}SjlFYRaC|(i zNwXRZ30H3{+VDwO9_|0b);R`Qwr$(GveLF~+qUh@O53(=+cqn0+pe^2n=kjd`@Hwg zz9(YFiWM<`juw6N(dTGi^H#hR?Y2t`MDqu(0wt2c6Ob94R1I3ml57gB2V*jQw*; z?&nnO&s%K?KAhy)`8-}|L3%CFgN8WB;ELStxaIF?n|nAnwNN2jmz&8gyy9V84!F9J zIu`&z-ceQMet`fJ=RB>a2foOX($VQOG-mD@DsRszk|f(NXo+$VZ$rF}3)J=PhY)&# zywHg9c&-?m#hyHV@ztIf_@NOQ1ERbkF;7Ilelg>pW{5I2jE1u)BPI05f*SIe8g}Vx zx=2y|wT_%iVlbxaX*us8>NiFIk$YDiy}e#Gdun&2OW) zmUqU?6rO7%+TG2;(f17CfB-eTndBy>l-@?go#Lq}x?s(>z1Fv5e%80I{uK1wEvFYQM@qopQo^8z3kLW4orVjR?9LRip{*jH?Mi5(X0?!X0eGFT<^_5~ zjvXu0MQcNo&ROWBBiOr*?TQ_ig53MW?RB{3Db!&>S!WgfC$6j!6PPYu?uN^4P@ zWC7N%PM#5U>+ChNj%37d)sMuy?80v}{4ill%=IGGYgv|l+d)EoyGgZFv-5Ns>Lx@= zg#QWQvBxO-g2^kHYdk(ehf=fG+LQ`oJMjH-AO2@uE`KS1z%*(J{~IRL_45Vm^q?c{ z>xl-w*poQCPk`Tk^EL(fE|nY9^r;_NoB6MEE_ahw>!0&mEj(QJfSFc2c-DPf`}|leSU}Z#zpEutyt%i zCi_S=+9lq0AjPymc8*25B{j&Zcr#g8fQ2fM7H(6#P3d0yV9l?)FX=N3&m;y!yW3~z zVJxHUPg6S7={`o}Q~PDzrcVonp6)bhJz~sTwFwFQJP$n+^5M8zm21%OzQc1IVGdca z8jSKLsKs=?BYH#ze)#=>fj?u;=LOf^A;tGx581?-N5}u`5vIVAwP22|ib__s?V8p# z^`#jdRWq!v+Hx>k5;r5=F=XU;(dVtwQlAqhL||^DI`gc}qFT)$KluQtTgEL+x(^=D z@H@w0LuMThwssw`W6wpeJ;2TL{pb~;yZjK|*P`70CfV=M3kB-DQ7z$Av~Fzf!v`!; z;3|*Tdo5nbRdhglD}w&aYoM8Yptrg-A%&(-7oUg1+ZI1P ztdfG;xUscbAJ?Rz;qg7@PkjFjd|#y^Rm692X1(Z1<=V$@cr!c|oNvAeB~{#v$ktmE zZ@1IBh~xW=>m~K5S0v0iDs*xp&%2+txuR1_o2e<-3?xyqD*U+7 zy?xjL6CT4b4J+|STc2IhJOE0o5RHAiykn;VCAi-W-ND8O&h1Mh0r>YY4c!y;F$q6k^q4vk*T8DJ3m$ktu#q0P z!y&8H54|&yq&?3P-S+jURzdJ<28I#Wj1rLR0vF9*LSmWp?$xhp#k;Ji|mD;`X2Vi0=j zr*iebb|aV)-<=ZQ?b`R5LlXwuq@olTAaVq(^JVk*y>fp^Y@$-M22WIS;EI=GtdxoH zC_Lk(tXo_{EdaM*xA}L7newW{n;>4cy&4Hmvtr68zPpWM#b2$9DOLWu8yN z>JOdT!gtidchXlA@zyDUmj@uPar6+w__q@sOUE(dtnOW*bA|BQ^{U8&%MtV{8Ee2xx5AZ#O#TJ*oYH2yL72qR zTlod9Ue0kM3jR7ub4<$A5j%Zf(&0!DKrD#uo}DX{z8&sOQu>mu+j??%B)CN~a$^;Y z-$gBVW}*BGmwrQ=O1_K_vymLXUp6@-h#6%6XU4GSit;hMf|_96Ou+5{8zi>?gp(MG zd#HAk0PGgK+yQ6I-V{T*qDWfL3T|Ce4{jndRKme+RiBoQOHb9+CpTTr*=xiLw=IHX z$6P$ZeQ@*5TU)VyDO#xeXdE{A9mr~c@53F@nigZeXuCDHET5y9d2aG@{9P)OE|Vha;L;3|YOES4IV=I4O@$ z$CGtbpCIo;^(xAD4dlC0YfBySsUeTHTRYuJn>))v0Ywz#iDN|Vuo5lo4aLeJl11U# zSxv-mAn9GKv-h1rcWnqS*TuzzpQr@hl!kJ(#+tF)A%}l4lyF z50a*XfZt|@JQlPqgjG20l5o3qJfBqDdGa_u3Co*ti_T-Y1XnZXX>UAFDnGF(HQ@_NSWI!S%&&N(Wf| z)12%fBo}Mz?A^dnI-f-I&x~vjS-dwaF-FmPCAuOBlXgrJLGeO)!jkt6Rgfh_CfQ17 z%(;0YzGY%?%>c&QL7sBvVCAl4-$w@+eYH&gIF2fj)< zxFd8k$;@y&Zd(geKe6+~g}1bg)NJnBm7BBouGcVd4hyjn(}? zmOtPAnLi6pE-yWw=BIBOp1E)wATOUSK-aWDwjC1AT4sp^N*cx83nPJJOs3o+P6DS2 zK&luE3gLW<=JS=^blbe3KR+Gtmwd8+ky1{GKf}SxsJ3d4XH5&S05C@HP7V7M5q-d4 zMx>cnOePoBUo9G|Zd6TPEuyf8MNn{1+F8!ZG{Li0?|n{&T0r)cQlp?s2>-z^8!~W% zZ)h6W%P$ZsI*VAinnjxB#6pExmeer6pGz*&Zrb9z!A$FlfuFLs3SyYlr-tpR;s(Ip zlh9s<@0m$PuAPfjyareY{slaYVwJ>vdV}jP#T&f(DHy*;>N*nbPJA+q-~VT0)hwE* z^9!8)S~0UOK-=l7kz6wLs%03zcU*shfAjUnad_X=UtKf7( z{@f>8K02K+#Raa!6A5NbU&B>jA%JV5S;iq2^fvh+%g(=0hRq39iZIqS0dd}wJ^Z$n z`&{Pm?7i|}4F9Ai;tPe)qkS{3tH&m=+@?w4F_JkxCzxzY{?Uph6tnW&`j%&PStaI~ zCxZx!5?f=&LQT1bAK{69SDOMYnqv7>iC-%EcCQPqB-%PAZ`3I6IROHzBp$y zXmDkvh`~o=Y)3~EM!RMZt133D1NE@Uv=pVZ+V(n@hnW5^m z$9@W#sgmU|W+@0_rkq-=r1vmY^WF~r)T*n8+P_$JNTzm;8(R-0JkkR)Dgs}MY+TQ) zv6e2!>@KQBCpU_`6sy*x0RFy1dk(@v6Vh<}z0~80<=VN%4{X@hmy(cEw!wE9S8^%N zAT+HzoQcEeMkO>oW1U#UguZHxgP7L+z=Cpejt^U*^McA6RSDOU@xDw%oLB#ssevUf@H; zkD1>arCwXf`%Yg7pK4g+$PnqYDZGqcZlyO*x)d==^*3a30n?hEhYsawGon0iV~T$>8_!10-$1j48naTmxdOTH0Zf zYb^Wu;%;vE=?=<2_3NnuAHl7OJGZh#=CyQ>IhL%J$ruxoj9T#pPPnpS+0Ee=X<4ZJ zlNN|OBoLiwc8il(9AO|~u{0I!8C^#Rm*)s6UlO{|uENRSI%~fZNNdhP^VYMm9}N?Q z#v*E_MM-A_NYR1`vW3r^DN(&)tR*saT}bLnY1jNv?l{h4Nayjex{_{s4SSxxNT7@w zm{I1-GI!CR417fgY1zgjY|_4PYL9A(n?xQkt3CBd1DH3n+~9~cjowmI$_TIAj$Y^_ z;&|(nPbPkSa9U%X?T2?V#E@7XYz$aUvf6<@r9K3HFs!|-RQZ^=usi>q)X82_V>e>0ooCBHENNI}#4vkOBF){1@gToR^PKPBB;QiZu4u>tP#aPirvzheIgGzz3Tt+O*-pqr?1bAw_FBZd3bl_1v zaXv*~En);Zl00_PpOW&tEv^qDy8Q~+8Jd42_C|W3(SEKtoFEhC4*^?Ows&FT#6i_)UyitWyb7L0wgEZx@mz^M&ad%GF-#-Ad4 z_icXrix;pjv|O-51@yd?R_sptnQG=uvY6^Mbj*xQKIPte3;lBIg4AW3)W;$kfT3cG z>B4q(t)YN!Hxk#(pG1%u3HGb#9r=Zlt3xWCjBiIr4@hp7kZZ@-0!lS)IP$X}lZ+T- z%s4>0NrdI;cSa&*cdkyJEBl^;#x?_^K#{!op%K?n=1!RPuCP5r9G~Ao_kY$A3vIB6 zy>FfT#gq8oI{BOTZ1wL#VuLx~1|4{XV)_e9ltAeqnJiyPNr2&gX6B7#g(zCHy)H}Q zy@KF|7$%gF7lQt*Wu}GweCm9y*=BR<@`{(&`{%M?LJdcHQ!oKal{T4?mrFAgL5F?u zu4V(*PK<`=M;X?DRft;%_7O$<{F;r+8u~G_;08AAkj|Pwe%*;?<<)mN<9Bqm zL&u6!+6;ul^354Ka*f@2k726pmN%AkAuhI{F6@hLiAVy3*{#?U3J{`Y{)-K~1+UoO zNyY>cxhz{^z)FvBn4Ye0IV}P>c8@xq{#gS(j1D;wmc=YjG!e^_Iy2q7qryW)O#?>w zNn`yI`oPf9zz?Fjq^t!(NTvVeMnGNW8V!7_Lg2ru;%^r8w<`Wl0~xM{+$kV0+Di0? zR*)W_g+~w&Ek=e;R-qWER|FW*OjCCoh6Sq1&`k$^L=}W(>8Onw|hkrxF9Fo^>0fUVp2s z@>o}-C<=vZF9l4z9}yLZxQ@^;0kkk}yq49on?GH$&aer08cNzD)A>=1kK7#@){sDy zVzg=|LL-fazf)r5d>0p#!)uHkuS6Xkj(6NC6Z-Xh&(_c~5hMXb4u z=BtTTxDL&*A`Vozj?Mb)I#eD_ZZ$o&_TC9>(3mi#v^IJHo*~fE8Y86>Ls$SSBV)GO z=qGqZb@vFeVW|h1=f3=7)5v2t_v`&$cAo#%RQ#7!_d_z3$fHhFsNpY{XXp4A=f9X6 z&Q3VP5Ij2aJ17XC8-hmZ@1UScdIA7k=fjKV$&BW+kGHEOn;#lA;0dIFC8P>F4F=?@ zU8Z<_#jJ3sec(dNF5ZqHbs23q^N+ta$y)zx+BI)b!C}a9GAV%}|0&87nNc1#5hh42 zpulhxV2;kePnByvw~V#e1(p)1ItcM@lP1%6j0+Q&LNCql?K>&UxmL1S=f=!K?5kr| zak)4;(gSBsptC^sWS$+c00cQyi^ZNs!pi(@?Fpgx$DKyGlqW(JM8b8N#)k+}_V)Lcj-=Z31CB*D)n{<29(}|8YQNWi^pSr2 z`*|DROCI{)mi%w#^1u7Y4BFwLhP)u8G0{BgL2F{DF?#tT3Ic>aPq8rf9pAs&$o$g>|a$9q-J3PGY=A;RTWl``;a`-M7T7%>H$fCTicNXuWZ@jdktZ@HQZ9|bVO8h?&RxC%e+@k6g|DGnZ}*Yb6>&)W=Ipd zUt9dy`1lnSXK);xV4BOJUHqNscHO@WDe_y{9t(!OIfXwrzlnBFZP0bdcnogl`8ZrN z6Y>jZ&cPJe%I%5M+Ot5hhw$={DHOSK&e~qNZUptR11o=SU-g}`u%(l6oqe)pe*cvz zm9%7&;}X7&S+FCz{hlVYTc4Biv&FgR=aYjIw1@(Ym&+AuB%@`fwn8bboZ47f`0}VF zvaG43C-suvETjD&zK4Ir0E{1q#Lj-h0rtKj8UO28@i*f*Noh@CUJgmCBr!Pn$2WN! z89A>(j2u;1LtcnLtQ8FE_mo+BG99sA{6bvLb?=SfeQ!7q8`qnB^czu;3)aO%tK(JU z)2c_7!|mDAH(koOwtpsrS8Bgy9MPkxI%Y~-7mCYaWa~yKE+c3_7{_4}@H2zq%p= z3UD@zBtFsKnhXm6smE5Nq-;32E{rRYMXF!R@HmF0jpPyeKA$%72M8KR{;m&@AOnY8 z7(N+-!bzIQBpLoVVIxIKDs=`BhgvgR-}I)imQkPLanuck{lw)19j zaOCx&P`>ptPHx`jK##x)jluP3dA8hA8eWN_+&8p@@39uISeCqG@gYX?iC8M$PGh{) zclfdJM}my<-fn6ld_N$Ch8htO+E9K~Q1itBMf^tUP^3@YnmD)vlwY$rFsyLOovRQ9 z?zgkUu6*&pfD0ZhFGa+NVPLIBo2bseL=EYKr=TAnA7pJ^vwWYssPo|oQ_7`Eps!}8 z+TTZcH(jdv6xpfX^1!jJ8C`O3(90T=OJm__V1RDjl#89m<=zNNaf=Gu$f;c){0aj& zp`AG6v10biJ9RQVOOoK~7qP~?a=(EVePjLsnz5ds3v4A;=K%Bk!o>lB3)V(2n9~Bd z0vHtnJZwl81vV4GG3gn`MH4uDu5$EXw?5q?biWSQghhIY7c9*l#P)>{NU+l?Wy7K; zItKpqZJcp@Jb~~dESS4ztt)!7={mPClXH04RVws0)O>;wRp!;WSkCUt5ye|5uDVf- z-lzd;yHO=v|0{4Qs`7%Qz#sFsK|D05*y<}p0``+;4PSkh@aogwoosP5Tp zh+g-IqOJ$spj<$W%~j!B|4N3uEmwPbS%udRfdA}>kDCZJ=D#bV;eV@$|K>se-4Oq) zBAP)0Ku`xI>z5#il(VsiD^XD3OL>SbP_7bXEX1{oJc5L61+=6{#!9peO_4mX5+bvV=lARYB1D&vXZP?Pz!HkF%Ua^Gn`+O-Uzp z)7Q_jUZ}rt7gWNwQxIzo7#9wip%T#~ehu>l^)6u0GKKYKFEZ;gD1pMON{qlJa#*Wu zG}}S42o|A_$u**dM|)Ts09T-lmBux-fhkg8O*9EluvFnP%1tqdjH~>a|70Msc=Sx>+P-~7nyN%CfS)%*r*N^gwIWC!;g?8@5!wD^Crf$0RQ~sSo9S)y80K_^Xpp# z|356~U&m*be-Bu!6s}}f<&d~xV}C7*4?-V#AG4%U4yt31cJYm1rH#gO@QS^E`jRFu zEGFvLeT@;%6Tj7*@9p9?o_GBepp<^Syl8#Oa@ca~{CGNJ^`$6>dNtcRy^YnV;tQP% znaln;bUk}{Cu!Zfex@#Z(RCcioPhy*`e^CV*{Jh9oSPx_$6zLqVIUi!GbD=erwX80 zb1_8hSMUV;EAN7MJygl!Kz`=>2S$G%v5TbAWVD<_AESxfDzP-D43UM?H=+@wKRu2r zx>c6xSWP9aj}TgNWV~`Dmc_8hmz14KH;N>M01BAX3@xm3xK;E{h8MkI#Rm9nF4V!h zS1yx#$1x;0#WkrPL^}cAaApi^Yc#uXBH8Y1bLdp?&v-CK3U;060K`?_bx zO0{9XFSZ$5))PCsS(r{F{HDnUH9_TJ`NWM23nUS9DAR1*!5cAny_Oe>W49-4pT`4x z371X#N-ogEJH_&C*Nwom&65reu#QMrwKo^9=d+!kRn5xU2OsEJ!y`u4u zlG(Y49lQq;<9A~0rf6{?S<1A&kj(Ty`YU}oLYKc{n1laYHTfIrnWV5~@x7sbPNlO* zMp{(;%Iao4lj5Ko>q&|3!-5Gc4@mNtwO`wGCRb;OSGw*_r-eZpAcg<>4h|s7kUokC z^q3lT>AK5GaJV~rO0oITv<;^+r07C~u=7Yjo-yqNm$#Mm&4zstrnB3EDYj2-_)d;m zo3gYi+3=XXoX|V^Ns&T)h&QMVo7YrYAxLN{Au1ZATk205E>v?U?1&b4`z(glO-i*U zv)X30gFvN1?JaPZ7L6=mv(>Y&`LHncaJ`lM&F>IV6Z z;}M$xFFTe-RIapFw_{%atn}{hYLPx7FwFDABuTc7AzgDKOCc_{}1f+A7uNCg6>n^St(Li zrL~>)4bK5;k;LTL)pwW=#>*V4NV9hXDe4poJ;H}^*9q;t;uJjxrD@^bH1pG&f8bHz zYu!8$zst(%e^bN%V|U6Tekbb8UumiQf}AZytm5O$+0h6YCvmuh;Q308GjvmR#| zr&+NUe^*WYLBfNU^Fcf0qnp{T)mZdqdn7Y8k;%^Z^!fbpfa&YiK}7W!(wGe8*kFj25}x?Du;%?O^d1T zvQnzOS6%B6rMw4u>19j?5KuJ~hBf5HwcS2Oib%k_a!bP|IoFU%)ccfGZa2uJs@B;% zWV9?DpGeXhD&(HAtnZ)Y15Ko0eB~3M8srj<3M8Uz{pQb|bcc{vvyS_p4NCLYFNCmf5&ixKc>S*){cpaamEx7`cS+&4 ziA+j@K!m#?4GrB0j)YcnL<9mNMwjU8Q=PK{mXHu_iyrvu;{`?(z7N=`|B8=u-PFK` zN-pxc+VI$HeOk}{V1IkS>|s&?c5ixOP^PS5Al_;kHf9F%E8ER>GBwg1UmNwLxLQfV z(;>1flKZ8U0qL375Sz0ni)Z%`99+t$v^fWd7%KT(2n3FnLe4Y*d>`DbiBqox(VT&!|74o z@{c3||Dya|+rA-WV$V%et_41zi|ubrjLUipg~}r)E~HRPBVdq;CRCAYv0*w9VhR)8 zN;gl0&1rL2_Y*-Ypf0Nm3o(U-E~gse86)ipiYRbwa<2KjJ&+PUU%sn@uxbUln=jo|8(zP@Y*r^+1~G2b|($rG!6krC)@^gK0x4 zrZi76U*~ym@cb2_eoJHR(dXPKrjn*6wjvt#RXRtWAa-ymY>Y?{N(a}(uTSnWSKQB- zxpn9j5sQE-moPk?5~%80$~%!ee4%moa~A2c*W!8MoMu8e4Jn)$OyFwGqFEHsW86CG zeuJ4@r5|JVa|~}T_~o0 zuAxmd1J@bS5Ym@fgST7((^LjFN@^rO|7NngI|#U`=N)skUV}#aFid~U%sgs6Vn14+ z=k@XV{8N9hLVT~@B(&U0#sSrRwfC9%S!5@r830VXV<4=&iUB#Y1rQc`j z?sB&isjSuofDpW0Ktp~Nf)(3$6)o0S&OJ1F31pHQzi&dnU8O=g%cTa8t5z4oc?R+;stC?@!hWaL2qSYUaFPCKqx#)3fVu{td%(dH zm73W%mJFGP6f(iB11ME@89(+1V*;EQm<2&15Wzx?7#tJ? zo4EE!k0Hrahc8+9syS=>%Aom^xy1zqO}}aRADYEB$9Wp(-&&20Z-=Yd4p&*MSDkK% z2&?ZS?N93-(=Lx5mt3!>Ua&uU>^ngCuXr08X!S*sFC{R7Vd3-y&w?AXLI^|&Bn{%8 zdyCdQV9+~_$J;#Pqv{5k;^VSYXWJ|U$JM=vLN$oRqbaj$!UfOrj9uL=5y+PNB| zqVRkWarR?Wb-gLt=n#MqrtXCy)GY^WZu6vnSt5!f6*BcIa zQV)vbNF3Ig6bx|$#2Mn~Mmb(kM94@2;{6o*xv|3vt$ZTFJz4^c;p8gNvaWMOPGJF;XSGs2}qi0QEg zMWn!3%}gb)gu-D6gFlrStM!&l;bY!Yf~H)n;C7$@guodi&jqxc%z$e!v!OQOQ(-A0 z$r-mH^Q*C>w`I*HhU_@L)xoxrHBAH>miE6`PHXXfmfO{k6Db5CM!JrUoSDw%VyNIt z?T^}aEJc#+^0a{EHMp>)afuM)kv%dumVR)YhX7)n2SmUZ8xyXl$$jdMNaQNWaINyD z@H0^khcaZ4EBebc8l6L%(jn$Ms^PtD)4hc4S~aG?b91GL&gsGJBI1L9hx;uM_s=f; z=yNVhNg0G7>Z~&csH4C%B(JL;vsG~5s9?u8#-i6xtSsW5b=?BMj_%W*3Xv>|<}4M{ z5-GFa<|}|1b~~|e5eV_dLGA_LUC%(nk|=~?X>|;Ew|Ut&ZwdCg=4nCEi!)L4 zz&L_N+~IH~#Kwt)igUC{p6e72;UVwSAm__3vjTUR-;Q;Qbnei)bOl{w#Nb6E95olM z0#R{l2iP1W-I4~Nku0h;!5ceDVQ0cb{QcAyHF4%;$bo>R=RRYMa7*L=G8AX*WsVptzoxvCg=MyHuEITg%G;ozrWk^|1~gX*AB43A+HI}-VqMW&GA-c>DTmr z7d6C4SVn*zIAWY!Lv)N)ehJ!nc0ipR$j@bjmN-(OD5IV9%4v#*<8{J?$0QJ_75CR) zV`cG2b)ceSFtAZrvMa$sI#HQqBL^la90V{=ITw151MO>6ha$!g#lq7&Wl{v?mH1{O zq>nnz*kN!ThJ@I191#MK2(l%3I;uOG$}SW_a;1_g<(JdSg*3mT zaM%>F8>S@oR3ltZVq0~3%}~i&vcspDt3g3cz-42DJ6s0rmt?4dI>DDCNHDdU+2dv# zLIF$B1C~d#Umv%LB+QL-g{SWOvwS>ZX}Q{& zzw&{`-a77`2#U2ATe_N4vBzbbU8&xdQB)O@6snWNSk0waIRv=S75$M&ijm&+GZus+ z#e9pue_g!OFM4foGLjQ6IkL7NxiM>tq$OdKL<~PviTTuFkU{l@TcV3W+HDT+L1C6q z#DfS0Q?$xT5d%5H@@c*^eBT1x+! zVTUd3drS_Qz)@gluy(B*6Q`7@o;`hT`Ns?fr3(O($>trx`ufu^I4y`P8edcCxrc3* zd&MPc>a)SKvo)I)q&i3^X_LzU*fm(QjGm_g>AYSt0RQH0vhF;?;hgtRC6<=L?_!a1 zH1wz5o3J6Y`CD)FF$~u`^gIt9Qh^sd;HPRctGhs_E`)V@e9Fn#l9A1KOY{BZ=-&)c zTAD68xcOh6ab9ef?4z1S=Bx~^xma6ji30#*mP&I(y$CA?HVR>i8m zauMcr;#s7;-C@$Z#Eu-EUw;7%RRG?JwA*KbAIkd-t=hRA1uiC`$vk3U@3PW)Npe{e ztr^5AjfwFScCJhKaR|SHJcqdQb%6OAGc)}T3R?7ppD6H(z!$UH=gGG$M4xM>$_##G zqG}Hq8f?k=Jd2muIP7q>RGk@ z;GS`x`&pVij=&cgxUD}xfC@gdUQJ@58r5h4f1%I+Q%>N^;@F4!`NR^KNNr^iCo^$kh8bzr>58Fs`TvMzHu`^cB78PCls1kgUgp9yGR z26#pK=(&(rGbNZC=1e@<8*)mPsvX4IK*5MHIsAbHyJ`UHIHinN^esif} z028Oa_$PdPCM3TDNVL)R7?fs`-_jPO4Fy5o@3V=X2Xt2r=eDG}ovgwo4+dMrKMk;l zj`4$q>L-pUfBInIO`LBHpUI-miA&VYNL{>^S0Ct=?I{MT6B@iptJaGOqvyLcwpEB9 z=)-?5vM7ljJRl12V0bEozMXK;PeWt{PaAE+G`|R{I6?K|MVK@CjFg9UIM3+~yd|-L zGkjcV7yl-h-#TIW=s#V6o4oXI|5J8mmZS{5c+^m8!aQdiPEcIU14w9iz~o5}ts~t} zXzc(>jqR@hO8?5ibx7wjGe<(!DBq5we3*>DQ$^GfXdKgzxS6udI=zqJAz~*JKgh73 z)KSi(tn_fTuj;f2J`n0;ui~^w)c#o7zc@|VfwaR7E=%4aZ3wCT5X0w-c>iXt{EjeH z458?~Eq6dXxLUX}uR?{RP_7i!W0*G;rz~7b5tOID$--U<1w@T=D>TB5ikBkXE5+C*exiDcfs!nMCq~i*qZU_2I z&Ya#bv&;ea#O3>CSrjm^xAS3%rK)iY#mdI>D#}&V%3z30*fTo-%YFgW1GtCD*is=3wF6j{+4x4;1hz7idMtaW?W0u0mh@b>I@?SrYZdOhBH*?rl`&whAncV zw!~@f6~z7GV_?=Nn4)}F)to_VO^4Q4UGvRh?WW=&(*@!nE@2!^1G5Ri#DH8nVXLLq zxV%7iun`dRdLYhj20CX$PBBmTU1%a21X#}>xIt((Pyp>sT#VV{?2PqqcS}#W zKX7(%^ezlnaUz5o-r^EN^o_4n_ya>a%Umf}yTC{;3U-vD<7HwPVcR~A#^i`a>jxkM ztbJxy_EK~YEXqsaD9p5wF?uK1E=$}3e?_-JWXWZZ1lF7~A!`i}u%`l1vp?v+0W`0T zBhyzcKK!IzA+%%*GIh}1{b|l2p=M%B`q5mYrwfeQ+K7}aH=VN54kbwnv_^iJmzJUP z&|=opqfFyIN!3Bv=Uy9M<3OU_YqTp7uGU9=X~&B-jzoh6?t@1l3+1&U3!Hb;eYbHg zo4a`T^F{yDn5SRSL$)9wYh% zDhJpv^3e-^m|+OzspaR-E%b2-)XAmtiJTzj$4$L6Vv|HQOzShq(@Rz+o-(WqBLnze zVoi|5Bm%gm6&7_`1I~A|vAb+y+7L>``)_RArMF1u1M(8jVb4SNHb?cIF^IRyImmD_ z6RvHF5qR`N>Pb_bUbdlQ`nYCuie`jyBa}`1_Dl1JM6jla;)!xgB_-FXTf@RgA=~o{ z^nVJ(42oVp74rKIHP-*W`}i%8{}~Guse!v{DkFc|Fia99q)2Iq2_VcBH%gOAW*7rV zFj(ak`OQ%!EiCwBkd9h#S}`zANkIUk^3{e@S(jE)&AM_Cp@}@ApUIpV5eCzoLyUIk46^VLo6JxV?xR~wwmggVT7zi zjUZ>{SjCsas4;$Xhol@uW=HGYW84bY-?DgVO9IZYWkWWzS_h;Tnz5W{#=?ROC%pEKWdb=S3HxZ`iH;x;5V~VQ-r!1G z7Yx_N@Edg?9+YT0-C_3rQ)hcjZNeCpHnPr^(d*<4E2mm?*VyE{So%vd{4 zvrC~vqHa?7EC%Qfcy0u)H-2%pc)GoXR>MV2xiqF|aB;dlnn&>*QNs(F)CA*r!EqDi z*vXw5g*sJ#@En_8$XzQvNCft56x-0GEM@xJ6DuH_fi0POWhv*7(lQy~>elL$A3K4C ztwiWaHXu`jJmlWl1xrR4yUw&zB`Xeld8vM^Vy?19t|AfvlJVPdg@RZ86wItG;DIyn^HK4(?m04LV#m%fB=*!^ zdG`0Ku!}9*trMdVcZ6f@>|>Xj#*2*kpi8u3eb+t6eaR z|BmbM1Uodv03^hSwh^SupP$_}YDx~>vc`xTo2*GXde6uGt|2#^k-ks|KaPM zzboCguI)<2wr$(Vif!Af*mlLXZB*=}V%xTDRg5?LIs5!@cKi0%+WHSxANQQ2&(W`e z%|Ipdp_rT3)!j3+W2KVkE}_6QhpoL?S}101?QtVhQkJFBeTz8YDCTv-$Vpl2aeQj-hIG6pe3UlBZ8PqK z)56~IH5P){C0#Il;bL0j@1=H2M^_L+sb7d7>OpRz-{#n04MjqtZNIe=lqm?Ns8@!} zIZw??O&VB;-ss~^tF4C=QWh^tisUeHpT6hLNCS+AJ{tl}ks0P8oiO#+TI5PzeorpK z@?(iQq!OTGOOputfJb!j?lJmjc0ksRkN2{>&S$|+^@Kz zOOFCzWjFQ|v{`E^d;O_S2WjnKa_KS_y?pSUmwHhHq-kwsRH@Aw(rhldX}pQX6OS4j z?kYSme$F20WCbHD_Gs(l*v-s=1O1dq9w&2p}r`(AGoxU?r=TUZcfQN$wKjdVwew>y-C%?+10)yMUz4H-O zEy}HSeg5`H5dt%6-^lcnZu){i`2-IF$9A9|WrWxaN5+}dp9`mCFGLRFT+@0_fjjE+ zjEm>853zgM=7Q$Nu?;60KmT0l$Miov7h(qtYt&Fi3OfDlI9bs#8Qun%f<}%chJ);~ z!AOwIesGt3cip@rT|u{>4E&)<-^9=zxas2`f1re^;9%~lA^LjLhRDy&dT?9V9;nrt z9o1`2S(wq`_4L>Jh7IbKa!gYYiWX# zwXDj6vBaobChL-L&G$kZv=}n`w;_=_+*TU+fw3LT;b}_ASJ}@#{_scH$!JQRg{Z5XbTkMFhc9hqJvBP zjfJfo1xC8%ax(#0HVCFtAVB$R11oXYq65r{sH=Cm0O*2*0P^=LSmKW5Y>-%yQe#w! z=8k=#W@A*oW}e;hW`%W5KfP`nXNjWpF-pM(iXr<^QL4#Al3_Az$YK4{zH>*-;6C+? z8(hxZa>+C1%!pyBqpc0* zmS&7K_|AK?a^d}2sk%6yt&rEsy0b+jON4i)vo7iLC7yH3-z&&=K#M&%s2IGqR-gw zZOMy;x};U=f}uIpv$UQmx5(|Og71_7HoNPhkhQw1#@OtGb*Ia|YTEq8x{k_*-gedB zqDGe2*sbL+vZ^0yO2FB{}N?4O+WNw~(uQ+6U0%$5t}Z z&~-HzZFGI|l9WI#h3gbIwHy;Kw5S;ss3Oupe{y>6A5s$E|B+l+GxkvY>tWdj?Atf2fAo5q z=5}@_4*zZUHk5V#Z}X&5GF^pbL_@yWdvU85s>CapxfNDMsy?e9%ghr~_cy5~=&!YA zz5%|)ouxND_bYlkTh{|_reCA)8R9HAJt1kHN0VvXN7=vIwtRe^q4cpug8!&CPw(^6 z&jSc`*WNp?6-96(cf^Xuwr>6VHe7Z@I;gRHehF>|WH}tXr8!tvH)y2?mUWUp*(2=J z^V2tvL1&THBiI~foA2yd2QPb_aIpl}Q0WaZ^_||&C-*b;(;vf|IEAS79;4 z#*$D>)}ZAQ_Klt&6h;$k^4f1|A47`ox$}~Qm^O1Wg=`*W+Z8Q6tG$0=c%Y7-_`(b6 zgE7YM*w0$zkR~1G;+Sgb7W8_B6n(pvNdD|4=>~9%t=GG6AhL2QysB8K;lRb5^gwcPq;k`|h?P5Fl!MOg(8)`icT6{ip< z0^abj41SuirN~+2*k1@Aq(Hh%vWY?#y5h1chf~<>qLIdKTHgwewh3hiSF}oF?<63< z4T5wr_H2=^CoWU(qda?A#XxLgt2_p(i{ph!KcK)nlq`?WiCeIE_ZKAmc0dL?uqbFJ zw4%f&9N<>~PLWWFH3`Q?c>agiK-H2>xcTKZ^nZB`*#DxxzeAVa{|(d8lMhbIuO=qj zQhbFewC4*7BvnL60`o#ddF;Mf$6MFM)e^4>`0J_sH%d`2n~Tskyqa}0p22SG@;EeA zz4h(9hp-|$dc#jVXf3M9tAGp{)0TIQl|QigL){C(hM+gka~^xl=kz)9vf*%`6^6u<-9I1Zv{Q&J| z;U&>t-5j$G(cH4`obRi#JsPu8Bw}=MC~;}lE5chX znRhgE{8&7Mu78UPsFD@wbFwrby)InllW{`3fZTOC`rM9$e}s9m@{6@XANeUeog@$D z0r6g0w1k>ySDP@;?;l&*)>+x~2QuJ9dVCWUx}yeyr>E*7Mj8QZm;XGc7MDAfR9kik zi0KUNWjg0)a718LgNK}9w=g@M{c(6^%3#I*sJ_c8nYK)`gvxLOHiUNxEf}7u%aAjP zW$b0Q7RDelS@1Q=VAt9y{Nc!@jnZsPfRqBu4NM-nH>$O#_MuO#O zPo>N1lllAbj9wk?Z?Bpz%8ud#FH(esn9=-AFFECNTrm*vY?4#!F6>DzanB-v6DJe( zrJq_-gbT&Kzf$E5?bUgrNzIuJVg>0?oNg_dm)=WDuf!?XmUgbO;>OGy`^G>Q30;kI zlxcb7v>QwPZ~SZV2Qe5Qe8bJdBmMmz697~o^8mm4(eDAHG1m-Vp3!{oNx3RG-CxU} zKPO*-2f(Tl9YZiQ@V)>_z0Ik+BhI}%sqFWOo88L5QzgACQUFVRx+47D1u!%$bt>P- zM_VJ;G*S4z(OZu3r9ap>kyXPnz%==RCUE>iII3>NL3vHr&4$+pM(y#tE^bzE8n545 zA4r26s~O6@quejB+xt3s&3p+B^oP5TbG!yR$Jk)8N8EYuGh`&t)8onNetTkY2_Ib6 z=Eqrt{rGlKl>VyY%eorl6+vMrkpaUfR+(HNi+2S@7R6Gt?`9NKr+r9h>M(6-8!}>_FhlrR zllL1-Tyu-_;3ZR&_j1?x_Jd7Z8#E-bmCW9y2`e3@Q_q*bx85%~*?oCl_T;{yUq^M1 zTy(4_W2XLQovLFy*POUjoMXlc?9STFcHbCRkufE2uKR7O;=e*P6Y;#;lbNKc-zIz3 zmPFj8nwqso57)}&=rmYA=?m5Hj4Y04S}A}kPwEKE^BuVI*Ac0*ehu@l;J}}XTWB0f zaO^yumv$!(+Zu--6@I)J-#3;fEW%r|$s$E-EMI?7`sHENF=aP@)iE-L)DTeq!Xhlm zjo^2vV3>O&e$Bq`I_8*|{gk*H7za)JhTn-9)7&DpWnx#;?in%H*pN9vUSd30GJky4 zGtFtjWjD4Ptf^Dvp0l4WcMX8qj>pe{Na*>@PiKNw#3z5NTO-UTVT5ZBYZB6|t+>m| z8$Hl-ogsEW7@sLMkw)-wTHx}*pCz_0v0n!I_(zf9vyX*G`0gVcCq0rqknPE|y+XH{ znnWLWrjl#Z#%c0fG024&t!Zb76xSx`0%yjhHh1zQ2Hm4TpLYGODyvsFb19_sAW5hc z<^%YhSExHKsI~##)&}`+WtxC!BaMC)%6l>GMk0onc^`vy)L~vB-`aYva?$JQjnCgB zMZ?vn=OJ>GlkIJ!)D6d~p^R2Epe#k$XZEtv2guMdbHy0+hI zL1^!JrhW^)VbTQ>lqyEdguuYjs&(e&nJswhW}k{?K0xwiR$?C#1pcM?fHymq@My2Q zO;&6+4gvV-BVoOL0d`w(>fK&V;0R6}x6&;LELEh3&zIRUL1H0J)n^Y=a>Mwml<5y) z$M4B`PQzVLmIf!96p8-i*3dEvog@gzgR z1dC~o_hM&dhg|3T%NuAn2_BM?t>_>s%E)qkgmYaL_go(L+5fH-&$?-Tc!)O^{+^WQ3+^A7wXb-Uh zUMT$YsgQ&+s=?^>zXizN@6j+4pT(1%mJMzb7E1U?q}=5_hi6Y8ZL>}vjc8nO#z4b( z$t=~#x1%}7v&+3mQrUNAQ2igx)~Q=A6}Zm60fJK7ubcWxY6S)7tdUj z+z`n^oqzBoALuR;*PyOTgH}WY7FHxW%>(4v7FGS-yB_;^D|oK3 zLvEIZkV7-eZyVeRgm-t`bJad{ZcT|-0KFARi3z|!BHlCyb;;WC3p0?}%bT?TXs-=J z62z4u6X!h;F<1?Qz#!8A9$Bf0&cnsd96e5FUEJ#ZgSSqGjg8sx=OS;59dffcksNsK z5Mm#VNWB4wBZ<9uyi05u<(?_k6NU|b?j-`1!J>eMy{-Np+ssghNUUcFqYX5))8Y3= zWH`KcfEVBPK)%L1@FUEEb93*EaS*W>K}*O7?)n;Ut~i6)&w}_v1w$)THCQ*ZDjD%K z6RsJ57Ksg~S&QUF;nv$2YWja}7S}qC;|lp|!hd{)hjIVC-2U^fmXv7yS3+Uv(-q#T z95=)9CjrIjyn?t=$hvSIMs|upXCPlDKGM9 z6P3Jz*nZ4I_KNdp>67!(rOU_n6H*`ZYZq|kyO)ydAFqj>x2PE#M{*_gNfaV|7;4mT z8+6H}`ogj^yZ2n*Eqba2w(zstF@$6l#6S$B_sI25pHk+?XI=(>2w+Rbabxf80ZvNe zU!_D~K?V+?W`tTM7@YGHk+0c=7eo0iZKh64@W0%1IQ)bqM`~bF4ioO?h#kC4E=$FC zEnt8ORU(N9+wFt(RfQE>st6(_f@2Z*t)B{*L6H!wIxLxl>+9}hG*j`20_!}lwV-xW zO(5E7845juKCQ4OY{1x2krMN+38?&EVI;@wxNO{~7&4A9wXBzFXbp`SOSXYnf@kjD zAkfULbjnR4G-=o7bPzML*_|!fyHvK0V4IH%s6*QQMW%xbPvU@4sV=vx5IUt3sMlm< zXQ^C-*j=55rq9T#!=SSo!wqiKHUhX@+mqB6mK&X#&tgA&uuV<`5PMM7SnDCr4P;>|c`(gfK9&;7gy3Ep9R zCN@Uo$cN$5bJ9ujr4^_RUxx43Lq2^t>Y`{wzn1sz76Zg_S~`inUid8zGyOV8d2Fao z<>+~1N1@wA0&bDRyEJzQu#?g)aIYictH{znAR+;Lx!1qRBA353U{2~hh`5VGee3#21l zb<{#ai|`y5Rqa5r^h)kyK&gO61S~t&QUACn1Orr+`>$guui!l6o9A#`f}AOEl$>Z*9^iEi=Nb+oZp_8FkflDZ zYMKko&00+!tiPfNi+Uok48~QTIoJ~NT79*1NuEEtUw>^h23MIs=15%`FB6+hS!r># zCEMwo7)EHx`R88Cp&zBtBlz+dPUuU}BosMnfA&kli10Q}hmz9N&p*pNCohBZx+mfs z1gzm88vR!P2p%6(e8yq#+TPTV@1Kf???|BObis$`5IT_q-Kv{ht>_g?1V)Rg=Z>V8 zHd|cYJ(5fr<;(RM99#h{JAo(*lPKe01OELJY79hK`=HZrorH2Z0g}WA_wP_vuusBi zumPJ9mHXNAsi;TwMBt6sgHJlSVx=XcC=hEhHSTki!MQb__I`6WruHpHfsDo#JSiGN zxadUcSray7cFXOl{~IDN8`y4EDM#;x%7Z5G3S1fNk1Ie~5Sg}|kmbb1sp_BR(4*`l z@f{1hhtN>(x>|Oa#G}PY($kRGtYjks0rB8?m_mpMQ~Qi`DfRRTGXfcL*QB*yk4bX| zTXfQ+WtuJDBEAkGHBha<79EZnJH!aG_+ca@0lg>)YDXuBDRn|;NE7aHw(3(0gEB)n zji_U0Elugoz%bO_!!FiLsZy+wmUv>o_GtnE##{5i^MS%{ zHTSv7GSZsy4Ktm$A$c&YZY5+WsrC=gl&wTj!!5T18zw36E`##9qR}Ga3cTPE=ld6s zsQkv1z9a@CC^h@6g3H<*Q|=Wl?v1v=1j)8;g1;`q13`m?t*!xN0N1EW;2 z7HH!UkxHI+<%TQ+dNFV_h1Q_G{?>71e~i*Wm4!(|GwMs(*31|L#OL zlw`i>ZV289U)YvudCA-$MKhsVEy#jkt^6;iku=axGR$M!|3BE412<8Z!uwh%C(*fX=wAT+?e-%x~=m)l#t2dNZe(CzlG23`|+w-OH&n;@e#PH z0)>(YQ4|OUirU*7>3;;7Si>O~{XIz@f)}YQI8AYR)*hHEZ`K7szDbG#dnZ0bOJhb+ zFv#6cE-PGmB)_q|(|Vj*W3lh1b-0g&B+V}$b)qST`^aL}GKE*=_*~sQ%oCTAT4O%$ z8pvroTN~Jh1?AR%wEPGjFH!s6X%{1W9GXqtHqyVo5>37pCFjXBPV;B1rd}UPg?n-u z`G&*z-E^~-JSpQ-4H&5*nh);vwp-%aZ&EopH+-R4^VaMgfIn=aMP?WUC)=fp+ZVg?X zSI&{V=T8_Tv}^+^!1#C&KTPfVXQRe0Gt2Bm&fEeT{8M-=mmttPJ zk^!ZZO8*b4FZxqm|GPIbn5CvFtNi;v#+z(SwcBUDlv4QB0sV_o{yxi`|HqP+%snWW z4*m;g0mUvBR~9Y_0sNOIEs$ZHVPky$7vM+jK#>GaW$OQE7G={d;8)|F-gk!=ZoZ$- z&TzTVAvG=l=OH50OM>Lv`t%9o^-C-Q|0}rx_8l;y%$A&BB~D46oDpyy-`|Do%XcrE zQQ+P&)9qD1as^AxM84L$kE+a2%aB&kjbWN0l-Z*c=|MQJjlj60+4K<2dr&Ex=g2o@ z5ktGy80LqQu}jD)3!0%mS-P%Z;UaUn^!z&dS{Pc2Ta&BfW|J2LP9_a7Ht!sIiE`Yz zL1$mCO$oW85kgzE+Y*M9Tbc7=Nd~a+VTEmre;pXWoYT-#i)wjq9G_f?_R$R=CEhtY zir66*Na0R+jvvKlWY9+<=w0mz?idq$aR7OZv*sO&P$X1Y-r;)^EaVDT1B$(Oev&Zh z>+}sZ0fr}m*kKI!IXyNln)Bb=N3hNL$>#N2>mINFdp`ME7W@_!G(X769NMw(TMnm&F%#AK7J z=CnfLsMuFDIXAA2PQjsmQ?}_l93R&K{?qlppA1KZ3_JHP5#U1ns}TM^hB{RKM*AoW zO67tgUPo&(G>9MUHbQ*&Ckj&4D&Gc$xBjUw&Sh%ZM2j`>Wfr zD&!XC4_qXSr3`iO8}I7HzHc)W%YGf{r^3mE6{(1!fE z7+{X^ZPWpbRZC+iSS7}$UWQWTWA$H_&bBZUZG=d&Oizuw?dCGp`;{qd2_l#!cB?UT znS^APROG)`6E{!2$qKM}zPl#d)KoS+_ z(_E>=0zB^8TfiwyC`3Nn!!G$IEFfz3f~ZiZV^ay07UQU7T!Iz9rS`K-jHQd-*xs$9 zCfh?Z=U=S}P9poP%Gi(Ur-G^2s94OWi3!e2%u3!_CFgm{g~UZ0n3yX*g3h$JlcQQz z@XchhIQsusA9vvCgT~TZTxf6$%jz0?X6nL)=w}tm+FDmvg{~^0G`Tsi=gf8;>P&31 zu(Pb^e^?r2zJ*WGFG~HqKr2K}XJUueaMr9xO=YTU)tHZ5;9SsTKCv5R4$ea(bJnFP z6Iq49YZb;Iaq}0jANqD*q!scZb$V<*AP~MueuhUFM2-;MK9py-<>?g2w>_+d{q%G5 z0wk9qdKW`qiwL&w`wElySgnZ1X;293Y(^8&1PW=V5}5+gjGEvvs!i}#UV!ug@ud4T z=nN8=ULG@%_PsU@VN=iz?YdV~g29?rZw2;~e-+Z9Zh;T9>!!S9wM^}pM3)ewUz_;a zJkq>gx*5$IP|~VE5k#XI^u?ebigx162Kq)LV+f1+mP|PUE>QbyoKg3`xO<9jx zNn&|ltG2-cup+r=r_cq&n-EFcSMwy+`)Og%T4Z{wmXHe)x zW>}P~!~y=wy!1;j(=rhypDO9u-;*gdNzYgbi^xq?W$k9x0X=2Um}ky~%DOZ^=jG8jG2lBX{H;)&=qA;?KRqF*UYVJH4>}ma>GRIl70pD2{85-H zQTuC_{zdwyJp5f-VGF0Zg>QaodeW6T-@u~srR~URpac0~nl%wKp%}7(SD1s!J-W;BfR1rNFH+nEsPe zDI$UdjFkFyLGUjpR@{+vMLMmM{k}!`!n@#mroM?c)@sX@GLsh_fUCRqlB26Gy)K~h zUEIE1jpp@3JbHF*Lf*8FY`Z&D$jF_JnFBqym0gz-ui5kPv3PKvuq`zC-M zF4USVHfL+Gh|ckw2Fg|_w!G&9=)!0YI^lIj#EiujLV+I_j(d2wS)ps>{_ zd@}sywz^9cdhZZ;M;{}-3IsFUL|!qdPVrFJp$>2YL6rt(@oDr7J(X>SIZy>JC)Ed8 zNd&JsbivPP+w)ijJZ$%@b2C6qj$i-rtd`yT&Dr)#aPwb=;lJEa|Ayi;DsTPe7Jgi^ zrDiOs@CS$H+ZZkZ0T<`G&;fl98+nW4ciX;T8l}M8m_+13bmmj3|i|YuOt-3ccpR2#HF4<`6cvD0Y+%~=7#~V{r zYK@w0%K~n@)ME8tqPFnXgOj#%=zFwsU-nj$PMR<&AH|<2vQs&8sGuf18~s#QBU&sY z&DEk}Py<(mGAI^_IT#y@!s4sk;CP7B-2kSGY#%(^Zt>aGFnalB#B8QcrA zXM+Wtsy`vAt=|Ce=o7Z<4|yu;hHT*8FwSEa?SZS}9m&y)~T@Nj$rV$K+ntE|0x%2MDitr3SRPb#=AT{j*V3hpoBrXgI65 z;}_aOqpv^ysxLbe>rmqs9`5C`E{cRT2dvtQ7~Xr#;DWMdOFFzLLB|Z&?dd}+@_f_b zVn${39^2;fhmWNizvo43Q}b$KR`iC!nIR6X3$DG68Z*T7Y21O+tuNW6DF(4HPf_y( zGUO^^y_>&+;|qw<;W*^rM0Lv}CqLgFmm~^fON<1;HA+f1k#|&6TXVmuyoh1BwtEub zmlIY)II$K>m;O15oY|n9L9i`z4xPBkRAF7}fHuj3ZFPS~)iGA~T+B{Tr}WKp>2RYs zzy!^gz%|@Kjc%M5Re|n7cycKrba>AVbJ?=S3|V{+jOu>2f55;0`XAJ$Kz69`*{?>* z^WPh(|6+F({{pIi**IUbJq_lXg6@1+;Tw{yiu6f1B4t!WgyTXir+#Z>D}~Y8+;zAl zA1c4U{Rfj;A6-rs1>eTZ{W`(EHGa9}+vNky5h${ZC3Bu99=H)v_;xN+!eoJKP9V6H z6-wXxAi~hZcTesR%Rv{=X6?#Yw88Dd72P(<)OE}e_#%S`ODZT~Z(fS9Se*cek~pni zG$&82ET-U=XLz2jS~kdN2Mv}t`|zFq1R=cG`G-F28P{m3W7ht91mpxxSS7Kl7_s1( zcB^v*j-BRlG#svstRzGRrz1fHEisdjEArL{C(s@HfmWSBL+iY=`|{6t54zv;w~BoASx~xi5;B@sd|mjfVSL%hYrh=&tp;(N+4> z*Xo`d^gl*)*91zg!-xYaYipkYVp})+x52%d(Y=bID*ZMVN3~UjIW+znOq77-}ywX9bq6({G&|h<21W6VN zROW>hS~CBS$Vq=1`eb0_2Gtg zXcSh~2`p^J>h(~vI-ZyDAdAY*NTvsq(k%8x?0 zj7q$|C!aD|JseOa3VKe)R6qERUkzviX0YQjgMh*A2q1(Tj_x?n7na&$+iGf=3;$g;@B z`NbMp)q){MWHfitVyx2l@BiCjyCp;Hvm-4~_-;qh0 zv70fmK2mQdDKlg&h;T0+2?it(VZK?CY&;|;4wL;4|LxlaO?%sNXMxgHnSruKI)+I3 zB-Ki(g|fSE@ytm+t> zpF{c+;*Bftd9KPmvvL9bW1`3xF}#9AJ649{+*ms{LOzv*F|C9ErV$xmk9`94**q9a zn}K3Nz5^Pp1hdRhz#u*E7afHtg)>I_i9g{a@z8Jm(z{_8beuhaLk09`mRNC{e#;%i zi9z5U#4m%yI|y@w$U6v10~|q@L=OZg#R|z_ocLKn*vMGqdsi`H;~z#&q?M1AC3%p^ zRV#2HslqlKHBBMrEiYaMzVyu=naOmvsowG^C-(ApB#-UiUiDZ|L@`uVXgAvUuplJ6 z3VqTOw$Q^t^8Ms(g=Oo`Y?9%W@+M)i<`ej5R>&-_m=~#jpKl_0MawS3_ z85ncA@vDVUl^x49z^kpYa)qL-vO>3`v0@QcV^BitTaS9YRE@DUsa zoLpTP2mICteKs@oDl$2*n#;kHV?uYVKyD|WpqRw(QvhGp(oCnBm_z~=uAt9YxL}@fb%)E zk-xNuMbaCV8gwU2qW3{GJ_#k_Q-9#nud4!`uG~GyB zRf{`R$07#WhTy@(0nh9a2 z5adiwy~aJ+)COS0T?7A!>|ui6c;=<^m@LjhZq9(b zKXsHYq3l2#9Uz{50sH60c^Q~{y$0Jy7?;OK550T>aklRo0(ng;kIY35{VkBc^4HQ1 zBc3}7-qH?NOLpH4q(FOww0w@J;|Vj9y$4kGVbqzabhh+s1<5B)7u)f5yO+0LGpim! z>Uh0Xy2A*=(JDY4X&o#^k4~dr$!0a8=9`spZ4gQ(_>{= zV>XFYB{>e1@YCyW8Nvb0;!v0B1%2F1Q}2EN0+LwB7zbtR`g*Z<$d7S{w*lA!ch6xs zsOIqskOZl&DU0`?K=#fe5>?y?(ZV8J1kl1S-04CFzJWEL3A7BzA0oWVV~dR3Wdc`1 zPoXE?(izff$C;Z4b@Fote2O>enV+dELN>`#4&4$D*hhEPz$T4!=F@V_kV zAwqlNI`s+Cr8Qws5|TZdm1Y>R9U)K;>prlML!uJ%Cxe`P?EX9vS${++;DQTSA+#zS zF(BZAUPHK|P&I$queI1x+ngVNIEFY-*{Fli=kYT=ar12#l%OSitn%=84;mpjyVJ(1 zmftSPg6WU&nqN zN_oVm;%0iOvE4}x#%`Sz!vOPCX=SkuQSH>=uUNu$V`p$7i(`GOZeyGS&+C47B7=QF znF{8W4+7xK#&Qp}#bHw>jg zYNMq%_F1%LG1F$%iHuH{$zIiF*F*WJL>#N(ok3gKM^YrU?n`h8$hmA7qHo+!2`YLP zxUwT<#&7Q>8U<-D=ri?#fveJ zYJ7AUqeP7;|7DXe9N5|8KhbY#P8^536j2X#yHlivBM`zF3^L+KY;#bwmiaI)Y%MIf z*}s3R&rH{MX}&4cbr8(-<`Qnn207gFfM8fEF%rfDK}{18t)F48c(MuG*K&!1?q#_y zKT3aF7QHF?L|cA_Bgx?!A&KtGpS`s;c0^8Aum}I#!j62ka0AD=<8xd8VT0>h)5@~` zYcI<|KLtB{nbq;&wx2fWZf30beWN9$QLZFU`OKs$Hnwi$TljEIHs$(BgGYB12k}fp zfEd4$X5CNfCROkuhkYiyun`vL9fYhrI4T1z5eXHe}n1X%KmI-q-Ub zYZXxaX1jGUw~)93(4mG7vx=QTtjKvK(Njb!w^Q3wBc$B3oAr|Yq-FjI0;`?!PO=lq zWZXg%>~=SGe^5lHS9SFo_@ucd9t7<%Tnp zPNiT_n!r5Xt>4eM-wYO%j@PiY7v2Z9*>`;pm+$=yFcoUKJAiYUOl zetYYF%pFl&^!nY^YH*PHop@bUK)B!X9XT4EIMqz`uXHfY<6qvhgSQCJYI%6R?*s|x zk?h9sH&C;O<&^IOarvZ{dfkdfEQ()I-yHw1=Nf zt9zBdHbBSXS8sLCBNT@Ik9|RBt9oZKI0j64yEcz-6f>!R>Zzb5NK7! zMj_XaFnQ{~o$A;r;>a54n`yQv(!McwWI>fL^d5lti%Ict4|8!3b8ru{aqCOqOg3-{ z{+d9Pw2q!GYIzIVu4LSfQoX)?=vMcC6rpa?Z9J`R7zM|*Sop!Jc@);C`4QH~H73Nf zH%;vmvWF1WBT9YEwG47p^zA6y5%oF_MRgoz2kEv@%6!mdA2cOFg)ISqnQlOwST7y6Ov&HiYr! zOmC(-hql^VG>t~bjT+FE;M%c=LRUbkz1bK%)@QN6cYMJkG@w>0pWUa4LN!T_Y8yg% zo0SMqYD)Z2E~F3d*)r}fDN_>6HbORxmVjvf_5yR@XBsrBor{`v;J-oE_`ZRt7Xhj# zg`|h17=d|;xb2yC-4PGB#RucnOoeWp2=PIyQLl$3KjcO_$#H&1)^p(xEZZrbIB%oM zukof-3)z*$#5RO+NAZdl%I_JFZL4V3LCSxYi~DnjZrMSU>h=?3xS4w2Q89K10k_9f z2C;#j$E`?%!sT1FQ^lZSPnT53RS$(5AEZwqEWjeQsQZfey4_C+&)1BSm}!gw5M70F z*tozO8i3f;D`w6cz%110cXG&^aXGv#c@q6PBlGwm~vSn8Q6b!rJCQ92Z$Q~ zY!tnyMH{QhWXXihQuj+LCZRZjXFjY)+OOE5f`miv^^)#*HGd#)NW=Zc=k>MD?b~XJ zyhONNo+n>s?);6M;t&K5<$_;s`RqRznIUE<*((B(qa%u*CB{**DtX2S zs1g(7>(q*vsfjH3w{}2T*6BzYLnH~&4>S4ZPWrK-(P~Gf070kk?r|!MfQ-HU6&%yB z{PrA{bwe{ge);8ep)`l`l=eg)G?w4Ab%A)LAB&jX1~}Yr%zB{J##xOC?7R4dBg+<_ zDW{?{usQE|SH74_!`3uu=-$c%YEoNEiZ;fa6n57!iViP-MFkM4y^w#-?eFtK=Lsz_ zZy1=0+(EDKl7i_Zt&DI;Vs%^hV8UO*%E{|)ydh$7V1he&+BQJ z9&DJ_A2r&bl*U(*{uKIujJ<<<=X;j5T@~B5Z6_7mwo|c_ie0g7n-$v?+qP{R@9%V< ze&@`2rhDePlKcVtduM&L_Szk|OQk7U!(reX;t%|V6oEjD+@m9PU0WJI1W6^ofrAFx|_FT zNzJ{&v|x{LC@g1&U*23u-khMMo@lC88iEKz1>yl2h|X`d6vJG~VPd;SSLHIkr7((% zlXai-#<6#DPDp0;VKVEN3>v#9=E|4E9r<4HSZARiXQz^F-Xu|C?opAi+3isY)WMk4 zg#qV+J$-N6CtQkSGhqZ;)V^TW5Hz!r27zoWZc~`C97obh-Cf6ZeD-D{ozDA)b8ItW zzn1En6Mog`fN`t?)!PKq)!36I88vT;)0OVNV$Iyl00!Gnv!47cXqdn74V%x>X^x!< zPkCRq+?Xr#U`~`t7f;squ~pI>55f)ro8}-G(Qt_Pp>%0bG+&|26dw zxvn%-E|o3P$K6ExH%*wT#+OHR(tdp-l+*r%s*r$8?hnQ))@m{M z<+ANOsvxdascJl1_ANs-ai;82OD!{3b$_btTc&E_R9Rd3uLaCsF`vIdEap#7s0taQ z9{a9&P0JC8GBl zGoaxk>M@H7fo2jvc}U}?zAKMDru4lhsfWUotL|*{Nm`U9J{dYKpf9ekh*He338XsH zMJ!DCTbxUl+Qf~XTond=CamCe&RwD6dCYmL zFH{Bztf&e)H3)9y?Ow0THqH#IMr&9X()BfPpLnf7B+gS=-9GnX2bZE+b$(^r1Y_I$ zGHS>tWAI4aPnSI)x`pfSB!#jXlt1MI)r zwS^YOY-op#u?7Z~b#DO`#Et^iK>_a*UR8=Ll3%M%=b;htY)R&@zjGSqL-!$@LhYu< z7k2cP^#)42Na-4VZ{mYevIB@&@7_r*MxA1o&N@m0D@aq~h35ZID##)&`>o6qTxyz% zRV6k=RkiPR-6Oy(kU|nL*&H z%9Dl)YG)-HsW_A(1rCP!am~TxVB-DF-3#0<2ridd&y_hA(v~PMCY~YG+JnZXh}j^U ziomY{!KEi$*z;UR7Tq_SG6q6rA<`YHEFe+kW>sLQd zx}tB2PfPfjs{^eg>(IJsOt2yOq--v&{SNjr#dwm2(T8cOTgMW*R}DAyB%Z~L23)-& zaIQ+bV>wi#lcw-%m;_-5CY{oUk;HbNfDa&Fq0`_wrTQmqW6|nVRXv3EEkf5#>%$eX z9-3_hYHxVgJ|WNZKsx_o=lP@f76z7a9Dw3K0)GFO;{R*Nru;vPA7AuG@f%hE#g7q2 zzpk;e&y7*((&^r}PLq_B?gHuleN$6H5n{w@fHVG>6h)5%OaO{NhLGljX#HN|iju|ra{m+Y~eUv=5vgD*!-Vhj1dUS={~v20KYFD59(BYH5qCfZZJQoU+kFNi}>susA?d$tGE9bjnTkP+^7 zG)P@ue%xqJovd1P`66@&`L3}OOoaX+T);?v&F!#W)0`UAgKI(H7a#I%yO^l$fxcLR z!~vGoZ={`HD6^U|td4_^e6GE_&kt-p0Lq`AxhI$XLyiwySt`JJJ~&85P)Qt<7R^Re zlZ(`Kqd826+#(lpFd!05Ai7S3jtLVHZei+E)P(v)yr^HC3vQcz>UKHx&EP^h;Dni2rUD9kg~M}#hMAB<@ev;W1U(iX{U4w&@&5yKDQv@S)8qS!arwdV`GK*x z$r(3&Tt;~?+rUfNiGP8f{14E*tNpyA-w?1EIstVjjXK%J9wV>6gbD~7xBW-mbqRFJ ztM5Z}Df11|0d@Zcs5|~2b%*@#pf}wiyDwF-@P7auQ1=q@aCE}rpG|&)=VXZq zDSohipCi#iA_Cm6`F-X@P>uhpyQgFS~l+v5!V>}J4fapd(npZU>YqJ{aE{;%|ncl|)_D z>RT_%f{@D*vp9n}(tN4y2%ej5CJ#aN=)|%I>Bf1X1s=fal2j(6&Jnpo!AY!q=Jqp4 zO#Up$lz%YpCsgmxAQ40G9^|TCtd-~v-(+Y!scJXId}$>&YveHN7AACo(`?k z0bl)-m>ie(=FH$5~YTzzg%b=VoEQj&1mi9 z96jJOw2DdGNz#zz(x7oe-mS+uXr|^CACdQO3u`r*tJwW0qDgG%q$Vv)U-GRoYCV+2n`|!T|jM|0h4B4>J1!+WtD+fmmm#1K* zQQ3Y1Hp4Z|AF#u(D`iTt+vzZk8{nNMKv=T2I2}X2x8H1wi?Fb!QO0Jl%@UTs7a?DB z_!SFJpawhZE3Om}6$~&In$w;*s|W>UZF z@ncbByb5WG@y@(lNruA9;P_D1u{BOIoC%e^50kOG_X04s8q#WCtlStzr-JN=0d}-> zcV4KT9bUOyzsy}7$?>hXG|CVha&jhbw(LFMKfc0$sj?Ga;=*4HCPKK+s-{(?SuDjt6$n_& zta+k1X|xl)e|)dugouCxZ^EuQbB?^InU1iM`YGCk`kDqNpi_hrOS~s2iU=xbp7lrs zLuyry6dnWN#+bUK2BITXYOXvR0c9gZh~fu5TGhU1KI^x%S&c(&hSWGa%>!ylAu*)y zA2Z)$i>k$R(>$6VU?<2)TKA*Rj*I+Ec+J^8KB65385I7SKPUpXXJsd>rJEzuE~nxn z@uWzrSKI$8Svz%+v(W(hMioq=Oz^~!R{J9#O+x5FzTlY{At~${u zh$6hx;Fa(G=7!^RUj=v_iZ{Bfv=CeNVXPLLGN!edo9%)@_9J&wHbQAVH}7gjbq)o# zzLk`1uRIr-Jgd}9EbuJIauW3xmoIjHSNaUPIix7+sm1ZAj`ZL!2n6ZjJ%!44*!=oC zaOftTv?`og=p{IS?pUu{qirBxPRh<4ESck60!Z$b2SEQAKDTMAZxLRdEjwlrE4A8d z=niYv_-Oz82En5m&8J+9)Ru;F{w^oH)hP42xFkEW;sv z{$tks&RBFAm*pD2;V(c}(nP!4tx5ah?>`2C!KWMC8=XYB_GI!tl5#S^ux*|n>%)j` zBxL^>JnUY2W=aV&*l`cwweomQbFMmlaJL!EJZxo34Kc&JY=(&x;l;)BFXC`g#!Rw# z-k_ooV&0ShIH;%;orBVhi_^4`AN3XUXn|85F-DTs*{{Dyfl-;sCmRtN1~mYkv(Sec zlEbX33am7sn-JO2Lbj#)6(x?Qn;DG%^c55K7^%Mg9mrUY!Yb3h_h5S{M5bD8krQCb zvM0cSL^2E=ymkpNQ2ElkpXBTV3{+xuw!i=b)xz)Ux=WwjdqYk*SEleGAHDjxmZ$#d zOKD@A;5su3IOCOeY?yb3r0%CWakA}@?l%k&BCabpoNx?}g5N-3nOVlEyf3zn9s*fH zNwF1YZ(TQ_oTTjqv+a<1p}|Ia(`qV+6>{X~{_aPK{J4Pp945l{B2g{H1N zc{jdOdR4-mXl%PpJWEs1GU)28BeudzH(;f`1Uh(2-2h#s=cc8PSc-Hme20!(&`HkR z_yKZXv<`g>2nZild(Y@V-8kSLRK?*^8yFW6EAo5#I4Gj@qpV1p#H!HG(UR9I`@r~W z;f@slt7-)tt2&5x)-dzxaLcc%<${px&C&iw1Qp^)q0Quqq%Sp|*6?^rV&ykJ?8c@R-IRkzE~hf^9;(v= zrWO1fyn1+b(h?-;@6FP7`8XUy00vV30|S4aj{nI3N`_2Evpp=VZ=74b`#Kj zklXcgy@WlY?CynWM#crzl*L;|2vU2Y-kGdR&>Q>c%%JMMauK(_oZjtWUfw&6l6}fF z>K>m7r@R)rqm+v0eKns-SZ@kJpKpaw^1zGl`_f(xE7ip`!&U}lc}NrutCCYgVVh4T zzdoBQ`v^KRuDaPzr45d%tzUO1Ob(PT<>}z=jxY!`ez22ML5f;Xl{L(v@Qgx7m%rWB zjoO$iZdeoEm4C4FLo0krZ*(Z!THC>Xm?FW>KORR|$XJ+256lbGQUY4+U?;DYOZoW% zx?o{8U%p6V>}{Pd>d^;ZvFlhwZT3jEe5Ee@hdc)w~=Cx^@a`HcUY%^Z`>;W>c|^wa2iEVoSTwCO}D70 z@qGQ*oa!j)R>0{6SxwIYZAuRvFVKx`ny$;F->EEDzwz0yv-#&)r6@UuVoLQ za_iSW`G3*kmfc2F|9cw0B*j|tFaUt0{{X{EG<4%TlP2D*saiWIL=f ztXpF()f)YuH1>f)fOmqn#0WRI&|vN1AS#?qACAY{Q81Q#2gNiFx4EuuLX-KZ^@*8(L~hDL zy12D6Bm63v-PoU)`p2l%r=_zo+KT-&m1yg$fY4Z#k&2UfetO3}@|x4Z|u<$byx z7jHFJgr$wFO&_ssy!*~?1Klm8PHJwHjk(9ppc>6+Kj8J#SBEV@RR1mk)cDI_V*mnD z{sRGj-Wy~p>R2KRpzy?7)eg6=U|63%nkE*2s+H2mDaOq36P^f>ME4jm%aB`{j$RZ! z)t!Wg!SB@Z3BHl;q#D&Q4*L%=I9%dPbR2GcyM13wuKLn=Ev|oWhXP|n=ARY8f{U9T zX*#I`;X6Aao;5-cXM)jw9Ic$vz-83rVsd4ZWX7Da<)C4rQ8hT|l~}*Kr*86kDdhAW zvI4WmcX&>ov_gl@ccr}&TCA&Pg>D3h`jASFMkS}Sh$P=Da z+o<+Tycp0werm`nW6I|-y5s$j z8On9tH6x&`g1q4@ogqvqtX*+gfg9lpK0;wwmtg% z{1%zcD@UmdH|qnX56Yt6rDYV&-ur6Lb*Q$R-j!N?W5KjM>m5=z-OsK19$=6@aZAf? zW;fWZ>M&}xbfaArwlZ>ZIK{T{?5n72aW9iPvATiObW${F#_N8z{NBN_#(}`K=e` zf=Kl-4Y5^x`=!FIg-}b-$E`Vl=Dl!_&-ioJ%$#|&eGl{eexZ!YI#N}06Hb&(?kgH|V-13qGi$!_{Dj40zeH4j`pYfiALmJqIX{*^b^`xtA^7uB z1t0@b*nh}C73-<$>|B-iN*QgaV3P;}2@wjTy!dLcc$R0X)qqhC7j<3yOm-o?^|Alc z8or9VX`R{FL2iQa!v;@=$3%zA;bXSm;uq)ZG=pcWqsQiL@8IA#B+Sx^M-Mm*e{ zT&DAroG$t52{=iZa2GsV+Qi&6Cl`4W3S4p0aYCU^)l1iu;KRHSa1s+RCKL6{X*O6n zo@(YGP1(G^ku`t`)h-GxzG_g=_%5_9Awie6t3w2W+IuYTx+z(e%Tzgju zV4Pv@J!!&U;lF81ZNT=2dQE3(Du!MkC>5+FdG(KrD|x>}&%Q6(7DU$P$HECKk!9li zB8d22`$RHEaKh+{pNRBAv1d>7y*=L`OyBNE@!)tWI9)W4h&|J(^iD(Xbf@T2gcLnYcIUbNPFhlgqEuU8)VL#E3>*dxc!vwoF`kOOwi zEvPT3C;OoGx+3O%0)A5%_y*k5dZiyfvBn8_t-4rySiHpN`^qg6S?$I3PKS<~rx5LY{Fd$YGXIUxO%i$zfM+Q|s= zx$q-v52;Z#(`gSP=;%JqekXj1l)J7_W)#=MpF)~!`fjJWPf(A0ir2{*Tl6GgT#M%NQ z;Q~l^ZY8^5S|m7#Y-gmseY9(AcE~PEz7FOeDBQy;3bN3tHgld(%QXF{+FL2 z>E=ORYMOyO+b+U;p_P}7`$WN4@K5$P?6G_gK#xvL7p>Dc!wu~M1Un=YJ_`nKtEouxFh2 ziVF%Ktz$T|Rqn5Cx(jL^SkfrP5d+-Q4Zr)J8d8gou4DF49;*K9_!E~4Mxdp+YGrMD z2K3k|tW}eRzo>_nPt6>TS#TQkx;iboyF|DZhzVH9yg&Xn0lHI_2bK*$Rs6rF>R;PS z<$qEIYz)0lrOGx+9evxqNNw+lDk8y5=_#^;^x zaGWg^9_!n>P2H-6^W)x2!(qVqI+?${YNepT&=4(^D#lGXik9GLbz_FMNR+^C5^5xI zaxhyjvr@tFlA$Ta)3h)dPaS0}_2AyLFcOz)E^~PR`9yuajF{Yt$TeyUq>_WE+Vdq^##D}*_)B>(n>#kq z;Bc31&T24u#y+vOi+)Kb% zVg5{b*)3@W$>AkpWbIpI@Q;a|*m8kh&X?zqP6iEBo!@`cFnF}qbwL1a*Z-cje?@;O zYXLM2g9q3+)+VvFvi#IZWv)k;Uqr8UIe;`SCC5^5e>geUaXD@#ZYJ%C{*Cfwip`wWBTdj$#yiFJ?dCP}xzylav`4NDrq z-dL+#*DlrIT7d_n#?WYO7My8E>raXrY&BseI_}no)|fHxX2WPuf+CcSYx-ic&ufVn zn99d4vjN2IH{!w@MvKAqQVUtlItSVC!R*AY-6b4VwI~YWMUdngU}E(uI_^bGiME7 z%fiDof!ro@1X>|JU7p0ww_2l8N!lf(n7^O38c4kT0E1m z(9TC{lZk~oyXKnix6n9 z?jvOJ6@Qh|$W1-C%#U$t_v(g@O3%Ipn$uy2kLSISIUX4wqwl;zZX!^qrq|*YDbnKE zdjI`~b?MUsOdUX;+~1n&|FU}ixqk-a-P!=Um3dY68qF!clBmV5 z)T4^~63yRv?KYPZ4@Ti&hkkAYZ~2D1ZUH}$DKoag%9`JRsoz{&r=~L3F*o{Tc)r5v z15aWcM7uY@MZ2EIc+h=izmmGA!RQtJHMn_`Hu!F1{d?WvY{)A1R@ZB`R@!p3zES3v zx#qVxuiduTWd_W*YAFENXHG{;#2Kpf^;IfI_9Acoxk8o^Z@5hT@nz z8iVa&ER{?Yf*H9Cq_xWA_Rf7<7)l3M(J4QexPs0gOzbnE!XX*Qungg0+3ltJVb_XU zhm!J|ewwgTN}AA(X#q8WuAmK}l~Bry<=_f_`0%WN6y}DAB$Cw#f?I*%iY4GLt$o|e z9bL^b3>Askg}+(fY9ieH;dQ98X8UbBWEi(u2f&}W>omusPR4nqJ3?RX`B{si>0(od z{?W^NvaBPHJYPL}=VgFQG<@&`f|Rl$77G%C4tf>5S2<;?lQvnS=n<>i*i@xzS+-r_ z4QOEWjA`UTPbnyLGnhCD3dRDYWqq`D^w9)F(0f5*noz9ZI}eYsnOf)fW1+lNj(sE@ zP!~J76G{riwAiDsxe3Bxgd%xR00HEa*|o{D=bLs-F-t&>q%x17=FQziANs866iK9y ze!0mgC-PS_o{QknMI!MmJ)4?{<$=jWA9?Uc&mrM5u?MieiZ;vQv44+z_RQWd^ z^dhL2!UAO3(;h?_RTX>m_3F;!Eh?$m>qg& zgC)(3VX)5I%|z#o(JhHU=lu!4@9M0jRL~*(Eq+~Za`}B?*@5l(`4sR*Wa&D>(6&k0 z;5sDbvE~4|)-{>PtPgW~59c!~h4;8xxNb67BUC3F?lOWp%2WY6yTVeDW^SFj;4JID z@nmDBDg*X@HvoeYl(3c|kY?bHVI@*-oPsGs><6zn=?H8T6g$*T#B*=!$^;XzoFAs7b#UkMh%eGKL>YG%z*ps0&>{!FBL zaTm}|uF45(+Rf}Btf1CVwV?AL06RWp-Uane7Nh6WP{-ERfeWu#9!R#>{4Ki%g`7Of<5l9;JN z_tQ74KLxc+{ul?wI()$!+hrn!}sz=X^UPZ z6HpPPnj)zF!u|3%8RJ>K8ZK_N-(*Vu(hH(A_r)!7U#qzacxBO%8VyWyDcG{r>`NK0V(TX#Vp&JL~f7Q zV;A4*>UQ6r{&bm5!6_8+f@6qoh(jh_ubVLEiHCckQUII=z9=lkSl2Qgdy(2h`5wR zttlJtkA5(zj)2go{BK3P3KrXq)Nr_5Yqr;wgR7mE;R4!sdIM_UJ~%Bw=*@Dw?~SYH zS_A32=uqvf^Ktb~o3ettmCCFllN`(_* zS>zHD6UY^fW%C^mk=C(8FDitaQjU_4*8_!JVjaQBJ^Ww$D3v2wZ&Y6HE#;*V=P42o z(QhPq<0lX1C!3i$&J7A9yj4eqlJXmrUKnRw2@V;#6I#qT0>x@Kq_h0~A8z%ob7tZJ z+*R@vNAR%YT zCZ1?E+>ZlwpBQzYzHoK=%k8*2mr_o-M`kXc(H~~;e6H7#zkj@*vVU=K?O|xIQ`s{RK+&wLQEfe1sre9Ryh!7*T&otAq*Gi& z1*{9AYecIF^BjMwLIH!BDkPF*Q1XkD_&X+4N1H;d;Z*Z(-)qezaq@O1r4ie@SJg7q zE-Vs#T)il4bVuq^LTK5yC(qp%s)% zHyF#IEg*lQIOd2u4UKa7AC0|N17{AI#TKdJX@LUgIboxY87UzKQ)i+_&0&^Um0Mhy zL7wIo0w+lu1$zfQI;e99^&~@x%EFeX7Q3Ya^SV_wrn0lEZ_nIYBsb-I*&mLYJK)YI1oVPGnL5t`J7|sT%dO@3n!~Jh3LQ# zRvF_Jq{BKE&O+7H^0sI6zL`j3lq6R4KKB=?3(;OonjtOy8{xaxzr~N;Ku54F{(1fX z*EFcVNbsNGD*wtW{UcmO8e0%@yUsHJsR>E6EI^qvETBxe%wi;0;R29XYGt%AVV0ds7qCPqjEdzXh#|*= z>03nGS2g6vT{|+-&_m>>oL#-fJ0m7kh2qWI7q6<*=TYTw7oF(bq(f!{28^^>;Z%e7 zvdhRIrnzKH8D-4!y8cW+bgiOZR-{%n<_K=7(Tp)gW2SE;Yq=IhuBha&@&HUEDzBEu zUUVjtRfEH<+XR#=mD8kOAXo!r(7~s(pwc0vQf%Qo*C=4VO?m|x-s;j+4=8t`+e0s- z`Qp1>pa0ir5`3_{D)^N``b3T_AFMEnUo18~ot~Wms&ae6*G6~>A9Q>l-(yrPo7oDT zkO0HFIbup(!JqcR`Ne=EsPES;b8yMA`qzb&k29Ypp9Fa z2wHP5p@mLub41q?gfbRNNXTVmA{*c-GlZv=7eJw(lE}Y8;mHQr0nv05XyJh}ShdW_ z9v3{1W2x(#KAoRmu25KeUaqTG(1{h!A=K=1Jge4FM!b-pgqFiU7NiljH7qH}@=l;^ z<-OMD)jRq-#zkKVpbo^b&O6-ObT;?#3HdfHh$O^MoRQ51Z5EpHr63EXj8lgYk_4X( zXgab;IGiKGUR1SbdQjB^dWej^r&fE~BM`D8+PDd-hY}MPh>=s#SkfC_an854=W?^I zka9;WaqHoGltcxdst*(RVxv{&=HzRE^ezlh*_g)m;IgSh6I*HX7HJzAI7(Na_ghBY z$BZsJ`qO7eYM&8W4KUFb&~Wopj=r#KTy}0Ir%hZ!HW34nwpLo52Fkamyghy3$5>Lz z0iv`|NA%N%aY}v3MR$Mh#*IVm!r6UzBh`w1a2yR6g&o1QXPpL*VB?HThi;Oq)APuI zy;ed9Vb-pIq)Rcx4jnc;e!29-eiOiaE6*D$8K2gwoJ-A6;_R0(eMPaHU|)O3LAANo z+ye@-hkx{?m|=M%bFjMR?WKAva2j|Trp6w~G$p3k5$DvaelU;XaFWgF5lSfTvtQye ztV8@=J#G&aee1z(Vd$QQo-v(iUVMWZZBjQ8P*m(WA$Z_(JWN^Vn`x#enD|Ii+Z;xY zgUXS-FJ!5Q9Lx3gx6ystA`?bO!I@z?CK1KZYZ@i|5W2qXii<_5x zU)lu&0oTvvcOnE-xCT;i1*d!If-8er-|F4BPkq6N+HGL_#T04+nM>L)sVY}?)=aEx z8?omf3>xshOS!X>tymwNs0f;9hzR+`$YWtC2HbJm>fw@F>72^@%Zi6h4|NhGKp~=) z^;1ms$)p6FQ570QRyV2##z0LJWcv&Yh0AN*^i>pD%)|m(0kDnDpFL{~ci3vVzBfyS zpwGQ>U0{mu@WP8HlAUSOC-7~}I7%)-{9?=3I)pm>ZnOi5hs|)6(5BNa*2pITe_o~S zWsk~KliT-wsBWFO$@bbv%SLVqcUxk*p`uX}qDAEDyQ4^f)=bSt~@<*8VF*cqj+$8rQE4#rJLiA4tMW_HEz~!?;<&c~t z;q_+wO*4m5sZUDTX(9bkB7M!XxrBCh=xN4-6%}5;H6uIyEjVeEA%!Vi>S`9-Y)T^a zXMl5PrdvzsCiQR+)Qt~#XsIiX$F?^xy{nvEQ&aV%LJ}jec}eJip#Rg~;^U5Qy@9s@ z6NsPy$x!6~7a{v+R#b)Bs}G7A>ZdH5!P0s4GDw^}5GYBPiGP56g(Oky6v%85zeI>& zBGd9%T<)lB%6eqZ{L4{q)BJ+i=Dg}@{(K&(M0no4wb$>Hst?GIuikI26KidC-=Z`3 zcI-f7n#31Zqb7RIXDH(G#0Y(_E@C#J~YV z#oxuh);&O>gD`Zwef{DNHR#g055) zaB1b^Mz?lLV=v2DguFhQvh%41K`p1#B3e{taS|6~D>wMkh9tffiK6bi;Oi{fHPRHr zVvgW}bku`Dnxsx;V zgZf&+dyts}%=V6?+zYXh)y42&d1Tt8c_kahJ0x_W#b%m~Q+s9}CEPnsgdkb|onp!e z;Rxe|OUN90xutjOy zC}v??5U9bUH|Vjb6ZW0^S9G@-aFnyVa?PA$BN3NvJJsZfTh#oN{E_hh=T5Na4Rn(; zTj|JF6Z#8@99;ZqeZj-BPxXA!Oebuo4Vr`&a!~Y_q4tPR-nB{S_b(!(nlmYN3E6uE zo@Eylh{(#GL6-hSo@m)hc0#nq4W9Vb<$k&7xbQ1?&(A#~psPZwzxW(=7wDO;g|ccb zxdrz2r69|@llOsM>b7Y`*cp>5&`Wl9=KJJ1dHuvVk)vZ*n)Q=LlojM4rsF*iG>}R1 zzlrFO&=3}94=gU7oK7$j-0bmytr_u!+g9(hms*UfE;QbUuSr@2MTMMo2HMuGyWj|0 zg&SN%?wN_h^p_{T`9Z&yV>22@}-gf_U zS)Nx94OTGqp$UwZBwH}(&Iag9ajCeFWz|J%H7Txr*uyt)DY>QRV>7O@_{<<0qhGUk z2vwG|8?(aV?8Q7CqFD$Hy#Y3Ex#a#NxjAqVM!l7T=C&=t;1>_UtntP?E{oPL?PL46 zi&M5~ATzbgV{5UmT!j$y5_Rjj|ylr3=@j_pZK4}^>K@~Tfsk>&>wo&Z_r6p2pCcJ|G_ zqP~Dxb>Ekw=sg~Hq>Iim9DRzhaLG+#@T@WeC)*2|Kx#3!(HX9iBK2PxzHAcB9reO> zxvg$>-C!fvZHXsVp>yKNagnXrnJVK%|xGX5%#ptE`{z&uyN3%1|>X)`Te`M361xdtR zlzxRaGcSHfay*GM(Pn5?)0Pg?#n z%oUeO$(_xZ0~SXKZsO2yJbiVe9J}z%v-=k?AZy1z>TD*Eub|@|n^>51HeN|=HLtQf zK-PhG?%zgB>|DgTgINWC7+~TX*=nA)OSEki0B;oN*s8lU4W^T}i(by*u}A(&z}a58 z6u&3*RBv?-YlZG5VOE>+CStA*h^E`w{ct4F0gtPl)V)7yY-{!4forGY^pPWOy_S*{ zD#{nhQP&%&g;|e2NggehY8&H6gtf^nF4{9<`A}eYR0|U&cQ9 z6e4=jifsG%kUnXVd~BKtZ>lLg@o7VP|L*(rfO7P84>gLYLYAfGnIG$_B&Au|m1bv9%Q}C^J5LM-djH2nvE|te^nhPl&L*Ca+-Eiz=^R zZ>WIssQ2k`(Q!HE!F)L`9dAST@!>Yw@tASzb-4CA(RS(hcEX1b*cqc?{YH^*-uH7$ zCnk<{GZh7Uxh~-2GV?>Q6O}r(KSvjK7CnF!25Xu)U2+uAyA*dw9_`p4ZnAP&h`NIn zVS~MEaCBm&w?&J|URnm>(~RZr3ZVJwX8-Fz5&bK3+|{1?74IK$Lh!>OZ|B?+3ihx=LYzgwL_7rQ zGiP82h;~u365D*(vlg_Haj7$x!<5H%)O|H10e32oh>8$yN}r1U@e&l2C)6xBPx@`z z#)aBR2%0Xymq7c=Dq?LCu3sH0igq-Rkgy+MK4A?>c_@ViL=fbdI072Uvh8vt0tt%s zrwE46x0dvuXY{01@ z_g;31i|Xw%u#OndApKApMPZ5SiD?OS@r5fPNL0AicVrUdx@#gU|Z8>pZoDBxfuxJ_1Q7O zIVfGFlI4ZVCNO`@X|VC4k(Ja2B>-I|KFXzPOATej@{6p`!?4T#$wjZ$3fr0u=0ygg z!20$T72TSQ8^dqiPKu5jWOt70mFFzQfx2M&L+0H-9TaCbXV`jK*MP?|_;rI}g?%a9 zQmo??$FTvG8BsB3m~k7LqJ*7`gBvL~*f|h=j}7FR)RhQAFMuA9HQJo4?}-Y~oc z`Mo8fFZ=54o{=59$d z+=-`Ag@j^tSnhXl_i4`_q1q+iy~#;;kY>@BFA_>F3?>)1;OB0mpBzK)9-?d(9cdGr zxn3I|ZE?e*F6?w>dA`#a;hXj7*<%*tdDNY`F-jQBCjLS$3)lQy2eExVJ7IMHeE*9JV z2?}LxtyD93QpM&v;LwmF<^xv@JVL7q;LmznJQfd74ItA|Od@X1t3m+MCI3vy<+)J4 zJV67r;F0z}F<~#qwwWrj+lV7Xr{6swL5q3?E3_}M@5KjtABYiJh^L!r8^kQ(hA-U# zV^^hj<|`XC13E)wd+mVppCb;=E5px6aC_;8;t(Xu;MYzQg0GX;wiLT6P%US2owP2Q zTBdh=W7Tcrzk1_j_UD-OdlqmX1CSr(0UDGP!)sHF)Z%)Or!P7D!)()F<`=A1Wb&9P z@jG&OiDPDp-O8Gp_&DeIa(U@vMzwFLb}3K0sEBD1(ZKZmT9urfjoFMgi?ZMqJH>x4 z*?#S)yh=GM{l;N$av*e$7!TN++=Cp+PIu?YylP{+_-MYsI>!SKtJp~)zPAQ^Z{$Kn z{H3a~MbP8aXtT}Wzq19Q?RcgA98N2ozPEWF|Nf+9m*om4-3~H^=HLx=4>xL^O9+NM zLMnot>$~)^_C3+l8B5{=rqMEF33x6L*TMq?xtoIz7Cw3 zIjbTYoWa*IbkVjquty|xDsa5s^x;+M!MII0*GVX0CSBaB*?lezt}(2~mrcb>%FC~p zk$IJKb11yG$!p$OjqTbEFAj^EW>DkX2Q4xx$s9B|ra{xx zGWIwQ2kBJp=uY|-KQ&bBD%X1%AZ9uY9CI8VGH4pMGgJny0=%{@g9x#NF$5D5X{frz zGp{!GhN$Q%)B6yT_!P-=6Qp(Hb|ZW{h-hkZ(cd^l$Lp*%w$&3T!Kqh9(M`Ei5V8t@ z(Q#newkLk$WIr~CT-JCS#6#iuq_(Wl4=+H-k_Nw8J=h>S(Y}ja%fIIJa|bqO6W;N?oFwatHgTKB5Z!ToZds-X{OfpFMXO! zDYs)je>sQ?esIB#OnC}N3pWCEMttx)Xas-ly({v;>!2M^C%T_ah*kCV0C(F?RpiWI z+W@Rj2`T`SPFReZ!dj;Fe}=xcW92k^7fGrGi%ZI|n4-$qzp{iwiuscoZ8%kebBD;G zF+oHS2sgx0NO3evo2`nx=EnPnV&x4I6{Z;hnC4V!I3!|~G4jooT-EA0fyvO7`3vS! zMR-)!oz4(N=6JK*7j~dr1)B2Au@4DaX@IFB*K+L|84@I>*UtTw^9aXyf;kr;$i>Pj znWNm{#w-PEf2SfbV@8T-04%MMXp)2xd*C{8EbSst;xR{o;QGtL@>k3vGoxZgjKYzv zgN*kJ#kW0F0R9*BMj*HBk;4@eQE{-(u!#bI7(%oIIt(dP|Yb*W#r>|koVqb9~m>e zfrdWPg>ycI)qGxHn6n@tI6L^hY($TyCZ` z^x-sQ2CR+O1h^?!OjC@_KblpV-OkIbG*az?>k!Fa?T;nR=H1FGvEmx6e(wf#=9#7_ z(rw`dAGM~DVl(X9D+ekE1D2OMh7RzUQQZr#$*IFjxph%SFN9rPnQIHM(Xh}9th*B$ zv6$8CxFEFcKoIe$v;(ETPHcv3dBJi(et`=>XlLiriu%h5rBE##3iFfa zjw_$u#^WE%8dtP;QiCi0v5#ush?0pMl`CKT9Rq`LjLCC9>L#hM{vKO5%O0>+En%Sv z&p5@vS$1(DZVe?Ul7jg(RoGk{Tc;D{;i9mfANx)AlUsifw~i1T4l2I@_59)d_zNd8 zG7>iL?!6*E}OgEWCM zi|h$@*u9Rcg;B^QPH6^B&X{8%=P(J4X~#!G8fO3VHB3uKnsQ6!vijBu>$qeZkR`n!ovrcefdEdLdcSa&RGpEXKSIlj!;*AW zAj@4M@#H$I^@J!v?Q@d})XEz!GMofuy0CL%d^AChHS*>{HZqpAnlSo%&8;5Z-2xos zICQkb92~Mx1|pmeY;p=aT0m8MEA@rx0u@Efb)hrXp_G`78lZ~3Ql(Q|^rJNn%B}YC zBG>CBmM}Rb;-^Fd>u~kr(fQrX%~qL>e27P}CnVagvs||1@yD?bf-iM6<S=-PyyBI^_t1dK#bhR*Ewg{*-_wH0Tu-(=ysq;H zEuFRP%=+^$LZeZpZC7eu%(}oxQ<~j}Gt1SHFKdXt9Q-FCC{C^B>oh-fn{V9^Pntlv zJ<$h#P4K*)8~DMdrVorv@JU01FPjl znQ;2L9Ddqk(Qxm5MZM~paEdhCmYx$)l5F>57O zo-(!SSiACYQ)pgp|M;) zx!_>Jle@=gn}fBL!zD$l%82PimBe!mN92Ng36Uu3CdFOE!r>i;had@747UE3+8f>u zLCwNcGEOmh(e#R6`Q@iq^{PQSm?G%VWm=+-5V76^T=q}hsUM|MH)-9iygh~nQ}ky#gqYPdi)=NQtv$iuMax# z!*2lBHcWwKglRP?`wtM!XGk~hK^N{MIyLp$HN$7$Jvo+6O`UcQ@Ud^sCjt!mFxd4J zX{YH9gWA`ROpWbpj6bbtHJ-y!&n-$#z2>#JKeH($f7v%?O<7N_I5TC-g5Z79i1O%< z`au@0KI89Q1k#=hx~uLd^%^>Ve9RyOG+gRe5jg3ZC$;an$fYdAl3Za2`Ak2?yG{ua zv+it1*;XGF%Suw$#Pqqb^(&FIvVm$oqS{~u^oiC@{+gG|F3j5~=C7Lz#MKSt)iu}< zc=RJ*xZel|%C9cp?kyKJtabRcJA|az{{$ms;M9;;m z%k{?=CY!+SxU@iK#=%rW<2Bs=Vv=nVYPWy`7QSzo0g(7#*YvW%uaSzDzD1355uOBr zHB}gfR#IB+$rN}dG=G==rsy_ggYMD`ZFgFVC^WMuyg1D5Dwga#mLqk{ksVO9BDYFr zIFPGZ36$1OVp}4OMbVqnkFws)M97Uvir!6VJV#%TgeBLI1=olr*KDd*;v#2Twt*0A zi90olrp;41_a+s0GM!COyvN{|I3;?Q@uWqZBb(<^!Tkq~eQj4Ku#Jc3pdxR5fN`)a==exO;f-Hxj0{D z$|4QEsOn+uOlA(v#0?H<>s6t0{EAmou?gKQ%^Tnx|N`C9@@nyG

36|GnJ~uC_hh7AmD!iF!pF!X((zUmfA+}61;U( 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 z�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%0D5TB;9*!d^60C}g?o{q>TgL438}8RUzWItn9Jth zPO=G{oWXS@{gCyWM-zDgGq;9Z*Ak4D;;lzS=&vOtd!yraC>WOd+NWdKOR}iXpVJM( zYJG=@F~L1|iT;TGhS2bjcPUQjA!HI_g-$jHp4wgmZi=Rb!I=V5cd9ak?D5MqAbnOY zx2P27J|`Z>s1c>j-L|6*v z&QDZ}=U+0d7gMp_S0zNI)E}_D@+#}FPV$Sj?n6Or$KNGII=Wu`M}qXD|5|?=Am7IS zc52}H>(rqB<3Ygp^Q@X$2m~Qn7$g=c^X`42C~q*Hm;@q&OpeB1OX5-L)>i`7m`KHf zA&e`0ErmC@3u%6}ob=tw8v9sxPm-AoV6?4il3J;Ttxci3=k8K(1h1{ z<8@Bw&c4U$9V3{sF<}*33snpXX3VbTi$oaem@nIvMc2);eKT^&QPyQ*xblOT*o(uf zKng?XNP!F016Zj0a3uqWDBAF*9=*+n9X1tJ#==B=X_$;yG=|RI7iRoi-!qYAOlI1c zinN>M&2&=seR67%2(kz+BJ2K;!?*xvH6eq@W;n#uJsc5_vF}Ch7hPCx7Hp{})tIk4 z3m5I!Pb@{*qxjjb9N+QLqO(T?iEA7ufY1wTx6r8N={&3kVFV(?AhMj7Wbl@dF7p7r z-Y3~KLou<3aN79h_W1DZm>oE@$#2Pwv3Vi1j5ZvCx98M}?^q+K=g(9r#h~_5_P8#c zcAVX-v>EV~gLnzy`0&rikgCTGq;;iyl4=^boZdmTD!Ly$uTU6igk6Xw5o(^6lmn>f+E{b)NJMNp$y;TD-UZTFQYEn z-w>3}g6?MGd2gAW@#)p2shyMU65nj#j<_p%KOu?h1+Upw&tb*E4T6$JKBaGLtE2zu z&_R)An!w(b+!LEFm@JdwkS!JrVO&X`?`7%O!l5T^Op`Kc2Px+0n*Tx4=!6k?RAg2CSGeZpX4hQ879M&uryW zp^M!sg-JBTtpc#OpDbE3SOv251N3w&L5Hr|dqgw}5eB-BcEP6$De<%L+(yWYW~a9A zuT=1h6q#J!TYa=cTN55ClIx(p^1#uUyzicZGqJ>cTW2+$C zGMJ|d;mo3}hL6NEMV`X)g|V3=;I4nAurO&piQ{1Ct_fP})gs7?dr4|xEFpg4}|# zl!dHq=2j-|ZZfh8zQoVEnv?P6{wX;b}>+209VhvbYqRFHTWt8H0b@&`q;`pZ3Bk@DsLxvi< z5K;|)d-*lhwTY6nKqeZ*SYtb?H&43r?ZUN1 z{7k6?)r~XYk+4f9&a6g2uc0oMg(0Im zSwwM;V5Df#118{UC!(t2xgsKuLmcUJ)tasB96E5tph~Eyix!T9Mr10M9f5b}Salne zWoYNd{S9$QB9febvCq`7?ZW7{EvMaEI^m-Qe(p%0@OsYF9o|IT>eQQiA5Fv~v#(~> zp2G)L%sTJ2T@bDPG~7^%>JeqTzr3k8aOxZ;iirCRXYmJI`^xfTn99+_{ChC6r>A4& z2Z=9Lv}*1~TYa=wqbmit@_wEDrFzS9ub$BaKyjfgi`tJNhCwz#XHDhkf+|af$CavX zTe&ocp{@b3@_u4|>xgax4)^TKDSX6Zth$}?%$xZ`i+D#a4LsR>2YEf+;XN{a#>1s^ zOjR#gTe79#jKV|8A4^W5>%~Unv#Q5!-ZkXEL+v?)B?BFJm2F}~LuPcy`F$48~_m4f-+2atMBybSK~(6)yj^!SyRvZkg1W^$`6f1V{kI|7&GN%mP@Q zQT~th86V)O26Pon!FQ#!qaT$mhG$T$+WGvk%Y7Lv@ON_+JgX0fK(oorK$xL55`&l|Q19 zMqQVR<42bSU1u`F%7~8KhU`_}G?qDH zbWHjqjWV=>EZVe2&J$ycsWwI|4;wV>f@DlgxSp3#r72NGKS4RgR<6CG8kqF-MskKh z4%w-R0(}X`x?Dq~A9T?>LeX6Z15=jydnr&$gavHIPESf4(IUJ0iQc=}q_yD0Ajubf z+?~s0bJPT`vsfYv8?SBMeHJRCb_{=HTl47+y3TfL4jm4_%{-s{>fMU=En~bDMl*&@|v=e)0$**jGP%|Ldp`eH8BcrG1}{4FJvE()=ijAXP(^z$~9nu>GJU5yBA{BQM2P@$7qJV)Q3D zTZP#}x25OkBoTvmDjlU%C`R>CWLaDy}IbrHmXeS#33Tzh2dSJwS%0--@OS{Y!aN zpg=ayUM)cZ8rR*1oJc?xU$*_F9_9s;p|Cr&(&k)lDjny)!Sxx|kQw8s7O5S1R8*>!;230^$%9wWpzKJdx9saZ-WPww5J;|GD z@RHavENap$ULg0{AA40yUpZ|%7lN3jy;+2S%%%+wLbq3#5}C`3ZMCT)0T7C41-3v z3%ZO4opT-f$>WvN#+748O~{CV8}SV1oS9|GZEgnN0qVk}%y7*km=V;u0$wAlI_aA7 z@YxyUcwfPm4|&Yt%I=l5$mEr5CE7(a^O=YkZKQ5^qzoJv#*j4qhNle1j;lb(*+YQy z6b?)9Fe3sSPR2l;O9vufW&B+AKRJR7-%~ z|0py_&3Y~I#oXp=Np-x9rF)nz@Le4k+TiiF0J!?A8#&f>S|s58EeV9dY2(%(N8)+; z=I{lS-a2mxFKJ)Fg)7V^l1OFpk7Tl3KCj;ob69mQ(Q_wXh@*B*yBhdP2(W{@XL!gB zOd|X)x?9|YXiwOhh1_}rrPhKo*Q5QY_Rn~VSkaBEj*#f}d8Ju@YKn$^@AzGK@|#pG zS7BP;)?_0Fb^C)&Y$dyF{rg0k=x8c*6_YF|?0p+kxV?4>9~%GSO(B{7$Gf&GB)!*z zHQm%T-C4xud53Hrut|?9==K(|q$t(EidmMgcxvfBpe4S7eML(X{GDrn%F)Eegdb~JD^c#_ z@6BankyQ|3gZr7X;-t?;;;`e&W_I7N4CqrW|A3B|`)Y3mK{0h>|x3-Dfhx zN?deSCw%?uGp4GB2RM~1ZWP0zw3!8-zd2Jp! zEUT5cHiX)T8uQEg{CM@SynK)jC#9Du!tbcNR82_ygshp#rT495UO{GF^c~f%D~%N@ z@Pha*!Jc>C3o*05w_{U%DC&A3r<5wnR-)4D}g{`fbXf+2R^|tN)wYT zNkCSO2@5eGLG0NAZ*7&5aX+J7eCE&Sc44%3P8O2d+B*_8FWFvbwlg;57K)~YNhFlx zhr&GnCrDSF`B;Gn$ZF$XS>Nvf#h&Ia!o>{i6J&X$HJh0?^MvZy z!irB-LzY!JbeB$3rM_iXl8-Ley`85OV#08xfN>6+JNMB-7@T8=Pt-;N$`sRtit>wu zPQmIG4(QCiH!Tg?5%e3b!#af6i`OoaF$Kb@V`)VrW()&3pPln8t*)ry&gwvdC7N@}yd6hjc9gR8_Vp5KFXs-BzoHJOy{#q9&RXtJwV|4PQ28 z4h>iOD1XOCh>JkjAQxP?vo59pDb2g`F|^s@tpZC;4uSThFa?1XS{ta-HY%9W=S~Fo zH~~^$Q$Y51W2by2^AJ;`w>oGRD}dbd^9?RD7#488;NgM?=i6%A@*=~BB`l`CqbXYp zh|g_2wwXjBD!tc}gt?E%BJ#{Y+@sR9e{ z#gp!ioGbYW%T&Rdaf$7{PkuQcls>8maK4wY^r1Y;|9nq!yO*r>!TApVm7IPLmwolwKlp|0KRE@ZWP!CTt@_?9V&I+OyiU6-6}Q#jJDC) z{ug@Z`i`nO_^kgquu5z}Oj-7Qk3DK-BI+^Ojt-0??fcIMp8W$`FK1#c9p_v2o{+qQ z=Z_2E71_IPqz^LRO9A&(LIMd%aemOyp!Vcsq&ngouK6`$CcsH7>7BF}Ea5=k6VLKm zMZN~~q-*n_CzR2LPHF?*^6z@`c+gX`W=FJUVLs&MzNfA617!7Woif!1ZRgNDFv6Pk*IH5Q4>_7Xdk}1P?oVOJH zu|PisxFn81gaSxDJ*X^u@D9I&A#X{DxUdW8r)+?Cxa7xw_fu+GzERv6T}}esN}hCY zS=IPlJu^Zek2<%@g%NE$7XNCed=gX^gvl*}fgP19dZ;pFYT3@?pzx-^+~bh{Xp%}w zLDHyivT5l%=QF(gKWxQ~&^S97^@5VS^^OPjI@|5>*dx|DkcbzSw-`@~o6x@w4}-{} z5XWj4Gp8nd__3`c+N*elKhum82$y@m)zVV4lYOXBl-k-2ie+7F2Ab_eI}YlmitU7S zAlbHn<$n-x0T+@bR6tWF|4LJTE*$)JzIed-EJqW7<+wcMu=JOs%AQUd;^cIsc)y%) zkL#!Nt=a!FK0?$cI969OxX|{NYqN!c29JvG`%R7Buw~(b(5uU=Ly;%hi1^g-HdR*< zD>bzR{&2w)FJFg1=o|D_YWQPZOdgotFFTU;t7gFUI)w63F#DX(;XOnR ze|6~Nkx@XMQU~gk>z}%^Uhx2QHL7OcC0$GQyG{+S+e+1lrQljw*1QGkRE=D+o9K^B zudXe|74=rWy-qv=aC?d#0x^0*IOrf*LGyH!LkyqTP_CVd_?Usqkk6-)&3wT@x_qO^b76J4O^T9E?qAg=>bLA@Cz!5xB44@-9>@XA?B6Y zrXO^@9Xj;1HyJx0yijO36Tqu{jRs+BLw4(ZE49{{u@>z|zF);_#e#0!c!tL<>=l%d&itHev%m0SwtcKd_Ez!u$9hc$G2y6jT>BH$!dinXNrS%l1-XO2IdS9*30s)AI_ z?Yf~o&8MLqhxxmKCnSmR^a+hXrSixi53=5S|7WGzdZ<(@8L29NR;qUA*>get4D^YU zt^pP6ogNOp(e``@8^;-oPu3!rTSg2OrYnFK+~Bq6H?5MTG$e?h+i((2J>pJP$*g0` zNF0E35j=H`Nccg@kPCF@WC;x4U4tIF^D3Z?QYnvk_GBCx*>3MeL3JbsQn5Q``e(Ft z^D7#6QDr8waxpH@sM1KEg?kth)(`g%XAro3j1`XHYNwdRj1~!qEl4~Cy7O87HQpkk zL3Bl}i>giDBehD@b-pTk;iv(x5vAmj@_9E2o+Dn(=x?$1-9G$^KKl=Cl->PH2uyVF zq)^T0li4V^$9M+eKYfpU@TjvX#x3=??}hQ}^ZfF?@IqF*L#>eH9cI00AJv;A)>e?+ z>!l@5VtuiuhS*e9rt#XL|!cCLIYatXJP-GR5a0bw88^|+WuF9`aN;}Q&7m)!=H-TAo3!Z zDs8T69pWou4@W-v{Fpw#`0Aae>&B8QiTrRHUmUhv>^$tQ|1>_&Cg;KEeeaEvU)S&n zHc;85p%CDZMVL(S$QTrJ8l+A2bK{3NOc9644Ih9P>2qD8W zd{@8LoO6URc+hztW6y8Hi5tqQbqzF8(;x#JgNSsHNTTDrriLSv@$PME!0&Kyn+GE;usPW6~N^}G}V&}a}{o$T{YOKLV(sG~ga4cZn?Xq#4U;3Ub z{Hr1F^H{w8jG0kukKPkc2wW!rx-Tc|B@L37U8f~@?S4yDnF5oX+9{7xR0cF|EJ)p- z%`j@ApqYvKELnl-2;oTouHIn^rH zATsN9r%h)`Sy5)e$KkR71*{)a%OW;V(QQJog3pyM?kE#d^r5)K(^62*nts`s>_bD2 z9YD_aSs)w*Y6CRn?^-mZo{}paj%|68;Xkn{F1UM9ww4{A>wEOGA-~p^6ub}2BJ%i+ zqRLz6j}}!fQ_L?{{JN@btMCC7RqE53V{cAak*y}w+k96tN$bm1Fr z?jFx>*GVK?aPd{zk^=B?=HhJ=gxR!x#H)G%6jiQyAC#xl(3qmECa)4N2B4^Ag+O*d zOoi_L)emAa!Zu}~T5ayr|G)IhgP3p_n}~4$F~R;TZ18*51f1{G02C!yO#;qWkqsaV zAqz{cl>N8~g?~E})yJY1gjf08)$(elQBgjPV$E&oDIQLgwO_)D!mF=UcsL##jn|HQ zwcPuigQW|9?5yw_R`3`4ez|#hF+Pf0Mc~s>uIrWU4|lrWTvt{UwRToSy&F&_R?+Pu z^$g>#u|!xwty|grs%UW z6NMvPeXa@iDHR)v($%^^>O^>qW)NL(3Y_$QC>;ZFHv4)T4dA4wa~HTaXp;*Fi=0|h z*=TOXp6SR$@34Ejvz-}FE#k5VYfo?Gj`O5f7&A}BLLN=~Y0v9}cL4e7H%Vlj7TS=) zs&!i9pC~<8b&^$J5>a^im4`mEfOUW!=~))gLX~TnWQM_FmSM-k95?;#uBZoP#qZN< zPy)(&SjeRKU(ozFkBa<#m);L=9!G^#Y`DqtvHZT8J zd2NDmuMBHk_UX}mb3f_1=eni&i?l*Zl^I95JzZkQ(Wu8Q%sLp3aY6jCaPOu1;gcWh zmP+A@(JB;?^~NN&(H$W^H%9Vei!i*^=hMK%J2LF+A7aVz!>x?4-US0{>_>ePc2y{UI7K25rh;wEima(g>MUt~z}m;fyO z8L&elYwkkb0&(n(kUlDPJi@jrf3Yvx7BynvtUauwcgj#Jc||ARzdac9ATx^f4r+Wr zX3zdj0b~2yYz9LF^)Bg-$5Sv5^P@k_hY+_y)D`DQn33%tGS zwzdz&p%B8&yv>Plo_uQUb!d2=nx1~Sp8ETG_G|a zKyo_#^$&`AHA+2ah~cO!2@(MAxfDH8gMqM+;Xv$>L0|I)#TEkP2!?Qo@huO>n07^5 z?TH&AKJ-?kon->*BZqv;x#a3J^-z1r910#+zx3?%p7aM>&ioV`7!O$1ECQZ!y=c*s@4YEs`Zh2_%> z1D#In3{@>;K@lT0#M$+o;BqtFr+Y>corD>&9elLrG+Y9~^^ALR-(B0UEsQ}H(v;tk zgsX#u&sN`mDRAdG>7GdaKIGO`9wD|s(|P*%qo=*Z)HxqwN2n^av|a#C+#g?~*Z)=C zedY%V6xjlzFGb)87PUK|(dten2O6z3kYs3?Bt3!02#F>2>B_1W*clAeFyhxM!<>D+ z1YskaRzMBI?nyX!x1+Yi8LQD@a*au1y4sVN395%4_tJ9KKH;?7=}@8FY3EVmc=L#; z`XI1`?D7pmmj8G%VeHO;!2&l zsy#wG19~mYpS_mhL$6g?vuEJe_nTL({{=tSfJrj|Ki41NCq$4NfS*VAZ)j=I$CL!A z*QPCGjMYzWR-_?`v=a>0n=vXDHj3i8FCYsoKJ?xSXdr?6;p*9 z;5nNxA0tZv#&irT&u!m`1X#3nHB~GI^e`^{StwDLxUyWU4?{5s3KO^vrZ(33#T5{U z+YdFMbL!Nv)=$>h^7&0dHEuZ+q9D`{psL_u@Fxun{<4*t5RGT@mYDWFb}iyr+gzG6 zd^NhPa+oD^5bsPRS$@l6&70vZd@&W<(xoQgT9*;>^wBIps^ET-D*Io9zs7i#LE#nM zhSNW}0`^a?5d6Ru$QHP!e{lsQx5h8AhB#SB>7bX1j^RzBJKa9(@#E29`mAPuweFh_ zt$XWG2w4^4Sj{5I^t&T#s^aJ_R~`UWO>7I7zlq$?!mtXXwQ{nc($==2=4s_1^asv? zEt)|-bY?0YMxw}lH~*+s>i1Z~*npsZ0KNPFG{wJY`mg&6;(>=Ga!J3vl%u6hSf)^} z28Syb$;PO{cMr)*cr5VPEq9gE&n^k03ajcpUVe0%5-r-t7f>bB*1DIHp6*?Qndp_7bdRrsRzdkg6!hvF&M1b?y|`8aGO2ZD`oe zi$*>NK1KC~QVfK6by!+a*%2%gIOiP2*)I`gXTsb-FaJhI7K7`}^oG0JYOWp^b&3m; z^UzfDYEYm+^|udlUB9~aIM3RTOH^TWBOrrJl}UK`JlfMArESh5mOGXcZxfjH!DTEt zqAdud=7;69a{)xN78bt=Ro5MsvV&5_<0j_rl6-EX)2a}#;~Q+=#!hGZ#l|pnV$`F_ zeN2JX6w#(ZLG06`6Z7!{xVkXL1P`+CG7je#0I%v#r*#9~q@XD4wkVRQSA85WR-6Gc?q&%ni6hL zKTCqq`x4n@?FelFb9s?=q=v=9E?%r-8Usq~XJ7t4rgc-o+UPxFvbh=0O`FW8~dP!IqY+}~J(>H-`JuN>={ z)4S}PribrvNwnap;{pUFiLV~OI>{7xBo_jC0P7aXq?o~s)yo{vo_iZh8q_1AaSb%X z`G{|6JBW`v~L{I6ox?cqF(0R9z@10|DMzZTnsi zXHaGXT$JL;uGuq=imj^3$$*A{Y_9`UsWzq7X5EraP={b(K2)pq(%Gz)kKKX9`f!-f z(6=ZQ68zc`jh?8YKFlL&y{(ztW_zmHm>5dRkU#KpilZR&EvEurO(Z`bn>&=4z&ALU zhTBJ~h7pZs*mdpNowScLk$M41sM3_h2%(_pN2mp=LrLb<+vZp)IvJl`-p?d-rPrF0 zf=yy9-+^=j{?8dr>Q7d+0?cTdqTO@|#~)@iw_!RzSrx7rdUzp=-7z-?dx{bOKUIFh z&vMtGachAZh{vVW2R7gu@3z<=I-DD<>(`#$Mqgv6m^M1Gf~#KcFEB=?WT0Gb-m(ABBjGM_OZdacZ}Z=1=`WoAS2)2{qXY0fxG*4` z@N)Iz6Y_8hDh(ioKkYbuKkfUkbk$p3XpcwJ zr3g3{F}3rfw*Bc;)Qv8zI8WL2vM7h(l3S`&dgB|T-t{l7-&h4cqAN0pStAO-Jyt?k zn5l+K1S}6$lypf&nJgigkvsmu@=R!z;U$IsOa+5r z=QPWzCu%u(I`5tt!`)XE#jDPV$vVill0kr`Sul04`%_7Ev9);Y#+=~G2zA)-C*w=j zI_@wj+m>53;igD(HJxAPyA;>$Lql?4b4)X~Yof;~8YCb1!Xl|it@KnB$grQMUo~e1 zjfiC4fd^3!5Op5|^dnw}2$^QQZet`n-6?c_WA zXZ!qYgrwV{v!@aQaMP2Dfg+n-V%!QoYy5hswZ7+(5ne5LB7VEq4RA{0u@PnM^I-sA z2-vt>(eJ8OID2msYFmDXRoz$?ZwxmS`~q9qxV~MVR>Udu!sIk(8OO%irZAm zX9REz{Ay?+9_voE4IBe^oYyGhIVDZ_(=ycyPdiW;bODkM#^)4Mcr5v(t*kZWLalRV>o1KRbkOY(Bd^|L$1)D zO7XF^%43-g2_3i+Fxm34Zc)%SJ=c#pY&jfWlH#@dRhRbm1na%dcGkb&T%8d$BkQ4Q z4tSjcJ6U*R(HIrqH@^^FmSUkAHceyTJVd9|gsskViL@1%_@{_kSLh6-*vdMZeHPsE z4oU>WmEW%_$B?fY5d)HPoN{7UBxkr+N_w0aLvnJ>_M!x@JZWV&o1)i7QS%jF*pPNJ zK&ou*_&elicK85Nh4uw9J;ECN1p<&N^D!4u=r-nc(~o6dU@F6@%59uq0%6IFD>8`9 zn(?-UcYA>$h(vT=USbH30;bsXWPCqe{wr??2h)cH2uQpFh4Q6ed^BmH2SRfDgB@odrYqLuhsMyM*mYyLw{Ej zB8s6yE;NE&Be`V@#=d&XHuX2H`sH%`gnp0}s!y+GBT#DV&v*8wD^jm_H#V66a4nMq zZJk(?fJvmAT=7#+J)?KK7Jvq-RZ{GMpzr+=t&+Ue--^Km{U;IxlRz%6hE^2m73Ti ze7R&qnwsv&&SdynA0XTUBl?Q_J^s3~YHYgzJ(8E}OHjJey_=x`tqYKua^#kTI>8;o z1MCytBfL~$3DKma;PVnwk_N*Pap%tB_&$=!NuA^8B_Yk6MSMfMqR7DDhKlfzAK=r( z(_;!oV9Y1h&PRNVi)~2ihyaBM-BLtYO1Ik1sl-AtCP3#i0+Jt>w10&^r5eMLp*C+f z-1*%X;_l$OGpBX6z%ZFUr+ow$Ndi?XW8{-g4b86g_9j!u?(Vx^f5T?QxGy^+m6Q^frHHiLd%iplz6Kwr5b{ zgVXt527eGhjHDqOd>QooF7B45(d5f^h)wWuPMCMz6_%WqyC-ipRc02|+g*TBnU|*< z;>hBvs2O{BBZy8G9R?_rnZkEvEU=!{3xodn=?KY+P0muE!I zMdDL3LN37R#sH`L(fQzX_1&gY*TxM+4^HP5A%OVcbV6nDc```A;-BKy@198m-|*D0 ziUSkQS+IK68VSm>83sbJdLNHa6+ni_#paQLkXv>oAgW+9R;?@Lf5Yg8tCA;Og zaqp9wbCsyUpp<=9<$`;z*3Th8W`QGnn5?VWH_9RPl-O9VlPcqY1=*0+1sj^gdn-_4 z+0#rkHy+!fb~Xh!FlAMX#{;zvAHo6(lPXC@;TCY!@}jd8ggk%9slGAf8EK~{JuWy( zz$t#Lx^V?I_J^BrR}D@YTEuxHb1Q`UgS6z-($6TuR7JxD_mN#{4j(z|dhvq8HT^>r z!C2Y|9;<^$k3For$i!8c&bL3=kYVC;nQLV5Bn$JstU5%B93D%%(5XeZ+-3_Yd|cAV zE3K3cVSGlZr=I?grT=FHFV8j$lRqFo&*A0gFbnZKY1&_5Df4(6lZh_ZtNNZi^8rd(WVynVn_xxTuQD-1xYyj@DhJ?(=I!`ocdD zgD_?o>3+sK>Gr_T>YuSrSgo_zejwI46l2&W&WG=W806SHPZMc)3kYfzFUeG^Q28wP zAk}B>vS9@Q5{Adizd%Az`Dvmg$~Nndw>y2r7BzBI`E@L-$a!%~Xks&!Gf$J0M`GLA zYLTC@8BD!byFWj&u$nL~d>?vlLirjqGBKveFp+eBsH%THHsqEAQJ}3ZiW9g5ELMbl zi;K;ywT`1K^b<`v^dW-Rit%)pZbXp0)byCG`yDXv8!nGD=ZF(S!k{*qV%^9L<5a?{ ziGIeX>aIvkp!cpM=~YJSJlDCY$?eM2y^$Qx%--=#=zskAPh z({E0%nPZCl=%4|UrY!Ge;hUC@UZ6fYE7ZZ}pcA~|V7yAHoCP>;7hJ`7Y0Y_1Af}2x zD^+1{*OPR=>GVF?0-=7Ur&YfCqH+`)WYh#uh3?jHjL|tp%Qp3LkmS$74<)m#n-_e zvTz?$=CF{Xu8%h{s336V7O?V3xU$Ooq+uN%HX)_+6(l!a1_{GV)Ko7i1gWT$fB5jh zXne-%bS@=zdU`&k&-cxfW1m#^hofKG!hMF9TRb^4GRYq-N-?lwCpI>O%IGqj!!?rJ zqoO`0k>+TD{zs8L*mLZuaV}v}h8KJMf7srYSC0CDBymg_4vXTS@hD*xJR1 z_!o~7q8ZPpy~hrxadCM?v5c-&5JaZ8zFqmUjYq4%4DDSVUq6n@?ly-~+Y_yVS~99y zzS!k>-E`^0SC7;rUC3IPosUVsF}od;*6Q!Ai&+gi9Fvxt*ImX;RLprxZ_@)Gwj{Yp z(CcKHR|PT$)I~L}q>%dr9({2Uf9Y6^J*sN^I4qETvK!VXN%~@F@FXgK9=399lYH|S8iko=C3xrf znHa>~nxe0rvn@tl_0<>idVjp>ACU`OA|`K)7%|pfy_zqjjDJ<~4k3r<@%D+vr=%lM z^7qva!(oxl6fcwYNFQlUg)Bxiw}JZGYV`a~>}YKL`lT;-s}@Z;uvJuLbB5K)>OQU~ zV^gxey?WeBK^gio5mhqUpH(MeGm9<`b?70;>8(FHc^ks3sc2js zT`n0AWNyG|w~a{t%2xjBd_+8%;(hE}iv{Sm+d*mxq?*x}UCU1Etp(`);DgB#adBms@r_5$*?Ova-8O9W!>Vvk&GihJxo@jME zb3~b~85~-dtkjGA9M=2Av_hLQ`%ySWpBXMv6zo*K7n~QUPKrjCqfsM|Tq9b7jhGYC zB;EQJ2F8Y`3lbc$#E}jOnaOD%3juH07)>R+%fGbDoS2>DO_px+O2mX0h*Th9$wwK% z8zMV#%s#cS`tXe#TZv{~#p!)d8Zw@uegV=%+@Z2Sl5ATJ`u5bEq4A!KYWnckR1r7| zN<)NWUZV@|RFcXFC39J~B11(fq=EuC`B3DO;aiSaBocOr-%F$qD5dy2?^rVW3AxQO z@+Bz(7z{LQ&cLkKYOf|sht&gVRlCg;@!6ZQ#Je9+%0H)@I-oVaz~dXHWyI1Dla0&o znI5w=-mZQVkJwS^An?HnjMl@p?$U8^_10g``5R9`-`oJcogWoz^_eAY;^d3+TIY_r ziZ``j3&9_&?x4`mYUud0QD1l7`OEQH_{jv@akfYmeVMO9iKuzX_w{>n9KQU}@x;WM z+!22nqs6Ug%tGiV*zZa?K6)L5Y@;XlR7>Arv+8{MX1$%fRA$l#UcJr@ll{ownyN_$ z(_n5#|56|oWj|1kwl5OLt)k*JP6?zyU^D+QyBXP}0j|uCa>xsN(Te>gzLuaUiiGx~&ypcK!WEK9M=|X-ZCVi@$4+r(lq zvxZM@B~}zn8h^{kMzn7xphf7_ZeJbxmh@gvcQvtFD8-z6wJyp$TKNsOzR~LntC6Xg zRP7JR#V5&eDcU`~eGQuL^`=wIkNcQa*9o!?`nX5#DiXiOyNW61a7RX>e2DAWEX-Vu z&P7u&Hh`nmjM8WBlvcM!Pze{Y$$o;@ZOwb%2ni2ZiAe^BIa-tBQ1 zEXlXbaKJ83NMm6FbFlT|77mpkKnSMB?BzAlsj*_$M@$V++TKN`TfML0(5`WG&pwr|2BlPIG_PboP zsjxmLBLP1zeaZwU<;4iquL3UH*29^i}J|SOPAx6X20KuV5V<=KWOFg7P z)}#Wf(icdja2vo^XE7$wmN1CX^(;vRcBC&9O5wC#!9`DDD3U_$J)~>Ci@LYP9`Ng- z*?mDJ{z_d!3f=dVuIU$bAB#Qk(nI@hMJiyX#1jkG1pF2A*ihpru<%LPf$#GrM=$81 z_ysu%{5kOB_|fmC=%JmAVCo(C;*bMsVszrKs2L6vHz`_R;6I|?g{Kca*dRvFC`ZkB zLvfR*1s>FaQ<}!mL|rOvAV0wYKH){bn;{qVOAI=w6g1>TyXz2x_A-JCh~WJ;u$Ba# zFX4GX*~o6b1%tJ;;Rboo?}q82)(l|tU3gQnoA_Yxqz>F5ANpN8J+vn1AWbk^9PD=* zb3pZciTD2_>??!n%(iWVySoN=hv4q+?(XhRu#FSk-66O`aQEQu?m>gY+kH--drx=W zu16JB`^WcduDQmXYswfBecfJSt#f2|FsMF#*hX|KM$cdyP$=YSpQH?QK-1S)?8n=x zwwn{yXzYhwc;eS^?MoeXhA&j@OPzSK*N9r~*V!F;GS|4)rw-it7uKR-J|O=3P?$e> zi8us4Ja2(Z-T&u&sfw$!nUURJmspck^%T&6YS1dMGN};O)M}!YRK@~H+CpMFN&^L! zfkUW-(GfVSAeb#W^*_jWW^#8JuQ1TWc7v~b%dRDGe%Wdmw=4{>@wgmKbspusFuz^j z?ooc4qhcQ5@L>6|ULTrF-L@vWQU_k+6;=s4w2kiZNcaT0Ts64^gFoO@#fx82$3mLY(8a8 zW;^R}9$}c~k|St5GAOqBvGm9-vV6ty5ViF1%Ay_Mnw_vq9GPv+G)?xgVsWBJ!-0j+ z@{5XNisq?%e)HM+54s#H&M+qor>s(5-3nL=Y*5B}_!-gq&Ldn3C#gX(GHj9yA&eqZ zk5+poGSrJpf(Yx@Xj+=tw1i?uKaq$c4K4(Y_+$6V);>Gy1G)7|+{ntZQRVxpzv}SdOxVHW@mu zVX}YW5_AZxK)^&CFBJ}u*SkX?mR=Q0Fdrci{E;EuEkSn=Lh5jW-Xo&@r9K}oYl!ON z+woaE?(a}_8fq=65DmgU(`RHXebOxNuw~)|iIEPWB-c6Dyo(`}8;MpQ!4x4ZzQwzn z>0(zT;PCkgXZ`#{A{i-?9`+17QMD=g?tf^fs_K(Q|TPqZos#yYES>Qu?^ z(T~KiP+c9u1UInWih7#v5Bim@zG`}Z;eMd z-$7AiKj69~WArmew1@i=(&||MM5oKIbeEr`>T9`3*{Tl;1>{1U4? zW4B*+jFdo3s8SyuiI?Q;FOGkn`V{P7jJY~gY3e;>yMT+~tn3mlflyPN2uY zPN=d4DyK7#mJv-&;~`H(Jk_0uxLTOPqK0!_CN3=L3&huAa&lu@u+MN>?N z($3en*tEi<**$9$23oVAG8H#IfOehIAL%+&#mWPr*WC_o8m-*0qF) zt{~ViwwheEQ&%__UZ)_)@Y-E2g`$ZAe)h*A`is2K5IS@$Z$s_0l^UOGuG?_$Gun_9 z`59wWRxYvM<8jea8}B#nZnvmOtJ#5}$B_jbk^c;9N0UHSxA zkB@+Q)w_42k9QGIc9~?o5k1Ap#in2+JoaGeN%E%{(40H)qhtO&P^j}=pH=S=(W#50 zj4W{YaXpQdDfn4>znF@mTMZQ_7w7o>Z=Nh|4j;M?^yDn4PoGHtuZR49d-9*k)=B^J zW5AY`3|0zI5E)`$M9dPT9OAX0iAIqGMhO)fhJ!)}`!vCQ?c84O5pMHY;!eDROyBFE zDWGR-yAvp%Sbx!sp^TPkYvF##_tWd=%F5gO%ecm;C`}7&K3?f0xA8-DV;RK6wb;S? z0-wUK%y$GQ4)>x$9Jdh~ZKPelsGwP0fnCr#sSNSxAgW;XpT^h%983E_LDg_#QV<*P zGmS>L@&ID|s9)BtA%qwxcPye=900kvNf&Br$WScL_)NrpN!jq0gmO<68&(vQe8V75Gpi0a zY@sjfeRdce%;h^Q)n(8|k?msTIFK3rBQaGx06)(OCO`E$on?b@lj9f&vU(vG8r603 z?_B-7({YYGj^9wx5!Tozc-!(+;rUo-Ive6|o>APGRo967)4Fvm+>2Fd95<3qLoo*p z3^n`V&BZ7Q6FV1XL9nW*F3v*rHdyX-1Am1J(Adl|J&j8<57RrCF;Vd_zBUHw=VN}|;TcCT6E<^D z+CoAu8ueuk-sF{BspnP+gWGerDOIP^$P;mAN~c|rdd&!k5dq#C7m~U0!+2U%y|f{J z5`|phjm%8G-d9R88)^z&5X>SkW}N}Q^rFv5mY4}vHWf++P5a*o6DrIPN-fO9w7vGV zm?Ma=E2x~^t0ND@k!WW25~`mG=P8hcF>cjlx@eAfQjd(1tG{ra38oAg8HXG@)7zQ}4vAB=U%YV~ee1 z)?(ahhop71S7%~`;i{S9*4XXlimg(Lpj)}t(J|cQ2f5OA*YYnxTtXz=3henyZr^f# zJ-D78>dd$O;(6HL-`w9UD*n3s?$33UGyHL#o#@jw$AA4=E&;MFJxVP!Gi_<786)}@ zziO{OLL@bPan?~{qq>p52)Qurc+vi@^dVSTw^CF!SyV;c2@|2bW?C!5Btxnvo{gGR z8d+{h(91$BJ9OO+34>V_^D9E^^;FY1h$K*Ei$q9jHTwCx=?KS#CWA=6)o?NkdZxQB zu}FlKL??W434t4nE=lisV{9@@>*T7&2-8;On>7w*S`){}6-k3Tn_syWkGH<@Vr&W{ ztBu5PYGza6jx+=|P(~KZwXX>l;9Ug{ zVQl5Y(U2$IKRPrSvn(IOC425GL!JS+hxIfRapq=FjcR@s2{eorRiix5V3e!4$F(^- z`6SyXgP{q_Az7;^IUQq*^BL)RAiB65!nm4$j9RYkp zxQ7kOFh{WnLRj#RWqlbu3vRbhkXAIX&7m*!-5B%{4xww?AhgSKV`3ay;W_m&RkhiT z>!R9_bWM#EjjBa9I~e%&+8u=LrF1s9!V*%)lVq;b%EzZ6)9vuWUZF$kj-zbF0gwfy zMx?Ur`tt0rzB;9Af_m#=4mA9pq2^7L1T%NbT=#4UsXQYGRr}5(vt~aW+swTgykZV( zW>(0HI*(Sa*n}P7LbnlNdutE;^%BE-01YvpYKRrXh|$5le?lM)vGuv|gdW|a=b(&V zxsvrY*bVLMV2GYqO2VL{?2El|C_IXQJ!W_KN-NF9mU7V6VqcQ*oU?h-=7@M!r(|S< z+rF-0Xo7p(x=>HnT&Eo@+U>;;NNwmj_*bUrP?cOq|JH}V?Bluqsv6A9Th_>LFk~BD zW`kxI=haORW+_q+cE!}GIm4+PXUk=VWnswzdJ%FNzpICzO6%DzMSjzxK8 zb5?`T>UsG5K|;g(^#e>j!-X1#Srom_aDeE(ktw5k7jP47+;7xO-}+8=1NU_gM@wLq z`?YO%(37gg@9__Cx1}~+;xgC0F;$(FbHu`7<4zop3gDr^9u{) z3C@XG-|V%TvpM@#(6AB9>mr~bw>G)?k4sX*R#+*KsoiW@IY&Z}Qaa!5&3%j^_4`rF;B;$q z-(=di?;y&G?pM+woqCzMf$EqB5rSO*B{MD>wO*!U61{sNVGfn%O>jRI6b^zUG&ie0 zXu+a%EP^SG%(oIKNX}eC#{Y-sbg3BW4OS?*oTO)EAlc<{}Hm`Y&f4 zBl^m$nv&yXxm$46gOMJQ-95G=aSxh9oI3T1)ae;x2OPjbJqv4&6+9R4 z<=Jn=2r`2{_|;eH1i=Rq*VIt{!MSGINV*UjV6_>5yT<$)p}PgcEr&Y&ueTtFfdq%a zXk%QrziK@oD)&3qI2{5XP&D=3P%~^Dan%W=b{LS?A*SK0ATL3C<^1Tgc(7!?j&mNd zJ~ra6a$10s3Xj23^sXIMRdXW}Q0CE&wn?5XI9dCbyYnsDM)Pr12t~KcM{9=_CUBp& z#%}GYaOjS^*$$4x1;0vN-FYdAe-i?jGa8JHD8Tu25tVl72j64LKSBAfLTAoaF^Y|y z?oD%;o#`-LEiQ6IDRJOdI_z`5hb!nzR%$Svt(lv?nL(ZvX5DCsw`HpUkTXqb)izH# z?}m-27MlITkWA!Iee@i3x|hO?M5AG`ZMVk0@D8&@I|Ic%?RZieDmvA^I6>ozdNwf- z8>b*M!wuD%n|w(~?B6FF<_NzgHTIQ^{ycmwW%SZoE5K}I#Bl!U63$z0e`&kKacvKo zSyHFk?R1fpnBG8gJdA45dBA!lePn+ElG#Oc0zM+0nb`%@g5qGa|KL4+bffoxKcurw z6SMuItYzpTyk9842>~1D#W($&j?-^ zQDk6E6173LBn71>n>GfuYY|){oz@Svl(H#pZR%#RX>qYp5F&gBO=@*%`x2%T+y5Qm zklj)wqz%=mS~QIo>eBrAlez=MhUI(w z6R$b{=rj?wIJk%;(*StCeQ&J<(>Vsb13J8eFu)Ysf2jY;!?zs`a?t8>yYz{S&ivPX z%fOCZy8P|iME8?bS1z@JGk-tVN1y^&kP1GzT4HS((p;)Xn&PQ}UU|}U>YTst&N1~* z$FYFjIS|e?e{XtIhOOj-J8tk_bw#}3zZO-by|QlL3uRl+_q-D3blgDsvBO@JPqKoh zBI3lus5Fp!GsBYn1Gi6~j!KkUFhZJyC zvzzpjb<70wyC3hIwS$yY`2Baem9(7d%(*wLT=^a;`K3@qY7((cPG4Wt!`YT&Gyvb> z>@(Nd8~}4yn&r+4UbXmR-x(l3759x1z!DTNe?{(g9ZiJ6FM!}L3H2e>;OdO1Q!vLW zO(|d&z9KJAZN$q1VTNa%D0)P;JTh&eyupbRu^E%N5p!q%+u0KDHy?$1cd*D03L|!* zfGI=qe&^mJr^GGwR?x^K05x%Y6+zJ!;oOD@!REM(<|fiK z#Vn=l8vwYMs3{hygGKc$ebrj)HEOO_t)#uu%f{+ZJLz%#0KX*xH&P%iTmdaaKL6P( zTRUQZOqL&OC7aX8L^SI9q)U2i($;TQl>mA&v<;+Jd3s^29N(z%r_U69LQu5`i`1b-8J`AsSQb#9aAJ49GO}t`9(CtiZP< z>Ql0z;*V^nd=+{FY8Su6&+X37i5v5T$sdha4b`>Xh#%1jWgzzI?oTTcp`!-_NQ}tK zy{cz9ed1+;)2m$=N2?hS;f>g~RBiw~v9K%|RFqOD8RBS(DqBhM-=es2{Zq79_CrbtUW7 zW+ULVejjmOHd7lebSVcA+=nh<1OoTmSHenJVvIDBnNIU8R}{0>wk4&QW#>I$a- zc~P^`H+0WB`5;SLEtHy0Ew!KU*?s6#I(`n;U}?wCMvG?MO6{$q{DbGkY*L2k%8W&> z$-2wY%U`b2=ELwAKnXkYh}wy3FyIWu)*#KNRm_ z>!qbzzAR95?tM?oZnM<-;T5Qxn{cN4n~D76^Y5>9TAA#>R}QZxcumihxF<#%^)9d) zZFqBLD5;9}y)>`e;4^=MD)_)Hcv?rUUETf{mEIroF`P2S^ccXh8WXTU z{C{G}-Q<0L9Qnx zh|QNMtWIHNcp7+IfQp~j@9)5#$yW;8kfuZ>6JxKgD_p3)TyCMZbO@A94~a#I?>m~8 zwb#u8sKGtwEUq6~GHvQLCcpL5?O3@lGCHwltOBbBiIS}{!QWWSFsX&LXUyY?>B3Uo z$zpd>N9#wJI~;Pg(E1XTl$zua#;2jvx95M4E3_4+K{I4qtyB{T%811})&D zYLb#6crDh&OWn}>D91*73S~RxX1)$gB3uGDoJv1ylWyxEcMLOP4)kW$(iyRr%tB@EFu~2;x z$uZZ^byXmFIZfEE&U7E5{FsY{k~?%Rg?tmXSI&X_(p+&fOTcNe2=aNG$YaBGaL^7;@k8a;cVd-mA_Kjux*qP1(|NN7^)XuN<4$T2Zs;waj>|-E5b9B@3jybj5w;-1CUQ&7H*Is^E@lv<*Cc1 zYvnw*yW1bEH4F;=DV66-s!!LI_0N#c+Co<1U?yprt2;kB%$Tz5MS$sx#k7=UHeHofVUca>B(OX-k<8471>`#)z@K_!;+W!K zo7Ex!RHxbAX4&c(?m?A<%eCg%gSh~`&TP6`KS{!F{lfEmkQv8a2t~LPT1VpIdf-DP z^{fD2nJ7sUELPA$kPoTM zV-To)dFf~%FDi=dt#Z3e_nkwvan84S+Im{$LxiVGZ`Op-$?QAxe8vOe(5H84ak;cf zTVqEO{`Py+Ih3CyqkbnOhd3t<*0>j_0?Ki5VTQt%O%FIPu!D2h-0foYjgA|TihDLS zQFb9pP4tk=bqm_$Wl+3hzsRY87p###H>vghDzkNhK1^06askb-2b<@P8fX;xTdRIU zGiEdC=6u#0V-WEfSL@`W=Dp|2!ZLzUa-h3{fz!w|gt>5aQ7Yy|+Pb7V zI49t*0TXrR`Dy_eF#Z22VE(zQ6Z2h0gV}W1eI&CY4n4-b;MqVFCbb&EouOB%@2d*sc!mb(g#N^l~Pwu;q8}9;T}0+}>eA zoOgO=M9<3`hUkkIGRX&9zDk=(TnBz_z!RfcK{U&#Kv!6{ApphPQcI5V=$m8V`=&m< zsL{PMi^YwxzIVA#gz1#-9{SxVPGOykr@{4_2guj2sq+#0of4p{!FXh;RMlzIYIKU% zDpeEoa<|s#b^dI1j#(FAojb!!z-M`1=KaI+iF)NrcT;=wJBEjXc}DY;Rl7Q}+V%7@ z1>wx%qdr4Vu?1_5v9{g&-Pl(fiSiU|MxojzccKA*U^jBZ8Y%I&^I{9;BiX6 zQX@eOE*p=6;SpBZNkt=Ql7&|Z;{w-_$g+29XL~Zp=w3zzb~HYMtIcGRo1Vd*3w3i| z(&9#CdH2cs1@svL4U~^WQ^5)~#jn;Aq(L4UttsQL^ zgc<1>T3eIr-_Nxc%$t!7$6w?KQjF)EC!{CGl3`bTQ|jHIY@k^OUh&xOSmC9`HQF)@ z+S8|Wmyi+a4d$4zO$}xpiATE1j5tTS@^lQS(z9tezAsR3alUiMknq4xyEWvac{XsvE3`h z6OGv7;f+6YvDwf^3HewP+D7z@f=g(PQ-Jlh%7`R@t1RilC}PJA3gWs1OYV?X#FFM= z+vikq`=dg#q%DHu0RR z*749$piL>Hn+Z8r+58nRs*I1ze?#iR(}Ln!O4i}9WN?YUqIOpE@HOs!!Ykez>;NJ& zd2YFE3ffpDiD8NYyKh{a_#SdsFp` z7}Uiov{l%#rd6sJgS7RDVP$a!lXD z?bjd-V8#?XPtjmuioZqmtR-Ta3Mr^g@iK;yOOC|6V@agniEpH|x0KJ}`BjTx8+Uz3 z%LSO`6VEj<(C?)p zpULQHkHZ?<7l_Lh5_Th7D_i64yUFN%$f~|~Y(;f|B#5^boqLh_RpkZbsUhZ49k*M% z@+JERDjezOIR?@H&~T8MNk-?_$=9;@c4nv4?ybOLt##kJe*wpZqi_e9H%g-ZBB8#!ojPnN!L9 zDIWMK%E|Ntsg37KCM8TVt(z83>628E$~|pSM31S7Jdt_r6RV2m(G$e344$0IsKSBz zsGM>G!4Oy}Ej+iI9qCW6-hNPyL4Lv>+_%XSNdYPH#>tU)n{HrEJP{#2mBG+cvg~Xvq}_}ba-cF$Q6a05ewA$=l3b_($IC=) zqS#|}E=OV#V8X+2>WjLe#HiAbsmh`(1gn-kb&3*+h67S1qNvh+LRUY(+S*3xz8!fLiG{E44Nk;i#_>0mt z67})FbQk}ydp?yg7$?x?yZ>Li|7ZB-PrE1n#qQU8%Ov5`Dm}qOp6e9lWCQ3K{~x>S z2|HwXvWyjbu;&gqGaNp|?nSLhQDvDRh}NPxQFf@Ba%>KgMSzy)3oB}|FDjDHi#uVC zs|XJg=xR;pWhxpWTS_1QcsF=*ti>g1R9~I06}OBe3cp#G8jp>ICRj4icEJEBO77@M z6Bp)(QrMy)G(vl49VnX$i7wxMq<{0fSBk@Y%TzSfTozp0)(D%X;u|K3W5r}%AV^x| zeS4ydJ{O^=*haY8(4z4wr5W~wz(2XJ8I+7(c!QXbphL<>Jfqw&SH->$Mw-{JeOF|1 zwSC7tM)%5lhdl5Otg>(&XMt>9X&_q(h4K-|*GcK&_|K*Ix4Y9qdvgl_P5%1-HT!?w zxBuhrhlO==P1<%5;rs%koHwOiHfkD7T}^ZM z-<@6#hkLgN2!dEIAw!nC1i8n)r|I#B0KJG$i3;|Iy-V=Y`+;G69s7q zyn==ryJA6&s<6cKxykp}vT5^RBbLO_ z+h%56)Gx`lF&0Ip4@|pPELo(u6C!~fg$Lq#a`>LXQ8?)A;WCEN%K58tq7L`<;xB?> zhAD=0zE(R=II$saDS3Cdtp>}s*nUu@{)6^eC2Rx zVc4r_s$XbJas)VdEAk6BYGT$=17M$fB#1B5*uH;`J80AJ;5wSfPTvafe+RunufcYP z65(Td6Lj$QqrGbNP+pDi!M>Y{`|PxFI3@V8{y9Tj@t30Aj~P3F{PNwVEAB?iz1}nS z{2(L@3?RfZANhkM93oHzZ^xLa(I=XsF4SjIOQ7mu2tq$=s&v32YmNVq%$?fS4 z3q^j;*pnvP%a@XG!(>b_kTA?@7{l;!#Udq3=%oTt_-1Z5`YOfYrj4guH>ZnNcC-|0z zk8-&7u5-Hu0zj6k~`@dMC6*$i_ zK0}eNcTcW`9myetZT~V5~;q)?joFZu9{h(S0~9SQFGPM6Fw=-mi$(eBfQkbhNi7 z7c0dT2Q8&nz;pC-w?|_v>-uz@^L|$RfcjsE{{v*G`0xx`fglqA@sG#O?0Dc#bcR)9qw=6_oewWFTdfy+(_H?Mn#1mL zfAZoyV#F&7M_S*YqIWKq@}}<|%QppKXTG{LwhdS#GNYUOMy#(;PBMX<(nwPPU!{gI z2h4#wg>oXXMO;Un=^WC5LbeaCY$8j@LU2l6!|L(G@h~(|;I*Ux6w?EIx{ zE{yD=d}5tJddT~vHS#*S5E#;I_`G>1ybWB`W20mEE73s$fIVq4Je%z0<cV7F+Kyf z>!tbkgz_=*HLE?g32e5BX}g2a{%K`ON5os_H;|c`KH`Vg^t#A-+;5Wga;yNQy=_RV z1y>U9AP6AyXA-gdexw5V&i)kkbIh67{X5-G`dwH?gjRjXP?RY)(p?+K>`W{o0<3Zo zuNr3&-suRs^ZMpq^HYBsFWkyg?c$aTI>+s0<_=n$}Z7r|`#BQzls z?JOl=A|tT@PHcSC0rpp`qaE}=i*hQ=P8o@?ld8go5m$$5zqYveEQIe9f{Z-6jVDwDeC9r z+Z)tZ0$$4bDGEDM(m8(Uu(1%5*N&&tCI}Mz2g=<<%NOBqtG~j~PSQ8{qmUjBpZV|(NyEpQuPfzwt1kcVMxu1m(N^(Pi^h7?|RLChcDEb4^!ft_+!V!wgD%$CO!i#qB z_;kH>@x%1ZEG8Hzr*DmX%%8GR7B|pehj<0xzQduyeGe&^H79y4mrc3kYK>`+!M#LI zIfSQMH<5I}2S=g(Zr}1!(;W=uX}QNw$ech@S%QqxygqpAK-Y|~T4zie?6w&|$$5aG z@f=9`8VH@d-T#3MCEQli$eZJjKjyedon01>X^!jscGz8-;Qbx^g)9w_!>c-q>(F!r z6+c@@YBkhL-Fsq1-yh2eT$=YBP4hygdw;6ueLQ@$OZ?7mv76=KKX`-1z~VVH(`H=| z4SPgS7}}b+ayYVPuJ-B##Z~>)`3%)#(hL7*_s;cTliZuNw>g@I!c`LeJ1%=|cG<=l z2wu%(nR_c*fR=T$e}Q{MOcb7JF?#*_1WlB9LX@TRRPW*Pu2BO+q9OXn-#WqxQhSJR zKrF|PB(J)*pWa!0#oUH?9OWPLtPY;{SOw;W58b^fj8lk4|e`z0$kK)?@#DfS3{gU827 zulo3!i@xSKqlf^01NW#GTb^dc%vUr#sV{LSDO%jKIz{4I7#}zxK z3^T!8CG40dHd&aCQ&4>iFZ+5H{?z>rczQ$%$WHsVl@a`e^s!u>=pAjx1Wr!x;AC>_ z>;Z638&E|CEh*0z(=xNPGfe1<-YEryjnvVfDDxJo9^b7~+LPB~Lk?E4a?bZt&wx$9 zrS*eiG!>54RMcy88Xlu541a-b3o#QJ2_wrjR&;#GFlx0D(N%;dI7aU=3Z=2vC zdh)qmu#r@D4{Z+}8#*;lV5WphdjbG`F!Df3=PUkDGOW`d+KgL0?^p$hUoTnuv`X;x z=cjh3evl`6%XyG%Ot!4syl7xFXf%CPRBF|-0 z0>;<%Pk4)@ycCxJ)Ize)tvtsE6v%s*#(+%GluY5k%pn_w2o7CXhc+C1mxf1qCxyw% zwkv$<)H<`zlmpAe*mzHX5vWSSFOr{aNjjJ04|?0VAg`ju8TBX)gZTq@IUCk{1pW%B zxtVbBgFry#{bxY^Psd7GPGJpL3)%Bc!^Q%Y6VW;5P^65vrfG$ULyoIZhJ~tF=NLt0 zceWeVC%regQrQL#7g=x#KsP=wN*cm9-}HPvoLp=ASl@gFt&N4ucjk3%if;UY=AMiw z_UQ6-3xFZ9Z3^2}ZOyrYx-{~j@@Zc&cec3|3SbQXkz>n$+}I&_eCVF2n1jy|Pq~i< zgC^ED@iwFYlgN}>XIvmkWlv*pVkbOR?q)07->le!mNH98L!!F&jUW(Vn%QV@w7tsg z{N2KRfaN-gX+z;G_R2W@a;xDQUJVX|4%wX2RJMaCtrX@dPD3M0-90fzj84llLgTzf zA?@cXX(ji{_Zl-D_CqFz-X5Q>sEs^Ew6;9Tj#%hq!}4EJ#<_L}m6-K)iVuJ-B1?XK4 zSWr#!{X^5?7~m@%n> z9lXwS=TmPGWjgU>sy>o|vI2$95lBa9`qJM#NB2y|#(JfkV80CU(oMM`MtO<1`=28I z_3&jdZ4&N*ho=_D#pl%#CU(4BI6*@Mfypj^gpgr`Sn6F7eID9FXIe=;x z>W7lJ)J5Zk=cruFs{{l*_N&?iT?N*==f( zhavS@b$T8y6HK6<*F^TXNg^nAzp%T^ih4r3aZ-XzD1DQqf*Cbu_RTgN@Jc_3OUWPF7dp(@77;G0B_+oMI*CW?SA zNG>~GB$?U+6^W8a{3=s4#+R6{@EhyU6B6~AnWz9M)#m*W z9A5d^0SAzI&KF3}Ab5D4~}I9Sue&*>eJ#x>M>jKKW+J}o~JX1KRFN5Rai~(8BbB2 zn#r(A!&P)@=~KF}0`fB&+Ksg;LY0(picVov=RSS$mcC2P*Gdo7;4_m`YD~XZ2A8nuF5E?Ot$}=`nQB&bs*9vmgFn#lf{No6oCe{p z@rsL!g7-o06%*%`Toy|pB-37FKMl2h8R}46mLQA~U4niI8m(!)AW~cZZtVVx>ROXN z-+OoJocNH+YB&hx20@pQZ=D5oowcYHgUsR1P%c{*4@9cm}mpn#K0S}-~lvLp-^oSA~b*#$);Nx%YuwD6t zyjdVp#{Dx={>eNS^QSD(o-ZeJiJT1d0iLu#f_h(`v?P^MF--w#pJIOACI?UtZ-3RY zQ|Ppt221eYWIXK5aco?7wXAzbEe@N}Z~xLQL#&l4`z?`^QvN$um1ADI?6&HN(H429 zQvfwL1S`UGJKI%)Wm+zGbeJB^M;PuWlFCMgQB#<3grLjYRcVQBUrwqB1o-6vbi;A=d3)r15r64qs5+PosG3>44&BvLbK~o;~if zi6L$RFruzBs#NXquZEyT3rHqU_P&eBG$Piwjkn(d$`9l@!$S#as|^-w_5)5bvgBFR z19=fC-wXzYl8RmrtLFQQhIM=ihLxL0<4afuSqbw$1x*SV#3BW-`+N5!52tft{q8b8 zV8y~DbapKzQdin}4Of^UQvXI;6QXr^gXdol@9=?VuP56$d9aE;!H{VU=8^X7Deei@ z()P@_K73g8lsJaM(VE%DV>n=${O~)V;2Gk9k%5bj(JAd`k@IrKK58@9(T?N#&Yo#? z;OytW!loNdUJVowHdlb2fd7A2_Bj62L2yg>2`ug*4|U54>*AHV&9N6Hb=zrY70rwC zIZ9$s!>S6iTo6+|#OzVTMyphDB)49}^TOa$g@f%*h!C5{s0rK9d0G4(-{;txdj1zf zD*i5x$bs#tq(F+i20R#btEYwyUriH`CmicC$-t3ve`cABK;BQGS&v45UwhgIjiHs& zZ)OUe#}4w0TTCP{KTi@j5`J8x3Q3i`-#dj8fg6m+WVtyx0ws0bDOj`l3@+wH8GNSI zZkJsaP>==gBu6kiNs`dCtAq?u-(PEGkvb1Wky2Mc8pa~BsqO=J25D~;Je7ybq{{lC zW1|%8$KyhGFPm&cFS6*ED)eBf@c!7 zJAbCw)-1TOn0H$S-VaVmX#;V6&Zv{$`Y+Bbc+6=v1tVCUal>_=6F=}Lvk>9TwN9mv z!KUVHjFRWM%?K(+l-O`5e`?zJ3LT(bI9pC@Ki$Gz=E0JslQ6MSGb)Q-W~p9o}vCWNGc!8FD(-jfHLIjqE|DkC2C+%R2vYrDf za9tk^PG_;HxT)aLuGkIaqWL74IgSD$e!#Sf85{L;m>I0hF+)0#*{0@H=1T^UV_s>9 z2Tq-e_s-~Wlh5TBm&N7OFM+K9P|hC0{=?B8H4^p9_;YY+XD{{D0J9)w-z&&hR*x{= zbKnLW*v|J1iNVSC%^$i>OK`?&x#iY(ZTnrQ_3oOPfjZKVM&FnKuWHh0kiq#@a;7-y zjC$v%(tvvi9||Ddmsz#U3xz$C>6s9AnFyd!#eS_dLJbb5GCukgiuvAqJYy7}$7tF4 z&|ZU^-K>jKGF7c8==_mC{YuF2Bq-RfU-(EyojKuQ% zwh16f;jpFVi!bgzFIRtT!z0glR9uXXHdClwh6d>=EC7M==BakltcOSBTFC+4MjttH z@7hXYgjzX`kq7UMcCsl*e`G;Z`F*u5<)c`oMSDi`xd4ak*+Dn}d(ibpzRZ8V*r^*q zgcwhRaF}F>P>&c4GI7%@1G5wK*;_tcCSGYmwTt8TdxwLUOTm;)!N{5aU}i;ymo#>)7$DwE^-{wu{N?(ZBtTTV zxO@gom`$Tv`7Lx8!hNbHZIYhZMz(y`GkpN#T$#;~oC9h>K~jj)9rgNe!-`k$cnr0` zI7IW$ap+H$K{w?!V8s`iZ%9sOw!y6LFPlTq3-@cE+c7Ju& zTc!M$S~rxwAdT|Ekw5zBazrZ^?|Ihsc{;0w#Jk7a3({90^YK`Aj)kddhuwyNL)qSQmKd&QQbMft6YLrQY70V+)?dXIU zUZWfHtFSbwG-n(r{9 zNqlSBQ#cKWJQ+K~Nl`o$sy_3CEuvOcuBI`CVPkD2Z)K<>idvSZ_9`p4121R9Q*s&e zy>42%(f5fQF~nE|;->baiu}G-*|Nf(NY=pRlC8hXejF@W@gcbRvv}o)R>s^_$hJQ$ zXt{^qAE`Mo*n;&Q=92e^c}y8Fa@_)Dnp%7#MqT9TI(Cl{BdH1o9y1j9t2G6<>R*g* zDWbHZLur$`t95rzR$>Ir9GTI>8!XZensv$zitSP|@T9Al1x_^epQ1wHIyil{i<$al zFdb0UAsx{0`$lDZbYhJM1n&XNmJto#c|pvklUKtT6GHTDiHa@cA}Rj(@edVRhUo~&?AOrbL9Bl z9$t(fbq4?+XAx?`CzWS7I5-Xx9LY1n2J`{1roqrJ_tmZFIgCNE9`om8Ddh|4zP0xX zK=EW)?j}{UmeSYe`KAlp{72Ox@szljV%vR~!f%ON!?JyzV+k>!#NEZ9X3a3^()R;{ zaIJ{Wo}mc>M45ms76eYRO;G@{=u}Z#WS{>e^xVjeZqS-{&b?mdO*z zDgaNqFUNlLUniaN$4RSY+}6rDc7q4b`*Z;Xxpu%5#sdv+KN5EOb&hJdrg(t@lvNu> zcX*VThg|J%Btwd(y4#80XcxaoxC>amPZ(pG>`+RVo_VBo{Xf3m!9UM^djf46+qP}H zu^QX9ZNIVIxKU% z=Fu`_3HYT$fM5E5D}{e^nu=rpQVM@dIIHA7m%hof+)%w?^y=~|5!4a1V6Bv+zT2{i z;cGRuI4yqY_*RtzWVqAg_}ThYJe2A zWv-ZdYwZQC39_cs1}!UO17BhCGDXt*U3bqXpoT+Rn}k&csNP^$LYXR3>iw>zU&VKv z6JEg;5v(GlW5I&BrEHq$197}mT@!)>2*&yd)3jv77?TG`qg%XxpLer|aI;w(j6yCHw=2XFrvM#V)EtD!l|WAW$@N?wsDC-7 zwm-lzWT%z+FEPh^U=~AG%Ds1{h1L*DX3$2rp?>n=g_+{*#9Wl!a2*) z3%Ay0cZz5A@(-oGP&;FBWo}$OsX57C`(aDJdgu%+P5u-+RrP3z`cS&7Xj0Eop5Lb) zFi|r$W~xh7r|@TANa^;_#c#>J;~&RMX2!qAOPjwM*<_m<%aw~!AReI9YgUusJ8(5> z5t;f+Fc8n$E}2{B7V7b!1cn|UE_!3#w-7FX{$Z_OYuc+fd<1+49@$P;z;-{NB^_gl zXl_!>mO5eXbkIj2y5nKiccaz~sM?=Kx5G@qwc8(d9>?|*+T1t4=$|)daA|DPAdNQn zG4woj35uu5r^s-#_Ra!K#8r`1BMx1ZA)UpR9KWODhLm7NH0$Jix6M!oNKVf>3Djuu z%2h$e7g~7v9v0#Cwkl~W3NY0G_MlOfp~%Ds(N$rKnP# z7YPB2vVH|)L~9e75_w%-i(zE21J7E+HVhZ zE+X{T@IsF9382~yoj(&#vNSB$jv@F-(}=$@-po3Tzm@huA78uq>*I>b5$K-`qDZhjry^pbG z=`DQ9A`ad7Sg!NlL%rZXo(Ktyq_VQk_1;aq?{+~xW)rU!w(%VbDvSC>5XBv!aEX|q z{j1te>MLy>6UYnH?H94WU(7wK^o_}p^Vkzr)m7v|RSNZ)2AaBDLsYqG%V4}FTKfvN zKe4=2Cb7~=gJIKh{vh(o#9MmffHURCf8=uh?kx&d`nUDmR70ax$GnAFSH7mVQDs!7 zy*?1F#6t1nqtNzFJ*6=Y_#3JWEB`|blxlwXSGukofgiZOTAKwVN{8L#KBaOW%bLMoYyf*Cg3aEoxI8#b=S4U#k$Cl{t+V zymE_S^te#RoVL&=Z5ksUO>UV;c^+X8VOw`ur^gsCssLEw#+WCi6Q84UQ=e7xOsh|& zFo<(vsx`v;3^5~rv9X!y`%A<&XKW`qa}sB-fektJnrw%Sx7ZMc_h7O{G2SdW3ILc; zFzn;GVed2jiQ7P^_#EnVLL_;F=5I8}pg5PnnCzo-r!ISK&e(&cbQA8)H0mZaks~rrk~NoP49I7YM~6r72m(u>uUw~4q#-HPkJJO0L_IX--LA5DhW`#E9) zkf4@j^Cvb1N99Sjs(toPLuFNw_Ej}~`pJB94`uWSpOE6zv2{qnJ(EWY+RbiUy$%U_ z88rxgSjHn_PneQFgXO1HHQ%JUVy9TtLkim1^bZy3$b+7vn(@M1f#`3D z{W{IR6&_*ifT|KKmnd4gt`GAcgh9x2jX*iPwIUuD9_U93))?%qg7 z#X$=CigYfj>%YgIdA5=cP#}(n|M~S}`oFIqHgG#ijTJpaZ<RpEzMdQYKFY72!G8mBBhJ=QN0=N-2Fx&XJmWgp$P)1Ldj-Eh$^dL@ zRF#?(ltowX9m@mM&L$h%vOXfYrt@9OC_f9il!;elTOVsCZaueAL~|ZO(v^y*t{@~y z&OeIVf45?mKvMip4-y$i0AqtqYG9Ea1X~NrO;YZ4{{@$lu8p}oIo*vdr?7O{{X>TS zk&}R9Q*+@yuUMn$>C@<FEo8Y^nv=w7M8C4HE%kpT(bOEGBWG&Jc4NEyA6s-VaeSn;%N(vx3T@acSaZBmDV!0boR|Qc zmh+)cIlwbnk|Nxv3J z##+p}2QR|jayG)rWNKs(c8XFpD!5|PR`WBru`ydE(+K?31qOp?l*jk_c$FZ@At$@* zb$e|nt&IwM|19oq(LS*x0$P**pk{>k#f3V6=+Xb@SMX0i1ZoOB44vEn1f=p>qlCv} zw`#p=#5^$*BJwM{O=zupqj4KqPtG>M1&v5?arkAOz^J>~D&}D&)5O{M8kf^q%faN` z;<=r`2U_AO#`?Ncdyxsrg44jGr8WKt+VL`dSi38)mpBUQHEIfWXs^6O(reJk@AUmn zDI}x4t{mDaYzBq9dHs0VOPu!OrO7K&6l!Y(!p&KVqW(mL7Anq6H?Jy8H1V*n_#V{2 zI6Pa7^b^>S_)3ruJuJPHOu#T^QemT$7 zmoVOdWHH7QP#{t`Y^UV9Y{k)6Z)cMo7jxX4t5El2*Yvw)g6)2psP6K9;|L5|4VfNn zZo5VJ;_Yi-wO0IwOen@?)pgJUce6zb-PxQ?I>Y-t^vm(H;WT@hOB-eF_YBv0YSmJ< zAJkPaW=)!g(@rU5CtrdWE%QRS6gQy2pBDNIJnW7La)Yuz!YZ-~*O3WPvRc+WV1*dz z+1~g+GQ0U?1N!gQ3P?rD$wKo|VIM-p zI;s+7wuc*LJcH)^ROtOV?IGS!fX$e}gYgYKJ4GD7dFku>rt=TF-(;3BW%%e&PF<1- z%*p0Fob0APA?eF zke$%lBnRPIV-9@6w~y>|r;OI-McgWp54aFs=D;v$z!14wKN(M@7RFYyI3RdaETib* zME3m7x#_sPwGBwkNz>2iXJLyQ5?tA##0EGZi|jNh%54+%c;JaD;)M`P8V4R&*TDu3 z2Wfw0bUFySC3n zOKl=>oET}?m&qHyuDr;(qug^oQ@p^eT3ly&rsAwktzm77mCbIvMulR)+)+!PBr|6b zXMcA9qaQ-H))tRs;i-cy|8tupXxxHc`D>f}-{d0mUp9#U96e{?7|qrv*;DPMr;sKy z=513c6v{Us6L$;ecUC3Z)W#owy9F=tQ_=l!*$r{w|3KVdZUh}oV&yg{ZZ?^T^*nU` zySXb@bcr+TL2PjF{MTnJx*Lzzfou@s_7j zdsV)~uON9~FXd&X4Z5KHL)F;z*536h$)tBof6dZUV1r!d?!{L-J9tAp4o?0prCNzI zicMbjI_pau-!YAiGqtr2iFz9c8~E=7?5q9gO2_X|cH8)7?*Kmo9*a(;Q^%m^5}xB# z%Vf>AoXZQt9$ky@4x!S-p&z7`-+F33U@Xq;j?2)2(EJ&Q2Dz$5cmza5)jz)v|IzsW zO)w;;8X2wJ2#)4|4&cue=?$xOLZwnx7Q$?mn1AI=14bp_L{mt-meo=r!}O;U2)=-B zbGu3*n{BnY#Z2@}W_0Y<7* zuJZ-Wrdw_lZNSGIok)vnKv^6h$b}0}Lpy&lqE-y1g`<^YQ|61Y&jk~(%v~4OgD_7` zV!4*LIQ-ye$w!{ZBr`8&QC4APYqQ;>{<1a%0#qf2<$oIM8;yuruw6%E-Br9qvR=i7 z%dD};cf-rLqr!ov+2`{kOg7)NV1)hH)mL|UmG{Qh^As|b@X2<)^^ltPdSl$Rq@}Xc zlwPBsVh3#=}zwf;f`Vg!BWTag4M}QLL#q_Lrb8>CCF?M2L6@|UOG(~X@OLA z=9_w8K%2RuDNxY*^~dhW29n=#2n0yUKLg}1AArqY(qPyWxnP6WK_r2`(F*<#5Zd-55y;*0VK~xekX8AJIs&Gi6_FjfYYa%UzvBA zl}>QC1*ku`7F-~2O`zb&gs9?Y!;g>?5J~n4d7vf zOpYOVDwU>8Uq}l;EB{5AKS>XS2TJ;~V)h5GBB$QQ7W-w7oQz23vF2AD;lytxlnpD4 zsgK}iI{`5=yrih0ZhBK#Xl`Ndt->Dl}Vsrj}`p7R~u7)RQD@JjnS{&aD>;d?4jr z-;9@>j)*Y+>MG;St^(oqQsiX(4z?Z_m42$55xgPX zG48#)3No-u#SQ;sUBb4TH#KSd9U8vQWPRfHKJJ&uL~9tiudRO4?l7@jZAfoHK^oTw zxA+`Ovg>%v3Fh;fr#VaQy+6jtLK|>H3zbVDozTmwIa9vv&}(KMl~{dy`onc8KOtHF z+azT8&-nOD`u-aqC=?DxUow0Py;EZqPgHxuq_nlEMbVPuKH(~=Y&Xj`jfhp!Rd5i# zskF#Lk@lw%1Y`0jP&Od6vH1Rm#|bbZ;g!Ol?*Y(I=b&$F8dw>VG$Z7)PDO%&I~W)K(M?^LAr?LWaOVPr<08bDS~EjRJK4rR zl?K>Lz{8;nq;Y0dBX`%W>W6uS9v^^f2JClAC2=7M5Hq(Q(SQ>PE`*M#eh8BsVGPG<}-dt;1fAk zzs2Cwc>z{ok+*p=0i@-Lv&()9d8tv{IFSlRo{7D?FPqYmmX(-rux7M=) z)qALi=yWG=YiKu?U&|lZBIh|9ef4*C&dAQR$HBON5U>RBwnchr-^rEEoYl^~rp@7B zM@D3=>n**6h+{0a*HgF8UDoSsEYWX&%62I4>ydHKeW^xq4)8`LBi3~;4F z|3|Ts`7f~&NJiQrDKXuz3;@a4r-I2Px>~nfIZp!bzoh{t$kf1@L8U?0(|CItd8FGtsle;(AF-c;jM8*L|xjWWBTvTvP(?|2m4}7EG!;8;Oub}TT zOTQlYwz9)NM^qkX{u1Vh%XZ>@nso`JO2{dS?%I%Np>X^O#Rkn;wd~+)wCK$e;jtYX znC(PLVYBJUw}?>4@$j=4rfcfh=iEbf+Eo;jo5=En7cQ{p{gu@3J{b z`{3J}Zh5D_gSE_B?6jw!aZH z`GUKpeL*;7^`GxY270t<2@pa=z^(j$8zz`J|K@a(m4JE0KoB+2(dwvD>VXwT81~pi z($e{UtDqz_Pkf5k58E2B9`G>5-_Oj2(Rd+^#`t+_rXcwcwTX#q&cr7mdSH(pdh z&tQ?#8|flMvMuPA$vBp0VNVMdFK}ecNRmKF5E4uk-2@`b(hP_3L(FcOkgrmcj6YI2 zvWj+Fm(jB+OX8>J?L&#K3O|2JOWQcYYL4{c-e;U*TwuY5vj`#D8s%HHxT?;p4G`s> zuKA#JxesT&3fkfa$-T315!X}P=%*cjf>T|cXN2__+2aP4XOJxaF?B9TV?&LYcMt_H z_99796)ebQ;?;1PyO;4xS_gG8YF~3j;u(X)0o}QO8Q6toW1ZP^ZzcCjA~&WOP4C`$ zhF;m)xk$o$uWYBz=bg5^t(DrNL_NkTjT-E4oyTmi*qcZO+cr)6?g92V=gqt~@DdBP z$BuquH9`Imtb>p?P;3zr#JMSugwj@`e#ewPLrj8A3?r+=1rbref{w4F^jQYn&mc{* zRH!{P+=<3V*mV*gbBoL|*@p>8BM*tQ3NW@%4YCgBa7s?xz8nCV%9vnu=3Tlhqn2PH z7^@-!EVYvo={wZyTnX=tU7QHMDBkm3C6e6e9&1+N;4)}E3U*4jk{{#LqNsYj89YPU zFvn^(^&{I^BI^S~+{y~pp{|fT#zsje;2Qu<(P6bIc?sDmz5jkCWpAQwEPxQN|L0fo zFI7JJH^70UfC>aA5KLYO*G<@mxF5;O}X zuv0no+Uh0GkA#-Ypm3!txJs?IMmHjXc4lk?5kS$LvpL=rC_~2Qrd2AOOF78sO)xEz zKvW_q158ZVa8#d7gC)_~{ZfEA+rc?i@zuS5S}(3sIRiE%k*oF+GYI{OQN%`BSWH># zm&%u2^=N~^qv@0>=i_Dt2MW_|svw`H>3$Z&ZK$qFY^KD#=mU2H16+pia3yO6FB76@ z$&dhotNKH=0k=XriZjY%9c!%rIl) zEoHj$jDNOOTu5o(fydaU8b~Br=++@L+qAaG&a%o-zSC&o#c{JW-9UQa`3U%bEDG* zleI5mDbe9xr)wK5{^s~uh%JzsFDUOk(tc=1fs~jw(Vw63>rE3@{UO_|MYJouhgc_r zSMXL&z2r|kjW)D?NU@QwqWLP4L0S!LNRy*&meFbZ#6<~$19~Qh;1Zv!y2a|)x=1zk zW)as*V%t)MBCTZ`<&Lo}I-(V4U1Dk|@8;8rF1lISd;B?5Uuxk@ej1%XT!ecgqc8JT%&=1)M&fzB{BbGv<72@6 z8*qk8`R5t#FFS#aqV#W)=#AZVdyvfnTPom++J?$QK^R7YQl)@8Pmvk|KH71UbJdx| zjcyr-6gb0y_J4HP7bhB&Rh}UQj`(;}-myCGkI&Z_{v_=NOU;-qn*A$$;ZKURP%&a-C7#nh8H}N#$vJd9OI4q;M!BImz}Dm z@jW%SUcJFS)xd?)S!*DjMh{U9m>w16wNzgd@N9g{o)a7j`{-Ybk1$B=;;{b{34e77 z7&y|PepuO#Y%?D(7>CI`W)?nUwj@iKlZYhQ#C0At%HOf+$kAjyrCU|2?pPg@5Dh!p zzXA3F3DIve?1MoCWLr^ zyFy!$3EqO@wr-0)fXqIaz%1_LAD9z+z1-g>XjIQM^KcS-33PEc!fC9KnX))ddOPi}o@cULn>`OPe6Uy} za;~f_vQ}tRN>rV!rdwY*k)t8(^>)?SoK_NDr!ClbIwrQ$n{YfCZ-$xr+DHjGwrnq9 z8hq3s&_e*UhZhrmhLlhQBNAGz1Q=jRn%m8OClrE=#`$g_hxl^YpAdp>P911&LhUs{ ziPBn&&vp53kvxraAD3!>e9%qEfFXwFTnqSa4yFTYBFv!L&xqeKQOj(?ulDZ4fkvCUJbn72JtNTVGJp$RFpE zt-B%i0(3sS`jDQ5u*`sID?8vD0ftM-vXud8o21S|_k(N;N6eMO+#~Eb%)$F9h~T`( z>!23q;-Sz4w$)c9v(Rp(BwC}#17yFO2o%;5QMNuWFb=Gb=4V#_k}#g3{MxEhvqbQG zE_3ZmwH7s?I4~NC)%Q43pT>|f8lv$PV;3RCz@HQDZo+8LvVit>(gVRj;E$q zVd`wiP$>UipB;ZwX%JW!-vUS2+tRUb_5d#FId;E;w|~foKMSFLgEV!7qI>aH-^Y%U zwTrD?g~2!xW*kEb;YV@@m#76N8s(14Bwuu8v`}NPW{UGO^`o(SG46qr@&Cv1-G1-S z-VJ;*G5_(&1jYavSsOSx{k`@kayr<2$w)!$6ws7%P=rz&jZP9$DHbvLDUVw6J3f+w z*$Q|ab6GW7nEY8N^HCwJ2IUhc-}n%Jm&Y~h)s&uI7sz3ruz7YA2#FHP2|0)fa=f?h zx$QDxZ=F3Ye>fqsCu%!E)ZX-s5c#>?dr)W%{w?|Wcrm3rS=7nt4P^9HIb5Xv-ZlF# zz`$~A5m#b0??GRPaIqat#W;NKucl}G%5EoxK9%1#JF@!Tv6s4!+(hM%CS|9!YOYrT zhC3?+*6gM`+&$sVK*29DI1L1W!~TaT(3BJ9$LQcTB#PqZ(YB3}l#0Us@|e+hK!h-+ z?x4^^Eqy*P)b2nQ6-xXoQY2iERA4u}8kL%E(}VzmIgNNS7u&+17T|TyIGK>UrUrJ~ zfG_4ydUII8T6D?OFKJKd2J zrcD2Q`kJ3jGJlY{{ObHbM!V1QjFT>&anS4&;AO@Vy|BBgOmcp<^HJ_i#cO8Cq#;zP zf&Tg$s;KS}q{68@EBS;ob*ZLWp}N#vaSrT3o31%?6_@^+df@5Ly3_>*#@bilcSrlj zS&ro|5z*)Ga9JROzEY5yq}7t7=qQMp1k9E#Emb8{q@JNHZnv4W0SYQKUO{tR)C|g> zpngX}4#q&`Ho7lmT`sejO^pMc-Bp_(SudliJ-OMFVUt>R?rrv9-VMth6+wWRkb~Qa zjCfn{6Mb*$@JaIqSH4yea!IFOt3(+U^UjA=?Xj6KPf4!qOT_ zisO?teiw87TudtMA-jf{h5T#2KK{ZbT*F?F>!W2cP>p62+MgSw!#i%48ID3V*y&27 zC@)+gv{p-$(~uSdBqw>oA;A8QAq9_h$8XyRH*UkTBh1XcIF0)O$I+`ehKn-$y*y*DUXtKtyZ#=ahuFM*phuqVz-U3+lhy@4iFku=np-&mjLmvQ}xrPqdoy(=v=PG49O2_M`RVA z#hz!erQ9&2R7()&Q3Q6tTp>2Ux3?FtZy~X;k27-$#LBWXhtg;l`nu&l%-xvW>s0Tf zc&T1S2lWDIAS&m`EkC9Y;%H>aKIU*tYTlaqjAT(AaSv^r0%K?BW$|{H*@@{(IYIB9R4C~ zFxDK$T?hsCW$clvrDW+6+u=JcZ=Vy2++`qn?)?4^eXN0-5q}5?kLPP4r7=+~1UWKi zo=oFwuKv>b6UbTi_2;Wjn#r4790|G1ar!4>b)sOGm6xlK^wW`=p?TVG?UqB2(O5U8 zh;;{3fdPlZqCbr6em>>i4C(>WAB0FS@^gFjS#mwRTqbtKD*Y z1ryiMG_7EJ$Sh~wfu5tkZXRgP?M#g7M+c>BDH@ImH; zIcZ89eVlLLcG_zbC){`KO47UeUTq!WrkYKMqDFZ` zfigspvc=Yk?8ZN|;uT%kVPjYs8}{p8%0CZJyy9?!?7V$ zWf1IHyoxswNr=jOV;|xN9$Q2F9xV2=2kV{ApxPsyhsF6pYGGZ4g*|E{lm!%w zWA{USxLC{-5~Q3FX@_ziMXI2z%FM4jyo29syoH(*-j;dklAmES-VHWNXg4%nYIY65 z$RMcN3+_(!KWCB&96HVK880bqkADF@mZsYvOXWkFq<&Q5n&Xuf8qNAa2=%-zFI!;e zq6Zc~*B2AU5co03tGoet5GWoWb`jEQ(yjkvtj>J;bmkYD3BITplTw@577egK&D^y} zlr~?YMvBCWdVwN6+_&)&Bn_)=)-6ducSo>DfxyTpswlb)r>LUDaNoF=jyM06i_<*@ zqk#DgEWeg`w;WTHh6-Ok^;9F3+(Hrcy5UK#Ji5k-Ei|nQj3s(S#3YwLoQsw6dkNJ= zdWdlA94O`eA8Q?U147yJ|Np=0FKzGdf7Oom8s-o3j)Jw(22=_`Oe)?jN<|fg0ErTj zdO!# zh>>qAc3jwN8@6!X=*rhDM6U2SjX{((rS~pV_Pt@AKwZab>*90fWNa&XZfkK#o&@D! zEA=Q~uO*s-DN%mxK}mtgoVZ@eC(z`EnU)5^a#x?b@-syePv!Y2eXc$$I5+SBDHlMk zm1x94JXcr9OJN%(3n@clN9wI(imoj`=1=_#Fh7RPaQ{tJ6>a8m!UO+Qu>bt0 z`j6W87ooqH;<73vs`>KP-RfT}6DEgIdL)!U--G!7%jkcy z3End}AJ6{H=&u1CBfwRT4HwC=2t|4&w_a0lGd=*)bYMjqPUO&;Vmq>0dn>y9{xzI0 z?v_502hIgP7*!iNOk<~6#cly(t;AXTxB+sAPmBJ-G%HSHaVCzCG$|zn)!?AiMTH4! zIgA3%B9K1tO)*nHeI2q)cf}v8`D0L=C1*BC2B2Ako2fcT2T#&?DHNB7h=_o0Pqf;L zn+$bgKCMN;Sogar$+r+*1Fu5WzK=a|zN>CJ8^JqX0FZ0Ho?`~o8qg>6w)m7OBk^y)4WK;Dah#vHcY)o?3= z4ApJQ2;83PhwrLc)J>(%IZt7E2+4v>mhF>w;i!6Zg>76H^>m+cUVpD@4>%5u;qbH? zs}b1U>%8RUF>fP=Jw+5h;ei5PW;LlGyRIxF&Nj)!PG;b&eqp+KP8+L^pF2w22!Q7 zH(h}!Y5}4M|9>7qvapl>$EoR}v}rdhkNQSm?NZ&^GicvOu$p8M&;mcr1(r%{EJpGs zA$HLN;S}&`Kr`h|s8(R)a11##9AVJxopPrIjKf4HW5dbwjBE3L-DyRys|y(Y0X$`M zEGvWQLwo8};t1}`+EF&3NS5A1Wei!DN=0b4b!e;Ts&tDEp;RsYHs6HPd$Rl_SSDJd zEeXE4D?N`fU|G>D&LQkvt0)*|FjJ)2!`ioo^`1g&Eb#!NglMWy7Xl}fz~1uHbHG`Y zi7#OV<9ngNj;;7bs|l*gxR6QMa{$#>qp#TZG-Q}5-eXwbMTeY~xGa?i^y0>;(i*9X z(^gkZBs&j}(j&lVLy$fTiJ~$c(XoKCwV;SI2U2MpK}y2Gj!1X&!6DhOf_y3z_NWYG z3+eWn#b2DofGF8Uw-v9BNg5w5Yi9B6kXE`tp<7Mf4jV`^Xwq53Z3UMy-Mee;r^1U-;1@Onlf)c0vrYlW?+^Wkc4@A`N6L!+_;2fvYs z6SnP^A(s+L=Ihkgp??;W0vqxI2x)Bq}R^p3L7lo zNl>Cw+zE#XAt9G-?hLDsVglRAll#RQ8kJG7o7FHD7m0naSw;0lUojwpDx}4Hhm=`S zVeFtjkd-yWdHwc_t`)CC&cbm$s4-VT8Aq0;eoM@T6h@yP&CirWt}(>b{H+_&ay&W^ zhp0EVXH4)nrm~Tj0l1vbugKCaI=P`*1)-X*O0Cl@phq$l z_OKDRy@He?7qJb~cyGA-67<4>+*3R3HQ0D)d8#LK=#&)7H;~{*B*?QAQ4%={&7#a`9GPfzi*5?99F;mK2m4>=fC`4o{cP}e>dc0WP~%xq^H<-B~PzF^?s9RCoNT0 z5h2w6Y>8G9n_-+`%?Mx1A@PdW50QjH9RP6O&qu6_fg-&`sdY8uWjPpM)_DGl$A~ky zHa?+7qH<$6kwm+?s9*55^k;&!8yB!&Jm{JBS;vX&wDeG3vpDwYMswrCD!H2(Td?)a zD??!#QkLBGnaq#h=F>Bm$ecqE_GpO?z!jEd!KP#^pGfALO@=+DNq|GeqQ$@4=fl)& zY*Jj5g5siLg26CgQvPA8n%3L=1y9ZM5-{5-gD?HBbRC(jPsB_WmlvR7HbLvp2Mp#%xl2SFusFfHyh7~GZSC)-Ar&ZM}!#@C~Ty|9y&7weg-`A#}XrLG2zi5fCs&W)6%s{d$!4C zOQ*z7EKb#2re(rf)@#)fvOA=mfM|l@jrbO14T`8>zkqXHeYnPv8oC|is>~UHV-v2k2~&yqt;{CK9KYsoTADuQM!hYb9uWiBFR*uC+Jx z`sh;2W$6h8cSQ|FMX3})_a~iUF1MNo4ulWiKf~uQ*M|>~&H&0qK8dldz_g1lMd+j{ z#j5~DVhH#c;^c^k2w<$7Xy7XOGC2-b5fDF*YI{OtaflL`55L(@A&E77SF@M9iB*oe ztEx@C&X1HkPzJB{W>#3{UQ>3|r!aoaO1=jEkmpu>mkLXj7uaRqlZJOG;MYp_){Bn;Xfl@1x{b#O>98Eu9H$r!qe8W>>Bn`$Z= zCts=e+IubRp;g+fFIX!TAA8s#|3|vP!0-YbO2!f~X={45&)yg!yP*w|>-Dvq&eu># zE2dlUGfnlOkZ{nmYwx>fn-N#5*A#o4UNxd3d|SX?`ql1gPw4lXA&E1;m5UPwHm5x9 z>}imk>(rW^%o~>h^&(FWxhIe=G6ZGg-Q-2b=YT72aYeUhn?RRCkJ(K(QN-a7Q_ZQY zkl^oRlDaQ1FSl_l86*y0Fc_+@!V^JV-J8S+Rci(>k*b0bCEtE#ne~-dKYSfP#%0l~ zd8~8}$T*1irHsSDivsICL2qnvcO%9pZsK~TcteRL0eL*Z*K;&GsUDXoP@(l1>X>r3 z=igyQ*qNJm5Zy8nX9o?K zoT{cWz4q65u3~?qXQ@Z2cSUkjm09`DcG^*eFtBbS>i|6LqHzO*Aovuj%Zi}kskS1M zJMq4Z#JgB~%9pG+8<|c{6~``9>_-WgQmD(K9|KRiBcUt>AYFJ^M}<9(xS+l@0M@|4 za6ioy*sZ0FJzN_DshMg^#5wQ;yBHoI^>6T`l^(a1ibAYTEu zQx@AuS;*Ca=F1Bgr~O`&U@md+D?qXZ-voh1t3jTB20%m(iT&-F7;vjD4XD^YK}j_w z>b3mR5#WWsP;E;b0KuElAt+wE{`8Hm{X3-ggvDWWb-&F>-&SY}?4ZOO^XbVA1Dn^0 zt50so#{&jKajqk)Gl?Q@j#!AkZ*=$T!-LXYr*J}x=lBxyIL?p@R={4S?0@w#?aR&* zV!qgsdR+EhFOp1J|H~}YeMKRQytKQH9q`*?Hq_3HE%~;aMbSsA;UlqX6=b8pmb?L8{=Kuz1! zNc;PsqGVEDvp@3?>KASCV3tIW@ZqK2Kc+OEl9@dWAatnz89INTz5nOGg|pLW0VUb@ zsu+>@S%<3qn@D_K7&IveatZZivt;84xDkn0P!f=QuK-2fPMiAd4PnSGspaUkl=iQD zgTcG1u1(($#XtRtyqN8i!jgclKE+^O+*4IuOnul&o}YTnI}Tk3?N$gP;wn0y2o~y3 z9S2HCck@>i@-?eutA)wY&u5c(#%-m^d-#2K&V2Zr9VDnk5o5& z8~>c{?!J_5(WAe5_Nh@*JE6`V4qdpE+^6IhtV5f-wE;*5dBSiU_IxUmux(9JiV#aR zN=O19j{ut{6A#+1nl{!jQ~A^d=8{3R@-!@OY$FLsj=|7d&daY5@Ro2ba_Hs>`RY_7 z^89tS_#wUoS)2)aFDKXxClIES<;e5S=C8GJ18^uzyx-^9f-3cwT!Y~6V>~E5R#C)P z4#8(J>_T%^h%-$c>#AoCV9Z;|y!h4n;z|! zAm$ev>8G64D#1ndNHiR74DtSx6_Xi9X(q zbXMd>d6!9P6r|L7$sRVue#Lu4*kf7a8hZWV05cJvu_nXieDfi;xYK#0iUonHb^AT& zO1>B3YBz2Q)(#PKLn*YOx*EMc{zuR+Krv?kUR|Ni> z?3lRg+aLhK2@D7)od3C|vi?2u+azoPGtN+lx|%YMRcUB4jHsz8!CK+5K)kUN;zSeD zs1d$)xa}~XDXtcrrd;eMzQ>XW0w8wk{E{x$OnX{x$vX~q*Un!v2|Bvo*WN&@Ll{8aLf*4)!-y*i^dCB~FRb9s5)-IG`;6-o6C1>aI)o~N-KjTTu%6F*Za za>p=;7@x^c$x0DZaUynxAz=MdEhVn-uY?DQ1fz5>Hu@-fM8{-I>!H|70(XD$wKb&$ ztJFffPO@e3$7%)^P2YB=)PPe+`^8S>akP6vYrYX018ezDvgk*@5a>ifJn2)Y=mzVK zjIlf;kPPev113ttRD-^BKj?c^cYa(T_^Gt<5$2dXG--A} z<4If&35=^Sj|=BV*Ektl<&*PbU8B>;8c&VXyK6X0J+ta$9&0f6+@hMMK_r7?loR}; zz34!l_uyo!_NUOv6!54=j0gKiSyK*a+Jet`fEcYx&k z@|*>87TcxX77V6L6fk#r$Ht<&dj6~F{>Dbj!J5!ki`XQw|5AIm}IwY5y*>&tgWg~OKJ^@5(F5}G1pud37HFX_K=ucx( z`d@%F7`mz2FTHC?5*Tp3Lg9CJ>5<2Pr|+|)@R0Me_uWHqDW$;_;@J+-Xd z#I!C_+(-L-j$&O(&~!n&!5F*`>ZHnZ*m7;8(bX$y*)DF}{Z3zn+SIR@Vl(imo2S-o zC_)pap%df75C95>m}9TjB;z4cV;qSH!WIz{MslK>OW_9Zdz%?=U6Ew&UL*ZcyD_6> z9kcnIlF2c;(q5Uk$S*n-h08Ib>7%Gq8uoCYsRV|}7@LUie8vp=($-cTEUW@`N5VwQOlKHA+x z(mHLmsZuX0PYDtg%C{|gO1B`Tlb@i#UaEO^mg~$Wu~xg@e4jt+6QBv5m7h~`D9Vyp zOQS{DH@~(T`a_0RbUqN{%02KM6tBDSl&%rcGBIKGq`Z*zP&KZ0bFrRVeg)OPB7iU5!td3;hH7Zwqy6~x5GpaPi&yV)0G#w*6iW=(u;g&e-I6C zCuYMm?&IY$K-f&~BI-QV_Q7S3%1r4^fHIb~eVDy@>P}>rUyW7myYgFjFPSU-KETuv zKu)0y)s8!0a^mo%w;#F`!)Y9ErFQH>_D1a^S)f|_v=$q|4Ff=jMaagQ%KqJC z`{wkzgW>OU!n8XD9^)vy$7&)@IkIQ}{IlWX;Iu8m1IL;B+BuJSR=}!C1A73%@ycEE zwleW!weM^>o28L;0pP$pjyO&TPC(3dH%1PJEo6;1BLG!ciUoU^v0Mi1#FDg6>`Phz z?WO&4+V5fK6thuPH?Gbe23`0&y>~7)tYn+Fr2*VA`Rs5!whO&bh*0~#h8>oWh<^<` z%sgnQskm6ceSZ=->=*)v9q8Y~&ZiuMG2pPX2?ghfasMUBJ}1|hVK7SE%RDbkoHUC) zUC-GG_W1X(vvk?UFrI-Os(WDTKnmgPw0KI9}t+YX3<`G+`6eJASp`s12*1=M(XQD?)Ga4Nm(Jc<%YrnT{*|wMDPJ?^-fl zY-lcyQS3}nr@_P<@NnD)?&U2QFPsxEuU=kxksSzTQ$5Q86pxYX7gFRg4_a6Mf)@hv z>_t*0_PuwSMkyuLi++Z2%jHhfC@2xkiIxaR{##Sh`Lrnc8;GT2;0xz} zmwvMR$w4UiD}IvJhCw<&DZ)d%G6kIygt!t-6pV<?Iqd}X)YAIZ=BN}33w2WM_TBPcfU4~hQuww>rovAJxD!_)+bn}OarLJ0G z_OE>CLoA@zgm=EDbw4_?4l@*b>|M$^x^`5RQL1YpMT>UrYbwkXQ)0?CgyPU);npzL zh|)F7&`};Ul4JAFS*?+H=FY?90q-h)>qlgUYm9;Z{d3@>GafoTDAdD7CyCXimS?QA z%TAW!BCCyZNP7Z~u>Rt&d$}9klqFf|{FO2_PUn+RC=&H$HtNwD2IE~iVHMjc=JG@z z$-uWPcmG12T4$eb8$#WBr-Eu>q9q;FA#b?F6jMjze(sKV%@CpJqOo~DYrrQ^^yFX0l6n4cylp~ zLzT>~QQmL_j8am88(_-AL)&2qn2LuD@r;35wM9UqbU0r_3Bn z=#0`~ybst*NJM+T<_IUbOXo|5Awi2xuB!%yQTu8rJXYlcD#hFdJ^YCBiGLa$Q~dUv z{noVFF6ELisy7nfD?EiEpJmVxliUsaa^83?Y*~2NB_`~3(IPk~|3ps5|KN$u(&GO& zzg9v3UkE?&XE6EaCjFNV`mf^@DopK?f$**H-h5Wgb6n{N1r)SkYFgz(2UDIs#=b^X zT_GL$v-(vq*}KPLUeq2sTkvh}9q!o6se9Ym^6(!Xi^Usg!lzF8Pa%6pboJ(s^zIa) z{j69~8?NR(?M^G3w+jAqI9R^zt3>CL!aqSzow+tICAV6DcTV&9Dtj%jxxE+p8mpSI zI89)xFQY`Dq*#LCO}V2gAyze!DAN=i)#)vW6@=^&;PZ`B{WBJDXxLG|?f$nEo(F;xGXZ1d=4I zMW87fs$N{{-V(K&42^SH9Z@)(e8mj{qT8$g!1*Y!nfy$%uTcX)Jk2)N_tiUuyJ8!| zFp;5t!etJ3ahaewN&OdPQ0`JA>vxyzswH;0k#9RV&Gwu*mI(6jZ$nrU1ueC<-L~4x zS8xd9lCB_dOfRyV*OQ64`e;KgKxXuJm!M;IDI*WDA6zs>csQs(uE|727YmXL1t=Xo zWE3ZCpF4I#acr{HhLm|SHrIW3xb{hlVVDQ9q)cKj02WHuX|dmsg5`em^e(J!tgI@R zs)E+`5S|c`p~WTN4o1g45Kj|R^)|+0tAYtPsk{bfQw_~Y>rEl)<#VdyG6ba&V=sOQ zM8j1sM5IR#lY0UG&&A;Um%P;fO~_#R8zCd{-{iqx1>1q2zx#G6=9hj4iB}32i}zU0 zU+Rgk`cLwpSvI1O?FDc$1P+Ed={J8Y2J^kjU55(;YD`MEe)G<%lwFZ0t-PQj9&7mF z@?Ez8{>oTyk_C0S%)!u6Xg;-a^LF$}h437T%vA&pyg__4LGS3=)h|nK+$t2Tzu7Qp%$#ltPPQ* z$uB9ZSXo_2gvd09oHs-EqZBF%qX8AtBZ06(&=Uf|l{^)>=_ok$GC#N2h?rFX(Qk4& zl_^$1mCCYwrgYFqYD$OjJe8FZtSDGKo1VB}MF}T$L2c(v#4fBwc|h*q(#=luMSl)l zk;IPvV($Zi)kP29y->99n0{f5{jB;B=AIXue4yieYN(yfdh*&`;J^)}xDtGja^Owh zovFuqIMBi-#~HyB5$^S=rsY2U!8-E&y>w)*Lg95p5@M-E8wGJ#W8*$eU66!Rb}#+2 zGzLM9Mu_nR&MZGmxx*gQ>>Nn)V5f?JxqDS-FE_Je^dHngSe|fr73n7+b+DvcSInr) zYYP(JuXA^YHMJKtwXsA6y_SRi+ZZDx3j5^xh>0FKTirR%e?n$;OdlE!SgHP*kiqhI zLdLto5}kT#^A2(rJjqtcHxLmzz6Z>jI6piVqoF{0R=38RaYO%iD%)fakY3L;2Bc%G zgtjt!1M|n3o$toi2--j3Zp6j$XKP473|T#ZYCm*+P1b9l6adJ%6SqMRBc0Aaa-1-!Ium3k zBzbHmFx~s00*+3WW+b&%+#sw5@Sw`cN?mPMXvrEMBLf_6uBm})1XnXi0FXS0(Rq51 zq`;(ZzAF~*nVu2(1seuXmaGN;1SafhJMQR#QMZSDJNgE0-&Xn1+)$MFuCT-!*NnPS z=*P#e(p}Z(I}L&2ey3xM0O=TcaREH1`x5J%KsrV=sbT#Qe6TaIk2W0c0zzzMz{$|o zL2AHts)S)O+mn~*==E_G&CJYRi&@`8wC2TcLa*`6N8yF4J=wKJVW3pi{aLV-*}iAk z_pE8ko|_oTXh@oO&FcQ73W|#kt z9?L?4>G)G#O13mo@a2TJT)W>M_Znxt|J%zm{5m2e;JLR0=7jJUwx4)N+qpRR*{U^R zm!dUc8|*NW-P+>}*8EE4qS2+JY&I_v4E;rKvD++gAw_b>ltSRUPR5F_I8MOGNW5moNKyR4qz{j;;fa=ZoOLOg>FF7m;PkB7|CHDM>W$ z5)^}}0A-J~C%jD#J=D_fanG`a-{L{q-Di04u1lGM5ZOQ^FWGU&G!ov_O3T&M=BKV% zg01iv;vD3NT_ZV`5obIck4`gB&j-P>d z`ByP8BRm8kqi2wk)U}JEB0#hB9yXE;^$CM*jAqWmEhtre3tcJ?(Kpkg_!zCOv5n`q z`T)AQ`{P$dEN_oYYlpAd;e*0rNosXE4?&x(ZWX&xW*C^VviS+~IQ?YD&N71iO!DwC zEa>1Ve}~Yg);W$3KnPL&Glc$q^844t_>azYo#I53sw72UEJlrt5JRjAyxJu3vA}GW zXcaB>baEsE9q#p$|9vo)gUV&&IUc(|POmXH+acg@_W`X8q5vdM2b7uQAFoW@s8DuY zjhc_cBj!$HhsAc?icZuZtOOiW7xiwx^kEWEjo0f(6juP5Okz*9)2AG==DX)*sAsGj z0$H-LnV)i9RObX@;-(iNU;qy#$+n}UwB$V4luYt~(-AzdqA97**{H=Cftn2vkqQ;}l^X2)`Ar;cu&=_Tf_?VO^DQ2}S-Zpz7 z;jGJ~CXW%7uWP9W4=UV%GO0_;ACh5}SxWZnIl*W@GX~Z_j+@$h5HHwS&!hQ?19EnM z-H2&ecn0_|Kduis1SY)|W`BJtM2~IJBb=e!Zb|3rv($gX+>=?`opeBT+iAnd^gwcX zG28}k1uc2(MhU#3hB9f`dpBUqaUyu{UL@5m_k#~dMCNSbn`srgMi(d)ST>h-(So#w z>02av;L+PLo_me&?W_8|ebLOW{$_AIA_lgvor_Kp0rUE*v0iOuVEfuQj%|qSuL_W| zOl4@~c5LXD_^JBCzyD=?zM)~WI&G7QJ$~prc0cC~$1d>LcL+T84fpiz`u%OtW+yfJ zeg?$N0dVX5?@TZjpt}37SN4KGbLghgQ)WengziDS1Dq5Q`66=K z)BOmJ=-kGQrq;ZQHt)A=rmYcBbkkvRvZXXiYMnol{Jq>+ z1=upe^=EjX$%rQfXUBL)O;ghgnC@GH=dKe6HFZZ9_whT(y1QX{X$vs^;zn!5&L&=o zC~F+Zin2SDOEF|}Q&XbJIHLBNzyTnV|Fb?!- zT~qk2LLhbAb1I`nef}l6QOvEIFh%a}!5fd-9)lwYKs4RwL09@L#)Q>H%Z+vh07ql& z5ylh`%zaAYfFZ}^a+%iL3!zbw8Cuqsm<+?+p?50!{zYqRj|`a-kw#+Pas&Xh*NyFD zBUNj-2~VA5^)05-wMDxR$l*n>YBL>}X|aQVnHRM6IL}x{UI? zXOQ}j?-59w%McLYZ4Me^+yXQ!AH0|!4(39oQR(86i)0QCq)6`=HhA?}qRmqhWAm73O_)ZgIR)d&I9yFpVp;b$(koqu<0 z{UA0|XaEJ|fIp`qt)~WUcu|@?YND^eItp>XIHeaidN(I^%LK7*6Hr2q-)ye@aU&=w`>*mZ3R;=6_Pd+LYX~nNSKK<0IF+*#JU{&EIZ0QkAA42Abe+YaWLjQ9CsYMTvyJxZN8@-jzkQXiMZAm-wTeul;hz#E1m62Sf#Aggx91OVL4Vj`M(y6rFnBP=rN4r-yA z@bQ_(k4S^ieikAL5(VL=B-hD>9S$Y-hlFsjc_NoGp&)?M%{E>Z;#cQsE&;4K@W$YV zVWjLW2R8AEOv4bmKDR;}vXZ7%mNBemOnncpY>CV6snZ(Njc%T89FCbz1tganLmi1U z6z*$YYT#Mn6w>KGo_SAYYfg#pcA)2+RlnY@t-Qt3NKM4xuCZ>@Rz9f@Wc^LXU(TS zRt=j?D&Z3gBPt}y9~7Z62|98(4EhHKaQcuaeqD4xq0-Tb1F_9-ECr`_oIxb@EW!l& zX2{mDS2C$cpoIv{ zUEtRC0$DSm5Y!rUM6uMhKVq`KR9b3*OvfFE8LnjMJ5PzUC$1O)v`1p4^%!!aa6}}3 z6r1Ye3VVJWs5C6BXiIlvq#Y-61)7ERdqGQk2q>T4lPp@et}iX~Zcl9ptK*6gmmYW+ zF+8)NI_;K>7s>K}EkeJ`uIg8%9DFr0i=KEUARrL7Ttg_Jdz9npupW1N`Et1m<$4^8 z8Kpap;?%z*NC?7Hgty)KZQ4I2PdHWHK6ap5f_ds;7OUKs#26YrL2Oe5-a4OPno0y; zHaoGMe_=HOdY`*)5h`P9HdNh!=7?qsT1XGSPJ(xc7FW>@3+6bWRUmS~C8| z*_hTcseF&BO|Addt2+;mu0>7SWFcGLP%hRHjYa%y@jla{iK==2Hwn(!`@gAAx;lT4 zy>G7X8Q1^t;QS|9Bl=ICPhu)iDS4fINev{431F7k#N~aX2EKJ9h~`PqQk=2?#V1xg zf`{sa7i6ZwK$_UQcYQxJ(G2=f*B?Zm)AXZ@f4z5f8;3+E#+QlYANwxcC2e2Y>@7Hh zMj1}vUmz^sdVa3teu;NTo+iH(G!BeS-dn2hU{Pr~ixj(yo<8A}HMg6F2X&_{fJ$1) zC}`S@`#N?n8=C9V82`>SWM>8@w&!?ye@}<|0X+>7;et;86fOrRSCGBVO{KsJeE9%J z7tY-P&#_`Mq4}K?Z2B{8AWww+_e3P-(4Z1F30`#e=a%Ap*{DT2Hfp5>-6+d%BftSe zHdP)@pSddd2rfO>X#Vw#!em~CRfdmxkXun!2=0gqNEXwW?|B+f*rs(lT}H)>tmhBPd;{tyNVnsI|TB2qX-cxG=16u~1%gP@8153Z!a(7q_SqpugCqWtb-<;GG4;2a%=yC2mGlNN zJH=b|XB7Ni2tLv=Z zQ&#p0!q*RQTYR~;wj^dpp4@Df1#nAKKg(7lk;A@~;w%=T4_leNTXRlK-xd#5F&ls4 zv0L;KtCvC{OVC_#8T-M>%*@rvK%_EC-YI+zB?iqWH2wMtN}MpEL?R>fWgUlEraOnQ zA$hidU-Fq)_P;!vS`2&)rlt|kN<;M;SjW-vjdTn^WmN4szV*}Fs9u%Q zGjhRk9D7$3eK5aIKD%ZYv4q^Z#)EzgV0r0LehkLA#$oW}Q#5ME$uObVrDprYOq>Qf z`8p2O9@M03YWgiEIR_>g=T54XK=PE=hw}^Bt4WM=vdQD$Bj-ZKoo>j1zlPsGmRHt4 zc?Lk&tUq$1-mTYu6`)yOJmt-T|B{}%2TMRSiiV!G$Ry`WH%y=Ebe$bJ%{aG?di&P) zE1djYt#CoFKwXJ&W4Nbxn&W-iXr8j>-}VMp8AL7oT5J#5g>YPWIw0W8JMW{#+JU)R z|MbjUuUqbQdnBjNAy_86zT)NL=GjpqdL{VsP1AZtZ2ogx`H9C6bS*k{rFP;ky5EqK zz9>|fR)ia%UhfP#kZ8%cIBu(UddF$)}6xo zc9FCzt-0+uDguHqxSlds0IO~7{mC7nF{NcIB4|GhLW1nJD;6)eKe>y==KKIRVJHT(KZ{3Mew> zPXUUIi63vmm@RV2F!mSYN2P`uJ6mKND3(C}C1 z1g7II^J2w>alquc8^=&KheFQi=bVctKPBm&`-_?uZ#b4qIh!SnqRP-t&VCyR$?w?I|Ic|4mZ7u#9bKOPjIKXdk-yNT%vZNw zSAdp#|Dt#VvRG#66Bvg`91gwM!^YZ{1~;7YNz*-Zp%cJwdR`#Q!pQfO7v+w=2y-m# zo7U)lmIVZtyP5yn!z)S$z8ltMKd5$NZgplgP13g0ubabykA3GQ%s{T?iDSF{nLA{8 zo)sQQ{gKz1V=tBHrQ%B^5M1Q*xyU&uU~SAvQ#Z*gFmmEguaHzpIELKBDkwgAeQ}w4 z>~SU%hu9EPa6eKL+*lF}eFc96zzN3>+v$)&3?I_w&kQj#qOcn<T$G%ZXCkcChNFYCCnh$BA7 zVk`3p*U46>sduD^L_TFo`sp!}|MH}Xdhum3Z)0z(JloK=+%m-0`eGvqLzw)sA-NYh zXUaocaNKl$9}GymY(Q-}Y7F(71_yExaQsf2oUo}zy2OewCPo5)Tm*Qs2U{AsGk={y z(V&GO14_JlI%38O-+ox3jk~p`L(=`piM9JS8f|<8VfG&Sn?#Tx!FKgP^j5-0FR>ii zlz3Uyv8vbTEHaH>nTdpK4v9ADYFgq6`j$lxh*A{o6xC$WuK}C=hghfCTN@nIxtuVG zHB_Ou_#4nW>huq;+l-#-&&Dbw@Z-+1r8TT*jp9h7$t5cOxh^3m7g-ZBQ4`xq)Bzar zx_zG(NUJq+%LP+J^E)p8hOQnufg(~Mx?KJlU4L>BfTN+@k^&-cDvyi~Xh31W^~<*z zNv)`eCMQ&?2!YZh^l-vb`lbd)`p$UkyrWF12b!jjC8`8Rek#AKP`*jF40O5oZ87g< zVJ-OsIa1ID7~F8diERZU#Xm!i8>E~#mwm3ZK#lKb4zC!NFB{fsK5_YOP&SLMzi7F* zHEzxTsV#0Zl}EQw+zRF9oB?NW`KWEx8J!E~9dCrXWJWgH#Yio7%-cl=toc||@qiH8 z?#tS|6R5#TE@M^UnA`zV3c;{W7HwucH_lwMb#%B8o6#FxRdb2-9b?d8jmdby9G((o zcGBDhwM8`9E;&paXD2#RmNTia&Mb@{xdW9fx%q}coeJ-ism1R&_i#A7Zu&#^|4AOR;KP0&Zo&Fb7=^ehCJhsySw)-oF6YJO2PY_c`Pj<(q zq`Di`$(> z2~=~G+`8Xjf6sRh`vkeL^^?1oAUp0*|6ZfiH8}DavY_v@@RCXH`oTeOlxc*`m11F~ zV8TdMdH***TIXIR4tyZC#Qzyvf0-owxyfc`{=pHq>@L%hazrks0UiKEBS4blgpgQ( zn=JXlABM%&`Om7JmcXrJoX`)NF*6^&QNr_l%)@7QB;ELFZVj06kmT4A$9uvp1sEzB zuKPin8nlvX3L2IB`xpqVi-tAl)!L`;dDOBX1Zp=6I}yr}+d)=8^h#94<|P_)rlFtZ zNDI!BucSPD&iRnzF>|fZLPltR1b_((G2=)u{?vtCn3D*J<_vx>UpjRZ&v11sS)gh# zuvX}H9YzwWx@)SeAq~15Rh1#GD3T@;5nRr~A$tZJ2*aV zfrYUvIpeCNR27Sn*Yve-R&-Y!VJ70ZO~TL>FEJQ^@-=Uo8O;f@AHaeY2M;7qK{*O& zlLFU}apa=NHl!wRsdV9ZMHzI4s*B_VOdGB;MWoCI$tnSLDRt!b15$OUf=qryO_{uQ z<0In(kURQ|bsr8P=WQ_{s<^zv`e*c8cIT4ClVtDTzOl@l(<&EnHXFyAmRgj%R9&My zz#eChL&-8AW?(A!Uie_DSIRSfMEUWHm9m!SLA{G+z%zOehopAG zeO!<``4;L~IPMPgYbReg= znkW*=AI`%|TGS=Gi6&N(KrChHz(S8UBO86*>$Rd}q?Mzz)G+`2FLK^K1YdX4L;Le@ z9PAr%y93HCHsJ-CzNnD)AI9)^795K{j0ZzlDMeyt^DO548ACRM0!ZvG`R8dVOE66? znmCZei3HgjZ&SBVpm&YUOPo5&RIe!4jSaS!Kt@wPOIhjlDw_u%-qBF>N zm!2H%>*^zJJw)W)T7`bWR5zgMLa>b|51eS2Ej9%#MD(ksr5CVj>C02r09{=c4ms7@ zloA5}s9I=qRBHQ!y~!i?e^jl^H55&1G5`L>&_%4%tgY7r1>#-G=+$V>&Et>wivBkV z{N+6P#}RJ`R>BDKu(DGNn3>DDQX|O~vwq)wLPn>l@*cqQ(iv5;$3q-HYNIkwR7ofY z5#uC)!2X@;`4aN7MGXYl|1r-0x#}kWy=?tU+EQ|;ER>I^2tX|_qrL#J#bnxjX49&O zzf|9O_Y>o}q6qUB7$LWmrbpQ6Zg7alpoL<%7}F}!cIZ+noACJ~bBCS=-!HUFu*k~~S(vxDmNLW>WNYV=0~L~`!c zmnACVgXS+r`!Kh({H)^9l=aeth~UFjfu<0$)(DMhp30&iFOEH*^S@*Gc1bb0SP(OP zK?2XlEYK1)3yH7GFYz4GnOJ1u@~xGd-cnWv8EP1v@TZE6f`+J_x~~~LnTVpES964P zN0T8IMYC9nPAEi+G7ibz)1ffQ4<~kzF1MZjs9i4n+sgdrEqX%GG+sHbllPFLLM^gx z^mAz~BrF!!uE?w>Bg^t7Cd3j;WXKkY@Tq=CqT%ikUd}9iJJG=~JtLqVH6yT}v~F9i zBpK9nZvB@0j#jT~6uv6#;1}+d>wTM%m#q9H&(GTpRBZivH#~d*{zC5?X5w^4vMc*F zob&GpmlhYer5ieP_ffdpW$NrrntsOCgi+tMYevBWmq0-=+IU2iPZM&?@&w7B9^nn* zzI#?n0Z?<=WNxA6m>Wsp%wrgD!*v)@Eo7(U43|4(39G^2yOpb(Ekdso)vO3Mz;eZ1 z0v(QN`s%A)W$=WxhfVp68)6o?PTPm^#E8M}L_ild@$(o~L5)xtc8Kwjx0pPCrkvyMe+L-- zayJfkgNE@cR{n0!Sl#;@FMj&wZ|~4rsUM!vfw-6X=YjA~_Dk8Ht@~f6jY6WGC+2q- zB$ZAwVCD@HG?fpPD%mGowlOwGlUWN}k{$hI*gvcqMZ-XiW+k=BE|4^vnRBx5c1M4{ z9G%q@d~myn%XMOLBt{aG6Nhh+aAYmx^)(A*>}iF58^QZJ9Dil@8Od(uP|>Nr?2agw zLlE_A9RgQb>vW=X&lTZ0cxss@Wem@nBj`b&6f_p+ONmQAb(|n>Tcjwh8R?@TC0#13 z;b+XIFX6b|ed$x;T#1Ix1wgVw2wgeEuq&obbA7^UX;BZY4xm>-HdFp2JRCkmXDLzd zSASg9Q7F39JS|})Q+RZrp2WPW7Km~r@T#$-m$fA$QlCt(xRr91n2-ufxe22jq)nQ%Kjec8F|d=ET- zTqxF96^!Y^uX4&)zCa}@D$OPTyd~Yt+w@$o!aih`ljxi@OIuty0U!sr6?u{alSzZ2 z0M%-3&yZ4xr;x42;PZk0;KcW*f_m?5P7|yJn=LSH{Q*tkXg6zg5%AQ3)uQBSq%HOn z7Pg_O_4>~PpfGESKEUjs^+QJ`0cI6*3TKED-EMfHv#hG40oTj1g|~IV?;Z56Q2x&J zj;X{MG;0>GJtq|n-YuleUx}yFluZ@nkHJ|6uvJ{}WG{!Dl_n$^tNwn~=;MkVhF&>+<=Nvz2z`Ed0!lLAe?doY85)>a1!m_L$q*+@C#zT3 zA{sK|M%WXF0r0^Hyn%sL*>Qv3+HV_%KOwU(xL>fr?Std9Pd?@9;no@={j^yYOIB!J ztjl49zEWnh;Q$<_Obywf4Eg;y+Cb;QKpUw};#8!{#acD_3O~VUILW|ei}n-Qtl}e^ zT}}OwFxx3&R#5%i_1OmfX`{s^pZ3LF|0;fy#X8cNn$MR_tFBUh--%h4Z9{xm zdMOQ)gO&R*c6xxLzKCu-Y(m?zGUrY9 zsW{O2b6p@K@jh(5bxw=CIbK3JeUr@o#}|VudBxpz<}2&;<}WmkWxp5{p2lRMX|h@w z!#HOe`Syy}hb(%sbju8XK~jCtna(FrPPSIdIl<=B=Zo0k#IEwY;p_kL z;s3ml+A95jA6~0=w)LspGT4+bC^|yMq>FTkj}n}Jb=JC-Y}FtE=1Kigw6k-5?}hS$ zl+LcukAvZE+cW3NVPLI!Y3&2sB%zADmmcv}&r%oipc%;T~F{iNN)3L zted=BLgDy&YuVebUvcwjCpRBLi?Azs=8~)snLMi@VpNjdcbPPg`CEEXkm0M~ZBgIv{HDBfg!|Lo6JT^Zb76Oa1?ov4MjsFed#%^QsNS-0Il zAw03tcf5ga79?`tf}OipO>MF6aMy25TBI*w-cN+b;&_ckO#2us&|Mk_r|jujA^l)` zKW3R0;YDzVG0!};p}`XUBZwitrt1$cd+LN+mpBH6)7ZgiYyeXZ(?o)gsKq(6lgv14 zo`@cp;rf^#F-pa8<~%%u*i#X(Ydh<6#THw8d6?fyMpfN3Yz>R+2Je#$S=nB{xkVQY zXW<<|b3@EBsURI`;sp)={xaG)X2@I~S4PnMxAq~lWbyPa<|O^+AO0_I^n}Uxp{cLE z#+9ZFye~^x5E0E0C!CdtFD+hFh%g@2;|uM@g`G~HZj-hxH_980PXN$$@m(VP%UYp{ zciCA^zWpj&-~RDw{O;?Ax=G?0`Vc$Xy!jP)Z|YB?_pVzf+$bXZ9>N!*4Q&&kclqo| zFAXPE_Xd0IZ5aE4-j}$(w=2x7Ml_6%GpE0xlb>6lgA7RV$%g>JOUV@|WQLXzy#P_l zGs?2fPr36JZg|T<#(4nGx(EhUkz((fwt$cOft3pbTWsbR$feV|D0k_Xrw!qdV8eT* zG|yEsxu5WOdPs0wdCZFYHULh&V({{5LKmvrL{0_^FKk8HpHdWR6+4*gR$<3Hjeo;F zcAZ54N#qRU2?83|Nhv^keEAT~elAr|x=qJ{T3_s_?lvBU5*Piu7eXa32hy$K^{jQD z39XQSg|EZg(*zh?@+)n3V~oi&G&pgh>?s{Vz^*k=8U*DrTN)vujWIkVCCXVwlT4d; zpBYx>{N`79YH*F-!TG7T;x$8Y#uS^c)vcWKyjQ2UU|N4OVw-t02>8AD82reZqrc|aO<5JGVu~je!WeepH#aW8`dY4Az{7Y6A4j z?$ANa=u#ATpVkvhY`af)Up)YA*JgTGuHPpS>dM`}B$sDjNITs^4?Vv}9>w zgP6M4IFDsL>}H+m-0rP0tbN=DilbK-MxlTqaFKSs$n@?An>7m_a^S`qlxLT|IaQ6y z!!19LGgJ+epxc=I$Mkp*3mu!*w!Y_3Y1Jl>jAR^Og9i~U9tett5s|hQ19v2DSZ=a- zxFA0t6~OzB6h;qiz3wF{V|3B@h)9I zGB||pU8sxL>I`CU@@EjtnTb%p5_o1XS5KxdlTfHSc{-*hA&UA%7g-zN$HnGd?cd;9 zF*u4FBwK8!OTwEl8uB;TPDLDGx8MTGzDIDN1xwR*$K}j7Ei1q$Hc;5_Z|9o9gwv2c zJ4oh}(#y&=OQ`pv3O5KXlGke&_U9K9Me2ZI$tcUeR4ZP5X*kgCopu@(`y3QHV;aeq zA+46Xaqts1eT`A(F!kVp>9kV=ka3xmQ8_bDZUnj`mb{d?TL5*JGuWQ|Fh?6vIW|IzsQ87r2Uy(0l6lRP$YvWJ$&(0Ux>@P zNe?jR96`@n4nihHzeSQJQE5?a)pe5q_QBN>zcM;Sg-;yWuUL0>%^$E`ynq^6+B3UF zJ@LnJiurB%#`!+q(X6z^Clpqsc_#C9F6Z`ZAWJ4J@yP%KDZHc9$0^IYI(UNTB=`ig zproA`zeAG2%BG9S43da46^(rVHnM@0Z4LD-_%)*Wn7uDAGD#>mTPTNTAisMsN!(%M z{3mwC2{{uL{f;es%D)#jkxckpVliO4;@LD2l+q9td!asU4ug6hi>uFrwx@aZ3$ zA=_U^=Rd;$fx8r=U1mtaC48{LG3sy8rjY2jnLX^^z5gKyG zV}RjkdQQ_pq3~46)=0(h)^rLx-Fz_;S#+h}4lfh!o(3jc)`GdCxiVa&ax(epLPLNm zEUbqB$(gusr^1N{FFx*-#-$x0pGBVckU|Sqwwq{{snvAlMG>sUgTy0|dS2S*-B|Kf z!`mGr=FdC})VrPl8P`@AZ^5L234gj7!b9fyP(#YimoF6I5|y%fXq^UAbMzD6Aq!VX zB6u#Ne5xE{hA?2zE9GW$NUH*=ANqeuaVh)ArIbe~G~MIDMSM8oK8}qZ1-jlB03E_? zoe~)L_t8%kZW5*4vKwV5^>68Bl)>d`nrC+G$Q?tOGJY)2KZif*U!2oY)bNNG#w(&# z#y?tnh^>Ar{384#BR){>cxLNw&*hJBCcX7QoN)XzPW}?EEB)0sQRad_5YhPxQ%MPQ zmBlqdi*8Ozy!TBaS5C$t=6GZ2Eoh~;2s;6*g3|Z%o^}6st4;b-V8l3pLCAR_Dj`Dj4?+HSIeG`77w>OFp=|~5!N~ffW z38Cwq{_!_;xgj23t~T^a+Oubt`9;A`3dOKfIsTag|E6Fp{xKbf*sw3d z`-v9cTK`r>%$MN7FsqpIo^X_~jgW&vk@QB9GC_pgV|1yMP~-V1iSz#D;W~>u6}TJO ztr)%1u#>f{AIk{Pe(Q9N&PyTwEj)J2N8s0QL{@%tX5qKm)*E!h9Wc8sOdsclP&H)i zUl4->Nqa+?x@hXhR#p3gB5!wo>@_6a+93^og!LmTRWrLAVh+Y;tb)U4H@W)8+5f|T zmW6d?SL^pFN#LJ9_n-G%V5~53OhV*M>|tx5Crv6$y@p9z6znRH@P!tNP>3%r1QO5> zkGN>x&$+chJ^GWWb%(Qs6LDcz`a`)un$U zJ4(tTWlJ}L0+$>3ir(q!O(xWz8Sm@>`>Asodh7ENnToFcy1~atHn^DuAvf$ofLUWw zzsZ)1kVWH;Wa|MS`vB1wR(3asvmv&b?R4YgSC0s4{SSsEI&vCcskEdxHD)8wcy{Ex z2dieB1{Fj=bA^yZE+y;%y?U6#v5DgWDx!rC{3n~o2(GW6aeIj~Df{44@x12V&^!5L z$gL>vx^?bsd7=_nqQnVI+%j-cvfLeMf9sJ>sT6wj&cY$m`Z1{L`PBG$jScKc;6~rF zv$TCKHVAe%&*(uubF*xa(Z*me8vL%2hmOzedXnJ) z8;kjZ@(RH4yt)C#O7l=HTdrvh+t~Z`)(qp^j$qwd1=d{fiGQR2Ep%k z54F!e38Mr;)hH||X5dI(MHLSc%wY>&`h^2sgKBg*mE;u7!2*&;6|^fp(Fxqq6^;nFLMQp+c9{ufwPgsfciV3eowt z<2iM%m8p=;kJ=s?BEwHRO4VR$H{4+shEB_h{H4=JjsG#8!qS6i-`pbJjiZL7N3zRT zt5KM>-Js+WkAZ=B)L-Q=N-9{8>s9!gRT529PnYB^ESfS|wpBE~)^b)`IX+7BA!4eR zc7Y~kL}lX*RarTQv@~9q5E65Alio^E9Qk@jBP@;c^>3xb?TcUMIF9t&<=ny9| z)2$C%99Yb*c^|iRfwg4KA7ox6wbF^>3Rp{C387U|!nPY;oTed=^rFo+BhQ7AWM2?E zW#p<~!y-eqy@Q<3xgfXTvp0L)UXidY%vQ)Rk-5l<&LEtBX0qnFXFoqABKb<<$kJTY@S5BS zf`4(GLsk`tgG+E#>C%^VIRd#yEsNi60LH^nm5Y^UbPLS`7yi@(9#3pk2&qMR zoaFDg7~SZ4WdYV017MB$??N&*pjq2rRR)-&Xg_6u>i0<>PeVK#3g>ExE7#^Ij+23q zS)#57EZGF`)WxNyG(43;*39<$HQmOl_zEfj z&Hz0u4!Xk5t6$NqJM0dBC$qAkHf*H{f^@yx!NKaXeK zW03Hx5bh6PZs-@BtRP%OjqEw!A4=KR<^vv-R{7@^Jo_mC3Z7<^t{-v7Z&f;~aTlo8 zRK*^B9=xYt=43QrcUX*(mptP(XXnz@j6EzOc;UQ5Rv2}L_ZQ8yaRDyVP9yGl2)fhR z8r5^%V{+v}iI8e*jL-4IUgaGo5j6xvM6RE48+o1AU?%Rea(J(4i{56hV?#s19-_C> z?7G=_#;-kGkX9iCg+vs4sz0Q(@VyFte>QOlZO#12%Auq&rFDSJY5Rgq10>(Rj1)q- z@YT>RM0+#@lfXn3sHM7dO-4olzycn-wuzMqzv`*C<|L>xDj_8tVbtP z-`54%}m5P-8E=HyuIf<{XhA%(!YAzn4Xpz><ZzRaNXZ`5P8#`7@c2ad^G#FRHLddW^ zoly3U-ANe=M(yeHD*Lp~Cqd8kSW7GHZ{Xb4XQPhJyM<}oT0i@8U2ivh;Z|vzb-Uha zs;xLeh63Y?MI;sP=bkTFi;SOlDNVY&^Ue4t#|*k#K_aepWKr8sLXv(_ov42XCC0O8zoOaaZGhfAr)38aye9ayA zu!(2NTyv$yQMTpI;tsUxcleoEatr#EG$z6A{F}XeTBZHmL26Mv>ghdrxd!aSpx%KHK?FK4@)&#(MSm9oFu?^09VqB$W6%S zg?2GkqX&U5Xj0AvXTO!56`-WCSeZ1Ib|q1$_mMRRjXmLY zCkaD_hF=aDXb4t{3LDh)wpA&q;s6Qp>0Xj^t+WqkStk|y;O4lGnQ@q*&1FuO9Xg%7 z^^!R95Vp%?W7t{00(ZUS(7*yOA-a0TD#1%aXq4^uXCIVhl&d=H8B`t9!aQZMGC6Ye zktj-VoGoiY(J&CU@B>hLsLLa=kaM&a9v%(4GoNVIB~C};pYH$Q;qhUCAlBIV?G{Jj11 zfy+>_eY~0B$7D}fi;|APm28he{k{>UcN4K+)Fli=XKtFsSZYF9%V4WjwPbSp3M0Sw z`Tdi}ZKPjw;?Kj`&-gqK8^?fu1p@xQ4^IuD$8f&F`SyIVktot3T&z^xqiC*8VLXv| zVnV}lvs+n=Q#`Bk-R)ZYTM@hEkbLc3C+sJuTD|g`ERDHH1SJkHoVbTWG*qLvKYXE# zk*ed3t@+}ONc)4|a6^MhvKlSPJ-R3RdKgMl4jx_>ATB>cPMQUwFdTmc|V1wQ$vJF@~$(B?R%Cbnlr`0W(3WeakA4q}da(8`b z@d}B!#o`jc!0IID;^RG?W9s^m><~frfp|yGH;0euk_gRRLYv6Q5#mk0-diY0 zs&W~!T4cQ%?dJ6%Lp-dMjgOSU(KczeLGb{CuB_gwN?vW~JL`1ViZ*NG4_KW7j0=;5 zWQ`U3F*F70hyuHGp-5v${tvb~5F>_sLz($&^*(_w;W>_%e;oxfH~KpMxF=WU-)pa5 z;`}|E9Q?=O$zLd|BhI5zuY$1`c(a+>d}b+9RM6v#C>SXGY$R~?tSkVFmt}Gbefd<1 z{rFe)^*xfdEZo*>EZ%v{Gqa$e9?<5^(`8mG$VOKiR`fB&p9PA-lDw*sEmsD-Py*#xyaVyWoA^izB+quNRK5*nDf;M znFvy8hAg=ip8j&rJY?jg^aISr4R4|YBOL4yFSesvHkNQPgPU|i9%;}#AG^^T1LJ4a z?1m#>Gm3OOeUZgVPh6Ty2MnSsGP&1^-uJ%Rn$@s&Sbmp-+*H?YADVe^X(zz|@tH?l zsPB3ng&*}YZ^(?Am|5D{Tc5dN&8Xkw)9@?02e1K*tvw4;^I;Tqtv;-tKdPtDQ6xSw zG79|l9*IL&qP;{c{tD6j$eV0oTFmbBr%`;K_ZZ5z?;SGSha$9fY1urhy+ZIedvWbj zw;E1^RS@W^RMF-ehCaqfj-$%R z@Cm!6wW%&&zd9h}8PR$g7+WAh_QQ5|ORKnLL=-=mP*0}urRk%GZNCO*V%TErPOT@x zxWnOLJCa9Z4&uFQU>Zj!)x%0ar=zQC=psj3wGoKbSTa72G?go(EJ^$!DE4%Sk0<=+ zr31Wm*lC(n(Y|9Qzw4cd4$=^D@?iijcmBqlZzU=MKX7C!NaEHxqw)u#;bJRjeQ zvD&>MCw{VY!|qSHu)d3z%LEi#9fetIiIsj)^S!W#ek-a7(VmaR*5S&l|0%Zay6Fvg ze-&H(0+UBHEnK@VJUpnx9vq5Pn1J&uk%}gzAf^wz_p^UfpIQt_*8Ta1Dy+v=u1y?5)05d5if@G z7$zMTfynS!$!jG*)oDfZ(wpoASfiuIfkp?-9CKtrtcPosat)k@QgGfq=}&_pOA(6z zbAoO3_ZiWKmAJ!utB&$ezQdRZM6?>7$WuwIJnitphhf{jdTiwu_`nmw1TFc7!5N@E z@Efi>P(*p{VQRFqYIZ&wo7O_NZsGeNy^>OSg*dW$a+k+c?hI8%T2}p?8k#ae*y|Z=2~zBR?B+oGwTr(xn#mZU1AEVos^@&Jy)4+Lj7ddH^38nsc#84sLc7aDGYAcW#p_0VW!&X$&f{^Z zUT$6NqOBiHhb^*j%GQhbSh4HaPRuFqa8JkU1AvG^d&fDfj;^yN*2?>*6V|2k* zY&fnx??!!~cF1yHs?yk$3=o~#hehALg^kH*V`DJi09A~1}d zBcrc{7li?eZm8gnRR>GD#2k{$cL*O*E&a@_lrnE$nF4|@g&dc1MLfJyDsy<64>94G z(qQn@w+MFO9efy7|TQe2Zzp z7f=&^pJaLnYDYjV1F&)n4iG)WbGY|a!ZQa z^%WgQ^U)bUJ;Bxf9% z7=m8HAr*suA&g%IJlCy4mWafDERRdD6sN(+4jT|x>d&EnsEz%Ql!Dc4khlNpAy}|o{F#Zj+^K$;>9Hd7IQJ9L?RYWABoJUXG!K&;?91MwEW%&C3 zOad>_fj%52k%M71UyvSRs#J>Updf~K;rW{Ei9k1!8SdxQ4e&(cn?AY|&5a$mnRo+I zBir2wYHhl$^ex@U`%3bV>7$wNv4jn7^Um6<;{6=Zj_b z5#uZB+fgdUO+qw#MX-ZKg+mP4 zMjgbi!An>3rkSXGg3^bI?PL$%84Okig4yqK^P*~1aR?$6%}YUyvz}gZcW~2YiO8*^ zOxBM3TMKB~xYf6~V@$o4p>!!TD@PGUn$%%(T3d#Wa+Pa*B~MEtoY|%bES-Eb|)T`YFDtnNsNXFh)K}pp5Zpcm?8agrJ zo~9IIKBLUJ!ANMMC`MMq^LwRU=c6YXXHs~F{8w!g&ZBp8!1cuq{5LNO5ip}dr^Zn_ zttO6ma_p}xdPk!wGeS_e>q4iC)La?u6@96MC-v1+l)ZaQtF<{ zanf%XJ=MF9-hWq;CN4}u3j0+O#4yrnBb_h!ip~rV_o*X;BEtwAV+l70Ay+nHsc4pR zrn3}@r&Z%Ky~Hgu80D2P3>=+wRj{-u+iuhr1UW#OHWfX#ti%Mjp6zW;HhE|o`X~5q znW}gRB@y%sZgk7h%h;w~&JnH;{IlV1BWtlC4C*|MM0b}Em}#;tE{(6rjMIc%jD-`d&Hab`xwo_~pUWbbXmQ zn)i-EsNDOxsI=@6MN3`kWCRv?QATz-L$8 zEIvu5qF!ab0p&jY6LEoq+3^8GhLvwidwGT0sN(RR+C*(8{ zn*Er#40<;4F$acb3H4pPBo1UwrbL>QT{%yY3|MOgG!s=Mdd2I4CV{CQ*{RU%j??=Ik+C?V8w)Zs*|B*`~z4y2ywN#Ihz7#?dP7HL-^Q^NLia>!)|Q`6>CQOo&!c?v-7cUCwQp z&Z9Rcr(+kyPgXAllll0ODXmwlBN)m8IgcOQ+KA`O~Iot=~c3+_xO;& zK-p(Bt4fMrRqI2hCP^Rcnir#lfG=&Nf|khP2Or>XLfHVaLu2lcY4}FhLc#}HTf~w2 zfwmpPg4ZTzHEN#M-vFJ$GQ$)N@rD@QAfQ7*V&$hO&zz@57buX>`7Eq>KB8UQ$CG<} zGHJ!rUP;+%s-@vmY0A;DhIlkITc_df58-q`6YJt6p>7^tVd2>z zJ7rh4Y$h30845M2?@s5MOji?zSHqtBLDJBM_=cm<_KAfiLrI6Z?vgw>2ybV-pRy7^ ztJt;?OsHhwZI5mU!ZhQ<=zoHaq8xXuOCsX2LITHz8E_eEMKIn1^ZjWCx|OZ@kB68| z7qb@exo8bdi&v}SXy)@wYuFTJpG^epUk$~k3OsOz@`R!mA&rr&uGVpP8;&n02v{_^N*778pw?A7|@!&KmmP_HRUr zwl9x}mDGx3m#eIJiP%vqJa@C14cAMwdo|=9Yhjzhe`BNG>O+s0^;80d=j)NAhREOB zCM6%@&iMevcK|OV+5e&VKfXcrA94q%=P+iw!`^5+?!95nzsx}#NY^oDOMrxQM=#Rk z${fhf$Nnh)0Uh^x_4$wIxa~M&t@J7jGP>2?ndzrzJPKp22NwpwJ;uUio~}Wua!gG> z#bQre@DJG|-9_)5#Ly&|-tpHzhhJ8&C)?GZl`U5D27LdJma3NPh@4t12$VM( z44b|V64E&N(?)1aOX_Xk@UH@)H>!&is?=0tA8S@AiC(8AX-<^=*v7~soB6;Y6FcaE zgoXNch{Xm&UQzTG?vqPzSRd?jR1EiigILdUtnwdc!a$Kn8C_$#K7c0@R?%52n(rWT zN9_hla%vLSs~JB>F>6p4ruP#+HePotjDrUQaWD3+XeIqBfv<<47VfEs`0J!(+7&C5 zde+-TP%c5QB+kpOzjxXnF}(Zm$a3TY+9UaYYVWu5{%Llh0L?CZZmQLE-dcGY=LE(S zsu;?*uh1}S;J-crwdR_b)?goWe$I>Li%^WPFNQ<25ZTDqh7H_}W9-Y+M(LK;i z)|-61>~p(MD&=naYT!sZ3rAhi^UkE0yCfxHVABx+x-ulM=sjbs6pg6m`xvTmkh@ZP zf);|R8rH%aVa=32Qlqe#tT85lZwt?i>er;A7(>!IKuffMMOD5p+9;YYEyoh0sIp2l zx$=f-#mV3-XeWpDT#zRZQG=y#pd7~|)yIs5IvkQ*efnJe?Xo&qBQ#pV_FU`~q6Y&x zT*BCL^4^IS2IppGcZ7`jr|{BGtbCk3KSKDsUa|dRIe-R(?2yxN{UghP%zw29NRGN_ zN2KHk=f2wLUA{T_cpfY2#uFyJ4SqhgehLMR4x z$h_wgFs>xG7Cl$=%P0~Y;>e?znGx!3-FmjC=odBgrT)emNM`z{HarnPWC8o36&g)#D%d+;_P$$2;8Q5CeCSrs78{#x zlxBT@m(CwZpO+wz>%yuAHkS&aeYT9ptygbhkkwEf9oypPjeLolh=yNP(JUh1V*%<2Pw1t|hwWrTW&zvEXMF+TqohL4c!o05>$weZnIdpzG*xqK~q%7Z8q>E5q zbqDew7)8QKrd?#T<`q!ImdeC=){4O!gtBe-eFvY zJofX({x-ahSn?2T7u%z86uss(9@}FwQ=PJ-7F?fg?iCu925A6o%r{3_$h7YZigC>C ze!dZ#h~jAe$_9C`|7O z_et9igZJwNZ_;!b*{oPpOsmYVpz4Q55EnR^9gi>y&}pQsgx@=6iIK*}kS0e=5X4@0 z*uN?n#t6JoTt_&dKRE&%FHy!&zJE`=&=}B`Uo1S?SABX${15eEl$`;pbiU|GV$- z(x)<>_TiIRMk09f;=!<~6%+s?82@Al#!jjZdVk(KTGB=cxwT>&He1%oEhJSa)6I5kRV z95(TTJL5$wNeo}DZV*#=H5*jXk8V%|a;7f*A0s;tm7vT{NwydpCAKg?5)v3iU?)>U zfyz7}MI?qS#kU+Q>D(JV$-!yahJ7%gRbx%0z;0SO9KSaneL+!y?&$q_jUA@Tw#hx!I9QVKlOKJR*9?jR%56+ z;3acE(31$BJ8n8;acH>*h*+d(*fVz^f|L6l!CfRk1o0|$<@KL^9^6~;ry4aNa_7+_ zwThz!YgIc1cyI2)9;PLET&8@&(RX63aEg%dK%${AgOTp0Rj2TG+Mt)p#N-nc=FjmJ zISZdlBa0Q?8_a-Tl~E7O~)w=MTvu^%Kz zTkSjWn;4g3^c5F7YnsT714~pz#pKx&tmJ784%B@A<{!Nxw@9x5C+iM~>tCGgpSkne zKl@?+hl>Tvg+Dr3Od@SHgq+wIC{Zi5qUgOzIYL}~0E;#FeDof;z|al%ey}r4le)HlrMj}eS+iGvvVmJYFn5fI-e%quFM4_wblEKCR2IHpn zrRyIzn*q4l4ZzKQzvYa<79EVAX2DjQzSx4qbwCFuF36urOE~h zhrQw51QowcS4#TPgZF4@)2$NLWp#t4UcX0fzS8Z35}N-3#XL))S~^`8hKG$@Sa`Y< zFT%Vz_krQX>G1ZO3L7kD`k$9JGCvxga<{T zxsZofo)YH~A2us95g__&h-NzYxJxiWcE=GH+^d1UyID%F=iHaRbl&y@IjGoSvR-d& zu#H~tQLLd&X!YxTl$OaQb(Zt48AK}k#dVei>SX)C&ym+{Z%w#*U%vW)5IFi!iqP&8 zaQpLZ>_{wk@d1YJ!=NEbv?qr`yk;kI6#cCgTkid4fn~7EGa|$IY4Iu~wC@F<6(v!EY(XHk zWeZezZYN=RPETPbZJ>LxWw*fpevOLaXZP2D*Jv29v{e7S$oh48;U8Q3`(0{P)zLs# z0uI2Ck+ai!3aOl8;|7MkttY5L?V(T|9I#V>j^bv*)9u>Uz7N#Dp{>JKFA4P%6eym0 z)%Aw!bA58ptan>A^V0X5^sCd0Z}v}=e2j5s1(4FL29Ww9OU?1#Iqqf|oJlYdJ&hnu ze5ES?-a+Wg>B_-=PU_1%b{;B|lcqdVY-1Z$t++ zZcmt}Od;pmC$x40ER*R8z+mpY#rE?B3Qi-|c+rJ`{&79#xHoRk4DJY^T9AArPY}01 zmPXEkw#{*DdiCE#=lm3{Z!P@? zVrV;V7K#tgFB&;J$I+L;K{%(`SkEmL(^0!-7;zbdp(Cl{vsYVY)imIT!Ktd><1nyv5++ z$E0`_8AX%}sW~|h+7Q7`hco^x2HKV+v$~BaR^=et^J%&lWaNe!qvvQ{NxT`iS(2_+ zWSlW4%arZIO@dtf-}K8aoWL6Y*vE{HOi!IGRkijiQ)IC8@>Ah> z{>~d|HKeRW{6qG8X)6$UcO4cI6oEc-JJu7aChD^Ro16y{qVYrgujr-&u~CNG1YI2i z&JoZ2KR-m0z`4Et8T-+euan!*k`F0Jwke!K7friJ#Iz6EZ$nBdKhO**35||iSG*=7 zm@TnPXpE`7y|9&nPCKsq^)~fa3+x?4rXsu~OwJEXdPdPXmL%G8Fy_UW2- z^JWSdohFTeIY%D8>LqpPJ!TdSy15fk6M+|rHeGOW^1MIYkAaEI;^r>nDRHjilf5dl zXhv__oyxpivirX(!*dCudlBGgat*wz6#wVX)o*3K-ScY zaV228BJ+BrZ`1py`-i7CW*#ymooVJ8;0ft`b606%ZN1h5Iyok5)hThe*RAIfK^AJY z`Gw3_wPl6nn63tBv;+&McEYO$R3K7eNe7?@j4jT_DMm%gFOvnK36O^`X><(pR7gSZ zLhgo_iT9vjK#hwBOG=_97Pux=^y?K^$5)8<4`+VrmqzcjNKKgng|C$hzHKN7iC^6G zwHhVENKxxzTj1|hw{zQtND=l_yaOx2RA6_psY4P-VVue0dZBTR_||RFjW27>PrQxY z%&n7)$s+sMl=~f-9Ub8JFu2@?DXW(~nTju>TBXkCQawM;hD@`$sxd%3CMwP@?i3go z&-n)cXOb0K^hG4s#L{lbyg401_~gNV5OiWxZeRa1WTkeoe$IT1wR9A`$bUeHVQkMR zQ@@wL$=wu$ME^X9gfvF-?JRUe3;u1uvn#1JCW)*HwJq6|uov%=8Z>4ay6-x%stjhZ zK2w!TiKDEcO+Ot&XNREAqlt@3x14>2pu32C|Tebi*1@G-&_V)+g+lV>-j^CE&q z(rgrnX-uV|S6`@S;)GpkP4i2Vo_X~0JIr?mm5!{+V1QI7yr0H7@eI5TMduE+?L~D@ z??cgH_Rw@xm?E$<5v3fD0~xOjm&n{!7kE3)|yoQ)8R>ii32O*IsOuiZ8@((B@Ptv z?3{@4G*p7NLOhN|k34+WMUPJyu_~_}1@Mqk{Kdp5aL{$dAfGGP&#SvqkgjIM#XjQ9 zfE*Fx^}};s>H!lENc0n`q71C-`Fr8OZQ#*j;0~yf?{{pcQ3R2bVVCzNAsbRbNWu<&qyDGN)4h-xMSy=_@x z*3rrcq=2Z#g0HR<8{=AXJ;;TSJS{vnZ1~{WT{b63!eujhgqZO4q)QEv(Q(t$K+N=+ zwz{7CIY;3@wR%193@J{Y$6UlciW;T^I0zsl zceVB3)9U7B_+~1;gmYWs2#W1%P%|b5T5?LD!48M@pQVX}sk5%$qvQ39_d4|4xK7Qx z_&^Gn|0@MdxRDAXAqRZ2(GvnszS}9vD}!#r?^C!ODYR@_txf^W*VnLzJ4u>%okcKo zE!f;AoJ6Z=sARxBJqrd$i0Vp$fz>9c!}nt+#-RJUz;=QRAJ6mf`!CA*+6dD#ct$~$ zbTysB6ZE_IGf1sCjn6b_#p<#g#a~N#ZO{*HsnnZb(H8%x?^FF4zg|r?nls4SgSA!J zBe{h*6jHY7fbsS=(m#x5vv1`uNKc_oqVI9vt2M)dsw~lw@e(VLLl=P$#hj8Q! zb?#MRjC5vlD)V|6#}e6dCwTY_;x9O8W;`Y_P19d)BlfXtUMY-kmj>Ul<(r?k+AVhn z#T@rRZPDD1*~exdT)-Li*?O>jvl~CYiGAU+)OhUp5R=`G48>J?BahjLM)ad34;Z^) zL2{&eqVbCIK9urmbd9PU8)qtomRGC9B}!*{lgCcyMZ-mc4hWNygEa9^CZidFRi^mv zcAYutH?<;i6R<#C;u0WX@%5}ck@L$}Lnn6Ouu7&H)2(BypXi)siHy0uO!&Do3XSA7 zz!6_$g;6R1Dj4ivaq69vqz<#ydSU1cKm}waPxm6K*`}Z_LNtt3ox4u{THeHwt#(Zf zs4NUn*}p_GfXaSrtoDC2W;SWJESi4FS!s9N8aUJMK$7si#u8h-`)k)kp z7{<=}B9m|IagMA0_V(ui+?xVm*VbAN7LSoBM^Gj{teZo=fOpQ( zx*(+RWsQREQ1xBF&o(ZXYo*q%Gq>Cuwr8JkTHbZ&Bh7h5=*v_*w0yChzEbjgyCaK< zX?Hm41M2bDbM8lF6Q+j?u3(RBqD+P;f5mm`$JkC*WZjHbN*LJ1D-$9wr5F>G1)^sD zTKHcuNr%>B7SP zRg`GaWanE^J>Sp~u6X{&qSWcy>YC~?Ru2DV6dy1iT!a8#dhT_WK6juO3m%+r!NK5L zZ@(q{;4%S^JyGgh^1$=ni;V1zS)*-;=7x&7P(P^}!@H8F9$Q1J4=SmTsPQ3QF7`{Y zJi}=whoOar&p^cN-tIsf?5aQ1;*5XcKpZ34kBZcbaHKfxx$)vXpfF@=-d0zk_AxD08AaAePAo1okhn%%81rttz(G zkAv@{sZ>n8wI$WNK3R*2b%vN7Ap9V04YXox&Sy6s6N|329I+Hw=K~Kb<;eyFG(fDZ zwA!e}Rym+Sxd8OfZqm12T`a6`l>-aM`NYC2T-e==aeL&cU0S&@SESZ}Ge@GAlf>cC zohk7C>n+ZGPB`}GDGT#+d#1}`&zf*OQUWrN&hT)(65e!#pf4*k8S`Rb1;41bAQE<+ ze)AN74LG2AjaE8O$pP446UD?ZQxQ7{JVWABcf-@U`Jljo=!DJGMK&33mwOBx>JVBw z@nQZ>^?rY|Etn2l?!D4lNOzxpcde%u(HcNaaobB(U6AfhU^Rij`g-8Q@ze)UXovGW|MvS zv)z)KnC+1^I1%GR#*Mado4LIpX6RTz&R}$f5#hgX=5l;_Pw8g_Ygj-|(@7FJVboP_0$y-cdewf@fY{{hWd0gG+C& z{<4yctF)%VD&-6T$M<7|)Cj90GLO7C(c5SLJDMR8v+6|w1_3?bo;;!X@AVeXKe5d3 zzrSCo9@SZ8JS9~A7qMt;N~?j^5r|4KZC{r^#QWmgtjh0)w({G9+$DkMPG%b!#03czlAk?28SdP+UKV?vU6|9S+=7^>3x`K z;H04E3&RqU&|m44f2ktPyR#HYTmsU27h}y$Vvi^7!b$>1>8Tbc@)*tR#Sys4oP>z( z0r?EK{WDZUvx*ZEC>x{^%4Zl0aYorh98cYzx%H2)(OPhsj|2HkVE?#2ZQ>jAXBr!N zFxu;vi62vH(lw3_d{vG$4`gSmBcF{#>qDPmT#B0@BgT$zheEf#HQ1t}&n z6^Mof^HylqWPPYBHddH^PCEM~=*2>GIi6s&d*D#ZAGDmkCldMZT0Jw8!u1PG5+~h{}|J3$j+NrbTtXjf^V1 z%RlF5Oi@3ZVA#neB73R+KxgW(Zzf+2;wZ&!{#1ZbBr7jNU9WaWBf?eHkRV?&t0+CN zF-h?3tphm@jApw0E0-rycp^=o@wb>o;AWK3*G!uu&EyxuYbroeeB1VewIxn^b3tmBU$uL zli8Lwdk<$N-XLMjw8pNf;ZNr2&uBtygwsDJJX_~P|M&_0t8s*qGSdvlW=4ec9mdjHRrVYPkDb1!w~D>aGJWZMVt9ub;B zzZJQ+Z;w?QSA0;ctu>`zG?!Vd_La`!_%`YW)%d|m3LX_oSX}rm%Y=j$RtjX@LmXrT zMTmjT7L=^@LLqV)n+(!uB#mwkf4ss0tQk1X-IYn+D9+uX7)HbH`y%reBa!N-;Q9zg zV1XC?$W1!o0u||cdUA$K09c!lnc68l-;5Uw5jIyoiQ)3X=^!J5tccJB!Y;h{j^2@l z_EPGj8O$*RpYDx^GX`CQU|Kx4cpOD+LhPGP>g*0ApRe#kB4ZtblIDk&n+)~QNXN?a zuC1K3ruzWLo#AIDgH zo1u5ke=C}>082zm|H7kG*Z7J))JJoRbu_Wy7!&vNPY9BI;2o+C#D?hV+94BlLE^I> z>4V^Ed|b2W;R2)AC(4Uj(^3|^3p`{yM$B zWq_(F4agE3kR`?c`PBawWd?8s833pKMwu8(g5&;x=1$)7T`MUSgT@6}BH5f9H6&zR@;i<-Nuz4_-)bY>_^(`&-ZzPN^) z6&N)|9qO988JKgz)gzHjTHD^~GHW+2()rocMCO-FH8i* zx!R`>RyW0N*$51|-0fRSkjwTKHepB%NgOHKX2^$W*tnR~e06b%AT?xTzi>I7S|M^3 z`vWA{k&kT?KJ1v#v>Oy2-!zoGRTf`6ph@zvAge}@5`M*baS#V`^@S0Y;$m{&yw20A z*3uM8i_Laok=(|tK%_5SmH(aDLO98r1RCs0iWYX#WULfSB3k%0)@Z4>E4FeQ7v1}^ z-&mBy5EQT|VmI_x4@gU+2=^r=jXJ7zRd)!ysb?*B)%DzETa9p2HQ_5$E%AxHvhYLJ zB+$8xURXfCUhlV=ZJKwUSsFqhZG7pxWnC804J(wr=dNxaGIDEb^#CWb0})OR=}_nRUtCcR234@{k%a%XC(E zk>WfMq^>$OO7&2t&b27oyO!`oUMa*p=y#49rrEv) zj~a)zYWQffDoi2S<@zrkALuR7wIN?}&T{=-tM(7)Ej&iuS3s>w_J3&cw-P`7LB1Xr zTfn9DQ^|`?zVjC9O~xC+22#d2S;`#JE*-;XXR`qI~k}a-tAS>+ICn*@wLOjMYB%Z?C-* zsF+GT#IWCY0oyf~-FgjbOPUOLqTg~Oyi_n-^pi%F47!eFLV2HreJAQQ3$yO8QrZ;EP zR6*^5{_bZ4F?b!)WdD(`V$u2I%n8E*RTqE+M+QM`N$Y}sX( z#>y7!yvi)yzvF+GiuNX6s14d;oMv8UKO$iNqnge}R!7TfwQ?uvx{_>VKHSYPb9Y$xDs@Op?1X2bM1VA`WjJ zxv$LgZ%4pAcpIbe3ZEG+VK97T$daS*(uqQi1#?ZYhbd}@?ZqdPAqU2C3!m+?13OLY z(^&OIAC9MBjPb8j+dpAi!ag6KHq92MFk72f_ikN600CaqGxI?f+NVrE!qbvciI6eK z+SFRH}E@bbmya9 zp!yv)o&#ay%im#R$UkADh7jJxKVc)=pRh6XSJ()rn^f>CY^47aHZFh0PU70uK5PT{ z|GrweO4KS_U=;YdfV+)du_88HUQ}6MsKj(BOB&ZTEvj46S*9 znjmp#`3?s2;W?b{>Wo$2>l%lr--zW`yQ=B*eN5H>RYCu|IvzrMyj+}_f> z8~i71JY0Kq()8agTi`Pa_cXP^d9_yv; z1LcbfG*4D1R}O@~MAwz-Iw;_n2`1OWOFWI{dA=-$@Wac0Z<#Z+IL>FSMxN z+a@IAAOrkYK2@O%kTworP79%hTMiB1Pu;R4J(8{xxMQt~m;uqh;IFLH^sUT!V7vqf zf+T3r)T5i#w_$SBex})N&)*jvUlu8`Nz=Xk(fGYq!KTE*+jh^kdatI_DFgAGE>asN zQ^Jr7%3B1Cb*my!4DCv`9qLDD zE87YIBFEc*C@{)koJR_*3ZTF|^h?(rL`2ZJJYRP{vHuSRhT_g)3Q%CSjtZ}o|9=#i z)g8G6(x;^=@6fuVxsl8XRA%9(L$A@aj=uc0XtqYFxt)Tysk{#JnS>}ly+^0}xj!D- zWCPvCi7y!~#uAv^Th+^n8}x5#G2E^tVD1vOc>F$Dzy)9|qu8^90^ey;k;}s`_gnP( z8>D9ul$}^*Dn+J{#dC&lHJICFt~GfE=ooNgi3lm17~|G!VWNX@0|zu$;Pbld@1p?0 zUBt#<&AFQ{rSu>!faB#JUR#-HU- zg-N{#mf0jQd>$4nPcYBM^m#RT`8Nh8FY^lnt5h0B`5Ob97Yp!zoX5-qwDvEVBcQe4 zN&`ZnUu#@YnExkp1aj>eV`FxJY@KHp()&fWE(-mgWSTNy|AQJnPBPIneHlwU%cR~) zzu_C>ufM&%14tcbeV*>6xoRa_N2$kGRfj}W53?VtxxG_?RGvC9o2n| z6$p=w55zPJ3@W>IUKOVp%%TXO0cac)-Cg8Sjq7aovSI`ly@ZXX(ICu^G+Wk>@6o^t zK!eNZ+1x0t+mqB6?7GRc3YRADYZ`3eVyF#9(I8P^jqbM#Nd_9Jt3XklitPiS$MA+v zH66!+?}Y{UKiL)^>T|xR}1_`j(9w?L9oe^j`XrG82<~9W(4Bd%-b&)4c2`>bL*6 z9cIVC1yJbYm3^UIh66^_OPk3ABMcDYqo;@38C4zmZX)Rc1&AC#6`;9}OHG$&?HZjGbdyML6@CnS6B*jFzXLsy$@e(wGP5x!s(4s$KusqyR zZ9mmu$i0%8%3fHt`UvHINd52qe<0g|#$yfN0O0X2pqKqWfXDw1OEC3cz#|6l%oj!gm3HUfT_cE+TpC~;A z57nz&!*}wHelDfim4k3tfDkt2mL*qQj#(!$9sQrB{z(oYZyEMs?QWOGESkFZ?I@;o72hS}UY`~js%+yK@z#om9o-6F556czxJQ#N2f`o9NM|8ExI{|u;3 z1X`#g-xjKfQ2r+$O`gBk0uc!Yl5tpSA`hlaF-u0=?e=aD{^dwDmvJrC%sx#vS za@e~V7ITfz#DT71n(w@_YkD>3tNR_G-^dPmX^AbanRszfn8a!&+0yJWf`UMpusX~B zXe7XXKNfTmJi1y2Gcq0BanooX(OH`b8e$}qdi7XFt650)LOgi3it-ZzXsnb#Xr;yR zD>jm(kqtr-fiQ3wcA=IW!iTmFM;JMu%HPa{%5#$fk&6ir^7ge+yUwS4oq_)O>(Zv$JFuRA6Q(lY3RlYx;%omXUtl>4DRm8w#0b$-A${VNd=D0)@ zP3N3eG*46?gc8hc)&YRgSgq&;W_tL>KsXLv(OyN)EE0y_OJPZK6FFP3xhgmz!-1SN zk{a&V${$*(zNfFA+D7}kq?x;VD^^lWTyS_5Ot5&?O*fEF)(|_FLSwGDKSW%GPg~1a za*b&yQ%7xyR&WMw(0Ub7gDIHOCh6KVNLi3b5}??BSwD0?_su65gGE#-IaHs7+z{;8 zADKptA{m^PgZ|`UpVTFtQCwRL?NLS|anTkt;eQ={zA+PZ9{64Co_rAJ2J_T2)Sbd} zyNmTaM-_@T=$5&Q77yWll$~%y(e?^2`r_t=2;DRCh3cE@Kkx+=t!h{A?^l%c=O-{7 z0R>PFG=w<8(@GEp+}&VMAnrjNbeM7rud|@U1)X6{tXC9bn37$c|HL{s=1_%%CFa=b z&uTk1rWQUSjuv{YT;10oU%~Kh!)%`&yw5=4o|{OYea@O0E-S}6@uwY&sa`}8630T{ z9pXJ(h9+%OgTaA8{;2Vo^Jxr`^pPJapmqTn+*M%DZ#Q*Oj z;s1v7nXvdLvd1$0YI!q6SX=y!8&*4}hluNAV zK^!wVUwf_$CP#o>A*i4W;cO6d97DdQW?@eWY>fviho(r&`?aE=0isXHyqQGHM(Egg75t%niSmT#&taTjd;toABp@~tF z@GcqAN1^6*r@v(3B6RbvD&m9@2E_HLS*?QMBm3qR*7=})G0))6iNlze2 z>Z%}AkvH8qR2C`J+W#;OWZy$z^4y|q>S)X{rWQ;l|RTqwC2uZihM$bM0uJrvM`vLPi0gYL}G+6Tg zoCg1U@eESbmPHl-h!}KSHcQ6cDGLi93T~j~LFF4D3jeH3jwuWd#K3PhrjR^SY0*T= zK3M&=lhFedH8(7@-|%b1b&gx}cXIQfmC4mb8n@{*FZ2ER4FHi#a1%M%S`%-}-zz@K z>^$TuZeZuB@`IRI#dTbkN|NeG)tI^t_0O1gxY~<^pINDVxwFDOoApf>kg)nK*V);!r1v-{${3BuIuFp9u2fq zd!ObJ3}%f8IX4x@g9P8z(q<gD0eWRciuLeS$B(|eiG9B&Y~ur6k; z{3nbKVPrW6v7A3B@5YWVVpU(2G}fWxAs4AerjHE&1_?xK-dpIeiDu&XB1l&*Q*3g zwGI(U@({sb(#s$^W=;#W*o%&!TpG2Hgfrv2=k72*&vAvq4RX-gL5l}|+07_BCTo3UFr!4{8|+@6tU{Es zX2LJpwFOtOMjo*#L6-6XCcz=_K$6+lryBTq9y5GC3auHg2Z6e`aql}rjSJF%oY=QW z@vub(?SapL{qjBMCQ;?FIT}1W;rebwAtS6el-~;Su`0l7@=B=^2+E{^uHnJYJUu6M zv_nTLAt9n1)*6CIG7ZXn-FpqGJ`OLn@tTmzW4J0c;w#_D?OB_Eq5o3`o5BGlMl1u!FHqQ8v8_v zNHdG4@5*?WnwQuMC(w&goNhyNU+FiA=C{g;MuSUjVpOEwja>%SB}RHUa$A98bbCye zWNl)y4JP>!4*;`O%(lU#Gva~OQbM~hM_qA`2xJngCIW`Za=2|&$7D(keT>OsI3R$~ z1eRlEy%Gp~U=quYp&wwh{gX2SFy7NycL=rtM!^)8ot)8PqJID*pbW!w=}!Ps0~k&u zyJc^m5WtXP0~ja(hX%0p!qh?F;Qpx>5%46Aaln;FZR5~z7J&P90NDarfO4$Jbo(8>B!HC; z$VOlRT>Z~`nK1&&D*(CCtbiOWK;1b1ybdxYCPEDmC^bsbPCChcaYT5H}1nS=n~nqV2KHI4S6&ewm0t+#$2Xp zMV8yaWf|wrOJ!T@Dk6lZa zsWy}_CS31aQBQ#eCbiDO#Q7WSZNjTkso1%mLn0bm7!S^PWoAXhs0x*ee|P0TWcA2p z#!UK^z&vm@c)L7Ago8HIjtxBw9lLCOEEBYsRk7(UwcFyE)f;&c>g&Mjsj;yv+rsuP znF%$Zv3b&<&jtGoz5U;0;>$F{yEC)vgYBArWkzN%*$M7rtV%tc4Tb!vnVV5BCi&(5 zt0V7ZEGHnX`(VcJWC;6o#KC1$7afN61sGAz_U_7_H?V4}Rv8tF?Ojlxkwb)>9m%|$ zJo!wJs_5MvvZEPi|0a8+qc*Q>0j^V;Bn?5Mzq|OzXk)EQNDxn;~ zB#Eb(hfKspv=R)M)JXrn%WHa~&lMu{SlJsV&JX7}S;o+G!M6n2;xX-=XZXp=$Cb~*b$!A8dBAru{`g159PglOtj09u03V}cVWGzn3)_mdN--eRPzJD zmQGeVWE%)DEhmI}6=WE_V5q=NCwh`Jlli{*3y0XQ+ywAK$+Qc|mnAtf>9%F*5Q)() z&IB)q*sAq6oCKq+zx%SnG+ zBZH9GX=7<3kA9M5Bb$(r>6rxnK>`ONDp!z@IcY9+p;=U5r;W=?zLT>=4(4LEFKr;o zOCAp9%8rM2wJGIB`*vf`qT1ii8M&6l=z(FHK=F0g3H)l$9&h}3Rm%*qV1 z<77?^f_)F2v@LsJ-&W-(W;>^jYgFS{eg#FQ6g1ce_`bmUI`Sr&$zO?#Q^t(}1n|3> z$Th#a1;S1B_VJXeJ=Q5q324^Ezaf85Bkk(JpOXsUq#Jiqa!2M^ah$67ux{SdHy+fu zVrw>J8b_gIKy|HT;Dfx(+B5GR3wK7U5B}J+%ebi9#u+!esN^=8$JNxKDqd5>-2W(= zTb_^CdwGa5Kgu`N|D|*c?`X)pcvdD{c?F|xx8QivhQ^zb4PNpY^+ncW#>1pPj3bpP zOTQzf1;J*CPuxq>`tr#o)j_>799OnLIah+q4++I_96DLxtX-qHaWZ9Q>+6 zig67-=3H&-4E#ydb$=xH_p4SI%`PIjAEZ_Ih}%*qLn6Det&_`HSTL8(2Hk8!crN3V zHZxZ9P+mcpw$%!vW{$zJwF`!E2e{&+slA)0TZe-&VflIK(Q8qyaMDH#)dC_|?<-ct zw_=+Jd~E>EGc#QoEzM%2k>TJOI`uP?6<80cX1BmCF2}Z$Sg^MI<2(``_iE~N*rAvDDm19?WROtZfrw)P}LM`(LWM6<1+0_BxkC8ahrq{)`&VuLQJz?I~erm>v~(%#&k-g@Y}>lsfd7{ z-}yP$pi6;?-C|i5OrVVD(1@3kSN~>>@|7Ve?v=`O3wJ&rPk#8fq)=AyGb#KvHD0!E zP>Azga2D>Ci|EA>BK?`eU{>%qE&Mk(-j@U_AKIYmMet2a24V!W!UAuklf=v~PTpS* z9%m@LYM|Rqrn3#3kwuQf>8`+rS@X}(CD*Dl_{j?(hlmE*Gko{s*z7*_Y9_-Q{gB-C zkh-~+eM1U2K9M#T2Igk-p}H1_$$Mit=O%Q?Hg$NFLi|D-7EdqtS2tndTHzxfpm&5h^xU4i3lgSDyBb;#Hsby5W^SrA5+z*Y@FQP@# zK3I6k;TDcES@VE{Zi{pXDx&geZ&RVNQ7kngqtLBZ__tQ4YB)5_{Nxa=0uPF%XqR|rMs9gOshWqSuM#-POh;t~=+#}@g_G~da20qK zh(hdd2`RG;T9 zI@gF+wY+4)dm2Xd=(-7purFeWahDzFT#$!xU2+yw-?6Y>O=Oltt@*Q--E3T-JN_E5 zH>__QkeWZk`7r7z?%SG=DKamqu}}0jf^2Tux7*t!`@O=od+s~wuopyLldO8D7F1xL z0e9P3LhQ3Ui_|X7^pNe!(Cn3v+&JZReoseMYb{t&zZoF!bL3)oLWKAd_~tbiTOlDq7! zba_K^#Z0C^7Vkp2*4ff!Ih2u+hSJU*b{unyRwQ#dpsvePC zq0I@6R~i*2io_AE-34woRk*J*`kD{X-L~-9&WPD;9F~QN*n6kOj@;Jqj6d-_%U&XC zxd@UtU0y{PJ!SJL?A4@8FV4sefBZzg&xghrxxTJDaFz6Wh~}m4=KLN4p}V5nPAumf zQ($EjC>NsV!|M628CP))lHOjwAEt!?OXixBMDEdy5+4IHLe+i)zbq*PcTaf)!|2=q}%8U3*Rdu{||5#=QZYIY=u9Nnw zYBK*4ZZG%efRp@jUs;VuE9_jRXCsfelXiie_bf8u&BjTtvQr0%!DE<&(x9l*b*Ov} zsBr>LFf!u7xnD*X@-f#gS$OMGM`Uw~es(3F@Je^fO#^Dg6kMpwa6ze^_=?J7;WZ z%^DF!h%0^gi-=83D#zloJGbMWT+X|2zxGns)8R+};ywBW9~*8Ctq9}Vd>7*K$qf9+ zpf3_8;a>ZuXyCv=>or9|s(RN#Dx*&UCSi#)Iq-<@2WB7aHPc3^=@m!smFX3EH~ZtV zO9XtJttx=8860CkT+3~1gxS*^^#i|Q;g8|3{U|-WdFl7Ga(l##bC&_WhVky(M7oUz zWIC;cHsf+x7Xi(~R^miF-`WTpx(=kEGQL$dFJE^00i}LB0oPv=Jd*1osxFwto*Jip)b05{v!%`h`N6*d$pXfHi*Gjy|NF{rv?t^vR?r9Iju`G$ zel&*jV1)43ZtVAuSMiV!(NS5>gA&4D`>{DeADMzacz4inufn7BoChgmG#}nUoe~)9 z;pwQc7!nnF)b0g(51$8Bo`glUE+12gSa;+PmoHMgCqt7>Ig_SX1(X1s@PtrN}58C>ZK0<>xi^&V92j$Q7QxnC!WQ+t_4b;sEIUt#X5a!>pyHrcA)WkAW~V-!o_ zfgWwK{*u-E_MN!g#k`MdULNRLANazwevZ*YwfuTz1xq<^HrHmk;tG0au=^Ga>(_=R z;@nItfQDK(FYhm|R=e1(N0n9`n=C}@%msc$Uu4`3vJv)Nt1_eCJhN^Le`yeSy@xmz z-)wBT8zJ8NVVhyDyDR=3NUVS?ACc~K-JGb= z7$+N@;uC(rrxh1Ta+UvFQ0}-1H94Z@fKoBjsom64i@g=BBBvlVSHF$0s7Rf2!rig` zOG3pyFOp%BWrJ}iCxrS(3;h@KNH#9IXLB!6wCFXS+NKa#tS8D5a__^eSCBhmV;Qic zlYSgY-%%a|S_6OcB9DAb0^d~!m;fqe`tIs{U|w7sSm7P3lHaa*Y@%v2fh*pei0+5Q0g#R{HQ z5Nr2y{mimTrKu$8-s_=>K@hg=q6<)L zHh8>0dS2`%k?jk^IEdJqSJ_Zz+|!SFrkJf}L9DTzxU7Wm>UG7qicfYA&{206LTja% zeSzF|RiH`l7xL3$h<*`%2ibq1L(!9b4;7-DEIRG=D_QF^T}Op+1+MsS{;==7NI^qAn(l=#$?=oCBlfq9hU zfx75inTMV*o7kBLyv$o}8_t2{NnmkV#u66p3Dwf6B*1&fW20`*Se5Ee3eXNa(4ym? zByI3kRiIAbKV0J|z1|j_>f_Aw0xEQ?66bw)opZk{gI0dn`>Y8bG>}pT{gqURC|@_P z&1%L#OD~#-zLm6!+8N?wiHss3=G^gM_2EPZWQ+d&;_l$JZd%`0l`yi}yK4skNV=d({ZrEwaqP4#gXpXGxN~J^o1RkogIdkcgIkgm61hSto*@Bo-wkY z%aJNz&!9_@x(w7n2RZ4`3vhF#;-gGryM5r#GTfs|`KCP(``MRc7W8b`b1}G0nUIS} zg!j84=Vh9}96A9tz%ZCPaV94AK^@WdTJ_#zW<0XB*JbG#jdOj%ep)*%$vfI7-nNau zzwfz!=R_KVE&Dr6vHKoG3R(>+EkxmaJSSwfK2k zBYmO$cyrZ0wk`5^2WMC&g|_FjtG?bskX2r}27V7FAyff=8?Fv<%ky`KXGl3i!2TOH zFZU=H)tpn+#@|sEwPga~R0`*0rj;w`oGZ8w+@<4w!^hLgi#g`_$6NF{{v);j>&J>K z*#I5*llEqWwd@!^@cR`BWH#i9XM6UJEXt(lpVh>l#V`!dF1SbBp!bY=Y~+5N2n?F- zaQ7AduRj@{N&V8GQr|mZ653n*UzzpR;-exy|1>mdjtHM&1btH0t|tU?K+eKD@eZ0p z9WnRvr2aGk*5hi2W0vjnf=k^dH>^kDfOn=(5T(4J|MOW3cMIV>&h|T~HI0wzi;r&O zray{&?F1ioH#!vBRYVCzC|m| zV?7zub0SHlr1l%`!nM>}+YQNt(x)=k+7KP;u8EAHSsqbvLB zT7p%RA4*mJwavVY3g0IPpEv(6B|dFn+U>w24sR+XALHJdn6;A}Sj`QbOV#KnYSA}R zA8#zjl=uSP$vM0uF?i$mla9p3ilmzDf#sQ#itLR30v}m`L%p zS$1%yc3UN61L3fOi8I3AjR<@2)(_+Z?p|?lbz&iEg@QEmglMM- z5k{eV%RL6u`w>pH!dxpvxK?p+&0^r1BmqfF5AK~19irJ#@1lX9%%3e#@1_P}3x>Aj z`lf9{7pI}0zH2AGFvn{;xE-n^SAXj~oiF}nCe_{Rn&Eg?U;d?WV};h4@>gxx`IkB7 z-&irzM2gQhG$MS>p^m@Ae%9?cP`)#J45DMz=aHGUhl+`!EORKzHDsGwlC8d}9<3pF8wxGk3hi_}zPD5C zfShNuwr90=2Sn5HYSZz0lX7WSZ*`X$b4l*n7P?fx6?^kli%ZOIY1d$N*JSmSz~!OK zWs=LK+@g;0vQ9%mSb@uZp~hNZ7s!jElY5OVE8X4o!HJT+BEqrmwCf8m=5HI=S#$$hw%NXH-dG z56Xy7=%*A%nWz?`);BkvUW7F=J$bElMmo8qWP`kM9{urr!Qs!{Dm+Fn@o1>p$}eFk zdGpC5I}1cV>=9ZG_>AnA93O&_yjfN#(oFJJO3NGnD#s}umr{mPF>a;`DQ6N*8c^m` zNOCX7DUyhxf>u&59XC*pRxVOaDk{&+9TzX(kWErl=9EqXFRzzMVpdkK9_K92tQeP2 zo>nzZqCziq$fH6pyJJwMQ$A!;#w!~?EZ52#*HErjGVY>^Q86x{0x7prF&0bGSFTn$ zv@h4H7)MrekEdELyHirGRz1Wl=TS^Dt+>-twoy6+E$O# ze?(vAFAyoOc=|MAe62HkrJm ze-jFYq=Dg;$Vsoi|wQM-;XT7qO>2+8MRFFq1a%OuvF zuO{7u11~0LqE~)4hEFSra33pRxzK)g5lEv0O$injy*d7?hImq} zncQ9u)zX4D#YypC*ZeU^38-DL#uC-C|DYh}J#^8nn%=QYlP@jRF61LKI|b(~hX9Lj z$-}p=EKbXU@K58`*rHXjlx5{$z2eN{B2<~XSe6L`M392hGlRp=bPJB`tA*Ol%bwY% z%%OWt_3q0HO-BuZW0fHS?n)M>Uyh>(T5Sdgf8D3}JY;54#sMHjxiXF7%p1CsHtRg8 zO?r74N8PhOmfu(S#=~swf=yT@Mpv2dP88lwKRT0^n?Oa+kq|iWgO9ju#ARA;(jq~k z*cT|gdT)mB~5q2a9)4_v+J-Gc-6ISl zuM8!-%0&yj?STJpi*d8{Q?m%RX~bhO@{c`i%@_&;AO@z$)W=TOiXWHs^;p|aT}tLIef7i^ z!=gKs5<2dvA0eZ{RcKWyu`5!OIZK&n%Gix9UXc|P?#ZE!>IjI#BmI9K{QRw^kgJ#M z*@8h7P>(vgB{(VPJ@QI zq>L|4K=V-N7DZ*RWC0~yc`MODZbD2P4I{mbUGOu?Wj}UVFBQFY-@%8+4ix@YY?#dj zWs-0VT<9Y#c(v}KZ?@Q5B>JspNaNkQ%Qb(!cHOtMzRl~M>g93j@*SEx(%&T-Hi1p_`*d19>Sls& zS{abm!cN4{!Vp8Lp_Lv~GbhQtTScWz7crS97Un<9k-!JAH^xv0)Q+hUmdH|jA70{DW{zotVZEI`wf7b$e zZAokZnT=i7a;a)nBub-6SDdxD$nu6Zm>(Mj%92{mi#EY1T}sV$;7a7A$0DzJ792nX zFMpWEsv`7m!&3Bch3jWDY5wjaY-92=3M%@*cAti z7;||0N%K9>YMv#Q@PPI7QZ5tP-&u9^j{w8s>Zl?Uk(IJ(k&6i-w0o+R9bM;ms9a_i z!NHziI8}$2{d0BR6-!dh9$KDOmd)nAw}n1X5RU?dM(vH5s$`&(va401lu_O9Q8Q-$ zOPOCt8AaWTkdl8rlUsuyGdQwu-lt2Z}uz<^`Pn> zKTw^MY#3E;5P#=mOG}j_5%63@v}w{!Y$m!axQM@~riNnSe*k?^3~VSQ!Q*o=KTl>d z<9|NBkI4R@zl~&Q`t{(LcKCHp61G-B7RrFn(Arjy_c3N&JSJBBdb>7^)khIeW)T=n z#)}_LEKo}imBs*B%!MN?VG@|Uey)YyF!yK3LzKuOt0F}crDGw$R4NWc3+GT&&{u=5T;4@K6e^5o+(2Urv%=y1#oeSeC;@@xtN1! zwm_3ygI9g8l@#+Zmgc_%Xs;ZHD{C^t<`moI`47@KBI3n+eV%FfB%WT+uJ$wwHYD{@ zCTa&ZVBhQE`zMM&E?zkzm0legVEd7y+qFvKid($GGq(CEHMZC{zADSy#$BR$OYFg2 z{fOq70!gFKFZ^hmJ9Q~r$8`#jx6m8A_jkv1N#AkklfG_UGgT*jmF_;{ZQXR|x+U1E z<67hbG?kR+D7|p;-m%!z2bo0CmX*Wj?EgbEq!aXu;swy|a)3`gr2iP-|7!W5gi9F! zRW*2f6)jo2ImrJ{^9v|yOI1CJk`NUU#oSN7P{O>PjU`ZY$@Rtv$dr8cUVI-4pG31g zP&fHu=7xO*iN9o7oQ@{bj?xD!fa^dum1ji5g$?yU zqe8h{W@^2}tE-9+bpU3-rvw+$InEX?YAFxAY(vcvttRxwD;75AR(N;uR6 zA==mk!X+uxV;GFkn~DxYf-_B5%yx&N_*T2=lS6Yi7*DDdrBU<$#ibrwZTSP0p?zR-T@$^|bf+=*!L8Zyv@qDo)8- z(M;LN6EfCyM$6|ixaw5gQo6Db-XGrasK*9FIH=63l5f3sIVzHk3T8_Sun#53x(qjQ zDo{seI1jF7NA??gLc!ohqLd&A`}n|!?_=D7_wnX)lV(?9zQeuxf0(^W2JOX4Zb-~J z5q{E-?m)o%z_WV&u{)8Y;PMli=8sQBw=43@9vKNMhuA7dDYe%*!cgV=Nu!&CcCuiH zT7dGwFgn0XtWcLTk`LULD;!5c8R0X@eD8BZvYlsL%nYlQGM1i!2^~KKVV(VZ8+kNQ zXnLYO$|*Q?r1ce5g?b9Y42*2lqL>%4QR*G|@03%%U)eVb2jWI^jF-%3YtTB-d{3#_ z2W}&=6n^eK1T*>OBR57;%}wB)!bT#Lux>7pb|E)eoZ_B5kl=QnRWSCbcRvfeV1MQ= z{X&f>dXq@lhTsx$US0qdaFZ$65i~P4v{W#5 zbhdK(Kg(g1%BwGqF|N;+^!%n&iScS<5Y!57U_EF0tYCmuR3Rf}T}ClW-HO8E=~c!> zV@8&<37h#3&_NJkn;ME|W%^p>c1Wc{ykD=rFSp=3;GMIw)GwWm7dG*ha=}S8NwC23`Jwc zDl?{MJ=|1_{bbvx-8P!0_LSOx(IV)=AqqF}H~5DcpguYy@Q$nGRsngSQ~}GE@bFr% zI0Bz505I0AbZJ*5RW21lbTw8(lr`MFMio|+E2fCzb^6?8cf7?qw3J37|3H343Eq)9 z8A0f!g!~M4T)zpdp|x(>Di{jX6EW6Fk&$ty*f>Q%prFaI%$DuYXllD6Fj&>0TKQbz zJPt;Zl)|a!=sgP{{RSP_7UE`o*`yNVInq;;YIn)P&VvJ(} zmomC=IuEX;My1zFHFZy1&AEa+>@F$8lMF^&G*fJ5t=J(-_L_zYjCq#}qpM`_x(+Ke zhxzEeeXKX7_@>y0s4)ya0fYjjcwrM$OAF&Km134&;szEM%28P+|4tSMF215tn-S@Y z^xQ4^3zKl%>S564LY3j=Tq7>NC_>l(47AX;WP0uF?DESg40#eh;@Wl%7*w_3Y0x9_ zy4gDT_TM`_5z9U!j4l&mHXKjo=N3>CS^vhODLi6!unE5xTAGIPeezvn9 zsFxBZoKLI145kDwwl|oRf#tMQeQ=f{ij*%*G^9{`83t5gd}TSefnH%~o>1K>3^wRU zCQDMrC9`S8NX2|1OUg;n_T;*ZD?YK5x+h5VzRVgr-frC)2k$gz1Vtp{-_~u1YX=B5!;8^w6l68DR3s^p3y7p8!PuIax1uA=K2$WOh&%X;S zzOEChJz8Vl6JD`mW(BB6z{HIzE9E)NR*KK^A)WQ{jnxjk4^Io`;Ychyi?LWG`tE~^ zY#fVbo18Z?sjzHTk2J7b#$8n4GVqS%7&U8gf?23GJBRjl@hO>3bxaBa&c!Q0dBzC$ z>>tOES|p*r_gfR^8?-J9g#%ry8qQ(XxX^2NS#pS8U~9OnOZ$vlGoRUCdd3qz(mb+7 zfcj~Hes@;*$FThhP%~H%ZzN&M$*&w6Slu}X!D+G>X?mz8`~p3zpb8&N_oJxJdrhYL z02WAJWDC5m+<9|c;ak)08);o&HJx)c17at!sm$%US8kN&lk3CdQ?7j`iO=(4nWZ=I zbyco;VfFlo{{mC?Y5Dn~Vwg(*2~v1(5(}%FPvkQ&5gC3?E3y6bavrMH8%@!SFE0S` z7u^RT4)SUiCe#yiuytfFHib|E(D01plrDAtomk&xS zxT#_g#3>{d3@^z&Nv&be6EuR3`xVUg$;DV?a$qDVPWurF5#8(&*u3pc^!tvM=mBrm zZQDu17n-Y*>lTrjJwL%sXg4$b!x)bwFE2~bUl4i6=PoiIZiq8?(-vI_1X-D)-``;m z3&Rlf^n1&;DjD@TRjz&`>lGXUp=P`2vciw)7)Pe5^w@$W^ol{y837d<_jtWrI@`wk zU#J;3>@Jtvb>QEz2~a3NDS^AuztG8|wKb(_X>22Lrj5%zZ3vO=NR?A@QsXYHNl6!B(H_hoxI&vEvEiPxWoQ;fjkN0jNymC)Yn+P+CiqE5Kdki|>N_IVvAq%;xPw zy;5nhXcdIhRbd$1HbgxO9km@m$=o2BU-moWW}TRr#Ww2-J(A`&`eAq^mGh0WZW@nl zp(c~RMEP-mp`--j?^RRZfdzXV!v>2iNMED6ttO4n~1Z?+Y()FepE@ z=6FXC?kR7IUmp%evsh2`;{I|;+08tihOVG;+J=`OA^MIe#2s;Ak(wVIo}qfMz4D8u z22eJ9dT4iV2H!rD9V9N7^r>TdoH{o>N@mvn5bIT{R~LoK--xv& zM%vp-Ee&*;p@qaGo|djrSE_frrH#f`8t%rW2A9>bFwO<;^kxU)byAIp-)I*+-sI59 zq&Twec8tIOp7Y;Bp+%L)1b^W-JqXQ7ZP8Q>Qx*hQW@CK#Cl9J>y$E$+RnD*H;)Lho zqK|dwmgu;vk#~t}fbN12QcX(jF>6HIIo}}ghMeePE%iZf@>fx zagPT?+dNuwgaO%903{3-3bE3uWQKVv&E?nE>wt-4FFwEX4xZz~h7>RG8O9AhDK_s| z@*_Q%n}3a4b>d}=iQ8#((%CP`RwmE-O6SHcl~Q3Fzm@0G2ar)gxWLwG^NFg7w}MTnkhqYn z2{iKzymM$qCXxqQGRQqUZxj4%aI-p^7|RdYO^gk zA?!vAt(t1NYx-~RN&T%iP;!f(829^s@b`VBLQ}=UxJxDx@-YSxFmw=jb1Cqug5u~~ zoulSvgka8DL=M(aPRZ@om+2S2_9~KLCcTEbEPn>_{YhatyF}e6DL*MkKw#%DD6I`8 z=yLl1_&N)&y4Ivy2X~j?!JXjlY}{RgY~0-?K!A+|2o_v|ySoQ>cXtmOg3I0Mlm2e^ z8Kci{Saa1|RZmsTPbo{`4DyCg@fDk~RV!Z&Vp5US^Q>$M@5SLTCI-&o)#KT_7)A|@ z6}x?d^I>LJJ;tZrj znaH`CBJvN6S;)ET7DwWED0MT~i|hs%ULvGLDM+}nz#v|xN-E~c{iqL#nu_+S`LaKq zdWH*S!wyD6)QuejgdrjP6gCeNGxnfA=y14j*DXa>jGmN>@s*#rJP=(H<=&?Q^EuVV zdNhG%66_9fiI)YqF5H$v(JCtR)&J96sfUhDU4WDTr%vj%-)m z_6_D{O$D>X9a`K1DPaiOU3+*$D{7j{-cMqFc0iR>&CiV3q|7`pY9SXtmAJ9nuQ<{1 zc4D32Cs~XRUnXZYVwZ{4VrUdIKpW`iPI?r90e2fiywcs)58d4)M7^!17%~^w5?LJ_ zyN{(JYI>{2%!BJ5mUA?#u7-`9Xyqc8a4V61

RzE=;%NsF%gmBt=M^WX0-Z+4F?U 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�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%0DnOLs7pw_5G8FJg9 zZ>RbLqgyqj&Kb)p!HG`ed(d6MD=%iK%ECLf-pj9DW_#G0A|P)!m76*5JXT|ao3G0^ z^T^xpQ^g&dLsi663qj(y)W13#j-eaLbtb=y4KaKimj-oi;jRqI=IU2J~|3KI|*DEwqlc&$Us#a?p=es6?A zAw3u0gLL_o4s&1fNNGs}T9??%hST5YaaqF1;$VP@DL-(Xz#)X@>hAyYUT1wUSX)FR z3NoLni})7bpEGq-0sOch+qV{D}HVTr1|rrKoKk+a-!%~kf;e8s+$(;^>(e#G2nws{8|5mQ=TNZ^_t6Kw+f6WI;X z0Ax8)Y`y6BXbFyQ4-gtNX71-)X%C9|mC%%~%#n8%?1x0?bh3TbH>r8&0KtnGb24!= z(Cm6c;z)yhIRr)U}|0O$M;<~H2lV|(=;X`V1 zr>cFoZxF)PM{}Q{T@(_%!uN+HoS_|2Jl3&$l1WY8e&FD$lBm&2uy2twE!=(>N#eRS zH-Q5`O4xtO95j`lGXd#t#>^O1rQf1T+1IRgR-D8Ur&HO!Ec9PHOO*6Ypd?xa*Uk5b z!(+q|POltY)lgDeU3n%ZKd0_yA$yxOhQ5Jc>WPPyOI48RZ!xNcQ#RCrn-8v^p{M!_MLodc<3bY;308Qfm)42GjeEgdp{gM5ohp+u8K!5_T z6V`_5lcvx~1nq81!ajb0CRG0A9d8|vF#e;4OX>x#H!A^4nEwXi9&|J3^z*jr{Mjhs zM21uUamU-M4!C9|Ve9TW6P` znD6F&AISm#XoNaRBqaFn6y8=8nf9Fm|hzCANyETqdp5!hq)DUn&MAjYH13*bPUo0x+&XHdQ{ z?WG0lSr|9Sjc;;brjdV5z@w<6U^uM7@2H7S)*=jDh$gq4$(>N#=`{!cUA&}AGACFm zt4l!nWAA$s1=Lvmbbj?N{TW}?f)7|Cg~pN;oQx-VA;;{skb93ovi^O zd2abJG&0JJv^YF{@$nVuI0T~bDrgsKGq~9yWS!uX5h#nJCdo_YRJ};+YC!|CER`dNMB1ZX8gUB(^!wwgeC{q zbd>ir2rqd)I;Vr9;$5a+qaplHL;D4HdM)OlbyHVx9e{UGIaduE@Fmn#r|uWNIfwgZ zP;d@ifn`ba@Z=VfvB1v{St0T_Xe71SxB#)xhFe91ldlHz=d+<&xf1X&$IV|bH}h4~ zv~mUSwX+E$40L*_E0nZ?C&6uzH*y{0)MY(wc3+XiDX+4C3cZj8*q&(>kg{yfr3JYo zrk~6%I3H~1PSN$DYi(D%@q6^2+TIBTD8@u4I9e|wc8j6b4x|c-)iT?hRe) zqr?SjlXkRz@EzZXa&!ie8P3(|;Kk~K-4(q^0^kwBGDMfjq#Ou0b3&dpbGU=l$5~|g zSj|1iSomw!VaDFmp}M3QTgtT%>0)71zP8{3$f6*`+6MsbbezGQIf*OG65{rYxnbLT z-hHI%<3ssnWu@wyvNjx#s3iq?w03vsZMK)3J@QeE*#Z_S;~4Q*N-rlLOFiT`>39!I zqh{8Lv76%BQy4XFGHP;`^XBVyimi*Z1dCGwuLNy|k_u=%TgmZ)4D~~gjv(dmyz_W} z=G5oSa9W^BOVqI4!$J^AZ^dN$DvX7Q0AgjQXG}hEk3>F-Q;$60xp%)CF>A^Ms5xBY zH{vM6A86{kM@uW8F_&Fj?|d=OsN6oo$yMW*_qo3|dQEtC=hKMELwa%dFU(V_^Hi4! zH~PkUrP>M2Nn;gOrlKl}(4jssa9QT(DNh|tdpGjRQNo~XCkv#YS+&gyn{Pbv6Gc@Q3o*L8v_CT&bH#K~%q*%-Ye2)}1rY6Kq2NHa~Xh-9LQP{OAS zrYG}oH23tr8662*6WA3~3a2-SE4EC}fk1EYNfHw(j>3!7Skq}A`op)X+9K0#*`mq% zp6uQ+D<%$f9Q8sTwSK7?0s_3nyc&U^ay&hMiG-psgM|#=<>@aWyz+w!7n7YfWyexm zEMxonlUsT7ICZ*}htQ%1xRyqmuD}U}pe${EY{{_Ns_<*y$uGLj-WrGyR~zK=PSXnr zD&LNDE0Kak(uu;P2DLj*5k$A#qMJ_;cme&f=hvH^PCYTf?$T|>UD;E~QZu=Qg)=Hw z^feLI4POEiD|hp%Yp<{DL{Au8@qy6O+bJMo!AO=Y&`OMBTy!QW6-TJ&+ z>~ZQovRF_hRWz;NkvX4{FV6FWAv&$NUSiQ5uu7+jtT3k?*&hzLdUsBDm=?^iMN`bU z&LeU#$iuX-u3i5_t-AkXH^Uyg4$E?xCh&&6F$j_b<;qHvm>!|76)*w{hk@hBgEmjsbY=5UKu))S1ZjonB^9A}U;GG7VlfXbnpg?< zm6DFQpAk5(ZO&loxJ$Lv;`ZOPx4-O*v|m7+jrbo>^KWIp$_mKV1yc5>|F(6>pxN#z z|JG`k?jr=l{MO4IiscsZ^A2%3Fw6gkUQQ5>hWUBu9mv91upTs)j zE>aOPN9p__>dxT2D)%dlnfk`7FO;l6_l5J_x+s^WFLQk{mes(nQ_hSe5Hm`EVTKbogs z$k@O-07EF9gh?&g;W3DenGKgiu+r3sm57oH6rkGlobQflLgJ;u|LJhODJlcqwE!m* zg}w`$KI0iDbmb)W0gS8JIJ)Y31B=*tfS2^-I9x$X2iUZd@&tEg(G+Ea0n5MHjE}(U zzor$?aLQ@35t|XD19QBZ=+5=Li28~>G%+ox_ndEfEWc3OlQZ!Uz9=pKsKq-~JU;iR z|Ff~L8g0pauYOHr(|rvMK7nn%i-yH3^d?IX zF`xBnKlNE%@-6uab6(zlPY#f5(%s3MOFo^fOYu-8*bbO<1%yA;Tx&tL8998mY30C^ zt2LEJ&MjB0!{=EqO9if$)%c^6U=-60BC7|>XNt3TFV_h{eG(N6c`AMYdatGJr`4Y4 ze-iGYWfv{5Y;n~jBmbqW(t|s%mKSTJVlaW|p2>lQx)C4`>-AW}@4o0ye6Q&PR+cTi zm@ylMVaT+ghF;ybGY+%zCYM!JKZ|0Zk@Z%V?-8P?lbf?y5mee#K7Wsr9J@XW-a-PlqA^Iq_7BAJr6eW%-)3RUrVz0+=l zjIZHx*oF@@=Ug+wxot%h@!I(@E#xeYJowJ*OFTOa+#9mA&;8Ez3d9+pqL)Lry|*9B z7Ikq;4bOj3Jo8x0hZQBxy`PS%r|jniY&b=eX~*~b9LG>RtZE-}MQqy$$lNnZTTqFq zb_cF*pJm<4@P{G%Y{uf>9Ut5RBdb<#zCyce-+t|j7&!q?E{;`2&2Os69_4_-&`{ix z2LXoj{{u_`T4u=a719Q1h4eqMnLqOWd{ryZU0!S-c^cf72MH)5``l}N0MR^7R@-CYEOs{Q{edhgyn>O^pqXYOBBzkU1j-<& zBElloFg1sar~NxA9EZQWGg=6B8e@-sL%Dp|$Sn?Kya>hNPS+A^um3I_G#O|^1r5=O z-b>SiDm3aKLZHBD6fdRW0854f*kKiO3;)=q78FC%WCXKf5Yl*W%ef0_Pr#Mi6qF8t zIQ4{T#7W5Avc10RwwuDkFls{Uqeh)29l;tHuSyTDAPsexV4P#kf0V_LMb_$~qm8w` zLjb1x@2m03iMceJJ72^Yka)yWzyUM5$`+oV*e6iuHqJhTkp{dHm5wl@H&Fm}a*83( z!@_(3;>24dzuf26cRoA1u2Y|5M;$hI+%N{Mmswn%9)av8p9wBGJ>+Hf~-cPth@1B&z9~>8C9i}BRtuQuQJ1EuNVb98qY>!(m+3TJe zriGnCp5ngf^8TDOld6>LwGhXd8lPqH6htB0Vp+4)4f%Y7qMI|bCruu2Yf1RYW1K2x z2zs)E`1TQ7O#2YGB4ZP?=XMwNjP*+=gW)uwFhwVhN@DjWM5jl&p< zi9|&l4c(RyDTHiN!aiEN6BP<^DoaT+$Y1CoJ-1Msd|Za*jDjo?C61n)9Pe5gDm*Sg zZTgq+V<;FD3)FWZ>T#8h+4^=2T1l%L!z6OlDSKM)ur1uA=<#1<_>)M@U~_&hSwR=0 zOf;f*(BEK|q(D=?gZrJe3MD+n}o`lmOt> z)FHkBPfX8i2(A?%fU`E)M=GX4-=5!%G#6}pql~OH%MhgB5J0tO(gHwZar@}NCGSl2 z6Pym_5xHh2aMh3oMkSEq^91M>M1KRwDQflMMBFn+)B@UUD4`B;_Q%8loHyc#lXhAjsp4!P0BfA-@m8)eI($8y#e-2cYB{gBo%ylf`7#75GT53YdTp8@Dr&8)rDm5qX9A)5&7in@JJ$oZEewR{Z*3bk!umvZP#Sh976ji zuj;WeOjKL9QjKU`FDKEFWX?Lv>bd;IwYNr?J&S>^ApjkUH8U&u3hgq*IA;M38HnKHx&xP&C%_jH(zSd5dDj<+(6e(YmdsD+h6DXEKF}mGVRzA_H*Q zo4u(DwKBfMXy^R6=!_*S<4O49TrsmlCNz|@_3$?e%5X1tPk;3+VP0GTsl_P0rcjD5BYWYQ=GnKaVCDQ01gYjIJW zwzo%;S!7XdCaWSdsHrEy6SfgUr>o$%=$;<2Ri(=o!A_r7V5X5w zo@*J#D(Ez9=MofOh?d8x@Z|oQtaY|1wg4CqJeXiDTVHnn<9t2Q)mh%g+YBg^7zZ6k;N7X@@Q}}D!p_|Bl5M@pb>SfT?0t~O@emcsLF6yX2pOk$hqKK)4}2*{I=6JC$o!*r^J^Udx0On zel-KNwN}9~;adi7re@;nbYk@;@IB_?{Hhn1iAOHqV<6 z9XjkXo!{qTX>*c9<63;dZpnP`my{oE$uzIi`*a#LS4SCV2Pu^N)yRiMo{J&qioP<8 zU+lAEDK#s70q-q&gn@a9oHRpUXNxu_@# zdMoN|**L?2NbLEOW~eFjlL&)}!DT3pVI6Rcax+R*Rcv*tW^&#+N3kAgTV=jrgX@lT zs#{qOKuiB~VcB@F`E-OQ#+|dyzz=9BBy+0Zu43YP*U6pF!NC>1b$fkP)-CPs0$&y| zwy2d;gS?yWAsFgc+b4Dmq(!Y7UfY9m;{11BpOKq=-ZXpV?T52F zZoE+lJ^91LfmH00U;x^eS03J*=EHiqOzEUphS`d$t5|#}KIdZ#dz&rw+25y&kSE_=dz}lIK>nb98!xh7?f)F$M3l+Ta5y;pkt;h zX0zt4b5L8To2-FFH_e5DcPVJ`CgrHalFN0$i812x1|;j_6xr5$ls(m*T+DACzyl;$|0P&ELV~R5NcH zt8!#1+(ZmGid)z_6YpIkIBJ3y6N9?LPAHBbQbPK0ZJ$#V^~6k3^U>Q!e~}YQujvQ& zv;zbe$B(FB_Sa0I>>p4o=T40y5W#mkm+H2F(uSQ-uK4&&n0=J`9e@C#9Qajzlo0Hr z;YMfo{YgEKOqg38!XVOKOtlgJeXx;(J*&x$v;sBZ*j!8x8_~Q$ z%lb!+vbpxHWh~zOi;&wZ`herbcZLeI%3O96xfMeI}gy4`3s zIBA=3nQ@IGsdG)wsC}k-mZpEd;rd|xm5v>Rr{AH0|A43eyAIv>t>AgJrExYfF~Ufd z|EALNNm#?+2tK|jT!#Hyc-O^dI7q~9@>?%7BOwpdvRmb`AzmqV{~{ET;kMJpIGRm5F8U#1Pc{_H9S zZ)~#hUP-r$AKj7_HomGe2KLD{8>LELekQJNv;jnS0t`N7sVQ303>rxXeFYc$7;T8r zM?bAu088o^v<=8pCv$Y|tg298EM_;70l*)WlIs}75L{`k;%GUXW5HDzF@&qF>T9Nv z#@dIA@J0>a6+0;TvX#dByI33NY;hiXDOjZ^3{>SP_?}NYf9%508Jjjeo1}DYWeio7 ztL*uPd1tfkTDCCNRb{?h@ypSDw&9sP-YTfoscB$?qI$K?ixDYU@L{Qd>xOT>f

* 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

usSsvg<7z%t!e+R?ebUMNTQ^VLOKy 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�_#H5gXFw)-m^6g9}*$ z1};g4SE;1yR#FY@ZX+FX+_@B>3POq%$C-r%MeIjXh+Ohh=|#dzfAmx?DrjWgGv{LU z(C6k`1$+qmEX6G=5Gvgp&iZV~KoJk=-tTdG5J&<7{=WT-6Ng?msIJaeMWFhs2(153 zMG*bcj@vnU{MV?VY-2YskLpWHSFu>n(2}QQuT%o+ba8lqPuyVY0JI>4HF|e8U|TI} zETkj7Q+rloR!A}5N4OQkoT0(-pDR5On&F}I$-c|n82HDM0JJo}T(BO)*{U(62^0NdD0y=9?^LBiII7wSmJ7N@6LiMB}zw zW;cm5Cp>mWq)AwIO^Xgew!Iv7i26??VVP;u@H1!k>1HpBM$V}6EvS+vG&;`o;>7Gr zm>h#ICmiy`+7NH?8}x&Hl;?>Pk1spt50j^$_WawC_ByVgH-m#%4S5({I`gG10eiEI z!CHT%#G=;8EjT@?-eo5d92pnm*BwDy$R1@l7K+aLY$n{8sSU&e!z*ATm|z(@Dh=Q+ zpmCWlwI5|q2fD};ZBOtdExH{nPv<57oJ;=Q_`Ynx03fyp$Mifd6xW1{LL>I4wl&0s zIXn^k6~~IHNRy>@x=50NU!gkPOPQe;_bjc8?BO{b(j+FHCp22)kk$EKip6G6r7gf> zNF{v~exU8TIcCE-=SQ?{7Bn?#$rtOb^b#11$iGg=yTPB`4JT!^6==1bNh zcIrk!H1{+*p6-0~a~|D5Po}rnOn(8%nh~@+Bx-iML_kvFJ||G7Qt;zn#Bh`;A|s1m z^8>@z`uX1itnyb!^TkB?Prz2GX(?-)0X{$5?Dg3^;7MePl1ACC^6-(Akmr;WLW`I5|g0PLprk zH{J4LSl#L|bbA!KwB*l>KR=*z-Y8+XrzV&t->~prIh}I$kA71BkrZYbcIE?X3EN}|FRYM zRnsl~=Z$JE1Zwbpau)Kp)ujm24i9;*Okt(8V_!Ymyl8wgW_?EeyoDnHM;22g1CQOL zTnD)&aH`TG1`<0=?N=o?a~Bh6o`Y5P{yejRY1EjY>GY_C1$NI7ufs$AdBarqqTYc#D~J$yN$X$YxM<>L0S9F4l}Z4Sm) z7?aJ!y>vSN6|TvG+B{Eha#2Pi*`h2QmAA_Uon4SRq!W9f_%(`P$enZW7_Ev{mr`0K zN8Tv`X==%~wNw=c@t7Pqr`zys+EFTf;P&|e{JrkW zvf^R(fg{7zSzRu+=hKd{xBP&RhRONBck+^qt@%}{wr z0DFJSO=Z)+HQZO1)FCI}rtF#5H+MIm-_R`7n0e=Fm2XcLu=IDlJ*UK=Xg}mD)∓ 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

kED! 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

g^u)77r?eZV(ND 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

#xHYX|~i_53MdjkI>q_ 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 z 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%0A^wO619 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®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 k~76OT-2mKRh^?HBT23gZZsWETT?CDOg^X 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΀yyO>*wP`-*M0)HcnzhC9fk%ZYQ z0UtSX;mu2lrXRnjuiXpmplQbBJcyleb^fVfoctCZu0U=&TZ%oU@SsEce$-^JM-?O; zrnlLLyUK9$D`U=KK>3zEC@OUV(hL8}G(V*Z27Fl93Wed>BCDwRvemPlnSD-w`e8w` zi~_~#&##B_w~NDOpH;%vuV-fP2<}5$BUw{Gw~AC?g6?vLjKw%*MX^T6>5|J1C_`v1 z(3_l0$9*`#*_QdU;Rz4AdAI^2AL5_gL^pe}jiJl;1dG?3PrbMl&`%LA%UjT8aq7A~ z2J}CdT2@LY%q0i`Uy+Wn$7b~axMMM)OJocWq!fxWr$SIkvSQ{=?}-mgw{$`qnVwBE z7xx5c)qnp>u2<%gI%M-_4jO)Pz5gab!ra=<%2>(V`u}IJGW_qhhwPtm_-8jGMI~k# z*rH60AEik`*N=*jFyPm6ep)`h1pRr*f!ap!1#Vjhl1~chFA?O=t60Wq8%P9pm}YU; z6BE-M(;woX@AvB$axZXa4F5=)*6@+R;9Rr;429I-nUli60DJL`*nkr{-U#AAR0tWC z3CkqIpsN(u!Y=>TmL5i_RR&kFex}US%?nwkrhi@jvg){yp1_Xy>b{j353`0_qCm zCCdWx-MbuSayZjuBYO5YgG4E-AY~?W1vODZT^w;rHZBwyLUn@#`as}a^-#+Z*{hQ( z@WQ$VLyL0UB8bz9=t(zNxoIV(MGPi0PXJx+k+r#usa*bVlKoBfQ1|?3)vaRFTM`23 z1z*xF`!k_n8m^SfxBjxGEB+CnoU_vb7Thke)7F5(7-lqQZc*_8XmdFe@()p1X6iGy za8;Ny!x~`=5!sv+o|#7s59$R>$dEDH)@dGEj?^;MjZU$U!LxjNn9qhN7)9&&IRrdK z+#{(0!csg<5Y4F^*8=qHz*?Coqa(LcQ6jmc6e2`}RNmO|VPfLi$a&djc`B%tM(V5g zbb~rEN2i1@H$C1%fh8DK!TzZ#{WP6*8;>Io1aV@c8K6eRjq+@)^qgb3w-Wp8N-(jE zl56tIGB166YQFZ^n2%Ln!dGCS9`Xx*4X!b))m~T)XOa^7pv*YBa~gw7Ikk>3@&_rO zSh=U(C%f?GQB^0OY81kq*~cqzgX_dwV6CqrxdfC%5uHGZ6#GSSA_(t9gccNCKK)|y z#EsShFUHVGS513mmibL04vCUyb6k0O(|AQsJW%J9esThFwD9G(v#LwF|GGZ@i96om zD1iO?Sz}KAxjz2Qe*XV^Z2sd8$yWCKXK21@tU1UO(2WyY4^B=@b>m``p=IO~Th5F$yX12haqhDBGA_N+nosJNAJCHK#PsH=2O6q`8$= zR_v=YZsSmAxVYW9BiXRSvYG6`a|~mNg-J@KVP~+kT78t~F(*zX@o0n3Z zEsyCa-4v3x+iv$pd+M;$U-Y5794co3JvhXQ?&6{rEDu>LE~es6z+?almYbC!)Eyc3 zlzA=+np9)fVxMqbs_`HpoR)f4UBx*mTpqXEY&SfdggR5~fI?b8Sq)~2?7eqZ)yH8% z2$wAFhD6h*ZL?tD{UUkoY0;e`h)fUp7SQE(?*p1{E9C!ym65RqE}S zjc|Fh@)a_UGy*I`4aaObI$}aRi{umq5?nof64PrBtLsTex5st@_sB^tua&L4mS9oTt@Ga?) zOg_H6TG3W;3Sd1<0*OQl!5ubSxwt-YZS;K6hEN4sJ+HX9W209xoLm$7RZS69U4M_})GCrysw7Y=1ZE}vG&MCnduw{GY`a=p4t;wvt^KXf zEckf2oXmXgKKRDH_S}J<>HZi*1eib}2~z2#3u5f#3xeM@L2XI0KVC+#82}$zx1+F8 zz0E_o>J0@Q$|h*styb_7^4g>!e=2)1gJ`&AxV4w1xS|Qge@;F)Xw@A8-v88tc)x{2 zg)ehc8}di6xD|tleldcG-DhC%kQzE5z!@Y!g=FB!->;1z6L3SyMS{vjs*g=FL=hEa z50Q;*n<;hU44T<@+&{oX%~ibx1(o~bT%-`Pl|H**PE_@0an*W|F~*omZPUA`ThVrk z-6WsIq{sksgH=K1!Xoz1O08=KLxDEGU2r;MHXNvQ{8=5dwZiUo<9&UWDrCUf}J->bA_E8N;j{~&(QvBG3Zxn)bz&{0#;Xd5PT z&JgLK?l3v^3`Q60+Nv#26sxB=(uc+07jIuw(P;}gOhj9A-Oa5m0uM#ZtzBhuVA*@8 zPkwt4ToIMi^ol}_+Oe44o*`x{6rd7kNTE=bQI7h<%eV^?6mrXyL&#vj-Q?g=)>g6z4wkAGWCH|xmG)z!^4a(V zSo^|bD=TEFvbE-*C0wnuSv8)*h|aV!;yeW_bB0_V4+OGq>NOm3^Vv~T_B#>X%>vIZ zLZzjxcNBsY@iY1A;Zwy-t0!+65u~H1GDuKrics4c%;G5Pa=8t&ENE0j-5#!!7)W2F z>I+62;lyo?C9*I#q!IX?Lx9HN2Ew~w#EszGp+umRJ6N9sW1}wGWG0d87m5`+QIIom+!P6yWhvI0 z7ZC2sR=h++XhW%>0-%6Mf2(qoMPSzj*?ofMbMP8snGZ;G?gYPTo#tCz4ir((&9My2d7|A`}T zVwNoMnz2JqD0>MGu-(!S?Bb-9`S9pmXUADTMuzD>h4U)jTEp)2xw3e{@2uPaf5Zth zU9(rqn#{|1_sG|joHsx}!l{DH#4@iU@QjK#{)#q*%HmtL!`m*_>jr18upO1ijA>gy z7-iuO&8;0acf;c|&QAM?o4sNB&>Es+{y^%<=UL#Ub6b|ZXq21fZE8v_HV7243Pt3L z*IfOugzf2ruKbPV6S>1+Pg>?d=~J@@|56abw>JYt8Hw^+gwu4nnMQL+m-z$Lcj3nM zLvTRT4!0sC#CKnpQfKkT_Cs{Q_f`R~V3VHmt7Oj@XJr+P>2aDfy!H+xErTky+h#t| zEdQ}i&}`CsXl9H%*0_QXyR4IOOj;$ZO;;AX3fn4>@u0XRoCR~qIi-O%QS63QuF#NH zqYX`}J(Qv~!P>1v=FC2T`QZg@BL73oXx@hGxtqaJ%d#aIOAoq3QPwExaf5=Zo)tYw z4Xvt>**KXe4$++SibC^VMt6gFt(oxYc3#e=oh+mj^p3EPDUUOjFVs!;u#lEKxWD*Z zM20;l_)3p+As2;%QMqS<*T0}aI#YIAH$_zD>I->6ks%Q9QM4IKmnKsNaIB+(UYIiM zmv4$|b2Rem(H<{FXuPNIsVWFX(p_48=pj-4Y*^`X@E2pJ@Pv*zY%=xmxj@r$h)}@Xss#v7BHnC4JQE zo}TxrFNU%Yj+x>BKio-*o}(IWGBi;dS+O!)(p=UfnS2LHC0_rByaAW$x^}d&mo0zV z!Nj4a*K>i0Ph`u*2^=ZB9qQ|Ln1fdIP-{riio&>aVP}rdh9vyvZ$sO{Bv%9MZQ(0d zqL|FMK~EzXor%M)xU>0CN%Jq(uQv>iWaeZX>lDQGB>UTmITZ;A zgaM23t}MJB_RQ6Qd*?%^6`gBgLxuols^o*om_HFzN~b=kpAiulFJUuk1^v6WTwq2v zb?J9NY@?m~-ow)7IEDbhU&(WUr5EPzVE(6Om-H5C&Cu-5792U_06XCdi9wZ@}Az3-(&c8xVv#J z3!~m&9|0_Gf5{B^G}?8-z)SApYSj!;*u@bHGBWFv4Jh{)szq(B3L2WkpHRjGC8kcG zpw_1{UM?ftyTuKyFRe8>YaxeB2VS!agD7>153x{gSJL|*_lRlR(AN}Ab&9#MToFuz z2wBW-o4MY{-+SRY-#_c?IBwOm`mXARpL-(ST<=^IB-6b-nO0S%XvG@9;f;9F-{G+B z*EZU5tQ^ZtJk}qB*-_GLN_rO+qM)VNRf%YvCW^ZQ^8lKsmKM;SEFEfHFJGl1D%`3F zm!Yr*4bm(`uE6;~Lpp|cr7z_hN7AiNSkxDijcaiq{ATUhbl{OF!A}I(T4z~ttilLy zA09}~Ym0wXOaMJlcpNpL%X)rVD{>QcA!~FOm8I91V08TD$qhnsGM^PlI_j}&Vj8E+ zZ*<}Y^`=Gyaw1)cNPF!$|t1UmT#&ADXTmPmuwYIf&GE?}$ zgh-iNn>+nieP?zw{|^%MhpvO^oDjZSsnua;od_a}00RO8cmiUr8AZdRJ-cwQS3e5K ztyD`a>8c{H{5^28^m=`Q-b+Oa8UaL-S6L(*!{?>fUuh$Jr*Fw*>vh3Yax|Ait=}F) zD|TXdKlJ+R9cU&fIdPepP+G5`KH2Q;Z{YdqTN1m8u`KUVJ@K)yA|k5+g8 zvA9J3J<0HY{U3_|-!B5r4vtRl|J(Z2ltq^R*;03Hk(>ZJ^64QbFVY$pj?yATg%U@N zn*}>aRBtA$t>0R(YVo`6%hlh<-yOq14iAU_0Pv0G>n(Lm3DD9(VO`^JJf3_t=}uq! z{JPkH^b?;Hr4JH9k%tjQ=nF$B*mLDW;C5gm6;>E&je!hl4{&c)dy?=;(d?f}#Zb#2 z;I{2({#F}75%gZ(>on`&QrAU+m)co3rf004=pq@RPG;9ukEzkfkK05v7}*n7fbN%0 z@!lZVY8uZO(dKJ2fAdL zc(7`#!BF;HDc%)n{uR`N9p^Ss`vPB8Ho}(LK)ovB%!y{<-E_*ShAt)|@DVwW1s^tatVYi#d-PJB^Ra zlZNt;e@KOB$j)!PhsY>&Q=4-dV9fTi=ZmE98|kxOjwX3;t?Gj8=l6TNuyw)_=7fbC z^3OHAhf7BZvJ3(XSQ1?%6NZ{VZ1%Cui^zC3~X0`cVe{a;M z8ZqnwdqkweYn?Dr+lcXctSP{>A78B zKQ%2BWAg~|DCUZn5+rJP1@SaoJnC19p}3McsRU&4AeCeokA&P2JGEwNapA`q!n3Ws1)VWi^sZ+XUW1W{iB&N*>r*qW<3tMo%(7CX&P6}roIYPn$c^?#)Nr4;6gqogSZhBy#DLV&+ z6#cxzAbc>uMh#7n7`1K}qE>&b_8T>#VP61rT-=W$@2Bc*GxRKv6sDvIdPvZb zLnfp~xi&JhVohzw&dMg`U5VqQ#;X2+-))w9{IIVy-y^MNP>b%>(AUsSGwETXz9KEH zWH&6g@lC}E71z#3PqMjtt8MYJel+Mo(A%F7v=3;em<->jfXlY?7i}ylRKxuOVus(s zY^d0;I4YE?G+3f$d7c|zlhdlPjmuMtHwaZ2ZaIH%R9XHY+X3}l1)8ylqol#0TTv5P zvLZZtT(QE`NvM!*?rX1{)b!A7vb{U*-V9Ur@{KFq3Xo)fQZo==G%RS9k;~#=&51}n z5p6?i-$<#CORy^HFaUc^HeeyC$i9$njo{qkF80%W*!i(M$YV(Uc%T9DONJ-(SY|WQ z7RBe!GZ>j?@`=Je(jaW7`97I8;|*qrbf&1Z{<)uiPH&rzFynQ7?=^n7q^<=MdZnT* zAz8(1^+iU;RAIwu@D0srTKK&*m)F`)vTyz?kDs=yOD??3h1I?OJzea7V#dG`0zMKW zYUlP-yk?ujo!4)nczYjL#$@I*U*$$Y!QH?>5pF)VhgN(Mvm&e)(^32G*R}NC?y>jX z_~Gj=Ld#T64eT!@GEv!QlTP69eF^+sKvC*uxdg8)M@W*gSU~wiFmv)ia+rrOIK~r0 zU3&31Vc&%PdMnM*WQ6?|aFAWGmo zjWc`0U1%?|XT}DbZ&xFNv-BUdN6?2~(=_f`;r7bpzk#DXqc=RmM*>*mR|A+oOK^)7 z93t9+R-L_3n4(x9*j6~I9NCx~-XSvx#O^qe{=HHD4Q{gmh%4Xt%@rg5EOUz1z#XHg zF7~tf2o^|%5Ff*V9oZl%y@NQDlGH&i;jD^bMIM%}day}!J8XwJ@vqrv7g?F{*xeOw zv~~kMQl?Y+6?P7x@;h|$JF)aIJk*Hqvj=?YfOLry`WR0T0+l`%vJ;jpbw~-x9(0Cm zPWsWsT8`4)OF2HXj)D)`kb(#}`rEZ~H6ww;+q0_pKK_Zi-v) z3-aC^NGnCMiMy61qRSw#>UyX#AT9Pyrfcx3ql7f!?y3LC1lkOh;y|=sZaKwVpe@x} zntT5j&FjB959(3w0tSHe$0>lY{13^>fA2j0*=(q8IiaYaer=huXIQt)E+Z4GD>kmW z(gY#Yp~fY!(KQwp#revM*mty%O0jnsG~}6hUrS)!^kL-qJd8Ow7jp4ELp+OxA8%VP zs4vdVr=53sUA{bL?QkC3`h6X&0Q^!!c*DUq7jLl9UG5DSB|5bBA6$_|Xix9e(d*HO zw9q=Zxt6c}-9O|Fvd#2f=8y?FFbsM<4EC{-Entuj>2)i7@dz&2`3 zC%W}qJyM!gb|%;aM~b@4*aLog89X+l-3ACDx-&c{#7dFSz>X*BAyc8@#?rHtpYf<8 z|G@Hu>9%R3QQZxD5-$xx3%$}8RJ*~HHl9I=~ zYd~_XO54(B($mPgn4nkb5FdW?^M|sg>ud4v#`NL}OwGsPPL;%_1RbB)y=#223gfy% zWsgBnf7!L}tD_GYJ3FUgs%4ZJTjU=}KDU4HT5`Ivct~)XjLsU}GYv2|N-*{-PS}An zW;pWA%YHe_hL2lN;*{>T`m;Oq_x@a++D!)^=@qiZw0GG40sa9lyC3DxK3kJ#*&Ar5 z5A6ZR3KEORaQ|gRg2_SdEq#DH(B?xrXn*Uc5?Ji2*T|^76|3`BG{(^6hhfulK1kC{ zFyOzHfa+Z%z0&J_IsK8WZQ$WqqU*&QVW3f*kt#qVE4QURilN2)DObK|5k9+vRnVMz zrz%cpm?X#I>wICXD_bsUiL9rj*Fuxo4PT^PMml+PQmz1gGO;9=r3kqG9jZAH z?>RYqg9xrsi}4Soqa+KkNkXX?hBj^)tf5({Lh0Q{qb;* z1H9cnqKgxVqs;vJlPLyX%LYiIf!!S43D}(T7C_ z&Ibh6GprOl-f=x6av8^cfD`N8f%mDXky1rAs|ym>#Osh>u|_nG^aI*bjS=F_a9aDy z)eW+xf3qQvDnZf9Z-@NckhFxuaFD2PBwsiqyn<7bwst9GfdH z`ONHu)732liVR{({%6l676uilux>#bRvIb~Z{o7V)rP zePk%g`P{XNmM=y;6htOEWq`xg_zO4?xWjGyh3H%RDW}O`^01`|8GuM!;s3V0VfXbU#6TLxKJhzUp zp~(*&p1&cvgPIoxMzk&{nH!t9Wq_A88MLP1_sgejHlU{HL!roRB`HOif$w}WtKr!G zGPAA6*RKP(Hd>9(QvLpVQVc4RuiSmlU~E7NVFI>Er>tnhh_Yp!H)Om!9BjZ^%+etf z?zP`{-$z8e!HBrpb94Ni2bktkxENL+621xDRS#e?5M=F2%nXTQgiIz#4IvtumKb4) zXR-`^(O=!*NV(s{&TX6xIa0fB=_dQanUhP6l2t_HVlTS+(nCAsg6w;e6ETX!vydEq z^u1wG%`U-SNXREUqBa9AR1&(wtKtqn?O6wao6cV^7lCtd&5mRyuDK*8ie-r;d$Q8! z24ua|%fO3_00rJxVZK`qbO&3>m@jxB{M@lxhfs4Y>;nk~+AU--AumgqbRd=cwk0lW zxvvv$cNr)3g1#Gb(TRHnQRmw-Xp53GL6;BYa~L$T&4MP?pZtik*xD%e1aOB7Vr@}_ z8-vqdOFaemFUY?m89!#e?jNjt4)9Ko$tQdAnZ9?Abq2fyJRrgheL}{<+(Ts>rj(N# zsN~ktAid&#{@c{oQIz)JC;$mD|1}c+*N5}M65waTn`m7>)jr?2d=*=6W1?0^^H5Sm zD3XVW7A1VYW6W$_lJ*x81ac(5)a9>%yuaO8402n5N<>t_a628Jr#W4AdG&m~-oo{w zUkaP=TZUO7S+?Tm`Qs3&3T?tVMCP^8H92#`K9F88f_9h^Vxn^^MjkJf(x208s|C@z zNl&Z9YMss~G1$M=8-Ek(ENQCki1<}Rc<4*PUQo$UEKgiuZ|tnfSw^|+VNx~w{fvud z#IUyeA#4(64|6%8(W!UmhO?aMrFN1skjO#pvlewljFx%xEtZX`(X7H$oj1=qDe`koodudiL0kGKnuVL}OE}s{ci$73#8}v?#=R%r%n>DK{$f>Xu zkOmdw@@s1~4P5$i8ph(X5~TY{Po1br!N3T5&5#HLIyDMXs%BYNn@@u*e?mAWRMfBz}>@bGT0{?y!;&bkUkMc=LErFaBeP27=jm?z-l#0 zcU_QYsDv#-rXM#ZzNg^}hdPB@_&H)!ZPHZTr^=Wt3<_?Qn>5?OIO8NRdSMVp?weGs zKisQh9=WE7zyZ%L=T7Yj|tjP!WLZ)z@@ufOWNxyQ%9JEA1g-(XP=R#g2d8_^c= z-Zj<@2_4Wuf<>j9ly2O`mwk=Ei;@lFr);W08@$a|b`8J8AZAepawDzk)?)eLRvvh` zs)ROZ@m=L!8+Cz&#eO0HWJM68IM~D;HWr=zHyMHu(kGSNyOX$xg@*c&Mo85cLq=aV zd#J$ys2k>Hu+(hL=-2pTR$s49Csw(t{U+n3;sN%es+4}S4yQQYc46$L^a)i2ey#w$ zzNZyIZ>JE;_FTFD0*fmp#B&H5c9n*8|1g$vv}DKc#|R7uC`{|C>QZTnhfbd#^D763h3EW}_0v|v=w{wf5j zYWFs5+{Pv3i6(4RCpC)lFwmdh{1Q>^_Nh&1n#qkXv$)Nsd7X}r-uiyoeY-R$2nScS zQCDxUUkdTJViQ@W({7oqvk%N`8cCcMK2bhW0!L?JX)a87VGBGB{`@&9%Z3aa$y+wK zK#T|dk%@p3LHkf2MgdZ7!kGg$(5rV3`=Si`VaOy=IIA^6wq1*y?gBLXf7Kpks-iFW-woF&e}|8T?;%nJ z06uE}HGKZp?NsM4i!DHNBC|pl4G?Q*tqMR0|5(+$9z&)S5eP$1Pnf4j1FSwaCZ;iP zZ|oG&qock9eNx`oBIn6OEC;7?Iz46bK5cjL`}w>9=@ZQOB-I`nY>B~J@kO}*$pbjo z4v-6-{;)jNgnbchn2_gS^69HxOwsg~4&^nF^N>?0TxUa4)3G&i%>jq8B}4&d0KIBL ze-ytVMM7P(5HrOb9v+ko0u2=nEdx*`F^X2ft_C9-oX8&-H9K)0w73JV1#8{AYThyB z(#~l?6ah~&mCgZ*ZKMhp2s#dUp(9-|NSx60&c>B(Qgmd`7TNY=0mMw}Hz-i6O%g_v@ua9aM99E{x1~Mb{GicReA5?LZi(?2WSJkq8TY3uFYh_*M zZsCki!mCe)K4KACvyA75ULO6dS!ww^^MOp^V42`au-SAb=bg_^nv?0r8-GtXsC=)P zgLhagGG?DPFjuvO)FJ+`H?T<3{-N$J<%EX=s8p$arMLi29i8dG6%3ZL?;>X3U)9mZ}z}v8(ood3rYkc9(674NOgH#{i;+LksA@ zP5MQcR+p@jJC13COt$e<)8r%GLc?z^XQm8a6)v>R7M~C!8ru>pO#zbY^XgT@*@_sF z&gACPw}8iEgt%FlbGEgZr8}NIP*HbROM29zejgL$$yx$xQw~3KMOYNCWd763Qax`Y z&Z20|I_X&qgn}I?TpQ5hrY=)gw5IYcxDfXNTTav2J8|jHYv6}Iqwm z(sPB4HRRZ`_eQzU(jVO)aPj7A+Q~-AK(H-(1tc-{0u7R@vjgY#~761Q>iEjyr4;f*koLs;G_ zeSG6zIDHW)reZ1zmfoAFVCIaGbM}r%k6|su9Da;kVvI0Sw#RFcKUOzO^;#GU={u=5b35<#rSvZN1A4DLvP}KNwsPiXx4B(eV{5xvrNphMr`RI> z5t{BIhq#5F*9s35rTG-xq;sR$1wXeCIPJ@~iumVXiSfhckE16ZEJXtZU-YKcaVV^h zwaaKrLJ}G&O#smvB1?Ae62a^!2<`8MnY^OJ_^Un=Ii$ISIgGI1cl>j=6*~;?%=?-i z2`M+fc8JK1$0m9R`(~e2jpXz`UZjaAEO=mKongZ2nOx?Pzt=(+uQ656b&ggWWg6b1 zatE`>5{tYK#z19)C%J@pOKVtk%1~2`W#_gLxUfY@9^k$(8Rv)|qLCFkWSZe(m?wTj zJ3L|lbqQ?<2@Ct-wPKQYiZ*~AFoinri7~Mjag%GT$rRPhjJT;dbe1Sf(6uX$2+{dO z(7re(p(&wGuuP<;wpSB8GA^PD5s;xXNnhR|USd)fog&V_S`FTvPRuagTNCVVUM62A zdy{pF!wcyYHkvX>nL#Yf_6t|<`>^``R06EOeL)u8p(D2z%7>nAGC{`=_WZr(QwP{+ zg#q4_ECb$@{P?djU)sRY%tYG2)8p^CuWF+NFvsz|BsY*))r6$iEJKeoOb}c!T+)og zW<0S3UX_>hv0|Sfqg^{Ub!DG@jN~7(%$l2v*_Y1CW%hN(^$Y45M4H*6RS?IW^=*6F zY&hn5$~?|=O5FK;zcKrEP%6q8T)}w18%^0B59QO|(n3+lKEy=noH)0KO#6Vy8WYiiG5Ov0cjzwHt^-CvYZ!^8Qy2vWqpx9+&-YRB* zj-Gc5@lMkvfJl=;GUaFqcbdTi_E+!lVx^*H6OZox^q`=vVMOlwmS&SB_ND>mBGTSG3I%>1BJb9Gu|x>1-@F05AtxWY=>LMHNz#nUA^&q(R53AjiT?WJ@^ z`kA8|!;<%l!8-8H-hh5m{P`;}&;uUqfvpRF(j>}et=wjKK*^~Ib$aY6`zTz<;@sjA z5U5`~@)Hxjke#5+XTh!05NoxUv&dj=$_rjOZ;w%Hwm~vz1Kqjt)?PYADlAfS6j?c$ zK!S9-aDwCptDT*_nXQSzfNgCzOIpW#6x#y_HXPSv9r`*-y|iq@iCHWsk16BNXnaZ2jhy2_tMBBBy~)Uf6C zt1GBU2IHlawS%_I+Am+cLku^dmCTu(`M=Hoz+CDV`D#C;l z)tK9^U6F7NvbCz}E>5On*2D|fi!{M6(7ZMX0%x6lAMxZ2{UR&y2u)n6M{1P%)jj^; zxO|9qt9-VAEKyp1;^Cn)$k`%z)!X;7?x#`49TOAGR6>Oo&psnLk#`}{sO+X}mEE2| z)ebsg-v+kMc%$XqI3vC7>b@1`uAmA#@?dIX)ohv$J7(mD0e&)48|o`9bb=cAEu`d> zbPtPbL6~*=!?~F8j!po@Im8)+^h44t&x2~|3saH$0g~vHZ~b7e1`)srU(g9F7HFAE zeSp3k88zCGv`4R^bNLdsFo0WJ2!ZN9AsOTxu)F~Wo&yc9{ld(*195Th{E5{2OSh0A zl|K9%8L2lYRjnkrY6+z=6jj2E;CcbA5YsdxRzdFXP5iA@d2vC=TU?TpJ8_7$57}R# z7Bh;}@$S#elpkd_l-wK+fh>3*-0qeb6l8~DegekMiOx11C&sB;UtGk9Egg zGW(@jTF1d@7puOrR(_vT>k~s&d`EDeaOWI-K?^8+nt=dkYJ~A5AwCZE?Gu@_5gfyR zaiDSuB&;3;l9`{WhkwVeetrWb2MJpF@cia5?`i}zFer?0@oS*2=ilB38`C|yG66bU zD8PCVKo$C%k(~{_nVp@PwFy1or;VL0y^FJjHQ=|ck&C0FiLEpJKZ_cvyn(T?iLtbu zk<~wXo2>X5$$=k;!#x%Y#lz-JpFJMev!3R)==2f40&-E2Xn5T>$fa~-+)NUmb+bVc z_w*j4)L5#ZV}~q z<~K^pLH#zh61VpxPYCkVv+zih5uM)-QuWN$3zjV@UFe+3DS9{-SrrNQ`zFFsO31&I z)*g&31_x7#-|_v=gV z-e1WZ#RUM7paMkVKQvnWXQ%m(NGO@u7}%TJ{m<58mV%5FARIm$HH(;4s#OGFH@kTc z`nB1G`Jn-bRK&`j*C~K-NVrar-}reT^E`vR6TC7Hu3=K2h-jx~u`}Ck!*lKcl;=iMEHr~nj#x`8=1kpytR<(ncWWm*7Bp9r!VHsa$3;s1s0^ zV4D&VRtw8i)h4~AP(BnD`o`b*+ISIlre0V$zyt18$b@$dV}NcSQ9a=G)#oNZ(j1Hz zlIRy}w92a3bGj2`Inu!@xAbv`c%CP`6R=5L+MlVpSGCDp&xJm^5i}jH!PFG!`T}(x zuMZXJyg@#-EpK#XPigBjR~wWpXpJU=Ol3fKr6ERg#gvAgV&L~LKBj-&1C(~~U-1F= z01?1^lE8nLCw6Woj&i2TZgzif2b84%1{#2Q2^?{pKspftutsV@D$9uH!k;{inkAZ| znD(0t7M^tRY^S7DC&M}W{SG0~Fj)%5+JHzY?6+BbYtk8Q9pydF9&yWN2kfQ3!S7<@ zi^LD727K3!*^LOOnC)*UL`slGr5;ABPe)uigVwPNJ1CCQVzOlxX9aBG>3f7sQ!Oxc zVI>sZ7SITzpEH?-p-D&bTP#X$YAj2sB#=Tk=c%bVkDxyNp!7H^o|x@2w{s|YHuRX# zy~S;qo`AL7u5WFSU()%NIOg0}Yo$5VOruhbq}4!IwU?%{Pgk(Vv3c9r>sAUzH$m9J zCnetHM}FJhoiq9V6_l#^-Pnv$b|s+%2PgKS9YGS#C@fVqt!I1g2xKgl=Bdy>rATGQtcp(=Q?t>5eU17ju94mOd3u2^TAtEo=Z z(bxvFq9xfaX$*T*?FRNMc;_S;mI7N>OGcK(hgxBtPsv<_ss{n z*ZJ8f1{Goqn_!tT#{%&*iv*c&rhRT0S0iweAF#R#PuvkEi%`ff4b@<^de}qj;CGo==(T%v@kus+a;0Kw1&nm*;F z%KA594DukSup02a)O|36G^UJg5H|4%HW;^DvSzy6hclZLJXe*mS? z8_kspVptw{P+jr`|<5SntzzvPeOYE}(zaJk|e3&Q%&j5brVC!Q38|xBg{n$3M z+vN&p;@jOytk5?E`;IW&YP7hah+u#RIwEMYPd(lBdjHj&j&#yf4HF@s-tA$c`HVTF9n4MZXtHHxf+68oeKrHhWP%s~M>I zN~X1+H6Lh`ppMgZ_TH+WXYE2n@kZJDY|ZJWn&~}x*GML~X{$5y`pomm~@?ho&x+x`@)F^CW6zjirhl9MM2!BL~8Rg^e z@UY^xZJO??w}z@P661=XV^I-{WMJZvG>OhS&~u7I=RXyl+QL;S^re1FMzs??qG1ah zb-%5Pc-f%(<>inM2g7b7t)DQkWYr?bC!1IK~J8t#$1@HkZdZ;;+8cIi$Ml+e{g8FZbTU^E| z9&}%jzu)jGu*Z`t0Fxg|aR2QM@84ndKO=BkN+@c80TAz6(n{!3^TB*VbI3kOMJu9w zD$5LPRM>p=o|GxFN^92xQxL-nrUkBro;>;sC7;v4EF|+Y=@L2D;;XDvt1pC&`^_oj z2=AqFhE1=_w&VBd5#D2K-?z7t93W?-?>~{Wp@@SNpwt4d_cgzHF_Nd8ZI2WwO33Gk zw;~CDE7-SJqHPIi+=SnUM|EKQE&!L0o`!9249ZHhszJm`CExVz=2CK&>=4s(fm38f zX*_VP(Z<3f+aNA6q^}A{hYHo*-@vQCK51>jJ3wnwo-EcuQ?Mf&zg5V?4Ajg4C!Z9l7TDc#p^fCzU3&|F6e>a zDjHFRAgR@TndNH%MY`(MeEtepz5@=1&fiB`;V->_*B&e{RXpko($_=UiCE6e~>P>mT5HngEEJ?GD8PjzNnQ{Z4Zp>03LI^8K zIi5Nk74^n37Pnd7j`Xu(alHQmf{Aa)jA=UZ8~%QL)YBaK9;49JSIfxk6MBsdTI-m5 zp556Q*l?{`7Dhg`PPYU$+YI#2Uj6;`i#g#xr|LCiy!xe$GFEu(O zaiZMog3u9WpVdwI<64wXg^v(}vIQ;|-v20hVt~92M#oF`{0j?TptUcX@Z`z99+x_U zoY4`%ssF?C)M_+39?bd;Xrl(Xq}Tz7+gIR1|6Wb}oT~5y_|JFO?x3DKES(Ad{Os5H zC*UR{B0Um^_sG??GdGq3qAy@qpWqZYdr3dUX=2w!Cl+WsL$OQ^QqXSV_D`J|GE=FkvQUJA_HFw)Jm-!FeS;O< zBNPcnG!pnlmX%KWEZQHqp|0|}qKJ@nmwA-tq?K7v(54W?g?PkZOm5L!B{4DM>>ts} z%|Bq?%*j+6c2(N!9AZ8#X5-EB&%0eFF5|=xfw6}b>5={*TA)xx@r`6t79{EVXudkN zwPOcmoFf^tg>pS|!q?Kz_Gc?oSHN%F#G^0)bwEMM%aGqNrd?BP7HP#noh zgs7RNRj8EqhSknNX^d1vv(x>+L((0opzbr) zXkq8fH7bS2C*sBGa;bYxK`ne7*}cpE_k#d+Bhp_J^4qs})NkMD|C|2epLJjT%ME1& z>8sM`c5DR7x)v5PVHXs6Z8e2)mvofO1vjH~6q6Q&(e^B}Xn>D)tIUm}mdXa@S2O8H zb|H~WMo?Viz0`U_;gIa+NR3a;#aZk}o!<|=uK=ET=f_Lhv3XYYt0CX^wXBSn^Yh8+ z^UIHi9)fM~*+>M^#yy<(8(2Rc8lsQiD-sA_NcarW?AZv2^X!NsbquoRVK~}RR%*x^1Q2Uba z9lOg6o*`czkn|AkV-FM`NRk(rDq6~p&0rUg6H++qutGFMn3Sc2v15^{ky{iVW2PrW za2Aj!N5W8TEZ$m%sF!6XOjR4$;IcS+klA>_BCTMh$KqfpJKdi@gB4Ih7*aqC$H2;v zK}<_a!)4p4JU=SVY0&XGv!9`0}J- zVjG!D6-R4Z4>Oq7TSldG3dPFMq+m;--M&;7r#plUC$SaKxcbjH9opW0j5&>akg3)- z(RQsYfGsGyaHo*5tJg8AjAgR1YW>y?Cm^?WAWX}Ygo|GB6ZC_H`$(@|3&r|Va8ZI8 zBtRk*Xe(*i?pYa^p*rPY!X=eC1)6ItM^!xhhObm>#u{l*6C0j<T!p*=2(yT9t35<_mae9WwCc$zp-HAR$WBBPPptUpT$ zV|0V=6B}rVB^fM)F46ZfG6h8Nk4vKQ(r{;4_DWPhQ^Z!Ty2Mny$99q`%CUXT3+(Dn z-xfhNqAr{ir3uYx8xq--u<9Tb5><#UKZHFYKS8A+lmR`QwrJQOQ9k2Up*+V8Z=2AonUxAFyIl4scIA$)MDBmrWbx!9EN+B9A=0Kdv1!_ z*==D0zX^&;bQc@EGJePR(|0%ifYF7zjZ;CdL!b-I2dLfN%9}63;8oI*E#MS0tZz z@@tk|2C_HDd~_ZKbei#o1}R<2{wQ3kqb^x(sADY1HDmEH9bAJM?8_5oayW<7VNCdB zVhQxB5OW+vZs!$T_(Tx*5;QNs(fD~8^x({UCdj^JX}DM+gz!KmSJtz0 zN|5{6SO4U*DkxVzI_Qza0U~?~9{ghjImivKSLoi_K2?rHR&EXL2S1WxnJdPd5U{&}+hMja@O$$k7;X_e3r~k6CGk!5`Bp$L6SgRqT@SPa*8bG= zhF?m|(i41j-GKx;#aDj+Iw^l=+KQ?%#LyL>nA2^Cdzv%hh*>dju1S2y!5m?g-j|u&r(6Hr z9`@MR>xWPu42_}a2@|z0`62}qCOmy(6s!WjofZfk0r4EVW46N^p!A>#GqcB~7&WR2 z;tD|>qLfWPfFCD5%^9Sc{~f<3a-|(L5`kF)HIVEXQAi%P0S@TgL^5A6^TQ+_k(zy? z(1$PeF`9%3;!|;WY_ES^o3N|1nD;_?-$4ta#G_efs`ryO2Nh?;*jsG97g=wyBo1u} z>-16qd$&WbiY+^0rS+ymS>G`=D;s_^z)Ds3COmha8T=l@a1p)=XMt&rk)bh93p>{l zd`254=LAg z7$W337hye2eaClwRi9u>5+Q0Nhvo!ch}7>AZj+7!QOOw5oUji-<3Q%8^E5gA*}_EX z|MOB~xa;mpnQ-g7P{K7Dgv5TREP}!4%OZ$%v~arScaii%smMqN%gA5oB= zdA+&hcbvL`f3KM{SMXiQ}0{HQK1%Pq+#o!fTyV~v!!X75d%?Vp9o0oHe z)YrdQSN}zWAlay{Ljx#~B|xk4pUomDn>(5q7>feN5bPX11YInwjsInBRP_t6u7LQ} zd6Zz4AZ8(?Kuj4{S8O2x4Gk<(NLN)@tY6vuJDy zGvA#%MnIdH5ov(5*swL`6mxq}8un^e6xL(X@^lLmI(1>blN^Q|U8Zxy{@mYQQN&=Y zMa83NUqT@rDdox&0ZTF1<>trXpYDr8S~U%qnZ)rK7xQT5dkX|f<=;_fD#0^x=4+gS z$xaj9Vlfgl`fmQ868dBM4pfp_22IBEzD0Eih1>>PtsBy9{ zJe$92m8fdGHB#`YvSuY+Ou{|e_T~I6$KZBcm*DawWA3GC6^Lvv>!c_SlM}X+NGw@3 zj4;<61S>Ghx~xwRN71y_K2<_;p2RyEvOBC=b_`vpWPd602%Qg9zz!#ACBTNlG$-6W zSSonZR2_|1Yq^pJ>lD4ceGO8aH7h0P{;9k~ zBjdNQQLHI+6Yf-2lgwe^>!g8WI(t~(fDPP=I{E(Iydup0CN#k})t9*nS1UhcU4P40sk$HzUJ3FfvNflFbtOBDt@I=)bADOpIzDTwQ;>J<;%vY^G*m(Uvy zpXJZ`dkST$8d^EkB2i992C0+QEH9}!QquB?V6PzM)3q$0&S3ZcdgH1TY#bb;)}-xd z2sdasSIH3$9#QQE@(+&ylaKXMxiC7_?Kk@6*BS^P8+#W@+vv>;59z=p$2Gj8>_C&#iNOn5;% zlJozt5+*T5@D5>7z#kRD9EN0poi}dj`jY7u8myY6*C*-8Yk_bgeYD;`EIk?~2-}8_ zN3|q=guMAJwkw014`zhoqd8=rUlTq+8oWWXNB)|DNk~;6-l`kU5|8;29|HxJXM#xb z%DMjx4CNh8+uiTs9c}d-(hbx-gxpOM^nwq}54x9Q8N3RwC#7*n>Ca)SkT;{)e{9YQ ze?VX?)5*s$D+X={n#>UkGYI#3-5Rz)N@jk6ig)WM>{oiCcjfo@%h1Xd!hUuHgv%Gxw&(Ql!R!Nzow25 zRTFjE*P2$}WH#3M9`b%!n61kac-v%gF}bXtCL(V5BPmsTqxP;1kwk)}C~9@Q+k$Yo**vh?;06782otlo17| z4ddi9C7YC*IO^XG#ii`_{m`e&9s6pXqnb!*qY%Da@ zfV41P(fl*zQ01W=d!|g3jn6`>J^lg(akP}y4jWi=LN!#Hc|a(f6Wycd;Do^%Ko3D|y3{^;q;*K0pe6?M#n>_BFuJwyz6>RzoIY&9bkHYy)H@|o^OX{%U#8312UW(IV1aNgoAj)ly zT#w8(CNjALClpq$GXdO83Ko+2xA$WoT!LC<>r5f^F0Z z31H4pt%mDBbJdCU-Gw14slNUIqH%|@Q#$wd7VDkPZ@YFJffaMb$lvQ!svWRE$LGM_ z)``2W4tTo$OiX%fs}Df9&iiRTMd2@>qiqKU!ts5N1dQDwN?lzm&%@JJ0Y{qvi5ubi zgFxz8xUZyDCTfrKOS*UOVNWHgU=#yrs-n6kg7a#)d{uL>SjvedzMY3J9y3E+dg^+o z@6M!W-i7tJwvH3XrV~!J19Zc^r^?mGwHltG3SHfM(-752^mvIRhTZ&5x`WN!B#YxT=>SA-&r?}NisW{8gk#k!W->*}W!rW_V9D$x`Do;64DTCQs*2R$qrx25KU~0Rn zOs+a6e|cJwEK5c4i)Cf--tj``;L>D<{yDs^u55mz)H+kH7D-;vuvTsJ@Cro%GAx56w z=7(1sK4}6kB9a*7mN_Yc=BYQCQeV;ecN3R-sa(YyA8DTx*lmJKk;7 zOiR$~7}>L^kkBD^*R;H9!r-r%6IG5)}_GTdOZ4){B6#0-R^{;*8yghVV`I2 zt#vVElstuEC-ogn!GzOoqc$Q_$o10Ax}><4WbR`FZ}32BG)qLEkP@9Bb1U<^ZUra1 z3X@@mjH$yhKIO+)M|>~ZIhvG3Khh4Aniy*;@E z$f$%kMbdyS;;~sFY92T@MD_k7UB49}7QWt;v{|1<1%sgNUJ&anizl}xh{sDSj4Pv! z0!dV=Pcp2}h$!D^ndcCWXF8Em*koqYV#Lb}%D?yjeRM>kRylEUBf9-)XT<(EhGmYe} zL8jum+F#l&~$p4Y5{m0P}EiWSn z%!uftVhqIpgVXcjC&boZ0U|P4y16(@eMnGZk;QU?t(9v>nBP<&e=mVaCK&OsNS(w& z8oS-m)Y;p}8O~3XMczf8bEc#y@9yY}!{EG~lbGk!xActJ9;}!``zbqdu{%-=-I=&W zstXt^VeFB44u1LgPWEwDgga);ab7g+%XxJs(5hXQ84LCRLW*VL$}Y`#by$_Yrc0Wq z)IByA(na;{RU%>n%QnYr{jy)`^R=SUm7@*ohqJe(ZQX{>7O+z zDjn2{vcn@9+ykFFmbiIprj^Jd3o}u5F>-Fk94F8?FL_OQDTT-)%g^2d*E2;2q>f2u zbQ@OH(q8ZPlk$2$^m3Gq_0@%3m!VH!is#r|Pv{h@ zpuPWPdf_iY#(c%z+9yCP@BvfIhW}YS99^880pmRufN$IXqo}QH;ABOr{Euh<(Abu+ z1xV32y4X8g0A#oSdi;;@HK_oUR|EhG&vaYOuuqX;#JOne0jPc4>`mf=igQ8W$cEz0 z8+Ei>m&;vY?8S=AgZ5MDDFc6^_A(5ar3dTV81^UKNgFy^CGpj*g{)`V0ZG+{@zZ=0G&ljE z-}NThFw<+<2N8m*6jtN3R@tNzWwH{*nu{`9I;Qd{bVu#Evxn$HltyT!goRP1qL6mRrujHeP6IPk33#AbRXF24=8J4xler&8lLZ zoOpO^yBc+&$vpv0%c&n5S#5E?OgHp0#IYS4kCO{n@K^y*ZGGAy{+8%MtdXai**18K z>vcv8DoyiFP5T%k7gMtyxm*uC`MYLo24Oau*;V1JI?H^^g|%2?gL9GpA8FqlT-mp_ z8QXR`wr!(h+dduJ>U3;7>DcJlwr$%sC%^Av?%bKL=1$EywX60YXRX?2?RPyd*Yom6 z`9#pT4P8U^vCY&WMael)Y3ynAkPb4daT4r8i|B*y=a4g8vfF1x#V~pq6D*dDhw(jVxcJHi{nCEYW6za+loqvW zH$aaEu)Z(}E`1vxxl0@p{BeRsdk^kEcc%{KOY9WUK(hIcvMje*;ztPv-Z_9&0YQ}j zVvns3>`M*}ip5pyTG6p6qMNBe`U2wW6hm7Ag>grr8rHlv34De}Z8r9YS{GKdm2;zFB`u<<3|1Bu z=!~~&G2xJidsr^DwKh?5RrDcZ^Ny-(whZQJlCe#21>fwdswJ3%^J0vPh)2>~7QlGq zh!IwYU?;81N}#9l+&Ceyw(&<|(`W1aK!rsCU8dP__l7?JY<~nvHh0|^#_>$Jc51h) zcWQccq$m?d0FkOHfS!>hBwlqp)yD#onx*Io+~`iDOF+5CN7ROsBC!&QrCQ6Z_eK@n zmzr;xS#!ZMY8QgJS|@nWS?|?OB~I5Ez;m`BWi;_hip?p;?pub#&tmL{o)zQ z&blrw|B$IL)mkWWwt<^)dY!!;RdRPY$?BA(=QK{}q7|XcaD}^rC^zvI|BvN_A*pNj zNC7Z#Cgnvkdnfb{gmdc>2D}%3cgph^UPlU!*wejclFQ)phEpad?rB$ZXfJ`L32_BS z>q$gj02s2;=Q-{$tH#J~&ABdB%(A`6BqIl{v(1gU$q!9`7+Uz`q7|Bn^FJdb;T-mK zq4G6u*pKt0ab0uWwGTz;Lr^W>a%Vvym(PU^B|55|XbWcPOq- z`^>#jI!}Tfc4-WK81S?}K4CU;$#nd-Ot5$5WG<)q9MJ_OFJsSGC&}ASAD{1U5)oI| zTw+h`n<|E#y@$Glm$8&m-H=ZTkBxUIs@kUTMH#8z@C3f$$ug~94k~~CTcz%vw%yLv z7nsm|MgJuKBQR05F*bBIw{Q}3GdBIw&$c#_hBhWvrVfszVs3vC$o<_%SJif16-D}3 zaH-Hxy^x*BSCIMsbGOcAq(Y%6aKRpjLQ08tQ2{0)vaMd#gi0cA_9ooT#;-2e(a+Tf zzSFO_d{5^Dmccv5#r0ry`Pt0e?DOOCQQ+%5wRGT5FPJ=y*v?a`IPWh&d2)lwrfkaD zQT8g_A%~$>vzP8ZN8=yFq@LJ~3TvgLrYzAI!oCrKIqt{eI*U%pZ+3VqEiW#Vp{VIB)btWj=N!g&;#=X@dS zs?=+eeE8Qz+FGUkWZY>KBQh&p_E=Juw!f1c4Nufn82h8Q&<)$VllF7aDZ5o{5n$Ar zcu;ncX*G)?(S@|oEZRgNe znHQ`$9OSG+4^xTgoqoo7%#kjT?{$NWR3T^8$ac4b)*Xa#MbgGtT5RWXqPy7caX*@@ zC(%xmA}03z*Box3f}}Htk~2X^8;6eY2kk-WW6|1Ay>kcUfuFVN%-8Qrdk{m^g?iy? za0(eK3{f!Aj&K_O&I~%X<*bQ&#zyduqMS?jtO2_YA3S)5j^Om*- z1(AUi+6U`>C zn`Bqa!9VVg6xuK)9j?}$9k=A$2oa_cxI|g=m^#v7rBelOD}~-^QSy z{=qH0OV`i%2e$(sGFq#IfzriFxXUu0BQI@$?Do$ z%e(Wl$&1NTTLGXp(N!^cX#|31jR9j!GG6`XZeUA7h1CrM74BSYXoHfC_f>~OLN3Oz%B!C&+pXpgeY6I@U2x?HHvtiJynKqZA z8PEbK+;`F9;$eoKTJBLF87Aa0wyGN!BXi^~4u!$oIz8#0R)yZDH36!KmKu|xxWCpt zk)R`6-+k~hhWz+eNS!`c!G&)tACwC#!j@q<5+2lRo`TCzQ`fam-b5y%zwchoUxTRf zdp;}gBu9c7Cb49b*q7Vr`qPI`5=7SvuXTYEw6B8j_Tde3M2e*VU5EI0J9h#E-b+Z^t z-V3Io#`{o%>ZwGjv!d%ijKOc5J!4Ah!IGn~c$1pUwvfz*SB1DIoJW@H(V~8a<*G4S z*oNgjb67c6cKJR2P@N(1?Kx1_N@eYalyX!SOvUUi+0WRB#Fa1I{%lJ3+N@E8;iTK% zi=QBV-2+7`n%Lo4GNcH1vA6`VG-FXpD-JsY$3H3^u&%x{FUjYHpnVf_lsgn)4hGGe z#7`KY`o$}2mvW*57BD|hP*4DxnWoSdU)T5|&~TlAWp^T@C6O7~|6TyFoERv2iQ9T_ zwmyAAlb`E^GcAtMB(Ie!9N4<|^=5^zR4!RAkC)a62Kh+1^R4hlRG-rCkB1~1+6|vI zIKVLjxr6i0;R1P>7s8mr&~<>tm`)l5YC@1cDsjVBRB4OcAaQbZ1ly=<*{hlSa}a2( zft|+;5oRNZuBp!WQ1U6uL1jZuav_MI-)3dQk1b@A^i0NFhv(=Gm(KW#%_7#Q&~oVU z#p@frnfc?Ebk7|3{P(YQX;P&32rxt&9#6;%GoCicE7<=vEc7_pKk`={_rm`R*!)fC z{%U1p>-;}WnZJu%oUZLGBU+IDBaK4+87TJ%cf*B<)-h6S6P=7OXnxKQ;que=WX%$y zKNN%h?{t;a_T7kE5=2q!#Ugp_#IbtWc-glZ97(?KAJ?c|(s*-Mx>-q zGfNMKWhWWhbRn3Vn3jUm8*?};b0}i_o^b|%WrULK2sr(hhp;Aw>Fi!nm za~q4B1w%82TTr(jCA-1haZA2;YvPtf5qc{h@~{~%ftuI(4A0SqbIqGKT@i>5_YPi| z+;eis%@OOyuwG)#z!#2L+363FCP%;x;5lZ5``8V^X19$*4`$!&qc3&xU(;O@_4BOT zD5^p^DOQltlO$9i&rh7zI+yS6c#T7!Nt3rROsr#~>B3pyi}~;`)wyM=AX8r#NXvCv z)qPyXKRNd387MFP)^vodCA#vP)9#iKbL)ZF?wQ^#y9-jfOa90g(~k%I3C+|4Q0sf= z6*i)KLxk*+wU+rs$S!2KEM_A4*DregYlm z$7&eV`goDW=Q-i}^*yO?^nK+48h>hrMb2>D2Fbl)$|E@t#;c{*5$zdnYCgBX}Amp9X>p zNy6mF$;DzwJ`fQ~Y0i!~&@1k=CMmk->Y;Sy>nY_r7a|M(%5s<%-=5q{J{di@0)D1g zqHnO+GKOR);o#vmgzXQXmxSEGXfjqB0G$;z31!9CANf_jEZ{=LETKMLsX=RleKW`9 zzh%J5kD3uSSd>d1ot_#x_)}c&+tXItJmKzJIgufAL1uDQp9=5^0H@@8Lv}N;5)XG{ zHH3!RKiaAzWGQ5fmHHdk!Vneo25Jp5!J;A+3XHQ0~$m>tkSFvz#Z7)ed(OXvT3&(%R)Y9@dO@pldh9Pm)Z@lyOvG53l9M3+m}MFGv2PD|Pq z>&&I=*0PswBR-A|!$6^fftJo)|G17b8O&S$gZ@WaE%?LbRTaJ?aPZ0+raaV~MktN$ zcKEPlE+c4*vRMPZvF2Js}Nc9)HA{+rpEZ-Az*8LV2FL|}~z4*AzPPh7%D!w|O8-f1q z*69+(ecM(yM}AEa?-O|k8R6!_H9lfT_@(=m_nRNs-Bz%}zQofXNP+I3kVW=MP@Y%H zZ({+7BeUt7T*V;iYBjD&2z46vjO z++>r6!ISZ;bv4|zmlfmBGNjTq{{3pYVD7e3Ri<_7&!GJ;9W3FkZ6STL$*AK3NLU(X zdq;^}qZx)R{gg!Oieotuo=ui+CUuRTCQar}y5PZ9a}#CZA(5_8i9x2PyE*l2=&KF2 zjEm$ETr6(HhDw7bq~0QTzlz4M0=JvkG9tvmao)s!xG~+!xvQtktmk?4Ygv`1RQu1g zYq}TRVkpZJwNkfG6+a)3pzCpO>q{v10)L=f34ge6{X(3l>}`M30;_je1HiJ~c%Q=B zZ3r$CTP+6HP4&!VZm-GAmQ~xUb5S>mM9r_M3(+~$%!)(+Scw*wc#eoitI~33Pf@Z;GWZ*{Mi)S4H7}7RVta zK&&M!_kRX)ovFz^E6arv;2afp*=vU{s5C-(mc2uac)R4O+TJbuf_o!JVHBy4wj4O? zSvvlpOOr3kSpK1}(XxIpqFrU*URqw!Pf*%mZ&mcdcUQxA>d6udE_ZDi!Okyp%#y%z zG!8~KnF~$W<&)71>Xy(7hqv3-w@vgiE9yvwAa}v*2ZUpI_yP3D=8zbb|0mYglZ#?b z8};Fu6_-u2jWtkR;LO&zcB-!RDvS)99A8Lw&_`}KOuzG2`q~HzN4LosJRjHznSeFs` zBAXsE#)Mm=%kYsu95DEfaT;kjjSf}GX2Xi3j%R>M{@%v2rLL%B)2jPz&8#OpvZ|TA z!`i6l8@$`R#9?B5OGzFV(P{Pk#T8B|b=Lx&_HbG5m9IYIg(UgHtz)+crU`-+&yY0M%8l#Zx2@Yc`yCH5UnKUsYlq|&&_OG#xjnR7x1T}`gP#~$FQ`wFr6 zem~Y%zAz0sMwj}45F*!N(aWvkXgW1bidL`5hdzisFi2wctjWig+NIcJ&c_~!Gy5i+ zhF2)!o0gtd#1Q|BD+|X zJ3Y3V@WJxu754m%M)4Kbbt9LgvG#f?IdV`uHzQ1f&DCs&DTTb$YqZap5g!o}c?kUv zMyL;)e95T8VACvdo9#81iDCT6wOv_}Z@#`@Ge|>? zsGL;ZdEF=x%-})t!03_zv2on>ZEdzAiLR~$2zmW>SjO^l)2DhkJGos<+J_s~Ut&U< zWLB3HufcDPoc`n1VtaXDW6jvIgHtSFv zkUDU5BitXNpg;O;&d^q;zg-0D&#^?iZhbq9w$uALKO8nKNJFN9ptV^HRd8MBAy(`F z5EyudxF-jw6qb5qj<7+`_}CHUU>#!nti8k473X4&E& zW(jkOqq?G~xMxqnn=GIosmkwgrd>Q& zDd}8NtAS)$^$5cBh+6gYy2LW0a#!(B&HGYq{Bq6Fglq-M0`v1%B#OCQC`xH*;|Ybj zsw(7|WMouDv*vb&J7=NygsbGvW-opC+I~bPv-|GfjYT;}k_eSAS9jve)&0*x9O@2+ zc6MLf_J8wqZ5@J~Kr@ zSh)JVwVJ1P8TKmLi#*oN{R;9aFTxyyxG$HydU!0AEBVsPd@_|#pwk<&I>HIo8^t&;7R@L&_Dn*$Mj1LzgtSj&thcLPb&&qs8XS z+a!hste5yoOm$4`1w3_d7m1Q6h9M59gtJh~A3E|b3y6W~mO`HQCK&w5 z%0NF^(;|BRAXbv!UP#3x#p)uwepq8%Mc6_t!%H@7Yr(U9v4MfeB0R?sw9_C(qHg7u zwza1pM{B`J?Wo(*5jNaG7FYQL*UicZxB(hfgggw3Rk7!nzZj~th8b3~UywgKs{B=g zhgV=OHnsWErB-W?>`_s6$wrY<*4y2@w7DVHRNm^=H9>%?qsR238G{nD%)6t51(F53 zSJ3pz^HV%ErAJ}1efVIHjT4FUZ(s`}7{h$wjcw?hij1O%)nj>^P|T28%`RP!R#4VZ z@I7kub#6b>``8WFC^cx)8nvT3sYm`eQ_Bmiu`~hkiI<3uys>NU^Y=ZdIVPFb)XY(= zZqB>kn_YcWGJ;B=(OTItuVK6*wkAt{a=;7gVbI+ zLSMOhbIkvg3*x^K*}u6t{Ti>1a|`Gn7Z)ovi^M?{_EI80)3G!b&AJk zCa*9NKmTb)u!t9M8PK1=V~o`LG5u3_M1J+5t|Z=HX}G~?fE?f2W}>=qmsk7Xc>0%G z7J5@B#4OP`>$1^j1NS7j)?g$++L15#D3c&%>6|7djV;sH?j%Cb?iKPB3p*v{h_Kvz zc#||{R2&ZPo|ZC5 zO*02h4$+t68E>e)r^Y)8{%P;iq;jJ(K*%;?Baj?4X3=wm(4J`5TX|uT;JY%j_j15p zrk!Ue;~u0rhY#MFpYHt;+4Ud}aDt=;n_t)9zATe6{au7?y^yU#SMM(#yBN``W%z(} z7_vY`3_}m4Q?9>V=9;(7$|0jSy;OzTb0zzs*Zgh3JJqit-xS3dF7g5QX~FXs=VROQF!ZHZec9-5V*T1r zH0`0PZIN+du2+>^6*LE`{hn_6{hL>=g^rD3h7rY2?Bbdce>lr`(wo%#nefc*rTPID zv|-eROnE(B*y!Y|wHiw)s7ub{a*7v~m74x<)Pq>mf1W}p>{gj?s>RBpI3(s#9-RFv zXh>t(MO|zvHKpcwNM2iZ$BOD|vINeK^3_@QUVn`=GUj2L4O+R5?JmwFS-owkaUDn# z%$IV1DA9n0VsN^7U!D(@e@Z@ZcT}IqD$mh1u&3A%c;jRtkj%PD)FVj#VeXMSF1HYm zjRrFnr-?p>HQ_cL&W^I0?<1`@x-M0ug1bC{<``x`^v@btKbLo&hV;esZlyTZ55h(C zbCy&bT@t3zKDI%Fet|6?*9{uF*AdzgA^i-T6R@E3ryWk0#9flp^0pX6u*z$d(}(1` z1G)+OBSzi-9uadHx8i>FzV2Uu-C~>{r4!xsvryWC|BjH7!m&Mh`UbdyqM*J`bVIsR zWJW2u3RUam8@czdl)_G)>UbC50JV9$eFCjvyT)Aoh8Lz|j_c0~9ih{lEU|p>feRhc z^e%9#d|1^jk@DrtlW}WFrWPK#>kLwQilNc?xJ@~a z259RgF?Fh!cKp_92Cm1zj8{g#*AT<79cIan=Zs|*eMdsRYghQd)()}OhtVSnev$N_ zpfCtNVO5Z^-hMTby4)dWLc%-6^@ks!DwT&Q3-m_1Afr@w0WVnonNj&we56YRV9xli z@89eY!GE86D`;X_QVNF;kyd9^nJIyWAurt%oq1rRBbyEBFJmqY5&`UJ897z@!>gVO zQIM6z;SpWFBYJH?l3i^BJ+T5)n1(U{Kho!tU_17Nq3P7sd7E@_|ICa1d8u$QClM)N4|+$qzgCh^8rnYGI%b9K^2LMI=A7 z6>kt_|K*>o+B#fdxI08{*U~UTusO`Qd|S8pJ0i-8cAS?5`U{dQ*b)Onk;C7AnSW69ER8moisyHczC6URmbVIilP!Hh9es_J=LdtLZ zSUvq)bt6z4;MeY#LAm-LXX5{%ot3{*C??KU|Hz^I)3W@VnyycL{j16NU?=P%QBzav@pT%8M<^Gk&BAzRhum5KhIsZ?Azq*ECn^#GQtty4a zNI#envSg=bF0cQWZOItv1;-@wkb|hq^kdHFZnrST1{$En+9<|?+HD2qO58MxRACpd z&X&wl94Kq8t<1N`sJh!lxZz;$Q5L_Q4QznZ|JcT z(vr#?P-meI#iPMU`_Wfc;aHSkkA2uVW%0h)s_8XR7rU8(Wi=f*25UGX{ZN?u#^ zxIAq&CxipZ=kXp^xOCz1mKFy}c~3pg7g+4*$EWdo?4?xAgjZ?XHcv#5a(!V9heuw^ zNbY#2E@4GriUU9w!!ht`lVBxB#`!Ma7L^rD98Dcnl9Xz!3pI#W9XryjKfQMDSBYBE9d9 zP~1^2GaShlLI7>s5JNG@X-tSD#*}3w@Yx_{#=>y%@Cg^ zqGQ-gv7bTB#iynSlqy2~dv1lhF>b4~PS}&=v&nI!k@n_S%K=RBaSN8XQptEFh=WgLLEFCAU*+vO{`3nhLvtP4nz7k z2$5TAH>l?Df@yM(((M-KZrO{*nIrl{I* z;TH1kmS5#S?UdU=zcPNY5#I2lVHGacJZrV&o7S>b#4upOHmAAp>6#eeh$z%nRQA#p z^^9l9{Q>fqRG0YHcaGO9>P)9TnoxH)4#$-`$#$le3s{v^mPcBxsra>FebtaP)RZ4@ zl7fP?O&!mGm_w8!n>^ZoHsZm>G8bJ0H)@xJ%{#3T*j=`q&fUA6VR-zkS61md(q(E+Y?gPplz1B!VC#;rmFg`qzH<)?f{_`)NgSnNQTUM&WWG5 z;duAirBrH&5N8e#q2F$vlI}4WoK9MP2M)nysp%oX;@()MTxtFoxu}b0KxJ3GcQuf1 z*>;X`=7ErKrdO0xg_8?Bl7%w4zT?U%(zH~Imsen3!6Ux-O=YFVDwqdiJPU>ZISUaU zg{GyK>SBi9Y;nVZwRN666ok~rzZzH2Msb<&lUXzKBh-=e3PP?L=*-v&n%8RsU+l+V`g$JvO|lCi@2IDh zU!$_Q$y(wWo9AWFP8#omdXadpNG(8v?5x9G-FlI=I^C=joox76m_vix6txknZ9E4V zT%C*hHmAbTk_$6KeyrqiaS9_WR8-pKG|FyFY1h5OCFeC8@{z4rmZf^J4*GO7FRQcY zg#P$kTu*s*vG5kYQv@4e49MdY*Du;7#U}Ek+(zw3dve1A&Ghi+Ub7e$E zgbnW!UwVC6pW{IM2%$lC7N*>hl1h9XtHE(f)#ZHv<+b0B@M!X7s-o@&`|A4Hrtq8!1 zNPLTyiqn#H9+eiVqAEXQj?6h~(_1e@TrOcc3$K9rrD#!@`nlq{qTP9IU4c;RK)`_0 zK+fA~YC@8wY6Txfr(;nFsBv2rrOZ)|E@4g+w*y90i`~sCZub&vo4efZA9P6Hqa38M zu^%$_Ya8EK&=M!SXr*A(02p;Ovuq}()^dyjMf`U;$ z$$`%!E+{ZGD^kO6{ZqC4U*Xir0^nd`XXxPM{!cjl-^Y;ruM71rW9*!btN_N2&Squ+ zw|`FXH$0XHRYQ9>2LdXb1Onpw4=4Dq*_8ilHU&ecuc}O&zoT@1`(`bzv zXOH0qp6HSdZt{M}1&`E8neQMBZnHj>}=+XHfZ%GK%y|-mn?ZNhhH-;?0ax3BxHX!M*{2Xx{27KUA8OpiB!wz`b|$E+@g zJiG1sr&TcF%S`;`r@j8YOPa)E0Y#&4*WgEAH2&GNPjkKZ{XSu~_@=tR-PLf(gE7~V zy;cYBnEPos=jG$a#i*}Z$6J%v^X=%C&yoDjyYCajTPor6w7}Oin~!xABSN5k1CuZ+vX!36Eg-Ux>0@9_E`*>bap!8TnSyrBed*O&I6pIhXw zBA>(YpJ&5_?=zk6Wa+x5>a$C28|N=Pj<+`t)yeV~^`m8a{15Ivs%>@d^TX9gnO^j>g9Rib8dNMa9<7M&e4=3}pt$Mb;x*KR5lY4pN<=62{&j$fx z13v8YSP_%gIro{z!iHC$_N>YW9;iVYf$W=U%}xhc)W%j9+Lkc8r(sT(OEbo&;n zohHpCV--v`jISH4nyleW*0wKxL5+VTdJ$LqN%&7`eKuMbWX;BO%q4g&;XV8-Q#M}m z*rrpMYn55O52oG~a8bEYD^j)E%9vl_@Fycn*ehY(%fU7EN71EY|cX}yLuP!81 zvOKL=fo$-%Y)74^h+Hn~;|5UmPIE@MMJWp&GIbb9GebK+v>%E{ee>!ll0B^Tf0DS0laQW`ZmR7n|1a~_6TQE~&)A5YGi`#xV z!xq)uMi!S!#351ZWBSdr&ZU0+#dF!U=FSP1r4=ix)}S3%ntKei?k3sg_e^!-0L|#2 zOPkYSqd375@HSj`XYzvDo=3ip=85PguCmGd@SWe|VOc5PShAcs){F6roo=e{nuuMi%+9B7qi9FZ0GVQjs~04T8WS*KVje(%kiWUJc{1Z2FBpwoa{*xEW@wAq@a?Bn<2dd{m9UAx%;CnL;Vo zX3E6t>B^v?eeCtwYK=C_6=%ahyhlV0lp5KPJiC04~sGjPP6`dX8|GnMw>yt z{Y@zGI-U^1>X0tedu9|7pV>s}WMh5{m(5xCUUT;qmp{eoD|re}sQ~P5P>={XJ-2xR zn}FKF-i1kA`A?@zMF42QZwhjvfrEhKo(}ZHV7gaEwXFhv_|1*e091X4hu`(XwPiG( zPLt=BzS|e4X9v&L6-nFuWvkD{h5Ln6bMz_ct$_ANK+>)UQ8r>=JAHifkO9%ayJkhW z`mTvyC)T7#nY+6OpMGbPHR8?vd>OW5YI&EcCU2|uDuc}VFFFAF8k89mrgw7PSi6EL z)@om@(7M)-w}2mqEB*6#&P|)>j9_z_meSGrC?`~K&mYbmE<`@lnP${3TZTL>ybqNp z;zbE*wI2CG7U8mI<;?ehCDr%0kM^TIXBY2R9a#f!rdW;(XM5$0M7zlvb4QL7@HMmmu0x!fz3nWHZsJ6!8fGv(!sRSAQl8aKF2*dD`Y`W@9>6w;oaIgS+=ttan5 zTWWoDA*?M;0Us3&Ot8wu!3C@0>S!)058B3+XLY`*0-lu_j_W7kcsR|?HgMy^rGAU{ z_dbXNnvt`4iU&3wfw7^?ZyPSKW;Xf@3W$m4q9}$h-e>37v@^o3t2Tk+X#m zqq9>!jn80E-0rn^36bgUiNi(9_W(S!P4wm3w-*o7Y$Hh!tE|m+e)S)nULEBeo%Dq_ zOK5WDhPS{mG%D~-(ZYTbJr&tlC6LCe7>_3b4#H#Db}VJ#Nn=mt&Yt9vX^fC2bA zE4bcFP$r&D2Nh2#o6JOch8M`43vh-WH*&h>m3!Cv+#FdF{NKl_Vi5-Yj7ja_frB$H zMk!`GWp=0E$&WtVWtfpnTCwTG(qe2jb`h7LzU2&&BWi%-0<^nX2hOwg+d27^hkpsJ z(okwVUxO@dsOpjS+-(p%t6RJm(@w>G?a@1AxUsBi z*?pOJDysRsIWhIw&#IiQ%k6}1-v|^c-E(Na%|fa^swTXxOAR&Zre?Q_0KBIQ(qLT9X;~=QoZU z=F=P-@mnTf!fOIC3-Ue-z{Fe3bIj>nq_$%c&Etdt z=&QKufw6*#2I`7R8g5L7u9)U$2SYC^M3z5VPj{a-egZN_ zVTB92h5#o*x;sM!I7r#|oASTp&37f=7ZiOIqP&3I9zF+KM}isCvDajznO+IvtPD2)SfaSX22?TWr;seqN0XuJB*S+BnvRX(5urV zqlfy2&K>6YxO&7uILwRxA|`QwcBBUGT;^9<)Y8KmDQZMPtpUe`q}Z6^paYeTVeIkH|M5s|78WmVZVGu4^5DOeB{C>w8c$6he4{3D@K~PKPwdtCZ zBZN+bf*ZI!#^qySE#P`nW)ERik6hll9B)_{yHK{=hoi4O1|w=k+}%F75KDD?D3%e(AKbCW73N{NEGCtw{RHBE6zorCp? zh;Q4{&BoT+m_?-S=cp_`qCiR4Sf2klUP6N@s?_gnxus&hIr;Ja zwDYo`A#>1Q)SwSV@5q)w@vTj@USN*tLCar{5P)!24|3cBblZ}%ODg=Sc16hzV+dkw zm2yLp6sJR9=Prl}Ph@hB2F0DgoQuc#wk@O|SWz;-bWIUkLftx?m8s@-b;5h0UrtnD zK50~9c6fh(+)vaDHJL3@#CYfaaKkCqkGSqWgE1DqIJl~n`7?Q$<`vH1aT(~X&ExG^ z_8O~I4~zy-XI!#yI--+~dppO=GC2-=JJZHo75Q5s7Y)YGKL$8vde*o&#JH2pva@&2 z1h8*9CW=tv2@mF(iucIauytt+8_n2vwgC;icKQ)c5X2MaDg@C9`xDh!(@2is%6W#L z`C~LvB#-5T@+5~S{jv42$&#Wtz-xA#@xo6v4-A<+r4v;|9r=tIIl0TKD-@oHgb%)# zeTo&Rbc;7_(f@4EL;oJDnND;{$eDB6u93##!zGIE98(5ElusB_hMs}W2?pyG>de{# zzlU6D<5?^ zCY@7{AEpk*As;ugcO&1*kaS8(^&zB2&8T-mt@ZZnXdniM!0P$qEn1^)!)Z^`=89BC zl65gOrP1n6?Qw52TrY-hseBHk7xes0lJ!udBQW;VgukQ@9AO6yg7(^DBH~@Lg7vryZ_;XLHnEl^%%)Y*hO@I_(Qps~8i{k4a*>+={Z`9H zKoeuS>1D%96;Z(b_zIP8a5{4vBljDqT*>?&E7SZf`bTu}HJ8R!Bg-%wZ4845FKwH3 z3`xiEShc5>fKu@4=`75&@&x@UvLlVP1zSjNm?nRQqsCnfb^^OAzmiScGVAt!iw3x5 z>vwenKqEI!t1}+~U|QpHxi3Pxp!b|Ejd9SW_4l}qPVFiWke{Z7c*`gx0?{P5 zw($ZAo<`z58RZ)n^2B%INIyHU{7PRm-S70X`&8nUakGf1P`z%KtBcEtj+5uJ4Q;g4 zygcagY_wO@p7nT-!vbE*%dcXgZ?NN=B-+9H*tEDDHc5in;9gh~kap@wk~8vSIq-6G zD1~id?gaz(+3R(^FEt0=8Se_&mE>qAsFL>e&L2M4IVYRPMsIlVnO)pyI6@7Ov0G=1 z54=-JH=^92GgKd=QQk%PX_TAm7eZuQ0)c^iPSf zWkDdHbwJ`KVf~VfH9(YvfAHmVFOw8=H9B+6@+|#6R5WIC8Fszm4r0{0-yq{=h-#_C z;x)qzHL+8HmK~0_{TRma$N*0s-#JXuT*hiaM5@tx*Km`K5SXn2?)j+(UpLdfx;%S% z0GEVqF*=_ZY#>^cPfj7#wdSZ7m{JyqmA^ERuPOs~MbJL@tlv&MlQ76hKk>n% z#$68(a(3V0eSg+4qw%eI`ws(iZwg~b4k|>#93dDS*_PyC6~`luASSe8r5=TVunD1Z zqQUmO7Y8Y=+pQI~)4_RDuw$2`upW0zktq-vK<3p*6u8Lr9l3yvuwT4Cc{jnRZ(%uz+8(aO{<3p*uWpeW2 zG0pSY$@BcmOFQYtav544I9_W<=EYq&EjmJ@9`=djot>HoankXOv!iRBI+fdp=jMmY zgD2sq^6dkI@4VMbnSkloYUlIyYUd5j!Fgln$MEJe$HA`iXvmg?V=V1c zD7h}7sLjw&vLGdUSSNkJ9p{H`^6^ws^f%B-=xRjD`U!KG8YEEi9aLD(&Zr_snQ2vd zvlM+a5VY3EnZ=+TEuL1*-NEz$80c}k;ZTo$h_QFx>Ow_Yb17pLHLqSp9m%oHdDzb2 zaOt&(0}%#>8>Dhx%rIx@atT1SRAJkchRgOl)9}U{xdI)=7q|k+3aww}C}JtkcWv-{ z)=wIQtA^2EYr6@)x9j>)D9Cw7Wg+HwIQ>T;<@f=E-`1+>QCdK;(J^E3;0cEU1ZbXV>p0vIk*io zgqba_6h{*Yiyzo_kZVp^bi0R9hKR*r$?m+f;!eyIeUS#@=M?d9Q#;rUe9~Q8bp>03 z4~<*Drqyc5#i>9n8%RWOQ*DK!%gulkTjbSjD2?M26=@HVyU7h*=0RZ61z7!Nia{$?OuVh{SXC>SgfvAiE7h zdJY6`7*z&iZ{`F{MGLV8*7a8>hDA(XVeyntctaE7-8Sr5l^R8XntB-d$@iS1V_r|% zv0$eK?$fR{bF!TOhBu&(xQQ6<%HNL?{l#u^-A%jf1Q8x96>n)!hdnTV?@T>OPJ`!5 zClan{fqQh#-y4vLjfc5#2oc}6IK{CI=BHNUVRZbq@NRN{XCN^pA4JNWZom3noS}W58NAGONRe(fbzQKrw3~@VJcy8yyJc?~0?D3%jXU-JL+T&Z7`D%ukLJBuz+`fzyrO1%xOz3+;my}xv`bER0YZyp5 zHH<;XavVO2BhthZp)$NnXx|gwz`TmdN>Bq2WYrnq5S_x)SLWD}( zjL8xCZXzb1{5@d?54fuh<&N^z`7@<$VAL2I56Vl#1iU>D7|%5F*E4t#Y6dI_VI6ph zngN{(RY{I4ZOh5Qiz3AvzjO5hvMe@Gx*g#PMf2U3(>3A=H?{}yy}5guzj*cf_V&e! zsjjZ3m@9a^hgYv(U604ZYF;l2x*&upY3n*m=W&leqaVR%cemfe_w;HN9~h%#48O4F z`;Dg&JI=3Pl&|8FmhBl%l;GK?n0_5VKFt`U*;9>SL2u8d# z&i;7;zup&)W<*f@K$9FZhesz^m>vRcVPEP&a>?GcYpno>x+xe4mPpxUg_2R>*~Pij zSo!a5~}=r#WoJrg^yFYv0PRq~Sw#GM{t1b>n{h z5nf^8CLQDe@-!ZvhTO(ifj+*sy8sL)(Z?G7*5@X>wm@y4UQkKbMmY7Z*v7AnbFU3= z3IF;TKIIU$m)N0g!?L9p>c&f3M;fJ_YYX_uYp`*$M6aMvnu>RVjoJEe^fhekv0v4C$S2>Wg@lMtu3E-yU5OY7EG!PtBrl{6@q zC^fo#4NsbVV%2q?yUQcvyn|sHIv>%H>oMtZZUS)a)^z>K9mI5+{h{sc(ZjZ#QxtWy zMYL%a2q8S=XZ>Y**b7r&d}EbJ{)huZ<|DN&Zv}z3BSW*UmCw;X{DqOlDUp#DJB4dk zPG<0M04J;ilo;;PO%6D7uo+ZK{L0{u;LiwdA3f&adizK=*5OlsC$63%ltmi*Hs7w% z{avpNLJrX%nHO~7bmkv2eUN^+h9_3 z_|YF>>#Q%tAxrOpO-!8&>&_+jm#gDtI3JF21x zH!E&CTV2$H@qr?;{H)4tz5oAmjyUQZbT;{Ub!2&`XOr< z<5a9;gGik)EkFnMgX|ijGR*_5+E@7HyuHDHX3ODV2JYPMDWC13d1sgL%Lh;C5KkC0 zzLO~U9wDUl1bp7_2FnX`i0KowD#d!1tJ2z$>JAdh=LV4esXEvk}AvHYKc}lA=Bc^HR3OitM5KJz&jR>$F!CFil z0M@qqjYf}B(DK~`jrZW&oDt@TFY)@Os4>0H!f~HFT5zZ^w~N8MaSR&5!(7>;p&M(< z`CtHfV2>RAmsT7-pl&WqI0!n-RB0mK0QdC*?VMcOzM^5Vi?B16^ak4y-GuHjM% z{0Pu0Fkc(4G+Yk=Z^@UuU1s%GxLu(5r!o-y}f zs(N2XxaCgLG#NG-V4#+wcku8a93TR14Ddk_BsDg5C7I1u3mIVoVx~in zJ};w08s?1oHnFUhyyLnKjTTG_laNV;xnhtk?C*lnzzF?73}t})FnYixa8efN0)&N2 z6uEQ5WqWjnJjbLm57A@9&lhpC4+|4IJVRpe&wX!X;z~flrgse!=SLb5CyAEhL>E$H zb2=BkEg2PKfFx^MFbx>Z)f?Jk&4)A0iQ;aEQT(K=fQ*=)Y>%g4gU1~AOV(Fza*P1; zgAN5QYmku)bTso58b(OSiwxH8`8YhKY2$@x(0dZTr2z}>ep@${a3&3JALkF8lj!wh~od=-E(KIB}0DgNaNTWGa?Obdm$v!eU|2Q+y#g!uq zMqxre_cFjQpCc2MHj>OJ*-DVb0Is4d%q+IeGKvb{NHffSa}iyE=O%lF=Iqu#tP7^jJQ z5u(pTv7H#FoHAJn9YfZelj@%t%7&||i;y=fUsP>guN&9AJ)!SSc3G?gVam-IN_&+4 zL3X&p1sx0I<|S>Vz|BVpbp_2-Y<3XMU0qDz>ECuF8U;soIa6F$WF!{dbfriPd2 zDrRwUFA#KB**lQLNOEVlfhIp`Fp&CG_MC9`^JEL|qbrgApPO3lbQ!r&XNt)<5Pma{ zoXtm3hMDX|ks@rH!y%d9q`H8|B*PHOAcK{r1z_eV1ZfQUxW+muvM@FqcT9?@f=GMn z8CsudSSk#ngNA6M2Saf>Vd`X;je#w|q7jo0GP=OMQA|q)8JL0&-gCMcD9}u{M&_AG&HhNtgm*)U z$_7TTS*NP2VD?CtR?eC7NzKlTC*x$BSs{U>BQ7_3V{ zdQ1qV5?a*)aJl^+Top%dW17%%?zqg=bE%l?n3DU&>|~}1aF%AU`k8tO90Xu8a7_yH znUk8QARDM|?2F?A*rzxzZ)w8-ut_3+=Q@gW^kNcZsT3?eh_x&@<=m3#ye^`Gh@|}< z7E|JO@cx-x&VAKh#JeMkv&;qo-=o7dEU@ul_yh2~C#D(z!09KOTMGg}yPP&9Nt7s7 zmQ0=C4%LN8wIgsAn)hAwCs|- z&?CLrtYS~)%QWFgZAow~N@3Icv#0hq||p^l=kTLN+*PzGWykV|%*$l4xv6DO4vAm5yu zNjq`r1x*-iJ^`qC^N$IboTSpgxKERdM&K>60zKY~m~maIGMdDl?P19_WMN?gk>JpJ z^6!Rwi1!367Q=-05?bJ&yV(-S5J_yrMS(QSW&m;=@u`@q!X3E0R|@jT6uyA}6IG1o zKG71KmUYks(%V%@41T_K(sgrWswnggjVvH|NtEo^t7@8NS|Kw3!7xBY%B4xu&vn0KgL_3*fCJ_{(d3mA<`d&~kPq?E_PO2w7D((5Fhgy#9p* z0tE|)wwBAPtKt|_>_-F{77M)h;BgZN2WFRK69!8*1OzKAV?bSOCY6Y%&rY~+WN8el z%!4=3gzOkPKy%PG7wT4--~HFW``f?!?ce>_eCHRw^Yh>N`gi`xcYf|Wzx z%*)g3=VRv|<%m37WimNY)f#s;viYbR4yJBwMUQ)gE4iU^2WZn3{1&qtSYLpdT60$W zJ9FDxNyf0UOD)Lu63OwaO_`au;eF)-F`&DK?!*Esh6va^nWc1wr zF0o)Z?6Q0dt2b^|WCz+}E@anw^>~@@@oFNsCQ5qRxSI!WrOHx7t? z&+IEs13h!Dg>EDy6;s+p57;?RMmG{MQ9{~>9myO6ViIU@y#^L!j>jICXZ*8nGM_Ve z#f&lBfxj0r{!zK;eyfJ=2Gi8a%a-MoYWE%&XiD1&>KTOD2qKo*d-=jY3*&ydD3_dN zDWL0xg5De@;moEj_V4lNqs+c_k7`YIXPBO#`D1B2(WABLNBdsPgZ)KcwBL%{-$6YQ zb>vdkf~HG=MENk&uWaM{UA)YvG*z)s2wr$DU)l#dbeKdVyH!RqdjO=6t!8^nEwjL~ zs&wrs^v!M`c90C_z;0XpW4Hj*gt6L!ZzbmKWMQo3zQM?c-q|F<4TC$`pqTl~v+Dw` zL_i=@80&^fznr=H6hnOyRgl8_rSG?5aDej&$+m_&E;B{~naBn9Szu~qBj@KAHF+%R z00kA3yt*$+V}KEBSs+2uu%yyUc9rEj*VF*kW7-(LgC85mDwQv&c5MUw)_To_ZFW6D zcz@KJ&2={53N@^<-z^t@h?8~SS0&qb)(l%%m~v(x%}CVhnrN z*Xf5Hfn_ynTXBchM)=S2?YLVqMwFfIC4)D5Xi|ITX(bPa+*R7rv+Vg!FtZy*O)9CF zROd$R%tYnirXH9V7#!ZzA5sAWjIN);e6&Ta`@VfQ;43gu;=pCcN&$!79gkLbi(5neJ29>|(l8jkKK-=)!3+4YsZz1jh#9@Acqqk%uwivMa4 zm^En;$mq?755I}NIPcPK^+Q&cf{!9IA{*Hi8V*^s^c#QY!#5v}Z$4zgpFe_Da6x$k zhVnaK<+d`kEM)3NvaSK67lD32UgUfp^G1POirGe zRF_@trl`N%?8=~2e=fYLS0^S(C}Fd4v-3OHE+QS-$bh+ub~*x}G9C-v7ty#x?t#DF zd|a}->=%s!=XI(X0>USW#1xGAU|6|EfAo_kWkXzLxY1@E47a=jS2*-D7(zwr%{RtO zx&jgmT+UpF==#*<+g)!f<(qM5W4au7%_;`RUB_Ku{CFY@qd^^YqFdf3t}?>_BydY| zWw{NBO~R}MK02S6_zu(`hFX7T-K@M`#Ub$$Bmu7bDu#TaU1y+T7ciTu3CdMNln1Q9@5x+29o6=g`(H#UKk|QkG)MPSy4Z< zF6}JAEV;q%Ub`iI1kdES8;@~G1ESN~!^i8RMt*^8`8LKSSH1)bjp7$^-z%)ZxS0gk zu%5-6kNDt}H35!%)&n8`&R3f^AM$qeVYFp=$X8CHBGdf}Tm;`U{LS5GZ$9k0Hy_sP zszbm_=?w3ncl|oya7cDQi_ied7l|UuW=9y&Rbd`recYHNShV1nCSX=IJzjM5J=-U4 zJwiJ-%eeA&!&S$mbdQI$GbD^l46AOIMdqYXp^TC#vdO6QYZW6dq3NgdE`ofC@<&m* zT!@`$>>DHF1tP1HLRam3m%ZMVZP=@E`oM-;jH*@kD0e+5=#moOGoI6~LQ>P#kU! zwRSj8X*hXy^L%F;m#nco(LlsA0isYezIqACwT|^vFr4aSDgse()UT5 zLt!M<2r|T0r?r2+LofA)Fp!VZTK{Qx|D-I7-`?$}s!EW93&AWn*Fc+PWN*w7^P$;E zmTky#Z<+=Qrv*GReNPKrWsE!FM&`F>j6?ddWZ;HdMqs5o*2N(9VhrNVH)>hot&KZ~ zHr3l;}9=)iUPtuyH{@P%JBmwUibi#1&s3MV}5w^(J?I^wHYTW z>n{(9UkUn#`UBQj#8+b9>iI5l?}KS!-Jd06QXI#{xb~Zmb1hrcx&yur=*s~Y4_w;? z2^J~60Y&iUqefO~F1%n!2r5W?q>hAp%l_Q|PPcgj$#KWYaQV7xWwT4;%31j(;1xeo)qk)1EqY$ZI7_tX1s&;)4GJb0QLrZ5H0aMcM2ch3R#J>#BlKJI%A zQ?~9&xGu-DQL!bSfc}xXav0Si+cO}`iiC|p^QJ^=5`B(%sblU7I^8qq=KyKDu{rl-+aU% zD%~Px=d*nxR_X|t(I!kA-4jq@YWD(E^A3Y-7LCjrZj_P*V%pz~MMDo1lY_(+7)-1# ztjV+y7?PIZS1sDIjFLigQ8eIpPFOOt7Oq?=kP-0VV$Nmhlsg%$Wob#SyzH|VH|WG} zuHqPmq&KgxU=d)bn`SM%(d;IBI6w#3X-YOZ$o$t5Um+u?;Uj^c!!^QGWPO5_2{AL> z+9U&|ktrGHaRa}qZD(5HO0q&SG7SLqNZI1D6Q&T5|Krx&0}19aY%IgtP?5{PD7LT~ zIIQBH3rIC-DW#-B9*2<%3Nm0NJq$|VaX;$p%C_@vi8{EK*)zMmT5XIf%;z*Pb`nVR z*u4TDG4KwafImzwKbyB4=@wKH6Ege!{Lr$?t!*i z=UIQo(&d74ME6KOf;X=S9hR8v-0Nq#CDLf%ORY{|LxBY~>4o4Qb6{W)Em8oE8{0rd z43ezV!Htu3`3^2ePw|8XrnpP}CpUns6x!u7Gemr~X?SNx4M>IE*xdCFkE$m(+76nl zh9Z54aPWZwAi9{F#$49A3HMxdZOB!j%9`|>j}9Vx)GYK%<|w&L^j1`7%Uoh;J)|RM zlog40Pm>qxgOTaET{_;^qR!$jl}* zgqO3hLfE~Br>U8^9My6kS*N{nrezTq=8R-q$e*NsiGBZJT9A*R@LEv-qBmRydWxwX zxt+TK!7flC3mprhQLQc^*`NC>$s~c_D@~zcegmvhl%$>`v&#U}r05R-v@}REQt_rR z4y$>r#v=E5#;mbD^uyh@dyUe38!r3gP8FQOIL>s^;uLgE;a=7v*w7eE-MWrSmJ$i+{S)sIPU zpED)_$Z8|A4!I!+w3Qqe%)m_4ktg6lA3p(viaZK^6gx2un8!oQnH$ zDRO~poZX{l2sDzHQ{q<4wR%U1KYLnE*s>@YmkblWCtR2i&-K@%W&`ih&zV?k8D$jN z3wsA_SF!k%>Zf8`GOcE0pFNc#m>@7oFzd2aHNn%!Xn>4k?52Ri{=feQRbog+p^GK( z2?W?R%;zJ*67NPFc}!Czsbt42Oqa?wt~IMTc{0yo@=RNV zjMLwy@2@%sH#dT=q-3@m1$jt&V5J5+kP(wjTbn^W97VEo0s9OPa5e?W!d+ku zX)xObJI~r9ZOnJW{X{42k zS-?yagGMp5U{v>_6c0z56H6Zq4K%An@2M!sd1Mzb?a|cmXexWO-2FXX1W?ol+RyfQ z_e#``=3uTf))0(9SXSXHl%Kprm(dthV?GgtzY-n<^n#`!_!5*Lp9CyO4xhv^KjeG{ zChkO#pIbe{JgS0XsQS`@J=gm&fH5>7v8XDXX^=RDPj1SGVsb+*>W091Ro>Z(TrM>a zNJdbl5LW$Cu2CxTg+lmYr@<+h`>?`AH#wPP0&7YD-(_h4$?{n_36Wo$Q)F#T7QtP zBpq2mTx{P z>FUuPlwPm8_MwV=tQHNhIP)1HCaR_bkVVtFl94bo)N#i&?4NiV>I6VDA+{^4YVk2qQ+DYJx&SGOfht z+iGK(PR8fO>@!7@i}0hwA*tW`!0h^5H^qSq$h%o8xi{B^JPu(hr8p_mek57j-+`oU zmeO+_#Kjkq?4nx(H#=^3Wn5HB^qRDLk7;TBtmy!0*Ddo&$uKN1iI1 zTBQnv4Mne)%7oJ>J8VK$Gv^-E(M${}j~6{SLU#2sbyYk7w#P)y4HHt)i^{Iqa%09l z924cg#y};NKBJHqQgMoD`%>zZwm4Bo zvo&LSO8H962pbEnp0F^LvXXp>GUQAXn%rK{m64KD=SxWvYlb3iAL1(sF6M*LQvu%J zpKspTL-o$0F$g$_apPGD6UfHE)zKf#9UYP;sBAKwRP%`nFceV4TJc3nQEGxlv=1|4 zSOtT9K1iRRGA-$a2H@{*{Z)u5+GC!drd$gIym~&LW?hhYeK(~wtK8R;;FO z49qjgYK&&#ek`7~AUZ@NT<8z4)VyDPM2Hgb4~8?xDdo1(dDw?e&Taw2<3j;u|pbNlD>mlbYp} zb$1GLi-R02(b#MX=1jcS&5_IZ>=wp?H`<)X;c(P2R!sclOXp@jFx?zCv;V#AS%XC; zkR}aO;_)(z43}Z982TDrAp(@9*{hrijf3EjW5_))kF*~d#;zm22RV(yNP)gZ3B)r} z=0hJigvE|j7QXYaXR}mUMq|-8njT@Lo^e(&RS1946A*Ke<2sadtj99R9Zb}!*I{!hEy=F3s4+f15?t0qURp>ccBESxW6l_I6K_W zBU`t6ca|*ShVVMnGfB_$30RO+3)ird(*Fj(vH!KO*}_d7X#`Sn2J-_FpE%VT&j2S` zx^sZvOggN?yo|yFR)+;XXiOuFNN69}RK+`cn+X?!t%Fp1p=3sHC8jemNt;K@1Y!rm zAo?(iT%UywlA6W4q}MPoC%zA`4Axh%zaC9P-o(_^nuff-+yq-%77pc13aOuGAr;PGgXIPc72G% zU@qrMC@k!i^9YoQKW3!WB&+IZt`ojbO$t+E77VwinU$^(q%%|Q{ecsVPRs5L2uh~# zsWwi|03Z#u^bUm={c6|_pA|uKxfC(XsRkA)){}{pcw15_sQQrlm3VO$(v#9OKs-cc zzs?3T#GIJ_jK@`xOngD@o0&s6SqzKhmm-82tMn|)UXc7t0S^R?neZ|71*{Pr2J!*8 z1>|PPLmunN6_P4X*LyPxkrvumo8Cz#<+$gOL;=UoI%!^*gqp{a7To0(_#0=1QpA;M z(!9pE#Yn27P``pw7J5@KKnHLmr-2ZuQ*!Z0AFwYaP&N-`p~5}T3=-O9CT|bUfV&Ot zO5aXuT(W-7EFWu58mO<|;4K7@Fnwu+aBNvGK#}TvWP1;pD~)VBHB^T|(CB#;?7VXm z&lkyb8lAAR>WE|)nlSTj(}_Bia-JTXK$dM}Ck|vxNe^$ZVUNU0h)67A59Fg44mFf} zAT+#<49;Jmr7|bFOZ7A+zNgxnsG8>{^yDT^0b9Y$xL(*GV3s25c8hi6qoP{RbVugi zoJ3J>Xw76-C8vyYrTX3I(DZ+zmwi&yWtIj%kxy!!+Y%2;Ku(Q^Zlrk#Mz|J= zEy{e$_|eL^_EYAVlB*r=X?jN5VNq!$2kz+=KSF$E`v$rzydn5vp+v}iA}V5MM6vsB zBXe0Xa^0zYluA#tf*}B-3$>wFX~h=xBuFZ1m33liLm`9;V=O0?_$kdP`?HAlI2m~d zt)`GZ6Lm^u&@;U?TF8A;H6_~=$Pz@wG!zOReAVbxPC^eIQVPv@0?(-6>)<7#MeV=> z9Kz4cS?v`^$gouaWiP83<5`7R%NyD3Kr|ODXPAnN(?Q--v%FYcg@#3#F`{I3VY6sr zbO#+{ZTN!uWu`s*)(b1}_&0h2)ZdT*H6)GpNRSeH(+Q7ZE_+8tbdt48OC88y2k9u+ zA@D5dU6JC{sZdHjS7?Mc=)8zgAV$Ps1Pov-+z~CM9x;*d6IcU^675-4i0XZGx@hil zj68^T5J?kUStt{-??ji7j59un7G`}jp^&1Vd4Xx3xW+#NIa0ZUJ4Df(3Fu(Fl8WFw z1d>a-OmdUWD1I_Dpw%Vx_A=ooHLn5)dXq3ECxWg;7X*ZP&5bL2*2^VRNNn;<@^ZVH z*X0AaRaAd84-30YT%0vzGM1JLT@qB9oq1I7@3n+1%%kiekj`>QBB=Ov6_x`!=-4?Z zY(Y4y<4_ZVxJIUqG^TGr4n5J{3mSzwxKO+xOR*cw@a}_A9*vwSI-R0WlG^3u@G)Rl zz{18wmqqJg}%P`1Z zY`MfNd6Srmbs%~-nTzRLrI1ZS7o8QPLDkcp>3rG(CWvejiB!Lcf){9|$hSt>mhzIY zK_52d%<2O&lu=4sP^nGq|B^tYl@RGjNH24T89T}4GdD`4CYK~>{aht_lR^TU3wD~j z)WZW?BQBXjbaE3k#Wta6V% zO}tE8B`dvj*g3;c=8OYgOo)K7d;1P9BxJ#Ji^`tt%@n^vjS1N)2x3vI9u#X|_JmlXZ9G7b5e2NB)veBpKS(A+qx0R_T+u`()rPj(EJjUl;)aY6%D6j||k(fF!Vedr%KgG0ST}vl&5adIgoSXo`E;Jg= zu7W!Ec(G0$XJ5R#f~paAY@_&4J(_^lK%uKGDA z{pf3;3D*gXWkg9Wa8_$r!_nPqR6nm#po6a!m{EIs3G6ZXWmdI<`?YmAfisn6V}`-8 zs)ni+;cLi*LK=3T(&6hcv7CVAPb--gs&Jk%GLK?l5AeMxL3j^P6cHBZR6~Dr!hwug z^aPqq5cNSHEfeKGF_yyfO<7q*dKysv^ zIJ3e-G7`Q|C`Gx!OFN*LtAQavG^u?M=oFaf?6SJMgqLa)nlx#iu2%{f@`I=hVO+L5 zS2E3T@&Y{%aXFv{?WPyWq|e9`k$Hk9E}Znm6aWsr(I|)Dec*d8+$dql+Q0-zB<1=-aao=y4o;}>DX2# z8(+VSx2>Kx`gBUUhtJW}U9P7fJiCgAbS&7*?A6OPx<9NR%&QmIlTYW!^UM^NUB9?* z=-NK(ZuG_d@-jW*wc=Z@q3r~M^^6aR*m8Y<8|xZ{^9k>sW&(O9lc|bi&ALmZz+Y^L zY`(B8A_M^tnNttTHlj0Epc(^%tP*fjddk^)EV$3uo_K;9u5e%!T!%NN=3B&v(HOUS zR&?SR8eNQ7ttELl9`4F%HYvP7e|)pLfEpa7uBBvl@_NR%d$C-G&W5}`Xo-{qHDx36 z*e7D)a}z``7r&9Xr3jO!7cauIk%-QH6qDgE#)pUn*v3Lqi$B;WXh10um$eqPqS3W= zECTo?5V@q70iAf?XaKzLNu!P!b%8uy(2w{PGIenhLzA>BOxu!xtVZJ!fTn>>F;@oV zn;@o*sDNOY)|%QIP7q7P@z`%#O}H}Di7Aemd=1<$)fl}I6p~_of?`VAI>Jvyg$y8| zM_l9+^$P`g)>aN#$XO&Q`57=eRB(36vJ!iCzMAkVKv6{c5Z;$VG9g z0N8loX{}Cdl|4O2uM21|uW#2CwpNm4pAN^HRC)X9(x<(0H`99mi|gCl)+M>hoTvW*J!shj!OeY)DcLKA zibvwIvF8u!#!y>Jj4h@xNRga!2ES?5$INZ{hLb@_BmOB9TEQC{S(nZ(A-NE}!fvec81Ft3D0=xjTD z5IyaAI0p}^l7-4zSI#sZBO*wYz^EYyBJOLTCL8ItGV!*TfLX(Wk4?I#*Lwa_iUh#k z3h>p%1@P{q03QAj-2I^OgTT;t`!*3O^o^q5|gv3)KUp(X4rtY+{>`I zuL5Dk&!$yqh%ulYc~8{El)1V%pc-vc0#VL&l3-qekLKJ?8fazCIT3PVUEz+=uIhiL z->L!dnMbGXVc7&jOYnqI=~)3L&uD+>`GOSUddQ;Tl%gJPHYt9sq?7DQi4QCIjT-1m zVI{B<1Djd$>y`8|(-#?T$q-TlnX_|yCo$8I6tf^JxsldYu!O!t-1ww%54vPyc0p<#W+2TEodzZDxdU%ll-F5kf0|SiSVvB$nrR+owYDoZBzu7 zR=ZrIGCB{@C;+ABo!0KE6mw5dOJ8`uz6&R88FfS$1v!ByLI@!g1Tb)5Sz;6JYAt@y zanx!L5ad(|xx7O`topOZrGrAl57 zq)Zs^tPAs^ST(+^mYAno<8^D-dvOBsR_-!m^Yk_4A*da=w;BtS73+Ci*Ik&24!R~w zWLf2JQrGaL05E|wo;_5r*gmqp2ut2``glH!;A0^w1JMf^qE5E&;A&Y*7lt5mAY(Bc zf0E509f=_E-k=-gbPcMJ&M~MNaEdRaN7J`jzYsajF@Y`)|aFtH;!%N=osd1R$E(TQ0q%!vf2#!He#Fb5AW z)}^?;X@n zk=A9`*VFB-9@z5iIzCN@kM`NqtBb?<;`#ZuR_xio9A4gz(^cB2mR&!+2D$VVw_~14 zm_V=44XYmzLZ**l|H9?T`z|HLXYX^vgQ^5?OVkco#jcYTap0F)7T`!^v6t_vLh0EOYWzjAfTwMTcObT9; ztJaYAT4Z%qOe)v;McYVhG|x0b0eRI$oWLy{VwyZ-1PS`VQ<&s*t%fe*FaS!e zpdvCqgW0*|MuI?Q7RM}MtXvKc!&Bs0(cR=Jd5KsUC)shb>M%3Rt_vv;UD7$G5RGBJ zxIjy$r7`z{f}oQEBO9G@kSs%tG4jlspl5yV!Xf!jA z*U=404g}ic3AzKhLhyiiTAIGIpVNj+#?F>;-bkMn7H%}OVX947>Xc-|8~`fj1l1+s zE|V#vd8=4(mH8MPF*jU}ARz)YiScj|pVYcK%m`Dbd`jk%Hl!;$4R>K@U!_mFdO(=OBJk zEs!_m3Ytc<-)k1#J!>F94Zd71%{q-+ zisuLkU$`x937S}^5LV-ZOLGx(S}5*b!yqN*3c8$R`7w2k5L`{2d_R2E{i)Q`E-SWtRN8v2rH(nLd6lu=N2 z52A8MhTA$pTO{YYQZyBcQJ4t7W>aNc5i}Av)Kz*>YnmjKfS068Y$R^UG-;VnNuHF` z*w)Ov!0b}1tT>+~3we};{ZT`ejQ!DL5XLTpEZ)>|0LY#Z6^VJRnv(p>#QT&(rwN>z zp3c~WF6oQpkwr^syvqdIT=0xhQ^ZIDlh;{H6Ex>BP(a{7=)Hz4b818=)qc(yJy!3q z)>M~j)*2ZSMm^{ceaZ%^H&F0WhSvN@h_RT zfAjBt_G=&E&p&$mFLCh~e4K!~)`G>zOxBR1D{p=V1w)y0@e({sP`!CF=Kl9UXfAL?NPyf!p z`1F^)idH|$*xk?m+21qf(~p1hlW+cA^ZP&j&+$(4`+w`F-~NrCFu(t6e>wZT-}+he z$!~tun9u%~|Lv1+{j0ZM``^uHKk-{2iTUiO{>JZp{L}dJGwknYfA8yWzxlKH^G`{n z&%W_1pZ@kgHd@7%yO-7tI^5>-pu{7qg>G!YlDj?E+<7z*rmyYiQmTFOluxcu64$YK z-OR=EPAV<42*;-2M&Cf++iHnmbButow=kwp?dv6#Ly{VmU$`O22o9El2=A2>eOMEe zz)brsH<#J!2<&$5>a&7thoO}XhDXBXbl!X8PpL6(jbpik zmY#CF71=USZ`>pI&Y9q*FNl{5Zph*hqy+Ok#Fuqk=_3wVw{d!eNFg-4r9EXi9$0qS zh@JAjT*N~g=$Jzy{PsqCJJVxM@_OSYi{^DNmZlut5;Ayw~ zts@&!`A9kbj zG5WbS@1L~lv!dV$#<}b)e0<<)c7LjBI(t<&u<`Ib*f;id7pKt;ch01PSf}W@dvS-+ z`f8jdq(6tF>?5=VKU``PXMB#86tZ#cPUM}tJrLgV87eSkccl2C(ZD%t}9<>9wf>a+IGj317b6UZA<@*Q+wia2sx91h_J!IUI2*W9r#|kg? zs=n~JUq(#tPlA_A{li+*++Y0g2`$ZxZoo=&w`nLR5gQ);WaNzUv^SHd)vOhu_u;{j z^zYPvrm}e4xSn)(bKMw`>pmT&e5niE%84ipWOPy+`yFL0HOBc0p6Wf8Cg%D52r|(s zOg!SNBt>h^t!zzy6>n2n#MkHblht?UaK(qA&kd_0$!TI;Iyx$Tm$R1(Dn8lf!}Imz zYyrc%-LfTj8Ao()Y3W`WSyU;hd@)nD4`m?{VIBpKhWrU)x2I3WqN%tddvHZhxd(Qs zd%sY%>_1IAf6J`ASy`Xj2O3aQI^;Znb{GIxM?FTsP}(tN8|@?)&{zu%da`7-DV|Sq zIStKCEP>N|=t3z>(n)1z7WyJuoF`dS8a%g7k5c3WjN)YvG-rC_vvJVv!t=%8Tv|;^ZIiBHp@#^Mgog1lh-PmH7i|r$g@p%=+jlI&twR$F0a;x~HC0QWJ zFa(6^^<8PN6b;Lc>s4mhBg=v&Q?EzX=@BOwcOnnV%8YWny=0esVNGf|mT!rG^lhO5 zw>|fj?OEf;zyp>|<8a$6o-EjrB?2f0g~}5eb?f*{;9v%!(p;v3_Rt7CUZ!>Pk%u^) zS=i{oJ+tP*2n z$NK&GHQYEZ_Ww?-rdUw1#83{)#BKL9^sb?Klo~E{a>RvOc+}utkMPi=N|{_SW#}Kx z-jP^})l@K_x$p`+f7xxmswLk7ZWuLIk1qIkFSPGo==1{qt${$$_Bb)%eB?Pdmz#!n zpMowuzI*NT?ggGM`R=v;y_Vj4N(uwM*YWQ2T6)yVeGBgW_qI0fo@rUz?S4N`ar^l# zKJH4Ly>~6VcM~}F;oIMDwiNDtbnOp7yu*91ZUpv!kKVNJ-?aapTw%KRUd9;Y_nLy( z-&@8@Q{jEKeDA^J!F>0jyvKAD!gsHgz}L<9e&6&5=+yi@C&|q33E1&{4#8B)-@Er{ zvt&8X>V2=jpV!1YZ$7^_a%1Za4_`Am3s}%SkVB556v-IOozn_~<~T`ntHO<0I?de; zOZWJc7iM>6{s^POu&N$V$Wy%KWLd`5f+m!GV~}Oxl5JIW*|u%7%eHOXwr$(C*`+Sq zwrv})?%X>wZzd)l&aWLiGQP7oonH14c=;1*#&95;t0Z3HIfi=^@%?1_bC3g zN1=2l3YI&eZQeKnnjslx_eC243Yu4lT@knRY?`RKx^)MApktfQ&H+?^lbOQa;9{@d zjhrQn5*R+<;P8m7t1eo7FTH-e%l&CF&G_&ayYJBx8*P!77zV}`j8ESDG% z%|4Z>27g_K+)GqDvj$=ZyeVV!mLA-DHQDvHY_A|q;6irP89qV9bGVJPI~gjjUtGCZz6ZELiWTXsXXhUw?1<6;qNribo$HL z9G;ww-ZEPUI9v)ma6xAd>rFpC(Q3+;`MBXba|(BS;}Z?|Wv{oe!da6L?=F8h^cE4- z65yT&K9{It_L=bk0(BH(zmBnMjjugwu$M^CKhC=I#mc}GNXUlrA#KM(UNGe#c=*!7 z0LV?au}UMG_0xa`OaVVOJDOj|%FHU-EkFTJ^XITST-dl9ofITv?3-(oeliFf#s5Jp zSQg)GpuAAea?J~=J@Ut8rnfnM(NnGud;`0_M}hNyHU*Q*y!7I{e^~ipVVx>|u1)*) zl|3Kzh~lt|5+r*pc8fC@1(8@9r|CIe5?xlAz?Hy5H@gv$!NqceB~^;0VZDy2%{;^x zgtgDmf&#kiOoB`Fbv+l3BJlROIX@f@V`B_8@k7j@S_;c_+W;Rzbig&}_nkm6JAr-? z+13kNg)NgAdwF5VzCJkCR3Sb5v-oFruRUPuEt)1R*-7ZBxT@1}&*r3m|cnUOd7P3LUZE zb<~%o+Vv^9vs~K=$m^Xo3TsD7==mt`-e+7gCl`kX$GyX+r3sKBbWy_mEonD6PK?55 zMI5F^jPig%9hM*UM6{(kPS$t&Gf&2~dq1%BGz`3vA!t7Cbe$Gq?A>+$sx&V>*@BD% zK4%adlEVi5r7yg!*PQp!agOvp40Zm+SoZoVQ!H$<*v<$s zh87NUR4aRS!$YXsFJQ+4;@-WGULD+ZvS@y5oM%WtM$lYIGgdu+7`R%~^!eVg^0PGL zJ@mTVFa4EFOyfF*G}exhmodom=VU;&etjct*2E%0Q+~F)(j*pwLcx2cgt30`KT2E{ zkR$T9j{3x`Q%+F`uyh=2ledoIpFg}!Xj&;DJgydBzpxT|MELjSG*ioiC=FJ$EOsQ! zd;`njx%&?NY?w}>&dwNpew|^eU+4B(H4raXLOJZkR}qj|+;6r#6!c#6VrYOEAD>r6NYd zW;T?7$qwkP@*f_LOtlKeBpgX%Aa^YMHn0I_^-yD>&B#(0Nx$Cn8uJf!urV<;$vHNK zy*dpnW1X(>qAQX#d!VO%obhZM)@$##ffnA6O(qFJ^=hak=k)u(mGEx>jglSsY6kmz@$>EA*?t&^z(W-`3v#bM)s1U;a*+~zJQ z4P9`I5B7JfkqPo2iT9Vv(HlwAS&$2+xy)|m3R7mC1um~uzcDi`1R1~!PzYI(%pAk& z-mfzTxn3F1NYwktvUo}%hM(_KMo@nkHWO3e;CA&mAr+7BVPO^Vsw7h$*96%&nHm#< zYe-0uta#$2iriCH$CU<387E1U<)X;>9EqlUm_v2UX)B>OWm~-;HNSCGPn3@)MH0mY z(a@_Ho5)1%kE6`V?I-50>=i4Q7cV#Lsgeaea>W#&O7%>8#~37oT|C|{VWamCaYv5A zxJZn=`C2V3m~6IaLh{``J39+e+|%dHPGPftVA9j(0@Kz2l>nuWqi0caTlW*xD9`>4 zMSt-#y^Vk#0+~njHiV<+kId@XITX6&xAd2P77}l^m>)8}3SBBpDUH&2Xt)T5qsHNo z&;y9b$oz$#$i)mnux*oTNS7MN@d*f+=MNso7tjB-~N8 zR}rgjCQAYgIyr3^7h95{N$SZUdo%J_Vsh%1@9fa-Z+k|LzRB8>(kD3ny}Z)=*kZ4> zt30Q5PFn8iOb6$ozEEylY(CQj9`UP57DQoz?+q*kEgo4sepk96M@XT;XfJqoRMOzK<5ox4g@;!bR2XcK_T%gH0K(cM4{da8JL~3!;yoDlk<2Y-F6B> zUr+Rtx{5r{lzIpLp)-}lsx@y*EzZ8oG03GSa*v*o2jIR2u=c`?!4U4j?$!pIl}}Rt z7g{KLTYvr|@I?Ty71n;KeJRzHBeEnT*Ml{G^K9g&2KSD@uey93pt&WIP7o1M&$c{r zttrEU4yR0QEg8>bG)z?HYy)~8098_i{&S@hST?%GwxqO%o1uAR86SOPAmX?r;Igz$ zFmMz_EJKP}w7^5Rog~lBy_C&MkP5LZm^vl06%Km!zu1TYar;KQ(pj|V_IOsoZ|so> zCbTE0l2Cx5=n$-XfL|2?9~xG_$Nkl)UUsJlckYCu+~h8-dKYTg`HZ_UANvf{q+Hwe zAz{GyZyNd#GSx;@RGPY8et(2)!3j}3E5ts)>VUP3IoYPE>*5om9_-6y4@2WIsRbt? z_bS<;G@Rkctw`oT+$v}XAU|X7z5Qm!hKqD0Ox^Vp5zXEo_GhhMyo?>vpHan>&PSL1 zjX)|GGcs%^#&d)q+ol5|J`?Q|porhPNu^Ms>&$VZZNN{Cc&v_bVxF0W_{qxY*TH&0 z&vz5vE)X!6=*`orHV<^PG*~MNP3k#VVyyPMi}A2d!9x#y8}M4mX5ywis6(5h8H#~k zGZ}b8%ePy|Q&Q?9^o|!53-%oUYXc?N5fQwfeh)eRP`}9VqD-Q_ zP_H1kNL$O?%~@{+I>PKP4U-&Y$Wi`^8>{iSQZ@d)igvvRLFz9lshTMo8}Ogn$Syq( z0IJX39f!zf@EQ(DXG1^_e65s6b(f-#Y20GggLFZ$f=Irmo|m$gh0@mj_vDU(v+E9I zlN10M?0yhJFzF1>Y!9$OS%n(}RK=@)XBhj{+aOVFZ%LCtnP2a0V9(7n@>aT#$DR|; zxB3(?*VrAGLs%}(O_)KP<@T1V8NVebAE_pr<=1GcdU83CUpC?q7EQ0hW`T%$*dUXD z4?v4p(hk_s)bFeTELjG)i2$~Rh5Ty-1^33ypU1DKg;x%L`(}Y{r^WCZJ11Uj2AW#j z5oU{MsEwJrs)hG*gDgPfeg<&msJ@9_n(U}Cy88*XmY$`INo)o}R0>uW6oLaMMEf+* zxUg#)i2y&neLrjZ1Zg6;$I*X={ieTLx8&^%R$7YwFYSjgKuG6IaNe~eY(o@I*h)4* zUfV#8mlL?hk=?VRUNFDB-7LwMUjAjR;~q!r*i-vs)J3!gtdF!o%X)eZMjv2~CP{Yw!9YTz0=kMlt! z?*tI`yEX#2i<+qK#4N`+e;!B&mmSL$To8%X&&n}Qnjr0{afph}sAghQB<#@MteFgl zJfhFv;l}IWW|rfYta;CexR{~F&im=0-#u$Ip$l0usRFhjz@+)`BB7jbH>zb7`{fbh zsHFh}hH`O%m+m?%f<;K;M`*I64O|PxfA&t*P%B2cP|dsUV@LwLvI%y@ZwW9#T`nS{ z>6Xe8DmSW=T9A?*4gr<#)``|3rqt9oam$m+9pHS6C1P4cA0%+&tFJDTz!NTsNe-x$ z9KJL1CG0uX)hjm&$3Vf{E4X$XB$ZYdsLr+KMU0Wu$BHz38sc1T9BH! z$Ld2Z;f`^SPG%oP6=PA$+T}7ADqq)xoRcdmn@88otfR^@%8x3G*VL?=#>7z{H6pGz zh={d!i_WtYa>~2_PMo(&&JtiBa8tQS%PXLCQz|jKbC)acKMrgjW5!~~L3|Xn_cbbm zH4^v+)M+ilE6$sZtOKr>6BJf{GoVN|i_1&}NiQfUagHyZEEbfTh z_X7x!hK%o^I*dk4pyVSgYwZ=ar&XcOL}>&tMM*Nftg>I>SrCHroy!G0NS0_E9AgZK z$eXhe4b1;l+&;r$^6u|su_tz)*4_TOvhnfR7Rja})D*j5r0jmIQyJaZeJ z6TMk1?no!z=R;s|WmOlCN5z>&hb36cSNm1HQ%BJ1#g^^P<#meK1o7?c`&%obDjxc_ z9|4cCj_e%tR7J^b8 zF_|vj)W+f2JxW*l$L7kmQl0G^Em@-fuIq}f;I!LK)yeO!Zw0GGS8r}n3F!!_4tL+!Pweo%f$ zC~MWY4f+W0wdoCVgO`9(J>$;5aO7a9Rt2TO<}Z4gNIR}4jCpStXn~L+nim^`zYIEO z%@tGF^bpjj)kk~N_`pqm>2q*kM^jOO0HrQlm*&$sM?iJ*lln*Env~c}d7=;xbw178 z0V7{joi>%_7yWjoaIP3v8Kj)6=t?dV4v1(zHRHy4%4H=C5gcV0Zd#UmUw`uZyNu#D zRKm9R6okDZ6Y&gM$eKHh8`B`fy5K27crAVa?UF3;YnS%2*@wvF;f`=+@tsZc?>z45 zoX1ye-xuy3`!qTMYGg-KYCv00HtnVCUyoMpo!m{46Yk@8`D4G$*Qrrg&WJ*%+|v9r;(dY zRhzG|eP#NNWB=msMv~V_LV5*LEdKb&Cr!z5tkxt%`Gx7)G>2DutxxYR9m}Qff7N-b zu0_L1`%y19h5!KI`2W`xQgXL5{-^R=wz9S&rYW)yDk`d-a)1Fc1HYjnA5v5}sj{Jv z#YGEnHM)lMTYaCv5^cvs!^%OoZ-MIN11Vb6_bJC5N0-;n$>~}EDZ^zb+ypqhJtDwDpgA7sU0fy%mxiH<_SlKx39BEzraFq9 zZn(`6_Zx`&T*k)1i556Q+`6*R39lpmutBdYezhJn?4U;&iqs9QUHZf+tV5wudWnpL zI^1%Te8SA7M4{rA!tz&(YBeQ1vZYJmB^@iCa`Jm@Ed|Q-QM(frXKe;15q|MmUNG80 z{srVeZVe<+#|fwJRO+yeaK9&xwi25&6b|jEFwc<#Ya36cE87#wlUTv{g9SWtF^3=OeTSCgVpD<4I8Dl&||S47wgBA|N<&>kVRp2z^m z9f7C~e~*yKu#I@m^f)7oLTjJir68`zTta*)!K7S8#mVx}UZa8WQazUGD0WM zrTG)TQy5$Tx^emRau|2K%r={*_!R-XMKVn@qagMf{!^hXB_vG^;FbO!ysl>Li}3yn z9t%UmbH`X4G}K-rI^NW``N|$(L$TqdIA9C96mPHH=-LQvjFYe#AHMbgb!#Ov{Wji+|}lD3obn=^C)7 z9S$32?_UHWmC6Uit%rfi^{K)58@M)R21f2-xZBX1;O@W?+;)NOe(r&5-~EG}ulr$! zfoel1Tbz8d8Beh;&VfIIz%et=RQ?>+K)+aTpx@{v&ohOLLLzb-AvW_Gd4SDL)4*>Q z0@TmAKl9?V*z6-EZWJaw9|npN$s?r4I-|$_`m6Q;4Sz=K1Gat#?8^$=>mj&e3Rg2D zpF(&;T?4-5_Idm5_#~=pXAGj6d(;IP5d{K-+NS$KIAD~68pZrf2rd|2s(bwAd5?3s z`d*JuTG>F|zstU3x)7W`1vq6SGB@`qte-gAOef!Bd$JGJs=af_6_akt3kCbkDH z0(ZcA{{CmU6aNvc>%0`km<0s@=z;|RVETUxcS;WC#&Y@&j>ZoE40P41n~q4!DBs%V z#m&Vmte_pBnt*G_Dh+ky>^1>7WO;hnGmA(Udg8nr~@3)Qx}AQb$egIpLPQ_bduI3<)!w5O%%3cb~xLx zds*H0gwYjf$+nM($a|9d6e%MVb|jFfDZG^jSx`v)Q)U~7MHta&bGZ^Zyf3PHYOJ;> zN+V(vsnaPEjE7|oqbkyWpQ^Q-!a@dxQm@~IfCPAW3Gj(?f+*&(j4zj39%h~9FgcJo ztToDPkxQf3kUYbb>}l6m0F@uAj5t7{^go&?CTr9f9sVpUP*{>WPc=`-_gBBEWKtz0 zQ2Q5pJXJ7Q7fFO@OBL6mU!}QsnXI`x(szeEyO%04-s?~*(k^qI4pI~f1XIGz+z0S2 zd#sTsRjL;UoK}!KIyQA9%rLS##?xXfR*ag#TFsp8!+p3TS--_CGWmPcYprmu)-`$_n47DL28?9?dnk ze%UhP7B{X#O`Uv7a2!(;vaZ@S9H|#8Eme2~o^y1mQ||-QY1xg17>{T8;v@RYDIdw^ zSvNxo@l2D^#Hl@6m~melh2_@791_gMl8=do#!01~Wz3T@AhN&2t*N(C1P_K_(c&e;Utjwz{8t}xdwCrQ#9Ih~T`$j`!N&=PdvvoEp*4Pav24c3)7WOUG zp~XZ*gcP>b^exH-m*$6^j5P?u1hM6zvYXDfWZMG)|}pl0^R{0f4dHCwuTxEOZU-X8KcE1@GTIsT|w0 z6^#=IQa_ttZoiEa z@j_~T5Y`(c4kj9CT_wQsfIGckrSi9mqjss8+XYf&R$PwEFx{v#BaJ*b`@}85XRlMu z;lUjo^~KS#1{yCIh|5?v8Ol-rE5A&XQUp~8M*e^(JYE9$4uh^3BVIr4h|DucnFZW3 zsTYcFVG`E|l>cE_S!$wQiSdz7L=K zU~B!nt1+JI?}D&G5QwJ2)U03Kn?l-bxWmhzOuvV<1Gsivmt8U({0* z_;K*bB6`rw0E8o{Hp# zis2yg;t5H&18wrMyh}RdPA9cv+7_03f8FEhbJ{@qr|a*NwE` zg|yD#XZyzI8$))ByrYPyu&anT2SD<@D~MS{=KD;bd@{XlhaM;-7O*Z}-X4=50t?~T z6Z!{5I!GP-R;aAD^ir=3@Nr&g$g@`dB$vhFQJ_mEv_08X{Q=L(7x8y8G--QsKyIVG zG5eA?)_WquXaq~zsG2Wh3G&-<{UchKM8e^w50zNY_X9(VwvqqR-*OpmB<3ErJbJP$l8&(Klj6PR$df*Ti#L46yd`hSk-DA5ac(_j^qnyJ5D$pi|FDZZy)g1QdJc2__6Suwyc=qo@ALK6Y#+ME;%#%`VR$q0062QcYz;J{EyF1T>xO~VEP|YqyPYc|IGsO zUlxv5Cd?N9o85mu3Gu(!nHgLCLo)jRVE+Tp|Mt!QJlE0A{6B2L|LrM@YXB|3esG)= z1_0n6daD0;%75Da``H5ej^>8a`VN-=j@!v9>yF6AsNauTjEUl8Aq2oc;n3h2>Eeu- zL~gr?=s;LmT1Eyx`vX%&OsDjxu-x1Ty&Z14n;_1qa$|vPV>A|#;|W$q%1)I_l-n&W zV`Sb>qrUtf-rU(a`rst z@tfeEM=^sKD40WmMuOq{)L=^TA_w6?=LLRi!63O4r~@Jg{l&PwF@hHjnv?>Ug^)%U z9dwZ5;-S&OMjGD*$brZQQLpEs65u8)I<~w4(m@`V6mZp|ri(;qz2>y%xJd-gt&u86>?;>H z77;%{=FSw;EGaR?i^I=6YqO}pU@1PNX{TL7rlv`S$JBTM1@(L*c}XnycZvnJZ8wz@ zSVWaxuChi{Y1%qr`8}a1ZWzU2D{kJtrkjSTJV{{8Y+6XqI8YM@n2S?9oiTHVxKsko zlsR%d)Io}bWMPv5jKrDTnR2Q)Ay^?!?_Xu{xNe21Q7CzPOKE7xT9H6@T*s%ZZB%I* z!AB{b)(=7Q*LF4}th0Ld7otU(w2avwh6)m37D*PAae5MF+JHKwrSp^1(lMpdD2zu^ zu@i-iJ=y(Q)jmeF#kl%uj^lAl(LW$eAvP2lw0ScT#`IMXi=Ezp)T5=Arkb(ZQ z;w*-%pgM+5cW*j&I*jP%GRWs5TfnGIQ3aL|xY3)lb7XvrvDs?3{8Z;;)nH%tNaqCc;+98x1?k41ekz@_53au!PvQ z$CJ2Qc-$m=ws!C)A)hn|rU`&AVneVHW9U75S(5$f7pPd}CEcO^ zy$PM$27SJyMj%%JGNBCujP+R>Lttvn{khd-d&KLJHn3~~4>rGNqYves7c80473zh| z0SEZaA4(?h)R981HS*?0R(0*7{4(~KVGak?sA*C{Q!{- zH(61fXx{dmGHze&s4jjLbOIM12TF0bL)m@zz9rqpPMI#)nq{j)?RFgQnp?H}rW=>2 zCoc?&=VuwJ$5sbQ(_>(MnkUZ3HsEiN`eP$_K^R3YnP@=arPe!jA^2C?I99Wan>vqt z!9fG=t~&HikT$up>B%0?3CnWOz>sCC@0yd0kvCH2ddVbpKeSDHWHR#MZGB(ndG@O< zG^?9#f59KKhP3b?{RpGLJK^3Q+0s7hI)^mYRPEYSz0FYQxnPpYq_ueBYem@Woc-9F z)nU)c9fr{Q9?>cUoAuj%rHE?9ZcohY@S3pctQDebiqAG~M$>AewH}m=orR3!>AH^Z zx&dt4FhUk_txD3YL()tTBfm+K{89(=gPtQury!c#lo|C~$uE3;6ZIQ&cfN(M&O1PR z9Vml1&H&=bV@m;_g_p;G*IQ~P_vY#y8HRlv(@SK;w>4il0#FNG{$dyp!v|(G96Hix zw{;qD;S^d~PAI$oY%KpnboB&yGo0sSD}eVm%A62}%={$wQr7WYR(n7S!Yy;G=KK0N zk)xU?g4Xh-Db14PgdtY&IzX*gS--260P%(g1Xlm7ivG@u5~B@%eTrzKPXG7A48*1ShMBwQrC0Yz|7Lv~@e4GSkE z6_JK`8z)$YpLw009v_MQoAqbd$D!S#L?a!p4(rdMtXd5M+g<+x3#LD~V#q49xO(qE zFg&5^AX6x1G%sglEy}9wsVxn;)UB~c-(a5h>Ll*T<{r0Ysu7=F{LrAKkQ`OmNlTTY z?}J~jm!obI6EUPkmr->*zn)Tl_PYAQL5LHG6p!-!Z&>^spJB=VDPoVG?my_6tu+Z8PWx zZ8T{5k1v9eQ0&4LZ&o3_w(-64v14%~E`tC_3Gjuj6iyFcs$?=@YpK6@F1-B zf23B-ID>hyAe8XGq`ZRTxcH%Bb-_&(Oi#Fl$uq0+SoW=oW$<6?GT$ynw}Qm%SaiCezO|XC@qG3vhX=$>%ZGay0|y6hUHf@Lsz?dxF)dty>7E8j~=$~ zS8o-200DA44Rh)c&Jiy2?_GZPMumO zyx@K*83m=uibB?R^Q$l@&Jat-P9TF-6!HT=EUY`V0$~JqPsqojfNg2u2 zDL9inJl2A#{UwY+@2shH&_Y0gqP0Y^*PyFA!!%(nRR%VZCo{3dn(%k|9r*JtT>z!pxO2B(7=o%lgKEpBFA3LY&X|{3ND65#F4p%o+ZaZ+_EQ#qFw^eOF zq|-R|W)5J-ua87L|4kw`A`dPJ8!_vx;@}(ARPM;jWfijQ(6QL5>yf`j^&lzIF=ayG zULoQ~vG1qyi8Q7zZ;KJETI$|KKp{;bxp>K4nwys9Dk(=}ah#x4NquZsM@BZ~R0OHk zYS~!m%%N13Rvf`rwuj${Y0k=Zw(88C%|cu@Qlq47%kK**G~m^W*GR5AJtVDQ52%PJ zJW-DCjdMG+0vT4`7@+7K)2G`?nz>(tng!xz`dfr%)@DRBS&qJ{Ds-%hqiT9vz&ysW zPS`FfNEK7LTH`otCs}KUf32nJ+vw}9fk`n-KGrrUfhJ^bJ}qO~tVO=6vf5Vt4k}sA zwD7Zg71l9dkS9Vs;aWH8kvZZ*>AB9|0tG#f=uSX5A={s3J{R5b@>t^Gqy4Kd^P@;q zsPsjtO-9x6Vt<_hnBx~~@iKlf9ua+O$POO(=CKCAr;eAOAt(W~p&!#?tSDw6E@iZQ zfBS%#EnW96L0{r&+Xp~=M`CZsRz(Cl>k|FG(64N?Axz>I{k3u*W!5k#9V?DKgdV|^HaAkVE_o6k znNG`>?QA1*ipa&8!LVNor@Q;kU=lh?4a%VcIO`-;wfCk2uUS#L@So$PDMr~ODyHsu zhAb(%=#BooEVG*#-HW(DVS)=F;!~72Xbp$bQ*d**V+|M);C<<-fV*4Peyr&UVpxt*r@& zQ=^RxVanCs_zn19BXW6hND=5S0D#+{sQf=iWD#2jYkemta~o47XFIEZ%_M`Bv}KX_ zk+}t-Xx0w}^ZfATs3kg@kb#kl;N!&tD1Jzj;d4_MQ%K%tty(Fkzo5STY7t4`!(U3m zdB@yL+vUhJUmT8Ijy4>xxS7^@egEnLqzyFyp{RpEl4??nhtrevHL<%P_evo*LK3D$ z?$ig6rN!#;V^2cND2Ch|oeTIAsM~EQ)EIPUb5G#dHBM|w*i72fY~Dk6G-=ahdc_kM z+)4=3*@x8h<$uXcr?+d5S-aEfpR_G<+GRh#<%s*eu=G3~vg#MKYiihQe}?71PqIu@ zb_wb{fX^hbS@mm@hAdb|KfpS7o?RsJiT2jLnk=sEU%5y(RPG=r?nq^YPS~6JQo1j_c^FAk)R zMld-Xt9PSR6Czn~*?jND(qh|~Mw8y&l9QMyw&GB39a)B!U!J$yPYe(e#tRXeQQF{7 zjj(wr3oBF|q!&RKIg>aq8I^4Rb9jpDdz(Kpp0hi%V7^}8h+?A(-JS#7I<_U>XOHBv z$c*%WS%KQhEny}=Fh4!B%5DH0NqMJcfc|;Sq&(|Cg@vstiV~~&z*Q_Fzm=ELe!1__ zv*h6cR`^g%{?HseOW8(Yi6}FYJP}9r%5MoQCB=73`{^NqBHbj?3^63r8$;sYS3vm8 z(kH7I*RX{N`%K(10?eCeCCIZb+81OlZBc4AM=Aq9*FXkt>Pu8&EHm?=US(>Q-$ctS zg~*ozNml-fy~ZFsFz>_~_@0)yrxE=m!F3uLNqyCtk`x$H#?bEgH=l@k9+tl5vjBmP zC;t*VezWF;Lc`YrZTItI2aX&|gB%HyOOX2hu%aBWNiM521U1Yh{$p%DNz%>FcvY@I zLyIy#I+^G1c zCHFt4{{L*6iJRKkI{diPPao}H-c+1wt%#(GJUks7#D^JNVAd=35j9bJ!%9d~zI@H1+S<7!+nn=KigVu8TYh^W-K7LC53ge0(S{L_&Q(%N z=iu-2*8R<2*XP!_kNfLf-(P%STrhn;?07-oTKIp&?hr;v62^%!N2y1##S?_c(#gc* zspHUzBZ*bT=7=#669)*@3I9kKBpQihCt#1VBp4^06SD!`*u!I*yXN*x;#c+1WNg)h za3Xgr!&EYVC&}1R0l5CMqXT%xSTJ^8pP5T+&t{=k#KAD?kzrb4)k{|wWy%(($)5kD za?LF_MAP&!l3O%P3vtZiH*RxI@VD)eID@t@3@ML2WNd8b&Yvu?FXl0s)YpW`-0y5V zaYT)x4m?UTE-hGn@H#ed;+Lh%cXd)~tUY-W8*8%amwL2R@5=0TA>!1xbbN~we-sCW z*>x@sPPkga*ia6M{Zox@y5m{~5r``8Y$e}OB0Qgw9zhAK?mAyj$+U{hvJP)FqpXtX zOm({|>^-$>U3o;qS;(=X+Zg;cNDXSr^_0r_Hd9t;$1$*@rrtF@uDPfet0lsQ;}yV3 z%mMA8<8qD!)zT~#n}S8($pAA)PNpxHJ6SA2{F2BlEEjzd#$lVhJiAr3!%IcCFRC8tfi?aGt8#rg49~=XC=}`7wHRa1rB=c zrV(vz7eJ@*MYkFVRsbmmL3Ke!D||0`cM;DW_5wqeBT8csO>+m_qAU&ouSdRf?=r-2 zaF4cA4O%z^>$A?ILLq@x;;PC^QrIFAX9&$yr^AF6PI$C72%*6tiB}h*@D_A z*+Ut{bM^!{YryJkP4%KeuLZ8wUrf^z*UMmChWhu!x(K9saf`pgI>5ty3p1u4zm*uM zpm3n1+VQX@4Cck{VK1EqcCbaKuxJ~GEVcSV619Z{DvMo&=FEv8_NyMsWJqyoIbyZP z4RaTreFg%%HrFIZIX@pWdpM|fCT12n#(-WDR+PAYIjTO!25?Fnb_%9_X1^Mf0xPvW znp%LtIy!2!habrm8%oAQs?(7!_{Ra_cbU64Xy&C3d2eUwoRU^ITl)}A)~3egj?Gh%tnBzA*-snM54OF zr!3O+7PR%;Wtcg`$vSTTqK<%~_7n;PGrO7jMUAAO_7qXa_0z{yh8;iOW6jl@oNv&V z*Vc0vw0fsO^*#nfT|`M?6OOz_NWp6XjywYz*DP3+b<4=*?kQ;Rxoa`Ifx`Sa14R8e zRb)m#o(9W%r8CR&ft4Pa;BDsZgk&kqagO0Qvt|V@ex4I_jGm|0}Wi_aZYp6~awv5oP$B zjcF7*5e&kgA0Iemksb-W0}>HhU4q_Uj1XMkbF`BfiHynq2t*tqRU+~|0;(Kb$l78M z)ru3kpnQ$e*}QUZYp|GByQ;ZTrTW2lyEDj$u~+=>^<>xmRL61F_MhVo&u!16@z4%{ zi6ICug}U_+7qEz1Ypkn;Ph6}k7kn5`(L`Xch8;>FdX#oO@%vejt4Lo?4%r6jvII%@xQktcbq zn$%AOESrSSe%1}j_d?bU+ILYaIpgm#X3uYUr(H5uuA?66ig?I;z}TDpDk>7AF8%~RjS(RAL+LYAmhD6+s?%TgYmULON>tVlH%x5xDpBW0pfUi!W3f z9mE+y?D(3o_~IjMI$3y2f$7m8(u^0X{i-tvLI+p?I(d(zI$zzQdpqEwL1H5C;rNx5a9cG<^aDfvAp zG%(FkgEi7})p?B%>tGrhjULVN5T*IW)lcnouV2cdl~?!2s%12h2N?+bfSNzs9mpfa z^R7XBCTi8o24@V*Kun;!^6lt|)X>8rvp9{MTOk38Dw|=Zh2i*MVsDFnJiIk2C zS=vsQ-m#AH=!DN9rb zx%<(?G%Be}2KT0Kz<$AuR?x(j(~uc;@s^QZ)-ac-jv;$S?@6oFvzMq%IObEXU{=ga zcsqo;3|;p#6$64$$%{9=G|0(;wPVEV@l&mYNdqDMb?As|(M_48(&`U4w{}zRwRF>t z@d#X4Z=rf&@hRMCeaQ}ByG;pby^RSl0Y=s$&SbmwoEjO;&@KtVQg4M60H<<8ySA@{ zXnOmE+!_XB{owD#yCnlbGpr04xBK&heG(ws1Fgtf!Idalk>|)8epo0QQJTmXeb$gI zyNFm{RXc(o!aGf02RwgOZBU^q$0UzwbWbRiIeK+caih4Wv#;(zT5h5iynFp3Q^%Il zlvL}sid&n_YA`W|MIpZ;T-&BtS8nP9{_diH_@<7c%PVTAspu$dj1z`_2=?;c(t&&r zk!6)}r3T2};?*psGnq^V>4(Ft#OB{TZrgcs*F^YVgVH${`93(zNU^iI@LlY&`!STj;!~h3g(32Fl}c zvB^Y3&&Za4c(GBScv6bdk%Ai;x*ne`>Vp7h)Qu2*VmJ-2KtLozT#9d%bDfp$?0sBA zQ)M~nl@~0)9a=?~rYjDbO`NgTNxsXUKyOs&#<3+bgu?aQj^?f#=(R<7{Q>BnH*{dJ z8N5i)9D^zNo`vDHTLuN{w1@_tTiHCAEciS-7)MQ0omIeu#KrySDL2h2qakJqYm@(N z9?nHPH!`K|8qs8YOiEs;Bb%CgXMReNiwgIWgvU4d;RV18B~l1n4F7_8txy;BY}|E~ zdjF-B^t?fZ9vPuCt%@J{{l~YFJ2t`VDZsnpoqmA8aD=kOJfsd)*(!MjKxyCW$*>O7 zQtD7Zf@DFi^-;d4s@bigpbk5V$st_dNXZJH79T6h*Rnoly7&*~{DwqE;xiIlOOIy$PhI1Y1$ z^*3QUjv3V8A`-iY=CJJ0c&nc9ao*YO-^17aZ`)c`#b~WU#(1Fm*>&sV+@Oa#ON`{W?T06##GQ}Dy1+w(n=+1RVecNl5lvhariBgDW60{!@}{Bf zqep(2tMoNeUmmL|Ocz4X<-yS1-}nTRJymkm=^B#i4tu-(g1am71h%z5Sa*ZCuJ_Ij zJq6<;&mBN}kt@1K#tV-fF!HiG@W5zPlK_Qz)C!m|bzQv8o%pNAG0FF%gA&_};Exs*+T#oNv=-FV#lokS^=kQ?wQp&E zk-Yw(U3>^a-&W;GHg`-`2~nt5YD7934^!1X^^KG3l3DtLU=H|@5PeHFVomUc4E9k2 zR%_MZtid)q@@~K539j$l4uu)^4`BZwlrk(p zs|l0N4cd_#0E^U{3`NeSQ4ZMK}YWXn@V35N@!Dz9uD5&RHS4ATm-B`W0*W-sRAoU?K0f&0&v-WP2 zxcq1UyP8CAgxqn7*3pApl26=+5vdvVV#=!e3)}c|;G~^Au~$3ZMbVHBpKU_@FXp~7 zs*Yt_7k77ecXxMp*Wm6BL4#{>cekLy-Ge&>g1fuhTeCJA%0=S*p*i#+GzbY4}7 zN&mH+NaH7yhJ3MQX-}HwAaQ!6V@o}Py%Y#-uZW;pZz@08B9O0*wZi)bl0Ni?RFDvu z1sSF__^ECa`>ntmw540PD>soH_U@vZUj#^q?>?p4U@_?Ge~^d>!viyT~=@2 zB9a@wGAEK7sWKQK ztjeGOk!aDqVH1D|BzGkyGP~G3ld@Yu6N($Ga=+M`%zP7>QcZ;D9;H1?87pLjH?(py z>YYk~fyDe+gg3Y{Bl&T%*jhZ;@tsrIE0Q7vyI@dXZ{9iCvAS4iM%fb5olb#) zH>k3M9LQY>*LakW!fyQ3~Z zij)cm?wFupU4Fh(Y)x|hP3(;5*q~rtcK#(nsIwEXq6a@Tj`sud&-5e$2!YisFc1(Y zAVvA_(~}BDc4nq>4i*+>&i|882~m-Em=i+s&E#@2hI=DY640@vrdJTsBC;39gHi+0 z7A9ePh-u)$Gjef=1xxR>g36Oh!|Qn+9>{OTwWgtM?`iJMmmNNO zx%NVMlKrI_PGM*`$cP>1ij<&)Pqy#d42$eNR>gxE#RO=m^YkflV>JYrexxY{Pok6I zA!bLePJN^mS!u6p4kkWp5_i9Qkt@pn(U?!6Bh{?vEV{epE|H2f3Bbz0X6#b*xmJCZ znC6onrKW$}Y#mYeZUpjPB#c-_8u59h!3YaI&u)i0{H2bHALYxvFh?cV$#1u5f=w$h z`RT_n4nvFOv*i{=|DwNo;(|D|+OupXvDG(mX>|9qxOfNidt1Cz3$?bg%^ksK?8V6J zdoSB0zX>JEn%K+>h$wURLK3&BpG8Y5;?NA%XpH#?h^93B6)2y%R6#8bOT_)AQ`J{O zlCbw=PGFZMVfkh2tfPQ&(B80t4gjRvnK~^(@4>TSAl!U`r<){4G}_l|+^vhx(bt+> z;(jlLes+`HwmwW%b1SuvwB3KpT5piVnQH=6q8|ecnXS2e<{U3o}oz@1LUjm zhW+O#)z+a%!gYkHiyQZfks=rk00iCFOxwM3WA$_?`q^tDe4e-aAyJr$1oCo8mLl`P ziI8{3#vPtlrmwhs*#4~gvY@mLTeUZj6BjSp8$Vq@=oaJufXp)?(BtB++u&s}4F1q`nu5mVJshFf3&=Pb8k52rC54Oqoo z#y7crO;(u9;&eQT<5>v|n>;P($+NsEokckiVf8wSBN_+MUO0Jzw9&1qNx0(y4uaP!0LwOo}~Hm^~|a;#>5YM>BspTc-LP>A;dT@w1tV z&16?w1AFQ5u8hW@k&Cekq#_T8`%{w8n7~p!2u)ss(93$#Nz$6SmdpaYAC6{Oxe!)& zB9Na5fn_LB5^$h1Gis zktqrT1An5gFFH=5pkCavSF;3fMrV5lqWabD2-%SiNxlon- zu)x`sd+GP_0WpAqMzVPyM_(=WJm1DupJAHcSqw9=(1%VKa9sULAiFH_`=c|ED>HXn z>9?7|-NT$#iKR1;H5W-&C+@aXl;1~Az#sMw*9n01^QB zb~Vct7k`6}ayB(x%L9zH6afG7?=vs|T$O(yUqX~;WI&XW_%$^-CuZp;>IovD=p7@x zp`r~)$hFv5KF6H6>(*4YJL!LUukeQUJ_LO(vvk*FfjQmFVP!oShw~5+*jNX~`$P&4 zKSo`ZmseP3qAnRh>_wt~l*=+eyTI_peSZxmw2E4P1_#kcME@p7G`-_U``3{(`4`WG z`PVKy=F_yC`j~hZ4>qRfzE(_Y)5ew55{p$4_Le=L%*IQ*JTc*YNKSq|_cSq!2!(T}e$CN>#lt4cKhYZdgl`sl_JL_OA}{&f z2h`{ZuDJYSJGG6kki1&zb;y=8^FR|Wv*EowmVV$!1P+=sYh&qsE9<`vJbcq z!WgypT(D5G+{-|q99bV9Gm>G?c2O_0QQm?9lF#!!rKm6cEk@z<0B$Xv^VV{)Qtk)H zpZljEb{+jVU`oya#*+O1WhmwU4!2Mlv!4?}<6pAcM$wrqK^tv=hjpZ`055k%bx#UU57f;9fXboD-aMTqNf;2HZP&?d2TYv{jl|P_cE*qG<8)L3q#PR zjE8rjmF}t>0h} zwCtQwTW27gC%cWRN(PmGqy#L2An3dQD}`Q@-WSDG(p>twYJoR|=b_jyUII20M_V|# zuqHsqEt4r+@3QMG%#j1t*d+4dQ7uLhHOoBQVO<8s_PG1iSJh_w2B5Mfb7pMSLq6{` zW`l-U5iwVLVnJ16=hd|bwVe^_v`r>#G6U}B&(x8~8^4-RrcNoMSUYomhO+(~!a3Jd ze?9Y$+!*9nJ?-ihd<6Yd9mIGBpp^it6BB?xXZ*M7pz3Dq;%epU=4$rW_W8#`jZwXD zm{mgKM{Gma0w2O##lw}ORQXgXxDRQ@ppDU-rp9D!zK^Mz=T5FKE5qFDKT>1OHpVtH z;CmCtvaU(PG&8_<)_KtdDEdBhEfER|`hz>da+-u2iw{`C5m$*J3%f&bSBX2JFSRr? zY`2AXgyX_FjWNTmRa;ZGS-bOx7OgBA*vL$=(uYXWz|F0e*m?89Z3TTEAv)!z&nC{# zXfimX z`SQB4lShipu4dDCM`2}%H$(Uq7;AVMp&)Pb9czDhm`h4E-jIn-5vF`G=as-O`Zd-1 zY;$ubU8uj-9)agk(ZQE7t|Oe2t60E!(TY)k#y`^6efY8zK-MZy8pGq6}p} zI@#*5_@lI7D!QR74!%yDXpvW8=-CAEZt?MQpZb{*?Gkblz78d5R3 zXA7lI%R^QJ=U+w&@lq@-aDvZ%CpH$Q$$5Fv3G;#N^l-$OHB3Lmry>pKo?Q*QUNMBR zzyi+7W! z?t6+~J@k>a97dQ(bkE@q+XlJW2B8*y+06h27k9 zijrfN0JT!3DZ!DuG}Q&t)6G(r`&lu4x2qU)t%t(i61l@W#pN%m6=BCsTy0TUNMp+j zAlZUXs?C6FF5?x~dX80di)Irh7S+NvF{)4(3Oz<(JDr^;C{L?9UolN_itp8t%s4|3aRxM(;zIHI1BzLB2s9uQ;ZS`cXp2#&uYB@=&a zzO@4G9tp5{|E(8)p$Psk%yu!oFhN2{p&!(9jX@+qIYh?^U~3(rNND(NZmWwEY|2{A zWtu0`ptnjh*5Y6jM(C-RiXKoG*xaQu&*+$i>ER=rmlg@#hcb^TV&&(qflJlRUgK46jtk!RN0b zzC-F1j5~cv(;pHa=g?;kr`EWz-{e5@g9_ljD23!hiolzfFY2CvFQqn}qjM?#X#oh? ztn1GK9X14@C@}t*x<6vMM8n_MqrnRyS>&Rm*-_VE;din|f#tNvpu;aSGSMLv)1N>P zO?5)uPTDf%ny<%~u6q$T!&uh!70UcIRDF54DIebV*Voc(HU#{M3x*r@^I|~kU}*5& zc-C&*E&RLPT>T!m^T^A75u_=WV5d#n5DZTucJd3uG8Q^wWikmc_l``?rsM@#?1V}UjNAR`t%F(?USxKd)(FCc5*)vM;Y;xAn zCZ=!j1d%hyjXOnM$|vXa4?!A7yeHbA3`O2hK4Z7RRp)iV_%kk`Uy?tfe1`4Q;8JKYn{sE0{x%y*<$$B z60>9tyvO6Vw%|H4xR$bkBC`3?)X@3Dc8NiVU-vQw_AEtJh?HcAi>FDYVZldd+S%Ov zH#jTTekOn3jmu8Icv<(^;Pt!cyjaEbyZ&JjVM&Q=gp+@Osm$AnR4^4WP7j#Wggr#8S>hR$L9R!zZ&oW{Iy)ju&tMC)FF7Vu2`m`S8kkp8$ zz3&}CCxeB;Tgl6zn$#USTkJ43A*zoW!X4jImKrGTTjk-{ua=24I3w*isXMSaZZJZc zLtB{T9{Ql{Ra&&SQji=>RCr%v#S;79Kxe)!ugS$fzCC~O&MdYVUQNg`F&{=`qCanJ z_*QDsxzm=+inc!4NHfEkCFfRToPE`Gety`qrL`^P`pMIg=$j+$T;zJMC(R;Z970rx zRsru3<9Fr-3W%MnEzeZJx-aGDo~}gLC$ex4LkXSu<&%Xl7s0{1n_H$dDL8WCVQ=)G zLs#qA(xedF-6ax#eCvI5q(hc{lJ%f#na?~81^)ug9PTz?LRl!8#-#x*g`(cnB3DC=;Psg%xK* ziAQ*Oecch4s(LBEH4}YIrZ#E(STvS`3}GEyAUAKd?y%c1+Rm$KGN)17SHkHwy#?!G zM|ec#AbOLg#0Vo!)#mXq*Qv$^xmDKClj9wT>60V##WRW2q6+e5Fx+3vfkP_u^-lT)uq9q5oM#I9DksmmavY2v+$mfht(KS6(-Em-_Iqj!I z`#0$71IY%YL|vTBeeq&%lu95?(NE+V6J~IdQ|KX3L)+K8gC$JKBiAV0H6604{*)BCEl0|jnaDn4)vCLMy1`dxDuX~*71YxsNo}77S4HTMTJayRYJ-h7XbTLMgy;^CU=;P}L z=zbWKEC6q7d6_%d(|PRo^;OQLyibclUGq7Q3$JSZEV04O{MKAOo6~enPMg)}qOfwv zasy5yv`llaa+H0`kn9)hEGN&YpRMkV`_>_Ifmx+E+n#1mTc1%TV|N3bcg?VVhT0_4 zry;!D21Lv+u2|_BZ!UUUFzVx3Ba>>SQ9L{-aq`VDK+O;ILiK3ra4G%X?FUPNSCct# zpN@`_4X%AzPv|1jh2|4bGwWHPt9~8}FT<|s+1b6RKzpNvYc|D%BANUM3yZ|!*wSfE zk&z{84rgn3_1W#w+!KCC>g*wb{Yh|Y9mnK`uISs+kaH|&;Ds{i-nz4{`m^0L)A3I? z8k)IS+?53wB66c}Gf(%Xx`?AZvW;(w87q^E{M8nX61Mh7Qn?fpuI#LtO{`^v1x!|@ z@(v=X)pEF3b5LQ0CB&a>?CH3!7m#P&o6i@C#;5Ve1|~-udVe@Ux`}1NT=x`<6IcTE znZe$ImD~;fOf$pYX|w$-5wP9VTjgXvD^&<*V=fK4AjbQH8zOHKGT#juqr@MFjaXWC zsNEfZPDU944o~d~shfGgqvBU@*jB@Qa3GO(imwjVrsPJ54;}2`W#e4aO#p{+(Os5J zkKs^~`-&qR{TWu9Es~_9OKc;av4pR!l@%{+*umRVx?+)Rt&2&-f=cafBC!@5vqslnyM@G-!$?ak=>`AJ4lj$j^D0h8`Aw!zs z1*2=xsH&A5EPIkV$EXA0jw;;WMDSFfupY|r4t(Y6GJj;h#`a^+CG&gqjthHrx!=(D z>TGQWWFZn+&DJre&YZg9K;bN0&t$SLB_9CMpAmxL1RaE&f(`+m^|{M5=x0apCq4%t zB7AAyNMb@;4ENeSh4#o0gSm>Yi3l~Hz;<{FAznPf!_@GY6eh5hRwCnJX8GWc7)~%0 zQtKgGR?T+fW&`Q_g4g8M`0SR0;M$kbGUDAwkcfIuukD!yD=)V4gT-~;pa z2q~`Ymhv`w)Qb7W;Fgl0Sh4cf-5YW)-8h0S0>pMoyAUOvG<1J|_A;3_rJ<_an4DQc z_j~4KvtARG7~a14o-k`U?sx%Q5hd7oV`GPyZOfHD!~w&+#M7^_#=Fp%CiVN)gwu1} zRzgP!^v=L-x#sTol)=Uuw_z;zU*VLnA3N?bL-*daz4En5m;;%gsN<+rrPBOM98PgC zzhTvLEISQaF?m|IOFTM$hW%2#)P22W%Ik5?*jckU1vyCsgUC?%9{+$Nvo-&#qvw_V zLbDkyc6883xoDRnp&Of?I=k2Fr}*p$Ei0ZfH^W79OkEzm08MYZF_HJuC${uR0&I-b z`_~PsOVGB4omNUYr;zd)N=kSqr?sxlh&P;=;~qT%sRVEi2%e@%Cj|-W z*md|FpY%9aQy-i3SFiQVUB{~__bE)ij-E3tXr%y;4#anEMiEnAXcdr$`jJ=dT~st# zAxNjCgqkzhh)#tfO^hC#j-?hvRQe=|IX&IS0DrW3$K~|i8 zPd=uco@Kb`i{Zz>p5;C+R4fbTCeR(}=nXt9Uaa=OE9q$dQS>21JuZ zR_-Q?puOZ1R(Iv>gfy&B6zi7jCK3Oz78c4ZBGb|F3Z!9B-J2G#XcJmE*!52nnMXnS zpo$fq=jx)&oAQAjZ$3y*9aov~swnfyvNoqy+l>i`+I*0CeS@q!^wxWjIFbM=X6E%HD&??RWHMD0@nLbw)!9NXqSwNf2K%XC0wb&n{l z?Jr2dB0Kw*PX%Mu@mq8PN`ACP`#BHYqBN`=4d zA0m3d0_tNSq8yk*WsJ{h~za&+!xjGxS+yn!{@$kV4_XLM`K zqV1RbqU|BYK4e&_w{og2l=V{{RXy%7R^6IiiZi&^<^&?P;qOo~Q$$UeM#pq4`zX`y z@f=&8>rzungjq=im_2V6R5*+QFWhZmIg+t{8X29id!oRpOidsO&s!|(_Yn0*YbgM=_mqYO6t0 z*li6StK`R@h-}CO949`|+$b`d4GT!oU5%fQD{FZOjzlu-Z3SSjV0zZYXF6Jalq|yq z;(jvrT&4o;F_L6=iDe#*g+FR;rqs-4-?CqJz*VOSd$;DaWbCOn{y6{K7Cb1<0H8hMdprRewr87(Q!2C0r~uiklKAG& z8t~c{lobV~LVX=aJSs_;Z%IZ`F-p!egyzsH#@{DT{}?XIc{fR1hWY6yhiHV9Au#)# zrXc3gaC;iCt|fBL284GvD0~;Jp)aEFyTHCb(7?yn*y%Vb2|)%Te^_Tx&mxc{AE10i znd&(7#xA|-XVdj(3B!{Z7wa`}J0%YbOQF2>GF>nI78&ANhrP%E51gA9Iuwu5R{_Z$ z@_;zwoap8r`f`{<%~@zKX7wu+>eBVnVheK5N~Bg=M;zG*e;rF3KUMXw{$CngUu~^A zJk^Cr!H=#FR7iih-@IPLKckY*pb{1{JzEXZW%q3EIA`4dfRJXvcHdbd@rBhb);$;S z$qAhtwRgHE$!-gBK8QO(`|k*R^8ioB_b>IyI69ZnT$k*EuVBnDPUjno)<_}~ZRW!u8;bB- z>QY-5VAefn-Od=kqx~79|B8rebOK~?v;n!?KeIUhN*d#)Jf_gkgoJlvDI)9D+_8}g zf`W{~sh|#AUI4E``*kx&uC0-Q7{2n`yYYk4Pod@?rsA#RBi2y)rI zcK_2_Z>Uhyso~oESWH7=XpdYqZEi88 z-B*m(-~F1S=z&Y+hf)#Am^4?O#%#5kbU;E>=ojr6p)U~H4fKzFW+i!3L9pc!uUOwm zTQEl4OjVA`YKy6j8&pQk;*OM6iQwy8a=_u+NZ$#RNH%FibuzSI(YjIZqG39%a}VU~ zkt8K5sOnt&Lli7b56db0g?pduQbA0>8K%zEwlRLHsVgi*`|#wcqO&BwwDtL|&@i!k zvX?-}KLOJh8`ntd{7~u~S)G?Ty zmej!sBoU*OX<`&xqhx_jlX^1$Q{A#AI#-td^@f0)|H?FQ!Srv=APOPYx-=in0=*WG48 z@vNG@7VA@mo`dO2OUesjE>xOyfo?$A$6ASMd+Zr(vu}xg9AL_F7c-#Mg>(s9+0T?P zOdw8Tv77Uqm_~`ZUT6iKKQqBjP2EY?nfU1wOuMTG{B@QNvf}z&YQxKVoWrWq5nQK&FAQz}h&M>-CZ@>{_ zWA?C4lwjmNwLeU5-@=Eqq=sQHPEu3U;_kjZgujKy^!qNyPa zY=X&Cw;NmHgrPyJ{*c@4LFI)-`qDso{pYxC(UN;JE8Nb}2Zho}yLG=h>9K?TT#k(n z+hI^Do-hq6r<~Cjvu2_!QiqPjE8pl@#M3-N>6>kSqHG=H%k@OMPS?!hY_G)u?qzCx zkQT{G5#BuFaO~R|=}ssSK?uv#PV6#}apj@|)k!3&S0Py-O7^6$7e6h9l$fGl1VnZ@ zfPjRd@71Wy5bl5ilp#reA0SRei`=NSx~ceXma`)KEDR)bMNLNWAH|{=)J?J zhNA8^@4RX^C`+BpyTIwgKtDdInBsRi&=TVuoDLy3bMg9+k`rg?C}AYGe8U=iY&1!Z+4h1Um+N;& z>rYp8O+|u5xi4k?d6d4DZYp9s8A~=-Xu=652GmRrM(gFqRilPKkuaUgQf>Te5tb5T z+xHA)S6$uZBlj=iVn#a7Ze>-bA!a`WExzZmFitK8;KuB8PjGJkpiV!X3RD8 zO+3Z*^aUqiV3gLf3xN01#@Nc6pG4-_wN|g~wUiys3p7<(tCx-^wW`fSxfVfBe_}0M zp|3V(Q2DuT&MP%va-1Qe=#I*1q;X*m)3`X3k}X|KW29LWddy?SKS{pTNl$XZyfspS z1dOYOOVZY4{I&YLE!gkc)`cPvw&MMvT@Rcld7T&N?)fe2R|te=_))2C3S zK{IH`Ce8%t`MA=eBlU^;ouhFzeHSQ?o?3=@4eh;Wsh=Y@DTMA&2zt<^L+H<0sragk z^5l$_L1F@8%Q`fBGU{=5D}Ge_!^TjuT=ZLH=+xiNFZW@P`uWUNLP>rl;&z}iWpGhk z$VU&sJmlD9rGMu?ro$EIIEWtuPyG0{m-Lvmgm>H=(cJ#SEMZy}x_+5*w>9*vqQZO? zTr&>(X{_V=!x9>$?Sv`)S5QfP1?0!wl?>OFKZ|kdXN}}5n0Z5J#GTAo5s_h67GH99 zjzsCG(N%$rg}s^ut`j-F8TjH6o>f$E3=xYHB_Us4=ix7DmxJEEa+@G-j)(PUbTV*@MCe?H%go(OU%$ihFU z8Q5q#q$zqaI7E`X!Ui9jj&$niE1mX6iY49`22f}b1D$`>89lALcG|-_w|wJj1%J_o3LwzJdDjPRinRznLou#DZ1Paqr#|pT zV_I8f6%zNbf19{D1LKMB6OdD}%}`;fN~JbfIBB#Dg`RLZz>{!9)pQz@)hsc4qgz0} z2=ch@LB-m8hC;s0wINzySD%!+`cDruR1+p`3$>k*k%1y|9Oo z^FJ^s|0wy#k16yCp@n{k#)Cv^cG$`N+8P=f(APwdvQm*%LBeDh7aCewaUE|QX>Yv* z{!Fz4ENVGFD0l7q^pVy2*{{TTW@&=sZe;|O zaWXe+esN8K)bWj1qGKD6%sYIqep%9I)Lz0ouPC+TSSYf0W2Xtn5wy z`_ae$DT3R(|E_~GNI(Ssvk2Ziu$%B&AR_^R4gar|Z$&h1KUg0 zzlw&nGc+zKC@Cl^C=^p26AKe-EfW)vl?DVy9uz4kuT)kb=-+DO<)xXs|L@-Z;m7_5 zz1ptE zyEm^Cm0Tb9GZvG}C3K$-Vd~Tl_7?Jd#b=d&CnmOU;fNWVSIuwj9lU1t^-Nr(^}to? zOLl54yE9jgT)`;tp^R>?uUt|w6CgqIhN3)n(Z2;=1TN4p!E5#oGO8@)vT+M`S>K#` zIyZX#r*Q!S3jP~~DmGxW7^AU810PQ&FmQgY^DrwsZjCQXs(Lr1{S-;OJn-;Av+&sG)1K%7o^}cq^F3goS2YtUH|75|qU8 zHQv??+QdOTSrL!C$&`#+8vE@c(d_3H`H`8@{r=4nFJ^WoUXe;wfVD&FfS&$cicV=N z6uBwrFG}is*~^gV>9siOwoAzOJP+?}^xd&+`n$PLjgM^LOh3jR|w(%?495PtwIuLljV;~oj@WxOw2*k z6^V&i36%WR1gZRIkdhse>>(xuh#IfsJiL@y-_XIk=7(&6%uJhjvLP)6N$_JT>2fAm zSs?d1?3G&~DfLOP`MKN@nK54}6GH7H-~iJS=x2^EdVNgqs8~DnX|rB$Z8K$5EkVEbD+AQ9>3BO>Bjo@qy|r zE#Tx|qiH8$1>9No?LB8Cm@$Pc(6AKwhL-qcO$&uVj9N+31!E$blCZ=}6v+W0R+m8P z#$U*Y(N*Gt<-&)oW8 zOS;!I6$Mcvz&qIZxgUt>O4E4GBNTRH##2wX(~h|a*VDa}B-Lh`so;*%_p@(zCnZzI zHNg&Q2^-PmFty=rD^42%MuhT3M10&=5WGxW!2Gm?!)xeMUB%l9-?{u6TWF)1eO3`O znK}E=7so>{CaDbc=c~v4)$9?c^SI}NIFP1F;!n9eG zvm|cfif7tcrI$p&)Lrqz7ua|a5TX%Uz#cx2u`YXN?3a=YGkp#5`s@;;cVC>BrAQO;ySPh61=!R*Cr;`gG&{ zI$e;T*kZgge<)bowU5N zRE655Je};s&YsHs#1v?K6k4<#dNf?M99?yEG&=ZV{N4DS zoLB2?)U_-upQ`Om)a+fP%dA|zXDdpS49gr!qHI9HC*-w(AUOkue{NeqKw<6)a)3Mi z=MR8r8OYMi+2LOqoBu8YAY%T9jEjS)%Mb zxH`L;xc+Zv0jL%JaXW4RIOczJGrt9`eh>KnA`Jlb{Nu7_rWR)AZuTbsk+7?ooujRh ztJ!}fY;Nn|@yC^+{%b;ZIcI?b0f^`cz?*t_{&^e!7;F(MF)L{?an=7CdNN@EI@%e) z+6iz_HLYANRUO=%P0UnX?OgxzQH#}e6>&9@-T_6v80{Wr1SAmEa|~%S-;e}nD39L z-%OmHu6Fysfc5|hAd&_h@ldcDi80guA|-a=AqYbru!SNV;bGjBh9VqoBWjAIgVCP7 zQd^93{VZ3;xiqTqL(k*XI1ovHnZqzGy+eC>oUX=B_e3E@DYbzUwxmI`9_l?=dl77G zuyeKzB~{F|>D1YSFyF*zj0Sxy-I@8LkH)OzOxwrrURgofnA057)*^@rwZ_bopx;Y= ziiVg;_17mjd06*eot0JX1{vCYo%IB&$P;tF1-hRkKBEBvWO|uA5fABSwtH%1+!n*c zST`X->KQ*W_v!~rNZEzl4Ua>2Y`J|tJlfIW;%q$ecGR4mH8C1!D}N?Gor*bB(;jad zw-;Scw^(tjmR4d#J?N=P(ytd*EcM)%_G5G=G4GcPVN&%ZY-cxKkDNZ)*j<^lF83H#odY1otKh(b+x_1LCDCHMTE~C z^wh0eq;dDGzWsUQ9{LEAg~;@)W5YeO$g=!eRyEJrxL?gDCP#dHh;RA&E$E;V;nynX#7#UbZoDAGT$O>NKY_g|F5%i>T1qsIU2oc2#1l(6%`y^Va z*&f0iQ$ z-)#}I938p3B=sJlMH!=71f)>&rIFje!;C>Tdf(-y!eCUwFB*>8di)fOp15W~0tO zx-0|~a&W7Zz%r=^UdA|01twj01%^d1bb6}W&Tlyl;rgH*%nm2eW0sI7H2g&Pq6H{t zlhj*>{KkpEnv>yNl#_uq{uNv2li(9XhQFXwcnO)mV8qu~)Oc}D9n!%u8$B~va}}CX zTEPlb8tb1+G%q?bAs6S63M`jmp)t?g&%octh>oqG;Mt-%aD;i+L@C#X^}Pew%v#M& zuu7fyilE15MCoS9QmkMMr`fuS(p9xt;~+a)I!M7zw-&!Yh&K7iI-dPz*6i&DkQ6~_rtf}U}-<}<;yatvdMjChOU^`&tR5Jy)M zW7vZr{=AT87bRdIq(IN^^Ev);c{SHKj)=C^)e;l@uUXLFyU5xkH(Wlzpe+Cz$^UNH znEx_h{~ZaELI5H`032H3Z|G*?Qn^$)Ma-qm*R1+^lFr}QXbA|vrHnn{lm(FPa@qXfzyOj+a-t_$l|li90y?<2x{5pr z@Vcb{UiWVg;IFOpAC_BMLQM4^W}BJp_Xlyn#v%dOSbke?m%sX3t)cILD~`sG%KLM` z>1Kw)?pLEb@Z5@mE@d%2Awmg&+COO-{yX|raARHRWXhfW_x4yQ6cnOfP`;j;fF%-i zWzS8>+X%y8z>AOdXU*|N!mF}deFP`7d@jbTa^LP=2!X!kaf85L6fs4QLP^wdMS3X9 zZ}I<<+M=+x(weQU+G^_g+HBVH>HKe5t9(q}^+wqYZ!rXur`QycAd7z8l|5dZt`ut zlKZV9i3`^r^|-cp=mk6Z-$8KX-PGfPm#!WsnLP!cjM?1@_-mvJv>%u=$ME9V`8w~U z<^9oCByc2#X91)m>>X1G>nwC@Kee0M+j_+=FtA2jK1GwZFE)YIFIq3?^&_ z7gt{Dt2|mpb*YWL;Ifdz)0VktoxvwGO_$OvQDEV;!qd?Ypu zaVJU>M87rfK4%;IQ$j^ATR2ji=JxJ=^@aG|ZX!Dw=`XR>X3+&iX$ol$fJkwT&`6_g zgX5!^>X9X4>x@$7j8Ytsc8~LQZ~1j^`@5SCb><5~%EXr*tY*HKiNnskw-I!AqvYRG ztgGCtO1TKgzm*j*kFUffkX_PzkaW54jBor;GVHhYTVh`^R0Oznya2Zj_wU9*$;j36 zw>|jF3?ysV{Px@UZ7#g+>cK`y$%_Vx!YJ%4p<}5p_{K?@11XM4qqJ>RP3G=+ ztOeE3{R=l%{fJd)(Mi6mEGWjzZ>X{JKb)@wI28wMo@cYVOigC-Hf&$`|90KrtK9Pt z)1ms(kj93M)6tfbmgS??qGPqMvgVjK0ufC6T1ay|2AvtBU;5JFO^Bn#fz90LsG(X@ zRBQFY`G3-%q_e9u(S*bo-_i44#L|ZE%7jGjE#m76JDSCZcg0sOW_L7N6m@Q+21jXB zbLp?;$+pUFyOygsv%(A+Q>Gh2M%2(K@ppZ*EjMOdY+kan7}Q~0C0Vszr>W6VkX{xj zYa3LN@6G&KaHeKM`rY~|{VbYJ*WSs!@Dr=H-7vKsE7R`sckguWTwaBn3572-D>= zIK6z&8q7gU;CE|DQY4dTS+hprP@kbpZpoj9yH4#VG%ChqVO5Wg>eUOoI)`@?h-_fA zd3oK$UK6;4SC z210@NYy?Eb1X&G*m=j;1(jt7pCgug^&kxHgVy)L^{HoJDq4^(CZwG)IO9fJ{>*kT^ zj!dKxSzo^U2K&WB8*Y2UTeNhEp0JR3Vp=$XcZM_n;8JBfQv3uXT}2Y2Xw^A!7@bO_ zs1LC_Nvn1m3VDuEuM312^16=HRAX)e=R|-PGk9^6(@f$GMNRSD0Xs*qaxti$#n8j~!_`8KdCo z#zh7=S;B<6O|q{{L55Nv_Bl6MX~|C;Ggpt`lcuQrL%=*sC?#sgr#c0tl|~KM_N^CQ zg}~(oQgMxl(>}{=nvdK-urNE+ZCrk!a;%Yjf{?hUT(~=Qt}x)*8JyQQepassVWket zz5PM^Z$Th_Ut&nqW4CI4Lp?R`yax_NKM~LRbX{ zQ!|%;@mfTUOf1dB?Op%-4H4DJvJSZFXu}^K1UW0&DsvoxNsZ9t#rMlGva+6RhJ@nxA%XHQAoE;J(l5NMFqMS!!IROT`DJd=ghp%_wvb5WhM$@)!+eW2r+qP}n zwry5cqSCf)+s?fCc7Nw~pK~$B-ap_OW39Pj#*7)U8G*HBb6pUk0eG&u4qpMWlEUy% z=(sb~fDWy#Y3-rFO*eSC6M&v#yy9isj`w2V^t2BYc=z@eKXH$pVCK&2wGU?-iw@O{ z+$?V*A6l{F@9CeV{TMABQ%^cQM20pZzog9k?8we`WD+3q%82#$o&(#8RF269k36>x z|C>-pxoxX2y7`uKZ0TiLbr?joMFjen#E}<77%~$k?CD((Y~u7L2cYQ{cQhfk&_kwx zD7LM>b+j$S_9y(>F;PH~fD8VpR(qkHy3ulwCv_SK*zB# zgB*S;)?X(WGbD)i0~Hn{cZ4{k@Z)Z5)T;{1n9PAq%{{*^t-58pr@2COAdbn+WgXBv zoIKD&nGG-cJ_If?Qy9e^x_V!ji`jx>^sVPsYZyX5$<11eW;4MWZ~&Yp%Ct z`TX%%seQhD!>evtoy85Q-QKXlYa9VfTbW@htrt9vwY2T@@~-2=d}N|);6QGRFDkA` zwbtxVXe{B$(rjO%SxdVwo#P;v)R+ID!d zul{?iRe|Cw;#nazM0fXy9f~1PP>x{It-y^!c*_3#X{u1bb#t!M(|x@h1-0sM$o{r) z#xCl$ADjr^o=&tl!Jx6QQVUiA0sCsA&$IC=Y`2TZV3QaNX;C5>}pnMQ;b* zACvz+1G9yQ`Rs(!BxNpRe zD;Srf&VVcsfi4*FuQiBMI|Y9l%Y@Wjp!PxW$RWe;I`bFhew6@wReIIv`_|)HDB)x4 zb^E4N-5M;FfZD7Cf@Jpb)~iMBE5416tc*I0S|u4(-1F{T zGJ393-l}!d@#b;PGGpaq)6#p7mNd$PxsX$5145m2DY+Osj6~ZyBB27xFOWdYdA2Uf zfL?bYVN3cGDORax>EZ4@E~(UpY&=~Q2xCerA4VHuH7#8x?K_R)i%hr3j+XL2rP@Er zn>s@#porL<0#?L(+ln=R6poc8qJStl<~=TI zPmO2uL-yMz)6#PdgHu;d50BGag@-NsO8U0xw6P)SNjga-O^V-?u&Wk4ELC`*&>dQA zog$k^816xynR+gkwzKjZ0&)^#^V?tovUWms?bP*UDK-n5%QdT2Pt7?Q)Rh(R?xt#mqiS<8p7_W zUd^Oe4Z+s4m)o03BD+?~3>YKNUuYtAR%jV+(N?hrVZG^6dk?eAYhcrYb}Ri zRVzPiVz1pDVV`fio{&k0SvS&nwsyk0i=pAJ8wFR5HGQGEa8B5ss@1%zS8b|Za%m^r zPoba{v0y2d{ii2e=pec?2Fwf};n1QWv_&K(%x65ca= z@eTf_g!CPF+3ZDBUJ4-;b^v+*y{3i!_YVg^`Kka;;amW%SI}fvzswD>_j`lt*~}g% z%NfX0F0nl^Bi9{_4v^8=pICj#`@A#R4e^jK@ecq$X)mzJFfk0z-)B#|Zo!JX>ghxV z_;|z^9E+bIf6ie@L_gYP6=@!hk%bizV;e^7J~!iL(l9V_qd*OHK6_n+^X9SvQ+*;z zll3bZ^Rwkg^k=+ctIn#$0XI;W9bN6yr zs0Q?mHN?7fMzF37WSSf1m>bHlG{~|y$Rbq5GDrl+Ed+q+ji`A_nv1`CzD7@fz}lGY z|C)Y~8HW?UWJ;@*7u|0sXY4D&#N98$JItGDN>CIq87D+7D9^mM7%`~Ng_NXZf2Wj~_6=AR5pg8!_e|Hjk* zt8#j&A&a4W!-B8{qoSs~{gSC!LckL^Db~+gF1?G65aCzIPuR>;^iYgK)Lih4#{eY~zj}8SD-5&WJ3?y+8m( zxOXggqKhChn<{t`O2+p*XeJOyx7G+E6i8{1ARvng)`rw!xV~Y^!F7Z-zG3f9SazVJ zb_67U<8xvDq|_p5S2<2qoYi6gXvP8inF0Vm_tZQl^QE@Uz6lL zAMjk94!KTxJr}pZfwls%%q}H;igf?CGY>oVbje;3unX?`fbiS%5vq$pR4=ZpM8imq zNo_~0#MG1ZVito=KcTTBX%uyc#r(puhQ;rlF4Ap+&;qcd+*(N5QF7Nqqb`-e*GkkH z;>-fhBA8q8qF*eT=NTptBoja)=GV!lH_=*CPEjw;Sf|-cDk6C*G%_i;}ziuhesPr_BuR zzwIVBIbT1#h>Odq+AinO!NDtXqfF zE#2L5J*f83M5GTvmlYod4p66$Sp--+lwv;~400Aabvgzc0DI|(Lj^F=No}TSzWXmSD=JY}J>cu8%tE))Vs@{4wtEB((NmHW+q%^{=;#*cA-QuJx;i+%>^e zco@U@Mc==A3{x15>P@gKgo(dv1erJTf|AE5%p$CaO7i8DRNggPJj1h z#4nNdfLte-W9$I!Z(IAy0QE!LzxJho^GDqMvu`aSvJq<;`-`*d&Jqy|6evwuv41kT z-!+DHM(oFZLti2looQF(lT@>jVAE1eD$f#>C8hD}GRQb3r3Tr!6WVhnOfh9; zS&d6-vI<%2_=2jQ9#VrJcq@|s^a@B0U1J9*9%rv~4M5|H=Co$fTggUx&!co9?L9z) zH>fJ^m~;r{6|cKObBXu>2;nQpoRE9!W6KOCya6^uRPEOx`uJkYm_VUd_(h0*i9$>+ zY{k?TRy?b*Fn<92o<9Ew=e2^EaBHnDe2hE6DtSDQ4{SkfdIw6a;+UwLh#;E=ljX0M%!~ZqkQ(Ugmr-d|h0&iaRi&1t$dFh1O|78jo zOkB8VR&Qp>;|3CrS2Rs5giG(Pyno@k@Vj5m!C1CAQ1_a@S3!WUmvPtTp3JUnCV-{O zgDHH6OT;MjBa`^CjmOSJ&t`BvftEOpccYw1t#FVMK6v@k>ZDX(9w3&#lFF)vqX*Ey z3$@QRa3!oh$6r>3;G%HpxI_E!tgduy(MLd=c;Cm{Z~^KS%`uVp4udUOckMs|`W5Rr z`YvrWe!4KHR2z+cT9ql*x4B*TCA%;~t(;e;adRP;&`+=AiRREGcny!`^M5&h{sWc+ z!RpOTf5zggKVbR)N$LOJgIHAqYZntCI~#i!=l_xmDjPUi{fpA~`q6cxd~aH3+9cXg zmM8;>=%UUGLKYIVmO#;l{L+GoIXny^U1if^`{U501u{qu)h+MMe-rWxR{_PQU=Y*U z`DM6nN9+cegGrG0nGDu22~f-|(P{dpPxjh6zM@y7*9NJF$$MwS<^|k{#2CIh&R>qJ zSwZ{jdan6c)4s${M~I?i9aMrU~G40#5{~GV}{UsUZwFC@2{w>0TYD z7#IW((va(dfXV)4=@2}K>1oVed58~eJKwThGt^*hDi+X1J_e(56@Rta>sJ)F49pi^ z*S0e5@)AZTw~>x;qBQ&9-$Igel1J%F#ZXLKiOE)`glH2OOIMP$)>bk7flI3;nN16W z@Zcy$RC%15b5frg)xZVW(;1nXahc1di}a3l0!foFRJ9o0#idt1u?I&Zj7rbn{57gn zmt2)aTBg#I7Y6c$r4_3+p_!xhX4sJ{idy2Co#oxS``r)0OQ}TYtV-pEHZl!UrRsJ{ zG;0fu#Uqm?26!sFb;f_ldKrd&B5bwfAoZY)LrBAm=!X?jmc$AMWttlz3+00DxZa1A`9V z6a>coSi{TNs}Gey#qKkMlG`ixQo=COyrJ_B%HK|RkZmrDYiZ9yv-f~ka@uy{Xs3s?2~q34FPM2tzW&X@3>g^xA| z0qIA|sI=!7bOW-d*p^jU)#68Eb8xE3VeVyVd?Xw6!fKDpW^uBY+j(|USZY;wVY7N3 zqx6lsu$x5MShx~(c(cqxk1$+|Dh0Ao?bTe!q?ILu0{4PfMQjY5pg`VU&Ungnoc6{kYdr#jKIeUsBenHPiChGtq{ z3s0$)&4=Ql_ZKWW%G^cTc)aV%Xg5G%gc9ylxHwx=D9S_ zE!y%_>1$LTqeTCnP4_GHxPvHuULd+pBkA-J1*T++aRK%?BOT6BN+*TZr=K{TW*g7? zm-zo}w~hPTt_tafpqo>~GUK)hvJ6ycLfe^i@f&&^GLbhI@kIfyp7XIko(@UBpMnuC zrq7L}ZFP|+iowoNB}TpLPhy}8%ALc<&}^oOj>gj5#vB7!54aJ8J%X|MLujXwJci7z zL2L?Q16?i78R}#DegNbQ%pR7Smn#Dgt>m>iA`1ZZQjmg66V2?)qid5ICZEXLq=17^}_Lsjut4~X6&CQTs|e*4PcZkX7=Lzz+|AXBD?ZQM(Zs-5-p9Pbv-_=yXFVr8S(_cx501o-hh)AO8GfXKljdX%)TpMH4a}qvN+gI$`x%A&J<> zz$Yo2C{Fsu;Cb9kx8}_42SQDfmOx4~DTo}%yI&p8ZQ(GO^4+#gRq3&0Lz_H`aVL3X z#C+#a7vWKjN<`2+#YWy-^{Sm)X+E9a8QD`m?j?8uJ8_7aU%ZF@7K+h!@BE%21$jA_$KI#K@r*Igk(P!6@>82sxo)>XLaEEexyGVQv{f z)TaMIu^28jy}6Y9{g+m7eiJ4mxfXYEZlI$QCNh|T!gn)LPRX6uEI|+dL)4Bw^Drn< zNUCZ8ckfh-Y9+P=4Rpw9a&}t&2Uj$QahPVh9 zm#NV3WKak*uAbz_d>*NhNwtB@Aje=$u4be?LTdrXTMs|f+=hy>`%RnaXPsDI{QV)u zz@P3C%4MbVb>gzX>{G*Bo-)R=j1SE-Cm{GlMVFhbw7KVt_)L%%?YGVFJTfxOI^VjL z5WS7ywPilvVK#F1^;c2J`Y{ocuEM z<&ny`pJZQw_@k=P1f%+c6GqKMqhSaVe&Uzema8&G;U#@LhF^~GIRswX!?1r-K?JXfZ7L2;ahTSe+^>3_ha_kz^HIy zUk+Ma!;R9mW9tynU#S%7kjB)*k&AT`e?TT^a?uOZN86)1*2^MS2a+>fOi4tyuh~W` z-VlHXKG2(i=`=6HUN5WW^cTkkeEu(w=YMWqOjlyc%g=b>;zyZF`JXrMADx5pk7Cv8 zXTJD9qs9NeVA`yT!7xTq8(QN;$Fc!g!`@`-;goV_TccSc0o5%{x&z#1QirC0aKO38Mi>Yel?tF_f=gH&m`iKrS%u&Fr}yuLco^`c~7L9WEqPGF7IQRESZ zJ2)firt6{s!IUsE<;MkPca` z*<{0VbL0`zFX{$01*Vv}tV|V&Omjz4CfY#>dCk?Hp|I(==7dMP`=d*DS^cON2? z6%cMB%%}s;&J+6Qt;Z*Q%g}$iW2$fHju)KSd8y=3a4gYL-v<$;<0+=iOEnmb;lN_v ze5#6Pv*^ag19R+V`>Bi;d4~~G|2$&lTo)G-Yh~*8-hBDg5r|Xey+%h8*`3Ms&t*Sm z>&(N5CfYc~;-Wi1->GVCzdj6aYJhfurk!4d#TQ!SFxAAVxXh>1AW-9f8L?Y9y` zGREeep>?B%2=nah`C&i#xov>;@sr1LaE6_0@h#A;?+}XU~=*>J2ckvX{-d;X1rno7IG3@ZZq(=m(jwrm}^>o=HFlGqHSN8n?d3zK)b+Nk~IdFDKIp(9Xt$*ci zt)e#9{8e>@O62~&;}(RC-Zn(Eok&*oj&F{Jo#yr=@e^aHZ4=`Bm5R2$SOEAEbmKyb zll};%$h)3F{!5KxO<|7>xhHVq=+=3hrxspXRD64qC$vdFn}>s)7aq2!BedHV-ffB? zI?othf++N3k)uTC%QA4pU`Ubl&4a8u z50zU+w=M6=WDaa81Z^n-ZD|l~Ng4Xw5c=E^+A2~VHkewt{Va{~1CL!VX7~E9F z_$!EOhQ_FspPU@NYaC9qFhbvhD~uL&SYg;enJo5^2o#;TTFC& zaUf$TjjwAed%P!0hM&3k^8>$_W?%$9-gk;dyTkXw?|*>HKiHjX;F?GIXR46>Q$A_` zvwZ%4WA}f-6fL!%`3N=lN^fahr>8_cDH{#P@y|tgBROWHYDVL#H!WGzmU%P;_ z6DCP_d(KT(uaFTfp+vJiJ1NmX_M?}0)|B7hWzmWkl!q$SoViK}xG`H-u2k1ykI#F` zkj0wDP}R`C^-bC}jc6R+ zrbygb9D@W`rG(^dV>RYJ+|U`JVY64;&mc9)?qKajj@HcfcxB}olVC;h$_~zyhYDyP zg^5VJ{ziRqQ4!(?W5MZFfMvtyjB`Uh_%ah<FMIG~O^ixJNWi3h5Q`0?Hw^YW>lN`XU>3YEg zG>6`=1f^MFiX$O|%-c0*p;!&_BeBU+&q?)^yWv#Lw9MSooAlZ#g#1IwwL$?+G8pHA zdjZx9Ym0lKk{%gONk$#z)f8s9)hP|%O!6=mo+(B?lJD{tG~7Kw{K90D$hip~y{5`r z4aNWh5r^m>d<1e>hGb6X7x7A3R3Es*Com#x^HCnL^UGA&W?mtdBA%c(<-Km1X}UKH z012Jo4YBedj9!O1X@W6zaU+a)MKJOad?}4YB1NoTi9Q3$Ft-`95{0+Zh&`53O>_Q8 z=3-7MN04K2F*P9GjQi)}-TST#JwzQb8Ye^GTXOTf+Uum%o?J;7SX}Yo=y&7fvJ;QY z2lSIk4QC`%_<~YLRa#spr4QB!Wj7$g8k88)Nqh z1IDVWD%sg3j1sF^L|ba&?}&@dGy1Yq*xvW;B(IGu5)_D|ou~=-3Ujivzdav0>#R|$ z-mg8VnkI9RnShy5E?rIwbW_w%2leeP0-O>z?K zm;}^uv2$Xw&Fo!Z5>Rbu##Bi1`bfl^5-=ZCid&n54RnFgOZr|7J|EFGz-{e<-N`2? z*1b|I-~*zzLZIwC0HYNtPvCzJea###{QvwI6~+Ci=cxa)JpX%NDR1E7WMce3s@@u9 zt$&Qdzf8SKYUL4Uh@dPJ8nrclN1#bG`x6SVSSaC7Hs>@4jW1ouMtnhhN0G6S@nPhB zDhx40qd=8V4ut*A&baqE<~F1A`}+C-)d!q@L+I_in(G)pxoWsVGYmIm3_OHT#VD+m z_oxt#HLy2}!@p^s%&~lvv5-qvNiCqKuYyy9Rbv2w`r|3$&_<=4u#U)MAl^ygF?MJ| zdOpQnVfpIWPaD;_rUrA=x(~r_<}{6r*FPpJ)9yllxp)b@TdFq(`@5?l5{eFSUgOrP=4)K%g61KhXNK)Bx zqt#^>qpy;z%Q)1aI!anFbh(&#k*hyVnSJ=a%)X`3Me>s8**SqQy-JNDE&%s5d~?WG zq8(JXF{B&uk*O_eBV~n1Ll8z}c1GhpF5C2z`6Jp5wuHZyAaa;wUW)w){94kX zB~=SSzRk&e^RrRC>G}A~lK7Sop{g*UR>qhN6$)6iUh!CmOO0U_G1*UX!!C_{$UY1h zB`w^>pQVh_`@yX9!Xs}1^{nf{GnrOI`lNv}7^*}IAn&4lfYA#4XYXnfGO(ZVC@yg? zeEK+SS94QHA_ROJMPPoVF4$kv)Nn7%HLNGo&0`2xZG`yE@utw6D}r!s@nnQ>_g)xr zL=h5WGG-~I38&gM*ATvsxXlmsJtICDM+Lxm78xM9f|bA=GuZA}s^Imr*zTo8)=R+Y zPiCLKa1L0HxF=;)aPe}%Lk<}K`wsjAQGz7PW2Jw%6=8_~HtYH~D(OGFx-Qjo#h>(^ zFElEfBnosuI|>R2T8P0viILiCmS&pv)?t`K4%rB35T%ew#ZxzI-2rWw3K_S6-cWf1 zx7L|x=29X2M`Ete7j4Jf+pA4kA8+^jn4V5r z8x5Pno<2!tW2sk|ttCvt&_ksq9wuSTBw4>I*_PS19mo?Vi=jY!xE<3qb4b;2CHk3o zA}uMM}Zuhtx0xqn@}JU5{X*=u@PuHVCsNQggHayIV8F!4TR? z74W3PMTbrp9~$bwW?;~^!2}TcR!H5qk zLvq$|Yd^ZRrrPv3mH$rIIkQe0L>a$6n3J~E@+u>ho?Mgu*`jpXR8o?$ekE(Yn2=uV zI&?4_ZgA<=b!Bm4yq{Q3AO~$KOI#<~06rN#V-SDVP=KZXt;NRcwq=A7%L)~tCT%j! zfDC#S(X`{4e23J=qYwrQjj=|FMb(eN>Qa@2QcRG_qH|o!&BV(qqkH_ZZ?+2_psi=i-FNC|qgY10WY^mhwcfE%3a(ARk?h(q#B|H!Xk< z)DZ*zP+*`)F$SILOmX><=;0R$R-1Ho@SG_!Zi#UUg60 zN`02y*?hMaOEE$W)#BTxF`HxqDWum#plS|m{FV#goD+^)&>ALc z!x6{4P&S~{lSflmVN=A$#rMM}c$t3-2f1qJw;jlB6kSjGXcz1Hfrd*RAo*THfsL## z5<4whIt8)L5#9-)BFdMJ@*}7F1gj1|+aDRlH1_AYU(8dY#>)Za9sFeyw;6n7SQWKr zw*RD(CytZz78!O6#yrdE;V%}cwMbx$Y#`P=$V6jAud;}66Jdyj#DeYv+$KkOgL#^j zdj?d>R^IM2_Qxrq7;v)1PgtA|zY=%@daAIUBq^ZcQ4)W-Wa08@uz5fx*x%Ry_{0k* zzeE4`PT@uT;=SVM38MO8`u`t9i>#fqiIbeIl8cRvfuo0*iLHs_e=(bM&hE}8vD|Qd zzhH#lvkf(&;$KvfaRQLjtA%J9+16YJjvqJ_NkE#PvNGCqY2#OZ8!L#C!^@6bbd^BO zvFK@HfhjxGPLct+Ok_lM`T~J3V7Cbh-GmfODC~tY_n(eIo$m1RGx~@W;g{_ zx~ek$RtNZMi~Hk_ufeM08^ivm{FEda61^#yo44%t49&{P@8z0*MOxYxp8#Y})W)ia zOHlB2m!hcqf9==*Lo6|xi%SasjN)g0cC$JD^XdJ6XRuXlEu8-`nE5Zt>0kCTDQWNj z=&*)+un8ayQZP^z>r*f)1M+zSm!MP*5~zUI0@Y8(Nj3TfN9HzT3TtX=G}kTjo-Dro zzvl3pYvji{FRwp~JUy>Xn^xmzLi-m_eW*tEf{QtAuzefH(c~;@hu1$iR8FMa~V6V22t}}7@|}2 zvFL9_Ref|1S=u?BHx5- zCA9(?n9qo=hAko0NwYZES?R2<@GPq>r61QcX7?& zS)4c~E~=UY8f`n++N1uOU2OiTgzTf#?=yTF=ih``09%SVva2kX?r~ev337@geL?%*}4AIWO$m zdM9WFQwvYs!IGAm1Qe_RXltZ9tMvz@S(QQU?)*v(;;`KxI6)F-trS!lzCt1cU2t^d z_h6!g?w25?@J)bmjfUMqoC>n4`E=j25!8 z&?|bTKsIo*#RZfZC?OWc`HjRE3Cja_?#+#)l*bj6{HK$B(IiTDoGPO;o!*K>gy)`C za_A-x;-r|GiHtU+$BQjO(ieH?0>=M~jT_0l!zm75leFoBtHbGUQ^Mb0GPSnDe+aIGkHqjM4zU;`BHf{4f1huDIrwh$TVT8y zc7t}Dd

    U!IY5K1;@Xu2@JfID%#`A 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 xQH>|k3e;+}EQjl6lHgF77f oldIn.size) nextIn ++ oldIn else oldIn ++ nextIn - val newIn = nextIn -- oldIn - state.incomingFacts = state.incomingFacts.updated(actualSuccessor, mergedIn) - /* - * Only process the successor with new facts. - * It is analyzed at least one time because of the null fact. - */ - if (newIn.nonEmpty) { - worklist.enqueue((actualSuccessor, newIn, None, None, None)) - } - } - } - } - } - - /** - * Collects the facts valid at an exit node based on the current results. - * - * @param exit The exit node. - * @return A map, mapping from each predecessor of the `exit` node to the facts valid at the `exit` node - * under the assumption that the predecessor was executed before. - */ - def collectResult(exit: CFGNode)(implicit state: State): Map[Statement, Set[IFDSFact]] = { - var result = Map.empty[Statement, Set[IFDSFact]] - exit.predecessors foreach { predecessor ⇒ - if (predecessor.isBasicBlock) { - val basicBlock = predecessor.asBasicBlock - // FIXME ... replace flatMap...isDefined by something that doesn't create intermediate data-structures - if (state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)).isDefined) { - val lastIndex = basicBlock.endPC - val stmt = Statement(state.method, basicBlock, state.code(lastIndex), lastIndex, state.code, state.cfg) - result += stmt → state.outgoingFacts(basicBlock)(exit) - } - } - } - result - } - - /** - * Creates the current (intermediate) result for the analysis. - * - * @return A result containing a map, which maps each exit statement to the facts valid after the statement, based on the current results. - * If the analysis is still waiting for its method's TAC or call graph or the IFDS of another method, an interim result will be returned. - * - */ - def createResult()(implicit state: State): ProperPropertyComputationResult = { - val propertyValue = createPropertyValue(mergeMaps( - collectResult(state.cfg.normalReturnNode), - collectResult(state.cfg.abnormalReturnNode) - )) - - var dependees: Set[SomeEOptionP] = state.pendingIfdsDependees.valuesIterator.toSet - // In the follwing, we really want to avoid useless copying of dependees: - if (state.cgDependency.isDefined) { - if (dependees.isEmpty) { - dependees = Set(state.cgDependency.get) - } else { - // We only implement what is required by the propery store/interface - new Iterable[SomeEOptionP] { - override def iterator: Iterator[SomeEOptionP] = { - // This method is actually not called by the property store... - Iterator.single(state.cgDependency.get) ++ dependees.toIterator - } - override def foreach[U](f: SomeEOptionP ⇒ U): Unit = { - f(state.cgDependency.get) - dependees.foreach(f) - } - override def size: Int = dependees.size + 1 - override def isEmpty = false - override def nonEmpty = true - } - } - } - - if (dependees.isEmpty) { - Result(state.source, propertyValue) - } else { - InterimResult.forUB(state.source, propertyValue, dependees, propertyUpdate) - } - } - - /** - * Called, when the call graph for this method or an IFDSProperty for another method was computed. - * Re-analyzes the relevant parts of this method and returns the new analysis result. - * - * @param eps The new property value. - * @return The new (interim) result of this analysis. - */ - def propertyUpdate(eps: SomeEPS)( - implicit - state: State - ): ProperPropertyComputationResult = { - (eps: @unchecked) match { - case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) ⇒ reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) - - case interimEUBP @ InterimEUBP(e: (DeclaredMethod, IFDSFact) @unchecked, ub: IFDSProperty[IFDSFact]) ⇒ - if (ub.flows.values.forall(_.isInstanceOf[AbstractIFDSNullFact])) { - // Do not re-analyze the caller if we only get the null fact. - // Update the pendingIfdsDependee entry to the new interim result. - state.pendingIfdsDependees += e → interimEUBP.asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] - } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) - - case FinalEP(_: DefinedMethod, _: Callees) ⇒ - reAnalyzebasicBlocks(state.pendingCgCallSites) - - case InterimEUBP(_: DefinedMethod, _: Callees) ⇒ - reAnalyzebasicBlocks(state.pendingCgCallSites) - } - - createResult() - } - - /** - * Computes for one basic block the facts valid on each CFG edge leaving the block if `sources` hold before the block. - * - * @param basicBlock The basic block, that will be analyzed. - * @param in The facts, that hold before the block. - * @param calleeWithUpdateIndex If the basic block is analyzed because there is new information for a callee, this is the call site's index. - * @param calleeWithUpdate If the basic block is analyzed because there is new information for a callee, this is the callee. - * @param calleeWithUpdateFact If the basic block is analyzed because there is new information for a callee with a specific input fact, - * this is the input fact. - * @return A map, mapping each successor node to its input facts. Instead of catch nodes, this map contains their handler nodes. - */ - def analyzeBasicBlock( - basicBlock: BasicBlock, - in: Set[IFDSFact], - calleeWithUpdateIndex: Option[Int], - calleeWithUpdate: Option[Method], - calleeWithUpdateFact: Option[IFDSFact] - )( - implicit - state: State - ): Map[CFGNode, Set[IFDSFact]] = { - - /* - * Collects information about a statement. - * - * @param index The statement's index. - * @return A tuple of the following elements: - * statement: The statement at `index`. - * calees: The methods possibly called at this statement, if it contains a call. - * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will be returned. - * calleeFact: If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdateFact` will be returned, None otherwise. - */ - def collectInformation( - index: Int - ): (Statement, Option[SomeSet[Method]], Option[IFDSFact]) = { - val stmt = state.code(index) - val statement = Statement(state.method, basicBlock, stmt, index, state.code, state.cfg) - val calleesO = - if (calleeWithUpdateIndex.contains(index)) calleeWithUpdate.map(Set(_)) else getCalleesIfCallStatement(basicBlock, index) - val calleeFact = if (calleeWithUpdateIndex.contains(index)) calleeWithUpdateFact else None - (statement, calleesO, calleeFact) - } - - var flows: Set[IFDSFact] = in - var index = basicBlock.startPC - - // Iterate over all statements but the last one, only keeping the resulting DataFlowFacts. - while (index < basicBlock.endPC) { - val (statement, calleesO, calleeFact) = collectInformation(index) - flows = if (calleesO.isEmpty) { - val successor = - Statement(state.method, basicBlock, state.code(index + 1), index + 1, state.code, state.cfg) - normalFlow(statement, successor, flows) - } else - // Inside a basic block, we only have one successor --> Take the head - handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head - index += 1 - } - - // Analyze the last statement for each possible successor statement. - val (statement, calleesO, callFact) = collectInformation(basicBlock.endPC) - var result: Map[CFGNode, Set[IFDSFact]] = - if (calleesO.isEmpty) { - var result: Map[CFGNode, Set[IFDSFact]] = Map.empty - for (node ← basicBlock.successors) { - result += node → normalFlow(statement, firstStatement(node), flows) - } - result - } else { - handleCall(basicBlock, statement, calleesO.get, flows, callFact).map(entry ⇒ entry._1.node → entry._2) - } - - // Propagate the null fact. - result = result.map(result ⇒ result._1 → (propagateNullFact(in, result._2))) - result - } - - /** - * Retrieves the expression of an assignment or expression statement. - * - * @param statement The statement. Must be an Assignment or ExprStmt. - * @return The statement's expression. - */ - def getExpression(statement: Stmt[V]): Expr[V] = statement.astID match { - case Assignment.ASTID ⇒ statement.asAssignment.expr - case ExprStmt.ASTID ⇒ statement.asExprStmt.expr - case _ ⇒ throw new UnknownError("Unexpected statement") - } - - /** - * Gets the set of all methods possibly called at some statement. - * - * @param basicBlock The basic block containing the statement. - * @param index The statement's index. - * @return All methods possibly called at the statement index or None, if the statement does not contain a call. - */ - def getCalleesIfCallStatement(basicBlock: BasicBlock, index: Int)(implicit state: State): Option[SomeSet[Method]] = { - val statement = state.code(index) - val pc = statement.pc - statement.astID match { - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ Some(getCallees(basicBlock, pc)) - case Assignment.ASTID | ExprStmt.ASTID ⇒ getExpression(statement).astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ Some(getCallees(basicBlock, pc)) - case _ ⇒ None - } - case _ ⇒ None - } - } - - /** - * Gets the set of all methods possibly called at some call statement. - * - * @param basicBlock The basic block containing the call. - * @param pc The call's program counter. - * @return All methods possibly called at the statement index. - */ - def getCallees(basicBlock: BasicBlock, pc: Int)(implicit state: State): SomeSet[Method] = { - val ep = propertyStore(declaredMethods(state.method), Callees.key) - ep match { - case FinalEP(_, p) ⇒ - state.cgDependency = None - state.pendingCgCallSites -= basicBlock - definedMethods(p.callees(pc)) - case InterimEUBP(_, p) ⇒ - addCgDependency(basicBlock, ep) - definedMethods(p.callees(pc)) - case _ ⇒ - addCgDependency(basicBlock, ep) - Set.empty - } - } - - /** - * Maps some declared methods to their defined methods. - * - * @param declaredMethods Some declared methods. - * @return All defined methods of `declaredMethods`. - */ - def definedMethods(declaredMethods: Iterator[DeclaredMethod]): SomeSet[Method] = { - val result = scala.collection.mutable.Set.empty[Method] - declaredMethods.filter(declaredMethod ⇒ declaredMethod.hasSingleDefinedMethod || declaredMethod.hasMultipleDefinedMethods).foreach(declaredMethod ⇒ - declaredMethod.foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod))) - result - } - - /** - * Sets the cgDependency to `ep` and adds the `basicBlock` to the pending cg call sites. - * - * @param basicBlock The basic block, which will be added to the pending cg call sites. - * @param ep The result of the call graph analysis. - */ - def addCgDependency(basicBlock: BasicBlock, ep: EOptionP[DeclaredMethod, Callees])(implicit state: State): Unit = { - state.cgDependency = Some(ep) - state.pendingCgCallSites += basicBlock - } - - /** - * Re-analyzes some basic blocks. - * - * @param basicBlocks The basic blocks, that will be re-analyzed. - */ - def reAnalyzebasicBlocks(basicBlocks: Set[BasicBlock])(implicit state: State): Unit = { - val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty - for (bb ← basicBlocks) - queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) - process(queue) - } - - /** - * Re-analyzes some call sites with respect to one specific callee. - * - * @param callSites The call sites, which are analyzed. - * @param callee The callee, which will be considered at the `callSites`. - * @param fact If defined, the `callee` will only be analyzed for this fact. - */ - def reAnalyzeCalls(callSites: Set[(BasicBlock, Int)], callee: Method, fact: Option[IFDSFact])(implicit state: State): Unit = { - val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = - mutable.Queue.empty - for ((block, index) ← callSites) - queue.enqueue( - ( - block, - state.incomingFacts(block), - Some(index), - Some(callee), - fact - ) - ) - process(queue) - } - - /** - * Processes a statement with a call. - * - * @param basicBlock The basic block that contains the statement - * @param call The call statement. - * @param callees All possible callees of the call. - * @param in The facts valid before the call statement. - * @param calleeWithUpdateFact If present, the `callees` will only be analyzed with this fact instead of the facts returned by callFlow. - * @return A map, mapping from each successor statement of the `call` to the facts valid at their start. - */ - def handleCall( - basicBlock: BasicBlock, - call: Statement, - callees: SomeSet[Method], - in: Set[IFDSFact], - calleeWithUpdateFact: Option[IFDSFact] - )( - implicit - state: State - ): Map[Statement, Set[IFDSFact]] = { - val successors = successorStatements(call, basicBlock) - // Facts valid at the start of each successor - var summaryEdges: Map[Statement, Set[IFDSFact]] = Map.empty - - // If calleeWithUpdateFact is present, this means that the basic block already has been analyzed with the `in` facts. - if (calleeWithUpdateFact.isEmpty) - for (successor ← successors) { - summaryEdges += successor → propagateNullFact(in, callToReturnFlow(call, successor, in)) - } - - for (calledMethod ← callees) { - val callee = declaredMethods(calledMethod) - if (callee.definedMethod.isNative) { - // We cannot analyze native methods. Let the concrete analysis decide what to do. - for { - successor ← successors - } { - summaryEdges += successor → (summaryEdges(successor) ++ nativeCall(call, callee, successor, in)) - } - } else { - val callToStart = - if (calleeWithUpdateFact.isDefined) calleeWithUpdateFact.toSet - else propagateNullFact(in, callFlow(call, callee, in)) - var allNewExitFacts: Map[Statement, Set[IFDSFact]] = Map.empty - // Collect exit facts for each input fact separately - for (fact ← callToStart) { - /* - * If this is a recursive call with the same input facts, we assume that the call only produces the facts that are already known. - * The call site is added to `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts become known for the input fact. - */ - if ((calledMethod eq state.method) && fact == state.source._2) { - // FIXME Get rid of "getOrElse(...,Set.empty)" due to its potentially very BAD performance - val newDependee = - state.pendingIfdsCallSites.getOrElse(state.source, Set.empty) + ((basicBlock, call.index)) - state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(state.source, newDependee) - allNewExitFacts = mergeMaps( - allNewExitFacts, - mergeMaps( - collectResult(state.cfg.normalReturnNode), - collectResult(state.cfg.abnormalReturnNode) - ) - ) - } else { - val e = (callee, fact) - val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] - val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[Statement, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[IFDSFact]]) ⇒ ep.ub.flows - case _ ⇒ Map.empty - } - val exitFacts: Map[Statement, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[IFDSFact]] ⇒ - // FIXME Get rid of "getOrElse(...,Set.empty)" due to its potentially very BAD performance - val newDependee = - state.pendingIfdsCallSites.getOrElse(e, Set.empty) - ((basicBlock, call.index)) - state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) - state.pendingIfdsDependees -= e - ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[IFDSFact]] ⇒ - /* - * Add the call site to `pendingIfdsCallSites` and `pendingIfdsDependees` and - * continue with the facts in the interim result for now. When the analysis for the - * callee finishes, the analysis for this call site will be triggered again. - */ - addIfdsDependee(e, callFlows, basicBlock, call.index) - ep.ub.flows - case _ ⇒ - addIfdsDependee(e, callFlows, basicBlock, call.index) - Map.empty - } - // Only process new facts that are not in `oldExitFacts` - allNewExitFacts = mergeMaps(allNewExitFacts, mapDifference(exitFacts, oldExitFacts)) - /* - * If new exit facts were discovered for the callee-fact-pair, all call sites depending on this pair have to be re-evaluated. - * oldValue is undefined if the callee-fact pair has not been queried before or returned a FinalEP. - */ - if (oldValue.isDefined && oldExitFacts != exitFacts) { - reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) - } - } - } - - //Create exit to return facts. At first for normal returns, then for abnormal returns. - for { - successor ← successors - if successor.node.isBasicBlock || successor.node.isNormalReturnExitNode - exitStatement ← allNewExitFacts.keys - if exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID - } { - // FIXME Get rid of "getOrElse(...,Set.empty)" due to its potentially very BAD performance - summaryEdges += successor → (summaryEdges.getOrElse(successor, Set.empty[IFDSFact]) ++ - returnFlow(call, callee, exitStatement, successor, allNewExitFacts.getOrElse(exitStatement, Set.empty))) - } - for { - successor ← successors - if successor.node.isCatchNode || successor.node.isAbnormalReturnExitNode - exitStatement ← allNewExitFacts.keys - if exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID - } { - // FIXME Get rid of "getOrElse(...,Set.empty)" due to its potentially very BAD performance - summaryEdges += successor → (summaryEdges.getOrElse(successor, Set.empty[IFDSFact]) ++ - returnFlow(call, callee, exitStatement, successor, allNewExitFacts.getOrElse(exitStatement, Set.empty))) - } - } - } - summaryEdges - } - - /** - * Determines the successor statements for one source statement. - * - * @param statement The source statement. - * @param basicBlock The basic block containing the source statement. - * @return All successors of `statement`. - */ - def successorStatements( - statement: Statement, - basicBlock: BasicBlock - )( - implicit - state: State - ): Set[Statement] = { - val index = statement.index - if (index == basicBlock.endPC) { - for (successorBlock ← basicBlock.successors) yield firstStatement(successorBlock) - } else { - val nextIndex = index + 1 - Set(Statement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, statement.code, statement.cfg)) - } - } - - /** - * Adds a method-fact-pair as to the IFDS call sites and dependees. - * - * @param entity The method-fact-pair. - * @param calleeProperty The property, that was returned for `entity`. - * @param callBB The basic block of the call site. - * @param callIndex The index of the call site. - */ - def addIfdsDependee( - entity: (DeclaredMethod, IFDSFact), - calleeProperty: EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]], - callBB: BasicBlock, - callIndex: Int - )( - implicit - state: State - ): Unit = { - val callSites = state.pendingIfdsCallSites - state.pendingIfdsCallSites = callSites.updated(entity, callSites.getOrElse(entity, Set.empty) + ((callBB, callIndex))) - state.pendingIfdsDependees += entity → calleeProperty - } - - /** - * If `from` contains a null fact, it will be added to `to`. - * - * @param from The set, which may contain the null fact initially. - * @param to The set, to which the null fact may be added. - * @return `to` with the null fact added, if it is contained in `from`. - */ - def propagateNullFact(from: Set[IFDSFact], to: Set[IFDSFact]): Set[IFDSFact] = { - val nullFact = from.find(_.isInstanceOf[AbstractIFDSNullFact]) - if (nullFact.isDefined) to + nullFact.get - else to - } - - /** - * Merges two maps that have sets as values. - * - * @param map1 The first map. - * @param map2 The second map. - * @return A map containing the keys of both maps. Each key is mapped to the union of both maps' values. - */ - def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { - var result = map1 - for ((key, values) ← map2) { - result.get(key) match { - case Some(resultValues) ⇒ - if (resultValues.size > values.size) - result = result.updated(key, resultValues ++ values) - else - result = result.updated(key, values ++ resultValues) - case None ⇒ - result = result.updated(key, values) - } - } - result - } - - /** - * Computes the difference of two maps that have sets as their values. - * - * @param minuend The map, from which elements will be removed. - * @param subtrahend The map, whose elements will be removed from `minuend`. - * @return A map, containing the keys and values of `minuend`. - * The values of the result only contain those elements not present in `subtrahend` for the same key. - */ - def mapDifference[S, T](minuend: Map[S, Set[T]], subtrahend: Map[S, Set[T]]): Map[S, Set[T]] = { - var result = minuend - for ((key, values) ← subtrahend) { - result = result.updated(key, result(key) -- values) - } - result - } - - /** - * Gets the call for a statement that contains a call. - * - * @param statement The statement. - * @return The call contained in `statement`. - */ - protected[this] def asCall(statement: Stmt[V]): Call[V] = statement.astID match { - case Assignment.ASTID ⇒ statement.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID ⇒ statement.asExprStmt.expr.asFunctionCall - case _ ⇒ statement.asMethodCall - } - - /** - * Gets the first statement of a cfg node. - * - * @param node The node, for which the first statement will be retrieved. - * @return If the `node` is a basic block, its first statement will be returned. - * If it is a catch node, the first statement of its handler will be returned. - * If it is an exit node an artificial statement without code will be returned. - */ - @tailrec - private def firstStatement(node: CFGNode)(implicit state: State): Statement = { - if (node.isBasicBlock) { - val index = node.asBasicBlock.startPC - Statement(state.method, node, state.code(index), index, state.code, state.cfg) - } else if (node.isCatchNode) { - firstStatement(node.successors.head) - } else if (node.isExitNode) { - Statement(state.method, node, null, 0, state.code, state.cfg) - } else throw new IllegalArgumentException(s"Unknown node type: $node") - } - - /** - * This method will be called if a non-overwritten declared method in a sub type shall be analyzed. - * Analyzes the defined method of the supertype instead. - * - * @param source A pair consisting of the declared method of the subtype and an input fact. - * @return The result of the analysis of the defined method of the supertype. - */ - def baseMethodResult(source: (DeclaredMethod, IFDSFact)): ProperPropertyComputationResult = { - - def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { - case FinalP(p) ⇒ Result(source, p) - - case ep @ InterimUBP(ub: Property) ⇒ - InterimResult.forUB(source, ub, Set(ep), c) - - case epk ⇒ - InterimResult.forUB(source, createPropertyValue(Map.empty), Set(epk), c) - } - c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) - } -} - -/** - * A statement that is passed to the concrete analysis. - * - * @param method The method containing the statement. - * @param node The basic block containing the statement. - * @param stmt The TAC statement. - * @param index The index of the Statement in the code. - * @param code The method's TAC code. - * @param cfg The method's CFG. - */ -case class Statement( - method: Method, - node: CFGNode, - stmt: Stmt[V], - index: Int, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] -) { - - override def hashCode(): Int = method.hashCode() * 31 + index - - override def equals(o: Any): Boolean = { - o match { - case s: Statement ⇒ s.index == index && s.method == method - case _ ⇒ false - } - } - - override def toString: String = s"${method.toJava}" - -} - -object AbstractIFDSAnalysis { - - /** - * The type of the TAC domain. - */ - type V = DUVar[ValueInformation] -} - -abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalysisScheduler { - - final override type InitializationData = AbstractIFDSAnalysis[IFDSFact] - - def property: IFDSPropertyMetaInformation[IFDSFact] - - final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) - - override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) - - override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI)) - - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - - /** - * Registers the analysis as a lazy computation, that is, the method - * will call `ProperytStore.scheduleLazyComputation`. - */ - final override def register( - p: SomeProject, - ps: PropertyStore, - analysis: AbstractIFDSAnalysis[IFDSFact] - ): FPCFAnalysis = { - ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) - analysis - } - - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { - val ifdsAnalysis = analysis.asInstanceOf[AbstractIFDSAnalysis[IFDSFact]] - for (e ← ifdsAnalysis.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } - } - - override def afterPhaseCompletion( - p: SomeProject, - ps: PropertyStore, - analysis: FPCFAnalysis - ): Unit = {} - -} - diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala new file mode 100644 index 0000000000..0bde8e977f --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -0,0 +1,1230 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package tac +package fpcf +package analyses +package ifds + +import java.io.File +import java.io.PrintWriter + +import scala.collection.{Set ⇒ SomeSet} +import scala.collection.mutable + +import com.typesafe.config.ConfigValueFactory +import javax.swing.JOptionPane + +import org.opalj.util.Milliseconds +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.EPK +import org.opalj.fpcf.FinalE +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimEUBP +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.InterimUBP +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.Result +import org.opalj.fpcf.SomeEOptionP +import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.seq.PKESequentialPropertyStore +import org.opalj.value.ValueInformation +import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.SomeProject +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFG +import org.opalj.br.cfg.CFGNode +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.DeclaredMethod +import org.opalj.br.DefinedMethod +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.ai.domain.l0.PrimitiveTACAIDomain +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.fpcf.properties.TheTACAI +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V + +/** + * The supertype of all IFDS facts. + */ +trait AbstractIFDSFact + +/** + * The super type of all null facts. + */ +trait AbstractIFDSNullFact extends AbstractIFDSFact + +/** + * A framework for IFDS analyses. + * + * @tparam IFDSFact The type of flow facts, which are tracked by the concrete analysis. + * @author Dominik Helm + * @author Mario Trageser + */ +abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAnalysis with Subsumable[IFDSFact] { + + /** + * Provides the concrete property key that must be unique for every distinct concrete analysis + * and the lower bound for the IFDSProperty. + */ + val propertyKey: IFDSPropertyMetaInformation[IFDSFact] + + /** + * All declared methods in the project. + */ + final protected implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + + /** + * Counts, how many times the abstract methods were called. + */ + var numberOfCalls = new NumberOfCalls() + + /** + * Counts, how many input facts are passed to callbacks. + */ + var sumOfInputfactsForCallbacks = 0L + + /** + * The entry points of this analysis. + */ + def entryPoints: Seq[(DeclaredMethod, IFDSFact)] + + /** + * The state of the analysis. For each method and source fact, there is a separate state. + * + * @param declaringClass The class defining the analyzed `method`. + * @param method The analyzed method. + * @param source The input fact, for which the `method` is analyzed. + * @param code The code of the `method`. + * @param cfg The control flow graph of the `method`. + * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input + * facts to the basic block and statement index, at which they may + * be called. + * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input + * facts to the intermediate result of their IFDS analysis. + * Only contains method-fact-pairs, for which this analysis is + * waiting for a final result. + * @param pendingCgCallSites The basic blocks containing call sites, for which the analysis is + * waiting for the final call graph result. + * @param incomingFacts Maps each basic block to the data flow facts valid at its first + * statement. + * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at + * the beginning of the successor. + */ + protected class State( + val declaringClass: ObjectType, + val method: Method, + val source: (DeclaredMethod, IFDSFact), + val code: Array[Stmt[V]], + val cfg: CFG[Stmt[V], TACStmts[V]], + var pendingIfdsCallSites: Map[(DeclaredMethod, IFDSFact), Set[(BasicBlock, Int)]], + var pendingTacCallSites: Map[DeclaredMethod, Set[BasicBlock]] = Map.empty, + var pendingIfdsDependees: Map[(DeclaredMethod, IFDSFact), EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] = Map.empty, + var pendingTacDependees: Map[Method, EOptionP[Method, TACAI]] = Map.empty, + var pendingCgCallSites: Set[BasicBlock] = Set.empty, + var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, + var outgoingFacts: Map[BasicBlock, Map[CFGNode, Set[IFDSFact]]] = Map.empty + ) + + /** + * The null fact of this analysis. + */ + protected def nullFact: IFDSFact + + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param successor The successor of the analyzed `statement`, for which the data flow shall be + * computed. + * @param in The facts, which hold before the execution of the `statement`. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + protected def normalFlow(statement: Statement, successor: Statement, in: Set[IFDSFact]): Set[IFDSFact] + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The facts, which hold before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + protected def callFlow(call: Statement, callee: DeclaredMethod, in: Set[IFDSFact], + source: (DeclaredMethod, IFDSFact)): Set[IFDSFact] + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param callee The method called by `call`, for which the data flow shall be computed. + * @param exit The statement, which terminated the `calle`. + * @param successor The statement of the caller, which will be executed after the `callee` + * returned. + * @param in The facts, which hold before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + protected def returnFlow(call: Statement, callee: DeclaredMethod, exit: Statement, + successor: Statement, in: Set[IFDSFact]): Set[IFDSFact] + + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param successor The statement, which will be executed after the call. + * @param in The facts, which hold before the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + protected def callToReturnFlow(call: Statement, successor: Statement, in: Set[IFDSFact], + source: (DeclaredMethod, IFDSFact)): Set[IFDSFact] + + /** + * When a callee outside of this analysis' context is called, this method computes the summary + * edge for the call. + * + * @param call The statement, which invoked the call. + * @param callee The method called by `call`. + * @param successor The statement, which will be executed after the call. + * @param in The facts facts, which hold before the `call`. + * @return The facts, which hold after the call, excluding the call to return flow. + */ + protected def callOutsideOfAnalysisContext(call: Statement, callee: DeclaredMethod, + successor: Statement, in: Set[IFDSFact]): Set[IFDSFact] + + /** + * Determines the basic blocks, at which the analysis starts. + * + * @param sourceFact The source fact of the analysis. + * @param cfg The control flow graph of the analyzed method. + * @return The basic blocks, at which the analysis starts. + */ + protected def startBlocks(sourceFact: IFDSFact, cfg: CFG[Stmt[V], TACStmts[V]]): Set[BasicBlock] + + /** + * Collects the facts valid at all exit nodes based on the current results. + * + * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at + * the exit node under the assumption that the predecessor was executed before. + */ + protected def collectResult(implicit state: State): Map[Statement, Set[IFDSFact]] + + /** + * Creates an IFDSProperty containing the result of this analysis. + * + * @param result Maps each exit statement to the facts, which hold after the exit statement. + * @return An IFDSProperty containing the `result`. + */ + protected def createPropertyValue(result: Map[Statement, Set[IFDSFact]]): IFDSProperty[IFDSFact] + + /** + * Determines the nodes, that will be analyzed after some `basicBlock`. + * + * @param basicBlock The basic block, that was analyzed before. + * @return The nodes, that will be analyzed after `basicBlock`. + */ + protected def nextNodes(basicBlock: BasicBlock): Set[CFGNode] + + /** + * Checks, if some `node` is the last node. + * + * @return True, if `node` is the last node, i.e. there is no next node. + */ + protected def isLastNode(node: CFGNode): Boolean + + /** + * If the passed `node` is a catch node, all successors of this catch node are determined. + * + * @param node The node. + * @return If the node is a catch node, all its successors will be returned. + * Otherwise, the node itself will be returned. + */ + protected def skipCatchNode(node: CFGNode): Set[BasicBlock] + + /** + * Determines the first index of some `basic block`, that will be analyzed. + * + * @param basicBlock The basic block. + * @return The first index of some `basic block`, that will be analyzed. + */ + protected def firstIndex(basicBlock: BasicBlock): Int + + /** + * Determines the last index of some `basic block`, that will be analzyed. + * + * @param basicBlock The basic block. + * @return The last index of some `basic block`, that will be analzyed. + */ + protected def lastIndex(basicBlock: BasicBlock): Int + + /** + * Determines the index, that will be analyzed after some other `index`. + * + * @param index The source index. + * @return The index, that will be analyzed after `index`. + */ + protected def nextIndex(index: Int): Int + + /** + * Gets the first statement of a node, that will be analyzed. + * + * @param node The node. + * @return The first statement of a node, that will be analyzed. + */ + protected def firstStatement(node: CFGNode)(implicit state: State): Statement + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + protected def nextStatements(statement: Statement)(implicit state: State): Set[Statement] + + /** + * Determines the facts, for which a `callee` is analyzed. + * + * @param call The call, which calls `callee`. + * @param callee The method, which is called by `call`. + * @param in The facts, which hold before the `call`. + * @return The facts, for which `callee` will be analyzed. + */ + protected def callToStartFacts(call: Statement, callee: DeclaredMethod, + in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] + + /** + * Collects the exit facts of a `callee` and adds them to the `summaryEdges`. + * + * @param summaryEdges The current summary edges. They map successor statements of the `call` + * to facts, which hold before they are executed. + * @param successors The successor of `call`, which is considered. + * @param call The statement, which calls `callee`. + * @param callee The method, called by `call`. + * @param exitFacts Maps exit statements of the `callee` to the facts, which hold after them. + * @return The summary edges plus the exit to return facts for `callee` and `successor`. + */ + protected def addExitToReturnFacts( + summaryEdges: Map[Statement, Set[IFDSFact]], + successors: Set[Statement], call: Statement, + callee: DeclaredMethod, + exitFacts: Map[Statement, Set[IFDSFact]] + )(implicit state: State): Map[Statement, Set[IFDSFact]] + + /** + * Performs an IFDS analysis for a method-fact-pair. + * + * @param entity The method-fact-pair, that will be analyzed. + * @return An IFDS property mapping from exit statements to the facts valid after these exit + * statements. Returns an interim result, if the TAC or call graph of this method or the + * IFDS analysis for a callee is still pending. + */ + def performAnalysis(entity: (DeclaredMethod, IFDSFact)): ProperPropertyComputationResult = { + val (declaredMethod, sourceFact) = entity + + /* + * The analysis can only handle single defined methods. + * If a method is not single defined, this analysis assumes that it does not create any + * facts. + */ + if (!declaredMethod.hasSingleDefinedMethod) + return Result(entity, createPropertyValue(Map.empty)); + + val method = declaredMethod.definedMethod + val declaringClass: ObjectType = method.classFile.thisType + + /* + * If this is not the method's declaration, but a non-overwritten method in a subtype, do + * not re-analyze the code. + */ + if (declaringClass ne declaredMethod.declaringClassType) return baseMethodResult(entity); + + /* + * Fetch the method's three address code. If it is not present, return an empty interim + * result. + */ + val (code, cfg) = propertyStore(method, TACAI.key) match { + case FinalP(TheTACAI(tac)) ⇒ (tac.stmts, tac.cfg) + + case epk: EPK[Method, TACAI] ⇒ + return InterimResult.forUB( + entity, + createPropertyValue(Map.empty), + Set(epk), + _ ⇒ performAnalysis(entity) + ); + + case tac ⇒ + throw new UnknownError(s"can't handle intermediate TACs ($tac)"); + } + + // Start processing at the start of the cfg with the given source fact + implicit val state: State = + new State(declaringClass, method, entity, code, cfg, Map(entity → Set.empty), Map()) + val queue = mutable.Queue.empty[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] + startBlocks(sourceFact, cfg).foreach { start ⇒ + state.incomingFacts += start → Set(sourceFact) + queue.enqueue((start, Set(sourceFact), None, None, None)) + } + process(queue) + createResult() + } + + /** + * Creates the current (intermediate) result for the analysis. + * + * @return A result containing a map, which maps each exit statement to the facts valid after + * the statement, based on the current results. If the analysis is still waiting for its + * method's TAC or call graph or the result of another method-fact-pair, an interim + * result will be returned. + * + */ + protected def createResult()(implicit state: State): ProperPropertyComputationResult = { + val propertyValue = createPropertyValue(collectResult) + val dependees = state.pendingIfdsDependees.values ++ state.pendingTacDependees.values + if (dependees.isEmpty) Result(state.source, propertyValue) + else InterimResult.forUB(state.source, propertyValue, dependees.toSet, propertyUpdate) + } + + /** + * Called, when there is an updated result for a tac, call graph or another method-fact-pair, + * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and + * returns the new analysis result. + * + * @param eps The new property value. + * @return The new (interim) result of this analysis. + */ + protected def propertyUpdate(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + (eps: @unchecked) match { + case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) ⇒ + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) + + case interimEUBP @ InterimEUBP(e: (DeclaredMethod, IFDSFact) @unchecked, + ub: IFDSProperty[IFDSFact]) ⇒ + if (ub.flows.values + .forall(facts ⇒ facts.size == 1 && facts.forall(_ == nullFact))) { + // Do not re-analyze the caller if we only get the null fact. + // Update the pendingIfdsDependee entry to the new interim result. + state.pendingIfdsDependees += + e → interimEUBP.asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + } else + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) + + case FinalEP(_: DefinedMethod, _: Callees) ⇒ + reAnalyzebasicBlocks(state.pendingCgCallSites) + + case InterimEUBP(_: DefinedMethod, _: Callees) ⇒ + reAnalyzebasicBlocks(state.pendingCgCallSites) + + case FinalEP(method: Method, _: TACAI) ⇒ + state.pendingTacDependees -= method + reAnalyzebasicBlocks(state.pendingTacCallSites(declaredMethods(method))) + } + + createResult() + } + + /** + * Checks, if a callee is inside this analysis' context. + * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. + * By default, native methods are not inside the analysis context. + * + * @param callee The callee. + * @return True, if the callee is inside the analysis context. + */ + protected def insideAnalysisContext(callee: DeclaredMethod): Boolean = + callee.definedMethod.body.isDefined + + /** + * Called, when some new information is found at the last node of the method. + * This method can be overwritten by a subclass to perform additional actions. + * + * @param nextIn The input facts, which were found. + * @param oldIn The input facts, which were already known, if present. + */ + protected def foundNewInformationForLastNode( + nextIn: Set[IFDSFact], + oldIn: Option[Set[IFDSFact]], + state: State + ): Unit = {} + + /** + * Processes a statement with a call. + * + * @param basicBlock The basic block, which contains the `call`. + * @param call The call statement. + * @param callees All possible callees. + * @param in The facts, which hold before the call statement. + * @param calleeWithUpdateFact If present, the `callees` will only be analyzed with this fact + * instead of the facts returned by callToStartFacts. + * @return A map, mapping from each successor statement of the `call` to the facts, which hold + * at their start. + */ + protected def handleCall(basicBlock: BasicBlock, call: Statement, callees: SomeSet[Method], + in: Set[IFDSFact], calleeWithUpdateFact: Option[IFDSFact])( + implicit + state: State + ): Map[Statement, Set[IFDSFact]] = { + val successors = nextStatements(call) + val inputFacts = beforeHandleCall(call, in) + // Facts valid at the start of each successor + var summaryEdges: Map[Statement, Set[IFDSFact]] = Map.empty + + /* + * If calleeWithUpdateFact is present, this means that the basic block already has been + * analyzed with the `inputFacts`. + */ + if (calleeWithUpdateFact.isEmpty) + for (successor ← successors) { + numberOfCalls.callToReturnFlow += 1 + sumOfInputfactsForCallbacks += in.size + summaryEdges += successor → + propagateNullFact( + inputFacts, + callToReturnFlow(call, successor, inputFacts, state.source) + ) + } + + for (calledMethod ← callees) { + val callee = declaredMethods(calledMethod) + if (!insideAnalysisContext(callee)) { + // Let the concrete analysis decide what to do. + for { + successor ← successors + } summaryEdges += + successor → (summaryEdges(successor) ++ + callOutsideOfAnalysisContext(call, callee, successor, in)) + } else { + val callToStart = + if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) + else { + propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) + } + var allNewExitFacts: Map[Statement, Set[IFDSFact]] = Map.empty + // Collect exit facts for each input fact separately + for (fact ← callToStart) { + /* + * If this is a recursive call with the same input facts, we assume that the + * call only produces the facts that are already known. The call site is added to + * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts + * become known for the input fact. + */ + if ((calledMethod eq state.method) && fact == state.source._2) { + val newDependee = + if (state.pendingIfdsCallSites.contains(state.source)) + state.pendingIfdsCallSites(state.source) + + ((basicBlock, call.index)) + else Set((basicBlock, call.index)) + state.pendingIfdsCallSites = + state.pendingIfdsCallSites.updated(state.source, newDependee) + allNewExitFacts = mergeMaps(allNewExitFacts, collectResult) + } else { + val e = (callee, fact) + val callFlows = propertyStore(e, propertyKey.key) + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + val oldValue = state.pendingIfdsDependees.get(e) + val oldExitFacts: Map[Statement, Set[IFDSFact]] = oldValue match { + case Some(ep: InterimEUBP[_, IFDSProperty[IFDSFact]]) ⇒ ep.ub.flows + case _ ⇒ Map.empty + } + val exitFacts: Map[Statement, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[IFDSFact]] ⇒ + if (state.pendingIfdsCallSites.contains(e) + && state.pendingIfdsCallSites(e).nonEmpty) { + val newDependee = + state.pendingIfdsCallSites(e) - ((basicBlock, call.index)) + state.pendingIfdsCallSites = + state.pendingIfdsCallSites.updated(e, newDependee) + } + state.pendingIfdsDependees -= e + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[IFDSFact]] ⇒ + /* + * Add the call site to `pendingIfdsCallSites` and + * `pendingIfdsDependees` and continue with the facts in the interim + * result for now. When the analysis for the callee finishes, the + * analysis for this call site will be triggered again. + */ + addIfdsDependee(e, callFlows, basicBlock, call.index) + ep.ub.flows + case _ ⇒ + addIfdsDependee(e, callFlows, basicBlock, call.index) + Map.empty + } + // Only process new facts that are not in `oldExitFacts` + allNewExitFacts = + mergeMaps( + allNewExitFacts, + filterNewInformation(exitFacts, oldExitFacts, project) + ) + /* + * If new exit facts were discovered for the callee-fact-pair, all call + * sites depending on this pair have to be re-evaluated. oldValue is + * undefined if the callee-fact pair has not been queried before or returned + * a FinalEP. + */ + if (oldValue.isDefined && oldExitFacts != exitFacts) { + reAnalyzeCalls( + state.pendingIfdsCallSites(e), + e._1.definedMethod, Some(e._2) + ) + } + } + } + summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, + allNewExitFacts) + } + } + summaryEdges + } + + /** + * This method is called at the beginning of handleCall. + * A subclass can overwrite this method, to change the input facts of the call. + * + * @param call The call statement. + * @param in The input facts, which hold before the `call`. + * @return The changed set of input facts. + */ + protected def beforeHandleCall(call: Statement, in: Set[IFDSFact]): Set[IFDSFact] = in + + /** + * Gets the call object for a statement that contains a call. + * + * @param call The call statement. + * @return The call object for `call`. + */ + protected def asCall(call: Stmt[V]): Call[V] = call.astID match { + case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall + case _ ⇒ call.asMethodCall + } + + /** + * Gets the set of all methods directly callable at some call statement. + * + * @param basicBlock The basic block containing the call. + * @param pc The call's program counter. + * @param caller The caller, performing the call. + * @return All methods directly callable at the statement index. + */ + protected def getCallees(basicBlock: BasicBlock, pc: Int, + caller: DeclaredMethod): SomeSet[Method] = { + val ep = propertyStore(caller, Callees.key) + ep match { + case FinalEP(_, p) ⇒ definedMethods(p.directCallees(pc)) + case _ ⇒ + throw new IllegalStateException( + "call graph mut be computed before the analysis starts" + ) + } + } + + /** + * Merges two maps that have sets as values. + * + * @param map1 The first map. + * @param map2 The second map. + * @return A map containing the keys of both maps. Each key is mapped to the union of both maps' + * values. + */ + protected def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { + var result = map1 + for ((key, values) ← map2) { + result.get(key) match { + case Some(resultValues) ⇒ + if (resultValues.size > values.size) + result = result.updated(key, resultValues ++ values) + else + result = result.updated(key, values ++ resultValues) + case None ⇒ + result = result.updated(key, values) + } + } + result + } + + /** + * Returns all methods, that can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @return All methods, that can be called from outside the library. + */ + protected def methodsCallableFromOutside: Set[DeclaredMethod] = + declaredMethods.declaredMethods.filter(canBeCalledFromOutside).toSet + + /** + * Checks, if some `method` can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @param method The method, which may be callable from outside. + * @return True, if `method` can be called from outside the library. + */ + protected def canBeCalledFromOutside(method: DeclaredMethod): Boolean = { + val FinalEP(_, callers) = propertyStore(method, Callers.key) + callers.hasCallersWithUnknownContext + } + + /** + * Analyzes a queue of BasicBlocks. + * + * @param worklist A queue of the following elements: + * bb The basic block that will be analyzed. + * in New data flow facts found to hold at the beginning of the basic block. + * calleeWithUpdateIndex If the basic block is analyzed because there is new information + * for a callee, this is the call site's index. + * calleeWithUpdate If the basic block is analyzed because there is new information for a + * callee, this is the callee. + * calleeWithUpdateFact If the basic block is analyzed because there is new information + * for a callee with a specific input fact, this is the input fact. + */ + private def process(worklist: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])])(implicit state: State): Unit = { + while (worklist.nonEmpty) { + val (basicBlock, in, calleeWithUpdateIndex, calleeWithUpdate, calleeWithUpdateFact) = + worklist.dequeue() + val oldOut = state.outgoingFacts.getOrElse(basicBlock, Map.empty) + val nextOut = analyzeBasicBlock(basicBlock, in, calleeWithUpdateIndex, calleeWithUpdate, + calleeWithUpdateFact) + val allOut = mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) + state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) + + for (successor ← nextNodes(basicBlock)) { + if (isLastNode(successor)) { + // Re-analyze recursive call sites with the same input fact. + val nextOutSuccessors = nextOut.get(successor) + if (nextOutSuccessors.isDefined && nextOutSuccessors.get.nonEmpty) { + val oldOutSuccessors = oldOut.get(successor) + if (oldOutSuccessors.isEmpty || containsNewInformation( + nextOutSuccessors.get, oldOutSuccessors.get, project + )) { + val source = state.source + foundNewInformationForLastNode( + nextOutSuccessors.get, oldOutSuccessors, state + ) + reAnalyzeCalls( + state.pendingIfdsCallSites(source), + source._1.definedMethod, Some(source._2) + ) + } + } + } else { + val successorBlock = successor.asBasicBlock + val nextIn = nextOut.getOrElse(successorBlock, Set.empty) + val oldIn = state.incomingFacts.getOrElse(successorBlock, Set.empty) + val newIn = notSubsumedBy(nextIn, oldIn, project) + val mergedIn = + if (nextIn.size > oldIn.size) nextIn ++ oldIn + else oldIn ++ nextIn + state.incomingFacts = + state.incomingFacts.updated(successorBlock, subsume(mergedIn, project)) + /* + * Only process the successor with new facts. + * It is analyzed at least one time because of the null fact. + */ + if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None, None)) + } + } + } + } + + /** + * Computes for one basic block the facts valid on each CFG edge leaving the block if `sources` + * held before the block. + * + * @param basicBlock The basic block, that will be analyzed. + * @param in The facts, that hold before the block. + * @param calleeWithUpdateIndex If the basic block is analyzed because there is new information + * for a callee, this is the call site's index. + * @param calleeWithUpdate If the basic block is analyzed because there is new information for + * a callee, this is the callee. + * @param calleeWithUpdateFact If the basic block is analyzed because there is new information + * for a callee with a specific input fact, this is the input fact. + * @return A map, mapping each successor node to its input facts. Instead of catch nodes, this + * map contains their handler nodes. + */ + private def analyzeBasicBlock(basicBlock: BasicBlock, in: Set[IFDSFact], + calleeWithUpdateIndex: Option[Int], + calleeWithUpdate: Option[Method], + calleeWithUpdateFact: Option[IFDSFact])( + implicit + state: State + ): Map[CFGNode, Set[IFDSFact]] = { + + /* + * Collects information about a statement. + * + * @param index The statement's index. + * @return A tuple of the following elements: + * statement: The statement at `index`. + * calees: The methods possibly called at this statement, if it contains a call. + * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will + * be returned. + * calleeFact: If `index` equals `calleeWithUpdateIndex`, only + * `calleeWithUpdateFact` will be returned, None otherwise. + */ + def collectInformation(index: Int): (Statement, Option[SomeSet[Method]], Option[IFDSFact]) = { + val stmt = state.code(index) + val statement = Statement(state.method, basicBlock, stmt, index, state.code, state.cfg) + val calleesO = + if (calleeWithUpdateIndex.contains(index)) calleeWithUpdate.map(Set(_)) + else getCalleesIfCallStatement(basicBlock, index) + val calleeFact = + if (calleeWithUpdateIndex.contains(index)) calleeWithUpdateFact + else None + (statement, calleesO, calleeFact) + } + + val last = lastIndex(basicBlock) + var flows: Set[IFDSFact] = in + var index = firstIndex(basicBlock) + + // Iterate over all statements but the last one, only keeping the resulting DataFlowFacts. + while (index != last) { + val (statement, calleesO, calleeFact) = collectInformation(index) + val next = nextIndex(index) + flows = + if (calleesO.isEmpty) { + val successor = Statement(state.method, basicBlock, state.code(next), next, + state.code, state.cfg) + numberOfCalls.normalFlow += 1 + sumOfInputfactsForCallbacks += in.size + normalFlow(statement, successor, flows) + } else + // Inside a basic block, we only have one successor --> Take the head + handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head + index = next + } + + // Analyze the last statement for each possible successor statement. + val (statement, calleesO, callFact) = collectInformation(last) + var result: Map[CFGNode, Set[IFDSFact]] = + if (calleesO.isEmpty) { + var result: Map[CFGNode, Set[IFDSFact]] = Map.empty + for (node ← nextNodes(basicBlock)) { + numberOfCalls.normalFlow += 1 + sumOfInputfactsForCallbacks += in.size + result += node → normalFlow(statement, firstStatement(node), flows) + } + result + } else handleCall(basicBlock, statement, calleesO.get, flows, callFact) + .map(entry ⇒ entry._1.node → entry._2) + + // Propagate the null fact. + result = result.map(result ⇒ result._1 → propagateNullFact(in, result._2)) + result + } + + /** + * Re-analyzes some basic blocks. + * + * @param basicBlocks The basic blocks, that will be re-analyzed. + */ + private def reAnalyzebasicBlocks(basicBlocks: Set[BasicBlock])(implicit state: State): Unit = { + val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty + for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) + process(queue) + } + + /** + * Re-analyzes some call sites with respect to one specific callee. + * + * @param callSites The call sites, which are analyzed. + * @param callee The callee, which will be considered at the `callSites`. + * @param fact If defined, the `callee` will only be analyzed for this fact. + */ + private def reAnalyzeCalls(callSites: Set[(BasicBlock, Int)], callee: Method, + fact: Option[IFDSFact])(implicit state: State): Unit = { + val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty + for ((block, index) ← callSites) + queue.enqueue((block, state.incomingFacts(block), Some(index), Some(callee), fact)) + process(queue) + } + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param basicBlock The basic block containing the statement. + * @param index The statement's index. + * @return All methods possibly called at the statement index or None, if the statement does not + * contain a call. + */ + private def getCalleesIfCallStatement(basicBlock: BasicBlock, index: Int)( + implicit + state: State + ): Option[SomeSet[Method]] = { + val statement = state.code(index) + val pc = statement.pc + statement.astID match { + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + Some(getCallees(basicBlock, pc, state.source._1)) + case Assignment.ASTID | ExprStmt.ASTID ⇒ getExpression(statement).astID match { + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | + VirtualFunctionCall.ASTID ⇒ + Some(getCallees(basicBlock, pc, state.source._1)) + case _ ⇒ None + } + case _ ⇒ None + } + } + + /** + * Maps some declared methods to their defined methods. + * + * @param declaredMethods Some declared methods. + * @return All defined methods of `declaredMethods`. + */ + private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): SomeSet[Method] = { + val result = scala.collection.mutable.Set.empty[Method] + declaredMethods.filter(declaredMethod ⇒ declaredMethod.hasSingleDefinedMethod || + declaredMethod.hasMultipleDefinedMethods). + foreach(declaredMethod ⇒ declaredMethod + .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod))) + result + } + + /** + * Retrieves the expression of an assignment or expression statement. + * + * @param statement The statement. Must be an Assignment or ExprStmt. + * @return The statement's expression. + */ + private def getExpression(statement: Stmt[V]): Expr[V] = statement.astID match { + case Assignment.ASTID ⇒ statement.asAssignment.expr + case ExprStmt.ASTID ⇒ statement.asExprStmt.expr + case _ ⇒ throw new UnknownError("Unexpected statement") + } + + /** + * If `from` contains a null fact, it will be added to `to`. + * + * @param from The set, which may contain the null fact initially. + * @param to The set, to which the null fact may be added. + * @return `to` with the null fact added, if it is contained in `from`. + */ + private def propagateNullFact(from: Set[IFDSFact], to: Set[IFDSFact]): Set[IFDSFact] = { + if (from.contains(nullFact)) to + nullFact + else to + } + + /** + * Adds a method-fact-pair as to the IFDS call sites and dependees. + * + * @param entity The method-fact-pair. + * @param calleeProperty The property, that was returned for `entity`. + * @param callBB The basic block of the call site. + * @param callIndex The index of the call site. + */ + private def addIfdsDependee( + entity: (DeclaredMethod, IFDSFact), + calleeProperty: EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]], + callBB: BasicBlock, + callIndex: Int + )(implicit state: State): Unit = { + val callSites = state.pendingIfdsCallSites + state.pendingIfdsCallSites = callSites.updated( + entity, + callSites.getOrElse(entity, Set.empty) + ((callBB, callIndex)) + ) + state.pendingIfdsDependees += entity → calleeProperty + } + + /** + * This method will be called if a non-overwritten declared method in a sub type shall be + * analyzed. Analyzes the defined method of the supertype instead. + * + * @param source A pair consisting of the declared method of the subtype and an input fact. + * @return The result of the analysis of the defined method of the supertype. + */ + private def baseMethodResult(source: (DeclaredMethod, IFDSFact)): ProperPropertyComputationResult = { + + def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { + case FinalP(p) ⇒ Result(source, p) + + case ep @ InterimUBP(ub: Property) ⇒ + InterimResult.forUB(source, ub, Set(ep), c) + + case epk ⇒ + InterimResult.forUB(source, createPropertyValue(Map.empty), Set(epk), c) + } + c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) + } +} + +object AbstractIFDSAnalysis { + + /** + * The type of the TAC domain. + */ + type V = DUVar[ValueInformation] + + /** + * When true, the cross product of exit and successor in returnFLow will be optimized. + */ + var OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW: Boolean = true + + /** + * Converts the index of a method's formal parameter to its tac index in the method's scope and + * vice versa. + * + * @param index The index of a formal parameter in the parameter list or of a variable. + * @param isStaticMethod States, whether the method is static. + * @return A tac index if a parameter index was passed or a parameter index if a tac index was + * passed. + */ + def switchParamAndVariableIndex(index: Int, isStaticMethod: Boolean): Int = + (if (isStaticMethod) -2 else -1) - index + +} + +/** + * A statement that is passed to the concrete analysis. + * + * @param method The method containing the statement. + * @param node The basic block containing the statement. + * @param stmt The TAC statement. + * @param index The index of the Statement in the code. + * @param code The method's TAC code. + * @param cfg The method's CFG. + */ +case class Statement( + method: Method, + node: CFGNode, + stmt: Stmt[V], + index: Int, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] +) { + + override def hashCode(): Int = method.hashCode() * 31 + index + + override def equals(o: Any): Boolean = o match { + case s: Statement ⇒ s.index == index && s.method == method + case _ ⇒ false + } + + override def toString: String = s"${method.toJava}" + +} + +/** + * Contains int variables, which count, how many times some method was called. + */ +class NumberOfCalls { + var normalFlow = 0 + var callFlow = 0 + var returnFlow = 0 + var callToReturnFlow = 0 +} + +abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalysisScheduler { + + final override type InitializationData = AbstractIFDSAnalysis[IFDSFact] + + def property: IFDSPropertyMetaInformation[IFDSFact] + + final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) + + override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey) + + override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI)) + + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + + /** + * Registers the analysis as a lazy computation, that is, the method + * will call `ProperytStore.scheduleLazyComputation`. + */ + final override def register(p: SomeProject, ps: PropertyStore, + analysis: AbstractIFDSAnalysis[IFDSFact]): FPCFAnalysis = { + ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) + analysis + } + + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { + val ifdsAnalysis = analysis.asInstanceOf[AbstractIFDSAnalysis[IFDSFact]] + for (e ← ifdsAnalysis.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } + } + + override def afterPhaseCompletion(p: SomeProject, ps: PropertyStore, + analysis: FPCFAnalysis): Unit = {} + +} + +abstract class AbsractIFDSAnalysisRunner { + + protected def analysisClass: IFDSAnalysis[_] + + protected def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit + + protected def run(debug: Boolean, useL2: Boolean, delay: Boolean, evalSchedulingStrategies: Boolean, + evaluationFile: Option[File]): Unit = { + + if (debug) { + PropertyStore.updateDebug(true) + } + + def evalProject(p: SomeProject): (Milliseconds, NumberOfCalls, Option[Object], Long) = { + if (useL2) { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) ⇒ requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] + } + } else { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + case None ⇒ Set(classOf[PrimitiveTACAIDomain]) + case Some(requirements) ⇒ requirements + classOf[PrimitiveTACAIDomain] + } + } + + val ps = p.get(PropertyStoreKey) + var analysisTime: Milliseconds = Milliseconds.None + p.get(RTACallGraphKey) + println("Start: "+new java.util.Date) + org.opalj.util.gc() + if (AbstractIFDSAnalysisRunner.MEASURE_MEMORY) + JOptionPane.showMessageDialog(null, "Call Graph finished") + val analysis = + time { + p.get(FPCFAnalysesManagerKey).runAll(analysisClass)._2 + }(t ⇒ analysisTime = t.toMilliseconds).collect { + case (_, a: AbstractIFDSAnalysis[_]) ⇒ a + }.head + if (AbstractIFDSAnalysisRunner.MEASURE_MEMORY) + JOptionPane.showMessageDialog(null, "Analysis finished") + + printAnalysisResults(analysis, ps) + println(s"The analysis took $analysisTime.") + println( + ps.statistics.iterator.map(_.toString()).toList + .sorted + .mkString("PropertyStore Statistics:\n\t", "\n\t", "\n") + ) + (analysisTime, analysis.numberOfCalls, additionalEvaluationResult(analysis), analysis.sumOfInputfactsForCallbacks) + } + + val p = Project(bytecode.RTJar) + + if (delay) { + println("Sleeping for three seconds.") + Thread.sleep(3000) + } + + if (evalSchedulingStrategies) { + val results = for { + i ← 1 to AbstractIFDSAnalysisRunner.NUM_EXECUTIONS_EVAL_SCHEDULING_STRATEGIES + strategy ← PKESequentialPropertyStore.Strategies + } yield { + println(s"Round: $i - $strategy") + val strategyValue = ConfigValueFactory.fromAnyRef(strategy) + val newConfig = + p.config.withValue(PKESequentialPropertyStore.TasksManagerKey, strategyValue) + val evaluationResult = evalProject(Project.recreate(p, newConfig)) + org.opalj.util.gc() + (i, strategy, evaluationResult._1, evaluationResult._2) + } + println(results.mkString("AllResults:\n\t", "\n\t", "\n")) + if (evaluationFile.nonEmpty) { + val pw = new PrintWriter(evaluationFile.get) + PKESequentialPropertyStore.Strategies.foreach { strategy ⇒ + val strategyResults = results.filter(_._2 == strategy) + val averageTime = strategyResults.map(_._3.timeSpan).sum / strategyResults.size + val (normalFlow, callToStart, exitToReturn, callToReturn) = computeAverageNumberCalls(strategyResults.map(_._4)) + pw.println(s"Strategy $strategy:") + pw.println(s"Average time: ${averageTime}ms") + pw.println(s"Average calls of normalFlow: $normalFlow") + pw.println(s"Average calls of callToStart: $callToStart") + pw.println(s"Average calls of exitToReturn: $exitToReturn") + pw.println(s"Average calls of callToReturn: $callToReturn") + pw.println() + } + pw.close() + } + } else { + var times = Seq.empty[Milliseconds] + var numberOfCalls = Seq.empty[NumberOfCalls] + var sumsOfInputFactsForCallbacks = Seq.empty[Long] + var additionalEvaluationResults = Seq.empty[Object] + for { + _ ← 1 to AbstractIFDSAnalysisRunner.NUM_EXECUTIONS + } { + val evaluationResult = evalProject(Project.recreate(p)) + val additionalEvaluationResult = evaluationResult._3 + times :+= evaluationResult._1 + numberOfCalls :+= evaluationResult._2 + sumsOfInputFactsForCallbacks :+= evaluationResult._4 + if (additionalEvaluationResult.isDefined) + additionalEvaluationResults :+= additionalEvaluationResult.get + } + if (evaluationFile.nonEmpty) { + val (normalFlow, callFlow, returnFlow, callToReturnFlow) = computeAverageNumberCalls(numberOfCalls) + val time = times.map(_.timeSpan).sum / times.size + val sumOfInputFactsForCallbacks = sumsOfInputFactsForCallbacks.sum / sumsOfInputFactsForCallbacks.size + val pw = new PrintWriter(evaluationFile.get) + pw.println(s"Average time: ${time}ms") + pw.println(s"Average calls of normalFlow: $normalFlow") + pw.println(s"Average calls of callFlow: $callFlow") + pw.println(s"Average calls of returnFlow: $returnFlow") + pw.println(s"Average calls of callToReturnFlow: $callToReturnFlow") + pw.println(s"Sum of input facts for callbacks: $sumOfInputFactsForCallbacks") + if (additionalEvaluationResults.nonEmpty) + writeAdditionalEvaluationResultsToFile(pw, additionalEvaluationResults) + pw.close() + } + } + } + + protected def additionalEvaluationResult(analysis: AbstractIFDSAnalysis[_]): Option[Object] = None + + protected def writeAdditionalEvaluationResultsToFile( + writer: PrintWriter, + additionalEvaluationResults: Seq[Object] + ): Unit = {} + + protected def canBeCalledFromOutside(method: DeclaredMethod, propertyStore: PropertyStore): Boolean = + propertyStore(method, Callers.key) match { + // This is the case, if the method may be called from outside the library. + case FinalEP(_, p: Callers) ⇒ p.hasCallersWithUnknownContext + case _ ⇒ + throw new IllegalStateException( + "call graph mut be computed before the analysis starts" + ) + } + + private def computeAverageNumberCalls(numberOfCalls: Seq[NumberOfCalls]): (Int, Int, Int, Int) = { + val length = numberOfCalls.length + val normalFlow = numberOfCalls.map(_.normalFlow).sum / length + val callFlow = numberOfCalls.map(_.callFlow).sum / length + val returnFlow = numberOfCalls.map(_.returnFlow).sum / length + val callToReturnFlow = numberOfCalls.map(_.callToReturnFlow).sum / length + (normalFlow, callFlow, returnFlow, callToReturnFlow) + } +} + +object AbstractIFDSAnalysisRunner { + var NUM_EXECUTIONS = 10 + var NUM_EXECUTIONS_EVAL_SCHEDULING_STRATEGIES = 2 + var MEASURE_MEMORY = false +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala new file mode 100644 index 0000000000..e1d3a38056 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala @@ -0,0 +1,476 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import scala.annotation.tailrec + +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.FinalE +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimEUBP +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.SomeEPS +import org.opalj.value.ValueInformation +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFGNode +import org.opalj.br.DeclaredMethod +import org.opalj.br.cfg.CatchNode +import org.opalj.br.cfg.CFG +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.tac.Stmt +import org.opalj.tac.TACStmts +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.fpcf.properties.TheTACAI +import org.opalj.tac.DUVar +import org.opalj.tac.Return +import org.opalj.tac.ReturnValue +import org.opalj.tac.TACMethodParameter +import org.opalj.tac.TACode + +/** + * A data flow fact, that was created by an unbalanced return. + * + * @tparam FactType The type of flow facts, which are tracked by the concrete analysis. + */ +trait UnbalancedReturnFact[FactType] extends AbstractIFDSFact { + + /** + * The index of the call, which creates the inner fact. + */ + val index: Int + + /** + * The fact, which will hold after `index`. + */ + val innerFact: FactType +} + +/** + * An IFDS analysis, which analyzes the code against the control flow direction. + * + * @tparam UnbalancedIFDSFact The type of unbalanced return facts facts, which are tracked by the + * concrete analysis. + * @author Mario Trageser + */ +abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]] + extends AbstractIFDSAnalysis[IFDSFact] { + + /** + * Called, when the entry point of the analyzed method is reached. + * Creates unbalanced return facts for the callers. + * + * @param facts The facts, which hold at the entry point of the analyzed method. + * @param call A call, which calls the analyzed method. + * @param caller The method, invoking the `call`. + * @param source The entity, which is currently analyzed. + * @return Unbalanced return facts, that hold after `call` under the assumption, that `facts` + * held at the entry point of the analyzed method. + */ + protected def unbalancedReturnFlow(facts: Set[IFDSFact], call: Statement, caller: DeclaredMethod, + source: (DeclaredMethod, IFDSFact)): Set[UnbalancedIFDSFact] + + /** + * If this method is analyzed for an unbalanced return fact, the single star block is the block, + * which contains the call. + * Otherwise, the start blocks are the return nodes of the method. + */ + override protected def startBlocks( + sourceFact: IFDSFact, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Set[BasicBlock] = + sourceFact match { + case fact: UnbalancedReturnFact[IFDSFact] ⇒ Set(cfg.bb(fact.index)) + case _ ⇒ Set(cfg.normalReturnNode, cfg.abnormalReturnNode).flatMap(_.predecessors) + .foldLeft(Set.empty[BasicBlock])((c, n) ⇒ c + n.asBasicBlock) + } + + /** + * Collects the output facts of the entry point of the analyzed method. + */ + override protected def collectResult(implicit state: State): Map[Statement, Set[IFDSFact]] = { + val startBlock = state.cfg.startBlock + val startPC = startBlock.startPC + val statement = Statement(state.method, startBlock, state.code(startPC), + startPC, state.code, state.cfg) + val exitFacts = state.outgoingFacts.get(startBlock).flatMap(_.get(SyntheticStartNode)) + if (exitFacts.isDefined) Map(statement → exitFacts.get) + else Map.empty + } + + /** + * If the update is for an IFDS entity with an unbalanced return fact, the IFDS dependency is + * updated if it is an interim result or removed if it is a final result. + * If the update is not for an unbalanced return fact, the update will be handled by + * AbstractIFDSAnalysis. + */ + override protected def propertyUpdate(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + (eps: @unchecked) match { + /* + * If the analysis for the unbalanced return finished, remove the entity from the + * dependencies. + */ + case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) ⇒ + if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]]) { + state.pendingIfdsDependees -= e + createResult + } else super.propertyUpdate(eps) + /* + * If there is an interim result for an unbalanced return fact, ignore it and just create the result. + */ + case interimEUBP @ InterimEUBP(e: (DeclaredMethod, IFDSFact) @unchecked, + _: IFDSProperty[IFDSFact]) ⇒ + if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]]) { + state.pendingIfdsDependees += + e → interimEUBP.asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + createResult() + } else super.propertyUpdate(eps) + + // If this is not an update for an unbalanced return fact, handle it as usual. + case _ ⇒ super.propertyUpdate(eps) + } + } + + /** + * If `basicBlock` is the method's entry point, the synthetic start node will be returned. + * Otherwise, its predecessors will be returned. + */ + override protected def nextNodes(basicBlock: BasicBlock): Set[CFGNode] = { + if (basicBlock.startPC == 0) Set(SyntheticStartNode) + else { + val predecessors = scala.collection.mutable.Set.empty[CFGNode] + basicBlock.predecessors.foreach { + case basicBlock: BasicBlock ⇒ predecessors += basicBlock + case catchNode: CatchNode ⇒ + predecessors ++= catchNode.predecessors.iterator.map(_.asBasicBlock) + } + predecessors.toSet + } + } + + /** + * The synthetic start node is the last node. + */ + override protected def isLastNode(node: CFGNode): Boolean = node == SyntheticStartNode + + /** + * When new output facts for the method's entry point are found, a concrete analysis may add + * additional facts. + * Then, all direct callers of the method are determined. For each caller, new unbalanced return + * facts are created and the caller entity will be added to the pending IFDS dependencies, so + * that the caller entity will be analyzed. + */ + override protected def foundNewInformationForLastNode( + nextIn: Set[IFDSFact], + oldIn: Option[Set[IFDSFact]], + state: State + ): Unit = { + var newIn = if (oldIn.isDefined) notSubsumedBy(nextIn, oldIn.get, project) else nextIn + val created = createFactsAtStartNode(newIn, state.source) + if (created.nonEmpty) { + // Add the created facts to newIn and update the outgoing facts in the state. + newIn = newIn ++ created + val startBlock = state.cfg.startBlock + val oldOut = state.outgoingFacts(startBlock) + val newOut = oldOut.updated(SyntheticStartNode, oldOut(SyntheticStartNode) ++ created) + state.outgoingFacts = state.outgoingFacts.updated(startBlock, newOut) + } + // Only create unbalanced returns, if we are at an entry point or in a (indirect) caller of it. + if (shouldPerformUnbalancedReturn(state.source)) { + // Get all callers of this method + propertyStore(state.source._1, Callers.key) match { + case FinalEP(_, p: Callers) ⇒ p.callers.foreach { callersProperty ⇒ + val (caller, callPc, directCall) = callersProperty + // We do not handle indirect calls. + if (directCall) { + val definedCaller = caller.definedMethod + // Get the caller's tac to create the unbalanced return facts + val callerTac = propertyStore(definedCaller, TACAI.key) + callerTac match { + case FinalP(TheTACAI(tac)) ⇒ + addDependencyForUnbalancedReturn(caller, tac.pcToIndex(callPc), + newIn, tac)(state) + case _ ⇒ + val pendingTacCallSites = state.pendingTacCallSites + state.pendingTacDependees += definedCaller → callerTac + state.pendingTacCallSites = pendingTacCallSites.updated( + caller, + pendingTacCallSites.getOrElse(caller, Set.empty) + + state.cfg.startBlock + ) + } + } + + } + case _ ⇒ + throw new IllegalStateException( + "call graph mut be computed before the analysis starts" + ) + } + } + } + + /** + * Called, when the analysis found new output facts at a method's start block. + * A concrete analysis may overwrite this method to create additional facts, which will be added + * to the analysis' result. + * + * @param in The new output facts at the start node. + * @param source The entity, which is analyzed. + * @return Nothing by default. + */ + protected def createFactsAtStartNode( + in: Set[IFDSFact], + source: (DeclaredMethod, IFDSFact) + ): Set[IFDSFact] = + Set.empty + + /** + * If the node is a basic block, it will be returned. + * Otherwise, all predecessors of the node will be returned. + */ + override protected def skipCatchNode(node: CFGNode): Set[BasicBlock] = + if (node.isBasicBlock) Set(node.asBasicBlock) + else node.predecessors.map(_.asBasicBlock) + + /** + * The first index of a basic block is its end index. + */ + override protected def firstIndex(basicBlock: BasicBlock): Int = basicBlock.endPC + + /** + * The last index of a basic block ist its start index. + */ + override protected def lastIndex(basicBlock: BasicBlock): Int = basicBlock.startPC + + /** + * The next index against the control flow direction. + */ + override protected def nextIndex(index: Int): Int = index - 1 + + /** + * If the `node` is a basic block, its end statement will be returned. + * If it is a catch node, the first statement of its throwing block will be returned. + * If it is a synthetic start node, an artificial statement without code will be returned. + */ + override protected def firstStatement(node: CFGNode)(implicit state: State): Statement = { + if (node.isBasicBlock) { + val index = node.asBasicBlock.endPC + Statement(state.method, node, state.code(index), index, state.code, state.cfg) + } else if (node.isCatchNode) firstStatement(node.successors.head) + else if (node == SyntheticStartNode) Statement(state.method, node, null, 0, state.code, state.cfg) + else throw new IllegalArgumentException(s"Unknown node type: $node") + } + + /** + * The successor statements against the control flow direction. + */ + override protected def nextStatements(statement: Statement)(implicit state: State): Set[Statement] = { + val index = statement.index + val basicBlock = statement.node.asBasicBlock + if (index == 0) { + Set(firstStatement(SyntheticStartNode)) + } else if (index == basicBlock.startPC) + basicBlock.predecessors.map(firstStatement(_)) + else { + val nextIndex = index - 1 + Set(Statement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + statement.code, statement.cfg)) + } + } + + /** + * Determines the callees' exit statements and all successor statements in the control flow + * direction, which my be executed after them. Calls returnFlow on those pairs. + */ + override protected def callToStartFacts(call: Statement, callee: DeclaredMethod, + in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { + val definedCallee = callee.definedMethod + val ep = propertyStore(definedCallee, TACAI.key) + ep match { + case FinalP(TheTACAI(tac)) ⇒ + val cfg = tac.cfg + val successors = predecessorStatementsWithNode(call) + val flow = scala.collection.mutable.Set.empty[IFDSFact] + (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors) + .foreach { bb ⇒ + val exitPc = bb.asBasicBlock.endPC + val calleeStmts = tac.stmts + val exitStmt = calleeStmts(exitPc) + val exitStatement = Statement(definedCallee, cfg.bb(exitPc), exitStmt, + exitPc, calleeStmts, cfg) + for { + successor ← successors + if !AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW || + (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && + (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || + (successor._2.isCatchNode || successor._2.isAbnormalReturnExitNode) && + (exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID) + } { + numberOfCalls.returnFlow += 1 + sumOfInputfactsForCallbacks += in.size + flow ++= returnFlow(call, callee, exitStatement, successor._1, in) + } + } + flow.toSet + case _ ⇒ + val pendingTacCallSites = state.pendingTacCallSites + val index = call.index + state.pendingTacDependees += definedCallee → ep + state.pendingTacCallSites = pendingTacCallSites.updated( + callee, + pendingTacCallSites.getOrElse(callee, Set.empty) + call.cfg.bb(index) + ) + Set.empty + } + } + + /** + * Calls callFlow for the facts in exitFacts and adds them for each successor to the summary + * edges. exitFacts should at most contain the callee's entry point. + */ + override protected def addExitToReturnFacts( + summaryEdges: Map[Statement, Set[IFDSFact]], + successors: Set[Statement], call: Statement, + callee: DeclaredMethod, + exitFacts: Map[Statement, Set[IFDSFact]] + )(implicit state: State): Map[Statement, Set[IFDSFact]] = { + var result = summaryEdges + if (exitFacts.nonEmpty) { + val in = exitFacts.head._2 + numberOfCalls.callFlow += 1 + sumOfInputfactsForCallbacks += in.size + val exitToReturnFacts = callFlow(call, callee, in, state.source) + successors.foreach { successor ⇒ + val updatedValue = result.get(successor) match { + case Some(facts) ⇒ + if (facts.size >= exitToReturnFacts.size) facts ++ exitToReturnFacts + else exitToReturnFacts ++ facts + case None ⇒ exitToReturnFacts + } + result = result.updated(successor, updatedValue) + } + } + result + } + + /** + * If there is an unbalanced return fact for this call, it will be replaced by its inner fact. + */ + override protected def beforeHandleCall(call: Statement, in: Set[IFDSFact]): Set[IFDSFact] = + in.map { + case unbalancedFact: UnbalancedReturnFact[IFDSFact] if unbalancedFact.index == call.index ⇒ + unbalancedFact.innerFact + case fact ⇒ fact + } + + /** + * Checks for the analyzed entity, if an unbalanced return should be performed. + * + * @param source The analyzed entity. + * @return False, if no unbalanced return should be performed. + */ + protected def shouldPerformUnbalancedReturn(source: (DeclaredMethod, IFDSFact)): Boolean = + source._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]] || entryPoints.contains(source) + + /** + * Determines the predecessor statements, i.e. the successor statements in the control flow + * direction. They are mapped to the corresponding predecessor node. When determining the node, + * catch nodes are not skipped. + * + * @param statement The statement, for which the predecessor statements will be determined. + * + * @return A map, mapping from a predecessor statement to the corresponding node. + */ + private def predecessorStatementsWithNode(statement: Statement)(implicit state: State): Map[Statement, CFGNode] = { + val index = statement.index + val basicBlock = statement.node.asBasicBlock + if (index == basicBlock.endPC) + basicBlock.successors.iterator.map(successorNode ⇒ lastStatement(successorNode) → successorNode).toMap + else { + val nextIndex = index + 1 + Map(Statement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + statement.code, statement.cfg) → basicBlock) + } + } + + /** + * Determines the last statement of a `node`. + * If it is a basic block, its entry point will be returned. + * If it is a catch node, the last statement of its successor will be returned. + * If it is an exit node, an artificial statement without code will be returned. + * + * @param node The node, for which the last statement will be determined. + * + * @return The last statement of `node`. + */ + @tailrec private def lastStatement(node: CFGNode)(implicit state: State): Statement = { + if (node.isBasicBlock) { + val index = node.asBasicBlock.startPC + Statement(state.method, node, state.code(index), index, state.code, state.cfg) + } else if (node.isCatchNode) lastStatement(node.successors.head) + else if (node.isExitNode) Statement(state.method, node, null, 0, state.code, state.cfg) + else throw new IllegalArgumentException(s"Unknown node type: $node") + } + + /** + * Adds a dependency to an unbalanced return fact to the pending IFDS dependencies. + * + * @param caller The caller of the analyzed method. + * @param callIndex The index in the `caller`'s context, at which the analyzed method is called. + * @param in The facts, which hold at the entry point of the analyzed method. + * @param tac The `caller`'s tac. + */ + private def addDependencyForUnbalancedReturn(caller: DeclaredMethod, callIndex: Int, + in: Set[IFDSFact], + tac: TACode[TACMethodParameter, DUVar[ValueInformation]])(implicit state: State): Unit = { + val callerStmts = tac.stmts + val callerCfg = tac.cfg + val call = Statement(caller.definedMethod, callerCfg.bb(callIndex), + callerStmts(callIndex), callIndex, callerStmts, callerCfg) + unbalancedReturnFlow(in, call, caller, state.source).foreach { in ⇒ + val callerEntity = (caller, in) + /* + * Add the caller with the unbalanced return fact as a dependency to + * start its analysis. + */ + val callerAnalysisResult = propertyStore(callerEntity, propertyKey.key) + callerAnalysisResult match { + case FinalEP(_, _) ⇒ // Caller was already analyzed with the fact + case _ ⇒ + val pendingIfdsCallSites = state.pendingIfdsCallSites + state.pendingIfdsDependees += callerEntity → + callerAnalysisResult.asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + state.pendingIfdsCallSites += callerEntity → + (pendingIfdsCallSites.getOrElse(callerEntity, Set.empty) + + ((state.cfg.startBlock, 0))) + } + } + } +} + +/** + * A synthetic node, that is the predecessor of a method's entry point. + * This node is necessary, because we need to store the output facts of the method's entry point in + * a map, which maps from successor statements of a node to its output facts. + * The synthetic start node represents the successor of the entry point in this map. + */ +object SyntheticStartNode extends CFGNode { + + override def isBasicBlock: Boolean = false + + override def isStartOfSubroutine: Boolean = false + + override def isAbnormalReturnExitNode: Boolean = false + + override def isExitNode: Boolean = false + + override def isNormalReturnExitNode: Boolean = false + + override def isCatchNode: Boolean = false + + override def toHRR: Option[String] = Some("Synthetic Start Node") + + override def nodeId: Int = -1 +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala new file mode 100644 index 0000000000..1bad8d01a3 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala @@ -0,0 +1,222 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.cfg.BasicBlock +import org.opalj.br.cfg.CFG +import org.opalj.br.cfg.CFGNode +import org.opalj.br.DeclaredMethod +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.Return +import org.opalj.tac.ReturnValue +import org.opalj.tac.Stmt +import org.opalj.tac.TACStmts + +/** + * An IFDS analysis, which analyzes the code in the control flow direction. + * + * @author Mario Trageser + */ +abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends AbstractIFDSAnalysis[IFDSFact] { + + /** + * The analysis starts at the entry block. + */ + override protected def startBlocks( + sourceFact: IFDSFact, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Set[BasicBlock] = + Set(cfg.startBlock) + + /** + * Collects the output facts at the predecessors of the normal and abnormal return node. + */ + override protected def collectResult(implicit state: State): Map[Statement, Set[IFDSFact]] = { + mergeMaps( + resultOfExitNode(state.cfg.normalReturnNode), + resultOfExitNode(state.cfg.abnormalReturnNode) + ) + } + + /** + * Returns the next successor nodes of `basicBlock`. + * Catch nodes are skipped to their successor. + */ + override protected def nextNodes(basicBlock: BasicBlock): Set[CFGNode] = + basicBlock.successors.map { successor ⇒ + if (successor.isCatchNode) successor.successors.head + else successor + } + + /** + * The exit nodes are the last nodes. + */ + override protected def isLastNode(node: CFGNode): Boolean = node.isExitNode + + /** + * If the node is a basic block, it will be returned. + * Otherwise, its first successor will be returned. + */ + override protected def skipCatchNode(node: CFGNode): Set[BasicBlock] = + Set((if (node.isBasicBlock) node else node.successors.head).asBasicBlock) + + /** + * The first index of a basic block is its start index. + */ + override protected def firstIndex(basicBlock: BasicBlock): Int = basicBlock.startPC + + /** + * The last index of a basic block is its end index. + */ + override protected def lastIndex(basicBlock: BasicBlock): Int = basicBlock.endPC + + /** + * The next index in the direction of the control flow. + */ + override protected def nextIndex(index: Int): Int = index + 1 + + /** + * If the `node` is a basic block, its first statement will be returned. + * If it is a catch node, the first statement of its handler will be returned. + * If it is an exit node, an artificial statement without code will be returned. + */ + override protected def firstStatement(node: CFGNode)(implicit state: State): Statement = { + if (node.isBasicBlock) { + val index = node.asBasicBlock.startPC + Statement(state.method, node, state.code(index), index, state.code, state.cfg) + } else if (node.isCatchNode) firstStatement(node.successors.head) + else if (node.isExitNode) Statement(state.method, node, null, 0, state.code, state.cfg) + else throw new IllegalArgumentException(s"Unknown node type: $node") + } + + /** + * The successor statements in the direction of the control flow. + */ + override protected def nextStatements(statement: Statement)(implicit state: State): Set[Statement] = { + val index = statement.index + val basicBlock = statement.node.asBasicBlock + if (index == basicBlock.endPC) + basicBlock.successors.map(firstStatement(_)) + else { + val nextIndex = index + 1 + Set(Statement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + statement.code, statement.cfg)) + } + } + + /** + * Calls callFlow. + */ + override protected def callToStartFacts(call: Statement, callee: DeclaredMethod, + in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { + numberOfCalls.callFlow += 1 + sumOfInputfactsForCallbacks += in.size + callFlow(call, callee, in, state.source) + } + + /** + * Combines each normal exit node with each normal successor and each abnormal exit statement + * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. + */ + override protected def addExitToReturnFacts( + summaryEdges: Map[Statement, Set[IFDSFact]], + successors: Set[Statement], call: Statement, + callee: DeclaredMethod, + exitFacts: Map[Statement, Set[IFDSFact]] + )(implicit state: State): Map[Statement, Set[IFDSFact]] = { + // First process for normal returns, then abnormal returns. + var result = summaryEdges + if (AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { + val successors = nextStatementsWithNode(call) + for { + successor ← successors + exitStatement ← exitFacts.keys + if (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && + (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || + (successor._2.isCatchNode || successor._2.isAbnormalReturnExitNode) && + (exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID) + } result = addSummaryEdge(result, call, exitStatement, successor._1, callee, exitFacts) + } else { + val successors = nextStatements(call) + for { + successor ← successors + exitStatement ← exitFacts.keys + } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) + } + result + } + + /** + * Like nextStatements, but maps each successor statement to the corresponding successor node. + * When determining the successor node, catch nodes are not skipped. + */ + private def nextStatementsWithNode(statement: Statement)(implicit state: State): Map[Statement, CFGNode] = { + val index = statement.index + val basicBlock = statement.node.asBasicBlock + if (index == basicBlock.endPC) + basicBlock.successors.iterator + .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap + else { + val nextIndex = index + 1 + Map(Statement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + statement.code, statement.cfg) → basicBlock) + } + } + + /** + * Collects the facts valid at an exit node based on the current results. + * + * @param exit The exit node. + * @return A map, mapping from each predecessor of the `exit` node to the facts valid at the + * `exit` node under the assumption that the predecessor was executed before. + */ + private def resultOfExitNode(exit: CFGNode)( + implicit + state: State + ): Map[Statement, Set[IFDSFact]] = { + var result = Map.empty[Statement, Set[IFDSFact]] + exit.predecessors foreach { predecessor ⇒ + if (predecessor.isBasicBlock) { + val basicBlock = predecessor.asBasicBlock + val exitFacts = state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)) + if (exitFacts.isDefined) { + val lastIndex = basicBlock.endPC + val stmt = Statement(state.method, basicBlock, state.code(lastIndex), + lastIndex, state.code, state.cfg) + result += stmt → exitFacts.get + } + } + } + result + } + + /** + * Adds a summary edge for a call to a map representing summary edges. + * + * @param summaryEdges The current map representing the summary edges. + * Maps from successor statements to facts, which hold at their beginning. + * @param call The call, calling the `callee`. + * @param exitStatement The exit statement for the new summary edge. + * @param successor The successor statement of the call for the new summary edge. + * @param callee The callee, called by `call`. + * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly + * found exit facts. + * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. + */ + private def addSummaryEdge(summaryEdges: Map[Statement, Set[IFDSFact]], call: Statement, + exitStatement: Statement, successor: Statement, + callee: DeclaredMethod, + allNewExitFacts: Map[Statement, Set[IFDSFact]]): Map[Statement, Set[IFDSFact]] = { + val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) + numberOfCalls.returnFlow += 1 + sumOfInputfactsForCallbacks += in.size + val returned = returnFlow(call, callee, exitStatement, successor, in) + val newFacts = + if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { + val summaryForSuccessor = summaryEdges(successor) + if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned + else returned ++ summaryForSuccessor + } else returned + summaryEdges.updated(successor, newFacts) + } + +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala new file mode 100644 index 0000000000..425874e995 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -0,0 +1,425 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import scala.annotation.tailrec + +import java.io.File +import java.io.PrintWriter + +import org.opalj.collection.immutable.EmptyIntTrieSet +import org.opalj.fpcf.PropertyKey +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.PropertyBounds +import org.opalj.value.ValueInformation +import org.opalj.br.analyses.SomeProject +import org.opalj.br.DeclaredMethod +import org.opalj.br.ReferenceType +import org.opalj.br.ArrayType +import org.opalj.br.ClassFile +import org.opalj.br.FieldType +import org.opalj.br.Method +import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.ArrayLoad +import org.opalj.tac.ArrayStore +import org.opalj.tac.Assignment +import org.opalj.tac.DUVar +import org.opalj.tac.Expr +import org.opalj.tac.GetField +import org.opalj.tac.GetStatic +import org.opalj.tac.New +import org.opalj.tac.ReturnValue +import org.opalj.tac.Var +import org.opalj.tac.fpcf.properties.TACAI + +trait VTAFact extends SubsumableFact +case object VTANullFact extends VTAFact with SubsumableNullFact + +/** + * A possible run time type of a variable. + * + * @param definedBy The variable's definition site. + * @param t The variable's type. + * @param upperBound True, if the variable's type could also be every subtype of `t`. + */ +case class VariableType(definedBy: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { + + /** + * If this VariableType is an upper bound, it subsumes every subtype. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { + if (upperBound) other match { + case VariableType(definedByOther, tOther, _) if definedBy == definedByOther && t.isObjectType && tOther.isObjectType ⇒ + project.classHierarchy.isSubtypeOf(tOther.asObjectType, t.asObjectType) + case _ ⇒ false + } + else false + } +} + +/** + * A possible run time type of the receiver of a call. + * + * @param line The line of the call. + * @param t The callee's type. + * @param upperBound True, if the callee's type could also be every subtype of `t`. + */ +case class CalleeType(line: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { + + /** + * If this CalleeType is an upper bound, it subsumes every subtype. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { + if (upperBound) other match { + case CalleeType(lineOther, tOther, _) if line == lineOther && t.isObjectType && tOther.isObjectType ⇒ + tOther.asObjectType.isSubtypeOf(t.asObjectType)(project.classHierarchy) + case _ ⇒ false + } + else false + } +} + +/** + * A variable type analysis implemented as an IFDS analysis. + * In contrast to an ordinary variable type analysis, which also determines types of fields, + * this analysis only determines the types of local variables. + * The subsuming traint can be mixed in to enable subsuming. + * + * @param project The analyzed project. + * @author Mario Trageser + */ +class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) + extends ForwardIFDSAnalysis[VTAFact] { + + override val propertyKey: IFDSPropertyMetaInformation[VTAFact] = VTAResult + + /** + * The analysis starts with all public methods in java.lang or org.opalj. + */ + override def entryPoints: Seq[(DeclaredMethod, VTAFact)] = { + p.allProjectClassFiles.filter(classInsideAnalysisContext) + .flatMap(classFile ⇒ classFile.methods) + .filter(isEntryPoint).map(method ⇒ declaredMethods(method)) + .flatMap(entryPointsForMethod) + } + + override protected def nullFact: VTAFact = VTANullFact + + override protected def createPropertyValue(result: Map[Statement, Set[VTAFact]]): IFDSProperty[VTAFact] = + new VTAResult(result) + + /** + * If a new object is instantiated and assigned to a variable or array, a new ValueType will be + * created for the assignment's target. + * If there is an assignment of a variable or array element, a new VariableType will be + * created for the assignment's target with the source's type. + * If there is a field read, a new VariableType will be created with the field's declared type. + */ + override protected def normalFlow(statement: Statement, successor: Statement, + in: Set[VTAFact]): Set[VTAFact] = { + val stmt = statement.stmt + stmt.astID match { + case Assignment.ASTID ⇒ + // Add facts for the assigned variable. + in ++ newFacts(statement.method, statement.stmt.asAssignment.expr, + statement.index, in) + case ArrayStore.ASTID ⇒ + /* + * Add facts for the array store, like it was a variable assignment. + * By doing so, we only want to get the variable's type. + * Then, we change the definedBy-index to the one of the array and wrap the variable's + * type with an array type. + * Note, that an array type may have at most 255 dimensions. + */ + val flow = scala.collection.mutable.Set.empty[VTAFact] + flow ++= in + newFacts(statement.method, stmt.asArrayStore.value, statement.index, + in).foreach { + case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) ⇒ + stmt.asArrayStore.arrayRef.asVar.definedBy + .foreach(flow += VariableType(_, ArrayType(t), upperBound)) + case _ ⇒ // Nothing to do + } + flow.toSet + // If the statement is neither an assignment, nor an array store, we just propagate our facts. + case _ ⇒ in + } + } + + /** + * For each variable, which can be passed as an argument to the call, a new VariableType is + * created for the callee context. + */ + override protected def callFlow(call: Statement, callee: DeclaredMethod, in: Set[VTAFact], + source: (DeclaredMethod, VTAFact)): Set[VTAFact] = { + val callObject = asCall(call.stmt) + val allParams = callObject.allParams + // Iterate over all input facts and over all parameters of the call. + val flow = scala.collection.mutable.Set.empty[VTAFact] + in.foreach { + case VariableType(definedBy, t, upperBound) ⇒ + allParams.iterator.zipWithIndex.foreach { + /* + * We are only interested in a pair of a variable type and a parameter, if the + * variable and the parameter can refer to the same object. + */ + case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ + // If this is the case, create a new fact for the method's formal parameter. + flow += VariableType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(parameterIndex, callee.definedMethod.isStatic), + t, upperBound + ) + case _ ⇒ // Nothing to do + } + case _ ⇒ // Nothing to do + } + flow.toSet + } + + /** + * If the call is an instance call, new CalleeTypes will be created for the call, one for each + * VariableType, which could be the call's target. + */ + override protected def callToReturnFlow(call: Statement, successor: Statement, + in: Set[VTAFact], + source: (DeclaredMethod, VTAFact)): Set[VTAFact] = { + // Check, to which variables the callee may refer + val calleeDefinitionSites = asCall(call.stmt).receiverOption + .map(callee ⇒ callee.asVar.definedBy).getOrElse(EmptyIntTrieSet) + val calleeTypeFacts = in.collect { + // If we know the variable's type, we also know on which type the call is performed. + case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) ⇒ + CalleeType(call.index, t, upperBound) + } + if (in.size >= calleeTypeFacts.size) in ++ calleeTypeFacts + else calleeTypeFacts ++ in + } + + /** + * If the call returns a value which is assigned to a variable, a new VariableType will be + * created in the caller context with the returned variable's type. + */ + override protected def returnFlow(call: Statement, callee: DeclaredMethod, exit: Statement, + successor: Statement, in: Set[VTAFact]): Set[VTAFact] = + // We only create a new fact, if the call returns a value, which is assigned to a variable. + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValue = exit.stmt.asReturnValue.expr.asVar + in.collect { + // If we know the type of the return value, we create a fact for the assigned variable. + case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) ⇒ + VariableType(call.index, t, upperBound) + } + } else Set.empty + + /** + * If a method outside of the analysis context is called, we assume that it returns every + * possible runtime type matching the compile time type. + */ + override protected def callOutsideOfAnalysisContext(statement: Statement, callee: DeclaredMethod, + successor: Statement, + in: Set[VTAFact]): Set[VTAFact] = { + val returnType = callee.descriptor.returnType + if (statement.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { + Set(VariableType(statement.index, returnType.asReferenceType, upperBound = true)) + } else Set.empty + } + + /** + * Only methods in java.lang and org.opalj are inside the analysis context. + * + * @param callee The callee. + * @return True, if the callee is inside the analysis context. + */ + override protected def insideAnalysisContext(callee: DeclaredMethod): Boolean = + classInsideAnalysisContext(callee.definedMethod.classFile) && + super.insideAnalysisContext(callee) + + /** + * Checks, if a class is inside the analysis context. + * By default, that are the packages java.lang and org.opalj. + * + * @param classFile The class, which is checked. + * @return True, if the class is inside the analysis context. + */ + private def classInsideAnalysisContext(classFile: ClassFile): Boolean = { + val fqn = classFile.fqn + fqn.startsWith("java/lang") || fqn.startsWith("org/opalj/fpcf/fixtures/vta") + } + + /** + * Checks, if a method is an entry point of this analysis. + * + * @param method The method to be checked. + * @return True, if this method is an entry point of the analysis. + */ + private def isEntryPoint(method: Method): Boolean = { + val declaredMethod = declaredMethods(method) + method.body.isDefined && canBeCalledFromOutside(declaredMethod) + } + + /** + * For an entry point method, this method computes all pairs (`method`, inputFact) where + * inputFact is a VariableType for one of the method's parameter with its compile time type as + * an upper bound. + * + * @param method The entry point method. + * + * @return All pairs (`method`, inputFact) where inputFact is a VariableType for one of the + * method's parameter with its compile time type as an upper bound. + */ + private def entryPointsForMethod(method: DeclaredMethod): Seq[(DeclaredMethod, VTAFact)] = { + // Iterate over all parameters, which have a reference type. + (method.descriptor.parameterTypes.zipWithIndex.collect { + case (t, index) if t.isReferenceType ⇒ + /* + * Create a fact for the parameter, which says, that the parameter may have any + * subtype of its compile time type. + */ + VariableType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, method.definedMethod.isStatic), + t.asReferenceType, upperBound = true + ) + /* + * In IFDS problems, we must always also analyze the null fact, because it creates the facts, + * which hold independently of other source facts. + * Map the input facts, in which we are interested, to a pair of the method and the fact. + */ + } :+ VTANullFact).map(fact ⇒ (method, fact)) + } + + /** + * When `normalFlow` reaches an assignment or array store, this method computes the new facts + * created by the statement. + * + * @param expression The source expression of the assignment or array store. + * @param statementIndex The statement's index. + * @param in The facts, which hold before the statement. + * @return The new facts created by the statement. + */ + private def newFacts(method: Method, expression: Expr[DUVar[ValueInformation]], + statementIndex: Int, + in: Set[VTAFact]): Iterator[VariableType] = expression.astID match { + case New.ASTID ⇒ in.iterator.collect { + // When a constructor is called, we always know the exact type. + case VTANullFact ⇒ + VariableType(statementIndex, expression.asNew.tpe, upperBound = false) + } + case Var.ASTID ⇒ in.iterator.collect { + // When we know the source type, we also know the type of the assigned variable. + case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ + VariableType(statementIndex, t, upperBound) + } + case ArrayLoad.ASTID ⇒ in.iterator.collect { + // When we know the array's type, we also know the type of the loaded element. + case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && + expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ + VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound) + } + case GetField.ASTID | GetStatic.ASTID ⇒ + val t = expression.asFieldRead.declaredFieldType + /* + * We do not track field types. So we must assume, that it contains any subtype of its + * compile time type. + */ + if (t.isReferenceType) + Iterator(VariableType(statementIndex, t.asReferenceType, upperBound = true)) + else Iterator.empty + case _ ⇒ Iterator.empty + } + + /** + * Checks, if some type is an array type containing an object type. + * + * @param t The type to be checked. + * @param includeObjectType If true, this method also returns true if `t` is an object type + * itself. + * + * @return True, if `t` is an array type of an object type. + */ + @tailrec private def isArrayOfObjectType(t: FieldType, includeObjectType: Boolean = false): Boolean = { + if (t.isArrayType) isArrayOfObjectType(t.asArrayType.elementType, includeObjectType = true) + else if (t.isObjectType && includeObjectType) true + else false + } +} + +object IFDSBasedVariableTypeAnalysis extends IFDSAnalysis[VTAFact] { + + var SUBSUMING: Boolean = true + + override val uses: Set[PropertyBounds] = + Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) + + override def init(p: SomeProject, ps: PropertyStore): IFDSBasedVariableTypeAnalysis = { + if (SUBSUMING) new IFDSBasedVariableTypeAnalysis()(p) with Subsuming[VTAFact] + else new IFDSBasedVariableTypeAnalysis()(p) + } + + override def property: IFDSPropertyMetaInformation[VTAFact] = VTAResult +} + +/** + * The IFDSProperty for this analysis. + */ +case class VTAResult(flows: Map[Statement, Set[VTAFact]]) extends IFDSProperty[VTAFact] { + + override type Self = VTAResult + + override def key: PropertyKey[VTAResult] = VTAResult.key +} + +object VTAResult extends IFDSPropertyMetaInformation[VTAFact] { + + override type Self = VTAResult + + val key: PropertyKey[VTAResult] = PropertyKey.create("VTA", new VTAResult(Map.empty)) +} + +class IFDSBasedVariableTypeAnalysisRunner extends AbsractIFDSAnalysisRunner { + + override def analysisClass: IFDSBasedVariableTypeAnalysis.type = IFDSBasedVariableTypeAnalysis + + override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = {} + + override protected def additionalEvaluationResult(analysis: AbstractIFDSAnalysis[_]): Option[Object] = + analysis match { + case subsuming: Subsuming[_] ⇒ Some(subsuming.numberOfSubsumptions) + case _ ⇒ None + } + + override protected def writeAdditionalEvaluationResultsToFile( + writer: PrintWriter, + additionalEvaluationResults: Seq[Object] + ): Unit = { + val numberOfSubsumptions = additionalEvaluationResults.map(_.asInstanceOf[NumberOfSubsumptions]) + val length = additionalEvaluationResults.length + val tries = numberOfSubsumptions.map(_.triesToSubsume).sum / length + val successes = numberOfSubsumptions.map(_.successfulSubsumes).sum / length + writer.println(s"Average tries to subsume: $tries") + writer.println(s"Average successful subsumes: $successes") + } +} + +object IFDSBasedVariableTypeAnalysisRunner { + def main(args: Array[String]): Unit = { + if (args.contains("--help")) { + println("Potential parameters:") + println(" -seq (to use the SequentialPropertyStore)") + println(" -l2 (to use the l2 domain instead of the default l1 domain)") + println(" -delay (for a three seconds delay before the taint flow analysis is started)") + println(" -debug (for debugging mode in the property store)") + println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") + println(" -f (Stores the average runtime to this file)") + } else { + val fileIndex = args.indexOf("-f") + new IFDSBasedVariableTypeAnalysisRunner().run( + args.contains("-debug"), + args.contains("-l2"), + args.contains("-delay"), + args.contains("-evalSchedulingStrategies"), + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala new file mode 100644 index 0000000000..9650d12f28 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala @@ -0,0 +1,105 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.analyses.SomeProject + +/** + * The supertype of all IFDS facts, which can subsume another fact. + */ +trait SubsumableFact extends AbstractIFDSFact { + + /** + * Checks, if this fact subsumes an `other` fact. + * + * @param other The other fact. + * @param project The analyzed project. + * @return True, if this fact subsumes the `other`fact + */ + def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean +} + +/** + * The null fact for subsumable facts. + */ +trait SubsumableNullFact extends SubsumableFact with AbstractIFDSNullFact { + + /** + * The null fact cannot subsume another fact. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = false +} + +/** + * Defines functions, which can be overwritten to implement subsuming. + * + * @author Mario Trageser + */ +trait Subsumable[IFDSFact <: AbstractIFDSFact] { + + /** + * A subclass can override this method to filter the `facts` in some set, which are not subsumed + * by another fact in the set. + * + * + * @param facts The set of facts. + * @param project The project, which is analyzed. + * @return The facts, which are not subsumed by any other fact in `facts`. + * By default, `facts` is returned without removing anything. + */ + protected def subsume[T <: IFDSFact](facts: Set[T], project: SomeProject): Set[T] = facts + + /** + * Checks, if any fact from some set is not equal to or subsumed by any fact in another set. + * A subclass implementing subsuming must overwrite this method to consider subsuming. + * + * @param newFacts The facts, which were found. Not empty. + * @param oldFacts The facts, which are already known. Not empty. + * @param project The project, which is analyzed. + * @return True, if any fact in `newFacts`, is not equal to or subsumed by any fact in + * `oldFacts`. + */ + protected def containsNewInformation[T <: IFDSFact]( + newFacts: Set[T], + oldFacts: Set[T], + project: SomeProject + ): Boolean = + newFacts.exists(newFact ⇒ !oldFacts.contains(newFact)) + + /** + * Filters the new information from a new set of exit facts given the already known exit facts. + * A subclass implementing subsuming must overwrite this method to consider subsuming. + * + * @param newExitFacts The new exit facts. + * @param oldExitFacts The old exit facts. + * @param project The project, which is analyzed. + * @return A map, containing the keys of `newExitFacts`. + * Facts, which are equal to or subsumed by any fact for the same statement in + * `oldExitFacts` are not present. + */ + protected def filterNewInformation[T <: IFDSFact]( + newExitFacts: Map[Statement, Set[T]], + oldExitFacts: Map[Statement, Set[T]], + project: SomeProject + ): Map[Statement, Set[T]] = { + var result = newExitFacts + for ((key, values) ← oldExitFacts) { + result = result.updated(key, result(key) -- values) + } + result + } + + /** + * Filters the `facts` from some set, which are not equal to or subsumed by any fact in an + * `other` set. + * A subclass implementing subsuming must overwrite this method to consider subsuming. + * + * @param facts The set, from which facts are filtered. + * @param otherFacts The set, which may subsume facts from `facts`. + * @param project The project, which is analyzed. + * @return The facts from `facts`, which are not equal to or subsumed by any fact in an + * `otherFacts`. + */ + protected def notSubsumedBy[T <: IFDSFact](facts: Set[T], otherFacts: Set[T], + project: SomeProject): Set[T] = + facts -- otherFacts +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala new file mode 100644 index 0000000000..86c6f6866b --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala @@ -0,0 +1,90 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.analyses.SomeProject + +/** + * An IFDS analysis, which implements subsuming. + * + * @author Mario Trageser + */ +trait Subsuming[IFDSFact <: SubsumableFact] extends Subsumable[IFDSFact] { + + val numberOfSubsumptions = new NumberOfSubsumptions + + /** + * Considers subsuming. + */ + override protected def subsume[T <: IFDSFact](facts: Set[T], project: SomeProject): Set[T] = { + val result = facts.foldLeft(facts) { + (result, fact) ⇒ + if (facts.exists(other ⇒ other != fact && other.subsumes(fact, project))) result - fact + else result + } + numberOfSubsumptions.triesToSubsume += 1 + if (result.size != facts.size) numberOfSubsumptions.successfulSubsumes += 1 + result + } + + /** + * Considers subsuming. + */ + override protected def containsNewInformation[T <: IFDSFact](newFacts: Set[T], oldFacts: Set[T], + project: SomeProject): Boolean = + newFacts.exists { + /* + * In most cases, the fact will be contained in the old facts. + * This is why we first do the contains check before linearly iterating over the old facts. + */ + fact ⇒ !(oldFacts.contains(fact) || oldFacts.exists(_.subsumes(fact, project))) + } + + /** + * Considers subsuming. + */ + override protected def filterNewInformation[T <: IFDSFact]( + newExitFacts: Map[Statement, Set[T]], + oldExitFacts: Map[Statement, Set[T]], project: SomeProject + ): Map[Statement, Set[T]] = + newExitFacts.keys.map { + statement ⇒ + val old = oldExitFacts.get(statement) + val newFacts = newExitFacts(statement) + val newInformation = + if (old.isDefined && old.get.nonEmpty) notSubsumedBy(newFacts, old.get, project) + else newFacts + statement → newInformation + }.toMap + + /** + * Considers subsuming. + */ + override protected def notSubsumedBy[T <: IFDSFact](facts: Set[T], otherFacts: Set[T], + project: SomeProject): Set[T] = { + val result = facts.foldLeft(facts) { + (result, fact) ⇒ + if (otherFacts.contains(fact) || otherFacts.exists(_.subsumes(fact, project))) + result - fact + else result + } + numberOfSubsumptions.triesToSubsume += 1 + if (result.size != facts.size) numberOfSubsumptions.successfulSubsumes += 1 + result + } +} + +/** + * Counts, how often subsume was called and how often it eliminated a fact. + */ +class NumberOfSubsumptions { + + /** + * The number of subsume and notSubsumedBy calls. + */ + var triesToSubsume = 0 + + /** + * The number of subsume and notSubsumedBy calls, which eliminated a fact. + */ + var successfulSubsumes = 0 +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala new file mode 100644 index 0000000000..4dc8bda7e9 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala @@ -0,0 +1,390 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.SomeProject +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.tac._ +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.BackwardIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact +import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation + +/** + * The unbalanced return fact of this analysis. + * + * @param index The index, at which the analyzed method is called by some caller. + * @param innerFact The fact, which will hold in the caller context after the call. + * @param callChain The current call chain from the sink. + */ +case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Seq[Method]) + extends UnbalancedReturnFact[Fact] with Fact + +/** + * An analysis that checks, if the return value of a `source` method can flow to the parameter of a + * `sink` method. + * + * @param project The project, that is analyzed + * @author Mario Trageser + */ +abstract class BackwardTaintAnalysis(implicit val project: SomeProject) + extends BackwardIFDSAnalysis[Fact, UnbalancedTaintFact] with TaintAnalysis { + + override val propertyKey: IFDSPropertyMetaInformation[Fact] = Taint + + /** + * Called in callToReturnFlow. Creates a FlowFact if necessary. + * + * @param call The call. + * @param in The facts, which hold before the call. + * @param source The entity, which is analyzed. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFactAtCall(call: Statement, in: Set[Fact], + source: (DeclaredMethod, Fact)): Option[FlowFact] + + /** + * Called, when a FlowFact holds at the start node of a callee. Creates a FlowFact in the caller + * context if necessary. + * + * @param calleeFact The FlowFact, which holds at the start node of the callee. + * @param source The analyzed entity. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def applyFlowFactFromCallee( + calleeFact: FlowFact, + source: (DeclaredMethod, Fact) + ): Option[FlowFact] + + /** + * Called, when new FlowFacts are found at the beginning of a method. + * Creates a FlowFact if necessary. + * + * @param in The newly found facts. + * @param source The entity, which is analyzed. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFactAtBeginningOfMethod( + in: Set[Fact], + source: (DeclaredMethod, Fact) + ): Option[FlowFact] + + override protected def nullFact: Fact = NullFact + + override protected def createPropertyValue(result: Map[Statement, Set[Fact]]): IFDSProperty[Fact] = + new Taint(result) + + /** + * If a tainted variable gets assigned a value, this value will be tainted. + */ + override protected def normalFlow(statement: Statement, successor: Statement, + in: Set[Fact]): Set[Fact] = { + val stmt = statement.stmt + stmt.astID match { + case Assignment.ASTID if isTainted(statement.index, in) ⇒ + in ++ createNewTaints(stmt.asAssignment.expr, statement) + case ArrayStore.ASTID ⇒ + val arrayStore = stmt.asArrayStore + val arrayIndex = TaintAnalysis.getIntConstant(arrayStore.index, statement.code) + val arrayDefinedBy = arrayStore.arrayRef.asVar.definedBy + val result = in ++ in.collect { + // In this case, we taint the whole array. + case Variable(index) if arrayDefinedBy.contains(index) ⇒ + createNewTaints(arrayStore.value, statement) + // In this case, we taint exactly the stored element. + case ArrayElement(index, taintedElement) if arrayDefinedBy.contains(index) && + (arrayIndex.isEmpty || arrayIndex.get == taintedElement) ⇒ + createNewTaints(arrayStore.value, statement) + }.flatten + if (arrayDefinedBy.size == 1 && arrayIndex.isDefined) + result - ArrayElement(arrayDefinedBy.head, arrayIndex.get) + else result + case PutField.ASTID ⇒ + val putField = stmt.asPutField + val objectDefinedBy = putField.objRef.asVar.definedBy + if (in.exists { + case InstanceField(index, declaringClass, name) if objectDefinedBy.contains(index) && + putField.declaringClass == declaringClass && putField.name == name ⇒ + true + case _ ⇒ false + }) in ++ createNewTaints(putField.value, statement) + else in + case PutStatic.ASTID ⇒ + val putStatic = stmt.asPutStatic + if (in.exists { + case StaticField(declaringClass, name) if putStatic.declaringClass == declaringClass && putStatic.name == name ⇒ + true + case _ ⇒ false + }) in ++ createNewTaints(putStatic.value, statement) + else in + case _ ⇒ in + } + } + + /** + * Taints the actual parameters in the caller context if the formal parameters in the callee + * context were tainted. + * Does not taint anything, if the sanitize method was called. + */ + override protected def callFlow(call: Statement, callee: DeclaredMethod, + in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = + if (sanitizesReturnValue(callee)) Set.empty + else taintActualsIfFormalsTainted(callee, call, in, source) + + /** + * If the returned value on in the caller context is tainted, the returned values in the callee + * context will be tainted. If an actual pass-by-reference-parameter in the caller context is + * tainted, the formal parameter in the callee context will be tainted. + */ + override protected def returnFlow(call: Statement, callee: DeclaredMethod, exit: Statement, + successor: Statement, in: Set[Fact]): Set[Fact] = { + val callObject = asCall(call.stmt) + val staticCall = callee.definedMethod.isStatic + val flow = collection.mutable.Set.empty[Fact] + if (call.stmt.astID == Assignment.ASTID && exit.stmt.astID == ReturnValue.ASTID) + in.foreach { + case Variable(index) if index == call.index ⇒ + flow ++= createNewTaints(exit.stmt.asReturnValue.expr, exit) + case ArrayElement(index, taintedElement) if index == call.index ⇒ + flow ++= createNewArrayElementTaints(exit.stmt.asReturnValue.expr, taintedElement, + call) + case InstanceField(index, declaringClass, name) if index == call.index ⇒ + flow ++= createNewInstanceFieldTaints(exit.stmt.asReturnValue.expr, declaringClass, + name, call) + case _ ⇒ // Nothing to do + } + val thisOffset = if (callee.definedMethod.isStatic) 0 else 1 + callObject.allParams.iterator.zipWithIndex + .filter(pair ⇒ (pair._2 == 0 && !staticCall) || + callObject.descriptor.parameterTypes(pair._2 - thisOffset).isReferenceType) + .foreach { pair ⇒ + val param = pair._1.asVar + val paramIndex = pair._2 + in.foreach { + case Variable(index) if param.definedBy.contains(index) ⇒ + flow += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall)) + case ArrayElement(index, taintedElement) if param.definedBy.contains(index) ⇒ + flow += ArrayElement( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement + ) + case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) ⇒ + flow += InstanceField( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), + declaringClass, name + ) + case staticField: StaticField ⇒ flow += staticField + case _ ⇒ // Nothing to do + } + } + flow.toSet + } + + /** + * Adds a FlowFact, if `createFlowFactAtCall` creates one. + * Removes taints according to `sanitizeParamters`. + */ + override protected def callToReturnFlow(call: Statement, successor: Statement, + in: Set[Fact], + source: (DeclaredMethod, Fact)): Set[Fact] = { + val flowFact = createFlowFactAtCall(call, in, source) + val result = in -- sanitizeParamters(call, in) + if (flowFact.isDefined) result + flowFact.get + else result + } + + /** + * If the returned value is tainted, all actual parameters will be tainted. + */ + override protected def callOutsideOfAnalysisContext(call: Statement, callee: DeclaredMethod, + successor: Statement, + in: Set[Fact]): Set[Fact] = { + val callStatement = asCall(call.stmt) + in ++ in.collect { + case Variable(index) if index == call.index ⇒ + callStatement.allParams.flatMap(createNewTaints(_, call)) + case ArrayElement(index, _) if index == call.index ⇒ + callStatement.allParams.flatMap(createNewTaints(_, call)) + case InstanceField(index, _, _) if index == call.index ⇒ + callStatement.allParams.flatMap(createNewTaints(_, call)) + }.flatten + } + + /** + * Creates an UnbalancedTaintFact for each actual parameter on the caller side, of the formal + * parameter of this method is tainted. + */ + override protected def unbalancedReturnFlow(facts: Set[Fact], call: Statement, + caller: DeclaredMethod, + source: (DeclaredMethod, Fact)): Set[UnbalancedTaintFact] = + taintActualsIfFormalsTainted(source._1, call, facts, source, isCallFlow = false) + .map(UnbalancedTaintFact(call.index, _, currentCallChain(source))) + // Avoid infinite loops. + .filter(unbalancedTaintFact ⇒ !containsHeadTwice(unbalancedTaintFact.callChain)) + + /** + * Propagates the call to createFlowFactAtBeginningOfMethod. + */ + override protected def createFactsAtStartNode( + in: Set[Fact], + source: (DeclaredMethod, Fact) + ): Set[Fact] = { + val flowFact = createFlowFactAtBeginningOfMethod(in, source) + if (flowFact.isDefined) Set(flowFact.get) + else Set.empty + } + + /** + * Gets the current call chain. + * If the input fact for the analysis is an UnbalancedTaintFact, this is the current method + * concatenated with the fact's call chain. + * Otherwise, it is just this method. + * + * @param source The entity, which is analyzed. + * @return The current call chain. + */ + protected def currentCallChain(source: (DeclaredMethod, Fact)): Seq[Method] = { + val method = source._1.definedMethod + val sourceFact = source._2 + sourceFact match { + case fact: UnbalancedTaintFact ⇒ method +: fact.callChain + case _ ⇒ Seq(method) + } + } + + /** + * Checks, if the head of a sequence is contained in its tail. + * + * @param callChain The sequence. + * @return True, if the head of `callChain` is contained in its tail. + */ + protected def containsHeadTwice(callChain: Seq[Method]): Boolean = + callChain.tail.contains(callChain.head) + + /** + * Checks, if some variable or array element is tainted. + * + * @param index The index of the variable or array element. + * @param in The current data flow facts. + * @param taintedElement If present, the taint of a specific array element will be checked. + * @return True, if the variable or array element is tainted. + */ + private def isTainted(index: Int, in: Set[Fact], taintedElement: Option[Int] = None): Boolean = in.exists { + case Variable(variableIndex) ⇒ variableIndex == index + case ArrayElement(variableIndex, element) ⇒ + variableIndex == index && (taintedElement.isEmpty || taintedElement.get == element) + case _ ⇒ false + } + + /** + * Taints all variables in an `expression`. + * + * @param expression The expression. + * @param statement The statement, which contains the expression. + * @return The new taints. + */ + private def createNewTaints(expression: Expr[V], statement: Statement): Set[Fact] = + expression.astID match { + case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) + case ArrayLoad.ASTID ⇒ + val arrayLoad = expression.asArrayLoad + val arrayIndex = TaintAnalysis.getIntConstant(expression.asArrayLoad.index, statement.code) + val arrayDefinedBy = arrayLoad.arrayRef.asVar.definedBy + if (arrayIndex.isDefined) arrayDefinedBy.map(ArrayElement(_, arrayIndex.get)) + else arrayDefinedBy.map(Variable) + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | + PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + acc ++ createNewTaints(expression.subExpr(subExpr), statement)) + case GetField.ASTID ⇒ + val getField = expression.asGetField + getField.objRef.asVar.definedBy + .map(InstanceField(_, getField.declaringClass, getField.name)) + /*case GetStatic.ASTID ⇒ + val getStatic = expression.asGetStatic + Set(StaticField(getStatic.declaringClass, getStatic.name))*/ + case _ ⇒ Set.empty + } + + /** + * Creates some taints for an array, but only taints some element. + * + * @param expression The expression, referring to the array. + * @param taintedElement The array element, which will be tainted. + * @param statement The statement, containing the expression. + * @return An ArrayElement fact for the expression and the tainted element. + */ + private def createNewArrayElementTaints(expression: Expr[V], taintedElement: Int, + statement: Statement): Set[Fact] = + createNewTaints(expression, statement).map { + case Variable(variableIndex) ⇒ ArrayElement(variableIndex, taintedElement) + // We do not nest taints. Instead, we taint the whole inner array. + case ArrayElement(variableIndex, _) ⇒ ArrayElement(variableIndex, taintedElement) + case InstanceField(variableIndex, _, _) ⇒ ArrayElement(variableIndex, taintedElement) + } + + private def createNewInstanceFieldTaints(expression: Expr[V], declaringClass: ObjectType, + name: String, statement: Statement): Set[Fact] = + createNewTaints(expression, statement).map { + case Variable(variableIndex) ⇒ InstanceField(variableIndex, declaringClass, name) + // We do not nest taints. Instead, taint the whole field. + case ArrayElement(variableIndex, _) ⇒ InstanceField(variableIndex, declaringClass, name) + case InstanceField(variableIndex, _, _) ⇒ + InstanceField(variableIndex, declaringClass, name) + } + + /** + * Taints actual parameters on in the caller context, if the corresponding formal parameter in + * the `callee` context is tainted. + * Additionally propagates taints of static fields. + * + * @param callee The callee. + * @param call The call, calling the `callee`. + * @param calleeFacts The facts in the `callee` context. + * @param source The entity, which is analyzed. + * @param isCallFlow True, if this method is called in callFlow. In this case, FlowFacts will + * be propagated to the caller context. + * @return The data flow facts in the caller context. + */ + private def taintActualsIfFormalsTainted( + callee: DeclaredMethod, + call: Statement, + calleeFacts: Set[Fact], + source: (DeclaredMethod, Fact), + isCallFlow: Boolean = true + ): Set[Fact] = { + val stmt = call.stmt + val callStatement = asCall(stmt) + val staticCall = callee.definedMethod.isStatic + val thisOffset = if (staticCall) 0 else 1 + val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) + .map(index ⇒ AbstractIFDSAnalysis.switchParamAndVariableIndex(index + thisOffset, staticCall)) + val facts = scala.collection.mutable.Set.empty[Fact] + calleeFacts.foreach { + case Variable(index) if formalParameterIndices.contains(index) ⇒ + facts ++= createNewTaints( + callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), call + ) + case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) ⇒ + facts ++= createNewArrayElementTaints( + callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + taintedElement, call + ) + case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) ⇒ + facts ++= createNewInstanceFieldTaints( + callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + declaringClass, name, call + ) + case staticField: StaticField ⇒ facts += staticField + // If the source was reached in a callee, create a flow fact from this method to the sink. + case calleeFact: FlowFact if isCallFlow ⇒ + val callerFact = applyFlowFactFromCallee(calleeFact, source) + if (callerFact.isDefined) facts += callerFact.get + case _ ⇒ // Nothing to do + } + facts.toSet + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala new file mode 100644 index 0000000000..85c6df547b --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala @@ -0,0 +1,372 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.fpcf.PropertyKey +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.SomeProject +import org.opalj.tac._ +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.ForwardIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation + +/** + * An analysis that checks, if the return value of a `source` method can flow to the parameter of a + * `sink` method. + * + * @param project The project, that is analyzed + * @author Mario Trageser + */ +abstract class ForwardTaintAnalysis(implicit val project: SomeProject) + extends ForwardIFDSAnalysis[Fact] with TaintAnalysis { + + override val propertyKey: IFDSPropertyMetaInformation[Fact] = Taint + + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a vairbale. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + protected def createTaints(callee: DeclaredMethod, call: Statement): Set[Fact] + + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFact(callee: DeclaredMethod, call: Statement, + in: Set[Fact]): Option[FlowFact] + + override protected def nullFact: Fact = NullFact + + override protected def createPropertyValue(result: Map[Statement, Set[Fact]]): IFDSProperty[Fact] = + new Taint(result) + + /** + * If a variable gets assigned a tainted value, the variable will be tainted. + */ + override protected def normalFlow(statement: Statement, successor: Statement, + in: Set[Fact]): Set[Fact] = + statement.stmt.astID match { + case Assignment.ASTID ⇒ + in ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) + case ArrayStore.ASTID ⇒ + val store = statement.stmt.asArrayStore + val definedBy = store.arrayRef.asVar.definedBy + val arrayIndex = TaintAnalysis.getIntConstant(store.index, statement.code) + if (isTainted(store.value, in)) { + if (arrayIndex.isDefined) + // Taint a known array index + definedBy.foldLeft(in) { (c, n) ⇒ + c + ArrayElement(n, arrayIndex.get) + } + else + // Taint the whole array if the index is unknown + definedBy.foldLeft(in) { (c, n) ⇒ + c + Variable(n) + } + } else if (arrayIndex.isDefined && definedBy.size == 1) + // Untaint if possible + in - ArrayElement(definedBy.head, arrayIndex.get) + else in + case PutField.ASTID ⇒ + val put = statement.stmt.asPutField + val definedBy = put.objRef.asVar.definedBy + if (isTainted(put.value, in)) + definedBy.foldLeft(in) { (in, defSite) ⇒ + in + InstanceField(defSite, put.declaringClass, put.name) + } + else + in + case PutStatic.ASTID ⇒ + val put = statement.stmt.asPutStatic + if (isTainted(put.value, in)) + in + StaticField(put.declaringClass, put.name) + else + in + case _ ⇒ in + } + + /** + * Propagates tainted parameters to the callee. If a call to the sink method with a tainted + * parameter is detected, no call-to-start + * edges will be created. + */ + override protected def callFlow(call: Statement, callee: DeclaredMethod, + in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = { + val callObject = asCall(call.stmt) + val allParams = callObject.allParams + var facts = Set.empty[Fact] + + if (relevantCallee(callee)) { + val allParamsWithIndices = allParams.zipWithIndex + in.foreach { + // Taint formal parameter if actual parameter is tainted + case Variable(index) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( + paramIndex, + callee.definedMethod.isStatic + )) + case _ ⇒ // Nothing to do + } + + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += ArrayElement( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), + taintedIndex + ) + case _ ⇒ // Nothing to do + } + + case InstanceField(index, declClass, taintedField) ⇒ + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.foreach { + case (param, pIndex) if param.asVar.definedBy.contains(index) && + (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || + classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ + facts += InstanceField( + AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), + declClass, taintedField + ) + case _ ⇒ // Nothing to do + } + + case sf: StaticField ⇒ facts += sf + + case _ ⇒ // Nothing to do + } + } + + facts + } + + /** + * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. + * If the callee's return value was tainted and it is assigned to a variable in the callee, the + * variable will be tainted. + * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds + * at this method. + * Creates new taints and FlowFacts, if necessary. + * If the sanitize method was called, nothing will be tainted. + */ + override protected def returnFlow(call: Statement, callee: DeclaredMethod, exit: Statement, + successor: Statement, in: Set[Fact]): Set[Fact] = { + + /** + * Checks whether the callee's formal parameter is of a reference type. + */ + def isRefTypeParam(index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (callee.definedMethod.isStatic) 0 else 1 + callee.descriptor.parameterType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + - parameterOffset + ).isReferenceType + } + + if (sanitizesReturnValue(callee)) return Set.empty + val callStatement = asCall(call.stmt) + val allParams = callStatement.allParams + var flows: Set[Fact] = Set.empty + in.foreach { + // Taint actual parameter if formal parameter is tainted + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(Variable) + + // Taint element of actual parameter if element of formal parameter is tainted + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) + + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + // Taint field of actual parameter if field of formal parameter is tainted + val param = + allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) + param.asVar.definedBy.foreach { defSite ⇒ + flows += InstanceField(defSite, declClass, taintedField) + } + + case sf: StaticField ⇒ flows += sf + + // Track the call chain to the sink back + case FlowFact(flow) if !flow.contains(call.method) ⇒ + flows += FlowFact(call.method +: flow) + case _ ⇒ + } + + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + in.foreach { + case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + flows += Variable(call.index) + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + flows += ArrayElement(call.index, taintedIndex) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + flows += InstanceField(call.index, declClass, taintedField) + case NullFact ⇒ + val taints = createTaints(callee, call) + if (taints.nonEmpty) flows ++= taints + case _ ⇒ // Nothing to do + } + } + val flowFact = createFlowFact(callee, call, in) + if (flowFact.isDefined) flows += flowFact.get + + flows + } + + /** + * Removes taints according to `sanitizeParamters`. + */ + override protected def callToReturnFlow(call: Statement, successor: Statement, + in: Set[Fact], + source: (DeclaredMethod, Fact)): Set[Fact] = + in -- sanitizeParamters(call, in) + + /** + * If a parameter is tainted, the result will also be tainted. + * We assume that the callee does not call the source method. + */ + override protected def callOutsideOfAnalysisContext(statement: Statement, callee: DeclaredMethod, + successor: Statement, + in: Set[Fact]): Set[Fact] = { + val allParams = asCall(statement.stmt).receiverOption ++ asCall(statement.stmt).params + if (statement.stmt.astID == Assignment.ASTID && in.exists { + case Variable(index) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case ArrayElement(index, _) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case _ ⇒ false + }) Set(Variable(statement.index)) + else Set.empty + } + + /** + * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. + * True by default. This method can be overwritten by a subclass. + * + * @param callee The callee. + * @return True, by default. + */ + protected def relevantCallee(callee: DeclaredMethod): Boolean = true + + /** + * Creates new facts for an assignment. A new fact for the assigned variable will be created, + * if the expression contains a tainted variable + * + * @param expression The source expression of the assignment + * @param statement The assignment statement + * @param in The incoming facts + * @return The new facts, created by the assignment + */ + private def createNewTaints(expression: Expr[V], statement: Statement, in: Set[Fact]): Set[Fact] = + expression.astID match { + case Var.ASTID ⇒ + val definedBy = expression.asVar.definedBy + in.collect { + case Variable(index) if definedBy.contains(index) ⇒ + Variable(statement.index) + } + case ArrayLoad.ASTID ⇒ + val loadExpression = expression.asArrayLoad + val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy + if (in.exists { + // One specific array element may be tainted + case ArrayElement(index, taintedElement) ⇒ + val loadedIndex = TaintAnalysis.getIntConstant(loadExpression.index, statement.code) + arrayDefinedBy.contains(index) && + (loadedIndex.isEmpty || taintedElement == loadedIndex.get) + // Or the whole array + case Variable(index) ⇒ arrayDefinedBy.contains(index) + case _ ⇒ false + }) Set(Variable(statement.index)) + else + Set.empty + case GetField.ASTID ⇒ + val get = expression.asGetField + val objectDefinedBy = get.objRef.asVar.definedBy + if (in.exists { + // The specific field may be tainted + case InstanceField(index, _, taintedField) ⇒ + taintedField == get.name && objectDefinedBy.contains(index) + // Or the whole object + case Variable(index) ⇒ objectDefinedBy.contains(index) + case _ ⇒ false + }) + Set(Variable(statement.index)) + else + Set.empty + case GetStatic.ASTID ⇒ + val get = expression.asGetStatic + if (in.contains(StaticField(get.declaringClass, get.name))) + Set(Variable(statement.index)) + else Set.empty + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | + NewArray.ASTID | ArrayLength.ASTID ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) + case _ ⇒ Set.empty + } + + /** + * Checks, if the result of some variable expression could be tainted. + * + * @param expression The variable expression. + * @param in The current data flow facts. + * @return True, if the expression could be tainted + */ + private def isTainted(expression: Expr[V], in: Set[Fact]): Boolean = { + val definedBy = expression.asVar.definedBy + expression.isVar && in.exists { + case Variable(index) ⇒ definedBy.contains(index) + case ArrayElement(index, _) ⇒ definedBy.contains(index) + case InstanceField(index, _, _) ⇒ definedBy.contains(index) + case _ ⇒ false + } + } +} + +/** + * The IFDSProperty for this analysis. + */ +case class Taint(flows: Map[Statement, Set[Fact]]) extends IFDSProperty[Fact] { + + override type Self = Taint + + override def key: PropertyKey[Taint] = Taint.key +} + +object Taint extends IFDSPropertyMetaInformation[Fact] { + + override type Self = Taint + + val key: PropertyKey[Taint] = PropertyKey.create("Taint", new Taint(Map.empty)) +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintAnalysis.scala new file mode 100644 index 0000000000..ed5d74340f --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintAnalysis.scala @@ -0,0 +1,117 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.br.DeclaredMethod +import org.opalj.br.Method +import org.opalj.br.ObjectType +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSNullFact +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.Assignment +import org.opalj.tac.Expr +import org.opalj.tac.Stmt +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V + +trait Fact extends AbstractIFDSFact + +case object NullFact extends Fact with AbstractIFDSNullFact + +/** + * A tainted variable. + * + * @param index The variable's definition site. + */ +case class Variable(index: Int) extends Fact + +/** + * A tainted array element. + * + * @param index The array's definition site. + * @param element The index of the tainted element in the array. + */ +case class ArrayElement(index: Int, element: Int) extends Fact + +/** + * A tainted static field. + * + * @param classType The field's class. + * @param fieldName The field's name. + */ +case class StaticField(classType: ObjectType, fieldName: String) extends Fact + +/** + * A tainted instance field. + * + * @param index The definition site of the field's value. + * @param classType The field's type. + * @param fieldName The field's value. + */ +case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends Fact + +/** + * A path of method calls, originating from the analyzed method, over which a tainted variable + * reaches the sink. + * + * @param flow A sequence of method calls, originating from but not including this method. + */ +case class FlowFact(flow: Seq[Method]) extends Fact { + override val hashCode: Int = { + var r = 1 + flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) + r + } +} + +trait TaintAnalysis { + + /** + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. + * + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. + */ + protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean + + /** + * Called in callToReturnFlow. This method can return facts, which will be removed after + * `callee` was called. I.e. the method could sanitize parameters. + * + * @param call The call statement. + * @param in The facts, which hold before the call. + * @return Facts, which will be removed from `in` after the call. + */ + protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] +} + +object TaintAnalysis { + + /** + * Checks, if some expression always evaluates to the same int constant. + * + * @param expression The expression. + * @param code The TAC code, which contains the expression. + * @return Some int, if this analysis is sure that `expression` always evaluates to the same int + * constant, None otherwise. + */ + def getIntConstant(expression: Expr[V], code: Array[Stmt[V]]): Option[Int] = { + if (expression.isIntConst) Some(expression.asIntConst.value) + else if (expression.isVar) { + val definedByIterator = expression.asVar.definedBy.iterator + var allDefinedByWereConstant = true + var result = scala.collection.mutable.Seq.empty[Int] + while (definedByIterator.hasNext && allDefinedByWereConstant) { + val definedBy = definedByIterator.next() + if (definedBy >= 0) { + val stmt = code(definedBy) + if (stmt.astID == Assignment.ASTID && stmt.asAssignment.expr.isIntConst) + result :+= stmt.asAssignment.expr.asIntConst.value + else allDefinedByWereConstant = false + } else allDefinedByWereConstant = false + } + if (allDefinedByWereConstant && result.tail.forall(_ == result.head)) + Some(result.head) + else None + } else None + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala index c99cdafbcf..5b0da7a71f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala @@ -7,7 +7,7 @@ package properties import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyMetaInformation import org.opalj.value.KnownTypedValue -import org.opalj.tac.fpcf.analyses.Statement +import org.opalj.tac.fpcf.analyses.ifds.Statement trait IFDSPropertyMetaInformation[DataFlowFact] extends PropertyMetaInformation diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala new file mode 100644 index 0000000000..a8dc0b5d28 --- /dev/null +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala @@ -0,0 +1,88 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.fpcf.PropertyStore +import org.opalj.br.analyses.SomeProject +import org.opalj.br.DeclaredMethod +import org.opalj.br.Method +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation + +case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Method]) + extends UnbalancedReturnFact[Fact] with Fact + +/** + * An analysis that checks, if the return value of a `source` method can flow to the parameter of a + * `sink` method. + * + * @author Mario Trageser + */ +class BackwardTaintAnalysisFixture private (implicit val pProject: SomeProject) + extends BackwardTaintAnalysis { + + override val entryPoints: Seq[(DeclaredMethod, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ + classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") + .flatMap(_.methods).filter(_.name == "sink") + .map(method ⇒ declaredMethods(method) → + Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(0, isStaticMethod = true))) + + /** + * The sanitize method is the sanitizer. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = callee.name == "sanitize" + + /** + * We do not sanitize paramters. + */ + override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * Create a flow fact, if a source method is called and the returned value is tainted. + * This is done in callToReturnFlow, because it may be the case that the callee never + * terminates. + * In this case, callFlow would never be called and no FlowFact would be created. + */ + override protected def createFlowFactAtCall(call: Statement, in: Set[Fact], + source: (DeclaredMethod, Fact)): Option[FlowFact] = { + val callPc = call.code(call.index).pc + if (in.exists { + case Variable(index) ⇒ index == call.index + case _ ⇒ false + } && getCallees(call.node.asBasicBlock, callPc, source._1).exists(_.name == "source")) { + val callChain = currentCallChain(source) + // Avoid infinite loops. + if (!containsHeadTwice(callChain)) + Some(FlowFact(callChain)) + else None + } else None + } + + /** + * When a callee calls the source, we create a FlowFact with the caller's call chain. + */ + override protected def applyFlowFactFromCallee( + calleeFact: FlowFact, + source: (DeclaredMethod, Fact) + ): Option[FlowFact] = + Some(FlowFact(currentCallChain(source))) + + /** + * This analysis does not create FlowFacts at the beginning of a method. + * Instead, FlowFacts are created, when the return value of source is tainted. + */ + override protected def createFlowFactAtBeginningOfMethod( + in: Set[Fact], + source: (DeclaredMethod, Fact) + ): Option[FlowFact] = + None +} + +object BackwardTaintAnalysisFixture extends IFDSAnalysis[Fact] { + + override def init(p: SomeProject, ps: PropertyStore) = new BackwardTaintAnalysisFixture()(p) + + override def property: IFDSPropertyMetaInformation[Fact] = Taint +} diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala new file mode 100644 index 0000000000..f1051ee181 --- /dev/null +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -0,0 +1,61 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.fpcf.PropertyStore +import org.opalj.br.analyses.SomeProject +import org.opalj.br.DeclaredMethod +import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation + +/** + * An analysis that checks, if the return value of the method `source` can flow to the parameter of + * the method `sink`. + * + * @author Mario Trageser + */ +class ForwardTaintAnalysisFixture private (implicit val pProject: SomeProject) + extends ForwardTaintAnalysis { + + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(DeclaredMethod, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ + classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") + .flatMap(classFile ⇒ classFile.methods) + .filter(method ⇒ method.isPublic && insideAnalysisContext(declaredMethods(method))) + .map(method ⇒ declaredMethods(method) → NullFact) + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = callee.name == "sanitize" + + /** + * We do not sanitize paramters. + */ + override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: DeclaredMethod, call: Statement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact(callee: DeclaredMethod, call: Statement, + in: Set[Fact]): Option[FlowFact] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) + else None +} + +object ForwardTaintAnalysisFixture extends IFDSAnalysis[Fact] { + + override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture()(p) + + override def property: IFDSPropertyMetaInformation[Fact] = Taint +} From 31c4c6b240540a47b4e880693e08181b74b2be02 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Tue, 29 Jun 2021 13:45:31 +0200 Subject: [PATCH 02/67] Create new project "ll" --- .../scala/org/opalj/ll/LLPlayground.scala | 11 +++++++ OPAL/ll/Readme.md | 4 +++ OPAL/ll/build.sbt | 1 + .../src/main/resources/org/opalj/ll/.gitkeep | 0 OPAL/ll/src/main/scala/org/opalj/ll/.gitkeep | 0 .../main/scala/org/opalj/ll/TestObject.scala | 9 ++++++ .../src/main/scala/org/opalj/ll/package.scala | 32 +++++++++++++++++++ .../src/test/resources/org/opalj/ll/.gitkeep | 0 OPAL/ll/src/test/scala/org/opalj/ll/.gitkeep | 0 build.sbt | 16 +++++++++- 10 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala create mode 100644 OPAL/ll/Readme.md create mode 100644 OPAL/ll/build.sbt create mode 100644 OPAL/ll/src/main/resources/org/opalj/ll/.gitkeep create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/.gitkeep create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/package.scala create mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/.gitkeep create mode 100644 OPAL/ll/src/test/scala/org/opalj/ll/.gitkeep diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala new file mode 100644 index 0000000000..c35cd43742 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala @@ -0,0 +1,11 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package ll + +object LLPlayground { + + def main(args: Array[String]): Unit = { + println("Hello World") + TestObject.foo() + } +} diff --git a/OPAL/ll/Readme.md b/OPAL/ll/Readme.md new file mode 100644 index 0000000000..c48c1c83aa --- /dev/null +++ b/OPAL/ll/Readme.md @@ -0,0 +1,4 @@ +# Overview +The ***LLVM*** (LL) module provides tools to work with the llvm compiler infrastructure. + + diff --git a/OPAL/ll/build.sbt b/OPAL/ll/build.sbt new file mode 100644 index 0000000000..b511e98651 --- /dev/null +++ b/OPAL/ll/build.sbt @@ -0,0 +1 @@ +// build settings reside in the opal root build.sbt file diff --git a/OPAL/ll/src/main/resources/org/opalj/ll/.gitkeep b/OPAL/ll/src/main/resources/org/opalj/ll/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/.gitkeep b/OPAL/ll/src/main/scala/org/opalj/ll/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala b/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala new file mode 100644 index 0000000000..0cadf1f86d --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala @@ -0,0 +1,9 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package ll + +object TestObject { + def foo(): Unit = { + println("bar") + } +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/package.scala b/OPAL/ll/src/main/scala/org/opalj/ll/package.scala new file mode 100644 index 0000000000..b247a58ba9 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/package.scala @@ -0,0 +1,32 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory + +import org.opalj.log.LogContext +import org.opalj.log.GlobalLogContext +import org.opalj.log.OPALLogger.info + +package object ll { + + final val FrameworkName = "OPAL LLVM" + + { + implicit val logContext: LogContext = GlobalLogContext + try { + assert(false) // <= test whether assertions are turned on or off... + info(FrameworkName, "Production Build") + } catch { + case _: AssertionError ⇒ info(FrameworkName, "Development Build with Assertions") + } + } + + // We want to make sure that the class loader is used which potentially can + // find the config files; the libraries (e.g., Typesafe Config) may have + // been loaded using the parent class loader and, hence, may not be able to + // find the config files at all. + val BaseConfig: Config = ConfigFactory.load(this.getClass.getClassLoader) + + final val ConfigKeyPrefix = "org.opalj.ll." +} diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/.gitkeep b/OPAL/ll/src/test/resources/org/opalj/ll/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/OPAL/ll/src/test/scala/org/opalj/ll/.gitkeep b/OPAL/ll/src/test/scala/org/opalj/ll/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/build.sbt b/build.sbt index 47e66ea9bc..31faff8628 100644 --- a/build.sbt +++ b/build.sbt @@ -161,6 +161,7 @@ lazy val `OPAL` = (project in file(".")) tac, de, av, + ll, framework, // bp, (just temporarily...) tools, @@ -325,6 +326,18 @@ lazy val `ArchitectureValidation` = (project in file("OPAL/av")) .dependsOn(de % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) + +lazy val ll = `LLVM` +lazy val `LLVM` = (project in file("OPAL/ll")) + .settings(buildSettings: _*) + .settings( + name := "LLVM", + scalacOptions in(Compile, doc) ++= Opts.doc.title("OPAL - LLVM"), + fork := true + ) +.dependsOn(common % "it->test;test->test;compile->compile") +.configs(IntegrationTest) + lazy val framework = `Framework` lazy val `Framework` = (project in file("OPAL/framework")) .settings(buildSettings: _*) @@ -336,7 +349,8 @@ lazy val `Framework` = (project in file("OPAL/framework")) .dependsOn( ba % "it->it;it->test;test->test;compile->compile", av % "it->it;it->test;test->test;compile->compile", - tac % "it->it;it->test;test->test;compile->compile" + tac % "it->it;it->test;test->test;compile->compile", + ll % "it->it;it->test;test->test;compile->compile" ) .configs(IntegrationTest) From a26ee83ce18824d6944bc0b6cfa1fac92a009b37 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Tue, 29 Jun 2021 17:46:55 +0200 Subject: [PATCH 03/67] Add llvm bindings dependency --- .../main/scala/org/opalj/ll/TestObject.scala | 2 + .../src/test/resources/org/opalj/ll/.gitkeep | 0 .../src/test/resources/org/opalj/ll/test.ll | 58 +++++++++++++++++++ build.sbt | 3 +- project/Dependencies.scala | 4 ++ 5 files changed, 66 insertions(+), 1 deletion(-) delete mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/.gitkeep create mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/test.ll diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala b/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala index 0cadf1f86d..39a8a5124a 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala @@ -2,6 +2,8 @@ package org.opalj package ll +import org.bytedeco.llvm.LLVM + object TestObject { def foo(): Unit = { println("bar") diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/.gitkeep b/OPAL/ll/src/test/resources/org/opalj/ll/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/test.ll b/OPAL/ll/src/test/resources/org/opalj/ll/test.ll new file mode 100644 index 0000000000..f3e449c659 --- /dev/null +++ b/OPAL/ll/src/test/resources/org/opalj/ll/test.ll @@ -0,0 +1,58 @@ +; ModuleID = 'test.c' +source_filename = "test.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00", align 1 +@.str.1 = private unnamed_addr constant [12 x i8] c"result: %i\0A\00", align 1 +@.str.2 = private unnamed_addr constant [8 x i8] c"foo: %i\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @pure_fuction(i32 %0, i32 %1) #0 { + %3 = alloca i32, align 4 + %4 = alloca i32, align 4 + store i32 %0, i32* %3, align 4 + store i32 %1, i32* %4, align 4 + %5 = load i32, i32* %3, align 4 + %6 = load i32, i32* %4, align 4 + %7 = add nsw i32 %5, %6 + ret i32 %7 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @impure_function(i32* %0) #0 { + %2 = alloca i32*, align 8 + store i32* %0, i32** %2, align 8 + %3 = load i32*, i32** %2, align 8 + store i32 23, i32* %3, align 4 + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { + %1 = alloca i32, align 4 + %2 = alloca i32, align 4 + %3 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i64 0, i64 0)) + %5 = call i32 @pure_fuction(i32 1, i32 2) + store i32 %5, i32* %2, align 4 + %6 = load i32, i32* %2, align 4 + %7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.1, i64 0, i64 0), i32 %6) + store i32 42, i32* %3, align 4 + call void @impure_function(i32* %3) + %8 = load i32, i32* %3, align 4 + %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i64 0, i64 0), i32 %8) + ret i32 0 +} + +declare dso_local i32 @printf(i8*, ...) #1 + +attributes #0 = { noinline nounwind optnone uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } +attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } + +!llvm.module.flags = !{!0} +!llvm.ident = !{!1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{!"Ubuntu clang version 12.0.0-1ubuntu1"} diff --git a/build.sbt b/build.sbt index 31faff8628..9dbc12aa5f 100644 --- a/build.sbt +++ b/build.sbt @@ -333,7 +333,8 @@ lazy val `LLVM` = (project in file("OPAL/ll")) .settings( name := "LLVM", scalacOptions in(Compile, doc) ++= Opts.doc.title("OPAL - LLVM"), - fork := true + fork := true, + libraryDependencies ++= Dependencies.ll ) .dependsOn(common % "it->test;test->test;compile->compile") .configs(IntegrationTest) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 5e8b78dcc7..be08a17a27 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -25,6 +25,8 @@ object Dependencies { val fastutil = "8.5.4" val openjfx = "16" + + val llvm = "11.1.0-1.5.5" } object library { @@ -49,6 +51,7 @@ object Dependencies { val jacksonDF = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-csv" % version.jacksonDF withSources () withJavadoc () val fastutil = "it.unimi.dsi" % "fastutil" % version.fastutil withSources () withJavadoc () val javafxBase = "org.openjfx" % "javafx-base" % version.openjfx classifier osName + val llvm = "org.bytedeco" % "llvm-platform" % version.llvm // --- test related dependencies @@ -67,6 +70,7 @@ object Dependencies { val si = Seq() val bi = Seq(commonstext) val br = Seq(scalaparsercombinators, scalaxml) + val ll = Seq(llvm) val tools = Seq(txtmark, jacksonDF) val hermes = Seq(txtmark, jacksonDF, javafxBase) From 1a3b939726e42658d514ffddd55ca03e967ed03b Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Mon, 5 Jul 2021 15:40:46 +0200 Subject: [PATCH 04/67] Read and dump module --- .../scala/org/opalj/ll/LLPlayground.scala | 7 +++-- .../src/main/scala/org/opalj/ll/Reader.scala | 28 +++++++++++++++++++ .../main/scala/org/opalj/ll/TestObject.scala | 11 -------- 3 files changed, 33 insertions(+), 13 deletions(-) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala delete mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala index c35cd43742..3c9610c4a6 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala @@ -2,10 +2,13 @@ package org.opalj package ll +import org.bytedeco.llvm.global.LLVM.LLVMDumpModule + + object LLPlayground { def main(args: Array[String]): Unit = { - println("Hello World") - TestObject.foo() + val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test.ll").get + LLVMDumpModule(module) } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala new file mode 100644 index 0000000000..981027d0de --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package ll + +import org.bytedeco.javacpp.BytePointer +import org.bytedeco.llvm.LLVM.{LLVMContextRef, LLVMMemoryBufferRef, LLVMModuleRef} +import org.bytedeco.llvm.global.LLVM.{LLVMContextCreate, LLVMCreateMemoryBufferWithContentsOfFile, LLVMDisposeMessage, LLVMParseIRInContext} + +object Reader { + def readIR(path: String): Option[LLVMModuleRef] = { + val file_buffer: LLVMMemoryBufferRef = new LLVMMemoryBufferRef() + val path_pointer: BytePointer = new BytePointer(path) + val out_message: BytePointer = new BytePointer() + if (LLVMCreateMemoryBufferWithContentsOfFile(path_pointer, file_buffer, out_message) != 0) { + System.err.println("Failed to load file: " + out_message.getString) + LLVMDisposeMessage(out_message) + return None + } + val context: LLVMContextRef = LLVMContextCreate() + val module: LLVMModuleRef = new LLVMModuleRef() + if (LLVMParseIRInContext(context, file_buffer, module, out_message) !=0 ) { + println("Failed to parse file: " + out_message) + LLVMDisposeMessage(out_message) + return None + } + Some(module) + } +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala b/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala deleted file mode 100644 index 39a8a5124a..0000000000 --- a/OPAL/ll/src/main/scala/org/opalj/ll/TestObject.scala +++ /dev/null @@ -1,11 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package ll - -import org.bytedeco.llvm.LLVM - -object TestObject { - def foo(): Unit = { - println("bar") - } -} \ No newline at end of file From 34d4e251832a8363b225cbd1d892960e82d5d53c Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Tue, 6 Jul 2021 17:51:18 +0200 Subject: [PATCH 05/67] Begin implementing wrappers --- .../scala/org/opalj/ll/LLPlayground.scala | 7 ++--- .../main/scala/org/opalj/ll/BasicBlock.scala | 6 ++++ .../main/scala/org/opalj/ll/Function.scala | 31 +++++++++++++++++++ .../src/main/scala/org/opalj/ll/Module.scala | 27 ++++++++++++++++ .../src/main/scala/org/opalj/ll/Reader.scala | 10 +++--- 5 files changed, 72 insertions(+), 9 deletions(-) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/Function.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/Module.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala index 3c9610c4a6..590673932a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala @@ -2,13 +2,12 @@ package org.opalj package ll -import org.bytedeco.llvm.global.LLVM.LLVMDumpModule - - object LLPlayground { def main(args: Array[String]): Unit = { val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test.ll").get - LLVMDumpModule(module) + println(module.repr) + println(module.functions.map(f => f.name).toList) + println(module.functions.map(f => f.repr).toList) } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala new file mode 100644 index 0000000000..8c617cfcb0 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala @@ -0,0 +1,6 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef} + +case class BasicBlock(ref: LLVMBasicBlockRef) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Function.scala new file mode 100644 index 0000000000..9e90ab06cb --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Function.scala @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} +import org.bytedeco.llvm.global.LLVM.{LLVMDisposeMessage, LLVMGetFirstBasicBlock, LLVMGetNextBasicBlock, LLVMGetValueName, LLVMPrintValueToString} + +case class Function(ref: LLVMValueRef) { + def repr(): String = { + val bytePointer = LLVMPrintValueToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } + + def name(): String = { + LLVMGetValueName(ref).getString + } + + def basicBlocks(): BasicBlockIterator = { + new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) + } +} + +class BasicBlockIterator(var ref: LLVMBasicBlockRef) extends Iterator[BasicBlock] { + override def hasNext: Boolean = LLVMGetNextBasicBlock(ref) != null + + override def next(): BasicBlock = { + this.ref = LLVMGetNextBasicBlock(ref) + BasicBlock(ref) + } +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala new file mode 100644 index 0000000000..9c98abb73e --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala @@ -0,0 +1,27 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.bytedeco.llvm.LLVM.{LLVMModuleRef, LLVMValueRef} +import org.bytedeco.llvm.global.LLVM.{LLVMDisposeMessage, LLVMGetFirstFunction, LLVMGetNextFunction, LLVMPrintModuleToString} + +case class Module(ref: LLVMModuleRef) { + def functions(): FunctionIterator = { + new FunctionIterator(LLVMGetFirstFunction(ref)) + } + + def repr(): String = { + val bytePointer = LLVMPrintModuleToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } +} + +class FunctionIterator(var ref: LLVMValueRef) extends Iterator[Function] { + override def hasNext: Boolean = LLVMGetNextFunction(ref) != null + + override def next(): Function = { + this.ref = LLVMGetNextFunction(ref) + Function(ref) + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala index 981027d0de..6905982a18 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala @@ -7,22 +7,22 @@ import org.bytedeco.llvm.LLVM.{LLVMContextRef, LLVMMemoryBufferRef, LLVMModuleRe import org.bytedeco.llvm.global.LLVM.{LLVMContextCreate, LLVMCreateMemoryBufferWithContentsOfFile, LLVMDisposeMessage, LLVMParseIRInContext} object Reader { - def readIR(path: String): Option[LLVMModuleRef] = { + def readIR(path: String): Option[Module] = { val file_buffer: LLVMMemoryBufferRef = new LLVMMemoryBufferRef() val path_pointer: BytePointer = new BytePointer(path) val out_message: BytePointer = new BytePointer() if (LLVMCreateMemoryBufferWithContentsOfFile(path_pointer, file_buffer, out_message) != 0) { - System.err.println("Failed to load file: " + out_message.getString) + System.err.println("Failed to load file: "+out_message.getString) LLVMDisposeMessage(out_message) return None } val context: LLVMContextRef = LLVMContextCreate() val module: LLVMModuleRef = new LLVMModuleRef() - if (LLVMParseIRInContext(context, file_buffer, module, out_message) !=0 ) { - println("Failed to parse file: " + out_message) + if (LLVMParseIRInContext(context, file_buffer, module, out_message) != 0) { + println("Failed to parse file: "+out_message) LLVMDisposeMessage(out_message) return None } - Some(module) + Some(Module(module)) } } \ No newline at end of file From 53da4de152cd90d9c29344abd0132a08b555b03a Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 7 Jul 2021 17:27:52 +0200 Subject: [PATCH 06/67] Read instructions --- .../scala/org/opalj/ll/LLPlayground.scala | 12 ++++++-- .../main/scala/org/opalj/ll/BasicBlock.scala | 19 +++++++++++-- .../main/scala/org/opalj/ll/Function.scala | 20 ++++--------- .../main/scala/org/opalj/ll/Instruction.scala | 28 +++++++++++++++++++ .../src/main/scala/org/opalj/ll/Module.scala | 5 ++-- .../src/main/scala/org/opalj/ll/Value.scala | 18 ++++++++++++ 6 files changed, 82 insertions(+), 20 deletions(-) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/Value.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala index 590673932a..67479e3292 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala @@ -6,8 +6,16 @@ object LLPlayground { def main(args: Array[String]): Unit = { val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test.ll").get - println(module.repr) + //println(module.repr) println(module.functions.map(f => f.name).toList) - println(module.functions.map(f => f.repr).toList) + //println(module.functions.map(f => f.repr).toList) + val functions = module.functions() + val function = functions.next() + //print(function.repr) + val block = function.basicBlocks().next(); + print(block.repr); + val instruction = block.instructions().next(); + println("Instruction: " + instruction.repr()) + println(block.instructions.toList) } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala index 8c617cfcb0..97a87a588c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala @@ -1,6 +1,21 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll -import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef} +import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} +import org.bytedeco.llvm.global.LLVM.{LLVMGetFirstInstruction, LLVMGetNextInstruction, LLVMBasicBlockAsValue} -case class BasicBlock(ref: LLVMBasicBlockRef) +case class BasicBlock(block_ref: LLVMBasicBlockRef) extends Value(LLVMBasicBlockAsValue(block_ref)) { + def instructions(): InstructionIterator = { + new InstructionIterator(LLVMGetFirstInstruction(block_ref)) + } +} + +class InstructionIterator(var ref: LLVMValueRef) extends Iterator[Instruction] { + override def hasNext: Boolean = LLVMGetNextInstruction(ref) != null + + override def next(): Instruction = { + val instruction = Instruction.fromValue(ref) + this.ref = LLVMGetNextInstruction(ref) + instruction + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Function.scala index 9e90ab06cb..56e0e455f5 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Function.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Function.scala @@ -2,19 +2,10 @@ package org.opalj.ll import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} -import org.bytedeco.llvm.global.LLVM.{LLVMDisposeMessage, LLVMGetFirstBasicBlock, LLVMGetNextBasicBlock, LLVMGetValueName, LLVMPrintValueToString} +import org.bytedeco.llvm.global.LLVM.{LLVMFunctionValueKind, LLVMGetFirstBasicBlock, LLVMGetNextBasicBlock, LLVMGetValueKind} -case class Function(ref: LLVMValueRef) { - def repr(): String = { - val bytePointer = LLVMPrintValueToString(ref) - val string = bytePointer.getString - LLVMDisposeMessage(bytePointer) - string - } - - def name(): String = { - LLVMGetValueName(ref).getString - } +case class Function(ref: LLVMValueRef) extends Value(ref) { + assert(LLVMGetValueKind(ref) == LLVMFunctionValueKind, "ref has to be a function") def basicBlocks(): BasicBlockIterator = { new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) @@ -22,10 +13,11 @@ case class Function(ref: LLVMValueRef) { } class BasicBlockIterator(var ref: LLVMBasicBlockRef) extends Iterator[BasicBlock] { - override def hasNext: Boolean = LLVMGetNextBasicBlock(ref) != null + override def hasNext: Boolean = ref != null override def next(): BasicBlock = { + val basicBlock = BasicBlock(ref) this.ref = LLVMGetNextBasicBlock(ref) - BasicBlock(ref) + basicBlock } } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala new file mode 100644 index 0000000000..d56b82366f --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.{LLVMGetInstructionOpcode, LLVMGetValueKind, LLVMInstructionValueKind, LLVMAlloca, LLVMStore, LLVMLoad, LLVMAdd, LLVMRet} + +object Instruction { + def fromValue(ref: LLVMValueRef): Instruction = { + assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") + LLVMGetInstructionOpcode(ref) match { + case LLVMAlloca => Alloca(ref) + case LLVMStore => Store(ref) + case LLVMLoad => Load(ref) + case LLVMAdd => Add(ref) + case LLVMRet => Ret(ref) + case opCode => throw new IllegalArgumentException("unknown instruction opcode: " + opCode) + } + } +} + +sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) +case class Alloca(ref: LLVMValueRef) extends Instruction(ref) +case class Store(ref: LLVMValueRef) extends Instruction(ref) +case class Load(ref: LLVMValueRef) extends Instruction(ref) +case class Add(ref: LLVMValueRef) extends Instruction(ref) +case class Ret(ref: LLVMValueRef) extends Instruction(ref) + + diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala index 9c98abb73e..923285b763 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala @@ -18,10 +18,11 @@ case class Module(ref: LLVMModuleRef) { } class FunctionIterator(var ref: LLVMValueRef) extends Iterator[Function] { - override def hasNext: Boolean = LLVMGetNextFunction(ref) != null + override def hasNext: Boolean = ref != null override def next(): Function = { + val function = Function(ref) this.ref = LLVMGetNextFunction(ref) - Function(ref) + function } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala new file mode 100644 index 0000000000..03da95f871 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.{LLVMDisposeMessage, LLVMGetValueName, LLVMPrintValueToString} + +abstract class Value (ref: LLVMValueRef) { + def repr(): String = { + val bytePointer = LLVMPrintValueToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } + + def name(): String = { + LLVMGetValueName(ref).getString + } +} From 283ef68c5d41e64383e1a092a75d6e8bdf79ea6b Mon Sep 17 00:00:00 2001 From: Tom Nikisch <63189423+Diarilex@users.noreply.github.com> Date: Wed, 14 Jul 2021 15:51:10 +0200 Subject: [PATCH 07/67] AndroidEntryPointFinder added --- .../br/analyses/cg/EntryPointFinder.scala | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala index 65f2901ecd..4d68972ba5 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala @@ -347,4 +347,44 @@ object AllEntryPointsFinder extends EntryPointFinder { project.allProjectClassFiles.flatMap(_.methodsWithBody) else project.allMethodsWithBody } +} + +/** + * The AndroidEntryPointFinder considers specific methods of app components as entry points. + * It does not work for androidx + * + * @author Tom Nikisch + */ +object AndroidEntryPointsFinder extends EntryPointFinder { + + val activityEPS: List[String] = List("onCreate", "onRestart", "onStart", "onResume", + "onStop", "onDestroy", "onActivityResult") + val serviceEPS: List[String] = List("onCreate", "onStartCommand", "onBind", "onStart") + val contentProviderEPS: List[String] = List("onCreate", "query", "insert", "update") + val locationListenerEPS: List[String] = List("onLocationChanged", "onProviderDisabled", "onProviderEnabled", + "onStatusChanged") + val onNmeaMessageListenerEPS: List[String] = List("onNmeaMessage") + val defaultEPS = Map("android/app/Activity" -> activityEPS, "android/app/Service" -> serviceEPS, + "android/content/ContentProvider" -> contentProviderEPS, + "android/location/LocationListener" -> locationListenerEPS, + "android/location/onNmeaMessageListener" -> onNmeaMessageListenerEPS) + + override def collectEntryPoints(project: SomeProject): Traversable[Method] = { + val eps = ArrayBuffer.empty[Method] + for ((superClass, methodList) ← defaultEPS) { + eps ++= findEPS(ObjectType(superClass), methodList, project) + } + eps + } + + def findEPS(ot: ObjectType, possibleEPS: List[String], project: SomeProject): ArrayBuffer[Method] = { + val eps = ArrayBuffer.empty[Method] + val classHierarchy = project.classHierarchy + classHierarchy.foreachSubclass(ot, project) { sc ⇒ + for (pep ← possibleEPS; m ← sc.findMethod(pep) if m.body.isDefined && !eps.contains(m)) { + eps += m + } + } + eps + } } \ No newline at end of file From c88385a5962f57a2b1deb6bab124880b897331ce Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Mon, 8 Nov 2021 19:55:24 +0100 Subject: [PATCH 08/67] Implement SimplePurityAnalysis which assumes everything to be Impure --- .../scala/org/opalj/ll/LLPlayground.scala | 4 +- .../org/opalj/ll/SimplePurityTests.scala | 24 +++++++ .../main/scala/org/opalj/ll/Instruction.scala | 13 ++-- .../scala/org/opalj/ll/LLVMFunctionsKey.scala | 13 ++++ .../scala/org/opalj/ll/LLVMModulesKey.scala | 20 ++++++ .../src/main/scala/org/opalj/ll/Module.scala | 8 +-- .../org/opalj/ll/SimplePurityAnalysis.scala | 63 +++++++++++++++++++ .../src/main/scala/org/opalj/ll/Value.scala | 2 +- build.sbt | 4 +- 9 files changed, 135 insertions(+), 16 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/LLVMFunctionsKey.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/LLVMModulesKey.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala index 67479e3292..36e348c048 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala @@ -7,7 +7,7 @@ object LLPlayground { def main(args: Array[String]): Unit = { val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test.ll").get //println(module.repr) - println(module.functions.map(f => f.name).toList) + println(module.functions.map(f ⇒ f.name).toList) //println(module.functions.map(f => f.repr).toList) val functions = module.functions() val function = functions.next() @@ -15,7 +15,7 @@ object LLPlayground { val block = function.basicBlocks().next(); print(block.repr); val instruction = block.instructions().next(); - println("Instruction: " + instruction.repr()) + println("Instruction: "+instruction.repr()) println(block.instructions.toList) } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala new file mode 100644 index 0000000000..2726deb45a --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.opalj.br.analyses.Project +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.scalatest.funspec.AnyFunSpec +import org.scalatest.matchers.should.Matchers + +import scala.collection.immutable.List + +class SimplePurityTests extends AnyFunSpec with Matchers { + describe("SimplePurityAnalysis") { + it("executes") { + val project = Project(Traversable.empty) + project.updateProjectInformationKeyInitializationData(LLVMModulesKey)(current => + current.getOrElse(Iterable.empty) ++ List("./OPAL/ll/src/test/resources/org/opalj/ll/test.ll") + ) + val (propertyStore, _) = project.get(FPCFAnalysesManagerKey).runAll(EagerSimplePurityAnalysis) + + assert(propertyStore.finalEntities(Pure).size == 0) + assert(propertyStore.finalEntities(Impure).size == 4) + } + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala index d56b82366f..5faae09ffd 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala @@ -8,12 +8,12 @@ object Instruction { def fromValue(ref: LLVMValueRef): Instruction = { assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") LLVMGetInstructionOpcode(ref) match { - case LLVMAlloca => Alloca(ref) - case LLVMStore => Store(ref) - case LLVMLoad => Load(ref) - case LLVMAdd => Add(ref) - case LLVMRet => Ret(ref) - case opCode => throw new IllegalArgumentException("unknown instruction opcode: " + opCode) + case LLVMAlloca ⇒ Alloca(ref) + case LLVMStore ⇒ Store(ref) + case LLVMLoad ⇒ Load(ref) + case LLVMAdd ⇒ Add(ref) + case LLVMRet ⇒ Ret(ref) + case opCode ⇒ throw new IllegalArgumentException("unknown instruction opcode: "+opCode) } } } @@ -25,4 +25,3 @@ case class Load(ref: LLVMValueRef) extends Instruction(ref) case class Add(ref: LLVMValueRef) extends Instruction(ref) case class Ret(ref: LLVMValueRef) extends Instruction(ref) - diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMFunctionsKey.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMFunctionsKey.scala new file mode 100644 index 0000000000..83a4370fe3 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMFunctionsKey.scala @@ -0,0 +1,13 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.opalj.br.analyses.{ProjectInformationKey, ProjectInformationKeys, SomeProject} + +object LLVMFunctionsKey extends ProjectInformationKey[Iterable[Function], Nothing] { + override def requirements(project: SomeProject): ProjectInformationKeys = Seq(LLVMModulesKey) + + override def compute(project: SomeProject): Iterable[Function] = { + project.get(LLVMModulesKey).flatMap(module => module.functions()) + } + +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMModulesKey.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMModulesKey.scala new file mode 100644 index 0000000000..4918514e1a --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMModulesKey.scala @@ -0,0 +1,20 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package ll + +import org.opalj.br.analyses.{ProjectInformationKey, SomeProject} + +object LLVMModulesKey extends ProjectInformationKey[Iterable[Module], Iterable[String]] { + /** + * The [[LLVMModulesKey]] has no special prerequisites. + * + * @return `Nil`. + */ + override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + + override def compute(project: SomeProject): Iterable[Module] = { + val modules_paths = project.getOrCreateProjectInformationKeyInitializationData(this, Iterable.empty) + modules_paths.map(path ⇒ Reader.readIR(path).get) + } + +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala index 923285b763..9a795364fe 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala @@ -10,10 +10,10 @@ case class Module(ref: LLVMModuleRef) { } def repr(): String = { - val bytePointer = LLVMPrintModuleToString(ref) - val string = bytePointer.getString - LLVMDisposeMessage(bytePointer) - string + val bytePointer = LLVMPrintModuleToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala new file mode 100644 index 0000000000..b0bf6fe887 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala @@ -0,0 +1,63 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll +import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} +import org.opalj.br.fpcf.{BasicFPCFEagerAnalysisScheduler, FPCFAnalysis, FPCFAnalysisScheduler} +import org.opalj.fpcf.{Entity, OrderedProperty, ProperPropertyComputationResult, PropertyBounds, PropertyKey, PropertyMetaInformation, PropertyStore, Result} + +sealed trait SimplePurityPropertyMetaInformation extends PropertyMetaInformation { + final type Self = SimplePurity +} + +sealed trait SimplePurity extends SimplePurityPropertyMetaInformation with OrderedProperty { + def meet(other: SimplePurity): SimplePurity = { + (this, other) match { + case (Pure, Pure) ⇒ Pure + case (_, _) ⇒ Impure + } + } + + override def checkIsEqualOrBetterThan(e: Entity, other: SimplePurity): Unit = { + if (meet(other) != other) { + throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + } + } + + final def key: PropertyKey[SimplePurity] = SimplePurity.key +} + +case object Pure extends SimplePurity + +case object Impure extends SimplePurity + +object SimplePurity extends SimplePurityPropertyMetaInformation { + final val key: PropertyKey[SimplePurity] = PropertyKey.create( + "SimplePurity", + Impure + ) +} + +class SimplePurityAnalysis(val project: SomeProject) extends FPCFAnalysis { + def analyzeSimplePurity(function: Function): ProperPropertyComputationResult = { + Result(function, Impure) + } +} + +trait SimplePurityAnalysisScheduler extends FPCFAnalysisScheduler { + def derivedProperty: PropertyBounds = PropertyBounds.ub(SimplePurity) + + override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMFunctionsKey) + + override def uses: Set[PropertyBounds] = Set.empty // TODO: check this later +} + +object EagerSimplePurityAnalysis extends SimplePurityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { + override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) + + override def derivesCollaboratively: Set[PropertyBounds] = Set.empty + + override def start(project: SomeProject, propertyStore: PropertyStore, initData: InitializationData): FPCFAnalysis = { + val analysis = new SimplePurityAnalysis(project) + propertyStore.scheduleEagerComputationsForEntities(project.get(LLVMFunctionsKey))(analysis.analyzeSimplePurity) + analysis + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala index 03da95f871..5678a562b3 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala @@ -4,7 +4,7 @@ package org.opalj.ll import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM.{LLVMDisposeMessage, LLVMGetValueName, LLVMPrintValueToString} -abstract class Value (ref: LLVMValueRef) { +abstract class Value(ref: LLVMValueRef) { def repr(): String = { val bytePointer = LLVMPrintValueToString(ref) val string = bytePointer.getString diff --git a/build.sbt b/build.sbt index 9dbc12aa5f..0b1292d098 100644 --- a/build.sbt +++ b/build.sbt @@ -336,7 +336,7 @@ lazy val `LLVM` = (project in file("OPAL/ll")) fork := true, libraryDependencies ++= Dependencies.ll ) -.dependsOn(common % "it->test;test->test;compile->compile") +.dependsOn(br % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) lazy val framework = `Framework` @@ -351,7 +351,7 @@ lazy val `Framework` = (project in file("OPAL/framework")) ba % "it->it;it->test;test->test;compile->compile", av % "it->it;it->test;test->test;compile->compile", tac % "it->it;it->test;test->test;compile->compile", - ll % "it->it;it->test;test->test;compile->compile" + ll % "it->it;it->test;test->test;compile->compile" ) .configs(IntegrationTest) From c87f9ac2e7722c1ef8edffd9aec4a3a17c2c579a Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 12 Nov 2021 14:14:05 +0100 Subject: [PATCH 09/67] Implement SimplePurityAnalysis --- .../scala/org/opalj/ll/LLPlayground.scala | 2 + .../validate/src/test/resources/llvm/a.out | Bin 0 -> 15936 bytes .../validate/src/test/resources/llvm/purity.c | 20 +++ .../src/test/resources/llvm/purity.ll | 57 +++++++ .../org/opalj/ll/SimplePurityTests.scala | 18 +- .../main/scala/org/opalj/ll/Instruction.scala | 27 --- .../scala/org/opalj/ll/LLVMFunctionsKey.scala | 13 -- .../scala/org/opalj/ll/LLVMModulesKey.scala | 20 --- .../main/scala/org/opalj/ll/LLVMProject.scala | 18 ++ .../scala/org/opalj/ll/LLVMProjectKey.scala | 14 ++ .../org/opalj/ll/SimplePurityAnalysis.scala | 43 ++++- .../src/main/scala/org/opalj/ll/Value.scala | 18 -- .../org/opalj/ll/{ => llvm}/BasicBlock.scala | 20 ++- .../org/opalj/ll/{ => llvm}/Function.scala | 11 +- .../org/opalj/ll/llvm/GlobalVariable.scala | 7 + .../scala/org/opalj/ll/llvm/Instruction.scala | 161 ++++++++++++++++++ .../org/opalj/ll/{ => llvm}/Module.scala | 9 +- .../org/opalj/ll/{ => llvm}/Reader.scala | 13 +- .../main/scala/org/opalj/ll/llvm/Value.scala | 52 ++++++ 19 files changed, 422 insertions(+), 101 deletions(-) create mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/a.out create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.c create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll delete mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala delete mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/LLVMFunctionsKey.scala delete mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/LLVMModulesKey.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/LLVMProjectKey.scala delete mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/Value.scala rename OPAL/ll/src/main/scala/org/opalj/ll/{ => llvm}/BasicBlock.scala (50%) rename OPAL/ll/src/main/scala/org/opalj/ll/{ => llvm}/Function.scala (79%) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/GlobalVariable.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala rename OPAL/ll/src/main/scala/org/opalj/ll/{ => llvm}/Module.scala (80%) rename OPAL/ll/src/main/scala/org/opalj/ll/{ => llvm}/Reader.scala (85%) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala index 36e348c048..25f59aec25 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala @@ -2,6 +2,8 @@ package org.opalj package ll +import org.opalj.ll.llvm.Reader + object LLPlayground { def main(args: Array[String]): Unit = { diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/a.out b/DEVELOPING_OPAL/validate/src/test/resources/llvm/a.out new file mode 100755 index 0000000000000000000000000000000000000000..dc75789c0de99e572d31211054ccc3a0ab572ac2 GIT binary patch literal 15936 zcmeHOU2Ggz6+XLm61Pq4b)3NFr_BIK2vxnY9mkj^O~ya#tSje-)Bz(Drfcumo@#ez znVqHUl0r}@$ZN!L=aa;px;>D?;w7yJlRTh9~=^<(2)kWu`rz`9f4KdG6 zyj4_W8+d%8Z*c>nC9c+7ESEFb)VRzOV@9Ey1&9rB&e5u@C?(J#s z=}r{wM3*8^zo-qmrVbrB0nyViPnhtjya5kNEBU`%xxVn?hG&1J-Fh2XZk;QJ8QR)`0~z)VaSMK|rZMlNmTq2O3~cM=Gu zeDuJfk>UOUqbt#s*jp>_=|<*M&dxK_z`CP2I_eim1g*`eWu8i#t$S$n+Zp@a4}OHx z9fb}GMyVXsm8NSz>L6grhw~+@;^Mll^cp<-^Qpk_6lNgIK$w9r17QZj41^g7GZ1Fr zBbtG~wLI{z)R}jhQgf@`+6|C;X5NifUr(KRrRj>wNOkWGgsVFi@!Yaa#}e7o9=U<0s-}p*u_D1T=V&&LqvU9%k)zrd5p%ue7&SCiblZlpX-&XZe{FSs_U0E2)>?J(zlVsUa1)ntJ1o+ght( z4c8FlQ~ReM`5bl%N^K`nvv1yf3P*8u)u$1OoV@ZM?NR*OXX||+%Iwp(XG=?S$=eOd zrIWtFQ2)rk(72-myZ<1zvD`ff%8&VuEhKN#VCTA3XzFsKrjrd9;pUI;RI8WiH_l5@ zJl#Bt{hLiL&6So?m$%lViWqhC`{=7xv8(R<0y;bAaTa4IaT;k1YL08ON-c#FW+2Q! zn1L_@VFtnsgc%4k5N06EK$w9r10SUf&~vzHGn=)wCv7L2c}SbIZOtlbJFI)bF^i?F z`>-k_>=iIMku44TdQsD}xOd*GR!^Z`Td7t@QSV1h{|?+n{WH{CZ&jk{jhKcSnqx!F z@kd+kd$Q>?9NfI`!9CkQNifxw#p4+ELp=xWYmR*Nh9p zdG_B+2FjzJg8NvgFQUIL7eh&Xkg%BZP$tMQr<0Tot^mJ zZ%6xQOUmr}32G{UWF9q~$(=LmXE@w{ss zupaeF@%JMhg=RP%7{@loF9vYx62;4PkEW6oo+Gdcy7_vMd87Y2BKU8HT*qECz}K-r z^~mQ)@b9YlHjvMS;4ffdrT*V-T)$TJtcTuD=u$;ZuT;&hu5pYOTG0FO~dB z#p5jVcm;nC3)H8e9KeKV5XbEh`ZCvA#AAp{-o)94`1)uI$o)d_O@xc#(+?@U8IJM& zMg3QUe%fzA^#|J>MqKmpr8tIoYcvkK0{t`zr+=R`U4=F>!Bu!1=tT|^!OMup@Oq&4 zPyF&U;_IW$P>6ZB{9n~iI9?Ar<2#x3L#`*7p9IHu@OULB&ofRh)sQ&q4DbTtG_GKO zenat5yjyd-91bwyxSa_bU(QdDTZWsSBB1D&@HLwW$e4~fWffi1G2EOnk+t(?5hF9U zF_pE)(^(_q+D_3(m&!0<=L%WVH8Y8hl_QKvD{mQT$4So^X5MvXVA4tFOe0guM8GPsJUq`gO$U6n8su&KjGrt3oD~| zZ$kqb%XT-QuV@?7>3k+@f-!s)qcT?BC>6~N;D4vsRz3qpCTlom)}BbaCRO8F6Gp+p zcw@X+3&R*aJRpOn>;NQ+GdVXsj@orRJuM~Nj$t|lNaStTOiblViGpJnOvjz^ zLE|M0_p(?ShWh%4+iCJu_;k8B4T;Q59-HvA>v$f%Z!a?p^x+`VMBq{(>q0_xGvS(L zEYsYh&sO1(Nh5u@RU(j^L zC}>K-Yo4l|W&Q;CBNm$R;EzjfeJJ}CgYjaE{tI$je0*7t#(Dp!#9@dMu|>a)C|x_! zzN`~v|EcUVrR$$WcruUgBS!fXzUGxfzp&RMd@Mi+nDB2Cx9sTv literal 0 HcmV?d00001 diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.c new file mode 100644 index 0000000000..0372fb1352 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.c @@ -0,0 +1,20 @@ +#include + +int foo = 23; + +int pure_function(int a, int b) { + return a + b; +} + +void impure_function() { + foo = 42; +} + +int main() { + printf("hello world, foo is %i\n", foo); + int result = pure_function(1,2); + printf("result: %i\n", result); + impure_function(); + printf("foo: %i\n", foo); + return 0; +} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll new file mode 100644 index 0000000000..1078c5ec4e --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll @@ -0,0 +1,57 @@ +; ModuleID = 'purity.c' +source_filename = "purity.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@foo = dso_local global i32 23, align 4 +@.str = private unnamed_addr constant [24 x i8] c"hello world, foo is %i\0A\00", align 1 +@.str.1 = private unnamed_addr constant [12 x i8] c"result: %i\0A\00", align 1 +@.str.2 = private unnamed_addr constant [8 x i8] c"foo: %i\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @pure_function(i32 %0, i32 %1) #0 { + %3 = alloca i32, align 4 + %4 = alloca i32, align 4 + store i32 %0, i32* %3, align 4 + store i32 %1, i32* %4, align 4 + %5 = load i32, i32* %3, align 4 + %6 = load i32, i32* %4, align 4 + %7 = add nsw i32 %5, %6 + ret i32 %7 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @impure_function() #0 { + store i32 42, i32* @foo, align 4 + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { + %1 = alloca i32, align 4 + %2 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %3 = load i32, i32* @foo, align 4 + %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([24 x i8], [24 x i8]* @.str, i64 0, i64 0), i32 %3) + %5 = call i32 @pure_function(i32 1, i32 2) + store i32 %5, i32* %2, align 4 + %6 = load i32, i32* %2, align 4 + %7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.1, i64 0, i64 0), i32 %6) + call void @impure_function() + %8 = load i32, i32* @foo, align 4 + %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i64 0, i64 0), i32 %8) + ret i32 0 +} + +declare dso_local i32 @printf(i8*, ...) #1 + +attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.module.flags = !{!0, !1, !2} +!llvm.ident = !{!3} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{!"Ubuntu clang version 13.0.0-2"} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala index 2726deb45a..0d683c873f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala @@ -12,13 +12,23 @@ class SimplePurityTests extends AnyFunSpec with Matchers { describe("SimplePurityAnalysis") { it("executes") { val project = Project(Traversable.empty) - project.updateProjectInformationKeyInitializationData(LLVMModulesKey)(current => - current.getOrElse(Iterable.empty) ++ List("./OPAL/ll/src/test/resources/org/opalj/ll/test.ll") + project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( + current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll") ) val (propertyStore, _) = project.get(FPCFAnalysesManagerKey).runAll(EagerSimplePurityAnalysis) - assert(propertyStore.finalEntities(Pure).size == 0) - assert(propertyStore.finalEntities(Impure).size == 4) + val impureFunctionNames = propertyStore + .finalEntities(Impure) + .asInstanceOf[Iterator[llvm.Function]] + .map(function ⇒ function.name()) + .toList + impureFunctionNames should contain("impure_function") + val pureFunctionNames = propertyStore + .finalEntities(Pure) + .asInstanceOf[Iterator[llvm.Function]] + .map(function ⇒ function.name()) + .toList + pureFunctionNames should contain("pure_function") } } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala deleted file mode 100644 index 5faae09ffd..0000000000 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Instruction.scala +++ /dev/null @@ -1,27 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll - -import org.bytedeco.llvm.LLVM.LLVMValueRef -import org.bytedeco.llvm.global.LLVM.{LLVMGetInstructionOpcode, LLVMGetValueKind, LLVMInstructionValueKind, LLVMAlloca, LLVMStore, LLVMLoad, LLVMAdd, LLVMRet} - -object Instruction { - def fromValue(ref: LLVMValueRef): Instruction = { - assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") - LLVMGetInstructionOpcode(ref) match { - case LLVMAlloca ⇒ Alloca(ref) - case LLVMStore ⇒ Store(ref) - case LLVMLoad ⇒ Load(ref) - case LLVMAdd ⇒ Add(ref) - case LLVMRet ⇒ Ret(ref) - case opCode ⇒ throw new IllegalArgumentException("unknown instruction opcode: "+opCode) - } - } -} - -sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) -case class Alloca(ref: LLVMValueRef) extends Instruction(ref) -case class Store(ref: LLVMValueRef) extends Instruction(ref) -case class Load(ref: LLVMValueRef) extends Instruction(ref) -case class Add(ref: LLVMValueRef) extends Instruction(ref) -case class Ret(ref: LLVMValueRef) extends Instruction(ref) - diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMFunctionsKey.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMFunctionsKey.scala deleted file mode 100644 index 83a4370fe3..0000000000 --- a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMFunctionsKey.scala +++ /dev/null @@ -1,13 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll - -import org.opalj.br.analyses.{ProjectInformationKey, ProjectInformationKeys, SomeProject} - -object LLVMFunctionsKey extends ProjectInformationKey[Iterable[Function], Nothing] { - override def requirements(project: SomeProject): ProjectInformationKeys = Seq(LLVMModulesKey) - - override def compute(project: SomeProject): Iterable[Function] = { - project.get(LLVMModulesKey).flatMap(module => module.functions()) - } - -} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMModulesKey.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMModulesKey.scala deleted file mode 100644 index 4918514e1a..0000000000 --- a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMModulesKey.scala +++ /dev/null @@ -1,20 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package ll - -import org.opalj.br.analyses.{ProjectInformationKey, SomeProject} - -object LLVMModulesKey extends ProjectInformationKey[Iterable[Module], Iterable[String]] { - /** - * The [[LLVMModulesKey]] has no special prerequisites. - * - * @return `Nil`. - */ - override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil - - override def compute(project: SomeProject): Iterable[Module] = { - val modules_paths = project.getOrCreateProjectInformationKeyInitializationData(this, Iterable.empty) - modules_paths.map(path ⇒ Reader.readIR(path).get) - } - -} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala new file mode 100644 index 0000000000..067adbc572 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +import org.opalj.ll.llvm.{Function, Module, Reader} + +class LLVMProject(val modules: Iterable[Module]) { + def functions(): Iterable[Function] = { + modules.flatMap(module ⇒ module.functions()) + } +} + +object LLVMProject { + def apply(modules_paths: Iterable[String]): LLVMProject = { + val modules = modules_paths.map(path ⇒ Reader.readIR(path).get) + val project = new LLVMProject(modules) + project + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProjectKey.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProjectKey.scala new file mode 100644 index 0000000000..d4a344d585 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProjectKey.scala @@ -0,0 +1,14 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj +package ll + +import org.opalj.br.analyses.{ProjectInformationKey, SomeProject} + +object LLVMProjectKey extends ProjectInformationKey[LLVMProject, Iterable[String]] { + override def requirements(project: SomeProject): Seq[ProjectInformationKey[Nothing, Nothing]] = Nil + + override def compute(project: SomeProject): LLVMProject = { + LLVMProject(project.getOrCreateProjectInformationKeyInitializationData(this, Iterable.empty)) + } + +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala index b0bf6fe887..6570c2edcd 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala @@ -2,7 +2,17 @@ package org.opalj.ll import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} import org.opalj.br.fpcf.{BasicFPCFEagerAnalysisScheduler, FPCFAnalysis, FPCFAnalysisScheduler} -import org.opalj.fpcf.{Entity, OrderedProperty, ProperPropertyComputationResult, PropertyBounds, PropertyKey, PropertyMetaInformation, PropertyStore, Result} +import org.opalj.fpcf.{ + Entity, + OrderedProperty, + ProperPropertyComputationResult, + PropertyBounds, + PropertyKey, + PropertyMetaInformation, + PropertyStore, + Result +} +import org.opalj.ll.llvm.{Function, GlobalVariable, Store} sealed trait SimplePurityPropertyMetaInformation extends PropertyMetaInformation { final type Self = SimplePurity @@ -38,26 +48,47 @@ object SimplePurity extends SimplePurityPropertyMetaInformation { class SimplePurityAnalysis(val project: SomeProject) extends FPCFAnalysis { def analyzeSimplePurity(function: Function): ProperPropertyComputationResult = { - Result(function, Impure) + function + .basicBlocks() + .flatMap(_.instructions()) + .foreach { + case instruction: Store ⇒ + instruction.dst() match { + case _: GlobalVariable ⇒ + return Result(function, Impure) + case _ ⇒ Unit + } + case _ ⇒ Unit + } + Result(function, Pure) } } trait SimplePurityAnalysisScheduler extends FPCFAnalysisScheduler { def derivedProperty: PropertyBounds = PropertyBounds.ub(SimplePurity) - override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMFunctionsKey) + override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) override def uses: Set[PropertyBounds] = Set.empty // TODO: check this later } -object EagerSimplePurityAnalysis extends SimplePurityAnalysisScheduler with BasicFPCFEagerAnalysisScheduler { +object EagerSimplePurityAnalysis + extends SimplePurityAnalysisScheduler + with BasicFPCFEagerAnalysisScheduler { override def derivesEagerly: Set[PropertyBounds] = Set(derivedProperty) override def derivesCollaboratively: Set[PropertyBounds] = Set.empty - override def start(project: SomeProject, propertyStore: PropertyStore, initData: InitializationData): FPCFAnalysis = { + override def start( + project: SomeProject, + propertyStore: PropertyStore, + initData: InitializationData + ): FPCFAnalysis = { val analysis = new SimplePurityAnalysis(project) - propertyStore.scheduleEagerComputationsForEntities(project.get(LLVMFunctionsKey))(analysis.analyzeSimplePurity) + val llvm_project = project.get(LLVMProjectKey) + propertyStore.scheduleEagerComputationsForEntities(llvm_project.functions())( + analysis.analyzeSimplePurity + ) analysis } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala deleted file mode 100644 index 5678a562b3..0000000000 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Value.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll - -import org.bytedeco.llvm.LLVM.LLVMValueRef -import org.bytedeco.llvm.global.LLVM.{LLVMDisposeMessage, LLVMGetValueName, LLVMPrintValueToString} - -abstract class Value(ref: LLVMValueRef) { - def repr(): String = { - val bytePointer = LLVMPrintValueToString(ref) - val string = bytePointer.getString - LLVMDisposeMessage(bytePointer) - string - } - - def name(): String = { - LLVMGetValueName(ref).getString - } -} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala similarity index 50% rename from OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala index 97a87a588c..633523464e 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/BasicBlock.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala @@ -1,20 +1,32 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll +package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} -import org.bytedeco.llvm.global.LLVM.{LLVMGetFirstInstruction, LLVMGetNextInstruction, LLVMBasicBlockAsValue} +import org.bytedeco.llvm.global.LLVM.{ + LLVMBasicBlockAsValue, + LLVMGetBasicBlockTerminator, + LLVMGetFirstInstruction, + LLVMGetNextInstruction +} -case class BasicBlock(block_ref: LLVMBasicBlockRef) extends Value(LLVMBasicBlockAsValue(block_ref)) { +case class BasicBlock(block_ref: LLVMBasicBlockRef) + extends Value(LLVMBasicBlockAsValue(block_ref)) { def instructions(): InstructionIterator = { new InstructionIterator(LLVMGetFirstInstruction(block_ref)) } + + def terminator(): Instruction = { + val instruction = Instruction(LLVMGetBasicBlockTerminator(block_ref)) + assert(instruction.is_terminator()) + instruction + } } class InstructionIterator(var ref: LLVMValueRef) extends Iterator[Instruction] { override def hasNext: Boolean = LLVMGetNextInstruction(ref) != null override def next(): Instruction = { - val instruction = Instruction.fromValue(ref) + val instruction = Instruction(ref) this.ref = LLVMGetNextInstruction(ref) instruction } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala similarity index 79% rename from OPAL/ll/src/main/scala/org/opalj/ll/Function.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala index 56e0e455f5..4a3f6ae652 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Function.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala @@ -1,8 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll +package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} -import org.bytedeco.llvm.global.LLVM.{LLVMFunctionValueKind, LLVMGetFirstBasicBlock, LLVMGetNextBasicBlock, LLVMGetValueKind} +import org.bytedeco.llvm.global.LLVM.{ + LLVMFunctionValueKind, + LLVMGetFirstBasicBlock, + LLVMGetNextBasicBlock, + LLVMGetValueKind +} case class Function(ref: LLVMValueRef) extends Value(ref) { assert(LLVMGetValueKind(ref) == LLVMFunctionValueKind, "ref has to be a function") @@ -20,4 +25,4 @@ class BasicBlockIterator(var ref: LLVMBasicBlockRef) extends Iterator[BasicBlock this.ref = LLVMGetNextBasicBlock(ref) basicBlock } -} \ No newline at end of file +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/GlobalVariable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/GlobalVariable.scala new file mode 100644 index 0000000000..4ded4873ba --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/GlobalVariable.scala @@ -0,0 +1,7 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ + +package org.opalj.ll.llvm + +import org.bytedeco.llvm.LLVM.LLVMValueRef + +case class GlobalVariable(ref: LLVMValueRef) extends Value(ref: LLVMValueRef) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala new file mode 100644 index 0000000000..b556d17469 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala @@ -0,0 +1,161 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM._ + +object Instruction { + def apply(ref: LLVMValueRef): Instruction = { + assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") + LLVMGetInstructionOpcode(ref) match { + case LLVMRet ⇒ Ret(ref) + case LLVMBr ⇒ Br(ref) + case LLVMSwitch ⇒ Switch(ref) + case LLVMIndirectBr ⇒ IndirectBr(ref) + case LLVMInvoke ⇒ Invoke(ref) + case LLVMUnreachable ⇒ Unreachable(ref) + case LLVMCallBr ⇒ CallBr(ref) + case LLVMFNeg ⇒ FNeg(ref) + case LLVMAdd ⇒ Add(ref) + case LLVMFAdd ⇒ FAdd(ref) + case LLVMSub ⇒ Sub(ref) + case LLVMFSub ⇒ FSub(ref) + case LLVMMul ⇒ Mul(ref) + case LLVMFMul ⇒ FMul(ref) + case LLVMUDiv ⇒ UDiv(ref) + case LLVMSDiv ⇒ SDiv(ref) + case LLVMFDiv ⇒ FDiv(ref) + case LLVMURem ⇒ URem(ref) + case LLVMSRem ⇒ SRem(ref) + case LLVMFRem ⇒ FRem(ref) + case LLVMShl ⇒ Shl(ref) + case LLVMLShr ⇒ LShr(ref) + case LLVMAShr ⇒ AShr(ref) + case LLVMAnd ⇒ And(ref) + case LLVMOr ⇒ Or(ref) + case LLVMXor ⇒ Xor(ref) + case LLVMAlloca ⇒ Alloca(ref) + case LLVMLoad ⇒ Load(ref) + case LLVMStore ⇒ Store(ref) + case LLVMGetElementPtr ⇒ GetElementPtr(ref) + case LLVMTrunc ⇒ Trunc(ref) + case LLVMZExt ⇒ ZExt(ref) + case LLVMSExt ⇒ SExt(ref) + case LLVMFPToUI ⇒ FPToUI(ref) + case LLVMFPToSI ⇒ FPToSI(ref) + case LLVMUIToFP ⇒ UIToFP(ref) + case LLVMSIToFP ⇒ SIToFP(ref) + case LLVMFPTrunc ⇒ FPTrunc(ref) + case LLVMFPExt ⇒ FPExt(ref) + case LLVMPtrToInt ⇒ PtrToInt(ref) + case LLVMIntToPtr ⇒ IntToPtr(ref) + case LLVMBitCast ⇒ BitCast(ref) + case LLVMAddrSpaceCast ⇒ AddrSpaceCast(ref) + case LLVMICmp ⇒ ICmp(ref) + case LLVMFCmp ⇒ FCmp(ref) + case LLVMPHI ⇒ PHI(ref) + case LLVMCall ⇒ Call(ref) + case LLVMSelect ⇒ Select(ref) + case LLVMUserOp1 ⇒ UserOp1(ref) + case LLVMUserOp2 ⇒ UserOp2(ref) + case LLVMVAArg ⇒ VAArg(ref) + case LLVMExtractElement ⇒ ExtractElement(ref) + case LLVMInsertElement ⇒ InsertElement(ref) + case LLVMShuffleVector ⇒ ShuffleVector(ref) + case LLVMExtractValue ⇒ ExtractValue(ref) + case LLVMInsertValue ⇒ InsertValue(ref) + case LLVMFreeze ⇒ Freeze(ref) + case LLVMFence ⇒ Fence(ref) + case LLVMAtomicCmpXchg ⇒ AtomicCmpXchg(ref) + case LLVMAtomicRMW ⇒ AtomicRMW(ref) + case LLVMResume ⇒ Resume(ref) + case LLVMLandingPad ⇒ LandingPad(ref) + case LLVMCleanupRet ⇒ CleanupRet(ref) + case LLVMCatchRet ⇒ CatchRet(ref) + case LLVMCatchPad ⇒ CatchPad(ref) + case LLVMCleanupPad ⇒ CleanupPad(ref) + case LLVMCatchSwitch ⇒ CatchSwitch(ref) + case opCode ⇒ throw new IllegalArgumentException("unknown instruction opcode: "+opCode) + } + } +} + +sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { + def is_terminator(): Boolean = !LLVMIsATerminatorInst(ref).isNull +} + +case class Ret(ref: LLVMValueRef) extends Instruction(ref) +case class Br(ref: LLVMValueRef) extends Instruction(ref) +case class Switch(ref: LLVMValueRef) extends Instruction(ref) +case class IndirectBr(ref: LLVMValueRef) extends Instruction(ref) +case class Invoke(ref: LLVMValueRef) extends Instruction(ref) +case class Unreachable(ref: LLVMValueRef) extends Instruction(ref) +case class CallBr(ref: LLVMValueRef) extends Instruction(ref) +case class FNeg(ref: LLVMValueRef) extends Instruction(ref) +case class Add(ref: LLVMValueRef) extends Instruction(ref) +case class FAdd(ref: LLVMValueRef) extends Instruction(ref) +case class Sub(ref: LLVMValueRef) extends Instruction(ref) +case class FSub(ref: LLVMValueRef) extends Instruction(ref) +case class Mul(ref: LLVMValueRef) extends Instruction(ref) +case class FMul(ref: LLVMValueRef) extends Instruction(ref) +case class UDiv(ref: LLVMValueRef) extends Instruction(ref) +case class SDiv(ref: LLVMValueRef) extends Instruction(ref) +case class FDiv(ref: LLVMValueRef) extends Instruction(ref) +case class URem(ref: LLVMValueRef) extends Instruction(ref) +case class SRem(ref: LLVMValueRef) extends Instruction(ref) +case class FRem(ref: LLVMValueRef) extends Instruction(ref) +case class Shl(ref: LLVMValueRef) extends Instruction(ref) +case class LShr(ref: LLVMValueRef) extends Instruction(ref) +case class AShr(ref: LLVMValueRef) extends Instruction(ref) +case class And(ref: LLVMValueRef) extends Instruction(ref) +case class Or(ref: LLVMValueRef) extends Instruction(ref) +case class Xor(ref: LLVMValueRef) extends Instruction(ref) +case class Alloca(ref: LLVMValueRef) extends Instruction(ref) +case class Load(ref: LLVMValueRef) extends Instruction(ref) +case class Store(ref: LLVMValueRef) extends Instruction(ref) { + def src(): Value = { + Value(LLVMGetOperand(ref, 0)) + } + + def dst(): Value = { + Value(LLVMGetOperand(ref, 1)) + } +} +case class GetElementPtr(ref: LLVMValueRef) extends Instruction(ref) +case class Trunc(ref: LLVMValueRef) extends Instruction(ref) +case class ZExt(ref: LLVMValueRef) extends Instruction(ref) +case class SExt(ref: LLVMValueRef) extends Instruction(ref) +case class FPToUI(ref: LLVMValueRef) extends Instruction(ref) +case class FPToSI(ref: LLVMValueRef) extends Instruction(ref) +case class UIToFP(ref: LLVMValueRef) extends Instruction(ref) +case class SIToFP(ref: LLVMValueRef) extends Instruction(ref) +case class FPTrunc(ref: LLVMValueRef) extends Instruction(ref) +case class FPExt(ref: LLVMValueRef) extends Instruction(ref) +case class PtrToInt(ref: LLVMValueRef) extends Instruction(ref) +case class IntToPtr(ref: LLVMValueRef) extends Instruction(ref) +case class BitCast(ref: LLVMValueRef) extends Instruction(ref) +case class AddrSpaceCast(ref: LLVMValueRef) extends Instruction(ref) +case class ICmp(ref: LLVMValueRef) extends Instruction(ref) +case class FCmp(ref: LLVMValueRef) extends Instruction(ref) +case class PHI(ref: LLVMValueRef) extends Instruction(ref) +case class Call(ref: LLVMValueRef) extends Instruction(ref) +case class Select(ref: LLVMValueRef) extends Instruction(ref) +case class UserOp1(ref: LLVMValueRef) extends Instruction(ref) +case class UserOp2(ref: LLVMValueRef) extends Instruction(ref) +case class VAArg(ref: LLVMValueRef) extends Instruction(ref) +case class ExtractElement(ref: LLVMValueRef) extends Instruction(ref) +case class InsertElement(ref: LLVMValueRef) extends Instruction(ref) +case class ShuffleVector(ref: LLVMValueRef) extends Instruction(ref) +case class ExtractValue(ref: LLVMValueRef) extends Instruction(ref) +case class InsertValue(ref: LLVMValueRef) extends Instruction(ref) +case class Freeze(ref: LLVMValueRef) extends Instruction(ref) +case class Fence(ref: LLVMValueRef) extends Instruction(ref) +case class AtomicCmpXchg(ref: LLVMValueRef) extends Instruction(ref) +case class AtomicRMW(ref: LLVMValueRef) extends Instruction(ref) +case class Resume(ref: LLVMValueRef) extends Instruction(ref) +case class LandingPad(ref: LLVMValueRef) extends Instruction(ref) +case class CleanupRet(ref: LLVMValueRef) extends Instruction(ref) +case class CatchRet(ref: LLVMValueRef) extends Instruction(ref) +case class CatchPad(ref: LLVMValueRef) extends Instruction(ref) +case class CleanupPad(ref: LLVMValueRef) extends Instruction(ref) +case class CatchSwitch(ref: LLVMValueRef) extends Instruction(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala similarity index 80% rename from OPAL/ll/src/main/scala/org/opalj/ll/Module.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala index 9a795364fe..31bd8737e3 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Module.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala @@ -1,8 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll +package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMModuleRef, LLVMValueRef} -import org.bytedeco.llvm.global.LLVM.{LLVMDisposeMessage, LLVMGetFirstFunction, LLVMGetNextFunction, LLVMPrintModuleToString} +import org.bytedeco.llvm.global.LLVM.{ + LLVMDisposeMessage, + LLVMGetFirstFunction, + LLVMGetNextFunction, + LLVMPrintModuleToString +} case class Module(ref: LLVMModuleRef) { def functions(): FunctionIterator = { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Reader.scala similarity index 85% rename from OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/Reader.scala index 6905982a18..f84d937233 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/Reader.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Reader.scala @@ -1,10 +1,15 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package ll + +package org.opalj.ll.llvm import org.bytedeco.javacpp.BytePointer import org.bytedeco.llvm.LLVM.{LLVMContextRef, LLVMMemoryBufferRef, LLVMModuleRef} -import org.bytedeco.llvm.global.LLVM.{LLVMContextCreate, LLVMCreateMemoryBufferWithContentsOfFile, LLVMDisposeMessage, LLVMParseIRInContext} +import org.bytedeco.llvm.global.LLVM.{ + LLVMContextCreate, + LLVMCreateMemoryBufferWithContentsOfFile, + LLVMDisposeMessage, + LLVMParseIRInContext +} object Reader { def readIR(path: String): Option[Module] = { @@ -25,4 +30,4 @@ object Reader { } Some(Module(module)) } -} \ No newline at end of file +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala new file mode 100644 index 0000000000..81df114e8d --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala @@ -0,0 +1,52 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM._ + +class Value(ref: LLVMValueRef) { + def repr(): String = { + val bytePointer = LLVMPrintValueToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } + + def name(): String = { + LLVMGetValueName(ref).getString + } +} + +object Value { + def apply(ref: LLVMValueRef): Value = { + LLVMGetValueKind(ref) match { + //LLVMArgumentValueKind + case LLVMBasicBlockValueKind ⇒ BasicBlock(LLVMValueAsBasicBlock(ref)) + //LLVMMemoryUseValueKind + //LLVMMemoryDefValueKind + //LLVMMemoryPhiValueKind + case LLVMFunctionValueKind ⇒ Function(ref) + //LLVMGlobalAliasValueKind + //LLVMGlobalIFuncValueKind + case LLVMGlobalVariableValueKind ⇒ GlobalVariable(ref) + //LLVMBlockAddressValueKind + //LLVMConstantExprValueKind + //LLVMConstantArrayValueKind + //LLVMConstantStructValueKind + //LLVMConstantVectorValueKind + //LLVMUndefValueValueKind + //LLVMConstantAggregateZeroValueKind + //LLVMConstantDataArrayValueKind + //LLVMConstantDataVectorValueKind + //LLVMConstantIntValueKind + //LLVMConstantFPValueKind + //LLVMConstantPointerNullValueKind + //LLVMConstantTokenNoneValueKind + //LLVMMetadataAsValueValueKind + //LLVMInlineAsmValueKind + case LLVMInstructionValueKind ⇒ Instruction(ref) + //LLVMPoisonValueValueKind + case valueKind ⇒ throw new IllegalArgumentException("unknown valueKind: "+valueKind) + } + } +} From df115618664d610b7200953db1c24fde6fd2cceb Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 12 Nov 2021 18:27:23 +0100 Subject: [PATCH 10/67] Implement viewCFG --- .../scala/org/opalj/ll/LLPlayground.scala | 21 +- .../scala/org/opalj/ll/llvm/BasicBlock.scala | 75 +- .../scala/org/opalj/ll/llvm/Function.scala | 49 +- .../scala/org/opalj/ll/llvm/Instruction.scala | 190 +-- .../main/scala/org/opalj/ll/llvm/Module.scala | 46 +- .../main/scala/org/opalj/ll/llvm/Value.scala | 86 +- .../ll/src/test/resources/org/opalj/ll/jsmn.h | 471 +++++++ .../ll/src/test/resources/org/opalj/ll/test.c | 19 + .../test/resources/org/opalj/ll/test_jsmn.c | 13 + .../test/resources/org/opalj/ll/test_jsmn.ll | 1223 +++++++++++++++++ 10 files changed, 1998 insertions(+), 195 deletions(-) create mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/jsmn.h create mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/test.c create mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.c create mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala index 25f59aec25..58b9f3b7e6 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala @@ -6,18 +6,11 @@ import org.opalj.ll.llvm.Reader object LLPlayground { - def main(args: Array[String]): Unit = { - val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test.ll").get - //println(module.repr) - println(module.functions.map(f ⇒ f.name).toList) - //println(module.functions.map(f => f.repr).toList) - val functions = module.functions() - val function = functions.next() - //print(function.repr) - val block = function.basicBlocks().next(); - print(block.repr); - val instruction = block.instructions().next(); - println("Instruction: "+instruction.repr()) - println(block.instructions.toList) - } + def main(args: Array[String]): Unit = { + val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll").get + val function = module.get_function("jsmn_parse_string") + println(function.name) + function.viewLLVMCFG(false) + function.viewCFG() + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala index 633523464e..e7ba8bc079 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala @@ -3,31 +3,66 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} import org.bytedeco.llvm.global.LLVM.{ - LLVMBasicBlockAsValue, - LLVMGetBasicBlockTerminator, - LLVMGetFirstInstruction, - LLVMGetNextInstruction + LLVMBasicBlockAsValue, + LLVMGetBasicBlockName, + LLVMGetBasicBlockTerminator, + LLVMGetFirstInstruction, + LLVMGetNextInstruction } +import org.opalj.graphs.Node case class BasicBlock(block_ref: LLVMBasicBlockRef) - extends Value(LLVMBasicBlockAsValue(block_ref)) { - def instructions(): InstructionIterator = { - new InstructionIterator(LLVMGetFirstInstruction(block_ref)) - } - - def terminator(): Instruction = { - val instruction = Instruction(LLVMGetBasicBlockTerminator(block_ref)) - assert(instruction.is_terminator()) - instruction - } + extends Value(LLVMBasicBlockAsValue(block_ref)) + with Node { + def instructions(): InstructionIterator = { + new InstructionIterator(LLVMGetFirstInstruction(block_ref)) + } + + def terminator(): Terminator = { + val instruction = Instruction(LLVMGetBasicBlockTerminator(block_ref)) + assert(instruction.is_terminator()) + instruction.asInstanceOf[Terminator] + } + + def blockName(): String = LLVMGetBasicBlockName(block_ref).getString + + /** + * Returns a human readable representation (HRR) of this node. + */ + override def toHRR: Option[String] = { + Some(name()) //TODO: maybe add more info + } + + /** + * An identifier that uniquely identifies this node in the graph to which this + * node belongs. By default two nodes are considered equal if they have the same + * unique id. + */ + override def nodeId: Int = { + block_ref.hashCode + } + + /** + * Returns `true` if this node has successor nodes. + */ + override def hasSuccessors: Boolean = { + terminator.hasSuccessors() + } + + /** + * Applies the given function for each successor node. + */ + override def foreachSuccessor(f: Node => Unit): Unit = { + terminator.foreachSuccessor(f) + } } class InstructionIterator(var ref: LLVMValueRef) extends Iterator[Instruction] { - override def hasNext: Boolean = LLVMGetNextInstruction(ref) != null + override def hasNext: Boolean = LLVMGetNextInstruction(ref) != null - override def next(): Instruction = { - val instruction = Instruction(ref) - this.ref = LLVMGetNextInstruction(ref) - instruction - } + override def next(): Instruction = { + val instruction = Instruction(ref) + this.ref = LLVMGetNextInstruction(ref) + instruction + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala index 4a3f6ae652..fd9031c372 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala @@ -3,26 +3,49 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} import org.bytedeco.llvm.global.LLVM.{ - LLVMFunctionValueKind, - LLVMGetFirstBasicBlock, - LLVMGetNextBasicBlock, - LLVMGetValueKind + LLVMFunctionValueKind, + LLVMGetEntryBasicBlock, + LLVMGetFirstBasicBlock, + LLVMGetNextBasicBlock, + LLVMGetValueKind, + LLVMViewFunctionCFG, + LLVMViewFunctionCFGOnly } +import org.opalj.io.writeAndOpen case class Function(ref: LLVMValueRef) extends Value(ref) { - assert(LLVMGetValueKind(ref) == LLVMFunctionValueKind, "ref has to be a function") + assert(LLVMGetValueKind(ref) == LLVMFunctionValueKind, "ref has to be a function") - def basicBlocks(): BasicBlockIterator = { - new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) + def basicBlocks(): BasicBlockIterator = { + new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) + } + + def entryBlock(): BasicBlock = { + BasicBlock(LLVMGetEntryBasicBlock(ref)) + } + + def viewCFG(): Unit = { + val entryNode = entryBlock() + val cfg_dot = org.opalj.graphs.toDot(Set(entryNode)) + writeAndOpen(cfg_dot, name + "-CFG", ".gv") + } + + def viewLLVMCFG(include_content: Boolean = true): Unit = { + if (include_content) { + LLVMViewFunctionCFG(ref) + } else { + LLVMViewFunctionCFGOnly(ref) } + } + } class BasicBlockIterator(var ref: LLVMBasicBlockRef) extends Iterator[BasicBlock] { - override def hasNext: Boolean = ref != null + override def hasNext: Boolean = ref != null - override def next(): BasicBlock = { - val basicBlock = BasicBlock(ref) - this.ref = LLVMGetNextBasicBlock(ref) - basicBlock - } + override def next(): BasicBlock = { + val basicBlock = BasicBlock(ref) + this.ref = LLVMGetNextBasicBlock(ref) + basicBlock + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala index b556d17469..72f7d4b86f 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala @@ -5,92 +5,102 @@ import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM._ object Instruction { - def apply(ref: LLVMValueRef): Instruction = { - assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") - LLVMGetInstructionOpcode(ref) match { - case LLVMRet ⇒ Ret(ref) - case LLVMBr ⇒ Br(ref) - case LLVMSwitch ⇒ Switch(ref) - case LLVMIndirectBr ⇒ IndirectBr(ref) - case LLVMInvoke ⇒ Invoke(ref) - case LLVMUnreachable ⇒ Unreachable(ref) - case LLVMCallBr ⇒ CallBr(ref) - case LLVMFNeg ⇒ FNeg(ref) - case LLVMAdd ⇒ Add(ref) - case LLVMFAdd ⇒ FAdd(ref) - case LLVMSub ⇒ Sub(ref) - case LLVMFSub ⇒ FSub(ref) - case LLVMMul ⇒ Mul(ref) - case LLVMFMul ⇒ FMul(ref) - case LLVMUDiv ⇒ UDiv(ref) - case LLVMSDiv ⇒ SDiv(ref) - case LLVMFDiv ⇒ FDiv(ref) - case LLVMURem ⇒ URem(ref) - case LLVMSRem ⇒ SRem(ref) - case LLVMFRem ⇒ FRem(ref) - case LLVMShl ⇒ Shl(ref) - case LLVMLShr ⇒ LShr(ref) - case LLVMAShr ⇒ AShr(ref) - case LLVMAnd ⇒ And(ref) - case LLVMOr ⇒ Or(ref) - case LLVMXor ⇒ Xor(ref) - case LLVMAlloca ⇒ Alloca(ref) - case LLVMLoad ⇒ Load(ref) - case LLVMStore ⇒ Store(ref) - case LLVMGetElementPtr ⇒ GetElementPtr(ref) - case LLVMTrunc ⇒ Trunc(ref) - case LLVMZExt ⇒ ZExt(ref) - case LLVMSExt ⇒ SExt(ref) - case LLVMFPToUI ⇒ FPToUI(ref) - case LLVMFPToSI ⇒ FPToSI(ref) - case LLVMUIToFP ⇒ UIToFP(ref) - case LLVMSIToFP ⇒ SIToFP(ref) - case LLVMFPTrunc ⇒ FPTrunc(ref) - case LLVMFPExt ⇒ FPExt(ref) - case LLVMPtrToInt ⇒ PtrToInt(ref) - case LLVMIntToPtr ⇒ IntToPtr(ref) - case LLVMBitCast ⇒ BitCast(ref) - case LLVMAddrSpaceCast ⇒ AddrSpaceCast(ref) - case LLVMICmp ⇒ ICmp(ref) - case LLVMFCmp ⇒ FCmp(ref) - case LLVMPHI ⇒ PHI(ref) - case LLVMCall ⇒ Call(ref) - case LLVMSelect ⇒ Select(ref) - case LLVMUserOp1 ⇒ UserOp1(ref) - case LLVMUserOp2 ⇒ UserOp2(ref) - case LLVMVAArg ⇒ VAArg(ref) - case LLVMExtractElement ⇒ ExtractElement(ref) - case LLVMInsertElement ⇒ InsertElement(ref) - case LLVMShuffleVector ⇒ ShuffleVector(ref) - case LLVMExtractValue ⇒ ExtractValue(ref) - case LLVMInsertValue ⇒ InsertValue(ref) - case LLVMFreeze ⇒ Freeze(ref) - case LLVMFence ⇒ Fence(ref) - case LLVMAtomicCmpXchg ⇒ AtomicCmpXchg(ref) - case LLVMAtomicRMW ⇒ AtomicRMW(ref) - case LLVMResume ⇒ Resume(ref) - case LLVMLandingPad ⇒ LandingPad(ref) - case LLVMCleanupRet ⇒ CleanupRet(ref) - case LLVMCatchRet ⇒ CatchRet(ref) - case LLVMCatchPad ⇒ CatchPad(ref) - case LLVMCleanupPad ⇒ CleanupPad(ref) - case LLVMCatchSwitch ⇒ CatchSwitch(ref) - case opCode ⇒ throw new IllegalArgumentException("unknown instruction opcode: "+opCode) - } + def apply(ref: LLVMValueRef): Instruction = { + assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") + LLVMGetInstructionOpcode(ref) match { + case LLVMRet => Ret(ref) + case LLVMBr => Br(ref) + case LLVMSwitch => Switch(ref) + case LLVMIndirectBr => IndirectBr(ref) + case LLVMInvoke => Invoke(ref) + case LLVMUnreachable => Unreachable(ref) + case LLVMCallBr => CallBr(ref) + case LLVMFNeg => FNeg(ref) + case LLVMAdd => Add(ref) + case LLVMFAdd => FAdd(ref) + case LLVMSub => Sub(ref) + case LLVMFSub => FSub(ref) + case LLVMMul => Mul(ref) + case LLVMFMul => FMul(ref) + case LLVMUDiv => UDiv(ref) + case LLVMSDiv => SDiv(ref) + case LLVMFDiv => FDiv(ref) + case LLVMURem => URem(ref) + case LLVMSRem => SRem(ref) + case LLVMFRem => FRem(ref) + case LLVMShl => Shl(ref) + case LLVMLShr => LShr(ref) + case LLVMAShr => AShr(ref) + case LLVMAnd => And(ref) + case LLVMOr => Or(ref) + case LLVMXor => Xor(ref) + case LLVMAlloca => Alloca(ref) + case LLVMLoad => Load(ref) + case LLVMStore => Store(ref) + case LLVMGetElementPtr => GetElementPtr(ref) + case LLVMTrunc => Trunc(ref) + case LLVMZExt => ZExt(ref) + case LLVMSExt => SExt(ref) + case LLVMFPToUI => FPToUI(ref) + case LLVMFPToSI => FPToSI(ref) + case LLVMUIToFP => UIToFP(ref) + case LLVMSIToFP => SIToFP(ref) + case LLVMFPTrunc => FPTrunc(ref) + case LLVMFPExt => FPExt(ref) + case LLVMPtrToInt => PtrToInt(ref) + case LLVMIntToPtr => IntToPtr(ref) + case LLVMBitCast => BitCast(ref) + case LLVMAddrSpaceCast => AddrSpaceCast(ref) + case LLVMICmp => ICmp(ref) + case LLVMFCmp => FCmp(ref) + case LLVMPHI => PHI(ref) + case LLVMCall => Call(ref) + case LLVMSelect => Select(ref) + case LLVMUserOp1 => UserOp1(ref) + case LLVMUserOp2 => UserOp2(ref) + case LLVMVAArg => VAArg(ref) + case LLVMExtractElement => ExtractElement(ref) + case LLVMInsertElement => InsertElement(ref) + case LLVMShuffleVector => ShuffleVector(ref) + case LLVMExtractValue => ExtractValue(ref) + case LLVMInsertValue => InsertValue(ref) + case LLVMFreeze => Freeze(ref) + case LLVMFence => Fence(ref) + case LLVMAtomicCmpXchg => AtomicCmpXchg(ref) + case LLVMAtomicRMW => AtomicRMW(ref) + case LLVMResume => Resume(ref) + case LLVMLandingPad => LandingPad(ref) + case LLVMCleanupRet => CleanupRet(ref) + case LLVMCatchRet => CatchRet(ref) + case LLVMCatchPad => CatchPad(ref) + case LLVMCleanupPad => CleanupPad(ref) + case LLVMCatchSwitch => CatchSwitch(ref) + case opCode => throw new IllegalArgumentException("unknown instruction opcode: " + opCode) } + } +} + +trait Terminator { + val ref: LLVMValueRef + def numSuccessors(): Int = LLVMGetNumSuccessors(ref) + def hasSuccessors(): Boolean = numSuccessors() > 0 + def getSuccessor(i: Int) = BasicBlock(LLVMGetSuccessor(ref, i)) + def foreachSuccessor(f: BasicBlock => Unit): Unit = { + (0 to numSuccessors() - 1).foreach(i => f(getSuccessor(i))) + } } sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { - def is_terminator(): Boolean = !LLVMIsATerminatorInst(ref).isNull + def is_terminator(): Boolean = !LLVMIsATerminatorInst(ref).isNull } -case class Ret(ref: LLVMValueRef) extends Instruction(ref) -case class Br(ref: LLVMValueRef) extends Instruction(ref) -case class Switch(ref: LLVMValueRef) extends Instruction(ref) -case class IndirectBr(ref: LLVMValueRef) extends Instruction(ref) -case class Invoke(ref: LLVMValueRef) extends Instruction(ref) -case class Unreachable(ref: LLVMValueRef) extends Instruction(ref) -case class CallBr(ref: LLVMValueRef) extends Instruction(ref) +case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class Br(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class Switch(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class IndirectBr(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class Invoke(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class Unreachable(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class CallBr(ref: LLVMValueRef) extends Instruction(ref) with Terminator case class FNeg(ref: LLVMValueRef) extends Instruction(ref) case class Add(ref: LLVMValueRef) extends Instruction(ref) case class FAdd(ref: LLVMValueRef) extends Instruction(ref) @@ -113,13 +123,13 @@ case class Xor(ref: LLVMValueRef) extends Instruction(ref) case class Alloca(ref: LLVMValueRef) extends Instruction(ref) case class Load(ref: LLVMValueRef) extends Instruction(ref) case class Store(ref: LLVMValueRef) extends Instruction(ref) { - def src(): Value = { - Value(LLVMGetOperand(ref, 0)) - } + def src(): Value = { + Value(LLVMGetOperand(ref, 0)).asInstanceOf[Value] + } - def dst(): Value = { - Value(LLVMGetOperand(ref, 1)) - } + def dst(): Value = { + Value(LLVMGetOperand(ref, 1)).asInstanceOf[Value] + } } case class GetElementPtr(ref: LLVMValueRef) extends Instruction(ref) case class Trunc(ref: LLVMValueRef) extends Instruction(ref) @@ -152,10 +162,10 @@ case class Freeze(ref: LLVMValueRef) extends Instruction(ref) case class Fence(ref: LLVMValueRef) extends Instruction(ref) case class AtomicCmpXchg(ref: LLVMValueRef) extends Instruction(ref) case class AtomicRMW(ref: LLVMValueRef) extends Instruction(ref) -case class Resume(ref: LLVMValueRef) extends Instruction(ref) +case class Resume(ref: LLVMValueRef) extends Instruction(ref) with Terminator case class LandingPad(ref: LLVMValueRef) extends Instruction(ref) -case class CleanupRet(ref: LLVMValueRef) extends Instruction(ref) -case class CatchRet(ref: LLVMValueRef) extends Instruction(ref) +case class CleanupRet(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class CatchRet(ref: LLVMValueRef) extends Instruction(ref) with Terminator case class CatchPad(ref: LLVMValueRef) extends Instruction(ref) case class CleanupPad(ref: LLVMValueRef) extends Instruction(ref) -case class CatchSwitch(ref: LLVMValueRef) extends Instruction(ref) +case class CatchSwitch(ref: LLVMValueRef) extends Instruction(ref) with Terminator diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala index 31bd8737e3..33a8a30783 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala @@ -3,31 +3,41 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMModuleRef, LLVMValueRef} import org.bytedeco.llvm.global.LLVM.{ - LLVMDisposeMessage, - LLVMGetFirstFunction, - LLVMGetNextFunction, - LLVMPrintModuleToString + LLVMDisposeMessage, + LLVMGetFirstFunction, + LLVMGetNamedFunction, + LLVMGetNextFunction, + LLVMPrintModuleToString } case class Module(ref: LLVMModuleRef) { - def functions(): FunctionIterator = { - new FunctionIterator(LLVMGetFirstFunction(ref)) - } + def functions(): FunctionIterator = { + new FunctionIterator(LLVMGetFirstFunction(ref)) + } + + def repr(): String = { + val bytePointer = LLVMPrintModuleToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } - def repr(): String = { - val bytePointer = LLVMPrintModuleToString(ref) - val string = bytePointer.getString - LLVMDisposeMessage(bytePointer) - string + def get_function(name: String): Function = { + val func_ref = LLVMGetNamedFunction(ref, name) + Value(func_ref) match { + case _: NullValue => + throw new IllegalArgumentException("Unknown function '" + name + "'") + case function: Function => function } + } } class FunctionIterator(var ref: LLVMValueRef) extends Iterator[Function] { - override def hasNext: Boolean = ref != null + override def hasNext: Boolean = ref != null - override def next(): Function = { - val function = Function(ref) - this.ref = LLVMGetNextFunction(ref) - function - } + override def next(): Function = { + val function = Function(ref) + this.ref = LLVMGetNextFunction(ref) + function + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala index 81df114e8d..2bebae7df7 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala @@ -4,49 +4,55 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM._ -class Value(ref: LLVMValueRef) { - def repr(): String = { - val bytePointer = LLVMPrintValueToString(ref) - val string = bytePointer.getString - LLVMDisposeMessage(bytePointer) - string - } +class OptionalValue - def name(): String = { - LLVMGetValueName(ref).getString - } +class Value(ref: LLVMValueRef) extends OptionalValue { + def repr(): String = { + val bytePointer = LLVMPrintValueToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } + + def name(): String = { + LLVMGetValueName(ref).getString + } } +case class NullValue() extends OptionalValue + object Value { - def apply(ref: LLVMValueRef): Value = { - LLVMGetValueKind(ref) match { - //LLVMArgumentValueKind - case LLVMBasicBlockValueKind ⇒ BasicBlock(LLVMValueAsBasicBlock(ref)) - //LLVMMemoryUseValueKind - //LLVMMemoryDefValueKind - //LLVMMemoryPhiValueKind - case LLVMFunctionValueKind ⇒ Function(ref) - //LLVMGlobalAliasValueKind - //LLVMGlobalIFuncValueKind - case LLVMGlobalVariableValueKind ⇒ GlobalVariable(ref) - //LLVMBlockAddressValueKind - //LLVMConstantExprValueKind - //LLVMConstantArrayValueKind - //LLVMConstantStructValueKind - //LLVMConstantVectorValueKind - //LLVMUndefValueValueKind - //LLVMConstantAggregateZeroValueKind - //LLVMConstantDataArrayValueKind - //LLVMConstantDataVectorValueKind - //LLVMConstantIntValueKind - //LLVMConstantFPValueKind - //LLVMConstantPointerNullValueKind - //LLVMConstantTokenNoneValueKind - //LLVMMetadataAsValueValueKind - //LLVMInlineAsmValueKind - case LLVMInstructionValueKind ⇒ Instruction(ref) - //LLVMPoisonValueValueKind - case valueKind ⇒ throw new IllegalArgumentException("unknown valueKind: "+valueKind) - } + def apply(ref: LLVMValueRef): OptionalValue = { + if (ref == null) return NullValue() + if (ref.isNull) return NullValue() + LLVMGetValueKind(ref) match { + //LLVMArgumentValueKind + case LLVMBasicBlockValueKind => BasicBlock(LLVMValueAsBasicBlock(ref)) + //LLVMMemoryUseValueKind + //LLVMMemoryDefValueKind + //LLVMMemoryPhiValueKind + case LLVMFunctionValueKind => Function(ref) + //LLVMGlobalAliasValueKind + //LLVMGlobalIFuncValueKind + case LLVMGlobalVariableValueKind => GlobalVariable(ref) + //LLVMBlockAddressValueKind + //LLVMConstantExprValueKind + //LLVMConstantArrayValueKind + //LLVMConstantStructValueKind + //LLVMConstantVectorValueKind + //LLVMUndefValueValueKind + //LLVMConstantAggregateZeroValueKind + //LLVMConstantDataArrayValueKind + //LLVMConstantDataVectorValueKind + //LLVMConstantIntValueKind + //LLVMConstantFPValueKind + //LLVMConstantPointerNullValueKind + //LLVMConstantTokenNoneValueKind + //LLVMMetadataAsValueValueKind + //LLVMInlineAsmValueKind + case LLVMInstructionValueKind => Instruction(ref) + //LLVMPoisonValueValueKind + case valueKind => throw new IllegalArgumentException("unknown valueKind: " + valueKind) } + } } diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/jsmn.h b/OPAL/ll/src/test/resources/org/opalj/ll/jsmn.h new file mode 100644 index 0000000000..8ac14c1bde --- /dev/null +++ b/OPAL/ll/src/test/resources/org/opalj/ll/jsmn.h @@ -0,0 +1,471 @@ +/* + * MIT License + * + * Copyright (c) 2010 Serge Zaitsev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef JSMN_H +#define JSMN_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef JSMN_STATIC +#define JSMN_API static +#else +#define JSMN_API extern +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1 << 0, + JSMN_ARRAY = 1 << 1, + JSMN_STRING = 1 << 2, + JSMN_PRIMITIVE = 1 << 3 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct jsmntok { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string. + */ +typedef struct jsmn_parser { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g. parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +JSMN_API void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each + * describing + * a single JSON object. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens); + +#ifndef JSMN_HEADER +/** + * Allocates a fresh unused token from the token pool. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, + const int start, const int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t': + case '\r': + case '\n': + case ' ': + case ',': + case ']': + case '}': + goto found; + default: + /* to quiet a warning from gcc*/ + break; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + const size_t len, jsmntok_t *tokens, + const size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + /* Skip starting quote */ + parser->pos++; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': + case '/': + case '\\': + case 'b': + case 'f': + case 'r': + case 'n': + case 't': + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; + i++) { + /* If it isn't a hex character we have an error */ + if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, + jsmntok_t *tokens, const unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': + case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + return JSMN_ERROR_NOMEM; + } + if (parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; +#ifdef JSMN_STRICT + /* In strict mode an object or array can't become a key */ + if (t->type == JSMN_OBJECT) { + return JSMN_ERROR_INVAL; + } +#endif + t->size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': + case ']': + if (tokens == NULL) { + break; + } + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if (token->type != type || parser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) { + return JSMN_ERROR_INVAL; + } + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + case '\t': + case '\r': + case '\n': + case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 't': + case 'f': + case 'n': + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + const jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) { + return r; + } + count++; + if (parser->toksuper != -1 && tokens != NULL) { + tokens[parser->toksuper].size++; + } + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +JSMN_API void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + +#endif /* JSMN_HEADER */ + +#ifdef __cplusplus +} +#endif + +#endif /* JSMN_H */ diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/test.c b/OPAL/ll/src/test/resources/org/opalj/ll/test.c new file mode 100644 index 0000000000..2da9eb0457 --- /dev/null +++ b/OPAL/ll/src/test/resources/org/opalj/ll/test.c @@ -0,0 +1,19 @@ +#include + +int pure_fuction(int a, int b) { + return a + b; +} + +void impure_function(int* foo) { + *foo = 23; +} + +int main() { + printf("hello world\n"); + int result = pure_fuction(1,2); + printf("result: %i\n", result); + int foo = 42; + impure_function(&foo); + printf("foo: %i", foo); + return 0; +} diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.c b/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.c new file mode 100644 index 0000000000..1f2f5ddfdf --- /dev/null +++ b/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.c @@ -0,0 +1,13 @@ +#include +#include +#include "jsmn.h" + +int main() { + char* s = "{\"test\": \"this is a test\", \"foo\": 42}"; + + jsmn_parser p; + jsmntok_t t[128]; /* We expect no more than 128 JSON tokens */ + + jsmn_init(&p); + int r = jsmn_parse(&p, s, strlen(s), t, 128); // "s" is the char array holding the json content +} diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll b/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll new file mode 100644 index 0000000000..c38e3849d3 --- /dev/null +++ b/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll @@ -0,0 +1,1223 @@ +; ModuleID = 'test_jsmn.c' +source_filename = "test_jsmn.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +%struct.jsmn_parser = type { i32, i32, i32 } +%struct.jsmntok = type { i32, i32, i32, i32 } + +@.str = private unnamed_addr constant [38 x i8] c"{\22test\22: \22this is a test\22, \22foo\22: 42}\00", align 1 + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @jsmn_parse(%struct.jsmn_parser* %0, i8* %1, i64 %2, %struct.jsmntok* %3, i32 %4) #0 { + %6 = alloca i32, align 4 + %7 = alloca %struct.jsmn_parser*, align 8 + %8 = alloca i8*, align 8 + %9 = alloca i64, align 8 + %10 = alloca %struct.jsmntok*, align 8 + %11 = alloca i32, align 4 + %12 = alloca i32, align 4 + %13 = alloca i32, align 4 + %14 = alloca %struct.jsmntok*, align 8 + %15 = alloca i32, align 4 + %16 = alloca i8, align 1 + %17 = alloca i32, align 4 + %18 = alloca %struct.jsmntok*, align 8 + store %struct.jsmn_parser* %0, %struct.jsmn_parser** %7, align 8 + store i8* %1, i8** %8, align 8 + store i64 %2, i64* %9, align 8 + store %struct.jsmntok* %3, %struct.jsmntok** %10, align 8 + store i32 %4, i32* %11, align 4 + %19 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %20 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %19, i32 0, i32 1 + %21 = load i32, i32* %20, align 4 + store i32 %21, i32* %15, align 4 + br label %22 + +22: ; preds = %337, %5 + %23 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %24 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %23, i32 0, i32 0 + %25 = load i32, i32* %24, align 4 + %26 = zext i32 %25 to i64 + %27 = load i64, i64* %9, align 8 + %28 = icmp ult i64 %26, %27 + br i1 %28, label %29, label %39 + +29: ; preds = %22 + %30 = load i8*, i8** %8, align 8 + %31 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %32 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %31, i32 0, i32 0 + %33 = load i32, i32* %32, align 4 + %34 = zext i32 %33 to i64 + %35 = getelementptr inbounds i8, i8* %30, i64 %34 + %36 = load i8, i8* %35, align 1 + %37 = sext i8 %36 to i32 + %38 = icmp ne i32 %37, 0 + br label %39 + +39: ; preds = %29, %22 + %40 = phi i1 [ false, %22 ], [ %38, %29 ] + br i1 %40, label %41, label %342 + +41: ; preds = %39 + %42 = load i8*, i8** %8, align 8 + %43 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %44 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %43, i32 0, i32 0 + %45 = load i32, i32* %44, align 4 + %46 = zext i32 %45 to i64 + %47 = getelementptr inbounds i8, i8* %42, i64 %46 + %48 = load i8, i8* %47, align 1 + store i8 %48, i8* %16, align 1 + %49 = load i8, i8* %16, align 1 + %50 = sext i8 %49 to i32 + switch i32 %50, label %303 [ + i32 123, label %51 + i32 91, label %51 + i32 125, label %101 + i32 93, label %101 + i32 34, label %183 + i32 9, label %216 + i32 13, label %216 + i32 10, label %216 + i32 32, label %216 + i32 58, label %217 + i32 44, label %224 + ] + +51: ; preds = %41, %41 + %52 = load i32, i32* %15, align 4 + %53 = add nsw i32 %52, 1 + store i32 %53, i32* %15, align 4 + %54 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %55 = icmp eq %struct.jsmntok* %54, null + br i1 %55, label %56, label %57 + +56: ; preds = %51 + br label %336 + +57: ; preds = %51 + %58 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %59 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %60 = load i32, i32* %11, align 4 + %61 = zext i32 %60 to i64 + %62 = call %struct.jsmntok* @jsmn_alloc_token(%struct.jsmn_parser* %58, %struct.jsmntok* %59, i64 %61) + store %struct.jsmntok* %62, %struct.jsmntok** %14, align 8 + %63 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 + %64 = icmp eq %struct.jsmntok* %63, null + br i1 %64, label %65, label %66 + +65: ; preds = %57 + store i32 -1, i32* %6, align 4 + br label %377 + +66: ; preds = %57 + %67 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %68 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %67, i32 0, i32 2 + %69 = load i32, i32* %68, align 4 + %70 = icmp ne i32 %69, -1 + br i1 %70, label %71, label %82 + +71: ; preds = %66 + %72 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %73 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %74 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %73, i32 0, i32 2 + %75 = load i32, i32* %74, align 4 + %76 = sext i32 %75 to i64 + %77 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %72, i64 %76 + store %struct.jsmntok* %77, %struct.jsmntok** %18, align 8 + %78 = load %struct.jsmntok*, %struct.jsmntok** %18, align 8 + %79 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %78, i32 0, i32 3 + %80 = load i32, i32* %79, align 4 + %81 = add nsw i32 %80, 1 + store i32 %81, i32* %79, align 4 + br label %82 + +82: ; preds = %71, %66 + %83 = load i8, i8* %16, align 1 + %84 = sext i8 %83 to i32 + %85 = icmp eq i32 %84, 123 + %86 = zext i1 %85 to i64 + %87 = select i1 %85, i32 1, i32 2 + %88 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 + %89 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %88, i32 0, i32 0 + store i32 %87, i32* %89, align 4 + %90 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %91 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %90, i32 0, i32 0 + %92 = load i32, i32* %91, align 4 + %93 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 + %94 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %93, i32 0, i32 1 + store i32 %92, i32* %94, align 4 + %95 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %96 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %95, i32 0, i32 1 + %97 = load i32, i32* %96, align 4 + %98 = sub i32 %97, 1 + %99 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %100 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %99, i32 0, i32 2 + store i32 %98, i32* %100, align 4 + br label %336 + +101: ; preds = %41, %41 + %102 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %103 = icmp eq %struct.jsmntok* %102, null + br i1 %103, label %104, label %105 + +104: ; preds = %101 + br label %336 + +105: ; preds = %101 + %106 = load i8, i8* %16, align 1 + %107 = sext i8 %106 to i32 + %108 = icmp eq i32 %107, 125 + %109 = zext i1 %108 to i64 + %110 = select i1 %108, i32 1, i32 2 + store i32 %110, i32* %17, align 4 + %111 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %112 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %111, i32 0, i32 1 + %113 = load i32, i32* %112, align 4 + %114 = sub i32 %113, 1 + store i32 %114, i32* %13, align 4 + br label %115 + +115: ; preds = %149, %105 + %116 = load i32, i32* %13, align 4 + %117 = icmp sge i32 %116, 0 + br i1 %117, label %118, label %152 + +118: ; preds = %115 + %119 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %120 = load i32, i32* %13, align 4 + %121 = sext i32 %120 to i64 + %122 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %119, i64 %121 + store %struct.jsmntok* %122, %struct.jsmntok** %14, align 8 + %123 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 + %124 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %123, i32 0, i32 1 + %125 = load i32, i32* %124, align 4 + %126 = icmp ne i32 %125, -1 + br i1 %126, label %127, label %148 + +127: ; preds = %118 + %128 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 + %129 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %128, i32 0, i32 2 + %130 = load i32, i32* %129, align 4 + %131 = icmp eq i32 %130, -1 + br i1 %131, label %132, label %148 + +132: ; preds = %127 + %133 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 + %134 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %133, i32 0, i32 0 + %135 = load i32, i32* %134, align 4 + %136 = load i32, i32* %17, align 4 + %137 = icmp ne i32 %135, %136 + br i1 %137, label %138, label %139 + +138: ; preds = %132 + store i32 -2, i32* %6, align 4 + br label %377 + +139: ; preds = %132 + %140 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %141 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %140, i32 0, i32 2 + store i32 -1, i32* %141, align 4 + %142 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %143 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %142, i32 0, i32 0 + %144 = load i32, i32* %143, align 4 + %145 = add i32 %144, 1 + %146 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 + %147 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %146, i32 0, i32 2 + store i32 %145, i32* %147, align 4 + br label %152 + +148: ; preds = %127, %118 + br label %149 + +149: ; preds = %148 + %150 = load i32, i32* %13, align 4 + %151 = add nsw i32 %150, -1 + store i32 %151, i32* %13, align 4 + br label %115, !llvm.loop !4 + +152: ; preds = %139, %115 + %153 = load i32, i32* %13, align 4 + %154 = icmp eq i32 %153, -1 + br i1 %154, label %155, label %156 + +155: ; preds = %152 + store i32 -2, i32* %6, align 4 + br label %377 + +156: ; preds = %152 + br label %157 + +157: ; preds = %179, %156 + %158 = load i32, i32* %13, align 4 + %159 = icmp sge i32 %158, 0 + br i1 %159, label %160, label %182 + +160: ; preds = %157 + %161 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %162 = load i32, i32* %13, align 4 + %163 = sext i32 %162 to i64 + %164 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %161, i64 %163 + store %struct.jsmntok* %164, %struct.jsmntok** %14, align 8 + %165 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 + %166 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %165, i32 0, i32 1 + %167 = load i32, i32* %166, align 4 + %168 = icmp ne i32 %167, -1 + br i1 %168, label %169, label %178 + +169: ; preds = %160 + %170 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 + %171 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %170, i32 0, i32 2 + %172 = load i32, i32* %171, align 4 + %173 = icmp eq i32 %172, -1 + br i1 %173, label %174, label %178 + +174: ; preds = %169 + %175 = load i32, i32* %13, align 4 + %176 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %177 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %176, i32 0, i32 2 + store i32 %175, i32* %177, align 4 + br label %182 + +178: ; preds = %169, %160 + br label %179 + +179: ; preds = %178 + %180 = load i32, i32* %13, align 4 + %181 = add nsw i32 %180, -1 + store i32 %181, i32* %13, align 4 + br label %157, !llvm.loop !6 + +182: ; preds = %174, %157 + br label %336 + +183: ; preds = %41 + %184 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %185 = load i8*, i8** %8, align 8 + %186 = load i64, i64* %9, align 8 + %187 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %188 = load i32, i32* %11, align 4 + %189 = zext i32 %188 to i64 + %190 = call i32 @jsmn_parse_string(%struct.jsmn_parser* %184, i8* %185, i64 %186, %struct.jsmntok* %187, i64 %189) + store i32 %190, i32* %12, align 4 + %191 = load i32, i32* %12, align 4 + %192 = icmp slt i32 %191, 0 + br i1 %192, label %193, label %195 + +193: ; preds = %183 + %194 = load i32, i32* %12, align 4 + store i32 %194, i32* %6, align 4 + br label %377 + +195: ; preds = %183 + %196 = load i32, i32* %15, align 4 + %197 = add nsw i32 %196, 1 + store i32 %197, i32* %15, align 4 + %198 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %199 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %198, i32 0, i32 2 + %200 = load i32, i32* %199, align 4 + %201 = icmp ne i32 %200, -1 + br i1 %201, label %202, label %215 + +202: ; preds = %195 + %203 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %204 = icmp ne %struct.jsmntok* %203, null + br i1 %204, label %205, label %215 + +205: ; preds = %202 + %206 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %207 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %208 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %207, i32 0, i32 2 + %209 = load i32, i32* %208, align 4 + %210 = sext i32 %209 to i64 + %211 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %206, i64 %210 + %212 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %211, i32 0, i32 3 + %213 = load i32, i32* %212, align 4 + %214 = add nsw i32 %213, 1 + store i32 %214, i32* %212, align 4 + br label %215 + +215: ; preds = %205, %202, %195 + br label %336 + +216: ; preds = %41, %41, %41, %41 + br label %336 + +217: ; preds = %41 + %218 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %219 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %218, i32 0, i32 1 + %220 = load i32, i32* %219, align 4 + %221 = sub i32 %220, 1 + %222 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %223 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %222, i32 0, i32 2 + store i32 %221, i32* %223, align 4 + br label %336 + +224: ; preds = %41 + %225 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %226 = icmp ne %struct.jsmntok* %225, null + br i1 %226, label %227, label %302 + +227: ; preds = %224 + %228 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %229 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %228, i32 0, i32 2 + %230 = load i32, i32* %229, align 4 + %231 = icmp ne i32 %230, -1 + br i1 %231, label %232, label %302 + +232: ; preds = %227 + %233 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %234 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %235 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %234, i32 0, i32 2 + %236 = load i32, i32* %235, align 4 + %237 = sext i32 %236 to i64 + %238 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %233, i64 %237 + %239 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %238, i32 0, i32 0 + %240 = load i32, i32* %239, align 4 + %241 = icmp ne i32 %240, 2 + br i1 %241, label %242, label %302 + +242: ; preds = %232 + %243 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %244 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %245 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %244, i32 0, i32 2 + %246 = load i32, i32* %245, align 4 + %247 = sext i32 %246 to i64 + %248 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %243, i64 %247 + %249 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %248, i32 0, i32 0 + %250 = load i32, i32* %249, align 4 + %251 = icmp ne i32 %250, 1 + br i1 %251, label %252, label %302 + +252: ; preds = %242 + %253 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %254 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %253, i32 0, i32 1 + %255 = load i32, i32* %254, align 4 + %256 = sub i32 %255, 1 + store i32 %256, i32* %13, align 4 + br label %257 + +257: ; preds = %298, %252 + %258 = load i32, i32* %13, align 4 + %259 = icmp sge i32 %258, 0 + br i1 %259, label %260, label %301 + +260: ; preds = %257 + %261 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %262 = load i32, i32* %13, align 4 + %263 = sext i32 %262 to i64 + %264 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %261, i64 %263 + %265 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %264, i32 0, i32 0 + %266 = load i32, i32* %265, align 4 + %267 = icmp eq i32 %266, 2 + br i1 %267, label %276, label %268 + +268: ; preds = %260 + %269 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %270 = load i32, i32* %13, align 4 + %271 = sext i32 %270 to i64 + %272 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %269, i64 %271 + %273 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %272, i32 0, i32 0 + %274 = load i32, i32* %273, align 4 + %275 = icmp eq i32 %274, 1 + br i1 %275, label %276, label %297 + +276: ; preds = %268, %260 + %277 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %278 = load i32, i32* %13, align 4 + %279 = sext i32 %278 to i64 + %280 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %277, i64 %279 + %281 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %280, i32 0, i32 1 + %282 = load i32, i32* %281, align 4 + %283 = icmp ne i32 %282, -1 + br i1 %283, label %284, label %296 + +284: ; preds = %276 + %285 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %286 = load i32, i32* %13, align 4 + %287 = sext i32 %286 to i64 + %288 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %285, i64 %287 + %289 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %288, i32 0, i32 2 + %290 = load i32, i32* %289, align 4 + %291 = icmp eq i32 %290, -1 + br i1 %291, label %292, label %296 + +292: ; preds = %284 + %293 = load i32, i32* %13, align 4 + %294 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %295 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %294, i32 0, i32 2 + store i32 %293, i32* %295, align 4 + br label %301 + +296: ; preds = %284, %276 + br label %297 + +297: ; preds = %296, %268 + br label %298 + +298: ; preds = %297 + %299 = load i32, i32* %13, align 4 + %300 = add nsw i32 %299, -1 + store i32 %300, i32* %13, align 4 + br label %257, !llvm.loop !7 + +301: ; preds = %292, %257 + br label %302 + +302: ; preds = %301, %242, %232, %227, %224 + br label %336 + +303: ; preds = %41 + %304 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %305 = load i8*, i8** %8, align 8 + %306 = load i64, i64* %9, align 8 + %307 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %308 = load i32, i32* %11, align 4 + %309 = zext i32 %308 to i64 + %310 = call i32 @jsmn_parse_primitive(%struct.jsmn_parser* %304, i8* %305, i64 %306, %struct.jsmntok* %307, i64 %309) + store i32 %310, i32* %12, align 4 + %311 = load i32, i32* %12, align 4 + %312 = icmp slt i32 %311, 0 + br i1 %312, label %313, label %315 + +313: ; preds = %303 + %314 = load i32, i32* %12, align 4 + store i32 %314, i32* %6, align 4 + br label %377 + +315: ; preds = %303 + %316 = load i32, i32* %15, align 4 + %317 = add nsw i32 %316, 1 + store i32 %317, i32* %15, align 4 + %318 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %319 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %318, i32 0, i32 2 + %320 = load i32, i32* %319, align 4 + %321 = icmp ne i32 %320, -1 + br i1 %321, label %322, label %335 + +322: ; preds = %315 + %323 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %324 = icmp ne %struct.jsmntok* %323, null + br i1 %324, label %325, label %335 + +325: ; preds = %322 + %326 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %327 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %328 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %327, i32 0, i32 2 + %329 = load i32, i32* %328, align 4 + %330 = sext i32 %329 to i64 + %331 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %326, i64 %330 + %332 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %331, i32 0, i32 3 + %333 = load i32, i32* %332, align 4 + %334 = add nsw i32 %333, 1 + store i32 %334, i32* %332, align 4 + br label %335 + +335: ; preds = %325, %322, %315 + br label %336 + +336: ; preds = %335, %302, %217, %216, %215, %182, %104, %82, %56 + br label %337 + +337: ; preds = %336 + %338 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %339 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %338, i32 0, i32 0 + %340 = load i32, i32* %339, align 4 + %341 = add i32 %340, 1 + store i32 %341, i32* %339, align 4 + br label %22, !llvm.loop !8 + +342: ; preds = %39 + %343 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %344 = icmp ne %struct.jsmntok* %343, null + br i1 %344, label %345, label %375 + +345: ; preds = %342 + %346 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %347 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %346, i32 0, i32 1 + %348 = load i32, i32* %347, align 4 + %349 = sub i32 %348, 1 + store i32 %349, i32* %13, align 4 + br label %350 + +350: ; preds = %371, %345 + %351 = load i32, i32* %13, align 4 + %352 = icmp sge i32 %351, 0 + br i1 %352, label %353, label %374 + +353: ; preds = %350 + %354 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %355 = load i32, i32* %13, align 4 + %356 = sext i32 %355 to i64 + %357 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %354, i64 %356 + %358 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %357, i32 0, i32 1 + %359 = load i32, i32* %358, align 4 + %360 = icmp ne i32 %359, -1 + br i1 %360, label %361, label %370 + +361: ; preds = %353 + %362 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %363 = load i32, i32* %13, align 4 + %364 = sext i32 %363 to i64 + %365 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %362, i64 %364 + %366 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %365, i32 0, i32 2 + %367 = load i32, i32* %366, align 4 + %368 = icmp eq i32 %367, -1 + br i1 %368, label %369, label %370 + +369: ; preds = %361 + store i32 -3, i32* %6, align 4 + br label %377 + +370: ; preds = %361, %353 + br label %371 + +371: ; preds = %370 + %372 = load i32, i32* %13, align 4 + %373 = add nsw i32 %372, -1 + store i32 %373, i32* %13, align 4 + br label %350, !llvm.loop !9 + +374: ; preds = %350 + br label %375 + +375: ; preds = %374, %342 + %376 = load i32, i32* %15, align 4 + store i32 %376, i32* %6, align 4 + br label %377 + +377: ; preds = %375, %369, %313, %193, %155, %138, %65 + %378 = load i32, i32* %6, align 4 + ret i32 %378 +} + +; Function Attrs: noinline nounwind optnone uwtable +define internal %struct.jsmntok* @jsmn_alloc_token(%struct.jsmn_parser* %0, %struct.jsmntok* %1, i64 %2) #0 { + %4 = alloca %struct.jsmntok*, align 8 + %5 = alloca %struct.jsmn_parser*, align 8 + %6 = alloca %struct.jsmntok*, align 8 + %7 = alloca i64, align 8 + %8 = alloca %struct.jsmntok*, align 8 + store %struct.jsmn_parser* %0, %struct.jsmn_parser** %5, align 8 + store %struct.jsmntok* %1, %struct.jsmntok** %6, align 8 + store i64 %2, i64* %7, align 8 + %9 = load %struct.jsmn_parser*, %struct.jsmn_parser** %5, align 8 + %10 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %9, i32 0, i32 1 + %11 = load i32, i32* %10, align 4 + %12 = zext i32 %11 to i64 + %13 = load i64, i64* %7, align 8 + %14 = icmp uge i64 %12, %13 + br i1 %14, label %15, label %16 + +15: ; preds = %3 + store %struct.jsmntok* null, %struct.jsmntok** %4, align 8 + br label %31 + +16: ; preds = %3 + %17 = load %struct.jsmntok*, %struct.jsmntok** %6, align 8 + %18 = load %struct.jsmn_parser*, %struct.jsmn_parser** %5, align 8 + %19 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %18, i32 0, i32 1 + %20 = load i32, i32* %19, align 4 + %21 = add i32 %20, 1 + store i32 %21, i32* %19, align 4 + %22 = zext i32 %20 to i64 + %23 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %17, i64 %22 + store %struct.jsmntok* %23, %struct.jsmntok** %8, align 8 + %24 = load %struct.jsmntok*, %struct.jsmntok** %8, align 8 + %25 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %24, i32 0, i32 2 + store i32 -1, i32* %25, align 4 + %26 = load %struct.jsmntok*, %struct.jsmntok** %8, align 8 + %27 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %26, i32 0, i32 1 + store i32 -1, i32* %27, align 4 + %28 = load %struct.jsmntok*, %struct.jsmntok** %8, align 8 + %29 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %28, i32 0, i32 3 + store i32 0, i32* %29, align 4 + %30 = load %struct.jsmntok*, %struct.jsmntok** %8, align 8 + store %struct.jsmntok* %30, %struct.jsmntok** %4, align 8 + br label %31 + +31: ; preds = %16, %15 + %32 = load %struct.jsmntok*, %struct.jsmntok** %4, align 8 + ret %struct.jsmntok* %32 +} + +; Function Attrs: noinline nounwind optnone uwtable +define internal i32 @jsmn_parse_string(%struct.jsmn_parser* %0, i8* %1, i64 %2, %struct.jsmntok* %3, i64 %4) #0 { + %6 = alloca i32, align 4 + %7 = alloca %struct.jsmn_parser*, align 8 + %8 = alloca i8*, align 8 + %9 = alloca i64, align 8 + %10 = alloca %struct.jsmntok*, align 8 + %11 = alloca i64, align 8 + %12 = alloca %struct.jsmntok*, align 8 + %13 = alloca i32, align 4 + %14 = alloca i8, align 1 + %15 = alloca i32, align 4 + store %struct.jsmn_parser* %0, %struct.jsmn_parser** %7, align 8 + store i8* %1, i8** %8, align 8 + store i64 %2, i64* %9, align 8 + store %struct.jsmntok* %3, %struct.jsmntok** %10, align 8 + store i64 %4, i64* %11, align 8 + %16 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %17 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %16, i32 0, i32 0 + %18 = load i32, i32* %17, align 4 + store i32 %18, i32* %13, align 4 + %19 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %20 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %19, i32 0, i32 0 + %21 = load i32, i32* %20, align 4 + %22 = add i32 %21, 1 + store i32 %22, i32* %20, align 4 + br label %23 + +23: ; preds = %211, %5 + %24 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %25 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %24, i32 0, i32 0 + %26 = load i32, i32* %25, align 4 + %27 = zext i32 %26 to i64 + %28 = load i64, i64* %9, align 8 + %29 = icmp ult i64 %27, %28 + br i1 %29, label %30, label %40 + +30: ; preds = %23 + %31 = load i8*, i8** %8, align 8 + %32 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %33 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %32, i32 0, i32 0 + %34 = load i32, i32* %33, align 4 + %35 = zext i32 %34 to i64 + %36 = getelementptr inbounds i8, i8* %31, i64 %35 + %37 = load i8, i8* %36, align 1 + %38 = sext i8 %37 to i32 + %39 = icmp ne i32 %38, 0 + br label %40 + +40: ; preds = %30, %23 + %41 = phi i1 [ false, %23 ], [ %39, %30 ] + br i1 %41, label %42, label %216 + +42: ; preds = %40 + %43 = load i8*, i8** %8, align 8 + %44 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %45 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %44, i32 0, i32 0 + %46 = load i32, i32* %45, align 4 + %47 = zext i32 %46 to i64 + %48 = getelementptr inbounds i8, i8* %43, i64 %47 + %49 = load i8, i8* %48, align 1 + store i8 %49, i8* %14, align 1 + %50 = load i8, i8* %14, align 1 + %51 = sext i8 %50 to i32 + %52 = icmp eq i32 %51, 34 + br i1 %52, label %53, label %75 + +53: ; preds = %42 + %54 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %55 = icmp eq %struct.jsmntok* %54, null + br i1 %55, label %56, label %57 + +56: ; preds = %53 + store i32 0, i32* %6, align 4 + br label %220 + +57: ; preds = %53 + %58 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %59 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %60 = load i64, i64* %11, align 8 + %61 = call %struct.jsmntok* @jsmn_alloc_token(%struct.jsmn_parser* %58, %struct.jsmntok* %59, i64 %60) + store %struct.jsmntok* %61, %struct.jsmntok** %12, align 8 + %62 = load %struct.jsmntok*, %struct.jsmntok** %12, align 8 + %63 = icmp eq %struct.jsmntok* %62, null + br i1 %63, label %64, label %68 + +64: ; preds = %57 + %65 = load i32, i32* %13, align 4 + %66 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %67 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %66, i32 0, i32 0 + store i32 %65, i32* %67, align 4 + store i32 -1, i32* %6, align 4 + br label %220 + +68: ; preds = %57 + %69 = load %struct.jsmntok*, %struct.jsmntok** %12, align 8 + %70 = load i32, i32* %13, align 4 + %71 = add nsw i32 %70, 1 + %72 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %73 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %72, i32 0, i32 0 + %74 = load i32, i32* %73, align 4 + call void @jsmn_fill_token(%struct.jsmntok* %69, i32 4, i32 %71, i32 %74) + store i32 0, i32* %6, align 4 + br label %220 + +75: ; preds = %42 + %76 = load i8, i8* %14, align 1 + %77 = sext i8 %76 to i32 + %78 = icmp eq i32 %77, 92 + br i1 %78, label %79, label %210 + +79: ; preds = %75 + %80 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %81 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %80, i32 0, i32 0 + %82 = load i32, i32* %81, align 4 + %83 = add i32 %82, 1 + %84 = zext i32 %83 to i64 + %85 = load i64, i64* %9, align 8 + %86 = icmp ult i64 %84, %85 + br i1 %86, label %87, label %210 + +87: ; preds = %79 + %88 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %89 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %88, i32 0, i32 0 + %90 = load i32, i32* %89, align 4 + %91 = add i32 %90, 1 + store i32 %91, i32* %89, align 4 + %92 = load i8*, i8** %8, align 8 + %93 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %94 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %93, i32 0, i32 0 + %95 = load i32, i32* %94, align 4 + %96 = zext i32 %95 to i64 + %97 = getelementptr inbounds i8, i8* %92, i64 %96 + %98 = load i8, i8* %97, align 1 + %99 = sext i8 %98 to i32 + switch i32 %99, label %205 [ + i32 34, label %100 + i32 47, label %100 + i32 92, label %100 + i32 98, label %100 + i32 102, label %100 + i32 114, label %100 + i32 110, label %100 + i32 116, label %100 + i32 117, label %101 + ] + +100: ; preds = %87, %87, %87, %87, %87, %87, %87, %87 + br label %209 + +101: ; preds = %87 + %102 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %103 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %102, i32 0, i32 0 + %104 = load i32, i32* %103, align 4 + %105 = add i32 %104, 1 + store i32 %105, i32* %103, align 4 + store i32 0, i32* %15, align 4 + br label %106 + +106: ; preds = %197, %101 + %107 = load i32, i32* %15, align 4 + %108 = icmp slt i32 %107, 4 + br i1 %108, label %109, label %126 + +109: ; preds = %106 + %110 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %111 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %110, i32 0, i32 0 + %112 = load i32, i32* %111, align 4 + %113 = zext i32 %112 to i64 + %114 = load i64, i64* %9, align 8 + %115 = icmp ult i64 %113, %114 + br i1 %115, label %116, label %126 + +116: ; preds = %109 + %117 = load i8*, i8** %8, align 8 + %118 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %119 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %118, i32 0, i32 0 + %120 = load i32, i32* %119, align 4 + %121 = zext i32 %120 to i64 + %122 = getelementptr inbounds i8, i8* %117, i64 %121 + %123 = load i8, i8* %122, align 1 + %124 = sext i8 %123 to i32 + %125 = icmp ne i32 %124, 0 + br label %126 + +126: ; preds = %116, %109, %106 + %127 = phi i1 [ false, %109 ], [ false, %106 ], [ %125, %116 ] + br i1 %127, label %128, label %200 + +128: ; preds = %126 + %129 = load i8*, i8** %8, align 8 + %130 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %131 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %130, i32 0, i32 0 + %132 = load i32, i32* %131, align 4 + %133 = zext i32 %132 to i64 + %134 = getelementptr inbounds i8, i8* %129, i64 %133 + %135 = load i8, i8* %134, align 1 + %136 = sext i8 %135 to i32 + %137 = icmp sge i32 %136, 48 + br i1 %137, label %138, label %148 + +138: ; preds = %128 + %139 = load i8*, i8** %8, align 8 + %140 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %141 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %140, i32 0, i32 0 + %142 = load i32, i32* %141, align 4 + %143 = zext i32 %142 to i64 + %144 = getelementptr inbounds i8, i8* %139, i64 %143 + %145 = load i8, i8* %144, align 1 + %146 = sext i8 %145 to i32 + %147 = icmp sle i32 %146, 57 + br i1 %147, label %192, label %148 + +148: ; preds = %138, %128 + %149 = load i8*, i8** %8, align 8 + %150 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %151 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %150, i32 0, i32 0 + %152 = load i32, i32* %151, align 4 + %153 = zext i32 %152 to i64 + %154 = getelementptr inbounds i8, i8* %149, i64 %153 + %155 = load i8, i8* %154, align 1 + %156 = sext i8 %155 to i32 + %157 = icmp sge i32 %156, 65 + br i1 %157, label %158, label %168 + +158: ; preds = %148 + %159 = load i8*, i8** %8, align 8 + %160 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %161 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %160, i32 0, i32 0 + %162 = load i32, i32* %161, align 4 + %163 = zext i32 %162 to i64 + %164 = getelementptr inbounds i8, i8* %159, i64 %163 + %165 = load i8, i8* %164, align 1 + %166 = sext i8 %165 to i32 + %167 = icmp sle i32 %166, 70 + br i1 %167, label %192, label %168 + +168: ; preds = %158, %148 + %169 = load i8*, i8** %8, align 8 + %170 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %171 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %170, i32 0, i32 0 + %172 = load i32, i32* %171, align 4 + %173 = zext i32 %172 to i64 + %174 = getelementptr inbounds i8, i8* %169, i64 %173 + %175 = load i8, i8* %174, align 1 + %176 = sext i8 %175 to i32 + %177 = icmp sge i32 %176, 97 + br i1 %177, label %178, label %188 + +178: ; preds = %168 + %179 = load i8*, i8** %8, align 8 + %180 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %181 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %180, i32 0, i32 0 + %182 = load i32, i32* %181, align 4 + %183 = zext i32 %182 to i64 + %184 = getelementptr inbounds i8, i8* %179, i64 %183 + %185 = load i8, i8* %184, align 1 + %186 = sext i8 %185 to i32 + %187 = icmp sle i32 %186, 102 + br i1 %187, label %192, label %188 + +188: ; preds = %178, %168 + %189 = load i32, i32* %13, align 4 + %190 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %191 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %190, i32 0, i32 0 + store i32 %189, i32* %191, align 4 + store i32 -2, i32* %6, align 4 + br label %220 + +192: ; preds = %178, %158, %138 + %193 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %194 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %193, i32 0, i32 0 + %195 = load i32, i32* %194, align 4 + %196 = add i32 %195, 1 + store i32 %196, i32* %194, align 4 + br label %197 + +197: ; preds = %192 + %198 = load i32, i32* %15, align 4 + %199 = add nsw i32 %198, 1 + store i32 %199, i32* %15, align 4 + br label %106, !llvm.loop !10 + +200: ; preds = %126 + %201 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %202 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %201, i32 0, i32 0 + %203 = load i32, i32* %202, align 4 + %204 = add i32 %203, -1 + store i32 %204, i32* %202, align 4 + br label %209 + +205: ; preds = %87 + %206 = load i32, i32* %13, align 4 + %207 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %208 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %207, i32 0, i32 0 + store i32 %206, i32* %208, align 4 + store i32 -2, i32* %6, align 4 + br label %220 + +209: ; preds = %200, %100 + br label %210 + +210: ; preds = %209, %79, %75 + br label %211 + +211: ; preds = %210 + %212 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %213 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %212, i32 0, i32 0 + %214 = load i32, i32* %213, align 4 + %215 = add i32 %214, 1 + store i32 %215, i32* %213, align 4 + br label %23, !llvm.loop !11 + +216: ; preds = %40 + %217 = load i32, i32* %13, align 4 + %218 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %219 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %218, i32 0, i32 0 + store i32 %217, i32* %219, align 4 + store i32 -3, i32* %6, align 4 + br label %220 + +220: ; preds = %216, %205, %188, %68, %64, %56 + %221 = load i32, i32* %6, align 4 + ret i32 %221 +} + +; Function Attrs: noinline nounwind optnone uwtable +define internal i32 @jsmn_parse_primitive(%struct.jsmn_parser* %0, i8* %1, i64 %2, %struct.jsmntok* %3, i64 %4) #0 { + %6 = alloca i32, align 4 + %7 = alloca %struct.jsmn_parser*, align 8 + %8 = alloca i8*, align 8 + %9 = alloca i64, align 8 + %10 = alloca %struct.jsmntok*, align 8 + %11 = alloca i64, align 8 + %12 = alloca %struct.jsmntok*, align 8 + %13 = alloca i32, align 4 + store %struct.jsmn_parser* %0, %struct.jsmn_parser** %7, align 8 + store i8* %1, i8** %8, align 8 + store i64 %2, i64* %9, align 8 + store %struct.jsmntok* %3, %struct.jsmntok** %10, align 8 + store i64 %4, i64* %11, align 8 + %14 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %15 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %14, i32 0, i32 0 + %16 = load i32, i32* %15, align 4 + store i32 %16, i32* %13, align 4 + br label %17 + +17: ; preds = %72, %5 + %18 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %19 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %18, i32 0, i32 0 + %20 = load i32, i32* %19, align 4 + %21 = zext i32 %20 to i64 + %22 = load i64, i64* %9, align 8 + %23 = icmp ult i64 %21, %22 + br i1 %23, label %24, label %34 + +24: ; preds = %17 + %25 = load i8*, i8** %8, align 8 + %26 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %27 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %26, i32 0, i32 0 + %28 = load i32, i32* %27, align 4 + %29 = zext i32 %28 to i64 + %30 = getelementptr inbounds i8, i8* %25, i64 %29 + %31 = load i8, i8* %30, align 1 + %32 = sext i8 %31 to i32 + %33 = icmp ne i32 %32, 0 + br label %34 + +34: ; preds = %24, %17 + %35 = phi i1 [ false, %17 ], [ %33, %24 ] + br i1 %35, label %36, label %77 + +36: ; preds = %34 + %37 = load i8*, i8** %8, align 8 + %38 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %39 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %38, i32 0, i32 0 + %40 = load i32, i32* %39, align 4 + %41 = zext i32 %40 to i64 + %42 = getelementptr inbounds i8, i8* %37, i64 %41 + %43 = load i8, i8* %42, align 1 + %44 = sext i8 %43 to i32 + switch i32 %44, label %46 [ + i32 58, label %45 + i32 9, label %45 + i32 13, label %45 + i32 10, label %45 + i32 32, label %45 + i32 44, label %45 + i32 93, label %45 + i32 125, label %45 + ] + +45: ; preds = %36, %36, %36, %36, %36, %36, %36, %36 + br label %78 + +46: ; preds = %36 + br label %47 + +47: ; preds = %46 + %48 = load i8*, i8** %8, align 8 + %49 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %50 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %49, i32 0, i32 0 + %51 = load i32, i32* %50, align 4 + %52 = zext i32 %51 to i64 + %53 = getelementptr inbounds i8, i8* %48, i64 %52 + %54 = load i8, i8* %53, align 1 + %55 = sext i8 %54 to i32 + %56 = icmp slt i32 %55, 32 + br i1 %56, label %67, label %57 + +57: ; preds = %47 + %58 = load i8*, i8** %8, align 8 + %59 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %60 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %59, i32 0, i32 0 + %61 = load i32, i32* %60, align 4 + %62 = zext i32 %61 to i64 + %63 = getelementptr inbounds i8, i8* %58, i64 %62 + %64 = load i8, i8* %63, align 1 + %65 = sext i8 %64 to i32 + %66 = icmp sge i32 %65, 127 + br i1 %66, label %67, label %71 + +67: ; preds = %57, %47 + %68 = load i32, i32* %13, align 4 + %69 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %70 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %69, i32 0, i32 0 + store i32 %68, i32* %70, align 4 + store i32 -2, i32* %6, align 4 + br label %107 + +71: ; preds = %57 + br label %72 + +72: ; preds = %71 + %73 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %74 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %73, i32 0, i32 0 + %75 = load i32, i32* %74, align 4 + %76 = add i32 %75, 1 + store i32 %76, i32* %74, align 4 + br label %17, !llvm.loop !12 + +77: ; preds = %34 + br label %78 + +78: ; preds = %77, %45 + %79 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %80 = icmp eq %struct.jsmntok* %79, null + br i1 %80, label %81, label %86 + +81: ; preds = %78 + %82 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %83 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %82, i32 0, i32 0 + %84 = load i32, i32* %83, align 4 + %85 = add i32 %84, -1 + store i32 %85, i32* %83, align 4 + store i32 0, i32* %6, align 4 + br label %107 + +86: ; preds = %78 + %87 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %88 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 + %89 = load i64, i64* %11, align 8 + %90 = call %struct.jsmntok* @jsmn_alloc_token(%struct.jsmn_parser* %87, %struct.jsmntok* %88, i64 %89) + store %struct.jsmntok* %90, %struct.jsmntok** %12, align 8 + %91 = load %struct.jsmntok*, %struct.jsmntok** %12, align 8 + %92 = icmp eq %struct.jsmntok* %91, null + br i1 %92, label %93, label %97 + +93: ; preds = %86 + %94 = load i32, i32* %13, align 4 + %95 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %96 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %95, i32 0, i32 0 + store i32 %94, i32* %96, align 4 + store i32 -1, i32* %6, align 4 + br label %107 + +97: ; preds = %86 + %98 = load %struct.jsmntok*, %struct.jsmntok** %12, align 8 + %99 = load i32, i32* %13, align 4 + %100 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %101 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %100, i32 0, i32 0 + %102 = load i32, i32* %101, align 4 + call void @jsmn_fill_token(%struct.jsmntok* %98, i32 8, i32 %99, i32 %102) + %103 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 + %104 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %103, i32 0, i32 0 + %105 = load i32, i32* %104, align 4 + %106 = add i32 %105, -1 + store i32 %106, i32* %104, align 4 + store i32 0, i32* %6, align 4 + br label %107 + +107: ; preds = %97, %93, %81, %67 + %108 = load i32, i32* %6, align 4 + ret i32 %108 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @jsmn_init(%struct.jsmn_parser* %0) #0 { + %2 = alloca %struct.jsmn_parser*, align 8 + store %struct.jsmn_parser* %0, %struct.jsmn_parser** %2, align 8 + %3 = load %struct.jsmn_parser*, %struct.jsmn_parser** %2, align 8 + %4 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %3, i32 0, i32 0 + store i32 0, i32* %4, align 4 + %5 = load %struct.jsmn_parser*, %struct.jsmn_parser** %2, align 8 + %6 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %5, i32 0, i32 1 + store i32 0, i32* %6, align 4 + %7 = load %struct.jsmn_parser*, %struct.jsmn_parser** %2, align 8 + %8 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %7, i32 0, i32 2 + store i32 -1, i32* %8, align 4 + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() #0 { + %1 = alloca i8*, align 8 + %2 = alloca %struct.jsmn_parser, align 4 + %3 = alloca [128 x %struct.jsmntok], align 16 + %4 = alloca i32, align 4 + store i8* getelementptr inbounds ([38 x i8], [38 x i8]* @.str, i64 0, i64 0), i8** %1, align 8 + call void @jsmn_init(%struct.jsmn_parser* %2) + %5 = load i8*, i8** %1, align 8 + %6 = load i8*, i8** %1, align 8 + %7 = call i64 @strlen(i8* %6) #2 + %8 = getelementptr inbounds [128 x %struct.jsmntok], [128 x %struct.jsmntok]* %3, i64 0, i64 0 + %9 = call i32 @jsmn_parse(%struct.jsmn_parser* %2, i8* %5, i64 %7, %struct.jsmntok* %8, i32 128) + store i32 %9, i32* %4, align 4 + ret i32 0 +} + +; Function Attrs: nounwind readonly willreturn +declare dso_local i64 @strlen(i8*) #1 + +; Function Attrs: noinline nounwind optnone uwtable +define internal void @jsmn_fill_token(%struct.jsmntok* %0, i32 %1, i32 %2, i32 %3) #0 { + %5 = alloca %struct.jsmntok*, align 8 + %6 = alloca i32, align 4 + %7 = alloca i32, align 4 + %8 = alloca i32, align 4 + store %struct.jsmntok* %0, %struct.jsmntok** %5, align 8 + store i32 %1, i32* %6, align 4 + store i32 %2, i32* %7, align 4 + store i32 %3, i32* %8, align 4 + %9 = load i32, i32* %6, align 4 + %10 = load %struct.jsmntok*, %struct.jsmntok** %5, align 8 + %11 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %10, i32 0, i32 0 + store i32 %9, i32* %11, align 4 + %12 = load i32, i32* %7, align 4 + %13 = load %struct.jsmntok*, %struct.jsmntok** %5, align 8 + %14 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %13, i32 0, i32 1 + store i32 %12, i32* %14, align 4 + %15 = load i32, i32* %8, align 4 + %16 = load %struct.jsmntok*, %struct.jsmntok** %5, align 8 + %17 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %16, i32 0, i32 2 + store i32 %15, i32* %17, align 4 + %18 = load %struct.jsmntok*, %struct.jsmntok** %5, align 8 + %19 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %18, i32 0, i32 3 + store i32 0, i32* %19, align 4 + ret void +} + +attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nounwind readonly willreturn "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #2 = { nounwind readonly willreturn } + +!llvm.module.flags = !{!0, !1, !2} +!llvm.ident = !{!3} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{!"Ubuntu clang version 13.0.0-2"} +!4 = distinct !{!4, !5} +!5 = !{!"llvm.loop.mustprogress"} +!6 = distinct !{!6, !5} +!7 = distinct !{!7, !5} +!8 = distinct !{!8, !5} +!9 = distinct !{!9, !5} +!10 = distinct !{!10, !5} +!11 = distinct !{!11, !5} +!12 = distinct !{!12, !5} From 905ad989feea1814a062bf7860e2d549d429ad93 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 6 Jan 2022 14:17:20 +0100 Subject: [PATCH 11/67] Add simple multilingual test project --- .../llvm/multilingual/simple/NativeTest.c | 21 +++++++ .../llvm/multilingual/simple/NativeTest.class | Bin 0 -> 1099 bytes .../llvm/multilingual/simple/NativeTest.h | 31 ++++++++++ .../llvm/multilingual/simple/NativeTest.java | 14 +++++ .../llvm/multilingual/simple/NativeTest.ll | 55 ++++++++++++++++++ .../llvm/multilingual/simple/build.sh | 3 + .../llvm/multilingual/simple/libnativetest.so | Bin 0 -> 15424 bytes .../resources/llvm/multilingual/simple/run.sh | 1 + 8 files changed, 125 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.c create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.class create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.h create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.java create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.ll create mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/build.sh create mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/libnativetest.so create mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/run.sh diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.c new file mode 100644 index 0000000000..9de4ce9cc8 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.c @@ -0,0 +1,21 @@ +#include +#include +#include +#include "NativeTest.h" +JNIEXPORT int JNICALL +Java_NativeTest_nativeFunction(JNIEnv *env, jobject obj, jint a, jint b) +{ + return a + b; +} + +JNIEXPORT int JNICALL +Java_NativeTest_foo(JNIEnv *env, jobject obj) +{ + return bar() + 23; +} + +int +bar() +{ + return 6*7; +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.class new file mode 100644 index 0000000000000000000000000000000000000000..efb81bdc44135e5344ca14b52f5b85995048e4df GIT binary patch literal 1099 zcmaJ=?M@Rx6g>lNcUjgJ%a?*^LD04+RZ&rjAF&CsNd=P7 z{s4oC58y)??`#`eQsSSP`!VOtxp(gT{&W5dzy@ABa4~0L-oXgw#&81*#=4oM?2?1q zST^oT77R-^?mBcJZ=t}D+VZ5QUowp53m+H~71a_9nVKiXPSkFQV2?L^ilimi-m%z@ zWK(-eG9>fWYN1NFFG|H|vJ-9YQEDdtxpv5pdCBK;zf{+OC-=*S-QH^Yz3ZI1ih`!t z_6&JuCnnz$pnBuQisHnuNH>8!Y~;txk%1nb<9;h zHFG*Yj557UQ{Que&`}_}SC9LOw`yJ^;K7N7XQmZf49opOJ$bB-MDIPi8n<~l)h5LTp)1qwQ6~oFXdvN8Kl*HSEigk+r`81LiEH#hQ +/* Header for class NativeTest */ + +#ifndef _Included_NativeTest +#define _Included_NativeTest +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: NativeTest + * Method: nativeFunction + * Signature: (II)I + */ +JNIEXPORT jint JNICALL Java_NativeTest_nativeFunction + (JNIEnv *, jobject, jint, jint); + +/* + * Class: NativeTest + * Method: foo + * Signature: ()I + */ +JNIEXPORT jint JNICALL Java_NativeTest_foo + (JNIEnv *, jobject); + +int bar(); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.java new file mode 100644 index 0000000000..f44b6f8c53 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.java @@ -0,0 +1,14 @@ +public class NativeTest { + public native int nativeFunction (int a, int b); + public native int foo (); + static + { + System.loadLibrary ("nativetest"); /* lowercase of classname! */ + } + public static void main (String[] args) + { + NativeTest demo = new NativeTest(); + System.out.println("result is: " + demo.nativeFunction(23,42)); + System.out.println("foo returns: " + demo.foo()); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.ll new file mode 100644 index 0000000000..bbf6edec26 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.ll @@ -0,0 +1,55 @@ +; ModuleID = 'NativeTest.c' +source_filename = "NativeTest.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +%struct.JNINativeInterface_ = type { i8*, i8*, i8*, i8*, i32 (%struct.JNINativeInterface_**)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*, %struct._jobject*, i8*, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, i8)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**, i8*)*, i32 (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i64)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, float)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, double)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i64)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, float)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, double)*, %struct._jobject* (%struct.JNINativeInterface_**, i16*, i32)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, i32, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i32* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i64* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, float* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, double* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i64*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, float*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, double*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i32*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i64*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, float*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, double*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i32*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i64*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, float*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, double*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct.JNINativeMethod*, i32)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct.JNIInvokeInterface_***)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*, i64)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)* } +%struct._jmethodID = type opaque +%struct._jfieldID = type opaque +%struct.__va_list_tag = type { i32, i32, i8*, i8* } +%union.jvalue = type { i64 } +%struct.JNINativeMethod = type { i8*, i8*, i8* } +%struct.JNIInvokeInterface_ = type { i8*, i8*, i8*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i32)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)* } +%struct._jobject = type opaque + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_NativeTest_nativeFunction(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2, i32 %3) #0 { + %5 = alloca %struct.JNINativeInterface_**, align 8 + %6 = alloca %struct._jobject*, align 8 + %7 = alloca i32, align 4 + %8 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %5, align 8 + store %struct._jobject* %1, %struct._jobject** %6, align 8 + store i32 %2, i32* %7, align 4 + store i32 %3, i32* %8, align 4 + %9 = load i32, i32* %7, align 4 + %10 = load i32, i32* %8, align 4 + %11 = add nsw i32 %9, %10 + ret i32 %11 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_NativeTest_foo(%struct.JNINativeInterface_** %0, %struct._jobject* %1) #0 { + %3 = alloca %struct.JNINativeInterface_**, align 8 + %4 = alloca %struct._jobject*, align 8 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %3, align 8 + store %struct._jobject* %1, %struct._jobject** %4, align 8 + %5 = call i32 @bar() + %6 = add nsw i32 %5, 23 + ret i32 %6 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @bar() #0 { + ret i32 42 +} + +attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.module.flags = !{!0, !1, !2} +!llvm.ident = !{!3} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{!"Ubuntu clang version 13.0.0-2"} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/build.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/build.sh new file mode 100755 index 0000000000..457ed0fd79 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/build.sh @@ -0,0 +1,3 @@ +clang -shared -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ -o libnativetest.so NativeTest.c +clang -S -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ NativeTest.c -emit-llvm +javac NativeTest.java \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/libnativetest.so b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/libnativetest.so new file mode 100755 index 0000000000000000000000000000000000000000..ced7738d45fd0a9aff6a47005995b8c31136c3f9 GIT binary patch literal 15424 zcmeHOU2Ggz6~4Pp8i)LNTeZ|ARWpQCq=inMv}ps8lXaYRCXt<*U?W*-G@1Rey=Z@; z-C5Tze+EN|6(U7K^Z^i3`-FsqDjp~jJh&JYR3S(ucmN)-P>USJ0+&A-9XU^Q&JEJ-GbD80xWFn!^y3|R<)M(0Z(ju5YCLMybI<9uoalhK9 zb+@%e@93?H5U9j2kFlM#g^puULIOh0Z0*uvV``2*w(xI40;6_!ku2pJh1AK-*qb!8 zBM3Vt+0GF=N9=%05+|_RxY03koX5pq91n>eP@fa=M&Anay6=cW44)7-5OyCUJC5_+ zHl+QcjJH)@*5&+wLhAFomj>&i|Fajy)u(B9rPiAL6QBR?Cy3p#alB3k3H$BD|L_+4A>tpT|9HDf zjC<5>HF~#^>=K1g_mI_AJS>oqr1Anqu2};H13hI0?6ZjW`usBl~E7U{3qPs(lN-iu`EAJ3b zR;wx-)KsaM&AIie`=mNMeD3t1f7Ctd@@<^Ze@Q_TIP<eC_$#p{+*vG(lQzJ;LX?gtigk;~rpHZ`x?=Ake=r zhO|eU0h-;p2{ejrchu)-P&w(`cn4V_Yt(7LVhrUPjn;nD{)<}w*w+~zzT+*fd$0cKl=tdJmzQ|Wd*e=cFFDAFgYM?aWY2*! zZF`P?zW)-5RpUeB-a`LD?s|)Fg}c3L{U?c7ouDXJ^K^J^`!S*umuT$P_-udcSrSS- z<7DbBerCg4Y^*PiuPu(OUdya?WmYdOn{l;#VJ!un)!TQP&4s1=DPt+pxxw-uJVGH{ zT5jjIl}+N?W;0+jU^8GdU^8GdU^8GdU^8GdU^8GdU^DQ4$UtI$*9rcX%-_nK^=5OF zFikj1n0mX}uti|3CY4v*{43 zy{VzSz2|#&J=;C6PVGDK$m56f=U29;*Lbx=diX}j>TLTA{m)Vi`kU&&-qiES!9914 z(*Pp>w8-_hSjZ>Z@^6Y>PRA?IY!*@ewxDoS$v#?dE2#nQuw1 z%a(W`-%B0l&&T-h7e3}M^q-i>rZUq=e+30 z>+X4tSPZ(#@%MdA=W({TvtQ*Qt&_U-hR-)of7m^I^4C_k+f) z%2mr(N`C*$l z{?kK4AJ2^WV*{s$Gvts*|Bu@fcnd*%=K*gmi0yEQtHd@Z=nW9>Vv|F9(;fx4K35Kd zta9sN&EPb8m1N~4zIbe z$GQw$5=9=DZpGF*kM%N5JoXKHtnR;Wo)ACufV_`~*KE*O zw}Flf7LQ-pDo-7j!5+^EKs+a4eB=-3UlIG$;tauX(FFd;kgX%m8;tKzho5CIKk!_E{djSG z(7;#7wkKkb=a^o;v8h4NP;DCgWfJh*!T5Myz<$Ri$v@5;cDN2-AX`>}J@$94JVs1v zjy|>zum|$ Date: Sat, 5 Feb 2022 16:53:26 +0100 Subject: [PATCH 12/67] Add taint testcase --- .../llvm/multilingual/taint/NativeTest.c | 45 +++++++ .../llvm/multilingual/taint/NativeTest.class | Bin 0 -> 995 bytes .../llvm/multilingual/taint/NativeTest.h | 30 +++++ .../llvm/multilingual/taint/NativeTest.java | 40 ++++++ .../llvm/multilingual/taint/NativeTest.ll | 117 ++++++++++++++++++ .../llvm/multilingual/taint/build.sh | 3 + .../llvm/multilingual/taint/libnativetest.so | Bin 0 -> 15624 bytes .../resources/llvm/multilingual/taint/run.sh | 1 + 8 files changed, 236 insertions(+) create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.c create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.class create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.h create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.java create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll create mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh create mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libnativetest.so create mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.c new file mode 100644 index 0000000000..9cc3144ce7 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include "NativeTest.h" +JNIEXPORT int JNICALL +Java_NativeTest_f1(JNIEnv *env, jobject obj, jint a, jint b) { + return a + b; +} + +JNIEXPORT int JNICALL +Java_NativeTest_f2(JNIEnv *env, jobject obj) { + return source() + 23; +} + +JNIEXPORT int JNICALL +Java_NativeTest_f3(JNIEnv *env, jobject obj, jint a) { + return sanitize(a); +} + +JNIEXPORT int JNICALL +Java_NativeTest_f4(JNIEnv *env, jobject obj, jint a) { + sink(a); + return 23; +} + +JNIEXPORT int JNICALL +Java_NativeTest_f5(JNIEnv *env, jobject obj, jint a, jint b) { + a = sanitize(a); + sink(a + b); + return b; +} + +int +source() { + return 6*7; +} + +void +sink(int num) {} + +int +sanitize(int num) { + return num; +} + diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.class new file mode 100644 index 0000000000000000000000000000000000000000..686b7acb65cb1c2d4f28a5fd3d547cea1232793d GIT binary patch literal 995 zcmZuwT~8BH5IwiwT^3rvQiWEuDlDv0zEOcbM-n|HO^d)?uSRnNicpFMXLKv*(8XnRnoI!UZX9VhrO7#sD^P z2e(YzMajfHOqrNK-b6v7o3b$}Q4#qho|t%wO?E$O1zr+cCbmH(j6k;t*}}w*AlU(l z{rZV_7+!;F=bg}NQjpduH*!~u{Z{!E>4qfjxlJ}1*EO!g3dxU>)Sy;5>;F;>HCpab z)vveR_BlyKc>VGXK?!r(8(bCX6@7gE0_qC`)}u{mP$}L7@&uJApgl#@vQ>G#R)E!laz;3iq6 +/* Header for class NativeTest */ + +#ifndef _Included_NativeTest +#define _Included_NativeTest +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT int JNICALL Java_NativeTest_f1(JNIEnv *env, jobject obj, jint a, jint b); + +JNIEXPORT int JNICALL Java_NativeTest_f2(JNIEnv *env, jobject obj); + +JNIEXPORT int JNICALL Java_NativeTest_f3(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT int JNICALL Java_NativeTest_f4(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT int JNICALL Java_NativeTest_f5(JNIEnv *env, jobject obj, jint a, jint b); + +int source(); + +void sink(int num); + +int sanitize(int num); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.java new file mode 100644 index 0000000000..9735a5a1f4 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.java @@ -0,0 +1,40 @@ +public class NativeTest { + public native int f1 (int a, int b); + public native int f2 (); + public native int f3 (int a); + public native int f4 (int a); + public native int f5 (int a, int b); + static + { + System.loadLibrary ("nativetest"); /* lowercase of classname! */ + } + public static void main (String[] args) + { + NativeTest demo = new NativeTest(); + int tainted = demo.source(); + demo.sink(tainted); + demo.sink(demo.sanitize(tainted)); + int untainted = 23; + demo.sink(untainted); + int native_tainted = demo.f1(tainted, untainted); + demo.sink(native_tainted); + demo.f4(native_tainted); + int native_source_taint = demo.f2(); + demo.f4(demo.f3(native_tainted)); + demo.sink(demo.f5(tainted, untainted)); + demo.sink(demo.f5(untainted, tainted)); + System.out.println("done"); + } + + public static int source() + { + return 42; + } + + public static void sink(int a) {} + + public static int sanitize(int a) + { + return a; + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll new file mode 100644 index 0000000000..0e50b52376 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll @@ -0,0 +1,117 @@ +; ModuleID = 'NativeTest.c' +source_filename = "NativeTest.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +%struct.JNINativeInterface_ = type { i8*, i8*, i8*, i8*, i32 (%struct.JNINativeInterface_**)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*, %struct._jobject*, i8*, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, i8)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**, i8*)*, i32 (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i64)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, float)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, double)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i64)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, float)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, double)*, %struct._jobject* (%struct.JNINativeInterface_**, i16*, i32)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, i32, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i32* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i64* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, float* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, double* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i64*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, float*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, double*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i32*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i64*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, float*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, double*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i32*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i64*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, float*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, double*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct.JNINativeMethod*, i32)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct.JNIInvokeInterface_***)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*, i64)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)* } +%struct._jmethodID = type opaque +%struct._jfieldID = type opaque +%struct.__va_list_tag = type { i32, i32, i8*, i8* } +%union.jvalue = type { i64 } +%struct.JNINativeMethod = type { i8*, i8*, i8* } +%struct.JNIInvokeInterface_ = type { i8*, i8*, i8*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i32)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)* } +%struct._jobject = type opaque + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_NativeTest_f1(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2, i32 %3) #0 { + %5 = alloca %struct.JNINativeInterface_**, align 8 + %6 = alloca %struct._jobject*, align 8 + %7 = alloca i32, align 4 + %8 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %5, align 8 + store %struct._jobject* %1, %struct._jobject** %6, align 8 + store i32 %2, i32* %7, align 4 + store i32 %3, i32* %8, align 4 + %9 = load i32, i32* %7, align 4 + %10 = load i32, i32* %8, align 4 + %11 = add nsw i32 %9, %10 + ret i32 %11 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_NativeTest_f2(%struct.JNINativeInterface_** %0, %struct._jobject* %1) #0 { + %3 = alloca %struct.JNINativeInterface_**, align 8 + %4 = alloca %struct._jobject*, align 8 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %3, align 8 + store %struct._jobject* %1, %struct._jobject** %4, align 8 + %5 = call i32 @source() + %6 = add nsw i32 %5, 23 + ret i32 %6 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_NativeTest_f3(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %7 = load i32, i32* %6, align 4 + %8 = call i32 @sanitize(i32 %7) + ret i32 %8 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @sanitize(i32 %0) #0 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + %3 = load i32, i32* %2, align 4 + ret i32 %3 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_NativeTest_f4(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %7 = load i32, i32* %6, align 4 + call void @sink(i32 %7) + ret i32 23 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @sink(i32 %0) #0 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_NativeTest_f5(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2, i32 %3) #0 { + %5 = alloca %struct.JNINativeInterface_**, align 8 + %6 = alloca %struct._jobject*, align 8 + %7 = alloca i32, align 4 + %8 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %5, align 8 + store %struct._jobject* %1, %struct._jobject** %6, align 8 + store i32 %2, i32* %7, align 4 + store i32 %3, i32* %8, align 4 + %9 = load i32, i32* %7, align 4 + %10 = call i32 @sanitize(i32 %9) + store i32 %10, i32* %7, align 4 + %11 = load i32, i32* %7, align 4 + %12 = load i32, i32* %8, align 4 + %13 = add nsw i32 %11, %12 + call void @sink(i32 %13) + %14 = load i32, i32* %8, align 4 + ret i32 %14 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @source() #0 { + ret i32 42 +} + +attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.module.flags = !{!0, !1, !2} +!llvm.ident = !{!3} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{!"Ubuntu clang version 13.0.0-2"} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh new file mode 100755 index 0000000000..457ed0fd79 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh @@ -0,0 +1,3 @@ +clang -shared -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ -o libnativetest.so NativeTest.c +clang -S -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ NativeTest.c -emit-llvm +javac NativeTest.java \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libnativetest.so b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libnativetest.so new file mode 100755 index 0000000000000000000000000000000000000000..3b7e7661f706f23a66240baff272c160597d40a6 GIT binary patch literal 15624 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 literal 0 HcmV?d00001 diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh new file mode 100755 index 0000000000..2c4db95e3f --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh @@ -0,0 +1 @@ +java -Djava.library.path=. NativeTest From b99997c56af152e0b1246e8eb11cbd6d6a341e04 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 18 Feb 2022 17:28:31 +0100 Subject: [PATCH 13/67] Format code --- .../scala/org/opalj/ll/LLPlayground.scala | 14 +- .../scala/org/opalj/ll/llvm/BasicBlock.scala | 92 +++++----- .../scala/org/opalj/ll/llvm/Function.scala | 70 +++---- .../scala/org/opalj/ll/llvm/Instruction.scala | 172 +++++++++--------- .../main/scala/org/opalj/ll/llvm/Module.scala | 54 +++--- .../main/scala/org/opalj/ll/llvm/Value.scala | 82 ++++----- 6 files changed, 242 insertions(+), 242 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala index 58b9f3b7e6..0d09626866 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala @@ -6,11 +6,11 @@ import org.opalj.ll.llvm.Reader object LLPlayground { - def main(args: Array[String]): Unit = { - val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll").get - val function = module.get_function("jsmn_parse_string") - println(function.name) - function.viewLLVMCFG(false) - function.viewCFG() - } + def main(args: Array[String]): Unit = { + val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll").get + val function = module.get_function("jsmn_parse_string") + println(function.name) + function.viewLLVMCFG(false) + function.viewCFG() + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala index e7ba8bc079..d14039e237 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala @@ -3,66 +3,66 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} import org.bytedeco.llvm.global.LLVM.{ - LLVMBasicBlockAsValue, - LLVMGetBasicBlockName, - LLVMGetBasicBlockTerminator, - LLVMGetFirstInstruction, - LLVMGetNextInstruction + LLVMBasicBlockAsValue, + LLVMGetBasicBlockName, + LLVMGetBasicBlockTerminator, + LLVMGetFirstInstruction, + LLVMGetNextInstruction } import org.opalj.graphs.Node case class BasicBlock(block_ref: LLVMBasicBlockRef) extends Value(LLVMBasicBlockAsValue(block_ref)) with Node { - def instructions(): InstructionIterator = { - new InstructionIterator(LLVMGetFirstInstruction(block_ref)) - } + def instructions(): InstructionIterator = { + new InstructionIterator(LLVMGetFirstInstruction(block_ref)) + } - def terminator(): Terminator = { - val instruction = Instruction(LLVMGetBasicBlockTerminator(block_ref)) - assert(instruction.is_terminator()) - instruction.asInstanceOf[Terminator] - } + def terminator(): Terminator = { + val instruction = Instruction(LLVMGetBasicBlockTerminator(block_ref)) + assert(instruction.is_terminator()) + instruction.asInstanceOf[Terminator] + } - def blockName(): String = LLVMGetBasicBlockName(block_ref).getString + def blockName(): String = LLVMGetBasicBlockName(block_ref).getString - /** - * Returns a human readable representation (HRR) of this node. - */ - override def toHRR: Option[String] = { - Some(name()) //TODO: maybe add more info - } + /** + * Returns a human readable representation (HRR) of this node. + */ + override def toHRR: Option[String] = { + Some(name()) //TODO: maybe add more info + } - /** - * An identifier that uniquely identifies this node in the graph to which this - * node belongs. By default two nodes are considered equal if they have the same - * unique id. - */ - override def nodeId: Int = { - block_ref.hashCode - } + /** + * An identifier that uniquely identifies this node in the graph to which this + * node belongs. By default two nodes are considered equal if they have the same + * unique id. + */ + override def nodeId: Int = { + block_ref.hashCode + } - /** - * Returns `true` if this node has successor nodes. - */ - override def hasSuccessors: Boolean = { - terminator.hasSuccessors() - } + /** + * Returns `true` if this node has successor nodes. + */ + override def hasSuccessors: Boolean = { + terminator.hasSuccessors() + } - /** - * Applies the given function for each successor node. - */ - override def foreachSuccessor(f: Node => Unit): Unit = { - terminator.foreachSuccessor(f) - } + /** + * Applies the given function for each successor node. + */ + override def foreachSuccessor(f: Node ⇒ Unit): Unit = { + terminator.foreachSuccessor(f) + } } class InstructionIterator(var ref: LLVMValueRef) extends Iterator[Instruction] { - override def hasNext: Boolean = LLVMGetNextInstruction(ref) != null + override def hasNext: Boolean = LLVMGetNextInstruction(ref) != null - override def next(): Instruction = { - val instruction = Instruction(ref) - this.ref = LLVMGetNextInstruction(ref) - instruction - } + override def next(): Instruction = { + val instruction = Instruction(ref) + this.ref = LLVMGetNextInstruction(ref) + instruction + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala index fd9031c372..ca8bc7a407 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala @@ -3,49 +3,49 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} import org.bytedeco.llvm.global.LLVM.{ - LLVMFunctionValueKind, - LLVMGetEntryBasicBlock, - LLVMGetFirstBasicBlock, - LLVMGetNextBasicBlock, - LLVMGetValueKind, - LLVMViewFunctionCFG, - LLVMViewFunctionCFGOnly + LLVMFunctionValueKind, + LLVMGetEntryBasicBlock, + LLVMGetFirstBasicBlock, + LLVMGetNextBasicBlock, + LLVMGetValueKind, + LLVMViewFunctionCFG, + LLVMViewFunctionCFGOnly } import org.opalj.io.writeAndOpen case class Function(ref: LLVMValueRef) extends Value(ref) { - assert(LLVMGetValueKind(ref) == LLVMFunctionValueKind, "ref has to be a function") - - def basicBlocks(): BasicBlockIterator = { - new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) - } - - def entryBlock(): BasicBlock = { - BasicBlock(LLVMGetEntryBasicBlock(ref)) - } - - def viewCFG(): Unit = { - val entryNode = entryBlock() - val cfg_dot = org.opalj.graphs.toDot(Set(entryNode)) - writeAndOpen(cfg_dot, name + "-CFG", ".gv") - } - - def viewLLVMCFG(include_content: Boolean = true): Unit = { - if (include_content) { - LLVMViewFunctionCFG(ref) - } else { - LLVMViewFunctionCFGOnly(ref) + assert(LLVMGetValueKind(ref) == LLVMFunctionValueKind, "ref has to be a function") + + def basicBlocks(): BasicBlockIterator = { + new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) + } + + def entryBlock(): BasicBlock = { + BasicBlock(LLVMGetEntryBasicBlock(ref)) + } + + def viewCFG(): Unit = { + val entryNode = entryBlock() + val cfg_dot = org.opalj.graphs.toDot(Set(entryNode)) + writeAndOpen(cfg_dot, name+"-CFG", ".gv") + } + + def viewLLVMCFG(include_content: Boolean = true): Unit = { + if (include_content) { + LLVMViewFunctionCFG(ref) + } else { + LLVMViewFunctionCFGOnly(ref) + } } - } } class BasicBlockIterator(var ref: LLVMBasicBlockRef) extends Iterator[BasicBlock] { - override def hasNext: Boolean = ref != null + override def hasNext: Boolean = ref != null - override def next(): BasicBlock = { - val basicBlock = BasicBlock(ref) - this.ref = LLVMGetNextBasicBlock(ref) - basicBlock - } + override def next(): BasicBlock = { + val basicBlock = BasicBlock(ref) + this.ref = LLVMGetNextBasicBlock(ref) + basicBlock + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala index 72f7d4b86f..5f999dbca5 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala @@ -5,93 +5,93 @@ import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM._ object Instruction { - def apply(ref: LLVMValueRef): Instruction = { - assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") - LLVMGetInstructionOpcode(ref) match { - case LLVMRet => Ret(ref) - case LLVMBr => Br(ref) - case LLVMSwitch => Switch(ref) - case LLVMIndirectBr => IndirectBr(ref) - case LLVMInvoke => Invoke(ref) - case LLVMUnreachable => Unreachable(ref) - case LLVMCallBr => CallBr(ref) - case LLVMFNeg => FNeg(ref) - case LLVMAdd => Add(ref) - case LLVMFAdd => FAdd(ref) - case LLVMSub => Sub(ref) - case LLVMFSub => FSub(ref) - case LLVMMul => Mul(ref) - case LLVMFMul => FMul(ref) - case LLVMUDiv => UDiv(ref) - case LLVMSDiv => SDiv(ref) - case LLVMFDiv => FDiv(ref) - case LLVMURem => URem(ref) - case LLVMSRem => SRem(ref) - case LLVMFRem => FRem(ref) - case LLVMShl => Shl(ref) - case LLVMLShr => LShr(ref) - case LLVMAShr => AShr(ref) - case LLVMAnd => And(ref) - case LLVMOr => Or(ref) - case LLVMXor => Xor(ref) - case LLVMAlloca => Alloca(ref) - case LLVMLoad => Load(ref) - case LLVMStore => Store(ref) - case LLVMGetElementPtr => GetElementPtr(ref) - case LLVMTrunc => Trunc(ref) - case LLVMZExt => ZExt(ref) - case LLVMSExt => SExt(ref) - case LLVMFPToUI => FPToUI(ref) - case LLVMFPToSI => FPToSI(ref) - case LLVMUIToFP => UIToFP(ref) - case LLVMSIToFP => SIToFP(ref) - case LLVMFPTrunc => FPTrunc(ref) - case LLVMFPExt => FPExt(ref) - case LLVMPtrToInt => PtrToInt(ref) - case LLVMIntToPtr => IntToPtr(ref) - case LLVMBitCast => BitCast(ref) - case LLVMAddrSpaceCast => AddrSpaceCast(ref) - case LLVMICmp => ICmp(ref) - case LLVMFCmp => FCmp(ref) - case LLVMPHI => PHI(ref) - case LLVMCall => Call(ref) - case LLVMSelect => Select(ref) - case LLVMUserOp1 => UserOp1(ref) - case LLVMUserOp2 => UserOp2(ref) - case LLVMVAArg => VAArg(ref) - case LLVMExtractElement => ExtractElement(ref) - case LLVMInsertElement => InsertElement(ref) - case LLVMShuffleVector => ShuffleVector(ref) - case LLVMExtractValue => ExtractValue(ref) - case LLVMInsertValue => InsertValue(ref) - case LLVMFreeze => Freeze(ref) - case LLVMFence => Fence(ref) - case LLVMAtomicCmpXchg => AtomicCmpXchg(ref) - case LLVMAtomicRMW => AtomicRMW(ref) - case LLVMResume => Resume(ref) - case LLVMLandingPad => LandingPad(ref) - case LLVMCleanupRet => CleanupRet(ref) - case LLVMCatchRet => CatchRet(ref) - case LLVMCatchPad => CatchPad(ref) - case LLVMCleanupPad => CleanupPad(ref) - case LLVMCatchSwitch => CatchSwitch(ref) - case opCode => throw new IllegalArgumentException("unknown instruction opcode: " + opCode) + def apply(ref: LLVMValueRef): Instruction = { + assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") + LLVMGetInstructionOpcode(ref) match { + case LLVMRet ⇒ Ret(ref) + case LLVMBr ⇒ Br(ref) + case LLVMSwitch ⇒ Switch(ref) + case LLVMIndirectBr ⇒ IndirectBr(ref) + case LLVMInvoke ⇒ Invoke(ref) + case LLVMUnreachable ⇒ Unreachable(ref) + case LLVMCallBr ⇒ CallBr(ref) + case LLVMFNeg ⇒ FNeg(ref) + case LLVMAdd ⇒ Add(ref) + case LLVMFAdd ⇒ FAdd(ref) + case LLVMSub ⇒ Sub(ref) + case LLVMFSub ⇒ FSub(ref) + case LLVMMul ⇒ Mul(ref) + case LLVMFMul ⇒ FMul(ref) + case LLVMUDiv ⇒ UDiv(ref) + case LLVMSDiv ⇒ SDiv(ref) + case LLVMFDiv ⇒ FDiv(ref) + case LLVMURem ⇒ URem(ref) + case LLVMSRem ⇒ SRem(ref) + case LLVMFRem ⇒ FRem(ref) + case LLVMShl ⇒ Shl(ref) + case LLVMLShr ⇒ LShr(ref) + case LLVMAShr ⇒ AShr(ref) + case LLVMAnd ⇒ And(ref) + case LLVMOr ⇒ Or(ref) + case LLVMXor ⇒ Xor(ref) + case LLVMAlloca ⇒ Alloca(ref) + case LLVMLoad ⇒ Load(ref) + case LLVMStore ⇒ Store(ref) + case LLVMGetElementPtr ⇒ GetElementPtr(ref) + case LLVMTrunc ⇒ Trunc(ref) + case LLVMZExt ⇒ ZExt(ref) + case LLVMSExt ⇒ SExt(ref) + case LLVMFPToUI ⇒ FPToUI(ref) + case LLVMFPToSI ⇒ FPToSI(ref) + case LLVMUIToFP ⇒ UIToFP(ref) + case LLVMSIToFP ⇒ SIToFP(ref) + case LLVMFPTrunc ⇒ FPTrunc(ref) + case LLVMFPExt ⇒ FPExt(ref) + case LLVMPtrToInt ⇒ PtrToInt(ref) + case LLVMIntToPtr ⇒ IntToPtr(ref) + case LLVMBitCast ⇒ BitCast(ref) + case LLVMAddrSpaceCast ⇒ AddrSpaceCast(ref) + case LLVMICmp ⇒ ICmp(ref) + case LLVMFCmp ⇒ FCmp(ref) + case LLVMPHI ⇒ PHI(ref) + case LLVMCall ⇒ Call(ref) + case LLVMSelect ⇒ Select(ref) + case LLVMUserOp1 ⇒ UserOp1(ref) + case LLVMUserOp2 ⇒ UserOp2(ref) + case LLVMVAArg ⇒ VAArg(ref) + case LLVMExtractElement ⇒ ExtractElement(ref) + case LLVMInsertElement ⇒ InsertElement(ref) + case LLVMShuffleVector ⇒ ShuffleVector(ref) + case LLVMExtractValue ⇒ ExtractValue(ref) + case LLVMInsertValue ⇒ InsertValue(ref) + case LLVMFreeze ⇒ Freeze(ref) + case LLVMFence ⇒ Fence(ref) + case LLVMAtomicCmpXchg ⇒ AtomicCmpXchg(ref) + case LLVMAtomicRMW ⇒ AtomicRMW(ref) + case LLVMResume ⇒ Resume(ref) + case LLVMLandingPad ⇒ LandingPad(ref) + case LLVMCleanupRet ⇒ CleanupRet(ref) + case LLVMCatchRet ⇒ CatchRet(ref) + case LLVMCatchPad ⇒ CatchPad(ref) + case LLVMCleanupPad ⇒ CleanupPad(ref) + case LLVMCatchSwitch ⇒ CatchSwitch(ref) + case opCode ⇒ throw new IllegalArgumentException("unknown instruction opcode: "+opCode) + } } - } } trait Terminator { - val ref: LLVMValueRef - def numSuccessors(): Int = LLVMGetNumSuccessors(ref) - def hasSuccessors(): Boolean = numSuccessors() > 0 - def getSuccessor(i: Int) = BasicBlock(LLVMGetSuccessor(ref, i)) - def foreachSuccessor(f: BasicBlock => Unit): Unit = { - (0 to numSuccessors() - 1).foreach(i => f(getSuccessor(i))) - } + val ref: LLVMValueRef + def numSuccessors(): Int = LLVMGetNumSuccessors(ref) + def hasSuccessors(): Boolean = numSuccessors() > 0 + def getSuccessor(i: Int) = BasicBlock(LLVMGetSuccessor(ref, i)) + def foreachSuccessor(f: BasicBlock ⇒ Unit): Unit = { + (0 to numSuccessors() - 1).foreach(i ⇒ f(getSuccessor(i))) + } } sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { - def is_terminator(): Boolean = !LLVMIsATerminatorInst(ref).isNull + def is_terminator(): Boolean = !LLVMIsATerminatorInst(ref).isNull } case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator @@ -123,13 +123,13 @@ case class Xor(ref: LLVMValueRef) extends Instruction(ref) case class Alloca(ref: LLVMValueRef) extends Instruction(ref) case class Load(ref: LLVMValueRef) extends Instruction(ref) case class Store(ref: LLVMValueRef) extends Instruction(ref) { - def src(): Value = { - Value(LLVMGetOperand(ref, 0)).asInstanceOf[Value] - } + def src(): Value = { + Value(LLVMGetOperand(ref, 0)).asInstanceOf[Value] + } - def dst(): Value = { - Value(LLVMGetOperand(ref, 1)).asInstanceOf[Value] - } + def dst(): Value = { + Value(LLVMGetOperand(ref, 1)).asInstanceOf[Value] + } } case class GetElementPtr(ref: LLVMValueRef) extends Instruction(ref) case class Trunc(ref: LLVMValueRef) extends Instruction(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala index 33a8a30783..40a31005cd 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala @@ -3,41 +3,41 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMModuleRef, LLVMValueRef} import org.bytedeco.llvm.global.LLVM.{ - LLVMDisposeMessage, - LLVMGetFirstFunction, - LLVMGetNamedFunction, - LLVMGetNextFunction, - LLVMPrintModuleToString + LLVMDisposeMessage, + LLVMGetFirstFunction, + LLVMGetNamedFunction, + LLVMGetNextFunction, + LLVMPrintModuleToString } case class Module(ref: LLVMModuleRef) { - def functions(): FunctionIterator = { - new FunctionIterator(LLVMGetFirstFunction(ref)) - } + def functions(): FunctionIterator = { + new FunctionIterator(LLVMGetFirstFunction(ref)) + } - def repr(): String = { - val bytePointer = LLVMPrintModuleToString(ref) - val string = bytePointer.getString - LLVMDisposeMessage(bytePointer) - string - } + def repr(): String = { + val bytePointer = LLVMPrintModuleToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } - def get_function(name: String): Function = { - val func_ref = LLVMGetNamedFunction(ref, name) - Value(func_ref) match { - case _: NullValue => - throw new IllegalArgumentException("Unknown function '" + name + "'") - case function: Function => function + def get_function(name: String): Function = { + val func_ref = LLVMGetNamedFunction(ref, name) + Value(func_ref) match { + case _: NullValue ⇒ + throw new IllegalArgumentException("Unknown function '"+name+"'") + case function: Function ⇒ function + } } - } } class FunctionIterator(var ref: LLVMValueRef) extends Iterator[Function] { - override def hasNext: Boolean = ref != null + override def hasNext: Boolean = ref != null - override def next(): Function = { - val function = Function(ref) - this.ref = LLVMGetNextFunction(ref) - function - } + override def next(): Function = { + val function = Function(ref) + this.ref = LLVMGetNextFunction(ref) + function + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala index 2bebae7df7..38d9510642 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala @@ -7,52 +7,52 @@ import org.bytedeco.llvm.global.LLVM._ class OptionalValue class Value(ref: LLVMValueRef) extends OptionalValue { - def repr(): String = { - val bytePointer = LLVMPrintValueToString(ref) - val string = bytePointer.getString - LLVMDisposeMessage(bytePointer) - string - } + def repr(): String = { + val bytePointer = LLVMPrintValueToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } - def name(): String = { - LLVMGetValueName(ref).getString - } + def name(): String = { + LLVMGetValueName(ref).getString + } } case class NullValue() extends OptionalValue object Value { - def apply(ref: LLVMValueRef): OptionalValue = { - if (ref == null) return NullValue() - if (ref.isNull) return NullValue() - LLVMGetValueKind(ref) match { - //LLVMArgumentValueKind - case LLVMBasicBlockValueKind => BasicBlock(LLVMValueAsBasicBlock(ref)) - //LLVMMemoryUseValueKind - //LLVMMemoryDefValueKind - //LLVMMemoryPhiValueKind - case LLVMFunctionValueKind => Function(ref) - //LLVMGlobalAliasValueKind - //LLVMGlobalIFuncValueKind - case LLVMGlobalVariableValueKind => GlobalVariable(ref) - //LLVMBlockAddressValueKind - //LLVMConstantExprValueKind - //LLVMConstantArrayValueKind - //LLVMConstantStructValueKind - //LLVMConstantVectorValueKind - //LLVMUndefValueValueKind - //LLVMConstantAggregateZeroValueKind - //LLVMConstantDataArrayValueKind - //LLVMConstantDataVectorValueKind - //LLVMConstantIntValueKind - //LLVMConstantFPValueKind - //LLVMConstantPointerNullValueKind - //LLVMConstantTokenNoneValueKind - //LLVMMetadataAsValueValueKind - //LLVMInlineAsmValueKind - case LLVMInstructionValueKind => Instruction(ref) - //LLVMPoisonValueValueKind - case valueKind => throw new IllegalArgumentException("unknown valueKind: " + valueKind) + def apply(ref: LLVMValueRef): OptionalValue = { + if (ref == null) return NullValue() + if (ref.isNull) return NullValue() + LLVMGetValueKind(ref) match { + //LLVMArgumentValueKind + case LLVMBasicBlockValueKind ⇒ BasicBlock(LLVMValueAsBasicBlock(ref)) + //LLVMMemoryUseValueKind + //LLVMMemoryDefValueKind + //LLVMMemoryPhiValueKind + case LLVMFunctionValueKind ⇒ Function(ref) + //LLVMGlobalAliasValueKind + //LLVMGlobalIFuncValueKind + case LLVMGlobalVariableValueKind ⇒ GlobalVariable(ref) + //LLVMBlockAddressValueKind + //LLVMConstantExprValueKind + //LLVMConstantArrayValueKind + //LLVMConstantStructValueKind + //LLVMConstantVectorValueKind + //LLVMUndefValueValueKind + //LLVMConstantAggregateZeroValueKind + //LLVMConstantDataArrayValueKind + //LLVMConstantDataVectorValueKind + //LLVMConstantIntValueKind + //LLVMConstantFPValueKind + //LLVMConstantPointerNullValueKind + //LLVMConstantTokenNoneValueKind + //LLVMMetadataAsValueValueKind + //LLVMInlineAsmValueKind + case LLVMInstructionValueKind ⇒ Instruction(ref) + //LLVMPoisonValueValueKind + case valueKind ⇒ throw new IllegalArgumentException("unknown valueKind: "+valueKind) + } } - } } From 90c179a61be401f974cbb11594256c7a6f60df60 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Mon, 21 Feb 2022 21:43:23 +0100 Subject: [PATCH 14/67] Adapt new ifds to new call graph (with context) --- .../heros/analyses/HerosAnalysis.scala | 29 +- .../analyses/HerosVariableTypeAnalysis.scala | 127 +++--- .../analyses/heros/analyses/VTAEquality.scala | 2 +- .../HerosForwardClassForNameAnalysis.scala | 128 ++++-- .../fpcf/analyses/heros/cfg/OpalICFG.scala | 97 +++-- .../analyses/ifds/AbstractIFDSAnalysis.scala | 373 +++++++++++------- .../analyses/ifds/BackwardIFDSAnalysis.scala | 183 +++++---- .../ifds/IFDSBasedVariableTypeAnalysis.scala | 163 +++++--- scalastyle-config.xml | 2 +- 9 files changed, 703 insertions(+), 401 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala index b069c620ec..d49762d069 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala @@ -3,12 +3,11 @@ package org.opalj.tac.fpcf.analyses.heros.analyses import java.io.File import java.io.PrintWriter - import heros.template.DefaultIFDSTabulationProblem import heros.solver.IFDSSolver + import javax.swing.JOptionPane import org.opalj.bytecode - import org.opalj.util.Milliseconds import org.opalj.util.PerformanceEvaluation.time import org.opalj.fpcf.FinalEP @@ -19,7 +18,7 @@ import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.analyses.Project -import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.fpcf.analyses.heros.cfg.OpalICFG @@ -28,7 +27,8 @@ import org.opalj.tac.Assignment import org.opalj.tac.Call import org.opalj.tac.ExprStmt import org.opalj.tac.Stmt -import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.cg.{RTACallGraphKey, TypeProviderKey} +import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V /** @@ -45,12 +45,14 @@ abstract class HerosAnalysis[F](p: SomeProject, icfg: OpalICFG) /** * The project's property store. */ - protected implicit val propertyStore: PropertyStore = p.get(PropertyStoreKey) + implicit protected val propertyStore: PropertyStore = p.get(PropertyStoreKey) /** * The project's declared methods. */ - protected implicit val declaredMethods: DeclaredMethods = p.get(DeclaredMethodsKey) + implicit protected val declaredMethods: DeclaredMethods = p.get(DeclaredMethodsKey) + + implicit protected val typeProvider: TypeProvider = p.get(TypeProviderKey) /** * The number of threads can be configured with NUM_THREADS. @@ -87,7 +89,9 @@ object HerosAnalysis { * @param method The method. * @return True, if the method can be called from outside. */ - def canBeCalledFromOutside(method: Method)(implicit declaredMethods: DeclaredMethods, propertyStore: PropertyStore): Boolean = { + def canBeCalledFromOutside( + method: Method + )(implicit declaredMethods: DeclaredMethods, propertyStore: PropertyStore): Boolean = { val FinalEP(_, callers) = propertyStore(declaredMethods(method), Callers.key) callers.hasCallersWithUnknownContext } @@ -147,8 +151,9 @@ abstract class HerosAnalysisRunner[F, Analysis <: HerosAnalysis[F]] { */ private def evalProject(p: SomeProject): Milliseconds = { p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) - case Some(requirements) ⇒ requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] + case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) ⇒ + requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] } p.get(RTACallGraphKey) val analysis = createAnalysis(p) @@ -158,7 +163,9 @@ abstract class HerosAnalysisRunner[F, Analysis <: HerosAnalysis[F]] { JOptionPane.showMessageDialog(null, "Call Graph finished") time { solver.solve() - } { t ⇒ analysisTime = t.toMilliseconds } + } { t ⇒ + analysisTime = t.toMilliseconds + } if (HerosAnalysis.MEASURE_MEMORY) JOptionPane.showMessageDialog(null, "Analysis finished") printResultsToConsole(analysis, analysisTime) @@ -172,4 +179,4 @@ object HerosAnalysisRunner { * The number of analysis runs, which will be performed by the run method. */ var NUM_EXECUTIONS = 10 -} \ No newline at end of file +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala index d11ed3f00a..a3bbcb1e3c 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala @@ -29,7 +29,7 @@ import org.opalj.br.FieldType import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.DeclaredMethod -import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.tac.fpcf.analyses.ifds.Statement import org.opalj.tac.Assignment import org.opalj.tac.Expr @@ -53,13 +53,16 @@ import org.opalj.tac.fpcf.analyses.ifds.VTANullFact * * @author Mario Trageser */ -class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMethods: Map[Method, util.Set[VTAFact]]) - extends HerosAnalysis[VTAFact](p, icfg) { +class HerosVariableTypeAnalysis( + p: SomeProject, + icfg: OpalForwardICFG, + initialMethods: Map[Method, util.Set[VTAFact]] +) extends HerosAnalysis[VTAFact](p, icfg) { override val initialSeeds: util.Map[Statement, util.Set[VTAFact]] = { var result: Map[Statement, util.Set[VTAFact]] = Map.empty for ((m, facts) ← initialMethods) { - result += icfg.getStartPointsOf(m).iterator().next() → facts + result += icfg.getStartPointsOf(m).iterator().next() -> facts } result.asJava } @@ -70,14 +73,17 @@ class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMe new FlowFunctions[Statement, VTAFact, Method]() { - override def getNormalFlowFunction(statement: Statement, succ: Statement): FlowFunction[VTAFact] = { + override def getNormalFlowFunction( + statement: Statement, + succ: Statement + ): FlowFunction[VTAFact] = { if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt stmt.astID match { case Assignment.ASTID ⇒ (source: VTAFact) ⇒ { - val fact = newFact(statement.method, statement.stmt.asAssignment.expr, - statement.index, source) + val fact = + newFact(statement.method, statement.stmt.asAssignment.expr, statement.index, source) if (fact.isDefined) TwoElementSet.twoElementSet(source, fact.get) else Collections.singleton(source) } @@ -85,8 +91,7 @@ class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMe (source: VTAFact) ⇒ { val flow = scala.collection.mutable.Set.empty[VTAFact] flow += source - newFact(statement.method, stmt.asArrayStore.value, statement.index, - source).foreach { + newFact(statement.method, stmt.asArrayStore.value, statement.index, source).foreach { case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) ⇒ stmt.asArrayStore.arrayRef.asVar.definedBy .foreach(flow += VariableType(_, ArrayType(t), upperBound)) @@ -110,8 +115,10 @@ class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMe allParams.iterator.zipWithIndex.foreach { case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ flow += VariableType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(parameterIndex, callee.isStatic), - t, upperBound + AbstractIFDSAnalysis + .switchParamAndVariableIndex(parameterIndex, callee.isStatic), + t, + upperBound ) case _ ⇒ } @@ -121,7 +128,12 @@ class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMe } } - override def getReturnFlowFunction(stmt: Statement, callee: Method, exit: Statement, succ: Statement): FlowFunction[VTAFact] = + override def getReturnFlowFunction( + stmt: Statement, + callee: Method, + exit: Statement, + succ: Statement + ): FlowFunction[VTAFact] = if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { val returnValue = exit.stmt.asReturnValue.expr.asVar source: VTAFact ⇒ { @@ -134,14 +146,22 @@ class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMe } } else KillAll.v() - override def getCallToReturnFlowFunction(statement: Statement, succ: Statement): FlowFunction[VTAFact] = { + override def getCallToReturnFlowFunction( + statement: Statement, + succ: Statement + ): FlowFunction[VTAFact] = { if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt val calleeDefinitionSites = asCall(stmt).receiverOption - .map(callee ⇒ callee.asVar.definedBy).getOrElse(EmptyIntTrieSet) + .map(callee ⇒ callee.asVar.definedBy) + .getOrElse(EmptyIntTrieSet) val callOutsideOfAnalysisContext = - getCallees(statement).exists(method ⇒ - !((method.hasSingleDefinedMethod || method.hasMultipleDefinedMethods) && insideAnalysisContext(method.definedMethod))) + getCallees(statement).exists( + method ⇒ + !((method.hasSingleDefinedMethod || method.hasMultipleDefinedMethods) && insideAnalysisContext( + method.definedMethod + )) + ) source: VTAFact ⇒ { val result = scala.collection.mutable.Set[VTAFact](source) source match { @@ -161,25 +181,31 @@ class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMe } } - private def newFact(method: Method, expression: Expr[DUVar[ValueInformation]], - statementIndex: Int, - source: VTAFact): Option[VariableType] = expression.astID match { - case New.ASTID ⇒ source match { - case VTANullFact ⇒ - Some(VariableType(statementIndex, expression.asNew.tpe, upperBound = false)) - case _ ⇒ None - } - case Var.ASTID ⇒ source match { - case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ - Some(VariableType(statementIndex, t, upperBound)) - case _ ⇒ None - } - case ArrayLoad.ASTID ⇒ source match { - case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && - expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ - Some(VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound)) - case _ ⇒ None - } + private def newFact( + method: Method, + expression: Expr[DUVar[ValueInformation]], + statementIndex: Int, + source: VTAFact + ): Option[VariableType] = expression.astID match { + case New.ASTID ⇒ + source match { + case VTANullFact ⇒ + Some(VariableType(statementIndex, expression.asNew.tpe, upperBound = false)) + case _ ⇒ None + } + case Var.ASTID ⇒ + source match { + case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ + Some(VariableType(statementIndex, t, upperBound)) + case _ ⇒ None + } + case ArrayLoad.ASTID ⇒ + source match { + case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && + expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ + Some(VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound)) + case _ ⇒ None + } case GetField.ASTID | GetStatic.ASTID ⇒ val t = expression.asFieldRead.declaredFieldType if (t.isReferenceType) @@ -188,7 +214,10 @@ class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMe case _ ⇒ None } - @tailrec private def isArrayOfObjectType(t: FieldType, includeObjectType: Boolean = false): Boolean = { + @tailrec private def isArrayOfObjectType( + t: FieldType, + includeObjectType: Boolean = false + ): Boolean = { if (t.isArrayType) isArrayOfObjectType(t.asArrayType.elementType, includeObjectType = true) else if (t.isObjectType && includeObjectType) true else false @@ -199,28 +228,37 @@ class HerosVariableTypeAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMe callee.classFile.fqn.startsWith("org/opalj/fpcf/fixtures/vta")) private def getCallees(statement: Statement): Iterator[DeclaredMethod] = { + val context = typeProvider.newContext(declaredMethods(statement.method)) val FinalEP(_, callees) = propertyStore(declaredMethods(statement.method), Callees.key) - callees.directCallees(statement.stmt.pc) + callees.directCallees(context, statement.stmt.pc).map(_.method) } } -class HerosVariableTypeAnalysisRunner extends HerosAnalysisRunner[VTAFact, HerosVariableTypeAnalysis] { +class HerosVariableTypeAnalysisRunner + extends HerosAnalysisRunner[VTAFact, HerosVariableTypeAnalysis] { override protected def createAnalysis(p: SomeProject): HerosVariableTypeAnalysis = { implicit val declaredMethods: DeclaredMethods = p.get(DeclaredMethodsKey) implicit val propertyStore: PropertyStore = p.get(PropertyStoreKey) val initialMethods = - p.allProjectClassFiles.filter(_.fqn.startsWith("java/lang")) + p.allProjectClassFiles + .filter(_.fqn.startsWith("java/lang")) .flatMap(classFile ⇒ classFile.methods) .filter(isEntryPoint) - .map(method ⇒ method → entryPointsForMethod(method).asJava).toMap + .map(method ⇒ method -> entryPointsForMethod(method).asJava) + .toMap new HerosVariableTypeAnalysis(p, new OpalForwardICFG(p), initialMethods) } - override protected def printResultsToConsole(analysis: HerosVariableTypeAnalysis, analysisTime: Milliseconds): Unit = {} + override protected def printResultsToConsole( + analysis: HerosVariableTypeAnalysis, + analysisTime: Milliseconds + ): Unit = {} - private def isEntryPoint(method: Method)(implicit declaredMethods: DeclaredMethods, propertyStore: PropertyStore): Boolean = { + private def isEntryPoint( + method: Method + )(implicit declaredMethods: DeclaredMethods, propertyStore: PropertyStore): Boolean = { method.body.isDefined && HerosAnalysis.canBeCalledFromOutside(method) } @@ -229,7 +267,8 @@ class HerosVariableTypeAnalysisRunner extends HerosAnalysisRunner[VTAFact, Heros case (t, index) if t.isReferenceType ⇒ VariableType( AbstractIFDSAnalysis.switchParamAndVariableIndex(index, method.isStatic), - t.asReferenceType, upperBound = true + t.asReferenceType, + upperBound = true ) } :+ VTANullFact).toSet } @@ -243,4 +282,4 @@ object HerosVariableTypeAnalysisRunner { if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None ) } -} \ No newline at end of file +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala index 5184750669..9e37428097 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala @@ -21,7 +21,7 @@ import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.DefinedMethod import org.opalj.br.Method import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.cg.InitialEntryPointsKey import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index 887291d061..66596c5dd2 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -22,7 +22,7 @@ import org.opalj.br.ObjectType import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.DeclaredMethod import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG import org.opalj.tac.Assignment @@ -60,12 +60,16 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V * * @author Mario Trageser */ -class HerosForwardClassForNameAnalysis(p: SomeProject, icfg: OpalForwardICFG, initialMethods: Map[Method, util.Set[Fact]]) extends HerosTaintAnalysis(p, icfg) { +class HerosForwardClassForNameAnalysis( + p: SomeProject, + icfg: OpalForwardICFG, + initialMethods: Map[Method, util.Set[Fact]] +) extends HerosTaintAnalysis(p, icfg) { override val initialSeeds: util.Map[Statement, util.Set[Fact]] = { var result: Map[Statement, util.Set[Fact]] = Map.empty for ((m, facts) ← initialMethods) { - result += icfg.getStartPointsOf(m).iterator().next() → facts + result += icfg.getStartPointsOf(m).iterator().next() -> facts } result.asJava } @@ -116,7 +120,9 @@ class HerosForwardClassForNameAnalysis(p: SomeProject, icfg: OpalForwardICFG, in val definedBy = put.objRef.asVar.definedBy (source: Fact) ⇒ { if (isTainted(put.value, source)) { - (definedBy.iterator.map(InstanceField(_, put.declaringClass, put.name)).toSet[Fact] + source).asJava + (definedBy.iterator + .map(InstanceField(_, put.declaringClass, put.name)) + .toSet[Fact] + source).asJava } else { Collections.singleton(source) } @@ -180,11 +186,13 @@ class HerosForwardClassForNameAnalysis(p: SomeProject, icfg: OpalForwardICFG, in (source: Fact) ⇒ { val result = new util.HashSet[Fact] (0 until expr.subExprCount) - .foreach(subExpression ⇒ - result.addAll( - handleAssignment(stmt, expr.subExpr(subExpression)) - .computeTargets(source) - )) + .foreach( + subExpression ⇒ + result.addAll( + handleAssignment(stmt, expr.subExpr(subExpression)) + .computeTargets(source) + ) + ) result } case _ ⇒ Identity.v() @@ -198,43 +206,68 @@ class HerosForwardClassForNameAnalysis(p: SomeProject, icfg: OpalForwardICFG, in source: Fact ⇒ (source match { case Variable(index) ⇒ // Taint formal parameter if actual parameter is tainted - allParamsWithIndices.collect { - case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ - Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic)) - }.toSet[Fact] + allParamsWithIndices + .collect { + case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ + Variable( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic) + ) + } + .toSet[Fact] case ArrayElement(index, taintedIndex) ⇒ // Taint element of formal parameter if element of actual parameter is tainted - allParamsWithIndices.collect { - case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ - ArrayElement(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic), taintedIndex) - }.toSet[Fact] + allParamsWithIndices + .collect { + case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ + ArrayElement( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic), + taintedIndex + ) + } + .toSet[Fact] case InstanceField(index, declClass, taintedField) ⇒ // Taint field of formal parameter if field of actual parameter is tainted // Only if the formal parameter is of a type that may have that field! - allParamsWithIndices.collect { - case (param, paramIdx) if param.asVar.definedBy.contains(index) && - (AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic) != -1 || - classHierarchy.isSubtypeOf(declClass, callee.classFile.thisType)) ⇒ - InstanceField(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic), declClass, taintedField) - }.toSet[Fact] + allParamsWithIndices + .collect { + case (param, paramIdx) if param.asVar.definedBy.contains(index) && + (AbstractIFDSAnalysis.switchParamAndVariableIndex( + paramIdx, + callee.isStatic + ) != -1 || + classHierarchy.isSubtypeOf(declClass, callee.classFile.thisType)) ⇒ + InstanceField( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic), + declClass, + taintedField + ) + } + .toSet[Fact] case sf: StaticField ⇒ Set(sf).asInstanceOf[Set[Fact]] case _ ⇒ Set.empty[Fact] }).asJava } else KillAll.v() } - override def getReturnFlowFunction(stmt: Statement, callee: Method, exit: Statement, succ: Statement): FlowFunction[Fact] = { + override def getReturnFlowFunction( + stmt: Statement, + callee: Method, + exit: Statement, + succ: Statement + ): FlowFunction[Fact] = { def isRefTypeParam(index: Int): Boolean = if (index == -1) true else { val parameterOffset = if (callee.isStatic) 0 else 1 - callee.descriptor.parameterType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) - - parameterOffset - ).isReferenceType + callee.descriptor + .parameterType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + - parameterOffset + ) + .isReferenceType } val callStatement = asCall(stmt.stmt) @@ -252,14 +285,22 @@ class HerosForwardClassForNameAnalysis(p: SomeProject, icfg: OpalForwardICFG, in case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ // Taint element of actual parameter if element of formal parameter is tainted val params = - asCall(stmt.stmt).allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) - params.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)).asInstanceOf[Iterator[Fact]].toSet + asCall(stmt.stmt).allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + ) + params.asVar.definedBy.iterator + .map(ArrayElement(_, taintedIndex)) + .asInstanceOf[Iterator[Fact]] + .toSet case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ // Taint field of actual parameter if field of formal parameter is tainted val params = allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) - params.asVar.definedBy.iterator.map(InstanceField(_, declClass, taintedField)).asInstanceOf[Iterator[Fact]].toSet + params.asVar.definedBy.iterator + .map(InstanceField(_, declClass, taintedField)) + .asInstanceOf[Iterator[Fact]] + .toSet case sf: StaticField ⇒ Set(sf) case FlowFact(flow) if !flow.contains(stmt.method) ⇒ @@ -306,7 +347,10 @@ class HerosForwardClassForNameAnalysis(p: SomeProject, icfg: OpalForwardICFG, in } } - override def getCallToReturnFlowFunction(stmt: Statement, succ: Statement): FlowFunction[Fact] = + override def getCallToReturnFlowFunction( + stmt: Statement, + succ: Statement + ): FlowFunction[Fact] = Identity.v() } } @@ -334,14 +378,16 @@ class HerosForwardClassForNameAnalysis(p: SomeProject, icfg: OpalForwardICFG, in declaredMethods(method).declaringClassType == ObjectType.Class && method.name == "forName" } -class HerosForwardClassForNameAnalysisRunner extends HerosAnalysisRunner[Fact, HerosForwardClassForNameAnalysis] { +class HerosForwardClassForNameAnalysisRunner + extends HerosAnalysisRunner[Fact, HerosForwardClassForNameAnalysis] { override protected def createAnalysis(p: SomeProject): HerosForwardClassForNameAnalysis = { val declaredMethods = p.get(DeclaredMethodsKey) val propertyStore = p.get(PropertyStoreKey) val initialMethods = scala.collection.mutable.Map.empty[Method, util.Set[Fact]] for { - method ← declaredMethods.declaredMethods.filter(canBeCalledFromOutside(_, propertyStore)) + method ← declaredMethods.declaredMethods + .filter(canBeCalledFromOutside(_, propertyStore)) .map(_.definedMethod) index ← method.descriptor.parameterTypes.zipWithIndex.collect { case (pType, index) if pType == ObjectType.String ⇒ @@ -350,12 +396,15 @@ class HerosForwardClassForNameAnalysisRunner extends HerosAnalysisRunner[Fact, H } { val fact = Variable(-2 - index) if (initialMethods.contains(method)) initialMethods(method).add(fact) - else initialMethods += (method → new util.HashSet[Fact](Collections.singleton(fact))) + else initialMethods += (method -> new util.HashSet[Fact](Collections.singleton(fact))) } new HerosForwardClassForNameAnalysis(p, new OpalForwardICFG(p), initialMethods.toMap) } - override protected def printResultsToConsole(analysis: HerosForwardClassForNameAnalysis, analysisTime: Milliseconds): Unit = { + override protected def printResultsToConsole( + analysis: HerosForwardClassForNameAnalysis, + analysisTime: Milliseconds + ): Unit = { for { method ← analysis.flowFacts.keys fact ← analysis.flowFacts(method) @@ -363,7 +412,10 @@ class HerosForwardClassForNameAnalysisRunner extends HerosAnalysisRunner[Fact, H println(s"Time: $analysisTime") } - private def canBeCalledFromOutside(method: DeclaredMethod, propertyStore: PropertyStore): Boolean = { + private def canBeCalledFromOutside( + method: DeclaredMethod, + propertyStore: PropertyStore + ): Boolean = { val FinalEP(_, callers) = propertyStore(method, Callers.key) callers.hasCallersWithUnknownContext } @@ -377,4 +429,4 @@ object HerosForwardClassForNameAnalysisRunner { if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None ) } -} \ No newline at end of file +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala index ca1752e482..dbbb22113f 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala @@ -4,11 +4,8 @@ package org.opalj.tac.fpcf.analyses.heros.cfg import java.util.{List ⇒ JList} import java.util.{Collection ⇒ JCollection} import java.util.{Set ⇒ JSet} - import scala.collection.JavaConverters._ - import heros.InterproceduralCFG - import org.opalj.fpcf.FinalEP import org.opalj.fpcf.PropertyStore import org.opalj.value.ValueInformation @@ -17,10 +14,10 @@ import org.opalj.br.Method import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.fpcf.properties.cg.Callees +import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.br.DefinedMethod import org.opalj.br.MultipleDefinedMethods -import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.analyses.ifds.Statement import org.opalj.tac.Assignment import org.opalj.tac.DUVar @@ -38,6 +35,8 @@ import org.opalj.tac.StaticFunctionCall import org.opalj.tac.StaticMethodCall import org.opalj.tac.VirtualFunctionCall import org.opalj.tac.VirtualMethodCall +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.fpcf.analyses.cg.TypeProvider /** * The superclass of the forward and backward ICFG for Heros analyses. @@ -47,59 +46,89 @@ import org.opalj.tac.VirtualMethodCall */ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[Statement, Method] { - val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) + val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = + project.get(LazyDetachedTACAIKey) implicit val ps: PropertyStore = project.get(PropertyStoreKey) implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) override def getMethodOf(stmt: Statement): Method = stmt.method override def getPredsOf(stmt: Statement): JList[Statement] = { - stmt.cfg.predecessors(stmt.index).toChain.map { index ⇒ - Statement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) - }.toList.asJava + stmt.cfg + .predecessors(stmt.index) + .toChain + .map { index ⇒ + Statement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) + } + .toList + .asJava } override def getSuccsOf(stmt: Statement): JList[Statement] = { - stmt.cfg.successors(stmt.index).toChain.map { index ⇒ - Statement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) - }.toList.asJava + stmt.cfg + .successors(stmt.index) + .toChain + .map { index ⇒ + Statement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) + } + .toList + .asJava } override def getCalleesOfCallAt(callInstr: Statement): JCollection[Method] = { - val FinalEP(_, callees) = ps(declaredMethods(callInstr.method), Callees.key) - callees.directCallees(callInstr.stmt.pc).collect { - case d: DefinedMethod ⇒ List(d.definedMethod) - case md: MultipleDefinedMethods ⇒ md.definedMethods - }.flatten.filter(_.body.isDefined).toList.asJava + val declaredMethod = declaredMethods(callInstr.method) + val FinalEP(_, callees) = ps(declaredMethod, Callees.key) + callees + .directCallees(typeProvider.newContext(declaredMethod), callInstr.stmt.pc) + .map(_.method) + .collect { + case d: DefinedMethod ⇒ List(d.definedMethod) + case md: MultipleDefinedMethods ⇒ md.definedMethods + } + .flatten + .filter(_.body.isDefined) + .toList + .asJava } override def getCallersOf(m: Method): JCollection[Statement] = { - val FinalEP(_, callers) = ps(declaredMethods(m), Callers.key) - callers.callers.flatMap { - case (method, pc, true) ⇒ - val TACode(_, code, pcToIndex, cfg, _) = tacai(method.definedMethod) - val index = pcToIndex(pc) - Some(Statement(method.definedMethod, cfg.bb(index), code(index), index, code, cfg)) - case _ ⇒ None - }.toSet.asJava + val declaredMethod = declaredMethods(m) + val FinalEP(_, callers) = ps(declaredMethod, Callers.key) + callers + .callers(declaredMethod) + .flatMap { + case (method, pc, true) ⇒ + val TACode(_, code, pcToIndex, cfg, _) = tacai(method.definedMethod) + val index = pcToIndex(pc) + Some(Statement(method.definedMethod, cfg.bb(index), code(index), index, code, cfg)) + case _ ⇒ None + } + .toSet + .asJava } override def getCallsFromWithin(m: Method): JSet[Statement] = { val TACode(_, code, _, cfg, _) = tacai(m) - code.zipWithIndex.collect { - case (mc: MethodCall[V], index) ⇒ Statement(m, cfg.bb(index), mc, index, code, cfg) - case (as @ Assignment(_, _, _: FunctionCall[V]), index) ⇒ - Statement(m, cfg.bb(index), as, index, code, cfg) - case (ex @ ExprStmt(_, _: FunctionCall[V]), index) ⇒ Statement(m, cfg.bb(index), ex, index, code, cfg) - }.toSet.asJava + code.zipWithIndex + .collect { + case (mc: MethodCall[V], index) ⇒ Statement(m, cfg.bb(index), mc, index, code, cfg) + case (as @ Assignment(_, _, _: FunctionCall[V]), index) ⇒ + Statement(m, cfg.bb(index), as, index, code, cfg) + case (ex @ ExprStmt(_, _: FunctionCall[V]), index) ⇒ + Statement(m, cfg.bb(index), ex, index, code, cfg) + } + .toSet + .asJava } - override def getReturnSitesOfCallAt(callInstr: Statement): JCollection[Statement] = getSuccsOf(callInstr) + override def getReturnSitesOfCallAt(callInstr: Statement): JCollection[Statement] = + getSuccsOf(callInstr) override def isCallStmt(stmt: Statement): Boolean = { def isCallExpr(expr: Expr[V]) = expr.astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - VirtualFunctionCall.ASTID ⇒ true + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + true case _ ⇒ false } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index 0bde8e977f..ec4b4f85a4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -7,13 +7,11 @@ package ifds import java.io.File import java.io.PrintWriter - import scala.collection.{Set ⇒ SomeSet} import scala.collection.mutable - import com.typesafe.config.ConfigValueFactory -import javax.swing.JOptionPane +import javax.swing.JOptionPane import org.opalj.util.Milliseconds import org.opalj.util.PerformanceEvaluation.time import org.opalj.fpcf.EOptionP @@ -47,8 +45,8 @@ import org.opalj.br.DeclaredMethod import org.opalj.br.DefinedMethod import org.opalj.br.analyses.Project import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.br.fpcf.properties.cg.Callees -import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.properties.cg.Callees +import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.ai.domain.l0.PrimitiveTACAIDomain @@ -58,7 +56,8 @@ import org.opalj.tac.fpcf.properties.IFDSProperty import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.fpcf.properties.TheTACAI -import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.cg.{RTACallGraphKey, TypeProviderKey} +import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V /** @@ -78,7 +77,9 @@ trait AbstractIFDSNullFact extends AbstractIFDSFact * @author Dominik Helm * @author Mario Trageser */ -abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAnalysis with Subsumable[IFDSFact] { +abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] + extends FPCFAnalysis + with Subsumable[IFDSFact] { /** * Provides the concrete property key that must be unique for every distinct concrete analysis @@ -89,7 +90,9 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn /** * All declared methods in the project. */ - final protected implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + implicit final protected val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + + implicit final protected val typeProvider: TypeProvider = project.get(TypeProviderKey) /** * Counts, how many times the abstract methods were called. @@ -159,7 +162,11 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * that the facts in `in` held before `statement` and `successor` will be * executed next. */ - protected def normalFlow(statement: Statement, successor: Statement, in: Set[IFDSFact]): Set[IFDSFact] + protected def normalFlow( + statement: Statement, + successor: Statement, + in: Set[IFDSFact] + ): Set[IFDSFact] /** * Computes the data flow for a call to start edge. @@ -171,8 +178,12 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @return The facts, which hold after the execution of `statement` under the assumption that * the facts in `in` held before `statement` and `statement` calls `callee`. */ - protected def callFlow(call: Statement, callee: DeclaredMethod, in: Set[IFDSFact], - source: (DeclaredMethod, IFDSFact)): Set[IFDSFact] + protected def callFlow( + call: Statement, + callee: DeclaredMethod, + in: Set[IFDSFact], + source: (DeclaredMethod, IFDSFact) + ): Set[IFDSFact] /** * Computes the data flow for an exit to return edge. @@ -187,8 +198,13 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * under the assumption that `in` held before the execution of `exit` and that * `successor` will be executed next. */ - protected def returnFlow(call: Statement, callee: DeclaredMethod, exit: Statement, - successor: Statement, in: Set[IFDSFact]): Set[IFDSFact] + protected def returnFlow( + call: Statement, + callee: DeclaredMethod, + exit: Statement, + successor: Statement, + in: Set[IFDSFact] + ): Set[IFDSFact] /** * Computes the data flow for a call to return edge. @@ -200,8 +216,12 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @return The facts, which hold after the call independently of what happens in the callee * under the assumption that `in` held before `call`. */ - protected def callToReturnFlow(call: Statement, successor: Statement, in: Set[IFDSFact], - source: (DeclaredMethod, IFDSFact)): Set[IFDSFact] + protected def callToReturnFlow( + call: Statement, + successor: Statement, + in: Set[IFDSFact], + source: (DeclaredMethod, IFDSFact) + ): Set[IFDSFact] /** * When a callee outside of this analysis' context is called, this method computes the summary @@ -213,8 +233,12 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @param in The facts facts, which hold before the `call`. * @return The facts, which hold after the call, excluding the call to return flow. */ - protected def callOutsideOfAnalysisContext(call: Statement, callee: DeclaredMethod, - successor: Statement, in: Set[IFDSFact]): Set[IFDSFact] + protected def callOutsideOfAnalysisContext( + call: Statement, + callee: DeclaredMethod, + successor: Statement, + in: Set[IFDSFact] + ): Set[IFDSFact] /** * Determines the basic blocks, at which the analysis starts. @@ -313,8 +337,10 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @param in The facts, which hold before the `call`. * @return The facts, for which `callee` will be analyzed. */ - protected def callToStartFacts(call: Statement, callee: DeclaredMethod, - in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] + protected def callToStartFacts(call: Statement, callee: DeclaredMethod, in: Set[IFDSFact])( + implicit + state: State + ): Set[IFDSFact] /** * Collects the exit facts of a `callee` and adds them to the `summaryEdges`. @@ -329,9 +355,10 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn */ protected def addExitToReturnFacts( summaryEdges: Map[Statement, Set[IFDSFact]], - successors: Set[Statement], call: Statement, - callee: DeclaredMethod, - exitFacts: Map[Statement, Set[IFDSFact]] + successors: Set[Statement], + call: Statement, + callee: DeclaredMethod, + exitFacts: Map[Statement, Set[IFDSFact]] )(implicit state: State): Map[Statement, Set[IFDSFact]] /** @@ -346,10 +373,10 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn val (declaredMethod, sourceFact) = entity /* - * The analysis can only handle single defined methods. - * If a method is not single defined, this analysis assumes that it does not create any - * facts. - */ + * The analysis can only handle single defined methods. + * If a method is not single defined, this analysis assumes that it does not create any + * facts. + */ if (!declaredMethod.hasSingleDefinedMethod) return Result(entity, createPropertyValue(Map.empty)); @@ -357,15 +384,15 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn val declaringClass: ObjectType = method.classFile.thisType /* - * If this is not the method's declaration, but a non-overwritten method in a subtype, do - * not re-analyze the code. - */ + * If this is not the method's declaration, but a non-overwritten method in a subtype, do + * not re-analyze the code. + */ if (declaringClass ne declaredMethod.declaringClassType) return baseMethodResult(entity); /* - * Fetch the method's three address code. If it is not present, return an empty interim - * result. - */ + * Fetch the method's three address code. If it is not present, return an empty interim + * result. + */ val (code, cfg) = propertyStore(method, TACAI.key) match { case FinalP(TheTACAI(tac)) ⇒ (tac.stmts, tac.cfg) @@ -383,10 +410,11 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn // Start processing at the start of the cfg with the given source fact implicit val state: State = - new State(declaringClass, method, entity, code, cfg, Map(entity → Set.empty), Map()) - val queue = mutable.Queue.empty[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] + new State(declaringClass, method, entity, code, cfg, Map(entity -> Set.empty), Map()) + val queue = mutable.Queue + .empty[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] startBlocks(sourceFact, cfg).foreach { start ⇒ - state.incomingFacts += start → Set(sourceFact) + state.incomingFacts += start -> Set(sourceFact) queue.enqueue((start, Set(sourceFact), None, None, None)) } process(queue) @@ -417,19 +445,24 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @param eps The new property value. * @return The new (interim) result of this analysis. */ - protected def propertyUpdate(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + protected def propertyUpdate( + eps: SomeEPS + )(implicit state: State): ProperPropertyComputationResult = { (eps: @unchecked) match { case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) ⇒ reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) - case interimEUBP @ InterimEUBP(e: (DeclaredMethod, IFDSFact) @unchecked, - ub: IFDSProperty[IFDSFact]) ⇒ + case interimEUBP @ InterimEUBP( + e: (DeclaredMethod, IFDSFact) @unchecked, + ub: IFDSProperty[IFDSFact] + ) ⇒ if (ub.flows.values .forall(facts ⇒ facts.size == 1 && facts.forall(_ == nullFact))) { // Do not re-analyze the caller if we only get the null fact. // Update the pendingIfdsDependee entry to the new interim result. state.pendingIfdsDependees += - e → interimEUBP.asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + e -> interimEUBP + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) @@ -483,8 +516,13 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @return A map, mapping from each successor statement of the `call` to the facts, which hold * at their start. */ - protected def handleCall(basicBlock: BasicBlock, call: Statement, callees: SomeSet[Method], - in: Set[IFDSFact], calleeWithUpdateFact: Option[IFDSFact])( + protected def handleCall( + basicBlock: BasicBlock, + call: Statement, + callees: SomeSet[Method], + in: Set[IFDSFact], + calleeWithUpdateFact: Option[IFDSFact] + )( implicit state: State ): Map[Statement, Set[IFDSFact]] = { @@ -494,14 +532,14 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn var summaryEdges: Map[Statement, Set[IFDSFact]] = Map.empty /* - * If calleeWithUpdateFact is present, this means that the basic block already has been - * analyzed with the `inputFacts`. - */ + * If calleeWithUpdateFact is present, this means that the basic block already has been + * analyzed with the `inputFacts`. + */ if (calleeWithUpdateFact.isEmpty) for (successor ← successors) { numberOfCalls.callToReturnFlow += 1 sumOfInputfactsForCallbacks += in.size - summaryEdges += successor → + summaryEdges += successor -> propagateNullFact( inputFacts, callToReturnFlow(call, successor, inputFacts, state.source) @@ -515,7 +553,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn for { successor ← successors } summaryEdges += - successor → (summaryEdges(successor) ++ + successor -> (summaryEdges(successor) ++ callOutsideOfAnalysisContext(call, callee, successor, in)) } else { val callToStart = @@ -527,11 +565,11 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn // Collect exit facts for each input fact separately for (fact ← callToStart) { /* - * If this is a recursive call with the same input facts, we assume that the - * call only produces the facts that are already known. The call site is added to - * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts - * become known for the input fact. - */ + * If this is a recursive call with the same input facts, we assume that the + * call only produces the facts that are already known. The call site is added to + * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts + * become known for the input fact. + */ if ((calledMethod eq state.method) && fact == state.source._2) { val newDependee = if (state.pendingIfdsCallSites.contains(state.source)) @@ -556,18 +594,17 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn && state.pendingIfdsCallSites(e).nonEmpty) { val newDependee = state.pendingIfdsCallSites(e) - ((basicBlock, call.index)) - state.pendingIfdsCallSites = - state.pendingIfdsCallSites.updated(e, newDependee) + state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) } state.pendingIfdsDependees -= e ep.p.flows case ep: InterimEUBP[_, IFDSProperty[IFDSFact]] ⇒ /* - * Add the call site to `pendingIfdsCallSites` and - * `pendingIfdsDependees` and continue with the facts in the interim - * result for now. When the analysis for the callee finishes, the - * analysis for this call site will be triggered again. - */ + * Add the call site to `pendingIfdsCallSites` and + * `pendingIfdsDependees` and continue with the facts in the interim + * result for now. When the analysis for the callee finishes, the + * analysis for this call site will be triggered again. + */ addIfdsDependee(e, callFlows, basicBlock, call.index) ep.ub.flows case _ ⇒ @@ -575,27 +612,26 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn Map.empty } // Only process new facts that are not in `oldExitFacts` - allNewExitFacts = - mergeMaps( - allNewExitFacts, - filterNewInformation(exitFacts, oldExitFacts, project) - ) + allNewExitFacts = mergeMaps( + allNewExitFacts, + filterNewInformation(exitFacts, oldExitFacts, project) + ) /* - * If new exit facts were discovered for the callee-fact-pair, all call - * sites depending on this pair have to be re-evaluated. oldValue is - * undefined if the callee-fact pair has not been queried before or returned - * a FinalEP. - */ + * If new exit facts were discovered for the callee-fact-pair, all call + * sites depending on this pair have to be re-evaluated. oldValue is + * undefined if the callee-fact pair has not been queried before or returned + * a FinalEP. + */ if (oldValue.isDefined && oldExitFacts != exitFacts) { reAnalyzeCalls( state.pendingIfdsCallSites(e), - e._1.definedMethod, Some(e._2) + e._1.definedMethod, + Some(e._2) ) } } } - summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, - allNewExitFacts) + summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) } } summaryEdges @@ -631,11 +667,15 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @param caller The caller, performing the call. * @return All methods directly callable at the statement index. */ - protected def getCallees(basicBlock: BasicBlock, pc: Int, - caller: DeclaredMethod): SomeSet[Method] = { + protected def getCallees( + basicBlock: BasicBlock, + pc: Int, + caller: DeclaredMethod + ): SomeSet[Method] = { val ep = propertyStore(caller, Callees.key) ep match { - case FinalEP(_, p) ⇒ definedMethods(p.directCallees(pc)) + case FinalEP(_, p) ⇒ + definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method)) case _ ⇒ throw new IllegalStateException( "call graph mut be computed before the analysis starts" @@ -701,13 +741,20 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * calleeWithUpdateFact If the basic block is analyzed because there is new information * for a callee with a specific input fact, this is the input fact. */ - private def process(worklist: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])])(implicit state: State): Unit = { + private def process( + worklist: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] + )(implicit state: State): Unit = { while (worklist.nonEmpty) { val (basicBlock, in, calleeWithUpdateIndex, calleeWithUpdate, calleeWithUpdateFact) = worklist.dequeue() val oldOut = state.outgoingFacts.getOrElse(basicBlock, Map.empty) - val nextOut = analyzeBasicBlock(basicBlock, in, calleeWithUpdateIndex, calleeWithUpdate, - calleeWithUpdateFact) + val nextOut = analyzeBasicBlock( + basicBlock, + in, + calleeWithUpdateIndex, + calleeWithUpdate, + calleeWithUpdateFact + ) val allOut = mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) @@ -718,15 +765,20 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn if (nextOutSuccessors.isDefined && nextOutSuccessors.get.nonEmpty) { val oldOutSuccessors = oldOut.get(successor) if (oldOutSuccessors.isEmpty || containsNewInformation( - nextOutSuccessors.get, oldOutSuccessors.get, project + nextOutSuccessors.get, + oldOutSuccessors.get, + project )) { val source = state.source foundNewInformationForLastNode( - nextOutSuccessors.get, oldOutSuccessors, state + nextOutSuccessors.get, + oldOutSuccessors, + state ) reAnalyzeCalls( state.pendingIfdsCallSites(source), - source._1.definedMethod, Some(source._2) + source._1.definedMethod, + Some(source._2) ) } } @@ -741,9 +793,9 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn state.incomingFacts = state.incomingFacts.updated(successorBlock, subsume(mergedIn, project)) /* - * Only process the successor with new facts. - * It is analyzed at least one time because of the null fact. - */ + * Only process the successor with new facts. + * It is analyzed at least one time because of the null fact. + */ if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None, None)) } } @@ -765,26 +817,29 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @return A map, mapping each successor node to its input facts. Instead of catch nodes, this * map contains their handler nodes. */ - private def analyzeBasicBlock(basicBlock: BasicBlock, in: Set[IFDSFact], - calleeWithUpdateIndex: Option[Int], - calleeWithUpdate: Option[Method], - calleeWithUpdateFact: Option[IFDSFact])( + private def analyzeBasicBlock( + basicBlock: BasicBlock, + in: Set[IFDSFact], + calleeWithUpdateIndex: Option[Int], + calleeWithUpdate: Option[Method], + calleeWithUpdateFact: Option[IFDSFact] + )( implicit state: State ): Map[CFGNode, Set[IFDSFact]] = { /* - * Collects information about a statement. - * - * @param index The statement's index. - * @return A tuple of the following elements: - * statement: The statement at `index`. - * calees: The methods possibly called at this statement, if it contains a call. - * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will - * be returned. - * calleeFact: If `index` equals `calleeWithUpdateIndex`, only - * `calleeWithUpdateFact` will be returned, None otherwise. - */ + * Collects information about a statement. + * + * @param index The statement's index. + * @return A tuple of the following elements: + * statement: The statement at `index`. + * calees: The methods possibly called at this statement, if it contains a call. + * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will + * be returned. + * calleeFact: If `index` equals `calleeWithUpdateIndex`, only + * `calleeWithUpdateFact` will be returned, None otherwise. + */ def collectInformation(index: Int): (Statement, Option[SomeSet[Method]], Option[IFDSFact]) = { val stmt = state.code(index) val statement = Statement(state.method, basicBlock, stmt, index, state.code, state.cfg) @@ -805,16 +860,15 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn while (index != last) { val (statement, calleesO, calleeFact) = collectInformation(index) val next = nextIndex(index) - flows = - if (calleesO.isEmpty) { - val successor = Statement(state.method, basicBlock, state.code(next), next, - state.code, state.cfg) - numberOfCalls.normalFlow += 1 - sumOfInputfactsForCallbacks += in.size - normalFlow(statement, successor, flows) - } else - // Inside a basic block, we only have one successor --> Take the head - handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head + flows = if (calleesO.isEmpty) { + val successor = + Statement(state.method, basicBlock, state.code(next), next, state.code, state.cfg) + numberOfCalls.normalFlow += 1 + sumOfInputfactsForCallbacks += in.size + normalFlow(statement, successor, flows) + } else + // Inside a basic block, we only have one successor --> Take the head + handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head index = next } @@ -826,14 +880,15 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn for (node ← nextNodes(basicBlock)) { numberOfCalls.normalFlow += 1 sumOfInputfactsForCallbacks += in.size - result += node → normalFlow(statement, firstStatement(node), flows) + result += node -> normalFlow(statement, firstStatement(node), flows) } result - } else handleCall(basicBlock, statement, calleesO.get, flows, callFact) - .map(entry ⇒ entry._1.node → entry._2) + } else + handleCall(basicBlock, statement, calleesO.get, flows, callFact) + .map(entry ⇒ entry._1.node -> entry._2) // Propagate the null fact. - result = result.map(result ⇒ result._1 → propagateNullFact(in, result._2)) + result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) result } @@ -855,8 +910,11 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @param callee The callee, which will be considered at the `callSites`. * @param fact If defined, the `callee` will only be analyzed for this fact. */ - private def reAnalyzeCalls(callSites: Set[(BasicBlock, Int)], callee: Method, - fact: Option[IFDSFact])(implicit state: State): Unit = { + private def reAnalyzeCalls( + callSites: Set[(BasicBlock, Int)], + callee: Method, + fact: Option[IFDSFact] + )(implicit state: State): Unit = { val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty for ((block, index) ← callSites) queue.enqueue((block, state.incomingFacts(block), Some(index), Some(callee), fact)) @@ -880,12 +938,13 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn statement.astID match { case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ Some(getCallees(basicBlock, pc, state.source._1)) - case Assignment.ASTID | ExprStmt.ASTID ⇒ getExpression(statement).astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - VirtualFunctionCall.ASTID ⇒ - Some(getCallees(basicBlock, pc, state.source._1)) - case _ ⇒ None - } + case Assignment.ASTID | ExprStmt.ASTID ⇒ + getExpression(statement).astID match { + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | + VirtualFunctionCall.ASTID ⇒ + Some(getCallees(basicBlock, pc, state.source._1)) + case _ ⇒ None + } case _ ⇒ None } } @@ -898,10 +957,17 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn */ private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): SomeSet[Method] = { val result = scala.collection.mutable.Set.empty[Method] - declaredMethods.filter(declaredMethod ⇒ declaredMethod.hasSingleDefinedMethod || - declaredMethod.hasMultipleDefinedMethods). - foreach(declaredMethod ⇒ declaredMethod - .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod))) + declaredMethods + .filter( + declaredMethod ⇒ + declaredMethod.hasSingleDefinedMethod || + declaredMethod.hasMultipleDefinedMethods + ) + .foreach( + declaredMethod ⇒ + declaredMethod + .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) + ) result } @@ -948,7 +1014,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn entity, callSites.getOrElse(entity, Set.empty) + ((callBB, callIndex)) ) - state.pendingIfdsDependees += entity → calleeProperty + state.pendingIfdsDependees += entity -> calleeProperty } /** @@ -958,7 +1024,9 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFAn * @param source A pair consisting of the declared method of the subtype and an input fact. * @return The result of the analysis of the defined method of the supertype. */ - private def baseMethodResult(source: (DeclaredMethod, IFDSFact)): ProperPropertyComputationResult = { + private def baseMethodResult( + source: (DeclaredMethod, IFDSFact) + ): ProperPropertyComputationResult = { def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { case FinalP(p) ⇒ Result(source, p) @@ -1057,8 +1125,11 @@ abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalys * Registers the analysis as a lazy computation, that is, the method * will call `ProperytStore.scheduleLazyComputation`. */ - final override def register(p: SomeProject, ps: PropertyStore, - analysis: AbstractIFDSAnalysis[IFDSFact]): FPCFAnalysis = { + final override def register( + p: SomeProject, + ps: PropertyStore, + analysis: AbstractIFDSAnalysis[IFDSFact] + ): FPCFAnalysis = { ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) analysis } @@ -1068,8 +1139,11 @@ abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalys for (e ← ifdsAnalysis.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } } - override def afterPhaseCompletion(p: SomeProject, ps: PropertyStore, - analysis: FPCFAnalysis): Unit = {} + override def afterPhaseCompletion( + p: SomeProject, + ps: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} } @@ -1079,8 +1153,13 @@ abstract class AbsractIFDSAnalysisRunner { protected def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit - protected def run(debug: Boolean, useL2: Boolean, delay: Boolean, evalSchedulingStrategies: Boolean, - evaluationFile: Option[File]): Unit = { + protected def run( + debug: Boolean, + useL2: Boolean, + delay: Boolean, + evalSchedulingStrategies: Boolean, + evaluationFile: Option[File] + ): Unit = { if (debug) { PropertyStore.updateDebug(true) @@ -1089,8 +1168,9 @@ abstract class AbsractIFDSAnalysisRunner { def evalProject(p: SomeProject): (Milliseconds, NumberOfCalls, Option[Object], Long) = { if (useL2) { p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) - case Some(requirements) ⇒ requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] + case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) ⇒ + requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] } } else { p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { @@ -1118,11 +1198,18 @@ abstract class AbsractIFDSAnalysisRunner { printAnalysisResults(analysis, ps) println(s"The analysis took $analysisTime.") println( - ps.statistics.iterator.map(_.toString()).toList + ps.statistics.iterator + .map(_.toString()) + .toList .sorted .mkString("PropertyStore Statistics:\n\t", "\n\t", "\n") ) - (analysisTime, analysis.numberOfCalls, additionalEvaluationResult(analysis), analysis.sumOfInputfactsForCallbacks) + ( + analysisTime, + analysis.numberOfCalls, + additionalEvaluationResult(analysis), + analysis.sumOfInputfactsForCallbacks + ) } val p = Project(bytecode.RTJar) @@ -1151,7 +1238,8 @@ abstract class AbsractIFDSAnalysisRunner { PKESequentialPropertyStore.Strategies.foreach { strategy ⇒ val strategyResults = results.filter(_._2 == strategy) val averageTime = strategyResults.map(_._3.timeSpan).sum / strategyResults.size - val (normalFlow, callToStart, exitToReturn, callToReturn) = computeAverageNumberCalls(strategyResults.map(_._4)) + val (normalFlow, callToStart, exitToReturn, callToReturn) = + computeAverageNumberCalls(strategyResults.map(_._4)) pw.println(s"Strategy $strategy:") pw.println(s"Average time: ${averageTime}ms") pw.println(s"Average calls of normalFlow: $normalFlow") @@ -1179,7 +1267,9 @@ abstract class AbsractIFDSAnalysisRunner { additionalEvaluationResults :+= additionalEvaluationResult.get } if (evaluationFile.nonEmpty) { - val (normalFlow, callFlow, returnFlow, callToReturnFlow) = computeAverageNumberCalls(numberOfCalls) + val (normalFlow, callFlow, returnFlow, callToReturnFlow) = computeAverageNumberCalls( + numberOfCalls + ) val time = times.map(_.timeSpan).sum / times.size val sumOfInputFactsForCallbacks = sumsOfInputFactsForCallbacks.sum / sumsOfInputFactsForCallbacks.size val pw = new PrintWriter(evaluationFile.get) @@ -1203,7 +1293,10 @@ abstract class AbsractIFDSAnalysisRunner { additionalEvaluationResults: Seq[Object] ): Unit = {} - protected def canBeCalledFromOutside(method: DeclaredMethod, propertyStore: PropertyStore): Boolean = + protected def canBeCalledFromOutside( + method: DeclaredMethod, + propertyStore: PropertyStore + ): Boolean = propertyStore(method, Callers.key) match { // This is the case, if the method may be called from outside the library. case FinalEP(_, p: Callers) ⇒ p.hasCallersWithUnknownContext @@ -1227,4 +1320,4 @@ object AbstractIFDSAnalysisRunner { var NUM_EXECUTIONS = 10 var NUM_EXECUTIONS_EVAL_SCHEDULING_STRATEGIES = 2 var MEASURE_MEMORY = false -} \ No newline at end of file +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala index e1d3a38056..d80c40e8a7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala @@ -16,7 +16,7 @@ import org.opalj.br.cfg.CFGNode import org.opalj.br.DeclaredMethod import org.opalj.br.cfg.CatchNode import org.opalj.br.cfg.CFG -import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V import org.opalj.tac.fpcf.properties.IFDSProperty import org.opalj.tac.Stmt @@ -54,8 +54,7 @@ trait UnbalancedReturnFact[FactType] extends AbstractIFDSFact { * concrete analysis. * @author Mario Trageser */ -abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]] - extends AbstractIFDSAnalysis[IFDSFact] { +abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]] extends AbstractIFDSAnalysis[IFDSFact] { /** * Called, when the entry point of the analyzed method is reached. @@ -68,8 +67,12 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * @return Unbalanced return facts, that hold after `call` under the assumption, that `facts` * held at the entry point of the analyzed method. */ - protected def unbalancedReturnFlow(facts: Set[IFDSFact], call: Statement, caller: DeclaredMethod, - source: (DeclaredMethod, IFDSFact)): Set[UnbalancedIFDSFact] + protected def unbalancedReturnFlow( + facts: Set[IFDSFact], + call: Statement, + caller: DeclaredMethod, + source: (DeclaredMethod, IFDSFact) + ): Set[UnbalancedIFDSFact] /** * If this method is analyzed for an unbalanced return fact, the single star block is the block, @@ -82,8 +85,10 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS ): Set[BasicBlock] = sourceFact match { case fact: UnbalancedReturnFact[IFDSFact] ⇒ Set(cfg.bb(fact.index)) - case _ ⇒ Set(cfg.normalReturnNode, cfg.abnormalReturnNode).flatMap(_.predecessors) - .foldLeft(Set.empty[BasicBlock])((c, n) ⇒ c + n.asBasicBlock) + case _ ⇒ + Set(cfg.normalReturnNode, cfg.abnormalReturnNode) + .flatMap(_.predecessors) + .foldLeft(Set.empty[BasicBlock])((c, n) ⇒ c + n.asBasicBlock) } /** @@ -92,10 +97,10 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS override protected def collectResult(implicit state: State): Map[Statement, Set[IFDSFact]] = { val startBlock = state.cfg.startBlock val startPC = startBlock.startPC - val statement = Statement(state.method, startBlock, state.code(startPC), - startPC, state.code, state.cfg) + val statement = + Statement(state.method, startBlock, state.code(startPC), startPC, state.code, state.cfg) val exitFacts = state.outgoingFacts.get(startBlock).flatMap(_.get(SyntheticStartNode)) - if (exitFacts.isDefined) Map(statement → exitFacts.get) + if (exitFacts.isDefined) Map(statement -> exitFacts.get) else Map.empty } @@ -105,25 +110,30 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * If the update is not for an unbalanced return fact, the update will be handled by * AbstractIFDSAnalysis. */ - override protected def propertyUpdate(eps: SomeEPS)(implicit state: State): ProperPropertyComputationResult = { + override protected def propertyUpdate( + eps: SomeEPS + )(implicit state: State): ProperPropertyComputationResult = { (eps: @unchecked) match { /* - * If the analysis for the unbalanced return finished, remove the entity from the - * dependencies. - */ + * If the analysis for the unbalanced return finished, remove the entity from the + * dependencies. + */ case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) ⇒ if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]]) { state.pendingIfdsDependees -= e createResult } else super.propertyUpdate(eps) /* - * If there is an interim result for an unbalanced return fact, ignore it and just create the result. - */ - case interimEUBP @ InterimEUBP(e: (DeclaredMethod, IFDSFact) @unchecked, - _: IFDSProperty[IFDSFact]) ⇒ + * If there is an interim result for an unbalanced return fact, ignore it and just create the result. + */ + case interimEUBP @ InterimEUBP( + e: (DeclaredMethod, IFDSFact) @unchecked, + _: IFDSProperty[IFDSFact] + ) ⇒ if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]]) { state.pendingIfdsDependees += - e → interimEUBP.asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + e -> interimEUBP + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] createResult() } else super.propertyUpdate(eps) @@ -180,29 +190,29 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS if (shouldPerformUnbalancedReturn(state.source)) { // Get all callers of this method propertyStore(state.source._1, Callers.key) match { - case FinalEP(_, p: Callers) ⇒ p.callers.foreach { callersProperty ⇒ - val (caller, callPc, directCall) = callersProperty - // We do not handle indirect calls. - if (directCall) { - val definedCaller = caller.definedMethod - // Get the caller's tac to create the unbalanced return facts - val callerTac = propertyStore(definedCaller, TACAI.key) - callerTac match { - case FinalP(TheTACAI(tac)) ⇒ - addDependencyForUnbalancedReturn(caller, tac.pcToIndex(callPc), - newIn, tac)(state) - case _ ⇒ - val pendingTacCallSites = state.pendingTacCallSites - state.pendingTacDependees += definedCaller → callerTac - state.pendingTacCallSites = pendingTacCallSites.updated( - caller, - pendingTacCallSites.getOrElse(caller, Set.empty) + - state.cfg.startBlock - ) + case FinalEP(_, p: Callers) ⇒ + p.callers(state.source._1).foreach { callersProperty ⇒ + val (caller, callPc, directCall) = callersProperty + // We do not handle indirect calls. + if (directCall) { + val definedCaller = caller.definedMethod + // Get the caller's tac to create the unbalanced return facts + val callerTac = propertyStore(definedCaller, TACAI.key) + callerTac match { + case FinalP(TheTACAI(tac)) ⇒ + addDependencyForUnbalancedReturn(caller, tac.pcToIndex(callPc), newIn, tac)(state) + case _ ⇒ + val pendingTacCallSites = state.pendingTacCallSites + state.pendingTacDependees += definedCaller -> callerTac + state.pendingTacCallSites = pendingTacCallSites.updated( + caller, + pendingTacCallSites.getOrElse(caller, Set.empty) + + state.cfg.startBlock + ) + } } - } - } + } case _ ⇒ throw new IllegalStateException( "call graph mut be computed before the analysis starts" @@ -259,14 +269,17 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val index = node.asBasicBlock.endPC Statement(state.method, node, state.code(index), index, state.code, state.cfg) } else if (node.isCatchNode) firstStatement(node.successors.head) - else if (node == SyntheticStartNode) Statement(state.method, node, null, 0, state.code, state.cfg) + else if (node == SyntheticStartNode) + Statement(state.method, node, null, 0, state.code, state.cfg) else throw new IllegalArgumentException(s"Unknown node type: $node") } /** * The successor statements against the control flow direction. */ - override protected def nextStatements(statement: Statement)(implicit state: State): Set[Statement] = { + override protected def nextStatements( + statement: Statement + )(implicit state: State): Set[Statement] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == 0) { @@ -275,8 +288,16 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS basicBlock.predecessors.map(firstStatement(_)) else { val nextIndex = index - 1 - Set(Statement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, - statement.code, statement.cfg)) + Set( + Statement( + statement.method, + basicBlock, + statement.code(nextIndex), + nextIndex, + statement.code, + statement.cfg + ) + ) } } @@ -284,8 +305,11 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * Determines the callees' exit statements and all successor statements in the control flow * direction, which my be executed after them. Calls returnFlow on those pairs. */ - override protected def callToStartFacts(call: Statement, callee: DeclaredMethod, - in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { + override protected def callToStartFacts( + call: Statement, + callee: DeclaredMethod, + in: Set[IFDSFact] + )(implicit state: State): Set[IFDSFact] = { val definedCallee = callee.definedMethod val ep = propertyStore(definedCallee, TACAI.key) ep match { @@ -298,8 +322,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val exitPc = bb.asBasicBlock.endPC val calleeStmts = tac.stmts val exitStmt = calleeStmts(exitPc) - val exitStatement = Statement(definedCallee, cfg.bb(exitPc), exitStmt, - exitPc, calleeStmts, cfg) + val exitStatement = + Statement(definedCallee, cfg.bb(exitPc), exitStmt, exitPc, calleeStmts, cfg) for { successor ← successors if !AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW || @@ -317,7 +341,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS case _ ⇒ val pendingTacCallSites = state.pendingTacCallSites val index = call.index - state.pendingTacDependees += definedCallee → ep + state.pendingTacDependees += definedCallee -> ep state.pendingTacCallSites = pendingTacCallSites.updated( callee, pendingTacCallSites.getOrElse(callee, Set.empty) + call.cfg.bb(index) @@ -332,9 +356,10 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS */ override protected def addExitToReturnFacts( summaryEdges: Map[Statement, Set[IFDSFact]], - successors: Set[Statement], call: Statement, - callee: DeclaredMethod, - exitFacts: Map[Statement, Set[IFDSFact]] + successors: Set[Statement], + call: Statement, + callee: DeclaredMethod, + exitFacts: Map[Statement, Set[IFDSFact]] )(implicit state: State): Map[Statement, Set[IFDSFact]] = { var result = summaryEdges if (exitFacts.nonEmpty) { @@ -383,15 +408,27 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * * @return A map, mapping from a predecessor statement to the corresponding node. */ - private def predecessorStatementsWithNode(statement: Statement)(implicit state: State): Map[Statement, CFGNode] = { + private def predecessorStatementsWithNode( + statement: Statement + )(implicit state: State): Map[Statement, CFGNode] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == basicBlock.endPC) - basicBlock.successors.iterator.map(successorNode ⇒ lastStatement(successorNode) → successorNode).toMap + basicBlock.successors.iterator + .map(successorNode ⇒ lastStatement(successorNode) -> successorNode) + .toMap else { val nextIndex = index + 1 - Map(Statement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, - statement.code, statement.cfg) → basicBlock) + Map( + Statement( + statement.method, + basicBlock, + statement.code(nextIndex), + nextIndex, + statement.code, + statement.cfg + ) -> basicBlock + ) } } @@ -422,27 +459,37 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * @param in The facts, which hold at the entry point of the analyzed method. * @param tac The `caller`'s tac. */ - private def addDependencyForUnbalancedReturn(caller: DeclaredMethod, callIndex: Int, - in: Set[IFDSFact], - tac: TACode[TACMethodParameter, DUVar[ValueInformation]])(implicit state: State): Unit = { + private def addDependencyForUnbalancedReturn( + caller: DeclaredMethod, + callIndex: Int, + in: Set[IFDSFact], + tac: TACode[TACMethodParameter, DUVar[ValueInformation]] + )(implicit state: State): Unit = { val callerStmts = tac.stmts val callerCfg = tac.cfg - val call = Statement(caller.definedMethod, callerCfg.bb(callIndex), - callerStmts(callIndex), callIndex, callerStmts, callerCfg) + val call = Statement( + caller.definedMethod, + callerCfg.bb(callIndex), + callerStmts(callIndex), + callIndex, + callerStmts, + callerCfg + ) unbalancedReturnFlow(in, call, caller, state.source).foreach { in ⇒ val callerEntity = (caller, in) /* - * Add the caller with the unbalanced return fact as a dependency to - * start its analysis. - */ + * Add the caller with the unbalanced return fact as a dependency to + * start its analysis. + */ val callerAnalysisResult = propertyStore(callerEntity, propertyKey.key) callerAnalysisResult match { case FinalEP(_, _) ⇒ // Caller was already analyzed with the fact case _ ⇒ val pendingIfdsCallSites = state.pendingIfdsCallSites - state.pendingIfdsDependees += callerEntity → - callerAnalysisResult.asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] - state.pendingIfdsCallSites += callerEntity → + state.pendingIfdsDependees += callerEntity -> + callerAnalysisResult + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + state.pendingIfdsCallSites += callerEntity -> (pendingIfdsCallSites.getOrElse(callerEntity, Set.empty) + ((state.cfg.startBlock, 0))) } @@ -473,4 +520,4 @@ object SyntheticStartNode extends CFGNode { override def toHRR: Option[String] = Some("Synthetic Start Node") override def nodeId: Int = -1 -} \ No newline at end of file +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala index 425874e995..0d630dc59f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -18,7 +18,7 @@ import org.opalj.br.ArrayType import org.opalj.br.ClassFile import org.opalj.br.FieldType import org.opalj.br.Method -import org.opalj.br.fpcf.properties.cg.Callers +import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.IFDSProperty import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation import org.opalj.tac.ArrayLoad @@ -98,15 +98,19 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) * The analysis starts with all public methods in java.lang or org.opalj. */ override def entryPoints: Seq[(DeclaredMethod, VTAFact)] = { - p.allProjectClassFiles.filter(classInsideAnalysisContext) + p.allProjectClassFiles + .filter(classInsideAnalysisContext) .flatMap(classFile ⇒ classFile.methods) - .filter(isEntryPoint).map(method ⇒ declaredMethods(method)) + .filter(isEntryPoint) + .map(method ⇒ declaredMethods(method)) .flatMap(entryPointsForMethod) } override protected def nullFact: VTAFact = VTANullFact - override protected def createPropertyValue(result: Map[Statement, Set[VTAFact]]): IFDSProperty[VTAFact] = + override protected def createPropertyValue( + result: Map[Statement, Set[VTAFact]] + ): IFDSProperty[VTAFact] = new VTAResult(result) /** @@ -116,26 +120,27 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) * created for the assignment's target with the source's type. * If there is a field read, a new VariableType will be created with the field's declared type. */ - override protected def normalFlow(statement: Statement, successor: Statement, - in: Set[VTAFact]): Set[VTAFact] = { + override protected def normalFlow( + statement: Statement, + successor: Statement, + in: Set[VTAFact] + ): Set[VTAFact] = { val stmt = statement.stmt stmt.astID match { case Assignment.ASTID ⇒ // Add facts for the assigned variable. - in ++ newFacts(statement.method, statement.stmt.asAssignment.expr, - statement.index, in) + in ++ newFacts(statement.method, statement.stmt.asAssignment.expr, statement.index, in) case ArrayStore.ASTID ⇒ /* - * Add facts for the array store, like it was a variable assignment. - * By doing so, we only want to get the variable's type. - * Then, we change the definedBy-index to the one of the array and wrap the variable's - * type with an array type. - * Note, that an array type may have at most 255 dimensions. - */ + * Add facts for the array store, like it was a variable assignment. + * By doing so, we only want to get the variable's type. + * Then, we change the definedBy-index to the one of the array and wrap the variable's + * type with an array type. + * Note, that an array type may have at most 255 dimensions. + */ val flow = scala.collection.mutable.Set.empty[VTAFact] flow ++= in - newFacts(statement.method, stmt.asArrayStore.value, statement.index, - in).foreach { + newFacts(statement.method, stmt.asArrayStore.value, statement.index, in).foreach { case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) ⇒ stmt.asArrayStore.arrayRef.asVar.definedBy .foreach(flow += VariableType(_, ArrayType(t), upperBound)) @@ -151,8 +156,12 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) * For each variable, which can be passed as an argument to the call, a new VariableType is * created for the callee context. */ - override protected def callFlow(call: Statement, callee: DeclaredMethod, in: Set[VTAFact], - source: (DeclaredMethod, VTAFact)): Set[VTAFact] = { + override protected def callFlow( + call: Statement, + callee: DeclaredMethod, + in: Set[VTAFact], + source: (DeclaredMethod, VTAFact) + ): Set[VTAFact] = { val callObject = asCall(call.stmt) val allParams = callObject.allParams // Iterate over all input facts and over all parameters of the call. @@ -161,14 +170,16 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) case VariableType(definedBy, t, upperBound) ⇒ allParams.iterator.zipWithIndex.foreach { /* - * We are only interested in a pair of a variable type and a parameter, if the - * variable and the parameter can refer to the same object. - */ + * We are only interested in a pair of a variable type and a parameter, if the + * variable and the parameter can refer to the same object. + */ case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ // If this is the case, create a new fact for the method's formal parameter. flow += VariableType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(parameterIndex, callee.definedMethod.isStatic), - t, upperBound + AbstractIFDSAnalysis + .switchParamAndVariableIndex(parameterIndex, callee.definedMethod.isStatic), + t, + upperBound ) case _ ⇒ // Nothing to do } @@ -181,12 +192,16 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) * If the call is an instance call, new CalleeTypes will be created for the call, one for each * VariableType, which could be the call's target. */ - override protected def callToReturnFlow(call: Statement, successor: Statement, - in: Set[VTAFact], - source: (DeclaredMethod, VTAFact)): Set[VTAFact] = { + override protected def callToReturnFlow( + call: Statement, + successor: Statement, + in: Set[VTAFact], + source: (DeclaredMethod, VTAFact) + ): Set[VTAFact] = { // Check, to which variables the callee may refer val calleeDefinitionSites = asCall(call.stmt).receiverOption - .map(callee ⇒ callee.asVar.definedBy).getOrElse(EmptyIntTrieSet) + .map(callee ⇒ callee.asVar.definedBy) + .getOrElse(EmptyIntTrieSet) val calleeTypeFacts = in.collect { // If we know the variable's type, we also know on which type the call is performed. case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) ⇒ @@ -200,8 +215,13 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) * If the call returns a value which is assigned to a variable, a new VariableType will be * created in the caller context with the returned variable's type. */ - override protected def returnFlow(call: Statement, callee: DeclaredMethod, exit: Statement, - successor: Statement, in: Set[VTAFact]): Set[VTAFact] = + override protected def returnFlow( + call: Statement, + callee: DeclaredMethod, + exit: Statement, + successor: Statement, + in: Set[VTAFact] + ): Set[VTAFact] = // We only create a new fact, if the call returns a value, which is assigned to a variable. if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { val returnValue = exit.stmt.asReturnValue.expr.asVar @@ -216,9 +236,12 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) * If a method outside of the analysis context is called, we assume that it returns every * possible runtime type matching the compile time type. */ - override protected def callOutsideOfAnalysisContext(statement: Statement, callee: DeclaredMethod, - successor: Statement, - in: Set[VTAFact]): Set[VTAFact] = { + override protected def callOutsideOfAnalysisContext( + statement: Statement, + callee: DeclaredMethod, + successor: Statement, + in: Set[VTAFact] + ): Set[VTAFact] = { val returnType = callee.descriptor.returnType if (statement.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { Set(VariableType(statement.index, returnType.asReferenceType, upperBound = true)) @@ -273,18 +296,19 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) (method.descriptor.parameterTypes.zipWithIndex.collect { case (t, index) if t.isReferenceType ⇒ /* - * Create a fact for the parameter, which says, that the parameter may have any - * subtype of its compile time type. - */ + * Create a fact for the parameter, which says, that the parameter may have any + * subtype of its compile time type. + */ VariableType( AbstractIFDSAnalysis.switchParamAndVariableIndex(index, method.definedMethod.isStatic), - t.asReferenceType, upperBound = true + t.asReferenceType, + upperBound = true ) /* - * In IFDS problems, we must always also analyze the null fact, because it creates the facts, - * which hold independently of other source facts. - * Map the input facts, in which we are interested, to a pair of the method and the fact. - */ + * In IFDS problems, we must always also analyze the null fact, because it creates the facts, + * which hold independently of other source facts. + * Map the input facts, in which we are interested, to a pair of the method and the fact. + */ } :+ VTANullFact).map(fact ⇒ (method, fact)) } @@ -297,31 +321,37 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) * @param in The facts, which hold before the statement. * @return The new facts created by the statement. */ - private def newFacts(method: Method, expression: Expr[DUVar[ValueInformation]], - statementIndex: Int, - in: Set[VTAFact]): Iterator[VariableType] = expression.astID match { - case New.ASTID ⇒ in.iterator.collect { - // When a constructor is called, we always know the exact type. - case VTANullFact ⇒ - VariableType(statementIndex, expression.asNew.tpe, upperBound = false) - } - case Var.ASTID ⇒ in.iterator.collect { - // When we know the source type, we also know the type of the assigned variable. - case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ - VariableType(statementIndex, t, upperBound) - } - case ArrayLoad.ASTID ⇒ in.iterator.collect { - // When we know the array's type, we also know the type of the loaded element. - case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && - expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ - VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound) - } + private def newFacts( + method: Method, + expression: Expr[DUVar[ValueInformation]], + statementIndex: Int, + in: Set[VTAFact] + ): Iterator[VariableType] = expression.astID match { + case New.ASTID ⇒ + in.iterator.collect { + // When a constructor is called, we always know the exact type. + case VTANullFact ⇒ + VariableType(statementIndex, expression.asNew.tpe, upperBound = false) + } + case Var.ASTID ⇒ + in.iterator.collect { + // When we know the source type, we also know the type of the assigned variable. + case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ + VariableType(statementIndex, t, upperBound) + } + case ArrayLoad.ASTID ⇒ + in.iterator.collect { + // When we know the array's type, we also know the type of the loaded element. + case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && + expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ + VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound) + } case GetField.ASTID | GetStatic.ASTID ⇒ val t = expression.asFieldRead.declaredFieldType /* - * We do not track field types. So we must assume, that it contains any subtype of its - * compile time type. - */ + * We do not track field types. So we must assume, that it contains any subtype of its + * compile time type. + */ if (t.isReferenceType) Iterator(VariableType(statementIndex, t.asReferenceType, upperBound = true)) else Iterator.empty @@ -337,7 +367,10 @@ class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) * * @return True, if `t` is an array type of an object type. */ - @tailrec private def isArrayOfObjectType(t: FieldType, includeObjectType: Boolean = false): Boolean = { + @tailrec private def isArrayOfObjectType( + t: FieldType, + includeObjectType: Boolean = false + ): Boolean = { if (t.isArrayType) isArrayOfObjectType(t.asArrayType.elementType, includeObjectType = true) else if (t.isObjectType && includeObjectType) true else false @@ -382,7 +415,9 @@ class IFDSBasedVariableTypeAnalysisRunner extends AbsractIFDSAnalysisRunner { override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = {} - override protected def additionalEvaluationResult(analysis: AbstractIFDSAnalysis[_]): Option[Object] = + override protected def additionalEvaluationResult( + analysis: AbstractIFDSAnalysis[_] + ): Option[Object] = analysis match { case subsuming: Subsuming[_] ⇒ Some(subsuming.numberOfSubsumptions) case _ ⇒ None diff --git a/scalastyle-config.xml b/scalastyle-config.xml index 604f24a237..5a2ed0a80d 100644 --- a/scalastyle-config.xml +++ b/scalastyle-config.xml @@ -35,7 +35,7 @@ - + From a46430976759f357da51e68bcf9fea5b6a0ba86e Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 23 Feb 2022 13:35:44 +0100 Subject: [PATCH 15/67] Add multilingual taint test --- .../llvm/multilingual/taint/NativeTest.class | Bin 995 -> 1621 bytes .../llvm/multilingual/taint/NativeTest.java | 79 +++++++++++++----- ...MultilingualForwardTaintAnalysisTest.scala | 48 +++++++++++ .../MultilingualForwardTaintAnalysis.scala | 79 ++++++++++++++++++ build.sbt | 21 ++--- 5 files changed, 197 insertions(+), 30 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.class index 686b7acb65cb1c2d4f28a5fd3d547cea1232793d..1ddeb56ba5d14d1e3ea58b5b5394dea6b94d25fd 100644 GIT binary patch literal 1621 zcmaJ>Yg5xu5It#uw&7ArkydGuhoB`OP+p2u@QGF{pdi)p0}~)P(UNLgbnrL%ML$$V zXZ!*FD95vDB*38U+-$OU&)MB`AHRNo{|O+67diq6hR~$Yj1~>CH|7U(ykt7B$5*!A zSlcxXEjinCaSOO6HT5a< z`>yK|T*6=oLkgF1<%9%l2i2Oj$Ii?96r!u(ies0@S1YztTdP$pb8kM3VGT_?WyjJn zg3%DJDvV)VL+2UM8ba>`TXIO3Y4EvXlgxHr&kt`%eOZM`OtIu@dB3u4xyt8dVn$&W z*O;zqJ8x++`37K4;Rfb4gsUc(%Kk)%%(I?b#tpErR|wE`%h$Eoh1Of$ZeOrEvK)!3M|=zYxlR^m}tvI3~~3$ zuM_MIEqp%ZR}Za5+iZM;_JuDRp7ANueAfaZ2wwuCfNR;SYbk+i*(@U?Si?FoLiwJF zBvtkYLNC7|QW&X2=V;@IaddJdI4*FcIeNchAfBni@Y9c>l>}`FBg&)Fju`*v(7_EI zN17e<@oSLVK7#}%kYr*WC@=k0T1nq#6b~_xGm5E0O#9SP1UcEw@DxF52J_5$&`A#( z{|j}&*U!NB64S?m`ibDd^zep<*JH_l0W(~eS;q0E@Njd)iKtzE`9-&Z{E6b@ZjW<>-C*(aT*4d)c)R?M zCe8ZKqFE&D7HMvi<_>hZ1U#A)t^9&9<1@|i{k+6LUJ72|1$UMazB*D0kY^b02rsOg mR=k1k({xX`Kc63!Kj233=)?<$SoS#^szs={&WH_RLh%nd671># literal 995 zcmZuwT~8BH5IwiwT^3rvQiWEuDlDv0zEOcbM-n|HO^d)?uSRnNicpFMXLKv*(8XnRnoI!UZX9VhrO7#sD^P z2e(YzMajfHOqrNK-b6v7o3b$}Q4#qho|t%wO?E$O1zr+cCbmH(j6k;t*}}w*AlU(l z{rZV_7+!;F=bg}NQjpduH*!~u{Z{!E>4qfjxlJ}1*EO!g3dxU>)Sy;5>;F;>HCpab z)vveR_BlyKc>VGXK?!r(8(bCX6@7gE0_qC`)}u{mP$}L7@&uJApgl#@vQ>G#R)E!laz;3iq6 println(s"flow: " + flow.map(_.toJava).mkString(", ")) + case _ => + } + } + } + } + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala new file mode 100644 index 0000000000..0c79deb848 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala @@ -0,0 +1,79 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds.taint + +import org.opalj.br.analyses.SomeProject +import org.opalj.br.DeclaredMethod +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.fpcf.analyses.ifds.{IFDSAnalysis, Statement} +import org.opalj.tac.fpcf.analyses.ifds.taint.{ + Fact, + FlowFact, + ForwardTaintAnalysis, + NullFact, + Taint, + Variable +} +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation + +/** + * An analysis that checks, if the return value of the method `source` can flow to the parameter of + * the method `sink`. + * + * @author Mario Trageser + * @author Marc Clement + */ +class MultilingualForwardTaintAnalysis private (implicit val pProject: SomeProject) + extends ForwardTaintAnalysis { + + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(DeclaredMethod, Fact)] = (for { + m <- p.allMethodsWithBody + if (m.name == "main") + } yield declaredMethods(m) -> NullFact) + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = + callee.name == "sanitize" + + /** + * We do not sanitize paramters. + */ + override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: DeclaredMethod, call: Statement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact( + callee: DeclaredMethod, + call: Statement, + in: Set[Fact] + ): Option[FlowFact] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) + else None + + // Multilingual additions here + + override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { + super.insideAnalysisContext(callee) //|| callee.definedMethod.isNative + } + +} + +object MultilingualForwardTaintAnalysis extends IFDSAnalysis[Fact] { + + override def init(p: SomeProject, ps: PropertyStore) = new MultilingualForwardTaintAnalysis()(p) + + override def property: IFDSPropertyMetaInformation[Fact] = Taint +} diff --git a/build.sbt b/build.sbt index f3bb9f3776..38847457b4 100644 --- a/build.sbt +++ b/build.sbt @@ -29,7 +29,6 @@ ThisBuild / licenses := Seq("BSD-2-Clause" -> url("https://opensource.org/licens usePgpKeyHex("80B9D3FB5A8508F6B4774932E71AFF01E234090C") - scalaVersion in ThisBuild := "2.12.15" ScalacConfiguration.globalScalacOptions @@ -45,9 +44,9 @@ ThisBuild / logBuffered := false ThisBuild / javacOptions ++= Seq("-encoding", "utf8", "-source", "1.8") -ThisBuild /testOptions := { +ThisBuild / testOptions := { baseDirectory - .map(bd ⇒ Seq(Tests.Argument("-u", bd.getAbsolutePath + "/shippable/testresults"))) + .map(bd => Seq(Tests.Argument("-u", bd.getAbsolutePath + "/shippable/testresults"))) .value } @@ -57,12 +56,12 @@ ThisBuild / testOptions += Tests.Argument("-o") // Required to get relative links in the generated source code documentation. ScalaUnidoc / unidoc / scalacOptions := { - baseDirectory.map(bd ⇒ Seq("-sourcepath", bd.getAbsolutePath)).value + baseDirectory.map(bd => Seq("-sourcepath", bd.getAbsolutePath)).value } ScalaUnidoc / unidoc / scalacOptions ++= Opts.doc.sourceUrl( - "https://raw.githubusercontent.com/stg-tud/opal/" + + "https://raw.githubusercontent.com/stg-tud/opal/" + (if (isSnapshot.value) "develop" else "master") + "/€{FILE_PATH}.scala" ) @@ -104,7 +103,10 @@ lazy val buildSettings = PublishingOverwrite.onSnapshotOverwriteSettings ++ Seq(libraryDependencies ++= Dependencies.testlibs) ++ Seq(Defaults.itSettings: _*) ++ - Seq(unmanagedSourceDirectories.withRank(KeyRanks.Invisible) := (Compile / scalaSource).value :: Nil) ++ + Seq( + unmanagedSourceDirectories + .withRank(KeyRanks.Invisible) := (Compile / scalaSource).value :: Nil + ) ++ Seq( Test / unmanagedSourceDirectories := (Test / javaSource).value :: (Test / scalaSource).value :: Nil ) ++ @@ -326,18 +328,17 @@ lazy val `ArchitectureValidation` = (project in file("OPAL/av")) .dependsOn(de % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) - lazy val ll = `LLVM` lazy val `LLVM` = (project in file("OPAL/ll")) .settings(buildSettings: _*) .settings( name := "LLVM", - scalacOptions in(Compile, doc) ++= Opts.doc.title("OPAL - LLVM"), + scalacOptions in (Compile, doc) ++= Opts.doc.title("OPAL - LLVM"), fork := true, libraryDependencies ++= Dependencies.ll ) -.dependsOn(br % "it->it;it->test;test->test;compile->compile") -.configs(IntegrationTest) + .dependsOn(tac % "it->it;it->test;test->test;compile->compile") + .configs(IntegrationTest) lazy val framework = `Framework` lazy val `Framework` = (project in file("OPAL/framework")) From 7d009738227a5651be125cf774330ec4045bd446 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 2 Mar 2022 18:59:26 +0100 Subject: [PATCH 16/67] Extract IFDSProblem(flow functions etc) from IFDSAnalysis --- .../heros/analyses/HerosAnalysis.scala | 4 +- .../analyses/HerosVariableTypeAnalysis.scala | 28 +- .../analyses/heros/analyses/VTAEquality.scala | 4 +- .../HerosBackwardClassForNameAnalysis.scala | 24 +- .../HerosForwardClassForNameAnalysis.scala | 32 +- .../analyses/heros/cfg/OpalBackwardICFG.scala | 24 +- .../analyses/heros/cfg/OpalForwardICFG.scala | 20 +- .../fpcf/analyses/heros/cfg/OpalICFG.scala | 36 +- .../BackwardClassForNameTaintAnalysis.scala | 20 +- .../ForwardClassForNameTaintAnalysis.scala | 23 +- ...MultilingualForwardTaintAnalysisTest.scala | 62 +-- .../MultilingualForwardTaintAnalysis.scala | 93 +++-- .../analyses/ifds/AbstractIFDSAnalysis.scala | 263 ++----------- .../analyses/ifds/BackwardIFDSAnalysis.scala | 119 ++---- .../analyses/ifds/ForwardIFDSAnalysis.scala | 44 +-- .../ifds/IFDSBasedVariableTypeAnalysis.scala | 357 +---------------- .../tac/fpcf/analyses/ifds/IFDSProblem.scala | 205 ++++++++++ .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 93 +++++ .../tac/fpcf/analyses/ifds/Subsumable.scala | 6 +- .../tac/fpcf/analyses/ifds/Subsuming.scala | 6 +- .../analyses/ifds/VariableTypeProblem.scala | 336 ++++++++++++++++ .../ifds/taint/BackwardTaintAnalysis.scala | 362 +----------------- .../ifds/taint/BackwardTaintProblem.scala | 359 +++++++++++++++++ .../ifds/taint/ForwardTaintAnalysis.scala | 340 +--------------- .../ifds/taint/ForwardTaintProblem.scala | 336 ++++++++++++++++ ...TaintAnalysis.scala => TaintProblem.scala} | 8 +- .../tac/fpcf/properties/IFDSProperty.scala | 4 +- .../taint/BackwardTaintAnalysisFixture.scala | 13 +- .../taint/ForwardTaintAnalysisFixture.scala | 12 +- 29 files changed, 1642 insertions(+), 1591 deletions(-) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/{TaintAnalysis.scala => TaintProblem.scala} (95%) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala index d49762d069..5f6529e38d 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala @@ -22,7 +22,7 @@ import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.fpcf.analyses.heros.cfg.OpalICFG -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.Assignment import org.opalj.tac.Call import org.opalj.tac.ExprStmt @@ -40,7 +40,7 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V * @author Mario Trageser */ abstract class HerosAnalysis[F](p: SomeProject, icfg: OpalICFG) - extends DefaultIFDSTabulationProblem[Statement, F, Method, OpalICFG](icfg) { + extends DefaultIFDSTabulationProblem[JavaStatement, F, Method, OpalICFG](icfg) { /** * The project's property store. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala index a3bbcb1e3c..ee9bb9b798 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala @@ -30,7 +30,7 @@ import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.DeclaredMethod import org.opalj.tac.fpcf.properties.cg.Callees -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.Assignment import org.opalj.tac.Expr import org.opalj.tac.GetStatic @@ -59,8 +59,8 @@ class HerosVariableTypeAnalysis( initialMethods: Map[Method, util.Set[VTAFact]] ) extends HerosAnalysis[VTAFact](p, icfg) { - override val initialSeeds: util.Map[Statement, util.Set[VTAFact]] = { - var result: Map[Statement, util.Set[VTAFact]] = Map.empty + override val initialSeeds: util.Map[JavaStatement, util.Set[VTAFact]] = { + var result: Map[JavaStatement, util.Set[VTAFact]] = Map.empty for ((m, facts) ← initialMethods) { result += icfg.getStartPointsOf(m).iterator().next() -> facts } @@ -69,13 +69,13 @@ class HerosVariableTypeAnalysis( override def createZeroValue(): VTAFact = VTANullFact - override protected def createFlowFunctionsFactory(): FlowFunctions[Statement, VTAFact, Method] = { + override protected def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, VTAFact, Method] = { - new FlowFunctions[Statement, VTAFact, Method]() { + new FlowFunctions[JavaStatement, VTAFact, Method]() { override def getNormalFlowFunction( - statement: Statement, - succ: Statement + statement: JavaStatement, + succ: JavaStatement ): FlowFunction[VTAFact] = { if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt @@ -103,7 +103,7 @@ class HerosVariableTypeAnalysis( } } - override def getCallFlowFunction(stmt: Statement, callee: Method): FlowFunction[VTAFact] = + override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[VTAFact] = if (!insideAnalysisContext(callee)) KillAll.v() else { val callObject = asCall(stmt.stmt) @@ -129,10 +129,10 @@ class HerosVariableTypeAnalysis( } override def getReturnFlowFunction( - stmt: Statement, + stmt: JavaStatement, callee: Method, - exit: Statement, - succ: Statement + exit: JavaStatement, + succ: JavaStatement ): FlowFunction[VTAFact] = if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { val returnValue = exit.stmt.asReturnValue.expr.asVar @@ -147,8 +147,8 @@ class HerosVariableTypeAnalysis( } else KillAll.v() override def getCallToReturnFlowFunction( - statement: Statement, - succ: Statement + statement: JavaStatement, + succ: JavaStatement ): FlowFunction[VTAFact] = { if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt @@ -227,7 +227,7 @@ class HerosVariableTypeAnalysis( callee.body.isDefined && (callee.classFile.fqn.startsWith("java/lang") || callee.classFile.fqn.startsWith("org/opalj/fpcf/fixtures/vta")) - private def getCallees(statement: Statement): Iterator[DeclaredMethod] = { + private def getCallees(statement: JavaStatement): Iterator[DeclaredMethod] = { val context = typeProvider.newContext(declaredMethods(statement.method)) val FinalEP(_, callees) = propertyStore(declaredMethods(statement.method), Callees.key) callees.directCallees(context, statement.stmt.pc).map(_.method) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala index 9e37428097..fe06dba7cf 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala @@ -31,7 +31,7 @@ import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG import org.opalj.tac.fpcf.analyses.ifds.CalleeType import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysis -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.VariableType import org.opalj.tac.fpcf.analyses.ifds.VTAFact import org.opalj.tac.fpcf.analyses.ifds.VTANullFact @@ -115,7 +115,7 @@ object VTAEquality { piKeyUnidueId != PropertyStoreKey.uniqueId } val propertyStore = project.get(PropertyStoreKey) - var result = Map.empty[Statement, Set[VTAFact]] + var result = Map.empty[JavaStatement, Set[VTAFact]] project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) case Some(requirements) ⇒ diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala index 4c16a96cd1..47703d67b7 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -16,7 +16,7 @@ import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.heros.cfg.OpalBackwardICFG import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.Assignment import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement import org.opalj.tac.fpcf.analyses.ifds.taint.Variable @@ -40,7 +40,7 @@ import org.opalj.tac.ReturnValue import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintAnalysis +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis /** @@ -51,7 +51,7 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis */ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) extends HerosTaintAnalysis(p, icfg) { - override val initialSeeds: util.Map[Statement, util.Set[Fact]] = + override val initialSeeds: util.Map[JavaStatement, util.Set[Fact]] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "java/lang/Class") .flatMap(classFile ⇒ classFile.methods) @@ -62,11 +62,11 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) override def followReturnsPastSeeds(): Boolean = true - override def createFlowFunctionsFactory(): FlowFunctions[Statement, Fact, Method] = { + override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { - new FlowFunctions[Statement, Fact, Method]() { + new FlowFunctions[JavaStatement, Fact, Method]() { - override def getNormalFlowFunction(statement: Statement, succ: Statement): FlowFunction[Fact] = { + override def getNormalFlowFunction(statement: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { val method = statement.method val stmt = statement.stmt source: Fact ⇒ { @@ -77,7 +77,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) else Set(source) case ArrayStore.ASTID ⇒ val arrayStore = stmt.asArrayStore - val arrayIndex = TaintAnalysis.getIntConstant(arrayStore.index, statement.code) + val arrayIndex = TaintProblem.getIntConstant(arrayStore.index, statement.code) val arrayDefinedBy = arrayStore.arrayRef.asVar.definedBy var facts = (source match { // In this case, we taint the whole array. @@ -127,7 +127,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } - override def getCallFlowFunction(stmt: Statement, callee: Method): FlowFunction[Fact] = { + override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[Fact] = { val callObject = asCall(stmt.stmt) val staticCall = callee.isStatic source: Fact ⇒ { @@ -169,7 +169,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } } - override def getReturnFlowFunction(statement: Statement, callee: Method, exit: Statement, succ: Statement): FlowFunction[Fact] = { + override def getReturnFlowFunction(statement: JavaStatement, callee: Method, exit: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { // If a method has no caller, returnFlow will be called with a null statement. if (statement == null) return Identity.v() val stmt = statement.stmt @@ -199,7 +199,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) }).asJava } - override def getCallToReturnFlowFunction(stmt: Statement, succ: Statement): FlowFunction[Fact] = + override def getCallToReturnFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = Identity.v() } } @@ -221,12 +221,12 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) }.toSet } - private def createNewTaints(expression: Expr[V], statement: Statement): Set[Fact] = + private def createNewTaints(expression: Expr[V], statement: JavaStatement): Set[Fact] = expression.astID match { case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) case ArrayLoad.ASTID ⇒ val arrayLoad = expression.asArrayLoad - val arrayIndex = TaintAnalysis.getIntConstant(expression.asArrayLoad.index, statement.code) + val arrayIndex = TaintProblem.getIntConstant(expression.asArrayLoad.index, statement.code) val arrayDefinedBy = arrayLoad.arrayRef.asVar.definedBy if (arrayIndex.isDefined) arrayDefinedBy.map(ArrayElement(_, arrayIndex.get)) else arrayDefinedBy.map(Variable) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index 66596c5dd2..7f293bbc75 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -33,7 +33,7 @@ import org.opalj.tac.PutField import org.opalj.tac.PutStatic import org.opalj.tac.ReturnValue import org.opalj.tac.Var -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement import org.opalj.tac.fpcf.analyses.ifds.taint.Fact import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact @@ -50,7 +50,7 @@ import org.opalj.tac.PrefixExpr import org.opalj.tac.PrimitiveTypecastExpr import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintAnalysis +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V @@ -66,8 +66,8 @@ class HerosForwardClassForNameAnalysis( initialMethods: Map[Method, util.Set[Fact]] ) extends HerosTaintAnalysis(p, icfg) { - override val initialSeeds: util.Map[Statement, util.Set[Fact]] = { - var result: Map[Statement, util.Set[Fact]] = Map.empty + override val initialSeeds: util.Map[JavaStatement, util.Set[Fact]] = { + var result: Map[JavaStatement, util.Set[Fact]] = Map.empty for ((m, facts) ← initialMethods) { result += icfg.getStartPointsOf(m).iterator().next() -> facts } @@ -78,18 +78,18 @@ class HerosForwardClassForNameAnalysis( var flowFacts = Map.empty[Method, Set[FlowFact]] - override def createFlowFunctionsFactory(): FlowFunctions[Statement, Fact, Method] = { + override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { - new FlowFunctions[Statement, Fact, Method]() { + new FlowFunctions[JavaStatement, Fact, Method]() { - override def getNormalFlowFunction(stmt: Statement, succ: Statement): FlowFunction[Fact] = { + override def getNormalFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { stmt.stmt.astID match { case Assignment.ASTID ⇒ handleAssignment(stmt, stmt.stmt.asAssignment.expr) case ArrayStore.ASTID ⇒ val store = stmt.stmt.asArrayStore val definedBy = store.arrayRef.asVar.definedBy - val index = TaintAnalysis.getIntConstant(store.index, stmt.code) + val index = TaintProblem.getIntConstant(store.index, stmt.code) (source: Fact) ⇒ { if (isTainted(store.value, source)) { if (index.isDefined) { @@ -131,7 +131,7 @@ class HerosForwardClassForNameAnalysis( } } - def handleAssignment(stmt: Statement, expr: Expr[V]): FlowFunction[Fact] = + def handleAssignment(stmt: JavaStatement, expr: Expr[V]): FlowFunction[Fact] = expr.astID match { case Var.ASTID ⇒ (source: Fact) ⇒ { @@ -148,7 +148,7 @@ class HerosForwardClassForNameAnalysis( source match { // The specific array element may be tainted case ArrayElement(index, taintedIndex) ⇒ - val arrIndex = TaintAnalysis.getIntConstant(load.index, stmt.code) + val arrIndex = TaintProblem.getIntConstant(load.index, stmt.code) if (arrayDefinedBy.contains(index) && (arrIndex.isEmpty || taintedIndex == arrIndex.get)) TwoElementSet.twoElementSet(source, Variable(stmt.index)) @@ -198,7 +198,7 @@ class HerosForwardClassForNameAnalysis( case _ ⇒ Identity.v() } - override def getCallFlowFunction(stmt: Statement, callee: Method): FlowFunction[Fact] = { + override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[Fact] = { val callObject = asCall(stmt.stmt) val allParams = callObject.allParams if (relevantCallee(callee)) { @@ -252,10 +252,10 @@ class HerosForwardClassForNameAnalysis( } override def getReturnFlowFunction( - stmt: Statement, + stmt: JavaStatement, callee: Method, - exit: Statement, - succ: Statement + exit: JavaStatement, + succ: JavaStatement ): FlowFunction[Fact] = { def isRefTypeParam(index: Int): Boolean = @@ -348,8 +348,8 @@ class HerosForwardClassForNameAnalysis( } override def getCallToReturnFlowFunction( - stmt: Statement, - succ: Statement + stmt: JavaStatement, + succ: JavaStatement ): FlowFunction[Fact] = Identity.v() } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala index e035296909..cd6e7be608 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala @@ -12,7 +12,7 @@ import scala.collection.JavaConverters._ import org.opalj.br.analyses.SomeProject import org.opalj.br.Method import org.opalj.br.cfg.BasicBlock -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.TACode /** @@ -22,22 +22,22 @@ import org.opalj.tac.TACode */ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { - override def getPredsOf(stmt: Statement): JList[Statement] = super.getSuccsOf(stmt) + override def getPredsOf(stmt: JavaStatement): JList[JavaStatement] = super.getSuccsOf(stmt) - override def getSuccsOf(stmt: Statement): JList[Statement] = super.getPredsOf(stmt) + override def getSuccsOf(stmt: JavaStatement): JList[JavaStatement] = super.getPredsOf(stmt) - override def getStartPointsOf(m: Method): JCollection[Statement] = { + override def getStartPointsOf(m: Method): JCollection[JavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).map { case bb: BasicBlock ⇒ val index = bb.endPC - Statement(m, bb, code(index), index, code, cfg) + JavaStatement(m, bb, code(index), index, code, cfg) }.asJava } - override def isExitStmt(stmt: Statement): Boolean = stmt.index == 0 + override def isExitStmt(stmt: JavaStatement): Boolean = stmt.index == 0 - override def isStartPoint(stmt: Statement): Boolean = { + override def isStartPoint(stmt: JavaStatement): Boolean = { val cfg = stmt.cfg val index = stmt.index (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).exists { @@ -45,8 +45,8 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { } } - override def allNonCallStartNodes(): JSet[Statement] = { - val res = new ConcurrentLinkedQueue[Statement] + override def allNonCallStartNodes(): JSet[JavaStatement] = { + val res = new ConcurrentLinkedQueue[JavaStatement] project.parForeachMethodWithBody() { mi ⇒ val m = mi.method val TACode(_, code, _, cfg, _) = tacai(m) @@ -58,7 +58,7 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { var index = 0 while (index < endIndex) { val stmt = code(index) - val statement = Statement(m, cfg.bb(index), stmt, index, code, cfg) + val statement = JavaStatement(m, cfg.bb(index), stmt, index, code, cfg) if (!(isCallStmt(statement) || startIndices.contains(index))) res.add(statement) index += 1 @@ -68,10 +68,10 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { Collections.emptySet() } - def getExitStmt(method: Method): Statement = { + def getExitStmt(method: Method): JavaStatement = { val tac = tacai(method) val cfg = tac.cfg val code = tac.stmts - Statement(method, cfg.startBlock, code(0), 0, code, cfg) + JavaStatement(method, cfg.startBlock, code(0), 0, code, cfg) } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala index a07cd1778f..092c13919f 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala @@ -9,7 +9,7 @@ import java.util.concurrent.ConcurrentLinkedQueue import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.tac.TACode -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement /** * A forward ICFG for Heros analyses. @@ -18,17 +18,17 @@ import org.opalj.tac.fpcf.analyses.ifds.Statement */ class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { - override def getStartPointsOf(m: Method): JCollection[Statement] = { + override def getStartPointsOf(m: Method): JCollection[JavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) - Collections.singletonList(Statement(m, cfg.startBlock, code(0), 0, code, cfg)) + Collections.singletonList(JavaStatement(m, cfg.startBlock, code(0), 0, code, cfg)) } - override def isExitStmt(stmt: Statement): Boolean = stmt.cfg.bb(stmt.index).successors.exists(_.isExitNode) + override def isExitStmt(stmt: JavaStatement): Boolean = stmt.cfg.bb(stmt.index).successors.exists(_.isExitNode) - override def isStartPoint(stmt: Statement): Boolean = stmt.index == 0 + override def isStartPoint(stmt: JavaStatement): Boolean = stmt.index == 0 - override def allNonCallStartNodes(): JSet[Statement] = { - val res = new ConcurrentLinkedQueue[Statement] + override def allNonCallStartNodes(): JSet[JavaStatement] = { + val res = new ConcurrentLinkedQueue[JavaStatement] project.parForeachMethodWithBody() { mi ⇒ val m = mi.method val TACode(_, code, _, cfg, _) = tacai(m) @@ -36,7 +36,7 @@ class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { var index = 1 while (index < endIndex) { val stmt = code(index) - val statement = Statement(m, cfg.bb(index), stmt, index, code, cfg) + val statement = JavaStatement(m, cfg.bb(index), stmt, index, code, cfg) if (!isCallStmt(statement)) res.add(statement) index += 1 @@ -46,13 +46,13 @@ class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { Collections.emptySet() } - def getExitStmts(method: Method): Iterator[Statement] = { + def getExitStmts(method: Method): Iterator[JavaStatement] = { val tac = tacai(method) val cfg = tac.cfg val code = tac.stmts cfg.allNodes.filter(_.isExitNode).flatMap(_.predecessors).map { bb ⇒ val endPc = bb.asBasicBlock.endPC - Statement(method, bb, code(endPc), endPc, code, cfg) + JavaStatement(method, bb, code(endPc), endPc, code, cfg) } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala index dbbb22113f..e0d4921ff9 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala @@ -18,7 +18,7 @@ import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.br.DefinedMethod import org.opalj.br.MultipleDefinedMethods import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.Assignment import org.opalj.tac.DUVar import org.opalj.tac.FunctionCall @@ -44,7 +44,7 @@ import org.opalj.tac.fpcf.analyses.cg.TypeProvider * @param project The project, which is analyzed. * @author Mario Trageser */ -abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[Statement, Method] { +abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaStatement, Method] { val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) @@ -52,31 +52,31 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[Stateme implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) - override def getMethodOf(stmt: Statement): Method = stmt.method + override def getMethodOf(stmt: JavaStatement): Method = stmt.method - override def getPredsOf(stmt: Statement): JList[Statement] = { + override def getPredsOf(stmt: JavaStatement): JList[JavaStatement] = { stmt.cfg .predecessors(stmt.index) .toChain .map { index ⇒ - Statement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) + JavaStatement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) } .toList .asJava } - override def getSuccsOf(stmt: Statement): JList[Statement] = { + override def getSuccsOf(stmt: JavaStatement): JList[JavaStatement] = { stmt.cfg .successors(stmt.index) .toChain .map { index ⇒ - Statement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) + JavaStatement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) } .toList .asJava } - override def getCalleesOfCallAt(callInstr: Statement): JCollection[Method] = { + override def getCalleesOfCallAt(callInstr: JavaStatement): JCollection[Method] = { val declaredMethod = declaredMethods(callInstr.method) val FinalEP(_, callees) = ps(declaredMethod, Callees.key) callees @@ -92,7 +92,7 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[Stateme .asJava } - override def getCallersOf(m: Method): JCollection[Statement] = { + override def getCallersOf(m: Method): JCollection[JavaStatement] = { val declaredMethod = declaredMethods(m) val FinalEP(_, callers) = ps(declaredMethod, Callers.key) callers @@ -101,31 +101,31 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[Stateme case (method, pc, true) ⇒ val TACode(_, code, pcToIndex, cfg, _) = tacai(method.definedMethod) val index = pcToIndex(pc) - Some(Statement(method.definedMethod, cfg.bb(index), code(index), index, code, cfg)) + Some(JavaStatement(method.definedMethod, cfg.bb(index), code(index), index, code, cfg)) case _ ⇒ None } .toSet .asJava } - override def getCallsFromWithin(m: Method): JSet[Statement] = { + override def getCallsFromWithin(m: Method): JSet[JavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) code.zipWithIndex .collect { - case (mc: MethodCall[V], index) ⇒ Statement(m, cfg.bb(index), mc, index, code, cfg) + case (mc: MethodCall[V], index) ⇒ JavaStatement(m, cfg.bb(index), mc, index, code, cfg) case (as @ Assignment(_, _, _: FunctionCall[V]), index) ⇒ - Statement(m, cfg.bb(index), as, index, code, cfg) + JavaStatement(m, cfg.bb(index), as, index, code, cfg) case (ex @ ExprStmt(_, _: FunctionCall[V]), index) ⇒ - Statement(m, cfg.bb(index), ex, index, code, cfg) + JavaStatement(m, cfg.bb(index), ex, index, code, cfg) } .toSet .asJava } - override def getReturnSitesOfCallAt(callInstr: Statement): JCollection[Statement] = + override def getReturnSitesOfCallAt(callInstr: JavaStatement): JCollection[JavaStatement] = getSuccsOf(callInstr) - override def isCallStmt(stmt: Statement): Boolean = { + override def isCallStmt(stmt: JavaStatement): Boolean = { def isCallExpr(expr: Expr[V]) = expr.astID match { case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ true @@ -141,8 +141,8 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[Stateme } } - override def isFallThroughSuccessor(stmt: Statement, succ: Statement): Boolean = ??? + override def isFallThroughSuccessor(stmt: JavaStatement, succ: JavaStatement): Boolean = ??? - override def isBranchTarget(stmt: Statement, succ: Statement): Boolean = ??? + override def isBranchTarget(stmt: JavaStatement, succ: JavaStatement): Boolean = ??? } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala index a5e795698c..e724c5726a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala @@ -10,15 +10,9 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.DefinedMethod import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysis -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact -import org.opalj.tac.fpcf.analyses.ifds.taint.Taint -import org.opalj.tac.fpcf.analyses.ifds.taint.Variable -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, BackwardTaintAnalysis, BackwardTaintProblem, Fact, FlowFact, InstanceField, Taint, Variable} +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact -import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement -import org.opalj.tac.fpcf.analyses.ifds.taint.InstanceField import org.opalj.tac.fpcf.analyses.ifds.AbsractIFDSAnalysisRunner import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation @@ -30,7 +24,9 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * @author Mario Trageser */ class BackwardClassForNameTaintAnalysis private (implicit project: SomeProject) - extends BackwardTaintAnalysis { + extends BackwardTaintAnalysis(new BackwardClassForNameTaintProblem(project)) + +class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProblem(p) { /** * The string parameters of all public methods are entry points. @@ -50,12 +46,12 @@ class BackwardClassForNameTaintAnalysis private (implicit project: SomeProject) /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * Do not perform unbalanced return for methods, which can be called from outside the library. */ - override protected def shouldPerformUnbalancedReturn(source: (DeclaredMethod, Fact)): Boolean = { + override def shouldPerformUnbalancedReturn(source: (DeclaredMethod, Fact)): Boolean = { super.shouldPerformUnbalancedReturn(source) && (!canBeCalledFromOutside(source._1) || // The source is callable from outside, but should create unbalanced return facts. @@ -66,7 +62,7 @@ class BackwardClassForNameTaintAnalysis private (implicit project: SomeProject) * This analysis does not create FlowFacts at calls. * Instead, FlowFacts are created at the start node of methods. */ - override protected def createFlowFactAtCall(call: Statement, in: Set[Fact], + override protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], source: (DeclaredMethod, Fact)): Option[FlowFact] = None /** diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala index 24313a893e..19b5b0de7a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala @@ -2,20 +2,14 @@ package org.opalj.tac.fpcf.analyses.taint import java.io.File - import org.opalj.fpcf.PropertyStore import org.opalj.br.DeclaredMethod import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact -import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintAnalysis -import org.opalj.tac.fpcf.analyses.ifds.taint.Taint -import org.opalj.tac.fpcf.analyses.ifds.taint.Variable -import org.opalj.tac.fpcf.analyses.ifds.Statement -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintAnalysis +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintAnalysis, ForwardTaintProblem, Taint, TaintProblem, Variable} +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.AbsractIFDSAnalysisRunner import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis import org.opalj.tac.fpcf.properties.IFDSProperty @@ -30,7 +24,10 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * @author Michael Eichberg */ class ForwardClassForNameTaintAnalysis private (implicit project: SomeProject) - extends ForwardTaintAnalysis with TaintAnalysis { + extends ForwardTaintAnalysis(new ForwardClassForNameTaintProblem(project)) + +class ForwardClassForNameTaintProblem(project: SomeProject) + extends ForwardTaintProblem(project) with TaintProblem { /** * The string parameters of all public methods are entry points. @@ -51,19 +48,19 @@ class ForwardClassForNameTaintAnalysis private (implicit project: SomeProject) /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * This analysis does not create new taints on the fly. * Instead, the string parameters of all public methods are tainted in the entry points. */ - override protected def createTaints(callee: DeclaredMethod, call: Statement): Set[Fact] = + override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = Set.empty /** * Create a FlowFact, if Class.forName is called with a tainted variable for the first parameter. */ - override protected def createFlowFact(callee: DeclaredMethod, call: Statement, + override protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, in: Set[Fact]): Option[FlowFact] = if (isClassForName(callee) && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) @@ -106,7 +103,7 @@ class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = for { - e ← analysis.entryPoints + e ← analysis.ifdsProblem.entryPoints flows = ps(e, ForwardClassForNameTaintAnalysis.property.key) fact ← flows.ub.asInstanceOf[IFDSProperty[Fact]].flows.values.flatten.toSet[Fact] } { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index 7c13629923..7f218aac02 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -12,37 +12,37 @@ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers { - describe("MultilingualForwardTaintAnalysis") { - it("executes") { - val project = - Project( - new java.io.File("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint") - ) - //project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( - // current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll") - //) - project.get(RTACallGraphKey) - val declaredMethods = project.get(DeclaredMethodsKey) - val manager = project.get(FPCFAnalysesManagerKey) - val (ps, analyses) = manager.runAll(MultilingualForwardTaintAnalysis) - for (method <- project.allMethodsWithBody) { - val flows = - ps((declaredMethods(method), NullFact), MultilingualForwardTaintAnalysis.property.key) - println("---METHOD: " + method.toJava + " ---") - for { - fact <- flows.ub - .asInstanceOf[IFDSProperty[Fact]] - .flows - .values - .flatten - .toSet[Fact] - } { - fact match { - case FlowFact(flow) => println(s"flow: " + flow.map(_.toJava).mkString(", ")) - case _ => - } + describe("MultilingualForwardTaintAnalysis") { + it("executes") { + val project = + Project( + new java.io.File("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint") + ) + //project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( + // current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll") + //) + project.get(RTACallGraphKey) + val declaredMethods = project.get(DeclaredMethodsKey) + val manager = project.get(FPCFAnalysesManagerKey) + val (ps, analyses) = manager.runAll(MultilingualForwardTaintAnalysis) + for (method ← project.allMethodsWithBody) { + val flows = + ps((declaredMethods(method), NullFact), MultilingualForwardTaintAnalysis.property.key) + println("---METHOD: "+method.toJava+" ---") + for { + fact ← flows.ub + .asInstanceOf[IFDSProperty[Fact]] + .flows + .values + .flatten + .toSet[Fact] + } { + fact match { + case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.toJava).mkString(", ")) + case _ ⇒ + } + } + } } - } } - } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala index 0c79deb848..23310ca942 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala @@ -4,15 +4,8 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod import org.opalj.fpcf.PropertyStore -import org.opalj.tac.fpcf.analyses.ifds.{IFDSAnalysis, Statement} -import org.opalj.tac.fpcf.analyses.ifds.taint.{ - Fact, - FlowFact, - ForwardTaintAnalysis, - NullFact, - Taint, - Variable -} +import org.opalj.tac.fpcf.analyses.ifds.{IFDSAnalysis, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintAnalysis, ForwardTaintProblem, NullFact, Taint, Variable} import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation /** @@ -23,57 +16,59 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * @author Marc Clement */ class MultilingualForwardTaintAnalysis private (implicit val pProject: SomeProject) - extends ForwardTaintAnalysis { + extends ForwardTaintAnalysis(new MultilingualForwardTaintProblem(pProject)) + +class MultilingualForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { - /** - * The analysis starts with all public methods in TaintAnalysisTestClass. - */ - override val entryPoints: Seq[(DeclaredMethod, Fact)] = (for { - m <- p.allMethodsWithBody - if (m.name == "main") - } yield declaredMethods(m) -> NullFact) + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(DeclaredMethod, Fact)] = (for { + m ← p.allMethodsWithBody + if (m.name == "main") + } yield declaredMethods(m) -> NullFact) - /** - * The sanitize method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = - callee.name == "sanitize" + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = + callee.name == "sanitize" - /** - * We do not sanitize paramters. - */ - override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + /** + * We do not sanitize paramters. + */ + override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty - /** - * Creates a new variable fact for the callee, if the source was called. - */ - override protected def createTaints(callee: DeclaredMethod, call: Statement): Set[Fact] = - if (callee.name == "source") Set(Variable(call.index)) - else Set.empty + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty - /** - * Create a FlowFact, if sink is called with a tainted variable. - * Note, that sink does not accept array parameters. No need to handle them. - */ - override protected def createFlowFact( - callee: DeclaredMethod, - call: Statement, - in: Set[Fact] - ): Option[FlowFact] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) - else None + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact( + callee: DeclaredMethod, + call: JavaStatement, + in: Set[Fact] + ): Option[FlowFact] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) + else None - // Multilingual additions here + // Multilingual additions here - override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { - super.insideAnalysisContext(callee) //|| callee.definedMethod.isNative - } + override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { + super.insideAnalysisContext(callee) //|| callee.definedMethod.isNative + } } object MultilingualForwardTaintAnalysis extends IFDSAnalysis[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new MultilingualForwardTaintAnalysis()(p) + override def init(p: SomeProject, ps: PropertyStore) = new MultilingualForwardTaintAnalysis()(p) - override def property: IFDSPropertyMetaInformation[Fact] = Taint + override def property: IFDSPropertyMetaInformation[Fact] = Taint } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index ec4b4f85a4..9cf42e5fa4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -60,24 +60,7 @@ import org.opalj.tac.cg.{RTACallGraphKey, TypeProviderKey} import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -/** - * The supertype of all IFDS facts. - */ -trait AbstractIFDSFact - -/** - * The super type of all null facts. - */ -trait AbstractIFDSNullFact extends AbstractIFDSFact - -/** - * A framework for IFDS analyses. - * - * @tparam IFDSFact The type of flow facts, which are tracked by the concrete analysis. - * @author Dominik Helm - * @author Mario Trageser - */ -abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] +abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement]) extends FPCFAnalysis with Subsumable[IFDSFact] { @@ -104,11 +87,6 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] */ var sumOfInputfactsForCallbacks = 0L - /** - * The entry points of this analysis. - */ - def entryPoints: Seq[(DeclaredMethod, IFDSFact)] - /** * The state of the analysis. For each method and source fact, there is a separate state. * @@ -146,100 +124,6 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] var outgoingFacts: Map[BasicBlock, Map[CFGNode, Set[IFDSFact]]] = Map.empty ) - /** - * The null fact of this analysis. - */ - protected def nullFact: IFDSFact - - /** - * Computes the data flow for a normal statement. - * - * @param statement The analyzed statement. - * @param successor The successor of the analyzed `statement`, for which the data flow shall be - * computed. - * @param in The facts, which hold before the execution of the `statement`. - * @return The facts, which hold after the execution of `statement` under the assumption - * that the facts in `in` held before `statement` and `successor` will be - * executed next. - */ - protected def normalFlow( - statement: Statement, - successor: Statement, - in: Set[IFDSFact] - ): Set[IFDSFact] - - /** - * Computes the data flow for a call to start edge. - * - * @param call The analyzed call statement. - * @param callee The called method, for which the data flow shall be computed. - * @param in The facts, which hold before the execution of the `call`. - * @param source The entity, which is analyzed. - * @return The facts, which hold after the execution of `statement` under the assumption that - * the facts in `in` held before `statement` and `statement` calls `callee`. - */ - protected def callFlow( - call: Statement, - callee: DeclaredMethod, - in: Set[IFDSFact], - source: (DeclaredMethod, IFDSFact) - ): Set[IFDSFact] - - /** - * Computes the data flow for an exit to return edge. - * - * @param call The statement, which called the `callee`. - * @param callee The method called by `call`, for which the data flow shall be computed. - * @param exit The statement, which terminated the `calle`. - * @param successor The statement of the caller, which will be executed after the `callee` - * returned. - * @param in The facts, which hold before the execution of the `exit`. - * @return The facts, which hold after the execution of `exit` in the caller's context - * under the assumption that `in` held before the execution of `exit` and that - * `successor` will be executed next. - */ - protected def returnFlow( - call: Statement, - callee: DeclaredMethod, - exit: Statement, - successor: Statement, - in: Set[IFDSFact] - ): Set[IFDSFact] - - /** - * Computes the data flow for a call to return edge. - * - * @param call The statement, which invoked the call. - * @param successor The statement, which will be executed after the call. - * @param in The facts, which hold before the `call`. - * @param source The entity, which is analyzed. - * @return The facts, which hold after the call independently of what happens in the callee - * under the assumption that `in` held before `call`. - */ - protected def callToReturnFlow( - call: Statement, - successor: Statement, - in: Set[IFDSFact], - source: (DeclaredMethod, IFDSFact) - ): Set[IFDSFact] - - /** - * When a callee outside of this analysis' context is called, this method computes the summary - * edge for the call. - * - * @param call The statement, which invoked the call. - * @param callee The method called by `call`. - * @param successor The statement, which will be executed after the call. - * @param in The facts facts, which hold before the `call`. - * @return The facts, which hold after the call, excluding the call to return flow. - */ - protected def callOutsideOfAnalysisContext( - call: Statement, - callee: DeclaredMethod, - successor: Statement, - in: Set[IFDSFact] - ): Set[IFDSFact] - /** * Determines the basic blocks, at which the analysis starts. * @@ -255,7 +139,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at * the exit node under the assumption that the predecessor was executed before. */ - protected def collectResult(implicit state: State): Map[Statement, Set[IFDSFact]] + protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] /** * Creates an IFDSProperty containing the result of this analysis. @@ -263,7 +147,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * @param result Maps each exit statement to the facts, which hold after the exit statement. * @return An IFDSProperty containing the `result`. */ - protected def createPropertyValue(result: Map[Statement, Set[IFDSFact]]): IFDSProperty[IFDSFact] + protected def createPropertyValue(result: Map[JavaStatement, Set[IFDSFact]]): IFDSProperty[IFDSFact] /** * Determines the nodes, that will be analyzed after some `basicBlock`. @@ -319,7 +203,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * @param node The node. * @return The first statement of a node, that will be analyzed. */ - protected def firstStatement(node: CFGNode)(implicit state: State): Statement + protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement /** * Determines the statement, that will be analyzed after some other `statement`. @@ -327,7 +211,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * @param statement The source statement. * @return The successor statements */ - protected def nextStatements(statement: Statement)(implicit state: State): Set[Statement] + protected def nextStatements(statement: JavaStatement)(implicit state: State): Set[JavaStatement] /** * Determines the facts, for which a `callee` is analyzed. @@ -337,7 +221,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * @param in The facts, which hold before the `call`. * @return The facts, for which `callee` will be analyzed. */ - protected def callToStartFacts(call: Statement, callee: DeclaredMethod, in: Set[IFDSFact])( + protected def callToStartFacts(call: JavaStatement, callee: DeclaredMethod, in: Set[IFDSFact])( implicit state: State ): Set[IFDSFact] @@ -354,12 +238,12 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * @return The summary edges plus the exit to return facts for `callee` and `successor`. */ protected def addExitToReturnFacts( - summaryEdges: Map[Statement, Set[IFDSFact]], - successors: Set[Statement], - call: Statement, + summaryEdges: Map[JavaStatement, Set[IFDSFact]], + successors: Set[JavaStatement], + call: JavaStatement, callee: DeclaredMethod, - exitFacts: Map[Statement, Set[IFDSFact]] - )(implicit state: State): Map[Statement, Set[IFDSFact]] + exitFacts: Map[JavaStatement, Set[IFDSFact]] + )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] /** * Performs an IFDS analysis for a method-fact-pair. @@ -457,7 +341,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] ub: IFDSProperty[IFDSFact] ) ⇒ if (ub.flows.values - .forall(facts ⇒ facts.size == 1 && facts.forall(_ == nullFact))) { + .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { // Do not re-analyze the caller if we only get the null fact. // Update the pendingIfdsDependee entry to the new interim result. state.pendingIfdsDependees += @@ -480,17 +364,6 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] createResult() } - /** - * Checks, if a callee is inside this analysis' context. - * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. - * By default, native methods are not inside the analysis context. - * - * @param callee The callee. - * @return True, if the callee is inside the analysis context. - */ - protected def insideAnalysisContext(callee: DeclaredMethod): Boolean = - callee.definedMethod.body.isDefined - /** * Called, when some new information is found at the last node of the method. * This method can be overwritten by a subclass to perform additional actions. @@ -518,18 +391,18 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] */ protected def handleCall( basicBlock: BasicBlock, - call: Statement, + call: JavaStatement, callees: SomeSet[Method], in: Set[IFDSFact], calleeWithUpdateFact: Option[IFDSFact] )( implicit state: State - ): Map[Statement, Set[IFDSFact]] = { + ): Map[JavaStatement, Set[IFDSFact]] = { val successors = nextStatements(call) val inputFacts = beforeHandleCall(call, in) // Facts valid at the start of each successor - var summaryEdges: Map[Statement, Set[IFDSFact]] = Map.empty + var summaryEdges: Map[JavaStatement, Set[IFDSFact]] = Map.empty /* * If calleeWithUpdateFact is present, this means that the basic block already has been @@ -542,26 +415,26 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] summaryEdges += successor -> propagateNullFact( inputFacts, - callToReturnFlow(call, successor, inputFacts, state.source) + ifdsProblem.callToReturnFlow(call, successor, inputFacts, state.source) ) } for (calledMethod ← callees) { val callee = declaredMethods(calledMethod) - if (!insideAnalysisContext(callee)) { + if (!ifdsProblem.insideAnalysisContext(callee)) { // Let the concrete analysis decide what to do. for { successor ← successors } summaryEdges += successor -> (summaryEdges(successor) ++ - callOutsideOfAnalysisContext(call, callee, successor, in)) + ifdsProblem.callOutsideOfAnalysisContext(call, callee, successor, in)) } else { val callToStart = if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) else { propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) } - var allNewExitFacts: Map[Statement, Set[IFDSFact]] = Map.empty + var allNewExitFacts: Map[JavaStatement, Set[IFDSFact]] = Map.empty // Collect exit facts for each input fact separately for (fact ← callToStart) { /* @@ -584,11 +457,11 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] val callFlows = propertyStore(e, propertyKey.key) .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[Statement, Set[IFDSFact]] = oldValue match { + val oldExitFacts: Map[JavaStatement, Set[IFDSFact]] = oldValue match { case Some(ep: InterimEUBP[_, IFDSProperty[IFDSFact]]) ⇒ ep.ub.flows case _ ⇒ Map.empty } - val exitFacts: Map[Statement, Set[IFDSFact]] = callFlows match { + val exitFacts: Map[JavaStatement, Set[IFDSFact]] = callFlows match { case ep: FinalEP[_, IFDSProperty[IFDSFact]] ⇒ if (state.pendingIfdsCallSites.contains(e) && state.pendingIfdsCallSites(e).nonEmpty) { @@ -645,43 +518,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * @param in The input facts, which hold before the `call`. * @return The changed set of input facts. */ - protected def beforeHandleCall(call: Statement, in: Set[IFDSFact]): Set[IFDSFact] = in - - /** - * Gets the call object for a statement that contains a call. - * - * @param call The call statement. - * @return The call object for `call`. - */ - protected def asCall(call: Stmt[V]): Call[V] = call.astID match { - case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall - case _ ⇒ call.asMethodCall - } - - /** - * Gets the set of all methods directly callable at some call statement. - * - * @param basicBlock The basic block containing the call. - * @param pc The call's program counter. - * @param caller The caller, performing the call. - * @return All methods directly callable at the statement index. - */ - protected def getCallees( - basicBlock: BasicBlock, - pc: Int, - caller: DeclaredMethod - ): SomeSet[Method] = { - val ep = propertyStore(caller, Callees.key) - ep match { - case FinalEP(_, p) ⇒ - definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method)) - case _ ⇒ - throw new IllegalStateException( - "call graph mut be computed before the analysis starts" - ) - } - } + protected def beforeHandleCall(call: JavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = in /** * Merges two maps that have sets as values. @@ -707,27 +544,6 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] result } - /** - * Returns all methods, that can be called from outside the library. - * The call graph must be computed, before this method may be invoked. - * - * @return All methods, that can be called from outside the library. - */ - protected def methodsCallableFromOutside: Set[DeclaredMethod] = - declaredMethods.declaredMethods.filter(canBeCalledFromOutside).toSet - - /** - * Checks, if some `method` can be called from outside the library. - * The call graph must be computed, before this method may be invoked. - * - * @param method The method, which may be callable from outside. - * @return True, if `method` can be called from outside the library. - */ - protected def canBeCalledFromOutside(method: DeclaredMethod): Boolean = { - val FinalEP(_, callers) = propertyStore(method, Callers.key) - callers.hasCallersWithUnknownContext - } - /** * Analyzes a queue of BasicBlocks. * @@ -840,12 +656,12 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * calleeFact: If `index` equals `calleeWithUpdateIndex`, only * `calleeWithUpdateFact` will be returned, None otherwise. */ - def collectInformation(index: Int): (Statement, Option[SomeSet[Method]], Option[IFDSFact]) = { + def collectInformation(index: Int): (JavaStatement, Option[SomeSet[Method]], Option[IFDSFact]) = { val stmt = state.code(index) - val statement = Statement(state.method, basicBlock, stmt, index, state.code, state.cfg) + val statement = JavaStatement(state.method, basicBlock, stmt, index, state.code, state.cfg) val calleesO = if (calleeWithUpdateIndex.contains(index)) calleeWithUpdate.map(Set(_)) - else getCalleesIfCallStatement(basicBlock, index) + else getCalleesIfCallStatement(statement) val calleeFact = if (calleeWithUpdateIndex.contains(index)) calleeWithUpdateFact else None @@ -862,10 +678,10 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] val next = nextIndex(index) flows = if (calleesO.isEmpty) { val successor = - Statement(state.method, basicBlock, state.code(next), next, state.code, state.cfg) + JavaStatement(state.method, basicBlock, state.code(next), next, state.code, state.cfg) numberOfCalls.normalFlow += 1 sumOfInputfactsForCallbacks += in.size - normalFlow(statement, successor, flows) + ifdsProblem.normalFlow(statement, successor, flows) } else // Inside a basic block, we only have one successor --> Take the head handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head @@ -880,7 +696,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] for (node ← nextNodes(basicBlock)) { numberOfCalls.normalFlow += 1 sumOfInputfactsForCallbacks += in.size - result += node -> normalFlow(statement, firstStatement(node), flows) + result += node -> ifdsProblem.normalFlow(statement, firstStatement(node), flows) } result } else @@ -929,20 +745,19 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * @return All methods possibly called at the statement index or None, if the statement does not * contain a call. */ - private def getCalleesIfCallStatement(basicBlock: BasicBlock, index: Int)( + private def getCalleesIfCallStatement(statement: JavaStatement)( implicit state: State ): Option[SomeSet[Method]] = { - val statement = state.code(index) - val pc = statement.pc - statement.astID match { + //val statement = state.code(index) + statement.stmt.astID match { case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ - Some(getCallees(basicBlock, pc, state.source._1)) + Some(definedMethods(ifdsProblem.getCallees(statement, state.source._1))) case Assignment.ASTID | ExprStmt.ASTID ⇒ - getExpression(statement).astID match { + getExpression(statement.stmt).astID match { case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - Some(getCallees(basicBlock, pc, state.source._1)) + Some(definedMethods(ifdsProblem.getCallees(statement, state.source._1))) case _ ⇒ None } case _ ⇒ None @@ -991,7 +806,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact] * @return `to` with the null fact added, if it is contained in `from`. */ private def propagateNullFact(from: Set[IFDSFact], to: Set[IFDSFact]): Set[IFDSFact] = { - if (from.contains(nullFact)) to + nullFact + if (from.contains(ifdsProblem.nullFact)) to + ifdsProblem.nullFact else to } @@ -1077,7 +892,7 @@ object AbstractIFDSAnalysis { * @param code The method's TAC code. * @param cfg The method's CFG. */ -case class Statement( +case class JavaStatement( method: Method, node: CFGNode, stmt: Stmt[V], @@ -1089,8 +904,8 @@ case class Statement( override def hashCode(): Int = method.hashCode() * 31 + index override def equals(o: Any): Boolean = o match { - case s: Statement ⇒ s.index == index && s.method == method - case _ ⇒ false + case s: JavaStatement ⇒ s.index == index && s.method == method + case _ ⇒ false } override def toString: String = s"${method.toJava}" @@ -1136,7 +951,7 @@ abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalys override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { val ifdsAnalysis = analysis.asInstanceOf[AbstractIFDSAnalysis[IFDSFact]] - for (e ← ifdsAnalysis.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } + for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } } override def afterPhaseCompletion( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala index d80c40e8a7..d85f207f5e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala @@ -29,24 +29,6 @@ import org.opalj.tac.ReturnValue import org.opalj.tac.TACMethodParameter import org.opalj.tac.TACode -/** - * A data flow fact, that was created by an unbalanced return. - * - * @tparam FactType The type of flow facts, which are tracked by the concrete analysis. - */ -trait UnbalancedReturnFact[FactType] extends AbstractIFDSFact { - - /** - * The index of the call, which creates the inner fact. - */ - val index: Int - - /** - * The fact, which will hold after `index`. - */ - val innerFact: FactType -} - /** * An IFDS analysis, which analyzes the code against the control flow direction. * @@ -54,26 +36,7 @@ trait UnbalancedReturnFact[FactType] extends AbstractIFDSFact { * concrete analysis. * @author Mario Trageser */ -abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]] extends AbstractIFDSAnalysis[IFDSFact] { - - /** - * Called, when the entry point of the analyzed method is reached. - * Creates unbalanced return facts for the callers. - * - * @param facts The facts, which hold at the entry point of the analyzed method. - * @param call A call, which calls the analyzed method. - * @param caller The method, invoking the `call`. - * @param source The entity, which is currently analyzed. - * @return Unbalanced return facts, that hold after `call` under the assumption, that `facts` - * held at the entry point of the analyzed method. - */ - protected def unbalancedReturnFlow( - facts: Set[IFDSFact], - call: Statement, - caller: DeclaredMethod, - source: (DeclaredMethod, IFDSFact) - ): Set[UnbalancedIFDSFact] - +abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement] with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem) { /** * If this method is analyzed for an unbalanced return fact, the single star block is the block, * which contains the call. @@ -94,11 +57,11 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS /** * Collects the output facts of the entry point of the analyzed method. */ - override protected def collectResult(implicit state: State): Map[Statement, Set[IFDSFact]] = { + override protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { val startBlock = state.cfg.startBlock val startPC = startBlock.startPC val statement = - Statement(state.method, startBlock, state.code(startPC), startPC, state.code, state.cfg) + JavaStatement(state.method, startBlock, state.code(startPC), startPC, state.code, state.cfg) val exitFacts = state.outgoingFacts.get(startBlock).flatMap(_.get(SyntheticStartNode)) if (exitFacts.isDefined) Map(statement -> exitFacts.get) else Map.empty @@ -177,7 +140,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS state: State ): Unit = { var newIn = if (oldIn.isDefined) notSubsumedBy(nextIn, oldIn.get, project) else nextIn - val created = createFactsAtStartNode(newIn, state.source) + val created = ifdsProblem.createFactsAtStartNode(newIn, state.source) if (created.nonEmpty) { // Add the created facts to newIn and update the outgoing facts in the state. newIn = newIn ++ created @@ -187,7 +150,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS state.outgoingFacts = state.outgoingFacts.updated(startBlock, newOut) } // Only create unbalanced returns, if we are at an entry point or in a (indirect) caller of it. - if (shouldPerformUnbalancedReturn(state.source)) { + if (ifdsProblem.shouldPerformUnbalancedReturn(state.source)) { // Get all callers of this method propertyStore(state.source._1, Callers.key) match { case FinalEP(_, p: Callers) ⇒ @@ -221,21 +184,6 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS } } - /** - * Called, when the analysis found new output facts at a method's start block. - * A concrete analysis may overwrite this method to create additional facts, which will be added - * to the analysis' result. - * - * @param in The new output facts at the start node. - * @param source The entity, which is analyzed. - * @return Nothing by default. - */ - protected def createFactsAtStartNode( - in: Set[IFDSFact], - source: (DeclaredMethod, IFDSFact) - ): Set[IFDSFact] = - Set.empty - /** * If the node is a basic block, it will be returned. * Otherwise, all predecessors of the node will be returned. @@ -264,13 +212,13 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * If it is a catch node, the first statement of its throwing block will be returned. * If it is a synthetic start node, an artificial statement without code will be returned. */ - override protected def firstStatement(node: CFGNode)(implicit state: State): Statement = { + override protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement = { if (node.isBasicBlock) { val index = node.asBasicBlock.endPC - Statement(state.method, node, state.code(index), index, state.code, state.cfg) + JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg) } else if (node.isCatchNode) firstStatement(node.successors.head) else if (node == SyntheticStartNode) - Statement(state.method, node, null, 0, state.code, state.cfg) + JavaStatement(state.method, node, null, 0, state.code, state.cfg) else throw new IllegalArgumentException(s"Unknown node type: $node") } @@ -278,8 +226,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * The successor statements against the control flow direction. */ override protected def nextStatements( - statement: Statement - )(implicit state: State): Set[Statement] = { + statement: JavaStatement + )(implicit state: State): Set[JavaStatement] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == 0) { @@ -289,7 +237,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS else { val nextIndex = index - 1 Set( - Statement( + JavaStatement( statement.method, basicBlock, statement.code(nextIndex), @@ -306,7 +254,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * direction, which my be executed after them. Calls returnFlow on those pairs. */ override protected def callToStartFacts( - call: Statement, + call: JavaStatement, callee: DeclaredMethod, in: Set[IFDSFact] )(implicit state: State): Set[IFDSFact] = { @@ -323,7 +271,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val calleeStmts = tac.stmts val exitStmt = calleeStmts(exitPc) val exitStatement = - Statement(definedCallee, cfg.bb(exitPc), exitStmt, exitPc, calleeStmts, cfg) + JavaStatement(definedCallee, cfg.bb(exitPc), exitStmt, exitPc, calleeStmts, cfg) for { successor ← successors if !AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW || @@ -334,7 +282,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS } { numberOfCalls.returnFlow += 1 sumOfInputfactsForCallbacks += in.size - flow ++= returnFlow(call, callee, exitStatement, successor._1, in) + flow ++= ifdsProblem.returnFlow(call, callee, exitStatement, successor._1, in) } } flow.toSet @@ -355,18 +303,18 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * edges. exitFacts should at most contain the callee's entry point. */ override protected def addExitToReturnFacts( - summaryEdges: Map[Statement, Set[IFDSFact]], - successors: Set[Statement], - call: Statement, + summaryEdges: Map[JavaStatement, Set[IFDSFact]], + successors: Set[JavaStatement], + call: JavaStatement, callee: DeclaredMethod, - exitFacts: Map[Statement, Set[IFDSFact]] - )(implicit state: State): Map[Statement, Set[IFDSFact]] = { + exitFacts: Map[JavaStatement, Set[IFDSFact]] + )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { var result = summaryEdges if (exitFacts.nonEmpty) { val in = exitFacts.head._2 numberOfCalls.callFlow += 1 sumOfInputfactsForCallbacks += in.size - val exitToReturnFacts = callFlow(call, callee, in, state.source) + val exitToReturnFacts = ifdsProblem.callFlow(call, callee, in, state.source) successors.foreach { successor ⇒ val updatedValue = result.get(successor) match { case Some(facts) ⇒ @@ -383,22 +331,13 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS /** * If there is an unbalanced return fact for this call, it will be replaced by its inner fact. */ - override protected def beforeHandleCall(call: Statement, in: Set[IFDSFact]): Set[IFDSFact] = + override protected def beforeHandleCall(call: JavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = in.map { case unbalancedFact: UnbalancedReturnFact[IFDSFact] if unbalancedFact.index == call.index ⇒ unbalancedFact.innerFact case fact ⇒ fact } - /** - * Checks for the analyzed entity, if an unbalanced return should be performed. - * - * @param source The analyzed entity. - * @return False, if no unbalanced return should be performed. - */ - protected def shouldPerformUnbalancedReturn(source: (DeclaredMethod, IFDSFact)): Boolean = - source._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]] || entryPoints.contains(source) - /** * Determines the predecessor statements, i.e. the successor statements in the control flow * direction. They are mapped to the corresponding predecessor node. When determining the node, @@ -409,8 +348,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * @return A map, mapping from a predecessor statement to the corresponding node. */ private def predecessorStatementsWithNode( - statement: Statement - )(implicit state: State): Map[Statement, CFGNode] = { + statement: JavaStatement + )(implicit state: State): Map[JavaStatement, CFGNode] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == basicBlock.endPC) @@ -420,7 +359,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS else { val nextIndex = index + 1 Map( - Statement( + JavaStatement( statement.method, basicBlock, statement.code(nextIndex), @@ -442,12 +381,12 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * * @return The last statement of `node`. */ - @tailrec private def lastStatement(node: CFGNode)(implicit state: State): Statement = { + @tailrec private def lastStatement(node: CFGNode)(implicit state: State): JavaStatement = { if (node.isBasicBlock) { val index = node.asBasicBlock.startPC - Statement(state.method, node, state.code(index), index, state.code, state.cfg) + JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg) } else if (node.isCatchNode) lastStatement(node.successors.head) - else if (node.isExitNode) Statement(state.method, node, null, 0, state.code, state.cfg) + else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg) else throw new IllegalArgumentException(s"Unknown node type: $node") } @@ -467,7 +406,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS )(implicit state: State): Unit = { val callerStmts = tac.stmts val callerCfg = tac.cfg - val call = Statement( + val call = JavaStatement( caller.definedMethod, callerCfg.bb(callIndex), callerStmts(callIndex), @@ -475,7 +414,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS callerStmts, callerCfg ) - unbalancedReturnFlow(in, call, caller, state.source).foreach { in ⇒ + ifdsProblem.unbalancedReturnFlow(in, call, caller, state.source).foreach { in ⇒ val callerEntity = (caller, in) /* * Add the caller with the unbalanced return fact as a dependency to diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala index 1bad8d01a3..fbd86164d0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala @@ -16,7 +16,7 @@ import org.opalj.tac.TACStmts * * @author Mario Trageser */ -abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends AbstractIFDSAnalysis[IFDSFact] { +abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem) { /** * The analysis starts at the entry block. @@ -30,7 +30,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends Abstrac /** * Collects the output facts at the predecessors of the normal and abnormal return node. */ - override protected def collectResult(implicit state: State): Map[Statement, Set[IFDSFact]] = { + override protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { mergeMaps( resultOfExitNode(state.cfg.normalReturnNode), resultOfExitNode(state.cfg.abnormalReturnNode) @@ -79,26 +79,26 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends Abstrac * If it is a catch node, the first statement of its handler will be returned. * If it is an exit node, an artificial statement without code will be returned. */ - override protected def firstStatement(node: CFGNode)(implicit state: State): Statement = { + override protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement = { if (node.isBasicBlock) { val index = node.asBasicBlock.startPC - Statement(state.method, node, state.code(index), index, state.code, state.cfg) + JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg) } else if (node.isCatchNode) firstStatement(node.successors.head) - else if (node.isExitNode) Statement(state.method, node, null, 0, state.code, state.cfg) + else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg) else throw new IllegalArgumentException(s"Unknown node type: $node") } /** * The successor statements in the direction of the control flow. */ - override protected def nextStatements(statement: Statement)(implicit state: State): Set[Statement] = { + override protected def nextStatements(statement: JavaStatement)(implicit state: State): Set[JavaStatement] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == basicBlock.endPC) basicBlock.successors.map(firstStatement(_)) else { val nextIndex = index + 1 - Set(Statement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + Set(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, statement.code, statement.cfg)) } } @@ -106,11 +106,11 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends Abstrac /** * Calls callFlow. */ - override protected def callToStartFacts(call: Statement, callee: DeclaredMethod, + override protected def callToStartFacts(call: JavaStatement, callee: DeclaredMethod, in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { numberOfCalls.callFlow += 1 sumOfInputfactsForCallbacks += in.size - callFlow(call, callee, in, state.source) + ifdsProblem.callFlow(call, callee, in, state.source) } /** @@ -118,11 +118,11 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends Abstrac * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. */ override protected def addExitToReturnFacts( - summaryEdges: Map[Statement, Set[IFDSFact]], - successors: Set[Statement], call: Statement, + summaryEdges: Map[JavaStatement, Set[IFDSFact]], + successors: Set[JavaStatement], call: JavaStatement, callee: DeclaredMethod, - exitFacts: Map[Statement, Set[IFDSFact]] - )(implicit state: State): Map[Statement, Set[IFDSFact]] = { + exitFacts: Map[JavaStatement, Set[IFDSFact]] + )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { // First process for normal returns, then abnormal returns. var result = summaryEdges if (AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { @@ -149,7 +149,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends Abstrac * Like nextStatements, but maps each successor statement to the corresponding successor node. * When determining the successor node, catch nodes are not skipped. */ - private def nextStatementsWithNode(statement: Statement)(implicit state: State): Map[Statement, CFGNode] = { + private def nextStatementsWithNode(statement: JavaStatement)(implicit state: State): Map[JavaStatement, CFGNode] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == basicBlock.endPC) @@ -157,7 +157,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends Abstrac .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap else { val nextIndex = index + 1 - Map(Statement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + Map(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, statement.code, statement.cfg) → basicBlock) } } @@ -172,15 +172,15 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends Abstrac private def resultOfExitNode(exit: CFGNode)( implicit state: State - ): Map[Statement, Set[IFDSFact]] = { - var result = Map.empty[Statement, Set[IFDSFact]] + ): Map[JavaStatement, Set[IFDSFact]] = { + var result = Map.empty[JavaStatement, Set[IFDSFact]] exit.predecessors foreach { predecessor ⇒ if (predecessor.isBasicBlock) { val basicBlock = predecessor.asBasicBlock val exitFacts = state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)) if (exitFacts.isDefined) { val lastIndex = basicBlock.endPC - val stmt = Statement(state.method, basicBlock, state.code(lastIndex), + val stmt = JavaStatement(state.method, basicBlock, state.code(lastIndex), lastIndex, state.code, state.cfg) result += stmt → exitFacts.get } @@ -202,14 +202,14 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends Abstrac * found exit facts. * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. */ - private def addSummaryEdge(summaryEdges: Map[Statement, Set[IFDSFact]], call: Statement, - exitStatement: Statement, successor: Statement, + private def addSummaryEdge(summaryEdges: Map[JavaStatement, Set[IFDSFact]], call: JavaStatement, + exitStatement: JavaStatement, successor: JavaStatement, callee: DeclaredMethod, - allNewExitFacts: Map[Statement, Set[IFDSFact]]): Map[Statement, Set[IFDSFact]] = { + allNewExitFacts: Map[JavaStatement, Set[IFDSFact]]): Map[JavaStatement, Set[IFDSFact]] = { val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) numberOfCalls.returnFlow += 1 sumOfInputfactsForCallbacks += in.size - val returned = returnFlow(call, callee, exitStatement, successor, in) + val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) val newFacts = if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { val summaryForSuccessor = summaryEdges(successor) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala index 0d630dc59f..c09ecf20cd 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -1,85 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds -import scala.annotation.tailrec - import java.io.File import java.io.PrintWriter -import org.opalj.collection.immutable.EmptyIntTrieSet import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.PropertyBounds -import org.opalj.value.ValueInformation + import org.opalj.br.analyses.SomeProject -import org.opalj.br.DeclaredMethod -import org.opalj.br.ReferenceType -import org.opalj.br.ArrayType -import org.opalj.br.ClassFile -import org.opalj.br.FieldType -import org.opalj.br.Method + import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.IFDSProperty import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation -import org.opalj.tac.ArrayLoad -import org.opalj.tac.ArrayStore -import org.opalj.tac.Assignment -import org.opalj.tac.DUVar -import org.opalj.tac.Expr -import org.opalj.tac.GetField -import org.opalj.tac.GetStatic -import org.opalj.tac.New -import org.opalj.tac.ReturnValue -import org.opalj.tac.Var import org.opalj.tac.fpcf.properties.TACAI -trait VTAFact extends SubsumableFact -case object VTANullFact extends VTAFact with SubsumableNullFact - -/** - * A possible run time type of a variable. - * - * @param definedBy The variable's definition site. - * @param t The variable's type. - * @param upperBound True, if the variable's type could also be every subtype of `t`. - */ -case class VariableType(definedBy: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { - - /** - * If this VariableType is an upper bound, it subsumes every subtype. - */ - override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { - if (upperBound) other match { - case VariableType(definedByOther, tOther, _) if definedBy == definedByOther && t.isObjectType && tOther.isObjectType ⇒ - project.classHierarchy.isSubtypeOf(tOther.asObjectType, t.asObjectType) - case _ ⇒ false - } - else false - } -} - -/** - * A possible run time type of the receiver of a call. - * - * @param line The line of the call. - * @param t The callee's type. - * @param upperBound True, if the callee's type could also be every subtype of `t`. - */ -case class CalleeType(line: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { - - /** - * If this CalleeType is an upper bound, it subsumes every subtype. - */ - override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { - if (upperBound) other match { - case CalleeType(lineOther, tOther, _) if line == lineOther && t.isObjectType && tOther.isObjectType ⇒ - tOther.asObjectType.isSubtypeOf(t.asObjectType)(project.classHierarchy) - case _ ⇒ false - } - else false - } -} - /** * A variable type analysis implemented as an IFDS analysis. * In contrast to an ordinary variable type analysis, which also determines types of fields, @@ -89,292 +24,16 @@ case class CalleeType(line: Int, t: ReferenceType, upperBound: Boolean) extends * @param project The analyzed project. * @author Mario Trageser */ -class IFDSBasedVariableTypeAnalysis private (implicit val project: SomeProject) - extends ForwardIFDSAnalysis[VTAFact] { +class IFDSBasedVariableTypeAnalysis private (ifdsProblem: VariableTypeProblem)(implicit val project: SomeProject) + extends ForwardIFDSAnalysis[VTAFact](ifdsProblem) { override val propertyKey: IFDSPropertyMetaInformation[VTAFact] = VTAResult - /** - * The analysis starts with all public methods in java.lang or org.opalj. - */ - override def entryPoints: Seq[(DeclaredMethod, VTAFact)] = { - p.allProjectClassFiles - .filter(classInsideAnalysisContext) - .flatMap(classFile ⇒ classFile.methods) - .filter(isEntryPoint) - .map(method ⇒ declaredMethods(method)) - .flatMap(entryPointsForMethod) - } - - override protected def nullFact: VTAFact = VTANullFact - override protected def createPropertyValue( - result: Map[Statement, Set[VTAFact]] + result: Map[JavaStatement, Set[VTAFact]] ): IFDSProperty[VTAFact] = new VTAResult(result) - /** - * If a new object is instantiated and assigned to a variable or array, a new ValueType will be - * created for the assignment's target. - * If there is an assignment of a variable or array element, a new VariableType will be - * created for the assignment's target with the source's type. - * If there is a field read, a new VariableType will be created with the field's declared type. - */ - override protected def normalFlow( - statement: Statement, - successor: Statement, - in: Set[VTAFact] - ): Set[VTAFact] = { - val stmt = statement.stmt - stmt.astID match { - case Assignment.ASTID ⇒ - // Add facts for the assigned variable. - in ++ newFacts(statement.method, statement.stmt.asAssignment.expr, statement.index, in) - case ArrayStore.ASTID ⇒ - /* - * Add facts for the array store, like it was a variable assignment. - * By doing so, we only want to get the variable's type. - * Then, we change the definedBy-index to the one of the array and wrap the variable's - * type with an array type. - * Note, that an array type may have at most 255 dimensions. - */ - val flow = scala.collection.mutable.Set.empty[VTAFact] - flow ++= in - newFacts(statement.method, stmt.asArrayStore.value, statement.index, in).foreach { - case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) ⇒ - stmt.asArrayStore.arrayRef.asVar.definedBy - .foreach(flow += VariableType(_, ArrayType(t), upperBound)) - case _ ⇒ // Nothing to do - } - flow.toSet - // If the statement is neither an assignment, nor an array store, we just propagate our facts. - case _ ⇒ in - } - } - - /** - * For each variable, which can be passed as an argument to the call, a new VariableType is - * created for the callee context. - */ - override protected def callFlow( - call: Statement, - callee: DeclaredMethod, - in: Set[VTAFact], - source: (DeclaredMethod, VTAFact) - ): Set[VTAFact] = { - val callObject = asCall(call.stmt) - val allParams = callObject.allParams - // Iterate over all input facts and over all parameters of the call. - val flow = scala.collection.mutable.Set.empty[VTAFact] - in.foreach { - case VariableType(definedBy, t, upperBound) ⇒ - allParams.iterator.zipWithIndex.foreach { - /* - * We are only interested in a pair of a variable type and a parameter, if the - * variable and the parameter can refer to the same object. - */ - case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ - // If this is the case, create a new fact for the method's formal parameter. - flow += VariableType( - AbstractIFDSAnalysis - .switchParamAndVariableIndex(parameterIndex, callee.definedMethod.isStatic), - t, - upperBound - ) - case _ ⇒ // Nothing to do - } - case _ ⇒ // Nothing to do - } - flow.toSet - } - - /** - * If the call is an instance call, new CalleeTypes will be created for the call, one for each - * VariableType, which could be the call's target. - */ - override protected def callToReturnFlow( - call: Statement, - successor: Statement, - in: Set[VTAFact], - source: (DeclaredMethod, VTAFact) - ): Set[VTAFact] = { - // Check, to which variables the callee may refer - val calleeDefinitionSites = asCall(call.stmt).receiverOption - .map(callee ⇒ callee.asVar.definedBy) - .getOrElse(EmptyIntTrieSet) - val calleeTypeFacts = in.collect { - // If we know the variable's type, we also know on which type the call is performed. - case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) ⇒ - CalleeType(call.index, t, upperBound) - } - if (in.size >= calleeTypeFacts.size) in ++ calleeTypeFacts - else calleeTypeFacts ++ in - } - - /** - * If the call returns a value which is assigned to a variable, a new VariableType will be - * created in the caller context with the returned variable's type. - */ - override protected def returnFlow( - call: Statement, - callee: DeclaredMethod, - exit: Statement, - successor: Statement, - in: Set[VTAFact] - ): Set[VTAFact] = - // We only create a new fact, if the call returns a value, which is assigned to a variable. - if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { - val returnValue = exit.stmt.asReturnValue.expr.asVar - in.collect { - // If we know the type of the return value, we create a fact for the assigned variable. - case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) ⇒ - VariableType(call.index, t, upperBound) - } - } else Set.empty - - /** - * If a method outside of the analysis context is called, we assume that it returns every - * possible runtime type matching the compile time type. - */ - override protected def callOutsideOfAnalysisContext( - statement: Statement, - callee: DeclaredMethod, - successor: Statement, - in: Set[VTAFact] - ): Set[VTAFact] = { - val returnType = callee.descriptor.returnType - if (statement.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { - Set(VariableType(statement.index, returnType.asReferenceType, upperBound = true)) - } else Set.empty - } - - /** - * Only methods in java.lang and org.opalj are inside the analysis context. - * - * @param callee The callee. - * @return True, if the callee is inside the analysis context. - */ - override protected def insideAnalysisContext(callee: DeclaredMethod): Boolean = - classInsideAnalysisContext(callee.definedMethod.classFile) && - super.insideAnalysisContext(callee) - - /** - * Checks, if a class is inside the analysis context. - * By default, that are the packages java.lang and org.opalj. - * - * @param classFile The class, which is checked. - * @return True, if the class is inside the analysis context. - */ - private def classInsideAnalysisContext(classFile: ClassFile): Boolean = { - val fqn = classFile.fqn - fqn.startsWith("java/lang") || fqn.startsWith("org/opalj/fpcf/fixtures/vta") - } - - /** - * Checks, if a method is an entry point of this analysis. - * - * @param method The method to be checked. - * @return True, if this method is an entry point of the analysis. - */ - private def isEntryPoint(method: Method): Boolean = { - val declaredMethod = declaredMethods(method) - method.body.isDefined && canBeCalledFromOutside(declaredMethod) - } - - /** - * For an entry point method, this method computes all pairs (`method`, inputFact) where - * inputFact is a VariableType for one of the method's parameter with its compile time type as - * an upper bound. - * - * @param method The entry point method. - * - * @return All pairs (`method`, inputFact) where inputFact is a VariableType for one of the - * method's parameter with its compile time type as an upper bound. - */ - private def entryPointsForMethod(method: DeclaredMethod): Seq[(DeclaredMethod, VTAFact)] = { - // Iterate over all parameters, which have a reference type. - (method.descriptor.parameterTypes.zipWithIndex.collect { - case (t, index) if t.isReferenceType ⇒ - /* - * Create a fact for the parameter, which says, that the parameter may have any - * subtype of its compile time type. - */ - VariableType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, method.definedMethod.isStatic), - t.asReferenceType, - upperBound = true - ) - /* - * In IFDS problems, we must always also analyze the null fact, because it creates the facts, - * which hold independently of other source facts. - * Map the input facts, in which we are interested, to a pair of the method and the fact. - */ - } :+ VTANullFact).map(fact ⇒ (method, fact)) - } - - /** - * When `normalFlow` reaches an assignment or array store, this method computes the new facts - * created by the statement. - * - * @param expression The source expression of the assignment or array store. - * @param statementIndex The statement's index. - * @param in The facts, which hold before the statement. - * @return The new facts created by the statement. - */ - private def newFacts( - method: Method, - expression: Expr[DUVar[ValueInformation]], - statementIndex: Int, - in: Set[VTAFact] - ): Iterator[VariableType] = expression.astID match { - case New.ASTID ⇒ - in.iterator.collect { - // When a constructor is called, we always know the exact type. - case VTANullFact ⇒ - VariableType(statementIndex, expression.asNew.tpe, upperBound = false) - } - case Var.ASTID ⇒ - in.iterator.collect { - // When we know the source type, we also know the type of the assigned variable. - case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ - VariableType(statementIndex, t, upperBound) - } - case ArrayLoad.ASTID ⇒ - in.iterator.collect { - // When we know the array's type, we also know the type of the loaded element. - case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && - expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ - VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound) - } - case GetField.ASTID | GetStatic.ASTID ⇒ - val t = expression.asFieldRead.declaredFieldType - /* - * We do not track field types. So we must assume, that it contains any subtype of its - * compile time type. - */ - if (t.isReferenceType) - Iterator(VariableType(statementIndex, t.asReferenceType, upperBound = true)) - else Iterator.empty - case _ ⇒ Iterator.empty - } - - /** - * Checks, if some type is an array type containing an object type. - * - * @param t The type to be checked. - * @param includeObjectType If true, this method also returns true if `t` is an object type - * itself. - * - * @return True, if `t` is an array type of an object type. - */ - @tailrec private def isArrayOfObjectType( - t: FieldType, - includeObjectType: Boolean = false - ): Boolean = { - if (t.isArrayType) isArrayOfObjectType(t.asArrayType.elementType, includeObjectType = true) - else if (t.isObjectType && includeObjectType) true - else false - } } object IFDSBasedVariableTypeAnalysis extends IFDSAnalysis[VTAFact] { @@ -385,8 +44,8 @@ object IFDSBasedVariableTypeAnalysis extends IFDSAnalysis[VTAFact] { Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) override def init(p: SomeProject, ps: PropertyStore): IFDSBasedVariableTypeAnalysis = { - if (SUBSUMING) new IFDSBasedVariableTypeAnalysis()(p) with Subsuming[VTAFact] - else new IFDSBasedVariableTypeAnalysis()(p) + if (SUBSUMING) new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p))(p) with Subsuming[VTAFact] + else new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p))(p) } override def property: IFDSPropertyMetaInformation[VTAFact] = VTAResult @@ -395,7 +54,7 @@ object IFDSBasedVariableTypeAnalysis extends IFDSAnalysis[VTAFact] { /** * The IFDSProperty for this analysis. */ -case class VTAResult(flows: Map[Statement, Set[VTAFact]]) extends IFDSProperty[VTAFact] { +case class VTAResult(flows: Map[JavaStatement, Set[VTAFact]]) extends IFDSProperty[VTAFact] { override type Self = VTAResult diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala new file mode 100644 index 0000000000..f6145d9cd8 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala @@ -0,0 +1,205 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.analyses.SomeProject + +/** + * The supertype of all IFDS facts. + */ +trait AbstractIFDSFact + +/** + * The super type of all null facts. + */ +trait AbstractIFDSNullFact extends AbstractIFDSFact + +/** + * A framework for IFDS analyses. + * + * @tparam IFDSFact The type of flow facts, which are tracked by the concrete analysis. + * @author Dominik Helm + * @author Mario Trageser + */ +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](project: SomeProject) { + /** + * The null fact of this analysis. + */ + def nullFact: IFDSFact + + /** + * The entry points of this analysis. + */ + def entryPoints: Seq[(Method, IFDSFact)] + + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param successor The successor of the analyzed `statement`, for which the data flow shall be + * computed. + * @param in The facts, which hold before the execution of the `statement`. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + def normalFlow( + statement: Statement, + successor: Statement, + in: Set[IFDSFact] + ): Set[IFDSFact] + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The facts, which hold before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + def callFlow( + call: Statement, + callee: Method, + in: Set[IFDSFact], + source: (Method, IFDSFact) + ): Set[IFDSFact] + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param callee The method called by `call`, for which the data flow shall be computed. + * @param exit The statement, which terminated the `calle`. + * @param successor The statement of the caller, which will be executed after the `callee` + * returned. + * @param in The facts, which hold before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + def returnFlow( + call: Statement, + callee: Method, + exit: Statement, + successor: Statement, + in: Set[IFDSFact] + ): Set[IFDSFact] + + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param successor The statement, which will be executed after the call. + * @param in The facts, which hold before the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + def callToReturnFlow( + call: Statement, + successor: Statement, + in: Set[IFDSFact], + source: (Method, IFDSFact) + ): Set[IFDSFact] + + /** + * Checks, if a callee is inside this analysis' context. + * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. + * By default, native methods are not inside the analysis context. + * + * @param callee The callee. + * @return True, if the callee is inside the analysis context. + */ + def insideAnalysisContext(callee: Method): Boolean + + /** + * When a callee outside of this analysis' context is called, this method computes the summary + * edge for the call. + * + * @param call The statement, which invoked the call. + * @param callee The method called by `call`. + * @param successor The statement, which will be executed after the call. + * @param in The facts facts, which hold before the `call`. + * @return The facts, which hold after the call, excluding the call to return flow. + */ + def callOutsideOfAnalysisContext( + call: Statement, + callee: Method, + successor: Statement, + in: Set[IFDSFact] + ): Set[IFDSFact] + + /** + * Gets the set of all methods directly callable at some call statement. + * + * @param statement The call statement. + * @param caller The caller, performing the call. + * @return All methods directly callable at the statement index. + */ + def getCallees( + statement: Statement, + caller: Method + ): Iterator[Method] +} + +/** + * A data flow fact, that was created by an unbalanced return. + * + * @tparam FactType The type of flow facts, which are tracked by the concrete analysis. + */ +trait UnbalancedReturnFact[FactType] extends AbstractIFDSFact { + + /** + * The index of the call, which creates the inner fact. + */ + val index: Int + + /** + * The fact, which will hold after `index`. + */ + val innerFact: FactType +} + +trait BackwardIFDSProblem[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact], Method, Statement] { + /** + * Checks for the analyzed entity, if an unbalanced return should be performed. + * + * @param source The analyzed entity. + * @return False, if no unbalanced return should be performed. + */ + def shouldPerformUnbalancedReturn(source: (Method, IFDSFact)): Boolean + + /** + * Called, when the entry point of the analyzed method is reached. + * Creates unbalanced return facts for the callers. + * + * @param facts The facts, which hold at the entry point of the analyzed method. + * @param call A call, which calls the analyzed method. + * @param caller The method, invoking the `call`. + * @param source The entity, which is currently analyzed. + * @return Unbalanced return facts, that hold after `call` under the assumption, that `facts` + * held at the entry point of the analyzed method. + */ + def unbalancedReturnFlow( + facts: Set[IFDSFact], + call: Statement, + caller: Method, + source: (Method, IFDSFact) + ): Set[UnbalancedIFDSFact] + + /** + * Called, when the analysis found new output facts at a method's start block. + * A concrete analysis may overwrite this method to create additional facts, which will be added + * to the analysis' result. + * + * @param in The new output facts at the start node. + * @param source The entity, which is analyzed. + * @return Nothing by default. + */ + def createFactsAtStartNode( + in: Set[IFDSFact], + source: (Method, IFDSFact) + ): Set[IFDSFact] = + Set.empty +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala new file mode 100644 index 0000000000..f757b8f557 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -0,0 +1,93 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.{DeclaredMethod} +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.{FinalEP, PropertyStore} +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.fpcf.analyses.cg.TypeProvider +import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt} +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.properties.cg.{Callees, Callers} + +abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, DeclaredMethod, JavaStatement](project) { + /** + * All declared methods in the project. + */ + implicit final protected val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + + final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + implicit final protected val typeProvider: TypeProvider = project.get(TypeProviderKey) + + /** + * Checks, if a callee is inside this analysis' context. + * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. + * By default, native methods are not inside the analysis context. + * + * @param callee The callee. + * @return True, if the callee is inside the analysis context. + */ + override def insideAnalysisContext(callee: DeclaredMethod): Boolean = + callee.definedMethod.body.isDefined + + override def getCallees( + statement: JavaStatement, + caller: DeclaredMethod + ): Iterator[DeclaredMethod] = { + val pc = statement.code(statement.index).pc + val ep = propertyStore(caller, Callees.key) + ep match { + case FinalEP(_, p) ⇒ + p.directCallees(typeProvider.newContext(caller), pc).map(_.method) + case _ ⇒ + throw new IllegalStateException( + "call graph mut be computed before the analysis starts" + ) + } + } + + /** + * Returns all methods, that can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @return All methods, that can be called from outside the library. + */ + protected def methodsCallableFromOutside: Set[DeclaredMethod] = + declaredMethods.declaredMethods.filter(canBeCalledFromOutside).toSet + + /** + * Checks, if some `method` can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @param method The method, which may be callable from outside. + * @return True, if `method` can be called from outside the library. + */ + protected def canBeCalledFromOutside(method: DeclaredMethod): Boolean = { + val FinalEP(_, callers) = propertyStore(method, Callers.key) + callers.hasCallersWithUnknownContext + } + + /** + * Gets the call object for a statement that contains a call. + * + * @param call The call statement. + * @return The call object for `call`. + */ + protected def asCall(call: Stmt[V]): Call[V] = call.astID match { + case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall + case _ ⇒ call.asMethodCall + } +} + +abstract class JavaBackwardIFDSProblem[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](project: SomeProject) extends JavaIFDSProblem[IFDSFact](project) with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement] { + /** + * Checks for the analyzed entity, if an unbalanced return should be performed. + * + * @param source The analyzed entity. + * @return False, if no unbalanced return should be performed. + */ + def shouldPerformUnbalancedReturn(source: (DeclaredMethod, IFDSFact)): Boolean = + source._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]] || entryPoints.contains(source) +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala index 9650d12f28..afffc3a991 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala @@ -77,10 +77,10 @@ trait Subsumable[IFDSFact <: AbstractIFDSFact] { * `oldExitFacts` are not present. */ protected def filterNewInformation[T <: IFDSFact]( - newExitFacts: Map[Statement, Set[T]], - oldExitFacts: Map[Statement, Set[T]], + newExitFacts: Map[JavaStatement, Set[T]], + oldExitFacts: Map[JavaStatement, Set[T]], project: SomeProject - ): Map[Statement, Set[T]] = { + ): Map[JavaStatement, Set[T]] = { var result = newExitFacts for ((key, values) ← oldExitFacts) { result = result.updated(key, result(key) -- values) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala index 86c6f6866b..d61b9208ec 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala @@ -43,9 +43,9 @@ trait Subsuming[IFDSFact <: SubsumableFact] extends Subsumable[IFDSFact] { * Considers subsuming. */ override protected def filterNewInformation[T <: IFDSFact]( - newExitFacts: Map[Statement, Set[T]], - oldExitFacts: Map[Statement, Set[T]], project: SomeProject - ): Map[Statement, Set[T]] = + newExitFacts: Map[JavaStatement, Set[T]], + oldExitFacts: Map[JavaStatement, Set[T]], project: SomeProject + ): Map[JavaStatement, Set[T]] = newExitFacts.keys.map { statement ⇒ val old = oldExitFacts.get(statement) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala new file mode 100644 index 0000000000..d7be114623 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala @@ -0,0 +1,336 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.{ArrayType, ClassFile, DeclaredMethod, FieldType, Method, ReferenceType} +import org.opalj.br.analyses.SomeProject +import org.opalj.collection.immutable.EmptyIntTrieSet +import org.opalj.tac.{ArrayLoad, ArrayStore, Assignment, DUVar, Expr, GetField, GetStatic, New, ReturnValue, Var} +import org.opalj.value.ValueInformation + +import scala.annotation.tailrec + +trait VTAFact extends SubsumableFact +case object VTANullFact extends VTAFact with SubsumableNullFact + +/** + * A possible run time type of a variable. + * + * @param definedBy The variable's definition site. + * @param t The variable's type. + * @param upperBound True, if the variable's type could also be every subtype of `t`. + */ +case class VariableType(definedBy: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { + + /** + * If this VariableType is an upper bound, it subsumes every subtype. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { + if (upperBound) other match { + case VariableType(definedByOther, tOther, _) if definedBy == definedByOther && t.isObjectType && tOther.isObjectType ⇒ + project.classHierarchy.isSubtypeOf(tOther.asObjectType, t.asObjectType) + case _ ⇒ false + } + else false + } +} + +/** + * A possible run time type of the receiver of a call. + * + * @param line The line of the call. + * @param t The callee's type. + * @param upperBound True, if the callee's type could also be every subtype of `t`. + */ +case class CalleeType(line: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { + + /** + * If this CalleeType is an upper bound, it subsumes every subtype. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { + if (upperBound) other match { + case CalleeType(lineOther, tOther, _) if line == lineOther && t.isObjectType && tOther.isObjectType ⇒ + tOther.asObjectType.isSubtypeOf(t.asObjectType)(project.classHierarchy) + case _ ⇒ false + } + else false + } +} + +class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact](project) { + override def nullFact: VTAFact = VTANullFact + + /** + * The analysis starts with all public methods in java.lang or org.opalj. + */ + override def entryPoints: Seq[(DeclaredMethod, VTAFact)] = { + project.allProjectClassFiles + .filter(classInsideAnalysisContext) + .flatMap(classFile ⇒ classFile.methods) + .filter(isEntryPoint) + .map(method ⇒ declaredMethods(method)) + .flatMap(entryPointsForMethod) + } + + /** + * If a new object is instantiated and assigned to a variable or array, a new ValueType will be + * created for the assignment's target. + * If there is an assignment of a variable or array element, a new VariableType will be + * created for the assignment's target with the source's type. + * If there is a field read, a new VariableType will be created with the field's declared type. + */ + override def normalFlow( + statement: JavaStatement, + successor: JavaStatement, + in: Set[VTAFact] + ): Set[VTAFact] = { + val stmt = statement.stmt + stmt.astID match { + case Assignment.ASTID ⇒ + // Add facts for the assigned variable. + in ++ newFacts(statement.method, statement.stmt.asAssignment.expr, statement.index, in) + case ArrayStore.ASTID ⇒ + /* + * Add facts for the array store, like it was a variable assignment. + * By doing so, we only want to get the variable's type. + * Then, we change the definedBy-index to the one of the array and wrap the variable's + * type with an array type. + * Note, that an array type may have at most 255 dimensions. + */ + val flow = scala.collection.mutable.Set.empty[VTAFact] + flow ++= in + newFacts(statement.method, stmt.asArrayStore.value, statement.index, in).foreach { + case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) ⇒ + stmt.asArrayStore.arrayRef.asVar.definedBy + .foreach(flow += VariableType(_, ArrayType(t), upperBound)) + case _ ⇒ // Nothing to do + } + flow.toSet + // If the statement is neither an assignment, nor an array store, we just propagate our facts. + case _ ⇒ in + } + } + + /** + * For each variable, which can be passed as an argument to the call, a new VariableType is + * created for the callee context. + */ + override def callFlow( + call: JavaStatement, + callee: DeclaredMethod, + in: Set[VTAFact], + source: (DeclaredMethod, VTAFact) + ): Set[VTAFact] = { + val callObject = asCall(call.stmt) + val allParams = callObject.allParams + // Iterate over all input facts and over all parameters of the call. + val flow = scala.collection.mutable.Set.empty[VTAFact] + in.foreach { + case VariableType(definedBy, t, upperBound) ⇒ + allParams.iterator.zipWithIndex.foreach { + /* + * We are only interested in a pair of a variable type and a parameter, if the + * variable and the parameter can refer to the same object. + */ + case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ + // If this is the case, create a new fact for the method's formal parameter. + flow += VariableType( + AbstractIFDSAnalysis + .switchParamAndVariableIndex(parameterIndex, callee.definedMethod.isStatic), + t, + upperBound + ) + case _ ⇒ // Nothing to do + } + case _ ⇒ // Nothing to do + } + flow.toSet + } + + /** + * If the call is an instance call, new CalleeTypes will be created for the call, one for each + * VariableType, which could be the call's target. + */ + override def callToReturnFlow( + call: JavaStatement, + successor: JavaStatement, + in: Set[VTAFact], + source: (DeclaredMethod, VTAFact) + ): Set[VTAFact] = { + // Check, to which variables the callee may refer + val calleeDefinitionSites = asCall(call.stmt).receiverOption + .map(callee ⇒ callee.asVar.definedBy) + .getOrElse(EmptyIntTrieSet) + val calleeTypeFacts = in.collect { + // If we know the variable's type, we also know on which type the call is performed. + case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) ⇒ + CalleeType(call.index, t, upperBound) + } + if (in.size >= calleeTypeFacts.size) in ++ calleeTypeFacts + else calleeTypeFacts ++ in + } + + /** + * If the call returns a value which is assigned to a variable, a new VariableType will be + * created in the caller context with the returned variable's type. + */ + override def returnFlow( + call: JavaStatement, + callee: DeclaredMethod, + exit: JavaStatement, + successor: JavaStatement, + in: Set[VTAFact] + ): Set[VTAFact] = + // We only create a new fact, if the call returns a value, which is assigned to a variable. + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValue = exit.stmt.asReturnValue.expr.asVar + in.collect { + // If we know the type of the return value, we create a fact for the assigned variable. + case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) ⇒ + VariableType(call.index, t, upperBound) + } + } else Set.empty + + /** + * If a method outside of the analysis context is called, we assume that it returns every + * possible runtime type matching the compile time type. + */ + override def callOutsideOfAnalysisContext( + statement: JavaStatement, + callee: DeclaredMethod, + successor: JavaStatement, + in: Set[VTAFact] + ): Set[VTAFact] = { + val returnType = callee.descriptor.returnType + if (statement.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { + Set(VariableType(statement.index, returnType.asReferenceType, upperBound = true)) + } else Set.empty + } + + /** + * Only methods in java.lang and org.opalj are inside the analysis context. + * + * @param callee The callee. + * @return True, if the callee is inside the analysis context. + */ + override def insideAnalysisContext(callee: DeclaredMethod): Boolean = + classInsideAnalysisContext(callee.definedMethod.classFile) && + super.insideAnalysisContext(callee) + + /** + * When `normalFlow` reaches an assignment or array store, this method computes the new facts + * created by the statement. + * + * @param expression The source expression of the assignment or array store. + * @param statementIndex The statement's index. + * @param in The facts, which hold before the statement. + * @return The new facts created by the statement. + */ + private def newFacts( + method: Method, + expression: Expr[DUVar[ValueInformation]], + statementIndex: Int, + in: Set[VTAFact] + ): Iterator[VariableType] = expression.astID match { + case New.ASTID ⇒ + in.iterator.collect { + // When a constructor is called, we always know the exact type. + case VTANullFact ⇒ + VariableType(statementIndex, expression.asNew.tpe, upperBound = false) + } + case Var.ASTID ⇒ + in.iterator.collect { + // When we know the source type, we also know the type of the assigned variable. + case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ + VariableType(statementIndex, t, upperBound) + } + case ArrayLoad.ASTID ⇒ + in.iterator.collect { + // When we know the array's type, we also know the type of the loaded element. + case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && + expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ + VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound) + } + case GetField.ASTID | GetStatic.ASTID ⇒ + val t = expression.asFieldRead.declaredFieldType + /* + * We do not track field types. So we must assume, that it contains any subtype of its + * compile time type. + */ + if (t.isReferenceType) + Iterator(VariableType(statementIndex, t.asReferenceType, upperBound = true)) + else Iterator.empty + case _ ⇒ Iterator.empty + } + + /** + * Checks, if some type is an array type containing an object type. + * + * @param t The type to be checked. + * @param includeObjectType If true, this method also returns true if `t` is an object type + * itself. + * + * @return True, if `t` is an array type of an object type. + */ + @tailrec private def isArrayOfObjectType( + t: FieldType, + includeObjectType: Boolean = false + ): Boolean = { + if (t.isArrayType) isArrayOfObjectType(t.asArrayType.elementType, includeObjectType = true) + else if (t.isObjectType && includeObjectType) true + else false + } + + /** + * Checks, if a class is inside the analysis context. + * By default, that are the packages java.lang and org.opalj. + * + * @param classFile The class, which is checked. + * @return True, if the class is inside the analysis context. + */ + private def classInsideAnalysisContext(classFile: ClassFile): Boolean = { + val fqn = classFile.fqn + fqn.startsWith("java/lang") || fqn.startsWith("org/opalj/fpcf/fixtures/vta") + } + + /** + * Checks, if a method is an entry point of this analysis. + * + * @param method The method to be checked. + * @return True, if this method is an entry point of the analysis. + */ + private def isEntryPoint(method: Method): Boolean = { + val declaredMethod = declaredMethods(method) + method.body.isDefined && canBeCalledFromOutside(declaredMethod) + } + + /** + * For an entry point method, this method computes all pairs (`method`, inputFact) where + * inputFact is a VariableType for one of the method's parameter with its compile time type as + * an upper bound. + * + * @param method The entry point method. + * + * @return All pairs (`method`, inputFact) where inputFact is a VariableType for one of the + * method's parameter with its compile time type as an upper bound. + */ + private def entryPointsForMethod(method: DeclaredMethod): Seq[(DeclaredMethod, VTAFact)] = { + // Iterate over all parameters, which have a reference type. + (method.descriptor.parameterTypes.zipWithIndex.collect { + case (t, index) if t.isReferenceType ⇒ + /* + * Create a fact for the parameter, which says, that the parameter may have any + * subtype of its compile time type. + */ + VariableType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, method.definedMethod.isStatic), + t.asReferenceType, + upperBound = true + ) + /* +* In IFDS problems, we must always also analyze the null fact, because it creates the facts, +* which hold independently of other source facts. +* Map the input facts, in which we are interested, to a pair of the method and the fact. +*/ + } :+ VTANullFact).map(fact ⇒ (method, fact)) + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala index 4dc8bda7e9..ae14054b99 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala @@ -1,15 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.SomeProject import org.opalj.br.Method -import org.opalj.br.ObjectType -import org.opalj.tac._ -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V import org.opalj.tac.fpcf.analyses.ifds.BackwardIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact import org.opalj.tac.fpcf.properties.IFDSProperty import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation @@ -31,360 +26,11 @@ case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Seq[Metho * @param project The project, that is analyzed * @author Mario Trageser */ -abstract class BackwardTaintAnalysis(implicit val project: SomeProject) - extends BackwardIFDSAnalysis[Fact, UnbalancedTaintFact] with TaintAnalysis { +abstract class BackwardTaintAnalysis(ifdsProblem: BackwardTaintProblem)(implicit val project: SomeProject) + extends BackwardIFDSAnalysis[Fact, UnbalancedTaintFact](ifdsProblem) { override val propertyKey: IFDSPropertyMetaInformation[Fact] = Taint - /** - * Called in callToReturnFlow. Creates a FlowFact if necessary. - * - * @param call The call. - * @param in The facts, which hold before the call. - * @param source The entity, which is analyzed. - * @return Some FlowFact, if necessary. Otherwise None. - */ - protected def createFlowFactAtCall(call: Statement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact] - - /** - * Called, when a FlowFact holds at the start node of a callee. Creates a FlowFact in the caller - * context if necessary. - * - * @param calleeFact The FlowFact, which holds at the start node of the callee. - * @param source The analyzed entity. - * @return Some FlowFact, if necessary. Otherwise None. - */ - protected def applyFlowFactFromCallee( - calleeFact: FlowFact, - source: (DeclaredMethod, Fact) - ): Option[FlowFact] - - /** - * Called, when new FlowFacts are found at the beginning of a method. - * Creates a FlowFact if necessary. - * - * @param in The newly found facts. - * @param source The entity, which is analyzed. - * @return Some FlowFact, if necessary. Otherwise None. - */ - protected def createFlowFactAtBeginningOfMethod( - in: Set[Fact], - source: (DeclaredMethod, Fact) - ): Option[FlowFact] - - override protected def nullFact: Fact = NullFact - - override protected def createPropertyValue(result: Map[Statement, Set[Fact]]): IFDSProperty[Fact] = + override protected def createPropertyValue(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[Fact] = new Taint(result) - - /** - * If a tainted variable gets assigned a value, this value will be tainted. - */ - override protected def normalFlow(statement: Statement, successor: Statement, - in: Set[Fact]): Set[Fact] = { - val stmt = statement.stmt - stmt.astID match { - case Assignment.ASTID if isTainted(statement.index, in) ⇒ - in ++ createNewTaints(stmt.asAssignment.expr, statement) - case ArrayStore.ASTID ⇒ - val arrayStore = stmt.asArrayStore - val arrayIndex = TaintAnalysis.getIntConstant(arrayStore.index, statement.code) - val arrayDefinedBy = arrayStore.arrayRef.asVar.definedBy - val result = in ++ in.collect { - // In this case, we taint the whole array. - case Variable(index) if arrayDefinedBy.contains(index) ⇒ - createNewTaints(arrayStore.value, statement) - // In this case, we taint exactly the stored element. - case ArrayElement(index, taintedElement) if arrayDefinedBy.contains(index) && - (arrayIndex.isEmpty || arrayIndex.get == taintedElement) ⇒ - createNewTaints(arrayStore.value, statement) - }.flatten - if (arrayDefinedBy.size == 1 && arrayIndex.isDefined) - result - ArrayElement(arrayDefinedBy.head, arrayIndex.get) - else result - case PutField.ASTID ⇒ - val putField = stmt.asPutField - val objectDefinedBy = putField.objRef.asVar.definedBy - if (in.exists { - case InstanceField(index, declaringClass, name) if objectDefinedBy.contains(index) && - putField.declaringClass == declaringClass && putField.name == name ⇒ - true - case _ ⇒ false - }) in ++ createNewTaints(putField.value, statement) - else in - case PutStatic.ASTID ⇒ - val putStatic = stmt.asPutStatic - if (in.exists { - case StaticField(declaringClass, name) if putStatic.declaringClass == declaringClass && putStatic.name == name ⇒ - true - case _ ⇒ false - }) in ++ createNewTaints(putStatic.value, statement) - else in - case _ ⇒ in - } - } - - /** - * Taints the actual parameters in the caller context if the formal parameters in the callee - * context were tainted. - * Does not taint anything, if the sanitize method was called. - */ - override protected def callFlow(call: Statement, callee: DeclaredMethod, - in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = - if (sanitizesReturnValue(callee)) Set.empty - else taintActualsIfFormalsTainted(callee, call, in, source) - - /** - * If the returned value on in the caller context is tainted, the returned values in the callee - * context will be tainted. If an actual pass-by-reference-parameter in the caller context is - * tainted, the formal parameter in the callee context will be tainted. - */ - override protected def returnFlow(call: Statement, callee: DeclaredMethod, exit: Statement, - successor: Statement, in: Set[Fact]): Set[Fact] = { - val callObject = asCall(call.stmt) - val staticCall = callee.definedMethod.isStatic - val flow = collection.mutable.Set.empty[Fact] - if (call.stmt.astID == Assignment.ASTID && exit.stmt.astID == ReturnValue.ASTID) - in.foreach { - case Variable(index) if index == call.index ⇒ - flow ++= createNewTaints(exit.stmt.asReturnValue.expr, exit) - case ArrayElement(index, taintedElement) if index == call.index ⇒ - flow ++= createNewArrayElementTaints(exit.stmt.asReturnValue.expr, taintedElement, - call) - case InstanceField(index, declaringClass, name) if index == call.index ⇒ - flow ++= createNewInstanceFieldTaints(exit.stmt.asReturnValue.expr, declaringClass, - name, call) - case _ ⇒ // Nothing to do - } - val thisOffset = if (callee.definedMethod.isStatic) 0 else 1 - callObject.allParams.iterator.zipWithIndex - .filter(pair ⇒ (pair._2 == 0 && !staticCall) || - callObject.descriptor.parameterTypes(pair._2 - thisOffset).isReferenceType) - .foreach { pair ⇒ - val param = pair._1.asVar - val paramIndex = pair._2 - in.foreach { - case Variable(index) if param.definedBy.contains(index) ⇒ - flow += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall)) - case ArrayElement(index, taintedElement) if param.definedBy.contains(index) ⇒ - flow += ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement - ) - case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) ⇒ - flow += InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), - declaringClass, name - ) - case staticField: StaticField ⇒ flow += staticField - case _ ⇒ // Nothing to do - } - } - flow.toSet - } - - /** - * Adds a FlowFact, if `createFlowFactAtCall` creates one. - * Removes taints according to `sanitizeParamters`. - */ - override protected def callToReturnFlow(call: Statement, successor: Statement, - in: Set[Fact], - source: (DeclaredMethod, Fact)): Set[Fact] = { - val flowFact = createFlowFactAtCall(call, in, source) - val result = in -- sanitizeParamters(call, in) - if (flowFact.isDefined) result + flowFact.get - else result - } - - /** - * If the returned value is tainted, all actual parameters will be tainted. - */ - override protected def callOutsideOfAnalysisContext(call: Statement, callee: DeclaredMethod, - successor: Statement, - in: Set[Fact]): Set[Fact] = { - val callStatement = asCall(call.stmt) - in ++ in.collect { - case Variable(index) if index == call.index ⇒ - callStatement.allParams.flatMap(createNewTaints(_, call)) - case ArrayElement(index, _) if index == call.index ⇒ - callStatement.allParams.flatMap(createNewTaints(_, call)) - case InstanceField(index, _, _) if index == call.index ⇒ - callStatement.allParams.flatMap(createNewTaints(_, call)) - }.flatten - } - - /** - * Creates an UnbalancedTaintFact for each actual parameter on the caller side, of the formal - * parameter of this method is tainted. - */ - override protected def unbalancedReturnFlow(facts: Set[Fact], call: Statement, - caller: DeclaredMethod, - source: (DeclaredMethod, Fact)): Set[UnbalancedTaintFact] = - taintActualsIfFormalsTainted(source._1, call, facts, source, isCallFlow = false) - .map(UnbalancedTaintFact(call.index, _, currentCallChain(source))) - // Avoid infinite loops. - .filter(unbalancedTaintFact ⇒ !containsHeadTwice(unbalancedTaintFact.callChain)) - - /** - * Propagates the call to createFlowFactAtBeginningOfMethod. - */ - override protected def createFactsAtStartNode( - in: Set[Fact], - source: (DeclaredMethod, Fact) - ): Set[Fact] = { - val flowFact = createFlowFactAtBeginningOfMethod(in, source) - if (flowFact.isDefined) Set(flowFact.get) - else Set.empty - } - - /** - * Gets the current call chain. - * If the input fact for the analysis is an UnbalancedTaintFact, this is the current method - * concatenated with the fact's call chain. - * Otherwise, it is just this method. - * - * @param source The entity, which is analyzed. - * @return The current call chain. - */ - protected def currentCallChain(source: (DeclaredMethod, Fact)): Seq[Method] = { - val method = source._1.definedMethod - val sourceFact = source._2 - sourceFact match { - case fact: UnbalancedTaintFact ⇒ method +: fact.callChain - case _ ⇒ Seq(method) - } - } - - /** - * Checks, if the head of a sequence is contained in its tail. - * - * @param callChain The sequence. - * @return True, if the head of `callChain` is contained in its tail. - */ - protected def containsHeadTwice(callChain: Seq[Method]): Boolean = - callChain.tail.contains(callChain.head) - - /** - * Checks, if some variable or array element is tainted. - * - * @param index The index of the variable or array element. - * @param in The current data flow facts. - * @param taintedElement If present, the taint of a specific array element will be checked. - * @return True, if the variable or array element is tainted. - */ - private def isTainted(index: Int, in: Set[Fact], taintedElement: Option[Int] = None): Boolean = in.exists { - case Variable(variableIndex) ⇒ variableIndex == index - case ArrayElement(variableIndex, element) ⇒ - variableIndex == index && (taintedElement.isEmpty || taintedElement.get == element) - case _ ⇒ false - } - - /** - * Taints all variables in an `expression`. - * - * @param expression The expression. - * @param statement The statement, which contains the expression. - * @return The new taints. - */ - private def createNewTaints(expression: Expr[V], statement: Statement): Set[Fact] = - expression.astID match { - case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) - case ArrayLoad.ASTID ⇒ - val arrayLoad = expression.asArrayLoad - val arrayIndex = TaintAnalysis.getIntConstant(expression.asArrayLoad.index, statement.code) - val arrayDefinedBy = arrayLoad.arrayRef.asVar.definedBy - if (arrayIndex.isDefined) arrayDefinedBy.map(ArrayElement(_, arrayIndex.get)) - else arrayDefinedBy.map(Variable) - case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | - PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ - acc ++ createNewTaints(expression.subExpr(subExpr), statement)) - case GetField.ASTID ⇒ - val getField = expression.asGetField - getField.objRef.asVar.definedBy - .map(InstanceField(_, getField.declaringClass, getField.name)) - /*case GetStatic.ASTID ⇒ - val getStatic = expression.asGetStatic - Set(StaticField(getStatic.declaringClass, getStatic.name))*/ - case _ ⇒ Set.empty - } - - /** - * Creates some taints for an array, but only taints some element. - * - * @param expression The expression, referring to the array. - * @param taintedElement The array element, which will be tainted. - * @param statement The statement, containing the expression. - * @return An ArrayElement fact for the expression and the tainted element. - */ - private def createNewArrayElementTaints(expression: Expr[V], taintedElement: Int, - statement: Statement): Set[Fact] = - createNewTaints(expression, statement).map { - case Variable(variableIndex) ⇒ ArrayElement(variableIndex, taintedElement) - // We do not nest taints. Instead, we taint the whole inner array. - case ArrayElement(variableIndex, _) ⇒ ArrayElement(variableIndex, taintedElement) - case InstanceField(variableIndex, _, _) ⇒ ArrayElement(variableIndex, taintedElement) - } - - private def createNewInstanceFieldTaints(expression: Expr[V], declaringClass: ObjectType, - name: String, statement: Statement): Set[Fact] = - createNewTaints(expression, statement).map { - case Variable(variableIndex) ⇒ InstanceField(variableIndex, declaringClass, name) - // We do not nest taints. Instead, taint the whole field. - case ArrayElement(variableIndex, _) ⇒ InstanceField(variableIndex, declaringClass, name) - case InstanceField(variableIndex, _, _) ⇒ - InstanceField(variableIndex, declaringClass, name) - } - - /** - * Taints actual parameters on in the caller context, if the corresponding formal parameter in - * the `callee` context is tainted. - * Additionally propagates taints of static fields. - * - * @param callee The callee. - * @param call The call, calling the `callee`. - * @param calleeFacts The facts in the `callee` context. - * @param source The entity, which is analyzed. - * @param isCallFlow True, if this method is called in callFlow. In this case, FlowFacts will - * be propagated to the caller context. - * @return The data flow facts in the caller context. - */ - private def taintActualsIfFormalsTainted( - callee: DeclaredMethod, - call: Statement, - calleeFacts: Set[Fact], - source: (DeclaredMethod, Fact), - isCallFlow: Boolean = true - ): Set[Fact] = { - val stmt = call.stmt - val callStatement = asCall(stmt) - val staticCall = callee.definedMethod.isStatic - val thisOffset = if (staticCall) 0 else 1 - val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) - .map(index ⇒ AbstractIFDSAnalysis.switchParamAndVariableIndex(index + thisOffset, staticCall)) - val facts = scala.collection.mutable.Set.empty[Fact] - calleeFacts.foreach { - case Variable(index) if formalParameterIndices.contains(index) ⇒ - facts ++= createNewTaints( - callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), call - ) - case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) ⇒ - facts ++= createNewArrayElementTaints( - callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), - taintedElement, call - ) - case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) ⇒ - facts ++= createNewInstanceFieldTaints( - callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), - declaringClass, name, call - ) - case staticField: StaticField ⇒ facts += staticField - // If the source was reached in a callee, create a flow fact from this method to the sink. - case calleeFact: FlowFact if isCallFlow ⇒ - val callerFact = applyFlowFactFromCallee(calleeFact, source) - if (callerFact.isDefined) facts += callerFact.get - case _ ⇒ // Nothing to do - } - facts.toSet - } } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala new file mode 100644 index 0000000000..03603c432b --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala @@ -0,0 +1,359 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.br.{DeclaredMethod, Method, ObjectType} +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.{ArrayLength, ArrayLoad, ArrayStore, Assignment, BinaryExpr, Compare, Expr, GetField, NewArray, PrefixExpr, PrimitiveTypecastExpr, PutField, PutStatic, ReturnValue, Var} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaBackwardIFDSProblem, JavaStatement} + +abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[Fact, UnbalancedTaintFact](project) with TaintProblem { + override def nullFact: Fact = NullFact + + /** + * If a tainted variable gets assigned a value, this value will be tainted. + */ + override def normalFlow(statement: JavaStatement, successor: JavaStatement, + in: Set[Fact]): Set[Fact] = { + val stmt = statement.stmt + stmt.astID match { + case Assignment.ASTID if isTainted(statement.index, in) ⇒ + in ++ createNewTaints(stmt.asAssignment.expr, statement) + case ArrayStore.ASTID ⇒ + val arrayStore = stmt.asArrayStore + val arrayIndex = TaintProblem.getIntConstant(arrayStore.index, statement.code) + val arrayDefinedBy = arrayStore.arrayRef.asVar.definedBy + val result = in ++ in.collect { + // In this case, we taint the whole array. + case Variable(index) if arrayDefinedBy.contains(index) ⇒ + createNewTaints(arrayStore.value, statement) + // In this case, we taint exactly the stored element. + case ArrayElement(index, taintedElement) if arrayDefinedBy.contains(index) && + (arrayIndex.isEmpty || arrayIndex.get == taintedElement) ⇒ + createNewTaints(arrayStore.value, statement) + }.flatten + if (arrayDefinedBy.size == 1 && arrayIndex.isDefined) + result - ArrayElement(arrayDefinedBy.head, arrayIndex.get) + else result + case PutField.ASTID ⇒ + val putField = stmt.asPutField + val objectDefinedBy = putField.objRef.asVar.definedBy + if (in.exists { + case InstanceField(index, declaringClass, name) if objectDefinedBy.contains(index) && + putField.declaringClass == declaringClass && putField.name == name ⇒ + true + case _ ⇒ false + }) in ++ createNewTaints(putField.value, statement) + else in + case PutStatic.ASTID ⇒ + val putStatic = stmt.asPutStatic + if (in.exists { + case StaticField(declaringClass, name) if putStatic.declaringClass == declaringClass && putStatic.name == name ⇒ + true + case _ ⇒ false + }) in ++ createNewTaints(putStatic.value, statement) + else in + case _ ⇒ in + } + } + + /** + * Taints the actual parameters in the caller context if the formal parameters in the callee + * context were tainted. + * Does not taint anything, if the sanitize method was called. + */ + override def callFlow(call: JavaStatement, callee: DeclaredMethod, + in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = + if (sanitizesReturnValue(callee)) Set.empty + else taintActualsIfFormalsTainted(callee, call, in, source) + + /** + * If the returned value on in the caller context is tainted, the returned values in the callee + * context will be tainted. If an actual pass-by-reference-parameter in the caller context is + * tainted, the formal parameter in the callee context will be tainted. + */ + override def returnFlow(call: JavaStatement, callee: DeclaredMethod, exit: JavaStatement, + successor: JavaStatement, in: Set[Fact]): Set[Fact] = { + val callObject = asCall(call.stmt) + val staticCall = callee.definedMethod.isStatic + val flow = collection.mutable.Set.empty[Fact] + if (call.stmt.astID == Assignment.ASTID && exit.stmt.astID == ReturnValue.ASTID) + in.foreach { + case Variable(index) if index == call.index ⇒ + flow ++= createNewTaints(exit.stmt.asReturnValue.expr, exit) + case ArrayElement(index, taintedElement) if index == call.index ⇒ + flow ++= createNewArrayElementTaints(exit.stmt.asReturnValue.expr, taintedElement, + call) + case InstanceField(index, declaringClass, name) if index == call.index ⇒ + flow ++= createNewInstanceFieldTaints(exit.stmt.asReturnValue.expr, declaringClass, + name, call) + case _ ⇒ // Nothing to do + } + val thisOffset = if (callee.definedMethod.isStatic) 0 else 1 + callObject.allParams.iterator.zipWithIndex + .filter(pair ⇒ (pair._2 == 0 && !staticCall) || + callObject.descriptor.parameterTypes(pair._2 - thisOffset).isReferenceType) + .foreach { pair ⇒ + val param = pair._1.asVar + val paramIndex = pair._2 + in.foreach { + case Variable(index) if param.definedBy.contains(index) ⇒ + flow += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall)) + case ArrayElement(index, taintedElement) if param.definedBy.contains(index) ⇒ + flow += ArrayElement( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement + ) + case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) ⇒ + flow += InstanceField( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), + declaringClass, name + ) + case staticField: StaticField ⇒ flow += staticField + case _ ⇒ // Nothing to do + } + } + flow.toSet + } + + /** + * Adds a FlowFact, if `createFlowFactAtCall` creates one. + * Removes taints according to `sanitizeParamters`. + */ + override def callToReturnFlow(call: JavaStatement, successor: JavaStatement, + in: Set[Fact], + source: (DeclaredMethod, Fact)): Set[Fact] = { + val flowFact = createFlowFactAtCall(call, in, source) + val result = in -- sanitizeParamters(call, in) + if (flowFact.isDefined) result + flowFact.get + else result + } + + /** + * If the returned value is tainted, all actual parameters will be tainted. + */ + override def callOutsideOfAnalysisContext(call: JavaStatement, callee: DeclaredMethod, + successor: JavaStatement, + in: Set[Fact]): Set[Fact] = { + val callStatement = asCall(call.stmt) + in ++ in.collect { + case Variable(index) if index == call.index ⇒ + callStatement.allParams.flatMap(createNewTaints(_, call)) + case ArrayElement(index, _) if index == call.index ⇒ + callStatement.allParams.flatMap(createNewTaints(_, call)) + case InstanceField(index, _, _) if index == call.index ⇒ + callStatement.allParams.flatMap(createNewTaints(_, call)) + }.flatten + } + + /** + * Creates an UnbalancedTaintFact for each actual parameter on the caller side, of the formal + * parameter of this method is tainted. + */ + override def unbalancedReturnFlow(facts: Set[Fact], call: JavaStatement, + caller: DeclaredMethod, + source: (DeclaredMethod, Fact)): Set[UnbalancedTaintFact] = + taintActualsIfFormalsTainted(source._1, call, facts, source, isCallFlow = false) + .map(UnbalancedTaintFact(call.index, _, currentCallChain(source))) + // Avoid infinite loops. + .filter(unbalancedTaintFact ⇒ !containsHeadTwice(unbalancedTaintFact.callChain)) + + /** + * Called in callToReturnFlow. Creates a FlowFact if necessary. + * + * @param call The call. + * @param in The facts, which hold before the call. + * @param source The entity, which is analyzed. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], + source: (DeclaredMethod, Fact)): Option[FlowFact] + + /** + * Called, when a FlowFact holds at the start node of a callee. Creates a FlowFact in the caller + * context if necessary. + * + * @param calleeFact The FlowFact, which holds at the start node of the callee. + * @param source The analyzed entity. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def applyFlowFactFromCallee( + calleeFact: FlowFact, + source: (DeclaredMethod, Fact) + ): Option[FlowFact] + + /** + * Called, when new FlowFacts are found at the beginning of a method. + * Creates a FlowFact if necessary. + * + * @param in The newly found facts. + * @param source The entity, which is analyzed. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFactAtBeginningOfMethod( + in: Set[Fact], + source: (DeclaredMethod, Fact) + ): Option[FlowFact] + + /** + * Propagates the call to createFlowFactAtBeginningOfMethod. + */ + override def createFactsAtStartNode( + in: Set[Fact], + source: (DeclaredMethod, Fact) + ): Set[Fact] = { + val flowFact = createFlowFactAtBeginningOfMethod(in, source) + if (flowFact.isDefined) Set(flowFact.get) + else Set.empty + } + + /** + * Gets the current call chain. + * If the input fact for the analysis is an UnbalancedTaintFact, this is the current method + * concatenated with the fact's call chain. + * Otherwise, it is just this method. + * + * @param source The entity, which is analyzed. + * @return The current call chain. + */ + protected def currentCallChain(source: (DeclaredMethod, Fact)): Seq[Method] = { + val method = source._1.definedMethod + val sourceFact = source._2 + sourceFact match { + case fact: UnbalancedTaintFact ⇒ method +: fact.callChain + case _ ⇒ Seq(method) + } + } + + /** + * Checks, if the head of a sequence is contained in its tail. + * + * @param callChain The sequence. + * @return True, if the head of `callChain` is contained in its tail. + */ + protected def containsHeadTwice(callChain: Seq[Method]): Boolean = + callChain.tail.contains(callChain.head) + + /** + * Checks, if some variable or array element is tainted. + * + * @param index The index of the variable or array element. + * @param in The current data flow facts. + * @param taintedElement If present, the taint of a specific array element will be checked. + * @return True, if the variable or array element is tainted. + */ + private def isTainted(index: Int, in: Set[Fact], taintedElement: Option[Int] = None): Boolean = in.exists { + case Variable(variableIndex) ⇒ variableIndex == index + case ArrayElement(variableIndex, element) ⇒ + variableIndex == index && (taintedElement.isEmpty || taintedElement.get == element) + case _ ⇒ false + } + + /** + * Taints all variables in an `expression`. + * + * @param expression The expression. + * @param statement The statement, which contains the expression. + * @return The new taints. + */ + private def createNewTaints(expression: Expr[V], statement: JavaStatement): Set[Fact] = + expression.astID match { + case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) + case ArrayLoad.ASTID ⇒ + val arrayLoad = expression.asArrayLoad + val arrayIndex = TaintProblem.getIntConstant(expression.asArrayLoad.index, statement.code) + val arrayDefinedBy = arrayLoad.arrayRef.asVar.definedBy + if (arrayIndex.isDefined) arrayDefinedBy.map(ArrayElement(_, arrayIndex.get)) + else arrayDefinedBy.map(Variable) + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | + PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + acc ++ createNewTaints(expression.subExpr(subExpr), statement)) + case GetField.ASTID ⇒ + val getField = expression.asGetField + getField.objRef.asVar.definedBy + .map(InstanceField(_, getField.declaringClass, getField.name)) + /*case GetStatic.ASTID ⇒ + val getStatic = expression.asGetStatic + Set(StaticField(getStatic.declaringClass, getStatic.name))*/ + case _ ⇒ Set.empty + } + + /** + * Creates some taints for an array, but only taints some element. + * + * @param expression The expression, referring to the array. + * @param taintedElement The array element, which will be tainted. + * @param statement The statement, containing the expression. + * @return An ArrayElement fact for the expression and the tainted element. + */ + private def createNewArrayElementTaints(expression: Expr[V], taintedElement: Int, + statement: JavaStatement): Set[Fact] = + createNewTaints(expression, statement).map { + case Variable(variableIndex) ⇒ ArrayElement(variableIndex, taintedElement) + // We do not nest taints. Instead, we taint the whole inner array. + case ArrayElement(variableIndex, _) ⇒ ArrayElement(variableIndex, taintedElement) + case InstanceField(variableIndex, _, _) ⇒ ArrayElement(variableIndex, taintedElement) + } + + private def createNewInstanceFieldTaints(expression: Expr[V], declaringClass: ObjectType, + name: String, statement: JavaStatement): Set[Fact] = + createNewTaints(expression, statement).map { + case Variable(variableIndex) ⇒ InstanceField(variableIndex, declaringClass, name) + // We do not nest taints. Instead, taint the whole field. + case ArrayElement(variableIndex, _) ⇒ InstanceField(variableIndex, declaringClass, name) + case InstanceField(variableIndex, _, _) ⇒ + InstanceField(variableIndex, declaringClass, name) + } + + /** + * Taints actual parameters on in the caller context, if the corresponding formal parameter in + * the `callee` context is tainted. + * Additionally propagates taints of static fields. + * + * @param callee The callee. + * @param call The call, calling the `callee`. + * @param calleeFacts The facts in the `callee` context. + * @param source The entity, which is analyzed. + * @param isCallFlow True, if this method is called in callFlow. In this case, FlowFacts will + * be propagated to the caller context. + * @return The data flow facts in the caller context. + */ + private def taintActualsIfFormalsTainted( + callee: DeclaredMethod, + call: JavaStatement, + calleeFacts: Set[Fact], + source: (DeclaredMethod, Fact), + isCallFlow: Boolean = true + ): Set[Fact] = { + val stmt = call.stmt + val callStatement = asCall(stmt) + val staticCall = callee.definedMethod.isStatic + val thisOffset = if (staticCall) 0 else 1 + val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) + .map(index ⇒ AbstractIFDSAnalysis.switchParamAndVariableIndex(index + thisOffset, staticCall)) + val facts = scala.collection.mutable.Set.empty[Fact] + calleeFacts.foreach { + case Variable(index) if formalParameterIndices.contains(index) ⇒ + facts ++= createNewTaints( + callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), call + ) + case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) ⇒ + facts ++= createNewArrayElementTaints( + callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + taintedElement, call + ) + case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) ⇒ + facts ++= createNewInstanceFieldTaints( + callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + declaringClass, name, call + ) + case staticField: StaticField ⇒ facts += staticField + // If the source was reached in a callee, create a flow fact from this method to the sink. + case calleeFact: FlowFact if isCallFlow ⇒ + val callerFact = applyFlowFactFromCallee(calleeFact, source) + if (callerFact.isDefined) facts += callerFact.get + case _ ⇒ // Nothing to do + } + facts.toSet + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala index 85c6df547b..ff5eef8457 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala @@ -2,13 +2,9 @@ package org.opalj.tac.fpcf.analyses.ifds.taint import org.opalj.fpcf.PropertyKey -import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.SomeProject -import org.opalj.tac._ -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V import org.opalj.tac.fpcf.analyses.ifds.ForwardIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.properties.IFDSProperty import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation @@ -19,345 +15,21 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * @param project The project, that is analyzed * @author Mario Trageser */ -abstract class ForwardTaintAnalysis(implicit val project: SomeProject) - extends ForwardIFDSAnalysis[Fact] with TaintAnalysis { +abstract class ForwardTaintAnalysis(ifdsProblem: ForwardTaintProblem)(implicit val project: SomeProject) + extends ForwardIFDSAnalysis[Fact](ifdsProblem) { override val propertyKey: IFDSPropertyMetaInformation[Fact] = Taint - /** - * Called, when the exit to return facts are computed for some `callee` with the null fact and - * the callee's return value is assigned to a vairbale. - * Creates a taint, if necessary. - * - * @param callee The called method. - * @param call The call. - * @return Some variable fact, if necessary. Otherwise none. - */ - protected def createTaints(callee: DeclaredMethod, call: Statement): Set[Fact] + val test = classHierarchy - /** - * Called, when the call to return facts are computed for some `callee`. - * Creates a FlowFact, if necessary. - * - * @param callee The method, which was called. - * @param call The call. - * @return Some FlowFact, if necessary. Otherwise None. - */ - protected def createFlowFact(callee: DeclaredMethod, call: Statement, - in: Set[Fact]): Option[FlowFact] - - override protected def nullFact: Fact = NullFact - - override protected def createPropertyValue(result: Map[Statement, Set[Fact]]): IFDSProperty[Fact] = + override protected def createPropertyValue(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[Fact] = new Taint(result) - - /** - * If a variable gets assigned a tainted value, the variable will be tainted. - */ - override protected def normalFlow(statement: Statement, successor: Statement, - in: Set[Fact]): Set[Fact] = - statement.stmt.astID match { - case Assignment.ASTID ⇒ - in ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) - case ArrayStore.ASTID ⇒ - val store = statement.stmt.asArrayStore - val definedBy = store.arrayRef.asVar.definedBy - val arrayIndex = TaintAnalysis.getIntConstant(store.index, statement.code) - if (isTainted(store.value, in)) { - if (arrayIndex.isDefined) - // Taint a known array index - definedBy.foldLeft(in) { (c, n) ⇒ - c + ArrayElement(n, arrayIndex.get) - } - else - // Taint the whole array if the index is unknown - definedBy.foldLeft(in) { (c, n) ⇒ - c + Variable(n) - } - } else if (arrayIndex.isDefined && definedBy.size == 1) - // Untaint if possible - in - ArrayElement(definedBy.head, arrayIndex.get) - else in - case PutField.ASTID ⇒ - val put = statement.stmt.asPutField - val definedBy = put.objRef.asVar.definedBy - if (isTainted(put.value, in)) - definedBy.foldLeft(in) { (in, defSite) ⇒ - in + InstanceField(defSite, put.declaringClass, put.name) - } - else - in - case PutStatic.ASTID ⇒ - val put = statement.stmt.asPutStatic - if (isTainted(put.value, in)) - in + StaticField(put.declaringClass, put.name) - else - in - case _ ⇒ in - } - - /** - * Propagates tainted parameters to the callee. If a call to the sink method with a tainted - * parameter is detected, no call-to-start - * edges will be created. - */ - override protected def callFlow(call: Statement, callee: DeclaredMethod, - in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = { - val callObject = asCall(call.stmt) - val allParams = callObject.allParams - var facts = Set.empty[Fact] - - if (relevantCallee(callee)) { - val allParamsWithIndices = allParams.zipWithIndex - in.foreach { - // Taint formal parameter if actual parameter is tainted - case Variable(index) ⇒ - allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( - paramIndex, - callee.definedMethod.isStatic - )) - case _ ⇒ // Nothing to do - } - - // Taint element of formal parameter if element of actual parameter is tainted - case ArrayElement(index, taintedIndex) ⇒ - allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), - taintedIndex - ) - case _ ⇒ // Nothing to do - } - - case InstanceField(index, declClass, taintedField) ⇒ - // Taint field of formal parameter if field of actual parameter is tainted - // Only if the formal parameter is of a type that may have that field! - allParamsWithIndices.foreach { - case (param, pIndex) if param.asVar.definedBy.contains(index) && - (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || - classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ - facts += InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), - declClass, taintedField - ) - case _ ⇒ // Nothing to do - } - - case sf: StaticField ⇒ facts += sf - - case _ ⇒ // Nothing to do - } - } - - facts - } - - /** - * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. - * If the callee's return value was tainted and it is assigned to a variable in the callee, the - * variable will be tainted. - * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds - * at this method. - * Creates new taints and FlowFacts, if necessary. - * If the sanitize method was called, nothing will be tainted. - */ - override protected def returnFlow(call: Statement, callee: DeclaredMethod, exit: Statement, - successor: Statement, in: Set[Fact]): Set[Fact] = { - - /** - * Checks whether the callee's formal parameter is of a reference type. - */ - def isRefTypeParam(index: Int): Boolean = - if (index == -1) true - else { - val parameterOffset = if (callee.definedMethod.isStatic) 0 else 1 - callee.descriptor.parameterType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) - - parameterOffset - ).isReferenceType - } - - if (sanitizesReturnValue(callee)) return Set.empty - val callStatement = asCall(call.stmt) - val allParams = callStatement.allParams - var flows: Set[Fact] = Set.empty - in.foreach { - // Taint actual parameter if formal parameter is tainted - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ - val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) - ) - flows ++= param.asVar.definedBy.iterator.map(Variable) - - // Taint element of actual parameter if element of formal parameter is tainted - case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ - val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) - ) - flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) - - case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ - // Taint field of actual parameter if field of formal parameter is tainted - val param = - allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) - param.asVar.definedBy.foreach { defSite ⇒ - flows += InstanceField(defSite, declClass, taintedField) - } - - case sf: StaticField ⇒ flows += sf - - // Track the call chain to the sink back - case FlowFact(flow) if !flow.contains(call.method) ⇒ - flows += FlowFact(call.method +: flow) - case _ ⇒ - } - - // Propagate taints of the return value - if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { - val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy - in.foreach { - case Variable(index) if returnValueDefinedBy.contains(index) ⇒ - flows += Variable(call.index) - case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ - flows += ArrayElement(call.index, taintedIndex) - case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ - flows += InstanceField(call.index, declClass, taintedField) - case NullFact ⇒ - val taints = createTaints(callee, call) - if (taints.nonEmpty) flows ++= taints - case _ ⇒ // Nothing to do - } - } - val flowFact = createFlowFact(callee, call, in) - if (flowFact.isDefined) flows += flowFact.get - - flows - } - - /** - * Removes taints according to `sanitizeParamters`. - */ - override protected def callToReturnFlow(call: Statement, successor: Statement, - in: Set[Fact], - source: (DeclaredMethod, Fact)): Set[Fact] = - in -- sanitizeParamters(call, in) - - /** - * If a parameter is tainted, the result will also be tainted. - * We assume that the callee does not call the source method. - */ - override protected def callOutsideOfAnalysisContext(statement: Statement, callee: DeclaredMethod, - successor: Statement, - in: Set[Fact]): Set[Fact] = { - val allParams = asCall(statement.stmt).receiverOption ++ asCall(statement.stmt).params - if (statement.stmt.astID == Assignment.ASTID && in.exists { - case Variable(index) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - } - case ArrayElement(index, _) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - } - case _ ⇒ false - }) Set(Variable(statement.index)) - else Set.empty - } - - /** - * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. - * True by default. This method can be overwritten by a subclass. - * - * @param callee The callee. - * @return True, by default. - */ - protected def relevantCallee(callee: DeclaredMethod): Boolean = true - - /** - * Creates new facts for an assignment. A new fact for the assigned variable will be created, - * if the expression contains a tainted variable - * - * @param expression The source expression of the assignment - * @param statement The assignment statement - * @param in The incoming facts - * @return The new facts, created by the assignment - */ - private def createNewTaints(expression: Expr[V], statement: Statement, in: Set[Fact]): Set[Fact] = - expression.astID match { - case Var.ASTID ⇒ - val definedBy = expression.asVar.definedBy - in.collect { - case Variable(index) if definedBy.contains(index) ⇒ - Variable(statement.index) - } - case ArrayLoad.ASTID ⇒ - val loadExpression = expression.asArrayLoad - val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy - if (in.exists { - // One specific array element may be tainted - case ArrayElement(index, taintedElement) ⇒ - val loadedIndex = TaintAnalysis.getIntConstant(loadExpression.index, statement.code) - arrayDefinedBy.contains(index) && - (loadedIndex.isEmpty || taintedElement == loadedIndex.get) - // Or the whole array - case Variable(index) ⇒ arrayDefinedBy.contains(index) - case _ ⇒ false - }) Set(Variable(statement.index)) - else - Set.empty - case GetField.ASTID ⇒ - val get = expression.asGetField - val objectDefinedBy = get.objRef.asVar.definedBy - if (in.exists { - // The specific field may be tainted - case InstanceField(index, _, taintedField) ⇒ - taintedField == get.name && objectDefinedBy.contains(index) - // Or the whole object - case Variable(index) ⇒ objectDefinedBy.contains(index) - case _ ⇒ false - }) - Set(Variable(statement.index)) - else - Set.empty - case GetStatic.ASTID ⇒ - val get = expression.asGetStatic - if (in.contains(StaticField(get.declaringClass, get.name))) - Set(Variable(statement.index)) - else Set.empty - case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | - NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ - acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) - case _ ⇒ Set.empty - } - - /** - * Checks, if the result of some variable expression could be tainted. - * - * @param expression The variable expression. - * @param in The current data flow facts. - * @return True, if the expression could be tainted - */ - private def isTainted(expression: Expr[V], in: Set[Fact]): Boolean = { - val definedBy = expression.asVar.definedBy - expression.isVar && in.exists { - case Variable(index) ⇒ definedBy.contains(index) - case ArrayElement(index, _) ⇒ definedBy.contains(index) - case InstanceField(index, _, _) ⇒ definedBy.contains(index) - case _ ⇒ false - } - } } /** * The IFDSProperty for this analysis. */ -case class Taint(flows: Map[Statement, Set[Fact]]) extends IFDSProperty[Fact] { +case class Taint(flows: Map[JavaStatement, Set[Fact]]) extends IFDSProperty[Fact] { override type Self = Taint diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala new file mode 100644 index 0000000000..7a3e6dc8be --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -0,0 +1,336 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.{ArrayLength, ArrayLoad, ArrayStore, Assignment, BinaryExpr, Compare, Expr, GetField, GetStatic, NewArray, PrefixExpr, PrimitiveTypecastExpr, PutField, PutStatic, ReturnValue, Var} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaIFDSProblem, JavaStatement} + +abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[Fact](project) with TaintProblem { + override def nullFact: Fact = NullFact + + /** + * If a variable gets assigned a tainted value, the variable will be tainted. + */ + override def normalFlow(statement: JavaStatement, successor: JavaStatement, + in: Set[Fact]): Set[Fact] = + statement.stmt.astID match { + case Assignment.ASTID ⇒ + in ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) + case ArrayStore.ASTID ⇒ + val store = statement.stmt.asArrayStore + val definedBy = store.arrayRef.asVar.definedBy + val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) + if (isTainted(store.value, in)) { + if (arrayIndex.isDefined) + // Taint a known array index + definedBy.foldLeft(in) { (c, n) ⇒ + c + ArrayElement(n, arrayIndex.get) + } + else + // Taint the whole array if the index is unknown + definedBy.foldLeft(in) { (c, n) ⇒ + c + Variable(n) + } + } else if (arrayIndex.isDefined && definedBy.size == 1) + // Untaint if possible + in - ArrayElement(definedBy.head, arrayIndex.get) + else in + case PutField.ASTID ⇒ + val put = statement.stmt.asPutField + val definedBy = put.objRef.asVar.definedBy + if (isTainted(put.value, in)) + definedBy.foldLeft(in) { (in, defSite) ⇒ + in + InstanceField(defSite, put.declaringClass, put.name) + } + else + in + case PutStatic.ASTID ⇒ + val put = statement.stmt.asPutStatic + if (isTainted(put.value, in)) + in + StaticField(put.declaringClass, put.name) + else + in + case _ ⇒ in + } + + /** + * Propagates tainted parameters to the callee. If a call to the sink method with a tainted + * parameter is detected, no call-to-start + * edges will be created. + */ + override def callFlow(call: JavaStatement, callee: DeclaredMethod, + in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = { + val callObject = asCall(call.stmt) + val allParams = callObject.allParams + var facts = Set.empty[Fact] + + if (relevantCallee(callee)) { + val allParamsWithIndices = allParams.zipWithIndex + in.foreach { + // Taint formal parameter if actual parameter is tainted + case Variable(index) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( + paramIndex, + callee.definedMethod.isStatic + )) + case _ ⇒ // Nothing to do + } + + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += ArrayElement( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), + taintedIndex + ) + case _ ⇒ // Nothing to do + } + + case InstanceField(index, declClass, taintedField) ⇒ + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.foreach { + case (param, pIndex) if param.asVar.definedBy.contains(index) && + (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || + project.classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ + facts += InstanceField( + AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), + declClass, taintedField + ) + case _ ⇒ // Nothing to do + } + + case sf: StaticField ⇒ facts += sf + + case _ ⇒ // Nothing to do + } + } + + facts + } + + /** + * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. + * If the callee's return value was tainted and it is assigned to a variable in the callee, the + * variable will be tainted. + * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds + * at this method. + * Creates new taints and FlowFacts, if necessary. + * If the sanitize method was called, nothing will be tainted. + */ + override def returnFlow(call: JavaStatement, callee: DeclaredMethod, exit: JavaStatement, + successor: JavaStatement, in: Set[Fact]): Set[Fact] = { + + /** + * Checks whether the callee's formal parameter is of a reference type. + */ + def isRefTypeParam(index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (callee.definedMethod.isStatic) 0 else 1 + callee.descriptor.parameterType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + - parameterOffset + ).isReferenceType + } + + if (sanitizesReturnValue(callee)) return Set.empty + val callStatement = asCall(call.stmt) + val allParams = callStatement.allParams + var flows: Set[Fact] = Set.empty + in.foreach { + // Taint actual parameter if formal parameter is tainted + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(Variable) + + // Taint element of actual parameter if element of formal parameter is tainted + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) + + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + // Taint field of actual parameter if field of formal parameter is tainted + val param = + allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) + param.asVar.definedBy.foreach { defSite ⇒ + flows += InstanceField(defSite, declClass, taintedField) + } + + case sf: StaticField ⇒ flows += sf + + // Track the call chain to the sink back + case FlowFact(flow) if !flow.contains(call.method) ⇒ + flows += FlowFact(call.method +: flow) + case _ ⇒ + } + + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + in.foreach { + case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + flows += Variable(call.index) + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + flows += ArrayElement(call.index, taintedIndex) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + flows += InstanceField(call.index, declClass, taintedField) + case NullFact ⇒ + val taints = createTaints(callee, call) + if (taints.nonEmpty) flows ++= taints + case _ ⇒ // Nothing to do + } + } + val flowFact = createFlowFact(callee, call, in) + if (flowFact.isDefined) flows += flowFact.get + + flows + } + + /** + * Removes taints according to `sanitizeParamters`. + */ + override def callToReturnFlow(call: JavaStatement, successor: JavaStatement, + in: Set[Fact], + source: (DeclaredMethod, Fact)): Set[Fact] = + in -- sanitizeParamters(call, in) + + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a vairbale. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] + + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, + in: Set[Fact]): Option[FlowFact] + + /** + * If a parameter is tainted, the result will also be tainted. + * We assume that the callee does not call the source method. + */ + override def callOutsideOfAnalysisContext(statement: JavaStatement, callee: DeclaredMethod, + successor: JavaStatement, + in: Set[Fact]): Set[Fact] = { + val allParams = asCall(statement.stmt).receiverOption ++ asCall(statement.stmt).params + if (statement.stmt.astID == Assignment.ASTID && in.exists { + case Variable(index) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case ArrayElement(index, _) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case _ ⇒ false + }) Set(Variable(statement.index)) + else Set.empty + } + + /** + * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. + * True by default. This method can be overwritten by a subclass. + * + * @param callee The callee. + * @return True, by default. + */ + protected def relevantCallee(callee: DeclaredMethod): Boolean = true + + /** + * Creates new facts for an assignment. A new fact for the assigned variable will be created, + * if the expression contains a tainted variable + * + * @param expression The source expression of the assignment + * @param statement The assignment statement + * @param in The incoming facts + * @return The new facts, created by the assignment + */ + private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: Set[Fact]): Set[Fact] = + expression.astID match { + case Var.ASTID ⇒ + val definedBy = expression.asVar.definedBy + in.collect { + case Variable(index) if definedBy.contains(index) ⇒ + Variable(statement.index) + } + case ArrayLoad.ASTID ⇒ + val loadExpression = expression.asArrayLoad + val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy + if (in.exists { + // One specific array element may be tainted + case ArrayElement(index, taintedElement) ⇒ + val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) + arrayDefinedBy.contains(index) && + (loadedIndex.isEmpty || taintedElement == loadedIndex.get) + // Or the whole array + case Variable(index) ⇒ arrayDefinedBy.contains(index) + case _ ⇒ false + }) Set(Variable(statement.index)) + else + Set.empty + case GetField.ASTID ⇒ + val get = expression.asGetField + val objectDefinedBy = get.objRef.asVar.definedBy + if (in.exists { + // The specific field may be tainted + case InstanceField(index, _, taintedField) ⇒ + taintedField == get.name && objectDefinedBy.contains(index) + // Or the whole object + case Variable(index) ⇒ objectDefinedBy.contains(index) + case _ ⇒ false + }) + Set(Variable(statement.index)) + else + Set.empty + case GetStatic.ASTID ⇒ + val get = expression.asGetStatic + if (in.contains(StaticField(get.declaringClass, get.name))) + Set(Variable(statement.index)) + else Set.empty + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | + NewArray.ASTID | ArrayLength.ASTID ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) + case _ ⇒ Set.empty + } + + /** + * Checks, if the result of some variable expression could be tainted. + * + * @param expression The variable expression. + * @param in The current data flow facts. + * @return True, if the expression could be tainted + */ + private def isTainted(expression: Expr[V], in: Set[Fact]): Boolean = { + val definedBy = expression.asVar.definedBy + expression.isVar && in.exists { + case Variable(index) ⇒ definedBy.contains(index) + case ArrayElement(index, _) ⇒ definedBy.contains(index) + case InstanceField(index, _, _) ⇒ definedBy.contains(index) + case _ ⇒ false + } + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala similarity index 95% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index ed5d74340f..219efd66f5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -6,7 +6,7 @@ import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSNullFact -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.Assignment import org.opalj.tac.Expr import org.opalj.tac.Stmt @@ -62,7 +62,7 @@ case class FlowFact(flow: Seq[Method]) extends Fact { } } -trait TaintAnalysis { +trait TaintProblem { /** * Checks, if some `callee` is a sanitizer, which sanitizes its return value. @@ -81,10 +81,10 @@ trait TaintAnalysis { * @param in The facts, which hold before the call. * @return Facts, which will be removed from `in` after the call. */ - protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] + protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] } -object TaintAnalysis { +object TaintProblem { /** * Checks, if some expression always evaluates to the same int constant. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala index 5b0da7a71f..9c811601d5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala @@ -7,7 +7,7 @@ package properties import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyMetaInformation import org.opalj.value.KnownTypedValue -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement trait IFDSPropertyMetaInformation[DataFlowFact] extends PropertyMetaInformation @@ -21,7 +21,7 @@ abstract class IFDSProperty[DataFlowFact] /** * Maps exit statements to the data flow facts, which hold after them. */ - def flows: Map[Statement, Set[DataFlowFact]] + def flows: Map[JavaStatement, Set[DataFlowFact]] override def equals(other: Any): Boolean = other match { case that: IFDSProperty[DataFlowFact] ⇒ diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala index a8dc0b5d28..2a53606052 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala @@ -7,7 +7,7 @@ import org.opalj.br.DeclaredMethod import org.opalj.br.Method import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation @@ -21,7 +21,9 @@ case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Met * @author Mario Trageser */ class BackwardTaintAnalysisFixture private (implicit val pProject: SomeProject) - extends BackwardTaintAnalysis { + extends BackwardTaintAnalysis(new BackwardTaintProblemFixture(pProject)) + +class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p) { override val entryPoints: Seq[(DeclaredMethod, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") @@ -37,7 +39,7 @@ class BackwardTaintAnalysisFixture private (implicit val pProject: SomeProject) /** * We do not sanitize paramters. */ - override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * Create a flow fact, if a source method is called and the returned value is tainted. @@ -45,13 +47,12 @@ class BackwardTaintAnalysisFixture private (implicit val pProject: SomeProject) * terminates. * In this case, callFlow would never be called and no FlowFact would be created. */ - override protected def createFlowFactAtCall(call: Statement, in: Set[Fact], + override protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], source: (DeclaredMethod, Fact)): Option[FlowFact] = { - val callPc = call.code(call.index).pc if (in.exists { case Variable(index) ⇒ index == call.index case _ ⇒ false - } && getCallees(call.node.asBasicBlock, callPc, source._1).exists(_.name == "source")) { + } && getCallees(call, source._1).exists(_.name == "source")) { val callChain = currentCallChain(source) // Avoid infinite loops. if (!containsHeadTwice(callChain)) diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala index f1051ee181..e3f6795060 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -5,7 +5,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.Statement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation /** @@ -15,7 +15,9 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * @author Mario Trageser */ class ForwardTaintAnalysisFixture private (implicit val pProject: SomeProject) - extends ForwardTaintAnalysis { + extends ForwardTaintAnalysis(new ForwardTaintProblemFixture(pProject)) + +class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) { /** * The analysis starts with all public methods in TaintAnalysisTestClass. @@ -34,12 +36,12 @@ class ForwardTaintAnalysisFixture private (implicit val pProject: SomeProject) /** * We do not sanitize paramters. */ - override protected def sanitizeParamters(call: Statement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * Creates a new variable fact for the callee, if the source was called. */ - override protected def createTaints(callee: DeclaredMethod, call: Statement): Set[Fact] = + override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = if (callee.name == "source") Set(Variable(call.index)) else Set.empty @@ -47,7 +49,7 @@ class ForwardTaintAnalysisFixture private (implicit val pProject: SomeProject) * Create a FlowFact, if sink is called with a tainted variable. * Note, that sink does not accept array parameters. No need to handle them. */ - override protected def createFlowFact(callee: DeclaredMethod, call: Statement, + override protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, in: Set[Fact]): Option[FlowFact] = if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) else None From e3838cf555c41bc129f18210a6ab9dc9bcbe5f02 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 2 Mar 2022 20:10:10 +0100 Subject: [PATCH 17/67] Replace specialized ifds analysis classes with parameter --- .../BackwardClassForNameTaintAnalysis.scala | 14 +++--- .../ForwardClassForNameTaintAnalysis.scala | 14 +++--- .../taint/BackwardFlowPathMatcher.scala | 2 +- .../taint/ForwardFlowPathMatcher.scala | 2 +- .../MultilingualForwardTaintAnalysis.scala | 10 ++--- .../analyses/ifds/AbstractIFDSAnalysis.scala | 17 +++---- .../analyses/ifds/BackwardIFDSAnalysis.scala | 7 +-- .../analyses/ifds/ForwardIFDSAnalysis.scala | 3 +- .../ifds/IFDSBasedVariableTypeAnalysis.scala | 18 +++----- .../tac/fpcf/analyses/ifds/IFDSProblem.scala | 2 +- .../ifds/taint/BackwardTaintAnalysis.scala | 36 --------------- .../ifds/taint/ForwardTaintAnalysis.scala | 44 ------------------- .../analyses/ifds/taint/TaintProblem.scala | 14 ++++-- .../tac/fpcf/properties/IFDSProperty.scala | 10 ++++- .../org/opalj/tac/fpcf/properties/Taint.scala | 25 +++++++++++ .../taint/BackwardTaintAnalysisFixture.scala | 11 ++--- .../taint/ForwardTaintAnalysisFixture.scala | 9 ++-- 17 files changed, 90 insertions(+), 148 deletions(-) delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala index e724c5726a..bf8bb5eb71 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala @@ -9,13 +9,9 @@ import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.SomeProject import org.opalj.br.DefinedMethod import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, BackwardTaintAnalysis, BackwardTaintProblem, Fact, FlowFact, InstanceField, Taint, Variable} -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact -import org.opalj.tac.fpcf.analyses.ifds.AbsractIFDSAnalysisRunner -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysis, JavaStatement, UnbalancedReturnFact} +import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, BackwardTaintProblem, Fact, FlowFact, InstanceField, Variable} +import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} /** * A backward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, @@ -23,8 +19,8 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * * @author Mario Trageser */ -class BackwardClassForNameTaintAnalysis private (implicit project: SomeProject) - extends BackwardTaintAnalysis(new BackwardClassForNameTaintProblem(project)) +class BackwardClassForNameTaintAnalysis private (implicit val project: SomeProject) + extends BackwardIFDSAnalysis(new BackwardClassForNameTaintProblem(project), Taint) class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProblem(p) { diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala index 19b5b0de7a..001de2a9b0 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala @@ -7,13 +7,9 @@ import org.opalj.br.DeclaredMethod import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintAnalysis, ForwardTaintProblem, Taint, TaintProblem, Variable} -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.AbsractIFDSAnalysisRunner -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis -import org.opalj.tac.fpcf.properties.IFDSProperty -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, ForwardIFDSAnalysis, IFDSAnalysis, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProblem, TaintProblem, Variable} +import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation, Taint} /** * A forward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, @@ -23,8 +19,8 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * @author Mario Trageser * @author Michael Eichberg */ -class ForwardClassForNameTaintAnalysis private (implicit project: SomeProject) - extends ForwardTaintAnalysis(new ForwardClassForNameTaintProblem(project)) +class ForwardClassForNameTaintAnalysis private (implicit val project: SomeProject) + extends ForwardIFDSAnalysis(new ForwardClassForNameTaintProblem(project), Taint) class ForwardClassForNameTaintProblem(project: SomeProject) extends ForwardTaintProblem(project) with TaintProblem { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala index a531f2b382..722661fcd4 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala @@ -16,7 +16,7 @@ import org.opalj.br.DefinedMethod import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixture import org.opalj.tac.fpcf.analyses.ifds.taint.Fact import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact -import org.opalj.tac.fpcf.analyses.ifds.taint.Taint +import org.opalj.tac.fpcf.properties.Taint /** * @author Mario Trageser diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala index 82afc0ca10..0920a779ed 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala @@ -10,7 +10,7 @@ import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.br.ElementValue import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact -import org.opalj.tac.fpcf.analyses.ifds.taint.Taint +import org.opalj.tac.fpcf.properties.Taint /** * @author Mario Trageser diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala index 23310ca942..90827a52af 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala @@ -4,9 +4,9 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod import org.opalj.fpcf.PropertyStore -import org.opalj.tac.fpcf.analyses.ifds.{IFDSAnalysis, JavaStatement} -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintAnalysis, ForwardTaintProblem, NullFact, Taint, Variable} -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysis, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProblem, NullFact, Variable} +import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} /** * An analysis that checks, if the return value of the method `source` can flow to the parameter of @@ -15,8 +15,8 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * @author Mario Trageser * @author Marc Clement */ -class MultilingualForwardTaintAnalysis private (implicit val pProject: SomeProject) - extends ForwardTaintAnalysis(new MultilingualForwardTaintProblem(pProject)) +class MultilingualForwardTaintAnalysis private (implicit val project: SomeProject) + extends ForwardIFDSAnalysis(new MultilingualForwardTaintProblem(project), Taint) class MultilingualForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index 9cf42e5fa4..630c9dbd51 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -60,16 +60,17 @@ import org.opalj.tac.cg.{RTACallGraphKey, TypeProviderKey} import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement]) +/** + * + * @param ifdsProblem + * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. + * @tparam IFDSFact + */ + +abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement], val propertyKey: IFDSPropertyMetaInformation[IFDSFact]) extends FPCFAnalysis with Subsumable[IFDSFact] { - /** - * Provides the concrete property key that must be unique for every distinct concrete analysis - * and the lower bound for the IFDSProperty. - */ - val propertyKey: IFDSPropertyMetaInformation[IFDSFact] - /** * All declared methods in the project. */ @@ -147,7 +148,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble * @param result Maps each exit statement to the facts, which hold after the exit statement. * @return An IFDSProperty containing the `result`. */ - protected def createPropertyValue(result: Map[JavaStatement, Set[IFDSFact]]): IFDSProperty[IFDSFact] + protected def createPropertyValue(result: Map[JavaStatement, Set[IFDSFact]]): IFDSProperty[IFDSFact] = propertyKey.create(result) /** * Determines the nodes, that will be analyzed after some `basicBlock`. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala index d85f207f5e..d39f8e312c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala @@ -2,7 +2,6 @@ package org.opalj.tac.fpcf.analyses.ifds import scala.annotation.tailrec - import org.opalj.fpcf.EOptionP import org.opalj.fpcf.FinalE import org.opalj.fpcf.FinalEP @@ -18,11 +17,9 @@ import org.opalj.br.cfg.CatchNode import org.opalj.br.cfg.CFG import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation, TACAI, TheTACAI} import org.opalj.tac.Stmt import org.opalj.tac.TACStmts -import org.opalj.tac.fpcf.properties.TACAI -import org.opalj.tac.fpcf.properties.TheTACAI import org.opalj.tac.DUVar import org.opalj.tac.Return import org.opalj.tac.ReturnValue @@ -36,7 +33,7 @@ import org.opalj.tac.TACode * concrete analysis. * @author Mario Trageser */ -abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement] with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem) { +abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement] with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { /** * If this method is analyzed for an unbalanced return fact, the single star block is the block, * which contains the call. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala index fbd86164d0..890a1d050f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala @@ -10,13 +10,14 @@ import org.opalj.tac.Return import org.opalj.tac.ReturnValue import org.opalj.tac.Stmt import org.opalj.tac.TACStmts +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation /** * An IFDS analysis, which analyzes the code in the control flow direction. * * @author Mario Trageser */ -abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem) { +abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { /** * The analysis starts at the entry block. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala index c09ecf20cd..fd88ee0e0d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -25,16 +25,7 @@ import org.opalj.tac.fpcf.properties.TACAI * @author Mario Trageser */ class IFDSBasedVariableTypeAnalysis private (ifdsProblem: VariableTypeProblem)(implicit val project: SomeProject) - extends ForwardIFDSAnalysis[VTAFact](ifdsProblem) { - - override val propertyKey: IFDSPropertyMetaInformation[VTAFact] = VTAResult - - override protected def createPropertyValue( - result: Map[JavaStatement, Set[VTAFact]] - ): IFDSProperty[VTAFact] = - new VTAResult(result) - -} + extends ForwardIFDSAnalysis[VTAFact](ifdsProblem, VTAResult) object IFDSBasedVariableTypeAnalysis extends IFDSAnalysis[VTAFact] { @@ -44,8 +35,9 @@ object IFDSBasedVariableTypeAnalysis extends IFDSAnalysis[VTAFact] { Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) override def init(p: SomeProject, ps: PropertyStore): IFDSBasedVariableTypeAnalysis = { - if (SUBSUMING) new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p))(p) with Subsuming[VTAFact] - else new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p))(p) + implicit val project = p + if (SUBSUMING) new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) with Subsuming[VTAFact] + else new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) } override def property: IFDSPropertyMetaInformation[VTAFact] = VTAResult @@ -57,6 +49,7 @@ object IFDSBasedVariableTypeAnalysis extends IFDSAnalysis[VTAFact] { case class VTAResult(flows: Map[JavaStatement, Set[VTAFact]]) extends IFDSProperty[VTAFact] { override type Self = VTAResult + override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[VTAFact] = new VTAResult(result) override def key: PropertyKey[VTAResult] = VTAResult.key } @@ -64,6 +57,7 @@ case class VTAResult(flows: Map[JavaStatement, Set[VTAFact]]) extends IFDSProper object VTAResult extends IFDSPropertyMetaInformation[VTAFact] { override type Self = VTAResult + override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[VTAFact] = new VTAResult(result) val key: PropertyKey[VTAResult] = PropertyKey.create("VTA", new VTAResult(Map.empty)) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala index f6145d9cd8..e9da408b3d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala @@ -20,7 +20,7 @@ trait AbstractIFDSNullFact extends AbstractIFDSFact * @author Dominik Helm * @author Mario Trageser */ -abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](project: SomeProject) { +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val project: SomeProject) { /** * The null fact of this analysis. */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala deleted file mode 100644 index ae14054b99..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysis.scala +++ /dev/null @@ -1,36 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds.taint - -import org.opalj.br.analyses.SomeProject -import org.opalj.br.Method -import org.opalj.tac.fpcf.analyses.ifds.BackwardIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact -import org.opalj.tac.fpcf.properties.IFDSProperty -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation - -/** - * The unbalanced return fact of this analysis. - * - * @param index The index, at which the analyzed method is called by some caller. - * @param innerFact The fact, which will hold in the caller context after the call. - * @param callChain The current call chain from the sink. - */ -case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Seq[Method]) - extends UnbalancedReturnFact[Fact] with Fact - -/** - * An analysis that checks, if the return value of a `source` method can flow to the parameter of a - * `sink` method. - * - * @param project The project, that is analyzed - * @author Mario Trageser - */ -abstract class BackwardTaintAnalysis(ifdsProblem: BackwardTaintProblem)(implicit val project: SomeProject) - extends BackwardIFDSAnalysis[Fact, UnbalancedTaintFact](ifdsProblem) { - - override val propertyKey: IFDSPropertyMetaInformation[Fact] = Taint - - override protected def createPropertyValue(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[Fact] = - new Taint(result) -} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala deleted file mode 100644 index ff5eef8457..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysis.scala +++ /dev/null @@ -1,44 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds.taint - -import org.opalj.fpcf.PropertyKey -import org.opalj.br.analyses.SomeProject -import org.opalj.tac.fpcf.analyses.ifds.ForwardIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.properties.IFDSProperty -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation - -/** - * An analysis that checks, if the return value of a `source` method can flow to the parameter of a - * `sink` method. - * - * @param project The project, that is analyzed - * @author Mario Trageser - */ -abstract class ForwardTaintAnalysis(ifdsProblem: ForwardTaintProblem)(implicit val project: SomeProject) - extends ForwardIFDSAnalysis[Fact](ifdsProblem) { - - override val propertyKey: IFDSPropertyMetaInformation[Fact] = Taint - - val test = classHierarchy - - override protected def createPropertyValue(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[Fact] = - new Taint(result) -} - -/** - * The IFDSProperty for this analysis. - */ -case class Taint(flows: Map[JavaStatement, Set[Fact]]) extends IFDSProperty[Fact] { - - override type Self = Taint - - override def key: PropertyKey[Taint] = Taint.key -} - -object Taint extends IFDSPropertyMetaInformation[Fact] { - - override type Self = Taint - - val key: PropertyKey[Taint] = PropertyKey.create("Taint", new Taint(Map.empty)) -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index 219efd66f5..1cdafd1fea 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -4,9 +4,7 @@ package org.opalj.tac.fpcf.analyses.ifds.taint import org.opalj.br.DeclaredMethod import org.opalj.br.Method import org.opalj.br.ObjectType -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSNullFact -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, JavaStatement, UnbalancedReturnFact} import org.opalj.tac.Assignment import org.opalj.tac.Expr import org.opalj.tac.Stmt @@ -62,6 +60,16 @@ case class FlowFact(flow: Seq[Method]) extends Fact { } } +/** + * The unbalanced return fact of this analysis. + * + * @param index The index, at which the analyzed method is called by some caller. + * @param innerFact The fact, which will hold in the caller context after the call. + * @param callChain The current call chain from the sink. + */ +case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Seq[Method]) + extends UnbalancedReturnFact[Fact] with Fact + trait TaintProblem { /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala index 9c811601d5..c89a97dec3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala @@ -9,7 +9,15 @@ import org.opalj.fpcf.PropertyMetaInformation import org.opalj.value.KnownTypedValue import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -trait IFDSPropertyMetaInformation[DataFlowFact] extends PropertyMetaInformation +trait IFDSPropertyMetaInformation[DataFlowFact] extends PropertyMetaInformation { + /** + * Creates an IFDSProperty containing the result of this analysis. + * + * @param result Maps each exit statement to the facts, which hold after the exit statement. + * @return An IFDSProperty containing the `result`. + */ + def create(result: Map[JavaStatement, Set[DataFlowFact]]): IFDSProperty[DataFlowFact] +} abstract class IFDSProperty[DataFlowFact] extends Property diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala new file mode 100644 index 0000000000..66976215d8 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala @@ -0,0 +1,25 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.properties + +import org.opalj.fpcf.PropertyKey +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact + +/** + * The IFDSProperty for this analysis. + */ +case class Taint(flows: Map[JavaStatement, Set[Fact]]) extends IFDSProperty[Fact] { + + override type Self = Taint + override def create(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[Fact] = new Taint(result) + + override def key: PropertyKey[Taint] = Taint.key +} + +object Taint extends IFDSPropertyMetaInformation[Fact] { + + override type Self = Taint + override def create(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[Fact] = new Taint(result) + + val key: PropertyKey[Taint] = PropertyKey.create("Taint", new Taint(Map.empty)) +} diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala index 2a53606052..50035e3671 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala @@ -5,11 +5,8 @@ import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod import org.opalj.br.Method -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysis, JavaStatement, UnbalancedReturnFact} +import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Method]) extends UnbalancedReturnFact[Fact] with Fact @@ -20,8 +17,8 @@ case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Met * * @author Mario Trageser */ -class BackwardTaintAnalysisFixture private (implicit val pProject: SomeProject) - extends BackwardTaintAnalysis(new BackwardTaintProblemFixture(pProject)) +class BackwardTaintAnalysisFixture private (implicit val project: SomeProject) + extends BackwardIFDSAnalysis(new BackwardTaintProblemFixture(project), Taint) class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p) { diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala index e3f6795060..f84437e643 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -4,9 +4,8 @@ package org.opalj.tac.fpcf.analyses.ifds.taint import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod -import org.opalj.tac.fpcf.analyses.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysis, JavaStatement} +import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} /** * An analysis that checks, if the return value of the method `source` can flow to the parameter of @@ -14,8 +13,8 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * * @author Mario Trageser */ -class ForwardTaintAnalysisFixture private (implicit val pProject: SomeProject) - extends ForwardTaintAnalysis(new ForwardTaintProblemFixture(pProject)) +class ForwardTaintAnalysisFixture private (implicit val project: SomeProject) + extends ForwardIFDSAnalysis(new ForwardTaintProblemFixture(project), Taint) class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) { From 633c3776b1649f65879f027ff23733cdb9615d0c Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Sat, 5 Mar 2022 15:13:32 +0100 Subject: [PATCH 18/67] WIP --- ...MultilingualForwardTaintAnalysisTest.scala | 13 +- .../org/opalj/ll/SimplePurityTests.scala | 1 + .../main/scala/org/opalj/ll/LLVMProject.scala | 4 + .../analyses}/SimplePurityAnalysis.scala | 15 +- .../ifds/AbstractNativeIFDSAnalysis.scala | 896 ++++++++++++++++++ .../ifds/ForwardNativeIFDSAnalysis.scala | 224 +++++ .../analyses/ifds/NativeIFDSProblem.scala | 29 + .../MultilingualForwardTaintAnalysis.scala | 104 +- .../taint/NativeForwardTaintProblem.scala | 338 +++++++ .../org/opalj/ll/fpcf/properties/Taint.scala | 23 + .../scala/org/opalj/fpcf/PropertyKind.scala | 2 +- .../analyses/ifds/AbstractIFDSAnalysis.scala | 92 +- .../analyses/ifds/BackwardIFDSAnalysis.scala | 8 +- .../analyses/ifds/ForwardIFDSAnalysis.scala | 2 +- .../ifds/IFDSBasedVariableTypeAnalysis.scala | 10 +- .../tac/fpcf/analyses/ifds/IFDSProblem.scala | 5 + .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 47 +- .../tac/fpcf/properties/IFDSProperty.scala | 13 +- .../org/opalj/tac/fpcf/properties/Taint.scala | 11 +- .../taint/BackwardTaintAnalysisFixture.scala | 2 +- .../taint/ForwardTaintAnalysisFixture.scala | 2 +- 21 files changed, 1713 insertions(+), 128 deletions(-) rename OPAL/ll/src/main/scala/org/opalj/ll/{ => fpcf/analyses}/SimplePurityAnalysis.scala (93%) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index 7f218aac02..cceffd715a 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -3,11 +3,11 @@ package org.opalj.ll import org.opalj.br.analyses.{DeclaredMethodsKey, Project} import org.opalj.br.fpcf.FPCFAnalysesManagerKey - import org.opalj.ll.fpcf.analyses.ifds.taint.MultilingualForwardTaintAnalysis import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact} -import org.opalj.tac.fpcf.properties.{IFDSProperty} +import org.opalj.tac.fpcf.properties.IFDSProperty import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers @@ -18,9 +18,10 @@ class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers Project( new java.io.File("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint") ) - //project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( - // current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll") - //) + project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( + current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll") + ) + project.get(LLVMProjectKey) project.get(RTACallGraphKey) val declaredMethods = project.get(DeclaredMethodsKey) val manager = project.get(FPCFAnalysesManagerKey) @@ -31,7 +32,7 @@ class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers println("---METHOD: "+method.toJava+" ---") for { fact ← flows.ub - .asInstanceOf[IFDSProperty[Fact]] + .asInstanceOf[IFDSProperty[JavaStatement, Fact]] .flows .values .flatten diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala index 0d683c873f..e575039335 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala @@ -3,6 +3,7 @@ package org.opalj.ll import org.opalj.br.analyses.Project import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.ll.fpcf.analyses.{EagerSimplePurityAnalysis, Impure, Pure} import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala index 067adbc572..24f3bae178 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala @@ -7,6 +7,10 @@ class LLVMProject(val modules: Iterable[Module]) { def functions(): Iterable[Function] = { modules.flatMap(module ⇒ module.functions()) } + + def function(name: String): Option[Function] = { + functions.find(_.name() == name) + } } object LLVMProject { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala similarity index 93% rename from OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala index 6570c2edcd..2bf1ec58b0 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/SimplePurityAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala @@ -1,17 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll +package org.opalj.ll.fpcf.analyses + import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} import org.opalj.br.fpcf.{BasicFPCFEagerAnalysisScheduler, FPCFAnalysis, FPCFAnalysisScheduler} -import org.opalj.fpcf.{ - Entity, - OrderedProperty, - ProperPropertyComputationResult, - PropertyBounds, - PropertyKey, - PropertyMetaInformation, - PropertyStore, - Result -} +import org.opalj.fpcf._ +import org.opalj.ll.LLVMProjectKey import org.opalj.ll.llvm.{Function, GlobalVariable, Store} sealed trait SimplePurityPropertyMetaInformation extends PropertyMetaInformation { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala new file mode 100644 index 0000000000..84db93ae14 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala @@ -0,0 +1,896 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ + +package org.opalj.ll.fpcf.analyses.ifds + +import java.io.File +import java.io.PrintWriter +import scala.collection.{Set => SomeSet} +import scala.collection.mutable +import com.typesafe.config.ConfigValueFactory + +import javax.swing.JOptionPane +import org.opalj.util.Milliseconds +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.fpcf.EOptionP +import org.opalj.fpcf.EPK +import org.opalj.fpcf.FinalE +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.FinalP +import org.opalj.fpcf.InterimEUBP +import org.opalj.fpcf.InterimResult +import org.opalj.fpcf.InterimUBP +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.fpcf.Property +import org.opalj.fpcf.PropertyBounds +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.Result +import org.opalj.fpcf.SomeEOptionP +import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.seq.PKESequentialPropertyStore +import org.opalj.value.ValueInformation +import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.tac.fpcf.properties.cg.Callees +import org.opalj.tac.fpcf.properties.cg.Callers +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.ai.domain.l0.PrimitiveTACAIDomain +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSAnalysis, IFDSProblem, NumberOfCalls, Subsumable} +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.ll.LLVMProjectKey +import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} + +/** + * + * @param ifdsProblem + * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. + * @tparam IFDSFact + */ + +abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( + val ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement], + val propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact]) + extends FPCFAnalysis + with Subsumable[IFDSFact] { + + /** + * Counts, how many times the abstract methods were called. + */ + var numberOfCalls = new NumberOfCalls() + + /** + * Counts, how many input facts are passed to callbacks. + */ + var sumOfInputfactsForCallbacks = 0L + + /** + * The state of the analysis. For each method and source fact, there is a separate state. + * + * @param declaringClass The class defining the analyzed `method`. + * @param method The analyzed method. + * @param source The input fact, for which the `method` is analyzed. + * @param code The code of the `method`. + * @param cfg The control flow graph of the `method`. + * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input + * facts to the basic block and statement index, at which they may + * be called. + * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input + * facts to the intermediate result of their IFDS analysis. + * Only contains method-fact-pairs, for which this analysis is + * waiting for a final result. + * @param pendingCgCallSites The basic blocks containing call sites, for which the analysis is + * waiting for the final call graph result. + * @param incomingFacts Maps each basic block to the data flow facts valid at its first + * statement. + * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at + * the beginning of the successor. + */ + protected class State( + //val declaringClass: ObjectType, + val function: Function, + val source: (Function, IFDSFact), + //val code: Array[Stmt[V]], + //val cfg: CFG[Stmt[V], TACStmts[V]], + var pendingIfdsCallSites: Map[(Function, IFDSFact), Set[(BasicBlock, Int)]], + //var pendingTacCallSites: Map[Function, Set[BasicBlock]] = Map.empty, + var pendingIfdsDependees: Map[(Function, IFDSFact), EOptionP[(Function, IFDSFact), IFDSProperty[IFDSFact]]] = Map.empty, + //var pendingTacDependees: Map[Function, EOptionP[Function, TACAI]] = Map.empty, + var pendingCgCallSites: Set[BasicBlock] = Set.empty, + var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, + var outgoingFacts: Map[BasicBlock, Map[BasicBlock, Set[IFDSFact]]] = Map.empty + ) + + /** + * Determines the basic blocks, at which the analysis starts. + * + * @param sourceFact The source fact of the analysis. + * @param cfg The control flow graph of the analyzed method. + * @return The basic blocks, at which the analysis starts. + */ + //protected def startBlocks(sourceFact: IFDSFact, cfg: CFG[Stmt[V], TACStmts[V]]): Set[BasicBlock] + + /** + * Collects the facts valid at all exit nodes based on the current results. + * + * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at + * the exit node under the assumption that the predecessor was executed before. + */ + //protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] + + /** + * Creates an IFDSProperty containing the result of this analysis. + * + * @param result Maps each exit statement to the facts, which hold after the exit statement. + * @return An IFDSProperty containing the `result`. + */ + //protected def createPropertyValue(result: Map[JavaStatement, Set[IFDSFact]]): IFDSProperty[IFDSFact] = propertyKey.create(result) + + /** + * Determines the nodes, that will be analyzed after some `basicBlock`. + * + * @param basicBlock The basic block, that was analyzed before. + * @return The nodes, that will be analyzed after `basicBlock`. + */ + //protected def nextNodes(basicBlock: BasicBlock): Set[CFGNode] + + /** + * Checks, if some `node` is the last node. + * + * @return True, if `node` is the last node, i.e. there is no next node. + */ + //protected def isLastNode(node: CFGNode): Boolean + + /** + * If the passed `node` is a catch node, all successors of this catch node are determined. + * + * @param node The node. + * @return If the node is a catch node, all its successors will be returned. + * Otherwise, the node itself will be returned. + */ + //protected def skipCatchNode(node: CFGNode): Set[BasicBlock] + + /** + * Determines the first index of some `basic block`, that will be analyzed. + * + * @param basicBlock The basic block. + * @return The first index of some `basic block`, that will be analyzed. + */ + //protected def firstIndex(basicBlock: BasicBlock): Int + + /** + * Determines the last index of some `basic block`, that will be analzyed. + * + * @param basicBlock The basic block. + * @return The last index of some `basic block`, that will be analzyed. + */ + //protected def lastIndex(basicBlock: BasicBlock): Int + + /** + * Determines the index, that will be analyzed after some other `index`. + * + * @param index The source index. + * @return The index, that will be analyzed after `index`. + */ + //protected def nextIndex(index: Int): Int + + /** + * Gets the first statement of a node, that will be analyzed. + * + * @param node The node. + * @return The first statement of a node, that will be analyzed. + */ + //protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + //protected def nextStatements(statement: JavaStatement)(implicit state: State): Set[JavaStatement] + + /** + * Determines the facts, for which a `callee` is analyzed. + * + * @param call The call, which calls `callee`. + * @param callee The method, which is called by `call`. + * @param in The facts, which hold before the `call`. + * @return The facts, for which `callee` will be analyzed. + */ + /*protected def callToStartFacts(call: JavaStatement, callee: DeclaredMethod, in: Set[IFDSFact])( + implicit + state: State + ): Set[IFDSFact]*/ + + /** + * Collects the exit facts of a `callee` and adds them to the `summaryEdges`. + * + * @param summaryEdges The current summary edges. They map successor statements of the `call` + * to facts, which hold before they are executed. + * @param successors The successor of `call`, which is considered. + * @param call The statement, which calls `callee`. + * @param callee The method, called by `call`. + * @param exitFacts Maps exit statements of the `callee` to the facts, which hold after them. + * @return The summary edges plus the exit to return facts for `callee` and `successor`. + */ + /*protected def addExitToReturnFacts( + summaryEdges: Map[JavaStatement, Set[IFDSFact]], + successors: Set[JavaStatement], + call: JavaStatement, + callee: DeclaredMethod, + exitFacts: Map[JavaStatement, Set[IFDSFact]] + )(implicit state: State): Map[JavaStatement, Set[IFDSFact]]*/ + + /** + * Performs an IFDS analysis for a method-fact-pair. + * + * @param entity The method-fact-pair, that will be analyzed. + * @return An IFDS property mapping from exit statements to the facts valid after these exit + * statements. Returns an interim result, if the TAC or call graph of this method or the + * IFDS analysis for a callee is still pending. + */ + def performAnalysis(entity: (Function, IFDSFact)): ProperPropertyComputationResult = { + val (function, sourceFact) = entity + + + + /* + * Fetch the method's three address code. If it is not present, return an empty interim + * result. + */ + val (code, cfg) = propertyStore(method, TACAI.key) match { + case FinalP(TheTACAI(tac)) ⇒ (tac.stmts, tac.cfg) + + case epk: EPK[Method, TACAI] ⇒ + return InterimResult.forUB( + entity, + createPropertyValue(Map.empty), + Set(epk), + _ ⇒ performAnalysis(entity) + ); + + case tac ⇒ + throw new UnknownError(s"can't handle intermediate TACs ($tac)"); + } + + // Start processing at the start of the cfg with the given source fact + implicit val state: State = + new State(declaringClass, method, entity, code, cfg, Map(entity -> Set.empty), Map()) + val queue = mutable.Queue + .empty[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] + startBlocks(sourceFact, cfg).foreach { start ⇒ + state.incomingFacts += start -> Set(sourceFact) + queue.enqueue((start, Set(sourceFact), None, None, None)) + } + process(queue) + createResult() + } + + /** + * Creates the current (intermediate) result for the analysis. + * + * @return A result containing a map, which maps each exit statement to the facts valid after + * the statement, based on the current results. If the analysis is still waiting for its + * method's TAC or call graph or the result of another method-fact-pair, an interim + * result will be returned. + * + */ + protected def createResult()(implicit state: State): ProperPropertyComputationResult = { + val propertyValue = createPropertyValue(collectResult) + val dependees = state.pendingIfdsDependees.values ++ state.pendingTacDependees.values + if (dependees.isEmpty) Result(state.source, propertyValue) + else InterimResult.forUB(state.source, propertyValue, dependees.toSet, propertyUpdate) + } + + /** + * Called, when there is an updated result for a tac, call graph or another method-fact-pair, + * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and + * returns the new analysis result. + * + * @param eps The new property value. + * @return The new (interim) result of this analysis. + */ + protected def propertyUpdate( + eps: SomeEPS + )(implicit state: State): ProperPropertyComputationResult = { + (eps: @unchecked) match { + case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) ⇒ + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) + + case interimEUBP @ InterimEUBP( + e: (DeclaredMethod, IFDSFact) @unchecked, + ub: IFDSProperty[IFDSFact] + ) ⇒ + if (ub.flows.values + .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { + // Do not re-analyze the caller if we only get the null fact. + // Update the pendingIfdsDependee entry to the new interim result. + state.pendingIfdsDependees += + e -> interimEUBP + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + } else + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) + + case FinalEP(_: DefinedMethod, _: Callees) ⇒ + reAnalyzebasicBlocks(state.pendingCgCallSites) + + case InterimEUBP(_: DefinedMethod, _: Callees) ⇒ + reAnalyzebasicBlocks(state.pendingCgCallSites) + + case FinalEP(method: Method, _: TACAI) ⇒ + state.pendingTacDependees -= method + reAnalyzebasicBlocks(state.pendingTacCallSites(declaredMethods(method))) + } + + createResult() + } + + /** + * Called, when some new information is found at the last node of the method. + * This method can be overwritten by a subclass to perform additional actions. + * + * @param nextIn The input facts, which were found. + * @param oldIn The input facts, which were already known, if present. + */ + protected def foundNewInformationForLastNode( + nextIn: Set[IFDSFact], + oldIn: Option[Set[IFDSFact]], + state: State + ): Unit = {} + + /** + * Processes a statement with a call. + * + * @param basicBlock The basic block, which contains the `call`. + * @param call The call statement. + * @param callees All possible callees. + * @param in The facts, which hold before the call statement. + * @param calleeWithUpdateFact If present, the `callees` will only be analyzed with this fact + * instead of the facts returned by callToStartFacts. + * @return A map, mapping from each successor statement of the `call` to the facts, which hold + * at their start. + */ + protected def handleCall( + basicBlock: BasicBlock, + call: JavaStatement, + callees: SomeSet[Method], + in: Set[IFDSFact], + calleeWithUpdateFact: Option[IFDSFact] + )( + implicit + state: State + ): Map[JavaStatement, Set[IFDSFact]] = { + val successors = nextStatements(call) + val inputFacts = beforeHandleCall(call, in) + // Facts valid at the start of each successor + var summaryEdges: Map[JavaStatement, Set[IFDSFact]] = Map.empty + + /* + * If calleeWithUpdateFact is present, this means that the basic block already has been + * analyzed with the `inputFacts`. + */ + if (calleeWithUpdateFact.isEmpty) + for (successor ← successors) { + numberOfCalls.callToReturnFlow += 1 + sumOfInputfactsForCallbacks += in.size + summaryEdges += successor -> + propagateNullFact( + inputFacts, + ifdsProblem.callToReturnFlow(call, successor, inputFacts, state.source) + ) + } + + for (calledMethod ← callees) { + val callee = declaredMethods(calledMethod) + if (!ifdsProblem.insideAnalysisContext(callee)) { + // Let the concrete analysis decide what to do. + for { + successor ← successors + } summaryEdges += + successor -> (summaryEdges(successor) ++ + ifdsProblem.callOutsideOfAnalysisContext(call, callee, successor, in)) + } else { + val callToStart = + if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) + else { + propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) + } + var allNewExitFacts: Map[JavaStatement, Set[IFDSFact]] = Map.empty + // Collect exit facts for each input fact separately + for (fact ← callToStart) { + /* + * If this is a recursive call with the same input facts, we assume that the + * call only produces the facts that are already known. The call site is added to + * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts + * become known for the input fact. + */ + if ((calledMethod eq state.method) && fact == state.source._2) { + val newDependee = + if (state.pendingIfdsCallSites.contains(state.source)) + state.pendingIfdsCallSites(state.source) + + ((basicBlock, call.index)) + else Set((basicBlock, call.index)) + state.pendingIfdsCallSites = + state.pendingIfdsCallSites.updated(state.source, newDependee) + allNewExitFacts = mergeMaps(allNewExitFacts, collectResult) + } else { + val e = (callee, fact) + val callFlows = propertyStore(e, propertyKey.key) + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + val oldValue = state.pendingIfdsDependees.get(e) + val oldExitFacts: Map[JavaStatement, Set[IFDSFact]] = oldValue match { + case Some(ep: InterimEUBP[_, IFDSProperty[IFDSFact]]) ⇒ ep.ub.flows + case _ ⇒ Map.empty + } + val exitFacts: Map[JavaStatement, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[IFDSFact]] ⇒ + if (state.pendingIfdsCallSites.contains(e) + && state.pendingIfdsCallSites(e).nonEmpty) { + val newDependee = + state.pendingIfdsCallSites(e) - ((basicBlock, call.index)) + state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) + } + state.pendingIfdsDependees -= e + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[IFDSFact]] ⇒ + /* + * Add the call site to `pendingIfdsCallSites` and + * `pendingIfdsDependees` and continue with the facts in the interim + * result for now. When the analysis for the callee finishes, the + * analysis for this call site will be triggered again. + */ + addIfdsDependee(e, callFlows, basicBlock, call.index) + ep.ub.flows + case _ ⇒ + addIfdsDependee(e, callFlows, basicBlock, call.index) + Map.empty + } + // Only process new facts that are not in `oldExitFacts` + allNewExitFacts = mergeMaps( + allNewExitFacts, + filterNewInformation(exitFacts, oldExitFacts, project) + ) + /* + * If new exit facts were discovered for the callee-fact-pair, all call + * sites depending on this pair have to be re-evaluated. oldValue is + * undefined if the callee-fact pair has not been queried before or returned + * a FinalEP. + */ + if (oldValue.isDefined && oldExitFacts != exitFacts) { + reAnalyzeCalls( + state.pendingIfdsCallSites(e), + e._1.definedMethod, + Some(e._2) + ) + } + } + } + summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) + } + } + summaryEdges + } + + /** + * This method is called at the beginning of handleCall. + * A subclass can overwrite this method, to change the input facts of the call. + * + * @param call The call statement. + * @param in The input facts, which hold before the `call`. + * @return The changed set of input facts. + */ + protected def beforeHandleCall(call: JavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = in + + /** + * Merges two maps that have sets as values. + * + * @param map1 The first map. + * @param map2 The second map. + * @return A map containing the keys of both maps. Each key is mapped to the union of both maps' + * values. + */ + protected def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { + var result = map1 + for ((key, values) ← map2) { + result.get(key) match { + case Some(resultValues) ⇒ + if (resultValues.size > values.size) + result = result.updated(key, resultValues ++ values) + else + result = result.updated(key, values ++ resultValues) + case None ⇒ + result = result.updated(key, values) + } + } + result + } + + /** + * Analyzes a queue of BasicBlocks. + * + * @param worklist A queue of the following elements: + * bb The basic block that will be analyzed. + * in New data flow facts found to hold at the beginning of the basic block. + * calleeWithUpdateIndex If the basic block is analyzed because there is new information + * for a callee, this is the call site's index. + * calleeWithUpdate If the basic block is analyzed because there is new information for a + * callee, this is the callee. + * calleeWithUpdateFact If the basic block is analyzed because there is new information + * for a callee with a specific input fact, this is the input fact. + */ + private def process( + worklist: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] + )(implicit state: State): Unit = { + while (worklist.nonEmpty) { + val (basicBlock, in, calleeWithUpdateIndex, calleeWithUpdate, calleeWithUpdateFact) = + worklist.dequeue() + val oldOut = state.outgoingFacts.getOrElse(basicBlock, Map.empty) + val nextOut = analyzeBasicBlock( + basicBlock, + in, + calleeWithUpdateIndex, + calleeWithUpdate, + calleeWithUpdateFact + ) + val allOut = mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) + state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) + + for (successor ← nextNodes(basicBlock)) { + if (isLastNode(successor)) { + // Re-analyze recursive call sites with the same input fact. + val nextOutSuccessors = nextOut.get(successor) + if (nextOutSuccessors.isDefined && nextOutSuccessors.get.nonEmpty) { + val oldOutSuccessors = oldOut.get(successor) + if (oldOutSuccessors.isEmpty || containsNewInformation( + nextOutSuccessors.get, + oldOutSuccessors.get, + project + )) { + val source = state.source + foundNewInformationForLastNode( + nextOutSuccessors.get, + oldOutSuccessors, + state + ) + reAnalyzeCalls( + state.pendingIfdsCallSites(source), + source._1.definedMethod, + Some(source._2) + ) + } + } + } else { + val successorBlock = successor.asBasicBlock + val nextIn = nextOut.getOrElse(successorBlock, Set.empty) + val oldIn = state.incomingFacts.getOrElse(successorBlock, Set.empty) + val newIn = notSubsumedBy(nextIn, oldIn, project) + val mergedIn = + if (nextIn.size > oldIn.size) nextIn ++ oldIn + else oldIn ++ nextIn + state.incomingFacts = + state.incomingFacts.updated(successorBlock, subsume(mergedIn, project)) + /* + * Only process the successor with new facts. + * It is analyzed at least one time because of the null fact. + */ + if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None, None)) + } + } + } + } + + /** + * Computes for one basic block the facts valid on each CFG edge leaving the block if `sources` + * held before the block. + * + * @param basicBlock The basic block, that will be analyzed. + * @param in The facts, that hold before the block. + * @param calleeWithUpdateIndex If the basic block is analyzed because there is new information + * for a callee, this is the call site's index. + * @param calleeWithUpdate If the basic block is analyzed because there is new information for + * a callee, this is the callee. + * @param calleeWithUpdateFact If the basic block is analyzed because there is new information + * for a callee with a specific input fact, this is the input fact. + * @return A map, mapping each successor node to its input facts. Instead of catch nodes, this + * map contains their handler nodes. + */ + private def analyzeBasicBlock( + basicBlock: BasicBlock, + in: Set[IFDSFact], + calleeWithUpdateIndex: Option[Int], + calleeWithUpdate: Option[Method], + calleeWithUpdateFact: Option[IFDSFact] + )( + implicit + state: State + ): Map[CFGNode, Set[IFDSFact]] = { + + /* + * Collects information about a statement. + * + * @param index The statement's index. + * @return A tuple of the following elements: + * statement: The statement at `index`. + * calees: The methods possibly called at this statement, if it contains a call. + * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will + * be returned. + * calleeFact: If `index` equals `calleeWithUpdateIndex`, only + * `calleeWithUpdateFact` will be returned, None otherwise. + */ + def collectInformation(index: Int): (JavaStatement, Option[SomeSet[Method]], Option[IFDSFact]) = { + val stmt = state.code(index) + val statement = JavaStatement(state.method, basicBlock, stmt, index, state.code, state.cfg) + val calleesO = + if (calleeWithUpdateIndex.contains(index)) calleeWithUpdate.map(Set(_)) + else getCalleesIfCallStatement(statement) + val calleeFact = + if (calleeWithUpdateIndex.contains(index)) calleeWithUpdateFact + else None + (statement, calleesO, calleeFact) + } + + val last = lastIndex(basicBlock) + var flows: Set[IFDSFact] = in + var index = firstIndex(basicBlock) + + // Iterate over all statements but the last one, only keeping the resulting DataFlowFacts. + while (index != last) { + val (statement, calleesO, calleeFact) = collectInformation(index) + val next = nextIndex(index) + flows = if (calleesO.isEmpty) { + val successor = + JavaStatement(state.method, basicBlock, state.code(next), next, state.code, state.cfg) + numberOfCalls.normalFlow += 1 + sumOfInputfactsForCallbacks += in.size + ifdsProblem.normalFlow(statement, successor, flows) + } else + // Inside a basic block, we only have one successor --> Take the head + handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head + index = next + } + + // Analyze the last statement for each possible successor statement. + val (statement, calleesO, callFact) = collectInformation(last) + var result: Map[CFGNode, Set[IFDSFact]] = + if (calleesO.isEmpty) { + var result: Map[CFGNode, Set[IFDSFact]] = Map.empty + for (node ← nextNodes(basicBlock)) { + numberOfCalls.normalFlow += 1 + sumOfInputfactsForCallbacks += in.size + result += node -> ifdsProblem.normalFlow(statement, firstStatement(node), flows) + } + result + } else + handleCall(basicBlock, statement, calleesO.get, flows, callFact) + .map(entry ⇒ entry._1.node -> entry._2) + + // Propagate the null fact. + result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) + result + } + + /** + * Re-analyzes some basic blocks. + * + * @param basicBlocks The basic blocks, that will be re-analyzed. + */ + private def reAnalyzebasicBlocks(basicBlocks: Set[BasicBlock])(implicit state: State): Unit = { + val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty + for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) + process(queue) + } + + /** + * Re-analyzes some call sites with respect to one specific callee. + * + * @param callSites The call sites, which are analyzed. + * @param callee The callee, which will be considered at the `callSites`. + * @param fact If defined, the `callee` will only be analyzed for this fact. + */ + private def reAnalyzeCalls( + callSites: Set[(BasicBlock, Int)], + callee: Method, + fact: Option[IFDSFact] + )(implicit state: State): Unit = { + val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty + for ((block, index) ← callSites) + queue.enqueue((block, state.incomingFacts(block), Some(index), Some(callee), fact)) + process(queue) + } + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param basicBlock The basic block containing the statement. + * @param index The statement's index. + * @return All methods possibly called at the statement index or None, if the statement does not + * contain a call. + */ + private def getCalleesIfCallStatement(statement: JavaStatement)( + implicit + state: State + ): Option[SomeSet[Method]] = { + //val statement = state.code(index) + statement.stmt.astID match { + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + Some(definedMethods(ifdsProblem.getCallees(statement, state.source._1))) + case Assignment.ASTID | ExprStmt.ASTID ⇒ + getExpression(statement.stmt).astID match { + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | + VirtualFunctionCall.ASTID ⇒ + Some(definedMethods(ifdsProblem.getCallees(statement, state.source._1))) + case _ ⇒ None + } + case _ ⇒ None + } + } + + /** + * Maps some declared methods to their defined methods. + * + * @param declaredMethods Some declared methods. + * @return All defined methods of `declaredMethods`. + */ + private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): SomeSet[Method] = { + val result = scala.collection.mutable.Set.empty[Method] + declaredMethods + .filter( + declaredMethod ⇒ + declaredMethod.hasSingleDefinedMethod || + declaredMethod.hasMultipleDefinedMethods + ) + .foreach( + declaredMethod ⇒ + declaredMethod + .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) + ) + result + } + + /** + * Retrieves the expression of an assignment or expression statement. + * + * @param statement The statement. Must be an Assignment or ExprStmt. + * @return The statement's expression. + */ + private def getExpression(statement: Stmt[V]): Expr[V] = statement.astID match { + case Assignment.ASTID ⇒ statement.asAssignment.expr + case ExprStmt.ASTID ⇒ statement.asExprStmt.expr + case _ ⇒ throw new UnknownError("Unexpected statement") + } + + /** + * If `from` contains a null fact, it will be added to `to`. + * + * @param from The set, which may contain the null fact initially. + * @param to The set, to which the null fact may be added. + * @return `to` with the null fact added, if it is contained in `from`. + */ + private def propagateNullFact(from: Set[IFDSFact], to: Set[IFDSFact]): Set[IFDSFact] = { + if (from.contains(ifdsProblem.nullFact)) to + ifdsProblem.nullFact + else to + } + + /** + * Adds a method-fact-pair as to the IFDS call sites and dependees. + * + * @param entity The method-fact-pair. + * @param calleeProperty The property, that was returned for `entity`. + * @param callBB The basic block of the call site. + * @param callIndex The index of the call site. + */ + private def addIfdsDependee( + entity: (DeclaredMethod, IFDSFact), + calleeProperty: EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]], + callBB: BasicBlock, + callIndex: Int + )(implicit state: State): Unit = { + val callSites = state.pendingIfdsCallSites + state.pendingIfdsCallSites = callSites.updated( + entity, + callSites.getOrElse(entity, Set.empty) + ((callBB, callIndex)) + ) + state.pendingIfdsDependees += entity -> calleeProperty + } + + /** + * This method will be called if a non-overwritten declared method in a sub type shall be + * analyzed. Analyzes the defined method of the supertype instead. + * + * @param source A pair consisting of the declared method of the subtype and an input fact. + * @return The result of the analysis of the defined method of the supertype. + */ + private def baseMethodResult( + source: (DeclaredMethod, IFDSFact) + ): ProperPropertyComputationResult = { + + def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { + case FinalP(p) ⇒ Result(source, p) + + case ep @ InterimUBP(ub: Property) ⇒ + InterimResult.forUB(source, ub, Set(ep), c) + + case epk ⇒ + InterimResult.forUB(source, createPropertyValue(Map.empty), Set(epk), c) + } + c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) + } +} + +object AbstractIFDSAnalysis { + + /** + * The type of the TAC domain. + */ + type V = DUVar[ValueInformation] + + /** + * When true, the cross product of exit and successor in returnFLow will be optimized. + */ + var OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW: Boolean = true + + /** + * Converts the index of a method's formal parameter to its tac index in the method's scope and + * vice versa. + * + * @param index The index of a formal parameter in the parameter list or of a variable. + * @param isStaticMethod States, whether the method is static. + * @return A tac index if a parameter index was passed or a parameter index if a tac index was + * passed. + */ + def switchParamAndVariableIndex(index: Int, isStaticMethod: Boolean): Int = + (if (isStaticMethod) -2 else -1) - index + +} +*/ +/** + * A statement that is passed to the concrete analysis. + * + * @param method The method containing the statement. + * @param node The basic block containing the statement. + * @param stmt The TAC statement. + * @param index The index of the Statement in the code. + * @param code The method's TAC code. + * @param cfg The method's CFG. + */ +case class LLVMStatement( + function: Function, + //node: CFGNode, + instruction: Instruction, + //index: Int, + //code: Array[Stmt[V]], + //cfg: CFG[Stmt[V], TACStmts[V]] + ) /*{ + + override def hashCode(): Int = method.hashCode() * 31 + index + + override def equals(o: Any): Boolean = o match { + case s: JavaStatement ⇒ s.index == index && s.method == method + case _ ⇒ false + } + + override def toString: String = s"${method.toJava}" + +}*/ +/* +/** + * Contains int variables, which count, how many times some method was called. + */ +class NumberOfCalls { + var normalFlow = 0 + var callFlow = 0 + var returnFlow = 0 + var callToReturnFlow = 0 +} +*/ +abstract class MultilingualIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysis[IFDSFact] { + override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq(LLVMProjectKey) +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala new file mode 100644 index 0000000000..c0ce123e6e --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala @@ -0,0 +1,224 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, AbstractIFDSFact} +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation + +/* +import org.opalj.br.DeclaredMethod +import org.opalj.br.cfg.{BasicBlock, CFG, CFGNode} +import org.opalj.tac.{Return, ReturnValue, Stmt, TACStmts} +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +*/ +/** + * An IFDS analysis, which analyzes the code in the control flow direction. + * + * @author Mario Trageser + */ + +abstract class ForwardNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: NativeIFDSProblem[IFDSFact], propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact]) extends AbstractNativeIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { +/* + /** + * The analysis starts at the entry block. + */ + override protected def startBlocks( + sourceFact: IFDSFact, + cfg: CFG[Stmt[V], TACStmts[V]] + ): Set[BasicBlock] = + Set(cfg.startBlock) + + /** + * Collects the output facts at the predecessors of the normal and abnormal return node. + */ + override protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { + mergeMaps( + resultOfExitNode(state.cfg.normalReturnNode), + resultOfExitNode(state.cfg.abnormalReturnNode) + ) + } + + /** + * Returns the next successor nodes of `basicBlock`. + * Catch nodes are skipped to their successor. + */ + override protected def nextNodes(basicBlock: BasicBlock): Set[CFGNode] = + basicBlock.successors.map { successor ⇒ + if (successor.isCatchNode) successor.successors.head + else successor + } + + /** + * The exit nodes are the last nodes. + */ + override protected def isLastNode(node: CFGNode): Boolean = node.isExitNode + + /** + * If the node is a basic block, it will be returned. + * Otherwise, its first successor will be returned. + */ + override protected def skipCatchNode(node: CFGNode): Set[BasicBlock] = + Set((if (node.isBasicBlock) node else node.successors.head).asBasicBlock) + + /** + * The first index of a basic block is its start index. + */ + override protected def firstIndex(basicBlock: BasicBlock): Int = basicBlock.startPC + + /** + * The last index of a basic block is its end index. + */ + override protected def lastIndex(basicBlock: BasicBlock): Int = basicBlock.endPC + + /** + * The next index in the direction of the control flow. + */ + override protected def nextIndex(index: Int): Int = index + 1 + + /** + * If the `node` is a basic block, its first statement will be returned. + * If it is a catch node, the first statement of its handler will be returned. + * If it is an exit node, an artificial statement without code will be returned. + */ + override protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement = { + if (node.isBasicBlock) { + val index = node.asBasicBlock.startPC + JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg) + } else if (node.isCatchNode) firstStatement(node.successors.head) + else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg) + else throw new IllegalArgumentException(s"Unknown node type: $node") + } + + /** + * The successor statements in the direction of the control flow. + */ + override protected def nextStatements(statement: JavaStatement)(implicit state: State): Set[JavaStatement] = { + val index = statement.index + val basicBlock = statement.node.asBasicBlock + if (index == basicBlock.endPC) + basicBlock.successors.map(firstStatement(_)) + else { + val nextIndex = index + 1 + Set(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + statement.code, statement.cfg)) + } + } + + /** + * Calls callFlow. + */ + override protected def callToStartFacts(call: JavaStatement, callee: DeclaredMethod, + in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { + numberOfCalls.callFlow += 1 + sumOfInputfactsForCallbacks += in.size + ifdsProblem.callFlow(call, callee, in, state.source) + } + + /** + * Combines each normal exit node with each normal successor and each abnormal exit statement + * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. + */ + override protected def addExitToReturnFacts( + summaryEdges: Map[JavaStatement, Set[IFDSFact]], + successors: Set[JavaStatement], call: JavaStatement, + callee: DeclaredMethod, + exitFacts: Map[JavaStatement, Set[IFDSFact]] + )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { + // First process for normal returns, then abnormal returns. + var result = summaryEdges + if (AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { + val successors = nextStatementsWithNode(call) + for { + successor ← successors + exitStatement ← exitFacts.keys + if (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && + (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || + (successor._2.isCatchNode || successor._2.isAbnormalReturnExitNode) && + (exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID) + } result = addSummaryEdge(result, call, exitStatement, successor._1, callee, exitFacts) + } else { + val successors = nextStatements(call) + for { + successor ← successors + exitStatement ← exitFacts.keys + } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) + } + result + } + + /** + * Like nextStatements, but maps each successor statement to the corresponding successor node. + * When determining the successor node, catch nodes are not skipped. + */ + private def nextStatementsWithNode(statement: JavaStatement)(implicit state: State): Map[JavaStatement, CFGNode] = { + val index = statement.index + val basicBlock = statement.node.asBasicBlock + if (index == basicBlock.endPC) + basicBlock.successors.iterator + .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap + else { + val nextIndex = index + 1 + Map(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + statement.code, statement.cfg) → basicBlock) + } + } + + /** + * Collects the facts valid at an exit node based on the current results. + * + * @param exit The exit node. + * @return A map, mapping from each predecessor of the `exit` node to the facts valid at the + * `exit` node under the assumption that the predecessor was executed before. + */ + private def resultOfExitNode(exit: CFGNode)( + implicit + state: State + ): Map[JavaStatement, Set[IFDSFact]] = { + var result = Map.empty[JavaStatement, Set[IFDSFact]] + exit.predecessors foreach { predecessor ⇒ + if (predecessor.isBasicBlock) { + val basicBlock = predecessor.asBasicBlock + val exitFacts = state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)) + if (exitFacts.isDefined) { + val lastIndex = basicBlock.endPC + val stmt = JavaStatement(state.method, basicBlock, state.code(lastIndex), + lastIndex, state.code, state.cfg) + result += stmt → exitFacts.get + } + } + } + result + } + + /** + * Adds a summary edge for a call to a map representing summary edges. + * + * @param summaryEdges The current map representing the summary edges. + * Maps from successor statements to facts, which hold at their beginning. + * @param call The call, calling the `callee`. + * @param exitStatement The exit statement for the new summary edge. + * @param successor The successor statement of the call for the new summary edge. + * @param callee The callee, called by `call`. + * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly + * found exit facts. + * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. + */ + private def addSummaryEdge(summaryEdges: Map[JavaStatement, Set[IFDSFact]], call: JavaStatement, + exitStatement: JavaStatement, successor: JavaStatement, + callee: DeclaredMethod, + allNewExitFacts: Map[JavaStatement, Set[IFDSFact]]): Map[JavaStatement, Set[IFDSFact]] = { + val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) + numberOfCalls.returnFlow += 1 + sumOfInputfactsForCallbacks += in.size + val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) + val newFacts = + if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { + val summaryForSuccessor = summaryEdges(successor) + if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned + else returned ++ summaryForSuccessor + } else returned + summaryEdges.updated(successor, newFacts) + } + +} +*/ \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala new file mode 100644 index 0000000000..972e67c618 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -0,0 +1,29 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.br.analyses.{SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.{PropertyStore} +import org.opalj.ll.LLVMProjectKey +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem} +import org.opalj.ll.llvm.Function + +abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, Function, LLVMStatement](project) { + final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + val llvmProject = project.get(LLVMProjectKey) + + /** + * Checks, if a callee is inside this analysis' context. + * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. + * By default, native methods are not inside the analysis context. + * + * @param callee The callee. + * @return True, if the callee is inside the analysis context. + */ + override def insideAnalysisContext(callee: Function): Boolean = true + + override def getCallees( + statement: LLVMStatement, + caller: Function + ): Iterator[Function] = ??? +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala index 90827a52af..e49ab829f4 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala @@ -3,22 +3,18 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod -import org.opalj.fpcf.PropertyStore -import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysis, JavaStatement} +import org.opalj.br.fpcf.FPCFAnalysis +import org.opalj.fpcf.{ProperPropertyComputationResult, PropertyBounds, PropertyStore} +import org.opalj.ll.LLVMProjectKey +import org.opalj.ll.fpcf.analyses.ifds.{ForwardNativeIFDSAnalysis, MultilingualIFDSAnalysis} +import org.opalj.ll.fpcf.properties.NativeTaint +import org.opalj.ll.llvm.Function +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, ForwardIFDSAnalysis, IFDSAnalysis, JavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProblem, NullFact, Variable} import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} -/** - * An analysis that checks, if the return value of the method `source` can flow to the parameter of - * the method `sink`. - * - * @author Mario Trageser - * @author Marc Clement - */ -class MultilingualForwardTaintAnalysis private (implicit val project: SomeProject) - extends ForwardIFDSAnalysis(new MultilingualForwardTaintProblem(project), Taint) - -class MultilingualForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { +class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { + val llvmProject = p.get(LLVMProjectKey) /** * The analysis starts with all public methods in TaintAnalysisTestClass. @@ -61,14 +57,88 @@ class MultilingualForwardTaintProblem(p: SomeProject) extends ForwardTaintProble // Multilingual additions here override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { - super.insideAnalysisContext(callee) //|| callee.definedMethod.isNative + super.insideAnalysisContext(callee) || callee.definedMethod.isNative } + override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { + val method = source._1.definedMethod + if (method.isNative) { + // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names + val nativeMethodName = "Java_" + method.classFile.fqn + "_" + method.name + val function = llvmProject.function(nativeMethodName) + return Some(delegate(source, ((function.get, source._2), NativeTaint), identity, propertyKey)) + } + super.specialCase(source, propertyKey) + } } -object MultilingualForwardTaintAnalysis extends IFDSAnalysis[Fact] { +class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaintProblem(p) { + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(Function, Fact)] = Seq.empty + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Function): Boolean = + callee.name == "sanitize" + + /** + * We do not sanitize paramters. + */ + override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact( + callee: DeclaredMethod, + call: JavaStatement, + in: Set[Fact] + ): Option[FlowFact] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) + else None + + // Multilingual additions here + + override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { + super.insideAnalysisContext(callee) || callee.definedMethod.isNative + } + + override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { + val method = source._1.definedMethod + if (method.isNative) { + // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names + val nativeMethodName = "Java_" + method.classFile.fqn + "_" + method.name + val function = llvmProject.function(nativeMethodName) + return Some(delegate(source, ((function.get, source._2), NativeTaint), identity, propertyKey)) + } + super.specialCase(source, propertyKey) + } +} + +class SimpleJavaForwardTaintAnalysis private (implicit val project: SomeProject) + extends ForwardIFDSAnalysis(new SimpleJavaForwardTaintProblem(project), Taint) + +class SimpleNativeForwardTaintAnalysis private (implicit val project: SomeProject) + extends ForwardNativeIFDSAnalysis(new SimpleNativeForwardTaintProblem(project), NativeTaint) + +object MultilingualForwardTaintAnalysis extends MultilingualIFDSAnalysis[Fact] { + + override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis()(p) + + override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint - override def init(p: SomeProject, ps: PropertyStore) = new MultilingualForwardTaintAnalysis()(p) + override val uses: Set[PropertyBounds] = super.uses ++ PropertyBounds.ub(NativeTaint) - override def property: IFDSPropertyMetaInformation[Fact] = Taint + override def register(p: SomeProject, ps: PropertyStore, analysis: AbstractIFDSAnalysis[Fact]): FPCFAnalysis = super.register(p, ps, analysis) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala new file mode 100644 index 0000000000..e978cb47a7 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -0,0 +1,338 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds.taint + +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.SomeProject +import org.opalj.ll.fpcf.analyses.ifds.NativeIFDSProblem +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, NullFact, TaintProblem} +import org.opalj.tac.{ArrayLength, ArrayLoad, ArrayStore, Assignment, BinaryExpr, Compare, Expr, GetField, GetStatic, NewArray, PrefixExpr, PrimitiveTypecastExpr, PutField, PutStatic, ReturnValue, Var} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaIFDSProblem, JavaStatement} + +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[Fact](project) { + override def nullFact: Fact = NullFact + + /** + * If a variable gets assigned a tainted value, the variable will be tainted. + */ + override def normalFlow(statement: JavaStatement, successor: JavaStatement, + in: Set[Fact]): Set[Fact] = + statement.stmt.astID match { + case Assignment.ASTID ⇒ + in ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) + case ArrayStore.ASTID ⇒ + val store = statement.stmt.asArrayStore + val definedBy = store.arrayRef.asVar.definedBy + val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) + if (isTainted(store.value, in)) { + if (arrayIndex.isDefined) + // Taint a known array index + definedBy.foldLeft(in) { (c, n) ⇒ + c + ArrayElement(n, arrayIndex.get) + } + else + // Taint the whole array if the index is unknown + definedBy.foldLeft(in) { (c, n) ⇒ + c + Variable(n) + } + } else if (arrayIndex.isDefined && definedBy.size == 1) + // Untaint if possible + in - ArrayElement(definedBy.head, arrayIndex.get) + else in + case PutField.ASTID ⇒ + val put = statement.stmt.asPutField + val definedBy = put.objRef.asVar.definedBy + if (isTainted(put.value, in)) + definedBy.foldLeft(in) { (in, defSite) ⇒ + in + InstanceField(defSite, put.declaringClass, put.name) + } + else + in + case PutStatic.ASTID ⇒ + val put = statement.stmt.asPutStatic + if (isTainted(put.value, in)) + in + StaticField(put.declaringClass, put.name) + else + in + case _ ⇒ in + } + + /** + * Propagates tainted parameters to the callee. If a call to the sink method with a tainted + * parameter is detected, no call-to-start + * edges will be created. + */ + override def callFlow(call: JavaStatement, callee: DeclaredMethod, + in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = { + val callObject = asCall(call.stmt) + val allParams = callObject.allParams + var facts = Set.empty[Fact] + + if (relevantCallee(callee)) { + val allParamsWithIndices = allParams.zipWithIndex + in.foreach { + // Taint formal parameter if actual parameter is tainted + case Variable(index) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( + paramIndex, + callee.definedMethod.isStatic + )) + case _ ⇒ // Nothing to do + } + + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += ArrayElement( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), + taintedIndex + ) + case _ ⇒ // Nothing to do + } + + case InstanceField(index, declClass, taintedField) ⇒ + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.foreach { + case (param, pIndex) if param.asVar.definedBy.contains(index) && + (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || + project.classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ + facts += InstanceField( + AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), + declClass, taintedField + ) + case _ ⇒ // Nothing to do + } + + case sf: StaticField ⇒ facts += sf + + case _ ⇒ // Nothing to do + } + } + + facts + } + + /** + * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. + * If the callee's return value was tainted and it is assigned to a variable in the callee, the + * variable will be tainted. + * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds + * at this method. + * Creates new taints and FlowFacts, if necessary. + * If the sanitize method was called, nothing will be tainted. + */ + override def returnFlow(call: JavaStatement, callee: DeclaredMethod, exit: JavaStatement, + successor: JavaStatement, in: Set[Fact]): Set[Fact] = { + + /** + * Checks whether the callee's formal parameter is of a reference type. + */ + def isRefTypeParam(index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (callee.definedMethod.isStatic) 0 else 1 + callee.descriptor.parameterType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + - parameterOffset + ).isReferenceType + } + + if (sanitizesReturnValue(callee)) return Set.empty + val callStatement = asCall(call.stmt) + val allParams = callStatement.allParams + var flows: Set[Fact] = Set.empty + in.foreach { + // Taint actual parameter if formal parameter is tainted + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(Variable) + + // Taint element of actual parameter if element of formal parameter is tainted + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) + + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + // Taint field of actual parameter if field of formal parameter is tainted + val param = + allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) + param.asVar.definedBy.foreach { defSite ⇒ + flows += InstanceField(defSite, declClass, taintedField) + } + + case sf: StaticField ⇒ flows += sf + + // Track the call chain to the sink back + case FlowFact(flow) if !flow.contains(call.method) ⇒ + flows += FlowFact(call.method +: flow) + case _ ⇒ + } + + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + in.foreach { + case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + flows += Variable(call.index) + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + flows += ArrayElement(call.index, taintedIndex) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + flows += InstanceField(call.index, declClass, taintedField) + case NullFact ⇒ + val taints = createTaints(callee, call) + if (taints.nonEmpty) flows ++= taints + case _ ⇒ // Nothing to do + } + } + val flowFact = createFlowFact(callee, call, in) + if (flowFact.isDefined) flows += flowFact.get + + flows + } + + /** + * Removes taints according to `sanitizeParamters`. + */ + override def callToReturnFlow(call: JavaStatement, successor: JavaStatement, + in: Set[Fact], + source: (DeclaredMethod, Fact)): Set[Fact] = + in -- sanitizeParamters(call, in) + + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a vairbale. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] + + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, + in: Set[Fact]): Option[FlowFact] + + /** + * If a parameter is tainted, the result will also be tainted. + * We assume that the callee does not call the source method. + */ + override def callOutsideOfAnalysisContext(statement: JavaStatement, callee: DeclaredMethod, + successor: JavaStatement, + in: Set[Fact]): Set[Fact] = { + val allParams = asCall(statement.stmt).receiverOption ++ asCall(statement.stmt).params + if (statement.stmt.astID == Assignment.ASTID && in.exists { + case Variable(index) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case ArrayElement(index, _) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case _ ⇒ false + }) Set(Variable(statement.index)) + else Set.empty + } + + /** + * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. + * True by default. This method can be overwritten by a subclass. + * + * @param callee The callee. + * @return True, by default. + */ + protected def relevantCallee(callee: DeclaredMethod): Boolean = true + + /** + * Creates new facts for an assignment. A new fact for the assigned variable will be created, + * if the expression contains a tainted variable + * + * @param expression The source expression of the assignment + * @param statement The assignment statement + * @param in The incoming facts + * @return The new facts, created by the assignment + */ + private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: Set[Fact]): Set[Fact] = + expression.astID match { + case Var.ASTID ⇒ + val definedBy = expression.asVar.definedBy + in.collect { + case Variable(index) if definedBy.contains(index) ⇒ + Variable(statement.index) + } + case ArrayLoad.ASTID ⇒ + val loadExpression = expression.asArrayLoad + val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy + if (in.exists { + // One specific array element may be tainted + case ArrayElement(index, taintedElement) ⇒ + val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) + arrayDefinedBy.contains(index) && + (loadedIndex.isEmpty || taintedElement == loadedIndex.get) + // Or the whole array + case Variable(index) ⇒ arrayDefinedBy.contains(index) + case _ ⇒ false + }) Set(Variable(statement.index)) + else + Set.empty + case GetField.ASTID ⇒ + val get = expression.asGetField + val objectDefinedBy = get.objRef.asVar.definedBy + if (in.exists { + // The specific field may be tainted + case InstanceField(index, _, taintedField) ⇒ + taintedField == get.name && objectDefinedBy.contains(index) + // Or the whole object + case Variable(index) ⇒ objectDefinedBy.contains(index) + case _ ⇒ false + }) + Set(Variable(statement.index)) + else + Set.empty + case GetStatic.ASTID ⇒ + val get = expression.asGetStatic + if (in.contains(StaticField(get.declaringClass, get.name))) + Set(Variable(statement.index)) + else Set.empty + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | + NewArray.ASTID | ArrayLength.ASTID ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) + case _ ⇒ Set.empty + } + + /** + * Checks, if the result of some variable expression could be tainted. + * + * @param expression The variable expression. + * @param in The current data flow facts. + * @return True, if the expression could be tainted + */ + private def isTainted(expression: Expr[V], in: Set[Fact]): Boolean = { + val definedBy = expression.asVar.definedBy + expression.isVar && in.exists { + case Variable(index) ⇒ definedBy.contains(index) + case ArrayElement(index, _) ⇒ definedBy.contains(index) + case InstanceField(index, _, _) ⇒ definedBy.contains(index) + case _ ⇒ false + } + } +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala new file mode 100644 index 0000000000..4cf9281a9f --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala @@ -0,0 +1,23 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.properties + +import org.opalj.fpcf.PropertyKey +import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation} + +case class NativeTaint(flows: Map[LLVMStatement, Set[Fact]]) extends IFDSProperty[LLVMStatement, Fact] { + + override type Self = NativeTaint + override def create(result: Map[LLVMStatement, Set[Fact]]): IFDSProperty[LLVMStatement, Fact] = new NativeTaint(result) + + override def key: PropertyKey[NativeTaint] = NativeTaint.key +} + +object NativeTaint extends IFDSPropertyMetaInformation[LLVMStatement, Fact] { + + override type Self = NativeTaint + override def create(result: Map[LLVMStatement, Set[Fact]]): IFDSProperty[LLVMStatement, Fact] = new NativeTaint(result) + + val key: PropertyKey[NativeTaint] = PropertyKey.create("NativeTaint", new NativeTaint(Map.empty)) +} \ No newline at end of file diff --git a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyKind.scala b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyKind.scala index 497459e79f..ac8d388950 100644 --- a/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyKind.scala +++ b/OPAL/si/src/main/scala/org/opalj/fpcf/PropertyKind.scala @@ -8,7 +8,7 @@ package org.opalj.fpcf * are generally only to be used if lower bounds cannot be computed or a very extensive and * are never of interest to any potential client. E.g., in case of an IFDS analysis, * computing the lower bound is not meaningful; in case of a call graph analysis, the lower - * bound is usually either prohibitively expensive or is not usefull to any analysis. + * bound is usually either prohibitively expensive or is not useful to any analysis. */ trait PropertyKind extends Any /* we now have a universal trait */ { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index 630c9dbd51..8138129340 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -7,28 +7,14 @@ package ifds import java.io.File import java.io.PrintWriter -import scala.collection.{Set ⇒ SomeSet} +import scala.collection.{Set => SomeSet} import scala.collection.mutable import com.typesafe.config.ConfigValueFactory import javax.swing.JOptionPane import org.opalj.util.Milliseconds import org.opalj.util.PerformanceEvaluation.time -import org.opalj.fpcf.EOptionP -import org.opalj.fpcf.EPK -import org.opalj.fpcf.FinalE -import org.opalj.fpcf.FinalEP -import org.opalj.fpcf.FinalP -import org.opalj.fpcf.InterimEUBP -import org.opalj.fpcf.InterimResult -import org.opalj.fpcf.InterimUBP -import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.fpcf.Property -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.Result -import org.opalj.fpcf.SomeEOptionP -import org.opalj.fpcf.SomeEPS +import org.opalj.fpcf.{EOptionP, EPK, FinalE, FinalEP, FinalP, InterimEUBP, InterimResult, ProperPropertyComputationResult, PropertyBounds, PropertyStore, Result, SomeEPS} import org.opalj.fpcf.seq.PKESequentialPropertyStore import org.opalj.value.ValueInformation import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler @@ -67,7 +53,7 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V * @tparam IFDSFact */ -abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement], val propertyKey: IFDSPropertyMetaInformation[IFDSFact]) +abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement], val propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends FPCFAnalysis with Subsumable[IFDSFact] { @@ -118,7 +104,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble val cfg: CFG[Stmt[V], TACStmts[V]], var pendingIfdsCallSites: Map[(DeclaredMethod, IFDSFact), Set[(BasicBlock, Int)]], var pendingTacCallSites: Map[DeclaredMethod, Set[BasicBlock]] = Map.empty, - var pendingIfdsDependees: Map[(DeclaredMethod, IFDSFact), EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] = Map.empty, + var pendingIfdsDependees: Map[(DeclaredMethod, IFDSFact), EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] = Map.empty, var pendingTacDependees: Map[Method, EOptionP[Method, TACAI]] = Map.empty, var pendingCgCallSites: Set[BasicBlock] = Set.empty, var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, @@ -148,7 +134,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble * @param result Maps each exit statement to the facts, which hold after the exit statement. * @return An IFDSProperty containing the `result`. */ - protected def createPropertyValue(result: Map[JavaStatement, Set[IFDSFact]]): IFDSProperty[IFDSFact] = propertyKey.create(result) + protected def createPropertyValue(result: Map[JavaStatement, Set[IFDSFact]]): IFDSProperty[JavaStatement, IFDSFact] = propertyKey.create(result) /** * Determines the nodes, that will be analyzed after some `basicBlock`. @@ -246,7 +232,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble exitFacts: Map[JavaStatement, Set[IFDSFact]] )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] - /** + /** * Performs an IFDS analysis for a method-fact-pair. * * @param entity The method-fact-pair, that will be analyzed. @@ -258,26 +244,25 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble val (declaredMethod, sourceFact) = entity /* - * The analysis can only handle single defined methods. - * If a method is not single defined, this analysis assumes that it does not create any - * facts. - */ + * The analysis can only handle single defined methods. + * If a method is not single defined, this analysis assumes that it does not create any + * facts. + */ if (!declaredMethod.hasSingleDefinedMethod) return Result(entity, createPropertyValue(Map.empty)); + ifdsProblem.specialCase(entity, propertyKey) match { + case Some(result) => return result; + case _ => + } + val method = declaredMethod.definedMethod val declaringClass: ObjectType = method.classFile.thisType /* - * If this is not the method's declaration, but a non-overwritten method in a subtype, do - * not re-analyze the code. - */ - if (declaringClass ne declaredMethod.declaringClassType) return baseMethodResult(entity); - - /* - * Fetch the method's three address code. If it is not present, return an empty interim - * result. - */ + * Fetch the method's three address code. If it is not present, return an empty interim + * result. + */ val (code, cfg) = propertyStore(method, TACAI.key) match { case FinalP(TheTACAI(tac)) ⇒ (tac.stmts, tac.cfg) @@ -339,7 +324,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble case interimEUBP @ InterimEUBP( e: (DeclaredMethod, IFDSFact) @unchecked, - ub: IFDSProperty[IFDSFact] + ub: IFDSProperty[JavaStatement, IFDSFact] @unchecked ) ⇒ if (ub.flows.values .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { @@ -347,7 +332,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble // Update the pendingIfdsDependee entry to the new interim result. state.pendingIfdsDependees += e -> interimEUBP - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) @@ -456,14 +441,14 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble } else { val e = (callee, fact) val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] val oldValue = state.pendingIfdsDependees.get(e) val oldExitFacts: Map[JavaStatement, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[IFDSFact]]) ⇒ ep.ub.flows + case Some(ep: InterimEUBP[_, IFDSProperty[JavaStatement, IFDSFact]]) ⇒ ep.ub.flows case _ ⇒ Map.empty } val exitFacts: Map[JavaStatement, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[IFDSFact]] ⇒ + case ep: FinalEP[_, IFDSProperty[JavaStatement, IFDSFact]] ⇒ if (state.pendingIfdsCallSites.contains(e) && state.pendingIfdsCallSites(e).nonEmpty) { val newDependee = @@ -472,7 +457,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble } state.pendingIfdsDependees -= e ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[IFDSFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[JavaStatement, IFDSFact]] ⇒ /* * Add the call site to `pendingIfdsCallSites` and * `pendingIfdsDependees` and continue with the facts in the interim @@ -821,7 +806,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble */ private def addIfdsDependee( entity: (DeclaredMethod, IFDSFact), - calleeProperty: EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]], + calleeProperty: EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]], callBB: BasicBlock, callIndex: Int )(implicit state: State): Unit = { @@ -832,29 +817,6 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble ) state.pendingIfdsDependees += entity -> calleeProperty } - - /** - * This method will be called if a non-overwritten declared method in a sub type shall be - * analyzed. Analyzes the defined method of the supertype instead. - * - * @param source A pair consisting of the declared method of the subtype and an input fact. - * @return The result of the analysis of the defined method of the supertype. - */ - private def baseMethodResult( - source: (DeclaredMethod, IFDSFact) - ): ProperPropertyComputationResult = { - - def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { - case FinalP(p) ⇒ Result(source, p) - - case ep @ InterimUBP(ub: Property) ⇒ - InterimResult.forUB(source, ub, Set(ep), c) - - case epk ⇒ - InterimResult.forUB(source, createPropertyValue(Map.empty), Set(epk), c) - } - c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) - } } object AbstractIFDSAnalysis { @@ -927,7 +889,7 @@ abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalys final override type InitializationData = AbstractIFDSAnalysis[IFDSFact] - def property: IFDSPropertyMetaInformation[IFDSFact] + def property: IFDSPropertyMetaInformation[JavaStatement, IFDSFact] final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) @@ -941,7 +903,7 @@ abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalys * Registers the analysis as a lazy computation, that is, the method * will call `ProperytStore.scheduleLazyComputation`. */ - final override def register( + override def register( p: SomeProject, ps: PropertyStore, analysis: AbstractIFDSAnalysis[IFDSFact] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala index d39f8e312c..20bd513fd1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala @@ -33,7 +33,7 @@ import org.opalj.tac.TACode * concrete analysis. * @author Mario Trageser */ -abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement] with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { +abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement] with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { /** * If this method is analyzed for an unbalanced return fact, the single star block is the block, * which contains the call. @@ -88,12 +88,12 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS */ case interimEUBP @ InterimEUBP( e: (DeclaredMethod, IFDSFact) @unchecked, - _: IFDSProperty[IFDSFact] + _: IFDSProperty[JavaStatement, IFDSFact] @unchecked ) ⇒ if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]]) { state.pendingIfdsDependees += e -> interimEUBP - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] createResult() } else super.propertyUpdate(eps) @@ -424,7 +424,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val pendingIfdsCallSites = state.pendingIfdsCallSites state.pendingIfdsDependees += callerEntity -> callerAnalysisResult - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] state.pendingIfdsCallSites += callerEntity -> (pendingIfdsCallSites.getOrElse(callerEntity, Set.empty) + ((state.cfg.startBlock, 0))) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala index 890a1d050f..0da76c3e2e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala @@ -17,7 +17,7 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * * @author Mario Trageser */ -abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { +abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { /** * The analysis starts at the entry block. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala index fd88ee0e0d..c792457dd1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -40,24 +40,24 @@ object IFDSBasedVariableTypeAnalysis extends IFDSAnalysis[VTAFact] { else new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) } - override def property: IFDSPropertyMetaInformation[VTAFact] = VTAResult + override def property: IFDSPropertyMetaInformation[JavaStatement, VTAFact] = VTAResult } /** * The IFDSProperty for this analysis. */ -case class VTAResult(flows: Map[JavaStatement, Set[VTAFact]]) extends IFDSProperty[VTAFact] { +case class VTAResult(flows: Map[JavaStatement, Set[VTAFact]]) extends IFDSProperty[JavaStatement, VTAFact] { override type Self = VTAResult - override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[VTAFact] = new VTAResult(result) + override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result) override def key: PropertyKey[VTAResult] = VTAResult.key } -object VTAResult extends IFDSPropertyMetaInformation[VTAFact] { +object VTAResult extends IFDSPropertyMetaInformation[JavaStatement, VTAFact] { override type Self = VTAResult - override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[VTAFact] = new VTAResult(result) + override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result) val key: PropertyKey[VTAResult] = PropertyKey.create("VTA", new VTAResult(Map.empty)) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala index e9da408b3d..553a2062df 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala @@ -2,6 +2,8 @@ package org.opalj.tac.fpcf.analyses.ifds import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{Entity, ProperPropertyComputationResult} +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation /** * The supertype of all IFDS facts. @@ -141,6 +143,9 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val statement: Statement, caller: Method ): Iterator[Method] + + def delegateAnalysis(source: (Method, IFDSFact)): Option[((Entity, IFDSFact), Set[IFDSFact] => Set[IFDSFact])] = None + def specialCase(source: (Method, IFDSFact), propertyKey: IFDSPropertyMetaInformation[Statement, IFDSFact]): Option[ProperPropertyComputationResult] = None } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index f757b8f557..6d6857cd11 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -1,14 +1,15 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds -import org.opalj.br.{DeclaredMethod} +import org.opalj.br.{DeclaredMethod, ObjectType} import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.fpcf.{FinalEP, PropertyStore} +import org.opalj.fpcf.{Entity, FinalEP, FinalP, InterimResult, InterimUBP, ProperPropertyComputationResult, Property, PropertyStore, Result, SomeEOptionP} import org.opalj.tac.cg.TypeProviderKey import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt} import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.properties.cg.{Callees, Callers} abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, DeclaredMethod, JavaStatement](project) { @@ -79,6 +80,48 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall case _ ⇒ call.asMethodCall } + + override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { + val declaredMethod = source._1 + val method = declaredMethod.definedMethod + val declaringClass: ObjectType = method.classFile.thisType + /* + * If this is not the method's declaration, but a non-overwritten method in a subtype, do + * not re-analyze the code. + */ + if (declaringClass ne declaredMethod.declaringClassType) Some(delegate( + source, ((declaredMethods(source._1.definedMethod), source._2), propertyKey), identity, propertyKey)) + None + } + + /** + * This method will be called if the analysis of a method shall be delegated to another analysis. + * + * @param source A pair consisting of the declared method of the subtype and an input fact. + * @param delegation A pair consisting of the delegated entity and an input fact as well as the delegated property. + * @param resultMapping A function that maps the results of the delegation. + * @param propertyKey the propertyKey used for the instantiation of new results + * @return The result of the other analysis. + */ + protected def delegate( + source: (DeclaredMethod, Fact), + delegation: ((Entity, Fact), IFDSPropertyMetaInformation[_, Fact]), + resultMapping: Set[Fact] => Set[Fact], + propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact] + ): ProperPropertyComputationResult = { + + def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { + case FinalP(p) ⇒ Result(source, propertyKey.create(Map.empty)) + + case ep @ InterimUBP(ub: Property) ⇒ + // TODO: resultMapping(ub.asInstanceOf[Set[Fact]]).asInstanceOf[Property] + InterimResult.forUB(source, propertyKey.create(Map.empty), Set(ep), c) + + case epk ⇒ + InterimResult.forUB(source, propertyKey.create(Map.empty), Set(epk), c) + } + c(propertyStore(delegation._1, delegation._2.key)) + } } abstract class JavaBackwardIFDSProblem[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](project: SomeProject) extends JavaIFDSProblem[IFDSFact](project) with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala index c89a97dec3..55dc7cfe63 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala @@ -7,21 +7,20 @@ package properties import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyMetaInformation import org.opalj.value.KnownTypedValue -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -trait IFDSPropertyMetaInformation[DataFlowFact] extends PropertyMetaInformation { +trait IFDSPropertyMetaInformation[Statement, DataFlowFact] extends PropertyMetaInformation { /** * Creates an IFDSProperty containing the result of this analysis. * * @param result Maps each exit statement to the facts, which hold after the exit statement. * @return An IFDSProperty containing the `result`. */ - def create(result: Map[JavaStatement, Set[DataFlowFact]]): IFDSProperty[DataFlowFact] + def create(result: Map[Statement, Set[DataFlowFact]]): IFDSProperty[Statement, DataFlowFact] } -abstract class IFDSProperty[DataFlowFact] +abstract class IFDSProperty[Statement, DataFlowFact] extends Property - with IFDSPropertyMetaInformation[DataFlowFact] { + with IFDSPropertyMetaInformation[Statement, DataFlowFact] { /** The type of the TAC domain. */ type V = DUVar[KnownTypedValue] @@ -29,10 +28,10 @@ abstract class IFDSProperty[DataFlowFact] /** * Maps exit statements to the data flow facts, which hold after them. */ - def flows: Map[JavaStatement, Set[DataFlowFact]] + def flows: Map[Statement, Set[DataFlowFact]] override def equals(other: Any): Boolean = other match { - case that: IFDSProperty[DataFlowFact] ⇒ + case that: IFDSProperty[Statement, DataFlowFact] ⇒ // We cached the "hashCode" to make the following comparison more efficient; // note that all properties are eventually added to some set and therefore // the hashCode is required anyway! diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala index 66976215d8..1419ce21a4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala @@ -5,21 +5,18 @@ import org.opalj.fpcf.PropertyKey import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -/** - * The IFDSProperty for this analysis. - */ -case class Taint(flows: Map[JavaStatement, Set[Fact]]) extends IFDSProperty[Fact] { +case class Taint(flows: Map[JavaStatement, Set[Fact]]) extends IFDSProperty[JavaStatement, Fact] { override type Self = Taint - override def create(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[Fact] = new Taint(result) + override def create(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[JavaStatement, Fact] = new Taint(result) override def key: PropertyKey[Taint] = Taint.key } -object Taint extends IFDSPropertyMetaInformation[Fact] { +object Taint extends IFDSPropertyMetaInformation[JavaStatement, Fact] { override type Self = Taint - override def create(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[Fact] = new Taint(result) + override def create(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[JavaStatement, Fact] = new Taint(result) val key: PropertyKey[Taint] = PropertyKey.create("Taint", new Taint(Map.empty)) } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala index 50035e3671..49bd24d23c 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala @@ -82,5 +82,5 @@ object BackwardTaintAnalysisFixture extends IFDSAnalysis[Fact] { override def init(p: SomeProject, ps: PropertyStore) = new BackwardTaintAnalysisFixture()(p) - override def property: IFDSPropertyMetaInformation[Fact] = Taint + override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala index f84437e643..6bcd05dc43 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -58,5 +58,5 @@ object ForwardTaintAnalysisFixture extends IFDSAnalysis[Fact] { override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture()(p) - override def property: IFDSPropertyMetaInformation[Fact] = Taint + override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint } From cc84ea105e97559f26700cd9c78feeb76a4f0cb5 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 10 Mar 2022 22:04:53 +0100 Subject: [PATCH 19/67] WIP --- .../tac/fpcf/analyses/IFDSEvaluation.scala | 10 +- .../analyses/heros/analyses/VTAEquality.scala | 8 +- ...dClassForNameTaintAnalysisScheduler.scala} | 31 +- ...dClassForNameTaintAnalysisScheduler.scala} | 16 +- .../BackwardTaintAnalysisFixtureTest.scala | 4 +- .../ForwardTaintAnalysisFixtureTest.scala | 4 +- .../scala/org/opalj/fpcf/ifds/VTATest.scala | 4 +- .../taint/BackwardFlowPathMatcher.scala | 4 +- .../vta/ExpectedCalleeMatcher.scala | 4 +- ...MultilingualForwardTaintAnalysisTest.scala | 6 +- .../ifds/AbstractNativeIFDSAnalysis.scala | 396 ++++++------------ .../ifds/taint/JavaForwardTaintAnalysis.scala | 80 ++++ .../MultilingualForwardTaintAnalysis.scala | 144 ------- .../taint/NativeForwardTaintAnalysis.scala | 56 +++ .../taint/NativeForwardTaintProblem.scala | 294 ++----------- .../org/opalj/ll/fpcf/properties/Taint.scala | 2 + .../scala/org/opalj/ll/llvm/BasicBlock.scala | 41 +- .../scala/org/opalj/ll/llvm/Instruction.scala | 12 +- .../analyses/ifds/AbstractIFDSAnalysis.scala | 6 +- ...SBasedVariableTypeAnalysisScheduler.scala} | 12 +- .../tac/fpcf/analyses/ifds/IFDSProblem.scala | 42 +- .../tac/fpcf/analyses/ifds/Subsumable.scala | 8 +- .../ifds/taint/BackwardTaintProblem.scala | 14 +- .../ifds/taint/ForwardTaintProblem.scala | 8 +- .../analyses/ifds/taint/TaintProblem.scala | 9 +- ...ckwardTaintAnalysisFixtureScheduler.scala} | 18 +- ...orwardTaintAnalysisFixtureScheduler.scala} | 14 +- 27 files changed, 442 insertions(+), 805 deletions(-) rename DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/{BackwardClassForNameTaintAnalysis.scala => BackwardClassForNameTaintAnalysisScheduler.scala} (84%) rename DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/{ForwardClassForNameTaintAnalysis.scala => ForwardClassForNameTaintAnalysisScheduler.scala} (89%) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala delete mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{IFDSBasedVariableTypeAnalysis.scala => IFDSBasedVariableTypeAnalysisScheduler.scala} (88%) rename OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/{BackwardTaintAnalysisFixture.scala => BackwardTaintAnalysisFixtureScheduler.scala} (84%) rename OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/{ForwardTaintAnalysisFixture.scala => ForwardTaintAnalysisFixtureScheduler.scala} (84%) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala index 26a37f1cda..d228f9265a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala @@ -8,7 +8,7 @@ import org.opalj.tac.fpcf.analyses.heros.analyses.taint.HerosForwardClassForName import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosVariableTypeAnalysisRunner import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysis +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisRunner import org.opalj.tac.fpcf.analyses.taint.BackwardClassForNameTaintAnalysisRunner import org.opalj.tac.fpcf.analyses.taint.ForwardClassForNameAnalysisRunner @@ -41,9 +41,9 @@ object IFDSEvaluation { AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW = true // Evaluation of subsuming - IFDSBasedVariableTypeAnalysis.SUBSUMING = false + IFDSBasedVariableTypeAnalysisScheduler.SUBSUMING = false IFDSBasedVariableTypeAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"NoSubsuming.txt")) - IFDSBasedVariableTypeAnalysis.SUBSUMING = true + IFDSBasedVariableTypeAnalysisScheduler.SUBSUMING = true // Evaluation of Heros analyses HerosForwardClassForNameAnalysisRunner.main(Array("-f", dir+"HerosForwardClassForNameAnalysis.txt")) @@ -58,11 +58,11 @@ object IFDSEvaluation { HerosAnalysis.NUM_THREADS = 1 // Evaluation of the scheduling strategies - IFDSBasedVariableTypeAnalysis.SUBSUMING = false + IFDSBasedVariableTypeAnalysisScheduler.SUBSUMING = false ForwardClassForNameAnalysisRunner.main(Array("-evalSchedulingStrategies", "-seq", "-l2", "-f", dir+"SchedulingForwardClassForNameAnalysis.txt")) BackwardClassForNameTaintAnalysisRunner.main(Array("-evalSchedulingStrategies", "-seq", "-l2", "-f", dir+"SchedulingBackwardClassForNameAnalysis.txt")) IFDSBasedVariableTypeAnalysisRunner.main(Array("-evalSchedulingStrategies", "-seq", "-l2", "-f", dir+"SchedulingIFDSBasedVariableTypeAnalysis.txt")) - IFDSBasedVariableTypeAnalysis.SUBSUMING = true + IFDSBasedVariableTypeAnalysisScheduler.SUBSUMING = true } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala index fe06dba7cf..ed8486fa0a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala @@ -30,7 +30,7 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG import org.opalj.tac.fpcf.analyses.ifds.CalleeType -import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysis +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.VariableType import org.opalj.tac.fpcf.analyses.ifds.VTAFact @@ -122,15 +122,15 @@ object VTAEquality { requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] } project.get(RTACallGraphKey) - project.get(FPCFAnalysesManagerKey).runAll(IFDSBasedVariableTypeAnalysis) + project.get(FPCFAnalysesManagerKey).runAll(IFDSBasedVariableTypeAnalysisScheduler) propertyStore - .entities(IFDSBasedVariableTypeAnalysis.property.key) + .entities(IFDSBasedVariableTypeAnalysisScheduler.property.key) .collect { case EPS((m: DefinedMethod, inputFact)) ⇒ (m, inputFact) } .foreach { entity ⇒ - val entityResult = propertyStore(entity, IFDSBasedVariableTypeAnalysis.property.key) match { + val entityResult = propertyStore(entity, IFDSBasedVariableTypeAnalysisScheduler.property.key) match { case FinalEP(_, VTAResult(map)) ⇒ map case _ ⇒ throw new RuntimeException } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala similarity index 84% rename from DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala rename to DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index bf8bb5eb71..32dcce428e 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -5,11 +5,10 @@ import java.io.File import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.EPS import org.opalj.fpcf.FinalEP -import org.opalj.br.DeclaredMethod +import org.opalj.br.{DeclaredMethod, DefinedMethod, Method} import org.opalj.br.analyses.SomeProject -import org.opalj.br.DefinedMethod import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysis, JavaStatement, UnbalancedReturnFact} +import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement, UnbalancedReturnFact} import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, BackwardTaintProblem, Fact, FlowFact, InstanceField, Variable} import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} @@ -19,7 +18,7 @@ import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} * * @author Mario Trageser */ -class BackwardClassForNameTaintAnalysis private (implicit val project: SomeProject) +class BackwardClassForNameTaintAnalysisScheduler private(implicit val project: SomeProject) extends BackwardIFDSAnalysis(new BackwardClassForNameTaintProblem(project), Taint) class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProblem(p) { @@ -42,7 +41,7 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * Do not perform unbalanced return for methods, which can be called from outside the library. @@ -59,16 +58,16 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb * Instead, FlowFacts are created at the start node of methods. */ override protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact] = None + source: (DeclaredMethod, Fact)): Option[FlowFact[Method]] = None /** * This analysis does not create FlowFacts at returns. * Instead, FlowFacts are created at the start node of methods. */ protected def applyFlowFactFromCallee( - calleeFact: FlowFact, + calleeFact: FlowFact[Method], source: (DeclaredMethod, Fact) - ): Option[FlowFact] = None + ): Option[FlowFact[Method]] = None /** * If we analyzed a transitive caller of the sink, which is callable from outside the library, @@ -77,7 +76,7 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb override protected def createFlowFactAtBeginningOfMethod( in: Set[Fact], source: (DeclaredMethod, Fact) - ): Option[FlowFact] = { + ): Option[FlowFact[Method]] = { if (source._2.isInstanceOf[UnbalancedReturnFact[Fact @unchecked]] && canBeCalledFromOutside(source._1) && in.exists { // index < 0 means, that it is a parameter. @@ -91,22 +90,22 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb } } -object BackwardClassForNameTaintAnalysis extends IFDSAnalysis[Fact] { +object BackwardClassForNameTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact] { - override def init(p: SomeProject, ps: PropertyStore): BackwardClassForNameTaintAnalysis = { + override def init(p: SomeProject, ps: PropertyStore): BackwardClassForNameTaintAnalysisScheduler = { p.get(RTACallGraphKey) - new BackwardClassForNameTaintAnalysis()(p) + new BackwardClassForNameTaintAnalysisScheduler()(p) } - override def property: IFDSPropertyMetaInformation[Fact] = Taint + override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint } class BackwardClassForNameTaintAnalysisRunner extends AbsractIFDSAnalysisRunner { - override def analysisClass: BackwardClassForNameTaintAnalysis.type = BackwardClassForNameTaintAnalysis + override def analysisClass: BackwardClassForNameTaintAnalysisScheduler.type = BackwardClassForNameTaintAnalysisScheduler override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = { - val propertyKey = BackwardClassForNameTaintAnalysis.property.key + val propertyKey = BackwardClassForNameTaintAnalysisScheduler.property.key val flowFactsAtSources = ps.entities(propertyKey).collect { case EPS((m: DefinedMethod, inputFact)) if canBeCalledFromOutside(m, ps) ⇒ (m, inputFact) @@ -122,7 +121,7 @@ class BackwardClassForNameTaintAnalysisRunner extends AbsractIFDSAnalysisRunner fact ← flowFactsAtSources } { fact match { - case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.toJava).mkString(", ")) + case FlowFact(flow) ⇒ println(s"flow: "+flow.asInstanceOf[Seq[Method]].map(_.toJava).mkString(", ")) case _ ⇒ } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala similarity index 89% rename from DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala rename to DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala index 001de2a9b0..d31f92d9b7 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -7,7 +7,7 @@ import org.opalj.br.DeclaredMethod import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, ForwardIFDSAnalysis, IFDSAnalysis, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProblem, TaintProblem, Variable} import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation, Taint} @@ -19,7 +19,7 @@ import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation, * @author Mario Trageser * @author Michael Eichberg */ -class ForwardClassForNameTaintAnalysis private (implicit val project: SomeProject) +class ForwardClassForNameTaintAnalysis$Scheduler private(implicit val project: SomeProject) extends ForwardIFDSAnalysis(new ForwardClassForNameTaintProblem(project), Taint) class ForwardClassForNameTaintProblem(project: SomeProject) @@ -44,7 +44,7 @@ class ForwardClassForNameTaintProblem(project: SomeProject) /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * This analysis does not create new taints on the fly. @@ -83,11 +83,11 @@ class ForwardClassForNameTaintProblem(project: SomeProject) method.declaringClassType == ObjectType.Class && method.name == "forName" } -object ForwardClassForNameTaintAnalysis extends IFDSAnalysis[Fact] { +object ForwardClassForNameTaintAnalysis$Scheduler extends IFDSAnalysisScheduler[Fact] { - override def init(p: SomeProject, ps: PropertyStore): ForwardClassForNameTaintAnalysis = { + override def init(p: SomeProject, ps: PropertyStore): ForwardClassForNameTaintAnalysis$Scheduler = { p.get(RTACallGraphKey) - new ForwardClassForNameTaintAnalysis()(p) + new ForwardClassForNameTaintAnalysis$Scheduler()(p) } override def property: IFDSPropertyMetaInformation[Fact] = Taint @@ -95,12 +95,12 @@ object ForwardClassForNameTaintAnalysis extends IFDSAnalysis[Fact] { class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { - override def analysisClass: ForwardClassForNameTaintAnalysis.type = ForwardClassForNameTaintAnalysis + override def analysisClass: ForwardClassForNameTaintAnalysis$Scheduler.type = ForwardClassForNameTaintAnalysis$Scheduler override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = for { e ← analysis.ifdsProblem.entryPoints - flows = ps(e, ForwardClassForNameTaintAnalysis.property.key) + flows = ps(e, ForwardClassForNameTaintAnalysis$Scheduler.property.key) fact ← flows.ub.asInstanceOf[IFDSProperty[Fact]].flows.values.flatten.toSet[Fact] } { fact match { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala index 77ea0833f5..3a45a84544 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala @@ -10,7 +10,7 @@ import org.opalj.br.analyses.Project import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixture +import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixtureScheduler import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact /** @@ -31,7 +31,7 @@ class BackwardTaintAnalysisFixtureTest extends PropertiesTest { } describe("Test the BackwardFlowPath annotations") { - val testContext = executeAnalyses(BackwardTaintAnalysisFixture) + val testContext = executeAnalyses(BackwardTaintAnalysisFixtureScheduler) val project = testContext.project val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala index e1dfa3dd12..0ac3ad52f3 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala @@ -10,7 +10,7 @@ import org.opalj.br.analyses.Project import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintAnalysisFixture +import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintAnalysisFixtureScheduler import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact /** @@ -31,7 +31,7 @@ class ForwardTaintAnalysisFixtureTest extends PropertiesTest { } describe("Test the ForwardFlowPath annotations") { - val testContext = executeAnalyses(ForwardTaintAnalysisFixture) + val testContext = executeAnalyses(ForwardTaintAnalysisFixtureScheduler) val project = testContext.project val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala index b2ab9ad01d..6fd9924ae1 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala @@ -11,7 +11,7 @@ import org.opalj.br.analyses.Project import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysis +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler import org.opalj.tac.fpcf.analyses.ifds.VTANullFact class VTATest extends PropertiesTest { @@ -29,7 +29,7 @@ class VTATest extends PropertiesTest { } describe("Test the ExpectedType annotations") { - val testContext = executeAnalyses(IFDSBasedVariableTypeAnalysis) + val testContext = executeAnalyses(IFDSBasedVariableTypeAnalysisScheduler) val project = testContext.project val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala index 722661fcd4..9de044a9ad 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala @@ -13,7 +13,7 @@ import org.opalj.br.ElementValue import org.opalj.br.ElementValuePair import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.DefinedMethod -import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixture +import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixtureScheduler import org.opalj.tac.fpcf.analyses.ifds.taint.Fact import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact import org.opalj.tac.fpcf.properties.Taint @@ -35,7 +35,7 @@ class BackwardFlowPathMatcher extends AbstractPropertyMatcher { evp.value.asArrayValue.values.map((value: ElementValue) ⇒ value.asStringValue.value)).head.toIndexedSeq val propertyStore = p.get(PropertyStoreKey) - val propertyKey = BackwardTaintAnalysisFixture.property.key + val propertyKey = BackwardTaintAnalysisFixtureScheduler.property.key val allReachableFlowFacts = propertyStore.entities(propertyKey).collect { case EPS((m: DefinedMethod, inputFact)) if m.definedMethod == method ⇒ diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala index 345c235d14..aaf37d7a27 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala @@ -15,7 +15,7 @@ import org.opalj.tac.DUVar import org.opalj.tac.TACMethodParameter import org.opalj.tac.TACode import org.opalj.tac.fpcf.analyses.ifds.CalleeType -import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysis +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler import org.opalj.tac.fpcf.analyses.ifds.VTAResult class ExpectedCalleeMatcher extends VTAMatcher { @@ -31,7 +31,7 @@ class ExpectedCalleeMatcher extends VTAMatcher { elementValuePairs(2).value.asBooleanValue.value ) val propertyStore = project.get(PropertyStoreKey) - val propertyKey = IFDSBasedVariableTypeAnalysis.property.key + val propertyKey = IFDSBasedVariableTypeAnalysisScheduler.property.key // Get ALL the exit facts for the method for ALL input facts val allReachableExitFacts = propertyStore.entities(propertyKey).collect { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index cceffd715a..e44f40411b 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -3,7 +3,7 @@ package org.opalj.ll import org.opalj.br.analyses.{DeclaredMethodsKey, Project} import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.ll.fpcf.analyses.ifds.taint.MultilingualForwardTaintAnalysis +import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler} import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact} @@ -25,10 +25,10 @@ class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers project.get(RTACallGraphKey) val declaredMethods = project.get(DeclaredMethodsKey) val manager = project.get(FPCFAnalysesManagerKey) - val (ps, analyses) = manager.runAll(MultilingualForwardTaintAnalysis) + val (ps, analyses) = manager.runAll(JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler) for (method ← project.allMethodsWithBody) { val flows = - ps((declaredMethods(method), NullFact), MultilingualForwardTaintAnalysis.property.key) + ps((declaredMethods(method), NullFact), JavaForwardTaintAnalysisScheduler.property.key) println("---METHOD: "+method.toJava+" ---") for { fact ← flows.ub diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala index 84db93ae14..01ec801755 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala @@ -2,15 +2,9 @@ package org.opalj.ll.fpcf.analyses.ifds -import java.io.File -import java.io.PrintWriter import scala.collection.{Set => SomeSet} import scala.collection.mutable -import com.typesafe.config.ConfigValueFactory -import javax.swing.JOptionPane -import org.opalj.util.Milliseconds -import org.opalj.util.PerformanceEvaluation.time import org.opalj.fpcf.EOptionP import org.opalj.fpcf.EPK import org.opalj.fpcf.FinalE @@ -43,7 +37,7 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.fpcf.properties.IFDSProperty import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSAnalysis, IFDSProblem, NumberOfCalls, Subsumable} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSAnalysisScheduler, IFDSProblem, NumberOfCalls, Subsumable} import org.opalj.br.analyses.ProjectInformationKeys import org.opalj.ll.LLVMProjectKey import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} @@ -59,7 +53,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( val ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement], val propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact]) extends FPCFAnalysis - with Subsumable[IFDSFact] { + with Subsumable[LLVMStatement, IFDSFact] { /** * Counts, how many times the abstract methods were called. @@ -80,8 +74,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param code The code of the `method`. * @param cfg The control flow graph of the `method`. * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input - * facts to the basic block and statement index, at which they may - * be called. + * facts to the instruction, at which they may be called. * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input * facts to the intermediate result of their IFDS analysis. * Only contains method-fact-pairs, for which this analysis is @@ -94,20 +87,17 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * the beginning of the successor. */ protected class State( - //val declaringClass: ObjectType, val function: Function, val source: (Function, IFDSFact), - //val code: Array[Stmt[V]], - //val cfg: CFG[Stmt[V], TACStmts[V]], - var pendingIfdsCallSites: Map[(Function, IFDSFact), Set[(BasicBlock, Int)]], - //var pendingTacCallSites: Map[Function, Set[BasicBlock]] = Map.empty, - var pendingIfdsDependees: Map[(Function, IFDSFact), EOptionP[(Function, IFDSFact), IFDSProperty[IFDSFact]]] = Map.empty, - //var pendingTacDependees: Map[Function, EOptionP[Function, TACAI]] = Map.empty, + var pendingIfdsCallSites: Map[(Function, IFDSFact), Set[LLVMStatement]] = Map.empty, + var pendingIfdsDependees: Map[(Function, IFDSFact), EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] = Map.empty, var pendingCgCallSites: Set[BasicBlock] = Set.empty, var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, var outgoingFacts: Map[BasicBlock, Map[BasicBlock, Set[IFDSFact]]] = Map.empty ) + type QueueEntry = (BasicBlock, Set[IFDSFact], Option[LLVMStatement], Option[Function], Option[IFDSFact]) + /** * Determines the basic blocks, at which the analysis starts. * @@ -115,7 +105,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param cfg The control flow graph of the analyzed method. * @return The basic blocks, at which the analysis starts. */ - //protected def startBlocks(sourceFact: IFDSFact, cfg: CFG[Stmt[V], TACStmts[V]]): Set[BasicBlock] + protected def startBlocks(sourceFact: IFDSFact, cfg: Function): Set[BasicBlock] /** * Collects the facts valid at all exit nodes based on the current results. @@ -123,7 +113,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at * the exit node under the assumption that the predecessor was executed before. */ - //protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] + protected def collectResult(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] /** * Creates an IFDSProperty containing the result of this analysis. @@ -131,7 +121,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param result Maps each exit statement to the facts, which hold after the exit statement. * @return An IFDSProperty containing the `result`. */ - //protected def createPropertyValue(result: Map[JavaStatement, Set[IFDSFact]]): IFDSProperty[IFDSFact] = propertyKey.create(result) + protected def createPropertyValue(result: Map[LLVMStatement, Set[IFDSFact]]): IFDSProperty[LLVMStatement, IFDSFact] = propertyKey.create(result) /** * Determines the nodes, that will be analyzed after some `basicBlock`. @@ -139,23 +129,14 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param basicBlock The basic block, that was analyzed before. * @return The nodes, that will be analyzed after `basicBlock`. */ - //protected def nextNodes(basicBlock: BasicBlock): Set[CFGNode] + protected def nextNodes(basicBlock: BasicBlock): Set[BasicBlock] /** * Checks, if some `node` is the last node. * * @return True, if `node` is the last node, i.e. there is no next node. */ - //protected def isLastNode(node: CFGNode): Boolean - - /** - * If the passed `node` is a catch node, all successors of this catch node are determined. - * - * @param node The node. - * @return If the node is a catch node, all its successors will be returned. - * Otherwise, the node itself will be returned. - */ - //protected def skipCatchNode(node: CFGNode): Set[BasicBlock] + protected def isLastNode(node: BasicBlock): Boolean /** * Determines the first index of some `basic block`, that will be analyzed. @@ -163,7 +144,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param basicBlock The basic block. * @return The first index of some `basic block`, that will be analyzed. */ - //protected def firstIndex(basicBlock: BasicBlock): Int + protected def firstIndex(basicBlock: BasicBlock): Int /** * Determines the last index of some `basic block`, that will be analzyed. @@ -171,7 +152,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param basicBlock The basic block. * @return The last index of some `basic block`, that will be analzyed. */ - //protected def lastIndex(basicBlock: BasicBlock): Int + protected def lastIndex(basicBlock: BasicBlock): Int /** * Determines the index, that will be analyzed after some other `index`. @@ -179,7 +160,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param index The source index. * @return The index, that will be analyzed after `index`. */ - //protected def nextIndex(index: Int): Int + protected def nextIndex(index: Int): Int /** * Gets the first statement of a node, that will be analyzed. @@ -187,7 +168,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param node The node. * @return The first statement of a node, that will be analyzed. */ - //protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement + protected def firstStatement(basicBlock: BasicBlock)(implicit state: State): LLVMStatement /** * Determines the statement, that will be analyzed after some other `statement`. @@ -195,7 +176,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param statement The source statement. * @return The successor statements */ - //protected def nextStatements(statement: JavaStatement)(implicit state: State): Set[JavaStatement] + protected def nextStatements(statement: LLVMStatement)(implicit state: State): Set[LLVMStatement] /** * Determines the facts, for which a `callee` is analyzed. @@ -205,10 +186,10 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param in The facts, which hold before the `call`. * @return The facts, for which `callee` will be analyzed. */ - /*protected def callToStartFacts(call: JavaStatement, callee: DeclaredMethod, in: Set[IFDSFact])( + protected def callToStartFacts(call: LLVMStatement, callee: Function, in: Set[IFDSFact])( implicit state: State - ): Set[IFDSFact]*/ + ): Set[IFDSFact] /** * Collects the exit facts of a `callee` and adds them to the `summaryEdges`. @@ -221,13 +202,13 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param exitFacts Maps exit statements of the `callee` to the facts, which hold after them. * @return The summary edges plus the exit to return facts for `callee` and `successor`. */ - /*protected def addExitToReturnFacts( - summaryEdges: Map[JavaStatement, Set[IFDSFact]], - successors: Set[JavaStatement], - call: JavaStatement, - callee: DeclaredMethod, - exitFacts: Map[JavaStatement, Set[IFDSFact]] - )(implicit state: State): Map[JavaStatement, Set[IFDSFact]]*/ + protected def addExitToReturnFacts( + summaryEdges: Map[LLVMStatement, Set[IFDSFact]], + successors: Set[LLVMStatement], + call: LLVMStatement, + callee: Function, + exitFacts: Map[LLVMStatement, Set[IFDSFact]] + )(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] /** * Performs an IFDS analysis for a method-fact-pair. @@ -240,33 +221,12 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( def performAnalysis(entity: (Function, IFDSFact)): ProperPropertyComputationResult = { val (function, sourceFact) = entity - - - /* - * Fetch the method's three address code. If it is not present, return an empty interim - * result. - */ - val (code, cfg) = propertyStore(method, TACAI.key) match { - case FinalP(TheTACAI(tac)) ⇒ (tac.stmts, tac.cfg) - - case epk: EPK[Method, TACAI] ⇒ - return InterimResult.forUB( - entity, - createPropertyValue(Map.empty), - Set(epk), - _ ⇒ performAnalysis(entity) - ); - - case tac ⇒ - throw new UnknownError(s"can't handle intermediate TACs ($tac)"); - } - // Start processing at the start of the cfg with the given source fact implicit val state: State = - new State(declaringClass, method, entity, code, cfg, Map(entity -> Set.empty), Map()) + new State(function, entity, Map(entity -> Set.empty), Map()) val queue = mutable.Queue - .empty[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] - startBlocks(sourceFact, cfg).foreach { start ⇒ + .empty[QueueEntry] + startBlocks(sourceFact, function).foreach { start ⇒ state.incomingFacts += start -> Set(sourceFact) queue.enqueue((start, Set(sourceFact), None, None, None)) } @@ -285,7 +245,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( */ protected def createResult()(implicit state: State): ProperPropertyComputationResult = { val propertyValue = createPropertyValue(collectResult) - val dependees = state.pendingIfdsDependees.values ++ state.pendingTacDependees.values + val dependees = state.pendingIfdsDependees.values if (dependees.isEmpty) Result(state.source, propertyValue) else InterimResult.forUB(state.source, propertyValue, dependees.toSet, propertyUpdate) } @@ -302,12 +262,12 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( eps: SomeEPS )(implicit state: State): ProperPropertyComputationResult = { (eps: @unchecked) match { - case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) ⇒ - reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) + case FinalE(e: (Function, IFDSFact) @unchecked) ⇒ + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) case interimEUBP @ InterimEUBP( - e: (DeclaredMethod, IFDSFact) @unchecked, - ub: IFDSProperty[IFDSFact] + e: (Function, IFDSFact) @unchecked, + ub: IFDSProperty[LLVMStatement, IFDSFact] ) ⇒ if (ub.flows.values .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { @@ -315,19 +275,15 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( // Update the pendingIfdsDependee entry to the new interim result. state.pendingIfdsDependees += e -> interimEUBP - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + .asInstanceOf[EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] } else - reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - case FinalEP(_: DefinedMethod, _: Callees) ⇒ + case FinalEP(_: Function, _: Callees) ⇒ reAnalyzebasicBlocks(state.pendingCgCallSites) - case InterimEUBP(_: DefinedMethod, _: Callees) ⇒ + case InterimEUBP(_: Function, _: Callees) ⇒ reAnalyzebasicBlocks(state.pendingCgCallSites) - - case FinalEP(method: Method, _: TACAI) ⇒ - state.pendingTacDependees -= method - reAnalyzebasicBlocks(state.pendingTacCallSites(declaredMethods(method))) } createResult() @@ -360,18 +316,18 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( */ protected def handleCall( basicBlock: BasicBlock, - call: JavaStatement, - callees: SomeSet[Method], + call: LLVMStatement, + callees: SomeSet[Function], in: Set[IFDSFact], calleeWithUpdateFact: Option[IFDSFact] )( implicit state: State - ): Map[JavaStatement, Set[IFDSFact]] = { + ): Map[LLVMStatement, Set[IFDSFact]] = { val successors = nextStatements(call) val inputFacts = beforeHandleCall(call, in) // Facts valid at the start of each successor - var summaryEdges: Map[JavaStatement, Set[IFDSFact]] = Map.empty + var summaryEdges: Map[LLVMStatement, Set[IFDSFact]] = Map.empty /* * If calleeWithUpdateFact is present, this means that the basic block already has been @@ -388,8 +344,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( ) } - for (calledMethod ← callees) { - val callee = declaredMethods(calledMethod) + for (callee ← callees) { if (!ifdsProblem.insideAnalysisContext(callee)) { // Let the concrete analysis decide what to do. for { @@ -403,7 +358,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( else { propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) } - var allNewExitFacts: Map[JavaStatement, Set[IFDSFact]] = Map.empty + var allNewExitFacts: Map[LLVMStatement, Set[IFDSFact]] = Map.empty // Collect exit facts for each input fact separately for (fact ← callToStart) { /* @@ -412,45 +367,45 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts * become known for the input fact. */ - if ((calledMethod eq state.method) && fact == state.source._2) { + if ((callee eq state.function) && fact == state.source._2) { val newDependee = if (state.pendingIfdsCallSites.contains(state.source)) state.pendingIfdsCallSites(state.source) + - ((basicBlock, call.index)) - else Set((basicBlock, call.index)) + ((basicBlock, call)) + else Set((basicBlock, call)) state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(state.source, newDependee) allNewExitFacts = mergeMaps(allNewExitFacts, collectResult) } else { val e = (callee, fact) val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]]] + .asInstanceOf[EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[JavaStatement, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[IFDSFact]]) ⇒ ep.ub.flows + val oldExitFacts: Map[LLVMStatement, Set[IFDSFact]] = oldValue match { + case Some(ep: InterimEUBP[_, IFDSProperty[LLVMStatement, IFDSFact]]) ⇒ ep.ub.flows case _ ⇒ Map.empty } - val exitFacts: Map[JavaStatement, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[IFDSFact]] ⇒ + val exitFacts: Map[LLVMStatement, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[LLVMStatement, IFDSFact]] ⇒ if (state.pendingIfdsCallSites.contains(e) && state.pendingIfdsCallSites(e).nonEmpty) { val newDependee = - state.pendingIfdsCallSites(e) - ((basicBlock, call.index)) + state.pendingIfdsCallSites(e) - ((basicBlock, call)) state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) } state.pendingIfdsDependees -= e ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[IFDSFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, IFDSFact]] ⇒ /* * Add the call site to `pendingIfdsCallSites` and * `pendingIfdsDependees` and continue with the facts in the interim * result for now. When the analysis for the callee finishes, the * analysis for this call site will be triggered again. */ - addIfdsDependee(e, callFlows, basicBlock, call.index) + addIfdsDependee(e, callFlows, basicBlock, call) ep.ub.flows case _ ⇒ - addIfdsDependee(e, callFlows, basicBlock, call.index) + addIfdsDependee(e, callFlows, basicBlock, call) Map.empty } // Only process new facts that are not in `oldExitFacts` @@ -467,7 +422,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( if (oldValue.isDefined && oldExitFacts != exitFacts) { reAnalyzeCalls( state.pendingIfdsCallSites(e), - e._1.definedMethod, + e._1, Some(e._2) ) } @@ -487,7 +442,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param in The input facts, which hold before the `call`. * @return The changed set of input facts. */ - protected def beforeHandleCall(call: JavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = in + protected def beforeHandleCall(call: LLVMStatement, in: Set[IFDSFact]): Set[IFDSFact] = in /** * Merges two maps that have sets as values. @@ -527,16 +482,16 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * for a callee with a specific input fact, this is the input fact. */ private def process( - worklist: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] + worklist: mutable.Queue[QueueEntry] )(implicit state: State): Unit = { while (worklist.nonEmpty) { - val (basicBlock, in, calleeWithUpdateIndex, calleeWithUpdate, calleeWithUpdateFact) = + val (basicBlock, in, calleeWithUpdateSite, calleeWithUpdate, calleeWithUpdateFact) = worklist.dequeue() val oldOut = state.outgoingFacts.getOrElse(basicBlock, Map.empty) val nextOut = analyzeBasicBlock( basicBlock, in, - calleeWithUpdateIndex, + calleeWithUpdateSite, calleeWithUpdate, calleeWithUpdateFact ) @@ -562,13 +517,13 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( ) reAnalyzeCalls( state.pendingIfdsCallSites(source), - source._1.definedMethod, + source._1, Some(source._2) ) } } } else { - val successorBlock = successor.asBasicBlock + val successorBlock = successor val nextIn = nextOut.getOrElse(successorBlock, Set.empty) val oldIn = state.incomingFacts.getOrElse(successorBlock, Set.empty) val newIn = notSubsumedBy(nextIn, oldIn, project) @@ -581,7 +536,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * Only process the successor with new facts. * It is analyzed at least one time because of the null fact. */ - if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None, None)) + if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None)) } } } @@ -593,8 +548,8 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * * @param basicBlock The basic block, that will be analyzed. * @param in The facts, that hold before the block. - * @param calleeWithUpdateIndex If the basic block is analyzed because there is new information - * for a callee, this is the call site's index. + * @param calleeWithUpdateSite If the basic block is analyzed because there is new information + * for a callee, this is the call site. * @param calleeWithUpdate If the basic block is analyzed because there is new information for * a callee, this is the callee. * @param calleeWithUpdateFact If the basic block is analyzed because there is new information @@ -605,13 +560,13 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( private def analyzeBasicBlock( basicBlock: BasicBlock, in: Set[IFDSFact], - calleeWithUpdateIndex: Option[Int], - calleeWithUpdate: Option[Method], + calleeWithUpdateSite: Option[LLVMStatement], + calleeWithUpdate: Option[Function], calleeWithUpdateFact: Option[IFDSFact] )( implicit state: State - ): Map[CFGNode, Set[IFDSFact]] = { + ): Map[BasicBlock, Set[IFDSFact]] = { /* * Collects information about a statement. @@ -619,20 +574,19 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param index The statement's index. * @return A tuple of the following elements: * statement: The statement at `index`. - * calees: The methods possibly called at this statement, if it contains a call. + * callees: The methods possibly called at this statement, if it contains a call. * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will * be returned. * calleeFact: If `index` equals `calleeWithUpdateIndex`, only * `calleeWithUpdateFact` will be returned, None otherwise. */ - def collectInformation(index: Int): (JavaStatement, Option[SomeSet[Method]], Option[IFDSFact]) = { - val stmt = state.code(index) - val statement = JavaStatement(state.method, basicBlock, stmt, index, state.code, state.cfg) + def collectInformation(instruction: Instruction): (LLVMStatement, Option[SomeSet[Function]], Option[IFDSFact]) = { + val statement = LLVMStatement(state.function, basicBlock, instruction) val calleesO = - if (calleeWithUpdateIndex.contains(index)) calleeWithUpdate.map(Set(_)) + if (calleeWithUpdateSite.contains(statement)) calleeWithUpdate.map(Set(_)) else getCalleesIfCallStatement(statement) val calleeFact = - if (calleeWithUpdateIndex.contains(index)) calleeWithUpdateFact + if (calleeWithUpdateSite.contains(statement)) calleeWithUpdateFact else None (statement, calleesO, calleeFact) } @@ -647,7 +601,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( val next = nextIndex(index) flows = if (calleesO.isEmpty) { val successor = - JavaStatement(state.method, basicBlock, state.code(next), next, state.code, state.cfg) + LLVMStatement(state.function, basicBlock, state.code(next), next) numberOfCalls.normalFlow += 1 sumOfInputfactsForCallbacks += in.size ifdsProblem.normalFlow(statement, successor, flows) @@ -659,9 +613,9 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( // Analyze the last statement for each possible successor statement. val (statement, calleesO, callFact) = collectInformation(last) - var result: Map[CFGNode, Set[IFDSFact]] = + var result: Map[BasicBlock, Set[IFDSFact]] = if (calleesO.isEmpty) { - var result: Map[CFGNode, Set[IFDSFact]] = Map.empty + var result: Map[BasicBlock, Set[IFDSFact]] = Map.empty for (node ← nextNodes(basicBlock)) { numberOfCalls.normalFlow += 1 sumOfInputfactsForCallbacks += in.size @@ -670,7 +624,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( result } else handleCall(basicBlock, statement, calleesO.get, flows, callFact) - .map(entry ⇒ entry._1.node -> entry._2) + .map(entry ⇒ entry._1.basicBlock -> entry._2) // Propagate the null fact. result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) @@ -683,7 +637,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param basicBlocks The basic blocks, that will be re-analyzed. */ private def reAnalyzebasicBlocks(basicBlocks: Set[BasicBlock])(implicit state: State): Unit = { - val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty + val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) process(queue) } @@ -696,13 +650,15 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param fact If defined, the `callee` will only be analyzed for this fact. */ private def reAnalyzeCalls( - callSites: Set[(BasicBlock, Int)], - callee: Method, + callSites: Set[LLVMStatement], + callee: Function, fact: Option[IFDSFact] )(implicit state: State): Unit = { - val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty - for ((block, index) ← callSites) - queue.enqueue((block, state.incomingFacts(block), Some(index), Some(callee), fact)) + val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty + for (callSite ← callSites) { + val block = callSite.basicBlock + queue.enqueue((block, state.incomingFacts(block), Some(callSite), Some(callee), fact)) + } process(queue) } @@ -714,57 +670,12 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @return All methods possibly called at the statement index or None, if the statement does not * contain a call. */ - private def getCalleesIfCallStatement(statement: JavaStatement)( + private def getCalleesIfCallStatement(statement: LLVMStatement)( implicit state: State - ): Option[SomeSet[Method]] = { - //val statement = state.code(index) - statement.stmt.astID match { - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ - Some(definedMethods(ifdsProblem.getCallees(statement, state.source._1))) - case Assignment.ASTID | ExprStmt.ASTID ⇒ - getExpression(statement.stmt).astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - VirtualFunctionCall.ASTID ⇒ - Some(definedMethods(ifdsProblem.getCallees(statement, state.source._1))) - case _ ⇒ None - } - case _ ⇒ None - } - } - - /** - * Maps some declared methods to their defined methods. - * - * @param declaredMethods Some declared methods. - * @return All defined methods of `declaredMethods`. - */ - private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): SomeSet[Method] = { - val result = scala.collection.mutable.Set.empty[Method] - declaredMethods - .filter( - declaredMethod ⇒ - declaredMethod.hasSingleDefinedMethod || - declaredMethod.hasMultipleDefinedMethods - ) - .foreach( - declaredMethod ⇒ - declaredMethod - .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) - ) - result - } - - /** - * Retrieves the expression of an assignment or expression statement. - * - * @param statement The statement. Must be an Assignment or ExprStmt. - * @return The statement's expression. - */ - private def getExpression(statement: Stmt[V]): Expr[V] = statement.astID match { - case Assignment.ASTID ⇒ statement.asAssignment.expr - case ExprStmt.ASTID ⇒ statement.asExprStmt.expr - case _ ⇒ throw new UnknownError("Unexpected statement") + ): Option[SomeSet[Function]] = { + // TODO + None } /** @@ -788,109 +699,72 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param callIndex The index of the call site. */ private def addIfdsDependee( - entity: (DeclaredMethod, IFDSFact), - calleeProperty: EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[IFDSFact]], + entity: (Function, IFDSFact), + calleeProperty: EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]], callBB: BasicBlock, - callIndex: Int + call: LLVMStatement )(implicit state: State): Unit = { val callSites = state.pendingIfdsCallSites state.pendingIfdsCallSites = callSites.updated( entity, - callSites.getOrElse(entity, Set.empty) + ((callBB, callIndex)) + callSites.getOrElse(entity, Set.empty) + ((callBB, call)) ) state.pendingIfdsDependees += entity -> calleeProperty } +} +object AbstractNativeIFDSAnalysis { /** - * This method will be called if a non-overwritten declared method in a sub type shall be - * analyzed. Analyzes the defined method of the supertype instead. - * - * @param source A pair consisting of the declared method of the subtype and an input fact. - * @return The result of the analysis of the defined method of the supertype. + * When true, the cross product of exit and successor in returnFLow will be optimized. */ - private def baseMethodResult( - source: (DeclaredMethod, IFDSFact) - ): ProperPropertyComputationResult = { + var OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW: Boolean = true +} - def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { - case FinalP(p) ⇒ Result(source, p) +/** + * A statement that is passed to the concrete analysis. + * + * @param instruction The LLVM instruction. + */ +case class LLVMStatement(instruction: Instruction) { + def function(): Function = instruction.function + def basicBlock(): BasicBlock = instruction.parent +} - case ep @ InterimUBP(ub: Property) ⇒ - InterimResult.forUB(source, ub, Set(ep), c) +abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalysisScheduler { - case epk ⇒ - InterimResult.forUB(source, createPropertyValue(Map.empty), Set(epk), c) - } - c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) - } -} + final override type InitializationData = AbstractNativeIFDSAnalysis[IFDSFact] -object AbstractIFDSAnalysis { + def property: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] - /** - * The type of the TAC domain. - */ - type V = DUVar[ValueInformation] + final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) - /** - * When true, the cross product of exit and successor in returnFLow will be optimized. - */ - var OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW: Boolean = true + override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) + + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} /** - * Converts the index of a method's formal parameter to its tac index in the method's scope and - * vice versa. - * - * @param index The index of a formal parameter in the parameter list or of a variable. - * @param isStaticMethod States, whether the method is static. - * @return A tac index if a parameter index was passed or a parameter index if a tac index was - * passed. + * Registers the analysis as a lazy computation, that is, the method + * will call `ProperytStore.scheduleLazyComputation`. */ - def switchParamAndVariableIndex(index: Int, isStaticMethod: Boolean): Int = - (if (isStaticMethod) -2 else -1) - index - -} -*/ -/** - * A statement that is passed to the concrete analysis. - * - * @param method The method containing the statement. - * @param node The basic block containing the statement. - * @param stmt The TAC statement. - * @param index The index of the Statement in the code. - * @param code The method's TAC code. - * @param cfg The method's CFG. - */ -case class LLVMStatement( - function: Function, - //node: CFGNode, - instruction: Instruction, - //index: Int, - //code: Array[Stmt[V]], - //cfg: CFG[Stmt[V], TACStmts[V]] - ) /*{ - - override def hashCode(): Int = method.hashCode() * 31 + index - - override def equals(o: Any): Boolean = o match { - case s: JavaStatement ⇒ s.index == index && s.method == method - case _ ⇒ false + override def register( + p: SomeProject, + ps: PropertyStore, + analysis: AbstractNativeIFDSAnalysis[IFDSFact] + ): FPCFAnalysis = { + ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) + analysis } - override def toString: String = s"${method.toJava}" + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { + val ifdsAnalysis = analysis.asInstanceOf[AbstractNativeIFDSAnalysis[IFDSFact]] + for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { + ps.force(e, ifdsAnalysis.propertyKey.key) + } + } -}*/ -/* -/** - * Contains int variables, which count, how many times some method was called. - */ -class NumberOfCalls { - var normalFlow = 0 - var callFlow = 0 - var returnFlow = 0 - var callToReturnFlow = 0 -} -*/ -abstract class MultilingualIFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysis[IFDSFact] { - override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq(LLVMProjectKey) + override def afterPhaseCompletion( + p: SomeProject, + ps: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala new file mode 100644 index 0000000000..4ce0381b18 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -0,0 +1,80 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds.taint + +import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} +import org.opalj.br.DeclaredMethod +import org.opalj.fpcf.{ProperPropertyComputationResult, PropertyBounds, PropertyStore} +import org.opalj.ll.LLVMProjectKey +import org.opalj.ll.fpcf.properties.NativeTaint +import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProblem, NullFact, Variable} +import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} + +class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { + val llvmProject = p.get(LLVMProjectKey) + + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(DeclaredMethod, Fact)] = (for { + m ← p.allMethodsWithBody + if (m.name == "main") + } yield declaredMethods(m) -> NullFact) + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = + callee.name == "sanitize" + + /** + * We do not sanitize parameters. + */ + override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact( + callee: DeclaredMethod, + call: JavaStatement, + in: Set[Fact] + ): Option[FlowFact] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) + else None + + // Multilingual additions here + + override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { + super.insideAnalysisContext(callee) || callee.definedMethod.isNative + } + + override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { + val method = source._1.definedMethod + if (method.isNative) { + // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names + val nativeMethodName = "Java_" + method.classFile.fqn + "_" + method.name + val function = llvmProject.function(nativeMethodName) + return Some(delegate(source, ((function.get, source._2), NativeTaint), identity, propertyKey)) + } + super.specialCase(source, propertyKey) + } +} + +class SimpleJavaForwardTaintAnalysis(implicit val project: SomeProject) + extends ForwardIFDSAnalysis(new SimpleJavaForwardTaintProblem(project), Taint) + +object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact] { + override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis()(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq(LLVMProjectKey) + override val uses: Set[PropertyBounds] = super.uses ++ PropertyBounds.ub(NativeTaint) +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala deleted file mode 100644 index e49ab829f4..0000000000 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/MultilingualForwardTaintAnalysis.scala +++ /dev/null @@ -1,144 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.fpcf.analyses.ifds.taint - -import org.opalj.br.analyses.SomeProject -import org.opalj.br.DeclaredMethod -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.fpcf.{ProperPropertyComputationResult, PropertyBounds, PropertyStore} -import org.opalj.ll.LLVMProjectKey -import org.opalj.ll.fpcf.analyses.ifds.{ForwardNativeIFDSAnalysis, MultilingualIFDSAnalysis} -import org.opalj.ll.fpcf.properties.NativeTaint -import org.opalj.ll.llvm.Function -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, ForwardIFDSAnalysis, IFDSAnalysis, JavaStatement} -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProblem, NullFact, Variable} -import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} - -class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { - val llvmProject = p.get(LLVMProjectKey) - - /** - * The analysis starts with all public methods in TaintAnalysisTestClass. - */ - override val entryPoints: Seq[(DeclaredMethod, Fact)] = (for { - m ← p.allMethodsWithBody - if (m.name == "main") - } yield declaredMethods(m) -> NullFact) - - /** - * The sanitize method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = - callee.name == "sanitize" - - /** - * We do not sanitize paramters. - */ - override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty - - /** - * Creates a new variable fact for the callee, if the source was called. - */ - override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = - if (callee.name == "source") Set(Variable(call.index)) - else Set.empty - - /** - * Create a FlowFact, if sink is called with a tainted variable. - * Note, that sink does not accept array parameters. No need to handle them. - */ - override protected def createFlowFact( - callee: DeclaredMethod, - call: JavaStatement, - in: Set[Fact] - ): Option[FlowFact] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) - else None - - // Multilingual additions here - - override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { - super.insideAnalysisContext(callee) || callee.definedMethod.isNative - } - - override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { - val method = source._1.definedMethod - if (method.isNative) { - // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names - val nativeMethodName = "Java_" + method.classFile.fqn + "_" + method.name - val function = llvmProject.function(nativeMethodName) - return Some(delegate(source, ((function.get, source._2), NativeTaint), identity, propertyKey)) - } - super.specialCase(source, propertyKey) - } -} - -class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaintProblem(p) { - /** - * The analysis starts with all public methods in TaintAnalysisTestClass. - */ - override val entryPoints: Seq[(Function, Fact)] = Seq.empty - - /** - * The sanitize method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: Function): Boolean = - callee.name == "sanitize" - - /** - * We do not sanitize paramters. - */ - override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty - - /** - * Creates a new variable fact for the callee, if the source was called. - */ - override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = - if (callee.name == "source") Set(Variable(call.index)) - else Set.empty - - /** - * Create a FlowFact, if sink is called with a tainted variable. - * Note, that sink does not accept array parameters. No need to handle them. - */ - override protected def createFlowFact( - callee: DeclaredMethod, - call: JavaStatement, - in: Set[Fact] - ): Option[FlowFact] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) - else None - - // Multilingual additions here - - override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { - super.insideAnalysisContext(callee) || callee.definedMethod.isNative - } - - override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { - val method = source._1.definedMethod - if (method.isNative) { - // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names - val nativeMethodName = "Java_" + method.classFile.fqn + "_" + method.name - val function = llvmProject.function(nativeMethodName) - return Some(delegate(source, ((function.get, source._2), NativeTaint), identity, propertyKey)) - } - super.specialCase(source, propertyKey) - } -} - -class SimpleJavaForwardTaintAnalysis private (implicit val project: SomeProject) - extends ForwardIFDSAnalysis(new SimpleJavaForwardTaintProblem(project), Taint) - -class SimpleNativeForwardTaintAnalysis private (implicit val project: SomeProject) - extends ForwardNativeIFDSAnalysis(new SimpleNativeForwardTaintProblem(project), NativeTaint) - -object MultilingualForwardTaintAnalysis extends MultilingualIFDSAnalysis[Fact] { - - override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis()(p) - - override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint - - override val uses: Set[PropertyBounds] = super.uses ++ PropertyBounds.ub(NativeTaint) - - override def register(p: SomeProject, ps: PropertyStore, analysis: AbstractIFDSAnalysis[Fact]): FPCFAnalysis = super.register(p, ps, analysis) -} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala new file mode 100644 index 0000000000..6eeb4ba5ab --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -0,0 +1,56 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds.taint + +import org.opalj.br.analyses.{SomeProject} +import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.ll.fpcf.analyses.ifds.{ForwardNativeIFDSAnalysis, LLVMStatement, NativeIFDSAnalysisScheduler} +import org.opalj.ll.fpcf.properties.NativeTaint +import org.opalj.ll.llvm.Function +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, Variable} +import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation} + +class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaintProblem(p) { + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(Function, Fact)] = Seq.empty + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Function): Boolean = + callee.name == "sanitize" + + /** + * We do not sanitize parameters. + */ + override protected def sanitizeParameters(call: LLVMStatement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: Function, call: LLVMStatement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact( + callee: Function, + call: LLVMStatement, + in: Set[Fact] + ): Option[FlowFact[NativeTaint.Callable]] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.function))) + else None +} + +class SimpleNativeForwardTaintAnalysis(implicit val project: SomeProject) + extends ForwardNativeIFDSAnalysis(new SimpleNativeForwardTaintProblem(project), NativeTaint) + +object NativeForwardTaintAnalysisScheduler extends NativeIFDSAnalysisScheduler[Fact] { + override def init(p: SomeProject, ps: PropertyStore) = new SimpleNativeForwardTaintAnalysis()(p) + override def property: IFDSPropertyMetaInformation[LLVMStatement, Fact] = NativeTaint + override val uses: Set[PropertyBounds] = super.uses // ++ PropertyBounds.ub(Taint) TODO: we do not use the native taint yet +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index e978cb47a7..7f96ee3b35 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -1,59 +1,22 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds.taint -import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.SomeProject -import org.opalj.ll.fpcf.analyses.ifds.NativeIFDSProblem -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, NullFact, TaintProblem} -import org.opalj.tac.{ArrayLength, ArrayLoad, ArrayStore, Assignment, BinaryExpr, Compare, Expr, GetField, GetStatic, NewArray, PrefixExpr, PrimitiveTypecastExpr, PutField, PutStatic, ReturnValue, Var} -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaIFDSProblem, JavaStatement} +import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeIFDSProblem} +import org.opalj.ll.llvm.Function -abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[Fact](project) { +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact, TaintProblem} + +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[Fact](project) with TaintProblem[Function, LLVMStatement] { override def nullFact: Fact = NullFact /** * If a variable gets assigned a tainted value, the variable will be tainted. */ - override def normalFlow(statement: JavaStatement, successor: JavaStatement, + override def normalFlow(statement: LLVMStatement, successor: LLVMStatement, in: Set[Fact]): Set[Fact] = - statement.stmt.astID match { - case Assignment.ASTID ⇒ - in ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) - case ArrayStore.ASTID ⇒ - val store = statement.stmt.asArrayStore - val definedBy = store.arrayRef.asVar.definedBy - val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) - if (isTainted(store.value, in)) { - if (arrayIndex.isDefined) - // Taint a known array index - definedBy.foldLeft(in) { (c, n) ⇒ - c + ArrayElement(n, arrayIndex.get) - } - else - // Taint the whole array if the index is unknown - definedBy.foldLeft(in) { (c, n) ⇒ - c + Variable(n) - } - } else if (arrayIndex.isDefined && definedBy.size == 1) - // Untaint if possible - in - ArrayElement(definedBy.head, arrayIndex.get) - else in - case PutField.ASTID ⇒ - val put = statement.stmt.asPutField - val definedBy = put.objRef.asVar.definedBy - if (isTainted(put.value, in)) - definedBy.foldLeft(in) { (in, defSite) ⇒ - in + InstanceField(defSite, put.declaringClass, put.name) - } - else - in - case PutStatic.ASTID ⇒ - val put = statement.stmt.asPutStatic - if (isTainted(put.value, in)) - in + StaticField(put.declaringClass, put.name) - else - in + statement match { + // TODO case _ ⇒ in } @@ -62,58 +25,10 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * parameter is detected, no call-to-start * edges will be created. */ - override def callFlow(call: JavaStatement, callee: DeclaredMethod, - in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = { - val callObject = asCall(call.stmt) - val allParams = callObject.allParams - var facts = Set.empty[Fact] - - if (relevantCallee(callee)) { - val allParamsWithIndices = allParams.zipWithIndex - in.foreach { - // Taint formal parameter if actual parameter is tainted - case Variable(index) ⇒ - allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( - paramIndex, - callee.definedMethod.isStatic - )) - case _ ⇒ // Nothing to do - } - - // Taint element of formal parameter if element of actual parameter is tainted - case ArrayElement(index, taintedIndex) ⇒ - allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), - taintedIndex - ) - case _ ⇒ // Nothing to do - } - - case InstanceField(index, declClass, taintedField) ⇒ - // Taint field of formal parameter if field of actual parameter is tainted - // Only if the formal parameter is of a type that may have that field! - allParamsWithIndices.foreach { - case (param, pIndex) if param.asVar.definedBy.contains(index) && - (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || - project.classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ - facts += InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), - declClass, taintedField - ) - case _ ⇒ // Nothing to do - } - - case sf: StaticField ⇒ facts += sf - - case _ ⇒ // Nothing to do - } - } - - facts + override def callFlow(call: LLVMStatement, callee: Function, + in: Set[Fact], source: (Function, Fact)): Set[Fact] = { + // TODO + Set.empty[Fact] } /** @@ -125,86 +40,19 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * Creates new taints and FlowFacts, if necessary. * If the sanitize method was called, nothing will be tainted. */ - override def returnFlow(call: JavaStatement, callee: DeclaredMethod, exit: JavaStatement, - successor: JavaStatement, in: Set[Fact]): Set[Fact] = { - - /** - * Checks whether the callee's formal parameter is of a reference type. - */ - def isRefTypeParam(index: Int): Boolean = - if (index == -1) true - else { - val parameterOffset = if (callee.definedMethod.isStatic) 0 else 1 - callee.descriptor.parameterType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) - - parameterOffset - ).isReferenceType - } - - if (sanitizesReturnValue(callee)) return Set.empty - val callStatement = asCall(call.stmt) - val allParams = callStatement.allParams - var flows: Set[Fact] = Set.empty - in.foreach { - // Taint actual parameter if formal parameter is tainted - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ - val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) - ) - flows ++= param.asVar.definedBy.iterator.map(Variable) - - // Taint element of actual parameter if element of formal parameter is tainted - case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ - val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) - ) - flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) - - case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ - // Taint field of actual parameter if field of formal parameter is tainted - val param = - allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) - param.asVar.definedBy.foreach { defSite ⇒ - flows += InstanceField(defSite, declClass, taintedField) - } - - case sf: StaticField ⇒ flows += sf - - // Track the call chain to the sink back - case FlowFact(flow) if !flow.contains(call.method) ⇒ - flows += FlowFact(call.method +: flow) - case _ ⇒ - } - - // Propagate taints of the return value - if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { - val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy - in.foreach { - case Variable(index) if returnValueDefinedBy.contains(index) ⇒ - flows += Variable(call.index) - case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ - flows += ArrayElement(call.index, taintedIndex) - case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ - flows += InstanceField(call.index, declClass, taintedField) - case NullFact ⇒ - val taints = createTaints(callee, call) - if (taints.nonEmpty) flows ++= taints - case _ ⇒ // Nothing to do - } - } - val flowFact = createFlowFact(callee, call, in) - if (flowFact.isDefined) flows += flowFact.get - - flows + override def returnFlow(call: LLVMStatement, callee: Function, exit: LLVMStatement, + successor: LLVMStatement, in: Set[Fact]): Set[Fact] = { + // TODO + Set.empty } /** * Removes taints according to `sanitizeParamters`. */ - override def callToReturnFlow(call: JavaStatement, successor: JavaStatement, + override def callToReturnFlow(call: LLVMStatement, successor: LLVMStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Set[Fact] = - in -- sanitizeParamters(call, in) + source: (Function, Fact)): Set[Fact] = + in -- sanitizeParameters(call, in) /** * Called, when the exit to return facts are computed for some `callee` with the null fact and @@ -215,7 +63,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @param call The call. * @return Some variable fact, if necessary. Otherwise none. */ - protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] + protected def createTaints(callee: Function, call: LLVMStatement): Set[Fact] /** * Called, when the call to return facts are computed for some `callee`. @@ -225,31 +73,18 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @param call The call. * @return Some FlowFact, if necessary. Otherwise None. */ - protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, - in: Set[Fact]): Option[FlowFact] + protected def createFlowFact(callee: Function, call: LLVMStatement, + in: Set[Fact]): Option[FlowFact[Function]] /** * If a parameter is tainted, the result will also be tainted. * We assume that the callee does not call the source method. */ - override def callOutsideOfAnalysisContext(statement: JavaStatement, callee: DeclaredMethod, - successor: JavaStatement, + override def callOutsideOfAnalysisContext(statement: LLVMStatement, callee: Function, + successor: LLVMStatement, in: Set[Fact]): Set[Fact] = { - val allParams = asCall(statement.stmt).receiverOption ++ asCall(statement.stmt).params - if (statement.stmt.astID == Assignment.ASTID && in.exists { - case Variable(index) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - } - case ArrayElement(index, _) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - } - case _ ⇒ false - }) Set(Variable(statement.index)) - else Set.empty + // TODO + Set.empty } /** @@ -259,80 +94,5 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @param callee The callee. * @return True, by default. */ - protected def relevantCallee(callee: DeclaredMethod): Boolean = true - - /** - * Creates new facts for an assignment. A new fact for the assigned variable will be created, - * if the expression contains a tainted variable - * - * @param expression The source expression of the assignment - * @param statement The assignment statement - * @param in The incoming facts - * @return The new facts, created by the assignment - */ - private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: Set[Fact]): Set[Fact] = - expression.astID match { - case Var.ASTID ⇒ - val definedBy = expression.asVar.definedBy - in.collect { - case Variable(index) if definedBy.contains(index) ⇒ - Variable(statement.index) - } - case ArrayLoad.ASTID ⇒ - val loadExpression = expression.asArrayLoad - val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy - if (in.exists { - // One specific array element may be tainted - case ArrayElement(index, taintedElement) ⇒ - val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) - arrayDefinedBy.contains(index) && - (loadedIndex.isEmpty || taintedElement == loadedIndex.get) - // Or the whole array - case Variable(index) ⇒ arrayDefinedBy.contains(index) - case _ ⇒ false - }) Set(Variable(statement.index)) - else - Set.empty - case GetField.ASTID ⇒ - val get = expression.asGetField - val objectDefinedBy = get.objRef.asVar.definedBy - if (in.exists { - // The specific field may be tainted - case InstanceField(index, _, taintedField) ⇒ - taintedField == get.name && objectDefinedBy.contains(index) - // Or the whole object - case Variable(index) ⇒ objectDefinedBy.contains(index) - case _ ⇒ false - }) - Set(Variable(statement.index)) - else - Set.empty - case GetStatic.ASTID ⇒ - val get = expression.asGetStatic - if (in.contains(StaticField(get.declaringClass, get.name))) - Set(Variable(statement.index)) - else Set.empty - case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | - NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ - acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) - case _ ⇒ Set.empty - } - - /** - * Checks, if the result of some variable expression could be tainted. - * - * @param expression The variable expression. - * @param in The current data flow facts. - * @return True, if the expression could be tainted - */ - private def isTainted(expression: Expr[V], in: Set[Fact]): Boolean = { - val definedBy = expression.asVar.definedBy - expression.isVar && in.exists { - case Variable(index) ⇒ definedBy.contains(index) - case ArrayElement(index, _) ⇒ definedBy.contains(index) - case InstanceField(index, _, _) ⇒ definedBy.contains(index) - case _ ⇒ false - } - } + protected def relevantCallee(callee: Function): Boolean = true } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala index 4cf9281a9f..c3f5401e34 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.properties +import org.opalj.br.Method import org.opalj.fpcf.PropertyKey import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement import org.opalj.tac.fpcf.analyses.ifds.taint.Fact @@ -17,6 +18,7 @@ case class NativeTaint(flows: Map[LLVMStatement, Set[Fact]]) extends IFDSPropert object NativeTaint extends IFDSPropertyMetaInformation[LLVMStatement, Fact] { override type Self = NativeTaint + type Callable = Either[Function, Method] override def create(result: Map[LLVMStatement, Set[Fact]]): IFDSProperty[LLVMStatement, Fact] = new NativeTaint(result) val key: PropertyKey[NativeTaint] = PropertyKey.create("NativeTaint", new NativeTaint(Map.empty)) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala index d14039e237..701e321254 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala @@ -2,26 +2,25 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} -import org.bytedeco.llvm.global.LLVM.{ - LLVMBasicBlockAsValue, - LLVMGetBasicBlockName, - LLVMGetBasicBlockTerminator, - LLVMGetFirstInstruction, - LLVMGetNextInstruction -} +import org.bytedeco.llvm.global.LLVM.{LLVMBasicBlockAsValue, LLVMGetBasicBlockName, LLVMGetBasicBlockParent, LLVMGetBasicBlockTerminator, LLVMGetFirstInstruction, LLVMGetLastInstruction, LLVMGetNextInstruction} import org.opalj.graphs.Node case class BasicBlock(block_ref: LLVMBasicBlockRef) extends Value(LLVMBasicBlockAsValue(block_ref)) with Node { - def instructions(): InstructionIterator = { - new InstructionIterator(LLVMGetFirstInstruction(block_ref)) - } + def parent() = Function(LLVMGetBasicBlockParent(block_ref)) + def instructions(): InstructionIterator = new InstructionIterator(LLVMGetFirstInstruction(block_ref)) + def firstInstruction(): Instruction = Instruction(LLVMGetFirstInstruction(block_ref)) + def lastInstruction(): Instruction = Instruction(LLVMGetLastInstruction(block_ref)) - def terminator(): Terminator = { - val instruction = Instruction(LLVMGetBasicBlockTerminator(block_ref)) - assert(instruction.is_terminator()) - instruction.asInstanceOf[Terminator] + def terminator(): Option[Instruction with Terminator] = { + OptionalInstruction(LLVMGetBasicBlockTerminator(block_ref)) match { + case Some(terminator) => { + assert(terminator.isTerminator()) + Some(terminator.asInstanceOf[Instruction with Terminator]) + } + case None => None + } } def blockName(): String = LLVMGetBasicBlockName(block_ref).getString @@ -45,16 +44,18 @@ case class BasicBlock(block_ref: LLVMBasicBlockRef) /** * Returns `true` if this node has successor nodes. */ - override def hasSuccessors: Boolean = { - terminator.hasSuccessors() - } + override def hasSuccessors: Boolean = terminator match { + case Some(t) => t.hasSuccessors + case None => false + } /** * Applies the given function for each successor node. */ - override def foreachSuccessor(f: Node ⇒ Unit): Unit = { - terminator.foreachSuccessor(f) - } + override def foreachSuccessor(f: Node ⇒ Unit): Unit = terminator match { + case Some(t) => t.foreachSuccessor(f) + case None => + } } class InstructionIterator(var ref: LLVMValueRef) extends Iterator[Instruction] { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala index 5f999dbca5..e51aad55dd 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala @@ -4,6 +4,13 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM._ +object OptionalInstruction { + def apply(ref: LLVMValueRef): Option[Instruction] = { + if (ref.isNull) return None + Some(Instruction(ref)) + } +} + object Instruction { def apply(ref: LLVMValueRef): Instruction = { assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") @@ -91,7 +98,10 @@ trait Terminator { } sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { - def is_terminator(): Boolean = !LLVMIsATerminatorInst(ref).isNull + def isTerminator(): Boolean = !LLVMIsATerminatorInst(ref).isNull + def parent(): BasicBlock = BasicBlock(LLVMGetInstructionParent(ref)) + def function(): Function = parent.parent + def next(): Option[Instruction] = OptionalInstruction(LLVMGetNextInstruction(ref)) } case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index 8138129340..05777d5232 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -55,7 +55,7 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement], val propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends FPCFAnalysis - with Subsumable[IFDSFact] { + with Subsumable[JavaStatement, IFDSFact] { /** * All declared methods in the project. @@ -885,7 +885,7 @@ class NumberOfCalls { var callToReturnFlow = 0 } -abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalysisScheduler { +abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalysisScheduler { final override type InitializationData = AbstractIFDSAnalysis[IFDSFact] @@ -927,7 +927,7 @@ abstract class IFDSAnalysis[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalys abstract class AbsractIFDSAnalysisRunner { - protected def analysisClass: IFDSAnalysis[_] + protected def analysisClass: IFDSAnalysisScheduler[_] protected def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysisScheduler.scala similarity index 88% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysisScheduler.scala index c792457dd1..2744ced696 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysisScheduler.scala @@ -24,20 +24,20 @@ import org.opalj.tac.fpcf.properties.TACAI * @param project The analyzed project. * @author Mario Trageser */ -class IFDSBasedVariableTypeAnalysis private (ifdsProblem: VariableTypeProblem)(implicit val project: SomeProject) +class IFDSBasedVariableTypeAnalysisScheduler private(ifdsProblem: VariableTypeProblem)(implicit val project: SomeProject) extends ForwardIFDSAnalysis[VTAFact](ifdsProblem, VTAResult) -object IFDSBasedVariableTypeAnalysis extends IFDSAnalysis[VTAFact] { +object IFDSBasedVariableTypeAnalysisScheduler extends IFDSAnalysisScheduler[VTAFact] { var SUBSUMING: Boolean = true override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) - override def init(p: SomeProject, ps: PropertyStore): IFDSBasedVariableTypeAnalysis = { + override def init(p: SomeProject, ps: PropertyStore): IFDSBasedVariableTypeAnalysisScheduler = { implicit val project = p - if (SUBSUMING) new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) with Subsuming[VTAFact] - else new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) + if (SUBSUMING) new IFDSBasedVariableTypeAnalysisScheduler(new VariableTypeProblem(p)) with Subsuming[VTAFact] + else new IFDSBasedVariableTypeAnalysisScheduler(new VariableTypeProblem(p)) } override def property: IFDSPropertyMetaInformation[JavaStatement, VTAFact] = VTAResult @@ -64,7 +64,7 @@ object VTAResult extends IFDSPropertyMetaInformation[JavaStatement, VTAFact] { class IFDSBasedVariableTypeAnalysisRunner extends AbsractIFDSAnalysisRunner { - override def analysisClass: IFDSBasedVariableTypeAnalysis.type = IFDSBasedVariableTypeAnalysis + override def analysisClass: IFDSBasedVariableTypeAnalysisScheduler.type = IFDSBasedVariableTypeAnalysisScheduler override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = {} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala index 553a2062df..b92afc23ed 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala @@ -22,7 +22,7 @@ trait AbstractIFDSNullFact extends AbstractIFDSFact * @author Dominik Helm * @author Mario Trageser */ -abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val project: SomeProject) { +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](val project: SomeProject) { /** * The null fact of this analysis. */ @@ -31,7 +31,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val /** * The entry points of this analysis. */ - def entryPoints: Seq[(Method, IFDSFact)] + def entryPoints: Seq[(Callable, IFDSFact)] /** * Computes the data flow for a normal statement. @@ -61,10 +61,10 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val * the facts in `in` held before `statement` and `statement` calls `callee`. */ def callFlow( - call: Statement, - callee: Method, - in: Set[IFDSFact], - source: (Method, IFDSFact) + call: Statement, + callee: Callable, + in: Set[IFDSFact], + source: (Callable, IFDSFact) ): Set[IFDSFact] /** @@ -81,11 +81,11 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val * `successor` will be executed next. */ def returnFlow( - call: Statement, - callee: Method, - exit: Statement, - successor: Statement, - in: Set[IFDSFact] + call: Statement, + callee: Callable, + exit: Statement, + successor: Statement, + in: Set[IFDSFact] ): Set[IFDSFact] /** @@ -102,7 +102,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val call: Statement, successor: Statement, in: Set[IFDSFact], - source: (Method, IFDSFact) + source: (Callable, IFDSFact) ): Set[IFDSFact] /** @@ -113,7 +113,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val * @param callee The callee. * @return True, if the callee is inside the analysis context. */ - def insideAnalysisContext(callee: Method): Boolean + def insideAnalysisContext(callee: Callable): Boolean /** * When a callee outside of this analysis' context is called, this method computes the summary @@ -126,10 +126,10 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val * @return The facts, which hold after the call, excluding the call to return flow. */ def callOutsideOfAnalysisContext( - call: Statement, - callee: Method, - successor: Statement, - in: Set[IFDSFact] + call: Statement, + callee: Callable, + successor: Statement, + in: Set[IFDSFact] ): Set[IFDSFact] /** @@ -141,11 +141,11 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Method, Statement](val */ def getCallees( statement: Statement, - caller: Method - ): Iterator[Method] + caller: Callable + ): Iterator[Callable] - def delegateAnalysis(source: (Method, IFDSFact)): Option[((Entity, IFDSFact), Set[IFDSFact] => Set[IFDSFact])] = None - def specialCase(source: (Method, IFDSFact), propertyKey: IFDSPropertyMetaInformation[Statement, IFDSFact]): Option[ProperPropertyComputationResult] = None + def delegateAnalysis(source: (Callable, IFDSFact)): Option[((Entity, IFDSFact), Set[IFDSFact] => Set[IFDSFact])] = None + def specialCase(source: (Callable, IFDSFact), propertyKey: IFDSPropertyMetaInformation[Statement, IFDSFact]): Option[ProperPropertyComputationResult] = None } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala index afffc3a991..3996fc340a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala @@ -34,7 +34,7 @@ trait SubsumableNullFact extends SubsumableFact with AbstractIFDSNullFact { * * @author Mario Trageser */ -trait Subsumable[IFDSFact <: AbstractIFDSFact] { +trait Subsumable[Statement, IFDSFact <: AbstractIFDSFact] { /** * A subclass can override this method to filter the `facts` in some set, which are not subsumed @@ -77,10 +77,10 @@ trait Subsumable[IFDSFact <: AbstractIFDSFact] { * `oldExitFacts` are not present. */ protected def filterNewInformation[T <: IFDSFact]( - newExitFacts: Map[JavaStatement, Set[T]], - oldExitFacts: Map[JavaStatement, Set[T]], + newExitFacts: Map[Statement, Set[T]], + oldExitFacts: Map[Statement, Set[T]], project: SomeProject - ): Map[JavaStatement, Set[T]] = { + ): Map[Statement, Set[T]] = { var result = newExitFacts for ((key, values) ← oldExitFacts) { result = result.updated(key, result(key) -- values) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala index 03603c432b..f54315d228 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala @@ -7,7 +7,7 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V import org.opalj.tac.{ArrayLength, ArrayLoad, ArrayStore, Assignment, BinaryExpr, Compare, Expr, GetField, NewArray, PrefixExpr, PrimitiveTypecastExpr, PutField, PutStatic, ReturnValue, Var} import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaBackwardIFDSProblem, JavaStatement} -abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[Fact, UnbalancedTaintFact](project) with TaintProblem { +abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[Fact, UnbalancedTaintFact](project) with TaintProblem[DeclaredMethod, JavaStatement] { override def nullFact: Fact = NullFact /** @@ -123,7 +123,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = { val flowFact = createFlowFactAtCall(call, in, source) - val result = in -- sanitizeParamters(call, in) + val result = in -- sanitizeParameters(call, in) if (flowFact.isDefined) result + flowFact.get else result } @@ -166,7 +166,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @return Some FlowFact, if necessary. Otherwise None. */ protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact] + source: (DeclaredMethod, Fact)): Option[FlowFact[Method]] /** * Called, when a FlowFact holds at the start node of a callee. Creates a FlowFact in the caller @@ -177,9 +177,9 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @return Some FlowFact, if necessary. Otherwise None. */ protected def applyFlowFactFromCallee( - calleeFact: FlowFact, + calleeFact: FlowFact[Method], source: (DeclaredMethod, Fact) - ): Option[FlowFact] + ): Option[FlowFact[Method]] /** * Called, when new FlowFacts are found at the beginning of a method. @@ -192,7 +192,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF protected def createFlowFactAtBeginningOfMethod( in: Set[Fact], source: (DeclaredMethod, Fact) - ): Option[FlowFact] + ): Option[FlowFact[Method]] /** * Propagates the call to createFlowFactAtBeginningOfMethod. @@ -349,7 +349,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF ) case staticField: StaticField ⇒ facts += staticField // If the source was reached in a callee, create a flow fact from this method to the sink. - case calleeFact: FlowFact if isCallFlow ⇒ + case calleeFact: FlowFact[Method] if isCallFlow ⇒ val callerFact = applyFlowFactFromCallee(calleeFact, source) if (callerFact.isDefined) facts += callerFact.get case _ ⇒ // Nothing to do diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 7a3e6dc8be..ec2b1d5d04 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -1,13 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.br.DeclaredMethod +import org.opalj.br.{DeclaredMethod, Method} import org.opalj.br.analyses.SomeProject import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V import org.opalj.tac.{ArrayLength, ArrayLoad, ArrayStore, Assignment, BinaryExpr, Compare, Expr, GetField, GetStatic, NewArray, PrefixExpr, PrimitiveTypecastExpr, PutField, PutStatic, ReturnValue, Var} import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaIFDSProblem, JavaStatement} -abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[Fact](project) with TaintProblem { +abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[Fact](project) with TaintProblem[DeclaredMethod, JavaStatement] { override def nullFact: Fact = NullFact /** @@ -202,7 +202,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem override def callToReturnFlow(call: JavaStatement, successor: JavaStatement, in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = - in -- sanitizeParamters(call, in) + in -- sanitizeParameters(call, in) /** * Called, when the exit to return facts are computed for some `callee` with the null fact and @@ -224,7 +224,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @return Some FlowFact, if necessary. Otherwise None. */ protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, - in: Set[Fact]): Option[FlowFact] + in: Set[Fact]): Option[FlowFact[Method]] /** * If a parameter is tainted, the result will also be tainted. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index 1cdafd1fea..5c700d1b3a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -1,7 +1,6 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.br.DeclaredMethod import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, JavaStatement, UnbalancedReturnFact} @@ -52,7 +51,7 @@ case class InstanceField(index: Int, classType: ObjectType, fieldName: String) e * * @param flow A sequence of method calls, originating from but not including this method. */ -case class FlowFact(flow: Seq[Method]) extends Fact { +case class FlowFact[Callable](flow: Seq[Callable]) extends Fact { override val hashCode: Int = { var r = 1 flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) @@ -70,7 +69,7 @@ case class FlowFact(flow: Seq[Method]) extends Fact { case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Seq[Method]) extends UnbalancedReturnFact[Fact] with Fact -trait TaintProblem { +trait TaintProblem[Callable, Statement] { /** * Checks, if some `callee` is a sanitizer, which sanitizes its return value. @@ -79,7 +78,7 @@ trait TaintProblem { * @param callee The method, which was called. * @return True, if the method is a sanitizer. */ - protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean + protected def sanitizesReturnValue(callee: Callable): Boolean /** * Called in callToReturnFlow. This method can return facts, which will be removed after @@ -89,7 +88,7 @@ trait TaintProblem { * @param in The facts, which hold before the call. * @return Facts, which will be removed from `in` after the call. */ - protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] + protected def sanitizeParameters(call: Statement, in: Set[Fact]): Set[Fact] } object TaintProblem { diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala similarity index 84% rename from OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala rename to OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala index 49bd24d23c..ab7baa3861 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala @@ -5,7 +5,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod import org.opalj.br.Method -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysis, JavaStatement, UnbalancedReturnFact} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement, UnbalancedReturnFact} import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Method]) @@ -17,7 +17,7 @@ case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Met * * @author Mario Trageser */ -class BackwardTaintAnalysisFixture private (implicit val project: SomeProject) +class BackwardTaintAnalysisFixtureScheduler private(implicit val project: SomeProject) extends BackwardIFDSAnalysis(new BackwardTaintProblemFixture(project), Taint) class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p) { @@ -36,7 +36,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p /** * We do not sanitize paramters. */ - override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * Create a flow fact, if a source method is called and the returned value is tainted. @@ -45,7 +45,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p * In this case, callFlow would never be called and no FlowFact would be created. */ override protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact] = { + source: (DeclaredMethod, Fact)): Option[FlowFact[Method]] = { if (in.exists { case Variable(index) ⇒ index == call.index case _ ⇒ false @@ -62,9 +62,9 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p * When a callee calls the source, we create a FlowFact with the caller's call chain. */ override protected def applyFlowFactFromCallee( - calleeFact: FlowFact, + calleeFact: FlowFact[Method], source: (DeclaredMethod, Fact) - ): Option[FlowFact] = + ): Option[FlowFact[Method]] = Some(FlowFact(currentCallChain(source))) /** @@ -74,13 +74,13 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p override protected def createFlowFactAtBeginningOfMethod( in: Set[Fact], source: (DeclaredMethod, Fact) - ): Option[FlowFact] = + ): Option[FlowFact[Method]] = None } -object BackwardTaintAnalysisFixture extends IFDSAnalysis[Fact] { +object BackwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new BackwardTaintAnalysisFixture()(p) + override def init(p: SomeProject, ps: PropertyStore) = new BackwardTaintAnalysisFixtureScheduler()(p) override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala similarity index 84% rename from OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala rename to OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala index 6bcd05dc43..e7605bab7f 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala @@ -3,8 +3,8 @@ package org.opalj.tac.fpcf.analyses.ifds.taint import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.SomeProject -import org.opalj.br.DeclaredMethod -import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysis, JavaStatement} +import org.opalj.br.{DeclaredMethod, Method} +import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement} import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} /** @@ -13,7 +13,7 @@ import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} * * @author Mario Trageser */ -class ForwardTaintAnalysisFixture private (implicit val project: SomeProject) +class ForwardTaintAnalysisFixtureScheduler private(implicit val project: SomeProject) extends ForwardIFDSAnalysis(new ForwardTaintProblemFixture(project), Taint) class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) { @@ -35,7 +35,7 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) /** * We do not sanitize paramters. */ - override protected def sanitizeParamters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * Creates a new variable fact for the callee, if the source was called. @@ -49,14 +49,14 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) * Note, that sink does not accept array parameters. No need to handle them. */ override protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, - in: Set[Fact]): Option[FlowFact] = + in: Set[Fact]): Option[FlowFact[Method]] = if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) else None } -object ForwardTaintAnalysisFixture extends IFDSAnalysis[Fact] { +object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture()(p) + override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixtureScheduler()(p) override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint } From cb4f4f7b0f7339cc7eaf1706e19ec317bfc52e09 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 11 Mar 2022 00:43:25 +0100 Subject: [PATCH 20/67] WIP --- ...rdClassForNameTaintAnalysisScheduler.scala | 2 +- ...rdClassForNameTaintAnalysisScheduler.scala | 2 +- ...MultilingualForwardTaintAnalysisTest.scala | 2 +- .../ifds/AbstractNativeIFDSAnalysis.scala | 1249 ++++++++--------- .../ll/fpcf/analyses/ifds/Callable.scala | 9 + .../ifds/ForwardNativeIFDSAnalysis.scala | 317 ++--- .../opalj/ll/fpcf/analyses/ifds/ICFG.scala | 71 + .../analyses/ifds/NativeForwardICFG.scala | 78 + .../analyses/ifds/NativeIFDSProblem.scala | 26 +- .../ifds/taint/JavaForwardTaintAnalysis.scala | 106 +- .../taint/NativeForwardTaintAnalysis.scala | 82 +- .../taint/NativeForwardTaintProblem.scala | 161 ++- .../org/opalj/ll/fpcf/properties/Taint.scala | 14 +- .../scala/org/opalj/ll/llvm/BasicBlock.scala | 22 +- .../scala/org/opalj/ll/llvm/Instruction.scala | 15 +- .../analyses/ifds/AbstractIFDSAnalysis.scala | 22 +- ...la => IFDSBasedVariableTypeAnalysis.scala} | 12 +- .../tac/fpcf/analyses/ifds/IFDSProblem.scala | 28 +- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 53 +- .../tac/fpcf/analyses/ifds/Subsuming.scala | 8 +- .../ifds/taint/BackwardTaintProblem.scala | 2 +- .../analyses/ifds/taint/TaintProblem.scala | 9 +- ...ackwardTaintAnalysisFixtureScheduler.scala | 4 +- ...ForwardTaintAnalysisFixtureScheduler.scala | 4 +- 24 files changed, 1131 insertions(+), 1167 deletions(-) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{IFDSBasedVariableTypeAnalysisScheduler.scala => IFDSBasedVariableTypeAnalysis.scala} (89%) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index 32dcce428e..73610c3ed8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -18,7 +18,7 @@ import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} * * @author Mario Trageser */ -class BackwardClassForNameTaintAnalysisScheduler private(implicit val project: SomeProject) +class BackwardClassForNameTaintAnalysisScheduler private (implicit val project: SomeProject) extends BackwardIFDSAnalysis(new BackwardClassForNameTaintProblem(project), Taint) class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProblem(p) { diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala index d31f92d9b7..d4e5846aad 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -19,7 +19,7 @@ import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation, * @author Mario Trageser * @author Michael Eichberg */ -class ForwardClassForNameTaintAnalysis$Scheduler private(implicit val project: SomeProject) +class ForwardClassForNameTaintAnalysis$Scheduler private (implicit val project: SomeProject) extends ForwardIFDSAnalysis(new ForwardClassForNameTaintProblem(project), Taint) class ForwardClassForNameTaintProblem(project: SomeProject) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index e44f40411b..9086636ae3 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -19,7 +19,7 @@ class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers new java.io.File("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint") ) project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( - current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll") + current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll") ) project.get(LLVMProjectKey) project.get(RTACallGraphKey) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala index 01ec801755..597b3c091c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala @@ -2,45 +2,16 @@ package org.opalj.ll.fpcf.analyses.ifds -import scala.collection.{Set => SomeSet} -import scala.collection.mutable - -import org.opalj.fpcf.EOptionP -import org.opalj.fpcf.EPK -import org.opalj.fpcf.FinalE -import org.opalj.fpcf.FinalEP -import org.opalj.fpcf.FinalP -import org.opalj.fpcf.InterimEUBP -import org.opalj.fpcf.InterimResult -import org.opalj.fpcf.InterimUBP -import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.fpcf.Property -import org.opalj.fpcf.PropertyBounds -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.Result -import org.opalj.fpcf.SomeEOptionP -import org.opalj.fpcf.SomeEPS -import org.opalj.fpcf.seq.PKESequentialPropertyStore -import org.opalj.value.ValueInformation -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.tac.fpcf.properties.cg.Callees -import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.ai.domain.l0.PrimitiveTACAIDomain -import org.opalj.ai.domain.l2 -import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.tac.fpcf.properties.IFDSProperty -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSAnalysisScheduler, IFDSProblem, NumberOfCalls, Subsumable} -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} +import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} +import org.opalj.fpcf._ import org.opalj.ll.LLVMProjectKey import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem, NumberOfCalls, Subsumable} +import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation} +import org.opalj.tac.fpcf.properties.cg.Callees + +import scala.collection.{mutable, Set ⇒ SomeSet} /** * @@ -50,525 +21,458 @@ import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} */ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( - val ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement], - val propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact]) - extends FPCFAnalysis + val ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement], + val icfg: ICFG[IFDSFact, Function, LLVMStatement, BasicBlock], + val propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] +) + extends FPCFAnalysis with Subsumable[LLVMStatement, IFDSFact] { - /** - * Counts, how many times the abstract methods were called. - */ - var numberOfCalls = new NumberOfCalls() - - /** - * Counts, how many input facts are passed to callbacks. - */ - var sumOfInputfactsForCallbacks = 0L - - /** - * The state of the analysis. For each method and source fact, there is a separate state. - * - * @param declaringClass The class defining the analyzed `method`. - * @param method The analyzed method. - * @param source The input fact, for which the `method` is analyzed. - * @param code The code of the `method`. - * @param cfg The control flow graph of the `method`. - * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input - * facts to the instruction, at which they may be called. - * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input - * facts to the intermediate result of their IFDS analysis. - * Only contains method-fact-pairs, for which this analysis is - * waiting for a final result. - * @param pendingCgCallSites The basic blocks containing call sites, for which the analysis is - * waiting for the final call graph result. - * @param incomingFacts Maps each basic block to the data flow facts valid at its first - * statement. - * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at - * the beginning of the successor. - */ - protected class State( - val function: Function, - val source: (Function, IFDSFact), - var pendingIfdsCallSites: Map[(Function, IFDSFact), Set[LLVMStatement]] = Map.empty, - var pendingIfdsDependees: Map[(Function, IFDSFact), EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] = Map.empty, - var pendingCgCallSites: Set[BasicBlock] = Set.empty, - var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, - var outgoingFacts: Map[BasicBlock, Map[BasicBlock, Set[IFDSFact]]] = Map.empty - ) - - type QueueEntry = (BasicBlock, Set[IFDSFact], Option[LLVMStatement], Option[Function], Option[IFDSFact]) - - /** - * Determines the basic blocks, at which the analysis starts. - * - * @param sourceFact The source fact of the analysis. - * @param cfg The control flow graph of the analyzed method. - * @return The basic blocks, at which the analysis starts. - */ - protected def startBlocks(sourceFact: IFDSFact, cfg: Function): Set[BasicBlock] - - /** - * Collects the facts valid at all exit nodes based on the current results. - * - * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at - * the exit node under the assumption that the predecessor was executed before. - */ - protected def collectResult(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] - - /** - * Creates an IFDSProperty containing the result of this analysis. - * - * @param result Maps each exit statement to the facts, which hold after the exit statement. - * @return An IFDSProperty containing the `result`. - */ - protected def createPropertyValue(result: Map[LLVMStatement, Set[IFDSFact]]): IFDSProperty[LLVMStatement, IFDSFact] = propertyKey.create(result) - - /** - * Determines the nodes, that will be analyzed after some `basicBlock`. - * - * @param basicBlock The basic block, that was analyzed before. - * @return The nodes, that will be analyzed after `basicBlock`. - */ - protected def nextNodes(basicBlock: BasicBlock): Set[BasicBlock] - - /** - * Checks, if some `node` is the last node. - * - * @return True, if `node` is the last node, i.e. there is no next node. - */ - protected def isLastNode(node: BasicBlock): Boolean - - /** - * Determines the first index of some `basic block`, that will be analyzed. - * - * @param basicBlock The basic block. - * @return The first index of some `basic block`, that will be analyzed. - */ - protected def firstIndex(basicBlock: BasicBlock): Int - - /** - * Determines the last index of some `basic block`, that will be analzyed. - * - * @param basicBlock The basic block. - * @return The last index of some `basic block`, that will be analzyed. - */ - protected def lastIndex(basicBlock: BasicBlock): Int - - /** - * Determines the index, that will be analyzed after some other `index`. - * - * @param index The source index. - * @return The index, that will be analyzed after `index`. - */ - protected def nextIndex(index: Int): Int - - /** - * Gets the first statement of a node, that will be analyzed. - * - * @param node The node. - * @return The first statement of a node, that will be analyzed. - */ - protected def firstStatement(basicBlock: BasicBlock)(implicit state: State): LLVMStatement - - /** - * Determines the statement, that will be analyzed after some other `statement`. - * - * @param statement The source statement. - * @return The successor statements - */ - protected def nextStatements(statement: LLVMStatement)(implicit state: State): Set[LLVMStatement] - - /** - * Determines the facts, for which a `callee` is analyzed. - * - * @param call The call, which calls `callee`. - * @param callee The method, which is called by `call`. - * @param in The facts, which hold before the `call`. - * @return The facts, for which `callee` will be analyzed. - */ - protected def callToStartFacts(call: LLVMStatement, callee: Function, in: Set[IFDSFact])( - implicit - state: State - ): Set[IFDSFact] - - /** - * Collects the exit facts of a `callee` and adds them to the `summaryEdges`. - * - * @param summaryEdges The current summary edges. They map successor statements of the `call` - * to facts, which hold before they are executed. - * @param successors The successor of `call`, which is considered. - * @param call The statement, which calls `callee`. - * @param callee The method, called by `call`. - * @param exitFacts Maps exit statements of the `callee` to the facts, which hold after them. - * @return The summary edges plus the exit to return facts for `callee` and `successor`. - */ - protected def addExitToReturnFacts( - summaryEdges: Map[LLVMStatement, Set[IFDSFact]], - successors: Set[LLVMStatement], - call: LLVMStatement, - callee: Function, - exitFacts: Map[LLVMStatement, Set[IFDSFact]] - )(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] - - /** - * Performs an IFDS analysis for a method-fact-pair. - * - * @param entity The method-fact-pair, that will be analyzed. - * @return An IFDS property mapping from exit statements to the facts valid after these exit - * statements. Returns an interim result, if the TAC or call graph of this method or the - * IFDS analysis for a callee is still pending. - */ - def performAnalysis(entity: (Function, IFDSFact)): ProperPropertyComputationResult = { - val (function, sourceFact) = entity - - // Start processing at the start of the cfg with the given source fact - implicit val state: State = - new State(function, entity, Map(entity -> Set.empty), Map()) - val queue = mutable.Queue - .empty[QueueEntry] - startBlocks(sourceFact, function).foreach { start ⇒ - state.incomingFacts += start -> Set(sourceFact) - queue.enqueue((start, Set(sourceFact), None, None, None)) + /** + * Counts, how many times the abstract methods were called. + */ + var numberOfCalls = new NumberOfCalls() + + /** + * Counts, how many input facts are passed to callbacks. + */ + var sumOfInputfactsForCallbacks = 0L + + /** + * The state of the analysis. For each method and source fact, there is a separate state. + * + * @param source The input fact, for which the `method` is analyzed. + * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input + * facts to the instruction, at which they may be called. + * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input + * facts to the intermediate result of their IFDS analysis. + * Only contains method-fact-pairs, for which this analysis is + * waiting for a final result. + * @param pendingCgCallSites The basic blocks containing call sites, for which the analysis is + * waiting for the final call graph result. + * @param incomingFacts Maps each basic block to the data flow facts valid at its first + * statement. + * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at + * the beginning of the successor. + */ + protected class State( + val source: (Function, IFDSFact), + var pendingIfdsCallSites: Map[(Function, IFDSFact), Set[LLVMStatement]] = Map.empty, + var pendingIfdsDependees: Map[(Function, IFDSFact), EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] = Map.empty, + var pendingCgCallSites: Set[BasicBlock] = Set.empty, + var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, + var outgoingFacts: Map[BasicBlock, Map[BasicBlock, Set[IFDSFact]]] = Map.empty + ) + + type QueueEntry = (BasicBlock, Set[IFDSFact], Option[LLVMStatement], Option[Function], Option[IFDSFact]) + + /** + * Collects the facts valid at all exit nodes based on the current results. + * + * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at + * the exit node under the assumption that the predecessor was executed before. + */ + protected def collectResult(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] + + /** + * Creates an IFDSProperty containing the result of this analysis. + * + * @param result Maps each exit statement to the facts, which hold after the exit statement. + * @return An IFDSProperty containing the `result`. + */ + protected def createPropertyValue(result: Map[LLVMStatement, Set[IFDSFact]]): IFDSProperty[LLVMStatement, IFDSFact] = propertyKey.create(result) + + /** + * Determines the facts, for which a `callee` is analyzed. + * + * @param call The call, which calls `callee`. + * @param callee The method, which is called by `call`. + * @param in The facts, which hold before the `call`. + * @return The facts, for which `callee` will be analyzed. + */ + protected def callToStartFacts(call: LLVMStatement, callee: Function, in: Set[IFDSFact])( + implicit + state: State + ): Set[IFDSFact] + + /** + * Collects the exit facts of a `callee` and adds them to the `summaryEdges`. + * + * @param summaryEdges The current summary edges. They map successor statements of the `call` + * to facts, which hold before they are executed. + * @param successors The successor of `call`, which is considered. + * @param call The statement, which calls `callee`. + * @param callee The method, called by `call`. + * @param exitFacts Maps exit statements of the `callee` to the facts, which hold after them. + * @return The summary edges plus the exit to return facts for `callee` and `successor`. + */ + protected def addExitToReturnFacts( + summaryEdges: Map[LLVMStatement, Set[IFDSFact]], + successors: Set[LLVMStatement], + call: LLVMStatement, + callee: Function, + exitFacts: Map[LLVMStatement, Set[IFDSFact]] + )(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] + + /** + * Performs an IFDS analysis for a method-fact-pair. + * + * @param entity The method-fact-pair, that will be analyzed. + * @return An IFDS property mapping from exit statements to the facts valid after these exit + * statements. Returns an interim result, if the TAC or call graph of this method or the + * IFDS analysis for a callee is still pending. + */ + def performAnalysis(entity: (Function, IFDSFact)): ProperPropertyComputationResult = { + val (function, sourceFact) = entity + + // Start processing at the start of the cfg with the given source fact + implicit val state: State = + new State(entity, Map(entity -> Set.empty), Map()) + val queue = mutable.Queue + .empty[QueueEntry] + icfg.startNodes(sourceFact, function).foreach { start ⇒ + state.incomingFacts += start -> Set(sourceFact) + queue.enqueue((start, Set(sourceFact), None, None, None)) + } + process(queue) + createResult() } - process(queue) - createResult() - } - - /** - * Creates the current (intermediate) result for the analysis. - * - * @return A result containing a map, which maps each exit statement to the facts valid after - * the statement, based on the current results. If the analysis is still waiting for its - * method's TAC or call graph or the result of another method-fact-pair, an interim - * result will be returned. - * - */ - protected def createResult()(implicit state: State): ProperPropertyComputationResult = { - val propertyValue = createPropertyValue(collectResult) - val dependees = state.pendingIfdsDependees.values - if (dependees.isEmpty) Result(state.source, propertyValue) - else InterimResult.forUB(state.source, propertyValue, dependees.toSet, propertyUpdate) - } - - /** - * Called, when there is an updated result for a tac, call graph or another method-fact-pair, - * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and - * returns the new analysis result. - * - * @param eps The new property value. - * @return The new (interim) result of this analysis. - */ - protected def propertyUpdate( - eps: SomeEPS - )(implicit state: State): ProperPropertyComputationResult = { - (eps: @unchecked) match { - case FinalE(e: (Function, IFDSFact) @unchecked) ⇒ - reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - - case interimEUBP @ InterimEUBP( - e: (Function, IFDSFact) @unchecked, - ub: IFDSProperty[LLVMStatement, IFDSFact] - ) ⇒ - if (ub.flows.values - .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { - // Do not re-analyze the caller if we only get the null fact. - // Update the pendingIfdsDependee entry to the new interim result. - state.pendingIfdsDependees += - e -> interimEUBP - .asInstanceOf[EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] - } else - reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - - case FinalEP(_: Function, _: Callees) ⇒ - reAnalyzebasicBlocks(state.pendingCgCallSites) - - case InterimEUBP(_: Function, _: Callees) ⇒ - reAnalyzebasicBlocks(state.pendingCgCallSites) + + /** + * Creates the current (intermediate) result for the analysis. + * + * @return A result containing a map, which maps each exit statement to the facts valid after + * the statement, based on the current results. If the analysis is still waiting for its + * method's TAC or call graph or the result of another method-fact-pair, an interim + * result will be returned. + * + */ + protected def createResult()(implicit state: State): ProperPropertyComputationResult = { + val propertyValue = createPropertyValue(collectResult) + val dependees = state.pendingIfdsDependees.values + if (dependees.isEmpty) Result(state.source, propertyValue) + else InterimResult.forUB(state.source, propertyValue, dependees.toSet, propertyUpdate) + } + + /** + * Called, when there is an updated result for a tac, call graph or another method-fact-pair, + * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and + * returns the new analysis result. + * + * @param eps The new property value. + * @return The new (interim) result of this analysis. + */ + protected def propertyUpdate( + eps: SomeEPS + )(implicit state: State): ProperPropertyComputationResult = { + (eps: @unchecked) match { + case FinalE(e: (Function, IFDSFact) @unchecked) ⇒ + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) + + case interimEUBP @ InterimEUBP( + e: (Function, IFDSFact) @unchecked, + ub: IFDSProperty[LLVMStatement, IFDSFact]@unchecked + ) ⇒ + if (ub.flows.values + .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { + // Do not re-analyze the caller if we only get the null fact. + // Update the pendingIfdsDependee entry to the new interim result. + state.pendingIfdsDependees += + e -> interimEUBP + .asInstanceOf[EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] + } else + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) + + case FinalEP(_: Function, _: Callees) ⇒ + reAnalyzebasicBlocks(state.pendingCgCallSites) + + case InterimEUBP(_: Function, _: Callees) ⇒ + reAnalyzebasicBlocks(state.pendingCgCallSites) + } + + createResult() } - createResult() - } - - /** - * Called, when some new information is found at the last node of the method. - * This method can be overwritten by a subclass to perform additional actions. - * - * @param nextIn The input facts, which were found. - * @param oldIn The input facts, which were already known, if present. - */ - protected def foundNewInformationForLastNode( - nextIn: Set[IFDSFact], - oldIn: Option[Set[IFDSFact]], - state: State - ): Unit = {} - - /** - * Processes a statement with a call. - * - * @param basicBlock The basic block, which contains the `call`. - * @param call The call statement. - * @param callees All possible callees. - * @param in The facts, which hold before the call statement. - * @param calleeWithUpdateFact If present, the `callees` will only be analyzed with this fact - * instead of the facts returned by callToStartFacts. - * @return A map, mapping from each successor statement of the `call` to the facts, which hold - * at their start. - */ - protected def handleCall( - basicBlock: BasicBlock, - call: LLVMStatement, - callees: SomeSet[Function], - in: Set[IFDSFact], - calleeWithUpdateFact: Option[IFDSFact] - )( - implicit - state: State - ): Map[LLVMStatement, Set[IFDSFact]] = { - val successors = nextStatements(call) - val inputFacts = beforeHandleCall(call, in) - // Facts valid at the start of each successor - var summaryEdges: Map[LLVMStatement, Set[IFDSFact]] = Map.empty - - /* + /** + * Called, when some new information is found at the last node of the method. + * This method can be overwritten by a subclass to perform additional actions. + * + * @param nextIn The input facts, which were found. + * @param oldIn The input facts, which were already known, if present. + */ + protected def foundNewInformationForLastNode( + nextIn: Set[IFDSFact], + oldIn: Option[Set[IFDSFact]], + state: State + ): Unit = {} + + /** + * Processes a statement with a call. + * + * @param basicBlock The basic block, which contains the `call`. + * @param call The call statement. + * @param callees All possible callees. + * @param in The facts, which hold before the call statement. + * @param calleeWithUpdateFact If present, the `callees` will only be analyzed with this fact + * instead of the facts returned by callToStartFacts. + * @return A map, mapping from each successor statement of the `call` to the facts, which hold + * at their start. + */ + protected def handleCall( + basicBlock: BasicBlock, + call: LLVMStatement, + callees: SomeSet[Function], + in: Set[IFDSFact], + calleeWithUpdateFact: Option[IFDSFact] + )( + implicit + state: State + ): Map[LLVMStatement, Set[IFDSFact]] = { + val successors = icfg.nextStatements(call) + val inputFacts = beforeHandleCall(call, in) + // Facts valid at the start of each successor + var summaryEdges: Map[LLVMStatement, Set[IFDSFact]] = Map.empty + + /* * If calleeWithUpdateFact is present, this means that the basic block already has been * analyzed with the `inputFacts`. */ - if (calleeWithUpdateFact.isEmpty) - for (successor ← successors) { - numberOfCalls.callToReturnFlow += 1 - sumOfInputfactsForCallbacks += in.size - summaryEdges += successor -> - propagateNullFact( - inputFacts, - ifdsProblem.callToReturnFlow(call, successor, inputFacts, state.source) - ) - } - - for (callee ← callees) { - if (!ifdsProblem.insideAnalysisContext(callee)) { - // Let the concrete analysis decide what to do. - for { - successor ← successors - } summaryEdges += - successor -> (summaryEdges(successor) ++ - ifdsProblem.callOutsideOfAnalysisContext(call, callee, successor, in)) - } else { - val callToStart = - if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) - else { - propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) - } - var allNewExitFacts: Map[LLVMStatement, Set[IFDSFact]] = Map.empty - // Collect exit facts for each input fact separately - for (fact ← callToStart) { - /* + if (calleeWithUpdateFact.isEmpty) + for (successor ← successors) { + numberOfCalls.callToReturnFlow += 1 + sumOfInputfactsForCallbacks += in.size + summaryEdges += successor -> + propagateNullFact( + inputFacts, + ifdsProblem.callToReturnFlow(call, successor, inputFacts, state.source) + ) + } + + for (callee ← callees) { + if (!ifdsProblem.insideAnalysisContext(callee)) { + // Let the concrete analysis decide what to do. + for { + successor ← successors + } summaryEdges += + successor -> (summaryEdges(successor) ++ + ifdsProblem.callOutsideOfAnalysisContext(call, callee, successor, in)) + } else { + val callToStart = + if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) + else { + propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) + } + var allNewExitFacts: Map[LLVMStatement, Set[IFDSFact]] = Map.empty + // Collect exit facts for each input fact separately + for (fact ← callToStart) { + /* * If this is a recursive call with the same input facts, we assume that the * call only produces the facts that are already known. The call site is added to * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts * become known for the input fact. */ - if ((callee eq state.function) && fact == state.source._2) { - val newDependee = - if (state.pendingIfdsCallSites.contains(state.source)) - state.pendingIfdsCallSites(state.source) + - ((basicBlock, call)) - else Set((basicBlock, call)) - state.pendingIfdsCallSites = - state.pendingIfdsCallSites.updated(state.source, newDependee) - allNewExitFacts = mergeMaps(allNewExitFacts, collectResult) - } else { - val e = (callee, fact) - val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] - val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[LLVMStatement, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[LLVMStatement, IFDSFact]]) ⇒ ep.ub.flows - case _ ⇒ Map.empty - } - val exitFacts: Map[LLVMStatement, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[LLVMStatement, IFDSFact]] ⇒ - if (state.pendingIfdsCallSites.contains(e) - && state.pendingIfdsCallSites(e).nonEmpty) { - val newDependee = - state.pendingIfdsCallSites(e) - ((basicBlock, call)) - state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) - } - state.pendingIfdsDependees -= e - ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, IFDSFact]] ⇒ - /* + if ((callee eq state.source._1) && fact == state.source._2) { + val newDependee = + if (state.pendingIfdsCallSites.contains(state.source)) + state.pendingIfdsCallSites(state.source) + + ((basicBlock, call)) + else Set((basicBlock, call)) + state.pendingIfdsCallSites = + state.pendingIfdsCallSites.updated(state.source, newDependee) + allNewExitFacts = mergeMaps(allNewExitFacts, collectResult) + } else { + val e = (callee, fact) + val callFlows = propertyStore(e, propertyKey.key) + .asInstanceOf[EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] + val oldValue = state.pendingIfdsDependees.get(e) + val oldExitFacts: Map[LLVMStatement, Set[IFDSFact]] = oldValue match { + case Some(ep: InterimEUBP[_, IFDSProperty[LLVMStatement, IFDSFact]]) ⇒ ep.ub.flows + case _ ⇒ Map.empty + } + val exitFacts: Map[LLVMStatement, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[LLVMStatement, IFDSFact]] ⇒ + if (state.pendingIfdsCallSites.contains(e) + && state.pendingIfdsCallSites(e).nonEmpty) { + val newDependee = + state.pendingIfdsCallSites(e) - ((basicBlock, call)) + state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) + } + state.pendingIfdsDependees -= e + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, IFDSFact]] ⇒ + /* * Add the call site to `pendingIfdsCallSites` and * `pendingIfdsDependees` and continue with the facts in the interim * result for now. When the analysis for the callee finishes, the * analysis for this call site will be triggered again. */ - addIfdsDependee(e, callFlows, basicBlock, call) - ep.ub.flows - case _ ⇒ - addIfdsDependee(e, callFlows, basicBlock, call) - Map.empty - } - // Only process new facts that are not in `oldExitFacts` - allNewExitFacts = mergeMaps( - allNewExitFacts, - filterNewInformation(exitFacts, oldExitFacts, project) - ) - /* + addIfdsDependee(e, callFlows, basicBlock, call) + ep.ub.flows + case _ ⇒ + addIfdsDependee(e, callFlows, basicBlock, call) + Map.empty + } + // Only process new facts that are not in `oldExitFacts` + allNewExitFacts = mergeMaps( + allNewExitFacts, + filterNewInformation(exitFacts, oldExitFacts, project) + ) + /* * If new exit facts were discovered for the callee-fact-pair, all call * sites depending on this pair have to be re-evaluated. oldValue is * undefined if the callee-fact pair has not been queried before or returned * a FinalEP. */ - if (oldValue.isDefined && oldExitFacts != exitFacts) { - reAnalyzeCalls( - state.pendingIfdsCallSites(e), - e._1, - Some(e._2) - ) + if (oldValue.isDefined && oldExitFacts != exitFacts) { + reAnalyzeCalls( + state.pendingIfdsCallSites(e), + e._1, + Some(e._2) + ) + } + } + } + summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) } - } } - summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) - } - } - summaryEdges - } - - /** - * This method is called at the beginning of handleCall. - * A subclass can overwrite this method, to change the input facts of the call. - * - * @param call The call statement. - * @param in The input facts, which hold before the `call`. - * @return The changed set of input facts. - */ - protected def beforeHandleCall(call: LLVMStatement, in: Set[IFDSFact]): Set[IFDSFact] = in - - /** - * Merges two maps that have sets as values. - * - * @param map1 The first map. - * @param map2 The second map. - * @return A map containing the keys of both maps. Each key is mapped to the union of both maps' - * values. - */ - protected def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { - var result = map1 - for ((key, values) ← map2) { - result.get(key) match { - case Some(resultValues) ⇒ - if (resultValues.size > values.size) - result = result.updated(key, resultValues ++ values) - else - result = result.updated(key, values ++ resultValues) - case None ⇒ - result = result.updated(key, values) - } + summaryEdges } - result - } - - /** - * Analyzes a queue of BasicBlocks. - * - * @param worklist A queue of the following elements: - * bb The basic block that will be analyzed. - * in New data flow facts found to hold at the beginning of the basic block. - * calleeWithUpdateIndex If the basic block is analyzed because there is new information - * for a callee, this is the call site's index. - * calleeWithUpdate If the basic block is analyzed because there is new information for a - * callee, this is the callee. - * calleeWithUpdateFact If the basic block is analyzed because there is new information - * for a callee with a specific input fact, this is the input fact. - */ - private def process( - worklist: mutable.Queue[QueueEntry] - )(implicit state: State): Unit = { - while (worklist.nonEmpty) { - val (basicBlock, in, calleeWithUpdateSite, calleeWithUpdate, calleeWithUpdateFact) = - worklist.dequeue() - val oldOut = state.outgoingFacts.getOrElse(basicBlock, Map.empty) - val nextOut = analyzeBasicBlock( - basicBlock, - in, - calleeWithUpdateSite, - calleeWithUpdate, - calleeWithUpdateFact - ) - val allOut = mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) - state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) - - for (successor ← nextNodes(basicBlock)) { - if (isLastNode(successor)) { - // Re-analyze recursive call sites with the same input fact. - val nextOutSuccessors = nextOut.get(successor) - if (nextOutSuccessors.isDefined && nextOutSuccessors.get.nonEmpty) { - val oldOutSuccessors = oldOut.get(successor) - if (oldOutSuccessors.isEmpty || containsNewInformation( - nextOutSuccessors.get, - oldOutSuccessors.get, - project - )) { - val source = state.source - foundNewInformationForLastNode( - nextOutSuccessors.get, - oldOutSuccessors, - state - ) - reAnalyzeCalls( - state.pendingIfdsCallSites(source), - source._1, - Some(source._2) - ) + + /** + * This method is called at the beginning of handleCall. + * A subclass can overwrite this method, to change the input facts of the call. + * + * @param call The call statement. + * @param in The input facts, which hold before the `call`. + * @return The changed set of input facts. + */ + protected def beforeHandleCall(call: LLVMStatement, in: Set[IFDSFact]): Set[IFDSFact] = in + + /** + * Merges two maps that have sets as values. + * + * @param map1 The first map. + * @param map2 The second map. + * @return A map containing the keys of both maps. Each key is mapped to the union of both maps' + * values. + */ + protected def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { + var result = map1 + for ((key, values) ← map2) { + result.get(key) match { + case Some(resultValues) ⇒ + if (resultValues.size > values.size) + result = result.updated(key, resultValues ++ values) + else + result = result.updated(key, values ++ resultValues) + case None ⇒ + result = result.updated(key, values) } - } - } else { - val successorBlock = successor - val nextIn = nextOut.getOrElse(successorBlock, Set.empty) - val oldIn = state.incomingFacts.getOrElse(successorBlock, Set.empty) - val newIn = notSubsumedBy(nextIn, oldIn, project) - val mergedIn = - if (nextIn.size > oldIn.size) nextIn ++ oldIn - else oldIn ++ nextIn - state.incomingFacts = - state.incomingFacts.updated(successorBlock, subsume(mergedIn, project)) - /* + } + result + } + + /** + * Analyzes a queue of BasicBlocks. + * + * @param worklist A queue of the following elements: + * bb The basic block that will be analyzed. + * in New data flow facts found to hold at the beginning of the basic block. + * calleeWithUpdateIndex If the basic block is analyzed because there is new information + * for a callee, this is the call site's index. + * calleeWithUpdate If the basic block is analyzed because there is new information for a + * callee, this is the callee. + * calleeWithUpdateFact If the basic block is analyzed because there is new information + * for a callee with a specific input fact, this is the input fact. + */ + private def process( + worklist: mutable.Queue[QueueEntry] + )(implicit state: State): Unit = { + while (worklist.nonEmpty) { + val (basicBlock, in, calleeWithUpdateSite, calleeWithUpdate, calleeWithUpdateFact) = + worklist.dequeue() + val oldOut = state.outgoingFacts.getOrElse(basicBlock, Map.empty) + val nextOut = analyzeBasicBlock( + basicBlock, + in, + calleeWithUpdateSite, + calleeWithUpdate, + calleeWithUpdateFact + ) + val allOut = mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) + state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) + + for (successor ← icfg.nextNodes(basicBlock)) { + if (icfg.isLastNode(successor)) { + // Re-analyze recursive call sites with the same input fact. + val nextOutSuccessors = nextOut.get(successor) + if (nextOutSuccessors.isDefined && nextOutSuccessors.get.nonEmpty) { + val oldOutSuccessors = oldOut.get(successor) + if (oldOutSuccessors.isEmpty || containsNewInformation( + nextOutSuccessors.get, + oldOutSuccessors.get, + project + )) { + val source = state.source + foundNewInformationForLastNode( + nextOutSuccessors.get, + oldOutSuccessors, + state + ) + reAnalyzeCalls( + state.pendingIfdsCallSites(source), + source._1, + Some(source._2) + ) + } + } + } else { + val successorBlock = successor + val nextIn = nextOut.getOrElse(successorBlock, Set.empty) + val oldIn = state.incomingFacts.getOrElse(successorBlock, Set.empty) + val newIn = notSubsumedBy(nextIn, oldIn, project) + val mergedIn = + if (nextIn.size > oldIn.size) nextIn ++ oldIn + else oldIn ++ nextIn + state.incomingFacts = + state.incomingFacts.updated(successorBlock, subsume(mergedIn, project)) + /* * Only process the successor with new facts. * It is analyzed at least one time because of the null fact. */ - if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None)) + if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None, None)) + } + } } - } } - } - - /** - * Computes for one basic block the facts valid on each CFG edge leaving the block if `sources` - * held before the block. - * - * @param basicBlock The basic block, that will be analyzed. - * @param in The facts, that hold before the block. - * @param calleeWithUpdateSite If the basic block is analyzed because there is new information - * for a callee, this is the call site. - * @param calleeWithUpdate If the basic block is analyzed because there is new information for - * a callee, this is the callee. - * @param calleeWithUpdateFact If the basic block is analyzed because there is new information - * for a callee with a specific input fact, this is the input fact. - * @return A map, mapping each successor node to its input facts. Instead of catch nodes, this - * map contains their handler nodes. - */ - private def analyzeBasicBlock( - basicBlock: BasicBlock, - in: Set[IFDSFact], - calleeWithUpdateSite: Option[LLVMStatement], - calleeWithUpdate: Option[Function], - calleeWithUpdateFact: Option[IFDSFact] - )( - implicit - state: State - ): Map[BasicBlock, Set[IFDSFact]] = { - - /* + + /** + * Computes for one basic block the facts valid on each CFG edge leaving the block if `sources` + * held before the block. + * + * @param basicBlock The basic block, that will be analyzed. + * @param in The facts, that hold before the block. + * @param calleeWithUpdateSite If the basic block is analyzed because there is new information + * for a callee, this is the call site. + * @param calleeWithUpdate If the basic block is analyzed because there is new information for + * a callee, this is the callee. + * @param calleeWithUpdateFact If the basic block is analyzed because there is new information + * for a callee with a specific input fact, this is the input fact. + * @return A map, mapping each successor node to its input facts. Instead of catch nodes, this + * map contains their handler nodes. + */ + private def analyzeBasicBlock( + basicBlock: BasicBlock, + in: Set[IFDSFact], + calleeWithUpdateSite: Option[LLVMStatement], + calleeWithUpdate: Option[Function], + calleeWithUpdateFact: Option[IFDSFact] + )( + implicit + state: State + ): Map[BasicBlock, Set[IFDSFact]] = { + + /* * Collects information about a statement. * * @param index The statement's index. @@ -580,144 +484,125 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * calleeFact: If `index` equals `calleeWithUpdateIndex`, only * `calleeWithUpdateFact` will be returned, None otherwise. */ - def collectInformation(instruction: Instruction): (LLVMStatement, Option[SomeSet[Function]], Option[IFDSFact]) = { - val statement = LLVMStatement(state.function, basicBlock, instruction) - val calleesO = - if (calleeWithUpdateSite.contains(statement)) calleeWithUpdate.map(Set(_)) - else getCalleesIfCallStatement(statement) - val calleeFact = - if (calleeWithUpdateSite.contains(statement)) calleeWithUpdateFact - else None - (statement, calleesO, calleeFact) + def collectInformation(statement: LLVMStatement): (Option[SomeSet[Function]], Option[IFDSFact]) = { + val calleesO = + if (calleeWithUpdateSite.contains(statement)) calleeWithUpdate.map(Set(_)) + else icfg.getCalleesIfCallStatement(statement) + val calleeFact = + if (calleeWithUpdateSite.contains(statement)) calleeWithUpdateFact + else None + (calleesO, calleeFact) + } + + val last = icfg.lastStatement(basicBlock) + var flows: Set[IFDSFact] = in + var statement: LLVMStatement = icfg.firstStatement(basicBlock) + + // Iterate over all statements but the last one, only keeping the resulting DataFlowFacts. + while (statement != last) { + val (calleesO, calleeFact) = collectInformation(statement) + val successor = icfg.nextStatement(statement) + flows = if (calleesO.isEmpty) { + numberOfCalls.normalFlow += 1 + sumOfInputfactsForCallbacks += in.size + ifdsProblem.normalFlow(statement, successor, flows) + } else + // Inside a basic block, we only have one successor --> Take the head + handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head + statement = successor + } + + // Analyze the last statement for each possible successor statement. + val (calleesO, callFact) = collectInformation(last) + var result: Map[BasicBlock, Set[IFDSFact]] = + if (calleesO.isEmpty) { + var result: Map[BasicBlock, Set[IFDSFact]] = Map.empty + for (node ← icfg.nextNodes(basicBlock)) { + numberOfCalls.normalFlow += 1 + sumOfInputfactsForCallbacks += in.size + result += node -> ifdsProblem.normalFlow(statement, icfg.firstStatement(node), flows) + } + result + } else + handleCall(basicBlock, statement, calleesO.get, flows, callFact) + .map(entry ⇒ entry._1.basicBlock -> entry._2) + + // Propagate the null fact. + result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) + result } - val last = lastIndex(basicBlock) - var flows: Set[IFDSFact] = in - var index = firstIndex(basicBlock) - - // Iterate over all statements but the last one, only keeping the resulting DataFlowFacts. - while (index != last) { - val (statement, calleesO, calleeFact) = collectInformation(index) - val next = nextIndex(index) - flows = if (calleesO.isEmpty) { - val successor = - LLVMStatement(state.function, basicBlock, state.code(next), next) - numberOfCalls.normalFlow += 1 - sumOfInputfactsForCallbacks += in.size - ifdsProblem.normalFlow(statement, successor, flows) - } else - // Inside a basic block, we only have one successor --> Take the head - handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head - index = next + /** + * Re-analyzes some basic blocks. + * + * @param basicBlocks The basic blocks, that will be re-analyzed. + */ + private def reAnalyzebasicBlocks(basicBlocks: Set[BasicBlock])(implicit state: State): Unit = { + val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty + for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) + process(queue) } - // Analyze the last statement for each possible successor statement. - val (statement, calleesO, callFact) = collectInformation(last) - var result: Map[BasicBlock, Set[IFDSFact]] = - if (calleesO.isEmpty) { - var result: Map[BasicBlock, Set[IFDSFact]] = Map.empty - for (node ← nextNodes(basicBlock)) { - numberOfCalls.normalFlow += 1 - sumOfInputfactsForCallbacks += in.size - result += node -> ifdsProblem.normalFlow(statement, firstStatement(node), flows) + /** + * Re-analyzes some call sites with respect to one specific callee. + * + * @param callSites The call sites, which are analyzed. + * @param callee The callee, which will be considered at the `callSites`. + * @param fact If defined, the `callee` will only be analyzed for this fact. + */ + private def reAnalyzeCalls( + callSites: Set[LLVMStatement], + callee: Function, + fact: Option[IFDSFact] + )(implicit state: State): Unit = { + val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty + for (callSite ← callSites) { + val block = callSite.basicBlock + queue.enqueue((block, state.incomingFacts(block), Some(callSite), Some(callee), fact)) } - result - } else - handleCall(basicBlock, statement, calleesO.get, flows, callFact) - .map(entry ⇒ entry._1.basicBlock -> entry._2) - - // Propagate the null fact. - result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) - result - } - - /** - * Re-analyzes some basic blocks. - * - * @param basicBlocks The basic blocks, that will be re-analyzed. - */ - private def reAnalyzebasicBlocks(basicBlocks: Set[BasicBlock])(implicit state: State): Unit = { - val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty - for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) - process(queue) - } - - /** - * Re-analyzes some call sites with respect to one specific callee. - * - * @param callSites The call sites, which are analyzed. - * @param callee The callee, which will be considered at the `callSites`. - * @param fact If defined, the `callee` will only be analyzed for this fact. - */ - private def reAnalyzeCalls( - callSites: Set[LLVMStatement], - callee: Function, - fact: Option[IFDSFact] - )(implicit state: State): Unit = { - val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty - for (callSite ← callSites) { - val block = callSite.basicBlock - queue.enqueue((block, state.incomingFacts(block), Some(callSite), Some(callee), fact)) + process(queue) + } + + /** + * If `from` contains a null fact, it will be added to `to`. + * + * @param from The set, which may contain the null fact initially. + * @param to The set, to which the null fact may be added. + * @return `to` with the null fact added, if it is contained in `from`. + */ + private def propagateNullFact(from: Set[IFDSFact], to: Set[IFDSFact]): Set[IFDSFact] = { + if (from.contains(ifdsProblem.nullFact)) to + ifdsProblem.nullFact + else to + } + + /** + * Adds a method-fact-pair as to the IFDS call sites and dependees. + * + * @param entity The method-fact-pair. + * @param calleeProperty The property, that was returned for `entity`. + * @param callBB The basic block of the call site. + * @param callIndex The index of the call site. + */ + private def addIfdsDependee( + entity: (Function, IFDSFact), + calleeProperty: EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]], + callBB: BasicBlock, + call: LLVMStatement + )(implicit state: State): Unit = { + val callSites = state.pendingIfdsCallSites + state.pendingIfdsCallSites = callSites.updated( + entity, + callSites.getOrElse(entity, Set.empty) + ((callBB, call)) + ) + state.pendingIfdsDependees += entity -> calleeProperty } - process(queue) - } - - /** - * Gets the set of all methods possibly called at some statement. - * - * @param basicBlock The basic block containing the statement. - * @param index The statement's index. - * @return All methods possibly called at the statement index or None, if the statement does not - * contain a call. - */ - private def getCalleesIfCallStatement(statement: LLVMStatement)( - implicit - state: State - ): Option[SomeSet[Function]] = { - // TODO - None - } - - /** - * If `from` contains a null fact, it will be added to `to`. - * - * @param from The set, which may contain the null fact initially. - * @param to The set, to which the null fact may be added. - * @return `to` with the null fact added, if it is contained in `from`. - */ - private def propagateNullFact(from: Set[IFDSFact], to: Set[IFDSFact]): Set[IFDSFact] = { - if (from.contains(ifdsProblem.nullFact)) to + ifdsProblem.nullFact - else to - } - - /** - * Adds a method-fact-pair as to the IFDS call sites and dependees. - * - * @param entity The method-fact-pair. - * @param calleeProperty The property, that was returned for `entity`. - * @param callBB The basic block of the call site. - * @param callIndex The index of the call site. - */ - private def addIfdsDependee( - entity: (Function, IFDSFact), - calleeProperty: EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]], - callBB: BasicBlock, - call: LLVMStatement - )(implicit state: State): Unit = { - val callSites = state.pendingIfdsCallSites - state.pendingIfdsCallSites = callSites.updated( - entity, - callSites.getOrElse(entity, Set.empty) + ((callBB, call)) - ) - state.pendingIfdsDependees += entity -> calleeProperty - } } object AbstractNativeIFDSAnalysis { - /** - * When true, the cross product of exit and successor in returnFLow will be optimized. - */ - var OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW: Boolean = true + /** + * When true, the cross product of exit and successor in returnFLow will be optimized. + */ + var OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW: Boolean = true } /** @@ -726,45 +611,45 @@ object AbstractNativeIFDSAnalysis { * @param instruction The LLVM instruction. */ case class LLVMStatement(instruction: Instruction) { - def function(): Function = instruction.function - def basicBlock(): BasicBlock = instruction.parent + def function(): Function = instruction.function + def basicBlock(): BasicBlock = instruction.parent } abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalysisScheduler { - final override type InitializationData = AbstractNativeIFDSAnalysis[IFDSFact] + final override type InitializationData = AbstractNativeIFDSAnalysis[IFDSFact] - def property: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] + def property: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] - final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) + final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) - override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) + override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - /** - * Registers the analysis as a lazy computation, that is, the method - * will call `ProperytStore.scheduleLazyComputation`. - */ - override def register( - p: SomeProject, - ps: PropertyStore, - analysis: AbstractNativeIFDSAnalysis[IFDSFact] - ): FPCFAnalysis = { - ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) - analysis - } + /** + * Registers the analysis as a lazy computation, that is, the method + * will call `ProperytStore.scheduleLazyComputation`. + */ + override def register( + p: SomeProject, + ps: PropertyStore, + analysis: AbstractNativeIFDSAnalysis[IFDSFact] + ): FPCFAnalysis = { + ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) + analysis + } - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { - val ifdsAnalysis = analysis.asInstanceOf[AbstractNativeIFDSAnalysis[IFDSFact]] - for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { - ps.force(e, ifdsAnalysis.propertyKey.key) + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { + val ifdsAnalysis = analysis.asInstanceOf[AbstractNativeIFDSAnalysis[IFDSFact]] + for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { + ps.force(e, ifdsAnalysis.propertyKey.key) + } } - } - override def afterPhaseCompletion( - p: SomeProject, - ps: PropertyStore, - analysis: FPCFAnalysis - ): Unit = {} + override def afterPhaseCompletion( + p: SomeProject, + ps: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala new file mode 100644 index 0000000000..ad77d3ae73 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala @@ -0,0 +1,9 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds +import org.opalj.br.Method +import org.opalj.ll.llvm.Function + +abstract class Callable + +case class LLVMFunction(val function: Function) extends Callable +case class JavaMethod(val method: Method) extends Callable diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala index c0ce123e6e..9d4c5a2530 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala @@ -1,224 +1,143 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds +import org.opalj.br.cfg.CFGNode +import org.opalj.ll.llvm.Function import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, AbstractIFDSFact} import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.tac.{Return, ReturnValue} -/* -import org.opalj.br.DeclaredMethod -import org.opalj.br.cfg.{BasicBlock, CFG, CFGNode} -import org.opalj.tac.{Return, ReturnValue, Stmt, TACStmts} -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation -*/ /** * An IFDS analysis, which analyzes the code in the control flow direction. * * @author Mario Trageser */ -abstract class ForwardNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: NativeIFDSProblem[IFDSFact], propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact]) extends AbstractNativeIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { -/* - /** - * The analysis starts at the entry block. - */ - override protected def startBlocks( - sourceFact: IFDSFact, - cfg: CFG[Stmt[V], TACStmts[V]] - ): Set[BasicBlock] = - Set(cfg.startBlock) - - /** - * Collects the output facts at the predecessors of the normal and abnormal return node. - */ - override protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { - mergeMaps( - resultOfExitNode(state.cfg.normalReturnNode), - resultOfExitNode(state.cfg.abnormalReturnNode) - ) - } - - /** - * Returns the next successor nodes of `basicBlock`. - * Catch nodes are skipped to their successor. - */ - override protected def nextNodes(basicBlock: BasicBlock): Set[CFGNode] = - basicBlock.successors.map { successor ⇒ - if (successor.isCatchNode) successor.successors.head - else successor +abstract class ForwardNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: NativeIFDSProblem[IFDSFact], propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact]) extends AbstractNativeIFDSAnalysis[IFDSFact](ifdsProblem, new NativeForwardICFG[IFDSFact], propertyKey) { + /** + * Collects the output facts at the predecessors of the normal and abnormal return node. + */ + override protected def collectResult(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] = { + mergeMaps( + resultOfExitNode(state.cfg.normalReturnNode), + resultOfExitNode(state.cfg.abnormalReturnNode) + ) } - /** - * The exit nodes are the last nodes. - */ - override protected def isLastNode(node: CFGNode): Boolean = node.isExitNode - - /** - * If the node is a basic block, it will be returned. - * Otherwise, its first successor will be returned. - */ - override protected def skipCatchNode(node: CFGNode): Set[BasicBlock] = - Set((if (node.isBasicBlock) node else node.successors.head).asBasicBlock) - - /** - * The first index of a basic block is its start index. - */ - override protected def firstIndex(basicBlock: BasicBlock): Int = basicBlock.startPC - - /** - * The last index of a basic block is its end index. - */ - override protected def lastIndex(basicBlock: BasicBlock): Int = basicBlock.endPC - - /** - * The next index in the direction of the control flow. - */ - override protected def nextIndex(index: Int): Int = index + 1 - - /** - * If the `node` is a basic block, its first statement will be returned. - * If it is a catch node, the first statement of its handler will be returned. - * If it is an exit node, an artificial statement without code will be returned. - */ - override protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement = { - if (node.isBasicBlock) { - val index = node.asBasicBlock.startPC - JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg) - } else if (node.isCatchNode) firstStatement(node.successors.head) - else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg) - else throw new IllegalArgumentException(s"Unknown node type: $node") - } - - /** - * The successor statements in the direction of the control flow. - */ - override protected def nextStatements(statement: JavaStatement)(implicit state: State): Set[JavaStatement] = { - val index = statement.index - val basicBlock = statement.node.asBasicBlock - if (index == basicBlock.endPC) - basicBlock.successors.map(firstStatement(_)) - else { - val nextIndex = index + 1 - Set(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, - statement.code, statement.cfg)) + /** + * Calls callFlow. + */ + override protected def callToStartFacts(call: LLVMStatement, callee: Function, + in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { + numberOfCalls.callFlow += 1 + sumOfInputfactsForCallbacks += in.size + ifdsProblem.callFlow(call, callee, in, state.source) } - } - - /** - * Calls callFlow. - */ - override protected def callToStartFacts(call: JavaStatement, callee: DeclaredMethod, - in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { - numberOfCalls.callFlow += 1 - sumOfInputfactsForCallbacks += in.size - ifdsProblem.callFlow(call, callee, in, state.source) - } - /** - * Combines each normal exit node with each normal successor and each abnormal exit statement - * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. - */ - override protected def addExitToReturnFacts( - summaryEdges: Map[JavaStatement, Set[IFDSFact]], - successors: Set[JavaStatement], call: JavaStatement, - callee: DeclaredMethod, - exitFacts: Map[JavaStatement, Set[IFDSFact]] - )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { - // First process for normal returns, then abnormal returns. - var result = summaryEdges - if (AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { - val successors = nextStatementsWithNode(call) - for { - successor ← successors - exitStatement ← exitFacts.keys - if (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && - (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || - (successor._2.isCatchNode || successor._2.isAbnormalReturnExitNode) && - (exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID) - } result = addSummaryEdge(result, call, exitStatement, successor._1, callee, exitFacts) - } else { - val successors = nextStatements(call) - for { - successor ← successors - exitStatement ← exitFacts.keys - } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) + /** + * Combines each normal exit node with each normal successor and each abnormal exit statement + * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. + */ + override protected def addExitToReturnFacts( + summaryEdges: Map[LLVMStatement, Set[IFDSFact]], + successors: Set[LLVMStatement], call: LLVMStatement, + callee: Function, + exitFacts: Map[LLVMStatement, Set[IFDSFact]] + )(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] = { + // First process for normal returns, then abnormal returns. + var result = summaryEdges + if (AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { + val successors = nextStatementsWithNode(call) + for { + successor ← successors + exitStatement ← exitFacts.keys + if (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && + (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || + (successor._2.isCatchNode || successor._2.isAbnormalReturnExitNode) && + (exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID) + } result = addSummaryEdge(result, call, exitStatement, successor._1, callee, exitFacts) + } else { + val successors = nextStatements(call) + for { + successor ← successors + exitStatement ← exitFacts.keys + } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) + } + result } - result - } - /** - * Like nextStatements, but maps each successor statement to the corresponding successor node. - * When determining the successor node, catch nodes are not skipped. - */ - private def nextStatementsWithNode(statement: JavaStatement)(implicit state: State): Map[JavaStatement, CFGNode] = { - val index = statement.index - val basicBlock = statement.node.asBasicBlock - if (index == basicBlock.endPC) - basicBlock.successors.iterator - .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap - else { - val nextIndex = index + 1 - Map(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, - statement.code, statement.cfg) → basicBlock) + /** + * Like nextStatements, but maps each successor statement to the corresponding successor node. + * When determining the successor node, catch nodes are not skipped. + */ + private def nextStatementsWithNode(statement: LLVMStatement)(implicit state: State): Map[LLVMStatement, CFGNode] = { + val index = statement.index + val basicBlock = statement.node.asBasicBlock + if (index == basicBlock.endPC) + basicBlock.successors.iterator + .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap + else { + val nextIndex = index + 1 + Map(LLVMStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + statement.code, statement.cfg) → basicBlock) + } } - } - /** - * Collects the facts valid at an exit node based on the current results. - * - * @param exit The exit node. - * @return A map, mapping from each predecessor of the `exit` node to the facts valid at the - * `exit` node under the assumption that the predecessor was executed before. - */ - private def resultOfExitNode(exit: CFGNode)( - implicit - state: State - ): Map[JavaStatement, Set[IFDSFact]] = { - var result = Map.empty[JavaStatement, Set[IFDSFact]] - exit.predecessors foreach { predecessor ⇒ - if (predecessor.isBasicBlock) { - val basicBlock = predecessor.asBasicBlock - val exitFacts = state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)) - if (exitFacts.isDefined) { - val lastIndex = basicBlock.endPC - val stmt = JavaStatement(state.method, basicBlock, state.code(lastIndex), - lastIndex, state.code, state.cfg) - result += stmt → exitFacts.get + /** + * Collects the facts valid at an exit node based on the current results. + * + * @param exit The exit node. + * @return A map, mapping from each predecessor of the `exit` node to the facts valid at the + * `exit` node under the assumption that the predecessor was executed before. + */ + private def resultOfExitNode(exit: CFGNode)( + implicit + state: State + ): Map[LLVMStatement, Set[IFDSFact]] = { + var result = Map.empty[LLVMStatement, Set[IFDSFact]] + exit.predecessors foreach { predecessor ⇒ + if (predecessor.isBasicBlock) { + val basicBlock = predecessor.asBasicBlock + val exitFacts = state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)) + if (exitFacts.isDefined) { + val lastIndex = basicBlock.endPC + val stmt = LLVMStatement(state.method, basicBlock, state.code(lastIndex), + lastIndex, state.code, state.cfg) + result += stmt → exitFacts.get + } + } } - } + result } - result - } - /** - * Adds a summary edge for a call to a map representing summary edges. - * - * @param summaryEdges The current map representing the summary edges. - * Maps from successor statements to facts, which hold at their beginning. - * @param call The call, calling the `callee`. - * @param exitStatement The exit statement for the new summary edge. - * @param successor The successor statement of the call for the new summary edge. - * @param callee The callee, called by `call`. - * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly - * found exit facts. - * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. - */ - private def addSummaryEdge(summaryEdges: Map[JavaStatement, Set[IFDSFact]], call: JavaStatement, - exitStatement: JavaStatement, successor: JavaStatement, - callee: DeclaredMethod, - allNewExitFacts: Map[JavaStatement, Set[IFDSFact]]): Map[JavaStatement, Set[IFDSFact]] = { - val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) - numberOfCalls.returnFlow += 1 - sumOfInputfactsForCallbacks += in.size - val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) - val newFacts = - if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { - val summaryForSuccessor = summaryEdges(successor) - if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned - else returned ++ summaryForSuccessor - } else returned - summaryEdges.updated(successor, newFacts) - } + /** + * Adds a summary edge for a call to a map representing summary edges. + * + * @param summaryEdges The current map representing the summary edges. + * Maps from successor statements to facts, which hold at their beginning. + * @param call The call, calling the `callee`. + * @param exitStatement The exit statement for the new summary edge. + * @param successor The successor statement of the call for the new summary edge. + * @param callee The callee, called by `call`. + * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly + * found exit facts. + * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. + */ + private def addSummaryEdge(summaryEdges: Map[LLVMStatement, Set[IFDSFact]], call: LLVMStatement, + exitStatement: LLVMStatement, successor: LLVMStatement, + callee: Function, + allNewExitFacts: Map[LLVMStatement, Set[IFDSFact]]): Map[LLVMStatement, Set[IFDSFact]] = { + val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) + numberOfCalls.returnFlow += 1 + sumOfInputfactsForCallbacks += in.size + val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) + val newFacts = + if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { + val summaryForSuccessor = summaryEdges(successor) + if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned + else returned ++ summaryForSuccessor + } else returned + summaryEdges.updated(successor, newFacts) + } } -*/ \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala new file mode 100644 index 0000000000..220f443a03 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala @@ -0,0 +1,71 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds +import scala.collection.{Set ⇒ SomeSet} +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact + +abstract class ICFG[IFDSFact <: AbstractIFDSFact, C, Statement, Node] { + /** + * Determines the basic blocks, at which the analysis starts. + * + * @param sourceFact The source fact of the analysis. + * @param callable The analyzed callable. + * @return The basic blocks, at which the analysis starts. + */ + def startNodes(sourceFact: IFDSFact, callable: C): Set[Node] + + /** + * Determines the nodes, that will be analyzed after some `node`. + * + * @param node The basic block, that was analyzed before. + * @return The nodes, that will be analyzed after `node`. + */ + def nextNodes(node: Node): Set[Node] + + /** + * Checks, if some `node` is the last node. + * + * @return True, if `node` is the last node, i.e. there is no next node. + */ + def isLastNode(node: Node): Boolean + + /** + * Determines the first index of some `basic block`, that will be analyzed. + * + * @param node The basic block. + * @return The first index of some `basic block`, that will be analyzed. + */ + def firstStatement(node: Node): Statement + + /** + * Determines the last index of some `basic block`, that will be analzyed. + * + * @param node The basic block. + * @return The last index of some `basic block`, that will be analzyed. + */ + def lastStatement(node: Node): Statement + + /** + * Determines the statement that will be analyzed after some other statement. + * + * @param statement The current statement. + * @return The statement that will be analyzed after `statement`. + */ + def nextStatement(statement: Statement): Statement + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + def nextStatements(statement: Statement): Set[Statement] + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + def getCalleesIfCallStatement(statement: Statement): Option[SomeSet[C]] +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala new file mode 100644 index 0000000000..f754a1e8e0 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -0,0 +1,78 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.ll.llvm.{BasicBlock, Function, Instruction, Terminator} +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact + +class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Function, LLVMStatement, BasicBlock] { + /** + * Determines the basic blocks, at which the analysis starts. + * + * @param sourceFact The source fact of the analysis. + * @param callable The analyzed callable. + * @return The basic blocks, at which the analysis starts. + */ + override def startNodes(sourceFact: IFDSFact, callable: Function): Set[BasicBlock] = Set(callable.entryBlock()) + + /** + * Determines the nodes, that will be analyzed after some `basicBlock`. + * + * @param node The basic block, that was analyzed before. + * @return The nodes, that will be analyzed after `basicBlock`. + */ + override def nextNodes(node: BasicBlock): Set[BasicBlock] = node.terminator match { + case Some(terminator) ⇒ terminator.successors().map(_.parent()).toSet + case None ⇒ Set.empty + } + + /** + * Checks, if some `node` is the last node. + * + * @return True, if `node` is the last node, i.e. there is no next node. + */ + override def isLastNode(node: BasicBlock): Boolean = !node.hasSuccessors + + /** + * Determines the first index of some `basic block`, that will be analyzed. + * + * @param basicBlock The basic block. + * @return The first index of some `basic block`, that will be analyzed. + */ + override def firstStatement(basicBlock: BasicBlock): LLVMStatement = LLVMStatement(basicBlock.firstInstruction()) + + /** + * Determines the last index of some `basic block`, that will be analzyed. + * + * @param basicBlock The basic block. + * @return The last index of some `basic block`, that will be analzyed. + */ + override def lastStatement(basicBlock: BasicBlock): LLVMStatement = LLVMStatement(basicBlock.lastInstruction()) + + /** + * Determines the statement that will be analyzed after some other statement. + * + * @param statement The current statement. + * @return The statement that will be analyzed after `statement`. + */ + override def nextStatement(statement: LLVMStatement): LLVMStatement = LLVMStatement(statement.instruction.next().get) + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + override def nextStatements(statement: LLVMStatement): Set[LLVMStatement] = { + if (!statement.instruction.isTerminator) return Set(nextStatement(statement)) + statement.instruction.asInstanceOf[Instruction with Terminator].successors().map(LLVMStatement(_)).toSet + } + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[Function]] = ??? +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index 972e67c618..4e32dbfe8b 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -9,21 +9,21 @@ import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem} import org.opalj.ll.llvm.Function abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, Function, LLVMStatement](project) { - final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) - val llvmProject = project.get(LLVMProjectKey) + final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + val llvmProject = project.get(LLVMProjectKey) - /** - * Checks, if a callee is inside this analysis' context. - * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. - * By default, native methods are not inside the analysis context. - * - * @param callee The callee. - * @return True, if the callee is inside the analysis context. - */ - override def insideAnalysisContext(callee: Function): Boolean = true + /** + * Checks, if a callee is inside this analysis' context. + * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. + * By default, native methods are not inside the analysis context. + * + * @param callee The callee. + * @return True, if the callee is inside the analysis context. + */ + override def insideAnalysisContext(callee: Function): Boolean = true - override def getCallees( + override def getCallees( statement: LLVMStatement, caller: Function - ): Iterator[Function] = ??? + ): Iterator[Function] = ??? } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 4ce0381b18..c31bfbe830 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -2,7 +2,7 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} -import org.opalj.br.DeclaredMethod +import org.opalj.br.{DeclaredMethod, Method} import org.opalj.fpcf.{ProperPropertyComputationResult, PropertyBounds, PropertyStore} import org.opalj.ll.LLVMProjectKey import org.opalj.ll.fpcf.properties.NativeTaint @@ -11,70 +11,70 @@ import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProbl import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { - val llvmProject = p.get(LLVMProjectKey) + val llvmProject = p.get(LLVMProjectKey) - /** - * The analysis starts with all public methods in TaintAnalysisTestClass. - */ - override val entryPoints: Seq[(DeclaredMethod, Fact)] = (for { - m ← p.allMethodsWithBody - if (m.name == "main") - } yield declaredMethods(m) -> NullFact) + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(DeclaredMethod, Fact)] = (for { + m ← p.allMethodsWithBody + if (m.name == "main") + } yield declaredMethods(m) -> NullFact) - /** - * The sanitize method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = - callee.name == "sanitize" + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = + callee.name == "sanitize" - /** - * We do not sanitize parameters. - */ - override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + /** + * We do not sanitize parameters. + */ + override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty - /** - * Creates a new variable fact for the callee, if the source was called. - */ - override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = - if (callee.name == "source") Set(Variable(call.index)) - else Set.empty + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty - /** - * Create a FlowFact, if sink is called with a tainted variable. - * Note, that sink does not accept array parameters. No need to handle them. - */ - override protected def createFlowFact( - callee: DeclaredMethod, - call: JavaStatement, - in: Set[Fact] - ): Option[FlowFact] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) - else None + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact( + callee: DeclaredMethod, + call: JavaStatement, + in: Set[Fact] + ): Option[FlowFact[Method]] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) + else None - // Multilingual additions here + // Multilingual additions here - override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { - super.insideAnalysisContext(callee) || callee.definedMethod.isNative - } + override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { + super.insideAnalysisContext(callee) || callee.definedMethod.isNative + } - override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { - val method = source._1.definedMethod - if (method.isNative) { - // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names - val nativeMethodName = "Java_" + method.classFile.fqn + "_" + method.name - val function = llvmProject.function(nativeMethodName) - return Some(delegate(source, ((function.get, source._2), NativeTaint), identity, propertyKey)) + override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { + val method = source._1.definedMethod + if (method.isNative) { + // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names + val nativeMethodName = "Java_"+method.classFile.fqn+"_"+method.name + val function = llvmProject.function(nativeMethodName) + return Some(delegate(source, ((function.get, source._2), NativeTaint), identity, propertyKey)) + } + super.specialCase(source, propertyKey) } - super.specialCase(source, propertyKey) - } } class SimpleJavaForwardTaintAnalysis(implicit val project: SomeProject) - extends ForwardIFDSAnalysis(new SimpleJavaForwardTaintProblem(project), Taint) + extends ForwardIFDSAnalysis(new SimpleJavaForwardTaintProblem(project), Taint) object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis()(p) - override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint - override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq(LLVMProjectKey) - override val uses: Set[PropertyBounds] = super.uses ++ PropertyBounds.ub(NativeTaint) + override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis()(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq(LLVMProjectKey) + override val uses: Set[PropertyBounds] = super.uses ++ PropertyBounds.ub(NativeTaint) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala index 6eeb4ba5ab..0fc3e05db1 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -1,56 +1,56 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds.taint -import org.opalj.br.analyses.{SomeProject} +import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.{PropertyBounds, PropertyStore} -import org.opalj.ll.fpcf.analyses.ifds.{ForwardNativeIFDSAnalysis, LLVMStatement, NativeIFDSAnalysisScheduler} +import org.opalj.ll.fpcf.analyses.ifds.{Callable, ForwardNativeIFDSAnalysis, LLVMFunction, LLVMStatement, NativeIFDSAnalysisScheduler} import org.opalj.ll.fpcf.properties.NativeTaint import org.opalj.ll.llvm.Function import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, Variable} -import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation} +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaintProblem(p) { - /** - * The analysis starts with all public methods in TaintAnalysisTestClass. - */ - override val entryPoints: Seq[(Function, Fact)] = Seq.empty - - /** - * The sanitize method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: Function): Boolean = - callee.name == "sanitize" - - /** - * We do not sanitize parameters. - */ - override protected def sanitizeParameters(call: LLVMStatement, in: Set[Fact]): Set[Fact] = Set.empty - - /** - * Creates a new variable fact for the callee, if the source was called. - */ - override protected def createTaints(callee: Function, call: LLVMStatement): Set[Fact] = - if (callee.name == "source") Set(Variable(call.index)) - else Set.empty - - /** - * Create a FlowFact, if sink is called with a tainted variable. - * Note, that sink does not accept array parameters. No need to handle them. - */ - override protected def createFlowFact( - callee: Function, - call: LLVMStatement, - in: Set[Fact] - ): Option[FlowFact[NativeTaint.Callable]] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.function))) - else None + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(Function, Fact)] = Seq.empty + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Function): Boolean = + callee.name == "sanitize" + + /** + * We do not sanitize parameters. + */ + override protected def sanitizeParameters(call: LLVMStatement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: Function, call: LLVMStatement): Set[Fact] = + if (callee.name == "source") Set(Variable(call)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact( + callee: Function, + call: LLVMStatement, + in: Set[Fact] + ): Option[FlowFact[Callable]] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact[Callable](Seq(LLVMFunction(call.function)))) + else None } class SimpleNativeForwardTaintAnalysis(implicit val project: SomeProject) - extends ForwardNativeIFDSAnalysis(new SimpleNativeForwardTaintProblem(project), NativeTaint) + extends ForwardNativeIFDSAnalysis(new SimpleNativeForwardTaintProblem(project), NativeTaint) object NativeForwardTaintAnalysisScheduler extends NativeIFDSAnalysisScheduler[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new SimpleNativeForwardTaintAnalysis()(p) - override def property: IFDSPropertyMetaInformation[LLVMStatement, Fact] = NativeTaint - override val uses: Set[PropertyBounds] = super.uses // ++ PropertyBounds.ub(Taint) TODO: we do not use the native taint yet + override def init(p: SomeProject, ps: PropertyStore) = new SimpleNativeForwardTaintAnalysis()(p) + override def property: IFDSPropertyMetaInformation[LLVMStatement, Fact] = NativeTaint + override val uses: Set[PropertyBounds] = super.uses // ++ PropertyBounds.ub(Taint) TODO: we do not use the native taint yet } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 7f96ee3b35..e8f73c7e59 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -2,97 +2,96 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject -import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeIFDSProblem} +import org.opalj.ll.fpcf.analyses.ifds.{Callable, LLVMStatement, NativeIFDSProblem} import org.opalj.ll.llvm.Function - import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact, TaintProblem} abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[Fact](project) with TaintProblem[Function, LLVMStatement] { - override def nullFact: Fact = NullFact + override def nullFact: Fact = NullFact - /** - * If a variable gets assigned a tainted value, the variable will be tainted. - */ - override def normalFlow(statement: LLVMStatement, successor: LLVMStatement, - in: Set[Fact]): Set[Fact] = - statement match { - // TODO - case _ ⇒ in - } + /** + * If a variable gets assigned a tainted value, the variable will be tainted. + */ + override def normalFlow(statement: LLVMStatement, successor: LLVMStatement, + in: Set[Fact]): Set[Fact] = + statement match { + // TODO + case _ ⇒ in + } - /** - * Propagates tainted parameters to the callee. If a call to the sink method with a tainted - * parameter is detected, no call-to-start - * edges will be created. - */ - override def callFlow(call: LLVMStatement, callee: Function, - in: Set[Fact], source: (Function, Fact)): Set[Fact] = { - // TODO - Set.empty[Fact] - } + /** + * Propagates tainted parameters to the callee. If a call to the sink method with a tainted + * parameter is detected, no call-to-start + * edges will be created. + */ + override def callFlow(call: LLVMStatement, callee: Function, + in: Set[Fact], source: (Function, Fact)): Set[Fact] = { + // TODO + Set.empty[Fact] + } - /** - * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. - * If the callee's return value was tainted and it is assigned to a variable in the callee, the - * variable will be tainted. - * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds - * at this method. - * Creates new taints and FlowFacts, if necessary. - * If the sanitize method was called, nothing will be tainted. - */ - override def returnFlow(call: LLVMStatement, callee: Function, exit: LLVMStatement, - successor: LLVMStatement, in: Set[Fact]): Set[Fact] = { - // TODO - Set.empty - } + /** + * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. + * If the callee's return value was tainted and it is assigned to a variable in the callee, the + * variable will be tainted. + * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds + * at this method. + * Creates new taints and FlowFacts, if necessary. + * If the sanitize method was called, nothing will be tainted. + */ + override def returnFlow(call: LLVMStatement, callee: Function, exit: LLVMStatement, + successor: LLVMStatement, in: Set[Fact]): Set[Fact] = { + // TODO + Set.empty + } - /** - * Removes taints according to `sanitizeParamters`. - */ - override def callToReturnFlow(call: LLVMStatement, successor: LLVMStatement, - in: Set[Fact], - source: (Function, Fact)): Set[Fact] = - in -- sanitizeParameters(call, in) + /** + * Removes taints according to `sanitizeParamters`. + */ + override def callToReturnFlow(call: LLVMStatement, successor: LLVMStatement, + in: Set[Fact], + source: (Function, Fact)): Set[Fact] = + in -- sanitizeParameters(call, in) - /** - * Called, when the exit to return facts are computed for some `callee` with the null fact and - * the callee's return value is assigned to a vairbale. - * Creates a taint, if necessary. - * - * @param callee The called method. - * @param call The call. - * @return Some variable fact, if necessary. Otherwise none. - */ - protected def createTaints(callee: Function, call: LLVMStatement): Set[Fact] + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a vairbale. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + protected def createTaints(callee: Function, call: LLVMStatement): Set[Fact] - /** - * Called, when the call to return facts are computed for some `callee`. - * Creates a FlowFact, if necessary. - * - * @param callee The method, which was called. - * @param call The call. - * @return Some FlowFact, if necessary. Otherwise None. - */ - protected def createFlowFact(callee: Function, call: LLVMStatement, - in: Set[Fact]): Option[FlowFact[Function]] + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFact(callee: Function, call: LLVMStatement, + in: Set[Fact]): Option[FlowFact[Callable]] - /** - * If a parameter is tainted, the result will also be tainted. - * We assume that the callee does not call the source method. - */ - override def callOutsideOfAnalysisContext(statement: LLVMStatement, callee: Function, - successor: LLVMStatement, - in: Set[Fact]): Set[Fact] = { - // TODO - Set.empty - } + /** + * If a parameter is tainted, the result will also be tainted. + * We assume that the callee does not call the source method. + */ + override def callOutsideOfAnalysisContext(statement: LLVMStatement, callee: Function, + successor: LLVMStatement, + in: Set[Fact]): Set[Fact] = { + // TODO + Set.empty + } - /** - * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. - * True by default. This method can be overwritten by a subclass. - * - * @param callee The callee. - * @return True, by default. - */ - protected def relevantCallee(callee: Function): Boolean = true + /** + * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. + * True by default. This method can be overwritten by a subclass. + * + * @param callee The callee. + * @return True, by default. + */ + protected def relevantCallee(callee: Function): Boolean = true } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala index c3f5401e34..14caed0f8f 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala @@ -1,7 +1,6 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.properties -import org.opalj.br.Method import org.opalj.fpcf.PropertyKey import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement import org.opalj.tac.fpcf.analyses.ifds.taint.Fact @@ -9,17 +8,16 @@ import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation} case class NativeTaint(flows: Map[LLVMStatement, Set[Fact]]) extends IFDSProperty[LLVMStatement, Fact] { - override type Self = NativeTaint - override def create(result: Map[LLVMStatement, Set[Fact]]): IFDSProperty[LLVMStatement, Fact] = new NativeTaint(result) + override type Self = NativeTaint + override def create(result: Map[LLVMStatement, Set[Fact]]): IFDSProperty[LLVMStatement, Fact] = new NativeTaint(result) - override def key: PropertyKey[NativeTaint] = NativeTaint.key + override def key: PropertyKey[NativeTaint] = NativeTaint.key } object NativeTaint extends IFDSPropertyMetaInformation[LLVMStatement, Fact] { - override type Self = NativeTaint - type Callable = Either[Function, Method] - override def create(result: Map[LLVMStatement, Set[Fact]]): IFDSProperty[LLVMStatement, Fact] = new NativeTaint(result) + override type Self = NativeTaint + override def create(result: Map[LLVMStatement, Set[Fact]]): IFDSProperty[LLVMStatement, Fact] = new NativeTaint(result) - val key: PropertyKey[NativeTaint] = PropertyKey.create("NativeTaint", new NativeTaint(Map.empty)) + val key: PropertyKey[NativeTaint] = PropertyKey.create("NativeTaint", new NativeTaint(Map.empty)) } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala index 701e321254..614d81871b 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala @@ -15,11 +15,11 @@ case class BasicBlock(block_ref: LLVMBasicBlockRef) def terminator(): Option[Instruction with Terminator] = { OptionalInstruction(LLVMGetBasicBlockTerminator(block_ref)) match { - case Some(terminator) => { - assert(terminator.isTerminator()) - Some(terminator.asInstanceOf[Instruction with Terminator]) - } - case None => None + case Some(terminator) ⇒ { + assert(terminator.isTerminator()) + Some(terminator.asInstanceOf[Instruction with Terminator]) + } + case None ⇒ None } } @@ -45,17 +45,17 @@ case class BasicBlock(block_ref: LLVMBasicBlockRef) * Returns `true` if this node has successor nodes. */ override def hasSuccessors: Boolean = terminator match { - case Some(t) => t.hasSuccessors - case None => false - } + case Some(t) ⇒ t.hasSuccessors + case None ⇒ false + } /** * Applies the given function for each successor node. */ override def foreachSuccessor(f: Node ⇒ Unit): Unit = terminator match { - case Some(t) => t.foreachSuccessor(f) - case None => - } + case Some(t) ⇒ t.foreachSuccessor(f) + case None ⇒ + } } class InstructionIterator(var ref: LLVMValueRef) extends Iterator[Instruction] { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala index e51aad55dd..e4781bca92 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala @@ -5,10 +5,10 @@ import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM._ object OptionalInstruction { - def apply(ref: LLVMValueRef): Option[Instruction] = { - if (ref.isNull) return None - Some(Instruction(ref)) - } + def apply(ref: LLVMValueRef): Option[Instruction] = { + if (ref.isNull) return None + Some(Instruction(ref)) + } } object Instruction { @@ -95,6 +95,9 @@ trait Terminator { def foreachSuccessor(f: BasicBlock ⇒ Unit): Unit = { (0 to numSuccessors() - 1).foreach(i ⇒ f(getSuccessor(i))) } + def successors(): Seq[Instruction] = { + (0 to numSuccessors() - 1).map(i ⇒ getSuccessor(i).firstInstruction()) + } } sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { @@ -102,6 +105,10 @@ sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { def parent(): BasicBlock = BasicBlock(LLVMGetInstructionParent(ref)) def function(): Function = parent.parent def next(): Option[Instruction] = OptionalInstruction(LLVMGetNextInstruction(ref)) + def successors(): Seq[Instruction] = next match { + case Some(successor) ⇒ Seq(successor) + case None ⇒ Seq() + } } case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index 05777d5232..73a103c091 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -7,14 +7,14 @@ package ifds import java.io.File import java.io.PrintWriter -import scala.collection.{Set => SomeSet} +import scala.collection.{Set ⇒ SomeSet} import scala.collection.mutable import com.typesafe.config.ConfigValueFactory import javax.swing.JOptionPane import org.opalj.util.Milliseconds import org.opalj.util.PerformanceEvaluation.time -import org.opalj.fpcf.{EOptionP, EPK, FinalE, FinalEP, FinalP, InterimEUBP, InterimResult, ProperPropertyComputationResult, PropertyBounds, PropertyStore, Result, SomeEPS} +import org.opalj.fpcf.{EOptionP, EPK, FinalE, FinalEP, FinalP, InterimEUBP, InterimResult, ProperPropertyComputationResult, PropertyBounds, PropertyStore, Result, SomeEPS} import org.opalj.fpcf.seq.PKESequentialPropertyStore import org.opalj.value.ValueInformation import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler @@ -103,12 +103,12 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble val code: Array[Stmt[V]], val cfg: CFG[Stmt[V], TACStmts[V]], var pendingIfdsCallSites: Map[(DeclaredMethod, IFDSFact), Set[(BasicBlock, Int)]], - var pendingTacCallSites: Map[DeclaredMethod, Set[BasicBlock]] = Map.empty, + var pendingTacCallSites: Map[DeclaredMethod, Set[BasicBlock]] = Map.empty, var pendingIfdsDependees: Map[(DeclaredMethod, IFDSFact), EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] = Map.empty, - var pendingTacDependees: Map[Method, EOptionP[Method, TACAI]] = Map.empty, - var pendingCgCallSites: Set[BasicBlock] = Set.empty, - var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, - var outgoingFacts: Map[BasicBlock, Map[CFGNode, Set[IFDSFact]]] = Map.empty + var pendingTacDependees: Map[Method, EOptionP[Method, TACAI]] = Map.empty, + var pendingCgCallSites: Set[BasicBlock] = Set.empty, + var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, + var outgoingFacts: Map[BasicBlock, Map[CFGNode, Set[IFDSFact]]] = Map.empty ) /** @@ -232,7 +232,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble exitFacts: Map[JavaStatement, Set[IFDSFact]] )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] - /** + /** * Performs an IFDS analysis for a method-fact-pair. * * @param entity The method-fact-pair, that will be analyzed. @@ -252,8 +252,8 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble return Result(entity, createPropertyValue(Map.empty)); ifdsProblem.specialCase(entity, propertyKey) match { - case Some(result) => return result; - case _ => + case Some(result) ⇒ return result; + case _ ⇒ } val method = declaredMethod.definedMethod @@ -445,7 +445,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble val oldValue = state.pendingIfdsDependees.get(e) val oldExitFacts: Map[JavaStatement, Set[IFDSFact]] = oldValue match { case Some(ep: InterimEUBP[_, IFDSProperty[JavaStatement, IFDSFact]]) ⇒ ep.ub.flows - case _ ⇒ Map.empty + case _ ⇒ Map.empty } val exitFacts: Map[JavaStatement, Set[IFDSFact]] = callFlows match { case ep: FinalEP[_, IFDSProperty[JavaStatement, IFDSFact]] ⇒ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysisScheduler.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala similarity index 89% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysisScheduler.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala index 2744ced696..34f44a1199 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysisScheduler.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -24,7 +24,7 @@ import org.opalj.tac.fpcf.properties.TACAI * @param project The analyzed project. * @author Mario Trageser */ -class IFDSBasedVariableTypeAnalysisScheduler private(ifdsProblem: VariableTypeProblem)(implicit val project: SomeProject) +class IFDSBasedVariableTypeAnalysis(ifdsProblem: VariableTypeProblem)(implicit val project: SomeProject) extends ForwardIFDSAnalysis[VTAFact](ifdsProblem, VTAResult) object IFDSBasedVariableTypeAnalysisScheduler extends IFDSAnalysisScheduler[VTAFact] { @@ -34,10 +34,10 @@ object IFDSBasedVariableTypeAnalysisScheduler extends IFDSAnalysisScheduler[VTAF override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) - override def init(p: SomeProject, ps: PropertyStore): IFDSBasedVariableTypeAnalysisScheduler = { + override def init(p: SomeProject, ps: PropertyStore): IFDSBasedVariableTypeAnalysis = { implicit val project = p - if (SUBSUMING) new IFDSBasedVariableTypeAnalysisScheduler(new VariableTypeProblem(p)) with Subsuming[VTAFact] - else new IFDSBasedVariableTypeAnalysisScheduler(new VariableTypeProblem(p)) + if (SUBSUMING) new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) with Subsuming[JavaStatement, VTAFact] + else new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) } override def property: IFDSPropertyMetaInformation[JavaStatement, VTAFact] = VTAResult @@ -72,8 +72,8 @@ class IFDSBasedVariableTypeAnalysisRunner extends AbsractIFDSAnalysisRunner { analysis: AbstractIFDSAnalysis[_] ): Option[Object] = analysis match { - case subsuming: Subsuming[_] ⇒ Some(subsuming.numberOfSubsumptions) - case _ ⇒ None + case subsuming: Subsuming[_, _] ⇒ Some(subsuming.numberOfSubsumptions) + case _ ⇒ None } override protected def writeAdditionalEvaluationResultsToFile( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala index b92afc23ed..5cbf3de3d9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala @@ -61,10 +61,10 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va * the facts in `in` held before `statement` and `statement` calls `callee`. */ def callFlow( - call: Statement, - callee: Callable, - in: Set[IFDSFact], - source: (Callable, IFDSFact) + call: Statement, + callee: Callable, + in: Set[IFDSFact], + source: (Callable, IFDSFact) ): Set[IFDSFact] /** @@ -81,11 +81,11 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va * `successor` will be executed next. */ def returnFlow( - call: Statement, - callee: Callable, - exit: Statement, - successor: Statement, - in: Set[IFDSFact] + call: Statement, + callee: Callable, + exit: Statement, + successor: Statement, + in: Set[IFDSFact] ): Set[IFDSFact] /** @@ -126,10 +126,10 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va * @return The facts, which hold after the call, excluding the call to return flow. */ def callOutsideOfAnalysisContext( - call: Statement, - callee: Callable, - successor: Statement, - in: Set[IFDSFact] + call: Statement, + callee: Callable, + successor: Statement, + in: Set[IFDSFact] ): Set[IFDSFact] /** @@ -144,7 +144,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va caller: Callable ): Iterator[Callable] - def delegateAnalysis(source: (Callable, IFDSFact)): Option[((Entity, IFDSFact), Set[IFDSFact] => Set[IFDSFact])] = None + def delegateAnalysis(source: (Callable, IFDSFact)): Option[((Entity, IFDSFact), Set[IFDSFact] ⇒ Set[IFDSFact])] = None def specialCase(source: (Callable, IFDSFact), propertyKey: IFDSPropertyMetaInformation[Statement, IFDSFact]): Option[ProperPropertyComputationResult] = None } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 6d6857cd11..bb50b8ae9c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -90,38 +90,39 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e * not re-analyze the code. */ if (declaringClass ne declaredMethod.declaringClassType) Some(delegate( - source, ((declaredMethods(source._1.definedMethod), source._2), propertyKey), identity, propertyKey)) + source, ((declaredMethods(source._1.definedMethod), source._2), propertyKey), identity, propertyKey + )) None - } + } - /** - * This method will be called if the analysis of a method shall be delegated to another analysis. - * - * @param source A pair consisting of the declared method of the subtype and an input fact. - * @param delegation A pair consisting of the delegated entity and an input fact as well as the delegated property. - * @param resultMapping A function that maps the results of the delegation. - * @param propertyKey the propertyKey used for the instantiation of new results - * @return The result of the other analysis. - */ - protected def delegate( - source: (DeclaredMethod, Fact), - delegation: ((Entity, Fact), IFDSPropertyMetaInformation[_, Fact]), - resultMapping: Set[Fact] => Set[Fact], - propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact] - ): ProperPropertyComputationResult = { + /** + * This method will be called if the analysis of a method shall be delegated to another analysis. + * + * @param source A pair consisting of the declared method of the subtype and an input fact. + * @param delegation A pair consisting of the delegated entity and an input fact as well as the delegated property. + * @param resultMapping A function that maps the results of the delegation. + * @param propertyKey the propertyKey used for the instantiation of new results + * @return The result of the other analysis. + */ + protected def delegate( + source: (DeclaredMethod, Fact), + delegation: ((Entity, Fact), IFDSPropertyMetaInformation[_, Fact]), + resultMapping: Set[Fact] ⇒ Set[Fact], + propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact] + ): ProperPropertyComputationResult = { - def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { - case FinalP(p) ⇒ Result(source, propertyKey.create(Map.empty)) + def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { + case FinalP(p) ⇒ Result(source, propertyKey.create(Map.empty)) - case ep @ InterimUBP(ub: Property) ⇒ - // TODO: resultMapping(ub.asInstanceOf[Set[Fact]]).asInstanceOf[Property] - InterimResult.forUB(source, propertyKey.create(Map.empty), Set(ep), c) + case ep @ InterimUBP(ub: Property) ⇒ + // TODO: resultMapping(ub.asInstanceOf[Set[Fact]]).asInstanceOf[Property] + InterimResult.forUB(source, propertyKey.create(Map.empty), Set(ep), c) - case epk ⇒ - InterimResult.forUB(source, propertyKey.create(Map.empty), Set(epk), c) + case epk ⇒ + InterimResult.forUB(source, propertyKey.create(Map.empty), Set(epk), c) + } + c(propertyStore(delegation._1, delegation._2.key)) } - c(propertyStore(delegation._1, delegation._2.key)) - } } abstract class JavaBackwardIFDSProblem[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](project: SomeProject) extends JavaIFDSProblem[IFDSFact](project) with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala index d61b9208ec..e40566428c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala @@ -8,7 +8,7 @@ import org.opalj.br.analyses.SomeProject * * @author Mario Trageser */ -trait Subsuming[IFDSFact <: SubsumableFact] extends Subsumable[IFDSFact] { +trait Subsuming[Statement, IFDSFact <: SubsumableFact] extends Subsumable[Statement, IFDSFact] { val numberOfSubsumptions = new NumberOfSubsumptions @@ -43,9 +43,9 @@ trait Subsuming[IFDSFact <: SubsumableFact] extends Subsumable[IFDSFact] { * Considers subsuming. */ override protected def filterNewInformation[T <: IFDSFact]( - newExitFacts: Map[JavaStatement, Set[T]], - oldExitFacts: Map[JavaStatement, Set[T]], project: SomeProject - ): Map[JavaStatement, Set[T]] = + newExitFacts: Map[Statement, Set[T]], + oldExitFacts: Map[Statement, Set[T]], project: SomeProject + ): Map[Statement, Set[T]] = newExitFacts.keys.map { statement ⇒ val old = oldExitFacts.get(statement) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala index f54315d228..525ced6da4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala @@ -349,7 +349,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF ) case staticField: StaticField ⇒ facts += staticField // If the source was reached in a callee, create a flow fact from this method to the sink. - case calleeFact: FlowFact[Method] if isCallFlow ⇒ + case calleeFact: FlowFact[Method] @unchecked if isCallFlow ⇒ val callerFact = applyFlowFactFromCallee(calleeFact, source) if (callerFact.isDefined) facts += callerFact.get case _ ⇒ // Nothing to do diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index 5c700d1b3a..f509c54bac 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -1,13 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.br.Method -import org.opalj.br.ObjectType -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, JavaStatement, UnbalancedReturnFact} -import org.opalj.tac.Assignment -import org.opalj.tac.Expr -import org.opalj.tac.Stmt +import org.opalj.br.{Method, ObjectType} +import org.opalj.tac.{Assignment, Expr, Stmt} import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, UnbalancedReturnFact} trait Fact extends AbstractIFDSFact diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala index ab7baa3861..931922d2c0 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala @@ -17,7 +17,7 @@ case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Met * * @author Mario Trageser */ -class BackwardTaintAnalysisFixtureScheduler private(implicit val project: SomeProject) +class BackwardTaintAnalysisFixture(implicit val project: SomeProject) extends BackwardIFDSAnalysis(new BackwardTaintProblemFixture(project), Taint) class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p) { @@ -80,7 +80,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p object BackwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new BackwardTaintAnalysisFixtureScheduler()(p) + override def init(p: SomeProject, ps: PropertyStore) = new BackwardTaintAnalysisFixture()(p) override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala index e7605bab7f..352a374d24 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala @@ -13,7 +13,7 @@ import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} * * @author Mario Trageser */ -class ForwardTaintAnalysisFixtureScheduler private(implicit val project: SomeProject) +class ForwardTaintAnalysisFixture(implicit val project: SomeProject) extends ForwardIFDSAnalysis(new ForwardTaintProblemFixture(project), Taint) class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) { @@ -56,7 +56,7 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixtureScheduler()(p) + override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture()(p) override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint } From 41d949c5cd9d716e5ef55d4205aed33917482018 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 11 Mar 2022 20:11:40 +0100 Subject: [PATCH 21/67] WIP --- .../HerosForwardClassForNameAnalysis.scala | 6 +- .../ifds/ForwardNativeIFDSAnalysis.scala | 139 +------ .../opalj/ll/fpcf/analyses/ifds/ICFG.scala | 59 ++- .../opalj/ll/fpcf/analyses/ifds/IFDS.scala | 28 ++ ...eIFDSAnalysis.scala => IFDSAnalysis.scala} | 360 +++++++----------- .../analyses/ifds/NativeForwardICFG.scala | 109 +++++- .../analyses/ifds/NativeIFDSAnalysis.scala | 37 ++ 7 files changed, 375 insertions(+), 363 deletions(-) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDS.scala rename OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/{AbstractNativeIFDSAnalysis.scala => IFDSAnalysis.scala} (61%) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index 7f293bbc75..a948f123e7 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -76,7 +76,7 @@ class HerosForwardClassForNameAnalysis( val classHierarchy: ClassHierarchy = p.classHierarchy - var flowFacts = Map.empty[Method, Set[FlowFact]] + var flowFacts = Map.empty[Method, Set[FlowFact[Method]]] override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { @@ -308,7 +308,7 @@ class HerosForwardClassForNameAnalysis( if (initialMethods.contains(stmt.method)) flowFacts = flowFacts.updated( stmt.method, - flowFacts.getOrElse(stmt.method, Set.empty[FlowFact]) + flowFact + flowFacts.getOrElse(stmt.method, Set.empty[FlowFact[Method]]) + flowFact ) Set(flowFact) case _ ⇒ @@ -335,7 +335,7 @@ class HerosForwardClassForNameAnalysis( if (initialMethods.contains(stmt.method)) flowFacts = flowFacts.updated( stmt.method, - flowFacts.getOrElse(stmt.method, Set.empty[FlowFact]) + flowFact + flowFacts.getOrElse(stmt.method, Set.empty[FlowFact[Method]]) + flowFact ) Some(flowFact) } else None diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala index 9d4c5a2530..059062d87f 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala @@ -1,11 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.br.cfg.CFGNode -import org.opalj.ll.llvm.Function -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, AbstractIFDSFact} +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation -import org.opalj.tac.{Return, ReturnValue} /** * An IFDS analysis, which analyzes the code in the control flow direction. @@ -13,131 +10,9 @@ import org.opalj.tac.{Return, ReturnValue} * @author Mario Trageser */ -abstract class ForwardNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: NativeIFDSProblem[IFDSFact], propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact]) extends AbstractNativeIFDSAnalysis[IFDSFact](ifdsProblem, new NativeForwardICFG[IFDSFact], propertyKey) { - /** - * Collects the output facts at the predecessors of the normal and abnormal return node. - */ - override protected def collectResult(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] = { - mergeMaps( - resultOfExitNode(state.cfg.normalReturnNode), - resultOfExitNode(state.cfg.abnormalReturnNode) - ) - } - - /** - * Calls callFlow. - */ - override protected def callToStartFacts(call: LLVMStatement, callee: Function, - in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { - numberOfCalls.callFlow += 1 - sumOfInputfactsForCallbacks += in.size - ifdsProblem.callFlow(call, callee, in, state.source) - } - - /** - * Combines each normal exit node with each normal successor and each abnormal exit statement - * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. - */ - override protected def addExitToReturnFacts( - summaryEdges: Map[LLVMStatement, Set[IFDSFact]], - successors: Set[LLVMStatement], call: LLVMStatement, - callee: Function, - exitFacts: Map[LLVMStatement, Set[IFDSFact]] - )(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] = { - // First process for normal returns, then abnormal returns. - var result = summaryEdges - if (AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { - val successors = nextStatementsWithNode(call) - for { - successor ← successors - exitStatement ← exitFacts.keys - if (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && - (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || - (successor._2.isCatchNode || successor._2.isAbnormalReturnExitNode) && - (exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID) - } result = addSummaryEdge(result, call, exitStatement, successor._1, callee, exitFacts) - } else { - val successors = nextStatements(call) - for { - successor ← successors - exitStatement ← exitFacts.keys - } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) - } - result - } - - /** - * Like nextStatements, but maps each successor statement to the corresponding successor node. - * When determining the successor node, catch nodes are not skipped. - */ - private def nextStatementsWithNode(statement: LLVMStatement)(implicit state: State): Map[LLVMStatement, CFGNode] = { - val index = statement.index - val basicBlock = statement.node.asBasicBlock - if (index == basicBlock.endPC) - basicBlock.successors.iterator - .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap - else { - val nextIndex = index + 1 - Map(LLVMStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, - statement.code, statement.cfg) → basicBlock) - } - } - - /** - * Collects the facts valid at an exit node based on the current results. - * - * @param exit The exit node. - * @return A map, mapping from each predecessor of the `exit` node to the facts valid at the - * `exit` node under the assumption that the predecessor was executed before. - */ - private def resultOfExitNode(exit: CFGNode)( - implicit - state: State - ): Map[LLVMStatement, Set[IFDSFact]] = { - var result = Map.empty[LLVMStatement, Set[IFDSFact]] - exit.predecessors foreach { predecessor ⇒ - if (predecessor.isBasicBlock) { - val basicBlock = predecessor.asBasicBlock - val exitFacts = state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)) - if (exitFacts.isDefined) { - val lastIndex = basicBlock.endPC - val stmt = LLVMStatement(state.method, basicBlock, state.code(lastIndex), - lastIndex, state.code, state.cfg) - result += stmt → exitFacts.get - } - } - } - result - } - - /** - * Adds a summary edge for a call to a map representing summary edges. - * - * @param summaryEdges The current map representing the summary edges. - * Maps from successor statements to facts, which hold at their beginning. - * @param call The call, calling the `callee`. - * @param exitStatement The exit statement for the new summary edge. - * @param successor The successor statement of the call for the new summary edge. - * @param callee The callee, called by `call`. - * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly - * found exit facts. - * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. - */ - private def addSummaryEdge(summaryEdges: Map[LLVMStatement, Set[IFDSFact]], call: LLVMStatement, - exitStatement: LLVMStatement, successor: LLVMStatement, - callee: Function, - allNewExitFacts: Map[LLVMStatement, Set[IFDSFact]]): Map[LLVMStatement, Set[IFDSFact]] = { - val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) - numberOfCalls.returnFlow += 1 - sumOfInputfactsForCallbacks += in.size - val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) - val newFacts = - if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { - val summaryForSuccessor = summaryEdges(successor) - if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned - else returned ++ summaryForSuccessor - } else returned - summaryEdges.updated(successor, newFacts) - } - -} +class ForwardNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( + ifdsProblem: NativeIFDSProblem[IFDSFact], + propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] +) extends NativeIFDSAnalysis[IFDSFact]( + ifdsProblem, new NativeForwardICFG[IFDSFact], propertyKey +) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala index 220f443a03..d0f3451154 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala @@ -1,9 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds import scala.collection.{Set ⇒ SomeSet} -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem} + +abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] { + type State = IFDSState[IFDSFact, C, S, Node] + type Problem = IFDSProblem[IFDSFact, C, S] -abstract class ICFG[IFDSFact <: AbstractIFDSFact, C, Statement, Node] { /** * Determines the basic blocks, at which the analysis starts. * @@ -34,7 +37,7 @@ abstract class ICFG[IFDSFact <: AbstractIFDSFact, C, Statement, Node] { * @param node The basic block. * @return The first index of some `basic block`, that will be analyzed. */ - def firstStatement(node: Node): Statement + def firstStatement(node: Node): S /** * Determines the last index of some `basic block`, that will be analzyed. @@ -42,7 +45,7 @@ abstract class ICFG[IFDSFact <: AbstractIFDSFact, C, Statement, Node] { * @param node The basic block. * @return The last index of some `basic block`, that will be analzyed. */ - def lastStatement(node: Node): Statement + def lastStatement(node: Node): S /** * Determines the statement that will be analyzed after some other statement. @@ -50,7 +53,7 @@ abstract class ICFG[IFDSFact <: AbstractIFDSFact, C, Statement, Node] { * @param statement The current statement. * @return The statement that will be analyzed after `statement`. */ - def nextStatement(statement: Statement): Statement + def nextStatement(statement: S): S /** * Determines the statement, that will be analyzed after some other `statement`. @@ -58,7 +61,7 @@ abstract class ICFG[IFDSFact <: AbstractIFDSFact, C, Statement, Node] { * @param statement The source statement. * @return The successor statements */ - def nextStatements(statement: Statement): Set[Statement] + def nextStatements(statement: S): Set[S] /** * Gets the set of all methods possibly called at some statement. @@ -67,5 +70,47 @@ abstract class ICFG[IFDSFact <: AbstractIFDSFact, C, Statement, Node] { * @return All callables possibly called at the statement or None, if the statement does not * contain a call. */ - def getCalleesIfCallStatement(statement: Statement): Option[SomeSet[C]] + def getCalleesIfCallStatement(statement: S): Option[SomeSet[C]] + + /** + * Collects the facts valid at all exit nodes based on the current results. + * + * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at + * the exit node under the assumption that the predecessor was executed before. + */ + def collectResult(implicit state: State): Map[S, Set[IFDSFact]] + + /** + * Determines the facts, for which a `callee` is analyzed. + * + * @param call The call, which calls `callee`. + * @param callee The method, which is called by `call`. + * @param in The facts, which hold before the `call`. + * @return The facts, for which `callee` will be analyzed. + */ + def callToStartFacts(call: S, callee: C, in: Set[IFDSFact])( + implicit + state: State, + ifdsProblem: Problem, + statistics: Statistics + ): Set[IFDSFact] + + /** + * Collects the exit facts of a `callee` and adds them to the `summaryEdges`. + * + * @param summaryEdges The current summary edges. They map successor statements of the `call` + * to facts, which hold before they are executed. + * @param successors The successor of `call`, which is considered. + * @param call The statement, which calls `callee`. + * @param callee The method, called by `call`. + * @param exitFacts Maps exit statements of the `callee` to the facts, which hold after them. + * @return The summary edges plus the exit to return facts for `callee` and `successor`. + */ + def addExitToReturnFacts( + summaryEdges: Map[S, Set[IFDSFact]], + successors: Set[S], + call: S, + callee: C, + exitFacts: Map[S, Set[IFDSFact]] + )(implicit state: State, ifdsProblem: Problem): Map[S, Set[IFDSFact]] } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDS.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDS.scala new file mode 100644 index 0000000000..c5f2449d57 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDS.scala @@ -0,0 +1,28 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +object IFDS { + /** + * Merges two maps that have sets as values. + * + * @param map1 The first map. + * @param map2 The second map. + * @return A map containing the keys of both maps. Each key is mapped to the union of both maps' + * values. + */ + def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { + var result = map1 + for ((key, values) ← map2) { + result.get(key) match { + case Some(resultValues) ⇒ + if (resultValues.size > values.size) + result = result.updated(key, resultValues ++ values) + else + result = result.updated(key, values ++ resultValues) + case None ⇒ + result = result.updated(key, values) + } + } + result + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala similarity index 61% rename from OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala index 597b3c091c..b8d6f66a02 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/AbstractNativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala @@ -1,33 +1,46 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ - package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} +import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} import org.opalj.fpcf._ -import org.opalj.ll.LLVMProjectKey -import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem, NumberOfCalls, Subsumable} -import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.properties.cg.Callees +import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation} import scala.collection.{mutable, Set ⇒ SomeSet} +abstract class Statement[Node] { + def node(): Node +} + /** + * The state of the analysis. For each method and source fact, there is a separate state. * - * @param ifdsProblem - * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. - * @tparam IFDSFact + * @param source The input fact, for which the `method` is analyzed. + * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input + * facts to the instruction, at which they may be called. + * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input + * facts to the intermediate result of their IFDS analysis. + * Only contains method-fact-pairs, for which this analysis is + * waiting for a final result. + * @param pendingCgCallSites The basic blocks containing call sites, for which the analysis is + * waiting for the final call graph result. + * @param incomingFacts Maps each basic block to the data flow facts valid at its first + * statement. + * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at + * the beginning of the successor. */ - -abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( - val ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement], - val icfg: ICFG[IFDSFact, Function, LLVMStatement, BasicBlock], - val propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] +protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( + val source: (C, IFDSFact), + var pendingIfdsCallSites: Map[(C, IFDSFact), Set[S]] = Map.empty, + var pendingIfdsDependees: Map[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] = Map.empty, + var pendingCgCallSites: Set[Node] = Set.empty, + var incomingFacts: Map[Node, Set[IFDSFact]] = Map.empty, + var outgoingFacts: Map[Node, Map[Node, Set[IFDSFact]]] = Map.empty ) - extends FPCFAnalysis - with Subsumable[LLVMStatement, IFDSFact] { +protected class Statistics { /** * Counts, how many times the abstract methods were called. */ @@ -37,42 +50,28 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * Counts, how many input facts are passed to callbacks. */ var sumOfInputfactsForCallbacks = 0L +} - /** - * The state of the analysis. For each method and source fact, there is a separate state. - * - * @param source The input fact, for which the `method` is analyzed. - * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input - * facts to the instruction, at which they may be called. - * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input - * facts to the intermediate result of their IFDS analysis. - * Only contains method-fact-pairs, for which this analysis is - * waiting for a final result. - * @param pendingCgCallSites The basic blocks containing call sites, for which the analysis is - * waiting for the final call graph result. - * @param incomingFacts Maps each basic block to the data flow facts valid at its first - * statement. - * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at - * the beginning of the successor. - */ - protected class State( - val source: (Function, IFDSFact), - var pendingIfdsCallSites: Map[(Function, IFDSFact), Set[LLVMStatement]] = Map.empty, - var pendingIfdsDependees: Map[(Function, IFDSFact), EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] = Map.empty, - var pendingCgCallSites: Set[BasicBlock] = Set.empty, - var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, - var outgoingFacts: Map[BasicBlock, Map[BasicBlock, Set[IFDSFact]]] = Map.empty - ) +/** + * + * @param ifdsProblem + * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. + * @tparam IFDSFact + */ +class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( + implicit + val ifdsProblem: IFDSProblem[IFDSFact, C, S], + val icfg: ICFG[IFDSFact, C, S, Node], + val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact] +) + extends FPCFAnalysis + with Subsumable[S, IFDSFact] { + type State = IFDSState[IFDSFact, C, S, Node] + override val project: SomeProject = ifdsProblem.project - type QueueEntry = (BasicBlock, Set[IFDSFact], Option[LLVMStatement], Option[Function], Option[IFDSFact]) + implicit var statistics = new Statistics - /** - * Collects the facts valid at all exit nodes based on the current results. - * - * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at - * the exit node under the assumption that the predecessor was executed before. - */ - protected def collectResult(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] + type QueueEntry = (Node, Set[IFDSFact], Option[S], Option[C], Option[IFDSFact]) /** * Creates an IFDSProperty containing the result of this analysis. @@ -80,39 +79,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param result Maps each exit statement to the facts, which hold after the exit statement. * @return An IFDSProperty containing the `result`. */ - protected def createPropertyValue(result: Map[LLVMStatement, Set[IFDSFact]]): IFDSProperty[LLVMStatement, IFDSFact] = propertyKey.create(result) - - /** - * Determines the facts, for which a `callee` is analyzed. - * - * @param call The call, which calls `callee`. - * @param callee The method, which is called by `call`. - * @param in The facts, which hold before the `call`. - * @return The facts, for which `callee` will be analyzed. - */ - protected def callToStartFacts(call: LLVMStatement, callee: Function, in: Set[IFDSFact])( - implicit - state: State - ): Set[IFDSFact] - - /** - * Collects the exit facts of a `callee` and adds them to the `summaryEdges`. - * - * @param summaryEdges The current summary edges. They map successor statements of the `call` - * to facts, which hold before they are executed. - * @param successors The successor of `call`, which is considered. - * @param call The statement, which calls `callee`. - * @param callee The method, called by `call`. - * @param exitFacts Maps exit statements of the `callee` to the facts, which hold after them. - * @return The summary edges plus the exit to return facts for `callee` and `successor`. - */ - protected def addExitToReturnFacts( - summaryEdges: Map[LLVMStatement, Set[IFDSFact]], - successors: Set[LLVMStatement], - call: LLVMStatement, - callee: Function, - exitFacts: Map[LLVMStatement, Set[IFDSFact]] - )(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] + protected def createPropertyValue(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] = propertyKey.create(result) /** * Performs an IFDS analysis for a method-fact-pair. @@ -122,7 +89,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * statements. Returns an interim result, if the TAC or call graph of this method or the * IFDS analysis for a callee is still pending. */ - def performAnalysis(entity: (Function, IFDSFact)): ProperPropertyComputationResult = { + def performAnalysis(entity: (C, IFDSFact)): ProperPropertyComputationResult = { val (function, sourceFact) = entity // Start processing at the start of the cfg with the given source fact @@ -148,7 +115,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * */ protected def createResult()(implicit state: State): ProperPropertyComputationResult = { - val propertyValue = createPropertyValue(collectResult) + val propertyValue = createPropertyValue(icfg.collectResult) val dependees = state.pendingIfdsDependees.values if (dependees.isEmpty) Result(state.source, propertyValue) else InterimResult.forUB(state.source, propertyValue, dependees.toSet, propertyUpdate) @@ -166,12 +133,12 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( eps: SomeEPS )(implicit state: State): ProperPropertyComputationResult = { (eps: @unchecked) match { - case FinalE(e: (Function, IFDSFact) @unchecked) ⇒ + case FinalE(e: (C, IFDSFact) @unchecked) ⇒ reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) case interimEUBP @ InterimEUBP( - e: (Function, IFDSFact) @unchecked, - ub: IFDSProperty[LLVMStatement, IFDSFact]@unchecked + e: (C, IFDSFact) @unchecked, + ub: IFDSProperty[S, IFDSFact] @unchecked ) ⇒ if (ub.flows.values .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { @@ -179,15 +146,15 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( // Update the pendingIfdsDependee entry to the new interim result. state.pendingIfdsDependees += e -> interimEUBP - .asInstanceOf[EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] + .asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - case FinalEP(_: Function, _: Callees) ⇒ - reAnalyzebasicBlocks(state.pendingCgCallSites) + case FinalEP(_: C, _: Callees) ⇒ + reAnalyzeBasicBlocks(state.pendingCgCallSites) - case InterimEUBP(_: Function, _: Callees) ⇒ - reAnalyzebasicBlocks(state.pendingCgCallSites) + case InterimEUBP(_: C, _: Callees) ⇒ + reAnalyzeBasicBlocks(state.pendingCgCallSites) } createResult() @@ -219,28 +186,28 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * at their start. */ protected def handleCall( - basicBlock: BasicBlock, - call: LLVMStatement, - callees: SomeSet[Function], + basicBlock: Node, + call: S, + callees: SomeSet[C], in: Set[IFDSFact], calleeWithUpdateFact: Option[IFDSFact] )( implicit state: State - ): Map[LLVMStatement, Set[IFDSFact]] = { + ): Map[S, Set[IFDSFact]] = { val successors = icfg.nextStatements(call) val inputFacts = beforeHandleCall(call, in) // Facts valid at the start of each successor - var summaryEdges: Map[LLVMStatement, Set[IFDSFact]] = Map.empty + var summaryEdges: Map[S, Set[IFDSFact]] = Map.empty /* - * If calleeWithUpdateFact is present, this means that the basic block already has been - * analyzed with the `inputFacts`. - */ +* If calleeWithUpdateFact is present, this means that the basic block already has been +* analyzed with the `inputFacts`. +*/ if (calleeWithUpdateFact.isEmpty) for (successor ← successors) { - numberOfCalls.callToReturnFlow += 1 - sumOfInputfactsForCallbacks += in.size + statistics.numberOfCalls.callToReturnFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size summaryEdges += successor -> propagateNullFact( inputFacts, @@ -260,17 +227,17 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( val callToStart = if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) else { - propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) + propagateNullFact(inputFacts, icfg.callToStartFacts(call, callee, inputFacts)) } - var allNewExitFacts: Map[LLVMStatement, Set[IFDSFact]] = Map.empty + var allNewExitFacts: Map[S, Set[IFDSFact]] = Map.empty // Collect exit facts for each input fact separately for (fact ← callToStart) { /* - * If this is a recursive call with the same input facts, we assume that the - * call only produces the facts that are already known. The call site is added to - * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts - * become known for the input fact. - */ +* If this is a recursive call with the same input facts, we assume that the +* call only produces the facts that are already known. The call site is added to +* `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts +* become known for the input fact. +*/ if ((callee eq state.source._1) && fact == state.source._2) { val newDependee = if (state.pendingIfdsCallSites.contains(state.source)) @@ -279,18 +246,18 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( else Set((basicBlock, call)) state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(state.source, newDependee) - allNewExitFacts = mergeMaps(allNewExitFacts, collectResult) + allNewExitFacts = IFDS.mergeMaps(allNewExitFacts, icfg.collectResult) } else { val e = (callee, fact) val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]]] + .asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[LLVMStatement, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[LLVMStatement, IFDSFact]]) ⇒ ep.ub.flows - case _ ⇒ Map.empty + val oldExitFacts: Map[S, Set[IFDSFact]] = oldValue match { + case Some(ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]]) ⇒ ep.ub.flows + case _ ⇒ Map.empty } - val exitFacts: Map[LLVMStatement, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[LLVMStatement, IFDSFact]] ⇒ + val exitFacts: Map[S, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ if (state.pendingIfdsCallSites.contains(e) && state.pendingIfdsCallSites(e).nonEmpty) { val newDependee = @@ -299,13 +266,13 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( } state.pendingIfdsDependees -= e ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, IFDSFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ /* - * Add the call site to `pendingIfdsCallSites` and - * `pendingIfdsDependees` and continue with the facts in the interim - * result for now. When the analysis for the callee finishes, the - * analysis for this call site will be triggered again. - */ +* Add the call site to `pendingIfdsCallSites` and +* `pendingIfdsDependees` and continue with the facts in the interim +* result for now. When the analysis for the callee finishes, the +* analysis for this call site will be triggered again. +*/ addIfdsDependee(e, callFlows, basicBlock, call) ep.ub.flows case _ ⇒ @@ -313,16 +280,16 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( Map.empty } // Only process new facts that are not in `oldExitFacts` - allNewExitFacts = mergeMaps( + allNewExitFacts = IFDS.mergeMaps( allNewExitFacts, filterNewInformation(exitFacts, oldExitFacts, project) ) /* - * If new exit facts were discovered for the callee-fact-pair, all call - * sites depending on this pair have to be re-evaluated. oldValue is - * undefined if the callee-fact pair has not been queried before or returned - * a FinalEP. - */ +* If new exit facts were discovered for the callee-fact-pair, all call +* sites depending on this pair have to be re-evaluated. oldValue is +* undefined if the callee-fact pair has not been queried before or returned +* a FinalEP. +*/ if (oldValue.isDefined && oldExitFacts != exitFacts) { reAnalyzeCalls( state.pendingIfdsCallSites(e), @@ -332,7 +299,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( } } } - summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) + summaryEdges = icfg.addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) } } summaryEdges @@ -346,31 +313,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param in The input facts, which hold before the `call`. * @return The changed set of input facts. */ - protected def beforeHandleCall(call: LLVMStatement, in: Set[IFDSFact]): Set[IFDSFact] = in - - /** - * Merges two maps that have sets as values. - * - * @param map1 The first map. - * @param map2 The second map. - * @return A map containing the keys of both maps. Each key is mapped to the union of both maps' - * values. - */ - protected def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { - var result = map1 - for ((key, values) ← map2) { - result.get(key) match { - case Some(resultValues) ⇒ - if (resultValues.size > values.size) - result = result.updated(key, resultValues ++ values) - else - result = result.updated(key, values ++ resultValues) - case None ⇒ - result = result.updated(key, values) - } - } - result - } + protected def beforeHandleCall(call: S, in: Set[IFDSFact]): Set[IFDSFact] = in /** * Analyzes a queue of BasicBlocks. @@ -399,7 +342,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( calleeWithUpdate, calleeWithUpdateFact ) - val allOut = mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) + val allOut = IFDS.mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) for (successor ← icfg.nextNodes(basicBlock)) { @@ -437,9 +380,9 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( state.incomingFacts = state.incomingFacts.updated(successorBlock, subsume(mergedIn, project)) /* - * Only process the successor with new facts. - * It is analyzed at least one time because of the null fact. - */ +* Only process the successor with new facts. +* It is analyzed at least one time because of the null fact. +*/ if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None, None)) } } @@ -462,29 +405,29 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * map contains their handler nodes. */ private def analyzeBasicBlock( - basicBlock: BasicBlock, + basicBlock: Node, in: Set[IFDSFact], - calleeWithUpdateSite: Option[LLVMStatement], - calleeWithUpdate: Option[Function], + calleeWithUpdateSite: Option[S], + calleeWithUpdate: Option[C], calleeWithUpdateFact: Option[IFDSFact] )( implicit state: State - ): Map[BasicBlock, Set[IFDSFact]] = { + ): Map[Node, Set[IFDSFact]] = { /* - * Collects information about a statement. - * - * @param index The statement's index. - * @return A tuple of the following elements: - * statement: The statement at `index`. - * callees: The methods possibly called at this statement, if it contains a call. - * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will - * be returned. - * calleeFact: If `index` equals `calleeWithUpdateIndex`, only - * `calleeWithUpdateFact` will be returned, None otherwise. - */ - def collectInformation(statement: LLVMStatement): (Option[SomeSet[Function]], Option[IFDSFact]) = { +* Collects information about a statement. +* +* @param index The statement's index. +* @return A tuple of the following elements: +* statement: The statement at `index`. +* callees: The methods possibly called at this statement, if it contains a call. +* If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will +* be returned. +* calleeFact: If `index` equals `calleeWithUpdateIndex`, only +* `calleeWithUpdateFact` will be returned, None otherwise. +*/ + def collectInformation(statement: S): (Option[SomeSet[C]], Option[IFDSFact]) = { val calleesO = if (calleeWithUpdateSite.contains(statement)) calleeWithUpdate.map(Set(_)) else icfg.getCalleesIfCallStatement(statement) @@ -496,15 +439,15 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( val last = icfg.lastStatement(basicBlock) var flows: Set[IFDSFact] = in - var statement: LLVMStatement = icfg.firstStatement(basicBlock) + var statement: S = icfg.firstStatement(basicBlock) // Iterate over all statements but the last one, only keeping the resulting DataFlowFacts. while (statement != last) { val (calleesO, calleeFact) = collectInformation(statement) val successor = icfg.nextStatement(statement) flows = if (calleesO.isEmpty) { - numberOfCalls.normalFlow += 1 - sumOfInputfactsForCallbacks += in.size + statistics.numberOfCalls.normalFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size ifdsProblem.normalFlow(statement, successor, flows) } else // Inside a basic block, we only have one successor --> Take the head @@ -514,18 +457,18 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( // Analyze the last statement for each possible successor statement. val (calleesO, callFact) = collectInformation(last) - var result: Map[BasicBlock, Set[IFDSFact]] = + var result: Map[Node, Set[IFDSFact]] = if (calleesO.isEmpty) { - var result: Map[BasicBlock, Set[IFDSFact]] = Map.empty + var result: Map[Node, Set[IFDSFact]] = Map.empty for (node ← icfg.nextNodes(basicBlock)) { - numberOfCalls.normalFlow += 1 - sumOfInputfactsForCallbacks += in.size + statistics.numberOfCalls.normalFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size result += node -> ifdsProblem.normalFlow(statement, icfg.firstStatement(node), flows) } result } else handleCall(basicBlock, statement, calleesO.get, flows, callFact) - .map(entry ⇒ entry._1.basicBlock -> entry._2) + .map(entry ⇒ entry._1.node -> entry._2) // Propagate the null fact. result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) @@ -537,7 +480,7 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * * @param basicBlocks The basic blocks, that will be re-analyzed. */ - private def reAnalyzebasicBlocks(basicBlocks: Set[BasicBlock])(implicit state: State): Unit = { + private def reAnalyzeBasicBlocks(basicBlocks: Set[Node])(implicit state: State): Unit = { val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) process(queue) @@ -551,14 +494,14 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param fact If defined, the `callee` will only be analyzed for this fact. */ private def reAnalyzeCalls( - callSites: Set[LLVMStatement], - callee: Function, + callSites: Set[S], + callee: C, fact: Option[IFDSFact] )(implicit state: State): Unit = { val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty for (callSite ← callSites) { - val block = callSite.basicBlock - queue.enqueue((block, state.incomingFacts(block), Some(callSite), Some(callee), fact)) + val node = callSite.node + queue.enqueue((node, state.incomingFacts(node), Some(callSite), Some(callee), fact)) } process(queue) } @@ -581,13 +524,13 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param entity The method-fact-pair. * @param calleeProperty The property, that was returned for `entity`. * @param callBB The basic block of the call site. - * @param callIndex The index of the call site. + * @param call The call site. */ private def addIfdsDependee( - entity: (Function, IFDSFact), - calleeProperty: EOptionP[(Function, IFDSFact), IFDSProperty[LLVMStatement, IFDSFact]], - callBB: BasicBlock, - call: LLVMStatement + entity: (C, IFDSFact), + calleeProperty: EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]], + callBB: Node, + call: S )(implicit state: State): Unit = { val callSites = state.pendingIfdsCallSites state.pendingIfdsCallSites = callSites.updated( @@ -598,33 +541,10 @@ abstract class AbstractNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( } } -object AbstractNativeIFDSAnalysis { - /** - * When true, the cross product of exit and successor in returnFLow will be optimized. - */ - var OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW: Boolean = true -} - -/** - * A statement that is passed to the concrete analysis. - * - * @param instruction The LLVM instruction. - */ -case class LLVMStatement(instruction: Instruction) { - def function(): Function = instruction.function - def basicBlock(): BasicBlock = instruction.parent -} - -abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalysisScheduler { - - final override type InitializationData = AbstractNativeIFDSAnalysis[IFDSFact] - - def property: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] - +abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] extends FPCFLazyAnalysisScheduler { + final override type InitializationData = IFDSAnalysis[IFDSFact, C, S, Node] + def property: IFDSPropertyMetaInformation[S, IFDSFact] final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) - - override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} /** @@ -634,14 +554,14 @@ abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends override def register( p: SomeProject, ps: PropertyStore, - analysis: AbstractNativeIFDSAnalysis[IFDSFact] + analysis: IFDSAnalysis[IFDSFact, C, S, Node] ): FPCFAnalysis = { ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) analysis } override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { - val ifdsAnalysis = analysis.asInstanceOf[AbstractNativeIFDSAnalysis[IFDSFact]] + val ifdsAnalysis = analysis.asInstanceOf[IFDSAnalysis[IFDSFact, C, S, Node]] for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } @@ -652,4 +572,4 @@ abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends ps: PropertyStore, analysis: FPCFAnalysis ): Unit = {} -} +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index f754a1e8e0..1d43c6525c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -2,7 +2,7 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.ll.llvm.{BasicBlock, Function, Instruction, Terminator} -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact} class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Function, LLVMStatement, BasicBlock] { /** @@ -75,4 +75,111 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun * contain a call. */ override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[Function]] = ??? + + /** + * Collects the output facts at the predecessors of the normal and abnormal return node. + */ + override protected def collectResult(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] = { + Map.empty //state.outgoingFacts + } + + /** + * Calls callFlow. + */ + override protected def callToStartFacts( + call: LLVMStatement, + callee: Function, + in: Set[IFDSFact] + )(implicit + state: State, + ifdsProblem: Problem, + statistics: Statistics + ): Set[IFDSFact] = { + statistics.numberOfCalls.callFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + ifdsProblem.callFlow(call, callee, in, state.source) + } + + /** + * Combines each normal exit node with each normal successor and each abnormal exit statement + * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. + */ + override protected def addExitToReturnFacts( + summaryEdges: Map[LLVMStatement, Set[IFDSFact]], + successors: Set[LLVMStatement], call: LLVMStatement, + callee: Function, + exitFacts: Map[LLVMStatement, Set[IFDSFact]] + )(implicit state: State, ifdsProblem: Problem): Map[LLVMStatement, Set[IFDSFact]] = { + // First process for normal returns, then abnormal returns. + var result = summaryEdges + if (ifdsProblem.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { + val successors = nextStatementsWithNode(call) + for { + successor ← successors + exitStatement ← exitFacts.keys + if (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && + (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || + (successor._2.isCatchNode || successor._2.isAbnormalReturnExitNode) && + (exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID) + } result = addSummaryEdge(result, call, exitStatement, successor._1, callee, exitFacts) + } else { + val successors = nextStatements(call) + for { + successor ← successors + exitStatement ← exitFacts.keys + } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) + } + result + } + + /** + * Like nextStatements, but maps each successor statement to the corresponding successor node. + * When determining the successor node, catch nodes are not skipped. + */ + private def nextStatementsWithNode(statement: LLVMStatement)(implicit state: State): Map[LLVMStatement, CFGNode] = { + val index = statement.index + val basicBlock = statement.node.asBasicBlock + if (index == basicBlock.endPC) + basicBlock.successors.iterator + .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap + else { + val nextIndex = index + 1 + Map(LLVMStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + statement.code, statement.cfg) → basicBlock) + } + } + + /** + * Adds a summary edge for a call to a map representing summary edges. + * + * @param summaryEdges The current map representing the summary edges. + * Maps from successor statements to facts, which hold at their beginning. + * @param call The call, calling the `callee`. + * @param exitStatement The exit statement for the new summary edge. + * @param successor The successor statement of the call for the new summary edge. + * @param callee The callee, called by `call`. + * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly + * found exit facts. + * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. + */ + private def addSummaryEdge(summaryEdges: Map[LLVMStatement, Set[IFDSFact]], call: LLVMStatement, + exitStatement: LLVMStatement, successor: LLVMStatement, + callee: Function, + allNewExitFacts: Map[LLVMStatement, Set[IFDSFact]])( + implicit + statistics: Statistics, + ifdsProblem: Problem + ): Map[LLVMStatement, Set[IFDSFact]] = { + val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) + statistics.numberOfCalls.returnFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) + val newFacts = + if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { + val summaryForSuccessor = summaryEdges(successor) + if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned + else returned ++ summaryForSuccessor + } else returned + summaryEdges.updated(successor, newFacts) + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala new file mode 100644 index 0000000000..76e1fa9d9b --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala @@ -0,0 +1,37 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ + +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.ll.LLVMProjectKey +import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem} +import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation + +/** + * + * @param ifdsProblem + * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. + * @tparam IFDSFact + */ +class NativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( + ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement], + icfg: ICFG[IFDSFact, Function, LLVMStatement, BasicBlock], + propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] +) + extends IFDSAnalysis[IFDSFact, Function, LLVMStatement, BasicBlock](ifdsProblem, icfg, propertyKey) + +/** + * A statement that is passed to the concrete analysis. + * + * @param instruction The LLVM instruction. + */ +case class LLVMStatement(instruction: Instruction) extends Statement[BasicBlock] { + def function(): Function = instruction.function + def basicBlock(): BasicBlock = instruction.parent + override def node(): BasicBlock = basicBlock +} + +abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysisScheduler[IFDSFact, Function, LLVMStatement, BasicBlock] { + override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) +} From 0b40422ef8e46babdbcbd8dee01f3eb097bf3fdf Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 16 Mar 2022 15:04:43 +0100 Subject: [PATCH 22/67] WIP --- .../opalj/ll/fpcf/analyses/ifds/ICFG.scala | 45 +--- .../ll/fpcf/analyses/ifds/IFDSAnalysis.scala | 223 +++++++++++------- .../analyses/ifds/NativeForwardICFG.scala | 114 +-------- .../analyses/ifds/AbstractIFDSAnalysis.scala | 4 +- .../tac/fpcf/analyses/ifds/IFDSProblem.scala | 4 +- .../analyses/ifds/VariableTypeProblem.scala | 2 +- .../ifds/taint/BackwardTaintProblem.scala | 2 +- .../ifds/taint/ForwardTaintProblem.scala | 4 +- 8 files changed, 162 insertions(+), 236 deletions(-) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala index d0f3451154..e75cf8bf05 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala @@ -4,9 +4,6 @@ import scala.collection.{Set ⇒ SomeSet} import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem} abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] { - type State = IFDSState[IFDSFact, C, S, Node] - type Problem = IFDSProblem[IFDSFact, C, S] - /** * Determines the basic blocks, at which the analysis starts. * @@ -73,44 +70,10 @@ abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[No def getCalleesIfCallStatement(statement: S): Option[SomeSet[C]] /** - * Collects the facts valid at all exit nodes based on the current results. - * - * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at - * the exit node under the assumption that the predecessor was executed before. - */ - def collectResult(implicit state: State): Map[S, Set[IFDSFact]] - - /** - * Determines the facts, for which a `callee` is analyzed. + * Determines whether the statement is an exit statement. * - * @param call The call, which calls `callee`. - * @param callee The method, which is called by `call`. - * @param in The facts, which hold before the `call`. - * @return The facts, for which `callee` will be analyzed. - */ - def callToStartFacts(call: S, callee: C, in: Set[IFDSFact])( - implicit - state: State, - ifdsProblem: Problem, - statistics: Statistics - ): Set[IFDSFact] - - /** - * Collects the exit facts of a `callee` and adds them to the `summaryEdges`. - * - * @param summaryEdges The current summary edges. They map successor statements of the `call` - * to facts, which hold before they are executed. - * @param successors The successor of `call`, which is considered. - * @param call The statement, which calls `callee`. - * @param callee The method, called by `call`. - * @param exitFacts Maps exit statements of the `callee` to the facts, which hold after them. - * @return The summary edges plus the exit to return facts for `callee` and `successor`. + * @param statement The source statement. + * @return Whether the statement flow may exit its callable (function/method) */ - def addExitToReturnFacts( - summaryEdges: Map[S, Set[IFDSFact]], - successors: Set[S], - call: S, - callee: C, - exitFacts: Map[S, Set[IFDSFact]] - )(implicit state: State, ifdsProblem: Problem): Map[S, Set[IFDSFact]] + def isExitStatement(statement: S): Boolean } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala index b8d6f66a02..6d7cb1fb05 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala @@ -29,7 +29,7 @@ abstract class Statement[Node] { * @param incomingFacts Maps each basic block to the data flow facts valid at its first * statement. * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at - * the beginning of the successor. + * the beginning of the successor. For exit statements the successor is None */ protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( val source: (C, IFDSFact), @@ -37,10 +37,11 @@ protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statem var pendingIfdsDependees: Map[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] = Map.empty, var pendingCgCallSites: Set[Node] = Set.empty, var incomingFacts: Map[Node, Set[IFDSFact]] = Map.empty, - var outgoingFacts: Map[Node, Map[Node, Set[IFDSFact]]] = Map.empty + var outgoingFacts: Map[Node, Map[Option[Node], Set[IFDSFact]]] = Map.empty ) protected class Statistics { + /** * Counts, how many times the abstract methods were called. */ @@ -63,8 +64,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod val ifdsProblem: IFDSProblem[IFDSFact, C, S], val icfg: ICFG[IFDSFact, C, S, Node], val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact] -) - extends FPCFAnalysis +) extends FPCFAnalysis with Subsumable[S, IFDSFact] { type State = IFDSState[IFDSFact, C, S, Node] override val project: SomeProject = ifdsProblem.project @@ -79,7 +79,8 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod * @param result Maps each exit statement to the facts, which hold after the exit statement. * @return An IFDSProperty containing the `result`. */ - protected def createPropertyValue(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] = propertyKey.create(result) + protected def createPropertyValue(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] = + propertyKey.create(result) /** * Performs an IFDS analysis for a method-fact-pair. @@ -115,7 +116,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod * */ protected def createResult()(implicit state: State): ProperPropertyComputationResult = { - val propertyValue = createPropertyValue(icfg.collectResult) + val propertyValue = createPropertyValue(collectResult) val dependees = state.pendingIfdsDependees.values if (dependees.isEmpty) Result(state.source, propertyValue) else InterimResult.forUB(state.source, propertyValue, dependees.toSet, propertyUpdate) @@ -160,19 +161,6 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod createResult() } - /** - * Called, when some new information is found at the last node of the method. - * This method can be overwritten by a subclass to perform additional actions. - * - * @param nextIn The input facts, which were found. - * @param oldIn The input facts, which were already known, if present. - */ - protected def foundNewInformationForLastNode( - nextIn: Set[IFDSFact], - oldIn: Option[Set[IFDSFact]], - state: State - ): Unit = {} - /** * Processes a statement with a call. * @@ -196,22 +184,21 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod state: State ): Map[S, Set[IFDSFact]] = { val successors = icfg.nextStatements(call) - val inputFacts = beforeHandleCall(call, in) // Facts valid at the start of each successor var summaryEdges: Map[S, Set[IFDSFact]] = Map.empty /* -* If calleeWithUpdateFact is present, this means that the basic block already has been -* analyzed with the `inputFacts`. -*/ + * If calleeWithUpdateFact is present, this means that the basic block already has been + * analyzed with the `inputFacts`. + */ if (calleeWithUpdateFact.isEmpty) for (successor ← successors) { statistics.numberOfCalls.callToReturnFlow += 1 statistics.sumOfInputfactsForCallbacks += in.size summaryEdges += successor -> propagateNullFact( - inputFacts, - ifdsProblem.callToReturnFlow(call, successor, inputFacts, state.source) + in, + ifdsProblem.callToReturnFlow(call, successor, in, state.source) ) } @@ -227,17 +214,17 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod val callToStart = if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) else { - propagateNullFact(inputFacts, icfg.callToStartFacts(call, callee, inputFacts)) + propagateNullFact(in, callToStartFacts(call, callee, in)) } var allNewExitFacts: Map[S, Set[IFDSFact]] = Map.empty // Collect exit facts for each input fact separately for (fact ← callToStart) { /* -* If this is a recursive call with the same input facts, we assume that the -* call only produces the facts that are already known. The call site is added to -* `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts -* become known for the input fact. -*/ + * If this is a recursive call with the same input facts, we assume that the + * call only produces the facts that are already known. The call site is added to + * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts + * become known for the input fact. + */ if ((callee eq state.source._1) && fact == state.source._2) { val newDependee = if (state.pendingIfdsCallSites.contains(state.source)) @@ -246,7 +233,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod else Set((basicBlock, call)) state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(state.source, newDependee) - allNewExitFacts = IFDS.mergeMaps(allNewExitFacts, icfg.collectResult) + allNewExitFacts = IFDS.mergeMaps(allNewExitFacts, collectResult) } else { val e = (callee, fact) val callFlows = propertyStore(e, propertyKey.key) @@ -268,11 +255,11 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod ep.p.flows case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ /* -* Add the call site to `pendingIfdsCallSites` and -* `pendingIfdsDependees` and continue with the facts in the interim -* result for now. When the analysis for the callee finishes, the -* analysis for this call site will be triggered again. -*/ + * Add the call site to `pendingIfdsCallSites` and + * `pendingIfdsDependees` and continue with the facts in the interim + * result for now. When the analysis for the callee finishes, the + * analysis for this call site will be triggered again. + */ addIfdsDependee(e, callFlows, basicBlock, call) ep.ub.flows case _ ⇒ @@ -285,11 +272,11 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod filterNewInformation(exitFacts, oldExitFacts, project) ) /* -* If new exit facts were discovered for the callee-fact-pair, all call -* sites depending on this pair have to be re-evaluated. oldValue is -* undefined if the callee-fact pair has not been queried before or returned -* a FinalEP. -*/ + * If new exit facts were discovered for the callee-fact-pair, all call + * sites depending on this pair have to be re-evaluated. oldValue is + * undefined if the callee-fact pair has not been queried before or returned + * a FinalEP. + */ if (oldValue.isDefined && oldExitFacts != exitFacts) { reAnalyzeCalls( state.pendingIfdsCallSites(e), @@ -299,21 +286,60 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod } } } - summaryEdges = icfg.addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) + summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) } } summaryEdges } /** - * This method is called at the beginning of handleCall. - * A subclass can overwrite this method, to change the input facts of the call. + * Collects the facts valid at all exit nodes based on the current results. * - * @param call The call statement. - * @param in The input facts, which hold before the `call`. - * @return The changed set of input facts. + * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at + * the exit node under the assumption that the predecessor was executed before. + */ + protected def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { + var result = Map.empty[S, Set[IFDSFact]] + state.outgoingFacts.foreach( + blockFacts ⇒ + blockFacts._2.get(None) match { + case Some(facts) ⇒ result += icfg.lastStatement(blockFacts._1) -> facts + } + ) + result + } + + /** + * Calls callFlow. + */ + protected def callToStartFacts(call: S, callee: C, in: Set[IFDSFact])( + implicit + state: State + ): Set[IFDSFact] = { + statistics.numberOfCalls.callFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + ifdsProblem.callFlow(call, callee, in, state.source) + } + + /** + * Combines each normal exit node with each normal successor and each abnormal exit statement + * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. */ - protected def beforeHandleCall(call: S, in: Set[IFDSFact]): Set[IFDSFact] = in + protected def addExitToReturnFacts( + summaryEdges: Map[S, Set[IFDSFact]], + successors: Set[S], + call: S, + callee: C, + exitFacts: Map[S, Set[IFDSFact]] + )(implicit state: State): Map[S, Set[IFDSFact]] = { + // First process for normal returns, then abnormal returns. + var result = summaryEdges + for { + successor ← successors + exitStatement ← exitFacts.keys + } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) + result + } /** * Analyzes a queue of BasicBlocks. @@ -348,20 +374,15 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod for (successor ← icfg.nextNodes(basicBlock)) { if (icfg.isLastNode(successor)) { // Re-analyze recursive call sites with the same input fact. - val nextOutSuccessors = nextOut.get(successor) + val nextOutSuccessors = nextOut.get(Some(successor)) if (nextOutSuccessors.isDefined && nextOutSuccessors.get.nonEmpty) { - val oldOutSuccessors = oldOut.get(successor) + val oldOutSuccessors = oldOut.get(Some(successor)) if (oldOutSuccessors.isEmpty || containsNewInformation( nextOutSuccessors.get, oldOutSuccessors.get, project )) { val source = state.source - foundNewInformationForLastNode( - nextOutSuccessors.get, - oldOutSuccessors, - state - ) reAnalyzeCalls( state.pendingIfdsCallSites(source), source._1, @@ -371,7 +392,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod } } else { val successorBlock = successor - val nextIn = nextOut.getOrElse(successorBlock, Set.empty) + val nextIn = nextOut.getOrElse(Some(successorBlock), Set.empty) val oldIn = state.incomingFacts.getOrElse(successorBlock, Set.empty) val newIn = notSubsumedBy(nextIn, oldIn, project) val mergedIn = @@ -380,9 +401,9 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod state.incomingFacts = state.incomingFacts.updated(successorBlock, subsume(mergedIn, project)) /* -* Only process the successor with new facts. -* It is analyzed at least one time because of the null fact. -*/ + * Only process the successor with new facts. + * It is analyzed at least one time because of the null fact. + */ if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None, None)) } } @@ -413,20 +434,20 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod )( implicit state: State - ): Map[Node, Set[IFDSFact]] = { + ): Map[Option[Node], Set[IFDSFact]] = { /* -* Collects information about a statement. -* -* @param index The statement's index. -* @return A tuple of the following elements: -* statement: The statement at `index`. -* callees: The methods possibly called at this statement, if it contains a call. -* If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will -* be returned. -* calleeFact: If `index` equals `calleeWithUpdateIndex`, only -* `calleeWithUpdateFact` will be returned, None otherwise. -*/ + * Collects information about a statement. + * + * @param index The statement's index. + * @return A tuple of the following elements: + * statement: The statement at `index`. + * callees: The methods possibly called at this statement, if it contains a call. + * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will + * be returned. + * calleeFact: If `index` equals `calleeWithUpdateIndex`, only + * `calleeWithUpdateFact` will be returned, None otherwise. + */ def collectInformation(statement: S): (Option[SomeSet[C]], Option[IFDSFact]) = { val calleesO = if (calleeWithUpdateSite.contains(statement)) calleeWithUpdate.map(Set(_)) @@ -448,7 +469,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod flows = if (calleesO.isEmpty) { statistics.numberOfCalls.normalFlow += 1 statistics.sumOfInputfactsForCallbacks += in.size - ifdsProblem.normalFlow(statement, successor, flows) + ifdsProblem.normalFlow(statement, Some(successor), flows) } else // Inside a basic block, we only have one successor --> Take the head handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head @@ -457,18 +478,27 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod // Analyze the last statement for each possible successor statement. val (calleesO, callFact) = collectInformation(last) - var result: Map[Node, Set[IFDSFact]] = + var result: Map[Option[Node], Set[IFDSFact]] = if (calleesO.isEmpty) { - var result: Map[Node, Set[IFDSFact]] = Map.empty + var result: Map[Option[Node], Set[IFDSFact]] = Map.empty for (node ← icfg.nextNodes(basicBlock)) { statistics.numberOfCalls.normalFlow += 1 statistics.sumOfInputfactsForCallbacks += in.size - result += node -> ifdsProblem.normalFlow(statement, icfg.firstStatement(node), flows) + result += Some(node) -> ifdsProblem.normalFlow( + statement, + Some(icfg.firstStatement(node)), + flows + ) + } + if (icfg.isExitStatement(last)) { + statistics.numberOfCalls.normalFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + result += None -> ifdsProblem.normalFlow(statement, None, flows) } result } else handleCall(basicBlock, statement, calleesO.get, flows, callFact) - .map(entry ⇒ entry._1.node -> entry._2) + .map(entry ⇒ Some(entry._1.node) -> entry._2) // Propagate the null fact. result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) @@ -539,9 +569,44 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod ) state.pendingIfdsDependees += entity -> calleeProperty } + + /** + * Adds a summary edge for a call to a map representing summary edges. + * + * @param summaryEdges The current map representing the summary edges. + * Maps from successor statements to facts, which hold at their beginning. + * @param call The call, calling the `callee`. + * @param exitStatement The exit statement for the new summary edge. + * @param successor The successor statement of the call for the new summary edge. + * @param callee The callee, called by `call`. + * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly + * found exit facts. + * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. + */ + private def addSummaryEdge( + summaryEdges: Map[S, Set[IFDSFact]], + call: S, + exitStatement: S, + successor: S, + callee: C, + allNewExitFacts: Map[S, Set[IFDSFact]] + ): Map[S, Set[IFDSFact]] = { + val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) + statistics.numberOfCalls.returnFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) + val newFacts = + if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { + val summaryForSuccessor = summaryEdges(successor) + if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned + else returned ++ summaryForSuccessor + } else returned + summaryEdges.updated(successor, newFacts) + } } -abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] extends FPCFLazyAnalysisScheduler { +abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] + extends FPCFLazyAnalysisScheduler { final override type InitializationData = IFDSAnalysis[IFDSFact, C, S, Node] def property: IFDSPropertyMetaInformation[S, IFDSFact] final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) @@ -572,4 +637,4 @@ abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, ps: PropertyStore, analysis: FPCFAnalysis ): Unit = {} -} \ No newline at end of file +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index 1d43c6525c..fcee5b9b5f 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -1,8 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.ll.llvm.{BasicBlock, Function, Instruction, Terminator} -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact} +import org.opalj.ll.llvm.{BasicBlock, Function, Instruction, Ret, Terminator} +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Function, LLVMStatement, BasicBlock] { /** @@ -74,112 +74,10 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun * @return All callables possibly called at the statement or None, if the statement does not * contain a call. */ - override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[Function]] = ??? + override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[Function]] = None //TODO - /** - * Collects the output facts at the predecessors of the normal and abnormal return node. - */ - override protected def collectResult(implicit state: State): Map[LLVMStatement, Set[IFDSFact]] = { - Map.empty //state.outgoingFacts - } - - /** - * Calls callFlow. - */ - override protected def callToStartFacts( - call: LLVMStatement, - callee: Function, - in: Set[IFDSFact] - )(implicit - state: State, - ifdsProblem: Problem, - statistics: Statistics - ): Set[IFDSFact] = { - statistics.numberOfCalls.callFlow += 1 - statistics.sumOfInputfactsForCallbacks += in.size - ifdsProblem.callFlow(call, callee, in, state.source) - } - - /** - * Combines each normal exit node with each normal successor and each abnormal exit statement - * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. - */ - override protected def addExitToReturnFacts( - summaryEdges: Map[LLVMStatement, Set[IFDSFact]], - successors: Set[LLVMStatement], call: LLVMStatement, - callee: Function, - exitFacts: Map[LLVMStatement, Set[IFDSFact]] - )(implicit state: State, ifdsProblem: Problem): Map[LLVMStatement, Set[IFDSFact]] = { - // First process for normal returns, then abnormal returns. - var result = summaryEdges - if (ifdsProblem.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { - val successors = nextStatementsWithNode(call) - for { - successor ← successors - exitStatement ← exitFacts.keys - if (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && - (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || - (successor._2.isCatchNode || successor._2.isAbnormalReturnExitNode) && - (exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID) - } result = addSummaryEdge(result, call, exitStatement, successor._1, callee, exitFacts) - } else { - val successors = nextStatements(call) - for { - successor ← successors - exitStatement ← exitFacts.keys - } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) - } - result - } - - /** - * Like nextStatements, but maps each successor statement to the corresponding successor node. - * When determining the successor node, catch nodes are not skipped. - */ - private def nextStatementsWithNode(statement: LLVMStatement)(implicit state: State): Map[LLVMStatement, CFGNode] = { - val index = statement.index - val basicBlock = statement.node.asBasicBlock - if (index == basicBlock.endPC) - basicBlock.successors.iterator - .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap - else { - val nextIndex = index + 1 - Map(LLVMStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, - statement.code, statement.cfg) → basicBlock) - } - } - - /** - * Adds a summary edge for a call to a map representing summary edges. - * - * @param summaryEdges The current map representing the summary edges. - * Maps from successor statements to facts, which hold at their beginning. - * @param call The call, calling the `callee`. - * @param exitStatement The exit statement for the new summary edge. - * @param successor The successor statement of the call for the new summary edge. - * @param callee The callee, called by `call`. - * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly - * found exit facts. - * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. - */ - private def addSummaryEdge(summaryEdges: Map[LLVMStatement, Set[IFDSFact]], call: LLVMStatement, - exitStatement: LLVMStatement, successor: LLVMStatement, - callee: Function, - allNewExitFacts: Map[LLVMStatement, Set[IFDSFact]])( - implicit - statistics: Statistics, - ifdsProblem: Problem - ): Map[LLVMStatement, Set[IFDSFact]] = { - val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) - statistics.numberOfCalls.returnFlow += 1 - statistics.sumOfInputfactsForCallbacks += in.size - val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) - val newFacts = - if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { - val summaryForSuccessor = summaryEdges(successor) - if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned - else returned ++ summaryForSuccessor - } else returned - summaryEdges.updated(successor, newFacts) + override def isExitStatement(statement: LLVMStatement): Boolean = statement.instruction match { + case Ret(_) ⇒ true + case _ ⇒ false } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index 73a103c091..8992484166 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -667,7 +667,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble JavaStatement(state.method, basicBlock, state.code(next), next, state.code, state.cfg) numberOfCalls.normalFlow += 1 sumOfInputfactsForCallbacks += in.size - ifdsProblem.normalFlow(statement, successor, flows) + ifdsProblem.normalFlow(statement, Some(successor), flows) } else // Inside a basic block, we only have one successor --> Take the head handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head @@ -682,7 +682,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble for (node ← nextNodes(basicBlock)) { numberOfCalls.normalFlow += 1 sumOfInputfactsForCallbacks += in.size - result += node -> ifdsProblem.normalFlow(statement, firstStatement(node), flows) + result += node -> ifdsProblem.normalFlow(statement, Some(firstStatement(node)), flows) } result } else diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala index 5cbf3de3d9..5938d0ff98 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala @@ -38,7 +38,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va * * @param statement The analyzed statement. * @param successor The successor of the analyzed `statement`, for which the data flow shall be - * computed. + * computed. It is not present for exit statements. * @param in The facts, which hold before the execution of the `statement`. * @return The facts, which hold after the execution of `statement` under the assumption * that the facts in `in` held before `statement` and `successor` will be @@ -46,7 +46,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va */ def normalFlow( statement: Statement, - successor: Statement, + successor: Option[Statement], in: Set[IFDSFact] ): Set[IFDSFact] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala index d7be114623..c2c1c60a78 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala @@ -80,7 +80,7 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] */ override def normalFlow( statement: JavaStatement, - successor: JavaStatement, + successor: Option[JavaStatement], in: Set[VTAFact] ): Set[VTAFact] = { val stmt = statement.stmt diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala index 525ced6da4..4949aaf116 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala @@ -13,7 +13,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF /** * If a tainted variable gets assigned a value, this value will be tainted. */ - override def normalFlow(statement: JavaStatement, successor: JavaStatement, + override def normalFlow(statement: JavaStatement, successor: Option[JavaStatement], in: Set[Fact]): Set[Fact] = { val stmt = statement.stmt stmt.astID match { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index ec2b1d5d04..0130c02e7a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -13,7 +13,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem /** * If a variable gets assigned a tainted value, the variable will be tainted. */ - override def normalFlow(statement: JavaStatement, successor: JavaStatement, + override def normalFlow(statement: JavaStatement, successor: Option[JavaStatement], in: Set[Fact]): Set[Fact] = statement.stmt.astID match { case Assignment.ASTID ⇒ @@ -61,7 +61,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * edges will be created. */ override def callFlow(call: JavaStatement, callee: DeclaredMethod, - in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = { + in: Set[Fact], a: (DeclaredMethod, Fact)): Set[Fact] = { val callObject = asCall(call.stmt) val allParams = callObject.allParams var facts = Set.empty[Fact] From 54bb8070c8dd5627e796a5aafbae1f7685baf90c Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 16 Mar 2022 16:18:15 +0100 Subject: [PATCH 23/67] WIP --- .../opalj/ll/fpcf/analyses/ifds/ICFG.scala | 3 ++- .../ll/fpcf/analyses/ifds/IFDSAnalysis.scala | 27 +++++++++---------- .../analyses/ifds/NativeIFDSAnalysis.scala | 2 +- .../ifds/taint/JavaForwardTaintAnalysis.scala | 2 +- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala index e75cf8bf05..5499e2405b 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala @@ -1,7 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact + import scala.collection.{Set ⇒ SomeSet} -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem} abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] { /** diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala index 6d7cb1fb05..43ed27f20c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala @@ -33,11 +33,11 @@ abstract class Statement[Node] { */ protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( val source: (C, IFDSFact), - var pendingIfdsCallSites: Map[(C, IFDSFact), Set[S]] = Map.empty, - var pendingIfdsDependees: Map[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] = Map.empty, - var pendingCgCallSites: Set[Node] = Set.empty, - var incomingFacts: Map[Node, Set[IFDSFact]] = Map.empty, - var outgoingFacts: Map[Node, Map[Option[Node], Set[IFDSFact]]] = Map.empty + var pendingIfdsCallSites: Map[(C, IFDSFact), Set[S]] = Map.empty[(C, IFDSFact), Set[S]], + var pendingIfdsDependees: Map[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] = Map.empty[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]], + var pendingCgCallSites: Set[Node] = Set.empty[Node], + var incomingFacts: Map[Node, Set[IFDSFact]] = Map.empty[Node, Set[IFDSFact]], + var outgoingFacts: Map[Node, Map[Option[Node], Set[IFDSFact]]] = Map.empty[Node, Map[Option[Node], Set[IFDSFact]]] ) protected class Statistics { @@ -95,7 +95,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod // Start processing at the start of the cfg with the given source fact implicit val state: State = - new State(entity, Map(entity -> Set.empty), Map()) + new State(entity, Map(entity -> Set.empty)) val queue = mutable.Queue .empty[QueueEntry] icfg.startNodes(sourceFact, function).foreach { start ⇒ @@ -151,10 +151,10 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - case FinalEP(_: C, _: Callees) ⇒ + case FinalEP(_: C @unchecked, _: Callees) ⇒ reAnalyzeBasicBlocks(state.pendingCgCallSites) - case InterimEUBP(_: C, _: Callees) ⇒ + case InterimEUBP(_: C @unchecked, _: Callees) ⇒ reAnalyzeBasicBlocks(state.pendingCgCallSites) } @@ -228,9 +228,8 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod if ((callee eq state.source._1) && fact == state.source._2) { val newDependee = if (state.pendingIfdsCallSites.contains(state.source)) - state.pendingIfdsCallSites(state.source) + - ((basicBlock, call)) - else Set((basicBlock, call)) + state.pendingIfdsCallSites(state.source) + call + else Set(call) state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(state.source, newDependee) allNewExitFacts = IFDS.mergeMaps(allNewExitFacts, collectResult) @@ -248,7 +247,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod if (state.pendingIfdsCallSites.contains(e) && state.pendingIfdsCallSites(e).nonEmpty) { val newDependee = - state.pendingIfdsCallSites(e) - ((basicBlock, call)) + state.pendingIfdsCallSites(e) - call state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) } state.pendingIfdsDependees -= e @@ -331,7 +330,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod call: S, callee: C, exitFacts: Map[S, Set[IFDSFact]] - )(implicit state: State): Map[S, Set[IFDSFact]] = { + ): Map[S, Set[IFDSFact]] = { // First process for normal returns, then abnormal returns. var result = summaryEdges for { @@ -565,7 +564,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod val callSites = state.pendingIfdsCallSites state.pendingIfdsCallSites = callSites.updated( entity, - callSites.getOrElse(entity, Set.empty) + ((callBB, call)) + callSites.getOrElse(entity, Set.empty) + call ) state.pendingIfdsDependees += entity -> calleeProperty } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala index 76e1fa9d9b..6dab11405a 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala @@ -19,7 +19,7 @@ class NativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( icfg: ICFG[IFDSFact, Function, LLVMStatement, BasicBlock], propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] ) - extends IFDSAnalysis[IFDSFact, Function, LLVMStatement, BasicBlock](ifdsProblem, icfg, propertyKey) + extends IFDSAnalysis[IFDSFact, Function, LLVMStatement, BasicBlock]()(ifdsProblem, icfg, propertyKey) /** * A statement that is passed to the concrete analysis. diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index c31bfbe830..4a67393593 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -76,5 +76,5 @@ object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact] { override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis()(p) override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq(LLVMProjectKey) - override val uses: Set[PropertyBounds] = super.uses ++ PropertyBounds.ub(NativeTaint) + override val uses: Set[PropertyBounds] = super.uses ++ Set(PropertyBounds.ub(NativeTaint)) } From 76a1c2792ef8941f455c04c1715fbb73931be495 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 17 Mar 2022 20:04:43 +0100 Subject: [PATCH 24/67] WIP (compiles) --- .../HerosBackwardClassForNameAnalysis.scala | 10 +--- .../HerosForwardClassForNameAnalysis.scala | 18 +++--- ...rdClassForNameTaintAnalysisScheduler.scala | 12 ++-- ...rdClassForNameTaintAnalysisScheduler.scala | 15 +++-- .../taint/BackwardFlowPathMatcher.scala | 17 ++---- .../taint/ForwardFlowPathMatcher.scala | 10 +--- ...MultilingualForwardTaintAnalysisTest.scala | 2 +- .../ll/fpcf/analyses/ifds/Callable.scala | 9 +-- .../ll/fpcf/analyses/ifds/IFDSAnalysis.scala | 3 +- .../ifds/taint/JavaForwardTaintAnalysis.scala | 14 ++--- .../taint/NativeForwardTaintAnalysis.scala | 25 ++++----- .../taint/NativeForwardTaintProblem.scala | 28 +++++----- .../ifds/taint/NativeTaintProblem.scala | 55 +++++++++++++++++++ .../org/opalj/ll/fpcf/properties/Taint.scala | 10 ++-- .../scala/org/opalj/ll/llvm/Instruction.scala | 4 +- .../tac/fpcf/analyses/ifds/Callable.scala | 21 +++++++ .../tac/fpcf/analyses/ifds/IFDSProblem.scala | 24 ++++---- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 6 +- .../ifds/taint/BackwardTaintProblem.scala | 12 ++-- .../ifds/taint/ForwardTaintProblem.scala | 14 ++--- .../analyses/ifds/taint/TaintProblem.scala | 10 ++-- ...ackwardTaintAnalysisFixtureScheduler.scala | 14 ++--- ...ForwardTaintAnalysisFixtureScheduler.scala | 10 ++-- 23 files changed, 199 insertions(+), 144 deletions(-) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala index 47703d67b7..2145578171 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -3,20 +3,17 @@ package org.opalj.tac.fpcf.analyses.heros.analyses.taint import java.io.File import java.util - import scala.collection.JavaConverters._ - import heros.FlowFunction import heros.FlowFunctions import heros.flowfunc.Identity - import org.opalj.util.Milliseconds import org.opalj.br.analyses.SomeProject import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.heros.cfg.OpalBackwardICFG import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, JavaStatement} import org.opalj.tac.Assignment import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement import org.opalj.tac.fpcf.analyses.ifds.taint.Variable @@ -41,7 +38,6 @@ import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis /** * An implementation of the BackwardClassForNameAnalysis in the Heros framework. @@ -118,7 +114,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) case InstanceField(index, _, _) if index < 0 ⇒ true case _ ⇒ false })) { - val fact = FlowFact(Seq(method)) + val fact = FlowFact(Seq(JavaMethod(method))) result += fact flowFacts = flowFacts.updated(method, flowFacts.getOrElse(method, Set.empty[FlowFact]) + fact) } @@ -270,7 +266,7 @@ class HerosBackwardClassForNameAnalysisRunner extends HerosAnalysisRunner[Fact, for { method ← analysis.flowFacts.keys fact ← analysis.flowFacts(method) - } println(s"flow: "+fact.flow.map(_.toJava).mkString(", ")) + } println(s"flow: "+fact.flow.map(_.signature).mkString(", ")) println(s"Time: $analysisTime") } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index a948f123e7..f7f86c7aa2 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -4,15 +4,12 @@ package org.opalj.tac.fpcf.analyses.heros.analyses.taint import java.io.File import java.util import java.util.Collections - import scala.collection.JavaConverters._ - import heros.FlowFunction import heros.FlowFunctions import heros.TwoElementSet import heros.flowfunc.Identity import heros.flowfunc.KillAll - import org.opalj.util.Milliseconds import org.opalj.fpcf.FinalEP import org.opalj.fpcf.PropertyStore @@ -33,7 +30,7 @@ import org.opalj.tac.PutField import org.opalj.tac.PutStatic import org.opalj.tac.ReturnValue import org.opalj.tac.Var -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, JavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement import org.opalj.tac.fpcf.analyses.ifds.taint.Fact import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact @@ -51,7 +48,6 @@ import org.opalj.tac.PrimitiveTypecastExpr import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V /** @@ -76,7 +72,7 @@ class HerosForwardClassForNameAnalysis( val classHierarchy: ClassHierarchy = p.classHierarchy - var flowFacts = Map.empty[Method, Set[FlowFact[Method]]] + var flowFacts = Map.empty[Method, Set[FlowFact]] override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { @@ -304,11 +300,11 @@ class HerosForwardClassForNameAnalysis( case sf: StaticField ⇒ Set(sf) case FlowFact(flow) if !flow.contains(stmt.method) ⇒ - val flowFact = FlowFact(stmt.method +: flow) + val flowFact = FlowFact(JavaMethod(stmt.method) +: flow) if (initialMethods.contains(stmt.method)) flowFacts = flowFacts.updated( stmt.method, - flowFacts.getOrElse(stmt.method, Set.empty[FlowFact[Method]]) + flowFact + flowFacts.getOrElse(stmt.method, Set.empty[FlowFact]) + flowFact ) Set(flowFact) case _ ⇒ @@ -331,11 +327,11 @@ class HerosForwardClassForNameAnalysis( val flowFact = if (isClassForName(callee) && source == Variable(-2)) { - val flowFact = FlowFact(Seq(stmt.method)) + val flowFact = FlowFact(Seq(JavaMethod(stmt.method))) if (initialMethods.contains(stmt.method)) flowFacts = flowFacts.updated( stmt.method, - flowFacts.getOrElse(stmt.method, Set.empty[FlowFact[Method]]) + flowFact + flowFacts.getOrElse(stmt.method, Set.empty[FlowFact]) + flowFact ) Some(flowFact) } else None @@ -408,7 +404,7 @@ class HerosForwardClassForNameAnalysisRunner for { method ← analysis.flowFacts.keys fact ← analysis.flowFacts(method) - } println(s"flow: "+fact.flow.map(_.toJava).mkString(", ")) + } println(s"flow: "+fact.flow.map(_.signature).mkString(", ")) println(s"Time: $analysisTime") } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index 73610c3ed8..e5b58f91c0 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.FinalEP import org.opalj.br.{DeclaredMethod, DefinedMethod, Method} import org.opalj.br.analyses.SomeProject import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement, UnbalancedReturnFact} +import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement, UnbalancedReturnFact} import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, BackwardTaintProblem, Fact, FlowFact, InstanceField, Variable} import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} @@ -58,16 +58,16 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb * Instead, FlowFacts are created at the start node of methods. */ override protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact[Method]] = None + source: (DeclaredMethod, Fact)): Option[FlowFact] = None /** * This analysis does not create FlowFacts at returns. * Instead, FlowFacts are created at the start node of methods. */ protected def applyFlowFactFromCallee( - calleeFact: FlowFact[Method], + calleeFact: FlowFact, source: (DeclaredMethod, Fact) - ): Option[FlowFact[Method]] = None + ): Option[FlowFact] = None /** * If we analyzed a transitive caller of the sink, which is callable from outside the library, @@ -76,7 +76,7 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb override protected def createFlowFactAtBeginningOfMethod( in: Set[Fact], source: (DeclaredMethod, Fact) - ): Option[FlowFact[Method]] = { + ): Option[FlowFact] = { if (source._2.isInstanceOf[UnbalancedReturnFact[Fact @unchecked]] && canBeCalledFromOutside(source._1) && in.exists { // index < 0 means, that it is a parameter. @@ -85,7 +85,7 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb case InstanceField(index, _, _) if index < 0 ⇒ true case _ ⇒ false }) { - Some(FlowFact(currentCallChain(source))) + Some(FlowFact(currentCallChain(source).map(JavaMethod(_)))) } else None } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala index d4e5846aad..aa10f861ef 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -3,11 +3,10 @@ package org.opalj.tac.fpcf.analyses.taint import java.io.File import org.opalj.fpcf.PropertyStore -import org.opalj.br.DeclaredMethod -import org.opalj.br.ObjectType +import org.opalj.br.{DeclaredMethod, Method, ObjectType} import org.opalj.br.analyses.SomeProject import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProblem, TaintProblem, Variable} import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation, Taint} @@ -23,7 +22,7 @@ class ForwardClassForNameTaintAnalysis$Scheduler private (implicit val project: extends ForwardIFDSAnalysis(new ForwardClassForNameTaintProblem(project), Taint) class ForwardClassForNameTaintProblem(project: SomeProject) - extends ForwardTaintProblem(project) with TaintProblem { + extends ForwardTaintProblem(project) with TaintProblem[DeclaredMethod, JavaStatement, Fact] { /** * The string parameters of all public methods are entry points. @@ -59,7 +58,7 @@ class ForwardClassForNameTaintProblem(project: SomeProject) override protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, in: Set[Fact]): Option[FlowFact] = if (isClassForName(callee) && in.contains(Variable(-2))) - Some(FlowFact(Seq(call.method))) + Some(FlowFact(Seq(JavaMethod(call.method)))) else None /** @@ -90,7 +89,7 @@ object ForwardClassForNameTaintAnalysis$Scheduler extends IFDSAnalysisScheduler[ new ForwardClassForNameTaintAnalysis$Scheduler()(p) } - override def property: IFDSPropertyMetaInformation[Fact] = Taint + override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint } class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { @@ -101,10 +100,10 @@ class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { for { e ← analysis.ifdsProblem.entryPoints flows = ps(e, ForwardClassForNameTaintAnalysis$Scheduler.property.key) - fact ← flows.ub.asInstanceOf[IFDSProperty[Fact]].flows.values.flatten.toSet[Fact] + fact ← flows.ub.asInstanceOf[IFDSProperty[JavaStatement, Fact]].flows.values.flatten.toSet[Fact] } { fact match { - case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.toJava).mkString(", ")) + case FlowFact(flow) ⇒ println(s"flow: "+flow.asInstanceOf[Set[Method]].map(_.toJava).mkString(", ")) case _ ⇒ } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala index 9de044a9ad..c784646fd5 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala @@ -1,21 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.taint -import org.opalj.fpcf.Entity -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher -import org.opalj.fpcf.EPS -import org.opalj.fpcf.FinalEP -import org.opalj.br.AnnotationLike -import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject -import org.opalj.br.ElementValue -import org.opalj.br.ElementValuePair import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.DefinedMethod -import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixtureScheduler -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.br._ +import org.opalj.fpcf.{EPS, Entity, FinalEP, Property} +import org.opalj.fpcf.properties.AbstractPropertyMatcher +import org.opalj.tac.fpcf.analyses.ifds.taint.{BackwardTaintAnalysisFixtureScheduler, Fact, FlowFact} import org.opalj.tac.fpcf.properties.Taint /** diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala index 0920a779ed..6232594cdc 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala @@ -1,14 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.taint -import org.opalj.fpcf.Entity -import org.opalj.fpcf.Property -import org.opalj.fpcf.properties.AbstractPropertyMatcher -import org.opalj.br.AnnotationLike -import org.opalj.br.ElementValuePair -import org.opalj.br.ObjectType import org.opalj.br.analyses.SomeProject -import org.opalj.br.ElementValue +import org.opalj.br.{AnnotationLike, ElementValue, ElementValuePair, ObjectType} +import org.opalj.fpcf.{Entity, Property} +import org.opalj.fpcf.properties.AbstractPropertyMatcher import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact import org.opalj.tac.fpcf.properties.Taint diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index 9086636ae3..8d30afbafa 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -39,7 +39,7 @@ class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers .toSet[Fact] } { fact match { - case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.toJava).mkString(", ")) + case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.signature).mkString(", ")) case _ ⇒ } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala index ad77d3ae73..ea5c328951 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala @@ -1,9 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.br.Method import org.opalj.ll.llvm.Function +import org.opalj.tac.fpcf.analyses.ifds.Callable -abstract class Callable +case class LLVMFunction(val function: Function) extends Callable { + override def name(): String = function.name() + override def signature(): String = function.name() // TODO: add signature +} -case class LLVMFunction(val function: Function) extends Callable -case class JavaMethod(val method: Method) extends Callable diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala index 43ed27f20c..47018dd13e 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala @@ -95,7 +95,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod // Start processing at the start of the cfg with the given source fact implicit val state: State = - new State(entity, Map(entity -> Set.empty)) + new State(entity, Map(entity -> Set.empty), Map.empty, Set.empty[Node]) val queue = mutable.Queue .empty[QueueEntry] icfg.startNodes(sourceFact, function).foreach { start ⇒ @@ -303,6 +303,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod blockFacts ⇒ blockFacts._2.get(None) match { case Some(facts) ⇒ result += icfg.lastStatement(blockFacts._1) -> facts + case None ⇒ } ) result diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 4a67393593..7073c39bfc 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -1,14 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds.taint +import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} -import org.opalj.br.{DeclaredMethod, Method} import org.opalj.fpcf.{ProperPropertyComputationResult, PropertyBounds, PropertyStore} import org.opalj.ll.LLVMProjectKey import org.opalj.ll.fpcf.properties.NativeTaint -import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement} -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProblem, NullFact, Variable} -import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} +import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, TACAI, Taint} class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { val llvmProject = p.get(LLVMProjectKey) @@ -47,8 +47,8 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( callee: DeclaredMethod, call: JavaStatement, in: Set[Fact] - ): Option[FlowFact[Method]] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) + ): Option[FlowFact] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(JavaMethod(call.method)))) else None // Multilingual additions here @@ -76,5 +76,5 @@ object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact] { override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis()(p) override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq(LLVMProjectKey) - override val uses: Set[PropertyBounds] = super.uses ++ Set(PropertyBounds.ub(NativeTaint)) + override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.ub(NativeTaint)) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala index 0fc3e05db1..3f5aacd78e 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -3,17 +3,16 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.{PropertyBounds, PropertyStore} -import org.opalj.ll.fpcf.analyses.ifds.{Callable, ForwardNativeIFDSAnalysis, LLVMFunction, LLVMStatement, NativeIFDSAnalysisScheduler} +import org.opalj.ll.fpcf.analyses.ifds.{ForwardNativeIFDSAnalysis, LLVMFunction, LLVMStatement, NativeIFDSAnalysisScheduler} import org.opalj.ll.fpcf.properties.NativeTaint import org.opalj.ll.llvm.Function -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, Variable} import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaintProblem(p) { /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(Function, Fact)] = Seq.empty + override val entryPoints: Seq[(Function, NativeFact)] = Seq.empty /** * The sanitize method is a sanitizer. @@ -24,13 +23,13 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint /** * We do not sanitize parameters. */ - override protected def sanitizeParameters(call: LLVMStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: LLVMStatement, in: Set[NativeFact]): Set[NativeFact] = Set.empty /** * Creates a new variable fact for the callee, if the source was called. */ - override protected def createTaints(callee: Function, call: LLVMStatement): Set[Fact] = - if (callee.name == "source") Set(Variable(call)) + override protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeFact] = + if (callee.name == "source") Set(NativeVariable(call)) else Set.empty /** @@ -40,17 +39,17 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint override protected def createFlowFact( callee: Function, call: LLVMStatement, - in: Set[Fact] - ): Option[FlowFact[Callable]] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact[Callable](Seq(LLVMFunction(call.function)))) + in: Set[NativeFact] + ): Option[NativeFlowFact] = + if (callee.name == "sink" && in.contains(NativeVariable(-2))) Some(NativeFlowFact(Seq(LLVMFunction(call.function)))) else None } -class SimpleNativeForwardTaintAnalysis(implicit val project: SomeProject) +class SimpleNativeForwardTaintAnalysis(implicit project: SomeProject) extends ForwardNativeIFDSAnalysis(new SimpleNativeForwardTaintProblem(project), NativeTaint) -object NativeForwardTaintAnalysisScheduler extends NativeIFDSAnalysisScheduler[Fact] { +object NativeForwardTaintAnalysisScheduler extends NativeIFDSAnalysisScheduler[NativeFact] { override def init(p: SomeProject, ps: PropertyStore) = new SimpleNativeForwardTaintAnalysis()(p) - override def property: IFDSPropertyMetaInformation[LLVMStatement, Fact] = NativeTaint - override val uses: Set[PropertyBounds] = super.uses // ++ PropertyBounds.ub(Taint) TODO: we do not use the native taint yet + override def property: IFDSPropertyMetaInformation[LLVMStatement, NativeFact] = NativeTaint + override val uses: Set[PropertyBounds] = Set() // ++ PropertyBounds.ub(Taint) TODO: we do not use the native taint yet } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index e8f73c7e59..03f0835c06 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -2,18 +2,18 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject -import org.opalj.ll.fpcf.analyses.ifds.{Callable, LLVMStatement, NativeIFDSProblem} +import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeIFDSProblem} import org.opalj.ll.llvm.Function -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact, TaintProblem} +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[Fact](project) with TaintProblem[Function, LLVMStatement] { - override def nullFact: Fact = NullFact +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[Function, LLVMStatement, NativeFact] { + override def nullFact: NativeFact = NativeNullFact /** * If a variable gets assigned a tainted value, the variable will be tainted. */ - override def normalFlow(statement: LLVMStatement, successor: LLVMStatement, - in: Set[Fact]): Set[Fact] = + override def normalFlow(statement: LLVMStatement, successor: Option[LLVMStatement], + in: Set[NativeFact]): Set[NativeFact] = statement match { // TODO case _ ⇒ in @@ -25,9 +25,9 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * edges will be created. */ override def callFlow(call: LLVMStatement, callee: Function, - in: Set[Fact], source: (Function, Fact)): Set[Fact] = { + in: Set[NativeFact], source: (Function, NativeFact)): Set[NativeFact] = { // TODO - Set.empty[Fact] + Set.empty[NativeFact] } /** @@ -40,7 +40,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * If the sanitize method was called, nothing will be tainted. */ override def returnFlow(call: LLVMStatement, callee: Function, exit: LLVMStatement, - successor: LLVMStatement, in: Set[Fact]): Set[Fact] = { + successor: LLVMStatement, in: Set[NativeFact]): Set[NativeFact] = { // TODO Set.empty } @@ -49,8 +49,8 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * Removes taints according to `sanitizeParamters`. */ override def callToReturnFlow(call: LLVMStatement, successor: LLVMStatement, - in: Set[Fact], - source: (Function, Fact)): Set[Fact] = + in: Set[NativeFact], + source: (Function, NativeFact)): Set[NativeFact] = in -- sanitizeParameters(call, in) /** @@ -62,7 +62,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @param call The call. * @return Some variable fact, if necessary. Otherwise none. */ - protected def createTaints(callee: Function, call: LLVMStatement): Set[Fact] + protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeFact] /** * Called, when the call to return facts are computed for some `callee`. @@ -73,7 +73,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @return Some FlowFact, if necessary. Otherwise None. */ protected def createFlowFact(callee: Function, call: LLVMStatement, - in: Set[Fact]): Option[FlowFact[Callable]] + in: Set[NativeFact]): Option[NativeFlowFact] /** * If a parameter is tainted, the result will also be tainted. @@ -81,7 +81,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD */ override def callOutsideOfAnalysisContext(statement: LLVMStatement, callee: Function, successor: LLVMStatement, - in: Set[Fact]): Set[Fact] = { + in: Set[NativeFact]): Set[NativeFact] = { // TODO Set.empty } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala new file mode 100644 index 0000000000..f2824ac9ec --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala @@ -0,0 +1,55 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds.taint + +import org.opalj.br.ObjectType +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} + +trait NativeFact extends AbstractIFDSFact + +case object NativeNullFact extends NativeFact with AbstractIFDSNullFact + +/** + * A tainted variable. + * + * @param definitionSite The variable's definition site. + */ +case class NativeVariable(definitionSite: Any) extends NativeFact + +/** + * A tainted array element. + * + * @param index The array's definition site. + * @param element The index of the tainted element in the array. + */ +case class NativeArrayElement(index: Int, element: Int) extends NativeFact + +/** + * A tainted static field. + * + * @param classType The field's class. + * @param fieldName The field's name. + */ +case class NativeStaticField(classType: ObjectType, fieldName: String) extends NativeFact + +/** + * A tainted instance field. + * + * @param index The definition site of the field's value. + * @param classType The field's type. + * @param fieldName The field's value. + */ +case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends NativeFact + +/** + * A path of method calls, originating from the analyzed method, over which a tainted variable + * reaches the sink. + * + * @param flow A sequence of method calls, originating from but not including this method. + */ +case class NativeFlowFact(flow: Seq[Callable]) extends NativeFact { + override val hashCode: Int = { + var r = 1 + flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) + r + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala index 14caed0f8f..70340e08d3 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala @@ -3,21 +3,21 @@ package org.opalj.ll.fpcf.properties import org.opalj.fpcf.PropertyKey import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.ll.fpcf.analyses.ifds.taint.NativeFact import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation} -case class NativeTaint(flows: Map[LLVMStatement, Set[Fact]]) extends IFDSProperty[LLVMStatement, Fact] { +case class NativeTaint(flows: Map[LLVMStatement, Set[NativeFact]]) extends IFDSProperty[LLVMStatement, NativeFact] { override type Self = NativeTaint - override def create(result: Map[LLVMStatement, Set[Fact]]): IFDSProperty[LLVMStatement, Fact] = new NativeTaint(result) + override def create(result: Map[LLVMStatement, Set[NativeFact]]): IFDSProperty[LLVMStatement, NativeFact] = new NativeTaint(result) override def key: PropertyKey[NativeTaint] = NativeTaint.key } -object NativeTaint extends IFDSPropertyMetaInformation[LLVMStatement, Fact] { +object NativeTaint extends IFDSPropertyMetaInformation[LLVMStatement, NativeFact] { override type Self = NativeTaint - override def create(result: Map[LLVMStatement, Set[Fact]]): IFDSProperty[LLVMStatement, Fact] = new NativeTaint(result) + override def create(result: Map[LLVMStatement, Set[NativeFact]]): IFDSProperty[LLVMStatement, NativeFact] = new NativeTaint(result) val key: PropertyKey[NativeTaint] = PropertyKey.create("NativeTaint", new NativeTaint(Map.empty)) } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala index e4781bca92..9ec3799521 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala @@ -105,10 +105,10 @@ sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { def parent(): BasicBlock = BasicBlock(LLVMGetInstructionParent(ref)) def function(): Function = parent.parent def next(): Option[Instruction] = OptionalInstruction(LLVMGetNextInstruction(ref)) - def successors(): Seq[Instruction] = next match { + /*def successors(): Seq[Instruction] = next match { case Some(successor) ⇒ Seq(successor) case None ⇒ Seq() - } + }*/ } case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala new file mode 100644 index 0000000000..965c4b582e --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.Method + +abstract class Callable { + /** + * The name of the Callable + */ + def name(): String + + /** + * The full name of the Callable including its signature + */ + def signature(): String +} + +case class JavaMethod(val method: Method) extends Callable { + override def name(): String = method.name + override def signature(): String = method.toJava +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala index 5938d0ff98..b806d39c1d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala @@ -22,7 +22,7 @@ trait AbstractIFDSNullFact extends AbstractIFDSFact * @author Dominik Helm * @author Mario Trageser */ -abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](val project: SomeProject) { +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C, Statement](val project: SomeProject) { /** * The null fact of this analysis. */ @@ -31,7 +31,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va /** * The entry points of this analysis. */ - def entryPoints: Seq[(Callable, IFDSFact)] + def entryPoints: Seq[(C, IFDSFact)] /** * Computes the data flow for a normal statement. @@ -62,9 +62,9 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va */ def callFlow( call: Statement, - callee: Callable, + callee: C, in: Set[IFDSFact], - source: (Callable, IFDSFact) + source: (C, IFDSFact) ): Set[IFDSFact] /** @@ -82,7 +82,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va */ def returnFlow( call: Statement, - callee: Callable, + callee: C, exit: Statement, successor: Statement, in: Set[IFDSFact] @@ -102,7 +102,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va call: Statement, successor: Statement, in: Set[IFDSFact], - source: (Callable, IFDSFact) + source: (C, IFDSFact) ): Set[IFDSFact] /** @@ -113,7 +113,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va * @param callee The callee. * @return True, if the callee is inside the analysis context. */ - def insideAnalysisContext(callee: Callable): Boolean + def insideAnalysisContext(callee: C): Boolean /** * When a callee outside of this analysis' context is called, this method computes the summary @@ -127,7 +127,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va */ def callOutsideOfAnalysisContext( call: Statement, - callee: Callable, + callee: C, successor: Statement, in: Set[IFDSFact] ): Set[IFDSFact] @@ -141,11 +141,11 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, Callable, Statement](va */ def getCallees( statement: Statement, - caller: Callable - ): Iterator[Callable] + caller: C + ): Iterator[C] - def delegateAnalysis(source: (Callable, IFDSFact)): Option[((Entity, IFDSFact), Set[IFDSFact] ⇒ Set[IFDSFact])] = None - def specialCase(source: (Callable, IFDSFact), propertyKey: IFDSPropertyMetaInformation[Statement, IFDSFact]): Option[ProperPropertyComputationResult] = None + def delegateAnalysis(source: (C, IFDSFact)): Option[((Entity, IFDSFact), Set[IFDSFact] ⇒ Set[IFDSFact])] = None + def specialCase(source: (C, IFDSFact), propertyKey: IFDSPropertyMetaInformation[Statement, IFDSFact]): Option[ProperPropertyComputationResult] = None } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index bb50b8ae9c..018217e470 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -90,7 +90,7 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e * not re-analyze the code. */ if (declaringClass ne declaredMethod.declaringClassType) Some(delegate( - source, ((declaredMethods(source._1.definedMethod), source._2), propertyKey), identity, propertyKey + source, ((declaredMethods(source._1.definedMethod), source._2), propertyKey), identity[Set[Fact]], propertyKey )) None } @@ -104,9 +104,9 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e * @param propertyKey the propertyKey used for the instantiation of new results * @return The result of the other analysis. */ - protected def delegate( + protected def delegate[DelegatedFact]( source: (DeclaredMethod, Fact), - delegation: ((Entity, Fact), IFDSPropertyMetaInformation[_, Fact]), + delegation: ((Entity, Fact), IFDSPropertyMetaInformation[_, DelegatedFact]), resultMapping: Set[Fact] ⇒ Set[Fact], propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact] ): ProperPropertyComputationResult = { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala index 4949aaf116..6684b440aa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala @@ -7,7 +7,7 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V import org.opalj.tac.{ArrayLength, ArrayLoad, ArrayStore, Assignment, BinaryExpr, Compare, Expr, GetField, NewArray, PrefixExpr, PrimitiveTypecastExpr, PutField, PutStatic, ReturnValue, Var} import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaBackwardIFDSProblem, JavaStatement} -abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[Fact, UnbalancedTaintFact](project) with TaintProblem[DeclaredMethod, JavaStatement] { +abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[Fact, UnbalancedTaintFact](project) with TaintProblem[DeclaredMethod, JavaStatement, Fact] { override def nullFact: Fact = NullFact /** @@ -166,7 +166,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @return Some FlowFact, if necessary. Otherwise None. */ protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact[Method]] + source: (DeclaredMethod, Fact)): Option[FlowFact] /** * Called, when a FlowFact holds at the start node of a callee. Creates a FlowFact in the caller @@ -177,9 +177,9 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @return Some FlowFact, if necessary. Otherwise None. */ protected def applyFlowFactFromCallee( - calleeFact: FlowFact[Method], + calleeFact: FlowFact, source: (DeclaredMethod, Fact) - ): Option[FlowFact[Method]] + ): Option[FlowFact] /** * Called, when new FlowFacts are found at the beginning of a method. @@ -192,7 +192,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF protected def createFlowFactAtBeginningOfMethod( in: Set[Fact], source: (DeclaredMethod, Fact) - ): Option[FlowFact[Method]] + ): Option[FlowFact] /** * Propagates the call to createFlowFactAtBeginningOfMethod. @@ -349,7 +349,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF ) case staticField: StaticField ⇒ facts += staticField // If the source was reached in a callee, create a flow fact from this method to the sink. - case calleeFact: FlowFact[Method] @unchecked if isCallFlow ⇒ + case calleeFact: FlowFact if isCallFlow ⇒ val callerFact = applyFlowFactFromCallee(calleeFact, source) if (callerFact.isDefined) facts += callerFact.get case _ ⇒ // Nothing to do diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 0130c02e7a..69b1577f57 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -1,13 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.br.{DeclaredMethod, Method} +import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.SomeProject import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.{ArrayLength, ArrayLoad, ArrayStore, Assignment, BinaryExpr, Compare, Expr, GetField, GetStatic, NewArray, PrefixExpr, PrimitiveTypecastExpr, PutField, PutStatic, ReturnValue, Var} -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaIFDSProblem, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.tac._ -abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[Fact](project) with TaintProblem[DeclaredMethod, JavaStatement] { +abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[Fact](project) with TaintProblem[DeclaredMethod, JavaStatement, Fact] { override def nullFact: Fact = NullFact /** @@ -169,8 +169,8 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem case sf: StaticField ⇒ flows += sf // Track the call chain to the sink back - case FlowFact(flow) if !flow.contains(call.method) ⇒ - flows += FlowFact(call.method +: flow) + case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ + flows += FlowFact(JavaMethod(call.method) +: flow) case _ ⇒ } @@ -224,7 +224,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @return Some FlowFact, if necessary. Otherwise None. */ protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, - in: Set[Fact]): Option[FlowFact[Method]] + in: Set[Fact]): Option[FlowFact] /** * If a parameter is tainted, the result will also be tainted. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index f509c54bac..9a8573d7ad 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -1,6 +1,6 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint - +import org.opalj.tac.fpcf.analyses.ifds.Callable import org.opalj.br.{Method, ObjectType} import org.opalj.tac.{Assignment, Expr, Stmt} import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V @@ -48,7 +48,7 @@ case class InstanceField(index: Int, classType: ObjectType, fieldName: String) e * * @param flow A sequence of method calls, originating from but not including this method. */ -case class FlowFact[Callable](flow: Seq[Callable]) extends Fact { +case class FlowFact(flow: Seq[Callable]) extends Fact { override val hashCode: Int = { var r = 1 flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) @@ -66,7 +66,7 @@ case class FlowFact[Callable](flow: Seq[Callable]) extends Fact { case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Seq[Method]) extends UnbalancedReturnFact[Fact] with Fact -trait TaintProblem[Callable, Statement] { +trait TaintProblem[C, Statement, IFDSFact] { /** * Checks, if some `callee` is a sanitizer, which sanitizes its return value. @@ -75,7 +75,7 @@ trait TaintProblem[Callable, Statement] { * @param callee The method, which was called. * @return True, if the method is a sanitizer. */ - protected def sanitizesReturnValue(callee: Callable): Boolean + protected def sanitizesReturnValue(callee: C): Boolean /** * Called in callToReturnFlow. This method can return facts, which will be removed after @@ -85,7 +85,7 @@ trait TaintProblem[Callable, Statement] { * @param in The facts, which hold before the call. * @return Facts, which will be removed from `in` after the call. */ - protected def sanitizeParameters(call: Statement, in: Set[Fact]): Set[Fact] + protected def sanitizeParameters(call: Statement, in: Set[IFDSFact]): Set[IFDSFact] } object TaintProblem { diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala index 931922d2c0..fc0eeb73ce 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala @@ -5,7 +5,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.br.analyses.SomeProject import org.opalj.br.DeclaredMethod import org.opalj.br.Method -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement, UnbalancedReturnFact} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement, UnbalancedReturnFact} import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Method]) @@ -45,7 +45,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p * In this case, callFlow would never be called and no FlowFact would be created. */ override protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact[Method]] = { + source: (DeclaredMethod, Fact)): Option[FlowFact] = { if (in.exists { case Variable(index) ⇒ index == call.index case _ ⇒ false @@ -53,7 +53,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p val callChain = currentCallChain(source) // Avoid infinite loops. if (!containsHeadTwice(callChain)) - Some(FlowFact(callChain)) + Some(FlowFact(callChain.map(JavaMethod(_)))) else None } else None } @@ -62,10 +62,10 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p * When a callee calls the source, we create a FlowFact with the caller's call chain. */ override protected def applyFlowFactFromCallee( - calleeFact: FlowFact[Method], + calleeFact: FlowFact, source: (DeclaredMethod, Fact) - ): Option[FlowFact[Method]] = - Some(FlowFact(currentCallChain(source))) + ): Option[FlowFact] = + Some(FlowFact(currentCallChain(source).map(JavaMethod(_)))) /** * This analysis does not create FlowFacts at the beginning of a method. @@ -74,7 +74,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p override protected def createFlowFactAtBeginningOfMethod( in: Set[Fact], source: (DeclaredMethod, Fact) - ): Option[FlowFact[Method]] = + ): Option[FlowFact] = None } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala index 352a374d24..e89c20c5de 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala @@ -1,10 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.fpcf.PropertyStore +import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.SomeProject -import org.opalj.br.{DeclaredMethod, Method} -import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaStatement} +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement} import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} /** @@ -49,8 +49,8 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) * Note, that sink does not accept array parameters. No need to handle them. */ override protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, - in: Set[Fact]): Option[FlowFact[Method]] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(call.method))) + in: Set[Fact]): Option[FlowFact] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(JavaMethod(call.method)))) else None } From 2bfaeeab32f4e47f3271b31274d95b17c82e454e Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 17 Mar 2022 21:45:16 +0100 Subject: [PATCH 25/67] Initialization fix --- .../org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala index 47018dd13e..1a7cc2e76a 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala @@ -53,6 +53,8 @@ protected class Statistics { var sumOfInputfactsForCallbacks = 0L } +protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalysis + /** * * @param ifdsProblem @@ -64,10 +66,8 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod val ifdsProblem: IFDSProblem[IFDSFact, C, S], val icfg: ICFG[IFDSFact, C, S, Node], val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact] -) extends FPCFAnalysis - with Subsumable[S, IFDSFact] { +) extends ProjectFPCFAnalysis(ifdsProblem.project) with Subsumable[S, IFDSFact] { type State = IFDSState[IFDSFact, C, S, Node] - override val project: SomeProject = ifdsProblem.project implicit var statistics = new Statistics From 6df8fab8dd696ff9ea7a7bff7c4e9acd220a7e0c Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 18 Mar 2022 17:39:29 +0100 Subject: [PATCH 26/67] Implement LLVM Argument, Type(s); Better Function/Instruction representation --- .../scala/org/opalj/ll/llvm/Argument.scala | 11 +++ .../scala/org/opalj/ll/llvm/Function.scala | 27 ++++-- .../scala/org/opalj/ll/llvm/Instruction.scala | 4 + .../main/scala/org/opalj/ll/llvm/Type.scala | 84 +++++++++++++++++++ .../main/scala/org/opalj/ll/llvm/Value.scala | 2 + 5 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/Argument.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Argument.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Argument.scala new file mode 100644 index 0000000000..b93d9faa28 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Argument.scala @@ -0,0 +1,11 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.{LLVMArgumentValueKind, LLVMGetParamParent, LLVMGetValueKind} + +case class Argument(ref: LLVMValueRef) extends Value(ref) { + assert(LLVMGetValueKind(ref) == LLVMArgumentValueKind, "ref has to be an argument") + + def parent(): Function = Function(LLVMGetParamParent(ref)) +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala index ca8bc7a407..e51860ba2b 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala @@ -2,15 +2,7 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} -import org.bytedeco.llvm.global.LLVM.{ - LLVMFunctionValueKind, - LLVMGetEntryBasicBlock, - LLVMGetFirstBasicBlock, - LLVMGetNextBasicBlock, - LLVMGetValueKind, - LLVMViewFunctionCFG, - LLVMViewFunctionCFGOnly -} +import org.bytedeco.llvm.global.LLVM.{LLVMFunctionValueKind, LLVMGetEntryBasicBlock, LLVMGetFirstBasicBlock, LLVMGetFirstParam, LLVMGetNextBasicBlock, LLVMGetNextParam, LLVMGetValueKind, LLVMViewFunctionCFG, LLVMViewFunctionCFGOnly} import org.opalj.io.writeAndOpen case class Function(ref: LLVMValueRef) extends Value(ref) { @@ -20,6 +12,10 @@ case class Function(ref: LLVMValueRef) extends Value(ref) { new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) } + def arguments(): ArgumentIterator = { + new ArgumentIterator(LLVMGetFirstParam(ref)) + } + def entryBlock(): BasicBlock = { BasicBlock(LLVMGetEntryBasicBlock(ref)) } @@ -38,6 +34,9 @@ case class Function(ref: LLVMValueRef) extends Value(ref) { } } + override def toString: String = { + s"Function(${name()}(${arguments.map(_.typ().repr()).mkString(", ")}))" + } } class BasicBlockIterator(var ref: LLVMBasicBlockRef) extends Iterator[BasicBlock] { @@ -49,3 +48,13 @@ class BasicBlockIterator(var ref: LLVMBasicBlockRef) extends Iterator[BasicBlock basicBlock } } + +class ArgumentIterator(var ref: LLVMValueRef) extends Iterator[Argument] { + override def hasNext: Boolean = ref != null + + override def next(): Argument = { + val argument = Argument(ref) + this.ref = LLVMGetNextParam(ref) + argument + } +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala index 9ec3799521..a9c14b1869 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala @@ -105,10 +105,14 @@ sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { def parent(): BasicBlock = BasicBlock(LLVMGetInstructionParent(ref)) def function(): Function = parent.parent def next(): Option[Instruction] = OptionalInstruction(LLVMGetNextInstruction(ref)) + /*def successors(): Seq[Instruction] = next match { case Some(successor) ⇒ Seq(successor) case None ⇒ Seq() }*/ + override def toString: String = { + s"${this.getClass.getSimpleName}(${repr()})" + } } case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala new file mode 100644 index 0000000000..09fc67248a --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala @@ -0,0 +1,84 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm + +import org.bytedeco.llvm.LLVM.LLVMTypeRef +import org.bytedeco.llvm.global.LLVM._ + +object Type { + def apply(ref: LLVMTypeRef): Type = { + LLVMGetTypeKind(ref) match { + case LLVMVoidTypeKind ⇒ VoidType(ref) + case LLVMHalfTypeKind ⇒ HalfType(ref) + case LLVMFloatTypeKind ⇒ FloatType(ref) + case LLVMDoubleTypeKind ⇒ DoubleType(ref) + case LLVMX86_FP80TypeKind ⇒ X86_FP80Type(ref) + case LLVMFP128TypeKind ⇒ FP128Type(ref) + case LLVMPPC_FP128TypeKind ⇒ PPC_FP128Type(ref) + case LLVMLabelTypeKind ⇒ LabelType(ref) + case LLVMIntegerTypeKind ⇒ IntegerType(ref) + case LLVMFunctionTypeKind ⇒ FunctionType(ref) + case LLVMStructTypeKind ⇒ StructType(ref) + case LLVMArrayTypeKind ⇒ ArrayType(ref) + case LLVMPointerTypeKind ⇒ PointerType(ref) + case LLVMVectorTypeKind ⇒ VectorType(ref) + case LLVMMetadataTypeKind ⇒ MetadataType(ref) + case LLVMX86_MMXTypeKind ⇒ X86_MMXType(ref) + case LLVMTokenTypeKind ⇒ TokenType(ref) + case LLVMScalableVectorTypeKind ⇒ ScalableVectorType(ref) + case LLVMBFloatTypeKind ⇒ FloatType(ref) + case typeKind ⇒ throw new IllegalArgumentException("unknown type kind: "+typeKind) + } + } +} + +sealed abstract class Type(ref: LLVMTypeRef) { + def repr(): String = { + val bytePointer = LLVMPrintTypeToString(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } + + override def toString: String = s"Type(${repr()})" +} + +/** type with no size */ +case class VoidType(ref: LLVMTypeRef) extends Type(ref) +/** 16 bit floating point type */ +case class HalfType(ref: LLVMTypeRef) extends Type(ref) +/** 32 bit floating point type */ +case class FloatType(ref: LLVMTypeRef) extends Type(ref) +/** 64 bit floating point type */ +case class DoubleType(ref: LLVMTypeRef) extends Type(ref) +/** 80 bit floating point type (X87) */ +case class X86_FP80Type(ref: LLVMTypeRef) extends Type(ref) +/** 128 bit floating point type (112-bit mantissa) */ +case class FP128Type(ref: LLVMTypeRef) extends Type(ref) +/** 128 bit floating point type (two 64-bits) */ +case class PPC_FP128Type(ref: LLVMTypeRef) extends Type(ref) +/** Labels */ +case class LabelType(ref: LLVMTypeRef) extends Type(ref) +/** Arbitrary bit width integers */ +case class IntegerType(ref: LLVMTypeRef) extends Type(ref) +/** Functions */ +case class FunctionType(ref: LLVMTypeRef) extends Type(ref) { + def returnType(): Type = Type(LLVMGetReturnType(ref)) +} +/** Structures */ +case class StructType(ref: LLVMTypeRef) extends Type(ref) +/** Arrays */ +case class ArrayType(ref: LLVMTypeRef) extends Type(ref) +/** Pointers */ +case class PointerType(ref: LLVMTypeRef) extends Type(ref) +/** Fixed width SIMD vector type */ +case class VectorType(ref: LLVMTypeRef) extends Type(ref) +/** Metadata */ +case class MetadataType(ref: LLVMTypeRef) extends Type(ref) +/** X86 MMX */ +case class X86_MMXType(ref: LLVMTypeRef) extends Type(ref) +/** Tokens */ +case class TokenType(ref: LLVMTypeRef) extends Type(ref) +/** Scalable SIMD vector type */ +case class ScalableVectorType(ref: LLVMTypeRef) extends Type(ref) +/** 16 bit brain floating point type */ +case class BFloatType(ref: LLVMTypeRef) extends Type(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala index 38d9510642..187ee515cc 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala @@ -17,6 +17,8 @@ class Value(ref: LLVMValueRef) extends OptionalValue { def name(): String = { LLVMGetValueName(ref).getString } + + def typ(): Type = Type(LLVMTypeOf(ref)) } case class NullValue() extends OptionalValue From f92c95370108c8010d865f6ae868de4b0105850e Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 18 Mar 2022 21:12:53 +0100 Subject: [PATCH 27/67] Move IFDS into separate project; Refactor handling of calls outside of IFDS analysis scope --- ...rdClassForNameTaintAnalysisScheduler.scala | 3 +- ...rdClassForNameTaintAnalysisScheduler.scala | 14 +- ...MultilingualForwardTaintAnalysisTest.scala | 2 +- OPAL/ifds/Readme.md | 4 + OPAL/ifds/build.sbt | 1 + .../main/scala/org/opalj/ifds/Callable.scala | 14 + .../src/main/scala/org/opalj}/ifds/ICFG.scala | 3 +- .../src/main/scala/org/opalj}/ifds/IFDS.scala | 3 +- .../scala/org/opalj}/ifds/IFDSAnalysis.scala | 164 ++++++------ .../scala/org/opalj/ifds/IFDSProblem.scala | 126 +++++++++ .../scala/org/opalj/ifds}/IFDSProperty.scala | 22 +- .../scala/org/opalj}/ifds/Subsumable.scala | 10 +- .../scala/org/opalj}/ifds/Subsuming.scala | 10 +- .../main/scala/org/opalj/ifds/package.scala | 32 +++ .../ll/fpcf/analyses/ifds/Callable.scala | 2 +- .../ifds/ForwardNativeIFDSAnalysis.scala | 18 -- .../analyses/ifds/NativeForwardICFG.scala | 2 +- .../analyses/ifds/NativeIFDSAnalysis.scala | 11 +- .../analyses/ifds/NativeIFDSProblem.scala | 25 +- .../ifds/taint/JavaForwardTaintAnalysis.scala | 28 +- .../taint/NativeForwardTaintAnalysis.scala | 6 +- .../taint/NativeForwardTaintProblem.scala | 11 +- .../ifds/taint/NativeTaintProblem.scala | 2 +- .../org/opalj/ll/fpcf/properties/Taint.scala | 2 +- .../main/scala/org/opalj/ll/llvm/Type.scala | 4 +- .../analyses/ifds/AbstractIFDSAnalysis.scala | 252 ++++++++++-------- .../analyses/ifds/BackwardIFDSAnalysis.scala | 9 +- .../tac/fpcf/analyses/ifds/Callable.scala | 13 +- .../tac/fpcf/analyses/ifds/ForwardICFG.scala | 94 +++++++ .../analyses/ifds/ForwardIFDSAnalysis.scala | 8 +- .../ifds/IFDSBasedVariableTypeAnalysis.scala | 6 +- .../tac/fpcf/analyses/ifds/IFDSProblem.scala | 149 +---------- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 69 ++--- .../analyses/ifds/VariableTypeProblem.scala | 32 +-- .../ifds/taint/BackwardTaintProblem.scala | 28 +- .../ifds/taint/ForwardTaintProblem.scala | 40 +-- .../analyses/ifds/taint/TaintProblem.scala | 7 +- .../org/opalj/tac/fpcf/properties/Taint.scala | 1 + ...ala => BackwardTaintAnalysisFixture.scala} | 12 +- ...cala => ForwardTaintAnalysisFixture.scala} | 5 +- build.sbt | 18 +- project/Dependencies.scala | 1 + 42 files changed, 675 insertions(+), 588 deletions(-) create mode 100644 OPAL/ifds/Readme.md create mode 100644 OPAL/ifds/build.sbt create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/Callable.scala rename OPAL/{ll/src/main/scala/org/opalj/ll/fpcf/analyses => ifds/src/main/scala/org/opalj}/ifds/ICFG.scala (96%) rename OPAL/{ll/src/main/scala/org/opalj/ll/fpcf/analyses => ifds/src/main/scala/org/opalj}/ifds/IFDS.scala (95%) rename OPAL/{ll/src/main/scala/org/opalj/ll/fpcf/analyses => ifds/src/main/scala/org/opalj}/ifds/IFDSAnalysis.scala (84%) create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala rename OPAL/{tac/src/main/scala/org/opalj/tac/fpcf/properties => ifds/src/main/scala/org/opalj/ifds}/IFDSProperty.scala (62%) rename OPAL/{tac/src/main/scala/org/opalj/tac/fpcf/analyses => ifds/src/main/scala/org/opalj}/ifds/Subsumable.scala (94%) rename OPAL/{tac/src/main/scala/org/opalj/tac/fpcf/analyses => ifds/src/main/scala/org/opalj}/ifds/Subsuming.scala (90%) create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/package.scala delete mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala rename OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/{BackwardTaintAnalysisFixtureScheduler.scala => BackwardTaintAnalysisFixture.scala} (89%) rename OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/{ForwardTaintAnalysisFixtureScheduler.scala => ForwardTaintAnalysisFixture.scala} (92%) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index e5b58f91c0..96705906b2 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -7,10 +7,11 @@ import org.opalj.fpcf.EPS import org.opalj.fpcf.FinalEP import org.opalj.br.{DeclaredMethod, DefinedMethod, Method} import org.opalj.br.analyses.SomeProject +import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement, UnbalancedReturnFact} import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, BackwardTaintProblem, Fact, FlowFact, InstanceField, Variable} -import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} +import org.opalj.tac.fpcf.properties.Taint /** * A backward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala index aa10f861ef..e840946bf3 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -1,14 +1,16 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.taint -import java.io.File -import org.opalj.fpcf.PropertyStore -import org.opalj.br.{DeclaredMethod, Method, ObjectType} import org.opalj.br.analyses.SomeProject +import org.opalj.br.{DeclaredMethod, Method, ObjectType} +import org.opalj.fpcf.PropertyStore +import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement} -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, ForwardTaintProblem, TaintProblem, Variable} -import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation, Taint} +import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.analyses.ifds._ +import org.opalj.tac.fpcf.properties.Taint + +import java.io.File /** * A forward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index 8d30afbafa..02053474f8 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -7,7 +7,7 @@ import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact} -import org.opalj.tac.fpcf.properties.IFDSProperty +import org.opalj.ifds.IFDSProperty import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers diff --git a/OPAL/ifds/Readme.md b/OPAL/ifds/Readme.md new file mode 100644 index 0000000000..0fbf431b65 --- /dev/null +++ b/OPAL/ifds/Readme.md @@ -0,0 +1,4 @@ +# Overview +The ***IFDS*** (ifds) module provides a generic implementation for IFDS analyses. + + diff --git a/OPAL/ifds/build.sbt b/OPAL/ifds/build.sbt new file mode 100644 index 0000000000..b511e98651 --- /dev/null +++ b/OPAL/ifds/build.sbt @@ -0,0 +1 @@ +// build settings reside in the opal root build.sbt file diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/Callable.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/Callable.scala new file mode 100644 index 0000000000..e0ea97d5a2 --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/Callable.scala @@ -0,0 +1,14 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ifds + +abstract class Callable { + /** + * The name of the Callable + */ + def name(): String + + /** + * The full name of the Callable including its signature + */ + def signature(): String +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala similarity index 96% rename from OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala rename to OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala index 5499e2405b..c5313988cc 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala @@ -1,6 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact +package org.opalj.ifds import scala.collection.{Set ⇒ SomeSet} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDS.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDS.scala similarity index 95% rename from OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDS.scala rename to OPAL/ifds/src/main/scala/org/opalj/ifds/IFDS.scala index c5f2449d57..accd57e752 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDS.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDS.scala @@ -1,7 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.fpcf.analyses.ifds +package org.opalj.ifds object IFDS { + /** * Merges two maps that have sets as values. * diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala similarity index 84% rename from OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala rename to OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 1a7cc2e76a..00ae0bfdaa 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -1,12 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.fpcf.analyses.ifds +package org.opalj.ifds import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} import org.opalj.fpcf._ -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem, NumberOfCalls, Subsumable} -import org.opalj.tac.fpcf.properties.cg.Callees -import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation} import scala.collection.{mutable, Set ⇒ SomeSet} @@ -40,6 +37,16 @@ protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statem var outgoingFacts: Map[Node, Map[Option[Node], Set[IFDSFact]]] = Map.empty[Node, Map[Option[Node], Set[IFDSFact]]] ) +/** + * Contains int variables, which count, how many times some method was called. + */ +class NumberOfCalls { + var normalFlow = 0 + var callFlow = 0 + var returnFlow = 0 + var callToReturnFlow = 0 +} + protected class Statistics { /** @@ -63,15 +70,15 @@ protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalys */ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( implicit - val ifdsProblem: IFDSProblem[IFDSFact, C, S], - val icfg: ICFG[IFDSFact, C, S, Node], + project: SomeProject, + val ifdsProblem: IFDSProblem[IFDSFact, C, S, Node], val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact] -) extends ProjectFPCFAnalysis(ifdsProblem.project) with Subsumable[S, IFDSFact] { +) extends ProjectFPCFAnalysis(project) with Subsumable[S, IFDSFact] { type State = IFDSState[IFDSFact, C, S, Node] + type QueueEntry = (Node, Set[IFDSFact], Option[S], Option[C], Option[IFDSFact]) implicit var statistics = new Statistics - - type QueueEntry = (Node, Set[IFDSFact], Option[S], Option[C], Option[IFDSFact]) + val icfg = ifdsProblem.icfg /** * Creates an IFDSProperty containing the result of this analysis. @@ -151,10 +158,12 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - case FinalEP(_: C @unchecked, _: Callees) ⇒ + case FinalEP(_: C @unchecked, _: Any) ⇒ + // TODO: Any was Callees, how to verify this? reAnalyzeBasicBlocks(state.pendingCgCallSites) - case InterimEUBP(_: C @unchecked, _: Callees) ⇒ + case InterimEUBP(_: C @unchecked, _: Any) ⇒ + // TODO: Any was Callees, how to verify this? reAnalyzeBasicBlocks(state.pendingCgCallSites) } @@ -203,89 +212,90 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod } for (callee ← callees) { - if (!ifdsProblem.insideAnalysisContext(callee)) { - // Let the concrete analysis decide what to do. - for { - successor ← successors - } summaryEdges += - successor -> (summaryEdges(successor) ++ - ifdsProblem.callOutsideOfAnalysisContext(call, callee, successor, in)) - } else { - val callToStart = - if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) - else { - propagateNullFact(in, callToStartFacts(call, callee, in)) - } - var allNewExitFacts: Map[S, Set[IFDSFact]] = Map.empty - // Collect exit facts for each input fact separately - for (fact ← callToStart) { - /* + ifdsProblem.outsideAnalysisContext(callee) match { + case Some(handler) ⇒ + // Let the concrete analysis decide what to do. + for { + successor ← successors + } summaryEdges += + successor -> (summaryEdges(successor) ++ + handler(call, successor, in)) + case None ⇒ + val callToStart = + if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) + else { + propagateNullFact(in, callToStartFacts(call, callee, in)) + } + var allNewExitFacts: Map[S, Set[IFDSFact]] = Map.empty + // Collect exit facts for each input fact separately + for (fact ← callToStart) { + /* * If this is a recursive call with the same input facts, we assume that the * call only produces the facts that are already known. The call site is added to * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts * become known for the input fact. */ - if ((callee eq state.source._1) && fact == state.source._2) { - val newDependee = - if (state.pendingIfdsCallSites.contains(state.source)) - state.pendingIfdsCallSites(state.source) + call - else Set(call) - state.pendingIfdsCallSites = - state.pendingIfdsCallSites.updated(state.source, newDependee) - allNewExitFacts = IFDS.mergeMaps(allNewExitFacts, collectResult) - } else { - val e = (callee, fact) - val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] - val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[S, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]]) ⇒ ep.ub.flows - case _ ⇒ Map.empty - } - val exitFacts: Map[S, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ - if (state.pendingIfdsCallSites.contains(e) - && state.pendingIfdsCallSites(e).nonEmpty) { - val newDependee = - state.pendingIfdsCallSites(e) - call - state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) - } - state.pendingIfdsDependees -= e - ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ - /* + if ((callee eq state.source._1) && fact == state.source._2) { + val newDependee = + if (state.pendingIfdsCallSites.contains(state.source)) + state.pendingIfdsCallSites(state.source) + call + else Set(call) + state.pendingIfdsCallSites = + state.pendingIfdsCallSites.updated(state.source, newDependee) + allNewExitFacts = IFDS.mergeMaps(allNewExitFacts, collectResult) + } else { + val e = (callee, fact) + val callFlows = propertyStore(e, propertyKey.key) + .asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] + val oldValue = state.pendingIfdsDependees.get(e) + val oldExitFacts: Map[S, Set[IFDSFact]] = oldValue match { + case Some(ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]]) ⇒ ep.ub.flows + case _ ⇒ Map.empty + } + val exitFacts: Map[S, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ + if (state.pendingIfdsCallSites.contains(e) + && state.pendingIfdsCallSites(e).nonEmpty) { + val newDependee = + state.pendingIfdsCallSites(e) - call + state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) + } + state.pendingIfdsDependees -= e + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ + /* * Add the call site to `pendingIfdsCallSites` and * `pendingIfdsDependees` and continue with the facts in the interim * result for now. When the analysis for the callee finishes, the * analysis for this call site will be triggered again. */ - addIfdsDependee(e, callFlows, basicBlock, call) - ep.ub.flows - case _ ⇒ - addIfdsDependee(e, callFlows, basicBlock, call) - Map.empty - } - // Only process new facts that are not in `oldExitFacts` - allNewExitFacts = IFDS.mergeMaps( - allNewExitFacts, - filterNewInformation(exitFacts, oldExitFacts, project) - ) - /* + addIfdsDependee(e, callFlows, basicBlock, call) + ep.ub.flows + case _ ⇒ + addIfdsDependee(e, callFlows, basicBlock, call) + Map.empty + } + // Only process new facts that are not in `oldExitFacts` + allNewExitFacts = IFDS.mergeMaps( + allNewExitFacts, + filterNewInformation(exitFacts, oldExitFacts, project) + ) + /* * If new exit facts were discovered for the callee-fact-pair, all call * sites depending on this pair have to be re-evaluated. oldValue is * undefined if the callee-fact pair has not been queried before or returned * a FinalEP. */ - if (oldValue.isDefined && oldExitFacts != exitFacts) { - reAnalyzeCalls( - state.pendingIfdsCallSites(e), - e._1, - Some(e._2) - ) + if (oldValue.isDefined && oldExitFacts != exitFacts) { + reAnalyzeCalls( + state.pendingIfdsCallSites(e), + e._1, + Some(e._2) + ) + } } } - } - summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) + summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) } } summaryEdges diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala new file mode 100644 index 0000000000..588d2d3e2e --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -0,0 +1,126 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ifds + +import org.opalj.fpcf.ProperPropertyComputationResult + +/** + * The supertype of all IFDS facts. + */ +trait AbstractIFDSFact + +/** + * The super type of all null facts. + */ +trait AbstractIFDSNullFact extends AbstractIFDSFact + +/** + * A framework for IFDS analyses. + * + * @tparam IFDSFact The type of flow facts, which are tracked by the concrete analysis. + * @author Dominik Helm + * @author Mario Trageser + */ +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node](val icfg: ICFG[IFDSFact, C, S, Node]) { + /** + * The null fact of this analysis. + */ + def nullFact: IFDSFact + + /** + * The entry points of this analysis. + */ + def entryPoints: Seq[(C, IFDSFact)] + + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param successor The successor of the analyzed `statement`, for which the data flow shall be + * computed. It is not present for exit statements. + * @param in The facts, which hold before the execution of the `statement`. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + def normalFlow( + statement: S, + successor: Option[S], + in: Set[IFDSFact] + ): Set[IFDSFact] + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The facts, which hold before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + def callFlow( + call: S, + callee: C, + in: Set[IFDSFact], + source: (C, IFDSFact) + ): Set[IFDSFact] + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param callee The method called by `call`, for which the data flow shall be computed. + * @param exit The statement, which terminated the `calle`. + * @param successor The statement of the caller, which will be executed after the `callee` + * returned. + * @param in The facts, which hold before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + def returnFlow( + call: S, + callee: C, + exit: S, + successor: S, + in: Set[IFDSFact] + ): Set[IFDSFact] + + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param successor The statement, which will be executed after the call. + * @param in The facts, which hold before the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + def callToReturnFlow( + call: S, + successor: S, + in: Set[IFDSFact], + source: (C, IFDSFact) + ): Set[IFDSFact] + + type OutsideAnalysisContextHandler = ((S, S, Set[IFDSFact]) ⇒ Set[IFDSFact]) { + def apply(call: S, successor: S, in: Set[IFDSFact]): Set[IFDSFact] + } + + /** + * Checks, if a callee is outside this analysis' context. + * By default, native methods are not inside the analysis context. + * For callees outside this analysis' context the returned handler is called + * to compute the summary edge for the call instead of analyzing the callee. + * + * @param callee The method called by `call`. + * @return The handler function. It receives + * the statement which invoked the call, + * the successor statement, which will be executed after the call and + * the set of input facts which hold before the `call`. + * It returns facts, which hold after the call, excluding the call to return flow. + */ + def outsideAnalysisContext(callee: C): Option[OutsideAnalysisContextHandler] + + def specialCase(source: (C, IFDSFact), propertyKey: IFDSPropertyMetaInformation[S, IFDSFact]): Option[ProperPropertyComputationResult] = None +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala similarity index 62% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala rename to OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala index 55dc7cfe63..5c29cbdbd9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/IFDSProperty.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala @@ -1,37 +1,29 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package tac -package fpcf -package properties - +package org.opalj.ifds import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyMetaInformation -import org.opalj.value.KnownTypedValue -trait IFDSPropertyMetaInformation[Statement, DataFlowFact] extends PropertyMetaInformation { +trait IFDSPropertyMetaInformation[S, IFDSFact] extends PropertyMetaInformation { /** * Creates an IFDSProperty containing the result of this analysis. * * @param result Maps each exit statement to the facts, which hold after the exit statement. * @return An IFDSProperty containing the `result`. */ - def create(result: Map[Statement, Set[DataFlowFact]]): IFDSProperty[Statement, DataFlowFact] + def create(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] } -abstract class IFDSProperty[Statement, DataFlowFact] +abstract class IFDSProperty[S, IFDSFact] extends Property - with IFDSPropertyMetaInformation[Statement, DataFlowFact] { - - /** The type of the TAC domain. */ - type V = DUVar[KnownTypedValue] + with IFDSPropertyMetaInformation[S, IFDSFact] { /** * Maps exit statements to the data flow facts, which hold after them. */ - def flows: Map[Statement, Set[DataFlowFact]] + def flows: Map[S, Set[IFDSFact]] override def equals(other: Any): Boolean = other match { - case that: IFDSProperty[Statement, DataFlowFact] ⇒ + case that: IFDSProperty[S, IFDSFact] ⇒ // We cached the "hashCode" to make the following comparison more efficient; // note that all properties are eventually added to some set and therefore // the hashCode is required anyway! diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/Subsumable.scala similarity index 94% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala rename to OPAL/ifds/src/main/scala/org/opalj/ifds/Subsumable.scala index 3996fc340a..ef4e6e24d9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsumable.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/Subsumable.scala @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds +package org.opalj.ifds import org.opalj.br.analyses.SomeProject @@ -34,7 +34,7 @@ trait SubsumableNullFact extends SubsumableFact with AbstractIFDSNullFact { * * @author Mario Trageser */ -trait Subsumable[Statement, IFDSFact <: AbstractIFDSFact] { +trait Subsumable[S, IFDSFact <: AbstractIFDSFact] { /** * A subclass can override this method to filter the `facts` in some set, which are not subsumed @@ -77,10 +77,10 @@ trait Subsumable[Statement, IFDSFact <: AbstractIFDSFact] { * `oldExitFacts` are not present. */ protected def filterNewInformation[T <: IFDSFact]( - newExitFacts: Map[Statement, Set[T]], - oldExitFacts: Map[Statement, Set[T]], + newExitFacts: Map[S, Set[T]], + oldExitFacts: Map[S, Set[T]], project: SomeProject - ): Map[Statement, Set[T]] = { + ): Map[S, Set[T]] = { var result = newExitFacts for ((key, values) ← oldExitFacts) { result = result.updated(key, result(key) -- values) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/Subsuming.scala similarity index 90% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala rename to OPAL/ifds/src/main/scala/org/opalj/ifds/Subsuming.scala index e40566428c..0bcb29d96b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Subsuming.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/Subsuming.scala @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds +package org.opalj.ifds import org.opalj.br.analyses.SomeProject @@ -8,7 +8,7 @@ import org.opalj.br.analyses.SomeProject * * @author Mario Trageser */ -trait Subsuming[Statement, IFDSFact <: SubsumableFact] extends Subsumable[Statement, IFDSFact] { +trait Subsuming[S, IFDSFact <: SubsumableFact] extends Subsumable[S, IFDSFact] { val numberOfSubsumptions = new NumberOfSubsumptions @@ -43,9 +43,9 @@ trait Subsuming[Statement, IFDSFact <: SubsumableFact] extends Subsumable[Statem * Considers subsuming. */ override protected def filterNewInformation[T <: IFDSFact]( - newExitFacts: Map[Statement, Set[T]], - oldExitFacts: Map[Statement, Set[T]], project: SomeProject - ): Map[Statement, Set[T]] = + newExitFacts: Map[S, Set[T]], + oldExitFacts: Map[S, Set[T]], project: SomeProject + ): Map[S, Set[T]] = newExitFacts.keys.map { statement ⇒ val old = oldExitFacts.get(statement) diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/package.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/package.scala new file mode 100644 index 0000000000..2f4f6f9986 --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/package.scala @@ -0,0 +1,32 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj + +import com.typesafe.config.Config +import com.typesafe.config.ConfigFactory + +import org.opalj.log.LogContext +import org.opalj.log.GlobalLogContext +import org.opalj.log.OPALLogger.info + +package object ifds { + + final val FrameworkName = "OPAL IFDS" + + { + implicit val logContext: LogContext = GlobalLogContext + try { + assert(false) // <= test whether assertions are turned on or off... + info(FrameworkName, "Production Build") + } catch { + case _: AssertionError ⇒ info(FrameworkName, "Development Build with Assertions") + } + } + + // We want to make sure that the class loader is used which potentially can + // find the config files; the libraries (e.g., Typesafe Config) may have + // been loaded using the parent class loader and, hence, may not be able to + // find the config files at all. + val BaseConfig: Config = ConfigFactory.load(this.getClass.getClassLoader) + + final val ConfigKeyPrefix = "org.opalj.ifds." +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala index ea5c328951..f7bb1b7366 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.ll.llvm.Function -import org.opalj.tac.fpcf.analyses.ifds.Callable +import org.opalj.ifds.Callable case class LLVMFunction(val function: Function) extends Callable { override def name(): String = function.name() diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala deleted file mode 100644 index 059062d87f..0000000000 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/ForwardNativeIFDSAnalysis.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.fpcf.analyses.ifds - -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation - -/** - * An IFDS analysis, which analyzes the code in the control flow direction. - * - * @author Mario Trageser - */ - -class ForwardNativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( - ifdsProblem: NativeIFDSProblem[IFDSFact], - propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] -) extends NativeIFDSAnalysis[IFDSFact]( - ifdsProblem, new NativeForwardICFG[IFDSFact], propertyKey -) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index fcee5b9b5f..e2c947fcb5 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -1,8 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds +import org.opalj.ifds.{AbstractIFDSFact, ICFG} import org.opalj.ll.llvm.{BasicBlock, Function, Instruction, Ret, Terminator} -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSFact class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Function, LLVMStatement, BasicBlock] { /** diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala index 6dab11405a..e18fe3036c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala @@ -2,11 +2,10 @@ package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} +import org.opalj.ifds.{AbstractIFDSFact, IFDSAnalysis, IFDSAnalysisScheduler, IFDSProblem, IFDSPropertyMetaInformation, Statement} import org.opalj.ll.LLVMProjectKey import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem} -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation /** * @@ -15,11 +14,11 @@ import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation * @tparam IFDSFact */ class NativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( - ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement], - icfg: ICFG[IFDSFact, Function, LLVMStatement, BasicBlock], + project: SomeProject, + ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement, BasicBlock], propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] ) - extends IFDSAnalysis[IFDSFact, Function, LLVMStatement, BasicBlock]()(ifdsProblem, icfg, propertyKey) + extends IFDSAnalysis[IFDSFact, Function, LLVMStatement, BasicBlock]()(project, ifdsProblem, propertyKey) /** * A statement that is passed to the concrete analysis. diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index 4e32dbfe8b..9f08749017 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -1,29 +1,16 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.br.analyses.{SomeProject} +import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.fpcf.{PropertyStore} +import org.opalj.fpcf.PropertyStore +import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem} import org.opalj.ll.LLVMProjectKey -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, IFDSProblem} -import org.opalj.ll.llvm.Function +import org.opalj.ll.llvm.{BasicBlock, Function} -abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, Function, LLVMStatement](project) { +abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, Function, LLVMStatement, BasicBlock](new NativeForwardICFG[Fact]) { final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) val llvmProject = project.get(LLVMProjectKey) - /** - * Checks, if a callee is inside this analysis' context. - * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. - * By default, native methods are not inside the analysis context. - * - * @param callee The callee. - * @return True, if the callee is inside the analysis context. - */ - override def insideAnalysisContext(callee: Function): Boolean = true - - override def getCallees( - statement: LLVMStatement, - caller: Function - ): Iterator[Function] = ??? + override def outsideAnalysisContext(callee: Function): Option[OutsideAnalysisContextHandler] = None } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 7073c39bfc..2f0150aef9 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -3,12 +3,13 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} -import org.opalj.fpcf.{ProperPropertyComputationResult, PropertyBounds, PropertyStore} +import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.ll.LLVMProjectKey import org.opalj.ll.fpcf.properties.NativeTaint import org.opalj.tac.fpcf.analyses.ifds.taint._ import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement} -import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, TACAI, Taint} +import org.opalj.tac.fpcf.properties.{TACAI, Taint} class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { val llvmProject = p.get(LLVMProjectKey) @@ -53,19 +54,22 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( // Multilingual additions here - override def insideAnalysisContext(callee: DeclaredMethod): Boolean = { - super.insideAnalysisContext(callee) || callee.definedMethod.isNative - } + override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = { + def handleNativeMethod(call: JavaStatement, successor: JavaStatement, in: Set[Fact]): Set[Fact] = { + //val method = callee.definedMethod - override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { - val method = source._1.definedMethod - if (method.isNative) { // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names - val nativeMethodName = "Java_"+method.classFile.fqn+"_"+method.name - val function = llvmProject.function(nativeMethodName) - return Some(delegate(source, ((function.get, source._2), NativeTaint), identity, propertyKey)) + //val nativeMethodName = "Java_"+method.classFile.fqn+"_"+method.name + //val function = llvmProject.function(nativeMethodName) + //val foo = propertyStore((function.get, source._2), NativeTaint) + Set.empty[Fact] + } + + if (callee.definedMethod.isNative) { + Some(handleNativeMethod _) + } else { + super.outsideAnalysisContext(callee) } - super.specialCase(source, propertyKey) } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala index 3f5aacd78e..18b274ba24 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -3,10 +3,10 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.{PropertyBounds, PropertyStore} -import org.opalj.ll.fpcf.analyses.ifds.{ForwardNativeIFDSAnalysis, LLVMFunction, LLVMStatement, NativeIFDSAnalysisScheduler} +import org.opalj.ifds.IFDSPropertyMetaInformation +import org.opalj.ll.fpcf.analyses.ifds.{NativeIFDSAnalysis, LLVMFunction, LLVMStatement, NativeIFDSAnalysisScheduler} import org.opalj.ll.fpcf.properties.NativeTaint import org.opalj.ll.llvm.Function -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaintProblem(p) { /** @@ -46,7 +46,7 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint } class SimpleNativeForwardTaintAnalysis(implicit project: SomeProject) - extends ForwardNativeIFDSAnalysis(new SimpleNativeForwardTaintProblem(project), NativeTaint) + extends NativeIFDSAnalysis(project, new SimpleNativeForwardTaintProblem(project), NativeTaint) object NativeForwardTaintAnalysisScheduler extends NativeIFDSAnalysisScheduler[NativeFact] { override def init(p: SomeProject, ps: PropertyStore) = new SimpleNativeForwardTaintAnalysis()(p) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 03f0835c06..e8a0ea185b 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -75,15 +75,8 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD protected def createFlowFact(callee: Function, call: LLVMStatement, in: Set[NativeFact]): Option[NativeFlowFact] - /** - * If a parameter is tainted, the result will also be tainted. - * We assume that the callee does not call the source method. - */ - override def callOutsideOfAnalysisContext(statement: LLVMStatement, callee: Function, - successor: LLVMStatement, - in: Set[NativeFact]): Set[NativeFact] = { - // TODO - Set.empty + override def outsideAnalysisContext(callee: Function): Option[OutsideAnalysisContextHandler] = { + None } /** diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala index f2824ac9ec..9bd08f0bab 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala @@ -2,7 +2,7 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.ObjectType -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} +import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} trait NativeFact extends AbstractIFDSFact diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala index 70340e08d3..8a6412dd96 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala @@ -2,9 +2,9 @@ package org.opalj.ll.fpcf.properties import org.opalj.fpcf.PropertyKey +import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement import org.opalj.ll.fpcf.analyses.ifds.taint.NativeFact -import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation} case class NativeTaint(flows: Map[LLVMStatement, Set[NativeFact]]) extends IFDSProperty[LLVMStatement, NativeFact] { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala index 09fc67248a..28cb1aafd7 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala @@ -39,7 +39,7 @@ sealed abstract class Type(ref: LLVMTypeRef) { string } - override def toString: String = s"Type(${repr()})" + override def toString: String = s"Type(${repr()})" } /** type with no size */ @@ -62,7 +62,7 @@ case class LabelType(ref: LLVMTypeRef) extends Type(ref) case class IntegerType(ref: LLVMTypeRef) extends Type(ref) /** Functions */ case class FunctionType(ref: LLVMTypeRef) extends Type(ref) { - def returnType(): Type = Type(LLVMGetReturnType(ref)) + def returnType(): Type = Type(LLVMGetReturnType(ref)) } /** Structures */ case class StructType(ref: LLVMTypeRef) extends Type(ref) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index 8992484166..02312962f3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -38,8 +38,7 @@ import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.ai.domain.l0.PrimitiveTACAIDomain import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.tac.fpcf.properties.IFDSProperty -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation +import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, IFDSProperty, IFDSPropertyMetaInformation, NumberOfCalls, Statement, Subsumable} import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.fpcf.properties.TheTACAI import org.opalj.tac.cg.{RTACallGraphKey, TypeProviderKey} @@ -52,9 +51,10 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. * @tparam IFDSFact */ - -abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement], val propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) - extends FPCFAnalysis +abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( + val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement, CFGNode], + val propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact] +) extends FPCFAnalysis with Subsumable[JavaStatement, IFDSFact] { /** @@ -72,7 +72,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble /** * Counts, how many input facts are passed to callbacks. */ - var sumOfInputfactsForCallbacks = 0L + var sumOfInputFactsForCallbacks = 0L /** * The state of the analysis. For each method and source fact, there is a separate state. @@ -134,7 +134,9 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble * @param result Maps each exit statement to the facts, which hold after the exit statement. * @return An IFDSProperty containing the `result`. */ - protected def createPropertyValue(result: Map[JavaStatement, Set[IFDSFact]]): IFDSProperty[JavaStatement, IFDSFact] = propertyKey.create(result) + protected def createPropertyValue( + result: Map[JavaStatement, Set[IFDSFact]] + ): IFDSProperty[JavaStatement, IFDSFact] = propertyKey.create(result) /** * Determines the nodes, that will be analyzed after some `basicBlock`. @@ -244,10 +246,10 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble val (declaredMethod, sourceFact) = entity /* - * The analysis can only handle single defined methods. - * If a method is not single defined, this analysis assumes that it does not create any - * facts. - */ + * The analysis can only handle single defined methods. + * If a method is not single defined, this analysis assumes that it does not create any + * facts. + */ if (!declaredMethod.hasSingleDefinedMethod) return Result(entity, createPropertyValue(Map.empty)); @@ -260,9 +262,9 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble val declaringClass: ObjectType = method.classFile.thisType /* - * Fetch the method's three address code. If it is not present, return an empty interim - * result. - */ + * Fetch the method's three address code. If it is not present, return an empty interim + * result. + */ val (code, cfg) = propertyStore(method, TACAI.key) match { case FinalP(TheTACAI(tac)) ⇒ (tac.stmts, tac.cfg) @@ -397,7 +399,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble if (calleeWithUpdateFact.isEmpty) for (successor ← successors) { numberOfCalls.callToReturnFlow += 1 - sumOfInputfactsForCallbacks += in.size + sumOfInputFactsForCallbacks += in.size summaryEdges += successor -> propagateNullFact( inputFacts, @@ -407,90 +409,92 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble for (calledMethod ← callees) { val callee = declaredMethods(calledMethod) - if (!ifdsProblem.insideAnalysisContext(callee)) { - // Let the concrete analysis decide what to do. - for { - successor ← successors - } summaryEdges += - successor -> (summaryEdges(successor) ++ - ifdsProblem.callOutsideOfAnalysisContext(call, callee, successor, in)) - } else { - val callToStart = - if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) - else { - propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) - } - var allNewExitFacts: Map[JavaStatement, Set[IFDSFact]] = Map.empty - // Collect exit facts for each input fact separately - for (fact ← callToStart) { - /* - * If this is a recursive call with the same input facts, we assume that the - * call only produces the facts that are already known. The call site is added to - * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts - * become known for the input fact. - */ - if ((calledMethod eq state.method) && fact == state.source._2) { - val newDependee = - if (state.pendingIfdsCallSites.contains(state.source)) - state.pendingIfdsCallSites(state.source) + - ((basicBlock, call.index)) - else Set((basicBlock, call.index)) - state.pendingIfdsCallSites = - state.pendingIfdsCallSites.updated(state.source, newDependee) - allNewExitFacts = mergeMaps(allNewExitFacts, collectResult) - } else { - val e = (callee, fact) - val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] - val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[JavaStatement, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[JavaStatement, IFDSFact]]) ⇒ ep.ub.flows - case _ ⇒ Map.empty - } - val exitFacts: Map[JavaStatement, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[JavaStatement, IFDSFact]] ⇒ - if (state.pendingIfdsCallSites.contains(e) - && state.pendingIfdsCallSites(e).nonEmpty) { - val newDependee = - state.pendingIfdsCallSites(e) - ((basicBlock, call.index)) - state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) - } - state.pendingIfdsDependees -= e - ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[JavaStatement, IFDSFact]] ⇒ - /* - * Add the call site to `pendingIfdsCallSites` and - * `pendingIfdsDependees` and continue with the facts in the interim - * result for now. When the analysis for the callee finishes, the - * analysis for this call site will be triggered again. - */ - addIfdsDependee(e, callFlows, basicBlock, call.index) - ep.ub.flows - case _ ⇒ - addIfdsDependee(e, callFlows, basicBlock, call.index) - Map.empty + ifdsProblem.outsideAnalysisContext(callee) match { + case Some(handler) ⇒ + // Let the concrete analysis decide what to do. + for { + successor ← successors + } summaryEdges += + successor -> (summaryEdges(successor) ++ + handler(call, successor, in)) + case None ⇒ + val callToStart = + if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) + else { + propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) } - // Only process new facts that are not in `oldExitFacts` - allNewExitFacts = mergeMaps( - allNewExitFacts, - filterNewInformation(exitFacts, oldExitFacts, project) - ) + var allNewExitFacts: Map[JavaStatement, Set[IFDSFact]] = Map.empty + // Collect exit facts for each input fact separately + for (fact ← callToStart) { /* - * If new exit facts were discovered for the callee-fact-pair, all call - * sites depending on this pair have to be re-evaluated. oldValue is - * undefined if the callee-fact pair has not been queried before or returned - * a FinalEP. + * If this is a recursive call with the same input facts, we assume that the + * call only produces the facts that are already known. The call site is added to + * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts + * become known for the input fact. */ - if (oldValue.isDefined && oldExitFacts != exitFacts) { - reAnalyzeCalls( - state.pendingIfdsCallSites(e), - e._1.definedMethod, - Some(e._2) + if ((calledMethod eq state.method) && fact == state.source._2) { + val newDependee = + if (state.pendingIfdsCallSites.contains(state.source)) + state.pendingIfdsCallSites(state.source) + + ((basicBlock, call.index)) + else Set((basicBlock, call.index)) + state.pendingIfdsCallSites = + state.pendingIfdsCallSites.updated(state.source, newDependee) + allNewExitFacts = mergeMaps(allNewExitFacts, collectResult) + } else { + val e = (callee, fact) + val callFlows = propertyStore(e, propertyKey.key) + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] + val oldValue = state.pendingIfdsDependees.get(e) + val oldExitFacts: Map[JavaStatement, Set[IFDSFact]] = oldValue match { + case Some(ep: InterimEUBP[_, IFDSProperty[JavaStatement, IFDSFact]]) ⇒ ep.ub.flows + case _ ⇒ Map.empty + } + val exitFacts: Map[JavaStatement, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[JavaStatement, IFDSFact]] ⇒ + if (state.pendingIfdsCallSites.contains(e) + && state.pendingIfdsCallSites(e).nonEmpty) { + val newDependee = + state.pendingIfdsCallSites(e) - ((basicBlock, call.index)) + state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) + } + state.pendingIfdsDependees -= e + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[JavaStatement, IFDSFact]] ⇒ + /* + * Add the call site to `pendingIfdsCallSites` and + * `pendingIfdsDependees` and continue with the facts in the interim + * result for now. When the analysis for the callee finishes, the + * analysis for this call site will be triggered again. + */ + addIfdsDependee(e, callFlows, basicBlock, call.index) + ep.ub.flows + case _ ⇒ + addIfdsDependee(e, callFlows, basicBlock, call.index) + Map.empty + } + // Only process new facts that are not in `oldExitFacts` + allNewExitFacts = mergeMaps( + allNewExitFacts, + filterNewInformation(exitFacts, oldExitFacts, project) ) + /* + * If new exit facts were discovered for the callee-fact-pair, all call + * sites depending on this pair have to be re-evaluated. oldValue is + * undefined if the callee-fact pair has not been queried before or returned + * a FinalEP. + */ + if (oldValue.isDefined && oldExitFacts != exitFacts) { + reAnalyzeCalls( + state.pendingIfdsCallSites(e), + e._1.definedMethod, + Some(e._2) + ) + } } } - } - summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) + summaryEdges = + addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) } } summaryEdges @@ -506,6 +510,29 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble */ protected def beforeHandleCall(call: JavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = in + /** + * Gets the set of all methods directly callable at some call statement. + * + * @param basicBlock The basic block containing the call. + * @param pc The call's program counter. + * @param caller The caller, performing the call. + * @return All methods directly callable at the statement index. + */ + protected def getCallees( + statement: JavaStatement, + caller: DeclaredMethod + ): Iterator[DeclaredMethod] = { + val pc = statement.code(statement.index).pc + val ep = propertyStore(caller, Callees.key) + ep match { + case FinalEP(_, p) ⇒ p.directCallees(typeProvider.newContext(caller), pc).map(_.method) + case _ ⇒ + throw new IllegalStateException( + "call graph mut be computed before the analysis starts" + ) + } + } + /** * Merges two maps that have sets as values. * @@ -642,7 +669,9 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble * calleeFact: If `index` equals `calleeWithUpdateIndex`, only * `calleeWithUpdateFact` will be returned, None otherwise. */ - def collectInformation(index: Int): (JavaStatement, Option[SomeSet[Method]], Option[IFDSFact]) = { + def collectInformation( + index: Int + ): (JavaStatement, Option[SomeSet[Method]], Option[IFDSFact]) = { val stmt = state.code(index) val statement = JavaStatement(state.method, basicBlock, stmt, index, state.code, state.cfg) val calleesO = @@ -666,7 +695,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble val successor = JavaStatement(state.method, basicBlock, state.code(next), next, state.code, state.cfg) numberOfCalls.normalFlow += 1 - sumOfInputfactsForCallbacks += in.size + sumOfInputFactsForCallbacks += in.size ifdsProblem.normalFlow(statement, Some(successor), flows) } else // Inside a basic block, we only have one successor --> Take the head @@ -681,7 +710,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble var result: Map[CFGNode, Set[IFDSFact]] = Map.empty for (node ← nextNodes(basicBlock)) { numberOfCalls.normalFlow += 1 - sumOfInputfactsForCallbacks += in.size + sumOfInputFactsForCallbacks += in.size result += node -> ifdsProblem.normalFlow(statement, Some(firstStatement(node)), flows) } result @@ -726,24 +755,21 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact](val ifdsProble /** * Gets the set of all methods possibly called at some statement. * - * @param basicBlock The basic block containing the statement. - * @param index The statement's index. - * @return All methods possibly called at the statement index or None, if the statement does not + * @param statement The statement. + * @return All methods possibly called at the statement or None, if the statement does not * contain a call. */ private def getCalleesIfCallStatement(statement: JavaStatement)( implicit state: State ): Option[SomeSet[Method]] = { - //val statement = state.code(index) statement.stmt.astID match { case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ - Some(definedMethods(ifdsProblem.getCallees(statement, state.source._1))) + Some(definedMethods(getCallees(statement, state.source._1))) case Assignment.ASTID | ExprStmt.ASTID ⇒ getExpression(statement.stmt).astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | - VirtualFunctionCall.ASTID ⇒ - Some(definedMethods(ifdsProblem.getCallees(statement, state.source._1))) + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + Some(definedMethods(getCallees(statement, state.source._1))) case _ ⇒ None } case _ ⇒ None @@ -862,7 +888,7 @@ case class JavaStatement( index: Int, code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] -) { +) extends Statement[CFGNode] { override def hashCode(): Int = method.hashCode() * 31 + index @@ -872,20 +898,10 @@ case class JavaStatement( } override def toString: String = s"${method.toJava}" - -} - -/** - * Contains int variables, which count, how many times some method was called. - */ -class NumberOfCalls { - var normalFlow = 0 - var callFlow = 0 - var returnFlow = 0 - var callToReturnFlow = 0 } -abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends FPCFLazyAnalysisScheduler { +abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] + extends FPCFLazyAnalysisScheduler { final override type InitializationData = AbstractIFDSAnalysis[IFDSFact] @@ -986,7 +1002,7 @@ abstract class AbsractIFDSAnalysisRunner { analysisTime, analysis.numberOfCalls, additionalEvaluationResult(analysis), - analysis.sumOfInputfactsForCallbacks + analysis.sumOfInputFactsForCallbacks ) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala index 20bd513fd1..b5a6da1895 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala @@ -15,9 +15,10 @@ import org.opalj.br.cfg.CFGNode import org.opalj.br.DeclaredMethod import org.opalj.br.cfg.CatchNode import org.opalj.br.cfg.CFG +import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.properties.{IFDSProperty, IFDSPropertyMetaInformation, TACAI, TheTACAI} +import org.opalj.tac.fpcf.properties.{TACAI, TheTACAI} import org.opalj.tac.Stmt import org.opalj.tac.TACStmts import org.opalj.tac.DUVar @@ -33,7 +34,7 @@ import org.opalj.tac.TACode * concrete analysis. * @author Mario Trageser */ -abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement] with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { +abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement, CFGNode] with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { /** * If this method is analyzed for an unbalanced return fact, the single star block is the block, * which contains the call. @@ -278,7 +279,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS (exitStatement.stmt.astID != Return.ASTID && exitStatement.stmt.astID != ReturnValue.ASTID) } { numberOfCalls.returnFlow += 1 - sumOfInputfactsForCallbacks += in.size + sumOfInputFactsForCallbacks += in.size flow ++= ifdsProblem.returnFlow(call, callee, exitStatement, successor._1, in) } } @@ -310,7 +311,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS if (exitFacts.nonEmpty) { val in = exitFacts.head._2 numberOfCalls.callFlow += 1 - sumOfInputfactsForCallbacks += in.size + sumOfInputFactsForCallbacks += in.size val exitToReturnFacts = ifdsProblem.callFlow(call, callee, in, state.source) successors.foreach { successor ⇒ val updatedValue = result.get(successor) match { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala index 965c4b582e..d6392889ac 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala @@ -2,18 +2,7 @@ package org.opalj.tac.fpcf.analyses.ifds import org.opalj.br.Method - -abstract class Callable { - /** - * The name of the Callable - */ - def name(): String - - /** - * The full name of the Callable including its signature - */ - def signature(): String -} +import org.opalj.ifds.Callable case class JavaMethod(val method: Method) extends Callable { override def name(): String = method.name diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala new file mode 100644 index 0000000000..f62acdf119 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala @@ -0,0 +1,94 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.cfg.CFGNode +import org.opalj.fpcf.{FinalEP, PropertyStore} +import org.opalj.ifds.{AbstractIFDSFact, ICFG} +import org.opalj.tac.fpcf.analyses.cg.TypeProvider +import org.opalj.tac.fpcf.properties.cg.Callees + +class ForwardICFG[IFDSFact <: AbstractIFDSFact](implicit + propertyStore: PropertyStore, + typeProvider: TypeProvider, + declaredMethods: DeclaredMethods +) extends ICFG[IFDSFact, DeclaredMethod, JavaStatement, CFGNode] { + /** + * Determines the basic blocks, at which the analysis starts. + * + * @param sourceFact The source fact of the analysis. + * @param callable The analyzed callable. + * @return The basic blocks, at which the analysis starts. + */ + override def startNodes(sourceFact: IFDSFact, callable: DeclaredMethod): Set[CFGNode] = ??? + + /** + * Determines the nodes, that will be analyzed after some `basicBlock`. + * + * @param node The basic block, that was analyzed before. + * @return The nodes, that will be analyzed after `basicBlock`. + */ + override def nextNodes(node: CFGNode): Set[CFGNode] = ??? + + /** + * Checks, if some `node` is the last node. + * + * @return True, if `node` is the last node, i.e. there is no next node. + */ + override def isLastNode(node: CFGNode): Boolean = ??? + + /** + * Determines the first index of some `basic block`, that will be analyzed. + * + * @param basicBlock The basic block. + * @return The first index of some `basic block`, that will be analyzed. + */ + override def firstStatement(basicBlock: CFGNode): JavaStatement = ??? + + /** + * Determines the last index of some `basic block`, that will be analzyed. + * + * @param basicBlock The basic block. + * @return The last index of some `basic block`, that will be analzyed. + */ + override def lastStatement(basicBlock: CFGNode): JavaStatement = ??? + + /** + * Determines the statement that will be analyzed after some other statement. + * + * @param statement The current statement. + * @return The statement that will be analyzed after `statement`. + */ + override def nextStatement(statement: JavaStatement): JavaStatement = ??? + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + override def nextStatements(statement: JavaStatement): Set[JavaStatement] = ??? + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + override def getCalleesIfCallStatement(statement: JavaStatement): Option[collection.Set[DeclaredMethod]] = { + val pc = statement.code(statement.index).pc + val caller = declaredMethods(statement.method) + val ep = propertyStore(caller, Callees.key) + ep match { + case FinalEP(_, p) ⇒ Some(p.directCallees(typeProvider.newContext(caller), pc).map(_.method).toSet) + case _ ⇒ + throw new IllegalStateException( + "call graph mut be computed before the analysis starts" + ) + } + } + + override def isExitStatement(statement: JavaStatement): Boolean = ??? +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala index 0da76c3e2e..52c18ff533 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala @@ -5,19 +5,19 @@ import org.opalj.br.cfg.BasicBlock import org.opalj.br.cfg.CFG import org.opalj.br.cfg.CFGNode import org.opalj.br.DeclaredMethod +import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V import org.opalj.tac.Return import org.opalj.tac.ReturnValue import org.opalj.tac.Stmt import org.opalj.tac.TACStmts -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation /** * An IFDS analysis, which analyzes the code in the control flow direction. * * @author Mario Trageser */ -abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { +abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement, CFGNode], propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { /** * The analysis starts at the entry block. @@ -110,7 +110,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF override protected def callToStartFacts(call: JavaStatement, callee: DeclaredMethod, in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { numberOfCalls.callFlow += 1 - sumOfInputfactsForCallbacks += in.size + sumOfInputFactsForCallbacks += in.size ifdsProblem.callFlow(call, callee, in, state.source) } @@ -209,7 +209,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF allNewExitFacts: Map[JavaStatement, Set[IFDSFact]]): Map[JavaStatement, Set[IFDSFact]] = { val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) numberOfCalls.returnFlow += 1 - sumOfInputfactsForCallbacks += in.size + sumOfInputFactsForCallbacks += in.size val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) val newFacts = if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala index 34f44a1199..5225f76733 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -3,16 +3,12 @@ package org.opalj.tac.fpcf.analyses.ifds import java.io.File import java.io.PrintWriter - import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.PropertyBounds - import org.opalj.br.analyses.SomeProject - +import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation, NumberOfSubsumptions, Subsuming} import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.tac.fpcf.properties.IFDSProperty -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.properties.TACAI /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala index b806d39c1d..6802c09c98 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala @@ -1,152 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds -import org.opalj.br.analyses.SomeProject -import org.opalj.fpcf.{Entity, ProperPropertyComputationResult} -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation - -/** - * The supertype of all IFDS facts. - */ -trait AbstractIFDSFact - -/** - * The super type of all null facts. - */ -trait AbstractIFDSNullFact extends AbstractIFDSFact - -/** - * A framework for IFDS analyses. - * - * @tparam IFDSFact The type of flow facts, which are tracked by the concrete analysis. - * @author Dominik Helm - * @author Mario Trageser - */ -abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C, Statement](val project: SomeProject) { - /** - * The null fact of this analysis. - */ - def nullFact: IFDSFact - - /** - * The entry points of this analysis. - */ - def entryPoints: Seq[(C, IFDSFact)] - - /** - * Computes the data flow for a normal statement. - * - * @param statement The analyzed statement. - * @param successor The successor of the analyzed `statement`, for which the data flow shall be - * computed. It is not present for exit statements. - * @param in The facts, which hold before the execution of the `statement`. - * @return The facts, which hold after the execution of `statement` under the assumption - * that the facts in `in` held before `statement` and `successor` will be - * executed next. - */ - def normalFlow( - statement: Statement, - successor: Option[Statement], - in: Set[IFDSFact] - ): Set[IFDSFact] - - /** - * Computes the data flow for a call to start edge. - * - * @param call The analyzed call statement. - * @param callee The called method, for which the data flow shall be computed. - * @param in The facts, which hold before the execution of the `call`. - * @param source The entity, which is analyzed. - * @return The facts, which hold after the execution of `statement` under the assumption that - * the facts in `in` held before `statement` and `statement` calls `callee`. - */ - def callFlow( - call: Statement, - callee: C, - in: Set[IFDSFact], - source: (C, IFDSFact) - ): Set[IFDSFact] - - /** - * Computes the data flow for an exit to return edge. - * - * @param call The statement, which called the `callee`. - * @param callee The method called by `call`, for which the data flow shall be computed. - * @param exit The statement, which terminated the `calle`. - * @param successor The statement of the caller, which will be executed after the `callee` - * returned. - * @param in The facts, which hold before the execution of the `exit`. - * @return The facts, which hold after the execution of `exit` in the caller's context - * under the assumption that `in` held before the execution of `exit` and that - * `successor` will be executed next. - */ - def returnFlow( - call: Statement, - callee: C, - exit: Statement, - successor: Statement, - in: Set[IFDSFact] - ): Set[IFDSFact] - - /** - * Computes the data flow for a call to return edge. - * - * @param call The statement, which invoked the call. - * @param successor The statement, which will be executed after the call. - * @param in The facts, which hold before the `call`. - * @param source The entity, which is analyzed. - * @return The facts, which hold after the call independently of what happens in the callee - * under the assumption that `in` held before `call`. - */ - def callToReturnFlow( - call: Statement, - successor: Statement, - in: Set[IFDSFact], - source: (C, IFDSFact) - ): Set[IFDSFact] - - /** - * Checks, if a callee is inside this analysis' context. - * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. - * By default, native methods are not inside the analysis context. - * - * @param callee The callee. - * @return True, if the callee is inside the analysis context. - */ - def insideAnalysisContext(callee: C): Boolean - - /** - * When a callee outside of this analysis' context is called, this method computes the summary - * edge for the call. - * - * @param call The statement, which invoked the call. - * @param callee The method called by `call`. - * @param successor The statement, which will be executed after the call. - * @param in The facts facts, which hold before the `call`. - * @return The facts, which hold after the call, excluding the call to return flow. - */ - def callOutsideOfAnalysisContext( - call: Statement, - callee: C, - successor: Statement, - in: Set[IFDSFact] - ): Set[IFDSFact] - - /** - * Gets the set of all methods directly callable at some call statement. - * - * @param statement The call statement. - * @param caller The caller, performing the call. - * @return All methods directly callable at the statement index. - */ - def getCallees( - statement: Statement, - caller: C - ): Iterator[C] - - def delegateAnalysis(source: (C, IFDSFact)): Option[((Entity, IFDSFact), Set[IFDSFact] ⇒ Set[IFDSFact])] = None - def specialCase(source: (C, IFDSFact), propertyKey: IFDSPropertyMetaInformation[Statement, IFDSFact]): Option[ProperPropertyComputationResult] = None -} +import org.opalj.ifds.AbstractIFDSFact /** * A data flow fact, that was created by an unbalanced return. @@ -207,4 +62,4 @@ trait BackwardIFDSProblem[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IF source: (Method, IFDSFact) ): Set[IFDSFact] = Set.empty -} \ No newline at end of file +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 018217e470..17ca169033 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -1,18 +1,21 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds -import org.opalj.br.{DeclaredMethod, ObjectType} import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} +import org.opalj.br.cfg.CFGNode import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.fpcf.{Entity, FinalEP, FinalP, InterimResult, InterimUBP, ProperPropertyComputationResult, Property, PropertyStore, Result, SomeEOptionP} +import org.opalj.br.{DeclaredMethod, ObjectType} +import org.opalj.fpcf._ import org.opalj.tac.cg.TypeProviderKey import org.opalj.tac.fpcf.analyses.cg.TypeProvider -import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt} import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.properties.IFDSPropertyMetaInformation -import org.opalj.tac.fpcf.properties.cg.{Callees, Callers} +import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, IFDSPropertyMetaInformation} +import org.opalj.tac.fpcf.properties.cg.Callers +import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt} -abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, DeclaredMethod, JavaStatement](project) { +abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, DeclaredMethod, JavaStatement, CFGNode]( + new ForwardICFG[Fact]()(project.get(PropertyStoreKey), project.get(TypeProviderKey), project.get(DeclaredMethodsKey)) +) { /** * All declared methods in the project. */ @@ -21,31 +24,9 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) implicit final protected val typeProvider: TypeProvider = project.get(TypeProviderKey) - /** - * Checks, if a callee is inside this analysis' context. - * If not, `callOutsideOfAnalysisContext` is called instead of analyzing the callee. - * By default, native methods are not inside the analysis context. - * - * @param callee The callee. - * @return True, if the callee is inside the analysis context. - */ - override def insideAnalysisContext(callee: DeclaredMethod): Boolean = - callee.definedMethod.body.isDefined - - override def getCallees( - statement: JavaStatement, - caller: DeclaredMethod - ): Iterator[DeclaredMethod] = { - val pc = statement.code(statement.index).pc - val ep = propertyStore(caller, Callees.key) - ep match { - case FinalEP(_, p) ⇒ - p.directCallees(typeProvider.newContext(caller), pc).map(_.method) - case _ ⇒ - throw new IllegalStateException( - "call graph mut be computed before the analysis starts" - ) - } + override def outsideAnalysisContext(callee: DeclaredMethod): Option[(JavaStatement, JavaStatement, Set[Fact]) ⇒ Set[Fact]] = callee.definedMethod.body.isDefined match { + case true ⇒ None + case false ⇒ Some((_call: JavaStatement, _successor: JavaStatement, in: Set[Fact]) ⇒ in) } /** @@ -89,39 +70,29 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e * If this is not the method's declaration, but a non-overwritten method in a subtype, do * not re-analyze the code. */ - if (declaringClass ne declaredMethod.declaringClassType) Some(delegate( - source, ((declaredMethods(source._1.definedMethod), source._2), propertyKey), identity[Set[Fact]], propertyKey - )) + if (declaringClass ne declaredMethod.declaringClassType) Some(baseMethodResult(source, propertyKey)) None } /** - * This method will be called if the analysis of a method shall be delegated to another analysis. + * This method will be called if a non-overwritten declared method in a sub type shall be + * analyzed. Analyzes the defined method of the supertype instead. * * @param source A pair consisting of the declared method of the subtype and an input fact. - * @param delegation A pair consisting of the delegated entity and an input fact as well as the delegated property. - * @param resultMapping A function that maps the results of the delegation. - * @param propertyKey the propertyKey used for the instantiation of new results - * @return The result of the other analysis. + * @return The result of the analysis of the defined method of the supertype. */ - protected def delegate[DelegatedFact]( - source: (DeclaredMethod, Fact), - delegation: ((Entity, Fact), IFDSPropertyMetaInformation[_, DelegatedFact]), - resultMapping: Set[Fact] ⇒ Set[Fact], - propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact] - ): ProperPropertyComputationResult = { + private def baseMethodResult(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): ProperPropertyComputationResult = { def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { - case FinalP(p) ⇒ Result(source, propertyKey.create(Map.empty)) + case FinalP(p) ⇒ Result(source, p) case ep @ InterimUBP(ub: Property) ⇒ - // TODO: resultMapping(ub.asInstanceOf[Set[Fact]]).asInstanceOf[Property] - InterimResult.forUB(source, propertyKey.create(Map.empty), Set(ep), c) + InterimResult.forUB(source, ub, Set(ep), c) case epk ⇒ InterimResult.forUB(source, propertyKey.create(Map.empty), Set(epk), c) } - c(propertyStore(delegation._1, delegation._2.key)) + c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala index c2c1c60a78..e96bfb0ceb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala @@ -4,6 +4,7 @@ package org.opalj.tac.fpcf.analyses.ifds import org.opalj.br.{ArrayType, ClassFile, DeclaredMethod, FieldType, Method, ReferenceType} import org.opalj.br.analyses.SomeProject import org.opalj.collection.immutable.EmptyIntTrieSet +import org.opalj.ifds.{AbstractIFDSFact, SubsumableFact, SubsumableNullFact} import org.opalj.tac.{ArrayLoad, ArrayStore, Assignment, DUVar, Expr, GetField, GetStatic, New, ReturnValue, Var} import org.opalj.value.ValueInformation @@ -190,31 +191,24 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] } } else Set.empty - /** - * If a method outside of the analysis context is called, we assume that it returns every - * possible runtime type matching the compile time type. - */ - override def callOutsideOfAnalysisContext( - statement: JavaStatement, - callee: DeclaredMethod, - successor: JavaStatement, - in: Set[VTAFact] - ): Set[VTAFact] = { - val returnType = callee.descriptor.returnType - if (statement.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { - Set(VariableType(statement.index, returnType.asReferenceType, upperBound = true)) - } else Set.empty - } - /** * Only methods in java.lang and org.opalj are inside the analysis context. * * @param callee The callee. * @return True, if the callee is inside the analysis context. */ - override def insideAnalysisContext(callee: DeclaredMethod): Boolean = - classInsideAnalysisContext(callee.definedMethod.classFile) && - super.insideAnalysisContext(callee) + override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = + if (classInsideAnalysisContext(callee.definedMethod.classFile) && + super.outsideAnalysisContext(callee).isEmpty) + None + else { + Some(((call: JavaStatement, successor: JavaStatement, in: Set[VTAFact]) ⇒ { + val returnType = callee.descriptor.returnType + if (call.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { + Set(VariableType(call.index, returnType.asReferenceType, upperBound = true)) + } else Set.empty[VTAFact] + }): OutsideAnalysisContextHandler) + } /** * When `normalFlow` reaches an assignment or array store, this method computes the new facts diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala index 6684b440aa..31108cc456 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala @@ -131,19 +131,21 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF /** * If the returned value is tainted, all actual parameters will be tainted. */ - override def callOutsideOfAnalysisContext(call: JavaStatement, callee: DeclaredMethod, - successor: JavaStatement, - in: Set[Fact]): Set[Fact] = { - val callStatement = asCall(call.stmt) - in ++ in.collect { - case Variable(index) if index == call.index ⇒ - callStatement.allParams.flatMap(createNewTaints(_, call)) - case ArrayElement(index, _) if index == call.index ⇒ - callStatement.allParams.flatMap(createNewTaints(_, call)) - case InstanceField(index, _, _) if index == call.index ⇒ - callStatement.allParams.flatMap(createNewTaints(_, call)) - }.flatten - } + override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = + super.outsideAnalysisContext(callee) match { + case Some(_) ⇒ Some((call: JavaStatement, successor: JavaStatement, in: Set[Fact]) ⇒ { + val callStatement = asCall(call.stmt) + in ++ in.collect { + case Variable(index) if index == call.index ⇒ + callStatement.allParams.flatMap(createNewTaints(_, call)) + case ArrayElement(index, _) if index == call.index ⇒ + callStatement.allParams.flatMap(createNewTaints(_, call)) + case InstanceField(index, _, _) if index == call.index ⇒ + callStatement.allParams.flatMap(createNewTaints(_, call)) + }.flatten + }) + case None ⇒ None + } /** * Creates an UnbalancedTaintFact for each actual parameter on the caller side, of the formal diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 69b1577f57..8422d20746 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -230,24 +230,28 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * If a parameter is tainted, the result will also be tainted. * We assume that the callee does not call the source method. */ - override def callOutsideOfAnalysisContext(statement: JavaStatement, callee: DeclaredMethod, - successor: JavaStatement, - in: Set[Fact]): Set[Fact] = { - val allParams = asCall(statement.stmt).receiverOption ++ asCall(statement.stmt).params - if (statement.stmt.astID == Assignment.ASTID && in.exists { - case Variable(index) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - } - case ArrayElement(index, _) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - } - case _ ⇒ false - }) Set(Variable(statement.index)) - else Set.empty + override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = { + super.outsideAnalysisContext(callee) match { + case Some(_) ⇒ Some(((call: JavaStatement, successor: JavaStatement, in: Set[Fact]) ⇒ { + val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params + if (call.stmt.astID == Assignment.ASTID && in.exists { + case Variable(index) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case ArrayElement(index, _) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case _ ⇒ false + }) Set(Variable(call.index)) + else Set.empty + }): OutsideAnalysisContextHandler) + case None ⇒ None + } + } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index 9a8573d7ad..64f11ce364 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -1,10 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.tac.fpcf.analyses.ifds.Callable + import org.opalj.br.{Method, ObjectType} -import org.opalj.tac.{Assignment, Expr, Stmt} +import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, UnbalancedReturnFact} +import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact +import org.opalj.tac.{Assignment, Expr, Stmt} trait Fact extends AbstractIFDSFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala index 1419ce21a4..b5852315f1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala @@ -2,6 +2,7 @@ package org.opalj.tac.fpcf.properties import org.opalj.fpcf.PropertyKey +import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.taint.Fact diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala similarity index 89% rename from OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala rename to OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala index fc0eeb73ce..a53196cccb 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixtureScheduler.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala @@ -1,12 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.fpcf.PropertyStore +import org.opalj.br.{DeclaredMethod, Method} import org.opalj.br.analyses.SomeProject -import org.opalj.br.DeclaredMethod -import org.opalj.br.Method -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement, UnbalancedReturnFact} -import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} +import org.opalj.fpcf.PropertyStore +import org.opalj.ifds.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ifds._ +import org.opalj.tac.fpcf.properties.Taint case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Method]) extends UnbalancedReturnFact[Fact] with Fact @@ -49,7 +49,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p if (in.exists { case Variable(index) ⇒ index == call.index case _ ⇒ false - } && getCallees(call, source._1).exists(_.name == "source")) { + } && icfg.getCalleesIfCallStatement(call).get.exists(_.name == "source")) { val callChain = currentCallChain(source) // Avoid infinite loops. if (!containsHeadTwice(callChain)) diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala similarity index 92% rename from OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala rename to OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala index e89c20c5de..3647385d62 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixtureScheduler.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -4,8 +4,9 @@ package org.opalj.tac.fpcf.analyses.ifds.taint import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.PropertyStore +import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement} -import org.opalj.tac.fpcf.properties.{IFDSPropertyMetaInformation, Taint} +import org.opalj.tac.fpcf.properties.Taint /** * An analysis that checks, if the return value of the method `source` can flow to the parameter of @@ -24,7 +25,7 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) override val entryPoints: Seq[(DeclaredMethod, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") .flatMap(classFile ⇒ classFile.methods) - .filter(method ⇒ method.isPublic && insideAnalysisContext(declaredMethods(method))) + .filter(method ⇒ method.isPublic && outsideAnalysisContext(declaredMethods(method)).isEmpty) .map(method ⇒ declaredMethods(method) → NullFact) /** diff --git a/build.sbt b/build.sbt index 38847457b4..895c297cd6 100644 --- a/build.sbt +++ b/build.sbt @@ -29,7 +29,7 @@ ThisBuild / licenses := Seq("BSD-2-Clause" -> url("https://opensource.org/licens usePgpKeyHex("80B9D3FB5A8508F6B4774932E71AFF01E234090C") -scalaVersion in ThisBuild := "2.12.15" +ThisBuild / scalaVersion := "2.12.15" ScalacConfiguration.globalScalacOptions @@ -279,6 +279,19 @@ lazy val `AbstractInterpretationFramework` = (project in file("OPAL/ai")) .dependsOn(br % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) +lazy val ifds = `IFDS` +lazy val `IFDS` = (project in file("OPAL/ifds")) + .settings(buildSettings: _*) + .settings( + name := "IFDS", + Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - IFDS"), + fork := true, + libraryDependencies ++= Dependencies.ifds + ) + .dependsOn(si % "it->it;it->test;test->test;compile->compile") + .dependsOn(br % "it->it;it->test;test->test;compile->compile") + .configs(IntegrationTest) + lazy val tac = `ThreeAddressCode` lazy val `ThreeAddressCode` = (project in file("OPAL/tac")) .settings(buildSettings: _*) @@ -291,6 +304,7 @@ lazy val `ThreeAddressCode` = (project in file("OPAL/tac")) run / fork := true ) .dependsOn(ai % "it->it;it->test;test->test;compile->compile") + .dependsOn(ifds % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) lazy val ba = `BytecodeAssembler` @@ -333,7 +347,7 @@ lazy val `LLVM` = (project in file("OPAL/ll")) .settings(buildSettings: _*) .settings( name := "LLVM", - scalacOptions in (Compile, doc) ++= Opts.doc.title("OPAL - LLVM"), + Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - LLVM"), fork := true, libraryDependencies ++= Dependencies.ll ) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 02e00f85eb..aded902f4f 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -71,6 +71,7 @@ object Dependencies { val bi = Seq(commonstext) val br = Seq(scalaparsercombinators, scalaxml) val ll = Seq(llvm) + val ifds = Seq() val tools = Seq(txtmark, jacksonDF) val hermes = Seq(txtmark, jacksonDF, javafxBase) From 3931605bdd5af66b51458606bde4eefa503b3f8d Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 24 Mar 2022 19:58:17 +0100 Subject: [PATCH 28/67] Re-implement ifds solver --- .../org/opalj/ifds/AbstractIFDSFact.scala | 40 + .../src/main/scala/org/opalj/ifds/ICFG.scala | 111 +- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 974 +++++++----------- .../scala/org/opalj/ifds/IFDSProblem.scala | 194 ++-- .../scala/org/opalj/ifds/Subsumable.scala | 105 -- .../main/scala/org/opalj/ifds/old/ICFG.scala | 81 ++ .../org/opalj/ifds/old/IFDSAnalysis.scala | 651 ++++++++++++ .../org/opalj/ifds/old/IFDSProblem.scala | 117 +++ .../scala/org/opalj/ifds/old/Subsumable.scala | 83 ++ .../org/opalj/ifds/{ => old}/Subsuming.scala | 3 +- .../analyses/ifds/NativeForwardICFG.scala | 3 +- .../analyses/ifds/NativeIFDSAnalysis.scala | 3 +- .../analyses/ifds/NativeIFDSProblem.scala | 3 +- .../analyses/ifds/AbstractIFDSAnalysis.scala | 3 +- .../analyses/ifds/BackwardIFDSAnalysis.scala | 3 +- .../tac/fpcf/analyses/ifds/ForwardICFG.scala | 3 +- .../analyses/ifds/ForwardIFDSAnalysis.scala | 12 +- .../ifds/IFDSBasedVariableTypeAnalysis.scala | 3 +- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 3 +- .../analyses/ifds/VariableTypeProblem.scala | 4 +- 20 files changed, 1489 insertions(+), 910 deletions(-) create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala delete mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/Subsumable.scala create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala rename OPAL/ifds/src/main/scala/org/opalj/ifds/{ => old}/Subsuming.scala (97%) diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala new file mode 100644 index 0000000000..43739d1edb --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala @@ -0,0 +1,40 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ifds + +import org.opalj.br.analyses.SomeProject + +/** + * The supertype of all IFDS facts. + */ +trait AbstractIFDSFact + +/** + * The super type of all null facts. + */ +trait AbstractIFDSNullFact extends AbstractIFDSFact + +/** + * The supertype of all IFDS facts, which can subsume another fact. + */ +trait SubsumableFact extends AbstractIFDSFact { + + /** + * Checks, if this fact subsumes an `other` fact. + * + * @param other The other fact. + * @param project The analyzed project. + * @return True, if this fact subsumes the `other`fact + */ + def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean +} + +/** + * The null fact for subsumable facts. + */ +trait SubsumableNullFact extends SubsumableFact with AbstractIFDSNullFact { + + /** + * The null fact cannot subsume another fact. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = false +} \ No newline at end of file diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala index c5313988cc..6fa1ac8579 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala @@ -1,79 +1,40 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ifds -import scala.collection.{Set ⇒ SomeSet} - -abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] { - /** - * Determines the basic blocks, at which the analysis starts. - * - * @param sourceFact The source fact of the analysis. - * @param callable The analyzed callable. - * @return The basic blocks, at which the analysis starts. - */ - def startNodes(sourceFact: IFDSFact, callable: C): Set[Node] - - /** - * Determines the nodes, that will be analyzed after some `node`. - * - * @param node The basic block, that was analyzed before. - * @return The nodes, that will be analyzed after `node`. - */ - def nextNodes(node: Node): Set[Node] - - /** - * Checks, if some `node` is the last node. - * - * @return True, if `node` is the last node, i.e. there is no next node. - */ - def isLastNode(node: Node): Boolean - - /** - * Determines the first index of some `basic block`, that will be analyzed. - * - * @param node The basic block. - * @return The first index of some `basic block`, that will be analyzed. - */ - def firstStatement(node: Node): S - - /** - * Determines the last index of some `basic block`, that will be analzyed. - * - * @param node The basic block. - * @return The last index of some `basic block`, that will be analzyed. - */ - def lastStatement(node: Node): S - - /** - * Determines the statement that will be analyzed after some other statement. - * - * @param statement The current statement. - * @return The statement that will be analyzed after `statement`. - */ - def nextStatement(statement: S): S - - /** - * Determines the statement, that will be analyzed after some other `statement`. - * - * @param statement The source statement. - * @return The successor statements - */ - def nextStatements(statement: S): Set[S] - - /** - * Gets the set of all methods possibly called at some statement. - * - * @param statement The statement. - * @return All callables possibly called at the statement or None, if the statement does not - * contain a call. - */ - def getCalleesIfCallStatement(statement: S): Option[SomeSet[C]] - - /** - * Determines whether the statement is an exit statement. - * - * @param statement The source statement. - * @return Whether the statement flow may exit its callable (function/method) - */ - def isExitStatement(statement: S): Boolean +import scala.collection.{Set => SomeSet} + +abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]] { + /** + * Determines the statements at which the analysis starts. + * + * @param callable The analyzed callable. + * @return The statements at which the analysis starts. + */ + def startStatements(callable: C): Set[S] + + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + def nextStatements(statement: S): Set[S] + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + def getCalleesIfCallStatement(statement: S): Option[SomeSet[C]] + + /** + * Determines whether the statement is an exit statement. + * + * @param statement The source statement. + * @return Whether the statement flow may exit its callable (function/method) + */ + def isExitStatement(statement: S): Boolean } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 00ae0bfdaa..1b3b04dd30 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -5,59 +5,115 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} import org.opalj.fpcf._ -import scala.collection.{mutable, Set ⇒ SomeSet} +import scala.collection.{mutable, Set => SomeSet} -abstract class Statement[Node] { - def node(): Node +abstract class Statement[C, Node] { + def node(): Node + def callable(): C +} + +case class Dependees[Work]() { + case class Dependee(eOptionP: SomeEOptionP, worklist: Set[Work] = Set.empty) + var dependees = Map.empty[SomeEPK, Dependee] + def get(entity: Entity, propertyKey: PropertyKey[Property])(implicit propertyStore: PropertyStore, work: Work): SomeEOptionP = { + val epk = EPK(entity, propertyKey) + val dependee = dependees.get(epk) match { + case Some(dependee) => Dependee(dependee.eOptionP, dependee.worklist + work) + case None => Dependee(propertyStore(epk), Set(work)) + } + dependees += epk -> dependee + dependee.eOptionP + } + + def forResult(): Set[SomeEOptionP] = { + dependees.values.map(_.eOptionP).toSet + } + def takeWork(epk: SomeEPK): Set[Work] = { + val dependee = dependees(epk) + dependees -= epk + dependee.worklist + } +} + +/** + * Keeps book of the path edges. + * An entry of (statement, fact) means an edge (s0, source fact) -> (statement, fact) exists, + * that is the fact reaches the statement as an input. + * Source fact is the fact within the analysis entity. + * */ +case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[C, _], C]() { + var edges = Map.empty[S, Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] + + /** + * Add the edge (s0, source fact) -> (statement, fact) to the path edges. + * Optionally give a predecessor for the statement. This is used for phi statements + * to distinguish the input flow and merge the facts later. + * @param statement the destination statement of the edge + * @param predecessor the predecessor of the statement. + * @return whether the edge was new + */ + def add(statement: S, fact: IFDSFact, predecessor: Option[S] = None): Boolean = { + // TODO: subsuming + edges.get(statement) match { + case None => + predecessor match { + case Some(predecessor) => + edges = edges.updated(statement, Right(Map(predecessor -> Set(fact)))) + case None => + edges = edges.updated(statement, Left(Set(fact))) + } + true + case Some(Left(existingFacts)) => + if (predecessor.isDefined) throw new IllegalArgumentException(s"${statement} does not accept a predecessor") + val isNew = !existingFacts.contains(fact) + edges = edges.updated(statement, Left(existingFacts + fact)) + isNew + case Some(Right(existingFacts)) => + predecessor match { + case None => throw new IllegalArgumentException(s"${statement} requires a predecessor") + case Some(predecessor) => existingFacts.get(statement) match { + case Some(existingPredecessorFacts) => { + val isNew = !existingPredecessorFacts.contains(fact) + edges = edges.updated(statement, Right(existingFacts.updated(predecessor, existingPredecessorFacts + fact))) + isNew + } + case None => { + edges = edges.updated(statement, Right(existingFacts.updated(predecessor, Set(fact)))) + true + } + } + } + } + } + + /** + * @param statement + * @return The edges reaching statement if any. In case the statement minds about predecessors it is a map with an entry for each predecessor + */ + def get(statement: S): Option[Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] = edges.get(statement) } /** * The state of the analysis. For each method and source fact, there is a separate state. * - * @param source The input fact, for which the `method` is analyzed. - * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input - * facts to the instruction, at which they may be called. - * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input - * facts to the intermediate result of their IFDS analysis. - * Only contains method-fact-pairs, for which this analysis is - * waiting for a final result. - * @param pendingCgCallSites The basic blocks containing call sites, for which the analysis is - * waiting for the final call graph result. - * @param incomingFacts Maps each basic block to the data flow facts valid at its first - * statement. - * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at - * the beginning of the successor. For exit statements the successor is None + * @param source The callable and input fact for which the callable is analyzed. + * @param endSummaries Output facts of the analyzed callable as pairs of exit statement and fact */ -protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( - val source: (C, IFDSFact), - var pendingIfdsCallSites: Map[(C, IFDSFact), Set[S]] = Map.empty[(C, IFDSFact), Set[S]], - var pendingIfdsDependees: Map[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] = Map.empty[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]], - var pendingCgCallSites: Set[Node] = Set.empty[Node], - var incomingFacts: Map[Node, Set[IFDSFact]] = Map.empty[Node, Set[IFDSFact]], - var outgoingFacts: Map[Node, Map[Option[Node], Set[IFDSFact]]] = Map.empty[Node, Map[Option[Node], Set[IFDSFact]]] -) +protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _], Work]( + val source: (C, IFDSFact), + val dependees: Dependees[Work] = Dependees[Work](), + val pathEdges: PathEdges[IFDSFact, S, C] = PathEdges[IFDSFact, S, C](), + var endSummaries: Set[(S, IFDSFact)] = Set.empty[(S, IFDSFact)] + ) /** * Contains int variables, which count, how many times some method was called. */ -class NumberOfCalls { - var normalFlow = 0 - var callFlow = 0 - var returnFlow = 0 - var callToReturnFlow = 0 -} - protected class Statistics { - - /** - * Counts, how many times the abstract methods were called. - */ - var numberOfCalls = new NumberOfCalls() - - /** - * Counts, how many input facts are passed to callbacks. - */ - var sumOfInputfactsForCallbacks = 0L + var normalFlow = 0 + var callFlow = 0 + var returnFlow = 0 + var callToReturnFlow = 0 } protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalysis @@ -68,583 +124,289 @@ protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalys * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. * @tparam IFDSFact */ -class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( - implicit - project: SomeProject, - val ifdsProblem: IFDSProblem[IFDSFact, C, S, Node], - val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact] -) extends ProjectFPCFAnalysis(project) with Subsumable[S, IFDSFact] { - type State = IFDSState[IFDSFact, C, S, Node] - type QueueEntry = (Node, Set[IFDSFact], Option[S], Option[C], Option[IFDSFact]) - - implicit var statistics = new Statistics - val icfg = ifdsProblem.icfg - - /** - * Creates an IFDSProperty containing the result of this analysis. - * - * @param result Maps each exit statement to the facts, which hold after the exit statement. - * @return An IFDSProperty containing the `result`. - */ - protected def createPropertyValue(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] = - propertyKey.create(result) - - /** - * Performs an IFDS analysis for a method-fact-pair. - * - * @param entity The method-fact-pair, that will be analyzed. - * @return An IFDS property mapping from exit statements to the facts valid after these exit - * statements. Returns an interim result, if the TAC or call graph of this method or the - * IFDS analysis for a callee is still pending. - */ - def performAnalysis(entity: (C, IFDSFact)): ProperPropertyComputationResult = { - val (function, sourceFact) = entity - - // Start processing at the start of the cfg with the given source fact - implicit val state: State = - new State(entity, Map(entity -> Set.empty), Map.empty, Set.empty[Node]) - val queue = mutable.Queue - .empty[QueueEntry] - icfg.startNodes(sourceFact, function).foreach { start ⇒ - state.incomingFacts += start -> Set(sourceFact) - queue.enqueue((start, Set(sourceFact), None, None, None)) - } - process(queue) - createResult() +class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]]( + implicit + project: SomeProject, + val ifdsProblem: IFDSProblem[IFDSFact, C, S], + val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact]) extends ProjectFPCFAnalysis(project) { + type Work = (S, IFDSFact, Option[S]) // statement, fact, predecessor + type Worklist = mutable.Queue[Work] + type State = IFDSState[IFDSFact, C, S, Work] + + implicit var statistics = new Statistics + val icfg = ifdsProblem.icfg + + /** + * Performs an IFDS analysis for a method-fact-pair. + * + * @param entity The method-fact-pair, that will be analyzed. + * @return An IFDS property mapping from exit statements to the facts valid after these exit + * statements. Returns an interim result, if the TAC or call graph of this method or the + * IFDS analysis for a callee is still pending. + */ + def performAnalysis(entity: (C, IFDSFact)): ProperPropertyComputationResult = { + val (function, sourceFact) = entity + + // Start processing at the start of the icfg with the given source fact + implicit val state: State = new IFDSState[IFDSFact, C, S, Work](entity) + implicit val queue: Worklist = mutable.Queue + .empty[Work] + icfg.startStatements(function).foreach { start ⇒ + state.pathEdges.add(start, sourceFact) // ifds line 2 + queue.enqueue((start, sourceFact, None)) // ifds line 3 } - - /** - * Creates the current (intermediate) result for the analysis. - * - * @return A result containing a map, which maps each exit statement to the facts valid after - * the statement, based on the current results. If the analysis is still waiting for its - * method's TAC or call graph or the result of another method-fact-pair, an interim - * result will be returned. - * - */ - protected def createResult()(implicit state: State): ProperPropertyComputationResult = { - val propertyValue = createPropertyValue(collectResult) - val dependees = state.pendingIfdsDependees.values - if (dependees.isEmpty) Result(state.source, propertyValue) - else InterimResult.forUB(state.source, propertyValue, dependees.toSet, propertyUpdate) - } - - /** - * Called, when there is an updated result for a tac, call graph or another method-fact-pair, - * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and - * returns the new analysis result. - * - * @param eps The new property value. - * @return The new (interim) result of this analysis. - */ - protected def propertyUpdate( - eps: SomeEPS - )(implicit state: State): ProperPropertyComputationResult = { - (eps: @unchecked) match { - case FinalE(e: (C, IFDSFact) @unchecked) ⇒ - reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - - case interimEUBP @ InterimEUBP( - e: (C, IFDSFact) @unchecked, - ub: IFDSProperty[S, IFDSFact] @unchecked - ) ⇒ - if (ub.flows.values - .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { - // Do not re-analyze the caller if we only get the null fact. - // Update the pendingIfdsDependee entry to the new interim result. - state.pendingIfdsDependees += - e -> interimEUBP - .asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] - } else - reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - - case FinalEP(_: C @unchecked, _: Any) ⇒ - // TODO: Any was Callees, how to verify this? - reAnalyzeBasicBlocks(state.pendingCgCallSites) - - case InterimEUBP(_: C @unchecked, _: Any) ⇒ - // TODO: Any was Callees, how to verify this? - reAnalyzeBasicBlocks(state.pendingCgCallSites) - } - - createResult() + process() + createResult() + } + + /** + * Creates the current (intermediate) result for the analysis. + * + * @return A result containing a map, which maps each exit statement to the facts valid after + * the statement, based on the current results. If the analysis is still waiting for its + * method's TAC or call graph or the result of another method-fact-pair, an interim + * result will be returned. + * + */ + private def createResult()(implicit state: State): ProperPropertyComputationResult = { + val propertyValue = createPropertyValue(collectResult) + val dependees = state.dependees.forResult + if (dependees.isEmpty) Result(state.source, propertyValue) + else InterimResult.forUB(state.source, propertyValue, dependees, propertyUpdate) + } + + /** + * Creates an IFDSProperty containing the result of this analysis. + * + * @param result Maps each exit statement to the facts, which hold after the exit statement. + * @return An IFDSProperty containing the `result`. + */ + private def createPropertyValue(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] = + propertyKey.create(result) + + /** + * Collects the facts valid at all exit nodes based on the current results. + * + * @return A map, mapping from each exit statement to the facts, which hold at + * after the exit statement. + */ + private def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { + var result = Map.empty[S, Set[IFDSFact]] + for {entry <- state.endSummaries } { + result.get(entry._1) match { + case Some(existingFacts) => result = result.updated(entry._1, existingFacts + entry._2) + case None => result = result.updated(entry._1, Set(entry._2)) + } } - - /** - * Processes a statement with a call. - * - * @param basicBlock The basic block, which contains the `call`. - * @param call The call statement. - * @param callees All possible callees. - * @param in The facts, which hold before the call statement. - * @param calleeWithUpdateFact If present, the `callees` will only be analyzed with this fact - * instead of the facts returned by callToStartFacts. - * @return A map, mapping from each successor statement of the `call` to the facts, which hold - * at their start. - */ - protected def handleCall( - basicBlock: Node, - call: S, - callees: SomeSet[C], - in: Set[IFDSFact], - calleeWithUpdateFact: Option[IFDSFact] - )( - implicit - state: State - ): Map[S, Set[IFDSFact]] = { - val successors = icfg.nextStatements(call) - // Facts valid at the start of each successor - var summaryEdges: Map[S, Set[IFDSFact]] = Map.empty - - /* - * If calleeWithUpdateFact is present, this means that the basic block already has been - * analyzed with the `inputFacts`. - */ - if (calleeWithUpdateFact.isEmpty) - for (successor ← successors) { - statistics.numberOfCalls.callToReturnFlow += 1 - statistics.sumOfInputfactsForCallbacks += in.size - summaryEdges += successor -> - propagateNullFact( - in, - ifdsProblem.callToReturnFlow(call, successor, in, state.source) - ) - } - - for (callee ← callees) { - ifdsProblem.outsideAnalysisContext(callee) match { - case Some(handler) ⇒ - // Let the concrete analysis decide what to do. - for { - successor ← successors - } summaryEdges += - successor -> (summaryEdges(successor) ++ - handler(call, successor, in)) - case None ⇒ - val callToStart = - if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) - else { - propagateNullFact(in, callToStartFacts(call, callee, in)) - } - var allNewExitFacts: Map[S, Set[IFDSFact]] = Map.empty - // Collect exit facts for each input fact separately - for (fact ← callToStart) { - /* - * If this is a recursive call with the same input facts, we assume that the - * call only produces the facts that are already known. The call site is added to - * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts - * become known for the input fact. - */ - if ((callee eq state.source._1) && fact == state.source._2) { - val newDependee = - if (state.pendingIfdsCallSites.contains(state.source)) - state.pendingIfdsCallSites(state.source) + call - else Set(call) - state.pendingIfdsCallSites = - state.pendingIfdsCallSites.updated(state.source, newDependee) - allNewExitFacts = IFDS.mergeMaps(allNewExitFacts, collectResult) - } else { - val e = (callee, fact) - val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] - val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[S, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]]) ⇒ ep.ub.flows - case _ ⇒ Map.empty - } - val exitFacts: Map[S, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ - if (state.pendingIfdsCallSites.contains(e) - && state.pendingIfdsCallSites(e).nonEmpty) { - val newDependee = - state.pendingIfdsCallSites(e) - call - state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) - } - state.pendingIfdsDependees -= e - ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ - /* - * Add the call site to `pendingIfdsCallSites` and - * `pendingIfdsDependees` and continue with the facts in the interim - * result for now. When the analysis for the callee finishes, the - * analysis for this call site will be triggered again. - */ - addIfdsDependee(e, callFlows, basicBlock, call) - ep.ub.flows - case _ ⇒ - addIfdsDependee(e, callFlows, basicBlock, call) - Map.empty - } - // Only process new facts that are not in `oldExitFacts` - allNewExitFacts = IFDS.mergeMaps( - allNewExitFacts, - filterNewInformation(exitFacts, oldExitFacts, project) - ) - /* - * If new exit facts were discovered for the callee-fact-pair, all call - * sites depending on this pair have to be re-evaluated. oldValue is - * undefined if the callee-fact pair has not been queried before or returned - * a FinalEP. - */ - if (oldValue.isDefined && oldExitFacts != exitFacts) { - reAnalyzeCalls( - state.pendingIfdsCallSites(e), - e._1, - Some(e._2) - ) - } - } - } - summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) - } - } - summaryEdges + result + } + + /** + * Called, when there is an updated result for a tac, call graph or another method-fact-pair, + * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and + * returns the new analysis result. + * + * @param eps The new property value. + * @return The new (interim) result of this analysis. + */ + private def propertyUpdate( + eps: SomeEPS + )(implicit state: State): ProperPropertyComputationResult = { + implicit val queue: mutable.Queue[Work] = mutable.Queue() + state.dependees.takeWork(eps.toEPK).foreach(queue.enqueue(_)) + process() + createResult() + } + + /** + * Analyzes a queue of BasicBlocks. + * + * @param worklist + */ + private def process()(implicit state: State, worklist: Worklist): Unit = { + while (worklist.nonEmpty) { // ifds line 10 + implicit val work = worklist.dequeue() // ifds line 11 + val (statement, in, predecessor) = work + icfg.getCalleesIfCallStatement(statement) match { + case Some(callees) => handleCall(statement, callees, in) // ifds line 13 + case None => if (icfg.isExitStatement(statement)) handleExit(statement, in) // ifds line 21 + else handleOther(statement, in, predecessor) // ifds line 33 + } } - - /** - * Collects the facts valid at all exit nodes based on the current results. - * - * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at - * the exit node under the assumption that the predecessor was executed before. - */ - protected def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { - var result = Map.empty[S, Set[IFDSFact]] - state.outgoingFacts.foreach( - blockFacts ⇒ - blockFacts._2.get(None) match { - case Some(facts) ⇒ result += icfg.lastStatement(blockFacts._1) -> facts - case None ⇒ - } - ) - result - } - - /** - * Calls callFlow. - */ - protected def callToStartFacts(call: S, callee: C, in: Set[IFDSFact])( - implicit - state: State - ): Set[IFDSFact] = { - statistics.numberOfCalls.callFlow += 1 - statistics.sumOfInputfactsForCallbacks += in.size - ifdsProblem.callFlow(call, callee, in, state.source) - } - - /** - * Combines each normal exit node with each normal successor and each abnormal exit statement - * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. - */ - protected def addExitToReturnFacts( - summaryEdges: Map[S, Set[IFDSFact]], - successors: Set[S], - call: S, - callee: C, - exitFacts: Map[S, Set[IFDSFact]] - ): Map[S, Set[IFDSFact]] = { - // First process for normal returns, then abnormal returns. - var result = summaryEdges - for { + } + + /** + * Processes a statement with a call. + * + * @param call The call statement. + * @param callees All possible callees. + * @param in The facts, which hold before the call statement. + * instead of the facts returned by callToStartFacts. + * @return A map, mapping from each successor statement of the `call` to the facts, which hold + * at their start. + */ + private def handleCall(call: S, + callees: SomeSet[C], + in: IFDSFact, + )( + implicit + state: State, + worklist: Worklist, + work: Work + ): Unit = { + val successors = icfg.nextStatements(call) + for (callee ← callees) { + ifdsProblem.outsideAnalysisContext(callee) match { + case Some(outsideAnalysisHandler) ⇒ + // Let the concrete analysis decide what to do. + for { successor ← successors - exitStatement ← exitFacts.keys - } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) - result + out <- outsideAnalysisHandler(call, successor, in) // ifds line 17 (only summary edges) + } { + propagate(successor, out, call) // ifds line 18 + } + case None ⇒ + for { + successor <- successors + out <- concreteCallFlow(call, callee, in) // ifds line 17 (only summary edges) + } { + propagate(successor, out, call) // ifds line 18 + } + } + + for { + successor ← successors + out <- callToReturnFlow(call, in) // ifds line 17 (without summary edge propagation) + } { + propagate(successor, out, call) // ifds line 18 + } } - - /** - * Analyzes a queue of BasicBlocks. - * - * @param worklist A queue of the following elements: - * bb The basic block that will be analyzed. - * in New data flow facts found to hold at the beginning of the basic block. - * calleeWithUpdateIndex If the basic block is analyzed because there is new information - * for a callee, this is the call site's index. - * calleeWithUpdate If the basic block is analyzed because there is new information for a - * callee, this is the callee. - * calleeWithUpdateFact If the basic block is analyzed because there is new information - * for a callee with a specific input fact, this is the input fact. - */ - private def process( - worklist: mutable.Queue[QueueEntry] - )(implicit state: State): Unit = { - while (worklist.nonEmpty) { - val (basicBlock, in, calleeWithUpdateSite, calleeWithUpdate, calleeWithUpdateFact) = - worklist.dequeue() - val oldOut = state.outgoingFacts.getOrElse(basicBlock, Map.empty) - val nextOut = analyzeBasicBlock( - basicBlock, - in, - calleeWithUpdateSite, - calleeWithUpdate, - calleeWithUpdateFact - ) - val allOut = IFDS.mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) - state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) - - for (successor ← icfg.nextNodes(basicBlock)) { - if (icfg.isLastNode(successor)) { - // Re-analyze recursive call sites with the same input fact. - val nextOutSuccessors = nextOut.get(Some(successor)) - if (nextOutSuccessors.isDefined && nextOutSuccessors.get.nonEmpty) { - val oldOutSuccessors = oldOut.get(Some(successor)) - if (oldOutSuccessors.isEmpty || containsNewInformation( - nextOutSuccessors.get, - oldOutSuccessors.get, - project - )) { - val source = state.source - reAnalyzeCalls( - state.pendingIfdsCallSites(source), - source._1, - Some(source._2) - ) - } - } - } else { - val successorBlock = successor - val nextIn = nextOut.getOrElse(Some(successorBlock), Set.empty) - val oldIn = state.incomingFacts.getOrElse(successorBlock, Set.empty) - val newIn = notSubsumedBy(nextIn, oldIn, project) - val mergedIn = - if (nextIn.size > oldIn.size) nextIn ++ oldIn - else oldIn ++ nextIn - state.incomingFacts = - state.incomingFacts.updated(successorBlock, subsume(mergedIn, project)) - /* - * Only process the successor with new facts. - * It is analyzed at least one time because of the null fact. - */ - if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None, None)) - } - } - } + } + + private def concreteCallFlow(call: S, callee: C, in: IFDSFact)(implicit state: State, work: Work): Set[IFDSFact] = { + var result = Set.empty[IFDSFact] + for (entryFact ← callFlow(call, callee, in)) { // ifds line 14 + val e = (callee, entryFact) + val callFlows = state.dependees.get(e, propertyKey.key).asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] + val exitFacts: Map[S, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ + ep.ub.flows + case _ ⇒ + Map.empty + } + for { + (exitStatement, exitStatementFacts) <- exitFacts // ifds line 15.2 + exitStatementFact <- exitStatementFacts // ifds line 15.3 + } { + result ++= returnFlow(exitStatement, exitStatementFact, call, in) + } } - - /** - * Computes for one basic block the facts valid on each CFG edge leaving the block if `sources` - * held before the block. - * - * @param basicBlock The basic block, that will be analyzed. - * @param in The facts, that hold before the block. - * @param calleeWithUpdateSite If the basic block is analyzed because there is new information - * for a callee, this is the call site. - * @param calleeWithUpdate If the basic block is analyzed because there is new information for - * a callee, this is the callee. - * @param calleeWithUpdateFact If the basic block is analyzed because there is new information - * for a callee with a specific input fact, this is the input fact. - * @return A map, mapping each successor node to its input facts. Instead of catch nodes, this - * map contains their handler nodes. - */ - private def analyzeBasicBlock( - basicBlock: Node, - in: Set[IFDSFact], - calleeWithUpdateSite: Option[S], - calleeWithUpdate: Option[C], - calleeWithUpdateFact: Option[IFDSFact] - )( - implicit - state: State - ): Map[Option[Node], Set[IFDSFact]] = { - - /* - * Collects information about a statement. - * - * @param index The statement's index. - * @return A tuple of the following elements: - * statement: The statement at `index`. - * callees: The methods possibly called at this statement, if it contains a call. - * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will - * be returned. - * calleeFact: If `index` equals `calleeWithUpdateIndex`, only - * `calleeWithUpdateFact` will be returned, None otherwise. - */ - def collectInformation(statement: S): (Option[SomeSet[C]], Option[IFDSFact]) = { - val calleesO = - if (calleeWithUpdateSite.contains(statement)) calleeWithUpdate.map(Set(_)) - else icfg.getCalleesIfCallStatement(statement) - val calleeFact = - if (calleeWithUpdateSite.contains(statement)) calleeWithUpdateFact - else None - (calleesO, calleeFact) - } - - val last = icfg.lastStatement(basicBlock) - var flows: Set[IFDSFact] = in - var statement: S = icfg.firstStatement(basicBlock) - - // Iterate over all statements but the last one, only keeping the resulting DataFlowFacts. - while (statement != last) { - val (calleesO, calleeFact) = collectInformation(statement) - val successor = icfg.nextStatement(statement) - flows = if (calleesO.isEmpty) { - statistics.numberOfCalls.normalFlow += 1 - statistics.sumOfInputfactsForCallbacks += in.size - ifdsProblem.normalFlow(statement, Some(successor), flows) - } else - // Inside a basic block, we only have one successor --> Take the head - handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head - statement = successor - } - - // Analyze the last statement for each possible successor statement. - val (calleesO, callFact) = collectInformation(last) - var result: Map[Option[Node], Set[IFDSFact]] = - if (calleesO.isEmpty) { - var result: Map[Option[Node], Set[IFDSFact]] = Map.empty - for (node ← icfg.nextNodes(basicBlock)) { - statistics.numberOfCalls.normalFlow += 1 - statistics.sumOfInputfactsForCallbacks += in.size - result += Some(node) -> ifdsProblem.normalFlow( - statement, - Some(icfg.firstStatement(node)), - flows - ) - } - if (icfg.isExitStatement(last)) { - statistics.numberOfCalls.normalFlow += 1 - statistics.sumOfInputfactsForCallbacks += in.size - result += None -> ifdsProblem.normalFlow(statement, None, flows) - } - result - } else - handleCall(basicBlock, statement, calleesO.get, flows, callFact) - .map(entry ⇒ Some(entry._1.node) -> entry._2) - - // Propagate the null fact. - result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) - result + result + } + + private def handleExit(statement: S, in: IFDSFact)(implicit state: State): Unit = { + state.endSummaries += ((statement, in)) // ifds line 21.1 + // ifds lines 22 - 31 are handled by the dependency propagation of the property store + } + + private def handleOther(statement: S, in: IFDSFact, predecessor: Option[S])(implicit state: State, worklist: Worklist): Unit = { + for { // ifds line 34 + successor <- icfg.nextStatements(statement) + out <- normalFlow(statement, in, predecessor) + } { + propagate(successor, out, statement) // ifds line 35 } + } - /** - * Re-analyzes some basic blocks. - * - * @param basicBlocks The basic blocks, that will be re-analyzed. - */ - private def reAnalyzeBasicBlocks(basicBlocks: Set[Node])(implicit state: State): Unit = { - val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty - for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) - process(queue) - } + private def propagate(successor: S, out: IFDSFact, predecessor: S)(implicit state: State, worklist: Worklist): Unit = { + val predecessorOption = if (ifdsProblem.needsPredecessor(successor)) Some(predecessor) else None - /** - * Re-analyzes some call sites with respect to one specific callee. - * - * @param callSites The call sites, which are analyzed. - * @param callee The callee, which will be considered at the `callSites`. - * @param fact If defined, the `callee` will only be analyzed for this fact. - */ - private def reAnalyzeCalls( - callSites: Set[S], - callee: C, - fact: Option[IFDSFact] - )(implicit state: State): Unit = { - val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty - for (callSite ← callSites) { - val node = callSite.node - queue.enqueue((node, state.incomingFacts(node), Some(callSite), Some(callee), fact)) - } - process(queue) - } - - /** - * If `from` contains a null fact, it will be added to `to`. - * - * @param from The set, which may contain the null fact initially. - * @param to The set, to which the null fact may be added. - * @return `to` with the null fact added, if it is contained in `from`. - */ - private def propagateNullFact(from: Set[IFDSFact], to: Set[IFDSFact]): Set[IFDSFact] = { - if (from.contains(ifdsProblem.nullFact)) to + ifdsProblem.nullFact - else to - } - - /** - * Adds a method-fact-pair as to the IFDS call sites and dependees. - * - * @param entity The method-fact-pair. - * @param calleeProperty The property, that was returned for `entity`. - * @param callBB The basic block of the call site. - * @param call The call site. - */ - private def addIfdsDependee( - entity: (C, IFDSFact), - calleeProperty: EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]], - callBB: Node, - call: S - )(implicit state: State): Unit = { - val callSites = state.pendingIfdsCallSites - state.pendingIfdsCallSites = callSites.updated( - entity, - callSites.getOrElse(entity, Set.empty) + call - ) - state.pendingIfdsDependees += entity -> calleeProperty - } - - /** - * Adds a summary edge for a call to a map representing summary edges. - * - * @param summaryEdges The current map representing the summary edges. - * Maps from successor statements to facts, which hold at their beginning. - * @param call The call, calling the `callee`. - * @param exitStatement The exit statement for the new summary edge. - * @param successor The successor statement of the call for the new summary edge. - * @param callee The callee, called by `call`. - * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly - * found exit facts. - * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. - */ - private def addSummaryEdge( - summaryEdges: Map[S, Set[IFDSFact]], - call: S, - exitStatement: S, - successor: S, - callee: C, - allNewExitFacts: Map[S, Set[IFDSFact]] - ): Map[S, Set[IFDSFact]] = { - val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) - statistics.numberOfCalls.returnFlow += 1 - statistics.sumOfInputfactsForCallbacks += in.size - val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) - val newFacts = - if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { - val summaryForSuccessor = summaryEdges(successor) - if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned - else returned ++ summaryForSuccessor - } else returned - summaryEdges.updated(successor, newFacts) + // ifds line 9 + if (state.pathEdges.add(successor, out, predecessorOption)) { + worklist.enqueue((successor, out, predecessorOption)) } + } + + /** + * ifds flow function + * @param statement n + * @param in d2 + * @param predecessor pi + */ + private def normalFlow(statement: S, in: IFDSFact, predecessor: Option[S]): Set[IFDSFact] = { + statistics.normalFlow += 1 + ifdsProblem.normalFlow(statement, in, predecessor) + } + + /** + * ifds passArgs function + * @param call n + * @param callee + * @param in d2 + * @return + */ + private def callFlow(call: S, callee: C, in: IFDSFact): Set[IFDSFact] = { + statistics.callFlow += 1 + ifdsProblem.callFlow(call, callee, in) + } + + /** + * ifds returnVal function + * @param exit n + * @param in d2 + * @param call c + * @param callFact d4 + * @return + */ + private def returnFlow(exit: S, in: IFDSFact, call: S, callFact: IFDSFact): Set[IFDSFact] = { + statistics.returnFlow += 1 + ifdsProblem.returnFlow(exit, in, call, callFact) + } + + /** + * ifds callFlow function + * @param call n + * @param in d2 + * @return + */ + private def callToReturnFlow(call: S, in: IFDSFact): Set[IFDSFact] = { + statistics.callToReturnFlow += 1 + ifdsProblem.callToReturnFlow(call, in) + } } -abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] - extends FPCFLazyAnalysisScheduler { - final override type InitializationData = IFDSAnalysis[IFDSFact, C, S, Node] - def property: IFDSPropertyMetaInformation[S, IFDSFact] - final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - - /** - * Registers the analysis as a lazy computation, that is, the method - * will call `ProperytStore.scheduleLazyComputation`. - */ - override def register( - p: SomeProject, - ps: PropertyStore, - analysis: IFDSAnalysis[IFDSFact, C, S, Node] - ): FPCFAnalysis = { - ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) - analysis - } - - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { - val ifdsAnalysis = analysis.asInstanceOf[IFDSAnalysis[IFDSFact, C, S, Node]] - for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { - ps.force(e, ifdsAnalysis.propertyKey.key) - } +abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]] + extends FPCFLazyAnalysisScheduler { + final override type InitializationData = IFDSAnalysis[IFDSFact, C, S] + def property: IFDSPropertyMetaInformation[S, IFDSFact] + final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + + /** + * Registers the analysis as a lazy computation, that is, the method + * will call `ProperytStore.scheduleLazyComputation`. + */ + override def register( + p: SomeProject, + ps: PropertyStore, + analysis: IFDSAnalysis[IFDSFact, C, S] + ): FPCFAnalysis = { + ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) + analysis + } + + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { + val ifdsAnalysis = analysis.asInstanceOf[IFDSAnalysis[IFDSFact, C, S]] + for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { + ps.force(e, ifdsAnalysis.propertyKey.key) } + } - override def afterPhaseCompletion( - p: SomeProject, - ps: PropertyStore, - analysis: FPCFAnalysis - ): Unit = {} + override def afterPhaseCompletion( + p: SomeProject, + ps: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala index 588d2d3e2e..a41db42ba9 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -3,124 +3,108 @@ package org.opalj.ifds import org.opalj.fpcf.ProperPropertyComputationResult -/** - * The supertype of all IFDS facts. - */ -trait AbstractIFDSFact - -/** - * The super type of all null facts. - */ -trait AbstractIFDSNullFact extends AbstractIFDSFact - /** * A framework for IFDS analyses. * * @tparam IFDSFact The type of flow facts, which are tracked by the concrete analysis. * @author Dominik Helm * @author Mario Trageser + * @author Marc Clement */ -abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node](val icfg: ICFG[IFDSFact, C, S, Node]) { - /** - * The null fact of this analysis. - */ - def nullFact: IFDSFact +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]](val icfg: ICFG[IFDSFact, C, S]) { + /** + * The null fact of this analysis. + */ + def nullFact: IFDSFact + + /** + * The entry points of this analysis. + */ + def entryPoints: Seq[(C, IFDSFact)] - /** - * The entry points of this analysis. - */ - def entryPoints: Seq[(C, IFDSFact)] + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param in The fact which holds before the execution of the `statement`. + * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be + * computed. Used for phi statements to distinguish the flow. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + def normalFlow( + statement: S, + in: IFDSFact, + predecessor: Option[S], + ): Set[IFDSFact] - /** - * Computes the data flow for a normal statement. - * - * @param statement The analyzed statement. - * @param successor The successor of the analyzed `statement`, for which the data flow shall be - * computed. It is not present for exit statements. - * @param in The facts, which hold before the execution of the `statement`. - * @return The facts, which hold after the execution of `statement` under the assumption - * that the facts in `in` held before `statement` and `successor` will be - * executed next. - */ - def normalFlow( - statement: S, - successor: Option[S], - in: Set[IFDSFact] - ): Set[IFDSFact] + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + def callFlow( + call: S, + callee: C, + in: IFDSFact + ): Set[IFDSFact] - /** - * Computes the data flow for a call to start edge. - * - * @param call The analyzed call statement. - * @param callee The called method, for which the data flow shall be computed. - * @param in The facts, which hold before the execution of the `call`. - * @param source The entity, which is analyzed. - * @return The facts, which hold after the execution of `statement` under the assumption that - * the facts in `in` held before `statement` and `statement` calls `callee`. - */ - def callFlow( - call: S, - callee: C, - in: Set[IFDSFact], - source: (C, IFDSFact) - ): Set[IFDSFact] + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + def returnFlow( + exit: S, + in: IFDSFact, + call: S, + callFact: IFDSFact + ): Set[IFDSFact] - /** - * Computes the data flow for an exit to return edge. - * - * @param call The statement, which called the `callee`. - * @param callee The method called by `call`, for which the data flow shall be computed. - * @param exit The statement, which terminated the `calle`. - * @param successor The statement of the caller, which will be executed after the `callee` - * returned. - * @param in The facts, which hold before the execution of the `exit`. - * @return The facts, which hold after the execution of `exit` in the caller's context - * under the assumption that `in` held before the execution of `exit` and that - * `successor` will be executed next. - */ - def returnFlow( - call: S, - callee: C, - exit: S, - successor: S, - in: Set[IFDSFact] - ): Set[IFDSFact] + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param in The facts, which hold before the `call`. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + def callToReturnFlow( + call: S, + in: IFDSFact + ): Set[IFDSFact] - /** - * Computes the data flow for a call to return edge. - * - * @param call The statement, which invoked the call. - * @param successor The statement, which will be executed after the call. - * @param in The facts, which hold before the `call`. - * @param source The entity, which is analyzed. - * @return The facts, which hold after the call independently of what happens in the callee - * under the assumption that `in` held before `call`. - */ - def callToReturnFlow( - call: S, - successor: S, - in: Set[IFDSFact], - source: (C, IFDSFact) - ): Set[IFDSFact] + def needsPredecessor(statement: S): Boolean - type OutsideAnalysisContextHandler = ((S, S, Set[IFDSFact]) ⇒ Set[IFDSFact]) { - def apply(call: S, successor: S, in: Set[IFDSFact]): Set[IFDSFact] - } + type OutsideAnalysisContextHandler = ((S, S, IFDSFact) ⇒ Set[IFDSFact]) { + def apply(call: S, successor: S, in: IFDSFact): Set[IFDSFact] + } - /** - * Checks, if a callee is outside this analysis' context. - * By default, native methods are not inside the analysis context. - * For callees outside this analysis' context the returned handler is called - * to compute the summary edge for the call instead of analyzing the callee. - * - * @param callee The method called by `call`. - * @return The handler function. It receives - * the statement which invoked the call, - * the successor statement, which will be executed after the call and - * the set of input facts which hold before the `call`. - * It returns facts, which hold after the call, excluding the call to return flow. - */ - def outsideAnalysisContext(callee: C): Option[OutsideAnalysisContextHandler] + /** + * Checks, if a callee is outside this analysis' context. + * By default, native methods are not inside the analysis context. + * For callees outside this analysis' context the returned handler is called + * to compute the summary edge for the call instead of analyzing the callee. + * + * @param callee The method called by `call`. + * @return The handler function. It receives + * the statement which invoked the call, + * the successor statement, which will be executed after the call and + * the set of input facts which hold before the `call`. + * It returns facts, which hold after the call, excluding the call to return flow. + */ + def outsideAnalysisContext(callee: C): Option[OutsideAnalysisContextHandler] - def specialCase(source: (C, IFDSFact), propertyKey: IFDSPropertyMetaInformation[S, IFDSFact]): Option[ProperPropertyComputationResult] = None + def specialCase(source: (C, IFDSFact), propertyKey: IFDSPropertyMetaInformation[S, IFDSFact]): Option[ProperPropertyComputationResult] = None } \ No newline at end of file diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/Subsumable.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/Subsumable.scala deleted file mode 100644 index ef4e6e24d9..0000000000 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/Subsumable.scala +++ /dev/null @@ -1,105 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ifds - -import org.opalj.br.analyses.SomeProject - -/** - * The supertype of all IFDS facts, which can subsume another fact. - */ -trait SubsumableFact extends AbstractIFDSFact { - - /** - * Checks, if this fact subsumes an `other` fact. - * - * @param other The other fact. - * @param project The analyzed project. - * @return True, if this fact subsumes the `other`fact - */ - def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean -} - -/** - * The null fact for subsumable facts. - */ -trait SubsumableNullFact extends SubsumableFact with AbstractIFDSNullFact { - - /** - * The null fact cannot subsume another fact. - */ - override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = false -} - -/** - * Defines functions, which can be overwritten to implement subsuming. - * - * @author Mario Trageser - */ -trait Subsumable[S, IFDSFact <: AbstractIFDSFact] { - - /** - * A subclass can override this method to filter the `facts` in some set, which are not subsumed - * by another fact in the set. - * - * - * @param facts The set of facts. - * @param project The project, which is analyzed. - * @return The facts, which are not subsumed by any other fact in `facts`. - * By default, `facts` is returned without removing anything. - */ - protected def subsume[T <: IFDSFact](facts: Set[T], project: SomeProject): Set[T] = facts - - /** - * Checks, if any fact from some set is not equal to or subsumed by any fact in another set. - * A subclass implementing subsuming must overwrite this method to consider subsuming. - * - * @param newFacts The facts, which were found. Not empty. - * @param oldFacts The facts, which are already known. Not empty. - * @param project The project, which is analyzed. - * @return True, if any fact in `newFacts`, is not equal to or subsumed by any fact in - * `oldFacts`. - */ - protected def containsNewInformation[T <: IFDSFact]( - newFacts: Set[T], - oldFacts: Set[T], - project: SomeProject - ): Boolean = - newFacts.exists(newFact ⇒ !oldFacts.contains(newFact)) - - /** - * Filters the new information from a new set of exit facts given the already known exit facts. - * A subclass implementing subsuming must overwrite this method to consider subsuming. - * - * @param newExitFacts The new exit facts. - * @param oldExitFacts The old exit facts. - * @param project The project, which is analyzed. - * @return A map, containing the keys of `newExitFacts`. - * Facts, which are equal to or subsumed by any fact for the same statement in - * `oldExitFacts` are not present. - */ - protected def filterNewInformation[T <: IFDSFact]( - newExitFacts: Map[S, Set[T]], - oldExitFacts: Map[S, Set[T]], - project: SomeProject - ): Map[S, Set[T]] = { - var result = newExitFacts - for ((key, values) ← oldExitFacts) { - result = result.updated(key, result(key) -- values) - } - result - } - - /** - * Filters the `facts` from some set, which are not equal to or subsumed by any fact in an - * `other` set. - * A subclass implementing subsuming must overwrite this method to consider subsuming. - * - * @param facts The set, from which facts are filtered. - * @param otherFacts The set, which may subsume facts from `facts`. - * @param project The project, which is analyzed. - * @return The facts from `facts`, which are not equal to or subsumed by any fact in an - * `otherFacts`. - */ - protected def notSubsumedBy[T <: IFDSFact](facts: Set[T], otherFacts: Set[T], - project: SomeProject): Set[T] = - facts -- otherFacts -} diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala new file mode 100644 index 0000000000..be874b174e --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala @@ -0,0 +1,81 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ifds.old + +import org.opalj.ifds.AbstractIFDSFact + +import scala.collection.{Set => SomeSet} + +abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] { + /** + * Determines the basic blocks, at which the analysis starts. + * + * @param sourceFact The source fact of the analysis. + * @param callable The analyzed callable. + * @return The basic blocks, at which the analysis starts. + */ + def startNodes(sourceFact: IFDSFact, callable: C): Set[Node] + + /** + * Determines the nodes, that will be analyzed after some `node`. + * + * @param node The basic block, that was analyzed before. + * @return The nodes, that will be analyzed after `node`. + */ + def nextNodes(node: Node): Set[Node] + + /** + * Checks, if some `node` is the last node. + * + * @return True, if `node` is the last node, i.e. there is no next node. + */ + def isLastNode(node: Node): Boolean + + /** + * Determines the first index of some `basic block`, that will be analyzed. + * + * @param node The basic block. + * @return The first index of some `basic block`, that will be analyzed. + */ + def firstStatement(node: Node): S + + /** + * Determines the last index of some `basic block`, that will be analzyed. + * + * @param node The basic block. + * @return The last index of some `basic block`, that will be analzyed. + */ + def lastStatement(node: Node): S + + /** + * Determines the statement that will be analyzed after some other statement. + * + * @param statement The current statement. + * @return The statement that will be analyzed after `statement`. + */ + def nextStatement(statement: S): S + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + def nextStatements(statement: S): Set[S] + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + def getCalleesIfCallStatement(statement: S): Option[SomeSet[C]] + + /** + * Determines whether the statement is an exit statement. + * + * @param statement The source statement. + * @return Whether the statement flow may exit its callable (function/method) + */ + def isExitStatement(statement: S): Boolean +} diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala new file mode 100644 index 0000000000..988e54fb90 --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala @@ -0,0 +1,651 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ifds.old + +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} +import org.opalj.fpcf._ +import org.opalj.ifds.{AbstractIFDSFact, IFDS, IFDSProperty, IFDSPropertyMetaInformation} + +import scala.collection.{mutable, Set => SomeSet} + +abstract class Statement[Node] { + def node(): Node +} + +/** + * The state of the analysis. For each method and source fact, there is a separate state. + * + * @param source The input fact, for which the `method` is analyzed. + * @param pendingIfdsCallSites Maps callees of the analyzed `method` together with their input + * facts to the instruction, at which they may be called. + * @param pendingIfdsDependees Maps callees of the analyzed `method` together with their input + * facts to the intermediate result of their IFDS analysis. + * Only contains method-fact-pairs, for which this analysis is + * waiting for a final result. + * @param pendingCgCallSites The basic blocks containing call sites, for which the analysis is + * waiting for the final call graph result. + * @param incomingFacts Maps each basic block to the data flow facts valid at its first + * statement. + * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at + * the beginning of the successor. For exit statements the successor is None + */ +protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( + val source: (C, IFDSFact), + var pendingIfdsCallSites: Map[(C, IFDSFact), Set[S]] = Map.empty[(C, IFDSFact), Set[S]], + var pendingIfdsDependees: Map[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] = Map.empty[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]], + var pendingCgCallSites: Set[Node] = Set.empty[Node], + var incomingFacts: Map[Node, Set[IFDSFact]] = Map.empty[Node, Set[IFDSFact]], + var outgoingFacts: Map[Node, Map[Option[Node], Set[IFDSFact]]] = Map.empty[Node, Map[Option[Node], Set[IFDSFact]]] +) + +/** + * Contains int variables, which count, how many times some method was called. + */ +class NumberOfCalls { + var normalFlow = 0 + var callFlow = 0 + var returnFlow = 0 + var callToReturnFlow = 0 +} + +protected class Statistics { + + /** + * Counts, how many times the abstract methods were called. + */ + var numberOfCalls = new NumberOfCalls() + + /** + * Counts, how many input facts are passed to callbacks. + */ + var sumOfInputfactsForCallbacks = 0L +} + +protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalysis + +/** + * + * @param ifdsProblem + * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. + * @tparam IFDSFact + */ +class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( + implicit + project: SomeProject, + val ifdsProblem: IFDSProblem[IFDSFact, C, S, Node], + val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact] +) extends ProjectFPCFAnalysis(project) with Subsumable[S, IFDSFact] { + type State = IFDSState[IFDSFact, C, S, Node] + type QueueEntry = (Node, Set[IFDSFact], Option[S], Option[C], Option[IFDSFact]) + + implicit var statistics = new Statistics + val icfg = ifdsProblem.icfg + + /** + * Creates an IFDSProperty containing the result of this analysis. + * + * @param result Maps each exit statement to the facts, which hold after the exit statement. + * @return An IFDSProperty containing the `result`. + */ + protected def createPropertyValue(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] = + propertyKey.create(result) + + /** + * Performs an IFDS analysis for a method-fact-pair. + * + * @param entity The method-fact-pair, that will be analyzed. + * @return An IFDS property mapping from exit statements to the facts valid after these exit + * statements. Returns an interim result, if the TAC or call graph of this method or the + * IFDS analysis for a callee is still pending. + */ + def performAnalysis(entity: (C, IFDSFact)): ProperPropertyComputationResult = { + val (function, sourceFact) = entity + + // Start processing at the start of the cfg with the given source fact + implicit val state: State = + new State(entity, Map(entity -> Set.empty), Map.empty, Set.empty[Node]) + val queue = mutable.Queue + .empty[QueueEntry] + icfg.startNodes(sourceFact, function).foreach { start ⇒ + state.incomingFacts += start -> Set(sourceFact) + queue.enqueue((start, Set(sourceFact), None, None, None)) + } + process(queue) + createResult() + } + + /** + * Creates the current (intermediate) result for the analysis. + * + * @return A result containing a map, which maps each exit statement to the facts valid after + * the statement, based on the current results. If the analysis is still waiting for its + * method's TAC or call graph or the result of another method-fact-pair, an interim + * result will be returned. + * + */ + protected def createResult()(implicit state: State): ProperPropertyComputationResult = { + val propertyValue = createPropertyValue(collectResult) + val dependees = state.pendingIfdsDependees.values + if (dependees.isEmpty) Result(state.source, propertyValue) + else InterimResult.forUB(state.source, propertyValue, dependees.toSet, propertyUpdate) + } + + /** + * Called, when there is an updated result for a tac, call graph or another method-fact-pair, + * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and + * returns the new analysis result. + * + * @param eps The new property value. + * @return The new (interim) result of this analysis. + */ + protected def propertyUpdate( + eps: SomeEPS + )(implicit state: State): ProperPropertyComputationResult = { + (eps: @unchecked) match { + case FinalE(e: (C, IFDSFact) @unchecked) ⇒ + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) + + case interimEUBP @ InterimEUBP( + e: (C, IFDSFact) @unchecked, + ub: IFDSProperty[S, IFDSFact] @unchecked + ) ⇒ + if (ub.flows.values + .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { + // Do not re-analyze the caller if we only get the null fact. + // Update the pendingIfdsDependee entry to the new interim result. + state.pendingIfdsDependees += + e -> interimEUBP + .asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] + } else + reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) + + case FinalEP(_: C @unchecked, _: Any) ⇒ + // TODO: Any was Callees, how to verify this? + reAnalyzeBasicBlocks(state.pendingCgCallSites) + + case InterimEUBP(_: C @unchecked, _: Any) ⇒ + // TODO: Any was Callees, how to verify this? + reAnalyzeBasicBlocks(state.pendingCgCallSites) + } + + createResult() + } + + /** + * Processes a statement with a call. + * + * @param basicBlock The basic block, which contains the `call`. + * @param call The call statement. + * @param callees All possible callees. + * @param in The facts, which hold before the call statement. + * @param calleeWithUpdateFact If present, the `callees` will only be analyzed with this fact + * instead of the facts returned by callToStartFacts. + * @return A map, mapping from each successor statement of the `call` to the facts, which hold + * at their start. + */ + protected def handleCall( + basicBlock: Node, + call: S, + callees: SomeSet[C], + in: Set[IFDSFact], + calleeWithUpdateFact: Option[IFDSFact] + )( + implicit + state: State + ): Map[S, Set[IFDSFact]] = { + val successors = icfg.nextStatements(call) + // Facts valid at the start of each successor + var summaryEdges: Map[S, Set[IFDSFact]] = Map.empty + + /* + * If calleeWithUpdateFact is present, this means that the basic block already has been + * analyzed with the `inputFacts`. + */ + if (calleeWithUpdateFact.isEmpty) + for (successor ← successors) { + statistics.numberOfCalls.callToReturnFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + summaryEdges += successor -> + propagateNullFact( + in, + ifdsProblem.callToReturnFlow(call, successor, in, state.source) + ) + } + + for (callee ← callees) { + ifdsProblem.outsideAnalysisContext(callee) match { + case Some(handler) ⇒ + // Let the concrete analysis decide what to do. + for { + successor ← successors + } summaryEdges += + successor -> (summaryEdges(successor) ++ + handler(call, successor, in)) + case None ⇒ + val callToStart = + if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) + else { + propagateNullFact(in, callToStartFacts(call, callee, in)) + } + var allNewExitFacts: Map[S, Set[IFDSFact]] = Map.empty + // Collect exit facts for each input fact separately + for (fact ← callToStart) { + /* + * If this is a recursive call with the same input facts, we assume that the + * call only produces the facts that are already known. The call site is added to + * `pendingIfdsCallSites`, so that it will be re-evaluated if new output facts + * become known for the input fact. + */ + if ((callee eq state.source._1) && fact == state.source._2) { + val newDependee = + if (state.pendingIfdsCallSites.contains(state.source)) + state.pendingIfdsCallSites(state.source) + call + else Set(call) + state.pendingIfdsCallSites = + state.pendingIfdsCallSites.updated(state.source, newDependee) + allNewExitFacts = IFDS.mergeMaps(allNewExitFacts, collectResult) + } else { + val e = (callee, fact) + val callFlows = propertyStore(e, propertyKey.key) + .asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] + val oldValue = state.pendingIfdsDependees.get(e) + val oldExitFacts: Map[S, Set[IFDSFact]] = oldValue match { + case Some(ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]]) ⇒ ep.ub.flows + case _ ⇒ Map.empty + } + val exitFacts: Map[S, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ + if (state.pendingIfdsCallSites.contains(e) + && state.pendingIfdsCallSites(e).nonEmpty) { + val newDependee = + state.pendingIfdsCallSites(e) - call + state.pendingIfdsCallSites = state.pendingIfdsCallSites.updated(e, newDependee) + } + state.pendingIfdsDependees -= e + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ + /* + * Add the call site to `pendingIfdsCallSites` and + * `pendingIfdsDependees` and continue with the facts in the interim + * result for now. When the analysis for the callee finishes, the + * analysis for this call site will be triggered again. + */ + addIfdsDependee(e, callFlows, basicBlock, call) + ep.ub.flows + case _ ⇒ + addIfdsDependee(e, callFlows, basicBlock, call) + Map.empty + } + // Only process new facts that are not in `oldExitFacts` + allNewExitFacts = IFDS.mergeMaps( + allNewExitFacts, + filterNewInformation(exitFacts, oldExitFacts, project) + ) + /* + * If new exit facts were discovered for the callee-fact-pair, all call + * sites depending on this pair have to be re-evaluated. oldValue is + * undefined if the callee-fact pair has not been queried before or returned + * a FinalEP. + */ + if (oldValue.isDefined && oldExitFacts != exitFacts) { + reAnalyzeCalls( + state.pendingIfdsCallSites(e), + e._1, + Some(e._2) + ) + } + } + } + summaryEdges = addExitToReturnFacts(summaryEdges, successors, call, callee, allNewExitFacts) + } + } + summaryEdges + } + + /** + * Collects the facts valid at all exit nodes based on the current results. + * + * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at + * the exit node under the assumption that the predecessor was executed before. + */ + protected def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { + var result = Map.empty[S, Set[IFDSFact]] + state.outgoingFacts.foreach( + blockFacts ⇒ + blockFacts._2.get(None) match { + case Some(facts) ⇒ result += icfg.lastStatement(blockFacts._1) -> facts + case None ⇒ + } + ) + result + } + + /** + * Calls callFlow. + */ + protected def callToStartFacts(call: S, callee: C, in: Set[IFDSFact])( + implicit + state: State + ): Set[IFDSFact] = { + statistics.numberOfCalls.callFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + ifdsProblem.callFlow(call, callee, in, state.source) + } + + /** + * Combines each normal exit node with each normal successor and each abnormal exit statement + * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. + */ + protected def addExitToReturnFacts( + summaryEdges: Map[S, Set[IFDSFact]], + successors: Set[S], + call: S, + callee: C, + exitFacts: Map[S, Set[IFDSFact]] + ): Map[S, Set[IFDSFact]] = { + // First process for normal returns, then abnormal returns. + var result = summaryEdges + for { + successor ← successors + exitStatement ← exitFacts.keys + } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) + result + } + + /** + * Analyzes a queue of BasicBlocks. + * + * @param worklist A queue of the following elements: + * bb The basic block that will be analyzed. + * in New data flow facts found to hold at the beginning of the basic block. + * calleeWithUpdateIndex If the basic block is analyzed because there is new information + * for a callee, this is the call site's index. + * calleeWithUpdate If the basic block is analyzed because there is new information for a + * callee, this is the callee. + * calleeWithUpdateFact If the basic block is analyzed because there is new information + * for a callee with a specific input fact, this is the input fact. + */ + private def process( + worklist: mutable.Queue[QueueEntry] + )(implicit state: State): Unit = { + while (worklist.nonEmpty) { + val (basicBlock, in, calleeWithUpdateSite, calleeWithUpdate, calleeWithUpdateFact) = + worklist.dequeue() + val oldOut = state.outgoingFacts.getOrElse(basicBlock, Map.empty) + val nextOut = analyzeBasicBlock( + basicBlock, + in, + calleeWithUpdateSite, + calleeWithUpdate, + calleeWithUpdateFact + ) + val allOut = IFDS.mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) + state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) + + for (successor ← icfg.nextNodes(basicBlock)) { + if (icfg.isLastNode(successor)) { + // Re-analyze recursive call sites with the same input fact. + val nextOutSuccessors = nextOut.get(Some(successor)) + if (nextOutSuccessors.isDefined && nextOutSuccessors.get.nonEmpty) { + val oldOutSuccessors = oldOut.get(Some(successor)) + if (oldOutSuccessors.isEmpty || containsNewInformation( + nextOutSuccessors.get, + oldOutSuccessors.get, + project + )) { + val source = state.source + reAnalyzeCalls( + state.pendingIfdsCallSites(source), + source._1, + Some(source._2) + ) + } + } + } else { + val successorBlock = successor + val nextIn = nextOut.getOrElse(Some(successorBlock), Set.empty) + val oldIn = state.incomingFacts.getOrElse(successorBlock, Set.empty) + val newIn = notSubsumedBy(nextIn, oldIn, project) + val mergedIn = + if (nextIn.size > oldIn.size) nextIn ++ oldIn + else oldIn ++ nextIn + state.incomingFacts = + state.incomingFacts.updated(successorBlock, subsume(mergedIn, project)) + /* + * Only process the successor with new facts. + * It is analyzed at least one time because of the null fact. + */ + if (newIn.nonEmpty) worklist.enqueue((successorBlock, newIn, None, None, None)) + } + } + } + } + + /** + * Computes for one basic block the facts valid on each CFG edge leaving the block if `sources` + * held before the block. + * + * @param basicBlock The basic block, that will be analyzed. + * @param in The facts, that hold before the block. + * @param calleeWithUpdateSite If the basic block is analyzed because there is new information + * for a callee, this is the call site. + * @param calleeWithUpdate If the basic block is analyzed because there is new information for + * a callee, this is the callee. + * @param calleeWithUpdateFact If the basic block is analyzed because there is new information + * for a callee with a specific input fact, this is the input fact. + * @return A map, mapping each successor node to its input facts. Instead of catch nodes, this + * map contains their handler nodes. + */ + private def analyzeBasicBlock( + basicBlock: Node, + in: Set[IFDSFact], + calleeWithUpdateSite: Option[S], + calleeWithUpdate: Option[C], + calleeWithUpdateFact: Option[IFDSFact] + )( + implicit + state: State + ): Map[Option[Node], Set[IFDSFact]] = { + + /* + * Collects information about a statement. + * + * @param index The statement's index. + * @return A tuple of the following elements: + * statement: The statement at `index`. + * callees: The methods possibly called at this statement, if it contains a call. + * If `index` equals `calleeWithUpdateIndex`, only `calleeWithUpdate` will + * be returned. + * calleeFact: If `index` equals `calleeWithUpdateIndex`, only + * `calleeWithUpdateFact` will be returned, None otherwise. + */ + def collectInformation(statement: S): (Option[SomeSet[C]], Option[IFDSFact]) = { + val calleesO = + if (calleeWithUpdateSite.contains(statement)) calleeWithUpdate.map(Set(_)) + else icfg.getCalleesIfCallStatement(statement) + val calleeFact = + if (calleeWithUpdateSite.contains(statement)) calleeWithUpdateFact + else None + (calleesO, calleeFact) + } + + val last = icfg.lastStatement(basicBlock) + var flows: Set[IFDSFact] = in + var statement: S = icfg.firstStatement(basicBlock) + + // Iterate over all statements but the last one, only keeping the resulting DataFlowFacts. + while (statement != last) { + val (calleesO, calleeFact) = collectInformation(statement) + val successor = icfg.nextStatement(statement) + flows = if (calleesO.isEmpty) { + statistics.numberOfCalls.normalFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + ifdsProblem.normalFlow(statement, Some(successor), flows) + } else + // Inside a basic block, we only have one successor --> Take the head + handleCall(basicBlock, statement, calleesO.get, flows, calleeFact).values.head + statement = successor + } + + // Analyze the last statement for each possible successor statement. + val (calleesO, callFact) = collectInformation(last) + var result: Map[Option[Node], Set[IFDSFact]] = + if (calleesO.isEmpty) { + var result: Map[Option[Node], Set[IFDSFact]] = Map.empty + for (node ← icfg.nextNodes(basicBlock)) { + statistics.numberOfCalls.normalFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + result += Some(node) -> ifdsProblem.normalFlow( + statement, + Some(icfg.firstStatement(node)), + flows + ) + } + if (icfg.isExitStatement(last)) { + statistics.numberOfCalls.normalFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + result += None -> ifdsProblem.normalFlow(statement, None, flows) + } + result + } else + handleCall(basicBlock, statement, calleesO.get, flows, callFact) + .map(entry ⇒ Some(entry._1.node) -> entry._2) + + // Propagate the null fact. + result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) + result + } + + /** + * Re-analyzes some basic blocks. + * + * @param basicBlocks The basic blocks, that will be re-analyzed. + */ + private def reAnalyzeBasicBlocks(basicBlocks: Set[Node])(implicit state: State): Unit = { + val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty + for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) + process(queue) + } + + /** + * Re-analyzes some call sites with respect to one specific callee. + * + * @param callSites The call sites, which are analyzed. + * @param callee The callee, which will be considered at the `callSites`. + * @param fact If defined, the `callee` will only be analyzed for this fact. + */ + private def reAnalyzeCalls( + callSites: Set[S], + callee: C, + fact: Option[IFDSFact] + )(implicit state: State): Unit = { + val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty + for (callSite ← callSites) { + val node = callSite.node + queue.enqueue((node, state.incomingFacts(node), Some(callSite), Some(callee), fact)) + } + process(queue) + } + + /** + * If `from` contains a null fact, it will be added to `to`. + * + * @param from The set, which may contain the null fact initially. + * @param to The set, to which the null fact may be added. + * @return `to` with the null fact added, if it is contained in `from`. + */ + private def propagateNullFact(from: Set[IFDSFact], to: Set[IFDSFact]): Set[IFDSFact] = { + if (from.contains(ifdsProblem.nullFact)) to + ifdsProblem.nullFact + else to + } + + /** + * Adds a method-fact-pair as to the IFDS call sites and dependees. + * + * @param entity The method-fact-pair. + * @param calleeProperty The property, that was returned for `entity`. + * @param callBB The basic block of the call site. + * @param call The call site. + */ + private def addIfdsDependee( + entity: (C, IFDSFact), + calleeProperty: EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]], + callBB: Node, + call: S + )(implicit state: State): Unit = { + val callSites = state.pendingIfdsCallSites + state.pendingIfdsCallSites = callSites.updated( + entity, + callSites.getOrElse(entity, Set.empty) + call + ) + state.pendingIfdsDependees += entity -> calleeProperty + } + + /** + * Adds a summary edge for a call to a map representing summary edges. + * + * @param summaryEdges The current map representing the summary edges. + * Maps from successor statements to facts, which hold at their beginning. + * @param call The call, calling the `callee`. + * @param exitStatement The exit statement for the new summary edge. + * @param successor The successor statement of the call for the new summary edge. + * @param callee The callee, called by `call`. + * @param allNewExitFacts A map, mapping from the exit statements of `callee` to their newly + * found exit facts. + * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. + */ + private def addSummaryEdge( + summaryEdges: Map[S, Set[IFDSFact]], + call: S, + exitStatement: S, + successor: S, + callee: C, + allNewExitFacts: Map[S, Set[IFDSFact]] + ): Map[S, Set[IFDSFact]] = { + val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) + statistics.numberOfCalls.returnFlow += 1 + statistics.sumOfInputfactsForCallbacks += in.size + val returned = ifdsProblem.returnFlow(call, callee, exitStatement, successor, in) + val newFacts = + if (summaryEdges.contains(successor) && summaryEdges(successor).nonEmpty) { + val summaryForSuccessor = summaryEdges(successor) + if (summaryForSuccessor.size >= returned.size) summaryForSuccessor ++ returned + else returned ++ summaryForSuccessor + } else returned + summaryEdges.updated(successor, newFacts) + } +} + +abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] + extends FPCFLazyAnalysisScheduler { + final override type InitializationData = IFDSAnalysis[IFDSFact, C, S, Node] + def property: IFDSPropertyMetaInformation[S, IFDSFact] + final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + + /** + * Registers the analysis as a lazy computation, that is, the method + * will call `ProperytStore.scheduleLazyComputation`. + */ + override def register( + p: SomeProject, + ps: PropertyStore, + analysis: IFDSAnalysis[IFDSFact, C, S, Node] + ): FPCFAnalysis = { + ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) + analysis + } + + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { + val ifdsAnalysis = analysis.asInstanceOf[IFDSAnalysis[IFDSFact, C, S, Node]] + for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { + ps.force(e, ifdsAnalysis.propertyKey.key) + } + } + + override def afterPhaseCompletion( + p: SomeProject, + ps: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} +} diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala new file mode 100644 index 0000000000..1cc785f942 --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala @@ -0,0 +1,117 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ifds.old + +import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} + +/** + * A framework for IFDS analyses. + * + * @tparam IFDSFact The type of flow facts, which are tracked by the concrete analysis. + * @author Dominik Helm + * @author Mario Trageser + */ +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node](val icfg: ICFG[IFDSFact, C, S, Node]) { + /** + * The null fact of this analysis. + */ + def nullFact: IFDSFact + + /** + * The entry points of this analysis. + */ + def entryPoints: Seq[(C, IFDSFact)] + + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param successor The successor of the analyzed `statement`, for which the data flow shall be + * computed. It is not present for exit statements. + * @param in The facts, which hold before the execution of the `statement`. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + def normalFlow( + statement: S, + successor: Option[S], + in: Set[IFDSFact] + ): Set[IFDSFact] + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The facts, which hold before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + def callFlow( + call: S, + callee: C, + in: Set[IFDSFact], + source: (C, IFDSFact) + ): Set[IFDSFact] + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param callee The method called by `call`, for which the data flow shall be computed. + * @param exit The statement, which terminated the `calle`. + * @param successor The statement of the caller, which will be executed after the `callee` + * returned. + * @param in The facts, which hold before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + def returnFlow( + call: S, + callee: C, + exit: S, + successor: S, + in: Set[IFDSFact] + ): Set[IFDSFact] + + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param successor The statement, which will be executed after the call. + * @param in The facts, which hold before the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + def callToReturnFlow( + call: S, + successor: S, + in: Set[IFDSFact], + source: (C, IFDSFact) + ): Set[IFDSFact] + + type OutsideAnalysisContextHandler = ((S, S, Set[IFDSFact]) ⇒ Set[IFDSFact]) { + def apply(call: S, successor: S, in: Set[IFDSFact]): Set[IFDSFact] + } + + /** + * Checks, if a callee is outside this analysis' context. + * By default, native methods are not inside the analysis context. + * For callees outside this analysis' context the returned handler is called + * to compute the summary edge for the call instead of analyzing the callee. + * + * @param callee The method called by `call`. + * @return The handler function. It receives + * the statement which invoked the call, + * the successor statement, which will be executed after the call and + * the set of input facts which hold before the `call`. + * It returns facts, which hold after the call, excluding the call to return flow. + */ + def outsideAnalysisContext(callee: C): Option[OutsideAnalysisContextHandler] + + def specialCase(source: (C, IFDSFact), propertyKey: IFDSPropertyMetaInformation[S, IFDSFact]): Option[ProperPropertyComputationResult] = None +} \ No newline at end of file diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala new file mode 100644 index 0000000000..35d7372ff1 --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala @@ -0,0 +1,83 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ifds.old + +import org.opalj.br.analyses.SomeProject +import org.opalj.ifds.AbstractIFDSFact + +/** + * Defines functions, which can be overwritten to implement subsuming. + * + * @author Mario Trageser + */ +trait Subsumable[S, IFDSFact <: AbstractIFDSFact] { + + /** + * A subclass can override this method to filter the `facts` in some set, which are not subsumed + * by another fact in the set. + * + * + * @param facts The set of facts. + * @param project The project, which is analyzed. + * @return The facts, which are not subsumed by any other fact in `facts`. + * By default, `facts` is returned without removing anything. + */ + protected def subsume[T <: IFDSFact](facts: Set[T], project: SomeProject): Set[T] = facts + + /** + * Checks, if any fact from some set is not equal to or subsumed by any fact in another set. + * A subclass implementing subsuming must overwrite this method to consider subsuming. + * + * @param newFacts The facts, which were found. Not empty. + * @param oldFacts The facts, which are already known. Not empty. + * @param project The project, which is analyzed. + * @return True, if any fact in `newFacts`, is not equal to or subsumed by any fact in + * `oldFacts`. + */ + protected def containsNewInformation[T <: IFDSFact]( + newFacts: Set[T], + oldFacts: Set[T], + project: SomeProject + ): Boolean = + newFacts.exists(newFact => !oldFacts.contains(newFact)) + + /** + * Filters the new information from a new set of exit facts given the already known exit facts. + * A subclass implementing subsuming must overwrite this method to consider subsuming. + * + * @param newExitFacts The new exit facts. + * @param oldExitFacts The old exit facts. + * @param project The project, which is analyzed. + * @return A map, containing the keys of `newExitFacts`. + * Facts, which are equal to or subsumed by any fact for the same statement in + * `oldExitFacts` are not present. + */ + protected def filterNewInformation[T <: IFDSFact]( + newExitFacts: Map[S, Set[T]], + oldExitFacts: Map[S, Set[T]], + project: SomeProject + ): Map[S, Set[T]] = { + var result = newExitFacts + for ((key, values) <- oldExitFacts) { + result = result.updated(key, result(key) -- values) + } + result + } + + /** + * Filters the `facts` from some set, which are not equal to or subsumed by any fact in an + * `other` set. + * A subclass implementing subsuming must overwrite this method to consider subsuming. + * + * @param facts The set, from which facts are filtered. + * @param otherFacts The set, which may subsume facts from `facts`. + * @param project The project, which is analyzed. + * @return The facts from `facts`, which are not equal to or subsumed by any fact in an + * `otherFacts`. + */ + protected def notSubsumedBy[T <: IFDSFact]( + facts: Set[T], + otherFacts: Set[T], + project: SomeProject + ): Set[T] = + facts -- otherFacts +} diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/Subsuming.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala similarity index 97% rename from OPAL/ifds/src/main/scala/org/opalj/ifds/Subsuming.scala rename to OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala index 0bcb29d96b..49032a6317 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/Subsuming.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala @@ -1,7 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ifds +package org.opalj.ifds.old import org.opalj.br.analyses.SomeProject +import org.opalj.ifds.SubsumableFact /** * An IFDS analysis, which implements subsuming. diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index e2c947fcb5..3feac68710 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -1,7 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.ifds.{AbstractIFDSFact, ICFG} +import org.opalj.ifds.AbstractIFDSFact +import org.opalj.ifds.old.ICFG import org.opalj.ll.llvm.{BasicBlock, Function, Instruction, Ret, Terminator} class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Function, LLVMStatement, BasicBlock] { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala index e18fe3036c..d267e889d0 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala @@ -3,7 +3,8 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} -import org.opalj.ifds.{AbstractIFDSFact, IFDSAnalysis, IFDSAnalysisScheduler, IFDSProblem, IFDSPropertyMetaInformation, Statement} +import org.opalj.ifds.old.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSProblem, Statement} +import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} import org.opalj.ll.LLVMProjectKey import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index 9f08749017..8d142ee0f7 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -4,7 +4,8 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.PropertyStore -import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem} +import org.opalj.ifds.AbstractIFDSFact +import org.opalj.ifds.old.IFDSProblem import org.opalj.ll.LLVMProjectKey import org.opalj.ll.llvm.{BasicBlock, Function} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index 02312962f3..7444706245 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -38,7 +38,8 @@ import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.ai.domain.l0.PrimitiveTACAIDomain import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, IFDSProperty, IFDSPropertyMetaInformation, NumberOfCalls, Statement, Subsumable} +import org.opalj.ifds.old.{IFDSProblem, NumberOfCalls, Statement, Subsumable} +import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation, AbstractIFDSFact} import org.opalj.tac.fpcf.properties.TACAI import org.opalj.tac.fpcf.properties.TheTACAI import org.opalj.tac.cg.{RTACallGraphKey, TypeProviderKey} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala index b5a6da1895..6776aeedd8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala @@ -15,7 +15,8 @@ import org.opalj.br.cfg.CFGNode import org.opalj.br.DeclaredMethod import org.opalj.br.cfg.CatchNode import org.opalj.br.cfg.CFG -import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, IFDSProperty, IFDSPropertyMetaInformation} +import org.opalj.ifds.old.IFDSProblem +import org.opalj.ifds.{AbstractIFDSFact, IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V import org.opalj.tac.fpcf.properties.{TACAI, TheTACAI} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala index f62acdf119..0ca5d9a4dc 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala @@ -5,7 +5,8 @@ import org.opalj.br.DeclaredMethod import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.cfg.CFGNode import org.opalj.fpcf.{FinalEP, PropertyStore} -import org.opalj.ifds.{AbstractIFDSFact, ICFG} +import org.opalj.ifds.AbstractIFDSFact +import org.opalj.ifds.old.ICFG import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.properties.cg.Callees diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala index 52c18ff533..a35282f372 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala @@ -1,16 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds -import org.opalj.br.cfg.BasicBlock -import org.opalj.br.cfg.CFG -import org.opalj.br.cfg.CFGNode import org.opalj.br.DeclaredMethod -import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, IFDSPropertyMetaInformation} +import org.opalj.br.cfg.{BasicBlock, CFG, CFGNode} +import org.opalj.ifds.old.IFDSProblem +import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} +import org.opalj.tac.{Return, ReturnValue, Stmt, TACStmts} import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.Return -import org.opalj.tac.ReturnValue -import org.opalj.tac.Stmt -import org.opalj.tac.TACStmts /** * An IFDS analysis, which analyzes the code in the control flow direction. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala index 5225f76733..8226d86357 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -7,7 +7,8 @@ import org.opalj.fpcf.PropertyKey import org.opalj.fpcf.PropertyStore import org.opalj.fpcf.PropertyBounds import org.opalj.br.analyses.SomeProject -import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation, NumberOfSubsumptions, Subsuming} +import org.opalj.ifds.old.{NumberOfSubsumptions, Subsuming} +import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.TACAI diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 17ca169033..a12dc598e0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -6,10 +6,11 @@ import org.opalj.br.cfg.CFGNode import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.{DeclaredMethod, ObjectType} import org.opalj.fpcf._ +import org.opalj.ifds.old.IFDSProblem +import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} import org.opalj.tac.cg.TypeProviderKey import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala index e96bfb0ceb..bb27daa2ec 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala @@ -1,11 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds -import org.opalj.br.{ArrayType, ClassFile, DeclaredMethod, FieldType, Method, ReferenceType} import org.opalj.br.analyses.SomeProject +import org.opalj.br._ import org.opalj.collection.immutable.EmptyIntTrieSet import org.opalj.ifds.{AbstractIFDSFact, SubsumableFact, SubsumableNullFact} -import org.opalj.tac.{ArrayLoad, ArrayStore, Assignment, DUVar, Expr, GetField, GetStatic, New, ReturnValue, Var} +import org.opalj.tac._ import org.opalj.value.ValueInformation import scala.annotation.tailrec From 3ee3a049b1c6d3e6e5947bf299e930e8a5f34bfd Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 24 Mar 2022 21:19:31 +0100 Subject: [PATCH 29/67] Use new implementation for native part --- .../analyses/ifds/NativeForwardICFG.scala | 60 ++------ .../analyses/ifds/NativeIFDSAnalysis.scala | 11 +- .../analyses/ifds/NativeIFDSProblem.scala | 7 +- .../taint/NativeForwardTaintProblem.scala | 135 +++++++----------- 4 files changed, 72 insertions(+), 141 deletions(-) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index 3feac68710..4956c58aa7 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -1,62 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.ifds.AbstractIFDSFact -import org.opalj.ifds.old.ICFG -import org.opalj.ll.llvm.{BasicBlock, Function, Instruction, Ret, Terminator} +import org.opalj.ifds.{AbstractIFDSFact, ICFG} +import org.opalj.ll.llvm.{Function, Instruction, Ret, Terminator} -class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Function, LLVMStatement, BasicBlock] { +class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Function, LLVMStatement] { /** - * Determines the basic blocks, at which the analysis starts. + * Determines the statements at which the analysis starts. * - * @param sourceFact The source fact of the analysis. - * @param callable The analyzed callable. - * @return The basic blocks, at which the analysis starts. + * @param callable The analyzed callable. + * @return The statements at which the analysis starts. */ - override def startNodes(sourceFact: IFDSFact, callable: Function): Set[BasicBlock] = Set(callable.entryBlock()) - - /** - * Determines the nodes, that will be analyzed after some `basicBlock`. - * - * @param node The basic block, that was analyzed before. - * @return The nodes, that will be analyzed after `basicBlock`. - */ - override def nextNodes(node: BasicBlock): Set[BasicBlock] = node.terminator match { - case Some(terminator) ⇒ terminator.successors().map(_.parent()).toSet - case None ⇒ Set.empty + override def startStatements(callable: Function): Set[LLVMStatement] = { + Set(LLVMStatement(callable.entryBlock().firstInstruction())) } - /** - * Checks, if some `node` is the last node. - * - * @return True, if `node` is the last node, i.e. there is no next node. - */ - override def isLastNode(node: BasicBlock): Boolean = !node.hasSuccessors - - /** - * Determines the first index of some `basic block`, that will be analyzed. - * - * @param basicBlock The basic block. - * @return The first index of some `basic block`, that will be analyzed. - */ - override def firstStatement(basicBlock: BasicBlock): LLVMStatement = LLVMStatement(basicBlock.firstInstruction()) - - /** - * Determines the last index of some `basic block`, that will be analzyed. - * - * @param basicBlock The basic block. - * @return The last index of some `basic block`, that will be analzyed. - */ - override def lastStatement(basicBlock: BasicBlock): LLVMStatement = LLVMStatement(basicBlock.lastInstruction()) - - /** - * Determines the statement that will be analyzed after some other statement. - * - * @param statement The current statement. - * @return The statement that will be analyzed after `statement`. - */ - override def nextStatement(statement: LLVMStatement): LLVMStatement = LLVMStatement(statement.instruction.next().get) - /** * Determines the statement, that will be analyzed after some other `statement`. * @@ -64,7 +22,7 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun * @return The successor statements */ override def nextStatements(statement: LLVMStatement): Set[LLVMStatement] = { - if (!statement.instruction.isTerminator) return Set(nextStatement(statement)) + if (!statement.instruction.isTerminator) return Set(LLVMStatement(statement.instruction.next().get)) statement.instruction.asInstanceOf[Instruction with Terminator].successors().map(LLVMStatement(_)).toSet } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala index d267e889d0..be36845fcc 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala @@ -3,7 +3,7 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} -import org.opalj.ifds.old.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSProblem, Statement} +import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSProblem, Statement} import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} import org.opalj.ll.LLVMProjectKey import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} @@ -16,22 +16,23 @@ import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} */ class NativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( project: SomeProject, - ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement, BasicBlock], + ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement], propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] ) - extends IFDSAnalysis[IFDSFact, Function, LLVMStatement, BasicBlock]()(project, ifdsProblem, propertyKey) + extends IFDSAnalysis[IFDSFact, Function, LLVMStatement]()(project, ifdsProblem, propertyKey) /** * A statement that is passed to the concrete analysis. * * @param instruction The LLVM instruction. */ -case class LLVMStatement(instruction: Instruction) extends Statement[BasicBlock] { +case class LLVMStatement(instruction: Instruction) extends Statement[Function, BasicBlock] { def function(): Function = instruction.function def basicBlock(): BasicBlock = instruction.parent override def node(): BasicBlock = basicBlock + override def callable(): Function = function } -abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysisScheduler[IFDSFact, Function, LLVMStatement, BasicBlock] { +abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysisScheduler[IFDSFact, Function, LLVMStatement] { override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index 8d142ee0f7..db4ac4bf4a 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -4,12 +4,11 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.PropertyStore -import org.opalj.ifds.AbstractIFDSFact -import org.opalj.ifds.old.IFDSProblem +import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem} import org.opalj.ll.LLVMProjectKey -import org.opalj.ll.llvm.{BasicBlock, Function} +import org.opalj.ll.llvm.Function -abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, Function, LLVMStatement, BasicBlock](new NativeForwardICFG[Fact]) { +abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, Function, LLVMStatement](new NativeForwardICFG[Fact]) { final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) val llvmProject = project.get(LLVMProjectKey) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index e8a0ea185b..3267c142cf 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -3,88 +3,61 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeIFDSProblem} -import org.opalj.ll.llvm.Function +import org.opalj.ll.llvm.{Function, PHI} import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[Function, LLVMStatement, NativeFact] { + class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[Function, LLVMStatement, NativeFact] { override def nullFact: NativeFact = NativeNullFact - /** - * If a variable gets assigned a tainted value, the variable will be tainted. - */ - override def normalFlow(statement: LLVMStatement, successor: Option[LLVMStatement], - in: Set[NativeFact]): Set[NativeFact] = - statement match { - // TODO - case _ ⇒ in - } - - /** - * Propagates tainted parameters to the callee. If a call to the sink method with a tainted - * parameter is detected, no call-to-start - * edges will be created. - */ - override def callFlow(call: LLVMStatement, callee: Function, - in: Set[NativeFact], source: (Function, NativeFact)): Set[NativeFact] = { - // TODO - Set.empty[NativeFact] - } - - /** - * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. - * If the callee's return value was tainted and it is assigned to a variable in the callee, the - * variable will be tainted. - * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds - * at this method. - * Creates new taints and FlowFacts, if necessary. - * If the sanitize method was called, nothing will be tainted. - */ - override def returnFlow(call: LLVMStatement, callee: Function, exit: LLVMStatement, - successor: LLVMStatement, in: Set[NativeFact]): Set[NativeFact] = { - // TODO - Set.empty - } - - /** - * Removes taints according to `sanitizeParamters`. - */ - override def callToReturnFlow(call: LLVMStatement, successor: LLVMStatement, - in: Set[NativeFact], - source: (Function, NativeFact)): Set[NativeFact] = - in -- sanitizeParameters(call, in) - - /** - * Called, when the exit to return facts are computed for some `callee` with the null fact and - * the callee's return value is assigned to a vairbale. - * Creates a taint, if necessary. - * - * @param callee The called method. - * @param call The call. - * @return Some variable fact, if necessary. Otherwise none. - */ - protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeFact] - - /** - * Called, when the call to return facts are computed for some `callee`. - * Creates a FlowFact, if necessary. - * - * @param callee The method, which was called. - * @param call The call. - * @return Some FlowFact, if necessary. Otherwise None. - */ - protected def createFlowFact(callee: Function, call: LLVMStatement, - in: Set[NativeFact]): Option[NativeFlowFact] - - override def outsideAnalysisContext(callee: Function): Option[OutsideAnalysisContextHandler] = { - None - } - - /** - * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. - * True by default. This method can be overwritten by a subclass. - * - * @param callee The callee. - * @return True, by default. - */ - protected def relevantCallee(callee: Function): Boolean = true -} \ No newline at end of file + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param in The fact which holds before the execution of the `statement`. + * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be + * computed. Used for phi statements to distinguish the flow. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + override def normalFlow(statement: LLVMStatement, in: NativeFact, predecessor: Option[LLVMStatement]): Set[NativeFact] = ??? + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + override def callFlow(call: LLVMStatement, callee: Function, in: NativeFact): Set[NativeFact] = ??? + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + override def returnFlow(exit: LLVMStatement, in: NativeFact, call: LLVMStatement, callFact: NativeFact): Set[NativeFact] = ??? + + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param in The facts, which hold before the `call`. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + override def callToReturnFlow(call: LLVMStatement, in: NativeFact): Set[NativeFact] = ??? + + override def needsPredecessor(statement: LLVMStatement): Boolean = statement.instruction match { + case PHI(_) => true + case _ => false + } + } \ No newline at end of file From 25d0f19106e437ee2bdf5a0bf3af4dabcab39c8d Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 25 Mar 2022 16:30:54 +0100 Subject: [PATCH 30/67] Implement forward taint analysis as test for new ifds solver --- .../NewForwardTaintAnalysisFixtureTest.scala | 41 +++ .../taint/ForwardFlowPathMatcher.scala | 8 +- .../main/scala/org/opalj/ifds/old/ICFG.scala | 4 +- .../org/opalj/ifds/old/IFDSAnalysis.scala | 12 +- .../org/opalj/ifds/old/IFDSProblem.scala | 4 +- .../taint/NativeForwardTaintAnalysis.scala | 6 +- .../taint/NativeForwardTaintProblem.scala | 4 +- .../analyses/ifds/AbstractIFDSAnalysis.scala | 60 ++- .../analyses/ifds/BackwardIFDSAnalysis.scala | 21 +- .../analyses/ifds/ForwardIFDSAnalysis.scala | 10 +- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 2 +- .../fpcf/analyses/ifds/NewForwardICFG.scala | 94 +++++ .../analyses/ifds/NewJavaIFDSProblem.scala | 60 +++ .../ifds/taint/NewForwardTaintProblem.scala | 346 ++++++++++++++++++ .../analyses/ifds/taint/NewTaintProblem.scala | 24 ++ .../opalj/tac/fpcf/properties/NewTaint.scala | 23 ++ .../NewForwardTaintAnalysisFixture.scala | 64 ++++ 17 files changed, 710 insertions(+), 73 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala create mode 100644 OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala new file mode 100644 index 0000000000..6ea6cc9271 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala @@ -0,0 +1,41 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.ifds + +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.Project +import org.opalj.fpcf.PropertiesTest +import org.opalj.fpcf.properties.taint.ForwardFlowPath +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.taint.{NewForwardTaintAnalysisFixtureScheduler, NullFact} + +import java.net.URL + +/** + * @author Mario Trageser + */ +class NewForwardTaintAnalysisFixtureTest extends PropertiesTest { + + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData( + AIDomainFactoryKey + )( + (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ + Set[Class[_ <: AnyRef]]( + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] + ) + ) + p.get(RTACallGraphKey) + } + + describe("Test the ForwardFlowPath annotations") { + val testContext = executeAnalyses(NewForwardTaintAnalysisFixtureScheduler) + val project = testContext.project + val eas = methodsWithAnnotations(project).map { + case (methods, entityString, annotations) ⇒ + ((methods, NullFact), entityString, annotations) + } + testContext.propertyStore.shutdown() + validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala index 6232594cdc..641c77611a 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala @@ -3,10 +3,10 @@ package org.opalj.fpcf.properties.taint import org.opalj.br.analyses.SomeProject import org.opalj.br.{AnnotationLike, ElementValue, ElementValuePair, ObjectType} -import org.opalj.fpcf.{Entity, Property} import org.opalj.fpcf.properties.AbstractPropertyMatcher +import org.opalj.fpcf.{Entity, Property} +import org.opalj.ifds.IFDSProperty import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact -import org.opalj.tac.fpcf.properties.Taint /** * @author Mario Trageser @@ -23,8 +23,8 @@ class ForwardFlowPathMatcher extends AbstractPropertyMatcher { val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) ⇒ evp.value.asArrayValue.values.map((value: ElementValue) ⇒ value.asStringValue.value)).head.toIndexedSeq - val flows = properties.filter(_.isInstanceOf[Taint]).head - .asInstanceOf[Taint] + val flows = properties.filter(_.isInstanceOf[IFDSProperty[_, _]]).head + .asInstanceOf[IFDSProperty[_, _]] .flows .values .fold(Set.empty)((acc, facts) ⇒ acc ++ facts) diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala index be874b174e..c7d5fb5d97 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala @@ -1,11 +1,11 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ifds.old -import org.opalj.ifds.AbstractIFDSFact +import org.opalj.ifds.{AbstractIFDSFact, Statement} import scala.collection.{Set => SomeSet} -abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] { +abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_, Node], Node] { /** * Determines the basic blocks, at which the analysis starts. * diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala index 988e54fb90..3b457ce3b5 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala @@ -4,14 +4,10 @@ package org.opalj.ifds.old import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} import org.opalj.fpcf._ -import org.opalj.ifds.{AbstractIFDSFact, IFDS, IFDSProperty, IFDSPropertyMetaInformation} +import org.opalj.ifds.{AbstractIFDSFact, IFDS, IFDSProperty, IFDSPropertyMetaInformation, Statement} import scala.collection.{mutable, Set => SomeSet} -abstract class Statement[Node] { - def node(): Node -} - /** * The state of the analysis. For each method and source fact, there is a separate state. * @@ -29,7 +25,7 @@ abstract class Statement[Node] { * @param outgoingFacts Maps each basic block and successor node to the data flow facts valid at * the beginning of the successor. For exit statements the successor is None */ -protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( +protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, Node], Node]( val source: (C, IFDSFact), var pendingIfdsCallSites: Map[(C, IFDSFact), Set[S]] = Map.empty[(C, IFDSFact), Set[S]], var pendingIfdsDependees: Map[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] = Map.empty[(C, IFDSFact), EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]], @@ -69,7 +65,7 @@ protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalys * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. * @tparam IFDSFact */ -class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node]( +class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, Node], Node]( implicit project: SomeProject, val ifdsProblem: IFDSProblem[IFDSFact, C, S, Node], @@ -616,7 +612,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Nod } } -abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node] +abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, Node], Node] extends FPCFLazyAnalysisScheduler { final override type InitializationData = IFDSAnalysis[IFDSFact, C, S, Node] def property: IFDSPropertyMetaInformation[S, IFDSFact] diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala index 1cc785f942..f05042bc88 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala @@ -2,7 +2,7 @@ package org.opalj.ifds.old import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} +import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation, Statement} /** * A framework for IFDS analyses. @@ -11,7 +11,7 @@ import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} * @author Dominik Helm * @author Mario Trageser */ -abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[Node], Node](val icfg: ICFG[IFDSFact, C, S, Node]) { +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_, Node], Node](val icfg: ICFG[IFDSFact, C, S, Node]) { /** * The null fact of this analysis. */ diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala index 18b274ba24..ca4bba3a30 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -23,12 +23,12 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint /** * We do not sanitize parameters. */ - override protected def sanitizeParameters(call: LLVMStatement, in: Set[NativeFact]): Set[NativeFact] = Set.empty + override protected def sanitizesParameter(call: LLVMStatement, in: NativeFact): Boolean = false /** * Creates a new variable fact for the callee, if the source was called. */ - override protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeFact] = + protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeFact] = if (callee.name == "source") Set(NativeVariable(call)) else Set.empty @@ -36,7 +36,7 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint * Create a FlowFact, if sink is called with a tainted variable. * Note, that sink does not accept array parameters. No need to handle them. */ - override protected def createFlowFact( + protected def createFlowFact( callee: Function, call: LLVMStatement, in: Set[NativeFact] diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 3267c142cf..5cc786fd19 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -4,9 +4,9 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeIFDSProblem} import org.opalj.ll.llvm.{Function, PHI} -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem +import org.opalj.tac.fpcf.analyses.ifds.taint.NewTaintProblem - class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[Function, LLVMStatement, NativeFact] { + abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with NewTaintProblem[Function, LLVMStatement, NativeFact] { override def nullFact: NativeFact = NativeNullFact /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index 7444706245..c1794bbb49 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -5,46 +5,30 @@ package fpcf package analyses package ifds -import java.io.File -import java.io.PrintWriter -import scala.collection.{Set ⇒ SomeSet} -import scala.collection.mutable import com.typesafe.config.ConfigValueFactory - -import javax.swing.JOptionPane -import org.opalj.util.Milliseconds -import org.opalj.util.PerformanceEvaluation.time -import org.opalj.fpcf.{EOptionP, EPK, FinalE, FinalEP, FinalP, InterimEUBP, InterimResult, ProperPropertyComputationResult, PropertyBounds, PropertyStore, Result, SomeEPS} -import org.opalj.fpcf.seq.PKESequentialPropertyStore -import org.opalj.value.ValueInformation -import org.opalj.br.fpcf.FPCFLazyAnalysisScheduler -import org.opalj.br.Method -import org.opalj.br.ObjectType -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.SomeProject -import org.opalj.br.cfg.BasicBlock -import org.opalj.br.cfg.CFG -import org.opalj.br.cfg.CFGNode -import org.opalj.br.fpcf.FPCFAnalysis -import org.opalj.br.DeclaredMethod -import org.opalj.br.DefinedMethod -import org.opalj.br.analyses.Project -import org.opalj.br.analyses.ProjectInformationKeys -import org.opalj.tac.fpcf.properties.cg.Callees -import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.br.fpcf.FPCFAnalysesManagerKey -import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.ai.domain.l0.PrimitiveTACAIDomain import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.ifds.old.{IFDSProblem, NumberOfCalls, Statement, Subsumable} -import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation, AbstractIFDSFact} -import org.opalj.tac.fpcf.properties.TACAI -import org.opalj.tac.fpcf.properties.TheTACAI +import org.opalj.br.{DeclaredMethod, DefinedMethod, Method, ObjectType} +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, Project, ProjectInformationKeys, SomeProject} +import org.opalj.br.cfg.{BasicBlock, CFG, CFGNode} +import org.opalj.br.fpcf.{FPCFAnalysesManagerKey, FPCFAnalysis, FPCFLazyAnalysisScheduler, PropertyStoreKey} +import org.opalj.fpcf.seq.PKESequentialPropertyStore +import org.opalj.fpcf._ +import org.opalj.ifds.old.{IFDSProblem, NumberOfCalls, Subsumable} +import org.opalj.ifds.{AbstractIFDSFact, IFDSProperty, IFDSPropertyMetaInformation, Statement} import org.opalj.tac.cg.{RTACallGraphKey, TypeProviderKey} import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.properties.{TACAI, TheTACAI} +import org.opalj.tac.fpcf.properties.cg.{Callees, Callers} +import org.opalj.util.Milliseconds +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.value.ValueInformation + +import java.io.{File, PrintWriter} +import javax.swing.JOptionPane +import scala.collection.{mutable, Set => SomeSet} /** * @@ -674,7 +658,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( index: Int ): (JavaStatement, Option[SomeSet[Method]], Option[IFDSFact]) = { val stmt = state.code(index) - val statement = JavaStatement(state.method, basicBlock, stmt, index, state.code, state.cfg) + val statement = JavaStatement(state.method, basicBlock, stmt, index, state.code, state.cfg, state.source._1) val calleesO = if (calleeWithUpdateIndex.contains(index)) calleeWithUpdate.map(Set(_)) else getCalleesIfCallStatement(statement) @@ -694,7 +678,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( val next = nextIndex(index) flows = if (calleesO.isEmpty) { val successor = - JavaStatement(state.method, basicBlock, state.code(next), next, state.code, state.cfg) + JavaStatement(state.method, basicBlock, state.code(next), next, state.code, state.cfg, state.source._1) numberOfCalls.normalFlow += 1 sumOfInputFactsForCallbacks += in.size ifdsProblem.normalFlow(statement, Some(successor), flows) @@ -888,8 +872,9 @@ case class JavaStatement( stmt: Stmt[V], index: Int, code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] -) extends Statement[CFGNode] { + cfg: CFG[Stmt[V], TACStmts[V]], + declaredMethod: DeclaredMethod +) extends Statement[DeclaredMethod, CFGNode] { override def hashCode(): Int = method.hashCode() * 31 + index @@ -899,6 +884,7 @@ case class JavaStatement( } override def toString: String = s"${method.toJava}" + override def callable(): DeclaredMethod = declaredMethod } abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala index 6776aeedd8..280459009a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala @@ -60,7 +60,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val startBlock = state.cfg.startBlock val startPC = startBlock.startPC val statement = - JavaStatement(state.method, startBlock, state.code(startPC), startPC, state.code, state.cfg) + JavaStatement(state.method, startBlock, state.code(startPC), startPC, state.code, state.cfg, state.source._1) val exitFacts = state.outgoingFacts.get(startBlock).flatMap(_.get(SyntheticStartNode)) if (exitFacts.isDefined) Map(statement -> exitFacts.get) else Map.empty @@ -214,10 +214,10 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS override protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement = { if (node.isBasicBlock) { val index = node.asBasicBlock.endPC - JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg) + JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg, state.source._1) } else if (node.isCatchNode) firstStatement(node.successors.head) else if (node == SyntheticStartNode) - JavaStatement(state.method, node, null, 0, state.code, state.cfg) + JavaStatement(state.method, node, null, 0, state.code, state.cfg, state.source._1) else throw new IllegalArgumentException(s"Unknown node type: $node") } @@ -242,7 +242,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS statement.code(nextIndex), nextIndex, statement.code, - statement.cfg + statement.cfg, + statement.declaredMethod ) ) } @@ -270,7 +271,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val calleeStmts = tac.stmts val exitStmt = calleeStmts(exitPc) val exitStatement = - JavaStatement(definedCallee, cfg.bb(exitPc), exitStmt, exitPc, calleeStmts, cfg) + JavaStatement(definedCallee, cfg.bb(exitPc), exitStmt, exitPc, calleeStmts, cfg, state.source._1) for { successor ← successors if !AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW || @@ -364,7 +365,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS statement.code(nextIndex), nextIndex, statement.code, - statement.cfg + statement.cfg, + statement.declaredMethod ) -> basicBlock ) } @@ -383,9 +385,9 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS @tailrec private def lastStatement(node: CFGNode)(implicit state: State): JavaStatement = { if (node.isBasicBlock) { val index = node.asBasicBlock.startPC - JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg) + JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg, state.source._1) } else if (node.isCatchNode) lastStatement(node.successors.head) - else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg) + else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg, state.source._1) else throw new IllegalArgumentException(s"Unknown node type: $node") } @@ -411,7 +413,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS callerStmts(callIndex), callIndex, callerStmts, - callerCfg + callerCfg, + caller ) ifdsProblem.unbalancedReturnFlow(in, call, caller, state.source).foreach { in ⇒ val callerEntity = (caller, in) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala index a35282f372..ab856ed85f 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala @@ -79,9 +79,9 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF override protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement = { if (node.isBasicBlock) { val index = node.asBasicBlock.startPC - JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg) + JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg, state.source._1) } else if (node.isCatchNode) firstStatement(node.successors.head) - else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg) + else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg, state.source._1) else throw new IllegalArgumentException(s"Unknown node type: $node") } @@ -96,7 +96,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF else { val nextIndex = index + 1 Set(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, - statement.code, statement.cfg)) + statement.code, statement.cfg, statement.declaredMethod)) } } @@ -155,7 +155,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF else { val nextIndex = index + 1 Map(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, - statement.code, statement.cfg) → basicBlock) + statement.code, statement.cfg, statement.declaredMethod) → basicBlock) } } @@ -178,7 +178,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF if (exitFacts.isDefined) { val lastIndex = basicBlock.endPC val stmt = JavaStatement(state.method, basicBlock, state.code(lastIndex), - lastIndex, state.code, state.cfg) + lastIndex, state.code, state.cfg, state.source._1) result += stmt → exitFacts.get } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index a12dc598e0..f1858d1cfc 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -72,7 +72,7 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e * not re-analyze the code. */ if (declaringClass ne declaredMethod.declaringClassType) Some(baseMethodResult(source, propertyKey)) - None + super.specialCase(source, propertyKey) } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala new file mode 100644 index 0000000000..cbe98191e1 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala @@ -0,0 +1,94 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.{DeclaredMethod, Method} +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.{FinalEP, PropertyStore} +import org.opalj.ifds.{AbstractIFDSFact, ICFG} +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.{DUVar, LazyDetachedTACAIKey, TACMethodParameter, TACode} +import org.opalj.tac.fpcf.analyses.cg.TypeProvider +import org.opalj.tac.fpcf.properties.cg.Callees +import org.opalj.value.ValueInformation + +class NewForwardICFG[IFDSFact <: AbstractIFDSFact](implicit project: SomeProject) + extends ICFG[IFDSFact, Method, NewJavaStatement] { + val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) + val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) + + /** + * Determines the statements at which the analysis starts. + * + * @param callable The analyzed callable. + * @return The statements at which the analysis starts. + */ + override def startStatements(callable: Method): Set[NewJavaStatement] = { + val TACode(_, code, _, cfg, _) = tacai(callable) + Set(NewJavaStatement(callable, cfg.startBlock, code(0), 0, code, cfg)) + } + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + override def nextStatements(statement: NewJavaStatement): Set[NewJavaStatement] = { + statement.cfg + .successors(statement.index) + .toChain + .map { index ⇒ + NewJavaStatement(statement.method, statement.node, statement.code(index), index, statement.code, statement.cfg) + } + .toSet + } + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + override def getCalleesIfCallStatement(statement: NewJavaStatement): Option[collection.Set[Method]] = { + val pc = statement.code(statement.index).pc + val caller = declaredMethods(statement.method) + val ep = propertyStore(caller, Callees.key) + ep match { + case FinalEP(_, p) ⇒ Some(definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method))) + case _ ⇒ + throw new IllegalStateException( + "call graph must be computed before the analysis starts" + ) + } + } + + override def isExitStatement(statement: NewJavaStatement): Boolean = { + statement.cfg.bb(statement.index).successors.exists(_.isExitNode) + } + + /** + * Maps some declared methods to their defined methods. + * + * @param declaredMethods Some declared methods. + * @return All defined methods of `declaredMethods`. + */ + private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): collection.Set[Method] = { + val result = scala.collection.mutable.Set.empty[Method] + declaredMethods + .filter( + declaredMethod ⇒ + declaredMethod.hasSingleDefinedMethod || + declaredMethod.hasMultipleDefinedMethods + ) + .foreach( + declaredMethod ⇒ + declaredMethod + .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) + ) + result + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala new file mode 100644 index 0000000000..03b03b023e --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala @@ -0,0 +1,60 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.br.cfg.{CFG, CFGNode} +import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, Statement} +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt, TACStmts} + +/** + * A statement that is passed to the concrete analysis. + * + * @param method The method containing the statement. + * @param node The basic block containing the statement. + * @param stmt The TAC statement. + * @param index The index of the Statement in the code. + * @param code The method's TAC code. + * @param cfg The method's CFG. + */ +case class NewJavaStatement( + method: Method, + node: CFGNode, + stmt: Stmt[V], + index: Int, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] + ) extends Statement[Method, CFGNode] { + + override def hashCode(): Int = method.hashCode() * 31 + index + + override def equals(o: Any): Boolean = o match { + case s: JavaStatement ⇒ s.index == index && s.method == method + case _ ⇒ false + } + + override def toString: String = s"${method.toJava}" + override def callable(): Method = method +} + +abstract class NewJavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) + extends IFDSProblem[Fact, Method, NewJavaStatement](new NewForwardICFG[Fact]()(project)) { + /** + * Gets the call object for a statement that contains a call. + * + * @param call The call statement. + * @return The call object for `call`. + */ + protected def asCall(call: Stmt[V]): Call[V] = call.astID match { + case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall + case _ ⇒ call.asMethodCall + } + + override def outsideAnalysisContext(callee: Method): Option[(NewJavaStatement, NewJavaStatement, Fact) ⇒ Set[Fact]] = callee.body.isDefined match { + case true ⇒ None + case false ⇒ Some((_call: NewJavaStatement, _successor: NewJavaStatement, in: Fact) ⇒ Set(in)) + } +} + diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala new file mode 100644 index 0000000000..51e0697ea8 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala @@ -0,0 +1,346 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.br.Method +import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} +import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, NewJavaStatement, NewJavaIFDSProblem} +import org.opalj.tac._ + +abstract class NewForwardTaintProblem(project: SomeProject) + extends NewJavaIFDSProblem[Fact](project) + with NewTaintProblem[Method, NewJavaStatement, Fact] { + val declaredMethods = project.get(DeclaredMethodsKey) + override def nullFact: Fact = NullFact + + override def needsPredecessor(statement: NewJavaStatement): Boolean = false + + /** + * If a variable gets assigned a tainted value, the variable will be tainted. + */ + override def normalFlow(statement: NewJavaStatement, in: Fact, predecessor: Option[NewJavaStatement]): Set[Fact] = { + val in_set = Set(in) // dirty hack for old code + statement.stmt.astID match { + case Assignment.ASTID ⇒ + in_set ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) + case ArrayStore.ASTID ⇒ + val store = statement.stmt.asArrayStore + val definedBy = store.arrayRef.asVar.definedBy + val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) + if (isTainted(store.value, in)) { + if (arrayIndex.isDefined) + // Taint a known array index + definedBy.foldLeft(in_set) { (c, n) ⇒ + c + ArrayElement(n, arrayIndex.get) + } + else + // Taint the whole array if the index is unknown + definedBy.foldLeft(in_set) { (c, n) ⇒ + c + Variable(n) + } + } else if (arrayIndex.isDefined && definedBy.size == 1) + // Untaint if possible + in_set - ArrayElement(definedBy.head, arrayIndex.get) + else in_set + case PutField.ASTID ⇒ + val put = statement.stmt.asPutField + val definedBy = put.objRef.asVar.definedBy + if (isTainted(put.value, in)) + definedBy.foldLeft(in_set) { (in, defSite) ⇒ + in + InstanceField(defSite, put.declaringClass, put.name) + } + else + in_set + case PutStatic.ASTID ⇒ + val put = statement.stmt.asPutStatic + if (isTainted(put.value, in)) + in_set + StaticField(put.declaringClass, put.name) + else + in_set + case _ ⇒ in_set + } + } + + /** + * Propagates tainted parameters to the callee. If a call to the sink method with a tainted + * parameter is detected, no call-to-start + * edges will be created. + */ + override def callFlow(call: NewJavaStatement, callee: Method, in: Fact): Set[Fact] = { + val in_set = Set(in) // compatilibity for old code + val callObject = asCall(call.stmt) + val allParams = callObject.allParams + var facts = Set.empty[Fact] + + if (relevantCallee(callee)) { + val allParamsWithIndices = allParams.zipWithIndex + in_set.foreach { + // Taint formal parameter if actual parameter is tainted + case Variable(index) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( + paramIndex, + callee.isStatic + )) + case _ ⇒ // Nothing to do + } + + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += ArrayElement( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.isStatic), + taintedIndex + ) + case _ ⇒ // Nothing to do + } + + case InstanceField(index, declClass, taintedField) ⇒ + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.foreach { + case (param, pIndex) if param.asVar.definedBy.contains(index) && + (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic) != -1 || + project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) ⇒ + facts += InstanceField( + AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic), + declClass, taintedField + ) + case _ ⇒ // Nothing to do + } + + case sf: StaticField ⇒ facts += sf + + case _ ⇒ // Nothing to do + } + } + + facts + } + + /** + * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. + * If the callee's return value was tainted and it is assigned to a variable in the callee, the + * variable will be tainted. + * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds + * at this method. + * Creates new taints and FlowFacts, if necessary. + * If the sanitize method was called, nothing will be tainted. + */ + override def returnFlow(exit: NewJavaStatement, in: Fact, call: NewJavaStatement, callFact: Fact): Set[Fact] = { + val in_set = Set(in) // dirty hack for compatibility with old code + val callee = exit.callable() + /** + * Checks whether the callee's formal parameter is of a reference type. + */ + def isRefTypeParam(index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (callee.isStatic) 0 else 1 + callee.descriptor.parameterType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + - parameterOffset + ).isReferenceType + } + + if (sanitizesReturnValue(callee)) return Set.empty + val callStatement = asCall(call.stmt) + val allParams = callStatement.allParams + var flows: Set[Fact] = Set.empty + in_set.foreach { + // Taint actual parameter if formal parameter is tainted + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(Variable) + + // Taint element of actual parameter if element of formal parameter is tainted + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) + + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + // Taint field of actual parameter if field of formal parameter is tainted + val param = + allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) + param.asVar.definedBy.foreach { defSite ⇒ + flows += InstanceField(defSite, declClass, taintedField) + } + + case sf: StaticField ⇒ flows += sf + + // Track the call chain to the sink back + case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ + flows += FlowFact(JavaMethod(call.method) +: flow) + case _ ⇒ + } + + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + in_set.foreach { + case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + flows += Variable(call.index) + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + flows += ArrayElement(call.index, taintedIndex) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + flows += InstanceField(call.index, declClass, taintedField) + case NullFact ⇒ + val taints = createTaints(callee, call) + if (taints.nonEmpty) flows ++= taints + case _ ⇒ // Nothing to do + } + } + val flowFact = createFlowFact(callee, call, in) + if (flowFact.isDefined) flows += flowFact.get + + flows + } + + /** + * Removes taints according to `sanitizesParameter`. + */ + override def callToReturnFlow(call: NewJavaStatement, in: Fact): Set[Fact] = + if (sanitizesParameter(call, in)) Set() else Set(in) + + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a vairbale. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + protected def createTaints(callee: Method, call: NewJavaStatement): Set[Fact] + + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFact(callee: Method, call: NewJavaStatement, + in: Fact): Option[FlowFact] + + /** + * If a parameter is tainted, the result will also be tainted. + * We assume that the callee does not call the source method. + */ + override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { + super.outsideAnalysisContext(callee) match { + case Some(_) ⇒ Some(((call: NewJavaStatement, successor: NewJavaStatement, in: Fact) ⇒ { + val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params + if (call.stmt.astID == Assignment.ASTID && (in match { + case Variable(index) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case ArrayElement(index, _) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case _ ⇒ false + })) Set(Variable(call.index)) + else Set.empty + }): OutsideAnalysisContextHandler) + case None ⇒ None + } + + } + + /** + * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. + * True by default. This method can be overwritten by a subclass. + * + * @param callee The callee. + * @return True, by default. + */ + protected def relevantCallee(callee: Method): Boolean = true + + /** + * Creates new facts for an assignment. A new fact for the assigned variable will be created, + * if the expression contains a tainted variable + * + * @param expression The source expression of the assignment + * @param statement The assignment statement + * @param in The incoming facts + * @return The new facts, created by the assignment + */ + private def createNewTaints(expression: Expr[V], statement: NewJavaStatement, in: Fact): Set[Fact] = { + val in_set: Set[Fact] = Set(in) // dirty fix to reuse old code + expression.astID match { + case Var.ASTID ⇒ + val definedBy = expression.asVar.definedBy + in_set.collect { + case Variable(index) if definedBy.contains(index) ⇒ + Variable(statement.index) + } + case ArrayLoad.ASTID ⇒ + val loadExpression = expression.asArrayLoad + val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy + if (in_set.exists { + // One specific array element may be tainted + case ArrayElement(index, taintedElement) ⇒ + val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) + arrayDefinedBy.contains(index) && + (loadedIndex.isEmpty || taintedElement == loadedIndex.get) + // Or the whole array + case Variable(index) ⇒ arrayDefinedBy.contains(index) + case _ ⇒ false + }) Set(Variable(statement.index)) + else + Set.empty + case GetField.ASTID ⇒ + val get = expression.asGetField + val objectDefinedBy = get.objRef.asVar.definedBy + if (in_set.exists { + // The specific field may be tainted + case InstanceField(index, _, taintedField) ⇒ + taintedField == get.name && objectDefinedBy.contains(index) + // Or the whole object + case Variable(index) ⇒ objectDefinedBy.contains(index) + case _ ⇒ false + }) + Set(Variable(statement.index)) + else + Set.empty + case GetStatic.ASTID ⇒ + val get = expression.asGetStatic + if (in_set.contains(StaticField(get.declaringClass, get.name))) + Set(Variable(statement.index)) + else Set.empty + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | + NewArray.ASTID | ArrayLength.ASTID ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) + case _ ⇒ Set.empty + } + } + + /** + * Checks, if the result of some variable expression could be tainted. + * + * @param expression The variable expression. + * @param in The current data flow facts. + * @return True, if the expression could be tainted + */ + private def isTainted(expression: Expr[V], in: Fact): Boolean = { + val definedBy = expression.asVar.definedBy + expression.isVar && (in match { + case Variable(index) ⇒ definedBy.contains(index) + case ArrayElement(index, _) ⇒ definedBy.contains(index) + case InstanceField(index, _, _) ⇒ definedBy.contains(index) + case _ ⇒ false + }) + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala new file mode 100644 index 0000000000..e435a3d0d3 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala @@ -0,0 +1,24 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +trait NewTaintProblem[C, Statement, IFDSFact] { + + /** + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. + * + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. + */ + protected def sanitizesReturnValue(callee: C): Boolean + + /** + * Called in callToReturnFlow. This method can return whether the input fact + * will be removed after `callee` was called. I.e. the method could sanitize parameters. + * + * @param call The call statement. + * @param in The fact which holds before the call. + * @return Whether in will be removed after the call. + */ + protected def sanitizesParameter(call: Statement, in: IFDSFact): Boolean +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala new file mode 100644 index 0000000000..8c506007f6 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala @@ -0,0 +1,23 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.properties + +import org.opalj.fpcf.PropertyKey +import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} +import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact + +case class NewTaint(flows: Map[NewJavaStatement, Set[Fact]]) extends IFDSProperty[NewJavaStatement, Fact] { + + override type Self = NewTaint + override def create(result: Map[NewJavaStatement, Set[Fact]]): IFDSProperty[NewJavaStatement, Fact] = new NewTaint(result) + + override def key: PropertyKey[NewTaint] = NewTaint.key +} + +object NewTaint extends IFDSPropertyMetaInformation[NewJavaStatement, Fact] { + + override type Self = NewTaint + override def create(result: Map[NewJavaStatement, Set[Fact]]): IFDSProperty[NewJavaStatement, Fact] = new NewTaint(result) + + val key: PropertyKey[NewTaint] = PropertyKey.create("NewTaint", new NewTaint(Map.empty)) +} diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala new file mode 100644 index 0000000000..1d30f75b02 --- /dev/null +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala @@ -0,0 +1,64 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint + +import org.opalj.br.Method +import org.opalj.br.analyses.{DeclaredMethodsKey, ProjectInformationKeys, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSPropertyMetaInformation} +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, NewJavaStatement} +import org.opalj.tac.fpcf.properties.NewTaint + +/** + * An analysis that checks, if the return value of the method `source` can flow to the parameter of + * the method `sink`. + * + * @author Mario Trageser + */ +class NewForwardTaintAnalysisFixture(project: SomeProject) + extends IFDSAnalysis()(project, new NewForwardTaintProblemFixture(project), NewTaint) + +class NewForwardTaintProblemFixture(p: SomeProject) extends NewForwardTaintProblem(p) { + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(Method, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ + classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") + .flatMap(classFile ⇒ classFile.methods) + .filter(method ⇒ method.isPublic && outsideAnalysisContext(method).isEmpty) + .map(method ⇒ method → NullFact) + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" + + /** + * We do not sanitize paramters. + */ + override protected def sanitizesParameter(call: NewJavaStatement, in: Fact): Boolean = false + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: Method, call: NewJavaStatement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact(callee: Method, call: NewJavaStatement, + in: Fact): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) Some(FlowFact(Seq(JavaMethod(call.method)))) + else None +} + +object NewForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact, Method, NewJavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new NewForwardTaintAnalysisFixture(p) + override def property: IFDSPropertyMetaInformation[NewJavaStatement, Fact] = NewTaint + override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(NewTaint)) + override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) +} From 3283b038c8f04032f0304347b4cdc2260c5f01d1 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 31 Mar 2022 14:35:43 +0200 Subject: [PATCH 31/67] Fix new forward taint analysis --- .../NewForwardTaintAnalysisFixtureTest.scala | 4 +- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 83 ++++++++++++------- .../scala/org/opalj/ifds/IFDSProblem.scala | 5 ++ .../fpcf/analyses/ifds/NewForwardICFG.scala | 42 ++++++++-- .../analyses/ifds/NewJavaIFDSProblem.scala | 13 ++- .../ifds/taint/NewForwardTaintProblem.scala | 2 +- 6 files changed, 102 insertions(+), 47 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala index 6ea6cc9271..584f5d5d32 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala @@ -32,8 +32,8 @@ class NewForwardTaintAnalysisFixtureTest extends PropertiesTest { val testContext = executeAnalyses(NewForwardTaintAnalysisFixtureScheduler) val project = testContext.project val eas = methodsWithAnnotations(project).map { - case (methods, entityString, annotations) ⇒ - ((methods, NullFact), entityString, annotations) + case (method, entityString, annotations) ⇒ + ((method, NullFact), entityString, annotations) } testContext.propertyStore.shutdown() validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 1b3b04dd30..d5f0e65381 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -21,7 +21,7 @@ case class Dependees[Work]() { case Some(dependee) => Dependee(dependee.eOptionP, dependee.worklist + work) case None => Dependee(propertyStore(epk), Set(work)) } - dependees += epk -> dependee + if(!dependee.eOptionP.isFinal) dependees += epk -> dependee dependee.eOptionP } @@ -103,7 +103,8 @@ protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statem val source: (C, IFDSFact), val dependees: Dependees[Work] = Dependees[Work](), val pathEdges: PathEdges[IFDSFact, S, C] = PathEdges[IFDSFact, S, C](), - var endSummaries: Set[(S, IFDSFact)] = Set.empty[(S, IFDSFact)] + var endSummaries: Set[(S, IFDSFact)] = Set.empty[(S, IFDSFact)], + var selfDependees: Set[Work] = Set.empty[Work] ) /** @@ -187,8 +188,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, /** * Collects the facts valid at all exit nodes based on the current results. * - * @return A map, mapping from each exit statement to the facts, which hold at - * after the exit statement. + * @return A map, mapping from each exit statement to the facts, which flow into exit statement. */ private def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { var result = Map.empty[S, Set[IFDSFact]] @@ -229,8 +229,11 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, val (statement, in, predecessor) = work icfg.getCalleesIfCallStatement(statement) match { case Some(callees) => handleCall(statement, callees, in) // ifds line 13 - case None => if (icfg.isExitStatement(statement)) handleExit(statement, in) // ifds line 21 - else handleOther(statement, in, predecessor) // ifds line 33 + case None => { + if (icfg.isExitStatement(statement)) handleExit(statement, in) // ifds line 21 + // in case of exceptions exit statements may also have some normal flow so no else here + handleOther(statement, in, predecessor) // ifds line 33 + } } } } @@ -261,25 +264,24 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, // Let the concrete analysis decide what to do. for { successor ← successors - out <- outsideAnalysisHandler(call, successor, in) // ifds line 17 (only summary edges) + out <- outsideAnalysisHandler(call, successor, in) // ifds line 17 (only summary edges) } { - propagate(successor, out, call) // ifds line 18 + propagate(successor, out, call) // ifds line 18 } case None ⇒ for { successor <- successors - out <- concreteCallFlow(call, callee, in) // ifds line 17 (only summary edges) + out <- concreteCallFlow(call, callee, in) // ifds line 17 (only summary edges) } { - propagate(successor, out, call) // ifds line 18 + propagate(successor, out, call) // ifds line 18 } } - - for { - successor ← successors - out <- callToReturnFlow(call, in) // ifds line 17 (without summary edge propagation) - } { - propagate(successor, out, call) // ifds line 18 - } + } + for { + successor ← successors + out <- callToReturnFlow(call, in) // ifds line 17 (without summary edge propagation) + } { + propagate(successor, out, call) // ifds line 18 } } @@ -287,14 +289,21 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, var result = Set.empty[IFDSFact] for (entryFact ← callFlow(call, callee, in)) { // ifds line 14 val e = (callee, entryFact) - val callFlows = state.dependees.get(e, propertyKey.key).asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] - val exitFacts: Map[S, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ - ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ - ep.ub.flows - case _ ⇒ - Map.empty + val exitFacts: Map[S, Set[IFDSFact]] = if(e == state.source) { + // handle self dependency on our own because property store can't handle it + state.selfDependees += work + collectResult(state) + } else { + // handle all other dependencies using property store + val callFlows = state.dependees.get(e, propertyKey.key).asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] + callFlows match { + case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ + ep.ub.flows + case _ ⇒ + Map.empty + } } for { (exitStatement, exitStatementFacts) <- exitFacts // ifds line 15.2 @@ -306,9 +315,15 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, result } - private def handleExit(statement: S, in: IFDSFact)(implicit state: State): Unit = { - state.endSummaries += ((statement, in)) // ifds line 21.1 + private def handleExit(statement: S, in: IFDSFact)(implicit state: State, worklist: Worklist): Unit = { + val newEdge = (statement, in) + if (!state.endSummaries.contains(newEdge)) { + state.endSummaries += ((statement, in)) // ifds line 21.1 + state.selfDependees.foreach(selfDependee => + worklist.enqueue(selfDependee)) + } // ifds lines 22 - 31 are handled by the dependency propagation of the property store + // except for self dependencies which are handled above } private def handleOther(statement: S, in: IFDSFact, predecessor: Option[S])(implicit state: State, worklist: Worklist): Unit = { @@ -337,7 +352,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, */ private def normalFlow(statement: S, in: IFDSFact, predecessor: Option[S]): Set[IFDSFact] = { statistics.normalFlow += 1 - ifdsProblem.normalFlow(statement, in, predecessor) + addNullFactIfConfigured(in, ifdsProblem.normalFlow(statement, in, predecessor)) } /** @@ -349,7 +364,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, */ private def callFlow(call: S, callee: C, in: IFDSFact): Set[IFDSFact] = { statistics.callFlow += 1 - ifdsProblem.callFlow(call, callee, in) + addNullFactIfConfigured(in, ifdsProblem.callFlow(call, callee, in)) } /** @@ -362,7 +377,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, */ private def returnFlow(exit: S, in: IFDSFact, call: S, callFact: IFDSFact): Set[IFDSFact] = { statistics.returnFlow += 1 - ifdsProblem.returnFlow(exit, in, call, callFact) + addNullFactIfConfigured(in, ifdsProblem.returnFlow(exit, in, call, callFact)) } /** @@ -373,7 +388,13 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, */ private def callToReturnFlow(call: S, in: IFDSFact): Set[IFDSFact] = { statistics.callToReturnFlow += 1 - ifdsProblem.callToReturnFlow(call, in) + addNullFactIfConfigured(in, ifdsProblem.callToReturnFlow(call, in)) + } + + private def addNullFactIfConfigured(in: IFDSFact, out: Set[IFDSFact]): Set[IFDSFact] = { + if (ifdsProblem.automaticallyPropagateNullFactInFlowFunctions && in == ifdsProblem.nullFact) + out + ifdsProblem.nullFact + else out } } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala index a41db42ba9..c97508ed00 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -17,6 +17,11 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: State */ def nullFact: IFDSFact + /** + * @return Whether the null Fact is automatically added to the result of every flow function where it is passed into + */ + def automaticallyPropagateNullFactInFlowFunctions: Boolean = true + /** * The entry points of this analysis. */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala index cbe98191e1..ce851b9bd4 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala @@ -7,7 +7,7 @@ import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.{FinalEP, PropertyStore} import org.opalj.ifds.{AbstractIFDSFact, ICFG} import org.opalj.tac.cg.TypeProviderKey -import org.opalj.tac.{DUVar, LazyDetachedTACAIKey, TACMethodParameter, TACode} +import org.opalj.tac.{Assignment, DUVar, Expr, ExprStmt, LazyDetachedTACAIKey, NonVirtualFunctionCall, NonVirtualMethodCall, StaticFunctionCall, StaticMethodCall, Stmt, TACMethodParameter, TACode, VirtualFunctionCall, VirtualMethodCall} import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.value.ValueInformation @@ -27,7 +27,7 @@ class NewForwardICFG[IFDSFact <: AbstractIFDSFact](implicit project: SomeProject */ override def startStatements(callable: Method): Set[NewJavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(callable) - Set(NewJavaStatement(callable, cfg.startBlock, code(0), 0, code, cfg)) + Set(NewJavaStatement(callable, 0, code, cfg)) } /** @@ -40,9 +40,7 @@ class NewForwardICFG[IFDSFact <: AbstractIFDSFact](implicit project: SomeProject statement.cfg .successors(statement.index) .toChain - .map { index ⇒ - NewJavaStatement(statement.method, statement.node, statement.code(index), index, statement.code, statement.cfg) - } + .map { index ⇒ NewJavaStatement(statement, index) } .toSet } @@ -53,12 +51,37 @@ class NewForwardICFG[IFDSFact <: AbstractIFDSFact](implicit project: SomeProject * @return All callables possibly called at the statement or None, if the statement does not * contain a call. */ - override def getCalleesIfCallStatement(statement: NewJavaStatement): Option[collection.Set[Method]] = { + override def getCalleesIfCallStatement(statement: NewJavaStatement): Option[collection.Set[Method]] = + statement.stmt.astID match { + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + Some(getCallees(statement)) + case Assignment.ASTID | ExprStmt.ASTID ⇒ + getExpression(statement.stmt).astID match { + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + Some(getCallees(statement)) + case _ ⇒ None + } + case _ ⇒ None + } + + /** + * Retrieves the expression of an assignment or expression statement. + * + * @param statement The statement. Must be an Assignment or ExprStmt. + * @return The statement's expression. + */ + private def getExpression(statement: Stmt[_]): Expr[_] = statement.astID match { + case Assignment.ASTID ⇒ statement.asAssignment.expr + case ExprStmt.ASTID ⇒ statement.asExprStmt.expr + case _ ⇒ throw new UnknownError("Unexpected statement") + } + + private def getCallees(statement: NewJavaStatement): collection.Set[Method] = { val pc = statement.code(statement.index).pc - val caller = declaredMethods(statement.method) + val caller = declaredMethods(statement.callable) val ep = propertyStore(caller, Callees.key) ep match { - case FinalEP(_, p) ⇒ Some(definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method))) + case FinalEP(_, p) ⇒ definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method)) case _ ⇒ throw new IllegalStateException( "call graph must be computed before the analysis starts" @@ -67,7 +90,8 @@ class NewForwardICFG[IFDSFact <: AbstractIFDSFact](implicit project: SomeProject } override def isExitStatement(statement: NewJavaStatement): Boolean = { - statement.cfg.bb(statement.index).successors.exists(_.isExitNode) + statement.index == statement.node.asBasicBlock.endPC && + statement.node.successors.exists(_.isExitNode) } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala index 03b03b023e..a1cf666a46 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala @@ -20,8 +20,6 @@ import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt, TACStmts} */ case class NewJavaStatement( method: Method, - node: CFGNode, - stmt: Stmt[V], index: Int, code: Array[Stmt[V]], cfg: CFG[Stmt[V], TACStmts[V]] @@ -30,12 +28,19 @@ case class NewJavaStatement( override def hashCode(): Int = method.hashCode() * 31 + index override def equals(o: Any): Boolean = o match { - case s: JavaStatement ⇒ s.index == index && s.method == method + case s: NewJavaStatement ⇒ s.index == index && s.method == method case _ ⇒ false } - override def toString: String = s"${method.toJava}" + override def toString: String = s"${method.signatureToJava(false)}[${index}]\n\t${stmt}\n\t${method.toJava}" override def callable(): Method = method + override def node(): CFGNode = cfg.bb(index) + def stmt: Stmt[V] = code(index) +} + +object NewJavaStatement { + def apply(oldStatement: NewJavaStatement, newIndex: Int): NewJavaStatement = + NewJavaStatement(oldStatement.method, newIndex, oldStatement.code, oldStatement.cfg) } abstract class NewJavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala index 51e0697ea8..04f5243396 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala @@ -210,7 +210,7 @@ abstract class NewForwardTaintProblem(project: SomeProject) /** * Called, when the exit to return facts are computed for some `callee` with the null fact and - * the callee's return value is assigned to a vairbale. + * the callee's return value is assigned to a variable. * Creates a taint, if necessary. * * @param callee The called method. From 313fd9a5d2178f112d52e2d50382278961778292 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 31 Mar 2022 16:16:43 +0200 Subject: [PATCH 32/67] Fix scalariformat and other compile errors --- .../heros/analyses/HerosAnalysis.scala | 4 +- .../analyses/HerosVariableTypeAnalysis.scala | 28 +- .../analyses/heros/analyses/VTAEquality.scala | 19 +- .../HerosBackwardClassForNameAnalysis.scala | 18 +- .../HerosForwardClassForNameAnalysis.scala | 26 +- .../analyses/heros/cfg/OpalBackwardICFG.scala | 39 +- .../analyses/heros/cfg/OpalForwardICFG.scala | 21 +- .../fpcf/analyses/heros/cfg/OpalICFG.scala | 36 +- .../NewForwardTaintAnalysisFixtureTest.scala | 38 +- .../DynamicConstantsCreationTest.scala | 2 +- .../org/opalj/ifds/AbstractIFDSFact.scala | 24 +- .../src/main/scala/org/opalj/ifds/ICFG.scala | 61 +- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 726 +++++++++--------- .../scala/org/opalj/ifds/IFDSProblem.scala | 180 ++--- .../main/scala/org/opalj/ifds/old/ICFG.scala | 2 +- .../org/opalj/ifds/old/IFDSAnalysis.scala | 2 +- .../scala/org/opalj/ifds/old/Subsumable.scala | 130 ++-- .../taint/NativeForwardTaintProblem.scala | 98 +-- .../SerializationRelatedCallsAnalysis.scala | 2 +- .../analyses/ifds/AbstractIFDSAnalysis.scala | 16 +- .../fpcf/analyses/ifds/NewForwardICFG.scala | 186 ++--- .../analyses/ifds/NewJavaIFDSProblem.scala | 64 +- .../ifds/taint/NewForwardTaintProblem.scala | 604 +++++++-------- .../analyses/ifds/taint/NewTaintProblem.scala | 34 +- .../opalj/tac/fpcf/properties/NewTaint.scala | 12 +- .../NewForwardTaintAnalysisFixture.scala | 70 +- 26 files changed, 1220 insertions(+), 1222 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala index 5f6529e38d..0de467bf9c 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala @@ -22,7 +22,7 @@ import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.fpcf.analyses.heros.cfg.OpalICFG -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement import org.opalj.tac.Assignment import org.opalj.tac.Call import org.opalj.tac.ExprStmt @@ -40,7 +40,7 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V * @author Mario Trageser */ abstract class HerosAnalysis[F](p: SomeProject, icfg: OpalICFG) - extends DefaultIFDSTabulationProblem[JavaStatement, F, Method, OpalICFG](icfg) { + extends DefaultIFDSTabulationProblem[NewJavaStatement, F, Method, OpalICFG](icfg) { /** * The project's property store. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala index ee9bb9b798..369320abc6 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala @@ -30,7 +30,7 @@ import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.DeclaredMethod import org.opalj.tac.fpcf.properties.cg.Callees -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement import org.opalj.tac.Assignment import org.opalj.tac.Expr import org.opalj.tac.GetStatic @@ -59,8 +59,8 @@ class HerosVariableTypeAnalysis( initialMethods: Map[Method, util.Set[VTAFact]] ) extends HerosAnalysis[VTAFact](p, icfg) { - override val initialSeeds: util.Map[JavaStatement, util.Set[VTAFact]] = { - var result: Map[JavaStatement, util.Set[VTAFact]] = Map.empty + override val initialSeeds: util.Map[NewJavaStatement, util.Set[VTAFact]] = { + var result: Map[NewJavaStatement, util.Set[VTAFact]] = Map.empty for ((m, facts) ← initialMethods) { result += icfg.getStartPointsOf(m).iterator().next() -> facts } @@ -69,13 +69,13 @@ class HerosVariableTypeAnalysis( override def createZeroValue(): VTAFact = VTANullFact - override protected def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, VTAFact, Method] = { + override protected def createFlowFunctionsFactory(): FlowFunctions[NewJavaStatement, VTAFact, Method] = { - new FlowFunctions[JavaStatement, VTAFact, Method]() { + new FlowFunctions[NewJavaStatement, VTAFact, Method]() { override def getNormalFlowFunction( - statement: JavaStatement, - succ: JavaStatement + statement: NewJavaStatement, + succ: NewJavaStatement ): FlowFunction[VTAFact] = { if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt @@ -103,7 +103,7 @@ class HerosVariableTypeAnalysis( } } - override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[VTAFact] = + override def getCallFlowFunction(stmt: NewJavaStatement, callee: Method): FlowFunction[VTAFact] = if (!insideAnalysisContext(callee)) KillAll.v() else { val callObject = asCall(stmt.stmt) @@ -129,10 +129,10 @@ class HerosVariableTypeAnalysis( } override def getReturnFlowFunction( - stmt: JavaStatement, + stmt: NewJavaStatement, callee: Method, - exit: JavaStatement, - succ: JavaStatement + exit: NewJavaStatement, + succ: NewJavaStatement ): FlowFunction[VTAFact] = if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { val returnValue = exit.stmt.asReturnValue.expr.asVar @@ -147,8 +147,8 @@ class HerosVariableTypeAnalysis( } else KillAll.v() override def getCallToReturnFlowFunction( - statement: JavaStatement, - succ: JavaStatement + statement: NewJavaStatement, + succ: NewJavaStatement ): FlowFunction[VTAFact] = { if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt @@ -227,7 +227,7 @@ class HerosVariableTypeAnalysis( callee.body.isDefined && (callee.classFile.fqn.startsWith("java/lang") || callee.classFile.fqn.startsWith("org/opalj/fpcf/fixtures/vta")) - private def getCallees(statement: JavaStatement): Iterator[DeclaredMethod] = { + private def getCallees(statement: NewJavaStatement): Iterator[DeclaredMethod] = { val context = typeProvider.newContext(declaredMethods(statement.method)) val FinalEP(_, callees) = propertyStore(declaredMethods(statement.method), Callees.key) callees.directCallees(context, statement.stmt.pc).map(_.method) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala index ed8486fa0a..606d03f934 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala @@ -31,7 +31,7 @@ import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG import org.opalj.tac.fpcf.analyses.ifds.CalleeType import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement import org.opalj.tac.fpcf.analyses.ifds.VariableType import org.opalj.tac.fpcf.analyses.ifds.VTAFact import org.opalj.tac.fpcf.analyses.ifds.VTANullFact @@ -115,7 +115,7 @@ object VTAEquality { piKeyUnidueId != PropertyStoreKey.uniqueId } val propertyStore = project.get(PropertyStoreKey) - var result = Map.empty[JavaStatement, Set[VTAFact]] + var result = Map.empty[NewJavaStatement, Set[VTAFact]] project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) case Some(requirements) ⇒ @@ -134,16 +134,17 @@ object VTAEquality { case FinalEP(_, VTAResult(map)) ⇒ map case _ ⇒ throw new RuntimeException } - entityResult.keys.foreach { statement ⇒ + entityResult.keys.foreach { oldStatement ⇒ + val statement = oldStatement.asNewJavaStatement /* - * Heros returns the facts before the statements. - * However, CalleeType facts hold after the statement and are therefore not returned. - * We do not consider them in this test. - * Additionally, Heros does not return null facts, so we filter them. - */ + * Heros returns the facts before the statements. + * However, CalleeType facts hold after the statement and are therefore not returned. + * We do not consider them in this test. + * Additionally, Heros does not return null facts, so we filter them. + */ result = result.updated( statement, - result.getOrElse(statement, Set.empty) ++ entityResult(statement) + result.getOrElse(statement, Set.empty) ++ entityResult(oldStatement) .filter(fact ⇒ fact != VTANullFact && !fact.isInstanceOf[CalleeType]) ) } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala index 2145578171..2423668655 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -13,7 +13,7 @@ import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.heros.cfg.OpalBackwardICFG import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, NewJavaStatement} import org.opalj.tac.Assignment import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement import org.opalj.tac.fpcf.analyses.ifds.taint.Variable @@ -47,7 +47,7 @@ import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem */ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) extends HerosTaintAnalysis(p, icfg) { - override val initialSeeds: util.Map[JavaStatement, util.Set[Fact]] = + override val initialSeeds: util.Map[NewJavaStatement, util.Set[Fact]] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "java/lang/Class") .flatMap(classFile ⇒ classFile.methods) @@ -58,11 +58,11 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) override def followReturnsPastSeeds(): Boolean = true - override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { + override def createFlowFunctionsFactory(): FlowFunctions[NewJavaStatement, Fact, Method] = { - new FlowFunctions[JavaStatement, Fact, Method]() { + new FlowFunctions[NewJavaStatement, Fact, Method]() { - override def getNormalFlowFunction(statement: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { + override def getNormalFlowFunction(statement: NewJavaStatement, succ: NewJavaStatement): FlowFunction[Fact] = { val method = statement.method val stmt = statement.stmt source: Fact ⇒ { @@ -123,7 +123,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } - override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[Fact] = { + override def getCallFlowFunction(stmt: NewJavaStatement, callee: Method): FlowFunction[Fact] = { val callObject = asCall(stmt.stmt) val staticCall = callee.isStatic source: Fact ⇒ { @@ -165,7 +165,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } } - override def getReturnFlowFunction(statement: JavaStatement, callee: Method, exit: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { + override def getReturnFlowFunction(statement: NewJavaStatement, callee: Method, exit: NewJavaStatement, succ: NewJavaStatement): FlowFunction[Fact] = { // If a method has no caller, returnFlow will be called with a null statement. if (statement == null) return Identity.v() val stmt = statement.stmt @@ -195,7 +195,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) }).asJava } - override def getCallToReturnFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = + override def getCallToReturnFlowFunction(stmt: NewJavaStatement, succ: NewJavaStatement): FlowFunction[Fact] = Identity.v() } } @@ -217,7 +217,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) }.toSet } - private def createNewTaints(expression: Expr[V], statement: JavaStatement): Set[Fact] = + private def createNewTaints(expression: Expr[V], statement: NewJavaStatement): Set[Fact] = expression.astID match { case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) case ArrayLoad.ASTID ⇒ diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index f7f86c7aa2..16f2ea3687 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -30,7 +30,7 @@ import org.opalj.tac.PutField import org.opalj.tac.PutStatic import org.opalj.tac.ReturnValue import org.opalj.tac.Var -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, NewJavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement import org.opalj.tac.fpcf.analyses.ifds.taint.Fact import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact @@ -62,8 +62,8 @@ class HerosForwardClassForNameAnalysis( initialMethods: Map[Method, util.Set[Fact]] ) extends HerosTaintAnalysis(p, icfg) { - override val initialSeeds: util.Map[JavaStatement, util.Set[Fact]] = { - var result: Map[JavaStatement, util.Set[Fact]] = Map.empty + override val initialSeeds: util.Map[NewJavaStatement, util.Set[Fact]] = { + var result: Map[NewJavaStatement, util.Set[Fact]] = Map.empty for ((m, facts) ← initialMethods) { result += icfg.getStartPointsOf(m).iterator().next() -> facts } @@ -74,11 +74,11 @@ class HerosForwardClassForNameAnalysis( var flowFacts = Map.empty[Method, Set[FlowFact]] - override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { + override def createFlowFunctionsFactory(): FlowFunctions[NewJavaStatement, Fact, Method] = { - new FlowFunctions[JavaStatement, Fact, Method]() { + new FlowFunctions[NewJavaStatement, Fact, Method]() { - override def getNormalFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { + override def getNormalFlowFunction(stmt: NewJavaStatement, succ: NewJavaStatement): FlowFunction[Fact] = { stmt.stmt.astID match { case Assignment.ASTID ⇒ handleAssignment(stmt, stmt.stmt.asAssignment.expr) @@ -127,7 +127,7 @@ class HerosForwardClassForNameAnalysis( } } - def handleAssignment(stmt: JavaStatement, expr: Expr[V]): FlowFunction[Fact] = + def handleAssignment(stmt: NewJavaStatement, expr: Expr[V]): FlowFunction[Fact] = expr.astID match { case Var.ASTID ⇒ (source: Fact) ⇒ { @@ -194,7 +194,7 @@ class HerosForwardClassForNameAnalysis( case _ ⇒ Identity.v() } - override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[Fact] = { + override def getCallFlowFunction(stmt: NewJavaStatement, callee: Method): FlowFunction[Fact] = { val callObject = asCall(stmt.stmt) val allParams = callObject.allParams if (relevantCallee(callee)) { @@ -248,10 +248,10 @@ class HerosForwardClassForNameAnalysis( } override def getReturnFlowFunction( - stmt: JavaStatement, + stmt: NewJavaStatement, callee: Method, - exit: JavaStatement, - succ: JavaStatement + exit: NewJavaStatement, + succ: NewJavaStatement ): FlowFunction[Fact] = { def isRefTypeParam(index: Int): Boolean = @@ -344,8 +344,8 @@ class HerosForwardClassForNameAnalysis( } override def getCallToReturnFlowFunction( - stmt: JavaStatement, - succ: JavaStatement + stmt: NewJavaStatement, + succ: NewJavaStatement ): FlowFunction[Fact] = Identity.v() } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala index cd6e7be608..dd5b26480d 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala @@ -1,19 +1,15 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.heros.cfg -import java.util.{List ⇒ JList} -import java.util.{Collection ⇒ JCollection} -import java.util.{Set ⇒ JSet} -import java.util.Collections -import java.util.concurrent.ConcurrentLinkedQueue - -import scala.collection.JavaConverters._ - -import org.opalj.br.analyses.SomeProject import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject import org.opalj.br.cfg.BasicBlock -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.TACode +import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement + +import java.util.concurrent.ConcurrentLinkedQueue +import java.util.{Collections, Collection ⇒ JCollection, List ⇒ JList, Set ⇒ JSet} +import scala.collection.JavaConverters._ /** * A backward ICFG for Heros analyses. @@ -22,22 +18,22 @@ import org.opalj.tac.TACode */ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { - override def getPredsOf(stmt: JavaStatement): JList[JavaStatement] = super.getSuccsOf(stmt) + override def getPredsOf(stmt: NewJavaStatement): JList[NewJavaStatement] = super.getSuccsOf(stmt) - override def getSuccsOf(stmt: JavaStatement): JList[JavaStatement] = super.getPredsOf(stmt) + override def getSuccsOf(stmt: NewJavaStatement): JList[NewJavaStatement] = super.getPredsOf(stmt) - override def getStartPointsOf(m: Method): JCollection[JavaStatement] = { + override def getStartPointsOf(m: Method): JCollection[NewJavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).map { case bb: BasicBlock ⇒ val index = bb.endPC - JavaStatement(m, bb, code(index), index, code, cfg) + NewJavaStatement(m, index, code, cfg) }.asJava } - override def isExitStmt(stmt: JavaStatement): Boolean = stmt.index == 0 + override def isExitStmt(stmt: NewJavaStatement): Boolean = stmt.index == 0 - override def isStartPoint(stmt: JavaStatement): Boolean = { + override def isStartPoint(stmt: NewJavaStatement): Boolean = { val cfg = stmt.cfg val index = stmt.index (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).exists { @@ -45,8 +41,8 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { } } - override def allNonCallStartNodes(): JSet[JavaStatement] = { - val res = new ConcurrentLinkedQueue[JavaStatement] + override def allNonCallStartNodes(): JSet[NewJavaStatement] = { + val res = new ConcurrentLinkedQueue[NewJavaStatement] project.parForeachMethodWithBody() { mi ⇒ val m = mi.method val TACode(_, code, _, cfg, _) = tacai(m) @@ -57,8 +53,7 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { } var index = 0 while (index < endIndex) { - val stmt = code(index) - val statement = JavaStatement(m, cfg.bb(index), stmt, index, code, cfg) + val statement = NewJavaStatement(m, index, code, cfg) if (!(isCallStmt(statement) || startIndices.contains(index))) res.add(statement) index += 1 @@ -68,10 +63,10 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { Collections.emptySet() } - def getExitStmt(method: Method): JavaStatement = { + def getExitStmt(method: Method): NewJavaStatement = { val tac = tacai(method) val cfg = tac.cfg val code = tac.stmts - JavaStatement(method, cfg.startBlock, code(0), 0, code, cfg) + NewJavaStatement(method, 0, code, cfg) } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala index 092c13919f..14706dff1a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala @@ -9,7 +9,7 @@ import java.util.concurrent.ConcurrentLinkedQueue import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.tac.TACode -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement /** * A forward ICFG for Heros analyses. @@ -18,25 +18,24 @@ import org.opalj.tac.fpcf.analyses.ifds.JavaStatement */ class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { - override def getStartPointsOf(m: Method): JCollection[JavaStatement] = { + override def getStartPointsOf(m: Method): JCollection[NewJavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) - Collections.singletonList(JavaStatement(m, cfg.startBlock, code(0), 0, code, cfg)) + Collections.singletonList(NewJavaStatement(m, 0, code, cfg)) } - override def isExitStmt(stmt: JavaStatement): Boolean = stmt.cfg.bb(stmt.index).successors.exists(_.isExitNode) + override def isExitStmt(stmt: NewJavaStatement): Boolean = stmt.cfg.bb(stmt.index).successors.exists(_.isExitNode) - override def isStartPoint(stmt: JavaStatement): Boolean = stmt.index == 0 + override def isStartPoint(stmt: NewJavaStatement): Boolean = stmt.index == 0 - override def allNonCallStartNodes(): JSet[JavaStatement] = { - val res = new ConcurrentLinkedQueue[JavaStatement] + override def allNonCallStartNodes(): JSet[NewJavaStatement] = { + val res = new ConcurrentLinkedQueue[NewJavaStatement] project.parForeachMethodWithBody() { mi ⇒ val m = mi.method val TACode(_, code, _, cfg, _) = tacai(m) val endIndex = code.length var index = 1 while (index < endIndex) { - val stmt = code(index) - val statement = JavaStatement(m, cfg.bb(index), stmt, index, code, cfg) + val statement = NewJavaStatement(m, index, code, cfg) if (!isCallStmt(statement)) res.add(statement) index += 1 @@ -46,13 +45,13 @@ class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { Collections.emptySet() } - def getExitStmts(method: Method): Iterator[JavaStatement] = { + def getExitStmts(method: Method): Iterator[NewJavaStatement] = { val tac = tacai(method) val cfg = tac.cfg val code = tac.stmts cfg.allNodes.filter(_.isExitNode).flatMap(_.predecessors).map { bb ⇒ val endPc = bb.asBasicBlock.endPC - JavaStatement(method, bb, code(endPc), endPc, code, cfg) + NewJavaStatement(method, endPc, code, cfg) } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala index e0d4921ff9..6fe2fce82e 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala @@ -18,7 +18,7 @@ import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.br.DefinedMethod import org.opalj.br.MultipleDefinedMethods import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement import org.opalj.tac.Assignment import org.opalj.tac.DUVar import org.opalj.tac.FunctionCall @@ -44,7 +44,7 @@ import org.opalj.tac.fpcf.analyses.cg.TypeProvider * @param project The project, which is analyzed. * @author Mario Trageser */ -abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaStatement, Method] { +abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[NewJavaStatement, Method] { val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) @@ -52,31 +52,31 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) - override def getMethodOf(stmt: JavaStatement): Method = stmt.method + override def getMethodOf(stmt: NewJavaStatement): Method = stmt.method - override def getPredsOf(stmt: JavaStatement): JList[JavaStatement] = { + override def getPredsOf(stmt: NewJavaStatement): JList[NewJavaStatement] = { stmt.cfg .predecessors(stmt.index) .toChain .map { index ⇒ - JavaStatement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) + NewJavaStatement(stmt.method, index, stmt.code, stmt.cfg) } .toList .asJava } - override def getSuccsOf(stmt: JavaStatement): JList[JavaStatement] = { + override def getSuccsOf(stmt: NewJavaStatement): JList[NewJavaStatement] = { stmt.cfg .successors(stmt.index) .toChain .map { index ⇒ - JavaStatement(stmt.method, stmt.node, stmt.code(index), index, stmt.code, stmt.cfg) + NewJavaStatement(stmt.method, index, stmt.code, stmt.cfg) } .toList .asJava } - override def getCalleesOfCallAt(callInstr: JavaStatement): JCollection[Method] = { + override def getCalleesOfCallAt(callInstr: NewJavaStatement): JCollection[Method] = { val declaredMethod = declaredMethods(callInstr.method) val FinalEP(_, callees) = ps(declaredMethod, Callees.key) callees @@ -92,7 +92,7 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta .asJava } - override def getCallersOf(m: Method): JCollection[JavaStatement] = { + override def getCallersOf(m: Method): JCollection[NewJavaStatement] = { val declaredMethod = declaredMethods(m) val FinalEP(_, callers) = ps(declaredMethod, Callers.key) callers @@ -101,31 +101,31 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta case (method, pc, true) ⇒ val TACode(_, code, pcToIndex, cfg, _) = tacai(method.definedMethod) val index = pcToIndex(pc) - Some(JavaStatement(method.definedMethod, cfg.bb(index), code(index), index, code, cfg)) + Some(NewJavaStatement(method.definedMethod, index, code, cfg)) case _ ⇒ None } .toSet .asJava } - override def getCallsFromWithin(m: Method): JSet[JavaStatement] = { + override def getCallsFromWithin(m: Method): JSet[NewJavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) code.zipWithIndex .collect { - case (mc: MethodCall[V], index) ⇒ JavaStatement(m, cfg.bb(index), mc, index, code, cfg) + case (mc: MethodCall[V], index) ⇒ NewJavaStatement(m, index, code, cfg) case (as @ Assignment(_, _, _: FunctionCall[V]), index) ⇒ - JavaStatement(m, cfg.bb(index), as, index, code, cfg) + NewJavaStatement(m, index, code, cfg) case (ex @ ExprStmt(_, _: FunctionCall[V]), index) ⇒ - JavaStatement(m, cfg.bb(index), ex, index, code, cfg) + NewJavaStatement(m, index, code, cfg) } .toSet .asJava } - override def getReturnSitesOfCallAt(callInstr: JavaStatement): JCollection[JavaStatement] = + override def getReturnSitesOfCallAt(callInstr: NewJavaStatement): JCollection[NewJavaStatement] = getSuccsOf(callInstr) - override def isCallStmt(stmt: JavaStatement): Boolean = { + override def isCallStmt(stmt: NewJavaStatement): Boolean = { def isCallExpr(expr: Expr[V]) = expr.astID match { case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ true @@ -141,8 +141,8 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta } } - override def isFallThroughSuccessor(stmt: JavaStatement, succ: JavaStatement): Boolean = ??? + override def isFallThroughSuccessor(stmt: NewJavaStatement, succ: NewJavaStatement): Boolean = ??? - override def isBranchTarget(stmt: JavaStatement, succ: JavaStatement): Boolean = ??? + override def isBranchTarget(stmt: NewJavaStatement, succ: NewJavaStatement): Boolean = ??? } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala index 584f5d5d32..f99c3468d0 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala @@ -16,26 +16,26 @@ import java.net.URL */ class NewForwardTaintAnalysisFixtureTest extends PropertiesTest { - override def init(p: Project[URL]): Unit = { - p.updateProjectInformationKeyInitializationData( - AIDomainFactoryKey - )( - (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ - Set[Class[_ <: AnyRef]]( - classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData( + AIDomainFactoryKey + )( + (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ + Set[Class[_ <: AnyRef]]( + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] + ) ) - ) - p.get(RTACallGraphKey) - } + p.get(RTACallGraphKey) + } - describe("Test the ForwardFlowPath annotations") { - val testContext = executeAnalyses(NewForwardTaintAnalysisFixtureScheduler) - val project = testContext.project - val eas = methodsWithAnnotations(project).map { - case (method, entityString, annotations) ⇒ - ((method, NullFact), entityString, annotations) + describe("Test the ForwardFlowPath annotations") { + val testContext = executeAnalyses(NewForwardTaintAnalysisFixtureScheduler) + val project = testContext.project + val eas = methodsWithAnnotations(project).map { + case (method, entityString, annotations) ⇒ + ((method, NullFact), entityString, annotations) + } + testContext.propertyStore.shutdown() + validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) } - testContext.propertyStore.shutdown() - validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) - } } diff --git a/OPAL/bc/src/test/scala/org/opalj/test/fixtures/dynamicConstants/DynamicConstantsCreationTest.scala b/OPAL/bc/src/test/scala/org/opalj/test/fixtures/dynamicConstants/DynamicConstantsCreationTest.scala index 23fb1d6517..73317bbe1b 100644 --- a/OPAL/bc/src/test/scala/org/opalj/test/fixtures/dynamicConstants/DynamicConstantsCreationTest.scala +++ b/OPAL/bc/src/test/scala/org/opalj/test/fixtures/dynamicConstants/DynamicConstantsCreationTest.scala @@ -406,7 +406,7 @@ class DynamicConstantsCreationTest extends AnyFlatSpec with Matchers with Before (0xff & 176).toByte // areturn )) )) - ), + ) ), attributes = RefArray( BootstrapMethods_attribute(17, RefArray( diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala index 43739d1edb..e200436c3f 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala @@ -18,14 +18,14 @@ trait AbstractIFDSNullFact extends AbstractIFDSFact */ trait SubsumableFact extends AbstractIFDSFact { - /** - * Checks, if this fact subsumes an `other` fact. - * - * @param other The other fact. - * @param project The analyzed project. - * @return True, if this fact subsumes the `other`fact - */ - def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean + /** + * Checks, if this fact subsumes an `other` fact. + * + * @param other The other fact. + * @param project The analyzed project. + * @return True, if this fact subsumes the `other`fact + */ + def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean } /** @@ -33,8 +33,8 @@ trait SubsumableFact extends AbstractIFDSFact { */ trait SubsumableNullFact extends SubsumableFact with AbstractIFDSNullFact { - /** - * The null fact cannot subsume another fact. - */ - override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = false + /** + * The null fact cannot subsume another fact. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = false } \ No newline at end of file diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala index 6fa1ac8579..02c951fbfc 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala @@ -1,40 +1,39 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ifds -import scala.collection.{Set => SomeSet} +import scala.collection.{Set ⇒ SomeSet} abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]] { - /** - * Determines the statements at which the analysis starts. - * - * @param callable The analyzed callable. - * @return The statements at which the analysis starts. - */ - def startStatements(callable: C): Set[S] + /** + * Determines the statements at which the analysis starts. + * + * @param callable The analyzed callable. + * @return The statements at which the analysis starts. + */ + def startStatements(callable: C): Set[S] + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + def nextStatements(statement: S): Set[S] - /** - * Determines the statement, that will be analyzed after some other `statement`. - * - * @param statement The source statement. - * @return The successor statements - */ - def nextStatements(statement: S): Set[S] + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + def getCalleesIfCallStatement(statement: S): Option[SomeSet[C]] - /** - * Gets the set of all methods possibly called at some statement. - * - * @param statement The statement. - * @return All callables possibly called at the statement or None, if the statement does not - * contain a call. - */ - def getCalleesIfCallStatement(statement: S): Option[SomeSet[C]] - - /** - * Determines whether the statement is an exit statement. - * - * @param statement The source statement. - * @return Whether the statement flow may exit its callable (function/method) - */ - def isExitStatement(statement: S): Boolean + /** + * Determines whether the statement is an exit statement. + * + * @param statement The source statement. + * @return Whether the statement flow may exit its callable (function/method) + */ + def isExitStatement(statement: S): Boolean } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index d5f0e65381..6bacadc905 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -5,34 +5,34 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} import org.opalj.fpcf._ -import scala.collection.{mutable, Set => SomeSet} +import scala.collection.{mutable, Set ⇒ SomeSet} abstract class Statement[C, Node] { - def node(): Node - def callable(): C + def node(): Node + def callable(): C } case class Dependees[Work]() { - case class Dependee(eOptionP: SomeEOptionP, worklist: Set[Work] = Set.empty) - var dependees = Map.empty[SomeEPK, Dependee] - def get(entity: Entity, propertyKey: PropertyKey[Property])(implicit propertyStore: PropertyStore, work: Work): SomeEOptionP = { - val epk = EPK(entity, propertyKey) - val dependee = dependees.get(epk) match { - case Some(dependee) => Dependee(dependee.eOptionP, dependee.worklist + work) - case None => Dependee(propertyStore(epk), Set(work)) + case class Dependee(eOptionP: SomeEOptionP, worklist: Set[Work] = Set.empty) + var dependees = Map.empty[SomeEPK, Dependee] + def get(entity: Entity, propertyKey: PropertyKey[Property])(implicit propertyStore: PropertyStore, work: Work): SomeEOptionP = { + val epk = EPK(entity, propertyKey) + val dependee = dependees.get(epk) match { + case Some(dependee) ⇒ Dependee(dependee.eOptionP, dependee.worklist + work) + case None ⇒ Dependee(propertyStore(epk), Set(work)) + } + if (!dependee.eOptionP.isFinal) dependees += epk -> dependee + dependee.eOptionP + } + + def forResult(): Set[SomeEOptionP] = { + dependees.values.map(_.eOptionP).toSet + } + def takeWork(epk: SomeEPK): Set[Work] = { + val dependee = dependees(epk) + dependees -= epk + dependee.worklist } - if(!dependee.eOptionP.isFinal) dependees += epk -> dependee - dependee.eOptionP - } - - def forResult(): Set[SomeEOptionP] = { - dependees.values.map(_.eOptionP).toSet - } - def takeWork(epk: SomeEPK): Set[Work] = { - val dependee = dependees(epk) - dependees -= epk - dependee.worklist - } } /** @@ -40,57 +40,57 @@ case class Dependees[Work]() { * An entry of (statement, fact) means an edge (s0, source fact) -> (statement, fact) exists, * that is the fact reaches the statement as an input. * Source fact is the fact within the analysis entity. - * */ + */ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[C, _], C]() { - var edges = Map.empty[S, Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] - - /** - * Add the edge (s0, source fact) -> (statement, fact) to the path edges. - * Optionally give a predecessor for the statement. This is used for phi statements - * to distinguish the input flow and merge the facts later. - * @param statement the destination statement of the edge - * @param predecessor the predecessor of the statement. - * @return whether the edge was new - */ - def add(statement: S, fact: IFDSFact, predecessor: Option[S] = None): Boolean = { - // TODO: subsuming - edges.get(statement) match { - case None => - predecessor match { - case Some(predecessor) => - edges = edges.updated(statement, Right(Map(predecessor -> Set(fact)))) - case None => - edges = edges.updated(statement, Left(Set(fact))) - } - true - case Some(Left(existingFacts)) => - if (predecessor.isDefined) throw new IllegalArgumentException(s"${statement} does not accept a predecessor") - val isNew = !existingFacts.contains(fact) - edges = edges.updated(statement, Left(existingFacts + fact)) - isNew - case Some(Right(existingFacts)) => - predecessor match { - case None => throw new IllegalArgumentException(s"${statement} requires a predecessor") - case Some(predecessor) => existingFacts.get(statement) match { - case Some(existingPredecessorFacts) => { - val isNew = !existingPredecessorFacts.contains(fact) - edges = edges.updated(statement, Right(existingFacts.updated(predecessor, existingPredecessorFacts + fact))) - isNew + var edges = Map.empty[S, Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] + + /** + * Add the edge (s0, source fact) -> (statement, fact) to the path edges. + * Optionally give a predecessor for the statement. This is used for phi statements + * to distinguish the input flow and merge the facts later. + * @param statement the destination statement of the edge + * @param predecessor the predecessor of the statement. + * @return whether the edge was new + */ + def add(statement: S, fact: IFDSFact, predecessor: Option[S] = None): Boolean = { + // TODO: subsuming + edges.get(statement) match { + case None ⇒ + predecessor match { + case Some(predecessor) ⇒ + edges = edges.updated(statement, Right(Map(predecessor -> Set(fact)))) + case None ⇒ + edges = edges.updated(statement, Left(Set(fact))) } - case None => { - edges = edges.updated(statement, Right(existingFacts.updated(predecessor, Set(fact)))) - true + true + case Some(Left(existingFacts)) ⇒ + if (predecessor.isDefined) throw new IllegalArgumentException(s"${statement} does not accept a predecessor") + val isNew = !existingFacts.contains(fact) + edges = edges.updated(statement, Left(existingFacts + fact)) + isNew + case Some(Right(existingFacts)) ⇒ + predecessor match { + case None ⇒ throw new IllegalArgumentException(s"${statement} requires a predecessor") + case Some(predecessor) ⇒ existingFacts.get(statement) match { + case Some(existingPredecessorFacts) ⇒ { + val isNew = !existingPredecessorFacts.contains(fact) + edges = edges.updated(statement, Right(existingFacts.updated(predecessor, existingPredecessorFacts + fact))) + isNew + } + case None ⇒ { + edges = edges.updated(statement, Right(existingFacts.updated(predecessor, Set(fact)))) + true + } + } } - } - } + } } - } - /** - * @param statement - * @return The edges reaching statement if any. In case the statement minds about predecessors it is a map with an entry for each predecessor - */ - def get(statement: S): Option[Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] = edges.get(statement) + /** + * @param statement + * @return The edges reaching statement if any. In case the statement minds about predecessors it is a map with an entry for each predecessor + */ + def get(statement: S): Option[Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] = edges.get(statement) } /** @@ -100,21 +100,21 @@ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[C, _], C]() { * @param endSummaries Output facts of the analyzed callable as pairs of exit statement and fact */ protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _], Work]( - val source: (C, IFDSFact), - val dependees: Dependees[Work] = Dependees[Work](), - val pathEdges: PathEdges[IFDSFact, S, C] = PathEdges[IFDSFact, S, C](), - var endSummaries: Set[(S, IFDSFact)] = Set.empty[(S, IFDSFact)], - var selfDependees: Set[Work] = Set.empty[Work] - ) + val source: (C, IFDSFact), + val dependees: Dependees[Work] = Dependees[Work](), + val pathEdges: PathEdges[IFDSFact, S, C] = PathEdges[IFDSFact, S, C](), + var endSummaries: Set[(S, IFDSFact)] = Set.empty[(S, IFDSFact)], + var selfDependees: Set[Work] = Set.empty[Work] +) /** * Contains int variables, which count, how many times some method was called. */ protected class Statistics { - var normalFlow = 0 - var callFlow = 0 - var returnFlow = 0 - var callToReturnFlow = 0 + var normalFlow = 0 + var callFlow = 0 + var returnFlow = 0 + var callToReturnFlow = 0 } protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalysis @@ -126,308 +126,310 @@ protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalys * @tparam IFDSFact */ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]]( - implicit - project: SomeProject, - val ifdsProblem: IFDSProblem[IFDSFact, C, S], - val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact]) extends ProjectFPCFAnalysis(project) { - type Work = (S, IFDSFact, Option[S]) // statement, fact, predecessor - type Worklist = mutable.Queue[Work] - type State = IFDSState[IFDSFact, C, S, Work] - - implicit var statistics = new Statistics - val icfg = ifdsProblem.icfg - - /** - * Performs an IFDS analysis for a method-fact-pair. - * - * @param entity The method-fact-pair, that will be analyzed. - * @return An IFDS property mapping from exit statements to the facts valid after these exit - * statements. Returns an interim result, if the TAC or call graph of this method or the - * IFDS analysis for a callee is still pending. - */ - def performAnalysis(entity: (C, IFDSFact)): ProperPropertyComputationResult = { - val (function, sourceFact) = entity - - // Start processing at the start of the icfg with the given source fact - implicit val state: State = new IFDSState[IFDSFact, C, S, Work](entity) - implicit val queue: Worklist = mutable.Queue - .empty[Work] - icfg.startStatements(function).foreach { start ⇒ - state.pathEdges.add(start, sourceFact) // ifds line 2 - queue.enqueue((start, sourceFact, None)) // ifds line 3 + implicit + project: SomeProject, + val ifdsProblem: IFDSProblem[IFDSFact, C, S], + val propertyKey: IFDSPropertyMetaInformation[S, IFDSFact] +) extends ProjectFPCFAnalysis(project) { + type Work = (S, IFDSFact, Option[S]) // statement, fact, predecessor + type Worklist = mutable.Queue[Work] + type State = IFDSState[IFDSFact, C, S, Work] + + implicit var statistics = new Statistics + val icfg = ifdsProblem.icfg + + /** + * Performs an IFDS analysis for a method-fact-pair. + * + * @param entity The method-fact-pair, that will be analyzed. + * @return An IFDS property mapping from exit statements to the facts valid after these exit + * statements. Returns an interim result, if the TAC or call graph of this method or the + * IFDS analysis for a callee is still pending. + */ + def performAnalysis(entity: (C, IFDSFact)): ProperPropertyComputationResult = { + val (function, sourceFact) = entity + + // Start processing at the start of the icfg with the given source fact + implicit val state: State = new IFDSState[IFDSFact, C, S, Work](entity) + implicit val queue: Worklist = mutable.Queue + .empty[Work] + icfg.startStatements(function).foreach { start ⇒ + state.pathEdges.add(start, sourceFact) // ifds line 2 + queue.enqueue((start, sourceFact, None)) // ifds line 3 + } + process() + createResult() } - process() - createResult() - } - - /** - * Creates the current (intermediate) result for the analysis. - * - * @return A result containing a map, which maps each exit statement to the facts valid after - * the statement, based on the current results. If the analysis is still waiting for its - * method's TAC or call graph or the result of another method-fact-pair, an interim - * result will be returned. - * - */ - private def createResult()(implicit state: State): ProperPropertyComputationResult = { - val propertyValue = createPropertyValue(collectResult) - val dependees = state.dependees.forResult - if (dependees.isEmpty) Result(state.source, propertyValue) - else InterimResult.forUB(state.source, propertyValue, dependees, propertyUpdate) - } - - /** - * Creates an IFDSProperty containing the result of this analysis. - * - * @param result Maps each exit statement to the facts, which hold after the exit statement. - * @return An IFDSProperty containing the `result`. - */ - private def createPropertyValue(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] = - propertyKey.create(result) - - /** - * Collects the facts valid at all exit nodes based on the current results. - * - * @return A map, mapping from each exit statement to the facts, which flow into exit statement. - */ - private def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { - var result = Map.empty[S, Set[IFDSFact]] - for {entry <- state.endSummaries } { - result.get(entry._1) match { - case Some(existingFacts) => result = result.updated(entry._1, existingFacts + entry._2) - case None => result = result.updated(entry._1, Set(entry._2)) - } + + /** + * Creates the current (intermediate) result for the analysis. + * + * @return A result containing a map, which maps each exit statement to the facts valid after + * the statement, based on the current results. If the analysis is still waiting for its + * method's TAC or call graph or the result of another method-fact-pair, an interim + * result will be returned. + * + */ + private def createResult()(implicit state: State): ProperPropertyComputationResult = { + val propertyValue = createPropertyValue(collectResult) + val dependees = state.dependees.forResult + if (dependees.isEmpty) Result(state.source, propertyValue) + else InterimResult.forUB(state.source, propertyValue, dependees, propertyUpdate) } - result - } - - /** - * Called, when there is an updated result for a tac, call graph or another method-fact-pair, - * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and - * returns the new analysis result. - * - * @param eps The new property value. - * @return The new (interim) result of this analysis. - */ - private def propertyUpdate( - eps: SomeEPS - )(implicit state: State): ProperPropertyComputationResult = { - implicit val queue: mutable.Queue[Work] = mutable.Queue() - state.dependees.takeWork(eps.toEPK).foreach(queue.enqueue(_)) - process() - createResult() - } - - /** - * Analyzes a queue of BasicBlocks. - * - * @param worklist - */ - private def process()(implicit state: State, worklist: Worklist): Unit = { - while (worklist.nonEmpty) { // ifds line 10 - implicit val work = worklist.dequeue() // ifds line 11 - val (statement, in, predecessor) = work - icfg.getCalleesIfCallStatement(statement) match { - case Some(callees) => handleCall(statement, callees, in) // ifds line 13 - case None => { - if (icfg.isExitStatement(statement)) handleExit(statement, in) // ifds line 21 - // in case of exceptions exit statements may also have some normal flow so no else here - handleOther(statement, in, predecessor) // ifds line 33 + + /** + * Creates an IFDSProperty containing the result of this analysis. + * + * @param result Maps each exit statement to the facts, which hold after the exit statement. + * @return An IFDSProperty containing the `result`. + */ + private def createPropertyValue(result: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] = + propertyKey.create(result) + + /** + * Collects the facts valid at all exit nodes based on the current results. + * + * @return A map, mapping from each exit statement to the facts, which flow into exit statement. + */ + private def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { + var result = Map.empty[S, Set[IFDSFact]] + for { entry ← state.endSummaries } { + result.get(entry._1) match { + case Some(existingFacts) ⇒ result = result.updated(entry._1, existingFacts + entry._2) + case None ⇒ result = result.updated(entry._1, Set(entry._2)) + } } - } + result } - } - - /** - * Processes a statement with a call. - * - * @param call The call statement. - * @param callees All possible callees. - * @param in The facts, which hold before the call statement. - * instead of the facts returned by callToStartFacts. - * @return A map, mapping from each successor statement of the `call` to the facts, which hold - * at their start. - */ - private def handleCall(call: S, - callees: SomeSet[C], - in: IFDSFact, - )( - implicit - state: State, - worklist: Worklist, - work: Work - ): Unit = { - val successors = icfg.nextStatements(call) - for (callee ← callees) { - ifdsProblem.outsideAnalysisContext(callee) match { - case Some(outsideAnalysisHandler) ⇒ - // Let the concrete analysis decide what to do. - for { + + /** + * Called, when there is an updated result for a tac, call graph or another method-fact-pair, + * for which the current analysis is waiting. Re-analyzes the relevant parts of this method and + * returns the new analysis result. + * + * @param eps The new property value. + * @return The new (interim) result of this analysis. + */ + private def propertyUpdate( + eps: SomeEPS + )(implicit state: State): ProperPropertyComputationResult = { + implicit val queue: mutable.Queue[Work] = mutable.Queue() + state.dependees.takeWork(eps.toEPK).foreach(queue.enqueue(_)) + process() + createResult() + } + + /** + * Analyzes a queue of BasicBlocks. + * + * @param worklist + */ + private def process()(implicit state: State, worklist: Worklist): Unit = { + while (worklist.nonEmpty) { // ifds line 10 + implicit val work = worklist.dequeue() // ifds line 11 + val (statement, in, predecessor) = work + icfg.getCalleesIfCallStatement(statement) match { + case Some(callees) ⇒ handleCall(statement, callees, in) // ifds line 13 + case None ⇒ { + if (icfg.isExitStatement(statement)) handleExit(statement, in) // ifds line 21 + // in case of exceptions exit statements may also have some normal flow so no else here + handleOther(statement, in, predecessor) // ifds line 33 + } + } + } + } + + /** + * Processes a statement with a call. + * + * @param call The call statement. + * @param callees All possible callees. + * @param in The facts, which hold before the call statement. + * instead of the facts returned by callToStartFacts. + * @return A map, mapping from each successor statement of the `call` to the facts, which hold + * at their start. + */ + private def handleCall( + call: S, + callees: SomeSet[C], + in: IFDSFact + )( + implicit + state: State, + worklist: Worklist, + work: Work + ): Unit = { + val successors = icfg.nextStatements(call) + for (callee ← callees) { + ifdsProblem.outsideAnalysisContext(callee) match { + case Some(outsideAnalysisHandler) ⇒ + // Let the concrete analysis decide what to do. + for { + successor ← successors + out ← outsideAnalysisHandler(call, successor, in) // ifds line 17 (only summary edges) + } { + propagate(successor, out, call) // ifds line 18 + } + case None ⇒ + for { + successor ← successors + out ← concreteCallFlow(call, callee, in) // ifds line 17 (only summary edges) + } { + propagate(successor, out, call) // ifds line 18 + } + } + } + for { successor ← successors - out <- outsideAnalysisHandler(call, successor, in) // ifds line 17 (only summary edges) - } { - propagate(successor, out, call) // ifds line 18 - } - case None ⇒ - for { - successor <- successors - out <- concreteCallFlow(call, callee, in) // ifds line 17 (only summary edges) - } { + out ← callToReturnFlow(call, in) // ifds line 17 (without summary edge propagation) + } { propagate(successor, out, call) // ifds line 18 - } - } + } } - for { - successor ← successors - out <- callToReturnFlow(call, in) // ifds line 17 (without summary edge propagation) - } { - propagate(successor, out, call) // ifds line 18 + + private def concreteCallFlow(call: S, callee: C, in: IFDSFact)(implicit state: State, work: Work): Set[IFDSFact] = { + var result = Set.empty[IFDSFact] + for (entryFact ← callFlow(call, callee, in)) { // ifds line 14 + val e = (callee, entryFact) + val exitFacts: Map[S, Set[IFDSFact]] = if (e == state.source) { + // handle self dependency on our own because property store can't handle it + state.selfDependees += work + collectResult(state) + } else { + // handle all other dependencies using property store + val callFlows = state.dependees.get(e, propertyKey.key).asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] + callFlows match { + case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ + ep.ub.flows + case _ ⇒ + Map.empty + } + } + for { + (exitStatement, exitStatementFacts) ← exitFacts // ifds line 15.2 + exitStatementFact ← exitStatementFacts // ifds line 15.3 + } { + result ++= returnFlow(exitStatement, exitStatementFact, call, in) + } + } + result + } + + private def handleExit(statement: S, in: IFDSFact)(implicit state: State, worklist: Worklist): Unit = { + val newEdge = (statement, in) + if (!state.endSummaries.contains(newEdge)) { + state.endSummaries += ((statement, in)) // ifds line 21.1 + state.selfDependees.foreach(selfDependee ⇒ + worklist.enqueue(selfDependee)) + } + // ifds lines 22 - 31 are handled by the dependency propagation of the property store + // except for self dependencies which are handled above + } + + private def handleOther(statement: S, in: IFDSFact, predecessor: Option[S])(implicit state: State, worklist: Worklist): Unit = { + for { // ifds line 34 + successor ← icfg.nextStatements(statement) + out ← normalFlow(statement, in, predecessor) + } { + propagate(successor, out, statement) // ifds line 35 + } } - } - - private def concreteCallFlow(call: S, callee: C, in: IFDSFact)(implicit state: State, work: Work): Set[IFDSFact] = { - var result = Set.empty[IFDSFact] - for (entryFact ← callFlow(call, callee, in)) { // ifds line 14 - val e = (callee, entryFact) - val exitFacts: Map[S, Set[IFDSFact]] = if(e == state.source) { - // handle self dependency on our own because property store can't handle it - state.selfDependees += work - collectResult(state) - } else { - // handle all other dependencies using property store - val callFlows = state.dependees.get(e, propertyKey.key).asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] - callFlows match { - case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ - ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ - ep.ub.flows - case _ ⇒ - Map.empty + + private def propagate(successor: S, out: IFDSFact, predecessor: S)(implicit state: State, worklist: Worklist): Unit = { + val predecessorOption = if (ifdsProblem.needsPredecessor(successor)) Some(predecessor) else None + + // ifds line 9 + if (state.pathEdges.add(successor, out, predecessorOption)) { + worklist.enqueue((successor, out, predecessorOption)) } - } - for { - (exitStatement, exitStatementFacts) <- exitFacts // ifds line 15.2 - exitStatementFact <- exitStatementFacts // ifds line 15.3 - } { - result ++= returnFlow(exitStatement, exitStatementFact, call, in) - } } - result - } - - private def handleExit(statement: S, in: IFDSFact)(implicit state: State, worklist: Worklist): Unit = { - val newEdge = (statement, in) - if (!state.endSummaries.contains(newEdge)) { - state.endSummaries += ((statement, in)) // ifds line 21.1 - state.selfDependees.foreach(selfDependee => - worklist.enqueue(selfDependee)) + + /** + * ifds flow function + * @param statement n + * @param in d2 + * @param predecessor pi + */ + private def normalFlow(statement: S, in: IFDSFact, predecessor: Option[S]): Set[IFDSFact] = { + statistics.normalFlow += 1 + addNullFactIfConfigured(in, ifdsProblem.normalFlow(statement, in, predecessor)) } - // ifds lines 22 - 31 are handled by the dependency propagation of the property store - // except for self dependencies which are handled above - } - - private def handleOther(statement: S, in: IFDSFact, predecessor: Option[S])(implicit state: State, worklist: Worklist): Unit = { - for { // ifds line 34 - successor <- icfg.nextStatements(statement) - out <- normalFlow(statement, in, predecessor) - } { - propagate(successor, out, statement) // ifds line 35 + + /** + * ifds passArgs function + * @param call n + * @param callee + * @param in d2 + * @return + */ + private def callFlow(call: S, callee: C, in: IFDSFact): Set[IFDSFact] = { + statistics.callFlow += 1 + addNullFactIfConfigured(in, ifdsProblem.callFlow(call, callee, in)) } - } - private def propagate(successor: S, out: IFDSFact, predecessor: S)(implicit state: State, worklist: Worklist): Unit = { - val predecessorOption = if (ifdsProblem.needsPredecessor(successor)) Some(predecessor) else None + /** + * ifds returnVal function + * @param exit n + * @param in d2 + * @param call c + * @param callFact d4 + * @return + */ + private def returnFlow(exit: S, in: IFDSFact, call: S, callFact: IFDSFact): Set[IFDSFact] = { + statistics.returnFlow += 1 + addNullFactIfConfigured(in, ifdsProblem.returnFlow(exit, in, call, callFact)) + } - // ifds line 9 - if (state.pathEdges.add(successor, out, predecessorOption)) { - worklist.enqueue((successor, out, predecessorOption)) + /** + * ifds callFlow function + * @param call n + * @param in d2 + * @return + */ + private def callToReturnFlow(call: S, in: IFDSFact): Set[IFDSFact] = { + statistics.callToReturnFlow += 1 + addNullFactIfConfigured(in, ifdsProblem.callToReturnFlow(call, in)) + } + + private def addNullFactIfConfigured(in: IFDSFact, out: Set[IFDSFact]): Set[IFDSFact] = { + if (ifdsProblem.automaticallyPropagateNullFactInFlowFunctions && in == ifdsProblem.nullFact) + out + ifdsProblem.nullFact + else out } - } - - /** - * ifds flow function - * @param statement n - * @param in d2 - * @param predecessor pi - */ - private def normalFlow(statement: S, in: IFDSFact, predecessor: Option[S]): Set[IFDSFact] = { - statistics.normalFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.normalFlow(statement, in, predecessor)) - } - - /** - * ifds passArgs function - * @param call n - * @param callee - * @param in d2 - * @return - */ - private def callFlow(call: S, callee: C, in: IFDSFact): Set[IFDSFact] = { - statistics.callFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.callFlow(call, callee, in)) - } - - /** - * ifds returnVal function - * @param exit n - * @param in d2 - * @param call c - * @param callFact d4 - * @return - */ - private def returnFlow(exit: S, in: IFDSFact, call: S, callFact: IFDSFact): Set[IFDSFact] = { - statistics.returnFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.returnFlow(exit, in, call, callFact)) - } - - /** - * ifds callFlow function - * @param call n - * @param in d2 - * @return - */ - private def callToReturnFlow(call: S, in: IFDSFact): Set[IFDSFact] = { - statistics.callToReturnFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.callToReturnFlow(call, in)) - } - - private def addNullFactIfConfigured(in: IFDSFact, out: Set[IFDSFact]): Set[IFDSFact] = { - if (ifdsProblem.automaticallyPropagateNullFactInFlowFunctions && in == ifdsProblem.nullFact) - out + ifdsProblem.nullFact - else out - } } abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]] - extends FPCFLazyAnalysisScheduler { - final override type InitializationData = IFDSAnalysis[IFDSFact, C, S] - def property: IFDSPropertyMetaInformation[S, IFDSFact] - final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) - override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} - - /** - * Registers the analysis as a lazy computation, that is, the method - * will call `ProperytStore.scheduleLazyComputation`. - */ - override def register( - p: SomeProject, - ps: PropertyStore, - analysis: IFDSAnalysis[IFDSFact, C, S] - ): FPCFAnalysis = { - ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) - analysis - } - - override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { - val ifdsAnalysis = analysis.asInstanceOf[IFDSAnalysis[IFDSFact, C, S]] - for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { - ps.force(e, ifdsAnalysis.propertyKey.key) + extends FPCFLazyAnalysisScheduler { + final override type InitializationData = IFDSAnalysis[IFDSFact, C, S] + def property: IFDSPropertyMetaInformation[S, IFDSFact] + final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) + override def beforeSchedule(p: SomeProject, ps: PropertyStore): Unit = {} + + /** + * Registers the analysis as a lazy computation, that is, the method + * will call `ProperytStore.scheduleLazyComputation`. + */ + override def register( + p: SomeProject, + ps: PropertyStore, + analysis: IFDSAnalysis[IFDSFact, C, S] + ): FPCFAnalysis = { + ps.registerLazyPropertyComputation(property.key, analysis.performAnalysis) + analysis + } + + override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { + val ifdsAnalysis = analysis.asInstanceOf[IFDSAnalysis[IFDSFact, C, S]] + for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { + ps.force(e, ifdsAnalysis.propertyKey.key) + } } - } - override def afterPhaseCompletion( - p: SomeProject, - ps: PropertyStore, - analysis: FPCFAnalysis - ): Unit = {} + override def afterPhaseCompletion( + p: SomeProject, + ps: PropertyStore, + analysis: FPCFAnalysis + ): Unit = {} } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala index c97508ed00..8dd8e9966f 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -12,104 +12,104 @@ import org.opalj.fpcf.ProperPropertyComputationResult * @author Marc Clement */ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]](val icfg: ICFG[IFDSFact, C, S]) { - /** - * The null fact of this analysis. - */ - def nullFact: IFDSFact + /** + * The null fact of this analysis. + */ + def nullFact: IFDSFact - /** - * @return Whether the null Fact is automatically added to the result of every flow function where it is passed into - */ - def automaticallyPropagateNullFactInFlowFunctions: Boolean = true + /** + * @return Whether the null Fact is automatically added to the result of every flow function where it is passed into + */ + def automaticallyPropagateNullFactInFlowFunctions: Boolean = true - /** - * The entry points of this analysis. - */ - def entryPoints: Seq[(C, IFDSFact)] + /** + * The entry points of this analysis. + */ + def entryPoints: Seq[(C, IFDSFact)] - /** - * Computes the data flow for a normal statement. - * - * @param statement The analyzed statement. - * @param in The fact which holds before the execution of the `statement`. - * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be - * computed. Used for phi statements to distinguish the flow. - * @return The facts, which hold after the execution of `statement` under the assumption - * that the facts in `in` held before `statement` and `successor` will be - * executed next. - */ - def normalFlow( - statement: S, - in: IFDSFact, - predecessor: Option[S], - ): Set[IFDSFact] + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param in The fact which holds before the execution of the `statement`. + * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be + * computed. Used for phi statements to distinguish the flow. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + def normalFlow( + statement: S, + in: IFDSFact, + predecessor: Option[S] + ): Set[IFDSFact] - /** - * Computes the data flow for a call to start edge. - * - * @param call The analyzed call statement. - * @param callee The called method, for which the data flow shall be computed. - * @param in The fact which holds before the execution of the `call`. - * @param source The entity, which is analyzed. - * @return The facts, which hold after the execution of `statement` under the assumption that - * the facts in `in` held before `statement` and `statement` calls `callee`. - */ - def callFlow( - call: S, - callee: C, - in: IFDSFact - ): Set[IFDSFact] + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + def callFlow( + call: S, + callee: C, + in: IFDSFact + ): Set[IFDSFact] - /** - * Computes the data flow for an exit to return edge. - * - * @param call The statement, which called the `callee`. - * @param exit The statement, which terminated the `callee`. - * @param in The fact which holds before the execution of the `exit`. - * @return The facts, which hold after the execution of `exit` in the caller's context - * under the assumption that `in` held before the execution of `exit` and that - * `successor` will be executed next. - */ - def returnFlow( - exit: S, - in: IFDSFact, - call: S, - callFact: IFDSFact - ): Set[IFDSFact] + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + def returnFlow( + exit: S, + in: IFDSFact, + call: S, + callFact: IFDSFact + ): Set[IFDSFact] - /** - * Computes the data flow for a call to return edge. - * - * @param call The statement, which invoked the call. - * @param in The facts, which hold before the `call`. - * @return The facts, which hold after the call independently of what happens in the callee - * under the assumption that `in` held before `call`. - */ - def callToReturnFlow( - call: S, - in: IFDSFact - ): Set[IFDSFact] + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param in The facts, which hold before the `call`. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + def callToReturnFlow( + call: S, + in: IFDSFact + ): Set[IFDSFact] - def needsPredecessor(statement: S): Boolean + def needsPredecessor(statement: S): Boolean - type OutsideAnalysisContextHandler = ((S, S, IFDSFact) ⇒ Set[IFDSFact]) { - def apply(call: S, successor: S, in: IFDSFact): Set[IFDSFact] - } + type OutsideAnalysisContextHandler = ((S, S, IFDSFact) ⇒ Set[IFDSFact]) { + def apply(call: S, successor: S, in: IFDSFact): Set[IFDSFact] + } - /** - * Checks, if a callee is outside this analysis' context. - * By default, native methods are not inside the analysis context. - * For callees outside this analysis' context the returned handler is called - * to compute the summary edge for the call instead of analyzing the callee. - * - * @param callee The method called by `call`. - * @return The handler function. It receives - * the statement which invoked the call, - * the successor statement, which will be executed after the call and - * the set of input facts which hold before the `call`. - * It returns facts, which hold after the call, excluding the call to return flow. - */ - def outsideAnalysisContext(callee: C): Option[OutsideAnalysisContextHandler] + /** + * Checks, if a callee is outside this analysis' context. + * By default, native methods are not inside the analysis context. + * For callees outside this analysis' context the returned handler is called + * to compute the summary edge for the call instead of analyzing the callee. + * + * @param callee The method called by `call`. + * @return The handler function. It receives + * the statement which invoked the call, + * the successor statement, which will be executed after the call and + * the set of input facts which hold before the `call`. + * It returns facts, which hold after the call, excluding the call to return flow. + */ + def outsideAnalysisContext(callee: C): Option[OutsideAnalysisContextHandler] - def specialCase(source: (C, IFDSFact), propertyKey: IFDSPropertyMetaInformation[S, IFDSFact]): Option[ProperPropertyComputationResult] = None + def specialCase(source: (C, IFDSFact), propertyKey: IFDSPropertyMetaInformation[S, IFDSFact]): Option[ProperPropertyComputationResult] = None } \ No newline at end of file diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala index c7d5fb5d97..2f39fc55a8 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala @@ -3,7 +3,7 @@ package org.opalj.ifds.old import org.opalj.ifds.{AbstractIFDSFact, Statement} -import scala.collection.{Set => SomeSet} +import scala.collection.{Set ⇒ SomeSet} abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_, Node], Node] { /** diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala index 3b457ce3b5..99a3dcdb5b 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala @@ -6,7 +6,7 @@ import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} import org.opalj.fpcf._ import org.opalj.ifds.{AbstractIFDSFact, IFDS, IFDSProperty, IFDSPropertyMetaInformation, Statement} -import scala.collection.{mutable, Set => SomeSet} +import scala.collection.{mutable, Set ⇒ SomeSet} /** * The state of the analysis. For each method and source fact, there is a separate state. diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala index 35d7372ff1..02c74965f1 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala @@ -11,73 +11,73 @@ import org.opalj.ifds.AbstractIFDSFact */ trait Subsumable[S, IFDSFact <: AbstractIFDSFact] { - /** - * A subclass can override this method to filter the `facts` in some set, which are not subsumed - * by another fact in the set. - * - * - * @param facts The set of facts. - * @param project The project, which is analyzed. - * @return The facts, which are not subsumed by any other fact in `facts`. - * By default, `facts` is returned without removing anything. - */ - protected def subsume[T <: IFDSFact](facts: Set[T], project: SomeProject): Set[T] = facts + /** + * A subclass can override this method to filter the `facts` in some set, which are not subsumed + * by another fact in the set. + * + * + * @param facts The set of facts. + * @param project The project, which is analyzed. + * @return The facts, which are not subsumed by any other fact in `facts`. + * By default, `facts` is returned without removing anything. + */ + protected def subsume[T <: IFDSFact](facts: Set[T], project: SomeProject): Set[T] = facts - /** - * Checks, if any fact from some set is not equal to or subsumed by any fact in another set. - * A subclass implementing subsuming must overwrite this method to consider subsuming. - * - * @param newFacts The facts, which were found. Not empty. - * @param oldFacts The facts, which are already known. Not empty. - * @param project The project, which is analyzed. - * @return True, if any fact in `newFacts`, is not equal to or subsumed by any fact in - * `oldFacts`. - */ - protected def containsNewInformation[T <: IFDSFact]( - newFacts: Set[T], - oldFacts: Set[T], - project: SomeProject - ): Boolean = - newFacts.exists(newFact => !oldFacts.contains(newFact)) + /** + * Checks, if any fact from some set is not equal to or subsumed by any fact in another set. + * A subclass implementing subsuming must overwrite this method to consider subsuming. + * + * @param newFacts The facts, which were found. Not empty. + * @param oldFacts The facts, which are already known. Not empty. + * @param project The project, which is analyzed. + * @return True, if any fact in `newFacts`, is not equal to or subsumed by any fact in + * `oldFacts`. + */ + protected def containsNewInformation[T <: IFDSFact]( + newFacts: Set[T], + oldFacts: Set[T], + project: SomeProject + ): Boolean = + newFacts.exists(newFact ⇒ !oldFacts.contains(newFact)) - /** - * Filters the new information from a new set of exit facts given the already known exit facts. - * A subclass implementing subsuming must overwrite this method to consider subsuming. - * - * @param newExitFacts The new exit facts. - * @param oldExitFacts The old exit facts. - * @param project The project, which is analyzed. - * @return A map, containing the keys of `newExitFacts`. - * Facts, which are equal to or subsumed by any fact for the same statement in - * `oldExitFacts` are not present. - */ - protected def filterNewInformation[T <: IFDSFact]( - newExitFacts: Map[S, Set[T]], - oldExitFacts: Map[S, Set[T]], - project: SomeProject - ): Map[S, Set[T]] = { - var result = newExitFacts - for ((key, values) <- oldExitFacts) { - result = result.updated(key, result(key) -- values) + /** + * Filters the new information from a new set of exit facts given the already known exit facts. + * A subclass implementing subsuming must overwrite this method to consider subsuming. + * + * @param newExitFacts The new exit facts. + * @param oldExitFacts The old exit facts. + * @param project The project, which is analyzed. + * @return A map, containing the keys of `newExitFacts`. + * Facts, which are equal to or subsumed by any fact for the same statement in + * `oldExitFacts` are not present. + */ + protected def filterNewInformation[T <: IFDSFact]( + newExitFacts: Map[S, Set[T]], + oldExitFacts: Map[S, Set[T]], + project: SomeProject + ): Map[S, Set[T]] = { + var result = newExitFacts + for ((key, values) ← oldExitFacts) { + result = result.updated(key, result(key) -- values) + } + result } - result - } - /** - * Filters the `facts` from some set, which are not equal to or subsumed by any fact in an - * `other` set. - * A subclass implementing subsuming must overwrite this method to consider subsuming. - * - * @param facts The set, from which facts are filtered. - * @param otherFacts The set, which may subsume facts from `facts`. - * @param project The project, which is analyzed. - * @return The facts from `facts`, which are not equal to or subsumed by any fact in an - * `otherFacts`. - */ - protected def notSubsumedBy[T <: IFDSFact]( - facts: Set[T], - otherFacts: Set[T], - project: SomeProject - ): Set[T] = - facts -- otherFacts + /** + * Filters the `facts` from some set, which are not equal to or subsumed by any fact in an + * `other` set. + * A subclass implementing subsuming must overwrite this method to consider subsuming. + * + * @param facts The set, from which facts are filtered. + * @param otherFacts The set, which may subsume facts from `facts`. + * @param project The project, which is analyzed. + * @return The facts from `facts`, which are not equal to or subsumed by any fact in an + * `otherFacts`. + */ + protected def notSubsumedBy[T <: IFDSFact]( + facts: Set[T], + otherFacts: Set[T], + project: SomeProject + ): Set[T] = + facts -- otherFacts } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 5cc786fd19..a5ba027553 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -6,58 +6,58 @@ import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeIFDSProblem} import org.opalj.ll.llvm.{Function, PHI} import org.opalj.tac.fpcf.analyses.ifds.taint.NewTaintProblem - abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with NewTaintProblem[Function, LLVMStatement, NativeFact] { +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with NewTaintProblem[Function, LLVMStatement, NativeFact] { override def nullFact: NativeFact = NativeNullFact - /** - * Computes the data flow for a normal statement. - * - * @param statement The analyzed statement. - * @param in The fact which holds before the execution of the `statement`. - * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be - * computed. Used for phi statements to distinguish the flow. - * @return The facts, which hold after the execution of `statement` under the assumption - * that the facts in `in` held before `statement` and `successor` will be - * executed next. - */ - override def normalFlow(statement: LLVMStatement, in: NativeFact, predecessor: Option[LLVMStatement]): Set[NativeFact] = ??? + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param in The fact which holds before the execution of the `statement`. + * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be + * computed. Used for phi statements to distinguish the flow. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + override def normalFlow(statement: LLVMStatement, in: NativeFact, predecessor: Option[LLVMStatement]): Set[NativeFact] = ??? - /** - * Computes the data flow for a call to start edge. - * - * @param call The analyzed call statement. - * @param callee The called method, for which the data flow shall be computed. - * @param in The fact which holds before the execution of the `call`. - * @param source The entity, which is analyzed. - * @return The facts, which hold after the execution of `statement` under the assumption that - * the facts in `in` held before `statement` and `statement` calls `callee`. - */ - override def callFlow(call: LLVMStatement, callee: Function, in: NativeFact): Set[NativeFact] = ??? + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + override def callFlow(call: LLVMStatement, callee: Function, in: NativeFact): Set[NativeFact] = ??? - /** - * Computes the data flow for an exit to return edge. - * - * @param call The statement, which called the `callee`. - * @param exit The statement, which terminated the `callee`. - * @param in The fact which holds before the execution of the `exit`. - * @return The facts, which hold after the execution of `exit` in the caller's context - * under the assumption that `in` held before the execution of `exit` and that - * `successor` will be executed next. - */ - override def returnFlow(exit: LLVMStatement, in: NativeFact, call: LLVMStatement, callFact: NativeFact): Set[NativeFact] = ??? + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + override def returnFlow(exit: LLVMStatement, in: NativeFact, call: LLVMStatement, callFact: NativeFact): Set[NativeFact] = ??? - /** - * Computes the data flow for a call to return edge. - * - * @param call The statement, which invoked the call. - * @param in The facts, which hold before the `call`. - * @return The facts, which hold after the call independently of what happens in the callee - * under the assumption that `in` held before `call`. - */ - override def callToReturnFlow(call: LLVMStatement, in: NativeFact): Set[NativeFact] = ??? + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param in The facts, which hold before the `call`. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + override def callToReturnFlow(call: LLVMStatement, in: NativeFact): Set[NativeFact] = ??? - override def needsPredecessor(statement: LLVMStatement): Boolean = statement.instruction match { - case PHI(_) => true - case _ => false - } - } \ No newline at end of file + override def needsPredecessor(statement: LLVMStatement): Boolean = statement.instruction match { + case PHI(_) ⇒ true + case _ ⇒ false + } +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/SerializationRelatedCallsAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/SerializationRelatedCallsAnalysis.scala index 789dfe5fe5..8d1e9f313a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/SerializationRelatedCallsAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/cg/SerializationRelatedCallsAnalysis.scala @@ -83,7 +83,7 @@ class OOSWriteObjectAnalysis private[analyses] ( val parameters = Seq(receiverOption.flatMap(os ⇒ persistentUVar(os.asVar))) implicit val state: CGState[ContextType] = new CGState[ContextType]( - callerContext, FinalEP(callerContext.method.definedMethod, TheTACAI(tac)), + callerContext, FinalEP(callerContext.method.definedMethod, TheTACAI(tac)) ) handleOOSWriteObject(callerContext, param, pc, receiver, parameters, indirectCalls) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala index c1794bbb49..6fc4ba891d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala @@ -28,7 +28,7 @@ import org.opalj.value.ValueInformation import java.io.{File, PrintWriter} import javax.swing.JOptionPane -import scala.collection.{mutable, Set => SomeSet} +import scala.collection.{mutable, Set ⇒ SomeSet} /** * @@ -867,12 +867,12 @@ object AbstractIFDSAnalysis { * @param cfg The method's CFG. */ case class JavaStatement( - method: Method, - node: CFGNode, - stmt: Stmt[V], - index: Int, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]], + method: Method, + node: CFGNode, + stmt: Stmt[V], + index: Int, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]], declaredMethod: DeclaredMethod ) extends Statement[DeclaredMethod, CFGNode] { @@ -885,6 +885,8 @@ case class JavaStatement( override def toString: String = s"${method.toJava}" override def callable(): DeclaredMethod = declaredMethod + + def asNewJavaStatement: NewJavaStatement = NewJavaStatement(method, index, code, cfg) } abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala index ce851b9bd4..fd5b85a16b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala @@ -13,106 +13,106 @@ import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.value.ValueInformation class NewForwardICFG[IFDSFact <: AbstractIFDSFact](implicit project: SomeProject) - extends ICFG[IFDSFact, Method, NewJavaStatement] { - val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) - val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) - implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) + extends ICFG[IFDSFact, Method, NewJavaStatement] { + val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) + val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) - /** - * Determines the statements at which the analysis starts. - * - * @param callable The analyzed callable. - * @return The statements at which the analysis starts. - */ - override def startStatements(callable: Method): Set[NewJavaStatement] = { - val TACode(_, code, _, cfg, _) = tacai(callable) - Set(NewJavaStatement(callable, 0, code, cfg)) - } + /** + * Determines the statements at which the analysis starts. + * + * @param callable The analyzed callable. + * @return The statements at which the analysis starts. + */ + override def startStatements(callable: Method): Set[NewJavaStatement] = { + val TACode(_, code, _, cfg, _) = tacai(callable) + Set(NewJavaStatement(callable, 0, code, cfg)) + } - /** - * Determines the statement, that will be analyzed after some other `statement`. - * - * @param statement The source statement. - * @return The successor statements - */ - override def nextStatements(statement: NewJavaStatement): Set[NewJavaStatement] = { - statement.cfg - .successors(statement.index) - .toChain - .map { index ⇒ NewJavaStatement(statement, index) } - .toSet - } + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + override def nextStatements(statement: NewJavaStatement): Set[NewJavaStatement] = { + statement.cfg + .successors(statement.index) + .toChain + .map { index ⇒ NewJavaStatement(statement, index) } + .toSet + } - /** - * Gets the set of all methods possibly called at some statement. - * - * @param statement The statement. - * @return All callables possibly called at the statement or None, if the statement does not - * contain a call. - */ - override def getCalleesIfCallStatement(statement: NewJavaStatement): Option[collection.Set[Method]] = - statement.stmt.astID match { - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ - Some(getCallees(statement)) - case Assignment.ASTID | ExprStmt.ASTID ⇒ - getExpression(statement.stmt).astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - Some(getCallees(statement)) - case _ ⇒ None + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + override def getCalleesIfCallStatement(statement: NewJavaStatement): Option[collection.Set[Method]] = + statement.stmt.astID match { + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + Some(getCallees(statement)) + case Assignment.ASTID | ExprStmt.ASTID ⇒ + getExpression(statement.stmt).astID match { + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + Some(getCallees(statement)) + case _ ⇒ None + } + case _ ⇒ None } - case _ ⇒ None - } - /** - * Retrieves the expression of an assignment or expression statement. - * - * @param statement The statement. Must be an Assignment or ExprStmt. - * @return The statement's expression. - */ - private def getExpression(statement: Stmt[_]): Expr[_] = statement.astID match { - case Assignment.ASTID ⇒ statement.asAssignment.expr - case ExprStmt.ASTID ⇒ statement.asExprStmt.expr - case _ ⇒ throw new UnknownError("Unexpected statement") - } + /** + * Retrieves the expression of an assignment or expression statement. + * + * @param statement The statement. Must be an Assignment or ExprStmt. + * @return The statement's expression. + */ + private def getExpression(statement: Stmt[_]): Expr[_] = statement.astID match { + case Assignment.ASTID ⇒ statement.asAssignment.expr + case ExprStmt.ASTID ⇒ statement.asExprStmt.expr + case _ ⇒ throw new UnknownError("Unexpected statement") + } - private def getCallees(statement: NewJavaStatement): collection.Set[Method] = { - val pc = statement.code(statement.index).pc - val caller = declaredMethods(statement.callable) - val ep = propertyStore(caller, Callees.key) - ep match { - case FinalEP(_, p) ⇒ definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method)) - case _ ⇒ - throw new IllegalStateException( - "call graph must be computed before the analysis starts" - ) + private def getCallees(statement: NewJavaStatement): collection.Set[Method] = { + val pc = statement.code(statement.index).pc + val caller = declaredMethods(statement.callable) + val ep = propertyStore(caller, Callees.key) + ep match { + case FinalEP(_, p) ⇒ definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method)) + case _ ⇒ + throw new IllegalStateException( + "call graph must be computed before the analysis starts" + ) + } } - } - override def isExitStatement(statement: NewJavaStatement): Boolean = { - statement.index == statement.node.asBasicBlock.endPC && - statement.node.successors.exists(_.isExitNode) - } + override def isExitStatement(statement: NewJavaStatement): Boolean = { + statement.index == statement.node.asBasicBlock.endPC && + statement.node.successors.exists(_.isExitNode) + } - /** - * Maps some declared methods to their defined methods. - * - * @param declaredMethods Some declared methods. - * @return All defined methods of `declaredMethods`. - */ - private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): collection.Set[Method] = { - val result = scala.collection.mutable.Set.empty[Method] - declaredMethods - .filter( - declaredMethod ⇒ - declaredMethod.hasSingleDefinedMethod || - declaredMethod.hasMultipleDefinedMethods - ) - .foreach( - declaredMethod ⇒ - declaredMethod - .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) - ) - result - } + /** + * Maps some declared methods to their defined methods. + * + * @param declaredMethods Some declared methods. + * @return All defined methods of `declaredMethods`. + */ + private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): collection.Set[Method] = { + val result = scala.collection.mutable.Set.empty[Method] + declaredMethods + .filter( + declaredMethod ⇒ + declaredMethod.hasSingleDefinedMethod || + declaredMethod.hasMultipleDefinedMethods + ) + .foreach( + declaredMethod ⇒ + declaredMethod + .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) + ) + result + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala index a1cf666a46..ce49168071 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala @@ -19,47 +19,47 @@ import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt, TACStmts} * @param cfg The method's CFG. */ case class NewJavaStatement( - method: Method, - index: Int, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] - ) extends Statement[Method, CFGNode] { + method: Method, + index: Int, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] +) extends Statement[Method, CFGNode] { - override def hashCode(): Int = method.hashCode() * 31 + index + override def hashCode(): Int = method.hashCode() * 31 + index - override def equals(o: Any): Boolean = o match { - case s: NewJavaStatement ⇒ s.index == index && s.method == method - case _ ⇒ false - } + override def equals(o: Any): Boolean = o match { + case s: NewJavaStatement ⇒ s.index == index && s.method == method + case _ ⇒ false + } - override def toString: String = s"${method.signatureToJava(false)}[${index}]\n\t${stmt}\n\t${method.toJava}" - override def callable(): Method = method - override def node(): CFGNode = cfg.bb(index) - def stmt: Stmt[V] = code(index) + override def toString: String = s"${method.signatureToJava(false)}[${index}]\n\t${stmt}\n\t${method.toJava}" + override def callable(): Method = method + override def node(): CFGNode = cfg.bb(index) + def stmt: Stmt[V] = code(index) } object NewJavaStatement { - def apply(oldStatement: NewJavaStatement, newIndex: Int): NewJavaStatement = - NewJavaStatement(oldStatement.method, newIndex, oldStatement.code, oldStatement.cfg) + def apply(oldStatement: NewJavaStatement, newIndex: Int): NewJavaStatement = + NewJavaStatement(oldStatement.method, newIndex, oldStatement.code, oldStatement.cfg) } abstract class NewJavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) - extends IFDSProblem[Fact, Method, NewJavaStatement](new NewForwardICFG[Fact]()(project)) { - /** - * Gets the call object for a statement that contains a call. - * - * @param call The call statement. - * @return The call object for `call`. - */ - protected def asCall(call: Stmt[V]): Call[V] = call.astID match { - case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall - case _ ⇒ call.asMethodCall - } + extends IFDSProblem[Fact, Method, NewJavaStatement](new NewForwardICFG[Fact]()(project)) { + /** + * Gets the call object for a statement that contains a call. + * + * @param call The call statement. + * @return The call object for `call`. + */ + protected def asCall(call: Stmt[V]): Call[V] = call.astID match { + case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall + case _ ⇒ call.asMethodCall + } - override def outsideAnalysisContext(callee: Method): Option[(NewJavaStatement, NewJavaStatement, Fact) ⇒ Set[Fact]] = callee.body.isDefined match { - case true ⇒ None - case false ⇒ Some((_call: NewJavaStatement, _successor: NewJavaStatement, in: Fact) ⇒ Set(in)) - } + override def outsideAnalysisContext(callee: Method): Option[(NewJavaStatement, NewJavaStatement, Fact) ⇒ Set[Fact]] = callee.body.isDefined match { + case true ⇒ None + case false ⇒ Some((_call: NewJavaStatement, _successor: NewJavaStatement, in: Fact) ⇒ Set(in)) + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala index 04f5243396..fff867665d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala @@ -8,339 +8,339 @@ import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, NewJa import org.opalj.tac._ abstract class NewForwardTaintProblem(project: SomeProject) - extends NewJavaIFDSProblem[Fact](project) + extends NewJavaIFDSProblem[Fact](project) with NewTaintProblem[Method, NewJavaStatement, Fact] { - val declaredMethods = project.get(DeclaredMethodsKey) - override def nullFact: Fact = NullFact + val declaredMethods = project.get(DeclaredMethodsKey) + override def nullFact: Fact = NullFact - override def needsPredecessor(statement: NewJavaStatement): Boolean = false + override def needsPredecessor(statement: NewJavaStatement): Boolean = false - /** - * If a variable gets assigned a tainted value, the variable will be tainted. - */ - override def normalFlow(statement: NewJavaStatement, in: Fact, predecessor: Option[NewJavaStatement]): Set[Fact] = { - val in_set = Set(in) // dirty hack for old code - statement.stmt.astID match { - case Assignment.ASTID ⇒ - in_set ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) - case ArrayStore.ASTID ⇒ - val store = statement.stmt.asArrayStore - val definedBy = store.arrayRef.asVar.definedBy - val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) - if (isTainted(store.value, in)) { - if (arrayIndex.isDefined) - // Taint a known array index - definedBy.foldLeft(in_set) { (c, n) ⇒ - c + ArrayElement(n, arrayIndex.get) - } - else - // Taint the whole array if the index is unknown - definedBy.foldLeft(in_set) { (c, n) ⇒ - c + Variable(n) - } - } else if (arrayIndex.isDefined && definedBy.size == 1) - // Untaint if possible - in_set - ArrayElement(definedBy.head, arrayIndex.get) - else in_set - case PutField.ASTID ⇒ - val put = statement.stmt.asPutField - val definedBy = put.objRef.asVar.definedBy - if (isTainted(put.value, in)) - definedBy.foldLeft(in_set) { (in, defSite) ⇒ - in + InstanceField(defSite, put.declaringClass, put.name) - } - else - in_set - case PutStatic.ASTID ⇒ - val put = statement.stmt.asPutStatic - if (isTainted(put.value, in)) - in_set + StaticField(put.declaringClass, put.name) - else - in_set - case _ ⇒ in_set + /** + * If a variable gets assigned a tainted value, the variable will be tainted. + */ + override def normalFlow(statement: NewJavaStatement, in: Fact, predecessor: Option[NewJavaStatement]): Set[Fact] = { + val in_set = Set(in) // dirty hack for old code + statement.stmt.astID match { + case Assignment.ASTID ⇒ + in_set ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) + case ArrayStore.ASTID ⇒ + val store = statement.stmt.asArrayStore + val definedBy = store.arrayRef.asVar.definedBy + val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) + if (isTainted(store.value, in)) { + if (arrayIndex.isDefined) + // Taint a known array index + definedBy.foldLeft(in_set) { (c, n) ⇒ + c + ArrayElement(n, arrayIndex.get) + } + else + // Taint the whole array if the index is unknown + definedBy.foldLeft(in_set) { (c, n) ⇒ + c + Variable(n) + } + } else if (arrayIndex.isDefined && definedBy.size == 1) + // Untaint if possible + in_set - ArrayElement(definedBy.head, arrayIndex.get) + else in_set + case PutField.ASTID ⇒ + val put = statement.stmt.asPutField + val definedBy = put.objRef.asVar.definedBy + if (isTainted(put.value, in)) + definedBy.foldLeft(in_set) { (in, defSite) ⇒ + in + InstanceField(defSite, put.declaringClass, put.name) + } + else + in_set + case PutStatic.ASTID ⇒ + val put = statement.stmt.asPutStatic + if (isTainted(put.value, in)) + in_set + StaticField(put.declaringClass, put.name) + else + in_set + case _ ⇒ in_set + } } - } - /** - * Propagates tainted parameters to the callee. If a call to the sink method with a tainted - * parameter is detected, no call-to-start - * edges will be created. - */ - override def callFlow(call: NewJavaStatement, callee: Method, in: Fact): Set[Fact] = { - val in_set = Set(in) // compatilibity for old code - val callObject = asCall(call.stmt) - val allParams = callObject.allParams - var facts = Set.empty[Fact] + /** + * Propagates tainted parameters to the callee. If a call to the sink method with a tainted + * parameter is detected, no call-to-start + * edges will be created. + */ + override def callFlow(call: NewJavaStatement, callee: Method, in: Fact): Set[Fact] = { + val in_set = Set(in) // compatilibity for old code + val callObject = asCall(call.stmt) + val allParams = callObject.allParams + var facts = Set.empty[Fact] - if (relevantCallee(callee)) { - val allParamsWithIndices = allParams.zipWithIndex - in_set.foreach { - // Taint formal parameter if actual parameter is tainted - case Variable(index) ⇒ - allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( - paramIndex, - callee.isStatic - )) - case _ ⇒ // Nothing to do - } + if (relevantCallee(callee)) { + val allParamsWithIndices = allParams.zipWithIndex + in_set.foreach { + // Taint formal parameter if actual parameter is tainted + case Variable(index) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( + paramIndex, + callee.isStatic + )) + case _ ⇒ // Nothing to do + } - // Taint element of formal parameter if element of actual parameter is tainted - case ArrayElement(index, taintedIndex) ⇒ - allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.isStatic), - taintedIndex - ) - case _ ⇒ // Nothing to do - } + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) ⇒ + allParamsWithIndices.foreach { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + facts += ArrayElement( + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.isStatic), + taintedIndex + ) + case _ ⇒ // Nothing to do + } - case InstanceField(index, declClass, taintedField) ⇒ - // Taint field of formal parameter if field of actual parameter is tainted - // Only if the formal parameter is of a type that may have that field! - allParamsWithIndices.foreach { - case (param, pIndex) if param.asVar.definedBy.contains(index) && - (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic) != -1 || - project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) ⇒ - facts += InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic), - declClass, taintedField - ) - case _ ⇒ // Nothing to do - } + case InstanceField(index, declClass, taintedField) ⇒ + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.foreach { + case (param, pIndex) if param.asVar.definedBy.contains(index) && + (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic) != -1 || + project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) ⇒ + facts += InstanceField( + AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic), + declClass, taintedField + ) + case _ ⇒ // Nothing to do + } - case sf: StaticField ⇒ facts += sf + case sf: StaticField ⇒ facts += sf - case _ ⇒ // Nothing to do - } - } + case _ ⇒ // Nothing to do + } + } - facts - } + facts + } - /** - * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. - * If the callee's return value was tainted and it is assigned to a variable in the callee, the - * variable will be tainted. - * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds - * at this method. - * Creates new taints and FlowFacts, if necessary. - * If the sanitize method was called, nothing will be tainted. - */ - override def returnFlow(exit: NewJavaStatement, in: Fact, call: NewJavaStatement, callFact: Fact): Set[Fact] = { - val in_set = Set(in) // dirty hack for compatibility with old code - val callee = exit.callable() /** - * Checks whether the callee's formal parameter is of a reference type. + * Taints an actual parameter, if the corresponding formal parameter was tainted in the callee. + * If the callee's return value was tainted and it is assigned to a variable in the callee, the + * variable will be tainted. + * If a FlowFact held in the callee, this method will be appended to a new FlowFact, which holds + * at this method. + * Creates new taints and FlowFacts, if necessary. + * If the sanitize method was called, nothing will be tainted. */ - def isRefTypeParam(index: Int): Boolean = - if (index == -1) true - else { - val parameterOffset = if (callee.isStatic) 0 else 1 - callee.descriptor.parameterType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) - - parameterOffset - ).isReferenceType - } + override def returnFlow(exit: NewJavaStatement, in: Fact, call: NewJavaStatement, callFact: Fact): Set[Fact] = { + val in_set = Set(in) // dirty hack for compatibility with old code + val callee = exit.callable() + /** + * Checks whether the callee's formal parameter is of a reference type. + */ + def isRefTypeParam(index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (callee.isStatic) 0 else 1 + callee.descriptor.parameterType( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + - parameterOffset + ).isReferenceType + } - if (sanitizesReturnValue(callee)) return Set.empty - val callStatement = asCall(call.stmt) - val allParams = callStatement.allParams - var flows: Set[Fact] = Set.empty - in_set.foreach { - // Taint actual parameter if formal parameter is tainted - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ - val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) - ) - flows ++= param.asVar.definedBy.iterator.map(Variable) + if (sanitizesReturnValue(callee)) return Set.empty + val callStatement = asCall(call.stmt) + val allParams = callStatement.allParams + var flows: Set[Fact] = Set.empty + in_set.foreach { + // Taint actual parameter if formal parameter is tainted + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(Variable) - // Taint element of actual parameter if element of formal parameter is tainted - case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ - val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) - ) - flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) + // Taint element of actual parameter if element of formal parameter is tainted + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + val param = allParams( + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) - case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ - // Taint field of actual parameter if field of formal parameter is tainted - val param = - allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) - param.asVar.definedBy.foreach { defSite ⇒ - flows += InstanceField(defSite, declClass, taintedField) - } + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + // Taint field of actual parameter if field of formal parameter is tainted + val param = + allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) + param.asVar.definedBy.foreach { defSite ⇒ + flows += InstanceField(defSite, declClass, taintedField) + } - case sf: StaticField ⇒ flows += sf + case sf: StaticField ⇒ flows += sf - // Track the call chain to the sink back - case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ - flows += FlowFact(JavaMethod(call.method) +: flow) - case _ ⇒ - } + // Track the call chain to the sink back + case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ + flows += FlowFact(JavaMethod(call.method) +: flow) + case _ ⇒ + } - // Propagate taints of the return value - if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { - val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy - in_set.foreach { - case Variable(index) if returnValueDefinedBy.contains(index) ⇒ - flows += Variable(call.index) - case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ - flows += ArrayElement(call.index, taintedIndex) - case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ - flows += InstanceField(call.index, declClass, taintedField) - case NullFact ⇒ - val taints = createTaints(callee, call) - if (taints.nonEmpty) flows ++= taints - case _ ⇒ // Nothing to do - } + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + in_set.foreach { + case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + flows += Variable(call.index) + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + flows += ArrayElement(call.index, taintedIndex) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + flows += InstanceField(call.index, declClass, taintedField) + case NullFact ⇒ + val taints = createTaints(callee, call) + if (taints.nonEmpty) flows ++= taints + case _ ⇒ // Nothing to do + } + } + val flowFact = createFlowFact(callee, call, in) + if (flowFact.isDefined) flows += flowFact.get + + flows } - val flowFact = createFlowFact(callee, call, in) - if (flowFact.isDefined) flows += flowFact.get - flows - } + /** + * Removes taints according to `sanitizesParameter`. + */ + override def callToReturnFlow(call: NewJavaStatement, in: Fact): Set[Fact] = + if (sanitizesParameter(call, in)) Set() else Set(in) - /** - * Removes taints according to `sanitizesParameter`. - */ - override def callToReturnFlow(call: NewJavaStatement, in: Fact): Set[Fact] = - if (sanitizesParameter(call, in)) Set() else Set(in) + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a variable. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + protected def createTaints(callee: Method, call: NewJavaStatement): Set[Fact] - /** - * Called, when the exit to return facts are computed for some `callee` with the null fact and - * the callee's return value is assigned to a variable. - * Creates a taint, if necessary. - * - * @param callee The called method. - * @param call The call. - * @return Some variable fact, if necessary. Otherwise none. - */ - protected def createTaints(callee: Method, call: NewJavaStatement): Set[Fact] + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + protected def createFlowFact(callee: Method, call: NewJavaStatement, + in: Fact): Option[FlowFact] - /** - * Called, when the call to return facts are computed for some `callee`. - * Creates a FlowFact, if necessary. - * - * @param callee The method, which was called. - * @param call The call. - * @return Some FlowFact, if necessary. Otherwise None. - */ - protected def createFlowFact(callee: Method, call: NewJavaStatement, - in: Fact): Option[FlowFact] + /** + * If a parameter is tainted, the result will also be tainted. + * We assume that the callee does not call the source method. + */ + override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { + super.outsideAnalysisContext(callee) match { + case Some(_) ⇒ Some(((call: NewJavaStatement, successor: NewJavaStatement, in: Fact) ⇒ { + val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params + if (call.stmt.astID == Assignment.ASTID && (in match { + case Variable(index) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case ArrayElement(index, _) ⇒ + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) ⇒ true + case _ ⇒ false + } + case _ ⇒ false + })) Set(Variable(call.index)) + else Set.empty + }): OutsideAnalysisContextHandler) + case None ⇒ None + } - /** - * If a parameter is tainted, the result will also be tainted. - * We assume that the callee does not call the source method. - */ - override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { - super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some(((call: NewJavaStatement, successor: NewJavaStatement, in: Fact) ⇒ { - val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params - if (call.stmt.astID == Assignment.ASTID && (in match { - case Variable(index) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - } - case ArrayElement(index, _) ⇒ - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false - } - case _ ⇒ false - })) Set(Variable(call.index)) - else Set.empty - }): OutsideAnalysisContextHandler) - case None ⇒ None } - } + /** + * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. + * True by default. This method can be overwritten by a subclass. + * + * @param callee The callee. + * @return True, by default. + */ + protected def relevantCallee(callee: Method): Boolean = true - /** - * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. - * True by default. This method can be overwritten by a subclass. - * - * @param callee The callee. - * @return True, by default. - */ - protected def relevantCallee(callee: Method): Boolean = true + /** + * Creates new facts for an assignment. A new fact for the assigned variable will be created, + * if the expression contains a tainted variable + * + * @param expression The source expression of the assignment + * @param statement The assignment statement + * @param in The incoming facts + * @return The new facts, created by the assignment + */ + private def createNewTaints(expression: Expr[V], statement: NewJavaStatement, in: Fact): Set[Fact] = { + val in_set: Set[Fact] = Set(in) // dirty fix to reuse old code + expression.astID match { + case Var.ASTID ⇒ + val definedBy = expression.asVar.definedBy + in_set.collect { + case Variable(index) if definedBy.contains(index) ⇒ + Variable(statement.index) + } + case ArrayLoad.ASTID ⇒ + val loadExpression = expression.asArrayLoad + val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy + if (in_set.exists { + // One specific array element may be tainted + case ArrayElement(index, taintedElement) ⇒ + val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) + arrayDefinedBy.contains(index) && + (loadedIndex.isEmpty || taintedElement == loadedIndex.get) + // Or the whole array + case Variable(index) ⇒ arrayDefinedBy.contains(index) + case _ ⇒ false + }) Set(Variable(statement.index)) + else + Set.empty + case GetField.ASTID ⇒ + val get = expression.asGetField + val objectDefinedBy = get.objRef.asVar.definedBy + if (in_set.exists { + // The specific field may be tainted + case InstanceField(index, _, taintedField) ⇒ + taintedField == get.name && objectDefinedBy.contains(index) + // Or the whole object + case Variable(index) ⇒ objectDefinedBy.contains(index) + case _ ⇒ false + }) + Set(Variable(statement.index)) + else + Set.empty + case GetStatic.ASTID ⇒ + val get = expression.asGetStatic + if (in_set.contains(StaticField(get.declaringClass, get.name))) + Set(Variable(statement.index)) + else Set.empty + case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | + NewArray.ASTID | ArrayLength.ASTID ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) + case _ ⇒ Set.empty + } + } - /** - * Creates new facts for an assignment. A new fact for the assigned variable will be created, - * if the expression contains a tainted variable - * - * @param expression The source expression of the assignment - * @param statement The assignment statement - * @param in The incoming facts - * @return The new facts, created by the assignment - */ - private def createNewTaints(expression: Expr[V], statement: NewJavaStatement, in: Fact): Set[Fact] = { - val in_set: Set[Fact] = Set(in) // dirty fix to reuse old code - expression.astID match { - case Var.ASTID ⇒ + /** + * Checks, if the result of some variable expression could be tainted. + * + * @param expression The variable expression. + * @param in The current data flow facts. + * @return True, if the expression could be tainted + */ + private def isTainted(expression: Expr[V], in: Fact): Boolean = { val definedBy = expression.asVar.definedBy - in_set.collect { - case Variable(index) if definedBy.contains(index) ⇒ - Variable(statement.index) - } - case ArrayLoad.ASTID ⇒ - val loadExpression = expression.asArrayLoad - val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy - if (in_set.exists { - // One specific array element may be tainted - case ArrayElement(index, taintedElement) ⇒ - val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) - arrayDefinedBy.contains(index) && - (loadedIndex.isEmpty || taintedElement == loadedIndex.get) - // Or the whole array - case Variable(index) ⇒ arrayDefinedBy.contains(index) - case _ ⇒ false - }) Set(Variable(statement.index)) - else - Set.empty - case GetField.ASTID ⇒ - val get = expression.asGetField - val objectDefinedBy = get.objRef.asVar.definedBy - if (in_set.exists { - // The specific field may be tainted - case InstanceField(index, _, taintedField) ⇒ - taintedField == get.name && objectDefinedBy.contains(index) - // Or the whole object - case Variable(index) ⇒ objectDefinedBy.contains(index) - case _ ⇒ false + expression.isVar && (in match { + case Variable(index) ⇒ definedBy.contains(index) + case ArrayElement(index, _) ⇒ definedBy.contains(index) + case InstanceField(index, _, _) ⇒ definedBy.contains(index) + case _ ⇒ false }) - Set(Variable(statement.index)) - else - Set.empty - case GetStatic.ASTID ⇒ - val get = expression.asGetStatic - if (in_set.contains(StaticField(get.declaringClass, get.name))) - Set(Variable(statement.index)) - else Set.empty - case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | - NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ - acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) - case _ ⇒ Set.empty } - } - - /** - * Checks, if the result of some variable expression could be tainted. - * - * @param expression The variable expression. - * @param in The current data flow facts. - * @return True, if the expression could be tainted - */ - private def isTainted(expression: Expr[V], in: Fact): Boolean = { - val definedBy = expression.asVar.definedBy - expression.isVar && (in match { - case Variable(index) ⇒ definedBy.contains(index) - case ArrayElement(index, _) ⇒ definedBy.contains(index) - case InstanceField(index, _, _) ⇒ definedBy.contains(index) - case _ ⇒ false - }) - } } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala index e435a3d0d3..1a25153864 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala @@ -3,22 +3,22 @@ package org.opalj.tac.fpcf.analyses.ifds.taint trait NewTaintProblem[C, Statement, IFDSFact] { - /** - * Checks, if some `callee` is a sanitizer, which sanitizes its return value. - * In this case, no return flow facts will be created. - * - * @param callee The method, which was called. - * @return True, if the method is a sanitizer. - */ - protected def sanitizesReturnValue(callee: C): Boolean + /** + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. + * + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. + */ + protected def sanitizesReturnValue(callee: C): Boolean - /** - * Called in callToReturnFlow. This method can return whether the input fact - * will be removed after `callee` was called. I.e. the method could sanitize parameters. - * - * @param call The call statement. - * @param in The fact which holds before the call. - * @return Whether in will be removed after the call. - */ - protected def sanitizesParameter(call: Statement, in: IFDSFact): Boolean + /** + * Called in callToReturnFlow. This method can return whether the input fact + * will be removed after `callee` was called. I.e. the method could sanitize parameters. + * + * @param call The call statement. + * @param in The fact which holds before the call. + * @return Whether in will be removed after the call. + */ + protected def sanitizesParameter(call: Statement, in: IFDSFact): Boolean } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala index 8c506007f6..9c9d6fafef 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala @@ -8,16 +8,16 @@ import org.opalj.tac.fpcf.analyses.ifds.taint.Fact case class NewTaint(flows: Map[NewJavaStatement, Set[Fact]]) extends IFDSProperty[NewJavaStatement, Fact] { - override type Self = NewTaint - override def create(result: Map[NewJavaStatement, Set[Fact]]): IFDSProperty[NewJavaStatement, Fact] = new NewTaint(result) + override type Self = NewTaint + override def create(result: Map[NewJavaStatement, Set[Fact]]): IFDSProperty[NewJavaStatement, Fact] = new NewTaint(result) - override def key: PropertyKey[NewTaint] = NewTaint.key + override def key: PropertyKey[NewTaint] = NewTaint.key } object NewTaint extends IFDSPropertyMetaInformation[NewJavaStatement, Fact] { - override type Self = NewTaint - override def create(result: Map[NewJavaStatement, Set[Fact]]): IFDSProperty[NewJavaStatement, Fact] = new NewTaint(result) + override type Self = NewTaint + override def create(result: Map[NewJavaStatement, Set[Fact]]): IFDSProperty[NewJavaStatement, Fact] = new NewTaint(result) - val key: PropertyKey[NewTaint] = PropertyKey.create("NewTaint", new NewTaint(Map.empty)) + val key: PropertyKey[NewTaint] = PropertyKey.create("NewTaint", new NewTaint(Map.empty)) } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala index 1d30f75b02..fca1bc59ac 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala @@ -17,48 +17,48 @@ import org.opalj.tac.fpcf.properties.NewTaint * @author Mario Trageser */ class NewForwardTaintAnalysisFixture(project: SomeProject) - extends IFDSAnalysis()(project, new NewForwardTaintProblemFixture(project), NewTaint) + extends IFDSAnalysis()(project, new NewForwardTaintProblemFixture(project), NewTaint) class NewForwardTaintProblemFixture(p: SomeProject) extends NewForwardTaintProblem(p) { - /** - * The analysis starts with all public methods in TaintAnalysisTestClass. - */ - override val entryPoints: Seq[(Method, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ - classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") - .flatMap(classFile ⇒ classFile.methods) - .filter(method ⇒ method.isPublic && outsideAnalysisContext(method).isEmpty) - .map(method ⇒ method → NullFact) + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(Method, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ + classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") + .flatMap(classFile ⇒ classFile.methods) + .filter(method ⇒ method.isPublic && outsideAnalysisContext(method).isEmpty) + .map(method ⇒ method → NullFact) - /** - * The sanitize method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" - /** - * We do not sanitize paramters. - */ - override protected def sanitizesParameter(call: NewJavaStatement, in: Fact): Boolean = false + /** + * We do not sanitize paramters. + */ + override protected def sanitizesParameter(call: NewJavaStatement, in: Fact): Boolean = false - /** - * Creates a new variable fact for the callee, if the source was called. - */ - override protected def createTaints(callee: Method, call: NewJavaStatement): Set[Fact] = - if (callee.name == "source") Set(Variable(call.index)) - else Set.empty + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: Method, call: NewJavaStatement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty - /** - * Create a FlowFact, if sink is called with a tainted variable. - * Note, that sink does not accept array parameters. No need to handle them. - */ - override protected def createFlowFact(callee: Method, call: NewJavaStatement, - in: Fact): Option[FlowFact] = - if (callee.name == "sink" && in == Variable(-2)) Some(FlowFact(Seq(JavaMethod(call.method)))) - else None + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact(callee: Method, call: NewJavaStatement, + in: Fact): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) Some(FlowFact(Seq(JavaMethod(call.method)))) + else None } object NewForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact, Method, NewJavaStatement] { - override def init(p: SomeProject, ps: PropertyStore) = new NewForwardTaintAnalysisFixture(p) - override def property: IFDSPropertyMetaInformation[NewJavaStatement, Fact] = NewTaint - override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(NewTaint)) - override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) + override def init(p: SomeProject, ps: PropertyStore) = new NewForwardTaintAnalysisFixture(p) + override def property: IFDSPropertyMetaInformation[NewJavaStatement, Fact] = NewTaint + override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(NewTaint)) + override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) } From 75be22961a46f018999195f97cb4267c10c40efb Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 1 Apr 2022 15:41:13 +0200 Subject: [PATCH 33/67] Refactor: Move old code to .old, remove New prefix --- .../tac/fpcf/analyses/IFDSEvaluation.scala | 5 +- .../heros/analyses/HerosAnalysis.scala | 6 +- .../analyses/HerosVariableTypeAnalysis.scala | 38 ++--- .../analyses/heros/analyses/VTAEquality.scala | 20 +-- .../HerosBackwardClassForNameAnalysis.scala | 28 ++-- .../HerosForwardClassForNameAnalysis.scala | 36 ++--- .../analyses/taint/HerosTaintAnalysis.scala | 3 +- .../analyses/heros/cfg/OpalBackwardICFG.scala | 24 +-- .../analyses/heros/cfg/OpalForwardICFG.scala | 20 +-- .../fpcf/analyses/heros/cfg/OpalICFG.scala | 38 ++--- ...rdClassForNameTaintAnalysisScheduler.scala | 27 ++-- ...rdClassForNameTaintAnalysisScheduler.scala | 19 +-- .../ForwardTaintAnalysisFixtureTest.scala | 16 +- .../BackwardTaintAnalysisFixtureTest.scala | 15 +- .../ForwardTaintAnalysisFixtureTest.scala} | 16 +- .../opalj/fpcf/ifds/{ => old}/VTATest.scala | 17 +-- .../taint/BackwardFlowPathMatcher.scala | 11 +- .../vta/ExpectedCalleeMatcher.scala | 4 +- .../properties/vta/ExpectedTypeMatcher.scala | 3 +- .../fpcf/properties/vta/VTAMatcher.scala | 2 +- ...MultilingualForwardTaintAnalysisTest.scala | 4 +- .../ifds/taint/JavaForwardTaintAnalysis.scala | 19 +-- .../taint/NativeForwardTaintProblem.scala | 4 +- .../tac/fpcf/analyses/ifds/Callable.scala | 2 +- .../tac/fpcf/analyses/ifds/ForwardICFG.scala | 141 ++++++++++-------- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 125 ++++++---------- .../fpcf/analyses/ifds/NewForwardICFG.scala | 118 --------------- .../analyses/ifds/NewJavaIFDSProblem.scala | 65 -------- .../ifds/{ => old}/AbstractIFDSAnalysis.scala | 112 +++++++------- .../ifds/{ => old}/BackwardIFDSAnalysis.scala | 85 +++++------ .../fpcf/analyses/ifds/old/ForwardICFG.scala | 95 ++++++++++++ .../ifds/{ => old}/ForwardIFDSAnalysis.scala | 45 +++--- .../IFDSBasedVariableTypeAnalysis.scala | 24 ++- .../analyses/ifds/{ => old}/IFDSProblem.scala | 2 +- .../analyses/ifds/old/JavaIFDSProblem.scala | 109 ++++++++++++++ .../ifds/{ => old}/VariableTypeProblem.scala | 22 +-- .../taint/BackwardTaintProblem.scala | 39 ++--- .../taint/ForwardTaintProblem.scala} | 124 ++++++++------- .../ifds/old/taint/TaintProblem.scala | 38 +++++ .../ifds/taint/ForwardTaintProblem.scala | 121 ++++++++------- .../analyses/ifds/taint/NewTaintProblem.scala | 24 --- .../analyses/ifds/taint/TaintProblem.scala | 25 +--- .../opalj/tac/fpcf/properties/NewTaint.scala | 23 --- .../opalj/tac/fpcf/properties/OldTaint.scala | 23 +++ .../taint/ForwardTaintAnalysisFixture.scala | 43 +++--- .../NewForwardTaintAnalysisFixture.scala | 64 -------- .../BackwardTaintAnalysisFixture.scala | 17 ++- .../old/ForwardTaintAnalysisFixture.scala | 66 ++++++++ 48 files changed, 947 insertions(+), 980 deletions(-) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/{ => old}/BackwardTaintAnalysisFixtureTest.scala (87%) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/{NewForwardTaintAnalysisFixtureTest.scala => old/ForwardTaintAnalysisFixtureTest.scala} (64%) rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/{ => old}/VTATest.scala (79%) delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{ => old}/AbstractIFDSAnalysis.scala (92%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{ => old}/BackwardIFDSAnalysis.scala (87%) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardICFG.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{ => old}/ForwardIFDSAnalysis.scala (77%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{ => old}/IFDSBasedVariableTypeAnalysis.scala (82%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{ => old}/IFDSProblem.scala (98%) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/JavaIFDSProblem.scala rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{ => old}/VariableTypeProblem.scala (96%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{ => old}/taint/BackwardTaintProblem.scala (91%) rename OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/{taint/NewForwardTaintProblem.scala => old/taint/ForwardTaintProblem.scala} (77%) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala delete mode 100644 OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala rename OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/{ => old}/BackwardTaintAnalysisFixture.scala (83%) create mode 100644 OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala index d228f9265a..573b8deca8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/IFDSEvaluation.scala @@ -2,14 +2,11 @@ package org.opalj.tac.fpcf.analyses import java.io.File - import org.opalj.tac.fpcf.analyses.heros.analyses.taint.HerosBackwardClassForNameAnalysisRunner import org.opalj.tac.fpcf.analyses.heros.analyses.taint.HerosForwardClassForNameAnalysisRunner import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosVariableTypeAnalysisRunner -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisRunner +import org.opalj.tac.fpcf.analyses.ifds.old.{AbstractIFDSAnalysis, IFDSBasedVariableTypeAnalysisRunner, IFDSBasedVariableTypeAnalysisScheduler} import org.opalj.tac.fpcf.analyses.taint.BackwardClassForNameTaintAnalysisRunner import org.opalj.tac.fpcf.analyses.taint.ForwardClassForNameAnalysisRunner diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala index 0de467bf9c..2d952a16f1 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala @@ -22,14 +22,14 @@ import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.fpcf.analyses.heros.cfg.OpalICFG -import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.Assignment import org.opalj.tac.Call import org.opalj.tac.ExprStmt import org.opalj.tac.Stmt import org.opalj.tac.cg.{RTACallGraphKey, TypeProviderKey} import org.opalj.tac.fpcf.analyses.cg.TypeProvider -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V /** * A common subclass of all Heros analyses. @@ -40,7 +40,7 @@ import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V * @author Mario Trageser */ abstract class HerosAnalysis[F](p: SomeProject, icfg: OpalICFG) - extends DefaultIFDSTabulationProblem[NewJavaStatement, F, Method, OpalICFG](icfg) { + extends DefaultIFDSTabulationProblem[JavaStatement, F, Method, OpalICFG](icfg) { /** * The project's property store. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala index 369320abc6..5c18d68234 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala @@ -2,19 +2,15 @@ package org.opalj.tac.fpcf.analyses.heros.analyses import scala.annotation.tailrec - import java.io.File import java.util import java.util.Collections - import scala.collection.JavaConverters._ - import heros.FlowFunctions import heros.FlowFunction import heros.flowfunc.Identity import heros.TwoElementSet import heros.flowfunc.KillAll - import org.opalj.util.Milliseconds import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG import org.opalj.collection.immutable.EmptyIntTrieSet @@ -30,7 +26,7 @@ import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.br.DeclaredMethod import org.opalj.tac.fpcf.properties.cg.Callees -import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.Assignment import org.opalj.tac.Expr import org.opalj.tac.GetStatic @@ -41,11 +37,7 @@ import org.opalj.tac.ArrayStore import org.opalj.tac.GetField import org.opalj.tac.Var import org.opalj.tac.ReturnValue -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.CalleeType -import org.opalj.tac.fpcf.analyses.ifds.VariableType -import org.opalj.tac.fpcf.analyses.ifds.VTAFact -import org.opalj.tac.fpcf.analyses.ifds.VTANullFact +import org.opalj.tac.fpcf.analyses.ifds.old.{AbstractIFDSAnalysis, CalleeType, VTAFact, VTANullFact, VariableType} /** * An implementation of the IFDSBasedVariableTypeAnalysis in the Heros framework. @@ -59,8 +51,8 @@ class HerosVariableTypeAnalysis( initialMethods: Map[Method, util.Set[VTAFact]] ) extends HerosAnalysis[VTAFact](p, icfg) { - override val initialSeeds: util.Map[NewJavaStatement, util.Set[VTAFact]] = { - var result: Map[NewJavaStatement, util.Set[VTAFact]] = Map.empty + override val initialSeeds: util.Map[JavaStatement, util.Set[VTAFact]] = { + var result: Map[JavaStatement, util.Set[VTAFact]] = Map.empty for ((m, facts) ← initialMethods) { result += icfg.getStartPointsOf(m).iterator().next() -> facts } @@ -69,13 +61,13 @@ class HerosVariableTypeAnalysis( override def createZeroValue(): VTAFact = VTANullFact - override protected def createFlowFunctionsFactory(): FlowFunctions[NewJavaStatement, VTAFact, Method] = { + override protected def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, VTAFact, Method] = { - new FlowFunctions[NewJavaStatement, VTAFact, Method]() { + new FlowFunctions[JavaStatement, VTAFact, Method]() { override def getNormalFlowFunction( - statement: NewJavaStatement, - succ: NewJavaStatement + statement: JavaStatement, + succ: JavaStatement ): FlowFunction[VTAFact] = { if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt @@ -103,7 +95,7 @@ class HerosVariableTypeAnalysis( } } - override def getCallFlowFunction(stmt: NewJavaStatement, callee: Method): FlowFunction[VTAFact] = + override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[VTAFact] = if (!insideAnalysisContext(callee)) KillAll.v() else { val callObject = asCall(stmt.stmt) @@ -129,10 +121,10 @@ class HerosVariableTypeAnalysis( } override def getReturnFlowFunction( - stmt: NewJavaStatement, + stmt: JavaStatement, callee: Method, - exit: NewJavaStatement, - succ: NewJavaStatement + exit: JavaStatement, + succ: JavaStatement ): FlowFunction[VTAFact] = if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { val returnValue = exit.stmt.asReturnValue.expr.asVar @@ -147,8 +139,8 @@ class HerosVariableTypeAnalysis( } else KillAll.v() override def getCallToReturnFlowFunction( - statement: NewJavaStatement, - succ: NewJavaStatement + statement: JavaStatement, + succ: JavaStatement ): FlowFunction[VTAFact] = { if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt @@ -227,7 +219,7 @@ class HerosVariableTypeAnalysis( callee.body.isDefined && (callee.classFile.fqn.startsWith("java/lang") || callee.classFile.fqn.startsWith("org/opalj/fpcf/fixtures/vta")) - private def getCallees(statement: NewJavaStatement): Iterator[DeclaredMethod] = { + private def getCallees(statement: JavaStatement): Iterator[DeclaredMethod] = { val context = typeProvider.newContext(declaredMethods(statement.method)) val FinalEP(_, callees) = propertyStore(declaredMethods(statement.method), Callees.key) callees.directCallees(context, statement.stmt.pc).map(_.method) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala index 606d03f934..7f460e3180 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala @@ -3,14 +3,11 @@ package org.opalj.tac.fpcf.analyses.heros.analyses import java.io.File import java.net.URL - import scala.collection.JavaConverters._ - import com.typesafe.config.Config import com.typesafe.config.ConfigValueFactory import heros.solver.IFDSSolver import org.opalj.BaseConfig - import org.opalj.util.ScalaMajorVersion import org.opalj.fpcf.EPS import org.opalj.fpcf.FinalEP @@ -29,17 +26,12 @@ import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG -import org.opalj.tac.fpcf.analyses.ifds.CalleeType -import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement -import org.opalj.tac.fpcf.analyses.ifds.VariableType -import org.opalj.tac.fpcf.analyses.ifds.VTAFact -import org.opalj.tac.fpcf.analyses.ifds.VTANullFact -import org.opalj.tac.fpcf.analyses.ifds.VTAResult +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.Assignment import org.opalj.tac.New import org.opalj.tac.Return import org.opalj.tac.ReturnValue +import org.opalj.tac.fpcf.analyses.ifds.old.{CalleeType, IFDSBasedVariableTypeAnalysisScheduler, VTAFact, VTANullFact, VTAResult, VariableType} object VTAEquality { @@ -115,7 +107,7 @@ object VTAEquality { piKeyUnidueId != PropertyStoreKey.uniqueId } val propertyStore = project.get(PropertyStoreKey) - var result = Map.empty[NewJavaStatement, Set[VTAFact]] + var result = Map.empty[JavaStatement, Set[VTAFact]] project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) case Some(requirements) ⇒ @@ -134,8 +126,8 @@ object VTAEquality { case FinalEP(_, VTAResult(map)) ⇒ map case _ ⇒ throw new RuntimeException } - entityResult.keys.foreach { oldStatement ⇒ - val statement = oldStatement.asNewJavaStatement + entityResult.keys.foreach { declaredMethodStatement ⇒ + val statement = declaredMethodStatement.asJavaStatement /* * Heros returns the facts before the statements. * However, CalleeType facts hold after the statement and are therefore not returned. @@ -144,7 +136,7 @@ object VTAEquality { */ result = result.updated( statement, - result.getOrElse(statement, Set.empty) ++ entityResult(oldStatement) + result.getOrElse(statement, Set.empty) ++ entityResult(declaredMethodStatement) .filter(fact ⇒ fact != VTANullFact && !fact.isInstanceOf[CalleeType]) ) } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala index 2423668655..49b5677f23 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -12,15 +12,11 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.Method import org.opalj.br.ObjectType import org.opalj.tac.fpcf.analyses.heros.cfg.OpalBackwardICFG -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, NewJavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} import org.opalj.tac.Assignment -import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement -import org.opalj.tac.fpcf.analyses.ifds.taint.Variable import org.opalj.tac.BinaryExpr import org.opalj.tac.Compare import org.opalj.tac.PrefixExpr -import org.opalj.tac.fpcf.analyses.ifds.taint.InstanceField import org.opalj.tac.ArrayLength import org.opalj.tac.ArrayLoad import org.opalj.tac.Expr @@ -28,16 +24,16 @@ import org.opalj.tac.GetField import org.opalj.tac.NewArray import org.opalj.tac.PrimitiveTypecastExpr import org.opalj.tac.Var -import org.opalj.tac.fpcf.analyses.ifds.taint.StaticField -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.ArrayStore import org.opalj.tac.PutField import org.opalj.tac.PutStatic import org.opalj.tac.ReturnValue import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner -import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.analyses.ifds.old.AbstractIFDSAnalysis import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem +import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, Fact, FlowFact, InstanceField, StaticField, Variable} /** * An implementation of the BackwardClassForNameAnalysis in the Heros framework. @@ -47,7 +43,7 @@ import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem */ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) extends HerosTaintAnalysis(p, icfg) { - override val initialSeeds: util.Map[NewJavaStatement, util.Set[Fact]] = + override val initialSeeds: util.Map[JavaStatement, util.Set[Fact]] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "java/lang/Class") .flatMap(classFile ⇒ classFile.methods) @@ -58,11 +54,11 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) override def followReturnsPastSeeds(): Boolean = true - override def createFlowFunctionsFactory(): FlowFunctions[NewJavaStatement, Fact, Method] = { + override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { - new FlowFunctions[NewJavaStatement, Fact, Method]() { + new FlowFunctions[JavaStatement, Fact, Method]() { - override def getNormalFlowFunction(statement: NewJavaStatement, succ: NewJavaStatement): FlowFunction[Fact] = { + override def getNormalFlowFunction(statement: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { val method = statement.method val stmt = statement.stmt source: Fact ⇒ { @@ -123,7 +119,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } - override def getCallFlowFunction(stmt: NewJavaStatement, callee: Method): FlowFunction[Fact] = { + override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[Fact] = { val callObject = asCall(stmt.stmt) val staticCall = callee.isStatic source: Fact ⇒ { @@ -165,7 +161,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } } - override def getReturnFlowFunction(statement: NewJavaStatement, callee: Method, exit: NewJavaStatement, succ: NewJavaStatement): FlowFunction[Fact] = { + override def getReturnFlowFunction(statement: JavaStatement, callee: Method, exit: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { // If a method has no caller, returnFlow will be called with a null statement. if (statement == null) return Identity.v() val stmt = statement.stmt @@ -195,7 +191,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) }).asJava } - override def getCallToReturnFlowFunction(stmt: NewJavaStatement, succ: NewJavaStatement): FlowFunction[Fact] = + override def getCallToReturnFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = Identity.v() } } @@ -217,7 +213,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) }.toSet } - private def createNewTaints(expression: Expr[V], statement: NewJavaStatement): Set[Fact] = + private def createNewTaints(expression: Expr[V], statement: JavaStatement): Set[Fact] = expression.astID match { case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) case ArrayLoad.ASTID ⇒ diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index 16f2ea3687..f8aee3d983 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -30,13 +30,7 @@ import org.opalj.tac.PutField import org.opalj.tac.PutStatic import org.opalj.tac.ReturnValue import org.opalj.tac.Var -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, NewJavaStatement} -import org.opalj.tac.fpcf.analyses.ifds.taint.ArrayElement -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact -import org.opalj.tac.fpcf.analyses.ifds.taint.InstanceField -import org.opalj.tac.fpcf.analyses.ifds.taint.StaticField -import org.opalj.tac.fpcf.analyses.ifds.taint.Variable +import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} import org.opalj.tac.ArrayLength import org.opalj.tac.ArrayLoad import org.opalj.tac.ArrayStore @@ -47,8 +41,10 @@ import org.opalj.tac.PrefixExpr import org.opalj.tac.PrimitiveTypecastExpr import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner +import org.opalj.tac.fpcf.analyses.ifds.old.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, Fact, FlowFact, InstanceField, StaticField, Variable} /** * An implementation of the ForwardClassForNameAnalysis in the Heros framework. @@ -62,8 +58,8 @@ class HerosForwardClassForNameAnalysis( initialMethods: Map[Method, util.Set[Fact]] ) extends HerosTaintAnalysis(p, icfg) { - override val initialSeeds: util.Map[NewJavaStatement, util.Set[Fact]] = { - var result: Map[NewJavaStatement, util.Set[Fact]] = Map.empty + override val initialSeeds: util.Map[JavaStatement, util.Set[Fact]] = { + var result: Map[JavaStatement, util.Set[Fact]] = Map.empty for ((m, facts) ← initialMethods) { result += icfg.getStartPointsOf(m).iterator().next() -> facts } @@ -74,11 +70,11 @@ class HerosForwardClassForNameAnalysis( var flowFacts = Map.empty[Method, Set[FlowFact]] - override def createFlowFunctionsFactory(): FlowFunctions[NewJavaStatement, Fact, Method] = { + override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { - new FlowFunctions[NewJavaStatement, Fact, Method]() { + new FlowFunctions[JavaStatement, Fact, Method]() { - override def getNormalFlowFunction(stmt: NewJavaStatement, succ: NewJavaStatement): FlowFunction[Fact] = { + override def getNormalFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { stmt.stmt.astID match { case Assignment.ASTID ⇒ handleAssignment(stmt, stmt.stmt.asAssignment.expr) @@ -127,7 +123,7 @@ class HerosForwardClassForNameAnalysis( } } - def handleAssignment(stmt: NewJavaStatement, expr: Expr[V]): FlowFunction[Fact] = + def handleAssignment(stmt: JavaStatement, expr: Expr[V]): FlowFunction[Fact] = expr.astID match { case Var.ASTID ⇒ (source: Fact) ⇒ { @@ -194,7 +190,7 @@ class HerosForwardClassForNameAnalysis( case _ ⇒ Identity.v() } - override def getCallFlowFunction(stmt: NewJavaStatement, callee: Method): FlowFunction[Fact] = { + override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[Fact] = { val callObject = asCall(stmt.stmt) val allParams = callObject.allParams if (relevantCallee(callee)) { @@ -248,10 +244,10 @@ class HerosForwardClassForNameAnalysis( } override def getReturnFlowFunction( - stmt: NewJavaStatement, + stmt: JavaStatement, callee: Method, - exit: NewJavaStatement, - succ: NewJavaStatement + exit: JavaStatement, + succ: JavaStatement ): FlowFunction[Fact] = { def isRefTypeParam(index: Int): Boolean = @@ -344,8 +340,8 @@ class HerosForwardClassForNameAnalysis( } override def getCallToReturnFlowFunction( - stmt: NewJavaStatement, - succ: NewJavaStatement + stmt: JavaStatement, + succ: JavaStatement ): FlowFunction[Fact] = Identity.v() } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala index 39ad7524c6..d6cf56faf3 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala @@ -4,8 +4,7 @@ package org.opalj.tac.fpcf.analyses.heros.analyses.taint import org.opalj.br.analyses.SomeProject import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.cfg.OpalICFG -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact -import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, NullFact} /** * A common subclass of all Heros taint analyses. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala index dd5b26480d..ffc6d8e75d 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala @@ -5,7 +5,7 @@ import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.br.cfg.BasicBlock import org.opalj.tac.TACode -import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import java.util.concurrent.ConcurrentLinkedQueue import java.util.{Collections, Collection ⇒ JCollection, List ⇒ JList, Set ⇒ JSet} @@ -18,22 +18,22 @@ import scala.collection.JavaConverters._ */ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { - override def getPredsOf(stmt: NewJavaStatement): JList[NewJavaStatement] = super.getSuccsOf(stmt) + override def getPredsOf(stmt: JavaStatement): JList[JavaStatement] = super.getSuccsOf(stmt) - override def getSuccsOf(stmt: NewJavaStatement): JList[NewJavaStatement] = super.getPredsOf(stmt) + override def getSuccsOf(stmt: JavaStatement): JList[JavaStatement] = super.getPredsOf(stmt) - override def getStartPointsOf(m: Method): JCollection[NewJavaStatement] = { + override def getStartPointsOf(m: Method): JCollection[JavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).map { case bb: BasicBlock ⇒ val index = bb.endPC - NewJavaStatement(m, index, code, cfg) + JavaStatement(m, index, code, cfg) }.asJava } - override def isExitStmt(stmt: NewJavaStatement): Boolean = stmt.index == 0 + override def isExitStmt(stmt: JavaStatement): Boolean = stmt.index == 0 - override def isStartPoint(stmt: NewJavaStatement): Boolean = { + override def isStartPoint(stmt: JavaStatement): Boolean = { val cfg = stmt.cfg val index = stmt.index (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).exists { @@ -41,8 +41,8 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { } } - override def allNonCallStartNodes(): JSet[NewJavaStatement] = { - val res = new ConcurrentLinkedQueue[NewJavaStatement] + override def allNonCallStartNodes(): JSet[JavaStatement] = { + val res = new ConcurrentLinkedQueue[JavaStatement] project.parForeachMethodWithBody() { mi ⇒ val m = mi.method val TACode(_, code, _, cfg, _) = tacai(m) @@ -53,7 +53,7 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { } var index = 0 while (index < endIndex) { - val statement = NewJavaStatement(m, index, code, cfg) + val statement = JavaStatement(m, index, code, cfg) if (!(isCallStmt(statement) || startIndices.contains(index))) res.add(statement) index += 1 @@ -63,10 +63,10 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { Collections.emptySet() } - def getExitStmt(method: Method): NewJavaStatement = { + def getExitStmt(method: Method): JavaStatement = { val tac = tacai(method) val cfg = tac.cfg val code = tac.stmts - NewJavaStatement(method, 0, code, cfg) + JavaStatement(method, 0, code, cfg) } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala index 14706dff1a..805126cd07 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala @@ -9,7 +9,7 @@ import java.util.concurrent.ConcurrentLinkedQueue import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.tac.TACode -import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement /** * A forward ICFG for Heros analyses. @@ -18,24 +18,24 @@ import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement */ class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { - override def getStartPointsOf(m: Method): JCollection[NewJavaStatement] = { + override def getStartPointsOf(m: Method): JCollection[JavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) - Collections.singletonList(NewJavaStatement(m, 0, code, cfg)) + Collections.singletonList(JavaStatement(m, 0, code, cfg)) } - override def isExitStmt(stmt: NewJavaStatement): Boolean = stmt.cfg.bb(stmt.index).successors.exists(_.isExitNode) + override def isExitStmt(stmt: JavaStatement): Boolean = stmt.cfg.bb(stmt.index).successors.exists(_.isExitNode) - override def isStartPoint(stmt: NewJavaStatement): Boolean = stmt.index == 0 + override def isStartPoint(stmt: JavaStatement): Boolean = stmt.index == 0 - override def allNonCallStartNodes(): JSet[NewJavaStatement] = { - val res = new ConcurrentLinkedQueue[NewJavaStatement] + override def allNonCallStartNodes(): JSet[JavaStatement] = { + val res = new ConcurrentLinkedQueue[JavaStatement] project.parForeachMethodWithBody() { mi ⇒ val m = mi.method val TACode(_, code, _, cfg, _) = tacai(m) val endIndex = code.length var index = 1 while (index < endIndex) { - val statement = NewJavaStatement(m, index, code, cfg) + val statement = JavaStatement(m, index, code, cfg) if (!isCallStmt(statement)) res.add(statement) index += 1 @@ -45,13 +45,13 @@ class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { Collections.emptySet() } - def getExitStmts(method: Method): Iterator[NewJavaStatement] = { + def getExitStmts(method: Method): Iterator[JavaStatement] = { val tac = tacai(method) val cfg = tac.cfg val code = tac.stmts cfg.allNodes.filter(_.isExitNode).flatMap(_.predecessors).map { bb ⇒ val endPc = bb.asBasicBlock.endPC - NewJavaStatement(method, endPc, code, cfg) + JavaStatement(method, endPc, code, cfg) } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala index 6fe2fce82e..22e314febb 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala @@ -18,14 +18,14 @@ import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.br.DefinedMethod import org.opalj.br.MultipleDefinedMethods import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.Assignment import org.opalj.tac.DUVar import org.opalj.tac.FunctionCall import org.opalj.tac.LazyDetachedTACAIKey import org.opalj.tac.TACMethodParameter import org.opalj.tac.TACode -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.Expr import org.opalj.tac.ExprStmt import org.opalj.tac.MethodCall @@ -44,7 +44,7 @@ import org.opalj.tac.fpcf.analyses.cg.TypeProvider * @param project The project, which is analyzed. * @author Mario Trageser */ -abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[NewJavaStatement, Method] { +abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaStatement, Method] { val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) @@ -52,31 +52,31 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[NewJava implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) - override def getMethodOf(stmt: NewJavaStatement): Method = stmt.method + override def getMethodOf(stmt: JavaStatement): Method = stmt.method - override def getPredsOf(stmt: NewJavaStatement): JList[NewJavaStatement] = { + override def getPredsOf(stmt: JavaStatement): JList[JavaStatement] = { stmt.cfg .predecessors(stmt.index) .toChain .map { index ⇒ - NewJavaStatement(stmt.method, index, stmt.code, stmt.cfg) + JavaStatement(stmt.method, index, stmt.code, stmt.cfg) } .toList .asJava } - override def getSuccsOf(stmt: NewJavaStatement): JList[NewJavaStatement] = { + override def getSuccsOf(stmt: JavaStatement): JList[JavaStatement] = { stmt.cfg .successors(stmt.index) .toChain .map { index ⇒ - NewJavaStatement(stmt.method, index, stmt.code, stmt.cfg) + JavaStatement(stmt.method, index, stmt.code, stmt.cfg) } .toList .asJava } - override def getCalleesOfCallAt(callInstr: NewJavaStatement): JCollection[Method] = { + override def getCalleesOfCallAt(callInstr: JavaStatement): JCollection[Method] = { val declaredMethod = declaredMethods(callInstr.method) val FinalEP(_, callees) = ps(declaredMethod, Callees.key) callees @@ -92,7 +92,7 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[NewJava .asJava } - override def getCallersOf(m: Method): JCollection[NewJavaStatement] = { + override def getCallersOf(m: Method): JCollection[JavaStatement] = { val declaredMethod = declaredMethods(m) val FinalEP(_, callers) = ps(declaredMethod, Callers.key) callers @@ -101,31 +101,31 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[NewJava case (method, pc, true) ⇒ val TACode(_, code, pcToIndex, cfg, _) = tacai(method.definedMethod) val index = pcToIndex(pc) - Some(NewJavaStatement(method.definedMethod, index, code, cfg)) + Some(JavaStatement(method.definedMethod, index, code, cfg)) case _ ⇒ None } .toSet .asJava } - override def getCallsFromWithin(m: Method): JSet[NewJavaStatement] = { + override def getCallsFromWithin(m: Method): JSet[JavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) code.zipWithIndex .collect { - case (mc: MethodCall[V], index) ⇒ NewJavaStatement(m, index, code, cfg) + case (mc: MethodCall[V], index) ⇒ JavaStatement(m, index, code, cfg) case (as @ Assignment(_, _, _: FunctionCall[V]), index) ⇒ - NewJavaStatement(m, index, code, cfg) + JavaStatement(m, index, code, cfg) case (ex @ ExprStmt(_, _: FunctionCall[V]), index) ⇒ - NewJavaStatement(m, index, code, cfg) + JavaStatement(m, index, code, cfg) } .toSet .asJava } - override def getReturnSitesOfCallAt(callInstr: NewJavaStatement): JCollection[NewJavaStatement] = + override def getReturnSitesOfCallAt(callInstr: JavaStatement): JCollection[JavaStatement] = getSuccsOf(callInstr) - override def isCallStmt(stmt: NewJavaStatement): Boolean = { + override def isCallStmt(stmt: JavaStatement): Boolean = { def isCallExpr(expr: Expr[V]) = expr.astID match { case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ true @@ -141,8 +141,8 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[NewJava } } - override def isFallThroughSuccessor(stmt: NewJavaStatement, succ: NewJavaStatement): Boolean = ??? + override def isFallThroughSuccessor(stmt: JavaStatement, succ: JavaStatement): Boolean = ??? - override def isBranchTarget(stmt: NewJavaStatement, succ: NewJavaStatement): Boolean = ??? + override def isBranchTarget(stmt: JavaStatement, succ: JavaStatement): Boolean = ??? } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index 96705906b2..b78debe065 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -1,17 +1,18 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.taint -import java.io.File -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.EPS -import org.opalj.fpcf.FinalEP -import org.opalj.br.{DeclaredMethod, DefinedMethod, Method} import org.opalj.br.analyses.SomeProject +import org.opalj.br.{DeclaredMethod, DefinedMethod, Method} +import org.opalj.fpcf.{EPS, FinalEP, PropertyStore} import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.{AbsractIFDSAnalysisRunner, AbstractIFDSAnalysis, BackwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement, UnbalancedReturnFact} -import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, BackwardTaintProblem, Fact, FlowFact, InstanceField, Variable} -import org.opalj.tac.fpcf.properties.Taint +import org.opalj.tac.fpcf.analyses.ifds.JavaMethod +import org.opalj.tac.fpcf.analyses.ifds.old.taint.BackwardTaintProblem +import org.opalj.tac.fpcf.analyses.ifds.old._ +import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.properties.OldTaint + +import java.io.File /** * A backward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, @@ -20,7 +21,7 @@ import org.opalj.tac.fpcf.properties.Taint * @author Mario Trageser */ class BackwardClassForNameTaintAnalysisScheduler private (implicit val project: SomeProject) - extends BackwardIFDSAnalysis(new BackwardClassForNameTaintProblem(project), Taint) + extends BackwardIFDSAnalysis(new BackwardClassForNameTaintProblem(project), OldTaint) class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProblem(p) { @@ -42,7 +43,7 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * Do not perform unbalanced return for methods, which can be called from outside the library. @@ -58,7 +59,7 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb * This analysis does not create FlowFacts at calls. * Instead, FlowFacts are created at the start node of methods. */ - override protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], + override protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[Fact], source: (DeclaredMethod, Fact)): Option[FlowFact] = None /** @@ -98,7 +99,7 @@ object BackwardClassForNameTaintAnalysisScheduler extends IFDSAnalysisScheduler[ new BackwardClassForNameTaintAnalysisScheduler()(p) } - override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint } class BackwardClassForNameTaintAnalysisRunner extends AbsractIFDSAnalysisRunner { @@ -111,7 +112,7 @@ class BackwardClassForNameTaintAnalysisRunner extends AbsractIFDSAnalysisRunner case EPS((m: DefinedMethod, inputFact)) if canBeCalledFromOutside(m, ps) ⇒ (m, inputFact) }.flatMap(ps(_, propertyKey) match { - case FinalEP(_, Taint(result)) ⇒ + case FinalEP(_, OldTaint(result)) ⇒ result.values.fold(Set.empty)((acc, facts) ⇒ acc ++ facts).filter { case FlowFact(_) ⇒ true case _ ⇒ false diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala index e840946bf3..19f0a4f9b8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -6,9 +6,10 @@ import org.opalj.br.{DeclaredMethod, Method, ObjectType} import org.opalj.fpcf.PropertyStore import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.analyses.ifds.old._ +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, Variable} import org.opalj.tac.fpcf.analyses.ifds._ -import org.opalj.tac.fpcf.properties.Taint +import org.opalj.tac.fpcf.properties.OldTaint import java.io.File @@ -21,10 +22,10 @@ import java.io.File * @author Michael Eichberg */ class ForwardClassForNameTaintAnalysis$Scheduler private (implicit val project: SomeProject) - extends ForwardIFDSAnalysis(new ForwardClassForNameTaintProblem(project), Taint) + extends ForwardIFDSAnalysis(new ForwardClassForNameTaintProblem(project), OldTaint) class ForwardClassForNameTaintProblem(project: SomeProject) - extends ForwardTaintProblem(project) with TaintProblem[DeclaredMethod, JavaStatement, Fact] { + extends old.taint.ForwardTaintProblem(project) with old.taint.TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, Fact] { /** * The string parameters of all public methods are entry points. @@ -45,19 +46,19 @@ class ForwardClassForNameTaintProblem(project: SomeProject) /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * This analysis does not create new taints on the fly. * Instead, the string parameters of all public methods are tainted in the entry points. */ - override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = + override protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[Fact] = Set.empty /** * Create a FlowFact, if Class.forName is called with a tainted variable for the first parameter. */ - override protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, + override protected def createFlowFact(callee: DeclaredMethod, call: DeclaredMethodJavaStatement, in: Set[Fact]): Option[FlowFact] = if (isClassForName(callee) && in.contains(Variable(-2))) Some(FlowFact(Seq(JavaMethod(call.method)))) @@ -91,7 +92,7 @@ object ForwardClassForNameTaintAnalysis$Scheduler extends IFDSAnalysisScheduler[ new ForwardClassForNameTaintAnalysis$Scheduler()(p) } - override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint } class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { @@ -102,7 +103,7 @@ class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { for { e ← analysis.ifdsProblem.entryPoints flows = ps(e, ForwardClassForNameTaintAnalysis$Scheduler.property.key) - fact ← flows.ub.asInstanceOf[IFDSProperty[JavaStatement, Fact]].flows.values.flatten.toSet[Fact] + fact ← flows.ub.asInstanceOf[IFDSProperty[DeclaredMethodJavaStatement, Fact]].flows.values.flatten.toSet[Fact] } { fact match { case FlowFact(flow) ⇒ println(s"flow: "+flow.asInstanceOf[Set[Method]].map(_.toJava).mkString(", ")) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala index 0ac3ad52f3..2506e8b9f4 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala @@ -1,18 +1,17 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.ifds -import java.net.URL - -import org.opalj.fpcf.properties.taint.ForwardFlowPath -import org.opalj.fpcf.PropertiesTest -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.Project import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.Project +import org.opalj.fpcf.PropertiesTest +import org.opalj.fpcf.properties.taint.ForwardFlowPath import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintAnalysisFixtureScheduler import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact +import java.net.URL + /** * @author Mario Trageser */ @@ -33,10 +32,9 @@ class ForwardTaintAnalysisFixtureTest extends PropertiesTest { describe("Test the ForwardFlowPath annotations") { val testContext = executeAnalyses(ForwardTaintAnalysisFixtureScheduler) val project = testContext.project - val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { - case (methods, entityString, annotations) ⇒ - ((declaredMethods(methods), NullFact), entityString, annotations) + case (method, entityString, annotations) ⇒ + ((method, NullFact), entityString, annotations) } testContext.propertyStore.shutdown() validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala similarity index 87% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala index 3a45a84544..4014747b21 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/BackwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala @@ -1,17 +1,16 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.ifds +package org.opalj.fpcf.ifds.old -import java.net.URL - -import org.opalj.fpcf.properties.taint.BackwardFlowPath -import org.opalj.fpcf.PropertiesTest -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.Project import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.{DeclaredMethodsKey, Project} +import org.opalj.fpcf.PropertiesTest +import org.opalj.fpcf.properties.taint.BackwardFlowPath import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.taint.BackwardTaintAnalysisFixtureScheduler import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact +import org.opalj.tac.fpcf.analyses.ifds.taint.old.BackwardTaintAnalysisFixtureScheduler + +import java.net.URL /** * @author Mario Trageser diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala similarity index 64% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala index f99c3468d0..735412faa1 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/NewForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala @@ -1,20 +1,21 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.ifds +package org.opalj.fpcf.ifds.old import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.br.analyses.Project +import org.opalj.br.analyses.{DeclaredMethodsKey, Project} import org.opalj.fpcf.PropertiesTest import org.opalj.fpcf.properties.taint.ForwardFlowPath import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.taint.{NewForwardTaintAnalysisFixtureScheduler, NullFact} +import org.opalj.tac.fpcf.analyses.ifds.taint.old.{ForwardTaintAnalysisFixtureScheduler} +import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact import java.net.URL /** * @author Mario Trageser */ -class NewForwardTaintAnalysisFixtureTest extends PropertiesTest { +class ForwardTaintAnalysisFixtureTest extends PropertiesTest { override def init(p: Project[URL]): Unit = { p.updateProjectInformationKeyInitializationData( @@ -29,11 +30,12 @@ class NewForwardTaintAnalysisFixtureTest extends PropertiesTest { } describe("Test the ForwardFlowPath annotations") { - val testContext = executeAnalyses(NewForwardTaintAnalysisFixtureScheduler) + val testContext = executeAnalyses(ForwardTaintAnalysisFixtureScheduler) val project = testContext.project + val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { - case (method, entityString, annotations) ⇒ - ((method, NullFact), entityString, annotations) + case (methods, entityString, annotations) ⇒ + ((declaredMethods(methods), NullFact), entityString, annotations) } testContext.propertyStore.shutdown() validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/VTATest.scala similarity index 79% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/VTATest.scala index 6fd9924ae1..c61d0cc7db 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/VTATest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/VTATest.scala @@ -1,18 +1,15 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.fpcf.ifds +package org.opalj.fpcf.ifds.old -import java.net.URL - -import org.opalj.fpcf.PropertiesTest -import org.opalj.fpcf.properties.vta.ExpectedCallee -import org.opalj.fpcf.properties.vta.ExpectedType -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.Project import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.{DeclaredMethodsKey, Project} +import org.opalj.fpcf.PropertiesTest +import org.opalj.fpcf.properties.vta.{ExpectedCallee, ExpectedType} import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ifds.VTANullFact +import org.opalj.tac.fpcf.analyses.ifds.old.{IFDSBasedVariableTypeAnalysisScheduler, VTANullFact} + +import java.net.URL class VTATest extends PropertiesTest { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala index c784646fd5..2d8b42ae1e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala @@ -1,13 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.fpcf.properties.taint +import org.opalj.br._ import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br._ -import org.opalj.fpcf.{EPS, Entity, FinalEP, Property} import org.opalj.fpcf.properties.AbstractPropertyMatcher -import org.opalj.tac.fpcf.analyses.ifds.taint.{BackwardTaintAnalysisFixtureScheduler, Fact, FlowFact} -import org.opalj.tac.fpcf.properties.Taint +import org.opalj.fpcf.{EPS, Entity, FinalEP, Property} +import org.opalj.tac.fpcf.analyses.ifds.taint.old.BackwardTaintAnalysisFixtureScheduler +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact} +import org.opalj.tac.fpcf.properties.OldTaint /** * @author Mario Trageser @@ -32,7 +33,7 @@ class BackwardFlowPathMatcher extends AbstractPropertyMatcher { case EPS((m: DefinedMethod, inputFact)) if m.definedMethod == method ⇒ (m, inputFact) }.flatMap(propertyStore(_, propertyKey) match { - case FinalEP(_, Taint(result)) ⇒ + case FinalEP(_, OldTaint(result)) ⇒ result.values.fold(Set.empty)((acc, facts) ⇒ acc ++ facts).collect { case FlowFact(methods) ⇒ methods.map(_.name) } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala index aaf37d7a27..e22b072a83 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala @@ -14,9 +14,7 @@ import org.opalj.br.Method import org.opalj.tac.DUVar import org.opalj.tac.TACMethodParameter import org.opalj.tac.TACode -import org.opalj.tac.fpcf.analyses.ifds.CalleeType -import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisScheduler -import org.opalj.tac.fpcf.analyses.ifds.VTAResult +import org.opalj.tac.fpcf.analyses.ifds.old.{CalleeType, IFDSBasedVariableTypeAnalysisScheduler, VTAResult} class ExpectedCalleeMatcher extends VTAMatcher { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala index 3bfedf76c5..697db17850 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala @@ -10,8 +10,7 @@ import org.opalj.br.Method import org.opalj.tac.DUVar import org.opalj.tac.TACMethodParameter import org.opalj.tac.TACode -import org.opalj.tac.fpcf.analyses.ifds.VariableType -import org.opalj.tac.fpcf.analyses.ifds.VTAResult +import org.opalj.tac.fpcf.analyses.ifds.old.{VTAResult, VariableType} class ExpectedTypeMatcher extends VTAMatcher { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala index 5f3d238a4e..9d2e658ccc 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala @@ -19,7 +19,7 @@ import org.opalj.tac.fpcf.properties.TheTACAI import org.opalj.tac.DUVar import org.opalj.tac.TACMethodParameter import org.opalj.tac.TACode -import org.opalj.tac.fpcf.analyses.ifds.VTAFact +import org.opalj.tac.fpcf.analyses.ifds.old.VTAFact abstract class VTAMatcher extends AbstractPropertyMatcher { diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index 02053474f8..d285f45118 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -5,9 +5,9 @@ import org.opalj.br.analyses.{DeclaredMethodsKey, Project} import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler} import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact} import org.opalj.ifds.IFDSProperty +import org.opalj.tac.fpcf.analyses.ifds.old.DeclaredMethodJavaStatement import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers @@ -32,7 +32,7 @@ class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers println("---METHOD: "+method.toJava+" ---") for { fact ← flows.ub - .asInstanceOf[IFDSProperty[JavaStatement, Fact]] + .asInstanceOf[IFDSProperty[DeclaredMethodJavaStatement, Fact]] .flows .values .flatten diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 2f0150aef9..0cc536787f 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -8,10 +8,11 @@ import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.ll.LLVMProjectKey import org.opalj.ll.fpcf.properties.NativeTaint import org.opalj.tac.fpcf.analyses.ifds.taint._ -import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement} -import org.opalj.tac.fpcf.properties.{TACAI, Taint} +import org.opalj.tac.fpcf.analyses.ifds.JavaMethod +import org.opalj.tac.fpcf.analyses.ifds.old.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, DeclaredMethodJavaStatement, taint} +import org.opalj.tac.fpcf.properties.{TACAI, OldTaint} -class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { +class SimpleJavaForwardTaintProblem(p: SomeProject) extends taint.ForwardTaintProblem(p) { val llvmProject = p.get(LLVMProjectKey) /** @@ -31,12 +32,12 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( /** * We do not sanitize parameters. */ - override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * Creates a new variable fact for the callee, if the source was called. */ - override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = + override protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[Fact] = if (callee.name == "source") Set(Variable(call.index)) else Set.empty @@ -46,7 +47,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( */ override protected def createFlowFact( callee: DeclaredMethod, - call: JavaStatement, + call: DeclaredMethodJavaStatement, in: Set[Fact] ): Option[FlowFact] = if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(JavaMethod(call.method)))) @@ -55,7 +56,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( // Multilingual additions here override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = { - def handleNativeMethod(call: JavaStatement, successor: JavaStatement, in: Set[Fact]): Set[Fact] = { + def handleNativeMethod(call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = { //val method = callee.definedMethod // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names @@ -74,11 +75,11 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( } class SimpleJavaForwardTaintAnalysis(implicit val project: SomeProject) - extends ForwardIFDSAnalysis(new SimpleJavaForwardTaintProblem(project), Taint) + extends ForwardIFDSAnalysis(new SimpleJavaForwardTaintProblem(project), OldTaint) object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact] { override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis()(p) - override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq(LLVMProjectKey) override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.ub(NativeTaint)) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index a5ba027553..f83212d1ec 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -4,9 +4,9 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeIFDSProblem} import org.opalj.ll.llvm.{Function, PHI} -import org.opalj.tac.fpcf.analyses.ifds.taint.NewTaintProblem +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with NewTaintProblem[Function, LLVMStatement, NativeFact] { +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[Function, LLVMStatement, NativeFact] { override def nullFact: NativeFact = NativeNullFact /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala index d6392889ac..4d22e7721a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala @@ -4,7 +4,7 @@ package org.opalj.tac.fpcf.analyses.ifds import org.opalj.br.Method import org.opalj.ifds.Callable -case class JavaMethod(val method: Method) extends Callable { +case class JavaMethod(method: Method) extends Callable { override def name(): String = method.name override def signature(): String = method.toJava } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala index 0ca5d9a4dc..cc7d9296d8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala @@ -1,67 +1,34 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds -import org.opalj.br.DeclaredMethod -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.cfg.CFGNode +import org.opalj.br.{DeclaredMethod, Method} +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.{FinalEP, PropertyStore} -import org.opalj.ifds.AbstractIFDSFact -import org.opalj.ifds.old.ICFG +import org.opalj.ifds.{AbstractIFDSFact, ICFG} +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.{Assignment, DUVar, Expr, ExprStmt, LazyDetachedTACAIKey, NonVirtualFunctionCall, NonVirtualMethodCall, StaticFunctionCall, StaticMethodCall, Stmt, TACMethodParameter, TACode, VirtualFunctionCall, VirtualMethodCall} import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.properties.cg.Callees +import org.opalj.value.ValueInformation -class ForwardICFG[IFDSFact <: AbstractIFDSFact](implicit - propertyStore: PropertyStore, - typeProvider: TypeProvider, - declaredMethods: DeclaredMethods -) extends ICFG[IFDSFact, DeclaredMethod, JavaStatement, CFGNode] { - /** - * Determines the basic blocks, at which the analysis starts. - * - * @param sourceFact The source fact of the analysis. - * @param callable The analyzed callable. - * @return The basic blocks, at which the analysis starts. - */ - override def startNodes(sourceFact: IFDSFact, callable: DeclaredMethod): Set[CFGNode] = ??? - - /** - * Determines the nodes, that will be analyzed after some `basicBlock`. - * - * @param node The basic block, that was analyzed before. - * @return The nodes, that will be analyzed after `basicBlock`. - */ - override def nextNodes(node: CFGNode): Set[CFGNode] = ??? - - /** - * Checks, if some `node` is the last node. - * - * @return True, if `node` is the last node, i.e. there is no next node. - */ - override def isLastNode(node: CFGNode): Boolean = ??? - - /** - * Determines the first index of some `basic block`, that will be analyzed. - * - * @param basicBlock The basic block. - * @return The first index of some `basic block`, that will be analyzed. - */ - override def firstStatement(basicBlock: CFGNode): JavaStatement = ??? - - /** - * Determines the last index of some `basic block`, that will be analzyed. - * - * @param basicBlock The basic block. - * @return The last index of some `basic block`, that will be analzyed. - */ - override def lastStatement(basicBlock: CFGNode): JavaStatement = ??? +class ForwardICFG[IFDSFact <: AbstractIFDSFact](implicit project: SomeProject) + extends ICFG[IFDSFact, Method, JavaStatement] { + val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) + val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) /** - * Determines the statement that will be analyzed after some other statement. + * Determines the statements at which the analysis starts. * - * @param statement The current statement. - * @return The statement that will be analyzed after `statement`. + * @param callable The analyzed callable. + * @return The statements at which the analysis starts. */ - override def nextStatement(statement: JavaStatement): JavaStatement = ??? + override def startStatements(callable: Method): Set[JavaStatement] = { + val TACode(_, code, _, cfg, _) = tacai(callable) + Set(JavaStatement(callable, 0, code, cfg)) + } /** * Determines the statement, that will be analyzed after some other `statement`. @@ -69,7 +36,13 @@ class ForwardICFG[IFDSFact <: AbstractIFDSFact](implicit * @param statement The source statement. * @return The successor statements */ - override def nextStatements(statement: JavaStatement): Set[JavaStatement] = ??? + override def nextStatements(statement: JavaStatement): Set[JavaStatement] = { + statement.cfg + .successors(statement.index) + .toChain + .map { index ⇒ JavaStatement(statement, index) } + .toSet + } /** * Gets the set of all methods possibly called at some statement. @@ -78,18 +51,68 @@ class ForwardICFG[IFDSFact <: AbstractIFDSFact](implicit * @return All callables possibly called at the statement or None, if the statement does not * contain a call. */ - override def getCalleesIfCallStatement(statement: JavaStatement): Option[collection.Set[DeclaredMethod]] = { + override def getCalleesIfCallStatement(statement: JavaStatement): Option[collection.Set[Method]] = + statement.stmt.astID match { + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + Some(getCallees(statement)) + case Assignment.ASTID | ExprStmt.ASTID ⇒ + getExpression(statement.stmt).astID match { + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + Some(getCallees(statement)) + case _ ⇒ None + } + case _ ⇒ None + } + + /** + * Retrieves the expression of an assignment or expression statement. + * + * @param statement The statement. Must be an Assignment or ExprStmt. + * @return The statement's expression. + */ + private def getExpression(statement: Stmt[_]): Expr[_] = statement.astID match { + case Assignment.ASTID ⇒ statement.asAssignment.expr + case ExprStmt.ASTID ⇒ statement.asExprStmt.expr + case _ ⇒ throw new UnknownError("Unexpected statement") + } + + private def getCallees(statement: JavaStatement): collection.Set[Method] = { val pc = statement.code(statement.index).pc - val caller = declaredMethods(statement.method) + val caller = declaredMethods(statement.callable) val ep = propertyStore(caller, Callees.key) ep match { - case FinalEP(_, p) ⇒ Some(p.directCallees(typeProvider.newContext(caller), pc).map(_.method).toSet) + case FinalEP(_, p) ⇒ definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method)) case _ ⇒ throw new IllegalStateException( - "call graph mut be computed before the analysis starts" + "call graph must be computed before the analysis starts" ) } } - override def isExitStatement(statement: JavaStatement): Boolean = ??? + override def isExitStatement(statement: JavaStatement): Boolean = { + statement.index == statement.node.asBasicBlock.endPC && + statement.node.successors.exists(_.isExitNode) + } + + /** + * Maps some declared methods to their defined methods. + * + * @param declaredMethods Some declared methods. + * @return All defined methods of `declaredMethods`. + */ + private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): collection.Set[Method] = { + val result = scala.collection.mutable.Set.empty[Method] + declaredMethods + .filter( + declaredMethod ⇒ + declaredMethod.hasSingleDefinedMethod || + declaredMethod.hasMultipleDefinedMethods + ) + .foreach( + declaredMethod ⇒ + declaredMethod + .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) + ) + result + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index f1858d1cfc..69f32f5d72 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -1,56 +1,51 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds -import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} -import org.opalj.br.cfg.CFGNode -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.{DeclaredMethod, ObjectType} -import org.opalj.fpcf._ -import org.opalj.ifds.old.IFDSProblem -import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} -import org.opalj.tac.cg.TypeProviderKey -import org.opalj.tac.fpcf.analyses.cg.TypeProvider -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt} +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.br.cfg.{CFG, CFGNode} +import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, Statement} +import org.opalj.tac.{Assignment, Call, DUVar, ExprStmt, Stmt, TACStmts} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.value.ValueInformation -abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, DeclaredMethod, JavaStatement, CFGNode]( - new ForwardICFG[Fact]()(project.get(PropertyStoreKey), project.get(TypeProviderKey), project.get(DeclaredMethodsKey)) -) { - /** - * All declared methods in the project. - */ - implicit final protected val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) +/** + * A statement that is passed to the concrete analysis. + * + * @param method The method containing the statement. + * @param node The basic block containing the statement. + * @param stmt The TAC statement. + * @param index The index of the Statement in the code. + * @param code The method's TAC code. + * @param cfg The method's CFG. + */ +case class JavaStatement( + method: Method, + index: Int, + code: Array[Stmt[V]], + cfg: CFG[Stmt[V], TACStmts[V]] +) extends Statement[Method, CFGNode] { - final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) - implicit final protected val typeProvider: TypeProvider = project.get(TypeProviderKey) + override def hashCode(): Int = method.hashCode() * 31 + index - override def outsideAnalysisContext(callee: DeclaredMethod): Option[(JavaStatement, JavaStatement, Set[Fact]) ⇒ Set[Fact]] = callee.definedMethod.body.isDefined match { - case true ⇒ None - case false ⇒ Some((_call: JavaStatement, _successor: JavaStatement, in: Set[Fact]) ⇒ in) + override def equals(o: Any): Boolean = o match { + case s: JavaStatement ⇒ s.index == index && s.method == method + case _ ⇒ false } - /** - * Returns all methods, that can be called from outside the library. - * The call graph must be computed, before this method may be invoked. - * - * @return All methods, that can be called from outside the library. - */ - protected def methodsCallableFromOutside: Set[DeclaredMethod] = - declaredMethods.declaredMethods.filter(canBeCalledFromOutside).toSet + override def toString: String = s"${method.signatureToJava(false)}[${index}]\n\t${stmt}\n\t${method.toJava}" + override def callable(): Method = method + override def node(): CFGNode = cfg.bb(index) + def stmt: Stmt[V] = code(index) +} - /** - * Checks, if some `method` can be called from outside the library. - * The call graph must be computed, before this method may be invoked. - * - * @param method The method, which may be callable from outside. - * @return True, if `method` can be called from outside the library. - */ - protected def canBeCalledFromOutside(method: DeclaredMethod): Boolean = { - val FinalEP(_, callers) = propertyStore(method, Callers.key) - callers.hasCallersWithUnknownContext - } +object JavaStatement { + def apply(referenceStatement: JavaStatement, newIndex: Int): JavaStatement = + JavaStatement(referenceStatement.method, newIndex, referenceStatement.code, referenceStatement.cfg) +} +abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) + extends IFDSProblem[Fact, Method, JavaStatement](new ForwardICFG[Fact]()(project)) { /** * Gets the call object for a statement that contains a call. * @@ -63,47 +58,15 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e case _ ⇒ call.asMethodCall } - override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): Option[ProperPropertyComputationResult] = { - val declaredMethod = source._1 - val method = declaredMethod.definedMethod - val declaringClass: ObjectType = method.classFile.thisType - /* - * If this is not the method's declaration, but a non-overwritten method in a subtype, do - * not re-analyze the code. - */ - if (declaringClass ne declaredMethod.declaringClassType) Some(baseMethodResult(source, propertyKey)) - super.specialCase(source, propertyKey) - } - - /** - * This method will be called if a non-overwritten declared method in a sub type shall be - * analyzed. Analyzes the defined method of the supertype instead. - * - * @param source A pair consisting of the declared method of the subtype and an input fact. - * @return The result of the analysis of the defined method of the supertype. - */ - private def baseMethodResult(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[JavaStatement, Fact]): ProperPropertyComputationResult = { - - def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { - case FinalP(p) ⇒ Result(source, p) - - case ep @ InterimUBP(ub: Property) ⇒ - InterimResult.forUB(source, ub, Set(ep), c) - - case epk ⇒ - InterimResult.forUB(source, propertyKey.create(Map.empty), Set(epk), c) - } - c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) + override def outsideAnalysisContext(callee: Method): Option[(JavaStatement, JavaStatement, Fact) ⇒ Set[Fact]] = callee.body.isDefined match { + case true ⇒ None + case false ⇒ Some((_call: JavaStatement, _successor: JavaStatement, in: Fact) ⇒ Set(in)) } } -abstract class JavaBackwardIFDSProblem[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](project: SomeProject) extends JavaIFDSProblem[IFDSFact](project) with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement] { +object JavaIFDSProblem { /** - * Checks for the analyzed entity, if an unbalanced return should be performed. - * - * @param source The analyzed entity. - * @return False, if no unbalanced return should be performed. + * The type of the TAC domain. */ - def shouldPerformUnbalancedReturn(source: (DeclaredMethod, IFDSFact)): Boolean = - source._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]] || entryPoints.contains(source) + type V = DUVar[ValueInformation] } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala deleted file mode 100644 index fd5b85a16b..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewForwardICFG.scala +++ /dev/null @@ -1,118 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds - -import org.opalj.br.{DeclaredMethod, Method} -import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.fpcf.{FinalEP, PropertyStore} -import org.opalj.ifds.{AbstractIFDSFact, ICFG} -import org.opalj.tac.cg.TypeProviderKey -import org.opalj.tac.{Assignment, DUVar, Expr, ExprStmt, LazyDetachedTACAIKey, NonVirtualFunctionCall, NonVirtualMethodCall, StaticFunctionCall, StaticMethodCall, Stmt, TACMethodParameter, TACode, VirtualFunctionCall, VirtualMethodCall} -import org.opalj.tac.fpcf.analyses.cg.TypeProvider -import org.opalj.tac.fpcf.properties.cg.Callees -import org.opalj.value.ValueInformation - -class NewForwardICFG[IFDSFact <: AbstractIFDSFact](implicit project: SomeProject) - extends ICFG[IFDSFact, Method, NewJavaStatement] { - val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) - val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) - implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) - implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) - - /** - * Determines the statements at which the analysis starts. - * - * @param callable The analyzed callable. - * @return The statements at which the analysis starts. - */ - override def startStatements(callable: Method): Set[NewJavaStatement] = { - val TACode(_, code, _, cfg, _) = tacai(callable) - Set(NewJavaStatement(callable, 0, code, cfg)) - } - - /** - * Determines the statement, that will be analyzed after some other `statement`. - * - * @param statement The source statement. - * @return The successor statements - */ - override def nextStatements(statement: NewJavaStatement): Set[NewJavaStatement] = { - statement.cfg - .successors(statement.index) - .toChain - .map { index ⇒ NewJavaStatement(statement, index) } - .toSet - } - - /** - * Gets the set of all methods possibly called at some statement. - * - * @param statement The statement. - * @return All callables possibly called at the statement or None, if the statement does not - * contain a call. - */ - override def getCalleesIfCallStatement(statement: NewJavaStatement): Option[collection.Set[Method]] = - statement.stmt.astID match { - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ - Some(getCallees(statement)) - case Assignment.ASTID | ExprStmt.ASTID ⇒ - getExpression(statement.stmt).astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ - Some(getCallees(statement)) - case _ ⇒ None - } - case _ ⇒ None - } - - /** - * Retrieves the expression of an assignment or expression statement. - * - * @param statement The statement. Must be an Assignment or ExprStmt. - * @return The statement's expression. - */ - private def getExpression(statement: Stmt[_]): Expr[_] = statement.astID match { - case Assignment.ASTID ⇒ statement.asAssignment.expr - case ExprStmt.ASTID ⇒ statement.asExprStmt.expr - case _ ⇒ throw new UnknownError("Unexpected statement") - } - - private def getCallees(statement: NewJavaStatement): collection.Set[Method] = { - val pc = statement.code(statement.index).pc - val caller = declaredMethods(statement.callable) - val ep = propertyStore(caller, Callees.key) - ep match { - case FinalEP(_, p) ⇒ definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method)) - case _ ⇒ - throw new IllegalStateException( - "call graph must be computed before the analysis starts" - ) - } - } - - override def isExitStatement(statement: NewJavaStatement): Boolean = { - statement.index == statement.node.asBasicBlock.endPC && - statement.node.successors.exists(_.isExitNode) - } - - /** - * Maps some declared methods to their defined methods. - * - * @param declaredMethods Some declared methods. - * @return All defined methods of `declaredMethods`. - */ - private def definedMethods(declaredMethods: Iterator[DeclaredMethod]): collection.Set[Method] = { - val result = scala.collection.mutable.Set.empty[Method] - declaredMethods - .filter( - declaredMethod ⇒ - declaredMethod.hasSingleDefinedMethod || - declaredMethod.hasMultipleDefinedMethods - ) - .foreach( - declaredMethod ⇒ - declaredMethod - .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) - ) - result - } -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala deleted file mode 100644 index ce49168071..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/NewJavaIFDSProblem.scala +++ /dev/null @@ -1,65 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds - -import org.opalj.br.Method -import org.opalj.br.analyses.SomeProject -import org.opalj.br.cfg.{CFG, CFGNode} -import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, Statement} -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt, TACStmts} - -/** - * A statement that is passed to the concrete analysis. - * - * @param method The method containing the statement. - * @param node The basic block containing the statement. - * @param stmt The TAC statement. - * @param index The index of the Statement in the code. - * @param code The method's TAC code. - * @param cfg The method's CFG. - */ -case class NewJavaStatement( - method: Method, - index: Int, - code: Array[Stmt[V]], - cfg: CFG[Stmt[V], TACStmts[V]] -) extends Statement[Method, CFGNode] { - - override def hashCode(): Int = method.hashCode() * 31 + index - - override def equals(o: Any): Boolean = o match { - case s: NewJavaStatement ⇒ s.index == index && s.method == method - case _ ⇒ false - } - - override def toString: String = s"${method.signatureToJava(false)}[${index}]\n\t${stmt}\n\t${method.toJava}" - override def callable(): Method = method - override def node(): CFGNode = cfg.bb(index) - def stmt: Stmt[V] = code(index) -} - -object NewJavaStatement { - def apply(oldStatement: NewJavaStatement, newIndex: Int): NewJavaStatement = - NewJavaStatement(oldStatement.method, newIndex, oldStatement.code, oldStatement.cfg) -} - -abstract class NewJavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) - extends IFDSProblem[Fact, Method, NewJavaStatement](new NewForwardICFG[Fact]()(project)) { - /** - * Gets the call object for a statement that contains a call. - * - * @param call The call statement. - * @return The call object for `call`. - */ - protected def asCall(call: Stmt[V]): Call[V] = call.astID match { - case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall - case _ ⇒ call.asMethodCall - } - - override def outsideAnalysisContext(callee: Method): Option[(NewJavaStatement, NewJavaStatement, Fact) ⇒ Set[Fact]] = callee.body.isDefined match { - case true ⇒ None - case false ⇒ Some((_call: NewJavaStatement, _successor: NewJavaStatement, in: Fact) ⇒ Set(in)) - } -} - diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala similarity index 92% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala index 6fc4ba891d..904a261b22 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala @@ -1,30 +1,28 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package tac -package fpcf -package analyses -package ifds +package org.opalj.tac.fpcf.analyses.ifds.old import com.typesafe.config.ConfigValueFactory import org.opalj.ai.domain.l0.PrimitiveTACAIDomain import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey -import org.opalj.br.{DeclaredMethod, DefinedMethod, Method, ObjectType} import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, Project, ProjectInformationKeys, SomeProject} import org.opalj.br.cfg.{BasicBlock, CFG, CFGNode} import org.opalj.br.fpcf.{FPCFAnalysesManagerKey, FPCFAnalysis, FPCFLazyAnalysisScheduler, PropertyStoreKey} -import org.opalj.fpcf.seq.PKESequentialPropertyStore +import org.opalj.br.{DeclaredMethod, DefinedMethod, Method, ObjectType} +import org.opalj.bytecode import org.opalj.fpcf._ +import org.opalj.fpcf.seq.PKESequentialPropertyStore import org.opalj.ifds.old.{IFDSProblem, NumberOfCalls, Subsumable} import org.opalj.ifds.{AbstractIFDSFact, IFDSProperty, IFDSPropertyMetaInformation, Statement} import org.opalj.tac.cg.{RTACallGraphKey, TypeProviderKey} import org.opalj.tac.fpcf.analyses.cg.TypeProvider -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.properties.{TACAI, TheTACAI} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.{JavaStatement, old} import org.opalj.tac.fpcf.properties.cg.{Callees, Callers} +import org.opalj.tac.fpcf.properties.{TACAI, TheTACAI} +import org.opalj.tac._ import org.opalj.util.Milliseconds import org.opalj.util.PerformanceEvaluation.time -import org.opalj.value.ValueInformation import java.io.{File, PrintWriter} import javax.swing.JOptionPane @@ -37,10 +35,10 @@ import scala.collection.{mutable, Set ⇒ SomeSet} * @tparam IFDSFact */ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( - val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement, CFGNode], - val propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact] + val ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, DeclaredMethodJavaStatement, CFGNode], + val propertyKey: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, IFDSFact] ) extends FPCFAnalysis - with Subsumable[JavaStatement, IFDSFact] { + with Subsumable[DeclaredMethodJavaStatement, IFDSFact] { /** * All declared methods in the project. @@ -88,12 +86,12 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( val code: Array[Stmt[V]], val cfg: CFG[Stmt[V], TACStmts[V]], var pendingIfdsCallSites: Map[(DeclaredMethod, IFDSFact), Set[(BasicBlock, Int)]], - var pendingTacCallSites: Map[DeclaredMethod, Set[BasicBlock]] = Map.empty, - var pendingIfdsDependees: Map[(DeclaredMethod, IFDSFact), EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] = Map.empty, - var pendingTacDependees: Map[Method, EOptionP[Method, TACAI]] = Map.empty, - var pendingCgCallSites: Set[BasicBlock] = Set.empty, - var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, - var outgoingFacts: Map[BasicBlock, Map[CFGNode, Set[IFDSFact]]] = Map.empty + var pendingTacCallSites: Map[DeclaredMethod, Set[BasicBlock]] = Map.empty, + var pendingIfdsDependees: Map[(DeclaredMethod, IFDSFact), EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]] = Map.empty, + var pendingTacDependees: Map[Method, EOptionP[Method, TACAI]] = Map.empty, + var pendingCgCallSites: Set[BasicBlock] = Set.empty, + var incomingFacts: Map[BasicBlock, Set[IFDSFact]] = Map.empty, + var outgoingFacts: Map[BasicBlock, Map[CFGNode, Set[IFDSFact]]] = Map.empty ) /** @@ -111,7 +109,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @return A map, mapping from each predecessor of all exit nodes to the facts, which hold at * the exit node under the assumption that the predecessor was executed before. */ - protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] + protected def collectResult(implicit state: State): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] /** * Creates an IFDSProperty containing the result of this analysis. @@ -120,8 +118,8 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @return An IFDSProperty containing the `result`. */ protected def createPropertyValue( - result: Map[JavaStatement, Set[IFDSFact]] - ): IFDSProperty[JavaStatement, IFDSFact] = propertyKey.create(result) + result: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] + ): IFDSProperty[DeclaredMethodJavaStatement, IFDSFact] = propertyKey.create(result) /** * Determines the nodes, that will be analyzed after some `basicBlock`. @@ -177,7 +175,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param node The node. * @return The first statement of a node, that will be analyzed. */ - protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement + protected def firstStatement(node: CFGNode)(implicit state: State): DeclaredMethodJavaStatement /** * Determines the statement, that will be analyzed after some other `statement`. @@ -185,7 +183,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param statement The source statement. * @return The successor statements */ - protected def nextStatements(statement: JavaStatement)(implicit state: State): Set[JavaStatement] + protected def nextStatements(statement: DeclaredMethodJavaStatement)(implicit state: State): Set[DeclaredMethodJavaStatement] /** * Determines the facts, for which a `callee` is analyzed. @@ -195,7 +193,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param in The facts, which hold before the `call`. * @return The facts, for which `callee` will be analyzed. */ - protected def callToStartFacts(call: JavaStatement, callee: DeclaredMethod, in: Set[IFDSFact])( + protected def callToStartFacts(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, in: Set[IFDSFact])( implicit state: State ): Set[IFDSFact] @@ -212,12 +210,12 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @return The summary edges plus the exit to return facts for `callee` and `successor`. */ protected def addExitToReturnFacts( - summaryEdges: Map[JavaStatement, Set[IFDSFact]], - successors: Set[JavaStatement], - call: JavaStatement, + summaryEdges: Map[DeclaredMethodJavaStatement, Set[IFDSFact]], + successors: Set[DeclaredMethodJavaStatement], + call: DeclaredMethodJavaStatement, callee: DeclaredMethod, - exitFacts: Map[JavaStatement, Set[IFDSFact]] - )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] + exitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] + )(implicit state: State): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] /** * Performs an IFDS analysis for a method-fact-pair. @@ -311,7 +309,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( case interimEUBP @ InterimEUBP( e: (DeclaredMethod, IFDSFact) @unchecked, - ub: IFDSProperty[JavaStatement, IFDSFact] @unchecked + ub: IFDSProperty[DeclaredMethodJavaStatement, IFDSFact] @unchecked ) ⇒ if (ub.flows.values .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { @@ -319,7 +317,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( // Update the pendingIfdsDependee entry to the new interim result. state.pendingIfdsDependees += e -> interimEUBP - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]] } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) @@ -364,18 +362,18 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( */ protected def handleCall( basicBlock: BasicBlock, - call: JavaStatement, + call: DeclaredMethodJavaStatement, callees: SomeSet[Method], in: Set[IFDSFact], calleeWithUpdateFact: Option[IFDSFact] )( implicit state: State - ): Map[JavaStatement, Set[IFDSFact]] = { + ): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = { val successors = nextStatements(call) val inputFacts = beforeHandleCall(call, in) // Facts valid at the start of each successor - var summaryEdges: Map[JavaStatement, Set[IFDSFact]] = Map.empty + var summaryEdges: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = Map.empty /* * If calleeWithUpdateFact is present, this means that the basic block already has been @@ -408,7 +406,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( else { propagateNullFact(inputFacts, callToStartFacts(call, callee, inputFacts)) } - var allNewExitFacts: Map[JavaStatement, Set[IFDSFact]] = Map.empty + var allNewExitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = Map.empty // Collect exit facts for each input fact separately for (fact ← callToStart) { /* @@ -429,14 +427,14 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( } else { val e = (callee, fact) val callFlows = propertyStore(e, propertyKey.key) - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]] val oldValue = state.pendingIfdsDependees.get(e) - val oldExitFacts: Map[JavaStatement, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[JavaStatement, IFDSFact]]) ⇒ ep.ub.flows + val oldExitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = oldValue match { + case Some(ep: InterimEUBP[_, IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]) ⇒ ep.ub.flows case _ ⇒ Map.empty } - val exitFacts: Map[JavaStatement, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[JavaStatement, IFDSFact]] ⇒ + val exitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = callFlows match { + case ep: FinalEP[_, IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]] ⇒ if (state.pendingIfdsCallSites.contains(e) && state.pendingIfdsCallSites(e).nonEmpty) { val newDependee = @@ -445,7 +443,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( } state.pendingIfdsDependees -= e ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[JavaStatement, IFDSFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]] ⇒ /* * Add the call site to `pendingIfdsCallSites` and * `pendingIfdsDependees` and continue with the facts in the interim @@ -493,7 +491,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @param in The input facts, which hold before the `call`. * @return The changed set of input facts. */ - protected def beforeHandleCall(call: JavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = in + protected def beforeHandleCall(call: DeclaredMethodJavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = in /** * Gets the set of all methods directly callable at some call statement. @@ -504,7 +502,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @return All methods directly callable at the statement index. */ protected def getCallees( - statement: JavaStatement, + statement: DeclaredMethodJavaStatement, caller: DeclaredMethod ): Iterator[DeclaredMethod] = { val pc = statement.code(statement.index).pc @@ -656,9 +654,9 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( */ def collectInformation( index: Int - ): (JavaStatement, Option[SomeSet[Method]], Option[IFDSFact]) = { + ): (DeclaredMethodJavaStatement, Option[SomeSet[Method]], Option[IFDSFact]) = { val stmt = state.code(index) - val statement = JavaStatement(state.method, basicBlock, stmt, index, state.code, state.cfg, state.source._1) + val statement = old.DeclaredMethodJavaStatement(state.method, basicBlock, stmt, index, state.code, state.cfg, state.source._1) val calleesO = if (calleeWithUpdateIndex.contains(index)) calleeWithUpdate.map(Set(_)) else getCalleesIfCallStatement(statement) @@ -678,7 +676,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( val next = nextIndex(index) flows = if (calleesO.isEmpty) { val successor = - JavaStatement(state.method, basicBlock, state.code(next), next, state.code, state.cfg, state.source._1) + old.DeclaredMethodJavaStatement(state.method, basicBlock, state.code(next), next, state.code, state.cfg, state.source._1) numberOfCalls.normalFlow += 1 sumOfInputFactsForCallbacks += in.size ifdsProblem.normalFlow(statement, Some(successor), flows) @@ -744,7 +742,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @return All methods possibly called at the statement or None, if the statement does not * contain a call. */ - private def getCalleesIfCallStatement(statement: JavaStatement)( + private def getCalleesIfCallStatement(statement: DeclaredMethodJavaStatement)( implicit state: State ): Option[SomeSet[Method]] = { @@ -817,7 +815,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( */ private def addIfdsDependee( entity: (DeclaredMethod, IFDSFact), - calleeProperty: EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]], + calleeProperty: EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]], callBB: BasicBlock, callIndex: Int )(implicit state: State): Unit = { @@ -831,12 +829,6 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( } object AbstractIFDSAnalysis { - - /** - * The type of the TAC domain. - */ - type V = DUVar[ValueInformation] - /** * When true, the cross product of exit and successor in returnFLow will be optimized. */ @@ -866,7 +858,7 @@ object AbstractIFDSAnalysis { * @param code The method's TAC code. * @param cfg The method's CFG. */ -case class JavaStatement( +case class DeclaredMethodJavaStatement( method: Method, node: CFGNode, stmt: Stmt[V], @@ -879,14 +871,14 @@ case class JavaStatement( override def hashCode(): Int = method.hashCode() * 31 + index override def equals(o: Any): Boolean = o match { - case s: JavaStatement ⇒ s.index == index && s.method == method - case _ ⇒ false + case s: DeclaredMethodJavaStatement ⇒ s.index == index && s.method == method + case _ ⇒ false } override def toString: String = s"${method.toJava}" override def callable(): DeclaredMethod = declaredMethod - def asNewJavaStatement: NewJavaStatement = NewJavaStatement(method, index, code, cfg) + def asJavaStatement: JavaStatement = JavaStatement(method, index, code, cfg) } abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] @@ -894,7 +886,7 @@ abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] final override type InitializationData = AbstractIFDSAnalysis[IFDSFact] - def property: IFDSPropertyMetaInformation[JavaStatement, IFDSFact] + def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, IFDSFact] final override def derivesLazily: Some[PropertyBounds] = Some(PropertyBounds.ub(property)) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/BackwardIFDSAnalysis.scala similarity index 87% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/BackwardIFDSAnalysis.scala index 280459009a..6dffb1bcd9 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/BackwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/BackwardIFDSAnalysis.scala @@ -1,32 +1,19 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds +package org.opalj.tac.fpcf.analyses.ifds.old -import scala.annotation.tailrec -import org.opalj.fpcf.EOptionP -import org.opalj.fpcf.FinalE -import org.opalj.fpcf.FinalEP -import org.opalj.fpcf.FinalP -import org.opalj.fpcf.InterimEUBP -import org.opalj.fpcf.ProperPropertyComputationResult -import org.opalj.fpcf.SomeEPS -import org.opalj.value.ValueInformation -import org.opalj.br.cfg.BasicBlock -import org.opalj.br.cfg.CFGNode import org.opalj.br.DeclaredMethod -import org.opalj.br.cfg.CatchNode -import org.opalj.br.cfg.CFG +import org.opalj.br.cfg.{BasicBlock, CFG, CFGNode, CatchNode} +import org.opalj.fpcf._ import org.opalj.ifds.old.IFDSProblem import org.opalj.ifds.{AbstractIFDSFact, IFDSProperty, IFDSPropertyMetaInformation} +import org.opalj.tac._ +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.old import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V import org.opalj.tac.fpcf.properties.{TACAI, TheTACAI} -import org.opalj.tac.Stmt -import org.opalj.tac.TACStmts -import org.opalj.tac.DUVar -import org.opalj.tac.Return -import org.opalj.tac.ReturnValue -import org.opalj.tac.TACMethodParameter -import org.opalj.tac.TACode +import org.opalj.value.ValueInformation + +import scala.annotation.tailrec /** * An IFDS analysis, which analyzes the code against the control flow direction. @@ -35,7 +22,7 @@ import org.opalj.tac.TACode * concrete analysis. * @author Mario Trageser */ -abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement, CFGNode] with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, JavaStatement], propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { +abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, DeclaredMethodJavaStatement, CFGNode] with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, DeclaredMethodJavaStatement], propertyKey: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { /** * If this method is analyzed for an unbalanced return fact, the single star block is the block, * which contains the call. @@ -56,11 +43,11 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS /** * Collects the output facts of the entry point of the analyzed method. */ - override protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { + override protected def collectResult(implicit state: State): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = { val startBlock = state.cfg.startBlock val startPC = startBlock.startPC val statement = - JavaStatement(state.method, startBlock, state.code(startPC), startPC, state.code, state.cfg, state.source._1) + old.DeclaredMethodJavaStatement(state.method, startBlock, state.code(startPC), startPC, state.code, state.cfg, state.source._1) val exitFacts = state.outgoingFacts.get(startBlock).flatMap(_.get(SyntheticStartNode)) if (exitFacts.isDefined) Map(statement -> exitFacts.get) else Map.empty @@ -90,12 +77,12 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS */ case interimEUBP @ InterimEUBP( e: (DeclaredMethod, IFDSFact) @unchecked, - _: IFDSProperty[JavaStatement, IFDSFact] @unchecked + _: IFDSProperty[DeclaredMethodJavaStatement, IFDSFact] @unchecked ) ⇒ if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]]) { state.pendingIfdsDependees += e -> interimEUBP - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]] createResult() } else super.propertyUpdate(eps) @@ -211,13 +198,13 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * If it is a catch node, the first statement of its throwing block will be returned. * If it is a synthetic start node, an artificial statement without code will be returned. */ - override protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement = { + override protected def firstStatement(node: CFGNode)(implicit state: State): DeclaredMethodJavaStatement = { if (node.isBasicBlock) { val index = node.asBasicBlock.endPC - JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg, state.source._1) + old.DeclaredMethodJavaStatement(state.method, node, state.code(index), index, state.code, state.cfg, state.source._1) } else if (node.isCatchNode) firstStatement(node.successors.head) else if (node == SyntheticStartNode) - JavaStatement(state.method, node, null, 0, state.code, state.cfg, state.source._1) + old.DeclaredMethodJavaStatement(state.method, node, null, 0, state.code, state.cfg, state.source._1) else throw new IllegalArgumentException(s"Unknown node type: $node") } @@ -225,8 +212,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * The successor statements against the control flow direction. */ override protected def nextStatements( - statement: JavaStatement - )(implicit state: State): Set[JavaStatement] = { + statement: DeclaredMethodJavaStatement + )(implicit state: State): Set[DeclaredMethodJavaStatement] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == 0) { @@ -236,7 +223,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS else { val nextIndex = index - 1 Set( - JavaStatement( + old.DeclaredMethodJavaStatement( statement.method, basicBlock, statement.code(nextIndex), @@ -254,7 +241,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * direction, which my be executed after them. Calls returnFlow on those pairs. */ override protected def callToStartFacts( - call: JavaStatement, + call: DeclaredMethodJavaStatement, callee: DeclaredMethod, in: Set[IFDSFact] )(implicit state: State): Set[IFDSFact] = { @@ -271,7 +258,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val calleeStmts = tac.stmts val exitStmt = calleeStmts(exitPc) val exitStatement = - JavaStatement(definedCallee, cfg.bb(exitPc), exitStmt, exitPc, calleeStmts, cfg, state.source._1) + old.DeclaredMethodJavaStatement(definedCallee, cfg.bb(exitPc), exitStmt, exitPc, calleeStmts, cfg, state.source._1) for { successor ← successors if !AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW || @@ -303,12 +290,12 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * edges. exitFacts should at most contain the callee's entry point. */ override protected def addExitToReturnFacts( - summaryEdges: Map[JavaStatement, Set[IFDSFact]], - successors: Set[JavaStatement], - call: JavaStatement, + summaryEdges: Map[DeclaredMethodJavaStatement, Set[IFDSFact]], + successors: Set[DeclaredMethodJavaStatement], + call: DeclaredMethodJavaStatement, callee: DeclaredMethod, - exitFacts: Map[JavaStatement, Set[IFDSFact]] - )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { + exitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] + )(implicit state: State): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = { var result = summaryEdges if (exitFacts.nonEmpty) { val in = exitFacts.head._2 @@ -331,7 +318,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS /** * If there is an unbalanced return fact for this call, it will be replaced by its inner fact. */ - override protected def beforeHandleCall(call: JavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = + override protected def beforeHandleCall(call: DeclaredMethodJavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = in.map { case unbalancedFact: UnbalancedReturnFact[IFDSFact] if unbalancedFact.index == call.index ⇒ unbalancedFact.innerFact @@ -348,8 +335,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * @return A map, mapping from a predecessor statement to the corresponding node. */ private def predecessorStatementsWithNode( - statement: JavaStatement - )(implicit state: State): Map[JavaStatement, CFGNode] = { + statement: DeclaredMethodJavaStatement + )(implicit state: State): Map[DeclaredMethodJavaStatement, CFGNode] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == basicBlock.endPC) @@ -359,7 +346,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS else { val nextIndex = index + 1 Map( - JavaStatement( + old.DeclaredMethodJavaStatement( statement.method, basicBlock, statement.code(nextIndex), @@ -382,12 +369,12 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * * @return The last statement of `node`. */ - @tailrec private def lastStatement(node: CFGNode)(implicit state: State): JavaStatement = { + @tailrec private def lastStatement(node: CFGNode)(implicit state: State): DeclaredMethodJavaStatement = { if (node.isBasicBlock) { val index = node.asBasicBlock.startPC - JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg, state.source._1) + old.DeclaredMethodJavaStatement(state.method, node, state.code(index), index, state.code, state.cfg, state.source._1) } else if (node.isCatchNode) lastStatement(node.successors.head) - else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg, state.source._1) + else if (node.isExitNode) old.DeclaredMethodJavaStatement(state.method, node, null, 0, state.code, state.cfg, state.source._1) else throw new IllegalArgumentException(s"Unknown node type: $node") } @@ -407,7 +394,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS )(implicit state: State): Unit = { val callerStmts = tac.stmts val callerCfg = tac.cfg - val call = JavaStatement( + val call = old.DeclaredMethodJavaStatement( caller.definedMethod, callerCfg.bb(callIndex), callerStmts(callIndex), @@ -429,7 +416,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val pendingIfdsCallSites = state.pendingIfdsCallSites state.pendingIfdsDependees += callerEntity -> callerAnalysisResult - .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[JavaStatement, IFDSFact]]] + .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]] state.pendingIfdsCallSites += callerEntity -> (pendingIfdsCallSites.getOrElse(callerEntity, Set.empty) + ((state.cfg.startBlock, 0))) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardICFG.scala new file mode 100644 index 0000000000..231b2afe35 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardICFG.scala @@ -0,0 +1,95 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.old + +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.DeclaredMethods +import org.opalj.br.cfg.CFGNode +import org.opalj.fpcf.{FinalEP, PropertyStore} +import org.opalj.ifds.AbstractIFDSFact +import org.opalj.ifds.old.ICFG +import org.opalj.tac.fpcf.analyses.cg.TypeProvider +import org.opalj.tac.fpcf.properties.cg.Callees + +class ForwardICFG[IFDSFact <: AbstractIFDSFact](implicit + propertyStore: PropertyStore, + typeProvider: TypeProvider, + declaredMethods: DeclaredMethods +) extends ICFG[IFDSFact, DeclaredMethod, DeclaredMethodJavaStatement, CFGNode] { + /** + * Determines the basic blocks, at which the analysis starts. + * + * @param sourceFact The source fact of the analysis. + * @param callable The analyzed callable. + * @return The basic blocks, at which the analysis starts. + */ + override def startNodes(sourceFact: IFDSFact, callable: DeclaredMethod): Set[CFGNode] = ??? + + /** + * Determines the nodes, that will be analyzed after some `basicBlock`. + * + * @param node The basic block, that was analyzed before. + * @return The nodes, that will be analyzed after `basicBlock`. + */ + override def nextNodes(node: CFGNode): Set[CFGNode] = ??? + + /** + * Checks, if some `node` is the last node. + * + * @return True, if `node` is the last node, i.e. there is no next node. + */ + override def isLastNode(node: CFGNode): Boolean = ??? + + /** + * Determines the first index of some `basic block`, that will be analyzed. + * + * @param basicBlock The basic block. + * @return The first index of some `basic block`, that will be analyzed. + */ + override def firstStatement(basicBlock: CFGNode): DeclaredMethodJavaStatement = ??? + + /** + * Determines the last index of some `basic block`, that will be analzyed. + * + * @param basicBlock The basic block. + * @return The last index of some `basic block`, that will be analzyed. + */ + override def lastStatement(basicBlock: CFGNode): DeclaredMethodJavaStatement = ??? + + /** + * Determines the statement that will be analyzed after some other statement. + * + * @param statement The current statement. + * @return The statement that will be analyzed after `statement`. + */ + override def nextStatement(statement: DeclaredMethodJavaStatement): DeclaredMethodJavaStatement = ??? + + /** + * Determines the statement, that will be analyzed after some other `statement`. + * + * @param statement The source statement. + * @return The successor statements + */ + override def nextStatements(statement: DeclaredMethodJavaStatement): Set[DeclaredMethodJavaStatement] = ??? + + /** + * Gets the set of all methods possibly called at some statement. + * + * @param statement The statement. + * @return All callables possibly called at the statement or None, if the statement does not + * contain a call. + */ + override def getCalleesIfCallStatement(statement: DeclaredMethodJavaStatement): Option[collection.Set[DeclaredMethod]] = { + val pc = statement.code(statement.index).pc + val caller = declaredMethods(statement.method) + val ep = propertyStore(caller, Callees.key) + ep match { + case FinalEP(_, p) ⇒ Some(p.directCallees(typeProvider.newContext(caller), pc).map(_.method).toSet) + case _ ⇒ + throw new IllegalStateException( + "call graph mut be computed before the analysis starts" + ) + } + } + + override def isExitStatement(statement: DeclaredMethodJavaStatement): Boolean = ??? +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardIFDSAnalysis.scala similarity index 77% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardIFDSAnalysis.scala index ab856ed85f..9079309db7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardIFDSAnalysis.scala @@ -1,19 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds +package org.opalj.tac.fpcf.analyses.ifds.old import org.opalj.br.DeclaredMethod import org.opalj.br.cfg.{BasicBlock, CFG, CFGNode} import org.opalj.ifds.old.IFDSProblem import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} +import org.opalj.tac.fpcf.analyses.ifds.old +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.{Return, ReturnValue, Stmt, TACStmts} -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V /** * An IFDS analysis, which analyzes the code in the control flow direction. * * @author Mario Trageser */ -abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, JavaStatement, CFGNode], propertyKey: IFDSPropertyMetaInformation[JavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { +abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IFDSProblem[IFDSFact, DeclaredMethod, DeclaredMethodJavaStatement, CFGNode], propertyKey: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, IFDSFact]) extends AbstractIFDSAnalysis[IFDSFact](ifdsProblem, propertyKey) { /** * The analysis starts at the entry block. @@ -27,7 +28,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF /** * Collects the output facts at the predecessors of the normal and abnormal return node. */ - override protected def collectResult(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { + override protected def collectResult(implicit state: State): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = { mergeMaps( resultOfExitNode(state.cfg.normalReturnNode), resultOfExitNode(state.cfg.abnormalReturnNode) @@ -76,26 +77,26 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF * If it is a catch node, the first statement of its handler will be returned. * If it is an exit node, an artificial statement without code will be returned. */ - override protected def firstStatement(node: CFGNode)(implicit state: State): JavaStatement = { + override protected def firstStatement(node: CFGNode)(implicit state: State): DeclaredMethodJavaStatement = { if (node.isBasicBlock) { val index = node.asBasicBlock.startPC - JavaStatement(state.method, node, state.code(index), index, state.code, state.cfg, state.source._1) + old.DeclaredMethodJavaStatement(state.method, node, state.code(index), index, state.code, state.cfg, state.source._1) } else if (node.isCatchNode) firstStatement(node.successors.head) - else if (node.isExitNode) JavaStatement(state.method, node, null, 0, state.code, state.cfg, state.source._1) + else if (node.isExitNode) old.DeclaredMethodJavaStatement(state.method, node, null, 0, state.code, state.cfg, state.source._1) else throw new IllegalArgumentException(s"Unknown node type: $node") } /** * The successor statements in the direction of the control flow. */ - override protected def nextStatements(statement: JavaStatement)(implicit state: State): Set[JavaStatement] = { + override protected def nextStatements(statement: DeclaredMethodJavaStatement)(implicit state: State): Set[DeclaredMethodJavaStatement] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == basicBlock.endPC) basicBlock.successors.map(firstStatement(_)) else { val nextIndex = index + 1 - Set(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + Set(old.DeclaredMethodJavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, statement.code, statement.cfg, statement.declaredMethod)) } } @@ -103,7 +104,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF /** * Calls callFlow. */ - override protected def callToStartFacts(call: JavaStatement, callee: DeclaredMethod, + override protected def callToStartFacts(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, in: Set[IFDSFact])(implicit state: State): Set[IFDSFact] = { numberOfCalls.callFlow += 1 sumOfInputFactsForCallbacks += in.size @@ -115,11 +116,11 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF * with each catch node. Calls returnFlow for those pairs and adds them to the summary edges. */ override protected def addExitToReturnFacts( - summaryEdges: Map[JavaStatement, Set[IFDSFact]], - successors: Set[JavaStatement], call: JavaStatement, + summaryEdges: Map[DeclaredMethodJavaStatement, Set[IFDSFact]], + successors: Set[DeclaredMethodJavaStatement], call: DeclaredMethodJavaStatement, callee: DeclaredMethod, - exitFacts: Map[JavaStatement, Set[IFDSFact]] - )(implicit state: State): Map[JavaStatement, Set[IFDSFact]] = { + exitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] + )(implicit state: State): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = { // First process for normal returns, then abnormal returns. var result = summaryEdges if (AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { @@ -146,7 +147,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF * Like nextStatements, but maps each successor statement to the corresponding successor node. * When determining the successor node, catch nodes are not skipped. */ - private def nextStatementsWithNode(statement: JavaStatement)(implicit state: State): Map[JavaStatement, CFGNode] = { + private def nextStatementsWithNode(statement: DeclaredMethodJavaStatement)(implicit state: State): Map[DeclaredMethodJavaStatement, CFGNode] = { val index = statement.index val basicBlock = statement.node.asBasicBlock if (index == basicBlock.endPC) @@ -154,7 +155,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap else { val nextIndex = index + 1 - Map(JavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, + Map(old.DeclaredMethodJavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, statement.code, statement.cfg, statement.declaredMethod) → basicBlock) } } @@ -169,15 +170,15 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF private def resultOfExitNode(exit: CFGNode)( implicit state: State - ): Map[JavaStatement, Set[IFDSFact]] = { - var result = Map.empty[JavaStatement, Set[IFDSFact]] + ): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = { + var result = Map.empty[DeclaredMethodJavaStatement, Set[IFDSFact]] exit.predecessors foreach { predecessor ⇒ if (predecessor.isBasicBlock) { val basicBlock = predecessor.asBasicBlock val exitFacts = state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)) if (exitFacts.isDefined) { val lastIndex = basicBlock.endPC - val stmt = JavaStatement(state.method, basicBlock, state.code(lastIndex), + val stmt = old.DeclaredMethodJavaStatement(state.method, basicBlock, state.code(lastIndex), lastIndex, state.code, state.cfg, state.source._1) result += stmt → exitFacts.get } @@ -199,10 +200,10 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF * found exit facts. * @return `summaryEdges` with an additional or updated summary edge from `call` to `successor`. */ - private def addSummaryEdge(summaryEdges: Map[JavaStatement, Set[IFDSFact]], call: JavaStatement, - exitStatement: JavaStatement, successor: JavaStatement, + private def addSummaryEdge(summaryEdges: Map[DeclaredMethodJavaStatement, Set[IFDSFact]], call: DeclaredMethodJavaStatement, + exitStatement: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, callee: DeclaredMethod, - allNewExitFacts: Map[JavaStatement, Set[IFDSFact]]): Map[JavaStatement, Set[IFDSFact]] = { + allNewExitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]]): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = { val in = allNewExitFacts.getOrElse(exitStatement, Set.empty) numberOfCalls.returnFlow += 1 sumOfInputFactsForCallbacks += in.size diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSBasedVariableTypeAnalysis.scala similarity index 82% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSBasedVariableTypeAnalysis.scala index 8226d86357..9983e0df4e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSBasedVariableTypeAnalysis.scala @@ -1,16 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds +package org.opalj.tac.fpcf.analyses.ifds.old -import java.io.File -import java.io.PrintWriter -import org.opalj.fpcf.PropertyKey -import org.opalj.fpcf.PropertyStore -import org.opalj.fpcf.PropertyBounds import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{PropertyBounds, PropertyKey, PropertyStore} import org.opalj.ifds.old.{NumberOfSubsumptions, Subsuming} import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} -import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.fpcf.properties.cg.Callers + +import java.io.{File, PrintWriter} /** * A variable type analysis implemented as an IFDS analysis. @@ -33,28 +31,28 @@ object IFDSBasedVariableTypeAnalysisScheduler extends IFDSAnalysisScheduler[VTAF override def init(p: SomeProject, ps: PropertyStore): IFDSBasedVariableTypeAnalysis = { implicit val project = p - if (SUBSUMING) new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) with Subsuming[JavaStatement, VTAFact] + if (SUBSUMING) new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) with Subsuming[DeclaredMethodJavaStatement, VTAFact] else new IFDSBasedVariableTypeAnalysis(new VariableTypeProblem(p)) } - override def property: IFDSPropertyMetaInformation[JavaStatement, VTAFact] = VTAResult + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, VTAFact] = VTAResult } /** * The IFDSProperty for this analysis. */ -case class VTAResult(flows: Map[JavaStatement, Set[VTAFact]]) extends IFDSProperty[JavaStatement, VTAFact] { +case class VTAResult(flows: Map[DeclaredMethodJavaStatement, Set[VTAFact]]) extends IFDSProperty[DeclaredMethodJavaStatement, VTAFact] { override type Self = VTAResult - override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result) + override def create(result: Map[DeclaredMethodJavaStatement, Set[VTAFact]]): IFDSProperty[DeclaredMethodJavaStatement, VTAFact] = new VTAResult(result) override def key: PropertyKey[VTAResult] = VTAResult.key } -object VTAResult extends IFDSPropertyMetaInformation[JavaStatement, VTAFact] { +object VTAResult extends IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, VTAFact] { override type Self = VTAResult - override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result) + override def create(result: Map[DeclaredMethodJavaStatement, Set[VTAFact]]): IFDSProperty[DeclaredMethodJavaStatement, VTAFact] = new VTAResult(result) val key: PropertyKey[VTAResult] = PropertyKey.create("VTA", new VTAResult(Map.empty)) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSProblem.scala similarity index 98% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSProblem.scala index 6802c09c98..4cf5cf8305 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSProblem.scala @@ -1,5 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds +package org.opalj.tac.fpcf.analyses.ifds.old import org.opalj.ifds.AbstractIFDSFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/JavaIFDSProblem.scala new file mode 100644 index 0000000000..b20b35ac90 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/JavaIFDSProblem.scala @@ -0,0 +1,109 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.old + +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} +import org.opalj.br.cfg.CFGNode +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.br.{DeclaredMethod, ObjectType} +import org.opalj.fpcf._ +import org.opalj.ifds.old.IFDSProblem +import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.fpcf.analyses.cg.TypeProvider +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.properties.cg.Callers +import org.opalj.tac.{Assignment, Call, ExprStmt, Stmt} + +abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, DeclaredMethod, DeclaredMethodJavaStatement, CFGNode]( + new ForwardICFG[Fact]()(project.get(PropertyStoreKey), project.get(TypeProviderKey), project.get(DeclaredMethodsKey)) +) { + /** + * All declared methods in the project. + */ + implicit final protected val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) + + final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) + implicit final protected val typeProvider: TypeProvider = project.get(TypeProviderKey) + + override def outsideAnalysisContext(callee: DeclaredMethod): Option[(DeclaredMethodJavaStatement, DeclaredMethodJavaStatement, Set[Fact]) ⇒ Set[Fact]] = callee.definedMethod.body.isDefined match { + case true ⇒ None + case false ⇒ Some((_call: DeclaredMethodJavaStatement, _successor: DeclaredMethodJavaStatement, in: Set[Fact]) ⇒ in) + } + + /** + * Returns all methods, that can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @return All methods, that can be called from outside the library. + */ + protected def methodsCallableFromOutside: Set[DeclaredMethod] = + declaredMethods.declaredMethods.filter(canBeCalledFromOutside).toSet + + /** + * Checks, if some `method` can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @param method The method, which may be callable from outside. + * @return True, if `method` can be called from outside the library. + */ + protected def canBeCalledFromOutside(method: DeclaredMethod): Boolean = { + val FinalEP(_, callers) = propertyStore(method, Callers.key) + callers.hasCallersWithUnknownContext + } + + /** + * Gets the call object for a statement that contains a call. + * + * @param call The call statement. + * @return The call object for `call`. + */ + protected def asCall(call: Stmt[V]): Call[V] = call.astID match { + case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall + case _ ⇒ call.asMethodCall + } + + override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact]): Option[ProperPropertyComputationResult] = { + val declaredMethod = source._1 + val method = declaredMethod.definedMethod + val declaringClass: ObjectType = method.classFile.thisType + /* + * If this is not the method's declaration, but a non-overwritten method in a subtype, do + * not re-analyze the code. + */ + if (declaringClass ne declaredMethod.declaringClassType) Some(baseMethodResult(source, propertyKey)) + super.specialCase(source, propertyKey) + } + + /** + * This method will be called if a non-overwritten declared method in a sub type shall be + * analyzed. Analyzes the defined method of the supertype instead. + * + * @param source A pair consisting of the declared method of the subtype and an input fact. + * @return The result of the analysis of the defined method of the supertype. + */ + private def baseMethodResult(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact]): ProperPropertyComputationResult = { + + def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { + case FinalP(p) ⇒ Result(source, p) + + case ep @ InterimUBP(ub: Property) ⇒ + InterimResult.forUB(source, ub, Set(ep), c) + + case epk ⇒ + InterimResult.forUB(source, propertyKey.create(Map.empty), Set(epk), c) + } + c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) + } +} + +abstract class JavaBackwardIFDSProblem[IFDSFact <: AbstractIFDSFact, UnbalancedIFDSFact <: IFDSFact with UnbalancedReturnFact[IFDSFact]](project: SomeProject) extends JavaIFDSProblem[IFDSFact](project) with BackwardIFDSProblem[IFDSFact, UnbalancedIFDSFact, DeclaredMethod, DeclaredMethodJavaStatement] { + /** + * Checks for the analyzed entity, if an unbalanced return should be performed. + * + * @param source The analyzed entity. + * @return False, if no unbalanced return should be performed. + */ + def shouldPerformUnbalancedReturn(source: (DeclaredMethod, IFDSFact)): Boolean = + source._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]] || entryPoints.contains(source) +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala similarity index 96% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala index bb27daa2ec..3544c794ff 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala @@ -1,8 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds +package org.opalj.tac.fpcf.analyses.ifds.old -import org.opalj.br.analyses.SomeProject import org.opalj.br._ +import org.opalj.br.analyses.SomeProject import org.opalj.collection.immutable.EmptyIntTrieSet import org.opalj.ifds.{AbstractIFDSFact, SubsumableFact, SubsumableNullFact} import org.opalj.tac._ @@ -80,8 +80,8 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] * If there is a field read, a new VariableType will be created with the field's declared type. */ override def normalFlow( - statement: JavaStatement, - successor: Option[JavaStatement], + statement: DeclaredMethodJavaStatement, + successor: Option[DeclaredMethodJavaStatement], in: Set[VTAFact] ): Set[VTAFact] = { val stmt = statement.stmt @@ -116,7 +116,7 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] * created for the callee context. */ override def callFlow( - call: JavaStatement, + call: DeclaredMethodJavaStatement, callee: DeclaredMethod, in: Set[VTAFact], source: (DeclaredMethod, VTAFact) @@ -152,8 +152,8 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] * VariableType, which could be the call's target. */ override def callToReturnFlow( - call: JavaStatement, - successor: JavaStatement, + call: DeclaredMethodJavaStatement, + successor: DeclaredMethodJavaStatement, in: Set[VTAFact], source: (DeclaredMethod, VTAFact) ): Set[VTAFact] = { @@ -175,10 +175,10 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] * created in the caller context with the returned variable's type. */ override def returnFlow( - call: JavaStatement, + call: DeclaredMethodJavaStatement, callee: DeclaredMethod, - exit: JavaStatement, - successor: JavaStatement, + exit: DeclaredMethodJavaStatement, + successor: DeclaredMethodJavaStatement, in: Set[VTAFact] ): Set[VTAFact] = // We only create a new fact, if the call returns a value, which is assigned to a variable. @@ -202,7 +202,7 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] super.outsideAnalysisContext(callee).isEmpty) None else { - Some(((call: JavaStatement, successor: JavaStatement, in: Set[VTAFact]) ⇒ { + Some(((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[VTAFact]) ⇒ { val returnType = callee.descriptor.returnType if (call.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { Set(VariableType(call.index, returnType.asReferenceType, upperBound = true)) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala similarity index 91% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala index 31108cc456..f261e267b6 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala @@ -1,19 +1,20 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds.taint +package org.opalj.tac.fpcf.analyses.ifds.old.taint -import org.opalj.br.{DeclaredMethod, Method, ObjectType} import org.opalj.br.analyses.SomeProject -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.{ArrayLength, ArrayLoad, ArrayStore, Assignment, BinaryExpr, Compare, Expr, GetField, NewArray, PrefixExpr, PrimitiveTypecastExpr, PutField, PutStatic, ReturnValue, Var} -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaBackwardIFDSProblem, JavaStatement} +import org.opalj.br.{DeclaredMethod, Method, ObjectType} +import org.opalj.tac._ +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.old.{AbstractIFDSAnalysis, JavaBackwardIFDSProblem, DeclaredMethodJavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint._ -abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[Fact, UnbalancedTaintFact](project) with TaintProblem[DeclaredMethod, JavaStatement, Fact] { +abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[Fact, UnbalancedTaintFact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, Fact] { override def nullFact: Fact = NullFact /** * If a tainted variable gets assigned a value, this value will be tainted. */ - override def normalFlow(statement: JavaStatement, successor: Option[JavaStatement], + override def normalFlow(statement: DeclaredMethodJavaStatement, successor: Option[DeclaredMethodJavaStatement], in: Set[Fact]): Set[Fact] = { val stmt = statement.stmt stmt.astID match { @@ -62,7 +63,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * context were tainted. * Does not taint anything, if the sanitize method was called. */ - override def callFlow(call: JavaStatement, callee: DeclaredMethod, + override def callFlow(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = if (sanitizesReturnValue(callee)) Set.empty else taintActualsIfFormalsTainted(callee, call, in, source) @@ -72,8 +73,8 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * context will be tainted. If an actual pass-by-reference-parameter in the caller context is * tainted, the formal parameter in the callee context will be tainted. */ - override def returnFlow(call: JavaStatement, callee: DeclaredMethod, exit: JavaStatement, - successor: JavaStatement, in: Set[Fact]): Set[Fact] = { + override def returnFlow(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, exit: DeclaredMethodJavaStatement, + successor: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = { val callObject = asCall(call.stmt) val staticCall = callee.definedMethod.isStatic val flow = collection.mutable.Set.empty[Fact] @@ -119,7 +120,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * Adds a FlowFact, if `createFlowFactAtCall` creates one. * Removes taints according to `sanitizeParamters`. */ - override def callToReturnFlow(call: JavaStatement, successor: JavaStatement, + override def callToReturnFlow(call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = { val flowFact = createFlowFactAtCall(call, in, source) @@ -133,7 +134,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF */ override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some((call: JavaStatement, successor: JavaStatement, in: Set[Fact]) ⇒ { + case Some(_) ⇒ Some((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[Fact]) ⇒ { val callStatement = asCall(call.stmt) in ++ in.collect { case Variable(index) if index == call.index ⇒ @@ -151,7 +152,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * Creates an UnbalancedTaintFact for each actual parameter on the caller side, of the formal * parameter of this method is tainted. */ - override def unbalancedReturnFlow(facts: Set[Fact], call: JavaStatement, + override def unbalancedReturnFlow(facts: Set[Fact], call: DeclaredMethodJavaStatement, caller: DeclaredMethod, source: (DeclaredMethod, Fact)): Set[UnbalancedTaintFact] = taintActualsIfFormalsTainted(source._1, call, facts, source, isCallFlow = false) @@ -167,7 +168,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @param source The entity, which is analyzed. * @return Some FlowFact, if necessary. Otherwise None. */ - protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], + protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[Fact], source: (DeclaredMethod, Fact)): Option[FlowFact] /** @@ -257,7 +258,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @param statement The statement, which contains the expression. * @return The new taints. */ - private def createNewTaints(expression: Expr[V], statement: JavaStatement): Set[Fact] = + private def createNewTaints(expression: Expr[V], statement: DeclaredMethodJavaStatement): Set[Fact] = expression.astID match { case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) case ArrayLoad.ASTID ⇒ @@ -289,7 +290,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @return An ArrayElement fact for the expression and the tainted element. */ private def createNewArrayElementTaints(expression: Expr[V], taintedElement: Int, - statement: JavaStatement): Set[Fact] = + statement: DeclaredMethodJavaStatement): Set[Fact] = createNewTaints(expression, statement).map { case Variable(variableIndex) ⇒ ArrayElement(variableIndex, taintedElement) // We do not nest taints. Instead, we taint the whole inner array. @@ -298,7 +299,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF } private def createNewInstanceFieldTaints(expression: Expr[V], declaringClass: ObjectType, - name: String, statement: JavaStatement): Set[Fact] = + name: String, statement: DeclaredMethodJavaStatement): Set[Fact] = createNewTaints(expression, statement).map { case Variable(variableIndex) ⇒ InstanceField(variableIndex, declaringClass, name) // We do not nest taints. Instead, taint the whole field. @@ -322,10 +323,10 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF */ private def taintActualsIfFormalsTainted( callee: DeclaredMethod, - call: JavaStatement, + call: DeclaredMethodJavaStatement, calleeFacts: Set[Fact], source: (DeclaredMethod, Fact), - isCallFlow: Boolean = true + isCallFlow: Boolean = true ): Set[Fact] = { val stmt = call.stmt val callStatement = asCall(stmt) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala similarity index 77% rename from OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala rename to OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala index fff867665d..3da067d12d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala @@ -1,28 +1,25 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds.taint +package org.opalj.tac.fpcf.analyses.ifds.old.taint -import org.opalj.br.Method -import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaMethod, NewJavaStatement, NewJavaIFDSProblem} +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.SomeProject import org.opalj.tac._ +import org.opalj.tac.fpcf.analyses.ifds.JavaMethod +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.old.{AbstractIFDSAnalysis, JavaIFDSProblem, DeclaredMethodJavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint._ -abstract class NewForwardTaintProblem(project: SomeProject) - extends NewJavaIFDSProblem[Fact](project) - with NewTaintProblem[Method, NewJavaStatement, Fact] { - val declaredMethods = project.get(DeclaredMethodsKey) +abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[Fact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, Fact] { override def nullFact: Fact = NullFact - override def needsPredecessor(statement: NewJavaStatement): Boolean = false - /** * If a variable gets assigned a tainted value, the variable will be tainted. */ - override def normalFlow(statement: NewJavaStatement, in: Fact, predecessor: Option[NewJavaStatement]): Set[Fact] = { - val in_set = Set(in) // dirty hack for old code + override def normalFlow(statement: DeclaredMethodJavaStatement, successor: Option[DeclaredMethodJavaStatement], + in: Set[Fact]): Set[Fact] = statement.stmt.astID match { case Assignment.ASTID ⇒ - in_set ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) + in ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) case ArrayStore.ASTID ⇒ val store = statement.stmt.asArrayStore val definedBy = store.arrayRef.asVar.definedBy @@ -30,58 +27,57 @@ abstract class NewForwardTaintProblem(project: SomeProject) if (isTainted(store.value, in)) { if (arrayIndex.isDefined) // Taint a known array index - definedBy.foldLeft(in_set) { (c, n) ⇒ + definedBy.foldLeft(in) { (c, n) ⇒ c + ArrayElement(n, arrayIndex.get) } else // Taint the whole array if the index is unknown - definedBy.foldLeft(in_set) { (c, n) ⇒ + definedBy.foldLeft(in) { (c, n) ⇒ c + Variable(n) } } else if (arrayIndex.isDefined && definedBy.size == 1) // Untaint if possible - in_set - ArrayElement(definedBy.head, arrayIndex.get) - else in_set + in - ArrayElement(definedBy.head, arrayIndex.get) + else in case PutField.ASTID ⇒ val put = statement.stmt.asPutField val definedBy = put.objRef.asVar.definedBy if (isTainted(put.value, in)) - definedBy.foldLeft(in_set) { (in, defSite) ⇒ + definedBy.foldLeft(in) { (in, defSite) ⇒ in + InstanceField(defSite, put.declaringClass, put.name) } else - in_set + in case PutStatic.ASTID ⇒ val put = statement.stmt.asPutStatic if (isTainted(put.value, in)) - in_set + StaticField(put.declaringClass, put.name) + in + StaticField(put.declaringClass, put.name) else - in_set - case _ ⇒ in_set + in + case _ ⇒ in } - } /** * Propagates tainted parameters to the callee. If a call to the sink method with a tainted * parameter is detected, no call-to-start * edges will be created. */ - override def callFlow(call: NewJavaStatement, callee: Method, in: Fact): Set[Fact] = { - val in_set = Set(in) // compatilibity for old code + override def callFlow(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, + in: Set[Fact], a: (DeclaredMethod, Fact)): Set[Fact] = { val callObject = asCall(call.stmt) val allParams = callObject.allParams var facts = Set.empty[Fact] if (relevantCallee(callee)) { val allParamsWithIndices = allParams.zipWithIndex - in_set.foreach { + in.foreach { // Taint formal parameter if actual parameter is tainted case Variable(index) ⇒ allParamsWithIndices.foreach { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( paramIndex, - callee.isStatic + callee.definedMethod.isStatic )) case _ ⇒ // Nothing to do } @@ -91,7 +87,7 @@ abstract class NewForwardTaintProblem(project: SomeProject) allParamsWithIndices.foreach { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ facts += ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.isStatic), + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), taintedIndex ) case _ ⇒ // Nothing to do @@ -102,10 +98,10 @@ abstract class NewForwardTaintProblem(project: SomeProject) // Only if the formal parameter is of a type that may have that field! allParamsWithIndices.foreach { case (param, pIndex) if param.asVar.definedBy.contains(index) && - (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic) != -1 || - project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) ⇒ + (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || + project.classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ facts += InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic), + AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), declClass, taintedField ) case _ ⇒ // Nothing to do @@ -129,18 +125,18 @@ abstract class NewForwardTaintProblem(project: SomeProject) * Creates new taints and FlowFacts, if necessary. * If the sanitize method was called, nothing will be tainted. */ - override def returnFlow(exit: NewJavaStatement, in: Fact, call: NewJavaStatement, callFact: Fact): Set[Fact] = { - val in_set = Set(in) // dirty hack for compatibility with old code - val callee = exit.callable() + override def returnFlow(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, exit: DeclaredMethodJavaStatement, + successor: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = { + /** * Checks whether the callee's formal parameter is of a reference type. */ def isRefTypeParam(index: Int): Boolean = if (index == -1) true else { - val parameterOffset = if (callee.isStatic) 0 else 1 + val parameterOffset = if (callee.definedMethod.isStatic) 0 else 1 callee.descriptor.parameterType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) - parameterOffset ).isReferenceType } @@ -149,25 +145,25 @@ abstract class NewForwardTaintProblem(project: SomeProject) val callStatement = asCall(call.stmt) val allParams = callStatement.allParams var flows: Set[Fact] = Set.empty - in_set.foreach { + in.foreach { // Taint actual parameter if formal parameter is tainted case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(Variable) // Taint element of actual parameter if element of formal parameter is tainted case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ // Taint field of actual parameter if field of formal parameter is tainted val param = - allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) + allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) param.asVar.definedBy.foreach { defSite ⇒ flows += InstanceField(defSite, declClass, taintedField) } @@ -183,7 +179,7 @@ abstract class NewForwardTaintProblem(project: SomeProject) // Propagate taints of the return value if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy - in_set.foreach { + in.foreach { case Variable(index) if returnValueDefinedBy.contains(index) ⇒ flows += Variable(call.index) case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ @@ -203,21 +199,23 @@ abstract class NewForwardTaintProblem(project: SomeProject) } /** - * Removes taints according to `sanitizesParameter`. + * Removes taints according to `sanitizeParamters`. */ - override def callToReturnFlow(call: NewJavaStatement, in: Fact): Set[Fact] = - if (sanitizesParameter(call, in)) Set() else Set(in) + override def callToReturnFlow(call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, + in: Set[Fact], + source: (DeclaredMethod, Fact)): Set[Fact] = + in -- sanitizeParameters(call, in) /** * Called, when the exit to return facts are computed for some `callee` with the null fact and - * the callee's return value is assigned to a variable. + * the callee's return value is assigned to a vairbale. * Creates a taint, if necessary. * * @param callee The called method. * @param call The call. * @return Some variable fact, if necessary. Otherwise none. */ - protected def createTaints(callee: Method, call: NewJavaStatement): Set[Fact] + protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[Fact] /** * Called, when the call to return facts are computed for some `callee`. @@ -227,18 +225,18 @@ abstract class NewForwardTaintProblem(project: SomeProject) * @param call The call. * @return Some FlowFact, if necessary. Otherwise None. */ - protected def createFlowFact(callee: Method, call: NewJavaStatement, - in: Fact): Option[FlowFact] + protected def createFlowFact(callee: DeclaredMethod, call: DeclaredMethodJavaStatement, + in: Set[Fact]): Option[FlowFact] /** * If a parameter is tainted, the result will also be tainted. * We assume that the callee does not call the source method. */ - override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { + override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = { super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some(((call: NewJavaStatement, successor: NewJavaStatement, in: Fact) ⇒ { + case Some(_) ⇒ Some(((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[Fact]) ⇒ { val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params - if (call.stmt.astID == Assignment.ASTID && (in match { + if (call.stmt.astID == Assignment.ASTID && in.exists { case Variable(index) ⇒ allParams.zipWithIndex.exists { case (param, _) if param.asVar.definedBy.contains(index) ⇒ true @@ -250,7 +248,7 @@ abstract class NewForwardTaintProblem(project: SomeProject) case _ ⇒ false } case _ ⇒ false - })) Set(Variable(call.index)) + }) Set(Variable(call.index)) else Set.empty }): OutsideAnalysisContextHandler) case None ⇒ None @@ -265,7 +263,7 @@ abstract class NewForwardTaintProblem(project: SomeProject) * @param callee The callee. * @return True, by default. */ - protected def relevantCallee(callee: Method): Boolean = true + protected def relevantCallee(callee: DeclaredMethod): Boolean = true /** * Creates new facts for an assignment. A new fact for the assigned variable will be created, @@ -276,19 +274,18 @@ abstract class NewForwardTaintProblem(project: SomeProject) * @param in The incoming facts * @return The new facts, created by the assignment */ - private def createNewTaints(expression: Expr[V], statement: NewJavaStatement, in: Fact): Set[Fact] = { - val in_set: Set[Fact] = Set(in) // dirty fix to reuse old code + private def createNewTaints(expression: Expr[V], statement: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = expression.astID match { case Var.ASTID ⇒ val definedBy = expression.asVar.definedBy - in_set.collect { + in.collect { case Variable(index) if definedBy.contains(index) ⇒ Variable(statement.index) } case ArrayLoad.ASTID ⇒ val loadExpression = expression.asArrayLoad val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy - if (in_set.exists { + if (in.exists { // One specific array element may be tainted case ArrayElement(index, taintedElement) ⇒ val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) @@ -303,7 +300,7 @@ abstract class NewForwardTaintProblem(project: SomeProject) case GetField.ASTID ⇒ val get = expression.asGetField val objectDefinedBy = get.objRef.asVar.definedBy - if (in_set.exists { + if (in.exists { // The specific field may be tainted case InstanceField(index, _, taintedField) ⇒ taintedField == get.name && objectDefinedBy.contains(index) @@ -316,7 +313,7 @@ abstract class NewForwardTaintProblem(project: SomeProject) Set.empty case GetStatic.ASTID ⇒ val get = expression.asGetStatic - if (in_set.contains(StaticField(get.declaringClass, get.name))) + if (in.contains(StaticField(get.declaringClass, get.name))) Set(Variable(statement.index)) else Set.empty case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | @@ -325,7 +322,6 @@ abstract class NewForwardTaintProblem(project: SomeProject) acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) case _ ⇒ Set.empty } - } /** * Checks, if the result of some variable expression could be tainted. @@ -334,13 +330,13 @@ abstract class NewForwardTaintProblem(project: SomeProject) * @param in The current data flow facts. * @return True, if the expression could be tainted */ - private def isTainted(expression: Expr[V], in: Fact): Boolean = { + private def isTainted(expression: Expr[V], in: Set[Fact]): Boolean = { val definedBy = expression.asVar.definedBy - expression.isVar && (in match { + expression.isVar && in.exists { case Variable(index) ⇒ definedBy.contains(index) case ArrayElement(index, _) ⇒ definedBy.contains(index) case InstanceField(index, _, _) ⇒ definedBy.contains(index) case _ ⇒ false - }) + } } } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala new file mode 100644 index 0000000000..0673873db8 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala @@ -0,0 +1,38 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.old.taint + +import org.opalj.br.Method +import org.opalj.tac.fpcf.analyses.ifds.old.UnbalancedReturnFact +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact + +/** + * The unbalanced return fact of this analysis. + * + * @param index The index, at which the analyzed method is called by some caller. + * @param innerFact The fact, which will hold in the caller context after the call. + * @param callChain The current call chain from the sink. + */ +case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Seq[Method]) + extends UnbalancedReturnFact[Fact] with Fact + +trait TaintProblem[C, Statement, IFDSFact] { + + /** + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. + * + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. + */ + protected def sanitizesReturnValue(callee: C): Boolean + + /** + * Called in callToReturnFlow. This method can return facts, which will be removed after + * `callee` was called. I.e. the method could sanitize parameters. + * + * @param call The call statement. + * @param in The facts, which hold before the call. + * @return Facts, which will be removed from `in` after the call. + */ + protected def sanitizeParameters(call: Statement, in: Set[IFDSFact]): Set[IFDSFact] +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 8422d20746..34a14f5de1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -1,23 +1,29 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.br.DeclaredMethod -import org.opalj.br.analyses.SomeProject -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.ifds.{AbstractIFDSAnalysis, JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.br.Method +import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} import org.opalj.tac._ +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.old.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} -abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[Fact](project) with TaintProblem[DeclaredMethod, JavaStatement, Fact] { +abstract class ForwardTaintProblem(project: SomeProject) + extends JavaIFDSProblem[Fact](project) + with TaintProblem[Method, JavaStatement, Fact] { + val declaredMethods = project.get(DeclaredMethodsKey) override def nullFact: Fact = NullFact + override def needsPredecessor(statement: JavaStatement): Boolean = false + /** * If a variable gets assigned a tainted value, the variable will be tainted. */ - override def normalFlow(statement: JavaStatement, successor: Option[JavaStatement], - in: Set[Fact]): Set[Fact] = + override def normalFlow(statement: JavaStatement, in: Fact, predecessor: Option[JavaStatement]): Set[Fact] = { + val in_set = Set(in) // dirty hack for old code statement.stmt.astID match { case Assignment.ASTID ⇒ - in ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) + in_set ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) case ArrayStore.ASTID ⇒ val store = statement.stmt.asArrayStore val definedBy = store.arrayRef.asVar.definedBy @@ -25,57 +31,58 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem if (isTainted(store.value, in)) { if (arrayIndex.isDefined) // Taint a known array index - definedBy.foldLeft(in) { (c, n) ⇒ + definedBy.foldLeft(in_set) { (c, n) ⇒ c + ArrayElement(n, arrayIndex.get) } else // Taint the whole array if the index is unknown - definedBy.foldLeft(in) { (c, n) ⇒ + definedBy.foldLeft(in_set) { (c, n) ⇒ c + Variable(n) } } else if (arrayIndex.isDefined && definedBy.size == 1) // Untaint if possible - in - ArrayElement(definedBy.head, arrayIndex.get) - else in + in_set - ArrayElement(definedBy.head, arrayIndex.get) + else in_set case PutField.ASTID ⇒ val put = statement.stmt.asPutField val definedBy = put.objRef.asVar.definedBy if (isTainted(put.value, in)) - definedBy.foldLeft(in) { (in, defSite) ⇒ + definedBy.foldLeft(in_set) { (in, defSite) ⇒ in + InstanceField(defSite, put.declaringClass, put.name) } else - in + in_set case PutStatic.ASTID ⇒ val put = statement.stmt.asPutStatic if (isTainted(put.value, in)) - in + StaticField(put.declaringClass, put.name) + in_set + StaticField(put.declaringClass, put.name) else - in - case _ ⇒ in + in_set + case _ ⇒ in_set } + } /** * Propagates tainted parameters to the callee. If a call to the sink method with a tainted * parameter is detected, no call-to-start * edges will be created. */ - override def callFlow(call: JavaStatement, callee: DeclaredMethod, - in: Set[Fact], a: (DeclaredMethod, Fact)): Set[Fact] = { + override def callFlow(call: JavaStatement, callee: Method, in: Fact): Set[Fact] = { + val in_set = Set(in) // compatilibity for old code val callObject = asCall(call.stmt) val allParams = callObject.allParams var facts = Set.empty[Fact] if (relevantCallee(callee)) { val allParamsWithIndices = allParams.zipWithIndex - in.foreach { + in_set.foreach { // Taint formal parameter if actual parameter is tainted case Variable(index) ⇒ allParamsWithIndices.foreach { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( paramIndex, - callee.definedMethod.isStatic + callee.isStatic )) case _ ⇒ // Nothing to do } @@ -85,7 +92,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem allParamsWithIndices.foreach { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ facts += ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), + AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.isStatic), taintedIndex ) case _ ⇒ // Nothing to do @@ -96,10 +103,10 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem // Only if the formal parameter is of a type that may have that field! allParamsWithIndices.foreach { case (param, pIndex) if param.asVar.definedBy.contains(index) && - (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || - project.classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ + (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic) != -1 || + project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) ⇒ facts += InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), + AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic), declClass, taintedField ) case _ ⇒ // Nothing to do @@ -123,18 +130,18 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * Creates new taints and FlowFacts, if necessary. * If the sanitize method was called, nothing will be tainted. */ - override def returnFlow(call: JavaStatement, callee: DeclaredMethod, exit: JavaStatement, - successor: JavaStatement, in: Set[Fact]): Set[Fact] = { - + override def returnFlow(exit: JavaStatement, in: Fact, call: JavaStatement, callFact: Fact): Set[Fact] = { + val in_set = Set(in) // dirty hack for compatibility with old code + val callee = exit.callable() /** * Checks whether the callee's formal parameter is of a reference type. */ def isRefTypeParam(index: Int): Boolean = if (index == -1) true else { - val parameterOffset = if (callee.definedMethod.isStatic) 0 else 1 + val parameterOffset = if (callee.isStatic) 0 else 1 callee.descriptor.parameterType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) - parameterOffset ).isReferenceType } @@ -143,25 +150,25 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem val callStatement = asCall(call.stmt) val allParams = callStatement.allParams var flows: Set[Fact] = Set.empty - in.foreach { + in_set.foreach { // Taint actual parameter if formal parameter is tainted case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(Variable) // Taint element of actual parameter if element of formal parameter is tainted case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ // Taint field of actual parameter if field of formal parameter is tainted val param = - allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) + allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) param.asVar.definedBy.foreach { defSite ⇒ flows += InstanceField(defSite, declClass, taintedField) } @@ -177,7 +184,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem // Propagate taints of the return value if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy - in.foreach { + in_set.foreach { case Variable(index) if returnValueDefinedBy.contains(index) ⇒ flows += Variable(call.index) case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ @@ -197,23 +204,21 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem } /** - * Removes taints according to `sanitizeParamters`. + * Removes taints according to `sanitizesParameter`. */ - override def callToReturnFlow(call: JavaStatement, successor: JavaStatement, - in: Set[Fact], - source: (DeclaredMethod, Fact)): Set[Fact] = - in -- sanitizeParameters(call, in) + override def callToReturnFlow(call: JavaStatement, in: Fact): Set[Fact] = + if (sanitizesParameter(call, in)) Set() else Set(in) /** * Called, when the exit to return facts are computed for some `callee` with the null fact and - * the callee's return value is assigned to a vairbale. + * the callee's return value is assigned to a variable. * Creates a taint, if necessary. * * @param callee The called method. * @param call The call. * @return Some variable fact, if necessary. Otherwise none. */ - protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] + protected def createTaints(callee: Method, call: JavaStatement): Set[Fact] /** * Called, when the call to return facts are computed for some `callee`. @@ -223,18 +228,18 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @param call The call. * @return Some FlowFact, if necessary. Otherwise None. */ - protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, - in: Set[Fact]): Option[FlowFact] + protected def createFlowFact(callee: Method, call: JavaStatement, + in: Fact): Option[FlowFact] /** * If a parameter is tainted, the result will also be tainted. * We assume that the callee does not call the source method. */ - override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = { + override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some(((call: JavaStatement, successor: JavaStatement, in: Set[Fact]) ⇒ { + case Some(_) ⇒ Some(((call: JavaStatement, successor: JavaStatement, in: Fact) ⇒ { val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params - if (call.stmt.astID == Assignment.ASTID && in.exists { + if (call.stmt.astID == Assignment.ASTID && (in match { case Variable(index) ⇒ allParams.zipWithIndex.exists { case (param, _) if param.asVar.definedBy.contains(index) ⇒ true @@ -246,7 +251,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem case _ ⇒ false } case _ ⇒ false - }) Set(Variable(call.index)) + })) Set(Variable(call.index)) else Set.empty }): OutsideAnalysisContextHandler) case None ⇒ None @@ -261,7 +266,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @param callee The callee. * @return True, by default. */ - protected def relevantCallee(callee: DeclaredMethod): Boolean = true + protected def relevantCallee(callee: Method): Boolean = true /** * Creates new facts for an assignment. A new fact for the assigned variable will be created, @@ -272,18 +277,19 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @param in The incoming facts * @return The new facts, created by the assignment */ - private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: Set[Fact]): Set[Fact] = + private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: Fact): Set[Fact] = { + val in_set: Set[Fact] = Set(in) // dirty fix to reuse old code expression.astID match { case Var.ASTID ⇒ val definedBy = expression.asVar.definedBy - in.collect { + in_set.collect { case Variable(index) if definedBy.contains(index) ⇒ Variable(statement.index) } case ArrayLoad.ASTID ⇒ val loadExpression = expression.asArrayLoad val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy - if (in.exists { + if (in_set.exists { // One specific array element may be tainted case ArrayElement(index, taintedElement) ⇒ val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) @@ -298,7 +304,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem case GetField.ASTID ⇒ val get = expression.asGetField val objectDefinedBy = get.objRef.asVar.definedBy - if (in.exists { + if (in_set.exists { // The specific field may be tainted case InstanceField(index, _, taintedField) ⇒ taintedField == get.name && objectDefinedBy.contains(index) @@ -311,7 +317,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem Set.empty case GetStatic.ASTID ⇒ val get = expression.asGetStatic - if (in.contains(StaticField(get.declaringClass, get.name))) + if (in_set.contains(StaticField(get.declaringClass, get.name))) Set(Variable(statement.index)) else Set.empty case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | @@ -320,6 +326,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) case _ ⇒ Set.empty } + } /** * Checks, if the result of some variable expression could be tainted. @@ -328,13 +335,13 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @param in The current data flow facts. * @return True, if the expression could be tainted */ - private def isTainted(expression: Expr[V], in: Set[Fact]): Boolean = { + private def isTainted(expression: Expr[V], in: Fact): Boolean = { val definedBy = expression.asVar.definedBy - expression.isVar && in.exists { + expression.isVar && (in match { case Variable(index) ⇒ definedBy.contains(index) case ArrayElement(index, _) ⇒ definedBy.contains(index) case InstanceField(index, _, _) ⇒ definedBy.contains(index) case _ ⇒ false - } + }) } } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala deleted file mode 100644 index 1a25153864..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewTaintProblem.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds.taint - -trait NewTaintProblem[C, Statement, IFDSFact] { - - /** - * Checks, if some `callee` is a sanitizer, which sanitizes its return value. - * In this case, no return flow facts will be created. - * - * @param callee The method, which was called. - * @return True, if the method is a sanitizer. - */ - protected def sanitizesReturnValue(callee: C): Boolean - - /** - * Called in callToReturnFlow. This method can return whether the input fact - * will be removed after `callee` was called. I.e. the method could sanitize parameters. - * - * @param call The call statement. - * @param in The fact which holds before the call. - * @return Whether in will be removed after the call. - */ - protected def sanitizesParameter(call: Statement, in: IFDSFact): Boolean -} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index 64f11ce364..b53b270570 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -1,11 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.br.{Method, ObjectType} +import org.opalj.br.ObjectType import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} -import org.opalj.tac.fpcf.analyses.ifds.AbstractIFDSAnalysis.V -import org.opalj.tac.fpcf.analyses.ifds.UnbalancedReturnFact import org.opalj.tac.{Assignment, Expr, Stmt} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V trait Fact extends AbstractIFDSFact @@ -57,16 +56,6 @@ case class FlowFact(flow: Seq[Callable]) extends Fact { } } -/** - * The unbalanced return fact of this analysis. - * - * @param index The index, at which the analyzed method is called by some caller. - * @param innerFact The fact, which will hold in the caller context after the call. - * @param callChain The current call chain from the sink. - */ -case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Seq[Method]) - extends UnbalancedReturnFact[Fact] with Fact - trait TaintProblem[C, Statement, IFDSFact] { /** @@ -79,14 +68,14 @@ trait TaintProblem[C, Statement, IFDSFact] { protected def sanitizesReturnValue(callee: C): Boolean /** - * Called in callToReturnFlow. This method can return facts, which will be removed after - * `callee` was called. I.e. the method could sanitize parameters. + * Called in callToReturnFlow. This method can return whether the input fact + * will be removed after `callee` was called. I.e. the method could sanitize parameters. * * @param call The call statement. - * @param in The facts, which hold before the call. - * @return Facts, which will be removed from `in` after the call. + * @param in The fact which holds before the call. + * @return Whether in will be removed after the call. */ - protected def sanitizeParameters(call: Statement, in: Set[IFDSFact]): Set[IFDSFact] + protected def sanitizesParameter(call: Statement, in: IFDSFact): Boolean } object TaintProblem { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala deleted file mode 100644 index 9c9d6fafef..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/NewTaint.scala +++ /dev/null @@ -1,23 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.properties - -import org.opalj.fpcf.PropertyKey -import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} -import org.opalj.tac.fpcf.analyses.ifds.NewJavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact - -case class NewTaint(flows: Map[NewJavaStatement, Set[Fact]]) extends IFDSProperty[NewJavaStatement, Fact] { - - override type Self = NewTaint - override def create(result: Map[NewJavaStatement, Set[Fact]]): IFDSProperty[NewJavaStatement, Fact] = new NewTaint(result) - - override def key: PropertyKey[NewTaint] = NewTaint.key -} - -object NewTaint extends IFDSPropertyMetaInformation[NewJavaStatement, Fact] { - - override type Self = NewTaint - override def create(result: Map[NewJavaStatement, Set[Fact]]): IFDSProperty[NewJavaStatement, Fact] = new NewTaint(result) - - val key: PropertyKey[NewTaint] = PropertyKey.create("NewTaint", new NewTaint(Map.empty)) -} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala new file mode 100644 index 0000000000..317526d0db --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala @@ -0,0 +1,23 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.properties + +import org.opalj.fpcf.PropertyKey +import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} +import org.opalj.tac.fpcf.analyses.ifds.old.DeclaredMethodJavaStatement +import org.opalj.tac.fpcf.analyses.ifds.taint.Fact + +case class OldTaint(flows: Map[DeclaredMethodJavaStatement, Set[Fact]]) extends IFDSProperty[DeclaredMethodJavaStatement, Fact] { + + override type Self = OldTaint + override def create(result: Map[DeclaredMethodJavaStatement, Set[Fact]]): IFDSProperty[DeclaredMethodJavaStatement, Fact] = new OldTaint(result) + + override def key: PropertyKey[OldTaint] = OldTaint.key +} + +object OldTaint extends IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] { + + override type Self = OldTaint + override def create(result: Map[DeclaredMethodJavaStatement, Set[Fact]]): IFDSProperty[DeclaredMethodJavaStatement, Fact] = new OldTaint(result) + + val key: PropertyKey[OldTaint] = PropertyKey.create("OldTaint", new OldTaint(Map.empty)) +} diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala index 3647385d62..8bc3659c3c 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -1,11 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.br.DeclaredMethod -import org.opalj.br.analyses.SomeProject -import org.opalj.fpcf.PropertyStore -import org.opalj.ifds.IFDSPropertyMetaInformation -import org.opalj.tac.fpcf.analyses.ifds.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, JavaMethod, JavaStatement} +import org.opalj.br.Method +import org.opalj.br.analyses.{DeclaredMethodsKey, ProjectInformationKeys, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSPropertyMetaInformation} +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} import org.opalj.tac.fpcf.properties.Taint /** @@ -14,34 +16,33 @@ import org.opalj.tac.fpcf.properties.Taint * * @author Mario Trageser */ -class ForwardTaintAnalysisFixture(implicit val project: SomeProject) - extends ForwardIFDSAnalysis(new ForwardTaintProblemFixture(project), Taint) +class ForwardTaintAnalysisFixture(project: SomeProject) + extends IFDSAnalysis()(project, new ForwardTaintProblemFixture(project), Taint) class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) { - /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(DeclaredMethod, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ + override val entryPoints: Seq[(Method, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") .flatMap(classFile ⇒ classFile.methods) - .filter(method ⇒ method.isPublic && outsideAnalysisContext(declaredMethods(method)).isEmpty) - .map(method ⇒ declaredMethods(method) → NullFact) + .filter(method ⇒ method.isPublic && outsideAnalysisContext(method).isEmpty) + .map(method ⇒ method → NullFact) /** * The sanitize method is a sanitizer. */ - override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = callee.name == "sanitize" + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" /** * We do not sanitize paramters. */ - override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizesParameter(call: JavaStatement, in: Fact): Boolean = false /** * Creates a new variable fact for the callee, if the source was called. */ - override protected def createTaints(callee: DeclaredMethod, call: JavaStatement): Set[Fact] = + override protected def createTaints(callee: Method, call: JavaStatement): Set[Fact] = if (callee.name == "source") Set(Variable(call.index)) else Set.empty @@ -49,15 +50,15 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) * Create a FlowFact, if sink is called with a tainted variable. * Note, that sink does not accept array parameters. No need to handle them. */ - override protected def createFlowFact(callee: DeclaredMethod, call: JavaStatement, - in: Set[Fact]): Option[FlowFact] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(JavaMethod(call.method)))) + override protected def createFlowFact(callee: Method, call: JavaStatement, + in: Fact): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) Some(FlowFact(Seq(JavaMethod(call.method)))) else None } -object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact] { - - override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture()(p) - +object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture(p) override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(Taint)) + override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala deleted file mode 100644 index fca1bc59ac..0000000000 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/NewForwardTaintAnalysisFixture.scala +++ /dev/null @@ -1,64 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds.taint - -import org.opalj.br.Method -import org.opalj.br.analyses.{DeclaredMethodsKey, ProjectInformationKeys, SomeProject} -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.fpcf.{PropertyBounds, PropertyStore} -import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSPropertyMetaInformation} -import org.opalj.tac.cg.TypeProviderKey -import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, NewJavaStatement} -import org.opalj.tac.fpcf.properties.NewTaint - -/** - * An analysis that checks, if the return value of the method `source` can flow to the parameter of - * the method `sink`. - * - * @author Mario Trageser - */ -class NewForwardTaintAnalysisFixture(project: SomeProject) - extends IFDSAnalysis()(project, new NewForwardTaintProblemFixture(project), NewTaint) - -class NewForwardTaintProblemFixture(p: SomeProject) extends NewForwardTaintProblem(p) { - /** - * The analysis starts with all public methods in TaintAnalysisTestClass. - */ - override val entryPoints: Seq[(Method, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ - classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") - .flatMap(classFile ⇒ classFile.methods) - .filter(method ⇒ method.isPublic && outsideAnalysisContext(method).isEmpty) - .map(method ⇒ method → NullFact) - - /** - * The sanitize method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" - - /** - * We do not sanitize paramters. - */ - override protected def sanitizesParameter(call: NewJavaStatement, in: Fact): Boolean = false - - /** - * Creates a new variable fact for the callee, if the source was called. - */ - override protected def createTaints(callee: Method, call: NewJavaStatement): Set[Fact] = - if (callee.name == "source") Set(Variable(call.index)) - else Set.empty - - /** - * Create a FlowFact, if sink is called with a tainted variable. - * Note, that sink does not accept array parameters. No need to handle them. - */ - override protected def createFlowFact(callee: Method, call: NewJavaStatement, - in: Fact): Option[FlowFact] = - if (callee.name == "sink" && in == Variable(-2)) Some(FlowFact(Seq(JavaMethod(call.method)))) - else None -} - -object NewForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact, Method, NewJavaStatement] { - override def init(p: SomeProject, ps: PropertyStore) = new NewForwardTaintAnalysisFixture(p) - override def property: IFDSPropertyMetaInformation[NewJavaStatement, Fact] = NewTaint - override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(NewTaint)) - override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) -} diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala similarity index 83% rename from OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala rename to OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala index a53196cccb..c1eabfe200 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/BackwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala @@ -1,12 +1,15 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds.taint +package org.opalj.tac.fpcf.analyses.ifds.taint.old -import org.opalj.br.{DeclaredMethod, Method} import org.opalj.br.analyses.SomeProject +import org.opalj.br.{DeclaredMethod, Method} import org.opalj.fpcf.PropertyStore import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ifds._ -import org.opalj.tac.fpcf.properties.Taint +import org.opalj.tac.fpcf.analyses.ifds.old._ +import org.opalj.tac.fpcf.analyses.ifds.old.taint.BackwardTaintProblem +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, Variable} +import org.opalj.tac.fpcf.properties.OldTaint case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Method]) extends UnbalancedReturnFact[Fact] with Fact @@ -18,7 +21,7 @@ case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Met * @author Mario Trageser */ class BackwardTaintAnalysisFixture(implicit val project: SomeProject) - extends BackwardIFDSAnalysis(new BackwardTaintProblemFixture(project), Taint) + extends BackwardIFDSAnalysis(new BackwardTaintProblemFixture(project), OldTaint) class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p) { @@ -36,7 +39,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p /** * We do not sanitize paramters. */ - override protected def sanitizeParameters(call: JavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty /** * Create a flow fact, if a source method is called and the returned value is tainted. @@ -44,7 +47,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p * terminates. * In this case, callFlow would never be called and no FlowFact would be created. */ - override protected def createFlowFactAtCall(call: JavaStatement, in: Set[Fact], + override protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[Fact], source: (DeclaredMethod, Fact)): Option[FlowFact] = { if (in.exists { case Variable(index) ⇒ index == call.index @@ -82,5 +85,5 @@ object BackwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact] override def init(p: SomeProject, ps: PropertyStore) = new BackwardTaintAnalysisFixture()(p) - override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala new file mode 100644 index 0000000000..5674798082 --- /dev/null +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala @@ -0,0 +1,66 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint.old + +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.PropertyStore +import org.opalj.ifds.IFDSPropertyMetaInformation +import org.opalj.tac.fpcf.analyses.ifds.JavaMethod +import org.opalj.tac.fpcf.analyses.ifds.old.{DeclaredMethodJavaStatement, ForwardIFDSAnalysis, IFDSAnalysisScheduler} +import org.opalj.tac.fpcf.analyses.ifds.old.taint.ForwardTaintProblem +import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact, Variable} +import org.opalj.tac.fpcf.properties.OldTaint + +/** + * An analysis that checks, if the return value of the method `source` can flow to the parameter of + * the method `sink`. + * + * @author Mario Trageser + */ +class ForwardTaintAnalysisFixture(implicit val project: SomeProject) + extends ForwardIFDSAnalysis(new ForwardTaintProblemFixture(project), OldTaint) + +class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) { + + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(DeclaredMethod, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ + classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") + .flatMap(classFile ⇒ classFile.methods) + .filter(method ⇒ method.isPublic && outsideAnalysisContext(declaredMethods(method)).isEmpty) + .map(method ⇒ declaredMethods(method) → NullFact) + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = callee.name == "sanitize" + + /** + * We do not sanitize paramters. + */ + override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[Fact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact(callee: DeclaredMethod, call: DeclaredMethodJavaStatement, + in: Set[Fact]): Option[FlowFact] = + if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(JavaMethod(call.method)))) + else None +} + +object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact] { + + override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture()(p) + + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint +} From 847baa5ba05427913a02aa8a12b6ac8d483198fe Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Sat, 2 Apr 2022 01:51:33 +0200 Subject: [PATCH 34/67] First native flows working --- .../scala/org/opalj/ll/LLPlayground.scala | 2 +- .../analyses/HerosVariableTypeAnalysis.scala | 53 ++--- .../HerosBackwardClassForNameAnalysis.scala | 16 +- .../HerosForwardClassForNameAnalysis.scala | 71 ++---- ...MultilingualForwardTaintAnalysisTest.scala | 13 +- .../org/opalj/ll/SimplePurityTests.scala | 9 +- .../main/scala/org/opalj/ifds/Callable.scala | 4 +- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 10 +- .../scala/org/opalj/ifds/IFDSProblem.scala | 10 +- .../main/scala/org/opalj/ll/LLVMProject.scala | 12 +- .../fpcf/analyses/SimplePurityAnalysis.scala | 10 +- .../ll/fpcf/analyses/ifds/Callable.scala | 6 +- .../analyses/ifds/NativeForwardICFG.scala | 13 +- .../analyses/ifds/NativeIFDSAnalysis.scala | 20 +- .../analyses/ifds/NativeIFDSProblem.scala | 2 +- .../ifds/taint/JavaForwardTaintAnalysis.scala | 223 +++++++++++++++--- .../taint/NativeForwardTaintAnalysis.scala | 8 +- .../taint/NativeForwardTaintProblem.scala | 52 +++- .../ifds/taint/NativeTaintProblem.scala | 13 +- .../main/scala/org/opalj/ll/llvm/Module.scala | 24 +- .../opalj/ll/llvm/{ => value}/Argument.scala | 7 +- .../ll/llvm/{ => value}/BasicBlock.scala | 21 +- .../opalj/ll/llvm/{ => value}/Function.scala | 18 +- .../ll/llvm/{ => value}/GlobalVariable.scala | 3 +- .../ll/llvm/{ => value}/Instruction.scala | 64 +++-- .../opalj/ll/llvm/value/ScalarConstants.scala | 10 + .../org/opalj/ll/llvm/{ => value}/Value.scala | 37 +-- .../tac/fpcf/analyses/ifds/Callable.scala | 4 +- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 18 +- .../ifds/old/AbstractIFDSAnalysis.scala | 13 - .../ifds/old/VariableTypeProblem.scala | 5 +- .../ifds/old/taint/BackwardTaintProblem.scala | 17 +- .../ifds/old/taint/ForwardTaintProblem.scala | 19 +- .../ifds/taint/ForwardTaintProblem.scala | 154 ++++++------ .../old/BackwardTaintAnalysisFixture.scala | 3 +- 35 files changed, 568 insertions(+), 396 deletions(-) rename OPAL/ll/src/main/scala/org/opalj/ll/llvm/{ => value}/Argument.scala (51%) rename OPAL/ll/src/main/scala/org/opalj/ll/llvm/{ => value}/BasicBlock.scala (68%) rename OPAL/ll/src/main/scala/org/opalj/ll/llvm/{ => value}/Function.scala (68%) rename OPAL/ll/src/main/scala/org/opalj/ll/llvm/{ => value}/GlobalVariable.scala (85%) rename OPAL/ll/src/main/scala/org/opalj/ll/llvm/{ => value}/Instruction.scala (84%) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/ScalarConstants.scala rename OPAL/ll/src/main/scala/org/opalj/ll/llvm/{ => value}/Value.scala (68%) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala index 0d09626866..ffba4b3b75 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala @@ -8,7 +8,7 @@ object LLPlayground { def main(args: Array[String]): Unit = { val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll").get - val function = module.get_function("jsmn_parse_string") + val function = module.function("jsmn_parse_string") println(function.name) function.viewLLVMCFG(false) function.viewCFG() diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala index 5c18d68234..267e732dad 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala @@ -1,43 +1,26 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.heros.analyses -import scala.annotation.tailrec +import heros.{FlowFunction, FlowFunctions, TwoElementSet} +import heros.flowfunc.{Identity, KillAll} +import org.opalj.br.{ArrayType, DeclaredMethod, FieldType, Method} +import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.collection.immutable.EmptyIntTrieSet +import org.opalj.fpcf.{FinalEP, PropertyStore} +import org.opalj.tac._ +import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.old.{CalleeType, VTAFact, VTANullFact, VariableType} +import org.opalj.tac.fpcf.properties.cg.Callees +import org.opalj.util.Milliseconds +import org.opalj.value.ValueInformation + import java.io.File import java.util import java.util.Collections +import scala.annotation.tailrec import scala.collection.JavaConverters._ -import heros.FlowFunctions -import heros.FlowFunction -import heros.flowfunc.Identity -import heros.TwoElementSet -import heros.flowfunc.KillAll -import org.opalj.util.Milliseconds -import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG -import org.opalj.collection.immutable.EmptyIntTrieSet -import org.opalj.fpcf.FinalEP -import org.opalj.fpcf.PropertyStore -import org.opalj.value.ValueInformation -import org.opalj.br.Method -import org.opalj.br.analyses.DeclaredMethods -import org.opalj.br.analyses.SomeProject -import org.opalj.br.ArrayType -import org.opalj.br.FieldType -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.br.DeclaredMethod -import org.opalj.tac.fpcf.properties.cg.Callees -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.Assignment -import org.opalj.tac.Expr -import org.opalj.tac.GetStatic -import org.opalj.tac.New -import org.opalj.tac.DUVar -import org.opalj.tac.ArrayLoad -import org.opalj.tac.ArrayStore -import org.opalj.tac.GetField -import org.opalj.tac.Var -import org.opalj.tac.ReturnValue -import org.opalj.tac.fpcf.analyses.ifds.old.{AbstractIFDSAnalysis, CalleeType, VTAFact, VTANullFact, VariableType} /** * An implementation of the IFDSBasedVariableTypeAnalysis in the Heros framework. @@ -107,7 +90,7 @@ class HerosVariableTypeAnalysis( allParams.iterator.zipWithIndex.foreach { case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ flow += VariableType( - AbstractIFDSAnalysis + JavaIFDSProblem .switchParamAndVariableIndex(parameterIndex, callee.isStatic), t, upperBound @@ -258,7 +241,7 @@ class HerosVariableTypeAnalysisRunner (method.descriptor.parameterTypes.zipWithIndex.collect { case (t, index) if t.isReferenceType ⇒ VariableType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, method.isStatic), + JavaIFDSProblem.switchParamAndVariableIndex(index, method.isStatic), t.asReferenceType, upperBound = true ) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala index 49b5677f23..5d68ea4f87 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -31,7 +31,7 @@ import org.opalj.tac.PutStatic import org.opalj.tac.ReturnValue import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner -import org.opalj.tac.fpcf.analyses.ifds.old.AbstractIFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, Fact, FlowFact, InstanceField, StaticField, Variable} @@ -143,14 +143,14 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) val paramIndex = pair._2 source match { case Variable(index) if param.definedBy.contains(index) ⇒ - Some(Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall))) + Some(Variable(JavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall))) case ArrayElement(index, taintedElement) if param.definedBy.contains(index) ⇒ Some(ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement + JavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement )) case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) ⇒ Some(InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), + JavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall), declaringClass, name )) case staticField: StaticField ⇒ Some(staticField) @@ -169,21 +169,21 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) val staticCall = callee.isStatic val thisOffset = if (staticCall) 0 else 1 val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) - .map(index ⇒ AbstractIFDSAnalysis.switchParamAndVariableIndex(index + thisOffset, staticCall)) + .map(index ⇒ JavaIFDSProblem.switchParamAndVariableIndex(index + thisOffset, staticCall)) source: Fact ⇒ (source match { case Variable(index) if formalParameterIndices.contains(index) ⇒ createNewTaints( - callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), statement + callStatement.allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), statement ) case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) ⇒ toArrayElement(createNewTaints( - callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + callStatement.allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), statement ), taintedElement) case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) ⇒ toInstanceField(createNewTaints( - callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + callStatement.allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), statement ), declaringClass, name) case staticField: StaticField ⇒ Set[Fact](staticField) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index f8aee3d983..31bc1b5ccf 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -1,50 +1,25 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.heros.analyses.taint +import heros.{FlowFunction, FlowFunctions, TwoElementSet} +import heros.flowfunc.{Identity, KillAll} +import org.opalj.br.{ClassHierarchy, DeclaredMethod, Method, ObjectType} +import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.{FinalEP, PropertyStore} +import org.opalj.tac._ +import org.opalj.tac.fpcf.analyses.heros.analyses.{HerosAnalysis, HerosAnalysisRunner} +import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties.cg.Callers +import org.opalj.util.Milliseconds + import java.io.File import java.util import java.util.Collections import scala.collection.JavaConverters._ -import heros.FlowFunction -import heros.FlowFunctions -import heros.TwoElementSet -import heros.flowfunc.Identity -import heros.flowfunc.KillAll -import org.opalj.util.Milliseconds -import org.opalj.fpcf.FinalEP -import org.opalj.fpcf.PropertyStore -import org.opalj.br.ClassHierarchy -import org.opalj.br.Method -import org.opalj.br.ObjectType -import org.opalj.br.analyses.DeclaredMethodsKey -import org.opalj.br.analyses.SomeProject -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.tac.fpcf.properties.cg.Callers -import org.opalj.br.DeclaredMethod -import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG -import org.opalj.tac.Assignment -import org.opalj.tac.Expr -import org.opalj.tac.GetField -import org.opalj.tac.GetStatic -import org.opalj.tac.PutField -import org.opalj.tac.PutStatic -import org.opalj.tac.ReturnValue -import org.opalj.tac.Var -import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} -import org.opalj.tac.ArrayLength -import org.opalj.tac.ArrayLoad -import org.opalj.tac.ArrayStore -import org.opalj.tac.BinaryExpr -import org.opalj.tac.Compare -import org.opalj.tac.NewArray -import org.opalj.tac.PrefixExpr -import org.opalj.tac.PrimitiveTypecastExpr -import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis -import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner -import org.opalj.tac.fpcf.analyses.ifds.old.AbstractIFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, Fact, FlowFact, InstanceField, StaticField, Variable} /** * An implementation of the ForwardClassForNameAnalysis in the Heros framework. @@ -202,7 +177,7 @@ class HerosForwardClassForNameAnalysis( .collect { case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ Variable( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic) + JavaIFDSProblem.switchParamAndVariableIndex(paramIdx, callee.isStatic) ) } .toSet[Fact] @@ -213,7 +188,7 @@ class HerosForwardClassForNameAnalysis( .collect { case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic), + JavaIFDSProblem.switchParamAndVariableIndex(paramIdx, callee.isStatic), taintedIndex ) } @@ -225,13 +200,13 @@ class HerosForwardClassForNameAnalysis( allParamsWithIndices .collect { case (param, paramIdx) if param.asVar.definedBy.contains(index) && - (AbstractIFDSAnalysis.switchParamAndVariableIndex( + (JavaIFDSProblem.switchParamAndVariableIndex( paramIdx, callee.isStatic ) != -1 || classHierarchy.isSubtypeOf(declClass, callee.classFile.thisType)) ⇒ InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIdx, callee.isStatic), + JavaIFDSProblem.switchParamAndVariableIndex(paramIdx, callee.isStatic), declClass, taintedField ) @@ -256,7 +231,7 @@ class HerosForwardClassForNameAnalysis( val parameterOffset = if (callee.isStatic) 0 else 1 callee.descriptor .parameterType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) - parameterOffset ) .isReferenceType @@ -270,7 +245,7 @@ class HerosForwardClassForNameAnalysis( case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ val params = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) params.asVar.definedBy.iterator.map(Variable).toSet @@ -278,7 +253,7 @@ class HerosForwardClassForNameAnalysis( // Taint element of actual parameter if element of formal parameter is tainted val params = asCall(stmt.stmt).allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) params.asVar.definedBy.iterator .map(ArrayElement(_, taintedIndex)) @@ -288,7 +263,7 @@ class HerosForwardClassForNameAnalysis( case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ // Taint field of actual parameter if field of formal parameter is tainted val params = - allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) + allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic)) params.asVar.definedBy.iterator .map(InstanceField(_, declClass, taintedField)) .asInstanceOf[Iterator[Fact]] diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index d285f45118..dc8a713336 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -1,13 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll -import org.opalj.br.analyses.{DeclaredMethodsKey, Project} +import org.opalj.br.analyses.Project import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.ifds.IFDSProperty import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler} import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact} -import org.opalj.ifds.IFDSProperty -import org.opalj.tac.fpcf.analyses.ifds.old.DeclaredMethodJavaStatement import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers @@ -23,23 +23,22 @@ class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers ) project.get(LLVMProjectKey) project.get(RTACallGraphKey) - val declaredMethods = project.get(DeclaredMethodsKey) val manager = project.get(FPCFAnalysesManagerKey) val (ps, analyses) = manager.runAll(JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler) for (method ← project.allMethodsWithBody) { val flows = - ps((declaredMethods(method), NullFact), JavaForwardTaintAnalysisScheduler.property.key) + ps((method, NullFact), JavaForwardTaintAnalysisScheduler.property.key) println("---METHOD: "+method.toJava+" ---") for { fact ← flows.ub - .asInstanceOf[IFDSProperty[DeclaredMethodJavaStatement, Fact]] + .asInstanceOf[IFDSProperty[JavaStatement, Fact]] .flows .values .flatten .toSet[Fact] } { fact match { - case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.signature).mkString(", ")) + case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.name).mkString(", ")) case _ ⇒ } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala index e575039335..c3e524d29d 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala @@ -4,6 +4,7 @@ package org.opalj.ll import org.opalj.br.analyses.Project import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.ll.fpcf.analyses.{EagerSimplePurityAnalysis, Impure, Pure} +import org.opalj.ll.llvm.value.Function import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers @@ -20,14 +21,14 @@ class SimplePurityTests extends AnyFunSpec with Matchers { val impureFunctionNames = propertyStore .finalEntities(Impure) - .asInstanceOf[Iterator[llvm.Function]] - .map(function ⇒ function.name()) + .asInstanceOf[Iterator[Function]] + .map(_.name) .toList impureFunctionNames should contain("impure_function") val pureFunctionNames = propertyStore .finalEntities(Pure) - .asInstanceOf[Iterator[llvm.Function]] - .map(function ⇒ function.name()) + .asInstanceOf[Iterator[Function]] + .map(_.name) .toList pureFunctionNames should contain("pure_function") } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/Callable.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/Callable.scala index e0ea97d5a2..8319a74e60 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/Callable.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/Callable.scala @@ -5,10 +5,10 @@ abstract class Callable { /** * The name of the Callable */ - def name(): String + def name: String /** * The full name of the Callable including its signature */ - def signature(): String + def signature: String } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 6bacadc905..76c62f4cf6 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -4,6 +4,7 @@ package org.opalj.ifds import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} import org.opalj.fpcf._ +import org.opalj.ifds.Dependees.Getter import scala.collection.{mutable, Set ⇒ SomeSet} @@ -33,6 +34,13 @@ case class Dependees[Work]() { dependees -= epk dependee.worklist } + + def getter()(implicit propertyStore: PropertyStore, work: Work): Getter = + (entity: Entity, propertyKey: PropertyKey[Property]) ⇒ get(entity, propertyKey) +} + +object Dependees { + type Getter = (Entity, PropertyKey[Property]) ⇒ SomeEOptionP } /** @@ -266,7 +274,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, // Let the concrete analysis decide what to do. for { successor ← successors - out ← outsideAnalysisHandler(call, successor, in) // ifds line 17 (only summary edges) + out ← outsideAnalysisHandler(call, successor, in, state.dependees.getter()) // ifds line 17 (only summary edges) } { propagate(successor, out, call) // ifds line 18 } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala index 8dd8e9966f..4c1434acf4 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ifds -import org.opalj.fpcf.ProperPropertyComputationResult +import org.opalj.ifds.Dependees.Getter /** * A framework for IFDS analyses. @@ -12,6 +12,8 @@ import org.opalj.fpcf.ProperPropertyComputationResult * @author Marc Clement */ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]](val icfg: ICFG[IFDSFact, C, S]) { + type Work = (S, IFDSFact, Option[S]) + /** * The null fact of this analysis. */ @@ -92,8 +94,8 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: State def needsPredecessor(statement: S): Boolean - type OutsideAnalysisContextHandler = ((S, S, IFDSFact) ⇒ Set[IFDSFact]) { - def apply(call: S, successor: S, in: IFDSFact): Set[IFDSFact] + type OutsideAnalysisContextHandler = ((S, S, IFDSFact, Getter) ⇒ Set[IFDSFact]) { + def apply(call: S, successor: S, in: IFDSFact, dependeesGetter: Getter): Set[IFDSFact] } /** @@ -110,6 +112,4 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: State * It returns facts, which hold after the call, excluding the call to return flow. */ def outsideAnalysisContext(callee: C): Option[OutsideAnalysisContextHandler] - - def specialCase(source: (C, IFDSFact), propertyKey: IFDSPropertyMetaInformation[S, IFDSFact]): Option[ProperPropertyComputationResult] = None } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala index 24f3bae178..c0aa2fb870 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala @@ -1,16 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll -import org.opalj.ll.llvm.{Function, Module, Reader} +import org.opalj.ll.llvm.{Module, Reader, value} class LLVMProject(val modules: Iterable[Module]) { - def functions(): Iterable[Function] = { - modules.flatMap(module ⇒ module.functions()) - } + def functions: Iterable[value.Function] = + modules.flatMap(module ⇒ module.functions) - def function(name: String): Option[Function] = { - functions.find(_.name() == name) - } + def function(name: String): Option[value.Function] = + functions.find(_.name == name) } object LLVMProject { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala index 2bf1ec58b0..2f9236a9db 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala @@ -5,7 +5,7 @@ import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} import org.opalj.br.fpcf.{BasicFPCFEagerAnalysisScheduler, FPCFAnalysis, FPCFAnalysisScheduler} import org.opalj.fpcf._ import org.opalj.ll.LLVMProjectKey -import org.opalj.ll.llvm.{Function, GlobalVariable, Store} +import org.opalj.ll.llvm.value.{Function, GlobalVariable, Store} sealed trait SimplePurityPropertyMetaInformation extends PropertyMetaInformation { final type Self = SimplePurity @@ -42,11 +42,11 @@ object SimplePurity extends SimplePurityPropertyMetaInformation { class SimplePurityAnalysis(val project: SomeProject) extends FPCFAnalysis { def analyzeSimplePurity(function: Function): ProperPropertyComputationResult = { function - .basicBlocks() - .flatMap(_.instructions()) + .basicBlocks + .flatMap(_.instructions) .foreach { case instruction: Store ⇒ - instruction.dst() match { + instruction.dst match { case _: GlobalVariable ⇒ return Result(function, Impure) case _ ⇒ Unit @@ -79,7 +79,7 @@ object EagerSimplePurityAnalysis ): FPCFAnalysis = { val analysis = new SimplePurityAnalysis(project) val llvm_project = project.get(LLVMProjectKey) - propertyStore.scheduleEagerComputationsForEntities(llvm_project.functions())( + propertyStore.scheduleEagerComputationsForEntities(llvm_project.functions)( analysis.analyzeSimplePurity ) analysis diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala index f7bb1b7366..a965fcc4f4 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala @@ -1,10 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.ll.llvm.Function import org.opalj.ifds.Callable +import org.opalj.ll.llvm.value.Function case class LLVMFunction(val function: Function) extends Callable { - override def name(): String = function.name() - override def signature(): String = function.name() // TODO: add signature + override def name: String = function.name + override def signature: String = function.name // TODO: add signature } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index 4956c58aa7..6d438f78c9 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -2,7 +2,7 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.ifds.{AbstractIFDSFact, ICFG} -import org.opalj.ll.llvm.{Function, Instruction, Ret, Terminator} +import org.opalj.ll.llvm.value.{Call, Function, Instruction, Ret, Terminator} class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Function, LLVMStatement] { /** @@ -12,7 +12,7 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun * @return The statements at which the analysis starts. */ override def startStatements(callable: Function): Set[LLVMStatement] = { - Set(LLVMStatement(callable.entryBlock().firstInstruction())) + Set(LLVMStatement(callable.entryBlock.firstInstruction)) } /** @@ -22,8 +22,8 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun * @return The successor statements */ override def nextStatements(statement: LLVMStatement): Set[LLVMStatement] = { - if (!statement.instruction.isTerminator) return Set(LLVMStatement(statement.instruction.next().get)) - statement.instruction.asInstanceOf[Instruction with Terminator].successors().map(LLVMStatement(_)).toSet + if (!statement.instruction.isTerminator) return Set(LLVMStatement(statement.instruction.next.get)) + statement.instruction.asInstanceOf[Instruction with Terminator].successors.map(LLVMStatement(_)).toSet } /** @@ -33,7 +33,10 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun * @return All callables possibly called at the statement or None, if the statement does not * contain a call. */ - override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[Function]] = None //TODO + override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[Function]] = statement.instruction match { + case call: Call ⇒ Some(Set(call.calledValue)) + case _ ⇒ None + } override def isExitStatement(statement: LLVMStatement): Boolean = statement.instruction match { case Ret(_) ⇒ true diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala index be36845fcc..7ac6716a57 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala @@ -6,7 +6,8 @@ import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSProblem, Statement} import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} import org.opalj.ll.LLVMProjectKey -import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} +import org.opalj.ll.llvm.value.{BasicBlock, Instruction} +import org.opalj.ll.llvm.value /** * @@ -16,23 +17,24 @@ import org.opalj.ll.llvm.{BasicBlock, Function, Instruction} */ class NativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( project: SomeProject, - ifdsProblem: IFDSProblem[IFDSFact, Function, LLVMStatement], + ifdsProblem: IFDSProblem[IFDSFact, value.Function, LLVMStatement], propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] ) - extends IFDSAnalysis[IFDSFact, Function, LLVMStatement]()(project, ifdsProblem, propertyKey) + extends IFDSAnalysis[IFDSFact, value.Function, LLVMStatement]()(project, ifdsProblem, propertyKey) /** * A statement that is passed to the concrete analysis. * * @param instruction The LLVM instruction. */ -case class LLVMStatement(instruction: Instruction) extends Statement[Function, BasicBlock] { - def function(): Function = instruction.function - def basicBlock(): BasicBlock = instruction.parent - override def node(): BasicBlock = basicBlock - override def callable(): Function = function +case class LLVMStatement(instruction: Instruction) extends Statement[value.Function, BasicBlock] { + def function: value.Function = instruction.function + def basicBlock: BasicBlock = instruction.parent + override def node: BasicBlock = basicBlock + override def callable: value.Function = function + override def toString: String = s"${function.name}\n\t${instruction}\n\t${function}" } -abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysisScheduler[IFDSFact, Function, LLVMStatement] { +abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysisScheduler[IFDSFact, value.Function, LLVMStatement] { override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index db4ac4bf4a..51c13ac346 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -6,7 +6,7 @@ import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.PropertyStore import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem} import org.opalj.ll.LLVMProjectKey -import org.opalj.ll.llvm.Function +import org.opalj.ll.llvm.value.Function abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, Function, LLVMStatement](new NativeForwardICFG[Fact]) { final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 0cc536787f..31148a7765 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -1,43 +1,45 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds.taint -import org.opalj.br.DeclaredMethod +import org.opalj.br.Method import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} -import org.opalj.fpcf.{PropertyBounds, PropertyStore} -import org.opalj.ifds.IFDSPropertyMetaInformation +import org.opalj.fpcf._ +import org.opalj.ifds.Dependees.Getter +import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.ll.LLVMProjectKey +import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement import org.opalj.ll.fpcf.properties.NativeTaint +import org.opalj.ll.llvm.value.{Function, Ret} import org.opalj.tac.fpcf.analyses.ifds.taint._ -import org.opalj.tac.fpcf.analyses.ifds.JavaMethod -import org.opalj.tac.fpcf.analyses.ifds.old.{ForwardIFDSAnalysis, IFDSAnalysisScheduler, DeclaredMethodJavaStatement, taint} -import org.opalj.tac.fpcf.properties.{TACAI, OldTaint} +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties.{TACAI, Taint} -class SimpleJavaForwardTaintProblem(p: SomeProject) extends taint.ForwardTaintProblem(p) { +class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { val llvmProject = p.get(LLVMProjectKey) /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(DeclaredMethod, Fact)] = (for { + override val entryPoints: Seq[(Method, Fact)] = (for { m ← p.allMethodsWithBody if (m.name == "main") - } yield declaredMethods(m) -> NullFact) + } yield m -> NullFact) /** * The sanitize method is a sanitizer. */ - override protected def sanitizesReturnValue(callee: DeclaredMethod): Boolean = + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" /** * We do not sanitize parameters. */ - override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizesParameter(call: JavaStatement, in: Fact): Boolean = false /** * Creates a new variable fact for the callee, if the source was called. */ - override protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[Fact] = + override protected def createTaints(callee: Method, call: JavaStatement): Set[Fact] = if (callee.name == "source") Set(Variable(call.index)) else Set.empty @@ -46,40 +48,197 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends taint.ForwardTaintPr * Note, that sink does not accept array parameters. No need to handle them. */ override protected def createFlowFact( - callee: DeclaredMethod, - call: DeclaredMethodJavaStatement, - in: Set[Fact] + callee: Method, + call: JavaStatement, + in: Fact ): Option[FlowFact] = - if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(JavaMethod(call.method)))) + if (callee.name == "sink" && in == Variable(-2)) + Some(FlowFact(Seq(JavaMethod(call.method), JavaMethod(callee)))) else None // Multilingual additions here - - override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = { - def handleNativeMethod(call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = { - //val method = callee.definedMethod - + override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { + def handleNativeMethod(call: JavaStatement, successor: JavaStatement, in: Fact, dependeesGetter: Getter): Set[Fact] = { // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names - //val nativeMethodName = "Java_"+method.classFile.fqn+"_"+method.name - //val function = llvmProject.function(nativeMethodName) - //val foo = propertyStore((function.get, source._2), NativeTaint) - Set.empty[Fact] + val nativeFunctionName = "Java_"+callee.classFile.fqn+"_"+callee.name + val function = llvmProject.function(nativeFunctionName).get + var result = Set.empty[Fact] + for (entryFact ← nativeCallFlow(call, function, in, callee)) { // ifds line 14 + val e = (function, entryFact) + val exitFacts: Map[LLVMStatement, Set[NativeFact]] = + dependeesGetter(e, NativeTaint.key).asInstanceOf[EOptionP[(LLVMStatement, NativeFact), IFDSProperty[LLVMStatement, NativeFact]]] match { + case ep: FinalEP[_, IFDSProperty[LLVMStatement, NativeFact]] ⇒ + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, NativeFact]] ⇒ + ep.ub.flows + case _ ⇒ + Map.empty + } + for { + (exitStatement, exitStatementFacts) ← exitFacts // ifds line 15.2 + exitStatementFact ← exitStatementFacts // ifds line 15.3 + } { + result ++= nativeReturnFlow(exitStatement, exitStatementFact, call, in, callee) + } + } + result } - if (callee.definedMethod.isNative) { + if (callee.isNative) { Some(handleNativeMethod _) } else { super.outsideAnalysisContext(callee) } } + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + private def nativeCallFlow( + call: JavaStatement, + callee: Function, + in: Fact, + nativeCallee: Method + ): Set[NativeFact] = { + val callObject = asCall(call.stmt) + val allParams = callObject.allParams + val allParamsWithIndices = allParams.zipWithIndex + in match { + // Taint formal parameter if actual parameter is tainted + case Variable(index) ⇒ + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + Some(NativeVariable(callee.argument(paramIndex + 2))) // offset JNIEnv + jobject + case _ ⇒ None // Nothing to do + }.toSet + + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) ⇒ + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + Some(NativeArrayElement(callee.argument(paramIndex + 2), taintedIndex)) // offset JNIEnv + jobject + case _ ⇒ None // Nothing to do + }.toSet + + case InstanceField(index, declaredClass, taintedField) ⇒ + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + Some(JavaInstanceField(paramIndex + 2, declaredClass, taintedField)) // TODO subtype check + case _ ⇒ None // Nothing to do + }.toSet + + case StaticField(classType, fieldName) ⇒ Set(JavaStaticField(classType, fieldName)) + + case NullFact ⇒ Set(NativeNullFact) + + case _ ⇒ Set() // Nothing to do + + } + } + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + private def nativeReturnFlow( + exit: LLVMStatement, + in: NativeFact, + call: JavaStatement, + callFact: Fact, + nativeCallee: Method + ): Set[Fact] = { + /** + * Checks whether the callee's formal parameter is of a reference type. + */ + def isRefTypeParam(index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (nativeCallee.isStatic) 0 else 1 + nativeCallee.descriptor.parameterType( + JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic) + - parameterOffset + ).isReferenceType + } + + if (sanitizesReturnValue(nativeCallee)) return Set.empty + val callStatement = asCall(call.stmt) + val allParams = callStatement.allParams + var flows: Set[Fact] = Set.empty + in match { + // Taint actual parameter if formal parameter is tainted + case JavaVariable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(Variable) + + // Taint element of actual parameter if element of formal parameter is tainted + case JavaArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) + + case JavaInstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + // Taint field of actual parameter if field of formal parameter is tainted + val param = + allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic)) + param.asVar.definedBy.foreach { defSite ⇒ + flows += InstanceField(defSite, declClass, taintedField) + } + + case JavaStaticField(objectType, fieldName) ⇒ flows += StaticField(objectType, fieldName) + + // Track the call chain to the sink back + case NativeFlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ + flows += FlowFact(JavaMethod(call.method) +: flow) + case NativeNullFact ⇒ flows += NullFact + case _ ⇒ + } + + // Propagate taints of the return value + exit.instruction match { + case ret: Ret ⇒ { + in match { + case NativeVariable(value) if value == ret.value ⇒ flows += Variable(call.index) + // TODO + /*case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + flows += ArrayElement(call.index, taintedIndex) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + flows += InstanceField(call.index, declClass, taintedField)*/ + case NativeNullFact ⇒ + val taints = createTaints(nativeCallee, call) + if (taints.nonEmpty) flows ++= taints + case _ ⇒ // Nothing to do + } + } + case _ ⇒ + } + flows + } } -class SimpleJavaForwardTaintAnalysis(implicit val project: SomeProject) - extends ForwardIFDSAnalysis(new SimpleJavaForwardTaintProblem(project), OldTaint) +class SimpleJavaForwardTaintAnalysis(project: SomeProject) + extends IFDSAnalysis()(project, new SimpleJavaForwardTaintProblem(project), Taint) -object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact] { - override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis()(p) - override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint - override def requiredProjectInformation: ProjectInformationKeys = super.requiredProjectInformation ++ Seq(LLVMProjectKey) +object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.ub(NativeTaint)) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala index ca4bba3a30..dbc612e17a 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -4,9 +4,9 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.{PropertyBounds, PropertyStore} import org.opalj.ifds.IFDSPropertyMetaInformation -import org.opalj.ll.fpcf.analyses.ifds.{NativeIFDSAnalysis, LLVMFunction, LLVMStatement, NativeIFDSAnalysisScheduler} +import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeIFDSAnalysis, NativeIFDSAnalysisScheduler} import org.opalj.ll.fpcf.properties.NativeTaint -import org.opalj.ll.llvm.Function +import org.opalj.ll.llvm.value.Function class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaintProblem(p) { /** @@ -29,7 +29,7 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint * Creates a new variable fact for the callee, if the source was called. */ protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeFact] = - if (callee.name == "source") Set(NativeVariable(call)) + if (callee.name == "source") Set.empty //TODO Set(NativeVariable()) else Set.empty /** @@ -41,7 +41,7 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint call: LLVMStatement, in: Set[NativeFact] ): Option[NativeFlowFact] = - if (callee.name == "sink" && in.contains(NativeVariable(-2))) Some(NativeFlowFact(Seq(LLVMFunction(call.function)))) + if (callee.name == "sink" && in.contains(JavaVariable(-2))) Some(NativeFlowFact(Seq(LLVMFunction(call.function)))) else None } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index f83212d1ec..313ac8a57c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -2,8 +2,8 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject -import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeIFDSProblem} -import org.opalj.ll.llvm.{Function, PHI} +import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeIFDSProblem} +import org.opalj.ll.llvm.value.{Add, Alloca, Function, Load, PHI, Ret, Store} import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[Function, LLVMStatement, NativeFact] { @@ -20,7 +20,22 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * that the facts in `in` held before `statement` and `successor` will be * executed next. */ - override def normalFlow(statement: LLVMStatement, in: NativeFact, predecessor: Option[LLVMStatement]): Set[NativeFact] = ??? + override def normalFlow(statement: LLVMStatement, in: NativeFact, predecessor: Option[LLVMStatement]): Set[NativeFact] = statement.instruction match { + case _: Alloca ⇒ Set(in) + case store: Store ⇒ in match { + case NativeVariable(value) if value == store.src ⇒ Set(in, NativeVariable(store.dst)) + case _ ⇒ Set(in) + } + case load: Load ⇒ in match { + case NativeVariable(value) if value == load.src ⇒ Set(in, NativeVariable(load)) + case _ ⇒ Set(in) + } + case add: Add ⇒ in match { + case NativeVariable(value) if value == add.op1 || value == add.op2 ⇒ Set(in, NativeVariable(add)) + case _ ⇒ Set(in) + } + case _ ⇒ Set(in) + } /** * Computes the data flow for a call to start edge. @@ -28,11 +43,17 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @param call The analyzed call statement. * @param callee The called method, for which the data flow shall be computed. * @param in The fact which holds before the execution of the `call`. - * @param source The entity, which is analyzed. * @return The facts, which hold after the execution of `statement` under the assumption that * the facts in `in` held before `statement` and `statement` calls `callee`. */ - override def callFlow(call: LLVMStatement, callee: Function, in: NativeFact): Set[NativeFact] = ??? + override def callFlow(call: LLVMStatement, callee: Function, in: NativeFact): Set[NativeFact] = in match { + // Taint formal parameter if actual parameter is tainted + case NativeVariable(value) ⇒ if (callee.arguments.exists(_ == value)) Set(in) else Set() + // TODO pass other java taints + case NativeNullFact ⇒ Set(in) + case _ ⇒ Set() // Nothing to do + + } /** * Computes the data flow for an exit to return edge. @@ -44,7 +65,24 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * under the assumption that `in` held before the execution of `exit` and that * `successor` will be executed next. */ - override def returnFlow(exit: LLVMStatement, in: NativeFact, call: LLVMStatement, callFact: NativeFact): Set[NativeFact] = ??? + override def returnFlow(exit: LLVMStatement, in: NativeFact, call: LLVMStatement, callFact: NativeFact): Set[NativeFact] = { + var flows: Set[NativeFact] = in match { + case NativeVariable(value) ⇒ exit.instruction match { + case ret: Ret if ret.value == value ⇒ Set(in) + case _ ⇒ Set() + } + case NativeNullFact ⇒ Set(NativeNullFact) + case NativeFlowFact(flow) if !flow.contains(LLVMFunction(call.function)) ⇒ + Set(NativeFlowFact(LLVMFunction(call.function) +: flow)) + case _ ⇒ Set() + } + if (exit.callable.name == "sink") in match { + case NativeVariable(value) if value == exit.callable.argument(0) ⇒ + flows += NativeFlowFact(Seq(LLVMFunction(call.callable), LLVMFunction(exit.callable))) + case _ ⇒ + } + flows + } /** * Computes the data flow for a call to return edge. @@ -54,7 +92,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @return The facts, which hold after the call independently of what happens in the callee * under the assumption that `in` held before `call`. */ - override def callToReturnFlow(call: LLVMStatement, in: NativeFact): Set[NativeFact] = ??? + override def callToReturnFlow(call: LLVMStatement, in: NativeFact): Set[NativeFact] = Set(in) override def needsPredecessor(statement: LLVMStatement): Boolean = statement.instruction match { case PHI(_) ⇒ true diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala index 9bd08f0bab..cacd8d45e0 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala @@ -3,6 +3,7 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.ObjectType import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} +import org.opalj.ll.llvm.value.Value trait NativeFact extends AbstractIFDSFact @@ -11,9 +12,10 @@ case object NativeNullFact extends NativeFact with AbstractIFDSNullFact /** * A tainted variable. * - * @param definitionSite The variable's definition site. + * @param index The variable's definition site. */ -case class NativeVariable(definitionSite: Any) extends NativeFact +case class JavaVariable(index: Int) extends NativeFact +case class NativeVariable(value: Value) extends NativeFact /** * A tainted array element. @@ -21,7 +23,8 @@ case class NativeVariable(definitionSite: Any) extends NativeFact * @param index The array's definition site. * @param element The index of the tainted element in the array. */ -case class NativeArrayElement(index: Int, element: Int) extends NativeFact +case class JavaArrayElement(index: Int, element: Int) extends NativeFact +case class NativeArrayElement(value: Value, element: Int) extends NativeFact /** * A tainted static field. @@ -29,7 +32,7 @@ case class NativeArrayElement(index: Int, element: Int) extends NativeFact * @param classType The field's class. * @param fieldName The field's name. */ -case class NativeStaticField(classType: ObjectType, fieldName: String) extends NativeFact +case class JavaStaticField(classType: ObjectType, fieldName: String) extends NativeFact /** * A tainted instance field. @@ -38,7 +41,7 @@ case class NativeStaticField(classType: ObjectType, fieldName: String) extends N * @param classType The field's type. * @param fieldName The field's value. */ -case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends NativeFact +case class JavaInstanceField(index: Int, classType: ObjectType, fieldName: String) extends NativeFact /** * A path of method calls, originating from the analyzed method, over which a tainted variable diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala index 40a31005cd..ea8b9f806c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala @@ -2,34 +2,28 @@ package org.opalj.ll.llvm import org.bytedeco.llvm.LLVM.{LLVMModuleRef, LLVMValueRef} -import org.bytedeco.llvm.global.LLVM.{ - LLVMDisposeMessage, - LLVMGetFirstFunction, - LLVMGetNamedFunction, - LLVMGetNextFunction, - LLVMPrintModuleToString -} +import org.bytedeco.llvm.global.LLVM.{LLVMDisposeMessage, LLVMGetFirstFunction, LLVMGetNamedFunction, LLVMGetNextFunction, LLVMPrintModuleToString} +import org.opalj.ll.llvm.value.{Value, Function} case class Module(ref: LLVMModuleRef) { - def functions(): FunctionIterator = { + def functions: FunctionIterator = { new FunctionIterator(LLVMGetFirstFunction(ref)) } - def repr(): String = { + def repr: String = { val bytePointer = LLVMPrintModuleToString(ref) val string = bytePointer.getString LLVMDisposeMessage(bytePointer) string } - def get_function(name: String): Function = { - val func_ref = LLVMGetNamedFunction(ref, name) - Value(func_ref) match { - case _: NullValue ⇒ + def function(name: String): Function = + Value(LLVMGetNamedFunction(ref, name)) match { + case None ⇒ throw new IllegalArgumentException("Unknown function '"+name+"'") - case function: Function ⇒ function + case Some(function: Function) ⇒ function + case Some(_) ⇒ throw new IllegalStateException("Expected LLVMGetNamedFunction to return a Function ref") } - } } class FunctionIterator(var ref: LLVMValueRef) extends Iterator[Function] { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Argument.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala similarity index 51% rename from OPAL/ll/src/main/scala/org/opalj/ll/llvm/Argument.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala index b93d9faa28..2c280e9939 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Argument.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala @@ -1,11 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.llvm +package org.opalj.ll.llvm.value import org.bytedeco.llvm.LLVM.LLVMValueRef -import org.bytedeco.llvm.global.LLVM.{LLVMArgumentValueKind, LLVMGetParamParent, LLVMGetValueKind} +import org.bytedeco.llvm.global.LLVM.{LLVMGetParamParent, LLVMGetValueKind, LLVMArgumentValueKind} +import org.opalj.ll.llvm.value case class Argument(ref: LLVMValueRef) extends Value(ref) { assert(LLVMGetValueKind(ref) == LLVMArgumentValueKind, "ref has to be an argument") - def parent(): Function = Function(LLVMGetParamParent(ref)) + def parent(): value.Function = value.Function(LLVMGetParamParent(ref)) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala similarity index 68% rename from OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala index 614d81871b..cb3638420d 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/BasicBlock.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala @@ -1,23 +1,24 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.llvm +package org.opalj.ll.llvm.value import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} -import org.bytedeco.llvm.global.LLVM.{LLVMBasicBlockAsValue, LLVMGetBasicBlockName, LLVMGetBasicBlockParent, LLVMGetBasicBlockTerminator, LLVMGetFirstInstruction, LLVMGetLastInstruction, LLVMGetNextInstruction} +import org.bytedeco.llvm.global.LLVM._ import org.opalj.graphs.Node +import org.opalj.ll.llvm.value case class BasicBlock(block_ref: LLVMBasicBlockRef) extends Value(LLVMBasicBlockAsValue(block_ref)) with Node { - def parent() = Function(LLVMGetBasicBlockParent(block_ref)) - def instructions(): InstructionIterator = new InstructionIterator(LLVMGetFirstInstruction(block_ref)) - def firstInstruction(): Instruction = Instruction(LLVMGetFirstInstruction(block_ref)) - def lastInstruction(): Instruction = Instruction(LLVMGetLastInstruction(block_ref)) + def parent = Function(LLVMGetBasicBlockParent(block_ref)) + def instructions: InstructionIterator = new InstructionIterator(LLVMGetFirstInstruction(block_ref)) + def firstInstruction: Instruction = Instruction(LLVMGetFirstInstruction(block_ref)) + def lastInstruction: Instruction = Instruction(LLVMGetLastInstruction(block_ref)) - def terminator(): Option[Instruction with Terminator] = { + def terminator(): Option[Instruction with value.Terminator] = { OptionalInstruction(LLVMGetBasicBlockTerminator(block_ref)) match { case Some(terminator) ⇒ { - assert(terminator.isTerminator()) - Some(terminator.asInstanceOf[Instruction with Terminator]) + assert(terminator.isTerminator) + Some(terminator.asInstanceOf[Instruction with value.Terminator]) } case None ⇒ None } @@ -29,7 +30,7 @@ case class BasicBlock(block_ref: LLVMBasicBlockRef) * Returns a human readable representation (HRR) of this node. */ override def toHRR: Option[String] = { - Some(name()) //TODO: maybe add more info + Some(name) //TODO: maybe add more info } /** diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala similarity index 68% rename from OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala index e51860ba2b..ce405e74ae 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Function.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala @@ -1,28 +1,26 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.llvm +package org.opalj.ll.llvm.value import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} -import org.bytedeco.llvm.global.LLVM.{LLVMFunctionValueKind, LLVMGetEntryBasicBlock, LLVMGetFirstBasicBlock, LLVMGetFirstParam, LLVMGetNextBasicBlock, LLVMGetNextParam, LLVMGetValueKind, LLVMViewFunctionCFG, LLVMViewFunctionCFGOnly} +import org.bytedeco.llvm.global.LLVM._ import org.opalj.io.writeAndOpen case class Function(ref: LLVMValueRef) extends Value(ref) { assert(LLVMGetValueKind(ref) == LLVMFunctionValueKind, "ref has to be a function") - def basicBlocks(): BasicBlockIterator = { + def basicBlocks: BasicBlockIterator = { new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) } - def arguments(): ArgumentIterator = { + def arguments: ArgumentIterator = { new ArgumentIterator(LLVMGetFirstParam(ref)) } + def argument(index: Int): Argument = Argument(LLVMGetParam(ref, index)) - def entryBlock(): BasicBlock = { - BasicBlock(LLVMGetEntryBasicBlock(ref)) - } + def entryBlock: BasicBlock = BasicBlock(LLVMGetEntryBasicBlock(ref)) def viewCFG(): Unit = { - val entryNode = entryBlock() - val cfg_dot = org.opalj.graphs.toDot(Set(entryNode)) + val cfg_dot = org.opalj.graphs.toDot(Set(entryBlock)) writeAndOpen(cfg_dot, name+"-CFG", ".gv") } @@ -35,7 +33,7 @@ case class Function(ref: LLVMValueRef) extends Value(ref) { } override def toString: String = { - s"Function(${name()}(${arguments.map(_.typ().repr()).mkString(", ")}))" + s"Function(${name}(${arguments.map(_.typ.repr).mkString(", ")}))" } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/GlobalVariable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala similarity index 85% rename from OPAL/ll/src/main/scala/org/opalj/ll/llvm/GlobalVariable.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala index 4ded4873ba..6736c2eac7 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/GlobalVariable.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala @@ -1,6 +1,5 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ - -package org.opalj.ll.llvm +package org.opalj.ll.llvm.value import org.bytedeco.llvm.LLVM.LLVMValueRef diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala similarity index 84% rename from OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala index a9c14b1869..3a4ea95d78 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala @@ -1,8 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.llvm +package org.opalj.ll.llvm.value import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM._ +import org.opalj.ll.llvm.Type object OptionalInstruction { def apply(ref: LLVMValueRef): Option[Instruction] = { @@ -89,33 +90,39 @@ object Instruction { trait Terminator { val ref: LLVMValueRef - def numSuccessors(): Int = LLVMGetNumSuccessors(ref) - def hasSuccessors(): Boolean = numSuccessors() > 0 + def numSuccessors: Int = LLVMGetNumSuccessors(ref) + def hasSuccessors: Boolean = numSuccessors > 0 def getSuccessor(i: Int) = BasicBlock(LLVMGetSuccessor(ref, i)) - def foreachSuccessor(f: BasicBlock ⇒ Unit): Unit = { - (0 to numSuccessors() - 1).foreach(i ⇒ f(getSuccessor(i))) - } - def successors(): Seq[Instruction] = { - (0 to numSuccessors() - 1).map(i ⇒ getSuccessor(i).firstInstruction()) - } + def foreachSuccessor(f: BasicBlock ⇒ Unit): Unit = + (0 to numSuccessors - 1).foreach(i ⇒ f(getSuccessor(i))) + def successors: Seq[Instruction] = + (0 to numSuccessors - 1).map(i ⇒ getSuccessor(i).firstInstruction) } sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { - def isTerminator(): Boolean = !LLVMIsATerminatorInst(ref).isNull - def parent(): BasicBlock = BasicBlock(LLVMGetInstructionParent(ref)) - def function(): Function = parent.parent - def next(): Option[Instruction] = OptionalInstruction(LLVMGetNextInstruction(ref)) + def isTerminator: Boolean = LLVMIsATerminatorInst(ref) != null + def parent: BasicBlock = BasicBlock(LLVMGetInstructionParent(ref)) + def function: Function = parent.parent + def next: Option[Instruction] = OptionalInstruction(LLVMGetNextInstruction(ref)) /*def successors(): Seq[Instruction] = next match { case Some(successor) ⇒ Seq(successor) case None ⇒ Seq() }*/ override def toString: String = { - s"${this.getClass.getSimpleName}(${repr()})" + s"${this.getClass.getSimpleName}(${repr})" + } + + def numOperands: Int = LLVMGetNumOperands(ref) + def operand(index: Int): Value = { + assert(index < numOperands) + Value(LLVMGetOperand(ref, index)).get } } -case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator +case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator { + def value: Value = operand(0) +} case class Br(ref: LLVMValueRef) extends Instruction(ref) with Terminator case class Switch(ref: LLVMValueRef) extends Instruction(ref) with Terminator case class IndirectBr(ref: LLVMValueRef) extends Instruction(ref) with Terminator @@ -123,7 +130,10 @@ case class Invoke(ref: LLVMValueRef) extends Instruction(ref) with Terminator case class Unreachable(ref: LLVMValueRef) extends Instruction(ref) with Terminator case class CallBr(ref: LLVMValueRef) extends Instruction(ref) with Terminator case class FNeg(ref: LLVMValueRef) extends Instruction(ref) -case class Add(ref: LLVMValueRef) extends Instruction(ref) +case class Add(ref: LLVMValueRef) extends Instruction(ref) { + def op1: Value = operand(0) + def op2: Value = operand(1) +} case class FAdd(ref: LLVMValueRef) extends Instruction(ref) case class Sub(ref: LLVMValueRef) extends Instruction(ref) case class FSub(ref: LLVMValueRef) extends Instruction(ref) @@ -141,16 +151,15 @@ case class AShr(ref: LLVMValueRef) extends Instruction(ref) case class And(ref: LLVMValueRef) extends Instruction(ref) case class Or(ref: LLVMValueRef) extends Instruction(ref) case class Xor(ref: LLVMValueRef) extends Instruction(ref) -case class Alloca(ref: LLVMValueRef) extends Instruction(ref) -case class Load(ref: LLVMValueRef) extends Instruction(ref) +case class Alloca(ref: LLVMValueRef) extends Instruction(ref) { + def allocatedType: Type = Type(LLVMGetAllocatedType(ref)) +} +case class Load(ref: LLVMValueRef) extends Instruction(ref) { + def src: Value = operand(0) +} case class Store(ref: LLVMValueRef) extends Instruction(ref) { - def src(): Value = { - Value(LLVMGetOperand(ref, 0)).asInstanceOf[Value] - } - - def dst(): Value = { - Value(LLVMGetOperand(ref, 1)).asInstanceOf[Value] - } + def src: Value = operand(0) + def dst: Value = operand(1) } case class GetElementPtr(ref: LLVMValueRef) extends Instruction(ref) case class Trunc(ref: LLVMValueRef) extends Instruction(ref) @@ -169,7 +178,10 @@ case class AddrSpaceCast(ref: LLVMValueRef) extends Instruction(ref) case class ICmp(ref: LLVMValueRef) extends Instruction(ref) case class FCmp(ref: LLVMValueRef) extends Instruction(ref) case class PHI(ref: LLVMValueRef) extends Instruction(ref) -case class Call(ref: LLVMValueRef) extends Instruction(ref) +case class Call(ref: LLVMValueRef) extends Instruction(ref) { + def calledValue: Function = Function(LLVMGetCalledValue(ref)) + def calledFunctionType: Type = Type(LLVMGetCalledFunctionType(ref)) +} case class Select(ref: LLVMValueRef) extends Instruction(ref) case class UserOp1(ref: LLVMValueRef) extends Instruction(ref) case class UserOp2(ref: LLVMValueRef) extends Instruction(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/ScalarConstants.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/ScalarConstants.scala new file mode 100644 index 0000000000..e0cbdac4ad --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/ScalarConstants.scala @@ -0,0 +1,10 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.{LLVMConstIntGetSExtValue, LLVMConstIntGetZExtValue} + +case class ConstantIntValue(ref: LLVMValueRef) extends Value(ref) { + def zeroExtendedValue(): Long = LLVMConstIntGetZExtValue(ref) + def signExtendedValue(): Long = LLVMConstIntGetSExtValue(ref) +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala similarity index 68% rename from OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala index 187ee515cc..c79570d6d8 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Value.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala @@ -1,34 +1,39 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.llvm +package org.opalj.ll.llvm.value import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM._ +import org.opalj.ll.llvm.Type -class OptionalValue - -class Value(ref: LLVMValueRef) extends OptionalValue { - def repr(): String = { +class Value(ref: LLVMValueRef) { + def repr: String = { val bytePointer = LLVMPrintValueToString(ref) val string = bytePointer.getString LLVMDisposeMessage(bytePointer) string } - def name(): String = { + def name: String = { LLVMGetValueName(ref).getString } - def typ(): Type = Type(LLVMTypeOf(ref)) -} + def typ: Type = Type(LLVMTypeOf(ref)) + + val address = ref.address + override def equals(other: Any): Boolean = + other.isInstanceOf[Value] && address == other.asInstanceOf[Value].address -case class NullValue() extends OptionalValue + override def toString: String = { + s"${getClass.getSimpleName}(${repr})" + } +} object Value { - def apply(ref: LLVMValueRef): OptionalValue = { - if (ref == null) return NullValue() - if (ref.isNull) return NullValue() - LLVMGetValueKind(ref) match { - //LLVMArgumentValueKind + def apply(ref: LLVMValueRef): Option[Value] = { + if (ref == null) return None + if (ref.isNull) return None + Some(LLVMGetValueKind(ref) match { + case LLVMArgumentValueKind ⇒ Argument(ref) case LLVMBasicBlockValueKind ⇒ BasicBlock(LLVMValueAsBasicBlock(ref)) //LLVMMemoryUseValueKind //LLVMMemoryDefValueKind @@ -46,7 +51,7 @@ object Value { //LLVMConstantAggregateZeroValueKind //LLVMConstantDataArrayValueKind //LLVMConstantDataVectorValueKind - //LLVMConstantIntValueKind + case LLVMConstantIntValueKind ⇒ ConstantIntValue(ref) //LLVMConstantFPValueKind //LLVMConstantPointerNullValueKind //LLVMConstantTokenNoneValueKind @@ -55,6 +60,6 @@ object Value { case LLVMInstructionValueKind ⇒ Instruction(ref) //LLVMPoisonValueValueKind case valueKind ⇒ throw new IllegalArgumentException("unknown valueKind: "+valueKind) - } + }) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala index 4d22e7721a..bc6f608ebe 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/Callable.scala @@ -5,6 +5,6 @@ import org.opalj.br.Method import org.opalj.ifds.Callable case class JavaMethod(method: Method) extends Callable { - override def name(): String = method.name - override def signature(): String = method.toJava + override def name: String = method.name + override def signature: String = method.toJava } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 69f32f5d72..6af60a2848 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -4,6 +4,7 @@ package org.opalj.tac.fpcf.analyses.ifds import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.br.cfg.{CFG, CFGNode} +import org.opalj.ifds.Dependees.Getter import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, Statement} import org.opalj.tac.{Assignment, Call, DUVar, ExprStmt, Stmt, TACStmts} import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V @@ -58,9 +59,9 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) case _ ⇒ call.asMethodCall } - override def outsideAnalysisContext(callee: Method): Option[(JavaStatement, JavaStatement, Fact) ⇒ Set[Fact]] = callee.body.isDefined match { + override def outsideAnalysisContext(callee: Method): Option[(JavaStatement, JavaStatement, Fact, Getter) ⇒ Set[Fact]] = callee.body.isDefined match { case true ⇒ None - case false ⇒ Some((_call: JavaStatement, _successor: JavaStatement, in: Fact) ⇒ Set(in)) + case false ⇒ Some((_: JavaStatement, _: JavaStatement, in: Fact, _: Getter) ⇒ Set(in)) } } @@ -69,4 +70,17 @@ object JavaIFDSProblem { * The type of the TAC domain. */ type V = DUVar[ValueInformation] + + /** + * Converts the index of a method's formal parameter to its tac index in the method's scope and + * vice versa. + * + * @param index The index of a formal parameter in the parameter list or of a variable. + * @param isStaticMethod States, whether the method is static. + * @return A tac index if a parameter index was passed or a parameter index if a tac index was + * passed. + */ + def switchParamAndVariableIndex(index: Int, isStaticMethod: Boolean): Int = + (if (isStaticMethod) -2 else -1) - index + } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala index 904a261b22..238220583e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala @@ -833,19 +833,6 @@ object AbstractIFDSAnalysis { * When true, the cross product of exit and successor in returnFLow will be optimized. */ var OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW: Boolean = true - - /** - * Converts the index of a method's formal parameter to its tac index in the method's scope and - * vice versa. - * - * @param index The index of a formal parameter in the parameter list or of a variable. - * @param isStaticMethod States, whether the method is static. - * @return A tac index if a parameter index was passed or a parameter index if a tac index was - * passed. - */ - def switchParamAndVariableIndex(index: Int, isStaticMethod: Boolean): Int = - (if (isStaticMethod) -2 else -1) - index - } /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala index 3544c794ff..c808dbba5a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala @@ -5,6 +5,7 @@ import org.opalj.br._ import org.opalj.br.analyses.SomeProject import org.opalj.collection.immutable.EmptyIntTrieSet import org.opalj.ifds.{AbstractIFDSFact, SubsumableFact, SubsumableNullFact} +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem ⇒ NewJavaIFDSProblem} import org.opalj.tac._ import org.opalj.value.ValueInformation @@ -135,7 +136,7 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ // If this is the case, create a new fact for the method's formal parameter. flow += VariableType( - AbstractIFDSAnalysis + NewJavaIFDSProblem .switchParamAndVariableIndex(parameterIndex, callee.definedMethod.isStatic), t, upperBound @@ -316,7 +317,7 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] * subtype of its compile time type. */ VariableType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, method.definedMethod.isStatic), + NewJavaIFDSProblem.switchParamAndVariableIndex(index, method.definedMethod.isStatic), t.asReferenceType, upperBound = true ) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala index f261e267b6..6468a299b8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala @@ -5,7 +5,8 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.{DeclaredMethod, Method, ObjectType} import org.opalj.tac._ import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -import org.opalj.tac.fpcf.analyses.ifds.old.{AbstractIFDSAnalysis, JavaBackwardIFDSProblem, DeclaredMethodJavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem ⇒ NewJavaIFDSProblem} +import org.opalj.tac.fpcf.analyses.ifds.old.{JavaBackwardIFDSProblem, DeclaredMethodJavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint._ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[Fact, UnbalancedTaintFact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, Fact] { @@ -99,14 +100,14 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF val paramIndex = pair._2 in.foreach { case Variable(index) if param.definedBy.contains(index) ⇒ - flow += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall)) + flow += Variable(NewJavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall)) case ArrayElement(index, taintedElement) if param.definedBy.contains(index) ⇒ flow += ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement + NewJavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement ) case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) ⇒ flow += InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, staticCall), + NewJavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall), declaringClass, name ) case staticField: StaticField ⇒ flow += staticField @@ -333,21 +334,21 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF val staticCall = callee.definedMethod.isStatic val thisOffset = if (staticCall) 0 else 1 val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) - .map(index ⇒ AbstractIFDSAnalysis.switchParamAndVariableIndex(index + thisOffset, staticCall)) + .map(index ⇒ NewJavaIFDSProblem.switchParamAndVariableIndex(index + thisOffset, staticCall)) val facts = scala.collection.mutable.Set.empty[Fact] calleeFacts.foreach { case Variable(index) if formalParameterIndices.contains(index) ⇒ facts ++= createNewTaints( - callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), call + callStatement.allParams(NewJavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), call ) case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) ⇒ facts ++= createNewArrayElementTaints( - callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + callStatement.allParams(NewJavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), taintedElement, call ) case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) ⇒ facts ++= createNewInstanceFieldTaints( - callStatement.allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, staticCall)), + callStatement.allParams(NewJavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), declaringClass, name, call ) case staticField: StaticField ⇒ facts += staticField diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala index 3da067d12d..caa998a1e1 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala @@ -6,8 +6,9 @@ import org.opalj.br.analyses.SomeProject import org.opalj.tac._ import org.opalj.tac.fpcf.analyses.ifds.JavaMethod import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -import org.opalj.tac.fpcf.analyses.ifds.old.{AbstractIFDSAnalysis, JavaIFDSProblem, DeclaredMethodJavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.old.{JavaIFDSProblem, DeclaredMethodJavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem ⇒ NewJavaIFDSProblem} abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[Fact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, Fact] { override def nullFact: Fact = NullFact @@ -75,7 +76,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem case Variable(index) ⇒ allParamsWithIndices.foreach { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( + facts += Variable(NewJavaIFDSProblem.switchParamAndVariableIndex( paramIndex, callee.definedMethod.isStatic )) @@ -87,7 +88,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem allParamsWithIndices.foreach { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ facts += ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), + NewJavaIFDSProblem.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), taintedIndex ) case _ ⇒ // Nothing to do @@ -98,10 +99,10 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem // Only if the formal parameter is of a type that may have that field! allParamsWithIndices.foreach { case (param, pIndex) if param.asVar.definedBy.contains(index) && - (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || + (NewJavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || project.classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ facts += InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), + NewJavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), declClass, taintedField ) case _ ⇒ // Nothing to do @@ -136,7 +137,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem else { val parameterOffset = if (callee.definedMethod.isStatic) 0 else 1 callee.descriptor.parameterType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + NewJavaIFDSProblem.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) - parameterOffset ).isReferenceType } @@ -149,21 +150,21 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem // Taint actual parameter if formal parameter is tainted case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + NewJavaIFDSProblem.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(Variable) // Taint element of actual parameter if element of formal parameter is tainted case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) + NewJavaIFDSProblem.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ // Taint field of actual parameter if field of formal parameter is tainted val param = - allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) + allParams(NewJavaIFDSProblem.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) param.asVar.definedBy.foreach { defSite ⇒ flows += InstanceField(defSite, declClass, taintedField) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 34a14f5de1..7833c5f270 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -3,9 +3,9 @@ package org.opalj.tac.fpcf.analyses.ifds.taint import org.opalj.br.Method import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} +import org.opalj.ifds.Dependees.Getter import org.opalj.tac._ import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -import org.opalj.tac.fpcf.analyses.ifds.old.AbstractIFDSAnalysis import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} abstract class ForwardTaintProblem(project: SomeProject) @@ -20,45 +20,38 @@ abstract class ForwardTaintProblem(project: SomeProject) * If a variable gets assigned a tainted value, the variable will be tainted. */ override def normalFlow(statement: JavaStatement, in: Fact, predecessor: Option[JavaStatement]): Set[Fact] = { - val in_set = Set(in) // dirty hack for old code statement.stmt.astID match { case Assignment.ASTID ⇒ - in_set ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) + Set(in) ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) case ArrayStore.ASTID ⇒ val store = statement.stmt.asArrayStore val definedBy = store.arrayRef.asVar.definedBy val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) if (isTainted(store.value, in)) { - if (arrayIndex.isDefined) + if (arrayIndex.isDefined) { // Taint a known array index - definedBy.foldLeft(in_set) { (c, n) ⇒ - c + ArrayElement(n, arrayIndex.get) - } - else + Set(in) ++ definedBy.map { ArrayElement(_, arrayIndex.get) } + } else // Taint the whole array if the index is unknown - definedBy.foldLeft(in_set) { (c, n) ⇒ - c + Variable(n) - } - } else if (arrayIndex.isDefined && definedBy.size == 1) - // Untaint if possible - in_set - ArrayElement(definedBy.head, arrayIndex.get) - else in_set + Set(in) ++ definedBy.map { Variable(_) } + } else if (arrayIndex.isDefined && definedBy.size == 1 && in == ArrayElement(definedBy.head, arrayIndex.get)) { + // untaint + Set() + } else Set(in) case PutField.ASTID ⇒ val put = statement.stmt.asPutField val definedBy = put.objRef.asVar.definedBy if (isTainted(put.value, in)) - definedBy.foldLeft(in_set) { (in, defSite) ⇒ - in + InstanceField(defSite, put.declaringClass, put.name) - } + Set(in) ++ definedBy.map { InstanceField(_, put.declaringClass, put.name) } else - in_set + Set(in) case PutStatic.ASTID ⇒ val put = statement.stmt.asPutStatic if (isTainted(put.value, in)) - in_set + StaticField(put.declaringClass, put.name) + Set(in, StaticField(put.declaringClass, put.name)) else - in_set - case _ ⇒ in_set + Set(in) + case _ ⇒ Set(in) } } @@ -68,57 +61,52 @@ abstract class ForwardTaintProblem(project: SomeProject) * edges will be created. */ override def callFlow(call: JavaStatement, callee: Method, in: Fact): Set[Fact] = { - val in_set = Set(in) // compatilibity for old code val callObject = asCall(call.stmt) val allParams = callObject.allParams - var facts = Set.empty[Fact] - if (relevantCallee(callee)) { - val allParamsWithIndices = allParams.zipWithIndex - in_set.foreach { - // Taint formal parameter if actual parameter is tainted - case Variable(index) ⇒ - allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex( - paramIndex, - callee.isStatic - )) - case _ ⇒ // Nothing to do - } + val allParamsWithIndices = allParams.zipWithIndex + in match { + // Taint formal parameter if actual parameter is tainted + case Variable(index) ⇒ + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + Some(Variable(JavaIFDSProblem.switchParamAndVariableIndex( + paramIndex, + callee.isStatic + ))) + case _ ⇒ None // Nothing to do + }.toSet - // Taint element of formal parameter if element of actual parameter is tainted - case ArrayElement(index, taintedIndex) ⇒ - allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - facts += ArrayElement( - AbstractIFDSAnalysis.switchParamAndVariableIndex(paramIndex, callee.isStatic), - taintedIndex - ) - case _ ⇒ // Nothing to do - } + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) ⇒ + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + Some(ArrayElement( + JavaIFDSProblem.switchParamAndVariableIndex(paramIndex, callee.isStatic), + taintedIndex + )) + case _ ⇒ None // Nothing to do + }.toSet - case InstanceField(index, declClass, taintedField) ⇒ - // Taint field of formal parameter if field of actual parameter is tainted - // Only if the formal parameter is of a type that may have that field! - allParamsWithIndices.foreach { - case (param, pIndex) if param.asVar.definedBy.contains(index) && - (AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic) != -1 || - project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) ⇒ - facts += InstanceField( - AbstractIFDSAnalysis.switchParamAndVariableIndex(pIndex, callee.isStatic), - declClass, taintedField - ) - case _ ⇒ // Nothing to do - } + case InstanceField(index, declClass, taintedField) ⇒ + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.flatMap { + case (param, pIndex) if param.asVar.definedBy.contains(index) && + (JavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.isStatic) != -1 || + project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) ⇒ + Some(InstanceField( + JavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.isStatic), + declClass, taintedField + )) + case _ ⇒ None // Nothing to do + }.toSet - case sf: StaticField ⇒ facts += sf + case sf: StaticField ⇒ Set(sf) - case _ ⇒ // Nothing to do - } - } + case _ ⇒ Set() // Nothing to do - facts + } } /** @@ -131,7 +119,6 @@ abstract class ForwardTaintProblem(project: SomeProject) * If the sanitize method was called, nothing will be tainted. */ override def returnFlow(exit: JavaStatement, in: Fact, call: JavaStatement, callFact: Fact): Set[Fact] = { - val in_set = Set(in) // dirty hack for compatibility with old code val callee = exit.callable() /** * Checks whether the callee's formal parameter is of a reference type. @@ -141,7 +128,7 @@ abstract class ForwardTaintProblem(project: SomeProject) else { val parameterOffset = if (callee.isStatic) 0 else 1 callee.descriptor.parameterType( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) - parameterOffset ).isReferenceType } @@ -150,25 +137,25 @@ abstract class ForwardTaintProblem(project: SomeProject) val callStatement = asCall(call.stmt) val allParams = callStatement.allParams var flows: Set[Fact] = Set.empty - in_set.foreach { + in match { // Taint actual parameter if formal parameter is tainted case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(Variable) // Taint element of actual parameter if element of formal parameter is tainted case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ val param = allParams( - AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic) + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ // Taint field of actual parameter if field of formal parameter is tainted val param = - allParams(AbstractIFDSAnalysis.switchParamAndVariableIndex(index, callee.isStatic)) + allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic)) param.asVar.definedBy.foreach { defSite ⇒ flows += InstanceField(defSite, declClass, taintedField) } @@ -184,7 +171,7 @@ abstract class ForwardTaintProblem(project: SomeProject) // Propagate taints of the return value if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy - in_set.foreach { + in match { case Variable(index) if returnValueDefinedBy.contains(index) ⇒ flows += Variable(call.index) case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ @@ -237,7 +224,7 @@ abstract class ForwardTaintProblem(project: SomeProject) */ override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some(((call: JavaStatement, successor: JavaStatement, in: Fact) ⇒ { + case Some(_) ⇒ Some(((call: JavaStatement, successor: JavaStatement, in: Fact, _: Getter) ⇒ { val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params if (call.stmt.astID == Assignment.ASTID && (in match { case Variable(index) ⇒ @@ -259,15 +246,6 @@ abstract class ForwardTaintProblem(project: SomeProject) } - /** - * Checks, if a `callee` should be analyzed, i.e. callFlow and returnFlow should create facts. - * True by default. This method can be overwritten by a subclass. - * - * @param callee The callee. - * @return True, by default. - */ - protected def relevantCallee(callee: Method): Boolean = true - /** * Creates new facts for an assignment. A new fact for the assigned variable will be created, * if the expression contains a tainted variable @@ -278,18 +256,18 @@ abstract class ForwardTaintProblem(project: SomeProject) * @return The new facts, created by the assignment */ private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: Fact): Set[Fact] = { - val in_set: Set[Fact] = Set(in) // dirty fix to reuse old code expression.astID match { case Var.ASTID ⇒ val definedBy = expression.asVar.definedBy - in_set.collect { + in match { case Variable(index) if definedBy.contains(index) ⇒ - Variable(statement.index) + Set(Variable(statement.index)) + case _ ⇒ Set() } case ArrayLoad.ASTID ⇒ val loadExpression = expression.asArrayLoad val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy - if (in_set.exists { + if (in match { // One specific array element may be tainted case ArrayElement(index, taintedElement) ⇒ val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) @@ -304,7 +282,7 @@ abstract class ForwardTaintProblem(project: SomeProject) case GetField.ASTID ⇒ val get = expression.asGetField val objectDefinedBy = get.objRef.asVar.definedBy - if (in_set.exists { + if (in match { // The specific field may be tainted case InstanceField(index, _, taintedField) ⇒ taintedField == get.name && objectDefinedBy.contains(index) @@ -317,7 +295,7 @@ abstract class ForwardTaintProblem(project: SomeProject) Set.empty case GetStatic.ASTID ⇒ val get = expression.asGetStatic - if (in_set.contains(StaticField(get.declaringClass, get.name))) + if (in == StaticField(get.declaringClass, get.name)) Set(Variable(statement.index)) else Set.empty case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala index c1eabfe200..7da6c67073 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala @@ -10,6 +10,7 @@ import org.opalj.tac.fpcf.analyses.ifds.old._ import org.opalj.tac.fpcf.analyses.ifds.old.taint.BackwardTaintProblem import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, Variable} import org.opalj.tac.fpcf.properties.OldTaint +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Method]) extends UnbalancedReturnFact[Fact] with Fact @@ -29,7 +30,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") .flatMap(_.methods).filter(_.name == "sink") .map(method ⇒ declaredMethods(method) → - Variable(AbstractIFDSAnalysis.switchParamAndVariableIndex(0, isStaticMethod = true))) + Variable(JavaIFDSProblem.switchParamAndVariableIndex(0, isStaticMethod = true))) /** * The sanitize method is the sanitizer. From 7d47c1489355e9581c89e2468fcd9099cd8b0739 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 13 Apr 2022 13:19:34 +0200 Subject: [PATCH 35/67] Fix native flows --- .../main/scala/org/opalj/ifds/IFDSAnalysis.scala | 3 ++- .../ifds/taint/JavaForwardTaintAnalysis.scala | 14 +++++++++----- .../ifds/taint/NativeForwardTaintProblem.scala | 15 +++++++++++---- .../scala/org/opalj/ll/llvm/value/Function.scala | 6 +++++- .../org/opalj/ll/llvm/value/Instruction.scala | 5 +++++ 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 76c62f4cf6..608626308e 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -297,7 +297,8 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, private def concreteCallFlow(call: S, callee: C, in: IFDSFact)(implicit state: State, work: Work): Set[IFDSFact] = { var result = Set.empty[IFDSFact] - for (entryFact ← callFlow(call, callee, in)) { // ifds line 14 + val entryFacts = callFlow(call, callee, in) + for (entryFact ← entryFacts) { // ifds line 14 val e = (callee, entryFact) val exitFacts: Map[S, Set[IFDSFact]] = if (e == state.source) { // handle self dependency on our own because property store can't handle it diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 31148a7765..c9a6c24026 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -10,6 +10,7 @@ import org.opalj.ll.LLVMProjectKey import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement import org.opalj.ll.fpcf.properties.NativeTaint import org.opalj.ll.llvm.value.{Function, Ret} +import org.opalj.tac.Assignment import org.opalj.tac.fpcf.analyses.ifds.taint._ import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} import org.opalj.tac.fpcf.properties.{TACAI, Taint} @@ -63,7 +64,8 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( val nativeFunctionName = "Java_"+callee.classFile.fqn+"_"+callee.name val function = llvmProject.function(nativeFunctionName).get var result = Set.empty[Fact] - for (entryFact ← nativeCallFlow(call, function, in, callee)) { // ifds line 14 + val entryFacts = nativeCallFlow(call, function, in, callee) + for (entryFact ← entryFacts) { // ifds line 14 val e = (function, entryFact) val exitFacts: Map[LLVMStatement, Set[NativeFact]] = dependeesGetter(e, NativeTaint.key).asInstanceOf[EOptionP[(LLVMStatement, NativeFact), IFDSProperty[LLVMStatement, NativeFact]]] match { @@ -115,7 +117,8 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( case Variable(index) ⇒ allParamsWithIndices.flatMap { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - Some(NativeVariable(callee.argument(paramIndex + 2))) // offset JNIEnv + jobject + // TODO: this is passed + Some(NativeVariable(callee.argument(paramIndex + 1))) // offset JNIEnv case _ ⇒ None // Nothing to do }.toSet @@ -123,7 +126,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( case ArrayElement(index, taintedIndex) ⇒ allParamsWithIndices.flatMap { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - Some(NativeArrayElement(callee.argument(paramIndex + 2), taintedIndex)) // offset JNIEnv + jobject + Some(NativeArrayElement(callee.argument(paramIndex + 1), taintedIndex)) // offset JNIEnv case _ ⇒ None // Nothing to do }.toSet @@ -132,7 +135,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( // Only if the formal parameter is of a type that may have that field! allParamsWithIndices.flatMap { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - Some(JavaInstanceField(paramIndex + 2, declaredClass, taintedField)) // TODO subtype check + Some(JavaInstanceField(paramIndex + 1, declaredClass, taintedField)) // TODO subtype check case _ ⇒ None // Nothing to do }.toSet @@ -215,7 +218,8 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( exit.instruction match { case ret: Ret ⇒ { in match { - case NativeVariable(value) if value == ret.value ⇒ flows += Variable(call.index) + case NativeVariable(value) if value == ret.value && call.stmt.astID == Assignment.ASTID ⇒ + flows += Variable(call.index) // TODO /*case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ flows += ArrayElement(call.index, taintedIndex) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 313ac8a57c..3d12a963fb 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -3,7 +3,7 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeIFDSProblem} -import org.opalj.ll.llvm.value.{Add, Alloca, Function, Load, PHI, Ret, Store} +import org.opalj.ll.llvm.value.{Add, Alloca, Call, Function, Load, PHI, Ret, Store} import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[Function, LLVMStatement, NativeFact] { @@ -48,10 +48,13 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD */ override def callFlow(call: LLVMStatement, callee: Function, in: NativeFact): Set[NativeFact] = in match { // Taint formal parameter if actual parameter is tainted - case NativeVariable(value) ⇒ if (callee.arguments.exists(_ == value)) Set(in) else Set() + case NativeVariable(value) ⇒ call.instruction.asInstanceOf[Call].indexOfArgument(value) match { + case Some(index) ⇒ Set(NativeVariable(callee.argument(index))) + case None ⇒ Set() + } // TODO pass other java taints - case NativeNullFact ⇒ Set(in) - case _ ⇒ Set() // Nothing to do + case NativeNullFact ⇒ Set(in) + case _ ⇒ Set() // Nothing to do } @@ -69,6 +72,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD var flows: Set[NativeFact] = in match { case NativeVariable(value) ⇒ exit.instruction match { case ret: Ret if ret.value == value ⇒ Set(in) + case _: Ret ⇒ Set() case _ ⇒ Set() } case NativeNullFact ⇒ Set(NativeNullFact) @@ -76,6 +80,9 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD Set(NativeFlowFact(LLVMFunction(call.function) +: flow)) case _ ⇒ Set() } + if (exit.callable.name == "source") in match { + case NativeNullFact ⇒ flows += NativeVariable(call.instruction) + } if (exit.callable.name == "sink") in match { case NativeVariable(value) if value == exit.callable.argument(0) ⇒ flows += NativeFlowFact(Seq(LLVMFunction(call.callable), LLVMFunction(exit.callable))) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala index ce405e74ae..1581640360 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala @@ -15,7 +15,11 @@ case class Function(ref: LLVMValueRef) extends Value(ref) { def arguments: ArgumentIterator = { new ArgumentIterator(LLVMGetFirstParam(ref)) } - def argument(index: Int): Argument = Argument(LLVMGetParam(ref, index)) + def argumentCount: Int = LLVMCountParams(ref) + def argument(index: Int): Argument = { + assert(index < argumentCount) + Argument(LLVMGetParam(ref, index)) + } def entryBlock: BasicBlock = BasicBlock(LLVMGetEntryBasicBlock(ref)) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala index 3a4ea95d78..0b76e584ea 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala @@ -181,6 +181,11 @@ case class PHI(ref: LLVMValueRef) extends Instruction(ref) case class Call(ref: LLVMValueRef) extends Instruction(ref) { def calledValue: Function = Function(LLVMGetCalledValue(ref)) def calledFunctionType: Type = Type(LLVMGetCalledFunctionType(ref)) + def indexOfArgument(argument: Value): Option[Int] = { + for (i ← 0 to numOperands) + if (operand(i) == argument) return Some(i) + None + } } case class Select(ref: LLVMValueRef) extends Instruction(ref) case class UserOp1(ref: LLVMValueRef) extends Instruction(ref) From 5e9cfaf8d5eaefcd5fd4246c71c70da61dc3654c Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 6 May 2022 12:55:18 +0200 Subject: [PATCH 36/67] Cleanup --- .../scala/org/opalj/ll/LLPlayground.scala | 16 - .../validate/src/test/resources/llvm/a.out | Bin 15936 -> 0 bytes .../src/main/resources/org/opalj/ll/.gitkeep | 0 OPAL/ll/src/main/scala/org/opalj/ll/.gitkeep | 0 .../ll/src/test/resources/org/opalj/ll/jsmn.h | 471 ------- .../ll/src/test/resources/org/opalj/ll/test.c | 19 - .../src/test/resources/org/opalj/ll/test.ll | 58 - .../test/resources/org/opalj/ll/test_jsmn.c | 13 - .../test/resources/org/opalj/ll/test_jsmn.ll | 1223 ----------------- OPAL/ll/src/test/scala/org/opalj/ll/.gitkeep | 0 10 files changed, 1800 deletions(-) delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala delete mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/a.out delete mode 100644 OPAL/ll/src/main/resources/org/opalj/ll/.gitkeep delete mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/.gitkeep delete mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/jsmn.h delete mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/test.c delete mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/test.ll delete mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.c delete mode 100644 OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll delete mode 100644 OPAL/ll/src/test/scala/org/opalj/ll/.gitkeep diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala deleted file mode 100644 index ffba4b3b75..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/ll/LLPlayground.scala +++ /dev/null @@ -1,16 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj -package ll - -import org.opalj.ll.llvm.Reader - -object LLPlayground { - - def main(args: Array[String]): Unit = { - val module = Reader.readIR("./OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll").get - val function = module.function("jsmn_parse_string") - println(function.name) - function.viewLLVMCFG(false) - function.viewCFG() - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/a.out b/DEVELOPING_OPAL/validate/src/test/resources/llvm/a.out deleted file mode 100755 index dc75789c0de99e572d31211054ccc3a0ab572ac2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15936 zcmeHOU2Ggz6+XLm61Pq4b)3NFr_BIK2vxnY9mkj^O~ya#tSje-)Bz(Drfcumo@#ez znVqHUl0r}@$ZN!L=aa;px;>D?;w7yJlRTh9~=^<(2)kWu`rz`9f4KdG6 zyj4_W8+d%8Z*c>nC9c+7ESEFb)VRzOV@9Ey1&9rB&e5u@C?(J#s z=}r{wM3*8^zo-qmrVbrB0nyViPnhtjya5kNEBU`%xxVn?hG&1J-Fh2XZk;QJ8QR)`0~z)VaSMK|rZMlNmTq2O3~cM=Gu zeDuJfk>UOUqbt#s*jp>_=|<*M&dxK_z`CP2I_eim1g*`eWu8i#t$S$n+Zp@a4}OHx z9fb}GMyVXsm8NSz>L6grhw~+@;^Mll^cp<-^Qpk_6lNgIK$w9r17QZj41^g7GZ1Fr zBbtG~wLI{z)R}jhQgf@`+6|C;X5NifUr(KRrRj>wNOkWGgsVFi@!Yaa#}e7o9=U<0s-}p*u_D1T=V&&LqvU9%k)zrd5p%ue7&SCiblZlpX-&XZe{FSs_U0E2)>?J(zlVsUa1)ntJ1o+ght( z4c8FlQ~ReM`5bl%N^K`nvv1yf3P*8u)u$1OoV@ZM?NR*OXX||+%Iwp(XG=?S$=eOd zrIWtFQ2)rk(72-myZ<1zvD`ff%8&VuEhKN#VCTA3XzFsKrjrd9;pUI;RI8WiH_l5@ zJl#Bt{hLiL&6So?m$%lViWqhC`{=7xv8(R<0y;bAaTa4IaT;k1YL08ON-c#FW+2Q! zn1L_@VFtnsgc%4k5N06EK$w9r10SUf&~vzHGn=)wCv7L2c}SbIZOtlbJFI)bF^i?F z`>-k_>=iIMku44TdQsD}xOd*GR!^Z`Td7t@QSV1h{|?+n{WH{CZ&jk{jhKcSnqx!F z@kd+kd$Q>?9NfI`!9CkQNifxw#p4+ELp=xWYmR*Nh9p zdG_B+2FjzJg8NvgFQUIL7eh&Xkg%BZP$tMQr<0Tot^mJ zZ%6xQOUmr}32G{UWF9q~$(=LmXE@w{ss zupaeF@%JMhg=RP%7{@loF9vYx62;4PkEW6oo+Gdcy7_vMd87Y2BKU8HT*qECz}K-r z^~mQ)@b9YlHjvMS;4ffdrT*V-T)$TJtcTuD=u$;ZuT;&hu5pYOTG0FO~dB z#p5jVcm;nC3)H8e9KeKV5XbEh`ZCvA#AAp{-o)94`1)uI$o)d_O@xc#(+?@U8IJM& zMg3QUe%fzA^#|J>MqKmpr8tIoYcvkK0{t`zr+=R`U4=F>!Bu!1=tT|^!OMup@Oq&4 zPyF&U;_IW$P>6ZB{9n~iI9?Ar<2#x3L#`*7p9IHu@OULB&ofRh)sQ&q4DbTtG_GKO zenat5yjyd-91bwyxSa_bU(QdDTZWsSBB1D&@HLwW$e4~fWffi1G2EOnk+t(?5hF9U zF_pE)(^(_q+D_3(m&!0<=L%WVH8Y8hl_QKvD{mQT$4So^X5MvXVA4tFOe0guM8GPsJUq`gO$U6n8su&KjGrt3oD~| zZ$kqb%XT-QuV@?7>3k+@f-!s)qcT?BC>6~N;D4vsRz3qpCTlom)}BbaCRO8F6Gp+p zcw@X+3&R*aJRpOn>;NQ+GdVXsj@orRJuM~Nj$t|lNaStTOiblViGpJnOvjz^ zLE|M0_p(?ShWh%4+iCJu_;k8B4T;Q59-HvA>v$f%Z!a?p^x+`VMBq{(>q0_xGvS(L zEYsYh&sO1(Nh5u@RU(j^L zC}>K-Yo4l|W&Q;CBNm$R;EzjfeJJ}CgYjaE{tI$je0*7t#(Dp!#9@dMu|>a)C|x_! zzN`~v|EcUVrR$$WcruUgBS!fXzUGxfzp&RMd@Mi+nDB2Cx9sTv diff --git a/OPAL/ll/src/main/resources/org/opalj/ll/.gitkeep b/OPAL/ll/src/main/resources/org/opalj/ll/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/.gitkeep b/OPAL/ll/src/main/scala/org/opalj/ll/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/jsmn.h b/OPAL/ll/src/test/resources/org/opalj/ll/jsmn.h deleted file mode 100644 index 8ac14c1bde..0000000000 --- a/OPAL/ll/src/test/resources/org/opalj/ll/jsmn.h +++ /dev/null @@ -1,471 +0,0 @@ -/* - * MIT License - * - * Copyright (c) 2010 Serge Zaitsev - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -#ifndef JSMN_H -#define JSMN_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef JSMN_STATIC -#define JSMN_API static -#else -#define JSMN_API extern -#endif - -/** - * JSON type identifier. Basic types are: - * o Object - * o Array - * o String - * o Other primitive: number, boolean (true/false) or null - */ -typedef enum { - JSMN_UNDEFINED = 0, - JSMN_OBJECT = 1 << 0, - JSMN_ARRAY = 1 << 1, - JSMN_STRING = 1 << 2, - JSMN_PRIMITIVE = 1 << 3 -} jsmntype_t; - -enum jsmnerr { - /* Not enough tokens were provided */ - JSMN_ERROR_NOMEM = -1, - /* Invalid character inside JSON string */ - JSMN_ERROR_INVAL = -2, - /* The string is not a full JSON packet, more bytes expected */ - JSMN_ERROR_PART = -3 -}; - -/** - * JSON token description. - * type type (object, array, string etc.) - * start start position in JSON data string - * end end position in JSON data string - */ -typedef struct jsmntok { - jsmntype_t type; - int start; - int end; - int size; -#ifdef JSMN_PARENT_LINKS - int parent; -#endif -} jsmntok_t; - -/** - * JSON parser. Contains an array of token blocks available. Also stores - * the string being parsed now and current position in that string. - */ -typedef struct jsmn_parser { - unsigned int pos; /* offset in the JSON string */ - unsigned int toknext; /* next token to allocate */ - int toksuper; /* superior token node, e.g. parent object or array */ -} jsmn_parser; - -/** - * Create JSON parser over an array of tokens - */ -JSMN_API void jsmn_init(jsmn_parser *parser); - -/** - * Run JSON parser. It parses a JSON data string into and array of tokens, each - * describing - * a single JSON object. - */ -JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, - jsmntok_t *tokens, const unsigned int num_tokens); - -#ifndef JSMN_HEADER -/** - * Allocates a fresh unused token from the token pool. - */ -static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, - const size_t num_tokens) { - jsmntok_t *tok; - if (parser->toknext >= num_tokens) { - return NULL; - } - tok = &tokens[parser->toknext++]; - tok->start = tok->end = -1; - tok->size = 0; -#ifdef JSMN_PARENT_LINKS - tok->parent = -1; -#endif - return tok; -} - -/** - * Fills token type and boundaries. - */ -static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type, - const int start, const int end) { - token->type = type; - token->start = start; - token->end = end; - token->size = 0; -} - -/** - * Fills next available token with JSON primitive. - */ -static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, - const size_t len, jsmntok_t *tokens, - const size_t num_tokens) { - jsmntok_t *token; - int start; - - start = parser->pos; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - switch (js[parser->pos]) { -#ifndef JSMN_STRICT - /* In strict mode primitive must be followed by "," or "}" or "]" */ - case ':': -#endif - case '\t': - case '\r': - case '\n': - case ' ': - case ',': - case ']': - case '}': - goto found; - default: - /* to quiet a warning from gcc*/ - break; - } - if (js[parser->pos] < 32 || js[parser->pos] >= 127) { - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } -#ifdef JSMN_STRICT - /* In strict mode primitive must be followed by a comma/object/array */ - parser->pos = start; - return JSMN_ERROR_PART; -#endif - -found: - if (tokens == NULL) { - parser->pos--; - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - parser->pos--; - return 0; -} - -/** - * Fills next token with JSON string. - */ -static int jsmn_parse_string(jsmn_parser *parser, const char *js, - const size_t len, jsmntok_t *tokens, - const size_t num_tokens) { - jsmntok_t *token; - - int start = parser->pos; - - /* Skip starting quote */ - parser->pos++; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c = js[parser->pos]; - - /* Quote: end of string */ - if (c == '\"') { - if (tokens == NULL) { - return 0; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - parser->pos = start; - return JSMN_ERROR_NOMEM; - } - jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos); -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - return 0; - } - - /* Backslash: Quoted symbol expected */ - if (c == '\\' && parser->pos + 1 < len) { - int i; - parser->pos++; - switch (js[parser->pos]) { - /* Allowed escaped symbols */ - case '\"': - case '/': - case '\\': - case 'b': - case 'f': - case 'r': - case 'n': - case 't': - break; - /* Allows escaped symbol \uXXXX */ - case 'u': - parser->pos++; - for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; - i++) { - /* If it isn't a hex character we have an error */ - if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ - (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ - (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ - parser->pos = start; - return JSMN_ERROR_INVAL; - } - parser->pos++; - } - parser->pos--; - break; - /* Unexpected symbol */ - default: - parser->pos = start; - return JSMN_ERROR_INVAL; - } - } - } - parser->pos = start; - return JSMN_ERROR_PART; -} - -/** - * Parse JSON string and fill tokens. - */ -JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len, - jsmntok_t *tokens, const unsigned int num_tokens) { - int r; - int i; - jsmntok_t *token; - int count = parser->toknext; - - for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { - char c; - jsmntype_t type; - - c = js[parser->pos]; - switch (c) { - case '{': - case '[': - count++; - if (tokens == NULL) { - break; - } - token = jsmn_alloc_token(parser, tokens, num_tokens); - if (token == NULL) { - return JSMN_ERROR_NOMEM; - } - if (parser->toksuper != -1) { - jsmntok_t *t = &tokens[parser->toksuper]; -#ifdef JSMN_STRICT - /* In strict mode an object or array can't become a key */ - if (t->type == JSMN_OBJECT) { - return JSMN_ERROR_INVAL; - } -#endif - t->size++; -#ifdef JSMN_PARENT_LINKS - token->parent = parser->toksuper; -#endif - } - token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); - token->start = parser->pos; - parser->toksuper = parser->toknext - 1; - break; - case '}': - case ']': - if (tokens == NULL) { - break; - } - type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); -#ifdef JSMN_PARENT_LINKS - if (parser->toknext < 1) { - return JSMN_ERROR_INVAL; - } - token = &tokens[parser->toknext - 1]; - for (;;) { - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - token->end = parser->pos + 1; - parser->toksuper = token->parent; - break; - } - if (token->parent == -1) { - if (token->type != type || parser->toksuper == -1) { - return JSMN_ERROR_INVAL; - } - break; - } - token = &tokens[token->parent]; - } -#else - for (i = parser->toknext - 1; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - if (token->type != type) { - return JSMN_ERROR_INVAL; - } - parser->toksuper = -1; - token->end = parser->pos + 1; - break; - } - } - /* Error if unmatched closing bracket */ - if (i == -1) { - return JSMN_ERROR_INVAL; - } - for (; i >= 0; i--) { - token = &tokens[i]; - if (token->start != -1 && token->end == -1) { - parser->toksuper = i; - break; - } - } -#endif - break; - case '\"': - r = jsmn_parse_string(parser, js, len, tokens, num_tokens); - if (r < 0) { - return r; - } - count++; - if (parser->toksuper != -1 && tokens != NULL) { - tokens[parser->toksuper].size++; - } - break; - case '\t': - case '\r': - case '\n': - case ' ': - break; - case ':': - parser->toksuper = parser->toknext - 1; - break; - case ',': - if (tokens != NULL && parser->toksuper != -1 && - tokens[parser->toksuper].type != JSMN_ARRAY && - tokens[parser->toksuper].type != JSMN_OBJECT) { -#ifdef JSMN_PARENT_LINKS - parser->toksuper = tokens[parser->toksuper].parent; -#else - for (i = parser->toknext - 1; i >= 0; i--) { - if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { - if (tokens[i].start != -1 && tokens[i].end == -1) { - parser->toksuper = i; - break; - } - } - } -#endif - } - break; -#ifdef JSMN_STRICT - /* In strict mode primitives are: numbers and booleans */ - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 't': - case 'f': - case 'n': - /* And they must not be keys of the object */ - if (tokens != NULL && parser->toksuper != -1) { - const jsmntok_t *t = &tokens[parser->toksuper]; - if (t->type == JSMN_OBJECT || - (t->type == JSMN_STRING && t->size != 0)) { - return JSMN_ERROR_INVAL; - } - } -#else - /* In non-strict mode every unquoted value is a primitive */ - default: -#endif - r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); - if (r < 0) { - return r; - } - count++; - if (parser->toksuper != -1 && tokens != NULL) { - tokens[parser->toksuper].size++; - } - break; - -#ifdef JSMN_STRICT - /* Unexpected char in strict mode */ - default: - return JSMN_ERROR_INVAL; -#endif - } - } - - if (tokens != NULL) { - for (i = parser->toknext - 1; i >= 0; i--) { - /* Unmatched opened object or array */ - if (tokens[i].start != -1 && tokens[i].end == -1) { - return JSMN_ERROR_PART; - } - } - } - - return count; -} - -/** - * Creates a new parser based over a given buffer with an array of tokens - * available. - */ -JSMN_API void jsmn_init(jsmn_parser *parser) { - parser->pos = 0; - parser->toknext = 0; - parser->toksuper = -1; -} - -#endif /* JSMN_HEADER */ - -#ifdef __cplusplus -} -#endif - -#endif /* JSMN_H */ diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/test.c b/OPAL/ll/src/test/resources/org/opalj/ll/test.c deleted file mode 100644 index 2da9eb0457..0000000000 --- a/OPAL/ll/src/test/resources/org/opalj/ll/test.c +++ /dev/null @@ -1,19 +0,0 @@ -#include - -int pure_fuction(int a, int b) { - return a + b; -} - -void impure_function(int* foo) { - *foo = 23; -} - -int main() { - printf("hello world\n"); - int result = pure_fuction(1,2); - printf("result: %i\n", result); - int foo = 42; - impure_function(&foo); - printf("foo: %i", foo); - return 0; -} diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/test.ll b/OPAL/ll/src/test/resources/org/opalj/ll/test.ll deleted file mode 100644 index f3e449c659..0000000000 --- a/OPAL/ll/src/test/resources/org/opalj/ll/test.ll +++ /dev/null @@ -1,58 +0,0 @@ -; ModuleID = 'test.c' -source_filename = "test.c" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-pc-linux-gnu" - -@.str = private unnamed_addr constant [13 x i8] c"hello world\0A\00", align 1 -@.str.1 = private unnamed_addr constant [12 x i8] c"result: %i\0A\00", align 1 -@.str.2 = private unnamed_addr constant [8 x i8] c"foo: %i\00", align 1 - -; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @pure_fuction(i32 %0, i32 %1) #0 { - %3 = alloca i32, align 4 - %4 = alloca i32, align 4 - store i32 %0, i32* %3, align 4 - store i32 %1, i32* %4, align 4 - %5 = load i32, i32* %3, align 4 - %6 = load i32, i32* %4, align 4 - %7 = add nsw i32 %5, %6 - ret i32 %7 -} - -; Function Attrs: noinline nounwind optnone uwtable -define dso_local void @impure_function(i32* %0) #0 { - %2 = alloca i32*, align 8 - store i32* %0, i32** %2, align 8 - %3 = load i32*, i32** %2, align 8 - store i32 23, i32* %3, align 4 - ret void -} - -; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @main() #0 { - %1 = alloca i32, align 4 - %2 = alloca i32, align 4 - %3 = alloca i32, align 4 - store i32 0, i32* %1, align 4 - %4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([13 x i8], [13 x i8]* @.str, i64 0, i64 0)) - %5 = call i32 @pure_fuction(i32 1, i32 2) - store i32 %5, i32* %2, align 4 - %6 = load i32, i32* %2, align 4 - %7 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([12 x i8], [12 x i8]* @.str.1, i64 0, i64 0), i32 %6) - store i32 42, i32* %3, align 4 - call void @impure_function(i32* %3) - %8 = load i32, i32* %3, align 4 - %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8 x i8]* @.str.2, i64 0, i64 0), i32 %8) - ret i32 0 -} - -declare dso_local i32 @printf(i8*, ...) #1 - -attributes #0 = { noinline nounwind optnone uwtable "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } -attributes #1 = { "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" } - -!llvm.module.flags = !{!0} -!llvm.ident = !{!1} - -!0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{!"Ubuntu clang version 12.0.0-1ubuntu1"} diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.c b/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.c deleted file mode 100644 index 1f2f5ddfdf..0000000000 --- a/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include -#include "jsmn.h" - -int main() { - char* s = "{\"test\": \"this is a test\", \"foo\": 42}"; - - jsmn_parser p; - jsmntok_t t[128]; /* We expect no more than 128 JSON tokens */ - - jsmn_init(&p); - int r = jsmn_parse(&p, s, strlen(s), t, 128); // "s" is the char array holding the json content -} diff --git a/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll b/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll deleted file mode 100644 index c38e3849d3..0000000000 --- a/OPAL/ll/src/test/resources/org/opalj/ll/test_jsmn.ll +++ /dev/null @@ -1,1223 +0,0 @@ -; ModuleID = 'test_jsmn.c' -source_filename = "test_jsmn.c" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-pc-linux-gnu" - -%struct.jsmn_parser = type { i32, i32, i32 } -%struct.jsmntok = type { i32, i32, i32, i32 } - -@.str = private unnamed_addr constant [38 x i8] c"{\22test\22: \22this is a test\22, \22foo\22: 42}\00", align 1 - -; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @jsmn_parse(%struct.jsmn_parser* %0, i8* %1, i64 %2, %struct.jsmntok* %3, i32 %4) #0 { - %6 = alloca i32, align 4 - %7 = alloca %struct.jsmn_parser*, align 8 - %8 = alloca i8*, align 8 - %9 = alloca i64, align 8 - %10 = alloca %struct.jsmntok*, align 8 - %11 = alloca i32, align 4 - %12 = alloca i32, align 4 - %13 = alloca i32, align 4 - %14 = alloca %struct.jsmntok*, align 8 - %15 = alloca i32, align 4 - %16 = alloca i8, align 1 - %17 = alloca i32, align 4 - %18 = alloca %struct.jsmntok*, align 8 - store %struct.jsmn_parser* %0, %struct.jsmn_parser** %7, align 8 - store i8* %1, i8** %8, align 8 - store i64 %2, i64* %9, align 8 - store %struct.jsmntok* %3, %struct.jsmntok** %10, align 8 - store i32 %4, i32* %11, align 4 - %19 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %20 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %19, i32 0, i32 1 - %21 = load i32, i32* %20, align 4 - store i32 %21, i32* %15, align 4 - br label %22 - -22: ; preds = %337, %5 - %23 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %24 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %23, i32 0, i32 0 - %25 = load i32, i32* %24, align 4 - %26 = zext i32 %25 to i64 - %27 = load i64, i64* %9, align 8 - %28 = icmp ult i64 %26, %27 - br i1 %28, label %29, label %39 - -29: ; preds = %22 - %30 = load i8*, i8** %8, align 8 - %31 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %32 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %31, i32 0, i32 0 - %33 = load i32, i32* %32, align 4 - %34 = zext i32 %33 to i64 - %35 = getelementptr inbounds i8, i8* %30, i64 %34 - %36 = load i8, i8* %35, align 1 - %37 = sext i8 %36 to i32 - %38 = icmp ne i32 %37, 0 - br label %39 - -39: ; preds = %29, %22 - %40 = phi i1 [ false, %22 ], [ %38, %29 ] - br i1 %40, label %41, label %342 - -41: ; preds = %39 - %42 = load i8*, i8** %8, align 8 - %43 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %44 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %43, i32 0, i32 0 - %45 = load i32, i32* %44, align 4 - %46 = zext i32 %45 to i64 - %47 = getelementptr inbounds i8, i8* %42, i64 %46 - %48 = load i8, i8* %47, align 1 - store i8 %48, i8* %16, align 1 - %49 = load i8, i8* %16, align 1 - %50 = sext i8 %49 to i32 - switch i32 %50, label %303 [ - i32 123, label %51 - i32 91, label %51 - i32 125, label %101 - i32 93, label %101 - i32 34, label %183 - i32 9, label %216 - i32 13, label %216 - i32 10, label %216 - i32 32, label %216 - i32 58, label %217 - i32 44, label %224 - ] - -51: ; preds = %41, %41 - %52 = load i32, i32* %15, align 4 - %53 = add nsw i32 %52, 1 - store i32 %53, i32* %15, align 4 - %54 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %55 = icmp eq %struct.jsmntok* %54, null - br i1 %55, label %56, label %57 - -56: ; preds = %51 - br label %336 - -57: ; preds = %51 - %58 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %59 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %60 = load i32, i32* %11, align 4 - %61 = zext i32 %60 to i64 - %62 = call %struct.jsmntok* @jsmn_alloc_token(%struct.jsmn_parser* %58, %struct.jsmntok* %59, i64 %61) - store %struct.jsmntok* %62, %struct.jsmntok** %14, align 8 - %63 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 - %64 = icmp eq %struct.jsmntok* %63, null - br i1 %64, label %65, label %66 - -65: ; preds = %57 - store i32 -1, i32* %6, align 4 - br label %377 - -66: ; preds = %57 - %67 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %68 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %67, i32 0, i32 2 - %69 = load i32, i32* %68, align 4 - %70 = icmp ne i32 %69, -1 - br i1 %70, label %71, label %82 - -71: ; preds = %66 - %72 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %73 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %74 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %73, i32 0, i32 2 - %75 = load i32, i32* %74, align 4 - %76 = sext i32 %75 to i64 - %77 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %72, i64 %76 - store %struct.jsmntok* %77, %struct.jsmntok** %18, align 8 - %78 = load %struct.jsmntok*, %struct.jsmntok** %18, align 8 - %79 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %78, i32 0, i32 3 - %80 = load i32, i32* %79, align 4 - %81 = add nsw i32 %80, 1 - store i32 %81, i32* %79, align 4 - br label %82 - -82: ; preds = %71, %66 - %83 = load i8, i8* %16, align 1 - %84 = sext i8 %83 to i32 - %85 = icmp eq i32 %84, 123 - %86 = zext i1 %85 to i64 - %87 = select i1 %85, i32 1, i32 2 - %88 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 - %89 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %88, i32 0, i32 0 - store i32 %87, i32* %89, align 4 - %90 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %91 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %90, i32 0, i32 0 - %92 = load i32, i32* %91, align 4 - %93 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 - %94 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %93, i32 0, i32 1 - store i32 %92, i32* %94, align 4 - %95 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %96 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %95, i32 0, i32 1 - %97 = load i32, i32* %96, align 4 - %98 = sub i32 %97, 1 - %99 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %100 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %99, i32 0, i32 2 - store i32 %98, i32* %100, align 4 - br label %336 - -101: ; preds = %41, %41 - %102 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %103 = icmp eq %struct.jsmntok* %102, null - br i1 %103, label %104, label %105 - -104: ; preds = %101 - br label %336 - -105: ; preds = %101 - %106 = load i8, i8* %16, align 1 - %107 = sext i8 %106 to i32 - %108 = icmp eq i32 %107, 125 - %109 = zext i1 %108 to i64 - %110 = select i1 %108, i32 1, i32 2 - store i32 %110, i32* %17, align 4 - %111 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %112 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %111, i32 0, i32 1 - %113 = load i32, i32* %112, align 4 - %114 = sub i32 %113, 1 - store i32 %114, i32* %13, align 4 - br label %115 - -115: ; preds = %149, %105 - %116 = load i32, i32* %13, align 4 - %117 = icmp sge i32 %116, 0 - br i1 %117, label %118, label %152 - -118: ; preds = %115 - %119 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %120 = load i32, i32* %13, align 4 - %121 = sext i32 %120 to i64 - %122 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %119, i64 %121 - store %struct.jsmntok* %122, %struct.jsmntok** %14, align 8 - %123 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 - %124 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %123, i32 0, i32 1 - %125 = load i32, i32* %124, align 4 - %126 = icmp ne i32 %125, -1 - br i1 %126, label %127, label %148 - -127: ; preds = %118 - %128 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 - %129 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %128, i32 0, i32 2 - %130 = load i32, i32* %129, align 4 - %131 = icmp eq i32 %130, -1 - br i1 %131, label %132, label %148 - -132: ; preds = %127 - %133 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 - %134 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %133, i32 0, i32 0 - %135 = load i32, i32* %134, align 4 - %136 = load i32, i32* %17, align 4 - %137 = icmp ne i32 %135, %136 - br i1 %137, label %138, label %139 - -138: ; preds = %132 - store i32 -2, i32* %6, align 4 - br label %377 - -139: ; preds = %132 - %140 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %141 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %140, i32 0, i32 2 - store i32 -1, i32* %141, align 4 - %142 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %143 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %142, i32 0, i32 0 - %144 = load i32, i32* %143, align 4 - %145 = add i32 %144, 1 - %146 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 - %147 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %146, i32 0, i32 2 - store i32 %145, i32* %147, align 4 - br label %152 - -148: ; preds = %127, %118 - br label %149 - -149: ; preds = %148 - %150 = load i32, i32* %13, align 4 - %151 = add nsw i32 %150, -1 - store i32 %151, i32* %13, align 4 - br label %115, !llvm.loop !4 - -152: ; preds = %139, %115 - %153 = load i32, i32* %13, align 4 - %154 = icmp eq i32 %153, -1 - br i1 %154, label %155, label %156 - -155: ; preds = %152 - store i32 -2, i32* %6, align 4 - br label %377 - -156: ; preds = %152 - br label %157 - -157: ; preds = %179, %156 - %158 = load i32, i32* %13, align 4 - %159 = icmp sge i32 %158, 0 - br i1 %159, label %160, label %182 - -160: ; preds = %157 - %161 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %162 = load i32, i32* %13, align 4 - %163 = sext i32 %162 to i64 - %164 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %161, i64 %163 - store %struct.jsmntok* %164, %struct.jsmntok** %14, align 8 - %165 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 - %166 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %165, i32 0, i32 1 - %167 = load i32, i32* %166, align 4 - %168 = icmp ne i32 %167, -1 - br i1 %168, label %169, label %178 - -169: ; preds = %160 - %170 = load %struct.jsmntok*, %struct.jsmntok** %14, align 8 - %171 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %170, i32 0, i32 2 - %172 = load i32, i32* %171, align 4 - %173 = icmp eq i32 %172, -1 - br i1 %173, label %174, label %178 - -174: ; preds = %169 - %175 = load i32, i32* %13, align 4 - %176 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %177 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %176, i32 0, i32 2 - store i32 %175, i32* %177, align 4 - br label %182 - -178: ; preds = %169, %160 - br label %179 - -179: ; preds = %178 - %180 = load i32, i32* %13, align 4 - %181 = add nsw i32 %180, -1 - store i32 %181, i32* %13, align 4 - br label %157, !llvm.loop !6 - -182: ; preds = %174, %157 - br label %336 - -183: ; preds = %41 - %184 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %185 = load i8*, i8** %8, align 8 - %186 = load i64, i64* %9, align 8 - %187 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %188 = load i32, i32* %11, align 4 - %189 = zext i32 %188 to i64 - %190 = call i32 @jsmn_parse_string(%struct.jsmn_parser* %184, i8* %185, i64 %186, %struct.jsmntok* %187, i64 %189) - store i32 %190, i32* %12, align 4 - %191 = load i32, i32* %12, align 4 - %192 = icmp slt i32 %191, 0 - br i1 %192, label %193, label %195 - -193: ; preds = %183 - %194 = load i32, i32* %12, align 4 - store i32 %194, i32* %6, align 4 - br label %377 - -195: ; preds = %183 - %196 = load i32, i32* %15, align 4 - %197 = add nsw i32 %196, 1 - store i32 %197, i32* %15, align 4 - %198 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %199 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %198, i32 0, i32 2 - %200 = load i32, i32* %199, align 4 - %201 = icmp ne i32 %200, -1 - br i1 %201, label %202, label %215 - -202: ; preds = %195 - %203 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %204 = icmp ne %struct.jsmntok* %203, null - br i1 %204, label %205, label %215 - -205: ; preds = %202 - %206 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %207 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %208 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %207, i32 0, i32 2 - %209 = load i32, i32* %208, align 4 - %210 = sext i32 %209 to i64 - %211 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %206, i64 %210 - %212 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %211, i32 0, i32 3 - %213 = load i32, i32* %212, align 4 - %214 = add nsw i32 %213, 1 - store i32 %214, i32* %212, align 4 - br label %215 - -215: ; preds = %205, %202, %195 - br label %336 - -216: ; preds = %41, %41, %41, %41 - br label %336 - -217: ; preds = %41 - %218 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %219 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %218, i32 0, i32 1 - %220 = load i32, i32* %219, align 4 - %221 = sub i32 %220, 1 - %222 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %223 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %222, i32 0, i32 2 - store i32 %221, i32* %223, align 4 - br label %336 - -224: ; preds = %41 - %225 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %226 = icmp ne %struct.jsmntok* %225, null - br i1 %226, label %227, label %302 - -227: ; preds = %224 - %228 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %229 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %228, i32 0, i32 2 - %230 = load i32, i32* %229, align 4 - %231 = icmp ne i32 %230, -1 - br i1 %231, label %232, label %302 - -232: ; preds = %227 - %233 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %234 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %235 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %234, i32 0, i32 2 - %236 = load i32, i32* %235, align 4 - %237 = sext i32 %236 to i64 - %238 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %233, i64 %237 - %239 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %238, i32 0, i32 0 - %240 = load i32, i32* %239, align 4 - %241 = icmp ne i32 %240, 2 - br i1 %241, label %242, label %302 - -242: ; preds = %232 - %243 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %244 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %245 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %244, i32 0, i32 2 - %246 = load i32, i32* %245, align 4 - %247 = sext i32 %246 to i64 - %248 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %243, i64 %247 - %249 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %248, i32 0, i32 0 - %250 = load i32, i32* %249, align 4 - %251 = icmp ne i32 %250, 1 - br i1 %251, label %252, label %302 - -252: ; preds = %242 - %253 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %254 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %253, i32 0, i32 1 - %255 = load i32, i32* %254, align 4 - %256 = sub i32 %255, 1 - store i32 %256, i32* %13, align 4 - br label %257 - -257: ; preds = %298, %252 - %258 = load i32, i32* %13, align 4 - %259 = icmp sge i32 %258, 0 - br i1 %259, label %260, label %301 - -260: ; preds = %257 - %261 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %262 = load i32, i32* %13, align 4 - %263 = sext i32 %262 to i64 - %264 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %261, i64 %263 - %265 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %264, i32 0, i32 0 - %266 = load i32, i32* %265, align 4 - %267 = icmp eq i32 %266, 2 - br i1 %267, label %276, label %268 - -268: ; preds = %260 - %269 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %270 = load i32, i32* %13, align 4 - %271 = sext i32 %270 to i64 - %272 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %269, i64 %271 - %273 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %272, i32 0, i32 0 - %274 = load i32, i32* %273, align 4 - %275 = icmp eq i32 %274, 1 - br i1 %275, label %276, label %297 - -276: ; preds = %268, %260 - %277 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %278 = load i32, i32* %13, align 4 - %279 = sext i32 %278 to i64 - %280 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %277, i64 %279 - %281 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %280, i32 0, i32 1 - %282 = load i32, i32* %281, align 4 - %283 = icmp ne i32 %282, -1 - br i1 %283, label %284, label %296 - -284: ; preds = %276 - %285 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %286 = load i32, i32* %13, align 4 - %287 = sext i32 %286 to i64 - %288 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %285, i64 %287 - %289 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %288, i32 0, i32 2 - %290 = load i32, i32* %289, align 4 - %291 = icmp eq i32 %290, -1 - br i1 %291, label %292, label %296 - -292: ; preds = %284 - %293 = load i32, i32* %13, align 4 - %294 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %295 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %294, i32 0, i32 2 - store i32 %293, i32* %295, align 4 - br label %301 - -296: ; preds = %284, %276 - br label %297 - -297: ; preds = %296, %268 - br label %298 - -298: ; preds = %297 - %299 = load i32, i32* %13, align 4 - %300 = add nsw i32 %299, -1 - store i32 %300, i32* %13, align 4 - br label %257, !llvm.loop !7 - -301: ; preds = %292, %257 - br label %302 - -302: ; preds = %301, %242, %232, %227, %224 - br label %336 - -303: ; preds = %41 - %304 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %305 = load i8*, i8** %8, align 8 - %306 = load i64, i64* %9, align 8 - %307 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %308 = load i32, i32* %11, align 4 - %309 = zext i32 %308 to i64 - %310 = call i32 @jsmn_parse_primitive(%struct.jsmn_parser* %304, i8* %305, i64 %306, %struct.jsmntok* %307, i64 %309) - store i32 %310, i32* %12, align 4 - %311 = load i32, i32* %12, align 4 - %312 = icmp slt i32 %311, 0 - br i1 %312, label %313, label %315 - -313: ; preds = %303 - %314 = load i32, i32* %12, align 4 - store i32 %314, i32* %6, align 4 - br label %377 - -315: ; preds = %303 - %316 = load i32, i32* %15, align 4 - %317 = add nsw i32 %316, 1 - store i32 %317, i32* %15, align 4 - %318 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %319 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %318, i32 0, i32 2 - %320 = load i32, i32* %319, align 4 - %321 = icmp ne i32 %320, -1 - br i1 %321, label %322, label %335 - -322: ; preds = %315 - %323 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %324 = icmp ne %struct.jsmntok* %323, null - br i1 %324, label %325, label %335 - -325: ; preds = %322 - %326 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %327 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %328 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %327, i32 0, i32 2 - %329 = load i32, i32* %328, align 4 - %330 = sext i32 %329 to i64 - %331 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %326, i64 %330 - %332 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %331, i32 0, i32 3 - %333 = load i32, i32* %332, align 4 - %334 = add nsw i32 %333, 1 - store i32 %334, i32* %332, align 4 - br label %335 - -335: ; preds = %325, %322, %315 - br label %336 - -336: ; preds = %335, %302, %217, %216, %215, %182, %104, %82, %56 - br label %337 - -337: ; preds = %336 - %338 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %339 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %338, i32 0, i32 0 - %340 = load i32, i32* %339, align 4 - %341 = add i32 %340, 1 - store i32 %341, i32* %339, align 4 - br label %22, !llvm.loop !8 - -342: ; preds = %39 - %343 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %344 = icmp ne %struct.jsmntok* %343, null - br i1 %344, label %345, label %375 - -345: ; preds = %342 - %346 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %347 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %346, i32 0, i32 1 - %348 = load i32, i32* %347, align 4 - %349 = sub i32 %348, 1 - store i32 %349, i32* %13, align 4 - br label %350 - -350: ; preds = %371, %345 - %351 = load i32, i32* %13, align 4 - %352 = icmp sge i32 %351, 0 - br i1 %352, label %353, label %374 - -353: ; preds = %350 - %354 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %355 = load i32, i32* %13, align 4 - %356 = sext i32 %355 to i64 - %357 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %354, i64 %356 - %358 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %357, i32 0, i32 1 - %359 = load i32, i32* %358, align 4 - %360 = icmp ne i32 %359, -1 - br i1 %360, label %361, label %370 - -361: ; preds = %353 - %362 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %363 = load i32, i32* %13, align 4 - %364 = sext i32 %363 to i64 - %365 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %362, i64 %364 - %366 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %365, i32 0, i32 2 - %367 = load i32, i32* %366, align 4 - %368 = icmp eq i32 %367, -1 - br i1 %368, label %369, label %370 - -369: ; preds = %361 - store i32 -3, i32* %6, align 4 - br label %377 - -370: ; preds = %361, %353 - br label %371 - -371: ; preds = %370 - %372 = load i32, i32* %13, align 4 - %373 = add nsw i32 %372, -1 - store i32 %373, i32* %13, align 4 - br label %350, !llvm.loop !9 - -374: ; preds = %350 - br label %375 - -375: ; preds = %374, %342 - %376 = load i32, i32* %15, align 4 - store i32 %376, i32* %6, align 4 - br label %377 - -377: ; preds = %375, %369, %313, %193, %155, %138, %65 - %378 = load i32, i32* %6, align 4 - ret i32 %378 -} - -; Function Attrs: noinline nounwind optnone uwtable -define internal %struct.jsmntok* @jsmn_alloc_token(%struct.jsmn_parser* %0, %struct.jsmntok* %1, i64 %2) #0 { - %4 = alloca %struct.jsmntok*, align 8 - %5 = alloca %struct.jsmn_parser*, align 8 - %6 = alloca %struct.jsmntok*, align 8 - %7 = alloca i64, align 8 - %8 = alloca %struct.jsmntok*, align 8 - store %struct.jsmn_parser* %0, %struct.jsmn_parser** %5, align 8 - store %struct.jsmntok* %1, %struct.jsmntok** %6, align 8 - store i64 %2, i64* %7, align 8 - %9 = load %struct.jsmn_parser*, %struct.jsmn_parser** %5, align 8 - %10 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %9, i32 0, i32 1 - %11 = load i32, i32* %10, align 4 - %12 = zext i32 %11 to i64 - %13 = load i64, i64* %7, align 8 - %14 = icmp uge i64 %12, %13 - br i1 %14, label %15, label %16 - -15: ; preds = %3 - store %struct.jsmntok* null, %struct.jsmntok** %4, align 8 - br label %31 - -16: ; preds = %3 - %17 = load %struct.jsmntok*, %struct.jsmntok** %6, align 8 - %18 = load %struct.jsmn_parser*, %struct.jsmn_parser** %5, align 8 - %19 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %18, i32 0, i32 1 - %20 = load i32, i32* %19, align 4 - %21 = add i32 %20, 1 - store i32 %21, i32* %19, align 4 - %22 = zext i32 %20 to i64 - %23 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %17, i64 %22 - store %struct.jsmntok* %23, %struct.jsmntok** %8, align 8 - %24 = load %struct.jsmntok*, %struct.jsmntok** %8, align 8 - %25 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %24, i32 0, i32 2 - store i32 -1, i32* %25, align 4 - %26 = load %struct.jsmntok*, %struct.jsmntok** %8, align 8 - %27 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %26, i32 0, i32 1 - store i32 -1, i32* %27, align 4 - %28 = load %struct.jsmntok*, %struct.jsmntok** %8, align 8 - %29 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %28, i32 0, i32 3 - store i32 0, i32* %29, align 4 - %30 = load %struct.jsmntok*, %struct.jsmntok** %8, align 8 - store %struct.jsmntok* %30, %struct.jsmntok** %4, align 8 - br label %31 - -31: ; preds = %16, %15 - %32 = load %struct.jsmntok*, %struct.jsmntok** %4, align 8 - ret %struct.jsmntok* %32 -} - -; Function Attrs: noinline nounwind optnone uwtable -define internal i32 @jsmn_parse_string(%struct.jsmn_parser* %0, i8* %1, i64 %2, %struct.jsmntok* %3, i64 %4) #0 { - %6 = alloca i32, align 4 - %7 = alloca %struct.jsmn_parser*, align 8 - %8 = alloca i8*, align 8 - %9 = alloca i64, align 8 - %10 = alloca %struct.jsmntok*, align 8 - %11 = alloca i64, align 8 - %12 = alloca %struct.jsmntok*, align 8 - %13 = alloca i32, align 4 - %14 = alloca i8, align 1 - %15 = alloca i32, align 4 - store %struct.jsmn_parser* %0, %struct.jsmn_parser** %7, align 8 - store i8* %1, i8** %8, align 8 - store i64 %2, i64* %9, align 8 - store %struct.jsmntok* %3, %struct.jsmntok** %10, align 8 - store i64 %4, i64* %11, align 8 - %16 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %17 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %16, i32 0, i32 0 - %18 = load i32, i32* %17, align 4 - store i32 %18, i32* %13, align 4 - %19 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %20 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %19, i32 0, i32 0 - %21 = load i32, i32* %20, align 4 - %22 = add i32 %21, 1 - store i32 %22, i32* %20, align 4 - br label %23 - -23: ; preds = %211, %5 - %24 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %25 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %24, i32 0, i32 0 - %26 = load i32, i32* %25, align 4 - %27 = zext i32 %26 to i64 - %28 = load i64, i64* %9, align 8 - %29 = icmp ult i64 %27, %28 - br i1 %29, label %30, label %40 - -30: ; preds = %23 - %31 = load i8*, i8** %8, align 8 - %32 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %33 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %32, i32 0, i32 0 - %34 = load i32, i32* %33, align 4 - %35 = zext i32 %34 to i64 - %36 = getelementptr inbounds i8, i8* %31, i64 %35 - %37 = load i8, i8* %36, align 1 - %38 = sext i8 %37 to i32 - %39 = icmp ne i32 %38, 0 - br label %40 - -40: ; preds = %30, %23 - %41 = phi i1 [ false, %23 ], [ %39, %30 ] - br i1 %41, label %42, label %216 - -42: ; preds = %40 - %43 = load i8*, i8** %8, align 8 - %44 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %45 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %44, i32 0, i32 0 - %46 = load i32, i32* %45, align 4 - %47 = zext i32 %46 to i64 - %48 = getelementptr inbounds i8, i8* %43, i64 %47 - %49 = load i8, i8* %48, align 1 - store i8 %49, i8* %14, align 1 - %50 = load i8, i8* %14, align 1 - %51 = sext i8 %50 to i32 - %52 = icmp eq i32 %51, 34 - br i1 %52, label %53, label %75 - -53: ; preds = %42 - %54 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %55 = icmp eq %struct.jsmntok* %54, null - br i1 %55, label %56, label %57 - -56: ; preds = %53 - store i32 0, i32* %6, align 4 - br label %220 - -57: ; preds = %53 - %58 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %59 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %60 = load i64, i64* %11, align 8 - %61 = call %struct.jsmntok* @jsmn_alloc_token(%struct.jsmn_parser* %58, %struct.jsmntok* %59, i64 %60) - store %struct.jsmntok* %61, %struct.jsmntok** %12, align 8 - %62 = load %struct.jsmntok*, %struct.jsmntok** %12, align 8 - %63 = icmp eq %struct.jsmntok* %62, null - br i1 %63, label %64, label %68 - -64: ; preds = %57 - %65 = load i32, i32* %13, align 4 - %66 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %67 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %66, i32 0, i32 0 - store i32 %65, i32* %67, align 4 - store i32 -1, i32* %6, align 4 - br label %220 - -68: ; preds = %57 - %69 = load %struct.jsmntok*, %struct.jsmntok** %12, align 8 - %70 = load i32, i32* %13, align 4 - %71 = add nsw i32 %70, 1 - %72 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %73 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %72, i32 0, i32 0 - %74 = load i32, i32* %73, align 4 - call void @jsmn_fill_token(%struct.jsmntok* %69, i32 4, i32 %71, i32 %74) - store i32 0, i32* %6, align 4 - br label %220 - -75: ; preds = %42 - %76 = load i8, i8* %14, align 1 - %77 = sext i8 %76 to i32 - %78 = icmp eq i32 %77, 92 - br i1 %78, label %79, label %210 - -79: ; preds = %75 - %80 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %81 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %80, i32 0, i32 0 - %82 = load i32, i32* %81, align 4 - %83 = add i32 %82, 1 - %84 = zext i32 %83 to i64 - %85 = load i64, i64* %9, align 8 - %86 = icmp ult i64 %84, %85 - br i1 %86, label %87, label %210 - -87: ; preds = %79 - %88 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %89 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %88, i32 0, i32 0 - %90 = load i32, i32* %89, align 4 - %91 = add i32 %90, 1 - store i32 %91, i32* %89, align 4 - %92 = load i8*, i8** %8, align 8 - %93 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %94 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %93, i32 0, i32 0 - %95 = load i32, i32* %94, align 4 - %96 = zext i32 %95 to i64 - %97 = getelementptr inbounds i8, i8* %92, i64 %96 - %98 = load i8, i8* %97, align 1 - %99 = sext i8 %98 to i32 - switch i32 %99, label %205 [ - i32 34, label %100 - i32 47, label %100 - i32 92, label %100 - i32 98, label %100 - i32 102, label %100 - i32 114, label %100 - i32 110, label %100 - i32 116, label %100 - i32 117, label %101 - ] - -100: ; preds = %87, %87, %87, %87, %87, %87, %87, %87 - br label %209 - -101: ; preds = %87 - %102 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %103 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %102, i32 0, i32 0 - %104 = load i32, i32* %103, align 4 - %105 = add i32 %104, 1 - store i32 %105, i32* %103, align 4 - store i32 0, i32* %15, align 4 - br label %106 - -106: ; preds = %197, %101 - %107 = load i32, i32* %15, align 4 - %108 = icmp slt i32 %107, 4 - br i1 %108, label %109, label %126 - -109: ; preds = %106 - %110 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %111 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %110, i32 0, i32 0 - %112 = load i32, i32* %111, align 4 - %113 = zext i32 %112 to i64 - %114 = load i64, i64* %9, align 8 - %115 = icmp ult i64 %113, %114 - br i1 %115, label %116, label %126 - -116: ; preds = %109 - %117 = load i8*, i8** %8, align 8 - %118 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %119 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %118, i32 0, i32 0 - %120 = load i32, i32* %119, align 4 - %121 = zext i32 %120 to i64 - %122 = getelementptr inbounds i8, i8* %117, i64 %121 - %123 = load i8, i8* %122, align 1 - %124 = sext i8 %123 to i32 - %125 = icmp ne i32 %124, 0 - br label %126 - -126: ; preds = %116, %109, %106 - %127 = phi i1 [ false, %109 ], [ false, %106 ], [ %125, %116 ] - br i1 %127, label %128, label %200 - -128: ; preds = %126 - %129 = load i8*, i8** %8, align 8 - %130 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %131 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %130, i32 0, i32 0 - %132 = load i32, i32* %131, align 4 - %133 = zext i32 %132 to i64 - %134 = getelementptr inbounds i8, i8* %129, i64 %133 - %135 = load i8, i8* %134, align 1 - %136 = sext i8 %135 to i32 - %137 = icmp sge i32 %136, 48 - br i1 %137, label %138, label %148 - -138: ; preds = %128 - %139 = load i8*, i8** %8, align 8 - %140 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %141 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %140, i32 0, i32 0 - %142 = load i32, i32* %141, align 4 - %143 = zext i32 %142 to i64 - %144 = getelementptr inbounds i8, i8* %139, i64 %143 - %145 = load i8, i8* %144, align 1 - %146 = sext i8 %145 to i32 - %147 = icmp sle i32 %146, 57 - br i1 %147, label %192, label %148 - -148: ; preds = %138, %128 - %149 = load i8*, i8** %8, align 8 - %150 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %151 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %150, i32 0, i32 0 - %152 = load i32, i32* %151, align 4 - %153 = zext i32 %152 to i64 - %154 = getelementptr inbounds i8, i8* %149, i64 %153 - %155 = load i8, i8* %154, align 1 - %156 = sext i8 %155 to i32 - %157 = icmp sge i32 %156, 65 - br i1 %157, label %158, label %168 - -158: ; preds = %148 - %159 = load i8*, i8** %8, align 8 - %160 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %161 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %160, i32 0, i32 0 - %162 = load i32, i32* %161, align 4 - %163 = zext i32 %162 to i64 - %164 = getelementptr inbounds i8, i8* %159, i64 %163 - %165 = load i8, i8* %164, align 1 - %166 = sext i8 %165 to i32 - %167 = icmp sle i32 %166, 70 - br i1 %167, label %192, label %168 - -168: ; preds = %158, %148 - %169 = load i8*, i8** %8, align 8 - %170 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %171 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %170, i32 0, i32 0 - %172 = load i32, i32* %171, align 4 - %173 = zext i32 %172 to i64 - %174 = getelementptr inbounds i8, i8* %169, i64 %173 - %175 = load i8, i8* %174, align 1 - %176 = sext i8 %175 to i32 - %177 = icmp sge i32 %176, 97 - br i1 %177, label %178, label %188 - -178: ; preds = %168 - %179 = load i8*, i8** %8, align 8 - %180 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %181 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %180, i32 0, i32 0 - %182 = load i32, i32* %181, align 4 - %183 = zext i32 %182 to i64 - %184 = getelementptr inbounds i8, i8* %179, i64 %183 - %185 = load i8, i8* %184, align 1 - %186 = sext i8 %185 to i32 - %187 = icmp sle i32 %186, 102 - br i1 %187, label %192, label %188 - -188: ; preds = %178, %168 - %189 = load i32, i32* %13, align 4 - %190 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %191 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %190, i32 0, i32 0 - store i32 %189, i32* %191, align 4 - store i32 -2, i32* %6, align 4 - br label %220 - -192: ; preds = %178, %158, %138 - %193 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %194 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %193, i32 0, i32 0 - %195 = load i32, i32* %194, align 4 - %196 = add i32 %195, 1 - store i32 %196, i32* %194, align 4 - br label %197 - -197: ; preds = %192 - %198 = load i32, i32* %15, align 4 - %199 = add nsw i32 %198, 1 - store i32 %199, i32* %15, align 4 - br label %106, !llvm.loop !10 - -200: ; preds = %126 - %201 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %202 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %201, i32 0, i32 0 - %203 = load i32, i32* %202, align 4 - %204 = add i32 %203, -1 - store i32 %204, i32* %202, align 4 - br label %209 - -205: ; preds = %87 - %206 = load i32, i32* %13, align 4 - %207 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %208 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %207, i32 0, i32 0 - store i32 %206, i32* %208, align 4 - store i32 -2, i32* %6, align 4 - br label %220 - -209: ; preds = %200, %100 - br label %210 - -210: ; preds = %209, %79, %75 - br label %211 - -211: ; preds = %210 - %212 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %213 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %212, i32 0, i32 0 - %214 = load i32, i32* %213, align 4 - %215 = add i32 %214, 1 - store i32 %215, i32* %213, align 4 - br label %23, !llvm.loop !11 - -216: ; preds = %40 - %217 = load i32, i32* %13, align 4 - %218 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %219 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %218, i32 0, i32 0 - store i32 %217, i32* %219, align 4 - store i32 -3, i32* %6, align 4 - br label %220 - -220: ; preds = %216, %205, %188, %68, %64, %56 - %221 = load i32, i32* %6, align 4 - ret i32 %221 -} - -; Function Attrs: noinline nounwind optnone uwtable -define internal i32 @jsmn_parse_primitive(%struct.jsmn_parser* %0, i8* %1, i64 %2, %struct.jsmntok* %3, i64 %4) #0 { - %6 = alloca i32, align 4 - %7 = alloca %struct.jsmn_parser*, align 8 - %8 = alloca i8*, align 8 - %9 = alloca i64, align 8 - %10 = alloca %struct.jsmntok*, align 8 - %11 = alloca i64, align 8 - %12 = alloca %struct.jsmntok*, align 8 - %13 = alloca i32, align 4 - store %struct.jsmn_parser* %0, %struct.jsmn_parser** %7, align 8 - store i8* %1, i8** %8, align 8 - store i64 %2, i64* %9, align 8 - store %struct.jsmntok* %3, %struct.jsmntok** %10, align 8 - store i64 %4, i64* %11, align 8 - %14 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %15 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %14, i32 0, i32 0 - %16 = load i32, i32* %15, align 4 - store i32 %16, i32* %13, align 4 - br label %17 - -17: ; preds = %72, %5 - %18 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %19 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %18, i32 0, i32 0 - %20 = load i32, i32* %19, align 4 - %21 = zext i32 %20 to i64 - %22 = load i64, i64* %9, align 8 - %23 = icmp ult i64 %21, %22 - br i1 %23, label %24, label %34 - -24: ; preds = %17 - %25 = load i8*, i8** %8, align 8 - %26 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %27 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %26, i32 0, i32 0 - %28 = load i32, i32* %27, align 4 - %29 = zext i32 %28 to i64 - %30 = getelementptr inbounds i8, i8* %25, i64 %29 - %31 = load i8, i8* %30, align 1 - %32 = sext i8 %31 to i32 - %33 = icmp ne i32 %32, 0 - br label %34 - -34: ; preds = %24, %17 - %35 = phi i1 [ false, %17 ], [ %33, %24 ] - br i1 %35, label %36, label %77 - -36: ; preds = %34 - %37 = load i8*, i8** %8, align 8 - %38 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %39 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %38, i32 0, i32 0 - %40 = load i32, i32* %39, align 4 - %41 = zext i32 %40 to i64 - %42 = getelementptr inbounds i8, i8* %37, i64 %41 - %43 = load i8, i8* %42, align 1 - %44 = sext i8 %43 to i32 - switch i32 %44, label %46 [ - i32 58, label %45 - i32 9, label %45 - i32 13, label %45 - i32 10, label %45 - i32 32, label %45 - i32 44, label %45 - i32 93, label %45 - i32 125, label %45 - ] - -45: ; preds = %36, %36, %36, %36, %36, %36, %36, %36 - br label %78 - -46: ; preds = %36 - br label %47 - -47: ; preds = %46 - %48 = load i8*, i8** %8, align 8 - %49 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %50 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %49, i32 0, i32 0 - %51 = load i32, i32* %50, align 4 - %52 = zext i32 %51 to i64 - %53 = getelementptr inbounds i8, i8* %48, i64 %52 - %54 = load i8, i8* %53, align 1 - %55 = sext i8 %54 to i32 - %56 = icmp slt i32 %55, 32 - br i1 %56, label %67, label %57 - -57: ; preds = %47 - %58 = load i8*, i8** %8, align 8 - %59 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %60 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %59, i32 0, i32 0 - %61 = load i32, i32* %60, align 4 - %62 = zext i32 %61 to i64 - %63 = getelementptr inbounds i8, i8* %58, i64 %62 - %64 = load i8, i8* %63, align 1 - %65 = sext i8 %64 to i32 - %66 = icmp sge i32 %65, 127 - br i1 %66, label %67, label %71 - -67: ; preds = %57, %47 - %68 = load i32, i32* %13, align 4 - %69 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %70 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %69, i32 0, i32 0 - store i32 %68, i32* %70, align 4 - store i32 -2, i32* %6, align 4 - br label %107 - -71: ; preds = %57 - br label %72 - -72: ; preds = %71 - %73 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %74 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %73, i32 0, i32 0 - %75 = load i32, i32* %74, align 4 - %76 = add i32 %75, 1 - store i32 %76, i32* %74, align 4 - br label %17, !llvm.loop !12 - -77: ; preds = %34 - br label %78 - -78: ; preds = %77, %45 - %79 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %80 = icmp eq %struct.jsmntok* %79, null - br i1 %80, label %81, label %86 - -81: ; preds = %78 - %82 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %83 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %82, i32 0, i32 0 - %84 = load i32, i32* %83, align 4 - %85 = add i32 %84, -1 - store i32 %85, i32* %83, align 4 - store i32 0, i32* %6, align 4 - br label %107 - -86: ; preds = %78 - %87 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %88 = load %struct.jsmntok*, %struct.jsmntok** %10, align 8 - %89 = load i64, i64* %11, align 8 - %90 = call %struct.jsmntok* @jsmn_alloc_token(%struct.jsmn_parser* %87, %struct.jsmntok* %88, i64 %89) - store %struct.jsmntok* %90, %struct.jsmntok** %12, align 8 - %91 = load %struct.jsmntok*, %struct.jsmntok** %12, align 8 - %92 = icmp eq %struct.jsmntok* %91, null - br i1 %92, label %93, label %97 - -93: ; preds = %86 - %94 = load i32, i32* %13, align 4 - %95 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %96 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %95, i32 0, i32 0 - store i32 %94, i32* %96, align 4 - store i32 -1, i32* %6, align 4 - br label %107 - -97: ; preds = %86 - %98 = load %struct.jsmntok*, %struct.jsmntok** %12, align 8 - %99 = load i32, i32* %13, align 4 - %100 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %101 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %100, i32 0, i32 0 - %102 = load i32, i32* %101, align 4 - call void @jsmn_fill_token(%struct.jsmntok* %98, i32 8, i32 %99, i32 %102) - %103 = load %struct.jsmn_parser*, %struct.jsmn_parser** %7, align 8 - %104 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %103, i32 0, i32 0 - %105 = load i32, i32* %104, align 4 - %106 = add i32 %105, -1 - store i32 %106, i32* %104, align 4 - store i32 0, i32* %6, align 4 - br label %107 - -107: ; preds = %97, %93, %81, %67 - %108 = load i32, i32* %6, align 4 - ret i32 %108 -} - -; Function Attrs: noinline nounwind optnone uwtable -define dso_local void @jsmn_init(%struct.jsmn_parser* %0) #0 { - %2 = alloca %struct.jsmn_parser*, align 8 - store %struct.jsmn_parser* %0, %struct.jsmn_parser** %2, align 8 - %3 = load %struct.jsmn_parser*, %struct.jsmn_parser** %2, align 8 - %4 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %3, i32 0, i32 0 - store i32 0, i32* %4, align 4 - %5 = load %struct.jsmn_parser*, %struct.jsmn_parser** %2, align 8 - %6 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %5, i32 0, i32 1 - store i32 0, i32* %6, align 4 - %7 = load %struct.jsmn_parser*, %struct.jsmn_parser** %2, align 8 - %8 = getelementptr inbounds %struct.jsmn_parser, %struct.jsmn_parser* %7, i32 0, i32 2 - store i32 -1, i32* %8, align 4 - ret void -} - -; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @main() #0 { - %1 = alloca i8*, align 8 - %2 = alloca %struct.jsmn_parser, align 4 - %3 = alloca [128 x %struct.jsmntok], align 16 - %4 = alloca i32, align 4 - store i8* getelementptr inbounds ([38 x i8], [38 x i8]* @.str, i64 0, i64 0), i8** %1, align 8 - call void @jsmn_init(%struct.jsmn_parser* %2) - %5 = load i8*, i8** %1, align 8 - %6 = load i8*, i8** %1, align 8 - %7 = call i64 @strlen(i8* %6) #2 - %8 = getelementptr inbounds [128 x %struct.jsmntok], [128 x %struct.jsmntok]* %3, i64 0, i64 0 - %9 = call i32 @jsmn_parse(%struct.jsmn_parser* %2, i8* %5, i64 %7, %struct.jsmntok* %8, i32 128) - store i32 %9, i32* %4, align 4 - ret i32 0 -} - -; Function Attrs: nounwind readonly willreturn -declare dso_local i64 @strlen(i8*) #1 - -; Function Attrs: noinline nounwind optnone uwtable -define internal void @jsmn_fill_token(%struct.jsmntok* %0, i32 %1, i32 %2, i32 %3) #0 { - %5 = alloca %struct.jsmntok*, align 8 - %6 = alloca i32, align 4 - %7 = alloca i32, align 4 - %8 = alloca i32, align 4 - store %struct.jsmntok* %0, %struct.jsmntok** %5, align 8 - store i32 %1, i32* %6, align 4 - store i32 %2, i32* %7, align 4 - store i32 %3, i32* %8, align 4 - %9 = load i32, i32* %6, align 4 - %10 = load %struct.jsmntok*, %struct.jsmntok** %5, align 8 - %11 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %10, i32 0, i32 0 - store i32 %9, i32* %11, align 4 - %12 = load i32, i32* %7, align 4 - %13 = load %struct.jsmntok*, %struct.jsmntok** %5, align 8 - %14 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %13, i32 0, i32 1 - store i32 %12, i32* %14, align 4 - %15 = load i32, i32* %8, align 4 - %16 = load %struct.jsmntok*, %struct.jsmntok** %5, align 8 - %17 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %16, i32 0, i32 2 - store i32 %15, i32* %17, align 4 - %18 = load %struct.jsmntok*, %struct.jsmntok** %5, align 8 - %19 = getelementptr inbounds %struct.jsmntok, %struct.jsmntok* %18, i32 0, i32 3 - store i32 0, i32* %19, align 4 - ret void -} - -attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } -attributes #1 = { nounwind readonly willreturn "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } -attributes #2 = { nounwind readonly willreturn } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.ident = !{!3} - -!0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{i32 7, !"uwtable", i32 1} -!2 = !{i32 7, !"frame-pointer", i32 2} -!3 = !{!"Ubuntu clang version 13.0.0-2"} -!4 = distinct !{!4, !5} -!5 = !{!"llvm.loop.mustprogress"} -!6 = distinct !{!6, !5} -!7 = distinct !{!7, !5} -!8 = distinct !{!8, !5} -!9 = distinct !{!9, !5} -!10 = distinct !{!10, !5} -!11 = distinct !{!11, !5} -!12 = distinct !{!12, !5} diff --git a/OPAL/ll/src/test/scala/org/opalj/ll/.gitkeep b/OPAL/ll/src/test/scala/org/opalj/ll/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 From 16a99ae5d14c8e804ba757cf3418f8df52bf0bc4 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 6 May 2022 18:25:58 +0200 Subject: [PATCH 37/67] Include llvm dependency using special sbt plugin --- build.sbt | 5 +++-- project/Dependencies.scala | 4 ---- project/plugins.sbt | 3 +++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build.sbt b/build.sbt index 895c297cd6..0f6bd79743 100644 --- a/build.sbt +++ b/build.sbt @@ -349,8 +349,9 @@ lazy val `LLVM` = (project in file("OPAL/ll")) name := "LLVM", Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - LLVM"), fork := true, - libraryDependencies ++= Dependencies.ll - ) + javaCppPresetLibs ++= Seq("llvm" -> "11.1.0"), + javaCppVersion := "1.5.5" +) .dependsOn(tac % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index aded902f4f..337aaf2865 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -25,8 +25,6 @@ object Dependencies { val fastutil = "8.5.4" val openjfx = "16" - - val llvm = "11.1.0-1.5.5" } object library { @@ -51,7 +49,6 @@ object Dependencies { val jacksonDF = "com.fasterxml.jackson.dataformat" % "jackson-dataformat-csv" % version.jacksonDF withSources () withJavadoc () val fastutil = "it.unimi.dsi" % "fastutil" % version.fastutil withSources () withJavadoc () val javafxBase = "org.openjfx" % "javafx-base" % version.openjfx classifier osName - val llvm = "org.bytedeco" % "llvm-platform" % version.llvm // --- test related dependencies @@ -70,7 +67,6 @@ object Dependencies { val si = Seq() val bi = Seq(commonstext) val br = Seq(scalaparsercombinators, scalaxml) - val ll = Seq(llvm) val ifds = Seq() val tools = Seq(txtmark, jacksonDF) val hermes = Seq(txtmark, jacksonDF, javafxBase) diff --git a/project/plugins.sbt b/project/plugins.sbt index ef74163f62..867adb1f84 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -20,3 +20,6 @@ addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.5.2") // For the deployment to maven central: addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.7") addSbtPlugin("com.github.sbt" % "sbt-pgp" % "2.1.2") + +// llvm +addSbtPlugin("org.bytedeco" % "sbt-javacpp" % "1.17") \ No newline at end of file From 89c1ff79d5885d8c4471810bcc7df529cd1d160f Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 11 May 2022 12:36:43 +0200 Subject: [PATCH 38/67] Fix sbt assembly --- build.sbt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.sbt b/build.sbt index 0f6bd79743..81dbd60f19 100644 --- a/build.sbt +++ b/build.sbt @@ -122,6 +122,9 @@ lazy val buildSettings = // see https://github.com/sbt/sbt-assembly/issues/391 Seq(assembly / assemblyMergeStrategy := { case "module-info.class" => MergeStrategy.discard + case PathList("META-INF", "versions", xs @ _, "module-info.class") => MergeStrategy.discard + case PathList("META-INF", "native-image", xs @ _, "jnijavacpp", "jni-config.json") => MergeStrategy.discard + case PathList("META-INF", "native-image", xs @ _, "jnijavacpp", "reflect-config.json") => MergeStrategy.discard case other => (assembly / assemblyMergeStrategy).value(other) }) From 89b91ca74fa0581fdd3100cab7bb0fc2b0f05213 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 11 May 2022 12:50:22 +0200 Subject: [PATCH 39/67] Reverst scalastyle change --- scalastyle-config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scalastyle-config.xml b/scalastyle-config.xml index 5a2ed0a80d..604f24a237 100644 --- a/scalastyle-config.xml +++ b/scalastyle-config.xml @@ -35,7 +35,7 @@ - + From 91bae7d66330e45ac46dcc26ba7ae1fedfde0125 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Wed, 8 Jun 2022 17:44:42 +0200 Subject: [PATCH 40/67] Improve test case --- .../llvm/multilingual/taint/NativeTest.h | 30 --------- .../taint/{NativeTest.c => TaintTest.c} | 12 ++-- .../{NativeTest.class => TaintTest.class} | Bin 1621 -> 1694 bytes .../llvm/multilingual/taint/TaintTest.h | 30 +++++++++ .../taint/{NativeTest.java => TaintTest.java} | 37 ++++++----- .../taint/{NativeTest.ll => TaintTest.ll} | 38 ++++++----- .../llvm/multilingual/taint/build.sh | 6 +- .../{libnativetest.so => libtainttest.so} | Bin 15624 -> 15680 bytes .../resources/llvm/multilingual/taint/run.sh | 2 +- ...MultilingualForwardTaintAnalysisTest.scala | 62 ++++++++++-------- .../ifds/taint/JavaForwardTaintAnalysis.scala | 15 ++++- 11 files changed, 126 insertions(+), 106 deletions(-) delete mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.h rename DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/{NativeTest.c => TaintTest.c} (53%) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/{NativeTest.class => TaintTest.class} (55%) create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h rename DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/{NativeTest.java => TaintTest.java} (58%) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/{NativeTest.ll => TaintTest.ll} (94%) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/{libnativetest.so => libtainttest.so} (66%) diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.h b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.h deleted file mode 100644 index c9987ab442..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.h +++ /dev/null @@ -1,30 +0,0 @@ -/* DO NOT EDIT THIS FILE - it is machine generated */ -#include -/* Header for class NativeTest */ - -#ifndef _Included_NativeTest -#define _Included_NativeTest -#ifdef __cplusplus -extern "C" { -#endif - -JNIEXPORT int JNICALL Java_NativeTest_f1(JNIEnv *env, jobject obj, jint a, jint b); - -JNIEXPORT int JNICALL Java_NativeTest_f2(JNIEnv *env, jobject obj); - -JNIEXPORT int JNICALL Java_NativeTest_f3(JNIEnv *env, jobject obj, jint a); - -JNIEXPORT int JNICALL Java_NativeTest_f4(JNIEnv *env, jobject obj, jint a); - -JNIEXPORT int JNICALL Java_NativeTest_f5(JNIEnv *env, jobject obj, jint a, jint b); - -int source(); - -void sink(int num); - -int sanitize(int num); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c similarity index 53% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.c rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c index 9cc3144ce7..6d36c646eb 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.c +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c @@ -1,30 +1,30 @@ #include #include #include -#include "NativeTest.h" +#include "TaintTest.h" JNIEXPORT int JNICALL -Java_NativeTest_f1(JNIEnv *env, jobject obj, jint a, jint b) { +Java_TaintTest_sum(JNIEnv *env, jobject obj, jint a, jint b) { return a + b; } JNIEXPORT int JNICALL -Java_NativeTest_f2(JNIEnv *env, jobject obj) { +Java_TaintTest_propagate_1source(JNIEnv *env, jobject obj) { return source() + 23; } JNIEXPORT int JNICALL -Java_NativeTest_f3(JNIEnv *env, jobject obj, jint a) { +Java_TaintTest_propagate_1sanitize(JNIEnv *env, jobject obj, jint a) { return sanitize(a); } JNIEXPORT int JNICALL -Java_NativeTest_f4(JNIEnv *env, jobject obj, jint a) { +Java_TaintTest_propagate_1sink(JNIEnv *env, jobject obj, jint a) { sink(a); return 23; } JNIEXPORT int JNICALL -Java_NativeTest_f5(JNIEnv *env, jobject obj, jint a, jint b) { +Java_TaintTest_sanitize_1only_1a_1into_1sink(JNIEnv *env, jobject obj, jint a, jint b) { a = sanitize(a); sink(a + b); return b; diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class similarity index 55% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.class rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class index 1ddeb56ba5d14d1e3ea58b5b5394dea6b94d25fd..d82f645a5774a2cfad4e9bc9d0ff2ada1fbdf188 100644 GIT binary patch delta 287 zcmcc0Gmn?!)W2Q(7#JArCUTguafT#j=9NrzU8llbl3H96ZyukPlV8roz|Nq+!=T8( z#>fB>w*ZSz=4E_V&si`sS1d2ETMVu!uX4PPHpL~W@jnQlJXI6Dazsb^URsd@tOUVEL delta 197 zcmbQodzFXd)W2Q(7#J9ACUTgubNMBfWR|5)bXh0GR+3s=5^v7Mz|Nq+!=T8(#>fB> zvY0H!_>7q;&2SQvhz(Pk5lDp#4}&X%H6sI4nhA*O&comV;TnUuUOWum4E7*dQw|1S zMh31tuq~4(Fy*uJLu}m4$~>8g*OGya!HR*C!J0vY!DjMORx4h6pojxd#1SasH2Dpy X2BX_#Q8qP3&&f7y>WsdVQ`oEkADbxZ diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h new file mode 100644 index 0000000000..e94fb775ff --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h @@ -0,0 +1,30 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class TaintTest */ + +#ifndef _Included_TaintTest +#define _Included_TaintTest +#ifdef __cplusplus +extern "C" { +#endif + +JNIEXPORT int JNICALL Java_TaintTest_sum(JNIEnv *env, jobject obj, jint a, jint b); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1source(JNIEnv *env, jobject obj); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1sanitize(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1sink(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT int JNICALL Java_TaintTest_sanitize_1only_1a_1into_1sink(JNIEnv *env, jobject obj, jint a, jint b); + +int source(); + +void sink(int num); + +int sanitize(int num); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java similarity index 58% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.java rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java index 1254f558a5..e16edcc3f9 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.java +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java @@ -1,24 +1,24 @@ -public class NativeTest { - private native int f1 (int a, int b); - private native int f2 (); - private native int f3 (int a); - private native int f4 (int a); - private native int f5 (int a, int b); +public class TaintTest { + private native int sum (int a, int b); + private native int propagate_source (); + private native int propagate_sanitize (int a); + private native int propagate_sink (int a); + private native int sanitize_only_a_into_sink (int a, int b); static { - System.loadLibrary ("nativetest"); /* lowercase of classname! */ + System.loadLibrary ("tainttest"); } public static void main (String[] args) { - NativeTest demo = new NativeTest(); + TaintTest demo = new TaintTest(); demo.test_1_flow(); demo.test_2_no_flow(); demo.test_3_no_flow(); demo.test_4_flow(); demo.test_5_flow(); demo.test_6_no_flow(); - demo.test_7(); - demo.test_8(); + demo.test_7_flow(); + demo.test_8_flow(); System.out.println("done"); } @@ -40,29 +40,30 @@ public void test_3_no_flow() { public void test_4_flow() { int tainted = this.source(); int untainted = 23; - int native_tainted = this.f1(tainted, untainted); + int native_tainted = this.sum(tainted, untainted); this.sink(native_tainted); } public void test_5_flow() { - int taint = this.f2(); - this.f4(taint); + int taint = this.propagate_source(); + this.propagate_sink(taint); } public void test_6_no_flow() { - this.f4(this.f3(this.f2())); + this.propagate_sink(this.propagate_sanitize(this.propagate_source())); } - public void test_7() { + public void test_7_flow() { + // has flow in case of abnormal return int tainted = this.source(); int untainted = 23; - this.sink(this.f5(tainted, untainted)); + this.sink(this.sanitize_only_a_into_sink(tainted, untainted)); } - public void test_8() { + public void test_8_flow() { int tainted = this.source(); int untainted = 23; - this.sink(this.f5(untainted, tainted)); + this.sink(this.sanitize_only_a_into_sink(untainted, tainted)); } private static int source() diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll similarity index 94% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll index 0e50b52376..2671f38cef 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll @@ -1,5 +1,5 @@ -; ModuleID = 'NativeTest.c' -source_filename = "NativeTest.c" +; ModuleID = 'TaintTest.c' +source_filename = "TaintTest.c" target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-linux-gnu" @@ -13,7 +13,7 @@ target triple = "x86_64-pc-linux-gnu" %struct._jobject = type opaque ; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @Java_NativeTest_f1(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2, i32 %3) #0 { +define dso_local i32 @Java_TaintTest_sum(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2, i32 noundef %3) #0 { %5 = alloca %struct.JNINativeInterface_**, align 8 %6 = alloca %struct._jobject*, align 8 %7 = alloca i32, align 4 @@ -29,7 +29,7 @@ define dso_local i32 @Java_NativeTest_f1(%struct.JNINativeInterface_** %0, %stru } ; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @Java_NativeTest_f2(%struct.JNINativeInterface_** %0, %struct._jobject* %1) #0 { +define dso_local i32 @Java_TaintTest_propagate_1source(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1) #0 { %3 = alloca %struct.JNINativeInterface_**, align 8 %4 = alloca %struct._jobject*, align 8 store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %3, align 8 @@ -40,7 +40,7 @@ define dso_local i32 @Java_NativeTest_f2(%struct.JNINativeInterface_** %0, %stru } ; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @Java_NativeTest_f3(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2) #0 { +define dso_local i32 @Java_TaintTest_propagate_1sanitize(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { %4 = alloca %struct.JNINativeInterface_**, align 8 %5 = alloca %struct._jobject*, align 8 %6 = alloca i32, align 4 @@ -48,12 +48,12 @@ define dso_local i32 @Java_NativeTest_f3(%struct.JNINativeInterface_** %0, %stru store %struct._jobject* %1, %struct._jobject** %5, align 8 store i32 %2, i32* %6, align 4 %7 = load i32, i32* %6, align 4 - %8 = call i32 @sanitize(i32 %7) + %8 = call i32 @sanitize(i32 noundef %7) ret i32 %8 } ; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @sanitize(i32 %0) #0 { +define dso_local i32 @sanitize(i32 noundef %0) #0 { %2 = alloca i32, align 4 store i32 %0, i32* %2, align 4 %3 = load i32, i32* %2, align 4 @@ -61,7 +61,7 @@ define dso_local i32 @sanitize(i32 %0) #0 { } ; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @Java_NativeTest_f4(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2) #0 { +define dso_local i32 @Java_TaintTest_propagate_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { %4 = alloca %struct.JNINativeInterface_**, align 8 %5 = alloca %struct._jobject*, align 8 %6 = alloca i32, align 4 @@ -69,19 +69,19 @@ define dso_local i32 @Java_NativeTest_f4(%struct.JNINativeInterface_** %0, %stru store %struct._jobject* %1, %struct._jobject** %5, align 8 store i32 %2, i32* %6, align 4 %7 = load i32, i32* %6, align 4 - call void @sink(i32 %7) + call void @sink(i32 noundef %7) ret i32 23 } ; Function Attrs: noinline nounwind optnone uwtable -define dso_local void @sink(i32 %0) #0 { +define dso_local void @sink(i32 noundef %0) #0 { %2 = alloca i32, align 4 store i32 %0, i32* %2, align 4 ret void } ; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @Java_NativeTest_f5(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2, i32 %3) #0 { +define dso_local i32 @Java_TaintTest_sanitize_1only_1a_1into_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2, i32 noundef %3) #0 { %5 = alloca %struct.JNINativeInterface_**, align 8 %6 = alloca %struct._jobject*, align 8 %7 = alloca i32, align 4 @@ -91,12 +91,12 @@ define dso_local i32 @Java_NativeTest_f5(%struct.JNINativeInterface_** %0, %stru store i32 %2, i32* %7, align 4 store i32 %3, i32* %8, align 4 %9 = load i32, i32* %7, align 4 - %10 = call i32 @sanitize(i32 %9) + %10 = call i32 @sanitize(i32 noundef %9) store i32 %10, i32* %7, align 4 %11 = load i32, i32* %7, align 4 %12 = load i32, i32* %8, align 4 %13 = add nsw i32 %11, %12 - call void @sink(i32 %13) + call void @sink(i32 noundef %13) %14 = load i32, i32* %8, align 4 ret i32 %14 } @@ -108,10 +108,12 @@ define dso_local i32 @source() #0 { attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } -!llvm.module.flags = !{!0, !1, !2} -!llvm.ident = !{!3} +!llvm.module.flags = !{!0, !1, !2, !3, !4} +!llvm.ident = !{!5} !0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{i32 7, !"uwtable", i32 1} -!2 = !{i32 7, !"frame-pointer", i32 2} -!3 = !{!"Ubuntu clang version 13.0.0-2"} +!1 = !{i32 7, !"PIC Level", i32 2} +!2 = !{i32 7, !"PIE Level", i32 2} +!3 = !{i32 7, !"uwtable", i32 1} +!4 = !{i32 7, !"frame-pointer", i32 2} +!5 = !{!"Ubuntu clang version 14.0.0-1ubuntu1"} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh index 457ed0fd79..935602dc5d 100755 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh @@ -1,3 +1,3 @@ -clang -shared -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ -o libnativetest.so NativeTest.c -clang -S -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ NativeTest.c -emit-llvm -javac NativeTest.java \ No newline at end of file +clang -shared -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ -o libtainttest.so TaintTest.c +clang -S -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ TaintTest.c -emit-llvm +javac TaintTest.java \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libnativetest.so b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libtainttest.so similarity index 66% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libnativetest.so rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libtainttest.so index 3b7e7661f706f23a66240baff272c160597d40a6..81016b7db05a824c5977a80c846feef60d3231d0 100755 GIT binary patch delta 1173 zcmaizUq}=|9LINd@65YWJT3N4VG)}6BE8h?-d>Tx`Geog{C@NO&CJex zc64BB;HKS17}jj`SF-Mq)n1a1>?Kco7SM&O()|0#=j$DjiThuc2gPjd#!#Ckps<^< zBkX#GmoXh}IkTwcw0I}>%fAlQPJX=hHTM0^;H{N&jvHgH*JYp1&rTlS+$~NlrC<)# z!W4PY=B>nPY+8|KV^B}gwl{cGN$esPje}YsOVy!A&`Htg9t>l}w4wN6C<5?+s)I!; zHWU>^NsY!8l~*#o#0jBK;QyvlX*ne%1U1fcikwMz$BPLBNmRvwq7qTMv}FZ9uNZYa zCrinGo)dTupGd|#YU-re)y*n08zkCJwYRkLKGw$u;DxEC8sQt=#Gh4xU9(KJXdwua zAQAjDJ$LNLXZ4n8&Iek~3NvOJnRDyr4a>6%mlJJ*o4rexBY4Ml*k-j?!B(htf!pID zOU)4TIMFKfdp-oYgPE?3q-Hob!*TdDUgE4CGu=r+N-%x#v?9t9!}(b+>pj33V*G+m zNO5*};+;q1FvfWtkMTYvQAUJ~H`O=B%>>MGwBr>XA^fV$;X*#&PXqGNjs<;o57E^8 z@9a0=@T|rfN#NTk+>IeBf%--oxk2#J=$PIP!%IAlLMP5AAd729aIMDDA5x6koUUhJ z-A9{eu%-cjQI(q{^Xjc!t{AJFHh6PFf8%Yl4i?n-lMO{UGjM=4$~nX*dS!`MR3WYM z{1$Sy!)>dIW6tb2YP+^RgLqUtn2whO$6tWbEo6QMLZz*&V1g;9g;k{@SgzypTXk&gQ O3keUyL6n81@c!SUj5O&0 delta 1037 zcmZ`&OH31C5T3u=2L)nUf>{cL7P!E{?Q^?GFjO%hh7j?Q2uL;yL9Jpz+Cl^q;N}q= zIS^mqiIcGsIe1#*K`}>w|zh9lWGcjA{9&v## zF~h;Bxq7KEXEU;#EpzP?yVUygUTvwPV`*nLXgl*|VQRi_Q`8zbQmvDb+DU)8zzV*z zJ`Hn_7LlHu%r%eyZmeu2-}C+9?c%N8_bn7_cpE8xV3nv^uDVCu)J;-tIGDt0j%SZ> zhHM7AINm)^l@ch1MYFRQ70!!mTp3%%498=Z>zV3QQt`3D^MlEhd^rfGl`%!$4TT^% zoE{w*++PVpQc1*9@reU!5lF@p*Y|%T!ce?_Ku8V?QE2OEKhYxFLqbRp0e@I4e8xE# zZD*ZAbIp)0k2?V@umMM`73oXBh(gXU+F5TD17d!t77X|FFdwv1HKhH z-OJ|vXg#`Mz29%iTCvAp#Z*6as!4n z6VI8rh%?~_c7qsL6B)uxr1IziWZ4HrK$frE7*5E^lroxhDjhV|Rn(%=2P- nWuf5>$68QnlGrpZG&MV?!^V_NT#~$ZkCi!l^(PGQRr}E&*FX41 diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh index 2c4db95e3f..7d5eb2bdee 100755 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh @@ -1 +1 @@ -java -Djava.library.path=. NativeTest +java -Djava.library.path=. TaintTest diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index dc8a713336..f0f1f004d2 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -13,36 +13,42 @@ import org.scalatest.matchers.should.Matchers class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers { describe("MultilingualForwardTaintAnalysis") { - it("executes") { - val project = - Project( - new java.io.File("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint") - ) - project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( - current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/NativeTest.ll") + val project = + Project( + new java.io.File("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint") ) - project.get(LLVMProjectKey) - project.get(RTACallGraphKey) - val manager = project.get(FPCFAnalysesManagerKey) - val (ps, analyses) = manager.runAll(JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler) - for (method ← project.allMethodsWithBody) { - val flows = - ps((method, NullFact), JavaForwardTaintAnalysisScheduler.property.key) - println("---METHOD: "+method.toJava+" ---") - for { - fact ← flows.ub - .asInstanceOf[IFDSProperty[JavaStatement, Fact]] - .flows - .values - .flatten - .toSet[Fact] - } { - fact match { - case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.name).mkString(", ")) - case _ ⇒ - } - } + project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( + current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll") + ) + project.get(LLVMProjectKey) + project.get(RTACallGraphKey) + val manager = project.get(FPCFAnalysesManagerKey) + val (ps, analyses) = manager.runAll(JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler) + for (method ← project.allMethodsWithBody) { + val flows = + ps((method, NullFact), JavaForwardTaintAnalysisScheduler.property.key) + println("---METHOD: " + method.toJava + " ---") + val flowFacts = flows.ub + .asInstanceOf[IFDSProperty[JavaStatement, Fact]] + .flows + .values + .flatten + .toSet[Fact] + .flatMap { + case FlowFact(flow) ⇒ Some(flow) + case _ ⇒ None } + for (flow <- flowFacts) + println(s"flow: " + flow.map(_.name).mkString(", ")) + if (method.name.contains("no_flow")) { + it(s"${method.name} has no flow") { + assert(flowFacts.isEmpty) + } + } else if (method.name.contains("flow")) { + it(s"${method.name} has some flow") { + assert(!flowFacts.isEmpty) + } + } } } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index c9a6c24026..2adf957abd 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -23,7 +23,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( */ override val entryPoints: Seq[(Method, Fact)] = (for { m ← p.allMethodsWithBody - if (m.name == "main") + if (m.name == "test_7_no_flow") } yield m -> NullFact) /** @@ -61,7 +61,14 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { def handleNativeMethod(call: JavaStatement, successor: JavaStatement, in: Fact, dependeesGetter: Getter): Set[Fact] = { // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names - val nativeFunctionName = "Java_"+callee.classFile.fqn+"_"+callee.name + val calleeName = callee.name.map( c => c match { + case c if isAlphaNumeric(c) => c + case '_' => "_1" + case ';' => "_2" + case '[' => "_3" + case c => s"_${c.toInt.toHexString.reverse.padTo(4, '0').reverse}" + }).mkString + val nativeFunctionName = "Java_"+callee.classFile.fqn+"_"+calleeName val function = llvmProject.function(nativeFunctionName).get var result = Set.empty[Fact] val entryFacts = nativeCallFlow(call, function, in, callee) @@ -235,6 +242,10 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( } flows } + + private def isAlphaNumeric(char: Char): Boolean = { + char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z' || char >= '0' && char <= '9' + } } class SimpleJavaForwardTaintAnalysis(project: SomeProject) From ed4ee71f3d232b765b5ac99027111debfbb58bb5 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 9 Jun 2022 13:12:34 +0200 Subject: [PATCH 41/67] Fix test --- .../llvm/multilingual/taint/TaintTest.class | Bin 1694 -> 1697 bytes .../llvm/multilingual/taint/TaintTest.java | 5 +- ...MultilingualForwardTaintAnalysisTest.scala | 46 +++++++++--------- .../ifds/taint/JavaForwardTaintAnalysis.scala | 17 +++---- .../taint/NativeForwardTaintProblem.scala | 1 + 5 files changed, 34 insertions(+), 35 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class index d82f645a5774a2cfad4e9bc9d0ff2ada1fbdf188..16c2d2c677014d667c545fa48899ec9df4a7a521 100644 GIT binary patch delta 75 zcmbQoyO4LnCoaB{)Z&tO^Z2~{jX&qJ@LDpkF<3EhGFUT+FxX69%4)@H4-|0#iZ}vA coF>0v)nIg+EXti`sS1d2ETMVu$U ZXVqYIpDe+q#^^QKo=u(6Z*n@D6#%xr5d#1K diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java index e16edcc3f9..cb8b884a22 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java @@ -17,7 +17,7 @@ public static void main (String[] args) demo.test_4_flow(); demo.test_5_flow(); demo.test_6_no_flow(); - demo.test_7_flow(); + demo.test_7_no_flow(); demo.test_8_flow(); System.out.println("done"); } @@ -53,8 +53,7 @@ public void test_6_no_flow() { this.propagate_sink(this.propagate_sanitize(this.propagate_source())); } - public void test_7_flow() { - // has flow in case of abnormal return + public void test_7_no_flow() { int tainted = this.source(); int untainted = 23; this.sink(this.sanitize_only_a_into_sink(tainted, untainted)); diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala index f0f1f004d2..56a0824708 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala @@ -25,30 +25,30 @@ class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers val manager = project.get(FPCFAnalysesManagerKey) val (ps, analyses) = manager.runAll(JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler) for (method ← project.allMethodsWithBody) { - val flows = - ps((method, NullFact), JavaForwardTaintAnalysisScheduler.property.key) - println("---METHOD: " + method.toJava + " ---") - val flowFacts = flows.ub - .asInstanceOf[IFDSProperty[JavaStatement, Fact]] - .flows - .values - .flatten - .toSet[Fact] - .flatMap { - case FlowFact(flow) ⇒ Some(flow) - case _ ⇒ None + val flows = + ps((method, NullFact), JavaForwardTaintAnalysisScheduler.property.key) + println("---METHOD: "+method.toJava+" ---") + val flowFacts = flows.ub + .asInstanceOf[IFDSProperty[JavaStatement, Fact]] + .flows + .values + .flatten + .toSet[Fact] + .flatMap { + case FlowFact(flow) ⇒ Some(flow) + case _ ⇒ None + } + for (flow ← flowFacts) + println(s"flow: "+flow.map(_.name).mkString(", ")) + if (method.name.contains("no_flow")) { + it(s"${method.name} has no flow") { + assert(flowFacts.isEmpty) + } + } else if (method.name.contains("flow")) { + it(s"${method.name} has some flow") { + assert(!flowFacts.isEmpty) + } } - for (flow <- flowFacts) - println(s"flow: " + flow.map(_.name).mkString(", ")) - if (method.name.contains("no_flow")) { - it(s"${method.name} has no flow") { - assert(flowFacts.isEmpty) - } - } else if (method.name.contains("flow")) { - it(s"${method.name} has some flow") { - assert(!flowFacts.isEmpty) - } - } } } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 2adf957abd..07b47e6f25 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -21,10 +21,9 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(Method, Fact)] = (for { + override val entryPoints: Seq[(Method, Fact)] = for { m ← p.allMethodsWithBody - if (m.name == "test_7_no_flow") - } yield m -> NullFact) + } yield m -> NullFact /** * The sanitize method is a sanitizer. @@ -61,12 +60,12 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { def handleNativeMethod(call: JavaStatement, successor: JavaStatement, in: Fact, dependeesGetter: Getter): Set[Fact] = { // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names - val calleeName = callee.name.map( c => c match { - case c if isAlphaNumeric(c) => c - case '_' => "_1" - case ';' => "_2" - case '[' => "_3" - case c => s"_${c.toInt.toHexString.reverse.padTo(4, '0').reverse}" + val calleeName = callee.name.map(c ⇒ c match { + case c if isAlphaNumeric(c) ⇒ c + case '_' ⇒ "_1" + case ';' ⇒ "_2" + case '[' ⇒ "_3" + case c ⇒ s"_${c.toInt.toHexString.reverse.padTo(4, '0').reverse}" }).mkString val nativeFunctionName = "Java_"+callee.classFile.fqn+"_"+calleeName val function = llvmProject.function(nativeFunctionName).get diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 3d12a963fb..b01c521736 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -24,6 +24,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD case _: Alloca ⇒ Set(in) case store: Store ⇒ in match { case NativeVariable(value) if value == store.src ⇒ Set(in, NativeVariable(store.dst)) + case NativeVariable(value) if value == store.dst ⇒ Set() case _ ⇒ Set(in) } case load: Load ⇒ in match { From 626cce9313ec400cf86350062167a052eae47de8 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 9 Jun 2022 13:58:38 +0200 Subject: [PATCH 42/67] Fix wrong flow by adding successor to return flow and checking for abnormal returns --- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 16 ++++++++-------- .../main/scala/org/opalj/ifds/IFDSProblem.scala | 14 ++++++++------ .../ifds/taint/JavaForwardTaintAnalysis.scala | 5 +++-- .../ifds/taint/NativeForwardTaintProblem.scala | 4 ++-- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 17 ++++++++++++++++- .../ifds/taint/ForwardTaintProblem.scala | 6 ++++-- 6 files changed, 41 insertions(+), 21 deletions(-) diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 608626308e..b8d8572fcf 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -281,7 +281,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, case None ⇒ for { successor ← successors - out ← concreteCallFlow(call, callee, in) // ifds line 17 (only summary edges) + out ← concreteCallFlow(call, callee, in, successor) // ifds line 17 (only summary edges) } { propagate(successor, out, call) // ifds line 18 } @@ -289,13 +289,13 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, } for { successor ← successors - out ← callToReturnFlow(call, in) // ifds line 17 (without summary edge propagation) + out ← callToReturnFlow(call, in, successor) // ifds line 17 (without summary edge propagation) } { propagate(successor, out, call) // ifds line 18 } } - private def concreteCallFlow(call: S, callee: C, in: IFDSFact)(implicit state: State, work: Work): Set[IFDSFact] = { + private def concreteCallFlow(call: S, callee: C, in: IFDSFact, successor: S)(implicit state: State, work: Work): Set[IFDSFact] = { var result = Set.empty[IFDSFact] val entryFacts = callFlow(call, callee, in) for (entryFact ← entryFacts) { // ifds line 14 @@ -320,7 +320,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, (exitStatement, exitStatementFacts) ← exitFacts // ifds line 15.2 exitStatementFact ← exitStatementFacts // ifds line 15.3 } { - result ++= returnFlow(exitStatement, exitStatementFact, call, in) + result ++= returnFlow(exitStatement, exitStatementFact, call, in, successor) } } result @@ -386,9 +386,9 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, * @param callFact d4 * @return */ - private def returnFlow(exit: S, in: IFDSFact, call: S, callFact: IFDSFact): Set[IFDSFact] = { + private def returnFlow(exit: S, in: IFDSFact, call: S, callFact: IFDSFact, successor: S): Set[IFDSFact] = { statistics.returnFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.returnFlow(exit, in, call, callFact)) + addNullFactIfConfigured(in, ifdsProblem.returnFlow(exit, in, call, callFact, successor)) } /** @@ -397,9 +397,9 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, * @param in d2 * @return */ - private def callToReturnFlow(call: S, in: IFDSFact): Set[IFDSFact] = { + private def callToReturnFlow(call: S, in: IFDSFact, successor: S): Set[IFDSFact] = { statistics.callToReturnFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.callToReturnFlow(call, in)) + addNullFactIfConfigured(in, ifdsProblem.callToReturnFlow(call, in, successor)) } private def addNullFactIfConfigured(in: IFDSFact, out: Set[IFDSFact]): Set[IFDSFact] = { diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala index 4c1434acf4..feabc8c1c9 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -73,10 +73,11 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: State * `successor` will be executed next. */ def returnFlow( - exit: S, - in: IFDSFact, - call: S, - callFact: IFDSFact + exit: S, + in: IFDSFact, + call: S, + callFact: IFDSFact, + successor: S ): Set[IFDSFact] /** @@ -88,8 +89,9 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: State * under the assumption that `in` held before `call`. */ def callToReturnFlow( - call: S, - in: IFDSFact + call: S, + in: IFDSFact, + successor: S ): Set[IFDSFact] def needsPredecessor(statement: S): Boolean diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 07b47e6f25..bf53796da4 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -86,7 +86,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( (exitStatement, exitStatementFacts) ← exitFacts // ifds line 15.2 exitStatementFact ← exitStatementFacts // ifds line 15.3 } { - result ++= nativeReturnFlow(exitStatement, exitStatementFact, call, in, callee) + result ++= nativeReturnFlow(exitStatement, exitStatementFact, call, in, callee, successor) } } result @@ -169,7 +169,8 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( in: NativeFact, call: JavaStatement, callFact: Fact, - nativeCallee: Method + nativeCallee: Method, + successor: JavaStatement ): Set[Fact] = { /** * Checks whether the callee's formal parameter is of a reference type. diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index b01c521736..ec5befbeb0 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -69,7 +69,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * under the assumption that `in` held before the execution of `exit` and that * `successor` will be executed next. */ - override def returnFlow(exit: LLVMStatement, in: NativeFact, call: LLVMStatement, callFact: NativeFact): Set[NativeFact] = { + override def returnFlow(exit: LLVMStatement, in: NativeFact, call: LLVMStatement, callFact: NativeFact, successor: LLVMStatement): Set[NativeFact] = { var flows: Set[NativeFact] = in match { case NativeVariable(value) ⇒ exit.instruction match { case ret: Ret if ret.value == value ⇒ Set(in) @@ -100,7 +100,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @return The facts, which hold after the call independently of what happens in the callee * under the assumption that `in` held before `call`. */ - override def callToReturnFlow(call: LLVMStatement, in: NativeFact): Set[NativeFact] = Set(in) + override def callToReturnFlow(call: LLVMStatement, in: NativeFact, successor: LLVMStatement): Set[NativeFact] = Set(in) override def needsPredecessor(statement: LLVMStatement): Boolean = statement.instruction match { case PHI(_) ⇒ true diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 6af60a2848..3308745c9d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -6,7 +6,7 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.cfg.{CFG, CFGNode} import org.opalj.ifds.Dependees.Getter import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, Statement} -import org.opalj.tac.{Assignment, Call, DUVar, ExprStmt, Stmt, TACStmts} +import org.opalj.tac.{Assignment, Call, DUVar, ExprStmt, Return, ReturnValue, Stmt, TACStmts} import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.value.ValueInformation @@ -59,6 +59,21 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) case _ ⇒ call.asMethodCall } + /** + * Checks if the return flow is actually possible from the given exit statement to the given successor. + * This is used to filter flows of exceptions into normal code without being caught + * + * @param exit the exit statement of the returning method + * @param successor the successor statement of the call within the callee function + * @return whether successor might actually be the next statement after the exit statement + */ + protected def isPossibleReturnFlow(exit: JavaStatement, successor: JavaStatement): Boolean = { + (successor.node.isBasicBlock || successor.node.isNormalReturnExitNode) && + (exit.stmt.astID == Return.ASTID || exit.stmt.astID == ReturnValue.ASTID) || + (successor.node.isCatchNode || successor.node.isAbnormalReturnExitNode) && + (exit.stmt.astID != Return.ASTID && exit.stmt.astID != ReturnValue.ASTID) + } + override def outsideAnalysisContext(callee: Method): Option[(JavaStatement, JavaStatement, Fact, Getter) ⇒ Set[Fact]] = callee.body.isDefined match { case true ⇒ None case false ⇒ Some((_: JavaStatement, _: JavaStatement, in: Fact, _: Getter) ⇒ Set(in)) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 7833c5f270..a9ef9ef001 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -118,7 +118,9 @@ abstract class ForwardTaintProblem(project: SomeProject) * Creates new taints and FlowFacts, if necessary. * If the sanitize method was called, nothing will be tainted. */ - override def returnFlow(exit: JavaStatement, in: Fact, call: JavaStatement, callFact: Fact): Set[Fact] = { + override def returnFlow(exit: JavaStatement, in: Fact, call: JavaStatement, callFact: Fact, successor: JavaStatement): Set[Fact] = { + if(!isPossibleReturnFlow(exit, successor)) return Set.empty + val callee = exit.callable() /** * Checks whether the callee's formal parameter is of a reference type. @@ -193,7 +195,7 @@ abstract class ForwardTaintProblem(project: SomeProject) /** * Removes taints according to `sanitizesParameter`. */ - override def callToReturnFlow(call: JavaStatement, in: Fact): Set[Fact] = + override def callToReturnFlow(call: JavaStatement, in: Fact, successor: JavaStatement): Set[Fact] = if (sanitizesParameter(call, in)) Set() else Set(in) /** From f34443b8893dd49421af6826940a5e567fb9f791 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 9 Jun 2022 16:55:35 +0200 Subject: [PATCH 43/67] Fix return flow of native code --- .../llvm/multilingual/taint/TaintTest.c | 22 ++++++++ .../llvm/multilingual/taint/TaintTest.class | Bin 1697 -> 2109 bytes .../llvm/multilingual/taint/TaintTest.h | 8 +++ .../llvm/multilingual/taint/TaintTest.java | 44 ++++++++++------ .../llvm/multilingual/taint/TaintTest.ll | 49 ++++++++++++++++++ .../llvm/multilingual/taint/libtainttest.so | Bin 15680 -> 15888 bytes .../taint/NativeForwardTaintProblem.scala | 5 +- 7 files changed, 110 insertions(+), 18 deletions(-) diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c index 6d36c646eb..9429031bc4 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c @@ -30,6 +30,28 @@ Java_TaintTest_sanitize_1only_1a_1into_1sink(JNIEnv *env, jobject obj, jint a, j return b; } +JNIEXPORT void JNICALL +Java_TaintTest_propagate_1identity_1to_1sink(JNIEnv *env, jobject obj, jint a) { + int b = identity(a); + sink(b); +} + +JNIEXPORT void JNICALL +Java_TaintTest_propagate_1zero_1to_1sink(JNIEnv *env, jobject obj, jint a) { + int b = zero(a); + sink(b); +} + +int +identity(int a) { + return a; +} + +int +zero(int a) { + return 0; +} + int source() { return 6*7; diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class index 16c2d2c677014d667c545fa48899ec9df4a7a521..a45f9ea6d49b6d53ac66e4381c4ad59356e18b67 100644 GIT binary patch literal 2109 zcmaJ??^7F96g^9TgzyMy_>m?7iBR1{JM0X32IgU{@3p0?P!jQ`?n5W~CE1t}r3EaSvx3uJzh1HBM_6 zLP>?I0+{UtxK1l6TA$KRFRhRfc<%l1cFJA3Oe=gK+?$5TtF>Tj!&n|X)Q{f{)UG3C*W7fIHR$YfM4?T=q3JX~5YVO`y!!>K{sdLH} zPwaSYx18LwIt#w%)=i_f7C}x!f5oxQC~o6U7#}O##XSvUJU);8^en68S(by$A(ZeVH6+BZ3@o8n|3vukErxb(H4)_+;fiyO zaZPfixTd(Sam{kw_ygI*QWMKTS}Pc#&?q8^b5331Gl6l8aZrq7it}U!le}9j6Tom36kT{=WX(9KeI}<;>MHfdS1)@PX&xlI|4QeSMrZ->&G*v{*jCJM#N+rLYldl<$FIh?9KQ)e0)^<%m& z+$MIqx$-}VZDx<|6DK8X9e`Exq^J`?`bBR8PrH&&czawYZG*{gvZW6wz$yDanJMPq zOJ<9-hm?6lnQaPi4Er*3%u;p{(b@hco(Fqx>?^7(Pzks zlV2#V|3=e!r=AnY5$OX{o|lL~Q=$Sd2;;=`VQE_V+Lyj8cgVY7Tx2@yp?G~0-<;#y RK(n>8VRls!Q5Gc}{R?_tgjWCn literal 1697 zcmaJ>Yg5xu5Ivzl+i)qh2o+ic6}1H9rJzU!pJ=r{L8{}2PJqZnOM-3C!QbQ;{ZJX5 z@dx;$9M7h)DGr*>&8BzvoZUV5@$=WW9{`r{LPr}SQM4;`pi@Kgjrrc3s+i99)Z>je z)~2VSGjBV#x1=GG8(WVerXjv&+K#to)jSGcdxo>`KwS6$DG1EE;C)t zcHYwD3Jt)5!c{D4h}BFkoBfdx@%fl*ic7vsj@lkO>Btp=`5Ov1B}sC(>h7A`re~EK z=_Ju@g*!r+XmK*7xb7<46IZIm6=W6neT4^-Zs1QTW!I_fm(4P}cFR(C3=g@TJlRgN zL3FDBSmB9C)>U!Mt)jhAHLLq<;g-9_PI`*AV?Em2*|4f>=0-(&+aXv(f9};$3Q~sr zW+m9a`l>JCiY>TAa}Q6-+HfaCc@pI51*JhJ-%t5(h*qQRFup?j%nuFE_!c+6wKfry zA8jI_Zz+gxshMx703+jA#Tqd}`HqPs)y#K9Uw%crI9`X&(an+M=;i3=7~sfq41dAt zRIUyqOh1V(5_BVm1kYU$lKdUVF`kYTvg}}l|3-O+a_Gl2(oD?L8Kl2JE9tw9(gCLP zMk#ZEOCfav5jqDMo*^j9U>@KAIvYS!f1p12$`SZsVn$fdDI$2v1H2L7^8TW8ND9*+d|} zGU~XVH!^kHD*fI5GZ7=o>1A(%ghDmq+ L*BP-cOep;Z4M7fE diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h index e94fb775ff..97da211dca 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h @@ -18,6 +18,14 @@ JNIEXPORT int JNICALL Java_TaintTest_propagate_1sink(JNIEnv *env, jobject obj, j JNIEXPORT int JNICALL Java_TaintTest_sanitize_1only_1a_1into_1sink(JNIEnv *env, jobject obj, jint a, jint b); +JNIEXPORT void JNICALL Java_TaintTest_propagate_1identity_1to_1sink(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT void JNICALL Java_TaintTest_propagate_1zero_1to_1sink(JNIEnv *env, jobject obj, jint a); + +int identity(int a); + +int zero(int a); + int source(); void sink(int num); diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java index cb8b884a22..14ef0d0bf3 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java @@ -4,6 +4,8 @@ public class TaintTest { private native int propagate_sanitize (int a); private native int propagate_sink (int a); private native int sanitize_only_a_into_sink (int a, int b); + private native void propagate_identity_to_sink(int a); + private native void propagate_zero_to_sink(int a); static { System.loadLibrary ("tainttest"); @@ -11,59 +13,69 @@ public class TaintTest { public static void main (String[] args) { TaintTest demo = new TaintTest(); - demo.test_1_flow(); - demo.test_2_no_flow(); - demo.test_3_no_flow(); - demo.test_4_flow(); - demo.test_5_flow(); - demo.test_6_no_flow(); - demo.test_7_no_flow(); - demo.test_8_flow(); + demo.test_java_flow(); + demo.test_java_sanitize_no_flow(); + demo.test_java_untainted_no_flow(); + demo.test_native_sum_flow(); + demo.test_native_to_java_to_native_flow(); + demo.test_native_to_java_to_native_sanitized_no_flow(); + demo.test_native_indirect_sanitized_no_flow(); + demo.test_native_indirect_flow(); + demo.test_native_identity_flow(); + demo.test_native_zero_no_flow(); System.out.println("done"); } - public void test_1_flow() { + public void test_java_flow() { int tainted = this.source(); this.sink(tainted); } - public void test_2_no_flow() { + public void test_java_sanitize_no_flow() { int tainted = this.source(); this.sink(this.sanitize(tainted)); } - public void test_3_no_flow() { + public void test_java_untainted_no_flow() { int untainted = 23; this.sink(untainted); } - public void test_4_flow() { + public void test_native_sum_flow() { int tainted = this.source(); int untainted = 23; int native_tainted = this.sum(tainted, untainted); this.sink(native_tainted); } - public void test_5_flow() { + public void test_native_to_java_to_native_flow() { int taint = this.propagate_source(); this.propagate_sink(taint); } - public void test_6_no_flow() { + public void test_native_to_java_to_native_sanitized_no_flow() { this.propagate_sink(this.propagate_sanitize(this.propagate_source())); } - public void test_7_no_flow() { + public void test_native_indirect_sanitized_no_flow() { int tainted = this.source(); int untainted = 23; this.sink(this.sanitize_only_a_into_sink(tainted, untainted)); } - public void test_8_flow() { + public void test_native_indirect_flow() { int tainted = this.source(); int untainted = 23; this.sink(this.sanitize_only_a_into_sink(untainted, tainted)); } + + public void test_native_identity_flow() { + this.propagate_identity_to_sink(source()); + } + + public void test_native_zero_no_flow() { + this.propagate_zero_to_sink(source()); + } private static int source() { diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll index 2671f38cef..6311c10be1 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll @@ -101,6 +101,55 @@ define dso_local i32 @Java_TaintTest_sanitize_1only_1a_1into_1sink(%struct.JNINa ret i32 %14 } +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @Java_TaintTest_propagate_1identity_1to_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + %7 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %8 = load i32, i32* %6, align 4 + %9 = call i32 @identity(i32 noundef %8) + store i32 %9, i32* %7, align 4 + %10 = load i32, i32* %7, align 4 + call void @sink(i32 noundef %10) + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @identity(i32 noundef %0) #0 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + %3 = load i32, i32* %2, align 4 + ret i32 %3 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @Java_TaintTest_propagate_1zero_1to_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + %7 = alloca i32, align 4 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %8 = load i32, i32* %6, align 4 + %9 = call i32 @zero(i32 noundef %8) + store i32 %9, i32* %7, align 4 + %10 = load i32, i32* %7, align 4 + call void @sink(i32 noundef %10) + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @zero(i32 noundef %0) #0 { + %2 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + ret i32 0 +} + ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @source() #0 { ret i32 42 diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libtainttest.so b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libtainttest.so index 81016b7db05a824c5977a80c846feef60d3231d0..01bad8136eaad9130c78afaf20eb907bceca593e 100755 GIT binary patch delta 1982 zcmZ`)T}&KR6ux(1pu3Ofi5P_wf`-9HL4L>tCvnthN66$3Q|S&fC#t)XNI z+tivMHeGsNh}N{L)(1`e=|)2nA4aWBi$2iwK^rlOv2kfkMB@V{n(^G3I|VoKB(vW= z-#Pc5J9E$dR-Z~I)6I@pc)m4M5-2971&ZgN^NVW^%Uhez$l?HZ2Ieyr^S{=L5$>zt zq8G%Zl}VCtg`SOk(AM(QjdvP%X0F@~rGNPI=UPF_v4jGA1bo<3R;e*i+UQH^S53M| zRbkT6=xqP)QmwOFntuPleWNd3&LrWy<5lmn8*|V~Eg-XqZc;|l&|Tp1;?OjaM|*iR z21g4#Gs`ZE-p!+L@u<%EGR|*sKE_vjfb)LJYvu7xoKJDyUgt^9>lWX7Z_{7#0!}_5 z$P0{eemr}#pqRpVu(~FGc=$*no;We6B*q4nWAP);6PtpuU`@r;@C5SrV$6ka3WE|Q zSR9Sv01lm|N67K&IDQ-l839S~C@O#Rs)wxbHl9T|ud4s$Rfr46XgPc@cwj{+Q<{04 zo#A#X)%*=?;Y+sgkt{9xW!pLDGw~&vkWtnKYabeDO`kKrkogx)e**oHv^`Zj)A3!Ub1z%cafWD_eu47mLhJ|E=gO_Yp#}0e7JMoUgEwT@?1oeCvj>fS5wm zLGo1`NdImaRd{4NVgzv+v5%eRABHi>juLSJF^0H^m_qy(F^#x^s3TV4&00o`IN+df zw~+eHFov?Hef4yg+05PBoi95cE-!kvte72nEk3MCE`lW0G$q+uxSRjWJa1-ZB#*%6 zeVTnkx>Z_jcaq&Xm}A-ig;swZU4cjagHj(mV6-9&h|Z+dMOOcoe6aW-D2i^D%xitF17(#(AzWn-atMe9gPd`F!c|;8FMp zfm_tI%V)2dr;7^aG{8+-A&uaOz1uS1rLD$~bACK~V($~CfVpk8+7v0KTb?}Kva{gp z2k$R5MQkisn=J`mrl5z0=m1Dz*=4tjjSFxHS-YuhoPZ3nOYH9j&Bg{;4a?#>XV<|S z370r|i(zXWjD%cv$JsaunaB(s0x2qsDc(&f=#PfoY3|whm;>I7`bE26R^WEDhbBR4 zmt85Jxea3DO7>8D7sWoy?2|=RwSp?#-WL`%?rGrd=)(5u@8}X20#?QlWIDp)#$L;N Q0ano)4q4tBNFDY60yXcEkpKVy delta 1402 zcmZvcU1$_n6vywK?AOGt?yj+$#OTfjl_cuSY;3Z#>#kW#AhQoCX;Co{a9dI}eJDwr zfCRxrQT#y6=Jde_!SzA>pbwF0N)(YuK`7DELLa2$VJ)O=UTjuh(x>UUJ7*pmdg1PG z&i|Y{_wG68&dk}x$(c53s=L_HUEkBT8tRNF^aJiQ-(R)#5Q zSKyhs-Y_!%W)yzoEwgD4EK9>&hr@Nz{Ad{Cal{?47sv<;vNGv1=|@S|NpB|od(!9N zPF*xnB5#nqOXOW6y^HkIqz_nmkDtR2-jDXLqZA_dMDqylD=~?(5<28K0_0a9?u#~+ zDO}I}*Q>*)zHedK7vu_Xe-tXdptZ}t9I$f!Mm`(*B0sA!c5;T^#mwv3=O~!hy-x&E z>*L$ZYu;nx`QqZZY?rshrbO&dM2s)i;=8WGQmBRRhDvB>dp}BSV((G&LRJ!w=9QK4 zpm{B;qEQ*NZM!mzKQFwyK?^VNFQF11;0=(9Y`1=o?E3(6{?e@CIVMV7k5gH*?K8*% zvV_#Dw!MHHK)yzfB1OVV$VN=k9MV9Rk;jp1$TLX&oo(ObFsbbEmj1TwoOM@;ajUoW zq+4o&3&7}R3YTMtZXKZ?H7Z*Xiy9s9+tIK{`2lIXq-dJ=WcK4bv+2&g@Lgxa=hOHX|F6A|kiN0KPmw-Hnc73U zTq1pAV_Q({jP6j#yYV`_LcJC5;MTZ=NfhVM>GYfEy%kzHV67fF!r@j|D=d1pL(H47 zwscQ(D02_YBn!M3s!1*Emcpe02}29J1#`I>#!!9hakg;T437*=4p6}Yuxcdh+sG1S zOLd3cYPvLGEOn0OpqkR;0#&F0?dfD>lq{D^9KK1bvRj)BJV^KPA*iOca6xg-;d01I z9PHuP6=NAqcCWGy4>C#F$T^_~tY&(!l Date: Thu, 9 Jun 2022 17:01:07 +0200 Subject: [PATCH 44/67] Format --- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 28 +++++++++---------- .../ifds/taint/ForwardTaintProblem.scala | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 3308745c9d..3b51be14d7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -59,20 +59,20 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) case _ ⇒ call.asMethodCall } - /** - * Checks if the return flow is actually possible from the given exit statement to the given successor. - * This is used to filter flows of exceptions into normal code without being caught - * - * @param exit the exit statement of the returning method - * @param successor the successor statement of the call within the callee function - * @return whether successor might actually be the next statement after the exit statement - */ - protected def isPossibleReturnFlow(exit: JavaStatement, successor: JavaStatement): Boolean = { - (successor.node.isBasicBlock || successor.node.isNormalReturnExitNode) && - (exit.stmt.astID == Return.ASTID || exit.stmt.astID == ReturnValue.ASTID) || - (successor.node.isCatchNode || successor.node.isAbnormalReturnExitNode) && - (exit.stmt.astID != Return.ASTID && exit.stmt.astID != ReturnValue.ASTID) - } + /** + * Checks if the return flow is actually possible from the given exit statement to the given successor. + * This is used to filter flows of exceptions into normal code without being caught + * + * @param exit the exit statement of the returning method + * @param successor the successor statement of the call within the callee function + * @return whether successor might actually be the next statement after the exit statement + */ + protected def isPossibleReturnFlow(exit: JavaStatement, successor: JavaStatement): Boolean = { + (successor.node.isBasicBlock || successor.node.isNormalReturnExitNode) && + (exit.stmt.astID == Return.ASTID || exit.stmt.astID == ReturnValue.ASTID) || + (successor.node.isCatchNode || successor.node.isAbnormalReturnExitNode) && + (exit.stmt.astID != Return.ASTID && exit.stmt.astID != ReturnValue.ASTID) + } override def outsideAnalysisContext(callee: Method): Option[(JavaStatement, JavaStatement, Fact, Getter) ⇒ Set[Fact]] = callee.body.isDefined match { case true ⇒ None diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index a9ef9ef001..c489272798 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -119,7 +119,7 @@ abstract class ForwardTaintProblem(project: SomeProject) * If the sanitize method was called, nothing will be tainted. */ override def returnFlow(exit: JavaStatement, in: Fact, call: JavaStatement, callFact: Fact, successor: JavaStatement): Set[Fact] = { - if(!isPossibleReturnFlow(exit, successor)) return Set.empty + if (!isPossibleReturnFlow(exit, successor)) return Set.empty val callee = exit.callable() /** From ab242a9c55d943102eed724da0564526e59d2d69 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 16 Jun 2022 17:13:54 +0200 Subject: [PATCH 45/67] Add native to java calls to TaintTest --- .../llvm/multilingual/taint/TaintTest.c | 29 +++- .../llvm/multilingual/taint/TaintTest.class | Bin 2109 -> 3675 bytes .../llvm/multilingual/taint/TaintTest.h | 6 + .../llvm/multilingual/taint/TaintTest.java | 49 ++++++- .../llvm/multilingual/taint/TaintTest.ll | 127 +++++++++++++++++- .../taint/{build.sh => build_run.sh} | 3 +- .../llvm/multilingual/taint/libtainttest.so | Bin 15888 -> 16208 bytes 7 files changed, 208 insertions(+), 6 deletions(-) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/{build.sh => build_run.sh} (82%) diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c index 9429031bc4..2dacc899e7 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c @@ -42,6 +42,29 @@ Java_TaintTest_propagate_1zero_1to_1sink(JNIEnv *env, jobject obj, jint a) { sink(b); } +JNIEXPORT void JNICALL +Java_TaintTest_propagate_1to_1java_1sink(JNIEnv *env, jobject obj, jint a) { + jclass klass = (*env)->GetObjectClass(env, obj); + // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/types.html#type-signatures + // not documented, but "V" is "void" + jmethodID java_sink = (*env)->GetMethodID(env, klass, "indirect_sink", "(I)V"); + (*env)->CallVoidMethod(env, obj, java_sink, a); +} + +JNIEXPORT int JNICALL +Java_TaintTest_propagate_1from_1java_1source(JNIEnv *env, jobject obj) { + jclass klass = (*env)->GetObjectClass(env, obj); + jmethodID java_source = (*env)->GetMethodID(env, klass, "indirect_source", "()I"); + return (*env)->CallIntMethod(env, obj, java_source); +} + +JNIEXPORT int JNICALL +Java_TaintTest_propagate_1java_1sanitize(JNIEnv *env, jobject obj, jint a) { + jclass klass = (*env)->GetObjectClass(env, obj); + jmethodID java_sanitize = (*env)->GetMethodID(env, klass, "indirect_sanitize", "(I)I"); + return (*env)->CallIntMethod(env, obj, java_sanitize, a); +} + int identity(int a) { return a; @@ -58,10 +81,12 @@ source() { } void -sink(int num) {} +sink(int num) { + printf("native %d\n", num); +} int sanitize(int num) { - return num; + return num - 19; } diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class index a45f9ea6d49b6d53ac66e4381c4ad59356e18b67..9fb1d0eb815534720fdec2d234e95e358ce5996b 100644 GIT binary patch literal 3675 zcmaJ^`F9gl6#j-T3E{O31zNjFL6$b41ym@ul%;?K%A&0lM2*vCC_|H(noO%jaToV} z1NZ$Wf6*Tb2hZ^j@Q?EN-IG^u+<8!0_!Tn<{eKKva(U!T7`9jTkGQ- z)3X<>OmRLR3biZTC4h}lfaeBAan_WQd7TPf0w4H)c**1nTY41k7VefP*LJdYfsxD9 zr1UCm5K2c)O3A2u6gDkKWi7|Ey~WVeEecx&byrmDidCSG<+%1Mqy)4+3Ys=^xga1# z+nEgow=3K$*bR5Uy7Pr;D?|?{>=1N?_Yvi)OafJ(Fd2+7c&Y3f&$6D}xke5#?JS8SIM_$s8IG$#8+(|WbDk~CG3ezId8j;ZZ z8i&O+)-t7}uVl|CToRu;N-@c}PHr({X1F(QMq(PrWdki$B5~v*4|UdyHI9NS+tFFJ z#x4~_DEN$lRa#JLL3l+4Pk3!*o?j**XMuU5Ig-FZpv696Kc}!*#U@eqLc8$1f%fI> za2u{FyeNAVvHD`c&1(f87RSrwSVk<+p%yD%Rd`KyqP^@xV1mZ9vY)@9a1Cz~lQ(Cr zQP-I^y-C}<#H*rbI$lw#A>F^c9^!bLUEOcCQuzs1>bnZ>NqMcxxn_3Eo+_AyMP~G< zo8_*zjM5orh+z-@ITUKihKS1|M44D3hT-Pgl1#|AC1c9m6*gly41e5@_&g zvz-NZ)*1-9K@g{-=CtREi`S$5LIVTQW2TeMS;b9bt~)zFH?-W*$dd}<#NwP)10D@k z@Ub0=?v0ihWj>DEp2ZIMJv)>*(Q6n`X~(e&qdBu!w2CqOWMK0$|7w{X!!HKz{(mT! z34G+ID#r7H{PluO361=HpLac+4bBazn=t<1g@F(FE3bKNtdLg5ixtwG{FzO}pV^Q6 znQg(JSrfM5SXtj93l9%ih#D`AB8YBSf#Qv117YVsBi4yPu&ZsACI*;dqZ z*-cz_Gn%o5OKznM&r90d5iQdxGUT`} zzk@wHsIe2P*alYPezJMs(i$gF=Ucp9G#&}bKE%ikQIZEWEj*}=TPwnoBD{q2!>O(%JTX}vt`Vy3M-vWkuMU#W^P5)LP4grL z+e9TJDk;%ff?284OdPmpaU7v@N6FziPHPakT1t7 z@NtqV=a^9EiRNLk7JDva7pmD$ko_dtPm#w{WG(i5$bPn(ZIV4j_B44sS8DbyZMmO@ zSNvdbk6x$V88abi5_>}#_IsRE(bd>m?7iBR1{JM0X32IgU{@3p0?P!jQ`?n5W~CE1t}r3EaSvx3uJzh1HBM_6 zLP>?I0+{UtxK1l6TA$KRFRhRfc<%l1cFJA3Oe=gK+?$5TtF>Tj!&n|X)Q{f{)UG3C*W7fIHR$YfM4?T=q3JX~5YVO`y!!>K{sdLH} zPwaSYx18LwIt#w%)=i_f7C}x!f5oxQC~o6U7#}O##XSvUJU);8^en68S(by$A(ZeVH6+BZ3@o8n|3vukErxb(H4)_+;fiyO zaZPfixTd(Sam{kw_ygI*QWMKTS}Pc#&?q8^b5331Gl6l8aZrq7it}U!le}9j6Tom36kT{=WX(9KeI}<;>MHfdS1)@PX&xlI|4QeSMrZ->&G*v{*jCJM#N+rLYldl<$FIh?9KQ)e0)^<%m& z+$MIqx$-}VZDx<|6DK8X9e`Exq^J`?`bBR8PrH&&czawYZG*{gvZW6wz$yDanJMPq zOJ<9-hm?6lnQaPi4Er*3%u;p{(b@hco(Fqx>?^7(Pzks zlV2#V|3=e!r=AnY5$OX{o|lL~Q=$Sd2;;=`VQE_V+Lyj8cgVY7Tx2@yp?G~0-<;#y RK(n>8VRls!Q5Gc}{R?_tgjWCn diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h index 97da211dca..1c2828dfda 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h @@ -22,6 +22,12 @@ JNIEXPORT void JNICALL Java_TaintTest_propagate_1identity_1to_1sink(JNIEnv *env, JNIEXPORT void JNICALL Java_TaintTest_propagate_1zero_1to_1sink(JNIEnv *env, jobject obj, jint a); +JNIEXPORT void JNICALL Java_TaintTest_propagate_1to_1java_1sink(JNIEnv *env, jobject obj, jint a); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1from_1java_1source(JNIEnv *env, jobject obj); + +JNIEXPORT int JNICALL Java_TaintTest_propagate_1java_1sanitize(JNIEnv *env, jobject obj, jint a); + int identity(int a); int zero(int a); diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java index 14ef0d0bf3..47b1f8f17f 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java @@ -6,6 +6,9 @@ public class TaintTest { private native int sanitize_only_a_into_sink (int a, int b); private native void propagate_identity_to_sink(int a); private native void propagate_zero_to_sink(int a); + private native void propagate_to_java_sink(int a); + private native int propagate_from_java_source(); + private native int propagate_java_sanitize(int a); static { System.loadLibrary ("tainttest"); @@ -23,25 +26,32 @@ public static void main (String[] args) demo.test_native_indirect_flow(); demo.test_native_identity_flow(); demo.test_native_zero_no_flow(); + demo.test_native_call_java_sink_flow(); + demo.test_native_call_java_source_flow(); + demo.test_native_call_java_sanitize_no_flow(); System.out.println("done"); } public void test_java_flow() { + System.out.println("java"); int tainted = this.source(); this.sink(tainted); } public void test_java_sanitize_no_flow() { + System.out.println("java sanitize"); int tainted = this.source(); this.sink(this.sanitize(tainted)); } public void test_java_untainted_no_flow() { + System.out.println("java untainted"); int untainted = 23; this.sink(untainted); } public void test_native_sum_flow() { + System.out.println("native sum"); int tainted = this.source(); int untainted = 23; int native_tainted = this.sum(tainted, untainted); @@ -49,43 +59,78 @@ public void test_native_sum_flow() { } public void test_native_to_java_to_native_flow() { + System.out.println("native to java to native"); int taint = this.propagate_source(); this.propagate_sink(taint); } public void test_native_to_java_to_native_sanitized_no_flow() { + System.out.println("native to java to native sanitized"); this.propagate_sink(this.propagate_sanitize(this.propagate_source())); } public void test_native_indirect_sanitized_no_flow() { + System.out.println("native indirect sanitized"); int tainted = this.source(); int untainted = 23; this.sink(this.sanitize_only_a_into_sink(tainted, untainted)); } public void test_native_indirect_flow() { + System.out.println("native indirect"); int tainted = this.source(); int untainted = 23; this.sink(this.sanitize_only_a_into_sink(untainted, tainted)); } public void test_native_identity_flow() { + System.out.println("native identity"); this.propagate_identity_to_sink(source()); } public void test_native_zero_no_flow() { + System.out.println("native zero"); this.propagate_zero_to_sink(source()); } + + public void test_native_call_java_sink_flow() { + System.out.println("native call java sink"); + this.propagate_to_java_sink(source()); + } + + public void test_native_call_java_source_flow() { + System.out.println("native call java source"); + this.sink(this.propagate_from_java_source()); + } + + public void test_native_call_java_sanitize_no_flow() { + System.out.println("native call java sanitize"); + this.sink(this.propagate_java_sanitize(this.source())); + } + + public int indirect_source() { + return source(); + } + + public void indirect_sink(int a) { + sink(a); + } + + public int indirect_sanitize(int a) { + return sanitize(a); + } private static int source() { return 42; } - private static void sink(int a) {} + private static void sink(int a) { + System.out.println("java " + a); + } private static int sanitize(int a) { - return a; + return a - 19; } } diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll index 6311c10be1..795c6fd28f 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll @@ -12,6 +12,14 @@ target triple = "x86_64-pc-linux-gnu" %struct.JNIInvokeInterface_ = type { i8*, i8*, i8*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i32)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)* } %struct._jobject = type opaque +@.str = private unnamed_addr constant [14 x i8] c"indirect_sink\00", align 1 +@.str.1 = private unnamed_addr constant [5 x i8] c"(I)V\00", align 1 +@.str.2 = private unnamed_addr constant [16 x i8] c"indirect_source\00", align 1 +@.str.3 = private unnamed_addr constant [4 x i8] c"()I\00", align 1 +@.str.4 = private unnamed_addr constant [18 x i8] c"indirect_sanitize\00", align 1 +@.str.5 = private unnamed_addr constant [5 x i8] c"(I)I\00", align 1 +@.str.6 = private unnamed_addr constant [11 x i8] c"native %d\0A\00", align 1 + ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @Java_TaintTest_sum(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2, i32 noundef %3) #0 { %5 = alloca %struct.JNINativeInterface_**, align 8 @@ -57,7 +65,8 @@ define dso_local i32 @sanitize(i32 noundef %0) #0 { %2 = alloca i32, align 4 store i32 %0, i32* %2, align 4 %3 = load i32, i32* %2, align 4 - ret i32 %3 + %4 = sub nsw i32 %3, 19 + ret i32 %4 } ; Function Attrs: noinline nounwind optnone uwtable @@ -77,6 +86,8 @@ define dso_local i32 @Java_TaintTest_propagate_1sink(%struct.JNINativeInterface_ define dso_local void @sink(i32 noundef %0) #0 { %2 = alloca i32, align 4 store i32 %0, i32* %2, align 4 + %3 = load i32, i32* %2, align 4 + %4 = call i32 (i8*, ...) @printf(i8* noundef getelementptr inbounds ([11 x i8], [11 x i8]* @.str.6, i64 0, i64 0), i32 noundef %3) ret void } @@ -150,12 +161,126 @@ define dso_local i32 @zero(i32 noundef %0) #0 { ret i32 0 } +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @Java_TaintTest_propagate_1to_1java_1sink(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + %7 = alloca %struct._jobject*, align 8 + %8 = alloca %struct._jmethodID*, align 8 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %9 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %10 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %9, align 8 + %11 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %10, i32 0, i32 31 + %12 = load %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)** %11, align 8 + %13 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %14 = load %struct._jobject*, %struct._jobject** %5, align 8 + %15 = call %struct._jobject* %12(%struct.JNINativeInterface_** noundef %13, %struct._jobject* noundef %14) + store %struct._jobject* %15, %struct._jobject** %7, align 8 + %16 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %17 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %16, align 8 + %18 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %17, i32 0, i32 33 + %19 = load %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)** %18, align 8 + %20 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %21 = load %struct._jobject*, %struct._jobject** %7, align 8 + %22 = call %struct._jmethodID* %19(%struct.JNINativeInterface_** noundef %20, %struct._jobject* noundef %21, i8* noundef getelementptr inbounds ([14 x i8], [14 x i8]* @.str, i64 0, i64 0), i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @.str.1, i64 0, i64 0)) + store %struct._jmethodID* %22, %struct._jmethodID** %8, align 8 + %23 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %24 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %23, align 8 + %25 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %24, i32 0, i32 61 + %26 = load void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)** %25, align 8 + %27 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %28 = load %struct._jobject*, %struct._jobject** %5, align 8 + %29 = load %struct._jmethodID*, %struct._jmethodID** %8, align 8 + %30 = load i32, i32* %6, align 4 + call void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...) %26(%struct.JNINativeInterface_** noundef %27, %struct._jobject* noundef %28, %struct._jmethodID* noundef %29, i32 noundef %30) + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_TaintTest_propagate_1from_1java_1source(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1) #0 { + %3 = alloca %struct.JNINativeInterface_**, align 8 + %4 = alloca %struct._jobject*, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca %struct._jmethodID*, align 8 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %3, align 8 + store %struct._jobject* %1, %struct._jobject** %4, align 8 + %7 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %8 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %7, align 8 + %9 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %8, i32 0, i32 31 + %10 = load %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)** %9, align 8 + %11 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %12 = load %struct._jobject*, %struct._jobject** %4, align 8 + %13 = call %struct._jobject* %10(%struct.JNINativeInterface_** noundef %11, %struct._jobject* noundef %12) + store %struct._jobject* %13, %struct._jobject** %5, align 8 + %14 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %15 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %14, align 8 + %16 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %15, i32 0, i32 33 + %17 = load %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)** %16, align 8 + %18 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %19 = load %struct._jobject*, %struct._jobject** %5, align 8 + %20 = call %struct._jmethodID* %17(%struct.JNINativeInterface_** noundef %18, %struct._jobject* noundef %19, i8* noundef getelementptr inbounds ([16 x i8], [16 x i8]* @.str.2, i64 0, i64 0), i8* noundef getelementptr inbounds ([4 x i8], [4 x i8]* @.str.3, i64 0, i64 0)) + store %struct._jmethodID* %20, %struct._jmethodID** %6, align 8 + %21 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %22 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %21, align 8 + %23 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %22, i32 0, i32 49 + %24 = load i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)** %23, align 8 + %25 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %3, align 8 + %26 = load %struct._jobject*, %struct._jobject** %4, align 8 + %27 = load %struct._jmethodID*, %struct._jmethodID** %6, align 8 + %28 = call i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...) %24(%struct.JNINativeInterface_** noundef %25, %struct._jobject* noundef %26, %struct._jmethodID* noundef %27) + ret i32 %28 +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @Java_TaintTest_propagate_1java_1sanitize(%struct.JNINativeInterface_** noundef %0, %struct._jobject* noundef %1, i32 noundef %2) #0 { + %4 = alloca %struct.JNINativeInterface_**, align 8 + %5 = alloca %struct._jobject*, align 8 + %6 = alloca i32, align 4 + %7 = alloca %struct._jobject*, align 8 + %8 = alloca %struct._jmethodID*, align 8 + store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %4, align 8 + store %struct._jobject* %1, %struct._jobject** %5, align 8 + store i32 %2, i32* %6, align 4 + %9 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %10 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %9, align 8 + %11 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %10, i32 0, i32 31 + %12 = load %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)** %11, align 8 + %13 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %14 = load %struct._jobject*, %struct._jobject** %5, align 8 + %15 = call %struct._jobject* %12(%struct.JNINativeInterface_** noundef %13, %struct._jobject* noundef %14) + store %struct._jobject* %15, %struct._jobject** %7, align 8 + %16 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %17 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %16, align 8 + %18 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %17, i32 0, i32 33 + %19 = load %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)** %18, align 8 + %20 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %21 = load %struct._jobject*, %struct._jobject** %7, align 8 + %22 = call %struct._jmethodID* %19(%struct.JNINativeInterface_** noundef %20, %struct._jobject* noundef %21, i8* noundef getelementptr inbounds ([18 x i8], [18 x i8]* @.str.4, i64 0, i64 0), i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @.str.5, i64 0, i64 0)) + store %struct._jmethodID* %22, %struct._jmethodID** %8, align 8 + %23 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %24 = load %struct.JNINativeInterface_*, %struct.JNINativeInterface_** %23, align 8 + %25 = getelementptr inbounds %struct.JNINativeInterface_, %struct.JNINativeInterface_* %24, i32 0, i32 49 + %26 = load i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)** %25, align 8 + %27 = load %struct.JNINativeInterface_**, %struct.JNINativeInterface_*** %4, align 8 + %28 = load %struct._jobject*, %struct._jobject** %5, align 8 + %29 = load %struct._jmethodID*, %struct._jmethodID** %8, align 8 + %30 = load i32, i32* %6, align 4 + %31 = call i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...) %26(%struct.JNINativeInterface_** noundef %27, %struct._jobject* noundef %28, %struct._jmethodID* noundef %29, i32 noundef %30) + ret i32 %31 +} + ; Function Attrs: noinline nounwind optnone uwtable define dso_local i32 @source() #0 { ret i32 42 } +declare i32 @printf(i8* noundef, ...) #1 + attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } !llvm.module.flags = !{!0, !1, !2, !3, !4} !llvm.ident = !{!5} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build_run.sh similarity index 82% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build_run.sh index 935602dc5d..3f98574bdb 100755 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build.sh +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build_run.sh @@ -1,3 +1,4 @@ clang -shared -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ -o libtainttest.so TaintTest.c clang -S -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ TaintTest.c -emit-llvm -javac TaintTest.java \ No newline at end of file +javac TaintTest.java +java -Djava.library.path=. TaintTest \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libtainttest.so b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libtainttest.so index 01bad8136eaad9130c78afaf20eb907bceca593e..302155e53b6492b7c8de9abb516c158d9fc771d6 100755 GIT binary patch literal 16208 zcmeHOYiu0V6~1;JIL*UL2oMsBOwte~G%R+SKnMiZan>1AC#1nfr6rxLy=!}^y=(2x zhB!)E(*%OGiDU`dAB9$?0;yCHjVMUaB5Fv4s?r~Zq87?SE(K{_tYQjOZk1;Hojd2u zjwkCRNL8g)b1m<8&vzd8%-Nao9nZbri8eP@R#YfyGt|whBq3F^jq^`|&vrDkysL(<+0U(G30RV$BRk7WDefhAI?x^r4%l zVx-cMO-At}k^BtyGuTgXn2ROIhpTg>JU4PYKdy9!Q-b=K!0r6Nj#Izca18wTkV7j$ z@%t+LD9;ORQubXu-gI@D;NufyQ6JwT3`XVNwxd<8tNQ+1*Vi7N_}#njY zezcHsAwA_qWw;2xD{JSjTse5^6uPsjW=DoeM>Si3-!reS`RJbFrkWStIx_b4@gE-e zLE&px935DFYTb3cAI^X08zZ4QYtE*(9`d>OyS;+YkHrwalMjex75lzRCOq^HPV`pr<-SnBTzmUCj4$o>wxjEUU98owBla zEMr@ivYJ}9Tk%9D(V5KJiA>A(hVFDK(GqLxPB^;q8moO@%<4#{V%^F833Y31Z_H|m zB~x}wB5PaOT#ujX&7^x{oiRIM)n(JUO#4|rUV~UFX_pD>HA$wvrFt_Mu){x6mc6aI zbgH}0s*725FiT@x^6_Qr37L#1Qg+gYhifdDg8=j6VZTauhZ zccpP~)a37h+`o77dA6AE_riymHzV3j-@B+M^5`uNPxmzrt%-t%=RZ-JpYZTcZwUcm!B>hXJkzaG96&R>sbug3SV`=oO3Rqovhe9(dl1QiG>5L6(j zKv0380zn0W3Ir9n;0h#D@nj~^j+b2YI%##&nmbg9a^BReUen~^HWj&9~Fzofm^ z`$82<=Ur4ah&U^eOAUSvHKSOehfih zZhQ(Ew4ee(1%e6$6$mO2R3NB8P=TNV|L+x$b#JoH4O3{{Xzd&|J=doWFQk(-V#oPG z>k87L$$Gk_j_9tF)6YuaWL+K23l5iPoF=HvylJRi!MHhL#Y?F19TS8+%3{E1_Rb?{D;@$k}9=U?DN z@g`WyQb#WPmcHB0$IoIAMZN;ej6F@VC-Pr%XmMd8F3U)M?ne;&ceVXxZheIRi{>v?Dk2Oc4y=-{7GM2mgQxZ0A-)jrW%Fy9AL7NV%bDK= z-r&9$Qf%MPi&^#mEMV}k=Dn6q|4FtV_Sye|dH;I#QSipuVbo)|59$0l&I-2qh~w;c=^3 zc@p+3yoTKG}77OSOxOLG(fY4CF_q4zCzpME{R>;pLG4u|R8W$nLbalf&3f5 zR5;t$W5?QncE-Uj>7~+kBHWqEh1+t;?)WvyxYANrEZe2R@xBy19ker!hW}7b5iIC3 ziS8IVaId%9R$+bGh0zUnrXkviefZQTcQ})d$LyF2C%X75>53zWNRF#>xi}uu#|W{W zWIKjW+Y}ke={RlKtP10T?ZI_e=8p0CD4H+QR13Eg-KN_&$7;O~P5OkiTJP_eQ;Gi` z?g)0VvCOw*KF2h6HadH<#9!u_f-=vf=_qcU?z~mze-UsLTl{6-Dk%Oi(T!qwn+bvD zjN&izM8T`sSn?M;!B*&L4l1(DLj?`?_vg>Filak1@ptFFJpYw>uZ%DCljBdZ{}v7? z^Ju|A=E)zkgtO|m4;?hdkHAbJ+E`HnRKgb4x za=a2oWWggo{~`7loa;+i#+P`I{F)+Bp*d#oooPUB3gQvcH z|ILPDDEN>gx}W~>pMb{eKWaFpf;UNEjv#eoQT-epnybtCCC`zvKhpIbYnmhJ4Y(X!qa@0&OPy$`LOK&9j1drlj9aY`5wOu9hr!~><1gYmb{cQ^k?Zq z`~_c!%ImKpY+LJ~$)bSx3BCh`*I%B?O!gN$zrVvvV4M8q3=X5Qt-Q zS0(;maT%v0{$BK*BHD0>0}gXyG@qtN200$N|IqWe1ZU delta 2252 zcmZuyZ){Ul6uNm=^5bV`6Zz|Z{#GYvrkOz*4jBgv z$sS-;v|?2xSNPhZorb}5P8=0DATL!pmi)A%2%kz(n4%7tlBz)|%XA(>t)F;F(T@uh zX3b;}2jlZTYfDrc$T;UwX^ETjTRGnW=gNZpMeg+=*Pi6s0_VM)Uld+E+QXdJC_lY~ ze}MBj&WkwPIA5^&HFsX)JAQz|9oF#!Ugmrkj5@Bt;lw8RQQiV4oHg*3b45KfqWH^V zScQ8WsM&+>Dv{klUBQ^aU!*#1tIN3+;k;qo&l|AW)fD2lZ>i?nYzuZpWpKn5gtuK@ zS`EJPAbjichImd{Myn0mmT^nrcULgvu?cHV8uo^1<*{>B6XowJhcxAOv@GrqWQODfTfb4*?>46@!#QSqfg{=vSoZ4KjwYkpoRz zGq0z9(Qug=ys_V#8A`1{e)?(CG^by{XB2ZlC(trEX& znu8nXhWh5p`lhEHw_&gU5$`wuGIDwdnM-iezt;P~UE(eH)vu17s>)W3b(9xrKdmim zWks?SM`YeKM-X#}1;ma8)4YnO2tgZ~5!PTAn$G~+svN*b+T@BMN*k#QFIOm|f zp;n%>bm!Et%AVOOp!TK8Vd?QD&JA7{i^L+-;n`h48^SaXD$ehtFI(@j)&tghpdmO` zy>ih59p#0PNJ)``=x<3D8nv)A>4z=abLw`7O|qZAqt?cmEA; zg&`~v?Q%>+<^7_@R+Dkg^G0^Jk8!@VvCeY7w7&)MUL)A|f1tklh=^Izd}Cg(VlBE)B%r`|=!@umMI}bk96zG2>=i5*~%;Bg@0}iF%Qq0( Date: Fri, 17 Jun 2022 15:56:59 +0200 Subject: [PATCH 46/67] Fix crashes and old tests --- .../ll/fpcf/analyses/ifds/NativeForwardICFG.scala | 13 ++++++++++--- .../ll/fpcf/analyses/ifds/NativeIFDSProblem.scala | 8 +++++++- .../ifds/taint/NativeForwardTaintProblem.scala | 10 +++++++++- .../scala/org/opalj/ll/llvm/value/Function.scala | 6 +++++- .../scala/org/opalj/ll/llvm/value/Instruction.scala | 12 ++++++------ 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index 6d438f78c9..db673d42a2 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -12,6 +12,8 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun * @return The statements at which the analysis starts. */ override def startStatements(callable: Function): Set[LLVMStatement] = { + if (callable.basicBlockCount == 0) + throw new IllegalArgumentException(s"${callable} does not contain any basic blocks and likely should not be in scope of the analysis") Set(LLVMStatement(callable.entryBlock.firstInstruction)) } @@ -33,9 +35,14 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun * @return All callables possibly called at the statement or None, if the statement does not * contain a call. */ - override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[Function]] = statement.instruction match { - case call: Call ⇒ Some(Set(call.calledValue)) - case _ ⇒ None + override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[Function]] = { + statement.instruction match { + case call: Call ⇒ call.calledValue match { + case function: Function ⇒ Some(Set(function)) + case _ ⇒ Some(Set()) // TODO + } + case _ ⇒ None + } } override def isExitStatement(statement: LLVMStatement): Boolean = statement.instruction match { diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index 51c13ac346..2a18289aeb 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -4,6 +4,7 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.PropertyStore +import org.opalj.ifds.Dependees.Getter import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem} import org.opalj.ll.LLVMProjectKey import org.opalj.ll.llvm.value.Function @@ -12,5 +13,10 @@ abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) val llvmProject = project.get(LLVMProjectKey) - override def outsideAnalysisContext(callee: Function): Option[OutsideAnalysisContextHandler] = None + override def outsideAnalysisContext(callee: Function): Option[(LLVMStatement, LLVMStatement, Fact, Getter) ⇒ Set[Fact]] = { + callee.basicBlockCount match { + case 0 ⇒ Some((_: LLVMStatement, _: LLVMStatement, in: Fact, _: Getter) ⇒ Set(in)) + case _ ⇒ None + } + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 9904362c36..b0e16588dd 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -3,7 +3,7 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeIFDSProblem} -import org.opalj.ll.llvm.value.{Add, Alloca, Call, Function, Load, PHI, Ret, Store} +import org.opalj.ll.llvm.value.{Add, Alloca, Call, Function, GetElementPtr, Load, PHI, Ret, Store, Sub} import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[Function, LLVMStatement, NativeFact] { @@ -35,6 +35,14 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD case NativeVariable(value) if value == add.op1 || value == add.op2 ⇒ Set(in, NativeVariable(add)) case _ ⇒ Set(in) } + case sub: Sub ⇒ in match { + case NativeVariable(value) if value == sub.op1 || value == sub.op2 ⇒ Set(in, NativeVariable(sub)) + case _ ⇒ Set(in) + } + case gep: GetElementPtr ⇒ in match { + case NativeVariable(value) ⇒ Set(in) // TODO + case _ ⇒ Set(in) + } case _ ⇒ Set(in) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala index 1581640360..ab0c5c9ff7 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala @@ -11,6 +11,7 @@ case class Function(ref: LLVMValueRef) extends Value(ref) { def basicBlocks: BasicBlockIterator = { new BasicBlockIterator(LLVMGetFirstBasicBlock(ref)) } + def basicBlockCount: Int = LLVMCountBasicBlocks(ref) def arguments: ArgumentIterator = { new ArgumentIterator(LLVMGetFirstParam(ref)) @@ -21,7 +22,10 @@ case class Function(ref: LLVMValueRef) extends Value(ref) { Argument(LLVMGetParam(ref, index)) } - def entryBlock: BasicBlock = BasicBlock(LLVMGetEntryBasicBlock(ref)) + def entryBlock: BasicBlock = { + if (basicBlockCount == 0) throw new IllegalStateException("this function does not contain any basic block and may not be defined") + BasicBlock(LLVMGetEntryBasicBlock(ref)) + } def viewCFG(): Unit = { val cfg_dot = org.opalj.graphs.toDot(Set(entryBlock)) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala index 0b76e584ea..6860e3b27a 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala @@ -14,6 +14,7 @@ object OptionalInstruction { object Instruction { def apply(ref: LLVMValueRef): Instruction = { + assert(ref != null && !ref.isNull(), "ref may not be null") assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") LLVMGetInstructionOpcode(ref) match { case LLVMRet ⇒ Ret(ref) @@ -105,10 +106,6 @@ sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { def function: Function = parent.parent def next: Option[Instruction] = OptionalInstruction(LLVMGetNextInstruction(ref)) - /*def successors(): Seq[Instruction] = next match { - case Some(successor) ⇒ Seq(successor) - case None ⇒ Seq() - }*/ override def toString: String = { s"${this.getClass.getSimpleName}(${repr})" } @@ -135,7 +132,10 @@ case class Add(ref: LLVMValueRef) extends Instruction(ref) { def op2: Value = operand(1) } case class FAdd(ref: LLVMValueRef) extends Instruction(ref) -case class Sub(ref: LLVMValueRef) extends Instruction(ref) +case class Sub(ref: LLVMValueRef) extends Instruction(ref) { + def op1: Value = operand(0) + def op2: Value = operand(1) +} case class FSub(ref: LLVMValueRef) extends Instruction(ref) case class Mul(ref: LLVMValueRef) extends Instruction(ref) case class FMul(ref: LLVMValueRef) extends Instruction(ref) @@ -179,7 +179,7 @@ case class ICmp(ref: LLVMValueRef) extends Instruction(ref) case class FCmp(ref: LLVMValueRef) extends Instruction(ref) case class PHI(ref: LLVMValueRef) extends Instruction(ref) case class Call(ref: LLVMValueRef) extends Instruction(ref) { - def calledValue: Function = Function(LLVMGetCalledValue(ref)) + def calledValue: Value = Value(LLVMGetCalledValue(ref)).get def calledFunctionType: Type = Type(LLVMGetCalledFunctionType(ref)) def indexOfArgument(argument: Value): Option[Int] = { for (i ← 0 to numOperands) From 943d7a18343a0cb6ce657dfeb16b51bb1239b3e7 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Sat, 18 Jun 2022 20:52:43 +0200 Subject: [PATCH 47/67] Add debugData for ifds; Add simple GetElementPointer handling --- .../analyses/heros/analyses/VTAEquality.scala | 4 +- ...rdClassForNameTaintAnalysisScheduler.scala | 2 +- .../llvm/multilingual/taint/TaintTest.c | 14 ++++++ .../llvm/multilingual/taint/TaintTest.class | Bin 3675 -> 3561 bytes .../llvm/multilingual/taint/TaintTest.h | 4 ++ .../llvm/multilingual/taint/TaintTest.java | 22 +++++++-- .../llvm/multilingual/taint/TaintTest.ll | 44 +++++++++++++++++- .../llvm/multilingual/taint/libtainttest.so | Bin 16208 -> 16392 bytes .../taint/BackwardFlowPathMatcher.scala | 2 +- .../vta/ExpectedCalleeMatcher.scala | 2 +- ...MultilingualForwardTaintAnalysisTest.scala | 24 +++++++++- OPAL/ifds/src/main/resources/reference.conf | 5 ++ .../scala/org/opalj/ifds/IFDSAnalysis.scala | 27 ++++++----- .../scala/org/opalj/ifds/IFDSProperty.scala | 8 +++- .../main/scala/org/opalj/ifds/package.scala | 4 +- OPAL/ll/src/main/resources/reference.conf | 5 ++ .../ifds/taint/JavaForwardTaintAnalysis.scala | 2 +- .../taint/NativeForwardTaintProblem.scala | 31 +++++++++--- .../ifds/taint/NativeTaintProblem.scala | 2 +- .../org/opalj/ll/fpcf/properties/Taint.scala | 4 +- .../org/opalj/ll/llvm/value/Instruction.scala | 7 ++- .../opalj/ll/llvm/value/ScalarConstants.scala | 4 +- .../old/IFDSBasedVariableTypeAnalysis.scala | 4 +- .../opalj/tac/fpcf/properties/OldTaint.scala | 4 +- .../org/opalj/tac/fpcf/properties/Taint.scala | 4 +- 25 files changed, 185 insertions(+), 44 deletions(-) create mode 100644 OPAL/ifds/src/main/resources/reference.conf create mode 100644 OPAL/ll/src/main/resources/reference.conf diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala index 7f460e3180..0dd686bfb7 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala @@ -123,8 +123,8 @@ object VTAEquality { } .foreach { entity ⇒ val entityResult = propertyStore(entity, IFDSBasedVariableTypeAnalysisScheduler.property.key) match { - case FinalEP(_, VTAResult(map)) ⇒ map - case _ ⇒ throw new RuntimeException + case FinalEP(_, VTAResult(map, _)) ⇒ map + case _ ⇒ throw new RuntimeException } entityResult.keys.foreach { declaredMethodStatement ⇒ val statement = declaredMethodStatement.asJavaStatement diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index b78debe065..7df17bb5bc 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -112,7 +112,7 @@ class BackwardClassForNameTaintAnalysisRunner extends AbsractIFDSAnalysisRunner case EPS((m: DefinedMethod, inputFact)) if canBeCalledFromOutside(m, ps) ⇒ (m, inputFact) }.flatMap(ps(_, propertyKey) match { - case FinalEP(_, OldTaint(result)) ⇒ + case FinalEP(_, OldTaint(result, _)) ⇒ result.values.fold(Set.empty)((acc, facts) ⇒ acc ++ facts).filter { case FlowFact(_) ⇒ true case _ ⇒ false diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c index 2dacc899e7..ce8eb75058 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c @@ -42,6 +42,20 @@ Java_TaintTest_propagate_1zero_1to_1sink(JNIEnv *env, jobject obj, jint a) { sink(b); } +JNIEXPORT void JNICALL +Java_TaintTest_native_1array_1tainted(JNIEnv *env, jobject obj) { + int a[2] = {0, 0}; + a[1] = source(); + sink(a[1]); +} + +JNIEXPORT void JNICALL +Java_TaintTest_native_1array_1untainted(JNIEnv *env, jobject obj) { + int a[2] = {0, 0}; + a[0] = source(); + sink(a[1]); +} + JNIEXPORT void JNICALL Java_TaintTest_propagate_1to_1java_1sink(JNIEnv *env, jobject obj, jint a) { jclass klass = (*env)->GetObjectClass(env, obj); diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class index 9fb1d0eb815534720fdec2d234e95e358ce5996b..fda9c3b9cfc8415e81aa367cb86b7d1ab0d4c757 100644 GIT binary patch literal 3561 zcmaJ^X?GMw6uq5oCX-Gw*~tb00w%+fpb)`?utYF{u$hp6fI}zK37yPz$C*wrsHmX0 zpn{5uyMFhJeh3^q#~xt zxYmjpO)V!fL@hcth{2Xz1K07LVy_@+_u6!{i+SI(&C@RD`O>LlqhPn^Vl6vnC7HQs zQAn4L%>rpH3Q0S)RY&)FtC(rKmb;pXbi0n{#A-t>*9|kt7_)ip)e#Y+jk!ifGHI+v z)7ee0x>Lt4G2Ob}blp}|q7VH6?9uT&UdYqU#A?bl6O@ayO!YL4dy2B0zOza0<%F9w zjl^IO`!tlr9NP@xMeGmYfR2|isG*^VwT8fwVB@P&!i-XGa5p^th%T~!JpIdDkiuz-!+u?jE(gYd!Box>|CeIV`1-)2x?l#1@Sz* zlysJidBdf$y%=QjH6544rJ9^c#u3C7YW-=fnV@1q5!G>3DC%=4)KLs#hN4t7X$j|} zV>(QUsWqLGsAI=hqehf_<3uH=LCkBYEg%WP${AD|7H<}H#AQ3$vffx}iDwA8=T(YO zlr6X=9d8J(Ig9f|pTVS99TdhBdY2NqfR}Zwh{lycH18w?@g`~WpqX73@C_Yr30RpZ zaK@ybCG{O0H}S59mV~irjyU$L;Z9rbH9n= zawe+cju9KTW|Bs7l`d#$vg{RS(d_d}f}i)} z#;of|${ywx4Q1$)jv96>Zl=1&9cOWQX>fg@IU{+%$<-yZhsge%p0&a2d8WOdXVmL? z+`OJA$Ll{*;~y)YPXv!je2TkVOI-Pq4vLFM{zl;9BUFy>SVM@dnk~%Mz}C#x%GSZQ ziERtpHntwN9e-hW{oXYUWWyNd9Rz4Zke3G?%?P7~ZyWf(6&+|p7cT+beClBycJia% zk4@Oi(fu4fjBRv`*Bj;D0p(tkEHlJqBGZpCJQSI3e~hEqCF@bfC40DJFKYM=Zp01} z@G7Keh80bl&>YX9@rXu?aP1~nAG!MJ^d7SCzNEMY6j!})jgu?f@)Upel4BpLFn~IK zd0CAz94ArgdAvzDPG|fHw;YVLcdX&;aAdkO*-!OwOOgLG<$ga?_=VP@|TiFpT8 z&h1yuH45>h5U*isDAK-$i_?YaI!d+?RP#ICK$-A1m|+>9dm_bVVYw_U5#d?G>-ju2 zv;#)TGsfUf5W_2)^5hujWVTZ{jJy!1y&z}v0&M2-08WwWGy^=t0MAk)=SapYTrtYI z0?H1?Sd4O#l;=q~MapSXULYCody2B3`I1P(;^j4=q`cO!kj<4J<0Z0Orl79Sn)kqB z=tKtX6r!&ZZ4f;}9509py-TfKEo&zQy#}`sY7#of12In!FO0>Y*L~<}A#{PzMMC2Q zeZ~UdANUUT5^)&={eyjO>AA_b&pjq^Ru)-Ub+JHcsD_dyMfuVZ20UT=93cye-@i zD_r3L({eA9$t}v2iu(5+<5nScn5wL!jPKLg64uJE*awODn(cf1$hQ!me!?&K4XytJ Dav>M5 literal 3675 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 delta 2103 zcmZWqe@qis9DlE+^sX&(rNvs-U_r(#vR)xjr2Hz8WanZWWSR_@2~o3wVTLS_MdDVL zq0Wf0_$AZKY!>4FU?Ij?O(aVkiDX;mwxBcHjMIS0k~&$yjU_B`-oDrS-hOm1x%<5L z`F_6d-n;v}@4Yj~XWxvkH??jYDGS@80!z23FtRsjo66EhQ&}V9LD6T{N4}9;vKTe# z0|lJ?j5w+f*eFc#MIy-yeH`XEmeX!+TAX{lDcN<#LMlK9LF zb}d47bXtpP&tHYt_bL z`lRNfH_mN!cMV+_J8(r=ad%&u=e=;9uK~X~1k<4`=rOm%hB;$-%)#O=oEqDJ4(tN) z3Aawzi9dp<=@U+!C?fta>81337%L`zJLx+~UqJk%&d1ms1xhKfnMf@!3 zbBUia9!v{0^s4>Pr_=my#*9EY1)_t7AY3oz3=vvM=yNAZ$>1g8 zmFR!<3Iv1=UNUDowA7?4G(%;05-&}W4zG{uD$R-_d@Xpqw8u2P@n6v# z_*3w3YqBl>n)QTqV)HJUv6tg|He<@Mh6yB;W0r9ZYFGO5m7|tYt$%R3PgLh}*A8mh ztAwcEpVn8u1`i}3{~ZKpVW1i**1uu9@^-_GAa<1_KlM43qYbN(PqnM6n%aZ6&n!9c zob#UO#WQlArLrDFB<~KKbTtbX@k=Vlaz24M*FiC&4>mbqpL;;Kh946{-zAsak{zOA z{Di+XrY&E48#fnKbrP{X$O$WBZ;mcr<$Kj9QTIYGqB-jX;00CJx zrLtj%TxhzS4FmEwT!FUGah`zrkjFb~m|ag}MTjOWC=xg;JiHYG6*Au2D>|@HO;yO= zNjldgXP2ooe%!6G7nNhW`UeQKD}gU ztrGHQ={)Dsd%KF!Vr@f3`zb%Ovfm|sFgaE=%wf)Ahn?Y4I2jHkQ{i?KD7Bru3npsi zJi`wLq91;)4dxlPF%appNYHtiY-C^=bVp=cidZztcqA+eWFG Date: Sun, 19 Jun 2022 20:02:46 +0200 Subject: [PATCH 48/67] Native to Java calls WIP --- .../taint/TaintTest.c | 0 .../llvm/cross_language/taint/TaintTest.class | Bin 0 -> 4038 bytes .../taint/TaintTest.h | 0 .../taint/TaintTest.java | 8 +-- .../taint/TaintTest.ll | 0 .../taint/build_run.sh | 0 .../taint/libtainttest.so | Bin .../taint/run.sh | 0 .../llvm/multilingual/simple/NativeTest.c | 21 ------- .../llvm/multilingual/simple/NativeTest.class | Bin 1099 -> 0 bytes .../llvm/multilingual/simple/NativeTest.h | 31 ---------- .../llvm/multilingual/simple/NativeTest.java | 14 ----- .../llvm/multilingual/simple/NativeTest.ll | 55 ------------------ .../llvm/multilingual/simple/build.sh | 3 - .../llvm/multilingual/simple/libnativetest.so | Bin 15424 -> 0 bytes .../resources/llvm/multilingual/simple/run.sh | 1 - .../llvm/multilingual/taint/TaintTest.class | Bin 3561 -> 0 bytes ...ossLanguageForwardTaintAnalysisTest.scala} | 10 ++-- .../src/main/scala/org/opalj/ifds/ICFG.scala | 2 +- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 8 +-- .../scala/org/opalj/ifds/IFDSProblem.scala | 2 +- .../ll/fpcf/analyses/ifds/Callable.scala | 6 +- .../analyses/ifds/NativeForwardICFG.scala | 36 ++++++++---- .../analyses/ifds/NativeIFDSAnalysis.scala | 24 ++------ .../analyses/ifds/NativeIFDSProblem.scala | 14 ++--- .../ll/fpcf/analyses/ifds/Statement.scala | 18 ++++++ .../ifds/taint/JavaForwardTaintAnalysis.scala | 12 ++-- .../taint/NativeForwardTaintAnalysis.scala | 10 ++-- .../taint/NativeForwardTaintProblem.scala | 45 +++++++------- .../main/scala/org/opalj/ll/llvm/Type.scala | 47 +++++++++++++-- .../scala/org/opalj/ll/llvm/package.scala | 10 ++++ .../scala/org/opalj/ll/llvm/value/Value.scala | 2 +- .../src/main/scala/org/opalj/ll/package.scala | 1 - 33 files changed, 161 insertions(+), 219 deletions(-) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/{multilingual => cross_language}/taint/TaintTest.c (100%) create mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.class rename DEVELOPING_OPAL/validate/src/test/resources/llvm/{multilingual => cross_language}/taint/TaintTest.h (100%) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/{multilingual => cross_language}/taint/TaintTest.java (96%) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/{multilingual => cross_language}/taint/TaintTest.ll (100%) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/{multilingual => cross_language}/taint/build_run.sh (100%) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/{multilingual => cross_language}/taint/libtainttest.so (100%) rename DEVELOPING_OPAL/validate/src/test/resources/llvm/{multilingual => cross_language}/taint/run.sh (100%) delete mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.c delete mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.class delete mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.h delete mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.java delete mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.ll delete mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/build.sh delete mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/libnativetest.so delete mode 100755 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/run.sh delete mode 100644 DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.class rename DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/{MultilingualForwardTaintAnalysisTest.scala => CrossLanguageForwardTaintAnalysisTest.scala} (87%) create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Statement.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.c similarity index 100% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.c rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.c diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.class new file mode 100644 index 0000000000000000000000000000000000000000..0a2f8b80b0535cfeff08f4eb77f38c46c5164a57 GIT binary patch literal 4038 zcmaJ^340S&6g@+igzzY#l$I_?fue0tii&`?RHcFh%VsN4QRB23%FuMCCevzh-*>@% z-}ilweENO<0DqL9=gz!LCTXyIFK?D}&b#-%d*59C`S;Ji0gRxOz%n$%(5TRaW&_F7 z=7KqxH=XIhBa^4CDbGOjuZ6{|J7`bd+O0U93 zp>)=zl#SY~(6!Mm`tO9+k#C1R+C7_F~7qjK)mRG%0 z;WB}4UJ9+d6@v=f1-~JRpEC3LAl60OnF(#ZLg7lmZoB~2oi9vTA^IwXor10kY(%*x zf0ko6u8!dvg&_=ACGq%T(X(cmyY4(wdF_~=-nKh zIeyjk>Z%j+LkbT|nrlF|sx0Gpl(JRSaF>PrxWW@c)(}k&A#ps#4ApcgonVV4GQxgF z;n^CyX|iO24&L(yI+n7-1b9*5CCQFRyiOL}S*_s1;y72?#fSxZlf{Zx6ke4a=%_?5 zFhOHl`H^~E;SIb=%&a+Mjk?a1>1Ay16t9Y&>3Bt{!}P$?N{r)eeyaRv%bAnl8S$>d zds5gd@~)X1vnLB?VUe{o>gFtF#hC3_hv#P}t-^#kndg(nS$=*P;QkX!)dyI5tIDaV z3xl;=dE!^~>dL+Lh*dm1mDgX?#JC@*{kE*uGEx4jZj%_W@brd3 zn(=i4-{9LAzLTJQZ=g@0!S~vB7Tg(YFgS;TIPEv5JXc(ND|%9CU?6(HbaHvC*f-|7 zGxKvpOC60osUl7+&RKQf(NF~)#87l^w8SX$aoqMSp1nWvY!oMY4Ff9eI96daZx)MI zF@|3ZY+2%8J+ou@jUTxGhjN*~M}B`}YGC3Cgna7tu^Ca|V9!UPogT$YCF!(cT+@D#l{>;MjXBLn@vjn)pa(sx7 zxR#jmH4PLK5B-JMZGT|-*wzvf94k4J9BVk*IXXFdI5u!>;@HB`&#{$b8^`4wJAT2g z);%R$TXE?iTP>gsakf$l?MR}7ckB4C6FulcFI%EMUiC93m$Jp#jt$tsr@I-uVQk`8 zax-oqVhmeoFdIK@_SM?#wX)~ z*=1^to6z7}yg@WFp*_itJ*n=V5^ftwW!6_2)kP|B4F!j2)G!72Ft#I%JvE@3iL8?L=n8^ub-eGT@J&!$zY?4)^;qV1w$iAqXzN|>(JX(0|c zK%F$5J4gbUo@L0rjz3>S{bN7D_lb<-9J#kJTzag@iQT8rscS{u+|0THx~pvJuz70qwEua@U`D z4WQSRUE( literal 0 HcmV?d00001 diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.h similarity index 100% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.h rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.h diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java similarity index 96% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java index bbd9444c81..db1997030d 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.java +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java @@ -30,9 +30,9 @@ public static void main (String[] args) demo.test_native_zero_no_flow(); demo.test_native_array_tainted_flow(); demo.test_native_array_untainted_no_flow(); - /*demo.test_native_call_java_sink_flow(); + demo.test_native_call_java_sink_flow(); demo.test_native_call_java_source_flow(); - demo.test_native_call_java_sanitize_no_flow();*/ + demo.test_native_call_java_sanitize_no_flow(); System.out.println("done"); } @@ -107,7 +107,7 @@ public void test_native_array_untainted_no_flow() { this.native_array_untainted(); } - /*public void test_native_call_java_sink_flow() { + public void test_native_call_java_sink_flow() { System.out.println("native call java sink"); this.propagate_to_java_sink(source()); } @@ -120,7 +120,7 @@ public void test_native_call_java_source_flow() { public void test_native_call_java_sanitize_no_flow() { System.out.println("native call java sanitize"); this.sink(this.propagate_java_sanitize(this.source())); - }*/ + } public int indirect_source() { return source(); diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.ll similarity index 100% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.ll diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build_run.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/build_run.sh similarity index 100% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/build_run.sh rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/build_run.sh diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libtainttest.so b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/libtainttest.so similarity index 100% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/libtainttest.so rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/libtainttest.so diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/run.sh similarity index 100% rename from DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/run.sh rename to DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/run.sh diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.c b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.c deleted file mode 100644 index 9de4ce9cc8..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.c +++ /dev/null @@ -1,21 +0,0 @@ -#include -#include -#include -#include "NativeTest.h" -JNIEXPORT int JNICALL -Java_NativeTest_nativeFunction(JNIEnv *env, jobject obj, jint a, jint b) -{ - return a + b; -} - -JNIEXPORT int JNICALL -Java_NativeTest_foo(JNIEnv *env, jobject obj) -{ - return bar() + 23; -} - -int -bar() -{ - return 6*7; -} \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.class deleted file mode 100644 index efb81bdc44135e5344ca14b52f5b85995048e4df..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1099 zcmaJ=?M@Rx6g>lNcUjgJ%a?*^LD04+RZ&rjAF&CsNd=P7 z{s4oC58y)??`#`eQsSSP`!VOtxp(gT{&W5dzy@ABa4~0L-oXgw#&81*#=4oM?2?1q zST^oT77R-^?mBcJZ=t}D+VZ5QUowp53m+H~71a_9nVKiXPSkFQV2?L^ilimi-m%z@ zWK(-eG9>fWYN1NFFG|H|vJ-9YQEDdtxpv5pdCBK;zf{+OC-=*S-QH^Yz3ZI1ih`!t z_6&JuCnnz$pnBuQisHnuNH>8!Y~;txk%1nb<9;h zHFG*Yj557UQ{Que&`}_}SC9LOw`yJ^;K7N7XQmZf49opOJ$bB-MDIPi8n<~l)h5LTp)1qwQ6~oFXdvN8Kl*HSEigk+r`81LiEH#hQ -/* Header for class NativeTest */ - -#ifndef _Included_NativeTest -#define _Included_NativeTest -#ifdef __cplusplus -extern "C" { -#endif -/* - * Class: NativeTest - * Method: nativeFunction - * Signature: (II)I - */ -JNIEXPORT jint JNICALL Java_NativeTest_nativeFunction - (JNIEnv *, jobject, jint, jint); - -/* - * Class: NativeTest - * Method: foo - * Signature: ()I - */ -JNIEXPORT jint JNICALL Java_NativeTest_foo - (JNIEnv *, jobject); - -int bar(); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.java deleted file mode 100644 index f44b6f8c53..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.java +++ /dev/null @@ -1,14 +0,0 @@ -public class NativeTest { - public native int nativeFunction (int a, int b); - public native int foo (); - static - { - System.loadLibrary ("nativetest"); /* lowercase of classname! */ - } - public static void main (String[] args) - { - NativeTest demo = new NativeTest(); - System.out.println("result is: " + demo.nativeFunction(23,42)); - System.out.println("foo returns: " + demo.foo()); - } -} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.ll b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.ll deleted file mode 100644 index bbf6edec26..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/NativeTest.ll +++ /dev/null @@ -1,55 +0,0 @@ -; ModuleID = 'NativeTest.c' -source_filename = "NativeTest.c" -target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-pc-linux-gnu" - -%struct.JNINativeInterface_ = type { i8*, i8*, i8*, i8*, i32 (%struct.JNINativeInterface_**)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*, %struct._jobject*, i8*, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, i8)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**)*, void (%struct.JNINativeInterface_**, i8*)*, i32 (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i64)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, float)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, double)*, %struct._jmethodID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, ...)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %struct.__va_list_tag*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jmethodID*, %union.jvalue*)*, %struct._jfieldID* (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i8*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i8 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i16 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, float (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, double (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i8)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i16)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, i64)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, float)*, void (%struct.JNINativeInterface_**, %struct._jobject*, %struct._jfieldID*, double)*, %struct._jobject* (%struct.JNINativeInterface_**, i16*, i32)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, i32, %struct._jobject*, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, %struct._jobject* (%struct.JNINativeInterface_**, i32)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i32* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, i64* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, float* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, double* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i64*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, float*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, double*, i32)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i32*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i64*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, float*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, double*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i32*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i64*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, float*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, double*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*, %struct.JNINativeMethod*, i32)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct.JNIInvokeInterface_***)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i16*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i32, i32, i8*)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i8*, i32)*, i16* (%struct.JNINativeInterface_**, %struct._jobject*, i8*)*, void (%struct.JNINativeInterface_**, %struct._jobject*, i16*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)*, void (%struct.JNINativeInterface_**, %struct._jobject*)*, i8 (%struct.JNINativeInterface_**)*, %struct._jobject* (%struct.JNINativeInterface_**, i8*, i64)*, i8* (%struct.JNINativeInterface_**, %struct._jobject*)*, i64 (%struct.JNINativeInterface_**, %struct._jobject*)*, i32 (%struct.JNINativeInterface_**, %struct._jobject*)*, %struct._jobject* (%struct.JNINativeInterface_**, %struct._jobject*)* } -%struct._jmethodID = type opaque -%struct._jfieldID = type opaque -%struct.__va_list_tag = type { i32, i32, i8*, i8* } -%union.jvalue = type { i64 } -%struct.JNINativeMethod = type { i8*, i8*, i8* } -%struct.JNIInvokeInterface_ = type { i8*, i8*, i8*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)*, i32 (%struct.JNIInvokeInterface_**)*, i32 (%struct.JNIInvokeInterface_**, i8**, i32)*, i32 (%struct.JNIInvokeInterface_**, i8**, i8*)* } -%struct._jobject = type opaque - -; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @Java_NativeTest_nativeFunction(%struct.JNINativeInterface_** %0, %struct._jobject* %1, i32 %2, i32 %3) #0 { - %5 = alloca %struct.JNINativeInterface_**, align 8 - %6 = alloca %struct._jobject*, align 8 - %7 = alloca i32, align 4 - %8 = alloca i32, align 4 - store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %5, align 8 - store %struct._jobject* %1, %struct._jobject** %6, align 8 - store i32 %2, i32* %7, align 4 - store i32 %3, i32* %8, align 4 - %9 = load i32, i32* %7, align 4 - %10 = load i32, i32* %8, align 4 - %11 = add nsw i32 %9, %10 - ret i32 %11 -} - -; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @Java_NativeTest_foo(%struct.JNINativeInterface_** %0, %struct._jobject* %1) #0 { - %3 = alloca %struct.JNINativeInterface_**, align 8 - %4 = alloca %struct._jobject*, align 8 - store %struct.JNINativeInterface_** %0, %struct.JNINativeInterface_*** %3, align 8 - store %struct._jobject* %1, %struct._jobject** %4, align 8 - %5 = call i32 @bar() - %6 = add nsw i32 %5, 23 - ret i32 %6 -} - -; Function Attrs: noinline nounwind optnone uwtable -define dso_local i32 @bar() #0 { - ret i32 42 -} - -attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } - -!llvm.module.flags = !{!0, !1, !2} -!llvm.ident = !{!3} - -!0 = !{i32 1, !"wchar_size", i32 4} -!1 = !{i32 7, !"uwtable", i32 1} -!2 = !{i32 7, !"frame-pointer", i32 2} -!3 = !{!"Ubuntu clang version 13.0.0-2"} diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/build.sh b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/build.sh deleted file mode 100755 index 457ed0fd79..0000000000 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -clang -shared -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ -o libnativetest.so NativeTest.c -clang -S -I/usr/lib/jvm/java-11-openjdk-amd64/include/ -I/usr/lib/jvm/java-11-openjdk-amd64/include/linux/ NativeTest.c -emit-llvm -javac NativeTest.java \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/libnativetest.so b/DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/simple/libnativetest.so deleted file mode 100755 index ced7738d45fd0a9aff6a47005995b8c31136c3f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15424 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 Dav>M5 diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala similarity index 87% rename from DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala rename to DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala index 00d2061e59..c1c2283ade 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/MultilingualForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala @@ -6,7 +6,7 @@ import org.opalj.br.analyses.Project import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.ifds import org.opalj.ifds.IFDSProperty -import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement +import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement} import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, NativeFact, NativeForwardTaintAnalysisScheduler, NativeNullFact} import org.opalj.ll.llvm.value.Function import org.opalj.log.GlobalLogContext @@ -16,18 +16,18 @@ import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact} import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers -class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers { +class CrossLanguageForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers { describe("MultilingualForwardTaintAnalysis") { implicit val config = BaseConfig.withValue(ifds.ConfigKeyPrefix+"debug", ConfigValueFactory.fromAnyRef(true)) val project = Project( - new java.io.File("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint"), + new java.io.File("./DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint"), GlobalLogContext, config ) project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( - current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/multilingual/taint/TaintTest.ll") + current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.ll") ) project.get(LLVMProjectKey) project.get(RTACallGraphKey) @@ -61,7 +61,7 @@ class MultilingualForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers } val function: Function = project.get(LLVMProjectKey).function("Java_TaintTest_native_1array_1tainted").get - val debugData = ps((function, NativeNullFact), NativeForwardTaintAnalysisScheduler.property.key).ub.asInstanceOf[IFDSProperty[LLVMStatement, NativeFact]].debugData + val debugData = ps((LLVMFunction(function), NativeNullFact), NativeForwardTaintAnalysisScheduler.property.key).ub.asInstanceOf[IFDSProperty[LLVMStatement, NativeFact]].debugData for { bb ← function.basicBlocks instruction ← bb.instructions diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala index 02c951fbfc..528dd51a32 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala @@ -3,7 +3,7 @@ package org.opalj.ifds import scala.collection.{Set ⇒ SomeSet} -abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]] { +abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]] { /** * Determines the statements at which the analysis starts. * diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index b3c351dc10..4c755a009c 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -49,7 +49,7 @@ object Dependees { * that is the fact reaches the statement as an input. * Source fact is the fact within the analysis entity. */ -case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[C, _], C]() { +case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C]() { var edges = Map.empty[S, Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] /** @@ -115,7 +115,7 @@ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[C, _], C]() { * @param source The callable and input fact for which the callable is analyzed. * @param endSummaries Output facts of the analyzed callable as pairs of exit statement and fact */ -protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _], Work]( +protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _], Work]( val source: (C, IFDSFact), val dependees: Dependees[Work] = Dependees[Work](), val pathEdges: PathEdges[IFDSFact, S, C] = PathEdges[IFDSFact, S, C](), @@ -141,7 +141,7 @@ protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalys * @param propertyKey Provides the concrete property key that must be unique for every distinct concrete analysis and the lower bound for the IFDSProperty. * @tparam IFDSFact */ -class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]]( +class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]]( implicit project: SomeProject, val ifdsProblem: IFDSProblem[IFDSFact, C, S], @@ -414,7 +414,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, } } -abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]] +abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]] extends FPCFLazyAnalysisScheduler { final override type InitializationData = IFDSAnalysis[IFDSFact, C, S] def property: IFDSPropertyMetaInformation[S, IFDSFact] diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala index feabc8c1c9..8d52e31d3b 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -11,7 +11,7 @@ import org.opalj.ifds.Dependees.Getter * @author Mario Trageser * @author Marc Clement */ -abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, _]](val icfg: ICFG[IFDSFact, C, S]) { +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]](val icfg: ICFG[IFDSFact, C, S]) { type Work = (S, IFDSFact, Option[S]) /** diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala index a965fcc4f4..ea57c283ba 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala @@ -3,7 +3,11 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.ifds.Callable import org.opalj.ll.llvm.value.Function -case class LLVMFunction(val function: Function) extends Callable { +abstract class NativeFunction extends Callable { + def name: String +} + +case class LLVMFunction(function: Function) extends NativeFunction { override def name: String = function.name override def signature: String = function.name // TODO: add signature } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index db673d42a2..97be8a70fc 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -2,19 +2,22 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.ifds.{AbstractIFDSFact, ICFG} -import org.opalj.ll.llvm.value.{Call, Function, Instruction, Ret, Terminator} +import org.opalj.ll.llvm.{FunctionType, PointerType} +import org.opalj.ll.llvm.value.{Call, Function, Instruction, Ret, Terminator, Value} -class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Function, LLVMStatement] { +class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, NativeFunction, LLVMStatement] { /** * Determines the statements at which the analysis starts. * * @param callable The analyzed callable. * @return The statements at which the analysis starts. */ - override def startStatements(callable: Function): Set[LLVMStatement] = { - if (callable.basicBlockCount == 0) - throw new IllegalArgumentException(s"${callable} does not contain any basic blocks and likely should not be in scope of the analysis") - Set(LLVMStatement(callable.entryBlock.firstInstruction)) + override def startStatements(callable: NativeFunction): Set[LLVMStatement] = callable match { + case LLVMFunction(function) ⇒ { + if (function.basicBlockCount == 0) + throw new IllegalArgumentException(s"${callable} does not contain any basic blocks and likely should not be in scope of the analysis") + Set(LLVMStatement(function.entryBlock.firstInstruction)) + } } /** @@ -35,13 +38,10 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun * @return All callables possibly called at the statement or None, if the statement does not * contain a call. */ - override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[Function]] = { + override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[NativeFunction]] = { statement.instruction match { - case call: Call ⇒ call.calledValue match { - case function: Function ⇒ Some(Set(function)) - case _ ⇒ Some(Set()) // TODO - } - case _ ⇒ None + case call: Call ⇒ Some(resolveCallee(call.calledValue)) + case _ ⇒ None } } @@ -49,4 +49,16 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Fun case Ret(_) ⇒ true case _ ⇒ false } + + private def resolveCallee(calledValue: Value): Set[NativeFunction] = calledValue match { + case function: Function ⇒ Set(LLVMFunction(function)) + case _ ⇒ calledValue.typ match { + case p: PointerType ⇒ { + val functionType = p.element.asInstanceOf[FunctionType] + println(functionType.repr()) + Set() + } + case _ ⇒ Set() + } + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala index 7ac6716a57..264f0553d4 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSAnalysis.scala @@ -3,11 +3,8 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.br.analyses.{ProjectInformationKeys, SomeProject} -import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSProblem, Statement} -import org.opalj.ifds.{AbstractIFDSFact, IFDSPropertyMetaInformation} +import org.opalj.ifds.{AbstractIFDSFact, IFDSAnalysis, IFDSAnalysisScheduler, IFDSProblem, IFDSPropertyMetaInformation} import org.opalj.ll.LLVMProjectKey -import org.opalj.ll.llvm.value.{BasicBlock, Instruction} -import org.opalj.ll.llvm.value /** * @@ -17,24 +14,11 @@ import org.opalj.ll.llvm.value */ class NativeIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( project: SomeProject, - ifdsProblem: IFDSProblem[IFDSFact, value.Function, LLVMStatement], + ifdsProblem: IFDSProblem[IFDSFact, NativeFunction, LLVMStatement], propertyKey: IFDSPropertyMetaInformation[LLVMStatement, IFDSFact] ) - extends IFDSAnalysis[IFDSFact, value.Function, LLVMStatement]()(project, ifdsProblem, propertyKey) + extends IFDSAnalysis[IFDSFact, NativeFunction, LLVMStatement]()(project, ifdsProblem, propertyKey) -/** - * A statement that is passed to the concrete analysis. - * - * @param instruction The LLVM instruction. - */ -case class LLVMStatement(instruction: Instruction) extends Statement[value.Function, BasicBlock] { - def function: value.Function = instruction.function - def basicBlock: BasicBlock = instruction.parent - override def node: BasicBlock = basicBlock - override def callable: value.Function = function - override def toString: String = s"${function.name}\n\t${instruction}\n\t${function}" -} - -abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysisScheduler[IFDSFact, value.Function, LLVMStatement] { +abstract class NativeIFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] extends IFDSAnalysisScheduler[IFDSFact, NativeFunction, LLVMStatement] { override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index 2a18289aeb..0b8061b1ee 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -7,16 +7,16 @@ import org.opalj.fpcf.PropertyStore import org.opalj.ifds.Dependees.Getter import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem} import org.opalj.ll.LLVMProjectKey -import org.opalj.ll.llvm.value.Function -abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, Function, LLVMStatement](new NativeForwardICFG[Fact]) { +abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, NativeFunction, LLVMStatement](new NativeForwardICFG[Fact]) { final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) val llvmProject = project.get(LLVMProjectKey) - override def outsideAnalysisContext(callee: Function): Option[(LLVMStatement, LLVMStatement, Fact, Getter) ⇒ Set[Fact]] = { - callee.basicBlockCount match { - case 0 ⇒ Some((_: LLVMStatement, _: LLVMStatement, in: Fact, _: Getter) ⇒ Set(in)) - case _ ⇒ None - } + override def outsideAnalysisContext(callee: NativeFunction): Option[(LLVMStatement, LLVMStatement, Fact, Getter) ⇒ Set[Fact]] = callee match { + case LLVMFunction(function) ⇒ + function.basicBlockCount match { + case 0 ⇒ Some((_: LLVMStatement, _: LLVMStatement, in: Fact, _: Getter) ⇒ Set(in)) + case _ ⇒ None + } } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Statement.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Statement.scala new file mode 100644 index 0000000000..67bc8c2a3f --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Statement.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.ifds.Statement +import org.opalj.ll.llvm.value.{BasicBlock, Instruction} + +/** + * A statement that is passed to the concrete analysis. + * + * @param instruction The LLVM instruction. + */ +case class LLVMStatement(instruction: Instruction) extends Statement[LLVMFunction, BasicBlock] { + def function: LLVMFunction = LLVMFunction(instruction.function) + def basicBlock: BasicBlock = instruction.parent + override def node: BasicBlock = basicBlock + override def callable: LLVMFunction = function + override def toString: String = s"${function.name}\n\t${instruction}\n\t${function}" +} \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index e8a9a33ff3..5bf61f6163 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -7,9 +7,9 @@ import org.opalj.fpcf._ import org.opalj.ifds.Dependees.Getter import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.ll.LLVMProjectKey -import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement +import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement} import org.opalj.ll.fpcf.properties.NativeTaint -import org.opalj.ll.llvm.value.{Function, Ret} +import org.opalj.ll.llvm.value.Ret import org.opalj.tac.Assignment import org.opalj.tac.fpcf.analyses.ifds.taint._ import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} @@ -68,7 +68,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( case c ⇒ s"_${c.toInt.toHexString.reverse.padTo(4, '0').reverse}" }).mkString val nativeFunctionName = "Java_"+callee.classFile.fqn+"_"+calleeName - val function = llvmProject.function(nativeFunctionName).get + val function = LLVMFunction(llvmProject.function(nativeFunctionName).get) var result = Set.empty[Fact] val entryFacts = nativeCallFlow(call, function, in, callee) for (entryFact ← entryFacts) { // ifds line 14 @@ -111,7 +111,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( */ private def nativeCallFlow( call: JavaStatement, - callee: Function, + callee: LLVMFunction, in: Fact, nativeCallee: Method ): Set[NativeFact] = { @@ -124,7 +124,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( allParamsWithIndices.flatMap { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ // TODO: this is passed - Some(NativeVariable(callee.argument(paramIndex + 1))) // offset JNIEnv + Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv case _ ⇒ None // Nothing to do }.toSet @@ -132,7 +132,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( case ArrayElement(index, taintedIndex) ⇒ allParamsWithIndices.flatMap { case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ - Some(NativeVariable(callee.argument(paramIndex + 1))) // offset JNIEnv + Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv case _ ⇒ None // Nothing to do }.toSet diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala index dbc612e17a..3a483308f0 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -4,7 +4,7 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.{PropertyBounds, PropertyStore} import org.opalj.ifds.IFDSPropertyMetaInformation -import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeIFDSAnalysis, NativeIFDSAnalysisScheduler} +import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeFunction, NativeIFDSAnalysis, NativeIFDSAnalysisScheduler} import org.opalj.ll.fpcf.properties.NativeTaint import org.opalj.ll.llvm.value.Function @@ -12,13 +12,13 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(Function, NativeFact)] = Seq.empty + override val entryPoints: Seq[(NativeFunction, NativeFact)] = Seq.empty /** * The sanitize method is a sanitizer. */ - override protected def sanitizesReturnValue(callee: Function): Boolean = - callee.name == "sanitize" + override protected def sanitizesReturnValue(callee: LLVMFunction): Boolean = + callee.function.name == "sanitize" /** * We do not sanitize parameters. @@ -41,7 +41,7 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint call: LLVMStatement, in: Set[NativeFact] ): Option[NativeFlowFact] = - if (callee.name == "sink" && in.contains(JavaVariable(-2))) Some(NativeFlowFact(Seq(LLVMFunction(call.function)))) + if (callee.name == "sink" && in.contains(JavaVariable(-2))) Some(NativeFlowFact(Seq(call.function))) else None } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 2bf08b41af..20bd3dace4 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -2,11 +2,11 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject -import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeIFDSProblem} -import org.opalj.ll.llvm.value.{Add, Alloca, BitCast, Call, Function, GetElementPtr, Load, PHI, Ret, Store, Sub} +import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeFunction, NativeIFDSProblem} +import org.opalj.ll.llvm.value.{Add, Alloca, BitCast, Call, GetElementPtr, Load, PHI, Ret, Store, Sub} import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[Function, LLVMStatement, NativeFact] { +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[LLVMFunction, LLVMStatement, NativeFact] { override def nullFact: NativeFact = NativeNullFact /** @@ -68,20 +68,23 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @return The facts, which hold after the execution of `statement` under the assumption that * the facts in `in` held before `statement` and `statement` calls `callee`. */ - override def callFlow(call: LLVMStatement, callee: Function, in: NativeFact): Set[NativeFact] = in match { - // Taint formal parameter if actual parameter is tainted - case NativeVariable(value) ⇒ call.instruction.asInstanceOf[Call].indexOfArgument(value) match { - case Some(index) ⇒ Set(NativeVariable(callee.argument(index))) - case None ⇒ Set() - } - // TODO pass other java taints - case NativeNullFact ⇒ Set(in) - case NativeArrayElement(base, indices) ⇒ call.instruction.asInstanceOf[Call].indexOfArgument(base) match { - case Some(index) ⇒ Set(NativeArrayElement(callee.argument(index), indices)) - case None ⇒ Set() - } - case _ ⇒ Set() // Nothing to do - + override def callFlow(call: LLVMStatement, callee: NativeFunction, in: NativeFact): Set[NativeFact] = callee match { + case LLVMFunction(callee) ⇒ + in match { + // Taint formal parameter if actual parameter is tainted + case NativeVariable(value) ⇒ call.instruction.asInstanceOf[Call].indexOfArgument(value) match { + case Some(index) ⇒ Set(NativeVariable(callee.argument(index))) + case None ⇒ Set() + } + // TODO pass other java taints + case NativeNullFact ⇒ Set(in) + case NativeArrayElement(base, indices) ⇒ call.instruction.asInstanceOf[Call].indexOfArgument(base) match { + case Some(index) ⇒ Set(NativeArrayElement(callee.argument(index), indices)) + case None ⇒ Set() + } + case _ ⇒ Set() // Nothing to do + } + case _ ⇒ throw new RuntimeException("this case should be handled by outsideAnalysisContext") } /** @@ -103,16 +106,16 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD case _ ⇒ Set() } case NativeNullFact ⇒ Set(NativeNullFact) - case NativeFlowFact(flow) if !flow.contains(LLVMFunction(call.function)) ⇒ - Set(NativeFlowFact(LLVMFunction(call.function) +: flow)) + case NativeFlowFact(flow) if !flow.contains(call.function) ⇒ + Set(NativeFlowFact(call.function +: flow)) case _ ⇒ Set() } if (exit.callable.name == "source") in match { case NativeNullFact ⇒ flows += NativeVariable(call.instruction) } if (exit.callable.name == "sink") in match { - case NativeVariable(value) if value == exit.callable.argument(0) ⇒ - flows += NativeFlowFact(Seq(LLVMFunction(call.callable), LLVMFunction(exit.callable))) + case NativeVariable(value) if value == exit.callable.function.argument(0) ⇒ + flows += NativeFlowFact(Seq(call.callable, exit.callable)) case _ ⇒ } flows diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala index 28cb1aafd7..23c7fb3f5c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.llvm +import org.bytedeco.javacpp.PointerPointer import org.bytedeco.llvm.LLVM.LLVMTypeRef import org.bytedeco.llvm.global.LLVM._ @@ -40,6 +41,14 @@ sealed abstract class Type(ref: LLVMTypeRef) { } override def toString: String = s"Type(${repr()})" + + def isSized: Boolean = intToBool(LLVMTypeIsSized(ref)) +} + +trait SequentialType { + val ref: LLVMTypeRef + + def element: Type = Type(LLVMGetElementType(ref)) } /** type with no size */ @@ -62,16 +71,44 @@ case class LabelType(ref: LLVMTypeRef) extends Type(ref) case class IntegerType(ref: LLVMTypeRef) extends Type(ref) /** Functions */ case class FunctionType(ref: LLVMTypeRef) extends Type(ref) { - def returnType(): Type = Type(LLVMGetReturnType(ref)) + def returnType: Type = Type(LLVMGetReturnType(ref)) + def isVarArg: Boolean = intToBool(LLVMIsFunctionVarArg(ref)) + + def paramCount: Int = LLVMCountParamTypes(ref) + def params: Iterable[Type] = { + val result = new PointerPointer[LLVMTypeRef](paramCount.toLong) + LLVMGetParamTypes(ref, result) + (0.toLong until paramCount.toLong).map(result.get(_)).map(p ⇒ Type(new LLVMTypeRef(p))) + } } /** Structures */ -case class StructType(ref: LLVMTypeRef) extends Type(ref) +case class StructType(ref: LLVMTypeRef) extends Type(ref) { + def name: String = { + val bytePointer = LLVMGetStructName(ref) + val string = bytePointer.getString + LLVMDisposeMessage(bytePointer) + string + } + def elementCount: Int = LLVMCountStructElementTypes(ref) + def elementAtIndex(i: Int) = { + assert(i < elementCount) + Type(LLVMStructGetTypeAtIndex(ref, i)) + } + def elements: Iterable[Type] = (0 until elementCount).map(elementAtIndex(_)) + def isPacked: Boolean = intToBool(LLVMIsPackedStruct(ref)) + def isOpaque: Boolean = intToBool(LLVMIsOpaqueStruct(ref)) + def isLiteral: Boolean = intToBool(LLVMIsLiteralStruct(ref)) +} /** Arrays */ -case class ArrayType(ref: LLVMTypeRef) extends Type(ref) +case class ArrayType(ref: LLVMTypeRef) extends Type(ref) with SequentialType { + def length: Int = LLVMGetArrayLength(ref) +} /** Pointers */ -case class PointerType(ref: LLVMTypeRef) extends Type(ref) +case class PointerType(ref: LLVMTypeRef) extends Type(ref) with SequentialType /** Fixed width SIMD vector type */ -case class VectorType(ref: LLVMTypeRef) extends Type(ref) +case class VectorType(ref: LLVMTypeRef) extends Type(ref) with SequentialType { + def size: Int = LLVMGetVectorSize(ref) +} /** Metadata */ case class MetadataType(ref: LLVMTypeRef) extends Type(ref) /** X86 MMX */ diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala new file mode 100644 index 0000000000..dac7bd1315 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala @@ -0,0 +1,10 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll + +package object llvm { + def intToBool(i: Int): Boolean = i match { + case 0 ⇒ false + case 1 ⇒ true + case _ ⇒ throw new IllegalArgumentException(s"${i} is not a valid Boolean") + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala index c79570d6d8..b94348c2d6 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala @@ -17,7 +17,7 @@ class Value(ref: LLVMValueRef) { LLVMGetValueName(ref).getString } - def typ: Type = Type(LLVMTypeOf(ref)) + def typ: Type = Type(LLVMTypeOf(ref)) // because type is a keyword val address = ref.address override def equals(other: Any): Boolean = diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/package.scala b/OPAL/ll/src/main/scala/org/opalj/ll/package.scala index b247a58ba9..c304c3c192 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/package.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/package.scala @@ -3,7 +3,6 @@ package org.opalj import com.typesafe.config.Config import com.typesafe.config.ConfigFactory - import org.opalj.log.LogContext import org.opalj.log.GlobalLogContext import org.opalj.log.OPALLogger.info From d3ab854c4983502af22e92412822e7f9a124efe6 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 23 Jun 2022 00:41:57 +0200 Subject: [PATCH 49/67] Small fixes --- .../src/main/scala/org/opalj/ifds/ICFG.scala | 2 +- .../scala/org/opalj/ifds/IFDSProblem.scala | 2 +- .../analyses/ifds/NativeForwardICFG.scala | 28 ++++++++++++------- .../main/scala/org/opalj/ll/llvm/Type.scala | 7 +---- .../tac/fpcf/analyses/ifds/ForwardICFG.scala | 6 ++-- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 2 +- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala index 528dd51a32..f250f46ce3 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala @@ -3,7 +3,7 @@ package org.opalj.ifds import scala.collection.{Set ⇒ SomeSet} -abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]] { +abstract class ICFG[C <: AnyRef, S <: Statement[_ <: C, _]] { /** * Determines the statements at which the analysis starts. * diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala index 8d52e31d3b..f09a3a8246 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -11,7 +11,7 @@ import org.opalj.ifds.Dependees.Getter * @author Mario Trageser * @author Marc Clement */ -abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]](val icfg: ICFG[IFDSFact, C, S]) { +abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]](val icfg: ICFG[C, S]) { type Work = (S, IFDSFact, Option[S]) /** diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index 97be8a70fc..2d89c8122e 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -2,10 +2,10 @@ package org.opalj.ll.fpcf.analyses.ifds import org.opalj.ifds.{AbstractIFDSFact, ICFG} -import org.opalj.ll.llvm.{FunctionType, PointerType} -import org.opalj.ll.llvm.value.{Call, Function, Instruction, Ret, Terminator, Value} +import org.opalj.ll.llvm.{PointerType, StructType} +import org.opalj.ll.llvm.value.{Call, Function, GetElementPtr, Instruction, Load, Ret, Terminator, Value} -class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, NativeFunction, LLVMStatement] { +class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[NativeFunction, LLVMStatement] { /** * Determines the statements at which the analysis starts. * @@ -52,13 +52,21 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[IFDSFact, Nat private def resolveCallee(calledValue: Value): Set[NativeFunction] = calledValue match { case function: Function ⇒ Set(LLVMFunction(function)) - case _ ⇒ calledValue.typ match { - case p: PointerType ⇒ { - val functionType = p.element.asInstanceOf[FunctionType] - println(functionType.repr()) - Set() - } - case _ ⇒ Set() + case load: Load ⇒ resolveCallee(load.src) + case gep: GetElementPtr if isJNI(gep) => Set() + case _ ⇒ Set() + } + + private def isJNI(gep: GetElementPtr): Boolean = { + if (gep.base.typ.isInstanceOf[PointerType]) { + val pointerType = gep.base.typ.asInstanceOf[PointerType] + if (pointerType.element.isInstanceOf[StructType]) { + val struct = pointerType.element.asInstanceOf[StructType] + if (struct.name == "struct.JNINativeInterface_") { + return true + } + } } + false } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala index 23c7fb3f5c..6d7c3accc2 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala @@ -83,12 +83,7 @@ case class FunctionType(ref: LLVMTypeRef) extends Type(ref) { } /** Structures */ case class StructType(ref: LLVMTypeRef) extends Type(ref) { - def name: String = { - val bytePointer = LLVMGetStructName(ref) - val string = bytePointer.getString - LLVMDisposeMessage(bytePointer) - string - } + def name: String = LLVMGetStructName(ref).getString def elementCount: Int = LLVMCountStructElementTypes(ref) def elementAtIndex(i: Int) = { assert(i < elementCount) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala index cc7d9296d8..f3c36034df 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala @@ -5,15 +5,15 @@ import org.opalj.br.{DeclaredMethod, Method} import org.opalj.br.analyses.{DeclaredMethods, DeclaredMethodsKey, SomeProject} import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.{FinalEP, PropertyStore} -import org.opalj.ifds.{AbstractIFDSFact, ICFG} +import org.opalj.ifds.ICFG import org.opalj.tac.cg.TypeProviderKey import org.opalj.tac.{Assignment, DUVar, Expr, ExprStmt, LazyDetachedTACAIKey, NonVirtualFunctionCall, NonVirtualMethodCall, StaticFunctionCall, StaticMethodCall, Stmt, TACMethodParameter, TACode, VirtualFunctionCall, VirtualMethodCall} import org.opalj.tac.fpcf.analyses.cg.TypeProvider import org.opalj.tac.fpcf.properties.cg.Callees import org.opalj.value.ValueInformation -class ForwardICFG[IFDSFact <: AbstractIFDSFact](implicit project: SomeProject) - extends ICFG[IFDSFact, Method, JavaStatement] { +class ForwardICFG(implicit project: SomeProject) + extends ICFG[Method, JavaStatement] { val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 3b51be14d7..5b7f146623 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -46,7 +46,7 @@ object JavaStatement { } abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) - extends IFDSProblem[Fact, Method, JavaStatement](new ForwardICFG[Fact]()(project)) { + extends IFDSProblem[Fact, Method, JavaStatement](new ForwardICFG()(project)) { /** * Gets the call object for a statement that contains a call. * From b6a220244b514ff21d9e0b86256d1501b65f66a4 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Sat, 25 Jun 2022 03:00:53 +0200 Subject: [PATCH 50/67] JNI call analysis WIP --- ...rossLanguageForwardTaintAnalysisTest.scala | 6 +- .../org/opalj/ifds/DataFlowAnalysis.scala | 45 +++++ .../scala/org/opalj/ifds/IFDSAnalysis.scala | 5 - .../scala/org/opalj/ifds/IFDSProperty.scala | 4 +- .../main/scala/org/opalj/ifds/Statement.scala | 7 + .../ll/fpcf/analyses/ifds/JNICallUtil.scala | 87 ++++++++++ .../analyses/ifds/NativeForwardICFG.scala | 34 ++-- .../analyses/ifds/NativeIFDSProblem.scala | 2 +- .../ifds/taint/JavaForwardTaintAnalysis.scala | 20 +-- .../taint/NativeForwardTaintAnalysis.scala | 16 +- .../taint/NativeForwardTaintProblem.scala | 20 +-- .../ifds/taint/NativeTaintProblem.scala | 18 +- .../org/opalj/ll/fpcf/properties/Taint.scala | 14 +- .../org/opalj/ll/llvm/value/Argument.scala | 6 +- .../org/opalj/ll/llvm/value/BasicBlock.scala | 5 +- .../org/opalj/ll/llvm/value/Function.scala | 6 +- .../opalj/ll/llvm/value/GlobalVariable.scala | 5 +- .../org/opalj/ll/llvm/value/Instruction.scala | 19 +-- .../scala/org/opalj/ll/llvm/value/Use.scala | 10 ++ .../scala/org/opalj/ll/llvm/value/User.scala | 18 ++ .../scala/org/opalj/ll/llvm/value/Value.scala | 40 +++-- .../constant/ConstantDataSequential.scala | 16 ++ .../value/constant/ConstantExpression.scala | 159 ++++++++++++++++++ .../{ => constant}/ScalarConstants.scala | 4 +- 24 files changed, 456 insertions(+), 110 deletions(-) create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/DataFlowAnalysis.scala create mode 100644 OPAL/ifds/src/main/scala/org/opalj/ifds/Statement.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Use.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/User.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantDataSequential.scala create mode 100644 OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala rename OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/{ => constant}/ScalarConstants.scala (74%) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala index c1c2283ade..da993e0871 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala @@ -7,7 +7,7 @@ import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.ifds import org.opalj.ifds.IFDSProperty import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement} -import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, NativeFact, NativeForwardTaintAnalysisScheduler, NativeNullFact} +import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, NativeTaintFact, NativeForwardTaintAnalysisScheduler, NativeTaintNullFact} import org.opalj.ll.llvm.value.Function import org.opalj.log.GlobalLogContext import org.opalj.tac.cg.RTACallGraphKey @@ -17,7 +17,7 @@ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers class CrossLanguageForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matchers { - describe("MultilingualForwardTaintAnalysis") { + describe("CrossLanguageForwardTaintAnalysis") { implicit val config = BaseConfig.withValue(ifds.ConfigKeyPrefix+"debug", ConfigValueFactory.fromAnyRef(true)) val project = Project( @@ -61,7 +61,7 @@ class CrossLanguageForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matcher } val function: Function = project.get(LLVMProjectKey).function("Java_TaintTest_native_1array_1tainted").get - val debugData = ps((LLVMFunction(function), NativeNullFact), NativeForwardTaintAnalysisScheduler.property.key).ub.asInstanceOf[IFDSProperty[LLVMStatement, NativeFact]].debugData + val debugData = ps((LLVMFunction(function), NativeTaintNullFact), NativeForwardTaintAnalysisScheduler.property.key).ub.asInstanceOf[IFDSProperty[LLVMStatement, NativeTaintFact]].debugData for { bb ← function.basicBlocks instruction ← bb.instructions diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/DataFlowAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/DataFlowAnalysis.scala new file mode 100644 index 0000000000..5897a62b5e --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/DataFlowAnalysis.scala @@ -0,0 +1,45 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ifds + +import scala.collection.mutable + +abstract class DataFlowAnalysis[Facts >: Null <: AnyRef, C <: AnyRef, S <: Statement[_ <: C, _]] { + def icfg: ICFG[C, S] + def entryFacts: Facts + def transferFunction(facts: Facts, statement: S, successor: S): Facts + def join(left: Facts, right: Facts): Facts + + def perform(callable: C): Map[S, Facts] = { + var facts = Map.empty[S, Facts] + val workList = new mutable.Queue[S]() + + for (entryStatement ← icfg.startStatements(callable)) { + facts = facts.updated(entryStatement, entryFacts) + workList.enqueue(entryStatement) + } + + while (workList.nonEmpty) { + val statement = workList.dequeue() + val inFacts = facts.get(statement).get + + for (successor ← icfg.nextStatements(statement)) { + val newOutFacts = transferFunction(inFacts, statement, successor) + facts.get(successor) match { + case None ⇒ { + facts = facts.updated(successor, newOutFacts) + workList.enqueue(successor) + } + case Some(existingOutFacts) ⇒ { + val outFacts = join(existingOutFacts, newOutFacts) + if (outFacts ne existingOutFacts) { + facts = facts.updated(successor, outFacts) + workList.enqueue(successor) + } + } + } + } + } + + facts + } +} \ No newline at end of file diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 4c755a009c..a2cb65c4b9 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -8,11 +8,6 @@ import org.opalj.ifds.Dependees.Getter import scala.collection.{mutable, Set ⇒ SomeSet} -abstract class Statement[C, Node] { - def node(): Node - def callable(): C -} - case class Dependees[Work]() { case class Dependee(eOptionP: SomeEOptionP, worklist: Set[Work] = Set.empty) var dependees = Map.empty[SomeEPK, Dependee] diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala index 48a01a23ae..0da5e3d16a 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala @@ -3,7 +3,7 @@ package org.opalj.ifds import org.opalj.fpcf.Property import org.opalj.fpcf.PropertyMetaInformation -trait IFDSPropertyMetaInformation[S, IFDSFact] extends PropertyMetaInformation { +trait IFDSPropertyMetaInformation[S, IFDSFact <: AbstractIFDSFact] extends PropertyMetaInformation { /** * Creates an IFDSProperty containing the result of this analysis. * @@ -14,7 +14,7 @@ trait IFDSPropertyMetaInformation[S, IFDSFact] extends PropertyMetaInformation { def create(result: Map[S, Set[IFDSFact]], debugData: Map[S, Set[IFDSFact]]): IFDSProperty[S, IFDSFact] } -abstract class IFDSProperty[S, IFDSFact] +abstract class IFDSProperty[S, IFDSFact <: AbstractIFDSFact] extends Property with IFDSPropertyMetaInformation[S, IFDSFact] { diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/Statement.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/Statement.scala new file mode 100644 index 0000000000..c98c245e7c --- /dev/null +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/Statement.scala @@ -0,0 +1,7 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ifds + +abstract class Statement[C, Node] { + def node(): Node + def callable(): C +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala new file mode 100644 index 0000000000..741692fb81 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala @@ -0,0 +1,87 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.fpcf.analyses.ifds + +import org.opalj.ll.llvm.value.constant.{ConstantDataArray, GetElementPtrConst} +import org.opalj.ll.llvm.value.{Argument, Call, GetElementPtr, GlobalVariable, Load, Store, Value} +import org.opalj.ll.llvm.{PointerType, StructType} + +object JNICallUtil { + + /** + * Checks whether the call is a call to the JNI interface. + * This is done by the assumption that every such calls first parameter is a struct of type "struct.JNINativeInterface_" + */ + def isJNICall(call: Call): Boolean = call.calledFunctionType.params.headOption match { + case Some(firstParam) ⇒ + firstParam match { + case p1: PointerType ⇒ + p1.element match { + case p2: PointerType ⇒ + p2.element match { + case struct: StructType if struct.name == "struct.JNINativeInterface_" ⇒ + true + case other ⇒ false + } + case _ ⇒ false + } + case _ ⇒ false + } + case _ ⇒ false + } + + def resolve(call: Call): Set[NativeFunction] = resolveJNIFunction(call) match { + case 'CallTypeMethod ⇒ { + resolveMethodId(call.operand(2)) + Set() // TODO + } // methodID is the third parameter + case _ ⇒ Set() + } + + private def resolveJNIFunction(call: Call): Symbol = call.calledValue match { + case load: Load ⇒ + load.src match { + // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/functions.html has the indices + case gep: GetElementPtr if gep.isConstant ⇒ gep.constants.tail.head match { + case 31 ⇒ 'GetObjectClass + case 33 ⇒ 'GetMethodId + case 49 | 61 ⇒ 'CallTypeMethod // CallIntMethod | CallVoidMethod + case index ⇒ throw new IllegalArgumentException(s"unknown JNI function index ${index}") + } + case _ ⇒ throw new IllegalArgumentException("unknown JNI load src") + } + case _ ⇒ throw new IllegalArgumentException("unknown JNI call argument") + } + + private def resolveMethodId(methodId: Value): Unit = { + val sources = methodId.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) + for (call ← sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call])) { + if (resolveJNIFunction(call) != 'GetMethodId) throw new IllegalArgumentException("unexpected call") + val name = resolveMethodName(call.operand(2)) // name is the third parameter + println(name) + if (!resolveClassIsThis(call.operand(1))) { // class is the second parameter + throw new IllegalArgumentException("unexpected class argument") + } + } + } + + private def resolveMethodName(name: Value): String = name match { + case gep: GetElementPtrConst ⇒ gep.base match { + case global: GlobalVariable ⇒ global.initializer match { + case stringData: ConstantDataArray ⇒ stringData.asString + } + } + } + + private def resolveClassIsThis(clazz: Value): Boolean = { + val sources = clazz.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) + sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call]).forall(call ⇒ { + if (resolveJNIFunction(call) != 'GetObjectClass) throw new IllegalArgumentException("unexpected call") + resolveObjectIsThis(call.operand(1)) // object is the second parameter + }) + } + + private def resolveObjectIsThis(obj: Value): Boolean = { + val sources = obj.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) + sources.forall(_.isInstanceOf[Argument]) && sources.forall(_.asInstanceOf[Argument].index == 1) + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index 2d89c8122e..2ffdf3b59f 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -1,11 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds -import org.opalj.ifds.{AbstractIFDSFact, ICFG} -import org.opalj.ll.llvm.{PointerType, StructType} -import org.opalj.ll.llvm.value.{Call, Function, GetElementPtr, Instruction, Load, Ret, Terminator, Value} +import org.opalj.ifds.ICFG +import org.opalj.ll.llvm.value.{Call, Function, Instruction, Ret, Terminator} -class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[NativeFunction, LLVMStatement] { +object NativeForwardICFG extends ICFG[NativeFunction, LLVMStatement] { /** * Determines the statements at which the analysis starts. * @@ -40,7 +39,7 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[NativeFunctio */ override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[NativeFunction]] = { statement.instruction match { - case call: Call ⇒ Some(resolveCallee(call.calledValue)) + case call: Call ⇒ Some(resolveCallee(call)) case _ ⇒ None } } @@ -50,23 +49,10 @@ class NativeForwardICFG[IFDSFact <: AbstractIFDSFact] extends ICFG[NativeFunctio case _ ⇒ false } - private def resolveCallee(calledValue: Value): Set[NativeFunction] = calledValue match { - case function: Function ⇒ Set(LLVMFunction(function)) - case load: Load ⇒ resolveCallee(load.src) - case gep: GetElementPtr if isJNI(gep) => Set() - case _ ⇒ Set() - } - - private def isJNI(gep: GetElementPtr): Boolean = { - if (gep.base.typ.isInstanceOf[PointerType]) { - val pointerType = gep.base.typ.asInstanceOf[PointerType] - if (pointerType.element.isInstanceOf[StructType]) { - val struct = pointerType.element.asInstanceOf[StructType] - if (struct.name == "struct.JNINativeInterface_") { - return true - } - } - } - false - } + private def resolveCallee(call: Call): Set[NativeFunction] = + if (call.calledValue.isInstanceOf[Function]) + Set(LLVMFunction(call.calledValue.asInstanceOf[Function])) + else if (JNICallUtil.isJNICall(call)) + JNICallUtil.resolve(call) + else Set() } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index 0b8061b1ee..ba2702ad33 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -8,7 +8,7 @@ import org.opalj.ifds.Dependees.Getter import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem} import org.opalj.ll.LLVMProjectKey -abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, NativeFunction, LLVMStatement](new NativeForwardICFG[Fact]) { +abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, NativeFunction, LLVMStatement](NativeForwardICFG) { final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) val llvmProject = project.get(LLVMProjectKey) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 5bf61f6163..31f6466230 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -73,11 +73,11 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( val entryFacts = nativeCallFlow(call, function, in, callee) for (entryFact ← entryFacts) { // ifds line 14 val e = (function, entryFact) - val exitFacts: Map[LLVMStatement, Set[NativeFact]] = - dependeesGetter(e, NativeTaint.key).asInstanceOf[EOptionP[(LLVMStatement, NativeFact), IFDSProperty[LLVMStatement, NativeFact]]] match { - case ep: FinalEP[_, IFDSProperty[LLVMStatement, NativeFact]] ⇒ + val exitFacts: Map[LLVMStatement, Set[NativeTaintFact]] = + dependeesGetter(e, NativeTaint.key).asInstanceOf[EOptionP[(LLVMStatement, NativeTaintFact), IFDSProperty[LLVMStatement, NativeTaintFact]]] match { + case ep: FinalEP[_, IFDSProperty[LLVMStatement, NativeTaintFact]] ⇒ ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, NativeFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, NativeTaintFact]] ⇒ ep.ub.flows case _ ⇒ Map.empty @@ -114,7 +114,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( callee: LLVMFunction, in: Fact, nativeCallee: Method - ): Set[NativeFact] = { + ): Set[NativeTaintFact] = { val callObject = asCall(call.stmt) val allParams = callObject.allParams val allParamsWithIndices = allParams.zipWithIndex @@ -147,7 +147,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( case StaticField(classType, fieldName) ⇒ Set(JavaStaticField(classType, fieldName)) - case NullFact ⇒ Set(NativeNullFact) + case NullFact ⇒ Set(NativeTaintNullFact) case _ ⇒ Set() // Nothing to do @@ -166,7 +166,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( */ private def nativeReturnFlow( exit: LLVMStatement, - in: NativeFact, + in: NativeTaintFact, call: JavaStatement, callFact: Fact, nativeCallee: Method, @@ -217,8 +217,8 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( // Track the call chain to the sink back case NativeFlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ flows += FlowFact(JavaMethod(call.method) +: flow) - case NativeNullFact ⇒ flows += NullFact - case _ ⇒ + case NativeTaintNullFact ⇒ flows += NullFact + case _ ⇒ } // Propagate taints of the return value @@ -232,7 +232,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( flows += ArrayElement(call.index, taintedIndex) case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ flows += InstanceField(call.index, declClass, taintedField)*/ - case NativeNullFact ⇒ + case NativeTaintNullFact ⇒ val taints = createTaints(nativeCallee, call) if (taints.nonEmpty) flows ++= taints case _ ⇒ // Nothing to do diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala index 3a483308f0..6ea41fd6f3 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -12,7 +12,7 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(NativeFunction, NativeFact)] = Seq.empty + override val entryPoints: Seq[(NativeFunction, NativeTaintFact)] = Seq.empty /** * The sanitize method is a sanitizer. @@ -23,12 +23,12 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint /** * We do not sanitize parameters. */ - override protected def sanitizesParameter(call: LLVMStatement, in: NativeFact): Boolean = false + override protected def sanitizesParameter(call: LLVMStatement, in: NativeTaintFact): Boolean = false /** * Creates a new variable fact for the callee, if the source was called. */ - protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeFact] = + protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeTaintFact] = if (callee.name == "source") Set.empty //TODO Set(NativeVariable()) else Set.empty @@ -39,17 +39,17 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint protected def createFlowFact( callee: Function, call: LLVMStatement, - in: Set[NativeFact] + in: Set[NativeTaintFact] ): Option[NativeFlowFact] = if (callee.name == "sink" && in.contains(JavaVariable(-2))) Some(NativeFlowFact(Seq(call.function))) else None } -class SimpleNativeForwardTaintAnalysis(implicit project: SomeProject) +class SimpleNativeForwardTaintAnalysis(project: SomeProject) extends NativeIFDSAnalysis(project, new SimpleNativeForwardTaintProblem(project), NativeTaint) -object NativeForwardTaintAnalysisScheduler extends NativeIFDSAnalysisScheduler[NativeFact] { - override def init(p: SomeProject, ps: PropertyStore) = new SimpleNativeForwardTaintAnalysis()(p) - override def property: IFDSPropertyMetaInformation[LLVMStatement, NativeFact] = NativeTaint +object NativeForwardTaintAnalysisScheduler extends NativeIFDSAnalysisScheduler[NativeTaintFact] { + override def init(p: SomeProject, ps: PropertyStore) = new SimpleNativeForwardTaintAnalysis(p) + override def property: IFDSPropertyMetaInformation[LLVMStatement, NativeTaintFact] = NativeTaint override val uses: Set[PropertyBounds] = Set() // ++ PropertyBounds.ub(Taint) TODO: we do not use the native taint yet } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 20bd3dace4..730891a66d 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -6,8 +6,8 @@ import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeFunct import org.opalj.ll.llvm.value.{Add, Alloca, BitCast, Call, GetElementPtr, Load, PHI, Ret, Store, Sub} import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeFact](project) with TaintProblem[LLVMFunction, LLVMStatement, NativeFact] { - override def nullFact: NativeFact = NativeNullFact +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeTaintFact](project) with TaintProblem[LLVMFunction, LLVMStatement, NativeTaintFact] { + override def nullFact: NativeTaintFact = NativeTaintNullFact /** * Computes the data flow for a normal statement. @@ -20,7 +20,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * that the facts in `in` held before `statement` and `successor` will be * executed next. */ - override def normalFlow(statement: LLVMStatement, in: NativeFact, predecessor: Option[LLVMStatement]): Set[NativeFact] = statement.instruction match { + override def normalFlow(statement: LLVMStatement, in: NativeTaintFact, predecessor: Option[LLVMStatement]): Set[NativeTaintFact] = statement.instruction match { case _: Alloca ⇒ Set(in) case store: Store ⇒ in match { case NativeVariable(value) if value == store.src ⇒ store.dst match { @@ -68,7 +68,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @return The facts, which hold after the execution of `statement` under the assumption that * the facts in `in` held before `statement` and `statement` calls `callee`. */ - override def callFlow(call: LLVMStatement, callee: NativeFunction, in: NativeFact): Set[NativeFact] = callee match { + override def callFlow(call: LLVMStatement, callee: NativeFunction, in: NativeTaintFact): Set[NativeTaintFact] = callee match { case LLVMFunction(callee) ⇒ in match { // Taint formal parameter if actual parameter is tainted @@ -77,7 +77,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD case None ⇒ Set() } // TODO pass other java taints - case NativeNullFact ⇒ Set(in) + case NativeTaintNullFact ⇒ Set(in) case NativeArrayElement(base, indices) ⇒ call.instruction.asInstanceOf[Call].indexOfArgument(base) match { case Some(index) ⇒ Set(NativeArrayElement(callee.argument(index), indices)) case None ⇒ Set() @@ -97,21 +97,21 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * under the assumption that `in` held before the execution of `exit` and that * `successor` will be executed next. */ - override def returnFlow(exit: LLVMStatement, in: NativeFact, call: LLVMStatement, callFact: NativeFact, successor: LLVMStatement): Set[NativeFact] = { + override def returnFlow(exit: LLVMStatement, in: NativeTaintFact, call: LLVMStatement, callFact: NativeTaintFact, successor: LLVMStatement): Set[NativeTaintFact] = { val callee = exit.callable() - var flows: Set[NativeFact] = if (sanitizesReturnValue(callee)) Set.empty else in match { + var flows: Set[NativeTaintFact] = if (sanitizesReturnValue(callee)) Set.empty else in match { case NativeVariable(value) ⇒ exit.instruction match { case ret: Ret if ret.value == value ⇒ Set(NativeVariable(call.instruction)) case _: Ret ⇒ Set() case _ ⇒ Set() } - case NativeNullFact ⇒ Set(NativeNullFact) + case NativeTaintNullFact ⇒ Set(NativeTaintNullFact) case NativeFlowFact(flow) if !flow.contains(call.function) ⇒ Set(NativeFlowFact(call.function +: flow)) case _ ⇒ Set() } if (exit.callable.name == "source") in match { - case NativeNullFact ⇒ flows += NativeVariable(call.instruction) + case NativeTaintNullFact ⇒ flows += NativeVariable(call.instruction) } if (exit.callable.name == "sink") in match { case NativeVariable(value) if value == exit.callable.function.argument(0) ⇒ @@ -129,7 +129,7 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * @return The facts, which hold after the call independently of what happens in the callee * under the assumption that `in` held before `call`. */ - override def callToReturnFlow(call: LLVMStatement, in: NativeFact, successor: LLVMStatement): Set[NativeFact] = Set(in) + override def callToReturnFlow(call: LLVMStatement, in: NativeTaintFact, successor: LLVMStatement): Set[NativeTaintFact] = Set(in) override def needsPredecessor(statement: LLVMStatement): Boolean = statement.instruction match { case PHI(_) ⇒ true diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala index 6b62db0389..d5e157c877 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala @@ -5,17 +5,17 @@ import org.opalj.br.ObjectType import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} import org.opalj.ll.llvm.value.Value -trait NativeFact extends AbstractIFDSFact +trait NativeTaintFact extends AbstractIFDSFact -case object NativeNullFact extends NativeFact with AbstractIFDSNullFact +case object NativeTaintNullFact extends NativeTaintFact with AbstractIFDSNullFact /** * A tainted variable. * * @param index The variable's definition site. */ -case class JavaVariable(index: Int) extends NativeFact -case class NativeVariable(value: Value) extends NativeFact +case class JavaVariable(index: Int) extends NativeTaintFact +case class NativeVariable(value: Value) extends NativeTaintFact /** * A tainted array element. @@ -23,8 +23,8 @@ case class NativeVariable(value: Value) extends NativeFact * @param index The array's definition site. * @param element The index of the tainted element in the array. */ -case class JavaArrayElement(index: Int, element: Int) extends NativeFact -case class NativeArrayElement(base: Value, indices: Iterable[Long]) extends NativeFact +case class JavaArrayElement(index: Int, element: Int) extends NativeTaintFact +case class NativeArrayElement(base: Value, indices: Iterable[Long]) extends NativeTaintFact /** * A tainted static field. @@ -32,7 +32,7 @@ case class NativeArrayElement(base: Value, indices: Iterable[Long]) extends Nati * @param classType The field's class. * @param fieldName The field's name. */ -case class JavaStaticField(classType: ObjectType, fieldName: String) extends NativeFact +case class JavaStaticField(classType: ObjectType, fieldName: String) extends NativeTaintFact /** * A tainted instance field. @@ -41,7 +41,7 @@ case class JavaStaticField(classType: ObjectType, fieldName: String) extends Nat * @param classType The field's type. * @param fieldName The field's value. */ -case class JavaInstanceField(index: Int, classType: ObjectType, fieldName: String) extends NativeFact +case class JavaInstanceField(index: Int, classType: ObjectType, fieldName: String) extends NativeTaintFact /** * A path of method calls, originating from the analyzed method, over which a tainted variable @@ -49,7 +49,7 @@ case class JavaInstanceField(index: Int, classType: ObjectType, fieldName: Strin * * @param flow A sequence of method calls, originating from but not including this method. */ -case class NativeFlowFact(flow: Seq[Callable]) extends NativeFact { +case class NativeFlowFact(flow: Seq[Callable]) extends NativeTaintFact { override val hashCode: Int = { var r = 1 flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala index f27a688d0b..d8adce7738 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala @@ -4,22 +4,22 @@ package org.opalj.ll.fpcf.properties import org.opalj.fpcf.PropertyKey import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.ll.fpcf.analyses.ifds.LLVMStatement -import org.opalj.ll.fpcf.analyses.ifds.taint.NativeFact +import org.opalj.ll.fpcf.analyses.ifds.taint.NativeTaintFact -case class NativeTaint(flows: Map[LLVMStatement, Set[NativeFact]], debugData: Map[LLVMStatement, Set[NativeFact]] = Map.empty) extends IFDSProperty[LLVMStatement, NativeFact] { +case class NativeTaint(flows: Map[LLVMStatement, Set[NativeTaintFact]], debugData: Map[LLVMStatement, Set[NativeTaintFact]] = Map.empty) extends IFDSProperty[LLVMStatement, NativeTaintFact] { override type Self = NativeTaint - override def create(result: Map[LLVMStatement, Set[NativeFact]]): IFDSProperty[LLVMStatement, NativeFact] = new NativeTaint(result) - override def create(result: Map[LLVMStatement, Set[NativeFact]], debugData: Map[LLVMStatement, Set[NativeFact]]): IFDSProperty[LLVMStatement, NativeFact] = new NativeTaint(result, debugData) + override def create(result: Map[LLVMStatement, Set[NativeTaintFact]]): IFDSProperty[LLVMStatement, NativeTaintFact] = new NativeTaint(result) + override def create(result: Map[LLVMStatement, Set[NativeTaintFact]], debugData: Map[LLVMStatement, Set[NativeTaintFact]]): IFDSProperty[LLVMStatement, NativeTaintFact] = new NativeTaint(result, debugData) override def key: PropertyKey[NativeTaint] = NativeTaint.key } -object NativeTaint extends IFDSPropertyMetaInformation[LLVMStatement, NativeFact] { +object NativeTaint extends IFDSPropertyMetaInformation[LLVMStatement, NativeTaintFact] { override type Self = NativeTaint - override def create(result: Map[LLVMStatement, Set[NativeFact]]): IFDSProperty[LLVMStatement, NativeFact] = new NativeTaint(result) - override def create(result: Map[LLVMStatement, Set[NativeFact]], debugData: Map[LLVMStatement, Set[NativeFact]]): IFDSProperty[LLVMStatement, NativeFact] = new NativeTaint(result, debugData) + override def create(result: Map[LLVMStatement, Set[NativeTaintFact]]): IFDSProperty[LLVMStatement, NativeTaintFact] = new NativeTaint(result) + override def create(result: Map[LLVMStatement, Set[NativeTaintFact]], debugData: Map[LLVMStatement, Set[NativeTaintFact]]): IFDSProperty[LLVMStatement, NativeTaintFact] = new NativeTaint(result, debugData) val key: PropertyKey[NativeTaint] = PropertyKey.create("NativeTaint", new NativeTaint(Map.empty)) } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala index 2c280e9939..9c0521a754 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Argument.scala @@ -5,8 +5,12 @@ import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM.{LLVMGetParamParent, LLVMGetValueKind, LLVMArgumentValueKind} import org.opalj.ll.llvm.value -case class Argument(ref: LLVMValueRef) extends Value(ref) { +case class Argument(ref: LLVMValueRef, index: Int) extends Value(ref) { assert(LLVMGetValueKind(ref) == LLVMArgumentValueKind, "ref has to be an argument") def parent(): value.Function = value.Function(LLVMGetParamParent(ref)) } + +object Argument { + def apply(ref: LLVMValueRef): Argument = value.Function(LLVMGetParamParent(ref)).arguments.find(_.ref == ref).get +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala index cb3638420d..19e555c478 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala @@ -4,7 +4,6 @@ package org.opalj.ll.llvm.value import org.bytedeco.llvm.LLVM.{LLVMBasicBlockRef, LLVMValueRef} import org.bytedeco.llvm.global.LLVM._ import org.opalj.graphs.Node -import org.opalj.ll.llvm.value case class BasicBlock(block_ref: LLVMBasicBlockRef) extends Value(LLVMBasicBlockAsValue(block_ref)) @@ -14,11 +13,11 @@ case class BasicBlock(block_ref: LLVMBasicBlockRef) def firstInstruction: Instruction = Instruction(LLVMGetFirstInstruction(block_ref)) def lastInstruction: Instruction = Instruction(LLVMGetLastInstruction(block_ref)) - def terminator(): Option[Instruction with value.Terminator] = { + def terminator(): Option[Instruction with Terminator] = { OptionalInstruction(LLVMGetBasicBlockTerminator(block_ref)) match { case Some(terminator) ⇒ { assert(terminator.isTerminator) - Some(terminator.asInstanceOf[Instruction with value.Terminator]) + Some(terminator.asInstanceOf[Instruction with Terminator]) } case None ⇒ None } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala index ab0c5c9ff7..16b13deaed 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Function.scala @@ -19,7 +19,7 @@ case class Function(ref: LLVMValueRef) extends Value(ref) { def argumentCount: Int = LLVMCountParams(ref) def argument(index: Int): Argument = { assert(index < argumentCount) - Argument(LLVMGetParam(ref, index)) + Argument(LLVMGetParam(ref, index), index) } def entryBlock: BasicBlock = { @@ -57,10 +57,12 @@ class BasicBlockIterator(var ref: LLVMBasicBlockRef) extends Iterator[BasicBlock class ArgumentIterator(var ref: LLVMValueRef) extends Iterator[Argument] { override def hasNext: Boolean = ref != null + var index = 0 override def next(): Argument = { - val argument = Argument(ref) + val argument = Argument(ref, index) this.ref = LLVMGetNextParam(ref) + index += 1 argument } } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala index 6736c2eac7..5939f33590 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/GlobalVariable.scala @@ -2,5 +2,8 @@ package org.opalj.ll.llvm.value import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.LLVMGetInitializer -case class GlobalVariable(ref: LLVMValueRef) extends Value(ref: LLVMValueRef) +case class GlobalVariable(ref: LLVMValueRef) extends Value(ref: LLVMValueRef) { + def initializer: Value = Value(LLVMGetInitializer(ref)).get +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala index 0dff6d0b8e..f2cf298efc 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala @@ -3,7 +3,8 @@ package org.opalj.ll.llvm.value import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM._ -import org.opalj.ll.llvm.Type +import org.opalj.ll.llvm.value.constant.ConstantIntValue +import org.opalj.ll.llvm.{FunctionType, Type} object OptionalInstruction { def apply(ref: LLVMValueRef): Option[Instruction] = { @@ -100,7 +101,7 @@ trait Terminator { (0 to numSuccessors - 1).map(i ⇒ getSuccessor(i).firstInstruction) } -sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { +sealed abstract class Instruction(ref: LLVMValueRef) extends User(ref) { def isTerminator: Boolean = LLVMIsATerminatorInst(ref) != null def parent: BasicBlock = BasicBlock(LLVMGetInstructionParent(ref)) def function: Function = parent.parent @@ -109,12 +110,6 @@ sealed abstract class Instruction(ref: LLVMValueRef) extends Value(ref) { override def toString: String = { s"${this.getClass.getSimpleName}(${repr})" } - - def numOperands: Int = LLVMGetNumOperands(ref) - def operand(index: Int): Value = { - assert(index < numOperands) - Value(LLVMGetOperand(ref, index)).get - } } case class Ret(ref: LLVMValueRef) extends Instruction(ref) with Terminator { @@ -166,6 +161,9 @@ case class GetElementPtr(ref: LLVMValueRef) extends Instruction(ref) { def isConstant = (1 until numOperands).forall(operand(_).isInstanceOf[ConstantIntValue]) def constants = (1 until numOperands).map(operand(_).asInstanceOf[ConstantIntValue].signExtendedValue) def isZero = isConstant && constants.forall(_ == 0) + + def numIndices: Int = LLVMGetNumIndices(ref) + def indices: Iterable[Int] = LLVMGetIndices(ref).asBuffer().array() } case class Trunc(ref: LLVMValueRef) extends Instruction(ref) case class ZExt(ref: LLVMValueRef) extends Instruction(ref) @@ -184,13 +182,14 @@ case class ICmp(ref: LLVMValueRef) extends Instruction(ref) case class FCmp(ref: LLVMValueRef) extends Instruction(ref) case class PHI(ref: LLVMValueRef) extends Instruction(ref) case class Call(ref: LLVMValueRef) extends Instruction(ref) { - def calledValue: Value = Value(LLVMGetCalledValue(ref)).get - def calledFunctionType: Type = Type(LLVMGetCalledFunctionType(ref)) + def calledValue: Value = Value(LLVMGetCalledValue(ref)).get // corresponds to last operand + def calledFunctionType: FunctionType = Type(LLVMGetCalledFunctionType(ref)).asInstanceOf[FunctionType] def indexOfArgument(argument: Value): Option[Int] = { for (i ← 0 to numOperands) if (operand(i) == argument) return Some(i) None } + def numArgOperands: Int = LLVMGetNumArgOperands(ref) } case class Select(ref: LLVMValueRef) extends Instruction(ref) case class UserOp1(ref: LLVMValueRef) extends Instruction(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Use.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Use.scala new file mode 100644 index 0000000000..4cf208eb0f --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Use.scala @@ -0,0 +1,10 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value + +import org.bytedeco.llvm.LLVM.LLVMUseRef +import org.bytedeco.llvm.global.LLVM.{LLVMGetUsedValue, LLVMGetUser} + +case class Use(ref: LLVMUseRef) { + def value: Value = Value(LLVMGetUsedValue(ref)).get + def user: Value = Value(LLVMGetUser(ref)).get +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/User.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/User.scala new file mode 100644 index 0000000000..ec9bc7fd60 --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/User.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm +package value + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM._ + +class User(ref: LLVMValueRef) extends Value(ref) { + def numOperands: Int = LLVMGetNumOperands(ref) + def operand(index: Int): Value = { + assert(index < numOperands) + Value(LLVMGetOperand(ref, index)).get + } + def operandUse(index: Int): UsesIterator = { + assert(index < numOperands) + new UsesIterator(LLVMGetOperandUse(ref, index)) + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala index b94348c2d6..6f3b314749 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala @@ -1,9 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.ll.llvm.value +package org.opalj.ll.llvm +package value -import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.LLVM.{LLVMUseRef, LLVMValueRef} import org.bytedeco.llvm.global.LLVM._ -import org.opalj.ll.llvm.Type +import org.opalj.ll.llvm.value.constant.{ConstantDataArray, ConstantDataVector, ConstantExpression, ConstantIntValue} class Value(ref: LLVMValueRef) { def repr: String = { @@ -26,6 +27,9 @@ class Value(ref: LLVMValueRef) { override def toString: String = { s"${getClass.getSimpleName}(${repr})" } + + def uses: UsesIterator = new UsesIterator(LLVMGetFirstUse(ref)) + def users: Iterator[Value] = uses.map(_.user) } object Value { @@ -33,33 +37,43 @@ object Value { if (ref == null) return None if (ref.isNull) return None Some(LLVMGetValueKind(ref) match { - case LLVMArgumentValueKind ⇒ Argument(ref) - case LLVMBasicBlockValueKind ⇒ BasicBlock(LLVMValueAsBasicBlock(ref)) + case LLVMArgumentValueKind ⇒ Argument(ref) + case LLVMBasicBlockValueKind ⇒ BasicBlock(LLVMValueAsBasicBlock(ref)) //LLVMMemoryUseValueKind //LLVMMemoryDefValueKind //LLVMMemoryPhiValueKind - case LLVMFunctionValueKind ⇒ Function(ref) + case LLVMFunctionValueKind ⇒ Function(ref) //LLVMGlobalAliasValueKind //LLVMGlobalIFuncValueKind - case LLVMGlobalVariableValueKind ⇒ GlobalVariable(ref) + case LLVMGlobalVariableValueKind ⇒ GlobalVariable(ref) //LLVMBlockAddressValueKind - //LLVMConstantExprValueKind + case LLVMConstantExprValueKind ⇒ ConstantExpression(ref) //LLVMConstantArrayValueKind //LLVMConstantStructValueKind //LLVMConstantVectorValueKind //LLVMUndefValueValueKind //LLVMConstantAggregateZeroValueKind - //LLVMConstantDataArrayValueKind - //LLVMConstantDataVectorValueKind - case LLVMConstantIntValueKind ⇒ ConstantIntValue(ref) + case LLVMConstantDataArrayValueKind ⇒ ConstantDataArray(ref) + case LLVMConstantDataVectorValueKind ⇒ ConstantDataVector(ref) + case LLVMConstantIntValueKind ⇒ ConstantIntValue(ref) //LLVMConstantFPValueKind //LLVMConstantPointerNullValueKind //LLVMConstantTokenNoneValueKind //LLVMMetadataAsValueValueKind //LLVMInlineAsmValueKind - case LLVMInstructionValueKind ⇒ Instruction(ref) + case LLVMInstructionValueKind ⇒ Instruction(ref) //LLVMPoisonValueValueKind - case valueKind ⇒ throw new IllegalArgumentException("unknown valueKind: "+valueKind) + case valueKind ⇒ throw new IllegalArgumentException("unknown valueKind: "+valueKind) }) } } + +class UsesIterator(var ref: LLVMUseRef) extends Iterator[Use] { + override def hasNext: Boolean = ref != null + + override def next(): Use = { + val use = value.Use(ref) + this.ref = LLVMGetNextUse(ref) + use + } +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantDataSequential.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantDataSequential.scala new file mode 100644 index 0000000000..59c1e63ffc --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantDataSequential.scala @@ -0,0 +1,16 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ + +package org.opalj.ll.llvm +package value +package constant + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM.LLVMGetAsString +import org.bytedeco.javacpp.SizeTPointer + +abstract class ConstantDataSequential(ref: LLVMValueRef) extends User(ref) { + def asString: String = LLVMGetAsString(ref, new SizeTPointer(1)).getString +} + +case class ConstantDataArray(ref: LLVMValueRef) extends ConstantDataSequential(ref) +case class ConstantDataVector(ref: LLVMValueRef) extends ConstantDataSequential(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala new file mode 100644 index 0000000000..adaf373c4a --- /dev/null +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala @@ -0,0 +1,159 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.ll.llvm.value +package constant + +import org.bytedeco.llvm.LLVM.LLVMValueRef +import org.bytedeco.llvm.global.LLVM._ +import org.opalj.ll.llvm.value.User + +object ConstantExpression { + def apply(ref: LLVMValueRef): ConstantExpression = { + assert(ref != null && !ref.isNull(), "ref may not be null") + assert(LLVMGetValueKind(ref) == LLVMConstantExprValueKind, "ref has to be an instruction") + LLVMGetConstOpcode(ref) match { + // case LLVMRet => RetConst(ref) + // case LLVMBr => BrConst(ref) + // case LLVMSwitch => SwitchConst(ref) + // case LLVMIndirectBr => IndirectBrConst(ref) + // case LLVMInvoke => InvokeConst(ref) + // case LLVMUnreachable => UnreachableConst(ref) + // case LLVMCallBr => CallBrConst(ref) + // case LLVMFNeg => FNegConst(ref) + // case LLVMAdd => AddConst(ref) + // case LLVMFAdd => FAddConst(ref) + // case LLVMSub => SubConst(ref) + // case LLVMFSub => FSubConst(ref) + // case LLVMMul => MulConst(ref) + // case LLVMFMul => FMulConst(ref) + // case LLVMUDiv => UDivConst(ref) + // case LLVMSDiv => SDivConst(ref) + // case LLVMFDiv => FDivConst(ref) + // case LLVMURem => URemConst(ref) + // case LLVMSRem => SRemConst(ref) + // case LLVMFRem => FRemConst(ref) + // case LLVMShl => ShlConst(ref) + // case LLVMLShr => LShrConst(ref) + // case LLVMAShr => AShrConst(ref) + // case LLVMAnd => AndConst(ref) + // case LLVMOr => OrConst(ref) + // case LLVMXor => XorConst(ref) + // case LLVMAlloca => AllocaConst(ref) + // case LLVMLoad => LoadConst(ref) + // case LLVMStore => StoreConst(ref) + case LLVMGetElementPtr ⇒ GetElementPtrConst(ref) + // case LLVMTrunc => TruncConst(ref) + // case LLVMZExt => ZExtConst(ref) + // case LLVMSExt => SExtConst(ref) + // case LLVMFPToUI => FPToUIConst(ref) + // case LLVMFPToSI => FPToSIConst(ref) + // case LLVMUIToFP => UIToFPConst(ref) + // case LLVMSIToFP => SIToFPConst(ref) + // case LLVMFPTrunc => FPTruncConst(ref) + // case LLVMFPExt => FPExtConst(ref) + // case LLVMPtrToInt => PtrToIntConst(ref) + // case LLVMIntToPtr => IntToPtrConst(ref) + // case LLVMBitCast => BitCastConst(ref) + // case LLVMAddrSpaceCast => AddrSpaceCastConst(ref) + // case LLVMICmp => ICmpConst(ref) + // case LLVMFCmp => FCmpConst(ref) + // case LLVMPHI => PHIConst(ref) + // case LLVMCall => CallConst(ref) + // case LLVMSelect => SelectConst(ref) + // case LLVMUserOp1 => UserOp1Const(ref) + // case LLVMUserOp2 => UserOp2Const(ref) + // case LLVMVAArg => VAArgConst(ref) + // case LLVMExtractElement => ExtractElementConst(ref) + // case LLVMInsertElement => InsertElementConst(ref) + // case LLVMShuffleVector => ShuffleVectorConst(ref) + // case LLVMExtractValue => ExtractValueConst(ref) + // case LLVMInsertValue => InsertValueConst(ref) + // case LLVMFreeze => FreezeConst(ref) + // case LLVMFence => FenceConst(ref) + // case LLVMAtomicCmpXchg => AtomicCmpXchgConst(ref) + // case LLVMAtomicRMW => AtomicRMWConst(ref) + // case LLVMResume => ResumeConst(ref) + // case LLVMLandingPad => LandingPadConst(ref) + // case LLVMCleanupRet => CleanupRetConst(ref) + // case LLVMCatchRet => CatchRetConst(ref) + // case LLVMCatchPad => CatchPadConst(ref) + // case LLVMCleanupPad => CleanupPadConst(ref) + // case LLVMCatchSwitch => CatchSwitchConst(ref) + case opCode ⇒ throw new IllegalArgumentException("unknown instruction opcode: "+opCode) + } + } +} + +sealed abstract class ConstantExpression(ref: LLVMValueRef) extends User(ref) + +// // case class RetConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class BrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SwitchConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class IndirectBrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class InvokeConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UnreachableConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CallBrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FNegConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AddConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FAddConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SubConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FSubConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class MulConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FMulConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UDivConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SDivConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FDivConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class URemConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SRemConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FRemConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ShlConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class LShrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AShrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AndConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class OrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class XorConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AllocaConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class LoadConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class StoreConst(ref: LLVMValueRef) extends ConstantExpression(ref) +case class GetElementPtrConst(ref: LLVMValueRef) extends ConstantExpression(ref) { + def base: Value = operand(0) + def isConstant = (1 until numOperands).forall(operand(_).isInstanceOf[ConstantIntValue]) + def constants = (1 until numOperands).map(operand(_).asInstanceOf[ConstantIntValue].signExtendedValue) + def isZero = isConstant && constants.forall(_ == 0) +} +// case class TruncConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ZExtConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SExtConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FPToUIConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FPToSIConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UIToFPConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SIToFPConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FPTruncConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FPExtConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class PtrToIntConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class IntToPtrConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class BitCastConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AddrSpaceCastConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ICmpConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FCmpConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class PHIConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CallConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class SelectConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UserOp1Const(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class UserOp2Const(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class VAArgConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ExtractElementConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class InsertElementConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ShuffleVectorConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ExtractValueConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class InsertValueConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FreezeConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class FenceConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AtomicCmpXchgConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class AtomicRMWConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class ResumeConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class LandingPadConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CleanupRetConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CatchRetConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CatchPadConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CleanupPadConst(ref: LLVMValueRef) extends ConstantExpression(ref) +// case class CatchSwitchConst(ref: LLVMValueRef) extends ConstantExpression(ref) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/ScalarConstants.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ScalarConstants.scala similarity index 74% rename from OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/ScalarConstants.scala rename to OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ScalarConstants.scala index 409571c89c..6d74d224a6 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/ScalarConstants.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ScalarConstants.scala @@ -1,10 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.llvm.value +package constant import org.bytedeco.llvm.LLVM.LLVMValueRef import org.bytedeco.llvm.global.LLVM.{LLVMConstIntGetSExtValue, LLVMConstIntGetZExtValue} +import org.opalj.ll.llvm.value.User -case class ConstantIntValue(ref: LLVMValueRef) extends Value(ref) { +case class ConstantIntValue(ref: LLVMValueRef) extends User(ref) { def zeroExtendedValue: Long = LLVMConstIntGetZExtValue(ref) def signExtendedValue: Long = LLVMConstIntGetSExtValue(ref) } From e5d34a1f54b1a8ce10328b8f8f2ea53eed72349d Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Sun, 26 Jun 2022 17:35:53 +0200 Subject: [PATCH 51/67] JNI call analysis WIP --- .../HerosBackwardClassForNameAnalysis.scala | 48 +++--- .../HerosForwardClassForNameAnalysis.scala | 74 ++++----- .../analyses/taint/HerosTaintAnalysis.scala | 6 +- ...rdClassForNameTaintAnalysisScheduler.scala | 22 +-- ...rdClassForNameTaintAnalysisScheduler.scala | 18 +-- .../ForwardTaintAnalysisFixtureTest.scala | 4 +- .../BackwardTaintAnalysisFixtureTest.scala | 4 +- .../old/ForwardTaintAnalysisFixtureTest.scala | 6 +- .../taint/BackwardFlowPathMatcher.scala | 4 +- ...rossLanguageForwardTaintAnalysisTest.scala | 8 +- .../src/main/scala/org/opalj/ifds/ICFG.scala | 2 +- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 2 +- .../ll/fpcf/analyses/ifds/Callable.scala | 5 + .../ll/fpcf/analyses/ifds/JNICallUtil.scala | 39 +++-- .../analyses/ifds/NativeForwardICFG.scala | 9 +- .../analyses/ifds/NativeIFDSProblem.scala | 58 ++++++- .../ifds/taint/JavaForwardTaintAnalysis.scala | 30 ++-- .../taint/NativeForwardTaintAnalysis.scala | 4 +- .../taint/NativeForwardTaintProblem.scala | 153 +++++++++++++++++- .../ifds/taint/NativeTaintProblem.scala | 4 +- .../org/opalj/ll/fpcf/properties/Taint.scala | 2 +- .../ifds/old/taint/BackwardTaintProblem.scala | 58 +++---- .../ifds/old/taint/ForwardTaintProblem.scala | 32 ++-- .../ifds/old/taint/TaintProblem.scala | 6 +- .../ifds/taint/ForwardTaintProblem.scala | 30 ++-- .../analyses/ifds/taint/TaintProblem.scala | 14 +- .../opalj/tac/fpcf/properties/OldTaint.scala | 14 +- .../org/opalj/tac/fpcf/properties/Taint.scala | 14 +- .../taint/ForwardTaintAnalysisFixture.scala | 14 +- .../old/BackwardTaintAnalysisFixture.scala | 24 +-- .../old/ForwardTaintAnalysisFixture.scala | 16 +- 31 files changed, 474 insertions(+), 250 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala index 5d68ea4f87..6b4634623a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -33,7 +33,7 @@ import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, Fact, FlowFact, InstanceField, StaticField, Variable} +import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, TaintFact, FlowFact, InstanceField, StaticField, Variable} /** * An implementation of the BackwardClassForNameAnalysis in the Heros framework. @@ -43,25 +43,25 @@ import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, Fact, FlowFact, Ins */ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) extends HerosTaintAnalysis(p, icfg) { - override val initialSeeds: util.Map[JavaStatement, util.Set[Fact]] = + override val initialSeeds: util.Map[JavaStatement, util.Set[TaintFact]] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "java/lang/Class") .flatMap(classFile ⇒ classFile.methods) .filter(_.name == "forName") - .map(method ⇒ icfg.getExitStmt(method) → Set[Fact](Variable(-2)).asJava).toMap.asJava + .map(method ⇒ icfg.getExitStmt(method) → Set[TaintFact](Variable(-2)).asJava).toMap.asJava var flowFacts = Map.empty[Method, Set[FlowFact]] override def followReturnsPastSeeds(): Boolean = true - override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { + override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, TaintFact, Method] = { - new FlowFunctions[JavaStatement, Fact, Method]() { + new FlowFunctions[JavaStatement, TaintFact, Method]() { - override def getNormalFlowFunction(statement: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { + override def getNormalFlowFunction(statement: JavaStatement, succ: JavaStatement): FlowFunction[TaintFact] = { val method = statement.method val stmt = statement.stmt - source: Fact ⇒ { + source: TaintFact ⇒ { var result = stmt.astID match { case Assignment.ASTID ⇒ if (isTainted(statement.index, source)) @@ -79,7 +79,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) case ArrayElement(index, taintedElement) if arrayDefinedBy.contains(index) && (arrayIndex.isEmpty || arrayIndex.get == taintedElement) ⇒ createNewTaints(arrayStore.value, statement) - case _ ⇒ Set.empty[Fact] + case _ ⇒ Set.empty[TaintFact] }) + source if (arrayDefinedBy.size == 1 && arrayIndex.isDefined) facts -= ArrayElement(arrayDefinedBy.head, arrayIndex.get) @@ -119,10 +119,10 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } - override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[Fact] = { + override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[TaintFact] = { val callObject = asCall(stmt.stmt) val staticCall = callee.isStatic - source: Fact ⇒ { + source: TaintFact ⇒ { val returnValueFacts = if (stmt.stmt.astID == Assignment.ASTID) source match { @@ -132,7 +132,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) toArrayElement(createNewTaintsForCallee(callee), taintedElement) case InstanceField(index, declaringClass, name) if index == stmt.index ⇒ toInstanceField(createNewTaintsForCallee(callee), declaringClass, name) - case _ ⇒ Set.empty[Fact] + case _ ⇒ Set.empty[TaintFact] } else Set.empty val thisOffset = if (callee.isStatic) 0 else 1 @@ -161,7 +161,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } } - override def getReturnFlowFunction(statement: JavaStatement, callee: Method, exit: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { + override def getReturnFlowFunction(statement: JavaStatement, callee: Method, exit: JavaStatement, succ: JavaStatement): FlowFunction[TaintFact] = { // If a method has no caller, returnFlow will be called with a null statement. if (statement == null) return Identity.v() val stmt = statement.stmt @@ -170,7 +170,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) val thisOffset = if (staticCall) 0 else 1 val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) .map(index ⇒ JavaIFDSProblem.switchParamAndVariableIndex(index + thisOffset, staticCall)) - source: Fact ⇒ + source: TaintFact ⇒ (source match { case Variable(index) if formalParameterIndices.contains(index) ⇒ createNewTaints( @@ -186,34 +186,34 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) callStatement.allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), statement ), declaringClass, name) - case staticField: StaticField ⇒ Set[Fact](staticField) - case _ ⇒ Set.empty[Fact] + case staticField: StaticField ⇒ Set[TaintFact](staticField) + case _ ⇒ Set.empty[TaintFact] }).asJava } - override def getCallToReturnFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = + override def getCallToReturnFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[TaintFact] = Identity.v() } } - private def isTainted(index: Int, source: Fact, taintedElement: Option[Int] = None): Boolean = source match { + private def isTainted(index: Int, source: TaintFact, taintedElement: Option[Int] = None): Boolean = source match { case Variable(variableIndex) ⇒ variableIndex == index case ArrayElement(variableIndex, element) ⇒ variableIndex == index && (taintedElement.isEmpty || taintedElement.get == element) case _ ⇒ false } - private def createNewTaintsForCallee(callee: Method): Set[Fact] = { + private def createNewTaintsForCallee(callee: Method): Set[TaintFact] = { icfg.getStartPointsOf(callee).asScala.flatMap { statement ⇒ val stmt = statement.stmt stmt.astID match { case ReturnValue.ASTID ⇒ createNewTaints(stmt.asReturnValue.expr, statement) - case _ ⇒ Set.empty[Fact] + case _ ⇒ Set.empty[TaintFact] } }.toSet } - private def createNewTaints(expression: Expr[V], statement: JavaStatement): Set[Fact] = + private def createNewTaints(expression: Expr[V], statement: JavaStatement): Set[TaintFact] = expression.astID match { case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) case ArrayLoad.ASTID ⇒ @@ -224,7 +224,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) else arrayDefinedBy.map(Variable) case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) ⇒ acc ++ createNewTaints(expression.subExpr(subExpr), statement)) case GetField.ASTID ⇒ val getField = expression.asGetField @@ -236,14 +236,14 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) case _ ⇒ Set.empty } - private def toArrayElement(facts: Set[Fact], taintedElement: Int): Set[Fact] = + private def toArrayElement(facts: Set[TaintFact], taintedElement: Int): Set[TaintFact] = facts.map { case Variable(variableIndex) ⇒ ArrayElement(variableIndex, taintedElement) case ArrayElement(variableIndex, _) ⇒ ArrayElement(variableIndex, taintedElement) case InstanceField(variableIndex, _, _) ⇒ ArrayElement(variableIndex, taintedElement) } - private def toInstanceField(facts: Set[Fact], declaringClass: ObjectType, name: String): Set[Fact] = + private def toInstanceField(facts: Set[TaintFact], declaringClass: ObjectType, name: String): Set[TaintFact] = facts.map { case Variable(variableIndex) ⇒ InstanceField(variableIndex, declaringClass, name) case ArrayElement(variableIndex, _) ⇒ InstanceField(variableIndex, declaringClass, name) @@ -253,7 +253,7 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } -class HerosBackwardClassForNameAnalysisRunner extends HerosAnalysisRunner[Fact, HerosBackwardClassForNameAnalysis] { +class HerosBackwardClassForNameAnalysisRunner extends HerosAnalysisRunner[TaintFact, HerosBackwardClassForNameAnalysis] { override protected def createAnalysis(p: SomeProject): HerosBackwardClassForNameAnalysis = new HerosBackwardClassForNameAnalysis(p, new OpalBackwardICFG(p)) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index 31bc1b5ccf..b77cdfb865 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -30,11 +30,11 @@ import scala.collection.JavaConverters._ class HerosForwardClassForNameAnalysis( p: SomeProject, icfg: OpalForwardICFG, - initialMethods: Map[Method, util.Set[Fact]] + initialMethods: Map[Method, util.Set[TaintFact]] ) extends HerosTaintAnalysis(p, icfg) { - override val initialSeeds: util.Map[JavaStatement, util.Set[Fact]] = { - var result: Map[JavaStatement, util.Set[Fact]] = Map.empty + override val initialSeeds: util.Map[JavaStatement, util.Set[TaintFact]] = { + var result: Map[JavaStatement, util.Set[TaintFact]] = Map.empty for ((m, facts) ← initialMethods) { result += icfg.getStartPointsOf(m).iterator().next() -> facts } @@ -45,11 +45,11 @@ class HerosForwardClassForNameAnalysis( var flowFacts = Map.empty[Method, Set[FlowFact]] - override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, Fact, Method] = { + override def createFlowFunctionsFactory(): FlowFunctions[JavaStatement, TaintFact, Method] = { - new FlowFunctions[JavaStatement, Fact, Method]() { + new FlowFunctions[JavaStatement, TaintFact, Method]() { - override def getNormalFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[Fact] = { + override def getNormalFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[TaintFact] = { stmt.stmt.astID match { case Assignment.ASTID ⇒ handleAssignment(stmt, stmt.stmt.asAssignment.expr) @@ -57,12 +57,12 @@ class HerosForwardClassForNameAnalysis( val store = stmt.stmt.asArrayStore val definedBy = store.arrayRef.asVar.definedBy val index = TaintProblem.getIntConstant(store.index, stmt.code) - (source: Fact) ⇒ { + (source: TaintFact) ⇒ { if (isTainted(store.value, source)) { if (index.isDefined) { - (definedBy.iterator.map[Fact](ArrayElement(_, index.get)).toSet + source).asJava + (definedBy.iterator.map[TaintFact](ArrayElement(_, index.get)).toSet + source).asJava } else { - (definedBy.iterator.map[Fact](Variable).toSet + source).asJava + (definedBy.iterator.map[TaintFact](Variable).toSet + source).asJava } } else { if (index.isDefined && definedBy.size == 1) { @@ -77,7 +77,7 @@ class HerosForwardClassForNameAnalysis( } case PutStatic.ASTID ⇒ val put = stmt.stmt.asPutStatic - (source: Fact) ⇒ { + (source: TaintFact) ⇒ { if (isTainted(put.value, source)) TwoElementSet.twoElementSet(source, StaticField(put.declaringClass, put.name)) else Collections.singleton(source) @@ -85,11 +85,11 @@ class HerosForwardClassForNameAnalysis( case PutField.ASTID ⇒ val put = stmt.stmt.asPutField val definedBy = put.objRef.asVar.definedBy - (source: Fact) ⇒ { + (source: TaintFact) ⇒ { if (isTainted(put.value, source)) { (definedBy.iterator .map(InstanceField(_, put.declaringClass, put.name)) - .toSet[Fact] + source).asJava + .toSet[TaintFact] + source).asJava } else { Collections.singleton(source) } @@ -98,10 +98,10 @@ class HerosForwardClassForNameAnalysis( } } - def handleAssignment(stmt: JavaStatement, expr: Expr[V]): FlowFunction[Fact] = + def handleAssignment(stmt: JavaStatement, expr: Expr[V]): FlowFunction[TaintFact] = expr.astID match { case Var.ASTID ⇒ - (source: Fact) ⇒ { + (source: TaintFact) ⇒ { source match { case Variable(index) if expr.asVar.definedBy.contains(index) ⇒ TwoElementSet.twoElementSet(source, Variable(stmt.index)) @@ -111,7 +111,7 @@ class HerosForwardClassForNameAnalysis( case ArrayLoad.ASTID ⇒ val load = expr.asArrayLoad val arrayDefinedBy = load.arrayRef.asVar.definedBy - (source: Fact) ⇒ { + (source: TaintFact) ⇒ { source match { // The specific array element may be tainted case ArrayElement(index, taintedIndex) ⇒ @@ -129,7 +129,7 @@ class HerosForwardClassForNameAnalysis( } case GetStatic.ASTID ⇒ val get = expr.asGetStatic - (source: Fact) ⇒ { + (source: TaintFact) ⇒ { if (source == StaticField(get.declaringClass, get.name)) TwoElementSet.twoElementSet(source, Variable(stmt.index)) else Collections.singleton(source) @@ -137,7 +137,7 @@ class HerosForwardClassForNameAnalysis( case GetField.ASTID ⇒ val get = expr.asGetField val objectDefinedBy = get.objRef.asVar.definedBy - (source: Fact) ⇒ { + (source: TaintFact) ⇒ { source match { // The specific field may be tainted case InstanceField(index, _, taintedField) if taintedField == get.name && objectDefinedBy.contains(index) ⇒ @@ -150,8 +150,8 @@ class HerosForwardClassForNameAnalysis( } case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ - (source: Fact) ⇒ { - val result = new util.HashSet[Fact] + (source: TaintFact) ⇒ { + val result = new util.HashSet[TaintFact] (0 until expr.subExprCount) .foreach( subExpression ⇒ @@ -165,12 +165,12 @@ class HerosForwardClassForNameAnalysis( case _ ⇒ Identity.v() } - override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[Fact] = { + override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[TaintFact] = { val callObject = asCall(stmt.stmt) val allParams = callObject.allParams if (relevantCallee(callee)) { val allParamsWithIndices = allParams.zipWithIndex - source: Fact ⇒ + source: TaintFact ⇒ (source match { case Variable(index) ⇒ // Taint formal parameter if actual parameter is tainted allParamsWithIndices @@ -180,7 +180,7 @@ class HerosForwardClassForNameAnalysis( JavaIFDSProblem.switchParamAndVariableIndex(paramIdx, callee.isStatic) ) } - .toSet[Fact] + .toSet[TaintFact] case ArrayElement(index, taintedIndex) ⇒ // Taint element of formal parameter if element of actual parameter is tainted @@ -192,7 +192,7 @@ class HerosForwardClassForNameAnalysis( taintedIndex ) } - .toSet[Fact] + .toSet[TaintFact] case InstanceField(index, declClass, taintedField) ⇒ // Taint field of formal parameter if field of actual parameter is tainted @@ -211,9 +211,9 @@ class HerosForwardClassForNameAnalysis( taintedField ) } - .toSet[Fact] - case sf: StaticField ⇒ Set(sf).asInstanceOf[Set[Fact]] - case _ ⇒ Set.empty[Fact] + .toSet[TaintFact] + case sf: StaticField ⇒ Set(sf).asInstanceOf[Set[TaintFact]] + case _ ⇒ Set.empty[TaintFact] }).asJava } else KillAll.v() } @@ -223,7 +223,7 @@ class HerosForwardClassForNameAnalysis( callee: Method, exit: JavaStatement, succ: JavaStatement - ): FlowFunction[Fact] = { + ): FlowFunction[TaintFact] = { def isRefTypeParam(index: Int): Boolean = if (index == -1) true @@ -240,7 +240,7 @@ class HerosForwardClassForNameAnalysis( val callStatement = asCall(stmt.stmt) val allParams = callStatement.allParams - source: Fact ⇒ { + source: TaintFact ⇒ { val paramFacts = source match { case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ @@ -257,7 +257,7 @@ class HerosForwardClassForNameAnalysis( ) params.asVar.definedBy.iterator .map(ArrayElement(_, taintedIndex)) - .asInstanceOf[Iterator[Fact]] + .asInstanceOf[Iterator[TaintFact]] .toSet case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ @@ -266,7 +266,7 @@ class HerosForwardClassForNameAnalysis( allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic)) params.asVar.definedBy.iterator .map(InstanceField(_, declClass, taintedField)) - .asInstanceOf[Iterator[Fact]] + .asInstanceOf[Iterator[TaintFact]] .toSet case sf: StaticField ⇒ Set(sf) @@ -307,8 +307,8 @@ class HerosForwardClassForNameAnalysis( Some(flowFact) } else None - val allFacts = paramFacts ++ returnFact.asInstanceOf[Option[Fact]] ++ - flowFact.asInstanceOf[Option[Fact]] + val allFacts = paramFacts ++ returnFact.asInstanceOf[Option[TaintFact]] ++ + flowFact.asInstanceOf[Option[TaintFact]] allFacts.asJava } @@ -317,7 +317,7 @@ class HerosForwardClassForNameAnalysis( override def getCallToReturnFlowFunction( stmt: JavaStatement, succ: JavaStatement - ): FlowFunction[Fact] = + ): FlowFunction[TaintFact] = Identity.v() } } @@ -325,7 +325,7 @@ class HerosForwardClassForNameAnalysis( /** * Returns true if the expression contains a taint. */ - private def isTainted(expr: Expr[V], in: Fact): Boolean = { + private def isTainted(expr: Expr[V], in: TaintFact): Boolean = { expr.isVar && (in match { case Variable(source) ⇒ expr.asVar.definedBy.contains(source) case ArrayElement(source, _) ⇒ expr.asVar.definedBy.contains(source) @@ -346,12 +346,12 @@ class HerosForwardClassForNameAnalysis( } class HerosForwardClassForNameAnalysisRunner - extends HerosAnalysisRunner[Fact, HerosForwardClassForNameAnalysis] { + extends HerosAnalysisRunner[TaintFact, HerosForwardClassForNameAnalysis] { override protected def createAnalysis(p: SomeProject): HerosForwardClassForNameAnalysis = { val declaredMethods = p.get(DeclaredMethodsKey) val propertyStore = p.get(PropertyStoreKey) - val initialMethods = scala.collection.mutable.Map.empty[Method, util.Set[Fact]] + val initialMethods = scala.collection.mutable.Map.empty[Method, util.Set[TaintFact]] for { method ← declaredMethods.declaredMethods .filter(canBeCalledFromOutside(_, propertyStore)) @@ -363,7 +363,7 @@ class HerosForwardClassForNameAnalysisRunner } { val fact = Variable(-2 - index) if (initialMethods.contains(method)) initialMethods(method).add(fact) - else initialMethods += (method -> new util.HashSet[Fact](Collections.singleton(fact))) + else initialMethods += (method -> new util.HashSet[TaintFact](Collections.singleton(fact))) } new HerosForwardClassForNameAnalysis(p, new OpalForwardICFG(p), initialMethods.toMap) } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala index d6cf56faf3..2d67edfef5 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala @@ -4,18 +4,18 @@ package org.opalj.tac.fpcf.analyses.heros.analyses.taint import org.opalj.br.analyses.SomeProject import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.cfg.OpalICFG -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, NullFact} +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, TaintNullFact} /** * A common subclass of all Heros taint analyses. * * @author Mario Trageser */ -abstract class HerosTaintAnalysis(p: SomeProject, icfg: OpalICFG) extends HerosAnalysis[Fact](p, icfg) { +abstract class HerosTaintAnalysis(p: SomeProject, icfg: OpalICFG) extends HerosAnalysis[TaintFact](p, icfg) { /** * Uses the NullFact of the TaintAnalysis. */ - override def createZeroValue(): Fact = NullFact + override def createZeroValue(): TaintFact = TaintNullFact } \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index 7df17bb5bc..b27fa8751d 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -28,7 +28,7 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb /** * The string parameters of all public methods are entry points. */ - override val entryPoints: Seq[(DeclaredMethod, Fact)] = + override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "java/lang/Class") .flatMap(classFile ⇒ classFile.methods) @@ -43,12 +43,12 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = Set.empty /** * Do not perform unbalanced return for methods, which can be called from outside the library. */ - override def shouldPerformUnbalancedReturn(source: (DeclaredMethod, Fact)): Boolean = { + override def shouldPerformUnbalancedReturn(source: (DeclaredMethod, TaintFact)): Boolean = { super.shouldPerformUnbalancedReturn(source) && (!canBeCalledFromOutside(source._1) || // The source is callable from outside, but should create unbalanced return facts. @@ -59,8 +59,8 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb * This analysis does not create FlowFacts at calls. * Instead, FlowFacts are created at the start node of methods. */ - override protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact] = None + override protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[TaintFact], + source: (DeclaredMethod, TaintFact)): Option[FlowFact] = None /** * This analysis does not create FlowFacts at returns. @@ -68,7 +68,7 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb */ protected def applyFlowFactFromCallee( calleeFact: FlowFact, - source: (DeclaredMethod, Fact) + source: (DeclaredMethod, TaintFact) ): Option[FlowFact] = None /** @@ -76,10 +76,10 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb * and a formal parameter is tainted, we create a FlowFact. */ override protected def createFlowFactAtBeginningOfMethod( - in: Set[Fact], - source: (DeclaredMethod, Fact) + in: Set[TaintFact], + source: (DeclaredMethod, TaintFact) ): Option[FlowFact] = { - if (source._2.isInstanceOf[UnbalancedReturnFact[Fact @unchecked]] && + if (source._2.isInstanceOf[UnbalancedReturnFact[TaintFact @unchecked]] && canBeCalledFromOutside(source._1) && in.exists { // index < 0 means, that it is a parameter. case Variable(index) if index < 0 ⇒ true @@ -92,14 +92,14 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb } } -object BackwardClassForNameTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact] { +object BackwardClassForNameTaintAnalysisScheduler extends IFDSAnalysisScheduler[TaintFact] { override def init(p: SomeProject, ps: PropertyStore): BackwardClassForNameTaintAnalysisScheduler = { p.get(RTACallGraphKey) new BackwardClassForNameTaintAnalysisScheduler()(p) } - override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, TaintFact] = OldTaint } class BackwardClassForNameTaintAnalysisRunner extends AbsractIFDSAnalysisRunner { diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala index 19f0a4f9b8..413d5e1314 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -7,7 +7,7 @@ import org.opalj.fpcf.PropertyStore import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.old._ -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, Variable} +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, Variable} import org.opalj.tac.fpcf.analyses.ifds._ import org.opalj.tac.fpcf.properties.OldTaint @@ -25,12 +25,12 @@ class ForwardClassForNameTaintAnalysis$Scheduler private (implicit val project: extends ForwardIFDSAnalysis(new ForwardClassForNameTaintProblem(project), OldTaint) class ForwardClassForNameTaintProblem(project: SomeProject) - extends old.taint.ForwardTaintProblem(project) with old.taint.TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, Fact] { + extends old.taint.ForwardTaintProblem(project) with old.taint.TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, TaintFact] { /** * The string parameters of all public methods are entry points. */ - override def entryPoints: Seq[(DeclaredMethod, Fact)] = for { + override def entryPoints: Seq[(DeclaredMethod, TaintFact)] = for { m ← methodsCallableFromOutside.toSeq if !m.definedMethod.isNative index ← m.descriptor.parameterTypes.zipWithIndex.collect { @@ -46,20 +46,20 @@ class ForwardClassForNameTaintProblem(project: SomeProject) /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = Set.empty /** * This analysis does not create new taints on the fly. * Instead, the string parameters of all public methods are tainted in the entry points. */ - override protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[Fact] = + override protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[TaintFact] = Set.empty /** * Create a FlowFact, if Class.forName is called with a tainted variable for the first parameter. */ override protected def createFlowFact(callee: DeclaredMethod, call: DeclaredMethodJavaStatement, - in: Set[Fact]): Option[FlowFact] = + in: Set[TaintFact]): Option[FlowFact] = if (isClassForName(callee) && in.contains(Variable(-2))) Some(FlowFact(Seq(JavaMethod(call.method)))) else None @@ -85,14 +85,14 @@ class ForwardClassForNameTaintProblem(project: SomeProject) method.declaringClassType == ObjectType.Class && method.name == "forName" } -object ForwardClassForNameTaintAnalysis$Scheduler extends IFDSAnalysisScheduler[Fact] { +object ForwardClassForNameTaintAnalysis$Scheduler extends IFDSAnalysisScheduler[TaintFact] { override def init(p: SomeProject, ps: PropertyStore): ForwardClassForNameTaintAnalysis$Scheduler = { p.get(RTACallGraphKey) new ForwardClassForNameTaintAnalysis$Scheduler()(p) } - override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, TaintFact] = OldTaint } class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { @@ -103,7 +103,7 @@ class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { for { e ← analysis.ifdsProblem.entryPoints flows = ps(e, ForwardClassForNameTaintAnalysis$Scheduler.property.key) - fact ← flows.ub.asInstanceOf[IFDSProperty[DeclaredMethodJavaStatement, Fact]].flows.values.flatten.toSet[Fact] + fact ← flows.ub.asInstanceOf[IFDSProperty[DeclaredMethodJavaStatement, TaintFact]].flows.values.flatten.toSet[TaintFact] } { fact match { case FlowFact(flow) ⇒ println(s"flow: "+flow.asInstanceOf[Set[Method]].map(_.toJava).mkString(", ")) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala index 2506e8b9f4..7c674cf6d2 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.PropertiesTest import org.opalj.fpcf.properties.taint.ForwardFlowPath import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintAnalysisFixtureScheduler -import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact import java.net.URL @@ -34,7 +34,7 @@ class ForwardTaintAnalysisFixtureTest extends PropertiesTest { val project = testContext.project val eas = methodsWithAnnotations(project).map { case (method, entityString, annotations) ⇒ - ((method, NullFact), entityString, annotations) + ((method, TaintNullFact), entityString, annotations) } testContext.propertyStore.shutdown() validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala index 4014747b21..492d15bbe7 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala @@ -7,7 +7,7 @@ import org.opalj.br.analyses.{DeclaredMethodsKey, Project} import org.opalj.fpcf.PropertiesTest import org.opalj.fpcf.properties.taint.BackwardFlowPath import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact import org.opalj.tac.fpcf.analyses.ifds.taint.old.BackwardTaintAnalysisFixtureScheduler import java.net.URL @@ -35,7 +35,7 @@ class BackwardTaintAnalysisFixtureTest extends PropertiesTest { val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { case (methods, entityString, annotations) ⇒ - ((declaredMethods(methods), NullFact), entityString, annotations) + ((declaredMethods(methods), TaintNullFact), entityString, annotations) } testContext.propertyStore.shutdown() validateProperties(testContext, eas, Set(BackwardFlowPath.PROPERTY_VALIDATOR_KEY)) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala index 735412faa1..b20f4b5545 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala @@ -7,8 +7,8 @@ import org.opalj.br.analyses.{DeclaredMethodsKey, Project} import org.opalj.fpcf.PropertiesTest import org.opalj.fpcf.properties.taint.ForwardFlowPath import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.taint.old.{ForwardTaintAnalysisFixtureScheduler} -import org.opalj.tac.fpcf.analyses.ifds.taint.NullFact +import org.opalj.tac.fpcf.analyses.ifds.taint.old.ForwardTaintAnalysisFixtureScheduler +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact import java.net.URL @@ -35,7 +35,7 @@ class ForwardTaintAnalysisFixtureTest extends PropertiesTest { val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { case (methods, entityString, annotations) ⇒ - ((declaredMethods(methods), NullFact), entityString, annotations) + ((declaredMethods(methods), TaintNullFact), entityString, annotations) } testContext.propertyStore.shutdown() validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala index 83a49616d1..968590cfc6 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala @@ -7,7 +7,7 @@ import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.properties.AbstractPropertyMatcher import org.opalj.fpcf.{EPS, Entity, FinalEP, Property} import org.opalj.tac.fpcf.analyses.ifds.taint.old.BackwardTaintAnalysisFixtureScheduler -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact} +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact} import org.opalj.tac.fpcf.properties.OldTaint /** @@ -22,7 +22,7 @@ class BackwardFlowPathMatcher extends AbstractPropertyMatcher { a: AnnotationLike, properties: Traversable[Property] ): Option[String] = { - val method = entity.asInstanceOf[(DefinedMethod, Fact)]._1.definedMethod + val method = entity.asInstanceOf[(DefinedMethod, TaintFact)]._1.definedMethod val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) ⇒ evp.value.asArrayValue.values.map((value: ElementValue) ⇒ value.asStringValue.value)).head.toIndexedSeq diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala index da993e0871..0a41e9f9b6 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala @@ -12,7 +12,7 @@ import org.opalj.ll.llvm.value.Function import org.opalj.log.GlobalLogContext import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact} +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, TaintNullFact} import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers @@ -35,14 +35,14 @@ class CrossLanguageForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matcher val (ps, analyses) = manager.runAll(JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler) for (method ← project.allMethodsWithBody) { val flows = - ps((method, NullFact), JavaForwardTaintAnalysisScheduler.property.key) + ps((method, TaintNullFact), JavaForwardTaintAnalysisScheduler.property.key) println("---METHOD: "+method.toJava+" ---") val flowFacts = flows.ub - .asInstanceOf[IFDSProperty[JavaStatement, Fact]] + .asInstanceOf[IFDSProperty[JavaStatement, TaintFact]] .flows .values .flatten - .toSet[Fact] + .toSet[TaintFact] .flatMap { case FlowFact(flow) ⇒ Some(flow) case _ ⇒ None diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala index f250f46ce3..e33548b660 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala @@ -27,7 +27,7 @@ abstract class ICFG[C <: AnyRef, S <: Statement[_ <: C, _]] { * @return All callables possibly called at the statement or None, if the statement does not * contain a call. */ - def getCalleesIfCallStatement(statement: S): Option[SomeSet[C]] + def getCalleesIfCallStatement(statement: S): Option[SomeSet[_ <: C]] /** * Determines whether the statement is an exit statement. diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index a2cb65c4b9..7ff0bd9772 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -259,7 +259,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < */ private def handleCall( call: S, - callees: SomeSet[C], + callees: SomeSet[_ <: C], in: IFDSFact )( implicit diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala index ea57c283ba..d851bc3685 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/Callable.scala @@ -1,5 +1,6 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds +import org.opalj.br.Method import org.opalj.ifds.Callable import org.opalj.ll.llvm.value.Function @@ -12,3 +13,7 @@ case class LLVMFunction(function: Function) extends NativeFunction { override def signature: String = function.name // TODO: add signature } +case class JNIMethod(method: Method) extends NativeFunction { + override def name: String = method.name + override def signature: String = method.toString +} diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala index 741692fb81..8e34a09c7d 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala @@ -1,6 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds +import org.opalj.br.analyses.DeclaredMethods import org.opalj.ll.llvm.value.constant.{ConstantDataArray, GetElementPtrConst} import org.opalj.ll.llvm.value.{Argument, Call, GetElementPtr, GlobalVariable, Load, Store, Value} import org.opalj.ll.llvm.{PointerType, StructType} @@ -29,12 +30,9 @@ object JNICallUtil { case _ ⇒ false } - def resolve(call: Call): Set[NativeFunction] = resolveJNIFunction(call) match { - case 'CallTypeMethod ⇒ { - resolveMethodId(call.operand(2)) - Set() // TODO - } // methodID is the third parameter - case _ ⇒ Set() + def resolve(call: Call)(implicit declaredMethods: DeclaredMethods): Set[_ <: NativeFunction] = resolveJNIFunction(call) match { + case 'CallTypeMethod ⇒ resolveMethodId(call.operand(2)) // methodID is the third parameter + case _ ⇒ Set() } private def resolveJNIFunction(call: Call): Symbol = call.calledValue match { @@ -52,19 +50,25 @@ object JNICallUtil { case _ ⇒ throw new IllegalArgumentException("unknown JNI call argument") } - private def resolveMethodId(methodId: Value): Unit = { + private def resolveMethodId(methodId: Value)(implicit declaredMethods: DeclaredMethods): Set[JNIMethod] = { val sources = methodId.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) - for (call ← sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call])) { + sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call]).map(call ⇒ { if (resolveJNIFunction(call) != 'GetMethodId) throw new IllegalArgumentException("unexpected call") - val name = resolveMethodName(call.operand(2)) // name is the third parameter - println(name) - if (!resolveClassIsThis(call.operand(1))) { // class is the second parameter + if (!resolveClassIsThis(call.operand(1))) // class is the second parameter throw new IllegalArgumentException("unexpected class argument") + + val functionName = call.function.name + if (!functionName.startsWith("Java_")) { + throw new IllegalArgumentException("unexpected function name") } - } + val className = call.function.name.substring(5).split("_").head + val name = resolveString(call.operand(2)) // name is the third parameter + val signature = resolveString(call.operand(3)) // signature is the third parameter + findJavaMethods(className, name, signature) + }).flatten.toSet } - private def resolveMethodName(name: Value): String = name match { + private def resolveString(name: Value): String = name match { case gep: GetElementPtrConst ⇒ gep.base match { case global: GlobalVariable ⇒ global.initializer match { case stringData: ConstantDataArray ⇒ stringData.asString @@ -84,4 +88,13 @@ object JNICallUtil { val sources = obj.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) sources.forall(_.isInstanceOf[Argument]) && sources.forall(_.asInstanceOf[Argument].index == 1) } + + private def findJavaMethods(className: String, name: String, signature: String)(implicit declaredMethods: DeclaredMethods): Set[JNIMethod] = { + declaredMethods.declaredMethods.filter(declaredMethod ⇒ { + val classType = declaredMethod.declaringClassType + (classType.simpleName == className && + declaredMethod.name == name && + declaredMethod.descriptor.toJVMDescriptor == signature) + }).map(_.definedMethod).map(JNIMethod(_)).toSet + } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index 2ffdf3b59f..a9e7ac5fc8 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -1,10 +1,13 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds +import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} import org.opalj.ifds.ICFG import org.opalj.ll.llvm.value.{Call, Function, Instruction, Ret, Terminator} -object NativeForwardICFG extends ICFG[NativeFunction, LLVMStatement] { +class NativeForwardICFG(project: SomeProject) extends ICFG[NativeFunction, LLVMStatement] { + implicit val declaredMethods = project.get(DeclaredMethodsKey) + /** * Determines the statements at which the analysis starts. * @@ -37,7 +40,7 @@ object NativeForwardICFG extends ICFG[NativeFunction, LLVMStatement] { * @return All callables possibly called at the statement or None, if the statement does not * contain a call. */ - override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[NativeFunction]] = { + override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[_ <: NativeFunction]] = { statement.instruction match { case call: Call ⇒ Some(resolveCallee(call)) case _ ⇒ None @@ -49,7 +52,7 @@ object NativeForwardICFG extends ICFG[NativeFunction, LLVMStatement] { case _ ⇒ false } - private def resolveCallee(call: Call): Set[NativeFunction] = + private def resolveCallee(call: Call): Set[_ <: NativeFunction] = if (call.calledValue.isInstanceOf[Function]) Set(LLVMFunction(call.calledValue.asInstanceOf[Function])) else if (JNICallUtil.isJNICall(call)) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index ba2702ad33..98f7a156cb 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -1,16 +1,19 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds +import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.{EOptionP, FinalEP, InterimEUBP, Property, PropertyKey, PropertyStore} import org.opalj.ifds.Dependees.Getter -import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem} +import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, IFDSProperty} import org.opalj.ll.LLVMProjectKey +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, NativeFunction, LLVMStatement](NativeForwardICFG) { +abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact, JavaFact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, NativeFunction, LLVMStatement](new NativeForwardICFG(project)) { final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) val llvmProject = project.get(LLVMProjectKey) + val javaPropertyKey: PropertyKey[Property] override def outsideAnalysisContext(callee: NativeFunction): Option[(LLVMStatement, LLVMStatement, Fact, Getter) ⇒ Set[Fact]] = callee match { case LLVMFunction(function) ⇒ @@ -18,5 +21,54 @@ abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) case 0 ⇒ Some((_: LLVMStatement, _: LLVMStatement, in: Fact, _: Getter) ⇒ Set(in)) case _ ⇒ None } + case JNIMethod(method) ⇒ Some(handleJavaMethod(method)) } + + private def handleJavaMethod(callee: Method)(call: LLVMStatement, successor: LLVMStatement, in: Fact, dependeesGetter: Getter): Set[Fact] = { + var result = Set.empty[Fact] + val entryFacts = javaCallFlow(call, callee, in) + for (entryFact ← entryFacts) { // ifds line 14 + val e = (callee, entryFact) + val exitFacts: Map[JavaStatement, Set[JavaFact]] = + dependeesGetter(e, javaPropertyKey).asInstanceOf[EOptionP[(JavaStatement, JavaFact), IFDSProperty[JavaStatement, JavaFact]]] match { + case ep: FinalEP[_, IFDSProperty[JavaStatement, JavaFact]] ⇒ + ep.p.flows + case ep: InterimEUBP[_, IFDSProperty[JavaStatement, JavaFact]] ⇒ + ep.ub.flows + case _ ⇒ + Map.empty + } + for { + (exitStatement, exitStatementFacts) ← exitFacts // ifds line 15.2 + exitStatementFact ← exitStatementFacts // ifds line 15.3 + } { + result ++= javaReturnFlow(exitStatement, exitStatementFact, call, in, successor) + } + } + result + } + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + protected def javaCallFlow( + call: LLVMStatement, + callee: Method, + in: Fact + ): Set[JavaFact] + + protected def javaReturnFlow( + exit: JavaStatement, + in: JavaFact, + call: LLVMStatement, + callFact: Fact, + successor: LLVMStatement + ): Set[Fact] } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index 31f6466230..ba739ace03 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -21,9 +21,9 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(Method, Fact)] = for { + override val entryPoints: Seq[(Method, TaintFact)] = for { m ← p.allMethodsWithBody - } yield m -> NullFact + } yield m -> TaintNullFact /** * The sanitize method is a sanitizer. @@ -34,12 +34,12 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( /** * We do not sanitize parameters. */ - override protected def sanitizesParameter(call: JavaStatement, in: Fact): Boolean = false + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false /** * Creates a new variable fact for the callee, if the source was called. */ - override protected def createTaints(callee: Method, call: JavaStatement): Set[Fact] = + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = if (callee.name == "source") Set(Variable(call.index)) else Set.empty @@ -50,7 +50,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( override protected def createFlowFact( callee: Method, call: JavaStatement, - in: Fact + in: TaintFact ): Option[FlowFact] = if (callee.name == "sink" && in == Variable(-2)) Some(FlowFact(Seq(JavaMethod(call.method), JavaMethod(callee)))) @@ -58,7 +58,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( // Multilingual additions here override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { - def handleNativeMethod(call: JavaStatement, successor: JavaStatement, in: Fact, dependeesGetter: Getter): Set[Fact] = { + def handleNativeMethod(call: JavaStatement, successor: JavaStatement, in: TaintFact, dependeesGetter: Getter): Set[TaintFact] = { // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names val calleeName = callee.name.map(c ⇒ c match { case c if isAlphaNumeric(c) ⇒ c @@ -69,7 +69,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( }).mkString val nativeFunctionName = "Java_"+callee.classFile.fqn+"_"+calleeName val function = LLVMFunction(llvmProject.function(nativeFunctionName).get) - var result = Set.empty[Fact] + var result = Set.empty[TaintFact] val entryFacts = nativeCallFlow(call, function, in, callee) for (entryFact ← entryFacts) { // ifds line 14 val e = (function, entryFact) @@ -112,7 +112,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( private def nativeCallFlow( call: JavaStatement, callee: LLVMFunction, - in: Fact, + in: TaintFact, nativeCallee: Method ): Set[NativeTaintFact] = { val callObject = asCall(call.stmt) @@ -147,7 +147,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( case StaticField(classType, fieldName) ⇒ Set(JavaStaticField(classType, fieldName)) - case NullFact ⇒ Set(NativeTaintNullFact) + case TaintNullFact ⇒ Set(NativeTaintNullFact) case _ ⇒ Set() // Nothing to do @@ -168,10 +168,10 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( exit: LLVMStatement, in: NativeTaintFact, call: JavaStatement, - callFact: Fact, + callFact: TaintFact, nativeCallee: Method, successor: JavaStatement - ): Set[Fact] = { + ): Set[TaintFact] = { /** * Checks whether the callee's formal parameter is of a reference type. */ @@ -188,7 +188,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( if (sanitizesReturnValue(nativeCallee)) return Set.empty val callStatement = asCall(call.stmt) val allParams = callStatement.allParams - var flows: Set[Fact] = Set.empty + var flows: Set[TaintFact] = Set.empty in match { // Taint actual parameter if formal parameter is tainted case JavaVariable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ @@ -217,7 +217,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( // Track the call chain to the sink back case NativeFlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ flows += FlowFact(JavaMethod(call.method) +: flow) - case NativeTaintNullFact ⇒ flows += NullFact + case NativeTaintNullFact ⇒ flows += TaintNullFact case _ ⇒ } @@ -251,9 +251,9 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( class SimpleJavaForwardTaintAnalysis(project: SomeProject) extends IFDSAnalysis()(project, new SimpleJavaForwardTaintProblem(project), Taint) -object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[Fact, Method, JavaStatement] { +object JavaForwardTaintAnalysisScheduler extends IFDSAnalysisScheduler[TaintFact, Method, JavaStatement] { override def init(p: SomeProject, ps: PropertyStore) = new SimpleJavaForwardTaintAnalysis(p) - override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint override def requiredProjectInformation: ProjectInformationKeys = Seq(LLVMProjectKey) override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.ub(NativeTaint)) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala index 6ea41fd6f3..307a2b762a 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -2,17 +2,19 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject -import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.fpcf.{PropertyBounds, PropertyKey, PropertyStore} import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeFunction, NativeIFDSAnalysis, NativeIFDSAnalysisScheduler} import org.opalj.ll.fpcf.properties.NativeTaint import org.opalj.ll.llvm.value.Function +import org.opalj.tac.fpcf.properties.Taint class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaintProblem(p) { /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ override val entryPoints: Seq[(NativeFunction, NativeTaintFact)] = Seq.empty + override val javaPropertyKey: PropertyKey[Taint] = Taint.key /** * The sanitize method is a sanitizer. diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 730891a66d..630e7a670e 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -1,12 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ll.fpcf.analyses.ifds.taint +import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeFunction, NativeIFDSProblem} import org.opalj.ll.llvm.value.{Add, Alloca, BitCast, Call, GetElementPtr, Load, PHI, Ret, Store, Sub} -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, TaintProblem} -abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeTaintFact](project) with TaintProblem[LLVMFunction, LLVMStatement, NativeTaintFact] { +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeTaintFact, TaintFact](project) with TaintProblem[LLVMFunction, LLVMStatement, NativeTaintFact] { override def nullFact: NativeTaintFact = NativeTaintNullFact /** @@ -135,4 +137,151 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD case PHI(_) ⇒ true case _ ⇒ false } + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + override protected def javaCallFlow( + call: LLVMStatement, + callee: Method, + in: NativeTaintFact + ): Set[TaintFact] = { + /*val callObject = asCall(call.stmt) + val allParams = callObject.allParams + val allParamsWithIndices = allParams.zipWithIndex + in match { + // Taint formal parameter if actual parameter is tainted + case Variable(index) ⇒ + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + // TODO: this is passed + Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv + case _ ⇒ None // Nothing to do + }.toSet + + // Taint element of formal parameter if element of actual parameter is tainted + case ArrayElement(index, taintedIndex) ⇒ + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv + case _ ⇒ None // Nothing to do + }.toSet + + case InstanceField(index, declaredClass, taintedField) ⇒ + // Taint field of formal parameter if field of actual parameter is tainted + // Only if the formal parameter is of a type that may have that field! + allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + Some(JavaInstanceField(paramIndex + 1, declaredClass, taintedField)) // TODO subtype check + case _ ⇒ None // Nothing to do + }.toSet + + case StaticField(classType, fieldName) ⇒ Set(JavaStaticField(classType, fieldName)) + + case NullFact ⇒ Set(NativeTaintNullFact) + + case _ ⇒ Set() // Nothing to do + + }*/ + Set() // TODO + } + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + override protected def javaReturnFlow( + exit: JavaStatement, + in: TaintFact, + call: LLVMStatement, + callFact: NativeTaintFact, + successor: LLVMStatement + ): Set[NativeTaintFact] = { + /* + val callee = exit.callable() + /** + * Checks whether the callee's formal parameter is of a reference type. + */ + def isRefTypeParam(index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (callee.isStatic) 0 else 1 + callee.descriptor.parameterType( + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) + - parameterOffset + ).isReferenceType + } + + if (sanitizesReturnValue(callee)) return Set.empty + val callStatement = asCall(call.stmt) + val allParams = callStatement.allParams + var flows: Set[Fact] = Set.empty + in match { + // Taint actual parameter if formal parameter is tainted + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(Variable) + + // Taint element of actual parameter if element of formal parameter is tainted + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) + + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + // Taint field of actual parameter if field of formal parameter is tainted + val param = + allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic)) + param.asVar.definedBy.foreach { defSite ⇒ + flows += InstanceField(defSite, declClass, taintedField) + } + + case sf: StaticField ⇒ flows += sf + + // Track the call chain to the sink back + case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ + flows += FlowFact(JavaMethod(call.method) +: flow) + case _ ⇒ + } + + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + in match { + case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + flows += Variable(call.index) + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + flows += ArrayElement(call.index, taintedIndex) + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + flows += InstanceField(call.index, declClass, taintedField) + case NullFact ⇒ + val taints = createTaints(callee, call) + if (taints.nonEmpty) flows ++= taints + case _ ⇒ // Nothing to do + } + } + val flowFact = createFlowFact(callee, call, in) + if (flowFact.isDefined) flows += flowFact.get + + flows + + */ + Set() // TODO + } } \ No newline at end of file diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala index d5e157c877..b743857069 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala @@ -2,12 +2,12 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.ObjectType -import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} +import org.opalj.ifds.{AbstractIFDSNullFact, Callable, AbstractIFDSFact} import org.opalj.ll.llvm.value.Value trait NativeTaintFact extends AbstractIFDSFact -case object NativeTaintNullFact extends NativeTaintFact with AbstractIFDSNullFact +object NativeTaintNullFact extends NativeTaintFact with AbstractIFDSNullFact /** * A tainted variable. diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala index d8adce7738..91f6ba4551 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/properties/Taint.scala @@ -22,4 +22,4 @@ object NativeTaint extends IFDSPropertyMetaInformation[LLVMStatement, NativeTain override def create(result: Map[LLVMStatement, Set[NativeTaintFact]], debugData: Map[LLVMStatement, Set[NativeTaintFact]]): IFDSProperty[LLVMStatement, NativeTaintFact] = new NativeTaint(result, debugData) val key: PropertyKey[NativeTaint] = PropertyKey.create("NativeTaint", new NativeTaint(Map.empty)) -} \ No newline at end of file +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala index 6468a299b8..045fa8ad87 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala @@ -9,14 +9,14 @@ import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem ⇒ NewJavaIFDSProblem} import org.opalj.tac.fpcf.analyses.ifds.old.{JavaBackwardIFDSProblem, DeclaredMethodJavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint._ -abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[Fact, UnbalancedTaintFact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, Fact] { - override def nullFact: Fact = NullFact +abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[TaintFact, UnbalancedTaintFact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, TaintFact] { + override def nullFact: TaintFact = TaintNullFact /** * If a tainted variable gets assigned a value, this value will be tainted. */ override def normalFlow(statement: DeclaredMethodJavaStatement, successor: Option[DeclaredMethodJavaStatement], - in: Set[Fact]): Set[Fact] = { + in: Set[TaintFact]): Set[TaintFact] = { val stmt = statement.stmt stmt.astID match { case Assignment.ASTID if isTainted(statement.index, in) ⇒ @@ -65,7 +65,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * Does not taint anything, if the sanitize method was called. */ override def callFlow(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, - in: Set[Fact], source: (DeclaredMethod, Fact)): Set[Fact] = + in: Set[TaintFact], source: (DeclaredMethod, TaintFact)): Set[TaintFact] = if (sanitizesReturnValue(callee)) Set.empty else taintActualsIfFormalsTainted(callee, call, in, source) @@ -75,10 +75,10 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * tainted, the formal parameter in the callee context will be tainted. */ override def returnFlow(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, exit: DeclaredMethodJavaStatement, - successor: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = { + successor: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = { val callObject = asCall(call.stmt) val staticCall = callee.definedMethod.isStatic - val flow = collection.mutable.Set.empty[Fact] + val flow = collection.mutable.Set.empty[TaintFact] if (call.stmt.astID == Assignment.ASTID && exit.stmt.astID == ReturnValue.ASTID) in.foreach { case Variable(index) if index == call.index ⇒ @@ -122,8 +122,8 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * Removes taints according to `sanitizeParamters`. */ override def callToReturnFlow(call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, - in: Set[Fact], - source: (DeclaredMethod, Fact)): Set[Fact] = { + in: Set[TaintFact], + source: (DeclaredMethod, TaintFact)): Set[TaintFact] = { val flowFact = createFlowFactAtCall(call, in, source) val result = in -- sanitizeParameters(call, in) if (flowFact.isDefined) result + flowFact.get @@ -135,7 +135,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF */ override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[Fact]) ⇒ { + case Some(_) ⇒ Some((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[TaintFact]) ⇒ { val callStatement = asCall(call.stmt) in ++ in.collect { case Variable(index) if index == call.index ⇒ @@ -153,9 +153,9 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * Creates an UnbalancedTaintFact for each actual parameter on the caller side, of the formal * parameter of this method is tainted. */ - override def unbalancedReturnFlow(facts: Set[Fact], call: DeclaredMethodJavaStatement, + override def unbalancedReturnFlow(facts: Set[TaintFact], call: DeclaredMethodJavaStatement, caller: DeclaredMethod, - source: (DeclaredMethod, Fact)): Set[UnbalancedTaintFact] = + source: (DeclaredMethod, TaintFact)): Set[UnbalancedTaintFact] = taintActualsIfFormalsTainted(source._1, call, facts, source, isCallFlow = false) .map(UnbalancedTaintFact(call.index, _, currentCallChain(source))) // Avoid infinite loops. @@ -169,8 +169,8 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @param source The entity, which is analyzed. * @return Some FlowFact, if necessary. Otherwise None. */ - protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact] + protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[TaintFact], + source: (DeclaredMethod, TaintFact)): Option[FlowFact] /** * Called, when a FlowFact holds at the start node of a callee. Creates a FlowFact in the caller @@ -182,7 +182,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF */ protected def applyFlowFactFromCallee( calleeFact: FlowFact, - source: (DeclaredMethod, Fact) + source: (DeclaredMethod, TaintFact) ): Option[FlowFact] /** @@ -194,17 +194,17 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @return Some FlowFact, if necessary. Otherwise None. */ protected def createFlowFactAtBeginningOfMethod( - in: Set[Fact], - source: (DeclaredMethod, Fact) + in: Set[TaintFact], + source: (DeclaredMethod, TaintFact) ): Option[FlowFact] /** * Propagates the call to createFlowFactAtBeginningOfMethod. */ override def createFactsAtStartNode( - in: Set[Fact], - source: (DeclaredMethod, Fact) - ): Set[Fact] = { + in: Set[TaintFact], + source: (DeclaredMethod, TaintFact) + ): Set[TaintFact] = { val flowFact = createFlowFactAtBeginningOfMethod(in, source) if (flowFact.isDefined) Set(flowFact.get) else Set.empty @@ -219,7 +219,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @param source The entity, which is analyzed. * @return The current call chain. */ - protected def currentCallChain(source: (DeclaredMethod, Fact)): Seq[Method] = { + protected def currentCallChain(source: (DeclaredMethod, TaintFact)): Seq[Method] = { val method = source._1.definedMethod val sourceFact = source._2 sourceFact match { @@ -245,7 +245,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @param taintedElement If present, the taint of a specific array element will be checked. * @return True, if the variable or array element is tainted. */ - private def isTainted(index: Int, in: Set[Fact], taintedElement: Option[Int] = None): Boolean = in.exists { + private def isTainted(index: Int, in: Set[TaintFact], taintedElement: Option[Int] = None): Boolean = in.exists { case Variable(variableIndex) ⇒ variableIndex == index case ArrayElement(variableIndex, element) ⇒ variableIndex == index && (taintedElement.isEmpty || taintedElement.get == element) @@ -259,7 +259,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @param statement The statement, which contains the expression. * @return The new taints. */ - private def createNewTaints(expression: Expr[V], statement: DeclaredMethodJavaStatement): Set[Fact] = + private def createNewTaints(expression: Expr[V], statement: DeclaredMethodJavaStatement): Set[TaintFact] = expression.astID match { case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) case ArrayLoad.ASTID ⇒ @@ -270,7 +270,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF else arrayDefinedBy.map(Variable) case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) ⇒ acc ++ createNewTaints(expression.subExpr(subExpr), statement)) case GetField.ASTID ⇒ val getField = expression.asGetField @@ -291,7 +291,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @return An ArrayElement fact for the expression and the tainted element. */ private def createNewArrayElementTaints(expression: Expr[V], taintedElement: Int, - statement: DeclaredMethodJavaStatement): Set[Fact] = + statement: DeclaredMethodJavaStatement): Set[TaintFact] = createNewTaints(expression, statement).map { case Variable(variableIndex) ⇒ ArrayElement(variableIndex, taintedElement) // We do not nest taints. Instead, we taint the whole inner array. @@ -300,7 +300,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF } private def createNewInstanceFieldTaints(expression: Expr[V], declaringClass: ObjectType, - name: String, statement: DeclaredMethodJavaStatement): Set[Fact] = + name: String, statement: DeclaredMethodJavaStatement): Set[TaintFact] = createNewTaints(expression, statement).map { case Variable(variableIndex) ⇒ InstanceField(variableIndex, declaringClass, name) // We do not nest taints. Instead, taint the whole field. @@ -325,17 +325,17 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF private def taintActualsIfFormalsTainted( callee: DeclaredMethod, call: DeclaredMethodJavaStatement, - calleeFacts: Set[Fact], - source: (DeclaredMethod, Fact), + calleeFacts: Set[TaintFact], + source: (DeclaredMethod, TaintFact), isCallFlow: Boolean = true - ): Set[Fact] = { + ): Set[TaintFact] = { val stmt = call.stmt val callStatement = asCall(stmt) val staticCall = callee.definedMethod.isStatic val thisOffset = if (staticCall) 0 else 1 val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) .map(index ⇒ NewJavaIFDSProblem.switchParamAndVariableIndex(index + thisOffset, staticCall)) - val facts = scala.collection.mutable.Set.empty[Fact] + val facts = scala.collection.mutable.Set.empty[TaintFact] calleeFacts.foreach { case Variable(index) if formalParameterIndices.contains(index) ⇒ facts ++= createNewTaints( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala index caa998a1e1..35a848b379 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala @@ -10,14 +10,14 @@ import org.opalj.tac.fpcf.analyses.ifds.old.{JavaIFDSProblem, DeclaredMethodJava import org.opalj.tac.fpcf.analyses.ifds.taint._ import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem ⇒ NewJavaIFDSProblem} -abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[Fact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, Fact] { - override def nullFact: Fact = NullFact +abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[TaintFact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, TaintFact] { + override def nullFact: TaintFact = TaintNullFact /** * If a variable gets assigned a tainted value, the variable will be tainted. */ override def normalFlow(statement: DeclaredMethodJavaStatement, successor: Option[DeclaredMethodJavaStatement], - in: Set[Fact]): Set[Fact] = + in: Set[TaintFact]): Set[TaintFact] = statement.stmt.astID match { case Assignment.ASTID ⇒ in ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) @@ -64,10 +64,10 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * edges will be created. */ override def callFlow(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, - in: Set[Fact], a: (DeclaredMethod, Fact)): Set[Fact] = { + in: Set[TaintFact], a: (DeclaredMethod, TaintFact)): Set[TaintFact] = { val callObject = asCall(call.stmt) val allParams = callObject.allParams - var facts = Set.empty[Fact] + var facts = Set.empty[TaintFact] if (relevantCallee(callee)) { val allParamsWithIndices = allParams.zipWithIndex @@ -127,7 +127,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * If the sanitize method was called, nothing will be tainted. */ override def returnFlow(call: DeclaredMethodJavaStatement, callee: DeclaredMethod, exit: DeclaredMethodJavaStatement, - successor: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = { + successor: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = { /** * Checks whether the callee's formal parameter is of a reference type. @@ -145,7 +145,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem if (sanitizesReturnValue(callee)) return Set.empty val callStatement = asCall(call.stmt) val allParams = callStatement.allParams - var flows: Set[Fact] = Set.empty + var flows: Set[TaintFact] = Set.empty in.foreach { // Taint actual parameter if formal parameter is tainted case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ @@ -187,7 +187,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem flows += ArrayElement(call.index, taintedIndex) case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ flows += InstanceField(call.index, declClass, taintedField) - case NullFact ⇒ + case TaintNullFact ⇒ val taints = createTaints(callee, call) if (taints.nonEmpty) flows ++= taints case _ ⇒ // Nothing to do @@ -203,8 +203,8 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * Removes taints according to `sanitizeParamters`. */ override def callToReturnFlow(call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, - in: Set[Fact], - source: (DeclaredMethod, Fact)): Set[Fact] = + in: Set[TaintFact], + source: (DeclaredMethod, TaintFact)): Set[TaintFact] = in -- sanitizeParameters(call, in) /** @@ -216,7 +216,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @param call The call. * @return Some variable fact, if necessary. Otherwise none. */ - protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[Fact] + protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[TaintFact] /** * Called, when the call to return facts are computed for some `callee`. @@ -227,7 +227,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @return Some FlowFact, if necessary. Otherwise None. */ protected def createFlowFact(callee: DeclaredMethod, call: DeclaredMethodJavaStatement, - in: Set[Fact]): Option[FlowFact] + in: Set[TaintFact]): Option[FlowFact] /** * If a parameter is tainted, the result will also be tainted. @@ -235,7 +235,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem */ override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = { super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some(((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[Fact]) ⇒ { + case Some(_) ⇒ Some(((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[TaintFact]) ⇒ { val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params if (call.stmt.astID == Assignment.ASTID && in.exists { case Variable(index) ⇒ @@ -275,7 +275,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @param in The incoming facts * @return The new facts, created by the assignment */ - private def createNewTaints(expression: Expr[V], statement: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = + private def createNewTaints(expression: Expr[V], statement: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = expression.astID match { case Var.ASTID ⇒ val definedBy = expression.asVar.definedBy @@ -319,7 +319,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem else Set.empty case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) ⇒ acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) case _ ⇒ Set.empty } @@ -331,7 +331,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem * @param in The current data flow facts. * @return True, if the expression could be tainted */ - private def isTainted(expression: Expr[V], in: Set[Fact]): Boolean = { + private def isTainted(expression: Expr[V], in: Set[TaintFact]): Boolean = { val definedBy = expression.asVar.definedBy expression.isVar && in.exists { case Variable(index) ⇒ definedBy.contains(index) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala index 0673873db8..ed8d7499ad 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala @@ -3,7 +3,7 @@ package org.opalj.tac.fpcf.analyses.ifds.old.taint import org.opalj.br.Method import org.opalj.tac.fpcf.analyses.ifds.old.UnbalancedReturnFact -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact /** * The unbalanced return fact of this analysis. @@ -12,8 +12,8 @@ import org.opalj.tac.fpcf.analyses.ifds.taint.Fact * @param innerFact The fact, which will hold in the caller context after the call. * @param callChain The current call chain from the sink. */ -case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Seq[Method]) - extends UnbalancedReturnFact[Fact] with Fact +case class UnbalancedTaintFact(index: Int, innerFact: TaintFact, callChain: Seq[Method]) + extends UnbalancedReturnFact[TaintFact] with TaintFact trait TaintProblem[C, Statement, IFDSFact] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index c489272798..8d5646e670 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -9,17 +9,17 @@ import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} abstract class ForwardTaintProblem(project: SomeProject) - extends JavaIFDSProblem[Fact](project) - with TaintProblem[Method, JavaStatement, Fact] { + extends JavaIFDSProblem[TaintFact](project) + with TaintProblem[Method, JavaStatement, TaintFact] { val declaredMethods = project.get(DeclaredMethodsKey) - override def nullFact: Fact = NullFact + override def nullFact: TaintFact = TaintNullFact override def needsPredecessor(statement: JavaStatement): Boolean = false /** * If a variable gets assigned a tainted value, the variable will be tainted. */ - override def normalFlow(statement: JavaStatement, in: Fact, predecessor: Option[JavaStatement]): Set[Fact] = { + override def normalFlow(statement: JavaStatement, in: TaintFact, predecessor: Option[JavaStatement]): Set[TaintFact] = { statement.stmt.astID match { case Assignment.ASTID ⇒ Set(in) ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) @@ -60,7 +60,7 @@ abstract class ForwardTaintProblem(project: SomeProject) * parameter is detected, no call-to-start * edges will be created. */ - override def callFlow(call: JavaStatement, callee: Method, in: Fact): Set[Fact] = { + override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { val callObject = asCall(call.stmt) val allParams = callObject.allParams @@ -118,7 +118,7 @@ abstract class ForwardTaintProblem(project: SomeProject) * Creates new taints and FlowFacts, if necessary. * If the sanitize method was called, nothing will be tainted. */ - override def returnFlow(exit: JavaStatement, in: Fact, call: JavaStatement, callFact: Fact, successor: JavaStatement): Set[Fact] = { + override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { if (!isPossibleReturnFlow(exit, successor)) return Set.empty val callee = exit.callable() @@ -138,7 +138,7 @@ abstract class ForwardTaintProblem(project: SomeProject) if (sanitizesReturnValue(callee)) return Set.empty val callStatement = asCall(call.stmt) val allParams = callStatement.allParams - var flows: Set[Fact] = Set.empty + var flows: Set[TaintFact] = Set.empty in match { // Taint actual parameter if formal parameter is tainted case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ @@ -180,7 +180,7 @@ abstract class ForwardTaintProblem(project: SomeProject) flows += ArrayElement(call.index, taintedIndex) case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ flows += InstanceField(call.index, declClass, taintedField) - case NullFact ⇒ + case TaintNullFact ⇒ val taints = createTaints(callee, call) if (taints.nonEmpty) flows ++= taints case _ ⇒ // Nothing to do @@ -195,7 +195,7 @@ abstract class ForwardTaintProblem(project: SomeProject) /** * Removes taints according to `sanitizesParameter`. */ - override def callToReturnFlow(call: JavaStatement, in: Fact, successor: JavaStatement): Set[Fact] = + override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = if (sanitizesParameter(call, in)) Set() else Set(in) /** @@ -207,7 +207,7 @@ abstract class ForwardTaintProblem(project: SomeProject) * @param call The call. * @return Some variable fact, if necessary. Otherwise none. */ - protected def createTaints(callee: Method, call: JavaStatement): Set[Fact] + protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] /** * Called, when the call to return facts are computed for some `callee`. @@ -218,7 +218,7 @@ abstract class ForwardTaintProblem(project: SomeProject) * @return Some FlowFact, if necessary. Otherwise None. */ protected def createFlowFact(callee: Method, call: JavaStatement, - in: Fact): Option[FlowFact] + in: TaintFact): Option[FlowFact] /** * If a parameter is tainted, the result will also be tainted. @@ -226,7 +226,7 @@ abstract class ForwardTaintProblem(project: SomeProject) */ override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some(((call: JavaStatement, successor: JavaStatement, in: Fact, _: Getter) ⇒ { + case Some(_) ⇒ Some(((call: JavaStatement, successor: JavaStatement, in: TaintFact, _: Getter) ⇒ { val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params if (call.stmt.astID == Assignment.ASTID && (in match { case Variable(index) ⇒ @@ -257,7 +257,7 @@ abstract class ForwardTaintProblem(project: SomeProject) * @param in The incoming facts * @return The new facts, created by the assignment */ - private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: Fact): Set[Fact] = { + private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: TaintFact): Set[TaintFact] = { expression.astID match { case Var.ASTID ⇒ val definedBy = expression.asVar.definedBy @@ -302,7 +302,7 @@ abstract class ForwardTaintProblem(project: SomeProject) else Set.empty case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[Fact])((acc, subExpr) ⇒ + (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) ⇒ acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) case _ ⇒ Set.empty } @@ -315,7 +315,7 @@ abstract class ForwardTaintProblem(project: SomeProject) * @param in The current data flow facts. * @return True, if the expression could be tainted */ - private def isTainted(expression: Expr[V], in: Fact): Boolean = { + private def isTainted(expression: Expr[V], in: TaintFact): Boolean = { val definedBy = expression.asVar.definedBy expression.isVar && (in match { case Variable(index) ⇒ definedBy.contains(index) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index b53b270570..c16314b9ba 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -6,16 +6,16 @@ import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} import org.opalj.tac.{Assignment, Expr, Stmt} import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -trait Fact extends AbstractIFDSFact +trait TaintFact extends AbstractIFDSFact -case object NullFact extends Fact with AbstractIFDSNullFact +case object TaintNullFact extends TaintFact with AbstractIFDSNullFact /** * A tainted variable. * * @param index The variable's definition site. */ -case class Variable(index: Int) extends Fact +case class Variable(index: Int) extends TaintFact /** * A tainted array element. @@ -23,7 +23,7 @@ case class Variable(index: Int) extends Fact * @param index The array's definition site. * @param element The index of the tainted element in the array. */ -case class ArrayElement(index: Int, element: Int) extends Fact +case class ArrayElement(index: Int, element: Int) extends TaintFact /** * A tainted static field. @@ -31,7 +31,7 @@ case class ArrayElement(index: Int, element: Int) extends Fact * @param classType The field's class. * @param fieldName The field's name. */ -case class StaticField(classType: ObjectType, fieldName: String) extends Fact +case class StaticField(classType: ObjectType, fieldName: String) extends TaintFact /** * A tainted instance field. @@ -40,7 +40,7 @@ case class StaticField(classType: ObjectType, fieldName: String) extends Fact * @param classType The field's type. * @param fieldName The field's value. */ -case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends Fact +case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends TaintFact /** * A path of method calls, originating from the analyzed method, over which a tainted variable @@ -48,7 +48,7 @@ case class InstanceField(index: Int, classType: ObjectType, fieldName: String) e * * @param flow A sequence of method calls, originating from but not including this method. */ -case class FlowFact(flow: Seq[Callable]) extends Fact { +case class FlowFact(flow: Seq[Callable]) extends TaintFact { override val hashCode: Int = { var r = 1 flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala index b70fa37316..2c7fca2faa 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala @@ -4,22 +4,22 @@ package org.opalj.tac.fpcf.properties import org.opalj.fpcf.PropertyKey import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.analyses.ifds.old.DeclaredMethodJavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact -case class OldTaint(flows: Map[DeclaredMethodJavaStatement, Set[Fact]], debugData: Map[DeclaredMethodJavaStatement, Set[Fact]] = Map.empty) extends IFDSProperty[DeclaredMethodJavaStatement, Fact] { +case class OldTaint(flows: Map[DeclaredMethodJavaStatement, Set[TaintFact]], debugData: Map[DeclaredMethodJavaStatement, Set[TaintFact]] = Map.empty) extends IFDSProperty[DeclaredMethodJavaStatement, TaintFact] { override type Self = OldTaint - override def create(result: Map[DeclaredMethodJavaStatement, Set[Fact]]): IFDSProperty[DeclaredMethodJavaStatement, Fact] = new OldTaint(result) - override def create(result: Map[DeclaredMethodJavaStatement, Set[Fact]], debugData: Map[DeclaredMethodJavaStatement, Set[Fact]]): IFDSProperty[DeclaredMethodJavaStatement, Fact] = new OldTaint(result, debugData) + override def create(result: Map[DeclaredMethodJavaStatement, Set[TaintFact]]): IFDSProperty[DeclaredMethodJavaStatement, TaintFact] = new OldTaint(result) + override def create(result: Map[DeclaredMethodJavaStatement, Set[TaintFact]], debugData: Map[DeclaredMethodJavaStatement, Set[TaintFact]]): IFDSProperty[DeclaredMethodJavaStatement, TaintFact] = new OldTaint(result, debugData) override def key: PropertyKey[OldTaint] = OldTaint.key } -object OldTaint extends IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] { +object OldTaint extends IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, TaintFact] { override type Self = OldTaint - override def create(result: Map[DeclaredMethodJavaStatement, Set[Fact]]): IFDSProperty[DeclaredMethodJavaStatement, Fact] = new OldTaint(result) - override def create(result: Map[DeclaredMethodJavaStatement, Set[Fact]], debugData: Map[DeclaredMethodJavaStatement, Set[Fact]]): IFDSProperty[DeclaredMethodJavaStatement, Fact] = new OldTaint(result, debugData) + override def create(result: Map[DeclaredMethodJavaStatement, Set[TaintFact]]): IFDSProperty[DeclaredMethodJavaStatement, TaintFact] = new OldTaint(result) + override def create(result: Map[DeclaredMethodJavaStatement, Set[TaintFact]], debugData: Map[DeclaredMethodJavaStatement, Set[TaintFact]]): IFDSProperty[DeclaredMethodJavaStatement, TaintFact] = new OldTaint(result, debugData) val key: PropertyKey[OldTaint] = PropertyKey.create("OldTaint", new OldTaint(Map.empty)) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala index ebda1e6377..6ac49d70eb 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala @@ -4,22 +4,22 @@ package org.opalj.tac.fpcf.properties import org.opalj.fpcf.PropertyKey import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.Fact +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact -case class Taint(flows: Map[JavaStatement, Set[Fact]], debugData: Map[JavaStatement, Set[Fact]] = Map.empty) extends IFDSProperty[JavaStatement, Fact] { +case class Taint(flows: Map[JavaStatement, Set[TaintFact]], debugData: Map[JavaStatement, Set[TaintFact]] = Map.empty) extends IFDSProperty[JavaStatement, TaintFact] { override type Self = Taint - override def create(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[JavaStatement, Fact] = new Taint(result) - override def create(result: Map[JavaStatement, Set[Fact]], debugData: Map[JavaStatement, Set[Fact]]): IFDSProperty[JavaStatement, Fact] = new Taint(result, debugData) + override def create(result: Map[JavaStatement, Set[TaintFact]]): IFDSProperty[JavaStatement, TaintFact] = new Taint(result) + override def create(result: Map[JavaStatement, Set[TaintFact]], debugData: Map[JavaStatement, Set[TaintFact]]): IFDSProperty[JavaStatement, TaintFact] = new Taint(result, debugData) override def key: PropertyKey[Taint] = Taint.key } -object Taint extends IFDSPropertyMetaInformation[JavaStatement, Fact] { +object Taint extends IFDSPropertyMetaInformation[JavaStatement, TaintFact] { override type Self = Taint - override def create(result: Map[JavaStatement, Set[Fact]]): IFDSProperty[JavaStatement, Fact] = new Taint(result) - override def create(result: Map[JavaStatement, Set[Fact]], debugData: Map[JavaStatement, Set[Fact]]): IFDSProperty[JavaStatement, Fact] = new Taint(result, debugData) + override def create(result: Map[JavaStatement, Set[TaintFact]]): IFDSProperty[JavaStatement, TaintFact] = new Taint(result) + override def create(result: Map[JavaStatement, Set[TaintFact]], debugData: Map[JavaStatement, Set[TaintFact]]): IFDSProperty[JavaStatement, TaintFact] = new Taint(result, debugData) val key: PropertyKey[Taint] = PropertyKey.create("Taint", new Taint(Map.empty)) } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala index 8bc3659c3c..ac6e2c566b 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -23,11 +23,11 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(Method, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ + override val entryPoints: Seq[(Method, TaintFact)] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") .flatMap(classFile ⇒ classFile.methods) .filter(method ⇒ method.isPublic && outsideAnalysisContext(method).isEmpty) - .map(method ⇒ method → NullFact) + .map(method ⇒ method → TaintNullFact) /** * The sanitize method is a sanitizer. @@ -37,12 +37,12 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) /** * We do not sanitize paramters. */ - override protected def sanitizesParameter(call: JavaStatement, in: Fact): Boolean = false + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false /** * Creates a new variable fact for the callee, if the source was called. */ - override protected def createTaints(callee: Method, call: JavaStatement): Set[Fact] = + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = if (callee.name == "source") Set(Variable(call.index)) else Set.empty @@ -51,14 +51,14 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) * Note, that sink does not accept array parameters. No need to handle them. */ override protected def createFlowFact(callee: Method, call: JavaStatement, - in: Fact): Option[FlowFact] = + in: TaintFact): Option[FlowFact] = if (callee.name == "sink" && in == Variable(-2)) Some(FlowFact(Seq(JavaMethod(call.method)))) else None } -object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact, Method, JavaStatement] { +object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[TaintFact, Method, JavaStatement] { override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture(p) - override def property: IFDSPropertyMetaInformation[JavaStatement, Fact] = Taint + override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(Taint)) override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala index 7da6c67073..18ab076ed6 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala @@ -8,12 +8,12 @@ import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ifds._ import org.opalj.tac.fpcf.analyses.ifds.old._ import org.opalj.tac.fpcf.analyses.ifds.old.taint.BackwardTaintProblem -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, Variable} +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, Variable} import org.opalj.tac.fpcf.properties.OldTaint import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem -case class UnbalancedTaintFact(index: Int, innerFact: Fact, callChain: Array[Method]) - extends UnbalancedReturnFact[Fact] with Fact +case class UnbalancedTaintFact(index: Int, innerFact: TaintFact, callChain: Array[Method]) + extends UnbalancedReturnFact[TaintFact] with TaintFact /** * An analysis that checks, if the return value of a `source` method can flow to the parameter of a @@ -26,7 +26,7 @@ class BackwardTaintAnalysisFixture(implicit val project: SomeProject) class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p) { - override val entryPoints: Seq[(DeclaredMethod, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ + override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") .flatMap(_.methods).filter(_.name == "sink") .map(method ⇒ declaredMethods(method) → @@ -40,7 +40,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p /** * We do not sanitize paramters. */ - override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = Set.empty /** * Create a flow fact, if a source method is called and the returned value is tainted. @@ -48,8 +48,8 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p * terminates. * In this case, callFlow would never be called and no FlowFact would be created. */ - override protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[Fact], - source: (DeclaredMethod, Fact)): Option[FlowFact] = { + override protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[TaintFact], + source: (DeclaredMethod, TaintFact)): Option[FlowFact] = { if (in.exists { case Variable(index) ⇒ index == call.index case _ ⇒ false @@ -67,7 +67,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p */ override protected def applyFlowFactFromCallee( calleeFact: FlowFact, - source: (DeclaredMethod, Fact) + source: (DeclaredMethod, TaintFact) ): Option[FlowFact] = Some(FlowFact(currentCallChain(source).map(JavaMethod(_)))) @@ -76,15 +76,15 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p * Instead, FlowFacts are created, when the return value of source is tainted. */ override protected def createFlowFactAtBeginningOfMethod( - in: Set[Fact], - source: (DeclaredMethod, Fact) + in: Set[TaintFact], + source: (DeclaredMethod, TaintFact) ): Option[FlowFact] = None } -object BackwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact] { +object BackwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[TaintFact] { override def init(p: SomeProject, ps: PropertyStore) = new BackwardTaintAnalysisFixture()(p) - override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, TaintFact] = OldTaint } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala index 5674798082..342ca917d7 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala @@ -8,7 +8,7 @@ import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ifds.JavaMethod import org.opalj.tac.fpcf.analyses.ifds.old.{DeclaredMethodJavaStatement, ForwardIFDSAnalysis, IFDSAnalysisScheduler} import org.opalj.tac.fpcf.analyses.ifds.old.taint.ForwardTaintProblem -import org.opalj.tac.fpcf.analyses.ifds.taint.{Fact, FlowFact, NullFact, Variable} +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, TaintNullFact, Variable} import org.opalj.tac.fpcf.properties.OldTaint /** @@ -25,11 +25,11 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(DeclaredMethod, Fact)] = p.allProjectClassFiles.filter(classFile ⇒ + override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = p.allProjectClassFiles.filter(classFile ⇒ classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") .flatMap(classFile ⇒ classFile.methods) .filter(method ⇒ method.isPublic && outsideAnalysisContext(declaredMethods(method)).isEmpty) - .map(method ⇒ declaredMethods(method) → NullFact) + .map(method ⇒ declaredMethods(method) → TaintNullFact) /** * The sanitize method is a sanitizer. @@ -39,12 +39,12 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) /** * We do not sanitize paramters. */ - override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[Fact]): Set[Fact] = Set.empty + override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = Set.empty /** * Creates a new variable fact for the callee, if the source was called. */ - override protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[Fact] = + override protected def createTaints(callee: DeclaredMethod, call: DeclaredMethodJavaStatement): Set[TaintFact] = if (callee.name == "source") Set(Variable(call.index)) else Set.empty @@ -53,14 +53,14 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) * Note, that sink does not accept array parameters. No need to handle them. */ override protected def createFlowFact(callee: DeclaredMethod, call: DeclaredMethodJavaStatement, - in: Set[Fact]): Option[FlowFact] = + in: Set[TaintFact]): Option[FlowFact] = if (callee.name == "sink" && in.contains(Variable(-2))) Some(FlowFact(Seq(JavaMethod(call.method)))) else None } -object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[Fact] { +object ForwardTaintAnalysisFixtureScheduler extends IFDSAnalysisScheduler[TaintFact] { override def init(p: SomeProject, ps: PropertyStore) = new ForwardTaintAnalysisFixture()(p) - override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact] = OldTaint + override def property: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, TaintFact] = OldTaint } From ad658900cb6e87e7a8d94c7ae3d8cd078e1da717 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Thu, 30 Jun 2022 19:30:05 +0200 Subject: [PATCH 52/67] Fix most errors after merge --- .../fpcf/analyses/PRATaintAnalysis.scala | 86 +++++----- .../heros/analyses/HerosAnalysis.scala | 14 +- .../analyses/HerosVariableTypeAnalysis.scala | 70 ++++---- .../analyses/heros/analyses/VTAEquality.scala | 46 +++--- .../HerosBackwardClassForNameAnalysis.scala | 120 +++++++------- .../HerosForwardClassForNameAnalysis.scala | 126 +++++++-------- .../analyses/heros/cfg/OpalBackwardICFG.scala | 10 +- .../analyses/heros/cfg/OpalForwardICFG.scala | 8 +- .../fpcf/analyses/heros/cfg/OpalICFG.scala | 40 +++-- ...rdClassForNameTaintAnalysisScheduler.scala | 32 ++-- ...rdClassForNameTaintAnalysisScheduler.scala | 20 +-- .../ForwardTaintAnalysisFixtureTest.scala | 4 +- .../BackwardTaintAnalysisFixtureTest.scala | 4 +- .../old/ForwardTaintAnalysisFixtureTest.scala | 4 +- .../org/opalj/fpcf/ifds/old/VTATest.scala | 4 +- .../taint/BackwardFlowPathMatcher.scala | 16 +- .../taint/ForwardFlowPathMatcher.scala | 10 +- .../vta/ExpectedCalleeMatcher.scala | 12 +- .../properties/vta/ExpectedTypeMatcher.scala | 6 +- .../fpcf/properties/vta/VTAMatcher.scala | 14 +- ...rossLanguageForwardTaintAnalysisTest.scala | 16 +- .../org/opalj/ll/SimplePurityTests.scala | 4 +- .../br/analyses/cg/EntryPointFinder.scala | 8 +- .../org/opalj/ifds/DataFlowAnalysis.scala | 8 +- .../src/main/scala/org/opalj/ifds/ICFG.scala | 2 +- .../src/main/scala/org/opalj/ifds/IFDS.scala | 6 +- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 82 +++++----- .../scala/org/opalj/ifds/IFDSProblem.scala | 2 +- .../scala/org/opalj/ifds/IFDSProperty.scala | 4 +- .../main/scala/org/opalj/ifds/Statement.scala | 4 +- .../main/scala/org/opalj/ifds/old/ICFG.scala | 2 +- .../org/opalj/ifds/old/IFDSAnalysis.scala | 62 ++++---- .../org/opalj/ifds/old/IFDSProblem.scala | 2 +- .../scala/org/opalj/ifds/old/Subsumable.scala | 4 +- .../scala/org/opalj/ifds/old/Subsuming.scala | 12 +- .../main/scala/org/opalj/ifds/package.scala | 2 +- .../main/scala/org/opalj/ll/LLVMProject.scala | 4 +- .../fpcf/analyses/SimplePurityAnalysis.scala | 14 +- .../ll/fpcf/analyses/ifds/JNICallUtil.scala | 52 +++--- .../analyses/ifds/NativeForwardICFG.scala | 10 +- .../analyses/ifds/NativeIFDSProblem.scala | 22 +-- .../ifds/taint/JavaForwardTaintAnalysis.scala | 80 +++++----- .../taint/NativeForwardTaintProblem.scala | 150 +++++++++--------- .../ifds/taint/NativeTaintProblem.scala | 2 +- .../main/scala/org/opalj/ll/llvm/Module.scala | 6 +- .../main/scala/org/opalj/ll/llvm/Type.scala | 46 +++--- .../scala/org/opalj/ll/llvm/package.scala | 6 +- .../org/opalj/ll/llvm/value/BasicBlock.scala | 16 +- .../org/opalj/ll/llvm/value/Instruction.scala | 144 ++++++++--------- .../scala/org/opalj/ll/llvm/value/Value.scala | 20 +-- .../value/constant/ConstantExpression.scala | 4 +- .../src/main/scala/org/opalj/ll/package.scala | 2 +- .../tac/fpcf/analyses/ifds/ForwardICFG.scala | 32 ++-- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 20 +-- .../ifds/old/AbstractIFDSAnalysis.scala | 128 +++++++-------- .../ifds/old/BackwardIFDSAnalysis.scala | 58 +++---- .../fpcf/analyses/ifds/old/ForwardICFG.scala | 4 +- .../ifds/old/ForwardIFDSAnalysis.scala | 18 +-- .../old/IFDSBasedVariableTypeAnalysis.scala | 4 +- .../analyses/ifds/old/JavaIFDSProblem.scala | 20 +-- .../ifds/old/VariableTypeProblem.scala | 60 +++---- .../ifds/old/taint/BackwardTaintProblem.scala | 110 ++++++------- .../ifds/old/taint/ForwardTaintProblem.scala | 120 +++++++------- .../ifds/taint/ForwardTaintProblem.scala | 114 ++++++------- .../analyses/ifds/taint/TaintProblem.scala | 2 +- .../taint/ForwardTaintAnalysisFixture.scala | 8 +- .../old/BackwardTaintAnalysisFixture.scala | 10 +- .../old/ForwardTaintAnalysisFixture.scala | 10 +- 68 files changed, 1079 insertions(+), 1083 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PRATaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PRATaintAnalysis.scala index 5b77902476..9a521b7a3b 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PRATaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/PRATaintAnalysis.scala @@ -42,7 +42,7 @@ case class FlowFact(flow: ListSet[Method]) extends Fact { override val hashCode: Int = { // HERE, a foldLeft introduces a lot of overhead due to (un)boxing. var r = 1 - flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) + flow.foreach(f => r = (r + f.hashCode()) * 31) r } } @@ -69,9 +69,9 @@ class PRATaintAnalysis private ( override def normalFlow(stmt: Statement, succ: Statement, in: Set[Fact]): Set[Fact] = stmt.stmt.astID match { - case Assignment.ASTID ⇒ + case Assignment.ASTID => handleAssignment(stmt, stmt.stmt.asAssignment.expr, in) - case _ ⇒ in + case _ => in } /** @@ -79,21 +79,21 @@ class PRATaintAnalysis private ( */ def isTainted(expr: Expr[V], in: Set[Fact]): Boolean = { expr.isVar && in.exists { - case Variable(index) ⇒ expr.asVar.definedBy.contains(index) - case _ ⇒ false + case Variable(index) => expr.asVar.definedBy.contains(index) + case _ => false } } def handleAssignment(stmt: Statement, expr: Expr[V], in: Set[Fact]): Set[Fact] = expr.astID match { - case Var.ASTID ⇒ + case Var.ASTID => val newTaint = in.collect { - case Variable(index) if expr.asVar.definedBy.contains(index) ⇒ + case Variable(index) if expr.asVar.definedBy.contains(index) => Some(Variable(stmt.index)) - case _ ⇒ None + case _ => None }.flatten in ++ newTaint - case _ ⇒ in + case _ => in } override def callFlow( @@ -107,9 +107,9 @@ class PRATaintAnalysis private ( Set.empty } else { in.collect { - case Variable(index) ⇒ // Taint formal parameter if actual parameter is tainted + case Variable(index) => // Taint formal parameter if actual parameter is tainted allParams.zipWithIndex.collect { - case (param, pIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, pIndex) if param.asVar.definedBy.contains(index) => Variable(paramToIndex(pIndex, !callee.definedMethod.isStatic)) } }.flatten @@ -142,18 +142,18 @@ class PRATaintAnalysis private ( else { val allParams = (asCall(stmt.stmt).receiverOption ++ asCall(stmt.stmt).params).toSeq var flows: Set[Fact] = Set.empty - for (fact ← in) { + for (fact <- in) { fact match { - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) => // Taint actual parameter if formal parameter is tainted val param = allParams(paramToIndex(index, !callee.definedMethod.isStatic)) flows ++= param.asVar.definedBy.iterator.map(Variable) - case FlowFact(flow) ⇒ + case FlowFact(flow) => val newFlow = flow + stmt.method flows += FlowFact(newFlow) - case _ ⇒ + case _ => } } @@ -161,7 +161,7 @@ class PRATaintAnalysis private ( if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { val returnValue = exit.stmt.asReturnValue.expr.asVar flows ++= in.collect { - case Variable(index) if returnValue.definedBy.contains(index) ⇒ + case Variable(index) if returnValue.definedBy.contains(index) => Variable(stmt.index) } } @@ -180,9 +180,9 @@ class PRATaintAnalysis private ( val call = asCall(stmt.stmt) if (isSink(call)) { if (in.exists { - case Variable(index) ⇒ - asCall(stmt.stmt).params.exists(p ⇒ p.asVar.definedBy.contains(index)) - case _ ⇒ false + case Variable(index) => + asCall(stmt.stmt).params.exists(p => p.asVar.definedBy.contains(index)) + case _ => false }) { in ++ Set(FlowFact(ListSet(stmt.method))) } else { @@ -202,8 +202,8 @@ class PRATaintAnalysis private ( } val entryPoints: Map[DeclaredMethod, Fact] = (for { - m ← p.allMethodsWithBody - } yield declaredMethods(m) → NullFact).toMap + m <- p.allMethodsWithBody + } yield declaredMethods(m) -> NullFact).toMap } @@ -244,11 +244,11 @@ object PRATaintAnalysisRunner { JavaClassFileReader().ClassFiles(cp), JavaClassFileReader().ClassFiles(new File("/home/dominik/Desktop/android.jar")), libraryClassFilesAreInterfacesOnly = false, - Traversable.empty + Iterable.empty ) p.getOrCreateProjectInformationKeyInitializationData( PropertyStoreKey, - (context: List[PropertyStoreContext[AnyRef]]) ⇒ { + (context: List[PropertyStoreContext[AnyRef]]) => { implicit val lg: LogContext = p.logContext val ps = PKESequentialPropertyStore.apply(context: _*) PropertyStore.updateDebug(false) @@ -256,9 +256,9 @@ object PRATaintAnalysisRunner { } ) p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey)( - (i: Option[Set[Class[_ <: AnyRef]]]) ⇒ (i match { - case None ⇒ Set(classOf[l1.DefaultDomainWithCFGAndDefUse[_]]) - case Some(requirements) ⇒ requirements + classOf[l1.DefaultDomainWithCFGAndDefUse[_]] + (i: Option[Set[Class[_ <: AnyRef]]]) => (i match { + case None => Set(classOf[l1.DefaultDomainWithCFGAndDefUse[_]]) + case Some(requirements) => requirements + classOf[l1.DefaultDomainWithCFGAndDefUse[_]] }): Set[Class[_ <: AnyRef]] ) @@ -270,15 +270,15 @@ object PRATaintAnalysisRunner { val (_, analyses) = manager.runAll(LazyL0BaseAIAnalysis, TACAITransformer, PRATaintAnalysis) - val entryPoints = analyses.collect { case (_, a: PRATaintAnalysis) ⇒ a.entryPoints }.head + val entryPoints = analyses.collect { case (_, a: PRATaintAnalysis) => a.entryPoints }.head for { - e ← entryPoints + e <- entryPoints flows = ps(e, PRATaintAnalysis.property.key) - fact ← flows.ub.asInstanceOf[IFDSProperty[Fact]].flows.values.flatten.toSet[Fact] + fact <- flows.ub.asInstanceOf[IFDSProperty[Fact]].flows.values.flatten.toSet[Fact] } { fact match { - case FlowFact(flow) ⇒ println(s"flow: "+flow.map(_.toJava).mkString(", ")) - case _ ⇒ + case FlowFact(flow) => println(s"flow: "+flow.map(_.toJava).mkString(", ")) + case _ => } } @@ -287,14 +287,14 @@ object PRATaintAnalysisRunner { def getList(file: File): Map[(String, MethodDescriptor), ObjectType] = { ( for { - line ← Source.fromFile(file).getLines() + line <- Source.fromFile(file).getLines() Array(declClass, returnType, signature, _) = line.split(' ') index = signature.indexOf("(") name = signature.substring(0, index) parameters = signature.substring(index + 1, signature.length - 1) jvmSignature = parameters.split(',').map(toJVMType).mkString("(", "", ")"+toJVMType(returnType)) descriptor = MethodDescriptor(jvmSignature) - } yield (name, descriptor) → ObjectType(declClass.replace('.', '/')) + } yield (name, descriptor) -> ObjectType(declClass.replace('.', '/')) ).toMap } @@ -302,16 +302,16 @@ object PRATaintAnalysisRunner { val trimmedType = javaType.trim if (trimmedType.endsWith("[]")) "["+toJVMType(trimmedType.substring(0, trimmedType.length - 2)) else trimmedType match { - case "void" ⇒ "V" - case "byte" ⇒ "B" - case "char" ⇒ "C" - case "double" ⇒ "D" - case "float" ⇒ "F" - case "int" ⇒ "I" - case "long" ⇒ "J" - case "short" ⇒ "S" - case "boolean" ⇒ "Z" - case _ ⇒ "L"+trimmedType.replace('.', '/')+";" + case "void" => "V" + case "byte" => "B" + case "char" => "C" + case "double" => "D" + case "float" => "F" + case "int" => "I" + case "long" => "J" + case "short" => "S" + case "boolean" => "Z" + case _ => "L"+trimmedType.replace('.', '/')+";" } } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala index 2d952a16f1..d5c9a7d998 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosAnalysis.scala @@ -64,9 +64,9 @@ abstract class HerosAnalysis[F](p: SomeProject, icfg: OpalICFG) * with FunctionCall) */ protected def asCall(stmt: Stmt[V]): Call[V] = stmt.astID match { - case Assignment.ASTID ⇒ stmt.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID ⇒ stmt.asExprStmt.expr.asFunctionCall - case _ ⇒ stmt.asMethodCall + case Assignment.ASTID => stmt.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID => stmt.asExprStmt.expr.asFunctionCall + case _ => stmt.asMethodCall } } @@ -132,7 +132,7 @@ abstract class HerosAnalysisRunner[F, Analysis <: HerosAnalysis[F]] { val p = Project(bytecode.RTJar) var times = Seq.empty[Milliseconds] for { - _ ← 1 to HerosAnalysisRunner.NUM_EXECUTIONS + _ <- 1 to HerosAnalysisRunner.NUM_EXECUTIONS } { times :+= evalProject(Project.recreate(p)) } @@ -151,8 +151,8 @@ abstract class HerosAnalysisRunner[F, Analysis <: HerosAnalysis[F]] { */ private def evalProject(p: SomeProject): Milliseconds = { p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) - case Some(requirements) ⇒ + case None => Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) => requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] } p.get(RTACallGraphKey) @@ -163,7 +163,7 @@ abstract class HerosAnalysisRunner[F, Analysis <: HerosAnalysis[F]] { JOptionPane.showMessageDialog(null, "Call Graph finished") time { solver.solve() - } { t ⇒ + } { t => analysisTime = t.toMilliseconds } if (HerosAnalysis.MEASURE_MEMORY) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala index 267e732dad..55caf3730e 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala @@ -36,7 +36,7 @@ class HerosVariableTypeAnalysis( override val initialSeeds: util.Map[JavaStatement, util.Set[VTAFact]] = { var result: Map[JavaStatement, util.Set[VTAFact]] = Map.empty - for ((m, facts) ← initialMethods) { + for ((m, facts) <- initialMethods) { result += icfg.getStartPointsOf(m).iterator().next() -> facts } result.asJava @@ -55,26 +55,26 @@ class HerosVariableTypeAnalysis( if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt stmt.astID match { - case Assignment.ASTID ⇒ - (source: VTAFact) ⇒ { + case Assignment.ASTID => + (source: VTAFact) => { val fact = newFact(statement.method, statement.stmt.asAssignment.expr, statement.index, source) if (fact.isDefined) TwoElementSet.twoElementSet(source, fact.get) else Collections.singleton(source) } - case ArrayStore.ASTID ⇒ - (source: VTAFact) ⇒ { + case ArrayStore.ASTID => + (source: VTAFact) => { val flow = scala.collection.mutable.Set.empty[VTAFact] flow += source newFact(statement.method, stmt.asArrayStore.value, statement.index, source).foreach { - case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) ⇒ + case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) => stmt.asArrayStore.arrayRef.asVar.definedBy .foreach(flow += VariableType(_, ArrayType(t), upperBound)) - case _ ⇒ // Nothing to do + case _ => // Nothing to do } flow.asJava } - case _ ⇒ Identity.v() + case _ => Identity.v() } } @@ -83,21 +83,21 @@ class HerosVariableTypeAnalysis( else { val callObject = asCall(stmt.stmt) val allParams = callObject.allParams - source: VTAFact ⇒ { + source: VTAFact => { val flow = scala.collection.mutable.Set.empty[VTAFact] source match { - case VariableType(definedBy, t, upperBound) ⇒ + case VariableType(definedBy, t, upperBound) => allParams.iterator.zipWithIndex.foreach { - case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ + case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) => flow += VariableType( JavaIFDSProblem .switchParamAndVariableIndex(parameterIndex, callee.isStatic), t, upperBound ) - case _ ⇒ + case _ => } - case _ ⇒ + case _ => } flow.asJava } @@ -111,12 +111,12 @@ class HerosVariableTypeAnalysis( ): FlowFunction[VTAFact] = if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { val returnValue = exit.stmt.asReturnValue.expr.asVar - source: VTAFact ⇒ { + source: VTAFact => { source match { // If we know the type of the return value, we create a fact for the assigned variable. - case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) ⇒ + case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) => Collections.singleton(VariableType(stmt.index, t, upperBound)) - case _ ⇒ Collections.emptySet() + case _ => Collections.emptySet() } } } else KillAll.v() @@ -128,21 +128,21 @@ class HerosVariableTypeAnalysis( if (!insideAnalysisContext(statement.method)) return KillAll.v() val stmt = statement.stmt val calleeDefinitionSites = asCall(stmt).receiverOption - .map(callee ⇒ callee.asVar.definedBy) + .map(callee => callee.asVar.definedBy) .getOrElse(EmptyIntTrieSet) val callOutsideOfAnalysisContext = getCallees(statement).exists( - method ⇒ + method => !((method.hasSingleDefinedMethod || method.hasMultipleDefinedMethods) && insideAnalysisContext( method.definedMethod )) ) - source: VTAFact ⇒ { + source: VTAFact => { val result = scala.collection.mutable.Set[VTAFact](source) source match { - case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) ⇒ + case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) => result += CalleeType(statement.index, t, upperBound) - case _ ⇒ + case _ => } if (callOutsideOfAnalysisContext) { val returnType = asCall(stmt).descriptor.returnType @@ -162,31 +162,31 @@ class HerosVariableTypeAnalysis( statementIndex: Int, source: VTAFact ): Option[VariableType] = expression.astID match { - case New.ASTID ⇒ + case New.ASTID => source match { - case VTANullFact ⇒ + case VTANullFact => Some(VariableType(statementIndex, expression.asNew.tpe, upperBound = false)) - case _ ⇒ None + case _ => None } - case Var.ASTID ⇒ + case Var.ASTID => source match { - case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ + case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) => Some(VariableType(statementIndex, t, upperBound)) - case _ ⇒ None + case _ => None } - case ArrayLoad.ASTID ⇒ + case ArrayLoad.ASTID => source match { case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && - expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ + expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) => Some(VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound)) - case _ ⇒ None + case _ => None } - case GetField.ASTID | GetStatic.ASTID ⇒ + case GetField.ASTID | GetStatic.ASTID => val t = expression.asFieldRead.declaredFieldType if (t.isReferenceType) Some(VariableType(statementIndex, t.asReferenceType, upperBound = true)) else None - case _ ⇒ None + case _ => None } @tailrec private def isArrayOfObjectType( @@ -219,9 +219,9 @@ class HerosVariableTypeAnalysisRunner val initialMethods = p.allProjectClassFiles .filter(_.fqn.startsWith("java/lang")) - .flatMap(classFile ⇒ classFile.methods) + .flatMap(classFile => classFile.methods) .filter(isEntryPoint) - .map(method ⇒ method -> entryPointsForMethod(method).asJava) + .map(method => method -> entryPointsForMethod(method).asJava) .toMap new HerosVariableTypeAnalysis(p, new OpalForwardICFG(p), initialMethods) } @@ -239,7 +239,7 @@ class HerosVariableTypeAnalysisRunner private def entryPointsForMethod(method: Method): Set[VTAFact] = { (method.descriptor.parameterTypes.zipWithIndex.collect { - case (t, index) if t.isReferenceType ⇒ + case (t, index) if t.isReferenceType => VariableType( JavaIFDSProblem.switchParamAndVariableIndex(index, method.isStatic), t.asReferenceType, diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala index 0dd686bfb7..f3439078bf 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala @@ -48,7 +48,7 @@ object VTAEquality { * because the VariableTypeFact holds after the statement. */ case statement if statement.stmt.astID != Return.ASTID && statement.stmt.astID != ReturnValue.ASTID - && (statement.stmt.astID != Assignment.ASTID || statement.stmt.asAssignment.expr.astID != New.ASTID) ⇒ + && (statement.stmt.astID != Assignment.ASTID || statement.stmt.asAssignment.expr.astID != New.ASTID) => val opalFacts = opalResult(statement) /* * ifdsResultsAt returns the facts before the statements. @@ -62,38 +62,38 @@ object VTAEquality { println(s"Statement: $statement") println(s"Opal: $opalFacts") println(s"Heros: $herosFacts") - println + println() } else - opalFacts.filter(!herosFacts.contains(_)).foreach { fact ⇒ + opalFacts.filter(!herosFacts.contains(_)).foreach { fact => println("Error: Heros fact missing:") println(s"Statement: $statement") println(s"Fact: $fact") println(s"Opal: $opalFacts") println(s"Heros: $herosFacts") - println + println() } - case _ ⇒ + case _ => } } private def performHerosAnalysis = { - val project = FixtureProject.recreate { piKeyUnidueId ⇒ + val project = FixtureProject.recreate { piKeyUnidueId => piKeyUnidueId != PropertyStoreKey.uniqueId } implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) - case Some(requirements) ⇒ + case None => Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) => requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] } project.get(RTACallGraphKey) val initialMethods = project.allProjectClassFiles .filter(_.fqn.startsWith("org/opalj/fpcf/fixtures/vta")) - .flatMap(classFile ⇒ classFile.methods) + .flatMap(classFile => classFile.methods) .filter(isEntryPoint) - .map(method ⇒ method -> entryPointsForMethod(method).asJava) + .map(method => method -> entryPointsForMethod(method).asJava) .toMap val cgf = new OpalForwardICFG(project) val herosAnalysis = new HerosVariableTypeAnalysis(project, cgf, initialMethods) @@ -103,14 +103,14 @@ object VTAEquality { } private def performOpalAnalysis = { - val project = FixtureProject.recreate { piKeyUnidueId ⇒ + val project = FixtureProject.recreate { piKeyUnidueId => piKeyUnidueId != PropertyStoreKey.uniqueId } val propertyStore = project.get(PropertyStoreKey) var result = Map.empty[JavaStatement, Set[VTAFact]] project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) - case Some(requirements) ⇒ + case None => Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) => requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] } project.get(RTACallGraphKey) @@ -118,15 +118,15 @@ object VTAEquality { propertyStore .entities(IFDSBasedVariableTypeAnalysisScheduler.property.key) .collect { - case EPS((m: DefinedMethod, inputFact)) ⇒ + case EPS((m: DefinedMethod, inputFact)) => (m, inputFact) } - .foreach { entity ⇒ + .foreach { entity => val entityResult = propertyStore(entity, IFDSBasedVariableTypeAnalysisScheduler.property.key) match { - case FinalEP(_, VTAResult(map, _)) ⇒ map - case _ ⇒ throw new RuntimeException + case FinalEP(_, VTAResult(map, _)) => map + case _ => throw new RuntimeException } - entityResult.keys.foreach { declaredMethodStatement ⇒ + entityResult.keys.foreach { declaredMethodStatement => val statement = declaredMethodStatement.asJavaStatement /* * Heros returns the facts before the statements. @@ -137,7 +137,7 @@ object VTAEquality { result = result.updated( statement, result.getOrElse(statement, Set.empty) ++ entityResult(declaredMethodStatement) - .filter(fact ⇒ fact != VTANullFact && !fact.isInstanceOf[CalleeType]) + .filter(fact => fact != VTANullFact && !fact.isInstanceOf[CalleeType]) ) } } @@ -154,7 +154,7 @@ object VTAEquality { private def entryPointsForMethod(method: Method): Set[VTAFact] = { (method.descriptor.parameterTypes.zipWithIndex.collect { - case (t, index) if t.isReferenceType ⇒ + case (t, index) if t.isReferenceType => VariableType( switchParamAndVariableIndex(index, method.isStatic), t.asReferenceType, @@ -173,12 +173,12 @@ object VTAEquality { val fixtureFiles = new File(sourceFolder) val fixtureClassFiles = ClassFiles(fixtureFiles) - val projectClassFiles = fixtureClassFiles.filter { cfSrc ⇒ + val projectClassFiles = fixtureClassFiles.filter { cfSrc => val (cf, _) = cfSrc cf.thisType.packageName.startsWith("org/opalj/fpcf/fixtures") } - val propertiesClassFiles = fixtureClassFiles.filter { cfSrc ⇒ + val propertiesClassFiles = fixtureClassFiles.filter { cfSrc => val (cf, _) = cfSrc cf.thisType.packageName.startsWith("org/opalj/fpcf/properties") } @@ -210,7 +210,7 @@ object VTAEquality { projectClassFiles, libraryClassFiles, libraryClassFilesAreInterfacesOnly = false, - virtualClassFiles = Traversable.empty + virtualClassFiles = Iterable.empty ) } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala index 6b4634623a..1377894911 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -44,11 +44,11 @@ import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, TaintFact, FlowFact class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) extends HerosTaintAnalysis(p, icfg) { override val initialSeeds: util.Map[JavaStatement, util.Set[TaintFact]] = - p.allProjectClassFiles.filter(classFile ⇒ + p.allProjectClassFiles.filter(classFile => classFile.thisType.fqn == "java/lang/Class") - .flatMap(classFile ⇒ classFile.methods) + .flatMap(classFile => classFile.methods) .filter(_.name == "forName") - .map(method ⇒ icfg.getExitStmt(method) → Set[TaintFact](Variable(-2)).asJava).toMap.asJava + .map(method => icfg.getExitStmt(method) -> Set[TaintFact](Variable(-2)).asJava).toMap.asJava var flowFacts = Map.empty[Method, Set[FlowFact]] @@ -61,54 +61,54 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) override def getNormalFlowFunction(statement: JavaStatement, succ: JavaStatement): FlowFunction[TaintFact] = { val method = statement.method val stmt = statement.stmt - source: TaintFact ⇒ { + source: TaintFact => { var result = stmt.astID match { - case Assignment.ASTID ⇒ + case Assignment.ASTID => if (isTainted(statement.index, source)) createNewTaints(stmt.asAssignment.expr, statement) + source else Set(source) - case ArrayStore.ASTID ⇒ + case ArrayStore.ASTID => val arrayStore = stmt.asArrayStore val arrayIndex = TaintProblem.getIntConstant(arrayStore.index, statement.code) val arrayDefinedBy = arrayStore.arrayRef.asVar.definedBy var facts = (source match { // In this case, we taint the whole array. - case Variable(index) if arrayDefinedBy.contains(index) ⇒ + case Variable(index) if arrayDefinedBy.contains(index) => createNewTaints(arrayStore.value, statement) // In this case, we taint exactly the stored element. case ArrayElement(index, taintedElement) if arrayDefinedBy.contains(index) && - (arrayIndex.isEmpty || arrayIndex.get == taintedElement) ⇒ + (arrayIndex.isEmpty || arrayIndex.get == taintedElement) => createNewTaints(arrayStore.value, statement) - case _ ⇒ Set.empty[TaintFact] + case _ => Set.empty[TaintFact] }) + source if (arrayDefinedBy.size == 1 && arrayIndex.isDefined) facts -= ArrayElement(arrayDefinedBy.head, arrayIndex.get) facts - case PutField.ASTID ⇒ + case PutField.ASTID => val putField = stmt.asPutField val objectDefinedBy = putField.objRef.asVar.definedBy if (source match { case InstanceField(index, declaringClass, name) if objectDefinedBy.contains(index) && - putField.declaringClass == declaringClass && putField.name == name ⇒ + putField.declaringClass == declaringClass && putField.name == name => true - case _ ⇒ false + case _ => false }) createNewTaints(putField.value, statement) + source else Set(source) - case PutStatic.ASTID ⇒ + case PutStatic.ASTID => val putStatic = stmt.asPutStatic if (source match { - case StaticField(declaringClass, name) if putStatic.declaringClass == declaringClass && putStatic.name == name ⇒ + case StaticField(declaringClass, name) if putStatic.declaringClass == declaringClass && putStatic.name == name => true - case _ ⇒ false + case _ => false }) createNewTaints(putStatic.value, statement) + source else Set(source) - case _ ⇒ Set(source) + case _ => Set(source) } if (icfg.isExitStmt(succ) && HerosAnalysis.canBeCalledFromOutside(method) && (source match { - case Variable(index) if index < 0 ⇒ true - case ArrayElement(index, _) if index < 0 ⇒ true - case InstanceField(index, _, _) if index < 0 ⇒ true - case _ ⇒ false + case Variable(index) if index < 0 => true + case ArrayElement(index, _) if index < 0 => true + case InstanceField(index, _, _) if index < 0 => true + case _ => false })) { val fact = FlowFact(Seq(JavaMethod(method))) result += fact @@ -122,39 +122,39 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[TaintFact] = { val callObject = asCall(stmt.stmt) val staticCall = callee.isStatic - source: TaintFact ⇒ { + source: TaintFact => { val returnValueFacts = if (stmt.stmt.astID == Assignment.ASTID) source match { - case Variable(index) if index == stmt.index ⇒ + case Variable(index) if index == stmt.index => createNewTaintsForCallee(callee) - case ArrayElement(index, taintedElement) if index == stmt.index ⇒ + case ArrayElement(index, taintedElement) if index == stmt.index => toArrayElement(createNewTaintsForCallee(callee), taintedElement) - case InstanceField(index, declaringClass, name) if index == stmt.index ⇒ + case InstanceField(index, declaringClass, name) if index == stmt.index => toInstanceField(createNewTaintsForCallee(callee), declaringClass, name) - case _ ⇒ Set.empty[TaintFact] + case _ => Set.empty[TaintFact] } else Set.empty val thisOffset = if (callee.isStatic) 0 else 1 val parameterFacts = callObject.allParams.zipWithIndex - .filter(pair ⇒ (pair._2 == 0 && !staticCall) || callObject.descriptor.parameterTypes(pair._2 - thisOffset).isReferenceType) - .flatMap { pair ⇒ + .filter(pair => (pair._2 == 0 && !staticCall) || callObject.descriptor.parameterTypes(pair._2 - thisOffset).isReferenceType) + .flatMap { pair => val param = pair._1.asVar val paramIndex = pair._2 source match { - case Variable(index) if param.definedBy.contains(index) ⇒ + case Variable(index) if param.definedBy.contains(index) => Some(Variable(JavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall))) - case ArrayElement(index, taintedElement) if param.definedBy.contains(index) ⇒ + case ArrayElement(index, taintedElement) if param.definedBy.contains(index) => Some(ArrayElement( JavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement )) - case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) ⇒ + case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) => Some(InstanceField( JavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall), declaringClass, name )) - case staticField: StaticField ⇒ Some(staticField) - case _ ⇒ None + case staticField: StaticField => Some(staticField) + case _ => None } } (returnValueFacts ++ parameterFacts).asJava @@ -169,25 +169,25 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) val staticCall = callee.isStatic val thisOffset = if (staticCall) 0 else 1 val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) - .map(index ⇒ JavaIFDSProblem.switchParamAndVariableIndex(index + thisOffset, staticCall)) - source: TaintFact ⇒ + .map(index => JavaIFDSProblem.switchParamAndVariableIndex(index + thisOffset, staticCall)) + source: TaintFact => (source match { - case Variable(index) if formalParameterIndices.contains(index) ⇒ + case Variable(index) if formalParameterIndices.contains(index) => createNewTaints( callStatement.allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), statement ) - case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) ⇒ + case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) => toArrayElement(createNewTaints( callStatement.allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), statement ), taintedElement) - case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) ⇒ + case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) => toInstanceField(createNewTaints( callStatement.allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), statement ), declaringClass, name) - case staticField: StaticField ⇒ Set[TaintFact](staticField) - case _ ⇒ Set.empty[TaintFact] + case staticField: StaticField => Set[TaintFact](staticField) + case _ => Set.empty[TaintFact] }).asJava } @@ -197,57 +197,57 @@ class HerosBackwardClassForNameAnalysis(p: SomeProject, icfg: OpalBackwardICFG) } private def isTainted(index: Int, source: TaintFact, taintedElement: Option[Int] = None): Boolean = source match { - case Variable(variableIndex) ⇒ variableIndex == index - case ArrayElement(variableIndex, element) ⇒ + case Variable(variableIndex) => variableIndex == index + case ArrayElement(variableIndex, element) => variableIndex == index && (taintedElement.isEmpty || taintedElement.get == element) - case _ ⇒ false + case _ => false } private def createNewTaintsForCallee(callee: Method): Set[TaintFact] = { - icfg.getStartPointsOf(callee).asScala.flatMap { statement ⇒ + icfg.getStartPointsOf(callee).asScala.flatMap { statement => val stmt = statement.stmt stmt.astID match { - case ReturnValue.ASTID ⇒ createNewTaints(stmt.asReturnValue.expr, statement) - case _ ⇒ Set.empty[TaintFact] + case ReturnValue.ASTID => createNewTaints(stmt.asReturnValue.expr, statement) + case _ => Set.empty[TaintFact] } }.toSet } private def createNewTaints(expression: Expr[V], statement: JavaStatement): Set[TaintFact] = expression.astID match { - case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) - case ArrayLoad.ASTID ⇒ + case Var.ASTID => expression.asVar.definedBy.map(Variable) + case ArrayLoad.ASTID => val arrayLoad = expression.asArrayLoad val arrayIndex = TaintProblem.getIntConstant(expression.asArrayLoad.index, statement.code) val arrayDefinedBy = arrayLoad.arrayRef.asVar.definedBy if (arrayIndex.isDefined) arrayDefinedBy.map(ArrayElement(_, arrayIndex.get)) else arrayDefinedBy.map(Variable) case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | - PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) ⇒ + PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID => + (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) => acc ++ createNewTaints(expression.subExpr(subExpr), statement)) - case GetField.ASTID ⇒ + case GetField.ASTID => val getField = expression.asGetField getField.objRef.asVar.definedBy .map(InstanceField(_, getField.declaringClass, getField.name)) - /*case GetStatic.ASTID ⇒ + /*case GetStatic.ASTID => val getStatic = expression.asGetStatic Set(StaticField(getStatic.declaringClass, getStatic.name))*/ - case _ ⇒ Set.empty + case _ => Set.empty } private def toArrayElement(facts: Set[TaintFact], taintedElement: Int): Set[TaintFact] = facts.map { - case Variable(variableIndex) ⇒ ArrayElement(variableIndex, taintedElement) - case ArrayElement(variableIndex, _) ⇒ ArrayElement(variableIndex, taintedElement) - case InstanceField(variableIndex, _, _) ⇒ ArrayElement(variableIndex, taintedElement) + case Variable(variableIndex) => ArrayElement(variableIndex, taintedElement) + case ArrayElement(variableIndex, _) => ArrayElement(variableIndex, taintedElement) + case InstanceField(variableIndex, _, _) => ArrayElement(variableIndex, taintedElement) } private def toInstanceField(facts: Set[TaintFact], declaringClass: ObjectType, name: String): Set[TaintFact] = facts.map { - case Variable(variableIndex) ⇒ InstanceField(variableIndex, declaringClass, name) - case ArrayElement(variableIndex, _) ⇒ InstanceField(variableIndex, declaringClass, name) - case InstanceField(variableIndex, _, _) ⇒ + case Variable(variableIndex) => InstanceField(variableIndex, declaringClass, name) + case ArrayElement(variableIndex, _) => InstanceField(variableIndex, declaringClass, name) + case InstanceField(variableIndex, _, _) => InstanceField(variableIndex, declaringClass, name) } @@ -260,8 +260,8 @@ class HerosBackwardClassForNameAnalysisRunner extends HerosAnalysisRunner[TaintF override protected def printResultsToConsole(analysis: HerosBackwardClassForNameAnalysis, analysisTime: Milliseconds): Unit = { for { - method ← analysis.flowFacts.keys - fact ← analysis.flowFacts(method) + method <- analysis.flowFacts.keys + fact <- analysis.flowFacts(method) } println(s"flow: "+fact.flow.map(_.signature).mkString(", ")) println(s"Time: $analysisTime") } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index b77cdfb865..1be87f0227 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -35,7 +35,7 @@ class HerosForwardClassForNameAnalysis( override val initialSeeds: util.Map[JavaStatement, util.Set[TaintFact]] = { var result: Map[JavaStatement, util.Set[TaintFact]] = Map.empty - for ((m, facts) ← initialMethods) { + for ((m, facts) <- initialMethods) { result += icfg.getStartPointsOf(m).iterator().next() -> facts } result.asJava @@ -51,13 +51,13 @@ class HerosForwardClassForNameAnalysis( override def getNormalFlowFunction(stmt: JavaStatement, succ: JavaStatement): FlowFunction[TaintFact] = { stmt.stmt.astID match { - case Assignment.ASTID ⇒ + case Assignment.ASTID => handleAssignment(stmt, stmt.stmt.asAssignment.expr) - case ArrayStore.ASTID ⇒ + case ArrayStore.ASTID => val store = stmt.stmt.asArrayStore val definedBy = store.arrayRef.asVar.definedBy val index = TaintProblem.getIntConstant(store.index, stmt.code) - (source: TaintFact) ⇒ { + (source: TaintFact) => { if (isTainted(store.value, source)) { if (index.isDefined) { (definedBy.iterator.map[TaintFact](ArrayElement(_, index.get)).toSet + source).asJava @@ -69,23 +69,23 @@ class HerosForwardClassForNameAnalysis( val idx = index.get val arrayDefinedBy = definedBy.head source match { - case ArrayElement(`arrayDefinedBy`, `idx`) ⇒ Collections.emptySet() - case _ ⇒ Collections.singleton(source) + case ArrayElement(`arrayDefinedBy`, `idx`) => Collections.emptySet() + case _ => Collections.singleton(source) } } else Collections.singleton(source) } } - case PutStatic.ASTID ⇒ + case PutStatic.ASTID => val put = stmt.stmt.asPutStatic - (source: TaintFact) ⇒ { + (source: TaintFact) => { if (isTainted(put.value, source)) TwoElementSet.twoElementSet(source, StaticField(put.declaringClass, put.name)) else Collections.singleton(source) } - case PutField.ASTID ⇒ + case PutField.ASTID => val put = stmt.stmt.asPutField val definedBy = put.objRef.asVar.definedBy - (source: TaintFact) ⇒ { + (source: TaintFact) => { if (isTainted(put.value, source)) { (definedBy.iterator .map(InstanceField(_, put.declaringClass, put.name)) @@ -94,27 +94,27 @@ class HerosForwardClassForNameAnalysis( Collections.singleton(source) } } - case _ ⇒ Identity.v() + case _ => Identity.v() } } def handleAssignment(stmt: JavaStatement, expr: Expr[V]): FlowFunction[TaintFact] = expr.astID match { - case Var.ASTID ⇒ - (source: TaintFact) ⇒ { + case Var.ASTID => + (source: TaintFact) => { source match { - case Variable(index) if expr.asVar.definedBy.contains(index) ⇒ + case Variable(index) if expr.asVar.definedBy.contains(index) => TwoElementSet.twoElementSet(source, Variable(stmt.index)) - case _ ⇒ Collections.singleton(source) + case _ => Collections.singleton(source) } } - case ArrayLoad.ASTID ⇒ + case ArrayLoad.ASTID => val load = expr.asArrayLoad val arrayDefinedBy = load.arrayRef.asVar.definedBy - (source: TaintFact) ⇒ { + (source: TaintFact) => { source match { // The specific array element may be tainted - case ArrayElement(index, taintedIndex) ⇒ + case ArrayElement(index, taintedIndex) => val arrIndex = TaintProblem.getIntConstant(load.index, stmt.code) if (arrayDefinedBy.contains(index) && (arrIndex.isEmpty || taintedIndex == arrIndex.get)) @@ -122,39 +122,39 @@ class HerosForwardClassForNameAnalysis( else Collections.singleton(source) // Or the whole array - case Variable(index) if arrayDefinedBy.contains(index) ⇒ + case Variable(index) if arrayDefinedBy.contains(index) => TwoElementSet.twoElementSet(source, Variable(stmt.index)) - case _ ⇒ Collections.singleton(source) + case _ => Collections.singleton(source) } } - case GetStatic.ASTID ⇒ + case GetStatic.ASTID => val get = expr.asGetStatic - (source: TaintFact) ⇒ { + (source: TaintFact) => { if (source == StaticField(get.declaringClass, get.name)) TwoElementSet.twoElementSet(source, Variable(stmt.index)) else Collections.singleton(source) } - case GetField.ASTID ⇒ + case GetField.ASTID => val get = expr.asGetField val objectDefinedBy = get.objRef.asVar.definedBy - (source: TaintFact) ⇒ { + (source: TaintFact) => { source match { // The specific field may be tainted - case InstanceField(index, _, taintedField) if taintedField == get.name && objectDefinedBy.contains(index) ⇒ + case InstanceField(index, _, taintedField) if taintedField == get.name && objectDefinedBy.contains(index) => TwoElementSet.twoElementSet(source, Variable(stmt.index)) // Or the whole object - case Variable(index) if objectDefinedBy.contains(index) ⇒ + case Variable(index) if objectDefinedBy.contains(index) => TwoElementSet.twoElementSet(source, Variable(stmt.index)) - case _ ⇒ Collections.singleton(source) + case _ => Collections.singleton(source) } } case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | - NewArray.ASTID | ArrayLength.ASTID ⇒ - (source: TaintFact) ⇒ { + NewArray.ASTID | ArrayLength.ASTID => + (source: TaintFact) => { val result = new util.HashSet[TaintFact] (0 until expr.subExprCount) .foreach( - subExpression ⇒ + subExpression => result.addAll( handleAssignment(stmt, expr.subExpr(subExpression)) .computeTargets(source) @@ -162,7 +162,7 @@ class HerosForwardClassForNameAnalysis( ) result } - case _ ⇒ Identity.v() + case _ => Identity.v() } override def getCallFlowFunction(stmt: JavaStatement, callee: Method): FlowFunction[TaintFact] = { @@ -170,23 +170,23 @@ class HerosForwardClassForNameAnalysis( val allParams = callObject.allParams if (relevantCallee(callee)) { val allParamsWithIndices = allParams.zipWithIndex - source: TaintFact ⇒ + source: TaintFact => (source match { - case Variable(index) ⇒ // Taint formal parameter if actual parameter is tainted + case Variable(index) => // Taint formal parameter if actual parameter is tainted allParamsWithIndices .collect { - case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIdx) if param.asVar.definedBy.contains(index) => Variable( JavaIFDSProblem.switchParamAndVariableIndex(paramIdx, callee.isStatic) ) } .toSet[TaintFact] - case ArrayElement(index, taintedIndex) ⇒ + case ArrayElement(index, taintedIndex) => // Taint element of formal parameter if element of actual parameter is tainted allParamsWithIndices .collect { - case (param, paramIdx) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIdx) if param.asVar.definedBy.contains(index) => ArrayElement( JavaIFDSProblem.switchParamAndVariableIndex(paramIdx, callee.isStatic), taintedIndex @@ -194,7 +194,7 @@ class HerosForwardClassForNameAnalysis( } .toSet[TaintFact] - case InstanceField(index, declClass, taintedField) ⇒ + case InstanceField(index, declClass, taintedField) => // Taint field of formal parameter if field of actual parameter is tainted // Only if the formal parameter is of a type that may have that field! allParamsWithIndices @@ -204,7 +204,7 @@ class HerosForwardClassForNameAnalysis( paramIdx, callee.isStatic ) != -1 || - classHierarchy.isSubtypeOf(declClass, callee.classFile.thisType)) ⇒ + classHierarchy.isSubtypeOf(declClass, callee.classFile.thisType)) => InstanceField( JavaIFDSProblem.switchParamAndVariableIndex(paramIdx, callee.isStatic), declClass, @@ -212,8 +212,8 @@ class HerosForwardClassForNameAnalysis( ) } .toSet[TaintFact] - case sf: StaticField ⇒ Set(sf).asInstanceOf[Set[TaintFact]] - case _ ⇒ Set.empty[TaintFact] + case sf: StaticField => Set(sf).asInstanceOf[Set[TaintFact]] + case _ => Set.empty[TaintFact] }).asJava } else KillAll.v() } @@ -240,16 +240,16 @@ class HerosForwardClassForNameAnalysis( val callStatement = asCall(stmt.stmt) val allParams = callStatement.allParams - source: TaintFact ⇒ { + source: TaintFact => { val paramFacts = source match { - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) => val params = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) params.asVar.definedBy.iterator.map(Variable).toSet - case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 => // Taint element of actual parameter if element of formal parameter is tainted val params = asCall(stmt.stmt).allParams( @@ -260,7 +260,7 @@ class HerosForwardClassForNameAnalysis( .asInstanceOf[Iterator[TaintFact]] .toSet - case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 => // Taint field of actual parameter if field of formal parameter is tainted val params = allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic)) @@ -269,8 +269,8 @@ class HerosForwardClassForNameAnalysis( .asInstanceOf[Iterator[TaintFact]] .toSet - case sf: StaticField ⇒ Set(sf) - case FlowFact(flow) if !flow.contains(stmt.method) ⇒ + case sf: StaticField => Set(sf) + case FlowFact(flow) if !flow.contains(stmt.method) => val flowFact = FlowFact(JavaMethod(stmt.method) +: flow) if (initialMethods.contains(stmt.method)) flowFacts = flowFacts.updated( @@ -278,7 +278,7 @@ class HerosForwardClassForNameAnalysis( flowFacts.getOrElse(stmt.method, Set.empty[FlowFact]) + flowFact ) Set(flowFact) - case _ ⇒ + case _ => Set.empty } @@ -286,13 +286,13 @@ class HerosForwardClassForNameAnalysis( if (exit.stmt.astID == ReturnValue.ASTID && stmt.stmt.astID == Assignment.ASTID) { val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy source match { - case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + case Variable(index) if returnValueDefinedBy.contains(index) => Some(Variable(stmt.index)) - case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) => Some(ArrayElement(stmt.index, taintedIndex)) - case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) => Some(InstanceField(stmt.index, declClass, taintedField)) - case _ ⇒ None + case _ => None } } else None @@ -327,18 +327,18 @@ class HerosForwardClassForNameAnalysis( */ private def isTainted(expr: Expr[V], in: TaintFact): Boolean = { expr.isVar && (in match { - case Variable(source) ⇒ expr.asVar.definedBy.contains(source) - case ArrayElement(source, _) ⇒ expr.asVar.definedBy.contains(source) - case InstanceField(source, _, _) ⇒ expr.asVar.definedBy.contains(source) - case _ ⇒ false + case Variable(source) => expr.asVar.definedBy.contains(source) + case ArrayElement(source, _) => expr.asVar.definedBy.contains(source) + case InstanceField(source, _, _) => expr.asVar.definedBy.contains(source) + case _ => false }) } private def relevantCallee(callee: Method): Boolean = callee.descriptor.parameterTypes.exists { - case ObjectType.Object ⇒ true - case ObjectType.String ⇒ true - case _ ⇒ false + case ObjectType.Object => true + case ObjectType.String => true + case _ => false } && (!HerosAnalysis.canBeCalledFromOutside(callee) || isClassForName(callee)) private def isClassForName(method: Method): Boolean = @@ -353,11 +353,11 @@ class HerosForwardClassForNameAnalysisRunner val propertyStore = p.get(PropertyStoreKey) val initialMethods = scala.collection.mutable.Map.empty[Method, util.Set[TaintFact]] for { - method ← declaredMethods.declaredMethods + method <- declaredMethods.declaredMethods .filter(canBeCalledFromOutside(_, propertyStore)) .map(_.definedMethod) - index ← method.descriptor.parameterTypes.zipWithIndex.collect { - case (pType, index) if pType == ObjectType.String ⇒ + index <- method.descriptor.parameterTypes.zipWithIndex.collect { + case (pType, index) if pType == ObjectType.String => index } } { @@ -373,8 +373,8 @@ class HerosForwardClassForNameAnalysisRunner analysisTime: Milliseconds ): Unit = { for { - method ← analysis.flowFacts.keys - fact ← analysis.flowFacts(method) + method <- analysis.flowFacts.keys + fact <- analysis.flowFacts(method) } println(s"flow: "+fact.flow.map(_.signature).mkString(", ")) println(s"Time: $analysisTime") } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala index ffc6d8e75d..606b4d7faf 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala @@ -8,7 +8,7 @@ import org.opalj.tac.TACode import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import java.util.concurrent.ConcurrentLinkedQueue -import java.util.{Collections, Collection ⇒ JCollection, List ⇒ JList, Set ⇒ JSet} +import java.util.{Collections, Collection => JCollection, List => JList, Set => JSet} import scala.collection.JavaConverters._ /** @@ -25,7 +25,7 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { override def getStartPointsOf(m: Method): JCollection[JavaStatement] = { val TACode(_, code, _, cfg, _) = tacai(m) (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).map { - case bb: BasicBlock ⇒ + case bb: BasicBlock => val index = bb.endPC JavaStatement(m, index, code, cfg) }.asJava @@ -37,19 +37,19 @@ class OpalBackwardICFG(project: SomeProject) extends OpalICFG(project) { val cfg = stmt.cfg val index = stmt.index (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).exists { - case bb: BasicBlock ⇒ bb.endPC == index + case bb: BasicBlock => bb.endPC == index } } override def allNonCallStartNodes(): JSet[JavaStatement] = { val res = new ConcurrentLinkedQueue[JavaStatement] - project.parForeachMethodWithBody() { mi ⇒ + project.parForeachMethodWithBody() { mi => val m = mi.method val TACode(_, code, _, cfg, _) = tacai(m) val endIndex = code.length val startIndices = (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors).map { - case bb: BasicBlock ⇒ bb.endPC + case bb: BasicBlock => bb.endPC } var index = 0 while (index < endIndex) { diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala index 805126cd07..b66210b769 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalForwardICFG.scala @@ -1,8 +1,8 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.heros.cfg -import java.util.{Collection ⇒ JCollection} -import java.util.{Set ⇒ JSet} +import java.util.{Collection => JCollection} +import java.util.{Set => JSet} import java.util.Collections import java.util.concurrent.ConcurrentLinkedQueue @@ -29,7 +29,7 @@ class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { override def allNonCallStartNodes(): JSet[JavaStatement] = { val res = new ConcurrentLinkedQueue[JavaStatement] - project.parForeachMethodWithBody() { mi ⇒ + project.parForeachMethodWithBody() { mi => val m = mi.method val TACode(_, code, _, cfg, _) = tacai(m) val endIndex = code.length @@ -49,7 +49,7 @@ class OpalForwardICFG(project: SomeProject) extends OpalICFG(project) { val tac = tacai(method) val cfg = tac.cfg val code = tac.stmts - cfg.allNodes.filter(_.isExitNode).flatMap(_.predecessors).map { bb ⇒ + cfg.allNodes.filter(_.isExitNode).flatMap(_.predecessors).map { bb => val endPc = bb.asBasicBlock.endPC JavaStatement(method, endPc, code, cfg) } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala index 22e314febb..9b8b35a608 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala @@ -1,9 +1,9 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.heros.cfg -import java.util.{List ⇒ JList} -import java.util.{Collection ⇒ JCollection} -import java.util.{Set ⇒ JSet} +import java.util.{List => JList} +import java.util.{Collection => JCollection} +import java.util.{Set => JSet} import scala.collection.JavaConverters._ import heros.InterproceduralCFG import org.opalj.fpcf.FinalEP @@ -46,7 +46,7 @@ import org.opalj.tac.fpcf.analyses.cg.TypeProvider */ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaStatement, Method] { - val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = + val tacai: Method => TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) implicit val ps: PropertyStore = project.get(PropertyStoreKey) implicit val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) @@ -57,8 +57,7 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta override def getPredsOf(stmt: JavaStatement): JList[JavaStatement] = { stmt.cfg .predecessors(stmt.index) - .toChain - .map { index ⇒ + .map { index => JavaStatement(stmt.method, index, stmt.code, stmt.cfg) } .toList @@ -68,8 +67,7 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta override def getSuccsOf(stmt: JavaStatement): JList[JavaStatement] = { stmt.cfg .successors(stmt.index) - .toChain - .map { index ⇒ + .map { index => JavaStatement(stmt.method, index, stmt.code, stmt.cfg) } .toList @@ -83,8 +81,8 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta .directCallees(typeProvider.newContext(declaredMethod), callInstr.stmt.pc) .map(_.method) .collect { - case d: DefinedMethod ⇒ List(d.definedMethod) - case md: MultipleDefinedMethods ⇒ md.definedMethods + case d: DefinedMethod => List(d.definedMethod) + case md: MultipleDefinedMethods => md.definedMethods } .flatten .filter(_.body.isDefined) @@ -98,11 +96,11 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta callers .callers(declaredMethod) .flatMap { - case (method, pc, true) ⇒ + case (method, pc, true) => val TACode(_, code, pcToIndex, cfg, _) = tacai(method.definedMethod) val index = pcToIndex(pc) Some(JavaStatement(method.definedMethod, index, code, cfg)) - case _ ⇒ None + case _ => None } .toSet .asJava @@ -112,10 +110,10 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta val TACode(_, code, _, cfg, _) = tacai(m) code.zipWithIndex .collect { - case (mc: MethodCall[V], index) ⇒ JavaStatement(m, index, code, cfg) - case (as @ Assignment(_, _, _: FunctionCall[V]), index) ⇒ + case (mc: MethodCall[V], index) => JavaStatement(m, index, code, cfg) + case (as @ Assignment(_, _, _: FunctionCall[V]), index) => JavaStatement(m, index, code, cfg) - case (ex @ ExprStmt(_, _: FunctionCall[V]), index) ⇒ + case (ex @ ExprStmt(_, _: FunctionCall[V]), index) => JavaStatement(m, index, code, cfg) } .toSet @@ -127,17 +125,17 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta override def isCallStmt(stmt: JavaStatement): Boolean = { def isCallExpr(expr: Expr[V]) = expr.astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => true - case _ ⇒ false + case _ => false } stmt.stmt.astID match { - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => true - case Assignment.ASTID ⇒ isCallExpr(stmt.stmt.asAssignment.expr) - case ExprStmt.ASTID ⇒ isCallExpr(stmt.stmt.asExprStmt.expr) - case _ ⇒ false + case Assignment.ASTID => isCallExpr(stmt.stmt.asAssignment.expr) + case ExprStmt.ASTID => isCallExpr(stmt.stmt.asExprStmt.expr) + case _ => false } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index b27fa8751d..b2609ca50d 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -29,11 +29,11 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb * The string parameters of all public methods are entry points. */ override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = - p.allProjectClassFiles.filter(classFile ⇒ + p.allProjectClassFiles.filter(classFile => classFile.thisType.fqn == "java/lang/Class") - .flatMap(classFile ⇒ classFile.methods) + .flatMap(classFile => classFile.methods) .filter(_.name == "forName") - .map(method ⇒ declaredMethods(method) → Variable(-2)) + .map(method => declaredMethods(method) -> Variable(-2)) /** * There is no sanitizing in this analysis. @@ -82,10 +82,10 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb if (source._2.isInstanceOf[UnbalancedReturnFact[TaintFact @unchecked]] && canBeCalledFromOutside(source._1) && in.exists { // index < 0 means, that it is a parameter. - case Variable(index) if index < 0 ⇒ true - case ArrayElement(index, _) if index < 0 ⇒ true - case InstanceField(index, _, _) if index < 0 ⇒ true - case _ ⇒ false + case Variable(index) if index < 0 => true + case ArrayElement(index, _) if index < 0 => true + case InstanceField(index, _, _) if index < 0 => true + case _ => false }) { Some(FlowFact(currentCallChain(source).map(JavaMethod(_)))) } else None @@ -109,22 +109,22 @@ class BackwardClassForNameTaintAnalysisRunner extends AbsractIFDSAnalysisRunner override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = { val propertyKey = BackwardClassForNameTaintAnalysisScheduler.property.key val flowFactsAtSources = ps.entities(propertyKey).collect { - case EPS((m: DefinedMethod, inputFact)) if canBeCalledFromOutside(m, ps) ⇒ + case EPS((m: DefinedMethod, inputFact)) if canBeCalledFromOutside(m, ps) => (m, inputFact) }.flatMap(ps(_, propertyKey) match { - case FinalEP(_, OldTaint(result, _)) ⇒ - result.values.fold(Set.empty)((acc, facts) ⇒ acc ++ facts).filter { - case FlowFact(_) ⇒ true - case _ ⇒ false + case FinalEP(_, OldTaint(result, _)) => + result.values.fold(Set.empty)((acc, facts) => acc ++ facts).filter { + case FlowFact(_) => true + case _ => false } - case _ ⇒ Seq.empty + case _ => Seq.empty }) for { - fact ← flowFactsAtSources + fact <- flowFactsAtSources } { fact match { - case FlowFact(flow) ⇒ println(s"flow: "+flow.asInstanceOf[Seq[Method]].map(_.toJava).mkString(", ")) - case _ ⇒ + case FlowFact(flow) => println(s"flow: "+flow.asInstanceOf[Seq[Method]].map(_.toJava).mkString(", ")) + case _ => } } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala index 413d5e1314..276606a827 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -31,10 +31,10 @@ class ForwardClassForNameTaintProblem(project: SomeProject) * The string parameters of all public methods are entry points. */ override def entryPoints: Seq[(DeclaredMethod, TaintFact)] = for { - m ← methodsCallableFromOutside.toSeq + m <- methodsCallableFromOutside.toSeq if !m.definedMethod.isNative - index ← m.descriptor.parameterTypes.zipWithIndex.collect { - case (pType, index) if pType == ObjectType.String ⇒ index + index <- m.descriptor.parameterTypes.zipWithIndex.collect { + case (pType, index) if pType == ObjectType.String => index } } yield (m, Variable(-2 - index)) @@ -70,9 +70,9 @@ class ForwardClassForNameTaintProblem(project: SomeProject) */ override protected def relevantCallee(callee: DeclaredMethod): Boolean = callee.descriptor.parameterTypes.exists { - case ObjectType.Object ⇒ true - case ObjectType.String ⇒ true - case _ ⇒ false + case ObjectType.Object => true + case ObjectType.String => true + case _ => false } && (!canBeCalledFromOutside(callee) || isClassForName(callee)) /** @@ -101,13 +101,13 @@ class ForwardClassForNameAnalysisRunner extends AbsractIFDSAnalysisRunner { override def printAnalysisResults(analysis: AbstractIFDSAnalysis[_], ps: PropertyStore): Unit = for { - e ← analysis.ifdsProblem.entryPoints + e <- analysis.ifdsProblem.entryPoints flows = ps(e, ForwardClassForNameTaintAnalysis$Scheduler.property.key) - fact ← flows.ub.asInstanceOf[IFDSProperty[DeclaredMethodJavaStatement, TaintFact]].flows.values.flatten.toSet[TaintFact] + fact <- flows.ub.asInstanceOf[IFDSProperty[DeclaredMethodJavaStatement, TaintFact]].flows.values.flatten.toSet[TaintFact] } { fact match { - case FlowFact(flow) ⇒ println(s"flow: "+flow.asInstanceOf[Set[Method]].map(_.toJava).mkString(", ")) - case _ ⇒ + case FlowFact(flow) => println(s"flow: "+flow.asInstanceOf[Set[Method]].map(_.toJava).mkString(", ")) + case _ => } } } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala index 7c674cf6d2..270d4ee496 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala @@ -21,7 +21,7 @@ class ForwardTaintAnalysisFixtureTest extends PropertiesTest { p.updateProjectInformationKeyInitializationData( AIDomainFactoryKey )( - (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ + (_: Option[Set[Class[_ <: AnyRef]]]) => Set[Class[_ <: AnyRef]]( classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] ) @@ -33,7 +33,7 @@ class ForwardTaintAnalysisFixtureTest extends PropertiesTest { val testContext = executeAnalyses(ForwardTaintAnalysisFixtureScheduler) val project = testContext.project val eas = methodsWithAnnotations(project).map { - case (method, entityString, annotations) ⇒ + case (method, entityString, annotations) => ((method, TaintNullFact), entityString, annotations) } testContext.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala index 492d15bbe7..d6d01df09e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala @@ -21,7 +21,7 @@ class BackwardTaintAnalysisFixtureTest extends PropertiesTest { p.updateProjectInformationKeyInitializationData( AIDomainFactoryKey )( - (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ + (_: Option[Set[Class[_ <: AnyRef]]]) => Set[Class[_ <: AnyRef]]( classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] ) @@ -34,7 +34,7 @@ class BackwardTaintAnalysisFixtureTest extends PropertiesTest { val project = testContext.project val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { - case (methods, entityString, annotations) ⇒ + case (methods, entityString, annotations) => ((declaredMethods(methods), TaintNullFact), entityString, annotations) } testContext.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala index b20f4b5545..470cb0092f 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala @@ -21,7 +21,7 @@ class ForwardTaintAnalysisFixtureTest extends PropertiesTest { p.updateProjectInformationKeyInitializationData( AIDomainFactoryKey )( - (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ + (_: Option[Set[Class[_ <: AnyRef]]]) => Set[Class[_ <: AnyRef]]( classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] ) @@ -34,7 +34,7 @@ class ForwardTaintAnalysisFixtureTest extends PropertiesTest { val project = testContext.project val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { - case (methods, entityString, annotations) ⇒ + case (methods, entityString, annotations) => ((declaredMethods(methods), TaintNullFact), entityString, annotations) } testContext.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/VTATest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/VTATest.scala index c61d0cc7db..1b0e99d240 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/VTATest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/VTATest.scala @@ -17,7 +17,7 @@ class VTATest extends PropertiesTest { p.updateProjectInformationKeyInitializationData( AIDomainFactoryKey )( - (_: Option[Set[Class[_ <: AnyRef]]]) ⇒ + (_: Option[Set[Class[_ <: AnyRef]]]) => Set[Class[_ <: AnyRef]]( classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] ) @@ -30,7 +30,7 @@ class VTATest extends PropertiesTest { val project = testContext.project val declaredMethods = project.get(DeclaredMethodsKey) val eas = methodsWithAnnotations(project).map { - case (methods, entityString, annotations) ⇒ + case (methods, entityString, annotations) => ((declaredMethods(methods), VTANullFact), entityString, annotations) } testContext.propertyStore.shutdown() diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala index 968590cfc6..e5dce50d5b 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala @@ -20,24 +20,24 @@ class BackwardFlowPathMatcher extends AbstractPropertyMatcher { as: Set[ObjectType], entity: Entity, a: AnnotationLike, - properties: Traversable[Property] + properties: Iterable[Property] ): Option[String] = { val method = entity.asInstanceOf[(DefinedMethod, TaintFact)]._1.definedMethod - val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) ⇒ - evp.value.asArrayValue.values.map((value: ElementValue) ⇒ + val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) => + evp.value.asArrayValue.values.map((value: ElementValue) => value.asStringValue.value)).head.toIndexedSeq val propertyStore = p.get(PropertyStoreKey) val propertyKey = BackwardTaintAnalysisFixtureScheduler.property.key val allReachableFlowFacts = propertyStore.entities(propertyKey).collect { - case EPS((m: DefinedMethod, inputFact)) if m.definedMethod == method ⇒ + case EPS((m: DefinedMethod, inputFact)) if m.definedMethod == method => (m, inputFact) }.flatMap(propertyStore(_, propertyKey) match { - case FinalEP(_, OldTaint(result, _)) ⇒ - result.values.fold(Set.empty)((acc, facts) ⇒ acc ++ facts).collect { - case FlowFact(methods) ⇒ methods.map(_.name) + case FinalEP(_, OldTaint(result, _)) => + result.values.fold(Set.empty)((acc, facts) => acc ++ facts).collect { + case FlowFact(methods) => methods.map(_.name) } - case _ ⇒ Seq.empty + case _ => Seq.empty }).toIndexedSeq if (expectedFlow.isEmpty) { if (allReachableFlowFacts.nonEmpty) return Some(s"There should be no flow for $entity") diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala index 641c77611a..9adb47944c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala @@ -18,18 +18,18 @@ class ForwardFlowPathMatcher extends AbstractPropertyMatcher { as: Set[ObjectType], entity: Entity, a: AnnotationLike, - properties: Traversable[Property] + properties: Iterable[Property] ): Option[String] = { - val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) ⇒ - evp.value.asArrayValue.values.map((value: ElementValue) ⇒ + val expectedFlow = a.elementValuePairs.map((evp: ElementValuePair) => + evp.value.asArrayValue.values.map((value: ElementValue) => value.asStringValue.value)).head.toIndexedSeq val flows = properties.filter(_.isInstanceOf[IFDSProperty[_, _]]).head .asInstanceOf[IFDSProperty[_, _]] .flows .values - .fold(Set.empty)((acc, facts) ⇒ acc ++ facts) + .fold(Set.empty)((acc, facts) => acc ++ facts) .collect { - case FlowFact(methods) ⇒ methods.map(_.name).toIndexedSeq + case FlowFact(methods) => methods.map(_.name).toIndexedSeq } if (expectedFlow.isEmpty) { if (flows.nonEmpty) return Some(s"There should be no flow for $entity") diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala index f6cad490df..a421de1b85 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedCalleeMatcher.scala @@ -21,7 +21,7 @@ class ExpectedCalleeMatcher extends VTAMatcher { def validateSingleAnnotation(project: SomeProject, entity: Entity, taCode: TACode[TACMethodParameter, DUVar[ValueInformation]], method: Method, annotation: AnnotationLike, - properties: Traversable[Property]): Option[String] = { + properties: Iterable[Property]): Option[String] = { val elementValuePairs = annotation.elementValuePairs val expected = ( elementValuePairs.head.value.asIntValue.value, @@ -33,18 +33,18 @@ class ExpectedCalleeMatcher extends VTAMatcher { // Get ALL the exit facts for the method for ALL input facts val allReachableExitFacts = propertyStore.entities(propertyKey).collect { - case EPS((m: DefinedMethod, inputFact)) if m.definedMethod == method ⇒ + case EPS((m: DefinedMethod, inputFact)) if m.definedMethod == method => (m, inputFact) }.flatMap(propertyStore(_, propertyKey) match { - case FinalEP(_, VTAResult(result, _)) ⇒ - result.values.fold(Set.empty)((acc, facts) ⇒ acc ++ facts).collect { - case CalleeType(index, t, upperBound) ⇒ + case FinalEP(_, VTAResult(result, _)) => + result.values.fold(Set.empty)((acc, facts) => acc ++ facts).collect { + case CalleeType(index, t, upperBound) => Seq(( taCode.lineNumber(method.body.get, index).get, referenceTypeToString(t), upperBound )) } - case _ ⇒ Seq.empty + case _ => Seq.empty }).flatten if (allReachableExitFacts.contains(expected)) None else Some(expected.toString) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala index 697db17850..8cdee1f8c9 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/ExpectedTypeMatcher.scala @@ -17,7 +17,7 @@ class ExpectedTypeMatcher extends VTAMatcher { def validateSingleAnnotation(project: SomeProject, entity: Entity, taCode: TACode[TACMethodParameter, DUVar[ValueInformation]], method: Method, annotation: AnnotationLike, - properties: Traversable[Property]): Option[String] = { + properties: Iterable[Property]): Option[String] = { val elementValuePairs = annotation.elementValuePairs val expected = ( elementValuePairs.head.value.asIntValue.value, @@ -28,9 +28,9 @@ class ExpectedTypeMatcher extends VTAMatcher { .asInstanceOf[VTAResult] .flows .values - .fold(Set.empty)((acc, facts) ⇒ acc ++ facts) + .fold(Set.empty)((acc, facts) => acc ++ facts) .collect { - case VariableType(definedBy, t, upperBound) ⇒ + case VariableType(definedBy, t, upperBound) => (taCode.lineNumber(method.body.get, definedBy).get, referenceTypeToString(t), upperBound) } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala index 9d2e658ccc..9162e95d7e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/vta/VTAMatcher.scala @@ -28,18 +28,18 @@ abstract class VTAMatcher extends AbstractPropertyMatcher { as: Set[ObjectType], entity: Entity, a: AnnotationLike, - properties: Traversable[Property] + properties: Iterable[Property] ): Option[String] = { val method = entity.asInstanceOf[(DefinedMethod, VTAFact)]._1.definedMethod val taCode = p.get(PropertyStoreKey)(method, TACAI.key) match { - case FinalP(TheTACAI(tac)) ⇒ tac - case _ ⇒ + case FinalP(TheTACAI(tac)) => tac + case _ => throw new IllegalStateException( "TAC of annotated method not present after analysis" ) } val result = a.elementValuePairs(0).value.asArrayValue.values - .map(annotationValue ⇒ + .map(annotationValue => validateSingleAnnotation(p, entity, taCode, method, annotationValue.asAnnotationValue.annotation, properties)).filter(_.isDefined) if (result.isEmpty) None @@ -49,11 +49,11 @@ abstract class VTAMatcher extends AbstractPropertyMatcher { def validateSingleAnnotation(project: SomeProject, entity: Entity, taCode: TACode[TACMethodParameter, DUVar[ValueInformation]], method: Method, annotation: AnnotationLike, - properties: Traversable[Property]): Option[String] + properties: Iterable[Property]): Option[String] def referenceTypeToString(t: ReferenceType): String = t match { - case objectType: ObjectType ⇒ objectType.simpleName - case arrayType: ArrayType ⇒ + case objectType: ObjectType => objectType.simpleName + case arrayType: ArrayType => referenceTypeToString(arrayType.elementType.asReferenceType)+"[]" } } \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala index 0a41e9f9b6..84e111556c 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala @@ -27,13 +27,13 @@ class CrossLanguageForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matcher ) project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( - current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.ll") + current => List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.ll") ) project.get(LLVMProjectKey) project.get(RTACallGraphKey) val manager = project.get(FPCFAnalysesManagerKey) val (ps, analyses) = manager.runAll(JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler) - for (method ← project.allMethodsWithBody) { + for (method <- project.allMethodsWithBody) { val flows = ps((method, TaintNullFact), JavaForwardTaintAnalysisScheduler.property.key) println("---METHOD: "+method.toJava+" ---") @@ -44,10 +44,10 @@ class CrossLanguageForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matcher .flatten .toSet[TaintFact] .flatMap { - case FlowFact(flow) ⇒ Some(flow) - case _ ⇒ None + case FlowFact(flow) => Some(flow) + case _ => None } - for (flow ← flowFacts) + for (flow <- flowFacts) println(s"flow: "+flow.map(_.name).mkString(", ")) if (method.name.contains("no_flow")) { it(s"${method.name} has no flow") { @@ -63,10 +63,10 @@ class CrossLanguageForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matcher val function: Function = project.get(LLVMProjectKey).function("Java_TaintTest_native_1array_1tainted").get val debugData = ps((LLVMFunction(function), NativeTaintNullFact), NativeForwardTaintAnalysisScheduler.property.key).ub.asInstanceOf[IFDSProperty[LLVMStatement, NativeTaintFact]].debugData for { - bb ← function.basicBlocks - instruction ← bb.instructions + bb <- function.basicBlocks + instruction <- bb.instructions } { - for (fact ← debugData.getOrElse(LLVMStatement(instruction), Set.empty)) + for (fact <- debugData.getOrElse(LLVMStatement(instruction), Set.empty)) println("\t"+fact) println(instruction.repr) } diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala index c3e524d29d..565cb4344e 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/SimplePurityTests.scala @@ -13,9 +13,9 @@ import scala.collection.immutable.List class SimplePurityTests extends AnyFunSpec with Matchers { describe("SimplePurityAnalysis") { it("executes") { - val project = Project(Traversable.empty) + val project = Project(Iterable.empty) project.updateProjectInformationKeyInitializationData(LLVMProjectKey)( - current ⇒ List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll") + current => List("./DEVELOPING_OPAL/validate/src/test/resources/llvm/purity.ll") ) val (propertyStore, _) = project.get(FPCFAnalysesManagerKey).runAll(EagerSimplePurityAnalysis) diff --git a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala index 54b90a2af1..cbf2456d4b 100644 --- a/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala +++ b/OPAL/br/src/main/scala/org/opalj/br/analyses/cg/EntryPointFinder.scala @@ -370,9 +370,9 @@ object AndroidEntryPointsFinder extends EntryPointFinder { "android/location/LocationListener" -> locationListenerEPS, "android/location/onNmeaMessageListener" -> onNmeaMessageListenerEPS) - override def collectEntryPoints(project: SomeProject): Traversable[Method] = { + override def collectEntryPoints(project: SomeProject): Iterable[Method] = { val eps = ArrayBuffer.empty[Method] - for ((superClass, methodList) ← defaultEPS) { + for ((superClass, methodList) <- defaultEPS) { eps ++= findEPS(ObjectType(superClass), methodList, project) } eps @@ -381,8 +381,8 @@ object AndroidEntryPointsFinder extends EntryPointFinder { def findEPS(ot: ObjectType, possibleEPS: List[String], project: SomeProject): ArrayBuffer[Method] = { val eps = ArrayBuffer.empty[Method] val classHierarchy = project.classHierarchy - classHierarchy.foreachSubclass(ot, project) { sc ⇒ - for (pep ← possibleEPS; m ← sc.findMethod(pep) if m.body.isDefined && !eps.contains(m)) { + classHierarchy.foreachSubclass(ot, project) { sc => + for (pep <- possibleEPS; m <- sc.findMethod(pep) if m.body.isDefined && !eps.contains(m)) { eps += m } } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/DataFlowAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/DataFlowAnalysis.scala index 5897a62b5e..e561e1dea2 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/DataFlowAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/DataFlowAnalysis.scala @@ -13,7 +13,7 @@ abstract class DataFlowAnalysis[Facts >: Null <: AnyRef, C <: AnyRef, S <: State var facts = Map.empty[S, Facts] val workList = new mutable.Queue[S]() - for (entryStatement ← icfg.startStatements(callable)) { + for (entryStatement <- icfg.startStatements(callable)) { facts = facts.updated(entryStatement, entryFacts) workList.enqueue(entryStatement) } @@ -22,14 +22,14 @@ abstract class DataFlowAnalysis[Facts >: Null <: AnyRef, C <: AnyRef, S <: State val statement = workList.dequeue() val inFacts = facts.get(statement).get - for (successor ← icfg.nextStatements(statement)) { + for (successor <- icfg.nextStatements(statement)) { val newOutFacts = transferFunction(inFacts, statement, successor) facts.get(successor) match { - case None ⇒ { + case None => { facts = facts.updated(successor, newOutFacts) workList.enqueue(successor) } - case Some(existingOutFacts) ⇒ { + case Some(existingOutFacts) => { val outFacts = join(existingOutFacts, newOutFacts) if (outFacts ne existingOutFacts) { facts = facts.updated(successor, outFacts) diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala index e33548b660..4d3d2c8bb1 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/ICFG.scala @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ifds -import scala.collection.{Set ⇒ SomeSet} +import scala.collection.{Set => SomeSet} abstract class ICFG[C <: AnyRef, S <: Statement[_ <: C, _]] { /** diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDS.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDS.scala index accd57e752..075ad7dc26 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDS.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDS.scala @@ -13,14 +13,14 @@ object IFDS { */ def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { var result = map1 - for ((key, values) ← map2) { + for ((key, values) <- map2) { result.get(key) match { - case Some(resultValues) ⇒ + case Some(resultValues) => if (resultValues.size > values.size) result = result.updated(key, resultValues ++ values) else result = result.updated(key, values ++ resultValues) - case None ⇒ + case None => result = result.updated(key, values) } } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 7ff0bd9772..30dd76b442 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -6,7 +6,7 @@ import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} import org.opalj.fpcf._ import org.opalj.ifds.Dependees.Getter -import scala.collection.{mutable, Set ⇒ SomeSet} +import scala.collection.{mutable, Set => SomeSet} case class Dependees[Work]() { case class Dependee(eOptionP: SomeEOptionP, worklist: Set[Work] = Set.empty) @@ -14,14 +14,14 @@ case class Dependees[Work]() { def get(entity: Entity, propertyKey: PropertyKey[Property])(implicit propertyStore: PropertyStore, work: Work): SomeEOptionP = { val epk = EPK(entity, propertyKey) val dependee = dependees.get(epk) match { - case Some(dependee) ⇒ Dependee(dependee.eOptionP, dependee.worklist + work) - case None ⇒ Dependee(propertyStore(epk), Set(work)) + case Some(dependee) => Dependee(dependee.eOptionP, dependee.worklist + work) + case None => Dependee(propertyStore(epk), Set(work)) } if (!dependee.eOptionP.isFinal) dependees += epk -> dependee dependee.eOptionP } - def forResult(): Set[SomeEOptionP] = { + def forResult: Set[SomeEOptionP] = { dependees.values.map(_.eOptionP).toSet } def takeWork(epk: SomeEPK): Set[Work] = { @@ -31,11 +31,11 @@ case class Dependees[Work]() { } def getter()(implicit propertyStore: PropertyStore, work: Work): Getter = - (entity: Entity, propertyKey: PropertyKey[Property]) ⇒ get(entity, propertyKey) + (entity: Entity, propertyKey: PropertyKey[Property]) => get(entity, propertyKey) } object Dependees { - type Getter = (Entity, PropertyKey[Property]) ⇒ SomeEOptionP + type Getter = (Entity, PropertyKey[Property]) => SomeEOptionP } /** @@ -58,29 +58,29 @@ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C] def add(statement: S, fact: IFDSFact, predecessor: Option[S] = None): Boolean = { // TODO: subsuming edges.get(statement) match { - case None ⇒ + case None => predecessor match { - case Some(predecessor) ⇒ + case Some(predecessor) => edges = edges.updated(statement, Right(Map(predecessor -> Set(fact)))) - case None ⇒ + case None => edges = edges.updated(statement, Left(Set(fact))) } true - case Some(Left(existingFacts)) ⇒ + case Some(Left(existingFacts)) => if (predecessor.isDefined) throw new IllegalArgumentException(s"${statement} does not accept a predecessor") val isNew = !existingFacts.contains(fact) edges = edges.updated(statement, Left(existingFacts + fact)) isNew - case Some(Right(existingFacts)) ⇒ + case Some(Right(existingFacts)) => predecessor match { - case None ⇒ throw new IllegalArgumentException(s"${statement} requires a predecessor") - case Some(predecessor) ⇒ existingFacts.get(statement) match { - case Some(existingPredecessorFacts) ⇒ { + case None => throw new IllegalArgumentException(s"${statement} requires a predecessor") + case Some(predecessor) => existingFacts.get(statement) match { + case Some(existingPredecessorFacts) => { val isNew = !existingPredecessorFacts.contains(fact) edges = edges.updated(statement, Right(existingFacts.updated(predecessor, existingPredecessorFacts + fact))) isNew } - case None ⇒ { + case None => { edges = edges.updated(statement, Right(existingFacts.updated(predecessor, Set(fact)))) true } @@ -95,10 +95,10 @@ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C] */ def get(statement: S): Option[Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] = edges.get(statement) - def debugData: Map[S, Set[IFDSFact]] = edges.foldLeft(Map.empty[S, Set[IFDSFact]])((result, elem) ⇒ { + def debugData: Map[S, Set[IFDSFact]] = edges.foldLeft(Map.empty[S, Set[IFDSFact]])((result, elem) => { val facts: Set[IFDSFact] = elem._2 match { - case Right(facts) ⇒ facts.foldLeft(Set.empty[IFDSFact])(_ ++ _._2) - case Left(facts) ⇒ facts + case Right(facts) => facts.foldLeft(Set.empty[IFDSFact])(_ ++ _._2) + case Left(facts) => facts } result.updated(elem._1, result.getOrElse(elem._1, Set.empty) ++ facts) }) @@ -164,7 +164,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < implicit val state: State = new IFDSState[IFDSFact, C, S, Work](entity) implicit val queue: Worklist = mutable.Queue .empty[Work] - icfg.startStatements(function).foreach { start ⇒ + icfg.startStatements(function).foreach { start => state.pathEdges.add(start, sourceFact) // ifds line 2 queue.enqueue((start, sourceFact, None)) // ifds line 3 } @@ -206,7 +206,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < * @return A map, mapping from each exit statement to the facts, which flow into exit statement. */ private def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { - state.endSummaries.foldLeft(Map.empty[S, Set[IFDSFact]])((result, entry) ⇒ + state.endSummaries.foldLeft(Map.empty[S, Set[IFDSFact]])((result, entry) => result.updated(entry._1, result.getOrElse(entry._1, Set.empty[IFDSFact]) + entry._2)) } @@ -237,8 +237,8 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < implicit val work = worklist.dequeue() // ifds line 11 val (statement, in, predecessor) = work icfg.getCalleesIfCallStatement(statement) match { - case Some(callees) ⇒ handleCall(statement, callees, in) // ifds line 13 - case None ⇒ { + case Some(callees) => handleCall(statement, callees, in) // ifds line 13 + case None => { if (icfg.isExitStatement(statement)) handleExit(statement, in) // ifds line 21 // in case of exceptions exit statements may also have some normal flow so no else here handleOther(statement, in, predecessor) // ifds line 33 @@ -268,28 +268,28 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < work: Work ): Unit = { val successors = icfg.nextStatements(call) - for (callee ← callees) { + for (callee <- callees) { ifdsProblem.outsideAnalysisContext(callee) match { - case Some(outsideAnalysisHandler) ⇒ + case Some(outsideAnalysisHandler) => // Let the concrete analysis decide what to do. for { - successor ← successors - out ← outsideAnalysisHandler(call, successor, in, state.dependees.getter()) // ifds line 17 (only summary edges) + successor <- successors + out <- outsideAnalysisHandler(call, successor, in, state.dependees.getter()) // ifds line 17 (only summary edges) } { propagate(successor, out, call) // ifds line 18 } - case None ⇒ + case None => for { - successor ← successors - out ← concreteCallFlow(call, callee, in, successor) // ifds line 17 (only summary edges) + successor <- successors + out <- concreteCallFlow(call, callee, in, successor) // ifds line 17 (only summary edges) } { propagate(successor, out, call) // ifds line 18 } } } for { - successor ← successors - out ← callToReturnFlow(call, in, successor) // ifds line 17 (without summary edge propagation) + successor <- successors + out <- callToReturnFlow(call, in, successor) // ifds line 17 (without summary edge propagation) } { propagate(successor, out, call) // ifds line 18 } @@ -298,7 +298,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < private def concreteCallFlow(call: S, callee: C, in: IFDSFact, successor: S)(implicit state: State, work: Work): Set[IFDSFact] = { var result = Set.empty[IFDSFact] val entryFacts = callFlow(call, callee, in) - for (entryFact ← entryFacts) { // ifds line 14 + for (entryFact <- entryFacts) { // ifds line 14 val e = (callee, entryFact) val exitFacts: Map[S, Set[IFDSFact]] = if (e == state.source) { // handle self dependency on our own because property store can't handle it @@ -308,17 +308,17 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < // handle all other dependencies using property store val callFlows = state.dependees.get(e, propertyKey.key).asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] callFlows match { - case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ + case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] => ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] => ep.ub.flows - case _ ⇒ + case _ => Map.empty } } for { - (exitStatement, exitStatementFacts) ← exitFacts // ifds line 15.2 - exitStatementFact ← exitStatementFacts // ifds line 15.3 + (exitStatement, exitStatementFacts) <- exitFacts // ifds line 15.2 + exitStatementFact <- exitStatementFacts // ifds line 15.3 } { result ++= returnFlow(exitStatement, exitStatementFact, call, in, successor) } @@ -330,7 +330,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < val newEdge = (statement, in) if (!state.endSummaries.contains(newEdge)) { state.endSummaries += ((statement, in)) // ifds line 21.1 - state.selfDependees.foreach(selfDependee ⇒ + state.selfDependees.foreach(selfDependee => worklist.enqueue(selfDependee)) } // ifds lines 22 - 31 are handled by the dependency propagation of the property store @@ -339,8 +339,8 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < private def handleOther(statement: S, in: IFDSFact, predecessor: Option[S])(implicit state: State, worklist: Worklist): Unit = { for { // ifds line 34 - successor ← icfg.nextStatements(statement) - out ← normalFlow(statement, in, predecessor) + successor <- icfg.nextStatements(statement) + out <- normalFlow(statement, in, predecessor) } { propagate(successor, out, statement) // ifds line 35 } @@ -431,7 +431,7 @@ abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { val ifdsAnalysis = analysis.asInstanceOf[IFDSAnalysis[IFDSFact, C, S]] - for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { + for (e <- ifdsAnalysis.ifdsProblem.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala index f09a3a8246..d7179fc451 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -96,7 +96,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: State def needsPredecessor(statement: S): Boolean - type OutsideAnalysisContextHandler = ((S, S, IFDSFact, Getter) ⇒ Set[IFDSFact]) { + type OutsideAnalysisContextHandler = ((S, S, IFDSFact, Getter) => Set[IFDSFact]) { def apply(call: S, successor: S, in: IFDSFact, dependeesGetter: Getter): Set[IFDSFact] } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala index 0da5e3d16a..85a491e788 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProperty.scala @@ -29,12 +29,12 @@ abstract class IFDSProperty[S, IFDSFact <: AbstractIFDSFact] def debugData: Map[S, Set[IFDSFact]] override def equals(other: Any): Boolean = other match { - case that: IFDSProperty[S, IFDSFact] ⇒ + case that: IFDSProperty[S @unchecked, IFDSFact @unchecked] => // We cached the "hashCode" to make the following comparison more efficient; // note that all properties are eventually added to some set and therefore // the hashCode is required anyway! (this eq that) || (this.hashCode == that.hashCode && this.flows == that.flows) - case _ ⇒ + case _ => false } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/Statement.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/Statement.scala index c98c245e7c..886fe3c906 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/Statement.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/Statement.scala @@ -2,6 +2,6 @@ package org.opalj.ifds abstract class Statement[C, Node] { - def node(): Node - def callable(): C + def node: Node + def callable: C } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala index 2f39fc55a8..c7d5fb5d97 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/ICFG.scala @@ -3,7 +3,7 @@ package org.opalj.ifds.old import org.opalj.ifds.{AbstractIFDSFact, Statement} -import scala.collection.{Set ⇒ SomeSet} +import scala.collection.{Set => SomeSet} abstract class ICFG[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_, Node], Node] { /** diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala index 99a3dcdb5b..17269161c9 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSAnalysis.scala @@ -6,7 +6,7 @@ import org.opalj.br.fpcf.{FPCFAnalysis, FPCFLazyAnalysisScheduler} import org.opalj.fpcf._ import org.opalj.ifds.{AbstractIFDSFact, IFDS, IFDSProperty, IFDSPropertyMetaInformation, Statement} -import scala.collection.{mutable, Set ⇒ SomeSet} +import scala.collection.{mutable, Set => SomeSet} /** * The state of the analysis. For each method and source fact, there is a separate state. @@ -102,7 +102,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, new State(entity, Map(entity -> Set.empty), Map.empty, Set.empty[Node]) val queue = mutable.Queue .empty[QueueEntry] - icfg.startNodes(sourceFact, function).foreach { start ⇒ + icfg.startNodes(sourceFact, function).foreach { start => state.incomingFacts += start -> Set(sourceFact) queue.enqueue((start, Set(sourceFact), None, None, None)) } @@ -138,15 +138,15 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, eps: SomeEPS )(implicit state: State): ProperPropertyComputationResult = { (eps: @unchecked) match { - case FinalE(e: (C, IFDSFact) @unchecked) ⇒ + case FinalE(e: (C, IFDSFact) @unchecked) => reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) case interimEUBP @ InterimEUBP( e: (C, IFDSFact) @unchecked, ub: IFDSProperty[S, IFDSFact] @unchecked - ) ⇒ + ) => if (ub.flows.values - .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { + .forall(facts => facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { // Do not re-analyze the caller if we only get the null fact. // Update the pendingIfdsDependee entry to the new interim result. state.pendingIfdsDependees += @@ -155,11 +155,11 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1, Some(e._2)) - case FinalEP(_: C @unchecked, _: Any) ⇒ + case FinalEP(_: C @unchecked, _: Any) => // TODO: Any was Callees, how to verify this? reAnalyzeBasicBlocks(state.pendingCgCallSites) - case InterimEUBP(_: C @unchecked, _: Any) ⇒ + case InterimEUBP(_: C @unchecked, _: Any) => // TODO: Any was Callees, how to verify this? reAnalyzeBasicBlocks(state.pendingCgCallSites) } @@ -198,7 +198,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, * analyzed with the `inputFacts`. */ if (calleeWithUpdateFact.isEmpty) - for (successor ← successors) { + for (successor <- successors) { statistics.numberOfCalls.callToReturnFlow += 1 statistics.sumOfInputfactsForCallbacks += in.size summaryEdges += successor -> @@ -208,16 +208,16 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, ) } - for (callee ← callees) { + for (callee <- callees) { ifdsProblem.outsideAnalysisContext(callee) match { - case Some(handler) ⇒ + case Some(handler) => // Let the concrete analysis decide what to do. for { - successor ← successors + successor <- successors } summaryEdges += successor -> (summaryEdges(successor) ++ handler(call, successor, in)) - case None ⇒ + case None => val callToStart = if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) else { @@ -225,7 +225,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, } var allNewExitFacts: Map[S, Set[IFDSFact]] = Map.empty // Collect exit facts for each input fact separately - for (fact ← callToStart) { + for (fact <- callToStart) { /* * If this is a recursive call with the same input facts, we assume that the * call only produces the facts that are already known. The call site is added to @@ -246,11 +246,11 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, .asInstanceOf[EOptionP[(C, IFDSFact), IFDSProperty[S, IFDSFact]]] val oldValue = state.pendingIfdsDependees.get(e) val oldExitFacts: Map[S, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]]) ⇒ ep.ub.flows - case _ ⇒ Map.empty + case Some(ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]]) => ep.ub.flows + case _ => Map.empty } val exitFacts: Map[S, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] ⇒ + case ep: FinalEP[_, IFDSProperty[S, IFDSFact]] => if (state.pendingIfdsCallSites.contains(e) && state.pendingIfdsCallSites(e).nonEmpty) { val newDependee = @@ -259,7 +259,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, } state.pendingIfdsDependees -= e ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[S, IFDSFact]] => /* * Add the call site to `pendingIfdsCallSites` and * `pendingIfdsDependees` and continue with the facts in the interim @@ -268,7 +268,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, */ addIfdsDependee(e, callFlows, basicBlock, call) ep.ub.flows - case _ ⇒ + case _ => addIfdsDependee(e, callFlows, basicBlock, call) Map.empty } @@ -307,10 +307,10 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, protected def collectResult(implicit state: State): Map[S, Set[IFDSFact]] = { var result = Map.empty[S, Set[IFDSFact]] state.outgoingFacts.foreach( - blockFacts ⇒ + blockFacts => blockFacts._2.get(None) match { - case Some(facts) ⇒ result += icfg.lastStatement(blockFacts._1) -> facts - case None ⇒ + case Some(facts) => result += icfg.lastStatement(blockFacts._1) -> facts + case None => } ) result @@ -342,8 +342,8 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, // First process for normal returns, then abnormal returns. var result = summaryEdges for { - successor ← successors - exitStatement ← exitFacts.keys + successor <- successors + exitStatement <- exitFacts.keys } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) result } @@ -375,10 +375,10 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, calleeWithUpdate, calleeWithUpdateFact ) - val allOut = IFDS.mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) + val allOut = IFDS.mergeMaps(oldOut, nextOut).view.mapValues(facts => subsume(facts, project)).toMap state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) - for (successor ← icfg.nextNodes(basicBlock)) { + for (successor <- icfg.nextNodes(basicBlock)) { if (icfg.isLastNode(successor)) { // Re-analyze recursive call sites with the same input fact. val nextOutSuccessors = nextOut.get(Some(successor)) @@ -488,7 +488,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, var result: Map[Option[Node], Set[IFDSFact]] = if (calleesO.isEmpty) { var result: Map[Option[Node], Set[IFDSFact]] = Map.empty - for (node ← icfg.nextNodes(basicBlock)) { + for (node <- icfg.nextNodes(basicBlock)) { statistics.numberOfCalls.normalFlow += 1 statistics.sumOfInputfactsForCallbacks += in.size result += Some(node) -> ifdsProblem.normalFlow( @@ -505,10 +505,10 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, result } else handleCall(basicBlock, statement, calleesO.get, flows, callFact) - .map(entry ⇒ Some(entry._1.node) -> entry._2) + .map(entry => Some(entry._1.node) -> entry._2) // Propagate the null fact. - result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) + result = result.map(result => result._1 -> propagateNullFact(in, result._2)) result } @@ -519,7 +519,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, */ private def reAnalyzeBasicBlocks(basicBlocks: Set[Node])(implicit state: State): Unit = { val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty - for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) + for (bb <- basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) process(queue) } @@ -536,7 +536,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[C, fact: Option[IFDSFact] )(implicit state: State): Unit = { val queue: mutable.Queue[QueueEntry] = mutable.Queue.empty - for (callSite ← callSites) { + for (callSite <- callSites) { val node = callSite.node queue.enqueue((node, state.incomingFacts(node), Some(callSite), Some(callee), fact)) } @@ -634,7 +634,7 @@ abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { val ifdsAnalysis = analysis.asInstanceOf[IFDSAnalysis[IFDSFact, C, S, Node]] - for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { + for (e <- ifdsAnalysis.ifdsProblem.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala index f05042bc88..21f6934963 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/IFDSProblem.scala @@ -94,7 +94,7 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: State source: (C, IFDSFact) ): Set[IFDSFact] - type OutsideAnalysisContextHandler = ((S, S, Set[IFDSFact]) ⇒ Set[IFDSFact]) { + type OutsideAnalysisContextHandler = ((S, S, Set[IFDSFact]) => Set[IFDSFact]) { def apply(call: S, successor: S, in: Set[IFDSFact]): Set[IFDSFact] } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala index 02c74965f1..66438926ef 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsumable.scala @@ -38,7 +38,7 @@ trait Subsumable[S, IFDSFact <: AbstractIFDSFact] { oldFacts: Set[T], project: SomeProject ): Boolean = - newFacts.exists(newFact ⇒ !oldFacts.contains(newFact)) + newFacts.exists(newFact => !oldFacts.contains(newFact)) /** * Filters the new information from a new set of exit facts given the already known exit facts. @@ -57,7 +57,7 @@ trait Subsumable[S, IFDSFact <: AbstractIFDSFact] { project: SomeProject ): Map[S, Set[T]] = { var result = newExitFacts - for ((key, values) ← oldExitFacts) { + for ((key, values) <- oldExitFacts) { result = result.updated(key, result(key) -- values) } result diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala index 49032a6317..93c4cdf04c 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala @@ -18,8 +18,8 @@ trait Subsuming[S, IFDSFact <: SubsumableFact] extends Subsumable[S, IFDSFact] { */ override protected def subsume[T <: IFDSFact](facts: Set[T], project: SomeProject): Set[T] = { val result = facts.foldLeft(facts) { - (result, fact) ⇒ - if (facts.exists(other ⇒ other != fact && other.subsumes(fact, project))) result - fact + (result, fact) => + if (facts.exists(other => other != fact && other.subsumes(fact, project))) result - fact else result } numberOfSubsumptions.triesToSubsume += 1 @@ -37,7 +37,7 @@ trait Subsuming[S, IFDSFact <: SubsumableFact] extends Subsumable[S, IFDSFact] { * In most cases, the fact will be contained in the old facts. * This is why we first do the contains check before linearly iterating over the old facts. */ - fact ⇒ !(oldFacts.contains(fact) || oldFacts.exists(_.subsumes(fact, project))) + fact => !(oldFacts.contains(fact) || oldFacts.exists(_.subsumes(fact, project))) } /** @@ -48,13 +48,13 @@ trait Subsuming[S, IFDSFact <: SubsumableFact] extends Subsumable[S, IFDSFact] { oldExitFacts: Map[S, Set[T]], project: SomeProject ): Map[S, Set[T]] = newExitFacts.keys.map { - statement ⇒ + statement => val old = oldExitFacts.get(statement) val newFacts = newExitFacts(statement) val newInformation = if (old.isDefined && old.get.nonEmpty) notSubsumedBy(newFacts, old.get, project) else newFacts - statement → newInformation + statement -> newInformation }.toMap /** @@ -63,7 +63,7 @@ trait Subsuming[S, IFDSFact <: SubsumableFact] extends Subsumable[S, IFDSFact] { override protected def notSubsumedBy[T <: IFDSFact](facts: Set[T], otherFacts: Set[T], project: SomeProject): Set[T] = { val result = facts.foldLeft(facts) { - (result, fact) ⇒ + (result, fact) => if (otherFacts.contains(fact) || otherFacts.exists(_.subsumes(fact, project))) result - fact else result diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/package.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/package.scala index a449e56348..8bfcc78717 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/package.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/package.scala @@ -16,7 +16,7 @@ package object ifds { assert(false) // <= test whether assertions are turned on or off... info(FrameworkName, "Production Build") } catch { - case _: AssertionError ⇒ info(FrameworkName, "Development Build with Assertions") + case _: AssertionError => info(FrameworkName, "Development Build with Assertions") } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala index c0aa2fb870..f299f70a5b 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/LLVMProject.scala @@ -5,7 +5,7 @@ import org.opalj.ll.llvm.{Module, Reader, value} class LLVMProject(val modules: Iterable[Module]) { def functions: Iterable[value.Function] = - modules.flatMap(module ⇒ module.functions) + modules.flatMap(module => module.functions) def function(name: String): Option[value.Function] = functions.find(_.name == name) @@ -13,7 +13,7 @@ class LLVMProject(val modules: Iterable[Module]) { object LLVMProject { def apply(modules_paths: Iterable[String]): LLVMProject = { - val modules = modules_paths.map(path ⇒ Reader.readIR(path).get) + val modules = modules_paths.map(path => Reader.readIR(path).get) val project = new LLVMProject(modules) project } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala index 2f9236a9db..07e430b044 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/SimplePurityAnalysis.scala @@ -14,14 +14,14 @@ sealed trait SimplePurityPropertyMetaInformation extends PropertyMetaInformation sealed trait SimplePurity extends SimplePurityPropertyMetaInformation with OrderedProperty { def meet(other: SimplePurity): SimplePurity = { (this, other) match { - case (Pure, Pure) ⇒ Pure - case (_, _) ⇒ Impure + case (Pure, Pure) => Pure + case (_, _) => Impure } } override def checkIsEqualOrBetterThan(e: Entity, other: SimplePurity): Unit = { if (meet(other) != other) { - throw new IllegalArgumentException(s"$e: impossible refinement: $other ⇒ $this") + throw new IllegalArgumentException(s"$e: impossible refinement: $other => $this") } } @@ -45,13 +45,13 @@ class SimplePurityAnalysis(val project: SomeProject) extends FPCFAnalysis { .basicBlocks .flatMap(_.instructions) .foreach { - case instruction: Store ⇒ + case instruction: Store => instruction.dst match { - case _: GlobalVariable ⇒ + case _: GlobalVariable => return Result(function, Impure) - case _ ⇒ Unit + case _ => () } - case _ ⇒ Unit + case _ => () } Result(function, Pure) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala index 8e34a09c7d..ded110d5fb 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/JNICallUtil.scala @@ -13,47 +13,47 @@ object JNICallUtil { * This is done by the assumption that every such calls first parameter is a struct of type "struct.JNINativeInterface_" */ def isJNICall(call: Call): Boolean = call.calledFunctionType.params.headOption match { - case Some(firstParam) ⇒ + case Some(firstParam) => firstParam match { - case p1: PointerType ⇒ + case p1: PointerType => p1.element match { - case p2: PointerType ⇒ + case p2: PointerType => p2.element match { - case struct: StructType if struct.name == "struct.JNINativeInterface_" ⇒ + case struct: StructType if struct.name == "struct.JNINativeInterface_" => true - case other ⇒ false + case other => false } - case _ ⇒ false + case _ => false } - case _ ⇒ false + case _ => false } - case _ ⇒ false + case _ => false } def resolve(call: Call)(implicit declaredMethods: DeclaredMethods): Set[_ <: NativeFunction] = resolveJNIFunction(call) match { - case 'CallTypeMethod ⇒ resolveMethodId(call.operand(2)) // methodID is the third parameter - case _ ⇒ Set() + case Symbol("CallTypeMethod") => resolveMethodId(call.operand(2)) // methodID is the third parameter + case _ => Set() } private def resolveJNIFunction(call: Call): Symbol = call.calledValue match { - case load: Load ⇒ + case load: Load => load.src match { // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/functions.html has the indices - case gep: GetElementPtr if gep.isConstant ⇒ gep.constants.tail.head match { - case 31 ⇒ 'GetObjectClass - case 33 ⇒ 'GetMethodId - case 49 | 61 ⇒ 'CallTypeMethod // CallIntMethod | CallVoidMethod - case index ⇒ throw new IllegalArgumentException(s"unknown JNI function index ${index}") + case gep: GetElementPtr if gep.isConstant => gep.constants.tail.head match { + case 31 => Symbol("GetObjectClass") + case 33 => Symbol("GetMethodId") + case 49 | 61 => Symbol("CallTypeMethod") // CallIntMethod | CallVoidMethod + case index => throw new IllegalArgumentException(s"unknown JNI function index ${index}") } - case _ ⇒ throw new IllegalArgumentException("unknown JNI load src") + case _ => throw new IllegalArgumentException("unknown JNI load src") } - case _ ⇒ throw new IllegalArgumentException("unknown JNI call argument") + case _ => throw new IllegalArgumentException("unknown JNI call argument") } private def resolveMethodId(methodId: Value)(implicit declaredMethods: DeclaredMethods): Set[JNIMethod] = { val sources = methodId.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) - sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call]).map(call ⇒ { - if (resolveJNIFunction(call) != 'GetMethodId) throw new IllegalArgumentException("unexpected call") + sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call]).map(call => { + if (resolveJNIFunction(call) != Symbol("GetMethodId")) throw new IllegalArgumentException("unexpected call") if (!resolveClassIsThis(call.operand(1))) // class is the second parameter throw new IllegalArgumentException("unexpected class argument") @@ -69,17 +69,17 @@ object JNICallUtil { } private def resolveString(name: Value): String = name match { - case gep: GetElementPtrConst ⇒ gep.base match { - case global: GlobalVariable ⇒ global.initializer match { - case stringData: ConstantDataArray ⇒ stringData.asString + case gep: GetElementPtrConst => gep.base match { + case global: GlobalVariable => global.initializer match { + case stringData: ConstantDataArray => stringData.asString } } } private def resolveClassIsThis(clazz: Value): Boolean = { val sources = clazz.asInstanceOf[Load].src.users.toSeq.filter(_.isInstanceOf[Store]).map(_.asInstanceOf[Store].src) - sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call]).forall(call ⇒ { - if (resolveJNIFunction(call) != 'GetObjectClass) throw new IllegalArgumentException("unexpected call") + sources.filter(_.isInstanceOf[Call]).map(_.asInstanceOf[Call]).forall(call => { + if (resolveJNIFunction(call) != Symbol("GetObjectClass")) throw new IllegalArgumentException("unexpected call") resolveObjectIsThis(call.operand(1)) // object is the second parameter }) } @@ -90,7 +90,7 @@ object JNICallUtil { } private def findJavaMethods(className: String, name: String, signature: String)(implicit declaredMethods: DeclaredMethods): Set[JNIMethod] = { - declaredMethods.declaredMethods.filter(declaredMethod ⇒ { + declaredMethods.declaredMethods.filter(declaredMethod => { val classType = declaredMethod.declaringClassType (classType.simpleName == className && declaredMethod.name == name && diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala index a9e7ac5fc8..f3717520cd 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeForwardICFG.scala @@ -15,7 +15,7 @@ class NativeForwardICFG(project: SomeProject) extends ICFG[NativeFunction, LLVMS * @return The statements at which the analysis starts. */ override def startStatements(callable: NativeFunction): Set[LLVMStatement] = callable match { - case LLVMFunction(function) ⇒ { + case LLVMFunction(function) => { if (function.basicBlockCount == 0) throw new IllegalArgumentException(s"${callable} does not contain any basic blocks and likely should not be in scope of the analysis") Set(LLVMStatement(function.entryBlock.firstInstruction)) @@ -42,14 +42,14 @@ class NativeForwardICFG(project: SomeProject) extends ICFG[NativeFunction, LLVMS */ override def getCalleesIfCallStatement(statement: LLVMStatement): Option[collection.Set[_ <: NativeFunction]] = { statement.instruction match { - case call: Call ⇒ Some(resolveCallee(call)) - case _ ⇒ None + case call: Call => Some(resolveCallee(call)) + case _ => None } } override def isExitStatement(statement: LLVMStatement): Boolean = statement.instruction match { - case Ret(_) ⇒ true - case _ ⇒ false + case Ret(_) => true + case _ => false } private def resolveCallee(call: Call): Set[_ <: NativeFunction] = diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala index 98f7a156cb..2cf906c9f1 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/NativeIFDSProblem.scala @@ -15,32 +15,32 @@ abstract class NativeIFDSProblem[Fact <: AbstractIFDSFact, JavaFact <: AbstractI val llvmProject = project.get(LLVMProjectKey) val javaPropertyKey: PropertyKey[Property] - override def outsideAnalysisContext(callee: NativeFunction): Option[(LLVMStatement, LLVMStatement, Fact, Getter) ⇒ Set[Fact]] = callee match { - case LLVMFunction(function) ⇒ + override def outsideAnalysisContext(callee: NativeFunction): Option[(LLVMStatement, LLVMStatement, Fact, Getter) => Set[Fact]] = callee match { + case LLVMFunction(function) => function.basicBlockCount match { - case 0 ⇒ Some((_: LLVMStatement, _: LLVMStatement, in: Fact, _: Getter) ⇒ Set(in)) - case _ ⇒ None + case 0 => Some((_: LLVMStatement, _: LLVMStatement, in: Fact, _: Getter) => Set(in)) + case _ => None } - case JNIMethod(method) ⇒ Some(handleJavaMethod(method)) + case JNIMethod(method) => Some(handleJavaMethod(method)) } private def handleJavaMethod(callee: Method)(call: LLVMStatement, successor: LLVMStatement, in: Fact, dependeesGetter: Getter): Set[Fact] = { var result = Set.empty[Fact] val entryFacts = javaCallFlow(call, callee, in) - for (entryFact ← entryFacts) { // ifds line 14 + for (entryFact <- entryFacts) { // ifds line 14 val e = (callee, entryFact) val exitFacts: Map[JavaStatement, Set[JavaFact]] = dependeesGetter(e, javaPropertyKey).asInstanceOf[EOptionP[(JavaStatement, JavaFact), IFDSProperty[JavaStatement, JavaFact]]] match { - case ep: FinalEP[_, IFDSProperty[JavaStatement, JavaFact]] ⇒ + case ep: FinalEP[_, IFDSProperty[JavaStatement, JavaFact]] => ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[JavaStatement, JavaFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[JavaStatement, JavaFact]] => ep.ub.flows - case _ ⇒ + case _ => Map.empty } for { - (exitStatement, exitStatementFacts) ← exitFacts // ifds line 15.2 - exitStatementFact ← exitStatementFacts // ifds line 15.3 + (exitStatement, exitStatementFacts) <- exitFacts // ifds line 15.2 + exitStatementFact <- exitStatementFacts // ifds line 15.3 } { result ++= javaReturnFlow(exitStatement, exitStatementFact, call, in, successor) } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index ba739ace03..f06643c5c5 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -22,7 +22,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( * The analysis starts with all public methods in TaintAnalysisTestClass. */ override val entryPoints: Seq[(Method, TaintFact)] = for { - m ← p.allMethodsWithBody + m <- p.allMethodsWithBody } yield m -> TaintNullFact /** @@ -60,31 +60,31 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { def handleNativeMethod(call: JavaStatement, successor: JavaStatement, in: TaintFact, dependeesGetter: Getter): Set[TaintFact] = { // https://docs.oracle.com/en/java/javase/13/docs/specs/jni/design.html#resolving-native-method-names - val calleeName = callee.name.map(c ⇒ c match { - case c if isAlphaNumeric(c) ⇒ c - case '_' ⇒ "_1" - case ';' ⇒ "_2" - case '[' ⇒ "_3" - case c ⇒ s"_${c.toInt.toHexString.reverse.padTo(4, '0').reverse}" + val calleeName = callee.name.map(c => c match { + case c if isAlphaNumeric(c) => c + case '_' => "_1" + case ';' => "_2" + case '[' => "_3" + case c => s"_${c.toInt.toHexString.reverse.padTo(4, '0').reverse}" }).mkString val nativeFunctionName = "Java_"+callee.classFile.fqn+"_"+calleeName val function = LLVMFunction(llvmProject.function(nativeFunctionName).get) var result = Set.empty[TaintFact] val entryFacts = nativeCallFlow(call, function, in, callee) - for (entryFact ← entryFacts) { // ifds line 14 + for (entryFact <- entryFacts) { // ifds line 14 val e = (function, entryFact) val exitFacts: Map[LLVMStatement, Set[NativeTaintFact]] = dependeesGetter(e, NativeTaint.key).asInstanceOf[EOptionP[(LLVMStatement, NativeTaintFact), IFDSProperty[LLVMStatement, NativeTaintFact]]] match { - case ep: FinalEP[_, IFDSProperty[LLVMStatement, NativeTaintFact]] ⇒ + case ep: FinalEP[_, IFDSProperty[LLVMStatement, NativeTaintFact]] => ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, NativeTaintFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[LLVMStatement, NativeTaintFact]] => ep.ub.flows - case _ ⇒ + case _ => Map.empty } for { - (exitStatement, exitStatementFacts) ← exitFacts // ifds line 15.2 - exitStatementFact ← exitStatementFacts // ifds line 15.3 + (exitStatement, exitStatementFacts) <- exitFacts // ifds line 15.2 + exitStatementFact <- exitStatementFacts // ifds line 15.3 } { result ++= nativeReturnFlow(exitStatement, exitStatementFact, call, in, callee, successor) } @@ -120,36 +120,36 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( val allParamsWithIndices = allParams.zipWithIndex in match { // Taint formal parameter if actual parameter is tainted - case Variable(index) ⇒ + case Variable(index) => allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => // TODO: this is passed Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv - case _ ⇒ None // Nothing to do + case _ => None // Nothing to do }.toSet // Taint element of formal parameter if element of actual parameter is tainted - case ArrayElement(index, taintedIndex) ⇒ + case ArrayElement(index, taintedIndex) => allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv - case _ ⇒ None // Nothing to do + case _ => None // Nothing to do }.toSet - case InstanceField(index, declaredClass, taintedField) ⇒ + case InstanceField(index, declaredClass, taintedField) => // Taint field of formal parameter if field of actual parameter is tainted // Only if the formal parameter is of a type that may have that field! allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => Some(JavaInstanceField(paramIndex + 1, declaredClass, taintedField)) // TODO subtype check - case _ ⇒ None // Nothing to do + case _ => None // Nothing to do }.toSet - case StaticField(classType, fieldName) ⇒ Set(JavaStaticField(classType, fieldName)) + case StaticField(classType, fieldName) => Set(JavaStaticField(classType, fieldName)) - case TaintNullFact ⇒ Set(NativeTaintNullFact) + case TaintNullFact => Set(NativeTaintNullFact) - case _ ⇒ Set() // Nothing to do + case _ => Set() // Nothing to do } } @@ -191,54 +191,54 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( var flows: Set[TaintFact] = Set.empty in match { // Taint actual parameter if formal parameter is tainted - case JavaVariable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + case JavaVariable(index) if index < 0 && index > -100 && isRefTypeParam(index) => val param = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(Variable) // Taint element of actual parameter if element of formal parameter is tainted - case JavaArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + case JavaArrayElement(index, taintedIndex) if index < 0 && index > -100 => val param = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) - case JavaInstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + case JavaInstanceField(index, declClass, taintedField) if index < 0 && index > -10 => // Taint field of actual parameter if field of formal parameter is tainted val param = allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic)) - param.asVar.definedBy.foreach { defSite ⇒ + param.asVar.definedBy.foreach { defSite => flows += InstanceField(defSite, declClass, taintedField) } - case JavaStaticField(objectType, fieldName) ⇒ flows += StaticField(objectType, fieldName) + case JavaStaticField(objectType, fieldName) => flows += StaticField(objectType, fieldName) // Track the call chain to the sink back - case NativeFlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ + case NativeFlowFact(flow) if !flow.contains(JavaMethod(call.method)) => flows += FlowFact(JavaMethod(call.method) +: flow) - case NativeTaintNullFact ⇒ flows += TaintNullFact - case _ ⇒ + case NativeTaintNullFact => flows += TaintNullFact + case _ => } // Propagate taints of the return value exit.instruction match { - case ret: Ret ⇒ { + case ret: Ret => { in match { - case NativeVariable(value) if value == ret.value && call.stmt.astID == Assignment.ASTID ⇒ + case NativeVariable(value) if value == ret.value && call.stmt.astID == Assignment.ASTID => flows += Variable(call.index) // TODO - /*case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + /*case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) => flows += ArrayElement(call.index, taintedIndex) - case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) => flows += InstanceField(call.index, declClass, taintedField)*/ - case NativeTaintNullFact ⇒ + case NativeTaintNullFact => val taints = createTaints(nativeCallee, call) if (taints.nonEmpty) flows ++= taints - case _ ⇒ // Nothing to do + case _ => // Nothing to do } } - case _ ⇒ + case _ => } flows } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index 630e7a670e..d083f77dd6 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -23,42 +23,42 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * executed next. */ override def normalFlow(statement: LLVMStatement, in: NativeTaintFact, predecessor: Option[LLVMStatement]): Set[NativeTaintFact] = statement.instruction match { - case _: Alloca ⇒ Set(in) - case store: Store ⇒ in match { - case NativeVariable(value) if value == store.src ⇒ store.dst match { - case dst: Alloca ⇒ Set(in, NativeVariable(dst)) - case gep: GetElementPtr if gep.isConstant ⇒ Set(in, NativeArrayElement(gep.base, gep.constants)) + case _: Alloca => Set(in) + case store: Store => in match { + case NativeVariable(value) if value == store.src => store.dst match { + case dst: Alloca => Set(in, NativeVariable(dst)) + case gep: GetElementPtr if gep.isConstant => Set(in, NativeArrayElement(gep.base, gep.constants)) } - case NativeArrayElement(base, indices) if store.src == base ⇒ Set(in, NativeArrayElement(store.dst, indices)) - case NativeVariable(value) if value == store.dst ⇒ Set() - case _ ⇒ Set(in) + case NativeArrayElement(base, indices) if store.src == base => Set(in, NativeArrayElement(store.dst, indices)) + case NativeVariable(value) if value == store.dst => Set() + case _ => Set(in) } - case load: Load ⇒ in match { - case NativeVariable(value) if value == load.src ⇒ Set(in, NativeVariable(load)) - case NativeArrayElement(base, indices) ⇒ load.src match { - case gep: GetElementPtr if gep.isConstant && gep.base == base && gep.constants == indices ⇒ Set(in, NativeVariable(load)) - case _ ⇒ Set(in, NativeArrayElement(load, indices)) + case load: Load => in match { + case NativeVariable(value) if value == load.src => Set(in, NativeVariable(load)) + case NativeArrayElement(base, indices) => load.src match { + case gep: GetElementPtr if gep.isConstant && gep.base == base && gep.constants == indices => Set(in, NativeVariable(load)) + case _ => Set(in, NativeArrayElement(load, indices)) } - case _ ⇒ Set(in) + case _ => Set(in) } - case add: Add ⇒ in match { - case NativeVariable(value) if value == add.op1 || value == add.op2 ⇒ Set(in, NativeVariable(add)) - case _ ⇒ Set(in) + case add: Add => in match { + case NativeVariable(value) if value == add.op1 || value == add.op2 => Set(in, NativeVariable(add)) + case _ => Set(in) } - case sub: Sub ⇒ in match { - case NativeVariable(value) if value == sub.op1 || value == sub.op2 ⇒ Set(in, NativeVariable(sub)) - case _ ⇒ Set(in) + case sub: Sub => in match { + case NativeVariable(value) if value == sub.op1 || value == sub.op2 => Set(in, NativeVariable(sub)) + case _ => Set(in) } - case gep: GetElementPtr ⇒ in match { - case NativeVariable(value) if value == gep.base ⇒ Set(in, NativeVariable(gep)) - case NativeArrayElement(base, indices) if base == gep.base && gep.isZero ⇒ Set(in, NativeArrayElement(gep, indices)) - case _ ⇒ Set(in) + case gep: GetElementPtr => in match { + case NativeVariable(value) if value == gep.base => Set(in, NativeVariable(gep)) + case NativeArrayElement(base, indices) if base == gep.base && gep.isZero => Set(in, NativeArrayElement(gep, indices)) + case _ => Set(in) } - case bitcast: BitCast ⇒ in match { - case NativeVariable(value) if value == bitcast.operand(0) ⇒ Set(in, NativeVariable(bitcast)) - case _ ⇒ Set(in) + case bitcast: BitCast => in match { + case NativeVariable(value) if value == bitcast.operand(0) => Set(in, NativeVariable(bitcast)) + case _ => Set(in) } - case _ ⇒ Set(in) + case _ => Set(in) } /** @@ -71,22 +71,22 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * the facts in `in` held before `statement` and `statement` calls `callee`. */ override def callFlow(call: LLVMStatement, callee: NativeFunction, in: NativeTaintFact): Set[NativeTaintFact] = callee match { - case LLVMFunction(callee) ⇒ + case LLVMFunction(callee) => in match { // Taint formal parameter if actual parameter is tainted - case NativeVariable(value) ⇒ call.instruction.asInstanceOf[Call].indexOfArgument(value) match { - case Some(index) ⇒ Set(NativeVariable(callee.argument(index))) - case None ⇒ Set() + case NativeVariable(value) => call.instruction.asInstanceOf[Call].indexOfArgument(value) match { + case Some(index) => Set(NativeVariable(callee.argument(index))) + case None => Set() } // TODO pass other java taints - case NativeTaintNullFact ⇒ Set(in) - case NativeArrayElement(base, indices) ⇒ call.instruction.asInstanceOf[Call].indexOfArgument(base) match { - case Some(index) ⇒ Set(NativeArrayElement(callee.argument(index), indices)) - case None ⇒ Set() + case NativeTaintNullFact => Set(in) + case NativeArrayElement(base, indices) => call.instruction.asInstanceOf[Call].indexOfArgument(base) match { + case Some(index) => Set(NativeArrayElement(callee.argument(index), indices)) + case None => Set() } - case _ ⇒ Set() // Nothing to do + case _ => Set() // Nothing to do } - case _ ⇒ throw new RuntimeException("this case should be handled by outsideAnalysisContext") + case _ => throw new RuntimeException("this case should be handled by outsideAnalysisContext") } /** @@ -100,25 +100,25 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD * `successor` will be executed next. */ override def returnFlow(exit: LLVMStatement, in: NativeTaintFact, call: LLVMStatement, callFact: NativeTaintFact, successor: LLVMStatement): Set[NativeTaintFact] = { - val callee = exit.callable() + val callee = exit.callable var flows: Set[NativeTaintFact] = if (sanitizesReturnValue(callee)) Set.empty else in match { - case NativeVariable(value) ⇒ exit.instruction match { - case ret: Ret if ret.value == value ⇒ Set(NativeVariable(call.instruction)) - case _: Ret ⇒ Set() - case _ ⇒ Set() + case NativeVariable(value) => exit.instruction match { + case ret: Ret if ret.value == value => Set(NativeVariable(call.instruction)) + case _: Ret => Set() + case _ => Set() } - case NativeTaintNullFact ⇒ Set(NativeTaintNullFact) - case NativeFlowFact(flow) if !flow.contains(call.function) ⇒ + case NativeTaintNullFact => Set(NativeTaintNullFact) + case NativeFlowFact(flow) if !flow.contains(call.function) => Set(NativeFlowFact(call.function +: flow)) - case _ ⇒ Set() + case _ => Set() } if (exit.callable.name == "source") in match { - case NativeTaintNullFact ⇒ flows += NativeVariable(call.instruction) + case NativeTaintNullFact => flows += NativeVariable(call.instruction) } if (exit.callable.name == "sink") in match { - case NativeVariable(value) if value == exit.callable.function.argument(0) ⇒ + case NativeVariable(value) if value == exit.callable.function.argument(0) => flows += NativeFlowFact(Seq(call.callable, exit.callable)) - case _ ⇒ + case _ => } flows } @@ -134,8 +134,8 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD override def callToReturnFlow(call: LLVMStatement, in: NativeTaintFact, successor: LLVMStatement): Set[NativeTaintFact] = Set(in) override def needsPredecessor(statement: LLVMStatement): Boolean = statement.instruction match { - case PHI(_) ⇒ true - case _ ⇒ false + case PHI(_) => true + case _ => false } /** @@ -158,36 +158,36 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD val allParamsWithIndices = allParams.zipWithIndex in match { // Taint formal parameter if actual parameter is tainted - case Variable(index) ⇒ + case Variable(index) => allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => // TODO: this is passed Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv - case _ ⇒ None // Nothing to do + case _ => None // Nothing to do }.toSet // Taint element of formal parameter if element of actual parameter is tainted - case ArrayElement(index, taintedIndex) ⇒ + case ArrayElement(index, taintedIndex) => allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv - case _ ⇒ None // Nothing to do + case _ => None // Nothing to do }.toSet - case InstanceField(index, declaredClass, taintedField) ⇒ + case InstanceField(index, declaredClass, taintedField) => // Taint field of formal parameter if field of actual parameter is tainted // Only if the formal parameter is of a type that may have that field! allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => Some(JavaInstanceField(paramIndex + 1, declaredClass, taintedField)) // TODO subtype check - case _ ⇒ None // Nothing to do + case _ => None // Nothing to do }.toSet - case StaticField(classType, fieldName) ⇒ Set(JavaStaticField(classType, fieldName)) + case StaticField(classType, fieldName) => Set(JavaStaticField(classType, fieldName)) - case NullFact ⇒ Set(NativeTaintNullFact) + case NullFact => Set(NativeTaintNullFact) - case _ ⇒ Set() // Nothing to do + case _ => Set() // Nothing to do }*/ Set() // TODO @@ -231,49 +231,49 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD var flows: Set[Fact] = Set.empty in match { // Taint actual parameter if formal parameter is tainted - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) => val param = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(Variable) // Taint element of actual parameter if element of formal parameter is tainted - case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 => val param = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) - case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 => // Taint field of actual parameter if field of formal parameter is tainted val param = allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic)) - param.asVar.definedBy.foreach { defSite ⇒ + param.asVar.definedBy.foreach { defSite => flows += InstanceField(defSite, declClass, taintedField) } - case sf: StaticField ⇒ flows += sf + case sf: StaticField => flows += sf // Track the call chain to the sink back - case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ + case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) => flows += FlowFact(JavaMethod(call.method) +: flow) - case _ ⇒ + case _ => } // Propagate taints of the return value if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy in match { - case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + case Variable(index) if returnValueDefinedBy.contains(index) => flows += Variable(call.index) - case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) => flows += ArrayElement(call.index, taintedIndex) - case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) => flows += InstanceField(call.index, declClass, taintedField) - case NullFact ⇒ + case NullFact => val taints = createTaints(callee, call) if (taints.nonEmpty) flows ++= taints - case _ ⇒ // Nothing to do + case _ => // Nothing to do } } val flowFact = createFlowFact(callee, call, in) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala index b743857069..a0ee97d10b 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeTaintProblem.scala @@ -52,7 +52,7 @@ case class JavaInstanceField(index: Int, classType: ObjectType, fieldName: Strin case class NativeFlowFact(flow: Seq[Callable]) extends NativeTaintFact { override val hashCode: Int = { var r = 1 - flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) + flow.foreach(f => r = (r + f.hashCode()) * 31) r } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala index ea8b9f806c..24a7b87d6d 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Module.scala @@ -19,10 +19,10 @@ case class Module(ref: LLVMModuleRef) { def function(name: String): Function = Value(LLVMGetNamedFunction(ref, name)) match { - case None ⇒ + case None => throw new IllegalArgumentException("Unknown function '"+name+"'") - case Some(function: Function) ⇒ function - case Some(_) ⇒ throw new IllegalStateException("Expected LLVMGetNamedFunction to return a Function ref") + case Some(function: Function) => function + case Some(_) => throw new IllegalStateException("Expected LLVMGetNamedFunction to return a Function ref") } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala index 6d7c3accc2..f21753bb24 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/Type.scala @@ -8,39 +8,39 @@ import org.bytedeco.llvm.global.LLVM._ object Type { def apply(ref: LLVMTypeRef): Type = { LLVMGetTypeKind(ref) match { - case LLVMVoidTypeKind ⇒ VoidType(ref) - case LLVMHalfTypeKind ⇒ HalfType(ref) - case LLVMFloatTypeKind ⇒ FloatType(ref) - case LLVMDoubleTypeKind ⇒ DoubleType(ref) - case LLVMX86_FP80TypeKind ⇒ X86_FP80Type(ref) - case LLVMFP128TypeKind ⇒ FP128Type(ref) - case LLVMPPC_FP128TypeKind ⇒ PPC_FP128Type(ref) - case LLVMLabelTypeKind ⇒ LabelType(ref) - case LLVMIntegerTypeKind ⇒ IntegerType(ref) - case LLVMFunctionTypeKind ⇒ FunctionType(ref) - case LLVMStructTypeKind ⇒ StructType(ref) - case LLVMArrayTypeKind ⇒ ArrayType(ref) - case LLVMPointerTypeKind ⇒ PointerType(ref) - case LLVMVectorTypeKind ⇒ VectorType(ref) - case LLVMMetadataTypeKind ⇒ MetadataType(ref) - case LLVMX86_MMXTypeKind ⇒ X86_MMXType(ref) - case LLVMTokenTypeKind ⇒ TokenType(ref) - case LLVMScalableVectorTypeKind ⇒ ScalableVectorType(ref) - case LLVMBFloatTypeKind ⇒ FloatType(ref) - case typeKind ⇒ throw new IllegalArgumentException("unknown type kind: "+typeKind) + case LLVMVoidTypeKind => VoidType(ref) + case LLVMHalfTypeKind => HalfType(ref) + case LLVMFloatTypeKind => FloatType(ref) + case LLVMDoubleTypeKind => DoubleType(ref) + case LLVMX86_FP80TypeKind => X86_FP80Type(ref) + case LLVMFP128TypeKind => FP128Type(ref) + case LLVMPPC_FP128TypeKind => PPC_FP128Type(ref) + case LLVMLabelTypeKind => LabelType(ref) + case LLVMIntegerTypeKind => IntegerType(ref) + case LLVMFunctionTypeKind => FunctionType(ref) + case LLVMStructTypeKind => StructType(ref) + case LLVMArrayTypeKind => ArrayType(ref) + case LLVMPointerTypeKind => PointerType(ref) + case LLVMVectorTypeKind => VectorType(ref) + case LLVMMetadataTypeKind => MetadataType(ref) + case LLVMX86_MMXTypeKind => X86_MMXType(ref) + case LLVMTokenTypeKind => TokenType(ref) + case LLVMScalableVectorTypeKind => ScalableVectorType(ref) + case LLVMBFloatTypeKind => FloatType(ref) + case typeKind => throw new IllegalArgumentException("unknown type kind: "+typeKind) } } } sealed abstract class Type(ref: LLVMTypeRef) { - def repr(): String = { + def repr: String = { val bytePointer = LLVMPrintTypeToString(ref) val string = bytePointer.getString LLVMDisposeMessage(bytePointer) string } - override def toString: String = s"Type(${repr()})" + override def toString: String = s"Type(${repr})" def isSized: Boolean = intToBool(LLVMTypeIsSized(ref)) } @@ -78,7 +78,7 @@ case class FunctionType(ref: LLVMTypeRef) extends Type(ref) { def params: Iterable[Type] = { val result = new PointerPointer[LLVMTypeRef](paramCount.toLong) LLVMGetParamTypes(ref, result) - (0.toLong until paramCount.toLong).map(result.get(_)).map(p ⇒ Type(new LLVMTypeRef(p))) + (0.toLong until paramCount.toLong).map(result.get(_)).map(p => Type(new LLVMTypeRef(p))) } } /** Structures */ diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala index dac7bd1315..253baa543b 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/package.scala @@ -3,8 +3,8 @@ package org.opalj.ll package object llvm { def intToBool(i: Int): Boolean = i match { - case 0 ⇒ false - case 1 ⇒ true - case _ ⇒ throw new IllegalArgumentException(s"${i} is not a valid Boolean") + case 0 => false + case 1 => true + case _ => throw new IllegalArgumentException(s"${i} is not a valid Boolean") } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala index 19e555c478..a6b3cf7bf2 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/BasicBlock.scala @@ -13,13 +13,13 @@ case class BasicBlock(block_ref: LLVMBasicBlockRef) def firstInstruction: Instruction = Instruction(LLVMGetFirstInstruction(block_ref)) def lastInstruction: Instruction = Instruction(LLVMGetLastInstruction(block_ref)) - def terminator(): Option[Instruction with Terminator] = { + def terminator: Option[Instruction with Terminator] = { OptionalInstruction(LLVMGetBasicBlockTerminator(block_ref)) match { - case Some(terminator) ⇒ { + case Some(terminator) => { assert(terminator.isTerminator) Some(terminator.asInstanceOf[Instruction with Terminator]) } - case None ⇒ None + case None => None } } @@ -45,16 +45,16 @@ case class BasicBlock(block_ref: LLVMBasicBlockRef) * Returns `true` if this node has successor nodes. */ override def hasSuccessors: Boolean = terminator match { - case Some(t) ⇒ t.hasSuccessors - case None ⇒ false + case Some(t) => t.hasSuccessors + case None => false } /** * Applies the given function for each successor node. */ - override def foreachSuccessor(f: Node ⇒ Unit): Unit = terminator match { - case Some(t) ⇒ t.foreachSuccessor(f) - case None ⇒ + override def foreachSuccessor(f: Node => Unit): Unit = terminator match { + case Some(t) => t.foreachSuccessor(f) + case None => } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala index f2cf298efc..85e12e9b3b 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Instruction.scala @@ -18,74 +18,74 @@ object Instruction { assert(ref != null && !ref.isNull(), "ref may not be null") assert(LLVMGetValueKind(ref) == LLVMInstructionValueKind, "ref has to be an instruction") LLVMGetInstructionOpcode(ref) match { - case LLVMRet ⇒ Ret(ref) - case LLVMBr ⇒ Br(ref) - case LLVMSwitch ⇒ Switch(ref) - case LLVMIndirectBr ⇒ IndirectBr(ref) - case LLVMInvoke ⇒ Invoke(ref) - case LLVMUnreachable ⇒ Unreachable(ref) - case LLVMCallBr ⇒ CallBr(ref) - case LLVMFNeg ⇒ FNeg(ref) - case LLVMAdd ⇒ Add(ref) - case LLVMFAdd ⇒ FAdd(ref) - case LLVMSub ⇒ Sub(ref) - case LLVMFSub ⇒ FSub(ref) - case LLVMMul ⇒ Mul(ref) - case LLVMFMul ⇒ FMul(ref) - case LLVMUDiv ⇒ UDiv(ref) - case LLVMSDiv ⇒ SDiv(ref) - case LLVMFDiv ⇒ FDiv(ref) - case LLVMURem ⇒ URem(ref) - case LLVMSRem ⇒ SRem(ref) - case LLVMFRem ⇒ FRem(ref) - case LLVMShl ⇒ Shl(ref) - case LLVMLShr ⇒ LShr(ref) - case LLVMAShr ⇒ AShr(ref) - case LLVMAnd ⇒ And(ref) - case LLVMOr ⇒ Or(ref) - case LLVMXor ⇒ Xor(ref) - case LLVMAlloca ⇒ Alloca(ref) - case LLVMLoad ⇒ Load(ref) - case LLVMStore ⇒ Store(ref) - case LLVMGetElementPtr ⇒ GetElementPtr(ref) - case LLVMTrunc ⇒ Trunc(ref) - case LLVMZExt ⇒ ZExt(ref) - case LLVMSExt ⇒ SExt(ref) - case LLVMFPToUI ⇒ FPToUI(ref) - case LLVMFPToSI ⇒ FPToSI(ref) - case LLVMUIToFP ⇒ UIToFP(ref) - case LLVMSIToFP ⇒ SIToFP(ref) - case LLVMFPTrunc ⇒ FPTrunc(ref) - case LLVMFPExt ⇒ FPExt(ref) - case LLVMPtrToInt ⇒ PtrToInt(ref) - case LLVMIntToPtr ⇒ IntToPtr(ref) - case LLVMBitCast ⇒ BitCast(ref) - case LLVMAddrSpaceCast ⇒ AddrSpaceCast(ref) - case LLVMICmp ⇒ ICmp(ref) - case LLVMFCmp ⇒ FCmp(ref) - case LLVMPHI ⇒ PHI(ref) - case LLVMCall ⇒ Call(ref) - case LLVMSelect ⇒ Select(ref) - case LLVMUserOp1 ⇒ UserOp1(ref) - case LLVMUserOp2 ⇒ UserOp2(ref) - case LLVMVAArg ⇒ VAArg(ref) - case LLVMExtractElement ⇒ ExtractElement(ref) - case LLVMInsertElement ⇒ InsertElement(ref) - case LLVMShuffleVector ⇒ ShuffleVector(ref) - case LLVMExtractValue ⇒ ExtractValue(ref) - case LLVMInsertValue ⇒ InsertValue(ref) - case LLVMFreeze ⇒ Freeze(ref) - case LLVMFence ⇒ Fence(ref) - case LLVMAtomicCmpXchg ⇒ AtomicCmpXchg(ref) - case LLVMAtomicRMW ⇒ AtomicRMW(ref) - case LLVMResume ⇒ Resume(ref) - case LLVMLandingPad ⇒ LandingPad(ref) - case LLVMCleanupRet ⇒ CleanupRet(ref) - case LLVMCatchRet ⇒ CatchRet(ref) - case LLVMCatchPad ⇒ CatchPad(ref) - case LLVMCleanupPad ⇒ CleanupPad(ref) - case LLVMCatchSwitch ⇒ CatchSwitch(ref) - case opCode ⇒ throw new IllegalArgumentException("unknown instruction opcode: "+opCode) + case LLVMRet => Ret(ref) + case LLVMBr => Br(ref) + case LLVMSwitch => Switch(ref) + case LLVMIndirectBr => IndirectBr(ref) + case LLVMInvoke => Invoke(ref) + case LLVMUnreachable => Unreachable(ref) + case LLVMCallBr => CallBr(ref) + case LLVMFNeg => FNeg(ref) + case LLVMAdd => Add(ref) + case LLVMFAdd => FAdd(ref) + case LLVMSub => Sub(ref) + case LLVMFSub => FSub(ref) + case LLVMMul => Mul(ref) + case LLVMFMul => FMul(ref) + case LLVMUDiv => UDiv(ref) + case LLVMSDiv => SDiv(ref) + case LLVMFDiv => FDiv(ref) + case LLVMURem => URem(ref) + case LLVMSRem => SRem(ref) + case LLVMFRem => FRem(ref) + case LLVMShl => Shl(ref) + case LLVMLShr => LShr(ref) + case LLVMAShr => AShr(ref) + case LLVMAnd => And(ref) + case LLVMOr => Or(ref) + case LLVMXor => Xor(ref) + case LLVMAlloca => Alloca(ref) + case LLVMLoad => Load(ref) + case LLVMStore => Store(ref) + case LLVMGetElementPtr => GetElementPtr(ref) + case LLVMTrunc => Trunc(ref) + case LLVMZExt => ZExt(ref) + case LLVMSExt => SExt(ref) + case LLVMFPToUI => FPToUI(ref) + case LLVMFPToSI => FPToSI(ref) + case LLVMUIToFP => UIToFP(ref) + case LLVMSIToFP => SIToFP(ref) + case LLVMFPTrunc => FPTrunc(ref) + case LLVMFPExt => FPExt(ref) + case LLVMPtrToInt => PtrToInt(ref) + case LLVMIntToPtr => IntToPtr(ref) + case LLVMBitCast => BitCast(ref) + case LLVMAddrSpaceCast => AddrSpaceCast(ref) + case LLVMICmp => ICmp(ref) + case LLVMFCmp => FCmp(ref) + case LLVMPHI => PHI(ref) + case LLVMCall => Call(ref) + case LLVMSelect => Select(ref) + case LLVMUserOp1 => UserOp1(ref) + case LLVMUserOp2 => UserOp2(ref) + case LLVMVAArg => VAArg(ref) + case LLVMExtractElement => ExtractElement(ref) + case LLVMInsertElement => InsertElement(ref) + case LLVMShuffleVector => ShuffleVector(ref) + case LLVMExtractValue => ExtractValue(ref) + case LLVMInsertValue => InsertValue(ref) + case LLVMFreeze => Freeze(ref) + case LLVMFence => Fence(ref) + case LLVMAtomicCmpXchg => AtomicCmpXchg(ref) + case LLVMAtomicRMW => AtomicRMW(ref) + case LLVMResume => Resume(ref) + case LLVMLandingPad => LandingPad(ref) + case LLVMCleanupRet => CleanupRet(ref) + case LLVMCatchRet => CatchRet(ref) + case LLVMCatchPad => CatchPad(ref) + case LLVMCleanupPad => CleanupPad(ref) + case LLVMCatchSwitch => CatchSwitch(ref) + case opCode => throw new IllegalArgumentException("unknown instruction opcode: "+opCode) } } } @@ -95,10 +95,10 @@ trait Terminator { def numSuccessors: Int = LLVMGetNumSuccessors(ref) def hasSuccessors: Boolean = numSuccessors > 0 def getSuccessor(i: Int) = BasicBlock(LLVMGetSuccessor(ref, i)) - def foreachSuccessor(f: BasicBlock ⇒ Unit): Unit = - (0 to numSuccessors - 1).foreach(i ⇒ f(getSuccessor(i))) + def foreachSuccessor(f: BasicBlock => Unit): Unit = + (0 to numSuccessors - 1).foreach(i => f(getSuccessor(i))) def successors: Seq[Instruction] = - (0 to numSuccessors - 1).map(i ⇒ getSuccessor(i).firstInstruction) + (0 to numSuccessors - 1).map(i => getSuccessor(i).firstInstruction) } sealed abstract class Instruction(ref: LLVMValueRef) extends User(ref) { @@ -185,7 +185,7 @@ case class Call(ref: LLVMValueRef) extends Instruction(ref) { def calledValue: Value = Value(LLVMGetCalledValue(ref)).get // corresponds to last operand def calledFunctionType: FunctionType = Type(LLVMGetCalledFunctionType(ref)).asInstanceOf[FunctionType] def indexOfArgument(argument: Value): Option[Int] = { - for (i ← 0 to numOperands) + for (i <- 0 to numOperands) if (operand(i) == argument) return Some(i) None } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala index 6f3b314749..3e347a5e32 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/Value.scala @@ -37,33 +37,33 @@ object Value { if (ref == null) return None if (ref.isNull) return None Some(LLVMGetValueKind(ref) match { - case LLVMArgumentValueKind ⇒ Argument(ref) - case LLVMBasicBlockValueKind ⇒ BasicBlock(LLVMValueAsBasicBlock(ref)) + case LLVMArgumentValueKind => Argument(ref) + case LLVMBasicBlockValueKind => BasicBlock(LLVMValueAsBasicBlock(ref)) //LLVMMemoryUseValueKind //LLVMMemoryDefValueKind //LLVMMemoryPhiValueKind - case LLVMFunctionValueKind ⇒ Function(ref) + case LLVMFunctionValueKind => Function(ref) //LLVMGlobalAliasValueKind //LLVMGlobalIFuncValueKind - case LLVMGlobalVariableValueKind ⇒ GlobalVariable(ref) + case LLVMGlobalVariableValueKind => GlobalVariable(ref) //LLVMBlockAddressValueKind - case LLVMConstantExprValueKind ⇒ ConstantExpression(ref) + case LLVMConstantExprValueKind => ConstantExpression(ref) //LLVMConstantArrayValueKind //LLVMConstantStructValueKind //LLVMConstantVectorValueKind //LLVMUndefValueValueKind //LLVMConstantAggregateZeroValueKind - case LLVMConstantDataArrayValueKind ⇒ ConstantDataArray(ref) - case LLVMConstantDataVectorValueKind ⇒ ConstantDataVector(ref) - case LLVMConstantIntValueKind ⇒ ConstantIntValue(ref) + case LLVMConstantDataArrayValueKind => ConstantDataArray(ref) + case LLVMConstantDataVectorValueKind => ConstantDataVector(ref) + case LLVMConstantIntValueKind => ConstantIntValue(ref) //LLVMConstantFPValueKind //LLVMConstantPointerNullValueKind //LLVMConstantTokenNoneValueKind //LLVMMetadataAsValueValueKind //LLVMInlineAsmValueKind - case LLVMInstructionValueKind ⇒ Instruction(ref) + case LLVMInstructionValueKind => Instruction(ref) //LLVMPoisonValueValueKind - case valueKind ⇒ throw new IllegalArgumentException("unknown valueKind: "+valueKind) + case valueKind => throw new IllegalArgumentException("unknown valueKind: "+valueKind) }) } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala index adaf373c4a..2da9696b8c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/llvm/value/constant/ConstantExpression.scala @@ -40,7 +40,7 @@ object ConstantExpression { // case LLVMAlloca => AllocaConst(ref) // case LLVMLoad => LoadConst(ref) // case LLVMStore => StoreConst(ref) - case LLVMGetElementPtr ⇒ GetElementPtrConst(ref) + case LLVMGetElementPtr => GetElementPtrConst(ref) // case LLVMTrunc => TruncConst(ref) // case LLVMZExt => ZExtConst(ref) // case LLVMSExt => SExtConst(ref) @@ -78,7 +78,7 @@ object ConstantExpression { // case LLVMCatchPad => CatchPadConst(ref) // case LLVMCleanupPad => CleanupPadConst(ref) // case LLVMCatchSwitch => CatchSwitchConst(ref) - case opCode ⇒ throw new IllegalArgumentException("unknown instruction opcode: "+opCode) + case opCode => throw new IllegalArgumentException("unknown instruction opcode: "+opCode) } } } diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/package.scala b/OPAL/ll/src/main/scala/org/opalj/ll/package.scala index c304c3c192..c910eb56ee 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/package.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/package.scala @@ -17,7 +17,7 @@ package object ll { assert(false) // <= test whether assertions are turned on or off... info(FrameworkName, "Production Build") } catch { - case _: AssertionError ⇒ info(FrameworkName, "Development Build with Assertions") + case _: AssertionError => info(FrameworkName, "Development Build with Assertions") } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala index f3c36034df..2f4653cf86 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala @@ -14,7 +14,7 @@ import org.opalj.value.ValueInformation class ForwardICFG(implicit project: SomeProject) extends ICFG[Method, JavaStatement] { - val tacai: Method ⇒ TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) + val tacai: Method => TACode[TACMethodParameter, DUVar[ValueInformation]] = project.get(LazyDetachedTACAIKey) val declaredMethods: DeclaredMethods = project.get(DeclaredMethodsKey) implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) implicit val typeProvider: TypeProvider = project.get(TypeProviderKey) @@ -39,9 +39,7 @@ class ForwardICFG(implicit project: SomeProject) override def nextStatements(statement: JavaStatement): Set[JavaStatement] = { statement.cfg .successors(statement.index) - .toChain - .map { index ⇒ JavaStatement(statement, index) } - .toSet + .map { index => JavaStatement(statement, index) } } /** @@ -53,15 +51,15 @@ class ForwardICFG(implicit project: SomeProject) */ override def getCalleesIfCallStatement(statement: JavaStatement): Option[collection.Set[Method]] = statement.stmt.astID match { - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => Some(getCallees(statement)) - case Assignment.ASTID | ExprStmt.ASTID ⇒ + case Assignment.ASTID | ExprStmt.ASTID => getExpression(statement.stmt).astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => Some(getCallees(statement)) - case _ ⇒ None + case _ => None } - case _ ⇒ None + case _ => None } /** @@ -71,9 +69,9 @@ class ForwardICFG(implicit project: SomeProject) * @return The statement's expression. */ private def getExpression(statement: Stmt[_]): Expr[_] = statement.astID match { - case Assignment.ASTID ⇒ statement.asAssignment.expr - case ExprStmt.ASTID ⇒ statement.asExprStmt.expr - case _ ⇒ throw new UnknownError("Unexpected statement") + case Assignment.ASTID => statement.asAssignment.expr + case ExprStmt.ASTID => statement.asExprStmt.expr + case _ => throw new UnknownError("Unexpected statement") } private def getCallees(statement: JavaStatement): collection.Set[Method] = { @@ -81,8 +79,8 @@ class ForwardICFG(implicit project: SomeProject) val caller = declaredMethods(statement.callable) val ep = propertyStore(caller, Callees.key) ep match { - case FinalEP(_, p) ⇒ definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method)) - case _ ⇒ + case FinalEP(_, p) => definedMethods(p.directCallees(typeProvider.newContext(caller), pc).map(_.method)) + case _ => throw new IllegalStateException( "call graph must be computed before the analysis starts" ) @@ -104,14 +102,14 @@ class ForwardICFG(implicit project: SomeProject) val result = scala.collection.mutable.Set.empty[Method] declaredMethods .filter( - declaredMethod ⇒ + declaredMethod => declaredMethod.hasSingleDefinedMethod || declaredMethod.hasMultipleDefinedMethods ) .foreach( - declaredMethod ⇒ + declaredMethod => declaredMethod - .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) + .foreachDefinedMethod(defineMethod => result.add(defineMethod)) ) result } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 5b7f146623..ead50657b3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -30,13 +30,13 @@ case class JavaStatement( override def hashCode(): Int = method.hashCode() * 31 + index override def equals(o: Any): Boolean = o match { - case s: JavaStatement ⇒ s.index == index && s.method == method - case _ ⇒ false + case s: JavaStatement => s.index == index && s.method == method + case _ => false } override def toString: String = s"${method.signatureToJava(false)}[${index}]\n\t${stmt}\n\t${method.toJava}" - override def callable(): Method = method - override def node(): CFGNode = cfg.bb(index) + override def callable: Method = method + override def node: CFGNode = cfg.bb(index) def stmt: Stmt[V] = code(index) } @@ -54,9 +54,9 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) * @return The call object for `call`. */ protected def asCall(call: Stmt[V]): Call[V] = call.astID match { - case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall - case _ ⇒ call.asMethodCall + case Assignment.ASTID => call.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID => call.asExprStmt.expr.asFunctionCall + case _ => call.asMethodCall } /** @@ -74,9 +74,9 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) (exit.stmt.astID != Return.ASTID && exit.stmt.astID != ReturnValue.ASTID) } - override def outsideAnalysisContext(callee: Method): Option[(JavaStatement, JavaStatement, Fact, Getter) ⇒ Set[Fact]] = callee.body.isDefined match { - case true ⇒ None - case false ⇒ Some((_: JavaStatement, _: JavaStatement, in: Fact, _: Getter) ⇒ Set(in)) + override def outsideAnalysisContext(callee: Method): Option[(JavaStatement, JavaStatement, Fact, Getter) => Set[Fact]] = callee.body.isDefined match { + case true => None + case false => Some((_: JavaStatement, _: JavaStatement, in: Fact, _: Getter) => Set(in)) } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala index 238220583e..f5ede93bed 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/AbstractIFDSAnalysis.scala @@ -26,7 +26,7 @@ import org.opalj.util.PerformanceEvaluation.time import java.io.{File, PrintWriter} import javax.swing.JOptionPane -import scala.collection.{mutable, Set ⇒ SomeSet} +import scala.collection.{mutable, Set => SomeSet} /** * @@ -237,8 +237,8 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( return Result(entity, createPropertyValue(Map.empty)); ifdsProblem.specialCase(entity, propertyKey) match { - case Some(result) ⇒ return result; - case _ ⇒ + case Some(result) => return result; + case _ => } val method = declaredMethod.definedMethod @@ -249,17 +249,17 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * result. */ val (code, cfg) = propertyStore(method, TACAI.key) match { - case FinalP(TheTACAI(tac)) ⇒ (tac.stmts, tac.cfg) + case FinalP(TheTACAI(tac)) => (tac.stmts, tac.cfg) - case epk: EPK[Method, TACAI] ⇒ + case epk: EPK[Method, TACAI] => return InterimResult.forUB( entity, createPropertyValue(Map.empty), Set(epk), - _ ⇒ performAnalysis(entity) + _ => performAnalysis(entity) ); - case tac ⇒ + case tac => throw new UnknownError(s"can't handle intermediate TACs ($tac)"); } @@ -268,7 +268,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( new State(declaringClass, method, entity, code, cfg, Map(entity -> Set.empty), Map()) val queue = mutable.Queue .empty[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] - startBlocks(sourceFact, cfg).foreach { start ⇒ + startBlocks(sourceFact, cfg).foreach { start => state.incomingFacts += start -> Set(sourceFact) queue.enqueue((start, Set(sourceFact), None, None, None)) } @@ -304,15 +304,15 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( eps: SomeEPS )(implicit state: State): ProperPropertyComputationResult = { (eps: @unchecked) match { - case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) ⇒ + case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) => reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) case interimEUBP @ InterimEUBP( e: (DeclaredMethod, IFDSFact) @unchecked, ub: IFDSProperty[DeclaredMethodJavaStatement, IFDSFact] @unchecked - ) ⇒ + ) => if (ub.flows.values - .forall(facts ⇒ facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { + .forall(facts => facts.size == 1 && facts.forall(_ == ifdsProblem.nullFact))) { // Do not re-analyze the caller if we only get the null fact. // Update the pendingIfdsDependee entry to the new interim result. state.pendingIfdsDependees += @@ -321,13 +321,13 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( } else reAnalyzeCalls(state.pendingIfdsCallSites(e), e._1.definedMethod, Some(e._2)) - case FinalEP(_: DefinedMethod, _: Callees) ⇒ + case FinalEP(_: DefinedMethod, _: Callees) => reAnalyzebasicBlocks(state.pendingCgCallSites) - case InterimEUBP(_: DefinedMethod, _: Callees) ⇒ + case InterimEUBP(_: DefinedMethod, _: Callees) => reAnalyzebasicBlocks(state.pendingCgCallSites) - case FinalEP(method: Method, _: TACAI) ⇒ + case FinalEP(method: Method, _: TACAI) => state.pendingTacDependees -= method reAnalyzebasicBlocks(state.pendingTacCallSites(declaredMethods(method))) } @@ -380,7 +380,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * analyzed with the `inputFacts`. */ if (calleeWithUpdateFact.isEmpty) - for (successor ← successors) { + for (successor <- successors) { numberOfCalls.callToReturnFlow += 1 sumOfInputFactsForCallbacks += in.size summaryEdges += successor -> @@ -390,17 +390,17 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( ) } - for (calledMethod ← callees) { + for (calledMethod <- callees) { val callee = declaredMethods(calledMethod) ifdsProblem.outsideAnalysisContext(callee) match { - case Some(handler) ⇒ + case Some(handler) => // Let the concrete analysis decide what to do. for { - successor ← successors + successor <- successors } summaryEdges += successor -> (summaryEdges(successor) ++ handler(call, successor, in)) - case None ⇒ + case None => val callToStart = if (calleeWithUpdateFact.isDefined) Set(calleeWithUpdateFact.get) else { @@ -408,7 +408,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( } var allNewExitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = Map.empty // Collect exit facts for each input fact separately - for (fact ← callToStart) { + for (fact <- callToStart) { /* * If this is a recursive call with the same input facts, we assume that the * call only produces the facts that are already known. The call site is added to @@ -430,11 +430,11 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]] val oldValue = state.pendingIfdsDependees.get(e) val oldExitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = oldValue match { - case Some(ep: InterimEUBP[_, IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]) ⇒ ep.ub.flows - case _ ⇒ Map.empty + case Some(ep: InterimEUBP[_, IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]) => ep.ub.flows + case _ => Map.empty } val exitFacts: Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = callFlows match { - case ep: FinalEP[_, IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]] ⇒ + case ep: FinalEP[_, IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]] => if (state.pendingIfdsCallSites.contains(e) && state.pendingIfdsCallSites(e).nonEmpty) { val newDependee = @@ -443,7 +443,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( } state.pendingIfdsDependees -= e ep.p.flows - case ep: InterimEUBP[_, IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]] ⇒ + case ep: InterimEUBP[_, IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]] => /* * Add the call site to `pendingIfdsCallSites` and * `pendingIfdsDependees` and continue with the facts in the interim @@ -452,7 +452,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( */ addIfdsDependee(e, callFlows, basicBlock, call.index) ep.ub.flows - case _ ⇒ + case _ => addIfdsDependee(e, callFlows, basicBlock, call.index) Map.empty } @@ -508,8 +508,8 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( val pc = statement.code(statement.index).pc val ep = propertyStore(caller, Callees.key) ep match { - case FinalEP(_, p) ⇒ p.directCallees(typeProvider.newContext(caller), pc).map(_.method) - case _ ⇒ + case FinalEP(_, p) => p.directCallees(typeProvider.newContext(caller), pc).map(_.method) + case _ => throw new IllegalStateException( "call graph mut be computed before the analysis starts" ) @@ -526,14 +526,14 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( */ protected def mergeMaps[S, T](map1: Map[S, Set[T]], map2: Map[S, Set[T]]): Map[S, Set[T]] = { var result = map1 - for ((key, values) ← map2) { + for ((key, values) <- map2) { result.get(key) match { - case Some(resultValues) ⇒ + case Some(resultValues) => if (resultValues.size > values.size) result = result.updated(key, resultValues ++ values) else result = result.updated(key, values ++ resultValues) - case None ⇒ + case None => result = result.updated(key, values) } } @@ -567,10 +567,10 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( calleeWithUpdate, calleeWithUpdateFact ) - val allOut = mergeMaps(oldOut, nextOut).mapValues(facts ⇒ subsume(facts, project)) + val allOut = mergeMaps(oldOut, nextOut).view.mapValues(facts => subsume(facts, project)).toMap state.outgoingFacts = state.outgoingFacts.updated(basicBlock, allOut) - for (successor ← nextNodes(basicBlock)) { + for (successor <- nextNodes(basicBlock)) { if (isLastNode(successor)) { // Re-analyze recursive call sites with the same input fact. val nextOutSuccessors = nextOut.get(successor) @@ -691,7 +691,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( var result: Map[CFGNode, Set[IFDSFact]] = if (calleesO.isEmpty) { var result: Map[CFGNode, Set[IFDSFact]] = Map.empty - for (node ← nextNodes(basicBlock)) { + for (node <- nextNodes(basicBlock)) { numberOfCalls.normalFlow += 1 sumOfInputFactsForCallbacks += in.size result += node -> ifdsProblem.normalFlow(statement, Some(firstStatement(node)), flows) @@ -699,10 +699,10 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( result } else handleCall(basicBlock, statement, calleesO.get, flows, callFact) - .map(entry ⇒ entry._1.node -> entry._2) + .map(entry => entry._1.node -> entry._2) // Propagate the null fact. - result = result.map(result ⇒ result._1 -> propagateNullFact(in, result._2)) + result = result.map(result => result._1 -> propagateNullFact(in, result._2)) result } @@ -713,7 +713,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( */ private def reAnalyzebasicBlocks(basicBlocks: Set[BasicBlock])(implicit state: State): Unit = { val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty - for (bb ← basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) + for (bb <- basicBlocks) queue.enqueue((bb, state.incomingFacts(bb), None, None, None)) process(queue) } @@ -730,7 +730,7 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( fact: Option[IFDSFact] )(implicit state: State): Unit = { val queue: mutable.Queue[(BasicBlock, Set[IFDSFact], Option[Int], Option[Method], Option[IFDSFact])] = mutable.Queue.empty - for ((block, index) ← callSites) + for ((block, index) <- callSites) queue.enqueue((block, state.incomingFacts(block), Some(index), Some(callee), fact)) process(queue) } @@ -747,15 +747,15 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( state: State ): Option[SomeSet[Method]] = { statement.stmt.astID match { - case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID ⇒ + case StaticMethodCall.ASTID | NonVirtualMethodCall.ASTID | VirtualMethodCall.ASTID => Some(definedMethods(getCallees(statement, state.source._1))) - case Assignment.ASTID | ExprStmt.ASTID ⇒ + case Assignment.ASTID | ExprStmt.ASTID => getExpression(statement.stmt).astID match { - case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID ⇒ + case StaticFunctionCall.ASTID | NonVirtualFunctionCall.ASTID | VirtualFunctionCall.ASTID => Some(definedMethods(getCallees(statement, state.source._1))) - case _ ⇒ None + case _ => None } - case _ ⇒ None + case _ => None } } @@ -769,14 +769,14 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( val result = scala.collection.mutable.Set.empty[Method] declaredMethods .filter( - declaredMethod ⇒ + declaredMethod => declaredMethod.hasSingleDefinedMethod || declaredMethod.hasMultipleDefinedMethods ) .foreach( - declaredMethod ⇒ + declaredMethod => declaredMethod - .foreachDefinedMethod(defineMethod ⇒ result.add(defineMethod)) + .foreachDefinedMethod(defineMethod => result.add(defineMethod)) ) result } @@ -788,9 +788,9 @@ abstract class AbstractIFDSAnalysis[IFDSFact <: AbstractIFDSFact]( * @return The statement's expression. */ private def getExpression(statement: Stmt[V]): Expr[V] = statement.astID match { - case Assignment.ASTID ⇒ statement.asAssignment.expr - case ExprStmt.ASTID ⇒ statement.asExprStmt.expr - case _ ⇒ throw new UnknownError("Unexpected statement") + case Assignment.ASTID => statement.asAssignment.expr + case ExprStmt.ASTID => statement.asExprStmt.expr + case _ => throw new UnknownError("Unexpected statement") } /** @@ -858,12 +858,12 @@ case class DeclaredMethodJavaStatement( override def hashCode(): Int = method.hashCode() * 31 + index override def equals(o: Any): Boolean = o match { - case s: DeclaredMethodJavaStatement ⇒ s.index == index && s.method == method - case _ ⇒ false + case s: DeclaredMethodJavaStatement => s.index == index && s.method == method + case _ => false } override def toString: String = s"${method.toJava}" - override def callable(): DeclaredMethod = declaredMethod + override def callable: DeclaredMethod = declaredMethod def asJavaStatement: JavaStatement = JavaStatement(method, index, code, cfg) } @@ -898,7 +898,7 @@ abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact] override def afterPhaseScheduling(ps: PropertyStore, analysis: FPCFAnalysis): Unit = { val ifdsAnalysis = analysis.asInstanceOf[AbstractIFDSAnalysis[IFDSFact]] - for (e ← ifdsAnalysis.ifdsProblem.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } + for (e <- ifdsAnalysis.ifdsProblem.entryPoints) { ps.force(e, ifdsAnalysis.propertyKey.key) } } override def afterPhaseCompletion( @@ -930,14 +930,14 @@ abstract class AbsractIFDSAnalysisRunner { def evalProject(p: SomeProject): (Milliseconds, NumberOfCalls, Option[Object], Long) = { if (useL2) { p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) - case Some(requirements) ⇒ + case None => Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) => requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] } } else { p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { - case None ⇒ Set(classOf[PrimitiveTACAIDomain]) - case Some(requirements) ⇒ requirements + classOf[PrimitiveTACAIDomain] + case None => Set(classOf[PrimitiveTACAIDomain]) + case Some(requirements) => requirements + classOf[PrimitiveTACAIDomain] } } @@ -951,8 +951,8 @@ abstract class AbsractIFDSAnalysisRunner { val analysis = time { p.get(FPCFAnalysesManagerKey).runAll(analysisClass)._2 - }(t ⇒ analysisTime = t.toMilliseconds).collect { - case (_, a: AbstractIFDSAnalysis[_]) ⇒ a + }(t => analysisTime = t.toMilliseconds).collect { + case (_, a: AbstractIFDSAnalysis[_]) => a }.head if (AbstractIFDSAnalysisRunner.MEASURE_MEMORY) JOptionPane.showMessageDialog(null, "Analysis finished") @@ -983,8 +983,8 @@ abstract class AbsractIFDSAnalysisRunner { if (evalSchedulingStrategies) { val results = for { - i ← 1 to AbstractIFDSAnalysisRunner.NUM_EXECUTIONS_EVAL_SCHEDULING_STRATEGIES - strategy ← PKESequentialPropertyStore.Strategies + i <- 1 to AbstractIFDSAnalysisRunner.NUM_EXECUTIONS_EVAL_SCHEDULING_STRATEGIES + strategy <- PKESequentialPropertyStore.Strategies } yield { println(s"Round: $i - $strategy") val strategyValue = ConfigValueFactory.fromAnyRef(strategy) @@ -997,7 +997,7 @@ abstract class AbsractIFDSAnalysisRunner { println(results.mkString("AllResults:\n\t", "\n\t", "\n")) if (evaluationFile.nonEmpty) { val pw = new PrintWriter(evaluationFile.get) - PKESequentialPropertyStore.Strategies.foreach { strategy ⇒ + PKESequentialPropertyStore.Strategies.foreach { strategy => val strategyResults = results.filter(_._2 == strategy) val averageTime = strategyResults.map(_._3.timeSpan).sum / strategyResults.size val (normalFlow, callToStart, exitToReturn, callToReturn) = @@ -1018,7 +1018,7 @@ abstract class AbsractIFDSAnalysisRunner { var sumsOfInputFactsForCallbacks = Seq.empty[Long] var additionalEvaluationResults = Seq.empty[Object] for { - _ ← 1 to AbstractIFDSAnalysisRunner.NUM_EXECUTIONS + _ <- 1 to AbstractIFDSAnalysisRunner.NUM_EXECUTIONS } { val evaluationResult = evalProject(Project.recreate(p)) val additionalEvaluationResult = evaluationResult._3 @@ -1061,8 +1061,8 @@ abstract class AbsractIFDSAnalysisRunner { ): Boolean = propertyStore(method, Callers.key) match { // This is the case, if the method may be called from outside the library. - case FinalEP(_, p: Callers) ⇒ p.hasCallersWithUnknownContext - case _ ⇒ + case FinalEP(_, p: Callers) => p.hasCallersWithUnknownContext + case _ => throw new IllegalStateException( "call graph mut be computed before the analysis starts" ) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/BackwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/BackwardIFDSAnalysis.scala index 6dffb1bcd9..1c518dbc6b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/BackwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/BackwardIFDSAnalysis.scala @@ -33,11 +33,11 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS cfg: CFG[Stmt[V], TACStmts[V]] ): Set[BasicBlock] = sourceFact match { - case fact: UnbalancedReturnFact[IFDSFact] ⇒ Set(cfg.bb(fact.index)) - case _ ⇒ + case fact: UnbalancedReturnFact[IFDSFact @unchecked] => Set(cfg.bb(fact.index)) + case _ => Set(cfg.normalReturnNode, cfg.abnormalReturnNode) .flatMap(_.predecessors) - .foldLeft(Set.empty[BasicBlock])((c, n) ⇒ c + n.asBasicBlock) + .foldLeft(Set.empty[BasicBlock])((c, n) => c + n.asBasicBlock) } /** @@ -67,10 +67,10 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS * If the analysis for the unbalanced return finished, remove the entity from the * dependencies. */ - case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) ⇒ - if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]]) { + case FinalE(e: (DeclaredMethod, IFDSFact) @unchecked) => + if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact @unchecked]]) { state.pendingIfdsDependees -= e - createResult + createResult() } else super.propertyUpdate(eps) /* * If there is an interim result for an unbalanced return fact, ignore it and just create the result. @@ -78,8 +78,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS case interimEUBP @ InterimEUBP( e: (DeclaredMethod, IFDSFact) @unchecked, _: IFDSProperty[DeclaredMethodJavaStatement, IFDSFact] @unchecked - ) ⇒ - if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]]) { + ) => + if (e._2.isInstanceOf[UnbalancedReturnFact[IFDSFact @unchecked]]) { state.pendingIfdsDependees += e -> interimEUBP .asInstanceOf[EOptionP[(DeclaredMethod, IFDSFact), IFDSProperty[DeclaredMethodJavaStatement, IFDSFact]]] @@ -87,7 +87,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS } else super.propertyUpdate(eps) // If this is not an update for an unbalanced return fact, handle it as usual. - case _ ⇒ super.propertyUpdate(eps) + case _ => super.propertyUpdate(eps) } } @@ -100,8 +100,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS else { val predecessors = scala.collection.mutable.Set.empty[CFGNode] basicBlock.predecessors.foreach { - case basicBlock: BasicBlock ⇒ predecessors += basicBlock - case catchNode: CatchNode ⇒ + case basicBlock: BasicBlock => predecessors += basicBlock + case catchNode: CatchNode => predecessors ++= catchNode.predecessors.iterator.map(_.asBasicBlock) } predecessors.toSet @@ -139,8 +139,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS if (ifdsProblem.shouldPerformUnbalancedReturn(state.source)) { // Get all callers of this method propertyStore(state.source._1, Callers.key) match { - case FinalEP(_, p: Callers) ⇒ - p.callers(state.source._1).foreach { callersProperty ⇒ + case FinalEP(_, p: Callers) => + p.callers(state.source._1).iterator.foreach { callersProperty => val (caller, callPc, directCall) = callersProperty // We do not handle indirect calls. if (directCall) { @@ -148,9 +148,9 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS // Get the caller's tac to create the unbalanced return facts val callerTac = propertyStore(definedCaller, TACAI.key) callerTac match { - case FinalP(TheTACAI(tac)) ⇒ + case FinalP(TheTACAI(tac)) => addDependencyForUnbalancedReturn(caller, tac.pcToIndex(callPc), newIn, tac)(state) - case _ ⇒ + case _ => val pendingTacCallSites = state.pendingTacCallSites state.pendingTacDependees += definedCaller -> callerTac state.pendingTacCallSites = pendingTacCallSites.updated( @@ -162,7 +162,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS } } - case _ ⇒ + case _ => throw new IllegalStateException( "call graph mut be computed before the analysis starts" ) @@ -248,19 +248,19 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val definedCallee = callee.definedMethod val ep = propertyStore(definedCallee, TACAI.key) ep match { - case FinalP(TheTACAI(tac)) ⇒ + case FinalP(TheTACAI(tac)) => val cfg = tac.cfg val successors = predecessorStatementsWithNode(call) val flow = scala.collection.mutable.Set.empty[IFDSFact] (cfg.normalReturnNode.predecessors ++ cfg.abnormalReturnNode.predecessors) - .foreach { bb ⇒ + .foreach { bb => val exitPc = bb.asBasicBlock.endPC val calleeStmts = tac.stmts val exitStmt = calleeStmts(exitPc) val exitStatement = old.DeclaredMethodJavaStatement(definedCallee, cfg.bb(exitPc), exitStmt, exitPc, calleeStmts, cfg, state.source._1) for { - successor ← successors + successor <- successors if !AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW || (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || @@ -273,7 +273,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS } } flow.toSet - case _ ⇒ + case _ => val pendingTacCallSites = state.pendingTacCallSites val index = call.index state.pendingTacDependees += definedCallee -> ep @@ -302,12 +302,12 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS numberOfCalls.callFlow += 1 sumOfInputFactsForCallbacks += in.size val exitToReturnFacts = ifdsProblem.callFlow(call, callee, in, state.source) - successors.foreach { successor ⇒ + successors.foreach { successor => val updatedValue = result.get(successor) match { - case Some(facts) ⇒ + case Some(facts) => if (facts.size >= exitToReturnFacts.size) facts ++ exitToReturnFacts else exitToReturnFacts ++ facts - case None ⇒ exitToReturnFacts + case None => exitToReturnFacts } result = result.updated(successor, updatedValue) } @@ -320,9 +320,9 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS */ override protected def beforeHandleCall(call: DeclaredMethodJavaStatement, in: Set[IFDSFact]): Set[IFDSFact] = in.map { - case unbalancedFact: UnbalancedReturnFact[IFDSFact] if unbalancedFact.index == call.index ⇒ + case unbalancedFact: UnbalancedReturnFact[IFDSFact @unchecked] if unbalancedFact.index == call.index => unbalancedFact.innerFact - case fact ⇒ fact + case fact => fact } /** @@ -341,7 +341,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS val basicBlock = statement.node.asBasicBlock if (index == basicBlock.endPC) basicBlock.successors.iterator - .map(successorNode ⇒ lastStatement(successorNode) -> successorNode) + .map(successorNode => lastStatement(successorNode) -> successorNode) .toMap else { val nextIndex = index + 1 @@ -403,7 +403,7 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS callerCfg, caller ) - ifdsProblem.unbalancedReturnFlow(in, call, caller, state.source).foreach { in ⇒ + ifdsProblem.unbalancedReturnFlow(in, call, caller, state.source).foreach { in => val callerEntity = (caller, in) /* * Add the caller with the unbalanced return fact as a dependency to @@ -411,8 +411,8 @@ abstract class BackwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact, UnbalancedIFDS */ val callerAnalysisResult = propertyStore(callerEntity, propertyKey.key) callerAnalysisResult match { - case FinalEP(_, _) ⇒ // Caller was already analyzed with the fact - case _ ⇒ + case FinalEP(_, _) => // Caller was already analyzed with the fact + case _ => val pendingIfdsCallSites = state.pendingIfdsCallSites state.pendingIfdsDependees += callerEntity -> callerAnalysisResult diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardICFG.scala index 231b2afe35..edec39f814 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardICFG.scala @@ -83,8 +83,8 @@ class ForwardICFG[IFDSFact <: AbstractIFDSFact](implicit val caller = declaredMethods(statement.method) val ep = propertyStore(caller, Callees.key) ep match { - case FinalEP(_, p) ⇒ Some(p.directCallees(typeProvider.newContext(caller), pc).map(_.method).toSet) - case _ ⇒ + case FinalEP(_, p) => Some(p.directCallees(typeProvider.newContext(caller), pc).map(_.method).toSet) + case _ => throw new IllegalStateException( "call graph mut be computed before the analysis starts" ) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardIFDSAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardIFDSAnalysis.scala index 9079309db7..d82c4fb10d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardIFDSAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/ForwardIFDSAnalysis.scala @@ -40,7 +40,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF * Catch nodes are skipped to their successor. */ override protected def nextNodes(basicBlock: BasicBlock): Set[CFGNode] = - basicBlock.successors.map { successor ⇒ + basicBlock.successors.map { successor => if (successor.isCatchNode) successor.successors.head else successor } @@ -126,8 +126,8 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF if (AbstractIFDSAnalysis.OPTIMIZE_CROSS_PRODUCT_IN_RETURN_FLOW) { val successors = nextStatementsWithNode(call) for { - successor ← successors - exitStatement ← exitFacts.keys + successor <- successors + exitStatement <- exitFacts.keys if (successor._2.isBasicBlock || successor._2.isNormalReturnExitNode) && (exitStatement.stmt.astID == Return.ASTID || exitStatement.stmt.astID == ReturnValue.ASTID) || (successor._2.isCatchNode || successor._2.isAbnormalReturnExitNode) && @@ -136,8 +136,8 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF } else { val successors = nextStatements(call) for { - successor ← successors - exitStatement ← exitFacts.keys + successor <- successors + exitStatement <- exitFacts.keys } result = addSummaryEdge(result, call, exitStatement, successor, callee, exitFacts) } result @@ -152,11 +152,11 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF val basicBlock = statement.node.asBasicBlock if (index == basicBlock.endPC) basicBlock.successors.iterator - .map(successorNode ⇒ firstStatement(successorNode) → successorNode).toMap + .map(successorNode => firstStatement(successorNode) -> successorNode).toMap else { val nextIndex = index + 1 Map(old.DeclaredMethodJavaStatement(statement.method, basicBlock, statement.code(nextIndex), nextIndex, - statement.code, statement.cfg, statement.declaredMethod) → basicBlock) + statement.code, statement.cfg, statement.declaredMethod) -> basicBlock) } } @@ -172,7 +172,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF state: State ): Map[DeclaredMethodJavaStatement, Set[IFDSFact]] = { var result = Map.empty[DeclaredMethodJavaStatement, Set[IFDSFact]] - exit.predecessors foreach { predecessor ⇒ + exit.predecessors foreach { predecessor => if (predecessor.isBasicBlock) { val basicBlock = predecessor.asBasicBlock val exitFacts = state.outgoingFacts.get(basicBlock).flatMap(_.get(exit)) @@ -180,7 +180,7 @@ abstract class ForwardIFDSAnalysis[IFDSFact <: AbstractIFDSFact](ifdsProblem: IF val lastIndex = basicBlock.endPC val stmt = old.DeclaredMethodJavaStatement(state.method, basicBlock, state.code(lastIndex), lastIndex, state.code, state.cfg, state.source._1) - result += stmt → exitFacts.get + result += stmt -> exitFacts.get } } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSBasedVariableTypeAnalysis.scala index 0fa3d340b3..07befc548d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/IFDSBasedVariableTypeAnalysis.scala @@ -69,8 +69,8 @@ class IFDSBasedVariableTypeAnalysisRunner extends AbsractIFDSAnalysisRunner { analysis: AbstractIFDSAnalysis[_] ): Option[Object] = analysis match { - case subsuming: Subsuming[_, _] ⇒ Some(subsuming.numberOfSubsumptions) - case _ ⇒ None + case subsuming: Subsuming[_, _] => Some(subsuming.numberOfSubsumptions) + case _ => None } override protected def writeAdditionalEvaluationResultsToFile( diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/JavaIFDSProblem.scala index b20b35ac90..6eb8f0c074 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/JavaIFDSProblem.scala @@ -25,9 +25,9 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e final implicit val propertyStore: PropertyStore = project.get(PropertyStoreKey) implicit final protected val typeProvider: TypeProvider = project.get(TypeProviderKey) - override def outsideAnalysisContext(callee: DeclaredMethod): Option[(DeclaredMethodJavaStatement, DeclaredMethodJavaStatement, Set[Fact]) ⇒ Set[Fact]] = callee.definedMethod.body.isDefined match { - case true ⇒ None - case false ⇒ Some((_call: DeclaredMethodJavaStatement, _successor: DeclaredMethodJavaStatement, in: Set[Fact]) ⇒ in) + override def outsideAnalysisContext(callee: DeclaredMethod): Option[(DeclaredMethodJavaStatement, DeclaredMethodJavaStatement, Set[Fact]) => Set[Fact]] = callee.definedMethod.body.isDefined match { + case true => None + case false => Some((_call: DeclaredMethodJavaStatement, _successor: DeclaredMethodJavaStatement, in: Set[Fact]) => in) } /** @@ -58,9 +58,9 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e * @return The call object for `call`. */ protected def asCall(call: Stmt[V]): Call[V] = call.astID match { - case Assignment.ASTID ⇒ call.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID ⇒ call.asExprStmt.expr.asFunctionCall - case _ ⇒ call.asMethodCall + case Assignment.ASTID => call.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID => call.asExprStmt.expr.asFunctionCall + case _ => call.asMethodCall } override def specialCase(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact]): Option[ProperPropertyComputationResult] = { @@ -85,12 +85,12 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) e private def baseMethodResult(source: (DeclaredMethod, Fact), propertyKey: IFDSPropertyMetaInformation[DeclaredMethodJavaStatement, Fact]): ProperPropertyComputationResult = { def c(eps: SomeEOptionP): ProperPropertyComputationResult = eps match { - case FinalP(p) ⇒ Result(source, p) + case FinalP(p) => Result(source, p) - case ep @ InterimUBP(ub: Property) ⇒ + case ep @ InterimUBP(ub: Property) => InterimResult.forUB(source, ub, Set(ep), c) - case epk ⇒ + case epk => InterimResult.forUB(source, propertyKey.create(Map.empty), Set(epk), c) } c(propertyStore((declaredMethods(source._1.definedMethod), source._2), propertyKey.key)) @@ -105,5 +105,5 @@ abstract class JavaBackwardIFDSProblem[IFDSFact <: AbstractIFDSFact, UnbalancedI * @return False, if no unbalanced return should be performed. */ def shouldPerformUnbalancedReturn(source: (DeclaredMethod, IFDSFact)): Boolean = - source._2.isInstanceOf[UnbalancedReturnFact[IFDSFact]] || entryPoints.contains(source) + source._2.isInstanceOf[UnbalancedReturnFact[IFDSFact @unchecked]] || entryPoints.contains(source) } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala index c808dbba5a..60a13a91e7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala @@ -5,7 +5,7 @@ import org.opalj.br._ import org.opalj.br.analyses.SomeProject import org.opalj.collection.immutable.EmptyIntTrieSet import org.opalj.ifds.{AbstractIFDSFact, SubsumableFact, SubsumableNullFact} -import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem ⇒ NewJavaIFDSProblem} +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem => NewJavaIFDSProblem} import org.opalj.tac._ import org.opalj.value.ValueInformation @@ -28,9 +28,9 @@ case class VariableType(definedBy: Int, t: ReferenceType, upperBound: Boolean) e */ override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { if (upperBound) other match { - case VariableType(definedByOther, tOther, _) if definedBy == definedByOther && t.isObjectType && tOther.isObjectType ⇒ + case VariableType(definedByOther, tOther, _) if definedBy == definedByOther && t.isObjectType && tOther.isObjectType => project.classHierarchy.isSubtypeOf(tOther.asObjectType, t.asObjectType) - case _ ⇒ false + case _ => false } else false } @@ -50,9 +50,9 @@ case class CalleeType(line: Int, t: ReferenceType, upperBound: Boolean) extends */ override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { if (upperBound) other match { - case CalleeType(lineOther, tOther, _) if line == lineOther && t.isObjectType && tOther.isObjectType ⇒ + case CalleeType(lineOther, tOther, _) if line == lineOther && t.isObjectType && tOther.isObjectType => tOther.asObjectType.isSubtypeOf(t.asObjectType)(project.classHierarchy) - case _ ⇒ false + case _ => false } else false } @@ -67,9 +67,9 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] override def entryPoints: Seq[(DeclaredMethod, VTAFact)] = { project.allProjectClassFiles .filter(classInsideAnalysisContext) - .flatMap(classFile ⇒ classFile.methods) + .flatMap(classFile => classFile.methods) .filter(isEntryPoint) - .map(method ⇒ declaredMethods(method)) + .map(method => declaredMethods(method)) .flatMap(entryPointsForMethod) } @@ -87,10 +87,10 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] ): Set[VTAFact] = { val stmt = statement.stmt stmt.astID match { - case Assignment.ASTID ⇒ + case Assignment.ASTID => // Add facts for the assigned variable. in ++ newFacts(statement.method, statement.stmt.asAssignment.expr, statement.index, in) - case ArrayStore.ASTID ⇒ + case ArrayStore.ASTID => /* * Add facts for the array store, like it was a variable assignment. * By doing so, we only want to get the variable's type. @@ -101,14 +101,14 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] val flow = scala.collection.mutable.Set.empty[VTAFact] flow ++= in newFacts(statement.method, stmt.asArrayStore.value, statement.index, in).foreach { - case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) ⇒ + case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) => stmt.asArrayStore.arrayRef.asVar.definedBy .foreach(flow += VariableType(_, ArrayType(t), upperBound)) - case _ ⇒ // Nothing to do + case _ => // Nothing to do } flow.toSet // If the statement is neither an assignment, nor an array store, we just propagate our facts. - case _ ⇒ in + case _ => in } } @@ -127,13 +127,13 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] // Iterate over all input facts and over all parameters of the call. val flow = scala.collection.mutable.Set.empty[VTAFact] in.foreach { - case VariableType(definedBy, t, upperBound) ⇒ + case VariableType(definedBy, t, upperBound) => allParams.iterator.zipWithIndex.foreach { /* * We are only interested in a pair of a variable type and a parameter, if the * variable and the parameter can refer to the same object. */ - case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) ⇒ + case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) => // If this is the case, create a new fact for the method's formal parameter. flow += VariableType( NewJavaIFDSProblem @@ -141,9 +141,9 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] t, upperBound ) - case _ ⇒ // Nothing to do + case _ => // Nothing to do } - case _ ⇒ // Nothing to do + case _ => // Nothing to do } flow.toSet } @@ -160,11 +160,11 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] ): Set[VTAFact] = { // Check, to which variables the callee may refer val calleeDefinitionSites = asCall(call.stmt).receiverOption - .map(callee ⇒ callee.asVar.definedBy) + .map(callee => callee.asVar.definedBy) .getOrElse(EmptyIntTrieSet) val calleeTypeFacts = in.collect { // If we know the variable's type, we also know on which type the call is performed. - case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) ⇒ + case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) => CalleeType(call.index, t, upperBound) } if (in.size >= calleeTypeFacts.size) in ++ calleeTypeFacts @@ -187,7 +187,7 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] val returnValue = exit.stmt.asReturnValue.expr.asVar in.collect { // If we know the type of the return value, we create a fact for the assigned variable. - case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) ⇒ + case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) => VariableType(call.index, t, upperBound) } } else Set.empty @@ -203,7 +203,7 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] super.outsideAnalysisContext(callee).isEmpty) None else { - Some(((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[VTAFact]) ⇒ { + Some(((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[VTAFact]) => { val returnType = callee.descriptor.returnType if (call.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { Set(VariableType(call.index, returnType.asReferenceType, upperBound = true)) @@ -226,26 +226,26 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] statementIndex: Int, in: Set[VTAFact] ): Iterator[VariableType] = expression.astID match { - case New.ASTID ⇒ + case New.ASTID => in.iterator.collect { // When a constructor is called, we always know the exact type. - case VTANullFact ⇒ + case VTANullFact => VariableType(statementIndex, expression.asNew.tpe, upperBound = false) } - case Var.ASTID ⇒ + case Var.ASTID => in.iterator.collect { // When we know the source type, we also know the type of the assigned variable. - case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) ⇒ + case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) => VariableType(statementIndex, t, upperBound) } - case ArrayLoad.ASTID ⇒ + case ArrayLoad.ASTID => in.iterator.collect { // When we know the array's type, we also know the type of the loaded element. case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && - expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) ⇒ + expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) => VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound) } - case GetField.ASTID | GetStatic.ASTID ⇒ + case GetField.ASTID | GetStatic.ASTID => val t = expression.asFieldRead.declaredFieldType /* * We do not track field types. So we must assume, that it contains any subtype of its @@ -254,7 +254,7 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] if (t.isReferenceType) Iterator(VariableType(statementIndex, t.asReferenceType, upperBound = true)) else Iterator.empty - case _ ⇒ Iterator.empty + case _ => Iterator.empty } /** @@ -311,7 +311,7 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] private def entryPointsForMethod(method: DeclaredMethod): Seq[(DeclaredMethod, VTAFact)] = { // Iterate over all parameters, which have a reference type. (method.descriptor.parameterTypes.zipWithIndex.collect { - case (t, index) if t.isReferenceType ⇒ + case (t, index) if t.isReferenceType => /* * Create a fact for the parameter, which says, that the parameter may have any * subtype of its compile time type. @@ -326,6 +326,6 @@ class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact] * which hold independently of other source facts. * Map the input facts, in which we are interested, to a pair of the method and the fact. */ - } :+ VTANullFact).map(fact ⇒ (method, fact)) + } :+ VTANullFact).map(fact => (method, fact)) } } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala index 045fa8ad87..89a7b9bb83 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala @@ -5,7 +5,7 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.{DeclaredMethod, Method, ObjectType} import org.opalj.tac._ import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem ⇒ NewJavaIFDSProblem} +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem => NewJavaIFDSProblem} import org.opalj.tac.fpcf.analyses.ifds.old.{JavaBackwardIFDSProblem, DeclaredMethodJavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint._ @@ -19,43 +19,43 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF in: Set[TaintFact]): Set[TaintFact] = { val stmt = statement.stmt stmt.astID match { - case Assignment.ASTID if isTainted(statement.index, in) ⇒ + case Assignment.ASTID if isTainted(statement.index, in) => in ++ createNewTaints(stmt.asAssignment.expr, statement) - case ArrayStore.ASTID ⇒ + case ArrayStore.ASTID => val arrayStore = stmt.asArrayStore val arrayIndex = TaintProblem.getIntConstant(arrayStore.index, statement.code) val arrayDefinedBy = arrayStore.arrayRef.asVar.definedBy val result = in ++ in.collect { // In this case, we taint the whole array. - case Variable(index) if arrayDefinedBy.contains(index) ⇒ + case Variable(index) if arrayDefinedBy.contains(index) => createNewTaints(arrayStore.value, statement) // In this case, we taint exactly the stored element. case ArrayElement(index, taintedElement) if arrayDefinedBy.contains(index) && - (arrayIndex.isEmpty || arrayIndex.get == taintedElement) ⇒ + (arrayIndex.isEmpty || arrayIndex.get == taintedElement) => createNewTaints(arrayStore.value, statement) }.flatten if (arrayDefinedBy.size == 1 && arrayIndex.isDefined) result - ArrayElement(arrayDefinedBy.head, arrayIndex.get) else result - case PutField.ASTID ⇒ + case PutField.ASTID => val putField = stmt.asPutField val objectDefinedBy = putField.objRef.asVar.definedBy if (in.exists { case InstanceField(index, declaringClass, name) if objectDefinedBy.contains(index) && - putField.declaringClass == declaringClass && putField.name == name ⇒ + putField.declaringClass == declaringClass && putField.name == name => true - case _ ⇒ false + case _ => false }) in ++ createNewTaints(putField.value, statement) else in - case PutStatic.ASTID ⇒ + case PutStatic.ASTID => val putStatic = stmt.asPutStatic if (in.exists { - case StaticField(declaringClass, name) if putStatic.declaringClass == declaringClass && putStatic.name == name ⇒ + case StaticField(declaringClass, name) if putStatic.declaringClass == declaringClass && putStatic.name == name => true - case _ ⇒ false + case _ => false }) in ++ createNewTaints(putStatic.value, statement) else in - case _ ⇒ in + case _ => in } } @@ -81,37 +81,37 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF val flow = collection.mutable.Set.empty[TaintFact] if (call.stmt.astID == Assignment.ASTID && exit.stmt.astID == ReturnValue.ASTID) in.foreach { - case Variable(index) if index == call.index ⇒ + case Variable(index) if index == call.index => flow ++= createNewTaints(exit.stmt.asReturnValue.expr, exit) - case ArrayElement(index, taintedElement) if index == call.index ⇒ + case ArrayElement(index, taintedElement) if index == call.index => flow ++= createNewArrayElementTaints(exit.stmt.asReturnValue.expr, taintedElement, call) - case InstanceField(index, declaringClass, name) if index == call.index ⇒ + case InstanceField(index, declaringClass, name) if index == call.index => flow ++= createNewInstanceFieldTaints(exit.stmt.asReturnValue.expr, declaringClass, name, call) - case _ ⇒ // Nothing to do + case _ => // Nothing to do } val thisOffset = if (callee.definedMethod.isStatic) 0 else 1 callObject.allParams.iterator.zipWithIndex - .filter(pair ⇒ (pair._2 == 0 && !staticCall) || + .filter(pair => (pair._2 == 0 && !staticCall) || callObject.descriptor.parameterTypes(pair._2 - thisOffset).isReferenceType) - .foreach { pair ⇒ + .foreach { pair => val param = pair._1.asVar val paramIndex = pair._2 in.foreach { - case Variable(index) if param.definedBy.contains(index) ⇒ + case Variable(index) if param.definedBy.contains(index) => flow += Variable(NewJavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall)) - case ArrayElement(index, taintedElement) if param.definedBy.contains(index) ⇒ + case ArrayElement(index, taintedElement) if param.definedBy.contains(index) => flow += ArrayElement( NewJavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall), taintedElement ) - case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) ⇒ + case InstanceField(index, declaringClass, name) if param.definedBy.contains(index) => flow += InstanceField( NewJavaIFDSProblem.switchParamAndVariableIndex(paramIndex, staticCall), declaringClass, name ) - case staticField: StaticField ⇒ flow += staticField - case _ ⇒ // Nothing to do + case staticField: StaticField => flow += staticField + case _ => // Nothing to do } } flow.toSet @@ -125,7 +125,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF in: Set[TaintFact], source: (DeclaredMethod, TaintFact)): Set[TaintFact] = { val flowFact = createFlowFactAtCall(call, in, source) - val result = in -- sanitizeParameters(call, in) + val result = in.filter(!sanitizesParameter(call, _)) if (flowFact.isDefined) result + flowFact.get else result } @@ -135,18 +135,18 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF */ override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[TaintFact]) ⇒ { + case Some(_) => Some((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[TaintFact]) => { val callStatement = asCall(call.stmt) in ++ in.collect { - case Variable(index) if index == call.index ⇒ + case Variable(index) if index == call.index => callStatement.allParams.flatMap(createNewTaints(_, call)) - case ArrayElement(index, _) if index == call.index ⇒ + case ArrayElement(index, _) if index == call.index => callStatement.allParams.flatMap(createNewTaints(_, call)) - case InstanceField(index, _, _) if index == call.index ⇒ + case InstanceField(index, _, _) if index == call.index => callStatement.allParams.flatMap(createNewTaints(_, call)) }.flatten }) - case None ⇒ None + case None => None } /** @@ -159,7 +159,7 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF taintActualsIfFormalsTainted(source._1, call, facts, source, isCallFlow = false) .map(UnbalancedTaintFact(call.index, _, currentCallChain(source))) // Avoid infinite loops. - .filter(unbalancedTaintFact ⇒ !containsHeadTwice(unbalancedTaintFact.callChain)) + .filter(unbalancedTaintFact => !containsHeadTwice(unbalancedTaintFact.callChain)) /** * Called in callToReturnFlow. Creates a FlowFact if necessary. @@ -223,8 +223,8 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF val method = source._1.definedMethod val sourceFact = source._2 sourceFact match { - case fact: UnbalancedTaintFact ⇒ method +: fact.callChain - case _ ⇒ Seq(method) + case fact: UnbalancedTaintFact => method +: fact.callChain + case _ => Seq(method) } } @@ -246,10 +246,10 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF * @return True, if the variable or array element is tainted. */ private def isTainted(index: Int, in: Set[TaintFact], taintedElement: Option[Int] = None): Boolean = in.exists { - case Variable(variableIndex) ⇒ variableIndex == index - case ArrayElement(variableIndex, element) ⇒ + case Variable(variableIndex) => variableIndex == index + case ArrayElement(variableIndex, element) => variableIndex == index && (taintedElement.isEmpty || taintedElement.get == element) - case _ ⇒ false + case _ => false } /** @@ -261,25 +261,25 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF */ private def createNewTaints(expression: Expr[V], statement: DeclaredMethodJavaStatement): Set[TaintFact] = expression.astID match { - case Var.ASTID ⇒ expression.asVar.definedBy.map(Variable) - case ArrayLoad.ASTID ⇒ + case Var.ASTID => expression.asVar.definedBy.map(Variable) + case ArrayLoad.ASTID => val arrayLoad = expression.asArrayLoad val arrayIndex = TaintProblem.getIntConstant(expression.asArrayLoad.index, statement.code) val arrayDefinedBy = arrayLoad.arrayRef.asVar.definedBy if (arrayIndex.isDefined) arrayDefinedBy.map(ArrayElement(_, arrayIndex.get)) else arrayDefinedBy.map(Variable) case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | - PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) ⇒ + PrimitiveTypecastExpr.ASTID | NewArray.ASTID | ArrayLength.ASTID => + (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) => acc ++ createNewTaints(expression.subExpr(subExpr), statement)) - case GetField.ASTID ⇒ + case GetField.ASTID => val getField = expression.asGetField getField.objRef.asVar.definedBy .map(InstanceField(_, getField.declaringClass, getField.name)) - /*case GetStatic.ASTID ⇒ + /*case GetStatic.ASTID => val getStatic = expression.asGetStatic Set(StaticField(getStatic.declaringClass, getStatic.name))*/ - case _ ⇒ Set.empty + case _ => Set.empty } /** @@ -293,19 +293,19 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF private def createNewArrayElementTaints(expression: Expr[V], taintedElement: Int, statement: DeclaredMethodJavaStatement): Set[TaintFact] = createNewTaints(expression, statement).map { - case Variable(variableIndex) ⇒ ArrayElement(variableIndex, taintedElement) + case Variable(variableIndex) => ArrayElement(variableIndex, taintedElement) // We do not nest taints. Instead, we taint the whole inner array. - case ArrayElement(variableIndex, _) ⇒ ArrayElement(variableIndex, taintedElement) - case InstanceField(variableIndex, _, _) ⇒ ArrayElement(variableIndex, taintedElement) + case ArrayElement(variableIndex, _) => ArrayElement(variableIndex, taintedElement) + case InstanceField(variableIndex, _, _) => ArrayElement(variableIndex, taintedElement) } private def createNewInstanceFieldTaints(expression: Expr[V], declaringClass: ObjectType, name: String, statement: DeclaredMethodJavaStatement): Set[TaintFact] = createNewTaints(expression, statement).map { - case Variable(variableIndex) ⇒ InstanceField(variableIndex, declaringClass, name) + case Variable(variableIndex) => InstanceField(variableIndex, declaringClass, name) // We do not nest taints. Instead, taint the whole field. - case ArrayElement(variableIndex, _) ⇒ InstanceField(variableIndex, declaringClass, name) - case InstanceField(variableIndex, _, _) ⇒ + case ArrayElement(variableIndex, _) => InstanceField(variableIndex, declaringClass, name) + case InstanceField(variableIndex, _, _) => InstanceField(variableIndex, declaringClass, name) } @@ -334,29 +334,29 @@ abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIF val staticCall = callee.definedMethod.isStatic val thisOffset = if (staticCall) 0 else 1 val formalParameterIndices = (0 until callStatement.descriptor.parametersCount) - .map(index ⇒ NewJavaIFDSProblem.switchParamAndVariableIndex(index + thisOffset, staticCall)) + .map(index => NewJavaIFDSProblem.switchParamAndVariableIndex(index + thisOffset, staticCall)) val facts = scala.collection.mutable.Set.empty[TaintFact] calleeFacts.foreach { - case Variable(index) if formalParameterIndices.contains(index) ⇒ + case Variable(index) if formalParameterIndices.contains(index) => facts ++= createNewTaints( callStatement.allParams(NewJavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), call ) - case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) ⇒ + case ArrayElement(index, taintedElement) if formalParameterIndices.contains(index) => facts ++= createNewArrayElementTaints( callStatement.allParams(NewJavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), taintedElement, call ) - case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) ⇒ + case InstanceField(index, declaringClass, name) if formalParameterIndices.contains(index) => facts ++= createNewInstanceFieldTaints( callStatement.allParams(NewJavaIFDSProblem.switchParamAndVariableIndex(index, staticCall)), declaringClass, name, call ) - case staticField: StaticField ⇒ facts += staticField + case staticField: StaticField => facts += staticField // If the source was reached in a callee, create a flow fact from this method to the sink. - case calleeFact: FlowFact if isCallFlow ⇒ + case calleeFact: FlowFact if isCallFlow => val callerFact = applyFlowFactFromCallee(calleeFact, source) if (callerFact.isDefined) facts += callerFact.get - case _ ⇒ // Nothing to do + case _ => // Nothing to do } facts.toSet } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala index 35a848b379..6561101b87 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala @@ -8,7 +8,7 @@ import org.opalj.tac.fpcf.analyses.ifds.JavaMethod import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.old.{JavaIFDSProblem, DeclaredMethodJavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint._ -import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem ⇒ NewJavaIFDSProblem} +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem => NewJavaIFDSProblem} abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[TaintFact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, TaintFact] { override def nullFact: TaintFact = TaintNullFact @@ -19,43 +19,43 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem override def normalFlow(statement: DeclaredMethodJavaStatement, successor: Option[DeclaredMethodJavaStatement], in: Set[TaintFact]): Set[TaintFact] = statement.stmt.astID match { - case Assignment.ASTID ⇒ + case Assignment.ASTID => in ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) - case ArrayStore.ASTID ⇒ + case ArrayStore.ASTID => val store = statement.stmt.asArrayStore val definedBy = store.arrayRef.asVar.definedBy val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) if (isTainted(store.value, in)) { if (arrayIndex.isDefined) // Taint a known array index - definedBy.foldLeft(in) { (c, n) ⇒ + definedBy.foldLeft(in) { (c, n) => c + ArrayElement(n, arrayIndex.get) } else // Taint the whole array if the index is unknown - definedBy.foldLeft(in) { (c, n) ⇒ + definedBy.foldLeft(in) { (c, n) => c + Variable(n) } } else if (arrayIndex.isDefined && definedBy.size == 1) // Untaint if possible in - ArrayElement(definedBy.head, arrayIndex.get) else in - case PutField.ASTID ⇒ + case PutField.ASTID => val put = statement.stmt.asPutField val definedBy = put.objRef.asVar.definedBy if (isTainted(put.value, in)) - definedBy.foldLeft(in) { (in, defSite) ⇒ + definedBy.foldLeft(in) { (in, defSite) => in + InstanceField(defSite, put.declaringClass, put.name) } else in - case PutStatic.ASTID ⇒ + case PutStatic.ASTID => val put = statement.stmt.asPutStatic if (isTainted(put.value, in)) in + StaticField(put.declaringClass, put.name) else in - case _ ⇒ in + case _ => in } /** @@ -73,44 +73,44 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem val allParamsWithIndices = allParams.zipWithIndex in.foreach { // Taint formal parameter if actual parameter is tainted - case Variable(index) ⇒ + case Variable(index) => allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => facts += Variable(NewJavaIFDSProblem.switchParamAndVariableIndex( paramIndex, callee.definedMethod.isStatic )) - case _ ⇒ // Nothing to do + case _ => // Nothing to do } // Taint element of formal parameter if element of actual parameter is tainted - case ArrayElement(index, taintedIndex) ⇒ + case ArrayElement(index, taintedIndex) => allParamsWithIndices.foreach { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => facts += ArrayElement( NewJavaIFDSProblem.switchParamAndVariableIndex(paramIndex, callee.definedMethod.isStatic), taintedIndex ) - case _ ⇒ // Nothing to do + case _ => // Nothing to do } - case InstanceField(index, declClass, taintedField) ⇒ + case InstanceField(index, declClass, taintedField) => // Taint field of formal parameter if field of actual parameter is tainted // Only if the formal parameter is of a type that may have that field! allParamsWithIndices.foreach { case (param, pIndex) if param.asVar.definedBy.contains(index) && (NewJavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic) != -1 || - project.classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) ⇒ + project.classHierarchy.isSubtypeOf(declClass, callee.declaringClassType)) => facts += InstanceField( NewJavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.definedMethod.isStatic), declClass, taintedField ) - case _ ⇒ // Nothing to do + case _ => // Nothing to do } - case sf: StaticField ⇒ facts += sf + case sf: StaticField => facts += sf - case _ ⇒ // Nothing to do + case _ => // Nothing to do } } @@ -148,49 +148,49 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem var flows: Set[TaintFact] = Set.empty in.foreach { // Taint actual parameter if formal parameter is tainted - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) => val param = allParams( NewJavaIFDSProblem.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(Variable) // Taint element of actual parameter if element of formal parameter is tainted - case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 => val param = allParams( NewJavaIFDSProblem.switchParamAndVariableIndex(index, callee.definedMethod.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) - case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 => // Taint field of actual parameter if field of formal parameter is tainted val param = allParams(NewJavaIFDSProblem.switchParamAndVariableIndex(index, callee.definedMethod.isStatic)) - param.asVar.definedBy.foreach { defSite ⇒ + param.asVar.definedBy.foreach { defSite => flows += InstanceField(defSite, declClass, taintedField) } - case sf: StaticField ⇒ flows += sf + case sf: StaticField => flows += sf // Track the call chain to the sink back - case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ + case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) => flows += FlowFact(JavaMethod(call.method) +: flow) - case _ ⇒ + case _ => } // Propagate taints of the return value if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy in.foreach { - case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + case Variable(index) if returnValueDefinedBy.contains(index) => flows += Variable(call.index) - case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) => flows += ArrayElement(call.index, taintedIndex) - case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) => flows += InstanceField(call.index, declClass, taintedField) - case TaintNullFact ⇒ + case TaintNullFact => val taints = createTaints(callee, call) if (taints.nonEmpty) flows ++= taints - case _ ⇒ // Nothing to do + case _ => // Nothing to do } } val flowFact = createFlowFact(callee, call, in) @@ -205,7 +205,7 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem override def callToReturnFlow(call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[TaintFact], source: (DeclaredMethod, TaintFact)): Set[TaintFact] = - in -- sanitizeParameters(call, in) + in.filter(!sanitizesParameter(call, _)) /** * Called, when the exit to return facts are computed for some `callee` with the null fact and @@ -235,24 +235,24 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem */ override def outsideAnalysisContext(callee: DeclaredMethod): Option[OutsideAnalysisContextHandler] = { super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some(((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[TaintFact]) ⇒ { + case Some(_) => Some(((call: DeclaredMethodJavaStatement, successor: DeclaredMethodJavaStatement, in: Set[TaintFact]) => { val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params if (call.stmt.astID == Assignment.ASTID && in.exists { - case Variable(index) ⇒ + case Variable(index) => allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false + case (param, _) if param.asVar.definedBy.contains(index) => true + case _ => false } - case ArrayElement(index, _) ⇒ + case ArrayElement(index, _) => allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false + case (param, _) if param.asVar.definedBy.contains(index) => true + case _ => false } - case _ ⇒ false + case _ => false }) Set(Variable(call.index)) else Set.empty }): OutsideAnalysisContextHandler) - case None ⇒ None + case None => None } } @@ -277,51 +277,51 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem */ private def createNewTaints(expression: Expr[V], statement: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = expression.astID match { - case Var.ASTID ⇒ + case Var.ASTID => val definedBy = expression.asVar.definedBy in.collect { - case Variable(index) if definedBy.contains(index) ⇒ + case Variable(index) if definedBy.contains(index) => Variable(statement.index) } - case ArrayLoad.ASTID ⇒ + case ArrayLoad.ASTID => val loadExpression = expression.asArrayLoad val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy if (in.exists { // One specific array element may be tainted - case ArrayElement(index, taintedElement) ⇒ + case ArrayElement(index, taintedElement) => val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) arrayDefinedBy.contains(index) && (loadedIndex.isEmpty || taintedElement == loadedIndex.get) // Or the whole array - case Variable(index) ⇒ arrayDefinedBy.contains(index) - case _ ⇒ false + case Variable(index) => arrayDefinedBy.contains(index) + case _ => false }) Set(Variable(statement.index)) else Set.empty - case GetField.ASTID ⇒ + case GetField.ASTID => val get = expression.asGetField val objectDefinedBy = get.objRef.asVar.definedBy if (in.exists { // The specific field may be tainted - case InstanceField(index, _, taintedField) ⇒ + case InstanceField(index, _, taintedField) => taintedField == get.name && objectDefinedBy.contains(index) // Or the whole object - case Variable(index) ⇒ objectDefinedBy.contains(index) - case _ ⇒ false + case Variable(index) => objectDefinedBy.contains(index) + case _ => false }) Set(Variable(statement.index)) else Set.empty - case GetStatic.ASTID ⇒ + case GetStatic.ASTID => val get = expression.asGetStatic if (in.contains(StaticField(get.declaringClass, get.name))) Set(Variable(statement.index)) else Set.empty case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | - NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) ⇒ + NewArray.ASTID | ArrayLength.ASTID => + (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) => acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) - case _ ⇒ Set.empty + case _ => Set.empty } /** @@ -334,10 +334,10 @@ abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem private def isTainted(expression: Expr[V], in: Set[TaintFact]): Boolean = { val definedBy = expression.asVar.definedBy expression.isVar && in.exists { - case Variable(index) ⇒ definedBy.contains(index) - case ArrayElement(index, _) ⇒ definedBy.contains(index) - case InstanceField(index, _, _) ⇒ definedBy.contains(index) - case _ ⇒ false + case Variable(index) => definedBy.contains(index) + case ArrayElement(index, _) => definedBy.contains(index) + case InstanceField(index, _, _) => definedBy.contains(index) + case _ => false } } } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 8d5646e670..987635fe5a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -21,9 +21,9 @@ abstract class ForwardTaintProblem(project: SomeProject) */ override def normalFlow(statement: JavaStatement, in: TaintFact, predecessor: Option[JavaStatement]): Set[TaintFact] = { statement.stmt.astID match { - case Assignment.ASTID ⇒ + case Assignment.ASTID => Set(in) ++ createNewTaints(statement.stmt.asAssignment.expr, statement, in) - case ArrayStore.ASTID ⇒ + case ArrayStore.ASTID => val store = statement.stmt.asArrayStore val definedBy = store.arrayRef.asVar.definedBy val arrayIndex = TaintProblem.getIntConstant(store.index, statement.code) @@ -38,20 +38,20 @@ abstract class ForwardTaintProblem(project: SomeProject) // untaint Set() } else Set(in) - case PutField.ASTID ⇒ + case PutField.ASTID => val put = statement.stmt.asPutField val definedBy = put.objRef.asVar.definedBy if (isTainted(put.value, in)) Set(in) ++ definedBy.map { InstanceField(_, put.declaringClass, put.name) } else Set(in) - case PutStatic.ASTID ⇒ + case PutStatic.ASTID => val put = statement.stmt.asPutStatic if (isTainted(put.value, in)) Set(in, StaticField(put.declaringClass, put.name)) else Set(in) - case _ ⇒ Set(in) + case _ => Set(in) } } @@ -67,44 +67,44 @@ abstract class ForwardTaintProblem(project: SomeProject) val allParamsWithIndices = allParams.zipWithIndex in match { // Taint formal parameter if actual parameter is tainted - case Variable(index) ⇒ + case Variable(index) => allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => Some(Variable(JavaIFDSProblem.switchParamAndVariableIndex( paramIndex, callee.isStatic ))) - case _ ⇒ None // Nothing to do + case _ => None // Nothing to do }.toSet // Taint element of formal parameter if element of actual parameter is tainted - case ArrayElement(index, taintedIndex) ⇒ + case ArrayElement(index, taintedIndex) => allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) ⇒ + case (param, paramIndex) if param.asVar.definedBy.contains(index) => Some(ArrayElement( JavaIFDSProblem.switchParamAndVariableIndex(paramIndex, callee.isStatic), taintedIndex )) - case _ ⇒ None // Nothing to do + case _ => None // Nothing to do }.toSet - case InstanceField(index, declClass, taintedField) ⇒ + case InstanceField(index, declClass, taintedField) => // Taint field of formal parameter if field of actual parameter is tainted // Only if the formal parameter is of a type that may have that field! allParamsWithIndices.flatMap { case (param, pIndex) if param.asVar.definedBy.contains(index) && (JavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.isStatic) != -1 || - project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) ⇒ + project.classHierarchy.isSubtypeOf(declClass, declaredMethods(callee).declaringClassType)) => Some(InstanceField( JavaIFDSProblem.switchParamAndVariableIndex(pIndex, callee.isStatic), declClass, taintedField )) - case _ ⇒ None // Nothing to do + case _ => None // Nothing to do }.toSet - case sf: StaticField ⇒ Set(sf) + case sf: StaticField => Set(sf) - case _ ⇒ Set() // Nothing to do + case _ => Set() // Nothing to do } } @@ -121,7 +121,7 @@ abstract class ForwardTaintProblem(project: SomeProject) override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { if (!isPossibleReturnFlow(exit, successor)) return Set.empty - val callee = exit.callable() + val callee = exit.callable /** * Checks whether the callee's formal parameter is of a reference type. */ @@ -141,49 +141,49 @@ abstract class ForwardTaintProblem(project: SomeProject) var flows: Set[TaintFact] = Set.empty in match { // Taint actual parameter if formal parameter is tainted - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) ⇒ + case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) => val param = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(Variable) // Taint element of actual parameter if element of formal parameter is tainted - case ArrayElement(index, taintedIndex) if index < 0 && index > -100 ⇒ + case ArrayElement(index, taintedIndex) if index < 0 && index > -100 => val param = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) - case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 ⇒ + case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 => // Taint field of actual parameter if field of formal parameter is tainted val param = allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic)) - param.asVar.definedBy.foreach { defSite ⇒ + param.asVar.definedBy.foreach { defSite => flows += InstanceField(defSite, declClass, taintedField) } - case sf: StaticField ⇒ flows += sf + case sf: StaticField => flows += sf // Track the call chain to the sink back - case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) ⇒ + case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) => flows += FlowFact(JavaMethod(call.method) +: flow) - case _ ⇒ + case _ => } // Propagate taints of the return value if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy in match { - case Variable(index) if returnValueDefinedBy.contains(index) ⇒ + case Variable(index) if returnValueDefinedBy.contains(index) => flows += Variable(call.index) - case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) ⇒ + case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) => flows += ArrayElement(call.index, taintedIndex) - case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) ⇒ + case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) => flows += InstanceField(call.index, declClass, taintedField) - case TaintNullFact ⇒ + case TaintNullFact => val taints = createTaints(callee, call) if (taints.nonEmpty) flows ++= taints - case _ ⇒ // Nothing to do + case _ => // Nothing to do } } val flowFact = createFlowFact(callee, call, in) @@ -226,24 +226,24 @@ abstract class ForwardTaintProblem(project: SomeProject) */ override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { super.outsideAnalysisContext(callee) match { - case Some(_) ⇒ Some(((call: JavaStatement, successor: JavaStatement, in: TaintFact, _: Getter) ⇒ { + case Some(_) => Some(((call: JavaStatement, successor: JavaStatement, in: TaintFact, _: Getter) => { val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params if (call.stmt.astID == Assignment.ASTID && (in match { - case Variable(index) ⇒ + case Variable(index) => allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false + case (param, _) if param.asVar.definedBy.contains(index) => true + case _ => false } - case ArrayElement(index, _) ⇒ + case ArrayElement(index, _) => allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) ⇒ true - case _ ⇒ false + case (param, _) if param.asVar.definedBy.contains(index) => true + case _ => false } - case _ ⇒ false + case _ => false })) Set(Variable(call.index)) else Set.empty }): OutsideAnalysisContextHandler) - case None ⇒ None + case None => None } } @@ -259,52 +259,52 @@ abstract class ForwardTaintProblem(project: SomeProject) */ private def createNewTaints(expression: Expr[V], statement: JavaStatement, in: TaintFact): Set[TaintFact] = { expression.astID match { - case Var.ASTID ⇒ + case Var.ASTID => val definedBy = expression.asVar.definedBy in match { - case Variable(index) if definedBy.contains(index) ⇒ + case Variable(index) if definedBy.contains(index) => Set(Variable(statement.index)) - case _ ⇒ Set() + case _ => Set() } - case ArrayLoad.ASTID ⇒ + case ArrayLoad.ASTID => val loadExpression = expression.asArrayLoad val arrayDefinedBy = loadExpression.arrayRef.asVar.definedBy if (in match { // One specific array element may be tainted - case ArrayElement(index, taintedElement) ⇒ + case ArrayElement(index, taintedElement) => val loadedIndex = TaintProblem.getIntConstant(loadExpression.index, statement.code) arrayDefinedBy.contains(index) && (loadedIndex.isEmpty || taintedElement == loadedIndex.get) // Or the whole array - case Variable(index) ⇒ arrayDefinedBy.contains(index) - case _ ⇒ false + case Variable(index) => arrayDefinedBy.contains(index) + case _ => false }) Set(Variable(statement.index)) else Set.empty - case GetField.ASTID ⇒ + case GetField.ASTID => val get = expression.asGetField val objectDefinedBy = get.objRef.asVar.definedBy if (in match { // The specific field may be tainted - case InstanceField(index, _, taintedField) ⇒ + case InstanceField(index, _, taintedField) => taintedField == get.name && objectDefinedBy.contains(index) // Or the whole object - case Variable(index) ⇒ objectDefinedBy.contains(index) - case _ ⇒ false + case Variable(index) => objectDefinedBy.contains(index) + case _ => false }) Set(Variable(statement.index)) else Set.empty - case GetStatic.ASTID ⇒ + case GetStatic.ASTID => val get = expression.asGetStatic if (in == StaticField(get.declaringClass, get.name)) Set(Variable(statement.index)) else Set.empty case BinaryExpr.ASTID | PrefixExpr.ASTID | Compare.ASTID | PrimitiveTypecastExpr.ASTID | - NewArray.ASTID | ArrayLength.ASTID ⇒ - (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) ⇒ + NewArray.ASTID | ArrayLength.ASTID => + (0 until expression.subExprCount).foldLeft(Set.empty[TaintFact])((acc, subExpr) => acc ++ createNewTaints(expression.subExpr(subExpr), statement, in)) - case _ ⇒ Set.empty + case _ => Set.empty } } @@ -318,10 +318,10 @@ abstract class ForwardTaintProblem(project: SomeProject) private def isTainted(expression: Expr[V], in: TaintFact): Boolean = { val definedBy = expression.asVar.definedBy expression.isVar && (in match { - case Variable(index) ⇒ definedBy.contains(index) - case ArrayElement(index, _) ⇒ definedBy.contains(index) - case InstanceField(index, _, _) ⇒ definedBy.contains(index) - case _ ⇒ false + case Variable(index) => definedBy.contains(index) + case ArrayElement(index, _) => definedBy.contains(index) + case InstanceField(index, _, _) => definedBy.contains(index) + case _ => false }) } } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index c16314b9ba..bc76a555e3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -51,7 +51,7 @@ case class InstanceField(index: Int, classType: ObjectType, fieldName: String) e case class FlowFact(flow: Seq[Callable]) extends TaintFact { override val hashCode: Int = { var r = 1 - flow.foreach(f ⇒ r = (r + f.hashCode()) * 31) + flow.foreach(f => r = (r + f.hashCode()) * 31) r } } diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala index ac6e2c566b..e1d28b25ac 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -23,11 +23,11 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(Method, TaintFact)] = p.allProjectClassFiles.filter(classFile ⇒ + override val entryPoints: Seq[(Method, TaintFact)] = p.allProjectClassFiles.filter(classFile => classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") - .flatMap(classFile ⇒ classFile.methods) - .filter(method ⇒ method.isPublic && outsideAnalysisContext(method).isEmpty) - .map(method ⇒ method → TaintNullFact) + .flatMap(classFile => classFile.methods) + .filter(method => method.isPublic && outsideAnalysisContext(method).isEmpty) + .map(method => method -> TaintNullFact) /** * The sanitize method is a sanitizer. diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala index 18ab076ed6..b3108ad321 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala @@ -26,10 +26,10 @@ class BackwardTaintAnalysisFixture(implicit val project: SomeProject) class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p) { - override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = p.allProjectClassFiles.filter(classFile ⇒ + override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = p.allProjectClassFiles.filter(classFile => classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") .flatMap(_.methods).filter(_.name == "sink") - .map(method ⇒ declaredMethods(method) → + .map(method => declaredMethods(method) -> Variable(JavaIFDSProblem.switchParamAndVariableIndex(0, isStaticMethod = true))) /** @@ -40,7 +40,7 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p /** * We do not sanitize paramters. */ - override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = Set.empty + override protected def sanitizesParameter(call: DeclaredMethodJavaStatement, in: TaintFact): Boolean = false /** * Create a flow fact, if a source method is called and the returned value is tainted. @@ -51,8 +51,8 @@ class BackwardTaintProblemFixture(p: SomeProject) extends BackwardTaintProblem(p override protected def createFlowFactAtCall(call: DeclaredMethodJavaStatement, in: Set[TaintFact], source: (DeclaredMethod, TaintFact)): Option[FlowFact] = { if (in.exists { - case Variable(index) ⇒ index == call.index - case _ ⇒ false + case Variable(index) => index == call.index + case _ => false } && icfg.getCalleesIfCallStatement(call).get.exists(_.name == "source")) { val callChain = currentCallChain(source) // Avoid infinite loops. diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala index 342ca917d7..87e7068157 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala @@ -25,11 +25,11 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = p.allProjectClassFiles.filter(classFile ⇒ + override val entryPoints: Seq[(DeclaredMethod, TaintFact)] = p.allProjectClassFiles.filter(classFile => classFile.thisType.fqn == "org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass") - .flatMap(classFile ⇒ classFile.methods) - .filter(method ⇒ method.isPublic && outsideAnalysisContext(declaredMethods(method)).isEmpty) - .map(method ⇒ declaredMethods(method) → TaintNullFact) + .flatMap(classFile => classFile.methods) + .filter(method => method.isPublic && outsideAnalysisContext(declaredMethods(method)).isEmpty) + .map(method => declaredMethods(method) -> TaintNullFact) /** * The sanitize method is a sanitizer. @@ -39,7 +39,7 @@ class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) /** * We do not sanitize paramters. */ - override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = Set.empty + override protected def sanitizesParameter(call: DeclaredMethodJavaStatement, in: TaintFact): Boolean = false /** * Creates a new variable fact for the callee, if the source was called. From c050890067df5dfff3ccbf987afbf1363994400b Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Fri, 1 Jul 2022 17:09:51 +0200 Subject: [PATCH 53/67] Fix remaining errors after merge --- .../analyses/HerosVariableTypeAnalysis.scala | 2 +- .../analyses/heros/analyses/VTAEquality.scala | 9 +++-- .../HerosBackwardClassForNameAnalysis.scala | 2 +- .../HerosForwardClassForNameAnalysis.scala | 4 +- .../analyses/heros/cfg/OpalBackwardICFG.scala | 2 +- .../fpcf/analyses/heros/cfg/OpalICFG.scala | 4 +- ...rdClassForNameTaintAnalysisScheduler.scala | 2 +- ...rdClassForNameTaintAnalysisScheduler.scala | 10 +++-- .../ifds/old/taint/BackwardTaintProblem.scala | 13 +++++++ .../ifds/old/taint/TaintProblem.scala | 38 ------------------- 10 files changed, 33 insertions(+), 53 deletions(-) delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala index 55caf3730e..6360338a24 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/HerosVariableTypeAnalysis.scala @@ -20,7 +20,7 @@ import java.io.File import java.util import java.util.Collections import scala.annotation.tailrec -import scala.collection.JavaConverters._ +import scala.jdk.CollectionConverters._ /** * An implementation of the IFDSBasedVariableTypeAnalysis in the Heros framework. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala index f3439078bf..7d468a0d40 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/VTAEquality.scala @@ -3,11 +3,13 @@ package org.opalj.tac.fpcf.analyses.heros.analyses import java.io.File import java.net.URL -import scala.collection.JavaConverters._ + +import scala.jdk.CollectionConverters._ + import com.typesafe.config.Config import com.typesafe.config.ConfigValueFactory import heros.solver.IFDSSolver -import org.opalj.BaseConfig + import org.opalj.util.ScalaMajorVersion import org.opalj.fpcf.EPS import org.opalj.fpcf.FinalEP @@ -22,6 +24,7 @@ import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.br.analyses.DeclaredMethods import org.opalj.br.analyses.cg.InitialEntryPointsKey import org.opalj.br.analyses.cg.InitialInstantiatedTypesKey +import org.opalj.br.BaseConfig import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.tac.cg.RTACallGraphKey @@ -31,7 +34,7 @@ import org.opalj.tac.Assignment import org.opalj.tac.New import org.opalj.tac.Return import org.opalj.tac.ReturnValue -import org.opalj.tac.fpcf.analyses.ifds.old.{CalleeType, IFDSBasedVariableTypeAnalysisScheduler, VTAFact, VTANullFact, VTAResult, VariableType} +import org.opalj.tac.fpcf.analyses.ifds.old.{CalleeType, IFDSBasedVariableTypeAnalysisScheduler, VariableType, VTAFact, VTANullFact, VTAResult} object VTAEquality { diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala index 1377894911..8056a692f4 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -3,7 +3,7 @@ package org.opalj.tac.fpcf.analyses.heros.analyses.taint import java.io.File import java.util -import scala.collection.JavaConverters._ +import scala.jdk.CollectionConverters._ import heros.FlowFunction import heros.FlowFunctions import heros.flowfunc.Identity diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index 1be87f0227..0d32fd7568 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -19,7 +19,7 @@ import org.opalj.util.Milliseconds import java.io.File import java.util import java.util.Collections -import scala.collection.JavaConverters._ +import scala.jdk.CollectionConverters._ /** * An implementation of the ForwardClassForNameAnalysis in the Heros framework. @@ -270,7 +270,7 @@ class HerosForwardClassForNameAnalysis( .toSet case sf: StaticField => Set(sf) - case FlowFact(flow) if !flow.contains(stmt.method) => + case FlowFact(flow) => val flowFact = FlowFact(JavaMethod(stmt.method) +: flow) if (initialMethods.contains(stmt.method)) flowFacts = flowFacts.updated( diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala index 606b4d7faf..1130a6b639 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalBackwardICFG.scala @@ -9,7 +9,7 @@ import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import java.util.concurrent.ConcurrentLinkedQueue import java.util.{Collections, Collection => JCollection, List => JList, Set => JSet} -import scala.collection.JavaConverters._ +import scala.jdk.CollectionConverters._ /** * A backward ICFG for Heros analyses. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala index 9b8b35a608..d69bb1cb5c 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/cfg/OpalICFG.scala @@ -4,7 +4,7 @@ package org.opalj.tac.fpcf.analyses.heros.cfg import java.util.{List => JList} import java.util.{Collection => JCollection} import java.util.{Set => JSet} -import scala.collection.JavaConverters._ +import scala.jdk.CollectionConverters._ import heros.InterproceduralCFG import org.opalj.fpcf.FinalEP import org.opalj.fpcf.PropertyStore @@ -95,7 +95,7 @@ abstract class OpalICFG(project: SomeProject) extends InterproceduralCFG[JavaSta val FinalEP(_, callers) = ps(declaredMethod, Callers.key) callers .callers(declaredMethod) - .flatMap { + .iterator.flatMap { case (method, pc, true) => val TACode(_, code, pcToIndex, cfg, _) = tacai(method.definedMethod) val index = pcToIndex(pc) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index b2609ca50d..866d4a88d1 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -43,7 +43,7 @@ class BackwardClassForNameTaintProblem(p: SomeProject) extends BackwardTaintProb /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = Set.empty + override protected def sanitizesParameter(call: DeclaredMethodJavaStatement, in: TaintFact): Boolean = false /** * Do not perform unbalanced return for methods, which can be called from outside the library. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala index 276606a827..afd177d23e 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -5,14 +5,16 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.{DeclaredMethod, Method, ObjectType} import org.opalj.fpcf.PropertyStore import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} + import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.old._ -import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, Variable} +import org.opalj.tac.fpcf.analyses.ifds.taint.{FlowFact, TaintFact, Variable} import org.opalj.tac.fpcf.analyses.ifds._ import org.opalj.tac.fpcf.properties.OldTaint - import java.io.File +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem + /** * A forward IFDS taint analysis, which tracks the String parameters of all methods of the rt.jar, * which are callable from outside the library, to calls of Class.forName. @@ -25,7 +27,7 @@ class ForwardClassForNameTaintAnalysis$Scheduler private (implicit val project: extends ForwardIFDSAnalysis(new ForwardClassForNameTaintProblem(project), OldTaint) class ForwardClassForNameTaintProblem(project: SomeProject) - extends old.taint.ForwardTaintProblem(project) with old.taint.TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, TaintFact] { + extends old.taint.ForwardTaintProblem(project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, TaintFact] { /** * The string parameters of all public methods are entry points. @@ -46,7 +48,7 @@ class ForwardClassForNameTaintProblem(project: SomeProject) /** * There is no sanitizing in this analysis. */ - override protected def sanitizeParameters(call: DeclaredMethodJavaStatement, in: Set[TaintFact]): Set[TaintFact] = Set.empty + override protected def sanitizesParameter(call: DeclaredMethodJavaStatement, in: TaintFact): Boolean = false /** * This analysis does not create new taints on the fly. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala index 89a7b9bb83..c92d93978b 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala @@ -9,6 +9,19 @@ import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem => NewJavaIFDSProblem} import org.opalj.tac.fpcf.analyses.ifds.old.{JavaBackwardIFDSProblem, DeclaredMethodJavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.analyses.ifds.old.UnbalancedReturnFact +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact + +/** + * The unbalanced return fact of this analysis. + * + * @param index The index, at which the analyzed method is called by some caller. + * @param innerFact The fact, which will hold in the caller context after the call. + * @param callChain The current call chain from the sink. + */ +case class UnbalancedTaintFact(index: Int, innerFact: TaintFact, callChain: Seq[Method]) + extends UnbalancedReturnFact[TaintFact] with TaintFact + abstract class BackwardTaintProblem(project: SomeProject) extends JavaBackwardIFDSProblem[TaintFact, UnbalancedTaintFact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, TaintFact] { override def nullFact: TaintFact = TaintNullFact diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala deleted file mode 100644 index ed8d7499ad..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/TaintProblem.scala +++ /dev/null @@ -1,38 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds.old.taint - -import org.opalj.br.Method -import org.opalj.tac.fpcf.analyses.ifds.old.UnbalancedReturnFact -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact - -/** - * The unbalanced return fact of this analysis. - * - * @param index The index, at which the analyzed method is called by some caller. - * @param innerFact The fact, which will hold in the caller context after the call. - * @param callChain The current call chain from the sink. - */ -case class UnbalancedTaintFact(index: Int, innerFact: TaintFact, callChain: Seq[Method]) - extends UnbalancedReturnFact[TaintFact] with TaintFact - -trait TaintProblem[C, Statement, IFDSFact] { - - /** - * Checks, if some `callee` is a sanitizer, which sanitizes its return value. - * In this case, no return flow facts will be created. - * - * @param callee The method, which was called. - * @return True, if the method is a sanitizer. - */ - protected def sanitizesReturnValue(callee: C): Boolean - - /** - * Called in callToReturnFlow. This method can return facts, which will be removed after - * `callee` was called. I.e. the method could sanitize parameters. - * - * @param call The call statement. - * @param in The facts, which hold before the call. - * @return Facts, which will be removed from `in` after the call. - */ - protected def sanitizeParameters(call: Statement, in: Set[IFDSFact]): Set[IFDSFact] -} From 655d22aee112c0f4bafd36c7fc950ee97e5c3fe4 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Sat, 2 Jul 2022 02:23:42 +0200 Subject: [PATCH 54/67] Implement VTA in new IFDS for evaluation --- .../tac/fpcf/analyses/NewIFDSEvaluation.scala | 31 ++ .../scala/org/opalj/ifds/IFDSAnalysis.scala | 18 +- .../fpcf/analyses/ifds/EvaluationRunner.scala | 192 ++++++++++ .../ifds/IFDSBasedVariableTypeAnalysis.scala | 108 ++++++ .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 3 + .../analyses/ifds/VariableTypeProblem.scala | 356 ++++++++++++++++++ 6 files changed, 701 insertions(+), 7 deletions(-) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/NewIFDSEvaluation.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/EvaluationRunner.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/NewIFDSEvaluation.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/NewIFDSEvaluation.scala new file mode 100644 index 0000000000..89c145e258 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/NewIFDSEvaluation.scala @@ -0,0 +1,31 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses + +import java.io.File + +import org.opalj.tac.fpcf.analyses.heros.analyses.HerosVariableTypeAnalysisRunner +import org.opalj.tac.fpcf.analyses.ifds.old +import org.opalj.tac.fpcf.analyses.ifds.IFDSBasedVariableTypeAnalysisRunner + +/** + * Generates some evaluation files related to the AbstractIFDSAnalysis. + * + * @author Mario Trageser + */ +object NewIFDSEvaluation { + + /** + * args should contain exactly one parameter: + * A directory, which will contain the evaluation files; terminated with a '/'. + * + */ + def main(args: Array[String]): Unit = { + val dir = args(0) + new File(dir).mkdirs + + old.IFDSBasedVariableTypeAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"OldIFDSBasedVariableTypeAnalysis.txt")) + HerosVariableTypeAnalysisRunner.main(Array("-f", dir+"HerosVariableTypeAnalysis.txt")) + IFDSBasedVariableTypeAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"IFDSBasedVariableTypeAnalysis.txt")) + } + +} diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 30dd76b442..718fb9ad9b 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -68,17 +68,19 @@ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C] true case Some(Left(existingFacts)) => if (predecessor.isDefined) throw new IllegalArgumentException(s"${statement} does not accept a predecessor") - val isNew = !existingFacts.contains(fact) - edges = edges.updated(statement, Left(existingFacts + fact)) - isNew + if (isNew(existingFacts, fact)) { + edges = edges.updated(statement, Left(existingFacts + fact)) + true + } else false case Some(Right(existingFacts)) => predecessor match { case None => throw new IllegalArgumentException(s"${statement} requires a predecessor") case Some(predecessor) => existingFacts.get(statement) match { case Some(existingPredecessorFacts) => { - val isNew = !existingPredecessorFacts.contains(fact) - edges = edges.updated(statement, Right(existingFacts.updated(predecessor, existingPredecessorFacts + fact))) - isNew + if (isNew(existingPredecessorFacts, fact)) { + edges = edges.updated(statement, Right(existingFacts.updated(predecessor, existingPredecessorFacts + fact))) + true + } else false } case None => { edges = edges.updated(statement, Right(existingFacts.updated(predecessor, Set(fact)))) @@ -89,6 +91,8 @@ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C] } } + private def isNew(existingFacts: Set[IFDSFact], newFact: IFDSFact): Boolean = !existingFacts.contains(newFact) + /** * @param statement * @return The edges reaching statement if any. In case the statement minds about predecessors it is a map with an entry for each predecessor @@ -121,7 +125,7 @@ protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statem /** * Contains int variables, which count, how many times some method was called. */ -protected class Statistics { +class Statistics { var normalFlow = 0 var callFlow = 0 var returnFlow = 0 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/EvaluationRunner.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/EvaluationRunner.scala new file mode 100644 index 0000000000..5a93c5e49d --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/EvaluationRunner.scala @@ -0,0 +1,192 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import java.io.File +import java.io.PrintWriter +import scala.language.existentials +import com.typesafe.config.ConfigValueFactory +import org.opalj.bytecode +import org.opalj.ifds.Statistics +import org.opalj.ifds.IFDSAnalysis +import org.opalj.ifds.IFDSAnalysisScheduler + +import org.opalj.util.Milliseconds +import org.opalj.util.PerformanceEvaluation.time +import org.opalj.fpcf.FinalEP +import org.opalj.fpcf.PropertyStore +import org.opalj.fpcf.seq.PKESequentialPropertyStore +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.br.DeclaredMethod +import org.opalj.br.analyses.Project +import org.opalj.br.analyses.SomeProject +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.ai.domain.l0.PrimitiveTACAIDomain +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.properties.cg.Callers + +abstract class EvaluationRunner { + + protected def analysisClass: IFDSAnalysisScheduler[_, _, _] + + protected def printAnalysisResults(analysis: IFDSAnalysis[_, _, _], ps: PropertyStore): Unit = () + + protected def run( + debug: Boolean, + useL2: Boolean, + delay: Boolean, + evalSchedulingStrategies: Boolean, + evaluationFile: Option[File] + ): Unit = { + + if (debug) { + PropertyStore.updateDebug(true) + } + + def evalProject(p: SomeProject): (Milliseconds, Statistics, Option[Object]) = { + if (useL2) { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + case None => Set(classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]]) + case Some(requirements) => + requirements + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[_]] + } + } else { + p.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { + case None => Set(classOf[PrimitiveTACAIDomain]) + case Some(requirements) => requirements + classOf[PrimitiveTACAIDomain] + } + } + + val ps = p.get(PropertyStoreKey) + var analysisTime: Milliseconds = Milliseconds.None + p.get(RTACallGraphKey) + println("Start: "+new java.util.Date) + org.opalj.util.gc() + val analysis = + time { + p.get(FPCFAnalysesManagerKey).runAll(analysisClass)._2 + }(t => analysisTime = t.toMilliseconds).collect { + case (_, a: IFDSAnalysis[_, _, _]) => a + }.head + + printAnalysisResults(analysis, ps) + println(s"The analysis took $analysisTime.") + println( + ps.statistics.iterator + .map(_.toString()) + .toList + .sorted + .mkString("PropertyStore Statistics:\n\t", "\n\t", "\n") + ) + ( + analysisTime, + analysis.statistics, + additionalEvaluationResult(analysis) + ) + } + + val p = Project(bytecode.RTJar) + + if (delay) { + println("Sleeping for three seconds.") + Thread.sleep(3000) + } + + if (evalSchedulingStrategies) { + val results = for { + i <- 1 to EvaluationRunner.NUM_EXECUTIONS_EVAL_SCHEDULING_STRATEGIES + strategy <- PKESequentialPropertyStore.Strategies + } yield { + println(s"Round: $i - $strategy") + val strategyValue = ConfigValueFactory.fromAnyRef(strategy) + val newConfig = + p.config.withValue(PKESequentialPropertyStore.TasksManagerKey, strategyValue) + val evaluationResult = evalProject(Project.recreate(p, newConfig)) + org.opalj.util.gc() + (i, strategy, evaluationResult._1, evaluationResult._2) + } + println(results.mkString("AllResults:\n\t", "\n\t", "\n")) + if (evaluationFile.nonEmpty) { + val pw = new PrintWriter(evaluationFile.get) + PKESequentialPropertyStore.Strategies.foreach { strategy => + val strategyResults = results.filter(_._2 == strategy) + val averageTime = strategyResults.map(_._3.timeSpan).sum / strategyResults.size + val (normalFlow, callToStart, exitToReturn, callToReturn) = + computeAverageStatistics(strategyResults.map(_._4)) + pw.println(s"Strategy $strategy:") + pw.println(s"Average time: ${averageTime}ms") + pw.println(s"Average calls of normalFlow: $normalFlow") + pw.println(s"Average calls of callToStart: $callToStart") + pw.println(s"Average calls of exitToReturn: $exitToReturn") + pw.println(s"Average calls of callToReturn: $callToReturn") + pw.println() + } + pw.close() + } + } else { + var times = Seq.empty[Milliseconds] + var statistics = Seq.empty[Statistics] + var additionalEvaluationResults = Seq.empty[Object] + for { + _ <- 1 to EvaluationRunner.NUM_EXECUTIONS + } { + val evaluationResult = evalProject(Project.recreate(p)) + val additionalEvaluationResult = evaluationResult._3 + times :+= evaluationResult._1 + statistics :+= evaluationResult._2 + if (additionalEvaluationResult.isDefined) + additionalEvaluationResults :+= additionalEvaluationResult.get + } + if (evaluationFile.nonEmpty) { + val (normalFlow, callFlow, returnFlow, callToReturnFlow) = computeAverageStatistics( + statistics + ) + val time = times.map(_.timeSpan).sum / times.size + val pw = new PrintWriter(evaluationFile.get) + pw.println(s"Average time: ${time}ms") + pw.println(s"Average calls of normalFlow: $normalFlow") + pw.println(s"Average calls of callFlow: $callFlow") + pw.println(s"Average calls of returnFlow: $returnFlow") + pw.println(s"Average calls of callToReturnFlow: $callToReturnFlow") + if (additionalEvaluationResults.nonEmpty) + writeAdditionalEvaluationResultsToFile(pw, additionalEvaluationResults) + pw.close() + } + } + } + + protected def additionalEvaluationResult(analysis: IFDSAnalysis[_, _, _]): Option[Object] = None + + protected def writeAdditionalEvaluationResultsToFile( + writer: PrintWriter, + additionalEvaluationResults: Seq[Object] + ): Unit = {} + + protected def canBeCalledFromOutside( + method: DeclaredMethod, + propertyStore: PropertyStore + ): Boolean = + propertyStore(method, Callers.key) match { + // This is the case, if the method may be called from outside the library. + case FinalEP(_, p: Callers) => p.hasCallersWithUnknownContext + case _ => + throw new IllegalStateException( + "call graph mut be computed before the analysis starts" + ) + } + + private def computeAverageStatistics(statistics: Seq[Statistics]): (Int, Int, Int, Int) = { + val length = statistics.length + val normalFlow = statistics.map(_.normalFlow).sum / length + val callFlow = statistics.map(_.callFlow).sum / length + val returnFlow = statistics.map(_.returnFlow).sum / length + val callToReturnFlow = statistics.map(_.callToReturnFlow).sum / length + (normalFlow, callFlow, returnFlow, callToReturnFlow) + } +} + +object EvaluationRunner { + var NUM_EXECUTIONS = 10 + var NUM_EXECUTIONS_EVAL_SCHEDULING_STRATEGIES = 2 +} \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala new file mode 100644 index 0000000000..f59c9783da --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -0,0 +1,108 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br.analyses.SomeProject +import org.opalj.fpcf.{PropertyBounds, PropertyKey, PropertyStore} +import org.opalj.ifds.old.{NumberOfSubsumptions, Subsuming} +import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} + +import org.opalj.tac.fpcf.properties.TACAI +import org.opalj.tac.fpcf.properties.cg.Callers +import java.io.{File, PrintWriter} + +import org.opalj.ifds.IFDSAnalysis +import org.opalj.ifds.IFDSAnalysisScheduler + +import org.opalj.br.Method +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.analyses.ProjectInformationKeys +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.tac.cg.TypeProviderKey + +/** + * A variable type analysis implemented as an IFDS analysis. + * In contrast to an ordinary variable type analysis, which also determines types of fields, + * this analysis only determines the types of local variables. + * The subsuming taint can be mixed in to enable subsuming. + * + * @param project The analyzed project. + * @author Mario Trageser + */ +class IFDSBasedVariableTypeAnalysis(project: SomeProject) + extends IFDSAnalysis()(project, new VariableTypeProblem(project), VTAResult) + +object IFDSBasedVariableTypeAnalysisScheduler extends IFDSAnalysisScheduler[VTAFact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new IFDSBasedVariableTypeAnalysis(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, VTAFact] = VTAResult + override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) + override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey, TypeProviderKey, PropertyStoreKey) +} + +/** + * The IFDSProperty for this analysis. + */ +case class VTAResult(flows: Map[JavaStatement, Set[VTAFact]], debugData: Map[JavaStatement, Set[VTAFact]] = Map.empty) extends IFDSProperty[JavaStatement, VTAFact] { + + override type Self = VTAResult + override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result) + override def create(result: Map[JavaStatement, Set[VTAFact]], debugData: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result, debugData) + + override def key: PropertyKey[VTAResult] = VTAResult.key +} + +object VTAResult extends IFDSPropertyMetaInformation[JavaStatement, VTAFact] { + + override type Self = VTAResult + override def create(result: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result) + override def create(result: Map[JavaStatement, Set[VTAFact]], debugData: Map[JavaStatement, Set[VTAFact]]): IFDSProperty[JavaStatement, VTAFact] = new VTAResult(result, debugData) + + val key: PropertyKey[VTAResult] = PropertyKey.create("VTAnew", new VTAResult(Map.empty)) +} + +class IFDSBasedVariableTypeAnalysisRunner extends EvaluationRunner { + + override def analysisClass: IFDSBasedVariableTypeAnalysisScheduler.type = IFDSBasedVariableTypeAnalysisScheduler + + override protected def additionalEvaluationResult( + analysis: IFDSAnalysis[_, _, _] + ): Option[Object] = + analysis match { + case subsuming: Subsuming[_, _] => Some(subsuming.numberOfSubsumptions) + case _ => None + } + + override protected def writeAdditionalEvaluationResultsToFile( + writer: PrintWriter, + additionalEvaluationResults: Seq[Object] + ): Unit = { + val numberOfSubsumptions = additionalEvaluationResults.map(_.asInstanceOf[NumberOfSubsumptions]) + val length = additionalEvaluationResults.length + val tries = numberOfSubsumptions.map(_.triesToSubsume).sum / length + val successes = numberOfSubsumptions.map(_.successfulSubsumes).sum / length + writer.println(s"Average tries to subsume: $tries") + writer.println(s"Average successful subsumes: $successes") + } +} + +object IFDSBasedVariableTypeAnalysisRunner { + def main(args: Array[String]): Unit = { + if (args.contains("--help")) { + println("Potential parameters:") + println(" -seq (to use the SequentialPropertyStore)") + println(" -l2 (to use the l2 domain instead of the default l1 domain)") + println(" -delay (for a three seconds delay before the taint flow analysis is started)") + println(" -debug (for debugging mode in the property store)") + println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") + println(" -f (Stores the average runtime to this file)") + } else { + val fileIndex = args.indexOf("-f") + new IFDSBasedVariableTypeAnalysisRunner().run( + args.contains("-debug"), + args.contains("-l2"), + args.contains("-delay"), + args.contains("-evalSchedulingStrategies"), + if (fileIndex >= 0) Some(new File(args(fileIndex + 1))) else None + ) + } + } +} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index ead50657b3..64bb043232 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -47,6 +47,9 @@ object JavaStatement { abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) extends IFDSProblem[Fact, Method, JavaStatement](new ForwardICFG()(project)) { + + override def needsPredecessor(statement: JavaStatement): Boolean = false + /** * Gets the call object for a statement that contains a call. * diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala new file mode 100644 index 0000000000..329b0c19aa --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala @@ -0,0 +1,356 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds + +import org.opalj.br._ +import org.opalj.br.analyses.SomeProject +import org.opalj.collection.immutable.EmptyIntTrieSet +import org.opalj.ifds.{AbstractIFDSFact, SubsumableFact, SubsumableNullFact} + +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem => NewJavaIFDSProblem} +import org.opalj.tac._ +import org.opalj.value.ValueInformation +import scala.annotation.tailrec + +import org.opalj.ifds.Dependees.Getter + +import org.opalj.fpcf.FinalEP +import org.opalj.br.analyses.DeclaredMethodsKey +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.tac.fpcf.properties.cg.Callers + +trait VTAFact extends SubsumableFact +case object VTANullFact extends VTAFact with SubsumableNullFact + +/** + * A possible run time type of a variable. + * + * @param definedBy The variable's definition site. + * @param t The variable's type. + * @param upperBound True, if the variable's type could also be every subtype of `t`. + */ +case class VariableType(definedBy: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { + + /** + * If this VariableType is an upper bound, it subsumes every subtype. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { + if (upperBound) other match { + case VariableType(definedByOther, tOther, _) if definedBy == definedByOther && t.isObjectType && tOther.isObjectType => + project.classHierarchy.isSubtypeOf(tOther.asObjectType, t.asObjectType) + case _ => false + } + else false + } +} + +/** + * A possible run time type of the receiver of a call. + * + * @param line The line of the call. + * @param t The callee's type. + * @param upperBound True, if the callee's type could also be every subtype of `t`. + */ +case class CalleeType(line: Int, t: ReferenceType, upperBound: Boolean) extends VTAFact { + + /** + * If this CalleeType is an upper bound, it subsumes every subtype. + */ + override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = { + if (upperBound) other match { + case CalleeType(lineOther, tOther, _) if line == lineOther && t.isObjectType && tOther.isObjectType => + tOther.asObjectType.isSubtypeOf(t.asObjectType)(project.classHierarchy) + case _ => false + } + else false + } +} + +class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact](project) { + val propertyStore = project.get(PropertyStoreKey) + val declaredMethods = project.get(DeclaredMethodsKey) + + override def nullFact: VTAFact = VTANullFact + + /** + * The analysis starts with all public methods in java.lang or org.opalj. + */ + override def entryPoints: Seq[(Method, VTAFact)] = { + project.allProjectClassFiles + .filter(classInsideAnalysisContext) + .flatMap(classFile => classFile.methods) + .filter(isEntryPoint) + .flatMap(entryPointsForMethod) + } + + /** + * If a new object is instantiated and assigned to a variable or array, a new ValueType will be + * created for the assignment's target. + * If there is an assignment of a variable or array element, a new VariableType will be + * created for the assignment's target with the source's type. + * If there is a field read, a new VariableType will be created with the field's declared type. + */ + override def normalFlow( + statement: JavaStatement, + in: VTAFact, + predecessor: Option[JavaStatement] + ): Set[VTAFact] = { + val inSet = Set(in) + val stmt = statement.stmt + stmt.astID match { + case Assignment.ASTID => + // Add facts for the assigned variable. + inSet ++ newFacts(statement.method, statement.stmt.asAssignment.expr, statement.index, in) + case ArrayStore.ASTID => + /* +* Add facts for the array store, like it was a variable assignment. +* By doing so, we only want to get the variable's type. +* Then, we change the definedBy-index to the one of the array and wrap the variable's +* type with an array type. +* Note, that an array type may have at most 255 dimensions. +*/ + val flow = scala.collection.mutable.Set.empty[VTAFact] + flow ++= inSet + newFacts(statement.method, stmt.asArrayStore.value, statement.index, in).foreach { + case VariableType(_, t, upperBound) if !(t.isArrayType && t.asArrayType.dimensions <= 254) => + stmt.asArrayStore.arrayRef.asVar.definedBy + .foreach(flow += VariableType(_, ArrayType(t), upperBound)) + case _ => // Nothing to do + } + flow.toSet + // If the statement is neither an assignment, nor an array store, we just propagate our facts. + case _ => inSet + } + } + + /** + * For each variable, which can be passed as an argument to the call, a new VariableType is + * created for the callee context. + */ + override def callFlow( + call: JavaStatement, + callee: Method, + in: VTAFact + ): Set[VTAFact] = { + val inSet = Set(in) + val callObject = asCall(call.stmt) + val allParams = callObject.allParams + // Iterate over all input facts and over all parameters of the call. + val flow = scala.collection.mutable.Set.empty[VTAFact] + inSet.foreach { + case VariableType(definedBy, t, upperBound) => + allParams.iterator.zipWithIndex.foreach { + /* +* We are only interested in a pair of a variable type and a parameter, if the +* variable and the parameter can refer to the same object. +*/ + case (parameter, parameterIndex) if parameter.asVar.definedBy.contains(definedBy) => + // If this is the case, create a new fact for the method's formal parameter. + flow += VariableType( + NewJavaIFDSProblem + .switchParamAndVariableIndex(parameterIndex, callee.isStatic), + t, + upperBound + ) + case _ => // Nothing to do + } + case _ => // Nothing to do + } + flow.toSet + } + + /** + * If the call is an instance call, new CalleeTypes will be created for the call, one for each + * VariableType, which could be the call's target. + */ + override def callToReturnFlow( + call: JavaStatement, + in: VTAFact, + successor: JavaStatement + ): Set[VTAFact] = { + val inSet = Set(in) + // Check, to which variables the callee may refer + val calleeDefinitionSites = asCall(call.stmt).receiverOption + .map(callee => callee.asVar.definedBy) + .getOrElse(EmptyIntTrieSet) + val calleeTypeFacts = inSet.collect { + // If we know the variable's type, we also know on which type the call is performed. + case VariableType(index, t, upperBound) if calleeDefinitionSites.contains(index) => + CalleeType(call.index, t, upperBound) + } + if (inSet.size >= calleeTypeFacts.size) inSet ++ calleeTypeFacts + else calleeTypeFacts ++ inSet + } + + /** + * If the call returns a value which is assigned to a variable, a new VariableType will be + * created in the caller context with the returned variable's type. + */ + override def returnFlow( + exit: JavaStatement, + in: VTAFact, + call: JavaStatement, + callFact: VTAFact, + successor: JavaStatement + ): Set[VTAFact] = + // We only create a new fact, if the call returns a value, which is assigned to a variable. + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val inSet = Set(in) + val returnValue = exit.stmt.asReturnValue.expr.asVar + inSet.collect { + // If we know the type of the return value, we create a fact for the assigned variable. + case VariableType(definedBy, t, upperBound) if returnValue.definedBy.contains(definedBy) => + VariableType(call.index, t, upperBound) + } + } else Set.empty + + /** + * Only methods in java.lang and org.opalj are inside the analysis context. + * + * @param callee The callee. + * @return True, if the callee is inside the analysis context. + */ + override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = + if (classInsideAnalysisContext(callee.classFile) && + super.outsideAnalysisContext(callee).isEmpty) + None + else { + Some(((call: JavaStatement, successor: JavaStatement, in: VTAFact, getter: Getter) => { + val returnType = callee.descriptor.returnType + if (call.stmt.astID == Assignment.ASTID && returnType.isReferenceType) { + Set(VariableType(call.index, returnType.asReferenceType, upperBound = true)) + } else Set.empty[VTAFact] + }): OutsideAnalysisContextHandler) + } + + /** + * When `normalFlow` reaches an assignment or array store, this method computes the new facts + * created by the statement. + * + * @param expression The source expression of the assignment or array store. + * @param statementIndex The statement's index. + * @param in The facts, which hold before the statement. + * @return The new facts created by the statement. + */ + private def newFacts( + method: Method, + expression: Expr[DUVar[ValueInformation]], + statementIndex: Int, + in: VTAFact + ): Iterator[VariableType] = { + val inSet = Set(in) + expression.astID match { + case New.ASTID => + inSet.iterator.collect { + // When a constructor is called, we always know the exact type. + case VTANullFact => + VariableType(statementIndex, expression.asNew.tpe, upperBound = false) + } + case Var.ASTID => + inSet.iterator.collect { + // When we know the source type, we also know the type of the assigned variable. + case VariableType(index, t, upperBound) if expression.asVar.definedBy.contains(index) => + VariableType(statementIndex, t, upperBound) + } + case ArrayLoad.ASTID => + inSet.iterator.collect { + // When we know the array's type, we also know the type of the loaded element. + case VariableType(index, t, upperBound) if isArrayOfObjectType(t) && + expression.asArrayLoad.arrayRef.asVar.definedBy.contains(index) => + VariableType(statementIndex, t.asArrayType.elementType.asReferenceType, upperBound) + } + case GetField.ASTID | GetStatic.ASTID => + val t = expression.asFieldRead.declaredFieldType + /* + * We do not track field types. So we must assume, that it contains any subtype of its + * compile time type. + */ + if (t.isReferenceType) + Iterator(VariableType(statementIndex, t.asReferenceType, upperBound = true)) + else Iterator.empty + case _ => Iterator.empty + } + } + + /** + * Checks, if some type is an array type containing an object type. + * + * @param t The type to be checked. + * @param includeObjectType If true, this method also returns true if `t` is an object type + * itself. + * + * @return True, if `t` is an array type of an object type. + */ + @tailrec private def isArrayOfObjectType( + t: FieldType, + includeObjectType: Boolean = false + ): Boolean = { + if (t.isArrayType) isArrayOfObjectType(t.asArrayType.elementType, includeObjectType = true) + else if (t.isObjectType && includeObjectType) true + else false + } + + /** + * Checks, if a class is inside the analysis context. + * By default, that are the packages java.lang and org.opalj. + * + * @param classFile The class, which is checked. + * @return True, if the class is inside the analysis context. + */ + private def classInsideAnalysisContext(classFile: ClassFile): Boolean = { + val fqn = classFile.fqn + fqn.startsWith("java/lang") || fqn.startsWith("org/opalj/fpcf/fixtures/vta") + } + + /** + * Checks, if a method is an entry point of this analysis. + * + * @param method The method to be checked. + * @return True, if this method is an entry point of the analysis. + */ + private def isEntryPoint(method: Method): Boolean = { + method.body.isDefined && canBeCalledFromOutside(method) + } + + /** + * Checks, if some `method` can be called from outside the library. + * The call graph must be computed, before this method may be invoked. + * + * @param method The method, which may be callable from outside. + * @return True, if `method` can be called from outside the library. + */ + private def canBeCalledFromOutside(method: Method): Boolean = { + val FinalEP(_, callers) = propertyStore(declaredMethods(method), Callers.key) + callers.hasCallersWithUnknownContext + } + + /** + * For an entry point method, this method computes all pairs (`method`, inputFact) where + * inputFact is a VariableType for one of the method's parameter with its compile time type as + * an upper bound. + * + * @param method The entry point method. + * + * @return All pairs (`method`, inputFact) where inputFact is a VariableType for one of the + * method's parameter with its compile time type as an upper bound. + */ + private def entryPointsForMethod(method: Method): Seq[(Method, VTAFact)] = { + // Iterate over all parameters, which have a reference type. + (method.descriptor.parameterTypes.zipWithIndex.collect { + case (t, index) if t.isReferenceType => + /* +* Create a fact for the parameter, which says, that the parameter may have any +* subtype of its compile time type. +*/ + VariableType( + NewJavaIFDSProblem.switchParamAndVariableIndex(index, method.isStatic), + t.asReferenceType, + upperBound = true + ) + /* +* In IFDS problems, we must always also analyze the null fact, because it creates the facts, +* which hold independently of other source facts. +* Map the input facts, in which we are interested, to a pair of the method and the fact. +*/ + } :+ VTANullFact).map(fact => (method, fact)) + } +} \ No newline at end of file From b84492011e6893bbe9f5432e325dd4bb18c18dc1 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Sat, 2 Jul 2022 15:45:06 +0200 Subject: [PATCH 55/67] Implement Subsuming and add to evaluation --- .../tac/fpcf/analyses/NewIFDSEvaluation.scala | 1 + .../org/opalj/ifds/AbstractIFDSFact.scala | 27 +++----------- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 35 +++++++++++++------ .../scala/org/opalj/ifds/IFDSProblem.scala | 5 +++ .../scala/org/opalj/ifds/old/Subsuming.scala | 5 +-- .../ifds/IFDSBasedVariableTypeAnalysis.scala | 28 +++++++-------- .../analyses/ifds/VariableTypeProblem.scala | 9 ++--- .../ifds/old/VariableTypeProblem.scala | 10 +++--- 8 files changed, 62 insertions(+), 58 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/NewIFDSEvaluation.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/NewIFDSEvaluation.scala index 89c145e258..ef5b103cd6 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/NewIFDSEvaluation.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/NewIFDSEvaluation.scala @@ -26,6 +26,7 @@ object NewIFDSEvaluation { old.IFDSBasedVariableTypeAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"OldIFDSBasedVariableTypeAnalysis.txt")) HerosVariableTypeAnalysisRunner.main(Array("-f", dir+"HerosVariableTypeAnalysis.txt")) IFDSBasedVariableTypeAnalysisRunner.main(Array("-seq", "-l2", "-f", dir+"IFDSBasedVariableTypeAnalysis.txt")) + IFDSBasedVariableTypeAnalysisRunner.main(Array("-seq", "-l2", "-subsumeFacts", "-f", dir+"IFDSBasedVariableTypeAnalysisSubsuming.txt")) } } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala index e200436c3f..ae70c466de 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/AbstractIFDSFact.scala @@ -4,20 +4,9 @@ package org.opalj.ifds import org.opalj.br.analyses.SomeProject /** - * The supertype of all IFDS facts. + * The supertype of all IFDS facts, may implement "subsumes" to enable subsuming. */ -trait AbstractIFDSFact - -/** - * The super type of all null facts. - */ -trait AbstractIFDSNullFact extends AbstractIFDSFact - -/** - * The supertype of all IFDS facts, which can subsume another fact. - */ -trait SubsumableFact extends AbstractIFDSFact { - +trait AbstractIFDSFact { /** * Checks, if this fact subsumes an `other` fact. * @@ -25,16 +14,10 @@ trait SubsumableFact extends AbstractIFDSFact { * @param project The analyzed project. * @return True, if this fact subsumes the `other`fact */ - def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean + def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = false } /** - * The null fact for subsumable facts. + * The super type of all null facts. */ -trait SubsumableNullFact extends SubsumableFact with AbstractIFDSNullFact { - - /** - * The null fact cannot subsume another fact. - */ - override def subsumes(other: AbstractIFDSFact, project: SomeProject): Boolean = false -} \ No newline at end of file +trait AbstractIFDSNullFact extends AbstractIFDSFact diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 718fb9ad9b..d8ee7b4351 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -44,7 +44,7 @@ object Dependees { * that is the fact reaches the statement as an input. * Source fact is the fact within the analysis entity. */ -case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C]() { +case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C <: AnyRef](subsumes: (Set[IFDSFact], IFDSFact) => Boolean) { var edges = Map.empty[S, Either[Set[IFDSFact], Map[S, Set[IFDSFact]]]] /** @@ -56,7 +56,6 @@ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C] * @return whether the edge was new */ def add(statement: S, fact: IFDSFact, predecessor: Option[S] = None): Boolean = { - // TODO: subsuming edges.get(statement) match { case None => predecessor match { @@ -91,7 +90,9 @@ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C] } } - private def isNew(existingFacts: Set[IFDSFact], newFact: IFDSFact): Boolean = !existingFacts.contains(newFact) + private def isNew(existingFacts: Set[IFDSFact], newFact: IFDSFact): Boolean = { + !existingFacts.contains(newFact) && !subsumes(existingFacts, newFact) + } /** * @param statement @@ -112,15 +113,17 @@ case class PathEdges[IFDSFact <: AbstractIFDSFact, S <: Statement[_ <: C, _], C] * The state of the analysis. For each method and source fact, there is a separate state. * * @param source The callable and input fact for which the callable is analyzed. - * @param endSummaries Output facts of the analyzed callable as pairs of exit statement and fact + * @param subsumes The subsuming function, return whether a new fact is subsume by the existing ones */ protected class IFDSState[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _], Work]( - val source: (C, IFDSFact), - val dependees: Dependees[Work] = Dependees[Work](), - val pathEdges: PathEdges[IFDSFact, S, C] = PathEdges[IFDSFact, S, C](), - var endSummaries: Set[(S, IFDSFact)] = Set.empty[(S, IFDSFact)], - var selfDependees: Set[Work] = Set.empty[Work] -) + val source: (C, IFDSFact), + subsumes: (Set[IFDSFact], IFDSFact) => Boolean +) { + val dependees: Dependees[Work] = Dependees[Work]() + val pathEdges: PathEdges[IFDSFact, S, C] = PathEdges[IFDSFact, S, C](subsumes) + var endSummaries: Set[(S, IFDSFact)] = Set.empty[(S, IFDSFact)] + var selfDependees: Set[Work] = Set.empty[Work] +} /** * Contains int variables, which count, how many times some method was called. @@ -130,6 +133,8 @@ class Statistics { var callFlow = 0 var returnFlow = 0 var callToReturnFlow = 0 + var subsumeTries = 0 + var subsumptions = 0 } protected class ProjectFPCFAnalysis(val project: SomeProject) extends FPCFAnalysis @@ -165,7 +170,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < val (function, sourceFact) = entity // Start processing at the start of the icfg with the given source fact - implicit val state: State = new IFDSState[IFDSFact, C, S, Work](entity) + implicit val state: State = new IFDSState[IFDSFact, C, S, Work](entity, subsumes) implicit val queue: Worklist = mutable.Queue .empty[Work] icfg.startStatements(function).foreach { start => @@ -411,6 +416,14 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < out + ifdsProblem.nullFact else out } + + private def subsumes(existingFacts: Set[IFDSFact], newFact: IFDSFact)(implicit project: SomeProject): Boolean = { + statistics.subsumeTries += 1 + if (ifdsProblem.subsumeFacts && existingFacts.exists(_.subsumes(newFact, project))) { + statistics.subsumptions += 1 + true + } else false + } } abstract class IFDSAnalysisScheduler[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ <: C, _]] diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala index d7179fc451..1394d4ebf1 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSProblem.scala @@ -24,6 +24,11 @@ abstract class IFDSProblem[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: State */ def automaticallyPropagateNullFactInFlowFunctions: Boolean = true + /** + * @return Whether to try to subsume new facts under existing facts and save graph edges + */ + def subsumeFacts: Boolean = false + /** * The entry points of this analysis. */ diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala index 93c4cdf04c..06cd2476a4 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/old/Subsuming.scala @@ -1,15 +1,16 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.ifds.old +import org.opalj.ifds.AbstractIFDSFact + import org.opalj.br.analyses.SomeProject -import org.opalj.ifds.SubsumableFact /** * An IFDS analysis, which implements subsuming. * * @author Mario Trageser */ -trait Subsuming[S, IFDSFact <: SubsumableFact] extends Subsumable[S, IFDSFact] { +trait Subsuming[S, IFDSFact <: AbstractIFDSFact] extends Subsumable[S, IFDSFact] { val numberOfSubsumptions = new NumberOfSubsumptions diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala index f59c9783da..3189b06fe3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/IFDSBasedVariableTypeAnalysis.scala @@ -3,7 +3,6 @@ package org.opalj.tac.fpcf.analyses.ifds import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.{PropertyBounds, PropertyKey, PropertyStore} -import org.opalj.ifds.old.{NumberOfSubsumptions, Subsuming} import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.properties.TACAI @@ -12,6 +11,7 @@ import java.io.{File, PrintWriter} import org.opalj.ifds.IFDSAnalysis import org.opalj.ifds.IFDSAnalysisScheduler +import org.opalj.ifds.Statistics import org.opalj.br.Method import org.opalj.br.analyses.DeclaredMethodsKey @@ -28,11 +28,11 @@ import org.opalj.tac.cg.TypeProviderKey * @param project The analyzed project. * @author Mario Trageser */ -class IFDSBasedVariableTypeAnalysis(project: SomeProject) - extends IFDSAnalysis()(project, new VariableTypeProblem(project), VTAResult) +class IFDSBasedVariableTypeAnalysis(project: SomeProject, subsumeFacts: Boolean = false) + extends IFDSAnalysis()(project, new VariableTypeProblem(project, subsumeFacts), VTAResult) -object IFDSBasedVariableTypeAnalysisScheduler extends IFDSAnalysisScheduler[VTAFact, Method, JavaStatement] { - override def init(p: SomeProject, ps: PropertyStore) = new IFDSBasedVariableTypeAnalysis(p) +class IFDSBasedVariableTypeAnalysisScheduler(subsumeFacts: Boolean = false) extends IFDSAnalysisScheduler[VTAFact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new IFDSBasedVariableTypeAnalysis(p, subsumeFacts) override def property: IFDSPropertyMetaInformation[JavaStatement, VTAFact] = VTAResult override val uses: Set[PropertyBounds] = Set(PropertyBounds.finalP(TACAI), PropertyBounds.finalP(Callers)) override def requiredProjectInformation: ProjectInformationKeys = Seq(DeclaredMethodsKey, TypeProviderKey, PropertyStoreKey) @@ -59,26 +59,23 @@ object VTAResult extends IFDSPropertyMetaInformation[JavaStatement, VTAFact] { val key: PropertyKey[VTAResult] = PropertyKey.create("VTAnew", new VTAResult(Map.empty)) } -class IFDSBasedVariableTypeAnalysisRunner extends EvaluationRunner { +class IFDSBasedVariableTypeAnalysisRunner(subsumeFacts: Boolean = false) extends EvaluationRunner { - override def analysisClass: IFDSBasedVariableTypeAnalysisScheduler.type = IFDSBasedVariableTypeAnalysisScheduler + override def analysisClass: IFDSBasedVariableTypeAnalysisScheduler = new IFDSBasedVariableTypeAnalysisScheduler(subsumeFacts) override protected def additionalEvaluationResult( analysis: IFDSAnalysis[_, _, _] ): Option[Object] = - analysis match { - case subsuming: Subsuming[_, _] => Some(subsuming.numberOfSubsumptions) - case _ => None - } + if (analysis.ifdsProblem.subsumeFacts) Some(analysis.statistics) else None override protected def writeAdditionalEvaluationResultsToFile( writer: PrintWriter, additionalEvaluationResults: Seq[Object] ): Unit = { - val numberOfSubsumptions = additionalEvaluationResults.map(_.asInstanceOf[NumberOfSubsumptions]) + val numberOfSubsumptions = additionalEvaluationResults.map(_.asInstanceOf[Statistics]) val length = additionalEvaluationResults.length - val tries = numberOfSubsumptions.map(_.triesToSubsume).sum / length - val successes = numberOfSubsumptions.map(_.successfulSubsumes).sum / length + val tries = numberOfSubsumptions.map(_.subsumeTries).sum / length + val successes = numberOfSubsumptions.map(_.subsumptions).sum / length writer.println(s"Average tries to subsume: $tries") writer.println(s"Average successful subsumes: $successes") } @@ -93,10 +90,11 @@ object IFDSBasedVariableTypeAnalysisRunner { println(" -delay (for a three seconds delay before the taint flow analysis is started)") println(" -debug (for debugging mode in the property store)") println(" -evalSchedulingStrategies (evaluates all available scheduling strategies)") + println(" -subsumeFacts (enables subsuming)") println(" -f (Stores the average runtime to this file)") } else { val fileIndex = args.indexOf("-f") - new IFDSBasedVariableTypeAnalysisRunner().run( + new IFDSBasedVariableTypeAnalysisRunner(args.contains("-subsumeFacts")).run( args.contains("-debug"), args.contains("-l2"), args.contains("-delay"), diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala index 329b0c19aa..478054d25d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala @@ -4,13 +4,14 @@ package org.opalj.tac.fpcf.analyses.ifds import org.opalj.br._ import org.opalj.br.analyses.SomeProject import org.opalj.collection.immutable.EmptyIntTrieSet -import org.opalj.ifds.{AbstractIFDSFact, SubsumableFact, SubsumableNullFact} +import org.opalj.ifds.AbstractIFDSFact import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem => NewJavaIFDSProblem} import org.opalj.tac._ import org.opalj.value.ValueInformation import scala.annotation.tailrec +import org.opalj.ifds.AbstractIFDSNullFact import org.opalj.ifds.Dependees.Getter import org.opalj.fpcf.FinalEP @@ -18,8 +19,8 @@ import org.opalj.br.analyses.DeclaredMethodsKey import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.tac.fpcf.properties.cg.Callers -trait VTAFact extends SubsumableFact -case object VTANullFact extends VTAFact with SubsumableNullFact +trait VTAFact extends AbstractIFDSFact +case object VTANullFact extends VTAFact with AbstractIFDSNullFact /** * A possible run time type of a variable. @@ -65,7 +66,7 @@ case class CalleeType(line: Int, t: ReferenceType, upperBound: Boolean) extends } } -class VariableTypeProblem(project: SomeProject) extends JavaIFDSProblem[VTAFact](project) { +class VariableTypeProblem(project: SomeProject, override val subsumeFacts: Boolean = false) extends JavaIFDSProblem[VTAFact](project) { val propertyStore = project.get(PropertyStoreKey) val declaredMethods = project.get(DeclaredMethodsKey) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala index 60a13a91e7..a0332ee117 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/VariableTypeProblem.scala @@ -4,15 +4,17 @@ package org.opalj.tac.fpcf.analyses.ifds.old import org.opalj.br._ import org.opalj.br.analyses.SomeProject import org.opalj.collection.immutable.EmptyIntTrieSet -import org.opalj.ifds.{AbstractIFDSFact, SubsumableFact, SubsumableNullFact} +import org.opalj.ifds.AbstractIFDSFact + import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem => NewJavaIFDSProblem} import org.opalj.tac._ import org.opalj.value.ValueInformation - import scala.annotation.tailrec -trait VTAFact extends SubsumableFact -case object VTANullFact extends VTAFact with SubsumableNullFact +import org.opalj.ifds.AbstractIFDSNullFact + +trait VTAFact extends AbstractIFDSFact +case object VTANullFact extends VTAFact with AbstractIFDSNullFact /** * A possible run time type of a variable. From d43fb1169cdf7ccc701a0f73b184cb06fbdac643 Mon Sep 17 00:00:00 2001 From: Marc Clement Date: Sat, 2 Jul 2022 21:47:51 +0200 Subject: [PATCH 56/67] x-lang working in simple case --- .../HerosForwardClassForNameAnalysis.scala | 15 +- .../llvm/cross_language/taint/TaintTest.class | Bin 4038 -> 4084 bytes .../llvm/cross_language/taint/TaintTest.java | 6 + ...rossLanguageForwardTaintAnalysisTest.scala | 11 +- .../ifds/taint/JavaForwardTaintAnalysis.scala | 19 +-- .../taint/NativeForwardTaintAnalysis.scala | 8 +- .../taint/NativeForwardTaintProblem.scala | 155 +++++------------- .../tac/fpcf/analyses/ifds/ForwardICFG.scala | 2 +- .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 39 +++-- .../analyses/ifds/VariableTypeProblem.scala | 4 +- .../ifds/taint/ForwardTaintProblem.scala | 21 +-- 11 files changed, 96 insertions(+), 184 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index 0d32fd7568..ccd4256872 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -224,26 +224,13 @@ class HerosForwardClassForNameAnalysis( exit: JavaStatement, succ: JavaStatement ): FlowFunction[TaintFact] = { - - def isRefTypeParam(index: Int): Boolean = - if (index == -1) true - else { - val parameterOffset = if (callee.isStatic) 0 else 1 - callee.descriptor - .parameterType( - JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) - - parameterOffset - ) - .isReferenceType - } - val callStatement = asCall(stmt.stmt) val allParams = callStatement.allParams source: TaintFact => { val paramFacts = source match { - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) => + case Variable(index) if index < 0 && index > -100 && JavaIFDSProblem.isRefTypeParam(callee, index) => val params = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.class b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.class index 0a2f8b80b0535cfeff08f4eb77f38c46c5164a57..32654c849d6ed59b7cd6d1b9a1d841a87e08db8c 100644 GIT binary patch literal 4084 zcmaJ^i+>YU7XF4lGK5PTN@?v&3KVREQgji}ma0^cN(HpF5mDo`8OqRPrY6&}MR$F_ zb=Sx0y6&#;_xrVhMSuPQ`$z51@66mJlQh`=Zti5}`@Vb5x#!$D`QQI9{R&_Vzr?Wy z4KXw-G@;o*;;gl74QDNPe)#z8S$obm&^+q6jz4CgVQ^?B2GhXWY0Gi_X}jRZ5krG~ ztW!`}Z=lt2GftkobirH7&)ETMLJ+MA32ZR1p@Oie>zpU1ad2WN0Buxg$0h^oDR z2mv#q(y4HT*xBN9^|Tn9KArW>$)j$C9(mMVc~sCVq+PEJ>r=Q=U_BMslIx2@b|#AJ zSJ)!B_5kNvzO!tn3ro4O&^Cnu0c?!|d@nSLv!;~H+o5okz=wYyUNX7LmLY|taCbzx zl@Mt!t0-41TqBgOx|EVp!wS1sqcXPZJN`=9)9V!eAgC*%TIcONeXPXwdWF3Lx-tr~ z@_B0|U5ajL)%^-L2z1+OXx*(CQ5Y5c<|uy7%4Wk@7nmYtTgMb`66}`CV1ukIqi<0- zDCnxdMwDyvXDx2Up%@M;+=e4n#WJ;0@a-J4-CJVGY?=(p$nl0xNFDOfjIqv+((2R+@C?{0O{%M7~r2w15 zx|3Teg?pr!8YMs`?qyjCy1$fSg1JxOeyK{cbkjtd)fM2%dtt&d5D)XYpCur~IfV?Q zy!qWg)8GUv*u*@OMB{v~KTzQu-Vv6ga8|5LEatsMYu@r%Qo)YM!>mG1Y-x!+3@rCp zWS3Jddy^4~hZOQ6(H4==duW0i)1_rfwNuSrRyZdFSQZIuNdGq6qzN=XUt356$x*HPgG)mG*_&0|S(f(q-` z6(;0o6`qrn)_`Vrq*J|vh z$&v*+cz-j{xtd*0fHxKXF4+-@*Xg{M(+UC1#6K##7_mTavRLs?g?~v7bXKAlnxHXl zeDS@b@GjmXCTA_!fXCxqd-vZenP4)tUH!ufHJLa=yfQ9(<(mu@v#T ztY>8=o!PvVXO1+Edl_a@>!jn_$Ch%lc7EEL&GJrTj;|U9c<|I}^`RLS;^|4?EMFT_8BbJ-V+J%8h&s$hp99eC0pr<5{avunRFnOWY7J%2GM=!@_j;$QqIR-fdxr^gkjy)XvIBw)Pz;W{r7;l>>;%LRaW7x1e=>Xd| z?ZypsLL$9Mmc5h9CQ}zNHJVKIT*OSpgf=u1w2LMTqlF8#<5~)^)zdO#TBciMPDW$` zp;L9V_E773YVD;{`>4WxP;2egT5Y2B0JRdGm$BzYYK&kVo!x)~WV2V)8b`T1V)16t zc(80wqVqtqr?-gNv1F>R%BXHq!HAi-g+?8u;5egnE6qAYN%ojpc)vETU4&1Ia1m!l zlRZT&q-w);glZE^pxe2BN6BaFsa5vUJW1IOQE^2jDLO?gR_nA72OOi$aVF#ma@c}u zjYE7UPy0lpP~~DOaB`{2!48%YFh!|pI(Ro7oM95BD8?4Gp5z~EBzx&&J;{41d5V(v zQSyFDK0q<{vs!XD<0YOZ#LDv`$@D7XkxHyWA1$iPq7`#Qv(K%^ezeSfyq0~M?0K@! zkjDmCv#-%>ua~u-5_S>K)KV8n&GL6sjwCk3^{CH>)E8^1i=;k8YMvza#+q8?@n6a| zzf#LyB72!OpCgamvSweWZ4Q$Ebz!Gy^&3@N8Aj|p`42PZkC4g!S@ZX4ey8~SR>%)Z zx`?;yh&)P>$IyYtN#}1zEz-+bHm#9+^%dvnDf<+^@#i5wf=Iw(1I;c zBc3OKKRGpA&hHZZRITz$h)esdp*@`CuOX4F1-?okn`#Xl(jLn$cVEK40eW2tc4*2Q z^zKd4*k@~6pQbf+CpKnkS9qIY`Jx=tt=g34w)ZdM!&>PC3u^-_@k<)J24CT8&L$b( SaQsADz-0|w;0xntbo~bWW}T`4 literal 4038 zcmaJ^340S&6g@+igzzY#l$I_?fue0tii&`?RHcFh%VsN4QRB23%FuMCCevzh-*>@% z-}ilweENO<0DqL9=gz!LCTXyIFK?D}&b#-%d*59C`S;Ji0gRxOz%n$%(5TRaW&_F7 z=7KqxH=XIhBa^4CDbGOjuZ6{|J7`bd+O0U93 zp>)=zl#SY~(6!Mm`tO9+k#C1R+C7_F~7qjK)mRG%0 z;WB}4UJ9+d6@v=f1-~JRpEC3LAl60OnF(#ZLg7lmZoB~2oi9vTA^IwXor10kY(%*x zf0ko6u8!dvg&_=ACGq%T(X(cmyY4(wdF_~=-nKh zIeyjk>Z%j+LkbT|nrlF|sx0Gpl(JRSaF>PrxWW@c)(}k&A#ps#4ApcgonVV4GQxgF z;n^CyX|iO24&L(yI+n7-1b9*5CCQFRyiOL}S*_s1;y72?#fSxZlf{Zx6ke4a=%_?5 zFhOHl`H^~E;SIb=%&a+Mjk?a1>1Ay16t9Y&>3Bt{!}P$?N{r)eeyaRv%bAnl8S$>d zds5gd@~)X1vnLB?VUe{o>gFtF#hC3_hv#P}t-^#kndg(nS$=*P;QkX!)dyI5tIDaV z3xl;=dE!^~>dL+Lh*dm1mDgX?#JC@*{kE*uGEx4jZj%_W@brd3 zn(=i4-{9LAzLTJQZ=g@0!S~vB7Tg(YFgS;TIPEv5JXc(ND|%9CU?6(HbaHvC*f-|7 zGxKvpOC60osUl7+&RKQf(NF~)#87l^w8SX$aoqMSp1nWvY!oMY4Ff9eI96daZx)MI zF@|3ZY+2%8J+ou@jUTxGhjN*~M}B`}YGC3Cgna7tu^Ca|V9!UPogT$YCF!(cT+@D#l{>;MjXBLn@vjn)pa(sx7 zxR#jmH4PLK5B-JMZGT|-*wzvf94k4J9BVk*IXXFdI5u!>;@HB`&#{$b8^`4wJAT2g z);%R$TXE?iTP>gsakf$l?MR}7ckB4C6FulcFI%EMUiC93m$Jp#jt$tsr@I-uVQk`8 zax-oqVhmeoFdIK@_SM?#wX)~ z*=1^to6z7}yg@WFp*_itJ*n=V5^ftwW!6_2)kP|B4F!j2)G!72Ft#I%JvE@3iL8?L=n8^ub-eGT@J&!$zY?4)^;qV1w$iAqXzN|>(JX(0|c zK%F$5J4gbUo@L0rjz3>S{bN7D_lb<-9J#kJTzag@iQT8rscS{u+|0THx~pvJuz70qwEua@U`D z4WQSRUE( diff --git a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java index db1997030d..add60865a7 100644 --- a/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java +++ b/DEVELOPING_OPAL/validate/src/test/resources/llvm/cross_language/taint/TaintTest.java @@ -18,6 +18,12 @@ public class TaintTest { public static void main (String[] args) { TaintTest demo = new TaintTest(); + // force call graph analysis of indirect methods + // otherwise their callees are not analyzed, + // as they are only reachable through native code + // TODO: trigger cga from within other analysis + demo.indirect_sink(demo.indirect_sanitize(demo.indirect_source())); + demo.test_java_flow(); demo.test_java_sanitize_no_flow(); demo.test_java_untainted_no_flow(); diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala index 84e111556c..52bd492d90 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala @@ -2,17 +2,20 @@ package org.opalj.ll import com.typesafe.config.ConfigValueFactory + import org.opalj.br.analyses.Project import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.ifds import org.opalj.ifds.IFDSProperty import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement} -import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, NativeTaintFact, NativeForwardTaintAnalysisScheduler, NativeTaintNullFact} +import org.opalj.ll.fpcf.analyses.ifds.taint.{JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler, NativeTaintFact, NativeTaintNullFact} +import org.opalj.ll.fpcf.analyses.ifds.taint.SimpleJavaForwardTaintAnalysis import org.opalj.ll.llvm.value.Function + import org.opalj.log.GlobalLogContext import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, TaintNullFact} +import org.opalj.tac.fpcf.analyses.ifds.taint.{FlowFact, TaintFact} import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers @@ -33,9 +36,9 @@ class CrossLanguageForwardIFDSTaintAnalysisTests extends AnyFunSpec with Matcher project.get(RTACallGraphKey) val manager = project.get(FPCFAnalysesManagerKey) val (ps, analyses) = manager.runAll(JavaForwardTaintAnalysisScheduler, NativeForwardTaintAnalysisScheduler) - for (method <- project.allMethodsWithBody) { + for ((method, fact) <- analyses.head._2.asInstanceOf[SimpleJavaForwardTaintAnalysis].ifdsProblem.entryPoints) { val flows = - ps((method, TaintNullFact), JavaForwardTaintAnalysisScheduler.property.key) + ps((method, fact), JavaForwardTaintAnalysisScheduler.property.key) println("---METHOD: "+method.toJava+" ---") val flowFacts = flows.ub .asInstanceOf[IFDSProperty[JavaStatement, TaintFact]] diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index f06643c5c5..aae50c1dd9 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -115,7 +115,7 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( in: TaintFact, nativeCallee: Method ): Set[NativeTaintFact] = { - val callObject = asCall(call.stmt) + val callObject = JavaIFDSProblem.asCall(call.stmt) val allParams = callObject.allParams val allParamsWithIndices = allParams.zipWithIndex in match { @@ -172,26 +172,13 @@ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem( nativeCallee: Method, successor: JavaStatement ): Set[TaintFact] = { - /** - * Checks whether the callee's formal parameter is of a reference type. - */ - def isRefTypeParam(index: Int): Boolean = - if (index == -1) true - else { - val parameterOffset = if (nativeCallee.isStatic) 0 else 1 - nativeCallee.descriptor.parameterType( - JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic) - - parameterOffset - ).isReferenceType - } - if (sanitizesReturnValue(nativeCallee)) return Set.empty - val callStatement = asCall(call.stmt) + val callStatement = JavaIFDSProblem.asCall(call.stmt) val allParams = callStatement.allParams var flows: Set[TaintFact] = Set.empty in match { // Taint actual parameter if formal parameter is tainted - case JavaVariable(index) if index < 0 && index > -100 && isRefTypeParam(index) => + case JavaVariable(index) if index < 0 && index > -100 && JavaIFDSProblem.isRefTypeParam(nativeCallee, index) => val param = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, nativeCallee.isStatic) ) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala index 307a2b762a..cddb1c024c 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintAnalysis.scala @@ -4,7 +4,7 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.analyses.SomeProject import org.opalj.fpcf.{PropertyBounds, PropertyKey, PropertyStore} import org.opalj.ifds.IFDSPropertyMetaInformation -import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeFunction, NativeIFDSAnalysis, NativeIFDSAnalysisScheduler} +import org.opalj.ll.fpcf.analyses.ifds.{LLVMStatement, NativeFunction, NativeIFDSAnalysis, NativeIFDSAnalysisScheduler} import org.opalj.ll.fpcf.properties.NativeTaint import org.opalj.ll.llvm.value.Function import org.opalj.tac.fpcf.properties.Taint @@ -19,8 +19,8 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint /** * The sanitize method is a sanitizer. */ - override protected def sanitizesReturnValue(callee: LLVMFunction): Boolean = - callee.function.name == "sanitize" + override protected def sanitizesReturnValue(callee: NativeFunction): Boolean = + callee.name == "sanitize" /** * We do not sanitize parameters. @@ -31,7 +31,7 @@ class SimpleNativeForwardTaintProblem(p: SomeProject) extends NativeForwardTaint * Creates a new variable fact for the callee, if the source was called. */ protected def createTaints(callee: Function, call: LLVMStatement): Set[NativeTaintFact] = - if (callee.name == "source") Set.empty //TODO Set(NativeVariable()) + if (callee.name == "source") Set(NativeVariable(call.instruction)) else Set.empty /** diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index d083f77dd6..c1da0d0207 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -4,11 +4,19 @@ package org.opalj.ll.fpcf.analyses.ifds.taint import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.ll.fpcf.analyses.ifds.{LLVMFunction, LLVMStatement, NativeFunction, NativeIFDSProblem} +import org.opalj.ll.fpcf.analyses.ifds.JNIMethod import org.opalj.ll.llvm.value.{Add, Alloca, BitCast, Call, GetElementPtr, Load, PHI, Ret, Store, Sub} + import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, TaintProblem} - -abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeTaintFact, TaintFact](project) with TaintProblem[LLVMFunction, LLVMStatement, NativeTaintFact] { +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem +import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.analyses.ifds.taint.StaticField +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact +import org.opalj.tac.fpcf.analyses.ifds.taint.Variable +import org.opalj.tac.ReturnValue + +abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeTaintFact, TaintFact](project) with TaintProblem[NativeFunction, LLVMStatement, NativeTaintFact] { override def nullFact: NativeTaintFact = NativeTaintNullFact /** @@ -152,46 +160,20 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD call: LLVMStatement, callee: Method, in: NativeTaintFact - ): Set[TaintFact] = { - /*val callObject = asCall(call.stmt) - val allParams = callObject.allParams - val allParamsWithIndices = allParams.zipWithIndex - in match { - // Taint formal parameter if actual parameter is tainted - case Variable(index) => - allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) => - // TODO: this is passed - Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv - case _ => None // Nothing to do - }.toSet - - // Taint element of formal parameter if element of actual parameter is tainted - case ArrayElement(index, taintedIndex) => - allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) => - Some(NativeVariable(callee.function.argument(paramIndex + 1))) // offset JNIEnv - case _ => None // Nothing to do - }.toSet - - case InstanceField(index, declaredClass, taintedField) => - // Taint field of formal parameter if field of actual parameter is tainted - // Only if the formal parameter is of a type that may have that field! - allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) => - Some(JavaInstanceField(paramIndex + 1, declaredClass, taintedField)) // TODO subtype check - case _ => None // Nothing to do - }.toSet - - case StaticField(classType, fieldName) => Set(JavaStaticField(classType, fieldName)) - - case NullFact => Set(NativeTaintNullFact) - - case _ => Set() // Nothing to do - - }*/ - Set() // TODO - } + ): Set[TaintFact] = + in match { + // Taint formal parameter if actual parameter is tainted + case NativeVariable(value) => call.instruction.asInstanceOf[Call].indexOfArgument(value) match { + case Some(index) => Set(Variable(JavaIFDSProblem.switchParamAndVariableIndex( + index - 2, + callee.isStatic + ))) + case None => Set() + } + // TODO pass other java taints + case NativeTaintNullFact => Set(TaintNullFact) + case _ => Set() // Nothing to do + } /** * Computes the data flow for an exit to return edge. @@ -210,78 +192,27 @@ abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFD callFact: NativeTaintFact, successor: LLVMStatement ): Set[NativeTaintFact] = { - /* - val callee = exit.callable() - /** - * Checks whether the callee's formal parameter is of a reference type. - */ - def isRefTypeParam(index: Int): Boolean = - if (index == -1) true - else { - val parameterOffset = if (callee.isStatic) 0 else 1 - callee.descriptor.parameterType( - JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) - - parameterOffset - ).isReferenceType - } - - if (sanitizesReturnValue(callee)) return Set.empty - val callStatement = asCall(call.stmt) - val allParams = callStatement.allParams - var flows: Set[Fact] = Set.empty - in match { - // Taint actual parameter if formal parameter is tainted - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) => - val param = allParams( - JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) - ) - flows ++= param.asVar.definedBy.iterator.map(Variable) - - // Taint element of actual parameter if element of formal parameter is tainted - case ArrayElement(index, taintedIndex) if index < 0 && index > -100 => - val param = allParams( - JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) - ) - flows ++= param.asVar.definedBy.iterator.map(ArrayElement(_, taintedIndex)) - - case InstanceField(index, declClass, taintedField) if index < 0 && index > -10 => - // Taint field of actual parameter if field of formal parameter is tainted - val param = - allParams(JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic)) - param.asVar.definedBy.foreach { defSite => - flows += InstanceField(defSite, declClass, taintedField) - } - - case sf: StaticField => flows += sf - - // Track the call chain to the sink back - case FlowFact(flow) if !flow.contains(JavaMethod(call.method)) => - flows += FlowFact(JavaMethod(call.method) +: flow) - case _ => - } - - // Propagate taints of the return value - if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { - val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + val callee = exit.callable + if (sanitizesReturnValue(JNIMethod(callee))) return Set.empty + var flows: Set[NativeTaintFact] = Set.empty in match { - case Variable(index) if returnValueDefinedBy.contains(index) => - flows += Variable(call.index) - case ArrayElement(index, taintedIndex) if returnValueDefinedBy.contains(index) => - flows += ArrayElement(call.index, taintedIndex) - case InstanceField(index, declClass, taintedField) if returnValueDefinedBy.contains(index) => - flows += InstanceField(call.index, declClass, taintedField) - case NullFact => - val taints = createTaints(callee, call) - if (taints.nonEmpty) flows ++= taints - case _ => // Nothing to do - } - } - val flowFact = createFlowFact(callee, call, in) - if (flowFact.isDefined) flows += flowFact.get + case StaticField(classType, fieldName) => flows += JavaStaticField(classType, fieldName) - flows + // Track the call chain to the sink back + case FlowFact(flow) if !flow.contains(call.function) => + flows += NativeFlowFact(call.function +: flow) + case _ => + } - */ - Set() // TODO + // Propagate taints of the return value + if (exit.stmt.astID == ReturnValue.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + in match { + case Variable(index) if returnValueDefinedBy.contains(index) => + flows += NativeVariable(call.instruction) + case _ => // Nothing to do + } + } + flows } } \ No newline at end of file diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala index 2f4653cf86..082b4901f8 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/ForwardICFG.scala @@ -75,7 +75,7 @@ class ForwardICFG(implicit project: SomeProject) } private def getCallees(statement: JavaStatement): collection.Set[Method] = { - val pc = statement.code(statement.index).pc + val pc = statement.stmt.pc val caller = declaredMethods(statement.callable) val ep = propertyStore(caller, Callees.key) ep match { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 64bb043232..99e42f95e3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -14,8 +14,6 @@ import org.opalj.value.ValueInformation * A statement that is passed to the concrete analysis. * * @param method The method containing the statement. - * @param node The basic block containing the statement. - * @param stmt The TAC statement. * @param index The index of the Statement in the code. * @param code The method's TAC code. * @param cfg The method's CFG. @@ -50,18 +48,6 @@ abstract class JavaIFDSProblem[Fact <: AbstractIFDSFact](project: SomeProject) override def needsPredecessor(statement: JavaStatement): Boolean = false - /** - * Gets the call object for a statement that contains a call. - * - * @param call The call statement. - * @return The call object for `call`. - */ - protected def asCall(call: Stmt[V]): Call[V] = call.astID match { - case Assignment.ASTID => call.asAssignment.expr.asFunctionCall - case ExprStmt.ASTID => call.asExprStmt.expr.asFunctionCall - case _ => call.asMethodCall - } - /** * Checks if the return flow is actually possible from the given exit statement to the given successor. * This is used to filter flows of exceptions into normal code without being caught @@ -101,4 +87,29 @@ object JavaIFDSProblem { def switchParamAndVariableIndex(index: Int, isStaticMethod: Boolean): Int = (if (isStaticMethod) -2 else -1) - index + /** + * Gets the call object for a statement that contains a call. + * + * @param call The call statement. + * @return The call object for `call`. + */ + def asCall(call: Stmt[V]): Call[V] = call.astID match { + case Assignment.ASTID => call.asAssignment.expr.asFunctionCall + case ExprStmt.ASTID => call.asExprStmt.expr.asFunctionCall + case _ => call.asMethodCall + } + + /** + * Checks whether the callee's formal parameter is of a reference type. + */ + def isRefTypeParam(callee: Method, index: Int): Boolean = + if (index == -1) true + else { + val parameterOffset = if (callee.isStatic) 0 else 1 + callee.descriptor.parameterType( + switchParamAndVariableIndex(index, callee.isStatic) + - parameterOffset + ).isReferenceType + } + } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala index 478054d25d..399d46b729 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/VariableTypeProblem.scala @@ -133,7 +133,7 @@ class VariableTypeProblem(project: SomeProject, override val subsumeFacts: Boole in: VTAFact ): Set[VTAFact] = { val inSet = Set(in) - val callObject = asCall(call.stmt) + val callObject = JavaIFDSProblem.asCall(call.stmt) val allParams = callObject.allParams // Iterate over all input facts and over all parameters of the call. val flow = scala.collection.mutable.Set.empty[VTAFact] @@ -170,7 +170,7 @@ class VariableTypeProblem(project: SomeProject, override val subsumeFacts: Boole ): Set[VTAFact] = { val inSet = Set(in) // Check, to which variables the callee may refer - val calleeDefinitionSites = asCall(call.stmt).receiverOption + val calleeDefinitionSites = JavaIFDSProblem.asCall(call.stmt).receiverOption .map(callee => callee.asVar.definedBy) .getOrElse(EmptyIntTrieSet) val calleeTypeFacts = inSet.collect { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 987635fe5a..0ec4bb899c 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -61,7 +61,7 @@ abstract class ForwardTaintProblem(project: SomeProject) * edges will be created. */ override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { - val callObject = asCall(call.stmt) + val callObject = JavaIFDSProblem.asCall(call.stmt) val allParams = callObject.allParams val allParamsWithIndices = allParams.zipWithIndex @@ -122,26 +122,13 @@ abstract class ForwardTaintProblem(project: SomeProject) if (!isPossibleReturnFlow(exit, successor)) return Set.empty val callee = exit.callable - /** - * Checks whether the callee's formal parameter is of a reference type. - */ - def isRefTypeParam(index: Int): Boolean = - if (index == -1) true - else { - val parameterOffset = if (callee.isStatic) 0 else 1 - callee.descriptor.parameterType( - JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) - - parameterOffset - ).isReferenceType - } - if (sanitizesReturnValue(callee)) return Set.empty - val callStatement = asCall(call.stmt) + val callStatement = JavaIFDSProblem.asCall(call.stmt) val allParams = callStatement.allParams var flows: Set[TaintFact] = Set.empty in match { // Taint actual parameter if formal parameter is tainted - case Variable(index) if index < 0 && index > -100 && isRefTypeParam(index) => + case Variable(index) if index < 0 && index > -100 && JavaIFDSProblem.isRefTypeParam(callee, index) => val param = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) @@ -227,7 +214,7 @@ abstract class ForwardTaintProblem(project: SomeProject) override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { super.outsideAnalysisContext(callee) match { case Some(_) => Some(((call: JavaStatement, successor: JavaStatement, in: TaintFact, _: Getter) => { - val allParams = asCall(call.stmt).receiverOption ++ asCall(call.stmt).params + val allParams = JavaIFDSProblem.asCall(call.stmt).receiverOption ++ JavaIFDSProblem.asCall(call.stmt).params if (call.stmt.astID == Assignment.ASTID && (in match { case Variable(index) => allParams.zipWithIndex.exists { From 8baacc658ff158052a1fd8fd1e9b3a40d99e6c35 Mon Sep 17 00:00:00 2001 From: Tim Lange Date: Fri, 23 Sep 2022 01:44:56 +0200 Subject: [PATCH 57/67] Add javascript aware taint analysis --- .../fpcf/fixtures/js/Java2JsTestClass.java | 345 +++++++ .../taint/TaintAnalysisTestClass.java | 74 +- .../scala/org/opalj/fpcf/PropertiesTest.scala | 5 +- .../ForwardTaintAnalysisFixtureTest.scala | 3 + ...vaScriptAwareTaintProblemFixtureTest.scala | 42 + .../scala/org/opalj/ifds/IFDSAnalysis.scala | 42 +- OPAL/js/Readme.md | 146 +++ OPAL/js/build.sbt | 1 + .../java/org/opalj/js/wala_ifds/JSDomain.java | 19 + .../opalj/js/wala_ifds/JSFlowFunctions.java | 123 +++ .../org/opalj/js/wala_ifds/JSProblem.java | 101 ++ .../WalaJavaScriptIFDSTaintAnalysis.java | 60 ++ .../src/main/scala/org/opalj/js/JSFact.scala | 22 + .../opalj/js/JavaScriptAnalysisCaller.scala | 294 ++++++ .../js/JavaScriptAwareTaintProblem.scala | 291 ++++++ .../scala/org/opalj/js/JavaScriptSource.scala | 44 + .../org/opalj/js/LocalJSSourceFinder.scala | 184 ++++ .../JavaScriptAwareTaintAnalysisFixture.scala | 70 ++ .../summaries/java.io.BufferedInputStream.xml | 49 + .../java.io.BufferedOutputStream.xml | 55 ++ .../summaries/java.io.BufferedReader.xml | 67 ++ .../summaries/java.io.BufferedWriter.xml | 107 +++ .../java.io.ByteArrayInputStream.xml | 49 + .../java.io.ByteArrayOutputStream.xml | 77 ++ .../summaries/java.io.CharArrayReader.xml | 59 ++ .../summaries/java.io.CharArrayWriter.xml | 133 +++ .../summaries/java.io.DataInputStream.xml | 159 ++++ .../summaries/java.io.DataOutputStream.xml | 155 +++ .../main/resources/summaries/java.io.File.xml | 181 ++++ .../summaries/java.io.FileInputStream.xml | 4 + .../summaries/java.io.FileOutputStream.xml | 4 + .../summaries/java.io.FilterInputStream.xml | 17 + .../summaries/java.io.InputStream.xml | 33 + .../summaries/java.io.InputStreamReader.xml | 84 ++ .../summaries/java.io.LineNumberReader.xml | 59 ++ .../summaries/java.io.ObjectInputStream.xml | 177 ++++ .../summaries/java.io.ObjectOutputStream.xml | 225 +++++ .../summaries/java.io.OutputStream.xml | 40 + .../summaries/java.io.OutputStreamWriter.xml | 143 +++ .../summaries/java.io.PrintStream.xml | 351 +++++++ .../summaries/java.io.PrintWriter.xml | 372 ++++++++ .../summaries/java.io.StringReader.xml | 52 ++ .../summaries/java.io.StringWriter.xml | 119 +++ .../summaries/java.lang.Appendable.xml | 44 + .../resources/summaries/java.lang.Boolean.xml | 76 ++ .../resources/summaries/java.lang.Byte.xml | 148 +++ .../summaries/java.lang.CharSequence.xml | 29 + .../summaries/java.lang.Character.xml | 229 +++++ .../resources/summaries/java.lang.Class.xml | 59 ++ .../resources/summaries/java.lang.Double.xml | 154 +++ .../summaries/java.lang.Exception.xml | 4 + .../resources/summaries/java.lang.Float.xml | 163 ++++ .../resources/summaries/java.lang.Integer.xml | 212 +++++ .../resources/summaries/java.lang.Long.xml | 215 +++++ .../resources/summaries/java.lang.Math.xml | 681 ++++++++++++++ .../java.lang.NullPointerException.xml | 4 + .../resources/summaries/java.lang.Object.xml | 13 + .../summaries/java.lang.ProcessBuilder.xml | 62 ++ .../summaries/java.lang.RuntimeException.xml | 4 + .../resources/summaries/java.lang.Short.xml | 156 ++++ .../resources/summaries/java.lang.String.xml | 458 +++++++++ .../summaries/java.lang.StringBuffer.xml | 468 ++++++++++ .../summaries/java.lang.StringBuilder.xml | 438 +++++++++ .../resources/summaries/java.lang.System.xml | 29 + .../resources/summaries/java.lang.Thread.xml | 157 ++++ .../summaries/java.lang.ThreadLocal.xml | 42 + .../summaries/java.lang.Throwable.xml | 167 ++++ .../summaries/java.math.BigDecimal.xml | 881 ++++++++++++++++++ .../summaries/java.math.BigInteger.xml | 506 ++++++++++ .../resources/summaries/java.net.Proxy.xml | 57 ++ .../main/resources/summaries/java.net.URI.xml | 537 +++++++++++ .../main/resources/summaries/java.net.URL.xml | 567 +++++++++++ .../summaries/java.net.URLConnection.xml | 392 ++++++++ .../summaries/java.net.URLDecoder.xml | 21 + .../summaries/java.net.URLEncoder.xml | 21 + .../summaries/java.nio.ByteBuffer.xml | 453 +++++++++ .../summaries/java.nio.CharBuffer.xml | 294 ++++++ .../summaries/java.nio.DoubleBuffer.xml | 175 ++++ .../summaries/java.nio.FloatBuffer.xml | 174 ++++ .../summaries/java.nio.IntBuffer.xml | 174 ++++ .../summaries/java.nio.LongBuffer.xml | 174 ++++ .../summaries/java.nio.ShortBuffer.xml | 174 ++++ .../summaries/java.nio.file.Path.xml | 157 ++++ .../summaries/java.sql.ResultSet.xml | 679 ++++++++++++++ .../summaries/java.util.ArrayList.xml | 6 + .../resources/summaries/java.util.Arrays.xml | 390 ++++++++ .../summaries/java.util.Collection.xml | 61 ++ .../summaries/java.util.Collections.xml | 285 ++++++ .../resources/summaries/java.util.Deque.xml | 120 +++ .../summaries/java.util.Enumeration.xml | 15 + .../resources/summaries/java.util.HashMap.xml | 6 + .../resources/summaries/java.util.HashSet.xml | 18 + .../summaries/java.util.Iterator.xml | 15 + .../summaries/java.util.LinkedList.xml | 20 + .../resources/summaries/java.util.List.xml | 93 ++ .../summaries/java.util.ListIterator.xml | 41 + .../resources/summaries/java.util.Map.xml | 177 ++++ .../summaries/java.util.Optional.xml | 59 ++ .../summaries/java.util.OptionalDouble.xml | 49 + .../summaries/java.util.OptionalInt.xml | 49 + .../summaries/java.util.OptionalLong.xml | 49 + .../summaries/java.util.Properties.xml | 59 ++ .../resources/summaries/java.util.Queue.xml | 99 ++ .../resources/summaries/java.util.Random.xml | 13 + .../resources/summaries/java.util.Scanner.xml | 317 +++++++ .../resources/summaries/java.util.Set.xml | 18 + .../resources/summaries/java.util.Stack.xml | 139 +++ .../summaries/java.util.StringTokenizer.xml | 65 ++ .../resources/summaries/java.util.Vector.xml | 253 +++++ .../summaries/java.util.regex.Matcher.xml | 241 +++++ .../summaries/java.util.regex.Pattern.xml | 179 ++++ .../fpcf/analyses/ifds/JavaIFDSProblem.scala | 15 +- .../ifds/taint/ForwardTaintProblem.scala | 113 ++- .../analyses/ifds/taint/summaries/README.md | 0 .../ifds/taint/summaries/TaintSummaries.scala | 324 +++++++ .../taint/ForwardTaintAnalysisFixture.scala | 2 + build.sbt | 24 +- 117 files changed, 17358 insertions(+), 41 deletions(-) create mode 100644 DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/js/Java2JsTestClass.java create mode 100644 DEVELOPING_OPAL/validate/src/test/scala/org/opalj/js/JavaScriptAwareTaintProblemFixtureTest.scala create mode 100644 OPAL/js/Readme.md create mode 100644 OPAL/js/build.sbt create mode 100644 OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSDomain.java create mode 100644 OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSFlowFunctions.java create mode 100644 OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSProblem.java create mode 100644 OPAL/js/src/main/java/org/opalj/js/wala_ifds/WalaJavaScriptIFDSTaintAnalysis.java create mode 100644 OPAL/js/src/main/scala/org/opalj/js/JSFact.scala create mode 100644 OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala create mode 100644 OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala create mode 100644 OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala create mode 100644 OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala create mode 100644 OPAL/js/src/test/scala/org/opal/js/JavaScriptAwareTaintAnalysisFixture.scala create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.BufferedInputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.BufferedOutputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.BufferedReader.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.BufferedWriter.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.ByteArrayInputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.ByteArrayOutputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.CharArrayReader.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.CharArrayWriter.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.DataInputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.DataOutputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.File.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.FileInputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.FileOutputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.FilterInputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.InputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.InputStreamReader.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.LineNumberReader.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.ObjectInputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.ObjectOutputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.OutputStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.OutputStreamWriter.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.PrintStream.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.PrintWriter.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.StringReader.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.io.StringWriter.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Appendable.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Boolean.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Byte.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.CharSequence.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Character.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Class.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Double.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Exception.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Float.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Integer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Long.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Math.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.NullPointerException.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Object.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.ProcessBuilder.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.RuntimeException.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Short.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.String.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.StringBuffer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.StringBuilder.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.System.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Thread.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.ThreadLocal.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.lang.Throwable.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.math.BigDecimal.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.math.BigInteger.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.net.Proxy.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.net.URI.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.net.URL.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.net.URLConnection.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.net.URLDecoder.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.net.URLEncoder.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.nio.ByteBuffer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.nio.CharBuffer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.nio.DoubleBuffer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.nio.FloatBuffer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.nio.IntBuffer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.nio.LongBuffer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.nio.ShortBuffer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.nio.file.Path.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.sql.ResultSet.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.ArrayList.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Arrays.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Collection.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Collections.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Deque.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Enumeration.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.HashMap.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.HashSet.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Iterator.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.LinkedList.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.List.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.ListIterator.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Map.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Optional.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.OptionalDouble.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.OptionalInt.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.OptionalLong.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Properties.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Queue.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Random.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Scanner.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Set.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Stack.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.StringTokenizer.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.Vector.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.regex.Matcher.xml create mode 100644 OPAL/tac/src/main/resources/summaries/java.util.regex.Pattern.xml create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/README.md create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/js/Java2JsTestClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/js/Java2JsTestClass.java new file mode 100644 index 0000000000..46d683d967 --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/js/Java2JsTestClass.java @@ -0,0 +1,345 @@ +package org.opalj.fpcf.fixtures.js; + +import org.opalj.fpcf.properties.taint.ForwardFlowPath; + +import javax.script.*; + +public class Java2JsTestClass { + /* Test flows through Javascript. */ + + @ForwardFlowPath({"flowThroughJS"}) + public static void flowThroughJS() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + String pw = source(); + + se.put("secret", pw); + se.eval("var x = 42;"); + String fromJS = (String) se.get("secret"); + sink(fromJS); + } + + @ForwardFlowPath({}) + public static void jsOverwritesBinding() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + String pw = source(); + + se.put("secret", pw); + se.eval("secret = \"42\";"); + String fromJS = (String) se.get("secret"); + sink(fromJS); + } + + @ForwardFlowPath({"flowInsideJS"}) + public static void flowInsideJS() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + String pw = source(); + + se.put("secret", pw); + se.eval("var xxx = secret;"); + String fromJS = (String) se.get("xxx"); + sink(fromJS); + } + + @ForwardFlowPath({"flowInsideJS2"}) + public static void flowInsideJS2() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + String pw = source(); + + se.put("secret", pw); + se.eval("var xxx = secret;" + + "var yyy = xxx;"); + String fromJS = (String) se.get("yyy"); + sink(fromJS); + } + + @ForwardFlowPath({}) + public static void flowInsideJSLateOverwrite() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + String pw = source(); + + se.put("secret", pw); + se.eval("var xxx = secret;" + + "var xxx = 42;"); + String fromJS = (String) se.get("yyy"); + sink(fromJS); + } + + @ForwardFlowPath({"jsInvokeIdentity"}) + public static void jsInvokeIdentity() throws ScriptException, NoSuchMethodException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + String pw = source(); + + se.eval("function id(x) { return x; }"); + String fromJS = (String) ((Invocable) se).invokeFunction("id", pw); + sink(fromJS); + } + + @ForwardFlowPath({}) + public static void jsInvokeOverwrite() throws ScriptException, NoSuchMethodException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + String pw = source(); + + se.eval("function overwrite(x) { return \"42\"; }"); + String fromJS = (String) ((Invocable) se).invokeFunction("id", pw); + sink(fromJS); + } + + @ForwardFlowPath({"jsInvokeWithComputation"}) + public static void jsInvokeWithComputation() throws ScriptException, NoSuchMethodException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + se.eval("function check(str) {\n" + + " return str === \"1337\";\n" + + "}"); + + String pw = source(); + + Invocable inv = (Invocable) se; + Boolean state = (Boolean) inv.invokeFunction("check", pw); + sink(state); + } + + @ForwardFlowPath({}) + public static void jsUnusedTaintedParameter() throws ScriptException, NoSuchMethodException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + se.eval("function check(str, unused) {\n" + + " return str === \"1337\";\n" + + "}"); + + String pw = source(); + Invocable inv = (Invocable) se; + Boolean state = (Boolean) inv.invokeFunction("check", "1337", pw); + sink(state); + } + + /* More advanced flows. */ + + @ForwardFlowPath({"jsInterproceduralFlow"}) + public static void jsInterproceduralFlow() throws ScriptException, NoSuchMethodException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + + se.eval("function add42(x) {" + + " return x + 42;" + + "}" + + "function id(x) {" + + " return x;" + + "}" + + "function check(str) {\n" + + " return id(add42(str)) === id(\"1337\");\n" + + "}"); + String pw = source(); + Invocable inv = (Invocable) se; + Boolean state = (Boolean) inv.invokeFunction("check", pw); + sink(state); + } + + @ForwardFlowPath({"jsFunctionFlow"}) + public static void jsFunctionFlow() throws ScriptException, NoSuchMethodException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + + se.eval("function myfun(x) {\n" + + " var xxx = x;\n" + + " return xxx;\n" + + "}\n"); + String pw = source(); + Invocable inv = (Invocable) se; + String value = (String) inv.invokeFunction("myfun", pw); + sink(value); + } + + /* Test flows through ScriptEngine objects. */ + + @ForwardFlowPath({"simplePutGet"}) + public static void simplePutGet() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + + String pw = source(); + se.put("secret", pw); + Object out = se.get("secret"); + sink(out); + } + + @ForwardFlowPath({"overapproxPutGet"}) + public static void overapproxPutGet() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + + String pw = source(); + // String is no constant + se.put(Integer.toString(1337), pw); + // Because the .put had no constant string, we do not know the key here + // and taint the return as an over-approximation. + Object out = se.get("secret"); + sink(out); + } + + @ForwardFlowPath({}) + public static void overwritePutGet() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + + String pw = source(); + // String is no constant + se.put("secret", pw); + se.put("secret", "Const"); + Object out = se.get("secret"); + sink(out); + } + + @ForwardFlowPath({"bindingsSimplePutGet"}) + public static void bindingsSimplePutGet() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + Bindings b = se.createBindings(); + + String pw = source(); + se.put("secret", pw); + Object out = se.get("secret"); + sink(out); + } + + @ForwardFlowPath({"bindingsOverapproxPutGet"}) + public static void bindingsOverapproxPutGet() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + Bindings b = se.createBindings(); + + String pw = source(); + // String is no constant + se.put(Integer.toString(1337), pw); + // Because the .put had no constant string, we do not know the key here + // and taint the return as an over-approximation. + Object out = se.get("secret"); + sink(out); + } + + @ForwardFlowPath({}) + public static void BindingsOverwritePutGet() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + Bindings b = se.createBindings(); + + String pw = source(); + se.put("secret", pw); + se.put("secret", "Const"); + Object out = se.get("secret"); + sink(out); + } + + @ForwardFlowPath({}) + public static void bindingsPutRemoveGet() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + Bindings b = se.createBindings(); + + String pw = source(); + b.put("secret", pw); + b.remove("secret"); + Object out = b.get("secret"); + sink(out); + } + + @ForwardFlowPath({}) + public static void overwritePutRemoveGet() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + Bindings b = se.createBindings(); + + String pw = source(); + b.put("secret", pw); + b.remove("secret"); + Object out = b.get("secret"); + sink(out); + } + + @ForwardFlowPath({"bindingsPutAll"}) + public static void bindingsPutAll() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + Bindings b = se.createBindings(); + + String pw = source(); + b.put("secret", pw); + Bindings newb = se.createBindings(); + newb.putAll(b); + Object out = newb.get("secret"); + sink(out); + } + + @ForwardFlowPath({"interproceduralPutGet"}) + public static void interproceduralPutGet() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + + String pw = source(); + se.put("secret", pw); + id (se); + Object out = se.get("secret"); + sink(out); + } + + @ForwardFlowPath({}) + public static void interproceduralOverwrite() throws ScriptException + { + ScriptEngineManager sem = new ScriptEngineManager(); + ScriptEngine se = sem.getEngineByName("JavaScript"); + + String pw = source(); + se.put("secret", pw); + removeSecret (se); + Object out = se.get("secret"); + sink(out); + } + + public static Object id(Object obj) { + return obj; + } + + public static void removeSecret(ScriptEngine se) { + se.put("secret", 42); + } + + public static String source() { + return "1337"; + } + + private static void sink(String i) { + System.out.println(i); + } + + private static void sink(Object i) { + System.out.println(i); + } +} diff --git a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java index e3b5427b35..932c019863 100644 --- a/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java +++ b/DEVELOPING_OPAL/validate/src/test/java/org/opalj/fpcf/fixtures/taint/TaintAnalysisTestClass.java @@ -301,6 +301,48 @@ public void fieldTaintsAreConsideredInComputations() { sink(wrapper.field + 1); } + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void staticCalleeOverwritesTaint() { + Wrapper wrapper = new Wrapper(source()); + overwriteField(wrapper); + sink(wrapper.field); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void calleeOverwritesItsOwnField() { + Wrapper wrapper = new Wrapper(source()); + wrapper.overwrite(); + sink(wrapper.field); + } + + // TODO: misses that the parameter and this aliases +// @ForwardFlowPath({}) +// @BackwardFlowPath({}) +// public void instanceCalleeOverwritesTaint() { +// Wrapper wrapper = new Wrapper(source()); +// wrapper.overwriteArg(wrapper); +// sink(wrapper.field); +// } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void calleeOverwritesStaticFieldTaint() { + staticField = source(); + overwriteStaticField(); + sink(staticField); + } + + @ForwardFlowPath({}) + @BackwardFlowPath({}) + public void taintPartOfArrayOverwrite() { + int[] arr = new int[1]; + arr[0] = source(); + overwriteFirstArrayElement(arr); + sink(staticField); + } + //TODO Tests für statische Felder über Methodengrenzen //Does not work, because we do not know which exceptions cannot be thrown. @@ -325,6 +367,13 @@ public void iterationCountIsConsidered() { } }*/ + /* Tests using summaries. */ + @ForwardFlowPath({"flowWithSummary"}) + public static void flowWithSummary() { + Integer i = Integer.valueOf(source()); + sink(i.intValue()); + } + public int callSourcePublic() { return source(); } @@ -378,12 +427,25 @@ public static int source() { return 1; } - private static int sanitize(int i) {return i;} + private static int sanitize(int i) {return 42;} + private static void overwriteField(Wrapper wrapper) { + wrapper.field = 42; + } + + private static void overwriteStaticField() { + staticField = 42; + } + + private static void overwriteFirstArrayElement(int[] arr) { + arr[0] = 42; + } private static void sink(int i) { System.out.println(i); } - + private static void sink(String s) { + System.out.println(s); + } } abstract class A { @@ -414,4 +476,12 @@ class Wrapper { Wrapper(int field) { this.field = field; } + + public void overwrite() { + this.field = 42; + } + + public void overwriteArg(Wrapper wrapper) { + wrapper.field = 42; + } } \ No newline at end of file diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala index 4cb596345a..30c39363a0 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/PropertiesTest.scala @@ -4,19 +4,16 @@ package fpcf import java.io.File import java.net.URL - import com.typesafe.config.Config import com.typesafe.config.ConfigValueFactory - import org.opalj.bi.reader.ClassFileReader +import org.opalj.bytecode.RTJar import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers - import org.opalj.log.LogContext import org.opalj.util.ScalaMajorVersion import org.opalj.fpcf.properties.PropertyMatcher import org.opalj.fpcf.seq.PKESequentialPropertyStore -import org.opalj.bytecode.RTJar import org.opalj.br.DefinedMethod import org.opalj.br.analyses.VirtualFormalParameter import org.opalj.br.analyses.VirtualFormalParametersKey diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala index 270d4ee496..46994ed671 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala @@ -16,6 +16,9 @@ import java.net.URL * @author Mario Trageser */ class ForwardTaintAnalysisFixtureTest extends PropertiesTest { + override def fixtureProjectPackage: List[String] = List( + "org/opalj/fpcf/fixtures/taint" + ) override def init(p: Project[URL]): Unit = { p.updateProjectInformationKeyInitializationData( diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/js/JavaScriptAwareTaintProblemFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/js/JavaScriptAwareTaintProblemFixtureTest.scala new file mode 100644 index 0000000000..ec7d1c796b --- /dev/null +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/js/JavaScriptAwareTaintProblemFixtureTest.scala @@ -0,0 +1,42 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.js + +import org.opal.js.IFDSAnalysisJSFixtureScheduler +import org.opalj.fpcf.PropertiesTest +import org.opalj.ai.domain.l2 +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.Project +import org.opalj.fpcf.properties.taint.ForwardFlowPath +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact + +import java.net.URL + +class JavaScriptAwareTaintProblemFixtureTest extends PropertiesTest { + override def fixtureProjectPackage: List[String] = List( + "org/opalj/fpcf/fixtures/js" + ) + + override def init(p: Project[URL]): Unit = { + p.updateProjectInformationKeyInitializationData( + AIDomainFactoryKey + )( + (_: Option[Set[Class[_ <: AnyRef]]]) => + Set[Class[_ <: AnyRef]]( + classOf[l2.DefaultPerformInvocationsDomainWithCFGAndDefUse[URL]] + ) + ) + p.get(RTACallGraphKey) + } + + describe("Test the ForwardFlowPath annotations") { + val testContext = executeAnalyses(IFDSAnalysisJSFixtureScheduler) + val project = testContext.project + val eas = methodsWithAnnotations(project).map { + case (method, entityString, annotations) => + ((method, TaintNullFact), entityString, annotations) + } + testContext.propertyStore.shutdown() + validateProperties(testContext, eas, Set(ForwardFlowPath.PROPERTY_VALIDATOR_KEY)) + } +} diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index d8ee7b4351..a46d0e7ee6 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -158,6 +158,8 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < implicit var statistics = new Statistics val icfg = ifdsProblem.icfg + val DEBUG = false + /** * Performs an IFDS analysis for a method-fact-pair. * @@ -372,7 +374,15 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < */ private def normalFlow(statement: S, in: IFDSFact, predecessor: Option[S]): Set[IFDSFact] = { statistics.normalFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.normalFlow(statement, in, predecessor)) + val out = ifdsProblem.normalFlow(statement, in, predecessor) + if (DEBUG) { + printf("Normal: %s\n", statement.toString) + printf("In: %s\n", in.toString) + printf("Out: [") + out.foreach(f => printf("%s, ", f.toString)) + printf("]\n\n") + } + addNullFactIfConfigured(in, out) } /** @@ -384,7 +394,15 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < */ private def callFlow(call: S, callee: C, in: IFDSFact): Set[IFDSFact] = { statistics.callFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.callFlow(call, callee, in)) + val out = ifdsProblem.callFlow(call, callee, in) + if (DEBUG) { + printf("Call: %s\n", call.toString) + printf("In: %s\n", in.toString) + printf("Out: [") + out.foreach(f => printf("%s, ", f.toString)) + printf("]\n\n") + } + addNullFactIfConfigured(in, out) } /** @@ -397,7 +415,15 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < */ private def returnFlow(exit: S, in: IFDSFact, call: S, callFact: IFDSFact, successor: S): Set[IFDSFact] = { statistics.returnFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.returnFlow(exit, in, call, callFact, successor)) + val out = ifdsProblem.returnFlow(exit, in, call, callFact, successor) + if (DEBUG) { + printf("Return: %s\n", call.toString) + printf("In: %s\n", in.toString) + printf("Out: [") + out.foreach(f => printf("%s, ", f.toString)) + printf("]\n\n") + } + addNullFactIfConfigured(in, out) } /** @@ -408,7 +434,15 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < */ private def callToReturnFlow(call: S, in: IFDSFact, successor: S): Set[IFDSFact] = { statistics.callToReturnFlow += 1 - addNullFactIfConfigured(in, ifdsProblem.callToReturnFlow(call, in, successor)) + val out = ifdsProblem.callToReturnFlow(call, in, successor) + if (DEBUG) { + printf("CallToReturn: %s\n", call.toString) + printf("In: %s\n", in.toString) + printf("Out: [") + out.foreach(f => printf("%s, ", f.toString)) + printf("]\n\n") + } + addNullFactIfConfigured(in, out) } private def addNullFactIfConfigured(in: IFDSFact, out: Set[IFDSFact]): Set[IFDSFact] = { diff --git a/OPAL/js/Readme.md b/OPAL/js/Readme.md new file mode 100644 index 0000000000..d0995ae939 --- /dev/null +++ b/OPAL/js/Readme.md @@ -0,0 +1,146 @@ +# JavaScript module +This module provides an IFDS taint analysis that is aware of calls to `ScriptEngine` objects that evaluate JavaScript and models them by internally invoking another IFDS taint analysis running on the JavaScript code, handing back the results to the Java IFDS taint analysis. + +We identified four challenges: +1. Finding the JavaScript source code which is executed on a call to a `ScriptEngine`. +2. Tracking the taints inside the `ScriptEngine` object. +3. Handing over the taints between two taint analyses across different static analysis frameworks without rewriting an analysis. +4. Scalability in presence unboxing of primitive type wrappers. + +## 1. Finding the source code +`org.opal.js.LocalJSSourceFinder` implements a way to find the JavaScript source code. The class uses the Def-Use information provided by *TACAI* to find the source code. + +There are two ways in how JavaScript is invoked from Java. First, `eval()` can be used to evaluate code using a `ScriptEngine`: +```java +ScriptEngineManager sem = new ScriptEngineManager(); +ScriptEngine se = sem.getEngineByName("JavaScript"); +se.eval("*** insert JavaScript code here ***"); +``` +If the argument to `eval` is a constant string, the source code is already known. The argument could also be a `FileReader` or `File`. In that case, the `LocalJSSourceFinder` looks up to find the definition of the `FileReader`/`File` and tries to get the file name. + +The other way is to call `invokeFunction(functionName, ...args)` on the `ScriptEngine` object. That case is a bit more complicated, because the source code of the function was provided with an earlier call to `eval()`. So here `LocalJSSourceFinder` goes up to the definition of the `ScriptEngine` and then looks for `eval()` calls on the definition. + +### Limitations +The `LocalJSSourceFinder` is only able to find the source code intraprocedural, inherited from using the def-use chains. + +## 2. Tracking tainted JavaScript variables in Java +A `ScriptEngine` also holds an environment called `Binding` that maps variables names to values. With `put(varName, obj)` data can be handed to JavaScript and with `get(varName)` can be retrieved back from JavaScript. Every variable in the `Binding` is available as a global variable in the execution of the JavaScript script. Thus, to precisely model what's happening in JavaScript, we need to know the JavaScript variables that are tainted at a call to `eval` or `invokeFunction`. + +In essence, the environment is just a `Map` for Java. To track the tainted keys inside the map, we do manually model the behavior of the `get`, `put`, `remove` and `putAll` methods called on a `ScriptEngine` or `Binding` instance. At each call to first three, the first argument represents the variable name. Again, we use the def-use chains from *TACAI* to find a string constant. We introduce a new taint type called `BindingFact`, which holds a variable, either of type `ScriptEngine` or `Binding` and the tainted key. + +### Design Decisions +#### In case of a non-constant argument +```java +String var = getUserInput(); +se.put(var, "some value"); +``` +Whenever the analysis does not know the constant string value of the first argument to `put`, it does derive a wildcard fact. The analysis considers a wildcard fact as if all variables inside the environment are tainted. + +#### Killing taints +```java + String var; + | + se.put("v1", secret); + | + if (cond) + / \ + var = "v1"; var = "v2"; + \ / + se.remove(var); +``` +In the example above, `v1` is tainted inside the environment. Later on, `var` is removed from the environment but the constant string depends on the control-flow so `var` is either `"v1"` or `"v2"`. Herre two analysis could do two things: + +1. Kill the `BindingFact(_, "v1")`, because `"v1"` might have been removed. +2. Leave `BindingFact(_, "v1")` alive, because `"v2"` might have been removed. + +The analysis implements the second way. Having to decide in this case is just a limitation of our analysis not being path-sensitive. + +## 3. Gluing two analyses together +The semantics of `eval` and `invokeFunction` can be modelled by transforming the JavaScript source code. For this, we introduce two functions: `opal_source()` and `opal_last_stmt(...)`. The first one is marked as a source in the JavaScript analysis and used to taint all global variables that were handed over from Java. The latter is, as the name suggest, the last statement and records all taints that reach the end of the execution. We do provide dummy implementations for both functions to work around the fact that WALAs Call Graph generation does eliminate call sites without a callee. + +We do exploit that the entry and exit points of JavaScript script are easy to find out: The script is executed from top to bottom. Thus, we do perform the transformation on the source code directly. + +We do inject four symbols that may not be already in the script: `opal_source`, `opal_last_stmt`, `opal_fill_arg` and `opal_tainted_arg`. We assume that is the case, there is no check for this. + +* `opal_source` is a function that has no parameters and returns a value. This function shall be marked as a source in the JavaScript analysis. +* `opal_last_stmt` is a function with n parameters where n is the number of variables visible in the top-level scope. +* `opal_fill_arg` is an untainted variable used to generate valid function calls. +* `opal_tainted_arg` is a tainted variable used to generate valid function calls. + +```javascript +// Begin of OPAL generated code +function opal_source() { + return "secret"; +} +function opal_last_stmt(p0, p1) { } + +var secret = opal_source(); +// End of OPAL generated code + +var xxx = secret; + +// Begin of OPAL generated code +opal_last_stmt(secret, xxx); +// End of OPAL generated code +``` +Above is the generated code for a simple example where `secret` is handed over from the environment inside a `ScriptEngine`. Before the actual script, `secret` is declared in the top-level scope and tainted. After the script, `opal_last_stmt` takes all variable names as arguments. For each tainted argument, WALA is queried for the variable name and converted back to a `BindingFact`. + +One might ask why we need to pass all variables in scope as arguments. Take a look at the following JavaScript snippet: +```javascript +var secret = opal_source(); +secret = 42; +opal_last_stmt(secret); +``` +and now in SSA form: +```javascript +_1 = opal_source(); +_2 = 42; +opal_last_stmt(_2); +``` +In the SSA form, `_1` is tainted at the end and if we'd query the name of `_1`, that would be `secret`. To find out for which SSA variables we should query the name, we pass them as arguments. That also makes the Java analysis less dependent on the JavaScript analysis. In theory, to switch the underlying JavaScript analysis, one would only need to implement a way such that the JavaScript analysis returns the original variable names for the tainted arguments of `opal_last_stmt`. + +```javascript +// Begin of OPAL generated code +function opal_source() { + return "secret"; +} +function opal_last_stmt(p0) { } +// End of OPAL generated code + +function check(str, unused) { + return str === "1337"; +} + +// Begin of OPAL generated code +var opal_fill_arg = 42; +var opal_tainted_arg = opal_source(); +var opal_tainted_return = check(opal_fill_arg, opal_tainted_arg); +opal_last_stmt(opal_tainted_return); +// End of OPAL generated code +``` +Above is another example of a transformation, but this time for tainted arguments to `invokeFunction`. Here, we first generate the two variables, one tainted and one untainted. These are used to generate a valid call to the function that is invoked by `invokeFunction`. The return value is always named `opal_tainted_return` and also flows at the end into `opal_last_stmt`. Back in Java, when converting the variable names back to facts, the variable name `opal_tainted_return` indicates the special case that instead of a `BindingFact`, the return value should be tainted. + +### Limitations +In theory, if there are too many variables in the top-level scope, the number of arguments in `opal_last_stmt` could reach the maximum number of arguments of the Rhino JavaScript engine, which WALA uses. The limit seems to be in the thousands, so that should never accidentally happen in practise. + +Also, there is a bug in the WALA JavaScript IFDS analysis I did not fix. Look at the snippet below: +```javascript +var x; +function setX(v) { + x = v; +} +``` +Assume a call to `setX` with a tainted argument. At the end of the execution, `x` should be tainted. But in the WALA IFDS analysis, this is not the case. + +## 4. Scalability +When using `invokeFunction` to call JavaScript from Java, the variable parameters are passed as `Object` and the return value is an `Object` as well. In case of primitive arguments or returns, boxing and unboxing is needed. In that case, the analysis needs the JDK to precisely resolve boxings. But the IFDS solver in the current state is too inefficient such that the `JavaScriptAwareTaintProblem` gets slow. + +I have decided to implement a reader of the summaries from the *StubDroid* paper. Overriding the `useSummaries` field of `ForwardTaintProblem` enables the use of those summaries. With those, there is also no need to load the JDK, which further brings the analysis up to speed. +> S. Arzt and E. Bodden, "StubDroid: Automatic Inference of Precise Data-Flow Summaries for the Android Framework," 2016 IEEE/ACM 38th International Conference on Software Engineering (ICSE), 2016, pp. 725-735, doi: 10.1145/2884781.2884816. + + +## Things left open +* While the `LocalJSSourceFinder` is able to find filenames, I did not implement a way to find these files in the filesystem. Thus, the `JavaScriptAnalysisCaller` ignores `JavaScriptFileSource`. +* In various places, the analysis depends on finding a string constant. Plugging in a string analysis before running the JavaScript aware taint analysis might greatly improve the accuracy. +* Pooling of JavaScript analysis calls. Currently, for each taint that gets handed over to the JavaScript, a new analysis run is started. We could save the overhead of constructing a JavaScript AST and exploded supergraph everytime by pooling all taints and only calling the JavaScript analysis when the worklist of the Java IFDS solver is empty. +* Another optimization could be inside the IFDS solver: implementing unbalanced returns and only propagate zero-facts at sources. diff --git a/OPAL/js/build.sbt b/OPAL/js/build.sbt new file mode 100644 index 0000000000..b511e98651 --- /dev/null +++ b/OPAL/js/build.sbt @@ -0,0 +1 @@ +// build settings reside in the opal root build.sbt file diff --git a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSDomain.java b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSDomain.java new file mode 100644 index 0000000000..c192c5534e --- /dev/null +++ b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSDomain.java @@ -0,0 +1,19 @@ +package org.opalj.js.wala_ifds; + +import com.ibm.wala.dataflow.IFDS.PathEdge; +import com.ibm.wala.dataflow.IFDS.TabulationDomain; +import com.ibm.wala.ipa.cfg.BasicBlockInContext; +import com.ibm.wala.ssa.analysis.IExplodedBasicBlock; +import com.ibm.wala.util.collections.Pair; +import com.ibm.wala.util.intset.MutableMapping; + +class JSDomain extends MutableMapping>> + implements TabulationDomain>, BasicBlockInContext> +{ + private static final long serialVersionUID = -1897766113586243833L; + + @Override + public boolean hasPriorityOver(PathEdge> pathEdge, PathEdge> pathEdge1) { + return false; + } +} diff --git a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSFlowFunctions.java b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSFlowFunctions.java new file mode 100644 index 0000000000..b9eb8965f1 --- /dev/null +++ b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSFlowFunctions.java @@ -0,0 +1,123 @@ +package org.opalj.js.wala_ifds; + +import com.ibm.wala.dataflow.IFDS.*; +import com.ibm.wala.ipa.callgraph.CGNode; +import com.ibm.wala.ipa.cfg.BasicBlockInContext; +import com.ibm.wala.ssa.*; +import com.ibm.wala.ssa.analysis.IExplodedBasicBlock; +import com.ibm.wala.util.collections.Pair; +import com.ibm.wala.util.intset.IntSet; +import com.ibm.wala.util.intset.IntSetUtil; +import com.ibm.wala.util.intset.MutableIntSet; + +class JSFlowFunctions implements IPartiallyBalancedFlowFunctions> { + ISupergraph, CGNode> supergraph; + JSDomain domain; + + public JSFlowFunctions(JSDomain domain, ISupergraph, CGNode> supergraph) + { + this.supergraph = supergraph; + this.domain = domain; + } + + @Override + public IUnaryFlowFunction getNormalFlowFunction(BasicBlockInContext src, + BasicBlockInContext dest) { + final IExplodedBasicBlock ebb = src.getDelegate(); + final IExplodedBasicBlock dbb = dest.getDelegate(); + + return new IUnaryFlowFunction() { + + private void propagate(SSAInstruction inst, Pair> vn, MutableIntSet r) { + boolean propagates = inst instanceof SSAPhiInstruction || inst instanceof SSABinaryOpInstruction || inst instanceof SSAUnaryOpInstruction; + + if (propagates) { + for (int i = 0; i < inst.getNumberOfUses(); i++) { + if (vn.fst == inst.getUse(i)) { + Pair> nvn = Pair.make(inst.getDef(), src); + if (! domain.hasMappedIndex(nvn)) { + domain.add(nvn); + } + r.add(domain.getMappedIndex(nvn)); + } + } + } + } + + @Override + public IntSet getTargets(int d1) { + Pair> vn = domain.getMappedObject(d1); + MutableIntSet r = IntSetUtil.make(new int[] { domain.getMappedIndex(vn) }); + dbb.iteratePhis().forEachRemaining((inst) -> propagate(inst, vn, r)); + propagate(ebb.getInstruction(), vn, r); + + return r; + } + }; + } + + // taint parameters from arguments + @Override + public IUnaryFlowFunction getCallFlowFunction(BasicBlockInContext src, + BasicBlockInContext dest, BasicBlockInContext ret) { + final IExplodedBasicBlock ebb = src.getDelegate(); + SSAInstruction inst = ebb.getInstruction(); + return (d1) -> { + MutableIntSet r = IntSetUtil.make(); + Pair> vn = domain.getMappedObject(d1); + for (int i = 0; i < inst.getNumberOfUses(); i++) { + if (vn.fst == inst.getUse(i)) { + Pair> key = Pair.make(i+1, src); + if (! domain.hasMappedIndex(key)) { + domain.add(key); + } + r.add(domain.getMappedIndex(key)); + } + } + return r; + }; + } + + // pass tainted values back to caller + @Override + public IUnaryFlowFunction getReturnFlowFunction(BasicBlockInContext call, + BasicBlockInContext src, BasicBlockInContext dest) { + int retVal = call.getLastInstruction().getDef(); + return (d1) -> { + Pair> vn = domain.getMappedObject(d1); + MutableIntSet result = IntSetUtil.make(); + supergraph.getPredNodes(src).forEachRemaining(pb -> { + SSAInstruction inst = pb.getDelegate().getInstruction(); + if (inst instanceof SSAReturnInstruction && inst.getUse(0) == vn.fst) { + Pair> key = Pair.make(retVal, pb); + if (! domain.hasMappedIndex(key)) { + domain.add(key); + } + result.add(domain.getMappedIndex(key)); + } + }); + return result; + }; + } + + // local variables are not changed by calls. + @Override + public IUnaryFlowFunction getCallToReturnFlowFunction(BasicBlockInContext src, + BasicBlockInContext dest) { + return IdentityFlowFunction.identity(); + } + + // missing a callee, so assume it does nothing and keep local information. + @Override + public IUnaryFlowFunction getCallNoneToReturnFlowFunction(BasicBlockInContext src, + BasicBlockInContext dest) { + return IdentityFlowFunction.identity(); + } + + // data flow back from an unknown call site, so just keep what comes back. + @Override + public IFlowFunction getUnbalancedReturnFlowFunction(BasicBlockInContext src, + BasicBlockInContext dest) { + return IdentityFlowFunction.identity(); + } +} diff --git a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSProblem.java b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSProblem.java new file mode 100644 index 0000000000..839c992195 --- /dev/null +++ b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSProblem.java @@ -0,0 +1,101 @@ +package org.opalj.js.wala_ifds; + +import com.ibm.wala.dataflow.IFDS.*; +import com.ibm.wala.ipa.callgraph.CGNode; +import com.ibm.wala.ipa.cfg.BasicBlockInContext; +import com.ibm.wala.ssa.SSAInstruction; +import com.ibm.wala.ssa.analysis.IExplodedBasicBlock; +import com.ibm.wala.util.collections.HashSetFactory; +import com.ibm.wala.util.collections.Pair; + +import java.util.Collection; +import java.util.function.Function; + +class JSProblem implements PartiallyBalancedTabulationProblem, CGNode, Pair>> { + private final ISupergraph, CGNode> supergraph; + private final JSDomain domain; + private final Function, Boolean> sources; + /** path edges corresponding to taint sources */ + private final Collection>> initialSeeds; + + private final JSFlowFunctions flowFunctions; + + public JSProblem(JSDomain domain, ISupergraph, CGNode> supergraph, Function, Boolean> sources) + { + this.supergraph = supergraph; + this.domain = domain; + this.sources = sources; + this.initialSeeds = collectInitialSeeds(); + this.flowFunctions = new JSFlowFunctions(domain, supergraph); + } + + /** + * we use the entry block of the CGNode as the fake entry when propagating from + * callee to caller with unbalanced parens + */ + @Override + public BasicBlockInContext getFakeEntry(BasicBlockInContext node) { + final CGNode cgNode = node.getNode(); + return getFakeEntry(cgNode); + } + + /** + * we use the entry block of the CGNode as the "fake" entry when propagating + * from callee to caller with unbalanced parens + */ + private BasicBlockInContext getFakeEntry(final CGNode cgNode) { + BasicBlockInContext[] entriesForProcedure = supergraph.getEntriesForProcedure(cgNode); + assert entriesForProcedure.length == 1; + return entriesForProcedure[0]; + } + + /** + * collect sources of taint + */ + private Collection>> collectInitialSeeds() { + Collection>> result = HashSetFactory.make(); + for (BasicBlockInContext bb : supergraph) { + IExplodedBasicBlock ebb = bb.getDelegate(); + SSAInstruction instruction = ebb.getInstruction(); + + if (sources.apply(bb)) { + Pair> fact = Pair.make(instruction.getDef(), bb); + final CGNode cgNode = bb.getNode(); + int factNum = domain.add(fact); + BasicBlockInContext fakeEntry = getFakeEntry(cgNode); + // note that the fact number used for the source of this path edge doesn't + // really matter + result.add(PathEdge.createPathEdge(fakeEntry, factNum, bb, factNum)); + } + } + return result; + } + + @Override + public IPartiallyBalancedFlowFunctions> getFunctionMap() { + return flowFunctions; + } + + @Override + public JSDomain getDomain() { + return domain; + } + + /** + * we don't need a merge function; the default unioning of tabulation works fine + */ + @Override + public IMergeFunction getMergeFunction() { + return null; + } + + @Override + public ISupergraph, CGNode> getSupergraph() { + return supergraph; + } + + @Override + public Collection>> initialSeeds() { + return initialSeeds; + } +} \ No newline at end of file diff --git a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/WalaJavaScriptIFDSTaintAnalysis.java b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/WalaJavaScriptIFDSTaintAnalysis.java new file mode 100644 index 0000000000..ca0d89f56e --- /dev/null +++ b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/WalaJavaScriptIFDSTaintAnalysis.java @@ -0,0 +1,60 @@ +package org.opalj.js.wala_ifds; + +import com.ibm.wala.dataflow.IFDS.ICFGSupergraph; +import com.ibm.wala.dataflow.IFDS.PartiallyBalancedTabulationSolver; +import com.ibm.wala.dataflow.IFDS.TabulationDomain; +import com.ibm.wala.dataflow.IFDS.TabulationResult; +import com.ibm.wala.ipa.callgraph.CGNode; +import com.ibm.wala.ipa.callgraph.CallGraph; +import com.ibm.wala.ipa.cfg.BasicBlockInContext; +import com.ibm.wala.ssa.analysis.IExplodedBasicBlock; +import com.ibm.wala.util.CancelException; +import com.ibm.wala.util.collections.Pair; +import com.ibm.wala.util.intset.IntSet; +import scala.Function3; + +import java.util.function.Function; + +public class WalaJavaScriptIFDSTaintAnalysis { + + private final ICFGSupergraph supergraph; + + private final JSDomain domain; + + private final Function, Boolean> sources; + + public WalaJavaScriptIFDSTaintAnalysis(CallGraph cg, Function, Boolean> sources) + { + supergraph = ICFGSupergraph.make(cg); + this.sources = sources; + this.domain = new JSDomain(); + } + + public TabulationResult, CGNode, Pair>> analyze() { + PartiallyBalancedTabulationSolver, CGNode, Pair>> solver = PartiallyBalancedTabulationSolver + .createPartiallyBalancedTabulationSolver(new JSProblem(domain, supergraph, sources), null); + TabulationResult, CGNode, Pair>> result = null; + try { + result = solver.solve(); + } catch (CancelException e) { + // this shouldn't happen + assert false; + } + return result; + } + + public static void startJSAnalysis(CallGraph CG, Function, Boolean> sources, + Function3, + IntSet, + TabulationDomain>, + BasicBlockInContext>, + Void> sinks) { + WalaJavaScriptIFDSTaintAnalysis A = new WalaJavaScriptIFDSTaintAnalysis(CG, sources); + + TabulationResult, + CGNode, Pair>> R = A.analyze(); + + R.getSupergraphNodesReached().forEach((sbb) -> sinks.apply(sbb, R.getResult(sbb), A.domain)); + } +} diff --git a/OPAL/js/src/main/scala/org/opalj/js/JSFact.scala b/OPAL/js/src/main/scala/org/opalj/js/JSFact.scala new file mode 100644 index 0000000000..42c4c32915 --- /dev/null +++ b/OPAL/js/src/main/scala/org/opalj/js/JSFact.scala @@ -0,0 +1,22 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.js + +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact + +/* Common trait for facts for ScriptEngines. */ +trait JSFact extends TaintFact + +/** + * A tainted value inside a Key-Value-Map. + * + * @param index variable of a map type + * @param keyName name of the key. Empty string if unknown. + */ +case class BindingFact(index: Int, keyName: String) extends JSFact with TaintFact + +/** + * A tainted value inside a Key-Value-Map where the value is not statically known. + * + * @param index variable of a map type + */ +case class WildcardBindingFact(index: Int) extends JSFact with TaintFact diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala new file mode 100644 index 0000000000..f54269389a --- /dev/null +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala @@ -0,0 +1,294 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.js + +import com.ibm.wala.cast.js.ipa.callgraph.{JSCFABuilder, JSCallGraphUtil} +import com.ibm.wala.cast.js.translator.CAstRhinoTranslatorFactory +import com.ibm.wala.cast.js.util.JSCallGraphBuilderUtil +import com.ibm.wala.dataflow.IFDS.TabulationDomain +import com.ibm.wala.ipa.callgraph.CallGraph +import com.ibm.wala.ipa.cfg.BasicBlockInContext +import com.ibm.wala.ssa.SSAAbstractInvokeInstruction +import com.ibm.wala.ssa.analysis.IExplodedBasicBlock +import com.ibm.wala.util.collections.Pair +import com.ibm.wala.util.intset.{IntIterator, IntSet, IntSetUtil, MutableIntSet} +import org.opalj.br.analyses.SomeProject +import org.opalj.js.wala_ifds.WalaJavaScriptIFDSTaintAnalysis +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, TaintFact, Variable} +import org.mozilla.javascript.{Parser, Token} +import org.mozilla.javascript.ast.{AstNode, AstRoot, FunctionNode, NodeVisitor, Symbol} + +import java.io.File + +/** + * Class which does convert a call from Java to JavaScript into something that a JavaScript IFDS analysis + * can consume and converts the results back into facts for the Java taint analysis. + * + * @param p Project + */ +class JavaScriptAnalysisCaller(p: SomeProject) { + type Domain = TabulationDomain[Pair[Integer, BasicBlockInContext[IExplodedBasicBlock]], BasicBlockInContext[IExplodedBasicBlock]] + + val sourceFinder = new LocalJSSourceFinder(p) + + /** + * Analyze an evaluation of a JavaScript script. + * + * @param stmt Java Statement + * @param in incoming BindingFact + * @return set of taints + */ + def analyze(stmt: JavaStatement, in: JSFact): Set[TaintFact] = { + val sourceFiles = sourceFinder(stmt) + in match { + case b: BindingFact => sourceFiles.flatMap(s => analyzeScript(s, b)) + /* If we don't know what variable is tainted, we have to give up. */ + case b: WildcardBindingFact => Set(b) + } + } + + /** + * Analyze a call to a JavaScript function. + * + * @param stmt Java Statement + * @param in Variable-length parameter array + * @param fName function name + * @return set of taints + */ + def analyze(stmt: JavaStatement, in: ArrayElement, fName: String): Set[TaintFact] = { + val sourceFiles = sourceFinder(stmt) + sourceFiles.flatMap(s => analyzeFunction(s, in, fName, stmt.index)) + in + } + + /** + * Return all variable names that are in the top level scope. + * + * @param root root AST node of the script + * @return set of variable names + */ + private def getTopLevelVariableNames(root: AstRoot): Set[String] = { + var nameSet: Set[String] = Set() + root.getSymbols.forEach(symbol => { + if ((symbol.getDeclType == Token.VAR + || symbol.getDeclType == Token.LET + || symbol.getDeclType == Token.CONST) + && symbol.getContainingTable == root) + nameSet += symbol.getName + }) + nameSet + } + + /** + * Return the corresponding function node for the function symbol. + * + * @param func function symbol + * @return function node + */ + private def getFunctionNode(func: Symbol): Option[FunctionNode] = { + /** + * Visitor to find the node of the function in the AST. + */ + class FunctionFinder extends NodeVisitor { + var fnResult: Option[FunctionNode] = None + + override def visit(node: AstNode): Boolean = { + if (fnResult.isEmpty) + node match { + case fn: FunctionNode => + fnResult = Some(fn) + false + case _ => true + } + else + false + } + } + + val fnFinder = new FunctionFinder() + func.getContainingTable.visit(fnFinder) + fnFinder.fnResult + } + + /** + * Map a list of variable names to a list of BindingFacts + * + * @param vars variable names + * @param defSite definition site of the incoming BindingFact + * @return list of BindingFacts + */ + private def varNamesToFacts(vars: List[String], defSite: Integer): Set[TaintFact] = + vars.map(v => if (v == "opal_tainted_return") Variable(defSite) else BindingFact(defSite, v)).toSet + + /** + * Generate a argument list of length n with one tainted argument. + * + * @param n number of arguments + * @param taintedIdx index which should be tainted + * @return argument list as string + */ + private def generateFunctionArgs(n: Int, taintedIdx: Int): String = { + (0 until n).map(i => + if (i == taintedIdx) { + s"opal_tainted_arg" + } else { + s"opal_fill_arg" + }).mkString(", ") + } + + /** + * Prepare and analyze a JavaScript function call. + * + * @param sourceFile JavaScript source + * @param in incoming fact from a variable-length parameter list + * @param fName function name + * @param stmtIndex statement index + * @return set of facts + */ + private def analyzeFunction(sourceFile: JavaScriptSource, in: ArrayElement, + fName: String, stmtIndex: Int): Set[TaintFact] = { + val parser: Parser = new Parser() + + val root: AstRoot = parser.parse(sourceFile.asString, "fileName", 1) + val sTable = root.getSymbolTable + if (!sTable.containsKey(fName)) + return Set() + val fSymbol = sTable.get(fName) + if (fSymbol.getDeclType != Token.FUNCTION) + return Set() + val fNode = getFunctionNode(fSymbol) + if (fNode.isEmpty) + return Set() + + val nameSet: Set[String] = getTopLevelVariableNames(root)+"opal_tainted_return" + val beforeCode = + s"""function opal_source() { + | return \"secret\"; + |} + |function opal_last_stmt(${generateParams(nameSet.size)}) { } + |\n""".stripMargin + val funcCall = s"var opal_tainted_return = $fName(${generateFunctionArgs(fNode.get.getParamCount, in.element)});" + val afterCode = s""" + |var opal_fill_arg = 42; + |var opal_tainted_arg = opal_source(); + |$funcCall + |opal_last_stmt(${nameSet.mkString(", ")});\n""".stripMargin + + val f: File = sourceFile.asFile(beforeCode, afterCode) + analyzeCommon(f, stmtIndex) + } + + /** + * Generate a n-length parameter list. + * + * @param n number of parameters + * @return parameter list as string + */ + private def generateParams(n: Int): String = { + (0 until n).map(i => s"p$i").mkString(", ") + } + + /** + * Prepare and analyze a script evaluation. + * + * @param sourceFile JavaScript source + * @param in incoming BindingFact + * @return set of facts + */ + private def analyzeScript(sourceFile: JavaScriptSource, in: BindingFact): Set[TaintFact] = { + val parser: Parser = new Parser() + val taintedVar = s"var ${in.keyName} = opal_source();\n" + val root: AstRoot = parser.parse(taintedVar + sourceFile.asString, "fileName", 1) + val nameSet: Set[String] = getTopLevelVariableNames(root) + + val beforeCode = + s"""function opal_source() { + | return \"secret\"; + |} + |function opal_last_stmt(${generateParams(nameSet.size)}) { } + |\n + |$taintedVar""".stripMargin + val afterCode = s"\nopal_last_stmt(${nameSet.mkString(", ")});\n" + val f: File = sourceFile.asFile(beforeCode, afterCode) + analyzeCommon(f, in.index) + } + + /** + * Invoke the WALA IFDS taint analysis + * + * @param f JavaScript file + * @param idx Index used in the out facts + * @return set of facts + */ + private def analyzeCommon(f: File, idx: Int): Set[TaintFact] = { + JSCallGraphUtil.setTranslatorFactory(new CAstRhinoTranslatorFactory) + val B: JSCFABuilder = JSCallGraphBuilderUtil.makeScriptCGBuilder(f.getParent, f.getName) + val CG: CallGraph = B.makeCallGraph(B.getOptions) + + /** + * Check basic block for source. + * + * @param ebb exploded basic block + * @return true if ebb contains a source + */ + def sources(ebb: BasicBlockInContext[IExplodedBasicBlock]): Boolean = { + val inst = ebb.getDelegate.getInstruction + inst match { + case i: SSAAbstractInvokeInstruction => + CG.getPossibleTargets(ebb.getNode, i.getCallSite).forEach(target => { + if (target.getMethod.getDeclaringClass.getName.toString.endsWith("opal_source")) + return true + }) + case _ => + } + false + } + + var varsAliveAfterJS: List[String] = List() + + /** + * Add all tainted variable names to varsAliveAfterJS after the analysis ran. + * + * @param bb basic block + * @param r result set + * @param d domain + */ + def sinks(bb: BasicBlockInContext[IExplodedBasicBlock], r: IntSet, d: Domain): Void = { + val inst = bb.getDelegate.getInstruction + inst match { + case invInst: SSAAbstractInvokeInstruction => + CG.getPossibleTargets(bb.getNode, invInst.getCallSite).forEach(target => { + if (target.getMethod.getDeclaringClass.getName.toString.endsWith("opal_last_stmt")) { + /* Collect all parameters. */ + val paramIdx: MutableIntSet = IntSetUtil.make() + for (i <- 1 until invInst.getNumberOfPositionalParameters) { + paramIdx.add(inst.getUse(i)) + } + + /* Collect all tainted variables reaching the end of the JS script. */ + val it: IntIterator = r.intIterator() + val taints: MutableIntSet = IntSetUtil.make() + while (it.hasNext) { + val vn = d.getMappedObject(it.next()) + taints.add(vn.fst) + } + + /* Intersect all tainted variables with the parameters of the last statement. + This is a workaround to only reconstruct the variable names for the "last" SSA name. */ + val taintedParams: IntSet = taints.intersection(paramIdx) + val taintedIt: IntIterator = taintedParams.intIterator() + while (taintedIt.hasNext) { + val idx = taintedIt.next() + varsAliveAfterJS ++= bb.getNode.getIR.getLocalNames(1, idx).toList + } + } + }) + case _ => + } + null + } + + WalaJavaScriptIFDSTaintAnalysis.startJSAnalysis(CG, sources, sinks) + f.delete() + varNamesToFacts(varsAliveAfterJS, idx) + } +} diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala new file mode 100644 index 0000000000..25aa88bc59 --- /dev/null +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala @@ -0,0 +1,291 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.js + +import org.opalj.br.{Method, ObjectType} +import org.opalj.br.analyses.SomeProject +import org.opalj.collection.immutable.IntTrieSet +import org.opalj.ifds.Dependees.Getter +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.{NO_MATCH, V} +import org.opalj.tac.{AITACode, Assignment, Call, ComputeTACAIKey, Expr, ReturnValue, Stmt, TACMethodParameter, TACode} +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, FlowFact, ForwardTaintProblem, TaintFact, TaintNullFact, Variable} +import org.opalj.value.ValueInformation + +import scala.annotation.nowarn + +/** + * Java IFDS analysis that is able to resolve calls to JavaScript. + * + * @param p project + */ +class JavaScriptAwareTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { + final type TACAICode = TACode[TACMethodParameter, JavaIFDSProblem.V] + val tacaiKey: Method => AITACode[TACMethodParameter, ValueInformation] = p.get(ComputeTACAIKey) + + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a variable. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + override protected def createFlowFact( + callee: Method, + call: JavaStatement, + in: TaintFact + ): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) + Some(FlowFact(Seq(JavaMethod(call.method), JavaMethod(callee)))) + else None + + /** + * The entry points of this analysis. + */ + override def entryPoints: Seq[(Method, TaintFact)] = + for { + m <- p.allMethodsWithBody + if m.name == "main" + } yield m -> TaintNullFact + + /** + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. + * + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" + + /** + * Called in callToReturnFlow. This method can return whether the input fact + * will be removed after `callee` was called. I.e. the method could sanitize parameters. + * + * @param call The call statement. + * @param in The fact which holds before the call. + * @return Whether in will be removed after the call. + */ + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false + + override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { + val callObject = JavaIFDSProblem.asCall(call.stmt) + val allParams = callObject.allParams + + val allParamsWithIndices = allParams.zipWithIndex + in match { + case BindingFact(index, keyName) => allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) => + Some(BindingFact(JavaIFDSProblem.switchParamAndVariableIndex( + paramIndex, + callee.isStatic + ), keyName)) + case _ => None // Nothing to do + }.toSet + case _ => super.callFlow(call, callee, in) + } + } + + override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, + callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { + if (!isPossibleReturnFlow(exit, successor)) return Set.empty + val callee = exit.callable + if (sanitizesReturnValue(callee)) return Set.empty + val callStatement = JavaIFDSProblem.asCall(call.stmt) + val allParams = callStatement.allParams + + in match { + case BindingFact(index, keyName) => + var flows: Set[TaintFact] = Set.empty + if (index < 0 && index > -100) { + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.map(i => BindingFact(i, keyName)) + } + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefinedBy.contains(index)) + flows += BindingFact(call.index, keyName) + } + flows + case _ => super.returnFlow(exit, in, call, callFact, successor) + } + } + + /** + * Kills the flow. + * + * @return empty set + */ + def killFlow( + @nowarn call: JavaStatement, + @nowarn successor: JavaStatement, + @nowarn in: TaintFact, + @nowarn dependeesGetter: Getter + ): Set[TaintFact] = Set.empty + + val scriptEngineMethods: Map[ObjectType, List[String]] = Map( + ObjectType("javax/script/Invocable") -> List("invokeFunction"), + ObjectType("javax/script/ScriptEngine") -> List("put", "get", "eval"), + ObjectType("javax/script/Bindings") -> List("put", "get", "putAll", "remove"), + ) + + /** + * Checks whether we handle the method. + * + * @param objType type of the base object + * @param methodName method name of the call + * @return true if we have a rule for the method call + */ + def invokesScriptFunction(objType: ObjectType, methodName: String): Boolean = + scriptEngineMethods.exists(kv => objType.isSubtypeOf(kv._1)(p.classHierarchy) && kv._2.contains(methodName)) + + def invokesScriptFunction(callStmt: Call[JavaIFDSProblem.V]): Boolean = + invokesScriptFunction(callStmt.declaringClass.mostPreciseObjectType, callStmt.name) + + def invokesScriptFunction(method: Method): Boolean = + invokesScriptFunction(method.classFile.thisType, method.name) + + /** + * Kill the flow if we handle the call outside. + */ + override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { + if (invokesScriptFunction(callee)) { + Some(killFlow _) + } else { + super.outsideAnalysisContext(callee) + } + } + + /** + * Finds all definition/use sites inside the method. + * + * @param method method to be searched in + * @param sites definition or use sites + * @return sites as JavaStatement + */ + def searchStmts(method: Method, sites: IntTrieSet): Set[Stmt[JavaIFDSProblem.V]] = { + val taCode = tacaiKey(method) + sites.map(site => taCode.stmts.apply(site)) + } + + /** + * Returns all possible constant strings. Contains the empty string if at least one was non-constant. + * + * @param method method + * @param defSites def sites of the queried variable + * @return + */ + private def getPossibleStrings(method: Method, defSites: IntTrieSet): Set[String] = { + val taCode = tacaiKey(method) + + defSites.map(site => taCode.stmts.apply(site)).map { + case a: Assignment[JavaIFDSProblem.V] if a.expr.isStringConst => + a.expr.asStringConst.value + case _ => "" + } + } + + val jsAnalysis = new JavaScriptAnalysisCaller(p) + + override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { + val callStmt = JavaIFDSProblem.asCall(call.stmt) + val allParams = callStmt.allParams + val allParamsWithIndex = callStmt.allParams.zipWithIndex + + if (!invokesScriptFunction(callStmt)) { + in match { + case BindingFact(index, _) => + if (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == NO_MATCH) + Set(in) + else + Set() + case _ => super.callToReturnFlow(call, in, successor) + } + } else { + in match { + /* Call to invokeFunction. The variable length parameter list is an array in TACAI. */ + case arrIn: ArrayElement if callStmt.name == "invokeFunction" + && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, arrIn.index) == -3 => + val fNames = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + fNames.map(fName => + if (fName == "") + /* Function name is unknown. We don't know what to call */ + None + else + Some(jsAnalysis.analyze(call, arrIn, fName))).filter(_.isDefined).flatMap(_.get) ++ Set(in) + /* Call to eval. */ + case f: BindingFact if callStmt.name == "eval" + && (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -1 + || JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -3) => + jsAnalysis.analyze(call, f) + case f: WildcardBindingFact if callStmt.name == "eval" + && (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -1 + || JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -3) => + jsAnalysis.analyze(call, f) + /* Put obj in Binding */ + case Variable(index) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -3 => + val keyNames = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + val defSites = callStmt.receiverOption.get.asVar.definedBy + keyNames.flatMap(keyName => defSites.map(i => if (keyName == "") WildcardBindingFact(i) else BindingFact(i, keyName))) ++ Set(in) + /* putAll BindingFact to other BindingFact */ + case BindingFact(index, keyName) if callStmt.name == "putAll" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -2 => + callStmt.receiverOption.get.asVar.definedBy.map(i => if (keyName == "") WildcardBindingFact(i) else BindingFact(i, keyName)) ++ Set(in) + case WildcardBindingFact(index) if callStmt.name == "putAll" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -2 => + callStmt.receiverOption.get.asVar.definedBy.map(i => WildcardBindingFact(i)) ++ Set(in) + /* Overwrite BindingFact */ + case BindingFact(index, keyName) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + val possibleFields = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + if (possibleFields.size == 1 && possibleFields.contains(keyName)) + /* Key is definitely overwritten */ + Set() + else + Set(in) + case WildcardBindingFact(index) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + /* We never overwrite here as we don't know the key */ + Set(in) + /* Remove BindingFact */ + case BindingFact(index, keyName) if callStmt.name == "remove" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + val possibleFields = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + if (possibleFields.size == 1 && possibleFields.contains(keyName)) + Set() + else + Set(in) + case WildcardBindingFact(index) if callStmt.name == "remove" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + /* We never kill here as we don't know the key */ + Set(in) + /* get from BindingFact */ + case BindingFact(index, keyName) if callStmt.name == "get" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + val possibleFields = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + if (possibleFields.size == 1 && possibleFields.contains(keyName)) + Set(Variable(call.index), in) + else + Set(in) + case WildcardBindingFact(index) if callStmt.name == "get" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + Set(Variable(call.index), in) + case _ => Set(in) + } + } + } + + override def isTainted(expression: Expr[V], in: TaintFact): Boolean = { + val definedBy = expression.asVar.definedBy + expression.isVar && (in match { + case BindingFact(index, _) => definedBy.contains(index) + case _ => super.isTainted(expression, in) + }) + } +} diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala new file mode 100644 index 0000000000..e4859448e3 --- /dev/null +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala @@ -0,0 +1,44 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.js + +import java.io.{File, FileOutputStream} +import java.nio.file.Files + +/** + * Common trait for representing JavaScript sources embedded in Java source code. + */ +sealed trait JavaScriptSource { + /** + * Return the JavaScript source code as a string. + * @return JavaScript source + */ + def asString: String + + /** + * + * @param codeBefore Code to be added in front of the script. + * @param codeAfter Code to be added at the end of the script. + * @return file handler + */ + def asFile(codeBefore: String, codeAfter: String): File +} + +case class JavaScriptStringSource(source: String) extends JavaScriptSource { + lazy val tmpFile: File = Files.createTempFile("opal", ".js").toFile + + override def asString: String = source + + override def asFile(codeBefore: String, codeAfter: String): File = { + val code = codeBefore + source + codeAfter + val fos = new FileOutputStream(tmpFile) + fos.write(code.getBytes()) + fos.close() + tmpFile + } +} + +case class JavaScriptFileSource(path: String) extends JavaScriptSource { + override def asString: String = throw new RuntimeException("Not implemented!") + + override def asFile(codeBefore: String, codeAfter: String): File = throw new RuntimeException("Not implemented!") +} diff --git a/OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala b/OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala new file mode 100644 index 0000000000..c0bc8fada8 --- /dev/null +++ b/OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala @@ -0,0 +1,184 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.js + +import org.opalj.br.{Method, ObjectType} +import org.opalj.br.analyses.SomeProject +import org.opalj.collection.immutable.IntTrieSet +import org.opalj.tac.{AITACode, ASTNode, Assignment, Call, ComputeTACAIKey, Expr, ExprStmt, MethodCall, Stmt, TACMethodParameter, TACode} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaStatement} +import org.opalj.value.ValueInformation + +import scala.collection.mutable + +/** + * Uses the def-use information of TACAI to find the javascript sources passed + * to an ScriptEngine within method boundaries. + * + * @param p Project + */ +class LocalJSSourceFinder(val p: SomeProject) extends (JavaStatement => Set[JavaScriptSource]) { + final type TACAICode = TACode[TACMethodParameter, JavaIFDSProblem.V] + val tacaiKey: Method => AITACode[TACMethodParameter, ValueInformation] = p.get(ComputeTACAIKey) + + /* Maps a statement defining a JavaScript source to the instance. */ + val statementToJavaScriptSource: mutable.Map[Stmt[V], JavaScriptSource] = mutable.Map() + /* Maps the statement to search from to the resulting set of JavaScriptSources. */ + val jsSourceCache: mutable.Map[(JavaStatement, Expr[V]), Set[JavaScriptSource]] = mutable.Map() + + /** + * @see [[LocalJSSourceFinder.findJSSourceOnInvokeFunction()]] + */ + override def apply(stmt: JavaStatement): Set[JavaScriptSource] = findJSSourceOnInvokeFunction(stmt, JavaIFDSProblem.asCall(stmt.stmt).allParams.head.asVar) + + /** + * In case of a snippet like this: + * 1 ScriptEngine se = ...; + * 2 se.eval("JS Code"); + * 3 ((Invocable) se).invokeFunction("myFunction", args...); + * This functions finds "JS Code" given "se" at se.invokeFunction(). + * + * @param javaStmt statement to start with + * @param arg ScriptEngine variable + * @return javascript source code + */ + private def findJSSourceOnInvokeFunction(javaStmt: JavaStatement, arg: Expr[V]): Set[JavaScriptSource] = { + val decls = findCallOnObject(javaStmt.method, arg.asVar.definedBy, "getEngineByName") + + val maybeCached = jsSourceCache.get((javaStmt, arg)) + if (maybeCached.isDefined) + maybeCached.get + else + decls.flatMap(decl => { + val evals = findCallOnObject( + javaStmt.method, + decl.asAssignment.targetVar.usedBy, + "eval" + ) + val jsSources = evals.flatMap(eval => { + val evalCall = JavaIFDSProblem.asCall(eval) + varToJavaScriptSource(javaStmt.method, evalCall.params.head.asVar) + }) + jsSourceCache += (javaStmt, arg) -> jsSources + jsSources + }) + } + + /** + * Finds all definiton/use sites inside the method. + * + * @param method method to be searched in + * @param sites definition or use sites + * @return sites as JavaStatement + */ + private def searchStmts(method: Method, sites: IntTrieSet): Set[Stmt[JavaIFDSProblem.V]] = { + val taCode = tacaiKey(method) + sites.map(site => taCode.stmts.apply(site)) + } + + /** + * If stmt is a call, return it as a FunctionCall + * + * @param stmt Statement + * @return maybe a function call + */ + private def maybeCall(stmt: Stmt[JavaIFDSProblem.V]): Option[Call[JavaIFDSProblem.V]] = { + def isCall(node: ASTNode[JavaIFDSProblem.V]) = node match { + case expr: Expr[JavaIFDSProblem.V] => expr.isVirtualFunctionCall || expr.isStaticFunctionCall + case stmt: Stmt[JavaIFDSProblem.V] => (stmt.isNonVirtualMethodCall + || stmt.isVirtualMethodCall + || stmt.isStaticMethodCall) + } + + stmt match { + case exprStmt: ExprStmt[JavaIFDSProblem.V] if isCall(exprStmt.expr) => + Some(exprStmt.expr.asFunctionCall) + case assignStmt: Assignment[JavaIFDSProblem.V] if isCall(assignStmt.expr) => + Some(assignStmt.expr.asFunctionCall) + case call: MethodCall[JavaIFDSProblem.V] if isCall(stmt) => + Some(call) + case _ => None + } + } + + /** + * Finds instance method calls. + * + * @param method Method to search in. + * @param sites def/use sites + * @param methodName searched method name as string + * @return + */ + private def findCallOnObject(method: Method, sites: IntTrieSet, methodName: String): Set[Stmt[V]] = { + val stmts = searchStmts(method, sites) + stmts.map(stmt => maybeCall(stmt) match { + case Some(call) if call.name.equals(methodName) => Some(stmt) + case _ => None + }).filter(_.isDefined).map(_.get) + } + + /** + * Tries to resolve a variable either to a string constant or a file path containing the variable's value + * + * @param method method to be searched in + * @param variable variable of interest + * @return JavaScriptSource + */ + private def varToJavaScriptSource(method: Method, variable: JavaIFDSProblem.V): Set[JavaScriptSource] = { + val resultSet: mutable.Set[JavaScriptSource] = mutable.Set() + + def findFileArg(sites: IntTrieSet): Unit = { + val calls = findCallOnObject(method, sites, "") + calls.foreach(init => { + val defSitesOfFileSrc = init.asInstanceMethodCall.params.head.asVar.definedBy + val defs = searchStmts(method, defSitesOfFileSrc) + defs.foreach { + /* new File("path/to/src"); */ + case a: Assignment[JavaIFDSProblem.V] if a.expr.isStringConst => + // resultSet.add(statementToJavaScriptSource.getOrElseUpdate(a, + // JavaScriptFileSource(a.expr.asStringConst.value))) + /* File constructor argument is no string constant */ + case _ => + } + }) + } + + def findFileReaderArg(sites: IntTrieSet): Unit = { + val calls = findCallOnObject(method, sites, "") + calls.foreach(init => { + val defSitesOfFileReaderSrc = init.asInstanceMethodCall.params.head.asVar.definedBy + val defs = searchStmts(method, defSitesOfFileReaderSrc) + defs.foreach { + /* FileReader fr = new FileReader(new File("path/to/src")); */ + case a: Assignment[JavaIFDSProblem.V] if a.expr.isStringConst => + // resultSet.add(statementToJavaScriptSource.getOrElseUpdate(a, + // JavaScriptFileSource(a.expr.asStringConst.value))) + /* new FileReader(new File(...)); */ + case a: Assignment[JavaIFDSProblem.V] if a.expr.isNew => + if (a.expr.asNew.tpe.isSubtypeOf(ObjectType("java/io/File"))(p.classHierarchy)) + findFileArg(a.targetVar.usedBy) + // Unknown argument + case _ => + } + }) + } + + val nextJStmts = searchStmts(method, variable.definedBy) + nextJStmts.foreach { + /* se.eval("function() ..."); */ + case a: Assignment[JavaIFDSProblem.V] if a.expr.isStringConst => + resultSet.add(statementToJavaScriptSource.getOrElseUpdate(a, JavaScriptStringSource(a.expr.asStringConst.value))) + case a: Assignment[JavaIFDSProblem.V] if a.expr.isNew => + val tpe: ObjectType = a.expr.asNew.tpe + /* se.eval(new FileReader(...)); */ + if (tpe.isSubtypeOf(ObjectType("java/io/FileReader"))(p.classHierarchy)) + findFileReaderArg(a.targetVar.usedBy) + /* se.eval(new File(...)); */ + else if (tpe.isSubtypeOf(ObjectType("java/io/File"))(p.classHierarchy)) + findFileArg(a.targetVar.usedBy) + case _ => + } + + resultSet.toSet + } +} diff --git a/OPAL/js/src/test/scala/org/opal/js/JavaScriptAwareTaintAnalysisFixture.scala b/OPAL/js/src/test/scala/org/opal/js/JavaScriptAwareTaintAnalysisFixture.scala new file mode 100644 index 0000000000..2f2e5ed19a --- /dev/null +++ b/OPAL/js/src/test/scala/org/opal/js/JavaScriptAwareTaintAnalysisFixture.scala @@ -0,0 +1,70 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opal.js + +import org.opalj.br.Method +import org.opalj.br.analyses.{DeclaredMethodsKey, ProjectInformationKeys, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSPropertyMetaInformation} +import org.opalj.js.JavaScriptAwareTaintProblem +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, TaintNullFact, Variable} +import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties.Taint + +/** + * An analysis that checks, if the return value of the method `source` can flow to the parameter of + * the method `sink`. + * + * @author Mario Trageser + */ +class JavaScriptAwareTaintAnalysisFixture(project: SomeProject) + extends IFDSAnalysis()(project, new JavaScriptAwareTaintProblemProblemFixture(project), Taint) + +class JavaScriptAwareTaintProblemProblemFixture(p: SomeProject) extends JavaScriptAwareTaintProblem(p) { + /* Without, the tests are unbearably slow. */ + override def useSummaries = true + + /** + * The analysis starts with all public methods in TaintAnalysisTestClass. + */ + override val entryPoints: Seq[(Method, TaintFact)] = p.allProjectClassFiles.filter(classFile => + classFile.thisType.fqn == "org/opalj/fpcf/fixtures/js/Java2JsTestClass") + .flatMap(classFile => classFile.methods) + .filter(method => method.isPublic && outsideAnalysisContext(method).isEmpty) + .map(method => method -> TaintNullFact) + + /** + * The sanitize method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" + + /** + * We do not sanitize paramters. + */ + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false + + /** + * Creates a new variable fact for the callee, if the source was called. + */ + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Create a FlowFact, if sink is called with a tainted variable. + * Note, that sink does not accept array parameters. No need to handle them. + */ + override protected def createFlowFact(callee: Method, call: JavaStatement, + in: TaintFact): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) + Some(FlowFact(Seq(JavaMethod(call.method)))) + else None +} + +object IFDSAnalysisJSFixtureScheduler extends IFDSAnalysisScheduler[TaintFact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new JavaScriptAwareTaintAnalysisFixture(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint + override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(Taint)) + override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) +} diff --git a/OPAL/tac/src/main/resources/summaries/java.io.BufferedInputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.BufferedInputStream.xml new file mode 100644 index 0000000000..85006bb4c7 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.BufferedInputStream.xml @@ -0,0 +1,49 @@ + +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.BufferedOutputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.BufferedOutputStream.xml new file mode 100644 index 0000000000..35013f071d --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.BufferedOutputStream.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.BufferedReader.xml b/OPAL/tac/src/main/resources/summaries/java.io.BufferedReader.xml new file mode 100644 index 0000000000..0f399e3716 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.BufferedReader.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.BufferedWriter.xml b/OPAL/tac/src/main/resources/summaries/java.io.BufferedWriter.xml new file mode 100644 index 0000000000..c37bd77c3e --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.BufferedWriter.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.ByteArrayInputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.ByteArrayInputStream.xml new file mode 100644 index 0000000000..b3a7d61d72 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.ByteArrayInputStream.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.ByteArrayOutputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.ByteArrayOutputStream.xml new file mode 100644 index 0000000000..30c97061e7 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.ByteArrayOutputStream.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.CharArrayReader.xml b/OPAL/tac/src/main/resources/summaries/java.io.CharArrayReader.xml new file mode 100644 index 0000000000..45efab1df1 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.CharArrayReader.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.CharArrayWriter.xml b/OPAL/tac/src/main/resources/summaries/java.io.CharArrayWriter.xml new file mode 100644 index 0000000000..39152be6aa --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.CharArrayWriter.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.DataInputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.DataInputStream.xml new file mode 100644 index 0000000000..dc5cc5574b --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.DataInputStream.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.DataOutputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.DataOutputStream.xml new file mode 100644 index 0000000000..bfe755710f --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.DataOutputStream.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.File.xml b/OPAL/tac/src/main/resources/summaries/java.io.File.xml new file mode 100644 index 0000000000..2bb42b87c4 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.File.xml @@ -0,0 +1,181 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.FileInputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.FileInputStream.xml new file mode 100644 index 0000000000..746c9df3b6 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.FileInputStream.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.FileOutputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.FileOutputStream.xml new file mode 100644 index 0000000000..222c0f437c --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.FileOutputStream.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.FilterInputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.FilterInputStream.xml new file mode 100644 index 0000000000..b839b17c4d --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.FilterInputStream.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.InputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.InputStream.xml new file mode 100644 index 0000000000..4e2cab0b43 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.InputStream.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.InputStreamReader.xml b/OPAL/tac/src/main/resources/summaries/java.io.InputStreamReader.xml new file mode 100644 index 0000000000..5929805c49 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.InputStreamReader.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.LineNumberReader.xml b/OPAL/tac/src/main/resources/summaries/java.io.LineNumberReader.xml new file mode 100644 index 0000000000..dcccb2b32a --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.LineNumberReader.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.ObjectInputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.ObjectInputStream.xml new file mode 100644 index 0000000000..449c3cc737 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.ObjectInputStream.xml @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.ObjectOutputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.ObjectOutputStream.xml new file mode 100644 index 0000000000..82ece10632 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.ObjectOutputStream.xml @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.OutputStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.OutputStream.xml new file mode 100644 index 0000000000..363d1aa088 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.OutputStream.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.OutputStreamWriter.xml b/OPAL/tac/src/main/resources/summaries/java.io.OutputStreamWriter.xml new file mode 100644 index 0000000000..9e2dbfccfd --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.OutputStreamWriter.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.io.PrintStream.xml b/OPAL/tac/src/main/resources/summaries/java.io.PrintStream.xml new file mode 100644 index 0000000000..27f76ff312 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.PrintStream.xml @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.PrintWriter.xml b/OPAL/tac/src/main/resources/summaries/java.io.PrintWriter.xml new file mode 100644 index 0000000000..136f9cf2c8 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.PrintWriter.xml @@ -0,0 +1,372 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.StringReader.xml b/OPAL/tac/src/main/resources/summaries/java.io.StringReader.xml new file mode 100644 index 0000000000..e184bd1f43 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.StringReader.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.io.StringWriter.xml b/OPAL/tac/src/main/resources/summaries/java.io.StringWriter.xml new file mode 100644 index 0000000000..6533577544 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.io.StringWriter.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Appendable.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Appendable.xml new file mode 100644 index 0000000000..8722eaf5fb --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Appendable.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Boolean.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Boolean.xml new file mode 100644 index 0000000000..57385073fb --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Boolean.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Byte.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Byte.xml new file mode 100644 index 0000000000..28ef000a4c --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Byte.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.CharSequence.xml b/OPAL/tac/src/main/resources/summaries/java.lang.CharSequence.xml new file mode 100644 index 0000000000..a0733d5ba2 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.CharSequence.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Character.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Character.xml new file mode 100644 index 0000000000..5b78cb91ca --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Character.xml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Class.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Class.xml new file mode 100644 index 0000000000..c8d1bc4880 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Class.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Double.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Double.xml new file mode 100644 index 0000000000..a6e3563336 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Double.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Exception.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Exception.xml new file mode 100644 index 0000000000..6553a7d777 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Exception.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Float.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Float.xml new file mode 100644 index 0000000000..7d3de1a922 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Float.xml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Integer.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Integer.xml new file mode 100644 index 0000000000..bb61e88527 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Integer.xml @@ -0,0 +1,212 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Long.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Long.xml new file mode 100644 index 0000000000..012e50d8c6 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Long.xml @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Math.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Math.xml new file mode 100644 index 0000000000..0de3d2420f --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Math.xml @@ -0,0 +1,681 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.NullPointerException.xml b/OPAL/tac/src/main/resources/summaries/java.lang.NullPointerException.xml new file mode 100644 index 0000000000..1724c68718 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.NullPointerException.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Object.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Object.xml new file mode 100644 index 0000000000..8afcc0f453 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Object.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.ProcessBuilder.xml b/OPAL/tac/src/main/resources/summaries/java.lang.ProcessBuilder.xml new file mode 100644 index 0000000000..3a1cd57cb3 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.ProcessBuilder.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.RuntimeException.xml b/OPAL/tac/src/main/resources/summaries/java.lang.RuntimeException.xml new file mode 100644 index 0000000000..a5f0f11ca6 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.RuntimeException.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Short.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Short.xml new file mode 100644 index 0000000000..0176ec7840 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Short.xml @@ -0,0 +1,156 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.String.xml b/OPAL/tac/src/main/resources/summaries/java.lang.String.xml new file mode 100644 index 0000000000..2d88c24acf --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.String.xml @@ -0,0 +1,458 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.StringBuffer.xml b/OPAL/tac/src/main/resources/summaries/java.lang.StringBuffer.xml new file mode 100644 index 0000000000..45d17e936d --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.StringBuffer.xml @@ -0,0 +1,468 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.StringBuilder.xml b/OPAL/tac/src/main/resources/summaries/java.lang.StringBuilder.xml new file mode 100644 index 0000000000..0f0a227963 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.StringBuilder.xml @@ -0,0 +1,438 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.System.xml b/OPAL/tac/src/main/resources/summaries/java.lang.System.xml new file mode 100644 index 0000000000..2370e3f149 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.System.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Thread.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Thread.xml new file mode 100644 index 0000000000..22bb351ae1 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Thread.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.ThreadLocal.xml b/OPAL/tac/src/main/resources/summaries/java.lang.ThreadLocal.xml new file mode 100644 index 0000000000..bd35c46a82 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.ThreadLocal.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.lang.Throwable.xml b/OPAL/tac/src/main/resources/summaries/java.lang.Throwable.xml new file mode 100644 index 0000000000..fea8e12f90 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.lang.Throwable.xml @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.math.BigDecimal.xml b/OPAL/tac/src/main/resources/summaries/java.math.BigDecimal.xml new file mode 100644 index 0000000000..d51734a762 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.math.BigDecimal.xml @@ -0,0 +1,881 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.math.BigInteger.xml b/OPAL/tac/src/main/resources/summaries/java.math.BigInteger.xml new file mode 100644 index 0000000000..845dc12bf3 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.math.BigInteger.xml @@ -0,0 +1,506 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.net.Proxy.xml b/OPAL/tac/src/main/resources/summaries/java.net.Proxy.xml new file mode 100644 index 0000000000..091862e579 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.net.Proxy.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.net.URI.xml b/OPAL/tac/src/main/resources/summaries/java.net.URI.xml new file mode 100644 index 0000000000..dd33751420 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.net.URI.xml @@ -0,0 +1,537 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.net.URL.xml b/OPAL/tac/src/main/resources/summaries/java.net.URL.xml new file mode 100644 index 0000000000..dc8775f4a7 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.net.URL.xml @@ -0,0 +1,567 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.net.URLConnection.xml b/OPAL/tac/src/main/resources/summaries/java.net.URLConnection.xml new file mode 100644 index 0000000000..9aa6357ce0 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.net.URLConnection.xml @@ -0,0 +1,392 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.net.URLDecoder.xml b/OPAL/tac/src/main/resources/summaries/java.net.URLDecoder.xml new file mode 100644 index 0000000000..d77cded909 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.net.URLDecoder.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.net.URLEncoder.xml b/OPAL/tac/src/main/resources/summaries/java.net.URLEncoder.xml new file mode 100644 index 0000000000..d130acfc8a --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.net.URLEncoder.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.nio.ByteBuffer.xml b/OPAL/tac/src/main/resources/summaries/java.nio.ByteBuffer.xml new file mode 100644 index 0000000000..753ea64d0c --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.nio.ByteBuffer.xml @@ -0,0 +1,453 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.nio.CharBuffer.xml b/OPAL/tac/src/main/resources/summaries/java.nio.CharBuffer.xml new file mode 100644 index 0000000000..9c7728ad57 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.nio.CharBuffer.xml @@ -0,0 +1,294 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.nio.DoubleBuffer.xml b/OPAL/tac/src/main/resources/summaries/java.nio.DoubleBuffer.xml new file mode 100644 index 0000000000..a78282ed02 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.nio.DoubleBuffer.xml @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.nio.FloatBuffer.xml b/OPAL/tac/src/main/resources/summaries/java.nio.FloatBuffer.xml new file mode 100644 index 0000000000..e3aca493ca --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.nio.FloatBuffer.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.nio.IntBuffer.xml b/OPAL/tac/src/main/resources/summaries/java.nio.IntBuffer.xml new file mode 100644 index 0000000000..bf5c7898fa --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.nio.IntBuffer.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.nio.LongBuffer.xml b/OPAL/tac/src/main/resources/summaries/java.nio.LongBuffer.xml new file mode 100644 index 0000000000..112b3db88a --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.nio.LongBuffer.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.nio.ShortBuffer.xml b/OPAL/tac/src/main/resources/summaries/java.nio.ShortBuffer.xml new file mode 100644 index 0000000000..920dab843a --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.nio.ShortBuffer.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.nio.file.Path.xml b/OPAL/tac/src/main/resources/summaries/java.nio.file.Path.xml new file mode 100644 index 0000000000..fdaa153c4c --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.nio.file.Path.xml @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.sql.ResultSet.xml b/OPAL/tac/src/main/resources/summaries/java.sql.ResultSet.xml new file mode 100644 index 0000000000..a15e7d66f7 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.sql.ResultSet.xml @@ -0,0 +1,679 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.ArrayList.xml b/OPAL/tac/src/main/resources/summaries/java.util.ArrayList.xml new file mode 100644 index 0000000000..e7dde39081 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.ArrayList.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Arrays.xml b/OPAL/tac/src/main/resources/summaries/java.util.Arrays.xml new file mode 100644 index 0000000000..932381331a --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Arrays.xml @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Collection.xml b/OPAL/tac/src/main/resources/summaries/java.util.Collection.xml new file mode 100644 index 0000000000..b2a317642d --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Collection.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Collections.xml b/OPAL/tac/src/main/resources/summaries/java.util.Collections.xml new file mode 100644 index 0000000000..e5487db2c0 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Collections.xml @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Deque.xml b/OPAL/tac/src/main/resources/summaries/java.util.Deque.xml new file mode 100644 index 0000000000..2152c0907d --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Deque.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Enumeration.xml b/OPAL/tac/src/main/resources/summaries/java.util.Enumeration.xml new file mode 100644 index 0000000000..c2ba0c98de --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Enumeration.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.HashMap.xml b/OPAL/tac/src/main/resources/summaries/java.util.HashMap.xml new file mode 100644 index 0000000000..88cea68fae --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.HashMap.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.HashSet.xml b/OPAL/tac/src/main/resources/summaries/java.util.HashSet.xml new file mode 100644 index 0000000000..9c2ef3caa1 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.HashSet.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Iterator.xml b/OPAL/tac/src/main/resources/summaries/java.util.Iterator.xml new file mode 100644 index 0000000000..7d3b86873e --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Iterator.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.LinkedList.xml b/OPAL/tac/src/main/resources/summaries/java.util.LinkedList.xml new file mode 100644 index 0000000000..1836f7fc49 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.LinkedList.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.List.xml b/OPAL/tac/src/main/resources/summaries/java.util.List.xml new file mode 100644 index 0000000000..30e60c489d --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.List.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.ListIterator.xml b/OPAL/tac/src/main/resources/summaries/java.util.ListIterator.xml new file mode 100644 index 0000000000..92b09f5709 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.ListIterator.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Map.xml b/OPAL/tac/src/main/resources/summaries/java.util.Map.xml new file mode 100644 index 0000000000..6016ada266 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Map.xml @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Optional.xml b/OPAL/tac/src/main/resources/summaries/java.util.Optional.xml new file mode 100644 index 0000000000..697393bd5e --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Optional.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.OptionalDouble.xml b/OPAL/tac/src/main/resources/summaries/java.util.OptionalDouble.xml new file mode 100644 index 0000000000..0eccfa8399 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.OptionalDouble.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.OptionalInt.xml b/OPAL/tac/src/main/resources/summaries/java.util.OptionalInt.xml new file mode 100644 index 0000000000..dbc4c35bfb --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.OptionalInt.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.OptionalLong.xml b/OPAL/tac/src/main/resources/summaries/java.util.OptionalLong.xml new file mode 100644 index 0000000000..ef67d6c000 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.OptionalLong.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Properties.xml b/OPAL/tac/src/main/resources/summaries/java.util.Properties.xml new file mode 100644 index 0000000000..8b050f0a8e --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Properties.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Queue.xml b/OPAL/tac/src/main/resources/summaries/java.util.Queue.xml new file mode 100644 index 0000000000..3c141003a6 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Queue.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Random.xml b/OPAL/tac/src/main/resources/summaries/java.util.Random.xml new file mode 100644 index 0000000000..24022e232d --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Random.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Scanner.xml b/OPAL/tac/src/main/resources/summaries/java.util.Scanner.xml new file mode 100644 index 0000000000..169ddc3e3c --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Scanner.xml @@ -0,0 +1,317 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Set.xml b/OPAL/tac/src/main/resources/summaries/java.util.Set.xml new file mode 100644 index 0000000000..5d230b3b4b --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Set.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Stack.xml b/OPAL/tac/src/main/resources/summaries/java.util.Stack.xml new file mode 100644 index 0000000000..7ac9e30642 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Stack.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.StringTokenizer.xml b/OPAL/tac/src/main/resources/summaries/java.util.StringTokenizer.xml new file mode 100644 index 0000000000..8168807949 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.StringTokenizer.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.Vector.xml b/OPAL/tac/src/main/resources/summaries/java.util.Vector.xml new file mode 100644 index 0000000000..d4357a2093 --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.Vector.xml @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OPAL/tac/src/main/resources/summaries/java.util.regex.Matcher.xml b/OPAL/tac/src/main/resources/summaries/java.util.regex.Matcher.xml new file mode 100644 index 0000000000..16ed09a60b --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.regex.Matcher.xml @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/resources/summaries/java.util.regex.Pattern.xml b/OPAL/tac/src/main/resources/summaries/java.util.regex.Pattern.xml new file mode 100644 index 0000000000..d2b3d255ac --- /dev/null +++ b/OPAL/tac/src/main/resources/summaries/java.util.regex.Pattern.xml @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala index 99e42f95e3..a869c5ff52 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/JavaIFDSProblem.scala @@ -6,7 +6,7 @@ import org.opalj.br.analyses.SomeProject import org.opalj.br.cfg.{CFG, CFGNode} import org.opalj.ifds.Dependees.Getter import org.opalj.ifds.{AbstractIFDSFact, IFDSProblem, Statement} -import org.opalj.tac.{Assignment, Call, DUVar, ExprStmt, Return, ReturnValue, Stmt, TACStmts} +import org.opalj.tac.{Assignment, Call, DUVar, Expr, ExprStmt, Return, ReturnValue, Stmt, TACStmts} import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.value.ValueInformation @@ -112,4 +112,17 @@ object JavaIFDSProblem { ).isReferenceType } + val NO_MATCH = 256 + def getParameterIndex(allParamsWithIndex: Seq[(Expr[JavaIFDSProblem.V], Int)], index: Int): Int = { + getParameterIndex(allParamsWithIndex, index, isStaticMethod = false) + } + + def getParameterIndex(allParamsWithIndex: Seq[(Expr[JavaIFDSProblem.V], Int)], index: Int, isStaticMethod: Boolean): Int = { + allParamsWithIndex.find { + case (param, paramI) => param.asVar.definedBy.contains(index) + } match { + case Some((param, paramI)) => JavaIFDSProblem.switchParamAndVariableIndex(paramI, isStaticMethod) + case None => NO_MATCH + } + } } diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 0ec4bb899c..f3a1eba931 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -6,12 +6,19 @@ import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} import org.opalj.ifds.Dependees.Getter import org.opalj.tac._ import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.TaintSummaries import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} +import java.io.File + abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[TaintFact](project) with TaintProblem[Method, JavaStatement, TaintFact] { val declaredMethods = project.get(DeclaredMethodsKey) + + def useSummaries: Boolean = false + lazy val summaries: TaintSummaries = TaintSummaries(new File(getClass.getResource("/summaries/").getPath).listFiles().toList) + override def nullFact: TaintFact = TaintNullFact override def needsPredecessor(statement: JavaStatement): Boolean = false @@ -41,16 +48,28 @@ abstract class ForwardTaintProblem(project: SomeProject) case PutField.ASTID => val put = statement.stmt.asPutField val definedBy = put.objRef.asVar.definedBy - if (isTainted(put.value, in)) + if (isTainted(put.value, in)) { + // RHS is tainted, thus the lhs as well Set(in) ++ definedBy.map { InstanceField(_, put.declaringClass, put.name) } - else + } else if (isTainted(put.objRef, in)) { + // the lhs is tainted and overwritten + Set() + } else { + // if the taint is not affected, just leave it alive Set(in) + } case PutStatic.ASTID => val put = statement.stmt.asPutStatic - if (isTainted(put.value, in)) + if (isTainted(put.value, in)) { + // RHS is tainted, thus the lhs as well Set(in, StaticField(put.declaringClass, put.name)) - else + } else if (in == StaticField(put.declaringClass, put.name)) { + // If LHS is equal to the taint, untaint the field here + Set.empty + } else { + // if the taint is not affected, just leave it alive Set(in) + } case _ => Set(in) } } @@ -87,7 +106,6 @@ abstract class ForwardTaintProblem(project: SomeProject) )) case _ => None // Nothing to do }.toSet - case InstanceField(index, declClass, taintedField) => // Taint field of formal parameter if field of actual parameter is tainted // Only if the formal parameter is of a type that may have that field! @@ -182,8 +200,42 @@ abstract class ForwardTaintProblem(project: SomeProject) /** * Removes taints according to `sanitizesParameter`. */ - override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = - if (sanitizesParameter(call, in)) Set() else Set(in) + + override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { + if (sanitizesParameter(call, in)) return Set.empty + + val callStmt = JavaIFDSProblem.asCall(call.stmt) + val allParams = callStmt.allParams + + def isRefTypeParam(index: Int): Boolean = { + allParams.find(p => p.asVar.definedBy.contains(index)) match { + case Some(param) => param.asVar.value.isReferenceValue + case None => false + } + } + + if (useSummaries && summaries.isSummarized(callStmt)) { + summaries.compute(call, callStmt, in) + } else if (icfg.getCalleesIfCallStatement(call).isEmpty) { + // If the call does not have any callees, the code is unknown + // and we safely handle it as the identity + Set(in) + } else { + // Otherwise use the java call semantics + in match { + // Local variables that are of a reference type flow through the callee + case Variable(index) if isRefTypeParam(index) => Set.empty + // Arrays are references passed-by-value, thus their contents might change in the callee + case ArrayElement(index, _) if allParams.exists(p => p.asVar.definedBy.contains(index)) => Set.empty + // Fields can be written by reference, thus always through through the callee + case InstanceField(index, _, _) if allParams.exists(p => p.asVar.definedBy.contains(index)) => Set.empty + // Static fields are accessible everywhere, thus have to flow through all callee. + case StaticField(_, _) => Set.empty + // All facts that do not match any parameter or base object, as well as primitives flow over a call + case f: TaintFact => Set(f) + } + } + } /** * Called, when the exit to return facts are computed for some `callee` with the null fact and @@ -212,27 +264,30 @@ abstract class ForwardTaintProblem(project: SomeProject) * We assume that the callee does not call the source method. */ override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { - super.outsideAnalysisContext(callee) match { - case Some(_) => Some(((call: JavaStatement, successor: JavaStatement, in: TaintFact, _: Getter) => { - val allParams = JavaIFDSProblem.asCall(call.stmt).receiverOption ++ JavaIFDSProblem.asCall(call.stmt).params - if (call.stmt.astID == Assignment.ASTID && (in match { - case Variable(index) => - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) => true - case _ => false - } - case ArrayElement(index, _) => - allParams.zipWithIndex.exists { - case (param, _) if param.asVar.definedBy.contains(index) => true - case _ => false - } - case _ => false - })) Set(Variable(call.index)) - else Set.empty - }): OutsideAnalysisContextHandler) - case None => None - } - + if (useSummaries && summaries.isSummarized(callee)) { + /* Kill the flow and apply the summary in CallToReturn. */ + Some(((_: JavaStatement, _: JavaStatement, _: TaintFact, _: Getter) => Set.empty): OutsideAnalysisContextHandler) + } else + super.outsideAnalysisContext(callee) match { + case Some(_) => Some(((call: JavaStatement, successor: JavaStatement, in: TaintFact, _: Getter) => { + val allParams = JavaIFDSProblem.asCall(call.stmt).receiverOption ++ JavaIFDSProblem.asCall(call.stmt).params + if (call.stmt.astID == Assignment.ASTID && (in match { + case Variable(index) => + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) => true + case _ => false + } + case ArrayElement(index, _) => + allParams.zipWithIndex.exists { + case (param, _) if param.asVar.definedBy.contains(index) => true + case _ => false + } + case _ => false + })) Set(Variable(call.index)) + else Set.empty + }): OutsideAnalysisContextHandler) + case None => None + } } /** @@ -302,7 +357,7 @@ abstract class ForwardTaintProblem(project: SomeProject) * @param in The current data flow facts. * @return True, if the expression could be tainted */ - private def isTainted(expression: Expr[V], in: TaintFact): Boolean = { + protected def isTainted(expression: Expr[V], in: TaintFact): Boolean = { val definedBy = expression.asVar.definedBy expression.isVar && (in match { case Variable(index) => definedBy.contains(index) diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/README.md b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala new file mode 100644 index 0000000000..344d6cb09d --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala @@ -0,0 +1,324 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.ifds.taint.summaries + +import org.opalj.br.{ArrayType, BooleanType, ByteType, CharType, DoubleType, FieldType, FloatType, IntegerType, LongType, Method, ObjectType, ShortType, Type, VoidType} +import org.opalj.tac.Call +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, InstanceField, TaintFact, TaintNullFact, Variable} +import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.Flow.nodeToTaint +import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.ClassSummary.{signaturePattern, stringToFieldType, stringToType} + +import java.io.File +import scala.util.matching.Regex +import scala.xml.{Node, XML} + +/** + * This file implements a subset of the StubDroid summary specification. + * + * Not implemented (yet): + * - Summaries for inner classes + * - Type Checking + * - Metadata/Exclusiveness + * - Aliasing (depends on the IFDS analysis) + * - Access Paths with k > 1 (again, depends on the IFS analysis) + */ + +/** + * Holds summaries for all classes. + * + * @param files summary XML files + */ +case class TaintSummaries(files: List[File]) { + val summaries: Map[String, ClassSummary] = + (files.map(f => f.getName.replace(".xml", "").replace(".", "/")) + zip + files.map(f => new ClassSummary(XML.loadFile(f)))).toMap + + private def isSummarized(objType: ObjectType): Boolean = + summaries.contains(objType.fqn) + + /** + * Return true if the method is summarized. + * + * @param callStmt call site in TACAI + * @return whether the method is summarized. + */ + def isSummarized(callStmt: Call[JavaIFDSProblem.V]): Boolean = + isSummarized(callStmt.declaringClass.mostPreciseObjectType) + + /** + * Return true if the method is summarized. + * + * @param method callee + * @return whether the method is summarized. + */ + def isSummarized(method: Method): Boolean = + isSummarized(method.classFile.thisType) + + /** + * Applies the summary if available, else does nothing. + * Precondition: callee class is summarized. + * + * @param call call java statement + * @param callStmt call TAC statement + * @param in fact + * @return out set of facts + */ + def compute(call: JavaStatement, callStmt: Call[V], in: TaintFact): Set[TaintFact] = { + val classFqn = callStmt.declaringClass.mostPreciseObjectType.fqn + assert(summaries.contains(classFqn)) + + summaries(classFqn).compute(call, callStmt, in) + } +} + +/** + * Holds the methods summaries for a single class. + * + * @param summaryNode 'summary' XML node + */ +case class ClassSummary(summaryNode: Node) { + /* Method summaries. */ + def methods: Seq[MethodSummary] = (summaryNode \\ "methods" \\ "method") + .map(methodNode => { + val sig = (methodNode \@ "id") + .replace("<", "<") + .replace(">", ">") + signaturePattern.findFirstMatchIn(sig) match { + case Some(m) => + val paramTypes: List[FieldType] = + if (m.group(3) == "") List() + else m.group(3) + .split(",") + .map(s => stringToFieldType(s.strip())) + .toList + MethodSummary( + stringToType(m.group(1)), + m.group(2), + paramTypes, + methodNode + ) + case None => + throw new RuntimeException("couldn't parse the signature of "+sig); + } + }).toList + + /** + * Applies the summary if available, else does perform the identity. + * + * @param call call java statement + * @param callStmt call TAC statement + * @param in fact + * @return out set of facts + */ + def compute(call: JavaStatement, callStmt: Call[V], in: TaintFact): Set[TaintFact] = { + in match { + /* We do not have any summaries for null facts. */ + case TaintNullFact => Set(in) + case _ => + /* Find the right method, even when overloaded. */ + val methodSummary = methods.find(m => m.methodName == callStmt.name + && m.paramTypes + .zip(callStmt.descriptor.parameterTypes) + .forall(t => t._1 == t._2)) + methodSummary match { + case Some(methodSummary) => methodSummary.compute(call, callStmt, in) + in + case None => Set(in) + } + } + } +} + +object ClassSummary { + /* group 1 = return type, group 2 = method name, group 3 = param list */ + val signaturePattern: Regex = """([a-zA-Z.\[\]]*?) ([a-zA-Z0-9_<>]*?)\(([a-zA-Z.\[\],\s]*?)\)""".r + + /** + * Converts the type string in Soot's format to the OPAL ObjectType, excluding void. + * + * @param str type string + * @return ObjectType + */ + def stringToFieldType(str: String): FieldType = { + str match { + case "boolean" => BooleanType + case "byte" => ByteType + case "char" => CharType + case "short" => ShortType + case "int" => IntegerType + case "long" => LongType + case "float" => FloatType + case "double" => DoubleType + case "String" => ObjectType.String + case _ => + if (str.endsWith("[]")) + ArrayType(stringToFieldType(str.substring(0, str.length - 2))) + else + ObjectType(str.replace(".", "/")) + } + } + + /** + * Converts the type string in Soot's format to the OPAL ObjectType, including void. + * + * @param str type string + * @return ObjectType + */ + def stringToType(str: String): Type = { + str match { + case "void" => VoidType + case _ => stringToFieldType(str) + } + } +} + +/** + * Represents the summary of a single method. + * + * @param returnType return type + * @param methodName method name + * @param paramTypes parameter types + * @param methodNode 'method' XML node + */ +case class MethodSummary(returnType: Type, methodName: String, paramTypes: List[FieldType], methodNode: Node) { + def flows: Seq[Flow] = methodNode.head.map(flowNode => Flow(flowNode)) + + def compute(call: JavaStatement, callStmt: Call[V], in: TaintFact): Set[TaintFact] = { + flows.flatMap(f => + if (f.matchesFrom(callStmt, in)) + f.createTaint(call, callStmt, in) + else + Set.empty).toSet + } + + override def toString: String = s"${returnType.toString} ${methodName}(${paramTypes.mkString(", ")})" +} + +/** + * Represents one summarized flow. + * + * @param flowNode 'flow' XML node + */ +case class Flow(flowNode: Node) { + // only needed if we'd use a on-demand alias analysis + val isAlias: Boolean = (flowNode \@ "isAlias").equals("true") + // TODO: how important is type checking? + val typeChecking: Boolean = (flowNode \@ "typeChecking").equals("true") + val from: SummaryTaint = nodeToTaint((flowNode \\ "from").head) + val to: SummaryTaint = nodeToTaint((flowNode \\ "to").head) + + /** + * Return true if the taint matches the 'from' rule. + * + * @param callStmt call TAC statement + * @param in fact + * @return Boolean + */ + def matchesFrom(callStmt: Call[V], in: TaintFact): Boolean = { + val isStatic = callStmt.receiverOption.isEmpty + val allParamsWithIndex = callStmt.allParams.zipWithIndex + + from match { + case ParameterSummaryTaint(summaryIndex) => + in match { + case Variable(index) => + JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex + /* Summaries do not know array elements. */ + case ArrayElement(index, _) => + JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex + case InstanceField(index, _, fieldName) => + JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex + case _ => false + } + case ParameterSummaryTaintWithField(summaryIndex, summaryFieldName) => + in match { + case Variable(index) => + JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex + /* Summaries do not know array elements. */ + case ArrayElement(index, _) => + JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex + case InstanceField(index, _, fieldName) => + (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex + && summaryFieldName == fieldName) + case _ => false + } + case BaseObjectSummaryTaint(summaryFieldName) => + in match { + case InstanceField(index, _, fieldName) => + (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == -1 + && summaryFieldName == fieldName) + /* Over-approximation: apply the rule even though if no field is known. */ + case Variable(index) => + JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == -1 + case _ => false + } + case _ => false + } + } + + /** + * Return the resulting set of facts after applying the summary. + * + * @param call call java statement + * @param callStmt call TAC statement + * @param in fact + * @return Out set of facts + */ + def createTaint(call: JavaStatement, callStmt: Call[V], in: TaintFact): Set[TaintFact] = { + to match { + case ParameterSummaryTaint(summaryIndex) => + callStmt.params(summaryIndex).asVar.definedBy.map(i => Variable(i)) + case ParameterSummaryTaintWithField(summaryIndex, fieldName) => + callStmt.params(summaryIndex).asVar.definedBy.map(i => + InstanceField(i, callStmt.declaringClass.mostPreciseObjectType, fieldName)) + case BaseObjectSummaryTaint(fieldName) if callStmt.receiverOption.isDefined => + callStmt.receiverOption.get.asVar.definedBy.map(i => + InstanceField(i, callStmt.declaringClass.mostPreciseObjectType, fieldName)) + case ReturnSummaryTaint() if call.stmt.isAssignment => + Set(Variable(call.index)) + case ReturnSummaryTaintWithField(fieldName) if call.stmt.isAssignment => + Set(InstanceField(call.index, callStmt.declaringClass.mostPreciseObjectType, fieldName)) + case _ => Set.empty + } + } +} + +object Flow { + val firstFieldPattern: Regex = """\[[A-Za-z\.\[\]]*?: [A-Za-z\.\[\]]*? ([a-zA-z]*?)[,\]]""".r + + private def getFieldNameFromAttribute(attr: String): String = { + firstFieldPattern.findFirstMatchIn(attr) match { + case Some(m) => m.group(1) + case None => throw new RuntimeException("Failed to parse Access Path: "+attr) + } + } + + /** + * Maps a 'from' or 'to' XML node to an SummaryTaint + * + * @param node 'from' or 'to' node + * @return SummaryTaint + */ + def nodeToTaint(node: Node): SummaryTaint = { + (node \@ "sourceSinkType", node \@ "AccessPath") match { + case ("Parameter", "") => + ParameterSummaryTaint((node \@ "ParameterIndex").toInt - 2) + case ("Parameter", attr) => + ParameterSummaryTaintWithField( + (node \@ "ParameterIndex").toInt - 2, + getFieldNameFromAttribute(attr) + ) + case ("Field", attr) => BaseObjectSummaryTaint(getFieldNameFromAttribute(attr)) + case ("Return", "") => ReturnSummaryTaint() + case ("Return", attr) => ReturnSummaryTaintWithField(getFieldNameFromAttribute(attr)) + } + } +} + +trait SummaryTaint +case class ParameterSummaryTaint(index: Int) extends SummaryTaint +case class ParameterSummaryTaintWithField(index: Int, fieldName: String) extends SummaryTaint +case class BaseObjectSummaryTaint(fieldName: String) extends SummaryTaint +case class ReturnSummaryTaint() extends SummaryTaint +case class ReturnSummaryTaintWithField(fieldName: String) extends SummaryTaint diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala index e1d28b25ac..1a713effdb 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -20,6 +20,8 @@ class ForwardTaintAnalysisFixture(project: SomeProject) extends IFDSAnalysis()(project, new ForwardTaintProblemFixture(project), Taint) class ForwardTaintProblemFixture(p: SomeProject) extends ForwardTaintProblem(p) { + override def useSummaries = true + /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ diff --git a/build.sbt b/build.sbt index 03bd8a887e..e9dd66d910 100644 --- a/build.sbt +++ b/build.sbt @@ -168,6 +168,7 @@ lazy val `OPAL` = (project in file(".")) de, av, ll, + js, framework, // bp, (just temporarily...) tools, @@ -362,6 +363,26 @@ lazy val `LLVM` = (project in file("OPAL/ll")) .dependsOn(tac % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) +lazy val js = `JavaScript` +lazy val `JavaScript` = (project in file("OPAL/js")) + .settings(buildSettings: _*) + .settings( + name := "JavaScript", + Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - JS"), + fork := true, + libraryDependencies ++= Seq("com.ibm.wala" % "com.ibm.wala.core" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.util" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.shrike" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast.java" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast.java.ecj" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast.js" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast.js.rhino" % "1.5.7", + "org.mozilla" % "rhino" % "1.7.10") + ) + .dependsOn(tac % "it->it;it->test;test->test;compile->compile") + .configs(IntegrationTest) + lazy val framework = `Framework` lazy val `Framework` = (project in file("OPAL/framework")) .settings(buildSettings: _*) @@ -374,7 +395,8 @@ lazy val `Framework` = (project in file("OPAL/framework")) ba % "it->it;it->test;test->test;compile->compile", av % "it->it;it->test;test->test;compile->compile", tac % "it->it;it->test;test->test;compile->compile", - ll % "it->it;it->test;test->test;compile->compile" + ll % "it->it;it->test;test->test;compile->compile", + js % "it->it;it->test;test->test;compile->compile" ) .configs(IntegrationTest) From d10d586dd938dca7311e498b249f3396f5001ed7 Mon Sep 17 00:00:00 2001 From: Tim Lange Date: Fri, 30 Sep 2022 01:09:29 +0200 Subject: [PATCH 58/67] Add TODOs marking 'things left open' in the code --- .../org/opalj/js/JavaScriptAnalysisCaller.scala | 4 ++++ .../opalj/js/JavaScriptAwareTaintProblem.scala | 15 ++------------- .../scala/org/opalj/js/JavaScriptSource.scala | 1 + .../scala/org/opalj/js/LocalJSSourceFinder.scala | 2 ++ 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala index f54269389a..73f52448ea 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala @@ -39,6 +39,8 @@ class JavaScriptAnalysisCaller(p: SomeProject) { * @return set of taints */ def analyze(stmt: JavaStatement, in: JSFact): Set[TaintFact] = { + // TODO: pool queries + val sourceFiles = sourceFinder(stmt) in match { case b: BindingFact => sourceFiles.flatMap(s => analyzeScript(s, b)) @@ -56,6 +58,8 @@ class JavaScriptAnalysisCaller(p: SomeProject) { * @return set of taints */ def analyze(stmt: JavaStatement, in: ArrayElement, fName: String): Set[TaintFact] = { + // TODO: pool queries + val sourceFiles = sourceFinder(stmt) sourceFiles.flatMap(s => analyzeFunction(s, in, fName, stmt.index)) + in } diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala index 25aa88bc59..e487734036 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala @@ -6,7 +6,7 @@ import org.opalj.br.analyses.SomeProject import org.opalj.collection.immutable.IntTrieSet import org.opalj.ifds.Dependees.Getter import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.{NO_MATCH, V} -import org.opalj.tac.{AITACode, Assignment, Call, ComputeTACAIKey, Expr, ReturnValue, Stmt, TACMethodParameter, TACode} +import org.opalj.tac.{AITACode, Assignment, Call, ComputeTACAIKey, Expr, ReturnValue, TACMethodParameter, TACode} import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, FlowFact, ForwardTaintProblem, TaintFact, TaintNullFact, Variable} import org.opalj.value.ValueInformation @@ -170,18 +170,6 @@ class JavaScriptAwareTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) } } - /** - * Finds all definition/use sites inside the method. - * - * @param method method to be searched in - * @param sites definition or use sites - * @return sites as JavaStatement - */ - def searchStmts(method: Method, sites: IntTrieSet): Set[Stmt[JavaIFDSProblem.V]] = { - val taCode = tacaiKey(method) - sites.map(site => taCode.stmts.apply(site)) - } - /** * Returns all possible constant strings. Contains the empty string if at least one was non-constant. * @@ -192,6 +180,7 @@ class JavaScriptAwareTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) private def getPossibleStrings(method: Method, defSites: IntTrieSet): Set[String] = { val taCode = tacaiKey(method) + // TODO: use string analysis here defSites.map(site => taCode.stmts.apply(site)).map { case a: Assignment[JavaIFDSProblem.V] if a.expr.isStringConst => a.expr.asStringConst.value diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala index e4859448e3..128e6a1c48 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala @@ -37,6 +37,7 @@ case class JavaScriptStringSource(source: String) extends JavaScriptSource { } } +// TODO: Implement a way to read source files from disk and uncomment lines in LocalJSSourceFinder case class JavaScriptFileSource(path: String) extends JavaScriptSource { override def asString: String = throw new RuntimeException("Not implemented!") diff --git a/OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala b/OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala index c0bc8fada8..d50abb7ed8 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala +++ b/OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala @@ -127,6 +127,8 @@ class LocalJSSourceFinder(val p: SomeProject) extends (JavaStatement => Set[Java private def varToJavaScriptSource(method: Method, variable: JavaIFDSProblem.V): Set[JavaScriptSource] = { val resultSet: mutable.Set[JavaScriptSource] = mutable.Set() + // TODO: use a string analysis instead of 'Assignment with a.expr.isStringConst' here + def findFileArg(sites: IntTrieSet): Unit = { val calls = findCallOnObject(method, sites, "") calls.foreach(init => { From 05a0ebebd967005b6d68a1fa98424e894ef2dd01 Mon Sep 17 00:00:00 2001 From: roterEmil Date: Fri, 21 Oct 2022 14:18:34 +0200 Subject: [PATCH 59/67] Introducing a taint demo and make it work --- .../org/opalj/fpcf/analyses/TaintDemo.scala | 47 +++++++++++++++ .../HerosBackwardClassForNameAnalysis.scala | 2 +- .../HerosForwardClassForNameAnalysis.scala | 1 + .../analyses/taint/HerosTaintAnalysis.scala | 2 +- ...rdClassForNameTaintAnalysisScheduler.scala | 4 +- ...rdClassForNameTaintAnalysisScheduler.scala | 2 +- .../js/IFDSAnalysisFixtureScheduler.scala | 21 +++++++ .../src/main/scala/org/opalj/js/JSFact.scala | 2 +- .../opalj/js/JavaScriptAnalysisCaller.scala | 2 +- .../JavaScriptAwareTaintAnalysisFixture.scala | 24 +++----- .../js/JavaScriptAwareTaintProblem.scala | 3 +- .../ifds/taint/JavaForwardTaintAnalysis.scala | 2 +- .../taint/NativeForwardTaintProblem.scala | 7 +-- .../ifds/old/taint/BackwardTaintProblem.scala | 2 +- .../ifds/old/taint/ForwardTaintProblem.scala | 1 + .../ifds/taint/ForwardTaintProblem.scala | 1 + .../analyses/ifds/taint/TaintProblem.scala | 50 ---------------- .../ifds/taint/summaries/TaintSummaries.scala | 2 +- .../opalj/tac/fpcf/properties/OldTaint.scala | 2 +- .../org/opalj/tac/fpcf/properties/Taint.scala | 2 +- .../opalj/tac/fpcf/properties/TaintFact.scala | 59 +++++++++++++++++++ .../taint/ForwardTaintAnalysisFixture.scala | 2 +- .../old/BackwardTaintAnalysisFixture.scala | 2 +- .../old/ForwardTaintAnalysisFixture.scala | 2 +- build.sbt | 28 +++++---- project/Dependencies.scala | 3 + 26 files changed, 176 insertions(+), 99 deletions(-) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala create mode 100644 OPAL/js/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala rename OPAL/js/src/{test/scala/org/opal => main/scala/org/opalj}/js/JavaScriptAwareTaintAnalysisFixture.scala (67%) create mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala new file mode 100644 index 0000000000..987632d570 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala @@ -0,0 +1,47 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.fpcf.analyses + +import org.opalj.ai.domain +import org.opalj.ai.fpcf.properties.AIDomainFactoryKey +import org.opalj.br.analyses.{BasicReport, Project, ProjectAnalysisApplication} +import org.opalj.br.fpcf.FPCFAnalysesManagerKey +import org.opalj.fpcf.PropertyStore +import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.fpcf.properties.Taint +import org.opalj.js.IFDSAnalysisJSFixtureScheduler + +import java.net.URL + +object TaintDemo extends ProjectAnalysisApplication { + + override def title: String = "..." + + override def description: String = "..." + + override def doAnalyze( + project: Project[URL], + parameters: Seq[String], + isInterrupted: () => Boolean + ): BasicReport = { + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + var propertyStore: PropertyStore = null + val analysesManager = project.get(FPCFAnalysesManagerKey) + project.get(RTACallGraphKey) + + project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => + Set[Class[_ <: AnyRef]](classOf[domain.l2.DefaultPerformInvocationsDomainWithCFG[URL]]) + } + + propertyStore = analysesManager.runAll( + IFDSAnalysisJSFixtureScheduler + )._1 + propertyStore.waitOnPhaseCompletion(); + //Result: + propertyStore.entities(Taint.key).mkString("\n") + } +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala index 8056a692f4..91bd1d6c57 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosBackwardClassForNameAnalysis.scala @@ -33,7 +33,7 @@ import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysisRunner import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, TaintFact, FlowFact, InstanceField, StaticField, Variable} +import org.opalj.tac.fpcf.properties._ /** * An implementation of the BackwardClassForNameAnalysis in the Heros framework. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala index ccd4256872..8a01d44ad9 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosForwardClassForNameAnalysis.scala @@ -13,6 +13,7 @@ import org.opalj.tac.fpcf.analyses.heros.cfg.OpalForwardICFG import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.taint._ import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties._ import org.opalj.tac.fpcf.properties.cg.Callers import org.opalj.util.Milliseconds diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala index 2d67edfef5..43de79fdb0 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/heros/analyses/taint/HerosTaintAnalysis.scala @@ -4,7 +4,7 @@ package org.opalj.tac.fpcf.analyses.heros.analyses.taint import org.opalj.br.analyses.SomeProject import org.opalj.tac.fpcf.analyses.heros.analyses.HerosAnalysis import org.opalj.tac.fpcf.analyses.heros.cfg.OpalICFG -import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, TaintNullFact} +import org.opalj.tac.fpcf.properties._ /** * A common subclass of all Heros taint analyses. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala index 866d4a88d1..5e4f40c3cc 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/BackwardClassForNameTaintAnalysisScheduler.scala @@ -9,8 +9,8 @@ import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.JavaMethod import org.opalj.tac.fpcf.analyses.ifds.old.taint.BackwardTaintProblem import org.opalj.tac.fpcf.analyses.ifds.old._ -import org.opalj.tac.fpcf.analyses.ifds.taint._ -import org.opalj.tac.fpcf.properties.OldTaint +import org.opalj.tac.fpcf.properties._ +import org.opalj.tac.fpcf.properties.{FlowFact, OldTaint} import java.io.File diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala index afd177d23e..bca0428035 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/taint/ForwardClassForNameTaintAnalysisScheduler.scala @@ -8,7 +8,7 @@ import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.old._ -import org.opalj.tac.fpcf.analyses.ifds.taint.{FlowFact, TaintFact, Variable} +import org.opalj.tac.fpcf.properties._ import org.opalj.tac.fpcf.analyses.ifds._ import org.opalj.tac.fpcf.properties.OldTaint import java.io.File diff --git a/OPAL/js/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala b/OPAL/js/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala new file mode 100644 index 0000000000..bd3cc0a96d --- /dev/null +++ b/OPAL/js/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala @@ -0,0 +1,21 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org +package opalj +package js + +import org.opal.js.JavaScriptAwareTaintAnalysisFixture +import org.opalj.br.Method +import org.opalj.br.analyses.{DeclaredMethodsKey, ProjectInformationKeys, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.ifds.{IFDSAnalysisScheduler, IFDSPropertyMetaInformation} +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.properties.{Taint, TaintFact} + +object IFDSAnalysisJSFixtureScheduler extends IFDSAnalysisScheduler[TaintFact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new JavaScriptAwareTaintAnalysisFixture(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint + override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(Taint)) + override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) +} \ No newline at end of file diff --git a/OPAL/js/src/main/scala/org/opalj/js/JSFact.scala b/OPAL/js/src/main/scala/org/opalj/js/JSFact.scala index 42c4c32915..2b54b893ae 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/JSFact.scala +++ b/OPAL/js/src/main/scala/org/opalj/js/JSFact.scala @@ -1,7 +1,7 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.js -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact +import org.opalj.tac.fpcf.properties.TaintFact /* Common trait for facts for ScriptEngines. */ trait JSFact extends TaintFact diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala index 73f52448ea..4519d7fe51 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala @@ -14,7 +14,7 @@ import com.ibm.wala.util.intset.{IntIterator, IntSet, IntSetUtil, MutableIntSet} import org.opalj.br.analyses.SomeProject import org.opalj.js.wala_ifds.WalaJavaScriptIFDSTaintAnalysis import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, TaintFact, Variable} +import org.opalj.tac.fpcf.properties._ import org.mozilla.javascript.{Parser, Token} import org.mozilla.javascript.ast.{AstNode, AstRoot, FunctionNode, NodeVisitor, Symbol} diff --git a/OPAL/js/src/test/scala/org/opal/js/JavaScriptAwareTaintAnalysisFixture.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala similarity index 67% rename from OPAL/js/src/test/scala/org/opal/js/JavaScriptAwareTaintAnalysisFixture.scala rename to OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala index 2f2e5ed19a..bb659f993b 100644 --- a/OPAL/js/src/test/scala/org/opal/js/JavaScriptAwareTaintAnalysisFixture.scala +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala @@ -1,16 +1,15 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opal.js +package org +package opal +package js import org.opalj.br.Method -import org.opalj.br.analyses.{DeclaredMethodsKey, ProjectInformationKeys, SomeProject} -import org.opalj.br.fpcf.PropertyStoreKey -import org.opalj.fpcf.{PropertyBounds, PropertyStore} -import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSPropertyMetaInformation} +import org.opalj.br.analyses.SomeProject +import org.opalj.ifds.IFDSAnalysis import org.opalj.js.JavaScriptAwareTaintProblem -import org.opalj.tac.cg.TypeProviderKey -import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, TaintNullFact, Variable} -import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} -import org.opalj.tac.fpcf.properties.Taint +import org.opalj.tac.fpcf.analyses.ifds.JavaMethod +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.properties.{FlowFact, Taint, TaintFact, TaintNullFact, Variable} /** * An analysis that checks, if the return value of the method `source` can flow to the parameter of @@ -62,9 +61,4 @@ class JavaScriptAwareTaintProblemProblemFixture(p: SomeProject) extends JavaScri else None } -object IFDSAnalysisJSFixtureScheduler extends IFDSAnalysisScheduler[TaintFact, Method, JavaStatement] { - override def init(p: SomeProject, ps: PropertyStore) = new JavaScriptAwareTaintAnalysisFixture(p) - override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint - override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(Taint)) - override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) -} + diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala index e487734036..a7abbd03e6 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala @@ -6,9 +6,10 @@ import org.opalj.br.analyses.SomeProject import org.opalj.collection.immutable.IntTrieSet import org.opalj.ifds.Dependees.Getter import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.{NO_MATCH, V} +import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintProblem import org.opalj.tac.{AITACode, Assignment, Call, ComputeTACAIKey, Expr, ReturnValue, TACMethodParameter, TACode} import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} -import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, FlowFact, ForwardTaintProblem, TaintFact, TaintNullFact, Variable} +import org.opalj.tac.fpcf.properties._ import org.opalj.value.ValueInformation import scala.annotation.nowarn diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala index aae50c1dd9..9481f866f5 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/JavaForwardTaintAnalysis.scala @@ -13,7 +13,7 @@ import org.opalj.ll.llvm.value.Ret import org.opalj.tac.Assignment import org.opalj.tac.fpcf.analyses.ifds.taint._ import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} -import org.opalj.tac.fpcf.properties.{TACAI, Taint} +import org.opalj.tac.fpcf.properties._ class SimpleJavaForwardTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { val llvmProject = p.get(LLVMProjectKey) diff --git a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala index c1da0d0207..199ecf1947 100644 --- a/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala +++ b/OPAL/ll/src/main/scala/org/opalj/ll/fpcf/analyses/ifds/taint/NativeForwardTaintProblem.scala @@ -8,12 +8,9 @@ import org.opalj.ll.fpcf.analyses.ifds.JNIMethod import org.opalj.ll.llvm.value.{Add, Alloca, BitCast, Call, GetElementPtr, Load, PHI, Ret, Store, Sub} import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, TaintProblem} +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem -import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact -import org.opalj.tac.fpcf.analyses.ifds.taint.StaticField -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact -import org.opalj.tac.fpcf.analyses.ifds.taint.Variable +import org.opalj.tac.fpcf.properties._ import org.opalj.tac.ReturnValue abstract class NativeForwardTaintProblem(project: SomeProject) extends NativeIFDSProblem[NativeTaintFact, TaintFact](project) with TaintProblem[NativeFunction, LLVMStatement, NativeTaintFact] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala index c92d93978b..6a86552b1a 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/BackwardTaintProblem.scala @@ -10,7 +10,7 @@ import org.opalj.tac.fpcf.analyses.ifds.old.{JavaBackwardIFDSProblem, DeclaredMe import org.opalj.tac.fpcf.analyses.ifds.taint._ import org.opalj.tac.fpcf.analyses.ifds.old.UnbalancedReturnFact -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact +import org.opalj.tac.fpcf.properties._ /** * The unbalanced return fact of this analysis. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala index 6561101b87..9039effbd7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/old/taint/ForwardTaintProblem.scala @@ -8,6 +8,7 @@ import org.opalj.tac.fpcf.analyses.ifds.JavaMethod import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.old.{JavaIFDSProblem, DeclaredMethodJavaStatement} import org.opalj.tac.fpcf.analyses.ifds.taint._ +import org.opalj.tac.fpcf.properties._ import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem => NewJavaIFDSProblem} abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[TaintFact](project) with TaintProblem[DeclaredMethod, DeclaredMethodJavaStatement, TaintFact] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index f3a1eba931..b078fc58a5 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -8,6 +8,7 @@ import org.opalj.tac._ import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.TaintSummaries import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties._ import java.io.File diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index bc76a555e3..a3082eb64d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -1,60 +1,10 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.ifds.taint -import org.opalj.br.ObjectType -import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} import org.opalj.tac.{Assignment, Expr, Stmt} import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -trait TaintFact extends AbstractIFDSFact -case object TaintNullFact extends TaintFact with AbstractIFDSNullFact - -/** - * A tainted variable. - * - * @param index The variable's definition site. - */ -case class Variable(index: Int) extends TaintFact - -/** - * A tainted array element. - * - * @param index The array's definition site. - * @param element The index of the tainted element in the array. - */ -case class ArrayElement(index: Int, element: Int) extends TaintFact - -/** - * A tainted static field. - * - * @param classType The field's class. - * @param fieldName The field's name. - */ -case class StaticField(classType: ObjectType, fieldName: String) extends TaintFact - -/** - * A tainted instance field. - * - * @param index The definition site of the field's value. - * @param classType The field's type. - * @param fieldName The field's value. - */ -case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends TaintFact - -/** - * A path of method calls, originating from the analyzed method, over which a tainted variable - * reaches the sink. - * - * @param flow A sequence of method calls, originating from but not including this method. - */ -case class FlowFact(flow: Seq[Callable]) extends TaintFact { - override val hashCode: Int = { - var r = 1 - flow.foreach(f => r = (r + f.hashCode()) * 31) - r - } -} trait TaintProblem[C, Statement, IFDSFact] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala index 344d6cb09d..6d59e6f64e 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala @@ -5,7 +5,7 @@ import org.opalj.br.{ArrayType, BooleanType, ByteType, CharType, DoubleType, Fie import org.opalj.tac.Call import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaStatement} -import org.opalj.tac.fpcf.analyses.ifds.taint.{ArrayElement, InstanceField, TaintFact, TaintNullFact, Variable} +import org.opalj.tac.fpcf.properties._ import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.Flow.nodeToTaint import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.ClassSummary.{signaturePattern, stringToFieldType, stringToType} diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala index 2c7fca2faa..44501603d3 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/OldTaint.scala @@ -4,7 +4,7 @@ package org.opalj.tac.fpcf.properties import org.opalj.fpcf.PropertyKey import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.analyses.ifds.old.DeclaredMethodJavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact +import org.opalj.tac.fpcf.properties._ case class OldTaint(flows: Map[DeclaredMethodJavaStatement, Set[TaintFact]], debugData: Map[DeclaredMethodJavaStatement, Set[TaintFact]] = Map.empty) extends IFDSProperty[DeclaredMethodJavaStatement, TaintFact] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala index 6ac49d70eb..efe40947c0 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/Taint.scala @@ -4,7 +4,7 @@ package org.opalj.tac.fpcf.properties import org.opalj.fpcf.PropertyKey import org.opalj.ifds.{IFDSProperty, IFDSPropertyMetaInformation} import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintFact +import org.opalj.tac.fpcf.properties._ case class Taint(flows: Map[JavaStatement, Set[TaintFact]], debugData: Map[JavaStatement, Set[TaintFact]] = Map.empty) extends IFDSProperty[JavaStatement, TaintFact] { diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala new file mode 100644 index 0000000000..b2f7943641 --- /dev/null +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala @@ -0,0 +1,59 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org +package opalj +package tac +package fpcf +package properties + +import org.opalj.br.ObjectType +import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} + +trait TaintFact extends AbstractIFDSFact + +case object TaintNullFact extends TaintFact with AbstractIFDSNullFact + +/** + * A tainted variable. + * + * @param index The variable's definition site. + */ +case class Variable(index: Int) extends TaintFact + +/** + * A tainted array element. + * + * @param index The array's definition site. + * @param element The index of the tainted element in the array. + */ +case class ArrayElement(index: Int, element: Int) extends TaintFact + +/** + * A tainted static field. + * + * @param classType The field's class. + * @param fieldName The field's name. + */ +case class StaticField(classType: ObjectType, fieldName: String) extends TaintFact + +/** + * A tainted instance field. + * + * @param index The definition site of the field's value. + * @param classType The field's type. + * @param fieldName The field's value. + */ +case class InstanceField(index: Int, classType: ObjectType, fieldName: String) extends TaintFact + +/** + * A path of method calls, originating from the analyzed method, over which a tainted variable + * reaches the sink. + * + * @param flow A sequence of method calls, originating from but not including this method. + */ +case class FlowFact(flow: Seq[Callable]) extends TaintFact { + override val hashCode: Int = { + var r = 1 + flow.foreach(f => r = (r + f.hashCode()) * 31) + r + } +} \ No newline at end of file diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala index 1a713effdb..bdad59c511 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintAnalysisFixture.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.{PropertyBounds, PropertyStore} import org.opalj.ifds.{IFDSAnalysis, IFDSAnalysisScheduler, IFDSPropertyMetaInformation} import org.opalj.tac.cg.TypeProviderKey import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} -import org.opalj.tac.fpcf.properties.Taint +import org.opalj.tac.fpcf.properties._ /** * An analysis that checks, if the return value of the method `source` can flow to the parameter of diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala index b3108ad321..8530e625c3 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/BackwardTaintAnalysisFixture.scala @@ -8,7 +8,7 @@ import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ifds._ import org.opalj.tac.fpcf.analyses.ifds.old._ import org.opalj.tac.fpcf.analyses.ifds.old.taint.BackwardTaintProblem -import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, Variable} +import org.opalj.tac.fpcf.properties._ import org.opalj.tac.fpcf.properties.OldTaint import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem diff --git a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala index 87e7068157..064be535f3 100644 --- a/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala +++ b/OPAL/tac/src/test/scala/org/opalj/tac/fpcf/analyses/ifds/taint/old/ForwardTaintAnalysisFixture.scala @@ -8,7 +8,7 @@ import org.opalj.ifds.IFDSPropertyMetaInformation import org.opalj.tac.fpcf.analyses.ifds.JavaMethod import org.opalj.tac.fpcf.analyses.ifds.old.{DeclaredMethodJavaStatement, ForwardIFDSAnalysis, IFDSAnalysisScheduler} import org.opalj.tac.fpcf.analyses.ifds.old.taint.ForwardTaintProblem -import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact, TaintNullFact, Variable} +import org.opalj.tac.fpcf.properties._ import org.opalj.tac.fpcf.properties.OldTaint /** diff --git a/build.sbt b/build.sbt index e9dd66d910..82ae2e931e 100644 --- a/build.sbt +++ b/build.sbt @@ -144,10 +144,11 @@ def getScalariformPreferences(dir: File) = { ******************************************************************************/ lazy val opal = `OPAL` lazy val `OPAL` = (project in file(".")) -// .configure(_.copy(id = "OPAL")) + // .configure(_.copy(id = "OPAL")) .settings((Defaults.coreDefaultSettings ++ Seq(publishArtifact := false)): _*) .enablePlugins(ScalaUnidocPlugin) .settings( + javaCppVersion := Dependencies.javaCppVersion, ScalaUnidoc / unidoc / unidocProjectFilter := inAnyProject -- inProjects( hermes, validate, @@ -187,6 +188,7 @@ lazy val `Common` = (project in file("OPAL/common")) .settings(buildSettings: _*) .settings( name := "Common", + javaCppVersion := Dependencies.javaCppVersion, Compile / doc / scalacOptions := Opts.doc.title("OPAL-Common"), libraryDependencies ++= Dependencies.common(scalaVersion.value) ) @@ -245,7 +247,7 @@ lazy val `BytecodeRepresentation` = (project in file("OPAL/br")) Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - Bytecode Representation"), libraryDependencies ++= Dependencies.br, // Test / publishArtifact := true // Needed to get access to class TestResources and TestSupport - ) + ) .dependsOn(si % "it->it;it->test;test->test;compile->compile") .dependsOn(bi % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) @@ -357,9 +359,9 @@ lazy val `LLVM` = (project in file("OPAL/ll")) name := "LLVM", Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - LLVM"), fork := true, - javaCppPresetLibs ++= Seq("llvm" -> "11.1.0"), - javaCppVersion := "1.5.5" -) + javaCppPresetLibs ++= Dependencies.javaCppPresetLibs, + javaCppVersion := Dependencies.javaCppVersion + ) .dependsOn(tac % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) @@ -371,14 +373,14 @@ lazy val `JavaScript` = (project in file("OPAL/js")) Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - JS"), fork := true, libraryDependencies ++= Seq("com.ibm.wala" % "com.ibm.wala.core" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.util" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.shrike" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast.java" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast.java.ecj" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast.js" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast.js.rhino" % "1.5.7", - "org.mozilla" % "rhino" % "1.7.10") + "com.ibm.wala" % "com.ibm.wala.util" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.shrike" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast.java" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast.java.ecj" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast.js" % "1.5.7", + "com.ibm.wala" % "com.ibm.wala.cast.js.rhino" % "1.5.7", + "org.mozilla" % "rhino" % "1.7.10") ) .dependsOn(tac % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 52374eb5e8..c5ff55be67 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -73,4 +73,7 @@ object Dependencies { val tools = Seq(txtmark, jacksonDF) val hermes = Seq(txtmark, jacksonDF, javafxBase) + val javaCppVersion = "1.5.7" + val javaCppPresetLibs = Seq("llvm" -> "13.0.1") + } From 0848636a3c7565a4129c6bbb0d577b4f10b6b850 Mon Sep 17 00:00:00 2001 From: roterEmil Date: Fri, 21 Oct 2022 15:01:19 +0200 Subject: [PATCH 60/67] renaming js -> sql and other little improvements --- .../ForwardTaintAnalysisFixtureTest.scala | 2 +- .../BackwardTaintAnalysisFixtureTest.scala | 2 +- .../old/ForwardTaintAnalysisFixtureTest.scala | 2 +- .../taint/BackwardFlowPathMatcher.scala | 2 +- .../taint/ForwardFlowPathMatcher.scala | 2 +- ...vaScriptAwareTaintProblemFixtureTest.scala | 4 +- ...rossLanguageForwardTaintAnalysisTest.scala | 2 +- .../java/org/opalj/js/wala_ifds/JSDomain.java | 19 -- .../opalj/js/wala_ifds/JSFlowFunctions.java | 123 -------- .../org/opalj/js/wala_ifds/JSProblem.java | 101 ------ .../WalaJavaScriptIFDSTaintAnalysis.java | 60 ---- .../opalj/js/JavaScriptAnalysisCaller.scala | 298 ------------------ OPAL/{js => sql}/Readme.md | 0 OPAL/{js => sql}/build.sbt | 0 .../js/IFDSAnalysisFixtureScheduler.scala | 8 +- .../src/main/scala/org/opalj/js/JSFact.scala | 0 .../JavaScriptAwareTaintAnalysisFixture.scala | 12 +- .../js/JavaScriptAwareTaintProblem.scala | 37 +-- .../scala/org/opalj/js/JavaScriptSource.scala | 0 .../org/opalj/js/LocalJSSourceFinder.scala | 0 .../analyses/ifds/taint/TaintProblem.scala | 2 - .../opalj/tac/fpcf/properties/TaintFact.scala | 10 +- build.sbt | 23 +- 23 files changed, 47 insertions(+), 662 deletions(-) delete mode 100644 OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSDomain.java delete mode 100644 OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSFlowFunctions.java delete mode 100644 OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSProblem.java delete mode 100644 OPAL/js/src/main/java/org/opalj/js/wala_ifds/WalaJavaScriptIFDSTaintAnalysis.java delete mode 100644 OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala rename OPAL/{js => sql}/Readme.md (100%) rename OPAL/{js => sql}/build.sbt (100%) rename OPAL/{js => sql}/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala (63%) rename OPAL/{js => sql}/src/main/scala/org/opalj/js/JSFact.scala (100%) rename OPAL/{js => sql}/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala (87%) rename OPAL/{js => sql}/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala (93%) rename OPAL/{js => sql}/src/main/scala/org/opalj/js/JavaScriptSource.scala (100%) rename OPAL/{js => sql}/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala (100%) diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala index 46994ed671..51a3163dcd 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/ForwardTaintAnalysisFixtureTest.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.PropertiesTest import org.opalj.fpcf.properties.taint.ForwardFlowPath import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintAnalysisFixtureScheduler -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact +import org.opalj.tac.fpcf.properties.TaintNullFact import java.net.URL diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala index d6d01df09e..7f16a90125 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/BackwardTaintAnalysisFixtureTest.scala @@ -7,7 +7,7 @@ import org.opalj.br.analyses.{DeclaredMethodsKey, Project} import org.opalj.fpcf.PropertiesTest import org.opalj.fpcf.properties.taint.BackwardFlowPath import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact +import org.opalj.tac.fpcf.properties.TaintNullFact import org.opalj.tac.fpcf.analyses.ifds.taint.old.BackwardTaintAnalysisFixtureScheduler import java.net.URL diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala index 470cb0092f..df39091498 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/ifds/old/ForwardTaintAnalysisFixtureTest.scala @@ -8,7 +8,7 @@ import org.opalj.fpcf.PropertiesTest import org.opalj.fpcf.properties.taint.ForwardFlowPath import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.taint.old.ForwardTaintAnalysisFixtureScheduler -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact +import org.opalj.tac.fpcf.properties.TaintNullFact import java.net.URL diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala index e5dce50d5b..7a2266b0e6 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/BackwardFlowPathMatcher.scala @@ -7,7 +7,7 @@ import org.opalj.br.fpcf.PropertyStoreKey import org.opalj.fpcf.properties.AbstractPropertyMatcher import org.opalj.fpcf.{EPS, Entity, FinalEP, Property} import org.opalj.tac.fpcf.analyses.ifds.taint.old.BackwardTaintAnalysisFixtureScheduler -import org.opalj.tac.fpcf.analyses.ifds.taint.{TaintFact, FlowFact} +import org.opalj.tac.fpcf.properties._ import org.opalj.tac.fpcf.properties.OldTaint /** diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala index 9adb47944c..8e59387efd 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/fpcf/properties/taint/ForwardFlowPathMatcher.scala @@ -6,7 +6,7 @@ import org.opalj.br.{AnnotationLike, ElementValue, ElementValuePair, ObjectType} import org.opalj.fpcf.properties.AbstractPropertyMatcher import org.opalj.fpcf.{Entity, Property} import org.opalj.ifds.IFDSProperty -import org.opalj.tac.fpcf.analyses.ifds.taint.FlowFact +import org.opalj.tac.fpcf.properties._ /** * @author Mario Trageser diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/js/JavaScriptAwareTaintProblemFixtureTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/js/JavaScriptAwareTaintProblemFixtureTest.scala index ec7d1c796b..92850fa94b 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/js/JavaScriptAwareTaintProblemFixtureTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/js/JavaScriptAwareTaintProblemFixtureTest.scala @@ -1,14 +1,14 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.js -import org.opal.js.IFDSAnalysisJSFixtureScheduler +import org.opalj.js.IFDSAnalysisJSFixtureScheduler import org.opalj.fpcf.PropertiesTest import org.opalj.ai.domain.l2 import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.Project import org.opalj.fpcf.properties.taint.ForwardFlowPath import org.opalj.tac.cg.RTACallGraphKey -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintNullFact +import org.opalj.tac.fpcf.properties._ import java.net.URL diff --git a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala index 52bd492d90..cdedaff9cb 100644 --- a/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala +++ b/DEVELOPING_OPAL/validate/src/test/scala/org/opalj/ll/CrossLanguageForwardTaintAnalysisTest.scala @@ -15,7 +15,7 @@ import org.opalj.ll.llvm.value.Function import org.opalj.log.GlobalLogContext import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.analyses.ifds.taint.{FlowFact, TaintFact} +import org.opalj.tac.fpcf.properties._ import org.scalatest.funspec.AnyFunSpec import org.scalatest.matchers.should.Matchers diff --git a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSDomain.java b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSDomain.java deleted file mode 100644 index c192c5534e..0000000000 --- a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSDomain.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.opalj.js.wala_ifds; - -import com.ibm.wala.dataflow.IFDS.PathEdge; -import com.ibm.wala.dataflow.IFDS.TabulationDomain; -import com.ibm.wala.ipa.cfg.BasicBlockInContext; -import com.ibm.wala.ssa.analysis.IExplodedBasicBlock; -import com.ibm.wala.util.collections.Pair; -import com.ibm.wala.util.intset.MutableMapping; - -class JSDomain extends MutableMapping>> - implements TabulationDomain>, BasicBlockInContext> -{ - private static final long serialVersionUID = -1897766113586243833L; - - @Override - public boolean hasPriorityOver(PathEdge> pathEdge, PathEdge> pathEdge1) { - return false; - } -} diff --git a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSFlowFunctions.java b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSFlowFunctions.java deleted file mode 100644 index b9eb8965f1..0000000000 --- a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSFlowFunctions.java +++ /dev/null @@ -1,123 +0,0 @@ -package org.opalj.js.wala_ifds; - -import com.ibm.wala.dataflow.IFDS.*; -import com.ibm.wala.ipa.callgraph.CGNode; -import com.ibm.wala.ipa.cfg.BasicBlockInContext; -import com.ibm.wala.ssa.*; -import com.ibm.wala.ssa.analysis.IExplodedBasicBlock; -import com.ibm.wala.util.collections.Pair; -import com.ibm.wala.util.intset.IntSet; -import com.ibm.wala.util.intset.IntSetUtil; -import com.ibm.wala.util.intset.MutableIntSet; - -class JSFlowFunctions implements IPartiallyBalancedFlowFunctions> { - ISupergraph, CGNode> supergraph; - JSDomain domain; - - public JSFlowFunctions(JSDomain domain, ISupergraph, CGNode> supergraph) - { - this.supergraph = supergraph; - this.domain = domain; - } - - @Override - public IUnaryFlowFunction getNormalFlowFunction(BasicBlockInContext src, - BasicBlockInContext dest) { - final IExplodedBasicBlock ebb = src.getDelegate(); - final IExplodedBasicBlock dbb = dest.getDelegate(); - - return new IUnaryFlowFunction() { - - private void propagate(SSAInstruction inst, Pair> vn, MutableIntSet r) { - boolean propagates = inst instanceof SSAPhiInstruction || inst instanceof SSABinaryOpInstruction || inst instanceof SSAUnaryOpInstruction; - - if (propagates) { - for (int i = 0; i < inst.getNumberOfUses(); i++) { - if (vn.fst == inst.getUse(i)) { - Pair> nvn = Pair.make(inst.getDef(), src); - if (! domain.hasMappedIndex(nvn)) { - domain.add(nvn); - } - r.add(domain.getMappedIndex(nvn)); - } - } - } - } - - @Override - public IntSet getTargets(int d1) { - Pair> vn = domain.getMappedObject(d1); - MutableIntSet r = IntSetUtil.make(new int[] { domain.getMappedIndex(vn) }); - dbb.iteratePhis().forEachRemaining((inst) -> propagate(inst, vn, r)); - propagate(ebb.getInstruction(), vn, r); - - return r; - } - }; - } - - // taint parameters from arguments - @Override - public IUnaryFlowFunction getCallFlowFunction(BasicBlockInContext src, - BasicBlockInContext dest, BasicBlockInContext ret) { - final IExplodedBasicBlock ebb = src.getDelegate(); - SSAInstruction inst = ebb.getInstruction(); - return (d1) -> { - MutableIntSet r = IntSetUtil.make(); - Pair> vn = domain.getMappedObject(d1); - for (int i = 0; i < inst.getNumberOfUses(); i++) { - if (vn.fst == inst.getUse(i)) { - Pair> key = Pair.make(i+1, src); - if (! domain.hasMappedIndex(key)) { - domain.add(key); - } - r.add(domain.getMappedIndex(key)); - } - } - return r; - }; - } - - // pass tainted values back to caller - @Override - public IUnaryFlowFunction getReturnFlowFunction(BasicBlockInContext call, - BasicBlockInContext src, BasicBlockInContext dest) { - int retVal = call.getLastInstruction().getDef(); - return (d1) -> { - Pair> vn = domain.getMappedObject(d1); - MutableIntSet result = IntSetUtil.make(); - supergraph.getPredNodes(src).forEachRemaining(pb -> { - SSAInstruction inst = pb.getDelegate().getInstruction(); - if (inst instanceof SSAReturnInstruction && inst.getUse(0) == vn.fst) { - Pair> key = Pair.make(retVal, pb); - if (! domain.hasMappedIndex(key)) { - domain.add(key); - } - result.add(domain.getMappedIndex(key)); - } - }); - return result; - }; - } - - // local variables are not changed by calls. - @Override - public IUnaryFlowFunction getCallToReturnFlowFunction(BasicBlockInContext src, - BasicBlockInContext dest) { - return IdentityFlowFunction.identity(); - } - - // missing a callee, so assume it does nothing and keep local information. - @Override - public IUnaryFlowFunction getCallNoneToReturnFlowFunction(BasicBlockInContext src, - BasicBlockInContext dest) { - return IdentityFlowFunction.identity(); - } - - // data flow back from an unknown call site, so just keep what comes back. - @Override - public IFlowFunction getUnbalancedReturnFlowFunction(BasicBlockInContext src, - BasicBlockInContext dest) { - return IdentityFlowFunction.identity(); - } -} diff --git a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSProblem.java b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSProblem.java deleted file mode 100644 index 839c992195..0000000000 --- a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/JSProblem.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.opalj.js.wala_ifds; - -import com.ibm.wala.dataflow.IFDS.*; -import com.ibm.wala.ipa.callgraph.CGNode; -import com.ibm.wala.ipa.cfg.BasicBlockInContext; -import com.ibm.wala.ssa.SSAInstruction; -import com.ibm.wala.ssa.analysis.IExplodedBasicBlock; -import com.ibm.wala.util.collections.HashSetFactory; -import com.ibm.wala.util.collections.Pair; - -import java.util.Collection; -import java.util.function.Function; - -class JSProblem implements PartiallyBalancedTabulationProblem, CGNode, Pair>> { - private final ISupergraph, CGNode> supergraph; - private final JSDomain domain; - private final Function, Boolean> sources; - /** path edges corresponding to taint sources */ - private final Collection>> initialSeeds; - - private final JSFlowFunctions flowFunctions; - - public JSProblem(JSDomain domain, ISupergraph, CGNode> supergraph, Function, Boolean> sources) - { - this.supergraph = supergraph; - this.domain = domain; - this.sources = sources; - this.initialSeeds = collectInitialSeeds(); - this.flowFunctions = new JSFlowFunctions(domain, supergraph); - } - - /** - * we use the entry block of the CGNode as the fake entry when propagating from - * callee to caller with unbalanced parens - */ - @Override - public BasicBlockInContext getFakeEntry(BasicBlockInContext node) { - final CGNode cgNode = node.getNode(); - return getFakeEntry(cgNode); - } - - /** - * we use the entry block of the CGNode as the "fake" entry when propagating - * from callee to caller with unbalanced parens - */ - private BasicBlockInContext getFakeEntry(final CGNode cgNode) { - BasicBlockInContext[] entriesForProcedure = supergraph.getEntriesForProcedure(cgNode); - assert entriesForProcedure.length == 1; - return entriesForProcedure[0]; - } - - /** - * collect sources of taint - */ - private Collection>> collectInitialSeeds() { - Collection>> result = HashSetFactory.make(); - for (BasicBlockInContext bb : supergraph) { - IExplodedBasicBlock ebb = bb.getDelegate(); - SSAInstruction instruction = ebb.getInstruction(); - - if (sources.apply(bb)) { - Pair> fact = Pair.make(instruction.getDef(), bb); - final CGNode cgNode = bb.getNode(); - int factNum = domain.add(fact); - BasicBlockInContext fakeEntry = getFakeEntry(cgNode); - // note that the fact number used for the source of this path edge doesn't - // really matter - result.add(PathEdge.createPathEdge(fakeEntry, factNum, bb, factNum)); - } - } - return result; - } - - @Override - public IPartiallyBalancedFlowFunctions> getFunctionMap() { - return flowFunctions; - } - - @Override - public JSDomain getDomain() { - return domain; - } - - /** - * we don't need a merge function; the default unioning of tabulation works fine - */ - @Override - public IMergeFunction getMergeFunction() { - return null; - } - - @Override - public ISupergraph, CGNode> getSupergraph() { - return supergraph; - } - - @Override - public Collection>> initialSeeds() { - return initialSeeds; - } -} \ No newline at end of file diff --git a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/WalaJavaScriptIFDSTaintAnalysis.java b/OPAL/js/src/main/java/org/opalj/js/wala_ifds/WalaJavaScriptIFDSTaintAnalysis.java deleted file mode 100644 index ca0d89f56e..0000000000 --- a/OPAL/js/src/main/java/org/opalj/js/wala_ifds/WalaJavaScriptIFDSTaintAnalysis.java +++ /dev/null @@ -1,60 +0,0 @@ -package org.opalj.js.wala_ifds; - -import com.ibm.wala.dataflow.IFDS.ICFGSupergraph; -import com.ibm.wala.dataflow.IFDS.PartiallyBalancedTabulationSolver; -import com.ibm.wala.dataflow.IFDS.TabulationDomain; -import com.ibm.wala.dataflow.IFDS.TabulationResult; -import com.ibm.wala.ipa.callgraph.CGNode; -import com.ibm.wala.ipa.callgraph.CallGraph; -import com.ibm.wala.ipa.cfg.BasicBlockInContext; -import com.ibm.wala.ssa.analysis.IExplodedBasicBlock; -import com.ibm.wala.util.CancelException; -import com.ibm.wala.util.collections.Pair; -import com.ibm.wala.util.intset.IntSet; -import scala.Function3; - -import java.util.function.Function; - -public class WalaJavaScriptIFDSTaintAnalysis { - - private final ICFGSupergraph supergraph; - - private final JSDomain domain; - - private final Function, Boolean> sources; - - public WalaJavaScriptIFDSTaintAnalysis(CallGraph cg, Function, Boolean> sources) - { - supergraph = ICFGSupergraph.make(cg); - this.sources = sources; - this.domain = new JSDomain(); - } - - public TabulationResult, CGNode, Pair>> analyze() { - PartiallyBalancedTabulationSolver, CGNode, Pair>> solver = PartiallyBalancedTabulationSolver - .createPartiallyBalancedTabulationSolver(new JSProblem(domain, supergraph, sources), null); - TabulationResult, CGNode, Pair>> result = null; - try { - result = solver.solve(); - } catch (CancelException e) { - // this shouldn't happen - assert false; - } - return result; - } - - public static void startJSAnalysis(CallGraph CG, Function, Boolean> sources, - Function3, - IntSet, - TabulationDomain>, - BasicBlockInContext>, - Void> sinks) { - WalaJavaScriptIFDSTaintAnalysis A = new WalaJavaScriptIFDSTaintAnalysis(CG, sources); - - TabulationResult, - CGNode, Pair>> R = A.analyze(); - - R.getSupergraphNodesReached().forEach((sbb) -> sinks.apply(sbb, R.getResult(sbb), A.domain)); - } -} diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala deleted file mode 100644 index 4519d7fe51..0000000000 --- a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAnalysisCaller.scala +++ /dev/null @@ -1,298 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.js - -import com.ibm.wala.cast.js.ipa.callgraph.{JSCFABuilder, JSCallGraphUtil} -import com.ibm.wala.cast.js.translator.CAstRhinoTranslatorFactory -import com.ibm.wala.cast.js.util.JSCallGraphBuilderUtil -import com.ibm.wala.dataflow.IFDS.TabulationDomain -import com.ibm.wala.ipa.callgraph.CallGraph -import com.ibm.wala.ipa.cfg.BasicBlockInContext -import com.ibm.wala.ssa.SSAAbstractInvokeInstruction -import com.ibm.wala.ssa.analysis.IExplodedBasicBlock -import com.ibm.wala.util.collections.Pair -import com.ibm.wala.util.intset.{IntIterator, IntSet, IntSetUtil, MutableIntSet} -import org.opalj.br.analyses.SomeProject -import org.opalj.js.wala_ifds.WalaJavaScriptIFDSTaintAnalysis -import org.opalj.tac.fpcf.analyses.ifds.JavaStatement -import org.opalj.tac.fpcf.properties._ -import org.mozilla.javascript.{Parser, Token} -import org.mozilla.javascript.ast.{AstNode, AstRoot, FunctionNode, NodeVisitor, Symbol} - -import java.io.File - -/** - * Class which does convert a call from Java to JavaScript into something that a JavaScript IFDS analysis - * can consume and converts the results back into facts for the Java taint analysis. - * - * @param p Project - */ -class JavaScriptAnalysisCaller(p: SomeProject) { - type Domain = TabulationDomain[Pair[Integer, BasicBlockInContext[IExplodedBasicBlock]], BasicBlockInContext[IExplodedBasicBlock]] - - val sourceFinder = new LocalJSSourceFinder(p) - - /** - * Analyze an evaluation of a JavaScript script. - * - * @param stmt Java Statement - * @param in incoming BindingFact - * @return set of taints - */ - def analyze(stmt: JavaStatement, in: JSFact): Set[TaintFact] = { - // TODO: pool queries - - val sourceFiles = sourceFinder(stmt) - in match { - case b: BindingFact => sourceFiles.flatMap(s => analyzeScript(s, b)) - /* If we don't know what variable is tainted, we have to give up. */ - case b: WildcardBindingFact => Set(b) - } - } - - /** - * Analyze a call to a JavaScript function. - * - * @param stmt Java Statement - * @param in Variable-length parameter array - * @param fName function name - * @return set of taints - */ - def analyze(stmt: JavaStatement, in: ArrayElement, fName: String): Set[TaintFact] = { - // TODO: pool queries - - val sourceFiles = sourceFinder(stmt) - sourceFiles.flatMap(s => analyzeFunction(s, in, fName, stmt.index)) + in - } - - /** - * Return all variable names that are in the top level scope. - * - * @param root root AST node of the script - * @return set of variable names - */ - private def getTopLevelVariableNames(root: AstRoot): Set[String] = { - var nameSet: Set[String] = Set() - root.getSymbols.forEach(symbol => { - if ((symbol.getDeclType == Token.VAR - || symbol.getDeclType == Token.LET - || symbol.getDeclType == Token.CONST) - && symbol.getContainingTable == root) - nameSet += symbol.getName - }) - nameSet - } - - /** - * Return the corresponding function node for the function symbol. - * - * @param func function symbol - * @return function node - */ - private def getFunctionNode(func: Symbol): Option[FunctionNode] = { - /** - * Visitor to find the node of the function in the AST. - */ - class FunctionFinder extends NodeVisitor { - var fnResult: Option[FunctionNode] = None - - override def visit(node: AstNode): Boolean = { - if (fnResult.isEmpty) - node match { - case fn: FunctionNode => - fnResult = Some(fn) - false - case _ => true - } - else - false - } - } - - val fnFinder = new FunctionFinder() - func.getContainingTable.visit(fnFinder) - fnFinder.fnResult - } - - /** - * Map a list of variable names to a list of BindingFacts - * - * @param vars variable names - * @param defSite definition site of the incoming BindingFact - * @return list of BindingFacts - */ - private def varNamesToFacts(vars: List[String], defSite: Integer): Set[TaintFact] = - vars.map(v => if (v == "opal_tainted_return") Variable(defSite) else BindingFact(defSite, v)).toSet - - /** - * Generate a argument list of length n with one tainted argument. - * - * @param n number of arguments - * @param taintedIdx index which should be tainted - * @return argument list as string - */ - private def generateFunctionArgs(n: Int, taintedIdx: Int): String = { - (0 until n).map(i => - if (i == taintedIdx) { - s"opal_tainted_arg" - } else { - s"opal_fill_arg" - }).mkString(", ") - } - - /** - * Prepare and analyze a JavaScript function call. - * - * @param sourceFile JavaScript source - * @param in incoming fact from a variable-length parameter list - * @param fName function name - * @param stmtIndex statement index - * @return set of facts - */ - private def analyzeFunction(sourceFile: JavaScriptSource, in: ArrayElement, - fName: String, stmtIndex: Int): Set[TaintFact] = { - val parser: Parser = new Parser() - - val root: AstRoot = parser.parse(sourceFile.asString, "fileName", 1) - val sTable = root.getSymbolTable - if (!sTable.containsKey(fName)) - return Set() - val fSymbol = sTable.get(fName) - if (fSymbol.getDeclType != Token.FUNCTION) - return Set() - val fNode = getFunctionNode(fSymbol) - if (fNode.isEmpty) - return Set() - - val nameSet: Set[String] = getTopLevelVariableNames(root)+"opal_tainted_return" - val beforeCode = - s"""function opal_source() { - | return \"secret\"; - |} - |function opal_last_stmt(${generateParams(nameSet.size)}) { } - |\n""".stripMargin - val funcCall = s"var opal_tainted_return = $fName(${generateFunctionArgs(fNode.get.getParamCount, in.element)});" - val afterCode = s""" - |var opal_fill_arg = 42; - |var opal_tainted_arg = opal_source(); - |$funcCall - |opal_last_stmt(${nameSet.mkString(", ")});\n""".stripMargin - - val f: File = sourceFile.asFile(beforeCode, afterCode) - analyzeCommon(f, stmtIndex) - } - - /** - * Generate a n-length parameter list. - * - * @param n number of parameters - * @return parameter list as string - */ - private def generateParams(n: Int): String = { - (0 until n).map(i => s"p$i").mkString(", ") - } - - /** - * Prepare and analyze a script evaluation. - * - * @param sourceFile JavaScript source - * @param in incoming BindingFact - * @return set of facts - */ - private def analyzeScript(sourceFile: JavaScriptSource, in: BindingFact): Set[TaintFact] = { - val parser: Parser = new Parser() - val taintedVar = s"var ${in.keyName} = opal_source();\n" - val root: AstRoot = parser.parse(taintedVar + sourceFile.asString, "fileName", 1) - val nameSet: Set[String] = getTopLevelVariableNames(root) - - val beforeCode = - s"""function opal_source() { - | return \"secret\"; - |} - |function opal_last_stmt(${generateParams(nameSet.size)}) { } - |\n - |$taintedVar""".stripMargin - val afterCode = s"\nopal_last_stmt(${nameSet.mkString(", ")});\n" - val f: File = sourceFile.asFile(beforeCode, afterCode) - analyzeCommon(f, in.index) - } - - /** - * Invoke the WALA IFDS taint analysis - * - * @param f JavaScript file - * @param idx Index used in the out facts - * @return set of facts - */ - private def analyzeCommon(f: File, idx: Int): Set[TaintFact] = { - JSCallGraphUtil.setTranslatorFactory(new CAstRhinoTranslatorFactory) - val B: JSCFABuilder = JSCallGraphBuilderUtil.makeScriptCGBuilder(f.getParent, f.getName) - val CG: CallGraph = B.makeCallGraph(B.getOptions) - - /** - * Check basic block for source. - * - * @param ebb exploded basic block - * @return true if ebb contains a source - */ - def sources(ebb: BasicBlockInContext[IExplodedBasicBlock]): Boolean = { - val inst = ebb.getDelegate.getInstruction - inst match { - case i: SSAAbstractInvokeInstruction => - CG.getPossibleTargets(ebb.getNode, i.getCallSite).forEach(target => { - if (target.getMethod.getDeclaringClass.getName.toString.endsWith("opal_source")) - return true - }) - case _ => - } - false - } - - var varsAliveAfterJS: List[String] = List() - - /** - * Add all tainted variable names to varsAliveAfterJS after the analysis ran. - * - * @param bb basic block - * @param r result set - * @param d domain - */ - def sinks(bb: BasicBlockInContext[IExplodedBasicBlock], r: IntSet, d: Domain): Void = { - val inst = bb.getDelegate.getInstruction - inst match { - case invInst: SSAAbstractInvokeInstruction => - CG.getPossibleTargets(bb.getNode, invInst.getCallSite).forEach(target => { - if (target.getMethod.getDeclaringClass.getName.toString.endsWith("opal_last_stmt")) { - /* Collect all parameters. */ - val paramIdx: MutableIntSet = IntSetUtil.make() - for (i <- 1 until invInst.getNumberOfPositionalParameters) { - paramIdx.add(inst.getUse(i)) - } - - /* Collect all tainted variables reaching the end of the JS script. */ - val it: IntIterator = r.intIterator() - val taints: MutableIntSet = IntSetUtil.make() - while (it.hasNext) { - val vn = d.getMappedObject(it.next()) - taints.add(vn.fst) - } - - /* Intersect all tainted variables with the parameters of the last statement. - This is a workaround to only reconstruct the variable names for the "last" SSA name. */ - val taintedParams: IntSet = taints.intersection(paramIdx) - val taintedIt: IntIterator = taintedParams.intIterator() - while (taintedIt.hasNext) { - val idx = taintedIt.next() - varsAliveAfterJS ++= bb.getNode.getIR.getLocalNames(1, idx).toList - } - } - }) - case _ => - } - null - } - - WalaJavaScriptIFDSTaintAnalysis.startJSAnalysis(CG, sources, sinks) - f.delete() - varNamesToFacts(varsAliveAfterJS, idx) - } -} diff --git a/OPAL/js/Readme.md b/OPAL/sql/Readme.md similarity index 100% rename from OPAL/js/Readme.md rename to OPAL/sql/Readme.md diff --git a/OPAL/js/build.sbt b/OPAL/sql/build.sbt similarity index 100% rename from OPAL/js/build.sbt rename to OPAL/sql/build.sbt diff --git a/OPAL/js/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala b/OPAL/sql/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala similarity index 63% rename from OPAL/js/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala rename to OPAL/sql/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala index bd3cc0a96d..0adb3c38d8 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala +++ b/OPAL/sql/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala @@ -14,8 +14,8 @@ import org.opalj.tac.fpcf.analyses.ifds.JavaStatement import org.opalj.tac.fpcf.properties.{Taint, TaintFact} object IFDSAnalysisJSFixtureScheduler extends IFDSAnalysisScheduler[TaintFact, Method, JavaStatement] { - override def init(p: SomeProject, ps: PropertyStore) = new JavaScriptAwareTaintAnalysisFixture(p) - override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint - override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(Taint)) - override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) + override def init(p: SomeProject, ps: PropertyStore) = new JavaScriptAwareTaintAnalysisFixture(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint + override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(Taint)) + override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) } \ No newline at end of file diff --git a/OPAL/js/src/main/scala/org/opalj/js/JSFact.scala b/OPAL/sql/src/main/scala/org/opalj/js/JSFact.scala similarity index 100% rename from OPAL/js/src/main/scala/org/opalj/js/JSFact.scala rename to OPAL/sql/src/main/scala/org/opalj/js/JSFact.scala diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala b/OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala similarity index 87% rename from OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala rename to OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala index bb659f993b..212a470719 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala +++ b/OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala @@ -27,11 +27,12 @@ class JavaScriptAwareTaintProblemProblemFixture(p: SomeProject) extends JavaScri /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(Method, TaintFact)] = p.allProjectClassFiles.filter(classFile => - classFile.thisType.fqn == "org/opalj/fpcf/fixtures/js/Java2JsTestClass") - .flatMap(classFile => classFile.methods) - .filter(method => method.isPublic && outsideAnalysisContext(method).isEmpty) - .map(method => method -> TaintNullFact) + override val entryPoints: Seq[(Method, TaintFact)] = p.allProjectClassFiles + .flatMap(classFile => classFile.methods) + .filter(method => method.isPublic && outsideAnalysisContext(method).isEmpty) + .map( + method => + method -> TaintNullFact) /** * The sanitize method is a sanitizer. @@ -61,4 +62,3 @@ class JavaScriptAwareTaintProblemProblemFixture(p: SomeProject) extends JavaScri else None } - diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala b/OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala similarity index 93% rename from OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala rename to OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala index a7abbd03e6..9fc96ad14d 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala +++ b/OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala @@ -3,7 +3,6 @@ package org.opalj.js import org.opalj.br.{Method, ObjectType} import org.opalj.br.analyses.SomeProject -import org.opalj.collection.immutable.IntTrieSet import org.opalj.ifds.Dependees.Getter import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.{NO_MATCH, V} import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintProblem @@ -178,7 +177,7 @@ class JavaScriptAwareTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) * @param defSites def sites of the queried variable * @return */ - private def getPossibleStrings(method: Method, defSites: IntTrieSet): Set[String] = { + /*private def getPossibleStrings(method: Method, defSites: IntTrieSet): Set[String] = { val taCode = tacaiKey(method) // TODO: use string analysis here @@ -187,25 +186,23 @@ class JavaScriptAwareTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) a.expr.asStringConst.value case _ => "" } - } - - val jsAnalysis = new JavaScriptAnalysisCaller(p) + } */ override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { val callStmt = JavaIFDSProblem.asCall(call.stmt) - val allParams = callStmt.allParams + // val allParams = callStmt.allParams val allParamsWithIndex = callStmt.allParams.zipWithIndex - if (!invokesScriptFunction(callStmt)) { - in match { - case BindingFact(index, _) => - if (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == NO_MATCH) - Set(in) - else - Set() - case _ => super.callToReturnFlow(call, in, successor) - } - } else { + // if (!invokesScriptFunction(callStmt)) { + in match { + case BindingFact(index, _) => + if (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == NO_MATCH) + Set(in) + else + Set() + case _ => super.callToReturnFlow(call, in, successor) + } + /* } else { in match { /* Call to invokeFunction. The variable length parameter list is an array in TACAI. */ case arrIn: ArrayElement if callStmt.name == "invokeFunction" @@ -216,16 +213,16 @@ class JavaScriptAwareTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) /* Function name is unknown. We don't know what to call */ None else - Some(jsAnalysis.analyze(call, arrIn, fName))).filter(_.isDefined).flatMap(_.get) ++ Set(in) + {}//Some(jsAnalysis.analyze(call, arrIn, fName))).filter(_.isDefined).flatMap(_.get) ++ Set(in) /* Call to eval. */ case f: BindingFact if callStmt.name == "eval" && (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -1 || JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -3) => - jsAnalysis.analyze(call, f) + {}//jsAnalysis.analyze(call, f) case f: WildcardBindingFact if callStmt.name == "eval" && (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -1 || JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -3) => - jsAnalysis.analyze(call, f) + {}//jsAnalysis.analyze(call, f) /* Put obj in Binding */ case Variable(index) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -3 => val keyNames = getPossibleStrings(call.method, allParams(1).asVar.definedBy) @@ -268,7 +265,7 @@ class JavaScriptAwareTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) Set(Variable(call.index), in) case _ => Set(in) } - } + } */ } override def isTainted(expression: Expr[V], in: TaintFact): Boolean = { diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala b/OPAL/sql/src/main/scala/org/opalj/js/JavaScriptSource.scala similarity index 100% rename from OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala rename to OPAL/sql/src/main/scala/org/opalj/js/JavaScriptSource.scala diff --git a/OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala b/OPAL/sql/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala similarity index 100% rename from OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala rename to OPAL/sql/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala index a3082eb64d..030400361d 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/TaintProblem.scala @@ -4,8 +4,6 @@ package org.opalj.tac.fpcf.analyses.ifds.taint import org.opalj.tac.{Assignment, Expr, Stmt} import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V - - trait TaintProblem[C, Statement, IFDSFact] { /** diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala index b2f7943641..1dcd7ddb36 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala @@ -51,9 +51,9 @@ case class InstanceField(index: Int, classType: ObjectType, fieldName: String) e * @param flow A sequence of method calls, originating from but not including this method. */ case class FlowFact(flow: Seq[Callable]) extends TaintFact { - override val hashCode: Int = { - var r = 1 - flow.foreach(f => r = (r + f.hashCode()) * 31) - r - } + override val hashCode: Int = { + var r = 1 + flow.foreach(f => r = (r + f.hashCode()) * 31) + r + } } \ No newline at end of file diff --git a/build.sbt b/build.sbt index 82ae2e931e..c67dffbed7 100644 --- a/build.sbt +++ b/build.sbt @@ -169,7 +169,7 @@ lazy val `OPAL` = (project in file(".")) de, av, ll, - js, + sql, framework, // bp, (just temporarily...) tools, @@ -365,22 +365,13 @@ lazy val `LLVM` = (project in file("OPAL/ll")) .dependsOn(tac % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) -lazy val js = `JavaScript` -lazy val `JavaScript` = (project in file("OPAL/js")) +lazy val sql = `SQLTaint` +lazy val `JavaScript` = (project in file("OPAL/sql")) .settings(buildSettings: _*) .settings( - name := "JavaScript", - Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - JS"), - fork := true, - libraryDependencies ++= Seq("com.ibm.wala" % "com.ibm.wala.core" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.util" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.shrike" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast.java" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast.java.ecj" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast.js" % "1.5.7", - "com.ibm.wala" % "com.ibm.wala.cast.js.rhino" % "1.5.7", - "org.mozilla" % "rhino" % "1.7.10") + name := "SQL", + Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - SQL"), + fork := true ) .dependsOn(tac % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) @@ -398,7 +389,7 @@ lazy val `Framework` = (project in file("OPAL/framework")) av % "it->it;it->test;test->test;compile->compile", tac % "it->it;it->test;test->test;compile->compile", ll % "it->it;it->test;test->test;compile->compile", - js % "it->it;it->test;test->test;compile->compile" + sql % "it->it;it->test;test->test;compile->compile" ) .configs(IntegrationTest) From 3f78e270c6dbeb3df5feebfbec2875edce377bd6 Mon Sep 17 00:00:00 2001 From: roterEmil Date: Fri, 21 Oct 2022 15:12:54 +0200 Subject: [PATCH 61/67] reveresed renaming --- OPAL/{sql => js}/Readme.md | 0 OPAL/{sql => js}/build.sbt | 0 .../org/opalj/js/IFDSAnalysisFixtureScheduler.scala | 0 .../src/main/scala/org/opalj/js/JSFact.scala | 0 .../js/JavaScriptAwareTaintAnalysisFixture.scala | 0 .../org/opalj/js/JavaScriptAwareTaintProblem.scala | 0 .../main/scala/org/opalj/js/JavaScriptSource.scala | 0 .../scala/org/opalj/js/LocalJSSourceFinder.scala | 0 build.sbt | 12 ++++++------ 9 files changed, 6 insertions(+), 6 deletions(-) rename OPAL/{sql => js}/Readme.md (100%) rename OPAL/{sql => js}/build.sbt (100%) rename OPAL/{sql => js}/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala (100%) rename OPAL/{sql => js}/src/main/scala/org/opalj/js/JSFact.scala (100%) rename OPAL/{sql => js}/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala (100%) rename OPAL/{sql => js}/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala (100%) rename OPAL/{sql => js}/src/main/scala/org/opalj/js/JavaScriptSource.scala (100%) rename OPAL/{sql => js}/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala (100%) diff --git a/OPAL/sql/Readme.md b/OPAL/js/Readme.md similarity index 100% rename from OPAL/sql/Readme.md rename to OPAL/js/Readme.md diff --git a/OPAL/sql/build.sbt b/OPAL/js/build.sbt similarity index 100% rename from OPAL/sql/build.sbt rename to OPAL/js/build.sbt diff --git a/OPAL/sql/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala b/OPAL/js/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala similarity index 100% rename from OPAL/sql/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala rename to OPAL/js/src/main/scala/org/opalj/js/IFDSAnalysisFixtureScheduler.scala diff --git a/OPAL/sql/src/main/scala/org/opalj/js/JSFact.scala b/OPAL/js/src/main/scala/org/opalj/js/JSFact.scala similarity index 100% rename from OPAL/sql/src/main/scala/org/opalj/js/JSFact.scala rename to OPAL/js/src/main/scala/org/opalj/js/JSFact.scala diff --git a/OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala similarity index 100% rename from OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala rename to OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala diff --git a/OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala similarity index 100% rename from OPAL/sql/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala rename to OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintProblem.scala diff --git a/OPAL/sql/src/main/scala/org/opalj/js/JavaScriptSource.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala similarity index 100% rename from OPAL/sql/src/main/scala/org/opalj/js/JavaScriptSource.scala rename to OPAL/js/src/main/scala/org/opalj/js/JavaScriptSource.scala diff --git a/OPAL/sql/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala b/OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala similarity index 100% rename from OPAL/sql/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala rename to OPAL/js/src/main/scala/org/opalj/js/LocalJSSourceFinder.scala diff --git a/build.sbt b/build.sbt index c67dffbed7..aa540ca719 100644 --- a/build.sbt +++ b/build.sbt @@ -169,7 +169,7 @@ lazy val `OPAL` = (project in file(".")) de, av, ll, - sql, + js, framework, // bp, (just temporarily...) tools, @@ -365,12 +365,12 @@ lazy val `LLVM` = (project in file("OPAL/ll")) .dependsOn(tac % "it->it;it->test;test->test;compile->compile") .configs(IntegrationTest) -lazy val sql = `SQLTaint` -lazy val `JavaScript` = (project in file("OPAL/sql")) +lazy val js = `JavaScript` +lazy val `JavaScript` = (project in file("OPAL/js")) .settings(buildSettings: _*) .settings( - name := "SQL", - Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - SQL"), + name := "JavaScript", + Compile / doc / scalacOptions ++= Opts.doc.title("OPAL - JS"), fork := true ) .dependsOn(tac % "it->it;it->test;test->test;compile->compile") @@ -389,7 +389,7 @@ lazy val `Framework` = (project in file("OPAL/framework")) av % "it->it;it->test;test->test;compile->compile", tac % "it->it;it->test;test->test;compile->compile", ll % "it->it;it->test;test->test;compile->compile", - sql % "it->it;it->test;test->test;compile->compile" + js % "it->it;it->test;test->test;compile->compile" ) .configs(IntegrationTest) From cee3b628bc271b9009bcf3bf9c493fd6cb3421ef Mon Sep 17 00:00:00 2001 From: Imagee <47892322+Imagee@users.noreply.github.com> Date: Sat, 22 Oct 2022 17:38:06 +0200 Subject: [PATCH 62/67] InitialChanges --- .../org/opalj/fpcf/analyses/TaintDemo.scala | 5 +- .../sql/IFDSSqlAnalysisScheduler.scala | 18 + .../fpcf/analyses/sql/SqlTaintProblem.scala | 261 ++++++++++++++ .../JavaScriptAwareTaintAnalysisFixture.scala | 5 +- .../ifds/taint/ForwardTaintProblem.scala | 14 +- .../ifds/taint/summaries/TaintSummaries.scala | 324 ------------------ 6 files changed, 290 insertions(+), 337 deletions(-) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/IFDSSqlAnalysisScheduler.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala delete mode 100644 OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala index 987632d570..cc1de2d7aa 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala @@ -8,7 +8,8 @@ import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.fpcf.PropertyStore import org.opalj.tac.cg.RTACallGraphKey import org.opalj.tac.fpcf.properties.Taint -import org.opalj.js.IFDSAnalysisJSFixtureScheduler +//import org.opalj.js.IFDSAnalysisJSFixtureScheduler +import org.opalj.tac.fpcf.analyses.sql.IFDSSqlAnalysisScheduler import java.net.URL @@ -38,7 +39,7 @@ object TaintDemo extends ProjectAnalysisApplication { } propertyStore = analysesManager.runAll( - IFDSAnalysisJSFixtureScheduler + IFDSSqlAnalysisScheduler )._1 propertyStore.waitOnPhaseCompletion(); //Result: diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/IFDSSqlAnalysisScheduler.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/IFDSSqlAnalysisScheduler.scala new file mode 100644 index 0000000000..efcadf6b9a --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/IFDSSqlAnalysisScheduler.scala @@ -0,0 +1,18 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.sql + +import org.opalj.br.Method +import org.opalj.br.analyses.{DeclaredMethodsKey, ProjectInformationKeys, SomeProject} +import org.opalj.br.fpcf.PropertyStoreKey +import org.opalj.fpcf.{PropertyBounds, PropertyStore} +import org.opalj.ifds.{IFDSAnalysisScheduler, IFDSPropertyMetaInformation} +import org.opalj.tac.cg.TypeProviderKey +import org.opalj.tac.fpcf.analyses.ifds.JavaStatement +import org.opalj.tac.fpcf.properties.{Taint, TaintFact} + +object IFDSSqlAnalysisScheduler extends IFDSAnalysisScheduler[TaintFact, Method, JavaStatement] { + override def init(p: SomeProject, ps: PropertyStore) = new SqlTaintAnalysis(p) + override def property: IFDSPropertyMetaInformation[JavaStatement, TaintFact] = Taint + override val uses: Set[PropertyBounds] = Set(PropertyBounds.ub(Taint)) + override def requiredProjectInformation: ProjectInformationKeys = Seq(TypeProviderKey, DeclaredMethodsKey, PropertyStoreKey) +} \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala new file mode 100644 index 0000000000..34511cff4c --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala @@ -0,0 +1,261 @@ +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ +package org.opalj.tac.fpcf.analyses.sql + +import org.opalj.br.{Method} +import org.opalj.br.analyses.SomeProject +import org.opalj.ifds.IFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.{V} +import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintProblem +import org.opalj.tac.{AITACode,ComputeTACAIKey, Expr, TACMethodParameter, TACode} +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties._ +import org.opalj.value.ValueInformation + + +class SqlTaintAnalysis(project: SomeProject) + extends IFDSAnalysis()(project, new SqlTaintProblem(project), Taint) + +/** + * Java IFDS analysis that is able to resolve calls of sql statements. + * + * @param p project + */ +class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { + final type TACAICode = TACode[TACMethodParameter, JavaIFDSProblem.V] + val tacaiKey: Method => AITACode[TACMethodParameter, ValueInformation] = p.get(ComputeTACAIKey) + + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a variable. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + override protected def createFlowFact( + callee: Method, + call: JavaStatement, + in: TaintFact + ): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) + Some(FlowFact(Seq(JavaMethod(call.method), JavaMethod(callee)))) + else None + + /** + * The entry points of this analysis. + */ + override def entryPoints: Seq[(Method, TaintFact)] = + for { + m <- p.allMethodsWithBody + if m.name == "main" + } yield m -> TaintNullFact + + /** + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. + * + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" + + /** + * Called in callToReturnFlow. This method can return whether the input fact + * will be removed after `callee` was called. I.e. the method could sanitize parameters. + * + * @param call The call statement. + * @param in The fact which holds before the call. + * @return Whether in will be removed after the call. + */ + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false + + override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { + /* + val callObject = JavaIFDSProblem.asCall(call.stmt) + val allParams = callObject.allParams + + val allParamsWithIndices = allParams.zipWithIndex + in match { + case BindingFact(index, keyName) => allParamsWithIndices.flatMap { + case (param, paramIndex) if param.asVar.definedBy.contains(index) => + Some(BindingFact(JavaIFDSProblem.switchParamAndVariableIndex( + paramIndex, + callee.isStatic + ), keyName)) + case _ => None // Nothing to do + }.toSet + case _ => super.callFlow(call, callee, in) + } + + */ + super.callFlow(call, callee, in) + } + + override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, + callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { + + /* + if (!isPossibleReturnFlow(exit, successor)) return Set.empty + val callee = exit.callable + if (sanitizesReturnValue(callee)) return Set.empty + val callStatement = JavaIFDSProblem.asCall(call.stmt) + val allParams = callStatement.allParams + + in match { + case BindingFact(index, keyName) => + var flows: Set[TaintFact] = Set.empty + if (index < 0 && index > -100) { + val param = allParams( + JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) + ) + flows ++= param.asVar.definedBy.map(i => BindingFact(i, keyName)) + } + if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { + val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy + if (returnValueDefinedBy.contains(index)) + flows += BindingFact(call.index, keyName) + } + flows + case _ => super.returnFlow(exit, in, call, callFact, successor) + } + */ + + super.returnFlow(exit, in, call, callFact, successor) + } + + + /** + * Returns all possible constant strings. Contains the empty string if at least one was non-constant. + * + * @param method method + * @param defSites def sites of the queried variable + * @return + */ + /*private def getPossibleStrings(method: Method, defSites: IntTrieSet): Set[String] = { + val taCode = tacaiKey(method) + + // TODO: use string analysis here + defSites.map(site => taCode.stmts.apply(site)).map { + case a: Assignment[JavaIFDSProblem.V] if a.expr.isStringConst => + a.expr.asStringConst.value + case _ => "" + } + } */ + + override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { + + super.callToReturnFlow(call, in, successor) + /* + val callStmt = JavaIFDSProblem.asCall(call.stmt) + // val allParams = callStmt.allParams + val allParamsWithIndex = callStmt.allParams.zipWithIndex + + // if (!invokesScriptFunction(callStmt)) { + in match { + case BindingFact(index, _) => + if (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == NO_MATCH) + Set(in) + else + Set() + case _ => super.callToReturnFlow(call, in, successor) + } + + */ + + /* } else { + in match { + /* Call to invokeFunction. The variable length parameter list is an array in TACAI. */ + case arrIn: ArrayElement if callStmt.name == "invokeFunction" + && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, arrIn.index) == -3 => + val fNames = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + fNames.map(fName => + if (fName == "") + /* Function name is unknown. We don't know what to call */ + None + else + {}//Some(jsAnalysis.analyze(call, arrIn, fName))).filter(_.isDefined).flatMap(_.get) ++ Set(in) + /* Call to eval. */ + case f: BindingFact if callStmt.name == "eval" + && (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -1 + || JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -3) => + {}//jsAnalysis.analyze(call, f) + case f: WildcardBindingFact if callStmt.name == "eval" + && (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -1 + || JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -3) => + {}//jsAnalysis.analyze(call, f) + /* Put obj in Binding */ + case Variable(index) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -3 => + val keyNames = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + val defSites = callStmt.receiverOption.get.asVar.definedBy + keyNames.flatMap(keyName => defSites.map(i => if (keyName == "") WildcardBindingFact(i) else BindingFact(i, keyName))) ++ Set(in) + /* putAll BindingFact to other BindingFact */ + case BindingFact(index, keyName) if callStmt.name == "putAll" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -2 => + callStmt.receiverOption.get.asVar.definedBy.map(i => if (keyName == "") WildcardBindingFact(i) else BindingFact(i, keyName)) ++ Set(in) + case WildcardBindingFact(index) if callStmt.name == "putAll" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -2 => + callStmt.receiverOption.get.asVar.definedBy.map(i => WildcardBindingFact(i)) ++ Set(in) + /* Overwrite BindingFact */ + case BindingFact(index, keyName) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + val possibleFields = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + if (possibleFields.size == 1 && possibleFields.contains(keyName)) + /* Key is definitely overwritten */ + Set() + else + Set(in) + case WildcardBindingFact(index) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + /* We never overwrite here as we don't know the key */ + Set(in) + /* Remove BindingFact */ + case BindingFact(index, keyName) if callStmt.name == "remove" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + val possibleFields = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + if (possibleFields.size == 1 && possibleFields.contains(keyName)) + Set() + else + Set(in) + case WildcardBindingFact(index) if callStmt.name == "remove" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + /* We never kill here as we don't know the key */ + Set(in) + /* get from BindingFact */ + case BindingFact(index, keyName) if callStmt.name == "get" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + val possibleFields = getPossibleStrings(call.method, allParams(1).asVar.definedBy) + if (possibleFields.size == 1 && possibleFields.contains(keyName)) + Set(Variable(call.index), in) + else + Set(in) + case WildcardBindingFact(index) if callStmt.name == "get" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => + Set(Variable(call.index), in) + case _ => Set(in) + } + } */ + } + + override def isTainted(expression: Expr[V], in: TaintFact): Boolean = { + super.isTainted(expression, in) + /* + val definedBy = expression.asVar.definedBy + expression.isVar && (in match { + case BindingFact(index, _) => definedBy.contains(index) + case _ => super.isTainted(expression, in) + }) + + */ + } + + + //TODO + //Get Strings used by foo bar. + //Handle Concatenation. + //put the Strings in the Prototype +} diff --git a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala index 212a470719..ce944b5057 100644 --- a/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala +++ b/OPAL/js/src/main/scala/org/opalj/js/JavaScriptAwareTaintAnalysisFixture.scala @@ -27,13 +27,16 @@ class JavaScriptAwareTaintProblemProblemFixture(p: SomeProject) extends JavaScri /** * The analysis starts with all public methods in TaintAnalysisTestClass. */ - override val entryPoints: Seq[(Method, TaintFact)] = p.allProjectClassFiles + override val entryPoints: Seq[(Method, TaintFact)] = + p.allProjectClassFiles .flatMap(classFile => classFile.methods) .filter(method => method.isPublic && outsideAnalysisContext(method).isEmpty) .map( method => method -> TaintNullFact) + + /** * The sanitize method is a sanitizer. */ diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index b078fc58a5..7ea0f6a7f7 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -6,11 +6,11 @@ import org.opalj.br.analyses.{DeclaredMethodsKey, SomeProject} import org.opalj.ifds.Dependees.Getter import org.opalj.tac._ import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.TaintSummaries +//import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.TaintSummaries import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} import org.opalj.tac.fpcf.properties._ -import java.io.File +//import java.io.File abstract class ForwardTaintProblem(project: SomeProject) extends JavaIFDSProblem[TaintFact](project) @@ -18,7 +18,7 @@ abstract class ForwardTaintProblem(project: SomeProject) val declaredMethods = project.get(DeclaredMethodsKey) def useSummaries: Boolean = false - lazy val summaries: TaintSummaries = TaintSummaries(new File(getClass.getResource("/summaries/").getPath).listFiles().toList) + //lazy val summaries: TaintSummaries = TaintSummaries(new File(getClass.getResource("/summaries/").getPath).listFiles().toList) override def nullFact: TaintFact = TaintNullFact @@ -215,9 +215,7 @@ abstract class ForwardTaintProblem(project: SomeProject) } } - if (useSummaries && summaries.isSummarized(callStmt)) { - summaries.compute(call, callStmt, in) - } else if (icfg.getCalleesIfCallStatement(call).isEmpty) { + if (icfg.getCalleesIfCallStatement(call).isEmpty) { // If the call does not have any callees, the code is unknown // and we safely handle it as the identity Set(in) @@ -265,10 +263,6 @@ abstract class ForwardTaintProblem(project: SomeProject) * We assume that the callee does not call the source method. */ override def outsideAnalysisContext(callee: Method): Option[OutsideAnalysisContextHandler] = { - if (useSummaries && summaries.isSummarized(callee)) { - /* Kill the flow and apply the summary in CallToReturn. */ - Some(((_: JavaStatement, _: JavaStatement, _: TaintFact, _: Getter) => Set.empty): OutsideAnalysisContextHandler) - } else super.outsideAnalysisContext(callee) match { case Some(_) => Some(((call: JavaStatement, successor: JavaStatement, in: TaintFact, _: Getter) => { val allParams = JavaIFDSProblem.asCall(call.stmt).receiverOption ++ JavaIFDSProblem.asCall(call.stmt).params diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala deleted file mode 100644 index 6d59e6f64e..0000000000 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/summaries/TaintSummaries.scala +++ /dev/null @@ -1,324 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.ifds.taint.summaries - -import org.opalj.br.{ArrayType, BooleanType, ByteType, CharType, DoubleType, FieldType, FloatType, IntegerType, LongType, Method, ObjectType, ShortType, Type, VoidType} -import org.opalj.tac.Call -import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaStatement} -import org.opalj.tac.fpcf.properties._ -import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.Flow.nodeToTaint -import org.opalj.tac.fpcf.analyses.ifds.taint.summaries.ClassSummary.{signaturePattern, stringToFieldType, stringToType} - -import java.io.File -import scala.util.matching.Regex -import scala.xml.{Node, XML} - -/** - * This file implements a subset of the StubDroid summary specification. - * - * Not implemented (yet): - * - Summaries for inner classes - * - Type Checking - * - Metadata/Exclusiveness - * - Aliasing (depends on the IFDS analysis) - * - Access Paths with k > 1 (again, depends on the IFS analysis) - */ - -/** - * Holds summaries for all classes. - * - * @param files summary XML files - */ -case class TaintSummaries(files: List[File]) { - val summaries: Map[String, ClassSummary] = - (files.map(f => f.getName.replace(".xml", "").replace(".", "/")) - zip - files.map(f => new ClassSummary(XML.loadFile(f)))).toMap - - private def isSummarized(objType: ObjectType): Boolean = - summaries.contains(objType.fqn) - - /** - * Return true if the method is summarized. - * - * @param callStmt call site in TACAI - * @return whether the method is summarized. - */ - def isSummarized(callStmt: Call[JavaIFDSProblem.V]): Boolean = - isSummarized(callStmt.declaringClass.mostPreciseObjectType) - - /** - * Return true if the method is summarized. - * - * @param method callee - * @return whether the method is summarized. - */ - def isSummarized(method: Method): Boolean = - isSummarized(method.classFile.thisType) - - /** - * Applies the summary if available, else does nothing. - * Precondition: callee class is summarized. - * - * @param call call java statement - * @param callStmt call TAC statement - * @param in fact - * @return out set of facts - */ - def compute(call: JavaStatement, callStmt: Call[V], in: TaintFact): Set[TaintFact] = { - val classFqn = callStmt.declaringClass.mostPreciseObjectType.fqn - assert(summaries.contains(classFqn)) - - summaries(classFqn).compute(call, callStmt, in) - } -} - -/** - * Holds the methods summaries for a single class. - * - * @param summaryNode 'summary' XML node - */ -case class ClassSummary(summaryNode: Node) { - /* Method summaries. */ - def methods: Seq[MethodSummary] = (summaryNode \\ "methods" \\ "method") - .map(methodNode => { - val sig = (methodNode \@ "id") - .replace("<", "<") - .replace(">", ">") - signaturePattern.findFirstMatchIn(sig) match { - case Some(m) => - val paramTypes: List[FieldType] = - if (m.group(3) == "") List() - else m.group(3) - .split(",") - .map(s => stringToFieldType(s.strip())) - .toList - MethodSummary( - stringToType(m.group(1)), - m.group(2), - paramTypes, - methodNode - ) - case None => - throw new RuntimeException("couldn't parse the signature of "+sig); - } - }).toList - - /** - * Applies the summary if available, else does perform the identity. - * - * @param call call java statement - * @param callStmt call TAC statement - * @param in fact - * @return out set of facts - */ - def compute(call: JavaStatement, callStmt: Call[V], in: TaintFact): Set[TaintFact] = { - in match { - /* We do not have any summaries for null facts. */ - case TaintNullFact => Set(in) - case _ => - /* Find the right method, even when overloaded. */ - val methodSummary = methods.find(m => m.methodName == callStmt.name - && m.paramTypes - .zip(callStmt.descriptor.parameterTypes) - .forall(t => t._1 == t._2)) - methodSummary match { - case Some(methodSummary) => methodSummary.compute(call, callStmt, in) + in - case None => Set(in) - } - } - } -} - -object ClassSummary { - /* group 1 = return type, group 2 = method name, group 3 = param list */ - val signaturePattern: Regex = """([a-zA-Z.\[\]]*?) ([a-zA-Z0-9_<>]*?)\(([a-zA-Z.\[\],\s]*?)\)""".r - - /** - * Converts the type string in Soot's format to the OPAL ObjectType, excluding void. - * - * @param str type string - * @return ObjectType - */ - def stringToFieldType(str: String): FieldType = { - str match { - case "boolean" => BooleanType - case "byte" => ByteType - case "char" => CharType - case "short" => ShortType - case "int" => IntegerType - case "long" => LongType - case "float" => FloatType - case "double" => DoubleType - case "String" => ObjectType.String - case _ => - if (str.endsWith("[]")) - ArrayType(stringToFieldType(str.substring(0, str.length - 2))) - else - ObjectType(str.replace(".", "/")) - } - } - - /** - * Converts the type string in Soot's format to the OPAL ObjectType, including void. - * - * @param str type string - * @return ObjectType - */ - def stringToType(str: String): Type = { - str match { - case "void" => VoidType - case _ => stringToFieldType(str) - } - } -} - -/** - * Represents the summary of a single method. - * - * @param returnType return type - * @param methodName method name - * @param paramTypes parameter types - * @param methodNode 'method' XML node - */ -case class MethodSummary(returnType: Type, methodName: String, paramTypes: List[FieldType], methodNode: Node) { - def flows: Seq[Flow] = methodNode.head.map(flowNode => Flow(flowNode)) - - def compute(call: JavaStatement, callStmt: Call[V], in: TaintFact): Set[TaintFact] = { - flows.flatMap(f => - if (f.matchesFrom(callStmt, in)) - f.createTaint(call, callStmt, in) - else - Set.empty).toSet - } - - override def toString: String = s"${returnType.toString} ${methodName}(${paramTypes.mkString(", ")})" -} - -/** - * Represents one summarized flow. - * - * @param flowNode 'flow' XML node - */ -case class Flow(flowNode: Node) { - // only needed if we'd use a on-demand alias analysis - val isAlias: Boolean = (flowNode \@ "isAlias").equals("true") - // TODO: how important is type checking? - val typeChecking: Boolean = (flowNode \@ "typeChecking").equals("true") - val from: SummaryTaint = nodeToTaint((flowNode \\ "from").head) - val to: SummaryTaint = nodeToTaint((flowNode \\ "to").head) - - /** - * Return true if the taint matches the 'from' rule. - * - * @param callStmt call TAC statement - * @param in fact - * @return Boolean - */ - def matchesFrom(callStmt: Call[V], in: TaintFact): Boolean = { - val isStatic = callStmt.receiverOption.isEmpty - val allParamsWithIndex = callStmt.allParams.zipWithIndex - - from match { - case ParameterSummaryTaint(summaryIndex) => - in match { - case Variable(index) => - JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex - /* Summaries do not know array elements. */ - case ArrayElement(index, _) => - JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex - case InstanceField(index, _, fieldName) => - JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex - case _ => false - } - case ParameterSummaryTaintWithField(summaryIndex, summaryFieldName) => - in match { - case Variable(index) => - JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex - /* Summaries do not know array elements. */ - case ArrayElement(index, _) => - JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex - case InstanceField(index, _, fieldName) => - (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == summaryIndex - && summaryFieldName == fieldName) - case _ => false - } - case BaseObjectSummaryTaint(summaryFieldName) => - in match { - case InstanceField(index, _, fieldName) => - (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == -1 - && summaryFieldName == fieldName) - /* Over-approximation: apply the rule even though if no field is known. */ - case Variable(index) => - JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index, isStatic) == -1 - case _ => false - } - case _ => false - } - } - - /** - * Return the resulting set of facts after applying the summary. - * - * @param call call java statement - * @param callStmt call TAC statement - * @param in fact - * @return Out set of facts - */ - def createTaint(call: JavaStatement, callStmt: Call[V], in: TaintFact): Set[TaintFact] = { - to match { - case ParameterSummaryTaint(summaryIndex) => - callStmt.params(summaryIndex).asVar.definedBy.map(i => Variable(i)) - case ParameterSummaryTaintWithField(summaryIndex, fieldName) => - callStmt.params(summaryIndex).asVar.definedBy.map(i => - InstanceField(i, callStmt.declaringClass.mostPreciseObjectType, fieldName)) - case BaseObjectSummaryTaint(fieldName) if callStmt.receiverOption.isDefined => - callStmt.receiverOption.get.asVar.definedBy.map(i => - InstanceField(i, callStmt.declaringClass.mostPreciseObjectType, fieldName)) - case ReturnSummaryTaint() if call.stmt.isAssignment => - Set(Variable(call.index)) - case ReturnSummaryTaintWithField(fieldName) if call.stmt.isAssignment => - Set(InstanceField(call.index, callStmt.declaringClass.mostPreciseObjectType, fieldName)) - case _ => Set.empty - } - } -} - -object Flow { - val firstFieldPattern: Regex = """\[[A-Za-z\.\[\]]*?: [A-Za-z\.\[\]]*? ([a-zA-z]*?)[,\]]""".r - - private def getFieldNameFromAttribute(attr: String): String = { - firstFieldPattern.findFirstMatchIn(attr) match { - case Some(m) => m.group(1) - case None => throw new RuntimeException("Failed to parse Access Path: "+attr) - } - } - - /** - * Maps a 'from' or 'to' XML node to an SummaryTaint - * - * @param node 'from' or 'to' node - * @return SummaryTaint - */ - def nodeToTaint(node: Node): SummaryTaint = { - (node \@ "sourceSinkType", node \@ "AccessPath") match { - case ("Parameter", "") => - ParameterSummaryTaint((node \@ "ParameterIndex").toInt - 2) - case ("Parameter", attr) => - ParameterSummaryTaintWithField( - (node \@ "ParameterIndex").toInt - 2, - getFieldNameFromAttribute(attr) - ) - case ("Field", attr) => BaseObjectSummaryTaint(getFieldNameFromAttribute(attr)) - case ("Return", "") => ReturnSummaryTaint() - case ("Return", attr) => ReturnSummaryTaintWithField(getFieldNameFromAttribute(attr)) - } - } -} - -trait SummaryTaint -case class ParameterSummaryTaint(index: Int) extends SummaryTaint -case class ParameterSummaryTaintWithField(index: Int, fieldName: String) extends SummaryTaint -case class BaseObjectSummaryTaint(fieldName: String) extends SummaryTaint -case class ReturnSummaryTaint() extends SummaryTaint -case class ReturnSummaryTaintWithField(fieldName: String) extends SummaryTaint From d1240d179eebb6319abe18c773ebe38d6be478ab Mon Sep 17 00:00:00 2001 From: Imagee <47892322+Imagee@users.noreply.github.com> Date: Tue, 22 Nov 2022 02:10:52 +0100 Subject: [PATCH 63/67] First ClassTest successfully --- .../org/opalj/fpcf/analyses/Playground.scala | 52 ++++ .../org/opalj/fpcf/analyses/TaintDemo.scala | 5 +- .../opalj/tac/fpcf/analyses/sql/SQLFact.scala | 8 + .../analyses/sql/SqlStringTaintAnalyzer.scala | 209 ++++++++++++++ .../fpcf/analyses/sql/SqlTaintProblem.scala | 273 +++++++++++++++++- .../fpcf/analyses/sql/SqlTaintProblem02.scala | 86 ++++++ .../fpcf/analyses/sql/TaintMemorySQL.scala | 41 +++ .../scala/org/opalj/ifds/IFDSAnalysis.scala | 5 +- .../ifds/taint/ForwardTaintProblem.scala | 3 + .../opalj/tac/fpcf/properties/TaintFact.scala | 2 + 10 files changed, 670 insertions(+), 14 deletions(-) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/Playground.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SQLFact.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/TaintMemorySQL.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/Playground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/Playground.scala new file mode 100644 index 0000000000..8989f646cf --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/Playground.scala @@ -0,0 +1,52 @@ + +package org.opalj.fpcf.analyses + +import org.opalj.br.analyses.{BasicReport, Project, ProjectAnalysisApplication} +//import org.opalj.tac.LazyDetachedTACAIKey +//import org.opalj.tac.cg.CFA_1_0_CallGraphKey +import org.opalj.tac.fpcf.analyses.sql.SqlStringTaintAnalyzer + +import java.net.URL + +object Playground extends ProjectAnalysisApplication{ + override def doAnalyze(project: Project[URL], parameters: Seq[String], isInterrupted: () => Boolean): BasicReport ={ + val result = analyze(project) + BasicReport(result) + } + + def analyze(project: Project[URL]): String = { + + + //val computedCallGraph = project.get(CFA_1_0_CallGraphKey) + //val callGraph = computedCallGraph.numEdges + //val tacProvider = project.get(LazyDetachedTACAIKey) + + for{ + cf <- project.allProjectClassFiles + m <- cf.methods + if m.body.isDefined + if m.name == "main" + } { + //val tac = tacProvider(m) + //println(tac.cfg) + + val x = "TAINTED_VALUE" + val insert = "INSERT INTO tableA ( Id, name ) VALUES ( 01," + x + " );" + println("bei: ") + println(insert) + println(SqlStringTaintAnalyzer.doAnalyze(insert)) + + val select = "SELECT * FROM tableA ;"; + + val ergebnis = SqlStringTaintAnalyzer.doAnalyze(select) + + println("ergebnis:") + println(ergebnis) + + // println(callGraph) + } + + "" + } + +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala index cc1de2d7aa..c6a16430bf 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/TaintDemo.scala @@ -6,7 +6,7 @@ import org.opalj.ai.fpcf.properties.AIDomainFactoryKey import org.opalj.br.analyses.{BasicReport, Project, ProjectAnalysisApplication} import org.opalj.br.fpcf.FPCFAnalysesManagerKey import org.opalj.fpcf.PropertyStore -import org.opalj.tac.cg.RTACallGraphKey +import org.opalj.tac.cg.{AllocationSiteBasedPointsToCallGraphKey} import org.opalj.tac.fpcf.properties.Taint //import org.opalj.js.IFDSAnalysisJSFixtureScheduler import org.opalj.tac.fpcf.analyses.sql.IFDSSqlAnalysisScheduler @@ -32,7 +32,7 @@ object TaintDemo extends ProjectAnalysisApplication { var propertyStore: PropertyStore = null val analysesManager = project.get(FPCFAnalysesManagerKey) - project.get(RTACallGraphKey) + project.get(AllocationSiteBasedPointsToCallGraphKey) project.updateProjectInformationKeyInitializationData(AIDomainFactoryKey) { _ => Set[Class[_ <: AnyRef]](classOf[domain.l2.DefaultPerformInvocationsDomainWithCFG[URL]]) @@ -44,5 +44,6 @@ object TaintDemo extends ProjectAnalysisApplication { propertyStore.waitOnPhaseCompletion(); //Result: propertyStore.entities(Taint.key).mkString("\n") + } } \ No newline at end of file diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SQLFact.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SQLFact.scala new file mode 100644 index 0000000000..22fcf7940d --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SQLFact.scala @@ -0,0 +1,8 @@ + +package org.opalj.tac.fpcf.analyses.sql + +import org.opalj.tac.fpcf.properties.TaintFact + +trait SQLFact extends TaintFact + +case class StringValue(index: Int, values: Set[String], taintStatus: Boolean) extends SQLFact with TaintFact diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala new file mode 100644 index 0000000000..ad2094c704 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala @@ -0,0 +1,209 @@ + +package org.opalj.tac.fpcf.analyses.sql + +object SqlStringTaintAnalyzer { + + + var taintMemory = new TaintMemorySQL("TAINTED_VALUE") + val debugMode = true + + def doAnalyze(string: String) = { + + val normalizedString = preprozesing(string) + val insertPatter = raw"(INSERT).*".r + val updatePatter = raw"(UPDATE).*".r + val selectPatter = raw"(SELECT).*".r + + normalizedString match { + case i@insertPatter(_*) => + val result = analyzeInsertStatement(i) + if(debugMode) println(result._2) + result._1 + + case u@updatePatter(_*) => + val result = analyzeUpdateStatment(u) + if(debugMode) println(result._2) + result._1 + case s@selectPatter(_*) => + val result = analyzeSelectStatment(s) + if(debugMode) println(result._2) + result._1 + case _ => + if(debugMode) println("nothing analyzed") + false + } + } + + def preprozesing(string: String) = { + val (hasCorrectSyntax, sqlString) = filterSQLString(string) + var normalizedString = sqlString + + if (hasCorrectSyntax) { + normalizedString = normalizeSQLString(sqlString) + } + normalizedString + } + + def filterSQLString(string: String) = { + val word = raw"(\w+|'\w+'|`\w+`)" + val specialWord = raw"($word|(\w+(-|\.|\w+)+\w+)|('\w+(-|\.|\w+)+\w+')|(`\w+(-|\.|\w+)+\w+`))" + val wordlist = raw"$specialWord(\s*,\s*$specialWord)*" + val insertColumn = raw"\(\s*$wordlist\s*\)" + val values = raw"\(\s*$wordlist\s*\)(\s*,\s*\(\s*$wordlist\s*\))*" + + val insertPattern = raw"((INSERT|insert)\s+((IGNORE|ignore)\s+)?(INTO|into)\s+)($specialWord)\s+$insertColumn\s+(VALUES|values)\s+($values)\s*;\s*".r + val selectPattern = raw"(SELECT|select)\s+($wordlist|\*)\s+(FROM|from)\s+($specialWord)\s*(\s+(WHERE|where)\s+.+)?;".r + val updatePattern = raw"(UPDATE|update)\s+($specialWord)\s+(SET|set)\s+($specialWord\s+=\s+$specialWord)(\s*,\s*$specialWord\s+=\s+$specialWord)*(\s+(WHERE|where) .+)?\s*;\s*".r + + + string match { + case i@insertPattern(_*) => (true, i) + case s@selectPattern(_*) => (true, s) + case u@updatePattern(_*) => (true, u) + case _ => (false, "") + } + } + + def normalizeSQLString(sqlString: String): String = { + var nStr = sqlString.replaceAll(" ", " ") + + nStr = nStr.replaceAll(",", " , ") + nStr = nStr.replaceAll(";", " ; ") + nStr = nStr.replaceAll("=", " = ") + nStr = nStr.replaceAll(raw"\(", " ( ") + nStr = nStr.replaceAll(raw"\)", " ) ") + nStr = nStr.replace("select", "SELECT") + nStr = nStr.replace("from", "FROM") + nStr = nStr.replace("where", "WHERE") + nStr = nStr.replace("values", "VALUES") + nStr = nStr.replace("insert", "INSERT") + nStr = nStr.replace("into", "INTO") + nStr = nStr.replace("update", "UPDATE") + nStr = nStr.replace("set", "SET") + + while (nStr.contains(" ")) { + nStr = nStr.replaceAll(" ", " ") + } + nStr + } + + def extractInformationOfInsert(normalizedString: String) = { + + val insertPatter01 = raw"(INSERT (IGNORE\s)?INTO) (.+) (\( .+ \)) (VALUES) (\(.+\)) ; ".r + + var tableNameReturn = "" + var columnNamesReturn: Seq[String] = Seq() + var valuesGroupsReturn: Set[Array[String]] = Set(Array()) + + + normalizedString match { + case insertPatter01(_, _, tableName, columnNames, _, values) => + tableNameReturn = tableName + columnNamesReturn = columnNames.replaceAll(raw"(\(|\))", "").split(",").map(_.trim).toSeq + val removedBrackets = values.split(raw"\) , \(").map(x => x.replaceAll(raw"(\(|\))", "")) + valuesGroupsReturn = removedBrackets.map(x => x.split(",").map(_.trim)).toSet + case _ => + } + + (tableNameReturn, columnNamesReturn, valuesGroupsReturn) + + } + + def analyzeInsertStatement(normalizedString: String) = { + val (table, columns, valueGroups) = extractInformationOfInsert(normalizedString) + var taintedColumns: Set[String] = columns.filter(column => taintMemory.isTainted(column)).toSet + + valueGroups.foreach(array => { + for (i <- array.indices) { + if (taintMemory.isTainted(array(i))) taintedColumns += columns(i) + } + }) + + if (taintMemory.isTainted(table)) taintedColumns ++= columns + if (taintedColumns.nonEmpty) taintMemory.taintTableAndColums(table, taintedColumns) + + (taintedColumns.nonEmpty, + "\nResult of analyzeInsertStatement: \n" + + " analyzed String: " + normalizedString + " \n " + + taintedColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted") + + } + + def extractInformationOfSelect(normalizedString: String) = { + + val selectPatter01 = raw"(SELECT) (.+) FROM ((\w|-|'|´)+) (WHERE (.+))?; ".r + var tableNameReturn = "" + var columnNamesReturn: Seq[String] = Seq() + + normalizedString match { + case selectPatter01(_, columns, tableName, _, _, condition) => + columnNamesReturn = columns.split(",").map(_.trim).toSeq + tableNameReturn = tableName + case _ => + } + (tableNameReturn, columnNamesReturn) + } + + def analyzeSelectStatment(normalizedString: String) = { + val (table, columns) = extractInformationOfSelect(normalizedString) + var tempColumn: Set[String] = columns.toSet + + if (columns.contains("*")) { + + taintMemory.taintedTableAndColumns.get(table) match { + case Some(x) => tempColumn = x + case None => + } + } + + if (taintMemory.isTainted(table)) taintMemory.taintTableAndColums(table, tempColumn) + val (hastaints, taintedColumns) = taintMemory.columnsAreTainted(table, tempColumn) + + (hastaints, "\nResult of analyzeSelectStatment: \n" + + " analyzed String: " + normalizedString + " \n " + + taintedColumns.mkString("(", ",", ")") + " of table " + table + " where tainted") + } + + def extractInformationOfUpdate(normalizedString: String) = { + + val updatePattern = raw"(UPDATE) (.+) SET (.+) ; ".r + val valueAndConditionPattern = raw"('?\w+'? = '?\w+'? (, '?\w+'? = '?\w+'? )*)(WHERE .+)?".r + + var table = "" + var temp03: Set[(String, String)] = Set() + + normalizedString match { + case updatePattern(_, tableName, valuesAndCondition) => + table = tableName + + valuesAndCondition match { + case valueAndConditionPattern(a, _*) => + val temp = a.split(",").map(_.trim).toSet + temp03 = temp.map(str => str.split("=").map(_.trim)).map(array => (array(0), array(1))) + case _ => + } + case _ => + } + + (table, temp03) + } + + def analyzeUpdateStatment(normalizedString: String) = { + val (table, columnAndValues) = extractInformationOfUpdate(normalizedString) + var taintColumns: Set[String] = Set() + + for ((colmn, value) <- columnAndValues) { + if (taintMemory.isTainted(value) || taintMemory.isTainted(colmn) || taintMemory.isTainted(table)) { + taintColumns += colmn + } + } + + if (taintColumns.nonEmpty) taintMemory.taintTableAndColums(table, taintColumns) + + (taintColumns.nonEmpty, + "\nResult of analyzeUpdateStatment: \n" + + " analyzed String: " + normalizedString + " \n " + + taintColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted") + + } +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala index 34511cff4c..ba79e8f872 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala @@ -1,12 +1,12 @@ /* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.sql -import org.opalj.br.{Method} +import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.{V} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintProblem -import org.opalj.tac.{AITACode,ComputeTACAIKey, Expr, TACMethodParameter, TACode} +import org.opalj.tac.{AITACode, ComputeTACAIKey, Expr, StaticFunctionCall, Stmt, StringConst, TACMethodParameter, TACode, VirtualFunctionCall} import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} import org.opalj.tac.fpcf.properties._ import org.opalj.value.ValueInformation @@ -82,12 +82,90 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { */ override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false + override def normalFlow(statement:JavaStatement,in:TaintFact,predecessor:Option[JavaStatement]): Set[TaintFact] ={ + + + super.normalFlow(statement, in, predecessor) + } + override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { - /* + + /* val callObject = JavaIFDSProblem.asCall(call.stmt) val allParams = callObject.allParams + val allParamsWithIndices = allParams.zipWithIndex + + var facts:Set[TaintFact] = Set() + + for( (param,paramINdex) <- allParamsWithIndices){ + if(callee.descriptor.parameterTypes.head == ObjectType.String){ + val x = getPossibleString(param,call.code,in) + /* println(call.index) + println(in) + println(x) + */ + if(x._1.nonEmpty ){ + facts += StringValue(JavaIFDSProblem.switchParamAndVariableIndex(paramINdex,callee.isStatic),x._1,x._2) + } + } + } + + + def getPossibleString(param:Expr[V], stmts: Array[Stmt[V]],in:TaintFact):Tuple2[Set[String],Boolean] = { + + val defsites = param.asVar.definedBy + var result:Tuple2[Set[String],Boolean] = (Set.empty[String],false) + for (index <- defsites) { + + if(index < 0 )return result + val expr = call.code(index).asAssignment.expr + + val taintStatuts = in match { + case Variable(indexF) => indexF == index + case StringValue(indexF,values,taintStatus) => indexF == index + case _ => false + } + + expr match { + case StaticFunctionCall(pc, declaringClass, isInterface, name, descriptor, params) if name == "source" => + result = (Set("TAINTED"), true) + + case StringConst(_, v) => result = (Set(v), false) + + case VirtualFunctionCall(pc,declaringClass,isInterface,name,descriptor,receiver,params) if name == "toString" => + val tmp = getPossibleString(receiver.asVar, stmts, in) + + result = (tmp._1,tmp._2 || taintStatuts ) + + case VirtualFunctionCall(pc,declaringClass,isInterface,name,descriptor,receiver,params) if name == "append" => + val leftSideString = getPossibleString(receiver.asVar, stmts, in) + val rightSideString = getPossibleString(params.head.asVar, stmts, in) + var possibleString: Set[String] = Set() + + for { + l <- leftSideString._1 + r <- rightSideString._1 + } { + possibleString += l + r + } + result = (possibleString, leftSideString._2 || rightSideString._2 || taintStatuts) + + case _ => result = (Set(""), false) + } + + } + result + } + + + + /* + val callObject = JavaIFDSProblem.asCall(call.stmt) + val allParams = callObject.allParams val allParamsWithIndices = allParams.zipWithIndex + + in match { case BindingFact(index, keyName) => allParamsWithIndices.flatMap { case (param, paramIndex) if param.asVar.definedBy.contains(index) => @@ -101,12 +179,30 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { } */ + if(callee.name == "sink")Set.empty + else super.callFlow(call, callee, in) + + */ super.callFlow(call, callee, in) } override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { + /* + println(call.method) + println(exit.method) + println(callFact) + println(in) + println(call.method) + println(exit.method) + + + + */ + super.returnFlow(exit, in, call, callFact, successor) + + /* if (!isPossibleReturnFlow(exit, successor)) return Set.empty val callee = exit.callable @@ -115,25 +211,25 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { val allParams = callStatement.allParams in match { - case BindingFact(index, keyName) => + case StringValue(index, values, taintStatus) => var flows: Set[TaintFact] = Set.empty if (index < 0 && index > -100) { val param = allParams( JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) ) - flows ++= param.asVar.definedBy.map(i => BindingFact(i, keyName)) + flows ++= param.asVar.definedBy.map(i => StringValue(i, values, taintStatus)) } if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy if (returnValueDefinedBy.contains(index)) - flows += BindingFact(call.index, keyName) + flows += StringValue(call.index, values, taintStatus) } flows + case _ => super.returnFlow(exit, in, call, callFact, successor) } - */ - super.returnFlow(exit, in, call, callFact, successor) + */ } @@ -157,7 +253,109 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { - super.callToReturnFlow(call, in, successor) + /* + val callObject = JavaIFDSProblem.asCall(call.stmt) + + var flow:Set[TaintFact] = Set.empty + + if(callObject.name =="append") { + val leftsideVar = callObject.receiverOption.get.asVar + val rightsideVar = callObject.params.head.asVar + flow += in + + in match { + case Variable(index) => + if(leftsideVar.definedBy.contains(index) || rightsideVar.definedBy.contains(index)) flow += Variable(call.index) + case _ => flow += in + } + } + + if(callObject.name =="toString") { + val leftsideVar = callObject.receiverOption.get.asVar + flow += in + + in match { + case Variable(index) => + if(leftsideVar.definedBy.contains(index)) flow += Variable(call.index) + case _ => flow += in + } + } + + + if(callObject.name == "sink"){ + + in match { + case Variable(index) if callObject.params.head.asVar.definedBy.contains(index) => + + val calleeFact = Variable( + JavaIFDSProblem.switchParamAndVariableIndex(0, call.callable.isStatic) + ) + println("hier 008") + val flowFact = createFlowFact(call.callable, call, calleeFact) + if (flowFact.isDefined) { + flow += flowFact.get + + } + + case _=> + } + flow + + }else + + + */ + var flow:Set[TaintFact] = Set.empty + val callObject = JavaIFDSProblem.asCall(call.stmt) + if(callObject.name =="append"){ + flow += in + val rightsideVar = callObject.params.head.asVar + val leftsideVar = callObject.receiverOption.get.asVar + in match { + case Variable(index) => + if(leftsideVar.definedBy.contains(index)||rightsideVar.definedBy.contains(index)) flow += Variable(call.index) + case _=> + } + flow + } else if(callObject.name =="toString"){ + val leftsideVar = callObject.receiverOption.get.asVar + flow += in + + in match { + case Variable(index) => + if(leftsideVar.definedBy.contains(index)) flow += Variable(call.index) + case _ => + } + flow + }else if(callObject.name =="executeUpdate"){ + flow += in + val possibleParamStrings = getPossibleString(callObject.params(0),call.code,in) + if(possibleParamStrings._2){ + possibleParamStrings._1.foreach(input => + SqlStringTaintAnalyzer.doAnalyze(input) + ) + } + flow + } else if(callObject.name =="executeQuery"){ + flow += in + val possibleParamStrings = getPossibleString(callObject.params(0),call.code,in) + possibleParamStrings._1.foreach(input => { + val sqlResult = SqlStringTaintAnalyzer.doAnalyze(input) + if(sqlResult && call.stmt.isAssignment){ + flow += Variable(call.index) + } + }) + flow + } + else{ + icfg.getCalleesIfCallStatement(call) match { + case Some(callee) if(callee.isEmpty) => + Set(in) + case _ => super.callToReturnFlow(call, in, successor) + } + } + + /* val callStmt = JavaIFDSProblem.asCall(call.stmt) // val allParams = callStmt.allParams @@ -252,9 +450,64 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { */ } + /* + + + def getPossibleStrings(call: JavaStatement,defSites: IntTrieSet):Set[String]={ + + } + + + */ + + def getPossibleString(param:Expr[V], stmts: Array[Stmt[V]],in:TaintFact):(Set[String], Boolean) = { + val defSites = param.asVar.definedBy + var result: (Set[String], Boolean) = (Set(""), false) + for (defSiteIndex <- defSites) { + if (defSiteIndex >= 0) { + val expr = stmts(defSiteIndex).asAssignment.expr + + val taintStatuts = in match { + case Variable(index) => index == defSiteIndex + case StringValue(index, values, taintStatus) => index == defSiteIndex + case _ => false + } + expr match { + case StaticFunctionCall(pc, declaringClass, isInterface, name, descriptor, params) if name == "source" => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + + case StringConst(_, value) => + result = (result._1 ++ Set(value), result._2 ||taintStatuts) + + case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => + val tmp = getPossibleString(receiver.asVar, stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 || taintStatuts) + + case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => + val leftSideString = getPossibleString(receiver.asVar, stmts, in) + val rightSideString = getPossibleString(params.head.asVar, stmts, in) + var possibleString: Set[String] = Set() + + for { + leftString <- leftSideString._1 + rightString <- rightSideString._1 + } { + possibleString += leftString + rightString + } + result = (result._1 ++ possibleString, result._2 || leftSideString._2 || rightSideString._2 || taintStatuts) + + case _ => + } + } + } + result + } + //TODO + // Stringfacts + // AppendMethode //Get Strings used by foo bar. //Handle Concatenation. //put the Strings in the Prototype diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala new file mode 100644 index 0000000000..7fd3a99c58 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala @@ -0,0 +1,86 @@ + +package org.opalj.tac.fpcf.analyses.sql + +import org.opalj.br.Method +import org.opalj.br.analyses.SomeProject +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem +import org.opalj.tac.fpcf.properties.TaintFact + +class SqlTaintProblem02 (project: SomeProject) extends JavaIFDSProblem[TaintFact](project) with TaintProblem[Method, JavaStatement, TaintFact]{ + /** + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. + * + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = ??? + + /** + * Called in callToReturnFlow. This method can return whether the input fact + * will be removed after `callee` was called. I.e. the method could sanitize parameters. + * + * @param call The call statement. + * @param in The fact which holds before the call. + * @return Whether in will be removed after the call. + */ + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = ??? + + /** + * The null fact of this analysis. + */ + override def nullFact: TaintFact = ??? + + /** + * The entry points of this analysis. + */ + override def entryPoints: Seq[(Method, TaintFact)] = ??? + + /** + * Computes the data flow for a normal statement. + * + * @param statement The analyzed statement. + * @param in The fact which holds before the execution of the `statement`. + * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be + * computed. Used for phi statements to distinguish the flow. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + override def normalFlow(statement: JavaStatement, in: TaintFact, predecessor: Option[JavaStatement]): Set[TaintFact] = ??? + + /** + * Computes the data flow for a call to start edge. + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @param source The entity, which is analyzed. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = ??? + + /** + * Computes the data flow for an exit to return edge. + * + * @param call The statement, which called the `callee`. + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = ??? + + /** + * Computes the data flow for a call to return edge. + * + * @param call The statement, which invoked the call. + * @param in The facts, which hold before the `call`. + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. + */ + override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = ??? +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/TaintMemorySQL.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/TaintMemorySQL.scala new file mode 100644 index 0000000000..8079bffc85 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/TaintMemorySQL.scala @@ -0,0 +1,41 @@ + +package org.opalj.tac.fpcf.analyses.sql + +class TaintMemorySQL(dummyTaintRecognitionWord:String) { + + var tainted: Set[String] = Set(dummyTaintRecognitionWord) + var taintedTableAndColumns = scala.collection.mutable.Map[String, Set[String]]() + + def taint(toTaintedElement: String): Unit = { + tainted += toTaintedElement + } + + def isTainted(toInspectElement: String): Boolean = { + tainted.contains(toInspectElement) + } + + def clearMemory(): Unit = { + tainted = Set.empty[String] + } + + def taintTableAndColums(tableName: String, columns: Set[String]): Unit = { + val prev = taintedTableAndColumns.get(tableName) + + prev match { + case Some(x) => taintedTableAndColumns.put(tableName, x ++ columns) + case None => taintedTableAndColumns.put(tableName, columns) + } + } + + def columnsAreTainted(tableName: String, columns: Set[String]) = { + val empty: Set[String] = Set() + + if (taintedTableAndColumns.contains(tableName)) { + taintedTableAndColumns.get(tableName) match { + case Some(x) => ((x & columns).size > 0, (x & columns)) + case None => (false, empty) + } + } else (false, empty) + } + +} diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index a46d0e7ee6..7ac314f19d 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -158,7 +158,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < implicit var statistics = new Statistics val icfg = ifdsProblem.icfg - val DEBUG = false + val DEBUG = true /** * Performs an IFDS analysis for a method-fact-pair. @@ -248,7 +248,8 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < implicit val work = worklist.dequeue() // ifds line 11 val (statement, in, predecessor) = work icfg.getCalleesIfCallStatement(statement) match { - case Some(callees) => handleCall(statement, callees, in) // ifds line 13 + case Some(callees) => + handleCall(statement, callees, in) // ifds line 13 case None => { if (icfg.isExitStatement(statement)) handleExit(statement, in) // ifds line 21 // in case of exceptions exit statements may also have some normal flow so no else here diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala index 7ea0f6a7f7..b75afe37a2 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/analyses/ifds/taint/ForwardTaintProblem.scala @@ -215,6 +215,9 @@ abstract class ForwardTaintProblem(project: SomeProject) } } + + + if (icfg.getCalleesIfCallStatement(call).isEmpty) { // If the call does not have any callees, the code is unknown // and we safely handle it as the identity diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala index 1dcd7ddb36..09607b8498 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala @@ -10,6 +10,8 @@ import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} trait TaintFact extends AbstractIFDSFact +trait extraInfor + case object TaintNullFact extends TaintFact with AbstractIFDSNullFact /** From 41d729f1be6e15664e9b569af82e9e28c90c4134 Mon Sep 17 00:00:00 2001 From: Imagee <47892322+Imagee@users.noreply.github.com> Date: Wed, 30 Nov 2022 21:02:23 +0100 Subject: [PATCH 64/67] SqlFacts included --- .../opalj/tac/fpcf/analyses/sql/SQLFact.scala | 2 + .../analyses/sql/SqlStringTaintAnalyzer.scala | 99 ++++--- ...ntMemorySQL.scala => SqlTaintMemory.scala} | 4 +- .../fpcf/analyses/sql/SqlTaintProblem.scala | 274 +++++++++--------- .../fpcf/analyses/sql/SqlTaintProblem02.scala | 106 +++---- .../scala/org/opalj/ifds/IFDSAnalysis.scala | 2 +- .../opalj/tac/fpcf/properties/TaintFact.scala | 1 - 7 files changed, 245 insertions(+), 243 deletions(-) rename DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/{TaintMemorySQL.scala => SqlTaintMemory.scala} (89%) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SQLFact.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SQLFact.scala index 22fcf7940d..e1a4a0e192 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SQLFact.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SQLFact.scala @@ -6,3 +6,5 @@ import org.opalj.tac.fpcf.properties.TaintFact trait SQLFact extends TaintFact case class StringValue(index: Int, values: Set[String], taintStatus: Boolean) extends SQLFact with TaintFact + +case class SqlTaintFact(sqlTaintMemory: SqlTaintMemory) extends TaintFact diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala index ad2094c704..14c435c20a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala @@ -4,63 +4,70 @@ package org.opalj.tac.fpcf.analyses.sql object SqlStringTaintAnalyzer { - var taintMemory = new TaintMemorySQL("TAINTED_VALUE") + var taintMemory = new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'")) + val debugMode = true + def doAnalyze(string:String, taintMemorySQL: SqlTaintMemory): Boolean ={ + taintMemory = taintMemorySQL; + doAnalyze(string) + } + def doAnalyze(string: String) = { - val normalizedString = preprozesing(string) - val insertPatter = raw"(INSERT).*".r - val updatePatter = raw"(UPDATE).*".r - val selectPatter = raw"(SELECT).*".r + + if(string.isBlank) println("yes blank") + if(debugMode)println("String to analyze: " + string) + + val normalizedString = if(!string.isEmpty) preprozesing(string) else "" + val insertPatter = raw"(INSERT).*;.*".r + val updatePatter = raw"(UPDATE).*;.*".r + val selectPatter = raw"(SELECT).*;.*".r + normalizedString match { case i@insertPatter(_*) => - val result = analyzeInsertStatement(i) - if(debugMode) println(result._2) - result._1 + analyzeInsertStatement(i) case u@updatePatter(_*) => - val result = analyzeUpdateStatment(u) - if(debugMode) println(result._2) - result._1 + analyzeUpdateStatement(u) + case s@selectPatter(_*) => - val result = analyzeSelectStatment(s) - if(debugMode) println(result._2) - result._1 + analyzeSelectStatment(s) + case _ => - if(debugMode) println("nothing analyzed") + if(debugMode) println("Result of SqlAnalyzer: Ana does not support: "+ string) false } } def preprozesing(string: String) = { - val (hasCorrectSyntax, sqlString) = filterSQLString(string) - var normalizedString = sqlString - - if (hasCorrectSyntax) { - normalizedString = normalizeSQLString(sqlString) + val hasCorrectSyntax = checkSQLStringSyntax(string) + val normalizedString = if (hasCorrectSyntax) normalizeSQLString(string) else "" + if(debugMode && !hasCorrectSyntax ){ + println("Wrong Syntax :" + string) } normalizedString } - def filterSQLString(string: String) = { + def checkSQLStringSyntax(string: String) = { val word = raw"(\w+|'\w+'|`\w+`)" val specialWord = raw"($word|(\w+(-|\.|\w+)+\w+)|('\w+(-|\.|\w+)+\w+')|(`\w+(-|\.|\w+)+\w+`))" val wordlist = raw"$specialWord(\s*,\s*$specialWord)*" val insertColumn = raw"\(\s*$wordlist\s*\)" val values = raw"\(\s*$wordlist\s*\)(\s*,\s*\(\s*$wordlist\s*\))*" + +//Valid form := INSERT IGNORE* INTO* 'TableName' ( list of Column) VALUES val insertPattern = raw"((INSERT|insert)\s+((IGNORE|ignore)\s+)?(INTO|into)\s+)($specialWord)\s+$insertColumn\s+(VALUES|values)\s+($values)\s*;\s*".r val selectPattern = raw"(SELECT|select)\s+($wordlist|\*)\s+(FROM|from)\s+($specialWord)\s*(\s+(WHERE|where)\s+.+)?;".r val updatePattern = raw"(UPDATE|update)\s+($specialWord)\s+(SET|set)\s+($specialWord\s+=\s+$specialWord)(\s*,\s*$specialWord\s+=\s+$specialWord)*(\s+(WHERE|where) .+)?\s*;\s*".r - string match { - case i@insertPattern(_*) => (true, i) - case s@selectPattern(_*) => (true, s) - case u@updatePattern(_*) => (true, u) - case _ => (false, "") + case insertPattern(_*) => true + case selectPattern(_*) => true + case updatePattern(_*) => true + case _ => false } } @@ -84,6 +91,7 @@ object SqlStringTaintAnalyzer { while (nStr.contains(" ")) { nStr = nStr.replaceAll(" ", " ") } + if(debugMode) println("String normalized to: " + nStr) nStr } @@ -91,21 +99,21 @@ object SqlStringTaintAnalyzer { val insertPatter01 = raw"(INSERT (IGNORE\s)?INTO) (.+) (\( .+ \)) (VALUES) (\(.+\)) ; ".r - var tableNameReturn = "" - var columnNamesReturn: Seq[String] = Seq() - var valuesGroupsReturn: Set[Array[String]] = Set(Array()) + var extractedTableName = "" + var extractedColumnNames: Seq[String] = Seq() + var extractedValueGroups: Set[Array[String]] = Set(Array()) normalizedString match { case insertPatter01(_, _, tableName, columnNames, _, values) => - tableNameReturn = tableName - columnNamesReturn = columnNames.replaceAll(raw"(\(|\))", "").split(",").map(_.trim).toSeq + extractedTableName = tableName + extractedColumnNames = columnNames.replaceAll(raw"(\(|\))", "").split(",").map(_.trim).toSeq val removedBrackets = values.split(raw"\) , \(").map(x => x.replaceAll(raw"(\(|\))", "")) - valuesGroupsReturn = removedBrackets.map(x => x.split(",").map(_.trim)).toSet + extractedValueGroups = removedBrackets.map(x => x.split(",").map(_.trim)).toSet case _ => } - (tableNameReturn, columnNamesReturn, valuesGroupsReturn) + (extractedTableName, extractedColumnNames, extractedValueGroups) } @@ -121,11 +129,11 @@ object SqlStringTaintAnalyzer { if (taintMemory.isTainted(table)) taintedColumns ++= columns if (taintedColumns.nonEmpty) taintMemory.taintTableAndColums(table, taintedColumns) + if(debugMode) println("Result of analyzeInsertStatement: \n" + + " analyzed String: " + normalizedString + " \n " + + taintedColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted \n") - (taintedColumns.nonEmpty, - "\nResult of analyzeInsertStatement: \n" - + " analyzed String: " + normalizedString + " \n " - + taintedColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted") + taintedColumns.nonEmpty } @@ -158,10 +166,11 @@ object SqlStringTaintAnalyzer { if (taintMemory.isTainted(table)) taintMemory.taintTableAndColums(table, tempColumn) val (hastaints, taintedColumns) = taintMemory.columnsAreTainted(table, tempColumn) - - (hastaints, "\nResult of analyzeSelectStatment: \n" + if(debugMode)println("Result of analyzeSelectStatment: \n" + " analyzed String: " + normalizedString + " \n " - + taintedColumns.mkString("(", ",", ")") + " of table " + table + " where tainted") + + taintedColumns.mkString("(", ",", ")") + " of table " + table + " where tainted \n") + + hastaints } def extractInformationOfUpdate(normalizedString: String) = { @@ -188,7 +197,7 @@ object SqlStringTaintAnalyzer { (table, temp03) } - def analyzeUpdateStatment(normalizedString: String) = { + def analyzeUpdateStatement(normalizedString: String) = { val (table, columnAndValues) = extractInformationOfUpdate(normalizedString) var taintColumns: Set[String] = Set() @@ -199,11 +208,11 @@ object SqlStringTaintAnalyzer { } if (taintColumns.nonEmpty) taintMemory.taintTableAndColums(table, taintColumns) + if(debugMode)println("Result of analyzeUpdateStatment: \n" + + " analyzed String: " + normalizedString + " \n " + + taintColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted") - (taintColumns.nonEmpty, - "\nResult of analyzeUpdateStatment: \n" - + " analyzed String: " + normalizedString + " \n " - + taintColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted") + taintColumns.nonEmpty } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/TaintMemorySQL.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala similarity index 89% rename from DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/TaintMemorySQL.scala rename to DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala index 8079bffc85..ee2d637a46 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/TaintMemorySQL.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala @@ -1,9 +1,9 @@ package org.opalj.tac.fpcf.analyses.sql -class TaintMemorySQL(dummyTaintRecognitionWord:String) { +class SqlTaintMemory(dummyTaintRecognitionWords:Set[String]) { - var tainted: Set[String] = Set(dummyTaintRecognitionWord) + var tainted: Set[String] = dummyTaintRecognitionWords var taintedTableAndColumns = scala.collection.mutable.Map[String, Set[String]]() def taint(toTaintedElement: String): Unit = { diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala index ba79e8f872..f72c2ccfe8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala @@ -6,10 +6,9 @@ import org.opalj.br.analyses.SomeProject import org.opalj.ifds.IFDSAnalysis import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintProblem -import org.opalj.tac.{AITACode, ComputeTACAIKey, Expr, StaticFunctionCall, Stmt, StringConst, TACMethodParameter, TACode, VirtualFunctionCall} +import org.opalj.tac.{Expr, GetField, GetStatic, PutField, PutStatic, Stmt, StringConst, VirtualFunctionCall} import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} import org.opalj.tac.fpcf.properties._ -import org.opalj.value.ValueInformation class SqlTaintAnalysis(project: SomeProject) @@ -21,9 +20,12 @@ class SqlTaintAnalysis(project: SomeProject) * @param p project */ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { + /* final type TACAICode = TACode[TACMethodParameter, JavaIFDSProblem.V] val tacaiKey: Method => AITACode[TACMethodParameter, ValueInformation] = p.get(ComputeTACAIKey) + */ + /** * Called, when the exit to return facts are computed for some `callee` with the null fact and * the callee's return value is assigned to a variable. @@ -84,8 +86,11 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { override def normalFlow(statement:JavaStatement,in:TaintFact,predecessor:Option[JavaStatement]): Set[TaintFact] ={ + in match { + case sqlTaintFact:SqlTaintFact=> Set(sqlTaintFact) + case _ => super.normalFlow(statement, in, predecessor) + } - super.normalFlow(statement, in, predecessor) } override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { @@ -183,7 +188,14 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { else super.callFlow(call, callee, in) */ - super.callFlow(call, callee, in) + + in match { + case sqlTaintFact: SqlTaintFact => Set(sqlTaintFact) + case _=> super.callFlow(call, callee, in) + } + + + } override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, @@ -200,7 +212,11 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { */ - super.returnFlow(exit, in, call, callFact, successor) + + in match { + case sqlTaintFact: SqlTaintFact => Set(sqlTaintFact) ++ super.returnFlow(exit, in, call, callFact, successor) + case _=> super.returnFlow(exit, in, call, callFact, successor) + } /* @@ -253,107 +269,75 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { - /* - val callObject = JavaIFDSProblem.asCall(call.stmt) - - var flow:Set[TaintFact] = Set.empty - - if(callObject.name =="append") { - val leftsideVar = callObject.receiverOption.get.asVar - val rightsideVar = callObject.params.head.asVar - flow += in - - in match { - case Variable(index) => - if(leftsideVar.definedBy.contains(index) || rightsideVar.definedBy.contains(index)) flow += Variable(call.index) - case _ => flow += in - } - } - - if(callObject.name =="toString") { - val leftsideVar = callObject.receiverOption.get.asVar - flow += in - - in match { - case Variable(index) => - if(leftsideVar.definedBy.contains(index)) flow += Variable(call.index) - case _ => flow += in - } - } - + var flow:Set[TaintFact] = Set(in) + val callStmt = JavaIFDSProblem.asCall(call.stmt) - if(callObject.name == "sink"){ + callStmt.name match { + case "valueOf" if callStmt.declaringClass.toJava == "java.lang.Integer" => + in match { + case Variable(index) if callStmt.params.find(parm => parm.asVar.definedBy.contains(index)).nonEmpty => flow += Variable(call.index) + case _ => + } + flow - in match { - case Variable(index) if callObject.params.head.asVar.definedBy.contains(index) => - val calleeFact = Variable( - JavaIFDSProblem.switchParamAndVariableIndex(0, call.callable.isStatic) - ) - println("hier 008") - val flowFact = createFlowFact(call.callable, call, calleeFact) - if (flowFact.isDefined) { - flow += flowFact.get + case "append" => + val rightSideVar = callStmt.params.head.asVar + val leftSideVar = callStmt.receiverOption.get.asVar + in match { + case Variable(index) if leftSideVar.definedBy.contains(index)||rightSideVar.definedBy.contains(index) => + flow += Variable(call.index) + case _=> + } + flow - } + case "toString" => + val leftSideVar = callStmt.receiverOption.get.asVar + in match { + case Variable(index) if leftSideVar.definedBy.contains(index) => flow += Variable(call.index) + case _ => + } + flow + + case "executeUpdate" => + val (possibleParamStrings,taintStatus) = getPossibleStringsAndTaintStatus(callStmt.params(0),call.code,in) + if(taintStatus) possibleParamStrings.foreach(input => + if (SqlStringTaintAnalyzer.checkSQLStringSyntax(input) && SqlStringTaintAnalyzer.doAnalyze(input, new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'"))) ) { + flow += SqlTaintFact(SqlStringTaintAnalyzer.taintMemory) + + }) + flow + + case "executeQuery" => + in match { + case sqlTaintFact: SqlTaintFact => + val (possibleParamStrings,_) = getPossibleStringsAndTaintStatus(callStmt.params(0),call.code,in) + possibleParamStrings.foreach(string => { + if(SqlStringTaintAnalyzer.checkSQLStringSyntax(string) + && SqlStringTaintAnalyzer.doAnalyze(string,sqlTaintFact.sqlTaintMemory) + && call.stmt.isAssignment){ + flow += Variable(call.index) + } + }) + case _ => + } + flow - case _=> - } - flow + case _ => + icfg.getCalleesIfCallStatement(call) match { + case Some(callee) if callee.isEmpty => flow + case _ => super.callToReturnFlow(call, in, successor) + } - }else - */ - var flow:Set[TaintFact] = Set.empty - val callObject = JavaIFDSProblem.asCall(call.stmt) - if(callObject.name =="append"){ - flow += in - val rightsideVar = callObject.params.head.asVar - val leftsideVar = callObject.receiverOption.get.asVar - in match { - case Variable(index) => - if(leftsideVar.definedBy.contains(index)||rightsideVar.definedBy.contains(index)) flow += Variable(call.index) - case _=> - } - flow - } else if(callObject.name =="toString"){ - val leftsideVar = callObject.receiverOption.get.asVar - flow += in + //TODO sink case ? + // TODO filter for Analye, statt in der Analyse filtern. + // +1 für Calltiefe => weniger speicher auf heap + // +1 weniger calls + // +1 performance - in match { - case Variable(index) => - if(leftsideVar.definedBy.contains(index)) flow += Variable(call.index) - case _ => - } - flow - }else if(callObject.name =="executeUpdate"){ - flow += in - val possibleParamStrings = getPossibleString(callObject.params(0),call.code,in) - if(possibleParamStrings._2){ - possibleParamStrings._1.foreach(input => - SqlStringTaintAnalyzer.doAnalyze(input) - ) - } - flow - } else if(callObject.name =="executeQuery"){ - flow += in - val possibleParamStrings = getPossibleString(callObject.params(0),call.code,in) - possibleParamStrings._1.foreach(input => { - val sqlResult = SqlStringTaintAnalyzer.doAnalyze(input) - if(sqlResult && call.stmt.isAssignment){ - flow += Variable(call.index) - } - }) - flow } - else{ - icfg.getCalleesIfCallStatement(call) match { - case Some(callee) if(callee.isEmpty) => - Set(in) - case _ => super.callToReturnFlow(call, in, successor) - } - } /* @@ -449,53 +433,75 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { }) */ - } - /* - - - def getPossibleStrings(call: JavaStatement,defSites: IntTrieSet):Set[String]={ - } - - */ - - def getPossibleString(param:Expr[V], stmts: Array[Stmt[V]],in:TaintFact):(Set[String], Boolean) = { + // TODO: use string analysis here + def getPossibleStringsAndTaintStatus(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):(Set[String], Boolean) = { val defSites = param.asVar.definedBy var result: (Set[String], Boolean) = (Set(""), false) for (defSiteIndex <- defSites) { if (defSiteIndex >= 0) { val expr = stmts(defSiteIndex).asAssignment.expr - val taintStatuts = in match { - case Variable(index) => index == defSiteIndex - case StringValue(index, values, taintStatus) => index == defSiteIndex - case _ => false + in match { + case Variable(index) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case ArrayElement(index: Int,_) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case _ => + expr match { + + case StringConst(_, value) => + result = (result._1 ++ Set(value), result._2 ) + + case GetField(_,_,_,_,_) => + // letzte Putfield suchen von dort weiter + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) + if(lastPut >0){ + val putFieldExpression = stmts(lastPut).asPutField.value + val tmp = getPossibleStringsAndTaintStatus(putFieldExpression,stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 ) + } + + + case gstc:GetStatic => + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) + if(lastPut > 0){ + val putExpression = stmts(lastPut).asPutStatic.value + val tmp = getPossibleStringsAndTaintStatus(putExpression,stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 ) + } + + case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => + val tmp = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 ) + + case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => + val leftSideString = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) + val rightSideString = getPossibleStringsAndTaintStatus(params.head.asVar, stmts, in) + var possibleString: Set[String] = Set() + + for { + leftString <- leftSideString._1 + rightString <- rightSideString._1 + } { + possibleString += leftString + rightString + } + result = (result._1 ++ possibleString, result._2 || leftSideString._2 || rightSideString._2) + + case _ => + } } - expr match { - case StaticFunctionCall(pc, declaringClass, isInterface, name, descriptor, params) if name == "source" => + }else{ + in match { + case Variable(index) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case ArrayElement(index: Int,_) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> result = (result._1 ++ Set("TAINTED_VALUE"), true) - - case StringConst(_, value) => - result = (result._1 ++ Set(value), result._2 ||taintStatuts) - - case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => - val tmp = getPossibleString(receiver.asVar, stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 || taintStatuts) - - case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => - val leftSideString = getPossibleString(receiver.asVar, stmts, in) - val rightSideString = getPossibleString(params.head.asVar, stmts, in) - var possibleString: Set[String] = Set() - - for { - leftString <- leftSideString._1 - rightString <- rightSideString._1 - } { - possibleString += leftString + rightString - } - result = (result._1 ++ possibleString, result._2 || leftSideString._2 || rightSideString._2 || taintStatuts) - case _ => } } @@ -504,9 +510,7 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { } - //TODO - // Stringfacts // AppendMethode //Get Strings used by foo bar. //Handle Concatenation. diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala index 7fd3a99c58..53ca2bddb2 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala @@ -3,84 +3,72 @@ package org.opalj.tac.fpcf.analyses.sql import org.opalj.br.Method import org.opalj.br.analyses.SomeProject -import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaStatement} -import org.opalj.tac.fpcf.analyses.ifds.taint.TaintProblem -import org.opalj.tac.fpcf.properties.TaintFact +import org.opalj.ifds.IFDSAnalysis +import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintProblem +import org.opalj.tac.fpcf.properties.{FlowFact, Taint, TaintFact, TaintNullFact, Variable} -class SqlTaintProblem02 (project: SomeProject) extends JavaIFDSProblem[TaintFact](project) with TaintProblem[Method, JavaStatement, TaintFact]{ - /** - * Checks, if some `callee` is a sanitizer, which sanitizes its return value. - * In this case, no return flow facts will be created. - * - * @param callee The method, which was called. - * @return True, if the method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: Method): Boolean = ??? +class SqlTaintAnalysis02(project: SomeProject) + extends IFDSAnalysis()(project, new SqlTaintProblem02(project), Taint) +class SqlTaintProblem02(p: SomeProject) extends ForwardTaintProblem(p){ /** - * Called in callToReturnFlow. This method can return whether the input fact - * will be removed after `callee` was called. I.e. the method could sanitize parameters. + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a variable. + * Creates a taint, if necessary. * - * @param call The call statement. - * @param in The fact which holds before the call. - * @return Whether in will be removed after the call. + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. */ - override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = ??? + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty /** - * The null fact of this analysis. + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. */ - override def nullFact: TaintFact = ??? + override protected def createFlowFact(callee: Method, call: JavaStatement, in: TaintFact): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) + Some(FlowFact(Seq(JavaMethod(call.method), JavaMethod(callee)))) + else None /** * The entry points of this analysis. */ - override def entryPoints: Seq[(Method, TaintFact)] = ??? - - /** - * Computes the data flow for a normal statement. - * - * @param statement The analyzed statement. - * @param in The fact which holds before the execution of the `statement`. - * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be - * computed. Used for phi statements to distinguish the flow. - * @return The facts, which hold after the execution of `statement` under the assumption - * that the facts in `in` held before `statement` and `successor` will be - * executed next. - */ - override def normalFlow(statement: JavaStatement, in: TaintFact, predecessor: Option[JavaStatement]): Set[TaintFact] = ??? + override def entryPoints: Seq[(Method, TaintFact)] = + for { + m <- p.allMethodsWithBody + if m.name == "main" + } yield m -> TaintNullFact /** - * Computes the data flow for a call to start edge. + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. * - * @param call The analyzed call statement. - * @param callee The called method, for which the data flow shall be computed. - * @param in The fact which holds before the execution of the `call`. - * @param source The entity, which is analyzed. - * @return The facts, which hold after the execution of `statement` under the assumption that - * the facts in `in` held before `statement` and `statement` calls `callee`. + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. */ - override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = ??? + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" /** - * Computes the data flow for an exit to return edge. + * Called in callToReturnFlow. This method can return whether the input fact + * will be removed after `callee` was called. I.e. the method could sanitize parameters. * - * @param call The statement, which called the `callee`. - * @param exit The statement, which terminated the `callee`. - * @param in The fact which holds before the execution of the `exit`. - * @return The facts, which hold after the execution of `exit` in the caller's context - * under the assumption that `in` held before the execution of `exit` and that - * `successor` will be executed next. + * @param call The call statement. + * @param in The fact which holds before the call. + * @return Whether in will be removed after the call. */ - override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = ??? + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false - /** - * Computes the data flow for a call to return edge. - * - * @param call The statement, which invoked the call. - * @param in The facts, which hold before the `call`. - * @return The facts, which hold after the call independently of what happens in the callee - * under the assumption that `in` held before `call`. - */ - override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = ??? + override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { + print("index: "+call.index +" ") + println(in) + super.callToReturnFlow(call, in, successor) + } } diff --git a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala index 7ac314f19d..216ce58160 100644 --- a/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala +++ b/OPAL/ifds/src/main/scala/org/opalj/ifds/IFDSAnalysis.scala @@ -158,7 +158,7 @@ class IFDSAnalysis[IFDSFact <: AbstractIFDSFact, C <: AnyRef, S <: Statement[_ < implicit var statistics = new Statistics val icfg = ifdsProblem.icfg - val DEBUG = true + val DEBUG = false /** * Performs an IFDS analysis for a method-fact-pair. diff --git a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala index 09607b8498..6ee0e541df 100644 --- a/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala +++ b/OPAL/tac/src/main/scala/org/opalj/tac/fpcf/properties/TaintFact.scala @@ -10,7 +10,6 @@ import org.opalj.ifds.{AbstractIFDSFact, AbstractIFDSNullFact, Callable} trait TaintFact extends AbstractIFDSFact -trait extraInfor case object TaintNullFact extends TaintFact with AbstractIFDSNullFact From 16a2b852a5f52e2606520de3fb9bb625d9e07e6a Mon Sep 17 00:00:00 2001 From: Imagee <47892322+Imagee@users.noreply.github.com> Date: Wed, 11 Jan 2023 23:25:34 +0100 Subject: [PATCH 65/67] evaluations --- .../org/opalj/fpcf/analyses/Playground.scala | 62 ++++-- .../analyses/sql/SqlStringTaintAnalyzer.scala | 202 ++++++++++++------ .../fpcf/analyses/sql/SqlTaintMemory.scala | 11 +- .../fpcf/analyses/sql/SqlTaintProblem.scala | 141 ++++++++++-- 4 files changed, 307 insertions(+), 109 deletions(-) diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/Playground.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/Playground.scala index 8989f646cf..0fae2292a8 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/Playground.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/fpcf/analyses/Playground.scala @@ -4,7 +4,7 @@ package org.opalj.fpcf.analyses import org.opalj.br.analyses.{BasicReport, Project, ProjectAnalysisApplication} //import org.opalj.tac.LazyDetachedTACAIKey //import org.opalj.tac.cg.CFA_1_0_CallGraphKey -import org.opalj.tac.fpcf.analyses.sql.SqlStringTaintAnalyzer +//import org.opalj.tac.fpcf.analyses.sql.SqlStringTaintAnalyzer import java.net.URL @@ -30,23 +30,61 @@ object Playground extends ProjectAnalysisApplication{ //val tac = tacProvider(m) //println(tac.cfg) - val x = "TAINTED_VALUE" - val insert = "INSERT INTO tableA ( Id, name ) VALUES ( 01," + x + " );" - println("bei: ") - println(insert) - println(SqlStringTaintAnalyzer.doAnalyze(insert)) + val insertStatement = " INSERT INTO students (name,course, studID )VALUES ('Freddy' , 'Informatik', 'tainted') ;" - val select = "SELECT * FROM tableA ;"; + val x = a.normalizeSQLString(insertStatement) + analyzeInsert(x) + + } + + "" + } + + def analyzeInsert(insertStatement: String): Boolean = { + val pattern = raw"(INSERT).*(INTO)\s+(\w+)\s+(([\w\s,]+))\s+(VALUES)\s+([(\w\s,)]+);".r + val pattern(command, _, table, columns, _, values) = a.normalizeSQLString(insertStatement) + + println(command) + println(table) + println(columns) + println(values) + + true + } - val ergebnis = SqlStringTaintAnalyzer.doAnalyze(select) - println("ergebnis:") - println(ergebnis) + object a { + val WORD = raw"(\w+|'\w+'|`\w+`)" + val SPECIAL_WORD = raw"($WORD|(\w+(-|\.|\w+)+\w+)|('\w+(-|\.|\w+)+\w+')|(`\w+(-|\.|\w+)+\w+`))" + val WORD_LIST = raw"$SPECIAL_WORD(\s*,\s*$SPECIAL_WORD)*" + val INSERT_COLUMN = raw"\(\s*$WORD_LIST\s*\)" + val VALUES = raw"\(\s*$WORD_LIST\s*\)(\s*,\s*\(\s*$WORD_LIST\s*\))*" + val INSERT_PATTERN = raw"((INSERT|insert)\s+((IGNORE|ignore)\s+)?(INTO|into)\s+)($SPECIAL_WORD)\s+$INSERT_COLUMN\s+(VALUES|values)\s+($VALUES)\s*;\s*".r + val SELECT_PATTERN = raw"(SELECT|select)\s+($WORD_LIST|\*)\s+(FROM|from)\s+($SPECIAL_WORD)\s*(\s+(WHERE|where)\s+.+)?;".r + val UPDATE_PATTERN = raw"(UPDATE|update)\s+($SPECIAL_WORD)\s+(SET|set)\s+($SPECIAL_WORD\s+=\s+$SPECIAL_WORD)(\s*,\s*$SPECIAL_WORD\s+=\s+$SPECIAL_WORD)*(\s+(WHERE|where) .+)?\s*;\s*".r - // println(callGraph) + + def checkSQLStringSyntax(string: String) = { + string match { + case INSERT_PATTERN(_*) => true + case SELECT_PATTERN(_*) => true + case UPDATE_PATTERN(_*) => true + case _ => false + } } - "" + def normalizeSQLString(sqlString: String): String = { + + val pattern1 = raw"(\s+)" + val pattern2 = raw"([,;=()])" + val replacement1 = " " + val replacement2 = " $1 " + + val sb = new StringBuilder(sqlString) + sb.replace(0, sb.length(), sb.toString().replaceAll(pattern1, replacement1)) + sb.replace(0, sb.length(), sb.toString().replaceAll(pattern2, replacement2)) + sb.toString().toUpperCase().trim() + } } } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala index 14c435c20a..8eeb8a7e06 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala @@ -3,6 +3,27 @@ package org.opalj.tac.fpcf.analyses.sql object SqlStringTaintAnalyzer { + //Regex Pattern:{ + + /* + val WORD = raw"(\w+|'\w+'|`\w+`)" + val SPECIAL_WORD = raw"($WORD|(\w+(-|\.|\w+)+\w+)|('\w+(-|\.|\w+)+\w+')|(`\w+(-|\.|\w+)+\w+`))" + val WORD_LIST = raw"$SPECIAL_WORD(\s*,\s*$SPECIAL_WORD)*" + val INSERT_COLUMN = raw"\(\s*$WORD_LIST\s*\)" + val VALUES = raw"\(\s*$WORD_LIST\s*\)(\s*,\s*\(\s*$WORD_LIST\s*\))*" + val INSERT_PATTERN = raw"((INSERT|insert)\s+((IGNORE|ignore)\s+)?(INTO|into)\s+)($SPECIAL_WORD)\s+$INSERT_COLUMN\s+(VALUES|values)\s+($VALUES)\s*;\s*".r + val SELECT_PATTERN = raw"(SELECT|select)\s+($WORD_LIST|\*)\s+(FROM|from)\s+($SPECIAL_WORD)\s*(\s+(WHERE|where)\s+.+)?;".r + val UPDATE_PATTERN = raw"(UPDATE|update)\s+($SPECIAL_WORD)\s+(SET|set)\s+($SPECIAL_WORD\s+=\s+$SPECIAL_WORD)(\s*,\s*$SPECIAL_WORD\s+=\s+$SPECIAL_WORD)*(\s+(WHERE|where) .+)?\s*;\s*".r + + + val INSERT_PATTERN02 = "^INSERT\\s+(IGNORE\\s+)?INTO\\s+[\\w\\d_]+\\s+\\(([\\w\\d_]+(,\\s)?)+\\)\\s+VALUES\\s+\\((.+)(,\\s.+)*\\)\\s*;?$".r +} + */ + + val INSERT_PATTERN = "^\\s*(?i)INSERT\\s+(IGNORE\\s+)?(?i)INTO\\s+([^\\s(]+)\\s*\\(([^\\)]+)\\)\\s*(?i)VALUES\\s*(?:\\(([^\\)]+)\\)\\s*,\\s*)*\\(([^\\)]+)\\)\\s*$".r + val SELECT_PATTERN = "^\\s*(?i)SELECT\\s+(.+)\\s+(?i)FROM\\s+([^\\s]+)(?:\\s+(?i)WHERE\\s+(.+))?\\s*$".r + val UPDATE_PATTERN = "^\\s*(?i)UPDATE\\s+([^\\s]+)\\s+(?i)SET\\s+(.+)\\s+(?i)WHERE\\s+(.+)\\s*$".r + var taintMemory = new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'")) @@ -19,30 +40,26 @@ object SqlStringTaintAnalyzer { if(string.isBlank) println("yes blank") if(debugMode)println("String to analyze: " + string) - val normalizedString = if(!string.isEmpty) preprozesing(string) else "" - val insertPatter = raw"(INSERT).*;.*".r - val updatePatter = raw"(UPDATE).*;.*".r - val selectPatter = raw"(SELECT).*;.*".r - + val normalizedString = if(!string.isBlank) preprozesing(string) else "" normalizedString match { - case i@insertPatter(_*) => + case i@INSERT_PATTERN(_*) => analyzeInsertStatement(i) - case u@updatePatter(_*) => + case u@UPDATE_PATTERN(_*) => analyzeUpdateStatement(u) - case s@selectPatter(_*) => + case s@SELECT_PATTERN(_*) => analyzeSelectStatment(s) case _ => - if(debugMode) println("Result of SqlAnalyzer: Ana does not support: "+ string) + if(debugMode) println("Result of SqlAnalyzer: SqlAnalyzer does not support: "+ string) false } } def preprozesing(string: String) = { - val hasCorrectSyntax = checkSQLStringSyntax(string) + val hasCorrectSyntax = checkSQLStringSyntax3(string) val normalizedString = if (hasCorrectSyntax) normalizeSQLString(string) else "" if(debugMode && !hasCorrectSyntax ){ println("Wrong Syntax :" + string) @@ -50,85 +67,70 @@ object SqlStringTaintAnalyzer { normalizedString } - def checkSQLStringSyntax(string: String) = { - val word = raw"(\w+|'\w+'|`\w+`)" - val specialWord = raw"($word|(\w+(-|\.|\w+)+\w+)|('\w+(-|\.|\w+)+\w+')|(`\w+(-|\.|\w+)+\w+`))" - val wordlist = raw"$specialWord(\s*,\s*$specialWord)*" - val insertColumn = raw"\(\s*$wordlist\s*\)" - val values = raw"\(\s*$wordlist\s*\)(\s*,\s*\(\s*$wordlist\s*\))*" + def checkSQLStringSyntax3(input: String): Boolean = { - -//Valid form := INSERT IGNORE* INTO* 'TableName' ( list of Column) VALUES - val insertPattern = raw"((INSERT|insert)\s+((IGNORE|ignore)\s+)?(INTO|into)\s+)($specialWord)\s+$insertColumn\s+(VALUES|values)\s+($values)\s*;\s*".r - val selectPattern = raw"(SELECT|select)\s+($wordlist|\*)\s+(FROM|from)\s+($specialWord)\s*(\s+(WHERE|where)\s+.+)?;".r - val updatePattern = raw"(UPDATE|update)\s+($specialWord)\s+(SET|set)\s+($specialWord\s+=\s+$specialWord)(\s*,\s*$specialWord\s+=\s+$specialWord)*(\s+(WHERE|where) .+)?\s*;\s*".r - - string match { - case insertPattern(_*) => true - case selectPattern(_*) => true - case updatePattern(_*) => true + input match { + case INSERT_PATTERN(_*) => true + case SELECT_PATTERN(_*) => true + case UPDATE_PATTERN(_*) => true case _ => false } } def normalizeSQLString(sqlString: String): String = { - var nStr = sqlString.replaceAll(" ", " ") - - nStr = nStr.replaceAll(",", " , ") - nStr = nStr.replaceAll(";", " ; ") - nStr = nStr.replaceAll("=", " = ") - nStr = nStr.replaceAll(raw"\(", " ( ") - nStr = nStr.replaceAll(raw"\)", " ) ") - nStr = nStr.replace("select", "SELECT") - nStr = nStr.replace("from", "FROM") - nStr = nStr.replace("where", "WHERE") - nStr = nStr.replace("values", "VALUES") - nStr = nStr.replace("insert", "INSERT") - nStr = nStr.replace("into", "INTO") - nStr = nStr.replace("update", "UPDATE") - nStr = nStr.replace("set", "SET") - - while (nStr.contains(" ")) { - nStr = nStr.replaceAll(" ", " ") - } - if(debugMode) println("String normalized to: " + nStr) - nStr + + val sb = new StringBuilder(sqlString) + val pattern1 = raw"(\s+)" + val pattern2 = raw"([,;=()])" + val replacement1 = " " + val replacement2 = " $1 " + + // Doppelte Leerzeichen entfernen + sb.replace(0, sb.length(), sb.toString().replaceAll(pattern1, replacement1)) + + // Sonderzeichen durch ein Leerzeichen umgeben + sb.replace(0, sb.length(), sb.toString().replaceAll(pattern2, replacement2)) + + // Groß-/Kleinschreibung normalisieren und führenden/folgnde leerzeichen entfernen + val normalizedStrig = sb.toString().toUpperCase().trim + + if(debugMode) println("String normalized to: " + normalizedStrig) + normalizedStrig } - def extractInformationOfInsert(normalizedString: String) = { + def extractInformationOfInsert4(query: String): (String, Seq[String], Seq[Seq[String]]) = { + val insertPattern = "(?i)INSERT\\s*(?:IGNORE\\s+)?INTO\\s*(\\w+)\\s*\\((.*?)\\)\\s*VALUES\\s*(.*);?".r - val insertPatter01 = raw"(INSERT (IGNORE\s)?INTO) (.+) (\( .+ \)) (VALUES) (\(.+\)) ; ".r var extractedTableName = "" var extractedColumnNames: Seq[String] = Seq() - var extractedValueGroups: Set[Array[String]] = Set(Array()) - + var extractedValueGroups: Seq[Seq[String]] = Seq() - normalizedString match { - case insertPatter01(_, _, tableName, columnNames, _, values) => + query match { + case insertPattern(tableName, columns, values) => extractedTableName = tableName - extractedColumnNames = columnNames.replaceAll(raw"(\(|\))", "").split(",").map(_.trim).toSeq - val removedBrackets = values.split(raw"\) , \(").map(x => x.replaceAll(raw"(\(|\))", "")) - extractedValueGroups = removedBrackets.map(x => x.split(",").map(_.trim)).toSet + extractedColumnNames = columns.split(",").map(_.trim).toSeq + val removedBrackets = values.split("\\)\\s*,\\s*\\(").map(x => x.replaceAll(raw"(\(|\))", "")) + extractedValueGroups = removedBrackets.map(x => x.split(",").map(_.trim).toSeq).toSeq + case _ => } - (extractedTableName, extractedColumnNames, extractedValueGroups) - } + def analyzeInsertStatement(normalizedString: String) = { - val (table, columns, valueGroups) = extractInformationOfInsert(normalizedString) + val (table, columns, valueGroups) = extractInformationOfInsert4(normalizedString) var taintedColumns: Set[String] = columns.filter(column => taintMemory.isTainted(column)).toSet - valueGroups.foreach(array => { - for (i <- array.indices) { - if (taintMemory.isTainted(array(i))) taintedColumns += columns(i) + valueGroups.foreach(valueGroup => { + for (i <- valueGroup.indices) { + if (taintMemory.isTainted(valueGroup (i))) taintedColumns += columns(i).trim } }) - if (taintMemory.isTainted(table)) taintedColumns ++= columns - if (taintedColumns.nonEmpty) taintMemory.taintTableAndColums(table, taintedColumns) + if (taintMemory.isTainted(table)) taintedColumns ++= columns.map(str => str.trim) + if (taintedColumns.nonEmpty) taintMemory.taintTableAndColums(table.trim, taintedColumns) if(debugMode) println("Result of analyzeInsertStatement: \n" + " analyzed String: " + normalizedString + " \n " + taintedColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted \n") @@ -137,12 +139,11 @@ object SqlStringTaintAnalyzer { } - def extractInformationOfSelect(normalizedString: String) = { + def extractInformationOfSelect(normalizedString: String): (String, Seq[String]) ={ val selectPatter01 = raw"(SELECT) (.+) FROM ((\w|-|'|´)+) (WHERE (.+))?; ".r var tableNameReturn = "" var columnNamesReturn: Seq[String] = Seq() - normalizedString match { case selectPatter01(_, columns, tableName, _, _, condition) => columnNamesReturn = columns.split(",").map(_.trim).toSeq @@ -152,8 +153,23 @@ object SqlStringTaintAnalyzer { (tableNameReturn, columnNamesReturn) } + def extractInformationOfSelect2(normalizedString: String): (String, Seq[String]) = { + val selectRegex = "SELECT\\s+(DISTINCT\\s+)?(.*?)\\s+FROM\\s+([^\\s]+)\\s*(WHERE\\s+(.*))?".r + var tableNameReturn = "" + var columnNamesReturn: Seq[String] = Seq() + selectRegex.findFirstMatchIn(normalizedString) match { + case Some(str) => + columnNamesReturn = str.group(2).split(",").map(_.trim).toSeq + tableNameReturn = str.group(3) + case _ => + } + (tableNameReturn, columnNamesReturn) + } + + + def analyzeSelectStatment(normalizedString: String) = { - val (table, columns) = extractInformationOfSelect(normalizedString) + val (table, columns) = extractInformationOfSelect2(normalizedString) var tempColumn: Set[String] = columns.toSet if (columns.contains("*")) { @@ -197,8 +213,26 @@ object SqlStringTaintAnalyzer { (table, temp03) } + def extractInformationOfUpdate2(normalizedString: String): (String, Seq[(String, String)]) = { + val updateRegex = "UPDATE\\s+([^\\s]+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+?)\\s*;?\\s*$".r + + var table = "" + var columnValues: Seq[(String, String)] = Seq() + + updateRegex.findFirstMatchIn(normalizedString) match { + case Some(m) => + table = m.group(1) + columnValues = m.group(2).split(",").map(_.trim).toIndexedSeq.map { value => + val parts = value.split("=").map(_.trim) + (parts(0), parts(1)) + } + case _ => + } + (table, columnValues) + } + def analyzeUpdateStatement(normalizedString: String) = { - val (table, columnAndValues) = extractInformationOfUpdate(normalizedString) + val (table, columnAndValues) = extractInformationOfUpdate2(normalizedString) var taintColumns: Set[String] = Set() for ((colmn, value) <- columnAndValues) { @@ -215,4 +249,36 @@ object SqlStringTaintAnalyzer { taintColumns.nonEmpty } + + /* +def extractInformationOfInsert(normalizedString: String) = { + + val insertPatter01 = raw"(INSERT (IGNORE\s)?INTO) (.+) (\( .+ \)) (VALUES) (\(.+\)) ; ".r + + val columnNamesRegex = "\\(([^)]*)\\)".r + val valuesRegex = "VALUES\\s*\\(([^)]*)\\)".r + val insertRegex = raw"(INSERT (IGNORE\s)?INTO) (.+) (\( .+ \)) (VALUES) (\(.+\)) ; ".r + + + var extractedTableName = "" + var extractedColumnNames: Seq[String] = Seq() + var extractedValueGroups: Set[Array[String]] = Set(Array()) + + + normalizedString match { + case insertPatter01(_, _, tableName, columnNames, _, values) => + extractedTableName = tableName + extractedColumnNames = columnNames.replaceAll(raw"(\(|\))", "").split(",").map(_.trim).toSeq + val removedBrackets = values.split(raw"\) , \(").map(x => x.replaceAll(raw"(\(|\))", "")) + extractedValueGroups = removedBrackets.map(x => x.split(",").map(_.trim)).toSet + case insertRegex(_, _, tableName, columnNamesRegex(columnNames), _, valuesRegex(values)) => + println("klappt") + + case _ => + } + + (extractedTableName, extractedColumnNames, extractedValueGroups) + +} + */ } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala index ee2d637a46..f03e3f916d 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala @@ -30,12 +30,11 @@ class SqlTaintMemory(dummyTaintRecognitionWords:Set[String]) { def columnsAreTainted(tableName: String, columns: Set[String]) = { val empty: Set[String] = Set() - if (taintedTableAndColumns.contains(tableName)) { - taintedTableAndColumns.get(tableName) match { - case Some(x) => ((x & columns).size > 0, (x & columns)) - case None => (false, empty) - } - } else (false, empty) + taintedTableAndColumns.find { case (key, _) => key.trim == tableName.trim } match { + case Some((_, x)) => ((x & columns).size > 0, (x & columns)) + case None => (false, empty) + } } + } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala index f72c2ccfe8..728c549adc 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala @@ -6,7 +6,7 @@ import org.opalj.br.analyses.SomeProject import org.opalj.ifds.IFDSAnalysis import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintProblem -import org.opalj.tac.{Expr, GetField, GetStatic, PutField, PutStatic, Stmt, StringConst, VirtualFunctionCall} +import org.opalj.tac.{ArrayLoad, ArrayStore, Expr, GetField, GetStatic, PutField, PutStatic, Stmt, StringConst, VirtualFunctionCall} import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} import org.opalj.tac.fpcf.properties._ @@ -201,18 +201,6 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { - /* - println(call.method) - println(exit.method) - println(callFact) - println(in) - println(call.method) - println(exit.method) - - - - */ - in match { case sqlTaintFact: SqlTaintFact => Set(sqlTaintFact) ++ super.returnFlow(exit, in, call, callFact, successor) case _=> super.returnFlow(exit, in, call, callFact, successor) @@ -280,6 +268,13 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { } flow + case "intValue" => + val leftSideVar = callStmt.receiverOption.get.asVar + in match { + case Variable(index) if leftSideVar.definedBy.contains(index) => flow += Variable(call.index) + case _ => + } + flow case "append" => val rightSideVar = callStmt.params.head.asVar @@ -300,20 +295,24 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { flow case "executeUpdate" => - val (possibleParamStrings,taintStatus) = getPossibleStringsAndTaintStatus(callStmt.params(0),call.code,in) - if(taintStatus) possibleParamStrings.foreach(input => - if (SqlStringTaintAnalyzer.checkSQLStringSyntax(input) && SqlStringTaintAnalyzer.doAnalyze(input, new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'"))) ) { - flow += SqlTaintFact(SqlStringTaintAnalyzer.taintMemory) - - }) + if(callStmt.params.size >0){ + val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) + possibleParamStrings.foreach(input => + if ( + input.contains("TAINTED_VALUE") && + SqlStringTaintAnalyzer.checkSQLStringSyntax3(input) + && SqlStringTaintAnalyzer.doAnalyze(input, new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'"))) ) { + flow += SqlTaintFact(SqlStringTaintAnalyzer.taintMemory) + }) + } flow case "executeQuery" => in match { case sqlTaintFact: SqlTaintFact => - val (possibleParamStrings,_) = getPossibleStringsAndTaintStatus(callStmt.params(0),call.code,in) + val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) possibleParamStrings.foreach(string => { - if(SqlStringTaintAnalyzer.checkSQLStringSyntax(string) + if(SqlStringTaintAnalyzer.checkSQLStringSyntax3(string) && SqlStringTaintAnalyzer.doAnalyze(string,sqlTaintFact.sqlTaintMemory) && call.stmt.isAssignment){ flow += Variable(call.index) @@ -323,6 +322,23 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { } flow + case "source" => + in match { + case TaintNullFact => flow += Variable(call.index) + case _ => + } + flow + + case "sink" => + in match { + case Variable(index) if callStmt.params.find(parm => parm.asVar.definedBy.contains(index)).nonEmpty => + flow += FlowFact(Seq(JavaMethod(call.method))) + case _=> + } + flow + + + case _ => icfg.getCalleesIfCallStatement(call) match { case Some(callee) if callee.isEmpty => flow @@ -330,7 +346,6 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { } - //TODO sink case ? // TODO filter for Analye, statt in der Analyse filtern. // +1 für Calltiefe => weniger speicher auf heap @@ -479,6 +494,8 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { result = (result._1 ++ tmp._1, result._2 || tmp._2 ) case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => + + val leftSideString = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) val rightSideString = getPossibleStringsAndTaintStatus(params.head.asVar, stmts, in) var possibleString: Set[String] = Set() @@ -502,13 +519,91 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { result = (result._1 ++ Set("TAINTED_VALUE"), true) case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> result = (result._1 ++ Set("TAINTED_VALUE"), true) - case _ => + case _ => result = (result._1 ++ Set("PARAM_VALUE"), false) } } } + result } + def getPossibleStrings2(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):Set[String] = { + // Initialize the set of possible strings and taint status + var possibleStrings: Set[String] = Set() + val defSites = param.asVar.definedBy + + // Durche alle Defsites durchgehen um weitere möglichen Strings zu ermitteln + for (defSiteIndex <- defSites) { + var currentSetOfString: Set[String] = Set() + + in match { + // Wenn der ausdruck für den Intext getaintet ist, ersetzen wir in durch den TAINTIDENTIFIER + case Variable(index) if index == defSiteIndex => + currentSetOfString = Set("TAINTED_VALUE") + case ArrayElement(index: Int,_) if index == defSiteIndex => + currentSetOfString = Set("TAINTED_VALUE") + case InstanceField(index: Int, _, _) if index == defSiteIndex=> + currentSetOfString = Set("TAINTED_VALUE") + case _ if defSiteIndex >= 0 =>{ + val expr = stmts(defSiteIndex).asAssignment.expr + + //Weitere Untersuchung im Ausdruck + expr match { + // Den Wert aus der String Konstante nehmen + case StringConst(_, value) => currentSetOfString = Set(value) + + case GetField(_,_,_,_,_) => + // Um ein Wert zu kriegen suchen wir nach dem letzten PutField + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) + if(lastPut >0){ + val putFieldExpression = stmts(lastPut).asPutField.value + currentSetOfString = getPossibleStrings2(putFieldExpression,stmts, in) + } + + case gstc:GetStatic => + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) + if(lastPut > 0){ + val putExpression = stmts(lastPut).asPutStatic.value + currentSetOfString = getPossibleStrings2(putExpression,stmts, in) + } + + case ArrayLoad(pc,index,arrayRef) => + val elementIndex = index.asVar.value + val lastArrayStoreIndex = stmts.lastIndexWhere(stmt => stmt.astID == ArrayStore.ASTID && + stmt.asArrayStore.index.asVar.value == elementIndex) + if(lastArrayStoreIndex > 0){ + val arrayElementExpression = stmts(lastArrayStoreIndex).asArrayStore.value + currentSetOfString = getPossibleStrings2(arrayElementExpression,stmts,in) + } + + case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => + currentSetOfString = getPossibleStrings2(receiver.asVar, stmts, in) + + case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => + + //Alle möglichen Strings die aus dem receiver gewonnen der funktion append werden können + val leftSideString = getPossibleStrings2(receiver.asVar, stmts, in) + + //Alle möglichen Strings die aus dem parameter gewonnen der funktion append werden können + val rightSideString = getPossibleStrings2(params.head.asVar, stmts, in) + + //Alle Strings die auf der linken seite entstehen können mit allen auf der rechten kombinieren + for { + leftString <- leftSideString + rightString <- rightSideString + } { + currentSetOfString += leftString + rightString + } + case _ => currentSetOfString = Set("") + } + } + case _ => currentSetOfString = Set("PARAM_VALUE") + + } + possibleStrings = possibleStrings ++ currentSetOfString + } + possibleStrings + } //TODO // AppendMethode From e04f4dd8bfa897d0128bd695e1a38e4bd738f5d1 Mon Sep 17 00:00:00 2001 From: Imagee <47892322+Imagee@users.noreply.github.com> Date: Mon, 20 Feb 2023 09:36:53 +0100 Subject: [PATCH 66/67] String Analyzer cleaned --- ...Analyzer.scala => SqlStringAnalyzer.scala} | 40 +- .../analyses/sql/SqlStringAnalyzer02.scala | 284 +++++++++++ .../fpcf/analyses/sql/SqlTaintMemory.scala | 1 - .../fpcf/analyses/sql/SqlTaintMemory02.scala | 66 +++ .../fpcf/analyses/sql/SqlTaintProblem.scala | 428 ++++------------ .../fpcf/analyses/sql/SqlTaintProblem02.scala | 455 +++++++++++++++--- 6 files changed, 857 insertions(+), 417 deletions(-) rename DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/{SqlStringTaintAnalyzer.scala => SqlStringAnalyzer.scala} (87%) create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer02.scala create mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory02.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer.scala similarity index 87% rename from DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala rename to DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer.scala index 8eeb8a7e06..fc288b3adc 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringTaintAnalyzer.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer.scala @@ -1,7 +1,7 @@ package org.opalj.tac.fpcf.analyses.sql -object SqlStringTaintAnalyzer { +object SqlStringAnalyzer { //Regex Pattern:{ @@ -20,16 +20,16 @@ object SqlStringTaintAnalyzer { } */ - val INSERT_PATTERN = "^\\s*(?i)INSERT\\s+(IGNORE\\s+)?(?i)INTO\\s+([^\\s(]+)\\s*\\(([^\\)]+)\\)\\s*(?i)VALUES\\s*(?:\\(([^\\)]+)\\)\\s*,\\s*)*\\(([^\\)]+)\\)\\s*$".r - val SELECT_PATTERN = "^\\s*(?i)SELECT\\s+(.+)\\s+(?i)FROM\\s+([^\\s]+)(?:\\s+(?i)WHERE\\s+(.+))?\\s*$".r + val INSERT_PATTERN = "^\\s*(?i)INSERT\\s*(?:IGNORE\\s+)?INTO\\s*(\\w+)\\s*\\((.*?)\\)\\s*VALUES\\s*(.*);?".r + val SELECT_PATTERN = "^\\s*(?i)SELECT\\s+(.*)\\s+(?i)FROM\\s+([^\\s]+)(?:\\s+(?i)WHERE\\s+(.+))?\\s*;?".r val UPDATE_PATTERN = "^\\s*(?i)UPDATE\\s+([^\\s]+)\\s+(?i)SET\\s+(.+)\\s+(?i)WHERE\\s+(.+)\\s*$".r - var taintMemory = new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'")) + var taintMemory = new SqlTaintMemory(Set("TAINTED_VALUE", "'TAINTED_VALUE'")) val debugMode = true - def doAnalyze(string:String, taintMemorySQL: SqlTaintMemory): Boolean ={ + def doAnalyze(string: String, taintMemorySQL: SqlTaintMemory): Boolean = { taintMemory = taintMemorySQL; doAnalyze(string) } @@ -37,10 +37,10 @@ object SqlStringTaintAnalyzer { def doAnalyze(string: String) = { - if(string.isBlank) println("yes blank") - if(debugMode)println("String to analyze: " + string) + if (string.isBlank) println("yes blank") + if (debugMode) println("String to analyze: " + string) - val normalizedString = if(!string.isBlank) preprozesing(string) else "" + val normalizedString = if (!string.isBlank) preprozesing(string) else "" normalizedString match { case i@INSERT_PATTERN(_*) => @@ -53,7 +53,7 @@ object SqlStringTaintAnalyzer { analyzeSelectStatment(s) case _ => - if(debugMode) println("Result of SqlAnalyzer: SqlAnalyzer does not support: "+ string) + if (debugMode) println("Result of SqlAnalyzer: SqlAnalyzer does not support: " + string) false } } @@ -61,7 +61,7 @@ object SqlStringTaintAnalyzer { def preprozesing(string: String) = { val hasCorrectSyntax = checkSQLStringSyntax3(string) val normalizedString = if (hasCorrectSyntax) normalizeSQLString(string) else "" - if(debugMode && !hasCorrectSyntax ){ + if (debugMode && !hasCorrectSyntax) { println("Wrong Syntax :" + string) } normalizedString @@ -92,9 +92,9 @@ object SqlStringTaintAnalyzer { sb.replace(0, sb.length(), sb.toString().replaceAll(pattern2, replacement2)) // Groß-/Kleinschreibung normalisieren und führenden/folgnde leerzeichen entfernen - val normalizedStrig = sb.toString().toUpperCase().trim + val normalizedStrig = sb.toString().toUpperCase().trim - if(debugMode) println("String normalized to: " + normalizedStrig) + if (debugMode) println("String normalized to: " + normalizedStrig) normalizedStrig } @@ -105,8 +105,9 @@ object SqlStringTaintAnalyzer { var extractedTableName = "" var extractedColumnNames: Seq[String] = Seq() var extractedValueGroups: Seq[Seq[String]] = Seq() + val pre = query.replace(";", "") - query match { + pre match { case insertPattern(tableName, columns, values) => extractedTableName = tableName extractedColumnNames = columns.split(",").map(_.trim).toSeq @@ -123,21 +124,22 @@ object SqlStringTaintAnalyzer { val (table, columns, valueGroups) = extractInformationOfInsert4(normalizedString) var taintedColumns: Set[String] = columns.filter(column => taintMemory.isTainted(column)).toSet - valueGroups.foreach(valueGroup => { + valueGroups.foreach(valueGroup => { for (i <- valueGroup.indices) { - if (taintMemory.isTainted(valueGroup (i))) taintedColumns += columns(i).trim + if (taintMemory.isTainted(valueGroup(i))) taintedColumns += columns(i).trim } }) if (taintMemory.isTainted(table)) taintedColumns ++= columns.map(str => str.trim) if (taintedColumns.nonEmpty) taintMemory.taintTableAndColums(table.trim, taintedColumns) - if(debugMode) println("Result of analyzeInsertStatement: \n" - + " analyzed String: " + normalizedString + " \n " - + taintedColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted \n") taintedColumns.nonEmpty - } + + if (debugMode) println("Result of analyzeInsertStatement: \n" + + " analyzed String: " + normalizedString + " \n " + + taintedColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted \n") +} def extractInformationOfSelect(normalizedString: String): (String, Seq[String]) ={ diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer02.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer02.scala new file mode 100644 index 0000000000..1861ff92a2 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer02.scala @@ -0,0 +1,284 @@ + +package org.opalj.tac.fpcf.analyses.sql + +object SqlStringAnalyzer02 { + + /** + * Object to store information about tainted tables and columns. + * The specified taint identifiers are used as input to mark tainted data. + */ + var taintMemory = new SqlTaintMemory02(Set("TAINTED_VALUE","'TAINTED_VALUE'")) + + /** + * When 'true', enables console output that provides information on the results of individual helper methods. + */ + val DEBUG = true + + /** + * The specified regex patterns for the types of SQL commands + */ + val INSERT_PATTERN = "^\\s*(?i)INSERT\\s*(?:IGNORE\\s+)?INTO\\s*(\\w+)\\s*\\((.*?)\\)\\s*VALUES\\s*(.*);?".r + val SELECT_PATTERN = "^\\s*(?i)SELECT\\s+(.*)\\s+(?i)FROM\\s+([^\\s]+)(?:\\s+(?i)WHERE\\s+(.+))?\\s*;?".r + val UPDATE_PATTERN = "^\\s*(?i)UPDATE\\s+([^\\s]+)\\s+(?i)SET\\s+(.+)\\s+(?i)WHERE\\s+(.+)\\s*$".r + + /** + * Analyzes INSERT, UPDATE or SELECT commands regarding taint information + * + * @param sqlString SQL command to be analyzed for taint information + * @param taintMemorySQL the object used to store and retrieve taint information + * @return True, if taint information was added or extracted. + */ + def doAnalyze(sqlString: String, taintMemorySQL: SqlTaintMemory02): Boolean = { + taintMemory = taintMemorySQL + doAnalyze(sqlString) + } + + /** + * Analyzes INSERT, UPDATE or SELECT commands regarding taint information + * + * @param sqlString SQL command to be analyzed for taint information + * @return True, if taint information was added or extracted. + */ + def doAnalyze(sqlString: String):Boolean = { + + val hasValidSyntax = hasValidSqlSyntax(sqlString) + + //Normalizes the string to a uniform structure to simplify the analyses + val normalizedString = if (hasValidSyntax) normalizeSQLString(sqlString) else "" + + //Decides which type of SQL commands will be analyzed + val result = normalizedString match { + case i@INSERT_PATTERN(_*) => + analyzeInsertStatement(i) + + case u@UPDATE_PATTERN(_*) => + analyzeUpdateStatement(u) + + case s@SELECT_PATTERN(_*) => + analyzeSelectStatment(s) + + case _ => + if(DEBUG) println("Result of SqlAnalyzer: does not support: "+ sqlString) + false + } + + if(DEBUG){ + println("\n SqlAnalyzer:") + println(s"given String: $sqlString") + val message = if (hasValidSyntax) "has valid syntax" else "has invalid syntax" + println(s"$message") + println(s"Normalized SQL string: $normalizedString") + println(s"Result of SqlAnalyzer: $result \n") + } + result + } + + /** + * Checks the string for specified and valid SQL syntax + * + * @param str The string to check. + * @return True, if the string has a recognized valid SQL syntax. + */ + def hasValidSqlSyntax(str: String): Boolean = + str match { + case INSERT_PATTERN(_*) | SELECT_PATTERN(_*) | UPDATE_PATTERN(_*) => true + case _ => false + } + + /** + * Returns a normalized version of the given SQL string by: + * replacing multiple whitespaces with a single whitespace, + * surrounding special characters with a whitespace, + * converting the string to upper case, + * and removing leading/trailing + * + * @param sqlString the SQL string to be normalized + * @return the normalized SQL string + */ + def normalizeSQLString(sqlString: String): String = { + val whitespacePattern = raw"(\s+)" + val specialCharsPattern = raw"([,;=()])" + val whitespaceReplacement = " " + val specialCharsReplacement = " $1 " + + sqlString + // Remove double whitespace + .replaceAll(whitespacePattern, whitespaceReplacement) + //Surround special characters by whitespace + .replaceAll(specialCharsPattern, specialCharsReplacement) + // Normalize case sensitivity + .toUpperCase() + //remove leading/following spaces + .trim() + } + + /** + * Checks if the values in a column are tainted, based on the INSERT command. + * + * @param normalizedString The normalized insert command to be analyzed + * @return True, if any tainted columns or values were detected. + */ + def analyzeInsertStatement(normalizedString: String) = { + val (table, columns, valueGroups) = extractInformationOfInsert(normalizedString) + + //Checks for taint identifiers in table columns.If found, the table is considered to be tainted. + var taintedColumns: Set[String] = columns.filter(column => taintMemory.isTainted(column)).toSet + + //searches for taint identifiers within value groups, and assigns any columns that contain them as tainted. + valueGroups.foreach(valueGroup => { + for (i <- valueGroup.indices) { + if (taintMemory.isTainted(valueGroup (i))) taintedColumns += columns(i).trim + } + }) + + // Checks for taint identifiers in table. If so, all addressed columns are considered as tainted + if (taintMemory.isTainted(table)) taintedColumns ++= columns.map(str => str.trim) + + //If tainted columns are found, they are recorded along with the corresponding table. + if (taintedColumns.nonEmpty) taintMemory.taintTableAndColumns(table.trim, taintedColumns) + + if(DEBUG){ + println("analyzeInsertStatement:") + println(" extracted information:") + println(s" table name: $table") + println(s" column names: ${columns.mkString("(", ",", ")")}") + println(s" valueGroups: ${valueGroups.mkString("(", ",", ")")}") + println(s" tainted columns: ${taintedColumns.mkString("(", ",", ")")} of table $table") + } + taintedColumns.nonEmpty + } + + /** + * Extracts addressed fields from an INSERT query + * + * @param query the query string to extract information from. + * @return A tuple containing the extracted table name, column names, and value groups. + */ + def extractInformationOfInsert(query: String): (String, Seq[String], Seq[Seq[String]]) = { + val insertPattern = "(?i)INSERT\\s*(?:IGNORE\\s+)?INTO\\s*(\\w+)\\s*\\((.*?)\\)\\s*VALUES\\s*(.*);?".r + + var extractedTableName = "" + var extractedColumnNames: Seq[String] = Seq() + var extractedValueGroups: Seq[Seq[String]] = Seq() + val pre = query.replace(";","") + + pre match { + case insertPattern(tableName, columns, values) => + extractedTableName = tableName + extractedColumnNames = columns.split(",").map(_.trim).toSeq + val removedBrackets = values.split("\\)\\s*,\\s*\\(").map(x => x.replaceAll(raw"(\(|\))", "")) + extractedValueGroups = removedBrackets.map(x => x.split(",").map(_.trim).toSeq).toSeq + + case _ => + } + + (extractedTableName, extractedColumnNames, extractedValueGroups) + } + + /** + * Checks if the values in a column are tainted, based on the UPDATE command. + * + * @param normalizedString The normalized UPDATE command to be analyzed + * @return True, if any tainted columns or values were detected. + */ + def analyzeUpdateStatement(normalizedString: String): Boolean = { + val (table, columnAndValues) = extractInformationOfUpdate(normalizedString) + var taintColumns: Set[String] = Set() + + for ((colmn, value) <- columnAndValues) { + if (taintMemory.isTainted(value) || taintMemory.isTainted(colmn) || taintMemory.isTainted(table)) { + taintColumns += colmn + } + } + + if (taintColumns.nonEmpty) taintMemory.taintTableAndColumns(table, taintColumns) + + if (DEBUG) { + println("analyzeUpdateStatment:") + println(" extracted information:") + println(s" table name: $table") + println(s" column and Values: ${columnAndValues.mkString("(", ",", ")")}") + println(s" tainted columns: ${taintColumns.mkString("(", ",", ")")} of table $table") + } + taintColumns.nonEmpty + + } + + /** + * Extracts addressed fields from an UPDATE query + * + * @param query the query string to extract information from. + * @return A tuple containing the extracted table name, as well as a sequence of column and value tuples. + */ + def extractInformationOfUpdate(query: String): (String, Seq[(String, String)]) = { + val updateRegex = "UPDATE\\s+([^\\s]+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+?)\\s*;?\\s*$".r + + var table = "" + var columnValues: Seq[(String, String)] = Seq() + + updateRegex.findFirstMatchIn(query) match { + case Some(m) => + table = m.group(1) + + //extracts pairs of columns and their corresponding values. + columnValues = m.group(2).split(",").map(_.trim).toIndexedSeq.map { value => + val parts = value.split("=").map(_.trim) + (parts(0), parts(1)) + } + case _ => + } + (table, columnValues) + } + + + /** + * Checks if columns queried in SELECT commands could contain tainted information + * + * @param normalizedString The normalized SELECT command to be analyzed + * @return True, if any of the queried columns is tainted + */ + def analyzeSelectStatment(normalizedString: String): Boolean = { + val (tableName, selectedColumns) = extractInformationOfSelect(normalizedString) + + var columnsToTaint: Set[String] = selectedColumns.toSet + + // handles "SELECT * ..." + if (selectedColumns.contains("*")) { + taintMemory.getTaintedTableAndColumns(tableName) match { + case Some(x) => columnsToTaint = x + case None => + } + } + if (taintMemory.isTainted(tableName)) taintMemory.taintTableAndColumns(tableName, columnsToTaint) + + val taintedColumns = taintMemory.getTaintedColumns(tableName, columnsToTaint) + if (DEBUG) { + println("analyzeSelectStatment:") + println(" extracted information:") + println(s" table name: $tableName") + println(s" selected columns: ${selectedColumns.mkString("(", ",", ")")}") + println(s" tainted columns: ${taintedColumns.mkString("(", ",", ")")} of table $tableName") + } + taintedColumns.nonEmpty + } + + /** + * Extracts addressed fields from an SELECT query + * + * @param query the string to extract information from. + * @return A tuple containing the extracted table name, as well as a sequence of column + */ + def extractInformationOfSelect(query: String): (String, Seq[String]) = { + val selectRegex = "SELECT\\s+(DISTINCT\\s+)?(.*?)\\s+FROM\\s+([^\\s]+)\\s*(WHERE\\s+(.*))?".r + var table = "" + var columns: Seq[String] = Seq() + selectRegex.findFirstMatchIn(query) match { + case Some(str) => + columns = str.group(2).split(",").map(_.trim).toSeq + table = str.group(3) + case _ => + } + (table, columns) + } + +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala index f03e3f916d..bad2d6686a 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala @@ -36,5 +36,4 @@ class SqlTaintMemory(dummyTaintRecognitionWords:Set[String]) { } } - } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory02.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory02.scala new file mode 100644 index 0000000000..905c978392 --- /dev/null +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory02.scala @@ -0,0 +1,66 @@ + +package org.opalj.tac.fpcf.analyses.sql + +class SqlTaintMemory02(defaultTaintIdentifiers:Set[String]) { + + private var taintIdentifiers: Set[String] = defaultTaintIdentifiers + private val taintedTableAndColumns = scala.collection.mutable.Map[String, Set[String]]() + + + /** + * Adds a new identifier to the set of taint identifiers. + * + * @param identifier the identifier to add. + */ + def addTaintIdentifier(identifier: String): Unit = { + taintIdentifiers += identifier + } + + /** + * checks if the string represent a tainted part or not + * + * @param str the String to check. + * @return true, if the string is a taint identifier + */ + def isTainted(str: String): Boolean = { + taintIdentifiers.contains(str) + } + + /** + * Adds a set of tainted columns to a table. + * + * @param tableName the name of the table. + * @param columns the set of column names to add. + */ + def taintTableAndColumns(tableName: String, columns: Set[String]): Unit = { + val prev = taintedTableAndColumns.getOrElseUpdate(tableName, Set()) + taintedTableAndColumns.update(tableName, prev ++ columns) + } + + /** + * Checks if any of the given columns in the specified table are tainted. + * + * @param tableName the name of the table to check. + * @param columns the set of column names to check. + * @return the set of given columns that have been tainted for the given table. + */ + def getTaintedColumns(tableName: String, columns: Set[String]): Set[String] = { + taintedTableAndColumns.get(tableName.trim) match { + case Some(taintedColumns) => columns.intersect(taintedColumns) + case None => Set() + } + } + + /** + * retrieves a Set of all columns in the specified table that have been tainted. + * + * @param tableName the name of the table to retrieve tainted columns from. + * @return option value containing a Set of tainted column names associated with the specified tableName. or None if no columns in the table are tainted. + */ + def getTaintedTableAndColumns(tableName: String): Option[Set[String]] = { + taintedTableAndColumns.get(tableName) + } + + + +} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala index 728c549adc..b00b683464 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala @@ -85,159 +85,29 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false override def normalFlow(statement:JavaStatement,in:TaintFact,predecessor:Option[JavaStatement]): Set[TaintFact] ={ - in match { case sqlTaintFact:SqlTaintFact=> Set(sqlTaintFact) case _ => super.normalFlow(statement, in, predecessor) } - } override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { - - /* - val callObject = JavaIFDSProblem.asCall(call.stmt) - val allParams = callObject.allParams - val allParamsWithIndices = allParams.zipWithIndex - - var facts:Set[TaintFact] = Set() - - for( (param,paramINdex) <- allParamsWithIndices){ - if(callee.descriptor.parameterTypes.head == ObjectType.String){ - val x = getPossibleString(param,call.code,in) - /* println(call.index) - println(in) - println(x) - */ - if(x._1.nonEmpty ){ - facts += StringValue(JavaIFDSProblem.switchParamAndVariableIndex(paramINdex,callee.isStatic),x._1,x._2) - } - } - } - - - def getPossibleString(param:Expr[V], stmts: Array[Stmt[V]],in:TaintFact):Tuple2[Set[String],Boolean] = { - - val defsites = param.asVar.definedBy - var result:Tuple2[Set[String],Boolean] = (Set.empty[String],false) - for (index <- defsites) { - - if(index < 0 )return result - val expr = call.code(index).asAssignment.expr - - val taintStatuts = in match { - case Variable(indexF) => indexF == index - case StringValue(indexF,values,taintStatus) => indexF == index - case _ => false - } - - expr match { - case StaticFunctionCall(pc, declaringClass, isInterface, name, descriptor, params) if name == "source" => - result = (Set("TAINTED"), true) - - case StringConst(_, v) => result = (Set(v), false) - - case VirtualFunctionCall(pc,declaringClass,isInterface,name,descriptor,receiver,params) if name == "toString" => - val tmp = getPossibleString(receiver.asVar, stmts, in) - - result = (tmp._1,tmp._2 || taintStatuts ) - - case VirtualFunctionCall(pc,declaringClass,isInterface,name,descriptor,receiver,params) if name == "append" => - val leftSideString = getPossibleString(receiver.asVar, stmts, in) - val rightSideString = getPossibleString(params.head.asVar, stmts, in) - var possibleString: Set[String] = Set() - - for { - l <- leftSideString._1 - r <- rightSideString._1 - } { - possibleString += l + r - } - result = (possibleString, leftSideString._2 || rightSideString._2 || taintStatuts) - - case _ => result = (Set(""), false) - } - - } - result - } - - - - - /* - val callObject = JavaIFDSProblem.asCall(call.stmt) - val allParams = callObject.allParams - val allParamsWithIndices = allParams.zipWithIndex - - - in match { - case BindingFact(index, keyName) => allParamsWithIndices.flatMap { - case (param, paramIndex) if param.asVar.definedBy.contains(index) => - Some(BindingFact(JavaIFDSProblem.switchParamAndVariableIndex( - paramIndex, - callee.isStatic - ), keyName)) - case _ => None // Nothing to do - }.toSet - case _ => super.callFlow(call, callee, in) - } - - */ - if(callee.name == "sink")Set.empty - else super.callFlow(call, callee, in) - - */ - in match { case sqlTaintFact: SqlTaintFact => Set(sqlTaintFact) case _=> super.callFlow(call, callee, in) } - - - } - override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, - callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { - + override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { in match { case sqlTaintFact: SqlTaintFact => Set(sqlTaintFact) ++ super.returnFlow(exit, in, call, callFact, successor) case _=> super.returnFlow(exit, in, call, callFact, successor) } - - - /* - if (!isPossibleReturnFlow(exit, successor)) return Set.empty - val callee = exit.callable - if (sanitizesReturnValue(callee)) return Set.empty - val callStatement = JavaIFDSProblem.asCall(call.stmt) - val allParams = callStatement.allParams - - in match { - case StringValue(index, values, taintStatus) => - var flows: Set[TaintFact] = Set.empty - if (index < 0 && index > -100) { - val param = allParams( - JavaIFDSProblem.switchParamAndVariableIndex(index, callee.isStatic) - ) - flows ++= param.asVar.definedBy.map(i => StringValue(i, values, taintStatus)) - } - if (exit.stmt.astID == ReturnValue.ASTID && call.stmt.astID == Assignment.ASTID) { - val returnValueDefinedBy = exit.stmt.asReturnValue.expr.asVar.definedBy - if (returnValueDefinedBy.contains(index)) - flows += StringValue(call.index, values, taintStatus) - } - flows - - case _ => super.returnFlow(exit, in, call, callFact, successor) - } - - */ } - /** + +/** * Returns all possible constant strings. Contains the empty string if at least one was non-constant. * * @param method method @@ -256,7 +126,6 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { } */ override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { - var flow:Set[TaintFact] = Set(in) val callStmt = JavaIFDSProblem.asCall(call.stmt) @@ -267,7 +136,6 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { case _ => } flow - case "intValue" => val leftSideVar = callStmt.receiverOption.get.asVar in match { @@ -275,7 +143,6 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { case _ => } flow - case "append" => val rightSideVar = callStmt.params.head.asVar val leftSideVar = callStmt.receiverOption.get.asVar @@ -285,7 +152,6 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { case _=> } flow - case "toString" => val leftSideVar = callStmt.receiverOption.get.asVar in match { @@ -293,27 +159,25 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { case _ => } flow - case "executeUpdate" => if(callStmt.params.size >0){ val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) possibleParamStrings.foreach(input => if ( input.contains("TAINTED_VALUE") && - SqlStringTaintAnalyzer.checkSQLStringSyntax3(input) - && SqlStringTaintAnalyzer.doAnalyze(input, new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'"))) ) { - flow += SqlTaintFact(SqlStringTaintAnalyzer.taintMemory) + SqlStringAnalyzer.checkSQLStringSyntax3(input) + && SqlStringAnalyzer.doAnalyze(input, new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'"))) ) { + flow += SqlTaintFact(SqlStringAnalyzer.taintMemory) }) } flow - case "executeQuery" => in match { case sqlTaintFact: SqlTaintFact => val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) possibleParamStrings.foreach(string => { - if(SqlStringTaintAnalyzer.checkSQLStringSyntax3(string) - && SqlStringTaintAnalyzer.doAnalyze(string,sqlTaintFact.sqlTaintMemory) + if(SqlStringAnalyzer.checkSQLStringSyntax3(string) + && SqlStringAnalyzer.doAnalyze(string,sqlTaintFact.sqlTaintMemory) && call.stmt.isAssignment){ flow += Variable(call.index) } @@ -321,14 +185,12 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { case _ => } flow - case "source" => in match { case TaintNullFact => flow += Variable(call.index) case _ => } flow - case "sink" => in match { case Variable(index) if callStmt.params.find(parm => parm.asVar.definedBy.contains(index)).nonEmpty => @@ -336,208 +198,41 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { case _=> } flow - - - case _ => icfg.getCalleesIfCallStatement(call) match { case Some(callee) if callee.isEmpty => flow case _ => super.callToReturnFlow(call, in, successor) } - - //TODO sink case ? - // TODO filter for Analye, statt in der Analyse filtern. - // +1 für Calltiefe => weniger speicher auf heap - // +1 weniger calls - // +1 performance - - } - - - /* - val callStmt = JavaIFDSProblem.asCall(call.stmt) - // val allParams = callStmt.allParams - val allParamsWithIndex = callStmt.allParams.zipWithIndex - - // if (!invokesScriptFunction(callStmt)) { - in match { - case BindingFact(index, _) => - if (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == NO_MATCH) - Set(in) - else - Set() - case _ => super.callToReturnFlow(call, in, successor) } - - */ - - /* } else { - in match { - /* Call to invokeFunction. The variable length parameter list is an array in TACAI. */ - case arrIn: ArrayElement if callStmt.name == "invokeFunction" - && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, arrIn.index) == -3 => - val fNames = getPossibleStrings(call.method, allParams(1).asVar.definedBy) - fNames.map(fName => - if (fName == "") - /* Function name is unknown. We don't know what to call */ - None - else - {}//Some(jsAnalysis.analyze(call, arrIn, fName))).filter(_.isDefined).flatMap(_.get) ++ Set(in) - /* Call to eval. */ - case f: BindingFact if callStmt.name == "eval" - && (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -1 - || JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -3) => - {}//jsAnalysis.analyze(call, f) - case f: WildcardBindingFact if callStmt.name == "eval" - && (JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -1 - || JavaIFDSProblem.getParameterIndex(allParamsWithIndex, f.index) == -3) => - {}//jsAnalysis.analyze(call, f) - /* Put obj in Binding */ - case Variable(index) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -3 => - val keyNames = getPossibleStrings(call.method, allParams(1).asVar.definedBy) - val defSites = callStmt.receiverOption.get.asVar.definedBy - keyNames.flatMap(keyName => defSites.map(i => if (keyName == "") WildcardBindingFact(i) else BindingFact(i, keyName))) ++ Set(in) - /* putAll BindingFact to other BindingFact */ - case BindingFact(index, keyName) if callStmt.name == "putAll" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -2 => - callStmt.receiverOption.get.asVar.definedBy.map(i => if (keyName == "") WildcardBindingFact(i) else BindingFact(i, keyName)) ++ Set(in) - case WildcardBindingFact(index) if callStmt.name == "putAll" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -2 => - callStmt.receiverOption.get.asVar.definedBy.map(i => WildcardBindingFact(i)) ++ Set(in) - /* Overwrite BindingFact */ - case BindingFact(index, keyName) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => - val possibleFields = getPossibleStrings(call.method, allParams(1).asVar.definedBy) - if (possibleFields.size == 1 && possibleFields.contains(keyName)) - /* Key is definitely overwritten */ - Set() - else - Set(in) - case WildcardBindingFact(index) if callStmt.name == "put" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => - /* We never overwrite here as we don't know the key */ - Set(in) - /* Remove BindingFact */ - case BindingFact(index, keyName) if callStmt.name == "remove" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => - val possibleFields = getPossibleStrings(call.method, allParams(1).asVar.definedBy) - if (possibleFields.size == 1 && possibleFields.contains(keyName)) - Set() - else - Set(in) - case WildcardBindingFact(index) if callStmt.name == "remove" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => - /* We never kill here as we don't know the key */ - Set(in) - /* get from BindingFact */ - case BindingFact(index, keyName) if callStmt.name == "get" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => - val possibleFields = getPossibleStrings(call.method, allParams(1).asVar.definedBy) - if (possibleFields.size == 1 && possibleFields.contains(keyName)) - Set(Variable(call.index), in) - else - Set(in) - case WildcardBindingFact(index) if callStmt.name == "get" && JavaIFDSProblem.getParameterIndex(allParamsWithIndex, index) == -1 => - Set(Variable(call.index), in) - case _ => Set(in) - } - } */ } override def isTainted(expression: Expr[V], in: TaintFact): Boolean = { super.isTainted(expression, in) - /* - val definedBy = expression.asVar.definedBy - expression.isVar && (in match { - case BindingFact(index, _) => definedBy.contains(index) - case _ => super.isTainted(expression, in) - }) - - */ } - // TODO: use string analysis here - def getPossibleStringsAndTaintStatus(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):(Set[String], Boolean) = { - val defSites = param.asVar.definedBy - var result: (Set[String], Boolean) = (Set(""), false) - for (defSiteIndex <- defSites) { - if (defSiteIndex >= 0) { - val expr = stmts(defSiteIndex).asAssignment.expr - - in match { - case Variable(index) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case ArrayElement(index: Int,_) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case _ => - expr match { - - case StringConst(_, value) => - result = (result._1 ++ Set(value), result._2 ) - - case GetField(_,_,_,_,_) => - // letzte Putfield suchen von dort weiter - val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) - if(lastPut >0){ - val putFieldExpression = stmts(lastPut).asPutField.value - val tmp = getPossibleStringsAndTaintStatus(putFieldExpression,stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 ) - } - - - case gstc:GetStatic => - val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) - if(lastPut > 0){ - val putExpression = stmts(lastPut).asPutStatic.value - val tmp = getPossibleStringsAndTaintStatus(putExpression,stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 ) - } - - case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => - val tmp = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 ) - - case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => - - - val leftSideString = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) - val rightSideString = getPossibleStringsAndTaintStatus(params.head.asVar, stmts, in) - var possibleString: Set[String] = Set() - - for { - leftString <- leftSideString._1 - rightString <- rightSideString._1 - } { - possibleString += leftString + rightString - } - result = (result._1 ++ possibleString, result._2 || leftSideString._2 || rightSideString._2) - - case _ => - } - } - }else{ - in match { - case Variable(index) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case ArrayElement(index: Int,_) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case _ => result = (result._1 ++ Set("PARAM_VALUE"), false) - } - } - } - - result - } + /** + * Returns all possible constant strings. Concatenations of constants using the "append" function are reconstructed. + * tainted variables are replaced by taint identifiers. + * + * + * @param param + * @param stmts + * @param in + * @return + */ def getPossibleStrings2(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):Set[String] = { // Initialize the set of possible strings and taint status var possibleStrings: Set[String] = Set() val defSites = param.asVar.definedBy - // Durche alle Defsites durchgehen um weitere möglichen Strings zu ermitteln + // Go through all defsites to find out more possible strings for (defSiteIndex <- defSites) { var currentSetOfString: Set[String] = Set() in match { - // Wenn der ausdruck für den Intext getaintet ist, ersetzen wir in durch den TAINTIDENTIFIER + // If the expression for the index is maintained, we replace it with the TAINTED_VALUE case Variable(index) if index == defSiteIndex => currentSetOfString = Set("TAINTED_VALUE") case ArrayElement(index: Int,_) if index == defSiteIndex => @@ -605,9 +300,82 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { possibleStrings } - //TODO - // AppendMethode - //Get Strings used by foo bar. - //Handle Concatenation. - //put the Strings in the Prototype + + // TODO: use string analysis here + def getPossibleStringsAndTaintStatus(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):(Set[String], Boolean) = { + val defSites = param.asVar.definedBy + var result: (Set[String], Boolean) = (Set(""), false) + for (defSiteIndex <- defSites) { + if (defSiteIndex >= 0) { + val expr = stmts(defSiteIndex).asAssignment.expr + + in match { + case Variable(index) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case ArrayElement(index: Int,_) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case _ => + expr match { + + case StringConst(_, value) => + result = (result._1 ++ Set(value), result._2 ) + + case GetField(_,_,_,_,_) => + // letzte Putfield suchen von dort weiter + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) + if(lastPut >0){ + val putFieldExpression = stmts(lastPut).asPutField.value + val tmp = getPossibleStringsAndTaintStatus(putFieldExpression,stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 ) + } + + + case gstc:GetStatic => + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) + if(lastPut > 0){ + val putExpression = stmts(lastPut).asPutStatic.value + val tmp = getPossibleStringsAndTaintStatus(putExpression,stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 ) + } + + case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => + val tmp = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 ) + + case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => + + + val leftSideString = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) + val rightSideString = getPossibleStringsAndTaintStatus(params.head.asVar, stmts, in) + var possibleString: Set[String] = Set() + + for { + leftString <- leftSideString._1 + rightString <- rightSideString._1 + } { + possibleString += leftString + rightString + } + result = (result._1 ++ possibleString, result._2 || leftSideString._2 || rightSideString._2) + + case _ => + } + } + }else{ + in match { + case Variable(index) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case ArrayElement(index: Int,_) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case _ => result = (result._1 ++ Set("PARAM_VALUE"), false) + } + } + } + + result + } + } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala index 53ca2bddb2..ea5de98a22 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala @@ -1,74 +1,395 @@ - +/* BSD 2-Clause License - see OPAL/LICENSE for details. */ package org.opalj.tac.fpcf.analyses.sql import org.opalj.br.Method import org.opalj.br.analyses.SomeProject import org.opalj.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.{JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintProblem -import org.opalj.tac.fpcf.properties.{FlowFact, Taint, TaintFact, TaintNullFact, Variable} - -class SqlTaintAnalysis02(project: SomeProject) - extends IFDSAnalysis()(project, new SqlTaintProblem02(project), Taint) - -class SqlTaintProblem02(p: SomeProject) extends ForwardTaintProblem(p){ - /** - * Called, when the exit to return facts are computed for some `callee` with the null fact and - * the callee's return value is assigned to a variable. - * Creates a taint, if necessary. - * - * @param callee The called method. - * @param call The call. - * @return Some variable fact, if necessary. Otherwise none. - */ - override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = - if (callee.name == "source") Set(Variable(call.index)) - else Set.empty - - /** - * Called, when the call to return facts are computed for some `callee`. - * Creates a FlowFact, if necessary. - * - * @param callee The method, which was called. - * @param call The call. - * @return Some FlowFact, if necessary. Otherwise None. - */ - override protected def createFlowFact(callee: Method, call: JavaStatement, in: TaintFact): Option[FlowFact] = - if (callee.name == "sink" && in == Variable(-2)) - Some(FlowFact(Seq(JavaMethod(call.method), JavaMethod(callee)))) - else None - - /** - * The entry points of this analysis. - */ - override def entryPoints: Seq[(Method, TaintFact)] = - for { - m <- p.allMethodsWithBody - if m.name == "main" - } yield m -> TaintNullFact - - /** - * Checks, if some `callee` is a sanitizer, which sanitizes its return value. - * In this case, no return flow facts will be created. - * - * @param callee The method, which was called. - * @return True, if the method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" - - /** - * Called in callToReturnFlow. This method can return whether the input fact - * will be removed after `callee` was called. I.e. the method could sanitize parameters. - * - * @param call The call statement. - * @param in The fact which holds before the call. - * @return Whether in will be removed after the call. - */ - override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false - - override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { - print("index: "+call.index +" ") - println(in) - super.callToReturnFlow(call, in, successor) - } +import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} +import org.opalj.tac.fpcf.properties._ +import org.opalj.tac._ + + +class SqlTaintAnalysis(project: SomeProject) + extends IFDSAnalysis()(project, new SqlTaintProblem(project), Taint) + +/** + * Java IFDS analysis that is able to resolve calls of sql statements. + * + * @param p project + */ +class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { + + /** + * Called, when the exit to return facts are computed for some `callee` with the null fact and + * the callee's return value is assigned to a variable. + * Creates a taint, if necessary. + * + * @param callee The called method. + * @param call The call. + * @return Some variable fact, if necessary. Otherwise none. + */ + override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = + if (callee.name == "source") Set(Variable(call.index)) + else Set.empty + + /** + * Called, when the call to return facts are computed for some `callee`. + * Creates a FlowFact, if necessary. + * + * @param callee The method, which was called. + * @param call The call. + * @return Some FlowFact, if necessary. Otherwise None. + */ + override protected def createFlowFact( + callee: Method, + call: JavaStatement, + in: TaintFact + ): Option[FlowFact] = + if (callee.name == "sink" && in == Variable(-2)) + Some(FlowFact(Seq(JavaMethod(call.method), JavaMethod(callee)))) + else None + + /** + * The entry points of this analysis. + */ + override def entryPoints: Seq[(Method, TaintFact)] = + for { + m <- p.allMethodsWithBody + if m.name == "main" + } yield m -> TaintNullFact + + /** + * Checks, if some `callee` is a sanitizer, which sanitizes its return value. + * In this case, no return flow facts will be created. + * + * @param callee The method, which was called. + * @return True, if the method is a sanitizer. + */ + override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" + + /** + * Called in callToReturnFlow. This method can return whether the input fact + * will be removed after `callee` was called. I.e. the method could sanitize parameters. + * + * @param call The call statement. + * @param in The fact which holds before the call. + * @return Whether in will be removed after the call. + */ + override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false + + /** + * + * @param statement The analyzed statement. + * @param in The fact which holds before the execution of the `statement`. + * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be + * computed. Used for phi statements to distinguish the flow. + * @return The facts, which hold after the execution of `statement` under the assumption + * that the facts in `in` held before `statement` and `successor` will be + * executed next. + */ + override def normalFlow(statement:JavaStatement,in:TaintFact,predecessor:Option[JavaStatement]): Set[TaintFact] ={ + in match { + case sqlTaintFact:SqlTaintFact=> Set(sqlTaintFact) + case _ => super.normalFlow(statement, in, predecessor) + } + } + + /** + * + * @param call The analyzed call statement. + * @param callee The called method, for which the data flow shall be computed. + * @param in The fact which holds before the execution of the `call`. + * @return The facts, which hold after the execution of `statement` under the assumption that + * the facts in `in` held before `statement` and `statement` calls `callee`. + */ + override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { + in match { + case sqlTaintFact: SqlTaintFact => Set(sqlTaintFact) + case _=> super.callFlow(call, callee, in) + } + } + + /** + * + * @param exit The statement, which terminated the `callee`. + * @param in The fact which holds before the execution of the `exit`. + * @param call The statement, which called the `callee`. + * @param callFact + * @param successor + * @return The facts, which hold after the execution of `exit` in the caller's context + * under the assumption that `in` held before the execution of `exit` and that + * `successor` will be executed next. + */ + override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { + in match { + case sqlTaintFact: SqlTaintFact => Set(sqlTaintFact) ++ super.returnFlow(exit, in, call, callFact, successor) + case _=> super.returnFlow(exit, in, call, callFact, successor) + } + } + + + +/** + * Returns all possible constant strings. Contains the empty string if at least one was non-constant. + * + * @param method method + * @param defSites def sites of the queried variable + * @return + */ + /*private def getPossibleStrings(method: Method, defSites: IntTrieSet): Set[String] = { + val taCode = tacaiKey(method) + + // TODO: use string analysis here + defSites.map(site => taCode.stmts.apply(site)).map { + case a: Assignment[JavaIFDSProblem.V] if a.expr.isStringConst => + a.expr.asStringConst.value + case _ => "" + } + } */ + + override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { + var flow:Set[TaintFact] = Set(in) + val callStmt = JavaIFDSProblem.asCall(call.stmt) + + callStmt.name match { + case "valueOf" if callStmt.declaringClass.toJava == "java.lang.Integer" => + in match { + case Variable(index) if callStmt.params.find(parm => parm.asVar.definedBy.contains(index)).nonEmpty => flow += Variable(call.index) + case _ => + } + flow + case "intValue" => + val leftSideVar = callStmt.receiverOption.get.asVar + in match { + case Variable(index) if leftSideVar.definedBy.contains(index) => flow += Variable(call.index) + case _ => + } + flow + case "append" => + val rightSideVar = callStmt.params.head.asVar + val leftSideVar = callStmt.receiverOption.get.asVar + in match { + case Variable(index) if leftSideVar.definedBy.contains(index)||rightSideVar.definedBy.contains(index) => + flow += Variable(call.index) + case _=> + } + flow + case "toString" => + val leftSideVar = callStmt.receiverOption.get.asVar + in match { + case Variable(index) if leftSideVar.definedBy.contains(index) => flow += Variable(call.index) + case _ => + } + flow + case "executeUpdate" => + if(callStmt.params.size >0){ + val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) + possibleParamStrings.foreach(input => + if ( + input.contains("TAINTED_VALUE") && + SqlStringAnalyzer.checkSQLStringSyntax3(input) + && SqlStringAnalyzer.doAnalyze(input, new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'"))) ) { + flow += SqlTaintFact(SqlStringAnalyzer.taintMemory) + }) + } + flow + case "executeQuery" => + in match { + case sqlTaintFact: SqlTaintFact => + val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) + possibleParamStrings.foreach(string => { + if(SqlStringAnalyzer.checkSQLStringSyntax3(string) + && SqlStringAnalyzer.doAnalyze(string,sqlTaintFact.sqlTaintMemory) + && call.stmt.isAssignment){ + flow += Variable(call.index) + } + }) + case _ => + } + flow + case "source" => + in match { + case TaintNullFact => flow += Variable(call.index) + case _ => + } + flow + case "sink" => + in match { + case Variable(index) if callStmt.params.find(parm => parm.asVar.definedBy.contains(index)).nonEmpty => + flow += FlowFact(Seq(JavaMethod(call.method))) + case _=> + } + flow + case _ => + icfg.getCalleesIfCallStatement(call) match { + case Some(callee) if callee.isEmpty => flow + case _ => super.callToReturnFlow(call, in, successor) + } + + } + } + + override def isTainted(expression: Expr[V], in: TaintFact): Boolean = { + super.isTainted(expression, in) + } + + + def getPossibleStrings2(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):Set[String] = { + // Initialize the set of possible strings and taint status + var possibleStrings: Set[String] = Set() + val defSites = param.asVar.definedBy + + // Durche alle Defsites durchgehen um weitere möglichen Strings zu ermitteln + for (defSiteIndex <- defSites) { + var currentSetOfString: Set[String] = Set() + + in match { + // Wenn der ausdruck für den Intext getaintet ist, ersetzen wir in durch den TAINTIDENTIFIER + case Variable(index) if index == defSiteIndex => + currentSetOfString = Set("TAINTED_VALUE") + case ArrayElement(index: Int,_) if index == defSiteIndex => + currentSetOfString = Set("TAINTED_VALUE") + case InstanceField(index: Int, _, _) if index == defSiteIndex=> + currentSetOfString = Set("TAINTED_VALUE") + case _ if defSiteIndex >= 0 =>{ + val expr = stmts(defSiteIndex).asAssignment.expr + + //Weitere Untersuchung im Ausdruck + expr match { + // Den Wert aus der String Konstante nehmen + case StringConst(_, value) => currentSetOfString = Set(value) + + case GetField(_,_,_,_,_) => + // Um ein Wert zu kriegen suchen wir nach dem letzten PutField + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) + if(lastPut >0){ + val putFieldExpression = stmts(lastPut).asPutField.value + currentSetOfString = getPossibleStrings2(putFieldExpression,stmts, in) + } + + case gstc:GetStatic => + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) + if(lastPut > 0){ + val putExpression = stmts(lastPut).asPutStatic.value + currentSetOfString = getPossibleStrings2(putExpression,stmts, in) + } + + case ArrayLoad(pc,index,arrayRef) => + val elementIndex = index.asVar.value + val lastArrayStoreIndex = stmts.lastIndexWhere(stmt => stmt.astID == ArrayStore.ASTID && + stmt.asArrayStore.index.asVar.value == elementIndex) + if(lastArrayStoreIndex > 0){ + val arrayElementExpression = stmts(lastArrayStoreIndex).asArrayStore.value + currentSetOfString = getPossibleStrings2(arrayElementExpression,stmts,in) + } + + case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => + currentSetOfString = getPossibleStrings2(receiver.asVar, stmts, in) + + case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => + + //Alle möglichen Strings die aus dem receiver gewonnen der funktion append werden können + val leftSideString = getPossibleStrings2(receiver.asVar, stmts, in) + + //Alle möglichen Strings die aus dem parameter gewonnen der funktion append werden können + val rightSideString = getPossibleStrings2(params.head.asVar, stmts, in) + + //Alle Strings die auf der linken seite entstehen können mit allen auf der rechten kombinieren + for { + leftString <- leftSideString + rightString <- rightSideString + } { + currentSetOfString += leftString + rightString + } + case _ => currentSetOfString = Set("") + } + } + case _ => currentSetOfString = Set("PARAM_VALUE") + + } + possibleStrings = possibleStrings ++ currentSetOfString + } + possibleStrings + } + + + // TODO: use string analysis here + def getPossibleStringsAndTaintStatus(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):(Set[String], Boolean) = { + val defSites = param.asVar.definedBy + var result: (Set[String], Boolean) = (Set(""), false) + for (defSiteIndex <- defSites) { + if (defSiteIndex >= 0) { + val expr = stmts(defSiteIndex).asAssignment.expr + + in match { + case Variable(index) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case ArrayElement(index: Int,_) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case _ => + expr match { + + case StringConst(_, value) => + result = (result._1 ++ Set(value), result._2 ) + + case GetField(_,_,_,_,_) => + // letzte Putfield suchen von dort weiter + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) + if(lastPut >0){ + val putFieldExpression = stmts(lastPut).asPutField.value + val tmp = getPossibleStringsAndTaintStatus(putFieldExpression,stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 ) + } + + + case gstc:GetStatic => + val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) + if(lastPut > 0){ + val putExpression = stmts(lastPut).asPutStatic.value + val tmp = getPossibleStringsAndTaintStatus(putExpression,stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 ) + } + + case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => + val tmp = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) + result = (result._1 ++ tmp._1, result._2 || tmp._2 ) + + case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => + + + val leftSideString = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) + val rightSideString = getPossibleStringsAndTaintStatus(params.head.asVar, stmts, in) + var possibleString: Set[String] = Set() + + for { + leftString <- leftSideString._1 + rightString <- rightSideString._1 + } { + possibleString += leftString + rightString + } + result = (result._1 ++ possibleString, result._2 || leftSideString._2 || rightSideString._2) + + case _ => + } + } + }else{ + in match { + case Variable(index) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case ArrayElement(index: Int,_) if(index == defSiteIndex) => + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> + result = (result._1 ++ Set("TAINTED_VALUE"), true) + case _ => result = (result._1 ++ Set("PARAM_VALUE"), false) + } + } + } + + result + } + } From 40d1a9132fc457bdd4d4716e57318260483a9ef0 Mon Sep 17 00:00:00 2001 From: Imagee <47892322+Imagee@users.noreply.github.com> Date: Mon, 20 Feb 2023 11:02:49 +0100 Subject: [PATCH 67/67] String Analyzer cleaned --- .../fpcf/analyses/sql/SqlStringAnalyzer.scala | 384 ++++++++--------- .../analyses/sql/SqlStringAnalyzer02.scala | 284 ------------- .../fpcf/analyses/sql/SqlTaintMemory.scala | 71 +++- .../fpcf/analyses/sql/SqlTaintMemory02.scala | 66 --- .../fpcf/analyses/sql/SqlTaintProblem.scala | 161 ++----- .../fpcf/analyses/sql/SqlTaintProblem02.scala | 395 ------------------ 6 files changed, 279 insertions(+), 1082 deletions(-) delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer02.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory02.scala delete mode 100644 DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer.scala index fc288b3adc..44b02b8083 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer.scala @@ -3,46 +3,59 @@ package org.opalj.tac.fpcf.analyses.sql object SqlStringAnalyzer { - //Regex Pattern:{ - - /* - val WORD = raw"(\w+|'\w+'|`\w+`)" - val SPECIAL_WORD = raw"($WORD|(\w+(-|\.|\w+)+\w+)|('\w+(-|\.|\w+)+\w+')|(`\w+(-|\.|\w+)+\w+`))" - val WORD_LIST = raw"$SPECIAL_WORD(\s*,\s*$SPECIAL_WORD)*" - val INSERT_COLUMN = raw"\(\s*$WORD_LIST\s*\)" - val VALUES = raw"\(\s*$WORD_LIST\s*\)(\s*,\s*\(\s*$WORD_LIST\s*\))*" - val INSERT_PATTERN = raw"((INSERT|insert)\s+((IGNORE|ignore)\s+)?(INTO|into)\s+)($SPECIAL_WORD)\s+$INSERT_COLUMN\s+(VALUES|values)\s+($VALUES)\s*;\s*".r - val SELECT_PATTERN = raw"(SELECT|select)\s+($WORD_LIST|\*)\s+(FROM|from)\s+($SPECIAL_WORD)\s*(\s+(WHERE|where)\s+.+)?;".r - val UPDATE_PATTERN = raw"(UPDATE|update)\s+($SPECIAL_WORD)\s+(SET|set)\s+($SPECIAL_WORD\s+=\s+$SPECIAL_WORD)(\s*,\s*$SPECIAL_WORD\s+=\s+$SPECIAL_WORD)*(\s+(WHERE|where) .+)?\s*;\s*".r - + /** + * Object to store information about tainted tables and columns. + * The specified taint identifiers are used as input to mark tainted data. + */ + private var taintMemory = new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'")) - val INSERT_PATTERN02 = "^INSERT\\s+(IGNORE\\s+)?INTO\\s+[\\w\\d_]+\\s+\\(([\\w\\d_]+(,\\s)?)+\\)\\s+VALUES\\s+\\((.+)(,\\s.+)*\\)\\s*;?$".r -} + /** + * When 'true', enables console output that provides information on the results of individual helper methods. */ + val DEBUG = true + /** + * The specified regex patterns for the types of SQL commands + */ val INSERT_PATTERN = "^\\s*(?i)INSERT\\s*(?:IGNORE\\s+)?INTO\\s*(\\w+)\\s*\\((.*?)\\)\\s*VALUES\\s*(.*);?".r val SELECT_PATTERN = "^\\s*(?i)SELECT\\s+(.*)\\s+(?i)FROM\\s+([^\\s]+)(?:\\s+(?i)WHERE\\s+(.+))?\\s*;?".r val UPDATE_PATTERN = "^\\s*(?i)UPDATE\\s+([^\\s]+)\\s+(?i)SET\\s+(.+)\\s+(?i)WHERE\\s+(.+)\\s*$".r - - var taintMemory = new SqlTaintMemory(Set("TAINTED_VALUE", "'TAINTED_VALUE'")) - - val debugMode = true - - def doAnalyze(string: String, taintMemorySQL: SqlTaintMemory): Boolean = { - taintMemory = taintMemorySQL; - doAnalyze(string) + /** + * Analyzes INSERT, UPDATE or SELECT commands regarding taint information + * + * @param sqlString SQL command to be analyzed for taint information + * @param taintMemorySQL the object used to store and retrieve taint information + * @return True, if taint information was added or extracted. + */ + def doAnalyze(sqlString: String, taintMemorySQL: SqlTaintMemory): Boolean = { + taintMemory = taintMemorySQL + doAnalyze(sqlString) } - def doAnalyze(string: String) = { + /** + * Analyzes INSERT, UPDATE or SELECT commands regarding taint information + * + * @param sqlString SQL command to be analyzed for taint information + * @return True, if taint information was added or extracted. + */ + def doAnalyze(sqlString: String):Boolean = { + val hasValidSyntax = hasValidSqlSyntax(sqlString) - if (string.isBlank) println("yes blank") - if (debugMode) println("String to analyze: " + string) + //Normalizes the string to a uniform structure to simplify the analyses + val normalizedString = if (hasValidSyntax) normalizeSQLString(sqlString) else "" - val normalizedString = if (!string.isBlank) preprozesing(string) else "" + if(DEBUG){ + println("\n SqlAnalyzer:") + println(s"given String: $sqlString") + val message = if (hasValidSyntax) "has valid syntax" else "has invalid syntax" + println(s"$message") + println(s"Normalized SQL string: $normalizedString") + } - normalizedString match { + //Decides which type of SQL commands will be analyzed + val result = normalizedString match { case i@INSERT_PATTERN(_*) => analyzeInsertStatement(i) @@ -53,59 +66,100 @@ object SqlStringAnalyzer { analyzeSelectStatment(s) case _ => - if (debugMode) println("Result of SqlAnalyzer: SqlAnalyzer does not support: " + string) + if(DEBUG) println("Result of SqlAnalyzer: does not support: "+ sqlString) false } + result } - def preprozesing(string: String) = { - val hasCorrectSyntax = checkSQLStringSyntax3(string) - val normalizedString = if (hasCorrectSyntax) normalizeSQLString(string) else "" - if (debugMode && !hasCorrectSyntax) { - println("Wrong Syntax :" + string) - } - normalizedString - } - - def checkSQLStringSyntax3(input: String): Boolean = { - - input match { - case INSERT_PATTERN(_*) => true - case SELECT_PATTERN(_*) => true - case UPDATE_PATTERN(_*) => true + /** + * Checks the string for specified and valid SQL syntax + * + * @param str The string to check. + * @return True, if the string has a recognized valid SQL syntax. + */ + def hasValidSqlSyntax(str: String): Boolean = + str match { + case INSERT_PATTERN(_*) | SELECT_PATTERN(_*) | UPDATE_PATTERN(_*) => true case _ => false } - } + /** + * Returns a normalized version of the given SQL string by: + * replacing multiple whitespaces with a single whitespace, + * surrounding special characters with a whitespace, + * converting the string to upper case, + * and removing leading/trailing + * + * @param sqlString the SQL string to be normalized + * @return the normalized SQL string + */ def normalizeSQLString(sqlString: String): String = { + val whitespacePattern = raw"(\s+)" + val specialCharsPattern = raw"([,;=()])" + val whitespaceReplacement = " " + val specialCharsReplacement = " $1 " + + sqlString + // Remove double whitespace + .replaceAll(whitespacePattern, whitespaceReplacement) + //Surround special characters by whitespace + .replaceAll(specialCharsPattern, specialCharsReplacement) + // Normalize case sensitivity + .toUpperCase() + //remove leading/following spaces + .trim() + } - val sb = new StringBuilder(sqlString) - val pattern1 = raw"(\s+)" - val pattern2 = raw"([,;=()])" - val replacement1 = " " - val replacement2 = " $1 " + /** + * Checks if the values in a column are tainted, based on the INSERT command. + * + * @param normalizedString The normalized insert command to be analyzed + * @return True, if any tainted columns or values were detected. + */ + def analyzeInsertStatement(normalizedString: String) = { + val (table, columns, valueGroups) = extractInformationOfInsert(normalizedString) - // Doppelte Leerzeichen entfernen - sb.replace(0, sb.length(), sb.toString().replaceAll(pattern1, replacement1)) + //Checks for taint identifiers in table columns.If found, the table is considered to be tainted. + var taintedColumns: Set[String] = columns.filter(column => taintMemory.isTainted(column)).toSet + + //searches for taint identifiers within value groups, and assigns any columns that contain them as tainted. + valueGroups.foreach(valueGroup => { + for (i <- valueGroup.indices) { + if (taintMemory.isTainted(valueGroup (i))) taintedColumns += columns(i).trim + } + }) - // Sonderzeichen durch ein Leerzeichen umgeben - sb.replace(0, sb.length(), sb.toString().replaceAll(pattern2, replacement2)) + // Checks for taint identifiers in table. If so, all addressed columns are considered as tainted + if (taintMemory.isTainted(table)) taintedColumns ++= columns.map(str => str.trim) - // Groß-/Kleinschreibung normalisieren und führenden/folgnde leerzeichen entfernen - val normalizedStrig = sb.toString().toUpperCase().trim + //If tainted columns are found, they are recorded along with the corresponding table. + if (taintedColumns.nonEmpty) taintMemory.taintTableAndColumns(table.trim, taintedColumns) - if (debugMode) println("String normalized to: " + normalizedStrig) - normalizedStrig + if(DEBUG){ + println("analyzeInsertStatement:") + println(" extracted information:") + println(s" table name: $table") + println(s" column names: ${columns.mkString("(", ",", ")")}") + println(s" valueGroups: ${valueGroups.mkString("(", ",", ")")}") + println(s" tainted columns: ${taintedColumns.mkString("(", ",", ")")} of table $table \n") + } + taintedColumns.nonEmpty } - def extractInformationOfInsert4(query: String): (String, Seq[String], Seq[Seq[String]]) = { + /** + * Extracts addressed fields from an INSERT query + * + * @param query the query string to extract information from. + * @return A tuple containing the extracted table name, column names, and value groups. + */ + def extractInformationOfInsert(query: String): (String, Seq[String], Seq[Seq[String]]) = { val insertPattern = "(?i)INSERT\\s*(?:IGNORE\\s+)?INTO\\s*(\\w+)\\s*\\((.*?)\\)\\s*VALUES\\s*(.*);?".r - var extractedTableName = "" var extractedColumnNames: Seq[String] = Seq() var extractedValueGroups: Seq[Seq[String]] = Seq() - val pre = query.replace(";", "") + val pre = query.replace(";","") pre match { case insertPattern(tableName, columns, values) => @@ -116,116 +170,58 @@ object SqlStringAnalyzer { case _ => } - (extractedTableName, extractedColumnNames, extractedValueGroups) - } - - - def analyzeInsertStatement(normalizedString: String) = { - val (table, columns, valueGroups) = extractInformationOfInsert4(normalizedString) - var taintedColumns: Set[String] = columns.filter(column => taintMemory.isTainted(column)).toSet - - valueGroups.foreach(valueGroup => { - for (i <- valueGroup.indices) { - if (taintMemory.isTainted(valueGroup(i))) taintedColumns += columns(i).trim - } - }) - if (taintMemory.isTainted(table)) taintedColumns ++= columns.map(str => str.trim) - if (taintedColumns.nonEmpty) taintMemory.taintTableAndColums(table.trim, taintedColumns) - - taintedColumns.nonEmpty - - - if (debugMode) println("Result of analyzeInsertStatement: \n" - + " analyzed String: " + normalizedString + " \n " - + taintedColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted \n") -} - - def extractInformationOfSelect(normalizedString: String): (String, Seq[String]) ={ - - val selectPatter01 = raw"(SELECT) (.+) FROM ((\w|-|'|´)+) (WHERE (.+))?; ".r - var tableNameReturn = "" - var columnNamesReturn: Seq[String] = Seq() - normalizedString match { - case selectPatter01(_, columns, tableName, _, _, condition) => - columnNamesReturn = columns.split(",").map(_.trim).toSeq - tableNameReturn = tableName - case _ => - } - (tableNameReturn, columnNamesReturn) - } - - def extractInformationOfSelect2(normalizedString: String): (String, Seq[String]) = { - val selectRegex = "SELECT\\s+(DISTINCT\\s+)?(.*?)\\s+FROM\\s+([^\\s]+)\\s*(WHERE\\s+(.*))?".r - var tableNameReturn = "" - var columnNamesReturn: Seq[String] = Seq() - selectRegex.findFirstMatchIn(normalizedString) match { - case Some(str) => - columnNamesReturn = str.group(2).split(",").map(_.trim).toSeq - tableNameReturn = str.group(3) - case _ => - } - (tableNameReturn, columnNamesReturn) + (extractedTableName, extractedColumnNames, extractedValueGroups) } + /** + * Checks if the values in a column are tainted, based on the UPDATE command. + * + * @param normalizedString The normalized UPDATE command to be analyzed + * @return True, if any tainted columns or values were detected. + */ + def analyzeUpdateStatement(normalizedString: String): Boolean = { + val (table, columnAndValues) = extractInformationOfUpdate(normalizedString) + var taintColumns: Set[String] = Set() - - def analyzeSelectStatment(normalizedString: String) = { - val (table, columns) = extractInformationOfSelect2(normalizedString) - var tempColumn: Set[String] = columns.toSet - - if (columns.contains("*")) { - - taintMemory.taintedTableAndColumns.get(table) match { - case Some(x) => tempColumn = x - case None => + for ((colmn, value) <- columnAndValues) { + if (taintMemory.isTainted(value) || taintMemory.isTainted(colmn) || taintMemory.isTainted(table)) { + taintColumns += colmn } } - if (taintMemory.isTainted(table)) taintMemory.taintTableAndColums(table, tempColumn) - val (hastaints, taintedColumns) = taintMemory.columnsAreTainted(table, tempColumn) - if(debugMode)println("Result of analyzeSelectStatment: \n" - + " analyzed String: " + normalizedString + " \n " - + taintedColumns.mkString("(", ",", ")") + " of table " + table + " where tainted \n") + if (taintColumns.nonEmpty) taintMemory.taintTableAndColumns(table, taintColumns) - hastaints - } - - def extractInformationOfUpdate(normalizedString: String) = { - - val updatePattern = raw"(UPDATE) (.+) SET (.+) ; ".r - val valueAndConditionPattern = raw"('?\w+'? = '?\w+'? (, '?\w+'? = '?\w+'? )*)(WHERE .+)?".r - - var table = "" - var temp03: Set[(String, String)] = Set() - - normalizedString match { - case updatePattern(_, tableName, valuesAndCondition) => - table = tableName - - valuesAndCondition match { - case valueAndConditionPattern(a, _*) => - val temp = a.split(",").map(_.trim).toSet - temp03 = temp.map(str => str.split("=").map(_.trim)).map(array => (array(0), array(1))) - case _ => - } - case _ => + if (DEBUG) { + println("analyzeUpdateStatment:") + println(" extracted information:") + println(s" table name: $table") + println(s" column and Values: ${columnAndValues.mkString("(", ",", ")")}") + println(s" tainted columns: ${taintColumns.mkString("(", ",", ")")} of table $table \n") } + taintColumns.nonEmpty - (table, temp03) } - def extractInformationOfUpdate2(normalizedString: String): (String, Seq[(String, String)]) = { + /** + * Extracts addressed fields from an UPDATE query + * + * @param query the query string to extract information from. + * @return A tuple containing the extracted table name, as well as a sequence of column and value tuples. + */ + def extractInformationOfUpdate(query: String): (String, Seq[(String, String)]) = { val updateRegex = "UPDATE\\s+([^\\s]+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+?)\\s*;?\\s*$".r var table = "" var columnValues: Seq[(String, String)] = Seq() - updateRegex.findFirstMatchIn(normalizedString) match { + updateRegex.findFirstMatchIn(query) match { case Some(m) => table = m.group(1) + + //extracts pairs of columns and their corresponding values. columnValues = m.group(2).split(",").map(_.trim).toIndexedSeq.map { value => - val parts = value.split("=").map(_.trim) + val parts = value.split("=").map(_.trim) (parts(0), parts(1)) } case _ => @@ -233,54 +229,60 @@ object SqlStringAnalyzer { (table, columnValues) } - def analyzeUpdateStatement(normalizedString: String) = { - val (table, columnAndValues) = extractInformationOfUpdate2(normalizedString) - var taintColumns: Set[String] = Set() - for ((colmn, value) <- columnAndValues) { - if (taintMemory.isTainted(value) || taintMemory.isTainted(colmn) || taintMemory.isTainted(table)) { - taintColumns += colmn - } - } - - if (taintColumns.nonEmpty) taintMemory.taintTableAndColums(table, taintColumns) - if(debugMode)println("Result of analyzeUpdateStatment: \n" - + " analyzed String: " + normalizedString + " \n " - + taintColumns.mkString("(", ",", ")") + " of table " + table + " will be tainted") + /** + * Checks if columns queried in SELECT commands could contain tainted information + * + * @param normalizedString The normalized SELECT command to be analyzed + * @return True, if any of the queried columns is tainted + */ + def analyzeSelectStatment(normalizedString: String): Boolean = { + val (tableName, selectedColumns) = extractInformationOfSelect(normalizedString) - taintColumns.nonEmpty + var columnsToTaint: Set[String] = selectedColumns.toSet + // handles "SELECT * ..." + if (selectedColumns.contains("*")) { + taintMemory.getTaintedTableAndColumns(tableName) match { + case Some(x) => columnsToTaint = x + case None => + } + } + if (taintMemory.isTainted(tableName)) taintMemory.taintTableAndColumns(tableName, columnsToTaint) + + val taintedColumns = taintMemory.getTaintedColumns(tableName, columnsToTaint) + if (DEBUG) { + println("analyzeSelectStatment:") + println(" extracted information:") + println(s" table name: $tableName") + println(s" selected columns: ${selectedColumns.mkString("(", ",", ")")}") + println(s" tainted columns: ${taintedColumns.mkString("(", ",", ")")} of table $tableName \n") + } + taintedColumns.nonEmpty } - /* -def extractInformationOfInsert(normalizedString: String) = { - - val insertPatter01 = raw"(INSERT (IGNORE\s)?INTO) (.+) (\( .+ \)) (VALUES) (\(.+\)) ; ".r - - val columnNamesRegex = "\\(([^)]*)\\)".r - val valuesRegex = "VALUES\\s*\\(([^)]*)\\)".r - val insertRegex = raw"(INSERT (IGNORE\s)?INTO) (.+) (\( .+ \)) (VALUES) (\(.+\)) ; ".r - - - var extractedTableName = "" - var extractedColumnNames: Seq[String] = Seq() - var extractedValueGroups: Set[Array[String]] = Set(Array()) - - - normalizedString match { - case insertPatter01(_, _, tableName, columnNames, _, values) => - extractedTableName = tableName - extractedColumnNames = columnNames.replaceAll(raw"(\(|\))", "").split(",").map(_.trim).toSeq - val removedBrackets = values.split(raw"\) , \(").map(x => x.replaceAll(raw"(\(|\))", "")) - extractedValueGroups = removedBrackets.map(x => x.split(",").map(_.trim)).toSet - case insertRegex(_, _, tableName, columnNamesRegex(columnNames), _, valuesRegex(values)) => - println("klappt") + /** + * Extracts addressed fields from an SELECT query + * + * @param query the string to extract information from. + * @return A tuple containing the extracted table name, as well as a sequence of column + */ + def extractInformationOfSelect(query: String): (String, Seq[String]) = { + val selectRegex = "SELECT\\s+(DISTINCT\\s+)?(.*?)\\s+FROM\\s+([^\\s]+)\\s*(WHERE\\s+(.*))?".r + var table = "" + var columns: Seq[String] = Seq() + selectRegex.findFirstMatchIn(query) match { + case Some(str) => + columns = str.group(2).split(",").map(_.trim).toSeq + table = str.group(3) + case _ => + } + (table, columns) + } - case _ => + def getTaintMemory(): SqlTaintMemory ={ + taintMemory } - (extractedTableName, extractedColumnNames, extractedValueGroups) } - */ -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer02.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer02.scala deleted file mode 100644 index 1861ff92a2..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlStringAnalyzer02.scala +++ /dev/null @@ -1,284 +0,0 @@ - -package org.opalj.tac.fpcf.analyses.sql - -object SqlStringAnalyzer02 { - - /** - * Object to store information about tainted tables and columns. - * The specified taint identifiers are used as input to mark tainted data. - */ - var taintMemory = new SqlTaintMemory02(Set("TAINTED_VALUE","'TAINTED_VALUE'")) - - /** - * When 'true', enables console output that provides information on the results of individual helper methods. - */ - val DEBUG = true - - /** - * The specified regex patterns for the types of SQL commands - */ - val INSERT_PATTERN = "^\\s*(?i)INSERT\\s*(?:IGNORE\\s+)?INTO\\s*(\\w+)\\s*\\((.*?)\\)\\s*VALUES\\s*(.*);?".r - val SELECT_PATTERN = "^\\s*(?i)SELECT\\s+(.*)\\s+(?i)FROM\\s+([^\\s]+)(?:\\s+(?i)WHERE\\s+(.+))?\\s*;?".r - val UPDATE_PATTERN = "^\\s*(?i)UPDATE\\s+([^\\s]+)\\s+(?i)SET\\s+(.+)\\s+(?i)WHERE\\s+(.+)\\s*$".r - - /** - * Analyzes INSERT, UPDATE or SELECT commands regarding taint information - * - * @param sqlString SQL command to be analyzed for taint information - * @param taintMemorySQL the object used to store and retrieve taint information - * @return True, if taint information was added or extracted. - */ - def doAnalyze(sqlString: String, taintMemorySQL: SqlTaintMemory02): Boolean = { - taintMemory = taintMemorySQL - doAnalyze(sqlString) - } - - /** - * Analyzes INSERT, UPDATE or SELECT commands regarding taint information - * - * @param sqlString SQL command to be analyzed for taint information - * @return True, if taint information was added or extracted. - */ - def doAnalyze(sqlString: String):Boolean = { - - val hasValidSyntax = hasValidSqlSyntax(sqlString) - - //Normalizes the string to a uniform structure to simplify the analyses - val normalizedString = if (hasValidSyntax) normalizeSQLString(sqlString) else "" - - //Decides which type of SQL commands will be analyzed - val result = normalizedString match { - case i@INSERT_PATTERN(_*) => - analyzeInsertStatement(i) - - case u@UPDATE_PATTERN(_*) => - analyzeUpdateStatement(u) - - case s@SELECT_PATTERN(_*) => - analyzeSelectStatment(s) - - case _ => - if(DEBUG) println("Result of SqlAnalyzer: does not support: "+ sqlString) - false - } - - if(DEBUG){ - println("\n SqlAnalyzer:") - println(s"given String: $sqlString") - val message = if (hasValidSyntax) "has valid syntax" else "has invalid syntax" - println(s"$message") - println(s"Normalized SQL string: $normalizedString") - println(s"Result of SqlAnalyzer: $result \n") - } - result - } - - /** - * Checks the string for specified and valid SQL syntax - * - * @param str The string to check. - * @return True, if the string has a recognized valid SQL syntax. - */ - def hasValidSqlSyntax(str: String): Boolean = - str match { - case INSERT_PATTERN(_*) | SELECT_PATTERN(_*) | UPDATE_PATTERN(_*) => true - case _ => false - } - - /** - * Returns a normalized version of the given SQL string by: - * replacing multiple whitespaces with a single whitespace, - * surrounding special characters with a whitespace, - * converting the string to upper case, - * and removing leading/trailing - * - * @param sqlString the SQL string to be normalized - * @return the normalized SQL string - */ - def normalizeSQLString(sqlString: String): String = { - val whitespacePattern = raw"(\s+)" - val specialCharsPattern = raw"([,;=()])" - val whitespaceReplacement = " " - val specialCharsReplacement = " $1 " - - sqlString - // Remove double whitespace - .replaceAll(whitespacePattern, whitespaceReplacement) - //Surround special characters by whitespace - .replaceAll(specialCharsPattern, specialCharsReplacement) - // Normalize case sensitivity - .toUpperCase() - //remove leading/following spaces - .trim() - } - - /** - * Checks if the values in a column are tainted, based on the INSERT command. - * - * @param normalizedString The normalized insert command to be analyzed - * @return True, if any tainted columns or values were detected. - */ - def analyzeInsertStatement(normalizedString: String) = { - val (table, columns, valueGroups) = extractInformationOfInsert(normalizedString) - - //Checks for taint identifiers in table columns.If found, the table is considered to be tainted. - var taintedColumns: Set[String] = columns.filter(column => taintMemory.isTainted(column)).toSet - - //searches for taint identifiers within value groups, and assigns any columns that contain them as tainted. - valueGroups.foreach(valueGroup => { - for (i <- valueGroup.indices) { - if (taintMemory.isTainted(valueGroup (i))) taintedColumns += columns(i).trim - } - }) - - // Checks for taint identifiers in table. If so, all addressed columns are considered as tainted - if (taintMemory.isTainted(table)) taintedColumns ++= columns.map(str => str.trim) - - //If tainted columns are found, they are recorded along with the corresponding table. - if (taintedColumns.nonEmpty) taintMemory.taintTableAndColumns(table.trim, taintedColumns) - - if(DEBUG){ - println("analyzeInsertStatement:") - println(" extracted information:") - println(s" table name: $table") - println(s" column names: ${columns.mkString("(", ",", ")")}") - println(s" valueGroups: ${valueGroups.mkString("(", ",", ")")}") - println(s" tainted columns: ${taintedColumns.mkString("(", ",", ")")} of table $table") - } - taintedColumns.nonEmpty - } - - /** - * Extracts addressed fields from an INSERT query - * - * @param query the query string to extract information from. - * @return A tuple containing the extracted table name, column names, and value groups. - */ - def extractInformationOfInsert(query: String): (String, Seq[String], Seq[Seq[String]]) = { - val insertPattern = "(?i)INSERT\\s*(?:IGNORE\\s+)?INTO\\s*(\\w+)\\s*\\((.*?)\\)\\s*VALUES\\s*(.*);?".r - - var extractedTableName = "" - var extractedColumnNames: Seq[String] = Seq() - var extractedValueGroups: Seq[Seq[String]] = Seq() - val pre = query.replace(";","") - - pre match { - case insertPattern(tableName, columns, values) => - extractedTableName = tableName - extractedColumnNames = columns.split(",").map(_.trim).toSeq - val removedBrackets = values.split("\\)\\s*,\\s*\\(").map(x => x.replaceAll(raw"(\(|\))", "")) - extractedValueGroups = removedBrackets.map(x => x.split(",").map(_.trim).toSeq).toSeq - - case _ => - } - - (extractedTableName, extractedColumnNames, extractedValueGroups) - } - - /** - * Checks if the values in a column are tainted, based on the UPDATE command. - * - * @param normalizedString The normalized UPDATE command to be analyzed - * @return True, if any tainted columns or values were detected. - */ - def analyzeUpdateStatement(normalizedString: String): Boolean = { - val (table, columnAndValues) = extractInformationOfUpdate(normalizedString) - var taintColumns: Set[String] = Set() - - for ((colmn, value) <- columnAndValues) { - if (taintMemory.isTainted(value) || taintMemory.isTainted(colmn) || taintMemory.isTainted(table)) { - taintColumns += colmn - } - } - - if (taintColumns.nonEmpty) taintMemory.taintTableAndColumns(table, taintColumns) - - if (DEBUG) { - println("analyzeUpdateStatment:") - println(" extracted information:") - println(s" table name: $table") - println(s" column and Values: ${columnAndValues.mkString("(", ",", ")")}") - println(s" tainted columns: ${taintColumns.mkString("(", ",", ")")} of table $table") - } - taintColumns.nonEmpty - - } - - /** - * Extracts addressed fields from an UPDATE query - * - * @param query the query string to extract information from. - * @return A tuple containing the extracted table name, as well as a sequence of column and value tuples. - */ - def extractInformationOfUpdate(query: String): (String, Seq[(String, String)]) = { - val updateRegex = "UPDATE\\s+([^\\s]+)\\s+SET\\s+(.+?)\\s+WHERE\\s+(.+?)\\s*;?\\s*$".r - - var table = "" - var columnValues: Seq[(String, String)] = Seq() - - updateRegex.findFirstMatchIn(query) match { - case Some(m) => - table = m.group(1) - - //extracts pairs of columns and their corresponding values. - columnValues = m.group(2).split(",").map(_.trim).toIndexedSeq.map { value => - val parts = value.split("=").map(_.trim) - (parts(0), parts(1)) - } - case _ => - } - (table, columnValues) - } - - - /** - * Checks if columns queried in SELECT commands could contain tainted information - * - * @param normalizedString The normalized SELECT command to be analyzed - * @return True, if any of the queried columns is tainted - */ - def analyzeSelectStatment(normalizedString: String): Boolean = { - val (tableName, selectedColumns) = extractInformationOfSelect(normalizedString) - - var columnsToTaint: Set[String] = selectedColumns.toSet - - // handles "SELECT * ..." - if (selectedColumns.contains("*")) { - taintMemory.getTaintedTableAndColumns(tableName) match { - case Some(x) => columnsToTaint = x - case None => - } - } - if (taintMemory.isTainted(tableName)) taintMemory.taintTableAndColumns(tableName, columnsToTaint) - - val taintedColumns = taintMemory.getTaintedColumns(tableName, columnsToTaint) - if (DEBUG) { - println("analyzeSelectStatment:") - println(" extracted information:") - println(s" table name: $tableName") - println(s" selected columns: ${selectedColumns.mkString("(", ",", ")")}") - println(s" tainted columns: ${taintedColumns.mkString("(", ",", ")")} of table $tableName") - } - taintedColumns.nonEmpty - } - - /** - * Extracts addressed fields from an SELECT query - * - * @param query the string to extract information from. - * @return A tuple containing the extracted table name, as well as a sequence of column - */ - def extractInformationOfSelect(query: String): (String, Seq[String]) = { - val selectRegex = "SELECT\\s+(DISTINCT\\s+)?(.*?)\\s+FROM\\s+([^\\s]+)\\s*(WHERE\\s+(.*))?".r - var table = "" - var columns: Seq[String] = Seq() - selectRegex.findFirstMatchIn(query) match { - case Some(str) => - columns = str.group(2).split(",").map(_.trim).toSeq - table = str.group(3) - case _ => - } - (table, columns) - } - -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala index bad2d6686a..5de35a6200 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory.scala @@ -1,39 +1,66 @@ package org.opalj.tac.fpcf.analyses.sql -class SqlTaintMemory(dummyTaintRecognitionWords:Set[String]) { +class SqlTaintMemory(defaultTaintIdentifiers:Set[String]) { - var tainted: Set[String] = dummyTaintRecognitionWords - var taintedTableAndColumns = scala.collection.mutable.Map[String, Set[String]]() + private var taintIdentifiers: Set[String] = defaultTaintIdentifiers + private val taintedTableAndColumns = scala.collection.mutable.Map[String, Set[String]]() - def taint(toTaintedElement: String): Unit = { - tainted += toTaintedElement - } - def isTainted(toInspectElement: String): Boolean = { - tainted.contains(toInspectElement) + /** + * Adds a new identifier to the set of taint identifiers. + * + * @param identifier the identifier to add. + */ + def addTaintIdentifier(identifier: String): Unit = { + taintIdentifiers += identifier } - def clearMemory(): Unit = { - tainted = Set.empty[String] + /** + * checks if the string represent a tainted part or not + * + * @param str the String to check. + * @return true, if the string is a taint identifier + */ + def isTainted(str: String): Boolean = { + taintIdentifiers.contains(str) } - def taintTableAndColums(tableName: String, columns: Set[String]): Unit = { - val prev = taintedTableAndColumns.get(tableName) + /** + * Adds a set of tainted columns to a table. + * + * @param tableName the name of the table. + * @param columns the set of column names to add. + */ + def taintTableAndColumns(tableName: String, columns: Set[String]): Unit = { + val prev = taintedTableAndColumns.getOrElseUpdate(tableName, Set()) + taintedTableAndColumns.update(tableName, prev ++ columns) + } - prev match { - case Some(x) => taintedTableAndColumns.put(tableName, x ++ columns) - case None => taintedTableAndColumns.put(tableName, columns) + /** + * Checks if any of the given columns in the specified table are tainted. + * + * @param tableName the name of the table to check. + * @param columns the set of column names to check. + * @return the set of given columns that have been tainted for the given table. + */ + def getTaintedColumns(tableName: String, columns: Set[String]): Set[String] = { + taintedTableAndColumns.get(tableName.trim) match { + case Some(taintedColumns) => columns.intersect(taintedColumns) + case None => Set() } } - def columnsAreTainted(tableName: String, columns: Set[String]) = { - val empty: Set[String] = Set() - - taintedTableAndColumns.find { case (key, _) => key.trim == tableName.trim } match { - case Some((_, x)) => ((x & columns).size > 0, (x & columns)) - case None => (false, empty) - } + /** + * retrieves a Set of all columns in the specified table that have been tainted. + * + * @param tableName the name of the table to retrieve tainted columns from. + * @return option value containing a Set of tainted column names associated with the specified tableName. or None if no columns in the table are tainted. + */ + def getTaintedTableAndColumns(tableName: String): Option[Set[String]] = { + taintedTableAndColumns.get(tableName) } + + } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory02.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory02.scala deleted file mode 100644 index 905c978392..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintMemory02.scala +++ /dev/null @@ -1,66 +0,0 @@ - -package org.opalj.tac.fpcf.analyses.sql - -class SqlTaintMemory02(defaultTaintIdentifiers:Set[String]) { - - private var taintIdentifiers: Set[String] = defaultTaintIdentifiers - private val taintedTableAndColumns = scala.collection.mutable.Map[String, Set[String]]() - - - /** - * Adds a new identifier to the set of taint identifiers. - * - * @param identifier the identifier to add. - */ - def addTaintIdentifier(identifier: String): Unit = { - taintIdentifiers += identifier - } - - /** - * checks if the string represent a tainted part or not - * - * @param str the String to check. - * @return true, if the string is a taint identifier - */ - def isTainted(str: String): Boolean = { - taintIdentifiers.contains(str) - } - - /** - * Adds a set of tainted columns to a table. - * - * @param tableName the name of the table. - * @param columns the set of column names to add. - */ - def taintTableAndColumns(tableName: String, columns: Set[String]): Unit = { - val prev = taintedTableAndColumns.getOrElseUpdate(tableName, Set()) - taintedTableAndColumns.update(tableName, prev ++ columns) - } - - /** - * Checks if any of the given columns in the specified table are tainted. - * - * @param tableName the name of the table to check. - * @param columns the set of column names to check. - * @return the set of given columns that have been tainted for the given table. - */ - def getTaintedColumns(tableName: String, columns: Set[String]): Set[String] = { - taintedTableAndColumns.get(tableName.trim) match { - case Some(taintedColumns) => columns.intersect(taintedColumns) - case None => Set() - } - } - - /** - * retrieves a Set of all columns in the specified table that have been tainted. - * - * @param tableName the name of the table to retrieve tainted columns from. - * @return option value containing a Set of tainted column names associated with the specified tableName. or None if no columns in the table are tainted. - */ - def getTaintedTableAndColumns(tableName: String): Option[Set[String]] = { - taintedTableAndColumns.get(tableName) - } - - - -} diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala index b00b683464..5fac5fa128 100644 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala +++ b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem.scala @@ -106,25 +106,17 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { } - -/** - * Returns all possible constant strings. Contains the empty string if at least one was non-constant. + /** + * Definition of rules for implicit data flows for 'executeUpdate', 'executeQuery' function. + * Definition of rules for 'append','valueOf', 'intValue', 'toString', 'sink' and 'source' function + * (not optimized and only usable for specific cases) * - * @param method method - * @param defSites def sites of the queried variable - * @return + * @param call The statement, which invoked the call. + * @param in The facts, which hold before the `call`. + * @param successor + * @return The facts, which hold after the call independently of what happens in the callee + * under the assumption that `in` held before `call`. */ - /*private def getPossibleStrings(method: Method, defSites: IntTrieSet): Set[String] = { - val taCode = tacaiKey(method) - - // TODO: use string analysis here - defSites.map(site => taCode.stmts.apply(site)).map { - case a: Assignment[JavaIFDSProblem.V] if a.expr.isStringConst => - a.expr.asStringConst.value - case _ => "" - } - } */ - override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { var flow:Set[TaintFact] = Set(in) val callStmt = JavaIFDSProblem.asCall(call.stmt) @@ -161,22 +153,22 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { flow case "executeUpdate" => if(callStmt.params.size >0){ - val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) + val possibleParamStrings = getPossibleStrings(callStmt.params(0),call.code,in) possibleParamStrings.foreach(input => if ( input.contains("TAINTED_VALUE") && - SqlStringAnalyzer.checkSQLStringSyntax3(input) + SqlStringAnalyzer.hasValidSqlSyntax(input) && SqlStringAnalyzer.doAnalyze(input, new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'"))) ) { - flow += SqlTaintFact(SqlStringAnalyzer.taintMemory) + flow += SqlTaintFact(SqlStringAnalyzer.getTaintMemory()) }) } flow case "executeQuery" => in match { case sqlTaintFact: SqlTaintFact => - val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) + val possibleParamStrings = getPossibleStrings(callStmt.params(0),call.code,in) possibleParamStrings.foreach(string => { - if(SqlStringAnalyzer.checkSQLStringSyntax3(string) + if(SqlStringAnalyzer.hasValidSqlSyntax(string) && SqlStringAnalyzer.doAnalyze(string,sqlTaintFact.sqlTaintMemory) && call.stmt.isAssignment){ flow += Variable(call.index) @@ -207,24 +199,21 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { } } - override def isTainted(expression: Expr[V], in: TaintFact): Boolean = { - super.isTainted(expression, in) - } - - /** * Returns all possible constant strings. Concatenations of constants using the "append" function are reconstructed. * tainted variables are replaced by taint identifiers. + *(not optimized and only usable for specific cases) * - * - * @param param - * @param stmts - * @param in - * @return + * @param param Expression of a parameter for which a string is to be obtained. + * @param stmts Statements used for reconstruction + * @param in The fact which holds before the call. + * @return a set of possible and modified strings */ - def getPossibleStrings2(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):Set[String] = { + def getPossibleStrings(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):Set[String] = { // Initialize the set of possible strings and taint status var possibleStrings: Set[String] = Set() + + //def sites of the queried variable val defSites = param.asVar.definedBy // Go through all defsites to find out more possible strings @@ -242,24 +231,25 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { case _ if defSiteIndex >= 0 =>{ val expr = stmts(defSiteIndex).asAssignment.expr - //Weitere Untersuchung im Ausdruck + //Further expression investigation expr match { - // Den Wert aus der String Konstante nehmen + // Take the value from the string constant case StringConst(_, value) => currentSetOfString = Set(value) case GetField(_,_,_,_,_) => - // Um ein Wert zu kriegen suchen wir nach dem letzten PutField + //To get a value we look for the last PutField val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) if(lastPut >0){ val putFieldExpression = stmts(lastPut).asPutField.value - currentSetOfString = getPossibleStrings2(putFieldExpression,stmts, in) + currentSetOfString = getPossibleStrings(putFieldExpression,stmts, in) } case gstc:GetStatic => + //To get a value we look for the last PutStatic val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) if(lastPut > 0){ val putExpression = stmts(lastPut).asPutStatic.value - currentSetOfString = getPossibleStrings2(putExpression,stmts, in) + currentSetOfString = getPossibleStrings(putExpression,stmts, in) } case ArrayLoad(pc,index,arrayRef) => @@ -268,21 +258,21 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { stmt.asArrayStore.index.asVar.value == elementIndex) if(lastArrayStoreIndex > 0){ val arrayElementExpression = stmts(lastArrayStoreIndex).asArrayStore.value - currentSetOfString = getPossibleStrings2(arrayElementExpression,stmts,in) + currentSetOfString = getPossibleStrings(arrayElementExpression,stmts,in) } case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => - currentSetOfString = getPossibleStrings2(receiver.asVar, stmts, in) + currentSetOfString = getPossibleStrings(receiver.asVar, stmts, in) case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => - //Alle möglichen Strings die aus dem receiver gewonnen der funktion append werden können - val leftSideString = getPossibleStrings2(receiver.asVar, stmts, in) + //All possible strings that can be obtained from the receiver of the function 'append' + val leftSideString = getPossibleStrings(receiver.asVar, stmts, in) - //Alle möglichen Strings die aus dem parameter gewonnen der funktion append werden können - val rightSideString = getPossibleStrings2(params.head.asVar, stmts, in) + //All possible strings that can be obtained from the parameter of the function 'append' + val rightSideString = getPossibleStrings(params.head.asVar, stmts, in) - //Alle Strings die auf der linken seite entstehen können mit allen auf der rechten kombinieren + //All strings generated on the left side in combination with all strings on the right side. for { leftString <- leftSideString rightString <- rightSideString @@ -292,6 +282,8 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { case _ => currentSetOfString = Set("") } } + + //We assume that an index < 0 is a parameter. case _ => currentSetOfString = Set("PARAM_VALUE") } @@ -299,83 +291,4 @@ class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { } possibleStrings } - - - // TODO: use string analysis here - def getPossibleStringsAndTaintStatus(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):(Set[String], Boolean) = { - val defSites = param.asVar.definedBy - var result: (Set[String], Boolean) = (Set(""), false) - for (defSiteIndex <- defSites) { - if (defSiteIndex >= 0) { - val expr = stmts(defSiteIndex).asAssignment.expr - - in match { - case Variable(index) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case ArrayElement(index: Int,_) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case _ => - expr match { - - case StringConst(_, value) => - result = (result._1 ++ Set(value), result._2 ) - - case GetField(_,_,_,_,_) => - // letzte Putfield suchen von dort weiter - val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) - if(lastPut >0){ - val putFieldExpression = stmts(lastPut).asPutField.value - val tmp = getPossibleStringsAndTaintStatus(putFieldExpression,stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 ) - } - - - case gstc:GetStatic => - val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) - if(lastPut > 0){ - val putExpression = stmts(lastPut).asPutStatic.value - val tmp = getPossibleStringsAndTaintStatus(putExpression,stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 ) - } - - case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => - val tmp = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 ) - - case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => - - - val leftSideString = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) - val rightSideString = getPossibleStringsAndTaintStatus(params.head.asVar, stmts, in) - var possibleString: Set[String] = Set() - - for { - leftString <- leftSideString._1 - rightString <- rightSideString._1 - } { - possibleString += leftString + rightString - } - result = (result._1 ++ possibleString, result._2 || leftSideString._2 || rightSideString._2) - - case _ => - } - } - }else{ - in match { - case Variable(index) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case ArrayElement(index: Int,_) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case _ => result = (result._1 ++ Set("PARAM_VALUE"), false) - } - } - } - - result - } - } diff --git a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala b/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala deleted file mode 100644 index ea5de98a22..0000000000 --- a/DEVELOPING_OPAL/demos/src/main/scala/org/opalj/tac/fpcf/analyses/sql/SqlTaintProblem02.scala +++ /dev/null @@ -1,395 +0,0 @@ -/* BSD 2-Clause License - see OPAL/LICENSE for details. */ -package org.opalj.tac.fpcf.analyses.sql - -import org.opalj.br.Method -import org.opalj.br.analyses.SomeProject -import org.opalj.ifds.IFDSAnalysis -import org.opalj.tac.fpcf.analyses.ifds.JavaIFDSProblem.V -import org.opalj.tac.fpcf.analyses.ifds.taint.ForwardTaintProblem -import org.opalj.tac.fpcf.analyses.ifds.{JavaIFDSProblem, JavaMethod, JavaStatement} -import org.opalj.tac.fpcf.properties._ -import org.opalj.tac._ - - -class SqlTaintAnalysis(project: SomeProject) - extends IFDSAnalysis()(project, new SqlTaintProblem(project), Taint) - -/** - * Java IFDS analysis that is able to resolve calls of sql statements. - * - * @param p project - */ -class SqlTaintProblem(p: SomeProject) extends ForwardTaintProblem(p) { - - /** - * Called, when the exit to return facts are computed for some `callee` with the null fact and - * the callee's return value is assigned to a variable. - * Creates a taint, if necessary. - * - * @param callee The called method. - * @param call The call. - * @return Some variable fact, if necessary. Otherwise none. - */ - override protected def createTaints(callee: Method, call: JavaStatement): Set[TaintFact] = - if (callee.name == "source") Set(Variable(call.index)) - else Set.empty - - /** - * Called, when the call to return facts are computed for some `callee`. - * Creates a FlowFact, if necessary. - * - * @param callee The method, which was called. - * @param call The call. - * @return Some FlowFact, if necessary. Otherwise None. - */ - override protected def createFlowFact( - callee: Method, - call: JavaStatement, - in: TaintFact - ): Option[FlowFact] = - if (callee.name == "sink" && in == Variable(-2)) - Some(FlowFact(Seq(JavaMethod(call.method), JavaMethod(callee)))) - else None - - /** - * The entry points of this analysis. - */ - override def entryPoints: Seq[(Method, TaintFact)] = - for { - m <- p.allMethodsWithBody - if m.name == "main" - } yield m -> TaintNullFact - - /** - * Checks, if some `callee` is a sanitizer, which sanitizes its return value. - * In this case, no return flow facts will be created. - * - * @param callee The method, which was called. - * @return True, if the method is a sanitizer. - */ - override protected def sanitizesReturnValue(callee: Method): Boolean = callee.name == "sanitize" - - /** - * Called in callToReturnFlow. This method can return whether the input fact - * will be removed after `callee` was called. I.e. the method could sanitize parameters. - * - * @param call The call statement. - * @param in The fact which holds before the call. - * @return Whether in will be removed after the call. - */ - override protected def sanitizesParameter(call: JavaStatement, in: TaintFact): Boolean = false - - /** - * - * @param statement The analyzed statement. - * @param in The fact which holds before the execution of the `statement`. - * @param predecessor The predecessor of the analyzed `statement`, for which the data flow shall be - * computed. Used for phi statements to distinguish the flow. - * @return The facts, which hold after the execution of `statement` under the assumption - * that the facts in `in` held before `statement` and `successor` will be - * executed next. - */ - override def normalFlow(statement:JavaStatement,in:TaintFact,predecessor:Option[JavaStatement]): Set[TaintFact] ={ - in match { - case sqlTaintFact:SqlTaintFact=> Set(sqlTaintFact) - case _ => super.normalFlow(statement, in, predecessor) - } - } - - /** - * - * @param call The analyzed call statement. - * @param callee The called method, for which the data flow shall be computed. - * @param in The fact which holds before the execution of the `call`. - * @return The facts, which hold after the execution of `statement` under the assumption that - * the facts in `in` held before `statement` and `statement` calls `callee`. - */ - override def callFlow(call: JavaStatement, callee: Method, in: TaintFact): Set[TaintFact] = { - in match { - case sqlTaintFact: SqlTaintFact => Set(sqlTaintFact) - case _=> super.callFlow(call, callee, in) - } - } - - /** - * - * @param exit The statement, which terminated the `callee`. - * @param in The fact which holds before the execution of the `exit`. - * @param call The statement, which called the `callee`. - * @param callFact - * @param successor - * @return The facts, which hold after the execution of `exit` in the caller's context - * under the assumption that `in` held before the execution of `exit` and that - * `successor` will be executed next. - */ - override def returnFlow(exit: JavaStatement, in: TaintFact, call: JavaStatement, callFact: TaintFact, successor: JavaStatement): Set[TaintFact] = { - in match { - case sqlTaintFact: SqlTaintFact => Set(sqlTaintFact) ++ super.returnFlow(exit, in, call, callFact, successor) - case _=> super.returnFlow(exit, in, call, callFact, successor) - } - } - - - -/** - * Returns all possible constant strings. Contains the empty string if at least one was non-constant. - * - * @param method method - * @param defSites def sites of the queried variable - * @return - */ - /*private def getPossibleStrings(method: Method, defSites: IntTrieSet): Set[String] = { - val taCode = tacaiKey(method) - - // TODO: use string analysis here - defSites.map(site => taCode.stmts.apply(site)).map { - case a: Assignment[JavaIFDSProblem.V] if a.expr.isStringConst => - a.expr.asStringConst.value - case _ => "" - } - } */ - - override def callToReturnFlow(call: JavaStatement, in: TaintFact, successor: JavaStatement): Set[TaintFact] = { - var flow:Set[TaintFact] = Set(in) - val callStmt = JavaIFDSProblem.asCall(call.stmt) - - callStmt.name match { - case "valueOf" if callStmt.declaringClass.toJava == "java.lang.Integer" => - in match { - case Variable(index) if callStmt.params.find(parm => parm.asVar.definedBy.contains(index)).nonEmpty => flow += Variable(call.index) - case _ => - } - flow - case "intValue" => - val leftSideVar = callStmt.receiverOption.get.asVar - in match { - case Variable(index) if leftSideVar.definedBy.contains(index) => flow += Variable(call.index) - case _ => - } - flow - case "append" => - val rightSideVar = callStmt.params.head.asVar - val leftSideVar = callStmt.receiverOption.get.asVar - in match { - case Variable(index) if leftSideVar.definedBy.contains(index)||rightSideVar.definedBy.contains(index) => - flow += Variable(call.index) - case _=> - } - flow - case "toString" => - val leftSideVar = callStmt.receiverOption.get.asVar - in match { - case Variable(index) if leftSideVar.definedBy.contains(index) => flow += Variable(call.index) - case _ => - } - flow - case "executeUpdate" => - if(callStmt.params.size >0){ - val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) - possibleParamStrings.foreach(input => - if ( - input.contains("TAINTED_VALUE") && - SqlStringAnalyzer.checkSQLStringSyntax3(input) - && SqlStringAnalyzer.doAnalyze(input, new SqlTaintMemory(Set("TAINTED_VALUE","'TAINTED_VALUE'"))) ) { - flow += SqlTaintFact(SqlStringAnalyzer.taintMemory) - }) - } - flow - case "executeQuery" => - in match { - case sqlTaintFact: SqlTaintFact => - val possibleParamStrings = getPossibleStrings2(callStmt.params(0),call.code,in) - possibleParamStrings.foreach(string => { - if(SqlStringAnalyzer.checkSQLStringSyntax3(string) - && SqlStringAnalyzer.doAnalyze(string,sqlTaintFact.sqlTaintMemory) - && call.stmt.isAssignment){ - flow += Variable(call.index) - } - }) - case _ => - } - flow - case "source" => - in match { - case TaintNullFact => flow += Variable(call.index) - case _ => - } - flow - case "sink" => - in match { - case Variable(index) if callStmt.params.find(parm => parm.asVar.definedBy.contains(index)).nonEmpty => - flow += FlowFact(Seq(JavaMethod(call.method))) - case _=> - } - flow - case _ => - icfg.getCalleesIfCallStatement(call) match { - case Some(callee) if callee.isEmpty => flow - case _ => super.callToReturnFlow(call, in, successor) - } - - } - } - - override def isTainted(expression: Expr[V], in: TaintFact): Boolean = { - super.isTainted(expression, in) - } - - - def getPossibleStrings2(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):Set[String] = { - // Initialize the set of possible strings and taint status - var possibleStrings: Set[String] = Set() - val defSites = param.asVar.definedBy - - // Durche alle Defsites durchgehen um weitere möglichen Strings zu ermitteln - for (defSiteIndex <- defSites) { - var currentSetOfString: Set[String] = Set() - - in match { - // Wenn der ausdruck für den Intext getaintet ist, ersetzen wir in durch den TAINTIDENTIFIER - case Variable(index) if index == defSiteIndex => - currentSetOfString = Set("TAINTED_VALUE") - case ArrayElement(index: Int,_) if index == defSiteIndex => - currentSetOfString = Set("TAINTED_VALUE") - case InstanceField(index: Int, _, _) if index == defSiteIndex=> - currentSetOfString = Set("TAINTED_VALUE") - case _ if defSiteIndex >= 0 =>{ - val expr = stmts(defSiteIndex).asAssignment.expr - - //Weitere Untersuchung im Ausdruck - expr match { - // Den Wert aus der String Konstante nehmen - case StringConst(_, value) => currentSetOfString = Set(value) - - case GetField(_,_,_,_,_) => - // Um ein Wert zu kriegen suchen wir nach dem letzten PutField - val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) - if(lastPut >0){ - val putFieldExpression = stmts(lastPut).asPutField.value - currentSetOfString = getPossibleStrings2(putFieldExpression,stmts, in) - } - - case gstc:GetStatic => - val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) - if(lastPut > 0){ - val putExpression = stmts(lastPut).asPutStatic.value - currentSetOfString = getPossibleStrings2(putExpression,stmts, in) - } - - case ArrayLoad(pc,index,arrayRef) => - val elementIndex = index.asVar.value - val lastArrayStoreIndex = stmts.lastIndexWhere(stmt => stmt.astID == ArrayStore.ASTID && - stmt.asArrayStore.index.asVar.value == elementIndex) - if(lastArrayStoreIndex > 0){ - val arrayElementExpression = stmts(lastArrayStoreIndex).asArrayStore.value - currentSetOfString = getPossibleStrings2(arrayElementExpression,stmts,in) - } - - case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => - currentSetOfString = getPossibleStrings2(receiver.asVar, stmts, in) - - case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => - - //Alle möglichen Strings die aus dem receiver gewonnen der funktion append werden können - val leftSideString = getPossibleStrings2(receiver.asVar, stmts, in) - - //Alle möglichen Strings die aus dem parameter gewonnen der funktion append werden können - val rightSideString = getPossibleStrings2(params.head.asVar, stmts, in) - - //Alle Strings die auf der linken seite entstehen können mit allen auf der rechten kombinieren - for { - leftString <- leftSideString - rightString <- rightSideString - } { - currentSetOfString += leftString + rightString - } - case _ => currentSetOfString = Set("") - } - } - case _ => currentSetOfString = Set("PARAM_VALUE") - - } - possibleStrings = possibleStrings ++ currentSetOfString - } - possibleStrings - } - - - // TODO: use string analysis here - def getPossibleStringsAndTaintStatus(param:Expr[V], stmts: Array[Stmt[V]], in:TaintFact):(Set[String], Boolean) = { - val defSites = param.asVar.definedBy - var result: (Set[String], Boolean) = (Set(""), false) - for (defSiteIndex <- defSites) { - if (defSiteIndex >= 0) { - val expr = stmts(defSiteIndex).asAssignment.expr - - in match { - case Variable(index) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case ArrayElement(index: Int,_) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case _ => - expr match { - - case StringConst(_, value) => - result = (result._1 ++ Set(value), result._2 ) - - case GetField(_,_,_,_,_) => - // letzte Putfield suchen von dort weiter - val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutField.ASTID) - if(lastPut >0){ - val putFieldExpression = stmts(lastPut).asPutField.value - val tmp = getPossibleStringsAndTaintStatus(putFieldExpression,stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 ) - } - - - case gstc:GetStatic => - val lastPut = stmts.lastIndexWhere(stmt => stmt.astID == PutStatic.ASTID) - if(lastPut > 0){ - val putExpression = stmts(lastPut).asPutStatic.value - val tmp = getPossibleStringsAndTaintStatus(putExpression,stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 ) - } - - case VirtualFunctionCall(_, _, _, name, _, receiver, _) if name == "toString" => - val tmp = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) - result = (result._1 ++ tmp._1, result._2 || tmp._2 ) - - case VirtualFunctionCall(_, _, _, name, _, receiver, params) if name == "append" => - - - val leftSideString = getPossibleStringsAndTaintStatus(receiver.asVar, stmts, in) - val rightSideString = getPossibleStringsAndTaintStatus(params.head.asVar, stmts, in) - var possibleString: Set[String] = Set() - - for { - leftString <- leftSideString._1 - rightString <- rightSideString._1 - } { - possibleString += leftString + rightString - } - result = (result._1 ++ possibleString, result._2 || leftSideString._2 || rightSideString._2) - - case _ => - } - } - }else{ - in match { - case Variable(index) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case ArrayElement(index: Int,_) if(index == defSiteIndex) => - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case InstanceField(index: Int, _, _) if(index == defSiteIndex)=> - result = (result._1 ++ Set("TAINTED_VALUE"), true) - case _ => result = (result._1 ++ Set("PARAM_VALUE"), false) - } - } - } - - result - } - -}

    cyp!?fSG`6TPAB>CQniOPzuY1H6N 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�xWC&uxu#I`fsIL?p$Mi0~5DhNch5ADJVPZi* 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

bH_i=Xk>tTNtWYK*5bon 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ⅈ!Y_^Vk= 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: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