Skip to content

Commit bfd705e

Browse files
committed
More robust implementation
1 parent da84566 commit bfd705e

File tree

1 file changed

+53
-28
lines changed
  • cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph

1 file changed

+53
-28
lines changed

Diff for: cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/Extensions.kt

+53-28
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import de.fraunhofer.aisec.cpg.graph.statements.*
3434
import de.fraunhofer.aisec.cpg.graph.statements.expressions.*
3535
import de.fraunhofer.aisec.cpg.graph.statements.expressions.Block
3636
import de.fraunhofer.aisec.cpg.helpers.SubgraphWalker
37+
import kotlin.collections.firstOrNull
3738
import kotlin.math.absoluteValue
3839

3940
/**
@@ -227,7 +228,7 @@ fun Node.followPrevFullDFGEdgesUntilHit(
227228
predicate: (Node) -> Boolean,
228229
): FulfilledAndFailedPaths {
229230
return followXUntilHit(
230-
x = { currentNode, _ -> currentNode.prevFullDFG },
231+
x = { currentNode, _, _ -> currentNode.prevFullDFG },
231232
collectFailedPaths = collectFailedPaths,
232233
findAllPossiblePaths = findAllPossiblePaths,
233234
predicate = predicate,
@@ -267,7 +268,7 @@ fun Node.followPrevDFGEdgesUntilHit(
267268
predicate: (Node) -> Boolean,
268269
): FulfilledAndFailedPaths {
269270
return followXUntilHit(
270-
x = { currentNode, ctx ->
271+
x = { currentNode, ctx, path ->
271272
if (
272273
useIndexStack &&
273274
currentNode is InitializerListExpression &&
@@ -324,10 +325,10 @@ fun Node.followPrevDFGEdgesUntilHit(
324325
*/
325326
class Context(
326327
val indexStack: SimpleStack<IndexedDataflowGranularity> = SimpleStack(),
327-
val callStack: SimpleStack<CallExpression> = SimpleStack(),
328+
val callStack: ArrayDeque<CallExpression> = ArrayDeque<CallExpression>(),
328329
) {
329330
fun clone(): Context {
330-
return Context(indexStack.clone(), callStack.clone())
331+
return Context(indexStack.clone(), ArrayDeque(callStack))
331332
}
332333
}
333334

@@ -340,6 +341,9 @@ class SimpleStack<T>() {
340341
deque.addFirst(newElem)
341342
}
342343

