Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -587,18 +587,22 @@ open class PointsToPass(ctx: TranslationContext) : EOGStarterPass(ctx, orderDepe
// Then the memoryValues
if (key is HasMemoryValue && value.second.isNotEmpty()) {
value.second.forEach { (v, properties) ->
var granularity = default()
var shortFS = false
properties.forEach { p ->
when (p) {
// String indicated a partial target
is String -> granularity = PartialDataflowGranularity(p)
// Boolean says if this is a shortFS or not
is Boolean -> shortFS = p
else -> TODO()
// We already added the parameters value in initializeParameters, so we skip
// that now
if (v !is Parameter) {
var granularity = default()
var shortFS = false
properties.forEach { p ->
when (p) {
// String indicated a partial target
is String -> granularity = PartialDataflowGranularity(p)
// Boolean says if this is a shortFS or not
is Boolean -> shortFS = p
else -> TODO()
}
}
key.memoryValueEdges += Dataflow(v, key, granularity, shortFS)
}
key.memoryValueEdges += Dataflow(v, key, granularity, shortFS)
}
}

Expand Down Expand Up @@ -935,15 +939,9 @@ open class PointsToPass(ctx: TranslationContext) : EOGStarterPass(ctx, orderDepe
mutableSetOf(
NodeWithPropertiesKey(
matchingDeclarations,
// Add the parameter
// index to indicate
// to the
// calculatePrevDFGs
// function that we
// need to replace
// the
// value of the call
// argument
// Add the parameter index to indicate to the
// calculatePrevDFGs function that we need to
// replace the value of the call argument
equalLinkedHashSetOf(
matchingDeclarations.argumentIndex
),
Expand Down Expand Up @@ -1920,6 +1918,20 @@ open class PointsToPass(ctx: TranslationContext) : EOGStarterPass(ctx, orderDepe
}

private suspend fun calculateFunctionSummaries(invoke: Function): Function? {
fun addDummyFS() {
if (log.isTraceEnabled) {
log.trace("Creating dummy function summary")
}
val newValues = ConcurrentHashMap.newKeySet<FSEntry>()
invoke.parameters.forEach { newValues.add(FSEntry(0, it, 1, "", isDummy = true)) }
val entries = identitySetOf<Node>()
if (invoke.returns.isNotEmpty()) entries.addAll(invoke.returns) else entries.add(invoke)
entries.forEach { entry -> invoke.functionSummary.put(entry, newValues) }
if (log.isTraceEnabled) {
log.trace("Finished creating dummy function summary")
}
}

if (invoke.functionSummary.isEmpty()) {
if (invoke.hasBody()) {
if (log.isTraceEnabled) {
Expand Down Expand Up @@ -1970,28 +1982,13 @@ open class PointsToPass(ctx: TranslationContext) : EOGStarterPass(ctx, orderDepe
log.error(
"Cannot calculate functionSummary for ${invoke.name.localName} as it's recursively called. callChain: ${functionSummaryAnalysisChain.map{it.name.localName}}"
)
invoke.functionSummary.put(
newLiteral("dummy"),
ConcurrentHashMap.newKeySet<FSEntry>(),
)
return null
addDummyFS()
}
} else {
// Add a dummy function summary so that we don't try this every time
// In this dummy, all parameters point either to returns if we have any, or the
// Function itself
if (log.isTraceEnabled) {
log.trace("Creating dummy function summary")
}
val newValues = ConcurrentHashMap.newKeySet<FSEntry>()
invoke.parameters.forEach { newValues.add(FSEntry(0, it, 1, "")) }
val entries = identitySetOf<Node>()
if (invoke.returns.isNotEmpty()) entries.addAll(invoke.returns)
else entries.add(invoke)
entries.forEach { entry -> invoke.functionSummary.put(entry, newValues) }
if (log.isTraceEnabled) {
log.trace("Finished creating dummy function summary")
}
addDummyFS()
}
}
return invoke
Expand Down Expand Up @@ -3208,6 +3205,11 @@ open class PointsToPass(ctx: TranslationContext) : EOGStarterPass(ctx, orderDepe
PowersetLattice.Element(),
),
)
// Additionally, directly add the value to the node. This is necessary on recursive
// calls, when we are not yet finished with acceptInternal of one function, but we
// already need to fetch the PMVs because we have a recursive call while handling
// the function
src.memoryValueEdges += Dataflow(pmv, src)
} else {
// Link the PMVs with each other so that we can find them. This is
// especially important outside the respective function where we
Expand Down Expand Up @@ -3249,6 +3251,10 @@ open class PointsToPass(ctx: TranslationContext) : EOGStarterPass(ctx, orderDepe
PowersetLattice.Element(),
),
)
// Here again, directly add the value to the param to be able to access it directly
// instead of having to wait until acceptInternal is finished
param.memoryValueEdges +=
Dataflow(pmv, param, granularity = PartialDataflowGranularity(pmvName))
}

// Update the states
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2025, 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.frontends.cxx

import de.fraunhofer.aisec.cpg.graph.functions
import de.fraunhofer.aisec.cpg.graph.invoke
import de.fraunhofer.aisec.cpg.graph.variables
import de.fraunhofer.aisec.cpg.test.BaseTest
import de.fraunhofer.aisec.cpg.test.analyze
import java.io.File
import kotlin.test.Test
import org.junit.jupiter.api.assertNotNull

class FollowXTest : BaseTest() {
@Test
fun testFollowX() {
val file = File("src/test/resources/complex_dfg.c")
val result =
analyze(listOf(file), file.parentFile.toPath(), true) {
it.registerLanguage<CLanguage>()
}
assertNotNull(result)

// functions
val mainFunc = result.functions("main").single()

// Variables
val i = result.variables("i").single()

// actual tests
// This will run forever (or very long), so it's commented out.
// TODO: Fix
/* val (paths, time) =
measureTimedValue {
i.followDFGEdgesUntilHit(
findAllPossiblePaths = true,
sensitivities = OnlyFullDFG + FieldSensitive + ContextSensitive,
predicate = { node -> (node as? Reference)?.name?.localName == "i" },
)
}
println("Path lookup took $time")*/
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import de.fraunhofer.aisec.cpg.graph.declarations.Variable
import de.fraunhofer.aisec.cpg.graph.edges.flows.*
import de.fraunhofer.aisec.cpg.graph.expressions.*
import de.fraunhofer.aisec.cpg.helpers.toIdentitySet
import de.fraunhofer.aisec.cpg.test.analyze
import de.fraunhofer.aisec.cpg.test.analyzeAndGetFirstTU
import de.fraunhofer.aisec.cpg.test.assertLocalName
import java.io.File
Expand Down Expand Up @@ -5005,4 +5006,26 @@ class PointsToPassTest {
)
}
}

@Test
fun testDerefPMVsOfRecursiveFunctions() {
val file = File("src/test/resources/complex_dfg.c")
val result =
analyze(listOf(file), file.parentFile.toPath(), true) {
it.registerLanguage<CLanguage>()
}
assertNotNull(result)

// Functions
val func2 = result.functions("func2").single { it.body != null }
val func3 = result.functions("func3").single { it.body != null }

// Params and PMVs
val func2Param = func2.parameters.single()
val func2DerefPMV = func2Param.memoryValues.single { it.name.localName == "derefvalue" }

// actual tests
assertEquals(2, func2DerefPMV.prevDFG.size)
assertContains(func2DerefPMV.prevFullDFG, func3.allChildren<UnaryOperator>().single().input)
}
}
30 changes: 30 additions & 0 deletions cpg-language-cxx/src/test/resources/complex_dfg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include<stdio.h>

int func1(int* p);
int func2(int* p);
int func3(int* p);

int func3(int *p){
printf("%d\n", *p);
(*p)++;
func2(p);
}

int func2(int* p) {
for (int j=0; j<10; j++) {
func3(p);
}
}

int func1(int* p) {
(*p)++;
func2(p);
}

int main() {
int i=0;
int* p=&i;
func1(p);
printf("%d\n", i);
return 0;
}
Loading