diff --git a/README.md b/README.md index e5a97d2078..1ce2a109fb 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ The following authors have contributed to this project (in alphabetical order): * [maximiliankaul](https://github.com/maximiliankaul) * [obraunsdorf](https://github.com/obraunsdorf) * [oxisto](https://github.com/oxisto) +* [peckto](https://github.com/peckto) * [titze](https://github.com/titze) * [vfsrfs](https://github.com/vfsrfs) diff --git a/cpg-core/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXAmbiguitiesTest.kt b/cpg-core/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXAmbiguitiesTest.kt index 44421f48b2..e990e8ecc1 100644 --- a/cpg-core/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXAmbiguitiesTest.kt +++ b/cpg-core/src/test/java/de/fraunhofer/aisec/cpg/frontends/cpp/CXXAmbiguitiesTest.kt @@ -28,10 +28,14 @@ package de.fraunhofer.aisec.cpg.frontends.cpp import de.fraunhofer.aisec.cpg.TestUtils import de.fraunhofer.aisec.cpg.graph.bodyOrNull import de.fraunhofer.aisec.cpg.graph.byNameOrNull +import de.fraunhofer.aisec.cpg.graph.declarations.FunctionDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.MethodDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.ProblemDeclaration import de.fraunhofer.aisec.cpg.graph.declarations.RecordDeclaration import de.fraunhofer.aisec.cpg.graph.statements.DeclarationStatement +import de.fraunhofer.aisec.cpg.graph.statements.expressions.CallExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.CastExpression +import de.fraunhofer.aisec.cpg.graph.statements.expressions.MemberCallExpression import java.io.File import kotlin.test.assertContains import kotlin.test.assertEquals @@ -76,4 +80,61 @@ class CXXAmbiguitiesTest { assertNotNull(problem) assertContains(problem.problem, "CDT") } + + /** + * In CXX there is an ambiguity with the statement: "(A)(B);" 1) If A is a function pointer, + * this is a [CallExpression] 2) If A is a type, this is a [CastExpression] + */ + @Test + fun testFunctionCallOrTypeCast() { + val file = File("src/test/resources/function_ptr_or_type_cast.c") + val tu = TestUtils.analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) + assertNotNull(tu) + + val mainFunc = tu.byNameOrNull("main") + assertNotNull(mainFunc) + + val fooFunc = tu.byNameOrNull("foo") + assertNotNull(fooFunc) + + // First two Statements are CallExpressions + val s1 = mainFunc.getBodyStatementAs(1, CallExpression::class.java) + assertNotNull(s1) + assertEquals(s1.invokes.iterator().next(), fooFunc) + + val s2 = mainFunc.getBodyStatementAs(2, CallExpression::class.java) + assertNotNull(s2) + assertEquals(s2.invokes.iterator().next(), fooFunc) + + // Last two Statements are CastExpressions + val s3 = mainFunc.getBodyStatementAs(3, CastExpression::class.java) + assertNotNull(s3) + + val s4 = mainFunc.getBodyStatementAs(4, CastExpression::class.java) + assertNotNull(s4) + } + + /** + * In CXX there is an ambiguity with the statement: "(A.B)(C);" 1) If B is a method, this is a + * [MemberCallExpression] 2) if B is a function pointer, this is a [CallExpression]. + * + * Function pointer as a struct member are currently not supported in the cpg. This test case + * will just ensure that there will be no crash when parsing such a statement. When adding this + * functionality in the cpg, this test case must be adapted accordingly. + */ + @Test + fun testMethodOrFunction() { + val file = File("src/test/resources/method_or_function_call.cpp") + val tu = TestUtils.analyzeAndGetFirstTU(listOf(file), file.parentFile.toPath(), true) + assertNotNull(tu) + + val mainFunc = tu.byNameOrNull("main") + assertNotNull(mainFunc) + + val classA = tu.byNameOrNull("A") + assertNotNull(classA) + + val structB = tu.byNameOrNull("B") + assertNotNull(structB) + } } diff --git a/cpg-core/src/test/resources/function_ptr_or_type_cast.c b/cpg-core/src/test/resources/function_ptr_or_type_cast.c new file mode 100644 index 0000000000..d5f437e037 --- /dev/null +++ b/cpg-core/src/test/resources/function_ptr_or_type_cast.c @@ -0,0 +1,22 @@ +void foo(int i) { +} + +struct S { + int a; +} typedef s_t; + +typedef s_t* s_t_p; + +int main() { + void (*ptr)(int) = &foo; + + // this is a function call + (*ptr)(1); + (ptr)(2); + + // this is a type case + (int)(3); + (s_t_p)(4); + + return 0; +} diff --git a/cpg-core/src/test/resources/method_or_function_call.cpp b/cpg-core/src/test/resources/method_or_function_call.cpp new file mode 100644 index 0000000000..c12651c89c --- /dev/null +++ b/cpg-core/src/test/resources/method_or_function_call.cpp @@ -0,0 +1,27 @@ +struct A { + void foo(int i) { + } +}; + +struct B { + void (*bar)(int); +}; + +void bar(int i) { +} + +int main() { + A a; + B b; + b.bar = &bar; + + // foo is a method + (a.foo)(1); + a.foo(2); + + // bar is a function + (b.bar)(3); + (*b.bar)(3); + + return 0; +}