Skip to content

Add optional flag --propagage_exit_codes to reproduce #13133

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 26 additions & 11 deletions infra/helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ def main(): # pylint: disable=too-many-branches,too-many-return-statements
result = introspector(args)
elif args.command == 'reproduce':
result = reproduce(args)
if args.propagate_exit_codes:
return result
elif args.command == 'shell':
result = shell(args)
elif args.command == 'pull_images':
Expand Down Expand Up @@ -482,6 +484,14 @@ def get_parser(): # pylint: disable=too-many-statements,too-many-locals
reproduce_parser.add_argument('--valgrind',
action='store_true',
help='run with valgrind')
reproduce_parser.add_argument('--propagate_exit_codes',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed this with @jonathanmetzman offline -- this should be fine as default behaviour.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll get to updating this to be default behavior when I get the chance!

action='store_true',
default=False,
help='return underlying exit codes instead of True/False.')
reproduce_parser.add_argument('--err_result',
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than introduce another flag, perhaps we can just hardcode and document a specific error code for this?

i.e.

EXIT_CODE_FUZZER_NOT_EXISTS = 199

(probably need to pick an error code here is unlikely to clash generally)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds good to me! I will leave it up to you guys to decide on an exit code default, as I'm sure you're more aware of potential code conflicts.

help='exit code override for missing harness / fuzz targets '
'(default err_result = 1).',
type=int)
reproduce_parser.add_argument('project',
help='name of the project or path (external)')
reproduce_parser.add_argument('fuzzer_name', help='name of the fuzzer')
Expand Down Expand Up @@ -717,7 +727,7 @@ def prepare_aarch64_emulation():
subprocess.check_call(['docker', 'buildx', 'use', ARM_BUILDER_NAME])


def docker_run(run_args, print_output=True, architecture='x86_64'):
def docker_run(run_args, print_output=True, architecture='x86_64', propagate_exit_codes=False):
"""Calls `docker run`."""
platform = 'linux/arm64' if architecture == 'aarch64' else 'linux/amd64'
command = [
Expand All @@ -740,12 +750,14 @@ def docker_run(run_args, print_output=True, architecture='x86_64'):
if not print_output:
stdout = open(os.devnull, 'w')

exit_code = 0

try:
subprocess.check_call(command, stdout=stdout, stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
return False

return True
except subprocess.CalledProcessError as e:
print(f'subprocess command returned a non-zero exit status: {e.returncode}')
exit_code = e.returncode
return exit_code if propagate_exit_codes else exit_code == 0


def docker_build(build_args):
Expand Down Expand Up @@ -1532,7 +1544,8 @@ def fuzzbench_measure(args):
def reproduce(args):
"""Reproduces a specific test case from a specific project."""
return reproduce_impl(args.project, args.fuzzer_name, args.valgrind, args.e,
args.fuzzer_args, args.testcase_path, args.architecture)
args.fuzzer_args, args.testcase_path, args.architecture,
args.propagate_exit_codes, args.err_result)


def reproduce_impl( # pylint: disable=too-many-arguments
Expand All @@ -1543,14 +1556,16 @@ def reproduce_impl( # pylint: disable=too-many-arguments
fuzzer_args,
testcase_path,
architecture='x86_64',
run_function=docker_run,
err_result=False):
propagate_exit_codes=False,
err_result=1,
run_function=docker_run):
"""Reproduces a testcase in the container."""

if not check_project_exists(project):
return err_result
return err_result if propagate_exit_codes else False

if not _check_fuzzer_exists(project, fuzzer_name, architecture):
return err_result
return err_result if propagate_exit_codes else False

debugger = ''
env = ['HELPER=True', 'ARCHITECTURE=' + architecture]
Expand Down Expand Up @@ -1578,7 +1593,7 @@ def reproduce_impl( # pylint: disable=too-many-arguments
'-runs=100',
] + fuzzer_args

return run_function(run_args, architecture=architecture)
return run_function(run_args, architecture=architecture, propagate_exit_codes=propagate_exit_codes)


def _validate_project_name(project_name):
Expand Down