Skip to content

Commit

Permalink
Enable using function summaries to get more precise and custom DFG ed…
Browse files Browse the repository at this point in the history
…ges for inferred functions. (#1430)

* Initial prototype for specifying function dfg-summaries

* Connect new summaries also to dfg of the declaration

* Handle non-existing list

* Document the file format

* Added yml support

* Move dependency to toml file

* Document the class

* Propagate DFG to calling expressions and arguments

* Fix serialization

* integration test tag again

* integration test tag again

* New property edges

* Flows are now also control flow sensitive

* Copy property edges

* Remove unused method

* Re-add connector method if ControlFlowSensitiveDFG is not executed

* Fix

* Add another test

* Coverage++

* Try more tests, problems with ContextProvider

* Fix problem with types

* Handle supertypes properly, extend test

* More fixes, more tests, more types

* Documentation

* test coverage++

* All the small comments

* More comments, smaller fixes

* Merge methods together

* Less C&P
  • Loading branch information
KuechA authored Mar 19, 2024
1 parent 6c0deda commit bcaee5c
Show file tree
Hide file tree
Showing 22 changed files with 1,397 additions and 36 deletions.
2 changes: 2 additions & 0 deletions cpg-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ dependencies {
implementation(libs.bundles.log4j)
implementation(libs.kotlin.reflect)

implementation(libs.jacksonyml)

testImplementation(libs.junit.params)

testFixturesApi(libs.kotlin.test.junit5) // somehow just using testFixturesApi(kotlin("test")) does not work for testFixtures
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import de.fraunhofer.aisec.cpg.frontends.Language
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.passes.*
import de.fraunhofer.aisec.cpg.passes.inference.DFGFunctionSummaries
import de.fraunhofer.aisec.cpg.passes.order.*
import java.io.File
import java.nio.file.Path
Expand Down Expand Up @@ -102,6 +103,8 @@ private constructor(
*/
val replacedPasses:
Map<Pair<KClass<out Pass<out Node>>, KClass<out Language<*>>>, KClass<out Pass<out Node>>>,
/** This list contains the files with function summaries which should be considered. */
val functionSummaries: DFGFunctionSummaries,
languages: List<Language<*>>,
codeInNodes: Boolean,
processAnnotations: Boolean,
Expand Down Expand Up @@ -240,6 +243,7 @@ private constructor(
private val passes = mutableListOf<KClass<out Pass<*>>>()
private val replacedPasses =
mutableMapOf<Pair<KClass<out Pass<*>>, KClass<out Language<*>>>, KClass<out Pass<*>>>()
private val functionSummaries = mutableListOf<File>()
private var codeInNodes = true
private var processAnnotations = false
private var disableCleanup = false
Expand Down Expand Up @@ -420,6 +424,11 @@ private constructor(
return this
}

fun registerFunctionSummaries(vararg functionSummary: File): Builder {
this.functionSummaries.addAll(functionSummary)
return this
}

/** Registers an additional [Language]. */
fun registerLanguage(language: Language<*>): Builder {
languages.add(language)
Expand Down Expand Up @@ -625,6 +634,7 @@ private constructor(
includeBlocklist,
orderPasses(),
replacedPasses,
DFGFunctionSummaries.fromFiles(functionSummaries),
languages,
codeInNodes,
processAnnotations,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import de.fraunhofer.aisec.cpg.frontends.HasShortCircuitOperators
import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.graph.Node.Companion.EMPTY_NAME
import de.fraunhofer.aisec.cpg.graph.NodeBuilder.log
import de.fraunhofer.aisec.cpg.graph.edge.ContextSensitiveDataflow
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.graph.statements.expressions.AssignExpression
import de.fraunhofer.aisec.cpg.graph.types.ProblemType
Expand Down Expand Up @@ -561,8 +562,21 @@ fun <T> Literal<T>.duplicate(implicit: Boolean): Literal<T> {
duplicate.comment = this.comment
duplicate.file = this.file
duplicate.name = this.name.clone()
duplicate.nextDFG = this.nextDFG
duplicate.prevDFG = this.prevDFG
for (next in this.nextDFGEdges) {
duplicate.addNextDFG(
next.end,
next.granularity,
(next as? ContextSensitiveDataflow)?.callingContext
)
}
for (next in this.prevDFGEdges) {
duplicate.addPrevDFG(
next.start,
next.granularity,
(next as? ContextSensitiveDataflow)?.callingContext
)
}
// TODO: This loses the properties of the edges.
duplicate.nextEOG = this.nextEOG
duplicate.prevEOG = this.prevEOG
duplicate.isImplicit = implicit
Expand Down
29 changes: 24 additions & 5 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Node.kt
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,14 @@ open class Node : IVisitable<Node>, Persistable, LanguageProvider, ScopeProvider
fun addNextDFG(
next: Node,
granularity: Granularity = default(),
callingContext: CallingContext? = null,
) {
val edge = Dataflow(this, next, granularity)
val edge =
if (callingContext != null) {
ContextSensitiveDataflow(this, next, callingContext, granularity)
} else {
Dataflow(this, next, granularity)
}
nextDFGEdges.add(edge)
next.prevDFGEdges.add(edge)
}
Expand All @@ -269,12 +275,21 @@ open class Node : IVisitable<Node>, Persistable, LanguageProvider, ScopeProvider
}
}

/** Adds a [Dataflow] edge from [prev] node to this node, with the given [Granularity]. */
/**
* Adds a [Dataflow] edge from [prev] node to this node, with the given [Granularity] and
* [CallingContext].
*/
open fun addPrevDFG(
prev: Node,
granularity: Granularity = default(),
callingContext: CallingContext? = null,
) {
val edge = Dataflow(prev, this, granularity)
val edge =
if (callingContext != null) {
ContextSensitiveDataflow(prev, this, callingContext, granularity)
} else {
Dataflow(prev, this, granularity)
}
prevDFGEdges.add(edge)
prev.nextDFGEdges.add(edge)
}
Expand All @@ -288,12 +303,16 @@ open class Node : IVisitable<Node>, Persistable, LanguageProvider, ScopeProvider
prev.nextCDGEdges.add(edge)
}

/** Adds a [Dataflow] edge from all [prev] nodes to this node, with the given [Granularity]. */
/**
* Adds a [Dataflow] edge from all [prev] nodes to this node, with the given [Granularity] and
* [CallingContext] if applicable.
*/
fun addAllPrevDFG(
prev: Collection<Node>,
granularity: Granularity = full(),
callingContext: CallingContext? = null,
) {
prev.forEach { addPrevDFG(it, granularity) }
prev.forEach { addPrevDFG(it, granularity, callingContext) }
}

fun addAllPrevPDG(prev: Collection<Node>, dependenceType: DependenceType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
import de.fraunhofer.aisec.cpg.graph.declarations.FieldDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TupleDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression
import org.neo4j.ogm.annotation.RelationshipEntity

Expand Down Expand Up @@ -80,11 +81,40 @@ fun partial(target: Declaration?): PartialDataflowGranularity {
* [granularity].
*/
@RelationshipEntity
class Dataflow(
open class Dataflow(
start: Node,
end: Node,
/** The granularity of this dataflow. */
val granularity: Granularity = default(),
val granularity: Granularity = default()
) : PropertyEdge<Node>(start, end) {
override val label: String = "DFG"
}

sealed interface CallingContext

class CallingContextIn(
/** The call expression that affects this dataflow edge. */
val call: CallExpression
) : CallingContext

class CallingContextOut(
/** The call expression that affects this dataflow edge. */
val call: CallExpression
) : CallingContext

/**
* This edge class defines a flow of data between [start] and [end]. The flow must have a
* [callingContext] which allows for a context-sensitive dataflow analysis. This edge can also have
* a certain [granularity].
*/
@RelationshipEntity
class ContextSensitiveDataflow(
start: Node,
end: Node,
/** The calling context affecting this dataflow. */
val callingContext: CallingContext,
/** The granularity of this dataflow. */
granularity: Granularity,
) : Dataflow(start, end, granularity) {
override val label: String = "DFG"
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package de.fraunhofer.aisec.cpg.graph.edge

import de.fraunhofer.aisec.cpg.graph.declarations.TemplateDeclaration.TemplateInitialization
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import java.util.function.Function

/**
Expand All @@ -49,6 +50,9 @@ class PropertyEdgeConverterManager private constructor() {
addDeserializer("INSTANTIATION") { s: Any? ->
if (s != null) TemplateInitialization.valueOf(s.toString()) else null
}
addSerializer(CallExpression::class.java.name) { it.toString() }
addDeserializer("CALLING_CONTEXT_IN") { null } // TODO: Not supported yet
addDeserializer("CALLING_CONTEXT_OUT") { null } // TODO: Not supported yet
}

fun addSerializer(clazz: String, func: Function<Any, String>) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.Declaration
import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration
import de.fraunhofer.aisec.cpg.graph.edge.CallingContext
import de.fraunhofer.aisec.cpg.graph.edge.Granularity
import de.fraunhofer.aisec.cpg.graph.scopes.Scope
import de.fraunhofer.aisec.cpg.graph.types.HasType
Expand Down Expand Up @@ -147,8 +148,8 @@ open class Reference : Expression(), HasType.TypeObserver, HasAliases {
return super.hashCode()
}

override fun addPrevDFG(prev: Node, granularity: Granularity) {
super.addPrevDFG(prev, granularity)
override fun addPrevDFG(prev: Node, granularity: Granularity, callingContext: CallingContext?) {
super.addPrevDFG(prev, granularity, callingContext)

// We want to propagate assigned types all through the previous DFG nodes. Therefore, we
// override the DFG adding function here and add a type observer to the previous node (if it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import de.fraunhofer.aisec.cpg.frontends.LanguageFrontend
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration
import de.fraunhofer.aisec.cpg.graph.edge.CallingContextIn
import de.fraunhofer.aisec.cpg.graph.edge.Properties
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation
Expand Down Expand Up @@ -355,7 +356,9 @@ object Util {
fun attachCallParameters(target: FunctionDeclaration, call: CallExpression) {
// Add an incoming DFG edge from a member call's base to the method's receiver
if (target is MethodDeclaration && call is MemberCallExpression && !call.isStatic) {
target.receiver?.let { receiver -> call.base?.addNextDFG(receiver) }
target.receiver?.let { receiver ->
call.base?.addNextDFG(receiver, callingContext = CallingContextIn(call))
}
}

// Connect the arguments to parameters
Expand All @@ -370,12 +373,12 @@ object Util {
if (param.isVariadic) {
while (j < arguments.size) {
// map all the following arguments to this variadic param
param.addPrevDFG(arguments[j])
param.addPrevDFG(arguments[j], callingContext = CallingContextIn(call))
j++
}
break
} else {
param.addPrevDFG(arguments[j])
param.addPrevDFG(arguments[j], callingContext = CallingContextIn(call))
}
}
j++
Expand Down
Loading

0 comments on commit bcaee5c

Please sign in to comment.