Skip to content

Commit

Permalink
Implement initial PoC for checking return values (#846)
Browse files Browse the repository at this point in the history
Co-authored-by: Selina Lin <[email protected]>
Co-authored-by: Robert Haimerl <[email protected]>
  • Loading branch information
3 people authored Apr 15, 2024
1 parent ad301db commit b7043cf
Show file tree
Hide file tree
Showing 29 changed files with 2,109 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ package de.fraunhofer.aisec.codyze.backends.cpg.coko
import de.fraunhofer.aisec.codyze.backends.cpg.CPGBackend
import de.fraunhofer.aisec.codyze.backends.cpg.CPGConfiguration
import de.fraunhofer.aisec.codyze.backends.cpg.coko.dsl.*
import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.FollowsEvaluator
import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.NeverEvaluator
import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.OnlyEvaluator
import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.OrderEvaluator
import de.fraunhofer.aisec.codyze.backends.cpg.coko.evaluators.*
import de.fraunhofer.aisec.codyze.core.VersionProvider
import de.fraunhofer.aisec.codyze.core.backend.BackendConfiguration
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoBackend
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.WheneverEvaluator
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Condition
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Op
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.Order
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.ConditionComponent
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering.OrderToken
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.ordering.getOp
import de.fraunhofer.aisec.cpg.graph.Node
Expand Down Expand Up @@ -86,4 +86,13 @@ class CokoCpgBackend(config: BackendConfiguration) :
*/
override fun only(vararg ops: Op): OnlyEvaluator = OnlyEvaluator(ops.toList())
override fun never(vararg ops: Op): NeverEvaluator = NeverEvaluator(ops.toList())
override fun whenever(
premise: Condition.() -> ConditionComponent,
assertionBlock: WheneverEvaluator.() -> Unit
): WheneverEvaluator = CpgWheneverEvaluator(Condition().premise()).apply(assertionBlock)

override fun whenever(
premise: ConditionComponent,
assertionBlock: WheneverEvaluator.() -> Unit
): WheneverEvaluator = CpgWheneverEvaluator(premise).apply(assertionBlock)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2022, Fraunhofer AISEC. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.fraunhofer.aisec.codyze.backends.cpg.coko.dsl

import de.fraunhofer.aisec.codyze.backends.cpg.coko.Nodes
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoBackend
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.ArgumentItem
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.DataItem
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.ReturnValueItem
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.Value
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.VariableDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Reference

/**
* Get all [Nodes] that are associated with this [DataItem].
*/
context(CokoBackend)
fun DataItem<*>.cpgGetAllNodes(): Nodes =
when (this@DataItem) {
is ReturnValueItem -> op.cpgGetAllNodes().flatMap { it.getVariableInNextDFGOrThis() }
is Value -> this@DataItem.getNodes()
is ArgumentItem -> op.cpgGetAllNodes().map { it.arguments[index] } // TODO: Do we count starting at 0 or 1?
}

/**
* Get all [Nodes] that are associated with this [DataItem].
*/
context(CokoBackend)
fun DataItem<*>.cpgGetNodes(): Nodes {
return when (this@DataItem) {
is ReturnValueItem -> op.cpgGetNodes().flatMap { it.getVariableInNextDFGOrThis() }
is Value -> this@DataItem.getNodes()
is ArgumentItem -> op.cpgGetNodes().map { it.arguments[index] } // TODO: Do we count starting at 0 or 1?
}
}

/**
* Get all [Nodes] that evaluate to the same value as [Value.value].
*/
context(CokoBackend)
private fun Value<*>.getNodes(): Nodes {
val value = this.value
return if (value is Node) {
listOf(value)
} else {
cpg.literals.filter { node ->
node.value == value
} + cpg.variables.filter { node ->
node.evaluate() == value
}
}
}

/**
* Returns all [VariableDeclaration]s and [DeclaredReferenceExpression]s that have a DFG edge from [this].
* If there are none, returns [this].
*/
private fun Node.getVariableInNextDFGOrThis(): Nodes =
this.nextDFG
.filter {
next ->
next is Reference || next is VariableDeclaration
}.ifEmpty { listOf(this) }
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,13 @@ import de.fraunhofer.aisec.codyze.backends.cpg.coko.Nodes
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoBackend
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.CokoMarker
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.dsl.*
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.Definition
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.ParameterGroup
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.Signature
import de.fraunhofer.aisec.codyze.specificationLanguages.coko.core.modelling.*
import de.fraunhofer.aisec.cpg.TranslationResult
import de.fraunhofer.aisec.cpg.graph.*
import de.fraunhofer.aisec.cpg.graph.declarations.ValueDeclaration
import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConstructExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Expression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberExpression
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
import de.fraunhofer.aisec.cpg.query.dataFlow
import de.fraunhofer.aisec.cpg.query.executionPath

//
// all functions/properties defined here must use CokoBackend
Expand All @@ -42,19 +38,29 @@ val CokoBackend.cpg: TranslationResult

/** Get all [Nodes] that are associated with this [Op]. */
context(CokoBackend)
fun Op.cpgGetAllNodes(): Nodes =
fun Op.cpgGetAllNodes(): Collection<CallExpression> =
when (this@Op) {
is FunctionOp ->
this@Op.definitions.flatMap { def -> this@CokoBackend.cpgCallFqn(def.fqn) }
is ConstructorOp -> this@CokoBackend.cpgConstructor(this.classFqn)
is GroupingOp -> this@Op.ops.flatMap { it.cpgGetAllNodes() }
is ConditionalOp -> {
val resultNodes = resultOp.cpgGetAllNodes()
val conditionNodes = conditionOp.cpgGetAllNodes()
resultNodes.filter { resultNode ->
conditionNodes.any { conditionNode ->
dataFlow(conditionNode, resultNode).value
}
}
}
}

/**
* Get all [Nodes] that are associated with this [Op] and fulfill the [Signature]s of the
* [Definition]s.
*/
context(CokoBackend)
fun Op.cpgGetNodes(): Nodes =
fun Op.cpgGetNodes(): Collection<CallExpression> =
when (this@Op) {
is FunctionOp ->
this@Op.definitions
Expand All @@ -74,6 +80,18 @@ fun Op.cpgGetNodes(): Nodes =
sig.unorderedParameters.all { it?.cpgFlowsTo(arguments) ?: false }
}
}
is GroupingOp -> this@Op.ops.flatMap { it.cpgGetNodes() }
is ConditionalOp -> {
val resultNodes = resultOp.cpgGetNodes()
val conditionNodes = conditionOp.cpgGetNodes()
resultNodes.filter { resultNode ->
conditionNodes.any { conditionNode ->
// TODO: Is it correct to use the EOG relationship here?
val result = executionPath(conditionNode, resultNode)
result.value
}
}
}
}

