Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable using function summaries to get more precise and custom DFG edges for inferred functions. #1430

Merged
merged 33 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8475a22
Initial prototype for specifying function dfg-summaries
KuechA Feb 9, 2024
dea03f9
Connect new summaries also to dfg of the declaration
KuechA Feb 9, 2024
4fffec7
Handle non-existing list
KuechA Feb 9, 2024
af5fd2c
Document the file format
KuechA Feb 9, 2024
c1860a7
Added yml support
KuechA Feb 12, 2024
33de45c
Move dependency to toml file
KuechA Feb 12, 2024
fb44dff
Document the class
KuechA Feb 12, 2024
9a9372d
Propagate DFG to calling expressions and arguments
KuechA Feb 12, 2024
e2e6236
Fix serialization
KuechA Feb 12, 2024
35dcf01
integration test tag again
KuechA Feb 13, 2024
eed6009
integration test tag again
KuechA Feb 13, 2024
d34e7ab
New property edges
KuechA Mar 1, 2024
d72d9cc
Flows are now also control flow sensitive
KuechA Mar 1, 2024
e585b57
Merge branch 'main' into dfg-hardcode-file
KuechA Mar 12, 2024
7a22b10
Merge branch 'main' into dfg-hardcode-file
KuechA Mar 12, 2024
2eeb5de
Copy property edges
KuechA Mar 12, 2024
8e0d577
Remove unused method
KuechA Mar 12, 2024
a017f39
Re-add connector method if ControlFlowSensitiveDFG is not executed
KuechA Mar 12, 2024
bbce365
Fix
KuechA Mar 14, 2024
937734e
Add another test
KuechA Mar 14, 2024
bc4b101
Merge branch 'main' into dfg-hardcode-file
KuechA Mar 14, 2024
aac8bbc
Coverage++
KuechA Mar 14, 2024
28bc31e
Try more tests, problems with ContextProvider
KuechA Mar 14, 2024
548cffb
Fix problem with types
KuechA Mar 15, 2024
7501ac8
Handle supertypes properly, extend test
KuechA Mar 15, 2024
f98479f
More fixes, more tests, more types
KuechA Mar 15, 2024
8d1b686
Documentation
KuechA Mar 15, 2024
43ffd46
test coverage++
KuechA Mar 18, 2024
66654b4
All the small comments
KuechA Mar 19, 2024
4b3152a
More comments, smaller fixes
KuechA Mar 19, 2024
47f0737
Merge methods together
KuechA Mar 19, 2024
c320855
Merge branch 'main' into dfg-hardcode-file
KuechA Mar 19, 2024
e9ecdac
Less C&P
KuechA Mar 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -397,6 +401,17 @@ private constructor(
return this
}

inline fun <reified P : Pass<*>> unregisterPass(): Builder {
unregisterPass(P::class)
return this
}

/** Unregister a [Pass]. */
fun unregisterPass(passType: KClass<out Pass<*>>): Builder {
passes.remove(passType)
return this
}

/** Register an additional [Pass]. */
fun registerPass(passType: KClass<out Pass<*>>): Builder {
passes.add(passType)
Expand All @@ -420,6 +435,16 @@ private constructor(
return this
}

fun registerFunctionSummaries(functionSummaries: List<File>): Builder {
this.functionSummaries.addAll(functionSummaries)
return this
}

fun registerFunctionSummary(functionSummary: File): Builder {
this.functionSummaries.add(functionSummary)
return this
}

/** Registers an additional [Language]. */
fun registerLanguage(language: Language<*>): Builder {
languages.add(language)
Expand Down Expand Up @@ -625,6 +650,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,17 @@ 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) {
if (next is ContextsensitiveDataflow)
duplicate.addNextDFGContext(next.end, next.callingContext, next.granularity)
else duplicate.addNextDFG(next.end, next.granularity)
}
for (next in this.prevDFGEdges) {
if (next is ContextsensitiveDataflow)
duplicate.addPrevDFGContext(next.start, next.callingContext, next.granularity)
else duplicate.addNextDFG(next.start, next.granularity)
}
// TODO: This loses the properties of the edges.
duplicate.nextEOG = this.nextEOG
duplicate.prevEOG = this.prevEOG
duplicate.isImplicit = implicit
Expand Down
40 changes: 40 additions & 0 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 @@ -257,6 +257,20 @@ open class Node : IVisitable<Node>, Persistable, LanguageProvider, ScopeProvider
next.prevDFGEdges.add(edge)
}

