diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7bf1ea8..41c5b14 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -81,3 +81,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## 20230522
- Fix error in generator
- Add generation for pugi::char_t *&
+
+## 20230711
+- Support generation fuzz driver for Natch data: https://github.com/thientc/Futag-tests/tree/main/Natch
+
+## 20230807
+- Optimize ConsumerBuilder
+- Add example for context-generation https://github.com/thientc/Futag-tests/tree/main/json-c-contexts
\ No newline at end of file
diff --git a/futag-work.png b/futag-work.png
index 15333bc..eaab03e 100644
Binary files a/futag-work.png and b/futag-work.png differ
diff --git a/src/Checkers/lib/FutagAnalyzer.cpp b/src/Checkers/lib/FutagAnalyzer.cpp
index 0ba04a8..805d37f 100644
--- a/src/Checkers/lib/FutagAnalyzer.cpp
+++ b/src/Checkers/lib/FutagAnalyzer.cpp
@@ -2,7 +2,7 @@
* @file FutagAnalyzer.cpp
* @author Tran Chi Thien (thientcgithub@gmail.com)
* @brief
- * @version 2.0.4
+ * @version 2.0.5
* @date 2023-04-17
*
* @copyright Copyright (c) 2023
diff --git a/src/Checkers/lib/FutagConsumerAnalyzer.cpp b/src/Checkers/lib/FutagConsumerAnalyzer.cpp
index 3a96a1d..bb80eee 100644
--- a/src/Checkers/lib/FutagConsumerAnalyzer.cpp
+++ b/src/Checkers/lib/FutagConsumerAnalyzer.cpp
@@ -2,7 +2,7 @@
* @file FutagConsumerAnalyzer.cpp
* @author Tran Chi Thien
* @brief
- * @version 2.0.4
+ * @version 2.0.5
* @date 2023-04-17
*
* @copyright Copyright (c) 2023
@@ -516,6 +516,10 @@ void FutagConsumerAnalyzer::AnalyzeVisitedFunctionDecl(
llvm::outs() << "[Futag]: Print contexts\n";
// Build the CFG of current function
CFG *cfg = Mgr.getCFG(func);
+ //To speed up the analyzer for normal computer, we analyze only simple functions with number of block in CFG <= 30
+ if(cfg->size() > 30) {
+ return ;
+ }
if (!cfg) {
llvm::outs() << "-- Empty CFG for function: "
<< func->getNameAsString() << "\n";
diff --git a/src/clang/lib/Futag/Basic.cpp b/src/clang/lib/Futag/Basic.cpp
index 1ac3811..56434e8 100755
--- a/src/clang/lib/Futag/Basic.cpp
+++ b/src/clang/lib/Futag/Basic.cpp
@@ -13,7 +13,7 @@
* a tool of ISP RAS *
************************************************
*
- * @version 2.0.4
+ * @version 2.0.5
* @date 2023-04-17
*
* @copyright This file is distributed under the GPL v3 license
diff --git a/src/clang/lib/Futag/ConsumerFinder.cpp b/src/clang/lib/Futag/ConsumerFinder.cpp
index e3dcb76..4a75704 100644
--- a/src/clang/lib/Futag/ConsumerFinder.cpp
+++ b/src/clang/lib/Futag/ConsumerFinder.cpp
@@ -2,7 +2,7 @@
* @file ConsumerFinder.cpp
* @author Tran Chi Thien
* @brief This file contains functions for analyzing consumer program
- * @version 2.0.4
+ * @version 2.0.5
* @date 2023-04-17
*
* @copyright Copyright (c) 2023
diff --git a/src/clang/lib/Futag/MatchFinder.cpp b/src/clang/lib/Futag/MatchFinder.cpp
index 6743fc0..14fa67e 100644
--- a/src/clang/lib/Futag/MatchFinder.cpp
+++ b/src/clang/lib/Futag/MatchFinder.cpp
@@ -2,7 +2,7 @@
* @file MatchFinder.cpp
* @author Tran Chi Thien
* @brief
- * @version 2.0.4
+ * @version 2.0.5
* @date 2023-04-17
*
* @copyright Copyright (c) 2023
diff --git a/src/python/futag-package/README.md b/src/python/futag-package/README.md
index 286b138..4ea6177 100644
--- a/src/python/futag-package/README.md
+++ b/src/python/futag-package/README.md
@@ -20,7 +20,7 @@ This python package is for building library, generating and compiling fuzz-drive
## 1. Install
```bash
-pip install dist/futag-2.0.4.tar.gz
+pip install dist/futag-2.0.5.tar.gz
```
## 2. Preprocessor
diff --git a/src/python/futag-package/dist/futag-2.0.4-py3-none-any.whl b/src/python/futag-package/dist/futag-2.0.4-py3-none-any.whl
deleted file mode 100644
index 96b7bee..0000000
Binary files a/src/python/futag-package/dist/futag-2.0.4-py3-none-any.whl and /dev/null differ
diff --git a/src/python/futag-package/dist/futag-2.0.4.tar.gz b/src/python/futag-package/dist/futag-2.0.4.tar.gz
deleted file mode 100644
index 5cf6a1f..0000000
Binary files a/src/python/futag-package/dist/futag-2.0.4.tar.gz and /dev/null differ
diff --git a/src/python/futag-package/dist/futag-2.0.5-py3-none-any.whl b/src/python/futag-package/dist/futag-2.0.5-py3-none-any.whl
new file mode 100644
index 0000000..f5c7083
Binary files /dev/null and b/src/python/futag-package/dist/futag-2.0.5-py3-none-any.whl differ
diff --git a/src/python/futag-package/dist/futag-2.0.5.tar.gz b/src/python/futag-package/dist/futag-2.0.5.tar.gz
new file mode 100644
index 0000000..89caab9
Binary files /dev/null and b/src/python/futag-package/dist/futag-2.0.5.tar.gz differ
diff --git a/src/python/futag-package/setup.cfg b/src/python/futag-package/setup.cfg
index a081c30..14a6b1b 100644
--- a/src/python/futag-package/setup.cfg
+++ b/src/python/futag-package/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = futag
-version = 2.0.4
+version = 2.0.5
author = Futag-team of ISP RAS
author_email = thientcgithub@gmail.com
description = Python package of Futag
diff --git a/src/python/futag-package/setup.py b/src/python/futag-package/setup.py
index 7024d57..05d163a 100644
--- a/src/python/futag-package/setup.py
+++ b/src/python/futag-package/setup.py
@@ -2,7 +2,7 @@
setup(
name='futag',
- version='2.0.4',
+ version='2.0.5',
author='Futag-team of ISP RAS',
author_email='thientcgithub@gmail.com',
packages=['futag'],
diff --git a/src/python/futag-package/src/futag.egg-info/PKG-INFO b/src/python/futag-package/src/futag.egg-info/PKG-INFO
index 23dbc9b..e31a2f1 100644
--- a/src/python/futag-package/src/futag.egg-info/PKG-INFO
+++ b/src/python/futag-package/src/futag.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: futag
-Version: 2.0.4
+Version: 2.0.5
Summary: Futag tools for creating fuzz targets of software library
Home-page: https://github.com/ispras/Futag/tree/main/src/python/futag-package
Author: Futag-team of ISP RAS
@@ -36,7 +36,7 @@ This python package is for building library, generating and compiling fuzz-drive
## 1. Install
```bash
-pip install dist/futag-2.0.4.tar.gz
+pip install dist/futag-2.0.5.tar.gz
```
## 2. Preprocessor
diff --git a/src/python/futag-package/src/futag/fuzzer.py b/src/python/futag-package/src/futag/fuzzer.py
index 13934e1..c397658 100644
--- a/src/python/futag-package/src/futag/fuzzer.py
+++ b/src/python/futag-package/src/futag/fuzzer.py
@@ -578,7 +578,9 @@ def fuzz(self, extra_param: str = ""):
for func_dir in generated_functions:
self.backtraces = []
fuzz_driver_dirs = [x for x in func_dir.iterdir() if x.is_dir()]
+ # print(func_dir.as_posix())
for dir in fuzz_driver_dirs:
+ # print(dir.as_posix())
for x in [t for t in dir.glob("*.out") if t.is_file()]:
print("\n-- [Futag] FUZZING driver: " + x.stem + "... \n")
my_env = os.environ.copy()
@@ -676,20 +678,831 @@ def fuzz(self, extra_param: str = ""):
llvm_cov.as_posix(),
"report",
x.as_posix(),
- "-instr-profile=" + x.as_posix() + ".profdata",
+ "-instr-profile",
+ x.as_posix() + ".profdata",
+ "--object",
+ x.as_posix(),
+ ]
+ if self.debug:
+ print(" ".join(llvm_cov_report))
+ p = run(llvm_cov_report)
+
+ # llvm_cov_show = [
+ # llvm_cov.as_posix(),
+ # "show",
+ # x.as_posix(),
+ # "-instr-profile=" + x.as_posix() + ".profdata",
+ # ]
+ llvm_cov_show = [
+ llvm_cov.as_posix(),
+ "show",
+ x.as_posix(),
+ "-format=html",
+ "-instr-profile",
+ x.as_posix() + ".profdata",
+ "--object",
+ x.as_posix(),
+ ]
+
+ # cov_filename = x.as_posix() + ".cov"
+ # cov_file = open(cov_filename, "w")
+ # p = Popen(
+ # llvm_cov_show,
+ # stdout=cov_file,
+ # stderr=PIPE,
+ # universal_newlines=True,
+ # env=my_env,
+ # )
+ # output, errors = p.communicate()
+ # cov_file.close()
+ cov_filename = x.as_posix() + ".html"
+ cov_file = open(cov_filename, "w")
+ p = Popen(
+ llvm_cov_show,
+ stdout=cov_file,
+ stderr=PIPE,
+ universal_newlines=True,
+ env=my_env,
+ )
+ output, errors = p.communicate()
+ cov_file.close()
+ if self.coverage:
+ profdata_files = [x.as_posix() for x in self.fuzz_driver_path.glob("**/*.profraw") if x.is_file()]
+ object_list = [x.as_posix()[:-8] for x in self.fuzz_driver_path.glob("**/*.profraw") if x.is_file()]
+ object_files =[]
+ for o in object_list:
+ object_files += ["-object", o]
+
+ llvm_profdata = self.futag_llvm_package / "bin/llvm-profdata"
+ llvm_profdata_command = [
+ llvm_profdata.as_posix(),
+ "merge",
+ "-sparse"
+ ] + profdata_files + [
+ "-o",
+ (self.fuzz_driver_path / "futag-fuzz-result.profdata").as_posix(),
+ ]
+ if self.debug:
+ print(" ".join(llvm_profdata_command))
+ p = call(
+ llvm_profdata_command,
+ stdout=PIPE,
+ stderr=PIPE,
+ )
+
+ llvm_cov = self.futag_llvm_package / "bin/llvm-cov"
+ llvm_cov_report = [
+ llvm_cov.as_posix(),
+ "report",
+ ]+ object_files + [
+ "-instr-profile=" + (self.fuzz_driver_path / "futag-fuzz-result.profdata").as_posix()
+ ]
+ if self.debug:
+ print(" ".join(llvm_cov_report))
+ cov_report_filename = (self.fuzz_driver_path / "futag-coverage-report.txt").as_posix()
+ cov_report_file = open(cov_report_filename, "w")
+ p = Popen(
+ llvm_cov_report,
+ stdout=cov_report_file,
+ stderr=PIPE,
+ )
+
+
+ llvm_cov_show = [
+ llvm_cov.as_posix(),
+ "show",
+ "-format=html",
+ "-instr-profile=" + (self.fuzz_driver_path / "futag-fuzz-result.profdata").as_posix(),
+ ] + object_files
+
+ cov_filename = (self.fuzz_driver_path / "futag-coverage-result.html").as_posix()
+ cov_file = open(cov_filename, "w")
+ p = Popen(
+ llvm_cov_show,
+ stdout=cov_file,
+ stderr=PIPE,
+ )
+ if self.debug:
+ print(" ".join(llvm_cov_show))
+
+ template_file = self.futag_llvm_package / "svres-tmpl/svres.tmpl"
+ warning_info_text = ""
+ warning_info_path = Path.cwd().absolute() / "warning_info.svres"
+ warning_info_ex_text = ""
+ warning_info_ex_path = Path.cwd().absolute() / "warning_info_ex.svres"
+
+ if warning_info_path.exists() and warning_info_ex_path.exists():
+ with open("warning_info.svres", "r") as warning_info:
+ warning_info_text = warning_info.read()
+ with open("warning_info_ex.svres", "r") as warning_info_ex:
+ warning_info_ex_text = warning_info_ex.read()
+ with template_file.open() as tmpl:
+ lines = tmpl.read()
+ lines = lines.replace("WARNING_INFO", warning_info_text)
+ lines = lines.replace(
+ "WARNINGINFO_EXPLAINATION", warning_info_ex_text)
+ warning_info_path.unlink()
+ warning_info_ex_path.unlink()
+ with open((self.fuzz_driver_path / "futag.svres").as_posix(), "w") as svres:
+ svres.write(lines)
+ print("-- [Futag] Please import file ", (self.fuzz_driver_path /
+ "futag.svres").as_posix(), " to Svace project to view result!")
+ print("============ FINISH ============")
+
+class NatchFuzzer:
+ """Futag Fuzzer for Natch"""
+
+ def __init__(self, futag_llvm_package: str, fuzz_driver_path: str = FUZZ_DRIVER_PATH, debug: bool = False, gdb: bool = False, svres: bool = False, fork: int = 1, totaltime: int = 300, timeout: int = 10, memlimit: int = 2048, coverage: bool = False, leak: bool = False, introspect: bool = False):
+ """_summary_
+
+ Args:
+ futag_llvm_package (str): path to the futag llvm package (with binaries, scripts, etc)
+ fuzz_driver_path (str, optional): location of fuzz-drivers, default "futag-fuzz-drivers". Defaults to FUZZ_DRIVER_PATH.
+ debug (bool, optional): print debug infomation while fuzzing, default False. Defaults to False.
+ gdb (bool, optional): debug crashes with GDB, default False. Defaults to False.
+ svres (bool, optional): generate svres file for Svace (if you have Svace), default False. Defaults to False.
+ fork (int, optional): fork mode of libFuzzer (https://llvm.org/docs/LibFuzzer.html#fork-mode). Defaults to 1 - no fork mode.
+ totaltime (int, optional): total time of fuzzing one fuzz-driver, default 300 seconds. Defaults to 300.
+ timeout (int, optional): if an fuzz-drive takes longer than this timeout, the process is treated as a failure case. Defaults to 10.
+ memlimit (int, optional): option for rss_limit_mb of libFuzzer - Memory usage limit in Mb, 0 - disable the limit. Defaults to 2048.
+ coverage (bool, optional): option for showing coverage of fuzzing. Defaults to False.
+ leak (bool, optional): detecting memory leak, default False. Defaults to False.
+ introspect (bool, optional): option for integrate with fuzz-introspector (to be add soon). Defaults to False.
+ """
+
+ self.futag_llvm_package = futag_llvm_package
+ self.fuzz_driver_path = fuzz_driver_path
+
+ if Path(self.futag_llvm_package).exists():
+ self.futag_llvm_package = Path(self.futag_llvm_package).absolute()
+ else:
+ sys.exit(INVALID_FUTAG_PATH)
+
+ if Path(self.fuzz_driver_path).exists():
+ self.fuzz_driver_path = Path(self.fuzz_driver_path).absolute()
+ else:
+ sys.exit(INVALID_FUZZ_DRIVER_PATH)
+
+ self.svres = svres
+ self.leak = leak
+ self.debug = debug
+ self.gdb = gdb
+ if self.gdb and which("gdb") is None:
+ sys.exit(GDB_NOT_FOUND)
+
+ self.fork = fork
+ self.timeout = timeout
+ self.totaltime = totaltime
+ self.memlimit = memlimit
+ self.coverage = coverage
+ self.introspect = introspect
+ self.backtraces = [] # backtraces list
+ # Set for backtrace's hashes. If current backtrace's hash is not in set then add this backtrace to backtraces list, otherwise this backtrace will be passed
+ self.backtrace_hashes = (
+ set()
+ )
+
+ def __get_id_from_error(self, error_string):
+ error_id = 0
+ for c in error_string:
+ error_id += ord(c)
+ return str(error_id)
+
+ def __Printer(self, data):
+ sys.stdout.write("\r\x1b[K" + data.__str__())
+ sys.stdout.flush()
+
+ def __futag_escape(self, str):
+ str = str.replace("&", "&")
+ str = str.replace("<", "<")
+ str = str.replace(">", ">")
+ str = str.replace('"', """)
+ str = str.replace("\n", " ")
+ return str
+
+ def __get_backtrace_hash(self, backtrace):
+ '''
+ # Format of backtrace:
+ # backtrace= {
+ # "warnClass" : warnClass,
+ # "warnID": md5(warnClass+msg),
+ # "msg" : msg,
+ # "crash_line" : crash_line,
+ # "crash_file" : crash_file,
+ # "role_traces" : [{
+ # "role": role,
+ # "stack": {
+ # "function": trace.group(2),
+ # "file": trace.group(3),
+ # "location" : {
+ # "line": location.group(1),
+ # "col" : location.group(2)
+ # },
+ # "info" : ""
+ # }
+ # }]
+ # }
+ #
+ # HASH = warnID + role_traces["stack"]["file"] + role_traces["stack"]["location"]["line"] + role_traces["stack"]["file"]["col"]
+ '''
+ input_str = ""
+ for r in backtrace["role_traces"]:
+ for s in r["stack"]:
+ input_str += (
+ str(s["file"]) + str(s["location"]["line"]) +
+ str(s["location"]["col"])
+ )
+ return hash(str(backtrace["warnID"]) + input_str)
+
+ def __libFuzzerLog_parser(self, fuzz_driver: str, libFuzzer_log: str, gdb: bool = False):
+ """_summary_
+
+ Args:
+ fuzz_driver (str): path to the fuzz-driver
+ libFuzzer_log (str): path of libFuzzer log
+ gdb (bool, optional): option for parsing with GDB. Defaults to False.
+ """
+
+ # Thank https://regex101.com/
+ # match_error = "^==\d*==ERROR: (\w*): (.*)$"
+ match_error = "^==\d*==ERROR: (\w*): (.*) on.*$"
+ match_libFuzzer = "^==\d*== ERROR: (\w*): (.*)$"
+ match_summary = "^SUMMARY: \w*: (.*)$"
+ match_traceback = (
+ "^ *#(\d*) \d.\w* in ([\w:_\[\]()&<> *,]*) ([\/\w\d\-._]*):(\d*:?\d*)$"
+ )
+ match_tracepass = "^ *#(\d*) \d.\w* in ([\w:_\[\]()&<> *,]*) ([\(\)+\/\w\d\-._]*)$"
+ match_location = "(\d*):(\d*)"
+ match_exc_trace = "^.*\/llvm-11.1.0\/.*$"
+ match_exc_trace2 = "^.*libc-start.c.*$"
+ match_exc_trace3 = "^.*compiler-rt/lib/.*$"
+ match_exc_trace4 = "^.*LLVMFuzzerTestOneInput.*$"
+ # match_artifacts = "^artifact_prefix.*Test unit written to (.*)$"
+ match_artifacts = "^Running: (.*)$"
+ match_oom = "out-of-memory"
+
+ backtrace = {}
+ parsing_error = False
+ stack = []
+ info = ""
+ warnClass = ""
+ msg = ""
+ role_traces = []
+ role = ""
+ crash_file = ""
+ crash_line = 0
+ artifact_file = ""
+ with open(libFuzzer_log, "r", errors="ignore") as f:
+ lines = f.readlines()
+ if self.gdb:
+ print("-- [Futag] crash log:\n", "".join(lines))
+ for l in lines:
+ artifact = re.match(match_artifacts, l)
+ if artifact:
+ artifact_file = artifact.group(1)
+ error = re.match(match_error, l)
+ # if not error:
+ # error = re.match(match_libFuzzer, l)
+ if error:
+ parsing_error = True
+ warnClass = error.group(1)
+ msg = error.group(2)
+ continue
+ summary = re.match(match_summary, l)
+ if summary:
+ parsing_error = False
+ if role_traces:
+ backtrace = {
+ "warnClass": warnClass,
+ "warnID": self.__get_id_from_error(
+ warnClass + msg + crash_file + str(crash_line)
+ ),
+ "msg": msg,
+ "crash_line": crash_line,
+ "crash_file": crash_file,
+ "role_traces": role_traces,
+ }
+ crash_file = ""
+ crash_line = 0
+ role_traces = []
+ if parsing_error:
+ trace = re.match(match_traceback, l)
+ if trace:
+ if re.match(match_exc_trace, l):
+ continue
+ if re.match(match_exc_trace2, l):
+ continue
+ if re.match(match_exc_trace3, l):
+ continue
+ # if re.match(match_exc_trace4, l):
+ # continue
+ location = re.match(match_location, trace.group(4))
+ if location:
+ if not crash_line:
+ crash_line = location.group(1)
+ location = {"line": location.group(
+ 1), "col": location.group(2)}
+ else:
+ location = {"line": trace.group(4), "col": "0"}
+ if not crash_line:
+ crash_line = trace.group(4)
+ if not crash_file:
+ crash_file = trace.group(3)
+ stack.insert(
+ 0,
+ {
+ "function": trace.group(2),
+ "file": trace.group(3),
+ "location": location,
+ "info": "",
+ },
+ )
+ info = "Next: "
+ else:
+ if re.match(match_tracepass, l):
+ continue
+ empty_line = re.match("^$", l)
+ if not empty_line:
+ role = l
+ else:
+ if stack:
+ role_traces.append({"role": role, "stack": stack})
+ stack = []
+ role = ""
+ if not backtrace:
+ return
+ if gdb:
+ """
+ Execute gdb for 3 times:
+ - First time for setting breakpoints and output all args, variables
+ - Second time for getting type of args, variables
+ - Third time for getting value
+ """
+
+ match_variable = "^([a-zA-Z_0-9]*) = .*$"
+ match_empty = "^(.*) = 0x[0-9]$"
+ match_full_ff = "^(.*) = 0x[0-9]$"
+ match_error_gdb = "^([a-zA-Z_0-9]*) = .*('
+ )
+
+ for r in backtrace["role_traces"]:
+ loc_info = ""
+ for s in r["stack"]:
+ loc_info += (
+ ''
+ )
+ curren_explanation += (
+ ''
+ + loc_info
+ + ""
+ )
+ with open("warning_info_ex.svres", "a") as warning_info_ex:
+ warning_info_ex.write(
+ ''
+ + curren_explanation
+ + '.comment.statusDefault'
+ )
+ os.system("rm -f values_*")
+ os.system("rm -f types_*")
+ os.system("rm -f trace_*")
+
+ def fuzz(self, extra_param: str = ""):
+ """ helper for automatic fuzzing
+
+ Args:
+ extra_param (str, optional): Extra params for fuzzing. Defaults to "".
+ """
+ symbolizer = self.futag_llvm_package / "bin/llvm-symbolizer"
+ generated_functions = [
+ x for x in (self.fuzz_driver_path / "succeeded").iterdir() if x.is_dir()]
+ # for dir in generated_functions:
+ for func_dir in generated_functions:
+ self.backtraces = []
+ fuzz_driver_dirs = [x for x in func_dir.iterdir() if x.is_dir()]
+ # print(func_dir.as_posix())
+ for dir in fuzz_driver_dirs:
+ # print(dir.as_posix())
+ for x in [t for t in dir.glob("*.out") if t.is_file()]:
+ print("\n-- [Futag] FUZZING driver: " + x.stem + "... \n")
+ my_env = os.environ.copy()
+ if not self.leak:
+ my_env["ASAN_OPTIONS"] = "detect_leaks=0"
+
+ my_env["ASAN_SYMBOLIZER_PATH"] = symbolizer.as_posix()
+ if self.coverage:
+ my_env["LLVM_PROFILE_FILE"] = x.as_posix() + ".profraw"
+ if self.fork > 1:
+ # 1. Execute binary with -fork=4 -ignore_crashes=1 -max_total_time=10
+ # 2. Find all crash-* leak-* ... in artifact folder
+ # 3. Execute binary with these artifacts and save to log
+ # 4. With received log, parse to get traceback
+ # 5. Debug with GDB
+ execute_command = [
+ x.as_posix(),
+ (x.parents[3]/ "Natch_corpus" / x.parents[1].stem.replace("anonymous_", "")).as_posix(),
+ "-fork=" + str(self.fork),
+ "-ignore_crashes=1",
+ "-timeout=" + str(self.timeout),
+ "-rss_limit_mb=" + str(self.memlimit),
+ "-max_total_time=" + str(self.totaltime),
+ "-artifact_prefix=" + dir.as_posix() + "/",
+ ]
+ else:
+ execute_command = [
+ x.as_posix(),
+ (x.parents[3]/ "Natch_corpus" / x.parents[1].stem.replace("anonymous_", "")).as_posix(),
+ "-timeout=" + str(self.timeout),
+ "-rss_limit_mb=" + str(self.memlimit),
+ "-max_total_time=" + str(self.totaltime),
+ "-artifact_prefix=" + dir.as_posix() + "/",
+ ]
+ if extra_param:
+ execute_command = execute_command + extra_param.split(" ")
+ if self.debug:
+ print("-- [Futag] FUZZING command:" +
+ " ".join(execute_command))
+ p = call(
+ execute_command,
+ stdout=PIPE,
+ stderr=PIPE,
+ universal_newlines=True,
+ env=my_env,
+ )
+
+ # 2. Find all crash-* leak-* ... in artifact folder
+ crashes_files = [x for x in dir.glob(
+ "**/crash-*") if x.is_file()]
+ for cr in crashes_files:
+ getlog_command = [x.as_posix(), cr.as_posix()]
+ crashlog_filename = dir.as_posix() + "/" + cr.stem + ".log"
+ crashlog_file = open(crashlog_filename, "w")
+ p = Popen(
+ getlog_command,
+ stdout=PIPE,
+ stderr=crashlog_file,
+ universal_newlines=True,
+ env=my_env,
+ )
+ output, errors = p.communicate()
+ crashlog_file.close()
+ if self.gdb:
+ print(
+ "-- [Futag]: Parsing crashes with GDB: ", x.as_posix())
+ self.__libFuzzerLog_parser(
+ x.as_posix(), crashlog_filename, True)
+ else:
+ print(
+ "-- [Futag]: Parsing crash without GDB: ", x.as_posix())
+ self.__libFuzzerLog_parser(
+ x.as_posix(), crashlog_filename, False)
+
+ if self.coverage:
+ llvm_profdata = self.futag_llvm_package / "bin/llvm-profdata"
+ llvm_profdata_command = [
+ llvm_profdata.as_posix(),
+ "merge",
+ "-sparse",
+ x.as_posix() + ".profraw",
+ "-o",
+ x.as_posix() + ".profdata",
+ ]
+ if self.debug:
+ print(" ".join(llvm_profdata_command))
+ p = call(
+ llvm_profdata_command,
+ stdout=PIPE,
+ stderr=PIPE,
+ universal_newlines=True,
+ env=my_env,
+ )
+
+ llvm_cov = self.futag_llvm_package / "bin/llvm-cov"
+ llvm_cov_report = [
+ llvm_cov.as_posix(),
+ "report",
+ x.as_posix(),
+ "-instr-profile",
+ x.as_posix() + ".profdata",
+ "--object",
+ x.as_posix(),
]
if self.debug:
print(" ".join(llvm_cov_report))
p = run(llvm_cov_report)
+ # llvm_cov_show = [
+ # llvm_cov.as_posix(),
+ # "show",
+ # x.as_posix(),
+ # "-instr-profile=" + x.as_posix() + ".profdata",
+ # ]
llvm_cov_show = [
llvm_cov.as_posix(),
"show",
x.as_posix(),
- "-instr-profile=" + x.as_posix() + ".profdata",
+ "-format=html",
+ "-instr-profile",
+ x.as_posix() + ".profdata",
+ "--object",
+ x.as_posix(),
]
- cov_filename = x.as_posix() + ".cov"
+ # cov_filename = x.as_posix() + ".cov"
+ # cov_file = open(cov_filename, "w")
+ # p = Popen(
+ # llvm_cov_show,
+ # stdout=cov_file,
+ # stderr=PIPE,
+ # universal_newlines=True,
+ # env=my_env,
+ # )
+ # output, errors = p.communicate()
+ # cov_file.close()
+ cov_filename = x.as_posix() + ".html"
cov_file = open(cov_filename, "w")
p = Popen(
llvm_cov_show,
@@ -781,4 +1594,5 @@ def fuzz(self, extra_param: str = ""):
svres.write(lines)
print("-- [Futag] Please import file ", (self.fuzz_driver_path /
"futag.svres").as_posix(), " to Svace project to view result!")
- print("============ FINISH ============")
\ No newline at end of file
+ print("============ FINISH ============")
+
diff --git a/src/python/futag-package/src/futag/generator.py b/src/python/futag-package/src/futag/generator.py
index c1857c9..40cbe7d 100644
--- a/src/python/futag-package/src/futag/generator.py
+++ b/src/python/futag-package/src/futag/generator.py
@@ -27,6 +27,7 @@
# import shutil
from distutils.dir_util import copy_tree
+
class Generator:
"""Futag Generator"""
@@ -83,7 +84,6 @@ def __init__(self, futag_llvm_package: str, library_root: str, target_type: int
sys.exit(INVALID_TARGET_TYPE)
self.target_type = target_type
-
if pathlib.Path(self.futag_llvm_package).exists():
self.futag_llvm_package = pathlib.Path(
@@ -92,10 +92,10 @@ def __init__(self, futag_llvm_package: str, library_root: str, target_type: int
sys.exit(INVALID_FUTAG_PATH)
if self.target_type == LIBFUZZER:
- if not pathlib.Path(self.futag_llvm_package/ "bin/clang").exists():
+ if not pathlib.Path(self.futag_llvm_package / "bin/clang").exists():
sys.exit(INVALID_FUTAG_PATH)
else:
- if not pathlib.Path(self.futag_llvm_package/ "AFLplusplus/usr/local/bin/afl-clang-fast").exists():
+ if not pathlib.Path(self.futag_llvm_package / "AFLplusplus/usr/local/bin/afl-clang-fast").exists():
sys.exit(INVALID_FUTAG_PATH)
if pathlib.Path(self.library_root).exists():
@@ -188,7 +188,7 @@ def __get_compile_command(self, file):
"location": command["directory"]
}
else:
- if file.split(".")[-1] == "c":
+ if file.split(".")[-1] == "c":
return {
"compiler": "CC",
"command": "",
@@ -301,13 +301,16 @@ def __gen_builtin(self, param_name, gen_type_info):
return {
"gen_lines": [
"//GEN_BUILTIN\n",
- gen_type_info["type_name"].replace("(anonymous namespace)::","") + " " + param_name + ";\n",
+ gen_type_info["type_name"].replace(
+ "(anonymous namespace)::", "") + " " + param_name + ";\n",
"memcpy(&"+param_name+", futag_pos, sizeof(" +
- gen_type_info["type_name"].replace("(anonymous namespace)::","") + "));\n",
- "futag_pos += sizeof(" + gen_type_info["type_name"].replace("(anonymous namespace)::","") + ");\n"
+ gen_type_info["type_name"].replace(
+ "(anonymous namespace)::", "") + "));\n",
+ "futag_pos += sizeof(" + gen_type_info["type_name"].replace(
+ "(anonymous namespace)::", "") + ");\n"
],
"gen_free": [],
- "buffer_size": ["sizeof(" + gen_type_info["type_name"].replace("(anonymous namespace)::","")+")"]
+ "buffer_size": ["sizeof(" + gen_type_info["type_name"].replace("(anonymous namespace)::", "")+")"]
}
def __gen_strsize(self, param_name, param_type, dyn_size_idx, array_name):
@@ -339,14 +342,18 @@ def __gen_cstring(self, param_name, gen_type_info, dyn_cstring_size_idx):
gen_lines = [
"//GEN_CSTRING1\n",
- gen_type_info["base_type_name"] + " " + ref_name + \
- " = (" + gen_type_info["base_type_name"] + \
- ") malloc((dyn_cstring_size[" + str(dyn_cstring_size_idx - 1) + "] + 1)* sizeof(char));\n",
+ gen_type_info["base_type_name"] + " " + ref_name +
+ " = (" + gen_type_info["base_type_name"] +
+ ") malloc((dyn_cstring_size[" +
+ str(dyn_cstring_size_idx - 1) + "] + 1)* sizeof(char));\n",
"memset(" + ref_name +
- ", 0, dyn_cstring_size[" + str(dyn_cstring_size_idx - 1) + "] + 1);\n",
+ ", 0, dyn_cstring_size[" +
+ str(dyn_cstring_size_idx - 1) + "] + 1);\n",
"memcpy(" + ref_name +
- ", futag_pos, dyn_cstring_size[" + str(dyn_cstring_size_idx - 1) + "]);\n",
- "futag_pos += dyn_cstring_size[" + str(dyn_cstring_size_idx - 1) + "];\n",
+ ", futag_pos, dyn_cstring_size[" +
+ str(dyn_cstring_size_idx - 1) + "]);\n",
+ "futag_pos += dyn_cstring_size[" +
+ str(dyn_cstring_size_idx - 1) + "];\n",
]
if (gen_type_info["local_qualifier"]):
gen_lines += [gen_type_info["type_name"] +
@@ -362,7 +369,7 @@ def __gen_cstring(self, param_name, gen_type_info, dyn_cstring_size_idx):
],
"buffer_size": []
}
-
+
def __gen_wstring(self, param_name, gen_type_info, dyn_wstring_size_idx):
"""Declare and assign value for a C string type
@@ -380,14 +387,18 @@ def __gen_wstring(self, param_name, gen_type_info, dyn_wstring_size_idx):
gen_lines = [
"//GEN_WSTRING\n",
- gen_type_info["base_type_name"] + " " + ref_name + \
- " = (" + gen_type_info["base_type_name"] + \
- ") malloc((dyn_wstring_size[" + str(dyn_wstring_size_idx - 1) + "] + 1)* sizeof(wchar_t));\n",
+ gen_type_info["base_type_name"] + " " + ref_name +
+ " = (" + gen_type_info["base_type_name"] +
+ ") malloc((dyn_wstring_size[" + str(dyn_wstring_size_idx -
+ 1) + "] + 1)* sizeof(wchar_t));\n",
"memset(" + ref_name +
- ", 0, (dyn_wstring_size[" + str(dyn_wstring_size_idx - 1) + "] + 1)* sizeof(wchar_t));\n",
+ ", 0, (dyn_wstring_size[" + str(dyn_wstring_size_idx -
+ 1) + "] + 1)* sizeof(wchar_t));\n",
"memcpy(" + ref_name +
- ", futag_pos, dyn_wstring_size[" + str(dyn_wstring_size_idx - 1) + "]* sizeof(wchar_t));\n",
- "futag_pos += dyn_wstring_size[" + str(dyn_wstring_size_idx - 1) + "]* sizeof(wchar_t);\n",
+ ", futag_pos, dyn_wstring_size[" +
+ str(dyn_wstring_size_idx - 1) + "]* sizeof(wchar_t));\n",
+ "futag_pos += dyn_wstring_size[" +
+ str(dyn_wstring_size_idx - 1) + "]* sizeof(wchar_t);\n",
]
if (gen_type_info["local_qualifier"]):
gen_lines += [gen_type_info["type_name"] +
@@ -427,8 +438,10 @@ def __gen_cxxstring(self, param_name, gen_type_info, dyn_cxxstring_size_idx):
return {
"gen_lines": [
gen_type_info["type_name"] + " " + param_name +
- "(futag_pos, dyn_cxxstring_size[" + str(dyn_cxxstring_size_idx - 1) + "]); \n",
- "futag_pos += dyn_cxxstring_size[" + str(dyn_cxxstring_size_idx - 1) + "];\n",
+ "(futag_pos, dyn_cxxstring_size[" +
+ str(dyn_cxxstring_size_idx - 1) + "]); \n",
+ "futag_pos += dyn_cxxstring_size[" +
+ str(dyn_cxxstring_size_idx - 1) + "];\n",
],
"gen_free": [],
"buffer_size": []
@@ -580,7 +593,7 @@ def __gen_struct(self, struct_name, struct, gen_info):
self.dyn_cstring_size_idx += 1
curr_gen = self.__gen_cstring(
curr_name, gen_type_info, self.dyn_cstring_size_idx)
- #reinit value of curr_name to send reference of string
+ # reinit value of curr_name to send reference of string
# curr_name = "&" + curr_name # string_prefix
buffer_size += curr_gen["buffer_size"]
gen_lines += curr_gen["gen_lines"]
@@ -719,9 +732,11 @@ def __gen_class(self, param_name, class_record):
def __gen_input_file(self, param_name, gen_type_info):
cur_gen_free = [" " + x for x in self.gen_free]
if gen_type_info["gen_type"] == GEN_CSTRING:
- line = "const char* " + param_name + " = \"futag_input_file_" + str(self.file_idx - 1) + "\";\n"
+ line = "const char* " + param_name + \
+ " = \"futag_input_file_" + str(self.file_idx - 1) + "\";\n"
elif gen_type_info["gen_type"] == GEN_WSTRING:
- line = "const wchar_t * " + param_name + " = L\"futag_input_file_" + str(self.file_idx - 1) + "\";\n"
+ line = "const wchar_t * " + param_name + \
+ " = L\"futag_input_file_" + str(self.file_idx - 1) + "\";\n"
else:
return {
"gen_lines": [],
@@ -759,7 +774,8 @@ def __gen_file_descriptor(self, param_name, gen_type_info):
"const char* " + param_name + "_tmp" + str(self.file_idx) + " = \"futag_input_file_" +
str(self.file_idx - 1) + "\";\n",
"FILE * fp_" + str(self.file_idx - 1) +
- " = fopen(" + param_name + "_tmp" + str(self.file_idx) + ",\"w\");\n",
+ " = fopen(" + param_name + "_tmp" +
+ str(self.file_idx) + ",\"w\");\n",
"if (fp_" + str(self.file_idx - 1) + " == NULL) {\n",
]
gen_lines += cur_gen_free
@@ -770,7 +786,9 @@ def __gen_file_descriptor(self, param_name, gen_type_info):
"], fp_" + str(self.file_idx - 1) + ");\n",
"fclose(fp_" + str(self.file_idx - 1) + ");\n",
"futag_pos += file_size[" + str(self.file_idx - 1) + "];\n",
- gen_type_info["type_name"] + " " + param_name + "= open(" + param_name + "_tmp" + str(self.file_idx) + ", O_RDWR);\n"
+ gen_type_info["type_name"] + " " + param_name +
+ "= open(" + param_name + "_tmp" +
+ str(self.file_idx) + ", O_RDWR);\n"
]
gen_free = ["close(" + param_name + ");\n"]
return {
@@ -864,7 +882,8 @@ def __gen_var_function(self, func_param_name: str, func):
if gen_type_info["gen_type"] == GEN_BUILTIN:
this_gen_size = False
if arg["param_usage"] in ["FILE_DESCRIPTOR"]:
- curr_name = "fd_" + curr_name + str(self.file_idx) # string_prefix
+ curr_name = "fd_" + curr_name + \
+ str(self.file_idx) # string_prefix
self.file_idx += 1
curr_gen = self.__gen_file_descriptor(
curr_name, gen_type_info)
@@ -903,28 +922,32 @@ def __gen_var_function(self, func_param_name: str, func):
if gen_type_info["gen_type"] == GEN_CSTRING:
# GEN FILE NAME OR # GEN STRING
if (arg["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or arg["param_name"] in ["filename", "file", "filepath"] or arg["param_name"].find('file') != -1 or arg["param_name"].find('File') != -1) and len(arg["gen_list"]) == 1:
- curr_name = "f_" + curr_name + str(self.file_idx) # string_prefix
+ curr_name = "f_" + curr_name + \
+ str(self.file_idx) # string_prefix
self.file_idx += 1
curr_gen = self.__gen_input_file(
curr_name, gen_type_info)
else:
- curr_name = "str_" + curr_name + str(self.dyn_cstring_size_idx) # string_prefix
+ curr_name = "str_" + curr_name + \
+ str(self.dyn_cstring_size_idx) # string_prefix
self.dyn_cstring_size_idx += 1
curr_gen = self.__gen_cstring(
curr_name, gen_type_info, self.dyn_cstring_size_idx)
gen_dict["buffer_size"] += curr_gen["buffer_size"]
gen_dict["gen_lines"] += curr_gen["gen_lines"]
gen_dict["gen_free"] += curr_gen["gen_free"]
-
+
if gen_type_info["gen_type"] == GEN_REFSTRING:
# GEN FILE NAME OR # GEN STRING
if (arg["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or arg["param_name"] in ["filename", "file", "filepath"] or arg["param_name"].find('file') != -1 or arg["param_name"].find('File') != -1) and len(arg["gen_list"]) == 1:
- curr_name = "f_" + curr_name + str(self.file_idx) # string_prefix
+ curr_name = "f_" + curr_name + \
+ str(self.file_idx) # string_prefix
self.file_idx += 1
curr_gen = self.__gen_input_file(
curr_name, gen_type_info)
else:
- curr_name = "str_" + curr_name + str(self.dyn_cstring_size_idx) # string_prefix
+ curr_name = "str_" + curr_name + \
+ str(self.dyn_cstring_size_idx) # string_prefix
self.dyn_cstring_size_idx += 1
curr_gen = self.__gen_cstring(
curr_name, gen_type_info, self.dyn_cstring_size_idx)
@@ -932,16 +955,18 @@ def __gen_var_function(self, func_param_name: str, func):
gen_dict["buffer_size"] += curr_gen["buffer_size"]
gen_dict["gen_lines"] += curr_gen["gen_lines"]
gen_dict["gen_free"] += curr_gen["gen_free"]
-
+
if gen_type_info["gen_type"] == GEN_WSTRING:
# GEN FILE NAME OR # GEN STRING
if (arg["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or arg["param_name"] in ["filename", "file", "filepath"] or arg["param_name"].find('file') != -1 or arg["param_name"].find('File') != -1) and len(arg["gen_list"]) == 1:
- curr_name = "f_" + curr_name + str(self.file_idx) # string_prefix
+ curr_name = "f_" + curr_name + \
+ str(self.file_idx) # string_prefix
self.file_idx += 1
curr_gen = self.__gen_input_file(
curr_name, gen_type_info)
else:
- curr_name = "str_" + curr_name + str(self.dyn_wstring_size_idx) # string_prefix
+ curr_name = "str_" + curr_name + \
+ str(self.dyn_wstring_size_idx) # string_prefix
self.dyn_wstring_size_idx += 1
curr_gen = self.__gen_wstring(
curr_name, gen_type_info, self.dyn_wstring_size_idx)
@@ -951,14 +976,16 @@ def __gen_var_function(self, func_param_name: str, func):
if gen_type_info["gen_type"] == GEN_CXXSTRING:
- if (arg["param_name"] in ["filename", "file", "filepath", "path"] or arg["param_name"].find('file') != -1 or arg["param_name"].find('File') != -1 or arg["param_name"].find('path') != -1) and len(arg["gen_list"]) == 1:
- curr_name = "f_" + curr_name + str(self.file_idx) # string_prefix
+ if (arg["param_name"] in ["filename", "file", "filepath", "path"] or arg["param_name"].find('file') != -1 or arg["param_name"].find('File') != -1 or arg["param_name"].find('path') != -1) and len(arg["gen_list"]) == 1:
+ curr_name = "f_" + curr_name + \
+ str(self.file_idx) # string_prefix
self.file_idx += 1
curr_gen = self.__gen_input_file(
curr_name, gen_type_info)
else:
- curr_name = "str_" + curr_name + str(self.dyn_cxxstring_size_idx) # string_prefix
+ curr_name = "str_" + curr_name + \
+ str(self.dyn_cxxstring_size_idx) # string_prefix
self.dyn_cxxstring_size_idx += 1
curr_gen = self.__gen_cxxstring(
curr_name, gen_type_info, self.dyn_cxxstring_size_idx)
@@ -1046,7 +1073,8 @@ def __gen_var_function(self, func_param_name: str, func):
class_name = found_parent["qname"]
if func["func_type"] in [FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
function_call = " //declare the RECORD and call constructor\n"
- function_call +=" " + class_name.replace("::(anonymous namespace)", "") + func_param_name + "("+ ",".join(param_list)+");\n"
+ function_call += " " + class_name.replace(
+ "::(anonymous namespace)", "") + func_param_name + "(" + ",".join(param_list)+");\n"
else:
# Find default constructor
# TODO: add code for other constructors
@@ -1059,10 +1087,13 @@ def __gen_var_function(self, func_param_name: str, func):
if not found_default_constructor:
self.gen_this_function = False
function_call = " //declare the RECORD first\n"
- function_call += " " + class_name.replace("::(anonymous namespace)", "") + " " + func_param_name + ";\n"
+ function_call += " " + \
+ class_name.replace(
+ "::(anonymous namespace)", "") + " " + func_param_name + ";\n"
# call the method
function_call += " //METHOD CALL\n"
- function_call += " " + func_param_name + "." + func["name"]+"("+ ",".join(param_list)+");\n"
+ function_call += " " + func_param_name + "." + \
+ func["name"]+"(" + ",".join(param_list)+");\n"
else:
function_call = "//GEN_VAR_FUNCTION\n " + func["return_type"] + " " + func_param_name + \
@@ -1122,9 +1153,10 @@ def __wrapper_file(self, func):
"msg": "Error: File closed!"
}
return {
- "file": f,
- "msg": "Successed: " + full_path + " created!"
- }
+ "file": f,
+ "msg": "Successed: " + full_path + " created!"
+ }
+
def __anonymous_wrapper_file(self, func):
# if anonymous:
@@ -1162,11 +1194,12 @@ def __anonymous_wrapper_file(self, func):
file_name = filename + \
str(file_index) + "." + self.target_extension
- full_path_destination = (filepath / filename / dir_name / file_name).as_posix()
+ full_path_destination = (
+ filepath / filename / dir_name / file_name).as_posix()
with open(source_path, 'r') as s:
source_file = s.read()
d = open(full_path_destination, "w")
- d.write("//"+func["hash"]+ "\n")
+ d.write("//"+func["hash"] + "\n")
d.write(source_file)
d.close()
f = open(full_path_destination, 'a')
@@ -1174,7 +1207,6 @@ def __anonymous_wrapper_file(self, func):
return None
return f
-
def __log_file(self, func, anonymous: bool = False):
if anonymous:
filename = func["name"]
@@ -1234,9 +1266,12 @@ def __retrieve_old_values(self, old_values):
self.buffer_size = copy.copy(old_values["buffer_size"])
self.gen_lines = copy.copy(old_values["gen_lines"])
self.gen_free = copy.copy(old_values["gen_free"])
- self.dyn_cstring_size_idx = copy.copy(old_values["dyn_cstring_size_idx"])
- self.dyn_cxxstring_size_idx = copy.copy(old_values["dyn_cxxstring_size_idx"])
- self.dyn_wstring_size_idx = copy.copy(old_values["dyn_wstring_size_idx"])
+ self.dyn_cstring_size_idx = copy.copy(
+ old_values["dyn_cstring_size_idx"])
+ self.dyn_cxxstring_size_idx = copy.copy(
+ old_values["dyn_cxxstring_size_idx"])
+ self.dyn_wstring_size_idx = copy.copy(
+ old_values["dyn_wstring_size_idx"])
self.var_function_idx = copy.copy(old_values["var_function_idx"])
self.param_list = copy.copy(old_values["param_list"])
self.curr_func_log = copy.copy(old_values["curr_func_log"])
@@ -1244,7 +1279,6 @@ def __retrieve_old_values(self, old_values):
self.gen_this_function = copy.copy(old_values["gen_this_function"])
self.param_list = copy.copy(old_values["param_list"])
-
def __gen_anonymous_function(self, func, param_id) -> bool:
malloc_free = [
"unsigned char *",
@@ -1279,7 +1313,7 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
print(CANNOT_CREATE_WRAPPER_FILE, func["qname"])
return False
print(WRAPPER_FILE_CREATED, f.name)
-
+
for line in self.__gen_header(func["location"]["fullpath"]):
f.write("// " + line)
f.write('\n')
@@ -1294,13 +1328,16 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
else:
f.write(AFLPLUSPLUS_PREFIX)
- buffer_check = str(self.dyn_wstring_size_idx) + "*sizeof(wchar_t) + " + str(self.dyn_cxxstring_size_idx) + "*sizeof(char) + " + str(self.dyn_cstring_size_idx) + " + " + str(self.file_idx)
+ buffer_check = str(self.dyn_wstring_size_idx) + "*sizeof(wchar_t) + " + str(self.dyn_cxxstring_size_idx) + \
+ "*sizeof(char) + " + str(self.dyn_cstring_size_idx) + \
+ " + " + str(self.file_idx)
if self.buffer_size:
buffer_check += " + " + " + ".join(self.buffer_size)
f.write(" if (Fuzz_Size < " + buffer_check + ") return 0;\n")
if self.dyn_cstring_size_idx > 0:
- f.write(" size_t dyn_cstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check +" )));\n")
+ f.write(
+ " size_t dyn_cstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check + " )));\n")
f.write(" //generate random array of dynamic string sizes\n")
f.write(" size_t dyn_cstring_size[" +
str(self.dyn_cstring_size_idx) + "];\n")
@@ -1308,7 +1345,8 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
f.write(" srand(time(NULL));\n")
f.write(
" if(dyn_cstring_buffer == 0) dyn_cstring_size[0] = dyn_cstring_buffer; \n")
- f.write(" else dyn_cstring_size[0] = rand() % dyn_cstring_buffer; \n")
+ f.write(
+ " else dyn_cstring_size[0] = rand() % dyn_cstring_buffer; \n")
f.write(" size_t remain = dyn_cstring_size[0];\n")
f.write(" for(size_t i = 1; i< " +
str(self.dyn_cstring_size_idx) + " - 1; i++){\n")
@@ -1326,7 +1364,8 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
" //end of generation random array of dynamic string sizes\n")
if self.dyn_wstring_size_idx > 0:
- f.write(" size_t dyn_wstring_buffer = (size_t) ((Fuzz_Size + sizeof(wchar_t) - (" + buffer_check +" )))/sizeof(wchar_t);\n")
+ f.write(" size_t dyn_wstring_buffer = (size_t) ((Fuzz_Size + sizeof(wchar_t) - (" +
+ buffer_check + " )))/sizeof(wchar_t);\n")
f.write(" //generate random array of dynamic string sizes\n")
f.write(" size_t dyn_wstring_size[" +
str(self.dyn_wstring_size_idx) + "];\n")
@@ -1334,7 +1373,8 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
f.write(" srand(time(NULL));\n")
f.write(
" if(dyn_wstring_buffer == 0) dyn_wstring_size[0] = dyn_wstring_buffer; \n")
- f.write(" else dyn_wstring_size[0] = rand() % dyn_wstring_buffer; \n")
+ f.write(
+ " else dyn_wstring_size[0] = rand() % dyn_wstring_buffer; \n")
f.write(" size_t remain = dyn_wstring_size[0];\n")
f.write(" for(size_t i = 1; i< " +
str(self.dyn_wstring_size_idx) + " - 1; i++){\n")
@@ -1350,9 +1390,10 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
f.write(" dyn_wstring_size[0] = dyn_wstring_buffer;\n")
f.write(
" //end of generation random array of dynamic string sizes\n")
-
+
if self.dyn_cxxstring_size_idx > 0:
- f.write(" size_t dyn_cxxstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check +" )));\n")
+ f.write(
+ " size_t dyn_cxxstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check + " )));\n")
f.write(" //generate random array of dynamic string sizes\n")
f.write(" size_t dyn_cxxstring_size[" +
str(self.dyn_cxxstring_size_idx) + "];\n")
@@ -1360,7 +1401,8 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
f.write(" srand(time(NULL));\n")
f.write(
" if(dyn_cxxstring_buffer == 0) dyn_cxxstring_size[0] = dyn_cxxstring_buffer; \n")
- f.write(" else dyn_cxxstring_size[0] = rand() % dyn_cxxstring_buffer; \n")
+ f.write(
+ " else dyn_cxxstring_size[0] = rand() % dyn_cxxstring_buffer; \n")
f.write(" size_t remain = dyn_cxxstring_size[0];\n")
f.write(" for(size_t i = 1; i< " +
str(self.dyn_cxxstring_size_idx) + " - 1; i++){\n")
@@ -1373,12 +1415,14 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
f.write(
" dyn_cxxstring_size[" + str(self.dyn_cxxstring_size_idx) + " - 1] = dyn_cxxstring_buffer - remain;\n")
else:
- f.write(" dyn_cxxstring_size[0] = dyn_cxxstring_buffer;\n")
+ f.write(
+ " dyn_cxxstring_size[0] = dyn_cxxstring_buffer;\n")
f.write(
" //end of generation random array of dynamic string sizes\n")
if self.file_idx > 0:
- f.write(" size_t file_buffer = (size_t) ((Fuzz_Size + "+ str(self.file_idx) + " - (" + buffer_check +" )));\n")
+ f.write(" size_t file_buffer = (size_t) ((Fuzz_Size + " +
+ str(self.file_idx) + " - (" + buffer_check + " )));\n")
f.write(" //generate random array of dynamic file sizes\n")
f.write(" size_t file_size[" +
str(self.file_idx) + "];\n")
@@ -1408,12 +1452,13 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
f.write(" " + line)
f.write(" //" + func["qname"])
-
+
if func["func_type"] in [FUNC_CXXMETHOD, FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
class_name = found_parent["qname"]
if func["func_type"] in [FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
f.write(" //declare the RECORD and call constructor\n")
- f.write(" " + class_name.replace("::(anonymous namespace)", "") + " futag_target" + "(")
+ f.write(
+ " " + class_name.replace("::(anonymous namespace)", "") + " futag_target" + "(")
else:
# Find default constructor
# TODO: add code for other constructors
@@ -1429,7 +1474,8 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
f.close()
return False
f.write(" //declare the RECORD first\n")
- f.write(" " + class_name.replace("::(anonymous namespace)", "") + " futag_target;\n")
+ f.write(
+ " " + class_name.replace("::(anonymous namespace)", "") + " futag_target;\n")
# call the method
f.write(" //METHOD CALL\n")
f.write(" futag_target." + func["name"]+"(")
@@ -1439,7 +1485,8 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
f.write(" " + func["return_type"] +
" futag_target = " + func["qname"] + "(")
else:
- f.write(" " + func["qname"].replace("::(anonymous namespace)", "") + "(")
+ f.write(
+ " " + func["qname"].replace("::(anonymous namespace)", "") + "(")
param_list = []
for arg in self.param_list:
@@ -1482,14 +1529,15 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
if gen_type_info["gen_type"] == GEN_BUILTIN:
this_gen_size = False
if curr_param["param_usage"] in ["FILE_DESCRIPTOR"]:
- curr_name = "fd_" + curr_name + str(self.file_idx) # string_prefix
+ curr_name = "fd_" + curr_name + \
+ str(self.file_idx) # string_prefix
self.file_idx += 1
curr_gen = self.__gen_file_descriptor(
curr_name, gen_type_info)
self.__append_gen_dict(curr_gen)
break
# GEN STRING SIZE
-
+
elif param_id > 0 and (func["params"][param_id - 1]["gen_list"][0]["gen_type"] in [GEN_CSTRING, GEN_WSTRING, GEN_CXXSTRING] or curr_param["param_usage"] == "SIZE_FIELD"):
if gen_type_info["type_name"] in ["size_t", "unsigned char", "char", "int", "unsigned", "unsigned int", "short", "unsigned short", "short int", "unsigned short int"]:
dyn_size_idx = 0
@@ -1507,13 +1555,20 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
curr_gen = self.__gen_strsize(
curr_name, curr_param["param_type"], dyn_size_idx, array_name)
self.__append_gen_dict(curr_gen)
- this_gen_size = True
+ this_gen_size = True
break
if not this_gen_size:
curr_name = "b_" + curr_name # builtin_prefix
curr_gen = self.__gen_builtin(curr_name, gen_type_info)
self.__append_gen_dict(curr_gen)
+ if gen_type_info["gen_type"] == GEN_CFILE:
+ # GEN FILE NAME OR # GEN STRING
+ curr_name = "fc_" + curr_name # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(curr_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+
if gen_type_info["gen_type"] == GEN_CSTRING:
# GEN FILE NAME OR # GEN STRING
if (curr_param["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or curr_param["param_name"] in ["filename", "file", "filepath"] or curr_param["param_name"].find('file') != -1 or curr_param["param_name"].find('File') != -1) and len(curr_param["gen_list"]) == 1:
@@ -1530,7 +1585,7 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
self.__append_gen_dict(curr_gen)
if gen_type_info["gen_type"] == GEN_REFSTRING:
- print ("!!!GEN_REFSTRING\n\n\n")
+ print("!!!GEN_REFSTRING\n\n\n")
# GEN FILE NAME OR # GEN STRING
if (curr_param["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or curr_param["param_name"] in ["filename", "file", "filepath"] or curr_param["param_name"].find('file') != -1 or curr_param["param_name"].find('File') != -1) and len(curr_param["gen_list"]) == 1:
curr_name = "f_" + curr_name # string_prefix
@@ -1665,7 +1720,8 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
self.param_list += [curr_name]
curr_gen = self.__gen_var_function(
curr_name, curr_return_func["function"])
- self.__add_header(self.__get_function_header(func["location"]["fullpath"]))
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
self.__append_gen_dict(curr_gen)
#!!!call recursive
param_id += 1
@@ -1696,8 +1752,10 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
self.var_function_idx += 1
self.gen_lines += ["\n"]
self.param_list += [curr_name]
- curr_gen = self.__gen_var_function(curr_name, curr_return_func["function"])
- self.__add_header(self.__get_function_header(func["location"]["fullpath"]))
+ curr_gen = self.__gen_var_function(
+ curr_name, curr_return_func["function"])
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
self.__append_gen_dict(curr_gen)
#!!!call recursive
param_id += 1
@@ -1746,7 +1804,8 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
self.param_list += [curr_name]
curr_gen = self.__gen_var_function(
curr_name, curr_return_func["function"])
- self.__add_header(self.__get_function_header(func["location"]["fullpath"]))
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
self.__append_gen_dict(curr_gen)
#!!!call recursive
param_id += 1
@@ -1773,7 +1832,6 @@ def __gen_anonymous_function(self, func, param_id) -> bool:
param_id += 1
self.__gen_anonymous_function(func, param_id)
-
def __gen_target_function(self, func, param_id) -> bool:
malloc_free = [
"unsigned char *",
@@ -1806,14 +1864,15 @@ def __gen_target_function(self, func, param_id) -> bool:
return False
# generate file name
wrapper_result = self.__wrapper_file(func)
- print("Generating fuzzing-wapper for function ", func["qname"], ": ")
+ print("Generating fuzzing-wapper for function ",
+ func["qname"], ": ")
print("-- ", wrapper_result["msg"])
if not wrapper_result["file"]:
self.gen_this_function = False
return False
f = wrapper_result["file"]
-
- f.write("//"+func["hash"]+ "\n")
+
+ f.write("//"+func["hash"] + "\n")
for line in self.__gen_header(func["location"]["fullpath"]):
f.write(line)
f.write('\n')
@@ -1828,15 +1887,37 @@ def __gen_target_function(self, func, param_id) -> bool:
else:
f.write(AFLPLUSPLUS_PREFIX)
- # buffer_check = " if (Fuzz_Size < " + \
- # str(self.dyn_wstring_size_idx) + "*sizeof(wchar_t) +" + str(self.dyn_cxxstring_size_idx) + "*sizeof(char) + " + str(self.dyn_cstring_size_idx) + " + " + str(self.file_idx)
- buffer_check = str(self.dyn_wstring_size_idx) + "*sizeof(wchar_t) + " + str(self.dyn_cxxstring_size_idx) + "*sizeof(char) + " + str(self.dyn_cstring_size_idx) + " + " + str(self.file_idx)
+ # buffer_check = str(self.dyn_wstring_size_idx) + "*sizeof(wchar_t) + " + str(self.dyn_cxxstring_size_idx) + \
+ # "*sizeof(char) + " + str(self.dyn_cstring_size_idx) + \
+ # " + " + str(self.file_idx)
+ buffer_check_list =[]
+ wchar_t_check = ""
+ if self.dyn_wstring_size_idx > 0:
+ wchar_t_check = str(self.dyn_wstring_size_idx) + " * sizeof(wchar_t)"
+ buffer_check_list.append(wchar_t_check)
+ dyn_cstring_check = ""
+ if self.dyn_cstring_size_idx > 0:
+ dyn_cstring_check = str(self.dyn_cstring_size_idx) + " * sizeof(char)"
+ buffer_check_list.append(dyn_cstring_check)
+ file_idx_check = ""
+ if self.file_idx > 0:
+ file_idx_check = str(self.file_idx)
+ buffer_check_list.append(file_idx_check)
+
+ buffer_check = ""
if self.buffer_size:
- buffer_check += " + " + " + ".join(self.buffer_size)
+ if buffer_check_list:
+ buffer_check = "+".join(buffer_check_list) + " + " + " + ".join(self.buffer_size)
+ else:
+ buffer_check = " + ".join(self.buffer_size)
+ else:
+ buffer_check = "+".join(buffer_check_list)
+
f.write(" if (Fuzz_Size < " + buffer_check + ") return 0;\n")
if self.dyn_cstring_size_idx > 0:
- f.write(" size_t dyn_cstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check +" )));\n")
+ f.write(
+ " size_t dyn_cstring_buffer = (size_t) (Fuzz_Size + "+ str(self.dyn_cstring_size_idx) +"*sizeof(char) - (" + buffer_check + " ));\n")
f.write(" //generate random array of dynamic string sizes\n")
f.write(" size_t dyn_cstring_size[" +
str(self.dyn_cstring_size_idx) + "];\n")
@@ -1844,7 +1925,8 @@ def __gen_target_function(self, func, param_id) -> bool:
f.write(" srand(time(NULL));\n")
f.write(
" if(dyn_cstring_buffer == 0) dyn_cstring_size[0] = dyn_cstring_buffer; \n")
- f.write(" else dyn_cstring_size[0] = rand() % dyn_cstring_buffer; \n")
+ f.write(
+ " else dyn_cstring_size[0] = rand() % dyn_cstring_buffer; \n")
f.write(" size_t remain = dyn_cstring_size[0];\n")
f.write(" for(size_t i = 1; i< " +
str(self.dyn_cstring_size_idx) + " - 1; i++){\n")
@@ -1862,7 +1944,8 @@ def __gen_target_function(self, func, param_id) -> bool:
" //end of generation random array of dynamic string sizes\n")
if self.dyn_wstring_size_idx > 0:
- f.write(" size_t dyn_wstring_buffer = (size_t) ((Fuzz_Size + sizeof(wchar_t) - (" + buffer_check +" )))/sizeof(wchar_t);\n")
+ f.write(" size_t dyn_wstring_buffer = (size_t) (Fuzz_Size + " + str(self.dyn_wstring_size_idx) + "*sizeof(wchar_t) - (" +
+ buffer_check + " ))/sizeof(wchar_t);\n")
f.write(" //generate random array of dynamic string sizes\n")
f.write(" size_t dyn_wstring_size[" +
str(self.dyn_wstring_size_idx) + "];\n")
@@ -1870,7 +1953,8 @@ def __gen_target_function(self, func, param_id) -> bool:
f.write(" srand(time(NULL));\n")
f.write(
" if(dyn_wstring_buffer == 0) dyn_wstring_size[0] = dyn_wstring_buffer; \n")
- f.write(" else dyn_wstring_size[0] = rand() % dyn_wstring_buffer; \n")
+ f.write(
+ " else dyn_wstring_size[0] = rand() % dyn_wstring_buffer; \n")
f.write(" size_t remain = dyn_wstring_size[0];\n")
f.write(" for(size_t i = 1; i< " +
str(self.dyn_wstring_size_idx) + " - 1; i++){\n")
@@ -1886,9 +1970,10 @@ def __gen_target_function(self, func, param_id) -> bool:
f.write(" dyn_wstring_size[0] = dyn_wstring_buffer;\n")
f.write(
" //end of generation random array of dynamic string sizes\n")
-
+
if self.dyn_cxxstring_size_idx > 0:
- f.write(" size_t dyn_cxxstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check +" )));\n")
+ f.write(
+ " size_t dyn_cxxstring_buffer = (size_t) (Fuzz_Size - (" + buffer_check + " ));\n")
f.write(" //generate random array of dynamic string sizes\n")
f.write(" size_t dyn_cxxstring_size[" +
str(self.dyn_cxxstring_size_idx) + "];\n")
@@ -1896,7 +1981,8 @@ def __gen_target_function(self, func, param_id) -> bool:
f.write(" srand(time(NULL));\n")
f.write(
" if(dyn_cxxstring_buffer == 0) dyn_cxxstring_size[0] = dyn_cxxstring_buffer; \n")
- f.write(" else dyn_cxxstring_size[0] = rand() % dyn_cxxstring_buffer; \n")
+ f.write(
+ " else dyn_cxxstring_size[0] = rand() % dyn_cxxstring_buffer; \n")
f.write(" size_t remain = dyn_cxxstring_size[0];\n")
f.write(" for(size_t i = 1; i< " +
str(self.dyn_cxxstring_size_idx) + " - 1; i++){\n")
@@ -1909,12 +1995,14 @@ def __gen_target_function(self, func, param_id) -> bool:
f.write(
" dyn_cxxstring_size[" + str(self.dyn_cxxstring_size_idx) + " - 1] = dyn_cxxstring_buffer - remain;\n")
else:
- f.write(" dyn_cxxstring_size[0] = dyn_cxxstring_buffer;\n")
+ f.write(
+ " dyn_cxxstring_size[0] = dyn_cxxstring_buffer;\n")
f.write(
" //end of generation random array of dynamic string sizes\n")
if self.file_idx > 0:
- f.write(" size_t file_buffer = (size_t) ((Fuzz_Size + "+ str(self.file_idx) + " - (" + buffer_check +" )));\n")
+ f.write(" size_t file_buffer = (size_t) (Fuzz_Size + " +
+ str(self.file_idx) + " - (" + buffer_check + " ));\n")
f.write(" //generate random array of dynamic file sizes\n")
f.write(" size_t file_size[" +
str(self.file_idx) + "];\n")
@@ -1947,7 +2035,8 @@ def __gen_target_function(self, func, param_id) -> bool:
class_name = found_parent["qname"]
if func["func_type"] in [FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
f.write(" //declare the RECORD and call constructor\n")
- f.write(" " + class_name.replace("::(anonymous namespace)", "") + " futag_target" + "(")
+ f.write(
+ " " + class_name.replace("::(anonymous namespace)", "") + " futag_target" + "(")
else:
# Find default constructor
# TODO: add code for other constructors
@@ -2016,14 +2105,15 @@ def __gen_target_function(self, func, param_id) -> bool:
if gen_type_info["gen_type"] == GEN_BUILTIN:
this_gen_size = False
if curr_param["param_usage"] in ["FILE_DESCRIPTOR"]:
- curr_name = "fd_" + curr_name + str(self.file_idx) # string_prefix
+ curr_name = "fd_" + curr_name + \
+ str(self.file_idx) # string_prefix
self.file_idx += 1
curr_gen = self.__gen_file_descriptor(
curr_name, gen_type_info)
self.__append_gen_dict(curr_gen)
break
# GEN STRING SIZE
-
+
elif param_id > 0 and (func["params"][param_id - 1]["gen_list"][0]["gen_type"] in [GEN_CSTRING, GEN_WSTRING, GEN_CXXSTRING] or curr_param["param_usage"] == "SIZE_FIELD"):
if gen_type_info["type_name"] in ["size_t", "unsigned char", "char", "int", "unsigned", "unsigned int", "short", "unsigned short", "short int", "unsigned short int"]:
dyn_size_idx = 0
@@ -2041,7 +2131,7 @@ def __gen_target_function(self, func, param_id) -> bool:
curr_gen = self.__gen_strsize(
curr_name, curr_param["param_type"], dyn_size_idx, array_name)
self.__append_gen_dict(curr_gen)
- this_gen_size = True
+ this_gen_size = True
break
if not this_gen_size:
curr_name = "b_" + curr_name # builtin_prefix
@@ -2198,7 +2288,8 @@ def __gen_target_function(self, func, param_id) -> bool:
self.param_list += [curr_name]
curr_gen = self.__gen_var_function(
curr_name, curr_return_func["function"])
- self.__add_header(self.__get_function_header(func["location"]["fullpath"]))
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
self.__append_gen_dict(curr_gen)
#!!!call recursive
param_id += 1
@@ -2229,8 +2320,10 @@ def __gen_target_function(self, func, param_id) -> bool:
self.var_function_idx += 1
self.gen_lines += ["\n"]
self.param_list += [curr_name]
- curr_gen = self.__gen_var_function(curr_name, curr_return_func["function"])
- self.__add_header(self.__get_function_header(func["location"]["fullpath"]))
+ curr_gen = self.__gen_var_function(
+ curr_name, curr_return_func["function"])
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
self.__append_gen_dict(curr_gen)
#!!!call recursive
param_id += 1
@@ -2279,7 +2372,8 @@ def __gen_target_function(self, func, param_id) -> bool:
self.param_list += [curr_name]
curr_gen = self.__gen_var_function(
curr_name, curr_return_func["function"])
- self.__add_header(self.__get_function_header(func["location"]["fullpath"]))
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
self.__append_gen_dict(curr_gen)
#!!!call recursive
param_id += 1
@@ -2518,14 +2612,15 @@ def compile_targets(self, workers: int = 4, keep_failed: bool = False, extra_par
f for f in self.target_library['functions'] if f['qname'].replace(":", "_") == func_dir.name]
if not len(search_curr_func):
search_curr_func = [
- f for f in self.target_library['functions'] if 'anonymous_' + f['name'].replace(":", "_") == func_dir.name]
+ f for f in self.target_library['functions'] if 'anonymous_' + f['name'].replace(":", "_") == func_dir.name]
if not len(search_curr_func):
continue
current_func = search_curr_func[0]
func_file_location = current_func["location"]["fullpath"]
compiler_info = self.__get_compile_command(func_file_location)
-
- include_subdir = [] # List of Pathlib (-I parameters) in compile command.
+
+ # List of Pathlib (-I parameters) in compile command.
+ include_subdir = []
if os.path.exists(compiler_info["location"]):
current_location = os.getcwd()
@@ -2594,14 +2689,16 @@ def compile_targets(self, workers: int = 4, keep_failed: bool = False, extra_par
fuzz_driver_dirs = [x for x in func_dir.iterdir() if x.is_dir()]
for dir in fuzz_driver_dirs:
# for target_src in [t for t in dir.glob("*"+self.target_extension) if t.is_file()]:
- for target_src in [t for t in dir.glob("*") if t.is_file() and t.suffix in [".c", ".cc", ".cpp"]]:
+ for target_src in [t for t in dir.glob("*") if t.is_file() and t.suffix in [".c", ".cc", ".cpp", ".log"]]:
target_path = dir.as_posix() + "/" + target_src.stem + ".out"
error_path = dir.as_posix() + "/" + target_src.stem + ".err"
generated_targets += 1
if self.target_type == LIBFUZZER:
- compiler_cmd = [compiler_path.as_posix()] + compiler_flags_libFuzzer.split(" ") + current_include + ["-I" + x for x in extra_include.split(" ") if x.strip() ] + extra_params.split(" ") + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
+ compiler_cmd = [compiler_path.as_posix()] + compiler_flags_libFuzzer.split(" ") + current_include + ["-I" + x for x in extra_include.split(
+ " ") if x.strip()] + extra_params.split(" ") + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
else:
- compiler_cmd = [compiler_path.as_posix()] + compiler_flags_aflplusplus.split(" ") + current_include + ["-I" + x for x in extra_include.split(" ") if x.strip() ] + extra_params.split(" ") + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
+ compiler_cmd = [compiler_path.as_posix()] + compiler_flags_aflplusplus.split(" ") + current_include + ["-I" + x for x in extra_include.split(
+ " ") if x.strip()] + extra_params.split(" ") + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
compile_cmd_list.append({
"compiler_cmd": compiler_cmd,
@@ -2634,7 +2731,8 @@ def compile_targets(self, workers: int = 4, keep_failed: bool = False, extra_par
((self.succeeded_path /
dir.parents[1].name)).mkdir(parents=True, exist_ok=True)
# shutil.move(dir.parents[0].as_posix(), (self.succeeded_path / dir.parents[1].name).as_posix(), copy_function=shutil.copytree)
- copy_tree(dir.parents[0].as_posix(), (self.succeeded_path / dir.parents[1].name).as_posix())
+ copy_tree(dir.parents[0].as_posix(
+ ), (self.succeeded_path / dir.parents[1].name / dir.parents[0].name).as_posix())
if keep_failed:
failed_tree = set()
@@ -2648,12 +2746,13 @@ def compile_targets(self, workers: int = 4, keep_failed: bool = False, extra_par
if target not in failed_tree:
failed_tree.add(target)
for dir in failed_tree:
- if dir.parents[0] not in succeeded_dir :
+ if dir.parents[0] not in succeeded_dir:
if not (self.failed_path / dir.parents[1].name).exists():
((self.failed_path /
- dir.parents[1].name)).mkdir(parents=True, exist_ok=True)
+ dir.parents[1].name)).mkdir(parents=True, exist_ok=True)
# shutil.move(dir.parents[0].as_posix(), (self.failed_path / dir.parents[1].name).as_posix(), copy_function=shutil.copytree)
- copy_tree(dir.parents[0].as_posix(),(self.failed_path / dir.parents[1].name).as_posix())
+ copy_tree(dir.parents[0].as_posix(
+ ), (self.failed_path / dir.parents[1].name / dir.parents[0].name).as_posix())
else:
delete_folder(self.failed_path)
if not keep_original:
@@ -2664,7 +2763,7 @@ def compile_targets(self, workers: int = 4, keep_failed: bool = False, extra_par
+ str(len(compiled_targets_list))
+ " fuzz-driver(s)\n"
)
-
+
def gen_targets_from_callstack(self, target):
found_function = None
for func in self.target_library["functions"]:
@@ -2709,9 +2808,9 @@ def __init__(self, futag_llvm_package: str, library_root: str, target_type: int
self.consumer_contexts = None
self.total_context = []
self.header = []
-
+
self.gen_anonymous = False
- self.max_wrappers = 10
+ self.max_wrappers = 10
self.gen_this_function = True
self.gen_lines = []
self.buffer_size = []
@@ -2845,7 +2944,7 @@ def __get_compile_command(self, file):
"location": command["directory"]
}
else:
- if file.split(".")[-1] == "c":
+ if file.split(".")[-1] == "c":
return {
"compiler": "CC",
"command": "",
@@ -2996,7 +3095,8 @@ def __gen_cstring(self, param_name, gen_type_info, dyn_cstring_size_idx):
malloc = gen_type_info["base_type_name"] + " " + ref_name + \
" = (" + gen_type_info["base_type_name"] + \
- ") malloc(dyn_cstring_size[" + str(dyn_cstring_size_idx - 1) + "] + 1);\n"
+ ") malloc(dyn_cstring_size[" + \
+ str(dyn_cstring_size_idx - 1) + "] + 1);\n"
if "wchar_t" in gen_type_info["base_type_name"]:
malloc = gen_type_info["base_type_name"] + " " + ref_name + \
" = (" + gen_type_info["base_type_name"] + \
@@ -3006,10 +3106,13 @@ def __gen_cstring(self, param_name, gen_type_info, dyn_cstring_size_idx):
"//GEN_CSTRING\n",
malloc,
"memset(" + ref_name +
- ", 0, dyn_cstring_size[" + str(dyn_cstring_size_idx - 1) + "] + 1);\n",
+ ", 0, dyn_cstring_size[" +
+ str(dyn_cstring_size_idx - 1) + "] + 1);\n",
"memcpy(" + ref_name +
- ", futag_pos, dyn_cstring_size[" + str(dyn_cstring_size_idx - 1) + "] );\n",
- "futag_pos += dyn_cstring_size[" + str(dyn_cstring_size_idx - 1) + "];\n",
+ ", futag_pos, dyn_cstring_size[" +
+ str(dyn_cstring_size_idx - 1) + "] );\n",
+ "futag_pos += dyn_cstring_size[" +
+ str(dyn_cstring_size_idx - 1) + "];\n",
]
if (gen_type_info["local_qualifier"]):
gen_lines += [gen_type_info["type_name"] +
@@ -3043,14 +3146,18 @@ def __gen_wstring(self, param_name, gen_type_info, dyn_wstring_size_idx):
gen_lines = [
"//GEN_WSTRING\n",
- gen_type_info["base_type_name"] + " " + ref_name + \
- " = (" + gen_type_info["base_type_name"] + \
- ") malloc((dyn_wstring_size[" + str(dyn_wstring_size_idx - 1) + "] + 1)* sizeof(wchar_t));\n",
+ gen_type_info["base_type_name"] + " " + ref_name +
+ " = (" + gen_type_info["base_type_name"] +
+ ") malloc((dyn_wstring_size[" + str(dyn_wstring_size_idx -
+ 1) + "] + 1)* sizeof(wchar_t));\n",
"memset(" + ref_name +
- ", 0, (dyn_wstring_size[" + str(dyn_wstring_size_idx - 1) + "] + 1)* sizeof(wchar_t));\n",
+ ", 0, (dyn_wstring_size[" + str(dyn_wstring_size_idx -
+ 1) + "] + 1)* sizeof(wchar_t));\n",
"memcpy(" + ref_name +
- ", futag_pos, dyn_wstring_size[" + str(dyn_wstring_size_idx - 1) + "]* sizeof(wchar_t));\n",
- "futag_pos += dyn_wstring_size[" + str(dyn_wstring_size_idx - 1) + "]* sizeof(wchar_t);\n",
+ ", futag_pos, dyn_wstring_size[" +
+ str(dyn_wstring_size_idx - 1) + "]* sizeof(wchar_t));\n",
+ "futag_pos += dyn_wstring_size[" +
+ str(dyn_wstring_size_idx - 1) + "]* sizeof(wchar_t);\n",
]
if (gen_type_info["local_qualifier"]):
gen_lines += [gen_type_info["type_name"] +
@@ -3067,7 +3174,6 @@ def __gen_wstring(self, param_name, gen_type_info, dyn_wstring_size_idx):
"buffer_size": []
}
-
def __gen_cxxstring(self, param_name, gen_type_info, dyn_cxxstring_size_idx):
"""Declare and assign value for a C++ string type
@@ -3091,8 +3197,10 @@ def __gen_cxxstring(self, param_name, gen_type_info, dyn_cxxstring_size_idx):
return {
"gen_lines": [
gen_type_info["type_name"] + " " + param_name +
- "(futag_pos, dyn_cxxstring_size[" + str(dyn_cxxstring_size_idx - 1) + "]); \n",
- "futag_pos += dyn_cxxstring_size[" + str(dyn_cxxstring_size_idx - 1) + "];\n",
+ "(futag_pos, dyn_cxxstring_size[" +
+ str(dyn_cxxstring_size_idx - 1) + "]); \n",
+ "futag_pos += dyn_cxxstring_size[" +
+ str(dyn_cxxstring_size_idx - 1) + "];\n",
],
"gen_free": [],
"buffer_size": []
@@ -3131,7 +3239,8 @@ def __gen_enum(self, enum_record, param_name, gen_type_info, compiler_info, anon
"_enum_index, futag_pos, sizeof(unsigned int));\n",
# "enum " + enum_name + " " + param_name + " = static_cast(" + param_name + "_enum_index % " + str(enum_length) + ");\n",
+ ">(" + param_name + "_enum_index % " + \
+ str(enum_length) + ");\n",
"futag_pos += sizeof(unsigned int);\n"
],
"gen_free": [],
@@ -3406,7 +3515,7 @@ def __gen_input_file(self, param_name, gen_type_info):
"gen_free": [],
"buffer_size": []
}
-
+
def __gen_file_descriptor(self, param_name, gen_type_info):
if not "" in self.header:
self.header += [""]
@@ -3416,7 +3525,8 @@ def __gen_file_descriptor(self, param_name, gen_type_info):
"const char* " + param_name + "_tmp" + str(self.file_idx) + " = \"futag_input_file_" +
str(self.file_idx - 1) + "\";\n",
"FILE * fp_" + str(self.file_idx - 1) +
- " = fopen(" + param_name + "_tmp" + str(self.file_idx) + ",\"w\");\n",
+ " = fopen(" + param_name + "_tmp" +
+ str(self.file_idx) + ",\"w\");\n",
"if (fp_" + str(self.file_idx - 1) + " == NULL) {\n",
]
gen_lines += cur_gen_free
@@ -3427,7 +3537,9 @@ def __gen_file_descriptor(self, param_name, gen_type_info):
"], fp_" + str(self.file_idx - 1) + ");\n",
"fclose(fp_" + str(self.file_idx - 1) + ");\n",
"futag_pos += file_size[" + str(self.file_idx - 1) + "];\n",
- gen_type_info["type_name"] + " " + param_name + "= open(" + param_name + "_tmp" + str(self.file_idx) + ", O_RDWR);\n"
+ gen_type_info["type_name"] + " " + param_name +
+ "= open(" + param_name + "_tmp" +
+ str(self.file_idx) + ", O_RDWR);\n"
]
gen_free = ["close(" + param_name + ");\n"]
return {
@@ -3436,7 +3548,6 @@ def __gen_file_descriptor(self, param_name, gen_type_info):
"buffer_size": []
}
-
def __search_in_typedefs(self, type_name, typedefs):
# Are there multiple type definitions for the same data type???
result = None
@@ -3522,7 +3633,8 @@ def __gen_var_function(self, func_param_name: str, func):
if gen_type_info["gen_type"] == GEN_BUILTIN:
this_gen_size = False
if arg["param_usage"] in ["FILE_DESCRIPTOR"]:
- curr_name = "fd_" + curr_name + str(self.file_idx) # string_prefix
+ curr_name = "fd_" + curr_name + \
+ str(self.file_idx) # string_prefix
self.file_idx += 1
curr_gen = self.__gen_file_descriptor(
curr_name, gen_type_info)
@@ -3743,9 +3855,9 @@ def __wrapper_file(self, func):
"msg": "Error: File closed!"
}
return {
- "file": f,
- "msg": "Successed: " + full_path + " created!"
- }
+ "file": f,
+ "msg": "Successed: " + full_path + " created!"
+ }
def __log_file(self, func, anonymous: bool = False):
if anonymous:
@@ -3806,9 +3918,12 @@ def __retrieve_old_values(self, old_values):
self.buffer_size = copy.copy(old_values["buffer_size"])
self.gen_lines = copy.copy(old_values["gen_lines"])
self.gen_free = copy.copy(old_values["gen_free"])
- self.dyn_cstring_size_idx = copy.copy(old_values["dyn_cstring_size_idx"])
- self.dyn_cxxstring_size_idx = copy.copy(old_values["dyn_cxxstring_size_idx"])
- self.dyn_wstring_size_idx = copy.copy(old_values["dyn_wstring_size_idx"])
+ self.dyn_cstring_size_idx = copy.copy(
+ old_values["dyn_cstring_size_idx"])
+ self.dyn_cxxstring_size_idx = copy.copy(
+ old_values["dyn_cxxstring_size_idx"])
+ self.dyn_wstring_size_idx = copy.copy(
+ old_values["dyn_wstring_size_idx"])
self.var_function_idx = copy.copy(old_values["var_function_idx"])
self.param_list = copy.copy(old_values["param_list"])
self.curr_func_log = copy.copy(old_values["curr_func_log"])
@@ -3859,7 +3974,9 @@ def __gen_target_function(self, call, func, param_id) -> bool:
if func["func_type"] in [FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
gen_lines.append(
" //declare the RECORD and call constructor\n")
- func_call += " " + class_name.replace("::(anonymous namespace)", "") + " futag_target" + "("
+ func_call += " " + \
+ class_name.replace(
+ "::(anonymous namespace)", "") + " futag_target" + "("
func_call += ",".join(param_list)
func_call += ");\n"
gen_lines.append(func_call)
@@ -3966,7 +4083,8 @@ def __gen_target_function(self, call, func, param_id) -> bool:
# GEN STRING SIZE
this_gen_size = False
if curr_param["param_usage"] in ["FILE_DESCRIPTOR"]:
- curr_name = "fd_" + curr_name + str(self.file_idx) # string_prefix
+ curr_name = "fd_" + curr_name + \
+ str(self.file_idx) # string_prefix
self.file_idx += 1
curr_gen = self.__gen_file_descriptor(
curr_name, gen_type_info)
@@ -4147,7 +4265,8 @@ def __gen_target_function(self, call, func, param_id) -> bool:
self.param_list += [curr_name]
curr_gen = self.__gen_var_function(
curr_name, curr_return_func["function"])
- self.__add_header(self.__get_function_header(func["location"]["fullpath"]))
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
self.__append_gen_dict(curr_gen)
#!!!call recursive
param_id += 1
@@ -4181,7 +4300,8 @@ def __gen_target_function(self, call, func, param_id) -> bool:
self.param_list += [curr_name]
curr_gen = self.__gen_var_function(
curr_name, curr_return_func["function"])
- self.__add_header(self.__get_function_header(func["location"]["fullpath"]))
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
self.__append_gen_dict(curr_gen)
#!!!call recursive
param_id += 1
@@ -4230,7 +4350,8 @@ def __gen_target_function(self, call, func, param_id) -> bool:
self.param_list += [curr_name]
curr_gen = self.__gen_var_function(
curr_name, curr_return_func["function"])
- self.__add_header(self.__get_function_header(func["location"]["fullpath"]))
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
self.__append_gen_dict(curr_gen)
#!!!call recursive
param_id += 1
@@ -4451,9 +4572,11 @@ def compile_targets(self, workers: int = 4, keep_failed: bool = False, extra_par
error_path = dir.as_posix() + "/" + target_src.stem + ".err"
generated_targets += 1
if self.target_type == LIBFUZZER:
- compiler_cmd = [compiler_path.as_posix()] + compiler_flags_libFuzzer.split(" ") + current_include + ["-I" + x for x in extra_include.split(" ") if x.strip() ] + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
+ compiler_cmd = [compiler_path.as_posix()] + compiler_flags_libFuzzer.split(" ") + current_include + ["-I" + x for x in extra_include.split(
+ " ") if x.strip()] + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
else:
- compiler_cmd = [compiler_path.as_posix()] + compiler_flags_aflplusplus.split(" ") + current_include + ["-I" + x for x in extra_include.split(" ") if x.strip() ] + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
+ compiler_cmd = [compiler_path.as_posix()] + compiler_flags_aflplusplus.split(" ") + current_include + ["-I" + x for x in extra_include.split(
+ " ") if x.strip()] + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
compile_cmd_list.append({
"compiler_cmd": compiler_cmd,
@@ -4488,7 +4611,7 @@ def compile_targets(self, workers: int = 4, keep_failed: bool = False, extra_par
# shutil.move(dir.parents[0].as_posix(
# ), (self.succeeded_path / dir.parents[1].name).as_posix(), copy_function=shutil.copytree)
copy_tree(dir.parents[0].as_posix(
- ), (self.succeeded_path / dir.parents[1].name).as_posix())
+ ), (self.succeeded_path / dir.parents[1].name / dir.parents[0].name).as_posix())
if keep_failed:
failed_tree = set()
@@ -4502,14 +4625,14 @@ def compile_targets(self, workers: int = 4, keep_failed: bool = False, extra_par
if target not in failed_tree:
failed_tree.add(target)
for dir in failed_tree:
- if dir.parents[0] not in succeeded_dir :
+ if dir.parents[0] not in succeeded_dir:
if not (self.failed_path / dir.parents[1].name).exists():
((self.failed_path /
- dir.parents[1].name)).mkdir(parents=True, exist_ok=True)
+ dir.parents[1].name)).mkdir(parents=True, exist_ok=True)
# shutil.move(dir.parents[0].as_posix(
# ), (self.failed_path / dir.parents[1].name).as_posix(), copy_function=shutil.copytree)
copy_tree(dir.parents[0].as_posix(
- ), (self.failed_path / dir.parents[1].name).as_posix())
+ ), (self.failed_path / dir.parents[1].name / dir.parents[0].name).as_posix())
else:
delete_folder(self.failed_path)
@@ -4630,13 +4753,16 @@ def __gen_context_wrapper(self, func):
else:
f.write(AFLPLUSPLUS_PREFIX)
- buffer_check = str(self.dyn_wstring_size_idx) + "*sizeof(wchar_t) + " + str(self.dyn_cxxstring_size_idx) + "*sizeof(char) + " + str(self.dyn_cstring_size_idx) + " + " + str(self.file_idx)
+ buffer_check = str(self.dyn_wstring_size_idx) + "*sizeof(wchar_t) + " + str(self.dyn_cxxstring_size_idx) + \
+ "*sizeof(char) + " + str(self.dyn_cstring_size_idx) + \
+ " + " + str(self.file_idx)
if self.buffer_size:
buffer_check += " + " + " + ".join(self.buffer_size)
f.write(" if (Fuzz_Size < " + buffer_check + ") return 0;\n")
if self.dyn_cstring_size_idx > 0:
- f.write(" size_t dyn_cstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check +" )));\n")
+ f.write(
+ " size_t dyn_cstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check + " )));\n")
f.write(" //generate random array of dynamic string sizes\n")
f.write(" size_t dyn_cstring_size[" +
str(self.dyn_cstring_size_idx) + "];\n")
@@ -4644,7 +4770,8 @@ def __gen_context_wrapper(self, func):
f.write(" srand(time(NULL));\n")
f.write(
" if(dyn_cstring_buffer == 0) dyn_cstring_size[0] = dyn_cstring_buffer; \n")
- f.write(" else dyn_cstring_size[0] = rand() % dyn_cstring_buffer; \n")
+ f.write(
+ " else dyn_cstring_size[0] = rand() % dyn_cstring_buffer; \n")
f.write(" size_t remain = dyn_cstring_size[0];\n")
f.write(" for(size_t i = 1; i< " +
str(self.dyn_cstring_size_idx) + " - 1; i++){\n")
@@ -4662,7 +4789,8 @@ def __gen_context_wrapper(self, func):
" //end of generation random array of dynamic string sizes\n")
if self.dyn_wstring_size_idx > 0:
- f.write(" size_t dyn_wstring_buffer = (size_t) ((Fuzz_Size + sizeof(wchar_t) - (" + buffer_check +" )))/sizeof(wchar_t);\n")
+ f.write(" size_t dyn_wstring_buffer = (size_t) ((Fuzz_Size + sizeof(wchar_t) - (" +
+ buffer_check + " )))/sizeof(wchar_t);\n")
f.write(" //generate random array of dynamic string sizes\n")
f.write(" size_t dyn_wstring_size[" +
str(self.dyn_wstring_size_idx) + "];\n")
@@ -4670,7 +4798,8 @@ def __gen_context_wrapper(self, func):
f.write(" srand(time(NULL));\n")
f.write(
" if(dyn_wstring_buffer == 0) dyn_wstring_size[0] = dyn_wstring_buffer; \n")
- f.write(" else dyn_wstring_size[0] = rand() % dyn_wstring_buffer; \n")
+ f.write(
+ " else dyn_wstring_size[0] = rand() % dyn_wstring_buffer; \n")
f.write(" size_t remain = dyn_wstring_size[0];\n")
f.write(" for(size_t i = 1; i< " +
str(self.dyn_wstring_size_idx) + " - 1; i++){\n")
@@ -4686,9 +4815,10 @@ def __gen_context_wrapper(self, func):
f.write(" dyn_wstring_size[0] = dyn_wstring_buffer;\n")
f.write(
" //end of generation random array of dynamic string sizes\n")
-
+
if self.dyn_cxxstring_size_idx > 0:
- f.write(" size_t dyn_cxxstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check +" )));\n")
+ f.write(
+ " size_t dyn_cxxstring_buffer = (size_t) ((Fuzz_Size + sizeof(char) - (" + buffer_check + " )));\n")
f.write(" //generate random array of dynamic string sizes\n")
f.write(" size_t dyn_cxxstring_size[" +
str(self.dyn_cxxstring_size_idx) + "];\n")
@@ -4696,7 +4826,8 @@ def __gen_context_wrapper(self, func):
f.write(" srand(time(NULL));\n")
f.write(
" if(dyn_cxxstring_buffer == 0) dyn_cxxstring_size[0] = dyn_cxxstring_buffer; \n")
- f.write(" else dyn_cxxstring_size[0] = rand() % dyn_cxxstring_buffer; \n")
+ f.write(
+ " else dyn_cxxstring_size[0] = rand() % dyn_cxxstring_buffer; \n")
f.write(" size_t remain = dyn_cxxstring_size[0];\n")
f.write(" for(size_t i = 1; i< " +
str(self.dyn_cxxstring_size_idx) + " - 1; i++){\n")
@@ -4712,9 +4843,10 @@ def __gen_context_wrapper(self, func):
f.write(" dyn_cxxstring_size[0] = dyn_cxxstring_buffer;\n")
f.write(
" //end of generation random array of dynamic string sizes\n")
-
+
if self.file_idx > 0:
- f.write(" size_t file_buffer = (size_t) ((Fuzz_Size + "+ str(self.file_idx) + " - (" + buffer_check +" )));\n")
+ f.write(" size_t file_buffer = (size_t) ((Fuzz_Size + " +
+ str(self.file_idx) + " - (" + buffer_check + " )));\n")
f.write(" //generate random array of dynamic file sizes\n")
f.write(" size_t file_size[" +
str(self.file_idx) + "];\n")
@@ -4781,3 +4913,2557 @@ def gen_context(self, max_wrappers: int = 10):
break
self.__gen_context_wrapper(func)
+
+
+class NatchGenerator:
+ """Futag Generator for Natch"""
+
+ def __init__(self, futag_llvm_package: str, library_root: str, json_file: str, target_type: int = LIBFUZZER, output_path=FUZZ_DRIVER_PATH, build_path=BUILD_PATH, install_path=INSTALL_PATH):
+ """ Constructor of Generator class.
+
+ Args:
+ futag_llvm_package (str): path to the futag-llvm package (with binaries, scripts, etc.).
+ library_root (str): path to the library root.
+ json_file (str, optional): path to the JSON file from Natch.
+ target_type (int, optional): format of fuzz-drivers (LIBFUZZER or AFLPLUSPLUS). Defaults to LIBFUZZER.
+ output_path (_type_, optional): where to save fuzz-drivers, if this path exists, Futag will delete it and create new one. Defaults to FUZZ_DRIVER_PATH.
+ build_path (_type_, optional): path to the build directory. Defaults to BUILD_PATH.
+ install_path (_type_, optional): path to the install directory. Defaults to INSTALL_PATH.
+
+ Raises:
+ ValueError: INVALID_TARGET_TYPE: Invalid the type of target.
+ ValueError: INVALID_FUTAG_PATH: Invalid path of futag-llvm.
+ ValueError: INVALID_LIBPATH: Invalid path of library.
+ ValueError: INVALID_ANALYSIS_FILE: Invalid path to analysis result file.
+ ValueError: INVALID_BUILPATH: Invalid path to the library build path.
+ ValueError: INVALID_INSTALLPATH: Invalid path to the library install path.
+ """
+
+ self.output_path = None # Path for saving fuzzing drivers
+ self.tmp_output_path = None # Path for saving fuzzing drivers
+ self.json_file = json_file
+ self.futag_llvm_package = futag_llvm_package
+ self.library_root = library_root
+ self.target_library = None
+ self.header = []
+
+ self.gen_anonymous = False
+ self.max_wrappers = 10
+ self.gen_this_function = True
+ self.gen_lines = []
+ self.buffer_size = []
+ self.gen_free = []
+ self.dyn_cstring_size_idx = 0
+ self.dyn_cxxstring_size_idx = 0
+ self.dyn_wstring_size_idx = 0
+ self.file_idx = 0
+ self.curr_function = None
+ self.curr_func_log = ""
+ self.curr_gen_string = -1
+ self.param_list = []
+ self.var_function_idx = 0
+
+ # save the list of generated function for debugging
+ self.target_extension = ""
+ self.result_report = {}
+
+ if (target_type > 1 or target_type < 0):
+ sys.exit(INVALID_TARGET_TYPE)
+
+ self.target_type = target_type
+
+ if pathlib.Path(self.futag_llvm_package).exists():
+ self.futag_llvm_package = pathlib.Path(
+ self.futag_llvm_package).absolute()
+ else:
+ sys.exit(INVALID_FUTAG_PATH)
+
+ if self.target_type == LIBFUZZER:
+ if not pathlib.Path(self.futag_llvm_package / "bin/clang").exists():
+ sys.exit(INVALID_FUTAG_PATH)
+ else:
+ if not pathlib.Path(self.futag_llvm_package / "AFLplusplus/usr/local/bin/afl-clang-fast").exists():
+ sys.exit(INVALID_FUTAG_PATH)
+
+ if pathlib.Path(self.library_root).exists():
+ self.library_root = pathlib.Path(self.library_root).absolute()
+ else:
+ sys.exit(INVALID_LIBPATH)
+
+ if not pathlib.Path(json_file).exists():
+ sys.exit(INVALID_NATCH_JSON)
+ else:
+ self.json_file = pathlib.Path(json_file).absolute()
+
+ if (self.library_root / ANALYSIS_FILE_PATH).exists():
+ f = open((self.library_root / ANALYSIS_FILE_PATH).as_posix())
+ self.target_library = json.load(f)
+ tmp_output_path = "." + output_path
+ # create directory for function targets if not exists
+ # TODO: set option for deleting
+ if (self.library_root / output_path).exists():
+ delete_folder(self.library_root / output_path)
+ if (self.library_root / tmp_output_path).exists():
+ delete_folder(self.library_root / tmp_output_path)
+
+ simple_functions = []
+ if self.target_library["functions"]:
+ for f_iter in self.target_library["functions"]:
+ if f_iter["is_simple"]:
+ simple_functions.append(f_iter)
+ self.simple_functions = simple_functions
+ (self.library_root / output_path).mkdir(parents=True, exist_ok=True)
+ (self.library_root / tmp_output_path).mkdir(parents=True, exist_ok=True)
+ self.output_path = (self.library_root / output_path).absolute()
+ self.tmp_output_path = (
+ self.library_root / tmp_output_path).absolute()
+
+ succeeded_path = self.output_path / "succeeded"
+ if not succeeded_path.exists():
+ (succeeded_path).mkdir(parents=True, exist_ok=True)
+ self.succeeded_path = succeeded_path
+
+ failed_path = self.output_path / "failed"
+ if not failed_path.exists():
+ (failed_path).mkdir(parents=True, exist_ok=True)
+ self.failed_path = failed_path
+ else:
+ sys.exit(INVALID_ANALYSIS_FILE)
+
+ if not (self.library_root / build_path).exists():
+ sys.exit(INVALID_BUILPATH)
+ self.build_path = self.library_root / build_path
+
+ if not (self.library_root / install_path).exists():
+ sys.exit(INVALID_INSTALLPATH)
+ self.install_path = self.library_root / install_path
+
+ Natch_corpus_path = self.output_path / "Natch_corpus"
+ if not Natch_corpus_path.exists():
+ (Natch_corpus_path).mkdir(parents=True, exist_ok=True)
+ self.Natch_corpus_path = Natch_corpus_path
+
+ def parse_values(self):
+ print(self.Natch_corpus_path.as_posix())
+ natch_values = json.load(open(self.json_file.as_posix()))
+ if not natch_values:
+ raise ValueError(COULD_NOT_PARSE_NATCH_CALLSTACK)
+ function_name_list = set()
+ target_functions = []
+ for function in natch_values:
+ add_arg_list = False
+ if not function["Function name"] in function_name_list:
+ (self.Natch_corpus_path /
+ function["Function name"]).mkdir(parents=True, exist_ok=True)
+ function_name_list.add(function["Function name"])
+ add_arg_list = True
+
+ index = 0
+ blob_name = "blob" + str(index)
+ while ((self.Natch_corpus_path / function["Function name"] / blob_name).exists()):
+ index += 1
+ blob_name = "blob" + str(index)
+ arguments = []
+ print("-- Parsing data of function " + function["Function name"])
+ with open((self.Natch_corpus_path / function["Function name"] / blob_name).as_posix(), "wb") as f:
+ print(" [*] writing seed file: " + (self.Natch_corpus_path /
+ function["Function name"] / blob_name).as_posix() + "...")
+ for arg in function["Arguments"]:
+ arguments.append(arg["Type"])
+ if (arg["Type"] in ["char *", "const char *", "unsigned char *", "const unsigned char *", "const char *&"]):
+ # print(len(arg["Value"]))
+ f.write((len(arg["Value"])).to_bytes(
+ 4, byteorder='big'))
+ f.write(arg["Value"].encode())
+ else:
+ # f.write(arg["Value"]).to_bytes(arg["Size"], byteorder='big')
+ if (not type(arg["Value"]) is int):
+ f.write(int(arg["Value"]).to_bytes(
+ 8, byteorder='big'))
+ else:
+ f.write(arg["Value"].to_bytes(8, byteorder='big'))
+ if add_arg_list:
+ target_functions.append(
+ {
+ "name": function["Function name"],
+ "args": arguments
+ })
+ self.target_functions = target_functions
+
+ def __get_compile_command(self, file):
+ """ Get the compile command of given file
+
+ Args:
+ file (FILE): given file
+
+ Returns:
+ dict(compiler, command, file, location): dict consists of compiler type, compile command, file name, and file location.
+ """
+ if (self.build_path / "compile_commands.json").exists():
+ compile_commands = self.build_path / "compile_commands.json"
+ commands = json.load(open(compile_commands.as_posix()))
+ for command in commands:
+ if pathlib.Path(command["file"]) == pathlib.Path(file):
+ compiler = command["command"].split(" ")[0].split("/")[-1]
+ if compiler == "cc" or compiler == "clang" or compiler == "gcc":
+ return {
+ "compiler": "CC",
+ "command": command["command"],
+ "file": command["file"],
+ "location": command["directory"]
+ }
+ else:
+ return {
+ "compiler": "CXX",
+ "command": command["command"],
+ "file": command["file"],
+ "location": command["directory"]
+ }
+ return {
+ "compiler": "CXX",
+ "command": command["command"],
+ "file": command["file"],
+ "location": command["directory"]
+ }
+ else:
+ if file.split(".")[-1] == "c":
+ return {
+ "compiler": "CC",
+ "command": "",
+ "file": file,
+ "location": ""
+ }
+ else:
+ return {
+ "compiler": "CXX",
+ "command": "",
+ "file": file,
+ "location": ""
+ }
+ return {
+ "compiler": "CC",
+ "command": "",
+ "file": file,
+ "location": ""
+ }
+
+ def __gen_header(self, target_function_name):
+ """ Generate header for the target function
+
+ Args:
+ target_function_name (string): the target function name.
+
+ Returns:
+ list: list of included header.
+ """
+
+ defaults = ["stdio.h", "stddef.h", "time.h",
+ "stdlib.h", "string.h", "stdint.h"]
+ compiled_files = self.target_library["compiled_files"]
+ included_headers = []
+ found = False
+ for f in compiled_files:
+ if f["filename"] == target_function_name:
+ found = True
+ for header in f["headers"]:
+ if not header[1:-1] in defaults:
+ included_headers.append(header)
+ break
+ if not found:
+ short_filename = target_function_name.split('/')[-1]
+ for f in compiled_files:
+ if f["filename"].split('/')[-1] == short_filename:
+ found = True
+ for header in f["headers"]:
+ if not header[1:-1] in defaults:
+ included_headers.append(header)
+ break
+ include_lines = []
+ for i in defaults:
+ include_lines.append("#include <" + i + ">\n")
+ for i in included_headers:
+ include_lines.append("#include " + i + "\n")
+ if self.header:
+ for i in self.header:
+ if i not in included_headers:
+ include_lines.append("#include " + i + "\n")
+ return include_lines
+
+ def __get_function_header(self, func_location):
+ """ Generate header for the target function
+
+ Args:
+ func_location (string): function location.
+
+ Returns:
+ list: list of included header.
+ """
+ defaults = ["stdio.h", "stddef.h", "time.h",
+ "stdlib.h", "string.h", "stdint.h"]
+ compiled_files = self.target_library["compiled_files"]
+ included_headers = []
+ found = False
+ for f in compiled_files:
+ if f["filename"] == func_location:
+ found = True
+ for header in f["headers"]:
+ if not header[1:-1] in defaults:
+ included_headers.append(header)
+ break
+ if not found:
+ short_filename = func_location.split('/')[-1]
+ for f in compiled_files:
+ if f["filename"].split('/')[-1] == short_filename:
+ found = True
+ for header in f["headers"]:
+ if not header[1:-1] in defaults:
+ included_headers.append(header)
+ break
+ return included_headers
+
+ def __add_header(self, function_headers):
+ for h in function_headers:
+ if h not in self.header:
+ self.header.append(h)
+
+ def __gen_builtin(self, param_name, gen_type_info):
+ """Declare and assign value for a builtin type
+
+ Args:
+ param_name (str): parameter's name
+ gen_type_info (dict): information of parameter's type
+
+ Returns:
+ dict: (gen_lines, gen_free, buffer_size)
+ """
+ return {
+ "gen_lines": [
+ "//GEN_BUILTIN\n",
+ "if (Fuzz_Size_remain < sizeof(" + gen_type_info["type_name"].replace(
+ "(anonymous namespace)::", "") + ") return 0;\n",
+ "Fuzz_Size_remain = Fuzz_Size_remain - sizeof(" + gen_type_info["type_name"].replace(
+ "(anonymous namespace)::", "") + ");\n",
+ gen_type_info["type_name"].replace(
+ "(anonymous namespace)::", "") + " " + param_name + ";\n",
+ "memcpy(&"+param_name+", futag_pos, sizeof(" +
+ gen_type_info["type_name"].replace(
+ "(anonymous namespace)::", "") + "));\n",
+ "futag_pos += sizeof(" + gen_type_info["type_name"].replace(
+ "(anonymous namespace)::", "") + ");\n"
+ ],
+ "gen_free": [],
+ "buffer_size": ["sizeof(" + gen_type_info["type_name"].replace("(anonymous namespace)::", "")+")"]
+ }
+
+ def __gen_strsize(self, param_name, param_type, dyn_size_idx, array_name):
+ return {
+ "gen_lines": [
+ "//GEN_SIZE\n",
+ param_type + " " + param_name +
+ " = (" + param_type +
+ ") " + array_name + "[" + str(dyn_size_idx - 1) + "];\n",
+ ],
+ "gen_free": [],
+ "buffer_size": []
+ }
+
+ def __gen_cstring(self, param_name, gen_type_info, dyn_cstring_size_idx):
+ """Declare and assign value for a C string type
+
+ Args:
+ param_name (str): parameter's name
+ gen_type_info (dict): information of parameter's type
+ dyn_cstring_size_idx (int): id of dynamic cstring size
+
+ Returns:
+ dict: (gen_lines, gen_free, buffer_size)
+ """
+ ref_name = param_name
+ if (gen_type_info["local_qualifier"]):
+ ref_name = "r" + ref_name
+
+ gen_lines = [
+ "//GEN_CSTRING1\n",
+
+ "//Read length of str\n",
+ "if (Fuzz_Size_remain < sizeof(int)) return 0;\n",
+ "Fuzz_Size_remain -= sizeof(int);\n",
+ "int futag_str_size;\n"
+ "memcpy(&futag_str_size, futag_pos, sizeof(int));\n",
+ "futag_pos += sizeof(int);\n",
+ "if (Fuzz_Size_remain < (size_t)futag_str_size) return 0;\n",
+ "Fuzz_Size_remain = Fuzz_Size_remain - (size_t)futag_str_size;\n",
+
+ gen_type_info["base_type_name"] + " " + ref_name +
+ " = (" + gen_type_info["base_type_name"] +
+ ") malloc((futag_str_size + 1)* sizeof(char));\n",
+ "memset(" + ref_name +
+ ", 0, futag_str_size + 1);\n",
+ "memcpy(" + ref_name +
+ ", futag_pos, futag_str_size);\n",
+ "futag_pos += futag_str_size;\n",
+ ]
+ if (gen_type_info["local_qualifier"]):
+ gen_lines += [gen_type_info["type_name"] +
+ " " + param_name + " = " + ref_name + ";\n"]
+
+ return {
+ "gen_lines": gen_lines,
+ "gen_free": [
+ "if (" + ref_name + ") {\n",
+ " free(" + ref_name + ");\n",
+ " " + ref_name + " = NULL;\n",
+ "}\n"
+ ],
+ "buffer_size": []
+ }
+
+ def __gen_wstring(self, param_name, gen_type_info, dyn_wstring_size_idx):
+ """Declare and assign value for a C string type
+
+ Args:
+ param_name (str): parameter's name
+ gen_type_info (dict): information of parameter's type
+ dyn_wstring_size_idx (int): id of dynamic wstring size
+
+ Returns:
+ dict: (gen_lines, gen_free, buffer_size)
+ """
+ ref_name = param_name
+ if (gen_type_info["local_qualifier"]):
+ ref_name = "r" + ref_name
+
+ gen_lines = [
+ "//GEN_WSTRING\n",
+ "//Read length of str\n",
+ "if (Fuzz_Size_remain < sizeof(int)) return 0;\n",
+ "Fuzz_Size_remain -= sizeof(int);\n",
+ "int futag_str_size;\n"
+ "memcpy(&futag_str_size, futag_pos, sizeof(int));\n",
+ "futag_pos += sizeof(int);\n",
+ "if (Fuzz_Size_remain < (size_t)futag_str_size) return 0;\n",
+ "Fuzz_Size_remain = Fuzz_Size_remain - (size_t)futag_str_size;\n",
+
+ gen_type_info["base_type_name"] + " " + ref_name +
+ " = (" + gen_type_info["base_type_name"] +
+ ") malloc(futag_str_size + sizeof(wchar_t));\n",
+ "memset(" + ref_name +
+ ", 0, (int)((futag_str_size + sizeof(wchar_t))/4));\n",
+ "memcpy(" + ref_name + ", futag_pos, futag_str_size);\n",
+ "futag_pos += futag_str_size;\n",
+ ]
+ if (gen_type_info["local_qualifier"]):
+ gen_lines += [gen_type_info["type_name"] +
+ " " + param_name + " = " + ref_name + ";\n"]
+
+ return {
+ "gen_lines": gen_lines,
+ "gen_free": [
+ "if (" + ref_name + ") {\n",
+ " free(" + ref_name + ");\n",
+ " " + ref_name + " = NULL;\n",
+ "}\n"
+ ],
+ "buffer_size": []
+ }
+
+ def __gen_cxxstring(self, param_name, gen_type_info, dyn_cxxstring_size_idx):
+ """Declare and assign value for a C++ string type
+
+ Args:
+ param_name (str): parameter's name
+ gen_type_info (dict): information of parameter's type for initializing
+
+ Returns:
+ dict: (gen_lines, gen_free, buffer_size)
+ """
+ ref_name = param_name
+ if (gen_type_info["local_qualifier"]):
+ ref_name = "r" + ref_name
+ gen_lines = [
+ "//GEN_CXXSTRING\n",
+ ]
+ if (gen_type_info["local_qualifier"]):
+ gen_lines += [gen_type_info["type_name"] +
+ " " + param_name + " = " + ref_name + ";\n"]
+
+ return {
+ "gen_lines": [
+ "//Read length of str\n",
+ "if (Fuzz_Size_remain < sizeof(int)) return 0;\n",
+ "Fuzz_Size_remain -= sizeof(int);\n",
+ "int futag_str_size;\n",
+ "memcpy(&futag_str_size, futag_pos, sizeof(int));\n",
+ "futag_pos += sizeof(int);\n",
+ "if (Fuzz_Size_remain < (size_t)futag_str_size) return 0;\n",
+ "Fuzz_Size_remain = Fuzz_Size_remain - (size_t)futag_str_size;\n",
+
+ gen_type_info["type_name"] + " " + param_name +
+ "(futag_pos, futag_str_size); \n",
+ "futag_pos += futag_str_size;\n",
+ ],
+ "gen_free": [],
+ "buffer_size": []
+ }
+
+ def __gen_enum(self, enum_record, param_name, gen_type_info, compiler_info, anonymous: bool = False):
+
+ if anonymous:
+ enum_name = enum_record["name"]
+ else:
+ enum_name = enum_record["qname"]
+
+ enum_length = len(enum_record["enum_values"])
+ enum_name = gen_type_info["type_name"]
+ if compiler_info["compiler"] == "CC":
+ return {
+ "gen_lines": [
+ "//GEN_ENUM\n",
+ "unsigned int " + param_name + "_enum_index; \n",
+ "memcpy(&" + param_name +
+ "_enum_index, futag_pos, sizeof(unsigned int));\n",
+ enum_name + " " + param_name + " = " +
+ param_name + "_enum_index % " +
+ str(enum_length) + ";\n"
+ ],
+ "gen_free": [],
+ "buffer_size": ["sizeof(unsigned int)"]
+ }
+ else:
+ return {
+ "gen_lines": [
+ "//GEN_ENUM\n",
+ "unsigned int " + param_name + "_enum_index; \n",
+ "memcpy(&" + param_name +
+ "_enum_index, futag_pos, sizeof(unsigned int));\n",
+ # "enum " + enum_name + " " + param_name + " = static_cast(" + param_name + "_enum_index % " + str(enum_length) + ");\n"
+ ],
+ "gen_free": [],
+ "buffer_size": ["sizeof(unsigned int)"]
+ }
+
+ def __gen_array(self, param_name, gen_type_info):
+ return {
+ "gen_lines": [
+ "//GEN_ARRAY\n",
+ gen_type_info["type_name"] + " " + param_name + " = (" + gen_type_info["type_name"] + ") " +
+ "malloc(sizeof(" + gen_type_info["base_type_name"] +
+ ") * " + str(gen_type_info["length"]) + ");\n",
+ "memcpy(" + param_name + ", futag_pos, " + str(
+ gen_type_info["length"]) + " * sizeof(" + gen_type_info["base_type_name"] + "));\n",
+ "futag_pos += " +
+ str(gen_type_info["length"]) + " * sizeof(" +
+ gen_type_info["base_type_name"] + ");\n"
+ ],
+ "gen_free": [
+ "if (" + param_name + ") {\n",
+ " free( " + param_name + ");\n",
+ " " + param_name + " = NULL;\n",
+ "}\n"
+ ],
+ "buffer_size": [str(gen_type_info["length"]) + " * sizeof(" + gen_type_info["base_type_name"] + ")"]
+ }
+
+ def __gen_void(self, param_name):
+ return {
+ "gen_lines": [
+ "//GEN_VOID\n",
+ "const char *" + param_name + "= NULL; \n",
+ ],
+ "gen_free": [],
+ "buffer_size": []
+ }
+
+ def __gen_qualifier(self, param_name, prev_param_name, gen_type_info):
+ return {
+ "gen_lines": [
+ "//GEN_QUALIFIED\n",
+ gen_type_info["type_name"] + " " +
+ param_name + " = " + prev_param_name + ";\n"
+ ],
+ "gen_free": [],
+ "buffer_size": []
+ }
+
+ def __gen_pointer(self, param_name, prev_param_name, gen_type_info):
+ return {
+ "gen_lines": [
+ "//GEN_POINTER\n",
+ gen_type_info["type_name"].replace("(anonymous namespace)::", "") + " " + param_name +
+ " = & " + prev_param_name + ";\n"
+ ],
+ "gen_free": [],
+ "buffer_size": []
+ }
+
+ def __gen_struct(self, struct_name, struct, gen_info):
+ gen_lines = [gen_info["type_name"] + " " + struct_name + ";\n"]
+ gen_free = []
+ buffer_size = []
+ field_id = 0
+
+ for field in struct["fields"]:
+ curr_name = field["field_name"]
+ for gen_type_info in field["gen_list"]:
+ if gen_type_info["gen_type"] == GEN_BUILTIN:
+ this_gen_size = False
+ # if field_id > 0 and (struct["fields"][field_id - 1]["gen_list"][0]["gen_type"] in [GEN_CSTRING, GEN_WSTRING, GEN_CXXSTRING]):
+ # if gen_type_info["type_name"] in ["size_t", "unsigned char", "char", "int", "unsigned", "unsigned int", "short", "unsigned short", "short int", "unsigned short int"]:
+ # dyn_size_idx = 0
+ # array_name = ""
+ # if struct["fields"][field_id - 1]["gen_list"][0]["gen_type"] == GEN_CSTRING:
+ # dyn_size_idx = self.dyn_cstring_size_idx
+ # array_name = "dyn_cstring_size"
+ # elif struct["fields"][field_id - 1]["gen_list"][0]["gen_type"] == GEN_WSTRING:
+ # dyn_size_idx = self.dyn_wstring_size_idx
+ # array_name = "dyn_wstring_size"
+ # else:
+ # dyn_size_idx = self.dyn_cxxstring_size_idx
+ # array_name = "dyn_cxxstring_size"
+ # curr_name = "sz_" + curr_name # size_prefix
+ # curr_gen = self.__gen_strsize(
+ # curr_name, gen_type_info["type_name"], dyn_size_idx, array_name)
+ # buffer_size += curr_gen["buffer_size"]
+ # gen_lines += curr_gen["gen_lines"]
+ # gen_free += curr_gen["gen_free"]
+ # this_gen_size = True # with break, we may not need this variable :)
+ # break
+
+ if not this_gen_size:
+ curr_name = "b_" + curr_name # builtin_prefix
+ curr_gen = self.__gen_builtin(curr_name, gen_type_info)
+ buffer_size += curr_gen["buffer_size"]
+ gen_lines += curr_gen["gen_lines"]
+ gen_free += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_CSTRING:
+ curr_name = "strc_" + curr_name # string_prefix
+ self.dyn_cstring_size_idx += 1
+ curr_gen = self.__gen_cstring(
+ curr_name, gen_type_info, self.dyn_cstring_size_idx)
+ buffer_size += curr_gen["buffer_size"]
+ gen_lines += curr_gen["gen_lines"]
+ gen_free += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_REFSTRING:
+ curr_name = "strc_" + curr_name # string_prefix
+ self.dyn_cstring_size_idx += 1
+ curr_gen = self.__gen_cstring(
+ curr_name, gen_type_info, self.dyn_cstring_size_idx)
+ # reinit value of curr_name to send reference of string
+ # curr_name = "&" + curr_name # string_prefix
+ buffer_size += curr_gen["buffer_size"]
+ gen_lines += curr_gen["gen_lines"]
+ gen_free += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_WSTRING:
+ curr_name = "strc_" + curr_name # string_prefix
+ self.dyn_wstring_size_idx += 1
+ curr_gen = self.__gen_wstring(
+ curr_name, gen_type_info, self.dyn_wstring_size_idx)
+ buffer_size += curr_gen["buffer_size"]
+ gen_lines += curr_gen["gen_lines"]
+ gen_free += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_CXXSTRING:
+ curr_name = "strcxx_" + curr_name # string_prefix
+ self.dyn_cxxstring_size_idx += 1
+ curr_gen = self.__gen_cxxstring(
+ curr_name, gen_type_info, self.dyn_cxxstring_size_idx)
+ buffer_size += curr_gen["buffer_size"]
+ gen_lines += curr_gen["gen_lines"]
+ gen_free += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_ENUM: # GEN_ENUM
+ curr_name = "e_" + curr_name # enum_prefix
+ found_enum = None
+ # search in enum list of analysis result:
+ for enum in self.target_library["enums"]:
+ if len(gen_type_info["type_name"].split(" ")) > 1:
+ if enum["qname"] == gen_type_info["type_name"].split(" ")[
+ 1]:
+ found_enum = enum
+ break
+ else:
+ if enum["qname"] == gen_type_info["type_name"]:
+ found_enum = enum
+ break
+ if not found_enum:
+ # search in typedef list of analysis result:
+ for typedef in self.target_library["typedefs"]:
+ if typedef["name"] == gen_type_info["type_name"]:
+ enum_hash = typedef["type_source_hash"]
+ for enum in self.target_library["enums"]:
+ if enum["hash"] == enum_hash:
+ found_enum = enum
+ break
+ if not found_enum:
+ self.curr_func_log += f"- Can not generate for object: {str(gen_type_info)}\n"
+ self.gen_this_function = False
+ else:
+ compiler_info = self.__get_compile_command(
+ self.curr_function["location"]["fullpath"])
+ curr_gen = self.__gen_enum(
+ found_enum, curr_name, gen_type_info, compiler_info, self.gen_anonymous)
+ buffer_size += curr_gen["buffer_size"]
+ gen_lines += curr_gen["gen_lines"]
+ gen_free += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_ARRAY: # GEN_ARRAY
+ curr_name = "a_" + curr_name # array_prefix
+ curr_gen = self.__gen_array(curr_name, gen_type_info)
+ buffer_size += curr_gen["buffer_size"]
+ gen_lines += curr_gen["gen_lines"]
+ gen_free += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_QUALIFIER:
+ curr_name = "q_" + curr_name # qualifier_prefix
+ curr_gen = self.__gen_qualifier(
+ curr_name, prev_param_name, gen_type_info)
+ buffer_size += curr_gen["buffer_size"]
+ gen_lines += curr_gen["gen_lines"]
+ gen_free += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_POINTER:
+ curr_name = "p_" + curr_name # pointer_prefix
+ curr_gen = self.__gen_pointer(
+ curr_name, prev_param_name, gen_type_info)
+ buffer_size += curr_gen["buffer_size"]
+ gen_lines += curr_gen["gen_lines"]
+ gen_free += curr_gen["gen_free"]
+
+ prev_param_name = curr_name
+ gen_lines += [struct_name + "." +
+ field["field_name"] + " = " + curr_name + ";\n"]
+ field_id += 1
+
+ return {
+ "gen_lines": gen_lines,
+ "gen_free": gen_free,
+ "buffer_size": buffer_size
+ }
+
+ def __gen_union(self, param_name, class_record, gen_type_info):
+ """Declare and assign value for a union type
+
+ Args:
+ param_name (str): _description_
+ gen_type_info (dict): information of parameter's type for initializing
+
+ Returns:
+ dict: (gen_lines, gen_free, buffer_size)
+ """
+ return {
+ "gen_lines": [
+ "//GEN_UNION\n",
+ gen_type_info["type_name"] + " " + param_name + ";\n",
+ "memcpy(&"+param_name+", futag_pos, sizeof(" +
+ gen_type_info["type_name"] + "));\n",
+ "futag_pos += sizeof(" + gen_type_info["type_name"] + ");\n"
+ ],
+ "gen_free": [],
+ "buffer_size": ["sizeof(" + gen_type_info["type_name"] + ")"]
+ }
+
+ def __gen_class(self, param_name, class_record):
+ """Declare and assign value for a class type
+
+ Args:
+ param_name (str): _description_
+ gen_type_info (dict): information of parameter's type for initializing
+
+ Returns:
+ dict: (gen_lines, gen_free, buffer_size)
+ """
+ result = []
+ constructors = [c for c in self.target_library["functions"] if c['parent_hash'] == class_record['hash']
+ and c['is_simple'] and c["func_type"] in [FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]]
+
+ # if class has default constructor, then return this constructor
+ for c in constructors:
+ if c['is_simple'] and c["func_type"] == FUNC_DEFAULT_CONSTRUCTOR:
+ result.append(self.__gen_var_function(param_name, c))
+
+ return result
+
+ def __gen_input_file(self, param_name, gen_type_info):
+ cur_gen_free = [" " + x for x in self.gen_free]
+ if gen_type_info["gen_type"] == GEN_CSTRING:
+ line = "const char* " + param_name + \
+ " = \"futag_input_file_" + str(self.file_idx - 1) + "\";\n"
+ elif gen_type_info["gen_type"] == GEN_WSTRING:
+ line = "const wchar_t * " + param_name + \
+ " = L\"futag_input_file_" + str(self.file_idx - 1) + "\";\n"
+ else:
+ return {
+ "gen_lines": [],
+ "gen_free": [],
+ "buffer_size": []
+ }
+ gen_lines = [
+ "//GEN_INPUT_FILE\n",
+ line,
+ "FILE * fp_" + str(self.file_idx - 1) +
+ " = fopen(" + param_name + ",\"w\");\n",
+ "if (fp_" + str(self.file_idx - 1) + " == NULL) {\n",
+ ]
+ gen_lines += cur_gen_free
+ gen_lines += [
+ " return 0;\n",
+ "}\n",
+ "fwrite(futag_pos, 1, file_size[" + str(self.file_idx - 1) +
+ "], fp_" + str(self.file_idx - 1) + ");\n",
+ "fclose(fp_" + str(self.file_idx - 1) + ");\n",
+ "futag_pos += file_size[" + str(self.file_idx - 1) + "];\n"
+ ]
+ return {
+ "gen_lines": gen_lines,
+ "gen_free": [],
+ "buffer_size": []
+ }
+
+ def __gen_file_descriptor(self, param_name, gen_type_info):
+ if not "" in self.header:
+ self.header += [""]
+ cur_gen_free = [" " + x for x in self.gen_free]
+ gen_lines = [
+ "//GEN_FILE_DESCRIPTOR\n",
+ "const char* " + param_name + "_tmp" + str(self.file_idx) + " = \"futag_input_file_" +
+ str(self.file_idx - 1) + "\";\n",
+ "FILE * fp_" + str(self.file_idx - 1) +
+ " = fopen(" + param_name + "_tmp" +
+ str(self.file_idx) + ",\"w\");\n",
+ "if (fp_" + str(self.file_idx - 1) + " == NULL) {\n",
+ ]
+ gen_lines += cur_gen_free
+ gen_lines += [
+ " return 0;\n",
+ "}\n",
+ "fwrite(futag_pos, 1, file_size[" + str(self.file_idx - 1) +
+ "], fp_" + str(self.file_idx - 1) + ");\n",
+ "fclose(fp_" + str(self.file_idx - 1) + ");\n",
+ "futag_pos += file_size[" + str(self.file_idx - 1) + "];\n",
+ gen_type_info["type_name"] + " " + param_name +
+ "= open(" + param_name + "_tmp" +
+ str(self.file_idx) + ", O_RDWR);\n"
+ ]
+ gen_free = ["close(" + param_name + ");\n"]
+ return {
+ "gen_lines": gen_lines,
+ "gen_free": gen_free,
+ "buffer_size": []
+ }
+
+ def __search_in_typedefs(self, type_name, typedefs):
+ # Are there multiple type definitions for the same data type???
+ result = None
+ for td in typedefs:
+ if td["underlying_type"] == type_name:
+ return td
+ return result
+
+ def __search_return_types(self, param_gen_list, curr_function, function_lists):
+ result = []
+ for f in function_lists:
+ gen_list = []
+ # To avoid infinite loop, we search only function with different name
+ if f["qname"] == curr_function["qname"]:
+ continue
+ # Search only simple function with the same return type
+
+ compiler_info = self.__get_compile_command(
+ curr_function["location"]["fullpath"])
+ compiler = compiler_info["compiler"]
+
+ if f["gen_return_type"] and f["gen_return_type"][0]["type_name"] == param_gen_list[0]["type_name"] and f["is_simple"]:
+ if (compiler != "CXX" and f["storage_class"] != SC_STATIC) or (compiler == "CXX" and not f["access_type"] in [AS_PROTECTED, AS_PRIVATE]):
+ f_gen_list_length = len(f["gen_return_type"])
+ param_gen_list_length = len(param_gen_list)
+ min_length = f_gen_list_length if f_gen_list_length < param_gen_list_length else param_gen_list_length
+ iter = 0
+ while iter < min_length and f["gen_return_type"][iter]["type_name"] == param_gen_list[iter]["type_name"]:
+ iter += 1
+
+ last_iter = iter
+ while iter < f_gen_list_length:
+ curr_gen_field = f["gen_return_type"][iter]
+ if curr_gen_field["gen_type"] == GEN_POINTER:
+ # curr_gen_field["gen_type"] = GEN_VARADDR
+ curr_gen_field["gen_type_name"] = "_VAR_ADDRESS"
+ else:
+ curr_gen_field["gen_type"] == GEN_UNKNOWN
+
+ gen_list.append(curr_gen_field)
+ iter += 1
+
+ iter = last_iter
+ while iter < param_gen_list_length:
+ gen_list.append(param_gen_list[iter])
+ iter += 1
+
+ result.append({
+ "function": f,
+ "gen_list": gen_list,
+ })
+ return result
+
+ def __append_gen_dict(self, curr_gen):
+ if curr_gen:
+ self.buffer_size += curr_gen["buffer_size"]
+ self.gen_lines += curr_gen["gen_lines"]
+ self.gen_free += curr_gen["gen_free"]
+
+ def __gen_var_function(self, func_param_name: str, func):
+ """ Initialize for argument of function call """
+ # curr_dyn_size = 0
+ param_list = []
+ curr_gen_string = -1
+ gen_dict = {
+ "gen_lines": [],
+ "gen_free": [],
+ "buffer_size": [],
+ }
+ if not self.gen_anonymous and "(anonymous namespace)" in func["qname"]:
+ self.gen_this_function = False
+ return gen_dict
+ param_id = 0
+ for arg in func["params"]:
+ if len(arg["gen_list"]) > 1:
+ curr_name = "_" + str(self.var_function_idx) + \
+ "_" + arg["param_name"]
+ else:
+ curr_name = curr_name = str(
+ self.var_function_idx) + "_" + arg["param_name"]
+ prev_param_name = curr_name
+ for gen_type_info in arg["gen_list"]:
+ if gen_type_info["gen_type"] == GEN_BUILTIN:
+ this_gen_size = False
+ # if arg["param_usage"] in ["FILE_DESCRIPTOR"]:
+ # curr_name = "fd_" + curr_name + str(self.file_idx) # string_prefix
+ # self.file_idx += 1
+ # curr_gen = self.__gen_file_descriptor(
+ # curr_name, gen_type_info)
+ # gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ # gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ # gen_dict["gen_free"] += curr_gen["gen_free"]
+ # break
+ # elif param_id > 0 and (func["params"][param_id - 1]["gen_list"][0]["gen_type"] in [GEN_CSTRING, GEN_WSTRING, GEN_CXXSTRING] or arg["param_usage"] == "SIZE_FIELD"):
+ # if gen_type_info["type_name"] in ["size_t", "unsigned char", "char", "int", "unsigned", "unsigned int", "short", "unsigned short", "short int", "unsigned short int"]:
+ # dyn_size_idx = 0
+ # array_name = ""
+ # if func["params"][param_id - 1]["gen_list"][0]["gen_type"] == GEN_CSTRING:
+ # dyn_size_idx = self.dyn_cstring_size_idx
+ # array_name = "dyn_cstring_size"
+ # elif func["params"][param_id - 1]["gen_list"][0]["gen_type"] == GEN_WSTRING:
+ # dyn_size_idx = self.dyn_wstring_size_idx
+ # array_name = "dyn_wstring_size"
+ # else:
+ # dyn_size_idx = self.dyn_cxxstring_size_idx
+ # array_name = "dyn_cxxstring_size"
+ # curr_name = "sz_" + curr_name # size_prefix
+ # curr_gen = self.__gen_strsize(
+ # curr_name, arg["param_type"], dyn_size_idx, array_name)
+ # gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ # gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ # gen_dict["gen_free"] += curr_gen["gen_free"]
+ # this_gen_size = True # with break, we may not need this variable :)
+ # break
+ if not this_gen_size:
+ curr_name = "b_" + curr_name # builtin_prefix
+ curr_gen = self.__gen_builtin(curr_name, gen_type_info)
+ gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ gen_dict["gen_free"] += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_CSTRING:
+ # GEN FILE NAME OR # GEN STRING
+ if (arg["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or arg["param_name"] in ["filename", "file", "filepath"] or arg["param_name"].find('file') != -1 or arg["param_name"].find('File') != -1) and len(arg["gen_list"]) == 1:
+ curr_name = "f_" + curr_name + \
+ str(self.file_idx) # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+ curr_name = "str_" + curr_name + \
+ str(self.dyn_cstring_size_idx) # string_prefix
+ self.dyn_cstring_size_idx += 1
+ curr_gen = self.__gen_cstring(
+ curr_name, gen_type_info, self.dyn_cstring_size_idx)
+ gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ gen_dict["gen_free"] += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_REFSTRING:
+ # GEN FILE NAME OR # GEN STRING
+ if (arg["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or arg["param_name"] in ["filename", "file", "filepath"] or arg["param_name"].find('file') != -1 or arg["param_name"].find('File') != -1) and len(arg["gen_list"]) == 1:
+ curr_name = "f_" + curr_name + \
+ str(self.file_idx) # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+ curr_name = "str_" + curr_name + \
+ str(self.dyn_cstring_size_idx) # string_prefix
+ self.dyn_cstring_size_idx += 1
+ curr_gen = self.__gen_cstring(
+ curr_name, gen_type_info, self.dyn_cstring_size_idx)
+ # curr_name = "&" + curr_name
+ gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ gen_dict["gen_free"] += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_WSTRING:
+ # GEN FILE NAME OR # GEN STRING
+ if (arg["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or arg["param_name"] in ["filename", "file", "filepath"] or arg["param_name"].find('file') != -1 or arg["param_name"].find('File') != -1) and len(arg["gen_list"]) == 1:
+ curr_name = "f_" + curr_name + \
+ str(self.file_idx) # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+ curr_name = "str_" + curr_name + \
+ str(self.dyn_wstring_size_idx) # string_prefix
+ self.dyn_wstring_size_idx += 1
+ curr_gen = self.__gen_wstring(
+ curr_name, gen_type_info, self.dyn_wstring_size_idx)
+ gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ gen_dict["gen_free"] += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_CXXSTRING:
+
+ if (arg["param_name"] in ["filename", "file", "filepath", "path"] or arg["param_name"].find('file') != -1 or arg["param_name"].find('File') != -1 or arg["param_name"].find('path') != -1) and len(arg["gen_list"]) == 1:
+ curr_name = "f_" + curr_name + \
+ str(self.file_idx) # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+
+ curr_name = "str_" + curr_name + \
+ str(self.dyn_cxxstring_size_idx) # string_prefix
+ self.dyn_cxxstring_size_idx += 1
+ curr_gen = self.__gen_cxxstring(
+ curr_name, gen_type_info, self.dyn_cxxstring_size_idx)
+ gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ gen_dict["gen_free"] += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_ENUM: # GEN_ENUM
+
+ curr_name = "e_" + curr_name # enum_prefix
+ found_enum = None
+ # search in enum list of analysis result:
+ for enum in self.target_library["enums"]:
+ if len(gen_type_info["type_name"].split(" ")) > 1:
+ if enum["qname"] == gen_type_info["type_name"].split(" ")[
+ 1]:
+ found_enum = enum
+ break
+ else:
+ if enum["qname"] == gen_type_info["type_name"]:
+ found_enum = enum
+ break
+ if not found_enum:
+ # search in typedef list of analysis result:
+ for typedef in self.target_library["typedefs"]:
+ if typedef["name"] == gen_type_info["type_name"]:
+ enum_hash = typedef["type_source_hash"]
+ for enum in self.target_library["enums"]:
+ if enum["hash"] == enum_hash:
+ found_enum = enum
+ break
+ if not found_enum:
+ self.curr_func_log += f"- Can not generate for enum: {str(gen_type_info)}\n"
+ self.gen_this_function = False
+ else:
+ compiler_info = self.__get_compile_command(
+ func["location"]["fullpath"])
+ curr_gen = self.__gen_enum(
+ found_enum, curr_name, gen_type_info, compiler_info, self.gen_anonymous)
+ gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ gen_dict["gen_free"] += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_ARRAY: # GEN_ARRAY
+ curr_name = "a_" + curr_name # array_prefix
+ curr_gen = self.__gen_array(curr_name, gen_type_info)
+ gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ gen_dict["gen_free"] += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_VOID:
+ curr_name = "a_" + curr_name # void_prefix
+ self.curr_func_log += f"- Can not generate for object of void type: {str(gen_type_info)}\n"
+ self.gen_this_function = False
+
+ if gen_type_info["gen_type"] == GEN_QUALIFIER:
+ curr_name = "q_" + curr_name # qualifier_prefix
+ curr_gen = self.__gen_qualifier(
+ curr_name, prev_param_name, gen_type_info)
+ gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ gen_dict["gen_free"] += curr_gen["gen_free"]
+
+ if gen_type_info["gen_type"] == GEN_POINTER:
+ curr_name = "p_" + curr_name # qualifier_prefix
+ curr_gen = self.__gen_pointer(
+ curr_name, prev_param_name, gen_type_info)
+ gen_dict["buffer_size"] += curr_gen["buffer_size"]
+ gen_dict["gen_lines"] += curr_gen["gen_lines"]
+ gen_dict["gen_free"] += curr_gen["gen_free"]
+ prev_param_name = curr_name
+
+ param_id += 1
+ param_list.append(curr_name)
+
+ found_parent = None
+ if func["func_type"] in [FUNC_CXXMETHOD, FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
+ # Find parent class
+ for r in self.target_library["records"]:
+ if r["hash"] == func["parent_hash"]:
+ found_parent = r
+ break
+ if not found_parent:
+ self.gen_this_function = False
+ class_name = found_parent["qname"]
+ if func["func_type"] in [FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
+ function_call = " //declare the RECORD and call constructor\n"
+ function_call += " " + class_name.replace(
+ "::(anonymous namespace)", "") + func_param_name + "(" + ",".join(param_list)+");\n"
+ else:
+ # Find default constructor
+ # TODO: add code for other constructors
+ found_default_constructor = False
+ for fu in self.target_library["functions"]:
+ if fu["parent_hash"] == func["parent_hash"] and fu["func_type"] == FUNC_DEFAULT_CONSTRUCTOR:
+ found_default_constructor = True
+
+ # TODO: add code for other constructors!!!
+ if not found_default_constructor:
+ self.gen_this_function = False
+ function_call = " //declare the RECORD first\n"
+ function_call += " " + \
+ class_name.replace(
+ "::(anonymous namespace)", "") + " " + func_param_name + ";\n"
+ # call the method
+ function_call += " //METHOD CALL\n"
+ function_call += " " + func_param_name + "." + \
+ func["name"]+"(" + ",".join(param_list)+");\n"
+
+ else:
+ function_call = "//GEN_VAR_FUNCTION\n " + func["return_type"] + " " + func_param_name + \
+ " = " + func["qname"] + \
+ "(" + ",".join(param_list)+");\n"
+
+ gen_dict["gen_lines"] += [function_call]
+ return gen_dict
+
+ def __wrapper_file(self, func):
+
+ # if anonymous:
+ # filename = func["name"]
+ # filepath = self.tmp_output_path / "anonymous"
+ # else:
+ filename = func["qname"].replace(":", "_")
+ filepath = self.tmp_output_path
+
+ self.target_extension = func["location"]["fullpath"].split(".")[-1]
+ file_index = 1
+
+ # qname = func["qname"]
+ if len(filename) > 250:
+ return {
+ "file": None,
+ "msg": "Error: File name is too long (>250 characters)!"
+ }
+ dir_name = filename + str(file_index)
+
+ if not (filepath / filename).exists():
+ (filepath / filename).mkdir(parents=True, exist_ok=True)
+
+ # Each variant of fuzz-driver will be save in separated directory
+ # inside the directory of function
+
+ while (filepath / filename / dir_name).exists():
+ file_index += 1
+ dir_name = filename + str(file_index)
+ if file_index > self.max_wrappers:
+ break
+
+ if file_index > self.max_wrappers:
+ return {
+ "file": None,
+ "msg": "Warning: exeeded maximum number of generated fuzzing-wrappers for each function!"
+ }
+ (filepath / filename / dir_name).mkdir(parents=True, exist_ok=True)
+
+ file_name = filename + \
+ str(file_index) + "." + self.target_extension
+
+ full_path = (filepath / filename / dir_name / file_name).as_posix()
+ f = open(full_path, 'w')
+ if f.closed:
+ return {
+ "file": None,
+ "msg": "Error: File closed!"
+ }
+ return {
+ "file": f,
+ "msg": "Successed: " + full_path + " created!"
+ }
+
+ def __anonymous_wrapper_file(self, func):
+
+ # if anonymous:
+ # filename = func["name"]
+ # filepath = self.tmp_output_path / "anonymous"
+ # else:
+ source_path = func["location"]["fullpath"]
+ filename = "anonymous_" + func["name"].replace(":", "_")
+ filepath = self.tmp_output_path
+
+ self.target_extension = func["location"]["fullpath"].split(".")[-1]
+ file_index = 1
+
+ # qname = func["qname"]
+ if len(filename) > 250:
+ return None
+ dir_name = filename + str(file_index)
+
+ if not (filepath / filename).exists():
+ (filepath / filename).mkdir(parents=True, exist_ok=True)
+
+ # Each variant of fuzz-driver will be save in separated directory
+ # inside the directory of function
+
+ while (filepath / filename / dir_name).exists():
+ file_index += 1
+ dir_name = filename + str(file_index)
+ if file_index > self.max_wrappers:
+ break
+
+ if file_index > self.max_wrappers:
+ return None
+ (filepath / filename / dir_name).mkdir(parents=True, exist_ok=True)
+
+ file_name = filename + \
+ str(file_index) + "." + self.target_extension
+
+ full_path_destination = (
+ filepath / filename / dir_name / file_name).as_posix()
+ with open(source_path, 'r') as s:
+ source_file = s.read()
+ d = open(full_path_destination, "w")
+ d.write("//"+func["hash"] + "\n")
+ d.write(source_file)
+ d.close()
+ f = open(full_path_destination, 'a')
+ if f.closed:
+ return None
+ return f
+
+ def __log_file(self, func, anonymous: bool = False):
+ if anonymous:
+ filename = func["name"]
+ filepath = self.tmp_output_path / "anonymous"
+ else:
+ filename = func["qname"]
+ filepath = self.tmp_output_path
+
+ file_index = 1
+
+ # qname = func["qname"]
+ if len(filename) > 250:
+ return None
+ dir_name = filename + str(file_index)
+
+ if not (filepath / filename).exists():
+ (filepath / filename).mkdir(parents=True, exist_ok=True)
+
+ # Each variant of fuzz-driver will be save in separated directory
+ # inside the directory of function
+
+ while (filepath / filename / dir_name).exists():
+ file_index += 1
+ dir_name = filename + str(file_index)
+ if file_index > self.max_wrappers:
+ break
+
+ if file_index > self.max_wrappers:
+ return None
+ (filepath / filename / dir_name).mkdir(parents=True, exist_ok=True)
+
+ file_name = filename + str(file_index) + ".log"
+
+ full_path = (filepath / filename / dir_name / file_name).as_posix()
+ f = open(full_path, 'w')
+ if f.closed:
+ return None
+ return f
+
+ def __save_old_values(self):
+ return {
+ "buffer_size": copy.copy(self.buffer_size),
+ "gen_lines": copy.copy(self.gen_lines),
+ "gen_free": copy.copy(self.gen_free),
+ "dyn_cstring_size_idx": copy.copy(self.dyn_cstring_size_idx),
+ "dyn_cxxstring_size_idx": copy.copy(self.dyn_cxxstring_size_idx),
+ "dyn_wstring_size_idx": copy.copy(self.dyn_wstring_size_idx),
+ "var_function_idx": copy.copy(self.var_function_idx),
+ "param_list": copy.copy(self.param_list),
+ "curr_func_log": copy.copy(self.curr_func_log),
+ "file_idx": copy.copy(self.file_idx),
+ "gen_this_function": copy.copy(self.gen_this_function),
+ "param_list": copy.copy(self.param_list)
+ }
+
+ def __retrieve_old_values(self, old_values):
+ self.buffer_size = copy.copy(old_values["buffer_size"])
+ self.gen_lines = copy.copy(old_values["gen_lines"])
+ self.gen_free = copy.copy(old_values["gen_free"])
+ self.dyn_cstring_size_idx = copy.copy(
+ old_values["dyn_cstring_size_idx"])
+ self.dyn_cxxstring_size_idx = copy.copy(
+ old_values["dyn_cxxstring_size_idx"])
+ self.dyn_wstring_size_idx = copy.copy(
+ old_values["dyn_wstring_size_idx"])
+ self.var_function_idx = copy.copy(old_values["var_function_idx"])
+ self.param_list = copy.copy(old_values["param_list"])
+ self.curr_func_log = copy.copy(old_values["curr_func_log"])
+ self.file_idx = copy.copy(old_values["file_idx"])
+ self.gen_this_function = copy.copy(old_values["gen_this_function"])
+ self.param_list = copy.copy(old_values["param_list"])
+
+ def __gen_anonymous_function(self, func, param_id) -> bool:
+ malloc_free = [
+ "unsigned char *",
+ "char *",
+ "wchar_t *"
+ ]
+ if param_id == len(func['params']):
+ found_parent = None
+ if func["func_type"] in [FUNC_CXXMETHOD, FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
+ # Find parent class
+ for r in self.target_library["records"]:
+ if r["hash"] == func["parent_hash"]:
+ found_parent = r
+ break
+ if not found_parent:
+ self.gen_this_function = False
+
+ # If there is no buffer - return!
+ if (not len(self.buffer_size) and not self.dyn_cstring_size_idx and not self.dyn_cxxstring_size_idx and not self.dyn_wstring_size_idx and not self.file_idx) or not self.gen_this_function:
+ log = self.__log_file(func, self.gen_anonymous)
+ if not log:
+ print(CANNOT_CREATE_LOG_FILE, func["qname"])
+ else:
+ self.curr_func_log = f"Log for function: {func['qname']}\n{self.curr_func_log}"
+ log.write(self.curr_func_log)
+ log.close()
+ return False
+ # generate file name
+ f = self.__anonymous_wrapper_file(func)
+ if not f:
+ self.gen_this_function = False
+ print(CANNOT_CREATE_WRAPPER_FILE, func["qname"])
+ return False
+ print(WRAPPER_FILE_CREATED, f.name)
+
+ for line in self.__gen_header(func["location"]["fullpath"]):
+ f.write("// " + line)
+ f.write('\n')
+ compiler_info = self.__get_compile_command(
+ func["location"]["fullpath"])
+
+ if self.target_type == LIBFUZZER:
+ if compiler_info["compiler"] == "CC":
+ f.write(LIBFUZZER_PREFIX_C)
+ else:
+ f.write(LIBFUZZER_PREFIX_CXX)
+ else:
+ f.write(AFLPLUSPLUS_PREFIX)
+
+ f.write(" size_t Fuzz_Size_remain = Fuzz_Size;\n")
+ f.write(" uint8_t * futag_pos = Fuzz_Data;\n")
+ for line in self.gen_lines:
+ f.write(" " + line)
+
+ f.write(" //" + func["qname"])
+
+ if func["func_type"] in [FUNC_CXXMETHOD, FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
+ class_name = found_parent["qname"]
+ if func["func_type"] in [FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
+ f.write(" //declare the RECORD and call constructor\n")
+ f.write(
+ " " + class_name.replace("::(anonymous namespace)", "") + " futag_target" + "(")
+ else:
+ # Find default constructor
+ # TODO: add code for other constructors
+ found_default_constructor = False
+ for fu in self.target_library["functions"]:
+ if fu["parent_hash"] == func["parent_hash"] and fu["func_type"] == FUNC_DEFAULT_CONSTRUCTOR:
+ found_default_constructor = True
+
+ # TODO: add code for other constructors!!!
+ if not found_default_constructor:
+ self.gen_this_function = False
+ os.unlink(f.name)
+ f.close()
+ return False
+ f.write(" //declare the RECORD first\n")
+ f.write(
+ " " + class_name.replace("::(anonymous namespace)", "") + " futag_target;\n")
+ # call the method
+ f.write(" //METHOD CALL\n")
+ f.write(" futag_target." + func["name"]+"(")
+ else:
+ f.write(" //FUNCTION_CALL\n")
+ if func["return_type"] in malloc_free:
+ f.write(" " + func["return_type"] +
+ " futag_target = " + func["qname"] + "(")
+ else:
+ f.write(
+ " " + func["qname"].replace("::(anonymous namespace)", "") + "(")
+
+ param_list = []
+ for arg in self.param_list:
+ param_list.append(arg + " ")
+ f.write(",".join(param_list))
+ f.write(");\n")
+ # !attempting free on address which was not malloc()-ed
+
+ if func["return_type"] in malloc_free:
+ f.write(" if(futag_target){\n")
+ f.write(" free(futag_target);\n")
+ f.write(" futag_target = NULL;\n")
+ f.write(" }\n")
+
+ f.write(" //FREE\n")
+ for line in self.gen_free:
+ f.write(" " + line)
+ if self.target_type == LIBFUZZER:
+ f.write(LIBFUZZER_SUFFIX)
+ else:
+ f.write(AFLPLUSPLUS_SUFFIX)
+ f.close()
+ return True
+
+ curr_param = func["params"][param_id]
+ if len(curr_param["gen_list"]) > 1:
+ curr_name = "_" + curr_param["param_name"]
+ else:
+ curr_name = curr_param["param_name"]
+ prev_param_name = curr_name
+ gen_curr_param = True
+
+ curr_gen = {}
+ if len(curr_param["gen_list"]) == 0:
+ self.gen_this_function = False
+ return False
+ if curr_param["gen_list"][0]["gen_type"] in [GEN_BUILTIN, GEN_CSTRING, GEN_WSTRING, GEN_REFSTRING, GEN_CXXSTRING, GEN_ENUM, GEN_ARRAY, GEN_UNION, GEN_INPUT_FILE, GEN_OUTPUT_FILE, GEN_QUALIFIER, GEN_POINTER]:
+ for gen_type_info in curr_param["gen_list"]:
+ prev_param_name = curr_name
+ if gen_type_info["gen_type"] == GEN_BUILTIN:
+ this_gen_size = False
+ # if curr_param["param_usage"] in ["FILE_DESCRIPTOR"]:
+ # curr_name = "fd_" + curr_name + str(self.file_idx) # string_prefix
+ # self.file_idx += 1
+ # curr_gen = self.__gen_file_descriptor(
+ # curr_name, gen_type_info)
+ # self.__append_gen_dict(curr_gen)
+ # break
+ # # GEN STRING SIZE
+
+ # elif param_id > 0 and (func["params"][param_id - 1]["gen_list"][0]["gen_type"] in [GEN_CSTRING, GEN_WSTRING, GEN_CXXSTRING] or curr_param["param_usage"] == "SIZE_FIELD"):
+ # if gen_type_info["type_name"] in ["size_t", "unsigned char", "char", "int", "unsigned", "unsigned int", "short", "unsigned short", "short int", "unsigned short int"]:
+ # dyn_size_idx = 0
+ # array_name = ""
+ # if func["params"][param_id - 1]["gen_list"][0]["gen_type"] == GEN_CSTRING:
+ # dyn_size_idx = self.dyn_cstring_size_idx
+ # array_name = "dyn_cstring_size"
+ # elif func["params"][param_id - 1]["gen_list"][0]["gen_type"] == GEN_WSTRING:
+ # dyn_size_idx = self.dyn_wstring_size_idx
+ # array_name = "dyn_wstring_size"
+ # else:
+ # dyn_size_idx = self.dyn_cxxstring_size_idx
+ # array_name = "dyn_cxxstring_size"
+ # curr_name = "sz_" + curr_name # size_prefix
+ # curr_gen = self.__gen_strsize(
+ # curr_name, curr_param["param_type"], dyn_size_idx, array_name)
+ # self.__append_gen_dict(curr_gen)
+ # this_gen_size = True
+ # break
+ if not this_gen_size:
+ curr_name = "b_" + curr_name # builtin_prefix
+ curr_gen = self.__gen_builtin(curr_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_CSTRING:
+ # GEN FILE NAME OR # GEN STRING
+ if (curr_param["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or curr_param["param_name"] in ["filename", "file", "filepath"] or curr_param["param_name"].find('file') != -1 or curr_param["param_name"].find('File') != -1) and len(curr_param["gen_list"]) == 1:
+ curr_name = "f_" + curr_name # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+ # GEN STRING
+ curr_name = "str_" + curr_name # string_prefix
+ self.dyn_cstring_size_idx += 1
+ curr_gen = self.__gen_cstring(
+ curr_name, gen_type_info, self.dyn_cstring_size_idx)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_REFSTRING:
+ print("!!!GEN_REFSTRING\n\n\n")
+ # GEN FILE NAME OR # GEN STRING
+ if (curr_param["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or curr_param["param_name"] in ["filename", "file", "filepath"] or curr_param["param_name"].find('file') != -1 or curr_param["param_name"].find('File') != -1) and len(curr_param["gen_list"]) == 1:
+ curr_name = "f_" + curr_name # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+ # GEN_REFSTRING
+ curr_name = "str_" + curr_name # string_prefix
+ self.dyn_cstring_size_idx += 1
+ curr_gen = self.__gen_cstring(
+ curr_name, gen_type_info, self.dyn_cstring_size_idx)
+ # curr_name = "&" + curr_name
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_WSTRING:
+ # GEN FILE NAME OR # GEN STRING
+ if (curr_param["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or curr_param["param_name"] in ["filename", "file", "filepath"] or curr_param["param_name"].find('file') != -1 or curr_param["param_name"].find('File') != -1) and len(curr_param["gen_list"]) == 1:
+ curr_name = "f_" + curr_name # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+ # GEN STRING
+ curr_name = "str_" + curr_name # string_prefix
+ self.dyn_wstring_size_idx += 1
+ curr_gen = self.__gen_wstring(
+ curr_name, gen_type_info, self.dyn_wstring_size_idx)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_CXXSTRING:
+ curr_name = "str_" + curr_name # string_prefix
+ self.dyn_cxxstring_size_idx += 1
+ curr_gen = self.__gen_cxxstring(
+ curr_name, gen_type_info, self.dyn_cxxstring_size_idx)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_ENUM: # GEN_ENUM
+ curr_name = "e_" + curr_name # enum_prefix
+ found_enum = None
+ # search in enum list of analysis result:
+ for enum in self.target_library["enums"]:
+ if len(gen_type_info["type_name"].split(" ")) > 1:
+ if enum["qname"] == gen_type_info["type_name"].split(" ")[
+ 1]:
+ found_enum = enum
+ break
+ else:
+ if enum["qname"] == gen_type_info["type_name"]:
+ found_enum = enum
+ break
+ if not found_enum:
+ # search in typedef list of analysis result:
+ for typedef in self.target_library["typedefs"]:
+ if typedef["name"] == gen_type_info["type_name"]:
+ enum_hash = typedef["type_source_hash"]
+ for enum in self.target_library["enums"]:
+ if enum["hash"] == enum_hash:
+ found_enum = enum
+ break
+ if not found_enum:
+ self.curr_func_log += f"- Can not generate for enum: {str(gen_type_info)}\n"
+ gen_curr_param = False
+ else:
+ compiler_info = self.__get_compile_command(
+ func["location"]["fullpath"])
+ curr_gen = self.__gen_enum(
+ found_enum, curr_name, gen_type_info, compiler_info, self.gen_anonymous)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_UNION:
+ curr_name = "u_" + curr_name # union_prefix
+ curr_gen = self.__gen_union(curr_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_ARRAY: # GEN_ARRAY
+ curr_name = "a_" + curr_name # array_prefix
+ curr_gen = self.__gen_array(curr_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_QUALIFIER:
+ curr_name = "q_" + curr_name # qualifier_prefix
+ curr_gen = self.__gen_qualifier(
+ curr_name, prev_param_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_POINTER:
+ curr_name = "p_" + curr_name # qualifier_prefix
+ curr_gen = self.__gen_pointer(
+ curr_name, prev_param_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+ prev_param_name = curr_name
+ if not gen_curr_param:
+ self.gen_this_function = False
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ param_id += 1
+ self.__gen_anonymous_function(func, param_id)
+
+ else:
+ if curr_param["gen_list"][0]["gen_type"] == GEN_STRUCT:
+ # 1. Search for function call that generate struct type
+ # 2. If not found, find in typdef the derived type of current struct and then take the action of 1.
+ # 3. If not found, find the struct definition, check if the struct is simple and manual generate
+
+ curr_name = "s_" + curr_name # struct_prefix
+ # A variable of structure type can be initialized with other functions.
+ result_search_return_type = self.__search_return_types(
+ curr_param["gen_list"], func, self.target_library['functions'])
+
+ if not result_search_return_type:
+ # A struct type may be defined with different name through typdef
+ result_search_typedefs = self.__search_in_typedefs(
+ curr_param["gen_list"][0]["type_name"], self.target_library['typedefs'])
+ if result_search_typedefs:
+ typedef_gen_list = [{
+ "base_type_name": result_search_typedefs["underlying_type"],
+ "gen_type": GEN_STRUCT,
+ "gen_type_name": "_STRUCT",
+ "length": 0,
+ "local_qualifier": "",
+ "type_name": result_search_typedefs["name"]
+ }]
+ # Search typedef in return type of functions
+ result_search_typdef_return_type = self.__search_return_types(
+ typedef_gen_list, func, self.target_library['functions'])
+ if result_search_typdef_return_type:
+ old_values = self.__save_old_values()
+ for curr_return_func in result_search_typdef_return_type:
+ self.var_function_idx += 1
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ curr_gen = self.__gen_var_function(
+ curr_name, curr_return_func["function"])
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
+ self.__append_gen_dict(curr_gen)
+ #!!!call recursive
+ param_id += 1
+ self.__gen_anonymous_function(func, param_id)
+ param_id -= 1
+ self.__retrieve_old_values(old_values)
+ else:
+ found_struct = None
+ for record in self.target_library["records"]:
+ if len(curr_param["gen_list"][0]["type_name"].split(" ")) > 1 and record["type"] == STRUCT_RECORD and record["name"] == curr_param["gen_list"][0]["type_name"].split(" ")[1] and record["is_simple"]:
+ found_struct = record
+ break
+ if found_struct:
+ curr_gen = self.__gen_struct(
+ curr_name, record, curr_param["gen_list"][0])
+ self.__append_gen_dict(curr_gen)
+ else:
+ _tmp = curr_param["gen_list"][0]
+ self.curr_func_log += f"- Could not generate for object: {str(_tmp)}. Could not find function call to generate this struct!\n"
+ gen_curr_param = False
+ else:
+ _tmp = curr_param["gen_list"][0]
+ self.curr_func_log += f"- Could not generate for object: {str(_tmp)}. Could not create function call to generate this struct, and the definition of struct not found!\n"
+ gen_curr_param = False
+ else:
+ old_values = self.__save_old_values()
+ for curr_return_func in result_search_return_type:
+ self.var_function_idx += 1
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ curr_gen = self.__gen_var_function(
+ curr_name, curr_return_func["function"])
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
+ self.__append_gen_dict(curr_gen)
+ #!!!call recursive
+ param_id += 1
+ self.__gen_anonymous_function(func, param_id)
+ param_id -= 1
+ self.__retrieve_old_values(old_values)
+
+ if curr_param["gen_list"][0]["gen_type"] == GEN_CLASS:
+ # 1. Search for function call that generate class type
+ # 2. If not found, try to generate class through constructor/default constructor
+
+ curr_name = "c_" + curr_name # struct_prefix
+ # A variable of structure type can be initialized with other functions.
+ result_search_return_type = self.__search_return_types(
+ curr_param["gen_list"], func, self.target_library['functions'])
+
+ if not result_search_return_type:
+ found_class = None
+ for record in self.target_library["records"]:
+ if record["type"] == CLASS_RECORD and record["name"] == curr_param["gen_list"][0]["type_name"]:
+ found_class = record
+ break
+ if found_class:
+ curr_gen_list = self.__gen_class(
+ curr_name, found_class)
+ old_values = self.__save_old_values()
+ for curr_gen in curr_gen_list:
+ self.__append_gen_dict(curr_gen)
+ #!!!call recursive
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ param_id += 1
+ self.var_function_idx += 1
+ self.__gen_anonymous_function(func, param_id)
+ param_id -= 1
+ self.__retrieve_old_values(old_values)
+ else:
+ gen_type_info = curr_param["gen_list"][0]
+ self.curr_func_log += f"- Could not generate for object: {str(gen_type_info)}. Could not find function call to generate this class!\n"
+ gen_curr_param = False
+ else:
+ old_values = self.__save_old_values()
+ for curr_return_func in result_search_return_type:
+ self.var_function_idx += 1
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ curr_gen = self.__gen_var_function(
+ curr_name, curr_return_func["function"])
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
+ self.__append_gen_dict(curr_gen)
+ #!!!call recursive
+ param_id += 1
+ self.__gen_anonymous_function(func, param_id)
+ param_id -= 1
+ self.__retrieve_old_values(old_values)
+
+ if curr_param["gen_list"][0]["gen_type"] in [GEN_INCOMPLETE, GEN_VOID, GEN_FUNCTION, GEN_UNKNOWN]:
+ gen_type_info = curr_param["gen_list"][0]
+ self.curr_func_log += f"- Can not generate for object: {str(gen_type_info)}\n"
+ gen_curr_param = False
+
+ # if gen_type_info["gen_type"] == GEN_VOID:
+ # curr_name = "a_" + curr_name # void_prefix
+ # self.curr_func_log += f"- Can not generate for object: {str(gen_type_info)}\n"
+ # gen_curr_param = False
+ # # curr_gen = self.__gen_void(curr_name)
+ # # self.__append_gen_dict(curr_gen)
+
+ if not gen_curr_param:
+ self.gen_this_function = False
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ param_id += 1
+ self.__gen_anonymous_function(func, param_id)
+
+ def __gen_target_function(self, func, param_id) -> bool:
+ malloc_free = [
+ "unsigned char *",
+ "char *",
+ ]
+
+ if param_id == len(func['params']):
+ if not self.gen_anonymous and "(anonymous namespace)" in func["qname"]:
+ self.curr_func_log = f"This function is in anonymous namespace!"
+ self.gen_this_function = False
+ found_parent = None
+ if func["func_type"] in [FUNC_CXXMETHOD, FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
+ # Find parent class
+ for r in self.target_library["records"]:
+ if r["hash"] == func["parent_hash"]:
+ found_parent = r
+ break
+ if not found_parent:
+ self.gen_this_function = False
+
+ # If there is no buffer - return!
+ if (not len(self.buffer_size) and not self.dyn_cstring_size_idx and not self.dyn_cxxstring_size_idx and not self.dyn_wstring_size_idx and not self.file_idx) or not self.gen_this_function:
+ log = self.__log_file(func, self.gen_anonymous)
+ if not log:
+ print(CANNOT_CREATE_LOG_FILE, func["qname"])
+ else:
+ self.curr_func_log = f"Log for function: {func['qname']}\n{self.curr_func_log}"
+ log.write(self.curr_func_log)
+ log.close()
+ return False
+ # generate file name
+ wrapper_result = self.__wrapper_file(func)
+ print("Generating fuzzing-wapper for function ",
+ func["qname"], ": ")
+ print("-- ", wrapper_result["msg"])
+ if not wrapper_result["file"]:
+ self.gen_this_function = False
+ return False
+ f = wrapper_result["file"]
+
+ f.write("//"+func["hash"] + "\n")
+ for line in self.__gen_header(func["location"]["fullpath"]):
+ f.write(line)
+ f.write('\n')
+ compiler_info = self.__get_compile_command(
+ func["location"]["fullpath"])
+
+ if self.target_type == LIBFUZZER:
+ if compiler_info["compiler"] == "CC":
+ f.write(LIBFUZZER_PREFIX_C)
+ else:
+ f.write(LIBFUZZER_PREFIX_CXX)
+ else:
+ f.write(AFLPLUSPLUS_PREFIX)
+
+ f.write(" size_t Fuzz_Size_remain = Fuzz_Size;;\n")
+ f.write(" uint8_t * futag_pos = Fuzz_Data;\n")
+ for line in self.gen_lines:
+ f.write(" " + line)
+
+ if func["func_type"] in [FUNC_CXXMETHOD, FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
+ class_name = found_parent["qname"]
+ if func["func_type"] in [FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR]:
+ f.write(" //declare the RECORD and call constructor\n")
+ f.write(
+ " " + class_name.replace("::(anonymous namespace)", "") + " futag_target" + "(")
+ else:
+ # Find default constructor
+ # TODO: add code for other constructors
+ found_default_constructor = False
+ for fu in self.target_library["functions"]:
+ if fu["parent_hash"] == func["parent_hash"] and fu["func_type"] == FUNC_DEFAULT_CONSTRUCTOR:
+ found_default_constructor = True
+
+ # TODO: add code for other constructors!!!
+ if not found_default_constructor:
+ self.gen_this_function = False
+ os.unlink(f.name)
+ f.close()
+ return False
+ f.write(" //declare the RECORD first\n")
+ f.write(" " + class_name + " futag_target;\n")
+ # call the method
+ f.write(" //METHOD CALL\n")
+ f.write(" futag_target." + func["name"]+"(")
+ else:
+ f.write(" //FUNCTION_CALL\n")
+ if func["return_type"] in malloc_free:
+ f.write(" " + func["return_type"] +
+ " futag_target = " + func["qname"] + "(")
+ else:
+ f.write(" " + func["qname"] + "(")
+
+ param_list = []
+ for arg in self.param_list:
+ param_list.append(arg + " ")
+ f.write(",".join(param_list))
+ f.write(");\n")
+ # !attempting free on address which was not malloc()-ed
+
+ if func["return_type"] in malloc_free:
+ f.write(" if(futag_target){\n")
+ f.write(" free(futag_target);\n")
+ f.write(" futag_target = NULL;\n")
+ f.write(" }\n")
+
+ f.write(" //FREE\n")
+ for line in self.gen_free:
+ f.write(" " + line)
+ if self.target_type == LIBFUZZER:
+ f.write(LIBFUZZER_SUFFIX)
+ else:
+ f.write(AFLPLUSPLUS_SUFFIX)
+ f.close()
+ return True
+
+ curr_param = func["params"][param_id]
+ if len(curr_param["gen_list"]) > 1:
+ curr_name = "_" + curr_param["param_name"]
+ else:
+ curr_name = curr_param["param_name"]
+ prev_param_name = curr_name
+ gen_curr_param = True
+
+ curr_gen = {}
+ if len(curr_param["gen_list"]) == 0:
+ self.gen_this_function = False
+ return False
+ if curr_param["gen_list"][0]["gen_type"] in [GEN_BUILTIN, GEN_CSTRING, GEN_WSTRING, GEN_REFSTRING, GEN_CXXSTRING, GEN_ENUM, GEN_ARRAY, GEN_UNION, GEN_INPUT_FILE, GEN_OUTPUT_FILE, GEN_QUALIFIER, GEN_POINTER]:
+ for gen_type_info in curr_param["gen_list"]:
+ prev_param_name = curr_name
+ if gen_type_info["gen_type"] == GEN_BUILTIN:
+ this_gen_size = False
+ # if curr_param["param_usage"] in ["FILE_DESCRIPTOR"]:
+ # curr_name = "fd_" + curr_name + str(self.file_idx) # string_prefix
+ # self.file_idx += 1
+ # curr_gen = self.__gen_file_descriptor(
+ # curr_name, gen_type_info)
+ # self.__append_gen_dict(curr_gen)
+ # break
+ # # GEN STRING SIZE
+
+ # elif param_id > 0 and (func["params"][param_id - 1]["gen_list"][0]["gen_type"] in [GEN_CSTRING, GEN_WSTRING, GEN_CXXSTRING] or curr_param["param_usage"] == "SIZE_FIELD"):
+ # if gen_type_info["type_name"] in ["size_t", "unsigned char", "char", "int", "unsigned", "unsigned int", "short", "unsigned short", "short int", "unsigned short int"]:
+ # dyn_size_idx = 0
+ # array_name = ""
+ # if func["params"][param_id - 1]["gen_list"][0]["gen_type"] == GEN_CSTRING:
+ # dyn_size_idx = self.dyn_cstring_size_idx
+ # array_name = "dyn_cstring_size"
+ # elif func["params"][param_id - 1]["gen_list"][0]["gen_type"] == GEN_WSTRING:
+ # dyn_size_idx = self.dyn_wstring_size_idx
+ # array_name = "dyn_wstring_size"
+ # else:
+ # dyn_size_idx = self.dyn_cxxstring_size_idx
+ # array_name = "dyn_cxxstring_size"
+ # curr_name = "sz_" + curr_name # size_prefix
+ # curr_gen = self.__gen_strsize(
+ # curr_name, curr_param["param_type"], dyn_size_idx, array_name)
+ # self.__append_gen_dict(curr_gen)
+ # this_gen_size = True
+ # break
+ if not this_gen_size:
+ curr_name = "b_" + curr_name # builtin_prefix
+ curr_gen = self.__gen_builtin(curr_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_CSTRING:
+ # GEN FILE NAME OR # GEN STRING
+ if (curr_param["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or curr_param["param_name"] in ["filename", "file", "filepath"] or curr_param["param_name"].find('file') != -1 or curr_param["param_name"].find('File') != -1) and len(curr_param["gen_list"]) == 1:
+ curr_name = "f_" + curr_name # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+ # GEN STRING
+ curr_name = "str_" + curr_name # string_prefix
+ self.dyn_cstring_size_idx += 1
+ curr_gen = self.__gen_cstring(
+ curr_name, gen_type_info, self.dyn_cstring_size_idx)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_REFSTRING:
+ # GEN FILE NAME OR # GEN STRING
+ if (curr_param["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or curr_param["param_name"] in ["filename", "file", "filepath"] or curr_param["param_name"].find('file') != -1 or curr_param["param_name"].find('File') != -1) and len(curr_param["gen_list"]) == 1:
+ curr_name = "f_" + curr_name # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+ # GEN STRING
+ curr_name = "str_" + curr_name # string_prefix
+ self.dyn_cstring_size_idx += 1
+ curr_gen = self.__gen_cstring(
+ curr_name, gen_type_info, self.dyn_cstring_size_idx)
+ # curr_name = "&" + curr_name
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_WSTRING:
+ # GEN FILE NAME OR # GEN STRING
+ if (curr_param["param_usage"] in ["FILE_PATH_READ", "FILE_PATH_WRITE", "FILE_PATH_RW", "FILE_PATH"] or curr_param["param_name"] in ["filename", "file", "filepath"] or curr_param["param_name"].find('file') != -1 or curr_param["param_name"].find('File') != -1) and len(curr_param["gen_list"]) == 1:
+ curr_name = "f_" + curr_name # string_prefix
+ self.file_idx += 1
+ curr_gen = self.__gen_input_file(
+ curr_name, gen_type_info)
+ else:
+ # GEN STRING
+ curr_name = "str_" + curr_name # string_prefix
+ self.dyn_wstring_size_idx += 1
+ curr_gen = self.__gen_wstring(
+ curr_name, gen_type_info, self.dyn_wstring_size_idx)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_CXXSTRING:
+ curr_name = "str_" + curr_name # string_prefix
+ self.dyn_cxxstring_size_idx += 1
+ curr_gen = self.__gen_cxxstring(
+ curr_name, gen_type_info, self.dyn_cxxstring_size_idx)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_ENUM: # GEN_ENUM
+ curr_name = "e_" + curr_name # enum_prefix
+ found_enum = None
+ # search in enum list of analysis result:
+ for enum in self.target_library["enums"]:
+ if len(gen_type_info["type_name"].split(" ")) > 1:
+ if enum["qname"] == gen_type_info["type_name"].split(" ")[
+ 1]:
+ found_enum = enum
+ break
+ else:
+ if enum["qname"] == gen_type_info["type_name"]:
+ found_enum = enum
+ break
+ if not found_enum:
+ # search in typedef list of analysis result:
+ for typedef in self.target_library["typedefs"]:
+ if typedef["name"] == gen_type_info["type_name"]:
+ enum_hash = typedef["type_source_hash"]
+ for enum in self.target_library["enums"]:
+ if enum["hash"] == enum_hash:
+ found_enum = enum
+ break
+ if not found_enum:
+ self.curr_func_log += f"- Can not generate for enum: {str(gen_type_info)}\n"
+ gen_curr_param = False
+ else:
+ compiler_info = self.__get_compile_command(
+ func["location"]["fullpath"])
+ curr_gen = self.__gen_enum(
+ found_enum, curr_name, gen_type_info, compiler_info, self.gen_anonymous)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_UNION:
+ curr_name = "u_" + curr_name # union_prefix
+ curr_gen = self.__gen_union(curr_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_ARRAY: # GEN_ARRAY
+ curr_name = "a_" + curr_name # array_prefix
+ curr_gen = self.__gen_array(curr_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_QUALIFIER:
+ curr_name = "q_" + curr_name # qualifier_prefix
+ curr_gen = self.__gen_qualifier(
+ curr_name, prev_param_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+
+ if gen_type_info["gen_type"] == GEN_POINTER:
+ curr_name = "p_" + curr_name # qualifier_prefix
+ curr_gen = self.__gen_pointer(
+ curr_name, prev_param_name, gen_type_info)
+ self.__append_gen_dict(curr_gen)
+ prev_param_name = curr_name
+ if not gen_curr_param:
+ self.gen_this_function = False
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ param_id += 1
+ self.__gen_target_function(func, param_id)
+
+ else:
+ if curr_param["gen_list"][0]["gen_type"] == GEN_STRUCT:
+ # 1. Search for function call that generate struct type
+ # 2. If not found, find in typdef the derived type of current struct and then take the action of 1.
+ # 3. If not found, find the struct definition, check if the struct is simple and manual generate
+
+ curr_name = "s_" + curr_name # struct_prefix
+ # A variable of structure type can be initialized with other functions.
+ result_search_return_type = self.__search_return_types(
+ curr_param["gen_list"], func, self.target_library['functions'])
+
+ if not result_search_return_type:
+ # A struct type may be defined with different name through typdef
+ result_search_typedefs = self.__search_in_typedefs(
+ curr_param["gen_list"][0]["type_name"], self.target_library['typedefs'])
+ if result_search_typedefs:
+ typedef_gen_list = [{
+ "base_type_name": result_search_typedefs["underlying_type"],
+ "gen_type": GEN_STRUCT,
+ "gen_type_name": "_STRUCT",
+ "length": 0,
+ "local_qualifier": "",
+ "type_name": result_search_typedefs["name"]
+ }]
+ # Search typedef in return type of functions
+ result_search_typdef_return_type = self.__search_return_types(
+ typedef_gen_list, func, self.target_library['functions'])
+ if result_search_typdef_return_type:
+ old_values = self.__save_old_values()
+ for curr_return_func in result_search_typdef_return_type:
+ self.var_function_idx += 1
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ curr_gen = self.__gen_var_function(
+ curr_name, curr_return_func["function"])
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
+ self.__append_gen_dict(curr_gen)
+ #!!!call recursive
+ param_id += 1
+ self.__gen_target_function(func, param_id)
+ param_id -= 1
+ self.__retrieve_old_values(old_values)
+ else:
+ found_struct = None
+ for record in self.target_library["records"]:
+ if len(curr_param["gen_list"][0]["type_name"].split(" ")) > 1 and record["type"] == STRUCT_RECORD and record["name"] == curr_param["gen_list"][0]["type_name"].split(" ")[1] and record["is_simple"]:
+ found_struct = record
+ break
+ if found_struct:
+ curr_gen = self.__gen_struct(
+ curr_name, record, curr_param["gen_list"][0])
+ self.__append_gen_dict(curr_gen)
+ else:
+ _tmp = curr_param["gen_list"][0]
+ self.curr_func_log += f"- Could not generate for object: {str(_tmp)}. Could not find function call to generate this struct!\n"
+ gen_curr_param = False
+ else:
+ _tmp = curr_param["gen_list"][0]
+ self.curr_func_log += f"- Could not generate for object: {str(_tmp)}. Could not create function call to generate this struct, and the definition of struct not found!\n"
+ gen_curr_param = False
+ else:
+ old_values = self.__save_old_values()
+ for curr_return_func in result_search_return_type:
+ self.var_function_idx += 1
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ curr_gen = self.__gen_var_function(
+ curr_name, curr_return_func["function"])
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
+ self.__append_gen_dict(curr_gen)
+ #!!!call recursive
+ param_id += 1
+ self.__gen_target_function(func, param_id)
+ param_id -= 1
+ self.__retrieve_old_values(old_values)
+
+ if curr_param["gen_list"][0]["gen_type"] == GEN_CLASS:
+ # 1. Search for function call that generate class type
+ # 2. If not found, try to generate class through constructor/default constructor
+
+ curr_name = "c_" + curr_name # struct_prefix
+ # A variable of structure type can be initialized with other functions.
+ result_search_return_type = self.__search_return_types(
+ curr_param["gen_list"], func, self.target_library['functions'])
+
+ if not result_search_return_type:
+ found_class = None
+ for record in self.target_library["records"]:
+ if record["type"] == CLASS_RECORD and record["name"] == curr_param["gen_list"][0]["type_name"]:
+ found_class = record
+ break
+ if found_class:
+ curr_gen_list = self.__gen_class(
+ curr_name, found_class)
+ old_values = self.__save_old_values()
+ for curr_gen in curr_gen_list:
+ self.__append_gen_dict(curr_gen)
+ #!!!call recursive
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ param_id += 1
+ self.var_function_idx += 1
+ self.__gen_target_function(func, param_id)
+ param_id -= 1
+ self.__retrieve_old_values(old_values)
+ else:
+ gen_type_info = curr_param["gen_list"][0]
+ self.curr_func_log += f"- Could not generate for object: {str(gen_type_info)}. Could not find function call to generate this class!\n"
+ gen_curr_param = False
+ else:
+ old_values = self.__save_old_values()
+ for curr_return_func in result_search_return_type:
+ self.var_function_idx += 1
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ curr_gen = self.__gen_var_function(
+ curr_name, curr_return_func["function"])
+ self.__add_header(self.__get_function_header(
+ func["location"]["fullpath"]))
+ self.__append_gen_dict(curr_gen)
+ #!!!call recursive
+ param_id += 1
+ self.__gen_target_function(func, param_id)
+ param_id -= 1
+ self.__retrieve_old_values(old_values)
+
+ if curr_param["gen_list"][0]["gen_type"] in [GEN_INCOMPLETE, GEN_VOID, GEN_FUNCTION, GEN_UNKNOWN]:
+ gen_type_info = curr_param["gen_list"][0]
+ self.curr_func_log += f"- Can not generate for object: {str(gen_type_info)}\n"
+ gen_curr_param = False
+
+ # if gen_type_info["gen_type"] == GEN_VOID:
+ # curr_name = "a_" + curr_name # void_prefix
+ # self.curr_func_log += f"- Can not generate for object: {str(gen_type_info)}\n"
+ # gen_curr_param = False
+ # # curr_gen = self.__gen_void(curr_name)
+ # # self.__append_gen_dict(curr_gen)
+
+ if not gen_curr_param:
+ self.gen_this_function = False
+ self.gen_lines += ["\n"]
+ self.param_list += [curr_name]
+ param_id += 1
+ self.__gen_target_function(func, param_id)
+
+ def gen_targets(self, anonymous: bool = False, max_wrappers: int = 10):
+ """
+ Parameters
+ ----------
+ anonymous: bool
+ option for generating fuzz-targets of non-public functions, default to False.
+ """
+ self.gen_anonymous = anonymous
+ self.max_wrappers = max_wrappers
+ C_generated_function = []
+ C_unknown_function = []
+ Cplusplus_usual_class_method = []
+ Cplusplus_static_class_method = []
+ Cplusplus_anonymous_class_method = []
+ for target in self.target_functions:
+ for func in self.target_library["functions"]:
+ if target["name"] != func["name"]:
+ continue
+ # For C
+ if func["access_type"] == AS_NONE and func["fuzz_it"] and func["storage_class"] < 2 and (func["parent_hash"] == ""):
+ print(
+ "-- [Futag] Try to generate fuzz-driver for function: ", func["name"], "...")
+ C_generated_function.append(func["name"])
+ self.gen_this_function = True
+ self.header = []
+ self.buffer_size = []
+ self.gen_lines = []
+ self.gen_free = []
+ self.dyn_cstring_size_idx = 0
+ self.dyn_wstring_size_idx = 0
+ self.dyn_cxxstring_size_idx = 0
+ self.file_idx = 0
+ self.var_function_idx = 0
+ self.param_list = []
+ self.curr_function = func
+ self.curr_func_log = ""
+ if "(anonymous" in func["qname"]:
+ self.__gen_anonymous_function(func, 0)
+ else:
+ self.__gen_target_function(func, 0)
+ # self.__gen_target_function(func, 0)
+
+ # For C++, Declare object of class and then call the method
+ if func["access_type"] == AS_PUBLIC and func["fuzz_it"] and func["func_type"] in [FUNC_CXXMETHOD, FUNC_CONSTRUCTOR, FUNC_DEFAULT_CONSTRUCTOR, FUNC_GLOBAL, FUNC_STATIC] and (not "::operator" in func["qname"]):
+ Cplusplus_usual_class_method.append(func["qname"])
+ print(
+ "-- [Futag] Try to generate fuzz-driver for class method: ", func["name"], "...")
+ self.gen_this_function = True
+ self.header = []
+ self.buffer_size = []
+ self.gen_lines = []
+ self.gen_free = []
+ self.dyn_cstring_size_idx = 0
+ self.dyn_wstring_size_idx = 0
+ self.dyn_cxxstring_size_idx = 0
+ self.file_idx = 0
+ self.var_function_idx = 0
+ self.param_list = []
+ self.curr_function = func
+ self.curr_func_log = ""
+ if "(anonymous" in func["qname"]:
+ self.__gen_anonymous_function(func, 0)
+ else:
+ self.__gen_target_function(func, 0)
+
+ # For C++, Call the static function of class without declaring object
+ if func["access_type"] in [AS_NONE, AS_PUBLIC] and func["fuzz_it"] and func["func_type"] in [FUNC_CXXMETHOD, FUNC_GLOBAL, FUNC_STATIC] and func["storage_class"] == SC_STATIC:
+ self.gen_this_function = True
+ self.header = []
+ self.buffer_size = []
+ self.gen_lines = []
+ self.gen_free = []
+ self.dyn_cstring_size_idx = 0
+ self.dyn_wstring_size_idx = 0
+ self.dyn_cxxstring_size_idx = 0
+ self.file_idx = 0
+ self.var_function_idx = 0
+ self.param_list = []
+ self.curr_function = func
+ self.curr_func_log = ""
+ if (not "(anonymous namespace)" in func["qname"]) and (not "::operator" in func["qname"]):
+ Cplusplus_static_class_method.append(func["qname"])
+ if "(anonymous" in func["qname"]:
+ self.__gen_anonymous_function(func, 0)
+
+ # We dont generate for static function of C
+ if func["func_type"] == FUNC_UNKNOW_RECORD and func["storage_class"] == 2:
+ C_unknown_function.append(func["qname"])
+
+ self.result_report = {
+ "C_generated_functions": C_generated_function,
+ "Cplusplus_static_class_methods": Cplusplus_static_class_method,
+ "Cplusplus_usual_class_methods": Cplusplus_usual_class_method,
+ "Cplusplus_anonymous_class_methods": Cplusplus_anonymous_class_method,
+ "C_unknown_functions": C_unknown_function
+ }
+ json.dump(self.result_report, open(
+ (self.build_path / "result-report.json").as_posix(), "w"))
+
+ def compile_driver_worker(self, bgen_args):
+ with open(bgen_args["error_path"], "w") as error_log_file:
+ p = Popen(
+ bgen_args["compiler_cmd"],
+ stdout=PIPE,
+ stderr=error_log_file,
+ universal_newlines=True,
+ )
+
+ target_file = open(bgen_args["source_path"], "a")
+
+ target_file.write("\n// Compile database: \n")
+ target_file.write("/*\n")
+ target_file.write(
+ "command: " + bgen_args["compiler_info"]['command'] + "\n")
+ target_file.write("location: " +
+ bgen_args["compiler_info"]['location'] + "\n")
+ target_file.write("file: " + bgen_args["compiler_info"]['file'])
+ target_file.write("\n*/\n")
+
+ new_compiler_cmd = []
+ compiler_cmd = bgen_args["compiler_cmd"]
+ target_file.write("\n// Compile command:")
+ target_file.write("\n/* \n")
+ output, errors = p.communicate()
+ if p.returncode:
+ print(" ".join(bgen_args["compiler_cmd"]))
+ print("\n-- [Futag] ERROR on target ",
+ bgen_args["target_name"], "\n")
+ for c in compiler_cmd:
+ if c.find(self.tmp_output_path.as_posix()) >= 0:
+ new_compiler_cmd.append(
+ c.replace(self.tmp_output_path.as_posix(), self.failed_path.as_posix()))
+ else:
+ new_compiler_cmd.append(c)
+
+ else:
+ print("-- [Futag] Fuzz-driver ",
+ bgen_args["target_name"], " was compiled successfully!")
+ for c in compiler_cmd:
+ if c.find(self.tmp_output_path.as_posix()) >= 0:
+ new_compiler_cmd.append(
+ c.replace(self.tmp_output_path.as_posix(), self.succeeded_path.as_posix()))
+ else:
+ new_compiler_cmd.append(c)
+
+ target_file.write(" ".join(new_compiler_cmd))
+ target_file.write("\n */\n")
+
+ error_log_file = open(bgen_args["error_path"], "r")
+ if error_log_file:
+ target_file.write("\n// Error log:")
+ target_file.write("\n/* \n")
+ target_file.write("".join(error_log_file.readlines()))
+ error_log_file.close()
+ target_file.write("\n */\n")
+ target_file.close()
+
+ def compile_targets(self, workers: int = 4, keep_failed: bool = False, extra_params: str = "", extra_include: str = "", extra_dynamiclink: str = "", flags: str = "", coverage: bool = False, keep_original: bool = False):
+ """_summary_
+
+ Args:
+ workers (int, optional): number of processes for compiling. Defaults to 4.
+ keep_failed (bool, optional): option for saving not compiled fuzz-targets. Defaults to False.
+ extra_params (str, optional): option for adding parameters while compiling. Defaults to "".
+ extra_include (str, optional): option for adding included directories while compiling. Defaults to "".
+ extra_dynamiclink (str, optional): option for adding dynamic libraries while compiling. Defaults to "".
+ flags (str, optional): flags for compiling fuzz-drivers. Defaults to "-fsanitize=address -g -O0".
+ coverage (bool, optional): option for adding coverage flag. Defaults to False.
+ keep_original (bool, optional): option for keeping .futag-fuzz-drivers. Defaults to False.
+ """
+
+ # include_subdir = self.target_library["header_dirs"]
+ # include_subdir = include_subdir + [x.parents[0].as_posix() for x in (self.build_path).glob("**/*.h")] + [x.parents[0].as_posix() for x in (self.build_path).glob("**/*.hpp")] + [self.build_path.as_posix()]
+
+ # if (self.install_path / "include").exists():
+ # include_subdir = include_subdir + [x.parents[0].as_posix() for x in (self.install_path / "include").glob("**/*.h")] + [x.parents[0].as_posix() for x in (self.install_path / "include").glob("**/*.hpp")]
+ # include_subdir = list(set(include_subdir))
+ if not flags:
+ if coverage:
+ compiler_flags_aflplusplus = COMPILER_FLAGS + " " + \
+ COMPILER_COVERAGE_FLAGS + " " + DEBUG_FLAGS + " -fPIE"
+ compiler_flags_libFuzzer = FUZZ_COMPILER_FLAGS + " " +\
+ COMPILER_COVERAGE_FLAGS + " " + DEBUG_FLAGS
+ else:
+ compiler_flags_aflplusplus = COMPILER_FLAGS + " " + DEBUG_FLAGS + " -fPIE "
+ compiler_flags_libFuzzer = FUZZ_COMPILER_FLAGS + " " + DEBUG_FLAGS
+ else:
+ compiler_flags_aflplusplus = flags
+ compiler_flags_libFuzzer = flags
+ if coverage:
+ compiler_flags_aflplusplus = COMPILER_COVERAGE_FLAGS + \
+ " " + compiler_flags_aflplusplus
+ compiler_flags_libFuzzer = COMPILER_COVERAGE_FLAGS + " " + compiler_flags_libFuzzer
+
+ generated_functions = [
+ x for x in self.tmp_output_path.iterdir() if x.is_dir()]
+
+ generated_targets = 0
+
+ compile_cmd_list = []
+ static_lib = []
+ target_lib = [u for u in (self.library_root).glob(
+ "**/*.a") if u.is_file()]
+ if target_lib:
+ static_lib = ["-Wl,--start-group"]
+ for t in target_lib:
+ static_lib.append(t.as_posix())
+ static_lib.append("-Wl,--end-group")
+
+ for func_dir in generated_functions:
+ # Extract compiler cwd, to resolve relative includes
+ search_curr_func = [
+ f for f in self.target_library['functions'] if f['qname'].replace(":", "_") == func_dir.name]
+ if not len(search_curr_func):
+ search_curr_func = [
+ f for f in self.target_library['functions'] if 'anonymous_' + f['name'].replace(":", "_") == func_dir.name]
+ if not len(search_curr_func):
+ continue
+ current_func = search_curr_func[0]
+ func_file_location = current_func["location"]["fullpath"]
+ compiler_info = self.__get_compile_command(func_file_location)
+
+ # List of Pathlib (-I parameters) in compile command.
+ include_subdir = []
+
+ if os.path.exists(compiler_info["location"]):
+ current_location = os.getcwd()
+ os.chdir(compiler_info["location"])
+ for iter in compiler_info["command"].split(" "):
+ if iter[0:2] == "-I":
+ if pathlib.Path(iter[2:]).exists():
+ include_subdir.append(
+ "-I" + pathlib.Path(iter[2:]).absolute().as_posix() + "/")
+ os.chdir(current_location)
+
+ if not "-fPIE" in compiler_flags_aflplusplus:
+ compiler_flags_aflplusplus += " -fPIE"
+
+ if not "-ferror-limit=1" in compiler_flags_libFuzzer:
+ compiler_flags_libFuzzer += " -ferror-limit=1"
+
+ compiler_path = ""
+ if self.target_type == LIBFUZZER:
+ if compiler_info["compiler"] == "CC":
+ compiler_path = self.futag_llvm_package / "bin/clang"
+ else:
+ compiler_path = self.futag_llvm_package / "bin/clang++"
+ else:
+ if compiler_info["compiler"] == "CC":
+ compiler_path = self.futag_llvm_package / \
+ "AFLplusplus/usr/local/bin/afl-clang-fast"
+ else:
+ compiler_path = self.futag_llvm_package / \
+ "AFLplusplus/usr/local/bin/afl-clang-fast++"
+
+ current_func_compilation_opts = ""
+ compilation_opts = ""
+
+ for compiled_file in self.target_library["compiled_files"]:
+ if func_file_location == compiled_file["filename"]:
+ compilation_opts = compiled_file["compiler_opts"]
+ current_func_compilation_opts = compilation_opts.split(' ')
+ # Extract all include locations from compilation options
+ include_paths: List[pathlib.Path] = map(
+ pathlib.Path,
+ map(
+ current_func_compilation_opts.__getitem__,
+ [i + 1 for i,
+ x in enumerate(current_func_compilation_opts) if x == '-I']
+ ))
+
+ resolved_include_paths: List[pathlib.Path] = []
+ for include_path in include_paths:
+ if include_path.is_absolute():
+ resolved_include_paths.append(include_path)
+ else:
+ # Resolve relative include paths (e.g. in this case: -I.. -I.)
+ resolved_include_paths.append(
+ pathlib.Path(include_path).absolute())
+
+ current_include = []
+ if not include_subdir:
+ if resolved_include_paths:
+ for i in resolved_include_paths:
+ current_include.append("-I" + i.as_posix() + "/")
+ else:
+ for i in include_subdir:
+ current_include.append(i)
+
+ fuzz_driver_dirs = [x for x in func_dir.iterdir() if x.is_dir()]
+ for dir in fuzz_driver_dirs:
+ # for target_src in [t for t in dir.glob("*"+self.target_extension) if t.is_file()]:
+ for target_src in [t for t in dir.glob("*") if t.is_file() and t.suffix in [".c", ".cc", ".cpp"]]:
+ target_path = dir.as_posix() + "/" + target_src.stem + ".out"
+ error_path = dir.as_posix() + "/" + target_src.stem + ".err"
+ generated_targets += 1
+ if self.target_type == LIBFUZZER:
+ compiler_cmd = [compiler_path.as_posix()] + compiler_flags_libFuzzer.split(" ") + current_include + ["-I" + x for x in extra_include.split(
+ " ") if x.strip()] + extra_params.split(" ") + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
+ else:
+ compiler_cmd = [compiler_path.as_posix()] + compiler_flags_aflplusplus.split(" ") + current_include + ["-I" + x for x in extra_include.split(
+ " ") if x.strip()] + extra_params.split(" ") + [target_src.as_posix()] + ["-o"] + [target_path] + static_lib + extra_dynamiclink.split(" ")
+
+ compile_cmd_list.append({
+ "compiler_cmd": compiler_cmd,
+ "target_name": target_src.stem,
+ "error_path": error_path,
+ "source_path": target_src.as_posix(),
+ "binary_path": target_path,
+ "compiler_info": compiler_info,
+ })
+ with Pool(workers) as p:
+ p.map(self.compile_driver_worker, compile_cmd_list)
+
+ # Extract the results of compilation
+
+ compiled_targets_list = [
+ x for x in self.tmp_output_path.glob("**/*.out") if x.is_file()]
+ print("-- [Futag] collecting result ...")
+
+ succeeded_tree = set()
+ succeeded_dir = set()
+ # for compiled_target in compiled_targets_list:
+ # if compiled_target.parents[0].as_posix() not in succeeded_tree:
+ # succeeded_tree.add(compiled_target.parents[0].as_posix())
+ for compiled_target in compiled_targets_list:
+ if compiled_target not in succeeded_tree:
+ succeeded_tree.add(compiled_target)
+ succeeded_dir.add(compiled_target.parents[0])
+ for dir in succeeded_tree:
+ if not (self.succeeded_path / dir.parents[1].name).exists():
+ ((self.succeeded_path /
+ dir.parents[1].name)).mkdir(parents=True, exist_ok=True)
+ # shutil.move(dir.parents[0].as_posix(), (self.succeeded_path / dir.parents[1].name).as_posix(), copy_function=shutil.copytree)
+ copy_tree(dir.parents[0].as_posix(
+ ), (self.succeeded_path / dir.parents[1].name / dir.parents[0].name).as_posix())
+ if keep_failed:
+ failed_tree = set()
+ not_compiled_targets_list = [
+ x for x in self.tmp_output_path.glob("**/*.cc") if x.is_file()]
+ not_compiled_targets_list = not_compiled_targets_list + [
+ x for x in self.tmp_output_path.glob("**/*.c") if x.is_file()]
+ not_compiled_targets_list = not_compiled_targets_list + [
+ x for x in self.tmp_output_path.glob("**/*.cpp") if x.is_file()]
+ for target in not_compiled_targets_list:
+ if target not in failed_tree:
+ failed_tree.add(target)
+ for dir in failed_tree:
+ if dir.parents[0] not in succeeded_dir:
+ if not (self.failed_path / dir.parents[1].name).exists():
+ ((self.failed_path /
+ dir.parents[1].name)).mkdir(parents=True, exist_ok=True)
+ # shutil.move(dir.parents[0].as_posix(), (self.failed_path / dir.parents[1].name).as_posix(), copy_function=shutil.copytree)
+ copy_tree(dir.parents[0].as_posix(
+ ), (self.failed_path / dir.parents[1].name / dir.parents[0].name).as_posix())
+ else:
+ delete_folder(self.failed_path)
+ if not keep_original:
+ delete_folder(self.tmp_output_path)
+
+ print(
+ "-- [Futag] Result of compiling: "
+ + str(len(compiled_targets_list))
+ + " fuzz-driver(s)\n"
+ )
+
+ def gen_targets_from_callstack(self, target):
+ found_function = None
+ for func in self.target_library["functions"]:
+ # if func["qname"] == target["qname"] and func["location"]["line"] == target["location"]["line"]and func["location"]["line"]["file"]== target["location"]["line"]:
+ if func["qname"] == target["qname"]:
+ found_function = func
+ self.__gen_target_function(func, 0)
+ if not found_function:
+ sys.exit("Function \"%s\" not found in library!" % target["qname"])
diff --git a/src/python/futag-package/src/futag/preprocessor.py b/src/python/futag-package/src/futag/preprocessor.py
index 78dd8d6..54f8303 100644
--- a/src/python/futag-package/src/futag/preprocessor.py
+++ b/src/python/futag-package/src/futag/preprocessor.py
@@ -174,7 +174,7 @@ def build_cmake(self) -> bool:
"cmake",
f"-DLLVM_CONFIG_PATH={(self.futag_llvm_package / 'bin/llvm-config').as_posix()}",
f"-DCMAKE_INSTALL_PREFIX={self.install_path.as_posix()}",
- # f"-DCMAKE_EXPORT_COMPILE_COMMANDS=1",
+ f"-DCMAKE_EXPORT_COMPILE_COMMANDS=1",
f"-B{(self.build_path).as_posix()}",
f"-S{self.library_root.as_posix()}"
]
@@ -240,7 +240,7 @@ def build_cmake(self) -> bool:
f"-DCMAKE_C_COMPILER={(self.futag_llvm_package / 'bin/clang').as_posix()}",
f"-DCMAKE_C_FLAGS='{self.flags}'",
f"-B{(self.build_path).as_posix()}",
- f"-S{self.library_root.as_posix()}"
+ f"-S{self.library_root.as_posix()}",f"-DCMAKE_EXPORT_COMPILE_COMMANDS=1"
]
# my_env["CC"] = (self.futag_llvm_package / 'bin/clang').as_posix()
@@ -980,7 +980,7 @@ def build_cmake(self) -> bool:
"cplusplus",
"cmake",
f"-DLLVM_CONFIG_PATH={(self.futag_llvm_package / 'bin/llvm-config').as_posix()}",
- # f"-DCMAKE_EXPORT_COMPILE_COMMANDS=1",
+ f"-DCMAKE_EXPORT_COMPILE_COMMANDS=1",
f"-B{(self.build_path).as_posix()}",
f"-S{self.consumer_root.as_posix()}"
]
@@ -1028,11 +1028,11 @@ def build_cmake(self) -> bool:
universal_newlines=True, env=my_env)
print(LIB_ANALYZING_COMMAND, " ".join(p.args))
output, errors = p.communicate()
- if p.returncode:
- print(errors)
- sys.exit(LIB_ANALYZING_FAILED)
- else:
- print(LIB_ANALYZING_SUCCEEDED)
+ # if p.returncode:
+ # print(errors)
+ # sys.exit(LIB_ANALYZING_FAILED)
+ # else:
+ # print(LIB_ANALYZING_SUCCEEDED)
os.chdir(curr_dir)
return True
@@ -1110,11 +1110,11 @@ def build_configure(self) -> bool:
print(LIB_ANALYZING_COMMAND, " ".join(p.args))
output, errors = p.communicate()
- if p.returncode:
- print(errors)
- sys.exit(LIB_ANALYZING_FAILED)
- else:
- print(LIB_ANALYZING_SUCCEEDED)
+ # if p.returncode:
+ # print(errors)
+ # sys.exit(LIB_ANALYZING_FAILED)
+ # else:
+ # print(LIB_ANALYZING_SUCCEEDED)
os.chdir(curr_dir)
return True
diff --git a/src/python/futag-package/src/futag/sysmsg.py b/src/python/futag-package/src/futag/sysmsg.py
index e3f7118..5bd77a6 100644
--- a/src/python/futag-package/src/futag/sysmsg.py
+++ b/src/python/futag-package/src/futag/sysmsg.py
@@ -38,6 +38,7 @@
INVALID_ANALYSIS_FILE = "-- [Futag]: Incorrect path to analysis result file"
INVALID_CONTEXT_FILE_PATH = "-- [Futag]: Incorrect path to consumer context file"
INVALID_LIBPATH = "-- [Futag]: Incorrect path to the library root"
+INVALID_NATCH_JSON = "-- [Futag]: Incorrect path to JSON file from Natch."
INVALID_CONSUMER_PATH = "-- [Futag]: Incorrect path to the consumer program"
INVALID_DB_FILEPATH = "-- [Futag]: analysis result of testing library not found"
INVALID_BUILPATH = "-- [Futag]: Incorrect path to the library build path"
@@ -69,7 +70,7 @@
INVALID_TARGET_TYPE = "-- [Futag] Error: Unknown type of fuzz-driver for generating!"
# message for Natch
-COULD_NOT_PARSE_NATCH_CALLSTACK = "-- [Futag] Error: Could not parse file!"
+COULD_NOT_PARSE_NATCH_CALLSTACK = "-- [Futag] Error: Could not parse JSON file!"
# messages for GENERATOR