Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Frontend] Generalise project class #1990

Merged
merged 5 commits into from
Jan 17, 2025
Merged
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
65 changes: 65 additions & 0 deletions src/fuzz_introspector/frontends/datatypes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# 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.
#
################################################################################

from typing import Any, Optional


class Project():
"""Wrapper for doing analysis of a collection of source files."""

def __init__(self, source_code_files: list[Any]):
self.source_code_files = source_code_files

def dump_module_logic(self,
report_name: str,
entry_function: str = '',
harness_name: str = '',
harness_source: str = '',
dump_output: bool = True):
"""Dumps the data for the module in full."""
# Dummy function for subclasses
pass

def extract_calltree(self,
source_file: str = '',
source_code: Optional[Any] = None,
function: Optional[str] = None,
visited_functions: Optional[set[str]] = None,
depth: int = 0,
line_number: int = -1,
other_props: Optional[dict[str, Any]] = None) -> str:
"""Extracts calltree string of a calltree so that FI core can use it."""
# Dummy function for subclasses
return ''

def get_reachable_functions(
self,
source_file: str = '',
source_code: Optional[Any] = None,
function: Optional[str] = None,
visited_functions: Optional[set[str]] = None) -> set[str]:
"""Get a list of reachable functions for a provided function name."""
# Dummy function for subclasses
return set()

def get_source_codes_with_harnesses(self) -> list[Any]:
"""Gets the source codes that holds libfuzzer harnesses."""
harnesses = []
for source_code in self.source_code_files:
if source_code.has_libfuzzer_harness():
harnesses.append(source_code)

return harnesses
39 changes: 18 additions & 21 deletions src/fuzz_introspector/frontends/frontend_c.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,24 @@

from typing import Any, Optional, Set

from fuzz_introspector.frontends.datatypes import Project

logger = logging.getLogger(name=__name__)

tree_sitter_languages = {'c': Language(tree_sitter_c.language())}

language_parsers = {'c': Parser(Language(tree_sitter_c.language()))}


class Project():
class CProject(Project):
"""Wrapper for doing analysis of a collection of source files."""

def __init__(self, source_code_files):
self.source_code_files = source_code_files

