From bf502208c5713b2a0637d08cd6ad4e7ce00b18e7 Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Wed, 16 Oct 2024 15:02:11 +0200 Subject: [PATCH 01/12] Try a new eog iteration --- .../aisec/cpg/passes/UnreachableEOGPass.kt | 1 + .../cpg/helpers/functional/BasicLattices.kt | 245 ++++++++++++++++++ .../cpg/helpers/functional/EOGWorklist.kt | 66 +++++ .../cpg/passes/ControlDependenceGraphPass.kt | 1 + .../cpg/passes/ControlFlowSensitiveDFGPass.kt | 14 +- 5 files changed, 321 insertions(+), 6 deletions(-) create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt create mode 100644 cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt diff --git a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt index 01ab3025e4..76f2f6e204 100644 --- a/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt +++ b/cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt @@ -35,6 +35,7 @@ import de.fraunhofer.aisec.cpg.graph.edges.flows.EvaluationOrder import de.fraunhofer.aisec.cpg.graph.statements.IfStatement import de.fraunhofer.aisec.cpg.graph.statements.WhileStatement import de.fraunhofer.aisec.cpg.helpers.* +import de.fraunhofer.aisec.cpg.helpers.LatticeElement import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn /** diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt new file mode 100644 index 0000000000..0f39b1066f --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2024, 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.cpg.helpers.functional + +import de.fraunhofer.aisec.cpg.helpers.toIdentitySet +import kotlin.Pair +import kotlin.collections.component1 +import kotlin.collections.component2 +import kotlin.collections.toMap +import kotlin.hashCode + +/** + * A complete lattice is an ordered structure of values of type [T]. [T] could be anything, e.g., a + * set, a new data structure (like a range), or anything else. [T] depends on the analysis and + * typically has to abstract the value for the specific purpose. + * + * This class is actually used to hold individual instances of the lattice's elements and to compute + * bigger elements depending on these two elements. + * + * Implementations of this class have to implement the comparator, the least upper bound of two + * lattices. + */ +abstract class LatticeElement(val elements: T) : Comparable> { + /** + * Computes the least upper bound of this lattice and [other]. It returns a new object and does + * not modify either of the objects. + */ + abstract fun lub(other: LatticeElement): LatticeElement + + /** Duplicates the object, i.e., makes a deep copy. */ + abstract fun duplicate(): LatticeElement +} + +typealias PowersetLatticeT = LatticeElement> + +inline fun emptyPowersetLattice() = PowersetLattice(setOf()) + +/** + * Implements the [LatticeElement] for a lattice over a set of nodes. The lattice itself is + * constructed by the powerset. + */ +class PowersetLattice(elements: Set) : LatticeElement>(elements) { + override fun lub(other: LatticeElement>) = + PowersetLattice(this.elements.union(other.elements)) + + override fun duplicate(): LatticeElement> = + PowersetLattice(this.elements.toIdentitySet()) + + override fun compareTo(other: LatticeElement>): Int { + return if (this.elements == other.elements) { + 0 + } else if (this.elements.containsAll(other.elements)) { + 1 + } else { + -1 + } + } + + override fun equals(other: Any?): Boolean { + return other is PowersetLattice && this.elements == other.elements + } + + override fun hashCode(): Int { + return super.hashCode() * 31 + elements.hashCode() + } +} + +typealias MapLatticeT = LatticeElement> + +inline fun emptyMapLattice() = MapLattice>(mapOf()) + +/** Implements the [LatticeElement] for a lattice over a map of nodes to another lattice. */ +class MapLattice(elements: Map>) : + LatticeElement>>(elements) { + override fun lub( + other: LatticeElement>> + ): LatticeElement>> { + return MapLattice( + this.elements.entries.fold(other.elements) { current, (thisKey, thisValue) -> + val mutableMap = current.toMutableMap() + mutableMap.compute(thisKey) { k, v -> + if (v == null) thisValue else thisValue.lub(v) + } + mutableMap + } + ) + } + + override fun duplicate(): LatticeElement>> { + return MapLattice( + this.elements.map { (k, v) -> Pair>(k, v.duplicate()) }.toMap() + ) + } + + override fun compareTo(other: LatticeElement>>): Int { + if (this.elements.entries == other.elements.entries) return 0 + if ( + this.elements.keys.containsAll(other.elements.keys) && + this.elements.entries.all { (k, v) -> + other.elements[k]?.let { otherV -> v >= otherV } != false + } + ) + return 1 + return -1 + } + + override fun equals(other: Any?): Boolean { + return other is MapLattice && this.elements.entries == other.elements.entries + } + + override fun hashCode(): Int { + return super.hashCode() * 31 + elements.hashCode() + } +} + +open class TupleLattice(elements: Pair, LatticeElement>) : + LatticeElement, LatticeElement>>(elements) { + override fun lub( + other: LatticeElement, LatticeElement>> + ): LatticeElement, LatticeElement>> { + return TupleLattice( + Pair( + this.elements.first.lub(other.elements.first), + this.elements.second.lub(other.elements.second) + ) + ) + } + + override fun duplicate(): LatticeElement, LatticeElement>> { + return TupleLattice(Pair(elements.first.duplicate(), elements.second.duplicate())) + } + + override fun compareTo(other: LatticeElement, LatticeElement>>): Int { + if ( + this.elements.first == other.elements.first && + this.elements.second == other.elements.second + ) + return 0 + if ( + this.elements.first >= other.elements.first && + this.elements.second >= other.elements.second + ) + return 1 + return -1 + } + + override fun equals(other: Any?): Boolean { + if (other !is TupleLattice) return false + return other.elements.first == this.elements.first && + other.elements.second == this.elements.second + } + + override fun hashCode(): Int { + return super.hashCode() * 31 + elements.hashCode() + } + + operator fun component1() = this.elements.first + + operator fun component2() = this.elements.second +} + +class TripleLattice( + elements: Triple, LatticeElement, LatticeElement> +) : LatticeElement, LatticeElement, LatticeElement>>(elements) { + override fun lub( + other: LatticeElement, LatticeElement, LatticeElement>> + ): LatticeElement, LatticeElement, LatticeElement>> { + return TripleLattice( + Triple( + this.elements.first.lub(other.elements.first), + this.elements.second.lub(other.elements.second), + this.elements.third.lub(other.elements.third) + ) + ) + } + + override fun duplicate(): + LatticeElement, LatticeElement, LatticeElement>> { + return TripleLattice( + Triple( + elements.first.duplicate(), + elements.second.duplicate(), + elements.third.duplicate() + ) + ) + } + + override fun compareTo( + other: LatticeElement, LatticeElement, LatticeElement>> + ): Int { + if ( + this.elements.first == other.elements.first && + this.elements.second == other.elements.second && + this.elements.third == other.elements.third + ) + return 0 + if ( + this.elements.first >= other.elements.first && + this.elements.second >= other.elements.second && + this.elements.third >= other.elements.third + ) + return 1 + return -1 + } + + override fun equals(other: Any?): Boolean { + if (other !is TripleLattice) return false + return other.elements.first == this.elements.first && + other.elements.second == this.elements.second && + other.elements.third == this.elements.third + } + + override fun hashCode(): Int { + return super.hashCode() * 31 + elements.hashCode() + } + + operator fun component1() = this.elements.first + + operator fun component2() = this.elements.second + + operator fun component3() = this.elements.third +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt new file mode 100644 index 0000000000..4d773bcbab --- /dev/null +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024, 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.cpg.helpers.functional + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.graph.edges.Edge +import java.util.IdentityHashMap + +inline fun , V> iterateEOGClean( + startEdges: List, + startState: LatticeElement, + transformation: (K, LatticeElement) -> LatticeElement +): LatticeElement { + val globalState = IdentityHashMap>() + for (startEdge in startEdges) { + globalState[startEdge] = startState + } + val edgesList = mutableListOf() + startEdges.forEach { edgesList.add(it) } + + while (edgesList.isNotEmpty()) { + val nextEdge = edgesList.first() + edgesList.removeFirst() + + val nextGlobal = globalState[nextEdge] ?: continue + val newState = transformation(nextEdge, nextGlobal) + if (newState != nextGlobal) { + nextEdge.end.nextEOGEdges.forEach { + if (it is K) { + /*val oldStateForIt = globalState[it] + val newStateForIt = oldStateForIt?.let { newState.lub(it) } ?: newState + if (oldStateForIt == null || newStateForIt != oldStateForIt) { + globalState[it] = newStateForIt*/ + globalState[it] = newState + if (it !in edgesList) edgesList.add(0, it) + // } + } + } + } + } + + return globalState.values.fold(globalState.values.first()) { state, value -> state.lub(value) } +} diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt index c8177d627d..a8f78479ce 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt @@ -38,6 +38,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConditionalExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.ShortCircuitOperator import de.fraunhofer.aisec.cpg.helpers.* +import de.fraunhofer.aisec.cpg.helpers.LatticeElement import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn import java.util.* diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt index e9b06fc1ab..1f1aee753f 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt @@ -35,6 +35,8 @@ import de.fraunhofer.aisec.cpg.graph.edges.flows.partial import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.helpers.* +import de.fraunhofer.aisec.cpg.helpers.LatticeElement +import de.fraunhofer.aisec.cpg.helpers.PowersetLattice import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn import kotlin.collections.set import kotlin.contracts.ExperimentalContracts @@ -563,16 +565,16 @@ open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : EOGStarterPass */ protected class DFGPassState( /** - * A mapping of a [Node] to its [LatticeElement]. The keys of this state will later get the - * DFG edges from the value! + * A mapping of a [Node] to its [de.fraunhofer.aisec.cpg.helpers.functional.LatticeElement]. + * The keys of this state will later get the DFG edges from the value! */ var generalState: State = State(), /** * It's main purpose is to store the most recent mapping of a [Declaration] to its - * [LatticeElement]. However, it is also used to figure out if we have to continue with the - * iteration (something in the declarationState has changed) which is why we store all nodes - * here. However, since we never use them except from determining if we changed something, - * it won't affect the result. + * [de.fraunhofer.aisec.cpg.helpers.functional.LatticeElement]. However, it is also used to + * figure out if we have to continue with the iteration (something in the declarationState + * has changed) which is why we store all nodes here. However, since we never use them + * except from determining if we changed something, it won't affect the result. */ var declarationsState: State = State(), From 40e1180ef003f31ef5def7fb72a4ba4a2496e75a Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Mon, 4 Nov 2024 15:55:23 +0100 Subject: [PATCH 02/12] Small changes --- .../cpg/helpers/functional/BasicLattices.kt | 2 +- .../cpg/helpers/functional/EOGWorklist.kt | 37 +++++++++---------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt index 0f39b1066f..db558b4e3c 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt @@ -93,7 +93,7 @@ typealias MapLatticeT = LatticeElement> inline fun emptyMapLattice() = MapLattice>(mapOf()) /** Implements the [LatticeElement] for a lattice over a map of nodes to another lattice. */ -class MapLattice(elements: Map>) : +open class MapLattice(elements: Map>) : LatticeElement>>(elements) { override fun lub( other: LatticeElement>> diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt index 4d773bcbab..450d2fe5d3 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt @@ -25,20 +25,19 @@ */ package de.fraunhofer.aisec.cpg.helpers.functional -import de.fraunhofer.aisec.cpg.graph.Node -import de.fraunhofer.aisec.cpg.graph.edges.Edge +import de.fraunhofer.aisec.cpg.graph.edges.flows.EvaluationOrder import java.util.IdentityHashMap -inline fun , V> iterateEOGClean( - startEdges: List, +inline fun iterateEOGClean( + startEdges: List, startState: LatticeElement, - transformation: (K, LatticeElement) -> LatticeElement + transformation: (EvaluationOrder, LatticeElement) -> LatticeElement ): LatticeElement { - val globalState = IdentityHashMap>() + val globalState = IdentityHashMap>() for (startEdge in startEdges) { globalState[startEdge] = startState } - val edgesList = mutableListOf() + val edgesList = mutableListOf() startEdges.forEach { edgesList.add(it) } while (edgesList.isNotEmpty()) { @@ -47,20 +46,18 @@ inline fun , V> iterateEOGClean( val nextGlobal = globalState[nextEdge] ?: continue val newState = transformation(nextEdge, nextGlobal) - if (newState != nextGlobal) { - nextEdge.end.nextEOGEdges.forEach { - if (it is K) { - /*val oldStateForIt = globalState[it] - val newStateForIt = oldStateForIt?.let { newState.lub(it) } ?: newState - if (oldStateForIt == null || newStateForIt != oldStateForIt) { - globalState[it] = newStateForIt*/ - globalState[it] = newState - if (it !in edgesList) edgesList.add(0, it) - // } - } - } + // if (newState != nextGlobal) { + nextEdge.end.nextEOGEdges.forEach { + val oldGlobalIt = globalState[it] + val newGlobalIt = oldGlobalIt?.let { newState.lub(it) } ?: newState + globalState[it] = newGlobalIt + if (it !in edgesList && (oldGlobalIt == null || newGlobalIt != oldGlobalIt)) + edgesList.add(0, it) } + // } } - return globalState.values.fold(globalState.values.first()) { state, value -> state.lub(value) } + return globalState.values.fold(globalState.values.firstOrNull()) { state, value -> + state?.lub(value) + } ?: startState } From 4a5b3c122317f2d7a9e3358fcf50209af0c54a3e Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Mon, 4 Nov 2024 16:25:27 +0100 Subject: [PATCH 03/12] remove comment --- .../de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt index 450d2fe5d3..263a6d46ca 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/EOGWorklist.kt @@ -46,7 +46,6 @@ inline fun iterateEOGClean( val nextGlobal = globalState[nextEdge] ?: continue val newState = transformation(nextEdge, nextGlobal) - // if (newState != nextGlobal) { nextEdge.end.nextEOGEdges.forEach { val oldGlobalIt = globalState[it] val newGlobalIt = oldGlobalIt?.let { newState.lub(it) } ?: newState @@ -54,7 +53,6 @@ inline fun iterateEOGClean( if (it !in edgesList && (oldGlobalIt == null || newGlobalIt != oldGlobalIt)) edgesList.add(0, it) } - // } } return globalState.values.fold(globalState.values.firstOrNull()) { state, value -> From e3914e2472bb7dd3ba4c35142fdec4f11f718d5e Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Mon, 4 Nov 2024 16:26:15 +0100 Subject: [PATCH 04/12] small cleanup --- .../de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt index db558b4e3c..115cbbdd17 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt @@ -30,7 +30,6 @@ import kotlin.Pair import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.collections.toMap -import kotlin.hashCode /** * A complete lattice is an ordered structure of values of type [T]. [T] could be anything, e.g., a From 4b1258e4c6ae3897f4a8343b8c808037aca26c7c Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Tue, 5 Nov 2024 14:13:23 +0100 Subject: [PATCH 05/12] Cleanup --- .../aisec/cpg/passes/ControlDependenceGraphPass.kt | 1 - .../cpg/passes/ControlFlowSensitiveDFGPass.kt | 14 ++++++-------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt index a8f78479ce..c8177d627d 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt @@ -38,7 +38,6 @@ import de.fraunhofer.aisec.cpg.graph.statements.ReturnStatement import de.fraunhofer.aisec.cpg.graph.statements.expressions.ConditionalExpression import de.fraunhofer.aisec.cpg.graph.statements.expressions.ShortCircuitOperator import de.fraunhofer.aisec.cpg.helpers.* -import de.fraunhofer.aisec.cpg.helpers.LatticeElement import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn import java.util.* diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt index 1f1aee753f..e9b06fc1ab 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlFlowSensitiveDFGPass.kt @@ -35,8 +35,6 @@ import de.fraunhofer.aisec.cpg.graph.edges.flows.partial import de.fraunhofer.aisec.cpg.graph.statements.* import de.fraunhofer.aisec.cpg.graph.statements.expressions.* import de.fraunhofer.aisec.cpg.helpers.* -import de.fraunhofer.aisec.cpg.helpers.LatticeElement -import de.fraunhofer.aisec.cpg.helpers.PowersetLattice import de.fraunhofer.aisec.cpg.passes.configuration.DependsOn import kotlin.collections.set import kotlin.contracts.ExperimentalContracts @@ -565,16 +563,16 @@ open class ControlFlowSensitiveDFGPass(ctx: TranslationContext) : EOGStarterPass */ protected class DFGPassState( /** - * A mapping of a [Node] to its [de.fraunhofer.aisec.cpg.helpers.functional.LatticeElement]. - * The keys of this state will later get the DFG edges from the value! + * A mapping of a [Node] to its [LatticeElement]. The keys of this state will later get the + * DFG edges from the value! */ var generalState: State = State(), /** * It's main purpose is to store the most recent mapping of a [Declaration] to its - * [de.fraunhofer.aisec.cpg.helpers.functional.LatticeElement]. However, it is also used to - * figure out if we have to continue with the iteration (something in the declarationState - * has changed) which is why we store all nodes here. However, since we never use them - * except from determining if we changed something, it won't affect the result. + * [LatticeElement]. However, it is also used to figure out if we have to continue with the + * iteration (something in the declarationState has changed) which is why we store all nodes + * here. However, since we never use them except from determining if we changed something, + * it won't affect the result. */ var declarationsState: State = State(), From 9fc75c7e8679a315397d88eb793ef30c252ac8ad Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Tue, 5 Nov 2024 15:00:56 +0100 Subject: [PATCH 06/12] Test PowersetLattice --- .../aisec/cpg/helpers/BasicLatticesTest.kt | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt new file mode 100644 index 0000000000..b683813f8b --- /dev/null +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024, 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.cpg.helpers + +import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.helpers.functional.PowersetLattice +import de.fraunhofer.aisec.cpg.helpers.functional.PowersetLatticeT +import de.fraunhofer.aisec.cpg.helpers.functional.emptyPowersetLattice +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNotEquals +import kotlin.test.assertNotSame + +class BasicLatticesTest { + @Test + fun testPowersetLattice() { + val emptyLattice1: PowersetLatticeT = emptyPowersetLattice() + val emptyLattice2 = emptyPowersetLattice() + assertEquals(0, emptyLattice1.compareTo(emptyLattice2)) + assertEquals(emptyLattice1, emptyLattice2) + + val blaLattice1 = PowersetLattice(setOf("bla")) + val blaLattice2 = PowersetLattice(setOf("bla")) + assertEquals(0, blaLattice1.compareTo(blaLattice2)) + assertEquals(blaLattice1, blaLattice2) + + val blaFooLattice = PowersetLattice(setOf("bla", "foo")) + assertEquals(1, blaFooLattice.compareTo(blaLattice1)) + assertNotEquals(blaFooLattice, blaLattice1) + + assertEquals(-1, blaLattice1.compareTo(blaFooLattice)) + assertNotEquals(blaLattice1, blaFooLattice) + + val blaBlubLattice = PowersetLattice(setOf("bla", "blub")) + assertEquals(-1, blaFooLattice.compareTo(blaBlubLattice)) + assertNotEquals(blaFooLattice, blaBlubLattice) + + assertEquals(-1, blaBlubLattice.compareTo(blaFooLattice)) + assertNotEquals(blaBlubLattice, blaFooLattice) + + assertNotSame(emptyLattice1, emptyLattice1.duplicate()) + assertNotSame(emptyLattice1.elements, emptyLattice1.duplicate().elements) + assertNotSame(blaLattice1, blaLattice1.duplicate()) + assertNotSame(blaLattice1.elements, blaLattice1.duplicate().elements) + assertNotSame(blaFooLattice, blaFooLattice.duplicate()) + assertNotSame(blaFooLattice.elements, blaFooLattice.duplicate().elements) + assertNotSame(blaBlubLattice, blaBlubLattice.duplicate()) + assertNotSame(blaBlubLattice.elements, blaBlubLattice.duplicate().elements) + + val emptyLubEmpty = emptyLattice1.lub(emptyLattice1) + assertIs>(emptyLubEmpty) + assertNotSame(emptyLattice1, emptyLubEmpty) + assertEquals(emptyLattice1, emptyLubEmpty) + assertEquals(0, emptyLattice1.compareTo(emptyLubEmpty)) + assertNotSame(emptyLattice2, emptyLubEmpty) + assertEquals(emptyLattice1, emptyLubEmpty) + assertEquals(0, emptyLattice2.compareTo(emptyLubEmpty)) + + val emptyLattice3 = emptyPowersetLattice() + val emptyLubBla = emptyLattice3.lub(blaLattice1) + assertIs>(emptyLubBla) + assertNotSame(emptyLattice3, emptyLubBla) + assertNotEquals(emptyLattice3, emptyLubBla) + assertEquals(-1, emptyLattice3.compareTo(emptyLubBla)) + assertNotSame(blaLattice1, emptyLubBla) + assertEquals(blaLattice1, emptyLubBla) + assertEquals(0, blaLattice1.compareTo(emptyLubBla)) + + val blaFooBlub = blaFooLattice.lub(blaBlubLattice) + assertIs>(blaFooBlub) + assertNotSame(emptyLattice3, blaFooBlub) + assertNotEquals(emptyLattice3, blaFooBlub) + assertEquals(-1, emptyLattice3.compareTo(blaFooBlub)) + assertNotSame(blaLattice1, blaFooBlub) + assertNotEquals(blaLattice1, blaFooBlub) + assertEquals(-1, blaLattice1.compareTo(blaFooBlub)) + assertNotSame(blaFooLattice, blaFooBlub) + assertNotEquals(blaFooLattice, blaFooBlub) + assertEquals(-1, blaFooLattice.compareTo(blaFooBlub)) + assertNotSame(blaBlubLattice, blaFooBlub) + assertNotEquals(blaBlubLattice, blaFooBlub) + assertEquals(-1, blaBlubLattice.compareTo(blaFooBlub)) + assertEquals(setOf("bla", "blub", "foo"), blaFooBlub.elements) + } +} From ebd3f8798579ef0f0e99c18fc34f67ec51c79d2d Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Tue, 5 Nov 2024 16:36:39 +0100 Subject: [PATCH 07/12] MapLattice test --- .../cpg/helpers/functional/BasicLattices.kt | 6 +- .../aisec/cpg/helpers/BasicLatticesTest.kt | 100 ++++++++++++++++++ 2 files changed, 103 insertions(+), 3 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt index 115cbbdd17..11668449b9 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt @@ -89,7 +89,7 @@ class PowersetLattice(elements: Set) : LatticeElement>(elements) { typealias MapLatticeT = LatticeElement> -inline fun emptyMapLattice() = MapLattice>(mapOf()) +inline fun emptyMapLattice() = MapLattice(mapOf()) /** Implements the [LatticeElement] for a lattice over a map of nodes to another lattice. */ open class MapLattice(elements: Map>) : @@ -115,7 +115,7 @@ open class MapLattice(elements: Map>) : } override fun compareTo(other: LatticeElement>>): Int { - if (this.elements.entries == other.elements.entries) return 0 + if (this.elements == other.elements) return 0 if ( this.elements.keys.containsAll(other.elements.keys) && this.elements.entries.all { (k, v) -> @@ -127,7 +127,7 @@ open class MapLattice(elements: Map>) : } override fun equals(other: Any?): Boolean { - return other is MapLattice && this.elements.entries == other.elements.entries + return other is MapLattice && this.elements == other.elements } override fun hashCode(): Int { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt index b683813f8b..6b29f133b9 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt @@ -26,8 +26,10 @@ package de.fraunhofer.aisec.cpg.helpers import de.fraunhofer.aisec.cpg.graph.Node +import de.fraunhofer.aisec.cpg.helpers.functional.MapLattice import de.fraunhofer.aisec.cpg.helpers.functional.PowersetLattice import de.fraunhofer.aisec.cpg.helpers.functional.PowersetLatticeT +import de.fraunhofer.aisec.cpg.helpers.functional.emptyMapLattice import de.fraunhofer.aisec.cpg.helpers.functional.emptyPowersetLattice import kotlin.test.Test import kotlin.test.assertEquals @@ -106,4 +108,102 @@ class BasicLatticesTest { assertEquals(-1, blaBlubLattice.compareTo(blaFooBlub)) assertEquals(setOf("bla", "blub", "foo"), blaFooBlub.elements) } + + @Test + fun testMapLattice() { + val emptyLattice1 = emptyMapLattice>() + val emptyLattice2 = emptyMapLattice>() + assertEquals(0, emptyLattice1.compareTo(emptyLattice2)) + assertEquals(emptyLattice1, emptyLattice2) + + val aBlaLattice1 = + MapLattice>(mapOf("a" to PowersetLattice(setOf("bla")))) + val bBlaLattice2 = + MapLattice>(mapOf("a" to PowersetLattice(setOf("bla")))) + assertEquals(0, aBlaLattice1.compareTo(bBlaLattice2)) + assertEquals(aBlaLattice1, bBlaLattice2) + assertNotSame(aBlaLattice1, bBlaLattice2) + + val aBlaFooLattice = + MapLattice>(mapOf("a" to PowersetLattice(setOf("bla", "foo")))) + assertEquals(1, aBlaFooLattice.compareTo(aBlaLattice1)) + assertNotEquals(aBlaFooLattice, aBlaLattice1) + assertEquals(-1, aBlaLattice1.compareTo(aBlaFooLattice)) + assertNotEquals(aBlaLattice1, aBlaFooLattice) + + val aBlaBFooLattice = + MapLattice>( + mapOf("a" to PowersetLattice(setOf("bla")), "b" to PowersetLattice(setOf("foo"))) + ) + assertEquals(1, aBlaBFooLattice.compareTo(aBlaLattice1)) + assertNotEquals(aBlaBFooLattice, aBlaLattice1) + assertEquals(-1, aBlaLattice1.compareTo(aBlaBFooLattice)) + assertNotEquals(aBlaLattice1, aBlaBFooLattice) + + // Duplicates are equal but not identical. Same for the elements. + val emptyDuplicate = emptyLattice1.duplicate() + assertNotSame(emptyLattice1, emptyDuplicate) + // assertNotSame(emptyLattice1.elements, emptyDuplicate.elements) + assertEquals(emptyLattice1, emptyDuplicate) + val aBlaLatticeDuplicate = aBlaLattice1.duplicate() + assertNotSame(aBlaLattice1, aBlaLatticeDuplicate) + assertNotSame(aBlaLattice1.elements, aBlaLatticeDuplicate.elements) + assertEquals(aBlaLattice1, aBlaLatticeDuplicate) + val aBlaFooLatticeDuplicate = aBlaFooLattice.duplicate() + assertNotSame(aBlaFooLattice, aBlaFooLatticeDuplicate) + assertNotSame(aBlaBFooLattice.elements, aBlaFooLatticeDuplicate.elements) + assertEquals(aBlaFooLattice, aBlaFooLatticeDuplicate) + val aBlaBFooLatticeDuplicate = aBlaBFooLattice.duplicate() + assertNotSame(aBlaFooLattice, aBlaBFooLatticeDuplicate) + assertNotSame(aBlaBFooLattice.elements, aBlaBFooLatticeDuplicate.elements) + assertEquals(aBlaBFooLattice, aBlaBFooLatticeDuplicate) + + val emptyLubEmpty = emptyLattice1.lub(emptyLattice1) + assertIs>>(emptyLubEmpty) + assertNotSame(emptyLattice1, emptyLubEmpty) + assertEquals(emptyLattice1, emptyLubEmpty) + assertEquals(0, emptyLattice1.compareTo(emptyLubEmpty)) + assertNotSame(emptyLattice2, emptyLubEmpty) + assertEquals(emptyLattice1, emptyLubEmpty) + assertEquals(0, emptyLattice2.compareTo(emptyLubEmpty)) + + val emptyLubABla = emptyLattice1.lub(aBlaLattice1) + assertIs>>(emptyLubABla) + assertNotSame(emptyLattice1, emptyLubABla) + assertNotEquals(emptyLattice1, emptyLubABla) + assertEquals(-1, emptyLattice1.compareTo(emptyLubABla)) + assertNotSame(aBlaLattice1, emptyLubABla) + assertEquals(aBlaLattice1, emptyLubABla) + assertEquals(0, aBlaLattice1.compareTo(emptyLubABla)) + + val aFooBBlaLattice = + MapLattice>( + mapOf("a" to PowersetLattice(setOf("foo")), "b" to PowersetLattice(setOf("bla"))) + ) + val aBlaFooBBla = aBlaFooLattice.lub(aFooBBlaLattice) // a to {"foo", "bla"}, b to {"bla"} + assertIs>>(aBlaFooBBla) + assertNotSame(emptyLattice1, aBlaFooBBla) + assertNotEquals(emptyLattice1, aBlaFooBBla) + assertEquals(-1, emptyLattice1.compareTo(aBlaFooBBla)) + assertEquals(1, aBlaFooBBla.compareTo(emptyLattice1)) + assertNotSame(aBlaLattice1, aBlaFooBBla) + assertNotEquals(aBlaLattice1, aBlaFooBBla) + assertEquals(-1, aBlaLattice1.compareTo(aBlaFooBBla)) + assertEquals(1, aBlaFooBBla.compareTo(aBlaLattice1)) + assertNotSame(aBlaFooLattice, aBlaFooBBla) + assertNotEquals(aBlaFooLattice, aBlaFooBBla) + assertEquals(-1, aBlaFooLattice.compareTo(aBlaFooBBla)) + assertEquals(1, aBlaFooBBla.compareTo(aBlaFooLattice)) + assertNotSame(aBlaBFooLattice, aBlaFooBBla) + assertNotEquals(aBlaBFooLattice, aBlaFooBBla) + assertEquals(-1, aBlaBFooLattice.compareTo(aBlaFooBBla)) + assertEquals(-1, aBlaFooBBla.compareTo(aBlaBFooLattice)) + assertNotSame(aFooBBlaLattice, aBlaFooBBla) + assertNotEquals(aFooBBlaLattice, aBlaFooBBla) + assertEquals(-1, aFooBBlaLattice.compareTo(aBlaFooBBla)) + assertEquals(1, aBlaFooBBla.compareTo(aFooBBlaLattice)) + assertEquals(setOf("a", "b"), aBlaFooBBla.elements.keys) + assertEquals(setOf("bla", "foo"), aBlaFooBBla.elements["a"]?.elements) + assertEquals(setOf("bla"), aBlaFooBBla.elements["b"]?.elements) + } } From ce013210305070fcb53b79344a6106378be5ce99 Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Tue, 5 Nov 2024 16:40:08 +0100 Subject: [PATCH 08/12] Comment --- .../de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt index 6b29f133b9..6e2a5a86b2 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt @@ -143,7 +143,8 @@ class BasicLatticesTest { // Duplicates are equal but not identical. Same for the elements. val emptyDuplicate = emptyLattice1.duplicate() assertNotSame(emptyLattice1, emptyDuplicate) - // assertNotSame(emptyLattice1.elements, emptyDuplicate.elements) + // assertNotSame(emptyLattice1.elements, emptyDuplicate.elements) // Somehow, the empty set + // is the same assertEquals(emptyLattice1, emptyDuplicate) val aBlaLatticeDuplicate = aBlaLattice1.duplicate() assertNotSame(aBlaLattice1, aBlaLatticeDuplicate) From bef69b63cd18e322d71dc322678b86e9193bbaac Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Mon, 11 Nov 2024 17:00:44 +0100 Subject: [PATCH 09/12] Test tuple lattice --- .../cpg/helpers/functional/BasicLattices.kt | 3 +- .../aisec/cpg/helpers/BasicLatticesTest.kt | 47 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt index 11668449b9..f9e6aa85ba 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt @@ -79,7 +79,8 @@ class PowersetLattice(elements: Set) : LatticeElement>(elements) { } override fun equals(other: Any?): Boolean { - return other is PowersetLattice && this.elements == other.elements + // The call of `toSet` ensures that we don't get stuck for different types of sets. + return other is PowersetLattice && this.elements.toSet() == other.elements.toSet() } override fun hashCode(): Int { diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt index 6e2a5a86b2..bbfa8bbd4a 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt @@ -29,6 +29,7 @@ import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.helpers.functional.MapLattice import de.fraunhofer.aisec.cpg.helpers.functional.PowersetLattice import de.fraunhofer.aisec.cpg.helpers.functional.PowersetLatticeT +import de.fraunhofer.aisec.cpg.helpers.functional.TupleLattice import de.fraunhofer.aisec.cpg.helpers.functional.emptyMapLattice import de.fraunhofer.aisec.cpg.helpers.functional.emptyPowersetLattice import kotlin.test.Test @@ -36,6 +37,7 @@ import kotlin.test.assertEquals import kotlin.test.assertIs import kotlin.test.assertNotEquals import kotlin.test.assertNotSame +import kotlin.test.assertSame class BasicLatticesTest { @Test @@ -44,6 +46,7 @@ class BasicLatticesTest { val emptyLattice2 = emptyPowersetLattice() assertEquals(0, emptyLattice1.compareTo(emptyLattice2)) assertEquals(emptyLattice1, emptyLattice2) + assertNotSame(emptyLattice1.hashCode(), emptyLattice1.hashCode()) val blaLattice1 = PowersetLattice(setOf("bla")) val blaLattice2 = PowersetLattice(setOf("bla")) @@ -115,6 +118,7 @@ class BasicLatticesTest { val emptyLattice2 = emptyMapLattice>() assertEquals(0, emptyLattice1.compareTo(emptyLattice2)) assertEquals(emptyLattice1, emptyLattice2) + assertNotSame(emptyLattice1.hashCode(), emptyLattice1.hashCode()) val aBlaLattice1 = MapLattice>(mapOf("a" to PowersetLattice(setOf("bla")))) @@ -207,4 +211,47 @@ class BasicLatticesTest { assertEquals(setOf("bla", "foo"), aBlaFooBBla.elements["a"]?.elements) assertEquals(setOf("bla"), aBlaFooBBla.elements["b"]?.elements) } + + @Test + fun testPairLattice() { + val emptyEmpty = + TupleLattice, Set>( + Pair(emptyPowersetLattice(), emptyPowersetLattice()) + ) + val emptyBla = + TupleLattice, Set>( + Pair(emptyPowersetLattice(), PowersetLattice(setOf("bla"))) + ) + val blaEmpty = + TupleLattice, Set>( + Pair(PowersetLattice(setOf("bla")), emptyPowersetLattice()) + ) + val emptyBla2 = emptyBla.duplicate() + assertEquals(0, emptyBla.compareTo(emptyBla2)) + assertEquals(emptyBla, emptyBla2) + assertNotSame(emptyBla, emptyBla2) + assertNotSame(emptyBla.hashCode(), emptyBla2.hashCode()) + val (emptyBlaFirst, emptyBlaSecond) = emptyBla + assertSame(emptyBlaFirst, emptyBla.elements.first) + assertSame(emptyBlaSecond, emptyBla.elements.second) + assertNotSame(emptyBlaFirst, emptyBla2.elements.first) + assertEquals(emptyBlaFirst, emptyBla2.elements.first) + assertNotSame(emptyBlaSecond, emptyBla2.elements.second) + assertEquals(emptyBlaSecond, emptyBla2.elements.second) + + assertEquals(-1, emptyEmpty.compareTo(emptyBla)) + assertEquals(-1, emptyEmpty.compareTo(blaEmpty)) + assertEquals(1, emptyBla.compareTo(emptyEmpty)) + assertEquals(1, blaEmpty.compareTo(emptyEmpty)) + assertEquals(-1, blaEmpty.compareTo(emptyBla)) + assertEquals(-1, emptyBla.compareTo(blaEmpty)) + + val blaBla = emptyBla.lub(blaEmpty) + assertEquals(-1, emptyEmpty.compareTo(blaBla)) + assertEquals(-1, emptyBla.compareTo(blaBla)) + assertEquals(-1, blaEmpty.compareTo(blaBla)) + assertEquals(1, blaBla.compareTo(emptyEmpty)) + assertEquals(1, blaBla.compareTo(emptyBla)) + assertEquals(1, blaBla.compareTo(blaEmpty)) + } } From a5ff47ae27a4387e557d73bba7c96cbbbad8c454 Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Mon, 11 Nov 2024 17:14:12 +0100 Subject: [PATCH 10/12] Test triple lattice --- .../aisec/cpg/helpers/BasicLatticesTest.kt | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt index bbfa8bbd4a..ad501c2007 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt @@ -29,11 +29,13 @@ import de.fraunhofer.aisec.cpg.graph.Node import de.fraunhofer.aisec.cpg.helpers.functional.MapLattice import de.fraunhofer.aisec.cpg.helpers.functional.PowersetLattice import de.fraunhofer.aisec.cpg.helpers.functional.PowersetLatticeT +import de.fraunhofer.aisec.cpg.helpers.functional.TripleLattice import de.fraunhofer.aisec.cpg.helpers.functional.TupleLattice import de.fraunhofer.aisec.cpg.helpers.functional.emptyMapLattice import de.fraunhofer.aisec.cpg.helpers.functional.emptyPowersetLattice import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertIs import kotlin.test.assertNotEquals import kotlin.test.assertNotSame @@ -210,6 +212,10 @@ class BasicLatticesTest { assertEquals(setOf("a", "b"), aBlaFooBBla.elements.keys) assertEquals(setOf("bla", "foo"), aBlaFooBBla.elements["a"]?.elements) assertEquals(setOf("bla"), aBlaFooBBla.elements["b"]?.elements) + + assertFalse(aBlaFooBBla == emptyLattice1) // Wrong elements + assertFalse(aBlaFooBBla == aBlaFooBBla.elements["a"]) // Wrong types + assertFalse(aBlaFooBBla.elements["a"] == aBlaFooBBla) // Wrong types } @Test @@ -253,5 +259,85 @@ class BasicLatticesTest { assertEquals(1, blaBla.compareTo(emptyEmpty)) assertEquals(1, blaBla.compareTo(emptyBla)) assertEquals(1, blaBla.compareTo(blaEmpty)) + + // We explicitly want to call equals here + assertFalse(blaBla == emptyBla) // Wrong elements + assertFalse(blaBla == emptyBlaFirst) // Wrong types + assertFalse(emptyBlaFirst == blaBla) // Wrong types + } + + @Test + fun testTripleLattice() { + val emptyEmptyEmpty = + TripleLattice, Set, Set>( + Triple( + emptyPowersetLattice(), + emptyPowersetLattice(), + emptyPowersetLattice() + ) + ) + val emptyEmptyBla = + TripleLattice, Set, Set>( + Triple( + emptyPowersetLattice(), + emptyPowersetLattice(), + PowersetLattice(setOf("bla")) + ) + ) + val emptyBlaEmpty = + TripleLattice, Set, Set>( + Triple( + emptyPowersetLattice(), + PowersetLattice(setOf("bla")), + emptyPowersetLattice() + ) + ) + val blaEmptyEmpty = + TripleLattice, Set, Set>( + Triple( + PowersetLattice(setOf("bla")), + emptyPowersetLattice(), + emptyPowersetLattice() + ) + ) + val emptyBla2 = emptyEmptyBla.duplicate() + assertEquals(0, emptyEmptyBla.compareTo(emptyBla2)) + assertEquals(emptyEmptyBla, emptyBla2) + assertNotSame(emptyEmptyBla, emptyBla2) + assertNotSame(emptyEmptyBla.hashCode(), emptyBla2.hashCode()) + val (emptyBlaFirst, emptyBlaSecond, emptyBlaThird) = emptyEmptyBla + assertSame(emptyBlaFirst, emptyEmptyBla.elements.first) + assertSame(emptyBlaSecond, emptyEmptyBla.elements.second) + assertSame(emptyBlaThird, emptyEmptyBla.elements.third) + assertNotSame(emptyBlaFirst, emptyBla2.elements.first) + assertEquals(emptyBlaFirst, emptyBla2.elements.first) + assertNotSame(emptyBlaSecond, emptyBla2.elements.second) + assertEquals(emptyBlaSecond, emptyBla2.elements.second) + assertNotSame(emptyBlaThird, emptyBla2.elements.third) + assertEquals(emptyBlaThird, emptyBla2.elements.third) + + assertEquals(-1, emptyEmptyEmpty.compareTo(emptyEmptyBla)) + assertEquals(-1, emptyEmptyEmpty.compareTo(emptyBlaEmpty)) + assertEquals(-1, emptyEmptyEmpty.compareTo(blaEmptyEmpty)) + assertEquals(1, emptyEmptyBla.compareTo(emptyEmptyEmpty)) + assertEquals(1, blaEmptyEmpty.compareTo(emptyEmptyEmpty)) + assertEquals(1, emptyBlaEmpty.compareTo(emptyEmptyEmpty)) + assertEquals(-1, blaEmptyEmpty.compareTo(emptyEmptyBla)) + assertEquals(-1, emptyEmptyBla.compareTo(blaEmptyEmpty)) + assertEquals(-1, emptyBlaEmpty.compareTo(blaEmptyEmpty)) + + val blaEmptyBla = emptyEmptyBla.lub(blaEmptyEmpty) + assertEquals(-1, emptyEmptyEmpty.compareTo(blaEmptyBla)) + assertEquals(-1, emptyEmptyBla.compareTo(blaEmptyBla)) + assertEquals(-1, blaEmptyEmpty.compareTo(blaEmptyBla)) + assertEquals(-1, emptyBlaEmpty.compareTo(blaEmptyBla)) + assertEquals(1, blaEmptyBla.compareTo(emptyEmptyEmpty)) + assertEquals(1, blaEmptyBla.compareTo(emptyEmptyBla)) + assertEquals(1, blaEmptyBla.compareTo(blaEmptyEmpty)) + + // We explicitly want to call equals here + assertFalse(blaEmptyBla == emptyEmptyBla) // Wrong elements + assertFalse(blaEmptyBla == emptyBlaFirst) // Wrong types + assertFalse(emptyBlaFirst == blaEmptyBla) // Wrong types } } From 0adc0383c4a80f44cc524cd91264769b6dfba882 Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Tue, 12 Nov 2024 09:21:52 +0100 Subject: [PATCH 11/12] More equals --- .../aisec/cpg/helpers/BasicLatticesTest.kt | 39 ++++++++++++------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt index ad501c2007..ade2648a9f 100644 --- a/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt +++ b/cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/helpers/BasicLatticesTest.kt @@ -40,6 +40,7 @@ import kotlin.test.assertIs import kotlin.test.assertNotEquals import kotlin.test.assertNotSame import kotlin.test.assertSame +import kotlin.test.assertTrue class BasicLatticesTest { @Test @@ -124,11 +125,11 @@ class BasicLatticesTest { val aBlaLattice1 = MapLattice>(mapOf("a" to PowersetLattice(setOf("bla")))) - val bBlaLattice2 = + val aBlaLattice2 = MapLattice>(mapOf("a" to PowersetLattice(setOf("bla")))) - assertEquals(0, aBlaLattice1.compareTo(bBlaLattice2)) - assertEquals(aBlaLattice1, bBlaLattice2) - assertNotSame(aBlaLattice1, bBlaLattice2) + assertEquals(0, aBlaLattice1.compareTo(aBlaLattice2)) + assertEquals(aBlaLattice1, aBlaLattice2) + assertNotSame(aBlaLattice1, aBlaLattice2) val aBlaFooLattice = MapLattice>(mapOf("a" to PowersetLattice(setOf("bla", "foo")))) @@ -214,6 +215,8 @@ class BasicLatticesTest { assertEquals(setOf("bla"), aBlaFooBBla.elements["b"]?.elements) assertFalse(aBlaFooBBla == emptyLattice1) // Wrong elements + assertTrue(emptyLattice1 == emptyLattice2) // This is equal + assertTrue(aBlaLattice1 == aBlaLattice2) // This is equal assertFalse(aBlaFooBBla == aBlaFooBBla.elements["a"]) // Wrong types assertFalse(aBlaFooBBla.elements["a"] == aBlaFooBBla) // Wrong types } @@ -262,6 +265,8 @@ class BasicLatticesTest { // We explicitly want to call equals here assertFalse(blaBla == emptyBla) // Wrong elements + assertFalse(blaBla == blaEmpty) // Wrong elements + assertTrue(emptyBla2 == emptyBla) // Wrong elements assertFalse(blaBla == emptyBlaFirst) // Wrong types assertFalse(emptyBlaFirst == blaBla) // Wrong types } @@ -300,21 +305,21 @@ class BasicLatticesTest { emptyPowersetLattice() ) ) - val emptyBla2 = emptyEmptyBla.duplicate() - assertEquals(0, emptyEmptyBla.compareTo(emptyBla2)) - assertEquals(emptyEmptyBla, emptyBla2) - assertNotSame(emptyEmptyBla, emptyBla2) - assertNotSame(emptyEmptyBla.hashCode(), emptyBla2.hashCode()) + val emptyEmptyBla2 = emptyEmptyBla.duplicate() + assertEquals(0, emptyEmptyBla.compareTo(emptyEmptyBla2)) + assertEquals(emptyEmptyBla, emptyEmptyBla2) + assertNotSame(emptyEmptyBla, emptyEmptyBla2) + assertNotSame(emptyEmptyBla.hashCode(), emptyEmptyBla2.hashCode()) val (emptyBlaFirst, emptyBlaSecond, emptyBlaThird) = emptyEmptyBla assertSame(emptyBlaFirst, emptyEmptyBla.elements.first) assertSame(emptyBlaSecond, emptyEmptyBla.elements.second) assertSame(emptyBlaThird, emptyEmptyBla.elements.third) - assertNotSame(emptyBlaFirst, emptyBla2.elements.first) - assertEquals(emptyBlaFirst, emptyBla2.elements.first) - assertNotSame(emptyBlaSecond, emptyBla2.elements.second) - assertEquals(emptyBlaSecond, emptyBla2.elements.second) - assertNotSame(emptyBlaThird, emptyBla2.elements.third) - assertEquals(emptyBlaThird, emptyBla2.elements.third) + assertNotSame(emptyBlaFirst, emptyEmptyBla2.elements.first) + assertEquals(emptyBlaFirst, emptyEmptyBla2.elements.first) + assertNotSame(emptyBlaSecond, emptyEmptyBla2.elements.second) + assertEquals(emptyBlaSecond, emptyEmptyBla2.elements.second) + assertNotSame(emptyBlaThird, emptyEmptyBla2.elements.third) + assertEquals(emptyBlaThird, emptyEmptyBla2.elements.third) assertEquals(-1, emptyEmptyEmpty.compareTo(emptyEmptyBla)) assertEquals(-1, emptyEmptyEmpty.compareTo(emptyBlaEmpty)) @@ -337,6 +342,10 @@ class BasicLatticesTest { // We explicitly want to call equals here assertFalse(blaEmptyBla == emptyEmptyBla) // Wrong elements + assertFalse(emptyEmptyEmpty == emptyEmptyBla) // Wrong elements + assertFalse(emptyEmptyEmpty == emptyBlaEmpty) // Wrong elements + assertFalse(emptyEmptyEmpty == blaEmptyEmpty) // Wrong elements + assertTrue(emptyEmptyBla2 == emptyEmptyBla) // Same type and same elements assertFalse(blaEmptyBla == emptyBlaFirst) // Wrong types assertFalse(emptyBlaFirst == blaEmptyBla) // Wrong types } From 9e5a85a585c3a85d3dfbc4cffa690eec3e816749 Mon Sep 17 00:00:00 2001 From: Alexander Kuechler Date: Tue, 12 Nov 2024 15:21:21 +0100 Subject: [PATCH 12/12] Update MapLattice iteration --- .../cpg/helpers/functional/BasicLattices.kt | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt index f9e6aa85ba..ab7203c8cd 100644 --- a/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt +++ b/cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLattices.kt @@ -98,15 +98,21 @@ open class MapLattice(elements: Map>) : override fun lub( other: LatticeElement>> ): LatticeElement>> { - return MapLattice( - this.elements.entries.fold(other.elements) { current, (thisKey, thisValue) -> - val mutableMap = current.toMutableMap() - mutableMap.compute(thisKey) { k, v -> - if (v == null) thisValue else thisValue.lub(v) - } - mutableMap + val allKeys = other.elements.keys.union(this.elements.keys) + val newMap = + allKeys.fold(mutableMapOf>()) { current, key -> + val otherValue = other.elements[key] + val thisValue = this.elements[key] + val newValue = + if (thisValue != null && otherValue != null) { + thisValue.lub(otherValue) + } else if (thisValue != null) { + thisValue + } else otherValue + newValue?.let { current[key] = it } + current } - ) + return MapLattice(newMap) } override fun duplicate(): LatticeElement>> {