Skip to content

Commit 8502efe

Browse files
authored
Merge pull request #1922 from DaveLak/fix-fuzz-submodule-expected-exceptions-handling
Add graceful handling of expected exceptions in fuzz_submodule.py
2 parents 2345c1a + 2b64dee commit 8502efe

File tree

2 files changed

+44
-11
lines changed

2 files changed

+44
-11
lines changed

fuzzing/fuzz-targets/fuzz_submodule.py

+29-11
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,23 @@
33
import os
44
import tempfile
55
from configparser import ParsingError
6-
from utils import is_expected_exception_message
6+
from utils import is_expected_exception_message, get_max_filename_length
7+
from git import Repo, GitCommandError, InvalidGitRepositoryError
78

8-
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"):
9+
if getattr(sys, "frozen", False) and hasattr(sys, "_MEIPASS"): # pragma: no cover
910
path_to_bundled_git_binary = os.path.abspath(os.path.join(os.path.dirname(__file__), "git"))
1011
os.environ["GIT_PYTHON_GIT_EXECUTABLE"] = path_to_bundled_git_binary
1112

12-
with atheris.instrument_imports():
13-
from git import Repo, GitCommandError, InvalidGitRepositoryError
13+
if not sys.warnoptions: # pragma: no cover
14+
# The warnings filter below can be overridden by passing the -W option
15+
# to the Python interpreter command line or setting the `PYTHONWARNINGS` environment variable.
16+
import warnings
17+
import logging
18+
19+
# Fuzzing data causes some modules to generate a large number of warnings
20+
# which are not usually interesting and make the test output hard to read, so we ignore them.
21+
warnings.simplefilter("ignore")
22+
logging.getLogger().setLevel(logging.ERROR)
1423

1524

1625
def TestOneInput(data):
@@ -42,12 +51,12 @@ def TestOneInput(data):
4251
writer.release()
4352

4453
submodule.update(init=fdp.ConsumeBool(), dry_run=fdp.ConsumeBool(), force=fdp.ConsumeBool())
45-
4654
submodule_repo = submodule.module()
47-
new_file_path = os.path.join(
48-
submodule_repo.working_tree_dir,
49-
f"new_file_{fdp.ConsumeUnicodeNoSurrogates(fdp.ConsumeIntInRange(1, 512))}",
55+
56+
new_file_name = fdp.ConsumeUnicodeNoSurrogates(
57+
fdp.ConsumeIntInRange(1, max(1, get_max_filename_length(submodule_repo.working_tree_dir)))
5058
)
59+
new_file_path = os.path.join(submodule_repo.working_tree_dir, new_file_name)
5160
with open(new_file_path, "wb") as new_file:
5261
new_file.write(fdp.ConsumeBytes(fdp.ConsumeIntInRange(1, 512)))
5362
submodule_repo.index.add([new_file_path])
@@ -67,16 +76,24 @@ def TestOneInput(data):
6776
)
6877
repo.index.commit(f"Removed submodule {submodule_name}")
6978

70-
except (ParsingError, GitCommandError, InvalidGitRepositoryError, FileNotFoundError, BrokenPipeError):
79+
except (
80+
ParsingError,
81+
GitCommandError,
82+
InvalidGitRepositoryError,
83+
FileNotFoundError,
84+
FileExistsError,
85+
IsADirectoryError,
86+
NotADirectoryError,
87+
BrokenPipeError,
88+
):
7189
return -1
72-
except (ValueError, OSError) as e:
90+
except ValueError as e:
7391
expected_messages = [
7492
"SHA is empty",
7593
"Reference at",
7694
"embedded null byte",
7795
"This submodule instance does not exist anymore",
7896
"cmd stdin was empty",
79-
"File name too long",
8097
]
8198
if is_expected_exception_message(e, expected_messages):
8299
return -1
@@ -85,6 +102,7 @@ def TestOneInput(data):
85102

86103

87104
def main():
105+
atheris.instrument_all()
88106
atheris.Setup(sys.argv, TestOneInput)
89107
atheris.Fuzz()
90108

fuzzing/fuzz-targets/utils.py

+15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import atheris # pragma: no cover
2+
import os # pragma: no cover
23
from typing import List # pragma: no cover
34

45

@@ -20,3 +21,17 @@ def is_expected_exception_message(exception: Exception, error_message_list: List
2021
if error.lower() in exception_message:
2122
return True
2223
return False
24+
25+
26+
@atheris.instrument_func
27+
def get_max_filename_length(path: str) -> int: # pragma: no cover
28+
"""
29+
Get the maximum filename length for the filesystem containing the given path.
30+
31+
Args:
32+
path (str): The path to check the filesystem for.
33+
34+
Returns:
35+
int: The maximum filename length.
36+
"""
37+
return os.pathconf(path, "PC_NAME_MAX")

0 commit comments

Comments
 (0)