def dump_module_logic(self,
report_name,
entry_function: str = '',
harness_source: str = ''):
harness_name: str = '',
harness_source: str = '',
dump_output: bool = True):
"""Dumps the data for the module in full."""
logger.info('Dumping project-wide logic.')
report: dict[str, Any] = {'report': 'name'}
Expand Down Expand Up @@ -117,16 +118,9 @@ def dump_module_logic(self,
report['All functions']['Elements'] = function_list
report['included-header-files'] = list(included_header_files)

with open(report_name, 'w', encoding='utf-8') as f:
f.write(yaml.dump(report))

def get_source_codes_with_harnesses(self):
"""Gets the source codes that holds libfuzzer harnesses."""
harnesses = []
for source_code in self.source_code_files:
if source_code.has_libfuzzer_harness():
harnesses.append(source_code)
return harnesses
if dump_output:
with open(report_name, 'w', encoding='utf-8') as f:
f.write(yaml.dump(report))

def get_source_code_with_target(self, target_func_name):
for source_code in self.source_code_files:
Expand All @@ -137,11 +131,13 @@ def get_source_code_with_target(self, target_func_name):
return None

def extract_calltree(self,
source_code=None,
function=None,
visited_functions=None,
depth=0,
line_number=-1):
source_file: str = '',
source_code: Optional[Any] = None,
function: Optional[str] = None,
visited_functions: Optional[set[str]] = None,
depth: int = 0,
line_number: int = -1,
other_props: Optional[dict[str, Any]] = None) -> str:
"""Extracts calltree string of a calltree so that FI core can use it."""
# Create calltree from a given function
# Find the function in the source code
Expand Down Expand Up @@ -185,7 +181,8 @@ def extract_calltree(self,

def get_reachable_functions(
self,
source_code: Optional['SourceCodeFile'] = None,
source_file: str = '',
source_code: Optional[Any] = None,
function: Optional[str] = None,
visited_functions: Optional[set[str]] = None) -> Set[str]:
"""Gets the reachable frunctions from a given function."""
Expand Down
43 changes: 20 additions & 23 deletions src/fuzz_introspector/frontends/frontend_cpp.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
#
################################################################################

from typing import Any, Optional, Set, List
from typing import Any, Optional

import os
import logging
Expand All @@ -23,6 +23,8 @@
import tree_sitter_cpp
import yaml

from fuzz_introspector.frontends.datatypes import Project

logger = logging.getLogger(name=__name__)
LOG_FMT = '%(asctime)s.%(msecs)03d %(levelname)s %(module)s - %(funcName)s: %(message)s'

Expand Down Expand Up @@ -570,16 +572,18 @@ def extract_callsites(self, project):
self.detailed_callsites.append({'Src': src_loc, 'Dst': dst})


class Project():
class CppProject(Project):
"""Wrapper for doing analysis of a collection of source files."""

def __init__(self, source_code_files: list[SourceCodeFile]):
self.source_code_files = source_code_files
self.all_functions: List[FunctionDefinition] = []
super().__init__(source_code_files)
self.all_functions: list[FunctionDefinition] = []

def dump_module_logic(self,
report_name: str,
harness_name: Optional[str] = None,
entry_function: str = '',
harness_name: str = '',
harness_source: str = '',
dump_output=True):
"""Dumps the data for the module in full."""
logger.info('Dumping project-wide logic.')
Expand Down Expand Up @@ -652,14 +656,6 @@ def dump_module_logic(self,
with open(report_name, 'w', encoding='utf-8') as f:
f.write(yaml.dump(report))

def get_source_codes_with_harnesses(self) -> list[SourceCodeFile]:
"""Gets the source codes that holds libfuzzer harnesses."""
harnesses = []
for source_code in self.source_code_files:
if source_code.has_libfuzzer_harness():
harnesses.append(source_code)
return harnesses

def get_function_from_name(self, function_name):
for func in self.all_functions:
if func.name == function_name:
Expand All @@ -668,12 +664,13 @@ def get_function_from_name(self, function_name):
return None

def extract_calltree(self,
source_file: str,
source_code: Optional[SourceCodeFile] = None,
source_file: str = '',
source_code: Optional[Any] = None,
function: Optional[str] = None,
visited_functions: Optional[set[str]] = None,
depth: int = 0,
line_number: int = -1) -> str:
line_number: int = -1,
other_props: Optional[dict[str, Any]] = None) -> str:
"""Extracts calltree string of a calltree so that FI core can use it."""
# Create calltree from a given function
# Find the function in the source code
Expand Down Expand Up @@ -744,11 +741,12 @@ def extract_calltree(self,
logger.debug('Done')
return line_to_print

def get_reachable_functions(self,
source_code: Optional[SourceCodeFile] = None,
function: Optional[str] = None,
visited_functions: Optional[set[str]] = None,
depth: int = 0) -> Set[str]:
def get_reachable_functions(
self,
source_file: str = '',
source_code: Optional[Any] = 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
Expand Down Expand Up @@ -789,7 +787,6 @@ def get_reachable_functions(self,
source_code=source_code,
function=cs,
visited_functions=visited_functions,
depth=depth + 1,
)

return visited_functions
Expand Down Expand Up @@ -923,7 +920,7 @@ def analyse_source_code(source_content: str) -> SourceCodeFile:


def get_function_node(target_name: str,
function_list: List[FunctionDefinition],
function_list: list[FunctionDefinition],
one_layer_only: bool = False,
namespace: str = '') -> Optional[FunctionDefinition]:
"""Helper to retrieve the RustFunction object of a function."""
Expand Down
37 changes: 17 additions & 20 deletions src/fuzz_introspector/frontends/frontend_go.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
################################################################################
"""Fuzz Introspector Light frontend for Go"""

from typing import Optional
from typing import Any, Optional

import logging

from tree_sitter import Language, Parser, Node
import tree_sitter_go
import yaml

from typing import Any
from fuzz_introspector.frontends.datatypes import Project

logger = logging.getLogger(name=__name__)

Expand Down Expand Up @@ -178,11 +178,12 @@ def get_entry_function_name(self) -> str:
return ''


class Project():
class GoProject(Project):
"""Wrapper for doing analysis of a collection of source files."""

def __init__(self, source_code_files: list[SourceCodeFile]):
self.source_code_files = source_code_files
super().__init__(source_code_files)

full_functions_methods = [
item for src in source_code_files
for item in src.functions + src.methods
Expand All @@ -195,7 +196,9 @@ def __init__(self, source_code_files: list[SourceCodeFile]):
def dump_module_logic(self,
report_name: str,
entry_function: str = '',
harness_source: str = ''):
harness_name: str = '',
harness_source: str = '',
dump_output: bool = True):
"""Dumps the data for the module in full."""
logger.info('Dumping project-wide logic.')
report: dict[str, Any] = {'report': 'name'}
Expand Down Expand Up @@ -265,24 +268,18 @@ def dump_module_logic(self,
report['All functions'] = {}
report['All functions']['Elements'] = function_list

with open(report_name, 'w', encoding='utf-8') as f:
f.write(yaml.dump(report))

def get_source_codes_with_harnesses(self) -> list[SourceCodeFile]:
"""Gets the source codes that holds libfuzzer harnesses."""
harnesses = []
for source_code in self.source_code_files:
if source_code.has_libfuzzer_harness():
harnesses.append(source_code)
return harnesses
if dump_output:
with open(report_name, 'w', encoding='utf-8') as f:
f.write(yaml.dump(report))

def extract_calltree(self,
source_file: str,
source_code: Optional[SourceCodeFile] = None,
source_file: str = '',
source_code: Optional[Any] = None,
function: Optional[str] = None,
visited_functions: Optional[set[str]] = None,
depth: int = 0,
line_number: int = -1) -> str:
line_number: int = -1,
other_props: Optional[dict[str, Any]] = None) -> str:
"""Extracts calltree string of a calltree so that FI core can use it."""
if not visited_functions:
visited_functions = set()
Expand Down Expand Up @@ -329,8 +326,8 @@ def extract_calltree(self,

def get_reachable_functions(
self,
source_file: str,
source_code: Optional[SourceCodeFile] = None,
source_file: str = '',
source_code: Optional[Any] = None,
function: Optional[str] = None,
visited_functions: Optional[set[str]] = None) -> set[str]:
"""Get a list of reachable functions for a provided function name."""
Expand Down
Loading
Loading