/** Returns a list of [ValueDeclaration]s with the matching name. */
Expand Down Expand Up @@ -146,7 +164,10 @@ infix fun Any.cpgFlowsTo(that: Collection<Node>): Boolean =
true
} else {
when (this) {
is String -> that.any { Regex(this).matches((it as? Expression)?.evaluate()?.toString().orEmpty()) }
is String -> that.any {
val regex = Regex(this)
regex.matches((it as? Expression)?.evaluate()?.toString().orEmpty()) || regex.matches(it.code.orEmpty())
}
is Iterable<*> -> this.any { it?.cpgFlowsTo(that) ?: false }
is Array<*> -> this.any { it?.cpgFlowsTo(that) ?: false }
is Node -> that.any { dataFlow(this, it).value }
Expand Down Expand Up @@ -189,8 +210,10 @@ fun CallExpression.cpgSignature(vararg parameters: Any?, hasVarargs: Boolean = f
parameter.param cpgFlowsTo arguments[i]
// checks if the type of the argument is the same
is Type -> cpgCheckType(parameter, i)
// check if any of the Nodes from the Op flow to the argument
// check if any of the Nodes of the Op flow to the argument
is Op -> parameter.cpgGetNodes() cpgFlowsTo arguments[i]
// check if any of the Nodes of the DataItem flow to the argument
is DataItem<*> -> parameter.cpgGetNodes() cpgFlowsTo arguments[i]
// checks if there is dataflow from the parameter to the argument in the same position
else -> parameter cpgFlowsTo arguments[i]
}
Expand Down
Loading

0 comments on commit b7043cf

Please sign in to comment.