Skip to content

Commit

Permalink
Several improvements and cleanups to the Inference system (#1407)
Browse files Browse the repository at this point in the history
  • Loading branch information
oxisto authored Feb 6, 2024
1 parent eeb32fc commit 288ea53
Show file tree
Hide file tree
Showing 19 changed files with 470 additions and 115 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
*/
package de.fraunhofer.aisec.cpg.testcases

import de.fraunhofer.aisec.cpg.InferenceConfiguration
import de.fraunhofer.aisec.cpg.TranslationConfiguration
import de.fraunhofer.aisec.cpg.frontends.TestLanguage
import de.fraunhofer.aisec.cpg.graph.array
Expand Down Expand Up @@ -99,6 +100,7 @@ class Query {
.defaultPasses()
.registerLanguage(TestLanguage("."))
.registerPass<EdgeCachePass>()
.inferenceConfiguration(InferenceConfiguration.builder().enabled(false).build())
.build()
) =
testFrontend(config).build {
Expand Down Expand Up @@ -158,7 +160,7 @@ class Query {
memberCall("log", ref("logger")) {
member("INFO", ref("Level", makeMagic = false))
literal("put ", t("string")) +
member("a", ref("a", makeMagic = false)) +
member("a", ref("sc", makeMagic = false)) +
literal(" into highlyCriticalOperation()", t("string"))
}
returnStmt {}
Expand All @@ -175,6 +177,9 @@ class Query {
.defaultPasses()
.registerLanguage(TestLanguage("."))
.registerPass<EdgeCachePass>()
.inferenceConfiguration(
InferenceConfiguration.builder().inferFunctions(false).build()
)
.build()
) =
testFrontend(config).build {
Expand Down Expand Up @@ -215,7 +220,7 @@ class Query {
memberCall("log", ref("logger")) {
member("INFO", ref("Level", makeMagic = false))
literal("put ", t("string")) +
member("a", ref("a", makeMagic = false)) +
member("a", ref("sc", makeMagic = false)) +
literal(" into highlyCriticalOperation()", t("string"))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,58 @@ import org.apache.commons.lang3.builder.ToStringStyle
*/
class InferenceConfiguration
private constructor(
/** Enables smart guessing of cast vs. call expressions in the C/C++ language frontend */
/** Enables or disables the inference system as a whole. */
val enabled: Boolean,

/** Enables smart guessing of cast vs. call expressions in the C/C++ language frontend. */
val guessCastExpressions: Boolean,

/** Enables the inference of record declarations */
/** Enables the inference of record declarations. */
val inferRecords: Boolean,

/** Enables the inference of function declarations. */
val inferFunctions: Boolean,

/** Enables the inference of variables, such as global variables. */
val inferVariables: Boolean,

/**
* Uses heuristics to add DFG edges for call expressions to unresolved functions (i.e.,
* functions not implemented in the given source code).
*/
val inferDfgForUnresolvedSymbols: Boolean
) {
class Builder(
var guessCastExpressions: Boolean = false,
var inferRecords: Boolean = false,
var inferDfgForUnresolvedCalls: Boolean = true
private var enabled: Boolean = true,
private var guessCastExpressions: Boolean = true,
private var inferRecords: Boolean = true,
private var inferFunctions: Boolean = true,
private var inferVariables: Boolean = true,
private var inferDfgForUnresolvedCalls: Boolean = true
) {
fun guessCastExpressions(guess: Boolean) = apply { this.guessCastExpressions = guess }

fun enabled(infer: Boolean) = apply { this.enabled = infer }

fun inferRecords(infer: Boolean) = apply { this.inferRecords = infer }

fun inferFunctions(infer: Boolean) = apply { this.inferFunctions = infer }

fun inferVariables(infer: Boolean) = apply { this.inferVariables = infer }

fun inferDfgForUnresolvedCalls(infer: Boolean) = apply {
this.inferDfgForUnresolvedCalls = infer
}

fun build() =
InferenceConfiguration(guessCastExpressions, inferRecords, inferDfgForUnresolvedCalls)
InferenceConfiguration(
enabled,
guessCastExpressions,
inferRecords,
inferFunctions,
inferVariables,
inferDfgForUnresolvedCalls
)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ package de.fraunhofer.aisec.cpg.frontends

import de.fraunhofer.aisec.cpg.ScopeManager
import de.fraunhofer.aisec.cpg.TranslationContext
import de.fraunhofer.aisec.cpg.graph.Node
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TranslationUnitDeclaration
import de.fraunhofer.aisec.cpg.graph.scopes.GlobalScope
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression
import de.fraunhofer.aisec.cpg.graph.types.Type
Expand All @@ -42,7 +44,7 @@ import de.fraunhofer.aisec.cpg.passes.SymbolResolver
*
* Currently, this interface has no methods. However, in the future, this could be used to execute
* language/frontend-specific code for the particular trait. This could help to fine-tune the
* [CallResolver] for specific languages.
* [SymbolResolver] for specific languages.
*/
interface LanguageTrait

Expand Down Expand Up @@ -235,3 +237,12 @@ interface HasAnonymousIdentifier {
val anonymousIdentifier: String
get() = "_"
}

/**
* A language trait, that specifies that this language has global variables directly in the
* [GlobalScope], i.e,. not within a namespace, but directly contained in a
* [TranslationUnitDeclaration].
*/
interface HasGlobalVariables {
val globalVariableScopeClass: Class<out Node>
}
22 changes: 16 additions & 6 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ package de.fraunhofer.aisec.cpg.helpers
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.Properties
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.sarif.PhysicalLocation
Expand Down Expand Up @@ -209,13 +210,13 @@ object Util {
*/
@Suppress("NOTHING_TO_INLINE")
inline fun errorWithFileLocation(
node: Node,
node: Node?,
log: Logger,
format: String?,
vararg arguments: Any?
) {
log.error(
String.format("%s: %s", PhysicalLocation.locationLink(node.location), format),
String.format("%s: %s", PhysicalLocation.locationLink(node?.location), format),
*arguments
)
}
Expand Down Expand Up @@ -344,21 +345,30 @@ object Util {
}

/**
* Establish dataflow from call arguments to the target [FunctionDeclaration] parameters
* Establish data-flow from a [CallExpression] arguments to the target [FunctionDeclaration]
* parameters. Additionally, if the call is a [MemberCallExpression], it establishes a data-flow
* from the [MemberCallExpression.base] towards the [MethodDeclaration.receiver].
*
* @param target The call's target [FunctionDeclaration]
* @param arguments The call's arguments to be connected to the target's parameters
* @param call The [CallExpression]
*/
fun attachCallParameters(target: FunctionDeclaration, arguments: List<Expression>) {
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) }
}

// Connect the arguments to parameters
val arguments = call.arguments
target.parameterEdges.sortWith(Comparator.comparing { it.end.argumentIndex })

var j = 0
while (j < arguments.size) {
val parameters = target.parameters
if (j < parameters.size) {
val param = parameters[j]
if (param.isVariadic) {
while (j < arguments.size) {

// map all the following arguments to this variadic param
param.addPrevDFG(arguments[j])
j++
Expand Down
26 changes: 15 additions & 11 deletions cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,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.FieldDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.TupleDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration
import de.fraunhofer.aisec.cpg.graph.declarations.*
import de.fraunhofer.aisec.cpg.graph.edge.Properties
import de.fraunhofer.aisec.cpg.graph.statements.*
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
Expand Down Expand Up @@ -155,7 +152,18 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) {
* the function.
*/
protected fun handleFunctionDeclaration(node: FunctionDeclaration) {
node.allChildren<ReturnStatement>().forEach { node.addPrevDFG(it) }
if (node.isInferred) {
// If the function is inferred, we connect all parameters to the function declaration.
// The condition should make sure that we don't add edges multiple times, i.e., we
// only handle the declaration exactly once.
node.addAllPrevDFG(node.parameters)
// If it's a method with a receiver, we connect that one too.
if (node is MethodDeclaration) {
node.receiver?.let { node.addPrevDFG(it) }
}
} else {
node.allChildren<ReturnStatement>().forEach { node.addPrevDFG(it) }
}
}

/**
Expand Down Expand Up @@ -398,12 +406,8 @@ class DFGPass(ctx: TranslationContext) : ComponentPass(ctx) {
handleUnresolvedCalls(call, call)
} else if (call.invokes.isNotEmpty()) {
call.invokes.forEach {
if (it.isInferred && inferDfgForUnresolvedSymbols) {
handleUnresolvedCalls(call, it)
} else {
Util.attachCallParameters(it, call.arguments)
call.addPrevDFG(it)
}
Util.attachCallParameters(it, call)
call.addPrevDFG(it)
}
}
}
Expand Down
Loading

0 comments on commit 288ea53

Please sign in to comment.