All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.joern.dataflowengineoss.slicing.DataFlowSlicing.scala Maven / Gradle / Ivy

package io.joern.dataflowengineoss.slicing

import io.joern.dataflowengineoss.language.*
import io.joern.x2cpg.utils.ConcurrentTaskUtil
import io.shiftleft.codepropertygraph.generated.{Cpg, PropertyKeys}
import io.shiftleft.codepropertygraph.generated.nodes.*
import io.shiftleft.semanticcpg.language.*
import org.slf4j.LoggerFactory

import java.util.concurrent.Callable
import scala.concurrent.ExecutionContext
import scala.util.{Failure, Success, Try}

object DataFlowSlicing {

  implicit val resolver: ICallResolver = NoResolve
  private val logger                   = LoggerFactory.getLogger(getClass)

  def calculateDataFlowSlice(cpg: Cpg, config: DataFlowConfig): Option[DataFlowSlice] = {
    implicit val implicitConfig: DataFlowConfig = config

    val tasks = (config.fileFilter match {
      case Some(fileName) => cpg.file.nameExact(fileName).method.call
      case None           => cpg.call
    }).method.withMethodNameFilter.withMethodParameterFilter.withMethodAnnotationFilter.call.withExternalCalleeFilter.withSinkFilter
      .map(c => () => new TrackDataFlowTask(config, c).call())
      .l

    logger.info(s"Processing ${tasks.size} sinks")

    ConcurrentTaskUtil
      .runUsingThreadPool(tasks.iterator, config.parallelism.getOrElse(Runtime.getRuntime.availableProcessors()))
      .flatMap {
        case Success(slice) => slice
        case Failure(e) =>
          logger.warn("Exception encountered during slicing task", e)
          None
      }
      .reduceOption { (a, b) => DataFlowSlice(a.nodes ++ b.nodes, a.edges ++ b.edges) }
  }

  private class TrackDataFlowTask(config: DataFlowConfig, c: Call) {

    def call(): Option[DataFlowSlice] = {
      val sinks           = c.argument.l
      val sliceNodes      = sinks.iterator.repeat(_.ddgIn)(_.maxDepth(config.sliceDepth).emit).dedup.l
      val sliceNodesIdSet = sliceNodes.id.toSet
      // Lazily set up the rest if the filters are satisfied
      lazy val sliceEdges = sliceNodes.outE
        .filter(x => sliceNodesIdSet.contains(x.dst.id()))
        .map { e => SliceEdge(e.src.id(), e.dst.id(), e.label) }
        .toSet
      lazy val slice = Option(DataFlowSlice(sliceNodes.map(cfgNodeToSliceNode).toSet, sliceEdges))

      if (sliceNodes.isEmpty) None
      else slice
    }

  }

  /** True if the sinks are either calls to external methods or are in external method stubs.
    */
  private def sinksEndAtExternalMethod(sinks: List[Expression]) =
    sinks.isCall.callee.isExternal.nonEmpty || sinks.method.isExternal.nonEmpty

  private def cfgNodeToSliceNode(cfgNode: CfgNode): SliceNode = {
    val sliceNode = SliceNode(
      cfgNode.id(),
      cfgNode.label,
      code = cfgNode.code,
      parentMethod = cfgNode.method.fullName,
      parentFile = cfgNode.file.name.headOption.getOrElse(""),
      lineNumber = cfgNode.lineNumber,
      columnNumber = cfgNode.columnNumber
    )
    cfgNode match {
      case n: Method    => sliceNode.copy(name = n.name, typeFullName = n.methodReturn.typeFullName)
      case n: Return    => sliceNode.copy(name = "RET", typeFullName = n.method.methodReturn.typeFullName)
      case n: MethodRef => sliceNode.copy(name = n.methodFullName, code = n.code)
      case n: TypeRef   => sliceNode.copy(name = n.typeFullName, code = n.code)
      case n =>
        sliceNode.copy(name = n.property(PropertyKeys.Name), typeFullName = n.property(PropertyKeys.TypeFullName))
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy