From f4b95cf089706a29396b744b53a4ecdcc924d31c Mon Sep 17 00:00:00 2001
From: David Lakin <github@themoderndev.com>
Date: Mon, 22 Apr 2024 16:07:54 -0400
Subject: [PATCH] Fix Fuzzer Crash in ClusterFuzz Due to Missing Git Executable

A Git executable is not globally available in the ClusterFuzz container
environment where OSS-Fuzz executes fuzz tests, causing an error in the fuzz
harnesses when GitPython attempts to initialize, crashing the tests before they
can run.

To avoid this issue, we bundle the `git` binary that is available in the OSS-Fuzz
build container with the fuzz harness via Pyinstaller's `--add-binary` flag in
`build.sh` and use GitPython's `git.refresh(<full-path-to-git-executable>)`
method inside a Pyinstaller runtime check to initialize GitPython with the
bundled Git executable when running from the bundled application.

In all other execution environments, we assume a `git` executable is available
globally.

Fixes:
- https://github.com/gitpython-developers/GitPython/issues/1905
- https://github.com/google/oss-fuzz/issues/10600
---
 fuzzing/fuzz-targets/fuzz_config.py |  9 +++++++--
 fuzzing/fuzz-targets/fuzz_tree.py   | 11 +++++++----
 fuzzing/oss-fuzz-scripts/build.sh   |  2 +-
 3 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/fuzzing/fuzz-targets/fuzz_config.py b/fuzzing/fuzz-targets/fuzz_config.py
index 0a06956c8..7623ab98f 100644
--- a/fuzzing/fuzz-targets/fuzz_config.py
+++ b/fuzzing/fuzz-targets/fuzz_config.py
@@ -20,16 +20,21 @@
 import atheris
 import sys
 import io
+import os
 from configparser import MissingSectionHeaderError, ParsingError
 
 with atheris.instrument_imports():
-    from git import GitConfigParser
+    import git
 
 
 def TestOneInput(data):
+    if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
+        path_to_bundled_git_binary = os.path.abspath(os.path.join(os.path.dirname(__file__), "git"))
+        git.refresh(path_to_bundled_git_binary)
+
     sio = io.BytesIO(data)
     sio.name = "/tmp/fuzzconfig.config"
-    git_config = GitConfigParser(sio)
+    git_config = git.GitConfigParser(sio)
     try:
         git_config.read()
     except (MissingSectionHeaderError, ParsingError, UnicodeDecodeError):
diff --git a/fuzzing/fuzz-targets/fuzz_tree.py b/fuzzing/fuzz-targets/fuzz_tree.py
index 464235098..7187c4a6f 100644
--- a/fuzzing/fuzz-targets/fuzz_tree.py
+++ b/fuzzing/fuzz-targets/fuzz_tree.py
@@ -24,11 +24,14 @@
 import shutil
 
 with atheris.instrument_imports():
-    from git.objects import Tree
-    from git.repo import Repo
+    import git
 
 
 def TestOneInput(data):
+    if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
+        path_to_bundled_git_binary = os.path.abspath(os.path.join(os.path.dirname(__file__), "git"))
+        git.refresh(path_to_bundled_git_binary)
+
     fdp = atheris.FuzzedDataProvider(data)
     git_dir = "/tmp/.git"
     head_file = os.path.join(git_dir, "HEAD")
@@ -46,9 +49,9 @@ def TestOneInput(data):
     os.mkdir(common_dir)
     os.mkdir(objects_dir)
 
-    _repo = Repo("/tmp/")
+    _repo = git.Repo("/tmp/")
 
-    fuzz_tree = Tree(_repo, Tree.NULL_BIN_SHA, 0, "")
+    fuzz_tree = git.Tree(_repo, git.Tree.NULL_BIN_SHA, 0, "")
     try:
         fuzz_tree._deserialize(io.BytesIO(data))
     except IndexError:
diff --git a/fuzzing/oss-fuzz-scripts/build.sh b/fuzzing/oss-fuzz-scripts/build.sh
index ab46ec7a2..be31ac32a 100644
--- a/fuzzing/oss-fuzz-scripts/build.sh
+++ b/fuzzing/oss-fuzz-scripts/build.sh
@@ -14,7 +14,7 @@ find "$SEED_DATA_DIR" \( -name '*_seed_corpus.zip' -o -name '*.options' -o -name
 
 # Build fuzzers in $OUT.
 find "$SRC/gitpython/fuzzing" -name 'fuzz_*.py' -print0 | while IFS= read -r -d '' fuzz_harness; do
-  compile_python_fuzzer "$fuzz_harness"
+  compile_python_fuzzer "$fuzz_harness" --add-binary="$(command -v git):."
 
   common_base_dictionary_filename="$SEED_DATA_DIR/__base.dict"
   if [[ -r "$common_base_dictionary_filename" ]]; then