Skip to content

Commit 7de1556

Browse files
committed
Filter out non-bug exceptions using a pre-defined exception list.
This reduces false positive test failures by identifying and gracefully handling exceptions that are explicitly raised by GitPython, thus reducing the false-positive fuzzing test failure rate.
1 parent af0cd93 commit 7de1556

File tree

3 files changed

+56
-13
lines changed

3 files changed

+56
-13
lines changed

Diff for: fuzzing/fuzz-targets/fuzz_submodule.py

+44-12
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,51 @@
1+
# ruff: noqa: E402
12
import atheris
23
import sys
34
import os
5+
import traceback
46
import tempfile
57
from configparser import ParsingError
6-
from utils import is_expected_exception_message, get_max_filename_length
8+
from utils import get_max_filename_length
9+
import re
10+
11+
bundle_dir = os.path.dirname(os.path.abspath(__file__))
712

813
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): # pragma: no cover
9-
path_to_bundled_git_binary = os.path.abspath(os.path.join(os.path.dirname(__file__), "git"))
10-
os.environ["GIT_PYTHON_GIT_EXECUTABLE"] = path_to_bundled_git_binary
14+
bundled_git_binary_path = os.path.join(bundle_dir, "git")
15+
os.environ["GIT_PYTHON_GIT_EXECUTABLE"] = bundled_git_binary_path
1116

1217
from git import Repo, GitCommandError, InvalidGitRepositoryError
1318

19+
20+
def load_exception_list(file_path):
21+
"""Load and parse the exception list from a file."""
22+
try:
23+
with open(file_path, "r") as file:
24+
lines = file.readlines()
25+
exception_list = set()
26+
for line in lines:
27+
match = re.match(r"(.+):(\d+):", line)
28+
if match:
29+
file_path = match.group(1).strip()
30+
line_number = int(match.group(2).strip())
31+
exception_list.add((file_path, line_number))
32+
return exception_list
33+
except FileNotFoundError:
34+
print("File not found: %s", file_path)
35+
return set()
36+
except Exception as e:
37+
print("Error loading exception list: %s", e)
38+
return set()
39+
40+
41+
def check_exception_against_list(exception_list, exc_traceback):
42+
"""Check if the exception traceback matches any entry in the exception list."""
43+
for filename, lineno, _, _ in traceback.extract_tb(exc_traceback):
44+
if (filename, lineno) in exception_list:
45+
return True
46+
return False
47+
48+
1449
if not sys.warnoptions: # pragma: no cover
1550
# The warnings filter below can be overridden by passing the -W option
1651
# to the Python interpreter command line or setting the `PYTHONWARNINGS` environment variable.
@@ -89,17 +124,14 @@ def TestOneInput(data):
89124
BrokenPipeError,
90125
):
91126
return -1
92-
except ValueError as e:
93-
expected_messages = [
94-
"SHA is empty",
95-
"Reference at",
96-
"embedded null byte",
97-
"This submodule instance does not exist anymore",
98-
"cmd stdin was empty",
99-
]
100-
if is_expected_exception_message(e, expected_messages):
127+
except Exception as e:
128+
exc_traceback = e.__traceback__
129+
exception_list = load_exception_list(os.path.join(bundle_dir, "explicit-exceptions-list.txt"))
130+
if check_exception_against_list(exception_list, exc_traceback):
131+
print("Exception matches an entry in the exception list.")
101132
return -1
102133
else:
134+
print("Exception does not match any entry in the exception list.")
103135
raise e
104136

105137

Diff for: fuzzing/oss-fuzz-scripts/build.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ find "$SRC" -maxdepth 1 \
1515

1616
# Build fuzzers in $OUT.
1717
find "$SRC/gitpython/fuzzing" -name 'fuzz_*.py' -print0 | while IFS= read -r -d '' fuzz_harness; do
18-
compile_python_fuzzer "$fuzz_harness" --add-binary="$(command -v git):."
18+
compile_python_fuzzer "$fuzz_harness" --add-binary="$(command -v git):." --add-data="$SRC/explicit-exceptions-list.txt:."
1919
done

Diff for: fuzzing/oss-fuzz-scripts/container-environment-bootstrap.sh

+11
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,17 @@ create_seed_corpora_zips "$WORK/qa-assets/gitpython/corpora"
9191

9292
prepare_dictionaries_for_fuzz_targets "$WORK/qa-assets/gitpython/dictionaries" "$SRC/gitpython/fuzzing"
9393

94+
pushd "$SRC/gitpython/"
95+
# Search for 'raise' and 'assert' statements in Python files within GitPython's 'git/' directory and its submodules,
96+
# remove trailing colons, and save to 'explicit-exceptions-list.txt'. This file can then be used by fuzz harnesses to
97+
# check exception tracebacks:
98+
# If an exception found by the fuzzer originated in a file + line number in explicit-exceptions-list.txt, then it is not a bug.
99+
100+
git grep -n --recurse-submodules -e '\braise\b' -e '\bassert\b' -- "git/**/*.py" > "$SRC/explicit-exceptions-list.txt"
101+
102+
popd
103+
104+
94105
# The OSS-Fuzz base image has outdated dependencies by default so we upgrade them below.
95106
python3 -m pip install --upgrade pip
96107
# Upgrade to the latest versions known to work at the time the below changes were introduced:

0 commit comments

Comments
 (0)