/**
* Adds a [Dataflow] edge from this node to [next], with the given [CallingContext] and
* [Granularity].
*/
fun addNextDFGContext(
next: Node,
callingContext: CallingContext,
granularity: Granularity = default(),
) {
val edge = ContextsensitiveDataflow(this, next, callingContext, granularity)
nextDFGEdges.add(edge)
next.prevDFGEdges.add(edge)
}

fun removeNextDFG(next: Node?) {
if (next != null) {
val thisRemove =
Expand All @@ -279,6 +293,20 @@ open class Node : IVisitable<Node>, Persistable, LanguageProvider, ScopeProvider
prev.nextDFGEdges.add(edge)
}

/**
* Adds a [Dataflow] edge from [prev] node to this node, with the given [CallingContext] and
* [Granularity].
*/
open fun addPrevDFGContext(
prev: Node,
callingContext: CallingContext,
granularity: Granularity = default(),
) {
val edge = ContextsensitiveDataflow(prev, this, callingContext, granularity)
prevDFGEdges.add(edge)
prev.nextDFGEdges.add(edge)
}

fun addPrevCDG(
prev: Node,
properties: MutableMap<Properties, Any?> = EnumMap(Properties::class.java)
Expand All @@ -296,6 +324,18 @@ open class Node : IVisitable<Node>, Persistable, LanguageProvider, ScopeProvider
prev.forEach { addPrevDFG(it, granularity) }
}

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

fun addAllPrevPDG(prev: Collection<Node>, dependenceType: DependenceType) {
addAllPrevPDGEdges(prev.map { PropertyEdge(it, this) }, 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,43 @@ 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 callExpression: CallExpression
) : CallingContext

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

fun callIn(callExpression: CallExpression): CallingContext {
return CallingContextIn(callExpression)
}

/**
* This edge class defines a flow of data between [start] and [end]. The flow must have a
* [callingContext] and can 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 @@ -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?.addNextDFGContext(receiver, 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.addPrevDFGContext(arguments[j], CallingContextIn(call))
j++
}
break
} else {
param.addPrevDFG(arguments[j])
param.addPrevDFGContext(arguments[j], CallingContextIn(call))
}
}
j++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ package de.fraunhofer.aisec.cpg.passes
import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.edge.Properties
import de.fraunhofer.aisec.cpg.graph.edge.PropertyEdge
import de.fraunhofer.aisec.cpg.graph.edge.partial
import de.fraunhofer.aisec.cpg.graph.edge.*
import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement
import de.fraunhofer.aisec.cpg.graph.statements.ForEachStatement
import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement
Expand Down Expand Up @@ -122,11 +120,15 @@ open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : EOGStarterPass
)
}
} else {
key.addAllPrevDFG(
value.elements.filterNot {
(it is VariableDeclaration || it is ParameterDeclaration) && key == it
value.elements.forEach {
if ((it is VariableDeclaration || it is ParameterDeclaration) && key == it) {
// Nothing to do
} else if (it in edgePropertiesMap && edgePropertiesMap[it] is CallingContext) {
key.addPrevDFGContext(it, (edgePropertiesMap[it] as CallingContext))
} else {
key.addPrevDFG(it)
}
)
}
}
}
}
Expand Down Expand Up @@ -388,6 +390,34 @@ open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : EOGStarterPass
currentNode,
PowersetLattice(identitySetOf(currentNode))
)
} else if (currentNode is CallExpression) {
val functionsWithSummaries =
currentNode.invokes.filter {
it in ctx.config.functionSummaries.functionToChangedParameters
}
if (functionsWithSummaries.isNotEmpty()) {
for (invoked in functionsWithSummaries) {
val changedParams =
ctx.config.functionSummaries.functionToChangedParameters[invoked] ?: mapOf()
for ((param, _) in changedParams) {
if (param == (invoked as? MethodDeclaration)?.receiver) {
doubleState.declarationsState[
((currentNode as? MemberCallExpression)?.base as? Reference)
?.refersTo] = PowersetLattice(identitySetOf(param))
} else if (param is ParameterDeclaration) {
val arg = currentNode.arguments[param.argumentIndex]
doubleState.declarationsState[(arg as? Reference)?.refersTo] =
PowersetLattice(identitySetOf(param))
}
edgePropertiesMap[param] = CallingContextOut(currentNode)
}
}
} else {
doubleState.declarationsState.push(
currentNode,
doubleState.declarationsState[currentEdge.start]
)
}
} else {
doubleState.declarationsState.push(
currentNode,
Expand All @@ -397,6 +427,8 @@ open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : EOGStarterPass
return state
}

val edgePropertiesMap = mutableMapOf<Node, Any>()

/**
* Checks if the node performs an operation and an assignment at the same time e.g. with the
* operators +=, -=, *=, ...
Expand Down
Loading
Loading