344+
val current: T?
345+
get() = deque.firstOrNull()
346+
343347
fun checkAndPop(elemToPop: T): Boolean {
344348
if (deque.firstOrNull() == elemToPop) {
345349
deque.removeFirst()
@@ -475,7 +479,7 @@ fun Node.followNextPDGUntilHit(
475479
predicate: (Node) -> Boolean,
476480
): FulfilledAndFailedPaths {
477481
return followXUntilHit(
478-
x = { currentNode, _ ->
482+
x = { currentNode, _, _ ->
479483
val nextNodes = currentNode.nextPDG.toMutableList()
480484
if (interproceduralAnalysis) {
481485
nextNodes.addAll((currentNode as? CallExpression)?.calls ?: listOf())
@@ -504,7 +508,7 @@ fun Node.followNextCDGUntilHit(
504508
predicate: (Node) -> Boolean,
505509
): FulfilledAndFailedPaths {
506510
return followXUntilHit(
507-
x = { currentNode, _ ->
511+
x = { currentNode, _, _ ->
508512
val nextNodes = currentNode.nextCDG.toMutableList()
509513
if (interproceduralAnalysis) {
510514
nextNodes.addAll((currentNode as? CallExpression)?.calls ?: listOf())
@@ -534,7 +538,7 @@ fun Node.followPrevPDGUntilHit(
534538
predicate: (Node) -> Boolean,
535539
): FulfilledAndFailedPaths {
536540
return followXUntilHit(
537-
x = { currentNode, _ ->
541+
x = { currentNode, _, _ ->
538542
val nextNodes = currentNode.prevPDG.toMutableList()
539543
if (interproceduralAnalysis) {
540544
nextNodes.addAll(
@@ -568,7 +572,7 @@ fun Node.followPrevCDGUntilHit(
568572
predicate: (Node) -> Boolean,
569573
): FulfilledAndFailedPaths {
570574
return followXUntilHit(
571-
x = { currentNode, _ ->
575+
x = { currentNode, _, _ ->
572576
val nextNodes = currentNode.prevCDG.toMutableList()
573577
if (interproceduralAnalysis) {
574578
nextNodes.addAll(
@@ -596,7 +600,7 @@ fun Node.followPrevCDGUntilHit(
596600
* not mandatory**. If the list "failed" is empty, the path is mandatory.
597601
*/
598602
inline fun Node.followXUntilHit(
599-
noinline x: (Node, Context) -> Collection<Node>,
603+
noinline x: (Node, Context, List<Node>) -> Collection<Node>,
600604
collectFailedPaths: Boolean = true,
601605
findAllPossiblePaths: Boolean = true,
602606
context: Context = Context(),
@@ -621,7 +625,7 @@ inline fun Node.followXUntilHit(
621625
alreadySeenNodes.add(currentNode)
622626
// The last node of the path is where we continue. We get all of its outgoing CDG edges and
623627
// follow them
624-
var nextNodes = x(currentNode, currentContext)
628+
var nextNodes = x(currentNode, currentContext, currentPath.first)
625629

626630
// No further nodes in the path and the path criteria are not satisfied.
627631
if (nextNodes.isEmpty() && collectFailedPaths) failedPaths.add(currentPath.first)
@@ -672,7 +676,7 @@ fun Node.followNextFullDFGEdgesUntilHit(
672676
predicate: (Node) -> Boolean,
673677
): FulfilledAndFailedPaths {
674678
return followXUntilHit(
675-
x = { currentNode, _ -> currentNode.nextFullDFG },
679+
x = { currentNode, _, _ -> currentNode.nextFullDFG },
676680
collectFailedPaths = collectFailedPaths,
677681
findAllPossiblePaths = findAllPossiblePaths,
678682
predicate = predicate,
@@ -695,7 +699,7 @@ fun Node.followNextDFGEdgesUntilHit(
695699
predicate: (Node) -> Boolean,
696700
): FulfilledAndFailedPaths {
697701
return followXUntilHit(
698-
x = { currentNode, ctx ->
702+
x = { currentNode, ctx, path ->
699703
if (
700704
useIndexStack &&
701705
currentNode is InitializerListExpression &&
@@ -726,7 +730,7 @@ fun Node.followNextDFGEdgesUntilHit(
726730
currentNode.nextDFGEdges.forEach {
727731
if (it is ContextSensitiveDataflow && it.callingContext is CallingContextIn) {
728732
// Push the call of our calling context to the stack
729-
ctx.callStack.push(it.callingContext.call)
733+
ctx.callStack.addFirst(it.callingContext.call)
730734
}
731735
if (
732736
it.end is InitializerListExpression &&
@@ -739,21 +743,33 @@ fun Node.followNextDFGEdgesUntilHit(
739743
}
740744

741745
// We need to filter out the edges which based on the stack
742-
currentNode.nextDFGEdges
743-
.filter {
744-
if (ctx.callStack.isEmpty()) {
745-
true
746-
} else if (
747-
it is ContextSensitiveDataflow && it.callingContext is CallingContextOut
748-
) {
749-
// We are only interested in outgoing edges from our current "call-in".
750-
// If we found it, we can pop it.
751-
ctx.callStack.checkAndPop(it.callingContext.call)
752-
} else {
753-
true
746+
val selected =
747+
currentNode.nextDFGEdges
748+
.filter {
749+
if (ctx.callStack.isEmpty()) {
750+
true
751+
} else if (
752+
it is ContextSensitiveDataflow &&
753+
it.callingContext is CallingContextOut
754+
) {
755+
// We are only interested in outgoing edges from our current
756+
// "call-in", i.e., the call expression that is on the stack.
757+
ctx.callStack.firstOrNull() == it.callingContext.call
758+
} else {
759+
true
760+
}
754761
}
762+
.map { it.end }
763+
764+
// Let's do any remaining pop'ing
765+
currentNode.nextDFGEdges.forEach {
766+
if (it is ContextSensitiveDataflow && it.callingContext is CallingContextOut) {
767+
// Pop the current call, if it's on top
768+
ctx.callStack.removeIfFirst<CallExpression>(it.callingContext.call)
755769
}
756-
.map { it.end }
770+
}
771+
772+
selected
757773
}
758774
},
759775
collectFailedPaths = collectFailedPaths,
@@ -778,7 +794,7 @@ fun Node.followNextEOGEdgesUntilHit(
778794
predicate: (Node) -> Boolean,
779795
): FulfilledAndFailedPaths {
780796
return followXUntilHit(
781-
x = { currentNode, _ ->
797+
x = { currentNode, _, _ ->
782798
currentNode.nextEOGEdges.filter { it.unreachable != true }.map { it.end }
783799
},
784800
collectFailedPaths = collectFailedPaths,
@@ -803,7 +819,7 @@ fun Node.followPrevEOGEdgesUntilHit(
803819
predicate: (Node) -> Boolean,
804820
): FulfilledAndFailedPaths {
805821
return followXUntilHit(
806-
x = { currentNode, _ ->
822+
x = { currentNode, _, _ ->
807823
currentNode.prevEOGEdges.filter { it.unreachable != true }.map { it.start }
808824
},
809825
collectFailedPaths = collectFailedPaths,
@@ -1291,3 +1307,12 @@ val Expression.isImported: Boolean
12911307
get() {
12921308
return this.importedFrom.isNotEmpty()
12931309
}
1310+
1311+
private fun <T> ArrayDeque<T>.removeIfFirst(element: T): Boolean {
1312+
return if (firstOrNull() == element) {
1313+
removeFirst()
1314+
true
1315+
} else {
1316+
false
1317+
}
1318+
}

0 commit comments

Comments
 (0)