From 9cd903750b3a6d59abbacd6ad352ea92898e76f9 Mon Sep 17 00:00:00 2001 From: DavidKorczynski Date: Thu, 9 Jan 2025 17:56:25 +0000 Subject: [PATCH] Add C frontend tests and move CPP test data (#1944) * move tests around Signed-off-by: David Korczynski * add C test for new frontend Signed-off-by: David Korczynski --------- Signed-off-by: David Korczynski --- src/fuzz_introspector/frontends/frontend_c.py | 36 +++++++- .../source-code/c/simple-sample-1/fuzzer.c | 91 +++++++++++++++++++ .../cpp/test-project-1/sample.cpp | 0 .../source-code}/cpp/test-project-1/sample.h | 0 .../cpp/test-project-2/crossfile.cpp | 0 .../cpp/test-project-2/fuzzer.cpp | 0 .../cpp/test-project-2/recursive.cpp | 0 .../source-code}/cpp/test-project-2/sample.h | 0 .../cpp/test-project-3/deep_chain.cpp | 0 .../cpp/test-project-3/deep_chain.hpp | 0 .../cpp/test-project-4/deep_nested.cc | 0 .../cpp/test-project-4/deep_nested.hpp | 0 src/test/test_frontends_c.py | 35 +++++++ ...tter_frontend.py => test_frontends_cpp.py} | 26 +++--- 14 files changed, 174 insertions(+), 14 deletions(-) create mode 100644 src/test/data/source-code/c/simple-sample-1/fuzzer.c rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-1/sample.cpp (100%) rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-1/sample.h (100%) rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-2/crossfile.cpp (100%) rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-2/fuzzer.cpp (100%) rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-2/recursive.cpp (100%) rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-2/sample.h (100%) rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-3/deep_chain.cpp (100%) rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-3/deep_chain.hpp (100%) rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-4/deep_nested.cc (100%) rename src/test/{tree-sitter-frontend => data/source-code}/cpp/test-project-4/deep_nested.hpp (100%) create mode 100644 src/test/test_frontends_c.py rename src/test/{test_tree_sitter_frontend.py => test_frontends_cpp.py} (78%) diff --git a/src/fuzz_introspector/frontends/frontend_c.py b/src/fuzz_introspector/frontends/frontend_c.py index 165379550..580007c5f 100644 --- a/src/fuzz_introspector/frontends/frontend_c.py +++ b/src/fuzz_introspector/frontends/frontend_c.py @@ -24,7 +24,7 @@ import tree_sitter_c import yaml -from typing import Any +from typing import Any, Optional, Set logger = logging.getLogger(name=__name__) @@ -186,6 +186,40 @@ def extract_calltree(self, line_number=line_number) return line_to_print + def get_reachable_functions( + self, + source_code: Optional['SourceCodeFile'] = None, + function: Optional[str] = None, + visited_functions: Optional[set[str]] = None) -> Set[str]: + """Gets the reachable frunctions from a given function.""" + # Create calltree from a given function + # Find the function in the source code + if not visited_functions: + visited_functions = set() + + if not function: + return visited_functions + + if function in visited_functions: + return visited_functions + + if not source_code: + source_code = self.find_source_with_func_def(function) + + if not source_code: + return visited_functions + + func = source_code.get_function_node(function) + if not func: + return visited_functions + + callsites = func.callsites() + visited_functions.add(function) + for cs, _ in callsites: + visited_functions = self.get_reachable_functions( + function=cs, visited_functions=visited_functions) + return visited_functions + def find_source_with_func_def(self, target_function_name): """Finds the source code with a given function.""" source_codes_with_target = [] diff --git a/src/test/data/source-code/c/simple-sample-1/fuzzer.c b/src/test/data/source-code/c/simple-sample-1/fuzzer.c new file mode 100644 index 000000000..15ff60e3c --- /dev/null +++ b/src/test/data/source-code/c/simple-sample-1/fuzzer.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +int unreached_target2(const uint8_t *data) { + return 5; +} + +char *global1 = "FUZZCAFE"; +char *global2 = "FUZZKEYWORD"; +int GLB2 = 0xbeef; + +void unreached_target10(char *val) { + if (strcmp(val, global1) == 0) { + printf("Compare 1\n"); + } + if (strcmp(val, global2) == 0) { + printf("Compare 15\n"); + } + if (((int*)val) == GLB2) { + printf("Compare 3\n"); + } + if (strcmp(val, "RABBIT") == 0) { + printf("Compare 4\n"); + } + printf("Compare 2\n"); +} + +int unreached_target1(const uint8_t *data) { + if (data[0] == 0x11) { + return unreached_target2(data); + } + char *mc = (char*)malloc(12); + if (data[0] == 0x12) { + unreached_target10((char*)data); + return 0; + } + return 5; +} + +int un(char *n1, char *n2, char *n3, char *n5, size_t s1) { + return 0; +} + +int unreached_target3(const uint8_t *data, size_t *theval) { + if (data[0] == 0x11) { + return unreached_target1(data); + } + + return 5; +} + +char *d = "sf"; +int target2(const uint8_t *data) { + if (data[0] == 0x41) return 1; + unreached_target1(d); + return 2; +} + +int target3(const uint8_t *data) { + if (data[0] == 0x42) return 4; + return 3; +} + +int fuzz_entry(const uint8_t *data, size_t size) { + int ret; + if (size == 2) { + ret = target2(data); + } + else if (size == 3) { + ret = target3(data); + } + else { + ret = 1; + } + return ret; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size < 10) { + return 0; + } + char *kldfj = (char*)malloc(123); + char *nt = malloc(size+1); + memcpy(nt, data, size); + nt[size] = '\0'; + fuzz_entry(nt, size); + return 0; +} + diff --git a/src/test/tree-sitter-frontend/cpp/test-project-1/sample.cpp b/src/test/data/source-code/cpp/test-project-1/sample.cpp similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-1/sample.cpp rename to src/test/data/source-code/cpp/test-project-1/sample.cpp diff --git a/src/test/tree-sitter-frontend/cpp/test-project-1/sample.h b/src/test/data/source-code/cpp/test-project-1/sample.h similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-1/sample.h rename to src/test/data/source-code/cpp/test-project-1/sample.h diff --git a/src/test/tree-sitter-frontend/cpp/test-project-2/crossfile.cpp b/src/test/data/source-code/cpp/test-project-2/crossfile.cpp similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-2/crossfile.cpp rename to src/test/data/source-code/cpp/test-project-2/crossfile.cpp diff --git a/src/test/tree-sitter-frontend/cpp/test-project-2/fuzzer.cpp b/src/test/data/source-code/cpp/test-project-2/fuzzer.cpp similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-2/fuzzer.cpp rename to src/test/data/source-code/cpp/test-project-2/fuzzer.cpp diff --git a/src/test/tree-sitter-frontend/cpp/test-project-2/recursive.cpp b/src/test/data/source-code/cpp/test-project-2/recursive.cpp similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-2/recursive.cpp rename to src/test/data/source-code/cpp/test-project-2/recursive.cpp diff --git a/src/test/tree-sitter-frontend/cpp/test-project-2/sample.h b/src/test/data/source-code/cpp/test-project-2/sample.h similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-2/sample.h rename to src/test/data/source-code/cpp/test-project-2/sample.h diff --git a/src/test/tree-sitter-frontend/cpp/test-project-3/deep_chain.cpp b/src/test/data/source-code/cpp/test-project-3/deep_chain.cpp similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-3/deep_chain.cpp rename to src/test/data/source-code/cpp/test-project-3/deep_chain.cpp diff --git a/src/test/tree-sitter-frontend/cpp/test-project-3/deep_chain.hpp b/src/test/data/source-code/cpp/test-project-3/deep_chain.hpp similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-3/deep_chain.hpp rename to src/test/data/source-code/cpp/test-project-3/deep_chain.hpp diff --git a/src/test/tree-sitter-frontend/cpp/test-project-4/deep_nested.cc b/src/test/data/source-code/cpp/test-project-4/deep_nested.cc similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-4/deep_nested.cc rename to src/test/data/source-code/cpp/test-project-4/deep_nested.cc diff --git a/src/test/tree-sitter-frontend/cpp/test-project-4/deep_nested.hpp b/src/test/data/source-code/cpp/test-project-4/deep_nested.hpp similarity index 100% rename from src/test/tree-sitter-frontend/cpp/test-project-4/deep_nested.hpp rename to src/test/data/source-code/cpp/test-project-4/deep_nested.hpp diff --git a/src/test/test_frontends_c.py b/src/test/test_frontends_c.py new file mode 100644 index 000000000..d87a24eec --- /dev/null +++ b/src/test/test_frontends_c.py @@ -0,0 +1,35 @@ +# Copyright 2025 Fuzz Introspector Authors +# +# 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. +"""Unit testing script for the C frontend""" + +from fuzz_introspector.frontends import oss_fuzz # noqa: E402 + + +def test_simple_sample1(): + callsites, project = oss_fuzz.analyse_folder( + language='c', + directory='src/test/data/source-code/c/simple-sample-1/', + entrypoint='LLVMFuzzerTestOneInput', + ) + + functions_reached = project.get_reachable_functions( + source_code=None, + function='LLVMFuzzerTestOneInput', + visited_functions=set()) + + assert 'target3' in functions_reached + assert 'unreached_target3' not in functions_reached + + # Project check + assert len(project.get_source_codes_with_harnesses()) == 1 diff --git a/src/test/test_tree_sitter_frontend.py b/src/test/test_frontends_cpp.py similarity index 78% rename from src/test/test_tree_sitter_frontend.py rename to src/test/test_frontends_cpp.py index 924147f27..4ceae32d1 100644 --- a/src/test/test_tree_sitter_frontend.py +++ b/src/test/test_frontends_cpp.py @@ -11,7 +11,7 @@ # 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. -"""Unit testing script for tree-sitter-frontend.""" +"""Unit testing script for the CPP frontend""" from fuzz_introspector.frontends import oss_fuzz # noqa: E402 @@ -19,7 +19,7 @@ def test_tree_sitter_cpp_sample1(): callsites, project = oss_fuzz.analyse_folder( 'c++', - 'src/test/tree-sitter-frontend/cpp/test-project-1', + 'src/test/data/source-code/cpp/test-project-1', 'LLVMFuzzerTestOneInput', dump_output=False, ) @@ -30,35 +30,35 @@ def test_tree_sitter_cpp_sample1(): # Callsite check assert len(callsites[0].split('\n')) == 6 assert (' isPositive ' - 'src/test/tree-sitter-frontend/cpp/test-project-1/sample.cpp' + 'src/test/data/source-code/cpp/test-project-1/sample.cpp' in callsites[0]) def test_tree_sitter_cpp_sample2(): callsites, project = oss_fuzz.analyse_folder( 'c++', - 'src/test/tree-sitter-frontend/cpp/test-project-2', + 'src/test/data/source-code/cpp/test-project-2', 'LLVMFuzzerTestOneInput', dump_output=False, ) - # Project check + # Project checkdata/source-code assert len(project.get_source_codes_with_harnesses()) == 1 # Callsite check assert len(callsites[0].split('\n')) == 13 assert (' RecursiveNamespace::fibonacci ' - 'src/test/tree-sitter-frontend/cpp/test-project-2/recursive.cpp' + 'src/test/data/source-code/cpp/test-project-2/recursive.cpp' in callsites[0]) assert (' File2Namespace::functionInFile2 ' - 'src/test/tree-sitter-frontend/cpp/test-project-2/crossfile.cpp' + 'src/test/data/source-code/cpp/test-project-2/crossfile.cpp' in callsites[0]) def test_tree_sitter_cpp_sample3(): callsites, project = oss_fuzz.analyse_folder( 'c++', - 'src/test/tree-sitter-frontend/cpp/test-project-3', + 'src/test/data/source-code/cpp/test-project-3', 'LLVMFuzzerTestOneInput', dump_output=False, ) @@ -69,17 +69,17 @@ def test_tree_sitter_cpp_sample3(): # Callsite check assert len(callsites[0].split('\n')) == 14 assert (' std::reverse ' - 'src/test/tree-sitter-frontend/cpp/test-project-3/deep_chain.cpp' + 'src/test/data/source-code/cpp/test-project-3/deep_chain.cpp' in callsites[0]) assert (' DeepNamespace::level5 ' - 'src/test/tree-sitter-frontend/cpp/test-project-3/deep_chain.cpp' + 'src/test/data/source-code/cpp/test-project-3/deep_chain.cpp' in callsites[0]) def test_tree_sitter_cpp_sample4(): callsites, project = oss_fuzz.analyse_folder( 'c++', - 'src/test/tree-sitter-frontend/cpp/test-project-4', + 'src/test/data/source-code/cpp/test-project-4', 'LLVMFuzzerTestOneInput', dump_output=False, ) @@ -90,7 +90,7 @@ def test_tree_sitter_cpp_sample4(): # Callsite check assert len(callsites[0].split('\n')) == 7 assert (' Level1::Level2::Level3::Level4::DeepClass::deepMethod2 ' - 'src/test/tree-sitter-frontend/cpp/test-project-4/deep_nested.cc' + 'src/test/data/source-code/cpp/test-project-4/deep_nested.cc' in callsites[0]) @@ -98,7 +98,7 @@ def test_frontend_reachability1(): """Test reachability of a nested namespace.""" _, project = oss_fuzz.analyse_folder( 'c++', - 'src/test/tree-sitter-frontend/cpp/test-project-4', + 'src/test/data/source-code/cpp/test-project-4', 'LLVMFuzzerTestOneInput', dump_output=False, )